Compare commits
2361 Commits
asset-grea
...
functions
| Author | SHA1 | Date | |
|---|---|---|---|
| 6692ca602c | |||
| dabd59ba23 | |||
| 43f895a592 | |||
| 52606afaa6 | |||
| cd5f9516db | |||
| 8ce5c50411 | |||
| 37ee4d4e2a | |||
| d9356a5b8d | |||
| edd0928d09 | |||
| 0feed991e2 | |||
| 26e7498b0c | |||
| 4a8a7cc3ca | |||
| 78d8e9f962 | |||
| 4f8b24e0c5 | |||
| 8471b2f22e | |||
| 743443ac14 | |||
| 7686f1a8fe | |||
| b350b85aba | |||
| 87d20d7c92 | |||
| ee292426a7 | |||
| 4ed7334ef6 | |||
| b92b1a4fe5 | |||
| 096856a700 | |||
| beed795e5a | |||
| fcfe9784cb | |||
| 3b1c34267b | |||
| 3fe710aeaf | |||
| 6399bfcee3 | |||
| 7f35aa99c7 | |||
| 1945a23259 | |||
| fad8e475c7 | |||
| b80585ba30 | |||
| ecc78d29d6 | |||
| 4d74116218 | |||
| bd42f7c275 | |||
| e543c9ad32 | |||
| bdd69570e2 | |||
| 3e533af702 | |||
| 8363204347 | |||
| 0e0f6a9216 | |||
| 2b83d33032 | |||
| 4526aa70e5 | |||
| 1df94f6257 | |||
| 9564cd045a | |||
| de23a2c35b | |||
| fd46b5e9fe | |||
| 10927b6747 | |||
| ba1fcdf41a | |||
| 9906e5313b | |||
| 2e1d18600f | |||
| e4343f6aa2 | |||
| 953b8d6066 | |||
| f943f4f99c | |||
| 350bc04f56 | |||
| 9933b5bc27 | |||
| 2e950e3b89 | |||
| bb4b0cc954 | |||
| 1c81255cdb | |||
| 90b41c7c2f | |||
| 2630b4c231 | |||
| 630da3b5e3 | |||
| a88db0aa7d | |||
| f72b3f33a0 | |||
| 2c077e741d | |||
| 94c410a5af | |||
| a2a8932981 | |||
| e4a3186aad | |||
| dc89a3ec7c | |||
| b4bf6b9cc9 | |||
| 2d36836616 | |||
| 0b9888d038 | |||
| 43e2ca8fd1 | |||
| 8cda8bef1e | |||
| ff21d33bfe | |||
| bae72c4b8e | |||
| 23b52a1958 | |||
| 6218f315bb | |||
| 3f65050354 | |||
| f363ff348c | |||
| 003dc7ffc9 | |||
| 605928c15b | |||
| 9880bebd1a | |||
| 9677fbe029 | |||
| 6989f9be3d | |||
| ce14c2c7bd | |||
| 09149b4f0d | |||
| cb46cc6705 | |||
| 8fa26bf7e1 | |||
| cf59daf62f | |||
| 601ed4061a | |||
| 6905633e57 | |||
| 79b3de9a63 | |||
| 975decb5e3 | |||
| 58ec7b44fe | |||
| 57a063f599 | |||
| d944146ebe | |||
| 2e5ab9640e | |||
| 7c06e2e8d2 | |||
| a20c781343 | |||
| 599e8f7bb1 | |||
| 26f4fa9de7 | |||
| e52941fe5b | |||
| 337b918b92 | |||
| 323c20bbb7 | |||
| a11b21f1eb | |||
| 2430d76b1e | |||
| 9e490d7cf5 | |||
| 2929bc8d7e | |||
| a5470fe0ca | |||
| 06ddde0daa | |||
| 477d15998c | |||
| e8ea904cee | |||
| d17a352f23 | |||
| 787f6c8a7e | |||
| f6334efbd8 | |||
| 3a27fb824e | |||
| a39dc76dec | |||
| 3f29c1dd39 | |||
| b4a5df88fa | |||
| 9c0cd8d7a3 | |||
| 62bc3b2716 | |||
| fb953e0d8d | |||
| 92b5b1fb69 | |||
| 638a48885a | |||
| 45619f2f2f | |||
| 1824b8fb52 | |||
| cbadf3e29a | |||
| e7a9d2ce96 | |||
| a5b08c1d95 | |||
| 0116cc1f4d | |||
| 4141c905f3 | |||
| 4202a208c2 | |||
| 8c45804a1c | |||
| 97b6d31f41 | |||
| 24724b70f0 | |||
| 8667a87134 | |||
| fee675ffe0 | |||
| ce3ec2f172 | |||
| 3ad9ccaba7 | |||
| dae7527f4f | |||
| 9f63f863f1 | |||
| 83d5b3be12 | |||
| 2e28fc0dd6 | |||
| c5868657aa | |||
| 04bfe684c9 | |||
| e1a5d15d95 | |||
| 00b1399593 | |||
| 85c43fbd2e | |||
| 65237b2346 | |||
| 35af9ecd3d | |||
| 32b02c13d8 | |||
| bf74ef2e40 | |||
| b40bdad104 | |||
| ea5fa56ca9 | |||
| 0db9e85a7a | |||
| 5697fd53f2 | |||
| 4afdb40b4e | |||
| 296fd08875 | |||
| 03f51bf4e1 | |||
| a73b17564c | |||
| 2b0041e4dd | |||
| 7dad14ae79 | |||
| 40bac586dc | |||
| f439518781 | |||
| ed89a010cb | |||
| 0982fdccd9 | |||
| d8ca0ea86d | |||
| f6c377aa5b | |||
| 4684b69405 | |||
| d46ac7934a | |||
| 238059ac2a | |||
| 41df03637e | |||
| 090ae9aefe | |||
| 6aa7a269ae | |||
| 8ddf71d880 | |||
| 2d7fee45d6 | |||
| d54eebbfa7 | |||
| f1fc372df1 | |||
| c8afdc93f9 | |||
| c2c6d152ad | |||
| 66fdcd5f33 | |||
| e3ee421314 | |||
| 83308760a2 | |||
| f526c8d303 | |||
| d95b52f1c9 | |||
| 6b9e7dc585 | |||
| bc48280fed | |||
| 7a931deab2 | |||
| b7f6be1b8b | |||
| 9c20241272 | |||
| b2fb025153 | |||
| 5e49e3df6e | |||
| 5bbc1b2e16 | |||
| ea9282cbd6 | |||
| 2c68bb3e51 | |||
| c505fc4d23 | |||
| 4063e6c058 | |||
| be23e6e2f2 | |||
| b7a8ef4b9f | |||
| 1934749029 | |||
| 35aaf2fe51 | |||
| 9c2e7a4e1c | |||
| e083ab2d02 | |||
| 2b00e17124 | |||
| 089ee94b88 | |||
| ab3498b057 | |||
| 7d4fb6880e | |||
| d238e6410f | |||
| 187844f712 | |||
| 6732a03531 | |||
| 8753f92cb2 | |||
| 0ebb98ab3d | |||
| 30b50638a0 | |||
| 25fc970f44 | |||
| 1e2401a5ff | |||
| 2025065931 | |||
| 169bdd890f | |||
| ecfed4d70f | |||
| 8a8223c193 | |||
| 1273af1bdd | |||
| 3e325b42bb | |||
| f12b544515 | |||
| fb512bfa3d | |||
| 0099355bc6 | |||
| 799fb759a7 | |||
| 0cda96f1be | |||
| c2d3708224 | |||
| 36221652a4 | |||
| 7ed83e0bee | |||
| 06e7d91f55 | |||
| 4e0c2a8abd | |||
| 04c0c92f87 | |||
| ad58b620b2 | |||
| d7f18db74a | |||
| 1eca32bb84 | |||
| 4601ea8f82 | |||
| bf4f376060 | |||
| ee8dfc0062 | |||
| 335b5bc540 | |||
| 7250dfeb2e | |||
| feb938dfb8 | |||
| 4b8cd2cebd | |||
| 64b707eb67 | |||
| f85c4ca3ae | |||
| a80ffa4bc5 | |||
| 866894454f | |||
| 717beed8d7 | |||
| 03dbcd82a4 | |||
| 9c68e2ecc9 | |||
| b4b25eac9a | |||
| bed1a7cdb7 | |||
| 6600c532c4 | |||
| 81573c53b6 | |||
| d00557bd19 | |||
| dca21ef42f | |||
| 70ada64a98 | |||
| d40031df15 | |||
| adfd43db6b | |||
| aabe38f06c | |||
| a183cc3c4e | |||
| 3b65445f4e | |||
| a549241728 | |||
| b3c93cfe90 | |||
| 35ee68ab69 | |||
| 6caf9be863 | |||
| 1b6e9574a6 | |||
| e8990fd220 | |||
| a1f79adde9 | |||
| 6cc8f3cd71 | |||
| 3c519d4917 | |||
| 9d885235eb | |||
| e97a45ef8c | |||
| 74eeb7a759 | |||
| ed64bcfb71 | |||
| c78896e675 | |||
| aae54f903c | |||
| 98376bc8ee | |||
| 150d76aa7b | |||
| 3d4f0fe7fe | |||
| e733615650 | |||
| 54fe936849 | |||
| 136ac83c82 | |||
| 7b0ddb2420 | |||
| 717283c038 | |||
| 15c15d95e9 | |||
| f72a4ab0af | |||
| a7ce3fc324 | |||
| 7329734ec2 | |||
| 545e1b60f1 | |||
| 5c90bec61e | |||
| ec6fc1aaec | |||
| 17d18351c6 | |||
| 1c7e0b1755 | |||
| 9fa91b8719 | |||
| c6cf1082cb | |||
| 68b8979137 | |||
| 4c5332cae7 | |||
| ceb5471884 | |||
| 375ac4f37a | |||
| 06adff95b4 | |||
| 6bb09a50fb | |||
| 298b0cd863 | |||
| e810f4dd7b | |||
| 2d1cd3076f | |||
| ced689a43a | |||
| d16d58e6f9 | |||
| 79c5b4d4af | |||
| ccc8d664fc | |||
| 71124e192f | |||
| e1e1069b45 | |||
| 57ab7cefdd | |||
| bd5be7c70a | |||
| 0144b11673 | |||
| ae44dad2e1 | |||
| c03bb9db0d | |||
| 1fd9485a51 | |||
| 54229b8e82 | |||
| eba730f986 | |||
| fba5b503f5 | |||
| 9a3b370061 | |||
| 2b84a7d5b0 | |||
| f31dc22ebb | |||
| 34d06de0dc | |||
| b6945930e8 | |||
| 67724f884a | |||
| b52c4ddfb9 | |||
| 617ab7847f | |||
| 2225dab801 | |||
| e1a662ee3a | |||
| f0cb97c9a3 | |||
| 5419ae4d83 | |||
| fc30d4e272 | |||
| 21ebaf9bc6 | |||
| a1b0d7e982 | |||
| 510da24778 | |||
| 2646b9016f | |||
| cd2385f608 | |||
| 5381fbc1a3 | |||
| d720463300 | |||
| 776562623c | |||
| 8a41c15bf9 | |||
| 46b29a75a0 | |||
| 984ba7e943 | |||
| 764857eb37 | |||
| dc0d0d3936 | |||
| 5c83319029 | |||
| d7d365f6eb | |||
| b716661366 | |||
| 4de6eafba3 | |||
| ee52b2f7d8 | |||
| 6d3cd34bd5 | |||
| df783a4ee1 | |||
| 4d72f25e9a | |||
| 5d8036c675 | |||
| 03bde25be9 | |||
| 3a1aec54a7 | |||
| 2a25a0bd60 | |||
| a080464e31 | |||
| 95e0ea69a2 | |||
| a678e0df39 | |||
| 5d686b19a5 | |||
| a908d95d9f | |||
| 423060ac2f | |||
| fa942b5e6f | |||
| f4a9e125bf | |||
| 642fa43b13 | |||
| e60516bdee | |||
| 980b128b37 | |||
| bd92ed7e11 | |||
| bf85e6a637 | |||
| 35989c6715 | |||
| aa10876c62 | |||
| 6dd8723620 | |||
| e985b20a77 | |||
| d6f8ebf1f0 | |||
| 28c554ec27 | |||
| eaa0c567fc | |||
| 9e056e8fd4 | |||
| 73ed33953a | |||
| 032c685472 | |||
| a7d26bdc33 | |||
| 5685b0a72a | |||
| 5dada6b465 | |||
| b93d0f8975 | |||
| 60b3fc8239 | |||
| f961ef3e6b | |||
| d91b7625cf | |||
| 4df45d1c4c | |||
| 826a132791 | |||
| 24a7851fbe | |||
| daf119f2a7 | |||
| 4dccb3767a | |||
| ea5f871d29 | |||
| 4755ab5b50 | |||
| 6d05b0910a | |||
| e5f9640223 | |||
| 32c25e8c89 | |||
| 4e4d350967 | |||
| 2ce315c588 | |||
| 34d68139ac | |||
| b09c2c7d00 | |||
| a382054837 | |||
| 56dce4ae43 | |||
| 2390af8430 | |||
| 7c03565594 | |||
| e93fe105d1 | |||
| de56346eee | |||
| a5790756e0 | |||
| 94a280cb99 | |||
| 039a1134cd | |||
| ce35f42c0a | |||
| 6ec5ece968 | |||
| 29106bdaee | |||
| 98d9f2bdfa | |||
| 1996429dc6 | |||
| e1e1dad668 | |||
| d3dbacea08 | |||
| 293c476822 | |||
| e57a0ec211 | |||
| 95b2871550 | |||
| d4d0360604 | |||
| eb9f980912 | |||
| dd452a314a | |||
| ab678735ab | |||
| c44fdcfab2 | |||
| d5ec476e36 | |||
| 985e3e8ddc | |||
| f101aa6c4b | |||
| ee58e62f23 | |||
| db26a32601 | |||
| 2012ea2e9d | |||
| c6a00f46ad | |||
| 907f72d4bc | |||
| 182a56e3b0 | |||
| e5e295beed | |||
| 672b085264 | |||
| d709399b64 | |||
| 7fb768e2c4 | |||
| f6e39cc95d | |||
| 4127c7ff87 | |||
| 90529a8d4d | |||
| 9c102058e1 | |||
| 9e8f68f4fa | |||
| f9f6ab0251 | |||
| a4cc5f1cbb | |||
| d7b991aeb7 | |||
| f71eec7604 | |||
| 710c3b8312 | |||
| a561a457dc | |||
| 945a752902 | |||
| 28a70ff15c | |||
| 98a4a08fce | |||
| ea0b7dc6d1 | |||
| a57e050f43 | |||
| 49c3eb8478 | |||
| 529987089d | |||
| ceca20b798 | |||
| 02aef365ba | |||
| 0a667c6614 | |||
| cfac0f5914 | |||
| d33d8f5b20 | |||
| 70fe3d8dea | |||
| 288b562e46 | |||
| 021650c4c0 | |||
| 352afcd0b3 | |||
| 413af94fd2 | |||
| 4ade6cdfc0 | |||
| d148d2a447 | |||
| 5d42b6a204 | |||
| 97a5f427d0 | |||
| 48f2f3cea6 | |||
| abe3c18cf6 | |||
| 0e1a4f39ac | |||
| a4cf1eb035 | |||
| c0d0aff4da | |||
| 8ae3debad0 | |||
| 0fea5c5d59 | |||
| afe4577256 | |||
| 1f52afb3fd | |||
| f63d9357bf | |||
| 2780bb1000 | |||
| 0a7e29739d | |||
| 89335330fa | |||
| 5c316f0910 | |||
| 47e7a8966e | |||
| d81273eaed | |||
| e36da5e16e | |||
| 2335aaf58b | |||
| 7f9e7c23e0 | |||
| 7de6eb1b49 | |||
| 3ca095f1eb | |||
| 92130916d5 | |||
| 92d4376c8e | |||
| 00b4b90361 | |||
| 6d71136e4a | |||
| 2c83e550c4 | |||
| 0cf8799ef7 | |||
| 046204e7b2 | |||
| 3d16a8193f | |||
| 1aef4b1298 | |||
| ca19757aec | |||
| e81dbbaa2e | |||
| 2d4f8209a7 | |||
| 3ecb692b43 | |||
| ca1de7fe4c | |||
| be57a27d6d | |||
| 459b54b45c | |||
| 9843ca071c | |||
| 5e72329889 | |||
| 2423f1c531 | |||
| 44320e9e08 | |||
| 057ec4bc97 | |||
| 4e61bfbb15 | |||
| 758a751fb2 | |||
| 7398acb56d | |||
| 16f7804d5f | |||
| ccfe2a2db1 | |||
| 03d7ba7055 | |||
| 3235d6cd41 | |||
| 4dfc1e9026 | |||
| ce59ef0ea9 | |||
| d5e9fa61d9 | |||
| 112ffb49d1 | |||
| 1ac1081953 | |||
| 54b40587e4 | |||
| ad96830a4b | |||
| 1d69abe558 | |||
| a924c1a401 | |||
| bb15b89375 | |||
| ef2b03e317 | |||
| b6027a2f15 | |||
| a74a2882ed | |||
| 3a3970c2e0 | |||
| a708562177 | |||
| b9d42020be | |||
| fea9c7c1a3 | |||
| 9cc27bd6ff | |||
| 5f8bd0fa71 | |||
| de3d75110c | |||
| 2bae4dd503 | |||
| 8e1ced7596 | |||
| d47962f7a9 | |||
| 366a427f04 | |||
| fa7c300edc | |||
| e390a51cc7 | |||
| 470a2ce330 | |||
| 4e0b5fd215 | |||
| eada832040 | |||
| 9116016f93 | |||
| bef3e0022c | |||
| a4a46d8a97 | |||
| 1fb8e34c9c | |||
| ae862db1f4 | |||
| 7884ae9bc3 | |||
| 856049d771 | |||
| cccaca258b | |||
| 6e135618be | |||
| 06e398f427 | |||
| 2ae8040373 | |||
| 7317074eb3 | |||
| ab2625529c | |||
| a35f5ff3c8 | |||
| 3a4d9fa228 | |||
| 720edd0c30 | |||
| e0554f7f31 | |||
| 55ecd940ac | |||
| 07ac997f1c | |||
| 09862e2b2f | |||
| 62cd4cca56 | |||
| a63b896055 | |||
| f52e43554f | |||
| b1bee3ffc5 | |||
| d5bcdeef9f | |||
| 9fdef6ae5b | |||
| 5e20def930 | |||
| 1a90ef6e58 | |||
| f1dfadb3cb | |||
| fa18a28277 | |||
| 30a1a0bf58 | |||
| 146f6a827b | |||
| da0169bca4 | |||
| dafbb5daf7 | |||
| 5a6da2c1b1 | |||
| 45fd15f824 | |||
| 61609e90d5 | |||
| 8523b03473 | |||
| 4a0b22d449 | |||
| 2c8ad4ef0b | |||
| 4b1532b064 | |||
| 74e7816a22 | |||
| 8ed320be86 | |||
| ed727f3c0f | |||
| 100ff71944 | |||
| ef636af49e | |||
| fcfa225e5d | |||
| e778aba584 | |||
| ed47df29e3 | |||
| f91bd73986 | |||
| 11808ee3c2 | |||
| e9fd7c335f | |||
| 617d8ab8dc | |||
| 2b805f0ae8 | |||
| 72eb10b467 | |||
| 324537287f | |||
| 6379f1d035 | |||
| f354aebc6f | |||
| ae8332f7c6 | |||
| 3ba1c11f3b | |||
| 5b60e21f1b | |||
| badad91ac3 | |||
| 92e04dcd15 | |||
| d218811e85 | |||
| db0ee0fe36 | |||
| e09e429f57 | |||
| cdd69f8e6d | |||
| 53d345a897 | |||
| b7c516a121 | |||
| 16150b75ac | |||
| 5816f75544 | |||
| 0ceef31d99 | |||
| 568315b0b8 | |||
| 3baf1f7ffa | |||
| be738fa181 | |||
| cdeb4ecf8e | |||
| 98577ce2b2 | |||
| aec147ec74 | |||
| cdb7f3c192 | |||
| 269488d1c3 | |||
| c6ebe25337 | |||
| 353af1c51a | |||
| e1ca6c4129 | |||
| cacbe466b2 | |||
| 7bef9b83fb | |||
| dfa3ed8761 | |||
| 73a709844b | |||
| bc8d442717 | |||
| 4adbf49d83 | |||
| 59df914983 | |||
| b04aed8db6 | |||
| fc1ca9a2e2 | |||
| f6f4f2914c | |||
| 4d61b8b1f9 | |||
| 31ecaf4a8a | |||
| ff29177a4c | |||
| 84baab05b2 | |||
| 838ac481f2 | |||
| 5151d05625 | |||
| 53d3d32992 | |||
| 9dbe43f044 | |||
| 36310012ff | |||
| 2f6ea75758 | |||
| d0dd083cbb | |||
| 416d6a17dc | |||
| 59dc9f1f1a | |||
| c2aea94511 | |||
| 94d06d7094 | |||
| 2f02949798 | |||
| 0d4a90000d | |||
| b660768ae0 | |||
| 586a9c14ad | |||
| 376f4813dd | |||
| b08bcb1ef4 | |||
| c1d570ebb1 | |||
| 76655021c2 | |||
| 2b781131ed | |||
| 4d08c352c3 | |||
| f4455b442c | |||
| 34a66dea4e | |||
| 9952cbc715 | |||
| 0cc550108a | |||
| 2c06ccec82 | |||
| 56ab056637 | |||
| 78aa2d5f02 | |||
| a9a8741270 | |||
| f0b62c43ef | |||
| a1308c1f98 | |||
| 42894aa3df | |||
| 8a3ce65044 | |||
| 630d32b159 | |||
| 7588b4e493 | |||
| 745c6dec4d | |||
| 59b077c020 | |||
| cad83fee9f | |||
| 9cd357768d | |||
| 029e63093d | |||
| f84f8b2a02 | |||
| c696a29058 | |||
| 2ef5959f8a | |||
| b38ee6daab | |||
| b4130c3d2a | |||
| 1ee3c53ed2 | |||
| c365ec7c61 | |||
| 2efdb20b0b | |||
| 0627a6b2d5 | |||
| 375a600b8b | |||
| 909dc85f2f | |||
| 2892434cdf | |||
| 5ab8cc2eaf | |||
| 2cfb80f68c | |||
| ffad150868 | |||
| cfaa0e566a | |||
| 326878d82c | |||
| bdfc3e9a6b | |||
| b75cdf36a8 | |||
| 55d0c80611 | |||
| 302e1e9a86 | |||
| faf2184f52 | |||
| 65e6598e7e | |||
| 503fadaeb9 | |||
| 37bc9ffc53 | |||
| 123d945027 | |||
| 254b69417b | |||
| 0102c06cb2 | |||
| 4086bf9823 | |||
| d724444dba | |||
| 033924c630 | |||
| ab9a01313f | |||
| ea6db832a0 | |||
| 2263656526 | |||
| d31da8e870 | |||
| 727a8da77f | |||
| 3bca19b38a | |||
| 387a97ae42 | |||
| b65a28b9d7 | |||
| f2b2279c1a | |||
| cdcae9d22e | |||
| 5b0d883e50 | |||
| be2358fe61 | |||
| 802c820334 | |||
| abed72f22c | |||
| 74fba99cc6 | |||
| 5460fa6efc | |||
| 353d6112d2 | |||
| d942d6daa7 | |||
| 7ba7667d8e | |||
| b34c174dfb | |||
| c3d30f2992 | |||
| 6cdc6ce355 | |||
| d4e58e630c | |||
| 31a3dc769d | |||
| 89db5e8946 | |||
| e64fa963de | |||
| e0b82c6616 | |||
| 5c733ab445 | |||
| fcd6a9ddcb | |||
| 93c0fdcd45 | |||
| 5eecfaf824 | |||
| 63116f2c8c | |||
| ad8733038e | |||
| e397b67946 | |||
| 97da5f31d4 | |||
| 9c46c784d5 | |||
| cdfb3d3a28 | |||
| 8d860d4a9b | |||
| f1a7d102dc | |||
| cab2f0f6bd | |||
| cb974c4c66 | |||
| 691f2d449a | |||
| d2bd35eab4 | |||
| 21889a5475 | |||
| c3e289440d | |||
| 16d5919206 | |||
| 5b1d7afb9c | |||
| df47dd16ce | |||
| 561450715b | |||
| 4d4eeedfa0 | |||
| 9fecdb3a45 | |||
| 872ee8bbe0 | |||
| 8d285ee398 | |||
| e6ef82180f | |||
| d51f6b010d | |||
| afada870e7 | |||
| 8a3348d49b | |||
| b5d2265b60 | |||
| 7e3e301bb1 | |||
| a3080c2bc3 | |||
| 609147a4d9 | |||
| 6384a61a99 | |||
| 4a9bfa95ed | |||
| 881b5377cf | |||
| a5e1f95467 | |||
| f54bf13cc1 | |||
| f45badafd5 | |||
| 5f5a7fcd62 | |||
| 35de65ec81 | |||
| 0d81bf5cb0 | |||
| 345b86ca4e | |||
| 4de668c30c | |||
| 214964b2a0 | |||
| f21586cfa1 | |||
| 5b9ee5a55e | |||
| c5c05aa40b | |||
| 71184d2bc8 | |||
| e26301b30a | |||
| 2232300639 | |||
| a86dda2a80 | |||
| ef7f059216 | |||
| 3c4d8b1f59 | |||
| d24380dd43 | |||
| 3cdc35dfc4 | |||
| 9e9e150b74 | |||
| 592470b07b | |||
| 971ad95f2b | |||
| cabc40db81 | |||
| e23abea88d | |||
| 7b4a2d578f | |||
| e7aa06520a | |||
| 2e21ec0d88 | |||
| 54350b7db2 | |||
| 590b231124 | |||
| 7825047f47 | |||
| 1f957995f6 | |||
| 1b9cc8bfb1 | |||
| 09ffcc5de1 | |||
| 23de6e6960 | |||
| f082115395 | |||
| 0f47a7d0d7 | |||
| 8d31f929db | |||
| a988a16e3d | |||
| 5743c0eab6 | |||
| 3f28239efe | |||
| ef8775b676 | |||
| 880c5a4360 | |||
| cd48588cf6 | |||
| 972760397f | |||
| 8024da670c | |||
| 72a05f9f4f | |||
| 29c7232f4f | |||
| bfdf90e68c | |||
| ff3f7c5291 | |||
| a9112c4932 | |||
| b928050af3 | |||
| 01e97d117f | |||
| 2c0397edfb | |||
| a911775cbe | |||
| c339917d7f | |||
| 1b3ee092db | |||
| b22dc19ce7 | |||
| 844c2c8a79 | |||
| 147863ac7c | |||
| dcd84a1051 | |||
| cfb50f6581 | |||
| cdc432429f | |||
| 5d319ba8ff | |||
| 706bd95ae8 | |||
| 40e9cf9df8 | |||
| c541e31931 | |||
| b2a449e783 | |||
| 11d61f3d00 | |||
| dc02a49fb7 | |||
| 63133ee688 | |||
| da598e31eb | |||
| e876360aaa | |||
| aa5ea9f0fe | |||
| 5045219ca9 | |||
| d30979dddb | |||
| 57ceacf8b4 | |||
| 12663d2e21 | |||
| 0b8514a99c | |||
| 264719c0b4 | |||
| 213d807a08 | |||
| 524df333bc | |||
| 3d85989efa | |||
| 199d201cce | |||
| f45791f436 | |||
| a22b7177d2 | |||
| 9ba1fce608 | |||
| 4e2ee7e962 | |||
| d0eafffed4 | |||
| 6163ce3dee | |||
| 02cdf856f6 | |||
| d226d46144 | |||
| 58997f47af | |||
| 66c07705f9 | |||
| bc654dd3dd | |||
| 882e7e4903 | |||
| f541e79196 | |||
| 942cf217c5 | |||
| 02f4bf505a | |||
| b39ddcd628 | |||
| 5420375d4a | |||
| f59ea9a10f | |||
| 242e027dea | |||
| ded8e6f0af | |||
| d6ac3fd2f2 | |||
| a61852a5e2 | |||
| 1ed83f1004 | |||
| d22cf2fd77 | |||
| 3a17600bed | |||
| 3939274a46 | |||
| 964ee85777 | |||
| f58f26a35d | |||
| fd8244fb26 | |||
| a6d08dfccb | |||
| b14e8f35f7 | |||
| 1c8f6d8807 | |||
| 5f91002b51 | |||
| 0e528d5361 | |||
| 30185113fd | |||
| f0c354c97a | |||
| 6e828316b3 | |||
| d979cd2690 | |||
| c665ef49ad | |||
| c718c9e6c5 | |||
| 1b1292b7b1 | |||
| 560b55c4c8 | |||
| 22624ad774 | |||
| cb35556472 | |||
| 185ef25243 | |||
| 848603d032 | |||
| db230d4833 | |||
| 716cc34a87 | |||
| fbe0a3950f | |||
| 90aaf67e5a | |||
| 0edb87c629 | |||
| 4dd3773283 | |||
| 8443f319e3 | |||
| d3210c2547 | |||
| 24a02ee51b | |||
| fc0a7648b5 | |||
| a72bcf11e0 | |||
| 3398112573 | |||
| f7206f9910 | |||
| b245562d0e | |||
| 2491dc812f | |||
| ee8f58eb66 | |||
| e3454c0708 | |||
| 61ec4a3d89 | |||
| 370a506ad0 | |||
| c78acc85f3 | |||
| b9a2dc60e1 | |||
| b513079ee2 | |||
| 410599dca2 | |||
| 551ca28108 | |||
| 147d2dfac5 | |||
| 1d15c51bf9 | |||
| 0d700d201a | |||
| d2fe280a6e | |||
| fce176dbd0 | |||
| 49b6e1dccf | |||
| 27081694f9 | |||
| 4d46c481ac | |||
| 06565e21ea | |||
| e45fb9e735 | |||
| e485f030e8 | |||
| 2412efdf89 | |||
| 9ff4828501 | |||
| bde76782eb | |||
| 36016e2960 | |||
| 381ece9410 | |||
| 5f31282c46 | |||
| acd7915371 | |||
| a55745ba7f | |||
| 42c58fe840 | |||
| ea068aeb22 | |||
| bc218feff1 | |||
| fac51ca754 | |||
| 6e5e1e7ee7 | |||
| c238c179d7 | |||
| 25e5488594 | |||
| b52c3b3fc7 | |||
| 7ca34c3838 | |||
| 0ee75f415a | |||
| d95073c9c7 | |||
| 5a7dfe59da | |||
| c5449eb30f | |||
| e12b1cf8ce | |||
| a32472bd31 | |||
| 955c931b45 | |||
| 5ef500c27b | |||
| 645a54107c | |||
| cd0f866aef | |||
| 2cf9e5070a | |||
| 357be047aa | |||
| bccd47ab4f | |||
| 5a3a155438 | |||
| 3f01ddd5d2 | |||
| 0c98f500e2 | |||
| 3c541ff26c | |||
| 7b08fe78e5 | |||
| 9efc8d1d9f | |||
| a5260c143c | |||
| 43b449fc18 | |||
| 08f0727864 | |||
| f4e3d5f30e | |||
| 23c31ec27d | |||
| 2e712d4eec | |||
| 7e4715a534 | |||
| 58754850ca | |||
| dbdd6b88ef | |||
| c52a4316d0 | |||
| bb81caeb1e | |||
| 6d0dad6290 | |||
| 1e74b276c4 | |||
| d49f1f8772 | |||
| f35171a9a8 | |||
| a3d9fc20cf | |||
| c52e381c8b | |||
| af89849067 | |||
| 4d2dcb6a5f | |||
| 9c05112910 | |||
| dc383b2bfd | |||
| c20301adf1 | |||
| ac4c2b3a48 | |||
|
|
9644f7c8c7 | ||
| 1a0a642063 | |||
| b0034fd7a2 | |||
| 33949188a7 | |||
| fda6430d6a | |||
| 700ff7b073 | |||
| 6f511f6722 | |||
| 7f79bc3054 | |||
| 8c95d02504 | |||
| 6c78f41a2f | |||
| 0b2164c5ed | |||
| b45cd6b8bd | |||
| dc01c108d4 | |||
| 075a36ae95 | |||
| c26a23f245 | |||
| f591503f32 | |||
| 206495642f | |||
| a6b66db204 | |||
| ecb8d0bdae | |||
| a00101ae5d | |||
| 6f79e52665 | |||
| 607942330c | |||
| 38b973a679 | |||
| de8a52dcee | |||
| b0ca3469ed | |||
| 25ab857e4d | |||
| a8eb9448e7 | |||
| f52b66c7ec | |||
| a0d9f387bb | |||
| e16afbe4f5 | |||
| 7921c6cb7e | |||
| def527c9b0 | |||
| f073f232cf | |||
| b7bc639d00 | |||
| d39f5109cc | |||
| 1ca951cb6f | |||
| 8b0706a6c1 | |||
| 180d46c76e | |||
| a1fef73075 | |||
| 1ebfffa310 | |||
|
|
0b15817ad2 | ||
|
|
e0ebaf8f3c | ||
| 8aa6ff8ec9 | |||
| 52578524bd | |||
| 00d456761f | |||
| 8ef0c6b0df | |||
| b277709d53 | |||
| f8909cfab7 | |||
| 4457268059 | |||
| b429156131 | |||
| 2428486775 | |||
| 468be50313 | |||
| e231654e24 | |||
| b0585aa4d4 | |||
| b04ec4faa5 | |||
| b46e26cd34 | |||
| 71ec8895b7 | |||
| 4022d8784c | |||
| a0364a6227 | |||
| 2a7b684a8a | |||
| b3d5ef9971 | |||
| bd56ee7a78 | |||
| 18820b10fd | |||
| 39989cb106 | |||
| 4f282b4216 | |||
| 3ea03318bb | |||
| d5546057f2 | |||
| 31c6db7d2a | |||
| 5b8feb038d | |||
| 80ce0cd20f | |||
|
|
d0cdadc81e | ||
| db88d6e1a5 | |||
| 6b1e4f51c0 | |||
| 05d85c52a2 | |||
| 8e9323cafa | |||
| afe19d51e3 | |||
| f149445d38 | |||
| c21f39578f | |||
| d51db9a526 | |||
| fb034c8e14 | |||
| 5176484fbc | |||
| 71d8ebd201 | |||
| 375577cfb9 | |||
| 92319905a0 | |||
| 2f3ccb7e90 | |||
| d537af9303 | |||
| 2be7226e86 | |||
| d022328974 | |||
| 7f802fb750 | |||
| 3411511499 | |||
| 645293e2d9 | |||
| c5caaafd7f | |||
| 363ba2ee7a | |||
| 219573de22 | |||
| 480e284250 | |||
| f0b7499750 | |||
| d63f673e05 | |||
| b07626e48b | |||
| 79b85c1426 | |||
| 787e97057a | |||
| eb403083c6 | |||
| d3763513ff | |||
| f9a555a60a | |||
| d91a5b69d9 | |||
| 46bd09b789 | |||
| faeb98b48e | |||
| 0b970bc160 | |||
| 795bae3410 | |||
| e606316ed6 | |||
| 13dce85083 | |||
| 0ef25d8001 | |||
| 104df5f5f2 | |||
| 69a8699b54 | |||
| 3d1de1e44e | |||
| ffbe506a55 | |||
| 3d2e279b3d | |||
| 6dc48fd18d | |||
| 218fcf4473 | |||
| 1fd26737b5 | |||
| 2f948952b5 | |||
| f6ef23ebe7 | |||
| fe5356bf25 | |||
| 4c8746a965 | |||
| 28ba204e17 | |||
| 56e541dba6 | |||
| 27b365058e | |||
| 22bd4ad4fb | |||
| 7ffa849297 | |||
| 385e61a122 | |||
| a5bc1b002f | |||
| 8e8049fa28 | |||
| b1fb402e1b | |||
| 69b0da1987 | |||
| ed4db612f8 | |||
| 3e406633f7 | |||
| 529b78160c | |||
| 38463de2d9 | |||
| b6e533a0d9 | |||
| 0f80acd54b | |||
| 0f3b50329c | |||
| 14bb065f13 | |||
| eaaa8610fa | |||
| 36f0132394 | |||
| 670537ed8a | |||
| bff87f3a9a | |||
| 5878add697 | |||
| 3e0fd90605 | |||
| 042cc17bab | |||
| 760e084791 | |||
| 9d3a92d3e3 | |||
| e54acfa2ff | |||
| 5841005187 | |||
| 4f5093f403 | |||
| 26d0bbffa8 | |||
| 773b6bbd42 | |||
| c2e63d660b | |||
| 33c29f35b8 | |||
| 4bbee4acf2 | |||
| 99e5f16d4a | |||
| 093583620c | |||
| d29052a514 | |||
| 937dde6060 | |||
| e41084f94a | |||
| 40b3c50837 | |||
| 9fb73c42fd | |||
| c62b1850e3 | |||
| 910431e382 | |||
| bd514d20e0 | |||
| e38e761ba6 | |||
| b645fa8d12 | |||
| 0bf70bde16 | |||
| f78e2837ff | |||
| deecc8c6f8 | |||
| 555d29a0fe | |||
| c28a441a9d | |||
| 74bdbb94e5 | |||
| d2c1f7b9ca | |||
| aa1c4663a1 | |||
| ac5bf8e4f9 | |||
| e87cd0c807 | |||
| e2a6763be3 | |||
| c2461a8b87 | |||
| f2d3539cb5 | |||
| dbc453f414 | |||
| 58c67cdde4 | |||
| 4ed19b6b1e | |||
| 888aec8234 | |||
| 5a1a8fa917 | |||
| ed2180832e | |||
| 904966b3ef | |||
| 6e83213a5f | |||
| 41f83ef571 | |||
| a8c46c4bf0 | |||
| 1b274efd7c | |||
| 248ee40712 | |||
| 7db0b9c265 | |||
| 7a3e5c1dad | |||
| 9b3e1859ef | |||
| 63a9949c27 | |||
| 6c3afa7801 | |||
| 7e4c489778 | |||
| c92dd7bed6 | |||
| 7de561f930 | |||
| 03f4489e4d | |||
| 93b01da75e | |||
| 3976570539 | |||
| 2d925db8b0 | |||
| fd0226f614 | |||
| 85536c5e75 | |||
| f54507724f | |||
| 54724cc5b3 | |||
| b60b193384 | |||
| 299ba77cc9 | |||
| c0fd3b6962 | |||
| dec218643e | |||
| 1cc5f83442 | |||
| e468ca977e | |||
| d9f02f209b | |||
| f169a5eeb7 | |||
| bdcca74b23 | |||
| 1f79a069d3 | |||
| d4f57524be | |||
| 89001688d6 | |||
| 2859cf6902 | |||
| bc9d51d048 | |||
| 712701a4ef | |||
| f777ac8a52 | |||
| efa9b1cc41 | |||
| 5de6dca2f5 | |||
| cb091c196f | |||
| d9348adb33 | |||
| f1c69c71c6 | |||
| b1425fc68b | |||
| 556691b19a | |||
| a0f9564d1b | |||
| c21d8bd0d9 | |||
| a18d338d3f | |||
| a781b6fc3a | |||
| 17dd4e03b8 | |||
| fa26630d87 | |||
| 937a448de2 | |||
| d6a7832f6d | |||
| 9869bc3bc4 | |||
| d6b7aecbbc | |||
| 5bcc78ada9 | |||
| c900098170 | |||
| 51fb6b5098 | |||
| 5a269bfb57 | |||
| f0c164b861 | |||
| a1ba33090b | |||
| 6a63774f43 | |||
| d0d6f38754 | |||
| f179b2dd30 | |||
| f9caf48eab | |||
| f039c43f95 | |||
| 4b42c112fd | |||
| 3dfbbf606e | |||
| 9a4d125533 | |||
| 7d5ee7c689 | |||
| c71997dd67 | |||
| fb26304aa2 | |||
| 8a69395d6a | |||
| 86fb4243d1 | |||
| ddf0b72870 | |||
| b7efaadb8e | |||
| 227a28e9e5 | |||
| b1d21db61b | |||
| 6356f27a87 | |||
| fa9f34c056 | |||
| 1180996832 | |||
| dd47cbf3b7 | |||
| 0767dad328 | |||
| 9fa5d75d80 | |||
| ff398d29a6 | |||
| 9a965af529 | |||
| 49d2d8b47e | |||
| 10c81747fe | |||
| c23ccfc0ac | |||
| 0645752ab7 | |||
| bd49708841 | |||
| c67953c53e | |||
| 182cc12015 | |||
| 83e75345a2 | |||
| e29914292a | |||
| 6793bab243 | |||
| 2c0c758d72 | |||
| 234f5d9407 | |||
| d0eed2a7ac | |||
| 023f477bdd | |||
| 8dd5be76c0 | |||
| 24c5a21977 | |||
| f2737b927d | |||
| 1272208b2b | |||
| d908edbc8a | |||
| 9fd56bed3f | |||
| e47c49c872 | |||
| 168f5964b6 | |||
| fe8cace993 | |||
| ea6bbc1aec | |||
| d1f05d2415 | |||
| a6914c973f | |||
| 7aa22a24a7 | |||
| 4672d7aa83 | |||
| d6230d1563 | |||
| e800a2d5fa | |||
| 0df9c55578 | |||
| a2b128a451 | |||
| 54f7163310 | |||
| 09bc3a0aac | |||
| da04c045fe | |||
| ce2081e5b3 | |||
| 06d8ed1a8a | |||
| ec7054772b | |||
| bd573cf2d7 | |||
| 667b56f87b | |||
| eddb4d0af4 | |||
| c41a9d0e88 | |||
| 55a74c35dd | |||
| c35feef507 | |||
| 9a02226aa2 | |||
| f4dac36c05 | |||
| a5b90dc661 | |||
| 70f8f3231b | |||
| 6a8985dfa5 | |||
| f0cc032f83 | |||
| 39ebd2fd1f | |||
| 8fec347121 | |||
| f058e7b11a | |||
| 8ba1856a13 | |||
| 38e223e392 | |||
| f388fdc4f1 | |||
| 36bd4e1225 | |||
| e95c0078a2 | |||
| 94d2a11636 | |||
| ad6fa16304 | |||
| c5d18a0a8a | |||
| 6d208e3183 | |||
| 89a8b1f4f5 | |||
| aefdc1807a | |||
| b5a74ae166 | |||
| 034956978e | |||
| fa9bf0c5b1 | |||
| 719357ffda | |||
| f2421c042e | |||
| 0b3ff01ba4 | |||
| 39476c5ce3 | |||
| 419f0de8d3 | |||
| 25b3b122e6 | |||
| 2e1ba1c02a | |||
| 65f2000075 | |||
| 3c979ed37e | |||
| 89b625a0ce | |||
| b6b26efb9f | |||
| a84fcf8cde | |||
| 60a2d39782 | |||
| 45b4704df6 | |||
| 3915ca912a | |||
| 82844c5f11 | |||
| b6c1d3e1bd | |||
| fe4c294656 | |||
| b6820e4a87 | |||
| da34e3476e | |||
| b0015fc7cd | |||
| c77abe1e8d | |||
| 5366c5c827 | |||
| 53d00eb08b | |||
| b124984220 | |||
| 3708e233b5 | |||
| 5b7f49edc4 | |||
| 800a578ed7 | |||
| 12149d8e1b | |||
| 05d742427f | |||
| d8719603f8 | |||
| db6a45ce1f | |||
| e0a7c11dd0 | |||
| 32e435bbc3 | |||
| 9f1c625056 | |||
| abb76708d1 | |||
| 79ec9180e6 | |||
| 57f7d83def | |||
| 8eb57698c4 | |||
| 401ff628bd | |||
| 75cc874922 | |||
| 7a62e775bb | |||
| 615269b749 | |||
| 202708eeae | |||
| 5114a48fd7 | |||
| 35dcbf506d | |||
| ed9bc5f5c0 | |||
| 5f02cc2d3a | |||
| 5216bf6014 | |||
| dee695f2da | |||
| 4d6b6ab7e3 | |||
| d7fd4ed39c | |||
| b5af31fb57 | |||
| 157f0aedcb | |||
| 37a3a41de8 | |||
| 8e2ae56b25 | |||
| 25e7319488 | |||
| 18e8a12ef1 | |||
| 9386bcb29a | |||
| 0135d47a06 | |||
| 1f9186c270 | |||
| 85b8376829 | |||
| 3d93967292 | |||
| 8b13cd86ec | |||
| ab0f8b46a6 | |||
| 8ea9f57601 | |||
| d00457ba64 | |||
| ee70791626 | |||
| 1104d30f9e | |||
| d191a1cee7 | |||
| d58a5ac3aa | |||
| f09794f280 | |||
| b197bf5903 | |||
| 317a2ff1e9 | |||
| d34c240ebf | |||
| 804ea8fe82 | |||
| 5e22b41ce1 | |||
| ab6106149f | |||
| 7b674cc684 | |||
| fa483d50d1 | |||
| 7c10fa0ac3 | |||
| 761090b9d4 | |||
| 0a07de8839 | |||
| 1b1fcd79da | |||
| 52040433a1 | |||
| a9c1831b07 | |||
| d2e4f94205 | |||
| 4b9b9c7813 | |||
| 72913dbafc | |||
| 6ab1d300d6 | |||
| 1bfe567129 | |||
| fb1fdbd217 | |||
| 43d037b086 | |||
| d845e15750 | |||
| 8364c3aafc | |||
| 81795f30b4 | |||
| f3b06e8b15 | |||
| 3d919e445a | |||
| 9863f2bfec | |||
| 438aae9d0f | |||
| 30dee2dbd2 | |||
| d94c249194 | |||
| 0d28e20bc3 | |||
| b01071a4f3 | |||
| 435698e219 | |||
| ad7c91b346 | |||
| fed773cec6 | |||
| 9d79363098 | |||
| 317158e106 | |||
| 6e86e314ef | |||
| 65292981a4 | |||
| 6dd1e480b5 | |||
| 2a0abf0adf | |||
| f7e7335123 | |||
| 49e5434e94 | |||
| b293739f1f | |||
| c8816dbd31 | |||
| 6bfa473b60 | |||
| cba2b0367c | |||
| 1251ef30aa | |||
| fe697e5c1f | |||
| db89c65746 | |||
| 03e95e569b | |||
| d71543bf60 | |||
| fce3694f0d | |||
| d824cb4a7f | |||
| 16a23f4195 | |||
| 7387e36267 | |||
| fc412c78b4 | |||
| ef8cbccd54 | |||
| 5503207730 | |||
| fff24d71f7 | |||
| 3c67bda1d2 | |||
| 2e701cbaaf | |||
| 20bd3b5ed1 | |||
| e852f7d356 | |||
| 6715600796 | |||
| b832be6206 | |||
| b2188f052b | |||
| f204ea5624 | |||
| 1d2adb08f8 | |||
| f7e0f429e2 | |||
| 2746f423bd | |||
| f67431d729 | |||
| 30c6880fe0 | |||
| fa79121db2 | |||
| d5f21d2aac | |||
| 7beef61562 | |||
| 57cb7849fa | |||
| cc8ff01d8e | |||
| 2c8e8d547f | |||
| 681ae8d03c | |||
| 0380b22606 | |||
| 452369f1b7 | |||
| 278dc870c0 | |||
| f9edcf46b3 | |||
| 873f483836 | |||
| 7de0213588 | |||
| 821b7e5360 | |||
| fc490e4236 | |||
| b71291c96d | |||
| e02befdd2c | |||
| 5e7dc72f11 | |||
| 057455726c | |||
| c5330b6d94 | |||
| 9328e6f0bf | |||
| d3c5e882b0 | |||
| 16c92706d8 | |||
| 7f410f0655 | |||
| cdd5d41e6e | |||
| 3a0cfed4aa | |||
| 82ae82488e | |||
| e7ff5b1470 | |||
| 7a6ab07e9e | |||
| bee737bbad | |||
| d66644dd87 | |||
| be0ed93f99 | |||
| b06ed62500 | |||
| 8628f329e8 | |||
| 4ae4d125cd | |||
| c44a8e5ede | |||
| 4cb0cb6a06 | |||
| 96f4921704 | |||
| a6b5767d27 | |||
| 28c4b3e6f8 | |||
| fefeaec48d | |||
| 5aa6c03fbc | |||
| f16c8eec40 | |||
| 10437bba07 | |||
| 2df06e3172 | |||
| d14073a5fc | |||
| 918cdba723 | |||
| 33dda8463d | |||
| b9fee1a5d7 | |||
| 9af1aa48ab | |||
| d9277bf1c4 | |||
| 5db4d8269e | |||
| d037390cd2 | |||
| 011e88d03c | |||
| 29413c894b | |||
| b80000f073 | |||
| 9c7ef45214 | |||
| bf55ab38d8 | |||
| 2448312278 | |||
| 8840be2fb9 | |||
| aaef6e6e3a | |||
| 7a7592e615 | |||
| c347373fc6 | |||
| 32fdee4444 | |||
| f5358bc122 | |||
| 9a9b1e1595 | |||
| 6e786c85ea | |||
| 49804947e9 | |||
| b22a82869c | |||
| 4fcd92f624 | |||
| d94d68afde | |||
| 5481e5119b | |||
| 1a7507709f | |||
| a0cff9838a | |||
| deb642d2ec | |||
| e0af39a0dc | |||
| 7db1888bf2 | |||
| a4fd3f7cc4 | |||
| 33e09fe233 | |||
| d41e65e44c | |||
| d5b0df396a | |||
| 940776370c | |||
| 59d9310b28 | |||
| 77d8f118ef | |||
| 2e2fab2f3b | |||
| 145e061bce | |||
| 05027c4d41 | |||
| d7e2e5f99d | |||
| ad516eda2f | |||
| 6ae9324ada | |||
| 8a6a59d99e | |||
| 7e2418a877 | |||
| 7afeb9775a | |||
| f52ff92cc3 | |||
| ab0c7b8485 | |||
| f67bb0ad35 | |||
| c74de0c2e9 | |||
| b9baffc59d | |||
| eb3fc2c23a | |||
| 468e2b8e66 | |||
| 0de90d6f88 | |||
| 50802c0e6c | |||
| 00bde8527d | |||
| ac57332ddf | |||
| eb2da8bc5a | |||
| 6f069a03d1 | |||
| 4bdc04e8fb | |||
| 37c0e0e6e9 | |||
| a8eb1d28db | |||
| 585d6a51c0 | |||
| 6335c3dee8 | |||
| 6289393f7b | |||
| d65427ca94 | |||
| 2c3d53cf7f | |||
| 3c5f321639 | |||
| af46f07ef5 | |||
| b7b2849c82 | |||
| ae346fb579 | |||
| 4fb660d036 | |||
| 27149c94ee | |||
| 2618c497c5 | |||
| 3c80e4bc92 | |||
| 59e58328ec | |||
| c839b2b7b5 | |||
| ea5a9c8ec0 | |||
| 88f5ba087a | |||
| 749af6d36c | |||
| b60dcf9765 | |||
| 8e6bdabb00 | |||
| eb78369271 | |||
| 686fa5ca84 | |||
| 0fd83de6c3 | |||
| 80a84f7c11 | |||
| aaf4b00ba1 | |||
| 2ec577c75e | |||
| 2f91feeb03 | |||
| 78362edb49 | |||
| 1e45e1b6b2 | |||
| 0bbf940965 | |||
| d183496162 | |||
| b6e0a11e41 | |||
| a8473ead9e | |||
| ffb79ef037 | |||
| e9b83d74d2 | |||
| d53b5af637 | |||
| 5f7e8e6670 | |||
| ee16f26610 | |||
| c2e13a2f94 | |||
| 19dda33497 | |||
| 8dd4082b27 | |||
| d206062e10 | |||
| 1d031a92a5 | |||
| 425d620d57 | |||
| f77dfe6e94 | |||
| b65783941c | |||
| 0f26e99517 | |||
| 506b6d7df9 | |||
| efd91c4701 | |||
| 21dfdbc58b | |||
| 7e767d0858 | |||
| 3be92ce31b | |||
| bb537df8d0 | |||
| 3dbd8b81ba | |||
| 6c5a2fecf6 | |||
| 4c85e861ea | |||
| b0f2b24160 | |||
| 40f65055e3 | |||
| 624794e006 | |||
| a0fcdb0f98 | |||
| 035d92dd1a | |||
| a4a69e605c | |||
| 105353359c | |||
| c500e94670 | |||
| d2e050e5e1 | |||
| 485b07dc55 | |||
| 58452b095d | |||
| 788297480f | |||
| fad7613963 | |||
| b928475df0 | |||
| 2835c8df6f | |||
| 83a8f830c0 | |||
| 7326d0a3f8 | |||
| 17441b327b | |||
| 700c2c823a | |||
| 58253b535f | |||
| dd55ac9128 | |||
| 878028d385 | |||
| 42d70c8152 | |||
| ab136cb188 | |||
| f2ed497129 | |||
| e858f2e9ff | |||
| 551ae21bff | |||
| 2fa66f6f96 | |||
| 8349161f14 | |||
| db8c116f5c | |||
| 938bd0edc4 | |||
| 40a2f975dc | |||
| 06fab69664 | |||
| ddb44cefbd | |||
| 6ea4623ea1 | |||
| 1d807a8399 | |||
| 6704c82293 | |||
| 333d82ebc0 | |||
| 165580c661 | |||
| 9728f45e89 | |||
| 2d7673578e | |||
| 6736ce6a03 | |||
| 059639e301 | |||
| c2e4c7490f | |||
| 4b5a23b096 | |||
| c5744593ec | |||
| b12daf64ca | |||
| 450d3be01f | |||
| 7c4208f8a9 | |||
| 5f0286302e | |||
| da5f1dd8e5 | |||
| b247fc7d46 | |||
| a19a5f8675 | |||
| 3a87096f24 | |||
| 8ee05156e6 | |||
| 098cb4ceaa | |||
| cf81da2547 | |||
| ab51c0e7dc | |||
| 3e5fb5d83e | |||
| d5bc8a6840 | |||
| db06b3a0b4 | |||
| da1524fca8 | |||
| 5a31c30b5c | |||
| cae29a0d9d | |||
| c9958be749 | |||
| 1294a9b5b4 | |||
| 9f53e98472 | |||
| a7a6fe3ebc | |||
| 5d590fc1bb | |||
| e5fa7e58ba | |||
| f1369dd6ac | |||
| ad051d46aa | |||
| a29da3be76 | |||
| 82c4bdc6d0 | |||
| f0cca10248 | |||
| 0bf6fa4729 | |||
| 9c7c2d0de3 | |||
| 0886cdb1fe | |||
| c7b1129e77 | |||
| bcc877e180 | |||
| 2a9316d33c | |||
| 831d5665b0 | |||
| cfb18bb6c3 | |||
| dd8d2517d6 | |||
| 5561684337 | |||
| f7cca29528 | |||
| c8bf758a9a | |||
| c74934cbf9 | |||
| 10fe6f6037 | |||
| 05969e5418 | |||
| 0a7715e308 | |||
| 3138c7c5d7 | |||
| 3134c78661 | |||
| 20c7fa6bcc | |||
| d43d28a348 | |||
| 5554851f15 | |||
| ae80a416bd | |||
| 7b15ec8713 | |||
| 647503ec47 | |||
| 7629b86581 | |||
| 913e130897 | |||
| de5971b1df | |||
| d600fb3a45 | |||
| 17bff65c6b | |||
| 962a64aaaa | |||
| 5d08ae535a | |||
| dd856ee973 | |||
| e8e94a6800 | |||
| 14125d2652 | |||
| 155c1de9ad | |||
| 8482ee2be9 | |||
| 270f4cb1f0 | |||
| 125ff9f320 | |||
| e8ecbf3a42 | |||
| b010464151 | |||
| e77d269088 | |||
| dd201e3fc3 | |||
| 4faf2de83a | |||
| aa05731607 | |||
| 81eecb252a | |||
| 97ecf23069 | |||
| 8e3178e1f2 | |||
| 2d73ff2380 | |||
| c49ca5c99f | |||
| b5fda893ce | |||
| bcc8a07fd3 | |||
| 5da27a41c1 | |||
| da1eb092eb | |||
| 93e38f5f6b | |||
| 4ffc3c46a1 | |||
| a38b89ecb5 | |||
| 270cee3d4a | |||
| dc26770356 | |||
| a4de314d20 | |||
| c77945366c | |||
| f693a5b66d | |||
| c76ed1781b | |||
| 941159fba5 | |||
| e3d3d2eec7 | |||
| 3fd4418cfd | |||
| c0b36372d0 | |||
| 3c04bdbddb | |||
| 17804e4ecb | |||
| dbc50aa156 | |||
| c2fa4a33fa | |||
| 9351db7146 | |||
| 4c0e7a285a | |||
| d0cbd39310 | |||
| 2c519ceb63 | |||
| 2bcf091ba9 | |||
| 80ec80cb83 | |||
| ed02e1d252 | |||
| d3013152ce | |||
| 3d797d0070 | |||
| 41cbcf96e9 | |||
| 01196be661 | |||
| 741ed59745 | |||
| b61fce82f9 | |||
| c5850f6ef1 | |||
| 56a0a71649 | |||
| 6f728beec9 | |||
| 6262b2bf7c | |||
| 0387abd20f | |||
| 473b79b4c2 | |||
| 6ffab86a4d | |||
| 63c4712450 | |||
| 4df64a4393 | |||
| 6ac4a72b28 | |||
| 77df7c61e6 | |||
| b8cb48b630 | |||
| a5413ba0f8 | |||
| 5ea27e3d14 | |||
| a34761afc0 | |||
| 5c72fb6a34 | |||
| 3913b81f9e | |||
| a02cf6a05b | |||
| 68174cf0b2 | |||
| cbb8bd467b | |||
| 8c37c378a4 | |||
| e59d0263ba | |||
| dac0f4864d | |||
| 28887080a5 | |||
| 3ab2a2af29 | |||
| d5b707b6fb | |||
| 9ec8e308e7 | |||
| b3283176a0 | |||
| 24c3b57ec3 | |||
| 7863cf89de | |||
| 900e7f6352 | |||
| bddc6999b0 | |||
| 357fa4a5ba | |||
| 0338da1063 | |||
| 341b078d91 | |||
| d8d6f2a521 | |||
| 92a3a4e029 | |||
| 0b63963c5a | |||
| a10f336346 | |||
| 1bde9ab004 | |||
| ffeea91347 | |||
| e0032e8446 | |||
| d6eb9668a0 | |||
| 3bc03102d8 | |||
| 3cddf75818 | |||
| 9d92456c88 | |||
| 9107197320 | |||
| 0a66b12776 | |||
| 9de7d3fa45 | |||
| 3f82445d43 | |||
| eb74be102e | |||
| 836a780649 | |||
| 8ab87c1998 | |||
| 39d4e0182c | |||
| 23c1b1edc8 | |||
| 13e06e7670 | |||
| 6dedb028d8 | |||
| 3b6bed43cd | |||
| 92c1d9044c | |||
| 6e4866465d | |||
| dbced1d563 | |||
| 4b6799394f | |||
| f7fd87d9d2 | |||
| 3cb181ffe0 | |||
| f959f4d2b2 | |||
| f2e5ed0e34 | |||
| 38531114f7 | |||
| 4a2193927d | |||
| a2ef8e34d5 | |||
| ec2c1e54ed | |||
| d9c7df2492 | |||
| 9f6e0a74cb | |||
| 192b5e9cfb | |||
| e0c2784327 | |||
| 9880a1b366 | |||
| 6151808f1b | |||
| 989c2691ac | |||
| 5fe4ad93bc | |||
| dbb52fec27 | |||
| 6df353d805 | |||
| bbb3770be2 | |||
| 49131279ee | |||
| c1267d9e01 | |||
| 4cd8792882 | |||
| 6ad32d6675 | |||
| 4390091f8b | |||
| 2a957e405d | |||
| c38b9e12b2 | |||
| 2a87cfd809 | |||
| 0ca57c14af | |||
| 5d4bf32c40 | |||
| 9deeb8eb22 | |||
| 676918e7a1 | |||
| 6e652a42fc | |||
| 640a744d54 | |||
| 1ab8b7f658 | |||
| 28051e2754 | |||
| bde00c4aed | |||
| 576243ba39 | |||
| f09f8ad61f | |||
| 034639a877 | |||
| fd5d972efb | |||
| ffe0796c41 | |||
| e8e0e7bc69 | |||
| dc2979a942 | |||
| 2524a5e509 | |||
| 45271f785a | |||
| f5d0dbed20 | |||
| 3b00f6625c | |||
| d1aa60da80 | |||
| 0e0495cf32 | |||
| 8f70d4986e | |||
| 6c1e5d7f34 | |||
| 1aa827d496 | |||
| b287de4e68 | |||
| f16e64435f | |||
| 26ce24b928 | |||
| e80d9a5391 | |||
| f48b9823a4 | |||
| 1242de1b6d | |||
| 64210d6682 | |||
| 049d66aa80 | |||
| da3d297b3f | |||
| 7b75b665d3 | |||
| c9eb9f4ad1 | |||
| df0b4a4d0e | |||
| c27bbb3eca | |||
| 3f6357b970 | |||
| 93dea8967d | |||
| 5b6dde93e9 | |||
| 1db1ac121a | |||
| b76f54e540 | |||
| d01cc80b08 | |||
| 76c3bd772e | |||
| e0531b4338 | |||
| 05985d98ff | |||
| 833cdfdd0c | |||
| 21f1e501a7 | |||
| bbede5b3bd | |||
| bae0d22e06 | |||
| eece95823c | |||
| 9ca8b7a444 | |||
| a9efbd6bcd | |||
| 04a4f1aa4d | |||
| ad168871b1 | |||
| 4346e88286 | |||
| ccc4285dee | |||
| a8bc2fc2b7 | |||
| 81b9187dda | |||
| 844745da77 | |||
| 34fda46a3e | |||
| 866e9c31e9 | |||
| 6a7efc26bd | |||
| 12904214d1 | |||
| 9b3e36f6c4 | |||
| 7178267f20 | |||
| 2141478180 | |||
| 5333710282 | |||
| 8c6457820b | |||
| bd54f55f3a | |||
| 2213234715 | |||
| 9e1d43367c | |||
| 06a966ebe7 | |||
| d348a27380 | |||
| b07b7d0a63 | |||
| 5d83b59751 | |||
| 9bd835e1fb | |||
| 4687f961f0 | |||
| 6615900384 | |||
| 955dc9f977 | |||
| a628f1842b | |||
| 158425fc61 | |||
| f92bc17cb3 | |||
| 7e2f02b019 | |||
| 45917d435d | |||
| e352d7d3ca | |||
| 750d47e4d9 | |||
| db03f3399c | |||
| 54d4464a29 | |||
| 5a1279fdce | |||
| 6742728ff1 | |||
| 338d84676f | |||
| b26a74c470 | |||
| d866113089 | |||
| 76a1e9bdb8 | |||
| f6f8323082 | |||
| 0f1b4d7c38 | |||
| 6f96045c2f | |||
| 9e8babf885 | |||
| ece895e7d5 | |||
| be53a35ad3 | |||
| e38e04e9af | |||
| 44d8980780 | |||
| 5ea3eca380 | |||
| 954b2de3d5 | |||
| 28f239a384 | |||
| decd4d8ecf | |||
| d3b807ff02 | |||
| 87b472e1f6 | |||
| 586210c437 | |||
| b4a3d036a9 | |||
| 49553de662 | |||
| 59116b27ea | |||
| c251ddeafc | |||
| 362f8ec767 | |||
| 66e4eb795d | |||
| 05e3ee5ae6 | |||
| 5296f03f9f | |||
| acbfda0c6b | |||
| 7c5cf3fd79 | |||
| 84670db6ea | |||
| 2e2f289d25 | |||
| 1456a030f4 | |||
| 0f42dd82ac | |||
| cb158219e0 | |||
| 6fa863f6d9 | |||
| 3273d6be58 | |||
| 4a3720740d | |||
| 52ff44d02c | |||
| 23a8864669 | |||
| 072fbb030a | |||
| 2865ca6fb6 | |||
| c789c5288d | |||
| dd5c352af5 | |||
| 8675779372 | |||
| 6ce1e30539 | |||
| 4e84ea3da0 | |||
| 2a8c7b8230 | |||
| bcb6fa6e51 | |||
| ac7107df7c | |||
| d9097f7484 | |||
| c1ba8dcd85 | |||
| 118ce79baf | |||
| 6e78ef3ce5 | |||
| 40d37cc8a0 | |||
| 9b2f1c0b38 | |||
| 212014e0d2 | |||
| 7ace279696 | |||
| 62059b465f | |||
| c7447b51bd | |||
| 02765a86cc | |||
| 62f4467a49 | |||
| 25e1a10de6 | |||
| 7b210749bb | |||
| b9a4ff5d38 | |||
| 166e05b6ed | |||
| b958410308 | |||
| 9f3ffe53bd | |||
| b0f77c11d1 | |||
| 6af5d940db | |||
| bd480232ee | |||
| 8259f92110 | |||
| 6304ecbb06 | |||
| 40c12c675f | |||
| b60be30ed5 | |||
| cdf4591edb | |||
| c7bf98a186 | |||
| 031bc7ca2d | |||
| 721581c366 | |||
| 925b0b732c | |||
| 65ee9b6233 | |||
| 439fdfcda7 | |||
| 80a994776b | |||
| aebddd0ed6 | |||
| 1a8bc27052 | |||
| 0b2baaab1a | |||
| 895f68c8b2 | |||
| f0524205d6 | |||
| a90dca0ff5 | |||
| c23e2becdc | |||
| 72593ecaed | |||
| 6b72c23e0a | |||
| 024247797f | |||
| de7094a930 | |||
| 9539b402b8 | |||
| 8b777f5acf | |||
| 9295e3bdde | |||
| bf8a98ffc5 | |||
| 20e12025ea | |||
| 9421bf6655 | |||
| d79805e1db | |||
| 13ef2b1065 | |||
| f463cfb4c9 | |||
| c3a3b2e9b1 | |||
| 28a1b801b1 | |||
| b033626948 | |||
| 0c15bc41b0 | |||
| d819361cc5 | |||
| dc171b2a3e | |||
| e77a8b97e1 | |||
| cf72da183d | |||
| 0efee91b63 | |||
| 95ccb24857 | |||
| 24d89e1cce | |||
| ad79af4843 | |||
| c6e4c29988 | |||
| b31b4d5df8 | |||
| b52af3a031 | |||
| 8450e44cb6 | |||
| fb038ebe53 | |||
| 1a73618b36 | |||
| 8968155985 | |||
| 5cc1bbd6de | |||
| 1ae0a8d760 | |||
| 6db961e8b2 | |||
| 1d64c35c3b | |||
| e25a0c4bb3 | |||
| 81d5383ce2 | |||
| 8788431086 | |||
| 1b90194608 | |||
| 8d5f02a191 | |||
| 28fc08bff9 | |||
| c59b457ee5 | |||
| 46f5cf2b41 | |||
| a2ca81bf6e | |||
| 7533b4416e | |||
| 4c499e6d09 | |||
| fae338bf7c | |||
| 37bb26a217 | |||
| 882ce6812f | |||
| f7a9d8f447 | |||
| c7e2b27199 | |||
| af2f924b11 | |||
| d0ae91b7c0 | |||
| dee5ee40e0 | |||
| 614c8be1d8 | |||
| 51885aea9d | |||
| 185c80e9ba | |||
| 255e1fe149 | |||
| bd3334feaa | |||
| c15103f454 | |||
| 121189e560 | |||
| 163af9c8cb | |||
| a0627749b6 | |||
| 5a637fafd2 | |||
| 391ee16746 | |||
| cfafdde927 | |||
| eaf5914496 | |||
| a8cb18c644 | |||
| b5f64cdb6e | |||
| 5ab0eca7c6 | |||
| 6281aa7f5d | |||
| 5c916fa8f8 | |||
| 7fa6389d62 | |||
| dcd90fae85 | |||
| 9f850e5e4e | |||
| 5b3f7c09d2 | |||
| fe5c13386c | |||
| 4688ed1e28 | |||
| 20f9248222 | |||
| 657204dfce | |||
| 6c49690847 | |||
| 57450b4fbe | |||
| 1c844a694a | |||
| a53b2b4564 | |||
| ac02c2daf2 | |||
| e57c3996bd | |||
| f6d994faac | |||
| 0c8c082f9d | |||
| 2e5df2cfa6 | |||
| 01d09210ff | |||
| 75ecfd03ab | |||
| 468f4f7985 | |||
| dcd4e2f767 | |||
| 151c9cd9ff | |||
| 9697e81dab | |||
| 70534e5106 | |||
| d11b9c7239 | |||
| e60d04b129 | |||
| 2c5ca6c7be | |||
| b0552bbe57 | |||
| 51aa004a39 | |||
| ebab49d135 | |||
| 8b0c9e4331 | |||
| 4b39cbdeef | |||
| 8ba4f227ea | |||
| 771074d848 | |||
| b23cf5f41a | |||
| 8f5e6cf087 | |||
| c4f1560ef3 | |||
| c1bad0da7a | |||
| 3dcfe05354 | |||
| a6374a2896 | |||
| 5a81ea8dcd | |||
| d8815d5b41 | |||
| f0d53df716 | |||
| 7310c95ab7 | |||
| 8a8b7d40ac | |||
| 21fc76b50d | |||
| 662b5ade05 | |||
| 0e177a3b50 | |||
| 149e6aa58c | |||
| c090a2f821 | |||
| e99f72ae6d | |||
| aa179bdfa3 | |||
| 0ed75e7dbe | |||
| 910d7d4be0 | |||
| 5ad9010ebf | |||
| a42fe3e7df | |||
| a74e970b1d | |||
| 80efd2fed8 | |||
| 2cdb3d825a | |||
| 8085925bc6 | |||
| 1f504e3bd9 | |||
| 73ceb30a98 | |||
| b25aa2c753 | |||
| 7260d0333e | |||
| bdf1522fae | |||
| 7333478220 | |||
| 4a502bef3c | |||
| d26f3663b1 | |||
| dd372c4d3d | |||
| c4d221b149 | |||
| dbc584ea9c | |||
| 97997dd056 | |||
| 060a6c0e06 | |||
| 451884d2ea | |||
| 367b065369 | |||
| 5c72773a6d | |||
| b7ec20cc27 | |||
| 3901ae2cb7 | |||
| e8187d58e5 | |||
| 9458baa8fe | |||
| c9a9e8670b | |||
| 5846212aab | |||
| 4108d2f4f9 | |||
| eaeda7dc6b | |||
| adba334093 | |||
| 36efb78551 | |||
| 038a32ce1c | |||
| 95f02d7806 | |||
| be308462b4 | |||
| 660ca276ca | |||
| daaa02f8b1 | |||
| 6593ba7e6b | |||
| 4f686995c8 | |||
| 2fbdd08636 | |||
| 1472b4a77f | |||
| de6a5ea3a0 | |||
| d82a6a89b4 | |||
| 72b67178dc | |||
| dbd5c54510 | |||
| 2218d7bf9e | |||
| e865bc730d | |||
| c4f39dd4aa | |||
| 263bf06f66 | |||
| fa306a3c4a | |||
| 08da311c74 | |||
| 513b7b3c78 | |||
| 2ac967c63e | |||
| 7699ea4d27 | |||
| e3c61a30e1 | |||
| c1c17ba542 | |||
| 0267002a47 | |||
| dfdc955b1c | |||
| 144a90688c | |||
| a2ecba884a | |||
| e417f07ee2 | |||
| 66389f8933 | |||
| cca48e2d00 | |||
| d533afa17b | |||
| 1a282bdca1 | |||
| 6922161179 | |||
| a5cf0149dc | |||
| 30d7e0814f | |||
| c3b7bfbded | |||
| 7df89986a5 | |||
| 34e1684fa2 | |||
| 47ea90771d | |||
| d93ecc84bb | |||
| b166e18d56 | |||
| d7fca3e468 | |||
| e3cdeb80b1 | |||
| f2c8e39d29 | |||
| 27dd613a84 | |||
| aafe1034bb | |||
| 393f220f48 | |||
| 79d2ea4f41 | |||
| 139cdff657 | |||
| 10395fd00d | |||
| e49edb2ef1 | |||
| fb3e05ff62 | |||
| 548eb3a822 | |||
| 62cfea48a3 | |||
| caa03d7a5d | |||
| edad572189 | |||
| 8133fb0fad | |||
| 4c1a59fa96 | |||
| 49385f89b9 | |||
| c4364f3499 | |||
| 791902b2d2 | |||
| 785289594b | |||
| ffdf77dd59 | |||
| 6ad32098af | |||
| 34026205f0 | |||
| 5743ffc46f | |||
| c3b0b85d47 | |||
| ac8555af9e | |||
| 999a802600 | |||
| 59d589e736 | |||
| 4c3941745a | |||
| 04350ca976 | |||
| ff59b09f02 | |||
| d5e675bddb | |||
| ecd2e2b454 | |||
| a9c1796470 | |||
| 05f9492580 | |||
| 328a6e9c91 | |||
| 7773e1c2af | |||
| 542caff21b | |||
| 4d572be4e6 | |||
| 74e40a8863 | |||
| 8d55ff177d | |||
| fff8781973 | |||
| 252e46a346 | |||
| 010a4a76b5 | |||
| 434e2afe3c | |||
| 5784e17ff7 | |||
| 617d35315c | |||
| f63f7c1c86 | |||
| d5e25680df | |||
| 6185d5fa7d | |||
| 25ff42d7d5 | |||
| 317a2880f6 | |||
| f4e7621ee7 | |||
| cbefe2ad55 | |||
| 0774265195 | |||
| 3012e7a3c2 | |||
| a22763349a | |||
| 4328946e12 | |||
| be5c59f09a | |||
| a52d9b4bac | |||
| 59970cc12f | |||
| 46a4cf3697 | |||
| ebc898756a | |||
| a9322b7e58 | |||
| 54b8d6ba0b | |||
| a3aaf9d4f9 | |||
| b36a4d7c72 | |||
| 178d41c250 | |||
| 520d7cd663 | |||
| bd44822f89 | |||
| 37e0362215 | |||
| 9b3a4c1697 | |||
| 09a561aa25 | |||
| 7fbebda942 | |||
| ade4c61063 |
@@ -49,6 +49,7 @@ if(NOT LLVM_ROOT_DIR)
|
||||
OUTPUT_VARIABLE LLVM_ROOT_DIR
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
set(LLVM_ROOT_DIR ${LLVM_ROOT_DIR} CACHE PATH "Path to the LLVM installation")
|
||||
set(LLVM_INCLUDE_DIRS ${LLVM_ROOT_DIR}/include CACHE PATH "Path to the LLVM include directory")
|
||||
endif()
|
||||
if(NOT LLVM_LIBPATH)
|
||||
execute_process(COMMAND ${LLVM_CONFIG} --libdir
|
||||
|
||||
@@ -33,6 +33,7 @@ _modules = [
|
||||
"file",
|
||||
"image",
|
||||
"mesh",
|
||||
"modifiers",
|
||||
"node",
|
||||
"object",
|
||||
"object_align",
|
||||
|
||||
91
release/scripts/startup/bl_operators/modifiers.py
Normal file
91
release/scripts/startup/bl_operators/modifiers.py
Normal file
@@ -0,0 +1,91 @@
|
||||
import bpy
|
||||
from bpy.props import (
|
||||
StringProperty,
|
||||
)
|
||||
|
||||
class ModifierOperator:
|
||||
object_name: StringProperty()
|
||||
modifier_name: StringProperty()
|
||||
|
||||
def get_modifier(self):
|
||||
ob = bpy.data.objects.get(self.object_name)
|
||||
if ob is None:
|
||||
return None
|
||||
return ob.modifiers.get(self.modifier_name)
|
||||
|
||||
class NewDeformationFunction(bpy.types.Operator, ModifierOperator):
|
||||
bl_idname = "fn.new_deformation_function"
|
||||
bl_label = "New Deformation Function"
|
||||
|
||||
def execute(self, context):
|
||||
mod = self.get_modifier()
|
||||
if mod is None:
|
||||
return {'CANCELLED'}
|
||||
|
||||
from nodes.node_operators import new_function_tree
|
||||
tree = new_function_tree("Deformation Function", [
|
||||
("Vector", "Old Position"),
|
||||
("Float", "Control 1"),
|
||||
("Integer", "Control 2")
|
||||
], [
|
||||
("Vector", "New Position"),
|
||||
])
|
||||
|
||||
tree.new_link(
|
||||
tree.get_input_nodes()[0].outputs[0],
|
||||
tree.get_output_nodes()[0].inputs[0])
|
||||
|
||||
mod.function_tree = tree
|
||||
return {'FINISHED'}
|
||||
|
||||
class NewPointGeneratorFunction(bpy.types.Operator, ModifierOperator):
|
||||
bl_idname = "fn.new_point_generator_function"
|
||||
bl_label = "New Point Generator Function"
|
||||
|
||||
def execute(self, context):
|
||||
mod = self.get_modifier()
|
||||
if mod is None:
|
||||
return {'CANCELLED'}
|
||||
|
||||
from nodes.node_operators import new_function_tree
|
||||
tree = new_function_tree("Point Generator", [
|
||||
("Float", "Control 1"),
|
||||
("Integer", "Control 2"),
|
||||
], [
|
||||
("Vector List", "Points"),
|
||||
])
|
||||
|
||||
mod.function_tree = tree
|
||||
return {'FINISHED'}
|
||||
|
||||
class NewParticleSimulationTree(bpy.types.Operator, ModifierOperator):
|
||||
bl_idname = "fn.new_particle_simulation_tree"
|
||||
bl_label = "New Particle Simulation Tree"
|
||||
|
||||
def execute(self, context):
|
||||
mod = self.get_modifier()
|
||||
if mod is None:
|
||||
return {'CANCELLED'}
|
||||
|
||||
tree = bpy.data.node_groups.new("Particle Simulation", "FunctionTree")
|
||||
|
||||
type_node = tree.nodes.new("fn_ParticleSystemNode")
|
||||
|
||||
emitter_node = tree.nodes.new("fn_InitialGridEmitterNode")
|
||||
emitter_node.location = (-250, 200)
|
||||
|
||||
gravity_node = tree.nodes.new("fn_ForceNode")
|
||||
gravity_node.inputs[0].value = (0, 0, -1)
|
||||
gravity_node.location = (-250, -100)
|
||||
|
||||
tree.links.new(emitter_node.outputs[0], type_node.inputs[0])
|
||||
tree.links.new(gravity_node.outputs[0], type_node.inputs[0])
|
||||
|
||||
mod.node_tree = tree
|
||||
return {'FINISHED'}
|
||||
|
||||
classes = (
|
||||
NewDeformationFunction,
|
||||
NewPointGeneratorFunction,
|
||||
NewParticleSimulationTree,
|
||||
)
|
||||
@@ -410,7 +410,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
|
||||
row.prop(md, "mid_level")
|
||||
row.prop(md, "strength")
|
||||
|
||||
def DYNAMIC_PAINT(self, layout, _ob, _md):
|
||||
def DYNAMIC_PAINT(self, layout, _ob, md):
|
||||
layout.label(text="Settings are inside the Physics tab")
|
||||
|
||||
def EDGE_SPLIT(self, layout, _ob, md):
|
||||
@@ -1803,6 +1803,42 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
|
||||
col.prop(md, "thresh", text="Threshold")
|
||||
col.prop(md, "face_influence")
|
||||
|
||||
def FUNCTION_DEFORM(self, layout, ob, md):
|
||||
layout.prop(md, "control1")
|
||||
layout.prop(md, "control2")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(md, "function_tree")
|
||||
props = row.operator("fn.new_deformation_function", text="", icon="ADD")
|
||||
props.object_name = ob.name
|
||||
props.modifier_name = md.name
|
||||
|
||||
def FUNCTION_POINTS(self, layout, ob, md):
|
||||
layout.prop(md, "control1")
|
||||
layout.prop(md, "control2")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(md, "function_tree")
|
||||
props = row.operator("fn.new_point_generator_function", text="", icon="ADD")
|
||||
props.object_name = ob.name
|
||||
props.modifier_name = md.name
|
||||
|
||||
def BPARTICLES(self, layout, ob, md):
|
||||
row = layout.row(align=True)
|
||||
row.prop(md, "node_tree")
|
||||
props = row.operator("fn.new_particle_simulation_tree", text="", icon="ADD")
|
||||
props.object_name = ob.name
|
||||
props.modifier_name = md.name
|
||||
|
||||
layout.operator("object.bparticles_clear_cache", text="Clear Cache")
|
||||
|
||||
layout.prop(md, "output_type")
|
||||
|
||||
def BPARTICLES_OUTPUT(self, layout, ob, md):
|
||||
layout.prop(md, "source_object")
|
||||
layout.prop(md, "source_particle_system", icon="PHYSICS")
|
||||
layout.prop(md, "output_type")
|
||||
|
||||
|
||||
class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel):
|
||||
bl_label = "Modifiers"
|
||||
|
||||
@@ -207,6 +207,11 @@ class NODE_MT_add(bpy.types.Menu):
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
from nodes.base import BaseTree
|
||||
tree = context.space_data.node_tree
|
||||
if isinstance(tree, BaseTree):
|
||||
return
|
||||
|
||||
layout.operator_context = 'INVOKE_DEFAULT'
|
||||
props = layout.operator("node.add_search", text="Search...", icon='VIEWZOOM')
|
||||
props.use_transform = True
|
||||
|
||||
@@ -1229,6 +1229,7 @@ class USERPREF_PT_file_paths_data(FilePathsPanel, Panel):
|
||||
col.prop(paths, "script_directory", text="Scripts")
|
||||
col.prop(paths, "sound_directory", text="Sounds")
|
||||
col.prop(paths, "temporary_directory", text="Temporary Files")
|
||||
col.prop(paths, "nodelib_directory", text="Nodelib Files")
|
||||
|
||||
|
||||
class USERPREF_PT_file_paths_render(FilePathsPanel, Panel):
|
||||
|
||||
@@ -2194,6 +2194,10 @@ class VIEW3D_MT_add(Menu):
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("fn.new_particle_system", text="Particle Simulation", icon='MOD_PARTICLES')
|
||||
|
||||
layout.separator()
|
||||
|
||||
if VIEW3D_MT_camera_add.is_extended():
|
||||
layout.menu("VIEW3D_MT_camera_add", icon='OUTLINER_OB_CAMERA')
|
||||
else:
|
||||
|
||||
3
release/scripts/startup/nodes/__init__.py
Normal file
3
release/scripts/startup/nodes/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from . auto_load import init, register, unregister
|
||||
|
||||
init()
|
||||
138
release/scripts/startup/nodes/auto_load.py
Normal file
138
release/scripts/startup/nodes/auto_load.py
Normal file
@@ -0,0 +1,138 @@
|
||||
import os
|
||||
import bpy
|
||||
import sys
|
||||
import typing
|
||||
import inspect
|
||||
import pkgutil
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
|
||||
__all__ = (
|
||||
"init",
|
||||
"register",
|
||||
"unregister",
|
||||
)
|
||||
|
||||
modules = None
|
||||
ordered_classes = None
|
||||
|
||||
def init():
|
||||
global modules
|
||||
global ordered_classes
|
||||
|
||||
modules = get_all_submodules(Path(__file__).parent)
|
||||
ordered_classes = get_ordered_classes_to_register(modules)
|
||||
|
||||
def register():
|
||||
for cls in ordered_classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
for module in modules:
|
||||
if module.__name__ == __name__:
|
||||
continue
|
||||
if hasattr(module, "register"):
|
||||
module.register()
|
||||
|
||||
def unregister():
|
||||
for cls in reversed(ordered_classes):
|
||||
bpy.utils.unregister_class(cls)
|
||||
|
||||
for module in modules:
|
||||
if module.__name__ == __name__:
|
||||
continue
|
||||
if hasattr(module, "unregister"):
|
||||
module.unregister()
|
||||
|
||||
|
||||
# Import modules
|
||||
#################################################
|
||||
|
||||
def get_all_submodules(directory):
|
||||
return list(iter_submodules(directory, directory.name))
|
||||
|
||||
def iter_submodules(path, package_name):
|
||||
for name in sorted(iter_submodule_names(path)):
|
||||
yield importlib.import_module("." + name, package_name)
|
||||
|
||||
def iter_submodule_names(path, root=""):
|
||||
for _, module_name, is_package in pkgutil.iter_modules([str(path)]):
|
||||
if is_package:
|
||||
sub_path = path / module_name
|
||||
sub_root = root + module_name + "."
|
||||
yield from iter_submodule_names(sub_path, sub_root)
|
||||
else:
|
||||
yield root + module_name
|
||||
|
||||
|
||||
# Find classes to register
|
||||
#################################################
|
||||
|
||||
def get_ordered_classes_to_register(modules):
|
||||
return toposort(get_register_deps_dict(modules))
|
||||
|
||||
def get_register_deps_dict(modules):
|
||||
deps_dict = {}
|
||||
classes_to_register = set(iter_classes_to_register(modules))
|
||||
for cls in classes_to_register:
|
||||
deps_dict[cls] = set(iter_own_register_deps(cls, classes_to_register))
|
||||
return deps_dict
|
||||
|
||||
def iter_own_register_deps(cls, own_classes):
|
||||
yield from (dep for dep in iter_register_deps(cls) if dep in own_classes)
|
||||
|
||||
def iter_register_deps(cls):
|
||||
for value in typing.get_type_hints(cls, {}, {}).values():
|
||||
dependency = get_dependency_from_annotation(value)
|
||||
if dependency is not None:
|
||||
yield dependency
|
||||
|
||||
def get_dependency_from_annotation(value):
|
||||
if isinstance(value, tuple) and len(value) == 2:
|
||||
if value[0] in (bpy.props.PointerProperty, bpy.props.CollectionProperty):
|
||||
return value[1]["type"]
|
||||
return None
|
||||
|
||||
def iter_classes_to_register(modules):
|
||||
base_types = get_register_base_types()
|
||||
for cls in get_classes_in_modules(modules):
|
||||
if any(base in base_types for base in cls.__bases__):
|
||||
if not getattr(cls, "is_registered", False):
|
||||
yield cls
|
||||
|
||||
def get_classes_in_modules(modules):
|
||||
classes = set()
|
||||
for module in modules:
|
||||
for cls in iter_classes_in_module(module):
|
||||
classes.add(cls)
|
||||
return classes
|
||||
|
||||
def iter_classes_in_module(module):
|
||||
for value in module.__dict__.values():
|
||||
if inspect.isclass(value):
|
||||
yield value
|
||||
|
||||
def get_register_base_types():
|
||||
return set(getattr(bpy.types, name) for name in [
|
||||
"Panel", "Operator", "PropertyGroup",
|
||||
"AddonPreferences", "Header", "Menu",
|
||||
"Node", "NodeSocket", "NodeTree",
|
||||
"UIList", "RenderEngine"
|
||||
])
|
||||
|
||||
|
||||
# Find order to register to solve dependencies
|
||||
#################################################
|
||||
|
||||
def toposort(deps_dict):
|
||||
sorted_list = []
|
||||
sorted_values = set()
|
||||
while len(deps_dict) > 0:
|
||||
unsorted = []
|
||||
for value, deps in deps_dict.items():
|
||||
if len(deps) == 0:
|
||||
sorted_list.append(value)
|
||||
sorted_values.add(value)
|
||||
else:
|
||||
unsorted.append(value)
|
||||
deps_dict = {value : deps_dict[value] - sorted_values for value in unsorted}
|
||||
return sorted_list
|
||||
303
release/scripts/startup/nodes/base.py
Normal file
303
release/scripts/startup/nodes/base.py
Normal file
@@ -0,0 +1,303 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from . utils.generic import iter_subclasses_recursive
|
||||
import itertools
|
||||
from collections import defaultdict
|
||||
|
||||
class BaseTree:
|
||||
def new_link(self, a, b):
|
||||
if a.is_output:
|
||||
self.links.new(a, b)
|
||||
else:
|
||||
self.links.new(b, a)
|
||||
|
||||
def update(self):
|
||||
self.sync()
|
||||
|
||||
def sync(self):
|
||||
from . sync import sync_trees_and_dependent_trees
|
||||
sync_trees_and_dependent_trees({self})
|
||||
|
||||
|
||||
class SocketValueStates:
|
||||
def __init__(self, node):
|
||||
self.node = node
|
||||
self.input_value_storage = dict()
|
||||
|
||||
def store_current(self):
|
||||
for socket in self.node.inputs:
|
||||
if not isinstance(socket, DataSocket):
|
||||
continue
|
||||
storage_id = (socket.data_type, socket.identifier)
|
||||
self.input_value_storage[storage_id] = socket.get_state()
|
||||
|
||||
def try_load(self):
|
||||
for socket in self.node.inputs:
|
||||
if not isinstance(socket, DataSocket):
|
||||
continue
|
||||
storage_id = (socket.data_type, socket.identifier)
|
||||
if storage_id in self.input_value_storage:
|
||||
socket.restore_state(self.input_value_storage[storage_id])
|
||||
|
||||
|
||||
def get_new_node_identifier():
|
||||
import uuid
|
||||
return str(uuid.uuid4())
|
||||
|
||||
|
||||
class BaseNode:
|
||||
search_terms = tuple()
|
||||
search_terms_only = False
|
||||
|
||||
identifier: StringProperty()
|
||||
|
||||
def init(self, context):
|
||||
self.identifier = get_new_node_identifier()
|
||||
|
||||
from . sync import skip_syncing
|
||||
with skip_syncing():
|
||||
self.init_props()
|
||||
builder = self.get_node_builder()
|
||||
builder.initialize_decls()
|
||||
builder.build()
|
||||
|
||||
def init_props(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def get_search_terms(cls):
|
||||
if not cls.search_terms_only:
|
||||
yield (cls.bl_label, dict())
|
||||
yield from cls.search_terms
|
||||
|
||||
def sync_tree(self, context=None):
|
||||
self.tree.sync()
|
||||
|
||||
def rebuild(self):
|
||||
from . sync import skip_syncing
|
||||
with skip_syncing():
|
||||
self.socket_value_states.store_current()
|
||||
linkage_state = LinkageState(self)
|
||||
|
||||
self.rebuild_fast()
|
||||
|
||||
self.socket_value_states.try_load()
|
||||
linkage_state.try_restore()
|
||||
|
||||
def rebuild_fast(self):
|
||||
from . sync import skip_syncing
|
||||
with skip_syncing():
|
||||
builder = self.get_node_builder()
|
||||
builder.build()
|
||||
_decl_map_per_node[self] = builder.get_sockets_decl_map()
|
||||
|
||||
@property
|
||||
def tree(self):
|
||||
return self.id_data
|
||||
|
||||
def get_node_builder(self):
|
||||
from . node_builder import NodeBuilder
|
||||
builder = NodeBuilder(self)
|
||||
self.declaration(builder)
|
||||
return builder
|
||||
|
||||
def declaration(self, builder):
|
||||
raise NotImplementedError()
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
self.draw(layout)
|
||||
for decl in self.decl_map.iter_decls():
|
||||
decl.draw_node(layout)
|
||||
|
||||
def draw_buttons_ext(self, context, layout):
|
||||
self.draw_advanced(layout)
|
||||
|
||||
def draw(self, layout):
|
||||
pass
|
||||
|
||||
def draw_advanced(self, layout):
|
||||
pass
|
||||
|
||||
def draw_socket(self, layout, socket, text, decl, index_in_decl):
|
||||
decl.draw_socket(layout, socket, index_in_decl)
|
||||
|
||||
def draw_label(self):
|
||||
if self.hide:
|
||||
return self.draw_closed_label()
|
||||
else:
|
||||
return self.bl_label
|
||||
|
||||
def draw_closed_label(self):
|
||||
return self.bl_label
|
||||
|
||||
def iter_directly_used_trees(self):
|
||||
return
|
||||
yield
|
||||
|
||||
def invoke_function(self,
|
||||
layout, function_name, text,
|
||||
*, icon="NONE", settings=tuple()):
|
||||
assert isinstance(settings, tuple)
|
||||
props = layout.operator("fn.node_operator", text=text, icon=icon)
|
||||
self._set_common_invoke_props(props, function_name, settings)
|
||||
|
||||
def invoke_type_selection(self,
|
||||
layout, function_name, text,
|
||||
*, mode="ALL", icon="NONE", settings=tuple()):
|
||||
assert isinstance(settings, tuple)
|
||||
props = layout.operator("fn.node_data_type_selector", text=text, icon=icon)
|
||||
self._set_common_invoke_props(props, function_name, settings)
|
||||
props.mode = mode
|
||||
|
||||
def invoke_group_selector(self,
|
||||
layout, function_name, text,
|
||||
*, icon="NONE", settings=tuple()):
|
||||
assert isinstance(settings, tuple)
|
||||
props = layout.operator("fn.node_group_selector", text=text, icon=icon)
|
||||
self._set_common_invoke_props(props, function_name, settings)
|
||||
|
||||
def _set_common_invoke_props(self, props, function_name, settings):
|
||||
props.tree_name = self.id_data.name
|
||||
props.node_name = self.name
|
||||
props.function_name = function_name
|
||||
props.settings_repr = repr(settings)
|
||||
|
||||
@classmethod
|
||||
def iter_final_subclasses(cls):
|
||||
yield from filter(lambda x: issubclass(x, bpy.types.Node), iter_subclasses_recursive(cls))
|
||||
|
||||
def find_input(self, identifier):
|
||||
for socket in self.inputs:
|
||||
if socket.identifier == identifier:
|
||||
return socket
|
||||
else:
|
||||
return None
|
||||
|
||||
def find_output(self, identifier):
|
||||
for socket in self.outputs:
|
||||
if socket.identifier == identifier:
|
||||
return socket
|
||||
else:
|
||||
return None
|
||||
|
||||
def find_socket(self, identifier, is_output):
|
||||
if is_output:
|
||||
return self.find_output(identifier)
|
||||
else:
|
||||
return self.find_input(identifier)
|
||||
|
||||
def iter_sockets(self):
|
||||
yield from self.inputs
|
||||
yield from self.outputs
|
||||
|
||||
# Storage
|
||||
#########################
|
||||
|
||||
@property
|
||||
def decl_map(self):
|
||||
if self not in _decl_map_per_node:
|
||||
builder = self.get_node_builder()
|
||||
_decl_map_per_node[self] = builder.get_sockets_decl_map()
|
||||
return _decl_map_per_node[self]
|
||||
|
||||
@property
|
||||
def socket_value_states(self):
|
||||
if self not in _socket_value_states_per_node:
|
||||
_socket_value_states_per_node[self] = SocketValueStates(self)
|
||||
return _socket_value_states_per_node[self]
|
||||
|
||||
def free(self):
|
||||
if self in _decl_map_per_node:
|
||||
del _decl_map_per_node[self]
|
||||
|
||||
def copy(self, src_node):
|
||||
self.identifier = get_new_node_identifier()
|
||||
self.duplicate(src_node)
|
||||
|
||||
def duplicate(self, src_node):
|
||||
pass
|
||||
|
||||
|
||||
class BaseSocket:
|
||||
color = (0, 0, 0, 0)
|
||||
|
||||
def draw_color(self, context, node):
|
||||
return self.color
|
||||
|
||||
def draw(self, context, layout, node, text):
|
||||
decl, index = self.get_decl_with_index(node)
|
||||
node.draw_socket(layout, self, text, decl, index)
|
||||
|
||||
def draw_self(self, layout, node, text):
|
||||
layout.label(text=text)
|
||||
|
||||
def get_index(self, node):
|
||||
if self.is_output:
|
||||
return tuple(node.outputs).index(self)
|
||||
else:
|
||||
return tuple(node.inputs).index(self)
|
||||
|
||||
def to_id(self, node):
|
||||
return (node, self.is_output, self.identifier)
|
||||
|
||||
def get_decl(self, node):
|
||||
return node.decl_map.get_decl_by_socket(self)
|
||||
|
||||
def get_decl_with_index(self, node):
|
||||
decl_map = node.decl_map
|
||||
decl = decl_map.get_decl_by_socket(self)
|
||||
index = decl_map.get_socket_index_in_decl(self)
|
||||
return decl, index
|
||||
|
||||
class FunctionNode(BaseNode):
|
||||
pass
|
||||
|
||||
class SimulationNode(BaseNode):
|
||||
pass
|
||||
|
||||
class DataSocket(BaseSocket):
|
||||
def draw_self(self, layout, node, text):
|
||||
if not (self.is_linked or self.is_output) and hasattr(self, "draw_property"):
|
||||
self.draw_property(layout, node, text)
|
||||
else:
|
||||
layout.label(text=text)
|
||||
|
||||
def get_state(self):
|
||||
return None
|
||||
|
||||
def restore_state(self, state):
|
||||
pass
|
||||
|
||||
class LinkageState:
|
||||
def __init__(self, node):
|
||||
self.node = node
|
||||
self.tree = node.tree
|
||||
self.links_per_input = defaultdict(set)
|
||||
self.links_per_output = defaultdict(set)
|
||||
|
||||
for link in self.tree.links:
|
||||
if link.from_node == node:
|
||||
self.links_per_output[link.from_socket.identifier].add(link.to_socket)
|
||||
if link.to_node == node:
|
||||
self.links_per_input[link.to_socket.identifier].add(link.from_socket)
|
||||
|
||||
def try_restore(self):
|
||||
tree = self.tree
|
||||
for socket in self.node.inputs:
|
||||
for from_socket in self.links_per_input[socket.identifier]:
|
||||
tree.links.new(socket, from_socket)
|
||||
for socket in self.node.outputs:
|
||||
for to_socket in self.links_per_output[socket.identifier]:
|
||||
tree.links.new(to_socket, socket)
|
||||
|
||||
|
||||
_decl_map_per_node = {}
|
||||
_socket_value_states_per_node = {}
|
||||
|
||||
@bpy.app.handlers.persistent
|
||||
def clear_cached_node_states(_):
|
||||
_decl_map_per_node.clear()
|
||||
_socket_value_states_per_node.clear()
|
||||
|
||||
def register():
|
||||
bpy.app.handlers.load_pre.append(clear_cached_node_states)
|
||||
@@ -0,0 +1,26 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from .. base import SimulationNode
|
||||
from .. node_builder import NodeBuilder
|
||||
|
||||
|
||||
class AlwaysExecuteNode(bpy.types.Node, SimulationNode):
|
||||
bl_idname = "fn_AlwaysExecuteNode"
|
||||
bl_label = "Always Execute"
|
||||
|
||||
execute__prop: NodeBuilder.ExecuteInputProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.execute_input("execute", "Execute", "execute__prop")
|
||||
builder.influences_output("influence", "Influence")
|
||||
|
||||
|
||||
class MultiExecuteNode(bpy.types.Node, SimulationNode):
|
||||
bl_idname = "fn_MultiExecuteNode"
|
||||
bl_label = "Multi Execute"
|
||||
|
||||
execute__prop: NodeBuilder.ExecuteInputProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.execute_input("execute", "Execute", "execute__prop")
|
||||
builder.execute_output("execute", "Execute")
|
||||
@@ -0,0 +1,11 @@
|
||||
import bpy
|
||||
from .. base import SimulationNode
|
||||
from .. node_builder import NodeBuilder
|
||||
|
||||
class CombineInfluencesNode(bpy.types.Node, SimulationNode):
|
||||
bl_idname = "fn_CombineInfluencesNode"
|
||||
bl_label = "Combine Influences"
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.influences_input("influences", "Influences")
|
||||
builder.influences_output("influences", "Influences")
|
||||
18
release/scripts/startup/nodes/bparticle_nodes/condition.py
Normal file
18
release/scripts/startup/nodes/bparticle_nodes/condition.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from .. base import SimulationNode
|
||||
from .. node_builder import NodeBuilder
|
||||
|
||||
class ParticleConditionNode(bpy.types.Node, SimulationNode):
|
||||
bl_idname = "fn_ParticleConditionNode"
|
||||
bl_label = "Particle Condition"
|
||||
|
||||
execute_if_true__prop: NodeBuilder.ExecuteInputProperty()
|
||||
execute_if_false__prop: NodeBuilder.ExecuteInputProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("condition", "Condition", "Boolean")
|
||||
builder.execute_input("execute_if_true", "Execute If True", "execute_if_true__prop")
|
||||
builder.execute_input("execute_if_false", "Execute If False", "execute_if_false__prop")
|
||||
|
||||
builder.execute_output("execute", "Execute")
|
||||
@@ -0,0 +1,46 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from .. base import SimulationNode, FunctionNode
|
||||
from .. node_builder import NodeBuilder
|
||||
|
||||
class SetParticleAttributeNode(bpy.types.Node, SimulationNode):
|
||||
bl_idname = "fn_SetParticleAttributeNode"
|
||||
bl_label = "Set Attribute"
|
||||
|
||||
attribute_type: StringProperty(
|
||||
name="Attribute Type",
|
||||
default="Float",
|
||||
update=SimulationNode.sync_tree,
|
||||
)
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("name", "Name", "Text", default="My Attribute", display_name=False)
|
||||
builder.fixed_input("value", "Value", self.attribute_type)
|
||||
builder.execute_output("execute", "Execute")
|
||||
|
||||
def draw(self, layout):
|
||||
self.invoke_type_selection(layout, "set_type", "Select Type", mode="BASE", icon="SETTINGS")
|
||||
|
||||
def set_type(self, data_type):
|
||||
self.attribute_type = data_type
|
||||
|
||||
|
||||
class GetParticleAttributeNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_GetParticleAttributeNode"
|
||||
bl_label = "Get Attribute"
|
||||
|
||||
attribute_type: StringProperty(
|
||||
name="Attribute Type",
|
||||
default="Float",
|
||||
update=SimulationNode.sync_tree,
|
||||
)
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("name", "Name", "Text", default="My Attribute", display_name=False)
|
||||
builder.fixed_output("value", "Value", self.attribute_type)
|
||||
|
||||
def draw(self, layout):
|
||||
self.invoke_type_selection(layout, "set_type", "Select Type", mode="BASE", icon="SETTINGS")
|
||||
|
||||
def set_type(self, data_type):
|
||||
self.attribute_type = data_type
|
||||
169
release/scripts/startup/nodes/bparticle_nodes/custom_emitter.py
Normal file
169
release/scripts/startup/nodes/bparticle_nodes/custom_emitter.py
Normal file
@@ -0,0 +1,169 @@
|
||||
import bpy
|
||||
import uuid
|
||||
from bpy.props import *
|
||||
from .. base import SimulationNode, DataSocket, FunctionNode
|
||||
from .. node_builder import NodeBuilder
|
||||
from .. types import type_infos
|
||||
from .. sync import skip_syncing
|
||||
|
||||
|
||||
class CustomEmitterAttribute(bpy.types.PropertyGroup):
|
||||
def sync_tree(self, context):
|
||||
self.id_data.sync()
|
||||
|
||||
attribute_name: StringProperty(update=sync_tree)
|
||||
attribute_type: StringProperty(update=sync_tree)
|
||||
identifier: StringProperty()
|
||||
is_list: NodeBuilder.VectorizedProperty()
|
||||
|
||||
|
||||
class CustomEmitter(bpy.types.Node, SimulationNode):
|
||||
bl_idname = "fn_CustomEmitterNode"
|
||||
bl_label = "Custom Emitter"
|
||||
|
||||
execute_on_birth__prop: NodeBuilder.ExecuteInputProperty()
|
||||
|
||||
attributes: CollectionProperty(
|
||||
type=CustomEmitterAttribute,
|
||||
)
|
||||
|
||||
birth_time_mode: EnumProperty(
|
||||
name="Birth Time Mode",
|
||||
items=[
|
||||
("NONE", "None", "Manually specify birth times of every particle", "NONE", 0),
|
||||
("BEGIN", "Begin", "Spawn particles at the beginning of each time step", "NONE", 1),
|
||||
("END", "End", "Spawn particles at the end of each time step", "NONE", 2),
|
||||
("RANDOM", "Random", "Spawn particles at random moments in the time step", "NONE", 3),
|
||||
("LINEAR", "Linear", "Distribute particles linearly in each time step", "NONE", 4),
|
||||
],
|
||||
default="END",
|
||||
)
|
||||
|
||||
def init_props(self):
|
||||
self.add_attribute("Vector", "Position")
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
for i, item in enumerate(self.attributes):
|
||||
builder.vectorized_input(
|
||||
item.identifier,
|
||||
f"attributes[{i}].is_list",
|
||||
item.attribute_name,
|
||||
item.attribute_name,
|
||||
item.attribute_type)
|
||||
builder.execute_input("execute_on_birth", "Execute on Birth", "execute_on_birth__prop")
|
||||
builder.influences_output("emitter", "Emitter")
|
||||
|
||||
def draw(self, layout):
|
||||
layout.prop(self, "birth_time_mode", text="Birth")
|
||||
self.invoke_type_selection(layout, "add_attribute", "Add Attribute", mode="BASE")
|
||||
|
||||
def draw_socket(self, layout, socket, text, decl, index_in_decl):
|
||||
if isinstance(socket, DataSocket):
|
||||
index = list(self.inputs).index(socket)
|
||||
item = self.attributes[index]
|
||||
col = layout.column(align=True)
|
||||
row = col.row(align=True)
|
||||
row.prop(item, "attribute_name", text="")
|
||||
self.invoke_type_selection(row, "set_attribute_type", "",
|
||||
icon="SETTINGS", mode="BASE", settings=(index, ))
|
||||
self.invoke_function(row, "remove_attribute", "", icon="X", settings=(index, ))
|
||||
if not socket.is_linked and hasattr(socket, "draw_property"):
|
||||
socket.draw_property(col, self, "")
|
||||
else:
|
||||
decl.draw_socket(layout, socket, index_in_decl)
|
||||
|
||||
def add_attribute(self, data_type, name="My Attribute"):
|
||||
with skip_syncing():
|
||||
item = self.attributes.add()
|
||||
item.identifier = str(uuid.uuid4())
|
||||
item.attribute_type = data_type
|
||||
item.attribute_name = name
|
||||
|
||||
self.sync_tree()
|
||||
|
||||
def remove_attribute(self, index):
|
||||
self.attributes.remove(index)
|
||||
self.sync_tree()
|
||||
|
||||
def set_attribute_type(self, data_type, index):
|
||||
self.attributes[index].attribute_type = data_type
|
||||
|
||||
class EmitterTimeInfoNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_EmitterTimeInfoNode"
|
||||
bl_label = "Emitter Time Info"
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_output("duration", "Duration", "Float")
|
||||
builder.fixed_output("begin", "Begin", "Float")
|
||||
builder.fixed_output("end", "End", "Float")
|
||||
builder.fixed_output("step", "Step", "Integer")
|
||||
|
||||
|
||||
class SpawnParticlesAttribute(bpy.types.PropertyGroup):
|
||||
def sync_tree(self, context):
|
||||
self.id_data.sync()
|
||||
|
||||
attribute_name: StringProperty(update=sync_tree)
|
||||
attribute_type: StringProperty(update=sync_tree)
|
||||
identifier: StringProperty()
|
||||
is_list: NodeBuilder.VectorizedProperty()
|
||||
|
||||
|
||||
class SpawnParticlesNode(bpy.types.Node, SimulationNode):
|
||||
bl_idname = "fn_SpawnParticlesNode"
|
||||
bl_label = "Spawn Particles"
|
||||
|
||||
execute_on_birth__prop: NodeBuilder.ExecuteInputProperty()
|
||||
|
||||
attributes: CollectionProperty(
|
||||
type=SpawnParticlesAttribute,
|
||||
)
|
||||
|
||||
def init_props(self):
|
||||
self.add_attribute("Vector", "Position")
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
for i, item in enumerate(self.attributes):
|
||||
builder.vectorized_input(
|
||||
item.identifier,
|
||||
f"attributes[{i}].is_list",
|
||||
item.attribute_name,
|
||||
item.attribute_name,
|
||||
item.attribute_type)
|
||||
builder.execute_input("execute_on_birth", "Execute on Birth", "execute_on_birth__prop")
|
||||
builder.execute_output("execute", "Execute")
|
||||
builder.influences_output("spawn_system", "Spawn System")
|
||||
|
||||
def draw(self, layout):
|
||||
self.invoke_type_selection(layout, "add_attribute", "Add Attribute", mode="BASE")
|
||||
|
||||
def draw_socket(self, layout, socket, text, decl, index_in_decl):
|
||||
if isinstance(socket, DataSocket):
|
||||
index = list(self.inputs).index(socket)
|
||||
item = self.attributes[index]
|
||||
col = layout.column(align=True)
|
||||
row = col.row(align=True)
|
||||
row.prop(item, "attribute_name", text="")
|
||||
self.invoke_type_selection(row, "set_attribute_type", "",
|
||||
icon="SETTINGS", mode="BASE", settings=(index, ))
|
||||
self.invoke_function(row, "remove_attribute", "", icon="X", settings=(index, ))
|
||||
if not socket.is_linked and hasattr(socket, "draw_property"):
|
||||
socket.draw_property(col, self, "")
|
||||
else:
|
||||
decl.draw_socket(layout, socket, index_in_decl)
|
||||
|
||||
def add_attribute(self, data_type, name="My Attribute"):
|
||||
with skip_syncing():
|
||||
item = self.attributes.add()
|
||||
item.identifier = str(uuid.uuid4())
|
||||
item.attribute_type = data_type
|
||||
item.attribute_name = name
|
||||
|
||||
self.sync_tree()
|
||||
|
||||
def remove_attribute(self, index):
|
||||
self.attributes.remove(index)
|
||||
self.sync_tree()
|
||||
|
||||
def set_attribute_type(self, data_type, index):
|
||||
self.attributes[index].attribute_type = data_type
|
||||
60
release/scripts/startup/nodes/bparticle_nodes/events.py
Normal file
60
release/scripts/startup/nodes/bparticle_nodes/events.py
Normal file
@@ -0,0 +1,60 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from .. base import SimulationNode, FunctionNode
|
||||
from .. node_builder import NodeBuilder
|
||||
|
||||
|
||||
class AgeReachedEventNode(bpy.types.Node, SimulationNode):
|
||||
bl_idname = "fn_AgeReachedEventNode"
|
||||
bl_label = "Age Reached Event"
|
||||
|
||||
execute_on_event__prop: NodeBuilder.ExecuteInputProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("age", "Age", "Float", default=3)
|
||||
builder.execute_input("execute_on_event", "Execute on Event", "execute_on_event__prop")
|
||||
|
||||
builder.influences_output("event", "Event")
|
||||
|
||||
|
||||
class MeshCollisionEventNode(bpy.types.Node, SimulationNode):
|
||||
bl_idname = "fn_MeshCollisionEventNode"
|
||||
bl_label = "Mesh Collision Event"
|
||||
|
||||
execute_on_event__prop: NodeBuilder.ExecuteInputProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("object", "Object", "Object")
|
||||
builder.execute_input("execute_on_event", "Execute on Event", "execute_on_event__prop")
|
||||
|
||||
builder.influences_output("event", "Event")
|
||||
|
||||
|
||||
class CustomEventNode(bpy.types.Node, SimulationNode):
|
||||
bl_idname = "fn_CustomEventNode"
|
||||
bl_label = "Custom Event"
|
||||
|
||||
execute_on_event__prop: NodeBuilder.ExecuteInputProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("condition", "Condition", "Boolean")
|
||||
builder.fixed_input("time_factor", "Time Factor", "Float", default=1.0)
|
||||
builder.execute_input("execute_on_event", "Execute on Event", "execute_on_event__prop")
|
||||
|
||||
builder.influences_output("event", "Event")
|
||||
|
||||
|
||||
class EventFilterEndTimeNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_EventFilterEndTimeNode"
|
||||
bl_label = "Event Filter End Time"
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_output("end_time", "End Time", "Float")
|
||||
|
||||
|
||||
class EventFilterDurationNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_EventFilterDurationNode"
|
||||
bl_label = "Event Filter Duration"
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_output("duration", "Duration", "Float")
|
||||
13
release/scripts/startup/nodes/bparticle_nodes/forces.py
Normal file
13
release/scripts/startup/nodes/bparticle_nodes/forces.py
Normal file
@@ -0,0 +1,13 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from .. base import SimulationNode
|
||||
from .. node_builder import NodeBuilder
|
||||
|
||||
|
||||
class ForceNode(bpy.types.Node, SimulationNode):
|
||||
bl_idname = "fn_ForceNode"
|
||||
bl_label = "Force"
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("force", "Force", "Vector")
|
||||
builder.influences_output("force", "Force")
|
||||
@@ -0,0 +1,19 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from .. base import SimulationNode
|
||||
from .. node_builder import NodeBuilder
|
||||
|
||||
class InitialGridEmitterNode(bpy.types.Node, SimulationNode):
|
||||
bl_idname = "fn_InitialGridEmitterNode"
|
||||
bl_label = "Initial Grid Emitter"
|
||||
|
||||
execute_on_birth__prop: NodeBuilder.ExecuteInputProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("amount_x", "Amount X", "Integer", default=50)
|
||||
builder.fixed_input("amount_y", "Amount Y", "Integer", default=50)
|
||||
builder.fixed_input("step_x", "Step X", "Float", default=0.2)
|
||||
builder.fixed_input("step_y", "Step Y", "Float", default=0.2)
|
||||
builder.fixed_input("size", "Size", "Float", default=0.01)
|
||||
builder.execute_input("execute_on_birth", "Execute on Birth", "execute_on_birth__prop")
|
||||
builder.influences_output("emitter", "Emitter")
|
||||
@@ -0,0 +1,32 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from .. base import SimulationNode
|
||||
from .. node_builder import NodeBuilder
|
||||
|
||||
class MeshEmitterNode(bpy.types.Node, SimulationNode):
|
||||
bl_idname = "fn_MeshEmitterNode"
|
||||
bl_label = "Mesh Emitter"
|
||||
|
||||
execute_on_birth__prop: NodeBuilder.ExecuteInputProperty()
|
||||
|
||||
density_mode: EnumProperty(
|
||||
name="Density Mode",
|
||||
items=[
|
||||
('UNIFORM', "Uniform", "", 'NONE', 0),
|
||||
('VERTEX_WEIGHTS', "Vertex Weights", "", 'NONE', 1),
|
||||
],
|
||||
update=SimulationNode.sync_tree,
|
||||
)
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("object", "Object", "Object")
|
||||
builder.fixed_input("rate", "Rate", "Float", default=10)
|
||||
|
||||
if self.density_mode == 'VERTEX_WEIGHTS':
|
||||
builder.fixed_input("density_vertex_group", "Density Group", "Text")
|
||||
|
||||
builder.execute_input("execute_on_birth", "Execute on Birth", "execute_on_birth__prop")
|
||||
builder.influences_output("emitter", "Emitter")
|
||||
|
||||
def draw(self, layout):
|
||||
layout.prop(self, "density_mode")
|
||||
@@ -0,0 +1,15 @@
|
||||
import bpy
|
||||
from .. base import SimulationNode
|
||||
from .. node_builder import NodeBuilder
|
||||
|
||||
class ParticleSystemNode(bpy.types.Node, SimulationNode):
|
||||
bl_idname = "fn_ParticleSystemNode"
|
||||
bl_label = "Particle System"
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.background_color((0.8, 0.5, 0.4))
|
||||
|
||||
builder.influences_input("influences", "Influences")
|
||||
|
||||
def draw(self, layout):
|
||||
layout.prop(self, "name", text="", icon="PHYSICS")
|
||||
@@ -0,0 +1,17 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from .. base import SimulationNode
|
||||
from .. node_builder import NodeBuilder
|
||||
|
||||
class PointEmitterNode(bpy.types.Node, SimulationNode):
|
||||
bl_idname = "fn_PointEmitterNode"
|
||||
bl_label = "Point Emitter"
|
||||
|
||||
execute_on_birth__prop: NodeBuilder.ExecuteInputProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("position", "Position", "Vector")
|
||||
builder.fixed_input("velocity", "Velocity", "Vector", default=(1, 0, 0))
|
||||
builder.fixed_input("size", "Size", "Float", default=0.01)
|
||||
builder.execute_input("execute_on_birth", "Execute on Birth", "execute_on_birth__prop")
|
||||
builder.influences_output("emitter", "Emitter")
|
||||
@@ -0,0 +1,13 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from .. base import SimulationNode
|
||||
from .. node_builder import NodeBuilder
|
||||
|
||||
class SizeOverTimeNode(bpy.types.Node, SimulationNode):
|
||||
bl_idname = "fn_SizeOverTimeNode"
|
||||
bl_label = "Size Over Time"
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("final_size", "Final Size", "Float", default=0.0)
|
||||
builder.fixed_input("final_age", "Final Age", "Float", default=3)
|
||||
builder.influences_output("influence", "Influence")
|
||||
16
release/scripts/startup/nodes/bparticle_nodes/trails.py
Normal file
16
release/scripts/startup/nodes/bparticle_nodes/trails.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from .. base import SimulationNode
|
||||
from .. node_builder import NodeBuilder
|
||||
|
||||
class ParticleTrailsNode(bpy.types.Node, SimulationNode):
|
||||
bl_idname = "fn_ParticleTrailsNode"
|
||||
bl_label = "Particle Trails"
|
||||
|
||||
execute_on_birth__prop: NodeBuilder.ExecuteInputProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("rate", "Rate", "Float", default=20)
|
||||
builder.execute_input("execute_on_birth", "Execute on Birth", "execute_on_birth__prop")
|
||||
builder.influences_output("main_system", "Main System")
|
||||
builder.influences_output("trail_system", "Trail System")
|
||||
12
release/scripts/startup/nodes/declaration/__init__.py
Normal file
12
release/scripts/startup/nodes/declaration/__init__.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from . base import NoDefaultValue
|
||||
from . fixed_type import FixedSocketDecl
|
||||
from . dynamic_list import ListSocketDecl
|
||||
from . base_list_variadic import BaseListVariadic
|
||||
from . vectorized import VectorizedInputDecl, VectorizedOutputDecl
|
||||
|
||||
from . bparticles import (
|
||||
InfluencesSocketDecl,
|
||||
ExecuteOutputDecl,
|
||||
ExecuteInputDecl,
|
||||
ExecuteInputListDecl,
|
||||
)
|
||||
38
release/scripts/startup/nodes/declaration/base.py
Normal file
38
release/scripts/startup/nodes/declaration/base.py
Normal file
@@ -0,0 +1,38 @@
|
||||
NoDefaultValue = object()
|
||||
|
||||
class SocketDeclBase:
|
||||
def init(self):
|
||||
pass
|
||||
|
||||
def build(self, node_sockets):
|
||||
raise NotImplementedError()
|
||||
|
||||
def init_default(self, node_sockets):
|
||||
pass
|
||||
|
||||
def amount(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def validate(self, sockets):
|
||||
raise NotImplementedError()
|
||||
|
||||
def draw_node(self, layout):
|
||||
pass
|
||||
|
||||
def draw_socket(self, layout, socket, index):
|
||||
socket.draw_self(layout, self, socket.name)
|
||||
|
||||
def operator_socket_call(self, own_socket, other_socket):
|
||||
pass
|
||||
|
||||
def _data_socket_test(self, socket, name, data_type, identifier):
|
||||
from .. base import DataSocket
|
||||
if not isinstance(socket, DataSocket):
|
||||
return False
|
||||
if socket.name != name:
|
||||
return False
|
||||
if socket.data_type != data_type:
|
||||
return False
|
||||
if socket.identifier != identifier:
|
||||
return False
|
||||
return True
|
||||
152
release/scripts/startup/nodes/declaration/base_list_variadic.py
Normal file
152
release/scripts/startup/nodes/declaration/base_list_variadic.py
Normal file
@@ -0,0 +1,152 @@
|
||||
import bpy
|
||||
import uuid
|
||||
from bpy.props import *
|
||||
from . base import SocketDeclBase
|
||||
from .. base import DataSocket
|
||||
from .. types import type_infos
|
||||
from .. sockets import OperatorSocket
|
||||
|
||||
class BaseListVariadic(SocketDeclBase):
|
||||
def __init__(self, node, identifier: str, prop_name: str, base_type: str, default_amount: int):
|
||||
self.node = node
|
||||
self.identifier_suffix = identifier
|
||||
self.prop_name = prop_name
|
||||
self.base_type = base_type
|
||||
self.list_type = type_infos.to_list(base_type)
|
||||
self.default_amount = default_amount
|
||||
|
||||
def init(self):
|
||||
collection = self.get_collection()
|
||||
for _ in range(self.default_amount):
|
||||
item = collection.add()
|
||||
item.state = "BASE"
|
||||
item.identifier = str(uuid.uuid4())
|
||||
|
||||
def build(self, node_sockets):
|
||||
return list(self._build(node_sockets))
|
||||
|
||||
def _build(self, node_sockets):
|
||||
for item in self.get_collection():
|
||||
data_type = self.base_type if item.state == "BASE" else self.list_type
|
||||
yield type_infos.build(
|
||||
data_type,
|
||||
node_sockets,
|
||||
"",
|
||||
item.identifier)
|
||||
yield node_sockets.new("fn_OperatorSocket", "Operator")
|
||||
|
||||
def validate(self, sockets):
|
||||
collection = self.get_collection()
|
||||
if len(sockets) != len(collection) + 1:
|
||||
return False
|
||||
|
||||
for socket, item in zip(sockets[:-1], collection):
|
||||
data_type = self.base_type if item.state == "BASE" else self.list_type
|
||||
if not self._data_socket_test(socket, "", data_type, item.identifier):
|
||||
return False
|
||||
|
||||
if sockets[-1].bl_idname != "fn_OperatorSocket":
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def draw_socket(self, layout, socket, index):
|
||||
if isinstance(socket, OperatorSocket):
|
||||
props = layout.operator("fn.new_base_list_variadic_input", text="New Input", icon='ADD')
|
||||
props.tree_name = self.node.tree.name
|
||||
props.node_name = self.node.name
|
||||
props.prop_name = self.prop_name
|
||||
else:
|
||||
row = layout.row(align=True)
|
||||
socket.draw_self(row, self.node, str(index))
|
||||
props = row.operator("fn.remove_base_list_variadic_input", text="", icon='X')
|
||||
props.tree_name = self.node.tree.name
|
||||
props.node_name = self.node.name
|
||||
props.prop_name = self.prop_name
|
||||
props.index = index
|
||||
|
||||
def operator_socket_call(self, own_socket, linked_socket, connected_sockets):
|
||||
if len(connected_sockets) != 1:
|
||||
return
|
||||
connected_socket = next(iter(connected_sockets))
|
||||
if not isinstance(connected_socket, DataSocket):
|
||||
return
|
||||
|
||||
is_output = own_socket.is_output
|
||||
origin_data_type = connected_socket.data_type
|
||||
|
||||
if type_infos.is_link_allowed(origin_data_type, self.base_type):
|
||||
state = "BASE"
|
||||
elif type_infos.is_link_allowed(origin_data_type, self.list_type):
|
||||
state = "LIST"
|
||||
else:
|
||||
return
|
||||
|
||||
collection = self.get_collection()
|
||||
item = collection.add()
|
||||
item.state = state
|
||||
item.identifier = str(uuid.uuid4())
|
||||
|
||||
self.node.rebuild()
|
||||
|
||||
new_socket = self.node.find_socket(item.identifier, is_output)
|
||||
self.node.tree.new_link(linked_socket, new_socket)
|
||||
|
||||
def amount(self):
|
||||
return len(self.get_collection()) + 1
|
||||
|
||||
def get_collection(self):
|
||||
return getattr(self.node, self.prop_name)
|
||||
|
||||
@classmethod
|
||||
def Property(cls):
|
||||
return CollectionProperty(type=BaseListVariadicPropertyGroup)
|
||||
|
||||
class BaseListVariadicPropertyGroup(bpy.types.PropertyGroup):
|
||||
bl_idname = "fn_BaseListVariadicPropertyGroup"
|
||||
|
||||
state: EnumProperty(
|
||||
default="BASE",
|
||||
items=[
|
||||
("BASE", "Base", "", "NONE", 0),
|
||||
("LIST", "Base", "", "NONE", 1)])
|
||||
identifier: StringProperty()
|
||||
|
||||
class NewBaseListVariadicInputOperator(bpy.types.Operator):
|
||||
bl_idname = "fn.new_base_list_variadic_input"
|
||||
bl_label = "New Pack List Input"
|
||||
bl_options = {'INTERNAL'}
|
||||
|
||||
tree_name: StringProperty()
|
||||
node_name: StringProperty()
|
||||
prop_name: StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
tree = bpy.data.node_groups[self.tree_name]
|
||||
node = tree.nodes[self.node_name]
|
||||
collection = getattr(node, self.prop_name)
|
||||
|
||||
item = collection.add()
|
||||
item.state = "BASE"
|
||||
item.identifier = str(uuid.uuid4())
|
||||
|
||||
tree.sync()
|
||||
return {'FINISHED'}
|
||||
|
||||
class RemoveBaseListVariadicInputOperator(bpy.types.Operator):
|
||||
bl_idname = "fn.remove_base_list_variadic_input"
|
||||
bl_label = "Remove Pack List Input"
|
||||
bl_options = {'INTERNAL'}
|
||||
|
||||
tree_name: StringProperty()
|
||||
node_name: StringProperty()
|
||||
prop_name: StringProperty()
|
||||
index: IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
tree = bpy.data.node_groups[self.tree_name]
|
||||
node = tree.nodes[self.node_name]
|
||||
collection = getattr(node, self.prop_name)
|
||||
collection.remove(self.index)
|
||||
tree.sync()
|
||||
return {'FINISHED'}
|
||||
180
release/scripts/startup/nodes/declaration/bparticles.py
Normal file
180
release/scripts/startup/nodes/declaration/bparticles.py
Normal file
@@ -0,0 +1,180 @@
|
||||
import bpy
|
||||
import uuid
|
||||
from bpy.props import *
|
||||
from . base import SocketDeclBase
|
||||
from .. sockets import OperatorSocket, ExecuteSocket
|
||||
|
||||
MAX_LINK_LIMIT = 4095
|
||||
|
||||
class InfluencesSocketDecl(SocketDeclBase):
|
||||
def __init__(self, node, identifier: str, display_name: str):
|
||||
self.node = node
|
||||
self.identifier = identifier
|
||||
self.display_name = display_name
|
||||
|
||||
def build(self, node_sockets):
|
||||
socket = node_sockets.new("fn_InfluencesSocket", self.display_name, identifier=self.identifier)
|
||||
socket.link_limit = MAX_LINK_LIMIT
|
||||
socket.display_shape = 'DIAMOND'
|
||||
return [socket]
|
||||
|
||||
def validate(self, sockets):
|
||||
if len(sockets) != 1:
|
||||
return False
|
||||
socket = sockets[0]
|
||||
if socket.bl_idname != "fn_InfluencesSocket":
|
||||
return False
|
||||
if socket.name != self.display_name:
|
||||
return False
|
||||
if socket.link_limit != MAX_LINK_LIMIT:
|
||||
return False
|
||||
return True
|
||||
|
||||
def amount(self):
|
||||
return 1
|
||||
|
||||
class ExecuteOutputDecl(SocketDeclBase):
|
||||
def __init__(self, node, identifier: str, display_name: str):
|
||||
self.node = node
|
||||
self.identifier = identifier
|
||||
self.display_name = display_name
|
||||
|
||||
def build(self, node_sockets):
|
||||
socket = node_sockets.new("fn_ExecuteSocket", self.display_name, identifier=self.identifier)
|
||||
socket.display_shape = 'SQUARE'
|
||||
return [socket]
|
||||
|
||||
def amount(self):
|
||||
return 1
|
||||
|
||||
def validate(self, sockets):
|
||||
if len(sockets) != 1:
|
||||
return False
|
||||
socket = sockets[0]
|
||||
if socket.name != self.display_name:
|
||||
return False
|
||||
elif socket.identifier != self.identifier:
|
||||
return False
|
||||
elif socket.bl_idname != "fn_ExecuteSocket":
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class ExecuteInputDecl(SocketDeclBase):
|
||||
def __init__(self, node, identifier: str, display_name: str):
|
||||
self.node = node
|
||||
self.identifier = identifier
|
||||
self.display_name = display_name
|
||||
|
||||
def build(self, node_sockets):
|
||||
socket = node_sockets.new("fn_ExecuteSocket", self.display_name, identifier=self.identifier)
|
||||
socket.display_shape = "SQUARE"
|
||||
return [socket]
|
||||
|
||||
def amount(self):
|
||||
return 1
|
||||
|
||||
def validate(self, sockets):
|
||||
if len(sockets) != 1:
|
||||
return False
|
||||
if sockets[0].bl_idname != "fn_ExecuteSocket":
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class ExecuteInputListDecl(SocketDeclBase):
|
||||
def __init__(self, node, identifier: str, prop_name: str, display_name: str):
|
||||
self.node = node
|
||||
self.identifier = identifier
|
||||
self.display_name = display_name
|
||||
self.prop_name = prop_name
|
||||
|
||||
def build(self, node_sockets):
|
||||
return list(self._build(node_sockets))
|
||||
|
||||
def _build(self, node_sockets):
|
||||
items = self.get_items()
|
||||
for i, item in enumerate(items):
|
||||
socket = node_sockets.new(
|
||||
"fn_ExecuteSocket",
|
||||
self.display_name if i == 0 else "Then",
|
||||
identifier=item.identifier)
|
||||
socket.display_shape = 'SQUARE'
|
||||
yield socket
|
||||
socket = node_sockets.new(
|
||||
"fn_OperatorSocket",
|
||||
self.display_name)
|
||||
socket.display_shape = 'SQUARE'
|
||||
yield socket
|
||||
|
||||
def amount(self):
|
||||
return len(self.get_items()) + 1
|
||||
|
||||
def get_items(self):
|
||||
return getattr(self.node, self.prop_name)
|
||||
|
||||
def validate(self, sockets):
|
||||
if len(sockets) != self.amount():
|
||||
return False
|
||||
|
||||
for socket, item in zip(sockets[:-1], self.get_items()):
|
||||
if socket.identifier != item.identifier:
|
||||
return False
|
||||
elif socket.bl_idname != "fn_ExecuteSocket":
|
||||
return False
|
||||
|
||||
if not isinstance(sockets[-1], OperatorSocket):
|
||||
return False
|
||||
if not sockets[-1].name == self.display_name:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def draw_socket(self, layout, socket, index):
|
||||
row = layout.row(align=True)
|
||||
if index == 0:
|
||||
row.label(text=self.display_name)
|
||||
else:
|
||||
row.label(text="Then")
|
||||
if isinstance(socket, ExecuteSocket):
|
||||
props = row.operator("fn.remove_execute_socket", text="", icon="X")
|
||||
props.tree_name = socket.id_data.name
|
||||
props.node_name = self.node.name
|
||||
props.prop_name = self.prop_name
|
||||
props.index = index
|
||||
|
||||
def operator_socket_call(self, own_socket, linked_socket, connected_sockets):
|
||||
item = self.get_items().add()
|
||||
item.identifier = str(uuid.uuid4())
|
||||
|
||||
self.node.rebuild()
|
||||
|
||||
new_socket = self.node.find_socket(item.identifier, False)
|
||||
self.node.tree.new_link(linked_socket, new_socket)
|
||||
|
||||
@classmethod
|
||||
def Property(cls):
|
||||
return CollectionProperty(type=ExecuteInputItem)
|
||||
|
||||
|
||||
class ExecuteInputItem(bpy.types.PropertyGroup):
|
||||
identifier: StringProperty()
|
||||
|
||||
|
||||
class RemoveExecuteSocketOperator(bpy.types.Operator):
|
||||
bl_idname = "fn.remove_execute_socket"
|
||||
bl_label = "Remove Execute Socket"
|
||||
bl_options = {'INTERNAL'}
|
||||
|
||||
tree_name: StringProperty()
|
||||
node_name: StringProperty()
|
||||
prop_name: StringProperty()
|
||||
index: IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
tree = bpy.data.node_groups[self.tree_name]
|
||||
node = tree.nodes[self.node_name]
|
||||
collection = getattr(node, self.prop_name)
|
||||
collection.remove(self.index)
|
||||
tree.sync()
|
||||
return {'FINISHED'}
|
||||
41
release/scripts/startup/nodes/declaration/dynamic_list.py
Normal file
41
release/scripts/startup/nodes/declaration/dynamic_list.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from bpy.props import *
|
||||
from . base import SocketDeclBase
|
||||
from .. types import type_infos
|
||||
|
||||
class ListSocketDecl(SocketDeclBase):
|
||||
def __init__(self, node, identifier: str, display_name: str, prop_name: str, list_or_base: str):
|
||||
self.node = node
|
||||
self.identifier = identifier
|
||||
self.display_name = display_name
|
||||
self.prop_name = prop_name
|
||||
self.list_or_base = list_or_base
|
||||
|
||||
def build(self, node_sockets):
|
||||
data_type = self.get_data_type()
|
||||
return [type_infos.build(
|
||||
data_type,
|
||||
node_sockets,
|
||||
self.display_name,
|
||||
self.identifier)]
|
||||
|
||||
def validate(self, sockets):
|
||||
if len(sockets) != 1:
|
||||
return False
|
||||
return self._data_socket_test(sockets[0],
|
||||
self.display_name, self.get_data_type(), self.identifier)
|
||||
|
||||
def get_data_type(self):
|
||||
base_type = getattr(self.node, self.prop_name)
|
||||
if self.list_or_base == "BASE":
|
||||
return base_type
|
||||
elif self.list_or_base == "LIST":
|
||||
return type_infos.to_list(base_type)
|
||||
else:
|
||||
assert False
|
||||
|
||||
def amount(self):
|
||||
return 1
|
||||
|
||||
@classmethod
|
||||
def Property(cls):
|
||||
return StringProperty(default="Float")
|
||||
38
release/scripts/startup/nodes/declaration/fixed_type.py
Normal file
38
release/scripts/startup/nodes/declaration/fixed_type.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from . base import SocketDeclBase, NoDefaultValue
|
||||
from .. types import type_infos
|
||||
|
||||
class FixedSocketDecl(SocketDeclBase):
|
||||
def __init__(self, node, identifier: str, display_name: str, data_type: str, default, socket_settings: dict):
|
||||
self.node = node
|
||||
self.identifier = identifier
|
||||
self.display_name = display_name
|
||||
self.data_type = data_type
|
||||
self.default = default
|
||||
self.socket_settings = socket_settings
|
||||
|
||||
def build(self, node_sockets):
|
||||
socket = type_infos.build(self.data_type,
|
||||
node_sockets,
|
||||
self.display_name,
|
||||
self.identifier)
|
||||
for name, value in self.socket_settings.items():
|
||||
setattr(socket, name, value)
|
||||
return [socket]
|
||||
|
||||
def init_default(self, node_sockets):
|
||||
if self.default is not NoDefaultValue:
|
||||
socket = node_sockets[0]
|
||||
socket.restore_state(self.default)
|
||||
|
||||
def validate(self, sockets):
|
||||
if len(sockets) != 1:
|
||||
return False
|
||||
socket = sockets[0]
|
||||
for name, value in self.socket_settings.items():
|
||||
if getattr(socket, name) != value:
|
||||
return False
|
||||
return self._data_socket_test(sockets[0],
|
||||
self.display_name, self.data_type, self.identifier)
|
||||
|
||||
def amount(self):
|
||||
return 1
|
||||
94
release/scripts/startup/nodes/declaration/vectorized.py
Normal file
94
release/scripts/startup/nodes/declaration/vectorized.py
Normal file
@@ -0,0 +1,94 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from . base import SocketDeclBase, NoDefaultValue
|
||||
from .. types import type_infos
|
||||
from .. utils.generic import getattr_recursive
|
||||
|
||||
class VectorizedDeclBase:
|
||||
def build(self, node_sockets):
|
||||
data_type, name = self.get_type_and_name()
|
||||
socket = type_infos.build(
|
||||
data_type,
|
||||
node_sockets,
|
||||
name,
|
||||
self.identifier)
|
||||
for prop_name, value in self.socket_settings.items():
|
||||
setattr(socket, prop_name, value)
|
||||
return [socket]
|
||||
|
||||
def validate(self, sockets):
|
||||
if len(sockets) != 1:
|
||||
return False
|
||||
|
||||
socket = sockets[0]
|
||||
for prop_name, value in self.socket_settings.items():
|
||||
if getattr(socket, prop_name) != value:
|
||||
return False
|
||||
|
||||
data_type, name = self.get_type_and_name()
|
||||
return self._data_socket_test(socket,
|
||||
name, data_type, self.identifier)
|
||||
|
||||
def amount(self):
|
||||
return 1
|
||||
|
||||
def get_type_and_name(self):
|
||||
if self.is_vectorized():
|
||||
return self.list_type, self.list_name
|
||||
else:
|
||||
return self.base_type, self.base_name
|
||||
|
||||
|
||||
class VectorizedInputDecl(VectorizedDeclBase, SocketDeclBase):
|
||||
def __init__(self,
|
||||
node, identifier, prop_name,
|
||||
base_name, list_name,
|
||||
base_type, default, socket_settings):
|
||||
self.node = node
|
||||
self.identifier = identifier
|
||||
self.prop_name = prop_name
|
||||
self.base_name = base_name
|
||||
self.list_name = list_name
|
||||
self.base_type = base_type
|
||||
self.list_type = type_infos.to_list(base_type)
|
||||
self.default = default
|
||||
self.socket_settings = socket_settings
|
||||
|
||||
def init_default(self, node_sockets):
|
||||
if self.default is not NoDefaultValue:
|
||||
socket = node_sockets[0]
|
||||
socket.restore_state(self.default)
|
||||
|
||||
def is_vectorized(self):
|
||||
stored = getattr_recursive(self.node, self.prop_name)
|
||||
if stored == "BASE":
|
||||
return False
|
||||
elif stored == "LIST":
|
||||
return True
|
||||
else:
|
||||
assert False
|
||||
|
||||
@staticmethod
|
||||
def Property():
|
||||
return StringProperty(default="BASE")
|
||||
|
||||
|
||||
class VectorizedOutputDecl(VectorizedDeclBase, SocketDeclBase):
|
||||
def __init__(self,
|
||||
node, identifier, input_prop_names,
|
||||
base_name, list_name,
|
||||
base_type, socket_settings):
|
||||
self.node = node
|
||||
self.identifier = identifier
|
||||
self.input_prop_names = input_prop_names
|
||||
self.base_name = base_name
|
||||
self.list_name = list_name
|
||||
self.base_type = base_type
|
||||
self.list_type = type_infos.to_list(base_type)
|
||||
self.socket_settings = socket_settings
|
||||
|
||||
def is_vectorized(self):
|
||||
for prop_name in self.input_prop_names:
|
||||
if getattr_recursive(self.node, prop_name) == "LIST":
|
||||
return True
|
||||
return False
|
||||
14
release/scripts/startup/nodes/file_load.py
Normal file
14
release/scripts/startup/nodes/file_load.py
Normal file
@@ -0,0 +1,14 @@
|
||||
import bpy
|
||||
from bpy.app.handlers import persistent
|
||||
|
||||
@persistent
|
||||
def file_load_handler(dummy):
|
||||
from . sync import sync_trees_and_dependent_trees
|
||||
node_trees = set(tree for tree in bpy.data.node_groups if tree.bl_idname == "FunctionTree")
|
||||
sync_trees_and_dependent_trees(node_trees)
|
||||
|
||||
def register():
|
||||
bpy.app.handlers.load_post.append(file_load_handler)
|
||||
|
||||
def unregister():
|
||||
bpy.app.handlers.load_post.remove(file_load_handler)
|
||||
58
release/scripts/startup/nodes/function_nodes/color.py
Normal file
58
release/scripts/startup/nodes/function_nodes/color.py
Normal file
@@ -0,0 +1,58 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from .. base import FunctionNode
|
||||
from .. node_builder import NodeBuilder
|
||||
|
||||
|
||||
class SeparateColorNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_SeparateColorNode"
|
||||
bl_label = "Separate Color"
|
||||
|
||||
use_list__color: NodeBuilder.VectorizedProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.vectorized_input(
|
||||
"color", "use_list__color",
|
||||
"Color", "Colors", "Color")
|
||||
|
||||
builder.vectorized_output(
|
||||
"red", ["use_list__color"],
|
||||
"Red", "Red", "Float")
|
||||
builder.vectorized_output(
|
||||
"green", ["use_list__color"],
|
||||
"Green", "Green", "Float")
|
||||
builder.vectorized_output(
|
||||
"blue", ["use_list__color"],
|
||||
"Blue", "Blue", "Float")
|
||||
builder.vectorized_output(
|
||||
"alpha", ["use_list__color"],
|
||||
"Alpha", "Alpha", "Float")
|
||||
|
||||
|
||||
class CombineColorNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_CombineColorNode"
|
||||
bl_label = "Combine Color"
|
||||
|
||||
use_list__red: NodeBuilder.VectorizedProperty()
|
||||
use_list__green: NodeBuilder.VectorizedProperty()
|
||||
use_list__blue: NodeBuilder.VectorizedProperty()
|
||||
use_list__alpha: NodeBuilder.VectorizedProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.vectorized_input(
|
||||
"red", "use_list__red",
|
||||
"Red", "Red", "Float")
|
||||
builder.vectorized_input(
|
||||
"green", "use_list__green",
|
||||
"Green", "Green", "Float")
|
||||
builder.vectorized_input(
|
||||
"blue", "use_list__blue",
|
||||
"Blue", "Blue", "Float")
|
||||
builder.vectorized_input(
|
||||
"alpha", "use_list__alpha",
|
||||
"Alpha", "Alpha", "Float",
|
||||
default=1.0)
|
||||
|
||||
builder.vectorized_output(
|
||||
"color", ["use_list__red", "use_list__green", "use_list__blue", "use_list__alpha"],
|
||||
"Color", "Colors", "Color")
|
||||
31
release/scripts/startup/nodes/function_nodes/float_range.py
Normal file
31
release/scripts/startup/nodes/function_nodes/float_range.py
Normal file
@@ -0,0 +1,31 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from .. base import FunctionNode
|
||||
from .. node_builder import NodeBuilder
|
||||
|
||||
class FloatRangeNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_FloatRangeNode"
|
||||
bl_label = "Float Range"
|
||||
|
||||
mode: EnumProperty(
|
||||
name="Mode",
|
||||
items=[
|
||||
("AMOUNT_START_STEP", "Amount / Start / Step", "", "NONE", 0),
|
||||
("AMOUNT_START_STOP", "Amount / Start / Stop", "", "NONE", 1),
|
||||
],
|
||||
default="AMOUNT_START_STOP",
|
||||
update=FunctionNode.sync_tree,
|
||||
)
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("amount", "Amount", "Integer", default=10)
|
||||
builder.fixed_input("start", "Start", "Float")
|
||||
if self.mode == "AMOUNT_START_STEP":
|
||||
builder.fixed_input("step", "Step", "Float")
|
||||
elif self.mode == "AMOUNT_START_STOP":
|
||||
builder.fixed_input("stop", "Stop", "Float", default=1)
|
||||
|
||||
builder.fixed_output("list", "List", "Float List")
|
||||
|
||||
def draw(self, layout):
|
||||
layout.prop(self, "mode", text="")
|
||||
524
release/scripts/startup/nodes/function_nodes/groups.py
Normal file
524
release/scripts/startup/nodes/function_nodes/groups.py
Normal file
@@ -0,0 +1,524 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from .. types import type_infos
|
||||
from .. base import BaseNode, FunctionNode, DataSocket
|
||||
from .. function_tree import FunctionTree
|
||||
from .. node_builder import NodeBuilder
|
||||
from .. ui import NodeSidebarPanel
|
||||
from .. utils.pie_menu_helper import PieMenuHelper
|
||||
from .. sync import skip_syncing
|
||||
|
||||
interface_type_items = [
|
||||
("DATA", "Data", "Some data type like integer or vector", "NONE", 0),
|
||||
("EXECUTE", "Control Flow", "", "NONE", 1),
|
||||
("INFLUENCES", "Influences", "", "NONE", 2),
|
||||
]
|
||||
|
||||
class GroupInputNode(bpy.types.Node, BaseNode):
|
||||
bl_idname = "fn_GroupInputNode"
|
||||
bl_label = "Group Input"
|
||||
|
||||
input_name: StringProperty(
|
||||
default="Name",
|
||||
update=BaseNode.sync_tree,
|
||||
)
|
||||
|
||||
sort_index: IntProperty()
|
||||
|
||||
display_settings: BoolProperty(
|
||||
name="Display Settings",
|
||||
default=False,
|
||||
)
|
||||
|
||||
interface_type: EnumProperty(
|
||||
items=interface_type_items,
|
||||
default="DATA",
|
||||
update= BaseNode.sync_tree,
|
||||
)
|
||||
|
||||
data_type: StringProperty(
|
||||
default="Float",
|
||||
update=BaseNode.sync_tree,
|
||||
)
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
if self.interface_type == "DATA":
|
||||
builder.fixed_output("value", "Value", self.data_type)
|
||||
elif self.interface_type == "EXECUTE":
|
||||
builder.execute_output("execute", "Execute")
|
||||
elif self.interface_type == "INFLUENCES":
|
||||
builder.influences_output("influences", "Influences")
|
||||
else:
|
||||
assert False
|
||||
|
||||
def draw(self, layout):
|
||||
if not self.display_settings:
|
||||
return
|
||||
|
||||
layout.prop(self, "interface_type", text="")
|
||||
|
||||
if self.interface_type == "DATA":
|
||||
if hasattr(self.outputs[0], "draw_property"):
|
||||
self.outputs[0].draw_property(layout, self, "Default")
|
||||
|
||||
self.invoke_type_selection(layout, "set_data_type", "Select Type")
|
||||
|
||||
def draw_socket(self, layout, socket, text, decl, index_in_decl):
|
||||
row = layout.row(align=True)
|
||||
row.prop(self, "input_name", text="")
|
||||
row.prop(self, "display_settings", text="", icon="SETTINGS")
|
||||
|
||||
def draw_closed_label(self):
|
||||
return self.input_name + " (Input)"
|
||||
|
||||
def set_data_type(self, data_type):
|
||||
self.data_type = data_type
|
||||
|
||||
|
||||
class GroupOutputNode(bpy.types.Node, BaseNode):
|
||||
bl_idname = "fn_GroupOutputNode"
|
||||
bl_label = "Group Output"
|
||||
|
||||
sort_index: IntProperty()
|
||||
|
||||
display_settings: BoolProperty(
|
||||
name="Display Settings",
|
||||
default=False,
|
||||
)
|
||||
|
||||
output_name: StringProperty(
|
||||
default="Name",
|
||||
update=BaseNode.sync_tree,
|
||||
)
|
||||
|
||||
interface_type: EnumProperty(
|
||||
items=interface_type_items,
|
||||
default="DATA",
|
||||
update=BaseNode.sync_tree,
|
||||
)
|
||||
|
||||
data_type: StringProperty(
|
||||
default="Float",
|
||||
update=BaseNode.sync_tree,
|
||||
)
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
if self.interface_type == "DATA":
|
||||
builder.fixed_input("value", "Value", self.data_type)
|
||||
elif self.interface_type == "EXECUTE":
|
||||
builder.single_execute_input("execute", "Execute")
|
||||
elif self.interface_type == "INFLUENCES":
|
||||
builder.influences_input("influences", "Influences")
|
||||
|
||||
def draw(self, layout):
|
||||
if not self.display_settings:
|
||||
return
|
||||
|
||||
layout.prop(self, "interface_type", text="")
|
||||
|
||||
if self.interface_type == "DATA":
|
||||
self.invoke_type_selection(layout, "set_type_type", "Select Type")
|
||||
|
||||
def draw_socket(self, layout, socket, text, decl, index_in_decl):
|
||||
row = layout.row(align=True)
|
||||
row.prop(self, "output_name", text="")
|
||||
row.prop(self, "display_settings", text="", icon="SETTINGS")
|
||||
|
||||
def draw_closed_label(self):
|
||||
return self.output_name + " (Output)"
|
||||
|
||||
def set_type_type(self, data_type):
|
||||
self.data_type = data_type
|
||||
|
||||
class GroupNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_GroupNode"
|
||||
bl_label = "Group"
|
||||
bl_icon = "NODETREE"
|
||||
|
||||
node_group: PointerProperty(
|
||||
type=bpy.types.NodeTree,
|
||||
update=FunctionNode.sync_tree,
|
||||
)
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
if not isinstance(self.node_group, FunctionTree):
|
||||
return
|
||||
|
||||
for input_node in self.node_group.get_input_nodes():
|
||||
if input_node.interface_type == "DATA":
|
||||
builder.fixed_input(
|
||||
input_node.identifier,
|
||||
input_node.input_name,
|
||||
input_node.data_type,
|
||||
default=input_node.outputs[0].get_state())
|
||||
elif input_node.interface_type == "EXECUTE":
|
||||
builder.single_execute_input(input_node.identifier, input_node.input_name)
|
||||
elif input_node.interface_type == "INFLUENCES":
|
||||
builder.influences_input(input_node.identifier, input_node.input_name)
|
||||
else:
|
||||
assert False
|
||||
|
||||
for output_node in self.node_group.get_output_nodes():
|
||||
if output_node.interface_type == "DATA":
|
||||
builder.fixed_output(
|
||||
output_node.identifier,
|
||||
output_node.output_name,
|
||||
output_node.data_type)
|
||||
elif output_node.interface_type == "EXECUTE":
|
||||
builder.execute_output(output_node.identifier, output_node.output_name)
|
||||
elif output_node.interface_type == "INFLUENCES":
|
||||
builder.influences_output(output_node.identifier, output_node.output_name)
|
||||
else:
|
||||
assert False
|
||||
|
||||
def draw(self, layout):
|
||||
layout.scale_y = 1.3
|
||||
if self.node_group is None:
|
||||
self.invoke_group_selector(layout, "set_group", "Select Group", icon="NODETREE")
|
||||
elif not isinstance(self.node_group, FunctionTree):
|
||||
layout.label(text="Group not found!", icon="ERROR")
|
||||
self.invoke_group_selector(layout, "set_group", "Change Group", icon="NODETREE")
|
||||
|
||||
def draw_advanced(self, layout):
|
||||
col = layout.column()
|
||||
text = "Select Group" if self.node_group is None else self.node_group.name
|
||||
col.scale_y = 1.3
|
||||
self.invoke_group_selector(col, "set_group", text, icon="NODETREE")
|
||||
|
||||
def draw_label(self):
|
||||
if self.node_group is None:
|
||||
return "(G) -"
|
||||
else:
|
||||
return "(G) " + self.node_group.name
|
||||
|
||||
def set_group(self, group):
|
||||
self.node_group = group
|
||||
|
||||
def iter_directly_used_trees(self):
|
||||
if self.node_group is not None:
|
||||
yield self.node_group
|
||||
|
||||
|
||||
class GroupInterfacePanel(bpy.types.Panel, NodeSidebarPanel):
|
||||
bl_idname = "FN_PT_group_interface_panel"
|
||||
bl_label = "Group Interface"
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
try: return isinstance(context.space_data.edit_tree, FunctionTree)
|
||||
except: return False
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
tree = context.space_data.edit_tree
|
||||
draw_group_interface_panel(layout, tree)
|
||||
|
||||
|
||||
def draw_group_interface_panel(layout, tree):
|
||||
col = layout.column(align=True)
|
||||
col.label(text="Inputs:")
|
||||
box = col.box().column(align=True)
|
||||
for i, node in enumerate(tree.get_input_nodes()):
|
||||
row = box.row(align=True)
|
||||
row.prop(node, "input_name", text="")
|
||||
|
||||
props = row.operator("fn.move_group_interface", text="", icon="TRIA_UP")
|
||||
props.is_input = True
|
||||
props.from_index = i
|
||||
props.offset = -1
|
||||
|
||||
props = row.operator("fn.move_group_interface", text="", icon="TRIA_DOWN")
|
||||
props.is_input = True
|
||||
props.from_index = i
|
||||
props.offset = 1
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.label(text="Outputs:")
|
||||
box = col.box().column(align=True)
|
||||
for i, node in enumerate(tree.get_output_nodes()):
|
||||
row = box.row(align=True)
|
||||
row.prop(node, "output_name", text="")
|
||||
|
||||
props = row.operator("fn.move_group_interface", text="", icon="TRIA_UP")
|
||||
props.is_input = False
|
||||
props.from_index = i
|
||||
props.offset = -1
|
||||
|
||||
props = row.operator("fn.move_group_interface", text="", icon="TRIA_DOWN")
|
||||
props.is_input = False
|
||||
props.from_index = i
|
||||
props.offset = 1
|
||||
|
||||
|
||||
class MoveGroupInterface(bpy.types.Operator):
|
||||
bl_idname = "fn.move_group_interface"
|
||||
bl_label = "Move Group Interface"
|
||||
|
||||
is_input: BoolProperty()
|
||||
from_index: IntProperty()
|
||||
offset: IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
tree = context.space_data.node_tree
|
||||
|
||||
if self.is_input:
|
||||
nodes = tree.get_input_nodes()
|
||||
else:
|
||||
nodes = tree.get_output_nodes()
|
||||
|
||||
from_index = self.from_index
|
||||
to_index = min(max(self.from_index + self.offset, 0), len(nodes) - 1)
|
||||
|
||||
nodes[from_index], nodes[to_index] = nodes[to_index], nodes[from_index]
|
||||
|
||||
with skip_syncing():
|
||||
for i, node in enumerate(nodes):
|
||||
node.sort_index = i
|
||||
tree.sync()
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
def update_sort_indices(tree):
|
||||
for i, node in enumerate(tree.get_input_nodes()):
|
||||
node.sort_index = i
|
||||
for i, node in enumerate(tree.get_output_nodes()):
|
||||
node.sort_index = i
|
||||
|
||||
|
||||
class ManageGroupPieMenu(bpy.types.Menu, PieMenuHelper):
|
||||
bl_idname = "FN_MT_manage_group_pie"
|
||||
bl_label = "Manage Group"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
try:
|
||||
return isinstance(context.space_data.node_tree, FunctionTree)
|
||||
except:
|
||||
return False
|
||||
|
||||
def draw_top(self, layout):
|
||||
layout.operator("fn.open_group_management_popup", text="Group Management")
|
||||
|
||||
def draw_left(self, layout):
|
||||
node = bpy.context.active_node
|
||||
if node is None:
|
||||
self.empty(layout)
|
||||
return
|
||||
|
||||
possible_inputs = [(i, socket) for i, socket in enumerate(node.inputs)
|
||||
if socket_can_become_group_input(socket)]
|
||||
|
||||
if len(possible_inputs) == 0:
|
||||
self.empty(layout, "No inputs.")
|
||||
elif len(possible_inputs) == 1:
|
||||
props = layout.operator("fn.create_group_input_for_socket", text="New Group Input")
|
||||
props.input_index = possible_inputs[0][0]
|
||||
else:
|
||||
layout.operator("fn.create_group_input_for_socket_invoker", text="New Group Input")
|
||||
|
||||
def draw_right(self, layout):
|
||||
node = bpy.context.active_node
|
||||
if node is None:
|
||||
self.empty(layout)
|
||||
return
|
||||
|
||||
possible_outputs = [(i, socket) for i, socket in enumerate(node.outputs)
|
||||
if socket_can_become_group_output(socket)]
|
||||
|
||||
if len(possible_outputs) == 0:
|
||||
self.empty(layout, "No outputs.")
|
||||
elif len(possible_outputs) == 1:
|
||||
props = layout.operator("fn.create_group_output_for_socket", text="New Group Output")
|
||||
props.output_index = possible_outputs[0][0]
|
||||
else:
|
||||
layout.operator("fn.create_group_output_for_socket_invoker", text="New Group Output")
|
||||
|
||||
|
||||
class OpenGroupManagementPopup(bpy.types.Operator):
|
||||
bl_idname = "fn.open_group_management_popup"
|
||||
bl_label = "Group Management"
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def draw(self, context):
|
||||
draw_group_interface_panel(self.layout, context.space_data.node_tree)
|
||||
|
||||
def execute(self, context):
|
||||
return {"INTERFACE"}
|
||||
|
||||
|
||||
class CreateGroupInputForSocketInvoker(bpy.types.Operator):
|
||||
bl_idname = "fn.create_group_input_for_socket_invoker"
|
||||
bl_label = "Create Group Input for Socket Invoker"
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.popup_menu(self.draw_menu)
|
||||
return {"CANCELLED"}
|
||||
|
||||
@staticmethod
|
||||
def draw_menu(menu, context):
|
||||
node = bpy.context.active_node
|
||||
if node is None:
|
||||
return
|
||||
|
||||
layout = menu.layout.column()
|
||||
layout.operator_context = "INVOKE_DEFAULT"
|
||||
|
||||
for i, socket in enumerate(node.inputs):
|
||||
if socket_can_become_group_input(socket):
|
||||
props = layout.operator("fn.create_group_input_for_socket", text=socket.name)
|
||||
props.input_index = i
|
||||
|
||||
|
||||
class CreateGroupOutputForSocketInvoker(bpy.types.Operator):
|
||||
bl_idname = "fn.create_group_output_for_socket_invoker"
|
||||
bl_label = "Create Group Output for Socket Invoker"
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.popup_menu(self.draw_menu)
|
||||
return {"CANCELLED"}
|
||||
|
||||
@staticmethod
|
||||
def draw_menu(menu, context):
|
||||
node = bpy.context.active_node
|
||||
if node is None:
|
||||
return
|
||||
|
||||
layout = menu.layout.column()
|
||||
layout.operator_context = "INVOKE_DEFAULT"
|
||||
|
||||
for i, socket in enumerate(node.outputs):
|
||||
if socket_can_become_group_output(socket):
|
||||
props = layout.operator("fn.create_group_output_for_socket", text=socket.name)
|
||||
props.output_index = i
|
||||
|
||||
|
||||
class CreateGroupInputForSocket(bpy.types.Operator):
|
||||
bl_idname = "fn.create_group_input_for_socket"
|
||||
bl_label = "Create Group Input for Socket"
|
||||
|
||||
input_index: IntProperty()
|
||||
|
||||
def invoke(self, context, event):
|
||||
tree = context.space_data.node_tree
|
||||
node = context.active_node
|
||||
socket = node.inputs[self.input_index]
|
||||
|
||||
node.select = False
|
||||
|
||||
with skip_syncing():
|
||||
new_node = tree.nodes.new(type="fn_GroupInputNode")
|
||||
new_node.sort_index = 1000
|
||||
new_node.input_name = socket.name
|
||||
update_sort_indices(tree)
|
||||
|
||||
if isinstance(socket, DataSocket):
|
||||
new_node.interface_type = "DATA"
|
||||
new_node.data_type = socket.data_type
|
||||
elif socket.bl_idname == "fn_ExecuteSocket":
|
||||
new_node.interface_type = "EXECUTE"
|
||||
elif socket.bl_idname == "fn_InfluencesSocket":
|
||||
new_node.interface_type = "INFLUENCES"
|
||||
new_node.rebuild()
|
||||
|
||||
new_node.select = True
|
||||
new_node.parent = node.parent
|
||||
new_node.location = node.location
|
||||
new_node.location.x -= 200
|
||||
|
||||
new_node.outputs[0].restore_state(socket.get_state())
|
||||
tree.new_link(new_node.outputs[0], socket)
|
||||
|
||||
tree.sync()
|
||||
bpy.ops.node.translate_attach("INVOKE_DEFAULT")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class CreateGroupOutputForSocket(bpy.types.Operator):
|
||||
bl_idname = "fn.create_group_output_for_socket"
|
||||
bl_label = "Create Group Output for Socket"
|
||||
|
||||
output_index: IntProperty()
|
||||
|
||||
def invoke(self, context, event):
|
||||
tree = context.space_data.node_tree
|
||||
node = context.active_node
|
||||
socket = node.outputs[self.output_index]
|
||||
|
||||
node.select = False
|
||||
|
||||
with skip_syncing():
|
||||
new_node = tree.nodes.new(type="fn_GroupOutputNode")
|
||||
new_node.sort_index = 1000
|
||||
update_sort_indices(tree)
|
||||
|
||||
new_node.output_name = socket.name
|
||||
if isinstance(socket, DataSocket):
|
||||
new_node.interface_type = "DATA"
|
||||
new_node.data_type = socket.data_type
|
||||
elif socket.bl_idname == "fn_ExecuteSocket":
|
||||
new_node.interface_type = "EXECUTE"
|
||||
elif socket.bl_idname == "fn_InfluencesSocket":
|
||||
new_node.interface_type = "INFLUENCES"
|
||||
new_node.rebuild()
|
||||
|
||||
new_node.select = True
|
||||
new_node.parent = node.parent
|
||||
new_node.location = node.location
|
||||
new_node.location.x += 200
|
||||
|
||||
tree.new_link(new_node.inputs[0], socket)
|
||||
|
||||
tree.sync()
|
||||
bpy.ops.node.translate_attach("INVOKE_DEFAULT")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class OpenCloseGroupOperator(bpy.types.Operator):
|
||||
bl_idname = "fn.open_close_group"
|
||||
bl_label = "Open/Close Group"
|
||||
bl_options = {"INTERNAL"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
try: return context.space_data.node_tree.bl_idname == "FunctionTree"
|
||||
except: return False
|
||||
|
||||
def invoke(self, context, event):
|
||||
space_data = context.space_data
|
||||
active_node = context.active_node
|
||||
if isinstance(active_node, GroupNode) and active_node.node_group is not None and active_node.select:
|
||||
space_data.path.append(active_node.node_group, node=active_node)
|
||||
else:
|
||||
space_data.path.pop()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
def socket_can_become_group_input(socket):
|
||||
return socket.bl_idname != "fn_OperatorSocket" and not socket.is_linked
|
||||
|
||||
def socket_can_become_group_output(socket):
|
||||
return socket.bl_idname != "fn_OperatorSocket"
|
||||
|
||||
keymap = None
|
||||
|
||||
def register():
|
||||
global keymap
|
||||
|
||||
if not bpy.app.background:
|
||||
keymap = bpy.context.window_manager.keyconfigs.addon.keymaps.new(
|
||||
name="Node Editor", space_type="NODE_EDITOR")
|
||||
|
||||
kmi = keymap.keymap_items.new("wm.call_menu_pie", type="V", value="PRESS")
|
||||
kmi.properties.name = "FN_MT_manage_group_pie"
|
||||
|
||||
keymap.keymap_items.new("fn.open_close_group", type="TAB", value="PRESS")
|
||||
|
||||
def unregister():
|
||||
global keymap
|
||||
|
||||
if not bpy.app.background:
|
||||
bpy.context.window_manager.keyconfigs.addon.keymaps.remove(keymap)
|
||||
keymap = None
|
||||
69
release/scripts/startup/nodes/function_nodes/list.py
Normal file
69
release/scripts/startup/nodes/function_nodes/list.py
Normal file
@@ -0,0 +1,69 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from .. types import type_infos
|
||||
from .. base import FunctionNode
|
||||
from .. node_builder import NodeBuilder
|
||||
|
||||
|
||||
class GetListElementNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_GetListElementNode"
|
||||
bl_label = "Get List Element"
|
||||
|
||||
active_type: NodeBuilder.DynamicListProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.dynamic_list_input("list", "List", "active_type")
|
||||
builder.fixed_input("index", "Index", "Integer")
|
||||
builder.dynamic_base_input("fallback", "Fallback", "active_type")
|
||||
builder.dynamic_base_output("value", "Value", "active_type")
|
||||
|
||||
|
||||
class GetListElementsNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_GetListElementsNode"
|
||||
bl_label = "Get List Elements"
|
||||
|
||||
active_type: NodeBuilder.DynamicListProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.dynamic_list_input("list", "List", "active_type")
|
||||
builder.fixed_input("indices", "Indices", "Integer List")
|
||||
builder.dynamic_base_input("fallback", "Fallback", "active_type")
|
||||
builder.dynamic_list_output("values", "Values", "active_type")
|
||||
|
||||
|
||||
class ListLengthNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_ListLengthNode"
|
||||
bl_label = "List Length"
|
||||
|
||||
active_type: NodeBuilder.DynamicListProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.dynamic_list_input("list", "List", "active_type")
|
||||
builder.fixed_output("length", "Length", "Integer")
|
||||
|
||||
|
||||
class PackListNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_PackListNode"
|
||||
bl_label = "Pack List"
|
||||
|
||||
active_type: StringProperty(
|
||||
default="Float",
|
||||
update=FunctionNode.sync_tree)
|
||||
|
||||
variadic: NodeBuilder.BaseListVariadicProperty()
|
||||
|
||||
def declaration(self, builder):
|
||||
builder.base_list_variadic_input("inputs", "variadic", self.active_type)
|
||||
builder.fixed_output("output", "List", type_infos.to_list(self.active_type))
|
||||
|
||||
def draw_advanced(self, layout):
|
||||
self.invoke_type_selection(layout, "set_type", "Change Type", mode="BASE")
|
||||
|
||||
def set_type(self, data_type):
|
||||
self.active_type = data_type
|
||||
|
||||
@classmethod
|
||||
def get_search_terms(cls):
|
||||
for list_type in type_infos.iter_list_types():
|
||||
base_type = type_infos.to_base(list_type)
|
||||
yield ("Pack " + list_type, {"active_type" : base_type})
|
||||
129
release/scripts/startup/nodes/function_nodes/math.py
Normal file
129
release/scripts/startup/nodes/function_nodes/math.py
Normal file
@@ -0,0 +1,129 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from .. base import FunctionNode
|
||||
from .. node_builder import NodeBuilder
|
||||
|
||||
def create_variadic_math_node(data_type, idname, label):
|
||||
class MathNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = idname
|
||||
bl_label = label
|
||||
|
||||
variadic: NodeBuilder.BaseListVariadicProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.base_list_variadic_input("inputs", "variadic", data_type)
|
||||
|
||||
if NodeBuilder.BaseListVariadicPropertyHasList(self.variadic):
|
||||
builder.fixed_output("result", "Result", data_type + " List")
|
||||
else:
|
||||
builder.fixed_output("result", "Result", data_type)
|
||||
|
||||
return MathNode
|
||||
|
||||
def create_single_type_two_inputs_math_node(data_type, idname, label):
|
||||
return create_two_inputs_math_node(data_type, data_type, data_type, idname, label)
|
||||
|
||||
def create_two_inputs_math_node(input_type1, input_type2, output_type, idname, label):
|
||||
class MathNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = idname
|
||||
bl_label = label
|
||||
|
||||
use_list__a: NodeBuilder.VectorizedProperty()
|
||||
use_list__b: NodeBuilder.VectorizedProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.vectorized_input("a", "use_list__a", "A", "A", input_type1)
|
||||
builder.vectorized_input("b", "use_list__b", "B", "B", input_type2)
|
||||
builder.vectorized_output("result", ["use_list__a", "use_list__b"], "Result", "Result", output_type)
|
||||
|
||||
return MathNode
|
||||
|
||||
def create_single_input_math_node(input_type, output_type, idname, label):
|
||||
|
||||
class MathNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = idname
|
||||
bl_label = label
|
||||
|
||||
use_list: NodeBuilder.VectorizedProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.vectorized_input("input", "use_list", "Value", "Values", input_type)
|
||||
builder.vectorized_output("output", ["use_list"], "Result", "Result", output_type)
|
||||
|
||||
return MathNode
|
||||
|
||||
AddFloatsNode = create_variadic_math_node("Float", "fn_AddFloatsNode", "Add Floats")
|
||||
MultiplyFloatsNode = create_variadic_math_node("Float", "fn_MultiplyFloatsNode", "Multiply Floats")
|
||||
MinimumFloatsNode = create_variadic_math_node("Float", "fn_MinimumFloatsNode", "Minimum Floats")
|
||||
MaximumFloatsNode = create_variadic_math_node("Float", "fn_MaximumFloatsNode", "Maximum Floats")
|
||||
|
||||
SubtractFloatsNode = create_single_type_two_inputs_math_node("Float", "fn_SubtractFloatsNode", "Subtract Floats")
|
||||
DivideFloatsNode = create_single_type_two_inputs_math_node("Float", "fn_DivideFloatsNode", "Divide Floats")
|
||||
PowerFloatsNode = create_single_type_two_inputs_math_node("Float", "fn_PowerFloatsNode", "Power Floats")
|
||||
|
||||
SqrtFloatNode = create_single_input_math_node("Float", "Float", "fn_SqrtFloatNode", "Sqrt Float")
|
||||
AbsFloatNode = create_single_input_math_node("Float", "Float", "fn_AbsoluteFloatNode", "Absolute Float")
|
||||
SineFloatNode = create_single_input_math_node("Float", "Float", "fn_SineFloatNode", "Sine")
|
||||
CosineFloatNode = create_single_input_math_node("Float", "Float", "fn_CosineFloatNode", "Cosine")
|
||||
|
||||
CeilFloatNode = create_single_input_math_node("Float", "Float", "fn_CeilFloatNode", "Ceil Float")
|
||||
FloorFloatNode = create_single_input_math_node("Float", "Float", "fn_FloorFloatNode", "Floor Float")
|
||||
|
||||
AddVectorsNode = create_variadic_math_node("Vector", "fn_AddVectorsNode", "Add Vectors")
|
||||
SubtractVectorsNode = create_single_type_two_inputs_math_node("Vector", "fn_SubtractVectorsNode", "Subtract Vectors")
|
||||
MultiplyVectorsNode = create_variadic_math_node("Vector", "fn_MultiplyVectorsNode", "Multiply Vectors")
|
||||
DivideVectorsNode = create_single_type_two_inputs_math_node("Vector", "fn_DivideVectorsNode", "Divide Vectors")
|
||||
MultiplyVectorWithFloatNode = create_two_inputs_math_node("Vector", "Float", "Vector", "fn_MultiplyVectorWithFloatNode", "Multiply Vector with Float")
|
||||
|
||||
VectorCrossProductNode = create_single_type_two_inputs_math_node("Vector", "fn_VectorCrossProductNode", "Cross Product")
|
||||
VectorReflectNode = create_single_type_two_inputs_math_node("Vector", "fn_ReflectVectorNode", "Reflect Vector")
|
||||
VectorProjectNode = create_single_type_two_inputs_math_node("Vector", "fn_ProjectVectorNode", "Project Vector")
|
||||
VectorDotProductNode = create_two_inputs_math_node("Vector", "Vector", "Float", "fn_VectorDotProductNode", "Dot Product")
|
||||
VectorDistanceNode = create_two_inputs_math_node("Vector", "Vector", "Float", "fn_VectorDistanceNode", "Vector Distance")
|
||||
NormalizeVectorNode = create_single_input_math_node("Vector", "Vector", "fn_NormalizeVectorNode", "Normalize Vector")
|
||||
VectorLengthNode = create_single_input_math_node("Vector", "Float", "fn_VectorLengthNode", "Vector Length")
|
||||
|
||||
BooleanAndNode = create_variadic_math_node("Boolean", "fn_BooleanAndNode", "And")
|
||||
BooleanOrNode = create_variadic_math_node("Boolean", "fn_BooleanOrNode", "Or")
|
||||
BooleanNotNode = create_single_input_math_node("Boolean", "Boolean", "fn_BooleanNotNode", "Not")
|
||||
|
||||
LessThanFloatNode = create_two_inputs_math_node("Float", "Float", "Boolean", "fn_LessThanFloatNode", "Less Than Float")
|
||||
GreaterThanFloatNode = create_two_inputs_math_node("Float", "Float", "Boolean", "fn_GreaterThanFloatNode", "Greater Than Float")
|
||||
|
||||
class MapRangeNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_MapRangeNode"
|
||||
bl_label = "Map Range"
|
||||
|
||||
clamp: BoolProperty(
|
||||
name="Clamp",
|
||||
default=True,
|
||||
)
|
||||
|
||||
use_list__value: NodeBuilder.VectorizedProperty()
|
||||
use_list__from_min: NodeBuilder.VectorizedProperty()
|
||||
use_list__from_max: NodeBuilder.VectorizedProperty()
|
||||
use_list__to_min: NodeBuilder.VectorizedProperty()
|
||||
use_list__to_max: NodeBuilder.VectorizedProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.vectorized_input("value", "use_list__value", "Value", "Values", "Float")
|
||||
builder.vectorized_input("from_min", "use_list__from_min", "From Min", "From Min", "Float")
|
||||
builder.vectorized_input("from_max", "use_list__from_max", "From Max", "From Max", "Float")
|
||||
builder.vectorized_input("to_min", "use_list__to_min", "To Min", "To Min", "Float")
|
||||
builder.vectorized_input("to_max", "use_list__to_max", "To Max", "To Max", "Float")
|
||||
builder.vectorized_output("value", [
|
||||
"use_list__value", "use_list__from_min", "use_list__from_max",
|
||||
"use_list__to_min", "use_list__to_max"], "Value", "Values", "Float")
|
||||
|
||||
def draw(self, layout):
|
||||
layout.prop(self, "clamp")
|
||||
|
||||
class FloatClampNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_FloatClampNode"
|
||||
bl_label = "Clamp"
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("value", "Value", "Float")
|
||||
builder.fixed_input("min", "Min", "Float", default=0)
|
||||
builder.fixed_input("max", "Max", "Float", default=1)
|
||||
builder.fixed_output("value", "Value", "Float")
|
||||
@@ -0,0 +1,12 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from .. base import FunctionNode
|
||||
from .. node_builder import NodeBuilder
|
||||
|
||||
|
||||
class NodeInstanceIdentifierNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_NodeInstanceIdentifierNode"
|
||||
bl_label = "Node Instance Identifier"
|
||||
|
||||
def declaration(self, builder):
|
||||
builder.fixed_output("identifier", "Identifier", "Text")
|
||||
143
release/scripts/startup/nodes/function_nodes/noise.py
Normal file
143
release/scripts/startup/nodes/function_nodes/noise.py
Normal file
@@ -0,0 +1,143 @@
|
||||
import bpy
|
||||
import random
|
||||
from bpy.props import *
|
||||
from .. base import FunctionNode
|
||||
from .. node_builder import NodeBuilder
|
||||
|
||||
|
||||
class PerlinNoiseNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_PerlinNoiseNode"
|
||||
bl_label = "Perlin Noise"
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("position", "Position", "Vector")
|
||||
builder.fixed_input("amplitude", "Amplitude", "Float", default=1)
|
||||
builder.fixed_input("scale", "Scale", "Float", default=1)
|
||||
builder.fixed_output("noise_1d", "Noise", "Float")
|
||||
builder.fixed_output("noise_3d", "Noise", "Vector")
|
||||
|
||||
|
||||
class RandomFloatNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_RandomFloatNode"
|
||||
bl_label = "Random Float"
|
||||
|
||||
node_seed: IntProperty(
|
||||
name="Node Seed",
|
||||
)
|
||||
|
||||
def init_props(self):
|
||||
self.node_seed = new_node_seed()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("min", "Min", "Float", default=0)
|
||||
builder.fixed_input("max", "Max", "Float", default=1)
|
||||
builder.fixed_input("seed", "Seed", "Integer")
|
||||
builder.fixed_output("value", "Value", "Float")
|
||||
|
||||
def draw_advanced(self, layout):
|
||||
layout.prop(self, "node_seed")
|
||||
|
||||
def duplicate(self, src_node):
|
||||
self.node_seed = new_node_seed()
|
||||
|
||||
|
||||
class RandomFloatsNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_RandomFloatsNode"
|
||||
bl_label = "Random Floats"
|
||||
|
||||
node_seed: IntProperty(
|
||||
name="Node Seed",
|
||||
)
|
||||
|
||||
def init_props(self):
|
||||
self.node_seed = new_node_seed()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("amount", "Amount", "Integer", default=10)
|
||||
builder.fixed_input("min", "Min", "Float")
|
||||
builder.fixed_input("max", "Max", "Float", default=1)
|
||||
builder.fixed_input("seed", "Seed", "Integer")
|
||||
builder.fixed_output("values", "Values", "Float List")
|
||||
|
||||
def draw_advanced(self, layout):
|
||||
layout.prop(self, "node_seed")
|
||||
|
||||
def duplicate(self, src_node):
|
||||
self.node_seed = new_node_seed()
|
||||
|
||||
random_vector_mode_items = [
|
||||
("UNIFORM_IN_CUBE", "Uniform in Cube", "Generate a vector that is somewhere in the volume of a cube", "NONE", 0),
|
||||
("UNIFORM_ON_SPHERE", "Uniform on Sphere", "Generate a vector that is somewhere on the surface of a sphere", 1),
|
||||
("UNIFORM_IN_SPHERE", "Uniform in Sphere", "Generate a vector that is somewhere in the volume of a sphere", 2),
|
||||
]
|
||||
|
||||
class RandomVectorNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_RandomVectorNode"
|
||||
bl_label = "Random Vector"
|
||||
|
||||
node_seed: IntProperty(
|
||||
name="Node Seed",
|
||||
)
|
||||
|
||||
mode: EnumProperty(
|
||||
name="Mode",
|
||||
items=random_vector_mode_items,
|
||||
default="UNIFORM_IN_CUBE",
|
||||
)
|
||||
|
||||
use_list__factor: NodeBuilder.VectorizedProperty()
|
||||
use_list__seed: NodeBuilder.VectorizedProperty()
|
||||
|
||||
def init_props(self):
|
||||
self.node_seed = new_node_seed()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.vectorized_input("factor", "use_list__factor", "Factor", "Factors", "Vector", default=(1, 1, 1))
|
||||
builder.vectorized_input("seed", "use_list__seed", "Seed", "Seeds", "Integer")
|
||||
builder.vectorized_output("vector", ["use_list__factor", "use_list__seed"], "Vector", "Vectors", "Vector")
|
||||
|
||||
def draw(self, layout):
|
||||
layout.prop(self, "mode", text="")
|
||||
|
||||
def draw_advanced(self, layout):
|
||||
layout.prop(self, "node_seed")
|
||||
|
||||
def duplicate(self, src_node):
|
||||
self.node_seed = new_node_seed()
|
||||
|
||||
|
||||
class RandomVectorsNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_RandomVectorsNode"
|
||||
bl_label = "Random Vectors"
|
||||
|
||||
node_seed: IntProperty(
|
||||
name="Node Seed",
|
||||
)
|
||||
|
||||
mode: EnumProperty(
|
||||
name="Mode",
|
||||
items=random_vector_mode_items,
|
||||
default="UNIFORM_IN_CUBE",
|
||||
)
|
||||
|
||||
def init_props(self):
|
||||
self.node_seed = new_node_seed()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("amount", "Amount", "Integer", default=10)
|
||||
builder.fixed_input("factor", "Factor", "Vector", default=(1, 1, 1))
|
||||
builder.fixed_input("seed", "Seed", "Integer")
|
||||
builder.fixed_output("vectors", "Vectors", "Vector List")
|
||||
|
||||
def draw(self, layout):
|
||||
layout.prop(self, "mode", text="")
|
||||
|
||||
def draw_advanced(self, layout):
|
||||
layout.prop(self, "node_seed")
|
||||
|
||||
def duplicate(self, src_node):
|
||||
self.node_seed = new_node_seed()
|
||||
|
||||
|
||||
def new_node_seed():
|
||||
return random.randint(0, 10000)
|
||||
113
release/scripts/startup/nodes/function_nodes/object_mesh.py
Normal file
113
release/scripts/startup/nodes/function_nodes/object_mesh.py
Normal file
@@ -0,0 +1,113 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from .. base import FunctionNode
|
||||
from .. node_builder import NodeBuilder
|
||||
|
||||
class ObjectMeshNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_ObjectMeshNode"
|
||||
bl_label = "Object Mesh"
|
||||
|
||||
def declaration(self, builder):
|
||||
builder.fixed_input("object", "Object", "Object", display_name=False)
|
||||
builder.fixed_output("vertex_locations", "Vertex Locations", "Vector List")
|
||||
|
||||
|
||||
class VertexInfo(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_VertexInfoNode"
|
||||
bl_label = "Vertex Info"
|
||||
|
||||
def declaration(self, builder):
|
||||
builder.fixed_output("position", "Position", "Vector")
|
||||
|
||||
|
||||
class ClosestLocationOnObjectNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_ClosestLocationOnObjectNode"
|
||||
bl_label = "Closest Location on Object"
|
||||
|
||||
use_list__object: NodeBuilder.VectorizedProperty()
|
||||
use_list__position: NodeBuilder.VectorizedProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.vectorized_input("object", "use_list__object", "Object", "Objects", "Object", display_name=False)
|
||||
builder.vectorized_input("position", "use_list__position", "Position", "Positions", "Vector")
|
||||
|
||||
vectorize_props = ["use_list__object", "use_list__position"]
|
||||
builder.vectorized_output("closest_hook", vectorize_props, "Closest Hook", "Closest Hooks", "Surface Hook")
|
||||
builder.vectorized_output("closest_position", vectorize_props, "Closest Position", "Closest Positions", "Vector")
|
||||
builder.vectorized_output("closest_normal", vectorize_props, "Closest Normal", "Closest Normals", "Vector")
|
||||
|
||||
|
||||
class GetPositionOnSurfaceNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_GetPositionOnSurfaceNode"
|
||||
bl_label = "Get Position on Surface"
|
||||
|
||||
use_list__surface_hook: NodeBuilder.VectorizedProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.vectorized_input("surface_hook", "use_list__surface_hook", "Surface Hook", "Surface Hooks", "Surface Hook")
|
||||
builder.vectorized_output("position", ["use_list__surface_hook"], "Position", "Positions", "Vector")
|
||||
|
||||
|
||||
class GetNormalOnSurfaceNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_GetNormalOnSurfaceNode"
|
||||
bl_label = "Get Normal on Surface"
|
||||
|
||||
use_list__surface_hook: NodeBuilder.VectorizedProperty()
|
||||
|
||||
def declaration(self, builder):
|
||||
builder.vectorized_input("surface_hook", "use_list__surface_hook", "Surface Hook", "Surface Hooks", "Surface Hook")
|
||||
builder.vectorized_output("normal", ["use_list__surface_hook"], "Normal", "Normals", "Vector")
|
||||
|
||||
|
||||
class GetWeightOnSurfaceNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_GetWeightOnSurfaceNode"
|
||||
bl_label = "Get Weight on Surface"
|
||||
|
||||
use_list__surface_hook: NodeBuilder.VectorizedProperty()
|
||||
use_list__vertex_group_name: NodeBuilder.VectorizedProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.vectorized_input("surface_hook", "use_list__surface_hook", "Surface Hook", "Surface Hooks", "Surface Hook")
|
||||
builder.vectorized_input("vertex_group_name", "use_list__vertex_group_name", "Name", "Names", "Text",
|
||||
default="Group", display_name=False, display_icon="GROUP_VERTEX")
|
||||
builder.vectorized_output("weight", ["use_list__surface_hook", "use_list__vertex_group_name"], "Weight", "Weights", "Float")
|
||||
|
||||
|
||||
class GetImageColorOnSurfaceNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_GetImageColorOnSurfaceNode"
|
||||
bl_label = "Get Image Color on Surface"
|
||||
|
||||
use_list__surface_hook: NodeBuilder.VectorizedProperty()
|
||||
use_list__image: NodeBuilder.VectorizedProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.vectorized_input("surface_hook", "use_list__surface_hook", "Surface Hook", "Surface Hooks", "Surface Hook")
|
||||
builder.vectorized_input("image", "use_list__image", "Image", "Images", "Image", display_name=False)
|
||||
builder.vectorized_output("color", ["use_list__surface_hook", "use_list__image"], "Color", "Colors", "Color")
|
||||
|
||||
|
||||
class SampleObjectSurfaceNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_SampleObjectSurfaceNode"
|
||||
bl_label = "Sample Object Surface"
|
||||
|
||||
weight_mode: EnumProperty(
|
||||
name="Weight Mode",
|
||||
items=[
|
||||
("UNIFORM", "Uniform", "", "NONE", 0),
|
||||
("VERTEX_WEIGHTS", "Vertex Weights", "", "NONE", 1),
|
||||
],
|
||||
default="UNIFORM",
|
||||
update=FunctionNode.sync_tree,
|
||||
)
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("object", "Object", "Object", display_name=False)
|
||||
builder.fixed_input("amount", "Amount", "Integer", default=10)
|
||||
builder.fixed_input("seed", "Seed", "Integer")
|
||||
if self.weight_mode == "VERTEX_WEIGHTS":
|
||||
builder.fixed_input("vertex_group_name", "Vertex Group", "Text", default="Group")
|
||||
|
||||
builder.fixed_output("surface_hooks", "Surface Hooks", "Surface Hook List")
|
||||
|
||||
def draw(self, layout):
|
||||
layout.prop(self, "weight_mode", text="")
|
||||
@@ -0,0 +1,11 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from .. base import FunctionNode
|
||||
|
||||
class ObjectTransformsNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_ObjectTransformsNode"
|
||||
bl_label = "Object Transforms"
|
||||
|
||||
def declaration(self, builder):
|
||||
builder.fixed_input("object", "Object", "Object")
|
||||
builder.fixed_output("location", "Location", "Vector")
|
||||
79
release/scripts/startup/nodes/function_nodes/switch.py
Normal file
79
release/scripts/startup/nodes/function_nodes/switch.py
Normal file
@@ -0,0 +1,79 @@
|
||||
import bpy
|
||||
import uuid
|
||||
from bpy.props import *
|
||||
from .. base import FunctionNode
|
||||
from .. node_builder import NodeBuilder
|
||||
|
||||
class SwitchNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_SwitchNode"
|
||||
bl_label = "Switch"
|
||||
|
||||
data_type: StringProperty(
|
||||
default="Float",
|
||||
update=FunctionNode.sync_tree
|
||||
)
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("condition", "Condition", "Boolean")
|
||||
builder.fixed_input("true", "True", self.data_type)
|
||||
builder.fixed_input("false", "False", self.data_type)
|
||||
builder.fixed_output("result", "Result", self.data_type)
|
||||
|
||||
def draw(self, layout):
|
||||
self.invoke_type_selection(layout, "set_type", "Change Type")
|
||||
|
||||
def set_type(self, data_type):
|
||||
self.data_type = data_type
|
||||
|
||||
|
||||
class SelectNodeItem(bpy.types.PropertyGroup):
|
||||
identifier: StringProperty()
|
||||
|
||||
class SelectNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_SelectNode"
|
||||
bl_label = "Select"
|
||||
|
||||
data_type: StringProperty(
|
||||
default="Float",
|
||||
update=FunctionNode.sync_tree,
|
||||
)
|
||||
|
||||
input_items: CollectionProperty(
|
||||
type=SelectNodeItem,
|
||||
)
|
||||
|
||||
def init_props(self):
|
||||
self.add_input()
|
||||
self.add_input()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("select", "Select", "Integer")
|
||||
for i, item in enumerate(self.input_items):
|
||||
builder.fixed_input(item.identifier, str(i), self.data_type)
|
||||
builder.fixed_input("fallback", "Fallback", self.data_type)
|
||||
builder.fixed_output("result", "Result", self.data_type)
|
||||
|
||||
def draw(self, layout):
|
||||
self.invoke_type_selection(layout, "set_type", "Change Type")
|
||||
self.invoke_function(layout, "add_input", "Add Input")
|
||||
|
||||
def draw_socket(self, layout, socket, text, decl, index_in_decl):
|
||||
if len(socket.name) <= 3:
|
||||
index = int(socket.name)
|
||||
row = layout.row(align=True)
|
||||
decl.draw_socket(row, socket, index_in_decl)
|
||||
self.invoke_function(row, "remove_input", "", icon="X", settings=(index, ))
|
||||
else:
|
||||
decl.draw_socket(layout, socket, index_in_decl)
|
||||
|
||||
def set_type(self, data_type):
|
||||
self.data_type = data_type
|
||||
|
||||
def add_input(self):
|
||||
item = self.input_items.add()
|
||||
item.identifier = str(uuid.uuid4())
|
||||
self.sync_tree()
|
||||
|
||||
def remove_input(self, index):
|
||||
self.input_items.remove(index)
|
||||
self.sync_tree()
|
||||
20
release/scripts/startup/nodes/function_nodes/text.py
Normal file
20
release/scripts/startup/nodes/function_nodes/text.py
Normal file
@@ -0,0 +1,20 @@
|
||||
import bpy
|
||||
from .. node_builder import NodeBuilder
|
||||
from .. base import FunctionNode
|
||||
|
||||
class TextLengthNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_TextLengthNode"
|
||||
bl_label = "Text Length"
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("text", "Text", "Text")
|
||||
builder.fixed_output("length", "Length", "Integer")
|
||||
|
||||
|
||||
class JoinTextListNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_JoinTextListNode"
|
||||
bl_label = "Join Text List"
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("texts", "Texts", "Text List")
|
||||
builder.fixed_output("text", "Text", "Text")
|
||||
10
release/scripts/startup/nodes/function_nodes/time.py
Normal file
10
release/scripts/startup/nodes/function_nodes/time.py
Normal file
@@ -0,0 +1,10 @@
|
||||
import bpy
|
||||
from .. node_builder import NodeBuilder
|
||||
from .. base import FunctionNode
|
||||
|
||||
class TimeInfoNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_TimeInfoNode"
|
||||
bl_label = "Time Info"
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_output("frame", "Frame", "Float")
|
||||
29
release/scripts/startup/nodes/function_nodes/value.py
Normal file
29
release/scripts/startup/nodes/function_nodes/value.py
Normal file
@@ -0,0 +1,29 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from .. base import FunctionNode
|
||||
from .. node_builder import NodeBuilder
|
||||
|
||||
|
||||
class ValueNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_ValueNode"
|
||||
bl_label = "Value"
|
||||
|
||||
data_type: StringProperty(
|
||||
name="Data Type",
|
||||
default="Float",
|
||||
update=FunctionNode.sync_tree,
|
||||
)
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_output("value", "Value", self.data_type)
|
||||
|
||||
def draw_socket(self, layout, socket, text, decl, index_in_decl):
|
||||
row = layout.row(align=True)
|
||||
if hasattr(socket, "draw_property"):
|
||||
socket.draw_property(row, self, "")
|
||||
else:
|
||||
row.label(text=text)
|
||||
self.invoke_type_selection(row, "set_type", text="", icon="SETTINGS")
|
||||
|
||||
def set_type(self, data_type):
|
||||
self.data_type = data_type
|
||||
71
release/scripts/startup/nodes/function_nodes/vector.py
Normal file
71
release/scripts/startup/nodes/function_nodes/vector.py
Normal file
@@ -0,0 +1,71 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from .. base import FunctionNode
|
||||
from .. node_builder import NodeBuilder
|
||||
|
||||
|
||||
class VectorFromValueNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_VectorFromValueNode"
|
||||
bl_label = "Vector from Value"
|
||||
|
||||
use_list__value: NodeBuilder.VectorizedProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.vectorized_input("value", "use_list__value", "Value", "Values", "Float")
|
||||
builder.vectorized_output("vector", ["use_list__value"], "Vector", "Vectors", "Vector")
|
||||
|
||||
|
||||
class CombineVectorNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_CombineVectorNode"
|
||||
bl_label = "Combine Vector"
|
||||
|
||||
use_list__x: NodeBuilder.VectorizedProperty()
|
||||
use_list__y: NodeBuilder.VectorizedProperty()
|
||||
use_list__z: NodeBuilder.VectorizedProperty()
|
||||
|
||||
def declaration(self, builder):
|
||||
builder.vectorized_input(
|
||||
"x", "use_list__x",
|
||||
"X", "X", "Float")
|
||||
builder.vectorized_input(
|
||||
"y", "use_list__y",
|
||||
"Y", "Y", "Float")
|
||||
builder.vectorized_input(
|
||||
"z", "use_list__z",
|
||||
"Z", "Z", "Float")
|
||||
|
||||
builder.vectorized_output(
|
||||
"vector", ["use_list__x", "use_list__y", "use_list__z"],
|
||||
"Vector", "Vectors", "Vector")
|
||||
|
||||
|
||||
class SeparateVectorNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_SeparateVectorNode"
|
||||
bl_label = "Separate Vector"
|
||||
|
||||
use_list__vector: NodeBuilder.VectorizedProperty()
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.vectorized_input(
|
||||
"vector", "use_list__vector",
|
||||
"Vector", "Vectors", "Vector")
|
||||
|
||||
builder.vectorized_output(
|
||||
"x", ["use_list__vector"],
|
||||
"X", "X", "Float")
|
||||
builder.vectorized_output(
|
||||
"y", ["use_list__vector"],
|
||||
"Y", "Y", "Float")
|
||||
builder.vectorized_output(
|
||||
"z", ["use_list__vector"],
|
||||
"Z", "Z", "Float")
|
||||
|
||||
|
||||
class FindNonClosePointsNode(bpy.types.Node, FunctionNode):
|
||||
bl_idname = "fn_FindNonClosePointsNode"
|
||||
bl_label = "Find Non Close Point"
|
||||
|
||||
def declaration(self, builder: NodeBuilder):
|
||||
builder.fixed_input("points", "Points", "Vector List")
|
||||
builder.fixed_input("min_distance", "Min Distance", "Float", default=0.1)
|
||||
builder.fixed_output("indices", "Indices", "Integer List")
|
||||
57
release/scripts/startup/nodes/function_tree.py
Normal file
57
release/scripts/startup/nodes/function_tree.py
Normal file
@@ -0,0 +1,57 @@
|
||||
import bpy
|
||||
from collections import namedtuple
|
||||
|
||||
from . base import BaseTree, BaseNode
|
||||
from . graph import DirectedGraphBuilder, DirectedGraph
|
||||
|
||||
class FunctionTree(bpy.types.NodeTree, BaseTree):
|
||||
bl_idname = "FunctionTree"
|
||||
bl_icon = "MOD_DATA_TRANSFER"
|
||||
bl_label = "Function Nodes"
|
||||
|
||||
def get_input_nodes(self):
|
||||
input_nodes = [node for node in self.nodes if node.bl_idname == "fn_GroupInputNode"]
|
||||
sorted_input_nodes = sorted(input_nodes, key=lambda node: (node.sort_index, node.name))
|
||||
return sorted_input_nodes
|
||||
|
||||
def get_output_nodes(self):
|
||||
output_nodes = [node for node in self.nodes if node.bl_idname == "fn_GroupOutputNode"]
|
||||
sorted_output_nodes = sorted(output_nodes, key=lambda node: (node.sort_index, node.name))
|
||||
return sorted_output_nodes
|
||||
|
||||
def get_directly_used_trees(self):
|
||||
trees = set()
|
||||
for node in self.nodes:
|
||||
if isinstance(node, BaseNode):
|
||||
trees.update(node.iter_directly_used_trees())
|
||||
return trees
|
||||
|
||||
def find_callable_trees(self):
|
||||
used_by_trees = FunctionTree.BuildInvertedCallGraph().reachable(self)
|
||||
trees = [tree for tree in bpy.data.node_groups
|
||||
if isinstance(tree, FunctionTree) and tree not in used_by_trees]
|
||||
return trees
|
||||
|
||||
@staticmethod
|
||||
def BuildTreeCallGraph() -> DirectedGraph:
|
||||
'''
|
||||
Every vertex is a tree.
|
||||
Every edge (A, B) means: Tree A uses tree B.
|
||||
'''
|
||||
builder = DirectedGraphBuilder()
|
||||
for tree in bpy.data.node_groups:
|
||||
if isinstance(tree, FunctionTree):
|
||||
builder.add_vertex(tree)
|
||||
for dependency_tree in tree.get_directly_used_trees():
|
||||
builder.add_directed_edge(
|
||||
from_v=tree,
|
||||
to_v=dependency_tree)
|
||||
return builder.build()
|
||||
|
||||
@staticmethod
|
||||
def BuildInvertedCallGraph() -> DirectedGraph:
|
||||
'''
|
||||
Builds a directed graph in which every tree is a vertex.
|
||||
Every edge (A, B) means: Changes in A might affect B.
|
||||
'''
|
||||
return FunctionTree.BuildTreeCallGraph().inverted()
|
||||
87
release/scripts/startup/nodes/graph.py
Normal file
87
release/scripts/startup/nodes/graph.py
Normal file
@@ -0,0 +1,87 @@
|
||||
from collections import namedtuple, defaultdict
|
||||
|
||||
class DirectedGraph:
|
||||
def __init__(self, V, E):
|
||||
assert all(isinstance(e, tuple) for e in E)
|
||||
assert all(v1 in V and v2 in V for v1, v2 in E)
|
||||
self.V = set(V)
|
||||
self.E = set(E)
|
||||
|
||||
self.outgoing = defaultdict(set)
|
||||
self.incoming = defaultdict(set)
|
||||
self.neighbors = defaultdict(set)
|
||||
for v1, v2 in E:
|
||||
self.outgoing[v1].add(v2)
|
||||
self.incoming[v2].add(v1)
|
||||
self.neighbors[v1].add(v2)
|
||||
self.neighbors[v2].add(v1)
|
||||
|
||||
def inverted(self):
|
||||
return DirectedGraph(self.V, [(v2, v1) for v1, v2 in self.E])
|
||||
|
||||
def reachable(self, start_verts):
|
||||
return self._reachable(start_verts, self.outgoing)
|
||||
|
||||
def reachable_inversed(self, start_verts):
|
||||
return self._reachable(start_verts, self.incoming)
|
||||
|
||||
def connected(self, start_verts):
|
||||
return self._reachable(start_verts, self.neighbors)
|
||||
|
||||
def _reachable(self, start_verts, next_map):
|
||||
if start_verts in self.V:
|
||||
start_verts = (start_verts, )
|
||||
assert all(v in self.V for v in start_verts)
|
||||
|
||||
verts_to_check = set(start_verts)
|
||||
found_verts = set()
|
||||
while len(verts_to_check) > 0:
|
||||
v = verts_to_check.pop()
|
||||
found_verts.add(v)
|
||||
for prev_v in next_map[v]:
|
||||
if prev_v not in found_verts:
|
||||
verts_to_check.add(prev_v)
|
||||
return found_verts
|
||||
|
||||
def toposort(self):
|
||||
return self.toposort_partial(self.V)
|
||||
|
||||
def toposort_partial(self, verts_to_sort):
|
||||
verts_to_sort = set(verts_to_sort)
|
||||
sorted_verts = list()
|
||||
temp_marked_verts = set()
|
||||
finished_verts = set()
|
||||
|
||||
def visit(v):
|
||||
if v in finished_verts:
|
||||
return
|
||||
if v in temp_marked_verts:
|
||||
raise Exception("not a DAG")
|
||||
temp_marked_verts.add(v)
|
||||
for prev_v in self.incoming[v]:
|
||||
visit(prev_v)
|
||||
finished_verts.add(v)
|
||||
if v in verts_to_sort:
|
||||
sorted_verts.append(v)
|
||||
|
||||
for v in verts_to_sort:
|
||||
visit(v)
|
||||
|
||||
return tuple(sorted_verts)
|
||||
|
||||
|
||||
class DirectedGraphBuilder:
|
||||
def __init__(self):
|
||||
self.V = set()
|
||||
self.E = set()
|
||||
|
||||
def add_vertex(self, v):
|
||||
self.V.add(v)
|
||||
|
||||
def add_directed_edge(self, from_v, to_v):
|
||||
self.V.add(from_v)
|
||||
self.V.add(to_v)
|
||||
self.E.add((from_v, to_v))
|
||||
|
||||
def build(self):
|
||||
return DirectedGraph(self.V, self.E)
|
||||
291
release/scripts/startup/nodes/inferencing.py
Normal file
291
release/scripts/startup/nodes/inferencing.py
Normal file
@@ -0,0 +1,291 @@
|
||||
from collections import namedtuple, defaultdict
|
||||
from . utils.graph import iter_connected_components
|
||||
from . types import type_infos
|
||||
from . tree_data import TreeData
|
||||
|
||||
from . declaration import (
|
||||
FixedSocketDecl,
|
||||
ListSocketDecl,
|
||||
BaseListVariadic,
|
||||
VectorizedInputDecl,
|
||||
VectorizedOutputDecl,
|
||||
)
|
||||
|
||||
DecisionID = namedtuple("DecisionID", ("node", "prop_name"))
|
||||
|
||||
def get_inferencing_decisions(tree_data: TreeData):
|
||||
list_decisions = make_list_decisions(tree_data)
|
||||
vector_decisions = make_vector_decisions(tree_data, list_decisions)
|
||||
base_list_variadic_decisions = make_base_list_variadic_decisions(tree_data, list_decisions, vector_decisions)
|
||||
|
||||
decisions = dict()
|
||||
decisions.update(list_decisions)
|
||||
decisions.update(vector_decisions)
|
||||
decisions.update(base_list_variadic_decisions)
|
||||
return decisions
|
||||
|
||||
|
||||
# Inference list type decisions
|
||||
#################################################
|
||||
|
||||
def make_list_decisions(tree_data):
|
||||
decision_users = get_list_decision_ids_with_users(tree_data)
|
||||
decision_links = get_list_decision_links(tree_data)
|
||||
|
||||
decisions = dict()
|
||||
|
||||
for component in iter_connected_components(decision_users.keys(), decision_links):
|
||||
possible_types = set(iter_possible_list_component_types(
|
||||
component, decision_users, tree_data))
|
||||
|
||||
if len(possible_types) == 1:
|
||||
base_type = next(iter(possible_types))
|
||||
for decision_id in component:
|
||||
decisions[decision_id] = base_type
|
||||
|
||||
return decisions
|
||||
|
||||
def get_list_decision_ids_with_users(tree_data):
|
||||
decision_users = defaultdict(lambda: {"BASE": [], "LIST": []})
|
||||
|
||||
for node in tree_data.iter_nodes():
|
||||
for decl, sockets in node.decl_map.iter_decl_with_sockets():
|
||||
if isinstance(decl, ListSocketDecl):
|
||||
decision_id = DecisionID(node, decl.prop_name)
|
||||
decision_users[decision_id][decl.list_or_base].append(sockets[0])
|
||||
|
||||
return decision_users
|
||||
|
||||
def get_list_decision_links(tree_data):
|
||||
linked_decisions = defaultdict(set)
|
||||
|
||||
for from_socket, to_socket in tree_data.iter_connections():
|
||||
from_node = tree_data.get_node(from_socket)
|
||||
to_node = tree_data.get_node(to_socket)
|
||||
from_decl = from_socket.get_decl(from_node)
|
||||
to_decl = to_socket.get_decl(to_node)
|
||||
if isinstance(from_decl, ListSocketDecl) and isinstance(to_decl, ListSocketDecl):
|
||||
if from_decl.list_or_base == to_decl.list_or_base:
|
||||
from_decision_id = DecisionID(from_node, from_decl.prop_name)
|
||||
to_decision_id = DecisionID(to_node, to_decl.prop_name)
|
||||
linked_decisions[from_decision_id].add(to_decision_id)
|
||||
linked_decisions[to_decision_id].add(from_decision_id)
|
||||
|
||||
return linked_decisions
|
||||
|
||||
def iter_possible_list_component_types(component, decision_users, tree_data):
|
||||
for decision_id in component:
|
||||
for socket in decision_users[decision_id]["LIST"]:
|
||||
for other_node, other_socket in tree_data.iter_connected_sockets_with_nodes(socket):
|
||||
other_decl = other_socket.get_decl(other_node)
|
||||
if data_sockets_are_static(other_decl):
|
||||
data_type = other_socket.data_type
|
||||
if type_infos.is_list(data_type):
|
||||
yield type_infos.to_base(data_type)
|
||||
elif isinstance(other_decl, BaseListVariadic):
|
||||
yield other_decl.base_type
|
||||
elif isinstance(other_decl, VectorizedInputDecl):
|
||||
yield other_decl.base_type
|
||||
elif isinstance(other_decl, VectorizedOutputDecl):
|
||||
yield other_decl.base_type
|
||||
for socket in decision_users[decision_id]["BASE"]:
|
||||
for other_node, other_socket in tree_data.iter_connected_sockets_with_nodes(socket):
|
||||
other_decl = other_socket.get_decl(other_node)
|
||||
if data_sockets_are_static(other_decl):
|
||||
data_type = other_socket.data_type
|
||||
if type_infos.is_base(data_type):
|
||||
yield data_type
|
||||
elif isinstance(other_decl, BaseListVariadic):
|
||||
yield other_decl.base_type
|
||||
elif isinstance(other_decl, VectorizedInputDecl):
|
||||
yield other_decl.base_type
|
||||
elif isinstance(other_decl, VectorizedOutputDecl):
|
||||
yield other_decl.base_type
|
||||
|
||||
|
||||
# Inference vectorization decisions
|
||||
########################################
|
||||
|
||||
def make_vector_decisions(tree_data, list_decisions):
|
||||
graph, input_sockets, output_sockets = get_vector_decisions_graph(tree_data)
|
||||
|
||||
decisions = dict()
|
||||
decision_ids_with_collision = set()
|
||||
|
||||
for initial_decision_id, decision in iter_obligatory_vector_decisions(graph, input_sockets, output_sockets, tree_data, list_decisions):
|
||||
for decision_id in graph.reachable(initial_decision_id):
|
||||
if decision_id in decisions:
|
||||
if decisions[decision_id] != decision:
|
||||
decision_ids_with_collision.add(decision_id)
|
||||
else:
|
||||
decisions[decision_id] = decision
|
||||
|
||||
for decision_id in graph.V:
|
||||
decisions.setdefault(decision_id, "BASE")
|
||||
|
||||
while len(decision_ids_with_collision) > 0:
|
||||
collision_decision_id = decision_ids_with_collision.pop()
|
||||
connected_decision_ids = graph.connected(collision_decision_id)
|
||||
for decision_id in connected_decision_ids:
|
||||
decisions.pop(decision_id, None)
|
||||
decision_ids_with_collision.discard(decision_id)
|
||||
|
||||
return decisions
|
||||
|
||||
def get_vector_decisions_graph(tree_data):
|
||||
'''
|
||||
Builds a directed graph.
|
||||
Vertices in that graph are decision IDs.
|
||||
A directed edge (A, B) means: If A is a list, then B has to be a list.
|
||||
'''
|
||||
from . graph import DirectedGraphBuilder
|
||||
builder = DirectedGraphBuilder()
|
||||
input_sockets = set()
|
||||
output_sockets = set()
|
||||
|
||||
for node in tree_data.iter_nodes():
|
||||
for decl, sockets in node.decl_map.iter_decl_with_sockets():
|
||||
if isinstance(decl, VectorizedInputDecl):
|
||||
decision_id = DecisionID(node, decl.prop_name)
|
||||
builder.add_vertex(decision_id)
|
||||
input_sockets.add(sockets[0])
|
||||
elif isinstance(decl, VectorizedOutputDecl):
|
||||
output_sockets.add(sockets[0])
|
||||
|
||||
for from_socket, to_socket in tree_data.iter_connections():
|
||||
from_node = tree_data.get_node(from_socket)
|
||||
to_node = tree_data.get_node(to_socket)
|
||||
|
||||
from_decl = from_socket.get_decl(from_node)
|
||||
to_decl = to_socket.get_decl(to_node)
|
||||
|
||||
if isinstance(from_decl, VectorizedOutputDecl) and isinstance(to_decl, VectorizedInputDecl):
|
||||
for prop_name in from_decl.input_prop_names:
|
||||
from_decision_id = DecisionID(from_node, prop_name)
|
||||
to_decision_id = DecisionID(to_node, to_decl.prop_name)
|
||||
builder.add_directed_edge(from_decision_id, to_decision_id)
|
||||
|
||||
return builder.build(), input_sockets, output_sockets
|
||||
|
||||
def iter_obligatory_vector_decisions(graph, input_sockets, output_sockets, tree_data, list_decisions):
|
||||
for socket in input_sockets:
|
||||
other_node, other_socket = tree_data.try_get_origin_with_node(socket)
|
||||
if other_node is None:
|
||||
continue
|
||||
|
||||
node = tree_data.get_node(socket)
|
||||
decl = socket.get_decl(node)
|
||||
decision_id = DecisionID(node, decl.prop_name)
|
||||
|
||||
other_decl = other_socket.get_decl(other_node)
|
||||
if data_sockets_are_static(other_decl):
|
||||
other_data_type = other_socket.data_type
|
||||
if type_infos.is_list(other_data_type) and type_infos.is_link_allowed(other_data_type, decl.list_type):
|
||||
yield decision_id, "LIST"
|
||||
elif isinstance(other_decl, ListSocketDecl):
|
||||
if other_decl.list_or_base == "LIST":
|
||||
list_decision_id = DecisionID(other_node, other_decl.prop_name)
|
||||
if list_decision_id in list_decisions:
|
||||
other_base_type = list_decisions[list_decision_id]
|
||||
if type_infos.is_link_allowed(other_base_type, decl.base_type):
|
||||
yield decision_id, "LIST"
|
||||
else:
|
||||
old_data_type = other_socket.data_type
|
||||
if type_infos.is_link_allowed(old_data_type, decl.list_type):
|
||||
yield decision_id, "LIST"
|
||||
|
||||
for socket in output_sockets:
|
||||
node = tree_data.get_node(socket)
|
||||
decl = socket.get_decl(node)
|
||||
decision_ids = [DecisionID(node, p) for p in decl.input_prop_names]
|
||||
|
||||
for other_node, other_socket in tree_data.iter_connected_sockets_with_nodes(socket):
|
||||
other_decl = other_socket.get_decl(other_node)
|
||||
if data_sockets_are_static(other_decl):
|
||||
other_data_type = other_socket.data_type
|
||||
if type_infos.is_base(other_data_type) and type_infos.is_link_allowed(other_data_type, decl.base_type):
|
||||
for decision_id in decision_ids:
|
||||
yield decision_id, "BASE"
|
||||
elif isinstance(other_decl, ListSocketDecl):
|
||||
if other_decl.list_or_base == "BASE":
|
||||
list_decision_id = DecisionID(other_node, other_decl.prop_name)
|
||||
if list_decision_id in list_decisions:
|
||||
other_base_type = list_decisions[list_decision_id]
|
||||
if type_infos.is_link_allowed(decl.base_type, other_base_type):
|
||||
for decision_id in decision_ids:
|
||||
yield decision_id, "BASE"
|
||||
else:
|
||||
old_data_type = other_socket.data_type
|
||||
if type_infos.is_link_allowed(decl.base_type, old_data_type):
|
||||
for decision_id in decision_ids:
|
||||
yield decision_id, "BASE"
|
||||
|
||||
|
||||
# Inference pack list decisions
|
||||
########################################
|
||||
|
||||
def make_base_list_variadic_decisions(tree_data, list_decisions, vector_decisions):
|
||||
decisions = dict()
|
||||
|
||||
for decision_id, decl, socket in iter_base_list_variadic_sockets(tree_data):
|
||||
assert not socket.is_output
|
||||
|
||||
origin_node, origin_socket = tree_data.try_get_origin_with_node(socket)
|
||||
if origin_socket is None:
|
||||
decisions[decision_id] = "BASE"
|
||||
continue
|
||||
|
||||
origin_decl = origin_socket.get_decl(origin_node)
|
||||
if data_sockets_are_static(origin_decl):
|
||||
data_type = origin_socket.data_type
|
||||
if type_infos.is_link_allowed(data_type, decl.base_type):
|
||||
decisions[decision_id] = "BASE"
|
||||
elif type_infos.is_link_allowed(data_type, decl.list_type):
|
||||
decisions[decision_id] = "LIST"
|
||||
else:
|
||||
decisions[decision_id] = "BASE"
|
||||
elif isinstance(origin_decl, ListSocketDecl):
|
||||
list_decision_id = DecisionID(origin_node, origin_decl.prop_name)
|
||||
if list_decision_id in list_decisions:
|
||||
other_base_type = list_decisions[list_decision_id]
|
||||
if type_infos.is_link_allowed(other_base_type, decl.base_type):
|
||||
decisions[decision_id] = origin_decl.list_or_base
|
||||
else:
|
||||
decisions[decision_id] = "BASE"
|
||||
else:
|
||||
old_origin_type = origin_socket.data_type
|
||||
if type_infos.is_link_allowed(old_origin_type, decl.base_type):
|
||||
decisions[decision_id] = "BASE"
|
||||
elif type_infos.is_link_allowed(old_origin_type, decl.list_type):
|
||||
decisions[decision_id] = "LIST"
|
||||
else:
|
||||
decisions[decision_id] = "BASE"
|
||||
elif isinstance(origin_decl, VectorizedOutputDecl):
|
||||
other_base_type = origin_decl.base_type
|
||||
if type_infos.is_link_allowed(other_base_type, decl.base_type):
|
||||
for input_prop_name in origin_decl.input_prop_names:
|
||||
input_decision_id = DecisionID(origin_node, input_prop_name)
|
||||
if input_decision_id in vector_decisions:
|
||||
if vector_decisions[input_decision_id] == "LIST":
|
||||
decisions[decision_id] = "LIST"
|
||||
break
|
||||
else:
|
||||
decisions[decision_id] = "BASE"
|
||||
else:
|
||||
decisions[decision_id] = "BASE"
|
||||
else:
|
||||
decisions[decision_id] = "BASE"
|
||||
|
||||
return decisions
|
||||
|
||||
def data_sockets_are_static(decl):
|
||||
return isinstance(decl, FixedSocketDecl)
|
||||
|
||||
def iter_base_list_variadic_sockets(tree_data):
|
||||
for node in tree_data.iter_nodes():
|
||||
for decl, sockets in node.decl_map.iter_decl_with_sockets():
|
||||
if isinstance(decl, BaseListVariadic):
|
||||
collection = decl.get_collection()
|
||||
for i, socket in enumerate(sockets[:-1]):
|
||||
decision_id = DecisionID(node, f"{decl.prop_name}[{i}].state")
|
||||
yield decision_id, decl, socket
|
||||
16
release/scripts/startup/nodes/keymap.py
Normal file
16
release/scripts/startup/nodes/keymap.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import bpy
|
||||
|
||||
def register():
|
||||
wm = bpy.context.window_manager
|
||||
if wm.keyconfigs.addon is None:
|
||||
return
|
||||
|
||||
km = wm.keyconfigs.addon.keymaps.new(
|
||||
name="Node Editor",
|
||||
space_type='NODE_EDITOR')
|
||||
|
||||
km.keymap_items.new(
|
||||
"fn.node_search",
|
||||
type='A',
|
||||
value='PRESS',
|
||||
ctrl=True)
|
||||
95
release/scripts/startup/nodes/menu.py
Normal file
95
release/scripts/startup/nodes/menu.py
Normal file
@@ -0,0 +1,95 @@
|
||||
import bpy
|
||||
from . function_tree import FunctionTree
|
||||
|
||||
def draw_menu(self, context):
|
||||
tree = context.space_data.node_tree
|
||||
if not isinstance(tree, FunctionTree):
|
||||
return
|
||||
|
||||
layout = self.layout
|
||||
layout.operator_context = 'INVOKE_DEFAULT'
|
||||
|
||||
layout.operator("fn.node_search", text="Search", icon='VIEWZOOM')
|
||||
layout.separator()
|
||||
layout.menu("FN_MT_function_nodes_menu", text="Function Nodes")
|
||||
layout.separator()
|
||||
insert_node(layout, "fn_ParticleSystemNode", "Particle System")
|
||||
layout.menu("BP_MT_influences_nodes_menu", text="Influences")
|
||||
layout.menu("BP_MT_action_nodes_menu", text="Actions")
|
||||
|
||||
class FunctionNodesMenu(bpy.types.Menu):
|
||||
bl_idname = "FN_MT_function_nodes_menu"
|
||||
bl_label = "Function Nodes Menu"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator_context = 'INVOKE_DEFAULT'
|
||||
|
||||
insert_node(layout, "fn_SwitchNode", "Switch")
|
||||
layout.separator()
|
||||
insert_node(layout, "fn_FloatRangeNode", "Float Range")
|
||||
layout.separator()
|
||||
insert_node(layout, "fn_CombineVectorNode", "Combine Vector")
|
||||
insert_node(layout, "fn_SeparateVectorNode", "Separate Vector")
|
||||
insert_node(layout, "fn_VectorDistanceNode", "Vector Distance")
|
||||
layout.separator()
|
||||
insert_node(layout, "fn_SeparateColorNode", "Separate Color")
|
||||
insert_node(layout, "fn_CombineColorNode", "Combine Color")
|
||||
layout.separator()
|
||||
insert_node(layout, "fn_GetListElementNode", "Get List Element")
|
||||
insert_node(layout, "fn_ListLengthNode", "List Length")
|
||||
insert_node(layout, "fn_PackListNode", "Pack List")
|
||||
layout.separator()
|
||||
insert_node(layout, "fn_ObjectMeshNode", "Object Mesh")
|
||||
insert_node(layout, "fn_ObjectTransformsNode", "Object Transforms")
|
||||
insert_node(layout, "fn_TextLengthNode", "Text Length")
|
||||
|
||||
class InfluencesNodesMenu(bpy.types.Menu):
|
||||
bl_idname = "BP_MT_influences_nodes_menu"
|
||||
bl_label = "Influences Nodes Menu"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator_context = 'INVOKE_DEFAULT'
|
||||
|
||||
insert_node(layout, "fn_CombineInfluencesNode", "Combine Influences")
|
||||
layout.separator()
|
||||
insert_node(layout, "fn_InitialGridEmitterNode", "Initial Grid Emitter")
|
||||
insert_node(layout, "fn_MeshEmitterNode", "Mesh Emitter")
|
||||
insert_node(layout, "fn_PointEmitterNode", "Point Emitter")
|
||||
layout.separator()
|
||||
insert_node(layout, "fn_AgeReachedEventNode", "Age Reached Event")
|
||||
insert_node(layout, "fn_MeshCollisionEventNode", "Mesh Collision Event")
|
||||
insert_node(layout, "fn_CustomEventNode", "Custom Event")
|
||||
layout.separator()
|
||||
insert_node(layout, "fn_ForceNode", "Force")
|
||||
layout.separator()
|
||||
insert_node(layout, "fn_SizeOverTimeNode", "Size Over Time")
|
||||
insert_node(layout, "fn_ParticleTrailsNode", "Trails")
|
||||
insert_node(layout, "fn_AlwaysExecuteNode", "Always Execute")
|
||||
|
||||
|
||||
class ActionNodesMenu(bpy.types.Menu):
|
||||
bl_idname = "BP_MT_action_nodes_menu"
|
||||
bl_label = "Action Nodes Menu"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator_context = 'INVOKE_DEFAULT'
|
||||
|
||||
insert_node(layout, "fn_ParticleConditionNode", "Condition")
|
||||
|
||||
|
||||
def insert_node(layout, type, text, settings = {}, icon = "NONE"):
|
||||
operator = layout.operator("node.add_node", text = text, icon = icon)
|
||||
operator.type = type
|
||||
operator.use_transform = True
|
||||
for name, value in settings.items():
|
||||
item = operator.settings.add()
|
||||
item.name = name
|
||||
item.value = value
|
||||
return operator
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.NODE_MT_add.append(draw_menu)
|
||||
240
release/scripts/startup/nodes/node_builder.py
Normal file
240
release/scripts/startup/nodes/node_builder.py
Normal file
@@ -0,0 +1,240 @@
|
||||
from . declaration import (
|
||||
FixedSocketDecl,
|
||||
ListSocketDecl,
|
||||
BaseListVariadic,
|
||||
VectorizedInputDecl,
|
||||
VectorizedOutputDecl,
|
||||
InfluencesSocketDecl,
|
||||
ExecuteOutputDecl,
|
||||
ExecuteInputListDecl,
|
||||
ExecuteInputDecl,
|
||||
|
||||
NoDefaultValue,
|
||||
)
|
||||
|
||||
class NodeBuilder:
|
||||
def __init__(self, node):
|
||||
self.node = node
|
||||
self.input_declarations = []
|
||||
self.output_declarations = []
|
||||
self._background_color = None
|
||||
|
||||
def _add_input(self, decl):
|
||||
self.input_declarations.append(decl)
|
||||
|
||||
def _add_output(self, decl):
|
||||
self.output_declarations.append(decl)
|
||||
|
||||
def initialize_decls(self):
|
||||
for decl in self.input_declarations:
|
||||
decl.init()
|
||||
|
||||
for decl in self.output_declarations:
|
||||
decl.init()
|
||||
|
||||
def build(self):
|
||||
from . sync import skip_syncing
|
||||
with skip_syncing():
|
||||
self.node.inputs.clear()
|
||||
self.node.outputs.clear()
|
||||
|
||||
for decl in self.input_declarations:
|
||||
sockets = decl.build(self.node.inputs)
|
||||
assert len(sockets) == decl.amount()
|
||||
decl.init_default(sockets)
|
||||
|
||||
for decl in self.output_declarations:
|
||||
sockets = decl.build(self.node.outputs)
|
||||
assert len(sockets) == decl.amount()
|
||||
decl.init_default(sockets)
|
||||
|
||||
if self._background_color is not None:
|
||||
self.node.use_custom_color = True
|
||||
self.node.color = self._background_color
|
||||
|
||||
def get_sockets_decl_map(self):
|
||||
return SocketDeclMap(
|
||||
self.node,
|
||||
self.input_declarations,
|
||||
self.output_declarations)
|
||||
|
||||
def matches_sockets(self):
|
||||
if not self._declarations_matches_sockets(self.input_declarations, self.node.inputs):
|
||||
return False
|
||||
if not self._declarations_matches_sockets(self.output_declarations, self.node.outputs):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _declarations_matches_sockets(self, declarations, all_sockets):
|
||||
sockets_iter = iter(all_sockets)
|
||||
for decl in declarations:
|
||||
amount = decl.amount()
|
||||
try: sockets = [next(sockets_iter) for _ in range(amount)]
|
||||
except StopIteration: return False
|
||||
if not decl.validate(sockets):
|
||||
return False
|
||||
if len(tuple(sockets_iter)) > 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
# General Node Properties
|
||||
###################################
|
||||
|
||||
def background_color(self, color):
|
||||
assert len(color) == 3
|
||||
self._background_color = color
|
||||
|
||||
|
||||
# Fixed Data Types
|
||||
###################################
|
||||
|
||||
def fixed_input(self, identifier, name, data_type,
|
||||
*, default=NoDefaultValue, **kwargs):
|
||||
decl = FixedSocketDecl(self.node, identifier, name, data_type, default, kwargs)
|
||||
self._add_input(decl)
|
||||
|
||||
def fixed_output(self, identifier, name, data_type,
|
||||
*, default=NoDefaultValue, **kwargs):
|
||||
decl = FixedSocketDecl(self.node, identifier, name, data_type, default, kwargs)
|
||||
self._add_output(decl)
|
||||
|
||||
def fixed_pass_through(self, identifier, name, data_type, *, default=NoDefaultValue):
|
||||
self.fixed_input(identifier, name, data_type, default=default)
|
||||
self.fixed_output(identifier, name, data_type, default=default)
|
||||
|
||||
|
||||
# Packed List
|
||||
###################################
|
||||
|
||||
@staticmethod
|
||||
def BaseListVariadicProperty():
|
||||
return BaseListVariadic.Property()
|
||||
|
||||
@staticmethod
|
||||
def BaseListVariadicPropertyHasList(prop):
|
||||
return any(v.state == "LIST" for v in prop)
|
||||
|
||||
|
||||
def base_list_variadic_input(self, identifier, prop_name, base_type, default_amount=2):
|
||||
decl = BaseListVariadic(self.node, identifier, prop_name, base_type, default_amount)
|
||||
self._add_input(decl)
|
||||
|
||||
|
||||
# Dynamic List
|
||||
###################################
|
||||
|
||||
@staticmethod
|
||||
def DynamicListProperty():
|
||||
return ListSocketDecl.Property()
|
||||
|
||||
def dynamic_list_input(self, identifier, name, prop_name):
|
||||
decl = ListSocketDecl(self.node, identifier, name, prop_name, "LIST")
|
||||
self._add_input(decl)
|
||||
|
||||
def dynamic_list_output(self, identifier, name, prop_name):
|
||||
decl = ListSocketDecl(self.node, identifier, name, prop_name, "LIST")
|
||||
self._add_output(decl)
|
||||
|
||||
def dynamic_base_input(self, identifier, name, prop_name):
|
||||
decl = ListSocketDecl(self.node, identifier, name, prop_name, "BASE")
|
||||
self._add_input(decl)
|
||||
|
||||
def dynamic_base_output(self, identifier, name, prop_name):
|
||||
decl = ListSocketDecl(self.node, identifier, name, prop_name, "BASE")
|
||||
self._add_output(decl)
|
||||
|
||||
|
||||
# Vectorized
|
||||
##################################
|
||||
|
||||
@staticmethod
|
||||
def VectorizedProperty():
|
||||
return VectorizedInputDecl.Property()
|
||||
|
||||
def vectorized_input(self, identifier, prop_name, base_name, list_name, base_type,
|
||||
*, default=NoDefaultValue, **kwargs):
|
||||
decl = VectorizedInputDecl(
|
||||
self.node, identifier, prop_name,
|
||||
base_name, list_name, base_type,
|
||||
default, kwargs)
|
||||
self._add_input(decl)
|
||||
|
||||
def vectorized_output(self, identifier, input_prop_names, base_name, list_name, base_type,
|
||||
**kwargs):
|
||||
decl = VectorizedOutputDecl(
|
||||
self.node, identifier, input_prop_names,
|
||||
base_name, list_name, base_type, kwargs)
|
||||
self._add_output(decl)
|
||||
|
||||
|
||||
# BParticles
|
||||
###################################
|
||||
|
||||
def influences_input(self, identifier, name):
|
||||
decl = InfluencesSocketDecl(self.node, identifier, name)
|
||||
self._add_input(decl)
|
||||
|
||||
def influences_output(self, identifier, name):
|
||||
decl = InfluencesSocketDecl(self.node, identifier, name)
|
||||
self._add_output(decl)
|
||||
|
||||
@staticmethod
|
||||
def ExecuteInputProperty():
|
||||
return ExecuteInputListDecl.Property()
|
||||
|
||||
def execute_input(self, identifier, display_name, prop_name):
|
||||
decl = ExecuteInputListDecl(self.node, identifier, prop_name, display_name)
|
||||
self._add_input(decl)
|
||||
|
||||
def single_execute_input(self, identifier, name):
|
||||
decl = ExecuteInputDecl(self.node, identifier, name)
|
||||
self._add_input(decl)
|
||||
|
||||
def execute_output(self, identifier, name):
|
||||
decl = ExecuteOutputDecl(self.node, identifier, name)
|
||||
self._add_output(decl)
|
||||
|
||||
|
||||
|
||||
class SocketDeclMap:
|
||||
def __init__(self, node, input_declarations, output_declarations):
|
||||
self.node = node
|
||||
self._sockets_by_decl = dict()
|
||||
self._decl_by_socket = dict()
|
||||
self._socket_index_in_decl = dict()
|
||||
|
||||
for decl, sockets in iter_sockets_by_decl(node.inputs, input_declarations):
|
||||
self._sockets_by_decl[decl] = sockets
|
||||
for i, socket in enumerate(sockets):
|
||||
self._decl_by_socket[socket] = decl
|
||||
self._socket_index_in_decl[socket] = i
|
||||
|
||||
for decl, sockets in iter_sockets_by_decl(node.outputs, output_declarations):
|
||||
self._sockets_by_decl[decl] = sockets
|
||||
for i, socket in enumerate(sockets):
|
||||
self._decl_by_socket[socket] = decl
|
||||
self._socket_index_in_decl[socket] = i
|
||||
|
||||
def get_decl_by_socket(self, socket):
|
||||
return self._decl_by_socket[socket]
|
||||
|
||||
def get_socket_index_in_decl(self, socket):
|
||||
return self._socket_index_in_decl[socket]
|
||||
|
||||
def get_sockets_by_decl(self, decl):
|
||||
return self._sockets_by_decl[decl]
|
||||
|
||||
def iter_decl_with_sockets(self):
|
||||
yield from self._sockets_by_decl.items()
|
||||
|
||||
def iter_decls(self):
|
||||
yield from self._sockets_by_decl.keys()
|
||||
|
||||
|
||||
def iter_sockets_by_decl(node_sockets, declarations):
|
||||
node_sockets_iter = iter(node_sockets)
|
||||
for decl in declarations:
|
||||
amount = decl.amount()
|
||||
sockets_of_decl = tuple(next(node_sockets_iter) for _ in range(amount))
|
||||
assert decl.validate(sockets_of_decl)
|
||||
yield decl, sockets_of_decl
|
||||
151
release/scripts/startup/nodes/node_operators.py
Normal file
151
release/scripts/startup/nodes/node_operators.py
Normal file
@@ -0,0 +1,151 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from . types import type_infos
|
||||
from . function_tree import FunctionTree
|
||||
|
||||
def try_find_node(tree_name, node_name):
|
||||
tree = bpy.data.node_groups.get(tree_name)
|
||||
if tree is not None:
|
||||
return tree.nodes.get(node_name)
|
||||
return None
|
||||
|
||||
class NodeOperatorBase:
|
||||
tree_name: StringProperty()
|
||||
node_name: StringProperty()
|
||||
function_name: StringProperty()
|
||||
settings_repr: StringProperty()
|
||||
|
||||
def call(self, *args):
|
||||
node = try_find_node(self.tree_name, self.node_name)
|
||||
if node is None:
|
||||
return {'CANCELLED'}
|
||||
|
||||
function = getattr(node, self.function_name)
|
||||
settings = eval(self.settings_repr)
|
||||
function(*args, *settings)
|
||||
return {'FINISHED'}
|
||||
|
||||
class NodeOperator(bpy.types.Operator, NodeOperatorBase):
|
||||
bl_idname = "fn.node_operator"
|
||||
bl_label = "Generic Node Operator"
|
||||
bl_options = {'INTERNAL'}
|
||||
|
||||
def execute(self, context):
|
||||
return self.call()
|
||||
|
||||
class NodeDataTypeSelector(bpy.types.Operator, NodeOperatorBase):
|
||||
bl_idname = "fn.node_data_type_selector"
|
||||
bl_label = "Generic Node Data Type Selector"
|
||||
bl_options = {'INTERNAL'}
|
||||
bl_property = "item"
|
||||
|
||||
mode: EnumProperty(
|
||||
items=[
|
||||
("ALL", "All", ""),
|
||||
("BASE", "Base", ""),
|
||||
])
|
||||
|
||||
def get_items(self, context):
|
||||
if self.mode == "ALL":
|
||||
return type_infos.get_data_type_items()
|
||||
elif self.mode == "BASE":
|
||||
return type_infos.get_base_type_items()
|
||||
else:
|
||||
assert False
|
||||
|
||||
item: EnumProperty(items=get_items)
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.invoke_search_popup(self)
|
||||
return {'CANCELLED'}
|
||||
|
||||
def execute(self, context):
|
||||
return self.call(self.item)
|
||||
|
||||
class NodeGroupSelector(bpy.types.Operator, NodeOperatorBase):
|
||||
bl_idname = "fn.node_group_selector"
|
||||
bl_label = "Node Group Selector"
|
||||
bl_options = {'INTERNAL'}
|
||||
bl_property = "item"
|
||||
|
||||
def get_items(self, context):
|
||||
tree = bpy.data.node_groups.get(self.tree_name)
|
||||
possible_trees = tree.find_callable_trees()
|
||||
|
||||
items = []
|
||||
for tree in possible_trees:
|
||||
items.append((tree.name, tree.name, ""))
|
||||
items.append(("NONE", "None", ""))
|
||||
return items
|
||||
|
||||
item: EnumProperty(items=get_items)
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.invoke_search_popup(self)
|
||||
return {'CANCELLED'}
|
||||
|
||||
def execute(self, context):
|
||||
if self.item == "NONE":
|
||||
return self.call(None)
|
||||
else:
|
||||
return self.call(bpy.data.node_groups.get(self.item))
|
||||
|
||||
class MoveViewToNode(bpy.types.Operator):
|
||||
bl_idname = "fn.move_view_to_node"
|
||||
bl_label = "Move View to Node"
|
||||
bl_options = {'INTERNAL'}
|
||||
|
||||
tree_name: StringProperty()
|
||||
node_name: StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
target_node = try_find_node(self.tree_name, self.node_name)
|
||||
if target_node is None:
|
||||
return {'CANCELLED'}
|
||||
|
||||
tree = target_node.tree
|
||||
context.space_data.node_tree = tree
|
||||
for node in tree.nodes:
|
||||
node.select = False
|
||||
|
||||
target_node.select = True
|
||||
tree.nodes.active = target_node
|
||||
|
||||
bpy.ops.node.view_selected('INVOKE_DEFAULT')
|
||||
return {'FINISHED'}
|
||||
|
||||
def new_function_tree(name, inputs, outputs):
|
||||
tree = bpy.data.node_groups.new(name, "FunctionTree")
|
||||
|
||||
for i, (data_type, input_name) in enumerate(inputs):
|
||||
input_node = tree.nodes.new("fn_GroupInputNode")
|
||||
input_node.sort_index = i
|
||||
input_node.interface_type = "DATA"
|
||||
input_node.input_name = input_name
|
||||
input_node.data_type = data_type
|
||||
input_node.location = (-200, -i * 130)
|
||||
|
||||
for i, (data_type, output_name) in enumerate(outputs):
|
||||
output_node = tree.nodes.new("fn_GroupOutputNode")
|
||||
output_node.sort_index = i
|
||||
output_node.output_name = output_name
|
||||
output_node.data_type = data_type
|
||||
output_node.location = (200, -i * 130)
|
||||
|
||||
tree.sync()
|
||||
return tree
|
||||
|
||||
class NewParticleSystem(bpy.types.Operator):
|
||||
bl_idname = "fn.new_particle_system"
|
||||
bl_label = "New Particle System"
|
||||
|
||||
def execute(self, context):
|
||||
mesh = bpy.data.meshes.new("Particle Simulation")
|
||||
ob = bpy.data.objects.new("Particle Simulation", mesh)
|
||||
modifier = ob.modifiers.new("BParticles", 'BPARTICLES')
|
||||
|
||||
bpy.ops.fn.new_particle_simulation_tree(object_name=ob.name, modifier_name=modifier.name)
|
||||
|
||||
context.collection.objects.link(ob)
|
||||
|
||||
return {'FINISHED'}
|
||||
34
release/scripts/startup/nodes/problems.py
Normal file
34
release/scripts/startup/nodes/problems.py
Normal file
@@ -0,0 +1,34 @@
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from . ui import NodeSidebarPanel
|
||||
|
||||
warnings = []
|
||||
|
||||
def report_warning(node, msg):
|
||||
warnings.append((node, msg))
|
||||
|
||||
class ProblemsPanel(bpy.types.Panel, NodeSidebarPanel):
|
||||
bl_idname = "FN_PT_problems"
|
||||
bl_label = "Problems"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
for i, (node, msg) in enumerate(warnings):
|
||||
row = layout.row(align=True)
|
||||
row.label(text=msg)
|
||||
props = row.operator("fn.move_view_to_node", text="Find")
|
||||
props.tree_name = node.tree.name
|
||||
props.node_name = node.name
|
||||
props = row.operator("fn.remove_warning", text="", icon='X')
|
||||
props.index = i
|
||||
|
||||
class RemoveWarning(bpy.types.Operator):
|
||||
bl_idname = "fn.remove_warning"
|
||||
bl_label = "Remove Warning"
|
||||
bl_options = {'INTERNAL'}
|
||||
|
||||
index: IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
del warnings[self.index]
|
||||
return {'FINISHED'}
|
||||
94
release/scripts/startup/nodes/search.py
Normal file
94
release/scripts/startup/nodes/search.py
Normal file
@@ -0,0 +1,94 @@
|
||||
import os
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from pathlib import Path
|
||||
from . base import BaseNode
|
||||
from . utils.enum_items_cache import cache_enum_items
|
||||
from functools import lru_cache
|
||||
|
||||
@lru_cache()
|
||||
def get_node_group_names_in_file(path: str):
|
||||
with bpy.data.libraries.load(path) as (data_from, data_to):
|
||||
return list(data_from.node_groups)
|
||||
|
||||
class NodeSearch(bpy.types.Operator):
|
||||
bl_idname = "fn.node_search"
|
||||
bl_label = "Node Search"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
bl_property = "item"
|
||||
|
||||
def get_search_items(self, context):
|
||||
items = []
|
||||
tree = context.space_data.edit_tree
|
||||
for node_cls in BaseNode.iter_final_subclasses():
|
||||
for search_term, settings in node_cls.get_search_terms():
|
||||
item = encode_search_item(("BUILTIN", node_cls.bl_idname, settings), search_term)
|
||||
items.append(item)
|
||||
|
||||
current_tree = context.space_data.node_tree
|
||||
for tree in current_tree.find_callable_trees():
|
||||
item = encode_search_item(("EXISTING_GROUP", tree.name), tree.name + " (G)")
|
||||
items.append(item)
|
||||
|
||||
nodelibdir = context.preferences.filepaths.nodelib_directory
|
||||
if len(nodelibdir) > 0 and os.path.exists(nodelibdir):
|
||||
local_group_names = set(tree.name for tree in bpy.data.node_groups)
|
||||
for path in Path(nodelibdir).glob("**/*.blend"):
|
||||
if not path.is_file():
|
||||
continue
|
||||
for group_name in get_node_group_names_in_file(str(path)):
|
||||
if group_name not in local_group_names:
|
||||
item = encode_search_item(("LIB_GROUP", str(path), group_name), group_name + " (G)")
|
||||
items.append(item)
|
||||
|
||||
sorted_items = list(sorted(items, key=lambda item: item[1]))
|
||||
return sorted_items
|
||||
|
||||
item: EnumProperty(items=cache_enum_items(get_search_items))
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
try: return context.space_data.node_tree.bl_idname == "FunctionTree"
|
||||
except: return False
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.invoke_search_popup(self)
|
||||
return {'CANCELLED'}
|
||||
|
||||
def execute(self, context):
|
||||
tree = context.space_data.node_tree
|
||||
for node in tree.nodes:
|
||||
node.select = False
|
||||
|
||||
item_data = decode_search_item(self.item)
|
||||
item_type = item_data[0]
|
||||
|
||||
if item_type == "BUILTIN":
|
||||
idname, settings = item_data[1:]
|
||||
bpy.ops.node.add_node('INVOKE_DEFAULT', type=idname)
|
||||
new_node = context.active_node
|
||||
for key, value in settings.items():
|
||||
setattr(new_node, key, value)
|
||||
elif item_type == "EXISTING_GROUP":
|
||||
group_name = item_data[1]
|
||||
bpy.ops.node.add_node('INVOKE_DEFAULT', type="fn_GroupNode")
|
||||
new_node = context.active_node
|
||||
new_node.node_group = bpy.data.node_groups[group_name]
|
||||
elif item_type == "LIB_GROUP":
|
||||
path, group_name = item_data[1:]
|
||||
bpy.ops.node.add_node('INVOKE_DEFAULT', type="fn_GroupNode")
|
||||
new_node = context.active_node
|
||||
with bpy.data.libraries.load(path, link=True) as (data_from, data_to):
|
||||
data_to.node_groups = [group_name]
|
||||
new_node.node_group = bpy.data.node_groups[group_name]
|
||||
|
||||
bpy.ops.node.translate_attach("INVOKE_DEFAULT")
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
def encode_search_item(data, search_term):
|
||||
identifier = repr(data)
|
||||
return (identifier, search_term, "")
|
||||
|
||||
def decode_search_item(identifier):
|
||||
return eval(identifier)
|
||||
228
release/scripts/startup/nodes/sockets.py
Normal file
228
release/scripts/startup/nodes/sockets.py
Normal file
@@ -0,0 +1,228 @@
|
||||
import bpy
|
||||
from . base import DataSocket, BaseSocket
|
||||
from bpy.props import *
|
||||
|
||||
class OperatorSocket(bpy.types.NodeSocket, BaseSocket):
|
||||
bl_idname = "fn_OperatorSocket"
|
||||
bl_label = "Operator Socket"
|
||||
|
||||
def draw_color(self, context, node):
|
||||
return (0, 0, 0, 0)
|
||||
|
||||
class FloatSocket(bpy.types.NodeSocket, DataSocket):
|
||||
bl_idname = "fn_FloatSocket"
|
||||
bl_label = "Float Socket"
|
||||
data_type = "Float"
|
||||
color = (0, 0.3, 0.5, 1)
|
||||
|
||||
value: FloatProperty(
|
||||
name="Value",
|
||||
default=0.0,
|
||||
)
|
||||
|
||||
def draw_property(self, layout, node, text):
|
||||
layout.prop(self, "value", text=text)
|
||||
|
||||
def get_state(self):
|
||||
return self.value
|
||||
|
||||
def restore_state(self, state):
|
||||
self.value = state
|
||||
|
||||
class IntegerSocket(bpy.types.NodeSocket, DataSocket):
|
||||
bl_idname = "fn_IntegerSocket"
|
||||
bl_label = "Integer Socket"
|
||||
data_type = "Integer"
|
||||
color = (0.3, 0.7, 0.5, 1)
|
||||
|
||||
value: IntProperty(
|
||||
name="Value",
|
||||
default=0,
|
||||
)
|
||||
|
||||
def draw_property(self, layout, node, text):
|
||||
layout.prop(self, "value", text=text)
|
||||
|
||||
def get_state(self):
|
||||
return self.value
|
||||
|
||||
def restore_state(self, state):
|
||||
self.value = state
|
||||
|
||||
class VectorSocket(bpy.types.NodeSocket, DataSocket):
|
||||
bl_idname = "fn_VectorSocket"
|
||||
bl_label = "Vector Socket"
|
||||
data_type = "Vector"
|
||||
color = (0, 0, 0.5, 1)
|
||||
|
||||
value: FloatVectorProperty(
|
||||
name="Value",
|
||||
size=3,
|
||||
default=(0.0, 0.0, 0.0),
|
||||
)
|
||||
|
||||
def draw_property(self, layout, node, text):
|
||||
layout.column().prop(self, "value", text=text)
|
||||
|
||||
def get_state(self):
|
||||
return tuple(self.value)
|
||||
|
||||
def restore_state(self, state):
|
||||
self.value = state
|
||||
|
||||
class BooleanSocket(bpy.types.NodeSocket, DataSocket):
|
||||
bl_idname = "fn_BooleanSocket"
|
||||
bl_label = "Boolean Socket"
|
||||
data_type = "Boolean"
|
||||
color = (0.3, 0.3, 0.3, 1)
|
||||
|
||||
value: BoolProperty(
|
||||
name="Value",
|
||||
default=False,
|
||||
)
|
||||
|
||||
def draw_property(self, layout, node, text):
|
||||
layout.prop(self, "value", text=text)
|
||||
|
||||
def get_state(self):
|
||||
return self.value
|
||||
|
||||
def restore_state(self, state):
|
||||
self.value = state
|
||||
|
||||
class ObjectSocket(bpy.types.NodeSocket, DataSocket):
|
||||
bl_idname = "fn_ObjectSocket"
|
||||
bl_label = "Object Socket"
|
||||
data_type = "Object"
|
||||
color = (0, 0, 0, 1)
|
||||
|
||||
value: PointerProperty(
|
||||
name="Value",
|
||||
type=bpy.types.Object,
|
||||
)
|
||||
|
||||
display_name: BoolProperty(default=True)
|
||||
|
||||
def draw_property(self, layout, node, text):
|
||||
if not self.display_name:
|
||||
text = ""
|
||||
layout.prop(self, "value", text=text)
|
||||
|
||||
def get_state(self):
|
||||
return self.value
|
||||
|
||||
def restore_state(self, state):
|
||||
self.value = state
|
||||
|
||||
class ImageSocket(bpy.types.NodeSocket, DataSocket):
|
||||
bl_idname = "fn_ImageSocket"
|
||||
bl_label = "Image Socket"
|
||||
data_type = "Image"
|
||||
color = (0.6, 0.6, 0.6, 1)
|
||||
|
||||
value: PointerProperty(
|
||||
name="Value",
|
||||
type=bpy.types.Image,
|
||||
)
|
||||
|
||||
display_name: BoolProperty()
|
||||
|
||||
def draw_property(self, layout, node, text):
|
||||
if not self.display_name:
|
||||
text = ""
|
||||
layout.prop(self, "value", text=text)
|
||||
|
||||
def get_state(self):
|
||||
return self.value
|
||||
|
||||
def restore_state(self, state):
|
||||
self.value = state
|
||||
|
||||
class ColorSocket(bpy.types.NodeSocket, DataSocket):
|
||||
bl_idname = "fn_ColorSocket"
|
||||
bl_label = "Color Socket"
|
||||
data_type = "Color"
|
||||
color = (0.8, 0.8, 0.2, 1)
|
||||
|
||||
value: FloatVectorProperty(
|
||||
name="Value",
|
||||
size=4,
|
||||
default=(0.8, 0.8, 0.8, 1.0),
|
||||
subtype='COLOR',
|
||||
soft_min=0.0,
|
||||
soft_max=0.0,
|
||||
)
|
||||
|
||||
def draw_property(self, layout, node, text):
|
||||
layout.prop(self, "value", text=text)
|
||||
|
||||
def get_state(self):
|
||||
return tuple(self.value)
|
||||
|
||||
def restore_state(self, state):
|
||||
self.value = state
|
||||
|
||||
class TextSocket(bpy.types.NodeSocket, DataSocket):
|
||||
bl_idname = "fn_TextSocket"
|
||||
bl_label = "Text Socket"
|
||||
data_type = "Text"
|
||||
color = (0.8, 0.8, 0.8, 1)
|
||||
|
||||
value: StringProperty(
|
||||
name="Value",
|
||||
default="",
|
||||
)
|
||||
|
||||
display_name: BoolProperty(default=True)
|
||||
display_icon: StringProperty(default="NONE")
|
||||
|
||||
def draw_property(self, layout, node, text):
|
||||
if not self.display_name:
|
||||
text = ""
|
||||
layout.prop(self, "value", text=text, icon=self.display_icon)
|
||||
|
||||
def get_state(self):
|
||||
return self.value
|
||||
|
||||
def restore_state(self, state):
|
||||
self.value = state
|
||||
|
||||
def create_simple_data_socket(idname, data_type, color):
|
||||
return type(idname, (bpy.types.NodeSocket, DataSocket),
|
||||
{
|
||||
"bl_idname" : idname,
|
||||
"bl_label" : idname,
|
||||
"data_type" : data_type,
|
||||
"color" : color,
|
||||
})
|
||||
|
||||
FloatListSocket = create_simple_data_socket(
|
||||
"fn_FloatListSocket", "Float List", (0, 0.3, 0.5, 0.5))
|
||||
VectorListSocket = create_simple_data_socket(
|
||||
"fn_VectorListSocket", "Vector List", (0, 0, 0.5, 0.5))
|
||||
IntegerListSocket = create_simple_data_socket(
|
||||
"fn_IntegerListSocket", "Integer List", (0.3, 0.7, 0.5, 0.5))
|
||||
BooleanListSocket = create_simple_data_socket(
|
||||
"fn_BooleanListSocket", "Boolean List", (0.3, 0.3, 0.3, 0.5))
|
||||
ObjectListSocket = create_simple_data_socket(
|
||||
"fn_ObjectListSocket", "Object List", (0, 0, 0, 0.5))
|
||||
ImageListSocket = create_simple_data_socket(
|
||||
"fn_ImageListSocket", "Image List", (0.6, 0.6, 0.6, 0.5))
|
||||
ColorListSocket = create_simple_data_socket(
|
||||
"fn_ColorListSocket", "Color List", (0.8, 0.8, 0.2, 0.5))
|
||||
TextListSocket = create_simple_data_socket(
|
||||
"fn_TextListSocket", "Text List", (0.8, 0.8, 0.8, 0.5))
|
||||
SurfaceHookSocket = create_simple_data_socket(
|
||||
"fn_SurfaceHookSocket", "Surface Hook", (0.2, 0.8, 0.2, 1.0))
|
||||
SurfaceHookListSocket = create_simple_data_socket(
|
||||
"fn_SurfaceHookListSocket", "Surface Hook List", (0.2, 0.8, 0.2, 0.5))
|
||||
|
||||
class ExecuteSocket(bpy.types.NodeSocket, BaseSocket):
|
||||
bl_idname = "fn_ExecuteSocket"
|
||||
bl_label = "Control Flow Socket"
|
||||
color = (0.8, 0.2, 0.2, 1)
|
||||
|
||||
class InfluencesSocket(bpy.types.NodeSocket, BaseSocket):
|
||||
bl_idname = "fn_InfluencesSocket"
|
||||
bl_label = "Influences Socket"
|
||||
color = (0.8, 0.8, 0.2, 1)
|
||||
183
release/scripts/startup/nodes/sync.py
Normal file
183
release/scripts/startup/nodes/sync.py
Normal file
@@ -0,0 +1,183 @@
|
||||
import bpy
|
||||
from pprint import pprint
|
||||
from contextlib import contextmanager
|
||||
|
||||
from . base import BaseNode
|
||||
from . tree_data import TreeData
|
||||
from . graph import DirectedGraphBuilder
|
||||
from . function_tree import FunctionTree
|
||||
from . utils.generic import getattr_recursive, setattr_recursive
|
||||
|
||||
_is_syncing = False
|
||||
|
||||
def sync_trees_and_dependent_trees(trees):
|
||||
global _is_syncing
|
||||
if _is_syncing:
|
||||
return
|
||||
if _skip_syncing:
|
||||
return
|
||||
|
||||
_is_syncing = True
|
||||
|
||||
try:
|
||||
for tree in iter_trees_to_sync_in_order(trees):
|
||||
sync_tree(tree)
|
||||
finally:
|
||||
_is_syncing = False
|
||||
|
||||
def sync_tree(tree):
|
||||
rebuild_currently_outdated_nodes(tree)
|
||||
|
||||
tree_data = TreeData(tree)
|
||||
|
||||
tree_changed = run_socket_operators(tree_data)
|
||||
if tree_changed: tree_data = TreeData(tree)
|
||||
|
||||
tree_changed = do_inferencing_and_update_nodes(tree_data)
|
||||
if tree_changed: tree_data = TreeData(tree)
|
||||
|
||||
tree_changed = remove_invalid_links(tree_data)
|
||||
|
||||
# Sync skipping
|
||||
######################################
|
||||
|
||||
_skip_syncing = False
|
||||
|
||||
@contextmanager
|
||||
def skip_syncing():
|
||||
global _skip_syncing
|
||||
last_state = _skip_syncing
|
||||
_skip_syncing = True
|
||||
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
_skip_syncing = last_state
|
||||
|
||||
|
||||
# Tree sync ordering
|
||||
############################################
|
||||
|
||||
def iter_trees_to_sync_in_order(trees):
|
||||
stored_tree_ids = {id(tree) for tree in bpy.data.node_groups}
|
||||
if any(id(tree) not in stored_tree_ids for tree in trees):
|
||||
# can happen after undo or on load
|
||||
return
|
||||
|
||||
dependency_graph = FunctionTree.BuildInvertedCallGraph()
|
||||
all_trees_to_sync = dependency_graph.reachable(trees)
|
||||
trees_in_sync_order = dependency_graph.toposort_partial(all_trees_to_sync)
|
||||
yield from trees_in_sync_order
|
||||
|
||||
|
||||
# Rebuild already outdated nodes
|
||||
############################################
|
||||
|
||||
def rebuild_currently_outdated_nodes(tree):
|
||||
outdated_nodes = list(iter_nodes_with_outdated_sockets(tree))
|
||||
rebuild_nodes_and_try_keep_state(outdated_nodes)
|
||||
|
||||
def iter_nodes_with_outdated_sockets(tree):
|
||||
for node in tree.nodes:
|
||||
if isinstance(node, BaseNode):
|
||||
if not node_matches_current_declaration(node):
|
||||
yield node
|
||||
|
||||
def node_matches_current_declaration(node):
|
||||
from . node_builder import NodeBuilder
|
||||
builder = node.get_node_builder()
|
||||
return builder.matches_sockets()
|
||||
|
||||
|
||||
# Socket Operators
|
||||
############################################
|
||||
|
||||
def run_socket_operators(tree_data):
|
||||
from . sockets import OperatorSocket
|
||||
|
||||
tree_changed = False
|
||||
while True:
|
||||
for link in tree_data.iter_blinks():
|
||||
if isinstance(link.to_socket, OperatorSocket):
|
||||
own_node = link.to_node
|
||||
own_socket = link.to_socket
|
||||
linked_socket = link.from_socket
|
||||
connected_sockets = list(tree_data.iter_connected_origins(own_socket))
|
||||
elif isinstance(link.from_socket, OperatorSocket):
|
||||
own_node = link.from_node
|
||||
own_socket = link.from_socket
|
||||
linked_socket = link.to_socket
|
||||
connected_sockets = list(tree_data.iter_connected_targets(own_socket))
|
||||
else:
|
||||
continue
|
||||
|
||||
tree_data.tree.links.remove(link)
|
||||
decl = own_socket.get_decl(own_node)
|
||||
decl.operator_socket_call(own_socket, linked_socket, connected_sockets)
|
||||
tree_changed = True
|
||||
else:
|
||||
return tree_changed
|
||||
|
||||
|
||||
# Inferencing
|
||||
####################################
|
||||
|
||||
def do_inferencing_and_update_nodes(tree_data):
|
||||
from . inferencing import get_inferencing_decisions
|
||||
|
||||
decisions = get_inferencing_decisions(tree_data)
|
||||
|
||||
nodes_to_rebuild = set()
|
||||
|
||||
for decision_id, value in decisions.items():
|
||||
if getattr_recursive(decision_id.node, decision_id.prop_name) != value:
|
||||
setattr_recursive(decision_id.node, decision_id.prop_name, value)
|
||||
nodes_to_rebuild.add(decision_id.node)
|
||||
|
||||
rebuild_nodes_and_try_keep_state(nodes_to_rebuild)
|
||||
|
||||
tree_changed = len(nodes_to_rebuild) > 0
|
||||
return tree_changed
|
||||
|
||||
|
||||
# Remove Invalid Links
|
||||
####################################
|
||||
|
||||
def remove_invalid_links(tree_data):
|
||||
links_to_remove = set()
|
||||
for from_socket, to_socket in tree_data.iter_connections():
|
||||
if not is_link_valid(tree_data, from_socket, to_socket):
|
||||
links_to_remove.update(tree_data.iter_incident_links(to_socket))
|
||||
|
||||
tree_changed = len(links_to_remove) > 0
|
||||
|
||||
tree = tree_data.tree
|
||||
for link in links_to_remove:
|
||||
tree.links.remove(link)
|
||||
|
||||
return tree_changed
|
||||
|
||||
def is_link_valid(tree_data, from_socket, to_socket):
|
||||
from . types import type_infos
|
||||
from . base import DataSocket
|
||||
|
||||
is_data_src = isinstance(from_socket, DataSocket)
|
||||
is_data_dst = isinstance(to_socket, DataSocket)
|
||||
|
||||
if is_data_src != is_data_dst:
|
||||
return False
|
||||
|
||||
if is_data_src and is_data_dst:
|
||||
from_type = from_socket.data_type
|
||||
to_type = to_socket.data_type
|
||||
return type_infos.is_link_allowed(from_type, to_type)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
# Utils
|
||||
######################################
|
||||
|
||||
def rebuild_nodes_and_try_keep_state(nodes):
|
||||
for node in nodes:
|
||||
node.rebuild()
|
||||
133
release/scripts/startup/nodes/tree_data.py
Normal file
133
release/scripts/startup/nodes/tree_data.py
Normal file
@@ -0,0 +1,133 @@
|
||||
from collections import defaultdict
|
||||
from . base import BaseNode
|
||||
|
||||
class TreeData:
|
||||
def __init__(self, tree):
|
||||
self.tree = tree
|
||||
self.links_mapping = find_direct_links_mapping(tree)
|
||||
self.node_by_socket = get_node_by_socket_mapping(tree)
|
||||
self.connections_mapping = find_links_following_reroutes(self.links_mapping, self.node_by_socket)
|
||||
self.link_by_sockets = get_link_by_sockets_mapping(tree)
|
||||
|
||||
def iter_nodes(self):
|
||||
for node in self.tree.nodes:
|
||||
if isinstance(node, BaseNode):
|
||||
yield node
|
||||
|
||||
def iter_blinks(self):
|
||||
yield from self.tree.links
|
||||
|
||||
def iter_connections(self):
|
||||
for socket, others in self.connections_mapping.items():
|
||||
if socket.is_output:
|
||||
continue
|
||||
for other in others:
|
||||
yield other, socket
|
||||
|
||||
def get_node(self, socket):
|
||||
return self.node_by_socket[socket]
|
||||
|
||||
def iter_connected_origins(self, socket):
|
||||
node = self.get_node(socket)
|
||||
if is_reroute(node):
|
||||
socket = node.inputs[0]
|
||||
for other_socket in self.links_mapping[socket]:
|
||||
yield from self.iter_connected_origins(other_socket)
|
||||
else:
|
||||
if socket.is_output:
|
||||
yield socket
|
||||
else:
|
||||
yield from self.iter_connected_sockets(socket)
|
||||
|
||||
def iter_connected_targets(self, socket):
|
||||
node = self.get_node(socket)
|
||||
if is_reroute(node):
|
||||
socket = node.outputs[0]
|
||||
for other_socket in self.links_mapping[socket]:
|
||||
yield from self.iter_connected_targets(other_socket)
|
||||
else:
|
||||
if socket.is_output:
|
||||
yield from self.iter_connected_sockets(socket)
|
||||
else:
|
||||
yield socket
|
||||
|
||||
def iter_connected_sockets(self, socket):
|
||||
yield from self.connections_mapping[socket]
|
||||
|
||||
def iter_connected_sockets_with_nodes(self, socket):
|
||||
for other_socket in self.iter_connected_sockets(socket):
|
||||
other_node = self.get_node(other_socket)
|
||||
yield other_node, other_socket
|
||||
|
||||
def try_get_origin_with_node(self, socket):
|
||||
linked_sockets = self.connections_mapping[socket]
|
||||
amount = len(linked_sockets)
|
||||
if amount == 0:
|
||||
return None, None
|
||||
elif amount == 1:
|
||||
origin_socket = next(iter(linked_sockets))
|
||||
origin_node = self.get_node(origin_socket)
|
||||
return origin_node, origin_socket
|
||||
else:
|
||||
assert False
|
||||
|
||||
def iter_incident_links(self, socket):
|
||||
if socket.is_output:
|
||||
for other_socket in self.links_mapping[socket]:
|
||||
yield self.link_by_sockets[(socket, other_socket)]
|
||||
else:
|
||||
for other_socket in self.links_mapping[socket]:
|
||||
yield self.link_by_sockets[(other_socket, socket)]
|
||||
|
||||
def find_direct_links_mapping(tree):
|
||||
direct_links = defaultdict(set)
|
||||
for link in tree.links:
|
||||
direct_links[link.from_socket].add(link.to_socket)
|
||||
direct_links[link.to_socket].add(link.from_socket)
|
||||
return dict(direct_links)
|
||||
|
||||
def get_node_by_socket_mapping(tree):
|
||||
node_by_socket = dict()
|
||||
for node in tree.nodes:
|
||||
for socket in node.inputs:
|
||||
node_by_socket[socket] = node
|
||||
for socket in node.outputs:
|
||||
node_by_socket[socket] = node
|
||||
return node_by_socket
|
||||
|
||||
def get_link_by_sockets_mapping(tree):
|
||||
link_by_sockets = dict()
|
||||
for link in tree.links:
|
||||
link_by_sockets[(link.from_socket, link.to_socket)] = link
|
||||
return link_by_sockets
|
||||
|
||||
def find_links_following_reroutes(direct_links, node_by_socket):
|
||||
links = defaultdict(set)
|
||||
for socket, direct_linked_sockets in direct_links.items():
|
||||
node = node_by_socket[socket]
|
||||
if socket.is_output:
|
||||
# handle every link only once
|
||||
continue
|
||||
if is_reroute(node):
|
||||
continue
|
||||
|
||||
for other_socket in direct_linked_sockets:
|
||||
for origin_socket in iter_non_reroute_outputs(direct_links, node_by_socket, other_socket):
|
||||
links[socket].add(origin_socket)
|
||||
links[origin_socket].add(socket)
|
||||
return links
|
||||
|
||||
def iter_non_reroute_outputs(direct_links, node_by_socket, socket):
|
||||
assert socket.is_output
|
||||
|
||||
node = node_by_socket[socket]
|
||||
if is_reroute(node):
|
||||
input_socket = node.inputs[0]
|
||||
if input_socket in direct_links:
|
||||
for origin_socket in direct_links[input_socket]:
|
||||
yield from iter_non_reroute_outputs(direct_links, node_by_socket, origin_socket)
|
||||
else:
|
||||
yield socket
|
||||
|
||||
def is_reroute(node):
|
||||
return node.bl_idname == "NodeReroute"
|
||||
17
release/scripts/startup/nodes/tree_panel.py
Normal file
17
release/scripts/startup/nodes/tree_panel.py
Normal file
@@ -0,0 +1,17 @@
|
||||
import bpy
|
||||
from . ui import NodeSidebarPanel
|
||||
from . base import BaseTree
|
||||
|
||||
class TreePanel(bpy.types.Panel, NodeSidebarPanel):
|
||||
bl_idname = "FN_PT_tree_panel"
|
||||
bl_label = "Tree"
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
try: return isinstance(context.space_data.edit_tree, BaseTree)
|
||||
except: return False
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
tree = context.space_data.edit_tree
|
||||
16
release/scripts/startup/nodes/types.py
Normal file
16
release/scripts/startup/nodes/types.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from . import sockets as s
|
||||
from . types_base import DataTypesInfo
|
||||
|
||||
type_infos = DataTypesInfo()
|
||||
|
||||
type_infos.insert_data_type(s.FloatSocket, s.FloatListSocket)
|
||||
type_infos.insert_data_type(s.VectorSocket, s.VectorListSocket)
|
||||
type_infos.insert_data_type(s.IntegerSocket, s.IntegerListSocket)
|
||||
type_infos.insert_data_type(s.BooleanSocket, s.BooleanListSocket)
|
||||
type_infos.insert_data_type(s.ObjectSocket, s.ObjectListSocket)
|
||||
type_infos.insert_data_type(s.ImageSocket, s.ImageListSocket)
|
||||
type_infos.insert_data_type(s.ColorSocket, s.ColorListSocket)
|
||||
type_infos.insert_data_type(s.TextSocket, s.TextListSocket)
|
||||
type_infos.insert_data_type(s.SurfaceHookSocket, s.SurfaceHookListSocket)
|
||||
|
||||
type_infos.insert_conversion_group(["Boolean", "Integer", "Float"])
|
||||
164
release/scripts/startup/nodes/types_base.py
Normal file
164
release/scripts/startup/nodes/types_base.py
Normal file
@@ -0,0 +1,164 @@
|
||||
import itertools
|
||||
from collections import namedtuple
|
||||
|
||||
'''
|
||||
Type Rules
|
||||
==========
|
||||
|
||||
A -> B means, Type A can be converted to type B implicitely.
|
||||
A -!> B means, Type A cannot be converted to type B implicitely.
|
||||
A_List is the type that contains a list of elements of type A
|
||||
|
||||
Iff T1 -> T2, then T1_List -> T2_List.
|
||||
T -> T_List.
|
||||
T_List -!> T.
|
||||
|
||||
Types always come in pairs: T and T_List.
|
||||
There are no lists of lists.
|
||||
|
||||
A type can be in zero or one conversion group.
|
||||
Every type in this group can be converted to any other implicitely.
|
||||
The types within a group are ordered by their "rank".
|
||||
When two types with different rank are used in one expression,
|
||||
the type with lower rank is converted to the other.
|
||||
'''
|
||||
|
||||
# Type Info Container
|
||||
#####################################
|
||||
|
||||
ImplicitConversion = namedtuple("ImplicitConversion", ("from_type", "to_type"))
|
||||
|
||||
class DataTypesInfo:
|
||||
def __init__(self):
|
||||
self.data_types = set()
|
||||
self.cls_by_data_type = dict()
|
||||
self.list_by_base = dict()
|
||||
self.base_by_list = dict()
|
||||
self.unidirectional_conversions = set()
|
||||
self.conversion_groups = dict()
|
||||
self.all_implicit_conversions = set()
|
||||
|
||||
|
||||
# Insert New Information
|
||||
#############################
|
||||
|
||||
def insert_data_type(self, base_socket_cls, list_socket_cls):
|
||||
base_type = base_socket_cls.data_type
|
||||
list_type = list_socket_cls.data_type
|
||||
|
||||
assert base_type not in self.data_types
|
||||
assert list_type not in self.data_types
|
||||
|
||||
self.data_types.add(base_type)
|
||||
self.data_types.add(list_type)
|
||||
self.list_by_base[base_type] = list_type
|
||||
self.base_by_list[list_type] = base_type
|
||||
self.cls_by_data_type[base_type] = base_socket_cls
|
||||
self.cls_by_data_type[list_type] = list_socket_cls
|
||||
|
||||
self.all_implicit_conversions.add(ImplicitConversion(base_type, list_type))
|
||||
|
||||
def insert_conversion_group(self, types_by_rank):
|
||||
'''lowest rank comes first'''
|
||||
|
||||
for data_type in types_by_rank:
|
||||
assert self.is_data_type(data_type)
|
||||
assert self.is_base(data_type)
|
||||
assert data_type not in self.conversion_groups
|
||||
|
||||
group = tuple(types_by_rank)
|
||||
for data_type in types_by_rank:
|
||||
self.conversion_groups[data_type] = group
|
||||
|
||||
for from_base_type, to_base_type in itertools.combinations(group, 2):
|
||||
from_list_type = self.to_list(from_base_type)
|
||||
to_list_type = self.to_list(to_base_type)
|
||||
self.all_implicit_conversions.add(ImplicitConversion(from_base_type, to_base_type))
|
||||
self.all_implicit_conversions.add(ImplicitConversion(to_base_type, from_base_type))
|
||||
self.all_implicit_conversions.add(ImplicitConversion(from_list_type, to_list_type))
|
||||
self.all_implicit_conversions.add(ImplicitConversion(to_list_type, from_list_type))
|
||||
|
||||
def insert_unidirectional_conversion(self, from_type, to_type):
|
||||
assert self.is_data_type(from_type)
|
||||
assert self.is_data_type(to_type)
|
||||
assert self.is_base(from_type)
|
||||
assert self.is_base(to_type)
|
||||
|
||||
base_conversion = ImplicitConversion(from_type, to_type)
|
||||
assert base_conversion not in self.implicit_conversions
|
||||
self.implicit_conversions.add(base_conversion)
|
||||
self.all_implicit_conversions.add(base_conversion)
|
||||
|
||||
list_conversion = ImplicitConversion(
|
||||
self.to_list(from_type), self.to_list(to_type))
|
||||
assert list_conversion not in self.implicit_conversions
|
||||
self.implicit_conversions.add(list_conversion)
|
||||
self.all_implicit_conversions.add(list_conversion)
|
||||
|
||||
|
||||
# Query Information
|
||||
##########################
|
||||
|
||||
def is_data_type(self, data_type):
|
||||
return data_type in self.data_types
|
||||
|
||||
def is_base(self, data_type):
|
||||
return data_type in self.list_by_base
|
||||
|
||||
def is_list(self, data_type):
|
||||
return data_type in self.base_by_list
|
||||
|
||||
def to_list(self, data_type):
|
||||
assert self.is_base(data_type)
|
||||
return self.list_by_base[data_type]
|
||||
|
||||
def to_base(self, data_type):
|
||||
assert self.is_list(data_type)
|
||||
return self.base_by_list[data_type]
|
||||
|
||||
def get_data_type_items(self):
|
||||
items = []
|
||||
for data_type in self.data_types:
|
||||
items.append((data_type, data_type, ""))
|
||||
return items
|
||||
|
||||
def get_base_type_items(self):
|
||||
items = []
|
||||
for data_type in self.iter_base_types():
|
||||
items.append((data_type, data_type, ""))
|
||||
return items
|
||||
|
||||
def get_data_type_items_cb(self):
|
||||
def callback(_1, _2):
|
||||
return self.get_data_type_items()
|
||||
return callback
|
||||
|
||||
def get_socket_color(self, data_type):
|
||||
builder = self.to_builder(data_type)
|
||||
return builder.get_color()
|
||||
|
||||
def is_link_allowed(self, from_type, to_type):
|
||||
assert self.is_data_type(from_type)
|
||||
assert self.is_data_type(to_type)
|
||||
|
||||
if from_type == to_type:
|
||||
return True
|
||||
else:
|
||||
return self.is_implicitly_convertable(from_type, to_type)
|
||||
|
||||
def is_implicitly_convertable(self, from_type, to_type):
|
||||
return ImplicitConversion(from_type, to_type) in self.all_implicit_conversions
|
||||
|
||||
def iter_list_types(self):
|
||||
yield from self.base_by_list.keys()
|
||||
|
||||
def iter_base_types(self):
|
||||
yield from self.list_by_base.keys()
|
||||
|
||||
# Build
|
||||
##########################
|
||||
|
||||
def build(self, data_type, node_sockets, name, identifier):
|
||||
idname = self.cls_by_data_type[data_type].bl_idname
|
||||
socket = node_sockets.new(idname, name, identifier=identifier)
|
||||
return socket
|
||||
6
release/scripts/startup/nodes/ui.py
Normal file
6
release/scripts/startup/nodes/ui.py
Normal file
@@ -0,0 +1,6 @@
|
||||
import bpy
|
||||
|
||||
class NodeSidebarPanel:
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = "Node"
|
||||
20
release/scripts/startup/nodes/utils/enum_items_cache.py
Normal file
20
release/scripts/startup/nodes/utils/enum_items_cache.py
Normal file
@@ -0,0 +1,20 @@
|
||||
import functools
|
||||
from collections import defaultdict
|
||||
|
||||
cached_item_tuples_by_hash = defaultdict(list)
|
||||
|
||||
def cache_enum_items(items_cb):
|
||||
|
||||
@functools.wraps(items_cb)
|
||||
def wrapper(self, context):
|
||||
item_tuples = tuple(items_cb(self, context))
|
||||
item_tuples_hash = hash(item_tuples)
|
||||
|
||||
for cached_item_tuple in cached_item_tuples_by_hash[item_tuples_hash]:
|
||||
if cached_item_tuple == item_tuples:
|
||||
return cached_item_tuple
|
||||
else:
|
||||
cached_item_tuples_by_hash[item_tuples_hash].append(item_tuples)
|
||||
return item_tuples
|
||||
|
||||
return wrapper
|
||||
21
release/scripts/startup/nodes/utils/generic.py
Normal file
21
release/scripts/startup/nodes/utils/generic.py
Normal file
@@ -0,0 +1,21 @@
|
||||
import string
|
||||
import random
|
||||
|
||||
def iter_subclasses_recursive(cls):
|
||||
for sub in cls.__subclasses__():
|
||||
yield sub
|
||||
yield from iter_subclasses_recursive(sub)
|
||||
|
||||
def getattr_recursive(obj, name: str):
|
||||
if "." not in name and "[" not in name:
|
||||
return getattr(obj, name)
|
||||
else:
|
||||
# TODO: implement without eval
|
||||
return eval("obj." + name, globals(), locals())
|
||||
|
||||
def setattr_recursive(obj, name: str, value):
|
||||
if "." not in name and "[" not in name:
|
||||
setattr(obj, name, value)
|
||||
else:
|
||||
# TODO: implement without exec
|
||||
exec("obj." + name + " = value", globals(), locals())
|
||||
19
release/scripts/startup/nodes/utils/graph.py
Normal file
19
release/scripts/startup/nodes/utils/graph.py
Normal file
@@ -0,0 +1,19 @@
|
||||
def iter_connected_components(nodes: set, links: dict):
|
||||
nodes = set(nodes)
|
||||
while len(nodes) > 0:
|
||||
start_node = next(iter(nodes))
|
||||
component = depth_first_search(start_node, links)
|
||||
yield component
|
||||
nodes -= component
|
||||
|
||||
def depth_first_search(start_node, links):
|
||||
result = set()
|
||||
found = set()
|
||||
found.add(start_node)
|
||||
while len(found) > 0:
|
||||
node = found.pop()
|
||||
result.add(node)
|
||||
for linked_node in links[node]:
|
||||
if linked_node not in result:
|
||||
found.add(linked_node)
|
||||
return result
|
||||
38
release/scripts/startup/nodes/utils/pie_menu_helper.py
Normal file
38
release/scripts/startup/nodes/utils/pie_menu_helper.py
Normal file
@@ -0,0 +1,38 @@
|
||||
class PieMenuHelper:
|
||||
def draw(self, context):
|
||||
pie = self.layout.menu_pie()
|
||||
self.draw_left(pie)
|
||||
self.draw_right(pie)
|
||||
self.draw_bottom(pie)
|
||||
self.draw_top(pie)
|
||||
self.draw_top_left(pie)
|
||||
self.draw_top_right(pie)
|
||||
self.draw_bottom_left(pie)
|
||||
self.draw_bottom_right(pie)
|
||||
|
||||
def draw_left(self, layout):
|
||||
self.empty(layout)
|
||||
|
||||
def draw_right(self, layout):
|
||||
self.empty(layout)
|
||||
|
||||
def draw_bottom(self, layout):
|
||||
self.empty(layout)
|
||||
|
||||
def draw_top(self, layout):
|
||||
self.empty(layout)
|
||||
|
||||
def draw_top_left(self, layout):
|
||||
self.empty(layout)
|
||||
|
||||
def draw_top_right(self, layout):
|
||||
self.empty(layout)
|
||||
|
||||
def draw_bottom_left(self, layout):
|
||||
self.empty(layout)
|
||||
|
||||
def draw_bottom_right(self, layout):
|
||||
self.empty(layout)
|
||||
|
||||
def empty(self, layout, text=""):
|
||||
layout.row().label(text=text)
|
||||
@@ -113,6 +113,8 @@ add_subdirectory(nodes)
|
||||
add_subdirectory(modifiers)
|
||||
add_subdirectory(gpencil_modifiers)
|
||||
add_subdirectory(shader_fx)
|
||||
add_subdirectory(functions)
|
||||
add_subdirectory(simulations)
|
||||
add_subdirectory(io)
|
||||
add_subdirectory(makesdna)
|
||||
add_subdirectory(makesrna)
|
||||
|
||||
@@ -257,6 +257,8 @@ struct FCurve *iter_step_fcurve(struct FCurve *fcu_iter, const char rna_path[]);
|
||||
struct FCurve *id_data_find_fcurve(
|
||||
ID *id, void *data, struct StructRNA *type, const char *prop_name, int index, bool *r_driven);
|
||||
|
||||
void *get_driver_variable_function(struct DriverVar *dvar);
|
||||
|
||||
/* Get list of LinkData's containing pointers to the F-Curves which control the types of data
|
||||
* indicated
|
||||
* e.g. numMatches = list_find_data_fcurves(matches, &act->curves, "pose.bones[", "MyFancyBone");
|
||||
|
||||
34
source/blender/blenkernel/BKE_id_data_cache.h
Normal file
34
source/blender/blenkernel/BKE_id_data_cache.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef __BKE_ID_DATA_CACHE_H__
|
||||
#define __BKE_ID_DATA_CACHE_H__
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "BLI_kdopbvh.h"
|
||||
#include "BLI_kdtree.h"
|
||||
#include "BLI_map.h"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "BKE_bvhutils.h"
|
||||
|
||||
namespace BKE {
|
||||
|
||||
using BLI::Map;
|
||||
|
||||
class IDDataCache {
|
||||
private:
|
||||
mutable Map<Object *, BVHTreeFromMesh *> m_bvh_trees;
|
||||
mutable std::mutex m_bvt_trees_mutex;
|
||||
|
||||
public:
|
||||
IDDataCache() = default;
|
||||
~IDDataCache();
|
||||
|
||||
BVHTreeFromMesh *get_bvh_tree(Object *object) const;
|
||||
};
|
||||
|
||||
} // namespace BKE
|
||||
|
||||
#endif /* __BKE_ID_DATA_CACHE_H__ */
|
||||
108
source/blender/blenkernel/BKE_id_handle.h
Normal file
108
source/blender/blenkernel/BKE_id_handle.h
Normal file
@@ -0,0 +1,108 @@
|
||||
#ifndef __BKE_ID_HANDLE_H__
|
||||
#define __BKE_ID_HANDLE_H__
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLI_map.h"
|
||||
|
||||
extern "C" {
|
||||
struct ID;
|
||||
struct Object;
|
||||
struct Image;
|
||||
}
|
||||
|
||||
namespace BKE {
|
||||
|
||||
using BLI::Map;
|
||||
|
||||
/**
|
||||
* This is a weak reference to an ID data-block. It does not contain a pointer to the actual data.
|
||||
* It can happen that the IDHandle references data, that does not exist anymore. The handle does
|
||||
* not know that.
|
||||
*/
|
||||
class IDHandle {
|
||||
private:
|
||||
uint32_t m_identifier;
|
||||
|
||||
public:
|
||||
IDHandle() : m_identifier((uint32_t)-1)
|
||||
{
|
||||
}
|
||||
|
||||
IDHandle(struct ID *id);
|
||||
|
||||
friend bool operator==(IDHandle a, IDHandle b)
|
||||
{
|
||||
return a.m_identifier == b.m_identifier;
|
||||
}
|
||||
|
||||
friend bool operator!=(IDHandle a, IDHandle b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
uint32_t internal_identifier() const
|
||||
{
|
||||
return m_identifier;
|
||||
}
|
||||
};
|
||||
|
||||
class ObjectIDHandle : public IDHandle {
|
||||
public:
|
||||
ObjectIDHandle() : IDHandle()
|
||||
{
|
||||
}
|
||||
|
||||
ObjectIDHandle(struct Object *object);
|
||||
};
|
||||
|
||||
class ImageIDHandle : public IDHandle {
|
||||
public:
|
||||
ImageIDHandle() : IDHandle()
|
||||
{
|
||||
}
|
||||
|
||||
ImageIDHandle(struct Image *image);
|
||||
};
|
||||
|
||||
class IDHandleLookup {
|
||||
private:
|
||||
Map<IDHandle, ID *> m_handle_to_id_map;
|
||||
|
||||
public:
|
||||
void add(ID &id)
|
||||
{
|
||||
IDHandle handle(&id);
|
||||
m_handle_to_id_map.add(handle, &id);
|
||||
}
|
||||
|
||||
ID *lookup(IDHandle handle) const
|
||||
{
|
||||
return m_handle_to_id_map.lookup_default(handle, nullptr);
|
||||
}
|
||||
|
||||
struct Object *lookup(ObjectIDHandle handle) const
|
||||
{
|
||||
return reinterpret_cast<struct Object *>(this->lookup((IDHandle)handle));
|
||||
}
|
||||
|
||||
struct Image *lookup(ImageIDHandle handle) const
|
||||
{
|
||||
return reinterpret_cast<struct Image *>(this->lookup((IDHandle)handle));
|
||||
}
|
||||
|
||||
static const IDHandleLookup &Empty();
|
||||
};
|
||||
|
||||
} // namespace BKE
|
||||
|
||||
namespace BLI {
|
||||
template<> struct DefaultHash<BKE::IDHandle> {
|
||||
uint32_t operator()(const BKE::IDHandle &value) const
|
||||
{
|
||||
return value.internal_identifier();
|
||||
}
|
||||
};
|
||||
} // namespace BLI
|
||||
|
||||
#endif /* __BKE_ID_HANDLE_H__ */
|
||||
97
source/blender/blenkernel/BKE_surface_hook.h
Normal file
97
source/blender/blenkernel/BKE_surface_hook.h
Normal file
@@ -0,0 +1,97 @@
|
||||
#ifndef __BKE_SURFACE_HOOK_H__
|
||||
#define __BKE_SURFACE_HOOK_H__
|
||||
|
||||
#include "BLI_float3.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_id_handle.h"
|
||||
|
||||
namespace BKE {
|
||||
|
||||
using BLI::float3;
|
||||
|
||||
namespace SurfaceHookType {
|
||||
enum Enum {
|
||||
None,
|
||||
MeshObject,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* References a point on a surface. If the surface moves, the point moves with it.
|
||||
*/
|
||||
class SurfaceHook {
|
||||
private:
|
||||
SurfaceHookType::Enum m_type;
|
||||
|
||||
/**
|
||||
* Used to identify the object if m_type is MeshObject.
|
||||
*/
|
||||
ObjectIDHandle m_object_handle;
|
||||
|
||||
/* Index of the triangle that contains the referenced location. */
|
||||
uint32_t m_triangle_index;
|
||||
|
||||
/* Barycentric coordinates of the referenced location inside the triangle. */
|
||||
float3 m_bary_coords;
|
||||
|
||||
public:
|
||||
SurfaceHook() : m_type(SurfaceHookType::None)
|
||||
{
|
||||
}
|
||||
|
||||
SurfaceHook(ObjectIDHandle object_handle, uint32_t triangle_index, float3 bary_coords)
|
||||
: m_type(SurfaceHookType::MeshObject),
|
||||
m_object_handle(object_handle),
|
||||
m_triangle_index(triangle_index),
|
||||
m_bary_coords(bary_coords)
|
||||
{
|
||||
}
|
||||
|
||||
SurfaceHookType::Enum type() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
bool is_valid() const
|
||||
{
|
||||
return m_type != SurfaceHookType::None;
|
||||
}
|
||||
|
||||
ObjectIDHandle object_handle() const
|
||||
{
|
||||
BLI_assert(m_type == SurfaceHookType::MeshObject);
|
||||
return m_object_handle;
|
||||
}
|
||||
|
||||
uint32_t triangle_index() const
|
||||
{
|
||||
BLI_assert(m_type == SurfaceHookType::MeshObject);
|
||||
return m_triangle_index;
|
||||
}
|
||||
|
||||
float3 bary_coords() const
|
||||
{
|
||||
BLI_assert(m_type == SurfaceHookType::MeshObject);
|
||||
return m_bary_coords;
|
||||
}
|
||||
|
||||
static bool on_same_surface(const SurfaceHook &a, const SurfaceHook &b)
|
||||
{
|
||||
if (a.type() != b.type()) {
|
||||
return false;
|
||||
}
|
||||
switch (a.type()) {
|
||||
case BKE::SurfaceHookType::None:
|
||||
return true;
|
||||
case BKE::SurfaceHookType::MeshObject:
|
||||
return a.object_handle() == b.object_handle();
|
||||
}
|
||||
BLI_assert(false);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace BKE
|
||||
|
||||
#endif /* __BKE_SURFACE_HOOK_H__ */
|
||||
367
source/blender/blenkernel/BKE_virtual_node_tree.h
Normal file
367
source/blender/blenkernel/BKE_virtual_node_tree.h
Normal file
@@ -0,0 +1,367 @@
|
||||
#ifndef __BKE_VIRTUAL_NODE_TREE_H__
|
||||
#define __BKE_VIRTUAL_NODE_TREE_H__
|
||||
|
||||
#include "BLI_array_cxx.h"
|
||||
#include "BLI_linear_allocated_vector.h"
|
||||
#include "BLI_resource_collector.h"
|
||||
#include "BLI_string_map.h"
|
||||
#include "BLI_string_multi_map.h"
|
||||
#include "BLI_string_ref.h"
|
||||
#include "BLI_utility_mixins.h"
|
||||
#include "BLI_vector.h"
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
|
||||
namespace BKE {
|
||||
|
||||
using BLI::Array;
|
||||
using BLI::ArrayRef;
|
||||
using BLI::LinearAllocatedVector;
|
||||
using BLI::ResourceCollector;
|
||||
using BLI::StringMap;
|
||||
using BLI::StringMultiMap;
|
||||
using BLI::StringRef;
|
||||
using BLI::StringRefNull;
|
||||
using BLI::Vector;
|
||||
|
||||
class VSocket;
|
||||
class VInputSocket;
|
||||
class VOutputSocket;
|
||||
class VNode;
|
||||
class VirtualNodeTree;
|
||||
|
||||
/* Virtual Node Tree declarations
|
||||
******************************************/
|
||||
|
||||
class VSocket : BLI::NonCopyable, BLI::NonMovable {
|
||||
protected:
|
||||
LinearAllocatedVector<VSocket *> m_linked_sockets;
|
||||
LinearAllocatedVector<VSocket *> m_directly_linked_sockets;
|
||||
VNode *m_node;
|
||||
bool m_is_input;
|
||||
bNodeSocket *m_bsocket;
|
||||
uint m_id;
|
||||
PointerRNA m_rna;
|
||||
uint m_index;
|
||||
|
||||
friend VirtualNodeTree;
|
||||
|
||||
public:
|
||||
ArrayRef<const VSocket *> linked_sockets() const;
|
||||
ArrayRef<const VSocket *> directly_linked_sockets() const;
|
||||
|
||||
const VNode &node() const;
|
||||
const VirtualNodeTree &tree() const;
|
||||
uint id() const;
|
||||
|
||||
uint index() const;
|
||||
|
||||
bool is_input() const;
|
||||
bool is_output() const;
|
||||
|
||||
const VSocket &as_base() const;
|
||||
const VInputSocket &as_input() const;
|
||||
const VOutputSocket &as_output() const;
|
||||
|
||||
PointerRNA *rna() const;
|
||||
|
||||
StringRefNull idname() const;
|
||||
StringRefNull name() const;
|
||||
|
||||
bool is_linked() const;
|
||||
|
||||
bNodeSocket *bsocket() const;
|
||||
bNodeTree *btree() const;
|
||||
};
|
||||
|
||||
class VInputSocket final : public VSocket {
|
||||
public:
|
||||
ArrayRef<const VOutputSocket *> linked_sockets() const;
|
||||
ArrayRef<const VOutputSocket *> directly_linked_sockets() const;
|
||||
};
|
||||
|
||||
class VOutputSocket final : public VSocket {
|
||||
public:
|
||||
ArrayRef<const VInputSocket *> linked_sockets() const;
|
||||
ArrayRef<const VInputSocket *> directly_linked_sockets() const;
|
||||
};
|
||||
|
||||
class VNode : BLI::NonCopyable, BLI::NonMovable {
|
||||
private:
|
||||
VirtualNodeTree *m_vtree;
|
||||
LinearAllocatedVector<VInputSocket *> m_inputs;
|
||||
LinearAllocatedVector<VOutputSocket *> m_outputs;
|
||||
bNode *m_bnode;
|
||||
uint m_id;
|
||||
PointerRNA m_rna;
|
||||
|
||||
friend VirtualNodeTree;
|
||||
|
||||
public:
|
||||
const VirtualNodeTree &tree() const;
|
||||
|
||||
ArrayRef<const VInputSocket *> inputs() const;
|
||||
ArrayRef<const VOutputSocket *> outputs() const;
|
||||
|
||||
PointerRNA *rna() const;
|
||||
StringRefNull idname() const;
|
||||
StringRefNull name() const;
|
||||
|
||||
const VInputSocket &input(uint index) const;
|
||||
const VOutputSocket &output(uint index) const;
|
||||
|
||||
const VInputSocket &input(uint index, StringRef expected_name) const;
|
||||
const VOutputSocket &output(uint index, StringRef expected_name) const;
|
||||
|
||||
bNode *bnode() const;
|
||||
bNodeTree *btree() const;
|
||||
|
||||
uint id() const;
|
||||
};
|
||||
|
||||
class VirtualNodeTree : BLI::NonCopyable, BLI::NonMovable {
|
||||
private:
|
||||
BLI::LinearAllocator<> m_allocator;
|
||||
bNodeTree *m_btree;
|
||||
Vector<VNode *> m_nodes_by_id;
|
||||
Vector<VSocket *> m_sockets_by_id;
|
||||
Vector<VInputSocket *> m_input_sockets;
|
||||
Vector<VOutputSocket *> m_output_sockets;
|
||||
StringMultiMap<VNode *> m_nodes_by_idname;
|
||||
|
||||
public:
|
||||
VirtualNodeTree(bNodeTree *btree);
|
||||
~VirtualNodeTree();
|
||||
|
||||
ArrayRef<const VNode *> nodes() const;
|
||||
ArrayRef<const VNode *> nodes_with_idname(StringRef idname) const;
|
||||
uint socket_count() const;
|
||||
|
||||
ArrayRef<const VSocket *> all_sockets() const;
|
||||
ArrayRef<const VInputSocket *> all_input_sockets() const;
|
||||
ArrayRef<const VOutputSocket *> all_output_sockets() const;
|
||||
|
||||
const VSocket &socket_by_id(uint id) const;
|
||||
|
||||
bNodeTree *btree() const;
|
||||
|
||||
private:
|
||||
void find_targets_skipping_reroutes(VOutputSocket &vsocket,
|
||||
LinearAllocatedVector<VSocket *> &r_targets);
|
||||
};
|
||||
|
||||
/* Virtual Node Tree inline functions
|
||||
****************************************************/
|
||||
|
||||
inline ArrayRef<const VSocket *> VSocket::linked_sockets() const
|
||||
{
|
||||
return m_linked_sockets.as_ref();
|
||||
}
|
||||
|
||||
inline ArrayRef<const VSocket *> VSocket::directly_linked_sockets() const
|
||||
{
|
||||
return m_directly_linked_sockets.as_ref();
|
||||
}
|
||||
|
||||
inline const VirtualNodeTree &VSocket::tree() const
|
||||
{
|
||||
return m_node->tree();
|
||||
}
|
||||
|
||||
inline const VNode &VSocket::node() const
|
||||
{
|
||||
return *m_node;
|
||||
}
|
||||
|
||||
inline uint VSocket::id() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
inline uint VSocket::index() const
|
||||
{
|
||||
return m_index;
|
||||
}
|
||||
|
||||
inline bool VSocket::is_input() const
|
||||
{
|
||||
return m_is_input;
|
||||
}
|
||||
|
||||
inline bool VSocket::is_output() const
|
||||
{
|
||||
return !m_is_input;
|
||||
}
|
||||
|
||||
inline bool VSocket::is_linked() const
|
||||
{
|
||||
return m_linked_sockets.size() > 0;
|
||||
}
|
||||
|
||||
inline const VSocket &VSocket::as_base() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline const VInputSocket &VSocket::as_input() const
|
||||
{
|
||||
BLI_assert(this->is_input());
|
||||
return *(const VInputSocket *)this;
|
||||
}
|
||||
|
||||
inline const VOutputSocket &VSocket::as_output() const
|
||||
{
|
||||
BLI_assert(this->is_output());
|
||||
return *(const VOutputSocket *)this;
|
||||
}
|
||||
|
||||
inline PointerRNA *VSocket::rna() const
|
||||
{
|
||||
return const_cast<PointerRNA *>(&m_rna);
|
||||
}
|
||||
|
||||
inline StringRefNull VSocket::idname() const
|
||||
{
|
||||
return m_bsocket->idname;
|
||||
}
|
||||
|
||||
inline StringRefNull VSocket::name() const
|
||||
{
|
||||
return m_bsocket->name;
|
||||
}
|
||||
|
||||
inline bNodeSocket *VSocket::bsocket() const
|
||||
{
|
||||
return m_bsocket;
|
||||
}
|
||||
|
||||
inline ArrayRef<const VOutputSocket *> VInputSocket::linked_sockets() const
|
||||
{
|
||||
return m_linked_sockets.as_ref().cast<const VOutputSocket *>();
|
||||
;
|
||||
}
|
||||
|
||||
inline ArrayRef<const VOutputSocket *> VInputSocket::directly_linked_sockets() const
|
||||
{
|
||||
return m_directly_linked_sockets.as_ref().cast<const VOutputSocket *>();
|
||||
}
|
||||
|
||||
inline ArrayRef<const VInputSocket *> VOutputSocket::linked_sockets() const
|
||||
{
|
||||
return m_linked_sockets.as_ref().cast<const VInputSocket *>();
|
||||
}
|
||||
|
||||
inline ArrayRef<const VInputSocket *> VOutputSocket::directly_linked_sockets() const
|
||||
{
|
||||
return m_directly_linked_sockets.as_ref().cast<const VInputSocket *>();
|
||||
}
|
||||
|
||||
inline ArrayRef<const VInputSocket *> VNode::inputs() const
|
||||
{
|
||||
return m_inputs.as_ref();
|
||||
}
|
||||
|
||||
inline ArrayRef<const VOutputSocket *> VNode::outputs() const
|
||||
{
|
||||
return m_outputs.as_ref();
|
||||
}
|
||||
|
||||
inline const VirtualNodeTree &VNode::tree() const
|
||||
{
|
||||
return *m_vtree;
|
||||
}
|
||||
|
||||
inline PointerRNA *VNode::rna() const
|
||||
{
|
||||
return const_cast<PointerRNA *>(&m_rna);
|
||||
}
|
||||
|
||||
inline StringRefNull VNode::idname() const
|
||||
{
|
||||
return m_bnode->idname;
|
||||
}
|
||||
|
||||
inline StringRefNull VNode::name() const
|
||||
{
|
||||
return m_bnode->name;
|
||||
}
|
||||
|
||||
inline const VInputSocket &VNode::input(uint index) const
|
||||
{
|
||||
return *m_inputs[index];
|
||||
}
|
||||
|
||||
inline const VOutputSocket &VNode::output(uint index) const
|
||||
{
|
||||
return *m_outputs[index];
|
||||
}
|
||||
|
||||
inline const VInputSocket &VNode::input(uint index, StringRef expected_name) const
|
||||
{
|
||||
BLI_assert(m_inputs[index]->name() == expected_name);
|
||||
UNUSED_VARS_NDEBUG(expected_name);
|
||||
return *m_inputs[index];
|
||||
}
|
||||
|
||||
inline const VOutputSocket &VNode::output(uint index, StringRef expected_name) const
|
||||
{
|
||||
BLI_assert(m_outputs[index]->name() == expected_name);
|
||||
UNUSED_VARS_NDEBUG(expected_name);
|
||||
return *m_outputs[index];
|
||||
}
|
||||
|
||||
inline bNode *VNode::bnode() const
|
||||
{
|
||||
return m_bnode;
|
||||
}
|
||||
|
||||
inline uint VNode::id() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
inline bNodeTree *VirtualNodeTree::btree() const
|
||||
{
|
||||
return m_btree;
|
||||
}
|
||||
|
||||
inline ArrayRef<const VNode *> VirtualNodeTree::nodes() const
|
||||
{
|
||||
return m_nodes_by_id.as_ref();
|
||||
}
|
||||
|
||||
inline ArrayRef<const VNode *> VirtualNodeTree::nodes_with_idname(StringRef idname) const
|
||||
{
|
||||
return m_nodes_by_idname.lookup_default(idname);
|
||||
}
|
||||
|
||||
inline uint VirtualNodeTree::socket_count() const
|
||||
{
|
||||
return m_sockets_by_id.size();
|
||||
}
|
||||
|
||||
inline ArrayRef<const VSocket *> VirtualNodeTree::all_sockets() const
|
||||
{
|
||||
return m_sockets_by_id.as_ref();
|
||||
}
|
||||
|
||||
inline ArrayRef<const VInputSocket *> VirtualNodeTree::all_input_sockets() const
|
||||
{
|
||||
return m_input_sockets.as_ref();
|
||||
}
|
||||
|
||||
inline ArrayRef<const VOutputSocket *> VirtualNodeTree::all_output_sockets() const
|
||||
{
|
||||
return m_output_sockets.as_ref();
|
||||
}
|
||||
|
||||
inline const VSocket &VirtualNodeTree::socket_by_id(uint id) const
|
||||
{
|
||||
return *m_sockets_by_id[id];
|
||||
}
|
||||
|
||||
} // namespace BKE
|
||||
|
||||
#endif /* __BKE_VIRTUAL_NODE_TREE_H__ */
|
||||
@@ -37,6 +37,7 @@ set(INC
|
||||
../nodes
|
||||
../physics
|
||||
../shader_fx
|
||||
../simulations
|
||||
../render/extern/include
|
||||
../../../intern/ghost
|
||||
../../../intern/glew-mx
|
||||
@@ -128,6 +129,8 @@ set(SRC
|
||||
intern/icons_rasterize.c
|
||||
intern/idprop.c
|
||||
intern/idprop_utils.c
|
||||
intern/id_data_cache.cc
|
||||
intern/id_handle.cc
|
||||
intern/idtype.c
|
||||
intern/image.c
|
||||
intern/image_gen.c
|
||||
@@ -230,6 +233,7 @@ set(SRC
|
||||
intern/subdiv_stats.c
|
||||
intern/subdiv_topology.c
|
||||
intern/subsurf_ccg.c
|
||||
intern/surface_hook.cc
|
||||
intern/text.c
|
||||
intern/text_suggestions.c
|
||||
intern/texture.c
|
||||
@@ -243,6 +247,7 @@ set(SRC
|
||||
intern/tracking_util.c
|
||||
intern/undo_system.c
|
||||
intern/unit.c
|
||||
intern/virtual_node_tree.cc
|
||||
intern/volume.cc
|
||||
intern/volume_render.cc
|
||||
intern/workspace.c
|
||||
@@ -309,6 +314,8 @@ set(SRC
|
||||
BKE_hair.h
|
||||
BKE_icons.h
|
||||
BKE_idprop.h
|
||||
BKE_id_data_cache.h
|
||||
BKE_id_handle.h
|
||||
BKE_idtype.h
|
||||
BKE_image.h
|
||||
BKE_ipo.h
|
||||
@@ -374,12 +381,14 @@ set(SRC
|
||||
BKE_subdiv_mesh.h
|
||||
BKE_subdiv_topology.h
|
||||
BKE_subsurf.h
|
||||
BKE_surface_hook.h
|
||||
BKE_text.h
|
||||
BKE_text_suggestions.h
|
||||
BKE_texture.h
|
||||
BKE_tracking.h
|
||||
BKE_undo_system.h
|
||||
BKE_unit.h
|
||||
BKE_virtual_node_tree.h
|
||||
BKE_volume.h
|
||||
BKE_volume_render.h
|
||||
BKE_workspace.h
|
||||
|
||||
@@ -1871,15 +1871,15 @@ static DriverVarTypeInfo dvar_types[MAX_DVAR_TYPES] = {
|
||||
BEGIN_DVAR_TYPEDEF(DVAR_TYPE_ROT_DIFF) dvar_eval_rotDiff, /* eval callback */
|
||||
2, /* number of targets used */
|
||||
{"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */
|
||||
{DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY,
|
||||
DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */
|
||||
{DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY, DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY}
|
||||
/* flags */
|
||||
END_DVAR_TYPEDEF,
|
||||
|
||||
BEGIN_DVAR_TYPEDEF(DVAR_TYPE_LOC_DIFF) dvar_eval_locDiff, /* eval callback */
|
||||
2, /* number of targets used */
|
||||
{"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */
|
||||
{DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY,
|
||||
DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */
|
||||
{DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY, DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY}
|
||||
/* flags */
|
||||
END_DVAR_TYPEDEF,
|
||||
|
||||
BEGIN_DVAR_TYPEDEF(DVAR_TYPE_TRANSFORM_CHAN) dvar_eval_transChan, /* eval callback */
|
||||
@@ -1983,6 +1983,10 @@ void driver_change_variable_type(DriverVar *dvar, int type)
|
||||
if ((flags & DTAR_FLAG_ID_OB_ONLY) || (dtar->idtype == 0)) {
|
||||
dtar->idtype = ID_OB;
|
||||
}
|
||||
|
||||
if (type == DVAR_TYPE_FUNCTION) {
|
||||
dtar->idtype = ID_NT;
|
||||
}
|
||||
}
|
||||
DRIVER_TARGETS_LOOPER_END;
|
||||
}
|
||||
|
||||
31
source/blender/blenkernel/intern/id_data_cache.cc
Normal file
31
source/blender/blenkernel/intern/id_data_cache.cc
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "BKE_id_data_cache.h"
|
||||
|
||||
namespace BKE {
|
||||
|
||||
IDDataCache::~IDDataCache()
|
||||
{
|
||||
for (auto bvhtree : m_bvh_trees.values()) {
|
||||
if (bvhtree != nullptr) {
|
||||
free_bvhtree_from_mesh(bvhtree);
|
||||
delete bvhtree;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BVHTreeFromMesh *IDDataCache::get_bvh_tree(Object *object) const
|
||||
{
|
||||
BLI_assert(object != nullptr);
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_bvt_trees_mutex);
|
||||
|
||||
return m_bvh_trees.lookup_or_add(object, [&]() -> BVHTreeFromMesh * {
|
||||
if (object->type != OB_MESH) {
|
||||
return nullptr;
|
||||
}
|
||||
BVHTreeFromMesh *bvhtree_data = new BVHTreeFromMesh();
|
||||
BKE_bvhtree_from_mesh_get(bvhtree_data, (Mesh *)object->data, BVHTREE_FROM_LOOPTRI, 2);
|
||||
return bvhtree_data;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace BKE
|
||||
33
source/blender/blenkernel/intern/id_handle.cc
Normal file
33
source/blender/blenkernel/intern/id_handle.cc
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "BLI_hash.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_id_handle.h"
|
||||
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_image_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
namespace BKE {
|
||||
|
||||
IDHandle::IDHandle(ID *id)
|
||||
{
|
||||
BLI_assert(id != nullptr);
|
||||
m_identifier = BLI_hash_string(id->name);
|
||||
}
|
||||
|
||||
ObjectIDHandle::ObjectIDHandle(Object *object) : IDHandle(&object->id)
|
||||
{
|
||||
}
|
||||
|
||||
ImageIDHandle::ImageIDHandle(Image *image) : IDHandle(&image->id)
|
||||
{
|
||||
}
|
||||
|
||||
static IDHandleLookup empty_id_handle_lookup;
|
||||
|
||||
const IDHandleLookup &IDHandleLookup::Empty()
|
||||
{
|
||||
return empty_id_handle_lookup;
|
||||
}
|
||||
|
||||
} // namespace BKE
|
||||
@@ -721,7 +721,7 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc)
|
||||
/*
|
||||
* Shrinkwrap Target Surface Project mode
|
||||
*
|
||||
* It uses Newton's method to find a surface location with its
|
||||
* It uses Newton's method to find a Surface Hook with its
|
||||
* smooth normal pointing at the original point.
|
||||
*
|
||||
* The equation system on barycentric weights and normal multiplier:
|
||||
|
||||
7
source/blender/blenkernel/intern/surface_hook.cc
Normal file
7
source/blender/blenkernel/intern/surface_hook.cc
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "BKE_surface_hook.h"
|
||||
|
||||
#include "BLI_hash.h"
|
||||
|
||||
namespace BKE {
|
||||
|
||||
} // namespace BKE
|
||||
115
source/blender/blenkernel/intern/virtual_node_tree.cc
Normal file
115
source/blender/blenkernel/intern/virtual_node_tree.cc
Normal file
@@ -0,0 +1,115 @@
|
||||
#include "BKE_virtual_node_tree.h"
|
||||
|
||||
#include "BLI_listbase_wrapper.h"
|
||||
#include "BLI_map.h"
|
||||
|
||||
namespace BKE {
|
||||
|
||||
using BLI::Map;
|
||||
using BSocketList = BLI::IntrusiveListBaseWrapper<bNodeSocket>;
|
||||
using BNodeList = BLI::IntrusiveListBaseWrapper<bNode>;
|
||||
using BLinkList = BLI::IntrusiveListBaseWrapper<bNodeLink>;
|
||||
|
||||
static bool is_reroute_node(const VNode &vnode)
|
||||
{
|
||||
return vnode.idname() == "NodeReroute";
|
||||
}
|
||||
|
||||
VirtualNodeTree::VirtualNodeTree(bNodeTree *btree) : m_btree(btree)
|
||||
{
|
||||
BLI_assert(btree != nullptr);
|
||||
|
||||
VirtualNodeTree &vtree = *this;
|
||||
|
||||
Map<bNode *, VNode *> node_mapping;
|
||||
|
||||
for (bNode *bnode : BNodeList(btree->nodes)) {
|
||||
VNode &vnode = *vtree.m_allocator.construct<VNode>();
|
||||
|
||||
vnode.m_vtree = &vtree;
|
||||
vnode.m_bnode = bnode;
|
||||
vnode.m_id = vtree.m_nodes_by_id.append_and_get_index(&vnode);
|
||||
RNA_pointer_create(&btree->id, &RNA_Node, bnode, &vnode.m_rna);
|
||||
|
||||
for (bNodeSocket *bsocket : BSocketList(bnode->inputs)) {
|
||||
VInputSocket &vsocket = *vtree.m_allocator.construct<VInputSocket>();
|
||||
|
||||
vsocket.m_node = &vnode;
|
||||
vsocket.m_index = vnode.m_inputs.append_and_get_index(&vsocket, m_allocator);
|
||||
vsocket.m_is_input = true;
|
||||
vsocket.m_bsocket = bsocket;
|
||||
vsocket.m_id = vtree.m_sockets_by_id.append_and_get_index(&vsocket);
|
||||
RNA_pointer_create(&btree->id, &RNA_NodeSocket, bsocket, &vsocket.m_rna);
|
||||
|
||||
vtree.m_input_sockets.append(&vsocket);
|
||||
}
|
||||
|
||||
for (bNodeSocket *bsocket : BSocketList(bnode->outputs)) {
|
||||
VOutputSocket &vsocket = *vtree.m_allocator.construct<VOutputSocket>();
|
||||
|
||||
vsocket.m_node = &vnode;
|
||||
vsocket.m_index = vnode.m_outputs.append_and_get_index(&vsocket, m_allocator);
|
||||
vsocket.m_is_input = false;
|
||||
vsocket.m_bsocket = bsocket;
|
||||
vsocket.m_id = vtree.m_sockets_by_id.append_and_get_index(&vsocket);
|
||||
RNA_pointer_create(&btree->id, &RNA_NodeSocket, bsocket, &vsocket.m_rna);
|
||||
|
||||
vtree.m_output_sockets.append(&vsocket);
|
||||
}
|
||||
|
||||
node_mapping.add_new(bnode, &vnode);
|
||||
}
|
||||
|
||||
for (bNodeLink *blink : BLinkList(btree->links)) {
|
||||
VOutputSocket &from_vsocket =
|
||||
*node_mapping.lookup(blink->fromnode)
|
||||
->m_outputs[BSocketList(blink->fromnode->outputs).index_of(blink->fromsock)];
|
||||
VInputSocket &to_vsocket =
|
||||
*node_mapping.lookup(blink->tonode)
|
||||
->m_inputs[BSocketList(blink->tonode->inputs).index_of(blink->tosock)];
|
||||
|
||||
from_vsocket.m_directly_linked_sockets.append(&to_vsocket, m_allocator);
|
||||
to_vsocket.m_directly_linked_sockets.append(&from_vsocket, m_allocator);
|
||||
}
|
||||
|
||||
for (VOutputSocket *socket : vtree.m_output_sockets) {
|
||||
if (!is_reroute_node(socket->node())) {
|
||||
vtree.find_targets_skipping_reroutes(*socket, socket->m_linked_sockets);
|
||||
for (VSocket *target : socket->m_linked_sockets) {
|
||||
target->m_linked_sockets.append(socket, m_allocator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (VNode *vnode : vtree.m_nodes_by_id) {
|
||||
vtree.m_nodes_by_idname.add(vnode->idname(), vnode);
|
||||
}
|
||||
}
|
||||
|
||||
void VirtualNodeTree::find_targets_skipping_reroutes(VOutputSocket &vsocket,
|
||||
LinearAllocatedVector<VSocket *> &r_targets)
|
||||
{
|
||||
for (VSocket *direct_target : vsocket.m_directly_linked_sockets) {
|
||||
if (is_reroute_node(*direct_target->m_node)) {
|
||||
this->find_targets_skipping_reroutes(*direct_target->m_node->m_outputs[0], r_targets);
|
||||
}
|
||||
else if (!r_targets.contains(direct_target)) {
|
||||
r_targets.append(direct_target, m_allocator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VirtualNodeTree::~VirtualNodeTree()
|
||||
{
|
||||
for (VNode *node : m_nodes_by_id) {
|
||||
node->~VNode();
|
||||
}
|
||||
for (VInputSocket *socket : m_input_sockets) {
|
||||
socket->~VInputSocket();
|
||||
}
|
||||
for (VOutputSocket *socket : m_output_sockets) {
|
||||
socket->~VOutputSocket();
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace BKE
|
||||
83
source/blender/blenlib/BLI_buffer_cache.h
Normal file
83
source/blender/blenlib/BLI_buffer_cache.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#ifndef __BLI_BUFFER_ALLOCATOR_H__
|
||||
#define __BLI_BUFFER_ALLOCATOR_H__
|
||||
|
||||
#include "BLI_vector.h"
|
||||
|
||||
namespace BLI {
|
||||
|
||||
class BufferCache {
|
||||
private:
|
||||
static const int Alignment = 64;
|
||||
|
||||
struct BufferHead {
|
||||
uint buffer_size_in_bytes;
|
||||
|
||||
void *user_ptr()
|
||||
{
|
||||
BLI_STATIC_ASSERT(sizeof(BufferHead) <= Alignment, "");
|
||||
return POINTER_OFFSET(this, Alignment);
|
||||
}
|
||||
|
||||
static BufferHead *FromUserPtr(void *ptr)
|
||||
{
|
||||
return (BufferHead *)POINTER_OFFSET(ptr, -Alignment);
|
||||
}
|
||||
};
|
||||
|
||||
Vector<BufferHead *, 16> m_all_buffers;
|
||||
Vector<BufferHead *, 16> m_cached_buffers;
|
||||
|
||||
public:
|
||||
BufferCache() = default;
|
||||
|
||||
~BufferCache()
|
||||
{
|
||||
assert_same_size(m_cached_buffers, m_all_buffers);
|
||||
|
||||
for (BufferHead *head : m_all_buffers) {
|
||||
MEM_freeN((void *)head);
|
||||
}
|
||||
}
|
||||
|
||||
void *allocate(uint size, uint alignment)
|
||||
{
|
||||
UNUSED_VARS_NDEBUG(alignment);
|
||||
BLI_assert(alignment <= Alignment);
|
||||
|
||||
/* Only use buffer sizes that are a power of two, to make them easier to reuse. */
|
||||
uint padded_size = power_of_2_max_u(size);
|
||||
|
||||
/* Try to use a cached memory buffer. Start searching from the back to prefer buffers that have
|
||||
* been used "just before". */
|
||||
for (int i = m_cached_buffers.size() - 1; i >= 0; i--) {
|
||||
BufferHead *head = m_cached_buffers[i];
|
||||
if (head->buffer_size_in_bytes == padded_size) {
|
||||
void *user_ptr = head->user_ptr();
|
||||
m_cached_buffers.remove_and_reorder(i);
|
||||
return user_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
BufferHead *new_head = (BufferHead *)MEM_mallocN_aligned(
|
||||
padded_size + Alignment, Alignment, "allocate in BufferCache");
|
||||
new_head->buffer_size_in_bytes = padded_size;
|
||||
m_all_buffers.append(new_head);
|
||||
return new_head->user_ptr();
|
||||
}
|
||||
|
||||
void deallocate(void *buffer)
|
||||
{
|
||||
BufferHead *head = BufferHead::FromUserPtr(buffer);
|
||||
BLI_assert(m_all_buffers.contains(head));
|
||||
m_cached_buffers.append(head);
|
||||
}
|
||||
|
||||
void *allocate(uint element_amount, uint element_size, uint alignment)
|
||||
{
|
||||
return this->allocate(element_amount * element_size, alignment);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace BLI
|
||||
|
||||
#endif /* __BLI_BUFFER_ALLOCATOR_H__ */
|
||||
87
source/blender/blenlib/BLI_color.h
Normal file
87
source/blender/blenlib/BLI_color.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __BLI_COLOR_H__
|
||||
#define __BLI_COLOR_H__
|
||||
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
|
||||
#include "BLI_math_color.h"
|
||||
|
||||
namespace BLI {
|
||||
|
||||
struct rgba_f {
|
||||
float r, g, b, a;
|
||||
|
||||
rgba_f() = default;
|
||||
|
||||
rgba_f(float r, float g, float b, float a) : r(r), g(g), b(b), a(a)
|
||||
{
|
||||
}
|
||||
|
||||
operator float *()
|
||||
{
|
||||
return &r;
|
||||
}
|
||||
|
||||
operator std::array<float, 4>()
|
||||
{
|
||||
return {r, g, b, a};
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &stream, rgba_f c)
|
||||
{
|
||||
stream << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")";
|
||||
return stream;
|
||||
}
|
||||
};
|
||||
|
||||
struct rgba_b {
|
||||
uint8_t r, g, b, a;
|
||||
|
||||
rgba_b() = default;
|
||||
|
||||
rgba_b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : r(r), g(g), b(b), a(a)
|
||||
{
|
||||
}
|
||||
|
||||
rgba_b(rgba_f other)
|
||||
{
|
||||
rgba_float_to_uchar(*this, other);
|
||||
}
|
||||
|
||||
operator rgba_f() const
|
||||
{
|
||||
rgba_f result;
|
||||
rgba_uchar_to_float(result, *this);
|
||||
return result;
|
||||
}
|
||||
|
||||
operator uint8_t *()
|
||||
{
|
||||
return &r;
|
||||
}
|
||||
|
||||
operator const uint8_t *() const
|
||||
{
|
||||
return &r;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace BLI
|
||||
|
||||
#endif /* __BLI_COLOR_H__ */
|
||||
274
source/blender/blenlib/BLI_dot_export.h
Normal file
274
source/blender/blenlib/BLI_dot_export.h
Normal file
@@ -0,0 +1,274 @@
|
||||
#ifndef __BLI_DOT_EXPORT_H__
|
||||
#define __BLI_DOT_EXPORT_H__
|
||||
|
||||
/**
|
||||
* Language grammar: https://www.graphviz.org/doc/info/lang.html
|
||||
* Attributes: https://www.graphviz.org/doc/info/attrs.html
|
||||
* Node Shapes: https://www.graphviz.org/doc/info/shapes.html
|
||||
* Preview: https://dreampuf.github.io/GraphvizOnline
|
||||
*/
|
||||
|
||||
#include "BLI_map.h"
|
||||
#include "BLI_optional.h"
|
||||
#include "BLI_set.h"
|
||||
#include "BLI_string_map.h"
|
||||
#include "BLI_utility_mixins.h"
|
||||
#include "BLI_vector.h"
|
||||
|
||||
#include "BLI_dot_export_attribute_enums.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace BLI {
|
||||
namespace DotExport {
|
||||
|
||||
class Graph;
|
||||
class DirectedGraph;
|
||||
class UndirectedGraph;
|
||||
class Node;
|
||||
class NodePort;
|
||||
class DirectedEdge;
|
||||
class UndirectedEdge;
|
||||
class Cluster;
|
||||
class AttributeList;
|
||||
|
||||
class AttributeList {
|
||||
private:
|
||||
Map<std::string, std::string> m_attributes;
|
||||
|
||||
public:
|
||||
void export__as_bracket_list(std::stringstream &ss) const;
|
||||
|
||||
void set(StringRef key, StringRef value)
|
||||
{
|
||||
m_attributes.add_override(key, value);
|
||||
}
|
||||
};
|
||||
|
||||
class Graph {
|
||||
private:
|
||||
AttributeList m_attributes;
|
||||
Vector<std::unique_ptr<Node>> m_nodes;
|
||||
Vector<std::unique_ptr<Cluster>> m_clusters;
|
||||
|
||||
Set<Node *> m_top_level_nodes;
|
||||
Set<Cluster *> m_top_level_clusters;
|
||||
|
||||
friend Cluster;
|
||||
friend Node;
|
||||
|
||||
public:
|
||||
Node &new_node(StringRef label);
|
||||
Cluster &new_cluster(StringRef label = "");
|
||||
|
||||
void export__declare_nodes_and_clusters(std::stringstream &ss) const;
|
||||
|
||||
void set_attribute(StringRef key, StringRef value)
|
||||
{
|
||||
m_attributes.set(key, value);
|
||||
}
|
||||
|
||||
void set_rankdir(Attr_rankdir::Enum rankdir)
|
||||
{
|
||||
this->set_attribute("rankdir", Attr_rankdir::to_string(rankdir));
|
||||
}
|
||||
|
||||
void set_random_cluster_bgcolors();
|
||||
};
|
||||
|
||||
class Cluster {
|
||||
private:
|
||||
AttributeList m_attributes;
|
||||
Graph &m_graph;
|
||||
Cluster *m_parent = nullptr;
|
||||
Set<Cluster *> m_children;
|
||||
Set<Node *> m_nodes;
|
||||
|
||||
friend Graph;
|
||||
friend Node;
|
||||
|
||||
Cluster(Graph &graph) : m_graph(graph)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
void export__declare_nodes_and_clusters(std::stringstream &ss) const;
|
||||
|
||||
void set_attribute(StringRef key, StringRef value)
|
||||
{
|
||||
m_attributes.set(key, value);
|
||||
}
|
||||
|
||||
void set_parent_cluster(Cluster *cluster);
|
||||
void set_parent_cluster(Cluster &cluster)
|
||||
{
|
||||
this->set_parent_cluster(&cluster);
|
||||
}
|
||||
|
||||
void set_random_cluster_bgcolors();
|
||||
};
|
||||
|
||||
class Node {
|
||||
private:
|
||||
AttributeList m_attributes;
|
||||
Graph &m_graph;
|
||||
Cluster *m_cluster = nullptr;
|
||||
|
||||
friend Graph;
|
||||
|
||||
Node(Graph &graph) : m_graph(graph)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
const AttributeList &attributes() const
|
||||
{
|
||||
return m_attributes;
|
||||
}
|
||||
|
||||
AttributeList &attributes()
|
||||
{
|
||||
return m_attributes;
|
||||
}
|
||||
|
||||
void set_parent_cluster(Cluster *cluster);
|
||||
void set_parent_cluster(Cluster &cluster)
|
||||
{
|
||||
this->set_parent_cluster(&cluster);
|
||||
}
|
||||
|
||||
void set_attribute(StringRef key, StringRef value)
|
||||
{
|
||||
m_attributes.set(key, value);
|
||||
}
|
||||
|
||||
void set_shape(Attr_shape::Enum shape)
|
||||
{
|
||||
this->set_attribute("shape", Attr_shape::to_string(shape));
|
||||
}
|
||||
|
||||
/* See https://www.graphviz.org/doc/info/attrs.html#k:color. */
|
||||
void set_background_color(StringRef name)
|
||||
{
|
||||
this->set_attribute("fillcolor", name);
|
||||
this->set_attribute("style", "filled");
|
||||
}
|
||||
|
||||
void export__as_id(std::stringstream &ss) const;
|
||||
|
||||
void export__as_declaration(std::stringstream &ss) const;
|
||||
};
|
||||
|
||||
class UndirectedGraph final : public Graph {
|
||||
private:
|
||||
Vector<std::unique_ptr<UndirectedEdge>> m_edges;
|
||||
|
||||
public:
|
||||
std::string to_dot_string() const;
|
||||
|
||||
UndirectedEdge &new_edge(NodePort a, NodePort b);
|
||||
};
|
||||
|
||||
class DirectedGraph final : public Graph {
|
||||
private:
|
||||
Vector<std::unique_ptr<DirectedEdge>> m_edges;
|
||||
|
||||
public:
|
||||
std::string to_dot_string() const;
|
||||
|
||||
DirectedEdge &new_edge(NodePort from, NodePort to);
|
||||
};
|
||||
|
||||
class NodePort {
|
||||
private:
|
||||
Node *m_node;
|
||||
Optional<std::string> m_port_name;
|
||||
|
||||
public:
|
||||
NodePort(Node &node, Optional<std::string> port_name = {})
|
||||
: m_node(&node), m_port_name(std::move(port_name))
|
||||
{
|
||||
}
|
||||
|
||||
void to_dot_string(std::stringstream &ss) const;
|
||||
};
|
||||
|
||||
class Edge : BLI::NonCopyable, BLI::NonMovable {
|
||||
protected:
|
||||
AttributeList m_attributes;
|
||||
NodePort m_a;
|
||||
NodePort m_b;
|
||||
|
||||
public:
|
||||
Edge(NodePort a, NodePort b) : m_a(std::move(a)), m_b(std::move(b))
|
||||
{
|
||||
}
|
||||
|
||||
void set_attribute(StringRef key, StringRef value)
|
||||
{
|
||||
m_attributes.set(key, value);
|
||||
}
|
||||
|
||||
void set_arrowhead(Attr_arrowType::Enum type)
|
||||
{
|
||||
this->set_attribute("arrowhead", Attr_arrowType::to_string(type));
|
||||
}
|
||||
|
||||
void set_arrowtail(Attr_arrowType::Enum type)
|
||||
{
|
||||
this->set_attribute("arrowtail", Attr_arrowType::to_string(type));
|
||||
}
|
||||
|
||||
void set_dir(Attr_dirType::Enum type)
|
||||
{
|
||||
this->set_attribute("dir", Attr_dirType::to_string(type));
|
||||
}
|
||||
};
|
||||
|
||||
class DirectedEdge : public Edge {
|
||||
public:
|
||||
DirectedEdge(NodePort from, NodePort to) : Edge(std::move(from), std::move(to))
|
||||
{
|
||||
}
|
||||
|
||||
void export__as_edge_statement(std::stringstream &ss) const;
|
||||
};
|
||||
|
||||
class UndirectedEdge : public Edge {
|
||||
public:
|
||||
UndirectedEdge(NodePort a, NodePort b) : Edge(std::move(a), std::move(b))
|
||||
{
|
||||
}
|
||||
|
||||
void export__as_edge_statement(std::stringstream &ss) const;
|
||||
};
|
||||
|
||||
std::string color_attr_from_hsv(float h, float s, float v);
|
||||
|
||||
class NodeWithSocketsRef {
|
||||
private:
|
||||
Node *m_node;
|
||||
|
||||
public:
|
||||
NodeWithSocketsRef(Node &node,
|
||||
StringRef name,
|
||||
ArrayRef<std::string> input_names,
|
||||
ArrayRef<std::string> output_names);
|
||||
|
||||
NodePort input(uint index) const
|
||||
{
|
||||
std::string port = "\"in" + std::to_string(index) + "\"";
|
||||
return NodePort(*m_node, port);
|
||||
}
|
||||
|
||||
NodePort output(uint index) const
|
||||
{
|
||||
std::string port = "\"out" + std::to_string(index) + "\"";
|
||||
return NodePort(*m_node, port);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace DotExport
|
||||
} // namespace BLI
|
||||
|
||||
#endif /* __BLI_DOT_EXPORT_H__ */
|
||||
112
source/blender/blenlib/BLI_dot_export_attribute_enums.h
Normal file
112
source/blender/blenlib/BLI_dot_export_attribute_enums.h
Normal file
@@ -0,0 +1,112 @@
|
||||
#ifndef __BLI_DOT_EXPORT_ATTRIBUTE_ENUMS_H__
|
||||
#define __BLI_DOT_EXPORT_ATTRIBUTE_ENUMS_H__
|
||||
|
||||
#include "BLI_string_ref.h"
|
||||
|
||||
namespace BLI {
|
||||
namespace DotExport {
|
||||
|
||||
namespace Attr_rankdir {
|
||||
enum Enum {
|
||||
LeftToRight,
|
||||
TopToBottom,
|
||||
};
|
||||
|
||||
static StringRef to_string(Enum value)
|
||||
{
|
||||
switch (value) {
|
||||
case LeftToRight:
|
||||
return "LR";
|
||||
case TopToBottom:
|
||||
return "TB";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
} // namespace Attr_rankdir
|
||||
|
||||
namespace Attr_shape {
|
||||
enum Enum {
|
||||
Rectangle,
|
||||
Ellipse,
|
||||
Circle,
|
||||
Point,
|
||||
Diamond,
|
||||
Square,
|
||||
};
|
||||
|
||||
static StringRef to_string(Enum value)
|
||||
{
|
||||
switch (value) {
|
||||
case Rectangle:
|
||||
return "rectangle";
|
||||
case Ellipse:
|
||||
return "ellipse";
|
||||
case Circle:
|
||||
return "circle";
|
||||
case Point:
|
||||
return "point";
|
||||
case Diamond:
|
||||
return "diamond";
|
||||
case Square:
|
||||
return "square";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
} // namespace Attr_shape
|
||||
|
||||
namespace Attr_arrowType {
|
||||
enum Enum {
|
||||
Normal,
|
||||
Inv,
|
||||
Dot,
|
||||
None,
|
||||
Empty,
|
||||
Box,
|
||||
Vee,
|
||||
};
|
||||
|
||||
static StringRef to_string(Enum value)
|
||||
{
|
||||
switch (value) {
|
||||
case Normal:
|
||||
return "normal";
|
||||
case Inv:
|
||||
return "inv";
|
||||
case Dot:
|
||||
return "dot";
|
||||
case None:
|
||||
return "none";
|
||||
case Empty:
|
||||
return "empty";
|
||||
case Box:
|
||||
return "box";
|
||||
case Vee:
|
||||
return "vee";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
} // namespace Attr_arrowType
|
||||
|
||||
namespace Attr_dirType {
|
||||
enum Enum { Forward, Back, Both, None };
|
||||
|
||||
static StringRef to_string(Enum value)
|
||||
{
|
||||
switch (value) {
|
||||
case Forward:
|
||||
return "forward";
|
||||
case Back:
|
||||
return "back";
|
||||
case Both:
|
||||
return "both";
|
||||
case None:
|
||||
return "none";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
} // namespace Attr_dirType
|
||||
|
||||
} // namespace DotExport
|
||||
} // namespace BLI
|
||||
|
||||
#endif /* __BLI_DOT_EXPORT_ATTRIBUTE_ENUMS_H__ */
|
||||
90
source/blender/blenlib/BLI_float2.h
Normal file
90
source/blender/blenlib/BLI_float2.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __BLI_FLOAT2_H__
|
||||
#define __BLI_FLOAT2_H__
|
||||
|
||||
#include "BLI_float3.h"
|
||||
|
||||
namespace BLI {
|
||||
|
||||
struct float2 {
|
||||
float x, y;
|
||||
|
||||
float2() = default;
|
||||
|
||||
float2(const float *ptr) : x{ptr[0]}, y{ptr[1]}
|
||||
{
|
||||
}
|
||||
|
||||
float2(float x, float y) : x(x), y(y)
|
||||
{
|
||||
}
|
||||
|
||||
float2(float3 other) : x(other.x), y(other.y)
|
||||
{
|
||||
}
|
||||
|
||||
operator float *()
|
||||
{
|
||||
return &x;
|
||||
}
|
||||
|
||||
float2 clamped(float min, float max)
|
||||
{
|
||||
return {std::min(std::max(x, min), max), std::min(std::max(y, min), max)};
|
||||
}
|
||||
|
||||
float2 clamped_01()
|
||||
{
|
||||
return this->clamped(0, 1);
|
||||
}
|
||||
|
||||
friend float2 operator+(float2 a, float2 b)
|
||||
{
|
||||
return {a.x + b.x, a.y + b.y};
|
||||
}
|
||||
|
||||
friend float2 operator-(float2 a, float2 b)
|
||||
{
|
||||
return {a.x - b.x, a.y - b.y};
|
||||
}
|
||||
|
||||
friend float2 operator*(float2 a, float b)
|
||||
{
|
||||
return {a.x * b, a.y * b};
|
||||
}
|
||||
|
||||
friend float2 operator/(float2 a, float b)
|
||||
{
|
||||
return {a.x / b, a.y / b};
|
||||
}
|
||||
|
||||
friend float2 operator*(float a, float2 b)
|
||||
{
|
||||
return b * a;
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &stream, float2 v)
|
||||
{
|
||||
stream << "(" << v.x << ", " << v.y << ")";
|
||||
return stream;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace BLI
|
||||
|
||||
#endif /* __BLI_FLOAT2_H__ */
|
||||
229
source/blender/blenlib/BLI_float3.h
Normal file
229
source/blender/blenlib/BLI_float3.h
Normal file
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __BLI_FLOAT3_H__
|
||||
#define __BLI_FLOAT3_H__
|
||||
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
|
||||
namespace BLI {
|
||||
struct float3 {
|
||||
float x, y, z;
|
||||
|
||||
float3() = default;
|
||||
|
||||
float3(const float *ptr) : x{ptr[0]}, y{ptr[1]}, z{ptr[2]}
|
||||
{
|
||||
}
|
||||
|
||||
explicit float3(float value) : x(value), y(value), z(value)
|
||||
{
|
||||
}
|
||||
|
||||
explicit float3(int value) : x(value), y(value), z(value)
|
||||
{
|
||||
}
|
||||
|
||||
float3(float x, float y, float z) : x{x}, y{y}, z{z}
|
||||
{
|
||||
}
|
||||
|
||||
operator const float *() const
|
||||
{
|
||||
return (const float *)this;
|
||||
}
|
||||
|
||||
operator float *()
|
||||
{
|
||||
return (float *)this;
|
||||
}
|
||||
|
||||
operator std::array<float, 3>()
|
||||
{
|
||||
return {x, y, z};
|
||||
}
|
||||
|
||||
float normalize_and_get_length()
|
||||
{
|
||||
return normalize_v3(*this);
|
||||
}
|
||||
|
||||
float3 normalized() const
|
||||
{
|
||||
float3 result;
|
||||
normalize_v3_v3(result, *this);
|
||||
return result;
|
||||
}
|
||||
|
||||
float length() const
|
||||
{
|
||||
return len_v3(*this);
|
||||
}
|
||||
|
||||
float length_squared() const
|
||||
{
|
||||
return len_squared_v3(*this);
|
||||
}
|
||||
|
||||
void reflect(float3 normal)
|
||||
{
|
||||
*this = this->reflected(normal);
|
||||
}
|
||||
|
||||
float3 reflected(float3 normal) const
|
||||
{
|
||||
float3 result;
|
||||
reflect_v3_v3v3(result, *this, normal);
|
||||
return result;
|
||||
}
|
||||
|
||||
static float3 safe_divide(const float3 a, const float3 b)
|
||||
{
|
||||
float3 result;
|
||||
result.x = (b.x == 0.0f) ? 0.0f : a.x / b.x;
|
||||
result.y = (b.y == 0.0f) ? 0.0f : a.y / b.y;
|
||||
result.z = (b.z == 0.0f) ? 0.0f : a.z / b.z;
|
||||
return result;
|
||||
}
|
||||
|
||||
void invert()
|
||||
{
|
||||
x = -x;
|
||||
y = -y;
|
||||
z = -z;
|
||||
}
|
||||
|
||||
bool is_zero() const
|
||||
{
|
||||
return x != 0.0f && y != 0.0f && z != 0.0f;
|
||||
}
|
||||
|
||||
friend float3 operator+(float3 a, float3 b)
|
||||
{
|
||||
return {a.x + b.x, a.y + b.y, a.z + b.z};
|
||||
}
|
||||
|
||||
void operator+=(float3 b)
|
||||
{
|
||||
this->x += b.x;
|
||||
this->y += b.y;
|
||||
this->z += b.z;
|
||||
}
|
||||
|
||||
friend float3 operator-(float3 a, float3 b)
|
||||
{
|
||||
return {a.x - b.x, a.y - b.y, a.z - b.z};
|
||||
}
|
||||
|
||||
friend float3 operator-(float3 a)
|
||||
{
|
||||
return {-a.x, -a.y, -a.z};
|
||||
}
|
||||
|
||||
void operator-=(float3 b)
|
||||
{
|
||||
this->x -= b.x;
|
||||
this->y -= b.y;
|
||||
this->z -= b.z;
|
||||
}
|
||||
|
||||
void operator*=(float scalar)
|
||||
{
|
||||
this->x *= scalar;
|
||||
this->y *= scalar;
|
||||
this->z *= scalar;
|
||||
}
|
||||
|
||||
void operator*=(float3 other)
|
||||
{
|
||||
this->x *= other.x;
|
||||
this->y *= other.y;
|
||||
this->z *= other.z;
|
||||
}
|
||||
|
||||
friend float3 operator*(float3 a, float3 b)
|
||||
{
|
||||
return {a.x * b.x, a.y * b.y, a.z * b.z};
|
||||
}
|
||||
|
||||
friend float3 operator*(float3 a, float b)
|
||||
{
|
||||
return {a.x * b, a.y * b, a.z * b};
|
||||
}
|
||||
|
||||
friend float3 operator*(float a, float3 b)
|
||||
{
|
||||
return b * a;
|
||||
}
|
||||
|
||||
friend float3 operator/(float3 a, float3 b)
|
||||
{
|
||||
BLI_assert(!b.is_zero());
|
||||
return {a.x / b.x, a.y / b.y, a.z / b.z};
|
||||
}
|
||||
|
||||
friend float3 operator/(float3 a, float b)
|
||||
{
|
||||
BLI_assert(b != 0);
|
||||
return {a.x / b, a.y / b, a.z / b};
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &stream, float3 v)
|
||||
{
|
||||
stream << "(" << v.x << ", " << v.y << ", " << v.z << ")";
|
||||
return stream;
|
||||
}
|
||||
|
||||
static float dot(float3 a, float3 b)
|
||||
{
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z;
|
||||
}
|
||||
|
||||
static float3 cross_high_precision(float3 a, float3 b)
|
||||
{
|
||||
float3 result;
|
||||
cross_v3_v3v3_hi_prec(result, a, b);
|
||||
return result;
|
||||
}
|
||||
|
||||
static float3 project(float3 a, float3 b)
|
||||
{
|
||||
float3 result;
|
||||
project_v3_v3v3(result, a, b);
|
||||
return result;
|
||||
}
|
||||
|
||||
static float distance(float3 a, float3 b)
|
||||
{
|
||||
return (a - b).length();
|
||||
}
|
||||
|
||||
static float distance_squared(float3 a, float3 b)
|
||||
{
|
||||
return float3::dot(a, b);
|
||||
}
|
||||
|
||||
static float3 interpolate(float3 a, float3 b, float t)
|
||||
{
|
||||
return a * (1 - t) + b * t;
|
||||
}
|
||||
};
|
||||
} // namespace BLI
|
||||
|
||||
#endif /* __BLI_FLOAT3_H__ */
|
||||
103
source/blender/blenlib/BLI_float4x4.h
Normal file
103
source/blender/blenlib/BLI_float4x4.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __BLI_FLOAT4X4_H__
|
||||
#define __BLI_FLOAT4X4_H__
|
||||
|
||||
#include "BLI_array_ref.h"
|
||||
#include "BLI_float3.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
|
||||
namespace BLI {
|
||||
|
||||
struct float4x4 {
|
||||
float values[4][4];
|
||||
|
||||
float4x4() = default;
|
||||
|
||||
float4x4(float *matrix)
|
||||
{
|
||||
memcpy(values, matrix, sizeof(float) * 16);
|
||||
}
|
||||
|
||||
float4x4(float matrix[4][4]) : float4x4((float *)matrix)
|
||||
{
|
||||
}
|
||||
|
||||
operator float *()
|
||||
{
|
||||
return (float *)this;
|
||||
}
|
||||
|
||||
float4x4 inverted() const
|
||||
{
|
||||
float result[4][4];
|
||||
invert_m4_m4(result, values);
|
||||
return result;
|
||||
}
|
||||
|
||||
float4x4 inverted__LocRotScale() const
|
||||
{
|
||||
return this->inverted();
|
||||
}
|
||||
|
||||
float3 transform_position(float3 position) const
|
||||
{
|
||||
mul_m4_v3(values, position);
|
||||
return position;
|
||||
}
|
||||
|
||||
float3 transform_direction(float3 direction) const
|
||||
{
|
||||
mul_mat3_m4_v3(values, direction);
|
||||
return direction;
|
||||
}
|
||||
|
||||
static void transform_positions(ArrayRef<float4x4> matrices,
|
||||
ArrayRef<float3> positions,
|
||||
MutableArrayRef<float3> r_results)
|
||||
{
|
||||
uint amount = matrices.size();
|
||||
BLI_assert(amount == positions.size());
|
||||
BLI_assert(amount == r_results.size());
|
||||
for (uint i = 0; i < amount; i++) {
|
||||
r_results[i] = matrices[i].transform_position(positions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void transform_directions(ArrayRef<float4x4> matrices,
|
||||
ArrayRef<float3> directions,
|
||||
MutableArrayRef<float3> r_results)
|
||||
{
|
||||
uint amount = matrices.size();
|
||||
BLI_assert(amount == directions.size());
|
||||
BLI_assert(amount == r_results.size());
|
||||
for (uint i = 0; i < amount; i++) {
|
||||
r_results[i] = matrices[i].transform_direction(directions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static float4x4 interpolate(float4x4 a, float4x4 b, float t)
|
||||
{
|
||||
float result[4][4];
|
||||
interp_m4_m4m4(result, a.values, b.values, t);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace BLI
|
||||
|
||||
#endif /* __BLI_FLOAT4X4_H__ */
|
||||
94
source/blender/blenlib/BLI_float_interval.h
Normal file
94
source/blender/blenlib/BLI_float_interval.h
Normal file
@@ -0,0 +1,94 @@
|
||||
#ifndef __BLI_TIME_SPAN_H__
|
||||
#define __BLI_TIME_SPAN_H__
|
||||
|
||||
#include "BLI_array_ref.h"
|
||||
|
||||
namespace BLI {
|
||||
|
||||
class FloatInterval {
|
||||
private:
|
||||
float m_start;
|
||||
float m_size;
|
||||
|
||||
public:
|
||||
FloatInterval(float start, float size) : m_start(start), m_size(size)
|
||||
{
|
||||
BLI_assert(size >= 0.0f);
|
||||
}
|
||||
|
||||
float start() const
|
||||
{
|
||||
return m_start;
|
||||
}
|
||||
|
||||
float size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
float end() const
|
||||
{
|
||||
return m_start + m_size;
|
||||
}
|
||||
|
||||
float value_at(float factor) const
|
||||
{
|
||||
return m_start + factor * m_size;
|
||||
}
|
||||
|
||||
void value_at(ArrayRef<float> factors, MutableArrayRef<float> r_values)
|
||||
{
|
||||
assert_same_size(factors, r_values);
|
||||
for (uint i : factors.index_range()) {
|
||||
r_values[i] = this->value_at(factors[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void sample_linear(MutableArrayRef<float> r_values)
|
||||
{
|
||||
if (r_values.size() == 0) {
|
||||
return;
|
||||
}
|
||||
if (r_values.size() == 1) {
|
||||
r_values[0] = this->value_at(0.5f);
|
||||
}
|
||||
for (uint i : r_values.index_range()) {
|
||||
float factor = (i - 1) / (float)r_values.size();
|
||||
r_values[i] = this->value_at(factor);
|
||||
}
|
||||
}
|
||||
|
||||
float factor_of(float value) const
|
||||
{
|
||||
BLI_assert(m_size > 0.0f);
|
||||
return (value - m_start) / m_size;
|
||||
}
|
||||
|
||||
float safe_factor_of(float value) const
|
||||
{
|
||||
if (m_size > 0.0f) {
|
||||
return this->factor_of(value);
|
||||
}
|
||||
else {
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void uniform_sample_range(float samples_per_time,
|
||||
float &r_factor_start,
|
||||
float &r_factor_step) const
|
||||
{
|
||||
if (m_size == 0.0f) {
|
||||
/* Just needs to be greater than one. */
|
||||
r_factor_start = 2.0f;
|
||||
return;
|
||||
}
|
||||
r_factor_step = 1 / (m_size * samples_per_time);
|
||||
float time_start = std::ceil(m_start * samples_per_time) / samples_per_time;
|
||||
r_factor_start = this->safe_factor_of(time_start);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace BLI
|
||||
|
||||
#endif /* __BLI_TIME_SPAN_H__ */
|
||||
118
source/blender/blenlib/BLI_index_mask.h
Normal file
118
source/blender/blenlib/BLI_index_mask.h
Normal file
@@ -0,0 +1,118 @@
|
||||
#ifndef __BLI_INDEX_MASK_H__
|
||||
#define __BLI_INDEX_MASK_H__
|
||||
|
||||
#include "BLI_array_ref.h"
|
||||
#include "BLI_index_range.h"
|
||||
#include "BLI_vector.h"
|
||||
#include "BLI_vector_adaptor.h"
|
||||
|
||||
namespace BLI {
|
||||
|
||||
class IndexMask {
|
||||
private:
|
||||
ArrayRef<uint> m_indices;
|
||||
|
||||
public:
|
||||
IndexMask() = default;
|
||||
|
||||
IndexMask(ArrayRef<uint> indices) : m_indices(indices)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
for (uint i = 1; i < indices.size(); i++) {
|
||||
BLI_assert(indices[i - 1] < indices[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
IndexMask(IndexRange range) : m_indices(range.as_array_ref())
|
||||
{
|
||||
}
|
||||
|
||||
template<uint N, typename Allocator>
|
||||
IndexMask(const Vector<uint, N, Allocator> &vector) : IndexMask(vector.as_ref())
|
||||
{
|
||||
}
|
||||
|
||||
IndexMask(const VectorAdaptor<uint> &vector) : IndexMask(ArrayRef<uint>(vector))
|
||||
{
|
||||
}
|
||||
|
||||
explicit IndexMask(uint n) : IndexMask(IndexRange(n))
|
||||
{
|
||||
}
|
||||
|
||||
operator ArrayRef<uint>() const
|
||||
{
|
||||
return m_indices;
|
||||
}
|
||||
|
||||
const uint *begin() const
|
||||
{
|
||||
return m_indices.begin();
|
||||
}
|
||||
|
||||
const uint *end() const
|
||||
{
|
||||
return m_indices.end();
|
||||
}
|
||||
|
||||
uint operator[](uint index) const
|
||||
{
|
||||
return m_indices[index];
|
||||
}
|
||||
|
||||
uint size() const
|
||||
{
|
||||
return m_indices.size();
|
||||
}
|
||||
|
||||
uint min_array_size() const
|
||||
{
|
||||
return (m_indices.size() == 0) ? 0 : m_indices.last() + 1;
|
||||
}
|
||||
|
||||
ArrayRef<uint> indices() const
|
||||
{
|
||||
return m_indices;
|
||||
}
|
||||
|
||||
bool is_range() const
|
||||
{
|
||||
return m_indices.size() > 0 && m_indices.last() - m_indices.first() == m_indices.size() - 1;
|
||||
}
|
||||
|
||||
IndexRange as_range() const
|
||||
{
|
||||
BLI_assert(this->is_range());
|
||||
return IndexRange{m_indices.first(), m_indices.size()};
|
||||
}
|
||||
|
||||
template<typename FuncT> void foreach_index(const FuncT &func) const
|
||||
{
|
||||
if (this->is_range()) {
|
||||
IndexRange range = this->as_range();
|
||||
for (uint i : range) {
|
||||
func(i);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (uint i : m_indices) {
|
||||
func(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IndexRange index_range() const
|
||||
{
|
||||
return m_indices.index_range();
|
||||
}
|
||||
|
||||
uint last() const
|
||||
{
|
||||
return m_indices.last();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace BLI
|
||||
|
||||
#endif /* __BLI_INDEX_MASK_H__ */
|
||||
121
source/blender/blenlib/BLI_index_to_ref_map.h
Normal file
121
source/blender/blenlib/BLI_index_to_ref_map.h
Normal file
@@ -0,0 +1,121 @@
|
||||
#ifndef __BLI_INDEX_TO_REF_MAP_H__
|
||||
#define __BLI_INDEX_TO_REF_MAP_H__
|
||||
|
||||
#include "BLI_array_cxx.h"
|
||||
#include "BLI_multi_map.h"
|
||||
|
||||
namespace BLI {
|
||||
|
||||
template<typename T, uint N = 4, typename Allocator = GuardedAllocator> class IndexToRefMap {
|
||||
private:
|
||||
Array<T *, N, Allocator> m_array;
|
||||
|
||||
public:
|
||||
IndexToRefMap(uint size) : m_array(size, nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
uint size() const
|
||||
{
|
||||
return m_array.size();
|
||||
}
|
||||
|
||||
void add(uint key, T &value)
|
||||
{
|
||||
m_array[key] = &value;
|
||||
}
|
||||
|
||||
void add_new(uint key, T &value)
|
||||
{
|
||||
BLI_assert(m_array[key] == nullptr);
|
||||
m_array[key] = &value;
|
||||
}
|
||||
|
||||
bool contains(uint key) const
|
||||
{
|
||||
return m_array[key] != nullptr;
|
||||
}
|
||||
|
||||
const T &lookup(uint key) const
|
||||
{
|
||||
BLI_assert(this->contains(key));
|
||||
return *m_array[key];
|
||||
}
|
||||
|
||||
T &lookup(uint key)
|
||||
{
|
||||
BLI_assert(this->contains(key));
|
||||
return *m_array[key];
|
||||
}
|
||||
};
|
||||
|
||||
#define IndexToRefMultiMap_UNMAPPED nullptr
|
||||
#define IndexToRefMultiMap_MULTIMAPPED ((T *)1)
|
||||
|
||||
template<typename T, uint N = 4, typename Allocator = GuardedAllocator> class IndexToRefMultiMap {
|
||||
private:
|
||||
Array<T *> m_array;
|
||||
MultiMap<uint, T *> m_fallback_multimap;
|
||||
|
||||
public:
|
||||
IndexToRefMultiMap(uint max_index) : m_array(max_index, IndexToRefMultiMap_UNMAPPED)
|
||||
{
|
||||
}
|
||||
|
||||
uint max_index() const
|
||||
{
|
||||
return m_array.size();
|
||||
}
|
||||
|
||||
bool contains(uint key) const
|
||||
{
|
||||
return m_array[key] != IndexToRefMultiMap_UNMAPPED;
|
||||
}
|
||||
|
||||
ArrayRef<T *> lookup(uint key) const
|
||||
{
|
||||
T *const *stored_value_addr = &m_array[key];
|
||||
const T *stored_value = *stored_value_addr;
|
||||
if (stored_value == IndexToRefMultiMap_UNMAPPED) {
|
||||
return {};
|
||||
}
|
||||
else if (stored_value == IndexToRefMultiMap_MULTIMAPPED) {
|
||||
return m_fallback_multimap.lookup(key);
|
||||
}
|
||||
else {
|
||||
return ArrayRef<T *>(stored_value_addr, 1);
|
||||
}
|
||||
}
|
||||
|
||||
T &lookup_single(uint key)
|
||||
{
|
||||
T *stored_value = m_array[key];
|
||||
BLI_assert(stored_value != IndexToRefMultiMap_UNMAPPED &&
|
||||
stored_value != IndexToRefMultiMap_MULTIMAPPED);
|
||||
return *stored_value;
|
||||
}
|
||||
|
||||
void add(uint key, T &value)
|
||||
{
|
||||
T **stored_value_addr = &m_array[key];
|
||||
T *stored_value = *stored_value_addr;
|
||||
if (stored_value == IndexToRefMultiMap_UNMAPPED) {
|
||||
*stored_value_addr = &value;
|
||||
}
|
||||
else if (stored_value == IndexToRefMultiMap_MULTIMAPPED) {
|
||||
m_fallback_multimap.add(key, &value);
|
||||
}
|
||||
else {
|
||||
T *other_value = stored_value;
|
||||
*stored_value_addr = IndexToRefMultiMap_MULTIMAPPED;
|
||||
m_fallback_multimap.add_multiple_new(key, {other_value, &value});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#undef IndexToRefMultiMap_UNMAPPED
|
||||
#undef IndexToRefMultiMap_MULTIMAPPED
|
||||
|
||||
} // namespace BLI
|
||||
|
||||
#endif /* __BLI_INDEX_TO_REF_MAP_H__ */
|
||||
230
source/blender/blenlib/BLI_linear_allocated_vector.h
Normal file
230
source/blender/blenlib/BLI_linear_allocated_vector.h
Normal file
@@ -0,0 +1,230 @@
|
||||
#pragma once
|
||||
|
||||
#include "BLI_index_range.h"
|
||||
#include "BLI_linear_allocator.h"
|
||||
#include "BLI_memory_utils_cxx.h"
|
||||
|
||||
namespace BLI {
|
||||
|
||||
template<typename T> class LinearAllocatedVector : BLI::NonCopyable {
|
||||
private:
|
||||
T *m_begin;
|
||||
T *m_end;
|
||||
T *m_capacity_end;
|
||||
|
||||
#ifdef DEBUG
|
||||
uint m_debug_size;
|
||||
# define UPDATE_VECTOR_SIZE(ptr) (ptr)->m_debug_size = (ptr)->m_end - (ptr)->m_begin
|
||||
#else
|
||||
# define UPDATE_VECTOR_SIZE(ptr) ((void)0)
|
||||
#endif
|
||||
|
||||
public:
|
||||
LinearAllocatedVector() : m_begin(nullptr), m_end(nullptr), m_capacity_end(nullptr)
|
||||
{
|
||||
UPDATE_VECTOR_SIZE(this);
|
||||
}
|
||||
|
||||
~LinearAllocatedVector()
|
||||
{
|
||||
destruct_n(m_begin, this->size());
|
||||
}
|
||||
|
||||
LinearAllocatedVector(LinearAllocatedVector &&other)
|
||||
{
|
||||
m_begin = other.m_begin;
|
||||
m_end = other.m_end;
|
||||
m_capacity_end = other.m_capacity_end;
|
||||
|
||||
other.m_begin = nullptr;
|
||||
other.m_end = nullptr;
|
||||
other.m_capacity_end = nullptr;
|
||||
|
||||
UPDATE_VECTOR_SIZE(this);
|
||||
UPDATE_VECTOR_SIZE(&other);
|
||||
}
|
||||
|
||||
LinearAllocatedVector &operator=(LinearAllocatedVector &&other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
m_begin = other.m_begin;
|
||||
m_end = other.m_end;
|
||||
m_capacity_end = other.m_capacity_end;
|
||||
|
||||
other.m_begin = nullptr;
|
||||
other.m_end = nullptr;
|
||||
other.m_capacity_end = nullptr;
|
||||
|
||||
UPDATE_VECTOR_SIZE(this);
|
||||
UPDATE_VECTOR_SIZE(&other);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator ArrayRef<T>() const
|
||||
{
|
||||
return ArrayRef<T>(m_begin, this->size());
|
||||
}
|
||||
|
||||
operator MutableArrayRef<T>()
|
||||
{
|
||||
return MutableArrayRef<T>(m_begin, this->size());
|
||||
}
|
||||
|
||||
ArrayRef<T> as_ref() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
MutableArrayRef<T> as_mutable_ref() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
IndexRange index_range() const
|
||||
{
|
||||
return IndexRange(this->size());
|
||||
}
|
||||
|
||||
uint size() const
|
||||
{
|
||||
return m_end - m_begin;
|
||||
}
|
||||
|
||||
uint capacity() const
|
||||
{
|
||||
return m_capacity_end - m_begin;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
destruct_n(m_begin, this->size());
|
||||
m_end = m_begin;
|
||||
UPDATE_VECTOR_SIZE(this);
|
||||
}
|
||||
|
||||
void append_unchecked(const T &value)
|
||||
{
|
||||
BLI_assert(m_end < m_capacity_end);
|
||||
new (m_end) T(value);
|
||||
m_end++;
|
||||
UPDATE_VECTOR_SIZE(this);
|
||||
}
|
||||
|
||||
template<typename AllocT> void append(const T &value, LinearAllocator<AllocT> &allocator)
|
||||
{
|
||||
if (m_end == m_capacity_end) {
|
||||
this->grow(this->size() + 1, allocator);
|
||||
}
|
||||
this->append_unchecked(value);
|
||||
}
|
||||
|
||||
template<typename AllocT>
|
||||
uint append_and_get_index(const T &value, LinearAllocator<AllocT> &allocator)
|
||||
{
|
||||
uint index = this->size();
|
||||
this->append(value, allocator);
|
||||
return index;
|
||||
}
|
||||
|
||||
bool contains(const T &value) const
|
||||
{
|
||||
for (const T ¤t : *this) {
|
||||
if (current == value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const T &operator[](uint index) const
|
||||
{
|
||||
BLI_assert(index < this->size());
|
||||
return m_begin[index];
|
||||
}
|
||||
|
||||
T &operator[](uint index)
|
||||
{
|
||||
BLI_assert(index < this->size());
|
||||
return m_begin[index];
|
||||
}
|
||||
|
||||
const T *begin() const
|
||||
{
|
||||
return m_begin;
|
||||
}
|
||||
|
||||
const T *end() const
|
||||
{
|
||||
return m_end;
|
||||
}
|
||||
|
||||
T *begin()
|
||||
{
|
||||
return m_begin;
|
||||
}
|
||||
|
||||
T *end()
|
||||
{
|
||||
return m_end;
|
||||
}
|
||||
|
||||
void remove_and_reorder(uint index)
|
||||
{
|
||||
BLI_assert(index < this->size());
|
||||
T *element_to_remove = m_begin + index;
|
||||
m_end--;
|
||||
if (element_to_remove < m_end) {
|
||||
*element_to_remove = std::move(*m_end);
|
||||
}
|
||||
destruct(m_end);
|
||||
UPDATE_VECTOR_SIZE(this);
|
||||
}
|
||||
|
||||
int index_try(const T &value) const
|
||||
{
|
||||
for (T *current = m_begin; current != m_end; current++) {
|
||||
if (*current == value) {
|
||||
return current - m_begin;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint index(const T &value) const
|
||||
{
|
||||
int index = this->index_try(value);
|
||||
BLI_assert(index >= 0);
|
||||
return (uint)index;
|
||||
}
|
||||
|
||||
void remove_first_occurrence_and_reorder(const T &value)
|
||||
{
|
||||
uint index = this->index(value);
|
||||
this->remove_and_reorder((uint)index);
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename AllocT>
|
||||
BLI_NOINLINE void grow(uint min_capacity, LinearAllocator<AllocT> &allocator)
|
||||
{
|
||||
if (min_capacity <= this->capacity()) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint size = this->size();
|
||||
min_capacity = power_of_2_max_u(min_capacity);
|
||||
|
||||
T *new_begin = (T *)allocator.allocate(sizeof(T) * min_capacity, alignof(T));
|
||||
uninitialized_relocate_n(m_begin, size, new_begin);
|
||||
|
||||
m_begin = new_begin;
|
||||
m_end = new_begin + size;
|
||||
m_capacity_end = new_begin + min_capacity;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace BLI
|
||||
172
source/blender/blenlib/BLI_linear_allocator.h
Normal file
172
source/blender/blenlib/BLI_linear_allocator.h
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* A linear allocator is the simplest form of an allocator. It never reuses any memory, and
|
||||
* therefore does not need a deallocation method. It simply hands out consecutive buffers of
|
||||
* memory. When the current buffer is full, it reallocates a new larger buffer and continues.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_stack_cxx.h"
|
||||
#include "BLI_string_ref.h"
|
||||
#include "BLI_timeit.h"
|
||||
#include "BLI_utility_mixins.h"
|
||||
#include "BLI_vector.h"
|
||||
|
||||
namespace BLI {
|
||||
|
||||
template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopyable, NonMovable {
|
||||
private:
|
||||
Allocator m_allocator;
|
||||
Vector<void *> m_owned_buffers;
|
||||
Vector<ArrayRef<char>> m_unused_borrowed_buffers;
|
||||
|
||||
uintptr_t m_current_begin;
|
||||
uintptr_t m_current_end;
|
||||
uint m_next_min_alloc_size;
|
||||
|
||||
#ifdef DEBUG
|
||||
uint m_debug_allocated_amount = 0;
|
||||
#endif
|
||||
|
||||
public:
|
||||
LinearAllocator()
|
||||
{
|
||||
m_current_begin = 0;
|
||||
m_current_end = 0;
|
||||
m_next_min_alloc_size = 64;
|
||||
}
|
||||
|
||||
~LinearAllocator()
|
||||
{
|
||||
for (void *ptr : m_owned_buffers) {
|
||||
m_allocator.deallocate(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void provide_buffer(void *buffer, uint size)
|
||||
{
|
||||
m_unused_borrowed_buffers.append(ArrayRef<char>((char *)buffer, size));
|
||||
}
|
||||
|
||||
template<uint Size, uint Alignment>
|
||||
void provide_buffer(AlignedBuffer<Size, Alignment> &aligned_buffer)
|
||||
{
|
||||
this->provide_buffer(aligned_buffer.ptr(), Size);
|
||||
}
|
||||
|
||||
template<typename T> T *allocate()
|
||||
{
|
||||
return (T *)this->allocate(sizeof(T), alignof(T));
|
||||
}
|
||||
|
||||
template<typename T> MutableArrayRef<T> allocate_array(uint length)
|
||||
{
|
||||
return MutableArrayRef<T>((T *)this->allocate(sizeof(T) * length), length);
|
||||
}
|
||||
|
||||
void *allocate(uint size, uint alignment = 4)
|
||||
{
|
||||
BLI_assert(alignment >= 1);
|
||||
BLI_assert(is_power_of_2_i(alignment));
|
||||
|
||||
#ifdef DEBUG
|
||||
m_debug_allocated_amount += size;
|
||||
#endif
|
||||
|
||||
uintptr_t alignment_mask = alignment - 1;
|
||||
uintptr_t potential_allocation_begin = (m_current_begin + alignment_mask) & ~alignment_mask;
|
||||
uintptr_t potential_allocation_end = potential_allocation_begin + size;
|
||||
|
||||
if (potential_allocation_end <= m_current_end) {
|
||||
m_current_begin = potential_allocation_end;
|
||||
return (void *)potential_allocation_begin;
|
||||
}
|
||||
else {
|
||||
this->allocate_new_buffer(size + alignment);
|
||||
return this->allocate(size, alignment);
|
||||
}
|
||||
};
|
||||
|
||||
StringRefNull copy_string(StringRef str)
|
||||
{
|
||||
uint alloc_size = str.size() + 1;
|
||||
char *buffer = (char *)this->allocate(alloc_size, 1);
|
||||
str.copy(buffer, alloc_size);
|
||||
return StringRefNull((const char *)buffer);
|
||||
}
|
||||
|
||||
template<typename T, typename... Args> T *construct(Args &&... args)
|
||||
{
|
||||
void *buffer = this->allocate(sizeof(T), alignof(T));
|
||||
T *value = new (buffer) T(std::forward<Args>(args)...);
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
ArrayRef<T *> construct_elements_and_pointer_array(uint n, Args &&... args)
|
||||
{
|
||||
void *pointer_buffer = this->allocate(n * sizeof(T *), alignof(T *));
|
||||
void *element_buffer = this->allocate(n * sizeof(T), alignof(T));
|
||||
|
||||
MutableArrayRef<T *> pointers((T **)pointer_buffer, n);
|
||||
T *elements = (T *)element_buffer;
|
||||
|
||||
for (uint i : IndexRange(n)) {
|
||||
pointers[i] = elements + i;
|
||||
}
|
||||
for (uint i : IndexRange(n)) {
|
||||
new (elements + i) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
return pointers;
|
||||
}
|
||||
|
||||
template<typename T> MutableArrayRef<T> construct_array_copy(ArrayRef<T> source)
|
||||
{
|
||||
T *buffer = (T *)this->allocate(source.byte_size(), alignof(T));
|
||||
source.copy_to(buffer);
|
||||
return MutableArrayRef<T>(buffer, source.size());
|
||||
}
|
||||
|
||||
private:
|
||||
void allocate_new_buffer(uint min_allocation_size)
|
||||
{
|
||||
for (uint i : m_unused_borrowed_buffers.index_range()) {
|
||||
ArrayRef<char> buffer = m_unused_borrowed_buffers[i];
|
||||
if (buffer.size() >= min_allocation_size) {
|
||||
m_unused_borrowed_buffers.remove_and_reorder(i);
|
||||
m_current_begin = (uintptr_t)buffer.begin();
|
||||
m_current_end = (uintptr_t)buffer.end();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint size_in_bytes = power_of_2_min_u(std::max(min_allocation_size, m_next_min_alloc_size));
|
||||
m_next_min_alloc_size = size_in_bytes * 2;
|
||||
|
||||
void *buffer = m_allocator.allocate(size_in_bytes, __func__);
|
||||
m_owned_buffers.append(buffer);
|
||||
m_current_begin = (uintptr_t)buffer;
|
||||
m_current_end = m_current_begin + size_in_bytes;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace BLI
|
||||
225
source/blender/blenlib/BLI_multi_map.h
Normal file
225
source/blender/blenlib/BLI_multi_map.h
Normal file
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* A multimap is a map that allows storing multiple values per key.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_array_ref.h"
|
||||
#include "BLI_linear_allocator.h"
|
||||
#include "BLI_map.h"
|
||||
#include "BLI_vector.h"
|
||||
|
||||
namespace BLI {
|
||||
|
||||
template<typename KeyT, typename ValueT, uint N = 4> class MultiMap {
|
||||
private:
|
||||
struct Entry {
|
||||
ValueT *ptr = nullptr;
|
||||
uint length = 0;
|
||||
uint capacity = 0;
|
||||
};
|
||||
|
||||
LinearAllocator<> m_allocator;
|
||||
Map<KeyT, Entry> m_map;
|
||||
|
||||
public:
|
||||
MultiMap() = default;
|
||||
|
||||
~MultiMap()
|
||||
{
|
||||
this->foreach_value([](ValueT &value) { value.~ValueT(); });
|
||||
}
|
||||
|
||||
MultiMap(const MultiMap &other)
|
||||
{
|
||||
this->add_multiple(other);
|
||||
}
|
||||
|
||||
MultiMap &operator=(const MultiMap &other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
this->~MultiMap();
|
||||
new (this) MultiMap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint key_amount() const
|
||||
{
|
||||
return m_map.size();
|
||||
}
|
||||
|
||||
uint value_amount(const KeyT &key) const
|
||||
{
|
||||
return m_map.lookup_default(key, {}).length;
|
||||
}
|
||||
|
||||
void add_new(const KeyT &key, const ValueT &value)
|
||||
{
|
||||
BLI_assert(!this->contains(key));
|
||||
this->add(key, value);
|
||||
}
|
||||
|
||||
void add_multiple_new(const KeyT &key, ArrayRef<ValueT> values)
|
||||
{
|
||||
BLI_assert(!this->contains(key));
|
||||
this->add_multiple(key, values);
|
||||
}
|
||||
|
||||
bool add(const KeyT &key, const ValueT &value)
|
||||
{
|
||||
return this->add__impl(key, value);
|
||||
}
|
||||
bool add(const KeyT &key, ValueT &&value)
|
||||
{
|
||||
return this->add__impl(key, std::move(value));
|
||||
}
|
||||
bool add(KeyT &&key, const ValueT &value)
|
||||
{
|
||||
return this->add__impl(std::move(key), value);
|
||||
}
|
||||
bool add(KeyT &&key, ValueT &&value)
|
||||
{
|
||||
return this->add__impl(std::move(key), std::move(value));
|
||||
}
|
||||
|
||||
void add_multiple(const KeyT &key, ArrayRef<ValueT> values)
|
||||
{
|
||||
this->add_multiple__impl(key, values);
|
||||
}
|
||||
void add_multiple(const KeyT &&key, ArrayRef<ValueT> values)
|
||||
{
|
||||
this->add_multiple__impl(std::move(key), values);
|
||||
}
|
||||
|
||||
template<uint OtherN> void add_multiple(const MultiMap<KeyT, ValueT, OtherN> &other)
|
||||
{
|
||||
BLI_assert(this != &other);
|
||||
other.foreach_item(
|
||||
[&](const KeyT &key, ArrayRef<ValueT> values) { this->add_multiple(key, values); });
|
||||
}
|
||||
|
||||
ArrayRef<ValueT> lookup(const KeyT &key) const
|
||||
{
|
||||
const Entry &entry = m_map.lookup(key);
|
||||
return ArrayRef<ValueT>(entry.ptr, entry.length);
|
||||
}
|
||||
|
||||
ArrayRef<ValueT> lookup_default(const KeyT &key,
|
||||
ArrayRef<ValueT> default_array = ArrayRef<ValueT>()) const
|
||||
{
|
||||
const Entry *entry = m_map.lookup_ptr(key);
|
||||
if (entry == nullptr) {
|
||||
return default_array;
|
||||
}
|
||||
else {
|
||||
return ArrayRef<ValueT>(entry->ptr, entry->length);
|
||||
}
|
||||
}
|
||||
|
||||
bool contains(const KeyT &key) const
|
||||
{
|
||||
return m_map.contains(key);
|
||||
}
|
||||
|
||||
typename Map<KeyT, Entry>::KeyIterator keys() const
|
||||
{
|
||||
return m_map.keys();
|
||||
}
|
||||
|
||||
template<typename FuncT> void foreach_value(const FuncT &func) const
|
||||
{
|
||||
for (const Entry &entry : m_map.values()) {
|
||||
for (const ValueT &value : ArrayRef<ValueT>(entry.ptr, entry.length)) {
|
||||
func(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FuncT> void foreach_value(const FuncT &func)
|
||||
{
|
||||
for (Entry &entry : m_map.values()) {
|
||||
for (ValueT &value : MutableArrayRef<ValueT>(entry.ptr, entry.length)) {
|
||||
func(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FuncT> void foreach_item(const FuncT &func) const
|
||||
{
|
||||
for (auto item : m_map.items()) {
|
||||
const KeyT &key = item.key;
|
||||
ArrayRef<ValueT> values{item.value.ptr, item.value.length};
|
||||
func(key, values);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename ForwardKeyT>
|
||||
void add_multiple__impl(ForwardKeyT &&key, ArrayRef<ValueT> values)
|
||||
{
|
||||
for (const ValueT &value : values) {
|
||||
this->add(std::forward<ForwardKeyT>(key), value);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ForwardKeyT, typename ForwardValueT>
|
||||
bool add__impl(ForwardKeyT &&key, ForwardValueT &&value)
|
||||
{
|
||||
bool newly_inserted = m_map.add_or_modify(
|
||||
std::forward<ForwardKeyT>(key),
|
||||
/* Insert new key with value. */
|
||||
[&](Entry *r_entry) -> bool {
|
||||
uint initial_capacity = 1;
|
||||
ValueT *array = (ValueT *)m_allocator.allocate(sizeof(ValueT) * initial_capacity,
|
||||
alignof(ValueT));
|
||||
new (array) ValueT(std::forward<ForwardValueT>(value));
|
||||
r_entry->ptr = array;
|
||||
r_entry->length = 1;
|
||||
r_entry->capacity = initial_capacity;
|
||||
return true;
|
||||
},
|
||||
/* Append new value for existing key. */
|
||||
[&](Entry *entry) -> bool {
|
||||
if (entry->length < entry->capacity) {
|
||||
new (entry->ptr + entry->length) ValueT(std::forward<ForwardValueT>(value));
|
||||
entry->length++;
|
||||
}
|
||||
else {
|
||||
uint old_capacity = entry->capacity;
|
||||
BLI_assert(old_capacity >= 1);
|
||||
uint new_capacity = old_capacity * 2;
|
||||
ValueT *new_array = (ValueT *)m_allocator.allocate(sizeof(ValueT) * new_capacity,
|
||||
alignof(ValueT));
|
||||
uninitialized_relocate_n(entry->ptr, old_capacity, new_array);
|
||||
new (new_array + entry->length) ValueT(std::forward<ForwardValueT>(value));
|
||||
entry->ptr = new_array;
|
||||
entry->length++;
|
||||
entry->capacity = new_capacity;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return newly_inserted;
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace BLI */
|
||||
153
source/blender/blenlib/BLI_parallel.h
Normal file
153
source/blender/blenlib/BLI_parallel.h
Normal file
@@ -0,0 +1,153 @@
|
||||
#ifndef __BLI_PARALLEL_H__
|
||||
#define __BLI_PARALLEL_H__
|
||||
|
||||
#ifdef WITH_TBB
|
||||
# define TBB_SUPPRESS_DEPRECATED_MESSAGES 1
|
||||
# include "tbb/parallel_for.h"
|
||||
# include "tbb/parallel_invoke.h"
|
||||
#endif
|
||||
|
||||
#include "BLI_index_range.h"
|
||||
#include "BLI_multi_map.h"
|
||||
#include "BLI_string_map.h"
|
||||
#include "BLI_string_multi_map.h"
|
||||
|
||||
namespace BLI {
|
||||
|
||||
/**
|
||||
* Call func for every index in the IndexRange. func has to receive a single uint parameter.
|
||||
*/
|
||||
template<typename FuncT> void parallel_for(IndexRange range, const FuncT &func)
|
||||
{
|
||||
if (range.size() == 0) {
|
||||
return;
|
||||
}
|
||||
#ifdef WITH_TBB
|
||||
tbb::parallel_for(range.first(), range.one_after_last(), func);
|
||||
#else
|
||||
for (uint i : range) {
|
||||
func(i);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Call func for subranges of range. The size of the individual subranges is controlled by a
|
||||
* grain_size. func has to receive an IndexRange as parameter.
|
||||
*/
|
||||
template<typename FuncT>
|
||||
void blocked_parallel_for(IndexRange range, uint grain_size, const FuncT &func)
|
||||
{
|
||||
if (range.size() == 0) {
|
||||
return;
|
||||
}
|
||||
#ifdef WITH_TBB
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<uint>(range.first(), range.one_after_last(), grain_size),
|
||||
[&](const tbb::blocked_range<uint> &sub_range) { func(IndexRange(sub_range)); });
|
||||
#else
|
||||
UNUSED_VARS(grain_size);
|
||||
func(range);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke multiple functions in parallel.
|
||||
*/
|
||||
template<typename FuncT1, typename FuncT2>
|
||||
void parallel_invoke(const FuncT1 &func1, const FuncT2 &func2)
|
||||
{
|
||||
#ifdef WITH_TBB
|
||||
tbb::parallel_invoke(func1, func2);
|
||||
#else
|
||||
func1();
|
||||
func2();
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename FuncT1, typename FuncT2, typename FuncT3>
|
||||
void parallel_invoke(const FuncT1 &func1, const FuncT2 &func2, const FuncT3 &func3)
|
||||
{
|
||||
#ifdef WITH_TBB
|
||||
tbb::parallel_invoke(func1, func2, func3);
|
||||
#else
|
||||
func1();
|
||||
func2();
|
||||
func3();
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename KeyT, typename ValueT, uint N, typename FuncT>
|
||||
void parallel_map_items(const MultiMap<KeyT, ValueT, N> &multi_map, const FuncT &func)
|
||||
{
|
||||
ScopedVector<const KeyT *> key_vector;
|
||||
ScopedVector<ArrayRef<ValueT>> values_vector;
|
||||
|
||||
multi_map.foreach_item([&](const KeyT &key, ArrayRef<ValueT> values) {
|
||||
key_vector.append(&key);
|
||||
values_vector.append(values);
|
||||
});
|
||||
|
||||
parallel_for(key_vector.index_range(), [&](uint index) {
|
||||
const KeyT &key = *key_vector[index];
|
||||
ArrayRef<ValueT> values = values_vector[index];
|
||||
|
||||
func(key, values);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename ValueT, typename FuncT>
|
||||
void parallel_map_items(const StringMultiMap<ValueT> &string_multi_map, const FuncT &func)
|
||||
{
|
||||
ScopedVector<StringRefNull> key_vector;
|
||||
ScopedVector<ArrayRef<ValueT>> values_vector;
|
||||
|
||||
string_multi_map.foreach_item([&](StringRefNull key, ArrayRef<ValueT> values) {
|
||||
key_vector.append(key);
|
||||
values_vector.append(values);
|
||||
});
|
||||
|
||||
parallel_for(key_vector.index_range(), [&](uint index) {
|
||||
StringRefNull &key = key_vector[index];
|
||||
ArrayRef<ValueT> values = values_vector[index];
|
||||
|
||||
func(key, values);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename ValueT, typename FuncT>
|
||||
void parallel_map_items(const StringMap<ValueT> &string_map, const FuncT &func)
|
||||
{
|
||||
ScopedVector<StringRefNull> key_vector;
|
||||
ScopedVector<const ValueT *> value_vector;
|
||||
|
||||
string_map.foreach_item([&](StringRefNull key, const ValueT &value) {
|
||||
key_vector.append(key);
|
||||
value_vector.append(&value);
|
||||
});
|
||||
|
||||
parallel_for(key_vector.index_range(), [&](uint index) {
|
||||
StringRefNull key = key_vector[index];
|
||||
const ValueT &value = *value_vector[index];
|
||||
|
||||
func(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename ValueT, typename FuncT>
|
||||
void parallel_map_keys(const StringMap<ValueT> &string_map, const FuncT &func)
|
||||
{
|
||||
ScopedVector<StringRefNull> key_vector;
|
||||
|
||||
string_map.foreach_item(
|
||||
[&](StringRefNull key, const ValueT &UNUSED(value)) { key_vector.append(key); });
|
||||
|
||||
parallel_for(key_vector.index_range(), [&](uint index) {
|
||||
StringRefNull key = key_vector[index];
|
||||
func(key);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace BLI
|
||||
|
||||
#endif /* __BLI_PARALLEL_H__ */
|
||||
26
source/blender/blenlib/BLI_rand_cxx.h
Normal file
26
source/blender/blenlib/BLI_rand_cxx.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef __BLI_RAND_CXX_H__
|
||||
#define __BLI_RAND_CXX_H__
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace BLI {
|
||||
|
||||
inline uint32_t hash_from_path_and_line(const char *path, uint32_t line)
|
||||
{
|
||||
uint32_t hash = 5381;
|
||||
const char *str = path;
|
||||
char c = 0;
|
||||
while ((c = *str++)) {
|
||||
hash = hash * 37 + c;
|
||||
}
|
||||
hash = hash ^ ((line + 573259433) * 654188383);
|
||||
return hash;
|
||||
}
|
||||
|
||||
} // namespace BLI
|
||||
|
||||
#define BLI_RAND_PER_LINE_UINT32 BLI::hash_from_path_and_line(__FILE__, __LINE__)
|
||||
|
||||
#endif /* __BLI_RAND_CXX_H__ */
|
||||
105
source/blender/blenlib/BLI_resource_collector.h
Normal file
105
source/blender/blenlib/BLI_resource_collector.h
Normal file
@@ -0,0 +1,105 @@
|
||||
#ifndef __BLI_OWNED_RESOURCES_H__
|
||||
#define __BLI_OWNED_RESOURCES_H__
|
||||
|
||||
#include "BLI_linear_allocator.h"
|
||||
#include "BLI_string_ref.h"
|
||||
#include "BLI_utility_mixins.h"
|
||||
#include "BLI_vector.h"
|
||||
|
||||
namespace BLI {
|
||||
|
||||
class ResourceCollector : NonCopyable {
|
||||
private:
|
||||
struct ResourceData {
|
||||
void *data;
|
||||
void (*free)(void *data);
|
||||
const char *name;
|
||||
};
|
||||
|
||||
LinearAllocator<> m_allocator;
|
||||
Vector<ResourceData> m_resources;
|
||||
|
||||
public:
|
||||
ResourceCollector() = default;
|
||||
|
||||
~ResourceCollector()
|
||||
{
|
||||
for (int i = m_resources.size() - 1; i >= 0; i--) {
|
||||
ResourceData &data = m_resources[i];
|
||||
// std::cout << "FREE: " << data.name << std::endl;
|
||||
data.free(data.data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add another object that will be freed when this container is freed. Objects are freed in
|
||||
* reverse order.
|
||||
*/
|
||||
template<typename T> void add(std::unique_ptr<T> resource, const char *name)
|
||||
{
|
||||
BLI_assert(resource.get() != nullptr);
|
||||
this->add(
|
||||
resource.release(),
|
||||
[](void *data) {
|
||||
T *typed_data = reinterpret_cast<T *>(data);
|
||||
delete typed_data;
|
||||
},
|
||||
name);
|
||||
}
|
||||
|
||||
template<typename T> void add(destruct_ptr<T> resource, const char *name)
|
||||
{
|
||||
BLI_assert(resource.get() != nullptr);
|
||||
this->add(
|
||||
resource.release(),
|
||||
[](void *data) {
|
||||
T *typed_data = reinterpret_cast<T *>(data);
|
||||
typed_data->~T();
|
||||
},
|
||||
name);
|
||||
}
|
||||
|
||||
void *allocate(uint size, uint alignment)
|
||||
{
|
||||
return m_allocator.allocate(size, alignment);
|
||||
}
|
||||
|
||||
LinearAllocator<> &allocator()
|
||||
{
|
||||
return m_allocator;
|
||||
}
|
||||
|
||||
template<typename T, typename... Args> T &construct(const char *name, Args &&... args)
|
||||
{
|
||||
T *value = m_allocator.construct<T>(std::forward<Args>(args)...);
|
||||
this->add(destruct_ptr<T>(value), name);
|
||||
return *value;
|
||||
}
|
||||
|
||||
void add(void *userdata, void (*free)(void *), const char *name)
|
||||
{
|
||||
ResourceData data;
|
||||
data.name = name;
|
||||
data.data = userdata;
|
||||
data.free = free;
|
||||
m_resources.append(data);
|
||||
}
|
||||
|
||||
void print(StringRef name) const
|
||||
{
|
||||
if (m_resources.size() == 0) {
|
||||
std::cout << "\"" << name << "\" has no resources.\n";
|
||||
return;
|
||||
}
|
||||
else {
|
||||
std::cout << "Resources for \"" << name << "\":\n";
|
||||
for (const ResourceData &data : m_resources) {
|
||||
std::cout << " " << data.data << ": " << data.name << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace BLI
|
||||
|
||||
#endif /* __BLI_OWNED_RESOURCES_H__ */
|
||||
28
source/blender/blenlib/BLI_static_class_ids.h
Normal file
28
source/blender/blenlib/BLI_static_class_ids.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef __BLI_STATIC_CLASS_IDS_H__
|
||||
#define __BLI_STATIC_CLASS_IDS_H__
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
namespace BLI {
|
||||
|
||||
using class_id_t = uintptr_t;
|
||||
|
||||
template<typename T> class_id_t get_class_id();
|
||||
|
||||
} // namespace BLI
|
||||
|
||||
#define BLI_CREATE_CLASS_ID_UTIL1(class_name, id) \
|
||||
namespace BLI { \
|
||||
static char class_id_char##id = 0; \
|
||||
static class_id_t class_id##id = (class_id_t)&class_id_char##id; \
|
||||
template<> class_id_t get_class_id<class_name>() \
|
||||
{ \
|
||||
return class_id##id; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define BLI_CREATE_CLASS_ID_UTIL2(class_name, id) BLI_CREATE_CLASS_ID_UTIL1(class_name, id)
|
||||
|
||||
#define BLI_CREATE_CLASS_ID(class_name) BLI_CREATE_CLASS_ID_UTIL2(class_name, __LINE__)
|
||||
|
||||
#endif /* __BLI_STATIC_CLASS_IDS_H__ */
|
||||
@@ -140,12 +140,12 @@ template<typename T, typename Allocator = GuardedAllocator> class StringMap {
|
||||
return m_hashes[offset] == hash;
|
||||
}
|
||||
|
||||
bool has_exact_key(uint offset, StringRef key, const Vector<char> &chars) const
|
||||
bool has_exact_key(uint offset, StringRef key, const Vector<char, 4, Allocator> &chars) const
|
||||
{
|
||||
return key == this->get_key(offset, chars);
|
||||
}
|
||||
|
||||
StringRefNull get_key(uint offset, const Vector<char> &chars) const
|
||||
StringRefNull get_key(uint offset, const Vector<char, 4, Allocator> &chars) const
|
||||
{
|
||||
const char *ptr = chars.begin() + m_indices[offset];
|
||||
uint length = *(uint *)ptr;
|
||||
@@ -165,7 +165,7 @@ template<typename T, typename Allocator = GuardedAllocator> class StringMap {
|
||||
|
||||
using ArrayType = OpenAddressingArray<Item, 1, Allocator>;
|
||||
ArrayType m_array;
|
||||
Vector<char> m_chars;
|
||||
Vector<char, 4, Allocator> m_chars;
|
||||
|
||||
public:
|
||||
StringMap() = default;
|
||||
|
||||
81
source/blender/blenlib/BLI_string_multi_map.h
Normal file
81
source/blender/blenlib/BLI_string_multi_map.h
Normal file
@@ -0,0 +1,81 @@
|
||||
#ifndef __BLI_STRING_MULTI_MAP_H__
|
||||
#define __BLI_STRING_MULTI_MAP_H__
|
||||
|
||||
#include "BLI_string_map.h"
|
||||
#include "BLI_string_ref.h"
|
||||
#include "BLI_vector.h"
|
||||
|
||||
namespace BLI {
|
||||
|
||||
template<typename ValueT> class StringMultiMap {
|
||||
private:
|
||||
StringMap<Vector<ValueT>> m_map;
|
||||
|
||||
public:
|
||||
StringMultiMap() = default;
|
||||
~StringMultiMap() = default;
|
||||
|
||||
uint key_amount() const
|
||||
{
|
||||
return m_map.size();
|
||||
}
|
||||
|
||||
uint value_amount(StringRef key) const
|
||||
{
|
||||
return m_map.lookup(key).size();
|
||||
}
|
||||
|
||||
bool add(StringRef key, const ValueT &value)
|
||||
{
|
||||
if (m_map.contains(key)) {
|
||||
m_map.lookup(key).append(value);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
m_map.add_new(key, Vector<ValueT>({value}));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void add_multiple(StringRef key, ArrayRef<ValueT> values)
|
||||
{
|
||||
if (m_map.contains(key)) {
|
||||
m_map.lookup(key).extend(values);
|
||||
}
|
||||
else {
|
||||
m_map.add_new(key, values);
|
||||
}
|
||||
}
|
||||
|
||||
void add_multiple(const StringMultiMap<ValueT> &other)
|
||||
{
|
||||
other.foreach_item(
|
||||
[&](StringRefNull key, ArrayRef<ValueT> values) { this->add_multiple(key, values); });
|
||||
}
|
||||
|
||||
ArrayRef<ValueT> lookup(StringRef key) const
|
||||
{
|
||||
return m_map.lookup(key);
|
||||
}
|
||||
|
||||
ArrayRef<ValueT> lookup_default(StringRef key,
|
||||
ArrayRef<ValueT> default_array = ArrayRef<ValueT>()) const
|
||||
{
|
||||
const Vector<ValueT> *values = m_map.lookup_ptr(key);
|
||||
if (values == nullptr) {
|
||||
return default_array;
|
||||
}
|
||||
else {
|
||||
return *values;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FuncT> void foreach_item(const FuncT &func) const
|
||||
{
|
||||
m_map.foreach_item([&](StringRefNull key, ArrayRef<ValueT> vector) { func(key, vector); });
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace BLI
|
||||
|
||||
#endif /* __BLI_STRING_MULTI_MAP_H__ */
|
||||
@@ -94,12 +94,28 @@ class StringRefBase {
|
||||
return m_data + m_size;
|
||||
}
|
||||
|
||||
void copy_to__with_null(char *dst) const
|
||||
void unsafe_copy(char *dst) const
|
||||
{
|
||||
memcpy(dst, m_data, m_size);
|
||||
dst[m_size] = '\0';
|
||||
}
|
||||
|
||||
void copy(char *dst, uint dst_size) const
|
||||
{
|
||||
if (m_size < dst_size) {
|
||||
this->unsafe_copy(dst);
|
||||
}
|
||||
else {
|
||||
BLI_assert(false);
|
||||
dst[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
template<uint N> void copy(char (&dst)[N])
|
||||
{
|
||||
this->copy(dst, N);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when the string begins with the given prefix. Otherwise false.
|
||||
*/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user