Compare commits
580 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc0df8cc61 | ||
|
|
c30d7a2089 | ||
|
|
03bffb3319 | ||
|
|
65dc93ed37 | ||
|
|
8ed04b9c6e | ||
|
|
e6a9327bae | ||
|
|
658c506653 | ||
|
|
ad4a4281b4 | ||
|
|
6b0070ec56 | ||
|
|
c9cf6baf4b | ||
|
|
85a25a0a39 | ||
|
|
e192d254f9 | ||
|
|
9e2b9d7cf4 | ||
|
|
cd545213e4 | ||
|
|
935d2ff02c | ||
|
|
b7d6218b89 | ||
|
|
d86754b0e6 | ||
|
|
b97674a404 | ||
|
|
f91522fc14 | ||
|
|
9b4ef00278 | ||
|
|
43270c7763 | ||
|
|
9a98fb399c | ||
|
|
c255b0249f | ||
|
|
b0e71f6f18 | ||
|
|
7e59377daf | ||
|
|
73aa369e6d | ||
|
|
731d7e5d6b | ||
|
|
bc5e1c7df9 | ||
|
|
1a15f9b581 | ||
|
|
136d6052d8 | ||
|
|
811b270bae | ||
|
|
7870e3cfce | ||
|
|
4874af1d5a | ||
|
|
1b7ead0479 | ||
|
|
ac97984314 | ||
|
|
8c778e8afb | ||
|
|
2b6bbce1d9 | ||
|
|
b8e0a9c1dd | ||
|
|
4c3c3de065 | ||
|
|
a74801a0af | ||
|
|
cbe8217790 | ||
|
|
e1cdf34955 | ||
|
|
fefc20c52a | ||
|
|
ad1e574cfe | ||
|
|
c25d4b7dad | ||
|
|
207aa88daf | ||
|
|
3be08a3e63 | ||
|
|
7d91ffbafa | ||
|
|
128ba084ad | ||
|
|
0defff8f7c | ||
|
|
6e3497e4c4 | ||
|
|
1c309b2c89 | ||
|
|
35507e7fbb | ||
|
|
bc439829cf | ||
|
|
93f433f388 | ||
|
|
f9e99e2a33 | ||
|
|
94dcf7c3f3 | ||
|
|
c3bb29182e | ||
|
|
b11f6d5b3b | ||
|
|
d2b900f7c3 | ||
|
|
bf2b5d8882 | ||
|
|
e7878bdb8f | ||
|
|
0670550521 | ||
|
|
f5df923c51 | ||
|
|
f1f62816b5 | ||
|
|
db462690b3 | ||
|
|
c28685c700 | ||
|
|
1f1780597c | ||
|
|
1590251dad | ||
|
|
d25c17342b | ||
|
|
e105022185 | ||
|
|
16931917b7 | ||
|
|
e2e316a079 | ||
|
|
1dd2151a20 | ||
|
|
c7367ad46a | ||
|
|
21045411e7 | ||
|
|
128e8834e8 | ||
|
|
7433772606 | ||
|
|
8967f5f090 | ||
|
|
3b51a6e2a9 | ||
|
|
c5cb635b4e | ||
|
|
1e9f8d707e | ||
|
|
69a0aa4bd8 | ||
|
|
581fa88055 | ||
|
|
f991ae5aed | ||
|
|
44825ece04 | ||
|
|
eac2c5c020 | ||
|
|
a593e97227 | ||
|
|
ecad786f50 | ||
|
|
9012d33c05 | ||
|
|
268751815f | ||
|
|
6529b170e6 | ||
|
|
5ce465cb30 | ||
|
|
48f1df2fd6 | ||
|
|
3e6ddf560a | ||
|
|
0051533ac8 | ||
|
|
e69ac7ca28 | ||
|
|
9c8abb8edf | ||
|
|
9ee434f275 | ||
|
|
4826d9fbf0 | ||
|
|
3fd7b8ed3c | ||
|
|
ebc8d483d9 | ||
|
|
9821d3595a | ||
|
|
f21be9d10c | ||
|
|
d221036cde | ||
|
|
1dbff48ebb | ||
|
|
43e56fc433 | ||
|
|
b3b562f4bb | ||
|
|
784ac996d3 | ||
|
|
fb6a95078d | ||
|
|
189e7b8bc9 | ||
|
|
55967ad27c | ||
|
|
e2c82af4d6 | ||
|
|
8712923bec | ||
|
|
416635179b | ||
|
|
c1b635e036 | ||
|
|
2860d8f1de | ||
|
|
c1fb07b4c7 | ||
|
|
5036c2231c | ||
|
|
c848666e17 | ||
|
|
55aef98a30 | ||
|
|
7cbbf799dc | ||
|
|
30a4e0297c | ||
|
|
5f68f51693 | ||
|
|
8e6aaf29e0 | ||
|
|
cde0e74a2e | ||
|
|
2a573cbab3 | ||
|
|
941275a1b9 | ||
|
|
c5884ec498 | ||
|
|
da06cf52ec | ||
|
|
07e8f489c1 | ||
|
|
baed2ac031 | ||
|
|
cdab138b2f | ||
|
|
06524edfb3 | ||
|
|
0e40550427 | ||
|
|
e08b6ade9e | ||
|
|
36bc4944f9 | ||
|
|
a0be95d634 | ||
|
|
faf8d4c4ad | ||
|
|
8af740caa8 | ||
|
|
69fa3521f9 | ||
|
|
991a96d3dc | ||
|
|
b10bf06305 | ||
|
|
7f54b30fbe | ||
|
|
ee79043536 | ||
|
|
c9e6611b92 | ||
|
|
73d128d89a | ||
|
|
5a240acd86 | ||
|
|
7dc4a5cf87 | ||
|
|
6d835297b2 | ||
|
|
b44c29e235 | ||
|
|
8da9a9cf27 | ||
|
|
a3617626f7 | ||
|
|
e44832ea9e | ||
|
|
f841e78dcf | ||
|
|
e7a8d48cca | ||
|
|
69e8d0e32f | ||
|
|
830d1a6bf9 | ||
|
|
e9b72b442a | ||
|
|
77ac7eca18 | ||
|
|
86d4198ffd | ||
|
|
37dfe31ac2 | ||
|
|
584e1c48e6 | ||
|
|
15766ceb97 | ||
|
|
6e79f28b69 | ||
|
|
e9632d206b | ||
|
|
a0f55bfcb5 | ||
|
|
3c32f1da6e | ||
|
|
7e13c1b22a | ||
|
|
2fc52e673f | ||
|
|
48001a660b | ||
|
|
5a6daf79b6 | ||
|
|
ae4fd9f7df | ||
|
|
7dfa8776fd | ||
|
|
a836796fcc | ||
|
|
fb131972d4 | ||
|
|
140ce358fa | ||
|
|
dd92f2dccb | ||
|
|
d62004eadf | ||
|
|
abc21e9692 | ||
|
|
a13b0d5d91 | ||
|
|
12f8c75c2d | ||
|
|
bdc1920166 | ||
|
|
8cb0d57ffe | ||
|
|
7344689263 | ||
|
|
f521ca1118 | ||
|
|
6712f1383e | ||
|
|
d756fd4a29 | ||
|
|
e070ef1b7f | ||
|
|
ea7786a002 | ||
|
|
3eec07fac1 | ||
|
|
46afa76af8 | ||
|
|
6cc1ba64d8 | ||
|
|
225596481f | ||
|
|
4ef3b3b332 | ||
|
|
e7ac768b5b | ||
|
|
b1e8833daa | ||
|
|
155d71dc80 | ||
|
|
94ebf17134 | ||
|
|
d6fe2edf0d | ||
|
|
867cd7e583 | ||
|
|
1a2471a32d | ||
|
|
057dd9c01d | ||
|
|
5a2c0e15e9 | ||
|
|
ae5ae24f6f | ||
|
|
7a2f3fe840 | ||
|
|
b46788c81a | ||
|
|
3c1f37e5f9 | ||
|
|
d8d4322b2e | ||
|
|
1613e9ce46 | ||
|
|
aee3c8db1b | ||
|
|
becaeedff1 | ||
|
|
c9c6651368 | ||
|
|
d77371912b | ||
|
|
93cb12be89 | ||
|
|
4ac2bd8e92 | ||
|
|
34d52c975e | ||
|
|
85194c7f4f | ||
|
|
af009e03a0 | ||
|
|
cef7379c07 | ||
|
|
f1e75d8593 | ||
|
|
c5a194e98c | ||
|
|
7858033628 | ||
|
|
f2405e02f6 | ||
|
|
452221daa5 | ||
|
|
91f551c2d8 | ||
|
|
2ca1763280 | ||
|
|
da4a8333f7 | ||
|
|
c0b08f3219 | ||
|
|
c273e9125c | ||
|
|
7244b95844 | ||
|
|
a298b6587d | ||
|
|
d87f6b74f3 | ||
|
|
1f0b145740 | ||
|
|
f6d528d36d | ||
|
|
ea6502a282 | ||
|
|
8a36a94e73 | ||
|
|
1604eaa239 | ||
|
|
fec2fb7ce6 | ||
|
|
d2e3b854aa | ||
|
|
f8e8d33c61 | ||
|
|
dd76135efd | ||
|
|
d7be7a69ab | ||
|
|
fd942b28c6 | ||
|
|
7c850b0405 | ||
|
|
e3bb95b3dd | ||
|
|
662d8bcfda | ||
|
|
3c0b2c64e1 | ||
|
|
ed7d42cf6d | ||
|
|
8852914ceb | ||
|
|
944778175a | ||
|
|
81704c08c9 | ||
|
|
810059e6da | ||
|
|
8d95bd16fd | ||
|
|
4600253d1e | ||
|
|
d695aa9f57 | ||
|
|
7867aac55f | ||
|
|
e26563c3d6 | ||
|
|
07ad262857 | ||
|
|
cb0abd51db | ||
|
|
cc69baf0dd | ||
|
|
852a8d04c9 | ||
|
|
ffdacb3850 | ||
|
|
062e8357fa | ||
|
|
07dece9cd7 | ||
|
|
d628a513d9 | ||
|
|
6012ad9b1e | ||
|
|
c3e618de36 | ||
|
|
6b9795fe96 | ||
|
|
f7da3a347d | ||
|
|
cf020d06c6 | ||
|
|
137b5ca4f9 | ||
|
|
82be9326a8 | ||
|
|
222282dced | ||
|
|
c7a2086850 | ||
|
|
168ac5065d | ||
|
|
158ea1984f | ||
|
|
f66d73e385 | ||
|
|
252ed1c6f2 | ||
|
|
69e35167bc | ||
|
|
2140075133 | ||
|
|
80469ead18 | ||
|
|
77dc563219 | ||
|
|
9d7f0b22f7 | ||
|
|
56b17116e3 | ||
|
|
c71791b649 | ||
|
|
c8f8fcf9d3 | ||
|
|
d41a85f4a1 | ||
|
|
3c465f9a7a | ||
|
|
c25427cf4a | ||
|
|
1c8575e40c | ||
|
|
e512eab1e8 | ||
|
|
4dd8ceb245 | ||
|
|
07e2bd4bcf | ||
|
|
05eb62bb35 | ||
|
|
0df3342757 | ||
|
|
0615f38a26 | ||
|
|
1fe63b68ee | ||
|
|
1e7dbb5331 | ||
|
|
0ddc03b7c0 | ||
|
|
becc3d0953 | ||
|
|
48b1bfaebd | ||
|
|
c043026764 | ||
|
|
0aed9fc306 | ||
|
|
739559783b | ||
|
|
0f4076acab | ||
|
|
9692ac3f4d | ||
|
|
365a333b1d | ||
|
|
f039755bde | ||
|
|
d314f1bae2 | ||
|
|
aae1915519 | ||
|
|
220df8918c | ||
|
|
3e41655902 | ||
|
|
54a23f5ae7 | ||
|
|
edffcf8902 | ||
|
|
dd55f41264 | ||
|
|
8fef0052a3 | ||
|
|
f2db10d29a | ||
|
|
33756c775c | ||
|
|
ef65dd8cc6 | ||
|
|
74d0d851ca | ||
|
|
49ebc17334 | ||
|
|
548aba5b7c | ||
|
|
dc1f1e02a1 | ||
|
|
d10b809687 | ||
|
|
ad438ef339 | ||
|
|
8a7a6ed4f5 | ||
|
|
c6097e0397 | ||
|
|
0f3e856438 | ||
|
|
5b6ec81cee | ||
|
|
e888bfbc8d | ||
|
|
b04ab65258 | ||
|
|
46b707f246 | ||
|
|
f2a6073829 | ||
|
|
e5926a5371 | ||
|
|
86c4581a50 | ||
|
|
a80074dc21 | ||
|
|
3effdd1408 | ||
|
|
b927c55216 | ||
|
|
49c590a9b5 | ||
|
|
8df6a2dd83 | ||
|
|
fe66c95a29 | ||
|
|
c080959f64 | ||
|
|
f27a88787d | ||
|
|
5853495125 | ||
|
|
f49d94948d | ||
|
|
82dad0fad3 | ||
|
|
fea99498af | ||
|
|
5582ee8ed8 | ||
|
|
f4b6db9404 | ||
|
|
5c92362aae | ||
|
|
667ea9fa64 | ||
|
|
b710750035 | ||
|
|
491ff01c79 | ||
|
|
4981145dd8 | ||
|
|
e9497b03f4 | ||
|
|
d76e518db1 | ||
|
|
a2906c6aa5 | ||
|
|
e0676c66a0 | ||
|
|
0b12c5a169 | ||
|
|
39b61fe331 | ||
|
|
83e77681d9 | ||
|
|
95a69937bd | ||
|
|
c355bd7569 | ||
|
|
559a186cd9 | ||
|
|
7e81ef37d7 | ||
|
|
2e167d260d | ||
|
|
aa50d818ec | ||
|
|
b9dd0a3877 | ||
|
|
e03ef7e214 | ||
|
|
caa05d739f | ||
|
|
f580673dea | ||
|
|
297d271e63 | ||
|
|
a28e4be5a3 | ||
|
|
6b672acdc7 | ||
|
|
26f5368264 | ||
|
|
fc5b967973 | ||
|
|
3cf497fa91 | ||
|
|
f50411e9db | ||
|
|
74eca2e527 | ||
|
|
6200e78e93 | ||
|
|
904c122ee0 | ||
|
|
741afaea18 | ||
|
|
6cf86d80e2 | ||
|
|
886305ec13 | ||
|
|
ab040f5268 | ||
|
|
8404e0f670 | ||
|
|
3605ae14b5 | ||
|
|
bce372bd79 | ||
|
|
715c48b7eb | ||
|
|
eb9a3c2ad1 | ||
|
|
b261829aea | ||
|
|
595be6b7b8 | ||
|
|
9ed76ae4da | ||
|
|
2ae0ef40d4 | ||
|
|
f5f7d3c154 | ||
|
|
a9ab4dbe38 | ||
|
|
5c3c26851c | ||
|
|
2b3696aab1 | ||
|
|
35ec0c9bcf | ||
|
|
4f915d6708 | ||
|
|
f4eb8b246b | ||
|
|
1a80e52241 | ||
|
|
afa27a04fe | ||
|
|
b82c83de5e | ||
|
|
625e2305ba | ||
|
|
6b4781b7d5 | ||
|
|
7acffabdd8 | ||
|
|
e6f2aa2399 | ||
|
|
ece7e04c7c | ||
|
|
0b4d273e08 | ||
|
|
81e8d6d99c | ||
|
|
e96444671e | ||
|
|
e52785a8b4 | ||
|
|
ddccc5ff6b | ||
|
|
8bbcef585f | ||
|
|
e58041227b | ||
|
|
3678e8fb27 | ||
|
|
1146a9125b | ||
|
|
aab0c055ed | ||
|
|
685082e212 | ||
|
|
1bb8b636b9 | ||
|
|
43c51c3b82 | ||
|
|
b9ed64fba2 | ||
|
|
ceaac03adf | ||
|
|
3085a837c8 | ||
|
|
2e5e2c8430 | ||
|
|
48df5c0eb1 | ||
|
|
fcad6766c3 | ||
|
|
c91a4670de | ||
|
|
5e7c325874 | ||
|
|
6b8a1428d7 | ||
|
|
5bd81db37e | ||
|
|
9834baedfa | ||
|
|
0b4c42859d | ||
|
|
a92b45ae26 | ||
|
|
1e2096e691 | ||
|
|
3df6ffe280 | ||
|
|
d4f370c071 | ||
|
|
b894ea866a | ||
|
|
3a300a4ca3 | ||
|
|
0ad844d10e | ||
|
|
d310a45f72 | ||
|
|
57c31804b4 | ||
|
|
5fb70a9b9a | ||
|
|
d402143989 | ||
|
|
ad8d799cf6 | ||
|
|
cec705264d | ||
|
|
28a2e61361 | ||
|
|
6017215ada | ||
|
|
8f3128e4b3 | ||
|
|
109e6f590a | ||
|
|
ac86d75e10 | ||
|
|
8f0491ea57 | ||
|
|
6403e51ba7 | ||
|
|
e0522608a4 | ||
|
|
4b9a230803 | ||
|
|
fc5246efaa | ||
|
|
4eebfbb89f | ||
|
|
353d2c4744 | ||
|
|
2718e83132 | ||
|
|
7547d1179d | ||
|
|
61e89286bc | ||
|
|
53ae715816 | ||
|
|
8cb37dba36 | ||
|
|
4426f70de7 | ||
|
|
f0ca13150f | ||
|
|
780a20689c | ||
|
|
28f9ed1d8d | ||
|
|
2b5b8ad02c | ||
|
|
d5092b1765 | ||
|
|
fda62314f9 | ||
|
|
17018c137f | ||
|
|
8838c19c39 | ||
|
|
f02a99a4e2 | ||
|
|
7b26b0f23e | ||
|
|
0e9984827a | ||
|
|
033d41863a | ||
|
|
bc540044ac | ||
|
|
76a2535da3 | ||
|
|
ff48877d16 | ||
|
|
4b8efabc5f | ||
|
|
c79be090df | ||
|
|
626eab7e8f | ||
|
|
c103b7d883 | ||
|
|
cde055e29b | ||
|
|
1cb448c42e | ||
|
|
3d05444f30 | ||
|
|
7a551189d9 | ||
|
|
b885e70fed | ||
|
|
0a27c14041 | ||
|
|
eb8e33e311 | ||
|
|
2d44a60b90 | ||
|
|
135b6a5702 | ||
|
|
706c72fda8 | ||
|
|
fb7bdba388 | ||
|
|
733d08638d | ||
|
|
cb3cca8a64 | ||
|
|
a3a581794e | ||
|
|
f921e7610c | ||
|
|
8deb364025 | ||
|
|
3a9c6f56bf | ||
|
|
a612154123 | ||
|
|
bbc5e50491 | ||
|
|
6e3dd8165e | ||
|
|
a7689a8f54 | ||
|
|
731a1af1a6 | ||
|
|
f53ebd4389 | ||
|
|
a2d61cc30a | ||
|
|
baa6ddb401 | ||
|
|
4455aa6709 | ||
|
|
315a8534d5 | ||
|
|
d77bd4034d | ||
|
|
fa71797ed2 | ||
|
|
8c63552573 | ||
|
|
a18d53c637 | ||
|
|
ffe05368e8 | ||
|
|
923d84f378 | ||
|
|
e5334aae0a | ||
|
|
4675be7e2a | ||
|
|
bf3fc61ef7 | ||
|
|
ebd9fab312 | ||
|
|
d359cf33d1 | ||
|
|
14bab1e299 | ||
|
|
4c4ad144b9 | ||
|
|
1157c0b1c5 | ||
|
|
68b2c5e0c1 | ||
|
|
b97fd06f2a | ||
|
|
51f0f5bd66 | ||
|
|
66f445997d | ||
|
|
addd199407 | ||
|
|
73d4f7c1ea | ||
|
|
25fc5562db | ||
|
|
4d52bcb5b3 | ||
|
|
3d2de560b0 | ||
|
|
809e30d906 | ||
|
|
ef9e41f20d | ||
|
|
1b4849f214 | ||
|
|
26cc67cd41 | ||
|
|
e123d139e4 | ||
|
|
a8abd52afb | ||
|
|
091e99f21b | ||
|
|
7b7875e23f | ||
|
|
b6593c2a83 | ||
|
|
5ac7887360 | ||
|
|
ed7627af6f | ||
|
|
1ea9fc54b2 | ||
|
|
3819571ec0 | ||
|
|
94f131fc57 | ||
|
|
3c20314aab | ||
|
|
1506d36407 | ||
|
|
aa4b2967c7 | ||
|
|
a42881d31f | ||
|
|
6a3ff2f235 | ||
|
|
fc4e3dc362 | ||
|
|
d6c689c5bb | ||
|
|
8676e9b900 | ||
|
|
c271cad9aa | ||
|
|
8e3bf786c0 | ||
|
|
a6ba694fbd | ||
|
|
790ccc320e | ||
|
|
26e951e59b | ||
|
|
a97581f5d7 | ||
|
|
5bf280ca4d | ||
|
|
fe00baa701 | ||
|
|
d3cb1d7f42 | ||
|
|
593363732a | ||
|
|
d00f4cf715 | ||
|
|
cac31dbb21 | ||
|
|
eb1fa6ca04 | ||
|
|
0ac515ea5a | ||
|
|
190e2a4952 | ||
|
|
0857b1bab6 | ||
|
|
f3e42fdc95 | ||
|
|
d617f3308a | ||
|
|
27cec85443 | ||
|
|
63f7cab508 | ||
|
|
ce0ac1bee1 | ||
|
|
2c2584c8df | ||
|
|
14fd4d96c3 |
50
.github/workflows/jekyll-gh-pages.yml
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
# Sample workflow for building and deploying a Jekyll site to GitHub Pages
|
||||
name: Deploy Jekyll with GitHub Pages dependencies preinstalled
|
||||
|
||||
on:
|
||||
# Runs on pushes targeting the default branch
|
||||
push:
|
||||
branches: ["master"]
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
# Allow one concurrent deployment
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# Build job
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v2
|
||||
- name: Build with Jekyll
|
||||
uses: actions/jekyll-build-pages@v1
|
||||
with:
|
||||
source: ./docs
|
||||
destination: ./_site
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v1
|
||||
|
||||
# Deployment job
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v1
|
||||
8
.gitignore
vendored
@@ -24,3 +24,11 @@ osx/.DS_Store
|
||||
.DS_Store
|
||||
|
||||
osx/runvimix
|
||||
|
||||
*.autosave
|
||||
|
||||
flatpak/.flatpak-builder
|
||||
|
||||
flatpak/repo/
|
||||
|
||||
flatpak/build/
|
||||
|
||||
9
.gitmodules
vendored
@@ -13,9 +13,12 @@
|
||||
[submodule "ext/Dirent"]
|
||||
path = ext/Dirent
|
||||
url = https://github.com/tronkko/dirent.git
|
||||
[submodule "ext/tfd"]
|
||||
path = ext/tfd
|
||||
url = https://github.com/native-toolkit/tinyfiledialogs.git
|
||||
[submodule "ext/glm"]
|
||||
path = ext/glm
|
||||
url = https://github.com/g-truc/glm.git
|
||||
[submodule "ext/link"]
|
||||
path = ext/link
|
||||
url = https://github.com/Ableton/link.git
|
||||
[submodule "ext/tfd"]
|
||||
path = ext/tfd
|
||||
url = https://git.code.sf.net/p/tinyfiledialogs/code
|
||||
715
CMakeLists.txt
@@ -1,9 +1,15 @@
|
||||
#####
|
||||
##### This file is part of vimix - video live mixer
|
||||
##### **Copyright** (C) 2019-2023 Bruno Herbelin <bruno.herbelin@gmail.com>
|
||||
#####
|
||||
|
||||
cmake_minimum_required(VERSION 3.8.2)
|
||||
project(vimix VERSION 0.0.1 LANGUAGES CXX C)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
# use git
|
||||
#####
|
||||
##### Use git to read version from tags
|
||||
#####
|
||||
find_package (Git)
|
||||
if(GIT_EXECUTABLE)
|
||||
execute_process(
|
||||
@@ -16,23 +22,36 @@ if(GIT_EXECUTABLE)
|
||||
if(NOT GIT_DESCRIBE_ERROR_CODE)
|
||||
string(SUBSTRING ${GIT_DESCRIBE_VERSION} 0 1 VIMIX_VERSION_MAJOR)
|
||||
string(SUBSTRING ${GIT_DESCRIBE_VERSION} 2 1 VIMIX_VERSION_MINOR)
|
||||
string(SUBSTRING ${GIT_DESCRIBE_VERSION} 4 1 VIMIX_VERSION_PATCH)
|
||||
string(LENGTH ${GIT_DESCRIBE_VERSION} VIMIX_VERSION_LENGTH)
|
||||
if (VIMIX_VERSION_LENGTH GREATER 4)
|
||||
string(SUBSTRING ${GIT_DESCRIBE_VERSION} 4 1 VIMIX_VERSION_PATCH)
|
||||
else()
|
||||
set(VIMIX_VERSION_PATCH "0")
|
||||
endif()
|
||||
add_definitions(-DVIMIX_VERSION_MAJOR=${VIMIX_VERSION_MAJOR})
|
||||
add_definitions(-DVIMIX_VERSION_MINOR=${VIMIX_VERSION_MINOR})
|
||||
add_definitions(-DVIMIX_VERSION_PATCH=${VIMIX_VERSION_PATCH})
|
||||
message(STATUS "Compiling vimix version ${VIMIX_VERSION_MAJOR}.${VIMIX_VERSION_MINOR}.${VIMIX_VERSION_PATCH}")
|
||||
else()
|
||||
message(STATUS "Compiling vimix (unknown version)")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENTDIR ON)
|
||||
|
||||
# Find the cmake modules
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules )
|
||||
#####
|
||||
##### Find the cmake modules
|
||||
#####
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} /usr/share/cmake ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules )
|
||||
include(MacroLogFeature)
|
||||
include(MacroFindGStreamerLibrary)
|
||||
|
||||
|
||||
#####
|
||||
##### Configure cmake
|
||||
#####
|
||||
|
||||
set(BUILD_STATIC_LIBS ON)
|
||||
set(CMAKE_INCLUDE_CURRENTDIR ON)
|
||||
|
||||
if(UNIX)
|
||||
if (APPLE)
|
||||
add_definitions(-DAPPLE)
|
||||
@@ -43,12 +62,21 @@ if(UNIX)
|
||||
set(CMAKE_OSX_ARCHITECTURES "x86_64")
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14")
|
||||
|
||||
# CPACK
|
||||
set(CPACK_SYSTEM_NAME "OSX_${CMAKE_OSX_DEPLOYMENT_TARGET}_${CMAKE_OSX_ARCHITECTURES}")
|
||||
set(CPACK_GENERATOR DragNDrop)
|
||||
set(CPACK_BINARY_DRAGNDROP ON)
|
||||
set(APPLE_CODESIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/osx/entitlements.plist")
|
||||
|
||||
# find icu4c in OSX (pretty well hidden...)
|
||||
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/opt/icu4c/lib/pkgconfig")
|
||||
|
||||
else()
|
||||
add_definitions(-DLINUX)
|
||||
|
||||
# CPACK
|
||||
set(CPACK_SYSTEM_NAME "${CMAKE_HOST_SYSTEM_NAME}")
|
||||
|
||||
# linux opengl
|
||||
set(OpenGL_GL_PREFERENCE "GLVND")
|
||||
|
||||
@@ -63,23 +91,26 @@ elseif(WIN32)
|
||||
add_definitions(-DMINGW32)
|
||||
endif()
|
||||
|
||||
#####
|
||||
##### Dependencies
|
||||
#####
|
||||
|
||||
#
|
||||
# Basics
|
||||
#
|
||||
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
find_package(GLIB2)
|
||||
macro_log_feature(GLIB2_FOUND "GLib" "GTK general-purpose utility library" "http://www.gtk.org" TRUE)
|
||||
|
||||
find_package(GObject)
|
||||
macro_log_feature(GOBJECT_FOUND "GObject" "GTK object-oriented framework" "http://www.gtk.org" TRUE)
|
||||
|
||||
find_package(PNG REQUIRED)
|
||||
macro_log_feature(PNG_FOUND "PNG" "Portable Network Graphics" "http://www.libpng.org" TRUE)
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
#
|
||||
# GSTREAMER
|
||||
#
|
||||
find_package(GLIB2 REQUIRED)
|
||||
macro_log_feature(GLIB2_FOUND "GLib" "GTK general-purpose utility library" "http://www.gtk.org" TRUE)
|
||||
|
||||
find_package(GObject REQUIRED)
|
||||
macro_log_feature(GOBJECT_FOUND "GObject" "GTK object-oriented framework" "http://www.gtk.org" TRUE)
|
||||
|
||||
find_package(GStreamer 1.0.0 COMPONENTS base)
|
||||
macro_log_feature(GSTREAMER_FOUND "GStreamer"
|
||||
@@ -105,6 +136,17 @@ macro_log_feature(GSTREAMER_GL_LIBRARY_FOUND "GStreamerPluginsBase" "GStreamer o
|
||||
# Various preprocessor definitions for GST
|
||||
add_definitions(-DGST_DISABLE_XML -DGST_DISABLE_LOADSAVE)
|
||||
|
||||
include_directories(
|
||||
${GLIB2_INCLUDE_DIR}
|
||||
${GSTREAMER_INCLUDE_DIR}
|
||||
${GSTREAMER_AUDIO_INCLUDE_DIR}
|
||||
${GSTREAMER_VIDEO_INCLUDE_DIR}
|
||||
${GSTREAMER_BASE_INCLUDE_DIR}
|
||||
${GSTREAMER_APP_INCLUDE_DIR}
|
||||
${GSTREAMER_PBUTILS_INCLUDE_DIR}
|
||||
${GSTREAMER_GL_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
#
|
||||
# ICU4C
|
||||
#
|
||||
@@ -113,8 +155,17 @@ if (PKG_CONFIG_FOUND)
|
||||
else ()
|
||||
find_package(ICU REQUIRED COMPONENTS i18n io uc)
|
||||
endif ()
|
||||
|
||||
macro_log_feature(ICU_FOUND "ICU" "International Components for Unicode" "http://site.icu-project.org" TRUE)
|
||||
|
||||
include_directories(
|
||||
${ICU_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
link_directories(
|
||||
${ICU_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
#
|
||||
# GLFW3
|
||||
#
|
||||
@@ -123,30 +174,173 @@ if (PKG_CONFIG_FOUND)
|
||||
else ()
|
||||
find_package(glfw3 3.2 REQUIRED)
|
||||
endif()
|
||||
|
||||
macro_log_feature(GLFW3_FOUND "glfw3" "Open Source multi-platform library for OpenGL" "http://www.glfw.org" TRUE)
|
||||
|
||||
include_directories(
|
||||
${GLFW3_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
macro_display_feature_log()
|
||||
|
||||
# static sub packages in ext
|
||||
set(BUILD_STATIC_LIBS ON)
|
||||
link_directories(
|
||||
${GLFW3_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
#
|
||||
# GLM
|
||||
#
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/ext/glm)
|
||||
message(STATUS "Compiling 'GLM' OpenGL mathematics https://glm.g-truc.net -- ${CMAKE_CURRENT_SOURCE_DIR}/ext/glm")
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(GLM QUIET glm>=0.9.9)
|
||||
else ()
|
||||
find_package(glm 0.9.9 QUIET)
|
||||
endif()
|
||||
|
||||
if (GLM_FOUND)
|
||||
macro_log_feature(GLM_FOUND "GLM" "OpenGL mathematics" "https://glm.g-truc.net" TRUE)
|
||||
else ()
|
||||
set(GLM_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/ext/glm)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/ext/glm)
|
||||
message(STATUS "Compiling 'GLM' OpenGL mathematics https://glm.g-truc.net -- ${CMAKE_CURRENT_SOURCE_DIR}/ext/glm")
|
||||
endif()
|
||||
|
||||
include_directories(
|
||||
${GLM_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
#
|
||||
# TINY XML 2
|
||||
#
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(TINYXML2 QUIET tinyxml2>=8.0)
|
||||
else ()
|
||||
find_package(tinyxml2 8.0 QUIET)
|
||||
endif()
|
||||
|
||||
if (TINYXML2_FOUND)
|
||||
macro_log_feature(TINYXML2_FOUND "TinyXML2" "TinyXML2 library" "https://github.com/leethomason/tinyxml2.git" TRUE)
|
||||
else ()
|
||||
set(TINYXML2_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/ext/tinyxml2)
|
||||
set(TINYXML2_LIBRARIES TINYXML2)
|
||||
add_library(TINYXML2 "${CMAKE_CURRENT_SOURCE_DIR}/ext/tinyxml2/tinyxml2.cpp")
|
||||
message(STATUS "Compiling 'TinyXML2' from https://github.com/leethomason/tinyxml2.git -- ${TINYXML2_INCLUDE_DIRS}")
|
||||
endif()
|
||||
|
||||
include_directories(
|
||||
${TINYXML2_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
#
|
||||
# STB
|
||||
#
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(STB QUIET stb)
|
||||
else ()
|
||||
find_package(stb QUIET)
|
||||
endif()
|
||||
|
||||
if (STB_FOUND)
|
||||
macro_log_feature(STB_FOUND "STB" "single-file image and audio processing" "https://github.com/nothings/stb" TRUE)
|
||||
else ()
|
||||
set(STB_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/ext/stb)
|
||||
message(STATUS "Including 'STB' single-file image and audio processing from https://github.com/nothings/stb -- ${STB_INCLUDE_DIRS}")
|
||||
endif()
|
||||
|
||||
include_directories(
|
||||
${STB_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
#
|
||||
# Ableton LINK
|
||||
#
|
||||
find_package(AbletonLink QUIET)
|
||||
|
||||
if (AbletonLink_FOUND)
|
||||
macro_log_feature(AbletonLink_FOUND "AbletonLink" "Ableton Link synchronizer" "https://github.com/Ableton/link" TRUE)
|
||||
else ()
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/ext/link/AbletonLinkConfig.cmake)
|
||||
message(STATUS "Compiling 'Ableton Link' from https://github.com/Ableton/link -- ${CMAKE_CURRENT_SOURCE_DIR}/ext/link")
|
||||
endif()
|
||||
|
||||
include_directories(
|
||||
${link_HEADERS}
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# FILE DIALOG: use tinyfiledialog for all except Linux
|
||||
#
|
||||
if(APPLE OR WIN32)
|
||||
set(TINYFD_LIBRARIES TINYFD)
|
||||
set(TINYFD_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/tfd)
|
||||
include_directories(
|
||||
${TINYFD_INCLUDE_DIR}
|
||||
)
|
||||
add_library(TINYFD "${CMAKE_CURRENT_SOURCE_DIR}/ext/tfd/tinyfiledialogs.c")
|
||||
message(STATUS "Compiling 'TinyFileDialog' from https://git.code.sf.net/p/tinyfiledialogs/code -- ${TINYFD_INCLUDE_DIR}.")
|
||||
endif()
|
||||
|
||||
#
|
||||
# DIRENT (windows only)
|
||||
if(WIN32)
|
||||
set(DIRENT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/Dirent/include)
|
||||
include_directories(
|
||||
${DIRENT_INCLUDE_DIR}
|
||||
)
|
||||
message(STATUS "Including 'Dirent' from https://github.com/tronkko/dirent -- ${DIRENT_INCLUDE_DIR}.")
|
||||
endif()
|
||||
|
||||
|
||||
#
|
||||
# SHMDATA (Unix only)
|
||||
#
|
||||
if(UNIX)
|
||||
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(SHMDATA QUIET shmdata-1.3)
|
||||
endif()
|
||||
|
||||
if(SHMDATA_FOUND)
|
||||
find_library(GSTREAMER_SHMDATA_LIBRARY
|
||||
NAMES gstshmdata
|
||||
HINTS ${SHMDATA_LIBRARY_DIRS}/gstreamer-1.0
|
||||
)
|
||||
if(GSTREAMER_SHMDATA_LIBRARY)
|
||||
add_definitions(-DGSTREAMER_SHMDATA_PLUGIN=\"${GSTREAMER_SHMDATA_LIBRARY}\")
|
||||
endif()
|
||||
|
||||
macro_log_feature(GSTREAMER_SHMDATA_LIBRARY "GStreamerPluginShmdata" "Plugin to share any flow" "https://gitlab.com/sat-mtl/tools/shmdata" FALSE)
|
||||
endif(SHMDATA_FOUND)
|
||||
|
||||
endif(UNIX)
|
||||
|
||||
|
||||
# show message about found libs
|
||||
macro_display_feature_log()
|
||||
|
||||
#####
|
||||
##### Locally built libraries
|
||||
#####
|
||||
|
||||
#
|
||||
# GLAD
|
||||
#
|
||||
set(GLAD_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/glad/include)
|
||||
set(GLAD_LIBRARIES GLAD)
|
||||
set(GLAD_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/ext/glad/include)
|
||||
include_directories(
|
||||
${GLAD_INCLUDE_DIRS}
|
||||
)
|
||||
add_library(GLAD "${CMAKE_CURRENT_SOURCE_DIR}/ext/glad/src/glad.c")
|
||||
message(STATUS "Compiling 'GLAD' Open source multi-language OpenGL loader https://glad.dav1d.de -- ${GLAD_INCLUDE_DIR}")
|
||||
message(STATUS "Including 'GLAD' Open source multi-language OpenGL loader https://glad.dav1d.de -- ${GLAD_INCLUDE_DIRS}")
|
||||
|
||||
#
|
||||
# DEAR IMGUI
|
||||
#
|
||||
set(IMGUI_LIBRARIES IMGUI IMGUITEXTEDIT)
|
||||
set(IMGUI_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/ext/imgui)
|
||||
set(IMGUI_BACKEND_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/ext/imgui/examples)
|
||||
include_directories(
|
||||
${IMGUI_INCLUDE_DIRS}
|
||||
${IMGUI_BACKEND_INCLUDE_DIRS}
|
||||
)
|
||||
set(IMGUI_SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ext/imgui/imgui.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ext/imgui//imgui_demo.cpp
|
||||
@@ -154,18 +348,23 @@ set(IMGUI_SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ext/imgui//imgui_widgets.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ext/imgui/examples/imgui_impl_glfw.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ext/imgui/examples/imgui_impl_opengl3.cpp
|
||||
)
|
||||
set(IMGUI_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/imgui)
|
||||
)
|
||||
add_library(IMGUI "${IMGUI_SRCS}")
|
||||
target_compile_definitions(IMGUI PRIVATE "IMGUI_IMPL_OPENGL_LOADER_GLAD")
|
||||
message(STATUS "Compiling 'Dear ImGui' from https://github.com/ocornut/imgui.git -- ${IMGUI_INCLUDE_DIR}")
|
||||
target_compile_definitions(IMGUI PRIVATE "IMGUI_USE_STB_SPRINTF")
|
||||
message(STATUS "Compiling 'Dear ImGui' from https://github.com/ocornut/imgui.git -- ${IMGUI_INCLUDE_DIRS}")
|
||||
|
||||
#
|
||||
# TINY XML 2
|
||||
# ImGui Color Text Editor
|
||||
#
|
||||
set(TINYXML2_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/tinyxml2)
|
||||
add_library(TINYXML2 "${CMAKE_CURRENT_SOURCE_DIR}/ext/tinyxml2/tinyxml2.cpp")
|
||||
message(STATUS "Compiling 'TinyXML2' from https://github.com/leethomason/tinyxml2.git -- ${TINYXML2_INCLUDE_DIR}")
|
||||
set(IMGUITEXTEDIT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/ImGuiColorTextEdit)
|
||||
include_directories(
|
||||
${IMGUITEXTEDIT_INCLUDE_DIR}
|
||||
)
|
||||
add_library(IMGUITEXTEDIT "${CMAKE_CURRENT_SOURCE_DIR}/ext/ImGuiColorTextEdit/TextEditor.cpp")
|
||||
set_property(TARGET IMGUITEXTEDIT PROPERTY CXX_STANDARD 17)
|
||||
message(STATUS "Compiling 'ImGuiColorTextEdit' from https://github.com/BalazsJako/ImGuiColorTextEdit -- ${IMGUITEXTEDIT_INCLUDE_DIR}")
|
||||
|
||||
|
||||
#
|
||||
# OSCPack
|
||||
@@ -184,154 +383,94 @@ set(OSCPACK_SRCS
|
||||
${OSCPACK_PLATFORM_DIR}/NetworkingUtils.cpp
|
||||
${OSCPACK_PLATFORM_DIR}/UdpSocket.cpp
|
||||
)
|
||||
set(OSCPACK_LIBRARIES OSCPACK)
|
||||
set(OSCPACK_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/OSCPack)
|
||||
include_directories(
|
||||
${OSCPACK_INCLUDE_DIR}
|
||||
)
|
||||
add_library(OSCPACK "${OSCPACK_SRCS}")
|
||||
message(STATUS "Compiling 'OSCPack' from http://www.rossbencina.com/code/oscpack -- ${OSCPACK_INCLUDE_DIR}")
|
||||
|
||||
|
||||
#####
|
||||
##### Ressources
|
||||
#####
|
||||
|
||||
set(RSC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/rsc)
|
||||
|
||||
#
|
||||
# FILE DIALOG: use tinyfiledialog for all except Linux
|
||||
# Fonts
|
||||
#
|
||||
if(UNIX)
|
||||
if (APPLE)
|
||||
set(TINYFD_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/tfd)
|
||||
add_library(TINYFD "${CMAKE_CURRENT_SOURCE_DIR}/ext/tfd/tinyfiledialogs.c")
|
||||
message(STATUS "Compiling 'TinyFileDialog' from https://github.com/native-toolkit/tinyfiledialogs.git -- ${TINYFD_INCLUDE_DIR}.")
|
||||
set(TINYFD_LIBRARY TINYFD)
|
||||
endif()
|
||||
else()
|
||||
set(TINYFD_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/tfd)
|
||||
add_library(TINYFD "${CMAKE_CURRENT_SOURCE_DIR}/ext/tfd/tinyfiledialogs.c")
|
||||
message(STATUS "Compiling 'TinyFileDialog' from https://github.com/native-toolkit/tinyfiledialogs.git -- ${TINYFD_INCLUDE_DIR}.")
|
||||
set(TINYFD_LIBRARY TINYFD)
|
||||
|
||||
file(GLOB_RECURSE ROBOTO_REGULAR "${RSC_DIR}/*/Roboto-Regular.ttf")
|
||||
if(NOT ROBOTO_REGULAR)
|
||||
file(GLOB_RECURSE ROBOTO_REGULAR "/usr/share/fonts/*/Roboto-Regular.ttf")
|
||||
message(STATUS "Copy ${ROBOTO_REGULAR} to ${RSC_DIR}/fonts")
|
||||
file(COPY ${ROBOTO_REGULAR} DESTINATION ${RSC_DIR}/fonts)
|
||||
set(ROBOTO_REGULAR "${RSC_DIR}/fonts/Roboto-Regular.ttf")
|
||||
endif()
|
||||
|
||||
#
|
||||
# ImGui Color Text Editor
|
||||
#
|
||||
set(IMGUITEXTEDIT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/ImGuiColorTextEdit)
|
||||
set(IMGUITEXTEDIT_SRC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ext/ImGuiColorTextEdit/TextEditor.cpp
|
||||
)
|
||||
message(STATUS "Including 'ImGuiColorTextEdit' from https://github.com/BalazsJako/ImGuiColorTextEdit -- ${IMGUITEXTEDIT_INCLUDE_DIR}")
|
||||
|
||||
#
|
||||
# STB
|
||||
#
|
||||
set(STB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/stb)
|
||||
add_definitions(-DIMGUI_USE_STB_SPRINTF)
|
||||
message(STATUS "Including 'STB Nothings' from https://github.com/nothings/stb -- ${STB_INCLUDE_DIR}")
|
||||
|
||||
#
|
||||
# DIRENT (windows only)
|
||||
if(WIN32)
|
||||
set(DIRENT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/Dirent/include)
|
||||
message(STATUS "Including 'Dirent' from https://github.com/tronkko/dirent -- ${DIRENT_INCLUDE_DIR}.")
|
||||
file(GLOB_RECURSE ROBOTO_BOLD "${RSC_DIR}/*/Roboto-Bold.ttf")
|
||||
if(NOT ROBOTO_BOLD)
|
||||
file(GLOB_RECURSE ROBOTO_BOLD "/usr/share/fonts/*/Roboto-Bold.ttf")
|
||||
message(STATUS "Copy ${ROBOTO_BOLD} to ${RSC_DIR}/fonts")
|
||||
file(COPY ${ROBOTO_BOLD} DESTINATION ${RSC_DIR}/fonts)
|
||||
set(ROBOTO_BOLD "${RSC_DIR}/fonts/Roboto-Bold.ttf")
|
||||
endif()
|
||||
|
||||
#
|
||||
# Application
|
||||
#
|
||||
file(GLOB_RECURSE ROBOTO_ITALIC "${RSC_DIR}/*/Roboto-Italic.ttf")
|
||||
if(NOT ROBOTO_ITALIC)
|
||||
file(GLOB_RECURSE ROBOTO_ITALIC "/usr/share/fonts/*/Roboto-Italic.ttf")
|
||||
message(STATUS "Copy ${ROBOTO_ITALIC} to ${RSC_DIR}/fonts")
|
||||
file(COPY ${ROBOTO_ITALIC} DESTINATION ${RSC_DIR}/fonts)
|
||||
set(ROBOTO_ITALIC "${RSC_DIR}/fonts/Roboto-Italic.ttf")
|
||||
endif()
|
||||
|
||||
file(GLOB_RECURSE HACK_REGULAR "${RSC_DIR}/*/Hack-Regular.ttf")
|
||||
if(NOT HACK_REGULAR)
|
||||
file(GLOB_RECURSE HACK_REGULAR "/usr/share/fonts/*/Hack-Regular.ttf")
|
||||
message(STATUS "Copy ${HACK_REGULAR} to ${RSC_DIR}/fonts")
|
||||
file(COPY ${HACK_REGULAR} DESTINATION ${RSC_DIR}/fonts)
|
||||
set(HACK_REGULAR "${RSC_DIR}/fonts/Hack-Regular.ttf")
|
||||
endif()
|
||||
|
||||
file(GLOB_RECURSE AWESOME_SOLID "${RSC_DIR}/*/fa-solid-900.ttf")
|
||||
if(NOT AWESOME_SOLID)
|
||||
file(GLOB_RECURSE AWESOME_SOLID "/usr/share/fonts/*/fa-solid-900.ttf")
|
||||
message(STATUS "Copy ${AWESOME_SOLID} to ${RSC_DIR}/fonts")
|
||||
file(COPY ${AWESOME_SOLID} DESTINATION ${RSC_DIR}/fonts)
|
||||
set(AWESOME_SOLID "${RSC_DIR}/fonts/fa-solid-900.ttf")
|
||||
endif()
|
||||
|
||||
file(GLOB_RECURSE AWESOME_REGULAR "${RSC_DIR}/*/fa-regular-400.ttf")
|
||||
if(NOT AWESOME_REGULAR)
|
||||
file(GLOB_RECURSE AWESOME_REGULAR "/usr/share/fonts/*/fa-regular-400.ttf")
|
||||
message(STATUS "Copy ${AWESOME_REGULAR} to ${RSC_DIR}/fonts")
|
||||
file(COPY ${AWESOME_REGULAR} DESTINATION ${RSC_DIR}/fonts)
|
||||
set(AWESOME_REGULAR "${RSC_DIR}/fonts/fa-regular-400.ttf")
|
||||
endif()
|
||||
|
||||
# Setup the environment
|
||||
include_directories(
|
||||
${GSTREAMER_INCLUDE_DIR}
|
||||
${GSTREAMER_AUDIO_INCLUDE_DIR}
|
||||
${GSTREAMER_VIDEO_INCLUDE_DIR}
|
||||
${GSTREAMER_BASE_INCLUDE_DIR}
|
||||
${GSTREAMER_APP_INCLUDE_DIR}
|
||||
${GSTREAMER_PBUTILS_INCLUDE_DIR}
|
||||
${GSTREAMER_GL_INCLUDE_DIR}
|
||||
${GLFW3_INCLUDE_DIRS}
|
||||
${ICU_INCLUDE_DIRS}
|
||||
${GLM_INCLUDE_DIRS}
|
||||
${GLIB2_INCLUDE_DIR}
|
||||
${GLAD_INCLUDE_DIR}
|
||||
${IMGUI_INCLUDE_DIR}
|
||||
${IMGUI_INCLUDE_DIR}/examples
|
||||
${IMGUITEXTEDIT_INCLUDE_DIR}
|
||||
${TINYXML2_INCLUDE_DIR}
|
||||
${TINYFD_INCLUDE_DIR}
|
||||
${STB_INCLUDE_DIR}
|
||||
${DIRENT_INCLUDE_DIR}
|
||||
${OSCPACK_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
link_directories(
|
||||
${GLFW3_LIBRARY_DIRS}
|
||||
${ICU_LIBRARY_DIRS}
|
||||
"${RSC_DIR}/fonts"
|
||||
)
|
||||
|
||||
|
||||
set(VMIX_BINARY "vimix")
|
||||
set(VMIX_SRCS
|
||||
main.cpp
|
||||
Log.cpp
|
||||
BaseToolkit.cpp
|
||||
Shader.cpp
|
||||
ImageShader.cpp
|
||||
ImageProcessingShader.cpp
|
||||
UpdateCallback.cpp
|
||||
Scene.cpp
|
||||
Primitives.cpp
|
||||
Mesh.cpp
|
||||
Decorations.cpp
|
||||
View.cpp
|
||||
RenderView.cpp
|
||||
GeometryView.cpp
|
||||
MixingView.cpp
|
||||
MixingGroup.cpp
|
||||
LayerView.cpp
|
||||
TextureView.cpp
|
||||
TransitionView.cpp
|
||||
Source.cpp
|
||||
SourceList.cpp
|
||||
Session.cpp
|
||||
Selection.cpp
|
||||
SessionSource.cpp
|
||||
SessionVisitor.cpp
|
||||
Interpolator.cpp
|
||||
SessionCreator.cpp
|
||||
SessionParser.cpp
|
||||
Mixer.cpp
|
||||
FrameGrabber.cpp
|
||||
Recorder.cpp
|
||||
Streamer.cpp
|
||||
Loopback.cpp
|
||||
Settings.cpp
|
||||
Screenshot.cpp
|
||||
Resource.cpp
|
||||
Timeline.cpp
|
||||
Stream.cpp
|
||||
MediaPlayer.cpp
|
||||
MediaSource.cpp
|
||||
StreamSource.cpp
|
||||
PatternSource.cpp
|
||||
DeviceSource.cpp
|
||||
NetworkSource.cpp
|
||||
MultiFileSource.cpp
|
||||
FrameBuffer.cpp
|
||||
RenderingManager.cpp
|
||||
UserInterfaceManager.cpp
|
||||
PickingVisitor.cpp
|
||||
BoundingBoxVisitor.cpp
|
||||
DrawVisitor.cpp
|
||||
SearchVisitor.cpp
|
||||
ImGuiToolkit.cpp
|
||||
ImGuiVisitor.cpp
|
||||
InfoVisitor.cpp
|
||||
GstToolkit.cpp
|
||||
GlmToolkit.cpp
|
||||
SystemToolkit.cpp
|
||||
DialogToolkit.cpp
|
||||
tinyxml2Toolkit.cpp
|
||||
NetworkToolkit.cpp
|
||||
Connection.cpp
|
||||
ActionManager.cpp
|
||||
Overlay.cpp
|
||||
)
|
||||
#
|
||||
# CMake RC module
|
||||
#
|
||||
|
||||
# Include the CMake RC module
|
||||
include(CMakeRC)
|
||||
message(STATUS "Using 'CMakeRC ' from https://github.com/vector-of-bool/cmrc.git -- ${CMAKE_MODULE_PATH}.")
|
||||
|
||||
# set the files to package
|
||||
set(VMIX_RSC_FILES
|
||||
${ROBOTO_REGULAR}
|
||||
${ROBOTO_BOLD}
|
||||
${ROBOTO_ITALIC}
|
||||
${HACK_REGULAR}
|
||||
${AWESOME_REGULAR}
|
||||
${AWESOME_SOLID}
|
||||
./rsc/shaders/simple.fs
|
||||
./rsc/shaders/simple.vs
|
||||
./rsc/shaders/image.fs
|
||||
@@ -344,12 +483,6 @@ set(VMIX_RSC_FILES
|
||||
./rsc/shaders/image.vs
|
||||
./rsc/shaders/imageprocessing.fs
|
||||
./rsc/shaders/imageblending.fs
|
||||
./rsc/fonts/Hack-Regular.ttf
|
||||
./rsc/fonts/Roboto-Regular.ttf
|
||||
./rsc/fonts/Roboto-Bold.ttf
|
||||
./rsc/fonts/Roboto-Italic.ttf
|
||||
./rsc/fonts/fa-regular-400.ttf
|
||||
./rsc/fonts/fa-solid-900.ttf
|
||||
./rsc/images/mask_vignette.png
|
||||
./rsc/images/mask_halo.png
|
||||
./rsc/images/mask_glow.png
|
||||
@@ -360,6 +493,7 @@ set(VMIX_RSC_FILES
|
||||
./rsc/images/mask_linear_left.png
|
||||
./rsc/images/mask_linear_right.png
|
||||
./rsc/images/vimix_256x256.png
|
||||
./rsc/images/vimix_crow_white.png
|
||||
./rsc/images/icons.dds
|
||||
./rsc/images/gradient_0_cross_linear.png
|
||||
./rsc/images/gradient_1_black_linear.png
|
||||
@@ -404,6 +538,7 @@ set(VMIX_RSC_FILES
|
||||
./rsc/mesh/shadow_perspective.ply
|
||||
./rsc/mesh/point.ply
|
||||
./rsc/mesh/square_point.ply
|
||||
./rsc/mesh/triangle_point.ply
|
||||
./rsc/mesh/icon_video.ply
|
||||
./rsc/mesh/icon_image.ply
|
||||
./rsc/mesh/icon_render.ply
|
||||
@@ -428,92 +563,65 @@ set(VMIX_RSC_FILES
|
||||
./rsc/mesh/icon_crop.ply
|
||||
./rsc/mesh/icon_eye.ply
|
||||
./rsc/mesh/icon_eye_slash.ply
|
||||
./rsc/mesh/icon_vector_square_slash.ply
|
||||
./rsc/mesh/icon_tv.ply
|
||||
./rsc/mesh/icon_cube.ply
|
||||
./rsc/mesh/icon_sequence.ply
|
||||
./rsc/mesh/icon_receive.ply
|
||||
./rsc/mesh/h_line.ply
|
||||
./rsc/mesh/h_mark.ply
|
||||
./rsc/shaders/filters/default.glsl
|
||||
./rsc/shaders/filters/bloom.glsl
|
||||
./rsc/shaders/filters/bokeh.glsl
|
||||
./rsc/shaders/filters/talk.glsl
|
||||
./rsc/shaders/filters/fisheye.glsl
|
||||
./rsc/shaders/filters/blur.glsl
|
||||
./rsc/shaders/filters/blur_1.glsl
|
||||
./rsc/shaders/filters/blur_2.glsl
|
||||
./rsc/shaders/filters/lens_1.glsl
|
||||
./rsc/shaders/filters/lens_2.glsl
|
||||
./rsc/shaders/filters/hashedblur.glsl
|
||||
./rsc/shaders/filters/circularblur.glsl
|
||||
./rsc/shaders/filters/hashederosion.glsl
|
||||
./rsc/shaders/filters/hasheddilation.glsl
|
||||
./rsc/shaders/filters/sharp.glsl
|
||||
./rsc/shaders/filters/sharpen.glsl
|
||||
./rsc/shaders/filters/sharpen_1.glsl
|
||||
./rsc/shaders/filters/sharpen_2.glsl
|
||||
./rsc/shaders/filters/contour_2.glsl
|
||||
./rsc/shaders/filters/sharpenedge.glsl
|
||||
./rsc/shaders/filters/chromakey.glsl
|
||||
./rsc/shaders/filters/lumakey.glsl
|
||||
./rsc/shaders/filters/coloralpha.glsl
|
||||
./rsc/shaders/filters/bilinear.glsl
|
||||
./rsc/shaders/filters/edge.glsl
|
||||
./rsc/shaders/filters/sobel.glsl
|
||||
./rsc/shaders/filters/freichen.glsl
|
||||
./rsc/shaders/filters/kuwahara.glsl
|
||||
./rsc/shaders/filters/pixelate.glsl
|
||||
./rsc/shaders/filters/focus.glsl
|
||||
./rsc/shaders/filters/denoise.glsl
|
||||
./rsc/shaders/filters/noise.glsl
|
||||
./rsc/shaders/filters/grain.glsl
|
||||
./rsc/shaders/filters/stippling.glsl
|
||||
./rsc/shaders/filters/dithering.glsl
|
||||
./rsc/shaders/filters/erosion.glsl
|
||||
./rsc/shaders/filters/dilation.glsl
|
||||
./rsc/shaders/filters/tophat.glsl
|
||||
./rsc/shaders/filters/blackhat.glsl
|
||||
./rsc/shaders/filters/resample_double.glsl
|
||||
./rsc/shaders/filters/resample_half.glsl
|
||||
./rsc/shaders/filters/resample_quarter.glsl
|
||||
./rsc/shaders/filters/earlybird.glsl
|
||||
./rsc/shaders/filters/logo.glsl
|
||||
./rsc/shaders/filters/whitebalance.glsl
|
||||
)
|
||||
|
||||
# Include the CMake RC module
|
||||
include(CMakeRC)
|
||||
cmrc_add_resource_library(vmix-resources ALIAS vmix::rc NAMESPACE vmix WHENCE rsc ${VMIX_RSC_FILES})
|
||||
message(STATUS "Using 'CMakeRC ' from https://github.com/vector-of-bool/cmrc.git -- ${CMAKE_MODULE_PATH}.")
|
||||
|
||||
### DEFINE THE TARGET (OS specific)
|
||||
|
||||
IF(APPLE)
|
||||
|
||||
# set icon
|
||||
set(MACOSX_BUNDLE_ICON vimix.icns)
|
||||
set(MACOSX_BUNDLE_ICON_FILE ${CMAKE_SOURCE_DIR}/osx/${MACOSX_BUNDLE_ICON})
|
||||
# set where in the bundle to put the icns file
|
||||
set_source_files_properties(${MACOSX_BUNDLE_ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
|
||||
# create the application
|
||||
add_executable(${VMIX_BINARY} MACOSX_BUNDLE
|
||||
${VMIX_SRCS}
|
||||
${IMGUITEXTEDIT_SRC}
|
||||
${MACOSX_BUNDLE_ICON_FILE}
|
||||
)
|
||||
|
||||
# set the Info.plist file
|
||||
set(MACOSX_BUNDLE_PLIST_FILE ${CMAKE_SOURCE_DIR}/osx/Info.plist)
|
||||
set_target_properties(${VMIX_BINARY} PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${MACOSX_BUNDLE_PLIST_FILE})
|
||||
|
||||
set(PLATFORM_LIBS
|
||||
"-framework CoreFoundation"
|
||||
"-framework Appkit"
|
||||
)
|
||||
|
||||
ELSE(APPLE)
|
||||
|
||||
link_directories (${GTK3_LIBRARY_DIRS})
|
||||
|
||||
add_executable(${VMIX_BINARY}
|
||||
${VMIX_SRCS}
|
||||
${IMGUITEXTEDIT_SRC}
|
||||
)
|
||||
|
||||
set(PLATFORM_LIBS
|
||||
GTK::GTK
|
||||
)
|
||||
|
||||
ENDIF(APPLE)
|
||||
|
||||
### COMPILE THE TARGET (all OS)
|
||||
|
||||
set_property(TARGET ${VMIX_BINARY} PROPERTY CXX_STANDARD 17)
|
||||
set_property(TARGET ${VMIX_BINARY} PROPERTY C_STANDARD 11)
|
||||
|
||||
target_compile_definitions(${VMIX_BINARY} PUBLIC "IMGUI_IMPL_OPENGL_LOADER_GLAD")
|
||||
|
||||
target_link_libraries(${VMIX_BINARY} LINK_PRIVATE
|
||||
vmix::rc
|
||||
glm::glm
|
||||
GLAD
|
||||
TINYXML2
|
||||
IMGUI
|
||||
OSCPACK
|
||||
${TINYFD_LIBRARY}
|
||||
${GLFW3_LIBRARIES}
|
||||
${ICU_LIBRARIES}
|
||||
${CMAKE_DL_LIBS}
|
||||
${GOBJECT_LIBRARIES}
|
||||
${GSTREAMER_LIBRARY}
|
||||
${GSTREAMER_BASE_LIBRARY}
|
||||
${GSTREAMER_APP_LIBRARY}
|
||||
${GSTREAMER_AUDIO_LIBRARY}
|
||||
${GSTREAMER_VIDEO_LIBRARY}
|
||||
${GSTREAMER_PBUTILS_LIBRARY}
|
||||
${GSTREAMER_GL_LIBRARY}
|
||||
Threads::Threads
|
||||
PNG::PNG
|
||||
${PLATFORM_LIBS}
|
||||
)
|
||||
|
||||
|
||||
### DEFINE THE PACKAGING (all OS)
|
||||
#####
|
||||
##### DEFINE THE PACKAGING (all OS)
|
||||
#####
|
||||
|
||||
SET(CPACK_PACKAGE_NAME "vimix")
|
||||
SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "vimix\nReal-time video mixing for live performance.")
|
||||
@@ -530,120 +638,15 @@ SET(CPACK_SOURCE_IGNORE_FILES
|
||||
"/\\\\.gitignore$"
|
||||
"/\\\\.gitmodules$"
|
||||
)
|
||||
|
||||
|
||||
# optimize size ?
|
||||
SET(CPACK_STRIP_FILES TRUE)
|
||||
|
||||
### DEFINE THE PACKAGING (OS specific)
|
||||
|
||||
IF(APPLE)
|
||||
|
||||
# Bundle target
|
||||
set(CPACK_GENERATOR DragNDrop)
|
||||
set(CPACK_BINARY_DRAGNDROP ON)
|
||||
|
||||
# OSX cpack info
|
||||
set(CPACK_SYSTEM_NAME "OSX_${CMAKE_OSX_DEPLOYMENT_TARGET}_${CMAKE_OSX_ARCHITECTURES}")
|
||||
|
||||
install(TARGETS ${VMIX_BINARY}
|
||||
CONFIGURATIONS Release RelWithDebInfo
|
||||
BUNDLE DESTINATION . COMPONENT Runtime
|
||||
RUNTIME DESTINATION bin COMPONENT Runtime
|
||||
)
|
||||
|
||||
# create GST plugins directory in Bundle Resources subfolder
|
||||
set(plugin_dest_dir vimix.app/Contents/Resources/)
|
||||
|
||||
### TODO configure auto to find installation dir of gst
|
||||
|
||||
message(STATUS "install gst-plugins ${PKG_GSTREAMER_PLUGIN_DIR}")
|
||||
message(STATUS "install gst-plugins-base ${PKG_GSTREAMER_BASE_PLUGIN_DIR}")
|
||||
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PKG_GSTREAMER_PLUGINS_BAD gstreamer-plugins-bad-${GSTREAMER_ABI_VERSION})
|
||||
set(PKG_GSTREAMER_BAD_PLUGIN_DIR ${PKG_GSTREAMER_PLUGINS_BAD_LIBDIR}/gstreamer-${GSTREAMER_ABI_VERSION})
|
||||
message(STATUS "install gst-plugins-bad ${PKG_GSTREAMER_BAD_PLUGIN_DIR}")
|
||||
endif()
|
||||
|
||||
|
||||
# intall the gst-plugin-scanner program (used by plugins at load time)
|
||||
set(PKG_GSTREAMER_SCANNER "${PKG_GSTREAMER_PREFIX}/libexec/gstreamer-1.0/gst-plugin-scanner")
|
||||
message(STATUS "install gst-plugin-scanner ${PKG_GSTREAMER_SCANNER}")
|
||||
install(FILES "${PKG_GSTREAMER_SCANNER}"
|
||||
DESTINATION "${plugin_dest_dir}"
|
||||
PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
|
||||
COMPONENT Runtime
|
||||
)
|
||||
|
||||
# Install the gst-plugins (all those installed with brew )
|
||||
install(DIRECTORY "${PKG_GSTREAMER_PLUGIN_DIR}" DESTINATION "${plugin_dest_dir}" COMPONENT Runtime)
|
||||
install(DIRECTORY "${PKG_GSTREAMER_BASE_PLUGIN_DIR}" DESTINATION "${plugin_dest_dir}" COMPONENT Runtime)
|
||||
install(DIRECTORY "${PKG_GSTREAMER_BAD_PLUGIN_DIR}" DESTINATION "${plugin_dest_dir}" COMPONENT Runtime)
|
||||
|
||||
install(DIRECTORY "/usr/local/Cellar/gst-plugins-good/1.18.4/lib/gstreamer-1.0" DESTINATION "${plugin_dest_dir}" COMPONENT Runtime)
|
||||
install(DIRECTORY "/usr/local/Cellar/gst-plugins-ugly/1.18.4_1/lib/gstreamer-1.0" DESTINATION "${plugin_dest_dir}" COMPONENT Runtime)
|
||||
install(DIRECTORY "/usr/local/Cellar/gst-libav/1.18.4/lib/gstreamer-1.0" DESTINATION "${plugin_dest_dir}" COMPONENT Runtime)
|
||||
|
||||
# install locally recompiled & installed gst-plugins (because not included in brew package)
|
||||
install(FILES "/usr/local/lib/gstreamer-1.0/libgstapplemedia.dylib" DESTINATION "${plugin_dest_dir}/gstreamer-1.0" COMPONENT Runtime)
|
||||
install(FILES "/usr/local/lib/gstreamer-1.0/libgstde265.dylib" DESTINATION "${plugin_dest_dir}/gstreamer-1.0" COMPONENT Runtime)
|
||||
install(FILES "/usr/local/lib/gstreamer-1.0/libgstx265.dylib" DESTINATION "${plugin_dest_dir}/gstreamer-1.0" COMPONENT Runtime)
|
||||
|
||||
# ICU DATA LIB GST dependency : undocumented and hacked here : seems to work
|
||||
# install(FILES "${ICU_LINK_LIBRARIES}" DESTINATION "${plugin_dest_dir}/gstreamer-1.0" COMPONENT Runtime)
|
||||
install(FILES "/usr/local/Cellar/icu4c/69.1/lib/libicudata.69.1.dylib" DESTINATION "${plugin_dest_dir}/gstreamer-1.0" RENAME "libicudata.69.dylib" COMPONENT Runtime)
|
||||
message(STATUS "install ${ICU_LINK_LIBRARIES} from ${ICU_LIBRARY_DIRS}")
|
||||
|
||||
# package runtime fixup bundle
|
||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/vimix.app")
|
||||
install(CODE "
|
||||
file(GLOB_RECURSE GSTPLUGINS \"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/gstreamer-1.0/*.dylib\")
|
||||
list(APPEND LIBS_PATH \"\${ICU_LIBRARY_DIRS}\")
|
||||
include(BundleUtilities)
|
||||
set(BU_CHMOD_BUNDLE_ITEMS TRUE)
|
||||
fixup_bundle(\"${APPS}\" \"\${GSTPLUGINS}\" \"${LIBS_PATH}\")
|
||||
"
|
||||
COMPONENT Runtime
|
||||
)
|
||||
|
||||
set(APPLE_CODESIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/osx/entitlements.plist")
|
||||
set(APPLE_CODESIGN_IDENTITY "" CACHE STRING "")
|
||||
string(LENGTH "${APPLE_CODESIGN_IDENTITY}" APPLE_CODESIGN_IDENTITY_LENGHT)
|
||||
if( ${APPLE_CODESIGN_IDENTITY_LENGHT} LESS 40 )
|
||||
message(STATUS "Not signing bundle. Specify APPLE_CODESIGN_IDENTITY to cmake before running cpack to sign")
|
||||
else()
|
||||
install(CODE "
|
||||
execute_process(COMMAND
|
||||
codesign --verbose=4 --deep --force
|
||||
--entitlements \"${APPLE_CODESIGN_ENTITLEMENTS}\"
|
||||
--sign \"${APPLE_CODESIGN_IDENTITY}\"
|
||||
\"${APPS}\" )
|
||||
"
|
||||
COMPONENT Runtime
|
||||
)
|
||||
endif()
|
||||
|
||||
# # package runtime fixup bundle and codesign
|
||||
# set(BUNDLE_NAME "vimix.app")
|
||||
# set(BUNDLE_LIBS_DIR "${plugin_dest_dir}/gstreamer-1.0")
|
||||
# set(BUNDLE_DIRS "${ICU_LIBRARY_DIRS}")
|
||||
# set(APPLE_CODESIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/osx/entitlements.plist")
|
||||
|
||||
# configure_file(cmake/modules/BundleInstall.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/BundleInstall.cmake" @ONLY)
|
||||
# install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/BundleInstall.cmake" COMPONENT Runtime)
|
||||
|
||||
ELSE(APPLE)
|
||||
|
||||
install(TARGETS ${VMIX_BINARY}
|
||||
CONFIGURATIONS Release RelWithDebInfo
|
||||
RUNTIME DESTINATION bin COMPONENT Runtime
|
||||
)
|
||||
|
||||
ENDIF(APPLE)
|
||||
|
||||
# Package full name
|
||||
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}_${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}_${CPACK_SYSTEM_NAME}")
|
||||
set(CPACK_STRIP_FILES TRUE)
|
||||
|
||||
|
||||
#####
|
||||
##### Build Application
|
||||
#####
|
||||
add_subdirectory(src)
|
||||
|
||||
|
||||
# To Create a package, run "cpack"
|
||||
include(CPack)
|
||||
|
||||
128
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
581
DeviceSource.cpp
@@ -1,581 +0,0 @@
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include <gst/pbutils/gstdiscoverer.h>
|
||||
#include <gst/pbutils/pbutils.h>
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "DeviceSource.h"
|
||||
|
||||
#include "defines.h"
|
||||
#include "ImageShader.h"
|
||||
#include "ImageProcessingShader.h"
|
||||
#include "Resource.h"
|
||||
#include "Decorations.h"
|
||||
#include "Stream.h"
|
||||
#include "Visitor.h"
|
||||
#include "Log.h"
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define DEVICE_DEBUG
|
||||
//#define GST_DEVICE_DEBUG
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if defined(APPLE)
|
||||
std::string gst_plugin_device = "avfvideosrc";
|
||||
std::string gst_plugin_vidcap = "avfvideosrc capture-screen=true";
|
||||
#else
|
||||
std::string gst_plugin_device = "v4l2src";
|
||||
std::string gst_plugin_vidcap = "ximagesrc";
|
||||
#endif
|
||||
|
||||
////EXAMPLE :
|
||||
///
|
||||
//v4l2deviceprovider, udev-probed=(boolean)true,
|
||||
//device.bus_path=(string)pci-0000:00:14.0-usb-0:2:1.0,
|
||||
//sysfs.path=(string)/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/video4linux/video0,
|
||||
//device.bus=(string)usb,
|
||||
//device.subsystem=(string)video4linux,
|
||||
//device.vendor.id=(string)1bcf,
|
||||
//device.vendor.name=(string)"Sunplus\\x20IT\\x20Co\\x20",
|
||||
//device.product.id=(string)2286,
|
||||
//device.product.name=(string)"AUSDOM\ FHD\ Camera:\ AUSDOM\ FHD\ C",
|
||||
//device.serial=(string)Sunplus_IT_Co_AUSDOM_FHD_Camera,
|
||||
//device.capabilities=(string):capture:,
|
||||
//device.api=(string)v4l2,
|
||||
//device.path=(string)/dev/video0,
|
||||
//v4l2.device.driver=(string)uvcvideo,
|
||||
//v4l2.device.card=(string)"AUSDOM\ FHD\ Camera:\ AUSDOM\ FHD\ C",
|
||||
//v4l2.device.bus_info=(string)usb-0000:00:14.0-2,
|
||||
//v4l2.device.version=(uint)328748,
|
||||
//v4l2.device.capabilities=(uint)2225078273,
|
||||
//v4l2.device.device_caps=(uint)69206017;
|
||||
//Device added: AUSDOM FHD Camera: AUSDOM FHD C - v4l2src device=/dev/video0
|
||||
|
||||
//v4l2deviceprovider, udev-probed=(boolean)true,
|
||||
//device.bus_path=(string)pci-0000:00:14.0-usb-0:4:1.0,
|
||||
//sysfs.path=(string)/sys/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.0/video4linux/video2,
|
||||
//device.bus=(string)usb,
|
||||
//device.subsystem=(string)video4linux,
|
||||
//device.vendor.id=(string)046d,
|
||||
//device.vendor.name=(string)046d,
|
||||
//device.product.id=(string)080f,
|
||||
//device.product.name=(string)"UVC\ Camera\ \(046d:080f\)",
|
||||
//device.serial=(string)046d_080f_3EA77580,
|
||||
//device.capabilities=(string):capture:,
|
||||
//device.api=(string)v4l2,
|
||||
//device.path=(string)/dev/video2,
|
||||
//v4l2.device.driver=(string)uvcvideo,
|
||||
//v4l2.device.card=(string)"UVC\ Camera\ \(046d:080f\)",
|
||||
//v4l2.device.bus_info=(string)usb-0000:00:14.0-4,
|
||||
//v4l2.device.version=(uint)328748,
|
||||
//v4l2.device.capabilities=(uint)2225078273,
|
||||
//v4l2.device.device_caps=(uint)69206017; // decimal of hexadecimal v4l code Device Caps : 0x04200001
|
||||
//Device added: UVC Camera (046d:080f) - v4l2src device=/dev/video2
|
||||
|
||||
std::string pipelineForDevice(GstDevice *device, uint index)
|
||||
{
|
||||
std::ostringstream pipe;
|
||||
const gchar *str = gst_structure_get_string(gst_device_get_properties(device), "device.api");
|
||||
|
||||
if (str && gst_plugin_device.find(str) != std::string::npos)
|
||||
{
|
||||
pipe << gst_plugin_device;
|
||||
|
||||
#if defined(APPLE)
|
||||
pipe << " device-index=" << index;
|
||||
#else
|
||||
str = gst_structure_get_string(gst_device_get_properties(device), "device.path");
|
||||
if (str)
|
||||
pipe << " device=" << str;
|
||||
#endif
|
||||
}
|
||||
|
||||
return pipe.str();
|
||||
}
|
||||
|
||||
gboolean
|
||||
Device::callback_device_monitor (GstBus *, GstMessage * message, gpointer )
|
||||
{
|
||||
GstDevice *device;
|
||||
gchar *name;
|
||||
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_DEVICE_ADDED: {
|
||||
gst_message_parse_device_added (message, &device);
|
||||
name = gst_device_get_display_name (device);
|
||||
|
||||
// ignore if already in the list
|
||||
if ( std::find(manager().src_name_.begin(), manager().src_name_.end(), name) != manager().src_name_.end())
|
||||
break;
|
||||
|
||||
manager().src_name_.push_back(name);
|
||||
#ifdef GST_DEVICE_DEBUG
|
||||
gchar *stru = gst_structure_to_string( gst_device_get_properties(device) );
|
||||
g_print("\nDevice %s plugged : %s\n", name, stru);
|
||||
g_free (stru);
|
||||
#endif
|
||||
g_free (name);
|
||||
|
||||
std::string p = pipelineForDevice(device, manager().src_description_.size());
|
||||
manager().src_description_.push_back(p);
|
||||
|
||||
DeviceConfigSet confs = getDeviceConfigs(p);
|
||||
manager().src_config_.push_back(confs);
|
||||
|
||||
manager().list_uptodate_ = false;
|
||||
|
||||
gst_object_unref (device);
|
||||
}
|
||||
break;
|
||||
case GST_MESSAGE_DEVICE_REMOVED: {
|
||||
gst_message_parse_device_removed (message, &device);
|
||||
name = gst_device_get_display_name (device);
|
||||
manager().remove(name);
|
||||
#ifdef GST_DEVICE_DEBUG
|
||||
g_print("\nDevice %s unplugged\n", name);
|
||||
#endif
|
||||
g_free (name);
|
||||
|
||||
manager().list_uptodate_ = false;
|
||||
|
||||
gst_object_unref (device);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
void Device::remove(const char *device)
|
||||
{
|
||||
std::vector< std::string >::iterator nameit = src_name_.begin();
|
||||
std::vector< std::string >::iterator descit = src_description_.begin();
|
||||
std::vector< DeviceConfigSet >::iterator coit = src_config_.begin();
|
||||
while (nameit != src_name_.end()){
|
||||
|
||||
if ( (*nameit).compare(device) == 0 )
|
||||
{
|
||||
src_name_.erase(nameit);
|
||||
src_description_.erase(descit);
|
||||
src_config_.erase(coit);
|
||||
break;
|
||||
}
|
||||
|
||||
++nameit;
|
||||
++descit;
|
||||
++coit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Device::Device()
|
||||
{
|
||||
GstBus *bus;
|
||||
GstCaps *caps;
|
||||
|
||||
// create GStreamer device monitor to capture
|
||||
// when a device is plugged in or out
|
||||
monitor_ = gst_device_monitor_new ();
|
||||
|
||||
bus = gst_device_monitor_get_bus (monitor_);
|
||||
gst_bus_add_watch (bus, callback_device_monitor, NULL);
|
||||
gst_object_unref (bus);
|
||||
|
||||
caps = gst_caps_new_empty_simple ("video/x-raw");
|
||||
gst_device_monitor_add_filter (monitor_, "Video/Source", caps);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
gst_device_monitor_set_show_all_devices(monitor_, true);
|
||||
gst_device_monitor_start (monitor_);
|
||||
|
||||
// initial fill of the list
|
||||
GList *devices = gst_device_monitor_get_devices(monitor_);
|
||||
GList *tmp;
|
||||
for (tmp = devices; tmp ; tmp = tmp->next ) {
|
||||
|
||||
GstDevice *device = (GstDevice *) tmp->data;
|
||||
|
||||
gchar *name = gst_device_get_display_name (device);
|
||||
src_name_.push_back(name);
|
||||
g_free (name);
|
||||
|
||||
#ifdef GST_DEVICE_DEBUG
|
||||
gchar *stru = gst_structure_to_string( gst_device_get_properties(device) );
|
||||
g_print("\nDevice %s already plugged : %s", name, stru);
|
||||
g_free (stru);
|
||||
#endif
|
||||
|
||||
std::string p = pipelineForDevice(device, src_description_.size());
|
||||
src_description_.push_back(p);
|
||||
|
||||
DeviceConfigSet confs = getDeviceConfigs(p);
|
||||
src_config_.push_back(confs);
|
||||
}
|
||||
g_list_free(devices);
|
||||
|
||||
// Add config for plugged screen
|
||||
src_name_.push_back("Screen capture");
|
||||
src_description_.push_back(gst_plugin_vidcap);
|
||||
|
||||
// Try to auto find resolution
|
||||
DeviceConfigSet confs = getDeviceConfigs(gst_plugin_vidcap);
|
||||
if (!confs.empty()) {
|
||||
// fix the framerate (otherwise at 1 FPS
|
||||
DeviceConfig best = *confs.rbegin();
|
||||
DeviceConfigSet confscreen;
|
||||
best.fps_numerator = 15;
|
||||
confscreen.insert(best);
|
||||
src_config_.push_back(confscreen);
|
||||
}
|
||||
|
||||
// TODO Use lib glfw to get monitors
|
||||
// TODO Detect auto removal of monitors
|
||||
|
||||
list_uptodate_ = true;
|
||||
}
|
||||
|
||||
|
||||
int Device::numDevices() const
|
||||
{
|
||||
return src_name_.size();
|
||||
}
|
||||
|
||||
bool Device::exists(const std::string &device) const
|
||||
{
|
||||
std::vector< std::string >::const_iterator d = std::find(src_name_.begin(), src_name_.end(), device);
|
||||
return d != src_name_.end();
|
||||
}
|
||||
|
||||
struct hasDevice: public std::unary_function<DeviceSource*, bool>
|
||||
{
|
||||
inline bool operator()(const DeviceSource* elem) const {
|
||||
return (elem && elem->device() == _d);
|
||||
}
|
||||
explicit hasDevice(const std::string &d) : _d(d) { }
|
||||
private:
|
||||
std::string _d;
|
||||
};
|
||||
|
||||
Source *Device::createSource(const std::string &device) const
|
||||
{
|
||||
Source *s = nullptr;
|
||||
|
||||
// find if a DeviceSource with this device is already registered
|
||||
std::list< DeviceSource *>::const_iterator d = std::find_if(device_sources_.begin(), device_sources_.end(), hasDevice(device));
|
||||
|
||||
// if already registered, clone the device source
|
||||
if ( d != device_sources_.end()) {
|
||||
CloneSource *cs = (*d)->clone();
|
||||
s = cs;
|
||||
}
|
||||
// otherwise, we are free to create a new device source
|
||||
else {
|
||||
DeviceSource *ds = new DeviceSource();
|
||||
ds->setDevice(device);
|
||||
s = ds;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
bool Device::unplugged(const std::string &device) const
|
||||
{
|
||||
if (list_uptodate_)
|
||||
return false;
|
||||
return !exists(device);
|
||||
}
|
||||
|
||||
std::string Device::name(int index) const
|
||||
{
|
||||
if (index > -1 && index < (int) src_name_.size())
|
||||
return src_name_[index];
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string Device::description(int index) const
|
||||
{
|
||||
if (index > -1 && index < (int) src_description_.size())
|
||||
return src_description_[index];
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
DeviceConfigSet Device::config(int index) const
|
||||
{
|
||||
if (index > -1 && index < (int) src_config_.size())
|
||||
return src_config_[index];
|
||||
else
|
||||
return DeviceConfigSet();
|
||||
}
|
||||
|
||||
int Device::index(const std::string &device) const
|
||||
{
|
||||
int i = -1;
|
||||
std::vector< std::string >::const_iterator p = std::find(src_name_.begin(), src_name_.end(), device);
|
||||
if (p != src_name_.end())
|
||||
i = std::distance(src_name_.begin(), p);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
DeviceSource::DeviceSource(uint64_t id) : StreamSource(id)
|
||||
{
|
||||
// create stream
|
||||
stream_ = new Stream;
|
||||
|
||||
// set symbol
|
||||
symbol_ = new Symbol(Symbol::CAMERA, glm::vec3(0.75f, 0.75f, 0.01f));
|
||||
symbol_->scale_.y = 1.5f;
|
||||
}
|
||||
|
||||
DeviceSource::~DeviceSource()
|
||||
{
|
||||
// unregister this device source
|
||||
Device::manager().device_sources_.remove(this);
|
||||
}
|
||||
|
||||
void DeviceSource::setDevice(const std::string &devicename)
|
||||
{
|
||||
device_ = devicename;
|
||||
Log::Notify("Creating Source with device '%s'", device_.c_str());
|
||||
|
||||
int index = Device::manager().index(device_);
|
||||
if (index > -1) {
|
||||
|
||||
// register this device source
|
||||
Device::manager().device_sources_.push_back(this);
|
||||
|
||||
// start filling in the gstreamer pipeline
|
||||
std::ostringstream pipeline;
|
||||
pipeline << Device::manager().description(index);
|
||||
|
||||
// test the device and get config
|
||||
DeviceConfigSet confs = Device::manager().config(index);
|
||||
#ifdef DEVICE_DEBUG
|
||||
Log::Info("Device %s supported configs:", devicename.c_str());
|
||||
for( DeviceConfigSet::iterator it = confs.begin(); it != confs.end(); ++it ){
|
||||
float fps = static_cast<float>((*it).fps_numerator) / static_cast<float>((*it).fps_denominator);
|
||||
Log::Info(" - %s %s %d x %d %.1f fps", (*it).stream.c_str(), (*it).format.c_str(), (*it).width, (*it).height, fps);
|
||||
}
|
||||
#endif
|
||||
DeviceConfig best = *confs.rbegin();
|
||||
float fps = static_cast<float>(best.fps_numerator) / static_cast<float>(best.fps_denominator);
|
||||
Log::Info("Device %s selected its optimal config: %s %s %dx%d@%.1ffps", device_.c_str(), best.stream.c_str(), best.format.c_str(), best.width, best.height, fps);
|
||||
|
||||
pipeline << " ! " << best.stream;
|
||||
if (!best.format.empty())
|
||||
pipeline << ",format=" << best.format;
|
||||
pipeline << ",framerate=" << best.fps_numerator << "/" << best.fps_denominator;
|
||||
pipeline << ",width=" << best.width;
|
||||
pipeline << ",height=" << best.height;
|
||||
|
||||
if ( best.stream.find("jpeg") != std::string::npos )
|
||||
pipeline << " ! jpegdec";
|
||||
|
||||
if ( device_.find("Screen") != std::string::npos )
|
||||
pipeline << " ! videoconvert ! video/x-raw,format=RGB ! queue max-size-buffers=3";
|
||||
|
||||
pipeline << " ! videoconvert";
|
||||
|
||||
// resize render buffer
|
||||
if (renderbuffer_)
|
||||
renderbuffer_->resize(best.width, best.height);
|
||||
|
||||
// open gstreamer
|
||||
stream_->open( pipeline.str(), best.width, best.height);
|
||||
stream_->play(true);
|
||||
|
||||
// will be ready after init and one frame rendered
|
||||
ready_ = false;
|
||||
}
|
||||
else
|
||||
Log::Warning("No such device '%s'", device_.c_str());
|
||||
}
|
||||
|
||||
void DeviceSource::accept(Visitor& v)
|
||||
{
|
||||
Source::accept(v);
|
||||
if (!failed())
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
bool DeviceSource::failed() const
|
||||
{
|
||||
return stream_->failed() || Device::manager().unplugged(device_);
|
||||
}
|
||||
|
||||
DeviceConfigSet Device::getDeviceConfigs(const std::string &src_description)
|
||||
{
|
||||
DeviceConfigSet configs;
|
||||
|
||||
// create dummy pipeline to be tested
|
||||
std::string description = src_description;
|
||||
description += " name=devsrc ! fakesink name=sink";
|
||||
|
||||
// parse pipeline descriptor
|
||||
GError *error = NULL;
|
||||
GstElement *pipeline_ = gst_parse_launch (description.c_str(), &error);
|
||||
if (error != NULL) {
|
||||
Log::Warning("DeviceSource Could not construct test pipeline %s:\n%s", description.c_str(), error->message);
|
||||
g_clear_error (&error);
|
||||
return configs;
|
||||
}
|
||||
|
||||
// get the pipeline element named "devsrc" from the Device class
|
||||
GstElement *elem = gst_bin_get_by_name (GST_BIN (pipeline_), "devsrc");
|
||||
if (elem) {
|
||||
|
||||
// initialize the pipeline
|
||||
GstStateChangeReturn ret = gst_element_set_state (pipeline_, GST_STATE_PAUSED);
|
||||
if (ret != GST_STATE_CHANGE_FAILURE) {
|
||||
|
||||
// get the first pad and its content
|
||||
GstIterator *iter = gst_element_iterate_src_pads(elem);
|
||||
GValue vPad = G_VALUE_INIT;
|
||||
GstPad* pad_ret = NULL;
|
||||
if (gst_iterator_next(iter, &vPad) == GST_ITERATOR_OK)
|
||||
{
|
||||
pad_ret = GST_PAD(g_value_get_object(&vPad));
|
||||
GstCaps *device_caps = gst_pad_query_caps (pad_ret, NULL);
|
||||
|
||||
// loop over all caps offered by the pad
|
||||
int C = gst_caps_get_size(device_caps);
|
||||
for (int c = 0; c < C; ++c) {
|
||||
// get GST cap
|
||||
GstStructure *decice_cap_struct = gst_caps_get_structure (device_caps, c);
|
||||
#ifdef GST_DEVICE_DEBUG
|
||||
gchar *capstext = gst_structure_to_string (decice_cap_struct);
|
||||
g_print("\nDevice caps: %s", capstext);
|
||||
g_free(capstext);
|
||||
#endif
|
||||
|
||||
// fill our config
|
||||
DeviceConfig config;
|
||||
|
||||
// not managing opengl texture-target types
|
||||
// TODO: support input devices texture-target video/x-raw(memory:GLMemory) for improved pipeline
|
||||
if ( gst_structure_has_field (decice_cap_struct, "texture-target"))
|
||||
continue;
|
||||
|
||||
// NAME : typically video/x-raw or image/jpeg
|
||||
config.stream = gst_structure_get_name (decice_cap_struct);
|
||||
|
||||
// FORMAT : typically BGRA or YUVY
|
||||
if ( gst_structure_has_field (decice_cap_struct, "format")) {
|
||||
// get generic value
|
||||
const GValue *val = gst_structure_get_value(decice_cap_struct, "format");
|
||||
|
||||
// if its a list of format string
|
||||
if ( GST_VALUE_HOLDS_LIST(val)) {
|
||||
int N = gst_value_list_get_size(val);
|
||||
for (int n = 0; n < N; n++ ){
|
||||
std::string f = gst_value_serialize( gst_value_list_get_value(val, n) );
|
||||
|
||||
// preference order : 1) RGBx, 2) JPEG, 3) ALL OTHER
|
||||
// select f if it contains R (e.g. for RGBx) and not already RGB in config
|
||||
if ( (f.find("R") != std::string::npos) && (config.format.find("R") == std::string::npos ) ) {
|
||||
config.format = f;
|
||||
break;
|
||||
}
|
||||
// default, take at least one if nothing yet in config
|
||||
else if ( config.format.empty() )
|
||||
config.format = f;
|
||||
}
|
||||
|
||||
}
|
||||
// single format
|
||||
else {
|
||||
config.format = gst_value_serialize(val);
|
||||
}
|
||||
}
|
||||
|
||||
// FRAMERATE : can be a fraction of a list of fractions
|
||||
if ( gst_structure_has_field (decice_cap_struct, "framerate")) {
|
||||
|
||||
// get generic value
|
||||
const GValue *val = gst_structure_get_value(decice_cap_struct, "framerate");
|
||||
// if its a single fraction
|
||||
if ( GST_VALUE_HOLDS_FRACTION(val)) {
|
||||
config.fps_numerator = gst_value_get_fraction_numerator(val);
|
||||
config.fps_denominator= gst_value_get_fraction_denominator(val);
|
||||
}
|
||||
// if its a range of fraction; take the max
|
||||
else if ( GST_VALUE_HOLDS_FRACTION_RANGE(val)) {
|
||||
config.fps_numerator = gst_value_get_fraction_numerator(gst_value_get_fraction_range_max(val));
|
||||
config.fps_denominator= gst_value_get_fraction_denominator(gst_value_get_fraction_range_max(val));
|
||||
}
|
||||
// deal otherwise with a list of fractions; find the max
|
||||
else if ( GST_VALUE_HOLDS_LIST(val)) {
|
||||
gdouble fps_max = 1.0;
|
||||
// loop over all fractions
|
||||
int N = gst_value_list_get_size(val);
|
||||
for (int i = 0; i < N; ++i ){
|
||||
const GValue *frac = gst_value_list_get_value(val, i);
|
||||
// read one fraction in the list
|
||||
if ( GST_VALUE_HOLDS_FRACTION(frac)) {
|
||||
int n = gst_value_get_fraction_numerator(frac);
|
||||
int d = gst_value_get_fraction_denominator(frac);
|
||||
// keep only the higher FPS
|
||||
gdouble f = 1.0;
|
||||
gst_util_fraction_to_double( n, d, &f );
|
||||
if ( f > fps_max ) {
|
||||
config.fps_numerator = n;
|
||||
config.fps_denominator = d;
|
||||
fps_max = f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WIDTH and HEIGHT
|
||||
if ( gst_structure_has_field (decice_cap_struct, "width"))
|
||||
gst_structure_get_int (decice_cap_struct, "width", &config.width);
|
||||
if ( gst_structure_has_field (decice_cap_struct, "height"))
|
||||
gst_structure_get_int (decice_cap_struct, "height", &config.height);
|
||||
|
||||
|
||||
// add this config
|
||||
configs.insert(config);
|
||||
}
|
||||
|
||||
}
|
||||
gst_iterator_free(iter);
|
||||
|
||||
// terminate pipeline
|
||||
gst_element_set_state (pipeline_, GST_STATE_NULL);
|
||||
}
|
||||
|
||||
g_object_unref (elem);
|
||||
}
|
||||
|
||||
gst_object_unref (pipeline_);
|
||||
|
||||
return configs;
|
||||
}
|
||||
|
||||
|
||||
glm::ivec2 DeviceSource::icon() const
|
||||
{
|
||||
if ( device_.find("Screen") != std::string::npos )
|
||||
return glm::ivec2(19, 1);
|
||||
else
|
||||
return glm::ivec2(2, 14);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1213
FileDialog.cpp
112
FileDialog.h
@@ -1,112 +0,0 @@
|
||||
#ifndef __IMGUI_FILE_DIALOG_H_
|
||||
#define __IMGUI_FILE_DIALOG_H_
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <future>
|
||||
#include <functional>
|
||||
|
||||
#define MAX_FILE_DIALOG_NAME_BUFFER 1024
|
||||
|
||||
struct FileInfoStruct
|
||||
{
|
||||
char type = ' ';
|
||||
std::string filePath;
|
||||
std::string fileName;
|
||||
std::string ext;
|
||||
};
|
||||
|
||||
class FileDialog
|
||||
{
|
||||
private:
|
||||
std::vector<FileInfoStruct> m_FileList;
|
||||
std::map<std::string, ImVec4> m_FilterColor;
|
||||
std::string m_SelectedFileName;
|
||||
std::string m_SelectedExt;
|
||||
std::string m_CurrentPath;
|
||||
std::vector<std::string> m_CurrentPath_Decomposition;
|
||||
std::string m_Name;
|
||||
bool m_ShowDialog;
|
||||
bool m_ShowDrives;
|
||||
|
||||
public:
|
||||
static char FileNameBuffer[MAX_FILE_DIALOG_NAME_BUFFER];
|
||||
static char DirectoryNameBuffer[MAX_FILE_DIALOG_NAME_BUFFER];
|
||||
static char SearchBuffer[MAX_FILE_DIALOG_NAME_BUFFER];
|
||||
static int FilterIndex;
|
||||
bool IsOk;
|
||||
bool m_AnyWindowsHovered;
|
||||
bool m_CreateDirectoryMode;
|
||||
|
||||
private:
|
||||
std::string dlg_key;
|
||||
std::string dlg_name;
|
||||
const char *dlg_filters;
|
||||
std::string dlg_path;
|
||||
std::string dlg_defaultFileName;
|
||||
std::string dlg_defaultExt;
|
||||
std::function<void(std::string, bool*)> dlg_optionsPane;
|
||||
size_t dlg_optionsPaneWidth;
|
||||
std::string searchTag;
|
||||
std::string dlg_userString;
|
||||
|
||||
public:
|
||||
static FileDialog* Instance()
|
||||
{
|
||||
static FileDialog *_instance = new FileDialog();
|
||||
return _instance;
|
||||
}
|
||||
|
||||
static void RenderCurrent();
|
||||
// Open an Open File dialog for TEXT file
|
||||
static void SetCurrentOpenText();
|
||||
// Open an Open File dialog for IMAGE file
|
||||
static void SetCurrentOpenImage();
|
||||
// Open an Open File dialog for MEDIA file
|
||||
static void SetCurrentOpenMedia();
|
||||
|
||||
protected:
|
||||
|
||||
FileDialog(); // Prevent construction
|
||||
FileDialog(const FileDialog&) = delete; // Prevent construction by copying
|
||||
FileDialog& operator =(const FileDialog&) = delete; // Prevent assignment
|
||||
~FileDialog(); // Prevent unwanted destruction
|
||||
|
||||
public:
|
||||
void OpenDialog(const std::string& vKey, const char* vName, const char* vFilters,
|
||||
const std::string& vPath, const std::string& vDefaultFileName,
|
||||
std::function<void(std::string, bool*)> vOptionsPane, size_t vOptionsPaneWidth = 250, const std::string& vUserString = "");
|
||||
void OpenDialog(const std::string& vKey, const char* vName, const char* vFilters,
|
||||
const std::string& vDefaultFileName,
|
||||
std::function<void(std::string, bool*)> vOptionsPane, size_t vOptionsPaneWidth = 250, const std::string& vUserString = "");
|
||||
void OpenDialog(const std::string& vKey, const char* vName, const char* vFilters,
|
||||
const std::string& vPath, const std::string& vDefaultFileName, const std::string& vUserString = "");
|
||||
void OpenDialog(const std::string& vKey, const char* vName, const char* vFilters,
|
||||
const std::string& vFilePathName, const std::string& vUserString = "");
|
||||
|
||||
void CloseDialog(const std::string& vKey);
|
||||
bool Render(const std::string& vKey, ImVec2 geometry);
|
||||
std::string GetFilepathName();
|
||||
std::string GetCurrentPath();
|
||||
std::string GetCurrentFileName();
|
||||
std::string GetCurrentFilter();
|
||||
std::string GetUserString();
|
||||
|
||||
void SetFilterColor(const std::string &vFilter, ImVec4 vColor);
|
||||
bool GetFilterColor(const std::string &vFilter, ImVec4 *vColor);
|
||||
void ClearFilterColor();
|
||||
|
||||
private:
|
||||
void SetPath(const std::string& vPath);
|
||||
void ScanDir(const std::string& vPath);
|
||||
void SetCurrentDir(const std::string& vPath);
|
||||
bool CreateDir(const std::string& vPath);
|
||||
void ComposeNewPath(std::vector<std::string>::iterator vIter);
|
||||
void GetDrives();
|
||||
};
|
||||
|
||||
|
||||
#endif // __IMGUI_FILE_DIALOG_H_
|
||||
455
FrameBuffer.cpp
@@ -1,455 +0,0 @@
|
||||
#include <sstream>
|
||||
|
||||
#include "FrameBuffer.h"
|
||||
#include "ImageShader.h"
|
||||
#include "Resource.h"
|
||||
#include "Settings.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <stb_image.h>
|
||||
#include <stb_image_write.h>
|
||||
|
||||
const char* FrameBuffer::aspect_ratio_name[5] = { "4:3", "3:2", "16:10", "16:9", "21:9" };
|
||||
glm::vec2 FrameBuffer::aspect_ratio_size[5] = { glm::vec2(4.f,3.f), glm::vec2(3.f,2.f), glm::vec2(16.f,10.f), glm::vec2(16.f,9.f) , glm::vec2(21.f,9.f) };
|
||||
const char* FrameBuffer::resolution_name[5] = { "720", "1080", "1200", "1440", "2160" };
|
||||
float FrameBuffer::resolution_height[5] = { 720.f, 1080.f, 1200.f, 1440.f, 2160.f };
|
||||
|
||||
|
||||
glm::vec3 FrameBuffer::getResolutionFromParameters(int ar, int h)
|
||||
{
|
||||
float width = aspect_ratio_size[ar].x * resolution_height[h] / aspect_ratio_size[ar].y;
|
||||
glm::vec3 res = glm::vec3( width, resolution_height[h] , 0.f);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
glm::ivec2 FrameBuffer::getParametersFromResolution(glm::vec3 res)
|
||||
{
|
||||
glm::ivec2 p = glm::ivec2(-1);
|
||||
|
||||
// get aspect ratio parameter
|
||||
static int num_ar = ((int)(sizeof(FrameBuffer::aspect_ratio_size) / sizeof(*FrameBuffer::aspect_ratio_size)));
|
||||
float myratio = res.x / res.y;
|
||||
for(int ar = 0; ar < num_ar; ar++) {
|
||||
if ( myratio - (FrameBuffer::aspect_ratio_size[ar].x / FrameBuffer::aspect_ratio_size[ar].y ) < EPSILON){
|
||||
p.x = ar;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// get height parameter
|
||||
static int num_height = ((int)(sizeof(FrameBuffer::resolution_height) / sizeof(*FrameBuffer::resolution_height)));
|
||||
for(int h = 0; h < num_height; h++) {
|
||||
if ( res.y - FrameBuffer::resolution_height[h] < 1){
|
||||
p.y = h;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
FrameBuffer::FrameBuffer(glm::vec3 resolution, bool useAlpha, bool multiSampling):
|
||||
textureid_(0), intermediate_textureid_(0), framebufferid_(0), intermediate_framebufferid_(0),
|
||||
use_alpha_(useAlpha), use_multi_sampling_(multiSampling)
|
||||
{
|
||||
attrib_.viewport = glm::ivec2(resolution);
|
||||
setProjectionArea(glm::vec2(1.f, 1.f));
|
||||
attrib_.clear_color = glm::vec4(0.f, 0.f, 0.f, 0.f);
|
||||
}
|
||||
|
||||
FrameBuffer::FrameBuffer(uint width, uint height, bool useAlpha, bool multiSampling):
|
||||
textureid_(0), intermediate_textureid_(0), framebufferid_(0), intermediate_framebufferid_(0),
|
||||
use_alpha_(useAlpha), use_multi_sampling_(multiSampling)
|
||||
{
|
||||
attrib_.viewport = glm::ivec2(width, height);
|
||||
setProjectionArea(glm::vec2(1.f, 1.f));
|
||||
attrib_.clear_color = glm::vec4(0.f, 0.f, 0.f, 0.f);
|
||||
}
|
||||
|
||||
void FrameBuffer::init()
|
||||
{
|
||||
// generate texture
|
||||
glGenTextures(1, &textureid_);
|
||||
glBindTexture(GL_TEXTURE_2D, textureid_);
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, use_alpha_ ? GL_RGBA8 : GL_RGB8, attrib_.viewport.x, attrib_.viewport.y);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
// create a framebuffer object
|
||||
glGenFramebuffers(1, &framebufferid_);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebufferid_);
|
||||
|
||||
// take settings into account: no multisampling for level 0
|
||||
use_multi_sampling_ &= Settings::application.render.multisampling > 0;
|
||||
|
||||
if (use_multi_sampling_){
|
||||
|
||||
// create a multisample texture
|
||||
glGenTextures(1, &intermediate_textureid_);
|
||||
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, intermediate_textureid_);
|
||||
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, Settings::application.render.multisampling,
|
||||
use_alpha_ ? GL_RGBA8 : GL_RGB8, attrib_.viewport.x, attrib_.viewport.y, GL_TRUE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
|
||||
|
||||
// attach the multisampled texture to FBO (framebufferid_ currently binded)
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, intermediate_textureid_, 0);
|
||||
|
||||
// create an intermediate FBO : this is the FBO to use for reading
|
||||
glGenFramebuffers(1, &intermediate_framebufferid_);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, intermediate_framebufferid_);
|
||||
|
||||
// attach the 2D texture to intermediate FBO (intermediate_framebufferid_)
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureid_, 0);
|
||||
|
||||
// Log::Info("New FBO %d Multi Sampling ", framebufferid_);
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
// direct attach the 2D texture to FBO
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureid_, 0);
|
||||
|
||||
// Log::Info("New FBO %d Single Sampling ", framebufferid_);
|
||||
}
|
||||
|
||||
checkFramebufferStatus();
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
}
|
||||
|
||||
FrameBuffer::~FrameBuffer()
|
||||
{
|
||||
if (framebufferid_)
|
||||
glDeleteFramebuffers(1, &framebufferid_);
|
||||
if (intermediate_framebufferid_)
|
||||
glDeleteFramebuffers(1, &intermediate_framebufferid_);
|
||||
if (textureid_)
|
||||
glDeleteTextures(1, &textureid_);
|
||||
if (intermediate_textureid_)
|
||||
glDeleteTextures(1, &intermediate_textureid_);
|
||||
}
|
||||
|
||||
|
||||
uint FrameBuffer::texture() const
|
||||
{
|
||||
if (framebufferid_ == 0)
|
||||
return Resource::getTextureBlack();
|
||||
|
||||
return textureid_;
|
||||
}
|
||||
|
||||
float FrameBuffer::aspectRatio() const
|
||||
{
|
||||
return static_cast<float>(attrib_.viewport.x) / static_cast<float>(attrib_.viewport.y);
|
||||
}
|
||||
|
||||
|
||||
std::string FrameBuffer::info() const
|
||||
{
|
||||
glm::ivec2 p = FrameBuffer::getParametersFromResolution(resolution());
|
||||
std::ostringstream info;
|
||||
|
||||
info << attrib_.viewport.x << "x" << attrib_.viewport.y;
|
||||
if (p.x > -1)
|
||||
info << "px, " << FrameBuffer::aspect_ratio_name[p.x];
|
||||
|
||||
return info.str();
|
||||
}
|
||||
|
||||
glm::vec3 FrameBuffer::resolution() const
|
||||
{
|
||||
return glm::vec3(attrib_.viewport.x, attrib_.viewport.y, 0.f);
|
||||
}
|
||||
|
||||
void FrameBuffer::resize(int width, int height)
|
||||
{
|
||||
if (framebufferid_) {
|
||||
if (attrib_.viewport.x != width || attrib_.viewport.y != height)
|
||||
{
|
||||
// de-init
|
||||
glDeleteFramebuffers(1, &framebufferid_);
|
||||
framebufferid_ = 0;
|
||||
|
||||
if (intermediate_framebufferid_)
|
||||
glDeleteFramebuffers(1, &intermediate_framebufferid_);
|
||||
intermediate_framebufferid_ = 0;
|
||||
if (textureid_)
|
||||
glDeleteTextures(1, &textureid_);
|
||||
textureid_ = 0;
|
||||
if (intermediate_textureid_)
|
||||
glDeleteTextures(1, &intermediate_textureid_);
|
||||
intermediate_textureid_ = 0;
|
||||
|
||||
// change resolution
|
||||
attrib_.viewport = glm::ivec2(width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FrameBuffer::begin(bool clear)
|
||||
{
|
||||
if (!framebufferid_)
|
||||
init();
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebufferid_);
|
||||
|
||||
Rendering::manager().pushAttrib(attrib_);
|
||||
|
||||
if (clear)
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void FrameBuffer::end()
|
||||
{
|
||||
// if multisampling frame buffer
|
||||
if (use_multi_sampling_) {
|
||||
// blit the multisample FBO into unisample FBO to generate 2D texture
|
||||
// Doing this blit will automatically resolve the multisampled FBO.
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferid_);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, intermediate_framebufferid_);
|
||||
glBlitFramebuffer(0, 0, attrib_.viewport.x, attrib_.viewport.y,
|
||||
0, 0, attrib_.viewport.x, attrib_.viewport.y, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
}
|
||||
|
||||
FrameBuffer::release();
|
||||
|
||||
Rendering::manager().popAttrib();
|
||||
}
|
||||
|
||||
void FrameBuffer::release()
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void FrameBuffer::readPixels(uint8_t *target_data)
|
||||
{
|
||||
if (!framebufferid_)
|
||||
return;
|
||||
|
||||
if (use_multi_sampling_)
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, intermediate_framebufferid_);
|
||||
else
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferid_);
|
||||
|
||||
if (use_alpha())
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
||||
else
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
|
||||
glReadPixels(0, 0, attrib_.viewport.x, attrib_.viewport.y, (use_alpha_? GL_RGBA : GL_RGB), GL_UNSIGNED_BYTE, target_data);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
bool FrameBuffer::blit(FrameBuffer *destination)
|
||||
{
|
||||
if (!framebufferid_ || !destination || (use_alpha_ != destination->use_alpha_) )
|
||||
return false;
|
||||
|
||||
if (!destination->framebufferid_)
|
||||
destination->init();
|
||||
|
||||
if (use_multi_sampling_)
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, intermediate_framebufferid_);
|
||||
else
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferid_);
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, destination->framebufferid_);
|
||||
// blit to the frame buffer object
|
||||
glBlitFramebuffer(0, 0, attrib_.viewport.x, attrib_.viewport.y,
|
||||
0, 0, destination->width(), destination->height(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FrameBuffer::checkFramebufferStatus()
|
||||
{
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
switch (status){
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
|
||||
Log::Warning("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT is returned if any of the framebuffer attachment points are framebuffer incomplete.");
|
||||
break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
|
||||
Log::Warning("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT is returned if the framebuffer does not have at least one image attached to it.");
|
||||
break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
|
||||
Log::Warning("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER is returned if the value of GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is GL_NONE for any color attachment point(s) named by GL_DRAWBUFFERi.");
|
||||
break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
|
||||
Log::Warning("GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER is returned if GL_READ_BUFFER is not GL_NONE and the value of GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is GL_NONE for the color attachment point named by GL_READ_BUFFER.");
|
||||
break;
|
||||
case GL_FRAMEBUFFER_UNSUPPORTED:
|
||||
Log::Warning("GL_FRAMEBUFFER_UNSUPPORTED is returned if the combination of internal formats of the attached images violates an implementation-dependent set of restrictions.");
|
||||
break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
|
||||
Log::Warning("GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE is returned if the value of GL_RENDERBUFFER_SAMPLES is not the same for all attached renderbuffers; if the value of GL_TEXTURE_SAMPLES is the not same for all attached textures; or, if the attached images are a mix of renderbuffers and textures, the value of GL_RENDERBUFFER_SAMPLES does not match the value of GL_TEXTURE_SAMPLES.\nGL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE is also returned if the value of GL_TEXTURE_FIXED_SAMPLE_LOCATIONS is not the same for all attached textures; or, if the attached images are a mix of renderbuffers and textures, the value of GL_TEXTURE_FIXED_SAMPLE_LOCATIONS is not GL_TRUE for all attached textures.");
|
||||
break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
|
||||
Log::Warning("GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS is returned if any framebuffer attachment is layered, and any populated attachment is not layered, or if all populated color attachments are not from textures of the same target.");
|
||||
break;
|
||||
case GL_FRAMEBUFFER_UNDEFINED:
|
||||
Log::Warning(" GL_FRAMEBUFFER_UNDEFINED is returned if target is the default framebuffer, but the default framebuffer does not exist.");
|
||||
break;
|
||||
case GL_FRAMEBUFFER_COMPLETE:
|
||||
#ifndef NDEBUG
|
||||
Log::Info("Framebuffer created %d x %d.", width(), height());
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
glm::mat4 FrameBuffer::projection() const
|
||||
{
|
||||
return projection_;
|
||||
}
|
||||
|
||||
glm::vec2 FrameBuffer::projectionArea() const
|
||||
{
|
||||
return projection_area_;
|
||||
}
|
||||
|
||||
void FrameBuffer::setProjectionArea(glm::vec2 c)
|
||||
{
|
||||
projection_area_.x = CLAMP(c.x, 0.1f, 1.f);
|
||||
projection_area_.y = CLAMP(c.y, 0.1f, 1.f);
|
||||
projection_ = glm::ortho(-projection_area_.x, projection_area_.x, projection_area_.y, -projection_area_.y, -1.f, 1.f);
|
||||
}
|
||||
|
||||
|
||||
FrameBufferImage::FrameBufferImage(int w, int h) :
|
||||
rgb(nullptr), width(w), height(h)
|
||||
{
|
||||
if (width>0 && height>0)
|
||||
rgb = new uint8_t[width*height*3];
|
||||
}
|
||||
|
||||
FrameBufferImage::FrameBufferImage(jpegBuffer jpgimg) :
|
||||
rgb(nullptr), width(0), height(0)
|
||||
{
|
||||
int c = 0;
|
||||
if (jpgimg.buffer != nullptr && jpgimg.len >0)
|
||||
rgb = stbi_load_from_memory(jpgimg.buffer, jpgimg.len, &width, &height, &c, 3);
|
||||
}
|
||||
|
||||
FrameBufferImage::FrameBufferImage(const std::string &filename) :
|
||||
rgb(nullptr), width(0), height(0)
|
||||
{
|
||||
int c = 0;
|
||||
if (!filename.empty())
|
||||
rgb = stbi_load(filename.c_str(), &width, &height, &c, 3);
|
||||
}
|
||||
|
||||
FrameBufferImage::~FrameBufferImage()
|
||||
{
|
||||
if (rgb!=nullptr)
|
||||
delete rgb;
|
||||
}
|
||||
|
||||
FrameBufferImage::jpegBuffer FrameBufferImage::getJpeg() const
|
||||
{
|
||||
jpegBuffer jpgimg;
|
||||
|
||||
// if we hold a valid image
|
||||
if (rgb!=nullptr && width>0 && height>0) {
|
||||
|
||||
// allocate JPEG buffer
|
||||
// (NB: JPEG will need less than this but we can't know before...)
|
||||
jpgimg.buffer = (unsigned char *) malloc( width * height * 3 * sizeof(unsigned char));
|
||||
|
||||
stbi_write_jpg_to_func( [](void *context, void *data, int size)
|
||||
{
|
||||
memcpy(((FrameBufferImage::jpegBuffer*)context)->buffer + ((FrameBufferImage::jpegBuffer*)context)->len, data, size);
|
||||
((FrameBufferImage::jpegBuffer*)context)->len += size;
|
||||
}
|
||||
,&jpgimg, width, height, 3, rgb, FBI_JPEG_QUALITY);
|
||||
}
|
||||
|
||||
return jpgimg;
|
||||
}
|
||||
|
||||
FrameBufferImage *FrameBuffer::image(){
|
||||
|
||||
FrameBufferImage *img = nullptr;
|
||||
|
||||
// not ready
|
||||
if (!framebufferid_)
|
||||
return img;
|
||||
|
||||
// only compatible for RGB FrameBuffers
|
||||
if (use_alpha_ || use_multi_sampling_)
|
||||
return img;
|
||||
|
||||
// allocate image
|
||||
img = new FrameBufferImage(attrib_.viewport.x, attrib_.viewport.y);
|
||||
|
||||
// get pixels into image
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); // set buffer target readpixel
|
||||
readPixels(img->rgb);
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
bool FrameBuffer::fill(FrameBufferImage *image)
|
||||
{
|
||||
if (!framebufferid_)
|
||||
init();
|
||||
|
||||
// only compatible for RGB FrameBuffers
|
||||
if (use_alpha_ || use_multi_sampling_)
|
||||
return false;
|
||||
|
||||
// invalid image
|
||||
if ( image == nullptr ||
|
||||
image->rgb==nullptr ||
|
||||
image->width < 1 ||
|
||||
image->height < 1 )
|
||||
return false;
|
||||
|
||||
// is it same size ?
|
||||
if (image->width == attrib_.viewport.x && image->height == attrib_.viewport.y ) {
|
||||
// directly fill texture with image
|
||||
glBindTexture(GL_TEXTURE_2D, textureid_);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image->width, image->height,
|
||||
GL_RGB, GL_UNSIGNED_BYTE, image->rgb);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
else {
|
||||
uint textureimage, framebufferimage;
|
||||
// generate texture
|
||||
glGenTextures(1, &textureimage);
|
||||
glBindTexture(GL_TEXTURE_2D, textureimage);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, image->width, image->height, 0, GL_RGB, GL_UNSIGNED_BYTE, image->rgb);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
// create a framebuffer object
|
||||
glGenFramebuffers(1, &framebufferimage);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebufferimage);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureimage, 0);
|
||||
|
||||
// blit to the frame buffer object with interpolation
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferimage);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebufferid_);
|
||||
glBlitFramebuffer(0, 0, image->width, image->height,
|
||||
0, 0, attrib_.viewport.x, attrib_.viewport.y, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
// cleanup
|
||||
glDeleteFramebuffers(1, &framebufferimage);
|
||||
glDeleteTextures(1, &textureimage);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "Log.h"
|
||||
#include "Scene.h"
|
||||
#include "GarbageVisitor.h"
|
||||
|
||||
|
||||
GarbageVisitor::GarbageVisitor(Node *nodetocollect) : Visitor()
|
||||
{
|
||||
targets_.push_front(nodetocollect);
|
||||
current_ = nullptr;
|
||||
found_ = false;
|
||||
}
|
||||
|
||||
GarbageVisitor::GarbageVisitor(Source *sourcetocollect) : Visitor()
|
||||
{
|
||||
targets_.push_front(sourcetocollect->group(View::MIXING));
|
||||
targets_.push_front(sourcetocollect->group(View::GEOMETRY));
|
||||
targets_.push_front(sourcetocollect->group(View::RENDERING));
|
||||
current_ = nullptr;
|
||||
found_ = false;
|
||||
}
|
||||
|
||||
|
||||
void GarbageVisitor::visit(Node &n)
|
||||
{
|
||||
// found the target
|
||||
// if (n.id() == target_->id())
|
||||
// if ( &n == target_ )
|
||||
if ( std::count(targets_.begin(), targets_.end(), &n) )
|
||||
{
|
||||
// end recursive
|
||||
found_ = true;
|
||||
|
||||
// take the node out of the Tree
|
||||
if (current_)
|
||||
current_->detach(&n);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GarbageVisitor::~GarbageVisitor()
|
||||
{
|
||||
// actually delete the Node
|
||||
|
||||
}
|
||||
|
||||
|
||||
void GarbageVisitor::visit(Group &n)
|
||||
{
|
||||
// no need to go further if the node was found (or is this group)
|
||||
if (found_)
|
||||
return;
|
||||
|
||||
// current group
|
||||
current_ = &n;
|
||||
|
||||
// loop over members of a group
|
||||
// and stop when found
|
||||
for (NodeSet::iterator node = n.begin(); !found_ && node != n.end(); ++node) {
|
||||
// visit the child node
|
||||
(*node)->accept(*this);
|
||||
// un-stack recursive browsing
|
||||
current_ = &n;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GarbageVisitor::visit(Scene &n)
|
||||
{
|
||||
n.root()->accept(*this);
|
||||
}
|
||||
|
||||
void GarbageVisitor::visit(Switch &n)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GarbageVisitor::visit(Primitive &n)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
#ifndef GARBAGEVISITOR_H
|
||||
#define GARBAGEVISITOR_H
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "Source.h"
|
||||
#include "Visitor.h"
|
||||
|
||||
|
||||
class GarbageVisitor : public Visitor
|
||||
{
|
||||
Group *current_;
|
||||
std::list<Node *> targets_;
|
||||
bool found_;
|
||||
|
||||
public:
|
||||
GarbageVisitor(Source *sourcetocollect);
|
||||
GarbageVisitor(Node *nodetocollect);
|
||||
~GarbageVisitor();
|
||||
|
||||
void visit(Scene& n) override;
|
||||
void visit(Node& n) override;
|
||||
void visit(Group& n) override;
|
||||
void visit(Switch& n) override;
|
||||
void visit(Primitive& n) override;
|
||||
|
||||
};
|
||||
|
||||
#endif // GARBAGEVISITOR_H
|
||||
856
ImGuiVisitor.cpp
@@ -1,856 +0,0 @@
|
||||
#include "ImGuiVisitor.h"
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/gtc/constants.hpp>
|
||||
#include <glm/gtc/matrix_access.hpp>
|
||||
|
||||
#include <tinyxml2.h>
|
||||
#include "tinyxml2Toolkit.h"
|
||||
|
||||
#include "defines.h"
|
||||
#include "Log.h"
|
||||
#include "Scene.h"
|
||||
#include "Primitives.h"
|
||||
#include "ImageShader.h"
|
||||
#include "ImageProcessingShader.h"
|
||||
#include "MediaPlayer.h"
|
||||
#include "MediaSource.h"
|
||||
#include "SessionSource.h"
|
||||
#include "PatternSource.h"
|
||||
#include "DeviceSource.h"
|
||||
#include "NetworkSource.h"
|
||||
#include "MultiFileSource.h"
|
||||
#include "SessionCreator.h"
|
||||
#include "SessionVisitor.h"
|
||||
#include "Settings.h"
|
||||
#include "Mixer.h"
|
||||
#include "ActionManager.h"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "ImGuiToolkit.h"
|
||||
#include "BaseToolkit.h"
|
||||
#include "UserInterfaceManager.h"
|
||||
#include "SystemToolkit.h"
|
||||
|
||||
|
||||
ImGuiVisitor::ImGuiVisitor()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ImGuiVisitor::visit(Node &n)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ImGuiVisitor::visit(Group &n)
|
||||
{
|
||||
// MODEL VIEW
|
||||
ImGui::PushID(std::to_string(n.id()).c_str());
|
||||
|
||||
if (ImGuiToolkit::ButtonIcon(1, 16)) {
|
||||
n.translation_.x = 0.f;
|
||||
n.translation_.y = 0.f;
|
||||
n.rotation_.z = 0.f;
|
||||
n.scale_.x = 1.f;
|
||||
n.scale_.y = 1.f;
|
||||
Action::manager().store("Geometry Reset");
|
||||
}
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::Text("Geometry");
|
||||
|
||||
if (ImGuiToolkit::ButtonIcon(6, 15)) {
|
||||
n.translation_.x = 0.f;
|
||||
n.translation_.y = 0.f;
|
||||
Action::manager().store("Position 0.0, 0.0");
|
||||
}
|
||||
ImGui::SameLine(0, 10);
|
||||
float translation[2] = { n.translation_.x, n.translation_.y};
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
if ( ImGui::SliderFloat2("Position", translation, -5.0, 5.0) )
|
||||
{
|
||||
n.translation_.x = translation[0];
|
||||
n.translation_.y = translation[1];
|
||||
}
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()){
|
||||
std::ostringstream oss;
|
||||
oss << "Position " << std::setprecision(3) << n.translation_.x << ", " << n.translation_.y;
|
||||
Action::manager().store(oss.str());
|
||||
}
|
||||
if (ImGuiToolkit::ButtonIcon(3, 15)) {
|
||||
n.scale_.x = 1.f;
|
||||
n.scale_.y = 1.f;
|
||||
Action::manager().store("Scale 1.0 x 1.0");
|
||||
}
|
||||
ImGui::SameLine(0, 10);
|
||||
float scale[2] = { n.scale_.x, n.scale_.y} ;
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
if ( ImGui::SliderFloat2("Scale", scale, -MAX_SCALE, MAX_SCALE, "%.2f") )
|
||||
{
|
||||
n.scale_.x = CLAMP_SCALE(scale[0]);
|
||||
n.scale_.y = CLAMP_SCALE(scale[1]);
|
||||
}
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()){
|
||||
std::ostringstream oss;
|
||||
oss << "Scale " << std::setprecision(3) << n.scale_.x << " x " << n.scale_.y;
|
||||
Action::manager().store(oss.str());
|
||||
}
|
||||
|
||||
if (ImGuiToolkit::ButtonIcon(18, 9)){
|
||||
n.rotation_.z = 0.f;
|
||||
Action::manager().store("Angle 0.0");
|
||||
}
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
ImGui::SliderAngle("Angle", &(n.rotation_.z), -180.f, 180.f) ;
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
||||
std::ostringstream oss;
|
||||
oss << "Angle " << std::setprecision(3) << n.rotation_.z * 180.f / M_PI;
|
||||
Action::manager().store(oss.str());
|
||||
}
|
||||
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
// spacing
|
||||
ImGui::Spacing();
|
||||
}
|
||||
|
||||
void ImGuiVisitor::visit(Switch &n)
|
||||
{
|
||||
if (n.numChildren()>0)
|
||||
n.activeChild()->accept(*this);
|
||||
}
|
||||
|
||||
void ImGuiVisitor::visit(Scene &n)
|
||||
{
|
||||
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
|
||||
if (ImGui::CollapsingHeader("Scene Property Tree"))
|
||||
{
|
||||
n.root()->accept(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiVisitor::visit(Primitive &n)
|
||||
{
|
||||
ImGui::PushID(std::to_string(n.id()).c_str());
|
||||
ImGui::Text("Primitive %d");
|
||||
|
||||
n.shader()->accept(*this);
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void ImGuiVisitor::visit(FrameBufferSurface &n)
|
||||
{
|
||||
ImGui::Text("Framebuffer");
|
||||
}
|
||||
|
||||
void ImGuiVisitor::visit(MediaPlayer &n)
|
||||
{
|
||||
ImGui::Text("Media Player");
|
||||
}
|
||||
|
||||
void ImGuiVisitor::visit(Shader &n)
|
||||
{
|
||||
ImGui::PushID(std::to_string(n.id()).c_str());
|
||||
|
||||
// Base color
|
||||
// if (ImGuiToolkit::ButtonIcon(10, 2)) {
|
||||
// n.blending = Shader::BLEND_OPACITY;
|
||||
// n.color = glm::vec4(1.f, 1.f, 1.f, 1.f);
|
||||
// }
|
||||
// ImGui::SameLine(0, 10);
|
||||
// ImGui::ColorEdit3("Color", glm::value_ptr(n.color), ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel ) ;
|
||||
// ImGui::SameLine(0, 5);
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
int mode = n.blending;
|
||||
if (ImGui::Combo("Blending", &mode, "Normal\0Screen\0Subtract\0Multiply\0Soft light"
|
||||
"\0Hard light\0Soft subtract\0Lighten only\0") ) {
|
||||
n.blending = Shader::BlendMode(mode);
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "Blending ";
|
||||
switch(n.blending) {
|
||||
case Shader::BLEND_OPACITY:
|
||||
oss<<"Normal";
|
||||
break;
|
||||
case Shader::BLEND_SCREEN:
|
||||
oss<<"Screen";
|
||||
break;
|
||||
case Shader::BLEND_SUBTRACT:
|
||||
oss<<"Subtract";
|
||||
break;
|
||||
case Shader::BLEND_MULTIPLY:
|
||||
oss<<"Multiply";
|
||||
break;
|
||||
case Shader::BLEND_HARD_LIGHT:
|
||||
oss<<"Hard light";
|
||||
break;
|
||||
case Shader::BLEND_SOFT_LIGHT:
|
||||
oss<<"Soft light";
|
||||
break;
|
||||
case Shader::BLEND_SOFT_SUBTRACT:
|
||||
oss<<"Soft subtract";
|
||||
break;
|
||||
case Shader::BLEND_LIGHTEN_ONLY:
|
||||
oss<<"Lighten only";
|
||||
break;
|
||||
case Shader::BLEND_NONE:
|
||||
oss<<"None";
|
||||
break;
|
||||
}
|
||||
Action::manager().store(oss.str());
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
//void ImGuiVisitor::visit(ImageShader &n)
|
||||
//{
|
||||
// ImGui::PushID(std::to_string(n.id()).c_str());
|
||||
// // get index of the mask used in this ImageShader
|
||||
// int item_current = n.mask;
|
||||
//// if (ImGuiToolkit::ButtonIcon(10, 3)) n.mask = 0;
|
||||
//// ImGui::SameLine(0, 10);
|
||||
// ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
// // combo list of masks
|
||||
// if ( ImGui::Combo("Mask", &item_current, ImageShader::mask_names, IM_ARRAYSIZE(ImageShader::mask_names) ) )
|
||||
// {
|
||||
// if (item_current < (int) ImageShader::mask_presets.size())
|
||||
// n.mask = item_current;
|
||||
// else {
|
||||
// // TODO ask for custom mask
|
||||
// }
|
||||
// Action::manager().store("Mask "+ std::string(ImageShader::mask_names[n.mask]));
|
||||
// }
|
||||
// ImGui::PopID();
|
||||
//}
|
||||
|
||||
void ImGuiVisitor::visit(ImageProcessingShader &n)
|
||||
{
|
||||
ImGui::PushID(std::to_string(n.id()).c_str());
|
||||
|
||||
ImGuiToolkit::Icon(6, 2);
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::Text("Filters");
|
||||
|
||||
if (ImGuiToolkit::ButtonIcon(6, 4)) {
|
||||
n.gamma = glm::vec4(1.f, 1.f, 1.f, 1.f);
|
||||
Action::manager().store("Gamma & Color");
|
||||
}
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::ColorEdit3("Gamma Color", glm::value_ptr(n.gamma), ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel) ;
|
||||
if (ImGui::IsItemDeactivatedAfterEdit())
|
||||
Action::manager().store("Gamma Color changed");
|
||||
|
||||
ImGui::SameLine(0, 5);
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
ImGui::SliderFloat("Gamma", &n.gamma.w, 0.5f, 10.f, "%.2f", 2.f);
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()){
|
||||
std::ostringstream oss;
|
||||
oss << "Gamma " << std::setprecision(2) << n.gamma.w;
|
||||
Action::manager().store(oss.str());
|
||||
}
|
||||
|
||||
// ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
// ImGui::SliderFloat4("Levels", glm::value_ptr(n.levels), 0.0, 1.0);
|
||||
|
||||
if (ImGuiToolkit::ButtonIcon(5, 16)) {
|
||||
n.brightness = 0.f;
|
||||
n.contrast = 0.f;
|
||||
Action::manager().store("B & C 0.0 0.0");
|
||||
}
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
float bc[2] = { n.brightness, n.contrast};
|
||||
if ( ImGui::SliderFloat2("B & C", bc, -1.0, 1.0) )
|
||||
{
|
||||
n.brightness = bc[0];
|
||||
n.contrast = bc[1];
|
||||
}
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()){
|
||||
std::ostringstream oss;
|
||||
oss << "B & C " << std::setprecision(2) << n.brightness << " " << n.contrast;
|
||||
Action::manager().store(oss.str());
|
||||
}
|
||||
|
||||
if (ImGuiToolkit::ButtonIcon(9, 16)) {
|
||||
n.saturation = 0.f;
|
||||
Action::manager().store("Saturation 0.0");
|
||||
}
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
ImGui::SliderFloat("Saturation", &n.saturation, -1.0, 1.0);
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()){
|
||||
std::ostringstream oss;
|
||||
oss << "Saturation " << std::setprecision(2) << n.saturation;
|
||||
Action::manager().store(oss.str());
|
||||
}
|
||||
|
||||
if (ImGuiToolkit::ButtonIcon(12, 4)) {
|
||||
n.hueshift = 0.f;
|
||||
Action::manager().store("Hue shift 0.0");
|
||||
}
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
ImGui::SliderFloat("Hue shift", &n.hueshift, 0.0, 1.0);
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()){
|
||||
std::ostringstream oss;
|
||||
oss << "Hue shift " << std::setprecision(2) << n.hueshift;
|
||||
Action::manager().store(oss.str());
|
||||
}
|
||||
|
||||
if (ImGuiToolkit::ButtonIcon(18, 1)) {
|
||||
n.nbColors = 0;
|
||||
Action::manager().store("Posterize None");
|
||||
}
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
ImGui::SliderInt("Posterize", &n.nbColors, 0, 16, n.nbColors == 0 ? "None" : "%d colors");
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()){
|
||||
std::ostringstream oss;
|
||||
oss << "Posterize ";
|
||||
if (n.nbColors == 0) oss << "None"; else oss << n.nbColors;
|
||||
Action::manager().store(oss.str());
|
||||
}
|
||||
|
||||
if (ImGuiToolkit::ButtonIcon(8, 1)) {
|
||||
n.threshold = 0.f;
|
||||
Action::manager().store("Threshold None");
|
||||
}
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
ImGui::SliderFloat("Threshold", &n.threshold, 0.0, 1.0, n.threshold < 0.001 ? "None" : "%.2f");
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()){
|
||||
std::ostringstream oss;
|
||||
oss << "Threshold ";
|
||||
if (n.threshold < 0.001) oss << "None"; else oss << std::setprecision(2) << n.threshold;
|
||||
Action::manager().store(oss.str());
|
||||
}
|
||||
|
||||
if (ImGuiToolkit::ButtonIcon(3, 1)) {
|
||||
n.lumakey = 0.f;
|
||||
Action::manager().store("Lumakey 0.0");
|
||||
}
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
ImGui::SliderFloat("Lumakey", &n.lumakey, 0.0, 1.0);
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()){
|
||||
std::ostringstream oss;
|
||||
oss << "Lumakey " << std::setprecision(2) << n.lumakey;
|
||||
Action::manager().store(oss.str());
|
||||
}
|
||||
|
||||
if (ImGuiToolkit::ButtonIcon(13, 4)) {
|
||||
n.chromakey = glm::vec4(0.f, 0.8f, 0.f, 1.f);
|
||||
n.chromadelta = 0.f;
|
||||
Action::manager().store("Chromakey & Color Reset");
|
||||
}
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::ColorEdit3("Chroma color", glm::value_ptr(n.chromakey), ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel ) ;
|
||||
if (ImGui::IsItemDeactivatedAfterEdit())
|
||||
Action::manager().store("Chroma color changed");
|
||||
ImGui::SameLine(0, 5);
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
ImGui::SliderFloat("Chromakey", &n.chromadelta, 0.0, 1.0, n.chromadelta < 0.001 ? "None" : "Tolerance %.2f");
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()){
|
||||
std::ostringstream oss;
|
||||
oss << "Chromakey ";
|
||||
if (n.chromadelta < 0.001) oss << "None"; else oss << std::setprecision(2) << n.chromadelta;
|
||||
Action::manager().store(oss.str());
|
||||
}
|
||||
|
||||
if (ImGuiToolkit::ButtonIcon(6, 16)) {
|
||||
n.invert = 0;
|
||||
Action::manager().store("Invert None");
|
||||
}
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
if (ImGui::Combo("Invert", &n.invert, "None\0Invert Color\0Invert Luminance\0"))
|
||||
Action::manager().store("Invert " + std::string(n.invert<1 ? "None": (n.invert>1 ? "Luminance" : "Color")));
|
||||
|
||||
if (ImGuiToolkit::ButtonIcon(1, 7)) {
|
||||
n.filterid = 0;
|
||||
Action::manager().store("Filter None");
|
||||
}
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
if (ImGui::Combo("Filter", &n.filterid, ImageProcessingShader::filter_names, IM_ARRAYSIZE(ImageProcessingShader::filter_names) ) )
|
||||
Action::manager().store("Filter " + std::string(ImageProcessingShader::filter_names[n.filterid]));
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
ImGui::Spacing();
|
||||
}
|
||||
|
||||
void ImGuiVisitor::visit (Source& s)
|
||||
{
|
||||
ImGui::PushID(std::to_string(s.id()).c_str());
|
||||
// blending
|
||||
s.blendingShader()->accept(*this);
|
||||
|
||||
// preview
|
||||
float preview_width = ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN;
|
||||
float preview_height = 4.5f * ImGui::GetFrameHeightWithSpacing();
|
||||
ImVec2 pos = ImGui::GetCursorPos(); // remember where we were...
|
||||
|
||||
float space = ImGui::GetStyle().ItemSpacing.y;
|
||||
float width = preview_width;
|
||||
float height = s.frame()->projectionArea().y * width / ( s.frame()->projectionArea().x * s.frame()->aspectRatio());
|
||||
if (height > preview_height - space) {
|
||||
height = preview_height - space;
|
||||
width = height * s.frame()->aspectRatio() * ( s.frame()->projectionArea().x / s.frame()->projectionArea().y);
|
||||
}
|
||||
// centered image
|
||||
ImGui::SetCursorPos( ImVec2(pos.x + 0.5f * (preview_width-width), pos.y + 0.5f * (preview_height-height-space)) );
|
||||
ImGui::Image((void*)(uintptr_t) s.frame()->texture(), ImVec2(width, height));
|
||||
|
||||
// inform on visibility status
|
||||
ImGui::SetCursorPos( ImVec2(preview_width + 20, pos.y ) );
|
||||
if (s.active()) {
|
||||
if (s.blendingShader()->color.a > 0.f)
|
||||
ImGuiToolkit::HelpMarker("Visible", ICON_FA_EYE);
|
||||
else
|
||||
ImGuiToolkit::HelpMarker("Not visible", ICON_FA_EYE_SLASH);
|
||||
}
|
||||
else
|
||||
ImGuiToolkit::HelpMarker("Inactive", ICON_FA_SNOWFLAKE);
|
||||
|
||||
// Inform on workspace
|
||||
ImGui::SetCursorPos( ImVec2(preview_width + 20, pos.y + ImGui::GetFrameHeightWithSpacing()) );
|
||||
if (s.workspace() == Source::BACKGROUND)
|
||||
ImGuiToolkit::HelpIcon("in Background",10, 16);
|
||||
else if (s.workspace() == Source::FOREGROUND)
|
||||
ImGuiToolkit::HelpIcon("in Foreground",12, 16);
|
||||
else
|
||||
ImGuiToolkit::HelpIcon("in Workspace",11, 16);
|
||||
|
||||
// locking
|
||||
ImGui::SetCursorPos( ImVec2(preview_width + 20, pos.y + 2.f * ImGui::GetFrameHeightWithSpacing()) );
|
||||
const char *tooltip[2] = {"Unlocked", "Locked"};
|
||||
bool l = s.locked();
|
||||
if (ImGuiToolkit::IconToggle(15,6,17,6, &l, tooltip ) ) {
|
||||
s.setLocked(l);
|
||||
if (l) {
|
||||
Mixer::selection().clear();
|
||||
Action::manager().store(s.name() + std::string(": lock."));
|
||||
}
|
||||
else {
|
||||
Mixer::selection().set(&s);
|
||||
Action::manager().store(s.name() + std::string(": unlock."));
|
||||
}
|
||||
}
|
||||
|
||||
// toggle enable/disable image processing
|
||||
bool on = s.imageProcessingEnabled();
|
||||
ImGui::SetCursorPos( ImVec2(preview_width + 15, pos.y + 3.5f * ImGui::GetFrameHeightWithSpacing()) );
|
||||
if ( ImGuiToolkit::ButtonToggle(ICON_FA_MAGIC, &on) ){
|
||||
std::ostringstream oss;
|
||||
oss << s.name() << ": " << ( on ? "Enable Filter" : "Disable Filter");
|
||||
Action::manager().store(oss.str());
|
||||
}
|
||||
s.setImageProcessingEnabled(on);
|
||||
|
||||
// image processing pannel
|
||||
if (s.imageProcessingEnabled()) {
|
||||
|
||||
// menu icon for image processing
|
||||
ImGui::SetCursorPos( ImVec2( preview_width - ImGui::GetTextLineHeight(), pos.y + 4.5f * ImGui::GetFrameHeightWithSpacing())); // ...come back
|
||||
if (ImGuiToolkit::IconButton(5, 8))
|
||||
ImGui::OpenPopup( "MenuImageProcessing" );
|
||||
if (ImGui::BeginPopup( "MenuImageProcessing" ))
|
||||
{
|
||||
if (s.processingshader_link_.connected()) {
|
||||
if (ImGui::MenuItem( "Unfollow" )){
|
||||
s.processingshader_link_.disconnect();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ImGui::MenuItem("Reset" )){
|
||||
ImageProcessingShader defaultvalues;
|
||||
s.processingShader()->copy(defaultvalues);
|
||||
s.processingshader_link_.disconnect();
|
||||
std::ostringstream oss;
|
||||
oss << s.name() << ": " << "Reset Filter";
|
||||
Action::manager().store(oss.str());
|
||||
}
|
||||
if (ImGui::MenuItem("Copy" )){
|
||||
std::string clipboard = SessionVisitor::getClipboard(s.processingShader());
|
||||
if (!clipboard.empty())
|
||||
ImGui::SetClipboardText(clipboard.c_str());
|
||||
}
|
||||
const char *clipboard = ImGui::GetClipboardText();
|
||||
const bool can_paste = (clipboard != nullptr && SessionLoader::isClipboard(clipboard));
|
||||
if (ImGui::MenuItem("Paste", NULL, false, can_paste)) {
|
||||
SessionLoader::applyImageProcessing(s, clipboard);
|
||||
std::ostringstream oss;
|
||||
oss << s.name() << ": " << "Change Filter";
|
||||
Action::manager().store(oss.str());
|
||||
}
|
||||
// ImGui::Separator();
|
||||
// if (ImGui::BeginMenu("Follow"))
|
||||
// {
|
||||
// for (auto mpit = Mixer::manager().session()->begin();
|
||||
// mpit != Mixer::manager().session()->end(); mpit++ )
|
||||
// {
|
||||
// std::string label = (*mpit)->name();
|
||||
// if ( (*mpit)->id() != s.id() &&
|
||||
// (*mpit)->imageProcessingEnabled() &&
|
||||
// !(*mpit)->processingshader_link_.connected()) {
|
||||
// if (ImGui::MenuItem( label.c_str() )){
|
||||
// s.processingshader_link_.connect(*mpit);
|
||||
// s.touch();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// ImGui::EndMenu();
|
||||
// }
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
// full panel for image processing
|
||||
ImGui::SetCursorPos( ImVec2( pos.x, pos.y + preview_height)); // ...come back
|
||||
|
||||
if (s.processingshader_link_.connected()) {
|
||||
ImGuiToolkit::Icon(6, 2);
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::Text("Filters");
|
||||
Source *target = s.processingshader_link_.source();
|
||||
ImGui::Text("Following");
|
||||
if ( target != nullptr && ImGui::Button(target->name().c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
|
||||
Mixer::manager().setCurrentSource(target);
|
||||
}
|
||||
else
|
||||
s.processingShader()->accept(*this);
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void ImGuiVisitor::visit (MediaSource& s)
|
||||
{
|
||||
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
|
||||
ImGui::SameLine(0, 10);
|
||||
if ( s.mediaplayer()->isImage() )
|
||||
ImGui::Text("Image File");
|
||||
else
|
||||
ImGui::Text("Video File");
|
||||
|
||||
// Media info
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
s.accept(info);
|
||||
ImGui::Text("%s", info.str().c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
// icon (>) to open player
|
||||
if ( s.playable() ) {
|
||||
ImVec2 pos = ImGui::GetCursorPos();
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::SameLine(0, 10.f + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
if (ImGuiToolkit::IconButton(ICON_FA_PLAY_CIRCLE, "Open in Player"))
|
||||
UserInterface::manager().showSourceEditor(&s);
|
||||
ImGui::SetCursorPos(pos);
|
||||
}
|
||||
|
||||
// folder
|
||||
std::string path = SystemToolkit::path_filename(s.path());
|
||||
std::string label = BaseToolkit::trunc_string(path, 25);
|
||||
label = BaseToolkit::transliterate(label);
|
||||
ImGuiToolkit::ButtonOpenUrl( label.c_str(), path.c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Folder");
|
||||
}
|
||||
|
||||
void ImGuiVisitor::visit (SessionFileSource& s)
|
||||
{
|
||||
if (s.session() == nullptr)
|
||||
return;
|
||||
|
||||
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::Text("Session File");
|
||||
|
||||
// info
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
s.accept(info);
|
||||
ImGui::Text("%s", info.str().c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
// icon (>) to open player
|
||||
if ( s.playable() ) {
|
||||
ImVec2 pos = ImGui::GetCursorPos();
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::SameLine(0, 10.f + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
if (ImGuiToolkit::IconButton(ICON_FA_PLAY_CIRCLE, "Open in Player"))
|
||||
UserInterface::manager().showSourceEditor(&s);
|
||||
ImGui::SetCursorPos(pos);
|
||||
}
|
||||
|
||||
if ( ImGui::Button( ICON_FA_FILE_EXPORT " Import", ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
|
||||
Mixer::manager().import( &s );
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Sources");
|
||||
|
||||
if (ImGuiToolkit::ButtonIcon(3, 2)) s.session()->setFading(0.f);
|
||||
float f = s.session()->fading();
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
if (ImGui::SliderFloat("Fading", &f, 0.0, 1.0, f < 0.001 ? "None" : "%.2f") )
|
||||
s.session()->setFading(f);
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()){
|
||||
std::ostringstream oss;
|
||||
oss << s.name() << ": Fading " << std::setprecision(2) << f;
|
||||
Action::manager().store(oss.str());
|
||||
}
|
||||
if ( ImGui::Button( ICON_FA_FILE_UPLOAD " Open", ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
|
||||
Mixer::manager().set( s.detach() );
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("File");
|
||||
|
||||
std::string path = SystemToolkit::path_filename(s.path());
|
||||
std::string label = BaseToolkit::trunc_string(path, 25);
|
||||
label = BaseToolkit::transliterate(label);
|
||||
ImGuiToolkit::ButtonOpenUrl( label.c_str(), path.c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Folder");
|
||||
|
||||
}
|
||||
|
||||
void ImGuiVisitor::visit (SessionGroupSource& s)
|
||||
{
|
||||
if (s.session() == nullptr)
|
||||
return;
|
||||
|
||||
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::Text("Flat Sesion group");
|
||||
|
||||
// info
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
s.accept(info);
|
||||
ImGui::Text("%s", info.str().c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
// icon (>) to open player
|
||||
if ( s.playable() ) {
|
||||
ImVec2 pos = ImGui::GetCursorPos();
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::SameLine(0, 10.f + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
if (ImGuiToolkit::IconButton(ICON_FA_PLAY_CIRCLE, "Open in Player"))
|
||||
UserInterface::manager().showSourceEditor(&s);
|
||||
ImGui::SetCursorPos(pos);
|
||||
}
|
||||
|
||||
if ( ImGui::Button( ICON_FA_UPLOAD " Expand", ImVec2(IMGUI_RIGHT_ALIGN, 0)) ){
|
||||
Mixer::manager().import( &s );
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiVisitor::visit (RenderSource& s)
|
||||
{
|
||||
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::Text("Rendering Output");
|
||||
if ( ImGui::Button(IMGUI_TITLE_PREVIEW, ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
|
||||
Settings::application.widget.preview = true;
|
||||
}
|
||||
|
||||
void ImGuiVisitor::visit (CloneSource& s)
|
||||
{
|
||||
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::Text("Clone");
|
||||
if ( ImGui::Button(s.origin()->name().c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
|
||||
Mixer::manager().setCurrentSource(s.origin());
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Source");
|
||||
}
|
||||
|
||||
void ImGuiVisitor::visit (PatternSource& s)
|
||||
{
|
||||
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::Text("Pattern");
|
||||
|
||||
// stream info
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
s.accept(info);
|
||||
ImGui::Text("%s", info.str().c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
// icon (>) to open player
|
||||
if ( s.playable() ) {
|
||||
ImVec2 pos = ImGui::GetCursorPos();
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::SameLine(0, 10.f + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
if (ImGuiToolkit::IconButton(ICON_FA_PLAY_CIRCLE, "Open in Player"))
|
||||
UserInterface::manager().showSourceEditor(&s);
|
||||
ImGui::SetCursorPos(pos);
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
if (ImGui::BeginCombo("##Patterns", Pattern::pattern_types[s.pattern()->type()].c_str()) )
|
||||
{
|
||||
for (uint p = 0; p < Pattern::pattern_types.size(); ++p){
|
||||
if (ImGui::Selectable( Pattern::pattern_types[p].c_str() )) {
|
||||
s.setPattern(p, s.pattern()->resolution());
|
||||
info.reset();
|
||||
std::ostringstream oss;
|
||||
oss << s.name() << ": Pattern " << Pattern::pattern_types[p];
|
||||
Action::manager().store(oss.str());
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Generator");
|
||||
}
|
||||
|
||||
void ImGuiVisitor::visit (DeviceSource& s)
|
||||
{
|
||||
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::Text("Device");
|
||||
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
s.accept(info);
|
||||
ImGui::Text("%s", info.str().c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
// icon (>) to open player
|
||||
if ( s.playable() ) {
|
||||
ImVec2 pos = ImGui::GetCursorPos();
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::SameLine(0, 10.f + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
if (ImGuiToolkit::IconButton(ICON_FA_PLAY_CIRCLE, "Open in Player"))
|
||||
UserInterface::manager().showSourceEditor(&s);
|
||||
ImGui::SetCursorPos(pos);
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
if (ImGui::BeginCombo("##Hardware", s.device().c_str()))
|
||||
{
|
||||
for (int d = 0; d < Device::manager().numDevices(); ++d){
|
||||
std::string namedev = Device::manager().name(d);
|
||||
if (ImGui::Selectable( namedev.c_str() )) {
|
||||
s.setDevice(namedev);
|
||||
info.reset();
|
||||
std::ostringstream oss;
|
||||
oss << s.name() << " Device " << namedev;
|
||||
Action::manager().store(oss.str());
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ImGuiVisitor::visit (NetworkSource& s)
|
||||
{
|
||||
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::Text("Network stream");
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_STREAM, 0.9f));
|
||||
ImGui::Text("%s", s.connection().c_str());
|
||||
ImGui::PopStyleColor(1);
|
||||
|
||||
// network info
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
s.accept(info);
|
||||
ImGui::Text("%s", info.str().c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
// icon (>) to open player
|
||||
if ( s.playable() ) {
|
||||
ImVec2 pos = ImGui::GetCursorPos();
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::SameLine(0, 10.f + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
if (ImGuiToolkit::IconButton(ICON_FA_PLAY_CIRCLE, "Open in Player"))
|
||||
UserInterface::manager().showSourceEditor(&s);
|
||||
ImGui::SetCursorPos(pos);
|
||||
}
|
||||
|
||||
if ( ImGui::Button( ICON_FA_REPLY " Reconnect", ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
|
||||
{
|
||||
s.setConnection(s.connection());
|
||||
info.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ImGuiVisitor::visit (MultiFileSource& s)
|
||||
{
|
||||
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::Text("Images sequence");
|
||||
static int64_t id = 0;
|
||||
|
||||
// information text
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
s.accept(info);
|
||||
ImGui::Text("%s", info.str().c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
// icon (>) to open player
|
||||
if ( s.playable() ) {
|
||||
ImVec2 pos = ImGui::GetCursorPos();
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::SameLine(0, ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
if (ImGuiToolkit::IconButton(ICON_FA_PLAY_CIRCLE, "Open in Player"))
|
||||
UserInterface::manager().showSourceEditor(&s);
|
||||
ImGui::SetCursorPos(pos);
|
||||
}
|
||||
|
||||
// change range
|
||||
static int _begin = -1;
|
||||
if (_begin < 0 || id != s.id())
|
||||
_begin = s.begin();
|
||||
static int _end = -1;
|
||||
if (_end < 0 || id != s.id())
|
||||
_end = s.end();
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
ImGui::DragIntRange2("Range", &_begin, &_end, 1, s.sequence().min, s.sequence().max);
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()){
|
||||
s.setRange( _begin, _end );
|
||||
std::ostringstream oss;
|
||||
oss << s.name() << ": Range " << _begin << "-" << _end;
|
||||
Action::manager().store(oss.str());
|
||||
_begin = _end = -1;
|
||||
}
|
||||
|
||||
// change framerate
|
||||
static int _fps = -1;
|
||||
if (_fps < 0 || id != s.id())
|
||||
_fps = s.framerate();
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
ImGui::SliderInt("Framerate", &_fps, 1, 30, "%d fps");
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()){
|
||||
s.setFramerate(_fps);
|
||||
std::ostringstream oss;
|
||||
oss << s.name() << ": Framerate " << _fps << " fps";
|
||||
Action::manager().store(oss.str());
|
||||
_fps = -1;
|
||||
}
|
||||
|
||||
// offer to open file browser at location
|
||||
std::string path = SystemToolkit::path_filename(s.sequence().location);
|
||||
std::string label = BaseToolkit::trunc_string(path, 25);
|
||||
label = BaseToolkit::transliterate(label);
|
||||
ImGuiToolkit::ButtonOpenUrl( label.c_str(), path.c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Folder");
|
||||
|
||||
if (id != s.id())
|
||||
id = s.id();
|
||||
}
|
||||
259
InfoVisitor.cpp
@@ -1,259 +0,0 @@
|
||||
#include "InfoVisitor.h"
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/gtc/constants.hpp>
|
||||
#include <glm/gtc/matrix_access.hpp>
|
||||
|
||||
#include <tinyxml2.h>
|
||||
#include "tinyxml2Toolkit.h"
|
||||
|
||||
#include "defines.h"
|
||||
#include "Log.h"
|
||||
#include "Scene.h"
|
||||
#include "Primitives.h"
|
||||
#include "ImageShader.h"
|
||||
#include "ImageProcessingShader.h"
|
||||
#include "MediaPlayer.h"
|
||||
#include "MediaSource.h"
|
||||
#include "SessionSource.h"
|
||||
#include "PatternSource.h"
|
||||
#include "DeviceSource.h"
|
||||
#include "NetworkSource.h"
|
||||
#include "MultiFileSource.h"
|
||||
#include "SessionCreator.h"
|
||||
#include "SessionVisitor.h"
|
||||
#include "Settings.h"
|
||||
#include "Mixer.h"
|
||||
#include "ActionManager.h"
|
||||
|
||||
#include "BaseToolkit.h"
|
||||
#include "UserInterfaceManager.h"
|
||||
#include "SystemToolkit.h"
|
||||
|
||||
|
||||
InfoVisitor::InfoVisitor() : brief_(true), current_id_(0)
|
||||
{
|
||||
}
|
||||
|
||||
void InfoVisitor::visit(Node &n)
|
||||
{
|
||||
}
|
||||
|
||||
void InfoVisitor::visit(Group &n)
|
||||
{
|
||||
}
|
||||
|
||||
void InfoVisitor::visit(Switch &n)
|
||||
{
|
||||
}
|
||||
|
||||
void InfoVisitor::visit(Scene &n)
|
||||
{
|
||||
}
|
||||
|
||||
void InfoVisitor::visit(Primitive &n)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void InfoVisitor::visit(MediaPlayer &mp)
|
||||
{
|
||||
// do not ask twice
|
||||
if (current_id_ == mp.id())
|
||||
return;
|
||||
|
||||
std::ostringstream oss;
|
||||
if (brief_) {
|
||||
oss << SystemToolkit::filename(mp.filename()) << std::endl;
|
||||
oss << mp.width() << " x " << mp.height() << ", ";
|
||||
oss << mp.media().codec_name.substr(0, mp.media().codec_name.find_first_of(" (,"));
|
||||
if (!mp.isImage())
|
||||
oss << ", " << std::fixed << std::setprecision(1) << mp.frameRate() << " fps";
|
||||
}
|
||||
else {
|
||||
oss << mp.filename() << std::endl;
|
||||
oss << mp.media().codec_name << std::endl;
|
||||
oss << mp.width() << " x " << mp.height() ;
|
||||
if (!mp.isImage())
|
||||
oss << ", " << std::fixed << std::setprecision(1) << mp.frameRate() << " fps";
|
||||
}
|
||||
|
||||
information_ = oss.str();
|
||||
|
||||
// remember (except if codec was not identified yet)
|
||||
if ( !mp.media().codec_name.empty() )
|
||||
current_id_ = mp.id();
|
||||
}
|
||||
|
||||
void InfoVisitor::visit(Stream &n)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
if (brief_) {
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
}
|
||||
information_ = oss.str();
|
||||
}
|
||||
|
||||
|
||||
void InfoVisitor::visit (MediaSource& s)
|
||||
{
|
||||
s.mediaplayer()->accept(*this);
|
||||
}
|
||||
|
||||
void InfoVisitor::visit (SessionFileSource& s)
|
||||
{
|
||||
if (current_id_ == s.id() || s.session() == nullptr)
|
||||
return;
|
||||
|
||||
std::ostringstream oss;
|
||||
if (brief_) {
|
||||
oss << SystemToolkit::filename(s.path()) << " (";
|
||||
oss << s.session()->numSource() << " sources)" << std::endl;
|
||||
}
|
||||
else
|
||||
oss << s.path() << std::endl;
|
||||
|
||||
if (s.session()->frame()){
|
||||
oss << s.session()->frame()->width() << " x " << s.session()->frame()->height() << ", ";
|
||||
oss << "RGB";
|
||||
}
|
||||
|
||||
information_ = oss.str();
|
||||
current_id_ = s.id();
|
||||
}
|
||||
|
||||
void InfoVisitor::visit (SessionGroupSource& s)
|
||||
{
|
||||
if (current_id_ == s.id() || s.session() == nullptr)
|
||||
return;
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << s.session()->numSource() << " sources in group" << std::endl;
|
||||
if (s.session()->frame()){
|
||||
oss << s.session()->frame()->width() << " x " << s.session()->frame()->height() << ", ";
|
||||
oss << "RGB";
|
||||
}
|
||||
|
||||
information_ = oss.str();
|
||||
current_id_ = s.id();
|
||||
}
|
||||
|
||||
void InfoVisitor::visit (RenderSource& s)
|
||||
{
|
||||
if (current_id_ == s.id())
|
||||
return;
|
||||
|
||||
information_ = "Rendering Output";
|
||||
current_id_ = s.id();
|
||||
}
|
||||
|
||||
void InfoVisitor::visit (CloneSource& s)
|
||||
{
|
||||
if (current_id_ == s.id())
|
||||
return;
|
||||
|
||||
information_ = "Clone of " + s.origin()->name();
|
||||
current_id_ = s.id();
|
||||
}
|
||||
|
||||
void InfoVisitor::visit (PatternSource& s)
|
||||
{
|
||||
if (current_id_ == s.id())
|
||||
return;
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << Pattern::pattern_types[s.pattern()->type()] << std::endl;
|
||||
if (s.pattern()) {
|
||||
oss << s.pattern()->width() << " x " << s.pattern()->height();
|
||||
oss << ", RGB";
|
||||
}
|
||||
|
||||
information_ = oss.str();
|
||||
current_id_ = s.id();
|
||||
}
|
||||
|
||||
void InfoVisitor::visit (DeviceSource& s)
|
||||
{
|
||||
if (current_id_ == s.id())
|
||||
return;
|
||||
|
||||
std::ostringstream oss;
|
||||
|
||||
DeviceConfigSet confs = Device::manager().config( Device::manager().index(s.device().c_str()));
|
||||
if ( !confs.empty()) {
|
||||
DeviceConfig best = *confs.rbegin();
|
||||
float fps = static_cast<float>(best.fps_numerator) / static_cast<float>(best.fps_denominator);
|
||||
|
||||
if (brief_) {
|
||||
oss << best.width << " x " << best.height << ", ";
|
||||
oss << best.stream << " " << best.format << ", ";
|
||||
oss << std::fixed << std::setprecision(1) << fps << " fps";
|
||||
}
|
||||
else {
|
||||
oss << s.device() << std::endl;
|
||||
oss << best.width << " x " << best.height << ", ";
|
||||
oss << best.stream << " " << best.format << ", ";
|
||||
oss << std::fixed << std::setprecision(1) << fps << " fps";
|
||||
}
|
||||
}
|
||||
|
||||
information_ = oss.str();
|
||||
current_id_ = s.id();
|
||||
}
|
||||
|
||||
void InfoVisitor::visit (NetworkSource& s)
|
||||
{
|
||||
if (current_id_ == s.id())
|
||||
return;
|
||||
|
||||
NetworkStream *ns = s.networkStream();
|
||||
|
||||
std::ostringstream oss;
|
||||
if (brief_) {
|
||||
oss << ns->resolution().x << " x " << ns->resolution().y << ", ";
|
||||
oss << NetworkToolkit::protocol_name[ns->protocol()] << std::endl;
|
||||
oss << "IP " << ns->serverAddress();
|
||||
}
|
||||
else {
|
||||
oss << s.connection() << " (IP " << ns->serverAddress() << ")" << std::endl;
|
||||
oss << ns->resolution().x << " x " << ns->resolution().y << ", ";
|
||||
oss << NetworkToolkit::protocol_name[ns->protocol()];
|
||||
}
|
||||
|
||||
information_ = oss.str();
|
||||
current_id_ = s.id();
|
||||
}
|
||||
|
||||
|
||||
void InfoVisitor::visit (MultiFileSource& s)
|
||||
{
|
||||
if (current_id_ == s.id())
|
||||
return;
|
||||
|
||||
std::ostringstream oss;
|
||||
if (brief_) {
|
||||
oss << s.sequence().width << " x " << s.sequence().height << ", ";
|
||||
oss << s.sequence().codec << std::endl;
|
||||
oss << s.sequence().max - s.sequence().min + 1 << " images [";
|
||||
oss << s.sequence().min << " - " << s.sequence().max << "]";
|
||||
}
|
||||
else {
|
||||
oss << s.sequence().location << " [";
|
||||
oss << s.sequence().min << " - " << s.sequence().max << "]" << std::endl;
|
||||
oss << s.sequence().width << " x " << s.sequence().height << ", ";
|
||||
oss << s.sequence().codec << " (";
|
||||
oss << s.sequence().max - s.sequence().min + 1 << " images)";
|
||||
}
|
||||
|
||||
information_ = oss.str();
|
||||
current_id_ = s.id();
|
||||
}
|
||||
250
Loopback.cpp
@@ -1,250 +0,0 @@
|
||||
#include <thread>
|
||||
|
||||
// Desktop OpenGL function loader
|
||||
#include <glad/glad.h>
|
||||
|
||||
// gstreamer
|
||||
#include <gst/gstformat.h>
|
||||
#include <gst/video/video.h>
|
||||
|
||||
#include "defines.h"
|
||||
#include "Settings.h"
|
||||
#include "GstToolkit.h"
|
||||
#include "SystemToolkit.h"
|
||||
#include "FrameBuffer.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include "Loopback.h"
|
||||
|
||||
bool Loopback::system_loopback_initialized = false;
|
||||
|
||||
#if defined(LINUX)
|
||||
|
||||
/**
|
||||
*
|
||||
* Linux video 4 linux loopback device
|
||||
*
|
||||
* 1) Linux system has to have the v4l2loopback package
|
||||
* See documentation at https://github.com/umlaeute/v4l2loopback
|
||||
*
|
||||
* $ sudo -A apt install v4l2loopback-dkms
|
||||
*
|
||||
* 2) User (sudo) has to install a v4l2loopback
|
||||
*
|
||||
* $ sudo -A modprobe v4l2loopback exclusive_caps=1 video_nr=10
|
||||
*
|
||||
* 3) But to do that, the user has to enter sudo passwd
|
||||
*
|
||||
* The command line above should be preceeded by
|
||||
* export SUDO_ASKPASS="/tmp/mysudo.sh"
|
||||
*
|
||||
* where mysudo.sh contains the following:
|
||||
* #!/bin/bash
|
||||
* zenity --password --title=Authentication
|
||||
*
|
||||
* 4) Optionaly, we can set the dynamic properties of the stream
|
||||
*
|
||||
* $ sudo v4l2loopback-ctl set-caps "RGBA:640x480" /dev/video10
|
||||
* $ sudo v4l2loopback-ctl set-fps 30 /dev/video10
|
||||
*
|
||||
* 5) Finally, the gstreamer pipeline can write into v4l2sink
|
||||
*
|
||||
* gst-launch-1.0 videotestsrc ! v4l2sink device=/dev/video10
|
||||
*
|
||||
*
|
||||
* Useful command lines for debug
|
||||
* $ v4l2-ctl --all -d 10
|
||||
* $ gst-launch-1.0 v4l2src device=/dev/video10 ! videoconvert ! autovideosink
|
||||
* $ gst-launch-1.0 videotestsrc ! v4l2sink device=/dev/video10
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
std::string Loopback::system_loopback_name = "/dev/video10";
|
||||
std::string Loopback::system_loopback_pipeline = "appsrc name=src ! videoconvert ! videorate ! video/x-raw,framerate=30/1 ! v4l2sink sync=false name=sink";
|
||||
|
||||
bool Loopback::initializeSystemLoopback()
|
||||
{
|
||||
if (!Loopback::systemLoopbackInitialized()) {
|
||||
|
||||
// create script for asking sudo password
|
||||
std::string sudoscript = SystemToolkit::full_filename(SystemToolkit::settings_path(), "sudo.sh");
|
||||
FILE *file = fopen(sudoscript.c_str(), "w");
|
||||
if (file) {
|
||||
fprintf(file, "#!/bin/bash\n");
|
||||
fprintf(file, "zenity --password --title=Authentication\n");
|
||||
fclose(file);
|
||||
|
||||
// make script executable
|
||||
int fildes = 0;
|
||||
fildes = open(sudoscript.c_str(), O_RDWR);
|
||||
fchmod(fildes, S_IRWXU | S_IRWXG | S_IROTH | S_IWOTH);
|
||||
close(fildes);
|
||||
|
||||
// create command line for installing v4l2loopback
|
||||
std::string cmdline = "export SUDO_ASKPASS=\"" + sudoscript + "\"\n";
|
||||
cmdline += "sudo -A apt install v4l2loopback-dkms 2>&1\n";
|
||||
cmdline += "sudo -A modprobe -r v4l2loopback 2>&1\n";
|
||||
cmdline += "sudo -A modprobe v4l2loopback exclusive_caps=1 video_nr=10 card_label=\"vimix loopback\" 2>&1\n";
|
||||
|
||||
// execute v4l2 command line
|
||||
std::string report;
|
||||
FILE *fp = popen(cmdline.c_str(), "r");
|
||||
if (fp != NULL) {
|
||||
|
||||
// get stdout content from command line
|
||||
char linestdout[PATH_MAX];
|
||||
while (fgets(linestdout, PATH_MAX, fp) != NULL)
|
||||
report += linestdout;
|
||||
|
||||
// error reported by pclose?
|
||||
if (pclose(fp) != 0 )
|
||||
Log::Warning("Failed to initialize system v4l2loopback\n%s", report.c_str());
|
||||
// okay, probaly all good...
|
||||
else
|
||||
system_loopback_initialized = true;
|
||||
}
|
||||
else
|
||||
Log::Warning("Failed to initialize system v4l2loopback\nCannot execute command line");
|
||||
|
||||
}
|
||||
else
|
||||
Log::Warning("Failed to initialize system v4l2loopback\nCannot create script", sudoscript.c_str());
|
||||
}
|
||||
|
||||
return system_loopback_initialized;
|
||||
}
|
||||
|
||||
bool Loopback::systemLoopbackInitialized()
|
||||
{
|
||||
// test if already initialized
|
||||
if (!system_loopback_initialized) {
|
||||
// check the existence of loopback device
|
||||
if ( SystemToolkit::file_exists(system_loopback_name) )
|
||||
system_loopback_initialized = true;
|
||||
}
|
||||
|
||||
return system_loopback_initialized;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
std::string Loopback::system_loopback_name = "undefined";
|
||||
std::string Loopback::system_loopback_pipeline = "";
|
||||
|
||||
|
||||
bool Loopback::initializeSystemLoopback()
|
||||
{
|
||||
system_loopback_initialized = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Loopback::systemLoopbackInitialized()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
Loopback::Loopback() : FrameGrabber()
|
||||
{
|
||||
frame_duration_ = gst_util_uint64_scale_int (1, GST_SECOND, 30); // fixed 30 FPS
|
||||
}
|
||||
|
||||
void Loopback::init(GstCaps *caps)
|
||||
{
|
||||
// ignore
|
||||
if (caps == nullptr)
|
||||
return;
|
||||
|
||||
if (!Loopback::systemLoopbackInitialized()){
|
||||
Log::Warning("Loopback system shall be initialized first.");
|
||||
finished_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// create a gstreamer pipeline
|
||||
std::string description = Loopback::system_loopback_pipeline;
|
||||
|
||||
// parse pipeline descriptor
|
||||
GError *error = NULL;
|
||||
pipeline_ = gst_parse_launch (description.c_str(), &error);
|
||||
if (error != NULL) {
|
||||
Log::Warning("Loopback Could not construct pipeline %s:\n%s", description.c_str(), error->message);
|
||||
g_clear_error (&error);
|
||||
finished_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// setup device sink
|
||||
g_object_set (G_OBJECT (gst_bin_get_by_name (GST_BIN (pipeline_), "sink")),
|
||||
"device", Loopback::system_loopback_name.c_str(),
|
||||
NULL);
|
||||
|
||||
// setup custom app source
|
||||
src_ = GST_APP_SRC( gst_bin_get_by_name (GST_BIN (pipeline_), "src") );
|
||||
if (src_) {
|
||||
|
||||
g_object_set (G_OBJECT (src_),
|
||||
"is-live", TRUE,
|
||||
NULL);
|
||||
|
||||
// configure stream
|
||||
gst_app_src_set_stream_type( src_, GST_APP_STREAM_TYPE_STREAM);
|
||||
gst_app_src_set_latency( src_, -1, 0);
|
||||
|
||||
// Set buffer size
|
||||
gst_app_src_set_max_bytes( src_, buffering_size_ );
|
||||
|
||||
// specify streaming framerate in the given caps
|
||||
GstCaps *tmp = gst_caps_copy( caps );
|
||||
GValue v = { 0, };
|
||||
g_value_init (&v, GST_TYPE_FRACTION);
|
||||
gst_value_set_fraction (&v, 30, 1); // fixed 30 FPS
|
||||
gst_caps_set_value(tmp, "framerate", &v);
|
||||
g_value_unset (&v);
|
||||
|
||||
// instruct src to use the caps
|
||||
caps_ = gst_caps_copy( tmp );
|
||||
gst_app_src_set_caps (src_, caps_);
|
||||
gst_caps_unref (tmp);
|
||||
|
||||
// setup callbacks
|
||||
GstAppSrcCallbacks callbacks;
|
||||
callbacks.need_data = FrameGrabber::callback_need_data;
|
||||
callbacks.enough_data = FrameGrabber::callback_enough_data;
|
||||
callbacks.seek_data = NULL; // stream type is not seekable
|
||||
gst_app_src_set_callbacks( src_, &callbacks, this, NULL);
|
||||
|
||||
}
|
||||
else {
|
||||
Log::Warning("Loopback Could not configure source");
|
||||
finished_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// start recording
|
||||
GstStateChangeReturn ret = gst_element_set_state (pipeline_, GST_STATE_PLAYING);
|
||||
if (ret == GST_STATE_CHANGE_FAILURE) {
|
||||
Log::Warning("Loopback Could not open %s", Loopback::system_loopback_name.c_str());
|
||||
finished_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// all good
|
||||
#if defined(LINUX)
|
||||
Log::Notify("Loopback started (v4l2loopback on %s)", Loopback::system_loopback_name.c_str());
|
||||
#else
|
||||
Log::Notify("Loopback started (%s)", Loopback::system_loopback_name.c_str());
|
||||
#endif
|
||||
// start
|
||||
active_ = true;
|
||||
}
|
||||
|
||||
void Loopback::terminate()
|
||||
{
|
||||
Log::Notify("Loopback to %s terminated.", Loopback::system_loopback_name.c_str());
|
||||
}
|
||||
31
Loopback.h
@@ -1,31 +0,0 @@
|
||||
#ifndef LOOPBACK_H
|
||||
#define LOOPBACK_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <gst/pbutils/pbutils.h>
|
||||
#include <gst/app/gstappsrc.h>
|
||||
|
||||
#include "FrameGrabber.h"
|
||||
|
||||
|
||||
class Loopback : public FrameGrabber
|
||||
{
|
||||
static std::string system_loopback_pipeline;
|
||||
static std::string system_loopback_name;
|
||||
static bool system_loopback_initialized;
|
||||
|
||||
void init(GstCaps *caps) override;
|
||||
void terminate() override;
|
||||
|
||||
public:
|
||||
|
||||
Loopback();
|
||||
|
||||
static bool systemLoopbackInitialized();
|
||||
static bool initializeSystemLoopback();
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // LOOPBACK_H
|
||||
@@ -1,169 +0,0 @@
|
||||
#include <sstream>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include "PatternSource.h"
|
||||
|
||||
#include "defines.h"
|
||||
#include "ImageShader.h"
|
||||
#include "Resource.h"
|
||||
#include "Decorations.h"
|
||||
#include "Stream.h"
|
||||
#include "Visitor.h"
|
||||
#include "Log.h"
|
||||
|
||||
#define MAX_PATTERN 24
|
||||
|
||||
// smpte (0) – SMPTE 100%% color bars
|
||||
// snow (1) – Random (television snow)
|
||||
// black (2) – 100%% Black
|
||||
// white (3) – 100%% White
|
||||
// red (4) – Red
|
||||
// green (5) – Green
|
||||
// blue (6) – Blue
|
||||
// checkers-1 (7) – Checkers 1px
|
||||
// checkers-2 (8) – Checkers 2px
|
||||
// checkers-4 (9) – Checkers 4px
|
||||
// checkers-8 (10) – Checkers 8px
|
||||
// circular (11) – Circular
|
||||
// blink (12) – Blink
|
||||
// smpte75 (13) – SMPTE 75%% color bars
|
||||
// zone-plate (14) – Zone plate
|
||||
// gamut (15) – Gamut checkers
|
||||
// chroma-zone-plate (16) – Chroma zone plate
|
||||
// solid-color (17) – Solid color
|
||||
// ball (18) – Moving ball
|
||||
// smpte100 (19) – SMPTE 100%% color bars
|
||||
// bar (20) – Bar
|
||||
// pinwheel (21) – Pinwheel
|
||||
// spokes (22) – Spokes
|
||||
// gradient (23) – Gradient
|
||||
// colors (24) – Colors
|
||||
const char* pattern_internal_[MAX_PATTERN] = { "videotestsrc pattern=black",
|
||||
"videotestsrc pattern=white",
|
||||
"videotestsrc pattern=gradient",
|
||||
"videotestsrc pattern=checkers-1 ! video/x-raw,format=GRAY8 ! videoconvert",
|
||||
"videotestsrc pattern=checkers-8 ! video/x-raw,format=GRAY8 ! videoconvert",
|
||||
"videotestsrc pattern=circular",
|
||||
"frei0r-src-lissajous0r ratiox=0.001 ratioy=0.999 ! videoconvert",
|
||||
"videotestsrc pattern=pinwheel",
|
||||
"videotestsrc pattern=spokes",
|
||||
"videotestsrc pattern=red",
|
||||
"videotestsrc pattern=green",
|
||||
"videotestsrc pattern=blue",
|
||||
"videotestsrc pattern=smpte100",
|
||||
"videotestsrc pattern=colors",
|
||||
"videotestsrc pattern=smpte",
|
||||
"videotestsrc pattern=snow",
|
||||
"videotestsrc pattern=blink",
|
||||
"videotestsrc pattern=zone-plate",
|
||||
"videotestsrc pattern=chroma-zone-plate",
|
||||
"videotestsrc pattern=bar horizontal-speed=5",
|
||||
"videotestsrc pattern=ball",
|
||||
"frei0r-src-ising0r",
|
||||
"videotestsrc pattern=black ! timeoverlay halignment=center valignment=center font-desc=\"Sans, 72\" ",
|
||||
"videotestsrc pattern=black ! clockoverlay halignment=center valignment=center font-desc=\"Sans, 72\" "
|
||||
};
|
||||
|
||||
std::vector<std::string> Pattern::pattern_types = { "Black",
|
||||
"White",
|
||||
"Gradient",
|
||||
"Checkers 1x1 px",
|
||||
"Checkers 8x8 px",
|
||||
"Circles",
|
||||
"Lissajous",
|
||||
"Pinwheel",
|
||||
"Spokes",
|
||||
"Red",
|
||||
"Green",
|
||||
"Blue",
|
||||
"Color bars",
|
||||
"RGB grid",
|
||||
"SMPTE test pattern",
|
||||
"Television snow",
|
||||
"Blink",
|
||||
"Fresnel zone plate",
|
||||
"Chroma zone plate",
|
||||
"Bar moving",
|
||||
"Ball bouncing"
|
||||
#if GST_VERSION_MINOR > 17
|
||||
,
|
||||
"Blob",
|
||||
"Timer",
|
||||
"Clock"
|
||||
#endif
|
||||
};
|
||||
|
||||
Pattern::Pattern() : Stream(), type_(MAX_PATTERN) // invalid pattern
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
glm::ivec2 Pattern::resolution()
|
||||
{
|
||||
return glm::ivec2( width_, height_);
|
||||
}
|
||||
|
||||
|
||||
void Pattern::open( uint pattern, glm::ivec2 res )
|
||||
{
|
||||
type_ = MIN(pattern, MAX_PATTERN-1);
|
||||
std::string gstreamer_pattern = pattern_internal_[type_];
|
||||
|
||||
// there is always a special case...
|
||||
switch(type_)
|
||||
{
|
||||
case 18: // zone plates
|
||||
case 17:
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << " kx2=" << (int)(res.x * 10.f / res.y) << " ky2=10 kt=4";
|
||||
gstreamer_pattern += oss.str(); // Zone plate
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// all patterns before 'SMPTE test pattern' are single frames (not animated)
|
||||
single_frame_ = type_ < 14;
|
||||
Log::Info("Stream %d SingleFrame", single_frame_);
|
||||
|
||||
// (private) open stream
|
||||
Stream::open(gstreamer_pattern, res.x, res.y);
|
||||
}
|
||||
|
||||
PatternSource::PatternSource(uint64_t id) : StreamSource(id)
|
||||
{
|
||||
// create stream
|
||||
stream_ = static_cast<Stream *>( new Pattern );
|
||||
|
||||
// set symbol
|
||||
symbol_ = new Symbol(Symbol::PATTERN, glm::vec3(0.75f, 0.75f, 0.01f));
|
||||
symbol_->scale_.y = 1.5f;
|
||||
}
|
||||
|
||||
void PatternSource::setPattern(uint type, glm::ivec2 resolution)
|
||||
{
|
||||
Log::Notify("Creating Source with pattern '%s'", Pattern::pattern_types[type].c_str());
|
||||
|
||||
// open gstreamer
|
||||
pattern()->open( (uint) type, resolution );
|
||||
stream_->play(true);
|
||||
|
||||
// will be ready after init and one frame rendered
|
||||
ready_ = false;
|
||||
}
|
||||
|
||||
void PatternSource::accept(Visitor& v)
|
||||
{
|
||||
Source::accept(v);
|
||||
if (!failed())
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
Pattern *PatternSource::pattern() const
|
||||
{
|
||||
return dynamic_cast<Pattern *>(stream_);
|
||||
}
|
||||
|
||||
|
||||
69
README.md
@@ -7,8 +7,8 @@ computer generated graphics, with image processing effects in real-time.
|
||||
Its intuitive and hands-on user interface gives direct control on image opacity and
|
||||
shape for producing live graphics during concerts and VJ-ing sessions.
|
||||
|
||||
The ouput image is typically projected full-screen on an external
|
||||
monitor or a projector, but can be recorded live (no audio).
|
||||
The output image is typically projected full-screen on an external
|
||||
monitor or a projector, and can be streamed live (SRT, Shmdata) or recorded (without audio).
|
||||
|
||||
vimix is the successor for GLMixer - https://sourceforge.net/projects/glmixer/
|
||||
|
||||
@@ -17,45 +17,57 @@ vimix is the successor for GLMixer - https://sourceforge.net/projects/glmixer/
|
||||
GPL-3.0-or-later
|
||||
See [LICENSE](https://github.com/brunoherbelin/vimix/blob/master/LICENSE)
|
||||
|
||||
# Install
|
||||
# Install vimix
|
||||
|
||||
Check the [Quick Installation Guide](https://github.com/brunoherbelin/vimix/wiki/Quick-Installation-Guide)
|
||||
|
||||
### Linux
|
||||
|
||||
Download and install a release package from https://snapcraft.io/vimix
|
||||
Download and install a released [flatpak package](https://flathub.org/apps/details/io.github.brunoherbelin.Vimix)
|
||||
|
||||
$ snap install vimix
|
||||
~$ flatpak install --user vimix
|
||||
|
||||
NB: You'll need to setup the snap permissions.
|
||||
NB: Building your flatpak package is an option for testing the latest beta version from git ; instructions are [here](https://github.com/brunoherbelin/vimix/tree/master/flatpak).
|
||||
|
||||
|
||||
Download and install a released [snap package](https://snapcraft.io/vimix) (slower release frequency)
|
||||
|
||||
~$ snap install vimix
|
||||
|
||||
Install the stable debian package (slower release frequency)
|
||||
|
||||
~$ sudo apt install vimix
|
||||
|
||||
### Mac OSX
|
||||
|
||||
Download and open a release package from https://github.com/brunoherbelin/vimix/releases
|
||||
|
||||
NB: You'll need to accept the exception in OSX security preference.
|
||||
|
||||
# Build vimix
|
||||
|
||||
## Clone
|
||||
|
||||
$ git clone --recursive https://github.com/brunoherbelin/vimix.git
|
||||
~$ git clone --recursive https://github.com/brunoherbelin/vimix.git
|
||||
|
||||
This will create the directory 'vimix', download the latest version of vimix code,
|
||||
and (recursively) clone all the internal git dependencies.
|
||||
|
||||
To only update a cloned git copy:
|
||||
|
||||
$ git pull
|
||||
~$ git pull
|
||||
|
||||
## Compile
|
||||
|
||||
First time after git clone:
|
||||
|
||||
$ mkdir vimix-build
|
||||
$ cd vimix-build
|
||||
$ cmake -DCMAKE_BUILD_TYPE=Release ../vimix
|
||||
~$ mkdir vimix-build
|
||||
~$ cd vimix-build
|
||||
~$ cmake -DCMAKE_BUILD_TYPE=Release ../vimix
|
||||
|
||||
Compile (or re-compile after pull):
|
||||
|
||||
$ cmake --build .
|
||||
~$ cmake --build .
|
||||
|
||||
### Dependencies
|
||||
|
||||
@@ -69,31 +81,36 @@ Compile (or re-compile after pull):
|
||||
**Libraries:**
|
||||
|
||||
- gstreamer
|
||||
- gst-plugins : base, good, bad & ugly
|
||||
- gst-plugins (libav, base, good, bad & ugly)
|
||||
- libglfw3
|
||||
- libicu
|
||||
- libicu (icu-i18n icu-uc icu-io)
|
||||
|
||||
Optionnal:
|
||||
|
||||
- glm
|
||||
- stb
|
||||
- TinyXML2
|
||||
- AbletonLink
|
||||
- Shmdata
|
||||
|
||||
#### Install Dependencies
|
||||
|
||||
**Ubuntu**
|
||||
|
||||
$ apt-get install build-essential cmake libpng-dev libglfw3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libicu-dev libgtk-3-dev
|
||||
~$ apt-get install build-essential cmake libpng-dev libglfw3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-libav libicu-dev libgtk-3-dev
|
||||
|
||||
Optionnal:
|
||||
|
||||
~$ apt-get install libglm-dev libstb-dev libtinyxml2-dev ableton-link-dev
|
||||
|
||||
|
||||
Follow the instructions to [install Shmdata](https://gitlab.com/sat-mtl/tools/shmdata).
|
||||
|
||||
**OSX with Brew**
|
||||
|
||||
$ brew install cmake libpng glfw gstreamer gst-libav gst-plugins-bad gst-plugins-base gst-plugins-good gst-plugins-ugly icu4c
|
||||
~$ brew install cmake libpng glfw gstreamer gst-libav gst-plugins-bad gst-plugins-base gst-plugins-good gst-plugins-ugly icu4c
|
||||
|
||||
|
||||
#### Generate snap
|
||||
|
||||
To generate the snap (from vimix directory):
|
||||
|
||||
$ snapcraft
|
||||
|
||||
To install the locally created snap:
|
||||
|
||||
$ snap install --dangerous vimix_0.5_amd64.snap
|
||||
|
||||
### Memcheck
|
||||
|
||||
To generate memory usage plots in [massif format](https://valgrind.org/docs/manual/ms-manual.html):
|
||||
|
||||
147
RenderView.cpp
@@ -1,147 +0,0 @@
|
||||
#include <thread>
|
||||
|
||||
// Opengl
|
||||
#include <glad/glad.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/matrix_access.hpp>
|
||||
#include <glm/gtx/vector_angle.hpp>
|
||||
|
||||
#include "defines.h"
|
||||
#include "Settings.h"
|
||||
#include "Decorations.h"
|
||||
|
||||
#include "RenderView.h"
|
||||
|
||||
|
||||
RenderView::RenderView() : View(RENDERING), frame_buffer_(nullptr), fading_overlay_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
RenderView::~RenderView()
|
||||
{
|
||||
if (frame_buffer_)
|
||||
delete frame_buffer_;
|
||||
if (fading_overlay_)
|
||||
delete fading_overlay_;
|
||||
}
|
||||
|
||||
void RenderView::setFading(float f)
|
||||
{
|
||||
if (fading_overlay_ == nullptr)
|
||||
fading_overlay_ = new Surface;
|
||||
|
||||
fading_overlay_->shader()->color.a = CLAMP( f < EPSILON ? 0.f : f, 0.f, 1.f);
|
||||
}
|
||||
|
||||
float RenderView::fading() const
|
||||
{
|
||||
if (fading_overlay_)
|
||||
return fading_overlay_->shader()->color.a;
|
||||
else
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
void RenderView::setResolution(glm::vec3 resolution, bool useAlpha)
|
||||
{
|
||||
// use default resolution if invalid resolution is given (default behavior)
|
||||
if (resolution.x < 2.f || resolution.y < 2.f)
|
||||
resolution = FrameBuffer::getResolutionFromParameters(Settings::application.render.ratio, Settings::application.render.res);
|
||||
|
||||
// do we need to change resolution ?
|
||||
if (frame_buffer_ && frame_buffer_->resolution() != resolution) {
|
||||
|
||||
// new frame buffer
|
||||
delete frame_buffer_;
|
||||
frame_buffer_ = nullptr;
|
||||
}
|
||||
|
||||
if (!frame_buffer_)
|
||||
// output frame is an RBG Multisamples FrameBuffer
|
||||
frame_buffer_ = new FrameBuffer(resolution, useAlpha, true);
|
||||
|
||||
// reset fading
|
||||
setFading();
|
||||
}
|
||||
|
||||
void RenderView::draw()
|
||||
{
|
||||
static glm::mat4 projection = glm::ortho(-1.f, 1.f, 1.f, -1.f, -SCENE_DEPTH, 1.f);
|
||||
|
||||
if (frame_buffer_) {
|
||||
// draw in frame buffer
|
||||
glm::mat4 P = glm::scale( projection, glm::vec3(1.f / frame_buffer_->aspectRatio(), 1.f, 1.f));
|
||||
|
||||
// render the scene normally (pre-multiplied alpha in RGB)
|
||||
frame_buffer_->begin();
|
||||
scene.root()->draw(glm::identity<glm::mat4>(), P);
|
||||
fading_overlay_->draw(glm::identity<glm::mat4>(), projection);
|
||||
frame_buffer_->end();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderView::drawThumbnail()
|
||||
{
|
||||
if (frame_buffer_) {
|
||||
// if a thumbnailer is pending
|
||||
if (thumbnailer_.size() > 0) {
|
||||
|
||||
try {
|
||||
// new thumbnailing framebuffer
|
||||
FrameBuffer *frame_thumbnail = new FrameBuffer( glm::vec3(SESSION_THUMBNAIL_HEIGHT * frame_buffer_->aspectRatio(), SESSION_THUMBNAIL_HEIGHT, 1.f) );
|
||||
|
||||
// render
|
||||
if (Settings::application.render.blit) {
|
||||
if ( !frame_buffer_->blit(frame_thumbnail) )
|
||||
throw std::runtime_error("no blit");
|
||||
}
|
||||
else {
|
||||
FrameBufferSurface *thumb = new FrameBufferSurface(frame_buffer_);
|
||||
frame_thumbnail->begin();
|
||||
thumb->draw(glm::identity<glm::mat4>(), frame_thumbnail->projection());
|
||||
frame_thumbnail->end();
|
||||
delete thumb;
|
||||
}
|
||||
|
||||
// return valid thumbnail promise
|
||||
thumbnailer_.back().set_value( frame_thumbnail->image() );
|
||||
|
||||
// done with thumbnailing framebuffer
|
||||
delete frame_thumbnail;
|
||||
}
|
||||
catch(...) {
|
||||
// return failed thumbnail promise
|
||||
thumbnailer_.back().set_exception(std::current_exception());
|
||||
}
|
||||
|
||||
// done with this promise
|
||||
thumbnailer_.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FrameBufferImage *RenderView::thumbnail ()
|
||||
{
|
||||
// by default null image
|
||||
FrameBufferImage *img = nullptr;
|
||||
|
||||
// this function is always called from a parallel thread
|
||||
// So we wait for a few frames of rendering before trying to capture a thumbnail
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
// create and store a promise for a FrameBufferImage
|
||||
thumbnailer_.emplace_back( std::promise<FrameBufferImage *>() );
|
||||
|
||||
// future will return the promised FrameBufferImage
|
||||
std::future<FrameBufferImage *> ft = thumbnailer_.back().get_future();
|
||||
|
||||
try {
|
||||
// wait for a valid return value from promise
|
||||
img = ft.get();
|
||||
}
|
||||
// catch any failed promise
|
||||
catch (const std::exception&){
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
137
Screenshot.cpp
@@ -1,137 +0,0 @@
|
||||
#include "Screenshot.h"
|
||||
|
||||
#include <memory.h>
|
||||
#include <assert.h>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
// standalone image loader
|
||||
#include <stb_image.h>
|
||||
#include <stb_image_write.h>
|
||||
|
||||
|
||||
|
||||
Screenshot::Screenshot()
|
||||
{
|
||||
Width = Height = 0;
|
||||
Data = nullptr;
|
||||
Pbo = 0;
|
||||
Pbo_size = 0;
|
||||
Pbo_full = false;
|
||||
}
|
||||
|
||||
Screenshot::~Screenshot()
|
||||
{
|
||||
if (Pbo > 0)
|
||||
glDeleteBuffers(1, &Pbo);
|
||||
if (Data)
|
||||
free(Data);
|
||||
}
|
||||
|
||||
bool Screenshot::isFull()
|
||||
{
|
||||
return Pbo_full;
|
||||
}
|
||||
|
||||
void Screenshot::captureGL(int x, int y, int w, int h)
|
||||
{
|
||||
Width = w - x;
|
||||
Height = h - y;
|
||||
unsigned int size = Width * Height * 3;
|
||||
|
||||
// create BPO
|
||||
if (Pbo == 0)
|
||||
glGenBuffers(1, &Pbo);
|
||||
|
||||
// bind
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, Pbo);
|
||||
|
||||
// init
|
||||
if (Pbo_size != size) {
|
||||
Pbo_size = size;
|
||||
if (Data) free(Data);
|
||||
Data = (unsigned char*) malloc(Pbo_size);
|
||||
glBufferData(GL_PIXEL_PACK_BUFFER, Pbo_size, NULL, GL_STREAM_READ);
|
||||
}
|
||||
|
||||
// screenshot to PBO (fast)
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
glReadPixels(x, y, w, h, GL_RGB, GL_UNSIGNED_BYTE, 0);
|
||||
Pbo_full = true;
|
||||
|
||||
// done
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
void Screenshot::save(std::string filename)
|
||||
{
|
||||
// is there something to save?
|
||||
if (Pbo && Pbo_size > 0 && Pbo_full) {
|
||||
|
||||
// bind buffer
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, Pbo);
|
||||
|
||||
// get pixels (quite fast)
|
||||
unsigned char* ptr = (unsigned char*) glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
|
||||
if (NULL != ptr) {
|
||||
memmove(Data, ptr, Pbo_size);
|
||||
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
||||
}
|
||||
|
||||
// initiate saving in thread (slow)
|
||||
std::thread(storeToFile, this, filename).detach();
|
||||
|
||||
// ready for next
|
||||
Pbo_full = false;
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Screenshot::RemoveAlpha()
|
||||
{
|
||||
unsigned int* p = (unsigned int*)Data;
|
||||
int n = Width * Height;
|
||||
while (n-- > 0)
|
||||
{
|
||||
*p |= 0xFF000000;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
void Screenshot::FlipVertical()
|
||||
{
|
||||
int comp = 4;
|
||||
int stride = Width * comp;
|
||||
unsigned char* line_tmp = new unsigned char[stride];
|
||||
unsigned char* line_a = (unsigned char*)Data;
|
||||
unsigned char* line_b = (unsigned char*)Data + (stride * (Height - 1));
|
||||
while (line_a < line_b)
|
||||
{
|
||||
memcpy(line_tmp, line_a, stride);
|
||||
memcpy(line_a, line_b, stride);
|
||||
memcpy(line_b, line_tmp, stride);
|
||||
line_a += stride;
|
||||
line_b -= stride;
|
||||
}
|
||||
delete[] line_tmp;
|
||||
}
|
||||
|
||||
// Thread to perform slow operation of saving to file
|
||||
void Screenshot::storeToFile(Screenshot *s, std::string filename)
|
||||
{
|
||||
static std::atomic<bool> ScreenshotSavePending_ = false;
|
||||
// only one save at a time
|
||||
if (ScreenshotSavePending_)
|
||||
return;
|
||||
ScreenshotSavePending_ = true;
|
||||
// got data to save ?
|
||||
if (s && s->Data) {
|
||||
// save file
|
||||
stbi_flip_vertically_on_write(true);
|
||||
stbi_write_png(filename.c_str(), s->Width, s->Height, 3, s->Data, s->Width * 3);
|
||||
}
|
||||
ScreenshotSavePending_ = false;
|
||||
}
|
||||
576
Session.cpp
@@ -1,576 +0,0 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "defines.h"
|
||||
#include "BaseToolkit.h"
|
||||
#include "Source.h"
|
||||
#include "Settings.h"
|
||||
#include "FrameBuffer.h"
|
||||
#include "Session.h"
|
||||
#include "FrameGrabber.h"
|
||||
#include "SessionCreator.h"
|
||||
#include "SessionSource.h"
|
||||
#include "MixingGroup.h"
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
SessionNote::SessionNote(const std::string &t, bool l, int s): label(std::to_string(BaseToolkit::uniqueId())),
|
||||
text(t), large(l), stick(s), pos(glm::vec2(520.f, 30.f)), size(glm::vec2(220.f, 220.f))
|
||||
{
|
||||
}
|
||||
|
||||
Session::Session() : active_(true), filename_(""), failedSource_(nullptr), fading_target_(0.f), thumbnail_(nullptr)
|
||||
{
|
||||
config_[View::RENDERING] = new Group;
|
||||
config_[View::RENDERING]->scale_ = glm::vec3(0.f);
|
||||
|
||||
config_[View::GEOMETRY] = new Group;
|
||||
config_[View::GEOMETRY]->scale_ = Settings::application.views[View::GEOMETRY].default_scale;
|
||||
config_[View::GEOMETRY]->translation_ = Settings::application.views[View::GEOMETRY].default_translation;
|
||||
|
||||
config_[View::LAYER] = new Group;
|
||||
config_[View::LAYER]->scale_ = Settings::application.views[View::LAYER].default_scale;
|
||||
config_[View::LAYER]->translation_ = Settings::application.views[View::LAYER].default_translation;
|
||||
|
||||
config_[View::MIXING] = new Group;
|
||||
config_[View::MIXING]->scale_ = Settings::application.views[View::MIXING].default_scale;
|
||||
config_[View::MIXING]->translation_ = Settings::application.views[View::MIXING].default_translation;
|
||||
|
||||
config_[View::TEXTURE] = new Group;
|
||||
config_[View::TEXTURE]->scale_ = Settings::application.views[View::TEXTURE].default_scale;
|
||||
config_[View::TEXTURE]->translation_ = Settings::application.views[View::TEXTURE].default_translation;
|
||||
|
||||
snapshots_.xmlDoc_ = new tinyxml2::XMLDocument;
|
||||
}
|
||||
|
||||
|
||||
Session::~Session()
|
||||
{
|
||||
// TODO delete all mixing groups?
|
||||
auto group_iter = mixing_groups_.begin();
|
||||
while ( group_iter != mixing_groups_.end() ){
|
||||
delete (*group_iter);
|
||||
group_iter = mixing_groups_.erase(group_iter);
|
||||
}
|
||||
|
||||
// delete all sources
|
||||
for(auto it = sources_.begin(); it != sources_.end(); ) {
|
||||
// erase this source from the list
|
||||
it = deleteSource(*it);
|
||||
}
|
||||
|
||||
delete config_[View::RENDERING];
|
||||
delete config_[View::GEOMETRY];
|
||||
delete config_[View::LAYER];
|
||||
delete config_[View::MIXING];
|
||||
delete config_[View::TEXTURE];
|
||||
|
||||
snapshots_.keys_.clear();
|
||||
delete snapshots_.xmlDoc_;
|
||||
}
|
||||
|
||||
void Session::setActive (bool on)
|
||||
{
|
||||
if (active_ != on) {
|
||||
active_ = on;
|
||||
for(auto it = sources_.begin(); it != sources_.end(); ++it) {
|
||||
(*it)->setActive(active_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update all sources
|
||||
void Session::update(float dt)
|
||||
{
|
||||
// no update until render view is initialized
|
||||
if ( render_.frame() == nullptr )
|
||||
return;
|
||||
|
||||
// pre-render all sources
|
||||
failedSource_ = nullptr;
|
||||
bool ready = true;
|
||||
for( SourceList::iterator it = sources_.begin(); it != sources_.end(); ++it){
|
||||
|
||||
// ensure the RenderSource is rendering *this* session
|
||||
RenderSource *rs = dynamic_cast<RenderSource *>( *it );
|
||||
if ( rs!= nullptr && rs->session() != this )
|
||||
rs->setSession(this);
|
||||
|
||||
// discard failed source
|
||||
if ( (*it)->failed() ) {
|
||||
failedSource_ = (*it);
|
||||
}
|
||||
// render normally
|
||||
else {
|
||||
if ( !(*it)->ready() )
|
||||
ready = false;
|
||||
// render the source
|
||||
(*it)->render();
|
||||
// update the source
|
||||
(*it)->update(dt);
|
||||
}
|
||||
}
|
||||
|
||||
// update session's mixing groups
|
||||
auto group_iter = mixing_groups_.begin();
|
||||
while ( group_iter != mixing_groups_.end() ){
|
||||
// update all valid groups
|
||||
if ((*group_iter)->size() > 1) {
|
||||
(*group_iter)->update(dt);
|
||||
group_iter++;
|
||||
}
|
||||
else
|
||||
// delete invalid groups (singletons)
|
||||
group_iter = deleteMixingGroup(group_iter);
|
||||
}
|
||||
|
||||
// apply fading (smooth dicotomic reaching)
|
||||
float f = render_.fading();
|
||||
if ( ABS_DIFF(f, fading_target_) > EPSILON) {
|
||||
render_.setFading( f + ( fading_target_ - f ) / 2.f);
|
||||
}
|
||||
|
||||
// update the scene tree
|
||||
render_.update(dt);
|
||||
|
||||
// draw render view in Frame Buffer
|
||||
render_.draw();
|
||||
|
||||
// draw the thumbnail only after all sources are ready
|
||||
if (ready)
|
||||
render_.drawThumbnail();
|
||||
}
|
||||
|
||||
SourceList::iterator Session::addSource(Source *s)
|
||||
{
|
||||
// lock before change
|
||||
access_.lock();
|
||||
|
||||
// find the source
|
||||
SourceList::iterator its = find(s);
|
||||
|
||||
// ok, its NOT in the list !
|
||||
if (its == sources_.end()) {
|
||||
// insert the source in the rendering
|
||||
render_.scene.ws()->attach(s->group(View::RENDERING));
|
||||
// insert the source to the beginning of the list
|
||||
sources_.push_front(s);
|
||||
// return the iterator to the source created at the beginning
|
||||
its = sources_.begin();
|
||||
}
|
||||
|
||||
// unlock access
|
||||
access_.unlock();
|
||||
|
||||
return its;
|
||||
}
|
||||
|
||||
SourceList::iterator Session::deleteSource(Source *s)
|
||||
{
|
||||
// lock before change
|
||||
access_.lock();
|
||||
|
||||
// find the source
|
||||
SourceList::iterator its = find(s);
|
||||
// ok, its in the list !
|
||||
if (its != sources_.end()) {
|
||||
// remove Node from the rendering scene
|
||||
render_.scene.ws()->detach( s->group(View::RENDERING) );
|
||||
// inform group
|
||||
if (s->mixingGroup() != nullptr)
|
||||
s->mixingGroup()->detach(s);
|
||||
// erase the source from the update list & get next element
|
||||
its = sources_.erase(its);
|
||||
// delete the source : safe now
|
||||
delete s;
|
||||
}
|
||||
|
||||
// unlock access
|
||||
access_.unlock();
|
||||
|
||||
// return end of next element
|
||||
return its;
|
||||
}
|
||||
|
||||
void Session::removeSource(Source *s)
|
||||
{
|
||||
// lock before change
|
||||
access_.lock();
|
||||
|
||||
// find the source
|
||||
SourceList::iterator its = find(s);
|
||||
// ok, its in the list !
|
||||
if (its != sources_.end()) {
|
||||
// remove Node from the rendering scene
|
||||
render_.scene.ws()->detach( s->group(View::RENDERING) );
|
||||
// inform group
|
||||
if (s->mixingGroup() != nullptr)
|
||||
s->mixingGroup()->detach(s);
|
||||
// erase the source from the update list & get next element
|
||||
sources_.erase(its);
|
||||
}
|
||||
|
||||
// unlock access
|
||||
access_.unlock();
|
||||
}
|
||||
|
||||
Source *Session::popSource()
|
||||
{
|
||||
Source *s = nullptr;
|
||||
|
||||
SourceList::iterator its = sources_.begin();
|
||||
if (its != sources_.end())
|
||||
{
|
||||
s = *its;
|
||||
// remove Node from the rendering scene
|
||||
render_.scene.ws()->detach( s->group(View::RENDERING) );
|
||||
// erase the source from the update list & get next element
|
||||
sources_.erase(its);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void replaceThumbnail(Session *s)
|
||||
{
|
||||
if (s != nullptr) {
|
||||
FrameBufferImage *t = s->renderThumbnail();
|
||||
if (t != nullptr) // avoid recursive infinite loop
|
||||
s->setThumbnail(t);
|
||||
}
|
||||
}
|
||||
|
||||
void Session::setThumbnail(FrameBufferImage *t)
|
||||
{
|
||||
resetThumbnail();
|
||||
// replace with given image
|
||||
if (t != nullptr)
|
||||
thumbnail_ = t;
|
||||
// no thumbnail image given: capture from rendering in a parallel thread
|
||||
else
|
||||
std::thread( replaceThumbnail, this ).detach();
|
||||
}
|
||||
|
||||
void Session::resetThumbnail()
|
||||
{
|
||||
if (thumbnail_ != nullptr)
|
||||
delete thumbnail_;
|
||||
thumbnail_ = nullptr;
|
||||
}
|
||||
|
||||
void Session::setResolution(glm::vec3 resolution, bool useAlpha)
|
||||
{
|
||||
// setup the render view: if not specified the default config resulution will be used
|
||||
render_.setResolution( resolution, useAlpha );
|
||||
// store the actual resolution set in the render view
|
||||
config_[View::RENDERING]->scale_ = render_.resolution();
|
||||
}
|
||||
|
||||
void Session::setFading(float f, bool forcenow)
|
||||
{
|
||||
if (forcenow)
|
||||
render_.setFading( f );
|
||||
|
||||
fading_target_ = CLAMP(f, 0.f, 1.f);
|
||||
}
|
||||
|
||||
SourceList::iterator Session::begin()
|
||||
{
|
||||
return sources_.begin();
|
||||
}
|
||||
|
||||
SourceList::iterator Session::end()
|
||||
{
|
||||
return sources_.end();
|
||||
}
|
||||
|
||||
SourceList::iterator Session::find(Source *s)
|
||||
{
|
||||
return std::find(sources_.begin(), sources_.end(), s);
|
||||
}
|
||||
|
||||
SourceList::iterator Session::find(uint64_t id)
|
||||
{
|
||||
return std::find_if(sources_.begin(), sources_.end(), Source::hasId(id));
|
||||
}
|
||||
|
||||
SourceList::iterator Session::find(std::string namesource)
|
||||
{
|
||||
return std::find_if(sources_.begin(), sources_.end(), Source::hasName(namesource));
|
||||
}
|
||||
|
||||
SourceList::iterator Session::find(Node *node)
|
||||
{
|
||||
return std::find_if(sources_.begin(), sources_.end(), Source::hasNode(node));
|
||||
}
|
||||
|
||||
SourceList::iterator Session::find(float depth_from, float depth_to)
|
||||
{
|
||||
return std::find_if(sources_.begin(), sources_.end(), Source::hasDepth(depth_from, depth_to));
|
||||
}
|
||||
|
||||
SourceList Session::getDepthSortedList() const
|
||||
{
|
||||
return depth_sorted(sources_);
|
||||
}
|
||||
|
||||
uint Session::numSource() const
|
||||
{
|
||||
return sources_.size();
|
||||
}
|
||||
|
||||
SourceIdList Session::getIdList() const
|
||||
{
|
||||
return ids(sources_);
|
||||
}
|
||||
|
||||
std::list<std::string> Session::getNameList(uint64_t exceptid) const
|
||||
{
|
||||
std::list<std::string> namelist;
|
||||
|
||||
for( SourceList::const_iterator it = sources_.cbegin(); it != sources_.cend(); ++it) {
|
||||
if ( (*it)->id() != exceptid )
|
||||
namelist.push_back( (*it)->name() );
|
||||
}
|
||||
|
||||
return namelist;
|
||||
}
|
||||
|
||||
|
||||
bool Session::empty() const
|
||||
{
|
||||
return sources_.empty();
|
||||
}
|
||||
|
||||
SourceList::iterator Session::at(int index)
|
||||
{
|
||||
if (index<0)
|
||||
return sources_.end();
|
||||
|
||||
int i = 0;
|
||||
SourceList::iterator it = sources_.begin();
|
||||
while ( i < index && it != sources_.end() ){
|
||||
i++;
|
||||
++it;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
int Session::index(SourceList::iterator it) const
|
||||
{
|
||||
int index = -1;
|
||||
int count = 0;
|
||||
for(auto i = sources_.begin(); i != sources_.end(); ++i, ++count) {
|
||||
if ( i == it ) {
|
||||
index = count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
void Session::move(int current_index, int target_index)
|
||||
{
|
||||
if ( current_index < 0 || current_index > (int) sources_.size()
|
||||
|| target_index < 0 || target_index > (int) sources_.size()
|
||||
|| target_index == current_index )
|
||||
return;
|
||||
|
||||
SourceList::iterator from = at(current_index);
|
||||
SourceList::iterator to = at(target_index);
|
||||
if ( target_index > current_index )
|
||||
++to;
|
||||
|
||||
Source *s = (*from);
|
||||
sources_.erase(from);
|
||||
sources_.insert(to, s);
|
||||
}
|
||||
|
||||
bool Session::canlink (SourceList sources)
|
||||
{
|
||||
bool canlink = true;
|
||||
|
||||
// verify that all sources given are valid in the sesion
|
||||
validate(sources);
|
||||
|
||||
for (auto it = sources.begin(); it != sources.end(); ++it) {
|
||||
// this source is linked
|
||||
if ( (*it)->mixingGroup() != nullptr ) {
|
||||
// askt its group to detach it
|
||||
canlink = false;
|
||||
}
|
||||
}
|
||||
|
||||
return canlink;
|
||||
}
|
||||
|
||||
void Session::link(SourceList sources, Group *parent)
|
||||
{
|
||||
// we need at least 2 sources to make a group
|
||||
if (sources.size() > 1) {
|
||||
|
||||
unlink(sources);
|
||||
|
||||
// create and add a new mixing group
|
||||
MixingGroup *g = new MixingGroup(sources);
|
||||
mixing_groups_.push_back(g);
|
||||
|
||||
// if provided, attach the group to the parent
|
||||
if (g && parent != nullptr)
|
||||
g->attachTo( parent );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Session::unlink (SourceList sources)
|
||||
{
|
||||
// verify that all sources given are valid in the sesion
|
||||
validate(sources);
|
||||
|
||||
// brute force : detach all given sources
|
||||
for (auto it = sources.begin(); it != sources.end(); ++it) {
|
||||
// this source is linked
|
||||
if ( (*it)->mixingGroup() != nullptr ) {
|
||||
// askt its group to detach it
|
||||
(*it)->mixingGroup()->detach(*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Session::addNote(SessionNote note)
|
||||
{
|
||||
notes_.push_back( note );
|
||||
}
|
||||
|
||||
std::list<SessionNote>::iterator Session::beginNotes ()
|
||||
{
|
||||
return notes_.begin();
|
||||
}
|
||||
|
||||
std::list<SessionNote>::iterator Session::endNotes ()
|
||||
{
|
||||
return notes_.end();
|
||||
}
|
||||
|
||||
std::list<SessionNote>::iterator Session::deleteNote (std::list<SessionNote>::iterator n)
|
||||
{
|
||||
if (n != notes_.end())
|
||||
return notes_.erase(n);
|
||||
|
||||
return notes_.end();
|
||||
}
|
||||
|
||||
std::list<SourceList> Session::getMixingGroups () const
|
||||
{
|
||||
std::list<SourceList> lmg;
|
||||
|
||||
for (auto group_it = mixing_groups_.begin(); group_it!= mixing_groups_.end(); ++group_it)
|
||||
lmg.push_back( (*group_it)->getCopy() );
|
||||
|
||||
return lmg;
|
||||
}
|
||||
|
||||
std::list<MixingGroup *>::iterator Session::deleteMixingGroup (std::list<MixingGroup *>::iterator g)
|
||||
{
|
||||
if (g != mixing_groups_.end()) {
|
||||
delete (*g);
|
||||
return mixing_groups_.erase(g);
|
||||
}
|
||||
return mixing_groups_.end();
|
||||
}
|
||||
|
||||
std::list<MixingGroup *>::iterator Session::beginMixingGroup()
|
||||
{
|
||||
return mixing_groups_.begin();
|
||||
}
|
||||
|
||||
std::list<MixingGroup *>::iterator Session::endMixingGroup()
|
||||
{
|
||||
return mixing_groups_.end();
|
||||
}
|
||||
|
||||
|
||||
size_t Session::numPlayGroups() const
|
||||
{
|
||||
return play_groups_.size();
|
||||
}
|
||||
|
||||
void Session::addPlayGroup(const SourceIdList &ids)
|
||||
{
|
||||
play_groups_.push_back( ids );
|
||||
}
|
||||
|
||||
void Session::addToPlayGroup(size_t i, Source *s)
|
||||
{
|
||||
if (i < play_groups_.size() )
|
||||
{
|
||||
if ( std::find(play_groups_[i].begin(), play_groups_[i].end(), s->id()) == play_groups_[i].end() )
|
||||
play_groups_[i].push_back(s->id());
|
||||
}
|
||||
}
|
||||
|
||||
void Session::removeFromPlayGroup(size_t i, Source *s)
|
||||
{
|
||||
if (i < play_groups_.size() )
|
||||
{
|
||||
if ( std::find(play_groups_[i].begin(), play_groups_[i].end(), s->id()) != play_groups_[i].end() )
|
||||
play_groups_[i].remove( s->id() );
|
||||
}
|
||||
}
|
||||
|
||||
void Session::deletePlayGroup(size_t i)
|
||||
{
|
||||
if (i < play_groups_.size() )
|
||||
play_groups_.erase( play_groups_.begin() + i);
|
||||
}
|
||||
|
||||
SourceList Session::playGroup(size_t i) const
|
||||
{
|
||||
SourceList list;
|
||||
|
||||
if (i < play_groups_.size() )
|
||||
{
|
||||
for (auto sid = play_groups_[i].begin(); sid != play_groups_[i].end(); ++sid){
|
||||
|
||||
SourceList::const_iterator it = std::find_if(sources_.begin(), sources_.end(), Source::hasId( *sid));;
|
||||
if ( it != sources_.end())
|
||||
list.push_back( *it);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void Session::lock()
|
||||
{
|
||||
access_.lock();
|
||||
}
|
||||
|
||||
void Session::unlock()
|
||||
{
|
||||
access_.unlock();
|
||||
}
|
||||
|
||||
void Session::validate (SourceList &sources)
|
||||
{
|
||||
// verify that all sources given are valid in the sesion
|
||||
// and remove the invalid sources
|
||||
for (auto _it = sources.begin(); _it != sources.end(); ) {
|
||||
SourceList::iterator found = std::find(sources_.begin(), sources_.end(), *_it);
|
||||
if ( found == sources_.end() )
|
||||
_it = sources.erase(_it);
|
||||
else
|
||||
_it++;
|
||||
}
|
||||
}
|
||||
|
||||
Session *Session::load(const std::string& filename, uint recursion)
|
||||
{
|
||||
// create session
|
||||
SessionCreator creator(recursion);
|
||||
creator.load(filename);
|
||||
|
||||
// return created session
|
||||
return creator.session();
|
||||
}
|
||||
|
||||
@@ -1,408 +0,0 @@
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
|
||||
#include "SessionSource.h"
|
||||
|
||||
#include "defines.h"
|
||||
#include "Log.h"
|
||||
#include "FrameBuffer.h"
|
||||
#include "ImageShader.h"
|
||||
#include "ImageProcessingShader.h"
|
||||
#include "Resource.h"
|
||||
#include "Decorations.h"
|
||||
#include "SearchVisitor.h"
|
||||
#include "Session.h"
|
||||
#include "SessionCreator.h"
|
||||
#include "Mixer.h"
|
||||
|
||||
|
||||
SessionSource::SessionSource(uint64_t id) : Source(id), failed_(false), timer_(0), paused_(false)
|
||||
{
|
||||
session_ = new Session;
|
||||
}
|
||||
|
||||
SessionSource::~SessionSource()
|
||||
{
|
||||
// delete session
|
||||
if (session_)
|
||||
delete session_;
|
||||
}
|
||||
|
||||
Session *SessionSource::detach()
|
||||
{
|
||||
// remember pointer to give away
|
||||
Session *giveaway = session_;
|
||||
|
||||
// work on a new session
|
||||
session_ = new Session;
|
||||
|
||||
// un-ready
|
||||
ready_ = false;
|
||||
|
||||
// ask to delete me
|
||||
failed_ = true;
|
||||
|
||||
// lost ref to previous session: to be deleted elsewhere...
|
||||
return giveaway;
|
||||
}
|
||||
|
||||
bool SessionSource::failed() const
|
||||
{
|
||||
return failed_;
|
||||
}
|
||||
|
||||
uint SessionSource::texture() const
|
||||
{
|
||||
if (session_ && session_->frame())
|
||||
return session_->frame()->texture();
|
||||
else
|
||||
return Resource::getTextureBlack();
|
||||
}
|
||||
|
||||
void SessionSource::setActive (bool on)
|
||||
{
|
||||
Source::setActive(on);
|
||||
|
||||
// change status of session (recursive change of internal sources)
|
||||
if (session_) {
|
||||
session_->setActive(active_);
|
||||
|
||||
// change visibility of active surface (show preview of session when inactive)
|
||||
if (activesurface_) {
|
||||
if (active_)
|
||||
activesurface_->setTextureIndex(Resource::getTextureTransparent());
|
||||
else
|
||||
activesurface_->setTextureIndex(session_->frame()->texture());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SessionSource::update(float dt)
|
||||
{
|
||||
if (session_ == nullptr)
|
||||
return;
|
||||
|
||||
// update content
|
||||
if (active_ && !paused_) {
|
||||
session_->update(dt);
|
||||
timer_ += guint64(dt * 1000.f) * GST_USECOND;
|
||||
}
|
||||
|
||||
// delete a source which failed
|
||||
if (session_->failedSource() != nullptr) {
|
||||
session_->deleteSource(session_->failedSource());
|
||||
// fail session if all sources failed
|
||||
if ( session_->numSource() < 1)
|
||||
failed_ = true;
|
||||
}
|
||||
|
||||
Source::update(dt);
|
||||
}
|
||||
|
||||
void SessionSource::replay ()
|
||||
{
|
||||
if (session_) {
|
||||
for( SourceList::iterator it = session_->begin(); it != session_->end(); ++it)
|
||||
(*it)->replay();
|
||||
timer_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
SessionFileSource::SessionFileSource(uint64_t id) : SessionSource(id), path_(""), initialized_(false), wait_for_sources_(false)
|
||||
{
|
||||
// specific node for transition view
|
||||
groups_[View::TRANSITION]->visible_ = false;
|
||||
groups_[View::TRANSITION]->scale_ = glm::vec3(0.1f, 0.1f, 1.f);
|
||||
groups_[View::TRANSITION]->translation_ = glm::vec3(-1.f, 0.f, 0.f);
|
||||
|
||||
frames_[View::TRANSITION] = new Switch;
|
||||
Frame *frame = new Frame(Frame::ROUND, Frame::THIN, Frame::DROP);
|
||||
frame->translation_.z = 0.1;
|
||||
frame->color = glm::vec4( COLOR_DEFAULT_SOURCE, 0.95f);
|
||||
frames_[View::TRANSITION]->attach(frame);
|
||||
frame = new Frame(Frame::ROUND, Frame::LARGE, Frame::DROP);
|
||||
frame->translation_.z = 0.01;
|
||||
frame->color = glm::vec4( COLOR_TRANSITION_SOURCE, 1.f);
|
||||
frames_[View::TRANSITION]->attach(frame);
|
||||
groups_[View::TRANSITION]->attach(frames_[View::TRANSITION]);
|
||||
|
||||
overlays_[View::TRANSITION] = new Group;
|
||||
overlays_[View::TRANSITION]->translation_.z = 0.1;
|
||||
overlays_[View::TRANSITION]->visible_ = false;
|
||||
|
||||
Symbol *loader = new Symbol(Symbol::DOTS);
|
||||
loader->scale_ = glm::vec3(2.f, 2.f, 1.f);
|
||||
loader->update_callbacks_.push_back(new InfiniteGlowCallback);
|
||||
overlays_[View::TRANSITION]->attach(loader);
|
||||
Symbol *center = new Symbol(Symbol::CIRCLE_POINT, glm::vec3(0.f, -1.05f, 0.1f));
|
||||
overlays_[View::TRANSITION]->attach(center);
|
||||
groups_[View::TRANSITION]->attach(overlays_[View::TRANSITION]);
|
||||
|
||||
// set symbol
|
||||
symbol_ = new Symbol(Symbol::SESSION, glm::vec3(0.75f, 0.75f, 0.01f));
|
||||
symbol_->scale_.y = 1.5f;
|
||||
|
||||
}
|
||||
|
||||
void SessionFileSource::load(const std::string &p, uint recursion)
|
||||
{
|
||||
path_ = p;
|
||||
|
||||
// delete session
|
||||
if (session_) {
|
||||
delete session_;
|
||||
session_ = nullptr;
|
||||
}
|
||||
|
||||
// init session
|
||||
if ( path_.empty() ) {
|
||||
// empty session
|
||||
session_ = new Session;
|
||||
Log::Warning("Empty Session filename provided.");
|
||||
}
|
||||
else {
|
||||
// launch a thread to load the session file
|
||||
sessionLoader_ = std::async(std::launch::async, Session::load, path_, recursion);
|
||||
Log::Notify("Opening %s", p.c_str());
|
||||
}
|
||||
|
||||
// will be ready after init and one frame rendered
|
||||
initialized_ = false;
|
||||
ready_ = false;
|
||||
}
|
||||
|
||||
void SessionFileSource::init()
|
||||
{
|
||||
// init is first about getting the loaded session
|
||||
if (session_ == nullptr) {
|
||||
// did the loader finish ?
|
||||
if (sessionLoader_.wait_for(std::chrono::milliseconds(4)) == std::future_status::ready) {
|
||||
session_ = sessionLoader_.get();
|
||||
if (session_ == nullptr)
|
||||
failed_ = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
if (wait_for_sources_) {
|
||||
|
||||
// force update of of all sources
|
||||
active_ = true;
|
||||
touch();
|
||||
|
||||
// update to draw framebuffer
|
||||
session_->update(dt_);
|
||||
|
||||
// if all sources are ready, done with initialization!
|
||||
auto unintitializedsource = std::find_if_not(session_->begin(), session_->end(), Source::isInitialized);
|
||||
if (unintitializedsource == session_->end()) {
|
||||
// done init
|
||||
wait_for_sources_ = false;
|
||||
initialized_ = true;
|
||||
Log::Info("Source Session %s loaded %d sources.", path_.c_str(), session_->numSource());
|
||||
}
|
||||
}
|
||||
else if ( !failed_ ) {
|
||||
|
||||
// set resolution
|
||||
session_->setResolution( session_->config(View::RENDERING)->scale_ );
|
||||
|
||||
// update to draw framebuffer
|
||||
session_->update(dt_);
|
||||
|
||||
// get the texture index from framebuffer of session, apply it to the surface
|
||||
texturesurface_->setTextureIndex( session_->frame()->texture() );
|
||||
|
||||
// create Frame buffer matching size of session
|
||||
FrameBuffer *renderbuffer = new FrameBuffer( session_->frame()->resolution() );
|
||||
|
||||
// set the renderbuffer of the source and attach rendering nodes
|
||||
attach(renderbuffer);
|
||||
|
||||
// wait for all sources to init
|
||||
if (session_->numSource() > 0)
|
||||
wait_for_sources_ = true;
|
||||
else {
|
||||
initialized_ = true;
|
||||
Log::Info("New Session created (%d x %d).", renderbuffer->width(), renderbuffer->height());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (initialized_)
|
||||
{
|
||||
// remove the loading icon
|
||||
Node *loader = overlays_[View::TRANSITION]->back();
|
||||
overlays_[View::TRANSITION]->detach(loader);
|
||||
delete loader;
|
||||
// deep update to reorder
|
||||
++View::need_deep_update_;
|
||||
}
|
||||
}
|
||||
|
||||
void SessionFileSource::render()
|
||||
{
|
||||
if ( !initialized_ )
|
||||
init();
|
||||
else {
|
||||
// render the media player into frame buffer
|
||||
renderbuffer_->begin();
|
||||
texturesurface_->draw(glm::identity<glm::mat4>(), renderbuffer_->projection());
|
||||
renderbuffer_->end();
|
||||
ready_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SessionFileSource::accept(Visitor& v)
|
||||
{
|
||||
Source::accept(v);
|
||||
if (!failed())
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
|
||||
SessionGroupSource::SessionGroupSource(uint64_t id) : SessionSource(id), resolution_(glm::vec3(0.f))
|
||||
{
|
||||
// // redo frame for layers view
|
||||
// frames_[View::LAYER]->clear();
|
||||
|
||||
// // Groups in LAYER have an additional border
|
||||
// Group *group = new Group;
|
||||
// Frame *frame = new Frame(Frame::ROUND, Frame::THIN, Frame::PERSPECTIVE);
|
||||
// frame->translation_.z = 0.1;
|
||||
// frame->color = glm::vec4( COLOR_DEFAULT_SOURCE, 0.95f);
|
||||
// group->attach(frame);
|
||||
// Frame *persp = new Frame(Frame::GROUP, Frame::THIN, Frame::NONE);
|
||||
// persp->translation_.z = 0.1;
|
||||
// persp->color = glm::vec4( COLOR_DEFAULT_SOURCE, 0.95f);
|
||||
// group->attach(persp);
|
||||
// frames_[View::LAYER]->attach(group);
|
||||
|
||||
// group = new Group;
|
||||
// frame = new Frame(Frame::ROUND, Frame::LARGE, Frame::PERSPECTIVE);
|
||||
// frame->translation_.z = 0.1;
|
||||
// frame->color = glm::vec4( COLOR_HIGHLIGHT_SOURCE, 1.f);
|
||||
// group->attach(frame);
|
||||
// persp = new Frame(Frame::GROUP, Frame::LARGE, Frame::NONE);
|
||||
// persp->translation_.z = 0.1;
|
||||
// persp->color = glm::vec4( COLOR_HIGHLIGHT_SOURCE, 1.f);
|
||||
// group->attach(persp);
|
||||
// frames_[View::LAYER]->attach(group);
|
||||
|
||||
// set symbol
|
||||
symbol_ = new Symbol(Symbol::GROUP, glm::vec3(0.75f, 0.75f, 0.01f));
|
||||
symbol_->scale_.y = 1.5f;
|
||||
}
|
||||
|
||||
void SessionGroupSource::init()
|
||||
{
|
||||
if ( resolution_.x > 0.f && resolution_.y > 0.f ) {
|
||||
|
||||
session_->setResolution( resolution_ );
|
||||
|
||||
// update to draw framebuffer
|
||||
session_->update( dt_ );
|
||||
|
||||
// get the texture index from framebuffer of session, apply it to the surface
|
||||
texturesurface_->setTextureIndex( session_->frame()->texture() );
|
||||
|
||||
// create Frame buffer matching size of session
|
||||
FrameBuffer *renderbuffer = new FrameBuffer( session_->frame()->resolution() );
|
||||
|
||||
// set the renderbuffer of the source and attach rendering nodes
|
||||
attach(renderbuffer);
|
||||
|
||||
// deep update to reorder
|
||||
++View::need_deep_update_;
|
||||
|
||||
// done init
|
||||
Log::Info("Source Group (%d x %d).", int(renderbuffer->resolution().x), int(renderbuffer->resolution().y) );
|
||||
}
|
||||
}
|
||||
|
||||
bool SessionGroupSource::import(Source *source)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if ( session_ )
|
||||
{
|
||||
SourceList::iterator its = session_->addSource(source);
|
||||
if (its != session_->end())
|
||||
ret = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SessionGroupSource::accept(Visitor& v)
|
||||
{
|
||||
Source::accept(v);
|
||||
if (!failed())
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
RenderSource::RenderSource(uint64_t id) : Source(id), session_(nullptr)
|
||||
{
|
||||
// set symbol
|
||||
symbol_ = new Symbol(Symbol::RENDER, glm::vec3(0.75f, 0.75f, 0.01f));
|
||||
symbol_->scale_.y = 1.5f;
|
||||
}
|
||||
|
||||
bool RenderSource::failed() const
|
||||
{
|
||||
if ( renderbuffer_ != nullptr && session_ != nullptr )
|
||||
return renderbuffer_->resolution() != session_->frame()->resolution();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint RenderSource::texture() const
|
||||
{
|
||||
if (session_ && session_->frame())
|
||||
return session_->frame()->texture();
|
||||
else
|
||||
return Resource::getTextureBlack(); // getTextureTransparent ?
|
||||
}
|
||||
|
||||
void RenderSource::init()
|
||||
{
|
||||
if (session_ && session_->frame() && session_->frame()->texture() != Resource::getTextureBlack()) {
|
||||
|
||||
FrameBuffer *fb = session_->frame();
|
||||
|
||||
// get the texture index from framebuffer of view, apply it to the surface
|
||||
texturesurface_->setTextureIndex( fb->texture() );
|
||||
|
||||
// create Frame buffer matching size of output session
|
||||
FrameBuffer *renderbuffer = new FrameBuffer( fb->resolution() );
|
||||
|
||||
// set the renderbuffer of the source and attach rendering nodes
|
||||
attach(renderbuffer);
|
||||
|
||||
// deep update to reorder
|
||||
++View::need_deep_update_;
|
||||
|
||||
// done init
|
||||
Log::Info("Source Render linked to session (%d x %d).", int(fb->resolution().x), int(fb->resolution().y) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
glm::vec3 RenderSource::resolution() const
|
||||
{
|
||||
if (renderbuffer_ != nullptr)
|
||||
return renderbuffer_->resolution();
|
||||
else if (session_ && session_->frame())
|
||||
return session_->frame()->resolution();
|
||||
else
|
||||
return glm::vec3(0.f);
|
||||
}
|
||||
|
||||
void RenderSource::accept(Visitor& v)
|
||||
{
|
||||
Source::accept(v);
|
||||
// if (!failed())
|
||||
v.visit(*this);
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
#ifndef __UI_MANAGER_H_
|
||||
#define __UI_MANAGER_H_
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
#define NAV_COUNT 68
|
||||
#define NAV_MAX 64
|
||||
#define NAV_NEW 65
|
||||
#define NAV_MENU 66
|
||||
#define NAV_TRANS 67
|
||||
|
||||
#include "SourceList.h"
|
||||
#include "InfoVisitor.h"
|
||||
#include "DialogToolkit.h"
|
||||
#include "SessionParser.h"
|
||||
|
||||
|
||||
struct ImVec2;
|
||||
class MediaPlayer;
|
||||
class FrameBufferImage;
|
||||
class FrameGrabber;
|
||||
|
||||
class SourcePreview {
|
||||
|
||||
Source *source_;
|
||||
std::string label_;
|
||||
bool reset_;
|
||||
|
||||
public:
|
||||
SourcePreview();
|
||||
|
||||
void setSource(Source *s = nullptr, const std::string &label = "");
|
||||
Source *getSource();
|
||||
|
||||
void Render(float width, bool controlbutton = false);
|
||||
bool ready() const;
|
||||
inline bool filled() const { return source_ != nullptr; }
|
||||
};
|
||||
|
||||
class Thumbnail
|
||||
{
|
||||
float aspect_ratio_;
|
||||
uint texture_;
|
||||
|
||||
public:
|
||||
Thumbnail();
|
||||
~Thumbnail();
|
||||
|
||||
void reset();
|
||||
void fill (const FrameBufferImage *image);
|
||||
bool filled();
|
||||
void Render(float width);
|
||||
};
|
||||
|
||||
class Navigator
|
||||
{
|
||||
// geometry left bar & pannel
|
||||
float width_;
|
||||
float height_;
|
||||
float pannel_width_;
|
||||
float padding_width_;
|
||||
|
||||
// behavior pannel
|
||||
bool show_config_;
|
||||
bool pannel_visible_;
|
||||
bool view_pannel_visible;
|
||||
bool selected_button[NAV_COUNT];
|
||||
int pattern_type;
|
||||
std::list<std::string> _selectedFiles;
|
||||
void clearButtonSelection();
|
||||
void applyButtonSelection(int index);
|
||||
|
||||
// side pannels
|
||||
void RenderSourcePannel(Source *s);
|
||||
void RenderMainPannel();
|
||||
void RenderMainPannelVimix();
|
||||
void RenderMainPannelSettings();
|
||||
void RenderTransitionPannel();
|
||||
void RenderNewPannel();
|
||||
void RenderViewPannel(ImVec2 draw_pos, ImVec2 draw_size);
|
||||
|
||||
SourcePreview new_source_preview_;
|
||||
|
||||
public:
|
||||
Navigator();
|
||||
|
||||
bool pannelVisible() { return pannel_visible_; }
|
||||
void hidePannel();
|
||||
void showPannelSource(int index);
|
||||
void togglePannelMenu();
|
||||
void togglePannelNew();
|
||||
void showConfig();
|
||||
|
||||
void Render();
|
||||
};
|
||||
|
||||
class ToolBox
|
||||
{
|
||||
bool show_demo_window;
|
||||
bool show_icons_window;
|
||||
bool show_sandbox;
|
||||
|
||||
public:
|
||||
ToolBox();
|
||||
|
||||
void Render();
|
||||
};
|
||||
|
||||
class HelperToolbox
|
||||
{
|
||||
SessionParser parser_;
|
||||
|
||||
public:
|
||||
HelperToolbox();
|
||||
|
||||
void Render();
|
||||
|
||||
};
|
||||
|
||||
class SourceController
|
||||
{
|
||||
bool focused_;
|
||||
float min_width_;
|
||||
float h_space_;
|
||||
float v_space_;
|
||||
float scrollbar_;
|
||||
float timeline_height_;
|
||||
float mediaplayer_height_;
|
||||
float buttons_width_;
|
||||
float buttons_height_;
|
||||
|
||||
bool play_toggle_request_, replay_request_;
|
||||
std::string active_label_;
|
||||
int active_selection_;
|
||||
InfoVisitor info_;
|
||||
SourceList selection_;
|
||||
|
||||
// re-usable ui parts
|
||||
void DrawButtonBar(ImVec2 bottom, float width);
|
||||
const char *SourcePlayIcon(Source *s);
|
||||
bool SourceButton(Source *s, ImVec2 framesize);
|
||||
|
||||
// Render the sources dynamically selected
|
||||
void RenderSelectedSources();
|
||||
|
||||
// Render a stored selection
|
||||
bool selection_context_menu_;
|
||||
MediaPlayer *selection_mediaplayer_;
|
||||
double selection_target_slower_;
|
||||
double selection_target_faster_;
|
||||
void RenderSelectionContextMenu();
|
||||
void RenderSelection(size_t i);
|
||||
|
||||
// Render a single source
|
||||
void RenderSingleSource(Source *s);
|
||||
|
||||
// Render a single media player
|
||||
MediaPlayer *mediaplayer_active_;
|
||||
bool mediaplayer_edit_fading_;
|
||||
bool mediaplayer_mode_;
|
||||
bool mediaplayer_slider_pressed_;
|
||||
float mediaplayer_timeline_zoom_;
|
||||
void RenderMediaPlayer(MediaPlayer *mp);
|
||||
|
||||
public:
|
||||
SourceController();
|
||||
|
||||
inline void Play() { play_toggle_request_ = true; }
|
||||
inline void Replay() { replay_request_= true; }
|
||||
void Update();
|
||||
|
||||
void resetActiveSelection();
|
||||
void Render();
|
||||
bool Visible() const;
|
||||
inline bool Foccused() const { return focused_; }
|
||||
};
|
||||
|
||||
|
||||
class UserInterface
|
||||
{
|
||||
friend class Navigator;
|
||||
Navigator navigator;
|
||||
ToolBox toolbox;
|
||||
SourceController sourcecontrol;
|
||||
HelperToolbox sessiontoolbox;
|
||||
|
||||
bool ctrl_modifier_active;
|
||||
bool alt_modifier_active;
|
||||
bool shift_modifier_active;
|
||||
bool show_vimix_about;
|
||||
bool show_imgui_about;
|
||||
bool show_gst_about;
|
||||
bool show_opengl_about;
|
||||
int show_view_navigator;
|
||||
int target_view_navigator;
|
||||
unsigned int screenshot_step;
|
||||
|
||||
// frame grabbers
|
||||
FrameGrabber *video_recorder_;
|
||||
|
||||
#if defined(LINUX)
|
||||
FrameGrabber *webcam_emulator_;
|
||||
#endif
|
||||
|
||||
// Dialogs
|
||||
DialogToolkit::OpenSessionDialog *sessionopendialog;
|
||||
DialogToolkit::OpenSessionDialog *sessionimportdialog;
|
||||
DialogToolkit::SaveSessionDialog *sessionsavedialog;
|
||||
|
||||
// Private Constructor
|
||||
UserInterface();
|
||||
UserInterface(UserInterface const& copy) = delete;
|
||||
UserInterface& operator=(UserInterface const& copy) = delete;
|
||||
|
||||
public:
|
||||
|
||||
static UserInterface& manager()
|
||||
{
|
||||
// The only instance
|
||||
static UserInterface _instance;
|
||||
return _instance;
|
||||
}
|
||||
|
||||
// pre-loop initialization
|
||||
bool Init();
|
||||
// loop update start new frame
|
||||
void NewFrame();
|
||||
// loop update rendering
|
||||
void Render();
|
||||
// Post-loop termination
|
||||
void Terminate();
|
||||
|
||||
// status querries
|
||||
inline bool ctrlModifier() const { return ctrl_modifier_active; }
|
||||
inline bool altModifier() const { return alt_modifier_active; }
|
||||
inline bool shiftModifier() const { return shift_modifier_active; }
|
||||
|
||||
void showPannel(int id = 0);
|
||||
void showSourceEditor(Source *s);
|
||||
|
||||
// TODO implement the shader editor
|
||||
std::string currentTextEdit;
|
||||
void fillShaderEditor(const std::string &text);
|
||||
|
||||
void StartScreenshot();
|
||||
inline bool isRecording() const { return video_recorder_ != nullptr; }
|
||||
|
||||
protected:
|
||||
|
||||
void showMenuFile();
|
||||
void showMenuEdit();
|
||||
void selectSaveFilename();
|
||||
void selectOpenFilename();
|
||||
|
||||
void RenderMetrics (bool* p_open, int* p_corner, int *p_mode);
|
||||
void RenderPreview();
|
||||
void RenderHistory();
|
||||
void RenderShaderEditor();
|
||||
int RenderViewNavigator(int* shift);
|
||||
void RenderAbout(bool* p_open);
|
||||
void RenderNotes();
|
||||
|
||||
void handleKeyboard();
|
||||
void handleMouse();
|
||||
void handleScreenshot();
|
||||
};
|
||||
|
||||
#endif /* #define __UI_MANAGER_H_ */
|
||||
|
||||
@@ -41,8 +41,8 @@ foreach(_component ${GStreamerPluginsBad_FIND_COMPONENTS})
|
||||
_find_gst_plugins_bad_component(PLAYER gstplayer.h)
|
||||
elseif (${_component} STREQUAL "webrtc")
|
||||
_find_gst_plugins_bad_component(WEBRTC webrtc.h)
|
||||
elseif (${_component} STREQUAL "mpegts")
|
||||
_find_gst_plugins_bad_component(MPEGTS mpegts.h)
|
||||
elseif (${_component} STREQUAL "srtsrc")
|
||||
_find_gst_plugins_bad_component(SRTSRC mpegts.h)
|
||||
else()
|
||||
message (AUTHOR_WARNING "FindGStreamerPluginBad.cmake: Invalid component \"${_component}\" was specified")
|
||||
endif()
|
||||
|
||||
BIN
docs/images/SRT broadcast 1.png
Normal file
|
After Width: | Height: | Size: 598 KiB |
BIN
docs/images/SRT broadcast 2.png
Normal file
|
After Width: | Height: | Size: 612 KiB |
BIN
docs/images/SRT vimix receive 1.png
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
docs/images/SRT vimix receive 2.png
Normal file
|
After Width: | Height: | Size: 208 KiB |
BIN
docs/images/SRT vimix receive 3.png
Normal file
|
After Width: | Height: | Size: 381 KiB |
|
Before Width: | Height: | Size: 350 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 337 KiB |
|
Before Width: | Height: | Size: 356 KiB After Width: | Height: | Size: 429 KiB |
BIN
docs/images/TouchOSC Mk1 vimix current.jpg
Normal file
|
After Width: | Height: | Size: 138 KiB |
BIN
docs/images/TouchOSC Mk1 vimix mixing.jpg
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
docs/images/TouchOSC Mk1 vimix tuto 0.png
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
docs/images/TouchOSC Mk1 vimix tuto 1.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
docs/images/TouchOSC Mk1 vimix tuto 2.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
docs/images/TouchOSC Mk1 vimix tuto 3.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
docs/images/TouchOSC Mk1 vimix tuto 4.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
docs/images/TouchOSC Mk1 vimix tuto 5.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
docs/images/TouchOSC Mk1 vimix tuto 6.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
docs/images/TouchOSC Mk1 vimix tuto 7.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
docs/images/TouchOSC Mk1 vimix tuto 8.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 592 KiB After Width: | Height: | Size: 436 KiB |
|
Before Width: | Height: | Size: 772 KiB After Width: | Height: | Size: 987 KiB |
|
Before Width: | Height: | Size: 210 KiB After Width: | Height: | Size: 383 KiB |
|
Before Width: | Height: | Size: 846 KiB After Width: | Height: | Size: 745 KiB |
|
Before Width: | Height: | Size: 404 KiB After Width: | Height: | Size: 694 KiB |
|
Before Width: | Height: | Size: 582 KiB After Width: | Height: | Size: 496 KiB |
BIN
docs/images/manual_clonefilter_0.png
Normal file
|
After Width: | Height: | Size: 338 KiB |
BIN
docs/images/manual_clonefilter_1.png
Normal file
|
After Width: | Height: | Size: 498 KiB |
BIN
docs/images/manual_clonefilter_2.png
Normal file
|
After Width: | Height: | Size: 913 KiB |
BIN
docs/images/manual_filter_alpha_chromakey.png
Normal file
|
After Width: | Height: | Size: 113 KiB |
BIN
docs/images/manual_filter_alpha_fill.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
docs/images/manual_filter_alpha_lumakey.png
Normal file
|
After Width: | Height: | Size: 133 KiB |
BIN
docs/images/manual_filter_blur_closing.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
docs/images/manual_filter_blur_fast.png
Normal file
|
After Width: | Height: | Size: 676 KiB |
BIN
docs/images/manual_filter_blur_gaussian.png
Normal file
|
After Width: | Height: | Size: 592 KiB |
BIN
docs/images/manual_filter_blur_opening.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
docs/images/manual_filter_blur_scattered.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
docs/images/manual_filter_chain_0.png
Normal file
|
After Width: | Height: | Size: 426 KiB |
BIN
docs/images/manual_filter_chain_1.png
Normal file
|
After Width: | Height: | Size: 410 KiB |
BIN
docs/images/manual_filter_chain_2.png
Normal file
|
After Width: | Height: | Size: 526 KiB |
BIN
docs/images/manual_filter_chain_3.png
Normal file
|
After Width: | Height: | Size: 443 KiB |
BIN
docs/images/manual_filter_chain_4.png
Normal file
|
After Width: | Height: | Size: 441 KiB |
BIN
docs/images/manual_filter_chain_5.png
Normal file
|
After Width: | Height: | Size: 383 KiB |
BIN
docs/images/manual_filter_delay.png
Normal file
|
After Width: | Height: | Size: 863 KiB |
BIN
docs/images/manual_filter_downsample.png
Normal file
|
After Width: | Height: | Size: 742 KiB |
BIN
docs/images/manual_filter_edge_contour.png
Normal file
|
After Width: | Height: | Size: 330 KiB |
BIN
docs/images/manual_filter_edge_freichen.png
Normal file
|
After Width: | Height: | Size: 586 KiB |
BIN
docs/images/manual_filter_edge_sobel.png
Normal file
|
After Width: | Height: | Size: 565 KiB |
BIN
docs/images/manual_filter_edge_thresholding.png
Normal file
|
After Width: | Height: | Size: 330 KiB |
BIN
docs/images/manual_filter_sharpen_blackhat.png
Normal file
|
After Width: | Height: | Size: 990 KiB |
BIN
docs/images/manual_filter_sharpen_convolution.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
docs/images/manual_filter_sharpen_edge.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
docs/images/manual_filter_sharpen_unsharpmask.png
Normal file
|
After Width: | Height: | Size: 988 KiB |
BIN
docs/images/manual_filter_sharpen_whitehat.png
Normal file
|
After Width: | Height: | Size: 1006 KiB |
BIN
docs/images/manual_filter_smooth_add_grain.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
docs/images/manual_filter_smooth_add_noise.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
docs/images/manual_filter_smooth_bilateral.png
Normal file
|
After Width: | Height: | Size: 710 KiB |
BIN
docs/images/manual_filter_smooth_closing.png
Normal file
|
After Width: | Height: | Size: 792 KiB |
BIN
docs/images/manual_filter_smooth_dilation.png
Normal file
|
After Width: | Height: | Size: 810 KiB |
BIN
docs/images/manual_filter_smooth_erosion.png
Normal file
|
After Width: | Height: | Size: 779 KiB |
BIN
docs/images/manual_filter_smooth_kuwahara.png
Normal file
|
After Width: | Height: | Size: 838 KiB |
BIN
docs/images/manual_filter_smooth_opening.png
Normal file
|
After Width: | Height: | Size: 735 KiB |
BIN
docs/images/manual_filter_smooth_removenoise.png
Normal file
|
After Width: | Height: | Size: 761 KiB |
|
Before Width: | Height: | Size: 344 KiB After Width: | Height: | Size: 857 KiB |
|
Before Width: | Height: | Size: 372 KiB After Width: | Height: | Size: 986 KiB |
BIN
docs/images/manual_filters_2.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
docs/images/manual_general_0.png
Normal file
|
After Width: | Height: | Size: 752 KiB |
|
Before Width: | Height: | Size: 252 KiB After Width: | Height: | Size: 307 KiB |
BIN
docs/images/manual_general_2.png
Normal file
|
After Width: | Height: | Size: 294 KiB |
BIN
docs/images/manual_general_3.png
Normal file
|
After Width: | Height: | Size: 181 KiB |
|
Before Width: | Height: | Size: 311 KiB After Width: | Height: | Size: 219 KiB |
|
Before Width: | Height: | Size: 173 KiB After Width: | Height: | Size: 706 KiB |
|
Before Width: | Height: | Size: 201 KiB After Width: | Height: | Size: 538 KiB |
|
Before Width: | Height: | Size: 257 KiB After Width: | Height: | Size: 616 KiB |
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 641 KiB |
|
Before Width: | Height: | Size: 458 KiB After Width: | Height: | Size: 499 KiB |
|
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 590 KiB |