Compare commits
320
Commits
heiume-patch-2
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3e21ac98e | ||
|
|
9aaa894a2b | ||
|
|
685e225d85 | ||
|
|
dfd130c595 | ||
|
|
d81449fc12 | ||
|
|
a538f7d379 | ||
|
|
c1f0138fc7 | ||
|
|
bdb9a99c90 | ||
|
|
f8396e9a1b | ||
|
|
08b16d4efb | ||
|
|
c1ba8164fc | ||
|
|
41ea7fedaa | ||
|
|
420fbff0b9 | ||
|
|
240bc5d1b8 | ||
|
|
2d6e639af5 | ||
|
|
7abd8c604a | ||
|
|
d48a9e31fc | ||
|
|
f8b220049b | ||
|
|
8d0bf8f458 | ||
|
|
a7aea4dbee | ||
|
|
3d25c64b7d | ||
|
|
5273e07eac | ||
|
|
51ce8ba957 | ||
|
|
cffb7cafce | ||
|
|
6193b69102 | ||
|
|
aa3f5ee61a | ||
|
|
c7a487c2e7 | ||
|
|
53377929d6 | ||
|
|
db33d7bfd7 | ||
|
|
d356ee5b4a | ||
|
|
901b22c485 | ||
|
|
5a0edbf378 | ||
|
|
654fdb2a90 | ||
|
|
083e5784cf | ||
|
|
9d848594f6 | ||
|
|
c3ce88fd6f | ||
|
|
02be90f8ff | ||
|
|
34c5dcf65b | ||
|
|
94e35224c4 | ||
|
|
7b6ae6c5b2 | ||
|
|
5d15f76f46 | ||
|
|
005b0e533e | ||
|
|
5ff4ec9884 | ||
|
|
6d74a00019 | ||
|
|
14ead17dd4 | ||
|
|
2dfbc84f4f | ||
|
|
5db8a01b76 | ||
|
|
ea67fc1cee | ||
|
|
b36bea3694 | ||
|
|
b17e2ea383 | ||
|
|
d55f9be338 | ||
|
|
6a8a2853db | ||
|
|
907ba0285c | ||
|
|
b6259416a2 | ||
|
|
13b94fa25b | ||
|
|
8be57457c1 | ||
|
|
cfb7eb2ad5 | ||
|
|
31b3288d8d | ||
|
|
f1b7a40981 | ||
|
|
5093545ea8 | ||
|
|
b59acce30c | ||
|
|
0f7b8cfe26 | ||
|
|
a401da9dd2 | ||
|
|
4c4536a963 | ||
|
|
eb770eafee | ||
|
|
bd3ed5ff09 | ||
|
|
62344eb41d | ||
|
|
5663ff9e36 | ||
|
|
8fedf27435 | ||
|
|
5e53b90986 | ||
|
|
825f120032 | ||
|
|
fceed10afd | ||
|
|
ea44cd7e7d | ||
|
|
89f3df916f | ||
|
|
f569a6d430 | ||
|
|
3507055033 | ||
|
|
89c5432677 | ||
|
|
9456b37477 | ||
|
|
fd5b5dc5bb | ||
|
|
386f87a1e3 | ||
|
|
48297b566c | ||
|
|
5e670c2049 | ||
|
|
bbe89107cc | ||
|
|
6c90e5fa5f | ||
|
|
60925ec745 | ||
|
|
f06d7a8e21 | ||
|
|
b3eb1acfac | ||
|
|
a39070c545 | ||
|
|
c401a79b93 | ||
|
|
e1b96b510c | ||
|
|
17ff4787be | ||
|
|
57565169d4 | ||
|
|
df9085adef | ||
|
|
7c8005dae6 | ||
|
|
e408ac2dbe | ||
|
|
9f393c7c80 | ||
|
|
1e8187530e | ||
|
|
2dc693f474 | ||
|
|
f957bcf4b4 | ||
|
|
6aaa159b98 | ||
|
|
e769efcda7 | ||
|
|
fd8e8e7449 | ||
|
|
fd8ba2d9ce | ||
|
|
060eeb3457 | ||
|
|
a67c0c9958 | ||
|
|
68dee8a432 | ||
|
|
483b234182 | ||
|
|
5e6fcc2b11 | ||
|
|
3cd3b4c854 | ||
|
|
6781e75764 | ||
|
|
66999ed3e5 | ||
|
|
718e6b7807 | ||
|
|
4f8b27206c | ||
|
|
a91a92318d | ||
|
|
631277dfba | ||
|
|
e49e199641 | ||
|
|
fe4de7eb28 | ||
|
|
52b4112684 | ||
|
|
92d8456b89 | ||
|
|
961e9dfef3 | ||
|
|
0ad020beab | ||
|
|
2b76964b59 | ||
|
|
5ab3c62a2f | ||
|
|
abd1bc59d3 | ||
|
|
5f03f2def9 | ||
|
|
a2ec505971 | ||
|
|
0093f036ed | ||
|
|
0810567fa6 | ||
|
|
eb6b2eea6c | ||
|
|
0e3dce5354 | ||
|
|
ef86167448 | ||
|
|
eed3053b2e | ||
|
|
587cac06af | ||
|
|
6f0f2e2c30 | ||
|
|
0dbf974254 | ||
|
|
0659bfb2e7 | ||
|
|
ea6a5a7af6 | ||
|
|
4ae35f1a85 | ||
|
|
e783169a26 | ||
|
|
ae2bd4ff2d | ||
|
|
9aeed4c321 | ||
|
|
e5aa8fd373 | ||
|
|
a528037105 | ||
|
|
71550124cb | ||
|
|
d2deff2aa3 | ||
|
|
d009f1b64e | ||
|
|
af7d7b6d73 | ||
|
|
5bd4f12f74 | ||
|
|
e42b403931 | ||
|
|
ed1d0c89a8 | ||
|
|
8fb38a8f48 | ||
|
|
cc3b801133 | ||
|
|
a1aa5bfc1a | ||
|
|
c2034152b6 | ||
|
|
6552ea11a6 | ||
|
|
74e0bab1e8 | ||
|
|
fb2c232461 | ||
|
|
01d7218e4c | ||
|
|
d57935f9be | ||
|
|
829ae3e3ac | ||
|
|
9586149989 | ||
|
|
514858f573 | ||
|
|
c923a9ecd3 | ||
|
|
9b5007ab58 | ||
|
|
12bb56216b | ||
|
|
a67a34905e | ||
|
|
f5938a19fc | ||
|
|
0025a3f0da | ||
|
|
7266de5b56 | ||
|
|
93d0fcd137 | ||
|
|
c642143a8c | ||
|
|
7824c84048 | ||
|
|
07d05ca96c | ||
|
|
ee89241011 | ||
|
|
f4d75c56ab | ||
|
|
1f87e4057f | ||
|
|
2fe2040bbe | ||
|
|
df897fab29 | ||
|
|
c49aa0307f | ||
|
|
1a9f07a300 | ||
|
|
10bbb3e7ea | ||
|
|
61cc01fcc8 | ||
|
|
7a6993da45 | ||
|
|
0aef29123f | ||
|
|
43837e67db | ||
|
|
9ff481d012 | ||
|
|
430529b99d | ||
|
|
5e1c7ab059 | ||
|
|
480588b88e | ||
|
|
f0be1e220f | ||
|
|
456a9e7fc8 | ||
|
|
3ce10ddd85 | ||
|
|
938f48040b | ||
|
|
7a27c713a3 | ||
|
|
e0324954a3 | ||
|
|
e22d4d7dd6 | ||
|
|
4bc360868a | ||
|
|
e4117a7924 | ||
|
|
2c17452990 | ||
|
|
af53dc34ee | ||
|
|
0695e45c1d | ||
|
|
e286ede979 | ||
|
|
53556d852d | ||
|
|
d1aa090ba4 | ||
|
|
985504da7a | ||
|
|
bc8a92ce89 | ||
|
|
468cc25214 | ||
|
|
786bf57c82 | ||
|
|
ca1fd53ef0 | ||
|
|
321bbcbe5f | ||
|
|
1e346d616e | ||
|
|
7c005c9f72 | ||
|
|
19979f393c | ||
|
|
2702f46196 | ||
|
|
6a60969d85 | ||
|
|
3329186f60 | ||
|
|
81a324b0b4 | ||
|
|
9a71df80df | ||
|
|
3750ffa992 | ||
|
|
9075e23c36 | ||
|
|
f86dd770f1 | ||
|
|
24c447017f | ||
|
|
421d7abc0d | ||
|
|
e6fe1de910 | ||
|
|
2903902389 | ||
|
|
985ce2e8cd | ||
|
|
8f6e069e1e | ||
|
|
3f71469c34 | ||
|
|
49f5933d9a | ||
|
|
e87eacf6ed | ||
|
|
91374e3bbe | ||
|
|
cf7a4eed3d | ||
|
|
514a51e106 | ||
|
|
88b3611906 | ||
|
|
69f748d21a | ||
|
|
f34145e932 | ||
|
|
9b3cd30a20 | ||
|
|
a6a85a6985 | ||
|
|
18e2dc0725 | ||
|
|
8a4d9947b0 | ||
|
|
eacb284ff4 | ||
|
|
ed638f5101 | ||
|
|
c672d49828 | ||
|
|
7ff29eff6f | ||
|
|
817b982f4e | ||
|
|
a27dd78066 | ||
|
|
891438f1ca | ||
|
|
eb9591bee9 | ||
|
|
be00aa0a3b | ||
|
|
b2fc0c0324 | ||
|
|
de24b13187 | ||
|
|
e15a588415 | ||
|
|
99febf17bc | ||
|
|
6c20047ed1 | ||
|
|
42f2f62a61 | ||
|
|
63a9c7208b | ||
|
|
bc7c2026a2 | ||
|
|
06346b470b | ||
|
|
c17372b549 | ||
|
|
78dff5e438 | ||
|
|
5128ac4bed | ||
|
|
ad3ee80d1c | ||
|
|
5d5c5133e6 | ||
|
|
e85e7bc8fa | ||
|
|
7a362dae9d | ||
|
|
d5f0968802 | ||
|
|
e4ac8376e6 | ||
|
|
9b2ae46052 | ||
|
|
7297fac7fd | ||
|
|
29d5bce4d6 | ||
|
|
44df09ecdd | ||
|
|
80c3deab5a | ||
|
|
905780e7ce | ||
|
|
cd2995f183 | ||
|
|
0ec91151ca | ||
|
|
4319ead55c | ||
|
|
b0ab8e7bc3 | ||
|
|
46732bf415 | ||
|
|
62e18ec4ce | ||
|
|
7f384e348c | ||
|
|
584da94d04 | ||
|
|
5ed89b7440 | ||
|
|
4fb65c7b7a | ||
|
|
12ea59f808 | ||
|
|
f618c49a15 | ||
|
|
5b8396b576 | ||
|
|
afe6b178f8 | ||
|
|
63b060962b | ||
|
|
1c9ff066e4 | ||
|
|
c10ea31688 | ||
|
|
bd456102d2 | ||
|
|
a7f2cb8e33 | ||
|
|
805db3604d | ||
|
|
7462f9108b | ||
|
|
91000bc953 | ||
|
|
d935ffe4fb | ||
|
|
d2569d3501 | ||
|
|
ce79f6a949 | ||
|
|
5ecc8197f7 | ||
|
|
858728ae1f | ||
|
|
7df40e81e5 | ||
|
|
5512036527 | ||
|
|
aa33a63d3d | ||
|
|
95e97453f6 | ||
|
|
0a0873fe54 | ||
|
|
43a8da1c1a | ||
|
|
3bc93d10c8 | ||
|
|
5ebb9c4a18 | ||
|
|
e2e8b4df15 | ||
|
|
901c00c067 | ||
|
|
b25b0d7552 | ||
|
|
1a3a559a67 | ||
|
|
52f40ed5eb | ||
|
|
5b6d05339c | ||
|
|
cc0663f39e | ||
|
|
fd35d2873f | ||
|
|
db6ea0ab0c | ||
|
|
31a6454462 | ||
|
|
53d5cbafb5 | ||
|
|
f334cb0d3e |
@@ -1,7 +1,9 @@
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/node,python,react
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node,python,react
|
||||
|
||||
workflow_templates/
|
||||
js/output.css
|
||||
*.task
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
|
||||
@@ -1,44 +1,170 @@
|
||||
# avatar-graph-comfyui
|
||||
|
||||
This introduce a custom workflow on top of comfyui built for avatar-graph.
|
||||

|
||||
|
||||
Wanna animate or got a question? Join our [Discord](https://discord.gg/Xp6mZ4Ez5P)
|
||||
|
||||
A custom nodes module for **creating real-time interactive avatars** powered by blender bpy mesh api + Avatech Shape Flow runtime.
|
||||
|
||||
> **WARNING**
|
||||
> We are still making changes to the nodes and demo templates, please stay tuned.
|
||||
|
||||
# Demo
|
||||
|
||||
| <img src="https://github.com/avatechai/avatar-graph-comfyui/assets/73209427/12e2bfc6-438e-4d16-bead-9957ced3bae1" width="180"/><br>[Interact 👆](https://editor.avatech.ai/viewer?avatarId=cce15b92-6d1c-4966-91b9-362d7833cb5d) | <img src="https://github.com/avatechai/avatar-graph-comfyui/assets/73209427/0c497025-7ed5-4e25-b4d1-5a257e1ba814" width="180"/><br>[Interact 👆](https://editor.avatech.ai/viewer?avatarId=42a8182f-b140-48c0-a556-35cddf0f76f7) | <img src="https://github.com/avatechai/avatar-graph-comfyui/assets/73209427/a2bf71e3-0d9c-4ddd-957f-a6b0cb7e622a" width="180"/><br>[Interact 👆](https://editor.avatech.ai/viewer?avatarId=7c23b8d6-d1a5-41c7-a084-250461dbef22) | <img src="https://github.com/avatechai/avatar-graph-comfyui/assets/73209427/ad808c42-5297-4e61-8be8-d5cb7729d2ff" width="180"/><br>[Interact 👆](https://editor.avatech.ai/viewer?avatarId=268b32c4-f9b9-4db8-a27c-a7e974f0f0ac) |
|
||||
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|
||||
| <img src="https://github.com/avatechai/avatar-graph-comfyui/assets/73209427/1d1ad8f9-31a6-48ec-bad2-ce972ee3b12f" width="180"/><br>[Interact 👆](https://editor.avatech.ai/viewer?avatarId=f97fc5bb-93b0-4b02-bbc0-327dd41d0fc5) | <img src="https://github.com/avatechai/avatar-graph-comfyui/assets/73209427/06958585-f780-4b38-8f5d-bddabd7da78a" width="180"/><br>[Interact 👆](https://editor.avatech.ai/viewer?avatarId=4d50aa03-26e4-47e7-97b6-c3fe9d8fc96e) | <img src="https://github.com/avatechai/avatar-graph-comfyui/assets/73209427/3d0e6b54-d45f-45ac-90bd-d8b149880f98" width="180"/><br>[Interact 👆](https://editor.avatech.ai/viewer?avatarId=791014cb-7836-4641-afdb-ac331064b682) | <img src="https://github.com/avatechai/avatar-graph-comfyui/assets/73209427/1d1ad8f9-31a6-48ec-bad2-ce972ee3b12f" width="180"/><br>[Interact 👆](https://editor.avatech.ai/viewer?avatarId=f97fc5bb-93b0-4b02-bbc0-327dd41d0fc5) |
|
||||
|
||||
# How to?
|
||||
|
||||
- [Basic Rigging Workflow Template](#basic-rigging-workflow-template)
|
||||
- [Best Practices for image input](#best-practices-for-image-input)
|
||||
- [Custom Nodes List](#custom-nodes)
|
||||
- [Shape Flow](#shape-flow)
|
||||
- [Installation](#installation)
|
||||
- [Development](#development)
|
||||
- [Join Discord 💬](https://discord.gg/WNtBYksDwF)
|
||||
|
||||
# Basic Rigging Workflow Template
|
||||
|
||||
### 1. Creating an eye blink and lipsync avatar
|
||||
|
||||

|
||||
|
||||
For optimal results, please input a character image with an open mouth and a minimum resolution of 768x768. This higher resolution will enable the tool to accurately recognize and work with facial features.
|
||||
|
||||
Download: Save the image, and drag into Comfyui or [Simple Shape Flow](https://github.com/avatechai/avatar-graph-comfyui/blob/main/workflow_templates/SimpleEye+MouthMovement.json)
|
||||
|
||||
### 2. Creating an eye blink and lipsync emoji avatar
|
||||
|
||||
|  |  |
|
||||
| :--: | :--: |
|
||||
|
||||
Download: Save the image, and drag into Comfyui
|
||||
|
||||
|  |  |
|
||||
| :--: | :--: |
|
||||
|
||||
Download: Save the image, and drag into Comfyui or [Dog Workflow](https://github.com/avatechai/avatar-graph-comfyui/blob/main/workflow_templates/Dog_workflow.json)
|
||||
|
||||
# Best practices for image input
|
||||
|
||||
### 1. Generate a new character image
|
||||
|
||||

|
||||
|
||||
We need a character image with an open mouth and enable the tool to easily recognize facial features, so please add to the prompt:
|
||||
`looking at viewer, detailed face, open mouth, [smile], solo,eye-level angle`
|
||||
|
||||
Download: [Character Gen Template](https://github.com/avatechai/avatar-graph-comfyui/blob/main/workflow_templates/SimpleCharacterGen.json)
|
||||
|
||||
### 2. Make existing character image mouth open (Inpaint)
|
||||
|
||||

|
||||
|
||||
To maintain consistency with the base image, it is recommended to utilize a checkpoint model that aligns with its style.
|
||||
|
||||
Download: [Mouth Open Inpaint Template](<https://github.com/avatechai/avatar-graph-comfyui/blob/main/workflow_templates/MouthOpen_(inpaint).json>)
|
||||
|
||||
<details>
|
||||
<summary> Inpaint Demonstration </summary>
|
||||
|
||||
<video src="https://github.com/avatechai/avatar-graph-comfyui/assets/73209427/e3b77295-a1bf-4d96-9551-7cc423a4af73"/>
|
||||
|
||||
</details>
|
||||
|
||||
### 3. Pose Constraints (ControlNet)
|
||||
|
||||

|
||||
|
||||
Place normal and openpose image with reference to images.
|
||||
|
||||
Download: [ControlNet Gen](https://github.com/avatechai/avatar-graph-comfyui/tree/main/workflow_templates/TemplateGen01)
|
||||
|
||||
|
||||
# Recommend Checkpoint Model List
|
||||
|
||||
##### Anime Style SD1.5
|
||||
|
||||
- https://civitai.com/models/35960/flat-2d-animerge
|
||||
- https://civitai.com/models/24149/mistoonanime
|
||||
|
||||
##### Realistic Style SD1.5
|
||||
|
||||
- https://civitai.com/models/4201/realistic-vision-v51
|
||||
- https://civitai.com/models/49463/am-i-real
|
||||
- https://civitai.com/models/43331/majicmix-realistic
|
||||
|
||||
# Custom Nodes
|
||||
|
||||
Expand to see all the available nodes description.
|
||||
|
||||
<details>
|
||||
<summary> All Custom Nodes </summary>
|
||||
|
||||
| Name | Description | Preview |
|
||||
| -------------------- | ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `Segmentation (SAM)` | Integrative SAM node allowing you to directly select and create multiple image segment output. | <img src="https://avatech-avatar-dev1.nyc3.cdn.digitaloceanspaces.com/public-download/github-readme/270576351-8aabeba8-5450-4d39-8203-e91f9ab47190.png" width="300"> |
|
||||
|
||||
|
||||
| Name | Description | Preview |
|
||||
| ---------------------------- | ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `Create Mesh Layer` | Create a mesh object from the input images (usually a segmented part of the entire image) | <img src="https://avatech-avatar-dev1.nyc3.cdn.digitaloceanspaces.com/public-download/github-readme/270576646-40740d25-9411-4cd3-a6c0-8b9008bca41c.png" width="300"> |
|
||||
| `Join Meshes` | Combine multiple meshes into a single mesh object | <img src="https://avatech-avatar-dev1.nyc3.cdn.digitaloceanspaces.com/public-download/github-readme/270577004-ba7afbc5-9cd5-4f97-9614-f71133f5783e.png" width="300"> |
|
||||
| `Match Texture Aspect Ratio` | Since the mesh is created in 1:1 aspect ratio, a re-scale is needed at the end of the operation | <img src="https://avatech-avatar-dev1.nyc3.cdn.digitaloceanspaces.com/public-download/github-readme/270602782-cb7155be-fb31-49f8-a24a-d001a1484ea7.png" width="300"> |
|
||||
| `Plane Texture Unwrap` | Will perform mesh face fill and UV Cube project on the target plane mesh, scaled to bounds. | <img src="https://avatech-avatar-dev1.nyc3.cdn.digitaloceanspaces.com/public-download/github-readme/270603006-4b9c0cf5-0497-47bf-8e06-5a3370084c11.png" width="300"> |
|
||||
|
||||
| Name | Description | Preview |
|
||||
| ----------------------- | -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `Mesh Modify Shape Key` | Given shape key name & target vertex_group, modify the vertex / all vertex’s transform | <img src="https://avatech-avatar-dev1.nyc3.cdn.digitaloceanspaces.com/public-download/github-readme/270577944-ab4f259c-89a7-4f51-bc54-fd179e252073.png" width="300"> |
|
||||
| `Create Shape Flow` | Create runtime shape flow graph, allowing interactive inputs affecting shape keys value in runtime | <img src="https://avatech-avatar-dev1.nyc3.cdn.digitaloceanspaces.com/public-download/github-readme/270592752-abfdd801-0387-4c5d-9c11-6c23337ff1dd.png" width="300"> |
|
||||
|
||||
| Name | Description | Preview |
|
||||
| -------------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `Avatar Main Output` | The primary output of the .ava file. The embedded Avatar View will auto update with this node's output | <img src="https://avatech-avatar-dev1.nyc3.cdn.digitaloceanspaces.com/public-download/github-readme/270592519-6a9a8bb4-05ec-4a2e-98bf-194b6af3a62a.png" width="300"> |
|
||||
|
||||
</details>
|
||||
|
||||
# Shape Flow
|
||||
|
||||

|
||||
|
||||
# Installation
|
||||
|
||||
## Method 1 - Windows
|
||||
|
||||
1. Download Python environment from [here](https://avatech-avatar-dev1.nyc3.digitaloceanspaces.com/comfyui/ComfyUI_3.10.7z)
|
||||
|
||||
2. Unzip it to ComfyUI directory
|
||||
|
||||
3. Run the `run_cpu_3.10.bat` or `run_nvidia_gpu_3.10.bat`
|
||||
|
||||
4. Install avatar-graph-comfyui from [ComfyUI Manager](https://github.com/ltdrdata/ComfyUI-Manager)
|
||||
|
||||
## Method 2 - macOS/Linux
|
||||
|
||||
Make sure your Python environment is `3.10.x` as required by the [bpy](https://pypi.org/project/bpy/) package. Then go to the [ComfyUI](https://github.com/comfyanonymous/ComfyUI) directory and run:
|
||||
|
||||
> Suggest using conda for your comfyui python environment
|
||||
>
|
||||
> `conda create --name comfyui python=3.10`
|
||||
>
|
||||
> `conda activate comfyui`
|
||||
>
|
||||
> `pip install -r requirements.txt`
|
||||
|
||||
1. `cd custom_nodes`
|
||||
|
||||
2. `git clone https://github.com/avatechgg/avatar-graph-comfyui.git`
|
||||
|
||||
3. Install deps `cd avatar-graph-comfyui && python -m pip install -r requirements.txt`
|
||||
3. `cd avatar-graph-comfyui && python -m pip install -r requirements.txt`
|
||||
|
||||
4. Navigate to in your comfyui installation folder, open `comfyui/main.py` and add the following lines:
|
||||
```py
|
||||
def prompt_worker(q, server):
|
||||
e = execution.PromptExecutor(server)
|
||||
|
||||
# This is a workaround since using blender as a python module, the bpy module has to be imported after in the custom thread, otherwise it will cause a segfault if imported in the custom nodes.
|
||||
|
||||
# Add next line
|
||||
import global_bpy
|
||||
|
||||
while True:
|
||||
item, item_id = q.get()
|
||||
# Add next line
|
||||
global_bpy.reset_bpy()
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Add next two lines
|
||||
import sys
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), "custom_nodes", "avatar-graph-comfyui"))
|
||||
|
||||
|
||||
```
|
||||
|
||||
5. Restart comfyui
|
||||
|
||||
6. Run comfyui with enable-cors-header `python main.py --enable-cors-header` or (mac)`python main.py --force-fp16 --enable-cors-header`
|
||||
4. Restart ComfyUI with enable-cors-header `python main.py --enable-cors-header` or (for mac) `python main.py --force-fp16 --enable-cors-header`
|
||||
|
||||
# Development
|
||||
|
||||
If you are interested in contributing
|
||||
|
||||
For comfyui frontend extension, frontend js located at `avatar-graph-comfyui/js`
|
||||
|
||||
Web stack used: [vanjs](https://github.com/vanjs-org/van) [tailwindcss](https://github.com/tailwindlabs/tailwindcss)
|
||||
@@ -57,12 +183,31 @@ pnpm dev
|
||||
|
||||
For each changes, simply refresh the comfyui page to see the changes.
|
||||
|
||||
p.s. For tailwind autocomplete, add the following to your vscode settings.json.
|
||||
|
||||
<details>
|
||||
<summary>p.s. For tailwind autocomplete, add the following to your vscode settings.json.</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"tailwindCSS.experimental.classRegex": [
|
||||
["class\\s?:\\s?([\\s\\S]*)", "(?:\"|')([^\"']*)(?:\"|')"]
|
||||
]
|
||||
}
|
||||
```
|
||||
```
|
||||
</details>
|
||||
|
||||
## Update blender node types
|
||||
|
||||
To update blender operations input and output types (stored in `blender/input_types.txt`), run:
|
||||
|
||||
```bash
|
||||
python generate_blender_types.py
|
||||
```
|
||||
|
||||
# FAQ
|
||||
|
||||
## What is `--enable-cors-header` used for?
|
||||
It is used to enable communication between ComfyUI and our editor (https://editor.avatech.ai), which is in charge of animating static characters. The only messages exchanged between them are the character data like the meshes of eyes and mouth, and the JSON format of our editor graph.
|
||||
|
||||
When you execute the ComfyUI graph, it sends the character data and the JSON graph to our editor for animating. When you modify and save the graph in our editor, it sends the modified graph back to ComfyUI. To validate it, you can open the `js/index.js`, and log the message in `window.addEventListener("message", ...)` and `postMessage(message)`.
|
||||
|
||||
You can also run ComfyUI *without* the `--enable-cors-header`: execute the ComfyUI workflow, then download the .GLB or .GLTF format by right clicking the Avatar Main Output node and Save File option. Yet, this will disable the real-time character preview in the top-right corner of ComfyUI. Feel free to view it in other software like Blender.
|
||||
|
||||
+74
-18
@@ -4,14 +4,20 @@
|
||||
@nickname: Avatar Graph
|
||||
@description: Include nodes for sam + bpy operation, that allows workflow creations for generative 2d character rig.
|
||||
"""
|
||||
import routes
|
||||
|
||||
import inspect
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__)))
|
||||
|
||||
import routes
|
||||
import inspect
|
||||
import sys
|
||||
import importlib
|
||||
import subprocess
|
||||
from folder_paths import add_model_folder_path
|
||||
import requests
|
||||
import folder_paths
|
||||
from folder_paths import add_model_folder_path, get_filename_list, get_folder_paths
|
||||
from tqdm import tqdm
|
||||
|
||||
ag_path = os.path.join(os.path.dirname(__file__))
|
||||
|
||||
@@ -30,7 +36,7 @@ ag_path = os.path.join(os.path.dirname(__file__))
|
||||
|
||||
|
||||
def get_python_files(path):
|
||||
return [f[:-3] for f in os.listdir(path) if f.endswith('.py')]
|
||||
return [f[:-3] for f in os.listdir(path) if f.endswith(".py")]
|
||||
|
||||
|
||||
def append_to_sys_path(path):
|
||||
@@ -38,16 +44,67 @@ def append_to_sys_path(path):
|
||||
sys.path.append(path)
|
||||
|
||||
|
||||
def create_sam_model_dir():
|
||||
model_dir = os.path.join(ag_path, "../../models/sam")
|
||||
folder_paths.folder_names_and_paths["sams"] = (
|
||||
[os.path.join(folder_paths.models_dir, "sams")],
|
||||
folder_paths.supported_pt_extensions,
|
||||
)
|
||||
|
||||
|
||||
def download_model(url, save_path):
|
||||
response = requests.get(url, stream=True)
|
||||
response.raise_for_status()
|
||||
file_size = int(response.headers.get("Content-Length", 0))
|
||||
chunk_size = 1024
|
||||
num_bars = int(file_size / chunk_size)
|
||||
|
||||
with open(save_path, "wb") as f:
|
||||
for chunk in tqdm(
|
||||
response.iter_content(chunk_size=chunk_size),
|
||||
total=num_bars,
|
||||
unit="KB",
|
||||
desc=url.split("/")[-1],
|
||||
):
|
||||
f.write(chunk)
|
||||
|
||||
|
||||
def download_sam_model():
|
||||
model_dir = get_folder_paths("sams")[0]
|
||||
if not os.path.isdir(model_dir):
|
||||
os.makedirs(model_dir)
|
||||
add_model_folder_path('sam', model_dir)
|
||||
|
||||
add_model_folder_path("sams", model_dir)
|
||||
|
||||
files = get_filename_list("sams")
|
||||
if "sam_vit_h_4b8939.pth" not in files:
|
||||
print("Downloading sam model...")
|
||||
url = "https://dl.fbaipublicfiles.com/segment_anything/sam_vit_h_4b8939.pth"
|
||||
download_model(url, f"{model_dir}/sam_vit_h_4b8939.pth")
|
||||
|
||||
|
||||
create_sam_model_dir()
|
||||
download_sam_model()
|
||||
|
||||
paths = ['blender', 'sam', 'common']
|
||||
|
||||
def download_face_and_pose_landmarker():
|
||||
model_dir = os.path.join(ag_path, "mediapipe_models")
|
||||
if not os.path.isdir(model_dir):
|
||||
os.makedirs(model_dir)
|
||||
|
||||
model_path = os.path.join(model_dir, "face_landmarker.task")
|
||||
if not os.path.isfile(model_path):
|
||||
print("Downloading face landmarker model...")
|
||||
url = "https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task"
|
||||
download_model(url, model_path)
|
||||
|
||||
model_path = os.path.join(model_dir, "pose_landmarker_full.task")
|
||||
if not os.path.isfile(model_path):
|
||||
print("Downloading pose landmarker model...")
|
||||
url = "https://storage.googleapis.com/mediapipe-models/pose_landmarker/pose_landmarker_full/float16/latest/pose_landmarker_full.task"
|
||||
download_model(url, model_path)
|
||||
|
||||
|
||||
download_face_and_pose_landmarker()
|
||||
|
||||
paths = ["blender", "sam"]
|
||||
files = []
|
||||
|
||||
for path in paths:
|
||||
@@ -59,6 +116,7 @@ NODE_CLASS_MAPPINGS = {}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {}
|
||||
|
||||
import blender_node
|
||||
|
||||
base_class = blender_node.ObjectOps
|
||||
|
||||
# Import all the modules and append their mappings
|
||||
@@ -69,22 +127,20 @@ for file in files:
|
||||
if inspect.isclass(obj):
|
||||
if issubclass(obj, base_class) and obj != base_class:
|
||||
NODE_CLASS_MAPPINGS.update(obj.NODE_CLASS_MAPPINGS())
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(
|
||||
obj.NODE_DISPLAY_NAME_MAPPINGS())
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(obj.NODE_DISPLAY_NAME_MAPPINGS())
|
||||
|
||||
if (hasattr(module, 'BLENDER_NODES')):
|
||||
if hasattr(module, "BLENDER_NODES"):
|
||||
for node in module.BLENDER_NODES:
|
||||
# print(node)
|
||||
# NODE_CLASS_MAPPINGS.update({node: module.BLENDER_NODES[node]})
|
||||
# NODE_DISPLAY_NAME_MAPPINGS.update({node: node})
|
||||
NODE_CLASS_MAPPINGS.update(node.NODE_CLASS_MAPPINGS())
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(
|
||||
node.NODE_DISPLAY_NAME_MAPPINGS())
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(node.NODE_DISPLAY_NAME_MAPPINGS())
|
||||
|
||||
if hasattr(module, 'NODE_CLASS_MAPPINGS'):
|
||||
if hasattr(module, "NODE_CLASS_MAPPINGS"):
|
||||
NODE_CLASS_MAPPINGS.update(module.NODE_CLASS_MAPPINGS)
|
||||
if hasattr(module, 'NODE_DISPLAY_NAME_MAPPINGS'):
|
||||
if hasattr(module, "NODE_DISPLAY_NAME_MAPPINGS"):
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(module.NODE_DISPLAY_NAME_MAPPINGS)
|
||||
|
||||
WEB_DIRECTORY = "js"
|
||||
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
|
||||
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import platform
|
||||
import blender_node
|
||||
from mesh_utils import assign_texture, open_in_blender as open_blender, export_gltf
|
||||
from mesh_utils import upload_avatar_file, open_in_blender as open_blender, export_gltf
|
||||
import folder_paths
|
||||
|
||||
global_blender_path = ''
|
||||
@@ -33,6 +33,9 @@ class AvatarMainOutput(blender_node.ObjectOps):
|
||||
"open_in_blender": ("BOOLEAN", {
|
||||
"default": False
|
||||
}),
|
||||
"auto_save": ("BOOLEAN", {
|
||||
"default": False
|
||||
}),
|
||||
"blender_path_override": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": ''
|
||||
@@ -42,16 +45,18 @@ class AvatarMainOutput(blender_node.ObjectOps):
|
||||
"multiline": False,
|
||||
"default": "out"
|
||||
}),
|
||||
"model_type": (["GLB", "GLTF_EMBEDDED"],),
|
||||
"model_type": (["AVA","GLB", "GLTF_EMBEDDED"],),
|
||||
"write_mode": (["Overwrite", "Increment"],),
|
||||
|
||||
"blendshapes": ("blendshapes",),
|
||||
"upload_to_cloud": ("BOOLEAN", {
|
||||
"default": False
|
||||
}),
|
||||
"SHAPE_FLOW": ("SHAPE_FLOW",),
|
||||
}
|
||||
|
||||
OUTPUT_NODE = True
|
||||
RETURN_TYPES = ()
|
||||
|
||||
def blender_process(self, bpy, BPY_OBJ, BPY_OBJS=None, open_in_blender=False, blender_path_override='', filename='', model_type='', write_mode='', blendshapes=''):
|
||||
def blender_process(self, bpy, BPY_OBJ=None, BPY_OBJS=None, open_in_blender=False, auto_save=False, blender_path_override='', filename='', model_type='', write_mode='', upload_to_cloud=False, SHAPE_FLOW=''):
|
||||
if open_in_blender:
|
||||
p = blender_path_override if blender_path_override else global_blender_path
|
||||
output_file = self.output_dir + '/tmp.blend'
|
||||
@@ -63,7 +68,29 @@ class AvatarMainOutput(blender_node.ObjectOps):
|
||||
objs = BPY_OBJS if BPY_OBJS else [BPY_OBJ]
|
||||
|
||||
filepath = export_gltf(
|
||||
self.output_dir, objs, filename, model_type, write_mode
|
||||
self.output_dir, objs, filename, model_type, write_mode, SHAPE_FLOW
|
||||
)
|
||||
|
||||
return {"ui": {"gltfFilename": {filepath.replace(f"{self.output_dir}/", "")}, "blendshapes": {blendshapes}}}
|
||||
import global_bpy
|
||||
global_bpy.set_should_reset_scene(True)
|
||||
|
||||
outputs = {
|
||||
"gltfFilename": [filepath.replace(f"{self.output_dir}/", "")],
|
||||
"files": [{
|
||||
"filename": filepath.replace(f"{self.output_dir}/", ""),
|
||||
"content_type": "model/gltf+json",
|
||||
},],
|
||||
"SHAPE_FLOW": {SHAPE_FLOW},
|
||||
"auto_save": {'true' if auto_save else 'false'},
|
||||
}
|
||||
if upload_to_cloud:
|
||||
avatarId = upload_avatar_file(outputs)
|
||||
return {
|
||||
"ui": {
|
||||
**outputs,
|
||||
"avatarId": [avatarId],
|
||||
},
|
||||
}
|
||||
return {
|
||||
"ui": outputs
|
||||
}
|
||||
|
||||
@@ -5,17 +5,17 @@ class VECTOR3D:
|
||||
"required": {
|
||||
"x": ("FLOAT", {
|
||||
"default": 0,
|
||||
"step": 0.1,
|
||||
"step": 0.01,
|
||||
"display": "number"
|
||||
}),
|
||||
"y": ("FLOAT", {
|
||||
"default": 0,
|
||||
"step": 0.1,
|
||||
"step": 0.01,
|
||||
"display": "number"
|
||||
}),
|
||||
"z": ("FLOAT", {
|
||||
"default": 0,
|
||||
"step": 0.1,
|
||||
"step": 0.01,
|
||||
"display": "number"
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -5,17 +5,17 @@ class VECTOR4D:
|
||||
"required": {
|
||||
"x": ("FLOAT", {
|
||||
"default": 0,
|
||||
"step": 0.1,
|
||||
"step": 0.01,
|
||||
"display": "number"
|
||||
}),
|
||||
"y": ("FLOAT", {
|
||||
"default": 0,
|
||||
"step": 0.1,
|
||||
"step": 0.01,
|
||||
"display": "number"
|
||||
}),
|
||||
"z": ("FLOAT", {
|
||||
"default": 0,
|
||||
"step": 0.1,
|
||||
"step": 0.01,
|
||||
"display": "number"
|
||||
}),
|
||||
"u": ("FLOAT", {
|
||||
|
||||
+41
-16
@@ -1,5 +1,8 @@
|
||||
import inspect
|
||||
import re
|
||||
import json
|
||||
import os
|
||||
|
||||
BPY_OBJS = "BPY_OBJS"
|
||||
BPY_OBJ = "BPY_OBJ"
|
||||
|
||||
@@ -7,6 +10,14 @@ BPY_OBJS_TYPE = {
|
||||
BPY_OBJS: (BPY_OBJS,),
|
||||
}
|
||||
|
||||
node_input_types = {}
|
||||
with open(f"{os.path.dirname(__file__)}/input_types.txt") as f:
|
||||
input_types = f.readlines()
|
||||
for input_type in input_types:
|
||||
node_cls, node_types = input_type.split("|")
|
||||
node_input_types[node_cls] = json.loads(node_types)
|
||||
|
||||
type_generation = os.getenv('TYPE_GENERATION', 0)
|
||||
|
||||
class ObjectOps:
|
||||
@classmethod
|
||||
@@ -38,16 +49,29 @@ class ObjectOps:
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
result = {
|
||||
"required": {},
|
||||
"optional": {
|
||||
**cls.get_base_input_types(bpy),
|
||||
**cls.get_extra_input_types(bpy)
|
||||
if type_generation:
|
||||
import global_bpy
|
||||
|
||||
bpy = global_bpy.get_bpy()
|
||||
result = {
|
||||
"required": {},
|
||||
"optional": {
|
||||
**cls.get_base_input_types(bpy),
|
||||
**cls.get_extra_input_types(bpy),
|
||||
},
|
||||
}
|
||||
|
||||
return result
|
||||
elif cls.__name__ in node_input_types:
|
||||
return node_input_types[cls.__name__]
|
||||
else:
|
||||
return {
|
||||
"required": {},
|
||||
"optional": {
|
||||
**cls.get_base_input_types(None),
|
||||
**cls.get_extra_input_types(None),
|
||||
},
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def NODE_CLASS_MAPPINGS(cls):
|
||||
@@ -76,14 +100,14 @@ class ObjectOps:
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
if props.get("BPY_OBJ") != None:
|
||||
if props.get("BPY_OBJ") is not None:
|
||||
bpy.context.view_layer.objects.active = props["BPY_OBJ"]
|
||||
|
||||
results = self.blender_process(bpy, **props)
|
||||
|
||||
if results is None:
|
||||
print(results)
|
||||
if props.get("BPY_OBJ") != None:
|
||||
# print(results)
|
||||
if props.get("BPY_OBJ") is not None:
|
||||
return (props["BPY_OBJ"], )
|
||||
else:
|
||||
return (bpy.context.view_layer.objects.active, )
|
||||
@@ -190,12 +214,12 @@ def print_blender_functions(path):
|
||||
if ag_path not in sys.path:
|
||||
sys.path.append(ag_path)
|
||||
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
# import global_bpy
|
||||
# bpy = global_bpy.get_bpy()
|
||||
|
||||
# add a an empty object
|
||||
|
||||
print(dir(get_nested_attr(bpy, path)), type(get_nested_attr(bpy, path)))
|
||||
# print(dir(get_nested_attr(bpy, path)), type(get_nested_attr(bpy, path)))
|
||||
|
||||
|
||||
def create_ops_class(cls, path, name=None, name_prefix=''):
|
||||
@@ -236,7 +260,8 @@ def create_primitive_shape_class(cls, path, name=None, name_prefix=''):
|
||||
|
||||
|
||||
def assign_and_return(BPY_OBJ, name, value):
|
||||
BPY_OBJ[name] = value
|
||||
setattr(BPY_OBJ, name, value)
|
||||
# BPY_OBJ[name] = value
|
||||
# print(BPY_OBJ,name, BPY_OBJ[name])
|
||||
return None
|
||||
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
class CombineMesh:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"obj_1": ("BPY_OBJS",),
|
||||
"obj_2": ("BPY_OBJS",),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, obj_1, obj_2):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
override = bpy.context.copy()
|
||||
override["active_object"] = obj_1[0]
|
||||
override["selected_editable_objects"] = list([*obj_1, *obj_2])
|
||||
bpy.ops.object.join(override)
|
||||
joined_object = override["active_object"]
|
||||
return ([joined_object], )
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"CombineMesh": CombineMesh
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"CombineMesh": "Combine Mesh"
|
||||
}
|
||||
@@ -4,21 +4,20 @@ class CreateShapeFlow():
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"blendshapes": ("STRING", {
|
||||
"shape_flow": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": "{node: ''}"
|
||||
"default": '{"nodes": []}'
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("blendshapes",)
|
||||
RETURN_NAMES = ("blendshapes",)
|
||||
RETURN_TYPES = ("SHAPE_FLOW",)
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, blendshapes):
|
||||
return (blendshapes,)
|
||||
def process(self, shape_flow):
|
||||
return (shape_flow,)
|
||||
|
||||
# A dictionary that contains all nodes you want to export with their names
|
||||
# NOTE: names should be globally unique
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
class CreateVertexBounds():
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs_target": ("BPY_OBJS",),
|
||||
"name": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": "Group"
|
||||
}),
|
||||
"extrude": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01, "display": "number"}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs_target, name, extrude):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
if len(bpy_objs_target) == 0:
|
||||
# throw error
|
||||
return
|
||||
|
||||
target_object = bpy_objs_target[0]
|
||||
|
||||
# deselect all objects
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
# select only the target object
|
||||
bpy.context.view_layer.objects.active = target_object
|
||||
# enter enter edit mode and select all faces of the object to fill
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
|
||||
bpy.ops.mesh.extrude_region_move(MESH_OT_extrude_region={
|
||||
"use_normal_flip": False, "use_dissolve_ortho_edges": False, "mirror": False})
|
||||
|
||||
bpy.ops.transform.resize(
|
||||
value=(extrude, extrude, 1), constraint_axis=(False, False, False))
|
||||
|
||||
# remove the extruded vertex from the vertex group "name"
|
||||
|
||||
bpy.ops.object.vertex_group_remove_from()
|
||||
|
||||
# bpy.ops.mesh.delete(type='FACE')
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
return ([target_object],)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"CreateVertexBounds": CreateVertexBounds
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"CreateVertexBounds": "Vertex Bounds"
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
class CreateVertexGroup():
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs_target": ("BPY_OBJS",),
|
||||
"name": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": "Group"
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs_target, name):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
if len(bpy_objs_target) == 0:
|
||||
# throw error
|
||||
return
|
||||
|
||||
target_object = bpy_objs_target[0]
|
||||
|
||||
# deselect all objects
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
# select only the target object
|
||||
bpy.context.view_layer.objects.active = target_object
|
||||
# enter enter edit mode and select all faces of the object to fill
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
|
||||
# create vertex group and assign all selected faces to it (the name of the vertex group is the same as the name of the object)
|
||||
bpy.context.object.vertex_groups.new(name=name)
|
||||
bpy.ops.object.vertex_group_assign()
|
||||
|
||||
# bpy.ops.mesh.delete(type='FACE')
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
return ([target_object],)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"CreateVertexGroup": CreateVertexGroup
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"CreateVertexGroup": "Vertex Group (Old)"
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
class DissolveFaces():
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs_target": ("BPY_OBJS",),
|
||||
"target_vertex_group": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": ""
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs_target, target_vertex_group):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
target_object = bpy_objs_target[0]
|
||||
|
||||
# deselect all objects
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
# select only the target object
|
||||
bpy.context.view_layer.objects.active = target_object
|
||||
# enter enter edit mode and select all faces of the object to fill
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
bpy.ops.mesh.select_all(action='DESELECT')
|
||||
|
||||
# if we have vertex group, select that instead
|
||||
if target_vertex_group and target_vertex_group.strip() != "":
|
||||
group = target_object.vertex_groups.get(target_vertex_group)
|
||||
if group:
|
||||
bpy.ops.object.vertex_group_set_active(group=group.name)
|
||||
bpy.ops.object.vertex_group_select()
|
||||
else:
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
|
||||
# dissolve the faces
|
||||
# bpy.ops.mesh.dissolve_faces()
|
||||
bpy.ops.mesh.delete(type='ONLY_FACE')
|
||||
# bpy.ops.mesh.delete(type='FACE')
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
return ([target_object],)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"DissolveFaces": DissolveFaces
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"DissolveFaces": "Dissolve Faces (Old)"
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
class FillFace:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs_target": ("BPY_OBJS",),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs_target):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
target_object = bpy_objs_target[0]
|
||||
|
||||
# deselect all objects
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
# select only the target object
|
||||
bpy.context.view_layer.objects.active = target_object
|
||||
# enter enter edit mode and select all faces of the object to fill
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
|
||||
# fill the faces
|
||||
bpy.ops.mesh.fill()
|
||||
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
return ([target_object],)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"FillFace": FillFace
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"FillFace": "Fill Face (Old)"
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
class GroupObject():
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs_target": ("BPY_OBJS",),
|
||||
"bpy_objs_target_2": ("BPY_OBJS",),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs_target, bpy_objs_target_2):
|
||||
combined_list = bpy_objs_target + bpy_objs_target_2
|
||||
return (combined_list,)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"GroupObject": GroupObject
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"GroupObject": "Group Object"
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
class GroupVertexInside():
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"bpy": ("BPY",),
|
||||
"bpy_objs_target": ("BPY_OBJS",),
|
||||
"name": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": "Group"
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs_target, name):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
target_object = bpy_objs_target[0]
|
||||
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
# deselect all objects
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
# select only the target object
|
||||
bpy.context.view_layer.objects.active = target_object
|
||||
# enter enter edit mode and select all faces of the object to fill
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='DESELECT')
|
||||
|
||||
# select the vetex group's vertices
|
||||
bpy.ops.object.vertex_group_select()
|
||||
|
||||
# get vertex group
|
||||
vertex_group = target_object.vertex_groups[name]
|
||||
|
||||
verts = target_object.data.vertices
|
||||
|
||||
# all_vertices = [(v.co.x, v.co.y, i) for i, v in target_object.data.vertices]
|
||||
|
||||
# get the verts in the group
|
||||
verts_in_group = [v for v in verts if vertex_group.index in [vg.group for vg in v.groups]]
|
||||
|
||||
# get all selected vertices's xy
|
||||
# get the vertices of the vertex group
|
||||
vertex_group_vertices = [(v.co.x, v.co.y) for v in verts_in_group]
|
||||
|
||||
# get all vertices
|
||||
# all_vertices = [(v.co.x, v.co.y) for v in target_object.data.vertices]
|
||||
all_vertices_without_vertex_group = [(v.co.x, v.co.y, i) for i, v in enumerate(target_object.data.vertices) if v not in verts_in_group]
|
||||
|
||||
from scipy.spatial import ConvexHull, Delaunay
|
||||
|
||||
# create a convex hull of the vertex group's vertices
|
||||
hull = ConvexHull(vertex_group_vertices)
|
||||
|
||||
# create a Delaunay triangulation of the convex hull
|
||||
tri = Delaunay(hull.points[hull.vertices])
|
||||
|
||||
# get the indices of the vertices that are inside the convex hull
|
||||
inside_vertices_indices = [i for (x,y,i) in all_vertices_without_vertex_group if tri.find_simplex((x,y))>=0]
|
||||
|
||||
print(inside_vertices_indices)
|
||||
print(len(vertex_group_vertices))
|
||||
|
||||
bpy.ops.mesh.select_all(action='DESELECT')
|
||||
|
||||
# select the vertices that are inside the convex hull
|
||||
# for i in inside_vertices_indices:
|
||||
# target_object.data.vertices[i].select = True
|
||||
|
||||
# # select vertex from 1 to 10
|
||||
# for i in range(1, 100):
|
||||
# target_object.data.vertices[i].select = True
|
||||
|
||||
# create a new vertex group
|
||||
# bpy.context.object.vertex_groups.new(name="fuck")
|
||||
# bpy.ops.object.vertex_group_assign()
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
# create a new vertex group
|
||||
# new_group = bpy.context.object.vertex_groups.new(name="new_group")
|
||||
vertex_group.add(inside_vertices_indices, weight=1.0, type='REPLACE')
|
||||
|
||||
# assign the selected vertices to the new vertex group
|
||||
# new_group.add([v.index for v in target_object.data.vertices if v.select], weight=1.0, type='REPLACE')
|
||||
# new_group.add([20, 100], weight=1.0, type='REPLACE')
|
||||
# assign the selected vertices to the active vertex group
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
|
||||
return ([target_object],)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"GroupVertexInside": GroupVertexInside
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"GroupVertexInside": "Group Vertex Inside"
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import folder_paths
|
||||
import os
|
||||
from PIL import Image, ImageOps
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
import numpy as np
|
||||
import torch
|
||||
import json
|
||||
|
||||
class ImageAlphaMaskMerge:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required":
|
||||
{"image": ("IMAGE",) ,
|
||||
"mask": ("MASK",) },
|
||||
}
|
||||
|
||||
CATEGORY = "image"
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
FUNCTION = "load_image"
|
||||
def load_image(self, image, mask):
|
||||
if image.shape[1] == mask.shape[0] and image.shape[2] == mask.shape[1]:
|
||||
image = torch.cat((image, 1 - mask.unsqueeze(0).unsqueeze(3)), dim=3)
|
||||
return (image, )
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"Image Alpha Mask Merge": ImageAlphaMaskMerge,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"Image Alpha Mask Merge": "Image Alpha Mask Merge"
|
||||
}
|
||||
@@ -0,0 +1,597 @@
|
||||
Mesh_AttributeSet|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value_float": ["FLOAT", {"min": -3.4028234663852886e+38, "max": 3.4028234663852886e+38, "default": 0.0}], "value_float_vector_2d": ["B_VECTOR2", {}], "value_float_vector_3d": ["B_VECTOR3", {}], "value_int": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "value_int_vector_2d": ["B_VECTOR2", {}], "value_color": ["B_VECTOR4", {}], "value_bool": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_AverageNormals|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "average_type": [["CUSTOM_NORMAL", "FACE_AREA", "CORNER_ANGLE"]], "weight": ["INT", {"min": 1, "max": 100, "default": 50}], "threshold": ["FLOAT", {"min": 0.0, "max": 10.0, "default": 0.009999999776482582}]}}
|
||||
Mesh_BeautifyFill|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "angle_limit": ["FLOAT", {"min": 0.0, "max": 3.1415927410125732, "default": 3.1415927410125732}]}}
|
||||
Mesh_Bevel|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "offset_type": [["OFFSET", "WIDTH", "DEPTH", "PERCENT", "ABSOLUTE"]], "offset": ["FLOAT", {"min": 0.0, "max": 1000000.0, "default": 0.0}], "profile_type": [["SUPERELLIPSE", "CUSTOM"]], "offset_pct": ["FLOAT", {"min": 0.0, "max": 100.0, "default": 0.0}], "segments": ["INT", {"min": 1, "max": 1000, "default": 1}], "profile": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.5}], "affect": [["VERTICES", "EDGES"]], "clamp_overlap": ["BOOLEAN", {"default": false}], "loop_slide": ["BOOLEAN", {"default": true}], "mark_seam": ["BOOLEAN", {"default": false}], "mark_sharp": ["BOOLEAN", {"default": false}], "material": ["INT", {"min": -1, "max": 2147483647, "default": -1}], "harden_normals": ["BOOLEAN", {"default": false}], "face_strength_mode": [["NONE", "NEW", "AFFECTED", "ALL"]], "miter_outer": [["SHARP", "PATCH", "ARC"]], "miter_inner": [["SHARP", "ARC"]], "spread": ["FLOAT", {"min": 0.0, "max": 1000000.0, "default": 0.10000000149011612}], "vmesh_method": [["ADJ", "CUTOFF"]], "release_confirm": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_Bisect|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "plane_co": ["B_VECTOR3", {}], "plane_no": ["B_VECTOR3", {}], "use_fill": ["BOOLEAN", {"default": false}], "clear_inner": ["BOOLEAN", {"default": false}], "clear_outer": ["BOOLEAN", {"default": false}], "threshold": ["FLOAT", {"min": 0.0, "max": 10.0, "default": 9.999999747378752e-05}], "xstart": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "xend": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "ystart": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "yend": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "flip": ["BOOLEAN", {"default": false}], "cursor": ["INT", {"min": 0, "max": 2147483647, "default": 5}]}}
|
||||
Mesh_BlendFromShape|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "shape": ["B_ENUM", {}], "blend": ["FLOAT", {"min": -1000.0, "max": 1000.0, "default": 1.0}], "add": ["BOOLEAN", {"default": true}]}}
|
||||
Mesh_BridgeEdgeLoops|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["SINGLE", "CLOSED", "PAIRS"]], "use_merge": ["BOOLEAN", {"default": false}], "merge_factor": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.5}], "twist_offset": ["INT", {"min": -1000, "max": 1000, "default": 0}], "number_cuts": ["INT", {"min": 0, "max": 1000, "default": 0}], "interpolation": [["LINEAR", "PATH", "SURFACE"]], "smoothness": ["FLOAT", {"min": 0.0, "max": 1000.0, "default": 1.0}], "profile_shape_factor": ["FLOAT", {"min": -1000.0, "max": 1000.0, "default": 0.0}], "profile_shape": [["SMOOTH", "SPHERE", "ROOT", "INVERSE_SQUARE", "SHARP", "LINEAR"]]}}
|
||||
Mesh_ColorsReverse|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_ColorsRotate|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_ccw": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_ConvexHull|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "delete_unused": ["BOOLEAN", {"default": true}], "use_existing_faces": ["BOOLEAN", {"default": true}], "make_holes": ["BOOLEAN", {"default": false}], "join_triangles": ["BOOLEAN", {"default": true}], "face_threshold": ["FLOAT", {"min": 0.0, "max": 3.1415927410125732, "default": 0.6981316804885864}], "shape_threshold": ["FLOAT", {"min": 0.0, "max": 3.1415927410125732, "default": 0.6981316804885864}], "uvs": ["BOOLEAN", {"default": false}], "vcols": ["BOOLEAN", {"default": false}], "seam": ["BOOLEAN", {"default": false}], "sharp": ["BOOLEAN", {"default": false}], "materials": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_CustomdataBevelWeightEdgeAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_CustomdataBevelWeightEdgeClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_CustomdataBevelWeightVertexAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_CustomdataBevelWeightVertexClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_CustomdataCreaseEdgeAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_CustomdataCreaseEdgeClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_CustomdataCreaseVertexAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_CustomdataCreaseVertexClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_CustomdataCustomSplitnormalsAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_CustomdataCustomSplitnormalsClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_CustomdataMaskClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_CustomdataSkinAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_CustomdataSkinClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_Decimate|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "ratio": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 1.0}], "use_vertex_group": ["BOOLEAN", {"default": false}], "vertex_group_factor": ["FLOAT", {"min": 0.0, "max": 1000.0, "default": 1.0}], "invert_vertex_group": ["BOOLEAN", {"default": false}], "use_symmetry": ["BOOLEAN", {"default": false}], "symmetry_axis": [["X", "Y", "Z"]]}}
|
||||
Mesh_Delete|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["VERT", "EDGE", "FACE", "EDGE_FACE", "ONLY_FACE"]]}}
|
||||
Mesh_DeleteEdgeloop|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_face_split": ["BOOLEAN", {"default": true}]}}
|
||||
Mesh_DeleteLoose|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_verts": ["BOOLEAN", {"default": true}], "use_edges": ["BOOLEAN", {"default": true}], "use_faces": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_DissolveDegenerate|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "threshold": ["FLOAT", {"min": 9.999999974752427e-07, "max": 50.0, "default": 9.999999747378752e-05}]}}
|
||||
Mesh_DissolveEdges|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_verts": ["BOOLEAN", {"default": true}], "use_face_split": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_DissolveFaces|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_verts": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_DissolveLimited|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "angle_limit": ["FLOAT", {"min": 0.0, "max": 3.1415927410125732, "default": 0.0872664600610733}], "use_dissolve_boundaries": ["BOOLEAN", {"default": false}], "delimit": ["B_ENUM_SET", {}]}}
|
||||
Mesh_DissolveMode|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_verts": ["BOOLEAN", {"default": false}], "use_face_split": ["BOOLEAN", {"default": false}], "use_boundary_tear": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_DissolveVerts|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_face_split": ["BOOLEAN", {"default": false}], "use_boundary_tear": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_DupliExtrudeCursor|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "rotate_source": ["BOOLEAN", {"default": true}]}}
|
||||
Mesh_Duplicate|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mode": ["INT", {"min": 0, "max": 2147483647, "default": 1}]}}
|
||||
Mesh_DuplicateMove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_EdgeCollapse|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_EdgeFaceAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_EdgeRotate|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_ccw": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_EdgeSplit|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["EDGE", "VERT"]]}}
|
||||
Mesh_EdgeringSelect|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "extend": ["BOOLEAN", {"default": false}], "deselect": ["BOOLEAN", {"default": false}], "toggle": ["BOOLEAN", {"default": false}], "ring": ["BOOLEAN", {"default": true}]}}
|
||||
Mesh_EdgesSelectSharp|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "sharpness": ["FLOAT", {"min": 0.00017453292093705386, "max": 3.1415927410125732, "default": 0.5235987901687622}]}}
|
||||
Mesh_ExtrudeContext|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_normal_flip": ["BOOLEAN", {"default": false}], "use_dissolve_ortho_edges": ["BOOLEAN", {"default": false}], "mirror": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_ExtrudeContextMove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_ExtrudeEdgesIndiv|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_normal_flip": ["BOOLEAN", {"default": false}], "mirror": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_ExtrudeEdgesMove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_ExtrudeFacesIndiv|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mirror": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_ExtrudeFacesMove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_ExtrudeManifold|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_ExtrudeRegion|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_normal_flip": ["BOOLEAN", {"default": false}], "use_dissolve_ortho_edges": ["BOOLEAN", {"default": false}], "mirror": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_ExtrudeRegionMove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_ExtrudeRegionShrinkFatten|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_ExtrudeRepeat|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "steps": ["INT", {"min": 0, "max": 1000000, "default": 10}], "offset": ["B_VECTOR3", {}], "scale_offset": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 1.0}]}}
|
||||
Mesh_ExtrudeVerticesMove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_ExtrudeVertsIndiv|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mirror": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_FaceMakePlanar|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "factor": ["FLOAT", {"min": -10.0, "max": 10.0, "default": 1.0}], "repeat": ["INT", {"min": 1, "max": 10000, "default": 1}]}}
|
||||
Mesh_FaceSetExtract|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "add_boundary_loop": ["BOOLEAN", {"default": true}], "smooth_iterations": ["INT", {"min": 0, "max": 2147483647, "default": 4}], "apply_shrinkwrap": ["BOOLEAN", {"default": true}], "add_solidify": ["BOOLEAN", {"default": true}]}}
|
||||
Mesh_FaceSplitByEdges|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_FacesMirrorUv|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "direction": [["POSITIVE", "NEGATIVE"]], "precision": ["INT", {"min": 1, "max": 16, "default": 3}]}}
|
||||
Mesh_FacesSelectLinkedFlat|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "sharpness": ["FLOAT", {"min": 0.00017453292093705386, "max": 3.1415927410125732, "default": 0.01745329238474369}]}}
|
||||
Mesh_FacesShadeFlat|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_FacesShadeSmooth|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_Fill|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_beauty": ["BOOLEAN", {"default": true}]}}
|
||||
Mesh_FillGrid|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "span": ["INT", {"min": 1, "max": 1000, "default": 1}], "offset": ["INT", {"min": -1000, "max": 1000, "default": 0}], "use_interp_simple": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_FillHoles|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "sides": ["INT", {"min": 0, "max": 1000, "default": 4}]}}
|
||||
Mesh_FlipNormals|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "only_clnors": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_FlipQuadTessellation|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_Hide|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "unselected": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_Inset|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_boundary": ["BOOLEAN", {"default": true}], "use_even_offset": ["BOOLEAN", {"default": true}], "use_relative_offset": ["BOOLEAN", {"default": false}], "use_edge_rail": ["BOOLEAN", {"default": false}], "thickness": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 0.0}], "depth": ["FLOAT", {"min": -999999995904.0, "max": 999999995904.0, "default": 0.0}], "use_outset": ["BOOLEAN", {"default": false}], "use_select_inset": ["BOOLEAN", {"default": false}], "use_individual": ["BOOLEAN", {"default": false}], "use_interpolate": ["BOOLEAN", {"default": true}], "release_confirm": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_Intersect|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mode": [["SELECT", "SELECT_UNSELECT"]], "separate_mode": [["ALL", "CUT", "NONE"]], "threshold": ["FLOAT", {"min": 0.0, "max": 0.009999999776482582, "default": 9.999999974752427e-07}], "solver": [["FAST", "EXACT"]]}}
|
||||
Mesh_IntersectBoolean|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "operation": [["INTERSECT", "UNION", "DIFFERENCE"]], "use_swap": ["BOOLEAN", {"default": false}], "use_self": ["BOOLEAN", {"default": false}], "threshold": ["FLOAT", {"min": 0.0, "max": 0.009999999776482582, "default": 9.999999974752427e-07}], "solver": [["FAST", "EXACT"]]}}
|
||||
Mesh_KnifeProject|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "cut_through": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_KnifeTool|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_occlude_geometry": ["BOOLEAN", {"default": true}], "only_selected": ["BOOLEAN", {"default": false}], "xray": ["BOOLEAN", {"default": true}], "visible_measurements": [["NONE", "BOTH", "DISTANCE", "ANGLE"]], "angle_snapping": [["NONE", "SCREEN", "RELATIVE"]], "angle_snapping_increment": ["FLOAT", {"min": 0.0, "max": 3.1415927410125732, "default": 0.5235987901687622}], "wait_for_input": ["BOOLEAN", {"default": true}]}}
|
||||
Mesh_LoopMultiSelect|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "ring": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_LoopSelect|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "extend": ["BOOLEAN", {"default": false}], "deselect": ["BOOLEAN", {"default": false}], "toggle": ["BOOLEAN", {"default": false}], "ring": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_LoopToRegion|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "select_bigger": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_Loopcut|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "number_cuts": ["INT", {"min": 1, "max": 1000000, "default": 1}], "smoothness": ["FLOAT", {"min": -1000.0, "max": 1000.0, "default": 0.0}], "falloff": [["SMOOTH", "SPHERE", "ROOT", "INVERSE_SQUARE", "SHARP", "LINEAR"]], "object_index": ["INT", {"min": -1, "max": 2147483647, "default": -1}], "edge_index": ["INT", {"min": -1, "max": 2147483647, "default": -1}], "mesh_select_mode_init": ["B_BOOLEAN3", {}]}}
|
||||
Mesh_LoopcutSlide|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_MarkFreestyleEdge|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "clear": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_MarkFreestyleFace|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "clear": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_MarkSeam|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "clear": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_MarkSharp|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "clear": ["BOOLEAN", {"default": false}], "use_verts": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_Merge|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["CENTER", "CURSOR", "COLLAPSE", "FIRST", "LAST"]], "uvs": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_MergeNormals|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_ModWeightedStrength|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "set": ["BOOLEAN", {"default": false}], "face_strength": [["WEAK", "MEDIUM", "STRONG"]]}}
|
||||
Mesh_NormalsMakeConsistent|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "inside": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_NormalsTools|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mode": [["COPY", "PASTE", "ADD", "MULTIPLY", "RESET"]], "absolute": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_OffsetEdgeLoops|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_cap_endpoint": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_OffsetEdgeLoopsSlide|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_PaintMaskExtract|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mask_threshold": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.5}], "add_boundary_loop": ["BOOLEAN", {"default": true}], "smooth_iterations": ["INT", {"min": 0, "max": 2147483647, "default": 4}], "apply_shrinkwrap": ["BOOLEAN", {"default": true}], "add_solidify": ["BOOLEAN", {"default": true}]}}
|
||||
Mesh_PaintMaskSlice|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mask_threshold": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.5}], "fill_holes": ["BOOLEAN", {"default": true}], "new_object": ["BOOLEAN", {"default": true}]}}
|
||||
Mesh_PointNormals|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mode": [["COORDINATES", "MOUSE"]], "invert": ["BOOLEAN", {"default": false}], "align": ["BOOLEAN", {"default": false}], "target_location": ["B_VECTOR3", {}], "spherize": ["BOOLEAN", {"default": false}], "spherize_strength": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.10000000149011612}]}}
|
||||
Mesh_Poke|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "offset": ["FLOAT", {"min": -1000.0, "max": 1000.0, "default": 0.0}], "use_relative_offset": ["BOOLEAN", {"default": false}], "center_mode": [["MEDIAN_WEIGHTED", "MEDIAN", "BOUNDS"]]}}
|
||||
Mesh_PolybuildDeleteAtCursor|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mirror": ["BOOLEAN", {"default": false}], "use_proportional_edit": ["BOOLEAN", {"default": false}], "proportional_edit_falloff": [["SMOOTH", "SPHERE", "ROOT", "INVERSE_SQUARE", "SHARP", "LINEAR", "CONSTANT", "RANDOM"]], "proportional_size": ["FLOAT", {"min": 9.999999974752427e-07, "max": 999999995904.0, "default": 1.0}], "use_proportional_connected": ["BOOLEAN", {"default": false}], "use_proportional_projected": ["BOOLEAN", {"default": false}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_PolybuildDissolveAtCursor|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_PolybuildExtrudeAtCursorMove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_PolybuildFaceAtCursor|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "create_quads": ["BOOLEAN", {"default": true}], "mirror": ["BOOLEAN", {"default": false}], "use_proportional_edit": ["BOOLEAN", {"default": false}], "proportional_edit_falloff": [["SMOOTH", "SPHERE", "ROOT", "INVERSE_SQUARE", "SHARP", "LINEAR", "CONSTANT", "RANDOM"]], "proportional_size": ["FLOAT", {"min": 9.999999974752427e-07, "max": 999999995904.0, "default": 1.0}], "use_proportional_connected": ["BOOLEAN", {"default": false}], "use_proportional_projected": ["BOOLEAN", {"default": false}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_PolybuildFaceAtCursorMove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_PolybuildSplitAtCursor|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mirror": ["BOOLEAN", {"default": false}], "use_proportional_edit": ["BOOLEAN", {"default": false}], "proportional_edit_falloff": [["SMOOTH", "SPHERE", "ROOT", "INVERSE_SQUARE", "SHARP", "LINEAR", "CONSTANT", "RANDOM"]], "proportional_size": ["FLOAT", {"min": 9.999999974752427e-07, "max": 999999995904.0, "default": 1.0}], "use_proportional_connected": ["BOOLEAN", {"default": false}], "use_proportional_projected": ["BOOLEAN", {"default": false}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_PolybuildSplitAtCursorMove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_PolybuildTransformAtCursor|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mirror": ["BOOLEAN", {"default": false}], "use_proportional_edit": ["BOOLEAN", {"default": false}], "proportional_edit_falloff": [["SMOOTH", "SPHERE", "ROOT", "INVERSE_SQUARE", "SHARP", "LINEAR", "CONSTANT", "RANDOM"]], "proportional_size": ["FLOAT", {"min": 9.999999974752427e-07, "max": 999999995904.0, "default": 1.0}], "use_proportional_connected": ["BOOLEAN", {"default": false}], "use_proportional_projected": ["BOOLEAN", {"default": false}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_PolybuildTransformAtCursorMove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_QuadsConvertToTris|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "quad_method": [["BEAUTY", "FIXED", "FIXED_ALTERNATE", "SHORTEST_DIAGONAL", "LONGEST_DIAGONAL"]], "ngon_method": [["BEAUTY", "CLIP"]]}}
|
||||
Mesh_RegionToLoop|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_RemoveDoubles|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "threshold": ["FLOAT", {"min": 9.999999974752427e-07, "max": 50.0, "default": 9.999999747378752e-05}], "use_unselected": ["BOOLEAN", {"default": false}], "use_sharp_edge_from_normals": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_Reveal|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "select": ["BOOLEAN", {"default": true}]}}
|
||||
Mesh_Rip|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mirror": ["BOOLEAN", {"default": false}], "use_proportional_edit": ["BOOLEAN", {"default": false}], "proportional_edit_falloff": [["SMOOTH", "SPHERE", "ROOT", "INVERSE_SQUARE", "SHARP", "LINEAR", "CONSTANT", "RANDOM"]], "proportional_size": ["FLOAT", {"min": 9.999999974752427e-07, "max": 999999995904.0, "default": 1.0}], "use_proportional_connected": ["BOOLEAN", {"default": false}], "use_proportional_projected": ["BOOLEAN", {"default": false}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}], "use_fill": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_RipEdge|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mirror": ["BOOLEAN", {"default": false}], "use_proportional_edit": ["BOOLEAN", {"default": false}], "proportional_edit_falloff": [["SMOOTH", "SPHERE", "ROOT", "INVERSE_SQUARE", "SHARP", "LINEAR", "CONSTANT", "RANDOM"]], "proportional_size": ["FLOAT", {"min": 9.999999974752427e-07, "max": 999999995904.0, "default": 1.0}], "use_proportional_connected": ["BOOLEAN", {"default": false}], "use_proportional_projected": ["BOOLEAN", {"default": false}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_RipEdgeMove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_RipMove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_Screw|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "steps": ["INT", {"min": 1, "max": 100000, "default": 9}], "turns": ["INT", {"min": 1, "max": 100000, "default": 1}], "center": ["B_VECTOR3", {}], "axis": ["B_VECTOR3", {}]}}
|
||||
Mesh_SelectAll|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "action": [["TOGGLE", "SELECT", "DESELECT", "INVERT"]]}}
|
||||
Mesh_SelectAxis|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "orientation": [["GLOBAL", "LOCAL", "NORMAL", "GIMBAL", "VIEW", "CURSOR", "PARENT"]], "sign": [["POS", "NEG", "ALIGN"]], "axis": [["X", "Y", "Z"]], "threshold": ["FLOAT", {"min": 9.999999974752427e-07, "max": 50.0, "default": 9.999999747378752e-05}]}}
|
||||
Mesh_SelectFaceBySides|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "number": ["INT", {"min": 3, "max": 2147483647, "default": 4}], "type": [["LESS", "EQUAL", "GREATER", "NOTEQUAL"]], "extend": ["BOOLEAN", {"default": true}]}}
|
||||
Mesh_SelectInteriorFaces|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_SelectLess|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_face_step": ["BOOLEAN", {"default": true}]}}
|
||||
Mesh_SelectLinked|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "delimit": ["B_ENUM_SET", {}]}}
|
||||
Mesh_SelectLinkedPick|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "deselect": ["BOOLEAN", {"default": false}], "delimit": ["B_ENUM_SET", {}], "object_index": ["INT", {"min": -1, "max": 2147483647, "default": -1}], "index": ["INT", {"min": -1, "max": 2147483647, "default": -1}]}}
|
||||
Mesh_SelectLoose|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "extend": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_SelectMirror|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "axis": ["B_ENUM_SET", {}], "extend": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_SelectMode|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_extend": ["BOOLEAN", {"default": false}], "use_expand": ["BOOLEAN", {"default": false}], "type": [["VERT", "EDGE", "FACE"]], "action": [["DISABLE", "ENABLE", "TOGGLE"]]}}
|
||||
Mesh_SelectMore|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_face_step": ["BOOLEAN", {"default": true}]}}
|
||||
Mesh_SelectNextItem|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_SelectNonManifold|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "extend": ["BOOLEAN", {"default": true}], "use_wire": ["BOOLEAN", {"default": true}], "use_boundary": ["BOOLEAN", {"default": true}], "use_multi_face": ["BOOLEAN", {"default": true}], "use_non_contiguous": ["BOOLEAN", {"default": true}], "use_verts": ["BOOLEAN", {"default": true}]}}
|
||||
Mesh_SelectNth|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "skip": ["INT", {"min": 1, "max": 2147483647, "default": 1}], "nth": ["INT", {"min": 1, "max": 2147483647, "default": 1}], "offset": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}]}}
|
||||
Mesh_SelectPrevItem|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_SelectRandom|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "ratio": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.5}], "seed": ["INT", {"min": 0, "max": 2147483647, "default": 0}], "action": [["SELECT", "DESELECT"]]}}
|
||||
Mesh_SelectSimilar|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["NORMAL", "FACE", "VGROUP", "EDGE", "VCREASE", "LENGTH", "DIR", "FACE", "FACE_ANGLE", "CREASE", "BEVEL", "SEAM", "SHARP", "FREESTYLE_EDGE", "MATERIAL", "AREA", "SIDES", "PERIMETER", "NORMAL", "COPLANAR", "SMOOTH", "FACE_MAP", "FREESTYLE_FACE"]], "compare": [["EQUAL", "GREATER", "LESS"]], "threshold": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.0}]}}
|
||||
Mesh_SelectSimilarRegion|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_SelectUngrouped|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "extend": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_Separate|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["SELECTED", "MATERIAL", "LOOSE"]]}}
|
||||
Mesh_SetNormalsFromFaces|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "keep_sharp": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_ShapePropagateToAll|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_ShortestPathPick|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "edge_mode": [["SELECT", "SEAM", "SHARP", "CREASE", "BEVEL", "FREESTYLE"]], "use_face_step": ["BOOLEAN", {"default": false}], "use_topology_distance": ["BOOLEAN", {"default": false}], "use_fill": ["BOOLEAN", {"default": false}], "skip": ["INT", {"min": 0, "max": 2147483647, "default": 0}], "nth": ["INT", {"min": 1, "max": 2147483647, "default": 1}], "offset": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "index": ["INT", {"min": -1, "max": 2147483647, "default": -1}]}}
|
||||
Mesh_ShortestPathSelect|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "edge_mode": [["SELECT", "SEAM", "SHARP", "CREASE", "BEVEL", "FREESTYLE"]], "use_face_step": ["BOOLEAN", {"default": false}], "use_topology_distance": ["BOOLEAN", {"default": false}], "use_fill": ["BOOLEAN", {"default": false}], "skip": ["INT", {"min": 0, "max": 2147483647, "default": 0}], "nth": ["INT", {"min": 1, "max": 2147483647, "default": 1}], "offset": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}]}}
|
||||
Mesh_SmoothNormals|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "factor": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.5}]}}
|
||||
Mesh_Solidify|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "thickness": ["FLOAT", {"min": -10000.0, "max": 10000.0, "default": 0.009999999776482582}]}}
|
||||
Mesh_SortElements|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["VIEW_ZAXIS", "VIEW_XAXIS", "CURSOR_DISTANCE", "MATERIAL", "SELECTED", "RANDOMIZE", "REVERSE"]], "elements": ["B_ENUM_SET", {}], "reverse": ["BOOLEAN", {"default": false}], "seed": ["INT", {"min": 0, "max": 2147483647, "default": 0}]}}
|
||||
Mesh_Spin|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "steps": ["INT", {"min": 0, "max": 1000000, "default": 12}], "dupli": ["BOOLEAN", {"default": false}], "angle": ["FLOAT", {"min": -999999995904.0, "max": 999999995904.0, "default": 1.5707963705062866}], "use_auto_merge": ["BOOLEAN", {"default": true}], "use_normal_flip": ["BOOLEAN", {"default": false}], "center": ["B_VECTOR3", {}], "axis": ["B_VECTOR3", {}]}}
|
||||
Mesh_Split|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_SplitNormals|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_Subdivide|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "number_cuts": ["INT", {"min": 1, "max": 100, "default": 1}], "smoothness": ["FLOAT", {"min": 0.0, "max": 1000.0, "default": 0.0}], "ngon": ["BOOLEAN", {"default": true}], "quadcorner": [["INNERVERT", "PATH", "STRAIGHT_CUT", "FAN"]], "fractal": ["FLOAT", {"min": 0.0, "max": 1000000.0, "default": 0.0}], "fractal_along_normal": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.0}], "seed": ["INT", {"min": 0, "max": 2147483647, "default": 0}]}}
|
||||
Mesh_SubdivideEdgering|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "number_cuts": ["INT", {"min": 0, "max": 1000, "default": 10}], "interpolation": [["LINEAR", "PATH", "SURFACE"]], "smoothness": ["FLOAT", {"min": 0.0, "max": 1000.0, "default": 1.0}], "profile_shape_factor": ["FLOAT", {"min": -1000.0, "max": 1000.0, "default": 0.0}], "profile_shape": [["SMOOTH", "SPHERE", "ROOT", "INVERSE_SQUARE", "SHARP", "LINEAR"]]}}
|
||||
Mesh_Symmetrize|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "direction": [["NEGATIVE_X", "POSITIVE_X", "NEGATIVE_Y", "POSITIVE_Y", "NEGATIVE_Z", "POSITIVE_Z"]], "threshold": ["FLOAT", {"min": 0.0, "max": 10.0, "default": 9.999999747378752e-05}]}}
|
||||
Mesh_SymmetrySnap|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "direction": [["NEGATIVE_X", "POSITIVE_X", "NEGATIVE_Y", "POSITIVE_Y", "NEGATIVE_Z", "POSITIVE_Z"]], "threshold": ["FLOAT", {"min": 0.0, "max": 10.0, "default": 0.05000000074505806}], "factor": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.5}], "use_center": ["BOOLEAN", {"default": true}]}}
|
||||
Mesh_TrisConvertToQuads|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "face_threshold": ["FLOAT", {"min": 0.0, "max": 3.1415927410125732, "default": 0.6981316804885864}], "shape_threshold": ["FLOAT", {"min": 0.0, "max": 3.1415927410125732, "default": 0.6981316804885864}], "uvs": ["BOOLEAN", {"default": false}], "vcols": ["BOOLEAN", {"default": false}], "seam": ["BOOLEAN", {"default": false}], "sharp": ["BOOLEAN", {"default": false}], "materials": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_Unsubdivide|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "iterations": ["INT", {"min": 1, "max": 1000, "default": 2}]}}
|
||||
Mesh_UvTextureAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_UvTextureRemove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_UvsReverse|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_UvsRotate|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_ccw": ["BOOLEAN", {"default": false}]}}
|
||||
Mesh_VertConnect|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_VertConnectConcave|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_VertConnectNonplanar|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "angle_limit": ["FLOAT", {"min": 0.0, "max": 3.1415927410125732, "default": 0.0872664600610733}]}}
|
||||
Mesh_VertConnectPath|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Mesh_VerticesSmooth|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "factor": ["FLOAT", {"min": -10.0, "max": 10.0, "default": 0.0}], "repeat": ["INT", {"min": 1, "max": 1000, "default": 1}], "xaxis": ["BOOLEAN", {"default": true}], "yaxis": ["BOOLEAN", {"default": true}], "zaxis": ["BOOLEAN", {"default": true}], "wait_for_input": ["BOOLEAN", {"default": true}]}}
|
||||
Mesh_VerticesSmoothLaplacian|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "repeat": ["INT", {"min": 1, "max": 1000, "default": 1}], "lambda_factor": ["FLOAT", {"min": 1.0000000116860974e-07, "max": 1000.0, "default": 1.0}], "lambda_border": ["FLOAT", {"min": 1.0000000116860974e-07, "max": 1000.0, "default": 4.999999873689376e-05}], "use_x": ["BOOLEAN", {"default": true}], "use_y": ["BOOLEAN", {"default": true}], "use_z": ["BOOLEAN", {"default": true}], "preserve_volume": ["BOOLEAN", {"default": true}]}}
|
||||
Mesh_Wireframe|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_boundary": ["BOOLEAN", {"default": true}], "use_even_offset": ["BOOLEAN", {"default": true}], "use_relative_offset": ["BOOLEAN", {"default": false}], "use_replace": ["BOOLEAN", {"default": true}], "thickness": ["FLOAT", {"min": 0.0, "max": 10000.0, "default": 0.009999999776482582}], "offset": ["FLOAT", {"min": 0.0, "max": 10000.0, "default": 0.009999999776482582}], "use_crease": ["BOOLEAN", {"default": false}], "crease_weight": ["FLOAT", {"min": 0.0, "max": 1000.0, "default": 0.009999999776482582}]}}
|
||||
Object_Add|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "radius": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 1.0}], "type": [["MESH", "CURVE", "SURFACE", "META", "FONT", "CURVES", "POINTCLOUD", "VOLUME", "GPENCIL", "ARMATURE", "LATTICE", "EMPTY", "LIGHT", "LIGHT_PROBE", "CAMERA", "SPEAKER"]], "enter_editmode": ["BOOLEAN", {"default": false}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Object_AddNamed|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "linked": ["BOOLEAN", {"default": false}], "name": ["STRING", {"default": "", "multiline": false}], "session_uuid": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "matrix": ["B_VECTOR16", {}], "drop_x": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "drop_y": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}]}}
|
||||
Object_Align|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "bb_quality": ["BOOLEAN", {"default": true}], "align_mode": [["OPT_1", "OPT_2", "OPT_3"]], "relative_to": [["OPT_1", "OPT_2", "OPT_3", "OPT_4"]], "align_axis": [["X", "Y", "Z"]]}}
|
||||
Object_AnimTransformsToDeltas|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_ArmatureAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "radius": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 1.0}], "enter_editmode": ["BOOLEAN", {"default": false}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Object_AssignPropertyDefaults|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "process_data": ["BOOLEAN", {"default": true}], "process_bones": ["BOOLEAN", {"default": true}]}}
|
||||
Object_Bake|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["COMBINED", "AO", "SHADOW", "POSITION", "NORMAL", "UV", "ROUGHNESS", "EMIT", "ENVIRONMENT", "DIFFUSE", "GLOSSY", "TRANSMISSION"]], "pass_filter": [["NONE", "EMIT", "DIRECT", "INDIRECT", "COLOR", "DIFFUSE", "GLOSSY", "TRANSMISSION"]], "filepath": ["STRING", {"default": "", "multiline": false}], "width": ["INT", {"min": 1, "max": 2147483647, "default": 512}], "height": ["INT", {"min": 1, "max": 2147483647, "default": 512}], "margin": ["INT", {"min": 0, "max": 2147483647, "default": 16}], "margin_type": [["ADJACENT_FACES", "EXTEND"]], "use_selected_to_active": ["BOOLEAN", {"default": false}], "max_ray_distance": ["FLOAT", {"min": 0.0, "max": 3.4028234663852886e+38, "default": 0.0}], "cage_extrusion": ["FLOAT", {"min": 0.0, "max": 3.4028234663852886e+38, "default": 0.0}], "cage_object": ["STRING", {"default": "", "multiline": false}], "normal_space": [["OBJECT", "TANGENT"]], "normal_r": [["POS_X", "POS_Y", "POS_Z", "NEG_X", "NEG_Y", "NEG_Z"]], "normal_g": [["POS_X", "POS_Y", "POS_Z", "NEG_X", "NEG_Y", "NEG_Z"]], "normal_b": [["POS_X", "POS_Y", "POS_Z", "NEG_X", "NEG_Y", "NEG_Z"]], "target": [["IMAGE_TEXTURES", "VERTEX_COLORS"]], "save_mode": [["INTERNAL", "EXTERNAL"]], "use_clear": ["BOOLEAN", {"default": false}], "use_cage": ["BOOLEAN", {"default": false}], "use_split_materials": ["BOOLEAN", {"default": false}], "use_automatic_name": ["BOOLEAN", {"default": false}], "uv_layer": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_BakeImage|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_CameraAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "enter_editmode": ["BOOLEAN", {"default": false}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Object_ClearOverrideLibrary|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_CollectionAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_CollectionExternalAssetDrop|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "session_uuid": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}], "use_instance": ["BOOLEAN", {"default": true}], "drop_x": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "drop_y": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "collection": ["B_ENUM", {}]}}
|
||||
Object_CollectionInstanceAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "name": ["STRING", {"default": "Collection", "multiline": false}], "collection": ["B_ENUM", {}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}], "session_uuid": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "drop_x": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "drop_y": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}]}}
|
||||
Object_CollectionLink|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "collection": ["B_ENUM", {}]}}
|
||||
Object_CollectionObjectsSelect|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_CollectionRemove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_CollectionUnlink|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_ConstraintAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": ["B_ENUM", {}]}}
|
||||
Object_ConstraintAddWithTargets|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": ["B_ENUM", {}]}}
|
||||
Object_ConstraintsClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_ConstraintsCopy|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_Convert|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "target": [["CURVE", "MESH", "GPENCIL", "CURVES"]], "keep_original": ["BOOLEAN", {"default": false}], "merge_customdata": ["BOOLEAN", {"default": true}], "angle": ["FLOAT", {"min": 0.0, "max": 3.1415927410125732, "default": 1.2217304706573486}], "thickness": ["INT", {"min": 1, "max": 100, "default": 5}], "seams": ["BOOLEAN", {"default": false}], "faces": ["BOOLEAN", {"default": true}], "offset": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 0.009999999776482582}]}}
|
||||
Object_CorrectivesmoothBind|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_CurvesEmptyHairAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Object_CurvesRandomAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Object_DataInstanceAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "name": ["STRING", {"default": "", "multiline": false}], "session_uuid": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "type": [["ACTION", "ARMATURE", "BRUSH", "CACHEFILE", "CAMERA", "COLLECTION", "CURVE", "CURVES", "FONT", "GREASEPENCIL", "IMAGE", "KEY", "LATTICE", "LIBRARY", "LIGHT", "LIGHT_PROBE", "LINESTYLE", "MASK", "MATERIAL", "MESH", "META", "MOVIECLIP", "NODETREE", "OBJECT", "PAINTCURVE", "PALETTE", "PARTICLE", "POINTCLOUD", "SCENE", "SIMULATION", "SOUND", "SPEAKER", "TEXT", "TEXTURE", "VOLUME", "WINDOWMANAGER", "WORKSPACE", "WORLD"]], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}], "drop_x": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "drop_y": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}]}}
|
||||
Object_DataTransfer|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_reverse_transfer": ["BOOLEAN", {"default": false}], "use_freeze": ["BOOLEAN", {"default": false}], "data_type": [["VGROUP_WEIGHTS", "BEVEL_WEIGHT_VERT", "COLOR_VERTEX", "SHARP_EDGE", "SEAM", "CREASE", "BEVEL_WEIGHT_EDGE", "FREESTYLE_EDGE", "CUSTOM_NORMAL", "COLOR_CORNER", "UV", "SMOOTH", "FREESTYLE_FACE"]], "use_create": ["BOOLEAN", {"default": true}], "vert_mapping": [["TOPOLOGY", "NEAREST", "EDGE_NEAREST", "EDGEINTERP_NEAREST", "POLY_NEAREST", "POLYINTERP_NEAREST", "POLYINTERP_VNORPROJ"]], "edge_mapping": [["TOPOLOGY", "VERT_NEAREST", "NEAREST", "POLY_NEAREST", "EDGEINTERP_VNORPROJ"]], "loop_mapping": [["TOPOLOGY", "NEAREST_NORMAL", "NEAREST_POLYNOR", "NEAREST_POLY", "POLYINTERP_NEAREST", "POLYINTERP_LNORPROJ"]], "poly_mapping": [["TOPOLOGY", "NEAREST", "NORMAL", "POLYINTERP_PNORPROJ"]], "use_auto_transform": ["BOOLEAN", {"default": false}], "use_object_transform": ["BOOLEAN", {"default": true}], "use_max_distance": ["BOOLEAN", {"default": false}], "max_distance": ["FLOAT", {"min": 0.0, "max": 3.4028234663852886e+38, "default": 1.0}], "ray_radius": ["FLOAT", {"min": 0.0, "max": 3.4028234663852886e+38, "default": 0.0}], "islands_precision": ["FLOAT", {"min": 0.0, "max": 10.0, "default": 0.10000000149011612}], "layers_select_src": [["ACTIVE", "ALL", "BONE_SELECT", "BONE_DEFORM"]], "layers_select_dst": [["ACTIVE", "NAME", "INDEX"]], "mix_mode": [["REPLACE", "ABOVE_THRESHOLD", "BELOW_THRESHOLD", "MIX", "ADD", "SUB", "MUL"]], "mix_factor": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 1.0}]}}
|
||||
Object_DatalayoutTransfer|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}], "data_type": [["VGROUP_WEIGHTS", "BEVEL_WEIGHT_VERT", "COLOR_VERTEX", "SHARP_EDGE", "SEAM", "CREASE", "BEVEL_WEIGHT_EDGE", "FREESTYLE_EDGE", "CUSTOM_NORMAL", "COLOR_CORNER", "UV", "SMOOTH", "FREESTYLE_FACE"]], "use_delete": ["BOOLEAN", {"default": false}], "layers_select_src": [["ACTIVE", "ALL", "BONE_SELECT", "BONE_DEFORM"]], "layers_select_dst": [["ACTIVE", "NAME", "INDEX"]]}}
|
||||
Object_Delete|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_global": ["BOOLEAN", {"default": false}], "confirm": ["BOOLEAN", {"default": true}]}}
|
||||
Object_DropGeometryNodes|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "session_uuid": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}]}}
|
||||
Object_DropNamedImage|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "filepath": ["STRING", {"default": "", "multiline": false}], "relative_path": ["BOOLEAN", {"default": true}], "name": ["STRING", {"default": "", "multiline": false}], "session_uuid": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Object_DropNamedMaterial|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "name": ["STRING", {"default": "", "multiline": false}], "session_uuid": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}]}}
|
||||
Object_Duplicate|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "linked": ["BOOLEAN", {"default": false}], "mode": [["INIT", "DUMMY", "TRANSLATION", "ROTATION", "RESIZE", "SKIN_RESIZE", "TOSPHERE", "SHEAR", "BEND", "SHRINKFATTEN", "TILT", "TRACKBALL", "PUSHPULL", "CREASE", "VERTEX_CREASE", "MIRROR", "BONE_SIZE", "BONE_ENVELOPE", "BONE_ENVELOPE_DIST", "CURVE_SHRINKFATTEN", "MASK_SHRINKFATTEN", "GPENCIL_SHRINKFATTEN", "BONE_ROLL", "TIME_TRANSLATE", "TIME_SLIDE", "TIME_SCALE", "TIME_EXTEND", "BAKE_TIME", "BWEIGHT", "ALIGN", "EDGESLIDE", "SEQSLIDE", "GPENCIL_OPACITY"]]}}
|
||||
Object_DuplicateMove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_DuplicateMoveLinked|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_DuplicatesMakeReal|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_base_parent": ["BOOLEAN", {"default": false}], "use_hierarchy": ["BOOLEAN", {"default": false}]}}
|
||||
Object_EditmodeToggle|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_EffectorAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["FORCE", "WIND", "VORTEX", "MAGNET", "HARMONIC", "CHARGE", "LENNARDJ", "TEXTURE", "GUIDE", "BOID", "TURBULENCE", "DRAG", "FLUID"]], "radius": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 1.0}], "enter_editmode": ["BOOLEAN", {"default": false}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Object_EmptyAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["PLAIN_AXES", "ARROWS", "SINGLE_ARROW", "CIRCLE", "CUBE", "SPHERE", "CONE", "IMAGE"]], "radius": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 1.0}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Object_ExplodeRefresh|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_FaceMapAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_FaceMapAssign|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_FaceMapDeselect|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_FaceMapMove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "direction": [["UP", "DOWN"]]}}
|
||||
Object_FaceMapRemove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_FaceMapRemoveFrom|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_FaceMapSelect|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_ForcefieldToggle|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_GeometryNodeTreeCopyAssign|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_GeometryNodesInputAttributeToggle|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "prop_path": ["STRING", {"default": "", "multiline": false}], "modifier_name": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_GeometryNodesMoveToNodes|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_GpencilAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "radius": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 1.0}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}], "type": [["EMPTY", "STROKE", "MONKEY", "LRT_SCENE", "LRT_COLLECTION", "LRT_OBJECT"]], "use_in_front": ["BOOLEAN", {"default": true}], "stroke_depth_offset": ["FLOAT", {"min": 0.0, "max": 3.4028234663852886e+38, "default": 0.05000000074505806}], "use_lights": ["BOOLEAN", {"default": false}], "stroke_depth_order": [["2D", "3D"]]}}
|
||||
Object_GpencilModifierAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["GP_TEXTURE", "GP_TIME", "GP_WEIGHT_ANGLE", "GP_WEIGHT_PROXIMITY", "GP_ARRAY", "GP_BUILD", "GP_DASH", "GP_ENVELOPE", "GP_LENGTH", "GP_LINEART", "GP_MIRROR", "GP_MULTIPLY", "GP_OUTLINE", "GP_SIMPLIFY", "GP_SUBDIV", "GP_ARMATURE", "GP_HOOK", "GP_LATTICE", "GP_NOISE", "GP_OFFSET", "SHRINKWRAP", "GP_SMOOTH", "GP_THICK", "GP_COLOR", "GP_OPACITY", "GP_TINT"]]}}
|
||||
Object_GpencilModifierApply|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "apply_as": [["DATA", "SHAPE"]], "modifier": ["STRING", {"default": "", "multiline": false}], "report": ["BOOLEAN", {"default": false}]}}
|
||||
Object_GpencilModifierCopy|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_GpencilModifierCopyToSelected|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_GpencilModifierMoveDown|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_GpencilModifierMoveToIndex|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}], "index": ["INT", {"min": 0, "max": 2147483647, "default": 0}]}}
|
||||
Object_GpencilModifierMoveUp|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_GpencilModifierRemove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}], "report": ["BOOLEAN", {"default": false}]}}
|
||||
Object_HideCollection|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "collection_index": ["INT", {"min": -1, "max": 2147483647, "default": -1}], "toggle": ["BOOLEAN", {"default": false}], "extend": ["BOOLEAN", {"default": false}]}}
|
||||
Object_HideRenderClearAll|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_HideViewClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "select": ["BOOLEAN", {"default": true}]}}
|
||||
Object_HideViewSet|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "unselected": ["BOOLEAN", {"default": false}]}}
|
||||
Object_HookAddNewob|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_HookAddSelob|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_bone": ["BOOLEAN", {"default": false}]}}
|
||||
Object_HookAssign|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["B_ENUM", {}]}}
|
||||
Object_HookRecenter|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["B_ENUM", {}]}}
|
||||
Object_HookRemove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["B_ENUM", {}]}}
|
||||
Object_HookReset|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["B_ENUM", {}]}}
|
||||
Object_HookSelect|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["B_ENUM", {}]}}
|
||||
Object_InstanceOffsetFromCursor|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_InstanceOffsetFromObject|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_InstanceOffsetToCursor|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_IsolateTypeRender|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_Join|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_JoinShapes|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_JoinUvs|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_LaplaciandeformBind|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_LightAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["POINT", "SUN", "SPOT", "AREA"]], "radius": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 1.0}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Object_LightprobeAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["CUBEMAP", "PLANAR", "GRID"]], "radius": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 1.0}], "enter_editmode": ["BOOLEAN", {"default": false}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Object_LightprobeCacheBake|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "delay": ["INT", {"min": 0, "max": 2000, "default": 0}], "subset": [["ALL", "DIRTY", "SELECTED", "ACTIVE"]]}}
|
||||
Object_LightprobeCacheFree|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_LineartBakeStrokes|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_LineartBakeStrokesAll|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_LineartClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_LineartClearAll|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_LinkToCollection|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "collection_index": ["INT", {"min": -1, "max": 2147483647, "default": -1}], "is_new": ["BOOLEAN", {"default": false}], "new_collection_name": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_LoadBackgroundImage|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "filepath": ["STRING", {"default": "", "multiline": false}], "filter_image": ["BOOLEAN", {"default": true}], "filter_folder": ["BOOLEAN", {"default": true}], "view_align": ["BOOLEAN", {"default": true}]}}
|
||||
Object_LoadReferenceImage|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "filepath": ["STRING", {"default": "", "multiline": false}], "filter_image": ["BOOLEAN", {"default": true}], "filter_folder": ["BOOLEAN", {"default": true}], "view_align": ["BOOLEAN", {"default": true}]}}
|
||||
Object_LocationClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "clear_delta": ["BOOLEAN", {"default": false}]}}
|
||||
Object_MakeDupliFace|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_MakeLinksData|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["OBDATA", "MATERIAL", "ANIMATION", "GROUPS", "DUPLICOLLECTION", "FONTS", "MODIFIERS", "EFFECTS"]]}}
|
||||
Object_MakeLinksScene|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "scene": ["B_ENUM", {}]}}
|
||||
Object_MakeLocal|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["SELECT_OBJECT", "SELECT_OBDATA", "SELECT_OBDATA_MATERIAL", "ALL"]]}}
|
||||
Object_MakeOverrideLibrary|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "collection": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}]}}
|
||||
Object_MakeSingleUser|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["SELECTED_OBJECTS", "ALL"]], "object": ["BOOLEAN", {"default": false}], "obdata": ["BOOLEAN", {"default": false}], "material": ["BOOLEAN", {"default": false}], "animation": ["BOOLEAN", {"default": false}], "obdata_animation": ["BOOLEAN", {"default": false}]}}
|
||||
Object_MaterialSlotAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_MaterialSlotAssign|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_MaterialSlotCopy|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_MaterialSlotDeselect|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_MaterialSlotMove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "direction": [["UP", "DOWN"]]}}
|
||||
Object_MaterialSlotRemove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_MaterialSlotRemoveUnused|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_MaterialSlotSelect|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_MeshdeformBind|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_MetaballAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["BALL", "CAPSULE", "PLANE", "ELLIPSOID", "CUBE"]], "radius": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 2.0}], "enter_editmode": ["BOOLEAN", {"default": false}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Object_ModeSet|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mode": [["OBJECT", "EDIT", "POSE", "SCULPT", "VERTEX_PAINT", "WEIGHT_PAINT", "TEXTURE_PAINT", "PARTICLE_EDIT", "EDIT_GPENCIL", "SCULPT_GPENCIL", "PAINT_GPENCIL", "WEIGHT_GPENCIL", "VERTEX_GPENCIL", "SCULPT_CURVES"]], "toggle": ["BOOLEAN", {"default": false}]}}
|
||||
Object_ModeSetWithSubmode|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mode": [["OBJECT", "EDIT", "POSE", "SCULPT", "VERTEX_PAINT", "WEIGHT_PAINT", "TEXTURE_PAINT", "PARTICLE_EDIT", "EDIT_GPENCIL", "SCULPT_GPENCIL", "PAINT_GPENCIL", "WEIGHT_GPENCIL", "VERTEX_GPENCIL", "SCULPT_CURVES"]], "toggle": ["BOOLEAN", {"default": false}], "mesh_select_mode": [["VERT", "EDGE", "FACE"]]}}
|
||||
Object_ModifierAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["DATA_TRANSFER", "MESH_CACHE", "MESH_SEQUENCE_CACHE", "NORMAL_EDIT", "WEIGHTED_NORMAL", "UV_PROJECT", "UV_WARP", "VERTEX_WEIGHT_EDIT", "VERTEX_WEIGHT_MIX", "VERTEX_WEIGHT_PROXIMITY", "ARRAY", "BEVEL", "BOOLEAN", "BUILD", "DECIMATE", "EDGE_SPLIT", "NODES", "MASK", "MIRROR", "MESH_TO_VOLUME", "MULTIRES", "REMESH", "SCREW", "SKIN", "SOLIDIFY", "SUBSURF", "TRIANGULATE", "VOLUME_TO_MESH", "WELD", "WIREFRAME", "ARMATURE", "CAST", "CURVE", "DISPLACE", "HOOK", "LAPLACIANDEFORM", "LATTICE", "MESH_DEFORM", "SHRINKWRAP", "SIMPLE_DEFORM", "SMOOTH", "CORRECTIVE_SMOOTH", "LAPLACIANSMOOTH", "SURFACE_DEFORM", "WARP", "WAVE", "VOLUME_DISPLACE", "CLOTH", "COLLISION", "DYNAMIC_PAINT", "EXPLODE", "FLUID", "OCEAN", "PARTICLE_INSTANCE", "PARTICLE_SYSTEM", "SOFT_BODY", "SURFACE"]]}}
|
||||
Object_ModifierApply|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}], "report": ["BOOLEAN", {"default": false}], "merge_customdata": ["BOOLEAN", {"default": true}], "single_user": ["BOOLEAN", {"default": false}]}}
|
||||
Object_ModifierApplyAsShapekey|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "keep_modifier": ["BOOLEAN", {"default": false}], "modifier": ["STRING", {"default": "", "multiline": false}], "report": ["BOOLEAN", {"default": false}]}}
|
||||
Object_ModifierConvert|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_ModifierCopy|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_ModifierCopyToSelected|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_ModifierMoveDown|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_ModifierMoveToIndex|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}], "index": ["INT", {"min": 0, "max": 2147483647, "default": 0}]}}
|
||||
Object_ModifierMoveUp|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_ModifierRemove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}], "report": ["BOOLEAN", {"default": false}]}}
|
||||
Object_ModifierSetActive|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_MoveToCollection|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "collection_index": ["INT", {"min": -1, "max": 2147483647, "default": -1}], "is_new": ["BOOLEAN", {"default": false}], "new_collection_name": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_MultiresBaseApply|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_MultiresExternalPack|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_MultiresExternalSave|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "filepath": ["STRING", {"default": "", "multiline": false}], "hide_props_region": ["BOOLEAN", {"default": true}], "check_existing": ["BOOLEAN", {"default": true}], "filter_blender": ["BOOLEAN", {"default": false}], "filter_backup": ["BOOLEAN", {"default": false}], "filter_image": ["BOOLEAN", {"default": false}], "filter_movie": ["BOOLEAN", {"default": false}], "filter_python": ["BOOLEAN", {"default": false}], "filter_font": ["BOOLEAN", {"default": false}], "filter_sound": ["BOOLEAN", {"default": false}], "filter_text": ["BOOLEAN", {"default": false}], "filter_archive": ["BOOLEAN", {"default": false}], "filter_btx": ["BOOLEAN", {"default": true}], "filter_collada": ["BOOLEAN", {"default": false}], "filter_alembic": ["BOOLEAN", {"default": false}], "filter_usd": ["BOOLEAN", {"default": false}], "filter_obj": ["BOOLEAN", {"default": false}], "filter_volume": ["BOOLEAN", {"default": false}], "filter_folder": ["BOOLEAN", {"default": true}], "filter_blenlib": ["BOOLEAN", {"default": false}], "filemode": ["INT", {"min": 1, "max": 9, "default": 9}], "relative_path": ["BOOLEAN", {"default": true}], "display_type": [["DEFAULT", "LIST_VERTICAL", "LIST_HORIZONTAL", "THUMBNAIL"]], "sort_method": ["B_ENUM", {}], "modifier": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_MultiresHigherLevelsDelete|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_MultiresRebuildSubdiv|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_MultiresReshape|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_MultiresSubdivide|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}], "mode": [["CATMULL_CLARK", "SIMPLE", "LINEAR"]]}}
|
||||
Object_MultiresUnsubdivide|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_OceanBake|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}], "free": ["BOOLEAN", {"default": false}]}}
|
||||
Object_OriginClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_OriginSet|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["GEOMETRY_ORIGIN", "ORIGIN_GEOMETRY", "ORIGIN_CURSOR", "ORIGIN_CENTER_OF_MASS", "ORIGIN_CENTER_OF_VOLUME"]], "center": [["MEDIAN", "BOUNDS"]]}}
|
||||
Object_ParentClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["CLEAR", "CLEAR_KEEP_TRANSFORM", "CLEAR_INVERSE"]]}}
|
||||
Object_ParentInverseApply|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_ParentNoInverseSet|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "confirm": ["BOOLEAN", {"default": true}], "keep_transform": ["BOOLEAN", {"default": false}]}}
|
||||
Object_ParentSet|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["OBJECT", "ARMATURE", "ARMATURE_NAME", "ARMATURE_AUTO", "ARMATURE_ENVELOPE", "BONE", "BONE_RELATIVE", "CURVE", "FOLLOW", "PATH_CONST", "LATTICE", "VERTEX", "VERTEX_TRI"]], "xmirror": ["BOOLEAN", {"default": false}], "keep_transform": ["BOOLEAN", {"default": false}]}}
|
||||
Object_ParticleSystemAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_ParticleSystemRemove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_PathsCalculate|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "display_type": [["CURRENT_FRAME", "RANGE"]], "range": [["KEYS_ALL", "KEYS_SELECTED", "SCENE", "MANUAL"]]}}
|
||||
Object_PathsClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "only_selected": ["BOOLEAN", {"default": false}]}}
|
||||
Object_PathsUpdate|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_PathsUpdateVisible|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_PointcloudAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Object_PosemodeToggle|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_QuadriflowRemesh|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_mesh_symmetry": ["BOOLEAN", {"default": true}], "use_preserve_sharp": ["BOOLEAN", {"default": false}], "use_preserve_boundary": ["BOOLEAN", {"default": false}], "preserve_paint_mask": ["BOOLEAN", {"default": false}], "smooth_normals": ["BOOLEAN", {"default": false}], "mode": [["RATIO", "EDGE", "FACES"]], "target_ratio": ["FLOAT", {"min": 0.0, "max": 3.4028234663852886e+38, "default": 1.0}], "target_edge_length": ["FLOAT", {"min": 1.0000000116860974e-07, "max": 3.4028234663852886e+38, "default": 0.10000000149011612}], "target_faces": ["INT", {"min": 1, "max": 2147483647, "default": 4000}], "mesh_area": ["FLOAT", {"min": -3.4028234663852886e+38, "max": 3.4028234663852886e+38, "default": -1.0}], "seed": ["INT", {"min": 0, "max": 2147483647, "default": 0}]}}
|
||||
Object_QuickExplode|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "style": [["EXPLODE", "BLEND"]], "amount": ["INT", {"min": 2, "max": 10000, "default": 100}], "frame_duration": ["INT", {"min": 1, "max": 300000, "default": 50}], "frame_start": ["INT", {"min": 1, "max": 300000, "default": 1}], "frame_end": ["INT", {"min": 1, "max": 300000, "default": 10}], "velocity": ["FLOAT", {"min": 0.0, "max": 300000.0, "default": 1.0}], "fade": ["BOOLEAN", {"default": true}]}}
|
||||
Object_QuickFur|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "density": [["LOW", "MEDIUM", "HIGH"]], "length": ["FLOAT", {"min": 0.0010000000474974513, "max": 100.0, "default": 0.10000000149011612}], "radius": ["FLOAT", {"min": 0.0, "max": 10.0, "default": 0.0010000000474974513}], "view_percentage": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 1.0}], "apply_hair_guides": ["BOOLEAN", {"default": true}], "use_noise": ["BOOLEAN", {"default": true}], "use_frizz": ["BOOLEAN", {"default": true}]}}
|
||||
Object_QuickLiquid|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "show_flows": ["BOOLEAN", {"default": false}]}}
|
||||
Object_QuickSmoke|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "style": [["SMOKE", "FIRE", "BOTH"]], "show_flows": ["BOOLEAN", {"default": false}]}}
|
||||
Object_RandomizeTransform|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "random_seed": ["INT", {"min": 0, "max": 10000, "default": 0}], "use_delta": ["BOOLEAN", {"default": false}], "use_loc": ["BOOLEAN", {"default": true}], "loc": ["B_VECTOR3", {}], "use_rot": ["BOOLEAN", {"default": true}], "rot": ["B_VECTOR3", {}], "use_scale": ["BOOLEAN", {"default": true}], "scale_even": ["BOOLEAN", {"default": false}], "scale": ["B_VECTOR3", {}]}}
|
||||
Object_ResetOverrideLibrary|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_RotationClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "clear_delta": ["BOOLEAN", {"default": false}]}}
|
||||
Object_ScaleClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "clear_delta": ["BOOLEAN", {"default": false}]}}
|
||||
Object_SelectAll|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "action": [["TOGGLE", "SELECT", "DESELECT", "INVERT"]]}}
|
||||
Object_SelectByType|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "extend": ["BOOLEAN", {"default": false}], "type": [["MESH", "CURVE", "SURFACE", "META", "FONT", "CURVES", "POINTCLOUD", "VOLUME", "GPENCIL", "ARMATURE", "LATTICE", "EMPTY", "LIGHT", "LIGHT_PROBE", "CAMERA", "SPEAKER"]]}}
|
||||
Object_SelectCamera|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "extend": ["BOOLEAN", {"default": false}]}}
|
||||
Object_SelectGrouped|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "extend": ["BOOLEAN", {"default": false}], "type": [["CHILDREN_RECURSIVE", "CHILDREN", "PARENT", "SIBLINGS", "TYPE", "COLLECTION", "HOOK", "PASS", "COLOR", "KEYINGSET", "LIGHT_TYPE"]]}}
|
||||
Object_SelectHierarchy|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "direction": [["PARENT", "CHILD"]], "extend": ["BOOLEAN", {"default": false}]}}
|
||||
Object_SelectLess|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_SelectLinked|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "extend": ["BOOLEAN", {"default": false}], "type": [["OBDATA", "MATERIAL", "DUPGROUP", "PARTICLE", "LIBRARY", "LIBRARY_OBDATA"]]}}
|
||||
Object_SelectMirror|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "extend": ["BOOLEAN", {"default": false}]}}
|
||||
Object_SelectMore|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_SelectPattern|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "pattern": ["STRING", {"default": "*", "multiline": false}], "case_sensitive": ["BOOLEAN", {"default": false}], "extend": ["BOOLEAN", {"default": true}]}}
|
||||
Object_SelectRandom|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "ratio": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.5}], "seed": ["INT", {"min": 0, "max": 2147483647, "default": 0}], "action": [["SELECT", "DESELECT"]]}}
|
||||
Object_SelectSameCollection|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "collection": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_ShadeFlat|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_ShadeSmooth|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_auto_smooth": ["BOOLEAN", {"default": false}], "auto_smooth_angle": ["FLOAT", {"min": 0.0, "max": 3.1415927410125732, "default": 0.5235987901687622}]}}
|
||||
Object_ShaderfxAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["FX_BLUR", "FX_COLORIZE", "FX_FLIP", "FX_GLOW", "FX_PIXEL", "FX_RIM", "FX_SHADOW", "FX_SWIRL", "FX_WAVE"]]}}
|
||||
Object_ShaderfxCopy|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "shaderfx": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_ShaderfxMoveDown|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "shaderfx": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_ShaderfxMoveToIndex|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "shaderfx": ["STRING", {"default": "", "multiline": false}], "index": ["INT", {"min": 0, "max": 2147483647, "default": 0}]}}
|
||||
Object_ShaderfxMoveUp|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "shaderfx": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_ShaderfxRemove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "shaderfx": ["STRING", {"default": "", "multiline": false}], "report": ["BOOLEAN", {"default": false}]}}
|
||||
Object_ShapeKeyAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "from_mix": ["BOOLEAN", {"default": true}]}}
|
||||
Object_ShapeKeyClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_ShapeKeyMirror|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_topology": ["BOOLEAN", {"default": false}]}}
|
||||
Object_ShapeKeyMove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["TOP", "UP", "DOWN", "BOTTOM"]]}}
|
||||
Object_ShapeKeyRemove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "all": ["BOOLEAN", {"default": false}], "apply_mix": ["BOOLEAN", {"default": false}]}}
|
||||
Object_ShapeKeyRetime|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_ShapeKeyTransfer|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mode": [["OFFSET", "RELATIVE_FACE", "RELATIVE_EDGE"]], "use_clamp": ["BOOLEAN", {"default": false}]}}
|
||||
Object_SimulationNodesCacheBake|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "selected": ["BOOLEAN", {"default": false}]}}
|
||||
Object_SimulationNodesCacheCalculateToFrame|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "selected": ["BOOLEAN", {"default": false}]}}
|
||||
Object_SimulationNodesCacheDelete|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "selected": ["BOOLEAN", {"default": false}]}}
|
||||
Object_SkinArmatureCreate|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_SkinLooseMarkClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "action": [["MARK", "CLEAR"]]}}
|
||||
Object_SkinRadiiEqualize|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_SkinRootMark|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_SpeakerAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "enter_editmode": ["BOOLEAN", {"default": false}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Object_SubdivisionSet|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "level": ["INT", {"min": -100, "max": 100, "default": 1}], "relative": ["BOOLEAN", {"default": false}]}}
|
||||
Object_SurfacedeformBind|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "modifier": ["STRING", {"default": "", "multiline": false}]}}
|
||||
Object_TextAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "radius": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 1.0}], "enter_editmode": ["BOOLEAN", {"default": false}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Object_TrackClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["CLEAR", "CLEAR_KEEP_TRANSFORM"]]}}
|
||||
Object_TrackSet|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["DAMPTRACK", "TRACKTO", "LOCKTRACK"]]}}
|
||||
Object_TransferMode|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_flash_on_transfer": ["BOOLEAN", {"default": true}]}}
|
||||
Object_TransformApply|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "location": ["BOOLEAN", {"default": true}], "rotation": ["BOOLEAN", {"default": true}], "scale": ["BOOLEAN", {"default": true}], "properties": ["BOOLEAN", {"default": true}], "isolate_users": ["BOOLEAN", {"default": false}]}}
|
||||
Object_TransformAxisTarget|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_TransformToMouse|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "name": ["STRING", {"default": "", "multiline": false}], "session_uuid": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "matrix": ["B_VECTOR16", {}], "drop_x": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "drop_y": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}]}}
|
||||
Object_TransformsToDeltas|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mode": [["ALL", "LOC", "ROT", "SCALE"]], "reset_values": ["BOOLEAN", {"default": true}]}}
|
||||
Object_UnlinkData|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_VisualTransformApply|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_VolumeAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Object_VolumeImport|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "filepath": ["STRING", {"default": "", "multiline": false}], "directory": ["STRING", {"default": "", "multiline": false}], "hide_props_region": ["BOOLEAN", {"default": true}], "check_existing": ["BOOLEAN", {"default": false}], "filter_blender": ["BOOLEAN", {"default": false}], "filter_backup": ["BOOLEAN", {"default": false}], "filter_image": ["BOOLEAN", {"default": false}], "filter_movie": ["BOOLEAN", {"default": false}], "filter_python": ["BOOLEAN", {"default": false}], "filter_font": ["BOOLEAN", {"default": false}], "filter_sound": ["BOOLEAN", {"default": false}], "filter_text": ["BOOLEAN", {"default": false}], "filter_archive": ["BOOLEAN", {"default": false}], "filter_btx": ["BOOLEAN", {"default": false}], "filter_collada": ["BOOLEAN", {"default": false}], "filter_alembic": ["BOOLEAN", {"default": false}], "filter_usd": ["BOOLEAN", {"default": false}], "filter_obj": ["BOOLEAN", {"default": false}], "filter_volume": ["BOOLEAN", {"default": true}], "filter_folder": ["BOOLEAN", {"default": true}], "filter_blenlib": ["BOOLEAN", {"default": false}], "filemode": ["INT", {"min": 1, "max": 9, "default": 9}], "relative_path": ["BOOLEAN", {"default": true}], "display_type": [["DEFAULT", "LIST_VERTICAL", "LIST_HORIZONTAL", "THUMBNAIL"]], "sort_method": ["B_ENUM", {}], "use_sequence_detection": ["BOOLEAN", {"default": true}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Object_VoxelRemesh|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_VoxelSizeEdit|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_VertexGroupAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_VertexGroupAssign|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_VertexGroupAssignNew|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_VertexGroupClean|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "group_select_mode": ["B_ENUM", {}], "limit": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.0}], "keep_single": ["BOOLEAN", {"default": false}]}}
|
||||
Object_VertexGroupCopy|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_VertexGroupCopyToSelected|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_VertexGroupDeselect|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_VertexGroupInvert|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "group_select_mode": ["B_ENUM", {}], "auto_assign": ["BOOLEAN", {"default": true}], "auto_remove": ["BOOLEAN", {"default": true}]}}
|
||||
Object_VertexGroupLevels|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "group_select_mode": ["B_ENUM", {}], "offset": ["FLOAT", {"min": -1.0, "max": 1.0, "default": 0.0}], "gain": ["FLOAT", {"min": 0.0, "max": 3.4028234663852886e+38, "default": 1.0}]}}
|
||||
Object_VertexGroupLimitTotal|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "group_select_mode": ["B_ENUM", {}], "limit": ["INT", {"min": 1, "max": 32, "default": 4}]}}
|
||||
Object_VertexGroupLock|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "action": [["TOGGLE", "LOCK", "UNLOCK", "INVERT"]], "mask": [["ALL", "SELECTED", "UNSELECTED", "INVERT_UNSELECTED"]]}}
|
||||
Object_VertexGroupMirror|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mirror_weights": ["BOOLEAN", {"default": true}], "flip_group_names": ["BOOLEAN", {"default": true}], "all_groups": ["BOOLEAN", {"default": false}], "use_topology": ["BOOLEAN", {"default": false}]}}
|
||||
Object_VertexGroupMove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "direction": [["UP", "DOWN"]]}}
|
||||
Object_VertexGroupNormalize|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_VertexGroupNormalizeAll|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "group_select_mode": ["B_ENUM", {}], "lock_active": ["BOOLEAN", {"default": true}]}}
|
||||
Object_VertexGroupQuantize|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "group_select_mode": ["B_ENUM", {}], "steps": ["INT", {"min": 1, "max": 1000, "default": 4}]}}
|
||||
Object_VertexGroupRemove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "all": ["BOOLEAN", {"default": false}], "all_unlocked": ["BOOLEAN", {"default": false}]}}
|
||||
Object_VertexGroupRemoveFrom|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_all_groups": ["BOOLEAN", {"default": false}], "use_all_verts": ["BOOLEAN", {"default": false}]}}
|
||||
Object_VertexGroupSelect|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_VertexGroupSetActive|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "group": ["B_ENUM", {}]}}
|
||||
Object_VertexGroupSmooth|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "group_select_mode": ["B_ENUM", {}], "factor": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.5}], "repeat": ["INT", {"min": 1, "max": 10000, "default": 1}], "expand": ["FLOAT", {"min": -1.0, "max": 1.0, "default": 0.0}]}}
|
||||
Object_VertexGroupSort|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "sort_type": [["NAME", "BONE_HIERARCHY"]]}}
|
||||
Object_VertexParentSet|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "confirm": ["BOOLEAN", {"default": true}]}}
|
||||
Object_VertexWeightCopy|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_VertexWeightDelete|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "weight_group": ["INT", {"min": -1, "max": 2147483647, "default": -1}]}}
|
||||
Object_VertexWeightNormalizeActiveVertex|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Object_VertexWeightPaste|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "weight_group": ["INT", {"min": -1, "max": 2147483647, "default": -1}]}}
|
||||
Object_VertexWeightSetActive|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "weight_group": ["INT", {"min": -1, "max": 2147483647, "default": -1}]}}
|
||||
Mesh_PrimitiveCircleAdd|{"required": {}, "optional": {"seed": ["INT", {"default": 0, "min": 0, "max": 18446744073709551615}], "vertices": ["INT", {"min": 3, "max": 10000000, "default": 32}], "radius": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 1.0}], "fill_type": [["NOTHING", "NGON", "TRIFAN"]], "calc_uvs": ["BOOLEAN", {"default": true}], "enter_editmode": ["BOOLEAN", {"default": false}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Mesh_PrimitiveConeAdd|{"required": {}, "optional": {"seed": ["INT", {"default": 0, "min": 0, "max": 18446744073709551615}], "vertices": ["INT", {"min": 3, "max": 10000000, "default": 32}], "radius1": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 1.0}], "radius2": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 0.0}], "depth": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 2.0}], "end_fill_type": [["NOTHING", "NGON", "TRIFAN"]], "calc_uvs": ["BOOLEAN", {"default": true}], "enter_editmode": ["BOOLEAN", {"default": false}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Mesh_PrimitiveCubeAdd|{"required": {}, "optional": {"seed": ["INT", {"default": 0, "min": 0, "max": 18446744073709551615}], "size": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 2.0}], "calc_uvs": ["BOOLEAN", {"default": true}], "enter_editmode": ["BOOLEAN", {"default": false}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Mesh_PrimitiveCubeAddGizmo|{"required": {}, "optional": {"seed": ["INT", {"default": 0, "min": 0, "max": 18446744073709551615}], "calc_uvs": ["BOOLEAN", {"default": true}], "enter_editmode": ["BOOLEAN", {"default": false}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}], "matrix": ["B_VECTOR16", {}]}}
|
||||
Mesh_PrimitiveCylinderAdd|{"required": {}, "optional": {"seed": ["INT", {"default": 0, "min": 0, "max": 18446744073709551615}], "vertices": ["INT", {"min": 3, "max": 10000000, "default": 32}], "radius": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 1.0}], "depth": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 2.0}], "end_fill_type": [["NOTHING", "NGON", "TRIFAN"]], "calc_uvs": ["BOOLEAN", {"default": true}], "enter_editmode": ["BOOLEAN", {"default": false}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Mesh_PrimitiveGridAdd|{"required": {}, "optional": {"seed": ["INT", {"default": 0, "min": 0, "max": 18446744073709551615}], "x_subdivisions": ["INT", {"min": 1, "max": 10000000, "default": 10}], "y_subdivisions": ["INT", {"min": 1, "max": 10000000, "default": 10}], "size": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 2.0}], "calc_uvs": ["BOOLEAN", {"default": true}], "enter_editmode": ["BOOLEAN", {"default": false}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Mesh_PrimitiveIcoSphereAdd|{"required": {}, "optional": {"seed": ["INT", {"default": 0, "min": 0, "max": 18446744073709551615}], "subdivisions": ["INT", {"min": 1, "max": 10, "default": 2}], "radius": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 1.0}], "calc_uvs": ["BOOLEAN", {"default": true}], "enter_editmode": ["BOOLEAN", {"default": false}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Mesh_PrimitiveMonkeyAdd|{"required": {}, "optional": {"seed": ["INT", {"default": 0, "min": 0, "max": 18446744073709551615}], "size": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 2.0}], "calc_uvs": ["BOOLEAN", {"default": true}], "enter_editmode": ["BOOLEAN", {"default": false}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Mesh_PrimitivePlaneAdd|{"required": {}, "optional": {"seed": ["INT", {"default": 0, "min": 0, "max": 18446744073709551615}], "size": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 2.0}], "calc_uvs": ["BOOLEAN", {"default": true}], "enter_editmode": ["BOOLEAN", {"default": false}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Mesh_PrimitiveTorusAdd|{"required": {}, "optional": {"seed": ["INT", {"default": 0, "min": 0, "max": 18446744073709551615}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "major_segments": ["INT", {"min": 3, "max": 256, "default": 48}], "minor_segments": ["INT", {"min": 3, "max": 256, "default": 12}], "mode": [["MAJOR_MINOR", "EXT_INT"]], "major_radius": ["FLOAT", {"min": 0.0, "max": 10000.0, "default": 1.0}], "minor_radius": ["FLOAT", {"min": 0.0, "max": 10000.0, "default": 0.25}], "abso_major_rad": ["FLOAT", {"min": 0.0, "max": 10000.0, "default": 1.25}], "abso_minor_rad": ["FLOAT", {"min": 0.0, "max": 10000.0, "default": 0.75}], "generate_uvs": ["BOOLEAN", {"default": true}]}}
|
||||
Mesh_PrimitiveUvSphereAdd|{"required": {}, "optional": {"seed": ["INT", {"default": 0, "min": 0, "max": 18446744073709551615}], "segments": ["INT", {"min": 3, "max": 100000, "default": 32}], "ring_count": ["INT", {"min": 3, "max": 100000, "default": 16}], "radius": ["FLOAT", {"min": 0.0, "max": 999999995904.0, "default": 1.0}], "calc_uvs": ["BOOLEAN", {"default": true}], "enter_editmode": ["BOOLEAN", {"default": false}], "align": [["WORLD", "VIEW", "CURSOR"]], "location": ["B_VECTOR3", {}], "rotation": ["B_VECTOR3", {}], "scale": ["B_VECTOR3", {}]}}
|
||||
Transform_BboneResize|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["B_VECTOR3", {}], "orient_type": ["B_ENUM", {}], "orient_matrix": ["B_VECTOR9", {}], "orient_matrix_type": ["B_ENUM", {}], "constraint_axis": ["B_BOOLEAN3", {}], "mirror": ["BOOLEAN", {"default": false}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Transform_Bend|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["B_VECTOR1", {}], "mirror": ["BOOLEAN", {"default": false}], "use_proportional_edit": ["BOOLEAN", {"default": false}], "proportional_edit_falloff": [["SMOOTH", "SPHERE", "ROOT", "INVERSE_SQUARE", "SHARP", "LINEAR", "CONSTANT", "RANDOM"]], "proportional_size": ["FLOAT", {"min": 9.999999974752427e-07, "max": 999999995904.0, "default": 1.0}], "use_proportional_connected": ["BOOLEAN", {"default": false}], "use_proportional_projected": ["BOOLEAN", {"default": false}], "snap": ["BOOLEAN", {"default": false}], "gpencil_strokes": ["BOOLEAN", {"default": false}], "center_override": ["B_VECTOR3", {}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Transform_CreateOrientation|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "name": ["STRING", {"default": "", "multiline": false}], "use_view": ["BOOLEAN", {"default": false}], "use": ["BOOLEAN", {"default": false}], "overwrite": ["BOOLEAN", {"default": false}]}}
|
||||
Transform_DeleteOrientation|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Transform_EdgeBevelweight|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["FLOAT", {"min": -1.0, "max": 1.0, "default": 0.0}], "snap": ["BOOLEAN", {"default": false}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Transform_EdgeCrease|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["FLOAT", {"min": -1.0, "max": 1.0, "default": 0.0}], "snap": ["BOOLEAN", {"default": false}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Transform_EdgeSlide|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["FLOAT", {"min": -10.0, "max": 10.0, "default": 0.0}], "single_side": ["BOOLEAN", {"default": false}], "use_even": ["BOOLEAN", {"default": false}], "flipped": ["BOOLEAN", {"default": false}], "use_clamp": ["BOOLEAN", {"default": true}], "mirror": ["BOOLEAN", {"default": false}], "snap": ["BOOLEAN", {"default": false}], "snap_elements": ["B_ENUM_SET", {}], "use_snap_project": ["BOOLEAN", {"default": false}], "snap_target": [["CLOSEST", "CENTER", "MEDIAN", "ACTIVE"]], "use_snap_self": ["BOOLEAN", {"default": true}], "use_snap_edit": ["BOOLEAN", {"default": true}], "use_snap_nonedit": ["BOOLEAN", {"default": true}], "use_snap_selectable": ["BOOLEAN", {"default": false}], "snap_point": ["B_VECTOR3", {}], "correct_uv": ["BOOLEAN", {"default": true}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Transform_FromGizmo|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
Transform_Mirror|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "orient_type": ["B_ENUM", {}], "orient_matrix": ["B_VECTOR9", {}], "orient_matrix_type": ["B_ENUM", {}], "constraint_axis": ["B_BOOLEAN3", {}], "gpencil_strokes": ["BOOLEAN", {"default": false}], "center_override": ["B_VECTOR3", {}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Transform_PushPull|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["FLOAT", {"min": -3.4028234663852886e+38, "max": 3.4028234663852886e+38, "default": 0.0}], "mirror": ["BOOLEAN", {"default": false}], "use_proportional_edit": ["BOOLEAN", {"default": false}], "proportional_edit_falloff": [["SMOOTH", "SPHERE", "ROOT", "INVERSE_SQUARE", "SHARP", "LINEAR", "CONSTANT", "RANDOM"]], "proportional_size": ["FLOAT", {"min": 9.999999974752427e-07, "max": 999999995904.0, "default": 1.0}], "use_proportional_connected": ["BOOLEAN", {"default": false}], "use_proportional_projected": ["BOOLEAN", {"default": false}], "snap": ["BOOLEAN", {"default": false}], "center_override": ["B_VECTOR3", {}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Transform_Resize|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["B_VECTOR3", {}], "mouse_dir_constraint": ["B_VECTOR3", {}], "orient_type": ["B_ENUM", {}], "orient_matrix": ["B_VECTOR9", {}], "orient_matrix_type": ["B_ENUM", {}], "constraint_axis": ["B_BOOLEAN3", {}], "mirror": ["BOOLEAN", {"default": false}], "use_proportional_edit": ["BOOLEAN", {"default": false}], "proportional_edit_falloff": [["SMOOTH", "SPHERE", "ROOT", "INVERSE_SQUARE", "SHARP", "LINEAR", "CONSTANT", "RANDOM"]], "proportional_size": ["FLOAT", {"min": 9.999999974752427e-07, "max": 999999995904.0, "default": 1.0}], "use_proportional_connected": ["BOOLEAN", {"default": false}], "use_proportional_projected": ["BOOLEAN", {"default": false}], "snap": ["BOOLEAN", {"default": false}], "snap_elements": ["B_ENUM_SET", {}], "use_snap_project": ["BOOLEAN", {"default": false}], "snap_target": [["CLOSEST", "CENTER", "MEDIAN", "ACTIVE"]], "use_snap_self": ["BOOLEAN", {"default": true}], "use_snap_edit": ["BOOLEAN", {"default": true}], "use_snap_nonedit": ["BOOLEAN", {"default": true}], "use_snap_selectable": ["BOOLEAN", {"default": false}], "snap_point": ["B_VECTOR3", {}], "gpencil_strokes": ["BOOLEAN", {"default": false}], "texture_space": ["BOOLEAN", {"default": false}], "remove_on_cancel": ["BOOLEAN", {"default": false}], "center_override": ["B_VECTOR3", {}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Transform_Rotate|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["FLOAT", {"min": -3.4028234663852886e+38, "max": 3.4028234663852886e+38, "default": 0.0}], "orient_axis": [["X", "Y", "Z"]], "orient_type": ["B_ENUM", {}], "orient_matrix": ["B_VECTOR9", {}], "orient_matrix_type": ["B_ENUM", {}], "constraint_axis": ["B_BOOLEAN3", {}], "mirror": ["BOOLEAN", {"default": false}], "use_proportional_edit": ["BOOLEAN", {"default": false}], "proportional_edit_falloff": [["SMOOTH", "SPHERE", "ROOT", "INVERSE_SQUARE", "SHARP", "LINEAR", "CONSTANT", "RANDOM"]], "proportional_size": ["FLOAT", {"min": 9.999999974752427e-07, "max": 999999995904.0, "default": 1.0}], "use_proportional_connected": ["BOOLEAN", {"default": false}], "use_proportional_projected": ["BOOLEAN", {"default": false}], "snap": ["BOOLEAN", {"default": false}], "snap_elements": ["B_ENUM_SET", {}], "use_snap_project": ["BOOLEAN", {"default": false}], "snap_target": [["CLOSEST", "CENTER", "MEDIAN", "ACTIVE"]], "use_snap_self": ["BOOLEAN", {"default": true}], "use_snap_edit": ["BOOLEAN", {"default": true}], "use_snap_nonedit": ["BOOLEAN", {"default": true}], "use_snap_selectable": ["BOOLEAN", {"default": false}], "snap_point": ["B_VECTOR3", {}], "gpencil_strokes": ["BOOLEAN", {"default": false}], "center_override": ["B_VECTOR3", {}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Transform_RotateNormal|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["FLOAT", {"min": -3.4028234663852886e+38, "max": 3.4028234663852886e+38, "default": 0.0}], "orient_axis": [["X", "Y", "Z"]], "orient_type": ["B_ENUM", {}], "orient_matrix": ["B_VECTOR9", {}], "orient_matrix_type": ["B_ENUM", {}], "constraint_axis": ["B_BOOLEAN3", {}], "mirror": ["BOOLEAN", {"default": false}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Transform_SelectOrientation|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "orientation": ["B_ENUM", {}]}}
|
||||
Transform_SeqSlide|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["B_VECTOR2", {}], "snap": ["BOOLEAN", {"default": false}], "view2d_edge_pan": ["BOOLEAN", {"default": false}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Transform_Shear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["FLOAT", {"min": -3.4028234663852886e+38, "max": 3.4028234663852886e+38, "default": 0.0}], "orient_axis": [["X", "Y", "Z"]], "orient_axis_ortho": [["X", "Y", "Z"]], "orient_type": ["B_ENUM", {}], "orient_matrix": ["B_VECTOR9", {}], "orient_matrix_type": ["B_ENUM", {}], "mirror": ["BOOLEAN", {"default": false}], "use_proportional_edit": ["BOOLEAN", {"default": false}], "proportional_edit_falloff": [["SMOOTH", "SPHERE", "ROOT", "INVERSE_SQUARE", "SHARP", "LINEAR", "CONSTANT", "RANDOM"]], "proportional_size": ["FLOAT", {"min": 9.999999974752427e-07, "max": 999999995904.0, "default": 1.0}], "use_proportional_connected": ["BOOLEAN", {"default": false}], "use_proportional_projected": ["BOOLEAN", {"default": false}], "snap": ["BOOLEAN", {"default": false}], "gpencil_strokes": ["BOOLEAN", {"default": false}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Transform_ShrinkFatten|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["FLOAT", {"min": -3.4028234663852886e+38, "max": 3.4028234663852886e+38, "default": 0.0}], "use_even_offset": ["BOOLEAN", {"default": false}], "mirror": ["BOOLEAN", {"default": false}], "use_proportional_edit": ["BOOLEAN", {"default": false}], "proportional_edit_falloff": [["SMOOTH", "SPHERE", "ROOT", "INVERSE_SQUARE", "SHARP", "LINEAR", "CONSTANT", "RANDOM"]], "proportional_size": ["FLOAT", {"min": 9.999999974752427e-07, "max": 999999995904.0, "default": 1.0}], "use_proportional_connected": ["BOOLEAN", {"default": false}], "use_proportional_projected": ["BOOLEAN", {"default": false}], "snap": ["BOOLEAN", {"default": false}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Transform_SkinResize|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["B_VECTOR3", {}], "orient_type": ["B_ENUM", {}], "orient_matrix": ["B_VECTOR9", {}], "orient_matrix_type": ["B_ENUM", {}], "constraint_axis": ["B_BOOLEAN3", {}], "mirror": ["BOOLEAN", {"default": false}], "use_proportional_edit": ["BOOLEAN", {"default": false}], "proportional_edit_falloff": [["SMOOTH", "SPHERE", "ROOT", "INVERSE_SQUARE", "SHARP", "LINEAR", "CONSTANT", "RANDOM"]], "proportional_size": ["FLOAT", {"min": 9.999999974752427e-07, "max": 999999995904.0, "default": 1.0}], "use_proportional_connected": ["BOOLEAN", {"default": false}], "use_proportional_projected": ["BOOLEAN", {"default": false}], "snap": ["BOOLEAN", {"default": false}], "snap_elements": ["B_ENUM_SET", {}], "use_snap_project": ["BOOLEAN", {"default": false}], "snap_target": [["CLOSEST", "CENTER", "MEDIAN", "ACTIVE"]], "use_snap_self": ["BOOLEAN", {"default": true}], "use_snap_edit": ["BOOLEAN", {"default": true}], "use_snap_nonedit": ["BOOLEAN", {"default": true}], "use_snap_selectable": ["BOOLEAN", {"default": false}], "snap_point": ["B_VECTOR3", {}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Transform_Tilt|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["FLOAT", {"min": -3.4028234663852886e+38, "max": 3.4028234663852886e+38, "default": 0.0}], "mirror": ["BOOLEAN", {"default": false}], "use_proportional_edit": ["BOOLEAN", {"default": false}], "proportional_edit_falloff": [["SMOOTH", "SPHERE", "ROOT", "INVERSE_SQUARE", "SHARP", "LINEAR", "CONSTANT", "RANDOM"]], "proportional_size": ["FLOAT", {"min": 9.999999974752427e-07, "max": 999999995904.0, "default": 1.0}], "use_proportional_connected": ["BOOLEAN", {"default": false}], "use_proportional_projected": ["BOOLEAN", {"default": false}], "snap": ["BOOLEAN", {"default": false}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Transform_Tosphere|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.0}], "mirror": ["BOOLEAN", {"default": false}], "use_proportional_edit": ["BOOLEAN", {"default": false}], "proportional_edit_falloff": [["SMOOTH", "SPHERE", "ROOT", "INVERSE_SQUARE", "SHARP", "LINEAR", "CONSTANT", "RANDOM"]], "proportional_size": ["FLOAT", {"min": 9.999999974752427e-07, "max": 999999995904.0, "default": 1.0}], "use_proportional_connected": ["BOOLEAN", {"default": false}], "use_proportional_projected": ["BOOLEAN", {"default": false}], "snap": ["BOOLEAN", {"default": false}], "gpencil_strokes": ["BOOLEAN", {"default": false}], "center_override": ["B_VECTOR3", {}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Transform_Trackball|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["B_VECTOR2", {}], "mirror": ["BOOLEAN", {"default": false}], "use_proportional_edit": ["BOOLEAN", {"default": false}], "proportional_edit_falloff": [["SMOOTH", "SPHERE", "ROOT", "INVERSE_SQUARE", "SHARP", "LINEAR", "CONSTANT", "RANDOM"]], "proportional_size": ["FLOAT", {"min": 9.999999974752427e-07, "max": 999999995904.0, "default": 1.0}], "use_proportional_connected": ["BOOLEAN", {"default": false}], "use_proportional_projected": ["BOOLEAN", {"default": false}], "snap": ["BOOLEAN", {"default": false}], "gpencil_strokes": ["BOOLEAN", {"default": false}], "center_override": ["B_VECTOR3", {}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Transform_Transform|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mode": [["INIT", "DUMMY", "TRANSLATION", "ROTATION", "RESIZE", "SKIN_RESIZE", "TOSPHERE", "SHEAR", "BEND", "SHRINKFATTEN", "TILT", "TRACKBALL", "PUSHPULL", "CREASE", "VERTEX_CREASE", "MIRROR", "BONE_SIZE", "BONE_ENVELOPE", "BONE_ENVELOPE_DIST", "CURVE_SHRINKFATTEN", "MASK_SHRINKFATTEN", "GPENCIL_SHRINKFATTEN", "BONE_ROLL", "TIME_TRANSLATE", "TIME_SLIDE", "TIME_SCALE", "TIME_EXTEND", "BAKE_TIME", "BWEIGHT", "ALIGN", "EDGESLIDE", "SEQSLIDE", "GPENCIL_OPACITY"]], "value": ["B_VECTOR4", {}], "orient_axis": [["X", "Y", "Z"]], "orient_type": [["GLOBAL", "LOCAL", "NORMAL", "GIMBAL", "VIEW", "CURSOR", "PARENT"]], "orient_matrix": ["B_VECTOR9", {}], "orient_matrix_type": [["GLOBAL", "LOCAL", "NORMAL", "GIMBAL", "VIEW", "CURSOR", "PARENT"]], "constraint_axis": ["B_BOOLEAN3", {}], "mirror": ["BOOLEAN", {"default": false}], "use_proportional_edit": ["BOOLEAN", {"default": false}], "proportional_edit_falloff": [["SMOOTH", "SPHERE", "ROOT", "INVERSE_SQUARE", "SHARP", "LINEAR", "CONSTANT", "RANDOM"]], "proportional_size": ["FLOAT", {"min": 9.999999974752427e-07, "max": 999999995904.0, "default": 1.0}], "use_proportional_connected": ["BOOLEAN", {"default": false}], "use_proportional_projected": ["BOOLEAN", {"default": false}], "snap": ["BOOLEAN", {"default": false}], "snap_elements": ["B_ENUM_SET", {}], "use_snap_project": ["BOOLEAN", {"default": false}], "snap_target": [["CLOSEST", "CENTER", "MEDIAN", "ACTIVE"]], "use_snap_self": ["BOOLEAN", {"default": true}], "use_snap_edit": ["BOOLEAN", {"default": true}], "use_snap_nonedit": ["BOOLEAN", {"default": true}], "use_snap_selectable": ["BOOLEAN", {"default": false}], "snap_point": ["B_VECTOR3", {}], "snap_align": ["BOOLEAN", {"default": false}], "snap_normal": ["B_VECTOR3", {}], "gpencil_strokes": ["BOOLEAN", {"default": false}], "center_override": ["B_VECTOR3", {}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Transform_Translate|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["B_VECTOR3", {}], "orient_type": [["GLOBAL", "LOCAL", "NORMAL", "GIMBAL", "VIEW", "CURSOR", "PARENT"]], "orient_matrix": ["B_VECTOR9", {}], "orient_matrix_type": [["GLOBAL", "LOCAL", "NORMAL", "GIMBAL", "VIEW", "CURSOR", "PARENT"]], "constraint_axis": ["B_BOOLEAN3", {}], "mirror": ["BOOLEAN", {"default": false}], "use_proportional_edit": ["BOOLEAN", {"default": false}], "proportional_edit_falloff": [["SMOOTH", "SPHERE", "ROOT", "INVERSE_SQUARE", "SHARP", "LINEAR", "CONSTANT", "RANDOM"]], "proportional_size": ["FLOAT", {"min": 9.999999974752427e-07, "max": 999999995904.0, "default": 1.0}], "use_proportional_connected": ["BOOLEAN", {"default": false}], "use_proportional_projected": ["BOOLEAN", {"default": false}], "snap": ["BOOLEAN", {"default": false}], "snap_elements": ["B_ENUM_SET", {}], "use_snap_project": ["BOOLEAN", {"default": false}], "snap_target": [["CLOSEST", "CENTER", "MEDIAN", "ACTIVE"]], "use_snap_self": ["BOOLEAN", {"default": true}], "use_snap_edit": ["BOOLEAN", {"default": true}], "use_snap_nonedit": ["BOOLEAN", {"default": true}], "use_snap_selectable": ["BOOLEAN", {"default": false}], "snap_point": ["B_VECTOR3", {}], "snap_align": ["BOOLEAN", {"default": false}], "snap_normal": ["B_VECTOR3", {}], "gpencil_strokes": ["BOOLEAN", {"default": false}], "cursor_transform": ["BOOLEAN", {"default": false}], "texture_space": ["BOOLEAN", {"default": false}], "remove_on_cancel": ["BOOLEAN", {"default": false}], "view2d_edge_pan": ["BOOLEAN", {"default": false}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}], "use_automerge_and_split": ["BOOLEAN", {"default": false}]}}
|
||||
Transform_VertCrease|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["FLOAT", {"min": -1.0, "max": 1.0, "default": 0.0}], "snap": ["BOOLEAN", {"default": false}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Transform_VertSlide|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["FLOAT", {"min": -10.0, "max": 10.0, "default": 0.0}], "use_even": ["BOOLEAN", {"default": false}], "flipped": ["BOOLEAN", {"default": false}], "use_clamp": ["BOOLEAN", {"default": true}], "mirror": ["BOOLEAN", {"default": false}], "snap": ["BOOLEAN", {"default": false}], "snap_elements": ["B_ENUM_SET", {}], "use_snap_project": ["BOOLEAN", {"default": false}], "snap_target": [["CLOSEST", "CENTER", "MEDIAN", "ACTIVE"]], "use_snap_self": ["BOOLEAN", {"default": true}], "use_snap_edit": ["BOOLEAN", {"default": true}], "use_snap_nonedit": ["BOOLEAN", {"default": true}], "use_snap_selectable": ["BOOLEAN", {"default": false}], "snap_point": ["B_VECTOR3", {}], "correct_uv": ["BOOLEAN", {"default": true}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}]}}
|
||||
Transform_VertexRandom|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "offset": ["FLOAT", {"min": -3.4028234663852886e+38, "max": 3.4028234663852886e+38, "default": 0.0}], "uniform": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.0}], "normal": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.0}], "seed": ["INT", {"min": 0, "max": 10000, "default": 0}], "wait_for_input": ["BOOLEAN", {"default": true}]}}
|
||||
Transform_VertexWarp|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "warp_angle": ["FLOAT", {"min": -3.4028234663852886e+38, "max": 3.4028234663852886e+38, "default": 6.2831854820251465}], "offset_angle": ["FLOAT", {"min": -3.4028234663852886e+38, "max": 3.4028234663852886e+38, "default": 0.0}], "min": ["FLOAT", {"min": -3.4028234663852886e+38, "max": 3.4028234663852886e+38, "default": -1.0}], "max": ["FLOAT", {"min": -3.4028234663852886e+38, "max": 3.4028234663852886e+38, "default": 1.0}], "viewmat": ["B_VECTOR16", {}], "center": ["B_VECTOR3", {}]}}
|
||||
ObjectSet_ActiveMaterialIndex|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["INT", {"default": 0}]}}
|
||||
ObjectSet_ActiveShapeKeyIndex|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["INT", {"default": 2}]}}
|
||||
ObjectSet_AddRestPositionAttribute|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_DisplayBoundsType|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["STRING", {"default": "BOX"}]}}
|
||||
ObjectSet_DisplayType|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["STRING", {"default": "TEXTURED"}]}}
|
||||
ObjectSet_EmptyDisplaySize|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["FLOAT", {"default": 1.0}]}}
|
||||
ObjectSet_EmptyDisplayType|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["STRING", {"default": "ARROWS"}]}}
|
||||
ObjectSet_EmptyImageDepth|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["STRING", {"default": "DEFAULT"}]}}
|
||||
ObjectSet_EmptyImageSide|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["STRING", {"default": "DOUBLE_SIDED"}]}}
|
||||
ObjectSet_HideRender|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_HideSelect|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_HideViewport|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_InstanceFacesScale|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["FLOAT", {"default": 1.0}]}}
|
||||
ObjectSet_InstanceType|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["STRING", {"default": "NONE"}]}}
|
||||
ObjectSet_IsEmbeddedData|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_IsEvaluated|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_IsFromInstancer|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_IsFromSet|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_IsHoldout|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_IsInstancer|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_IsLibraryIndirect|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_IsMissing|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_IsRuntimeData|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_IsShadowCatcher|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_Lightgroup|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["STRING", {"default": ""}]}}
|
||||
ObjectSet_LockRotationW|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_LockRotations4D|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_Mode|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["STRING", {"default": "OBJECT"}]}}
|
||||
ObjectSet_Name|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["STRING", {"default": "Cube"}]}}
|
||||
ObjectSet_NameFull|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["STRING", {"default": "Cube"}]}}
|
||||
ObjectSet_ParentBone|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["STRING", {"default": ""}]}}
|
||||
ObjectSet_ParentType|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["STRING", {"default": "OBJECT"}]}}
|
||||
ObjectSet_PassIndex|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["INT", {"default": 0}]}}
|
||||
ObjectSet_RotationMode|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["STRING", {"default": "XYZ"}]}}
|
||||
ObjectSet_ShowAllEdges|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_ShowAxis|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_ShowBounds|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_ShowEmptyImageOnlyAxisAligned|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_ShowEmptyImageOrthographic|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": true}]}}
|
||||
ObjectSet_ShowEmptyImagePerspective|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": true}]}}
|
||||
ObjectSet_ShowInFront|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_ShowInstancerForRender|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": true}]}}
|
||||
ObjectSet_ShowInstancerForViewport|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": true}]}}
|
||||
ObjectSet_ShowName|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_ShowOnlyShapeKey|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_ShowTextureSpace|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_ShowTransparent|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_ShowWire|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_Tag|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_TrackAxis|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["STRING", {"default": "POS_Y"}]}}
|
||||
ObjectSet_Type|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["STRING", {"default": "MESH"}]}}
|
||||
ObjectSet_UpAxis|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["STRING", {"default": "Z"}]}}
|
||||
ObjectSet_UseCameraLockParent|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_UseDynamicTopologySculpting|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_UseEmptyImageAlpha|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_UseExtraUser|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_UseFakeUser|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_UseGreasePencilLights|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_UseInstanceFacesScale|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_UseInstanceVerticesRotation|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_UseMeshMirrorX|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_UseMeshMirrorY|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_UseMeshMirrorZ|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_UseShapeKeyEditMode|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectSet_UseSimulationCache|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": true}]}}
|
||||
ObjectSet_Users|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["INT", {"default": 1}]}}
|
||||
ObjectSet_VisibleCamera|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": true}]}}
|
||||
ObjectSet_VisibleDiffuse|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": true}]}}
|
||||
ObjectSet_VisibleGlossy|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": true}]}}
|
||||
ObjectSet_VisibleShadow|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": true}]}}
|
||||
ObjectSet_VisibleTransmission|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": true}]}}
|
||||
ObjectSet_VisibleVolumeScatter|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "value": ["BOOLEAN", {"default": true}]}}
|
||||
ObjectCall_AnimationDataClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
ObjectCall_AnimationDataCreate|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
ObjectCall_AssetClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
ObjectCall_AssetGeneratePreview|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
ObjectCall_AssetMark|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
ObjectCall_CacheRelease|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
ObjectCall_CalcMatrixCamera|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "depsgraph": ["depsgraph", {"default": null}], "x": ["INT", {"default": 1}], "y": ["INT", {"default": 1}], "scale_x": ["INT", {"default": 1}], "scale_y": ["INT", {"default": 1}]}}
|
||||
ObjectCall_CameraFitCoords|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "depsgraph": ["depsgraph", {"default": null}], " coordinates": [" coordinates", {"default": null}]}}
|
||||
ObjectCall_ClosestPointOnMesh|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "origin": ["origin", {"default": null}], "distance": ["FLOAT", {"default": 1.84467e+19}]}}
|
||||
ObjectCall_ConvertSpace|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], " 0": [" 0", {"default": null}]}}
|
||||
ObjectCall_Copy|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
ObjectCall_CrazyspaceDisplacementToDeformed|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "vertex_index": ["INT", {"default": 0}], " 0": [" 0", {"default": null}]}}
|
||||
ObjectCall_CrazyspaceDisplacementToOriginal|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "vertex_index": ["INT", {"default": 0}], " 0": [" 0", {"default": null}]}}
|
||||
ObjectCall_CrazyspaceEval|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "depsgraph": ["depsgraph", {"default": null}], " scene": [" scene", {"default": null}]}}
|
||||
ObjectCall_CrazyspaceEvalClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
ObjectCall_EvaluatedGet|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "depsgraph": ["depsgraph", {"default": null}]}}
|
||||
ObjectCall_FindArmature|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
ObjectCall_GenerateGpencilStrokes|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "grease_pencil_object": ["grease_pencil_object", {"default": null}], "use_collections": ["BOOLEAN", {"default": true}], "scale_thickness": ["INT", {"default": 1}], "sample": ["INT", {"default": 0}]}}
|
||||
ObjectCall_HideGet|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
ObjectCall_HideSet|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "state": ["state", {"default": null}]}}
|
||||
ObjectCall_HoldoutGet|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
ObjectCall_IndirectOnlyGet|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
ObjectCall_IsDeformModified|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "scene": ["scene", {"default": null}], " settings": [" settings", {"default": null}]}}
|
||||
ObjectCall_IsModified|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "scene": ["scene", {"default": null}], " settings": [" settings", {"default": null}]}}
|
||||
ObjectCall_LocalViewGet|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "viewport": ["viewport", {"default": null}]}}
|
||||
ObjectCall_LocalViewSet|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "viewport": ["viewport", {"default": null}], " state": [" state", {"default": null}]}}
|
||||
ObjectCall_MakeLocal|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "clear_proxy": ["BOOLEAN", {"default": true}]}}
|
||||
ObjectCall_OverrideCreate|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "remap_local_usages": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectCall_OverrideHierarchyCreate|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "scene": ["scene", {"default": null}], " view_layer": [" view_layer", {"default": null}], "do_fully_editable": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectCall_OverrideTemplateCreate|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
ObjectCall_PreviewEnsure|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
ObjectCall_RayCast|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "origin": ["origin", {"default": null}], " direction": [" direction", {"default": null}], "distance": ["FLOAT", {"default": 1.70141e+38}]}}
|
||||
ObjectCall_SelectGet|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
ObjectCall_SelectSet|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "state": ["state", {"default": null}]}}
|
||||
ObjectCall_ShapeKeyAdd|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "name": ["STRING", {"default": "Key"}], "from_mix": ["BOOLEAN", {"default": true}]}}
|
||||
ObjectCall_ShapeKeyClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
ObjectCall_ShapeKeyRemove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "key": ["key", {"default": null}]}}
|
||||
ObjectCall_ToCurve|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "depsgraph": ["depsgraph", {"default": null}], "apply_modifiers": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectCall_ToCurveClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
ObjectCall_ToMesh|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "preserve_all_data_layers": ["BOOLEAN", {"default": false}]}}
|
||||
ObjectCall_ToMeshClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
ObjectCall_UpdateFromEditmode|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
ObjectCall_UpdateTag|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
ObjectCall_UserClear|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
ObjectCall_UserOfId|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "id": ["id", {"default": null}]}}
|
||||
ObjectCall_UserRemap|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "new_id": ["new_id", {"default": null}]}}
|
||||
ObjectCall_VisibleGet|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
ObjectCall_VisibleInViewportGet|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "viewport": ["viewport", {"default": null}]}}
|
||||
UV_Align|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "axis": [["ALIGN_S", "ALIGN_T", "ALIGN_U", "ALIGN_AUTO", "ALIGN_X", "ALIGN_Y"]]}}
|
||||
UV_AlignRotation|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "method": [["AUTO", "EDGE", "GEOMETRY"]], "axis": [["X", "Y", "Z"]]}}
|
||||
UV_AverageIslandsScale|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "scale_uv": ["BOOLEAN", {"default": false}], "shear": ["BOOLEAN", {"default": false}]}}
|
||||
UV_Copy|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
UV_CubeProject|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "cube_size": ["FLOAT", {"min": 0.0, "max": 3.4028234663852886e+38, "default": 1.0}], "correct_aspect": ["BOOLEAN", {"default": true}], "clip_to_bounds": ["BOOLEAN", {"default": false}], "scale_to_bounds": ["BOOLEAN", {"default": false}]}}
|
||||
UV_CursorSet|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "location": ["B_VECTOR2", {}]}}
|
||||
UV_CylinderProject|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "direction": [["VIEW_ON_EQUATOR", "VIEW_ON_POLES", "ALIGN_TO_OBJECT"]], "align": [["POLAR_ZX", "POLAR_ZY"]], "pole": [["PINCH", "FAN"]], "seam": ["BOOLEAN", {"default": false}], "radius": ["FLOAT", {"min": 0.0, "max": 3.4028234663852886e+38, "default": 1.0}], "correct_aspect": ["BOOLEAN", {"default": true}], "clip_to_bounds": ["BOOLEAN", {"default": false}], "scale_to_bounds": ["BOOLEAN", {"default": false}]}}
|
||||
UV_ExportLayout|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "filepath": ["STRING", {"default": "", "multiline": false}], "export_all": ["BOOLEAN", {"default": false}], "modified": ["BOOLEAN", {"default": false}], "mode": [["SVG", "EPS", "PNG"]], "size": ["B_VECTOR2", {}], "opacity": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.25}], "check_existing": ["BOOLEAN", {"default": true}]}}
|
||||
UV_FollowActiveQuads|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mode": [["EVEN", "LENGTH", "LENGTH_AVERAGE"]]}}
|
||||
UV_Hide|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "unselected": ["BOOLEAN", {"default": false}]}}
|
||||
UV_LightmapPack|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "PREF_CONTEXT": [["SEL_FACES", "ALL_FACES"]], "PREF_PACK_IN_ONE": ["BOOLEAN", {"default": true}], "PREF_NEW_UVLAYER": ["BOOLEAN", {"default": false}], "PREF_BOX_DIV": ["INT", {"min": 1, "max": 48, "default": 12}], "PREF_MARGIN_DIV": ["FLOAT", {"min": 0.0010000000474974513, "max": 1.0, "default": 0.10000000149011612}]}}
|
||||
UV_MarkSeam|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "clear": ["BOOLEAN", {"default": false}]}}
|
||||
UV_MinimizeStretch|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "fill_holes": ["BOOLEAN", {"default": true}], "blend": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.0}], "iterations": ["INT", {"min": 0, "max": 2147483647, "default": 0}]}}
|
||||
UV_PackIslands|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "udim_source": [["CLOSEST_UDIM", "ACTIVE_UDIM", "ORIGINAL_AABB"]], "rotate": ["BOOLEAN", {"default": true}], "rotate_method": [["AXIS_ALIGNED", "CARDINAL", "ANY"]], "scale": ["BOOLEAN", {"default": true}], "merge_overlap": ["BOOLEAN", {"default": false}], "margin_method": [["SCALED", "ADD", "FRACTION"]], "margin": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.0010000000474974513}], "pin": ["BOOLEAN", {"default": false}], "pin_method": [["SCALE", "ROTATION", "ROTATION_SCALE", "LOCKED"]], "shape_method": [["CONCAVE", "CONVEX", "AABB"]]}}
|
||||
UV_Paste|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
UV_Pin|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "clear": ["BOOLEAN", {"default": false}]}}
|
||||
UV_ProjectFromView|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "orthographic": ["BOOLEAN", {"default": false}], "camera_bounds": ["BOOLEAN", {"default": true}], "correct_aspect": ["BOOLEAN", {"default": true}], "clip_to_bounds": ["BOOLEAN", {"default": false}], "scale_to_bounds": ["BOOLEAN", {"default": false}]}}
|
||||
UV_RandomizeUvTransform|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "random_seed": ["INT", {"min": 0, "max": 10000, "default": 0}], "use_loc": ["BOOLEAN", {"default": true}], "loc": ["B_VECTOR2", {}], "use_rot": ["BOOLEAN", {"default": true}], "rot": ["FLOAT", {"min": -6.2831854820251465, "max": 6.2831854820251465, "default": 0.0}], "use_scale": ["BOOLEAN", {"default": true}], "scale_even": ["BOOLEAN", {"default": false}], "scale": ["B_VECTOR2", {}]}}
|
||||
UV_RemoveDoubles|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "threshold": ["FLOAT", {"min": 0.0, "max": 10.0, "default": 0.019999999552965164}], "use_unselected": ["BOOLEAN", {"default": false}]}}
|
||||
UV_Reset|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
UV_Reveal|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "select": ["BOOLEAN", {"default": true}]}}
|
||||
UV_Rip|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mirror": ["BOOLEAN", {"default": false}], "release_confirm": ["BOOLEAN", {"default": false}], "use_accurate": ["BOOLEAN", {"default": false}], "location": ["B_VECTOR2", {}]}}
|
||||
UV_RipMove|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
UV_SeamsFromIslands|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mark_seams": ["BOOLEAN", {"default": true}], "mark_sharp": ["BOOLEAN", {"default": false}]}}
|
||||
UV_Select|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "extend": ["BOOLEAN", {"default": false}], "deselect": ["BOOLEAN", {"default": false}], "toggle": ["BOOLEAN", {"default": false}], "deselect_all": ["BOOLEAN", {"default": false}], "select_passthrough": ["BOOLEAN", {"default": false}], "location": ["B_VECTOR2", {}]}}
|
||||
UV_SelectAll|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "action": [["TOGGLE", "SELECT", "DESELECT", "INVERT"]]}}
|
||||
UV_SelectBox|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "pinned": ["BOOLEAN", {"default": false}], "xmin": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "xmax": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "ymin": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "ymax": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "wait_for_input": ["BOOLEAN", {"default": true}], "mode": [["SET", "ADD", "SUB"]]}}
|
||||
UV_SelectCircle|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "x": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "y": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "radius": ["INT", {"min": 1, "max": 2147483647, "default": 25}], "wait_for_input": ["BOOLEAN", {"default": true}], "mode": [["SET", "ADD", "SUB"]]}}
|
||||
UV_SelectEdgeRing|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "extend": ["BOOLEAN", {"default": false}], "location": ["B_VECTOR2", {}]}}
|
||||
UV_SelectLasso|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "mode": [["SET", "ADD", "SUB"]]}}
|
||||
UV_SelectLess|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
UV_SelectLinked|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
UV_SelectLinkedPick|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "extend": ["BOOLEAN", {"default": false}], "deselect": ["BOOLEAN", {"default": false}], "location": ["B_VECTOR2", {}]}}
|
||||
UV_SelectLoop|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "extend": ["BOOLEAN", {"default": false}], "location": ["B_VECTOR2", {}]}}
|
||||
UV_SelectMode|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["VERTEX", "EDGE", "FACE", "ISLAND"]]}}
|
||||
UV_SelectMore|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
UV_SelectOverlap|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "extend": ["BOOLEAN", {"default": false}]}}
|
||||
UV_SelectPinned|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
UV_SelectSimilar|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "type": [["PIN", "LENGTH", "LENGTH_3D", "AREA", "AREA_3D", "MATERIAL", "OBJECT", "SIDES", "WINDING", "FACE"]], "compare": [["EQUAL", "GREATER", "LESS"]], "threshold": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.0}]}}
|
||||
UV_SelectSplit|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
UV_ShortestPathPick|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_face_step": ["BOOLEAN", {"default": false}], "use_topology_distance": ["BOOLEAN", {"default": false}], "use_fill": ["BOOLEAN", {"default": false}], "skip": ["INT", {"min": 0, "max": 2147483647, "default": 0}], "nth": ["INT", {"min": 1, "max": 2147483647, "default": 1}], "offset": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}], "object_index": ["INT", {"min": -1, "max": 2147483647, "default": -1}], "index": ["INT", {"min": -1, "max": 2147483647, "default": -1}]}}
|
||||
UV_ShortestPathSelect|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_face_step": ["BOOLEAN", {"default": false}], "use_topology_distance": ["BOOLEAN", {"default": false}], "use_fill": ["BOOLEAN", {"default": false}], "skip": ["INT", {"min": 0, "max": 2147483647, "default": 0}], "nth": ["INT", {"min": 1, "max": 2147483647, "default": 1}], "offset": ["INT", {"min": -2147483648, "max": 2147483647, "default": 0}]}}
|
||||
UV_SmartProject|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "angle_limit": ["FLOAT", {"min": 0.0, "max": 1.5707963705062866, "default": 1.1519173383712769}], "margin_method": [["SCALED", "ADD", "FRACTION"]], "island_margin": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.0}], "area_weight": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.0}], "correct_aspect": ["BOOLEAN", {"default": true}], "scale_to_bounds": ["BOOLEAN", {"default": false}]}}
|
||||
UV_SnapCursor|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "target": [["PIXELS", "SELECTED", "ORIGIN"]]}}
|
||||
UV_SnapSelected|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "target": [["PIXELS", "CURSOR", "CURSOR_OFFSET", "ADJACENT_UNSELECTED"]]}}
|
||||
UV_SphereProject|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "direction": [["VIEW_ON_EQUATOR", "VIEW_ON_POLES", "ALIGN_TO_OBJECT"]], "align": [["POLAR_ZX", "POLAR_ZY"]], "pole": [["PINCH", "FAN"]], "seam": ["BOOLEAN", {"default": false}], "correct_aspect": ["BOOLEAN", {"default": true}], "clip_to_bounds": ["BOOLEAN", {"default": false}], "scale_to_bounds": ["BOOLEAN", {"default": false}]}}
|
||||
UV_Stitch|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "use_limit": ["BOOLEAN", {"default": false}], "snap_islands": ["BOOLEAN", {"default": true}], "limit": ["FLOAT", {"min": 0.0, "max": 3.4028234663852886e+38, "default": 0.009999999776482582}], "static_island": ["INT", {"min": 0, "max": 2147483647, "default": 0}], "active_object_index": ["INT", {"min": 0, "max": 2147483647, "default": 0}], "midpoint_snap": ["BOOLEAN", {"default": false}], "clear_seams": ["BOOLEAN", {"default": true}], "mode": [["VERTEX", "EDGE"]], "stored_mode": [["VERTEX", "EDGE"]], "objects_selection_count": ["B_VECTOR6", {}]}}
|
||||
UV_Unwrap|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"], "method": [["ANGLE_BASED", "CONFORMAL"]], "fill_holes": ["BOOLEAN", {"default": true}], "correct_aspect": ["BOOLEAN", {"default": true}], "use_subsurf_data": ["BOOLEAN", {"default": false}], "margin_method": [["SCALED", "ADD", "FRACTION"]], "margin": ["FLOAT", {"min": 0.0, "max": 1.0, "default": 0.0010000000474974513}]}}
|
||||
UV_Weld|{"required": {}, "optional": {"BPY_OBJ": ["BPY_OBJ"]}}
|
||||
@@ -1,121 +0,0 @@
|
||||
class KnifeProjection:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs_knife": ("BPY_OBJS",),
|
||||
"bpy_objs_target": ("BPY_OBJS",),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs_knife, bpy_objs_target):
|
||||
import mathutils
|
||||
import math
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
knife_object = bpy_objs_knife[0]
|
||||
target_object = bpy_objs_target[0]
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
bpy.context.view_layer.objects.active = target_object
|
||||
# knife_object.select_set(True)
|
||||
# target_object.select_set(True)
|
||||
|
||||
# bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
area = next(
|
||||
area for area in bpy.context.screen.areas if area.type == 'VIEW_3D')
|
||||
space = next(space for space in area.spaces if space.type == 'VIEW_3D')
|
||||
region = next(
|
||||
region for region in area.regions if region.type == 'WINDOW')
|
||||
|
||||
# space.region_3d.view_perspective = 'ORTHO'
|
||||
# space.region_3d.view_rotation = mathutils.Euler(
|
||||
# (0, 0, 0)).to_quaternion()
|
||||
|
||||
# set blender to orthographic view without using numpad
|
||||
|
||||
# bpy.ops.view3d.view_axis(type='TOP')
|
||||
|
||||
override = bpy.context.copy()
|
||||
|
||||
# override["active_object"] = target_object
|
||||
# override["edit_object"] = target_object
|
||||
# override["selected_objects"] = [knife_object, target_object]
|
||||
# override["selected_editable_objects"] = [knife_object, target_object]
|
||||
|
||||
override["region"] = region
|
||||
override["area"] = area
|
||||
override["space"] = space
|
||||
|
||||
override["active_object"] = target_object
|
||||
override["edit_object"] = target_object
|
||||
override["selected_objects"] = [knife_object, target_object]
|
||||
override["selected_editable_objects"] = [knife_object, target_object]
|
||||
|
||||
knife_object.select_set(False)
|
||||
target_object.select_set(True)
|
||||
# move the kinfe object up a bit
|
||||
bpy.ops.transform.translate(value=(0, 0, -0.1))
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
knife_object.select_set(False)
|
||||
|
||||
# Get the viewport rotation
|
||||
viewport_rotation = space.region_3d.view_rotation
|
||||
|
||||
with bpy.context.temp_override(**override):
|
||||
# # Create a rotation matrix representing a top-down view
|
||||
# rotation_matrix = mathutils.Matrix.Rotation(math.radians(90.0), 4, 'X')
|
||||
|
||||
# # Create a rotation matrix to make the object face the user
|
||||
# rotation_matrix_face_user = mathutils.Matrix.Rotation(math.radians(180.0), 4, 'Z')
|
||||
|
||||
# # Create a rotation matrix for the viewport rotation
|
||||
# rotation_matrix_viewport = viewport_rotation.to_matrix().to_4x4()
|
||||
|
||||
# # Remember the original locations
|
||||
# original_location_knife = knife_object.location.copy()
|
||||
# original_location_target = target_object.location.copy()
|
||||
|
||||
# # Move the objects to the origin
|
||||
# knife_object.location = mathutils.Vector((0, 0, 0))
|
||||
# target_object.location = mathutils.Vector((0, 0, 0))
|
||||
|
||||
# # Rotate the objects
|
||||
# knife_object.matrix_world = rotation_matrix @ rotation_matrix_face_user @ rotation_matrix_viewport @ knife_object.matrix_world
|
||||
# target_object.matrix_world = rotation_matrix @ rotation_matrix_face_user @ rotation_matrix_viewport @ target_object.matrix_world
|
||||
|
||||
bpy.ops.view3d.view_axis(type='TOP', align_active=False)
|
||||
bpy.ops.view3d.view_persportho()
|
||||
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
||||
bpy.ops.mesh.knife_project()
|
||||
|
||||
# # Rotate the objects back
|
||||
# knife_object.matrix_world = rotation_matrix.inverted() @ knife_object.matrix_world
|
||||
# target_object.matrix_world = rotation_matrix.inverted() @ target_object.matrix_world
|
||||
|
||||
# # Move the objects back to their original locations
|
||||
# knife_object.location = original_location_knife
|
||||
# target_object.location = original_location_target
|
||||
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
return ([target_object],)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"KnifeProjection": KnifeProjection
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"KnifeProjection": "Knife Projection"
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import folder_paths
|
||||
import os
|
||||
from PIL import Image, ImageOps
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
import numpy as np
|
||||
import torch
|
||||
import json
|
||||
|
||||
class LoadImageWithAlpha:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
input_dir = folder_paths.get_input_directory()
|
||||
files = [f for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f))]
|
||||
return {"required":
|
||||
{"image": (sorted(files), {"image_upload": True})},
|
||||
}
|
||||
|
||||
CATEGORY = "image"
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
FUNCTION = "load_image"
|
||||
def load_image(self, image):
|
||||
image_path = folder_paths.get_annotated_filepath(image)
|
||||
i = Image.open(image_path)
|
||||
i = ImageOps.exif_transpose(i)
|
||||
image = i.convert("RGBA")
|
||||
image = np.array(image).astype(np.float32) / 255.0
|
||||
image = torch.from_numpy(image)[None,]
|
||||
|
||||
print(image.shape)
|
||||
|
||||
return (image, )
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(s, image):
|
||||
image_path = folder_paths.get_annotated_filepath(image)
|
||||
m = hashlib.sha256()
|
||||
with open(image_path, 'rb') as f:
|
||||
m.update(f.read())
|
||||
return m.digest().hex()
|
||||
|
||||
@classmethod
|
||||
def VALIDATE_INPUTS(s, image):
|
||||
if not folder_paths.exists_annotated_filepath(image):
|
||||
return "Invalid image file: {}".format(image)
|
||||
|
||||
return True
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LoadImageWithAlpha": LoadImageWithAlpha,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LoadImageWithAlpha": "LoadImageWithAlpha"
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
class MeshFromTexture:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
# For disabling cache
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE", "BPY_OBJS")
|
||||
RETURN_NAMES = ("image", "bpy_objs")
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, image, seed):
|
||||
import torch
|
||||
import cv2
|
||||
import numpy as np
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
image = np.copy(image[0].numpy())
|
||||
|
||||
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
|
||||
gray = (gray * 255).astype(np.uint8)
|
||||
|
||||
# Find contours
|
||||
contours, _ = cv2.findContours(
|
||||
gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||
|
||||
# Get the largest contour
|
||||
areas = [cv2.contourArea(contour) for contour in contours]
|
||||
|
||||
max_area_index = areas.index(max(areas))
|
||||
largest_contour = contours[max_area_index]
|
||||
contours = [largest_contour]
|
||||
|
||||
def normalize_vertices(vertices, max_value):
|
||||
return vertices / float(max_value) * 2 - 1
|
||||
|
||||
# Get the image width and height
|
||||
height, width = image.shape[:2]
|
||||
|
||||
# Normalize the vertices
|
||||
normalized_contours = []
|
||||
for contour in contours:
|
||||
normalized_contour = []
|
||||
for vertex in contour:
|
||||
normalized_vertex = [normalize_vertices(
|
||||
vertex[0][0], width), normalize_vertices(vertex[0][1], height) * -1]
|
||||
normalized_contour.append(normalized_vertex)
|
||||
normalized_contours.append(
|
||||
np.array(normalized_contour, dtype=np.float32))
|
||||
|
||||
meshes = []
|
||||
# print(len(normalized_contours))
|
||||
for i, contour in enumerate(normalized_contours):
|
||||
# Create a new mesh for each contour
|
||||
mesh = bpy.data.meshes.new(name=f"NewMesh{i}")
|
||||
# Create a new object for each mesh
|
||||
obj = bpy.data.objects.new(f"NewObject{i}", mesh)
|
||||
# Link the object to the current collection
|
||||
bpy.context.collection.objects.link(obj)
|
||||
|
||||
# Add a z coordinate to each vertex
|
||||
ordered_vertices = [(*vertex, 0) for vertex in contour]
|
||||
# Create a face from the vertices
|
||||
face = list(range(len(ordered_vertices)))
|
||||
|
||||
# Create the mesh from the vertices and face
|
||||
mesh.from_pydata(ordered_vertices, [], [face])
|
||||
|
||||
# Create a default shape key for the mesh
|
||||
sk_basis = obj.shape_key_add(name='Basis')
|
||||
|
||||
meshes.append(obj) # Add the object to the list of meshes
|
||||
|
||||
# Draw contours on the original image
|
||||
if not image.flags['C_CONTIGUOUS']:
|
||||
image = np.ascontiguousarray(image)
|
||||
cv2.drawContours(image, contours, -1, (0, 255, 0), 3)
|
||||
|
||||
# Convert image back to tensor
|
||||
image = [torch.from_numpy(image)]
|
||||
return (image, meshes)
|
||||
|
||||
|
||||
# A dictionary that contains all nodes you want to export with their names
|
||||
# NOTE: names should be globally unique
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"MeshFromTexture": MeshFromTexture
|
||||
}
|
||||
|
||||
# A dictionary that contains the friendly/humanly readable titles for the nodes
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"MeshFromTexture": "Mesh from texture"
|
||||
}
|
||||
+68
-8
@@ -1,6 +1,9 @@
|
||||
import atexit
|
||||
import subprocess
|
||||
import os
|
||||
import folder_paths
|
||||
import requests
|
||||
import json
|
||||
|
||||
def genreate_mesh_from_texture(bpy, image):
|
||||
import torch
|
||||
@@ -14,6 +17,9 @@ def genreate_mesh_from_texture(bpy, image):
|
||||
contours, _ = cv2.findContours(
|
||||
gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||
|
||||
if len(contours) == 0:
|
||||
raise Exception("No contours found. Please ensure that the image has the correct segments (e.g. when you click on the mouth, it should display a proper blue area over the mouth region).")
|
||||
|
||||
# Get the largest contour
|
||||
areas = [cv2.contourArea(contour) for contour in contours]
|
||||
|
||||
@@ -67,7 +73,7 @@ def genreate_mesh_from_texture(bpy, image):
|
||||
cv2.drawContours(image, contours, -1, (0, 255, 0), 3)
|
||||
|
||||
# Convert image back to tensor
|
||||
image = [torch.from_numpy(image)]
|
||||
image = torch.from_numpy(np.expand_dims(image, axis=0))
|
||||
return (image, meshes[0])
|
||||
|
||||
|
||||
@@ -86,7 +92,7 @@ def assign_texture(bpy, BPY_OBJ, texture, texture_name):
|
||||
|
||||
# Create an image with the required dimensions
|
||||
img = bpy.data.images.new(
|
||||
texture_name, width=texture.shape[1], height=texture.shape[0])
|
||||
texture_name, width=texture.shape[1], height=texture.shape[0], alpha = True)
|
||||
|
||||
# If there is no alpha channel, append one full of 1's
|
||||
if texture.shape[2] == 3:
|
||||
@@ -118,6 +124,7 @@ def assign_texture(bpy, BPY_OBJ, texture, texture_name):
|
||||
# Create a material
|
||||
mat = bpy.data.materials.new("MaterialName")
|
||||
mat.use_nodes = True
|
||||
mat.blend_method = 'BLEND'
|
||||
nodes = mat.node_tree.nodes
|
||||
for node in nodes:
|
||||
nodes.remove(node)
|
||||
@@ -138,6 +145,8 @@ def assign_texture(bpy, BPY_OBJ, texture, texture_name):
|
||||
texture_node.outputs['Color'])
|
||||
links.new(output_node.inputs['Surface'], bsdf_node.outputs['BSDF'])
|
||||
|
||||
links.new(bsdf_node.inputs['Alpha'], texture_node.outputs['Alpha'])
|
||||
|
||||
# Assign the material to the active object
|
||||
if obj.data.materials:
|
||||
obj.data.materials[0] = mat
|
||||
@@ -196,7 +205,7 @@ def kill_blender_process():
|
||||
process.kill()
|
||||
|
||||
|
||||
def export_gltf(output_dir, bpy_objects, filename, model_type, write_mode):
|
||||
def export_gltf(output_dir, bpy_objects, filename, model_type, write_mode, metadata):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
# print(bpy, bpy_objects)
|
||||
@@ -206,21 +215,72 @@ def export_gltf(output_dir, bpy_objects, filename, model_type, write_mode):
|
||||
override["selected_objects"] = list(bpy_objects)
|
||||
override["active_object"] = list(bpy_objects)[0]
|
||||
|
||||
bpy_objects[0]["metadata"] = metadata
|
||||
for obj in bpy_objects:
|
||||
obj.select_set(True)
|
||||
|
||||
filepath = output_dir + "/" + filename + '.' + ("glb" if model_type == "GLB" else "gltf")
|
||||
def get_file_extension(model_type):
|
||||
if model_type == "GLB":
|
||||
return ".glb"
|
||||
elif model_type == "GLTF_EMBEDDED":
|
||||
return ".gltf"
|
||||
elif model_type == "GLTF_SEPARATE":
|
||||
return ".gltf"
|
||||
elif model_type == "AVA":
|
||||
return ".ava"
|
||||
|
||||
ext = get_file_extension(model_type)
|
||||
filepath = output_dir + "/" + filename + ext + (".glb" if model_type == "AVA" else "")
|
||||
|
||||
if write_mode == "Increment":
|
||||
count = 0
|
||||
# while file exists, increment count
|
||||
while os.path.exists(output_dir + "/" + filename + '_' + str(count) + '.' + ("glb" if model_type == "GLB" else "gltf")):
|
||||
while os.path.exists(output_dir + "/" + filename + '_' + str(count) + ext):
|
||||
count += 1
|
||||
|
||||
filepath = output_dir + "/" + filename + '_' + str(count) + '.' + ("glb" if model_type == "GLB" else "gltf")
|
||||
filepath = output_dir + "/" + filename + '_' + str(count) + ext + (".glb" if model_type == "AVA" else "")
|
||||
|
||||
with bpy.context.temp_override(**override):
|
||||
bpy.ops.export_scene.gltf(filepath=filepath, export_format=model_type, use_selection=True)
|
||||
bpy.ops.export_scene.gltf(filepath=filepath, export_format="GLB" if model_type == "AVA" else model_type, use_selection=True, export_extras=True)
|
||||
# print(filepath)
|
||||
if filepath.endswith('.ava.glb'):
|
||||
new_filepath = filepath.replace('.ava.glb', '.ava')
|
||||
os.replace(filepath, new_filepath)
|
||||
filepath = new_filepath
|
||||
|
||||
return filepath
|
||||
return filepath
|
||||
|
||||
def get_avatar_file(output):
|
||||
avatar_filename = output["gltfFilename"][0]
|
||||
with open(
|
||||
f"{folder_paths.get_output_directory()}/{avatar_filename}", "rb"
|
||||
) as f:
|
||||
return f.read()
|
||||
|
||||
def upload_avatar_file(output):
|
||||
file = get_avatar_file(output)
|
||||
response = requests.get("https://labs.avatech.ai/api/share")
|
||||
labData = response.json()
|
||||
modelId = labData["modelId"]
|
||||
|
||||
# upload model
|
||||
headers = {
|
||||
"x-amz-acl": "public-read",
|
||||
"Content-Type": "model/gltf-binary",
|
||||
"Content-Length": str(len(file)),
|
||||
}
|
||||
requests.put(labData["url"], headers=headers, data=file)
|
||||
|
||||
# send notification
|
||||
webhook_url = os.getenv("DISCORD_WEBHOOK_URL")
|
||||
data = {
|
||||
"username": "Avabot",
|
||||
"avatar_url": "https://avatech-avatar-dev1.nyc3.cdn.digitaloceanspaces.com/avatechai.png",
|
||||
"content": "[API Call] New register!",
|
||||
}
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
response = requests.post(webhook_url, headers=headers, data=json.dumps(data))
|
||||
|
||||
return modelId
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
import subprocess
|
||||
import folder_paths
|
||||
import os
|
||||
from mesh_utils import open_in_blender
|
||||
|
||||
|
||||
class OpenInBlender:
|
||||
def __init__(self):
|
||||
self.my_blender_process = None
|
||||
self.output_dir = folder_paths.get_output_directory()
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs": ("BPY_OBJS",),
|
||||
"blender_path": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": "blender"
|
||||
}),
|
||||
"shading": (["Material", "Solid", "Rendered", "Wireframe"],),
|
||||
"camera_location": ("VECTOR3D",),
|
||||
"camera_rotation": ("VECTOR3D",),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ()
|
||||
FUNCTION = "process"
|
||||
|
||||
OUTPUT_NODE = True
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs, blender_path, shading, camera_location, camera_rotation):
|
||||
output_file = self.output_dir + '/tmp.blend'
|
||||
p = open_in_blender(self.my_blender_process, blender_path=blender_path, output_file=output_file, camera_location=camera_location,
|
||||
camera_rotation=camera_rotation, shading=shading)
|
||||
self.my_blender_process = p
|
||||
return {"ui": {}}
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"OpenInBlender": OpenInBlender
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"OpenInBlender": "Open in Blender"
|
||||
}
|
||||
@@ -9,9 +9,12 @@ class Object_CreateMeshLayer(blender_node.ObjectOps):
|
||||
|
||||
EXTRA_INPUT_TYPES = {
|
||||
"image": ("IMAGE",),
|
||||
"face_threshold": ("FLOAT", {"display": "number", "default": 0.7}),
|
||||
"convex_hull": ("BOOLEAN", {"default": True}),
|
||||
# "face_threshold": ("FLOAT", {"display": "number", "default": 0.7}),
|
||||
"shape_threshold": ("FLOAT", {"display": "number", "default": 0.7}),
|
||||
"mesh_layer_name": ("STRING", {"default": "mesh_layer"}),
|
||||
"scale_x": ("FLOAT", {"display": "number", "default": 1}),
|
||||
"scale_y": ("FLOAT", {"display": "number", "default": 1}),
|
||||
"extrude_x": ("FLOAT", {"display": "number", "default": 0}),
|
||||
"extrude_y": ("FLOAT", {"display": "number", "default": 0}),
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
@@ -19,22 +22,27 @@ class Object_CreateMeshLayer(blender_node.ObjectOps):
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJ", "IMAGE")
|
||||
|
||||
def blender_process(self, bpy, image, face_threshold, shape_threshold, mesh_layer_name, extrude_x, extrude_y, seed):
|
||||
def blender_process(self, bpy, image, convex_hull, shape_threshold, mesh_layer_name, scale_x,scale_y , extrude_x, extrude_y, seed):
|
||||
image, BPY_OBJ = genreate_mesh_from_texture(bpy, image)
|
||||
|
||||
bpy.context.view_layer.objects.active = BPY_OBJ
|
||||
|
||||
self.edit_mode(bpy)
|
||||
bpy.ops.mesh.convex_hull(
|
||||
delete_unused=True, use_existing_faces=True,
|
||||
face_threshold=face_threshold,
|
||||
shape_threshold=shape_threshold,
|
||||
)
|
||||
|
||||
if convex_hull:
|
||||
bpy.ops.mesh.convex_hull(
|
||||
delete_unused=True, use_existing_faces=True,
|
||||
shape_threshold=shape_threshold,
|
||||
# face_threshold=face_threshold
|
||||
face_threshold=0.7
|
||||
)
|
||||
|
||||
bpy.ops.mesh.delete(type='EDGE_FACE')
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
bpy.ops.mesh.edge_face_add()
|
||||
|
||||
bpy.ops.transform.resize(value=(scale_x, scale_y, 1))
|
||||
|
||||
bpy.context.object.vertex_groups.new(name=mesh_layer_name)
|
||||
bpy.ops.object.vertex_group_assign()
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import blender_node
|
||||
from mesh_utils import genreate_mesh_from_texture
|
||||
|
||||
class Object_CreateMeshLayer_Advanced(blender_node.ObjectOps):
|
||||
|
||||
BASE_INPUT_TYPES = {}
|
||||
|
||||
CUSTOM_NAME = "Create Mesh Layer (Advanced)"
|
||||
|
||||
EXTRA_INPUT_TYPES = {
|
||||
"image": ("IMAGE",),
|
||||
"convex_hull": ("BOOLEAN", {"default": True}),
|
||||
# "face_threshold": ("FLOAT", {"display": "number", "default": 0.7}),
|
||||
"shape_threshold": ("FLOAT", {"display": "number", "default": 0.7}),
|
||||
"mesh_layer_name": ("STRING", {"default": "mesh_layer"}),
|
||||
"scale_x": ("FLOAT", {"display": "number", "default": 1}),
|
||||
"scale_y": ("FLOAT", {"display": "number", "default": 1}),
|
||||
"extrude_x": ("FLOAT", {"display": "number", "default": 0}),
|
||||
"extrude_y": ("FLOAT", {"display": "number", "default": 0}),
|
||||
"inner_translate_x": ("FLOAT", {"display": "number", "default": 0, "step": 0.01}),
|
||||
"inner_translate_y": ("FLOAT", {"display": "number", "default": 0, "step": 0.01}),
|
||||
"outer_translate_x": ("FLOAT", {"display": "number", "default": 0, "step": 0.01}),
|
||||
"outer_translate_y": ("FLOAT", {"display": "number", "default": 0, "step": 0.01}),
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJ", "IMAGE")
|
||||
|
||||
def blender_process(self, bpy, image, convex_hull, shape_threshold, mesh_layer_name, scale_x,scale_y , extrude_x, extrude_y, inner_translate_x, inner_translate_y, outer_translate_x, outer_translate_y, seed):
|
||||
image, BPY_OBJ = genreate_mesh_from_texture(bpy, image)
|
||||
|
||||
bpy.context.view_layer.objects.active = BPY_OBJ
|
||||
|
||||
self.edit_mode(bpy)
|
||||
|
||||
if convex_hull:
|
||||
bpy.ops.mesh.convex_hull(
|
||||
delete_unused=True, use_existing_faces=True,
|
||||
shape_threshold=shape_threshold,
|
||||
# face_threshold=face_threshold
|
||||
face_threshold=0.7
|
||||
)
|
||||
|
||||
bpy.ops.mesh.delete(type='EDGE_FACE')
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
bpy.ops.mesh.edge_face_add()
|
||||
|
||||
bpy.ops.transform.resize(value=(scale_x, scale_y, 1))
|
||||
bpy.ops.transform.translate(value=(inner_translate_x, inner_translate_y, 0))
|
||||
|
||||
bpy.context.object.vertex_groups.new(name=mesh_layer_name)
|
||||
bpy.ops.object.vertex_group_assign()
|
||||
|
||||
if extrude_x != 0 or extrude_y != 0:
|
||||
bpy.ops.mesh.extrude_region_move()
|
||||
bpy.ops.object.vertex_group_remove_from()
|
||||
bpy.ops.transform.resize(value=(extrude_x, extrude_y, 0))
|
||||
bpy.ops.transform.translate(value=(outer_translate_x, outer_translate_y, 0))
|
||||
bpy.ops.mesh.delete(type='ONLY_FACE')
|
||||
|
||||
self.object_mode(bpy)
|
||||
return (BPY_OBJ, image)
|
||||
@@ -8,5 +8,6 @@ class GroupOps(blender_node.ObjectOps):
|
||||
RETURN_TYPES = (blender_node.BPY_OBJS,)
|
||||
|
||||
def blender_process(self, bpy, BPY_OBJ, BPY_OBJ2, **props):
|
||||
return ([BPY_OBJ, BPY_OBJ2],)
|
||||
prop_values = props.values()
|
||||
return ([BPY_OBJ, BPY_OBJ2, *prop_values],)
|
||||
|
||||
|
||||
+7
-7
@@ -9,13 +9,13 @@ class Mesh_JoinMesh(blender_node.ObjectOps):
|
||||
CUSTOM_NAME = "Join Meshes"
|
||||
|
||||
def blender_process(self, bpy, BPY_OBJ, **props):
|
||||
|
||||
prop_values = props.values()
|
||||
|
||||
override = bpy.context.copy()
|
||||
override["active_object"] = BPY_OBJ
|
||||
override["selected_editable_objects"] = list(prop_values) + [BPY_OBJ]
|
||||
bpy.ops.object.join(override)
|
||||
prop_values = props.values()
|
||||
for obj in list(prop_values) + [BPY_OBJ]:
|
||||
if obj is not None:
|
||||
obj.select_set(True)
|
||||
if bpy.context.view_layer.objects is not None:
|
||||
bpy.context.view_layer.objects.active = BPY_OBJ
|
||||
bpy.ops.object.join()
|
||||
|
||||
return (BPY_OBJ,)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ class Object_MatchTextureAspectRatio(blender_node.EditOps):
|
||||
|
||||
EXTRA_INPUT_TYPES = {
|
||||
"image": ("IMAGE",),
|
||||
"scale": ('FLOAT', {'default': 0.001, })
|
||||
"scale": ('FLOAT', {'default': 0.001, "display": "number" , "step": 0.001})
|
||||
}
|
||||
|
||||
CUSTOM_NAME = "Match Texture Aspect Ratio"
|
||||
|
||||
@@ -51,8 +51,8 @@ class Mesh_ModifyShapeKey(blender_node.ObjectOps):
|
||||
# Create a new array of verts with sk as co and verts' groups
|
||||
new_verts = []
|
||||
for i, vert in enumerate(verts):
|
||||
print(i)
|
||||
print(sk.data[i].co)
|
||||
# print(i)
|
||||
# print(sk.data[i].co)
|
||||
new_vert = {
|
||||
'co': sk.data[i].co,
|
||||
'groups': vert.groups
|
||||
@@ -129,6 +129,6 @@ class Mesh_ModifyShapeKey(blender_node.ObjectOps):
|
||||
new_pos = center + vec
|
||||
|
||||
# Apply new position
|
||||
print(i, vert['co'], new_pos, weight)
|
||||
# print(i, vert['co'], new_pos, weight)
|
||||
|
||||
sk.data[i].co = new_pos
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import blender_node
|
||||
from mesh_utils import genreate_mesh_from_texture, assign_texture
|
||||
|
||||
class Object_UV_Modifier(blender_node.EditOps):
|
||||
|
||||
EXTRA_INPUT_TYPES = {
|
||||
"scale": ('FLOAT', {'default': 1, "display": "number", "step": 0.01}),
|
||||
"texture_name": ('STRING', {'default': 'Texture', })
|
||||
}
|
||||
|
||||
CUSTOM_NAME = "UV Modifier"
|
||||
|
||||
def blender_process(self, bpy, BPY_OBJ, scale, texture_name):
|
||||
import bmesh
|
||||
|
||||
bm = bmesh.from_edit_mesh(BPY_OBJ.data)
|
||||
|
||||
uv_layer = bm.loops.layers.uv.verify()
|
||||
for f in bm.faces:
|
||||
# move all of the UVs in this face up one UDIM tile
|
||||
for l in f.loops:
|
||||
l[uv_layer].uv = (l[uv_layer].uv[0], 0.998 if l[uv_layer].uv[1] == 1 else l[uv_layer].uv[1])
|
||||
|
||||
bmesh.update_edit_mesh(BPY_OBJ.data)
|
||||
@@ -5,7 +5,7 @@ class Object_PlaneTextureUnwrap(blender_node.EditOps):
|
||||
|
||||
EXTRA_INPUT_TYPES = {
|
||||
"image": ("IMAGE",),
|
||||
"scale": ('FLOAT', {'default': 1, }),
|
||||
"scale": ('FLOAT', {'default': 1, "display": "number", "step": 0.01}),
|
||||
"texture_name": ('STRING', {'default': 'Texture', })
|
||||
}
|
||||
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
class Plane:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"division": ("INT", {
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": 256,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), # For disabling cache
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, division, seed):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
# Define coordinates for the plane to object mode
|
||||
if bpy.context.active_object is not None:
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
coords = [(-1, -1, 0), (1, -1, 0), (-1, 1, 0), (1, 1, 0)]
|
||||
|
||||
# Create a mesh object
|
||||
mesh = bpy.data.meshes.new(name="PlaneMesh")
|
||||
mesh.from_pydata(coords,[],[(0,1,3,2)])
|
||||
|
||||
# Create a new object with the mesh
|
||||
object_a = bpy.data.objects.new(name="PlaneObject", object_data=mesh)
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
bpy.context.collection.objects.link(object_a)
|
||||
bpy.context.view_layer.objects.active = object_a
|
||||
object_a.select_set(True)
|
||||
|
||||
# Invert the Y axis of the UV to fix the vertical inversion
|
||||
bpy.ops.transform.resize(value=(-1, -1, 1))
|
||||
|
||||
# Create a new UV map
|
||||
uv_map = mesh.uv_layers.new(name="newUV")
|
||||
mesh.uv_layers.active = uv_map
|
||||
|
||||
# Unwrap the mesh
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.uv.unwrap()
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
# Subdivide the plane
|
||||
if division > 0:
|
||||
original_mode = bpy.context.object.mode
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.subdivide(number_cuts=division)
|
||||
bpy.ops.object.mode_set(mode=original_mode)
|
||||
|
||||
sk_basis = object_a.shape_key_add(name='Basis')
|
||||
|
||||
return ([object_a],)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"Plane": Plane
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"Plane": "Plane (Old)"
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import folder_paths
|
||||
import os
|
||||
from PIL import Image, ImageOps
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
import numpy as np
|
||||
import torch
|
||||
import json
|
||||
|
||||
class SaveImageWithWorkflow:
|
||||
def __init__(self):
|
||||
self.output_dir = folder_paths.get_output_directory()
|
||||
self.type = "output"
|
||||
self.prefix_append = ""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
input_dir = folder_paths.get_input_directory()
|
||||
files = [f for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f))]
|
||||
return {"required":
|
||||
{"image": (sorted(files), {"image_upload": True}),
|
||||
"filename_prefix": ("STRING", {"default": "ComfyUI"})},
|
||||
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ()
|
||||
FUNCTION = "save_images"
|
||||
|
||||
OUTPUT_NODE = True
|
||||
|
||||
CATEGORY = "image"
|
||||
|
||||
def save_images(self, image, filename_prefix="ComfyUI", prompt=None, extra_pnginfo=None):
|
||||
image_path = folder_paths.get_annotated_filepath(image)
|
||||
i = Image.open(image_path)
|
||||
i = ImageOps.exif_transpose(i)
|
||||
image = i.convert("RGBA")
|
||||
image = np.array(image).astype(np.float32) / 255.0
|
||||
image = torch.from_numpy(image)[None,]
|
||||
images=(image)
|
||||
|
||||
filename_prefix += self.prefix_append
|
||||
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, self.output_dir, images[0].shape[1], images[0].shape[0])
|
||||
results = list()
|
||||
for image in images:
|
||||
i = 255. * image.cpu().numpy()
|
||||
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
|
||||
metadata = None
|
||||
metadata = PngInfo()
|
||||
if prompt is not None:
|
||||
# prompt = json.loads(prompt)
|
||||
prompt = {k: v for k, v in prompt.items() if v['class_type'] != 'Save Image With Workflow'}
|
||||
metadata.add_text("prompt", json.dumps(prompt))
|
||||
|
||||
print(extra_pnginfo)
|
||||
|
||||
# if prompt is not None:
|
||||
# metadata.add_text("prompt", json.dumps(prompt))
|
||||
if extra_pnginfo is not None:
|
||||
for x in extra_pnginfo:
|
||||
if (x == 'workflow'):
|
||||
extra_pnginfo[x]["nodes"] = [node for node in extra_pnginfo[x]["nodes"] if node['type'] != 'Save Image With Workflow']
|
||||
|
||||
metadata.add_text(x, json.dumps(extra_pnginfo[x]))
|
||||
|
||||
file = f"{filename}_{counter:05}_.png"
|
||||
img.save(os.path.join(full_output_folder, file), pnginfo=metadata, compress_level=4)
|
||||
results.append({
|
||||
"filename": file,
|
||||
"subfolder": subfolder,
|
||||
"type": self.type
|
||||
})
|
||||
counter += 1
|
||||
|
||||
return { "ui": { "images": results } }
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"Save Image With Workflow": SaveImageWithWorkflow,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"Save Image With Workflow": "Save Image With Workflow"
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
class ScaleVertex():
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs_target": ("BPY_OBJS",),
|
||||
"extrude": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01, "display": "number"}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs_target, extrude):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
if len(bpy_objs_target) == 0:
|
||||
# throw error
|
||||
return
|
||||
|
||||
target_object = bpy_objs_target[0]
|
||||
|
||||
# deselect all objects
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
# select only the target object
|
||||
bpy.context.view_layer.objects.active = target_object
|
||||
# enter enter edit mode and select all faces of the object to fill
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
|
||||
bpy.ops.transform.resize(
|
||||
value=(extrude, extrude, 1), constraint_axis=(False, False, False))
|
||||
|
||||
# remove the extruded vertex from the vertex group "name"
|
||||
# bpy.ops.object.vertex_group_remove_from()
|
||||
|
||||
# bpy.ops.mesh.delete(type='FACE')
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
return ([target_object],)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"ScaleVertex": ScaleVertex
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"ScaleVertex": "Scale Vertex"
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
class Texture:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs": ("BPY_OBJS",),
|
||||
"texture": ("IMAGE",),
|
||||
"texture_name": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": "my_image"
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
# OUTPUT_NODE = False
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs, texture, texture_name):
|
||||
import numpy as np
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
# Convert image to numpy
|
||||
texture = texture[0].numpy()
|
||||
|
||||
# Flip the image vertically
|
||||
texture = np.flipud(texture)
|
||||
|
||||
# Create an image with the required dimensions
|
||||
img = bpy.data.images.new(texture_name, width=texture.shape[1], height=texture.shape[0])
|
||||
|
||||
# If there is no alpha channel, append one full of 1's
|
||||
if texture.shape[2] == 3:
|
||||
alpha_channel = np.ones((*texture.shape[:2], 1))
|
||||
texture = np.concatenate((texture, alpha_channel), axis=2)
|
||||
|
||||
# Flatten image data and rearrange color channels for blender
|
||||
img.pixels = texture.ravel()
|
||||
|
||||
# Pack image to store it within .blend file
|
||||
img.pack()
|
||||
|
||||
# Save image to a file
|
||||
# img.filepath_raw = 'test.png'
|
||||
# img.file_format = 'PNG'
|
||||
# img.save()
|
||||
|
||||
# Get the active object
|
||||
obj = bpy_objs[0]
|
||||
|
||||
# Create a material
|
||||
mat = bpy.data.materials.new("MaterialName")
|
||||
mat.use_nodes = True
|
||||
nodes = mat.node_tree.nodes
|
||||
for node in nodes:
|
||||
nodes.remove(node)
|
||||
|
||||
# Add a new texture node
|
||||
texture_node = nodes.new(type='ShaderNodeTexImage')
|
||||
texture_node.image = img
|
||||
|
||||
# Add a new BSDF node
|
||||
bsdf_node = nodes.new(type='ShaderNodeBsdfPrincipled')
|
||||
|
||||
# Add a new output node
|
||||
output_node = nodes.new(type='ShaderNodeOutputMaterial')
|
||||
|
||||
# Link nodes together
|
||||
links = mat.node_tree.links
|
||||
links.new(bsdf_node.inputs['Base Color'], texture_node.outputs['Color'])
|
||||
links.new(output_node.inputs['Surface'], bsdf_node.outputs['BSDF'])
|
||||
|
||||
# Assign the material to the active object
|
||||
if obj.data.materials:
|
||||
obj.data.materials[0] = mat
|
||||
else:
|
||||
obj.data.materials.append(mat)
|
||||
return (bpy_objs,)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"Texture": Texture
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"Texture": "Texture"
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
class TransfromObject():
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs_target": ("BPY_OBJS",),
|
||||
"x": ("FLOAT", {"default": 0.0, "min": -10.0, "max": 10.0, "step": 0.01, "display": "number"}),
|
||||
"y": ("FLOAT", {"default": 0.0, "min": -10.0, "max": 10.0, "step": 0.01, "display": "number"}),
|
||||
"z": ("FLOAT", {"default": 0.0, "min": -10.0, "max": 10.0, "step": 0.01, "display": "number"}),
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs_target, x, y, z):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
target_object = bpy_objs_target[0]
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
bpy.context.view_layer.objects.active = target_object
|
||||
|
||||
# move the object in level transformation
|
||||
target_object.location.x += x
|
||||
target_object.location.y += y
|
||||
target_object.location.z += z
|
||||
|
||||
return ([target_object],)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"TransfromObject": TransfromObject
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"TransfromObject": "Transfrom Object"
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
class UV_Unwrap():
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs_target": ("BPY_OBJS",),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs_target):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
target_object = bpy_objs_target[0]
|
||||
|
||||
bpy.context.view_layer.objects.active = target_object
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
# deselect all objects
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
# select only the target object
|
||||
bpy.context.view_layer.objects.active = target_object
|
||||
# enter enter edit mode and select all faces of the object to fill
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
|
||||
# perform a cube projection unwrap
|
||||
bpy.ops.uv.cube_project(cube_size=1.0, correct_aspect=True, clip_to_bounds=False, scale_to_bounds=True)
|
||||
|
||||
# bpy.ops.mesh.delete(type='FACE')
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
|
||||
return ([target_object],)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"UV_Unwrap": UV_Unwrap
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"UV_Unwrap": "UV Unwrap"
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import torch
|
||||
from einops import rearrange
|
||||
from nodes import PreviewImage
|
||||
|
||||
class PointVisualizer(PreviewImage):
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"images": ("IMAGE",),
|
||||
"point": ("VECTOR3D",),
|
||||
"point_size": ("INT", {
|
||||
"default": 1,
|
||||
"min": 10,
|
||||
"max": 20,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "image"
|
||||
|
||||
def process(self, images, point, point_size):
|
||||
x, y, z = point
|
||||
h, w, c = images[0].shape
|
||||
point_image = torch.zeros(images[0].shape)
|
||||
|
||||
center_x, center_y, _ = point
|
||||
top_left_x = center_x - point_size // 2
|
||||
top_left_y = center_y - point_size // 2
|
||||
|
||||
# Make sure the square is within the image boundaries
|
||||
top_left_x = max(0, min(w - point_size, top_left_x))
|
||||
top_left_y = max(0, min(h - point_size, top_left_y))
|
||||
|
||||
point_image[top_left_y:top_left_y+point_size, top_left_x:top_left_x+point_size] = 1
|
||||
point_image = rearrange(point_image, 'h w c -> 1 h w c')
|
||||
|
||||
# Blending
|
||||
images = images * 0.2 + point_image * 0.8
|
||||
return self.save_images(images)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"PointVisualizer": PointVisualizer
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"PointVisualizer": "Point Visualizer"
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
class VECTOR3D:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"x": ("INT", {
|
||||
"default": 0,
|
||||
"min": -1024,
|
||||
"max": 1024,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"y": ("INT", {
|
||||
"default": 0,
|
||||
"min": -1024,
|
||||
"max": 1024,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"z": ("INT", {
|
||||
"default": 0,
|
||||
"min": -1024,
|
||||
"max": 1024,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("VECTOR3D",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "utils"
|
||||
|
||||
def run(self, x, y, z):
|
||||
return ([x, y, z],)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"VECTOR3D": VECTOR3D
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"VECTOR3D": "Vector 3D"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
os.environ["TYPE_GENERATION"] = "1"
|
||||
|
||||
current_dir = os.path.dirname(__file__)
|
||||
blender_dir = os.path.join(current_dir, "blender")
|
||||
sys.path.append(blender_dir)
|
||||
|
||||
import json
|
||||
from blender.ops_mesh import BLENDER_NODES
|
||||
|
||||
|
||||
with open(f"{blender_dir}/input_types.txt", "w") as f:
|
||||
for node in BLENDER_NODES:
|
||||
results = node.INPUT_TYPES()
|
||||
f.write(node.__name__ + "|" + json.dumps(results) + "\n")
|
||||
+35
-23
@@ -1,37 +1,49 @@
|
||||
import bpy
|
||||
global_bpy = bpy
|
||||
|
||||
def get_bpy():
|
||||
global global_bpy
|
||||
global_bpy = bpy
|
||||
should_reset_scene = True
|
||||
|
||||
|
||||
def set_should_reset_scene(value):
|
||||
global should_reset_scene
|
||||
should_reset_scene = value
|
||||
|
||||
|
||||
def get_bpy():
|
||||
global global_bpy, should_reset_scene
|
||||
if should_reset_scene:
|
||||
reset_bpy()
|
||||
should_reset_scene = False
|
||||
return global_bpy
|
||||
|
||||
|
||||
def reset_bpy():
|
||||
global global_bpy
|
||||
bpy = global_bpy
|
||||
# not using this cause this cause seg fault frequently
|
||||
# global_bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
|
||||
# check if there's no object, return
|
||||
if len(bpy.data.objects) == 0:
|
||||
return
|
||||
# # check if there's no object, return
|
||||
# if len(bpy.data.objects) == 0:
|
||||
# return
|
||||
|
||||
# enter object mode
|
||||
if bpy.context.mode != 'OBJECT':
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
# # enter object mode
|
||||
# if bpy.context.mode != 'OBJECT':
|
||||
# bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
# Delete all objects
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
bpy.ops.object.select_all(action='SELECT')
|
||||
bpy.ops.object.delete()
|
||||
# # Delete all objects
|
||||
# bpy.ops.object.select_all(action='DESELECT')
|
||||
# bpy.ops.object.select_all(action='SELECT')
|
||||
# bpy.ops.object.delete()
|
||||
|
||||
# Delete all meshes
|
||||
for mesh in bpy.data.meshes:
|
||||
bpy.data.meshes.remove(mesh)
|
||||
# # Delete all meshes
|
||||
# for mesh in bpy.data.meshes:
|
||||
# bpy.data.meshes.remove(mesh)
|
||||
|
||||
# Delete all materials
|
||||
for material in bpy.data.materials:
|
||||
bpy.data.materials.remove(material)
|
||||
# # Delete all materials
|
||||
# for material in bpy.data.materials:
|
||||
# bpy.data.materials.remove(material)
|
||||
|
||||
# Delete all textures
|
||||
for texture in bpy.data.textures:
|
||||
bpy.data.textures.remove(texture)
|
||||
# # Delete all textures
|
||||
# for texture in bpy.data.textures:
|
||||
# bpy.data.textures.remove(texture)
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import { alertDialog } from "./state.js";
|
||||
import { van } from "./van.js";
|
||||
const { div, span } = van.tags;
|
||||
|
||||
export function Alert() {
|
||||
const color = van.state("bg-orange-100 text-orange-700 border-orange-500");
|
||||
|
||||
van.derive(() => {
|
||||
if (alertDialog.val.time > 0) {
|
||||
switch (alertDialog.val.type) {
|
||||
case "error":
|
||||
color.val = "bg-red-100 text-red-700 border-red-500";
|
||||
break;
|
||||
case "success":
|
||||
color.val = "bg-green-100 text-green-700 border-green-500";
|
||||
break;
|
||||
case "info":
|
||||
color.val = "bg-blue-100 text-blue-700 border-blue-500";
|
||||
break;
|
||||
case "warning":
|
||||
default:
|
||||
color.val = "bg-orange-100 text-orange-700 border-orange-500";
|
||||
break;
|
||||
}
|
||||
setTimeout(() => {
|
||||
alertDialog.val = { text: "", time: 0 };
|
||||
}, alertDialog.val.time);
|
||||
}
|
||||
});
|
||||
|
||||
return div(
|
||||
{
|
||||
class: () =>
|
||||
"absolute z-[100] bottom-8 flex justify-center w-full " +
|
||||
(alertDialog.val.text ? "" : "hidden"),
|
||||
},
|
||||
div(
|
||||
{
|
||||
class: () => `${color.val} border-t-4 rounded-sm p-2`,
|
||||
},
|
||||
() => span(alertDialog.val.text)
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { van } from "./van.js";
|
||||
const { div, span } = van.tags;
|
||||
|
||||
export function AppHeader() {
|
||||
return div(
|
||||
{
|
||||
class: () => "absolute flex justify-between top-0 w-full text-white p-4",
|
||||
},
|
||||
div(
|
||||
{},
|
||||
span(
|
||||
{
|
||||
class:
|
||||
"block bg-gradient-to-b from-gray-500 to-white text-transparent bg-clip-text text-2xl",
|
||||
},
|
||||
"Avatech v1"
|
||||
),
|
||||
span(
|
||||
{
|
||||
class:
|
||||
"bg-gradient-to-b from-gray-500 to-white text-transparent bg-clip-text text-lg",
|
||||
},
|
||||
"Get your DALLE3 AI Personal Clone"
|
||||
)
|
||||
),
|
||||
span({ class: "text-gray-300" }, "Twitter")
|
||||
);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import { iframeSrc, showEditor } from './state.js';
|
||||
import { van } from './van.js';
|
||||
const { button, iframe, div, img } = van.tags;
|
||||
|
||||
export function AvatarEditor() {
|
||||
return div(
|
||||
{
|
||||
class: 'w-full h-full',
|
||||
},
|
||||
button(
|
||||
{
|
||||
class: () =>
|
||||
'px-4 py-2 rounded-md absolute left-0 top-0 z-[100] bg-gray-800 pointer-events-auto ' +
|
||||
(showEditor.val ? '' : 'hidden'),
|
||||
onclick: () => {
|
||||
console.log('close');
|
||||
showEditor.val = false;
|
||||
},
|
||||
},
|
||||
'back',
|
||||
),
|
||||
iframe({
|
||||
id: 'avatech-editor-iframe',
|
||||
title: 'avatech-editor-iframe',
|
||||
name: 'avatech-editor-iframe',
|
||||
allow: "cross-origin-isolated",
|
||||
class: () =>
|
||||
'w-full h-full pointer-events-auto ' + (showEditor.val ? '' : 'hidden'),
|
||||
src: iframeSrc,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,774 @@
|
||||
import { van } from "./van.js";
|
||||
import {
|
||||
imageUrl,
|
||||
showPreview,
|
||||
previewUrl,
|
||||
showEditor,
|
||||
previewImg,
|
||||
previewImgLoading,
|
||||
alertDialog,
|
||||
isGenerateFlow,
|
||||
enableAutoSegment
|
||||
} from "./state.js";
|
||||
const { button, iframe, div, img, input, label, span, textarea, ul, li } =
|
||||
van.tags;
|
||||
import { app } from "./app.js";
|
||||
import { uploadPreview } from "./index.js";
|
||||
import { api } from "./api.js";
|
||||
import { segmented, uploadSegments } from "./LayerEditor.js";
|
||||
import { initModel } from "./onnx.js";
|
||||
// import { uploadSegments } from "./LayerEditor.js";
|
||||
|
||||
const workflowList = [
|
||||
"idle_avatar_(trigger)",
|
||||
"Auto_segment_workflow",
|
||||
"BronyaZaychik_(ChinaDress)",
|
||||
"BronyaZaychik_(Default_Silverwing)",
|
||||
"BronyaZaychik_(Non-official_office_ladysuit)",
|
||||
"BronyaZaychik_(Official_office_ladysuit)",
|
||||
"BronyaZaychikLora_withhand",
|
||||
"SilverWolf_(Default)",
|
||||
"SilverWolf_(Maid)",
|
||||
"SilverWolfLora_withhand",
|
||||
];
|
||||
|
||||
function editSegment(stage) {
|
||||
/** @type {import('../../../web/types/litegraph.js').LGraph}*/
|
||||
const graph = app.graph;
|
||||
const imageNodes = graph.findNodesByType("LoadImage");
|
||||
if (!imageNodes[0].imgs) return;
|
||||
|
||||
const nodes = graph.findNodesByType("SAM MultiLayer");
|
||||
|
||||
/** @type {any[]}*/
|
||||
const widgets = nodes[0].widgets;
|
||||
console.log(nodes[0]);
|
||||
console.log(nodes[0].widgets);
|
||||
widgets.find((x) => x.type == "button").callback();
|
||||
stage.val = 2;
|
||||
}
|
||||
|
||||
// const workflowList = ["Auto_segment_workflow"];
|
||||
/**
|
||||
* Load JSON workflow
|
||||
* @param {string} name - The name of the workflow to load
|
||||
*/
|
||||
async function loadJSONWorkflow(name) {
|
||||
if (name === 'default' || name.toLowerCase().startsWith("auto_segment")) {
|
||||
enableAutoSegment.val = true
|
||||
} else {
|
||||
enableAutoSegment.val = false
|
||||
}
|
||||
const json = await (await fetch(`./get_workflow?name=${name}`)).json();
|
||||
app.loadGraphData(json);
|
||||
console.log(json);
|
||||
}
|
||||
|
||||
async function updatePositivePrompt(app, prompt) {
|
||||
const positivePrompt = app.graph
|
||||
.findNodesByType("CLIPTextEncode")
|
||||
.find((x) => x.color == "#232");
|
||||
if (!positivePrompt) {
|
||||
alertDialog.val = {
|
||||
text: "Cannot find the CLIPTextEncode node. Please make sure the workflow is correct.",
|
||||
time: 5000,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
positivePrompt.widgets[0].inputEl.value = prompt;
|
||||
}
|
||||
|
||||
async function updateSeedValue(app, seed) {
|
||||
const kSampler = app.graph.findNodesByType("KSampler")[0];
|
||||
if (!kSampler) {
|
||||
alertDialog.val = {
|
||||
text: "Cannot find the KSampler node. Please make sure the workflow is correct.",
|
||||
time: 5000,
|
||||
};
|
||||
return;
|
||||
}
|
||||
kSampler.widgets[0].value = seed;
|
||||
kSampler.widgets[1].value = "fixed";
|
||||
}
|
||||
|
||||
async function uploadImage() {
|
||||
/** @type {import('../../../web/types/litegraph.js').LGraph}*/
|
||||
const graph = app.graph;
|
||||
const nodes = graph.findNodesByType("LoadImage");
|
||||
previewImgLoading.val = true;
|
||||
console.log(previewImgLoading.val);
|
||||
|
||||
/** @type {any[]}*/
|
||||
const widgets = nodes[0].widgets;
|
||||
console.log(nodes[0]);
|
||||
widgets.find((x) => x.type == "button").callback();
|
||||
while (true) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
if (nodes[0]?.imgs) {
|
||||
if (previewImg.val != "" && previewImg.val == nodes[0].imgs[0].currentSrc)
|
||||
continue;
|
||||
previewImgLoading.val = false;
|
||||
return nodes[0].imgs[0].currentSrc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const jsonWorkflowLoading = van.state(true);
|
||||
export const sharedAvatarLink = van.state("");
|
||||
|
||||
async function prepareImageFromUrlRedirect(stage) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
const queue_id = new URLSearchParams(window.location.search).get("queue-id");
|
||||
if (queue_id && queue_id != "") {
|
||||
console.log(queue_id);
|
||||
stage.val = 1;
|
||||
const graph = app.graph;
|
||||
const node = graph.findNodesByType("LoadImage");
|
||||
const imageName = queue_id + ".png";
|
||||
console.log(node[0]);
|
||||
node[0].widgets_values[0] = imageName;
|
||||
node[0].widgets[0].value = imageName;
|
||||
node[0].widgets[0]._value = imageName;
|
||||
graph.change();
|
||||
previewImg.val = api.apiURL(
|
||||
`/view?filename=${encodeURIComponent(
|
||||
imageName
|
||||
)}&type=input&subfolder=create_avatar_endpoint${app.getPreviewFormatParam()}`
|
||||
);
|
||||
console.log(previewImg);
|
||||
}
|
||||
const dragndrop = document.getElementById("dnd");
|
||||
dragndrop.addEventListener("dragenter", (evt) => {
|
||||
evt.preventDefault();
|
||||
dragndrop.className =
|
||||
"h-96 w-full border-2 border-purple-500 text-purple-500 border-dashed rounded-lg flex justify-center items-center";
|
||||
});
|
||||
dragndrop.addEventListener("dragleave", (evt) => {
|
||||
evt.preventDefault();
|
||||
dragndrop.className =
|
||||
"h-96 w-full border-2 border-black border-dashed items-center rounded-lg flex justify-center";
|
||||
});
|
||||
dragndrop.addEventListener("dragover", (evt) => {
|
||||
evt.preventDefault();
|
||||
});
|
||||
dragndrop.addEventListener("drop", async (evt) => {
|
||||
evt.preventDefault();
|
||||
dragndrop.className =
|
||||
"h-96 w-full border-2 border-black border-dashed items-center rounded-lg flex justify-center";
|
||||
if (evt.dataTransfer.files.length > 1) return;
|
||||
if (
|
||||
evt.dataTransfer.files[0].type != "image/jpeg" &&
|
||||
evt.dataTransfer.files[0].type != "image/png" &&
|
||||
evt.dataTransfer.files[0].type != "image/webp"
|
||||
)
|
||||
return;
|
||||
stage.val = 1;
|
||||
previewImg.val = URL.createObjectURL(evt.dataTransfer.files[0]);
|
||||
if (Object.entries(evt.dataTransfer.files).length) {
|
||||
await uploadFile(evt.dataTransfer.files[0], true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function AvatarPreview() {
|
||||
console.log("getting workflow json now");
|
||||
|
||||
const loading = van.state(false);
|
||||
const shareLoading = van.state("share"); // share, loading, shared
|
||||
|
||||
api.addEventListener("execution_start", (evt) => {
|
||||
loading.val = true;
|
||||
});
|
||||
|
||||
api.addEventListener("executed", (evt) => {
|
||||
const nodeId = evt.detail.node;
|
||||
const targetNode = graph._nodes_by_id[nodeId];
|
||||
if (targetNode.type === "AvatarMainOutput") {
|
||||
loading.val = false;
|
||||
}
|
||||
});
|
||||
|
||||
const email = van.state("");
|
||||
const stage = van.state(0); // 0: upload image, 1: edit segment, 2: generate
|
||||
|
||||
// This will wait 2 seconds until the everything is loaded
|
||||
prepareImageFromUrlRedirect(stage);
|
||||
|
||||
const renderSteps = () => {
|
||||
return div(
|
||||
{
|
||||
class: () =>
|
||||
"flex flex-col bg-white justify-center w-[32rem] max-w-[100%]",
|
||||
},
|
||||
div(
|
||||
{
|
||||
class: () =>
|
||||
" bg-gradient-to-b from-black via-[#5F5F5F] via-60% to-white text-transparent bg-clip-text font-gabarito text-4xl",
|
||||
},
|
||||
"Avatech v1"
|
||||
),
|
||||
div(
|
||||
{
|
||||
class: () =>
|
||||
" bg-gradient-to-b from-black via-[#5F5F5F] via-50% to-white text-transparent bg-clip-text font-gabarito text-2xl",
|
||||
},
|
||||
"Get your DALLE3 AI Personal Clone"
|
||||
),
|
||||
div(
|
||||
{
|
||||
class: () =>
|
||||
" w-full flex flex-col justify-center items-center gap-4",
|
||||
},
|
||||
!isGenerateFlow.val
|
||||
? div(
|
||||
{
|
||||
class: () =>
|
||||
"flex flex-col justify-center items-center gap-4 w-full",
|
||||
},
|
||||
div(
|
||||
{ class: () => "w-full flex mt-2" },
|
||||
button(
|
||||
{
|
||||
class: () => `btn w-full normal-case`,
|
||||
onclick: async () => {
|
||||
// previewImg.val = await uploadImage();
|
||||
// stage.val = 1;
|
||||
var input = document.createElement("input");
|
||||
input.type = "file";
|
||||
|
||||
document.body.appendChild(input);
|
||||
|
||||
// when the input content changes, do something
|
||||
input.onchange = async function (e) {
|
||||
stage.val = 1;
|
||||
if (Object.entries(e.target.files).length) {
|
||||
await uploadFile(e.target.files[0], true);
|
||||
}
|
||||
previewImg.val = URL.createObjectURL(e.target.files[0]);
|
||||
// upload files
|
||||
document.body.removeChild(input);
|
||||
};
|
||||
|
||||
// Trigger file browser
|
||||
input.click();
|
||||
},
|
||||
},
|
||||
div({ class: "badge badge-neutral" }, "1"),
|
||||
div("Upload your image"),
|
||||
span({
|
||||
class: "iconify text-lg",
|
||||
"data-icon": "material-symbols:drive-folder-upload",
|
||||
"data-inline": "false",
|
||||
}),
|
||||
() =>
|
||||
previewImgLoading.val
|
||||
? span({
|
||||
class: "loading loading-spinner loading-md",
|
||||
})
|
||||
: "",
|
||||
),
|
||||
),
|
||||
() => {
|
||||
const dnd = div(
|
||||
{
|
||||
id: "dnd",
|
||||
class: () =>
|
||||
"h-96 w-full border-2 border-black border-dashed items-center rounded-lg flex justify-center text-black",
|
||||
},
|
||||
"or drag and drop the image here",
|
||||
);
|
||||
const image = img({
|
||||
class: () => "z-[10] object-contain w-full h-[394px] border",
|
||||
src: previewImg,
|
||||
onload: () => {
|
||||
segmented.val = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (isMobileDevice()) {
|
||||
return previewImg.val !== "" ? image : "";
|
||||
} else {
|
||||
return previewImg.val === "" ? dnd : image;
|
||||
}
|
||||
},
|
||||
button(
|
||||
{
|
||||
class: () =>
|
||||
"btn w-full normal-case " +
|
||||
(stage.val < 1 ? "btn-disabled" : ""),
|
||||
onclick: () => {
|
||||
enableAutoSegment.val = true;
|
||||
editSegment(stage)
|
||||
},
|
||||
},
|
||||
div({ class: "badge badge-neutral" }, "2"),
|
||||
"Edit Segment",
|
||||
),
|
||||
button(
|
||||
{
|
||||
class: () =>
|
||||
"btn w-full normal-case " +
|
||||
(stage.val < 2 ? "btn-disabled" : ""),
|
||||
onclick: async () => {
|
||||
// const uploaded = await uploadSegments();
|
||||
// if (!uploaded) return;
|
||||
|
||||
const graph = app.graph;
|
||||
const imageNodes = graph.findNodesByType("LoadImage");
|
||||
if (!imageNodes[0].imgs) return;
|
||||
document.getElementById("queue-button").click();
|
||||
},
|
||||
},
|
||||
div({ class: "badge badge-neutral" }, "3"),
|
||||
() =>
|
||||
loading.val
|
||||
? span({
|
||||
class: "loading loading-spinner loading-md",
|
||||
})
|
||||
: "Make It Alive!",
|
||||
),
|
||||
)
|
||||
: div(
|
||||
{
|
||||
class:
|
||||
"flex flex-col justify-center items-center gap-4 w-full text-black",
|
||||
},
|
||||
div(
|
||||
{
|
||||
class:
|
||||
"w-full mt-2 flex flex-col rounded-md left-0 top-0",
|
||||
},
|
||||
textarea({
|
||||
class:
|
||||
"textarea textarea-bordered border-gray-300 border-b-0 focus:outline-none resize-none rounded-t-md rounded-b-none text-md h-36",
|
||||
placeholder: "Enter your prompt",
|
||||
defaultValue:
|
||||
"1girl, looking at viewer, open mouth, simple background, white background, smile",
|
||||
id: "positivePromptProxy",
|
||||
}),
|
||||
div(
|
||||
{
|
||||
class:
|
||||
"flex flex-row gap-2 border border-gray-300 rounded-b-md text-md items-center",
|
||||
},
|
||||
span({ class: "ml-4" }, "Seed"),
|
||||
div({ class: "divider divider-horizontal m-0" }),
|
||||
input({
|
||||
type: "text",
|
||||
class: "input border-none focus:outline-none w-full p-0",
|
||||
placeholder: "Seed",
|
||||
defaultValue: "1234",
|
||||
id: "seedProxy",
|
||||
}),
|
||||
div(
|
||||
{
|
||||
onclick: () => {
|
||||
const random4Digits =
|
||||
Math.floor(Math.random() * 9000) + 1000;
|
||||
console.log(
|
||||
random4Digits,
|
||||
document.getElementById("seedProxy").value,
|
||||
);
|
||||
document.getElementById("seedProxy").value =
|
||||
random4Digits.toString();
|
||||
},
|
||||
},
|
||||
span({
|
||||
class: "iconify text-2xl mr-4 hover:cursor-pointer",
|
||||
"data-icon": "fad:random-1dice",
|
||||
"data-inline": "false",
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
button(
|
||||
{
|
||||
class: "btn w-full normal-case ",
|
||||
onclick: async () => {
|
||||
loading.val = true;
|
||||
|
||||
updatePositivePrompt(
|
||||
app,
|
||||
document.getElementById("positivePromptProxy").value,
|
||||
);
|
||||
updateSeedValue(
|
||||
app,
|
||||
document.getElementById("seedProxy").value,
|
||||
);
|
||||
|
||||
const sam = app.graph.findNodesByType("SAM MultiLayer")[0];
|
||||
if (!sam) {
|
||||
alertDialog.val = {
|
||||
text: "Cannot find the SAM node. Please make sure the workflow is correct.",
|
||||
time: 5000,
|
||||
};
|
||||
return;
|
||||
}
|
||||
const ckpt = sam.widgets[0].value;
|
||||
const modelType = ckpt.match(/vit_[lbh]/)?.[0];
|
||||
await initModel(modelType);
|
||||
await uploadSegments();
|
||||
|
||||
document.getElementById("queue-button").click();
|
||||
},
|
||||
},
|
||||
div({ class: "badge badge-neutral" }, "1"),
|
||||
() =>
|
||||
loading.val
|
||||
? span({ class: "loading loading-spinner loading-md" })
|
||||
: "Make It Alive!",
|
||||
),
|
||||
button(
|
||||
{
|
||||
class: () =>
|
||||
"btn w-full normal-case ",
|
||||
onclick: () => {
|
||||
enableAutoSegment.val = false;
|
||||
editSegment(stage)
|
||||
},
|
||||
},
|
||||
div({ class: "badge badge-neutral" }, "2"),
|
||||
"Edit Segment",
|
||||
),
|
||||
// button(
|
||||
// {
|
||||
// class: "btn w-full normal-case",
|
||||
// onclick: () => {
|
||||
// /** @type {import('../../../web/types/litegraph.js').LGraph}*/
|
||||
// const graph = app.graph;
|
||||
// const nodes = graph.findNodesByType("SAM MultiLayer");
|
||||
|
||||
// /** @type {any[]}*/
|
||||
// const widgets = nodes[0].widgets;
|
||||
// console.log(nodes[0]);
|
||||
// console.log(nodes[0].widgets);
|
||||
// widgets.find((x) => x.type == "button").callback();
|
||||
// },
|
||||
// },
|
||||
// div({ class: "badge badge-neutral" }, "2"),
|
||||
// "(Optional) Edit Segment",
|
||||
// ),
|
||||
),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const renderIFrame = () => {
|
||||
return iframe({
|
||||
id: "avatech-viewer-iframe",
|
||||
title: "avatech-viewer-iframe",
|
||||
name: "avatech-viewer-iframe",
|
||||
allow: "cross-origin-isolated",
|
||||
class: () =>
|
||||
"w-full h-full min-w-[350px] min-h-[350px] z-[100] pointer-events-auto flex border-none overflow-hidden bg-transparent" +
|
||||
(showPreview.val ? "" : "hidden"),
|
||||
// src: "https://labs.avatech.ai/viewer/default",
|
||||
// src: "http://localhost:3000/viewer/default",
|
||||
src: previewUrl,
|
||||
});
|
||||
};
|
||||
|
||||
const renderShareLink = () => {
|
||||
return div(
|
||||
{
|
||||
class: () =>
|
||||
"w-full flex flex-col gap-2 justify-center items-center mt-8",
|
||||
},
|
||||
div(
|
||||
{
|
||||
class: () =>
|
||||
"w-full flex justify-center font-bold italic text-gray-500",
|
||||
},
|
||||
span("We are launching OpenAI Assistant API integration soon!")
|
||||
),
|
||||
div(
|
||||
{ class: () => "w-[24rem] flex justify-center items-center" },
|
||||
input({
|
||||
type: "text",
|
||||
class: () =>
|
||||
"w-full input input-bordered text-black rounded rounded-l-md rounded-r-none !outline-none",
|
||||
onchange: (e) => {
|
||||
email.val = e.target.value;
|
||||
},
|
||||
placeholder: "Enter your email",
|
||||
}),
|
||||
button(
|
||||
{
|
||||
class: () =>
|
||||
"btn rounded rounded-l-none rounded-r-md no-animation bg-neutral-800 hover:bg-neutral-950 text-white border-none normal-case",
|
||||
onclick: async () => {
|
||||
if (shareLoading.val === "share") {
|
||||
shareLoading.val = "loading";
|
||||
const url = await (await fetch("./get_webhook")).json();
|
||||
await uploadPreview();
|
||||
await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username: "Avabot",
|
||||
avatar_url:
|
||||
"https://avatech-avatar-dev1.nyc3.cdn.digitaloceanspaces.com/avatechai.png",
|
||||
content: "New register! \n" + email.val,
|
||||
}),
|
||||
});
|
||||
shareLoading.val = "shared";
|
||||
}
|
||||
if (sharedAvatarLink.val) {
|
||||
await navigator.clipboard.writeText(sharedAvatarLink.val);
|
||||
alertDialog.val = {
|
||||
text: "Avatar link copied to clipboard!",
|
||||
type: "success",
|
||||
time: 5000,
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
() => {
|
||||
switch (shareLoading.val) {
|
||||
case "share":
|
||||
return "Get Avatar Link";
|
||||
case "loading":
|
||||
return span({
|
||||
class: "loading loading-spinner loading-md",
|
||||
});
|
||||
case "shared":
|
||||
return span({
|
||||
class: "iconify text-xl",
|
||||
"data-icon": "lucide:copy-check",
|
||||
});
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const renderCloseButton = () => {
|
||||
return button(
|
||||
{
|
||||
class: () =>
|
||||
"btn flex flex-row btn-ghost text-black normal-case rounded-md left-0 top-0 z-[200] pointer-events-auto sm:btn-md btn-sm ",
|
||||
onclick: () => {
|
||||
showPreview.val = false;
|
||||
},
|
||||
},
|
||||
span({
|
||||
class: "iconify text-lg",
|
||||
"data-icon": "ic:round-close",
|
||||
"data-inline": "false",
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const renderRestartButton = () => {
|
||||
return button(
|
||||
{
|
||||
class: () =>
|
||||
"btn flex flex-row btn-ghost text-black normal-case rounded-md left-0 top-0 z-[200] pointer-events-auto sm:btn-md btn-sm ",
|
||||
onclick: () => {
|
||||
fetch("https://7a49f4ad27be4dcf.ngrok.app/restart");
|
||||
},
|
||||
},
|
||||
span({
|
||||
class: "iconify text-lg",
|
||||
"data-icon": "mdi:restart",
|
||||
"data-inline": "false",
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const renderChangeWorkflowButton = () => {
|
||||
return div(
|
||||
{
|
||||
class: () =>
|
||||
"dropdown dropdown-hover dropdown-bottom z-[200] pointer-events-auto text-black ",
|
||||
},
|
||||
label(
|
||||
{
|
||||
class: () =>
|
||||
"btn flex flex-row btn-ghost normal-case rounded-md sm:btn-md btn-sm",
|
||||
tabIndex: () => 0,
|
||||
},
|
||||
span({
|
||||
class: "iconify text-lg",
|
||||
"data-icon": "ic:round-swap-vert",
|
||||
"data-inline": "false",
|
||||
}),
|
||||
span({ class: "sm:flex hidden" }, () =>
|
||||
jsonWorkflowLoading.val ? "Loading" : "Change workflow"
|
||||
)
|
||||
),
|
||||
ul(
|
||||
{
|
||||
class: () =>
|
||||
"dropdown-content -left-[100px] z-[200] menu p-2 shadow rounded-box w-96 bg-white",
|
||||
tabIndex: () => 0,
|
||||
},
|
||||
workflowList.map((val, index) => {
|
||||
return li(
|
||||
{
|
||||
class: () => "p-4 btn btn-ghost items-start",
|
||||
onclick: async (e) => {
|
||||
e.preventDefault();
|
||||
document.activeElement.blur()
|
||||
await loadJSONWorkflow(val);
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
const kSampler = app.graph.findNodesByType("KSampler")[0];
|
||||
if (!kSampler) isGenerateFlow.val = false;
|
||||
else isGenerateFlow.val = true;
|
||||
},
|
||||
},
|
||||
() => val
|
||||
);
|
||||
}),
|
||||
div({ class: () => "divider !my-0" }),
|
||||
li(
|
||||
{
|
||||
class: () => "p-4 btn btn-ghost items-start",
|
||||
onclick: (e) => {
|
||||
let input = document.createElement("input");
|
||||
input.type = "file";
|
||||
document.body.appendChild(input);
|
||||
input.accept = ".json,image/png,.latent,.safetensors";
|
||||
input.onchange = async function (e) {
|
||||
if (Object.entries(e.target.files).length) {
|
||||
await app.handleFile(e.target.files[0]);
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
const kSampler = app.graph.findNodesByType("KSampler")[0];
|
||||
if (!kSampler) isGenerateFlow.val = false;
|
||||
else isGenerateFlow.val = true;
|
||||
document.body.removeChild(input);
|
||||
};
|
||||
input.click();
|
||||
// document.getElementById("comfy-load-button").click();
|
||||
},
|
||||
},
|
||||
"Import..."
|
||||
)
|
||||
)
|
||||
);
|
||||
// return button(
|
||||
// {
|
||||
// class: () =>
|
||||
// "btn text-black flex flex-row btn-ghost normal-case rounded-md left-0 top-0 z-[200] pointer-events-auto sm:btn-md btn-sm ",
|
||||
// onclick: () => {
|
||||
// let input = document.createElement("input");
|
||||
// input.type = "file";
|
||||
// document.body.appendChild(input);
|
||||
// input.accept = ".json,image/png,.latent,.safetensors";
|
||||
// input.onchange = async function (e) {
|
||||
// if (Object.entries(e.target.files).length) {
|
||||
// await app.handleFile(e.target.files[0]);
|
||||
// }
|
||||
// await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
// const kSampler = app.graph.findNodesByType("KSampler")[0];
|
||||
// if (!kSampler) isGenerateFlow.val = false;
|
||||
// else isGenerateFlow.val = true;
|
||||
// document.body.removeChild(input);
|
||||
// };
|
||||
// input.click();
|
||||
// // document.getElementById("comfy-load-button").click();
|
||||
// },
|
||||
// },
|
||||
// span({
|
||||
// class: "iconify text-lg",
|
||||
// "data-icon": "ic:round-swap-vert",
|
||||
// "data-inline": "false",
|
||||
// }),
|
||||
// span({ class: "sm:flex hidden" }, () =>
|
||||
// jsonWorkflowLoading.val ? "Loading" : "Change workflow",
|
||||
// ),
|
||||
// );
|
||||
};
|
||||
|
||||
const renderTwitter = () => {
|
||||
return button(
|
||||
{
|
||||
class: () =>
|
||||
"absolute top-4 right-4 btn sm:w-32 w-20 text-black btn-ghost text-xs z-[200] !px-0 normal-case sm:btn-md btn-sm",
|
||||
onclick: () => window.open("https://twitter.com/avatech_gg", "_blank"),
|
||||
},
|
||||
"Twitter"
|
||||
);
|
||||
};
|
||||
|
||||
const isMobileDevice = () => {
|
||||
return window.screen.width < 768;
|
||||
};
|
||||
|
||||
return div(
|
||||
{
|
||||
class: () => {
|
||||
console.log(showPreview);
|
||||
|
||||
return (
|
||||
(showPreview.val ? "" : "hidden ") +
|
||||
"absolute w-[360px] h-[360px] rounded-xl overflow-hidden right-0 top-0 z-[99] pointer-events-auto flex border-none bg-transparent"
|
||||
);
|
||||
},
|
||||
},
|
||||
renderIFrame(),
|
||||
);
|
||||
}
|
||||
|
||||
function showImage(name) {
|
||||
const graph = app.graph;
|
||||
const node = graph.findNodesByType("LoadImage");
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
node[0].imgs = [img];
|
||||
app.graph.setDirtyCanvas(true);
|
||||
};
|
||||
let folder_separator = name.lastIndexOf("/");
|
||||
let subfolder = "";
|
||||
if (folder_separator > -1) {
|
||||
subfolder = name.substring(0, folder_separator);
|
||||
name = name.substring(folder_separator + 1);
|
||||
}
|
||||
img.src = api.apiURL(
|
||||
`/view?filename=${encodeURIComponent(
|
||||
name
|
||||
)}&type=input&subfolder=${subfolder}${app.getPreviewFormatParam()}`
|
||||
);
|
||||
node.setSizeForImage?.();
|
||||
}
|
||||
|
||||
async function uploadFile(file, updateNode, pasted = false) {
|
||||
try {
|
||||
// Wrap file in formdata so it includes filename
|
||||
const graph = app.graph;
|
||||
const nodes = graph.findNodesByType("LoadImage");
|
||||
const widgets = nodes[0].widgets.find((w) => w.name === "image");
|
||||
const body = new FormData();
|
||||
body.append("image", file);
|
||||
if (pasted) body.append("subfolder", "pasted");
|
||||
const resp = await api.fetchApi("/upload/image", {
|
||||
method: "POST",
|
||||
body,
|
||||
});
|
||||
|
||||
if (resp.status === 200) {
|
||||
const data = await resp.json();
|
||||
// Add the file to the dropdown list and update the widget value
|
||||
let path = data.name;
|
||||
if (data.subfolder) path = data.subfolder + "/" + path;
|
||||
|
||||
if (!widgets.options.values.includes(path)) {
|
||||
widgets.options.values.push(path);
|
||||
}
|
||||
|
||||
if (updateNode) {
|
||||
showImage(path);
|
||||
widgets.value = path;
|
||||
}
|
||||
} else {
|
||||
alert(resp.status + " - " + resp.statusText);
|
||||
}
|
||||
} catch (error) {
|
||||
alert(error);
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import { van } from "./van.js";
|
||||
import { showEditor } from "./state.js";
|
||||
const { button, iframe, div, img } = van.tags;
|
||||
|
||||
export function AvatarViewer() {
|
||||
return div(
|
||||
{
|
||||
class: () =>
|
||||
"w-[320px] h-[370px] absolute right-0 top-0 z-[100] pointer-events-auto mt-4 mr-4 " +
|
||||
(!showEditor.val ? "" : "hidden"),
|
||||
},
|
||||
iframe({
|
||||
id: "avatech-viewer-iframe",
|
||||
title: "avatech-viewer-iframe",
|
||||
name: "avatech-viewer-iframe",
|
||||
allow: "cross-origin-isolated",
|
||||
class: () =>
|
||||
"w-full h-full flex pointer-events-auto rounded-2xl border-none " +
|
||||
(!showEditor.val ? "" : "hidden"),
|
||||
src: "https://labs.avatech.ai/viewer/default",
|
||||
// src: 'http://localhost:3000/viewer/default',
|
||||
})
|
||||
);
|
||||
}
|
||||
+11
-8
@@ -1,19 +1,22 @@
|
||||
import { ImageEditor } from './ImageEditor.js';
|
||||
import { AvatarEditor } from './AvatarEditor.js';
|
||||
import { LayerEditor } from './LayerEditor.js';
|
||||
import { ShapeFlowEditor } from './ShapeFlowEditor.js';
|
||||
import { van } from './van.js';
|
||||
import { AvatarViewer } from './AvatarViewer.js';
|
||||
import { AvatarPreview } from './AvatarPreview.js';
|
||||
import { Loading } from './Loading.js';
|
||||
import { Alert } from './Alert.js';
|
||||
import { AppHeader } from './AppHeader.js';
|
||||
const { button, iframe, div, img } = van.tags;
|
||||
|
||||
export function Container() {
|
||||
return div(
|
||||
{
|
||||
class: 'fixed left-0 top-0 w-full h-full z-[200] pointer-events-none',
|
||||
class: 'fixed left-0 top-0 w-full h-full z-[1000] pointer-events-none',
|
||||
id: 'avatech-editor',
|
||||
},
|
||||
AvatarEditor(),
|
||||
ImageEditor(),
|
||||
AvatarViewer(),
|
||||
Loading()
|
||||
ShapeFlowEditor(),
|
||||
LayerEditor(),
|
||||
AvatarPreview(),
|
||||
Loading(),
|
||||
Alert()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { van } from "./van.js";
|
||||
const { button, div, span, input } = van.tags;
|
||||
|
||||
export function GetShareLink() {
|
||||
return div(
|
||||
{
|
||||
class: () =>
|
||||
"absolute flex flex-col justify-center items-center top-0 left-0 bg-gray-900 bg-opacity-50 pointer-events-auto w-full h-full gap-2",
|
||||
},
|
||||
span("We're launching OpenAI Assistant API integration soon!"),
|
||||
div(
|
||||
{
|
||||
class: "w-[24rem] flex justify-center items-center",
|
||||
},
|
||||
input({
|
||||
class:
|
||||
"w-full input input-bordered text-black rounded rounded-l-md rounded-r-none",
|
||||
placeholder: "Email",
|
||||
}),
|
||||
button(
|
||||
{
|
||||
class:
|
||||
"btn rounded rounded-l-none rounded-r-md no-animation bg-neutral hover:bg-neutral-focus text-white border-none normal-case",
|
||||
},
|
||||
"Get Avatar Link"
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1,265 +0,0 @@
|
||||
import { SideBar } from "./SideBar.js";
|
||||
import { initModel, runONNX } from "./onnx.js";
|
||||
import {
|
||||
showImageEditor,
|
||||
point_label,
|
||||
imageUrl,
|
||||
imageContainerSize,
|
||||
imagePrompts,
|
||||
targetNode,
|
||||
imageSize,
|
||||
selectedLayer,
|
||||
imagePromptsMulti,
|
||||
embeddings,
|
||||
} from "./state.js";
|
||||
import { van } from "./van.js";
|
||||
const { button, div, img, canvas, span } = van.tags;
|
||||
|
||||
let throttle = false;
|
||||
|
||||
export function updateImagePrompts() {
|
||||
if (selectedLayer.val !== "" && selectedLayer.val !== undefined) {
|
||||
imagePromptsMulti.val = {
|
||||
...imagePromptsMulti.val,
|
||||
[selectedLayer.val]: imagePrompts.val,
|
||||
};
|
||||
|
||||
targetNode.val.widgets.find((x) => x.name === "image_prompts_json").value =
|
||||
JSON.stringify(imagePromptsMulti.val);
|
||||
} else {
|
||||
targetNode.val.widgets.find((x) => x.name === "image_prompts_json").value =
|
||||
JSON.stringify(imagePrompts.val);
|
||||
}
|
||||
targetNode.val.graph.change();
|
||||
}
|
||||
|
||||
function handleClick(e) {
|
||||
const rect = e.target.getBoundingClientRect();
|
||||
const x = e.clientX - rect.left;
|
||||
const y = e.clientY - rect.top;
|
||||
const relativeX = Math.trunc(
|
||||
((x / e.target.offsetWidth) * imageSize.val.width) / imageSize.val.imgScale
|
||||
);
|
||||
const relativeY = Math.trunc(
|
||||
((y / e.target.offsetHeight) * imageSize.val.height) /
|
||||
imageSize.val.imgScale
|
||||
);
|
||||
|
||||
imagePrompts.val = [
|
||||
...imagePrompts.val,
|
||||
{ x: relativeX, y: relativeY, label: e.isRight ? 0 : 1 },
|
||||
];
|
||||
|
||||
updateImagePrompts();
|
||||
drawSegment(getClicks());
|
||||
}
|
||||
|
||||
function handlePointClick(e, point) {
|
||||
e.preventDefault();
|
||||
imagePrompts.val = imagePrompts.val.filter(
|
||||
(x) => !(x.x === point.x && x.y === point.y)
|
||||
);
|
||||
updateImagePrompts();
|
||||
drawSegment(getClicks());
|
||||
}
|
||||
|
||||
function handleImageSize(image) {
|
||||
// Input images to SAM must be resized so the longest side is 1024
|
||||
const documentHeight = document.documentElement.clientHeight;
|
||||
const LONG_SIDE_LENGTH = 1024;
|
||||
let w = image.naturalWidth;
|
||||
let h = image.naturalHeight;
|
||||
const samScale = LONG_SIDE_LENGTH / Math.max(h, w);
|
||||
const imgScale = documentHeight / Math.max(h, w);
|
||||
return { height: h, width: w, samScale, imgScale };
|
||||
}
|
||||
|
||||
export function getClicks() {
|
||||
return imagePrompts.val.map((point) => ({
|
||||
x: point.x,
|
||||
y: point.y,
|
||||
clickType: point.label,
|
||||
}));
|
||||
}
|
||||
|
||||
export function drawSegment(clicks) {
|
||||
const canvas = document.getElementById("mask-canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (clicks.length === 0) {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
return;
|
||||
}
|
||||
if (embeddings.val) {
|
||||
runONNX(clicks, embeddings.val).then((mask) => {
|
||||
if (mask) {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.drawImage(mask, 0, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
initModel();
|
||||
|
||||
export function ImageEditor() {
|
||||
let realTimeSegment = true;
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (showImageEditor.val && e.code === "Tab") {
|
||||
e.preventDefault();
|
||||
realTimeSegment = !realTimeSegment;
|
||||
if (!realTimeSegment) {
|
||||
drawSegment(getClicks());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return div(
|
||||
{
|
||||
class: () =>
|
||||
"absolute flex bg-gray-900 bg-opacity-50 top-0 w-full h-full pointer-events-auto " +
|
||||
(showImageEditor.val ? "" : "hidden"),
|
||||
},
|
||||
button(
|
||||
{
|
||||
class: () =>
|
||||
"btn btn-circle flex flex-row btn-ghost normal-case absolute p-0 rounded-md left-2 top-0 z-[200] w-fit",
|
||||
onclick: () => {
|
||||
console.log("close");
|
||||
showImageEditor.val = false;
|
||||
},
|
||||
},
|
||||
span({
|
||||
class: "iconify text-lg",
|
||||
"data-icon": "ic:baseline-arrow-back",
|
||||
"data-inline": "false",
|
||||
}),
|
||||
div("Back")
|
||||
),
|
||||
div(
|
||||
{
|
||||
class:
|
||||
"hidden w-full flex justify-center absolute top-0 left-0 right-0 items-center",
|
||||
},
|
||||
button(
|
||||
{
|
||||
class: () => " px-4 py-2 rounded-md left-0 top-0 z-[200] ",
|
||||
onclick: () => {
|
||||
point_label.val = 1;
|
||||
},
|
||||
},
|
||||
"Positive"
|
||||
),
|
||||
button(
|
||||
{
|
||||
class: () => " px-4 py-2 rounded-md left-0 top-0 z-[200] ",
|
||||
onclick: () => {
|
||||
point_label.val = 0;
|
||||
},
|
||||
},
|
||||
"Negative"
|
||||
)
|
||||
),
|
||||
div(
|
||||
{
|
||||
class: "flex items-center justify-center w-full h-full",
|
||||
id: "image-container",
|
||||
},
|
||||
img({
|
||||
class:
|
||||
"fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2",
|
||||
src: imageUrl,
|
||||
onload: (e) => {
|
||||
imageSize.val = handleImageSize(e.target);
|
||||
|
||||
document.getElementById("image-container").style.scale =
|
||||
imageSize.val.imgScale;
|
||||
|
||||
imageContainerSize.val = {
|
||||
width: e.target.offsetWidth,
|
||||
height: e.target.offsetHeight,
|
||||
};
|
||||
|
||||
const canvas = document.getElementById("mask-canvas");
|
||||
canvas.width = e.target.naturalWidth;
|
||||
canvas.height = e.target.naturalHeight;
|
||||
},
|
||||
oncontextmenu: (e) => {
|
||||
e.preventDefault();
|
||||
e.isRight = true;
|
||||
handleClick(e);
|
||||
},
|
||||
onclick: (e) => {
|
||||
handleClick(e);
|
||||
},
|
||||
onmouseleave: (e) => {
|
||||
drawSegment(getClicks());
|
||||
},
|
||||
onmousemove: (e) => {
|
||||
if (!throttle) {
|
||||
throttle = true;
|
||||
setTimeout(() => {
|
||||
throttle = false;
|
||||
|
||||
if (embeddings.val && realTimeSegment) {
|
||||
const rect = e.target.getBoundingClientRect();
|
||||
const x = e.clientX - rect.left;
|
||||
const y = e.clientY - rect.top;
|
||||
const relativeX = Math.trunc(
|
||||
((x / e.target.offsetWidth) * imageSize.val.width) /
|
||||
imageSize.val.imgScale
|
||||
);
|
||||
const relativeY = Math.trunc(
|
||||
((y / e.target.offsetHeight) * imageSize.val.height) /
|
||||
imageSize.val.imgScale
|
||||
);
|
||||
|
||||
const clicks = [
|
||||
...getClicks(),
|
||||
{ x: relativeX, y: relativeY, clickType: 1 },
|
||||
];
|
||||
drawSegment(clicks);
|
||||
}
|
||||
}, 10);
|
||||
}
|
||||
},
|
||||
}),
|
||||
canvas({
|
||||
class:
|
||||
"pointer-events-none fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 opacity-80",
|
||||
id: "mask-canvas",
|
||||
}),
|
||||
() => {
|
||||
return div(
|
||||
{
|
||||
class: "absolute w-full h-full pointer-events-none",
|
||||
style: () =>
|
||||
`width: ${imageContainerSize.val.width}px; height: ${imageContainerSize.val.height}px;`,
|
||||
},
|
||||
...imagePrompts.val?.map((point) => {
|
||||
return button({
|
||||
style: () =>
|
||||
`left: ${
|
||||
(point.x / imageSize.val.width) * imageContainerSize.val.width
|
||||
}px; top: ${
|
||||
(point.y / imageSize.val.height) *
|
||||
imageContainerSize.val.height
|
||||
}px; transform: translate(-50%, -50%);`,
|
||||
class: () =>
|
||||
`absolute w-3 h-3 p-0 rounded-full pointer-events-auto ${
|
||||
point.label === 1 ? "bg-green-500" : "bg-red-500"
|
||||
}`,
|
||||
|
||||
oncontextmenu: (e) => {
|
||||
handlePointClick(e, point);
|
||||
},
|
||||
onclick: (e) => {
|
||||
handlePointClick(e, point);
|
||||
},
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
),
|
||||
SideBar()
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,696 @@
|
||||
import { SideBar } from "./SideBar.js";
|
||||
import { api } from "./api.js";
|
||||
import { app } from "./app.js";
|
||||
import { runONNX } from "./onnx.js";
|
||||
import {
|
||||
showImageEditor,
|
||||
point_label,
|
||||
imageUrl,
|
||||
imageContainerSize,
|
||||
imagePrompts,
|
||||
targetNode,
|
||||
imageSize,
|
||||
selectedLayer,
|
||||
imagePromptsMulti,
|
||||
embeddings,
|
||||
embeddingID,
|
||||
alertDialog,
|
||||
allImagePrompts,
|
||||
boxesMulti,
|
||||
enableAutoSegment,
|
||||
} from "./state.js";
|
||||
import { van } from "./van.js";
|
||||
import vision from "https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.3";
|
||||
const { PoseLandmarker, FaceLandmarker, FilesetResolver } = vision;
|
||||
const { button, div, img, canvas, span } = van.tags;
|
||||
|
||||
let throttle = false;
|
||||
const positivePrompt = van.state(true);
|
||||
const enableBackgroundRemover = van.state(true);
|
||||
const isMobileDevice = () => {
|
||||
return window.screen.width < 768;
|
||||
};
|
||||
|
||||
// Auto segmentation
|
||||
const filesetResolver = await FilesetResolver.forVisionTasks(
|
||||
"https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.3/wasm"
|
||||
);
|
||||
const faceLandmarker = await FaceLandmarker.createFromOptions(filesetResolver, {
|
||||
baseOptions: {
|
||||
modelAssetPath: `https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task`,
|
||||
delegate: "GPU",
|
||||
},
|
||||
// outputFaceBlendshapes: true,
|
||||
runningMode: "IMAGE",
|
||||
numFaces: 1,
|
||||
});
|
||||
const poseLandmarker = await PoseLandmarker.createFromOptions(filesetResolver, {
|
||||
baseOptions: {
|
||||
modelAssetPath: `https://storage.googleapis.com/mediapipe-models/pose_landmarker/pose_landmarker_full/float16/1/pose_landmarker_full.task`,
|
||||
delegate: "GPU",
|
||||
},
|
||||
runningMode: "IMAGE",
|
||||
numPoses: 1,
|
||||
});
|
||||
const layerMapping = {
|
||||
L_eye: {
|
||||
useMiddle: false,
|
||||
positiveOffsetX: 0,
|
||||
positiveOffsetY: 0,
|
||||
negativeOffsetX: 0,
|
||||
negativeOffsetY: 0,
|
||||
positiveScale: 0.25,
|
||||
negativeScale: 0.5,
|
||||
indices: FaceLandmarker.FACE_LANDMARKS_LEFT_EYE,
|
||||
},
|
||||
R_eye: {
|
||||
useMiddle: false,
|
||||
positiveOffsetX: 0,
|
||||
positiveOffsetY: 0,
|
||||
negativeOffsetX: 0,
|
||||
negativeOffsetY: 0,
|
||||
positiveScale: 0.25,
|
||||
negativeScale: 0.5,
|
||||
indices: FaceLandmarker.FACE_LANDMARKS_RIGHT_EYE,
|
||||
},
|
||||
L_iris: {
|
||||
useMiddle: false,
|
||||
positiveOffsetX: 0,
|
||||
positiveOffsetY: 0,
|
||||
negativeOffsetX: 0,
|
||||
negativeOffsetY: 0,
|
||||
positiveScale: -0.2,
|
||||
negativeScale: 0.5,
|
||||
indices: FaceLandmarker.FACE_LANDMARKS_LEFT_IRIS,
|
||||
},
|
||||
R_iris: {
|
||||
useMiddle: false,
|
||||
positiveOffsetX: 0,
|
||||
positiveOffsetY: 0,
|
||||
negativeOffsetX: 0,
|
||||
negativeOffsetY: 0,
|
||||
positiveScale: -0.2,
|
||||
negativeScale: 0.5,
|
||||
indices: FaceLandmarker.FACE_LANDMARKS_RIGHT_IRIS,
|
||||
},
|
||||
face: {
|
||||
useMiddle: false,
|
||||
positiveOffsetX: 0,
|
||||
positiveOffsetY: 60,
|
||||
negativeOffsetX: 0,
|
||||
negativeOffsetY: 0,
|
||||
positiveScale: 0.5,
|
||||
negativeScale: 0,
|
||||
indices: FaceLandmarker.FACE_LANDMARKS_FACE_OVAL,
|
||||
},
|
||||
mouth: {
|
||||
useMiddle: false,
|
||||
positiveOffsetX: 0,
|
||||
positiveOffsetY: 0,
|
||||
negativeOffsetX: 0,
|
||||
negativeOffsetY: 0,
|
||||
positiveScale: -0.3,
|
||||
negativeScale: 0.3,
|
||||
// https://stackoverflow.com/questions/66649492/how-to-get-specific-landmark-of-face-like-lips-or-eyes-using-tensorflow-js-face
|
||||
indices: [61, 37, 270, 91, 314].map((x) => ({
|
||||
start: x,
|
||||
end: x,
|
||||
})),
|
||||
},
|
||||
mouth_in: {
|
||||
useMiddle: false,
|
||||
positiveOffsetX: 0,
|
||||
positiveOffsetY: 0,
|
||||
negativeOffsetX: 0,
|
||||
negativeOffsetY: 0,
|
||||
positiveScale: -0.5,
|
||||
negativeScale: 0.5,
|
||||
// https://stackoverflow.com/questions/66649492/how-to-get-specific-landmark-of-face-like-lips-or-eyes-using-tensorflow-js-face
|
||||
indices: [310, 88].map((x) => ({
|
||||
start: x,
|
||||
end: x,
|
||||
})),
|
||||
},
|
||||
};
|
||||
|
||||
export const segmented = van.state(false);
|
||||
|
||||
export async function autoSegment() {
|
||||
const image = document.getElementById("image");
|
||||
const landmarks = faceLandmarker.detect(image).faceLandmarks[0];
|
||||
|
||||
Object.entries(layerMapping).forEach(([key, value]) => {
|
||||
imagePromptsMulti.val[key] = [];
|
||||
});
|
||||
|
||||
Object.entries(layerMapping).forEach(([key, value]) => {
|
||||
const positivePoints = [];
|
||||
const middlePoints = [];
|
||||
const negativePoints = [];
|
||||
|
||||
// Positive points
|
||||
for (const { start, end } of value.indices) {
|
||||
const startPoint = landmarks[start];
|
||||
// const endPoint = landmarks[end];
|
||||
|
||||
const startX = startPoint.x * imageSize.val.width;
|
||||
const startY = startPoint.y * imageSize.val.height;
|
||||
|
||||
// const endX = endPoint.x * imageSize.val.width;
|
||||
// const endY = endPoint.y * imageSize.val.height;
|
||||
|
||||
if (middlePoints.length === 0) {
|
||||
middlePoints.push({ x: startX, y: startY, label: 1, isAuto: true });
|
||||
// middlePoints.push({ x: endX, y: endY, label: 1 });
|
||||
} else {
|
||||
middlePoints[0].x += startX;
|
||||
middlePoints[0].y += startY;
|
||||
// middlePoints[1].x += endX;
|
||||
// middlePoints[1].y += endY;
|
||||
}
|
||||
positivePoints.push({ x: startX, y: startY, label: 1, isAuto: true });
|
||||
// positivePoints.push({ x: endX, y: endY, label: 1 });
|
||||
|
||||
// imagePrompts.val = [...imagePrompts.val, { x, y, label: 1 }];
|
||||
}
|
||||
|
||||
// Middle points
|
||||
const len = value.indices.length;
|
||||
middlePoints[0].x /= len;
|
||||
middlePoints[0].y /= len;
|
||||
// middlePoints[1].x /= len;
|
||||
// middlePoints[1].y /= len;
|
||||
|
||||
if (value.useMiddle) {
|
||||
imagePromptsMulti.val[key] = [
|
||||
...imagePromptsMulti.val[key],
|
||||
...middlePoints,
|
||||
];
|
||||
} else {
|
||||
// Negative points
|
||||
for (const [i, { start, end }] of value.indices.entries()) {
|
||||
const startPoint = landmarks[start];
|
||||
// const endPoint = landmarks[end];
|
||||
|
||||
const startX = startPoint.x * imageSize.val.width;
|
||||
const startY = startPoint.y * imageSize.val.height;
|
||||
|
||||
// const endX = endPoint.x * imageSize.val.width;
|
||||
// const endY = endPoint.y * imageSize.val.height;
|
||||
|
||||
const middlePoint = middlePoints[0];
|
||||
const directionVector = {
|
||||
x: middlePoint.x - startX,
|
||||
y: middlePoint.y - startY,
|
||||
};
|
||||
const directionVectorLength = Math.sqrt(
|
||||
directionVector.x * directionVector.x +
|
||||
directionVector.y * directionVector.y
|
||||
);
|
||||
|
||||
if (value.negativeScale !== 0) {
|
||||
const negativePointDistance =
|
||||
value.negativeScale * directionVectorLength;
|
||||
const negativePoint = {
|
||||
x:
|
||||
startX -
|
||||
(negativePointDistance * directionVector.x) /
|
||||
directionVectorLength -
|
||||
value.negativeOffsetX,
|
||||
y:
|
||||
startY -
|
||||
(negativePointDistance * directionVector.y) /
|
||||
directionVectorLength -
|
||||
value.negativeOffsetY,
|
||||
label: 0,
|
||||
isAuto: true,
|
||||
};
|
||||
negativePoints.push(negativePoint);
|
||||
}
|
||||
|
||||
const positivePointDistance =
|
||||
value.positiveScale * directionVectorLength;
|
||||
positivePoints[i] = {
|
||||
x:
|
||||
positivePoints[i].x -
|
||||
(positivePointDistance * directionVector.x) /
|
||||
directionVectorLength -
|
||||
value.positiveOffsetX,
|
||||
y:
|
||||
positivePoints[i].y -
|
||||
(positivePointDistance * directionVector.y) /
|
||||
directionVectorLength -
|
||||
value.positiveOffsetY,
|
||||
label: 1,
|
||||
isAuto: true,
|
||||
};
|
||||
}
|
||||
imagePromptsMulti.val[key] = [
|
||||
...imagePromptsMulti.val[key],
|
||||
...positivePoints,
|
||||
...negativePoints,
|
||||
];
|
||||
}
|
||||
|
||||
// Find bounding box of positive/negative points
|
||||
const points = negativePoints.length > 0 ? negativePoints : positivePoints;
|
||||
const box = {
|
||||
x1: Math.min(...points.map((x) => x.x)),
|
||||
y1: Math.min(...points.map((x) => x.y)),
|
||||
x2: Math.max(...points.map((x) => x.x)),
|
||||
y2: Math.max(...points.map((x) => x.y)),
|
||||
};
|
||||
boxesMulti.val[key] = box;
|
||||
});
|
||||
|
||||
const poseLandmarks = poseLandmarker.detect(image).landmarks[0];
|
||||
const positiveBreathX =
|
||||
((poseLandmarks[11].x + poseLandmarks[12].x) / 2) * imageSize.val.width;
|
||||
const positiveBreathY =
|
||||
((poseLandmarks[11].y + poseLandmarks[12].y) / 2) * imageSize.val.height;
|
||||
const negativeBreathX1 = poseLandmarks[0].x * imageSize.val.width;
|
||||
const negativeBreathY1 = poseLandmarks[0].y * imageSize.val.height;
|
||||
const negativeBreathX2 = poseLandmarks[9].x * imageSize.val.width;
|
||||
const negativeBreathY2 = poseLandmarks[9].y * imageSize.val.height;
|
||||
const negativeBreathX3 = poseLandmarks[10].x * imageSize.val.width;
|
||||
const negativeBreathY3 = poseLandmarks[10].y * imageSize.val.height;
|
||||
|
||||
imagePromptsMulti.val["breath"] = [
|
||||
{ x: positiveBreathX, y: positiveBreathY, label: 1, isAuto: true },
|
||||
{ x: negativeBreathX1, y: negativeBreathY1, label: 0, isAuto: true },
|
||||
{ x: negativeBreathX2, y: negativeBreathY2, label: 0, isAuto: true },
|
||||
{ x: negativeBreathX3, y: negativeBreathY3, label: 0, isAuto: true },
|
||||
];
|
||||
imagePrompts.val = imagePromptsMulti.val[selectedLayer.val];
|
||||
segmented.val = true;
|
||||
console.log("Done");
|
||||
}
|
||||
|
||||
export function setRemoveBackgroundNode() {
|
||||
const rmBgNodes = app.graph.findNodesByType(
|
||||
"Image Rembg (Remove Background)"
|
||||
);
|
||||
if (!rmBgNodes?.length) {
|
||||
alertDialog.val = {
|
||||
text: "Remove background node not found. Please ensure the workflow is correct.",
|
||||
time: 5000,
|
||||
};
|
||||
return;
|
||||
}
|
||||
rmBgNodes.forEach((node) => {
|
||||
// node is bypassed if mode is 4
|
||||
node.mode = enableBackgroundRemover.val ? 0 : 4;
|
||||
});
|
||||
}
|
||||
|
||||
export function updateImagePrompts() {
|
||||
if (selectedLayer.val !== "" && selectedLayer.val !== undefined) {
|
||||
imagePromptsMulti.val = {
|
||||
...imagePromptsMulti.val,
|
||||
[selectedLayer.val]: imagePrompts.val,
|
||||
};
|
||||
|
||||
targetNode.val.widgets.find((x) => x.name === "image_prompts_json").value =
|
||||
JSON.stringify(imagePromptsMulti.val);
|
||||
|
||||
// const canvas = document.getElementById("mask-canvas");
|
||||
// const base64Image = canvas.toDataURL();
|
||||
// api.fetchApi("/segments", {
|
||||
// method: "POST",
|
||||
// body: JSON.stringify({
|
||||
// name: embeddingID.val,
|
||||
// segments: {
|
||||
// [selectedLayer.val]: base64Image,
|
||||
// },
|
||||
// }),
|
||||
// });
|
||||
} else {
|
||||
targetNode.val.widgets.find((x) => x.name === "image_prompts_json").value =
|
||||
JSON.stringify(imagePrompts.val);
|
||||
}
|
||||
targetNode.val.graph.change();
|
||||
}
|
||||
|
||||
export async function uploadSegments() {
|
||||
const emptyLayers = [];
|
||||
Object.entries(imagePromptsMulti.val).forEach(([key, value]) => {
|
||||
if (value.length === 0) {
|
||||
emptyLayers.push(key);
|
||||
}
|
||||
});
|
||||
if (emptyLayers.length > 0) {
|
||||
alertDialog.val = {
|
||||
text: "The following layers have no segments: " + emptyLayers.join(", "),
|
||||
time: 5000,
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
const segments = {};
|
||||
for (const [layer, prompts] of Object.entries(imagePromptsMulti.val)) {
|
||||
await drawSegment(getClicks(prompts), layer, false);
|
||||
const canvas = document.getElementById("mask-canvas");
|
||||
const base64Image = canvas.toDataURL();
|
||||
segments[layer] = base64Image;
|
||||
// download image
|
||||
// const a = document.createElement("a");
|
||||
// a.href = base64Image;
|
||||
// a.download = layer + ".png";
|
||||
// a.click();
|
||||
}
|
||||
await api.fetchApi("/segments", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
name: embeddingID.val,
|
||||
segments,
|
||||
}),
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
async function handleClick(e) {
|
||||
const rect = e.target.getBoundingClientRect();
|
||||
const x = e.clientX - rect.left;
|
||||
const y = e.clientY - rect.top;
|
||||
const relativeX = Math.trunc(
|
||||
((x / e.target.offsetWidth) * imageSize.val.width) / imageSize.val.imgScale
|
||||
);
|
||||
const relativeY = Math.trunc(
|
||||
((y / e.target.offsetHeight) * imageSize.val.height) /
|
||||
imageSize.val.imgScale
|
||||
);
|
||||
|
||||
let label;
|
||||
if (isMobileDevice()) {
|
||||
label = positivePrompt.val ? 1 : 0;
|
||||
} else {
|
||||
label = e.isRight ? 0 : 1;
|
||||
}
|
||||
|
||||
imagePrompts.val = [
|
||||
...imagePrompts.val,
|
||||
{ x: relativeX, y: relativeY, label },
|
||||
];
|
||||
await drawSegment(getClicks());
|
||||
updateImagePrompts();
|
||||
}
|
||||
|
||||
async function handlePointClick(e, point) {
|
||||
e.preventDefault();
|
||||
imagePrompts.val = imagePrompts.val.filter(
|
||||
(x) => !(x.x === point.x && x.y === point.y)
|
||||
);
|
||||
await drawSegment(getClicks());
|
||||
updateImagePrompts();
|
||||
}
|
||||
|
||||
function handleImageSize(image) {
|
||||
// Input images to SAM must be resized so the longest side is 1024
|
||||
const documentHeight = document.documentElement.clientHeight;
|
||||
const LONG_SIDE_LENGTH = 1024;
|
||||
let w = image.naturalWidth;
|
||||
let h = image.naturalHeight;
|
||||
const samScale = LONG_SIDE_LENGTH / Math.max(h, w);
|
||||
const imgScale = documentHeight / Math.max(h, w);
|
||||
return { height: h, width: w, samScale, imgScale };
|
||||
}
|
||||
|
||||
export function getClicks(prompts) {
|
||||
return (prompts || imagePrompts.val).map((point) => ({
|
||||
x: point.x,
|
||||
y: point.y,
|
||||
clickType: point.label,
|
||||
isAuto: point.isAuto,
|
||||
}));
|
||||
}
|
||||
|
||||
export async function drawSegment(clicks, layer, drawBox = true) {
|
||||
const canvas = document.getElementById("mask-canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (clicks.length === 0) {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
return;
|
||||
}
|
||||
if (embeddings.val) {
|
||||
const box = enableAutoSegment.val
|
||||
? boxesMulti.val[layer || selectedLayer.val]
|
||||
: null;
|
||||
const filteredClicks = enableAutoSegment.val
|
||||
? clicks
|
||||
: clicks.filter((click) => !click.isAuto);
|
||||
if (filteredClicks.length === 0) {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
return;
|
||||
}
|
||||
const mask = await runONNX(filteredClicks, embeddings.val, box);
|
||||
if (mask) {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.drawImage(mask, 0, 0);
|
||||
if (box && drawBox) {
|
||||
ctx.strokeStyle = "green";
|
||||
ctx.lineWidth = 5;
|
||||
ctx.strokeRect(box.x1, box.y1, box.x2 - box.x1, box.y2 - box.y1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function LayerEditor() {
|
||||
let realTimeSegment = true;
|
||||
|
||||
const showSidebar = van.state(true);
|
||||
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (showImageEditor.val && e.code === "Tab") {
|
||||
e.preventDefault();
|
||||
realTimeSegment = !realTimeSegment;
|
||||
if (!realTimeSegment) {
|
||||
drawSegment(getClicks());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return div(
|
||||
{
|
||||
class: () =>
|
||||
"absolute flex bg-gray-900 bg-opacity-50 top-0 w-full h-full pointer-events-auto z-[1000] " +
|
||||
(showImageEditor.val ? "" : "hidden"),
|
||||
},
|
||||
div(
|
||||
{
|
||||
class:
|
||||
"absolute top-4 left-4 right-0 flex w-full gap-2 justify-start z-[200]",
|
||||
},
|
||||
button(
|
||||
{
|
||||
class: () => "btn btn-neutral flex flex-row normal-case rounded-md",
|
||||
onclick: async () => {
|
||||
console.log("close");
|
||||
showImageEditor.val = false;
|
||||
await uploadSegments();
|
||||
|
||||
const isEqual = allImagePrompts.val.map(
|
||||
(x) =>
|
||||
JSON.stringify(imagePromptsMulti.val) ===
|
||||
JSON.stringify(x.prompt)
|
||||
);
|
||||
if (!isEqual.includes(true))
|
||||
allImagePrompts.val = [
|
||||
...allImagePrompts.val,
|
||||
{
|
||||
version: "v" + allImagePrompts.val.length,
|
||||
prompt: imagePromptsMulti.val,
|
||||
},
|
||||
];
|
||||
|
||||
// api.fetchApi("/segments_order", {
|
||||
// method: "POST",
|
||||
// body: JSON.stringify({
|
||||
// name: embeddingID.val,
|
||||
// order: Object.keys(imagePromptsMulti.val),
|
||||
// }),
|
||||
// });
|
||||
},
|
||||
},
|
||||
span({
|
||||
class: "iconify text-lg",
|
||||
"data-icon": "ic:baseline-arrow-back",
|
||||
"data-inline": "false",
|
||||
}),
|
||||
div("Back")
|
||||
),
|
||||
button(
|
||||
{
|
||||
class: () => "btn btn-neutral flex flex-row normal-case rounded-md",
|
||||
onclick: () => (showSidebar.val = !showSidebar.val),
|
||||
},
|
||||
div(() => (showSidebar.val ? "Hide UI" : "Show UI"))
|
||||
),
|
||||
button(
|
||||
{
|
||||
class: () => "btn btn-neutral flex flex-row normal-case rounded-md",
|
||||
onclick: () => {
|
||||
enableAutoSegment.val = !enableAutoSegment.val;
|
||||
drawSegment(getClicks());
|
||||
},
|
||||
},
|
||||
() => (enableAutoSegment.val ? "Auto Segment On" : "Auto Segment Off")
|
||||
),
|
||||
button(
|
||||
{
|
||||
class: () => "btn btn-neutral flex flex-row normal-case rounded-md",
|
||||
onclick: () => {
|
||||
enableBackgroundRemover.val = !enableBackgroundRemover.val;
|
||||
setRemoveBackgroundNode();
|
||||
},
|
||||
},
|
||||
() =>
|
||||
enableBackgroundRemover.val
|
||||
? "Background Remover On"
|
||||
: "Background Remover Off"
|
||||
),
|
||||
button(
|
||||
{
|
||||
class: () =>
|
||||
`btn btn-neutral flex flex-row normal-case rounded-md ${
|
||||
isMobileDevice() ? "" : "hidden"
|
||||
}`,
|
||||
onclick: () => (positivePrompt.val = !positivePrompt.val),
|
||||
},
|
||||
div(() => (positivePrompt.val ? "Positive" : "Negative"))
|
||||
)
|
||||
),
|
||||
div(
|
||||
{
|
||||
class:
|
||||
"hidden w-full justify-center absolute top-0 left-0 right-0 items-center",
|
||||
},
|
||||
button(
|
||||
{
|
||||
class: () => " px-4 py-2 rounded-md left-0 top-0 z-[200] ",
|
||||
onclick: () => {
|
||||
point_label.val = 1;
|
||||
},
|
||||
},
|
||||
"Positive"
|
||||
),
|
||||
button(
|
||||
{
|
||||
class: () => " px-4 py-2 rounded-md left-0 top-0 z-[200] ",
|
||||
onclick: () => {
|
||||
point_label.val = 0;
|
||||
},
|
||||
},
|
||||
"Negative"
|
||||
)
|
||||
),
|
||||
div(
|
||||
{
|
||||
class: "flex items-center justify-center w-full h-full",
|
||||
id: "image-container",
|
||||
},
|
||||
img({
|
||||
id: "image",
|
||||
class:
|
||||
"fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2",
|
||||
src: imageUrl,
|
||||
onload: async (e) => {
|
||||
imageSize.val = handleImageSize(e.target);
|
||||
|
||||
document.getElementById("image-container").style.scale =
|
||||
imageSize.val.imgScale;
|
||||
|
||||
imageContainerSize.val = {
|
||||
width: e.target.offsetWidth,
|
||||
height: e.target.offsetHeight,
|
||||
};
|
||||
|
||||
const canvas = document.getElementById("mask-canvas");
|
||||
canvas.width = e.target.naturalWidth;
|
||||
canvas.height = e.target.naturalHeight;
|
||||
},
|
||||
oncontextmenu: async (e) => {
|
||||
e.preventDefault();
|
||||
e.isRight = true;
|
||||
await handleClick(e);
|
||||
},
|
||||
onclick: async (e) => {
|
||||
await handleClick(e);
|
||||
},
|
||||
onmouseleave: (e) => {
|
||||
drawSegment(getClicks());
|
||||
},
|
||||
onmousemove: (e) => {
|
||||
if (!throttle) {
|
||||
throttle = true;
|
||||
setTimeout(() => {
|
||||
throttle = false;
|
||||
|
||||
if (embeddings.val && realTimeSegment) {
|
||||
const rect = e.target.getBoundingClientRect();
|
||||
const x = e.clientX - rect.left;
|
||||
const y = e.clientY - rect.top;
|
||||
const relativeX = Math.trunc(
|
||||
((x / e.target.offsetWidth) * imageSize.val.width) /
|
||||
imageSize.val.imgScale
|
||||
);
|
||||
const relativeY = Math.trunc(
|
||||
((y / e.target.offsetHeight) * imageSize.val.height) /
|
||||
imageSize.val.imgScale
|
||||
);
|
||||
|
||||
const clicks = [
|
||||
...getClicks(),
|
||||
{ x: relativeX, y: relativeY, clickType: 1 },
|
||||
];
|
||||
drawSegment(clicks);
|
||||
}
|
||||
}, 10);
|
||||
}
|
||||
},
|
||||
}),
|
||||
() =>
|
||||
canvas({
|
||||
class:
|
||||
"pointer-events-none fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 opacity-80",
|
||||
style: () =>
|
||||
`width: ${imageContainerSize.val.width}px; height: ${imageContainerSize.val.height}px;`,
|
||||
id: "mask-canvas",
|
||||
}),
|
||||
() =>
|
||||
div(
|
||||
{
|
||||
class: "absolute w-full h-full pointer-events-none",
|
||||
style: () =>
|
||||
`width: ${imageContainerSize.val.width}px; height: ${imageContainerSize.val.height}px;`,
|
||||
},
|
||||
...(enableAutoSegment.val
|
||||
? imagePrompts.val
|
||||
: imagePrompts.val?.filter((click) => !click.isAuto)
|
||||
).map((point) => {
|
||||
return button({
|
||||
style: () =>
|
||||
`left: ${
|
||||
(point.x / imageSize.val.width) * imageContainerSize.val.width
|
||||
}px; top: ${
|
||||
(point.y / imageSize.val.height) *
|
||||
imageContainerSize.val.height
|
||||
}px; transform: translate(-50%, -50%);`,
|
||||
class: () =>
|
||||
`absolute w-3 h-3 p-0 rounded-full pointer-events-auto ${
|
||||
point.label === 1 ? "bg-green-500" : "bg-red-500"
|
||||
}`,
|
||||
|
||||
oncontextmenu: async (e) => {
|
||||
await handlePointClick(e, point);
|
||||
},
|
||||
onclick: async (e) => {
|
||||
await handlePointClick(e, point);
|
||||
},
|
||||
});
|
||||
})
|
||||
)
|
||||
),
|
||||
() => (showSidebar.val ? SideBar() : div())
|
||||
);
|
||||
}
|
||||
+5
-5
@@ -1,17 +1,17 @@
|
||||
import { showLoading } from "./state.js";
|
||||
import { loadingCaption, showLoading } from "./state.js";
|
||||
import { van } from "./van.js";
|
||||
const { div, span } = van.tags;
|
||||
|
||||
export function Loading() {
|
||||
console.log("???", showLoading.val);
|
||||
return div(
|
||||
{
|
||||
class: () =>
|
||||
"absolute flex justify-center items-center top-0 left-0 bg-gray-900 bg-opacity-50 pointer-events-auto w-full h-full " +
|
||||
"absolute flex flex-col justify-center items-center top-0 left-0 bg-gray-900 bg-opacity-50 pointer-events-auto w-full h-full z-[1001] " +
|
||||
(showLoading.val ? "" : "hidden"),
|
||||
},
|
||||
span({
|
||||
class: "loading loading-spinner loading-lg",
|
||||
})
|
||||
class: "loading loading-spinner loading-lg mb-2",
|
||||
}),
|
||||
() => span(loadingCaption.val)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import { iframeSrc, showEditor } from "./state.js";
|
||||
import { van } from "./van.js";
|
||||
const { button, iframe, div, img, span, dialog, form } = van.tags;
|
||||
|
||||
export function ShapeFlowEditor() {
|
||||
return div(
|
||||
{
|
||||
class: "w-full h-full relative",
|
||||
},
|
||||
button(
|
||||
{
|
||||
class: () =>
|
||||
"ml-2 mt-2 btn btn-circle flex flex-row btn-ghost normal-case absolute px-4 rounded-md left-0 top-0 z-[200] w-fit pointer-events-auto bg-black " +
|
||||
(showEditor.val ? "" : "hidden"),
|
||||
onclick: () => {
|
||||
console.log("close");
|
||||
document.getElementById("alert-dialog").showModal();
|
||||
},
|
||||
},
|
||||
span({
|
||||
class: "iconify text-lg",
|
||||
"data-icon": "ic:baseline-arrow-back",
|
||||
"data-inline": "false",
|
||||
}),
|
||||
div("Back & Save")
|
||||
),
|
||||
iframe({
|
||||
id: "avatech-editor-iframe",
|
||||
title: "avatech-editor-iframe",
|
||||
name: "avatech-editor-iframe",
|
||||
allow: "cross-origin-isolated; clipboard-read; clipboard-write",
|
||||
class: () =>
|
||||
"w-full h-full pointer-events-auto " + (showEditor.val ? "" : "hidden"),
|
||||
src: iframeSrc,
|
||||
}),
|
||||
dialog(
|
||||
{
|
||||
id: "alert-dialog",
|
||||
class: "modal modal-bottom sm:modal-middle",
|
||||
},
|
||||
div(
|
||||
{
|
||||
class: "modal-box",
|
||||
},
|
||||
div({ class: "text-black" }, div({ class: "text-xl font-bold" }, "Shape Flow Editor"), div({ class: "" }, "The shape flow will be save in CreateShapeFlow node (comfyui node)! Or discard the changes!")),
|
||||
div(
|
||||
{ class: "modal-action" },
|
||||
form(
|
||||
{ method: "dialog", class: "flex flex-row gap-4" },
|
||||
button(
|
||||
{
|
||||
class: "btn",
|
||||
onclick: () => {
|
||||
showEditor.val = false;
|
||||
},
|
||||
},
|
||||
"Discard"
|
||||
),
|
||||
button(
|
||||
{
|
||||
class: "btn",
|
||||
onclick: () => {
|
||||
const editor = document.getElementById(
|
||||
"avatech-editor-iframe"
|
||||
);
|
||||
editor.contentWindow.postMessage(
|
||||
{
|
||||
method: "back",
|
||||
},
|
||||
"*"
|
||||
);
|
||||
showEditor.val = false;
|
||||
},
|
||||
},
|
||||
"Save & Close"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
+250
-179
@@ -1,9 +1,11 @@
|
||||
import { drawSegment, getClicks, updateImagePrompts } from "./ImageEditor.js";
|
||||
import { drawSegment, getClicks, updateImagePrompts } from "./LayerEditor.js";
|
||||
import {
|
||||
imagePrompts,
|
||||
selectedLayer,
|
||||
imagePromptsMulti,
|
||||
targetNode,
|
||||
showImageEditor,
|
||||
allImagePrompts,
|
||||
} from "./state.js";
|
||||
import { van } from "./van.js";
|
||||
const {
|
||||
@@ -22,201 +24,270 @@ const {
|
||||
span,
|
||||
} = van.tags;
|
||||
|
||||
van.derive(() => {
|
||||
if (
|
||||
showImageEditor.val &&
|
||||
targetNode.val != undefined &&
|
||||
targetNode.val.outputs &&
|
||||
targetNode.val.type === "SAM MultiLayer"
|
||||
) {
|
||||
const outputNames = targetNode.val.outputs.map((x) => x.name).slice(1);
|
||||
const record = Object.keys(imagePromptsMulti.val);
|
||||
|
||||
const diff = outputNames.filter((x) => !record.includes(x));
|
||||
const missingDiff = record.filter((x) => !outputNames.includes(x));
|
||||
|
||||
if (diff.length > 0) {
|
||||
console.log("Cleaning up missing output slots", diff);
|
||||
diff.forEach((x) => {
|
||||
targetNode.val.removeOutput(targetNode.val.findOutputSlot(x));
|
||||
});
|
||||
targetNode.val.graph.change();
|
||||
}
|
||||
|
||||
if (missingDiff.length > 0) {
|
||||
console.log("Adding missing output slots", diff);
|
||||
missingDiff.forEach((x) => {
|
||||
targetNode.val.addOutput(
|
||||
x,
|
||||
targetNode.val.type === "SAM MultiLayer" ? "IMAGE" : "SAM_PROMPT"
|
||||
);
|
||||
});
|
||||
targetNode.val.graph.change();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export function SideBar() {
|
||||
const layer_to_delete = van.state("");
|
||||
|
||||
return div(
|
||||
{
|
||||
class:
|
||||
"ml-2 z-100 w-fit flex-col flex justify-center absolute top-0 left-0 bottom-0 items-start gap-2",
|
||||
},
|
||||
|
||||
() => {
|
||||
const layers = Object.entries(imagePromptsMulti.val);
|
||||
return ul(
|
||||
{
|
||||
class: "menu bg-base-200 w-56 rounded-box text-base-content ",
|
||||
},
|
||||
layers.length === 0 ? li(a("Empty layer")) : null,
|
||||
...layers.map(([key, value]) => {
|
||||
return li(
|
||||
a(
|
||||
{
|
||||
class: () =>
|
||||
`normal-case text-start items-start flex items-center justify-between ${
|
||||
selectedLayer.val === key ? "active" : ""
|
||||
}`,
|
||||
onclick: () => {
|
||||
selectedLayer.val = key;
|
||||
imagePrompts.val = imagePromptsMulti.val[key];
|
||||
drawSegment(getClicks());
|
||||
},
|
||||
},
|
||||
key,
|
||||
div(
|
||||
{},
|
||||
button(
|
||||
{
|
||||
class:
|
||||
"btn btn-circle btn-xs btn-ghost group hover:text-red-500",
|
||||
onclick: (e) => {
|
||||
console.log("clear");
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
imagePrompts.val = [];
|
||||
imagePromptsMulti.val[key] = [];
|
||||
drawSegment([]);
|
||||
updateImagePrompts();
|
||||
},
|
||||
},
|
||||
span({
|
||||
class: "iconify",
|
||||
"data-icon": "ant-design:clear-outlined",
|
||||
"data-inline": "false",
|
||||
})
|
||||
),
|
||||
button(
|
||||
{
|
||||
class:
|
||||
"btn btn-circle btn-xs btn-ghost group hover:text-red-500",
|
||||
onclick: (e) => {
|
||||
console.log("delete");
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
layer_to_delete.val = key;
|
||||
setTimeout(() => {
|
||||
delete_layer_dialog.showModal();
|
||||
}, 0);
|
||||
},
|
||||
},
|
||||
span({
|
||||
class: "iconify",
|
||||
"data-icon": "ic:baseline-delete",
|
||||
"data-inline": "false",
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}),
|
||||
div({ class: "divider !py-0 my-0" }),
|
||||
li(
|
||||
a(
|
||||
{
|
||||
class: "flex items-center justify-between",
|
||||
onclick: () => {
|
||||
my_modal_3.showModal();
|
||||
},
|
||||
},
|
||||
"New Layer",
|
||||
span({
|
||||
class: "iconify",
|
||||
"data-icon": "ic:outline-plus",
|
||||
"data-inline": "false",
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
},
|
||||
() =>
|
||||
ConfirmDialog(
|
||||
{
|
||||
id: "delete_layer_dialog",
|
||||
title: "Delete Layer: " + layer_to_delete.val,
|
||||
onsubmit: () => {
|
||||
imagePromptsMulti.val = Object.fromEntries(
|
||||
Object.entries(imagePromptsMulti.val).filter(
|
||||
([key, value]) => key !== layer_to_delete.val
|
||||
)
|
||||
);
|
||||
console.log(imagePromptsMulti.val);
|
||||
if (selectedLayer.val === layer_to_delete.val) {
|
||||
// Select another layer if there is one
|
||||
if (Object.keys(imagePromptsMulti.val).length > 0) {
|
||||
selectedLayer.val = Object.keys(imagePromptsMulti.val)[0];
|
||||
imagePrompts.val = imagePromptsMulti.val[selectedLayer.val];
|
||||
} else {
|
||||
selectedLayer.val = "";
|
||||
imagePrompts.val = [];
|
||||
}
|
||||
}
|
||||
const outputIndex = targetNode.val.findOutputSlot(
|
||||
layer_to_delete.val
|
||||
);
|
||||
targetNode.val.removeOutput(outputIndex);
|
||||
targetNode.val.graph.change();
|
||||
updateImagePrompts();
|
||||
delete_layer_dialog.close();
|
||||
div(
|
||||
{
|
||||
class:
|
||||
"ml-2 z-100 w-fit flex-col flex justify-center absolute top-0 left-0 bottom-0 items-start gap-2",
|
||||
},
|
||||
() => {
|
||||
const layers = Object.entries(imagePromptsMulti.val);
|
||||
return ul(
|
||||
{
|
||||
class: "menu bg-base-200 w-56 rounded-box text-base-content ",
|
||||
},
|
||||
},
|
||||
p("Are you sure you want to delete this layer?")
|
||||
),
|
||||
() =>
|
||||
dialog(
|
||||
{ id: "my_modal_3", class: "modal" },
|
||||
div(
|
||||
{ class: "modal-box text-base-content" },
|
||||
form(
|
||||
button(
|
||||
{
|
||||
class: "gap-2 flex flex-col",
|
||||
method: "dialog",
|
||||
onsubmit: (e) => {
|
||||
console.log("add new layer");
|
||||
e.preventDefault();
|
||||
const inputText = e.target.elements[1].value;
|
||||
imagePromptsMulti.val = {
|
||||
...imagePromptsMulti.val,
|
||||
[inputText]: [],
|
||||
};
|
||||
console.log(inputText, imagePromptsMulti.val);
|
||||
my_modal_3.close();
|
||||
e.target.elements[1].value = "";
|
||||
targetNode.val.addOutput(
|
||||
inputText,
|
||||
targetNode.val.type === "SAM" ? "IMAGE" : "SAM_PROMPT"
|
||||
);
|
||||
targetNode.val.graph.change();
|
||||
if (
|
||||
selectedLayer.val === null ||
|
||||
selectedLayer.val === undefined ||
|
||||
selectedLayer.val === ""
|
||||
) {
|
||||
selectedLayer.val = inputText;
|
||||
}
|
||||
onclick: () => {
|
||||
layers.map(([key, value]) => {
|
||||
imagePrompts.val = [];
|
||||
imagePromptsMulti.val[key] = [];
|
||||
});
|
||||
drawSegment([]);
|
||||
updateImagePrompts();
|
||||
},
|
||||
class: "btn btn-ghost normal-case flex",
|
||||
},
|
||||
button(
|
||||
"Clear ALL"
|
||||
),
|
||||
layers.length === 0 ? li(a("Empty layer")) : null,
|
||||
...layers.map(([key, value]) => {
|
||||
return li(
|
||||
a(
|
||||
{
|
||||
class: () =>
|
||||
`normal-case text-start items-start flex items-center justify-between ${
|
||||
selectedLayer.val === key ? "active" : ""
|
||||
}`,
|
||||
onclick: () => {
|
||||
selectedLayer.val = key;
|
||||
imagePrompts.val = imagePromptsMulti.val[key];
|
||||
drawSegment(getClicks());
|
||||
},
|
||||
},
|
||||
key,
|
||||
div(
|
||||
{},
|
||||
button(
|
||||
{
|
||||
class:
|
||||
"btn btn-circle btn-xs btn-ghost group hover:text-red-500",
|
||||
onclick: (e) => {
|
||||
console.log("clear");
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
imagePrompts.val = [];
|
||||
imagePromptsMulti.val[key] = [];
|
||||
drawSegment([]);
|
||||
updateImagePrompts();
|
||||
},
|
||||
},
|
||||
span({
|
||||
class: "iconify",
|
||||
"data-icon": "ant-design:clear-outlined",
|
||||
"data-inline": "false",
|
||||
})
|
||||
),
|
||||
button(
|
||||
{
|
||||
class:
|
||||
"btn btn-circle btn-xs btn-ghost group hover:text-red-500",
|
||||
onclick: (e) => {
|
||||
console.log("delete");
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
layer_to_delete.val = key;
|
||||
setTimeout(() => {
|
||||
delete_layer_dialog.showModal();
|
||||
}, 0);
|
||||
},
|
||||
},
|
||||
span({
|
||||
class: "iconify",
|
||||
"data-icon": "ic:baseline-delete",
|
||||
"data-inline": "false",
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}),
|
||||
div({ class: "divider !py-0 my-0" }),
|
||||
li(
|
||||
a(
|
||||
{
|
||||
type: "button",
|
||||
class: "btn btn-sm btn-circle btn-ghost absolute right-2 top-2",
|
||||
onclick: (e) => {
|
||||
e.stopPropagation();
|
||||
my_modal_3.close();
|
||||
class: "flex items-center justify-between",
|
||||
onclick: () => {
|
||||
my_modal_3.showModal();
|
||||
},
|
||||
},
|
||||
"✕"
|
||||
),
|
||||
h3(
|
||||
{ class: "font-bold text-lg text-base-content" },
|
||||
"Add new layer!"
|
||||
),
|
||||
input({
|
||||
type: "text",
|
||||
placeholder: "Type here",
|
||||
class: "input input-bordered w-full",
|
||||
autofocus: true,
|
||||
}),
|
||||
button(
|
||||
"New Layer",
|
||||
span({
|
||||
class: "iconify",
|
||||
"data-icon": "ic:outline-plus",
|
||||
"data-inline": "false",
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
},
|
||||
() =>
|
||||
ConfirmDialog(
|
||||
{
|
||||
id: "delete_layer_dialog",
|
||||
title: "Delete Layer: " + layer_to_delete.val,
|
||||
onsubmit: () => {
|
||||
imagePromptsMulti.val = Object.fromEntries(
|
||||
Object.entries(imagePromptsMulti.val).filter(
|
||||
([key, value]) => key !== layer_to_delete.val
|
||||
)
|
||||
);
|
||||
console.log(imagePromptsMulti.val);
|
||||
if (selectedLayer.val === layer_to_delete.val) {
|
||||
// Select another layer if there is one
|
||||
if (Object.keys(imagePromptsMulti.val).length > 0) {
|
||||
selectedLayer.val = Object.keys(imagePromptsMulti.val)[0];
|
||||
imagePrompts.val = imagePromptsMulti.val[selectedLayer.val];
|
||||
} else {
|
||||
selectedLayer.val = "";
|
||||
imagePrompts.val = [];
|
||||
}
|
||||
}
|
||||
targetNode.val.graph.change();
|
||||
updateImagePrompts();
|
||||
delete_layer_dialog.close();
|
||||
},
|
||||
},
|
||||
p("Are you sure you want to delete this layer?")
|
||||
),
|
||||
() =>
|
||||
dialog(
|
||||
{ id: "my_modal_3", class: "modal" },
|
||||
div(
|
||||
{ class: "modal-box text-base-content" },
|
||||
form(
|
||||
{
|
||||
type: "submit",
|
||||
class: "btn btn-sm btn-ghost place-self-end",
|
||||
class: "gap-2 flex flex-col",
|
||||
method: "dialog",
|
||||
onsubmit: (e) => {
|
||||
console.log("add new layer");
|
||||
e.preventDefault();
|
||||
const inputText = e.target.elements[1].value;
|
||||
imagePromptsMulti.val = {
|
||||
...imagePromptsMulti.val,
|
||||
[inputText]: [],
|
||||
};
|
||||
console.log(inputText, imagePromptsMulti.val);
|
||||
my_modal_3.close();
|
||||
e.target.elements[1].value = "";
|
||||
|
||||
selectedLayer.val = inputText;
|
||||
imagePrompts.val = imagePromptsMulti.val[inputText];
|
||||
drawSegment(getClicks());
|
||||
updateImagePrompts();
|
||||
},
|
||||
},
|
||||
"Confirm"
|
||||
button(
|
||||
{
|
||||
type: "button",
|
||||
class:
|
||||
"btn btn-sm btn-circle btn-ghost absolute right-2 top-2",
|
||||
onclick: (e) => {
|
||||
e.stopPropagation();
|
||||
my_modal_3.close();
|
||||
},
|
||||
},
|
||||
"✕"
|
||||
),
|
||||
h3(
|
||||
{ class: "font-bold text-lg text-base-content" },
|
||||
"Add new layer!"
|
||||
),
|
||||
input({
|
||||
type: "text",
|
||||
placeholder: "Type here",
|
||||
class: "input input-bordered w-full",
|
||||
autofocus: true,
|
||||
}),
|
||||
button(
|
||||
{
|
||||
type: "submit",
|
||||
class: "btn btn-sm btn-ghost place-self-end",
|
||||
},
|
||||
"Confirm"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
div(
|
||||
{
|
||||
class:
|
||||
"ml-2 z-100 w-fit flex-col flex justify-center absolute top-0 right-0 bottom-0 items-start gap-2 bg-transparent",
|
||||
},
|
||||
() => {
|
||||
return ul(
|
||||
{
|
||||
class: "menu bg-base-200 w-56 rounded-box text-base-content ",
|
||||
},
|
||||
span("Segment History"),
|
||||
...allImagePrompts.val.map((e) =>
|
||||
li(
|
||||
a(
|
||||
{
|
||||
class:
|
||||
"normal-case text-start flex items-center justify-between",
|
||||
onclick: async () => {
|
||||
imagePromptsMulti.val = e.prompt;
|
||||
imagePrompts.val = imagePromptsMulti.val[selectedLayer.val];
|
||||
drawSegment(getClicks());
|
||||
updateImagePrompts();
|
||||
},
|
||||
},
|
||||
e.version
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { ComfyDialog, $el } from '../../scripts/ui.js';
|
||||
|
||||
export class InfoDialog extends ComfyDialog {
|
||||
constructor() {
|
||||
super();
|
||||
this.element.classList.add("comfy-normal-modal");
|
||||
}
|
||||
createButtons() {
|
||||
return [
|
||||
$el("button", {
|
||||
type: "button",
|
||||
textContent: "Close",
|
||||
onclick: () => this.close(),
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
close() {
|
||||
this.element.style.display = "none";
|
||||
}
|
||||
|
||||
show(html) {
|
||||
if (typeof html === "string") {
|
||||
this.textElement.innerHTML = html;
|
||||
} else {
|
||||
this.textElement.replaceChildren(html);
|
||||
}
|
||||
this.element.style.display = "flex";
|
||||
this.element.style.zIndex = 1001;
|
||||
}
|
||||
}
|
||||
|
||||
export const infoDialog = new InfoDialog()
|
||||
+402
-103
@@ -10,14 +10,37 @@ import {
|
||||
imagePromptsMulti,
|
||||
selectedLayer,
|
||||
showLoading,
|
||||
loadingCaption,
|
||||
alertDialog,
|
||||
showPreview,
|
||||
shareLoading,
|
||||
previewModelId,
|
||||
embeddingID,
|
||||
enableAutoSegment,
|
||||
} from "./state.js";
|
||||
import { van } from "./van.js";
|
||||
import { app } from "./app.js";
|
||||
import { api } from "./api.js";
|
||||
import { Container } from "./Container.js";
|
||||
import { loadNpyTensor } from "./onnx.js";
|
||||
import { initModel, loadNpyTensor } from "./onnx.js";
|
||||
import "https://code.iconify.design/3/3.1.0/iconify.min.js";
|
||||
import { drawSegment, getClicks } from "./ImageEditor.js";
|
||||
import {
|
||||
autoSegment,
|
||||
drawSegment,
|
||||
getClicks,
|
||||
segmented,
|
||||
} from "./LayerEditor.js";
|
||||
import { infoDialog } from "./dialog.js";
|
||||
import { sharedAvatarLink } from "./AvatarPreview.js";
|
||||
import { updateImagePrompts } from "./LayerEditor.js";
|
||||
|
||||
export const generatedImages = {};
|
||||
|
||||
const stylesheet = document.createElement("link");
|
||||
stylesheet.setAttribute("type", "text/css");
|
||||
stylesheet.setAttribute("rel", "stylesheet");
|
||||
stylesheet.setAttribute("href", "./avatar-graph-comfyui/tw-styles.css");
|
||||
document.head.appendChild(stylesheet);
|
||||
|
||||
/** @type {import( '../../../web/types/litegraph.js').LGraphGroup} */
|
||||
const recomputeInsideNodesOps = LGraphGroup.prototype.recomputeInsideNodes;
|
||||
@@ -199,7 +222,7 @@ function openInAvatechEditor(url, fileName) {
|
||||
key: "key",
|
||||
value: fileName,
|
||||
method: "store",
|
||||
blendshapes: targetNode.val.widgets.find((x) => x.name === "blendshapes")
|
||||
blendshapes: targetNode.val.widgets.find((x) => x.name === "shape_flow")
|
||||
.value,
|
||||
},
|
||||
"*"
|
||||
@@ -207,7 +230,7 @@ function openInAvatechEditor(url, fileName) {
|
||||
}
|
||||
|
||||
function updateBlendshapesPrompts(value) {
|
||||
targetNode.val.widgets.find((x) => x.name === "blendshapes").value = value;
|
||||
targetNode.val.widgets.find((x) => x.name === "shape_flow").value = value;
|
||||
targetNode.val.graph.change();
|
||||
}
|
||||
|
||||
@@ -223,15 +246,35 @@ function getInputWidgetValue(node, inputIndex, widgetName) {
|
||||
/** @type {LGraphNode} */
|
||||
let nodea = graph._nodes_by_id[targetLink.origin_id];
|
||||
|
||||
while (nodea.type == "Reroute") {
|
||||
while (nodea.type === "Reroute") {
|
||||
nodea = nodea.getInputNode(0);
|
||||
}
|
||||
|
||||
console.log(targetLink, nodea);
|
||||
console.log(nodea.getInputNode(0, true));
|
||||
|
||||
if (nodea.type === "LoadImage") {
|
||||
/** @type {string} */
|
||||
const isGeneratedImage = false;
|
||||
return [
|
||||
isGeneratedImage,
|
||||
nodea.widgets.find((x) => x.name === widgetName).value,
|
||||
];
|
||||
}
|
||||
|
||||
const saveImageNodeLink = nodea.outputs
|
||||
.find((x) => x.type === "IMAGE")
|
||||
.links.find((link) => {
|
||||
const targetLink = graph.links[link];
|
||||
const targetNode = graph._nodes_by_id[targetLink.target_id];
|
||||
if (targetNode.type === "SaveImage") {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
const saveImageNode =
|
||||
graph._nodes_by_id[graph.links[saveImageNodeLink].target_id];
|
||||
/** @type {string} */
|
||||
return nodea.widgets.find((x) => x.name === widgetName).value;
|
||||
const isGeneratedImage = true;
|
||||
return [isGeneratedImage, generatedImages[saveImageNode.id]];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -239,45 +282,88 @@ function getInputWidgetValue(node, inputIndex, widgetName) {
|
||||
* @param {LGraphNode} node
|
||||
*/
|
||||
function showMyImageEditor(node) {
|
||||
let connectedImageFileName = getInputWidgetValue(node, 0, "image");
|
||||
const split = connectedImageFileName.split("/");
|
||||
if (split.length > 1) connectedImageFileName = split[1];
|
||||
|
||||
const embeddingFilename = node.widgets.find(
|
||||
(x) => x.name === "embedding_id"
|
||||
).value;
|
||||
|
||||
const v = JSON.parse(
|
||||
node.widgets.find((x) => x.name === "image_prompts_json").value
|
||||
let [isGeneratedImage, connectedImageFileName] = getInputWidgetValue(
|
||||
node,
|
||||
0,
|
||||
"image"
|
||||
);
|
||||
|
||||
if (!Array.isArray(v)) {
|
||||
// this is a multi prompt
|
||||
imagePromptsMulti.val = v;
|
||||
selectedLayer.val = Object.keys(imagePromptsMulti.val)[0];
|
||||
imagePrompts.val = imagePromptsMulti.val[selectedLayer.val];
|
||||
} else {
|
||||
// this is a single prompt
|
||||
selectedLayer.val = "";
|
||||
imagePromptsMulti.val = {};
|
||||
imagePrompts.val = v;
|
||||
if (!connectedImageFileName) {
|
||||
alertDialog.val = {
|
||||
text: "Please connect or generate an image first",
|
||||
time: 3000,
|
||||
};
|
||||
return;
|
||||
}
|
||||
showImageEditor.val = true;
|
||||
imageUrl.val = api.apiURL(
|
||||
`/view?filename=${encodeURIComponent(
|
||||
connectedImageFileName
|
||||
)}&type=input&subfolder=${split.length > 1 ? split[0] : ""}`
|
||||
);
|
||||
const embeedingUrl = api.apiURL(
|
||||
`/view?filename=${encodeURIComponent(
|
||||
`${embeddingFilename}.npy`
|
||||
)}&type=output&subfolder=`
|
||||
);
|
||||
loadNpyTensor(embeedingUrl).then((tensor) => {
|
||||
embeddings.val = tensor;
|
||||
drawSegment(getClicks());
|
||||
|
||||
loadingCaption.val = "Loading SAM model...";
|
||||
showLoading.val = true;
|
||||
|
||||
const ckpt = node.widgets.find((x) => x.name === "ckpt").value;
|
||||
const modelType = ckpt.match(/vit_[lbh]/)?.[0];
|
||||
initModel(modelType).then((res) => {
|
||||
loadingCaption.val = "Computing image embedding...";
|
||||
|
||||
const split = connectedImageFileName.split("/");
|
||||
let id = connectedImageFileName;
|
||||
if (split.length > 1) id = split[1];
|
||||
|
||||
node.widgets.find((x) => x.name === "embedding_id").value = id;
|
||||
embeddingID.val = id;
|
||||
|
||||
api
|
||||
.fetchApi("/sam_model", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
image: connectedImageFileName,
|
||||
isGeneratedImage,
|
||||
embedding_id: id,
|
||||
ckpt,
|
||||
// remote: true,
|
||||
}),
|
||||
})
|
||||
.then(() => {
|
||||
showLoading.val = false;
|
||||
const v = JSON.parse(
|
||||
node.widgets.find((x) => x.name === "image_prompts_json").value
|
||||
);
|
||||
|
||||
if (!Array.isArray(v)) {
|
||||
// this is a multi prompt
|
||||
imagePromptsMulti.val = v;
|
||||
selectedLayer.val = Object.keys(imagePromptsMulti.val)[0];
|
||||
imagePrompts.val = imagePromptsMulti.val[selectedLayer.val];
|
||||
} else {
|
||||
// this is a single prompt
|
||||
selectedLayer.val = "";
|
||||
imagePromptsMulti.val = {};
|
||||
imagePrompts.val = v;
|
||||
}
|
||||
showImageEditor.val = true;
|
||||
const subfolder =
|
||||
isGeneratedImage || split.length === 1 ? "" : split[0];
|
||||
imageUrl.val = api.apiURL(
|
||||
`/view?filename=${encodeURIComponent(connectedImageFileName)}&type=${
|
||||
isGeneratedImage ? "output" : "input"
|
||||
}&subfolder=${subfolder}`
|
||||
);
|
||||
const embeedingUrl = api.apiURL(
|
||||
`/view?filename=${encodeURIComponent(
|
||||
`${id}_${modelType}.npy`
|
||||
)}&type=output&subfolder=`
|
||||
);
|
||||
loadNpyTensor(embeedingUrl).then(async (tensor) => {
|
||||
embeddings.val = tensor;
|
||||
if (enableAutoSegment.val && !segmented.val) await autoSegment();
|
||||
drawSegment(getClicks());
|
||||
updateImagePrompts();
|
||||
});
|
||||
targetNode.val = node;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
showLoading.val = false;
|
||||
});
|
||||
});
|
||||
targetNode.val = node;
|
||||
}
|
||||
|
||||
/** @typedef {import('../../../web/types/comfy.js').ComfyExtension} ComfyExtension*/
|
||||
@@ -286,45 +372,25 @@ const ext = {
|
||||
getCustomWidgets(app) {
|
||||
return {
|
||||
SAM_PROMPTS(node, inputName, inputData, app) {
|
||||
const btn = node.addWidget("button", "Edit prompt", "", () => {
|
||||
showLoading.val = true;
|
||||
|
||||
let connectedImageFileName = getInputWidgetValue(node, 0, "image");
|
||||
|
||||
const split = connectedImageFileName.split("/");
|
||||
let id = connectedImageFileName;
|
||||
if (split.length > 1) id = split[1];
|
||||
|
||||
node.widgets.find((x) => x.name === "embedding_id").value = id;
|
||||
|
||||
const ckpt = node.widgets.find((x) => x.name === "ckpt").value;
|
||||
const model_type = node.widgets.find(
|
||||
(x) => x.name === "model_type"
|
||||
).value;
|
||||
|
||||
api
|
||||
.fetchApi("/sam_model", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
image: connectedImageFileName,
|
||||
embedding_id: id,
|
||||
ckpt,
|
||||
model_type,
|
||||
}),
|
||||
})
|
||||
.then(() => {
|
||||
showLoading.val = false;
|
||||
showMyImageEditor(node);
|
||||
});
|
||||
const asd = document.createElement("div");
|
||||
Object.assign(asd, {
|
||||
id: "sam",
|
||||
onclick: () => {
|
||||
showMyImageEditor(node);
|
||||
},
|
||||
});
|
||||
btn.serialize = false;
|
||||
document.body.append(asd);
|
||||
|
||||
const btn = node.addWidget("button", "Edit prompt", "", () => {
|
||||
showMyImageEditor(node);
|
||||
btn.serialize = false;
|
||||
});
|
||||
return {
|
||||
widget: btn,
|
||||
};
|
||||
},
|
||||
BLENDSHAPES_CONFIG(node, inputName, inputData, app) {
|
||||
const btn = node.addWidget("button", "Edit blendshapes", "", () => {
|
||||
const btn = node.addWidget("button", "Edit Shape Flow", "", () => {
|
||||
targetNode.val = node;
|
||||
openInAvatechEditor(
|
||||
"https://editor.avatech.ai?comfyui=true",
|
||||
@@ -361,6 +427,24 @@ const ext = {
|
||||
widget: btn,
|
||||
};
|
||||
},
|
||||
GROUP_OPS(node, inputName, inputData, app) {
|
||||
const btn = node.addWidget("button", "Add OBJ", "", () => {
|
||||
node.addInput("BPY_OBJ" + (node.inputs.length + 1), "BPY_OBJ");
|
||||
node.graph.change();
|
||||
});
|
||||
return {
|
||||
widget: btn,
|
||||
};
|
||||
},
|
||||
GROUP_OPS_DELETE(node, inputName, inputData, app) {
|
||||
const btn = node.addWidget("button", "Delete OBJ", "", () => {
|
||||
node.removeInput(node.inputs.length - 1);
|
||||
node.graph.change();
|
||||
});
|
||||
return {
|
||||
widget: btn,
|
||||
};
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
@@ -377,6 +461,7 @@ const ext = {
|
||||
|
||||
node.computeParentGroupResize();
|
||||
};
|
||||
injectUIComponentToComfyuimenu();
|
||||
},
|
||||
|
||||
async setup() {
|
||||
@@ -389,6 +474,10 @@ const ext = {
|
||||
});
|
||||
|
||||
api.addEventListener("executed", (evt) => {
|
||||
const images = evt.detail?.output.images;
|
||||
if (images?.length > 0 && images[0].type === "output") {
|
||||
generatedImages[evt.detail.node] = images[0].filename;
|
||||
}
|
||||
if (evt.detail?.output.gltfFilename) {
|
||||
const viewer = document.getElementById(
|
||||
"avatech-viewer-iframe"
|
||||
@@ -401,11 +490,23 @@ const ext = {
|
||||
api.api_base +
|
||||
`/view?filename=${evt.detail?.output.gltfFilename[0]}`;
|
||||
|
||||
if (
|
||||
gltfFilename.endsWith(".ava") &&
|
||||
evt.detail?.output.auto_save[0] == "true"
|
||||
) {
|
||||
const link = document.createElement("a");
|
||||
link.href = gltfFilename;
|
||||
link.download = gltfFilename.split("/").pop();
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
|
||||
fileName.val = gltfFilename;
|
||||
viewer.postMessage(
|
||||
JSON.stringify({
|
||||
avatarURL: gltfFilename,
|
||||
blendshapes: evt.detail?.output.blendshapes[0],
|
||||
blendshapes: evt.detail?.output.SHAPE_FLOW[0],
|
||||
}),
|
||||
"*"
|
||||
);
|
||||
@@ -414,10 +515,23 @@ const ext = {
|
||||
|
||||
window.addEventListener(
|
||||
"keydown",
|
||||
(event) => {
|
||||
async (event) => {
|
||||
if (event.key === "Escape") {
|
||||
event.preventDefault();
|
||||
showImageEditor.val = false;
|
||||
if (my_modal_3.open) {
|
||||
my_modal_3.close();
|
||||
} else {
|
||||
showImageEditor.val = false;
|
||||
showEditor.val = false;
|
||||
await uploadSegments();
|
||||
// api.fetchApi("/segments_order", {
|
||||
// method: "POST",
|
||||
// body: JSON.stringify({
|
||||
// name: embeddingID.val,
|
||||
// order: Object.keys(imagePromptsMulti.val),
|
||||
// }),
|
||||
// });
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -477,6 +591,36 @@ const ext = {
|
||||
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
switch (nodeData.name) {
|
||||
case "AvatarMainOutput":
|
||||
addMenuHandler(nodeType, function (_, options) {
|
||||
const output = app.nodeOutputs[this.id + ""];
|
||||
if (!output || !output.gltfFilename) return;
|
||||
|
||||
const gltfFilename =
|
||||
window.location.protocol +
|
||||
"//" +
|
||||
api.api_host +
|
||||
api.api_base +
|
||||
`/view?filename=${output.gltfFilename[0]}`;
|
||||
|
||||
options.unshift({
|
||||
content: "Save file",
|
||||
callback: () => {
|
||||
const a = document.createElement("a");
|
||||
let url = new URL(gltfFilename);
|
||||
url.searchParams.delete("preview");
|
||||
a.href = url;
|
||||
a.setAttribute(
|
||||
"download",
|
||||
new URLSearchParams(url.search).get("filename")
|
||||
);
|
||||
document.body.append(a);
|
||||
a.click();
|
||||
requestAnimationFrame(() => a.remove());
|
||||
},
|
||||
});
|
||||
});
|
||||
break;
|
||||
case "ExportGLTF":
|
||||
addMenuHandler(nodeType, function (_, options) {
|
||||
const output = app.nodeOutputs[this.id + ""];
|
||||
@@ -527,20 +671,7 @@ const ext = {
|
||||
});
|
||||
});
|
||||
break;
|
||||
case "SAM_Prompt_Image":
|
||||
nodeData.input.required.sam = ["SAM_PROMPTS"];
|
||||
// nodeData.input.required.upload = ['IMAGEUPLOAD'];
|
||||
// nodeData.input.required.prompts_points = ["IMAGEUPLOAD"];
|
||||
addMenuHandler(nodeType, function (_, options) {
|
||||
options.unshift({
|
||||
content: "Open In Points Editor (Local)",
|
||||
callback: () => {
|
||||
showMyImageEditor(this);
|
||||
},
|
||||
});
|
||||
});
|
||||
break;
|
||||
case "SAM":
|
||||
case "SAM MultiLayer":
|
||||
nodeData.input.required.sam = ["SAM_PROMPTS"];
|
||||
// nodeData.input.required.upload = ['IMAGEUPLOAD'];
|
||||
// nodeData.input.required.prompts_points = ["IMAGEUPLOAD"];
|
||||
@@ -555,26 +686,194 @@ const ext = {
|
||||
break;
|
||||
case "CreateShapeFlow":
|
||||
nodeData.input.required.blendshape = ["BLENDSHAPES_CONFIG"];
|
||||
addMenuHandler(nodeType, function (_, options) {
|
||||
options.unshift({
|
||||
content: "Open In Blendshapes Editor",
|
||||
callback: () => {
|
||||
openInAvatechEditor(
|
||||
"https://editor.avatech.ai?comfyui=true",
|
||||
gltfFilename
|
||||
);
|
||||
},
|
||||
});
|
||||
});
|
||||
break;
|
||||
case "Mesh_JoinMesh":
|
||||
nodeData.input.required.obj = ["MESH_GROUP_CONFIG"];
|
||||
nodeData.input.required.del_obj = ["MESH_GROUP_DELETE"];
|
||||
break;
|
||||
case "GroupOps":
|
||||
nodeData.input.required.obj = ["GROUP_OPS"];
|
||||
nodeData.input.required.del_obj = ["GROUP_OPS_DELETE"];
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export async function uploadPreview() {
|
||||
if (fileName.val == "")
|
||||
app.ui.dialog.show("Please create your avatar first.");
|
||||
else {
|
||||
const file = await fetch(fileName.val)
|
||||
.then((e) => e.arrayBuffer())
|
||||
.then((e) => new Uint8Array(e));
|
||||
const labData = await fetch("https://labs.avatech.ai/api/share", {
|
||||
method: "GET",
|
||||
}).then((e) => e.json());
|
||||
|
||||
await fetch(labData.url, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"x-amz-acl": "public-read",
|
||||
"Content-Type": "model/gltf-binary",
|
||||
"Content-Length": file.length,
|
||||
},
|
||||
body: file,
|
||||
}).catch((error) => console.error(error));
|
||||
|
||||
sharedAvatarLink.val = `https://editor.avatech.ai/viewer?avatarId=${labData?.modelId}`;
|
||||
previewModelId.val = labData.modelId;
|
||||
return labData;
|
||||
}
|
||||
}
|
||||
|
||||
function injectUIComponentToComfyuimenu() {
|
||||
const menu = document.querySelector(".comfy-menu");
|
||||
const avatarPreview = document.createElement("button");
|
||||
avatarPreview.textContent = "Avatar Preview";
|
||||
avatarPreview.onclick = () => {
|
||||
showPreview.val = !showPreview.val;
|
||||
localStorage.setItem("showPreview", showPreview.val);
|
||||
};
|
||||
|
||||
const apiFormat = document.createElement("button");
|
||||
const a = document.createElement("a");
|
||||
apiFormat.textContent = "Save API Format (Avatech)";
|
||||
apiFormat.onclick = () => {
|
||||
let filename = "workflow_api.json";
|
||||
filename = prompt("Save workflow (API) as:", filename);
|
||||
if (!filename) return;
|
||||
if (!filename.toLowerCase().endsWith(".json")) {
|
||||
filename += ".json";
|
||||
}
|
||||
app.graphToPrompt().then(p=>{
|
||||
console.log('fkfk');
|
||||
let json = JSON.stringify(p.output, null, 2); // convert the data to a JSON string
|
||||
json = json.replace(/"seed": (\d+)/g, `"seed": "SEED"`).replace(/"image": "(?!.*mask.*\.png).*"/g, '"image": "reference_image_avatech"').replace(/"embedding_id": ".*"/g, '"embedding_id": "embedding_id_avatech"');
|
||||
const blob = new Blob([json], {type: "application/json"});
|
||||
const url = URL.createObjectURL(blob);
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
setTimeout(function () {
|
||||
a.remove();
|
||||
window.URL.revokeObjectURL(url);
|
||||
}, 0);
|
||||
});
|
||||
};
|
||||
|
||||
const dropdown = document.createElement("div");
|
||||
dropdown.textContent = "▼";
|
||||
dropdown.className = "dropdownbtn";
|
||||
dropdown.onclick = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
LiteGraph.closeAllContextMenus();
|
||||
const menu = new LiteGraph.ContextMenu(
|
||||
[
|
||||
{
|
||||
title: "Create new share link",
|
||||
callback: async () => {
|
||||
shareAvatar.textContent = "Loading...";
|
||||
shareAvatar.append(dropdown);
|
||||
|
||||
await uploadPreview();
|
||||
|
||||
shareLoading.val = false;
|
||||
shareAvatar.textContent = "Share Avatar";
|
||||
shareAvatar.append(dropdown);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Update avatar in current share link",
|
||||
callback: async () => {
|
||||
if (!previewModelId.val)
|
||||
app.ui.dialog.show("Please share your avatar first.");
|
||||
else {
|
||||
if (shareLoading.val) return;
|
||||
|
||||
const file = await fetch(fileName.val)
|
||||
.then((e) => e.arrayBuffer())
|
||||
.then((e) => new Uint8Array(e));
|
||||
|
||||
const labData = await fetch(
|
||||
"https://labs.avatech.ai/api/share?id=" + previewModelId.val,
|
||||
{
|
||||
method: "GET",
|
||||
}
|
||||
).then((e) => e.json());
|
||||
|
||||
await fetch(labData.url, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"x-amz-acl": "public-read",
|
||||
"Content-Type": "model/gltf-binary",
|
||||
"Content-Length": file.length,
|
||||
},
|
||||
body: file,
|
||||
}).catch((error) => console.error(error));
|
||||
|
||||
await fetch(
|
||||
"https://labs.avatech.ai/api/purgecdn?id=" + previewModelId.val,
|
||||
{
|
||||
method: "GET",
|
||||
}
|
||||
).catch((error) => console.error(error));
|
||||
|
||||
infoDialog.show(
|
||||
`Preview updated: <a href='https://editor.avatech.ai/viewer?avatarId=${labData.modelId}' target="_blank">https://editor.avatech.ai/viewer?avatarId=` +
|
||||
labData.modelId +
|
||||
"</a>\n Remember to hard refresh before checking out the new preview!"
|
||||
);
|
||||
|
||||
shareLoading.val = false;
|
||||
shareAvatar.textContent = "Share Avatar";
|
||||
shareAvatar.append(dropdown);
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
{
|
||||
event: e,
|
||||
scale: 1.3,
|
||||
},
|
||||
window
|
||||
);
|
||||
menu.root.classList.add("popup");
|
||||
};
|
||||
|
||||
const shareAvatar = document.createElement("button");
|
||||
shareAvatar.textContent = "Share Avatar";
|
||||
shareAvatar.className = "sharebtn";
|
||||
shareAvatar.onclick = async () => {
|
||||
if (shareLoading.val) return;
|
||||
|
||||
if (!previewModelId.val) {
|
||||
shareLoading.val = true;
|
||||
shareAvatar.textContent = "Loading...";
|
||||
shareAvatar.append(dropdown);
|
||||
|
||||
await uploadPreview();
|
||||
|
||||
shareLoading.val = false;
|
||||
shareAvatar.textContent = "Share Avatar";
|
||||
shareAvatar.append(dropdown);
|
||||
} else {
|
||||
infoDialog.show(
|
||||
`Preview avatar url: <a href='https://editor.avatech.ai/viewer?avatarId=${previewModelId.val}' target="_blank">https://editor.avatech.ai/viewer?avatarId=${previewModelId.val}</a>` +
|
||||
`\nChat url: <a href='https://labs.avatech.ai?avatarId=${previewModelId.val}' target="_blank">https://labs.avatech.ai?avatarId=${previewModelId.val}</a>`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
menu.append(avatarPreview);
|
||||
menu.append(shareAvatar);
|
||||
menu.append(apiFormat);
|
||||
|
||||
shareAvatar.append(dropdown);
|
||||
}
|
||||
|
||||
app.registerExtension(ext);
|
||||
|
||||
+8
-12
@@ -5,19 +5,16 @@ import { modelData, onnxMaskToImage } from "./onnx_helper.js";
|
||||
|
||||
ort.env.wasm.wasmPaths = "https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/";
|
||||
|
||||
// Define image, embedding and model paths
|
||||
const IMAGE_PATH = "/assets/data/dogs.jpg";
|
||||
const IMAGE_EMBEDDING = "/assets/data/dogs_embedding.npy";
|
||||
const MODEL_DIR = "http://127.0.0.1:8188/sam_model";
|
||||
|
||||
export let model = null;
|
||||
|
||||
// Initialize the ONNX model
|
||||
export const initModel = async () => {
|
||||
export const initModel = async (modelType) => {
|
||||
try {
|
||||
if (MODEL_DIR === undefined) return;
|
||||
const URL = MODEL_DIR;
|
||||
model = await ort.InferenceSession.create(URL);
|
||||
if (!model) {
|
||||
model = await ort.InferenceSession.create(
|
||||
`${location.protocol}//${location.host}/sam_model?type=${modelType}`
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
@@ -25,14 +22,12 @@ export const initModel = async () => {
|
||||
|
||||
export const loadNpyTensor = async (tensorFile, dType = "float32") => {
|
||||
let npLoader = new npyjs();
|
||||
console.log('tensorFile', tensorFile);
|
||||
const npArray = await npLoader.load(tensorFile);
|
||||
console.log('np array', npArray);
|
||||
const tensor = new ort.Tensor(dType, npArray.data, npArray.shape);
|
||||
return tensor;
|
||||
};
|
||||
|
||||
export const runONNX = async (clicks, tensor) => {
|
||||
export const runONNX = async (clicks, tensor, box) => {
|
||||
// console.log('tensor', tensor);
|
||||
try {
|
||||
if (
|
||||
@@ -49,6 +44,7 @@ export const runONNX = async (clicks, tensor) => {
|
||||
clicks,
|
||||
tensor,
|
||||
modelScale: imageSize.val,
|
||||
box,
|
||||
});
|
||||
if (feeds === undefined) return;
|
||||
// Run the SAM ONNX model with the feeds returned from modelData()
|
||||
|
||||
+21
-10
@@ -4,7 +4,7 @@
|
||||
// This source code is licensed under the license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
const modelData = ({ clicks, tensor, modelScale }) => {
|
||||
const modelData = ({ clicks, tensor, modelScale, box }) => {
|
||||
const imageEmbedding = tensor;
|
||||
let pointCoords;
|
||||
let pointLabels;
|
||||
@@ -18,8 +18,9 @@ const modelData = ({ clicks, tensor, modelScale }) => {
|
||||
// If there is no box input, a single padding point with
|
||||
// label -1 and coordinates (0.0, 0.0) should be concatenated
|
||||
// so initialize the array to support (n + 1) points.
|
||||
pointCoords = new Float32Array(2 * (n + 1));
|
||||
pointLabels = new Float32Array(n + 1);
|
||||
const numPoints = box ? n + 3 : n + 1;
|
||||
pointCoords = new Float32Array(2 * numPoints);
|
||||
pointLabels = new Float32Array(numPoints);
|
||||
|
||||
// Add clicks and scale to what SAM expects
|
||||
for (let i = 0; i < n; i++) {
|
||||
@@ -28,15 +29,25 @@ const modelData = ({ clicks, tensor, modelScale }) => {
|
||||
pointLabels[i] = clicks[i].clickType;
|
||||
}
|
||||
|
||||
// Add in the extra point/label when only clicks and no box
|
||||
// The extra point is at (0, 0) with label -1
|
||||
pointCoords[2 * n] = 0.0;
|
||||
pointCoords[2 * n + 1] = 0.0;
|
||||
pointLabels[n] = -1.0;
|
||||
if (box) {
|
||||
pointCoords[2 * n] = box.x1 * modelScale.samScale;
|
||||
pointCoords[2 * n + 1] = box.y1 * modelScale.samScale;
|
||||
pointLabels[n] = 2;
|
||||
|
||||
pointCoords[2 * n + 2] = box.x2 * modelScale.samScale;
|
||||
pointCoords[2 * n + 3] = box.y2 * modelScale.samScale;
|
||||
pointLabels[n + 1] = 3;
|
||||
} else {
|
||||
// Add in the extra point/label when only clicks and no box
|
||||
// The extra point is at (0, 0) with label -1
|
||||
pointCoords[2 * n] = 0.0;
|
||||
pointCoords[2 * n + 1] = 0.0;
|
||||
pointLabels[n] = -1.0;
|
||||
}
|
||||
|
||||
// Create the tensor
|
||||
pointCoordsTensor = new ort.Tensor("float32", pointCoords, [1, n + 1, 2]);
|
||||
pointLabelsTensor = new ort.Tensor("float32", pointLabels, [1, n + 1]);
|
||||
pointCoordsTensor = new ort.Tensor("float32", pointCoords, [1, numPoints, 2]);
|
||||
pointLabelsTensor = new ort.Tensor("float32", pointLabels, [1, numPoints]);
|
||||
}
|
||||
const imageSizeTensor = new ort.Tensor("float32", [
|
||||
modelScale.height,
|
||||
|
||||
+41
-5
@@ -9,26 +9,61 @@
|
||||
* @typedef {Object} Point
|
||||
* @property {number} x - The x coordinate
|
||||
* @property {number} y - The y coordinate
|
||||
* @property {number} label - The label
|
||||
* @property {>} label - The label
|
||||
*
|
||||
* @typedef {Object} Box
|
||||
* @property {number} x1
|
||||
* @property {number} y1
|
||||
* @property {number} x2
|
||||
* @property {number} y2
|
||||
*/
|
||||
|
||||
import { van } from './van.js';
|
||||
import { van } from "./van.js";
|
||||
|
||||
export const iframeSrc = van.state('https://editor.avatech.ai?comfyui=true');
|
||||
export const iframeSrc = van.state("https://editor.avatech.ai?comfyui=true");
|
||||
export const showEditor = van.state(false);
|
||||
export const fileName = van.state('');
|
||||
// localStorage.getItem("showPreview") == 'true'
|
||||
export const showPreview = van.state(true);
|
||||
export const previewUrl = van.state(
|
||||
"https://editor.avatech.ai/viewer?avatarId=default&debug=true&width=350&height=350&hideTrigger=true&voiceSelection=true&hideUI=true"
|
||||
);
|
||||
export const previewImg = van.state("");
|
||||
export const previewImgLoading = van.state(false);
|
||||
export const enableAutoSegment = van.state(false);
|
||||
// export const previewUrl = van.state("http://localhost:3006/viewer?avatarId=default&hideUI=true&debug=true&width=300&height=300&showAudioControl=true");
|
||||
export const isDirty = van.state(false);
|
||||
export const fileName = van.state("");
|
||||
export const showImageEditor = van.state(false);
|
||||
export const showLoading = van.state(false);
|
||||
export const imageUrl = van.state('');
|
||||
export const alertDialog = van.state({
|
||||
text: "",
|
||||
time: 0,
|
||||
});
|
||||
export const shareLoading = van.state(false);
|
||||
export const previewModelId = van.state("");
|
||||
|
||||
export const isGenerateFlow = van.state(false);
|
||||
|
||||
export const loadingCaption = van.state("");
|
||||
export const imageUrl = van.state("");
|
||||
export const point_label = van.state(1);
|
||||
export const imageContainerSize = van.state({
|
||||
width: 0,
|
||||
height: 0,
|
||||
});
|
||||
|
||||
/** @type {State<Box>} */
|
||||
export const boxes = van.state();
|
||||
|
||||
/** @type {State<Record<string, Box>>} */
|
||||
export const boxesMulti = van.state({});
|
||||
|
||||
/** @type {State<Point[]>} */
|
||||
export const imagePrompts = van.state([]);
|
||||
|
||||
export const allImagePrompts = van.state([{}]);
|
||||
|
||||
|
||||
/** @type {State<Record<string, Point[]>>} */
|
||||
export const imagePromptsMulti = van.state({});
|
||||
|
||||
@@ -40,3 +75,4 @@ export const targetNode = van.state();
|
||||
|
||||
export const imageSize = van.state({ width: 0, height: 0, samScale: 0 });
|
||||
export const embeddings = van.state();
|
||||
export const embeddingID = van.state("Test");
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,3 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Gabarito&display=swap');
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
.comfy-normal-modal {
|
||||
display: none; /* Hidden by default */
|
||||
position: fixed; /* Stay in place */
|
||||
z-index: 100; /* Sit on top */
|
||||
padding: 30px 30px 10px 30px;
|
||||
background-color: var(--comfy-menu-bg); /* Modal background */
|
||||
box-shadow: 0 0 20px #888888;
|
||||
border-radius: 10px;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
max-width: 80vw;
|
||||
max-height: 80vh;
|
||||
transform: translate(-50%, -50%);
|
||||
overflow: hidden;
|
||||
justify-content: center;
|
||||
font-family: monospace;
|
||||
font-size: 15px;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.comfy-normal-modal p {
|
||||
overflow: auto;
|
||||
white-space: pre-line; /* This will respect line breaks */
|
||||
margin-bottom: 20px; /* Add some margin between the text and the close button*/
|
||||
}
|
||||
|
||||
.comfy-normal-modal a {
|
||||
text-decoration-line: underline;
|
||||
}
|
||||
|
||||
.comfy-normal-modal button {
|
||||
font-size: 20px;
|
||||
color: var(--input-text);
|
||||
background-color: var(--comfy-input-bg);
|
||||
border-radius: 8px;
|
||||
border-color: var(--border-color);
|
||||
border-style: solid;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.comfy-normal-modal button:hover {
|
||||
filter: brightness(1.2);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sharebtn {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.popup ~ .litecontextmenu {
|
||||
transform: scale(1.3);
|
||||
}
|
||||
|
||||
.dropdownbtn {
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 24px;
|
||||
height: 30px;
|
||||
justify-content: center;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-top-right-radius: 0.375rem;
|
||||
border-bottom-right-radius: 0.375rem;
|
||||
}
|
||||
|
||||
.dropdownbtn:hover {
|
||||
filter: brightness(1.6);
|
||||
background-color: var(--comfy-menu-bg);
|
||||
}
|
||||
|
||||
.comfy-menu > button,
|
||||
.comfy-menu-btns button,
|
||||
.comfy-menu .comfy-list button,
|
||||
.comfy-modal button {
|
||||
color: var(--input-text);
|
||||
background-color: var(--comfy-input-bg);
|
||||
border-radius: 8px;
|
||||
border-color: var(--border-color);
|
||||
border-style: solid;
|
||||
margin-top: 2px;
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.comfy-menu > button:hover,
|
||||
.comfy-menu-btns button:hover,
|
||||
.comfy-menu .comfy-list button:hover,
|
||||
.comfy-modal button:hover,
|
||||
.comfy-settings-btn:hover {
|
||||
filter: brightness(1.2);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.comfy-list {
|
||||
color: var(--descrip-text);
|
||||
background-color: var(--comfy-menu-bg);
|
||||
margin-bottom: 10px;
|
||||
border-color: var(--border-color);
|
||||
border-style: solid;
|
||||
border-width: 3px;
|
||||
}
|
||||
|
||||
.comfy-list-items {
|
||||
overflow-y: scroll;
|
||||
max-height: 100px;
|
||||
min-height: 25px;
|
||||
background-color: var(--comfy-input-bg);
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.comfy-list h4 {
|
||||
min-width: 160px;
|
||||
margin: 0;
|
||||
padding: 3px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.comfy-list-items button {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.comfy-list-actions {
|
||||
margin: 5px;
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.comfy-list-actions button {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
img {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
img[src] {
|
||||
display: block;
|
||||
}
|
||||
+3108
File diff suppressed because it is too large
Load Diff
+2
-3
@@ -4,15 +4,14 @@
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev": "concurrently \"tailwindcss -i ./js/style.css -o ./js/output.css --watch -m\" \"node ./style-watcher.js\""
|
||||
"dev": "tailwindcss -i ./js/styles.css -o ./js/tw-styles.css --watch"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"chokidar": "^3.5.3",
|
||||
"concurrently": "^8.2.1",
|
||||
"daisyui": "^3.7.5",
|
||||
"daisyui": "^4.0.7",
|
||||
"tailwindcss": "^3.3.3"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+14
-209
@@ -8,12 +8,9 @@ devDependencies:
|
||||
chokidar:
|
||||
specifier: ^3.5.3
|
||||
version: 3.5.3
|
||||
concurrently:
|
||||
specifier: ^8.2.1
|
||||
version: 8.2.1
|
||||
daisyui:
|
||||
specifier: ^3.7.5
|
||||
version: 3.7.5
|
||||
specifier: ^4.0.7
|
||||
version: 4.0.7(postcss@8.4.29)
|
||||
tailwindcss:
|
||||
specifier: ^3.3.3
|
||||
version: 3.3.3
|
||||
@@ -25,13 +22,6 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/@babel/runtime@7.22.11:
|
||||
resolution: {integrity: sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
regenerator-runtime: 0.14.0
|
||||
dev: true
|
||||
|
||||
/@jridgewell/gen-mapping@0.3.3:
|
||||
resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
@@ -83,18 +73,6 @@ packages:
|
||||
fastq: 1.15.0
|
||||
dev: true
|
||||
|
||||
/ansi-regex@5.0.1:
|
||||
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/ansi-styles@4.3.0:
|
||||
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
color-convert: 2.0.1
|
||||
dev: true
|
||||
|
||||
/any-promise@1.3.0:
|
||||
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
|
||||
dev: true
|
||||
@@ -139,14 +117,6 @@ packages:
|
||||
engines: {node: '>= 6'}
|
||||
dev: true
|
||||
|
||||
/chalk@4.1.2:
|
||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
supports-color: 7.2.0
|
||||
dev: true
|
||||
|
||||
/chokidar@3.5.3:
|
||||
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
|
||||
engines: {node: '>= 8.10.0'}
|
||||
@@ -162,30 +132,6 @@ packages:
|
||||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
/cliui@8.0.1:
|
||||
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
wrap-ansi: 7.0.0
|
||||
dev: true
|
||||
|
||||
/color-convert@2.0.1:
|
||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||
engines: {node: '>=7.0.0'}
|
||||
dependencies:
|
||||
color-name: 1.1.4
|
||||
dev: true
|
||||
|
||||
/color-name@1.1.4:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
dev: true
|
||||
|
||||
/colord@2.9.3:
|
||||
resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==}
|
||||
dev: true
|
||||
|
||||
/commander@4.1.1:
|
||||
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -195,22 +141,6 @@ packages:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
dev: true
|
||||
|
||||
/concurrently@8.2.1:
|
||||
resolution: {integrity: sha512-nVraf3aXOpIcNud5pB9M82p1tynmZkrSGQ1p6X/VY8cJ+2LMVqAgXsJxYYefACSHbTYlm92O1xuhdGTjwoEvbQ==}
|
||||
engines: {node: ^14.13.0 || >=16.0.0}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
date-fns: 2.30.0
|
||||
lodash: 4.17.21
|
||||
rxjs: 7.8.1
|
||||
shell-quote: 1.8.1
|
||||
spawn-command: 0.0.2
|
||||
supports-color: 8.1.1
|
||||
tree-kill: 1.2.2
|
||||
yargs: 17.7.2
|
||||
dev: true
|
||||
|
||||
/css-selector-tokenizer@0.8.0:
|
||||
resolution: {integrity: sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==}
|
||||
dependencies:
|
||||
@@ -224,24 +154,21 @@ packages:
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/daisyui@3.7.5:
|
||||
resolution: {integrity: sha512-udhiBJYVvcPGXa+mL5IElke6EdddKecjbbz6m43+9IVqzK8GBwetg092Edclo42TkNboLD9nzodeesJygqZt2A==}
|
||||
engines: {node: '>=16.9.0'}
|
||||
dependencies:
|
||||
colord: 2.9.3
|
||||
css-selector-tokenizer: 0.8.0
|
||||
postcss: 8.4.29
|
||||
postcss-js: 4.0.1(postcss@8.4.29)
|
||||
tailwindcss: 3.3.3
|
||||
transitivePeerDependencies:
|
||||
- ts-node
|
||||
/culori@3.2.0:
|
||||
resolution: {integrity: sha512-HIEbTSP7vs1mPq/2P9In6QyFE0Tkpevh0k9a+FkjhD+cwsYm9WRSbn4uMdW9O0yXlNYC3ppxL3gWWPOcvEl57w==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
dev: true
|
||||
|
||||
/date-fns@2.30.0:
|
||||
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
|
||||
engines: {node: '>=0.11'}
|
||||
/daisyui@4.0.7(postcss@8.4.29):
|
||||
resolution: {integrity: sha512-D84DnNDZKcamwNsxCMrwYaddyz5kC6VO6oe30nM1x67GzCAfarfd3Ar1rpLGXCIqSsEoNZUHO8EcXvX93W2ZkA==}
|
||||
engines: {node: '>=16.9.0'}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.22.11
|
||||
css-selector-tokenizer: 0.8.0
|
||||
culori: 3.2.0
|
||||
picocolors: 1.0.0
|
||||
postcss-js: 4.0.1(postcss@8.4.29)
|
||||
transitivePeerDependencies:
|
||||
- postcss
|
||||
dev: true
|
||||
|
||||
/didyoumean@1.2.2:
|
||||
@@ -252,15 +179,6 @@ packages:
|
||||
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
|
||||
dev: true
|
||||
|
||||
/emoji-regex@8.0.0:
|
||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||
dev: true
|
||||
|
||||
/escalade@3.1.1:
|
||||
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
|
||||
engines: {node: '>=6'}
|
||||
dev: true
|
||||
|
||||
/fast-glob@3.3.1:
|
||||
resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==}
|
||||
engines: {node: '>=8.6.0'}
|
||||
@@ -305,11 +223,6 @@ packages:
|
||||
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
|
||||
dev: true
|
||||
|
||||
/get-caller-file@2.0.5:
|
||||
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
|
||||
engines: {node: 6.* || 8.* || >= 10.*}
|
||||
dev: true
|
||||
|
||||
/glob-parent@5.1.2:
|
||||
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -335,11 +248,6 @@ packages:
|
||||
path-is-absolute: 1.0.1
|
||||
dev: true
|
||||
|
||||
/has-flag@4.0.0:
|
||||
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/has@1.0.3:
|
||||
resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
@@ -376,11 +284,6 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/is-fullwidth-code-point@3.0.0:
|
||||
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/is-glob@4.0.3:
|
||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -407,10 +310,6 @@ packages:
|
||||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||
dev: true
|
||||
|
||||
/lodash@4.17.21:
|
||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||
dev: true
|
||||
|
||||
/merge2@1.4.1:
|
||||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -580,15 +479,6 @@ packages:
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/regenerator-runtime@0.14.0:
|
||||
resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==}
|
||||
dev: true
|
||||
|
||||
/require-directory@2.1.1:
|
||||
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/resolve@1.22.4:
|
||||
resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==}
|
||||
hasBin: true
|
||||
@@ -609,41 +499,11 @@ packages:
|
||||
queue-microtask: 1.2.3
|
||||
dev: true
|
||||
|
||||
/rxjs@7.8.1:
|
||||
resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
|
||||
dependencies:
|
||||
tslib: 2.6.2
|
||||
dev: true
|
||||
|
||||
/shell-quote@1.8.1:
|
||||
resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==}
|
||||
dev: true
|
||||
|
||||
/source-map-js@1.0.2:
|
||||
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/spawn-command@0.0.2:
|
||||
resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==}
|
||||
dev: true
|
||||
|
||||
/string-width@4.2.3:
|
||||
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
emoji-regex: 8.0.0
|
||||
is-fullwidth-code-point: 3.0.0
|
||||
strip-ansi: 6.0.1
|
||||
dev: true
|
||||
|
||||
/strip-ansi@6.0.1:
|
||||
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
ansi-regex: 5.0.1
|
||||
dev: true
|
||||
|
||||
/sucrase@3.34.0:
|
||||
resolution: {integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -658,20 +518,6 @@ packages:
|
||||
ts-interface-checker: 0.1.13
|
||||
dev: true
|
||||
|
||||
/supports-color@7.2.0:
|
||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
has-flag: 4.0.0
|
||||
dev: true
|
||||
|
||||
/supports-color@8.1.1:
|
||||
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
has-flag: 4.0.0
|
||||
dev: true
|
||||
|
||||
/supports-preserve-symlinks-flag@1.0.0:
|
||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -728,60 +574,19 @@ packages:
|
||||
is-number: 7.0.0
|
||||
dev: true
|
||||
|
||||
/tree-kill@1.2.2:
|
||||
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/ts-interface-checker@0.1.13:
|
||||
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
|
||||
dev: true
|
||||
|
||||
/tslib@2.6.2:
|
||||
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
|
||||
dev: true
|
||||
|
||||
/util-deprecate@1.0.2:
|
||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||
dev: true
|
||||
|
||||
/wrap-ansi@7.0.0:
|
||||
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
dev: true
|
||||
|
||||
/wrappy@1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
dev: true
|
||||
|
||||
/y18n@5.0.8:
|
||||
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/yaml@2.3.2:
|
||||
resolution: {integrity: sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==}
|
||||
engines: {node: '>= 14'}
|
||||
dev: true
|
||||
|
||||
/yargs-parser@21.1.1:
|
||||
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
||||
engines: {node: '>=12'}
|
||||
dev: true
|
||||
|
||||
/yargs@17.7.2:
|
||||
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
cliui: 8.0.1
|
||||
escalade: 3.1.1
|
||||
get-caller-file: 2.0.5
|
||||
require-directory: 2.1.1
|
||||
string-width: 4.2.3
|
||||
y18n: 5.0.8
|
||||
yargs-parser: 21.1.1
|
||||
dev: true
|
||||
|
||||
+4
-1
@@ -3,6 +3,9 @@ numpy
|
||||
opencv-python
|
||||
opencv-contrib-python
|
||||
einops
|
||||
bpy
|
||||
bpy==3.6.0
|
||||
segment-anything
|
||||
tqdm
|
||||
python-dotenv
|
||||
mediapipe
|
||||
# -e git+https://github.com/facebookresearch/segment-anything.git#egg=segment_anything
|
||||
@@ -1,12 +1,24 @@
|
||||
from aiohttp import web
|
||||
from segment_anything import sam_model_registry, SamPredictor
|
||||
from PIL import Image, ImageOps
|
||||
from dotenv import load_dotenv
|
||||
from blender.mesh_utils import upload_avatar_file
|
||||
import os
|
||||
import requests
|
||||
import folder_paths
|
||||
import json
|
||||
import numpy as np
|
||||
import server
|
||||
import re
|
||||
import base64
|
||||
from PIL import Image
|
||||
import io
|
||||
import time
|
||||
import execution
|
||||
import random
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
# For speeding up ONNX model, see https://github.com/facebookresearch/segment-anything/tree/main/demo#onnx-multithreading-with-sharedarraybuffer
|
||||
def inject_headers(original_handler):
|
||||
@@ -32,24 +44,34 @@ for item in server.PromptServer.instance.routes._items:
|
||||
server.PromptServer.instance.routes._items = routes
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.get("/avatar-graph-comfyui/tw-styles.css")
|
||||
async def get_web_styles(request):
|
||||
filename = os.path.join(os.path.dirname(__file__), "js/tw-styles.css")
|
||||
return web.FileResponse(filename)
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.get("/sam_model")
|
||||
async def get_sam_model(request):
|
||||
filename = os.path.join(folder_paths.base_path, "web/models/sam.onnx")
|
||||
# print(filename)
|
||||
model_type = request.rel_url.query.get("type", "vit_h")
|
||||
filename = os.path.join(folder_paths.base_path, f"web/models/sam_{model_type}.onnx")
|
||||
if not os.path.isfile(filename):
|
||||
os.makedirs(os.path.dirname(filename), exist_ok=True)
|
||||
print(f"Downloading ONNX model to {filename}")
|
||||
response = requests.get(
|
||||
"https://avatech-avatar-dev1.nyc3.cdn.digitaloceanspaces.com/models/sam.onnx"
|
||||
f"https://avatech-avatar-dev1.nyc3.cdn.digitaloceanspaces.com/models/sam_{model_type}.onnx"
|
||||
)
|
||||
response.raise_for_status()
|
||||
with open(filename, "wb") as f:
|
||||
f.write(response.content)
|
||||
print(f"ONNX model downloaded: {filename}")
|
||||
print(f"ONNX model downloaded")
|
||||
return web.FileResponse(filename)
|
||||
|
||||
|
||||
def load_image(image):
|
||||
image_path = folder_paths.get_annotated_filepath(image)
|
||||
def load_image(image, is_generated_image):
|
||||
if is_generated_image:
|
||||
image_path = f"{folder_paths.get_output_directory()}/{image}"
|
||||
else:
|
||||
image_path = folder_paths.get_annotated_filepath(image)
|
||||
i = Image.open(image_path)
|
||||
i = ImageOps.exif_transpose(i)
|
||||
image = i.convert("RGB")
|
||||
@@ -60,26 +82,303 @@ def load_image(image):
|
||||
@server.PromptServer.instance.routes.post("/sam_model")
|
||||
async def post_sam_model(request):
|
||||
post = await request.json()
|
||||
is_generated_image = post.get("isGeneratedImage")
|
||||
emb_id = post.get("embedding_id")
|
||||
emb_filename = f"{folder_paths.get_output_directory()}/{emb_id}.npy"
|
||||
ckpt = post.get("ckpt")
|
||||
ckpt = folder_paths.get_full_path("sams", ckpt)
|
||||
remote = post.get("remote")
|
||||
model_type = re.findall(r"vit_[lbh]", ckpt)[0]
|
||||
emb_filename = f"{folder_paths.get_output_directory()}/{emb_id}_{model_type}.npy"
|
||||
output_json_filename = (
|
||||
f"{folder_paths.get_output_directory()}/{emb_id}_{model_type}.json"
|
||||
)
|
||||
if not os.path.exists(emb_filename):
|
||||
image = load_image(post.get("image"))
|
||||
ckpt = post.get("ckpt")
|
||||
model_type = post.get("model_type")
|
||||
ckpt = folder_paths.get_full_path("sam", ckpt)
|
||||
sam = sam_model_registry[model_type](checkpoint=ckpt)
|
||||
predictor = SamPredictor(sam)
|
||||
|
||||
image_np = (image * 255).astype(np.uint8)
|
||||
predictor.set_image(image_np)
|
||||
emb = predictor.get_image_embedding().cpu().numpy()
|
||||
np.save(emb_filename, emb)
|
||||
with open(f"{folder_paths.get_output_directory()}/{emb_id}.json", "w") as f:
|
||||
json.dump(
|
||||
{
|
||||
"input_size": predictor.input_size,
|
||||
"original_size": predictor.original_size,
|
||||
image = load_image(post.get("image"), is_generated_image)
|
||||
if remote:
|
||||
# Run embed in remote server
|
||||
image = Image.fromarray((image * 255).astype(np.uint8))
|
||||
buffered = io.BytesIO()
|
||||
image.save(buffered, format="PNG")
|
||||
image = base64.b64encode(buffered.getvalue()).decode()
|
||||
res = requests.post(
|
||||
"https://avatechgg--sam-embed.modal.run",
|
||||
headers={
|
||||
"Content-type": "application/json",
|
||||
"Accept": "application/json",
|
||||
},
|
||||
f,
|
||||
data=json.dumps(
|
||||
{
|
||||
"image": image,
|
||||
}
|
||||
),
|
||||
).json()
|
||||
emb, input_size, original_size = (
|
||||
res["emb"],
|
||||
res["input_size"],
|
||||
res["original_size"],
|
||||
)
|
||||
emb = np.array(emb).astype(np.float32)
|
||||
np.save(emb_filename, emb)
|
||||
with open(output_json_filename, "w") as f:
|
||||
data = {
|
||||
"input_size": input_size,
|
||||
"original_size": original_size,
|
||||
}
|
||||
json.dump(data, f)
|
||||
else:
|
||||
sam = sam_model_registry[model_type](checkpoint=ckpt)
|
||||
predictor = SamPredictor(sam)
|
||||
|
||||
image_np = (image * 255).astype(np.uint8)
|
||||
predictor.set_image(image_np)
|
||||
emb = predictor.get_image_embedding().cpu().numpy()
|
||||
np.save(emb_filename, emb)
|
||||
with open(output_json_filename, "w") as f:
|
||||
json.dump(
|
||||
{
|
||||
"input_size": predictor.input_size,
|
||||
"original_size": predictor.original_size,
|
||||
},
|
||||
f,
|
||||
)
|
||||
print("Finished embedding")
|
||||
return web.json_response({})
|
||||
|
||||
|
||||
def save_image(image, save_name=None):
|
||||
input_folder = folder_paths.get_input_directory()
|
||||
name, extension = os.path.splitext(image.filename)
|
||||
|
||||
if save_name == None:
|
||||
save_name = f"{name}{extension}"
|
||||
i = 1
|
||||
while os.path.exists(f"{input_folder}/{save_name}"):
|
||||
save_name = f"{name}_{i}{extension}"
|
||||
i += 1
|
||||
|
||||
with open(f"{input_folder}/{save_name}", "wb") as f:
|
||||
f.write(image.file.read())
|
||||
|
||||
return save_name
|
||||
|
||||
|
||||
def post_prompt(json_data):
|
||||
prompt_server = server.PromptServer.instance
|
||||
json_data = prompt_server.trigger_on_prompt(json_data)
|
||||
|
||||
if "number" in json_data:
|
||||
number = float(json_data["number"])
|
||||
else:
|
||||
number = prompt_server.number
|
||||
if "front" in json_data:
|
||||
if json_data["front"]:
|
||||
number = -number
|
||||
|
||||
prompt_server.number += 1
|
||||
|
||||
if "prompt" in json_data:
|
||||
prompt = json_data["prompt"]
|
||||
valid = execution.validate_prompt(prompt)
|
||||
extra_data = {}
|
||||
if "extra_data" in json_data:
|
||||
extra_data = json_data["extra_data"]
|
||||
|
||||
if "client_id" in json_data:
|
||||
extra_data["client_id"] = json_data["client_id"]
|
||||
if valid[0]:
|
||||
prompt_id = str(uuid.uuid4())
|
||||
outputs_to_execute = valid[2]
|
||||
prompt_server.prompt_queue.put(
|
||||
(number, prompt_id, prompt, extra_data, outputs_to_execute)
|
||||
)
|
||||
response = {
|
||||
"prompt_id": prompt_id,
|
||||
"number": number,
|
||||
"node_errors": valid[3],
|
||||
}
|
||||
return web.json_response(response)
|
||||
else:
|
||||
print("invalid prompt:", valid[1])
|
||||
return web.json_response(
|
||||
{"error": valid[1], "node_errors": valid[3]}, status=400
|
||||
)
|
||||
else:
|
||||
return web.json_response({"error": "no prompt", "node_errors": []}, status=400)
|
||||
|
||||
|
||||
def randomSeed(num_digits=15):
|
||||
range_start = 10 ** (num_digits - 1)
|
||||
range_end = (10**num_digits) - 1
|
||||
return random.randint(range_start, range_end)
|
||||
|
||||
|
||||
def load_workflow(workflow_name):
|
||||
with open(
|
||||
os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
f"workflow_templates/api/{workflow_name}.json",
|
||||
)
|
||||
) as f:
|
||||
return "\n".join(f.readlines())
|
||||
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.post("/avatar_generation")
|
||||
async def post_prompt_block(request):
|
||||
prompt_server = server.PromptServer.instance
|
||||
post = await request.post()
|
||||
uploaded_workflow = post.get("workflow")
|
||||
workflow_name = post.get("workflow_name")
|
||||
if uploaded_workflow is not None:
|
||||
workflow = uploaded_workflow
|
||||
elif workflow_name is not None:
|
||||
workflow = load_workflow(workflow_name)
|
||||
|
||||
ref_image = post.get("ref_image")
|
||||
base_image = post.get("base_image")
|
||||
if ref_image is not None:
|
||||
image_path = save_image(ref_image)
|
||||
image_name, image_ext = os.path.splitext(image_path)
|
||||
workflow = workflow.replace("reference_image_avatech", image_path)
|
||||
elif base_image is not None:
|
||||
image_path = save_image(base_image)
|
||||
image_name, image_ext = os.path.splitext(image_path)
|
||||
workflow = workflow.replace("base_image", image_path)
|
||||
workflow = workflow.replace("reference_image_avatech", image_path) # TMP
|
||||
|
||||
for key, value in post.items():
|
||||
if key.startswith("mask_"):
|
||||
mask_name = image_name + "_" + key.replace("mask_", "") + image_ext
|
||||
mask_path = save_image(value, save_name=mask_name)
|
||||
workflow = workflow.replace(f'"{key}"', f'"{mask_path}"')
|
||||
|
||||
workflow = workflow.replace("embedding_id_avatech", image_path)
|
||||
workflow = workflow.replace("SEED", str(randomSeed()))
|
||||
api_prompt = json.loads(workflow)
|
||||
|
||||
# skip generation part if base_image is provided
|
||||
if base_image is not None:
|
||||
for value in api_prompt.values():
|
||||
if (
|
||||
value["class_type"] == "LoadImageFromRequest"
|
||||
and value["inputs"]["name"] == image_path
|
||||
):
|
||||
del value["inputs"]["image"]
|
||||
elif (
|
||||
value["class_type"] == "PreviewImage"
|
||||
or value["class_type"] == "SaveImage"
|
||||
):
|
||||
value["inputs"] = {}
|
||||
|
||||
res = post_prompt({"prompt": api_prompt})
|
||||
prompt_id = json.loads(res.text)["prompt_id"]
|
||||
while True:
|
||||
history = prompt_server.prompt_queue.get_history(prompt_id=prompt_id)
|
||||
if history:
|
||||
# file = get_avatar_file(history[prompt_id]["outputs"])
|
||||
# return web.Response(body=file)
|
||||
outputs = history[prompt_id]["outputs"]
|
||||
for node_id, output in outputs.items():
|
||||
if "gltfFilename" in output:
|
||||
modelId = upload_avatar_file(output)
|
||||
print("model id", modelId)
|
||||
return web.json_response({"id": modelId}, status=200)
|
||||
time.sleep(0.5)
|
||||
|
||||
|
||||
# @server.PromptServer.instance.routes.get("/get_default_workflow")
|
||||
# async def get_default_workflow(request):
|
||||
# # json_link = "https://cdn.discordapp.com/attachments/1119102674437156984/1172255632586448987/workflow_boy_2_1.json?ex=655fa722&is=654d3222&hm=463fa6a3c6ea60f7471196ff45382c729d3b856e86282f905d37a0398711860e&" # YP workflow
|
||||
# # json_link = "https://cdn.discordapp.com/attachments/729003657483518063/1172504658812608572/workflow_15.json?ex=65608f0e&is=654e1a0e&hm=f707d887b9294c1e9b26e54856b1e516d1725a1b25d044b46229cea6e5c804a1&" # Benny workflow
|
||||
# # json_link = 'https://cdn.discordapp.com/attachments/1110859802701221898/1173536418337914970/newstyle.json?ex=65644ff5&is=6551daf5&hm=f129838fae10197351bd27c69c7ff5eb4edf2c7d6ed74e6db8b55ddaa3c77dee&' # Deepwoo workflow
|
||||
# json_link = 'https://cdn.discordapp.com/attachments/729003657483518063/1174045115757633596/girl1114.json?ex=656629b8&is=6553b4b8&hm=df3d7798b887e2b3b6b06ea438f1bc4ba041dd0f9daf54ea48101845ec7f4243&'
|
||||
# response = requests.get(json_link)
|
||||
# response.raise_for_status()
|
||||
# return web.json_response(response.json())
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.get("/get_workflow")
|
||||
async def get_workflow(request):
|
||||
name = request.rel_url.query.get("name", "default")
|
||||
# if name == "default":
|
||||
# json_link = 'https://cdn.discordapp.com/attachments/729003657483518063/1174045115757633596/girl1114.json?ex=656629b8&is=6553b4b8&hm=df3d7798b887e2b3b6b06ea438f1bc4ba041dd0f9daf54ea48101845ec7f4243&'
|
||||
# response = requests.get(json_link)
|
||||
# response.raise_for_status()
|
||||
# workflow = response.json()
|
||||
# else:
|
||||
if name == "default":
|
||||
name = "Auto_segment_workflow"
|
||||
|
||||
workflows_path = os.path.join(os.path.dirname(__file__), "workflow_templates")
|
||||
workflow = json.load(open(f"{workflows_path}/{name}.json"))
|
||||
return web.json_response(workflow)
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.post("/segments")
|
||||
async def post_segments(request):
|
||||
post = await request.json()
|
||||
name = post.get("name")
|
||||
segments = post.get("segments")
|
||||
output_dir = os.path.join(folder_paths.base_path, f"output/segments_{name}")
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
for key, value in segments.items():
|
||||
filename = os.path.join(output_dir, f"{key}.png")
|
||||
with open(filename, "wb") as f:
|
||||
f.write(base64.b64decode(value.split(",")[1]))
|
||||
|
||||
order = list(segments.keys())
|
||||
with open(os.path.join(output_dir, "order.json"), "w") as f:
|
||||
json.dump(order, f)
|
||||
return web.json_response({})
|
||||
|
||||
|
||||
# @server.PromptServer.instance.routes.post("/segments_order")
|
||||
# async def post_segments(request):
|
||||
# post = await request.json()
|
||||
# name = post.get("name")
|
||||
# order = post.get("order")
|
||||
# output_dir = os.path.join(folder_paths.base_path, f"output/{name}")
|
||||
# os.makedirs(output_dir, exist_ok=True)
|
||||
# with open(os.path.join(output_dir, "order.json") , "w") as f:
|
||||
# json.dump(order, f)
|
||||
# return web.json_response({})
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.get("/get_webhook")
|
||||
async def get_webhook(request):
|
||||
url = os.getenv("DISCORD_WEBHOOK_URL")
|
||||
return web.json_response(url)
|
||||
|
||||
|
||||
import uuid
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.post("/create_avatar_from_image")
|
||||
async def post_input_file(request):
|
||||
post = await request.read()
|
||||
|
||||
# Doesn't seems working when file isnt png / or nothing is uploaded
|
||||
if not post:
|
||||
raise web.HTTPBadRequest(reason="No image data received")
|
||||
|
||||
try:
|
||||
queue_id = uuid.uuid4()
|
||||
|
||||
output_dir = os.path.join(
|
||||
folder_paths.base_path, "input", "create_avatar_endpoint"
|
||||
)
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
filename = os.path.join(output_dir, str(queue_id) + ".png")
|
||||
with open(filename, "wb") as f:
|
||||
f.write(post)
|
||||
|
||||
return web.json_response(
|
||||
{
|
||||
"redirect_url": "https://ai-assistant.avatech.ai?queue-id="
|
||||
+ str(queue_id)
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return web.json_response({"error": e})
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import torch
|
||||
from einops import rearrange, repeat
|
||||
from torchvision import transforms
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
import cv2
|
||||
import base64
|
||||
import requests
|
||||
import json
|
||||
import io
|
||||
|
||||
class AvatarSegmentation:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE", "IMAGE", "IMAGE", "IMAGE", "IMAGE")
|
||||
RETURN_NAMES = ("back_hair", "front_hair","eyes", "eyelashes", "mouth")
|
||||
|
||||
FUNCTION = "segment"
|
||||
|
||||
CATEGORY = "image"
|
||||
|
||||
def segment(self, image):
|
||||
# Convert tensor to PIL image
|
||||
image = image[0]
|
||||
image = rearrange(image, 'h w c -> c h w')
|
||||
image = transforms.ToPILImage()(image)
|
||||
|
||||
# Save image to in-memory file
|
||||
image_buff = io.BytesIO()
|
||||
image.save(image_buff, format="PNG")
|
||||
image_string = base64.b64encode(image_buff.getvalue()).decode("utf-8")
|
||||
|
||||
# Send request to API
|
||||
url = 'https://q41iq6s6t8.execute-api.ap-southeast-1.amazonaws.com/seg-cpu'
|
||||
headers = {
|
||||
'content-type': 'application/json'
|
||||
}
|
||||
data = {
|
||||
'img_str': image_string
|
||||
}
|
||||
response = requests.post(url, headers=headers, data=json.dumps(data))
|
||||
|
||||
# Convert response to tensors
|
||||
outs = []
|
||||
masks = response.json()
|
||||
for segment_name, img_str in masks.items():
|
||||
mask = base64.b64decode(img_str.encode('utf-8'))
|
||||
mask = Image.open(io.BytesIO(mask))
|
||||
mask = np.array(mask)
|
||||
mask = torch.from_numpy(mask) # shape: H, W, 3
|
||||
mask = rearrange(mask, 'h w c -> 1 h w c')
|
||||
outs.append(mask)
|
||||
return outs
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"AvatarSegmentation": AvatarSegmentation
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"AvatarSegmentation": "Avatar Segmentation"
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import folder_paths
|
||||
from PIL import Image, ImageOps
|
||||
import numpy as np
|
||||
import torch
|
||||
|
||||
class LoadImageFromRequest:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"name": (
|
||||
"STRING",
|
||||
{"multiline": False, "default": "face.png"},
|
||||
),
|
||||
},
|
||||
"optional": {
|
||||
"image": ("IMAGE",),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("image",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "image"
|
||||
|
||||
def run(self, name, image=None):
|
||||
try:
|
||||
image_path = folder_paths.get_annotated_filepath(name)
|
||||
image = Image.open(image_path)
|
||||
image = ImageOps.exif_transpose(image)
|
||||
# image = image.convert("RGB")
|
||||
image = np.array(image).astype(np.float32) / 255.0
|
||||
image = torch.from_numpy(image)[None,]
|
||||
return [image]
|
||||
except:
|
||||
return [image]
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {"LoadImageFromRequest": LoadImageFromRequest}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {"LoadImageFromRequest": "Load Image From Request"}
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
import folder_paths
|
||||
import os
|
||||
import numpy as np
|
||||
import torch
|
||||
from segment_anything import sam_model_registry, SamPredictor
|
||||
from einops import rearrange, repeat
|
||||
|
||||
|
||||
global_predictor = None
|
||||
|
||||
class SAM:
|
||||
def __init__(self):
|
||||
self.predictor = None
|
||||
self.output_dir = folder_paths.get_output_directory()
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
input_dir = folder_paths.get_input_directory()
|
||||
files = [
|
||||
f
|
||||
for f in os.listdir(input_dir)
|
||||
if os.path.isfile(os.path.join(input_dir, f))
|
||||
]
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
"model_type": (["vit_h", "vit_l", "vit_b"],),
|
||||
"ckpt": (folder_paths.get_filename_list("sam"),),
|
||||
"embedding_id": (
|
||||
"STRING",
|
||||
{"multiline": False, "default": "embedding"},
|
||||
),
|
||||
# "image": (sorted(files), ),
|
||||
"image_prompts_json": ("STRING", {"multiline": False, "default": "[]"}),
|
||||
},
|
||||
}
|
||||
|
||||
CATEGORY = "image"
|
||||
|
||||
RETURN_TYPES = ("SAM_PROMPT",)
|
||||
FUNCTION = "load_image"
|
||||
|
||||
def load_image(self, image, model_type, ckpt, embedding_id, image_prompts_json):
|
||||
import json
|
||||
|
||||
global global_predictor
|
||||
|
||||
if global_predictor is None:
|
||||
ckpt = folder_paths.get_full_path("sam", ckpt)
|
||||
sam = sam_model_registry[model_type](checkpoint=ckpt)
|
||||
predictor = SamPredictor(sam)
|
||||
global_predictor = predictor
|
||||
|
||||
predictor = global_predictor
|
||||
|
||||
emb_filename = f"{self.output_dir}/{embedding_id}.npy"
|
||||
if not os.path.exists(emb_filename):
|
||||
image_np = (image[0].numpy() * 255).astype(np.uint8)
|
||||
predictor.set_image(image_np)
|
||||
emb = predictor.get_image_embedding().cpu().numpy()
|
||||
np.save(emb_filename, emb)
|
||||
else:
|
||||
emb = np.load(emb_filename)
|
||||
|
||||
with open(f"{self.output_dir}/{embedding_id}.json") as f:
|
||||
data = json.load(f)
|
||||
predictor.input_size = data["input_size"]
|
||||
predictor.features = torch.from_numpy(emb)
|
||||
predictor.is_image_set = True
|
||||
predictor.original_size = data["original_size"]
|
||||
|
||||
image_prompts = json.loads(image_prompts_json)
|
||||
|
||||
result = [image_prompts]
|
||||
|
||||
if isinstance(image_prompts, list):
|
||||
pass
|
||||
elif all(isinstance(item, list) for item in image_prompts.values()):
|
||||
for item in image_prompts.values():
|
||||
if (len(item) == 0):
|
||||
h, w, c = image[0].shape
|
||||
result.append(torch.zeros(1, h, w, c))
|
||||
continue
|
||||
point_coords = np.array([[p['x'], p['y']] for p in item])
|
||||
point_labels = np.array([p['label'] for p in item])
|
||||
|
||||
masks, _, _ = predictor.predict(
|
||||
point_coords=point_coords,
|
||||
point_labels=point_labels,
|
||||
)
|
||||
masks = torch.from_numpy(masks)
|
||||
masks = rearrange(masks[0], 'h w -> 1 h w')
|
||||
out_image = repeat(masks, '1 h w -> 1 h w c', c=3) * image
|
||||
result.append(out_image)
|
||||
return result
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {"SAM": SAM}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {"SAM": "Segmentation (SAM)"}
|
||||
@@ -1,52 +0,0 @@
|
||||
import folder_paths
|
||||
import torch
|
||||
from segment_anything import SamAutomaticMaskGenerator, sam_model_registry
|
||||
from einops import rearrange, repeat
|
||||
|
||||
|
||||
class SAM_Load_Embedding:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"filename": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": "embeddings"
|
||||
}),
|
||||
"embedding_id": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": "embedding"
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("EMBEDDINGS",)
|
||||
RETURN_NAMES = ("EMBEDDINGS",)
|
||||
|
||||
OUTPUT_NODE = True
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "image"
|
||||
|
||||
def process(self, filename, embedding_id):
|
||||
import json
|
||||
import numpy as np
|
||||
|
||||
data = {}
|
||||
with open(filename, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
# Convert list to numpy ndarray
|
||||
data['image_embedding'] = np.array(data['image_embedding'])
|
||||
|
||||
return (data, )
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"SAM_Load_Embedding": SAM_Load_Embedding
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"SAM_Load_Embedding": "SAM_Load_Embedding "
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import folder_paths
|
||||
import torch
|
||||
from segment_anything import SamAutomaticMaskGenerator, sam_model_registry
|
||||
from einops import rearrange, repeat
|
||||
import requests
|
||||
from PIL import Image
|
||||
import io
|
||||
import numpy as np
|
||||
from segment_anything import SamAutomaticMaskGenerator, sam_model_registry, SamPredictor
|
||||
from einops import rearrange, repeat
|
||||
|
||||
class SAM_Remote_Emb:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"model_type": (["vit_h", "vit_l", "vit_b"],),
|
||||
"ckpt": (folder_paths.get_filename_list("sam"),),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("SAM", "SAMPREDICTOR")
|
||||
RETURN_NAMES = ("sam", "predictor")
|
||||
|
||||
FUNCTION = "segment"
|
||||
|
||||
CATEGORY = "image"
|
||||
|
||||
def segment(self, model_type, ckpt):
|
||||
ckpt = folder_paths.get_full_path("sam", ckpt)
|
||||
sam = sam_model_registry[model_type](checkpoint=ckpt)
|
||||
predictor = SamPredictor(sam)
|
||||
|
||||
return (sam, predictor)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"SAM_Loader": SAM_Remote_Emb
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"SAM_Loader": "SAM Loader"
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
import folder_paths
|
||||
import os
|
||||
import numpy as np
|
||||
import torch
|
||||
import re
|
||||
import json
|
||||
from segment_anything import sam_model_registry, SamPredictor
|
||||
from einops import rearrange, repeat
|
||||
from PIL import Image
|
||||
import mediapipe as mp
|
||||
from math import sqrt
|
||||
|
||||
BaseOptions = mp.tasks.BaseOptions
|
||||
FaceLandmarker = mp.tasks.vision.FaceLandmarker
|
||||
FaceLandmarkerOptions = mp.tasks.vision.FaceLandmarkerOptions
|
||||
PoseLandmarker = mp.tasks.vision.PoseLandmarker
|
||||
PoseLandmarkerOptions = mp.tasks.vision.PoseLandmarkerOptions
|
||||
VisionRunningMode = mp.tasks.vision.RunningMode
|
||||
|
||||
global_predictor = None
|
||||
face_landmarker = None
|
||||
pose_landmarker = None
|
||||
|
||||
# For auto-segmentation
|
||||
layerMapping = {
|
||||
"L_eye": {
|
||||
"useMiddle": False,
|
||||
"positiveOffsetX": 0,
|
||||
"positiveOffsetY": 0,
|
||||
"negativeOffsetX": 0,
|
||||
"negativeOffsetY": 0,
|
||||
"positiveScale": 0,
|
||||
"negativeScale": 0.5,
|
||||
"indices": mp.solutions.face_mesh.FACEMESH_LEFT_EYE,
|
||||
},
|
||||
"R_eye": {
|
||||
"useMiddle": False,
|
||||
"positiveOffsetX": 0,
|
||||
"positiveOffsetY": 0,
|
||||
"negativeOffsetX": 0,
|
||||
"negativeOffsetY": 0,
|
||||
"positiveScale": 0,
|
||||
"negativeScale": 0.5,
|
||||
"indices": mp.solutions.face_mesh.FACEMESH_RIGHT_EYE,
|
||||
},
|
||||
"L_iris": {
|
||||
"useMiddle": False,
|
||||
"positiveOffsetX": 0,
|
||||
"positiveOffsetY": 0,
|
||||
"negativeOffsetX": 0,
|
||||
"negativeOffsetY": 0,
|
||||
"positiveScale": -0.2,
|
||||
"negativeScale": 0.5,
|
||||
"indices": mp.solutions.face_mesh.FACEMESH_LEFT_IRIS,
|
||||
},
|
||||
"R_iris": {
|
||||
"useMiddle": False,
|
||||
"positiveOffsetX": 0,
|
||||
"positiveOffsetY": 0,
|
||||
"negativeOffsetX": 0,
|
||||
"negativeOffsetY": 0,
|
||||
"positiveScale": -0.2,
|
||||
"negativeScale": 0.5,
|
||||
"indices": mp.solutions.face_mesh.FACEMESH_RIGHT_IRIS,
|
||||
},
|
||||
"face": {
|
||||
"useMiddle": False,
|
||||
"positiveOffsetX": 0,
|
||||
"positiveOffsetY": 40,
|
||||
"negativeOffsetX": 0,
|
||||
"negativeOffsetY": 60,
|
||||
"positiveScale": 0.2,
|
||||
"negativeScale": 0.6,
|
||||
"indices": mp.solutions.face_mesh.FACEMESH_FACE_OVAL,
|
||||
},
|
||||
"mouth": {
|
||||
"useMiddle": False,
|
||||
"positiveOffsetX": 0,
|
||||
"positiveOffsetY": 0,
|
||||
"negativeOffsetX": 0,
|
||||
"negativeOffsetY": 0,
|
||||
"positiveScale": -0.3,
|
||||
"negativeScale": 0.3,
|
||||
# https://stackoverflow.com/questions/66649492/how-to-get-specific-landmark-of-face-like-lips-or-eyes-using-tensorflow-js-face
|
||||
"indices": [[x, x] for x in [61, 37, 270, 91, 314]],
|
||||
},
|
||||
"mouth_in": {
|
||||
"useMiddle": False,
|
||||
"positiveOffsetX": 0,
|
||||
"positiveOffsetY": 0,
|
||||
"negativeOffsetX": 0,
|
||||
"negativeOffsetY": 0,
|
||||
"positiveScale": -0.5,
|
||||
"negativeScale": 0.5,
|
||||
# https://stackoverflow.com/questions/66649492/how-to-get-specific-landmark-of-face-like-lips-or-eyes-using-tensorflow-js-face
|
||||
"indices": [[x, x] for x in [310, 88]],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class SAMMultiLayer:
|
||||
def __init__(self):
|
||||
self.predictor = None
|
||||
self.output_dir = folder_paths.get_output_directory()
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
"ckpt": (folder_paths.get_filename_list("sams"),),
|
||||
"embedding_id": (
|
||||
"STRING",
|
||||
{"multiline": False, "default": "embedding"},
|
||||
),
|
||||
"image_prompts_json": ("STRING", {"multiline": False, "default": "[]"}),
|
||||
},
|
||||
}
|
||||
|
||||
CATEGORY = "image"
|
||||
|
||||
RETURN_TYPES = ("SAM_PROMPT",)
|
||||
FUNCTION = "load_image"
|
||||
|
||||
def load_models(self, ckpt, model_type):
|
||||
global global_predictor, face_landmarker, pose_landmarker
|
||||
|
||||
ckpt = folder_paths.get_full_path("sams", ckpt)
|
||||
sam = sam_model_registry[model_type](checkpoint=ckpt) # .to("cuda")
|
||||
global_predictor = SamPredictor(sam)
|
||||
|
||||
face_landmarker_model_path = os.path.join(
|
||||
os.path.dirname(__file__), "../mediapipe_models/face_landmarker.task"
|
||||
)
|
||||
face_landmarker_options = FaceLandmarkerOptions(
|
||||
base_options=BaseOptions(model_asset_path=face_landmarker_model_path),
|
||||
running_mode=VisionRunningMode.IMAGE,
|
||||
)
|
||||
face_landmarker = FaceLandmarker.create_from_options(face_landmarker_options)
|
||||
|
||||
pose_landmarker_model_path = os.path.join(
|
||||
os.path.dirname(__file__), "../mediapipe_models/pose_landmarker_full.task"
|
||||
)
|
||||
pose_landmarker_options = PoseLandmarkerOptions(
|
||||
base_options=BaseOptions(model_asset_path=pose_landmarker_model_path),
|
||||
running_mode=VisionRunningMode.IMAGE,
|
||||
)
|
||||
pose_landmarker = PoseLandmarker.create_from_options(pose_landmarker_options)
|
||||
return global_predictor, face_landmarker, pose_landmarker
|
||||
|
||||
def auto_segment(self, image, face_landmarks, pose_landmarks):
|
||||
H, W, C = image.shape
|
||||
imagePromptsMulti = {}
|
||||
boxesMulti = {}
|
||||
|
||||
for key, value in layerMapping.items():
|
||||
positivePoints = []
|
||||
middlePoints = []
|
||||
negativePoints = []
|
||||
|
||||
for index in value["indices"]:
|
||||
start, end = index
|
||||
startPoint = face_landmarks[start]
|
||||
|
||||
startX = startPoint.x * W
|
||||
startY = startPoint.y * H
|
||||
|
||||
if len(middlePoints) == 0:
|
||||
middlePoints.append({"x": startX, "y": startY, "label": 1})
|
||||
else:
|
||||
middlePoints[0]["x"] += startX
|
||||
middlePoints[0]["y"] += startY
|
||||
|
||||
positivePoints.append({"x": startX, "y": startY, "label": 1})
|
||||
|
||||
len_indices = len(value["indices"])
|
||||
middlePoints[0]["x"] /= len_indices
|
||||
middlePoints[0]["y"] /= len_indices
|
||||
|
||||
if value["useMiddle"]:
|
||||
imagePromptsMulti[key] = middlePoints
|
||||
else:
|
||||
for i, index in enumerate(value["indices"]):
|
||||
start, end = index
|
||||
startPoint = face_landmarks[start]
|
||||
|
||||
startX = startPoint.x * W
|
||||
startY = startPoint.y * H
|
||||
|
||||
middlePoint = middlePoints[0]
|
||||
directionVector = {
|
||||
"x": middlePoint["x"] - startX,
|
||||
"y": middlePoint["y"] - startY,
|
||||
}
|
||||
directionVectorLength = sqrt(
|
||||
directionVector["x"] * directionVector["x"]
|
||||
+ directionVector["y"] * directionVector["y"]
|
||||
)
|
||||
|
||||
if value["negativeScale"] != 0:
|
||||
negativePointDistance = (
|
||||
value["negativeScale"] * directionVectorLength
|
||||
)
|
||||
negativePoint = {
|
||||
"x": startX
|
||||
- (negativePointDistance * directionVector["x"])
|
||||
/ directionVectorLength
|
||||
- value["negativeOffsetX"],
|
||||
"y": startY
|
||||
- (negativePointDistance * directionVector["y"])
|
||||
/ directionVectorLength
|
||||
- value["negativeOffsetY"],
|
||||
"label": 0,
|
||||
}
|
||||
negativePoints.append(negativePoint)
|
||||
|
||||
positivePointDistance = (
|
||||
value["positiveScale"] * directionVectorLength
|
||||
)
|
||||
positivePoints[i] = {
|
||||
"x": positivePoints[i]["x"]
|
||||
- (positivePointDistance * directionVector["x"])
|
||||
/ directionVectorLength
|
||||
- value["positiveOffsetX"],
|
||||
"y": positivePoints[i]["y"]
|
||||
- (positivePointDistance * directionVector["y"])
|
||||
/ directionVectorLength
|
||||
- value["positiveOffsetY"],
|
||||
"label": 1,
|
||||
}
|
||||
|
||||
imagePromptsMulti[key] = positivePoints + negativePoints
|
||||
|
||||
points = negativePoints if len(negativePoints) > 0 else positivePoints
|
||||
box = np.array(
|
||||
[
|
||||
min(x["x"] for x in points),
|
||||
min(x["y"] for x in points),
|
||||
max(x["x"] for x in points),
|
||||
max(x["y"] for x in points),
|
||||
]
|
||||
)
|
||||
boxesMulti[key] = box
|
||||
|
||||
if pose_landmarks is not None:
|
||||
positiveBreathX = (
|
||||
(pose_landmarks[11].x + pose_landmarks[12].x) / 2
|
||||
) * W
|
||||
positiveBreathY = (
|
||||
(pose_landmarks[11].y + pose_landmarks[12].y) / 2
|
||||
) * H
|
||||
negativeBreathX1 = pose_landmarks[0].x * W
|
||||
negativeBreathY1 = pose_landmarks[0].y * H
|
||||
negativeBreathX2 = pose_landmarks[9].x * W
|
||||
negativeBreathY2 = pose_landmarks[9].y * H
|
||||
negativeBreathX3 = pose_landmarks[10].x * W
|
||||
negativeBreathY3 = pose_landmarks[10].y * H
|
||||
imagePromptsMulti["breath"] = [
|
||||
{"x": positiveBreathX, "y": positiveBreathY, "label": 1},
|
||||
{"x": negativeBreathX1, "y": negativeBreathY1, "label": 0},
|
||||
{"x": negativeBreathX2, "y": negativeBreathY2, "label": 0},
|
||||
{"x": negativeBreathX3, "y": negativeBreathY3, "label": 0},
|
||||
]
|
||||
|
||||
return imagePromptsMulti, boxesMulti
|
||||
|
||||
def detect_face(self, np_image):
|
||||
global face_landmarker, pose_landmarker
|
||||
mp_image = mp.Image(
|
||||
image_format=mp.ImageFormat.SRGB, data=(np_image * 255).astype(np.uint8)
|
||||
)
|
||||
face_landmarks = face_landmarker.detect(mp_image).face_landmarks
|
||||
face_landmarks = face_landmarks[0] if len(face_landmarks) > 0 else None
|
||||
pose_landmarks = pose_landmarker.detect(mp_image).pose_landmarks
|
||||
pose_landmarks = pose_landmarks[0] if len(pose_landmarks) > 0 else None
|
||||
imagePromptsMulti, boxesMulti = self.auto_segment(
|
||||
np_image, face_landmarks, pose_landmarks
|
||||
)
|
||||
|
||||
return imagePromptsMulti, boxesMulti
|
||||
|
||||
def load_image(self, image, ckpt, embedding_id, image_prompts_json):
|
||||
image_prompts = json.loads(image_prompts_json.replace("'", '"'))
|
||||
|
||||
order_file = f"{self.output_dir}/segments_{embedding_id}/order.json"
|
||||
if os.path.exists(order_file):
|
||||
# Frontend uploads segments images to backend => backend reads all segments images and passes them to next nodes
|
||||
with open(order_file) as f:
|
||||
order = json.load(f)
|
||||
|
||||
result = [image_prompts]
|
||||
|
||||
for segment in order:
|
||||
image = Image.open(
|
||||
f"{self.output_dir}/segments_{embedding_id}/{segment}.png"
|
||||
)
|
||||
image = np.array(image).astype(np.float32) / 255.0
|
||||
image = torch.from_numpy(image)[None,]
|
||||
result.append(image)
|
||||
|
||||
return result
|
||||
else:
|
||||
# Frontend uploads clicks coordinates to backend => backend runs SAM and passes the segments to next nodes
|
||||
model_type = re.findall(r"vit_[lbh]", ckpt)[0]
|
||||
|
||||
global global_predictor
|
||||
if global_predictor is None:
|
||||
global_predictor, _, _ = self.load_models(ckpt, model_type)
|
||||
|
||||
if image.shape[3] == 4:
|
||||
image = image[:, :, :, :3]
|
||||
|
||||
emb_filename = f"{self.output_dir}/{embedding_id}_{model_type}.npy"
|
||||
if not os.path.exists(emb_filename):
|
||||
image_np = (image[0].numpy() * 255).astype(np.uint8)
|
||||
global_predictor.set_image(image_np)
|
||||
emb = global_predictor.get_image_embedding().cpu().numpy()
|
||||
np.save(emb_filename, emb)
|
||||
|
||||
with open(
|
||||
f"{self.output_dir}/{embedding_id}_{model_type}.json", "w"
|
||||
) as f:
|
||||
data = {
|
||||
"input_size": global_predictor.input_size,
|
||||
"original_size": global_predictor.original_size,
|
||||
}
|
||||
json.dump(data, f)
|
||||
else:
|
||||
emb = np.load(emb_filename)
|
||||
|
||||
with open(f"{self.output_dir}/{embedding_id}_{model_type}.json") as f:
|
||||
data = json.load(f)
|
||||
global_predictor.input_size = data["input_size"]
|
||||
global_predictor.features = torch.from_numpy(emb)
|
||||
global_predictor.is_image_set = True
|
||||
global_predictor.original_size = data["original_size"]
|
||||
|
||||
imagePromptsMulti, boxesMulti = self.detect_face(image[0].numpy())
|
||||
|
||||
image_prompts = json.loads(image_prompts_json.replace("'", '"'))
|
||||
result = [image_prompts]
|
||||
|
||||
if isinstance(image_prompts, list):
|
||||
pass
|
||||
elif all(isinstance(item, list) for item in image_prompts.values()):
|
||||
for key, item in image_prompts.items():
|
||||
if len(item) == 0:
|
||||
h, w, c = image[0].shape
|
||||
result.append(torch.zeros(1, h, w, c))
|
||||
continue
|
||||
|
||||
points = (
|
||||
imagePromptsMulti[key] if key in imagePromptsMulti else item
|
||||
)
|
||||
point_coords = np.array([[p["x"], p["y"]] for p in points])
|
||||
point_labels = np.array([p["label"] for p in points])
|
||||
|
||||
masks, _, _ = global_predictor.predict(
|
||||
point_coords=point_coords,
|
||||
point_labels=point_labels,
|
||||
box=boxesMulti[key] if key in boxesMulti else None,
|
||||
)
|
||||
masks = torch.from_numpy(masks)
|
||||
masks = rearrange(masks[0], "h w -> 1 h w")
|
||||
out_image = repeat(masks, "1 h w -> 1 h w c", c=3) * image
|
||||
result.append(out_image)
|
||||
return result
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {"SAM MultiLayer": SAMMultiLayer}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {"SAM MultiLayer": "SAM MultiLayer"}
|
||||
@@ -1,110 +0,0 @@
|
||||
import torch
|
||||
from einops import rearrange, repeat
|
||||
import numpy as np
|
||||
import cv2
|
||||
|
||||
class SAM_Predict:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
"embeddings": ("EMBEDDINGS",),
|
||||
"predictor": ("SAMPREDICTOR",),
|
||||
},
|
||||
"optional": {
|
||||
"prompt": ("SAM_PROMPT", ),
|
||||
"mask": ("IMAGE",),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE","IMAGE", "IMAGE")
|
||||
RETURN_NAMES = ("image","out_image", "mask")
|
||||
|
||||
FUNCTION = "segment"
|
||||
|
||||
CATEGORY = "image"
|
||||
|
||||
def segment(self, image, embeddings, predictor, prompt, mask=None):
|
||||
image_embedding_list = embeddings['image_embedding']
|
||||
shape = tuple(embeddings['shape'])
|
||||
input_size = tuple(embeddings['input_size'])
|
||||
|
||||
# Convert the list back to a numpy array with the original shape
|
||||
image_embedding_np = np.array(image_embedding_list, dtype=np.single).reshape(shape)
|
||||
|
||||
# Convert the numpy array to a PyTorch tensor
|
||||
image_embedding_tensor = torch.from_numpy(image_embedding_np)
|
||||
|
||||
# Set the image embeddings for the model
|
||||
# predictor.set_torch_image(image_embedding_tensor, image.shape[:2])
|
||||
# print(image[0].shape[:2])
|
||||
predictor.input_size = input_size
|
||||
predictor.features = image_embedding_tensor
|
||||
predictor.is_image_set = True
|
||||
predictor.original_size = image[0].shape[:2]
|
||||
|
||||
# prompt = [{"x":364,"y":153,"label":1},{"x":296,"y":189,"label":1},{"x":277,"y":246,"label":1}]
|
||||
|
||||
# if point_1 != None:
|
||||
# x, y, z = point_1
|
||||
|
||||
# point_coords = np.array([[x, y]])
|
||||
# point_labels = np.array([1])
|
||||
|
||||
if prompt == None or len(prompt) == 0:
|
||||
return (image, image, None)
|
||||
|
||||
point_coords = np.array([[p['x'], p['y']] for p in prompt])
|
||||
point_labels = np.array([p['label'] for p in prompt])
|
||||
|
||||
masks, iou_predictions, low_res_masks = predictor.predict(
|
||||
point_coords=point_coords,
|
||||
point_labels=point_labels,
|
||||
)
|
||||
|
||||
if mask != None:
|
||||
# scale the mask to 256x256
|
||||
cv2_mask = cv2.resize(np.array(mask[0]), (256, 256))
|
||||
cv2_mask = cv2_mask[np.newaxis, :, :]
|
||||
cv2_mask = (cv2_mask * 255).astype(int)
|
||||
|
||||
true_locations = np.array(np.where(cv2_mask[0] == 255))
|
||||
if true_locations.shape[1] > 0:
|
||||
# Randomly select a point in the mask
|
||||
rand_index = np.random.randint(true_locations.shape[1])
|
||||
y, x = true_locations[:, rand_index]
|
||||
point_coords = np.array([[x, y]])
|
||||
point_labels = np.array([1])
|
||||
|
||||
masks, iou_predictions, low_res_masks = predictor.predict(
|
||||
point_coords=point_coords,
|
||||
point_labels=point_labels,
|
||||
mask_input=cv2_mask
|
||||
)
|
||||
else:
|
||||
# No detected mask
|
||||
h, w, c = image[0].shape
|
||||
masks = np.zeros((1, h, w))
|
||||
|
||||
masks = torch.from_numpy(masks)
|
||||
|
||||
masks = rearrange(masks[0], 'h w -> 1 h w')
|
||||
# masks = rearrange(masks, 'c h w -> 1 c h w')
|
||||
out_image = repeat(masks, '1 h w -> 1 h w c', c=3) * image
|
||||
|
||||
print(masks.shape, torch.max(masks), torch.min(masks))
|
||||
print(image.shape, torch.max(image), torch.min(image))
|
||||
|
||||
# print(emb)
|
||||
|
||||
# print(masks, out_image)
|
||||
return (image, out_image, masks)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"SAM_Predict": SAM_Predict
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"SAM_Predict": "SAM Predict"
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
import requests
|
||||
from PIL import Image
|
||||
import io
|
||||
import numpy as np
|
||||
import folder_paths
|
||||
|
||||
class SAM_Embedding:
|
||||
def __init__(self):
|
||||
self.output_dir = folder_paths.get_output_directory()
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
"embedding_id": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": "embedding"
|
||||
}),
|
||||
},
|
||||
"optional": {
|
||||
"predictor": ("SAMPREDICTOR",)
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("EMBEDDINGS", )
|
||||
RETURN_NAMES = ("embeddings", )
|
||||
|
||||
FUNCTION = "segment"
|
||||
|
||||
CATEGORY = "image"
|
||||
|
||||
def segment(self, image, embedding_id, predictor=None):
|
||||
# Convert PyTorch tensor to numpy array
|
||||
image_np = (image[0].numpy() * 255).astype(np.uint8)
|
||||
|
||||
if predictor != None:
|
||||
predictor.set_image(image_np)
|
||||
emb = predictor.get_image_embedding().cpu().numpy()
|
||||
output = {
|
||||
"image_embedding": emb,
|
||||
"shape": emb.shape,
|
||||
"input_size": predictor.input_size
|
||||
}
|
||||
else:
|
||||
# Convert numpy array to PIL Image
|
||||
img = Image.fromarray(image_np)
|
||||
|
||||
# Create an in-memory bytes buffer
|
||||
img_byte_arr = io.BytesIO()
|
||||
|
||||
# Save the PIL Image to the bytes buffer in PNG format
|
||||
img.save(img_byte_arr, format='PNG')
|
||||
|
||||
# Get the bytes value of the buffer
|
||||
img_byte_arr = img_byte_arr.getvalue()
|
||||
|
||||
# Create a dictionary with the image bytes
|
||||
files = {'image': ('image.png', img_byte_arr)}
|
||||
|
||||
# Send the POST request
|
||||
response = requests.post('https://avatechgg--segment-anything-entrypoint.modal.run', files=files)
|
||||
|
||||
# Check if the request was successful
|
||||
if response.status_code == 200:
|
||||
# Parse the JSON response
|
||||
output = response.json()
|
||||
else:
|
||||
print(f"Request failed with status code {response.status_code}")
|
||||
output = None
|
||||
|
||||
# sam = sam_model_registry[model_type](checkpoint=ckpt)
|
||||
# predictor = SamPredictor(sam)
|
||||
# masks = predictor.set_torch_image
|
||||
# masks = predictor.predict
|
||||
|
||||
# print(output)
|
||||
|
||||
np.save(f"{self.output_dir}/{embedding_id}.npy", output["image_embedding"])
|
||||
return (output, )
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"SAM_Embedding": SAM_Embedding
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"SAM_Embedding": "SAM Embedding"
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
import folder_paths
|
||||
import os
|
||||
import numpy as np
|
||||
from segment_anything import sam_model_registry, SamPredictor
|
||||
|
||||
|
||||
class SAM_Prompt_Image:
|
||||
def __init__(self):
|
||||
self.output_dir = folder_paths.get_output_directory()
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
input_dir = folder_paths.get_input_directory()
|
||||
files = [
|
||||
f
|
||||
for f in os.listdir(input_dir)
|
||||
if os.path.isfile(os.path.join(input_dir, f))
|
||||
]
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
"model_type": (["vit_h", "vit_l", "vit_b"],),
|
||||
"ckpt": (folder_paths.get_filename_list("sam"),),
|
||||
"embedding_id": (
|
||||
"STRING",
|
||||
{"multiline": False, "default": "embedding"},
|
||||
),
|
||||
# "image": (sorted(files), ),
|
||||
"image_prompts_json": ("STRING", {"multiline": False, "default": "[]"}),
|
||||
},
|
||||
}
|
||||
|
||||
CATEGORY = "image"
|
||||
|
||||
RETURN_TYPES = ("SAM_PROMPT",)
|
||||
FUNCTION = "load_image"
|
||||
|
||||
def load_image(self, image, model_type, ckpt, embedding_id, image_prompts_json):
|
||||
import json
|
||||
|
||||
emb_filename = f"{self.output_dir}/{embedding_id}.npy"
|
||||
if not os.path.exists(emb_filename):
|
||||
ckpt = folder_paths.get_full_path("sam", ckpt)
|
||||
sam = sam_model_registry[model_type](checkpoint=ckpt)
|
||||
predictor = SamPredictor(sam)
|
||||
|
||||
image_np = (image[0].numpy() * 255).astype(np.uint8)
|
||||
predictor.set_image(image_np)
|
||||
emb = predictor.get_image_embedding().cpu().numpy()
|
||||
np.save(emb_filename, emb)
|
||||
|
||||
image_prompts = json.loads(image_prompts_json)
|
||||
|
||||
result = (image_prompts,)
|
||||
|
||||
if isinstance(image_prompts, list):
|
||||
pass
|
||||
elif all(isinstance(item, list) for item in image_prompts.values()):
|
||||
for item in image_prompts.values():
|
||||
result += (item,)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {"SAM_Prompt_Image": SAM_Prompt_Image}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {"SAM_Prompt_Image": "SAM_Prompt_Image "}
|
||||
@@ -1,64 +0,0 @@
|
||||
import folder_paths
|
||||
import torch
|
||||
import os
|
||||
from segment_anything import SamAutomaticMaskGenerator, sam_model_registry
|
||||
from einops import rearrange, repeat
|
||||
|
||||
class SAM_Save_Embedding:
|
||||
def __init__(self):
|
||||
self.output_dir = folder_paths.get_output_directory()
|
||||
self.type = "output"
|
||||
self.prefix_append = ""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"embeddings": ("EMBEDDINGS",),
|
||||
"filename": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": "embeddings"
|
||||
}),
|
||||
"write_mode": (["Overwrite", "Increment"],),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ()
|
||||
RETURN_NAMES = ()
|
||||
|
||||
OUTPUT_NODE = True
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "image"
|
||||
|
||||
def process(self, embeddings, filename, write_mode):
|
||||
import json
|
||||
import numpy as np
|
||||
|
||||
filepath = self.output_dir + "/" + filename + '.json'
|
||||
|
||||
if write_mode == "Increment":
|
||||
count = 0
|
||||
# while file exists, increment count
|
||||
while os.path.exists(self.output_dir + "/" + filename + '_' + str(count) + '.json'):
|
||||
count += 1
|
||||
|
||||
filepath = self.output_dir + "/" + filename + '_' + str(count) + '.json'
|
||||
|
||||
# print(embeddings)
|
||||
if isinstance(embeddings['image_embedding'], np.ndarray):
|
||||
embeddings['image_embedding'] = embeddings['image_embedding'].tolist()
|
||||
with open(filepath, 'w') as f:
|
||||
json.dump(embeddings, f)
|
||||
|
||||
return { "ui" : { "file": { filepath.replace(f"{self.output_dir}/", "") } } }
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"SAM_Save_Embedding": SAM_Save_Embedding
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"SAM_Save_Embedding": "SAM_Save_Embedding "
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const chokidar = require('chokidar');
|
||||
const path = require('path');
|
||||
|
||||
// Initialize watcher.
|
||||
const watcher = chokidar.watch(path.resolve(__dirname, './js/output.css'), {
|
||||
persistent: true
|
||||
});
|
||||
|
||||
// Add event listeners.
|
||||
watcher
|
||||
.on('change', filePath => {
|
||||
console.log(`File ${filePath} has been changed`);
|
||||
let cssContent = fs.readFileSync(filePath, 'utf8');
|
||||
let jsContent = `let style = document.createElement('style');
|
||||
style.innerHTML = \`${cssContent.replace(/\\/g, '\\\\')}\`;
|
||||
document.head.appendChild(style);`;
|
||||
fs.writeFileSync(path.resolve(__dirname, './js/style-inject.js'), jsContent);
|
||||
});
|
||||
|
||||
// On script exit, close the watcher
|
||||
process.on('exit', (code) => {
|
||||
watcher.close();
|
||||
});
|
||||
@@ -3,6 +3,9 @@ module.exports = {
|
||||
// content: ['./js/**/*.{html,js}'],
|
||||
content: ['./js/**/*.{html,js}'],
|
||||
theme: {
|
||||
fontFamily: {
|
||||
'gabarito': ['Gabarito'],
|
||||
},
|
||||
extend: {},
|
||||
},
|
||||
daisyui: {
|
||||
|
||||
Reference in New Issue
Block a user