Compare commits
333
Commits
heiume-patch-2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acc0a8d1cc | ||
|
|
82e3f4dbec | ||
|
|
10d93e0823 | ||
|
|
2ded553837 | ||
|
|
00a7d5a26c | ||
|
|
980408b363 | ||
|
|
eaaab6e346 | ||
|
|
eccc4bca31 | ||
|
|
8ed66b3260 | ||
|
|
7d0cfb6e5b | ||
|
|
a9c3075b70 | ||
|
|
0f29e2c27d | ||
|
|
b3ee46fdf2 | ||
|
|
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
|
# Created by https://www.toptal.com/developers/gitignore/api/node,python,react
|
||||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node,python,react
|
# Edit at https://www.toptal.com/developers/gitignore?templates=node,python,react
|
||||||
|
|
||||||
|
workflow_templates/
|
||||||
js/output.css
|
js/output.css
|
||||||
|
*.task
|
||||||
|
|
||||||
### Node ###
|
### Node ###
|
||||||
# Logs
|
# Logs
|
||||||
|
|||||||
@@ -1,44 +1,170 @@
|
|||||||
# avatar-graph-comfyui
|
# 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
|
# 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`
|
1. `cd custom_nodes`
|
||||||
|
|
||||||
2. `git clone https://github.com/avatechgg/avatar-graph-comfyui.git`
|
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:
|
4. Restart ComfyUI with enable-cors-header `python main.py --enable-cors-header` or (for mac) `python main.py --force-fp16 --enable-cors-header`
|
||||||
```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`
|
|
||||||
|
|
||||||
# Development
|
# Development
|
||||||
|
|
||||||
|
If you are interested in contributing
|
||||||
|
|
||||||
For comfyui frontend extension, frontend js located at `avatar-graph-comfyui/js`
|
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)
|
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.
|
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
|
```json
|
||||||
{
|
{
|
||||||
"tailwindCSS.experimental.classRegex": [
|
"tailwindCSS.experimental.classRegex": [
|
||||||
["class\\s?:\\s?([\\s\\S]*)", "(?:\"|')([^\"']*)(?:\"|')"]
|
["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
|
@nickname: Avatar Graph
|
||||||
@description: Include nodes for sam + bpy operation, that allows workflow creations for generative 2d character rig.
|
@description: Include nodes for sam + bpy operation, that allows workflow creations for generative 2d character rig.
|
||||||
"""
|
"""
|
||||||
import routes
|
|
||||||
|
|
||||||
import inspect
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
sys.path.append(os.path.join(os.path.dirname(__file__)))
|
||||||
|
|
||||||
|
import routes
|
||||||
|
import inspect
|
||||||
|
import sys
|
||||||
import importlib
|
import importlib
|
||||||
import subprocess
|
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__))
|
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):
|
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):
|
def append_to_sys_path(path):
|
||||||
@@ -38,16 +44,67 @@ def append_to_sys_path(path):
|
|||||||
sys.path.append(path)
|
sys.path.append(path)
|
||||||
|
|
||||||
|
|
||||||
def create_sam_model_dir():
|
folder_paths.folder_names_and_paths["sams"] = (
|
||||||
model_dir = os.path.join(ag_path, "../../models/sam")
|
[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):
|
if not os.path.isdir(model_dir):
|
||||||
os.makedirs(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 = []
|
files = []
|
||||||
|
|
||||||
for path in paths:
|
for path in paths:
|
||||||
@@ -59,6 +116,7 @@ NODE_CLASS_MAPPINGS = {}
|
|||||||
NODE_DISPLAY_NAME_MAPPINGS = {}
|
NODE_DISPLAY_NAME_MAPPINGS = {}
|
||||||
|
|
||||||
import blender_node
|
import blender_node
|
||||||
|
|
||||||
base_class = blender_node.ObjectOps
|
base_class = blender_node.ObjectOps
|
||||||
|
|
||||||
# Import all the modules and append their mappings
|
# Import all the modules and append their mappings
|
||||||
@@ -69,22 +127,20 @@ for file in files:
|
|||||||
if inspect.isclass(obj):
|
if inspect.isclass(obj):
|
||||||
if issubclass(obj, base_class) and obj != base_class:
|
if issubclass(obj, base_class) and obj != base_class:
|
||||||
NODE_CLASS_MAPPINGS.update(obj.NODE_CLASS_MAPPINGS())
|
NODE_CLASS_MAPPINGS.update(obj.NODE_CLASS_MAPPINGS())
|
||||||
NODE_DISPLAY_NAME_MAPPINGS.update(
|
NODE_DISPLAY_NAME_MAPPINGS.update(obj.NODE_DISPLAY_NAME_MAPPINGS())
|
||||||
obj.NODE_DISPLAY_NAME_MAPPINGS())
|
|
||||||
|
|
||||||
if (hasattr(module, 'BLENDER_NODES')):
|
if hasattr(module, "BLENDER_NODES"):
|
||||||
for node in module.BLENDER_NODES:
|
for node in module.BLENDER_NODES:
|
||||||
# print(node)
|
# print(node)
|
||||||
# NODE_CLASS_MAPPINGS.update({node: module.BLENDER_NODES[node]})
|
# NODE_CLASS_MAPPINGS.update({node: module.BLENDER_NODES[node]})
|
||||||
# NODE_DISPLAY_NAME_MAPPINGS.update({node: node})
|
# NODE_DISPLAY_NAME_MAPPINGS.update({node: node})
|
||||||
NODE_CLASS_MAPPINGS.update(node.NODE_CLASS_MAPPINGS())
|
NODE_CLASS_MAPPINGS.update(node.NODE_CLASS_MAPPINGS())
|
||||||
NODE_DISPLAY_NAME_MAPPINGS.update(
|
NODE_DISPLAY_NAME_MAPPINGS.update(node.NODE_DISPLAY_NAME_MAPPINGS())
|
||||||
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)
|
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)
|
NODE_DISPLAY_NAME_MAPPINGS.update(module.NODE_DISPLAY_NAME_MAPPINGS)
|
||||||
|
|
||||||
WEB_DIRECTORY = "js"
|
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 platform
|
||||||
import blender_node
|
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
|
import folder_paths
|
||||||
|
|
||||||
global_blender_path = ''
|
global_blender_path = ''
|
||||||
@@ -33,6 +33,9 @@ class AvatarMainOutput(blender_node.ObjectOps):
|
|||||||
"open_in_blender": ("BOOLEAN", {
|
"open_in_blender": ("BOOLEAN", {
|
||||||
"default": False
|
"default": False
|
||||||
}),
|
}),
|
||||||
|
"auto_save": ("BOOLEAN", {
|
||||||
|
"default": False
|
||||||
|
}),
|
||||||
"blender_path_override": ("STRING", {
|
"blender_path_override": ("STRING", {
|
||||||
"multiline": False,
|
"multiline": False,
|
||||||
"default": ''
|
"default": ''
|
||||||
@@ -42,16 +45,18 @@ class AvatarMainOutput(blender_node.ObjectOps):
|
|||||||
"multiline": False,
|
"multiline": False,
|
||||||
"default": "out"
|
"default": "out"
|
||||||
}),
|
}),
|
||||||
"model_type": (["GLB", "GLTF_EMBEDDED"],),
|
"model_type": (["AVA","GLB", "GLTF_EMBEDDED"],),
|
||||||
"write_mode": (["Overwrite", "Increment"],),
|
"write_mode": (["Overwrite", "Increment"],),
|
||||||
|
"upload_to_cloud": ("BOOLEAN", {
|
||||||
"blendshapes": ("blendshapes",),
|
"default": False
|
||||||
|
}),
|
||||||
|
"SHAPE_FLOW": ("SHAPE_FLOW",),
|
||||||
}
|
}
|
||||||
|
|
||||||
OUTPUT_NODE = True
|
OUTPUT_NODE = True
|
||||||
RETURN_TYPES = ()
|
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:
|
if open_in_blender:
|
||||||
p = blender_path_override if blender_path_override else global_blender_path
|
p = blender_path_override if blender_path_override else global_blender_path
|
||||||
output_file = self.output_dir + '/tmp.blend'
|
output_file = self.output_dir + '/tmp.blend'
|
||||||
@@ -63,7 +68,30 @@ class AvatarMainOutput(blender_node.ObjectOps):
|
|||||||
objs = BPY_OBJS if BPY_OBJS else [BPY_OBJ]
|
objs = BPY_OBJS if BPY_OBJS else [BPY_OBJ]
|
||||||
|
|
||||||
filepath = export_gltf(
|
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",
|
||||||
|
"type": "output"
|
||||||
|
},],
|
||||||
|
"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": {
|
"required": {
|
||||||
"x": ("FLOAT", {
|
"x": ("FLOAT", {
|
||||||
"default": 0,
|
"default": 0,
|
||||||
"step": 0.1,
|
"step": 0.01,
|
||||||
"display": "number"
|
"display": "number"
|
||||||
}),
|
}),
|
||||||
"y": ("FLOAT", {
|
"y": ("FLOAT", {
|
||||||
"default": 0,
|
"default": 0,
|
||||||
"step": 0.1,
|
"step": 0.01,
|
||||||
"display": "number"
|
"display": "number"
|
||||||
}),
|
}),
|
||||||
"z": ("FLOAT", {
|
"z": ("FLOAT", {
|
||||||
"default": 0,
|
"default": 0,
|
||||||
"step": 0.1,
|
"step": 0.01,
|
||||||
"display": "number"
|
"display": "number"
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,17 +5,17 @@ class VECTOR4D:
|
|||||||
"required": {
|
"required": {
|
||||||
"x": ("FLOAT", {
|
"x": ("FLOAT", {
|
||||||
"default": 0,
|
"default": 0,
|
||||||
"step": 0.1,
|
"step": 0.01,
|
||||||
"display": "number"
|
"display": "number"
|
||||||
}),
|
}),
|
||||||
"y": ("FLOAT", {
|
"y": ("FLOAT", {
|
||||||
"default": 0,
|
"default": 0,
|
||||||
"step": 0.1,
|
"step": 0.01,
|
||||||
"display": "number"
|
"display": "number"
|
||||||
}),
|
}),
|
||||||
"z": ("FLOAT", {
|
"z": ("FLOAT", {
|
||||||
"default": 0,
|
"default": 0,
|
||||||
"step": 0.1,
|
"step": 0.01,
|
||||||
"display": "number"
|
"display": "number"
|
||||||
}),
|
}),
|
||||||
"u": ("FLOAT", {
|
"u": ("FLOAT", {
|
||||||
|
|||||||
+41
-16
@@ -1,5 +1,8 @@
|
|||||||
import inspect
|
import inspect
|
||||||
import re
|
import re
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
BPY_OBJS = "BPY_OBJS"
|
BPY_OBJS = "BPY_OBJS"
|
||||||
BPY_OBJ = "BPY_OBJ"
|
BPY_OBJ = "BPY_OBJ"
|
||||||
|
|
||||||
@@ -7,6 +10,14 @@ BPY_OBJS_TYPE = {
|
|||||||
BPY_OBJS: (BPY_OBJS,),
|
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:
|
class ObjectOps:
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -38,16 +49,29 @@ class ObjectOps:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
import global_bpy
|
if type_generation:
|
||||||
bpy = global_bpy.get_bpy()
|
import global_bpy
|
||||||
result = {
|
|
||||||
"required": {},
|
bpy = global_bpy.get_bpy()
|
||||||
"optional": {
|
result = {
|
||||||
**cls.get_base_input_types(bpy),
|
"required": {},
|
||||||
**cls.get_extra_input_types(bpy)
|
"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
|
@classmethod
|
||||||
def NODE_CLASS_MAPPINGS(cls):
|
def NODE_CLASS_MAPPINGS(cls):
|
||||||
@@ -76,14 +100,14 @@ class ObjectOps:
|
|||||||
import global_bpy
|
import global_bpy
|
||||||
bpy = global_bpy.get_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"]
|
bpy.context.view_layer.objects.active = props["BPY_OBJ"]
|
||||||
|
|
||||||
results = self.blender_process(bpy, **props)
|
results = self.blender_process(bpy, **props)
|
||||||
|
|
||||||
if results is None:
|
if results is None:
|
||||||
print(results)
|
# print(results)
|
||||||
if props.get("BPY_OBJ") != None:
|
if props.get("BPY_OBJ") is not None:
|
||||||
return (props["BPY_OBJ"], )
|
return (props["BPY_OBJ"], )
|
||||||
else:
|
else:
|
||||||
return (bpy.context.view_layer.objects.active, )
|
return (bpy.context.view_layer.objects.active, )
|
||||||
@@ -190,12 +214,12 @@ def print_blender_functions(path):
|
|||||||
if ag_path not in sys.path:
|
if ag_path not in sys.path:
|
||||||
sys.path.append(ag_path)
|
sys.path.append(ag_path)
|
||||||
|
|
||||||
import global_bpy
|
# import global_bpy
|
||||||
bpy = global_bpy.get_bpy()
|
# bpy = global_bpy.get_bpy()
|
||||||
|
|
||||||
# add a an empty object
|
# 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=''):
|
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):
|
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])
|
# print(BPY_OBJ,name, BPY_OBJ[name])
|
||||||
return None
|
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):
|
def INPUT_TYPES(s):
|
||||||
return {
|
return {
|
||||||
"required": {
|
"required": {
|
||||||
"blendshapes": ("STRING", {
|
"shape_flow": ("STRING", {
|
||||||
"multiline": False,
|
"multiline": False,
|
||||||
"default": "{node: ''}"
|
"default": '{"nodes": []}'
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
RETURN_TYPES = ("blendshapes",)
|
RETURN_TYPES = ("SHAPE_FLOW",)
|
||||||
RETURN_NAMES = ("blendshapes",)
|
|
||||||
FUNCTION = "process"
|
FUNCTION = "process"
|
||||||
|
|
||||||
CATEGORY = "mesh"
|
CATEGORY = "mesh"
|
||||||
|
|
||||||
def process(self, blendshapes):
|
def process(self, shape_flow):
|
||||||
return (blendshapes,)
|
return (shape_flow,)
|
||||||
|
|
||||||
# A dictionary that contains all nodes you want to export with their names
|
# A dictionary that contains all nodes you want to export with their names
|
||||||
# NOTE: names should be globally unique
|
# 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 atexit
|
||||||
import subprocess
|
import subprocess
|
||||||
import os
|
import os
|
||||||
|
import folder_paths
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
def genreate_mesh_from_texture(bpy, image):
|
def genreate_mesh_from_texture(bpy, image):
|
||||||
import torch
|
import torch
|
||||||
@@ -14,6 +17,9 @@ def genreate_mesh_from_texture(bpy, image):
|
|||||||
contours, _ = cv2.findContours(
|
contours, _ = cv2.findContours(
|
||||||
gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
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
|
# Get the largest contour
|
||||||
areas = [cv2.contourArea(contour) for contour in contours]
|
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)
|
cv2.drawContours(image, contours, -1, (0, 255, 0), 3)
|
||||||
|
|
||||||
# Convert image back to tensor
|
# Convert image back to tensor
|
||||||
image = [torch.from_numpy(image)]
|
image = torch.from_numpy(np.expand_dims(image, axis=0))
|
||||||
return (image, meshes[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
|
# Create an image with the required dimensions
|
||||||
img = bpy.data.images.new(
|
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 there is no alpha channel, append one full of 1's
|
||||||
if texture.shape[2] == 3:
|
if texture.shape[2] == 3:
|
||||||
@@ -118,6 +124,7 @@ def assign_texture(bpy, BPY_OBJ, texture, texture_name):
|
|||||||
# Create a material
|
# Create a material
|
||||||
mat = bpy.data.materials.new("MaterialName")
|
mat = bpy.data.materials.new("MaterialName")
|
||||||
mat.use_nodes = True
|
mat.use_nodes = True
|
||||||
|
mat.blend_method = 'BLEND'
|
||||||
nodes = mat.node_tree.nodes
|
nodes = mat.node_tree.nodes
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
nodes.remove(node)
|
nodes.remove(node)
|
||||||
@@ -138,6 +145,8 @@ def assign_texture(bpy, BPY_OBJ, texture, texture_name):
|
|||||||
texture_node.outputs['Color'])
|
texture_node.outputs['Color'])
|
||||||
links.new(output_node.inputs['Surface'], bsdf_node.outputs['BSDF'])
|
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
|
# Assign the material to the active object
|
||||||
if obj.data.materials:
|
if obj.data.materials:
|
||||||
obj.data.materials[0] = mat
|
obj.data.materials[0] = mat
|
||||||
@@ -196,7 +205,7 @@ def kill_blender_process():
|
|||||||
process.kill()
|
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
|
import global_bpy
|
||||||
bpy = global_bpy.get_bpy()
|
bpy = global_bpy.get_bpy()
|
||||||
# print(bpy, bpy_objects)
|
# 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["selected_objects"] = list(bpy_objects)
|
||||||
override["active_object"] = list(bpy_objects)[0]
|
override["active_object"] = list(bpy_objects)[0]
|
||||||
|
|
||||||
|
bpy_objects[0]["metadata"] = metadata
|
||||||
for obj in bpy_objects:
|
for obj in bpy_objects:
|
||||||
obj.select_set(True)
|
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":
|
if write_mode == "Increment":
|
||||||
count = 0
|
count = 0
|
||||||
# while file exists, increment count
|
# 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
|
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):
|
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)
|
# 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?version=v2")
|
||||||
|
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 = {
|
EXTRA_INPUT_TYPES = {
|
||||||
"image": ("IMAGE",),
|
"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}),
|
"shape_threshold": ("FLOAT", {"display": "number", "default": 0.7}),
|
||||||
"mesh_layer_name": ("STRING", {"default": "mesh_layer"}),
|
"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_x": ("FLOAT", {"display": "number", "default": 0}),
|
||||||
"extrude_y": ("FLOAT", {"display": "number", "default": 0}),
|
"extrude_y": ("FLOAT", {"display": "number", "default": 0}),
|
||||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||||
@@ -19,22 +22,27 @@ class Object_CreateMeshLayer(blender_node.ObjectOps):
|
|||||||
|
|
||||||
RETURN_TYPES = ("BPY_OBJ", "IMAGE")
|
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)
|
image, BPY_OBJ = genreate_mesh_from_texture(bpy, image)
|
||||||
|
|
||||||
bpy.context.view_layer.objects.active = BPY_OBJ
|
bpy.context.view_layer.objects.active = BPY_OBJ
|
||||||
|
|
||||||
self.edit_mode(bpy)
|
self.edit_mode(bpy)
|
||||||
bpy.ops.mesh.convex_hull(
|
|
||||||
delete_unused=True, use_existing_faces=True,
|
if convex_hull:
|
||||||
face_threshold=face_threshold,
|
bpy.ops.mesh.convex_hull(
|
||||||
shape_threshold=shape_threshold,
|
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.delete(type='EDGE_FACE')
|
||||||
bpy.ops.mesh.select_all(action='SELECT')
|
bpy.ops.mesh.select_all(action='SELECT')
|
||||||
bpy.ops.mesh.edge_face_add()
|
bpy.ops.mesh.edge_face_add()
|
||||||
|
|
||||||
|
bpy.ops.transform.resize(value=(float(scale_x), float(scale_y), 1))
|
||||||
|
|
||||||
bpy.context.object.vertex_groups.new(name=mesh_layer_name)
|
bpy.context.object.vertex_groups.new(name=mesh_layer_name)
|
||||||
bpy.ops.object.vertex_group_assign()
|
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,)
|
RETURN_TYPES = (blender_node.BPY_OBJS,)
|
||||||
|
|
||||||
def blender_process(self, bpy, BPY_OBJ, BPY_OBJ2, **props):
|
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"
|
CUSTOM_NAME = "Join Meshes"
|
||||||
|
|
||||||
def blender_process(self, bpy, BPY_OBJ, **props):
|
def blender_process(self, bpy, BPY_OBJ, **props):
|
||||||
|
prop_values = props.values()
|
||||||
prop_values = props.values()
|
for obj in list(prop_values) + [BPY_OBJ]:
|
||||||
|
if obj is not None:
|
||||||
override = bpy.context.copy()
|
obj.select_set(True)
|
||||||
override["active_object"] = BPY_OBJ
|
if bpy.context.view_layer.objects is not None:
|
||||||
override["selected_editable_objects"] = list(prop_values) + [BPY_OBJ]
|
bpy.context.view_layer.objects.active = BPY_OBJ
|
||||||
bpy.ops.object.join(override)
|
bpy.ops.object.join()
|
||||||
|
|
||||||
return (BPY_OBJ,)
|
return (BPY_OBJ,)
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ class Object_MatchTextureAspectRatio(blender_node.EditOps):
|
|||||||
|
|
||||||
EXTRA_INPUT_TYPES = {
|
EXTRA_INPUT_TYPES = {
|
||||||
"image": ("IMAGE",),
|
"image": ("IMAGE",),
|
||||||
"scale": ('FLOAT', {'default': 0.001, })
|
"scale": ('FLOAT', {'default': 0.001, "display": "number" , "step": 0.001})
|
||||||
}
|
}
|
||||||
|
|
||||||
CUSTOM_NAME = "Match Texture Aspect Ratio"
|
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
|
# Create a new array of verts with sk as co and verts' groups
|
||||||
new_verts = []
|
new_verts = []
|
||||||
for i, vert in enumerate(verts):
|
for i, vert in enumerate(verts):
|
||||||
print(i)
|
# print(i)
|
||||||
print(sk.data[i].co)
|
# print(sk.data[i].co)
|
||||||
new_vert = {
|
new_vert = {
|
||||||
'co': sk.data[i].co,
|
'co': sk.data[i].co,
|
||||||
'groups': vert.groups
|
'groups': vert.groups
|
||||||
@@ -129,6 +129,6 @@ class Mesh_ModifyShapeKey(blender_node.ObjectOps):
|
|||||||
new_pos = center + vec
|
new_pos = center + vec
|
||||||
|
|
||||||
# Apply new position
|
# Apply new position
|
||||||
print(i, vert['co'], new_pos, weight)
|
# print(i, vert['co'], new_pos, weight)
|
||||||
|
|
||||||
sk.data[i].co = new_pos
|
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 = {
|
EXTRA_INPUT_TYPES = {
|
||||||
"image": ("IMAGE",),
|
"image": ("IMAGE",),
|
||||||
"scale": ('FLOAT', {'default': 1, }),
|
"scale": ('FLOAT', {'default': 1, "display": "number", "step": 0.01}),
|
||||||
"texture_name": ('STRING', {'default': 'Texture', })
|
"texture_name": ('STRING', {'default': 'Texture', })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import blender_node
|
||||||
|
|
||||||
|
|
||||||
|
class Mesh_SetShapeKeyValue(blender_node.ObjectOps):
|
||||||
|
|
||||||
|
CUSTOM_NAME = "Set Shape Key Value"
|
||||||
|
|
||||||
|
EXTRA_INPUT_TYPES = {
|
||||||
|
"shape_key_name": ("STRING", {
|
||||||
|
"multiline": False,
|
||||||
|
"default": "my_shape_key",
|
||||||
|
}),
|
||||||
|
"value": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01, "display": "number"}),
|
||||||
|
}
|
||||||
|
|
||||||
|
def blender_process(self, bpy, BPY_OBJ, shape_key_name, value):
|
||||||
|
# Check if the object has shape keys
|
||||||
|
if BPY_OBJ.data.shape_keys:
|
||||||
|
# Check if the specified shape key exists
|
||||||
|
if shape_key_name in BPY_OBJ.data.shape_keys.key_blocks:
|
||||||
|
BPY_OBJ.data.shape_keys.key_blocks[shape_key_name].value = float(value)
|
||||||
|
else:
|
||||||
|
print(f"The shape key {shape_key_name} does not exist on the object.")
|
||||||
|
else:
|
||||||
|
print("The object does not have any shape keys.")
|
||||||
|
return (BPY_OBJ,)
|
||||||
@@ -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,132 @@
|
|||||||
|
import blender_node
|
||||||
|
import math
|
||||||
|
import folder_paths
|
||||||
|
import torch
|
||||||
|
import numpy as np
|
||||||
|
import os
|
||||||
|
from PIL import Image, ImageOps
|
||||||
|
|
||||||
|
def get_incremented_filename(folder_path, base_filename):
|
||||||
|
# Initialize the counter and create the full initial path
|
||||||
|
counter = 0
|
||||||
|
output_path = f"{folder_path}/{base_filename}.png"
|
||||||
|
|
||||||
|
# Check if the file exists and increment the counter until the file does not exist
|
||||||
|
while os.path.exists(output_path):
|
||||||
|
counter += 1
|
||||||
|
output_path = f"{folder_path}/{base_filename}_{counter}.png"
|
||||||
|
|
||||||
|
return output_path
|
||||||
|
|
||||||
|
class BlenderRenderImage(blender_node.ObjectOps):
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
EXTRA_INPUT_TYPES = {
|
||||||
|
}
|
||||||
|
|
||||||
|
# OUTPUT_NODE = True
|
||||||
|
RETURN_TYPES = ("BPY_OBJ", "IMAGE")
|
||||||
|
|
||||||
|
def add_light(self, bpy):
|
||||||
|
# Check if there is at least one light source in the scene
|
||||||
|
light_exists = any(ob for ob in bpy.data.objects if ob.type == 'LIGHT')
|
||||||
|
if not light_exists:
|
||||||
|
# Create a new Area light datablock for ambient light
|
||||||
|
light_data = bpy.data.lights.new(name='AmbientLight', type='AREA')
|
||||||
|
light_object = bpy.data.objects.new(name='AmbientLight', object_data=light_data)
|
||||||
|
bpy.context.collection.objects.link(light_object)
|
||||||
|
# Position the light in the scene
|
||||||
|
light_object.location = (0, 0, 10)
|
||||||
|
# Set light size for soft shadows and ambient effect
|
||||||
|
light_data.size = 10
|
||||||
|
light_data.energy = 1000
|
||||||
|
print("Added an ambient light source to the scene.")
|
||||||
|
|
||||||
|
def add_camera(self, bpy):
|
||||||
|
# Check if there is a camera in the scene
|
||||||
|
if bpy.context.scene.camera:
|
||||||
|
return bpy.context.scene.camera
|
||||||
|
|
||||||
|
# If not, create a new camera
|
||||||
|
cam_data = bpy.data.cameras.new(name='Camera')
|
||||||
|
cam = bpy.data.objects.new(name='Camera', object_data=cam_data)
|
||||||
|
bpy.context.collection.objects.link(cam)
|
||||||
|
# Set the new camera to the active camera
|
||||||
|
bpy.context.scene.camera = cam
|
||||||
|
# Position the camera to a default view
|
||||||
|
cam.location = (0, 0, 10)
|
||||||
|
return cam
|
||||||
|
|
||||||
|
def get_texture_size(self, obj):
|
||||||
|
# Get the first material slot
|
||||||
|
mat = obj.data.materials[0]
|
||||||
|
|
||||||
|
# Check if the material has a node tree
|
||||||
|
if mat.node_tree:
|
||||||
|
nodes = mat.node_tree.nodes
|
||||||
|
# Find an image texture node in the node tree
|
||||||
|
for node in nodes:
|
||||||
|
if node.type == 'TEX_IMAGE':
|
||||||
|
texture = node.image
|
||||||
|
if texture:
|
||||||
|
return texture.size
|
||||||
|
print("No image texture node found in the material's node tree.")
|
||||||
|
else:
|
||||||
|
print("Material has no node tree.")
|
||||||
|
|
||||||
|
|
||||||
|
def blender_process(self, bpy, BPY_OBJ=None):
|
||||||
|
cam = self.add_camera(bpy)
|
||||||
|
|
||||||
|
plane = BPY_OBJ
|
||||||
|
if plane:
|
||||||
|
tex_width, tex_height = self.get_texture_size(plane)
|
||||||
|
# Calculate the aspect ratio of the plane
|
||||||
|
aspect_ratio_plane = tex_width / tex_height
|
||||||
|
|
||||||
|
# Set the render resolution to match the plane's aspect ratio
|
||||||
|
# Choose an arbitrary resolution for the longer side of the plane
|
||||||
|
base_resolution = 512
|
||||||
|
|
||||||
|
if aspect_ratio_plane > 1:
|
||||||
|
# Plane is wider than it is tall
|
||||||
|
bpy.context.scene.render.resolution_x = base_resolution
|
||||||
|
bpy.context.scene.render.resolution_y = int(base_resolution / aspect_ratio_plane)
|
||||||
|
else:
|
||||||
|
# Plane is taller than it is wide
|
||||||
|
bpy.context.scene.render.resolution_x = int(base_resolution * aspect_ratio_plane)
|
||||||
|
bpy.context.scene.render.resolution_y = base_resolution
|
||||||
|
bpy.context.scene.render.resolution_percentage = 100
|
||||||
|
|
||||||
|
|
||||||
|
ortho_scale = max(plane.dimensions.x, plane.dimensions.y)
|
||||||
|
cam.data.type = 'ORTHO'
|
||||||
|
cam.data.ortho_scale = ortho_scale
|
||||||
|
|
||||||
|
self.add_light(bpy)
|
||||||
|
|
||||||
|
# Update the scene to reflect changes
|
||||||
|
bpy.context.view_layer.update()
|
||||||
|
|
||||||
|
# Set render engine (e.g., 'BLENDER_EEVEE', 'CYCLES', 'BLENDER_WORKBENCH')
|
||||||
|
bpy.context.scene.render.engine = "BLENDER_EEVEE"
|
||||||
|
|
||||||
|
# Specify the render output path
|
||||||
|
|
||||||
|
output_path = get_incremented_filename(folder_paths.get_output_directory(), "render")
|
||||||
|
bpy.context.scene.render.filepath = output_path
|
||||||
|
|
||||||
|
# Render the image
|
||||||
|
bpy.ops.render.render(write_still=True)
|
||||||
|
|
||||||
|
# Load the image
|
||||||
|
i = Image.open(output_path)
|
||||||
|
i = ImageOps.exif_transpose(i)
|
||||||
|
image = i.convert("RGB")
|
||||||
|
image = np.array(image).astype(np.float32) / 255.0
|
||||||
|
image = torch.from_numpy(image)[None,]
|
||||||
|
|
||||||
|
# print(image.shape)
|
||||||
|
|
||||||
|
return (BPY_OBJ, image)
|
||||||
@@ -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
|
import bpy
|
||||||
global_bpy = bpy
|
|
||||||
|
|
||||||
def get_bpy():
|
global_bpy = bpy
|
||||||
global global_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
|
return global_bpy
|
||||||
|
|
||||||
|
|
||||||
def reset_bpy():
|
def reset_bpy():
|
||||||
global global_bpy
|
global global_bpy
|
||||||
bpy = global_bpy
|
bpy = global_bpy
|
||||||
# not using this cause this cause seg fault frequently
|
# 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
|
# # check if there's no object, return
|
||||||
if len(bpy.data.objects) == 0:
|
# if len(bpy.data.objects) == 0:
|
||||||
return
|
# return
|
||||||
|
|
||||||
# enter object mode
|
# # enter object mode
|
||||||
if bpy.context.mode != 'OBJECT':
|
# if bpy.context.mode != 'OBJECT':
|
||||||
bpy.ops.object.mode_set(mode='OBJECT')
|
# bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
|
|
||||||
# Delete all objects
|
# # Delete all objects
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
# bpy.ops.object.select_all(action='DESELECT')
|
||||||
bpy.ops.object.select_all(action='SELECT')
|
# bpy.ops.object.select_all(action='SELECT')
|
||||||
bpy.ops.object.delete()
|
# bpy.ops.object.delete()
|
||||||
|
|
||||||
# Delete all meshes
|
# # Delete all meshes
|
||||||
for mesh in bpy.data.meshes:
|
# for mesh in bpy.data.meshes:
|
||||||
bpy.data.meshes.remove(mesh)
|
# bpy.data.meshes.remove(mesh)
|
||||||
|
|
||||||
# Delete all materials
|
# # Delete all materials
|
||||||
for material in bpy.data.materials:
|
# for material in bpy.data.materials:
|
||||||
bpy.data.materials.remove(material)
|
# bpy.data.materials.remove(material)
|
||||||
|
|
||||||
# Delete all textures
|
# # Delete all textures
|
||||||
for texture in bpy.data.textures:
|
# for texture in bpy.data.textures:
|
||||||
bpy.data.textures.remove(texture)
|
# 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 && !showEditor.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',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
import { combinePointsNode, samPrompts } from "./state.js";
|
||||||
|
import { van } from "./van.js";
|
||||||
|
const { div, dialog, form, button, h3, input, span } = van.tags;
|
||||||
|
|
||||||
|
van.derive(() => {
|
||||||
|
if (
|
||||||
|
combinePointsNode.val != undefined &&
|
||||||
|
combinePointsNode.val.type === "Combine Points"
|
||||||
|
) {
|
||||||
|
const inputNames = combinePointsNode.val.inputs?.map((x) => x.name) || [];
|
||||||
|
const record = Object.keys(samPrompts.val);
|
||||||
|
|
||||||
|
const diff = inputNames.filter((x) => !record.includes(x));
|
||||||
|
const missingDiff = record.filter((x) => !inputNames.includes(x));
|
||||||
|
|
||||||
|
if (diff.length > 0) {
|
||||||
|
diff.forEach((x) => {
|
||||||
|
combinePointsNode.val.removeInput(
|
||||||
|
combinePointsNode.val.findInputSlot(x)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
combinePointsNode.val.graph.change();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (missingDiff.length > 0) {
|
||||||
|
missingDiff.forEach((x) => {
|
||||||
|
combinePointsNode.val.addInput(x, "POINTS");
|
||||||
|
});
|
||||||
|
combinePointsNode.val.graph.change();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export function CombinePointsDialog() {
|
||||||
|
const showAddLayer = van.state(false);
|
||||||
|
|
||||||
|
return div(
|
||||||
|
{
|
||||||
|
class: () =>
|
||||||
|
"absolute z-[100] top-0 left-0 flex justify-center w-full h-full ",
|
||||||
|
},
|
||||||
|
() =>
|
||||||
|
dialog(
|
||||||
|
{ id: "combine_points_dialog", class: "modal" },
|
||||||
|
div(
|
||||||
|
{ class: "modal-box text-base-content" },
|
||||||
|
form(
|
||||||
|
{
|
||||||
|
class: "gap-2 flex flex-col",
|
||||||
|
method: "dialog",
|
||||||
|
onsubmit: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
combine_points_dialog.close();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
button(
|
||||||
|
{
|
||||||
|
type: "button",
|
||||||
|
class: "btn btn-sm btn-circle btn-ghost absolute right-2 top-2",
|
||||||
|
onclick: (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
combine_points_dialog.close();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"✕"
|
||||||
|
),
|
||||||
|
h3({ class: "font-bold text-lg text-base-content" }, "Edit points"),
|
||||||
|
() =>
|
||||||
|
div(
|
||||||
|
{ class: "flex flex-col gap-2 mb-2" },
|
||||||
|
...Object.keys(samPrompts.val).map((key) => {
|
||||||
|
return span(key);
|
||||||
|
})
|
||||||
|
),
|
||||||
|
|
||||||
|
() =>
|
||||||
|
showAddLayer.val
|
||||||
|
? div(
|
||||||
|
{
|
||||||
|
class:
|
||||||
|
"flex flex-row justify-center items-center border rounded-md pr-2",
|
||||||
|
},
|
||||||
|
input({
|
||||||
|
type: "text",
|
||||||
|
placeholder: "Type here",
|
||||||
|
id: "layerName",
|
||||||
|
class:
|
||||||
|
"input input-ghost w-full focus:ring-0 focus:border-none focus:outline-none",
|
||||||
|
autofocus: true,
|
||||||
|
}),
|
||||||
|
button(
|
||||||
|
{
|
||||||
|
onclick: (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
showAddLayer.val = false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
span({
|
||||||
|
class: "iconify text-2xl",
|
||||||
|
"data-icon": "iconoir:cancel",
|
||||||
|
})
|
||||||
|
),
|
||||||
|
button(
|
||||||
|
{
|
||||||
|
onclick: (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
showAddLayer.val = false;
|
||||||
|
const inputText =
|
||||||
|
document.getElementById("layerName").value;
|
||||||
|
samPrompts.val = {
|
||||||
|
...samPrompts.val,
|
||||||
|
[inputText]: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
span({
|
||||||
|
class: "iconify text-2xl",
|
||||||
|
"data-icon": "mdi:tick",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: button(
|
||||||
|
{
|
||||||
|
class: "btn btn-outline btn",
|
||||||
|
onclick: (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
showAddLayer.val = true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Add new layer"
|
||||||
|
),
|
||||||
|
button(
|
||||||
|
{
|
||||||
|
type: "submit",
|
||||||
|
class: "btn btn-sm btn-ghost place-self-end",
|
||||||
|
},
|
||||||
|
"Confirm"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
+16
-11
@@ -1,19 +1,24 @@
|
|||||||
import { ImageEditor } from './ImageEditor.js';
|
import { LayerEditor } from "./LayerEditor.js";
|
||||||
import { AvatarEditor } from './AvatarEditor.js';
|
import { ShapeFlowEditor } from "./ShapeFlowEditor.js";
|
||||||
import { van } from './van.js';
|
import { van } from "./van.js";
|
||||||
import { AvatarViewer } from './AvatarViewer.js';
|
import { AvatarPreview } from "./AvatarPreview.js";
|
||||||
import { Loading } from './Loading.js';
|
import { Loading } from "./Loading.js";
|
||||||
|
import { Alert } from "./Alert.js";
|
||||||
|
import { AppHeader } from "./AppHeader.js";
|
||||||
|
import { CombinePointsDialog } from "./CombinePointsDialog.js";
|
||||||
const { button, iframe, div, img } = van.tags;
|
const { button, iframe, div, img } = van.tags;
|
||||||
|
|
||||||
export function Container() {
|
export function Container() {
|
||||||
return div(
|
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',
|
id: "avatech-editor",
|
||||||
},
|
},
|
||||||
AvatarEditor(),
|
CombinePointsDialog(),
|
||||||
ImageEditor(),
|
ShapeFlowEditor(),
|
||||||
AvatarViewer(),
|
LayerEditor(),
|
||||||
Loading()
|
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";
|
import { van } from "./van.js";
|
||||||
const { div, span } = van.tags;
|
const { div, span } = van.tags;
|
||||||
|
|
||||||
export function Loading() {
|
export function Loading() {
|
||||||
console.log("???", showLoading.val);
|
|
||||||
return div(
|
return div(
|
||||||
{
|
{
|
||||||
class: () =>
|
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"),
|
(showLoading.val ? "" : "hidden"),
|
||||||
},
|
},
|
||||||
span({
|
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"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
+255
-179
@@ -1,9 +1,12 @@
|
|||||||
import { drawSegment, getClicks, updateImagePrompts } from "./ImageEditor.js";
|
import { drawSegment, getClicks, updateImagePrompts } from "./LayerEditor.js";
|
||||||
import {
|
import {
|
||||||
imagePrompts,
|
imagePrompts,
|
||||||
selectedLayer,
|
selectedLayer,
|
||||||
imagePromptsMulti,
|
imagePromptsMulti,
|
||||||
targetNode,
|
targetNode,
|
||||||
|
showImageEditor,
|
||||||
|
allImagePrompts,
|
||||||
|
samPrompts,
|
||||||
} from "./state.js";
|
} from "./state.js";
|
||||||
import { van } from "./van.js";
|
import { van } from "./van.js";
|
||||||
const {
|
const {
|
||||||
@@ -22,201 +25,274 @@ const {
|
|||||||
span,
|
span,
|
||||||
} = van.tags;
|
} = van.tags;
|
||||||
|
|
||||||
|
export const updateOutputs = () => {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
van.derive(() => {
|
||||||
|
if (
|
||||||
|
showImageEditor.val &&
|
||||||
|
targetNode.val != undefined &&
|
||||||
|
targetNode.val.outputs &&
|
||||||
|
targetNode.val.type === "SAM MultiLayer"
|
||||||
|
) {
|
||||||
|
updateOutputs();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export function SideBar() {
|
export function SideBar() {
|
||||||
const layer_to_delete = van.state("");
|
const layer_to_delete = van.state("");
|
||||||
|
|
||||||
return div(
|
return div(
|
||||||
{
|
div(
|
||||||
class:
|
{
|
||||||
"ml-2 z-100 w-fit flex-col flex justify-center absolute top-0 left-0 bottom-0 items-start gap-2",
|
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);
|
const layers = Object.entries(imagePromptsMulti.val);
|
||||||
return ul(
|
return ul(
|
||||||
{
|
{
|
||||||
class: "menu bg-base-200 w-56 rounded-box text-base-content ",
|
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();
|
|
||||||
},
|
},
|
||||||
},
|
button(
|
||||||
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(
|
|
||||||
{
|
{
|
||||||
class: "gap-2 flex flex-col",
|
onclick: () => {
|
||||||
method: "dialog",
|
layers.map(([key, value]) => {
|
||||||
onsubmit: (e) => {
|
imagePrompts.val = [];
|
||||||
console.log("add new layer");
|
imagePromptsMulti.val[key] = [];
|
||||||
e.preventDefault();
|
});
|
||||||
const inputText = e.target.elements[1].value;
|
drawSegment([]);
|
||||||
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;
|
|
||||||
}
|
|
||||||
updateImagePrompts();
|
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: "flex items-center justify-between",
|
||||||
class: "btn btn-sm btn-circle btn-ghost absolute right-2 top-2",
|
onclick: () => {
|
||||||
onclick: (e) => {
|
my_modal_3.showModal();
|
||||||
e.stopPropagation();
|
|
||||||
my_modal_3.close();
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"✕"
|
"New Layer",
|
||||||
),
|
span({
|
||||||
h3(
|
class: "iconify",
|
||||||
{ class: "font-bold text-lg text-base-content" },
|
"data-icon": "ic:outline-plus",
|
||||||
"Add new layer!"
|
"data-inline": "false",
|
||||||
),
|
})
|
||||||
input({
|
)
|
||||||
type: "text",
|
)
|
||||||
placeholder: "Type here",
|
);
|
||||||
class: "input input-bordered w-full",
|
},
|
||||||
autofocus: true,
|
() =>
|
||||||
}),
|
ConfirmDialog(
|
||||||
button(
|
{
|
||||||
|
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: "gap-2 flex flex-col",
|
||||||
class: "btn btn-sm btn-ghost place-self-end",
|
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()
|
||||||
+434
-94
@@ -5,19 +5,45 @@ import {
|
|||||||
imageUrl,
|
imageUrl,
|
||||||
imagePrompts,
|
imagePrompts,
|
||||||
targetNode,
|
targetNode,
|
||||||
|
combinePointsNode,
|
||||||
fileName,
|
fileName,
|
||||||
embeddings,
|
embeddings,
|
||||||
imagePromptsMulti,
|
imagePromptsMulti,
|
||||||
selectedLayer,
|
selectedLayer,
|
||||||
showLoading,
|
showLoading,
|
||||||
|
loadingCaption,
|
||||||
|
alertDialog,
|
||||||
|
showPreview,
|
||||||
|
shareLoading,
|
||||||
|
previewModelId,
|
||||||
|
embeddingID,
|
||||||
|
enableAutoSegment,
|
||||||
|
samPrompts,
|
||||||
} from "./state.js";
|
} from "./state.js";
|
||||||
import { van } from "./van.js";
|
import { van } from "./van.js";
|
||||||
import { app } from "./app.js";
|
import { app } from "./app.js";
|
||||||
import { api } from "./api.js";
|
import { api } from "./api.js";
|
||||||
import { Container } from "./Container.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 "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";
|
||||||
|
import { updateOutputs } from "./SideBar.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} */
|
/** @type {import( '../../../web/types/litegraph.js').LGraphGroup} */
|
||||||
const recomputeInsideNodesOps = LGraphGroup.prototype.recomputeInsideNodes;
|
const recomputeInsideNodesOps = LGraphGroup.prototype.recomputeInsideNodes;
|
||||||
@@ -199,7 +225,7 @@ function openInAvatechEditor(url, fileName) {
|
|||||||
key: "key",
|
key: "key",
|
||||||
value: fileName,
|
value: fileName,
|
||||||
method: "store",
|
method: "store",
|
||||||
blendshapes: targetNode.val.widgets.find((x) => x.name === "blendshapes")
|
blendshapes: targetNode.val.widgets.find((x) => x.name === "shape_flow")
|
||||||
.value,
|
.value,
|
||||||
},
|
},
|
||||||
"*"
|
"*"
|
||||||
@@ -207,7 +233,7 @@ function openInAvatechEditor(url, fileName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateBlendshapesPrompts(value) {
|
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();
|
targetNode.val.graph.change();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,15 +249,35 @@ function getInputWidgetValue(node, inputIndex, widgetName) {
|
|||||||
/** @type {LGraphNode} */
|
/** @type {LGraphNode} */
|
||||||
let nodea = graph._nodes_by_id[targetLink.origin_id];
|
let nodea = graph._nodes_by_id[targetLink.origin_id];
|
||||||
|
|
||||||
while (nodea.type == "Reroute") {
|
while (nodea.type === "Reroute") {
|
||||||
nodea = nodea.getInputNode(0);
|
nodea = nodea.getInputNode(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(targetLink, nodea);
|
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} */
|
/** @type {string} */
|
||||||
return nodea.widgets.find((x) => x.name === widgetName).value;
|
const isGeneratedImage = true;
|
||||||
|
return [isGeneratedImage, generatedImages[saveImageNode.id]];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -239,45 +285,87 @@ function getInputWidgetValue(node, inputIndex, widgetName) {
|
|||||||
* @param {LGraphNode} node
|
* @param {LGraphNode} node
|
||||||
*/
|
*/
|
||||||
function showMyImageEditor(node) {
|
function showMyImageEditor(node) {
|
||||||
let connectedImageFileName = getInputWidgetValue(node, 0, "image");
|
let [isGeneratedImage, connectedImageFileName] = getInputWidgetValue(
|
||||||
const split = connectedImageFileName.split("/");
|
node,
|
||||||
if (split.length > 1) connectedImageFileName = split[1];
|
0,
|
||||||
|
"image"
|
||||||
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
|
|
||||||
);
|
);
|
||||||
|
if (!connectedImageFileName) {
|
||||||
if (!Array.isArray(v)) {
|
alertDialog.val = {
|
||||||
// this is a multi prompt
|
text: "Please connect or generate an image first",
|
||||||
imagePromptsMulti.val = v;
|
time: 3000,
|
||||||
selectedLayer.val = Object.keys(imagePromptsMulti.val)[0];
|
};
|
||||||
imagePrompts.val = imagePromptsMulti.val[selectedLayer.val];
|
return;
|
||||||
} else {
|
|
||||||
// this is a single prompt
|
|
||||||
selectedLayer.val = "";
|
|
||||||
imagePromptsMulti.val = {};
|
|
||||||
imagePrompts.val = v;
|
|
||||||
}
|
}
|
||||||
showImageEditor.val = true;
|
|
||||||
imageUrl.val = api.apiURL(
|
loadingCaption.val = "Loading SAM model...";
|
||||||
`/view?filename=${encodeURIComponent(
|
showLoading.val = true;
|
||||||
connectedImageFileName
|
|
||||||
)}&type=input&subfolder=${split.length > 1 ? split[0] : ""}`
|
const ckpt = node.widgets.find((x) => x.name === "ckpt").value;
|
||||||
);
|
const modelType = ckpt.match(/vit_[lbh]/)?.[0];
|
||||||
const embeedingUrl = api.apiURL(
|
initModel(modelType).then((res) => {
|
||||||
`/view?filename=${encodeURIComponent(
|
loadingCaption.val = "Computing image embedding...";
|
||||||
`${embeddingFilename}.npy`
|
|
||||||
)}&type=output&subfolder=`
|
const split = connectedImageFileName.split("/");
|
||||||
);
|
let id = connectedImageFileName;
|
||||||
loadNpyTensor(embeedingUrl).then((tensor) => {
|
if (split.length > 1) id = split[1];
|
||||||
embeddings.val = tensor;
|
|
||||||
drawSegment(getClicks());
|
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();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
showLoading.val = false;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
targetNode.val = node;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @typedef {import('../../../web/types/comfy.js').ComfyExtension} ComfyExtension*/
|
/** @typedef {import('../../../web/types/comfy.js').ComfyExtension} ComfyExtension*/
|
||||||
@@ -286,45 +374,44 @@ const ext = {
|
|||||||
getCustomWidgets(app) {
|
getCustomWidgets(app) {
|
||||||
return {
|
return {
|
||||||
SAM_PROMPTS(node, inputName, inputData, app) {
|
SAM_PROMPTS(node, inputName, inputData, app) {
|
||||||
const btn = node.addWidget("button", "Edit prompt", "", () => {
|
const asd = document.createElement("div");
|
||||||
showLoading.val = true;
|
Object.assign(asd, {
|
||||||
|
id: "sam",
|
||||||
let connectedImageFileName = getInputWidgetValue(node, 0, "image");
|
onclick: () => {
|
||||||
|
showMyImageEditor(node);
|
||||||
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);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
btn.serialize = false;
|
document.body.append(asd);
|
||||||
|
|
||||||
|
const btn = node.addWidget("button", "Edit prompt", "", () => {
|
||||||
|
showMyImageEditor(node);
|
||||||
|
btn.serialize = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
targetNode.val = node;
|
||||||
|
node.onConnectInput = (node, slot, targetSlot) => {
|
||||||
|
if (targetSlot.name === "SAM_PROMPTS") {
|
||||||
|
imagePromptsMulti.val = samPrompts.val;
|
||||||
|
updateOutputs();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
widget: btn,
|
widget: btn,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
COMBINE_POINTS(node, inputName, inputData, app) {
|
||||||
|
const btn = node.addWidget("button", "Edit points", "", () => {
|
||||||
|
console.log("Edit points");
|
||||||
|
combine_points_dialog.showModal();
|
||||||
|
combinePointsNode.val = node;
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
widget: btn,
|
||||||
|
};
|
||||||
|
},
|
||||||
BLENDSHAPES_CONFIG(node, inputName, inputData, app) {
|
BLENDSHAPES_CONFIG(node, inputName, inputData, app) {
|
||||||
const btn = node.addWidget("button", "Edit blendshapes", "", () => {
|
const btn = node.addWidget("button", "Edit Shape Flow", "", () => {
|
||||||
targetNode.val = node;
|
targetNode.val = node;
|
||||||
openInAvatechEditor(
|
openInAvatechEditor(
|
||||||
"https://editor.avatech.ai?comfyui=true",
|
"https://editor.avatech.ai?comfyui=true",
|
||||||
@@ -361,6 +448,24 @@ const ext = {
|
|||||||
widget: btn,
|
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 +482,7 @@ const ext = {
|
|||||||
|
|
||||||
node.computeParentGroupResize();
|
node.computeParentGroupResize();
|
||||||
};
|
};
|
||||||
|
injectUIComponentToComfyuimenu();
|
||||||
},
|
},
|
||||||
|
|
||||||
async setup() {
|
async setup() {
|
||||||
@@ -389,6 +495,10 @@ const ext = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
api.addEventListener("executed", (evt) => {
|
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) {
|
if (evt.detail?.output.gltfFilename) {
|
||||||
const viewer = document.getElementById(
|
const viewer = document.getElementById(
|
||||||
"avatech-viewer-iframe"
|
"avatech-viewer-iframe"
|
||||||
@@ -401,11 +511,23 @@ const ext = {
|
|||||||
api.api_base +
|
api.api_base +
|
||||||
`/view?filename=${evt.detail?.output.gltfFilename[0]}`;
|
`/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;
|
fileName.val = gltfFilename;
|
||||||
viewer.postMessage(
|
viewer.postMessage(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
avatarURL: gltfFilename,
|
avatarURL: gltfFilename,
|
||||||
blendshapes: evt.detail?.output.blendshapes[0],
|
blendshapes: evt.detail?.output.SHAPE_FLOW[0],
|
||||||
}),
|
}),
|
||||||
"*"
|
"*"
|
||||||
);
|
);
|
||||||
@@ -414,10 +536,23 @@ const ext = {
|
|||||||
|
|
||||||
window.addEventListener(
|
window.addEventListener(
|
||||||
"keydown",
|
"keydown",
|
||||||
(event) => {
|
async (event) => {
|
||||||
if (event.key === "Escape") {
|
if (event.key === "Escape") {
|
||||||
event.preventDefault();
|
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 +612,36 @@ const ext = {
|
|||||||
|
|
||||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||||
switch (nodeData.name) {
|
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":
|
case "ExportGLTF":
|
||||||
addMenuHandler(nodeType, function (_, options) {
|
addMenuHandler(nodeType, function (_, options) {
|
||||||
const output = app.nodeOutputs[this.id + ""];
|
const output = app.nodeOutputs[this.id + ""];
|
||||||
@@ -527,7 +692,7 @@ const ext = {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "SAM_Prompt_Image":
|
case "SAM MultiLayer":
|
||||||
nodeData.input.required.sam = ["SAM_PROMPTS"];
|
nodeData.input.required.sam = ["SAM_PROMPTS"];
|
||||||
// nodeData.input.required.upload = ['IMAGEUPLOAD'];
|
// nodeData.input.required.upload = ['IMAGEUPLOAD'];
|
||||||
// nodeData.input.required.prompts_points = ["IMAGEUPLOAD"];
|
// nodeData.input.required.prompts_points = ["IMAGEUPLOAD"];
|
||||||
@@ -540,10 +705,9 @@ const ext = {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "SAM":
|
case "Combine Points":
|
||||||
nodeData.input.required.sam = ["SAM_PROMPTS"];
|
nodeData.input.required.sam = ["COMBINE_POINTS"];
|
||||||
// nodeData.input.required.upload = ['IMAGEUPLOAD'];
|
|
||||||
// nodeData.input.required.prompts_points = ["IMAGEUPLOAD"];
|
|
||||||
addMenuHandler(nodeType, function (_, options) {
|
addMenuHandler(nodeType, function (_, options) {
|
||||||
options.unshift({
|
options.unshift({
|
||||||
content: "Open In Points Editor (Local)",
|
content: "Open In Points Editor (Local)",
|
||||||
@@ -555,26 +719,202 @@ const ext = {
|
|||||||
break;
|
break;
|
||||||
case "CreateShapeFlow":
|
case "CreateShapeFlow":
|
||||||
nodeData.input.required.blendshape = ["BLENDSHAPES_CONFIG"];
|
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;
|
break;
|
||||||
case "Mesh_JoinMesh":
|
case "Mesh_JoinMesh":
|
||||||
nodeData.input.required.obj = ["MESH_GROUP_CONFIG"];
|
nodeData.input.required.obj = ["MESH_GROUP_CONFIG"];
|
||||||
nodeData.input.required.del_obj = ["MESH_GROUP_DELETE"];
|
nodeData.input.required.del_obj = ["MESH_GROUP_DELETE"];
|
||||||
break;
|
break;
|
||||||
|
case "GroupOps":
|
||||||
|
nodeData.input.required.obj = ["GROUP_OPS"];
|
||||||
|
nodeData.input.required.del_obj = ["GROUP_OPS_DELETE"];
|
||||||
default:
|
default:
|
||||||
break;
|
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) => {
|
||||||
|
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);
|
app.registerExtension(ext);
|
||||||
|
|||||||
+12
-14
@@ -1,23 +1,22 @@
|
|||||||
import "https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js";
|
import "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.16.3/dist/ort.min.js";
|
||||||
import npyjs from "https://esm.sh/npyjs";
|
import npyjs from "https://esm.sh/npyjs";
|
||||||
import { imageSize } from "./state.js";
|
import { imageSize } from "./state.js";
|
||||||
import { modelData, onnxMaskToImage } from "./onnx_helper.js";
|
import { modelData, onnxMaskToImage } from "./onnx_helper.js";
|
||||||
|
|
||||||
ort.env.wasm.wasmPaths = "https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/";
|
ort.env.wasm.wasmPaths = "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.16.3/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;
|
export let model = null;
|
||||||
|
let modelType = null;
|
||||||
|
|
||||||
// Initialize the ONNX model
|
// Initialize the ONNX model
|
||||||
export const initModel = async () => {
|
export const initModel = async (type) => {
|
||||||
try {
|
try {
|
||||||
if (MODEL_DIR === undefined) return;
|
if (!model || modelType !== type) {
|
||||||
const URL = MODEL_DIR;
|
modelType = type;
|
||||||
model = await ort.InferenceSession.create(URL);
|
model = await ort.InferenceSession.create(
|
||||||
|
`${location.protocol}//${location.host}/sam_model?type=${modelType}`
|
||||||
|
);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
@@ -25,14 +24,12 @@ export const initModel = async () => {
|
|||||||
|
|
||||||
export const loadNpyTensor = async (tensorFile, dType = "float32") => {
|
export const loadNpyTensor = async (tensorFile, dType = "float32") => {
|
||||||
let npLoader = new npyjs();
|
let npLoader = new npyjs();
|
||||||
console.log('tensorFile', tensorFile);
|
|
||||||
const npArray = await npLoader.load(tensorFile);
|
const npArray = await npLoader.load(tensorFile);
|
||||||
console.log('np array', npArray);
|
|
||||||
const tensor = new ort.Tensor(dType, npArray.data, npArray.shape);
|
const tensor = new ort.Tensor(dType, npArray.data, npArray.shape);
|
||||||
return tensor;
|
return tensor;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const runONNX = async (clicks, tensor) => {
|
export const runONNX = async (clicks, tensor, box) => {
|
||||||
// console.log('tensor', tensor);
|
// console.log('tensor', tensor);
|
||||||
try {
|
try {
|
||||||
if (
|
if (
|
||||||
@@ -49,6 +46,7 @@ export const runONNX = async (clicks, tensor) => {
|
|||||||
clicks,
|
clicks,
|
||||||
tensor,
|
tensor,
|
||||||
modelScale: imageSize.val,
|
modelScale: imageSize.val,
|
||||||
|
box,
|
||||||
});
|
});
|
||||||
if (feeds === undefined) return;
|
if (feeds === undefined) return;
|
||||||
// Run the SAM ONNX model with the feeds returned from modelData()
|
// 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
|
// This source code is licensed under the license found in the
|
||||||
// LICENSE file in the root directory of this source tree.
|
// LICENSE file in the root directory of this source tree.
|
||||||
|
|
||||||
const modelData = ({ clicks, tensor, modelScale }) => {
|
const modelData = ({ clicks, tensor, modelScale, box }) => {
|
||||||
const imageEmbedding = tensor;
|
const imageEmbedding = tensor;
|
||||||
let pointCoords;
|
let pointCoords;
|
||||||
let pointLabels;
|
let pointLabels;
|
||||||
@@ -18,8 +18,9 @@ const modelData = ({ clicks, tensor, modelScale }) => {
|
|||||||
// If there is no box input, a single padding point with
|
// If there is no box input, a single padding point with
|
||||||
// label -1 and coordinates (0.0, 0.0) should be concatenated
|
// label -1 and coordinates (0.0, 0.0) should be concatenated
|
||||||
// so initialize the array to support (n + 1) points.
|
// so initialize the array to support (n + 1) points.
|
||||||
pointCoords = new Float32Array(2 * (n + 1));
|
const numPoints = box ? n + 3 : n + 1;
|
||||||
pointLabels = new Float32Array(n + 1);
|
pointCoords = new Float32Array(2 * numPoints);
|
||||||
|
pointLabels = new Float32Array(numPoints);
|
||||||
|
|
||||||
// Add clicks and scale to what SAM expects
|
// Add clicks and scale to what SAM expects
|
||||||
for (let i = 0; i < n; i++) {
|
for (let i = 0; i < n; i++) {
|
||||||
@@ -28,15 +29,25 @@ const modelData = ({ clicks, tensor, modelScale }) => {
|
|||||||
pointLabels[i] = clicks[i].clickType;
|
pointLabels[i] = clicks[i].clickType;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add in the extra point/label when only clicks and no box
|
if (box) {
|
||||||
// The extra point is at (0, 0) with label -1
|
pointCoords[2 * n] = box.x1 * modelScale.samScale;
|
||||||
pointCoords[2 * n] = 0.0;
|
pointCoords[2 * n + 1] = box.y1 * modelScale.samScale;
|
||||||
pointCoords[2 * n + 1] = 0.0;
|
pointLabels[n] = 2;
|
||||||
pointLabels[n] = -1.0;
|
|
||||||
|
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
|
// Create the tensor
|
||||||
pointCoordsTensor = new ort.Tensor("float32", pointCoords, [1, n + 1, 2]);
|
pointCoordsTensor = new ort.Tensor("float32", pointCoords, [1, numPoints, 2]);
|
||||||
pointLabelsTensor = new ort.Tensor("float32", pointLabels, [1, n + 1]);
|
pointLabelsTensor = new ort.Tensor("float32", pointLabels, [1, numPoints]);
|
||||||
}
|
}
|
||||||
const imageSizeTensor = new ort.Tensor("float32", [
|
const imageSizeTensor = new ort.Tensor("float32", [
|
||||||
modelScale.height,
|
modelScale.height,
|
||||||
|
|||||||
+47
-6
@@ -9,26 +9,62 @@
|
|||||||
* @typedef {Object} Point
|
* @typedef {Object} Point
|
||||||
* @property {number} x - The x coordinate
|
* @property {number} x - The x coordinate
|
||||||
* @property {number} y - The y 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 showEditor = van.state(false);
|
||||||
export const fileName = van.state('');
|
// localStorage.getItem("showPreview") == 'true'
|
||||||
|
console.log(localStorage.getItem("showPreview"));
|
||||||
|
if (localStorage.getItem("showPreview") == null)
|
||||||
|
localStorage.setItem("showPreview", 'true')
|
||||||
|
export const showPreview = van.state(localStorage.getItem("showPreview") == '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 showImageEditor = van.state(false);
|
||||||
export const showLoading = 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 point_label = van.state(1);
|
||||||
export const imageContainerSize = van.state({
|
export const imageContainerSize = van.state({
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** @type {State<Box>} */
|
||||||
|
export const boxes = van.state();
|
||||||
|
|
||||||
|
/** @type {State<Record<string, Box>>} */
|
||||||
|
export const boxesMulti = van.state({});
|
||||||
|
|
||||||
/** @type {State<Point[]>} */
|
/** @type {State<Point[]>} */
|
||||||
export const imagePrompts = van.state([]);
|
export const imagePrompts = van.state([]);
|
||||||
|
|
||||||
|
export const allImagePrompts = van.state([{}]);
|
||||||
|
|
||||||
/** @type {State<Record<string, Point[]>>} */
|
/** @type {State<Record<string, Point[]>>} */
|
||||||
export const imagePromptsMulti = van.state({});
|
export const imagePromptsMulti = van.state({});
|
||||||
|
|
||||||
@@ -40,3 +76,8 @@ export const targetNode = van.state();
|
|||||||
|
|
||||||
export const imageSize = van.state({ width: 0, height: 0, samScale: 0 });
|
export const imageSize = van.state({ width: 0, height: 0, samScale: 0 });
|
||||||
export const embeddings = van.state();
|
export const embeddings = van.state();
|
||||||
|
export const embeddingID = van.state("Test");
|
||||||
|
|
||||||
|
/** @type {State<LGraphNode>} */
|
||||||
|
export const combinePointsNode = van.state();
|
||||||
|
export const samPrompts = van.state({});
|
||||||
|
|||||||
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;
|
||||||
|
}
|
||||||
+3281
File diff suppressed because it is too large
Load Diff
+2
-3
@@ -4,15 +4,14 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"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": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.5.3",
|
||||||
"concurrently": "^8.2.1",
|
"daisyui": "^4.0.7",
|
||||||
"daisyui": "^3.7.5",
|
|
||||||
"tailwindcss": "^3.3.3"
|
"tailwindcss": "^3.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+14
-209
@@ -8,12 +8,9 @@ devDependencies:
|
|||||||
chokidar:
|
chokidar:
|
||||||
specifier: ^3.5.3
|
specifier: ^3.5.3
|
||||||
version: 3.5.3
|
version: 3.5.3
|
||||||
concurrently:
|
|
||||||
specifier: ^8.2.1
|
|
||||||
version: 8.2.1
|
|
||||||
daisyui:
|
daisyui:
|
||||||
specifier: ^3.7.5
|
specifier: ^4.0.7
|
||||||
version: 3.7.5
|
version: 4.0.7(postcss@8.4.29)
|
||||||
tailwindcss:
|
tailwindcss:
|
||||||
specifier: ^3.3.3
|
specifier: ^3.3.3
|
||||||
version: 3.3.3
|
version: 3.3.3
|
||||||
@@ -25,13 +22,6 @@ packages:
|
|||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
dev: true
|
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:
|
/@jridgewell/gen-mapping@0.3.3:
|
||||||
resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
|
resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
|
||||||
engines: {node: '>=6.0.0'}
|
engines: {node: '>=6.0.0'}
|
||||||
@@ -83,18 +73,6 @@ packages:
|
|||||||
fastq: 1.15.0
|
fastq: 1.15.0
|
||||||
dev: true
|
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:
|
/any-promise@1.3.0:
|
||||||
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
|
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
|
||||||
dev: true
|
dev: true
|
||||||
@@ -139,14 +117,6 @@ packages:
|
|||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
dev: true
|
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:
|
/chokidar@3.5.3:
|
||||||
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
|
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
|
||||||
engines: {node: '>= 8.10.0'}
|
engines: {node: '>= 8.10.0'}
|
||||||
@@ -162,30 +132,6 @@ packages:
|
|||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
dev: true
|
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:
|
/commander@4.1.1:
|
||||||
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
|
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
@@ -195,22 +141,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||||
dev: true
|
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:
|
/css-selector-tokenizer@0.8.0:
|
||||||
resolution: {integrity: sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==}
|
resolution: {integrity: sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -224,24 +154,21 @@ packages:
|
|||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/daisyui@3.7.5:
|
/culori@3.2.0:
|
||||||
resolution: {integrity: sha512-udhiBJYVvcPGXa+mL5IElke6EdddKecjbbz6m43+9IVqzK8GBwetg092Edclo42TkNboLD9nzodeesJygqZt2A==}
|
resolution: {integrity: sha512-HIEbTSP7vs1mPq/2P9In6QyFE0Tkpevh0k9a+FkjhD+cwsYm9WRSbn4uMdW9O0yXlNYC3ppxL3gWWPOcvEl57w==}
|
||||||
engines: {node: '>=16.9.0'}
|
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.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
|
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/date-fns@2.30.0:
|
/daisyui@4.0.7(postcss@8.4.29):
|
||||||
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
|
resolution: {integrity: sha512-D84DnNDZKcamwNsxCMrwYaddyz5kC6VO6oe30nM1x67GzCAfarfd3Ar1rpLGXCIqSsEoNZUHO8EcXvX93W2ZkA==}
|
||||||
engines: {node: '>=0.11'}
|
engines: {node: '>=16.9.0'}
|
||||||
dependencies:
|
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
|
dev: true
|
||||||
|
|
||||||
/didyoumean@1.2.2:
|
/didyoumean@1.2.2:
|
||||||
@@ -252,15 +179,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
|
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
|
||||||
dev: true
|
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:
|
/fast-glob@3.3.1:
|
||||||
resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==}
|
resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==}
|
||||||
engines: {node: '>=8.6.0'}
|
engines: {node: '>=8.6.0'}
|
||||||
@@ -305,11 +223,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
|
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
|
||||||
dev: true
|
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:
|
/glob-parent@5.1.2:
|
||||||
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
@@ -335,11 +248,6 @@ packages:
|
|||||||
path-is-absolute: 1.0.1
|
path-is-absolute: 1.0.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/has-flag@4.0.0:
|
|
||||||
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/has@1.0.3:
|
/has@1.0.3:
|
||||||
resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
|
resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
|
||||||
engines: {node: '>= 0.4.0'}
|
engines: {node: '>= 0.4.0'}
|
||||||
@@ -376,11 +284,6 @@ packages:
|
|||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
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:
|
/is-glob@4.0.3:
|
||||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -407,10 +310,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/lodash@4.17.21:
|
|
||||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/merge2@1.4.1:
|
/merge2@1.4.1:
|
||||||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@@ -580,15 +479,6 @@ packages:
|
|||||||
picomatch: 2.3.1
|
picomatch: 2.3.1
|
||||||
dev: true
|
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:
|
/resolve@1.22.4:
|
||||||
resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==}
|
resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -609,41 +499,11 @@ packages:
|
|||||||
queue-microtask: 1.2.3
|
queue-microtask: 1.2.3
|
||||||
dev: true
|
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:
|
/source-map-js@1.0.2:
|
||||||
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
|
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
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:
|
/sucrase@3.34.0:
|
||||||
resolution: {integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==}
|
resolution: {integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -658,20 +518,6 @@ packages:
|
|||||||
ts-interface-checker: 0.1.13
|
ts-interface-checker: 0.1.13
|
||||||
dev: true
|
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:
|
/supports-preserve-symlinks-flag@1.0.0:
|
||||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -728,60 +574,19 @@ packages:
|
|||||||
is-number: 7.0.0
|
is-number: 7.0.0
|
||||||
dev: true
|
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:
|
/ts-interface-checker@0.1.13:
|
||||||
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
|
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/tslib@2.6.2:
|
|
||||||
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/util-deprecate@1.0.2:
|
/util-deprecate@1.0.2:
|
||||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||||
dev: true
|
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:
|
/wrappy@1.0.2:
|
||||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/y18n@5.0.8:
|
|
||||||
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/yaml@2.3.2:
|
/yaml@2.3.2:
|
||||||
resolution: {integrity: sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==}
|
resolution: {integrity: sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==}
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
dev: true
|
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-python
|
||||||
opencv-contrib-python
|
opencv-contrib-python
|
||||||
einops
|
einops
|
||||||
bpy
|
bpy==3.6.0
|
||||||
segment-anything
|
segment-anything
|
||||||
|
tqdm
|
||||||
|
python-dotenv
|
||||||
|
mediapipe
|
||||||
# -e git+https://github.com/facebookresearch/segment-anything.git#egg=segment_anything
|
# -e git+https://github.com/facebookresearch/segment-anything.git#egg=segment_anything
|
||||||
@@ -1,12 +1,24 @@
|
|||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from segment_anything import sam_model_registry, SamPredictor
|
from segment_anything import sam_model_registry, SamPredictor
|
||||||
from PIL import Image, ImageOps
|
from PIL import Image, ImageOps
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from blender.mesh_utils import upload_avatar_file
|
||||||
import os
|
import os
|
||||||
import requests
|
import requests
|
||||||
import folder_paths
|
import folder_paths
|
||||||
import json
|
import json
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import server
|
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
|
# For speeding up ONNX model, see https://github.com/facebookresearch/segment-anything/tree/main/demo#onnx-multithreading-with-sharedarraybuffer
|
||||||
def inject_headers(original_handler):
|
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._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")
|
@server.PromptServer.instance.routes.get("/sam_model")
|
||||||
async def get_sam_model(request):
|
async def get_sam_model(request):
|
||||||
filename = os.path.join(folder_paths.base_path, "web/models/sam.onnx")
|
model_type = request.rel_url.query.get("type", "vit_h")
|
||||||
# print(filename)
|
filename = os.path.join(folder_paths.base_path, f"web/models/sam_{model_type}.onnx")
|
||||||
if not os.path.isfile(filename):
|
if not os.path.isfile(filename):
|
||||||
os.makedirs(os.path.dirname(filename), exist_ok=True)
|
os.makedirs(os.path.dirname(filename), exist_ok=True)
|
||||||
|
print(f"Downloading ONNX model to {filename}")
|
||||||
response = requests.get(
|
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()
|
response.raise_for_status()
|
||||||
with open(filename, "wb") as f:
|
with open(filename, "wb") as f:
|
||||||
f.write(response.content)
|
f.write(response.content)
|
||||||
print(f"ONNX model downloaded: {filename}")
|
print(f"ONNX model downloaded")
|
||||||
return web.FileResponse(filename)
|
return web.FileResponse(filename)
|
||||||
|
|
||||||
|
|
||||||
def load_image(image):
|
def load_image(image, is_generated_image):
|
||||||
image_path = folder_paths.get_annotated_filepath(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 = Image.open(image_path)
|
||||||
i = ImageOps.exif_transpose(i)
|
i = ImageOps.exif_transpose(i)
|
||||||
image = i.convert("RGB")
|
image = i.convert("RGB")
|
||||||
@@ -60,26 +82,349 @@ def load_image(image):
|
|||||||
@server.PromptServer.instance.routes.post("/sam_model")
|
@server.PromptServer.instance.routes.post("/sam_model")
|
||||||
async def post_sam_model(request):
|
async def post_sam_model(request):
|
||||||
post = await request.json()
|
post = await request.json()
|
||||||
|
is_generated_image = post.get("isGeneratedImage")
|
||||||
emb_id = post.get("embedding_id")
|
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):
|
if not os.path.exists(emb_filename):
|
||||||
image = load_image(post.get("image"))
|
image = load_image(post.get("image"), is_generated_image)
|
||||||
ckpt = post.get("ckpt")
|
if remote:
|
||||||
model_type = post.get("model_type")
|
# Run embed in remote server
|
||||||
ckpt = folder_paths.get_full_path("sam", ckpt)
|
image = Image.fromarray((image * 255).astype(np.uint8))
|
||||||
sam = sam_model_registry[model_type](checkpoint=ckpt)
|
buffered = io.BytesIO()
|
||||||
predictor = SamPredictor(sam)
|
image.save(buffered, format="PNG")
|
||||||
|
image = base64.b64encode(buffered.getvalue()).decode()
|
||||||
image_np = (image * 255).astype(np.uint8)
|
res = requests.post(
|
||||||
predictor.set_image(image_np)
|
"https://avatechgg--sam-embed.modal.run",
|
||||||
emb = predictor.get_image_embedding().cpu().numpy()
|
headers={
|
||||||
np.save(emb_filename, emb)
|
"Content-type": "application/json",
|
||||||
with open(f"{folder_paths.get_output_directory()}/{emb_id}.json", "w") as f:
|
"Accept": "application/json",
|
||||||
json.dump(
|
|
||||||
{
|
|
||||||
"input_size": predictor.input_size,
|
|
||||||
"original_size": predictor.original_size,
|
|
||||||
},
|
},
|
||||||
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({})
|
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)
|
||||||
|
|
||||||
|
# TODO: refactor the code
|
||||||
|
@server.PromptServer.instance.routes.post("/rendering_generation")
|
||||||
|
async def post_data_generation(request):
|
||||||
|
prompt_server = server.PromptServer.instance
|
||||||
|
post = await request.json()
|
||||||
|
workflow_name = post.get("workflow_name")
|
||||||
|
workflow = load_workflow(workflow_name)
|
||||||
|
workflow = workflow.replace("SEED", str(randomSeed()))
|
||||||
|
|
||||||
|
inputs = post.get("inputs")
|
||||||
|
|
||||||
|
for key, value in inputs.items():
|
||||||
|
workflow = workflow.replace(f'"{key}"', f'"{str(value)}"')
|
||||||
|
|
||||||
|
res = post_prompt({"prompt": json.loads(workflow)})
|
||||||
|
prompt_id = json.loads(res.text)["prompt_id"]
|
||||||
|
while True:
|
||||||
|
history = prompt_server.prompt_queue.get_history(prompt_id=prompt_id)
|
||||||
|
if history:
|
||||||
|
outputs = history[prompt_id]["outputs"]
|
||||||
|
for node_id, output in outputs.items():
|
||||||
|
if "images" in output:
|
||||||
|
filename = output["images"][0]["filename"]
|
||||||
|
if filename.startswith("rendered"):
|
||||||
|
return web.json_response({"image": filename}, status=200)
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
@server.PromptServer.instance.routes.post("/image_generation")
|
||||||
|
async def post_image_generation(request):
|
||||||
|
prompt_server = server.PromptServer.instance
|
||||||
|
workflow = load_workflow("generation")
|
||||||
|
workflow = workflow.replace("SEED", str(randomSeed()))
|
||||||
|
|
||||||
|
res = post_prompt({"prompt": json.loads(workflow)})
|
||||||
|
prompt_id = json.loads(res.text)["prompt_id"]
|
||||||
|
while True:
|
||||||
|
history = prompt_server.prompt_queue.get_history(prompt_id=prompt_id)
|
||||||
|
if history:
|
||||||
|
outputs = history[prompt_id]["outputs"]
|
||||||
|
for node_id, output in outputs.items():
|
||||||
|
if "images" in output:
|
||||||
|
filename = output["images"][0]["filename"]
|
||||||
|
if filename.startswith("avatar"):
|
||||||
|
return web.json_response({"image": filename}, 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,27 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
class CombinePoints:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_NAMES = ("SAM_PROMPTS",)
|
||||||
|
RETURN_TYPES = ("STRING",)
|
||||||
|
|
||||||
|
FUNCTION = "run"
|
||||||
|
|
||||||
|
CATEGORY = "image"
|
||||||
|
|
||||||
|
# OUTPUT_NODE = True
|
||||||
|
|
||||||
|
def run(self, *args, **kwargs):
|
||||||
|
sam_prompts = json.dumps(kwargs, default=str)
|
||||||
|
return (sam_prompts,)
|
||||||
|
|
||||||
|
|
||||||
|
NODE_CLASS_MAPPINGS = {"Combine Points": CombinePoints}
|
||||||
|
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS = {"Combine Points": "Combine Points"}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
|
||||||
|
|
||||||
|
class ExtractBoundaryPoints:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"image": ("IMAGE",),
|
||||||
|
"n_points": ("INT", {"default": -1, "min": -1, "max": 100}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("POINTS", "IMAGE")
|
||||||
|
|
||||||
|
FUNCTION = "run"
|
||||||
|
|
||||||
|
CATEGORY = "image"
|
||||||
|
|
||||||
|
def find_main_contour(self, image, n_points):
|
||||||
|
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)
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
max_area_index = areas.index(max(areas))
|
||||||
|
largest_contour = contours[max_area_index]
|
||||||
|
if n_points > 0:
|
||||||
|
divided_by = int(largest_contour.shape[0] / n_points)
|
||||||
|
divided_by = min(divided_by, largest_contour.shape[0])
|
||||||
|
largest_contour = largest_contour[::divided_by].astype(int)
|
||||||
|
contours = [largest_contour]
|
||||||
|
|
||||||
|
if not image.flags["C_CONTIGUOUS"]:
|
||||||
|
image = np.ascontiguousarray(image)
|
||||||
|
cv2.drawContours(image, contours, -1, (0, 255, 0), 3)
|
||||||
|
|
||||||
|
points = []
|
||||||
|
for point in largest_contour:
|
||||||
|
points.append(
|
||||||
|
{"x": point[0][0], "y": point[0][1], "label": 1, "isAuto": True}
|
||||||
|
)
|
||||||
|
|
||||||
|
return image, points
|
||||||
|
|
||||||
|
def run(self, image, n_points):
|
||||||
|
contour_image, points = self.find_main_contour(image, n_points)
|
||||||
|
contour_image = torch.from_numpy(np.expand_dims(contour_image, axis=0))
|
||||||
|
print(points)
|
||||||
|
return (points, contour_image)
|
||||||
|
|
||||||
|
|
||||||
|
NODE_CLASS_MAPPINGS = {"Extract Boundary Points": ExtractBoundaryPoints}
|
||||||
|
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS = {"Extract Boundary Points": "Extract Boundary Points"}
|
||||||
@@ -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"}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
class LoadValueFromRequest:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"name": (
|
||||||
|
"STRING",
|
||||||
|
{"multiline": False, "default": "key_name"},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"optional": {
|
||||||
|
"value": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01, "display": "number"}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("FLOAT",)
|
||||||
|
RETURN_NAMES = ("value",)
|
||||||
|
|
||||||
|
FUNCTION = "run"
|
||||||
|
|
||||||
|
CATEGORY = "image"
|
||||||
|
|
||||||
|
def run(self, name, value=None):
|
||||||
|
if name:
|
||||||
|
value = name
|
||||||
|
return (value,)
|
||||||
|
|
||||||
|
|
||||||
|
NODE_CLASS_MAPPINGS = {"LoadValueFromRequest": LoadValueFromRequest}
|
||||||
|
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS = {"LoadValueFromRequest": "Load Value 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,385 @@
|
|||||||
|
import folder_paths
|
||||||
|
import os
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
import uuid
|
||||||
|
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
|
||||||
|
if len(face_landmarks) == 0:
|
||||||
|
print("Warning: no face detected")
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
pose_landmarks = pose_landmarker.detect(mp_image).pose_landmarks
|
||||||
|
if len(pose_landmarks) == 0:
|
||||||
|
print("Warning: no pose detected")
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
imagePromptsMulti, boxesMulti = self.auto_segment(
|
||||||
|
np_image, face_landmarks[0], pose_landmarks[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
return imagePromptsMulti, boxesMulti
|
||||||
|
|
||||||
|
def load_image(self, image, ckpt, embedding_id, image_prompts_json):
|
||||||
|
if 'COMFY_DEPLOY' in os.environ and os.getenv('COMFY_DEPLOY', 'FALSE') == "TRUE":
|
||||||
|
embedding_id = str(uuid.uuid4())
|
||||||
|
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] # use imagePromptsMulti
|
||||||
|
|
||||||
|
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 imagePromptsMulti is not None and 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 boxesMulti is not None and 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}'],
|
||||||
content: ['./js/**/*.{html,js}'],
|
content: ['./js/**/*.{html,js}'],
|
||||||
theme: {
|
theme: {
|
||||||
|
fontFamily: {
|
||||||
|
'gabarito': ['Gabarito'],
|
||||||
|
},
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
daisyui: {
|
daisyui: {
|
||||||
|
|||||||
Reference in New Issue
Block a user