mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-08 07:28:22 -10:00
Compare commits
821
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1cee23b82 | ||
|
|
3a4d5788a8 | ||
|
|
7e3ca7eb46 | ||
|
|
95620e5de6 | ||
|
|
ae738abcf5 | ||
|
|
e3c815c194 | ||
|
|
daca57de4c | ||
|
|
5650199b04 | ||
|
|
b1e392be2f | ||
|
|
9f0e21f941 | ||
|
|
f740583547 | ||
|
|
fd92813198 | ||
|
|
fc87b129ac | ||
|
|
d5509411de | ||
|
|
a1d7ec7855 | ||
|
|
7d3bc7fc58 | ||
|
|
6ed82d88a9 | ||
|
|
8b49ebf323 | ||
|
|
4bcc85e963 | ||
|
|
235391154e | ||
|
|
1c5bf39e8d | ||
|
|
0b3ff5e698 | ||
|
|
5c0e88dd60 | ||
|
|
1498367e48 | ||
|
|
a8747b7b93 | ||
|
|
dadf58f6b1 | ||
|
|
e9a642673c | ||
|
|
42fbc92d73 | ||
|
|
6141fc7f73 | ||
|
|
c1fd6e7026 | ||
|
|
9114d94e0c | ||
|
|
235234ea66 | ||
|
|
512887482a | ||
|
|
04fd9679bd | ||
|
|
07a2f5a155 | ||
|
|
85874105e3 | ||
|
|
248b70b780 | ||
|
|
f05a50f57b | ||
|
|
a5fc5e322d | ||
|
|
91c4884fd3 | ||
|
|
eaad5a01a2 | ||
|
|
905417b866 | ||
|
|
5061a112db | ||
|
|
b7981c0ce4 | ||
|
|
c0328bb4f5 | ||
|
|
dd2074d91d | ||
|
|
e7a8adb367 | ||
|
|
809ac2300d | ||
|
|
8ea179781a | ||
|
|
77768b3328 | ||
|
|
873162690d | ||
|
|
5c5ce5e66a | ||
|
|
7d54dd9a05 | ||
|
|
eb322476b2 | ||
|
|
aadb22c9f1 | ||
|
|
d4b0dc9cc2 | ||
|
|
b7e703553b | ||
|
|
6b97dcfe60 | ||
|
|
8099bb737b | ||
|
|
91d6d90c9b | ||
|
|
e5e5d14108 | ||
|
|
4fa43ed639 | ||
|
|
1ace8b3abc | ||
|
|
520e532c56 | ||
|
|
9a9e088861 | ||
|
|
39f24bce23 | ||
|
|
3dd9bc8aef | ||
|
|
03aa479efc | ||
|
|
31931777bc | ||
|
|
59d0c561d6 | ||
|
|
ca55f4ea9c | ||
|
|
8c223afcd0 | ||
|
|
4dc8c57d3c | ||
|
|
82f74d570b | ||
|
|
8df4835d51 | ||
|
|
5a5dbd0df2 | ||
|
|
cc876f8bc3 | ||
|
|
45d09fd49b | ||
|
|
6fcdabeb45 | ||
|
|
2e632467f4 | ||
|
|
5c599fa9ee | ||
|
|
6b654ce38a | ||
|
|
2cba774a33 | ||
|
|
32fec00e97 | ||
|
|
66c9baf893 | ||
|
|
1c04be91bb | ||
|
|
6d52cab0a4 | ||
|
|
aa60a639fb | ||
|
|
dfd9fe7610 | ||
|
|
ef0c6b5c78 | ||
|
|
a3f493d8c1 | ||
|
|
b6981875a5 | ||
|
|
7c1147541d | ||
|
|
be9a881cb5 | ||
|
|
8f2aec3b9a | ||
|
|
78abbf89e6 | ||
|
|
4de3451d6c | ||
|
|
8f5f77fa42 | ||
|
|
354246caf4 | ||
|
|
4797b29311 | ||
|
|
2e0253e301 | ||
|
|
0d188fe076 | ||
|
|
c463709399 | ||
|
|
25357ce8a2 | ||
|
|
9953c6a7c4 | ||
|
|
1db36b7ba5 | ||
|
|
77b6648d22 | ||
|
|
09ca5d3dd6 | ||
|
|
942f72d09a | ||
|
|
ee27db60de | ||
|
|
66c0d27270 | ||
|
|
206364a113 | ||
|
|
ac9547d610 | ||
|
|
6534c0a3f6 | ||
|
|
cdeeee688d | ||
|
|
8b5f257d0d | ||
|
|
b2652e0628 | ||
|
|
2eea0a6eb1 | ||
|
|
67c67548fd | ||
|
|
12e45e88fa | ||
|
|
e2129355bf | ||
|
|
857582a980 | ||
|
|
c4dd593dd7 | ||
|
|
30404d4b04 | ||
|
|
c8e3e01ca0 | ||
|
|
16ed58f403 | ||
|
|
5ac2571efa | ||
|
|
7dfa5b5739 | ||
|
|
0178374b98 | ||
|
|
738e4a8153 | ||
|
|
1a4181fb67 | ||
|
|
85c2b3a7f0 | ||
|
|
508b8a176b | ||
|
|
ca313ae17f | ||
|
|
87bb2cbcc7 | ||
|
|
9e673ff7fe | ||
|
|
126b6718db | ||
|
|
b0fc182fab | ||
|
|
158292afba | ||
|
|
e334c1f736 | ||
|
|
f6fa053d94 | ||
|
|
4e24fba7fa | ||
|
|
0ea638f520 | ||
|
|
d75ec93484 | ||
|
|
468a9f280a | ||
|
|
69f4fa60f5 | ||
|
|
95ed536225 | ||
|
|
96d3eebad9 | ||
|
|
3b21b2469f | ||
|
|
cbe1d20fcc | ||
|
|
3d3bbb5dc4 | ||
|
|
4c5b1c679b | ||
|
|
65941e2e84 | ||
|
|
538adf1bf6 | ||
|
|
a6d8b6b5fa | ||
|
|
7fb5f08530 | ||
|
|
ffad093153 | ||
|
|
6a73d8f9d0 | ||
|
|
a0a88128ef | ||
|
|
6fad19c8bc | ||
|
|
119b5d662b | ||
|
|
0b612c2f1a | ||
|
|
8aee62b818 | ||
|
|
1314b8634a | ||
|
|
b557985e90 | ||
|
|
4f43a2edf3 | ||
|
|
6083c89b45 | ||
|
|
eefba9159a | ||
|
|
8fed2fcfe3 | ||
|
|
d149dd18b5 | ||
|
|
a7d624f82d | ||
|
|
aaab3fa03b | ||
|
|
0d51cb0d14 | ||
|
|
1c56d9f497 | ||
|
|
dfc8cd1168 | ||
|
|
ccaa766474 | ||
|
|
741c28d785 | ||
|
|
d73a21a579 | ||
|
|
5b8f4343a3 | ||
|
|
310785a961 | ||
|
|
9029aad64d | ||
|
|
acc018507b | ||
|
|
25698b4534 | ||
|
|
a2f6897c43 | ||
|
|
0777f24f08 | ||
|
|
ed45baa843 | ||
|
|
13ff2ec907 | ||
|
|
e795327a3f | ||
|
|
3c238d61e2 | ||
|
|
2d3e7e54e8 | ||
|
|
8ba05c1c3d | ||
|
|
277962974c | ||
|
|
eab5b8b40a | ||
|
|
842554e237 | ||
|
|
563dc35dea | ||
|
|
3fc6958f88 | ||
|
|
a886a1659c | ||
|
|
99f69c903e | ||
|
|
830fd70f9e | ||
|
|
c4ff34970f | ||
|
|
f216b8bf7f | ||
|
|
0ec2c7fef0 | ||
|
|
1f70fb2bf2 | ||
|
|
304a6a3a0d | ||
|
|
ec6bea233b | ||
|
|
9e495d7f94 | ||
|
|
0344fd6e35 | ||
|
|
b8b7d41786 | ||
|
|
3410822d9d | ||
|
|
aea63832ed | ||
|
|
1ee1886a9a | ||
|
|
c0a8ceb48f | ||
|
|
6bdbbd5c40 | ||
|
|
c66839a9bf | ||
|
|
40ba3162dc | ||
|
|
e81547ae88 | ||
|
|
7577018bd3 | ||
|
|
2181512949 | ||
|
|
dff5e4c21d | ||
|
|
d7ea260607 | ||
|
|
7f7ed74bd6 | ||
|
|
0df6c22216 | ||
|
|
e8ab065bde | ||
|
|
f79b83c60b | ||
|
|
3ab6e43254 | ||
|
|
abbef793fc | ||
|
|
bd85f61971 | ||
|
|
47967cb72a | ||
|
|
7f56037369 | ||
|
|
659828c213 | ||
|
|
2c4884595e | ||
|
|
1621bd169b | ||
|
|
c496a08ab1 | ||
|
|
012e37afb9 | ||
|
|
4fc1183622 | ||
|
|
1eaa67bb5b | ||
|
|
bf74f29634 | ||
|
|
6159e13162 | ||
|
|
1bd09a91d3 | ||
|
|
8cf0e8eb1d | ||
|
|
2dcb75057c | ||
|
|
d95f173f26 | ||
|
|
47b21f6792 | ||
|
|
5ec8896c4a | ||
|
|
560b49158b | ||
|
|
c109dc6aca | ||
|
|
581e5b0242 | ||
|
|
ace3fc619e | ||
|
|
7d418d50ce | ||
|
|
ebd24341d4 | ||
|
|
e8c80dbefc | ||
|
|
68e19a19c5 | ||
|
|
f780d9e22a | ||
|
|
c5117cf55e | ||
|
|
f9704ebd89 | ||
|
|
eb738b3ef7 | ||
|
|
f04debfb36 | ||
|
|
aa8fb89a17 | ||
|
|
44e5f89e41 | ||
|
|
fee0b900de | ||
|
|
6b209223ee | ||
|
|
cb06c73b6a | ||
|
|
7ab8a96903 | ||
|
|
a58376404d | ||
|
|
cfe89f411b | ||
|
|
91c9417d3e | ||
|
|
f37c519b79 | ||
|
|
4dd194f4d6 | ||
|
|
b38367b22f | ||
|
|
72517ff9a2 | ||
|
|
c3e212094e | ||
|
|
5f7721f300 | ||
|
|
9e9378dcb6 | ||
|
|
9b9041513d | ||
|
|
51aac78080 | ||
|
|
3dd21f6cb6 | ||
|
|
a42b27c0b8 | ||
|
|
ca73920b8e | ||
|
|
17374106d1 | ||
|
|
6da5302d34 | ||
|
|
d0a1efacb8 | ||
|
|
8168099eac | ||
|
|
ae943503cb | ||
|
|
4368db845d | ||
|
|
c25163e265 | ||
|
|
4bf9277c10 | ||
|
|
0bcdf19851 | ||
|
|
c4095d1af1 | ||
|
|
5d8f779cff | ||
|
|
6f2592e733 | ||
|
|
528a43952e | ||
|
|
62d1f1978b | ||
|
|
b9b556c7a5 | ||
|
|
16b0db8eb9 | ||
|
|
2c36ea35b4 | ||
|
|
43e540cc4c | ||
|
|
67627bd786 | ||
|
|
ea0c4b7b78 | ||
|
|
29e56bb19a | ||
|
|
57da3e1ef6 | ||
|
|
16ac19946f | ||
|
|
e319b6f941 | ||
|
|
8c88b68327 | ||
|
|
e75ed62692 | ||
|
|
748857ca2f | ||
|
|
bff8231d87 | ||
|
|
29087a3237 | ||
|
|
a228a127e2 | ||
|
|
2692240ed9 | ||
|
|
a47dcc0548 | ||
|
|
9fed19d4fc | ||
|
|
9446e61587 | ||
|
|
1ffd0d3ef8 | ||
|
|
1a94018fb4 | ||
|
|
e8ef727a19 | ||
|
|
dfdfd59367 | ||
|
|
4f7b718167 | ||
|
|
b1046b1c1c | ||
|
|
4663aed194 | ||
|
|
a56432f5fc | ||
|
|
206ce536ec | ||
|
|
d8ccf84452 | ||
|
|
bed9c13233 | ||
|
|
16993b236f | ||
|
|
94f8d547e0 | ||
|
|
b6908d10c4 | ||
|
|
a54aa24a64 | ||
|
|
d083089e1d | ||
|
|
338436d590 | ||
|
|
1d2f778b76 | ||
|
|
aad1bbbb05 | ||
|
|
1e4bede8c5 | ||
|
|
38b331dc92 | ||
|
|
d3965b1f9d | ||
|
|
986099a495 | ||
|
|
07ff605c58 | ||
|
|
a8171c118f | ||
|
|
4b9c7b19aa | ||
|
|
33d2294a49 | ||
|
|
8e558b2095 | ||
|
|
703bfab587 | ||
|
|
344502c436 | ||
|
|
ef06593ed7 | ||
|
|
a22b336bfe | ||
|
|
37b81efea4 | ||
|
|
7c6f99c68b | ||
|
|
f7b7aaead3 | ||
|
|
80dcefa0fc | ||
|
|
27a70d545d | ||
|
|
e4614f01ba | ||
|
|
e2d8502a3e | ||
|
|
dee0cf600f | ||
|
|
6181c0b2f7 | ||
|
|
0f942afaae | ||
|
|
cdde87662a | ||
|
|
4e7f8fa92a | ||
|
|
867ebfed89 | ||
|
|
0ff8a2c159 | ||
|
|
8272304cfa | ||
|
|
0522566790 | ||
|
|
59e7b10b73 | ||
|
|
eb4cfeb256 | ||
|
|
a7b1c00a84 | ||
|
|
e484a84d97 | ||
|
|
bd2bb12ad3 | ||
|
|
c890c0af1f | ||
|
|
9cda43afb2 | ||
|
|
34b4321416 | ||
|
|
3a18a88e30 | ||
|
|
686bf15879 | ||
|
|
22f0ba89a4 | ||
|
|
a3bf47991b | ||
|
|
6af1768147 | ||
|
|
0d9fbd4725 | ||
|
|
c6975fda3c | ||
|
|
52baa1f115 | ||
|
|
d684dcea1b | ||
|
|
fa7f49f1e4 | ||
|
|
cc3dc5f566 | ||
|
|
19c50412ba | ||
|
|
cdd391caa8 | ||
|
|
4860f72678 | ||
|
|
5ba33228cb | ||
|
|
b7f3fdbb98 | ||
|
|
1bffdce6ef | ||
|
|
b130d526ff | ||
|
|
4c1ed2f66a | ||
|
|
d30a73a578 | ||
|
|
fa03117eaf | ||
|
|
2eb109dc49 | ||
|
|
bef57f7559 | ||
|
|
b807c670db | ||
|
|
24b71441c0 | ||
|
|
e47581ffa5 | ||
|
|
c585314ff1 | ||
|
|
97d09ae6ad | ||
|
|
250bf919a7 | ||
|
|
f8dd06ccef | ||
|
|
67416cc8f6 | ||
|
|
7118697a35 | ||
|
|
095c1ebe2b | ||
|
|
8c89c19c7e | ||
|
|
ae3d06c54f | ||
|
|
dbbb8af17b | ||
|
|
a23830359a | ||
|
|
31e572ec03 | ||
|
|
29b5c4bf7a | ||
|
|
3811270a09 | ||
|
|
64fc95ec8c | ||
|
|
0c70672a84 | ||
|
|
67e6bcca9f | ||
|
|
8de33ab56a | ||
|
|
b786b8e230 | ||
|
|
9dc308331b | ||
|
|
628ac86c3e | ||
|
|
dcee8a302e | ||
|
|
c7673f0591 | ||
|
|
000d0c3a0d | ||
|
|
ec19f9be98 | ||
|
|
3005a67573 | ||
|
|
33d4a7efa7 | ||
|
|
6c4e03d9dc | ||
|
|
537ee433de | ||
|
|
2cff45f2c5 | ||
|
|
466e4c159f | ||
|
|
6c7de5120e | ||
|
|
b2b0ba3630 | ||
|
|
14dcff6f5f | ||
|
|
d826537485 | ||
|
|
95c0fad96f | ||
|
|
85b3a30376 | ||
|
|
3c347ae938 | ||
|
|
09b28f2219 | ||
|
|
3c0eec27de | ||
|
|
2fb078f32b | ||
|
|
a19b3030b6 | ||
|
|
5095b1c57a | ||
|
|
ab14742f96 | ||
|
|
492c4ba10a | ||
|
|
999eb8a3f9 | ||
|
|
03aa75c39a | ||
|
|
4f096c26b2 | ||
|
|
3db9fe9383 | ||
|
|
d1e236451f | ||
|
|
6cf7cb0843 | ||
|
|
2084eb01e8 | ||
|
|
7d1365800f | ||
|
|
66ce0acc17 | ||
|
|
c29954d672 | ||
|
|
f2d5246ccb | ||
|
|
b6dcc4f5ef | ||
|
|
bd62ceeeb0 | ||
|
|
f1bf0a411c | ||
|
|
28898ca8e3 | ||
|
|
3acdf3e801 | ||
|
|
f1101bee5d | ||
|
|
e617f0cc41 | ||
|
|
7c9f73fbdc | ||
|
|
c36b47a35c | ||
|
|
8d0b0a76f2 | ||
|
|
36e5afe1be | ||
|
|
20398150d8 | ||
|
|
e085cc20ab | ||
|
|
6b5869853c | ||
|
|
a6411cc1d3 | ||
|
|
bd4427a832 | ||
|
|
6dbd7c34e6 | ||
|
|
7a3029d1e3 | ||
|
|
1bca5f128d | ||
|
|
6bbb6be880 | ||
|
|
c1c2b72ef0 | ||
|
|
1aa85d236e | ||
|
|
1afc5eec13 | ||
|
|
759c9b1148 | ||
|
|
47bb378342 | ||
|
|
0b3dad8dc0 | ||
|
|
13e601a523 | ||
|
|
5de1b684f4 | ||
|
|
c5dcaf88d8 | ||
|
|
55558cc608 | ||
|
|
addc3ef383 | ||
|
|
2c1abf78d1 | ||
|
|
77c950ccd4 | ||
|
|
b160184f5b | ||
|
|
b1b197cc9a | ||
|
|
941b7298cc | ||
|
|
f8d0a9bc7d | ||
|
|
72fa41a284 | ||
|
|
72a797f732 | ||
|
|
2b0ec7161a | ||
|
|
637a9621f1 | ||
|
|
70c5aa88f2 | ||
|
|
1857f9b928 | ||
|
|
90c23c007c | ||
|
|
fb4169ba9f | ||
|
|
4648721674 | ||
|
|
ae4a7319e5 | ||
|
|
3c7f7f9df0 | ||
|
|
e11250f7e8 | ||
|
|
24dec72f41 | ||
|
|
89c36b68c7 | ||
|
|
e66d9d1246 | ||
|
|
789135516e | ||
|
|
91b61d446f | ||
|
|
8e5aafb6e5 | ||
|
|
cb0080bfc1 | ||
|
|
a03180c46e | ||
|
|
a18fdd2112 | ||
|
|
7c81ac1d86 | ||
|
|
bc7414c403 | ||
|
|
a7418d6dde | ||
|
|
f83c2e9cdc | ||
|
|
965a327630 | ||
|
|
1448256140 | ||
|
|
7de592bf9b | ||
|
|
783852a115 | ||
|
|
c9bb3f4caa | ||
|
|
84fd7123a5 | ||
|
|
3e1b020bf5 | ||
|
|
60d6aaccec | ||
|
|
18a957e8bf | ||
|
|
ad7bddfd41 | ||
|
|
a90d859891 | ||
|
|
ff9d06cfe1 | ||
|
|
a158029120 | ||
|
|
96e8d5b66e | ||
|
|
85fd0b0498 | ||
|
|
5d3e6789e6 | ||
|
|
b8e5a395dd | ||
|
|
1a1c3df379 | ||
|
|
0dc0d67c76 | ||
|
|
0edb360fc2 | ||
|
|
1743e5f5d4 | ||
|
|
ed3f07284d | ||
|
|
c1b222ffd5 | ||
|
|
3503b9214b | ||
|
|
aff8cc8095 | ||
|
|
884581a836 | ||
|
|
841872aae0 | ||
|
|
98af9dd903 | ||
|
|
ea37b3cb17 | ||
|
|
9b1e9600ce | ||
|
|
3035a868ab | ||
|
|
a88c50b6f2 | ||
|
|
ac24ca9fd3 | ||
|
|
07a806e01f | ||
|
|
ccd33eb50f | ||
|
|
1805c77f7c | ||
|
|
d085951767 | ||
|
|
c3f19631ce | ||
|
|
ca9eff3bc5 | ||
|
|
0a19616bc9 | ||
|
|
481ebec316 | ||
|
|
99d8b19ee8 | ||
|
|
5adc3216df | ||
|
|
55f39e088d | ||
|
|
71b572b7f4 | ||
|
|
c1908669d9 | ||
|
|
255e9aa21e | ||
|
|
a9d25fd18d | ||
|
|
72c5d17739 | ||
|
|
550f459747 | ||
|
|
ecf10d8227 | ||
|
|
8ab448dd70 | ||
|
|
ea53cff723 | ||
|
|
6da72bb1a0 | ||
|
|
c7fbefeb58 | ||
|
|
98e316bca6 | ||
|
|
78a1e5baab | ||
|
|
080ce8419a | ||
|
|
06e9248079 | ||
|
|
407888641c | ||
|
|
e04c60ed6f | ||
|
|
ac554f24c2 | ||
|
|
56670b2ed7 | ||
|
|
81d9ca8e4a | ||
|
|
7e26955853 | ||
|
|
c085eef9a0 | ||
|
|
bf2ca61e5a | ||
|
|
8061125d84 | ||
|
|
72013d4c31 | ||
|
|
93b1c0df17 | ||
|
|
41f889e55d | ||
|
|
416d45d060 | ||
|
|
18be4e854c | ||
|
|
7d92178777 | ||
|
|
ac69bd5712 | ||
|
|
d6b6ea715a | ||
|
|
ffe647be79 | ||
|
|
fff2e6d1fc | ||
|
|
23e46ae7c4 | ||
|
|
74d270270e | ||
|
|
df69d195fe | ||
|
|
8c59b06b59 | ||
|
|
6c871ac339 | ||
|
|
24afebb808 | ||
|
|
e59d31d66e | ||
|
|
19c01563ca | ||
|
|
80f1f50582 | ||
|
|
a057e111ba | ||
|
|
b45998b58d | ||
|
|
6aac8c7bde | ||
|
|
a2da77e14c | ||
|
|
7ca6b679b7 | ||
|
|
3d6bbb0b9b | ||
|
|
a912956601 | ||
|
|
a769a7ef87 | ||
|
|
991e09006e | ||
|
|
3e6c3f6f62 | ||
|
|
2ee3dd70ea | ||
|
|
c259dfb9cd | ||
|
|
2947c7972d | ||
|
|
eda8c65b58 | ||
|
|
186f8ff5b9 | ||
|
|
c86c4c3822 | ||
|
|
bea9d56feb | ||
|
|
dc6d2be515 | ||
|
|
298ec5519c | ||
|
|
7025e3443e | ||
|
|
88b87b86fc | ||
|
|
b34ca3cfed | ||
|
|
1b2eece53a | ||
|
|
472a3f09f7 | ||
|
|
e8313fa577 | ||
|
|
d6c4ff374c | ||
|
|
b14c12befa | ||
|
|
2d7606740a | ||
|
|
5a8454e956 | ||
|
|
1712a42807 | ||
|
|
f6b1eaa846 | ||
|
|
d20ff9b367 | ||
|
|
a3ab30cd96 | ||
|
|
f811d7591f | ||
|
|
7e9f0c8e6f | ||
|
|
48f76f7cd1 | ||
|
|
255814dd4f | ||
|
|
e076aa6990 | ||
|
|
16d48fbf1b | ||
|
|
937de9355b | ||
|
|
947b25e2cc | ||
|
|
e6f92f316a | ||
|
|
f65de9bb43 | ||
|
|
956e908b6e | ||
|
|
0442c1c6ac | ||
|
|
1a3b14316d | ||
|
|
350e8a2cf5 | ||
|
|
4cd9993640 | ||
|
|
41137fedbd | ||
|
|
52901e7351 | ||
|
|
eb165123ac | ||
|
|
526076f8ff | ||
|
|
c30ce55942 | ||
|
|
8236a7d7f8 | ||
|
|
70e63dcf6c | ||
|
|
557ff6be1d | ||
|
|
95896e6ec7 | ||
|
|
9447d74da8 | ||
|
|
e6acd9a313 | ||
|
|
c1cb978687 | ||
|
|
043e6a79a8 | ||
|
|
97416feb2a | ||
|
|
51698af1b0 | ||
|
|
9d3659cf88 | ||
|
|
3eddce06dd | ||
|
|
9c75a2178c | ||
|
|
d26253c5f8 | ||
|
|
aac3bbde8e | ||
|
|
6b5cac174a | ||
|
|
1f39915a5e | ||
|
|
d60306288b | ||
|
|
8f3ef45fc9 | ||
|
|
762997fc7d | ||
|
|
305e71e290 | ||
|
|
befe668c8b | ||
|
|
25e87fb8a0 | ||
|
|
9a11f232f6 | ||
|
|
49a37ccf91 | ||
|
|
ee8d03b4cf | ||
|
|
16b80faaaf | ||
|
|
0f7810fdb4 | ||
|
|
f85dfbb048 | ||
|
|
96ed407dd7 | ||
|
|
cd7cf82430 | ||
|
|
cff16f7b0b | ||
|
|
1a08bfa3e7 | ||
|
|
dbc52e2aa7 | ||
|
|
b1725bae76 | ||
|
|
4b10c61a5f | ||
|
|
c18220048d | ||
|
|
08b8c1d768 | ||
|
|
0f6a61894a | ||
|
|
3345aee144 | ||
|
|
207d3e6eba | ||
|
|
8be0a19a98 | ||
|
|
b517a47f81 | ||
|
|
4afd5d9d1c | ||
|
|
7eaff0e63f | ||
|
|
755df8c38b | ||
|
|
8357964e53 | ||
|
|
e10b431735 | ||
|
|
be5d7cc2cd | ||
|
|
2e016ccffb | ||
|
|
4305649a64 | ||
|
|
27c971a92b | ||
|
|
1e1c8c7cb8 | ||
|
|
94bb727ebe | ||
|
|
43a46641eb | ||
|
|
884259fc14 | ||
|
|
0b6a3115a7 | ||
|
|
ec6a786654 | ||
|
|
47bd990bca | ||
|
|
a3744d8818 | ||
|
|
5dc0c0d98d | ||
|
|
ad6551cf38 | ||
|
|
1cdc0840be | ||
|
|
e7f619d65e | ||
|
|
ec3313ffea | ||
|
|
f2c099a7a9 | ||
|
|
3ddc93fd9d | ||
|
|
6b32bdb732 | ||
|
|
cabc6f6412 | ||
|
|
164e20ef96 | ||
|
|
d18b97014e | ||
|
|
abdf713944 | ||
|
|
e6c6cc3864 | ||
|
|
24a3369e9d | ||
|
|
cb94183629 | ||
|
|
7ecfa209ca | ||
|
|
22105d1e3e | ||
|
|
5ee1b6d243 | ||
|
|
3577c005e2 | ||
|
|
b2891bfe21 | ||
|
|
82aa027d0d | ||
|
|
588659a367 | ||
|
|
423d14c24a | ||
|
|
2f44c3242f | ||
|
|
1167a4edcb | ||
|
|
862010b4e0 | ||
|
|
95dff1dacf | ||
|
|
bc38232f3a | ||
|
|
a29708ea89 | ||
|
|
baeb9ac78b | ||
|
|
211b9c9d71 | ||
|
|
4c05a103fa | ||
|
|
540e52ffbc | ||
|
|
902d40f3df | ||
|
|
e82fc4ac2f | ||
|
|
71673241fd | ||
|
|
0fc55babaf | ||
|
|
f93cb23d43 | ||
|
|
0415b69407 | ||
|
|
4ce085cd49 | ||
|
|
3edb4575d3 | ||
|
|
26af516704 | ||
|
|
668dccf925 | ||
|
|
acdf88fdeb | ||
|
|
577c49eedb | ||
|
|
9cd3d80b81 | ||
|
|
e8316662b8 | ||
|
|
6a82c1eee7 | ||
|
|
a309cb51b1 | ||
|
|
af60cefbd5 | ||
|
|
8e2b1f354a | ||
|
|
1d3ee16198 | ||
|
|
cb7f2a5dfd | ||
|
|
18a621764c | ||
|
|
22c7e31c8a | ||
|
|
e4775948d7 | ||
|
|
00ff9e8751 | ||
|
|
ba26f6e221 | ||
|
|
ed01e155f6 | ||
|
|
cd110bb2c1 | ||
|
|
2c30dbcda6 | ||
|
|
160d49fa82 | ||
|
|
431ad4144f | ||
|
|
2555d11427 | ||
|
|
e1882fc314 | ||
|
|
2804b5d9d3 | ||
|
|
2bbe201130 | ||
|
|
bbbc08fa87 | ||
|
|
a8def1e1b3 | ||
|
|
987a442dd6 | ||
|
|
09d39da97c | ||
|
|
0a2e6f581b | ||
|
|
98cde8f21f | ||
|
|
9d22d75ba8 | ||
|
|
df095e6bdf | ||
|
|
243f7f7ce8 | ||
|
|
32b6696162 | ||
|
|
ac8befa4e6 | ||
|
|
8958de35bc | ||
|
|
c03dbea7ea | ||
|
|
9107da8c9c | ||
|
|
1abb1833aa | ||
|
|
a2e70b8c5c | ||
|
|
1029de8073 | ||
|
|
3abcb24815 | ||
|
|
13d68bdfc6 | ||
|
|
de0bbaa4f6 | ||
|
|
ce569d400e | ||
|
|
adb94dc4d2 | ||
|
|
916082d910 | ||
|
|
e52f23d45d | ||
|
|
2dfc086c94 | ||
|
|
0a53aff061 | ||
|
|
2dab381e88 | ||
|
|
83514409f3 | ||
|
|
340f39f44d | ||
|
|
418a944127 | ||
|
|
22180c0b45 | ||
|
|
2949ca2628 | ||
|
|
dbf952e031 | ||
|
|
5b1285e608 | ||
|
|
accbc1d25d | ||
|
|
8acff8e02c | ||
|
|
89d13ab774 | ||
|
|
c008dfc303 | ||
|
|
e003d08249 | ||
|
|
c8baf8b344 | ||
|
|
b5f75af66e | ||
|
|
25415342ea |
No files matched your search
+4
-30
@@ -1,33 +1,3 @@
|
||||
*.o
|
||||
*.a
|
||||
.dirstamp
|
||||
libtool
|
||||
ltmain.sh
|
||||
m4/libtool.m4
|
||||
m4/ltoptions.m4
|
||||
m4/ltsugar.m4
|
||||
m4/ltversion.m4
|
||||
m4/lt~obsolete.m4
|
||||
compile
|
||||
.deps/
|
||||
code/.deps/
|
||||
Makefile
|
||||
Makefile.in
|
||||
aclocal.m4
|
||||
autom4te.cache/
|
||||
config.*
|
||||
code/fs2_open_*
|
||||
code/wxFRED2_*
|
||||
configure
|
||||
depcomp
|
||||
install-sh
|
||||
missing
|
||||
.svn
|
||||
*.layout
|
||||
projects/Xcode/Frameworks/
|
||||
|
||||
#========
|
||||
|
||||
#OS junk files
|
||||
[Tt]humbs.db
|
||||
*.DS_Store
|
||||
@@ -286,3 +256,7 @@ CMakeSettings.json
|
||||
|
||||
# Weird files created by Linux
|
||||
.fuse_hidden*
|
||||
|
||||
# This gets ignored by one of the rules above for some reason...
|
||||
!/code/scripting/lua/LuaConvert.h
|
||||
!/code/debugconsole/
|
||||
@@ -1,6 +1,3 @@
|
||||
[submodule "cmake/external/cotire"]
|
||||
path = cmake/external/cotire
|
||||
url = https://github.com/sakra/cotire.git
|
||||
[submodule "cmake/external/rpavlik-cmake-modules"]
|
||||
path = cmake/external/rpavlik-cmake-modules
|
||||
url = https://github.com/asarium/cmake-modules.git
|
||||
+52
-16
@@ -4,6 +4,7 @@ dist: trusty
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/cmake
|
||||
- $HOME/.ccache
|
||||
addons:
|
||||
apt:
|
||||
packages: &global_deps
|
||||
@@ -16,6 +17,7 @@ addons:
|
||||
- libjansson-dev
|
||||
- libsdl2-dev
|
||||
- valgrind
|
||||
- qt57base
|
||||
coverity_scan:
|
||||
project:
|
||||
name: "scp-fs2open/fs2open.github.com"
|
||||
@@ -23,6 +25,19 @@ addons:
|
||||
build_command: "ci/travis/script.sh"
|
||||
# must match TRAVIS_BRANCH check below
|
||||
branch_pattern: coverity_scan
|
||||
env:
|
||||
global:
|
||||
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
||||
# via the "travis encrypt" command using the project repo's public key
|
||||
# also note that fork's don't get access to secure keys
|
||||
# you have to test using a branch in the main repo
|
||||
- secure: "C3wF967ZeliwwP1vC12EMIBOaC568n26Z/cPnwzn317ve59DWH3YSfPZVgvt08GzmaXITGGsJvPB+qxfGoKvQzYE4O1B73q81RpUe5pB3IhF+ThQ91VfQZIRJR5xEGJaLINUlHTTZGX5jxlkVO9wAcauVj/s3b8sQ3dvUZauPvk="
|
||||
# These declarations set INDIEGAMES_USER and INDIEGAMES_PASSWORD for uploading nightly builds to the indiegames server
|
||||
- secure: "lUuRu8XIi8MKNf1ebOfyC4iVEpPyASojJIDWXRo2bNqEF4ILAljXdkVeXeN6H5yKCHD52cxCNTtyePIG/o3p3QEuGSVud9bDS3D7LaQicLETGGrsw3l07U49gF2OKHXGopg0MzBbZUU9QmhoFTAe8t6PV9NDuEayYCheqvTqjZg="
|
||||
- secure: "E/DHLYcPK/hBUqxBYYlUqp5sjgvIqY5A0qRHyQ4bmjsB+WB/Xx71bGCSiF7hk6lTxar1ZI1i5RAXXh9AIvNVaYAIdEOlCP+q3gvwCYuc5naZEKmkxlUCf0oEL4O9Fye93A/3VkFHuBdC1TGPYYf69m4W5r4367wWO9dPRIdWpJA="
|
||||
# These declarations set FS2DOWNLOADS_USER and FS2DOWNLOADS_PASSWORD
|
||||
- secure: "h6GbMX2jLsW1bS9fCM9fthOSJ1nfQQIRWi6EUH2p8u4NFLD5Bfi/C8JhQPKHUzyo3wvKkSHl+PsW7bgr1oXLusRwAur/dtv2N3imqvtbfIz305b00hZlsvbcYmQmUzQFSAdOGShK57JJ5aKQKccaUBP25KqB7Q+JaqBRdaMBS54="
|
||||
- secure: "fUDAPAUUaaYZy8I4zU3SJ0JcK7aw4eb6eTdaEr7LyNyTMSfPgwY6P1MrbSQC0lJNQIUQ5ffjdxE5g7D7OYpoJF9L14ni4UY8t2QASt7kmpgcYihmjeuYy8FQXgmFG8CTxiWBPKTybZJ7voQcvIvRaNG7b2wM9PMfc5lRIondB6c="
|
||||
deploy:
|
||||
- provider: releases
|
||||
api_key:
|
||||
@@ -34,21 +49,19 @@ deploy:
|
||||
condition: '"$RELEASE_BUILD" == true'
|
||||
tags: true
|
||||
|
||||
- provider: bintray
|
||||
user: sirknightly
|
||||
key:
|
||||
secure: "N/6QerkSgkt3elhJMeOyyHHjFri1Bi0GF0msucVCuTXVxMmq/E2XVldHT1TVfD7u6EUlkH5Tr+Aer5fXrrDfk2RZH1RXgQk1FYWUC+JY9dpD6AOtlffRfVHwhSMsZbuSt0sbDmHjnrFiGnxOFojBHpA/iTdZ4fvUl6Mg47hxaVI="
|
||||
file: ci/bintray.json
|
||||
- provider: script
|
||||
script: bash ci/travis/ftp_deploy.sh
|
||||
on:
|
||||
condition: '"$NIGHTLY_BUILD" == true'
|
||||
tags: true
|
||||
env:
|
||||
global:
|
||||
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
||||
# via the "travis encrypt" command using the project repo's public key
|
||||
# also note that fork's don't get access to secure keys
|
||||
# you have to test using a branch in the main repo
|
||||
- secure: "C3wF967ZeliwwP1vC12EMIBOaC568n26Z/cPnwzn317ve59DWH3YSfPZVgvt08GzmaXITGGsJvPB+qxfGoKvQzYE4O1B73q81RpUe5pB3IhF+ThQ91VfQZIRJR5xEGJaLINUlHTTZGX5jxlkVO9wAcauVj/s3b8sQ3dvUZauPvk="
|
||||
|
||||
- provider: script
|
||||
skip_cleanup: true
|
||||
script: bash ci/travis/ftp_deploy.sh
|
||||
on:
|
||||
condition: $TEST_BUILD = true
|
||||
all_branches: true
|
||||
|
||||
matrix:
|
||||
include:
|
||||
# note that gcc Debug MUST be 1st for Coverity
|
||||
@@ -58,8 +71,19 @@ matrix:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'ppa:beineri/opt-qt571-trusty'
|
||||
packages:
|
||||
- *global_deps
|
||||
- g++-5
|
||||
- os: linux
|
||||
env: CONFIGURATION="Debug" CC=gcc-7 CXX=g++-7
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'ppa:beineri/opt-qt571-trusty'
|
||||
packages:
|
||||
- g++-7
|
||||
- *global_deps
|
||||
- os: linux
|
||||
env: CONFIGURATION="Debug" CC=clang-3.6 CXX=clang++-3.6
|
||||
@@ -67,10 +91,11 @@ matrix:
|
||||
apt:
|
||||
sources:
|
||||
- llvm-toolchain-precise
|
||||
- sourceline: 'ppa:beineri/opt-qt571-trusty'
|
||||
packages:
|
||||
- *global_deps
|
||||
- clang-3.6
|
||||
- libc++-dev
|
||||
- *global_deps
|
||||
- os: osx
|
||||
env: CONFIGURATION="Debug"
|
||||
|
||||
@@ -80,8 +105,19 @@ matrix:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'ppa:beineri/opt-qt571-trusty'
|
||||
packages:
|
||||
- *global_deps
|
||||
- g++-5
|
||||
- os: linux
|
||||
env: CONFIGURATION="Release" CC=gcc-7 CXX=g++-7
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'ppa:beineri/opt-qt571-trusty'
|
||||
packages:
|
||||
- g++-7
|
||||
- *global_deps
|
||||
- os: linux
|
||||
env: CONFIGURATION="Release" CC=clang-3.6 CXX=clang++-3.6
|
||||
@@ -89,10 +125,11 @@ matrix:
|
||||
apt:
|
||||
sources:
|
||||
- llvm-toolchain-precise
|
||||
- sourceline: 'ppa:beineri/opt-qt571-trusty'
|
||||
packages:
|
||||
- *global_deps
|
||||
- clang-3.6
|
||||
- libc++-dev
|
||||
- *global_deps
|
||||
- os: osx
|
||||
env: CONFIGURATION="Release"
|
||||
before_install:
|
||||
@@ -108,8 +145,7 @@ install:
|
||||
- "./ci/travis/install.sh"
|
||||
- export PATH=$HOME/cmake/bin:$PATH
|
||||
before_script:
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then QT_ENV_SCRIPT=$(find /opt -name 'qt*-env.sh'); source $QT_ENV_SCRIPT; fi
|
||||
- "./ci/travis/before_script.sh"
|
||||
script:
|
||||
- if [ "$BUILD_DEPLOYMENT" = true ] ; then ./ci/travis/release.sh ; else ./ci/travis/script.sh ; fi
|
||||
before_deploy:
|
||||
- "./ci/travis/before_deploy.sh"
|
||||
+14
-7
@@ -40,6 +40,14 @@ set(CMAKE_CONFIGURATION_TYPES Debug Release FastDebug CACHE STRING "Available co
|
||||
|
||||
PROJECT(FS2_Open)
|
||||
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
# Currently only GCC support ccache properly without generating additional warnings
|
||||
if(CCACHE_PROGRAM AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
message(STATUS "Using ccache for compilation: ${CCACHE_PROGRAM}")
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCACHE_PROGRAM}")
|
||||
endif()
|
||||
|
||||
IF(CMAKE_CROSSCOMPILING)
|
||||
SET(IMPORT_EXE_DIR "IMPORT_EXE_DIR-NOTFOUND" CACHE FILEPATH "Required for a cross compiling build, point to binary directory of a cmake build for the real OS")
|
||||
INCLUDE(${IMPORT_EXE_DIR}/ImportExecutables.cmake)
|
||||
@@ -53,7 +61,7 @@ endif()
|
||||
LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/finder")
|
||||
LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/external/rpavlik-cmake-modules")
|
||||
LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/external/cotire/CMake")
|
||||
LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/external/cotire")
|
||||
|
||||
IF(RESET_INSTALL_PREFIX)
|
||||
IF(NOT $ENV{FS2PATH} STREQUAL "")
|
||||
@@ -85,7 +93,7 @@ OPTION(FSO_BUILD_TESTS "Build unit tests" OFF)
|
||||
|
||||
OPTION(FSO_DEVELOPMENT_MODE "Generate binaries in development mode, only use if you know what you're doing!" OFF)
|
||||
|
||||
OPTION(FSO_BUILD_WXFRED2 "Build wxFRED2 binary" OFF)
|
||||
OPTION(FSO_BUILD_QTFRED "Build qtFRED2 binary" OFF)
|
||||
|
||||
IF(WIN32 OR APPLE)
|
||||
# On windows and mac the default should be to always build the included libraries
|
||||
@@ -147,9 +155,9 @@ IF(FSO_BUILD_FRED2)
|
||||
ADD_SUBDIRECTORY(fred2)
|
||||
ENDIF(FSO_BUILD_FRED2)
|
||||
|
||||
IF(FSO_BUILD_WXFRED2)
|
||||
ADD_SUBDIRECTORY(wxfred2)
|
||||
ENDIF(FSO_BUILD_WXFRED2)
|
||||
IF(FSO_BUILD_QTFRED)
|
||||
ADD_SUBDIRECTORY(qtfred)
|
||||
ENDIF()
|
||||
|
||||
if(FSO_BUILD_TESTS)
|
||||
add_subdirectory(test)
|
||||
@@ -176,12 +184,11 @@ include(doxygen)
|
||||
|
||||
# Print used options to log
|
||||
IF (WIN32)
|
||||
message(STATUS "Building FRED2: ${FSO_BUILD_FRED2}")
|
||||
message(STATUS "Using text to speech: ${FSO_USE_SPEECH}")
|
||||
message(STATUS "Using voice recogition: ${FSO_USE_VOICEREC}")
|
||||
elseif(UNIX)
|
||||
message(STATUS "Building AppImage: ${FSO_BUILD_APPIMAGE}")
|
||||
ENDIF()
|
||||
message(STATUS "Building FSO tools: ${FSO_BUILD_TOOLS}")
|
||||
message(STATUS "Building wxFRED: ${FSO_BUILD_WXFRED2}")
|
||||
message(STATUS "Building qtFRED: ${FSO_BUILD_QTFRED}")
|
||||
message(STATUS "Fatal warnings: ${FSO_FATAL_WARNINGS}")
|
||||
+34
-10
@@ -8,9 +8,11 @@ environment:
|
||||
- BuildID: 0
|
||||
CMAKE_GENERATOR: Visual Studio 12 2013
|
||||
PlatformToolset: v120_xp
|
||||
QT_DIR: C:\Qt\5.7\msvc2013
|
||||
- BuildID: 1
|
||||
CMAKE_GENERATOR: Visual Studio 14 2015 Win64
|
||||
PlatformToolset: v140_xp
|
||||
QT_DIR: C:\Qt\5.7\msvc2015_64
|
||||
before_build:
|
||||
- git submodule update --init --recursive
|
||||
build_script:
|
||||
@@ -25,15 +27,37 @@ deploy:
|
||||
on:
|
||||
ReleaseBuild: true # deploy on release push only
|
||||
|
||||
- provider: BinTray
|
||||
username: sirknightly
|
||||
api_key:
|
||||
secure: 242iJ1rSVahaH+SskiwK2YcIVTO+yEOn5lRAvrBESHVfw6cqrSWfUROPp0Y6bxxX
|
||||
subject: scp-fs2open
|
||||
repo: FSO
|
||||
package: nightly
|
||||
version: $(VersionName)
|
||||
publish: true
|
||||
override: true
|
||||
- provider: FTP
|
||||
protocol: sftp
|
||||
host: scp.indiegames.us
|
||||
username:
|
||||
secure: SFivQZfatqWHXK1ijYK3GQ==
|
||||
password:
|
||||
secure: q6Zk/412o1ri+NEya1dl1Q==
|
||||
folder: public_html/builds/nightly/$(VersionName)
|
||||
on:
|
||||
NightlyBuild: true # deploy on nightly push only
|
||||
|
||||
- provider: FTP
|
||||
protocol: ftp
|
||||
host: swc.fs2downloads.com
|
||||
username:
|
||||
secure: EXg2ADoKzMduZF90ip2Q0g==
|
||||
password:
|
||||
secure: x0LL0uY9yBAQ7JTARhQ6AA==
|
||||
folder: swc.fs2downloads.com/builds/nightly/$(VersionName)
|
||||
beta: true
|
||||
on:
|
||||
NightlyBuild: true # deploy on nightly push only
|
||||
|
||||
- provider: FTP
|
||||
protocol: ftp
|
||||
host: swc.fs2downloads.com
|
||||
username:
|
||||
secure: EXg2ADoKzMduZF90ip2Q0g==
|
||||
password:
|
||||
secure: x0LL0uY9yBAQ7JTARhQ6AA==
|
||||
folder: swc.fs2downloads.com/builds/test/$(VersionName)
|
||||
beta: true
|
||||
on:
|
||||
TestBuild: true # deploy on test build push only
|
||||
+42
-5
@@ -4,6 +4,8 @@ class BuildConfig {
|
||||
[string]$PackageType
|
||||
[string]$Toolset
|
||||
[string]$SimdType
|
||||
[string]$QtDir
|
||||
[Bool]$SourcePackage
|
||||
}
|
||||
|
||||
$NightlyConfigurations = @(
|
||||
@@ -12,12 +14,16 @@ $NightlyConfigurations = @(
|
||||
PackageType="Win64";
|
||||
Toolset="v140_xp";
|
||||
SimdType="SSE2";
|
||||
QtDir="C:\Qt\5.7\msvc2015_64";
|
||||
SourcePackage=$false;
|
||||
},
|
||||
[BuildConfig]@{
|
||||
Generator="Visual Studio 14 2015";
|
||||
PackageType="Win32";
|
||||
Toolset="v140_xp";
|
||||
SimdType="SSE2";
|
||||
QtDir="C:\Qt\5.7\msvc2015";
|
||||
SourcePackage=$false;
|
||||
}
|
||||
)
|
||||
$ReleaseConfigurations = @(
|
||||
@@ -26,24 +32,32 @@ $ReleaseConfigurations = @(
|
||||
PackageType="Win32";
|
||||
Toolset="v140_xp";
|
||||
SimdType="SSE2";
|
||||
QtDir="C:\Qt\5.7\msvc2015";
|
||||
SourcePackage=$true;
|
||||
}
|
||||
[BuildConfig]@{
|
||||
Generator="Visual Studio 14 2015";
|
||||
PackageType="Win32-AVX";
|
||||
Toolset="v140_xp";
|
||||
SimdType="AVX";
|
||||
QtDir="C:\Qt\5.7\msvc2015";
|
||||
SourcePackage=$false;
|
||||
}
|
||||
[BuildConfig]@{
|
||||
Generator="Visual Studio 14 2015 Win64";
|
||||
PackageType="Win64";
|
||||
Toolset="v140_xp";
|
||||
SimdType="SSE2";
|
||||
QtDir="C:\Qt\5.7\msvc2015_64";
|
||||
SourcePackage=$false;
|
||||
}
|
||||
[BuildConfig]@{
|
||||
Generator="Visual Studio 14 2015 Win64";
|
||||
PackageType="Win64-AVX";
|
||||
Toolset="v140_xp";
|
||||
SimdType="AVX";
|
||||
QtDir="C:\Qt\5.7\msvc2015_64";
|
||||
SourcePackage=$false;
|
||||
}
|
||||
)
|
||||
|
||||
@@ -52,16 +66,21 @@ $BuildConfigurations = $null
|
||||
# Default values
|
||||
$ReleaseBuild = $false
|
||||
$NightlyBuild = $false
|
||||
$DeployBuild = $false
|
||||
$TestBuild = $false
|
||||
|
||||
if ([System.Convert]::ToBoolean($env:APPVEYOR_REPO_TAG) -And ("$env:APPVEYOR_REPO_TAG_NAME" -match "^release_(.*)")) {
|
||||
# Set to true to test either the release or the nightly part of this script
|
||||
$ReleaseTest = $false
|
||||
$NightlyTest = $false
|
||||
$TestBuildTest = $false
|
||||
|
||||
if ($ReleaseTest -Or ([System.Convert]::ToBoolean($env:APPVEYOR_REPO_TAG) -And ("$env:APPVEYOR_REPO_TAG_NAME" -match "^release_(.*)"))) {
|
||||
# Tag matches
|
||||
$ReleaseBuild = $true
|
||||
$PackageName = "fs2_open_$($matches[1])"
|
||||
$BuildConfigurations = $ReleaseConfigurations
|
||||
}
|
||||
|
||||
if ([System.Convert]::ToBoolean($env:APPVEYOR_REPO_TAG) -And ("$env:APPVEYOR_REPO_TAG_NAME" -match "^nightly_(.*)")) {
|
||||
if ($NightlyTest -Or ([System.Convert]::ToBoolean($env:APPVEYOR_REPO_TAG) -And ("$env:APPVEYOR_REPO_TAG_NAME" -match "^nightly_(.*)"))) {
|
||||
# Tag matches
|
||||
$NightlyBuild = $true
|
||||
$PackageName = "nightly_$($matches[1])"
|
||||
@@ -69,19 +88,31 @@ if ([System.Convert]::ToBoolean($env:APPVEYOR_REPO_TAG) -And ("$env:APPVEYOR_REP
|
||||
$BuildConfigurations = $NightlyConfigurations
|
||||
}
|
||||
|
||||
if ($TestBuildTest -Or ([System.Convert]::ToInt32($env:APPVEYOR_PULL_REQUEST_NUMBER) -le 0 -And ("$env:APPVEYOR_REPO_BRANCH" -match "^test\/(.*)"))) {
|
||||
# Tag matches
|
||||
$TestBuild = $true
|
||||
$PackageName = "test_$($matches[1])"
|
||||
Set-AppveyorBuildVariable 'VersionName' "$($matches[1])"
|
||||
$BuildConfigurations = $NightlyConfigurations
|
||||
|
||||
# Override the revision string so that the builds are named correctly
|
||||
[System.IO.File]::WriteAllLines("$env:APPVEYOR_BUILD_FOLDER/version_override.cmake", "set(FSO_VERSION_REVISION_STR $env:VersionName)")
|
||||
}
|
||||
|
||||
# Multiply by 2 so that we can add 0 or 1 for debug or release Configs
|
||||
$buildID = [convert]::ToInt32($env:BuildID) * 2
|
||||
if ($Env:Configuration -eq "Release") {
|
||||
$buildID = $buildID + 1
|
||||
}
|
||||
Write-Host "$buildID"
|
||||
|
||||
if ($ReleaseBuild -Or $NightlyBuild) {
|
||||
$DeployBuild = $false
|
||||
if ($ReleaseBuild -Or $NightlyBuild -Or $TestBuild) {
|
||||
$DeployBuild = $true
|
||||
}
|
||||
|
||||
Set-AppveyorBuildVariable 'ReleaseBuild' "$ReleaseBuild"
|
||||
Set-AppveyorBuildVariable 'NightlyBuild' "$NightlyBuild"
|
||||
Set-AppveyorBuildVariable 'TestBuild' "$TestBuild"
|
||||
|
||||
New-Item build -type directory
|
||||
Set-Location -Path build
|
||||
@@ -94,6 +125,11 @@ if ($DeployBuild) {
|
||||
}
|
||||
|
||||
$buildConfig = $BuildConfigurations[$buildID]
|
||||
|
||||
if ($buildConfig.SourcePackage -eq $True) {
|
||||
7z a -xr'!.git' "$($PackageName)-source-Win.zip" "$env:APPVEYOR_BUILD_FOLDER"
|
||||
Push-AppveyorArtifact "$($PackageName)-source-Win.zip"
|
||||
}
|
||||
|
||||
cmake -DCMAKE_INSTALL_PREFIX="$env:APPVEYOR_BUILD_FOLDER/../install" -DFSO_USE_SPEECH="ON" `
|
||||
-DFSO_USE_VOICEREC="ON" -DMSVC_SIMD_INSTRUCTIONS="$($buildConfig.SimdType)" `
|
||||
@@ -112,6 +148,7 @@ if ($DeployBuild) {
|
||||
Push-AppveyorArtifact "$($PackageName)-builds-$($buildConfig.PackageType).zip"
|
||||
} else {
|
||||
cmake -DFSO_USE_SPEECH="ON" -DFSO_FATAL_WARNINGS="ON" -DFSO_USE_VOICEREC="ON" -DFSO_BUILD_TESTS="ON" -DMSVC_SIMD_INSTRUCTIONS=SSE2 `
|
||||
-DFSO_BUILD_QTFRED=ON -DQT5_INSTALL_ROOT="$env:QT_DIR" -DFSO_BUILD_FRED2="OFF" `
|
||||
-G "$Env:CMAKE_GENERATOR" -T "$Env:PlatformToolset" ..
|
||||
|
||||
cmake --build . --config "$Env:CONFIGURATION" -- /verbosity:minimal
|
||||
|
||||
+2
-15
@@ -1,21 +1,8 @@
|
||||
|
||||
if ([System.Convert]::ToBoolean($env:ReleaseBuild) -Or [System.Convert]::ToBoolean($env:NightlyBuild)) {
|
||||
if ([System.Convert]::ToBoolean($env:ReleaseBuild) -Or [System.Convert]::ToBoolean($env:NightlyBuild) -Or [System.Convert]::ToBoolean($env:TestBuild)) {
|
||||
# Skip tests for deployment
|
||||
exit 0
|
||||
} else {
|
||||
# Appveyor doesn't have OpenAL so copy our DLLs to the output FOLDER
|
||||
$arch=""
|
||||
if ($Env:CMAKE_GENERATOR -Match "Win64") {
|
||||
$arch="win64"
|
||||
} else {
|
||||
$arch="win32"
|
||||
}
|
||||
$dllPath = "$Env:APPVEYOR_BUILD_FOLDER\lib\openal\libs\$arch\OpenAL32.dll"
|
||||
$copyDestination = "$pwd\bin\$Env:CONFIGURATION\"
|
||||
|
||||
write "Copying OpenAL DLL..."
|
||||
Copy-Item "$dllPath" "$copyDestination"
|
||||
|
||||
} else {
|
||||
write "Running unit tests..."
|
||||
# Run unit tests
|
||||
&"$pwd\bin\$Env:CONFIGURATION\unittests.exe" --gtest_shuffle 2>&1
|
||||
|
||||
@@ -6,6 +6,11 @@ mkdir -p build
|
||||
cd build
|
||||
|
||||
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
if [ "$RELEASE_BUILD" = true ]; then
|
||||
PARENT_DIR="$(basename $PWD)"
|
||||
(cd .. && tar -czvf /tmp/builds/$PACKAGE_NAME-source-Unix.tar.gz --exclude-vcs "$PARENT_DIR")
|
||||
fi
|
||||
|
||||
CXXFLAGS="-m64 -mtune=generic -mfpmath=sse -msse -msse2 -pipe -Wno-unknown-pragmas"
|
||||
CFLAGS="-m64 -mtune=generic -mfpmath=sse -msse -msse2 -pipe -Wno-unknown-pragmas"
|
||||
|
||||
@@ -27,7 +32,7 @@ if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
cd ..
|
||||
done
|
||||
else
|
||||
eval $CMAKE -DCMAKE_BUILD_TYPE=$CONFIGURATION -DFSO_BUILD_TESTS=ON -DFSO_BUILD_INCLUDED_LIBS=ON ..
|
||||
eval $CMAKE -DCMAKE_BUILD_TYPE=$CONFIGURATION -DFSO_BUILD_TESTS=ON -DFSO_BUILD_INCLUDED_LIBS=ON -DFSO_BUILD_QTFRED=ON ..
|
||||
fi
|
||||
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
if [ "$BUILD_DEPLOYMENT" = true ]; then
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
RELEASE_BUILD=false
|
||||
NIGHTLY_BUILD=false
|
||||
TEST_BUILD=false
|
||||
|
||||
BUILD_DEPLOYMENT=false
|
||||
BUILD_CONFIGS="Release FastDebug"
|
||||
VERSION_NAME=""
|
||||
@@ -9,10 +11,12 @@ PACKAGE_NAME=""
|
||||
|
||||
RELEASE_PATTERN="^release_(.*)$"
|
||||
NIGHTLY_PATTERN="^nightly_(.*)$"
|
||||
TEST_BUILD_PATTERN="^test\/(.*)$"
|
||||
|
||||
# These are for testing
|
||||
NIGHTLY_TEST=false
|
||||
RELEASE_TEST=false
|
||||
TEST_BUILD_TEST=false
|
||||
|
||||
if [ "$RELEASE_TEST" = true ] || [[ "$TRAVIS_TAG" =~ $RELEASE_PATTERN ]]; then
|
||||
echo "This is a release tag!";
|
||||
@@ -25,13 +29,27 @@ if [ "$NIGHTLY_TEST" = true ] || [[ "$TRAVIS_TAG" =~ $NIGHTLY_PATTERN ]]; then
|
||||
VERSION_NAME="${BASH_REMATCH[1]}";
|
||||
PACKAGE_NAME="nightly_${BASH_REMATCH[1]}";
|
||||
fi
|
||||
if [ "$TEST_BUILD_TEST" = true ] || [[ "$TRAVIS_PULL_REQUEST" == "false" && "$TRAVIS_BRANCH" =~ $TEST_BUILD_PATTERN ]]; then
|
||||
echo "This is a test build branch!";
|
||||
TEST_BUILD=true;
|
||||
if [ "$TEST_BUILD_TEST" = true ]; then
|
||||
VERSION_NAME="test";
|
||||
PACKAGE_NAME="test_test";
|
||||
else
|
||||
VERSION_NAME="${BASH_REMATCH[1]}";
|
||||
PACKAGE_NAME="test_${BASH_REMATCH[1]}";
|
||||
fi
|
||||
|
||||
if ([ "$RELEASE_BUILD" = true ] || [ "$NIGHTLY_BUILD" = true ]); then
|
||||
# Override the revision string so that the builds are named correctly
|
||||
echo "set(FSO_VERSION_REVISION_STR $VERSION_NAME)" > "$TRAVIS_BUILD_DIR/version_override.cmake"
|
||||
fi
|
||||
|
||||
if ([ "$RELEASE_BUILD" = true ] || [ "$NIGHTLY_BUILD" = true ] || [ "$TEST_BUILD" = true ]); then
|
||||
BUILD_DEPLOYMENT=true;
|
||||
fi
|
||||
|
||||
if [ "$BUILD_DEPLOYMENT" = true ]; then
|
||||
if ([ "$TRAVIS_OS_NAME" = "linux" ] && (! [[ "$CC" =~ ^gcc.*$ ]])); then
|
||||
if ([ "$TRAVIS_OS_NAME" = "linux" ] && (! [[ "$CC" =~ ^gcc-5.*$ ]])); then
|
||||
echo "Skipping non-release compiler";
|
||||
exit 0;
|
||||
fi
|
||||
@@ -44,6 +62,7 @@ fi
|
||||
|
||||
export RELEASE_BUILD
|
||||
export NIGHTLY_BUILD
|
||||
export TEST_BUILD
|
||||
export BUILD_DEPLOYMENT
|
||||
export BUILD_CONFIGS
|
||||
export VERSION_NAME
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -u
|
||||
|
||||
if [[ ! "$(curl -V)" == *"sftp"* ]]; then
|
||||
# Before
|
||||
echo "Before:"
|
||||
curl -V
|
||||
|
||||
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
|
||||
# Since the Travis CI people are not capable of enabling SSH/SCP support for their libcurl we need to build that manually...
|
||||
# These commands are based on the tutorial from here: http://zeroset.mnim.org/2013/03/14/sftp-support-for-curl-in-ubuntu-12-10-quantal-quetzal-and-later/
|
||||
mkdir /tmp/curl
|
||||
cd /tmp/curl
|
||||
sudo apt-get update
|
||||
sudo apt-get install build-essential debhelper libssh2-1-dev libgnutls-dev libidn11-dev libkrb5-dev libldap2-dev libnss3-dev librtmp-dev libtool openssh-server quilt
|
||||
apt-get source curl
|
||||
sudo apt-get build-dep curl
|
||||
|
||||
cd curl-*
|
||||
export DEB_BUILD_OPTIONS="nocheck"
|
||||
sudo dpkg-buildpackage -uc -us -j"$(grep -c ^processor /proc/cpuinfo)"
|
||||
|
||||
cd ..
|
||||
dpkg -l | grep curl
|
||||
ls -al
|
||||
sudo dpkg -i libcurl3*.deb
|
||||
sudo dpkg -i curl*.deb
|
||||
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
brew install curl --with-libssh2
|
||||
export PATH="/usr/local/opt/curl/bin:$PATH"
|
||||
fi
|
||||
|
||||
# After
|
||||
echo "After:"
|
||||
curl -V
|
||||
fi
|
||||
|
||||
cd /tmp/builds
|
||||
|
||||
for file in *; do
|
||||
if [ "$TEST_BUILD" = true ]; then
|
||||
# Upload to fs2downloads
|
||||
curl -k "ftp://swc.fs2downloads.com/swc.fs2downloads.com/builds/test/$VERSION_NAME/" --user "$FS2DOWNLOADS_USER:$FS2DOWNLOADS_PASSWORD" -T "$file" --ftp-create-dirs
|
||||
else
|
||||
# Upload to indiegames
|
||||
curl -k "sftp://scp.indiegames.us/~/public_html/builds/nightly/$VERSION_NAME/" --user "$INDIEGAMES_USER:$INDIEGAMES_PASSWORD" -T "$file" --ftp-create-dirs
|
||||
|
||||
# Upload to fs2downloads
|
||||
curl -k "ftp://swc.fs2downloads.com/swc.fs2downloads.com/builds/nightly/$VERSION_NAME/" --user "$FS2DOWNLOADS_USER:$FS2DOWNLOADS_PASSWORD" -T "$file" --ftp-create-dirs
|
||||
fi
|
||||
done
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
HERE=$(dirname $(readlink -f "${0}"))
|
||||
export LD_LIBRARY_PATH="${HERE}"/lib:$PATH
|
||||
export LD_LIBRARY_PATH="${HERE}"/lib:"$LD_LIBRARY_PATH"
|
||||
"${HERE}"/bin/$<TARGET_FILE_NAME:Freespace2> $@
|
||||
@@ -1,6 +1,9 @@
|
||||
|
||||
include(WriteCompilerDetectionHeader)
|
||||
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED 11)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
set(REQUIRED_CXX_FEATURES cxx_rvalue_references cxx_nullptr)
|
||||
|
||||
get_property(CMAKE_CXX_KNOWN_FEATURES GLOBAL PROPERTY CMAKE_CXX_KNOWN_FEATURES)
|
||||
|
||||
@@ -27,6 +27,7 @@ int main( int argc, char* argv[] )
|
||||
int SSE = false;
|
||||
int SSE2 = false;
|
||||
int AVX = false;
|
||||
int AVX2 = false;
|
||||
|
||||
int info[4];
|
||||
cpuid(info, 0);
|
||||
@@ -40,8 +41,14 @@ int main( int argc, char* argv[] )
|
||||
|
||||
AVX = (info[2] & ((int)1 << 28)) != 0;
|
||||
}
|
||||
if (nIds >= 7){
|
||||
cpuid(info,0x00000007);
|
||||
AVX2 = (info[1] & ((int)1 << 5)) != 0;
|
||||
}
|
||||
|
||||
if(AVX)
|
||||
if (AVX2)
|
||||
std::cout << "AVX2";
|
||||
else if(AVX)
|
||||
std::cout << "AVX";
|
||||
else if(SSE2)
|
||||
std::cout << "SSE2";
|
||||
|
||||
Vendored
-1
Submodule cmake/external/cotire deleted from 40a46bf2fb.
Vendored
+4054
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,9 @@
|
||||
|
||||
|
||||
#ifndef __FREDRC_H
|
||||
#define __FREDRC_H
|
||||
|
||||
#define ABOUT_LINE_1 "FRED2_OPEN - FreeSpace Editor, Version @FSO_PRODUCT_VERSION_STRING@ OpenGL"
|
||||
#define MAINFRAME_CONTENTS "FRED2_OPEN @FSO_PRODUCT_VERSION_STRING@ - FreeSpace2 Mission Editor\nUntitled\nFRED2\nFreeSpace2 Missions (*.fs2)\n.fs2\nFreeSpace2Mission\nFreeSpace2 Mission"
|
||||
|
||||
#endif // __FREDRC_H
|
||||
@@ -1,5 +1,7 @@
|
||||
|
||||
include(platformChecks)
|
||||
|
||||
include(Cxx11)
|
||||
include(platformChecks)
|
||||
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_LIST_DIR}/project.h.in ${GENERATED_SOURCE_DIR}/project.h)
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_LIST_DIR}/fred_rc.h.in ${GENERATED_SOURCE_DIR}/fred_rc.h)
|
||||
@@ -1,2 +1,18 @@
|
||||
|
||||
target_compile_definitions(platform INTERFACE SCP_SOLARIS)
|
||||
|
||||
find_library(SOCKET_LIB socket)
|
||||
|
||||
if (NOT SOCKET_LIB)
|
||||
message(FATAL_ERROR "Couldn't find libsocket! Please install it and rerun the configuration.")
|
||||
else()
|
||||
target_link_libraries(platform INTERFACE "${SOCKET_LIB}")
|
||||
endif()
|
||||
|
||||
find_library(NSL_LIB nsl)
|
||||
|
||||
if (NOT NSL_LIB)
|
||||
message(FATAL_ERROR "Couldn't find libnsl! Please install it and rerun the configuration.")
|
||||
else()
|
||||
target_link_libraries(platform INTERFACE "${NSL_LIB}")
|
||||
endif()
|
||||
@@ -15,9 +15,9 @@ CHECK_INCLUDE_FILE_CXX("cxxabi.h" HAVE_CXXAPI_H)
|
||||
|
||||
CHECK_TYPE_SIZE("max_align_t" MAX_ALIGN_T)
|
||||
|
||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||
set(CMAKE_REQUIRED_FLAGS "-std=c++11") # required for g++ <= 5
|
||||
endif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
endif()
|
||||
|
||||
set(CMAKE_EXTRA_INCLUDE_FILES "cstddef")
|
||||
CHECK_TYPE_SIZE("std::max_align_t" STD_MAX_ALIGN_T LANGUAGE CXX)
|
||||
@@ -25,6 +25,9 @@ set(CMAKE_EXTRA_INCLUDE_FILES "type_traits")
|
||||
CHECK_TYPE_SIZE("std::is_trivially_copyable<int>" STD_IS_TRIVIALLY_COPYABLE LANGUAGE CXX)
|
||||
set(CMAKE_EXTRA_INCLUDE_FILES)
|
||||
|
||||
CHECK_TYPE_SIZE("char32_t" CHAR32_T LANGUAGE CXX)
|
||||
CHECK_TYPE_SIZE("U'b'" UNICODE_CHAR_LITERAL LANGUAGE CXX)
|
||||
|
||||
set(CMAKE_REQUIRED_FLAGS)
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#cmakedefine HAVE_STD_MAX_ALIGN_T
|
||||
|
||||
#cmakedefine HAVE_STD_IS_TRIVIALLY_COPYABLE
|
||||
#cmakedefine HAVE_STD_IS_TRIVIALLY_COPYABLE_ARRAY
|
||||
|
||||
#cmakedefine01 IS_64BIT
|
||||
|
||||
@@ -21,4 +22,7 @@
|
||||
|
||||
#cmakedefine01 HAVE_STRINGS_H
|
||||
|
||||
#cmakedefine01 HAVE_CHAR32_T
|
||||
#cmakedefine01 HAVE_UNICODE_CHAR_LITERAL
|
||||
|
||||
#endif // __PLATFORM_CHECKS_H__
|
||||
@@ -33,7 +33,9 @@ set(WARNING_FLAGS
|
||||
/wd4311 # Disables warnings about casting pointer types to ints. The funny thing is these warnings can't be resolved, just disabled... - m!m
|
||||
/wd4302 # Same as above - m!m
|
||||
/wd4366 # The result of the unary '&' operator may be unaligned - m!m
|
||||
/wd4592 # "symbol will be dynamically initialized" is being generated by a compiler bug, see https://stackoverflow.com/a/34027257 for an explanation - m!m
|
||||
$<$<CONFIG:Release>:/wd4101> # In release mode there are unreferenced variables because debug needs them
|
||||
/wd4995 # "'vsprintf': name was marked as #pragma deprecated" is being caused by our custom definition of vsprintf. I have no idea what causes this or how to fix it - m!m
|
||||
)
|
||||
|
||||
target_compile_options(compiler INTERFACE ${WARNING_FLAGS})
|
||||
@@ -88,7 +90,7 @@ ENDIF(MSVC_USE_RUNTIME_DLL)
|
||||
INCLUDE(MSVCMultipleProcessCompile)
|
||||
|
||||
# Visual Studio supports compiling for multiple vector instruction sets
|
||||
SET(POSSIBLE_INSTUCTION_SETS "" SSE SSE2 AVX)
|
||||
SET(POSSIBLE_INSTUCTION_SETS "" SSE SSE2 AVX AVX2)
|
||||
|
||||
if (NOT DEFINED MSVC_SIMD_INSTRUCTIONS)
|
||||
detect_simd_instructions(MSVC_DETECTED_SIMD_INSTRUCTIONS)
|
||||
|
||||
@@ -20,7 +20,7 @@ ENDIF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
# Copy release settings to FastDebug
|
||||
set(CMAKE_C_FLAGS_FASTDEBUG "${CMAKE_C_FLAGS_RELEASE}")
|
||||
set(CMAKE_CXX_FLAGS_FASTDEBUG "${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_FASTDEBUG "${CMAKE_EXE_LINKER_RELEASE}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_FASTDEBUG "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
|
||||
set(CMAKE_STATIC_LINKER_FLAGS_FASTDEBUG "${CMAKE_STATIC_LINKER_FLAGS_RELEASE}")
|
||||
|
||||
if (MSVC)
|
||||
|
||||
@@ -195,3 +195,31 @@ function(suppress_warnings _target)
|
||||
target_compile_options(${_target} PRIVATE "-w")
|
||||
endif()
|
||||
endfunction(suppress_warnings)
|
||||
|
||||
|
||||
function(list_target_dependencies _target _out_var)
|
||||
set(out_list)
|
||||
set(work_libs ${_target})
|
||||
list(LENGTH work_libs libs_length)
|
||||
|
||||
while(libs_length GREATER 0)
|
||||
list(GET work_libs 0 current)
|
||||
list(REMOVE_AT work_libs 0)
|
||||
list(APPEND out_list ${current})
|
||||
|
||||
get_target_property(current_libs ${current} INTERFACE_LINK_LIBRARIES)
|
||||
|
||||
if(current_libs)
|
||||
foreach(lib ${current_libs})
|
||||
if (TARGET ${lib})
|
||||
list(APPEND work_libs ${lib})
|
||||
endif (TARGET ${lib})
|
||||
endforeach(lib)
|
||||
endif(current_libs)
|
||||
|
||||
list(LENGTH work_libs libs_length)
|
||||
endwhile(libs_length GREATER 0)
|
||||
|
||||
list(REMOVE_DUPLICATES out_list)
|
||||
set(${_out_var} ${out_list} PARENT_SCOPE)
|
||||
endfunction(list_target_dependencies)
|
||||
+2
-2
@@ -7,8 +7,8 @@ if (EXISTS "${PROJECT_SOURCE_DIR}/version_override.cmake")
|
||||
endif()
|
||||
|
||||
set_if_not_defined(FSO_VERSION_MAJOR 3)
|
||||
set_if_not_defined(FSO_VERSION_MINOR 7)
|
||||
set_if_not_defined(FSO_VERSION_BUILD 5)
|
||||
set_if_not_defined(FSO_VERSION_MINOR 8)
|
||||
set_if_not_defined(FSO_VERSION_BUILD 1)
|
||||
|
||||
# A numeric revision e.g. the CI build number
|
||||
set_if_not_defined(FSO_VERSION_REVISION 0)
|
||||
|
||||
+3
-3
@@ -21,7 +21,7 @@ TARGET_INCLUDE_DIRECTORIES(code PUBLIC ${GENERATED_SOURCE_DIR}/code)
|
||||
|
||||
target_link_libraries(code PUBLIC OpenGL)
|
||||
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC ${OPENAL_LIBS})
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC openal)
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC ${LUA_LIBS})
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC ${PNG_LIBS})
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC ${JPEG_LIBS})
|
||||
@@ -30,6 +30,8 @@ TARGET_LINK_LIBRARIES(code PUBLIC sdl2)
|
||||
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC ffmpeg)
|
||||
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC utfcpp)
|
||||
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC platform)
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC compiler)
|
||||
|
||||
@@ -58,8 +60,6 @@ set_target_properties(code PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "prefix_head
|
||||
include(util)
|
||||
configure_cotire(code)
|
||||
|
||||
include(../cmake/Cxx11.cmake)
|
||||
|
||||
# Ensure that Xcode generates debug symbols on macOS for Debug builds
|
||||
set_target_properties(code PROPERTIES XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS[variant=Debug] "YES")
|
||||
set_target_properties(code PROPERTIES XCODE_ATTRIBUTE_COPY_PHASE_STRIP[variant=Debug] "NO")
|
||||
|
||||
@@ -498,7 +498,8 @@ void parse_ai_profiles_tbl(const char *filename)
|
||||
}
|
||||
|
||||
// find next valid option
|
||||
skip_to_start_of_string_either("$", "#");
|
||||
if (!skip_to_start_of_string_either("$", "#"))
|
||||
break;
|
||||
saved_Mp = Mp;
|
||||
}
|
||||
}
|
||||
|
||||
+14
-6
@@ -3814,7 +3814,7 @@ float ai_path_0()
|
||||
int path_num;
|
||||
Assert(aip->active_goal >= 0);
|
||||
ai_goal *aigp = &aip->goals[aip->active_goal];
|
||||
Assert(aigp->flags[AI::Goal_Flags::Dockee_index_valid] || aigp->flags[AI::Goal_Flags::Docker_index_valid]);
|
||||
Assert(aigp->flags[AI::Goal_Flags::Dockee_index_valid] && aigp->flags[AI::Goal_Flags::Docker_index_valid]);
|
||||
|
||||
path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aigp->dockee.index);
|
||||
ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
|
||||
@@ -3957,7 +3957,7 @@ float ai_path_1()
|
||||
int path_num;
|
||||
Assert(aip->active_goal >= 0);
|
||||
ai_goal *aigp = &aip->goals[aip->active_goal];
|
||||
Assert(aigp->flags[AI::Goal_Flags::Dockee_index_valid] || aigp->flags[AI::Goal_Flags::Docker_index_valid]);
|
||||
Assert(aigp->flags[AI::Goal_Flags::Dockee_index_valid] && aigp->flags[AI::Goal_Flags::Docker_index_valid]);
|
||||
|
||||
path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aigp->dockee.index);
|
||||
ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
|
||||
@@ -10395,11 +10395,12 @@ void ai_get_dock_goal_indexes(object *objp, ai_info *aip, ai_goal *aigp, object
|
||||
case AIS_DOCK_2:
|
||||
case AIS_DOCK_3:
|
||||
Warning(LOCATION, "Normally dock indexes should be calculated for only AIS_DOCK_0 and AIS_UNDOCK_0. Trace out and debug.");
|
||||
FALLTHROUGH;
|
||||
case AIS_DOCK_0:
|
||||
{
|
||||
// get them from the active goal
|
||||
Assert(aigp != NULL);
|
||||
Assert(aigp->flags[AI::Goal_Flags::Dockee_index_valid] || aigp->flags[AI::Goal_Flags::Docker_index_valid]);
|
||||
Assert(aigp->flags[AI::Goal_Flags::Dockee_index_valid] && aigp->flags[AI::Goal_Flags::Docker_index_valid]);
|
||||
docker_index = aigp->docker.index;
|
||||
dockee_index = aigp->dockee.index;
|
||||
Assert(docker_index >= 0);
|
||||
@@ -10412,6 +10413,7 @@ void ai_get_dock_goal_indexes(object *objp, ai_info *aip, ai_goal *aigp, object
|
||||
case AIS_UNDOCK_1:
|
||||
case AIS_UNDOCK_2:
|
||||
Warning(LOCATION, "Normally dock indexes should be calculated for only AIS_DOCK_0 and AIS_UNDOCK_0. Trace out and debug.");
|
||||
FALLTHROUGH;
|
||||
case AIS_UNDOCK_0:
|
||||
{
|
||||
// get them from the guy I'm docked to
|
||||
@@ -10478,9 +10480,11 @@ void ai_cleanup_dock_mode_subjective(object *objp)
|
||||
case AIS_UNDOCK_0:
|
||||
model_anim_start_type(shipp, TRIGGER_TYPE_DOCKED, docker_index, -1);
|
||||
model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKED, dockee_index, -1);
|
||||
FALLTHROUGH;
|
||||
case AIS_UNDOCK_1:
|
||||
model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_3, docker_index, -1);
|
||||
model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKING_STAGE_3, dockee_index, -1);
|
||||
FALLTHROUGH;
|
||||
case AIS_UNDOCK_2:
|
||||
model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_2, docker_index, -1);
|
||||
model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKING_STAGE_2, dockee_index, -1);
|
||||
@@ -10490,9 +10494,11 @@ void ai_cleanup_dock_mode_subjective(object *objp)
|
||||
case AIS_DOCK_4A:
|
||||
model_anim_start_type(shipp, TRIGGER_TYPE_DOCKED, docker_index, -1);
|
||||
model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKED, dockee_index, -1);
|
||||
FALLTHROUGH;
|
||||
case AIS_DOCK_3:
|
||||
model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_3, docker_index, -1);
|
||||
model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKING_STAGE_3, dockee_index, -1);
|
||||
FALLTHROUGH;
|
||||
case AIS_DOCK_2:
|
||||
model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_2, docker_index, -1);
|
||||
model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKING_STAGE_2, dockee_index, -1);
|
||||
@@ -10833,7 +10839,7 @@ void ai_dock()
|
||||
|
||||
if (aip->submode == AIS_DOCK_3) {
|
||||
// Play a ship docking attach sound
|
||||
snd_play_3d( &Snds[SND_DOCK_ATTACH], &Pl_objp->pos, &View_position );
|
||||
snd_play_3d( gamesnd_get_game_sound(SND_DOCK_ATTACH), &Pl_objp->pos, &View_position );
|
||||
|
||||
// start the dock animation
|
||||
model_anim_start_type(shipp, TRIGGER_TYPE_DOCKED, docker_index, 1);
|
||||
@@ -10960,7 +10966,7 @@ void ai_dock()
|
||||
ai_find_path(Pl_objp, OBJ_INDEX(goal_objp), path_num, 0);
|
||||
|
||||
// Play a ship docking detach sound
|
||||
snd_play_3d( &Snds[SND_DOCK_DETACH], &Pl_objp->pos, &View_position );
|
||||
snd_play_3d( gamesnd_get_game_sound(SND_DOCK_DETACH), &Pl_objp->pos, &View_position );
|
||||
|
||||
aip->submode = AIS_UNDOCK_1;
|
||||
aip->submode_start_time = Missiontime;
|
||||
@@ -10980,7 +10986,7 @@ void ai_dock()
|
||||
// play the depart sound, but only once, since this mode is called multiple times per frame
|
||||
if ( !(aigp->flags[AI::Goal_Flags::Depart_sound_played]))
|
||||
{
|
||||
snd_play_3d( &Snds[SND_DOCK_DEPART], &Pl_objp->pos, &View_position );
|
||||
snd_play_3d( gamesnd_get_game_sound(SND_DOCK_DEPART), &Pl_objp->pos, &View_position );
|
||||
aigp->flags.set(AI::Goal_Flags::Depart_sound_played);
|
||||
}
|
||||
|
||||
@@ -12376,6 +12382,7 @@ void ai_maybe_evade_locked_missile(object *objp, ai_info *aip)
|
||||
case AIM_DOCK: // Ships in dock mode can evade iif they are not currently repairing or docked.
|
||||
if (object_is_docked(objp) || (aip->ai_flags[AI::AI_Flags::Repairing, AI::AI_Flags::Being_repaired]))
|
||||
break;
|
||||
FALLTHROUGH;
|
||||
case AIM_GUARD:
|
||||
// If in guard mode and far away from guard object, don't pursue guy that hit me.
|
||||
if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
|
||||
@@ -12383,6 +12390,7 @@ void ai_maybe_evade_locked_missile(object *objp, ai_info *aip)
|
||||
return;
|
||||
}
|
||||
}
|
||||
FALLTHROUGH;
|
||||
case AIM_EVADE:
|
||||
case AIM_GET_BEHIND:
|
||||
case AIM_STAY_NEAR:
|
||||
|
||||
+1
-1
@@ -1640,7 +1640,7 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
}
|
||||
else
|
||||
{
|
||||
Int3(); // get ALLENDER
|
||||
mprintf(("Potentially incorrect behaviour in AI goal for ship %s: Ship %s could not be found among currently active, departed, or yet-to-arrive ships.\nPlease check the mission file.\n", Ships[objp->instance].ship_name, aigp->target_name));
|
||||
status = SHIP_STATUS_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2051,7 +2051,7 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
|
||||
if ( wip->launch_snd != -1 ) {
|
||||
// Don't play turret firing sound if turret sits on player ship... it gets annoying.
|
||||
if ( parent_objnum != OBJ_INDEX(Player_obj) || (turret->flags[Ship::Subsystem_Flags::Play_sound_for_player]) ) {
|
||||
snd_play_3d( &Snds[wip->launch_snd], turret_pos, &View_position );
|
||||
snd_play_3d( gamesnd_get_game_sound(wip->launch_snd), turret_pos, &View_position );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2138,7 +2138,7 @@ void turret_swarm_fire_from_turret(turret_swarm_info *tsi)
|
||||
if ( Weapon_info[tsi->weapon_class].launch_snd != -1 ) {
|
||||
// Don't play turret firing sound if turret sits on player ship... it gets annoying.
|
||||
if ( tsi->parent_objnum != OBJ_INDEX(Player_obj) ) {
|
||||
snd_play_3d( &Snds[Weapon_info[tsi->weapon_class].launch_snd], &turret_pos, &View_position );
|
||||
snd_play_3d( gamesnd_get_game_sound(Weapon_info[tsi->weapon_class].launch_snd), &turret_pos, &View_position );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-1
@@ -733,6 +733,10 @@ anim *anim_load(const char *real_filename, int cf_dir_type, int file_mapped)
|
||||
|
||||
anim_read_header(ptr, fp);
|
||||
|
||||
if (ptr->width < 0 || ptr->height < 0) {
|
||||
Error(LOCATION, "Ani file %s has a faulty header and cannot be loaded.", name);
|
||||
}
|
||||
|
||||
if(ptr->num_keys > 0){
|
||||
ptr->keys = (key_frame*)vm_malloc(sizeof(key_frame) * ptr->num_keys);
|
||||
Assert(ptr->keys != NULL);
|
||||
@@ -761,7 +765,7 @@ anim *anim_load(const char *real_filename, int cf_dir_type, int file_mapped)
|
||||
}
|
||||
|
||||
// couldn't memory-map file... must be in a packfile, so stream manually
|
||||
if ( file_mapped && !ptr->cfile_ptr ) {
|
||||
if ( file_mapped == PAGE_FROM_MEM && !ptr->cfile_ptr ) {
|
||||
ptr->flags &= ~ANF_MEM_MAPPED;
|
||||
ptr->flags |= ANF_STREAMED;
|
||||
ptr->cfile_ptr = cfopen(name, "rb", CFILE_NORMAL, cf_dir_type);
|
||||
@@ -969,6 +973,11 @@ void anim_display_info(char *real_filename)
|
||||
}
|
||||
|
||||
anim_read_header(&A, fp);
|
||||
|
||||
if (A.width < 0 || A.height < 0) {
|
||||
Error(LOCATION, "Ani file %s has a faulty header and cannot be loaded.", filename);
|
||||
}
|
||||
|
||||
// read the keyframe frame nums and offsets
|
||||
key_frame_nums = (int*)vm_malloc(sizeof(int)*A.num_keys);
|
||||
Assert(key_frame_nums != NULL);
|
||||
|
||||
@@ -40,9 +40,8 @@ typedef struct {
|
||||
|
||||
enum
|
||||
{
|
||||
PAGE_FROM_MEM = 0,
|
||||
PAGE_FROM_DISK = 1,
|
||||
PAGE_FROM_DISK_FORCED = 2
|
||||
PAGE_FROM_DISK = 0,
|
||||
PAGE_FROM_MEM = 1
|
||||
};
|
||||
|
||||
extern int Anim_paused;
|
||||
@@ -59,7 +58,7 @@ int anim_stop_playing(anim_instance* anim_instance);
|
||||
int anim_show_next_frame(anim_instance *instance, float frametime);
|
||||
void anim_release_all_instances(int screen_id = 0);
|
||||
void anim_release_render_instance(anim_instance* instance);
|
||||
anim *anim_load(const char *name, int cf_dir_type = CF_TYPE_ANY, int file_mapped = PAGE_FROM_MEM);
|
||||
anim *anim_load(const char *name, int cf_dir_type = CF_TYPE_ANY, int file_mapped = PAGE_FROM_DISK);
|
||||
int anim_free(anim *ptr);
|
||||
int anim_playing(anim_instance *ai);
|
||||
int anim_write_frames_out(char *filename);
|
||||
|
||||
@@ -512,7 +512,14 @@ int unpack_pixel(anim_instance *ai, ubyte *data, ubyte pix, int aabitmap, int bp
|
||||
bit_16 = (ushort)pix;
|
||||
break;
|
||||
case 8:
|
||||
bit_8 = pix;
|
||||
// 8 bit-per-pixel aa bitmaps are a bit special since they only use value in the range [0, 15] where 15 wraps
|
||||
// around back to 0. Since the rest of the code expects the value to be in the range [0, 255] the pixel value
|
||||
// needs to be adjusted here. By muliplying the value with 17 the original range [0, 15] is mapped to [0, 255]
|
||||
if (pix >= 15) {
|
||||
bit_8 = 0;
|
||||
} else {
|
||||
bit_8 = (ubyte)(pix * 17);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Int3();
|
||||
@@ -596,7 +603,14 @@ int unpack_pixel_count(anim_instance *ai, ubyte *data, ubyte pix, int count = 0,
|
||||
bit_16 = (ushort)pix;
|
||||
break;
|
||||
case 8 :
|
||||
bit_8 = pix;
|
||||
// 8 bit-per-pixel aa bitmaps are a bit special since they only use value in the range [0, 15] where 15 wraps
|
||||
// around back to 0. Since the rest of the code expects the value to be in the range [0, 255] the pixel value
|
||||
// needs to be adjusted here. By muliplying the value with 17 the original range [0, 15] is mapped to [0, 255]
|
||||
if (pix >= 15) {
|
||||
bit_8 = 0;
|
||||
} else {
|
||||
bit_8 = (ubyte)(pix * 17);
|
||||
}
|
||||
break;
|
||||
default :
|
||||
Int3();
|
||||
|
||||
@@ -1312,7 +1312,7 @@ void asteriod_explode_sound(object *objp, int type, int play_loud)
|
||||
range_factor = 1.0f;
|
||||
}
|
||||
|
||||
snd_play_3d( &Snds[sound_index], &objp->pos, &Eye_position, objp->radius, NULL, 0, 1.0f, SND_PRIORITY_MUST_PLAY, NULL, range_factor );
|
||||
snd_play_3d( gamesnd_get_game_sound(sound_index), &objp->pos, &Eye_position, objp->radius, NULL, 0, 1.0f, SND_PRIORITY_MUST_PLAY, NULL, range_factor );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1568,8 +1568,12 @@ void asteroid_maybe_break_up(object *pasteroid_obj)
|
||||
vec3d tempv = dir_vec;
|
||||
vm_vec_random_cone(&dir_vec, &tempv, (360.0f / total_roids / 2));
|
||||
|
||||
// Make the roid inherit half of the parent's velocity
|
||||
vm_vec_copy_normalize(&parent_vel, &pasteroid_obj->phys_info.vel);
|
||||
// Make the roid inherit half of parent's directional movement
|
||||
if (!IS_VEC_NULL(&pasteroid_obj->phys_info.vel)) {
|
||||
vm_vec_copy_normalize(&parent_vel, &pasteroid_obj->phys_info.vel);
|
||||
} else {
|
||||
vm_vec_rand_vec_quick(&parent_vel);
|
||||
}
|
||||
vm_vec_scale(&parent_vel, 0.5f);
|
||||
|
||||
// Make the hit position affect the direction, but only a little
|
||||
|
||||
@@ -31,6 +31,7 @@ union bm_extra_info {
|
||||
int keyframe; //!< used for animations -- keyframe info
|
||||
float total_time; //!< used for animations -- total animation time (not always derived from num_frames/fps)
|
||||
ubyte fps; //!< used for animations -- frames per second
|
||||
bool is_array; //!< Flag for if all frames of an animation have the same size which means that it can be put into a texture array
|
||||
|
||||
struct {
|
||||
// stuff for static animations
|
||||
@@ -89,7 +90,7 @@ struct bitmap_entry {
|
||||
#endif
|
||||
};
|
||||
|
||||
extern bitmap_entry bm_bitmaps[MAX_BITMAPS];
|
||||
extern bitmap_entry* bm_bitmaps;
|
||||
|
||||
// image specific lock functions
|
||||
void bm_lock_ani( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, int bpp, ubyte flags );
|
||||
|
||||
+195
-32
@@ -24,6 +24,7 @@
|
||||
#include "debugconsole/console.h"
|
||||
#include "globalincs/systemvars.h"
|
||||
#include "graphics/2d.h"
|
||||
#include "graphics/matrix.h"
|
||||
#include "graphics/grinternal.h"
|
||||
#include "io/key.h"
|
||||
#include "io/timer.h"
|
||||
@@ -83,17 +84,19 @@ const int BM_ANI_NUM_TYPES = sizeof(bm_ani_type_list) / sizeof(bm_ani_type_list[
|
||||
void(*bm_set_components)(ubyte *pixel, ubyte *r, ubyte *g, ubyte *b, ubyte *a) = NULL;
|
||||
void(*bm_set_components_32)(ubyte *pixel, ubyte *r, ubyte *g, ubyte *b, ubyte *a) = NULL;
|
||||
|
||||
int MAX_BITMAPS = DEFAULT_MAX_BITMAPS;
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// Declaration of protected variables (defined in cmdline.cpp).
|
||||
extern int Cmdline_cache_bitmaps;
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// Definition of public variables (declared as extern in bm_internal.h).
|
||||
bitmap_entry bm_bitmaps[MAX_BITMAPS];
|
||||
bitmap_entry* bm_bitmaps = nullptr;
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// Definition of private variables at file scope (static).
|
||||
static int bm_inited = 0;
|
||||
static bool bm_inited = false;
|
||||
static uint Bm_next_signature = 0x1234;
|
||||
static int bm_next_handle = 1;
|
||||
static int Bm_low_mem = 0;
|
||||
@@ -480,7 +483,11 @@ void bm_close() {
|
||||
for (i = 0; i<MAX_BITMAPS; i++) {
|
||||
bm_free_data(i); // clears flags, bbp, data, etc
|
||||
}
|
||||
bm_inited = 0;
|
||||
if (bm_bitmaps != nullptr) {
|
||||
delete[] bm_bitmaps;
|
||||
bm_bitmaps = nullptr;
|
||||
}
|
||||
bm_inited = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,7 +498,7 @@ int bm_create(int bpp, int w, int h, void *data, int flags) {
|
||||
Assert((bpp == 16) || (bpp == 24) || (bpp == 32));
|
||||
}
|
||||
|
||||
if (!bm_inited) bm_init();
|
||||
Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
|
||||
|
||||
int n = -1;
|
||||
|
||||
@@ -871,7 +878,7 @@ int bm_get_info(int handle, int *w, int * h, ubyte * flags, int *nframes, int *f
|
||||
if (h) *h = bmp->h;
|
||||
if (flags) *flags = bmp->flags;
|
||||
|
||||
if (bm_is_anim(bitmapnum) == true) {
|
||||
if (bm_is_anim(bitmapnum)) {
|
||||
if (nframes) {
|
||||
*nframes = bm_bitmaps[bitmapnum].info.ani.num_frames;
|
||||
}
|
||||
@@ -942,7 +949,7 @@ void bm_get_palette(int handle, ubyte *pal, char *name) {
|
||||
}
|
||||
|
||||
uint bm_get_signature(int handle) {
|
||||
if (!bm_inited) bm_init();
|
||||
Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
|
||||
|
||||
int bitmapnum = handle % MAX_BITMAPS;
|
||||
Assertion(bm_bitmaps[bitmapnum].handle == handle, "Invalid bitmap handle %d passed to bm_get_signature().\nThis might be due to an invalid animation somewhere else.\n", handle); // INVALID BITMAP HANDLE!
|
||||
@@ -967,7 +974,7 @@ int bm_get_tcache_type(int num) {
|
||||
}
|
||||
|
||||
BM_TYPE bm_get_type(int handle) {
|
||||
if (!bm_inited) bm_init();
|
||||
Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
|
||||
|
||||
int bitmapnum = handle % MAX_BITMAPS;
|
||||
Assertion(bm_bitmaps[bitmapnum].handle == handle, "Invalid bitmap handle %d passed to bm_get_type().\nThis might be due to an invalid animation somewhere else.\n", handle); // INVALID BITMAP HANDLE!
|
||||
@@ -989,16 +996,17 @@ bool bm_has_alpha_channel(int handle) {
|
||||
}
|
||||
|
||||
void bm_init() {
|
||||
Assertion(!bm_inited, "bmpman cannot be initialized more than once!");
|
||||
|
||||
int i;
|
||||
|
||||
mprintf(("Size of bitmap info = " SIZE_T_ARG " KB\n", sizeof(bm_bitmaps) / 1024));
|
||||
mprintf(("Size of bitmap extra info = " SIZE_T_ARG " bytes\n", sizeof(bm_extra_info)));
|
||||
|
||||
if (!bm_inited) {
|
||||
bm_inited = 1;
|
||||
atexit(bm_close);
|
||||
if (bm_bitmaps == nullptr) {
|
||||
bm_bitmaps = new bitmap_entry[MAX_BITMAPS];
|
||||
}
|
||||
|
||||
mprintf(("Size of bitmap info = " SIZE_T_ARG " KB\n", (sizeof(bm_bitmaps[0]) * MAX_BITMAPS) / 1024));
|
||||
mprintf(("Size of bitmap extra info = " SIZE_T_ARG " bytes\n", sizeof(bm_extra_info)));
|
||||
|
||||
for (i = 0; i<MAX_BITMAPS; i++) {
|
||||
bm_bitmaps[i].filename[0] = '\0';
|
||||
bm_bitmaps[i].type = BM_TYPE_NONE;
|
||||
@@ -1022,6 +1030,8 @@ void bm_init() {
|
||||
|
||||
bm_free_data(i); // clears flags, bbp, data, etc
|
||||
}
|
||||
|
||||
bm_inited = true;
|
||||
}
|
||||
|
||||
int bm_is_compressed(int num) {
|
||||
@@ -1203,8 +1213,7 @@ int bm_load(const char *real_filename) {
|
||||
CFILE *img_cfp = NULL;
|
||||
int handle = -1;
|
||||
|
||||
if (!bm_inited)
|
||||
bm_init();
|
||||
Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
|
||||
|
||||
// if no file was passed then get out now
|
||||
if ((real_filename == NULL) || (strlen(real_filename) <= 0))
|
||||
@@ -1509,8 +1518,7 @@ int bm_load_animation(const char *real_filename, int *nframes, int *fps, int *ke
|
||||
size_t img_size = 0;
|
||||
char clean_name[MAX_FILENAME_LEN];
|
||||
|
||||
if (!bm_inited)
|
||||
bm_init();
|
||||
Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
|
||||
|
||||
// set output param defaults before going any further
|
||||
if (nframes != nullptr)
|
||||
@@ -1619,6 +1627,10 @@ int bm_load_animation(const char *real_filename, int *nframes, int *fps, int *ke
|
||||
#endif
|
||||
anim_read_header(&the_anim, img_cfp);
|
||||
|
||||
if (the_anim.width < 0 || the_anim.height < 0) {
|
||||
Error(LOCATION, "Ani file %s has a faulty header and cannot be loaded.", real_filename);
|
||||
}
|
||||
|
||||
anim_frames = the_anim.total_frames;
|
||||
anim_fps = the_anim.fps;
|
||||
if (anim_fps == 0) {
|
||||
@@ -1697,6 +1709,8 @@ int bm_load_animation(const char *real_filename, int *nframes, int *fps, int *ke
|
||||
}
|
||||
|
||||
int first_handle = bm_get_next_handle();
|
||||
// if all images of the animation have the same size then we can use a texture array
|
||||
bool is_array = true;
|
||||
|
||||
for (i = 0; i < anim_frames; i++) {
|
||||
memset(&bm_bitmaps[n + i], 0, sizeof(bitmap_entry));
|
||||
@@ -1783,8 +1797,39 @@ int bm_load_animation(const char *real_filename, int *nframes, int *fps, int *ke
|
||||
bm_bitmaps[n + i].info.ani.apng.is_apng = false;
|
||||
}
|
||||
|
||||
if (bm_bitmaps[n].bm.w != bm_bitmaps[n + i].bm.w || bm_bitmaps[n].bm.h != bm_bitmaps[n + i].bm.h) {
|
||||
// We found a frame with a different size than the first frame -> this can't be used as a texture array
|
||||
is_array = false;
|
||||
|
||||
Warning(LOCATION, "Animation '%s' has images that are of different sizes (currently at frame %d)."
|
||||
"Performance could be improved by making all images the same size.", filename, i + 1);
|
||||
}
|
||||
if (bm_bitmaps[n].comp_type != bm_bitmaps[n + i].comp_type) {
|
||||
// Different compression type
|
||||
is_array = false;
|
||||
|
||||
Warning(LOCATION, "Animation '%s' has images that are of different compression formats (currently at frame %d)."
|
||||
"Performance could be improved by making all images the same compression format.", filename, i + 1);
|
||||
}
|
||||
if (bm_bitmaps[n].bm.true_bpp != bm_bitmaps[n + i].bm.true_bpp) {
|
||||
// We found a frame with an incompatible pixel format
|
||||
is_array = false;
|
||||
|
||||
Warning(LOCATION, "Animation '%s' has images that are of different pixel formats (currently at frame %d)."
|
||||
"Performance could be improved by making all images the same pixel format.", filename, i + 1);
|
||||
}
|
||||
if (bm_bitmaps[n].num_mipmaps != bm_bitmaps[n + i].num_mipmaps) {
|
||||
// We found a frame with a different number of mipmaps
|
||||
is_array = false;
|
||||
|
||||
Warning(LOCATION, "Animation '%s' has images that have a different number of mipmaps (currently at frame %d)."
|
||||
"Performance could be improved by giving all frames the same number of mipmaps.", filename, i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Set array flag of first frame
|
||||
bm_bitmaps[n].info.ani.is_array = is_array;
|
||||
|
||||
if (nframes != nullptr)
|
||||
*nframes = anim_frames;
|
||||
|
||||
@@ -1869,14 +1914,15 @@ int bm_load_sub_slow(const char *real_filename, const int num_ext, const char **
|
||||
char full_path[MAX_PATH];
|
||||
size_t size = 0, offset = 0;
|
||||
int rval = -1;
|
||||
const void* file_data = nullptr;
|
||||
|
||||
rval = cf_find_file_location_ext(real_filename, num_ext, ext_list, dir_type, sizeof(full_path) - 1, full_path, &size, &offset, 0);
|
||||
rval = cf_find_file_location_ext(real_filename, num_ext, ext_list, dir_type, sizeof(full_path) - 1, full_path, &size, &offset, 0, &file_data);
|
||||
|
||||
// could not be found, or is invalid for some reason
|
||||
if ((rval < 0) || (rval >= num_ext))
|
||||
return -1;
|
||||
|
||||
CFILE *test = cfopen_special(full_path, "rb", size, offset, dir_type);
|
||||
CFILE *test = cfopen_special(full_path, "rb", size, offset, file_data, dir_type);
|
||||
|
||||
if (test != NULL) {
|
||||
if (img_cfp != NULL)
|
||||
@@ -1893,7 +1939,7 @@ bitmap * bm_lock(int handle, int bpp, ubyte flags, bool nodebug) {
|
||||
bitmap *bmp;
|
||||
bitmap_entry *be;
|
||||
|
||||
if (!bm_inited) bm_init();
|
||||
Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
|
||||
|
||||
int bitmapnum = handle % MAX_BITMAPS;
|
||||
|
||||
@@ -2235,6 +2281,9 @@ void bm_lock_jpg(int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, int b
|
||||
// Unload any existing data
|
||||
bm_free_data(bitmapnum);
|
||||
|
||||
// JPEG actually only support 24 bits per pixel so we enforce that here
|
||||
bpp = 24;
|
||||
|
||||
d_size = (bpp >> 3);
|
||||
|
||||
// allocate bitmap data
|
||||
@@ -2295,7 +2344,7 @@ void bm_lock_pcx(int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, int b
|
||||
// this will populate filename[] whether it's EFF or not
|
||||
EFF_FILENAME_CHECK;
|
||||
|
||||
pcx_error = pcx_read_bitmap(filename, data, NULL, (bpp >> 3), (flags & BMP_AABITMAP), 0, be->dir_type);
|
||||
pcx_error = pcx_read_bitmap(filename, data, NULL, (bpp >> 3), (flags & BMP_AABITMAP), (flags & BMP_MASK_BITMAP) != 0, be->dir_type);
|
||||
|
||||
if (pcx_error != PCX_ERROR_NONE) {
|
||||
mprintf(("Couldn't load PCX!!! (%s)\n", filename));
|
||||
@@ -2374,7 +2423,7 @@ void bm_lock_tga(int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, int b
|
||||
Assert(byte_size);
|
||||
Assert(be->mem_taken > 0);
|
||||
|
||||
data = (ubyte*)bm_malloc(bitmapnum, be->mem_taken);
|
||||
data = (ubyte*)bm_malloc(bitmapnum, static_cast<size_t>(bmp->w * bmp->h * byte_size));
|
||||
|
||||
if (data) {
|
||||
memset(data, 0, be->mem_taken);
|
||||
@@ -2459,8 +2508,7 @@ int bm_make_render_target(int width, int height, int flags) {
|
||||
int bpp = 32;
|
||||
int size = 0;
|
||||
|
||||
if (!bm_inited)
|
||||
bm_init();
|
||||
Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
|
||||
|
||||
// Find an open slot (starting from the end)
|
||||
for (n = -1, i = MAX_BITMAPS - 1; i >= 0; i--) {
|
||||
@@ -2833,12 +2881,16 @@ int bm_release(int handle, int clear_render_targets) {
|
||||
}
|
||||
|
||||
// be sure that all frames of an ani are unloaded - taylor
|
||||
if (bm_is_anim(n) == true) {
|
||||
if (bm_is_anim(n)) {
|
||||
int i, first = be->info.ani.first_frame, total = bm_bitmaps[first].info.ani.num_frames;
|
||||
|
||||
// The graphics system requires that the bitmap information for the whole animation is still valid when the data
|
||||
// is freed so we first free all the data and then clear the bitmaps data
|
||||
for (i = 0; i < total; i++) {
|
||||
bm_free_data(first + i, true); // clears flags, bbp, data, etc
|
||||
}
|
||||
|
||||
for (i = 0; i < total; i++) {
|
||||
memset(&bm_bitmaps[first + i], 0, sizeof(bitmap_entry));
|
||||
|
||||
bm_bitmaps[first + i].type = BM_TYPE_NONE;
|
||||
@@ -2887,8 +2939,7 @@ int bm_release(int handle, int clear_render_targets) {
|
||||
}
|
||||
|
||||
int bm_reload(int bitmap_handle, const char* filename) {
|
||||
if (!bm_inited)
|
||||
bm_init();
|
||||
Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
|
||||
|
||||
// if no file was passed then get out now
|
||||
if ((filename == NULL) || (strlen(filename) <= 0))
|
||||
@@ -3068,10 +3119,7 @@ bool bm_set_render_target(int handle, int face) {
|
||||
|
||||
gr_reset_clip();
|
||||
|
||||
if (gr_screen.mode == GR_OPENGL) {
|
||||
extern void opengl_setup_viewport();
|
||||
opengl_setup_viewport();
|
||||
}
|
||||
gr_setup_viewport();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -3201,7 +3249,7 @@ int bm_unload_fast(int handle, int clear_render_targets) {
|
||||
void bm_unlock(int handle) {
|
||||
bitmap_entry *be;
|
||||
|
||||
if (!bm_inited) bm_init();
|
||||
Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
|
||||
|
||||
int bitmapnum = handle % MAX_BITMAPS;
|
||||
|
||||
@@ -3257,3 +3305,118 @@ int find_block_of(int n)
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool bm_is_texture_array(const int handle) {
|
||||
int bitmapnum = handle % MAX_BITMAPS;
|
||||
|
||||
if (!bm_is_anim(bitmapnum)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// This will return the index of the first animation frame
|
||||
int n = bm_get_cache_slot(handle, 0);
|
||||
bitmap_entry *be = &bm_bitmaps[n];
|
||||
|
||||
return be->info.ani.is_array;
|
||||
}
|
||||
|
||||
int bm_get_base_frame(const int handle, int* num_frames) {
|
||||
if (handle == -1) {
|
||||
if (num_frames != nullptr) {
|
||||
*num_frames = 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If the bitmap is no animation then num_frames will still be at least 1
|
||||
auto animation_begin = bm_get_info(handle, nullptr, nullptr, nullptr, num_frames);
|
||||
|
||||
if (animation_begin < 0) {
|
||||
// Some kind of error
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!bm_is_texture_array(animation_begin)) {
|
||||
// If this is not a texture array then we just treat it like a single frame animation
|
||||
animation_begin = handle;
|
||||
if (num_frames != nullptr) {
|
||||
*num_frames = 1;
|
||||
}
|
||||
}
|
||||
return animation_begin;
|
||||
}
|
||||
int bm_get_array_index(const int handle) {
|
||||
return handle - bm_get_base_frame(handle, nullptr);
|
||||
}
|
||||
|
||||
int bmpman_count_bitmaps() {
|
||||
if (!bm_inited)
|
||||
return -1;
|
||||
|
||||
int total_bitmaps = 0;
|
||||
for (int i = 0; i < MAX_BITMAPS; i++) {
|
||||
if (bm_bitmaps[i].type != BM_TYPE_NONE) {
|
||||
total_bitmaps++;
|
||||
}
|
||||
}
|
||||
|
||||
return total_bitmaps;
|
||||
}
|
||||
|
||||
bool bm_validate_filename(const SCP_string& file, bool single_frame, bool animation) {
|
||||
Assertion(single_frame || animation, "At least one of single_frame or animation must be true!");
|
||||
|
||||
if (!VALID_FNAME(file.c_str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int handle = -1;
|
||||
if (single_frame && animation) {
|
||||
// Caller has indicated that single frames and animations are valid
|
||||
handle = bm_load_either(file.c_str());
|
||||
} else if (single_frame) {
|
||||
handle = bm_load(file);
|
||||
} else {
|
||||
handle = bm_load_animation(file.c_str());
|
||||
}
|
||||
if (handle != -1) {
|
||||
bm_release(handle);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
SDL_Surface* bm_to_sdl_surface(int handle) {
|
||||
Assertion(bm_is_valid(handle), "%d is no valid bitmap handle!", handle);
|
||||
|
||||
int w;
|
||||
int h;
|
||||
|
||||
bm_get_info(handle, &w, &h, nullptr, nullptr);
|
||||
Uint32 rmask, gmask, bmask, amask;
|
||||
|
||||
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
||||
rmask = 0x0000ff00;
|
||||
gmask = 0x00ff0000;
|
||||
bmask = 0xff000000;
|
||||
amask = 0x000000ff;
|
||||
#else
|
||||
rmask = 0x00ff0000;
|
||||
gmask = 0x0000ff00;
|
||||
bmask = 0x000000ff;
|
||||
amask = 0xff000000;
|
||||
#endif
|
||||
|
||||
SDL_Surface* bitmapSurface = SDL_CreateRGBSurface(0, w, h, 32, rmask, gmask, bmask, amask);
|
||||
if (SDL_LockSurface(bitmapSurface) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
bitmap* bmp = bm_lock(handle, 32, BMP_TEX_XPARENT);
|
||||
|
||||
memcpy(bitmapSurface->pixels, reinterpret_cast<void*>(bmp->data), static_cast<size_t>(w * h * 4));
|
||||
|
||||
bm_unlock(handle);
|
||||
SDL_UnlockSurface(bitmapSurface);
|
||||
|
||||
return bitmapSurface;
|
||||
|
||||
}
|
||||
+62
-2
@@ -33,7 +33,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief How many bitmaps the game can handle
|
||||
* @brief How many bitmaps the game can handle by default
|
||||
*
|
||||
* @attention MAX_BITMAPS shouldn't need to be bumped again. With the fixed bm_release() and it's proper use even the
|
||||
* largest missions should stay under this number. With the largest retail missions and wasteful content we should
|
||||
@@ -45,7 +45,9 @@
|
||||
* If anything we could/should reduce MAX_BITMAPS in the future. Where it's at now should accomidate even the
|
||||
* largest mods. -- Taylor
|
||||
*/
|
||||
#define MAX_BITMAPS 4750
|
||||
#define DEFAULT_MAX_BITMAPS 4750
|
||||
|
||||
extern int MAX_BITMAPS;
|
||||
|
||||
// Flag positions for bitmap.flags
|
||||
// ***** NOTE: bitmap.flags is an 8-bit value, no more BMP_TEX_* flags can be added unless the type is changed!! ******
|
||||
@@ -56,6 +58,7 @@
|
||||
#define BMP_TEX_DXT3 (1<<4) //!< dxt3 compressed 8r8g8b4a (32bit)
|
||||
#define BMP_TEX_DXT5 (1<<5) //!< dxt5 compressed 8r8g8b8a (32bit)
|
||||
#define BMP_TEX_CUBEMAP (1<<6) //!< a texture made for cubic environment map
|
||||
#define BMP_MASK_BITMAP (1<<7) //!< a bitmap that will be used for masking mouse interaction. Typically not used in render operations
|
||||
|
||||
// Combined flags
|
||||
#define BMP_TEX_COMP ( BMP_TEX_DXT1 | BMP_TEX_DXT3 | BMP_TEX_DXT5 ) //!< Compressed textures
|
||||
@@ -698,4 +701,61 @@ bool bm_load_and_parse_eff(const char *filename, int dir_type, int *nframes, int
|
||||
*/
|
||||
int bm_get_anim_frame(const int frame1_handle, float elapsed_time, const float divisor = 0.0f, const bool loop = false);
|
||||
|
||||
/**
|
||||
* @brief Determines if the given handle could be used in a texture array
|
||||
*
|
||||
* @param handle The handle to query the value for
|
||||
* @return @c true if all frames have the same size, @c false otherwise. Always returns @c true for single images
|
||||
*/
|
||||
bool bm_is_texture_array(const int handle);
|
||||
|
||||
/**
|
||||
* @brief Gets the base frame of the specified animation
|
||||
*
|
||||
* @details The base frame is the frame of the animation that should be used to determine if two animation frames can be
|
||||
* used in the same draw call. Use this if you want to optimize your draw calls to make use of texture arrays.
|
||||
*
|
||||
* @param handle The handle of the animation to check. This may also be a single frame bitmap
|
||||
* @param num_frames Optionally return the number of frames in the animation
|
||||
* @return The bitmap handle of the base frame or -1 on error
|
||||
*/
|
||||
int bm_get_base_frame(const int handle, int* num_frames = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Get the array index of the specified bitmap
|
||||
*
|
||||
* This should be used when passing the array index to the GPU (e.g. when building uniform structs or streaming particle
|
||||
* data)
|
||||
*
|
||||
* @param handle The handle of the bitmap
|
||||
* @return The index into the array
|
||||
*/
|
||||
int bm_get_array_index(const int handle);
|
||||
|
||||
/**
|
||||
* @brief Counts how many slots are used in bm_bitmaps
|
||||
*
|
||||
* This needs to iterate through all the slots in order to determine whether or not they're used, so shouldn't be used frivolously.
|
||||
*
|
||||
* @return The number of used slots
|
||||
*/
|
||||
int bmpman_count_bitmaps();
|
||||
|
||||
/**
|
||||
* @brief Checks if the given filename is a valid effect or texture file name
|
||||
*
|
||||
* @note At least one of @c single_frame or @c animation must be true since the result would make no sense otherwise. It
|
||||
* is also valid to set both parmeters to @c true.
|
||||
*
|
||||
* @todo This currently just tries to load the file but could be improved by not actually loading the file in the future.
|
||||
*
|
||||
* @param file The file name to check
|
||||
* @param single_frame If set to @c true then single frames are valid files
|
||||
* @param animation If set to @c true then animations are valid files
|
||||
* @return @c true if the file name is valid, @c false otherwise
|
||||
*/
|
||||
bool bm_validate_filename(const SCP_string& file, bool single_frame, bool animation);
|
||||
|
||||
SDL_Surface* bm_to_sdl_surface(int handle);
|
||||
|
||||
#endif
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "globalincs/alphacolors.h"
|
||||
#include "globalincs/linklist.h"
|
||||
#include "globalincs/systemvars.h" //VM_FREECAMERA etc
|
||||
#include "graphics/font.h"
|
||||
#include "graphics/matrix.h"
|
||||
#include "hud/hud.h" //hud_get_draw
|
||||
#include "math/vecmat.h"
|
||||
#include "mod_table/mod_table.h"
|
||||
|
||||
+68
-36
@@ -88,6 +88,7 @@ cf_pathtype Pathtypes[CF_MAX_PATH_TYPES] = {
|
||||
{ CF_TYPE_INTEL_ANIMS, "data" DIR_SEPARATOR_STR "intelanims", ".pcx .ani .eff .tga .jpg .png .dds", CF_TYPE_DATA },
|
||||
{ CF_TYPE_SCRIPTS, "data" DIR_SEPARATOR_STR "scripts", ".lua .lc", CF_TYPE_DATA },
|
||||
{ CF_TYPE_FICTION, "data" DIR_SEPARATOR_STR "fiction", ".txt", CF_TYPE_DATA },
|
||||
{ CF_TYPE_FREDDOCS, "data" DIR_SEPARATOR_STR "freddocs", ".html", CF_TYPE_DATA },
|
||||
};
|
||||
|
||||
|
||||
@@ -109,6 +110,7 @@ static const char *Cfile_cdrom_dir = NULL;
|
||||
static int cfget_cfile_block();
|
||||
static CFILE *cf_open_fill_cfblock(const char* source, int line, FILE * fp, int type);
|
||||
static CFILE *cf_open_packed_cfblock(const char* source, int line, FILE *fp, int type, size_t offset, size_t size);
|
||||
static CFILE *cf_open_memory_fill_cfblock(const char* source, int line, const void* data, size_t size, int dir_type);
|
||||
|
||||
#if defined _WIN32
|
||||
static CFILE *cf_open_mapped_fill_cfblock(const char* source, int line, HANDLE hFile, int type);
|
||||
@@ -763,16 +765,16 @@ CFILE *_cfopen(const char* source, int line, const char *file_path, const char *
|
||||
char copy_file_path[MAX_PATH_LEN]; // FIX change in memory from cf_find_file_location
|
||||
strcpy_s(copy_file_path, file_path);
|
||||
|
||||
|
||||
if ( cf_find_file_location( copy_file_path, dir_type, sizeof(longname) - 1, longname, &size, &offset, localize ) ) {
|
||||
const void* file_data = nullptr;
|
||||
if ( cf_find_file_location( copy_file_path, dir_type, sizeof(longname) - 1, longname, &size, &offset, localize, &file_data ) ) {
|
||||
|
||||
// Fount it, now create a cfile out of it
|
||||
nprintf(("CFileDebug", "Requested file %s found at: %s\n", file_path, longname));
|
||||
|
||||
if ( type & CFILE_MEMORY_MAPPED ) {
|
||||
|
||||
// Can't open memory mapped files out of pack files
|
||||
if ( offset == 0 ) {
|
||||
// Can't open memory mapped files out of pack or memory files
|
||||
if ( offset == 0 && file_data != nullptr ) {
|
||||
#if defined _WIN32
|
||||
HANDLE hFile;
|
||||
|
||||
@@ -790,18 +792,8 @@ CFILE *_cfopen(const char* source, int line, const char *file_path, const char *
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
FILE *fp = fopen( longname, "rb" );
|
||||
|
||||
if ( fp ) {
|
||||
if ( offset ) {
|
||||
// Found it in a pack file
|
||||
return cf_open_packed_cfblock(source, line, fp, dir_type, offset, size );
|
||||
} else {
|
||||
// Found it in a normal file
|
||||
return cf_open_fill_cfblock(source, line, fp, dir_type);
|
||||
}
|
||||
}
|
||||
// since cfopen_special already has all the code to handle the opening we can just use that here
|
||||
return _cfopen_special(source, line, longname, mode, size, offset, file_data, dir_type);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -820,7 +812,7 @@ CFILE *_cfopen(const char* source, int line, const char *file_path, const char *
|
||||
// returns: success ==> address of CFILE structure
|
||||
// error ==> NULL
|
||||
//
|
||||
CFILE *_cfopen_special(const char* source, int line, const char *file_path, const char *mode, const size_t size, const size_t offset, int dir_type)
|
||||
CFILE *_cfopen_special(const char* source, int line, const char *file_path, const char *mode, const size_t size, const size_t offset, const void* data, int dir_type)
|
||||
{
|
||||
if ( !cfile_inited) {
|
||||
Int3();
|
||||
@@ -836,19 +828,27 @@ CFILE *_cfopen_special(const char* source, int line, const char *file_path, cons
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// "file_path" should already be a fully qualified path, so just try to open it
|
||||
FILE *fp = fopen( file_path, "rb" );
|
||||
|
||||
if ( !fp )
|
||||
return NULL;
|
||||
|
||||
if ( offset ) {
|
||||
// it's in a pack file
|
||||
return cf_open_packed_cfblock(source, line, fp, dir_type, offset, size);
|
||||
} else {
|
||||
// it's a normal file
|
||||
return cf_open_fill_cfblock(source, line, fp, dir_type);
|
||||
// In-Memory files are a bit different from normal files so we need to handle them separately
|
||||
if (data != nullptr) {
|
||||
return cf_open_memory_fill_cfblock(source, line, data, size, dir_type);
|
||||
}
|
||||
else {
|
||||
// "file_path" should already be a fully qualified path, so just try to open it
|
||||
FILE *fp = fopen(file_path, "rb");
|
||||
|
||||
if (fp) {
|
||||
if (offset) {
|
||||
// Found it in a pack file
|
||||
return cf_open_packed_cfblock(source, line, fp, dir_type, offset, size);
|
||||
}
|
||||
else {
|
||||
// Found it in a normal file
|
||||
return cf_open_fill_cfblock(source, line, fp, dir_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -922,7 +922,7 @@ int cfclose( CFILE * cfile )
|
||||
cb = &Cfile_block_list[cfile->id];
|
||||
|
||||
result = 0;
|
||||
if ( cb->data ) {
|
||||
if ( cb->data && cb->mem_mapped ) {
|
||||
// close memory mapped file
|
||||
#if defined _WIN32
|
||||
result = UnmapViewOfFile((void*)cb->data);
|
||||
@@ -936,7 +936,8 @@ int cfclose( CFILE * cfile )
|
||||
// FIXME: result is wrong after munmap() but it is successful
|
||||
//result = munmap(cb->data, cb->data_length);
|
||||
//Assert(result);
|
||||
munmap(cb->data, cb->data_length);
|
||||
// This const_cast is safe since the pointer returned by mmap was also non-const
|
||||
munmap(const_cast<void*>(cb->data), cb->data_length);
|
||||
if ( cb->fp != NULL)
|
||||
result = fclose(cb->fp);
|
||||
#endif
|
||||
@@ -996,6 +997,7 @@ static CFILE *cf_open_fill_cfblock(const char* source, int line, FILE *fp, int t
|
||||
cfp->id = cfile_block_index;
|
||||
cfp->version = 0;
|
||||
cfbp->data = NULL;
|
||||
cfbp->mem_mapped = false;
|
||||
cfbp->fp = fp;
|
||||
cfbp->dir_type = type;
|
||||
cfbp->max_read_len = 0;
|
||||
@@ -1038,6 +1040,7 @@ static CFILE *cf_open_packed_cfblock(const char* source, int line, FILE *fp, int
|
||||
cfp->version = 0;
|
||||
cfbp->data = NULL;
|
||||
cfbp->fp = fp;
|
||||
cfbp->mem_mapped = false;
|
||||
cfbp->dir_type = type;
|
||||
cfbp->max_read_len = 0;
|
||||
|
||||
@@ -1083,6 +1086,7 @@ static CFILE *cf_open_mapped_fill_cfblock(const char* source, int line, FILE *fp
|
||||
cfp->version = 0;
|
||||
cfbp->max_read_len = 0;
|
||||
cfbp->fp = NULL;
|
||||
cfbp->mem_mapped = true;
|
||||
#if defined _WIN32
|
||||
cfbp->hInFile = hFile;
|
||||
#endif
|
||||
@@ -1117,6 +1121,37 @@ static CFILE *cf_open_mapped_fill_cfblock(const char* source, int line, FILE *fp
|
||||
}
|
||||
}
|
||||
|
||||
static CFILE *cf_open_memory_fill_cfblock(const char* source, int line, const void* data, size_t size, int dir_type)
|
||||
{
|
||||
int cfile_block_index;
|
||||
|
||||
cfile_block_index = cfget_cfile_block();
|
||||
if ( cfile_block_index == -1 ) {
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
CFILE *cfp;
|
||||
Cfile_block *cfbp;
|
||||
cfbp = &Cfile_block_list[cfile_block_index];
|
||||
|
||||
cfp = &Cfile_list[cfile_block_index];
|
||||
cfp->id = cfile_block_index;
|
||||
cfp->version = 0;
|
||||
cfbp->max_read_len = 0;
|
||||
cfbp->fp = NULL;
|
||||
cfbp->mem_mapped = false;
|
||||
cfbp->dir_type = dir_type;
|
||||
|
||||
cfbp->source_file = source;
|
||||
cfbp->line_num = line;
|
||||
|
||||
cf_init_lowlevel_read_code(cfp, 0, size, 0 );
|
||||
cfbp->data = data;
|
||||
|
||||
return cfp;
|
||||
}
|
||||
}
|
||||
|
||||
int cf_get_dir_type(CFILE *cfile)
|
||||
{
|
||||
return Cfile_block_list[cfile->id].dir_type;
|
||||
@@ -1127,7 +1162,7 @@ int cf_get_dir_type(CFILE *cfile)
|
||||
//
|
||||
//
|
||||
|
||||
void *cf_returndata(CFILE *cfile)
|
||||
const void *cf_returndata(CFILE *cfile)
|
||||
{
|
||||
Assert(cfile != NULL);
|
||||
Cfile_block *cb;
|
||||
@@ -1418,9 +1453,7 @@ int cfilelength( CFILE * cfile )
|
||||
cb = &Cfile_block_list[cfile->id];
|
||||
|
||||
// TODO: return length of memory mapped file
|
||||
Assert( !cb->data );
|
||||
|
||||
Assert(cb->fp != NULL);
|
||||
Assert( !cb->mem_mapped );
|
||||
|
||||
// cb->size gets set at cfopen
|
||||
|
||||
@@ -1519,7 +1552,6 @@ int cfputc(int c, CFILE *cfile)
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// cfputs() writes a string to a file
|
||||
//
|
||||
// returns: success ==> non-negative value
|
||||
|
||||
+9
-8
@@ -78,8 +78,9 @@ typedef struct {
|
||||
#define CF_TYPE_INTEL_ANIMS 34
|
||||
#define CF_TYPE_SCRIPTS 35
|
||||
#define CF_TYPE_FICTION 36
|
||||
#define CF_TYPE_FREDDOCS 37
|
||||
|
||||
#define CF_MAX_PATH_TYPES 37 // Can be as high as you'd like //DTP; yeah but beware alot of things uses CF_MAX_PATH_TYPES
|
||||
#define CF_MAX_PATH_TYPES 38 // Can be as high as you'd like //DTP; yeah but beware alot of things uses CF_MAX_PATH_TYPES
|
||||
|
||||
|
||||
// TRUE if type is specified and valid
|
||||
@@ -139,7 +140,7 @@ CFILE *_cfopen(const char* source_file, int line, const char *filename, const ch
|
||||
// like cfopen(), but it accepts a fully qualified path only (ie, the result of a cf_find_file_location() call)
|
||||
// NOTE: only supports reading files!!
|
||||
CFILE *_cfopen_special(const char* source_file, int line, const char *file_path, const char *mode,
|
||||
const size_t size, const size_t offset, int dir_type = CF_TYPE_ANY);
|
||||
const size_t size, const size_t offset, const void* data, int dir_type = CF_TYPE_ANY);
|
||||
#define cfopen_special(...) _cfopen_special(LOCATION, __VA_ARGS__) // Pass source location to the function
|
||||
|
||||
// Flush the open file buffer
|
||||
@@ -197,9 +198,9 @@ int cfwrite_compressed(void *param_buf, int param_elsize, int param_nelem, CFILE
|
||||
int cfseek(CFILE *fp, int offset, int where);
|
||||
|
||||
// Returns current position of file.
|
||||
int cftell(CFILE *fp);
|
||||
|
||||
// cfputc() writes a character to a file
|
||||
int cftell(CFILE *fp);
|
||||
|
||||
// cfputc() writes a character to a file
|
||||
int cfputc(int c, CFILE *cfile);
|
||||
|
||||
// cfputs() writes a string to a file
|
||||
@@ -215,7 +216,7 @@ char *cfgets(char *buf, int n, CFILE *cfile);
|
||||
int cfeof(CFILE *cfile);
|
||||
|
||||
// Return the data pointer associated with the CFILE structure (for memory mapped files)
|
||||
void *cf_returndata(CFILE *cfile);
|
||||
const void *cf_returndata(CFILE *cfile);
|
||||
|
||||
// get the 2 byte checksum of the passed filename - return 0 if operation failed, 1 if succeeded
|
||||
int cf_chksum_short(const char *filename, ushort *chksum, int max_size = -1, int cf_type = CF_TYPE_ANY );
|
||||
@@ -325,7 +326,7 @@ void cf_sort_filenames( SCP_vector<SCP_string> &list, int sort, SCP_vector<file_
|
||||
// size - File size
|
||||
// offset - Offset into pack file. 0 if not a packfile.
|
||||
// Returns: If not found returns 0.
|
||||
int cf_find_file_location( const char *filespec, int pathtype, int max_out, char *pack_filename, size_t *size, size_t *offset, bool localize = false);
|
||||
int cf_find_file_location( const char *filespec, int pathtype, int max_out, char *pack_filename, size_t *size, size_t *offset, bool localize = false, const void** data_out = nullptr);
|
||||
|
||||
// Searches for a file. Follows all rules and precedence and searches
|
||||
// CD's and pack files. Searches all locations in order for first filename using ext filter list.
|
||||
@@ -339,7 +340,7 @@ int cf_find_file_location( const char *filespec, int pathtype, int max_out, char
|
||||
// offset - Offset into pack file. 0 if not a packfile.
|
||||
// Returns: If not found returns -1, else returns offset into ext_list.
|
||||
// (NOTE: This function is exponentially slow, so don't use it unless truely needed!!)
|
||||
int cf_find_file_location_ext(const char *filename, const int ext_num, const char **ext_list, int pathtype, int max_out = 0, char *pack_filename = NULL, size_t *size = NULL, size_t *offset = NULL, bool localize = false);
|
||||
int cf_find_file_location_ext(const char *filename, const int ext_num, const char **ext_list, int pathtype, int max_out = 0, char *pack_filename = NULL, size_t *size = NULL, size_t *offset = NULL, bool localize = false, const void** data_out = nullptr);
|
||||
|
||||
// Functions to change directories
|
||||
int cfile_chdir(const char *dir);
|
||||
|
||||
+56
-38
@@ -74,18 +74,13 @@ int cfeof(CFILE *cfile)
|
||||
Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
|
||||
cb = &Cfile_block_list[cfile->id];
|
||||
|
||||
int result;
|
||||
|
||||
result = 0;
|
||||
|
||||
// cfeof() not supported for memory-mapped files
|
||||
Assert( !cb->data );
|
||||
|
||||
Assert(cb->fp != NULL);
|
||||
int result = 0;
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
auto raw_position = ftell(cb->fp) - cb->lib_offset;
|
||||
Assert(raw_position == cb->raw_position);
|
||||
if (cb->fp) {
|
||||
auto raw_position = ftell(cb->fp) - cb->lib_offset;
|
||||
Assert(raw_position == cb->raw_position);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (cb->raw_position >= cb->size ) {
|
||||
@@ -109,14 +104,11 @@ int cftell( CFILE * cfile )
|
||||
Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
|
||||
cb = &Cfile_block_list[cfile->id];
|
||||
|
||||
// Doesn't work for memory mapped files
|
||||
Assert( !cb->data );
|
||||
|
||||
Assert(cb->fp != NULL);
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
auto raw_position = ftell(cb->fp) - cb->lib_offset;
|
||||
Assert(raw_position == cb->raw_position);
|
||||
if (cb->fp) {
|
||||
auto raw_position = ftell(cb->fp) - cb->lib_offset;
|
||||
Assert(raw_position == cb->raw_position);
|
||||
}
|
||||
#endif
|
||||
|
||||
// The rest of the code still uses ints, do an overflow check to detect cases where this fails
|
||||
@@ -141,8 +133,7 @@ int cfseek( CFILE *cfile, int offset, int where )
|
||||
|
||||
|
||||
// TODO: seek to offset in memory mapped file
|
||||
Assert( !cb->data );
|
||||
Assert( cb->fp != NULL );
|
||||
Assert( !cb->mem_mapped );
|
||||
|
||||
size_t goal_position;
|
||||
|
||||
@@ -166,14 +157,22 @@ int cfseek( CFILE *cfile, int offset, int where )
|
||||
// Make sure we don't seek beyond the end of the file
|
||||
CAP(goal_position, cb->lib_offset, cb->lib_offset + cb->size);
|
||||
|
||||
int result = fseek(cb->fp, (long)goal_position, SEEK_SET );
|
||||
Assertion(goal_position >= cb->lib_offset, "Invalid offset values detected while seeking! Goal was " SIZE_T_ARG ", lib_offset is " SIZE_T_ARG ".", goal_position, cb->lib_offset);
|
||||
int result = 0;
|
||||
|
||||
if (cb->fp) {
|
||||
// If we have a file pointer we can also seek in that file
|
||||
result = fseek(cb->fp, (long)goal_position, SEEK_SET );
|
||||
Assertion(goal_position >= cb->lib_offset, "Invalid offset values detected while seeking! Goal was " SIZE_T_ARG ", lib_offset is " SIZE_T_ARG ".", goal_position, cb->lib_offset);
|
||||
}
|
||||
// If we only have a data pointer this will do all the work
|
||||
cb->raw_position = goal_position - cb->lib_offset;
|
||||
Assertion(cb->raw_position <= cb->size, "Invalid raw_position value detected!");
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
auto tmp_offset = ftell(cb->fp) - cb->lib_offset;
|
||||
Assert(tmp_offset==cb->raw_position);
|
||||
if (cb->fp) {
|
||||
auto tmp_offset = ftell(cb->fp) - cb->lib_offset;
|
||||
Assert(tmp_offset == cb->raw_position);
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
@@ -197,13 +196,6 @@ int cfread(void *buf, int elsize, int nelem, CFILE *cfile)
|
||||
|
||||
Cfile_block *cb = &Cfile_block_list[cfile->id];
|
||||
|
||||
// cfread() not supported for memory-mapped files
|
||||
if(cb->data != NULL)
|
||||
{
|
||||
Warning(LOCATION, "Writing is not supported for mem-mapped files");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( (cb->raw_position+size) > cb->size ) {
|
||||
Assertion(cb->raw_position <= cb->size, "Invalid raw_position value detected!");
|
||||
size = cb->size - cb->raw_position;
|
||||
@@ -222,15 +214,24 @@ int cfread(void *buf, int elsize, int nelem, CFILE *cfile)
|
||||
}
|
||||
}
|
||||
|
||||
size_t bytes_read = fread( buf, 1, size, cb->fp );
|
||||
size_t bytes_read;
|
||||
if (cb->data != nullptr) {
|
||||
// This is a file from memory
|
||||
bytes_read = size;
|
||||
memcpy(buf, reinterpret_cast<const char*>(cb->data) + cb->raw_position, size);
|
||||
} else {
|
||||
bytes_read = fread( buf, 1, size, cb->fp );
|
||||
}
|
||||
if ( bytes_read > 0 ) {
|
||||
cb->raw_position += bytes_read;
|
||||
Assertion(cb->raw_position <= cb->size, "Invalid raw_position value detected!");
|
||||
}
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
auto tmp_offset = ftell(cb->fp) - cb->lib_offset;
|
||||
Assert(tmp_offset==cb->raw_position);
|
||||
if (cb->fp) {
|
||||
auto tmp_offset = ftell(cb->fp) - cb->lib_offset;
|
||||
Assert(tmp_offset==cb->raw_position);
|
||||
}
|
||||
#endif
|
||||
|
||||
return (int)(bytes_read / elsize);
|
||||
@@ -254,14 +255,31 @@ int cfread_lua_number(double *buf, CFILE *cfile)
|
||||
return 0;
|
||||
}
|
||||
|
||||
long orig_pos = ftell(cb->fp);
|
||||
int items_read = fscanf(cb->fp, LUA_NUMBER_SCAN, buf);
|
||||
cb->raw_position += ftell(cb->fp)-orig_pos;
|
||||
size_t advance = 0;
|
||||
int items_read;
|
||||
if (cb->fp) {
|
||||
long orig_pos = ftell(cb->fp);
|
||||
items_read = fscanf(cb->fp, LUA_NUMBER_SCAN, buf);
|
||||
advance = (size_t) (ftell(cb->fp)-orig_pos);
|
||||
} else {
|
||||
int read;
|
||||
// %n returns the number of bytes currently read so we append that to the scan format at the end so it will return
|
||||
// how many bytes we have consumed
|
||||
items_read = sscanf(reinterpret_cast<const char*>(cb->data), LUA_NUMBER_SCAN "%n", buf, &read);
|
||||
if (items_read == 2) {
|
||||
// We need to correct the items read counter since we read one additional item
|
||||
items_read = 1;
|
||||
}
|
||||
advance = (size_t) read;
|
||||
}
|
||||
cb->raw_position += advance;
|
||||
Assertion(cb->raw_position <= cb->size, "Invalid raw_position value detected!");
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
auto tmp_offset = ftell(cb->fp) - cb->lib_offset;
|
||||
Assert(tmp_offset==cb->raw_position);
|
||||
if (cb->fp) {
|
||||
auto tmp_offset = ftell(cb->fp) - cb->lib_offset;
|
||||
Assert(tmp_offset==cb->raw_position);
|
||||
}
|
||||
#endif
|
||||
|
||||
return items_read;
|
||||
|
||||
@@ -26,7 +26,8 @@ typedef struct Cfile_block {
|
||||
int type; // CFILE_BLOCK_UNUSED, CFILE_BLOCK_USED
|
||||
int dir_type; // directory location
|
||||
FILE *fp; // File pointer if opening an individual file
|
||||
void *data; // Pointer for memory-mapped file access. NULL if not mem-mapped.
|
||||
const void *data; // Pointer for memory-mapped file access. NULL if not mem-mapped.
|
||||
bool mem_mapped; // Flag for memory mapped files (if data is not null and this is false it means that it's an embedded file)
|
||||
#ifdef _WIN32
|
||||
HANDLE hInFile; // Handle from CreateFile()
|
||||
HANDLE hMapFile; // Handle from CreateFileMapping()
|
||||
|
||||
+146
-29
@@ -37,12 +37,16 @@
|
||||
#include "cfile/cfilesystem.h"
|
||||
#include "cmdline/cmdline.h"
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "def_files/def_files.h"
|
||||
#include "localization/localize.h"
|
||||
#include "osapi/osapi.h"
|
||||
#include "parse/parselo.h"
|
||||
|
||||
#define CF_ROOTTYPE_PATH 0
|
||||
#define CF_ROOTTYPE_PACK 1
|
||||
enum CfileRootType {
|
||||
CF_ROOTTYPE_PATH = 0,
|
||||
CF_ROOTTYPE_PACK = 1,
|
||||
CF_ROOTTYPE_MEMORY = 2,
|
||||
};
|
||||
|
||||
// for a defined and specifically set location to get/send pilot/campaign files
|
||||
SCP_string Pilot_file_path;
|
||||
@@ -53,8 +57,8 @@ SCP_string Pilot_file_path;
|
||||
// specifying cd-rom tree
|
||||
// searching for pack files on CD-rom tree
|
||||
typedef struct cf_root {
|
||||
char path[CF_MAX_PATHNAME_LENGTH]; // Contains something like c:\projects\freespace or c:\projects\freespace\freespace.vp
|
||||
int roottype; // CF_ROOTTYPE_PATH = Path, CF_ROOTTYPE_PACK =Pack file
|
||||
char path[CF_MAX_PATHNAME_LENGTH]; // Contains something like c:\projects\freespace or c:\projects\freespace\freespace.vp
|
||||
int roottype; // CF_ROOTTYPE_PATH = Path, CF_ROOTTYPE_PACK =Pack file, CF_ROOTTYPE_MEMORY=In memory
|
||||
} cf_root;
|
||||
|
||||
// convenient type for sorting (see cf_build_pack_list())
|
||||
@@ -79,13 +83,14 @@ static int Num_path_roots = 0;
|
||||
|
||||
// Created by searching all roots in order. This means Files is then sorted by precedence.
|
||||
typedef struct cf_file {
|
||||
char name_ext[CF_MAX_FILENAME_LENGTH]; // Filename and extension
|
||||
int root_index; // Where in Roots this is located
|
||||
int pathtype_index; // Where in Paths this is located
|
||||
time_t write_time; // When it was last written
|
||||
int size; // How big it is in bytes
|
||||
int pack_offset; // For pack files, where it is at. 0 if not in a pack file. This can be used to tell if in a pack file.
|
||||
char* real_name; // For real files, the full path
|
||||
char name_ext[CF_MAX_FILENAME_LENGTH]; // Filename and extension
|
||||
int root_index; // Where in Roots this is located
|
||||
int pathtype_index; // Where in Paths this is located
|
||||
time_t write_time; // When it was last written
|
||||
int size; // How big it is in bytes
|
||||
int pack_offset; // For pack files, where it is at. 0 if not in a pack file. This can be used to tell if in a pack file.
|
||||
char* real_name; // For real files, the full path
|
||||
const void* data; // For in-memory files, the data pointer
|
||||
} cf_file;
|
||||
|
||||
#define CF_NUM_FILES_PER_BLOCK 512
|
||||
@@ -510,6 +515,11 @@ void cf_build_root_list(const char *cdrom_dir)
|
||||
|
||||
}
|
||||
|
||||
// The final root is the in-memory root
|
||||
root = cf_create_root();
|
||||
memset(root->path, 0, sizeof(root->path));
|
||||
root->roottype = CF_ROOTTYPE_MEMORY;
|
||||
|
||||
}
|
||||
|
||||
// Given a lower case list of file extensions
|
||||
@@ -836,7 +846,48 @@ void cf_search_root_pack(int root_index)
|
||||
mprintf(( "%i files\n", num_files ));
|
||||
}
|
||||
|
||||
void cf_search_memory_root(int root_index) {
|
||||
int num_files = 0;
|
||||
mprintf(( "Searching memory root ... " ));
|
||||
|
||||
auto default_files = defaults_get_all();
|
||||
for (auto& default_file : default_files) {
|
||||
// Pure built in files have an empty path_type string
|
||||
if (strlen(default_file.path_type) <= 0) {
|
||||
// Ignore pure builtin files. They should only be accessible with defaults_get_file
|
||||
continue;
|
||||
}
|
||||
|
||||
int pathtype = -1;
|
||||
for (int i = 0; i < CF_MAX_PATH_TYPES; ++i) {
|
||||
if (Pathtypes[i].path == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(Pathtypes[i].path, default_file.path_type)) {
|
||||
pathtype = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Assertion(pathtype != -1, "Default file '%s%s%s' does not use a valid path type!",
|
||||
default_file.path_type,
|
||||
DIR_SEPARATOR_STR,
|
||||
default_file.filename);
|
||||
|
||||
cf_file *file = cf_create_file();
|
||||
|
||||
strcpy_s( file->name_ext, default_file.filename );
|
||||
file->root_index = root_index;
|
||||
file->pathtype_index = pathtype;
|
||||
file->write_time = time(nullptr); // Just assume that memory files were last written to just now
|
||||
file->size = (int)default_file.size;
|
||||
file->data = default_file.data;
|
||||
|
||||
num_files++;
|
||||
}
|
||||
|
||||
mprintf(( "%i files\n", num_files ));
|
||||
}
|
||||
|
||||
void cf_build_file_list()
|
||||
{
|
||||
@@ -851,6 +902,8 @@ void cf_build_file_list()
|
||||
cf_search_root_path(i);
|
||||
} else if ( root->roottype == CF_ROOTTYPE_PACK ) {
|
||||
cf_search_root_pack(i);
|
||||
} else if (root->roottype == CF_ROOTTYPE_MEMORY) {
|
||||
cf_search_memory_root(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -932,7 +985,7 @@ void cf_free_secondary_filelist()
|
||||
*
|
||||
* @return If not found returns 0.
|
||||
*/
|
||||
int cf_find_file_location( const char *filespec, int pathtype, int max_out, char *pack_filename, size_t *size, size_t *offset, bool localize )
|
||||
int cf_find_file_location( const char *filespec, int pathtype, int max_out, char *pack_filename, size_t *size, size_t *offset, bool localize, const void** data_out )
|
||||
{
|
||||
int i;
|
||||
uint ui;
|
||||
@@ -1067,8 +1120,16 @@ int cf_find_file_location( const char *filespec, int pathtype, int max_out, char
|
||||
if (offset)
|
||||
*offset = (size_t)f->pack_offset;
|
||||
|
||||
if (data_out)
|
||||
*data_out = f->data;
|
||||
|
||||
if (pack_filename) {
|
||||
if (f->pack_offset < 1) {
|
||||
if (f->data != nullptr) {
|
||||
// This is an in-memory file so we just copy the pathtype name + file name
|
||||
strncpy(pack_filename, Pathtypes[f->pathtype_index].path, max_out);
|
||||
strncat(pack_filename, DIR_SEPARATOR_STR, max_out);
|
||||
strncat(pack_filename, f->name_ext, max_out);
|
||||
} else if (f->pack_offset < 1) {
|
||||
// This is a real file, return the actual file path
|
||||
strncpy( pack_filename, f->real_name, max_out );
|
||||
} else {
|
||||
@@ -1092,8 +1153,16 @@ int cf_find_file_location( const char *filespec, int pathtype, int max_out, char
|
||||
if (offset)
|
||||
*offset = (size_t)f->pack_offset;
|
||||
|
||||
if (data_out)
|
||||
*data_out = f->data;
|
||||
|
||||
if (pack_filename) {
|
||||
if (f->pack_offset < 1) {
|
||||
if (f->data != nullptr) {
|
||||
// This is an in-memory file so we just copy the pathtype name + file name
|
||||
strncpy(pack_filename, Pathtypes[f->pathtype_index].path, max_out);
|
||||
strncat(pack_filename, DIR_SEPARATOR_STR, max_out);
|
||||
strncat(pack_filename, f->name_ext, max_out);
|
||||
} else if (f->pack_offset < 1) {
|
||||
// This is a real file, return the actual file path
|
||||
strncpy( pack_filename, f->real_name, max_out );
|
||||
} else {
|
||||
@@ -1132,7 +1201,7 @@ extern char *stristr(char *str, const char *substr);
|
||||
*
|
||||
* @return If not found returns -1, else returns offset into ext_list.
|
||||
*/
|
||||
int cf_find_file_location_ext( const char *filename, const int ext_num, const char **ext_list, int pathtype, int max_out, char *pack_filename, size_t *size, size_t *offset, bool localize )
|
||||
int cf_find_file_location_ext( const char *filename, const int ext_num, const char **ext_list, int pathtype, int max_out, char *pack_filename, size_t *size, size_t *offset, bool localize, const void** data_out)
|
||||
{
|
||||
int cur_ext, i;
|
||||
uint ui;
|
||||
@@ -1217,6 +1286,9 @@ int cf_find_file_location_ext( const char *filename, const int ext_num, const ch
|
||||
if (size)
|
||||
*size = findstruct.size;
|
||||
|
||||
if (data_out)
|
||||
*data_out = nullptr;
|
||||
|
||||
_findclose(findhandle);
|
||||
|
||||
if (offset)
|
||||
@@ -1235,6 +1307,9 @@ int cf_find_file_location_ext( const char *filename, const int ext_num, const ch
|
||||
if (size)
|
||||
*size = filelength( fileno(fp) );
|
||||
|
||||
if (data_out)
|
||||
*data_out = nullptr;
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if (offset)
|
||||
@@ -1334,15 +1409,25 @@ int cf_find_file_location_ext( const char *filename, const int ext_num, const ch
|
||||
if (offset)
|
||||
*offset = f->pack_offset;
|
||||
|
||||
if (data_out)
|
||||
*data_out = f->data;
|
||||
|
||||
if (pack_filename) {
|
||||
if (f->pack_offset < 1) {
|
||||
if (f->data != nullptr) {
|
||||
// This is an in-memory file so we just copy the pathtype name + file name
|
||||
strncpy(pack_filename, Pathtypes[f->pathtype_index].path, max_out);
|
||||
strncat(pack_filename, DIR_SEPARATOR_STR, max_out);
|
||||
strncat(pack_filename, f->name_ext, max_out);
|
||||
}
|
||||
else if (f->pack_offset < 1) {
|
||||
// This is a real file, return the actual file path
|
||||
strncpy( pack_filename, f->real_name, max_out );
|
||||
} else {
|
||||
strncpy(pack_filename, f->real_name, max_out);
|
||||
}
|
||||
else {
|
||||
// File is in a pack file
|
||||
cf_root *r = cf_get_root(f->root_index);
|
||||
|
||||
strncpy( pack_filename, r->path, max_out );
|
||||
strncpy(pack_filename, r->path, max_out);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1362,15 +1447,25 @@ int cf_find_file_location_ext( const char *filename, const int ext_num, const ch
|
||||
if (offset)
|
||||
*offset = f->pack_offset;
|
||||
|
||||
if (data_out)
|
||||
*data_out = f->data;
|
||||
|
||||
if (pack_filename) {
|
||||
if (f->pack_offset < 1) {
|
||||
if (f->data != nullptr) {
|
||||
// This is an in-memory file so we just copy the pathtype name + file name
|
||||
strncpy(pack_filename, Pathtypes[f->pathtype_index].path, max_out);
|
||||
strncat(pack_filename, DIR_SEPARATOR_STR, max_out);
|
||||
strncat(pack_filename, f->name_ext, max_out);
|
||||
}
|
||||
else if (f->pack_offset < 1) {
|
||||
// This is a real file, return the actual file path
|
||||
strncpy( pack_filename, f->real_name, max_out );
|
||||
} else {
|
||||
strncpy(pack_filename, f->real_name, max_out);
|
||||
}
|
||||
else {
|
||||
// File is in a pack file
|
||||
cf_root *r = cf_get_root(f->root_index);
|
||||
|
||||
strncpy( pack_filename, r->path, max_out );
|
||||
strncpy(pack_filename, r->path, max_out);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1432,6 +1527,7 @@ int cf_matches_spec(const char *filespec, const char *filename)
|
||||
int (*Get_file_list_filter)(const char *filename) = NULL;
|
||||
const char *Get_file_list_child = NULL;
|
||||
int Skip_packfile_search = 0;
|
||||
bool Skip_memory_files = false;
|
||||
|
||||
static bool verify_file_list_child()
|
||||
{
|
||||
@@ -1567,8 +1663,10 @@ int cf_get_file_list( SCP_vector<SCP_string> &list, int pathtype, const char *fi
|
||||
continue;
|
||||
|
||||
char fn[MAX_PATH];
|
||||
snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
|
||||
fn[MAX_PATH-1] = 0;
|
||||
if (snprintf(fn, MAX_PATH, "%s/%s", filespec, dir->d_name) >= MAX_PATH) {
|
||||
// Make sure the string is null terminated
|
||||
fn[MAX_PATH-1] = 0;
|
||||
}
|
||||
|
||||
struct stat buf;
|
||||
if (stat(fn, &buf) == -1) {
|
||||
@@ -1633,6 +1731,11 @@ int cf_get_file_list( SCP_vector<SCP_string> &list, int pathtype, const char *fi
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Skip_memory_files && f->data != nullptr) {
|
||||
// If we want to skip memory files and this is a memory file then ignore it
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
|
||||
//mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
|
||||
|
||||
@@ -1776,8 +1879,10 @@ int cf_get_file_list( int max, char **list, int pathtype, const char *filter, in
|
||||
continue;
|
||||
|
||||
char fn[MAX_PATH];
|
||||
snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
|
||||
fn[MAX_PATH-1] = 0;
|
||||
if (snprintf(fn, MAX_PATH, "%s/%s", filespec, dir->d_name) >= MAX_PATH) {
|
||||
// Make sure the string is null terminated
|
||||
fn[MAX_PATH-1] = 0;
|
||||
}
|
||||
|
||||
struct stat buf;
|
||||
if (stat(fn, &buf) == -1) {
|
||||
@@ -1843,6 +1948,11 @@ int cf_get_file_list( int max, char **list, int pathtype, const char *filter, in
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Skip_memory_files && f->data != nullptr) {
|
||||
// If we want to skip memory files and this is a memory file then ignore it
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
|
||||
|
||||
//mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
|
||||
@@ -1991,8 +2101,10 @@ int cf_get_file_list_preallocated( int max, char arr[][MAX_FILENAME_LEN], char *
|
||||
continue;
|
||||
|
||||
char fn[MAX_PATH];
|
||||
snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
|
||||
fn[MAX_PATH-1] = 0;
|
||||
if (snprintf(fn, MAX_PATH, "%s/%s", filespec, dir->d_name) >= MAX_PATH) {
|
||||
// Make sure the string is null terminated
|
||||
fn[MAX_PATH-1] = 0;
|
||||
}
|
||||
|
||||
struct stat buf;
|
||||
if (stat(fn, &buf) == -1) {
|
||||
@@ -2061,6 +2173,11 @@ int cf_get_file_list_preallocated( int max, char arr[][MAX_FILENAME_LEN], char *
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Skip_memory_files && f->data != nullptr) {
|
||||
// If we want to skip memory files and this is a memory file then ignore it
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
|
||||
|
||||
//mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
|
||||
|
||||
+317
-85
@@ -23,6 +23,9 @@
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "osapi/osapi.h"
|
||||
#include "cfile/cfilesystem.h"
|
||||
#include "sound/speech.h"
|
||||
#include "sound/openal.h"
|
||||
#include "io/joy.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
@@ -38,6 +41,9 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <cstdio>
|
||||
|
||||
#include <jansson.h>
|
||||
|
||||
// Stupid windows workaround...
|
||||
#ifdef MessageBox
|
||||
@@ -60,6 +66,11 @@ const char *cmdline_arg_types[] =
|
||||
"STRING",
|
||||
};
|
||||
|
||||
enum class flag_output_type {
|
||||
Binary,
|
||||
Json_V1,
|
||||
};
|
||||
|
||||
// variables
|
||||
class cmdline_parm {
|
||||
public:
|
||||
@@ -78,6 +89,7 @@ public:
|
||||
float get_float();
|
||||
char *str();
|
||||
bool check_if_args_is_valid();
|
||||
bool has_param();
|
||||
};
|
||||
|
||||
static cmdline_parm Parm_list(NULL, NULL, AT_NONE);
|
||||
@@ -148,7 +160,6 @@ Flag exe_params[] =
|
||||
{ "-noenv", "Disable environment maps", true, EASY_DEFAULT_MEM, EASY_MEM_ALL_ON, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-env", },
|
||||
{ "-nomotiondebris", "Disable motion debris", true, EASY_ALL_ON, EASY_DEFAULT, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-nomotiondebris",},
|
||||
{ "-noscalevid", "Disable scale-to-window for movies", true, 0, EASY_DEFAULT, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-noscalevid", },
|
||||
{ "-missile_lighting", "Apply lighting to missiles" , true, EASY_ALL_ON, EASY_DEFAULT, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-missile_lighting", },
|
||||
{ "-nonormal", "Disable normal maps", true, EASY_DEFAULT_MEM, EASY_MEM_ALL_ON, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-normal" },
|
||||
{ "-no_emissive_light", "Disable emissive light from ships", true, 0, EASY_DEFAULT, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-no_emissive_light" },
|
||||
{ "-noheight", "Disable height/parallax maps", true, EASY_DEFAULT_MEM, EASY_MEM_ALL_ON, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-height" },
|
||||
@@ -177,7 +188,6 @@ Flag exe_params[] =
|
||||
{ "-no_ap_interrupt", "Disable interrupting autopilot", true, 0, EASY_DEFAULT, "Gameplay", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-no_ap_interrupt", },
|
||||
{ "-stretch_menu", "Stretch interface to fill screen", true, 0, EASY_DEFAULT, "Gameplay", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-stretch_menu", },
|
||||
|
||||
{ "-snd_preload", "Preload mission game sounds", true, EASY_MEM_ALL_ON, EASY_DEFAULT_MEM, "Audio", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-snd_preload", },
|
||||
{ "-nosound", "Disable all sound", false, 0, EASY_DEFAULT, "Audio", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-nosound", },
|
||||
{ "-nomusic", "Disable music", false, 0, EASY_DEFAULT, "Audio", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-nomusic", },
|
||||
{ "-no_enhanced_sound", "Disable enhanced sound", false, 0, EASY_DEFAULT, "Audio", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-no_enhanced_sound", },
|
||||
@@ -205,11 +215,11 @@ Flag exe_params[] =
|
||||
{ "-use_gldrawelements","Don't use glDrawRangeElements", true, 0, EASY_DEFAULT, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-use_gldrawelements", },
|
||||
{ "-old_collision", "Use old collision detection system", true, EASY_DEFAULT, EASY_ALL_ON, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-old_collision", },
|
||||
{ "-gl_finish", "Fix input lag on some ATI+Linux systems", true, 0, EASY_DEFAULT, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-gl_finish", },
|
||||
{ "-no_batching", "Disable batched model rendering", true, 0, EASY_DEFAULT, "Troubleshoot", "", },
|
||||
{ "-no_geo_effects", "Disable geometry shader for effects", true, 0, EASY_DEFAULT, "Troubleshoot", "", },
|
||||
{ "-set_cpu_affinity", "Sets processor affinity to config value", true, 0, EASY_DEFAULT, "Troubleshoot", "", },
|
||||
{ "-nograb", "Disables mouse grabbing", true, 0, EASY_DEFAULT, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-nograb", },
|
||||
{ "-noshadercache", "Disables the shader cache", true, 0, EASY_DEFAULT, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-noshadercache", },
|
||||
{ "-no_bindless_tex", "Disable bindless texturing", true, 0, EASY_DEFAULT, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-no_bindless_tex", },
|
||||
#ifdef WIN32
|
||||
{ "-fix_registry", "Use a different registry path", true, 0, EASY_DEFAULT, "Troubleshoot", "", },
|
||||
#endif
|
||||
@@ -218,6 +228,7 @@ Flag exe_params[] =
|
||||
{ "-voicer", "Enable voice recognition", true, 0, EASY_DEFAULT, "Experimental", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-voicer", },
|
||||
|
||||
{ "-fps", "Show frames per second on HUD", false, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-fps", },
|
||||
{ "-bmpmanusage", "Show how many BMPMAN slots are in use", false, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-bmpmanusage", },
|
||||
{ "-pos", "Show position of camera", false, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-pos", },
|
||||
{ "-window", "Run in window", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-window", },
|
||||
{ "-fullscreen_window", "Run in fullscreen window", false, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-fullscreen_window", },
|
||||
@@ -303,7 +314,6 @@ cmdline_parm spec_static_arg("-spec_static", "Adjusts suns contribution to specu
|
||||
cmdline_parm spec_point_arg("-spec_point", "Adjusts laser weapons contribution to specular highlights", AT_FLOAT);
|
||||
cmdline_parm spec_tube_arg("-spec_tube", "Adjusts beam weapons contribution to specular highlights", AT_FLOAT);
|
||||
cmdline_parm ambient_factor_arg("-ambient_factor", "Adjusts ambient light applied to all parts of a ship", AT_INT); // Cmdline_ambient_factor
|
||||
cmdline_parm missile_lighting_arg("-missile_lighting", NULL, AT_NONE); // Cmdline_missile_lighting
|
||||
cmdline_parm env("-noenv", NULL, AT_NONE); // Cmdline_env
|
||||
cmdline_parm glow_arg("-noglow", NULL, AT_NONE); // Cmdline_glow -- use Bobs glow code
|
||||
cmdline_parm nomotiondebris_arg("-nomotiondebris", NULL, AT_NONE); // Cmdline_nomotiondebris -- Removes those ugly floating rocks -C
|
||||
@@ -321,10 +331,10 @@ cmdline_parm fxaa_preset_arg("-fxaa_preset", "FXAA quality (0-9), requires -post
|
||||
cmdline_parm fb_explosions_arg("-fb_explosions", NULL, AT_NONE);
|
||||
cmdline_parm fb_thrusters_arg("-fb_thrusters", NULL, AT_NONE);
|
||||
cmdline_parm flightshaftsoff_arg("-nolightshafts", NULL, AT_NONE);
|
||||
cmdline_parm no_batching("-no_batching", NULL, AT_NONE);
|
||||
cmdline_parm shadow_quality_arg("-shadow_quality", NULL, AT_INT);
|
||||
cmdline_parm enable_shadows_arg("-enable_shadows", NULL, AT_NONE);
|
||||
cmdline_parm no_deferred_lighting_arg("-no_deferred", NULL, AT_NONE); // Cmdline_no_deferred
|
||||
cmdline_parm anisotropy_level_arg("-anisotropic_filter", NULL, AT_INT);
|
||||
|
||||
float Cmdline_clip_dist = Default_min_draw_distance;
|
||||
float Cmdline_fov = 0.75f;
|
||||
@@ -332,7 +342,6 @@ float Cmdline_ogl_spec = 80.0f;
|
||||
int Cmdline_ambient_factor = 128;
|
||||
int Cmdline_env = 1;
|
||||
int Cmdline_mipmap = 0;
|
||||
int Cmdline_missile_lighting = 0;
|
||||
int Cmdline_glow = 1;
|
||||
int Cmdline_nomotiondebris = 0;
|
||||
int Cmdline_noscalevid = 0;
|
||||
@@ -349,10 +358,10 @@ int Cmdline_fxaa_preset = 6;
|
||||
extern int Fxaa_preset_last_frame;
|
||||
bool Cmdline_fb_explosions = 0;
|
||||
bool Cmdline_fb_thrusters = false;
|
||||
bool Cmdline_no_batching = false;
|
||||
extern bool ls_force_off;
|
||||
int Cmdline_shadow_quality = 0;
|
||||
int Cmdline_no_deferred_lighting = 0;
|
||||
int Cmdline_aniso_level = 0;
|
||||
|
||||
// Game Speed related
|
||||
cmdline_parm cache_bitmaps_arg("-cache_bitmaps", NULL, AT_NONE); // Cmdline_cache_bitmaps
|
||||
@@ -393,11 +402,9 @@ int Cmdline_stretch_menu = 0;
|
||||
|
||||
// Audio related
|
||||
cmdline_parm query_speech_arg("-query_speech", NULL, AT_NONE); // Cmdline_query_speech
|
||||
cmdline_parm snd_preload_arg("-snd_preload", NULL, AT_NONE); // Cmdline_snd_preload
|
||||
cmdline_parm voice_recognition_arg("-voicer", NULL, AT_NONE); // Cmdline_voice_recognition
|
||||
|
||||
int Cmdline_query_speech = 0;
|
||||
int Cmdline_snd_preload = 0; // preload game sounds during mission load
|
||||
int Cmdline_voice_recognition = 0;
|
||||
int Cmdline_no_enhanced_sound = 0;
|
||||
|
||||
@@ -443,6 +450,7 @@ cmdline_parm no_geo_sdr_effects("-no_geo_effects", NULL, AT_NONE);
|
||||
cmdline_parm set_cpu_affinity("-set_cpu_affinity", NULL, AT_NONE);
|
||||
cmdline_parm nograb_arg("-nograb", NULL, AT_NONE);
|
||||
cmdline_parm noshadercache_arg("-noshadercache", NULL, AT_NONE);
|
||||
cmdline_parm no_bindless_tex_arg("-no_bindless_tex", NULL, AT_NONE);
|
||||
#ifdef WIN32
|
||||
cmdline_parm fix_registry("-fix_registry", NULL, AT_NONE);
|
||||
#endif
|
||||
@@ -461,6 +469,7 @@ bool Cmdline_no_geo_sdr_effects = false;
|
||||
bool Cmdline_set_cpu_affinity = false;
|
||||
bool Cmdline_nograb = false;
|
||||
bool Cmdline_noshadercache = false;
|
||||
bool Cmdline_no_bindless_textures = false;
|
||||
#ifdef WIN32
|
||||
bool Cmdline_alternate_registry_path = false;
|
||||
#endif
|
||||
@@ -472,6 +481,7 @@ cmdline_parm dis_weapons("-dis_weapons", NULL, AT_NONE); // Cmdline_dis_weapons
|
||||
cmdline_parm noparseerrors_arg("-noparseerrors", NULL, AT_NONE); // Cmdline_noparseerrors -- turns off parsing errors -C
|
||||
cmdline_parm extra_warn_arg("-extra_warn", "Enable 'extra' warnings", AT_NONE); // Cmdline_extra_warn
|
||||
cmdline_parm fps_arg("-fps", NULL, AT_NONE); // Cmdline_show_fps
|
||||
cmdline_parm bmpmanusage_arg("-bmpmanusage", NULL, AT_NONE); // Cmdline_bmpman_usage
|
||||
cmdline_parm pos_arg("-pos", NULL, AT_NONE); // Cmdline_show_pos
|
||||
cmdline_parm stats_arg("-stats", NULL, AT_NONE); // Cmdline_show_stats
|
||||
cmdline_parm save_render_targets_arg("-save_render_target", NULL, AT_NONE); // Cmdline_save_render_targets
|
||||
@@ -497,11 +507,13 @@ char *Cmdline_start_mission = NULL;
|
||||
int Cmdline_old_collision_sys = 0;
|
||||
int Cmdline_dis_collisions = 0;
|
||||
int Cmdline_dis_weapons = 0;
|
||||
bool Cmdline_output_sexp_info = false;
|
||||
int Cmdline_noparseerrors = 0;
|
||||
#ifdef Allow_NoWarn
|
||||
int Cmdline_nowarn = 0; // turn warnings off in FRED
|
||||
#endif
|
||||
int Cmdline_extra_warn = 0;
|
||||
int Cmdline_bmpman_usage = 0;
|
||||
int Cmdline_show_pos = 0;
|
||||
int Cmdline_show_stats = 0;
|
||||
int Cmdline_save_render_targets = 0;
|
||||
@@ -522,7 +534,7 @@ bool Cmdline_show_video_info = false;
|
||||
bool Cmdline_debug_window = false;
|
||||
|
||||
// Other
|
||||
cmdline_parm get_flags_arg("-get_flags", "Output the launcher flags file", AT_NONE);
|
||||
cmdline_parm get_flags_arg("-get_flags", "Output the launcher flags file", AT_STRING);
|
||||
cmdline_parm output_sexp_arg("-output_sexps", NULL, AT_NONE); //WMC - outputs all SEXPs to sexps.html
|
||||
cmdline_parm output_scripting_arg("-output_scripting", NULL, AT_NONE); //WMC
|
||||
|
||||
@@ -535,6 +547,8 @@ cmdline_parm deprecated_tbp_arg("-tbp", "Deprecated", AT_NONE);
|
||||
cmdline_parm deprecated_jpgtga_arg("-jpgtga", "Deprecated", AT_NONE);
|
||||
cmdline_parm deprecated_htl_arg("-nohtl", "Deprecated", AT_NONE);
|
||||
cmdline_parm deprecated_brieflighting_arg("-brief_lighting", "Deprecated", AT_NONE);
|
||||
cmdline_parm deprecated_sndpreload_arg("-snd_preload", "Deprecated", AT_NONE);
|
||||
cmdline_parm deprecated_missile_lighting_arg("-missile_lighting", "Deprecated", AT_NONE);
|
||||
|
||||
int Cmdline_deprecated_spec = 0;
|
||||
int Cmdline_deprecated_glow = 0;
|
||||
@@ -544,6 +558,7 @@ int Cmdline_deprecated_tbp = 0;
|
||||
int Cmdline_deprecated_jpgtga = 0;
|
||||
int Cmdline_deprecated_nohtl = 0;
|
||||
bool Cmdline_deprecated_brief_lighting = 0;
|
||||
bool Cmdline_deprecated_missile_lighting = false;
|
||||
|
||||
#ifndef NDEBUG
|
||||
// NOTE: this assumes that os_init() has already been called but isn't a fatal error if it hasn't
|
||||
@@ -609,6 +624,11 @@ void cmdline_debug_print_cmdline()
|
||||
{
|
||||
mprintf(("Deprecated flag '-brief_lighting' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
|
||||
if (Cmdline_deprecated_missile_lighting)
|
||||
{
|
||||
mprintf(("Deprecated flag '-missile_lighting' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -987,32 +1007,32 @@ void os_init_cmdline(int argc, char *argv[])
|
||||
delete[] buf;
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// parse user specific cmdline_fso config file (will supersede options in global file)
|
||||
fp = fopen(os_get_config_path("data/cmdline_fso.cfg").c_str(), "rt");
|
||||
|
||||
// parse user specific cmdline_fso config file (will supersede options in global file)
|
||||
fp = fopen(os_get_config_path("data/cmdline_fso.cfg").c_str(), "rt");
|
||||
// if the file exists, get a single line, and deal with it
|
||||
if ( fp ) {
|
||||
char *buf, *p;
|
||||
|
||||
// if the file exists, get a single line, and deal with it
|
||||
if ( fp ) {
|
||||
char *buf, *p;
|
||||
auto len = static_cast<int>(filelength( fileno(fp) )) + 2;
|
||||
buf = new char [len];
|
||||
|
||||
auto len = static_cast<int>(filelength( fileno(fp) )) + 2;
|
||||
buf = new char [len];
|
||||
if (fgets(buf, len-1, fp) != nullptr)
|
||||
{
|
||||
// replace the newline character with a NULL
|
||||
if ( (p = strrchr(buf, '\n')) != NULL ) {
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
if (fgets(buf, len-1, fp) != nullptr)
|
||||
{
|
||||
// replace the newline character with a NULL
|
||||
if ( (p = strrchr(buf, '\n')) != NULL ) {
|
||||
*p = '\0';
|
||||
// append a space for the os_parse_parms() check
|
||||
strcat_s(buf, len, " ");
|
||||
|
||||
os_process_cmdline(buf);
|
||||
}
|
||||
|
||||
// append a space for the os_parse_parms() check
|
||||
strcat_s(buf, len, " ");
|
||||
|
||||
os_process_cmdline(buf);
|
||||
delete [] buf;
|
||||
fclose(fp);
|
||||
}
|
||||
delete [] buf;
|
||||
fclose(fp);
|
||||
}
|
||||
} // If cmdline included PARSE_COMMAND_LINE_STRING
|
||||
|
||||
@@ -1143,6 +1163,9 @@ char *cmdline_parm::str()
|
||||
|
||||
return args;
|
||||
}
|
||||
bool cmdline_parm::has_param() {
|
||||
return args != nullptr;
|
||||
}
|
||||
|
||||
#ifdef SCP_UNIX
|
||||
// Return a vector with all filesystem names of "parent/dir" relative to parent.
|
||||
@@ -1241,6 +1264,248 @@ static void handle_unix_modlist(char **modlist, size_t *len)
|
||||
|
||||
// external entry point into this modules
|
||||
|
||||
static json_t* json_get_v1() {
|
||||
auto root = json_object();
|
||||
|
||||
{
|
||||
auto version_obj = json_object();
|
||||
|
||||
json_object_set_new(version_obj, "full", json_string(FS_VERSION_FULL));
|
||||
json_object_set_new(version_obj, "major", json_integer(FS_VERSION_MAJOR));
|
||||
json_object_set_new(version_obj, "minor", json_integer(FS_VERSION_MINOR));
|
||||
json_object_set_new(version_obj, "build", json_integer(FS_VERSION_BUILD));
|
||||
|
||||
json_object_set_new(version_obj, "has_revision", json_boolean(FS_VERSION_HAS_REVIS));
|
||||
json_object_set_new(version_obj, "revision", json_integer(FS_VERSION_REVIS));
|
||||
json_object_set_new(version_obj, "revision_str", json_string(FS_VERSION_REVIS_STR));
|
||||
|
||||
json_object_set_new(root, "version", version_obj);
|
||||
}
|
||||
{
|
||||
auto easy_array = json_array();
|
||||
|
||||
for (auto& easy_flag : easy_flags) {
|
||||
json_array_append_new(easy_array, json_string(easy_flag.name));
|
||||
}
|
||||
|
||||
json_object_set_new(root, "easy_flags", easy_array);
|
||||
}
|
||||
{
|
||||
auto flags_array = json_array();
|
||||
|
||||
for (auto& flag : exe_params) {
|
||||
auto flag_obj = json_object();
|
||||
|
||||
json_object_set_new(flag_obj, "name", json_string(flag.name));
|
||||
json_object_set_new(flag_obj, "description", json_string(flag.desc));
|
||||
json_object_set_new(flag_obj, "fso_only", json_boolean(flag.fso_only));
|
||||
json_object_set_new(flag_obj, "on_flags", json_integer(flag.on_flags));
|
||||
json_object_set_new(flag_obj, "off_flags", json_integer(flag.off_flags));
|
||||
json_object_set_new(flag_obj, "type", json_string(flag.type));
|
||||
json_object_set_new(flag_obj, "web_url", json_string(flag.web_url));
|
||||
|
||||
json_array_append_new(flags_array, flag_obj);
|
||||
}
|
||||
|
||||
json_object_set_new(root, "flags", flags_array);
|
||||
}
|
||||
{
|
||||
auto caps_array = json_array();
|
||||
|
||||
json_array_append_new(caps_array, json_string("OpenAL"));
|
||||
json_array_append_new(caps_array, json_string("No D3D"));
|
||||
json_array_append_new(caps_array, json_string("New Sound"));
|
||||
json_array_append_new(caps_array, json_string("SDL"));
|
||||
|
||||
json_object_set_new(root, "caps", caps_array);
|
||||
}
|
||||
{
|
||||
auto voices_array = json_array();
|
||||
|
||||
auto voices = speech_enumerate_voices();
|
||||
|
||||
for (auto& voice : voices) {
|
||||
json_array_append_new(voices_array, json_string(voice.c_str()));
|
||||
}
|
||||
|
||||
json_object_set_new(root, "voices", voices_array);
|
||||
}
|
||||
{
|
||||
auto resolution_array = json_array();
|
||||
|
||||
auto displays = gr_enumerate_displays();
|
||||
|
||||
for (auto& display : displays) {
|
||||
auto display_obj = json_object();
|
||||
|
||||
json_object_set_new(display_obj, "index", json_integer(display.index));
|
||||
json_object_set_new(display_obj, "name", json_string(display.name.c_str()));
|
||||
|
||||
json_object_set_new(display_obj, "x", json_integer(display.x));
|
||||
json_object_set_new(display_obj, "y", json_integer(display.y));
|
||||
json_object_set_new(display_obj, "width", json_integer(display.width));
|
||||
json_object_set_new(display_obj, "height", json_integer(display.height));
|
||||
|
||||
auto modes_array = json_array();
|
||||
|
||||
for (auto& mode : display.video_modes) {
|
||||
auto mode_obj = json_object();
|
||||
|
||||
json_object_set_new(mode_obj, "x", json_integer(mode.width));
|
||||
json_object_set_new(mode_obj, "y", json_integer(mode.height));
|
||||
json_object_set_new(mode_obj, "bits", json_integer(mode.bit_depth));
|
||||
|
||||
json_array_append_new(modes_array, mode_obj);
|
||||
}
|
||||
|
||||
json_object_set_new(display_obj, "modes", modes_array);
|
||||
|
||||
json_array_append_new(resolution_array, display_obj);
|
||||
}
|
||||
|
||||
json_object_set_new(root, "displays", resolution_array);
|
||||
}
|
||||
{
|
||||
auto openal_obj = json_object();
|
||||
|
||||
auto openal_info = openal_get_platform_information();
|
||||
|
||||
json_object_set_new(openal_obj, "version_major", json_integer(openal_info.version_major));
|
||||
json_object_set_new(openal_obj, "version_minor", json_integer(openal_info.version_minor));
|
||||
|
||||
json_object_set(openal_obj, "default_playback", json_string(openal_info.default_playback_device.c_str()));
|
||||
json_object_set(openal_obj, "default_capture", json_string(openal_info.default_capture_device.c_str()));
|
||||
|
||||
{
|
||||
auto playback_array = json_array();
|
||||
|
||||
for (auto& device : openal_info.playback_devices) {
|
||||
json_array_append_new(playback_array, json_string(device.c_str()));
|
||||
}
|
||||
|
||||
json_object_set_new(openal_obj, "playback_devices", playback_array);
|
||||
}
|
||||
{
|
||||
auto capture_array = json_array();
|
||||
|
||||
for (auto& device : openal_info.capture_devices) {
|
||||
json_array_append_new(capture_array, json_string(device.c_str()));
|
||||
}
|
||||
|
||||
json_object_set_new(openal_obj, "capture_devices", capture_array);
|
||||
}
|
||||
|
||||
json_object_set_new(root, "openal", openal_obj);
|
||||
}
|
||||
{
|
||||
auto joystick_array = json_array();
|
||||
|
||||
auto joysticks = io::joystick::getJoystickInformations();
|
||||
for (auto& info : joysticks) {
|
||||
auto joystick_obj = json_object();
|
||||
|
||||
json_object_set_new(joystick_obj, "name", json_string(info.name.c_str()));
|
||||
json_object_set_new(joystick_obj, "guid", json_string(info.guid.c_str()));
|
||||
|
||||
json_object_set_new(joystick_obj, "num_axes", json_integer(info.num_axes));
|
||||
json_object_set_new(joystick_obj, "num_balls", json_integer(info.num_balls));
|
||||
json_object_set_new(joystick_obj, "num_buttons", json_integer(info.num_buttons));
|
||||
json_object_set_new(joystick_obj, "num_hats", json_integer(info.num_hats));
|
||||
|
||||
json_object_set_new(joystick_obj, "is_haptic", json_boolean(info.is_haptic));
|
||||
|
||||
json_array_append_new(joystick_array, joystick_obj);
|
||||
}
|
||||
|
||||
json_object_set_new(root, "joysticks", joystick_array);
|
||||
}
|
||||
{
|
||||
json_object_set_new(root, "pref_path", json_string(os_get_config_path().c_str()));
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
static void write_flags_file() {
|
||||
FILE *fp = fopen("flags.lch","w");
|
||||
|
||||
if (fp == NULL) {
|
||||
os::dialogs::Message(os::dialogs::MESSAGEBOX_ERROR, "Error creating flag list for launcher");
|
||||
return;
|
||||
}
|
||||
|
||||
int easy_flag_size = sizeof(EasyFlag);
|
||||
int flag_size = sizeof(Flag);
|
||||
|
||||
int num_easy_flags = sizeof(easy_flags) / easy_flag_size;
|
||||
int num_flags = sizeof(exe_params) / flag_size;
|
||||
|
||||
// Launcher will check its using structures of the same size
|
||||
fwrite(&easy_flag_size, sizeof(int), 1, fp);
|
||||
fwrite(&flag_size, sizeof(int), 1, fp);
|
||||
|
||||
fwrite(&num_easy_flags, sizeof(int), 1, fp);
|
||||
fwrite(&easy_flags, sizeof(easy_flags), 1, fp);
|
||||
|
||||
fwrite(&num_flags, sizeof(int), 1, fp);
|
||||
fwrite(&exe_params, sizeof(exe_params), 1, fp);
|
||||
|
||||
{
|
||||
// cheap and bastardly cap check for builds
|
||||
// (needs to be compatible with older Launchers, which means having
|
||||
// this implies an OpenAL build for old Launchers)
|
||||
ubyte build_caps = 0;
|
||||
|
||||
/* portej05 defined this always */
|
||||
build_caps |= BUILD_CAPS_OPENAL;
|
||||
build_caps |= BUILD_CAPS_NO_D3D;
|
||||
build_caps |= BUILD_CAPS_NEW_SND;
|
||||
build_caps |= BUILD_CAPS_SDL;
|
||||
|
||||
fwrite(&build_caps, 1, 1, fp);
|
||||
}
|
||||
|
||||
fflush(fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
static flag_output_type get_flags_output_type() {
|
||||
Assertion(get_flags_arg.found(), "This function is only valid if -get_flags is present!");
|
||||
|
||||
if (!get_flags_arg.has_param()) {
|
||||
// Default to binary mode
|
||||
return flag_output_type::Binary;
|
||||
}
|
||||
|
||||
SCP_string type(get_flags_arg.str());
|
||||
|
||||
if (type == "binary") {
|
||||
return flag_output_type::Binary;
|
||||
} else if (type == "json_v1") {
|
||||
return flag_output_type::Json_V1;
|
||||
} else {
|
||||
// This is supposed to make it easy for the launcher to recognize an unsupported type
|
||||
printf("OUTPUT TYPE NOT SUPPORTED!\n");
|
||||
// Default to json in this case so that no flags.lch file is created
|
||||
return flag_output_type::Json_V1;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_flags() {
|
||||
auto type = get_flags_output_type();
|
||||
switch(type) {
|
||||
case flag_output_type::Binary:
|
||||
write_flags_file();
|
||||
break;
|
||||
case flag_output_type::Json_V1:
|
||||
json_t* root = json_get_v1();
|
||||
|
||||
json_dumpf(root, stdout, JSON_INDENT(4));
|
||||
json_decref(root);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool SetCmdlineParams()
|
||||
// Sets externed variables used for communication cmdline information
|
||||
{
|
||||
@@ -1248,47 +1513,7 @@ bool SetCmdlineParams()
|
||||
|
||||
// DO THIS FIRST to avoid unrecognized flag warnings when just getting flag file
|
||||
if ( get_flags_arg.found() ) {
|
||||
FILE *fp = fopen("flags.lch","w");
|
||||
|
||||
if (fp == NULL) {
|
||||
os::dialogs::Message(os::dialogs::MESSAGEBOX_ERROR, "Error creating flag list for launcher");
|
||||
return false;
|
||||
}
|
||||
|
||||
int easy_flag_size = sizeof(EasyFlag);
|
||||
int flag_size = sizeof(Flag);
|
||||
|
||||
int num_easy_flags = sizeof(easy_flags) / easy_flag_size;
|
||||
int num_flags = sizeof(exe_params) / flag_size;
|
||||
|
||||
// Launcher will check its using structures of the same size
|
||||
fwrite(&easy_flag_size, sizeof(int), 1, fp);
|
||||
fwrite(&flag_size, sizeof(int), 1, fp);
|
||||
|
||||
fwrite(&num_easy_flags, sizeof(int), 1, fp);
|
||||
fwrite(&easy_flags, sizeof(easy_flags), 1, fp);
|
||||
|
||||
fwrite(&num_flags, sizeof(int), 1, fp);
|
||||
fwrite(&exe_params, sizeof(exe_params), 1, fp);
|
||||
|
||||
{
|
||||
// cheap and bastardly cap check for builds
|
||||
// (needs to be compatible with older Launchers, which means having
|
||||
// this implies an OpenAL build for old Launchers)
|
||||
ubyte build_caps = 0;
|
||||
|
||||
/* portej05 defined this always */
|
||||
build_caps |= BUILD_CAPS_OPENAL;
|
||||
build_caps |= BUILD_CAPS_NO_D3D;
|
||||
build_caps |= BUILD_CAPS_NEW_SND;
|
||||
build_caps |= BUILD_CAPS_SDL;
|
||||
|
||||
|
||||
fwrite(&build_caps, 1, 1, fp);
|
||||
}
|
||||
|
||||
fflush(fp);
|
||||
fclose(fp);
|
||||
write_flags();
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1541,6 +1766,11 @@ bool SetCmdlineParams()
|
||||
Show_framerate = 1;
|
||||
}
|
||||
|
||||
if (bmpmanusage_arg.found())
|
||||
{
|
||||
Cmdline_bmpman_usage = 1;
|
||||
}
|
||||
|
||||
if(pos_arg.found())
|
||||
{
|
||||
Cmdline_show_pos = 1;
|
||||
@@ -1667,7 +1897,7 @@ bool SetCmdlineParams()
|
||||
Output_scripting_meta = true;
|
||||
|
||||
if (output_sexp_arg.found() ) {
|
||||
output_sexps("sexps.html");
|
||||
Cmdline_output_sexp_info = true;
|
||||
}
|
||||
|
||||
if ( no_pbo_arg.found() )
|
||||
@@ -1710,6 +1940,11 @@ bool SetCmdlineParams()
|
||||
Cmdline_noshadercache = true;
|
||||
}
|
||||
|
||||
if (no_bindless_tex_arg.found())
|
||||
{
|
||||
Cmdline_no_bindless_textures = true;
|
||||
}
|
||||
|
||||
if (portable_mode.found())
|
||||
{
|
||||
Cmdline_portable_mode = true;
|
||||
@@ -1721,11 +1956,6 @@ bool SetCmdlineParams()
|
||||
}
|
||||
#endif
|
||||
|
||||
if ( snd_preload_arg.found() )
|
||||
{
|
||||
Cmdline_snd_preload = 1;
|
||||
}
|
||||
|
||||
if ( env.found() ) {
|
||||
Cmdline_env = 0;
|
||||
}
|
||||
@@ -1764,9 +1994,6 @@ bool SetCmdlineParams()
|
||||
if ( rearm_timer_arg.found() )
|
||||
Cmdline_rearm_timer = 1;
|
||||
|
||||
if ( missile_lighting_arg.found() )
|
||||
Cmdline_missile_lighting = 1;
|
||||
|
||||
if ( save_render_targets_arg.found() )
|
||||
Cmdline_save_render_targets = 1;
|
||||
|
||||
@@ -1801,11 +2028,6 @@ bool SetCmdlineParams()
|
||||
Cmdline_fb_thrusters = true;
|
||||
}
|
||||
|
||||
if ( no_batching.found() )
|
||||
{
|
||||
Cmdline_no_batching = true;
|
||||
}
|
||||
|
||||
if ( postprocess_arg.found() )
|
||||
{
|
||||
Cmdline_postprocess = 1;
|
||||
@@ -1841,6 +2063,11 @@ bool SetCmdlineParams()
|
||||
Cmdline_no_deferred_lighting = 1;
|
||||
}
|
||||
|
||||
if (anisotropy_level_arg.found())
|
||||
{
|
||||
Cmdline_aniso_level = anisotropy_level_arg.get_int();
|
||||
}
|
||||
|
||||
if (frame_profile_write_file.found())
|
||||
{
|
||||
Cmdline_profile_write_file = true;
|
||||
@@ -1926,6 +2153,11 @@ bool SetCmdlineParams()
|
||||
Cmdline_deprecated_brief_lighting = 1;
|
||||
}
|
||||
|
||||
if (deprecated_missile_lighting_arg.found())
|
||||
{
|
||||
Cmdline_deprecated_missile_lighting = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,6 @@ extern float static_point_factor;
|
||||
extern float static_tube_factor;
|
||||
extern int Cmdline_ambient_factor;
|
||||
extern int Cmdline_env;
|
||||
extern int Cmdline_missile_lighting;
|
||||
extern int Cmdline_glow;
|
||||
extern int Cmdline_nomotiondebris;
|
||||
extern int Cmdline_noscalevid; // disables fit-to-window for movies - taylor
|
||||
@@ -77,10 +76,10 @@ extern bool Cmdline_fxaa;
|
||||
extern int Cmdline_fxaa_preset;
|
||||
extern bool Cmdline_fb_explosions;
|
||||
extern bool Cmdline_fb_thrusters;
|
||||
extern bool Cmdline_no_batching;
|
||||
extern int Cmdline_shadow_quality;
|
||||
extern int Cmdline_no_deferred_lighting;
|
||||
extern int Cmdline_no_emissive;
|
||||
extern int Cmdline_aniso_level;
|
||||
|
||||
// Game Speed related
|
||||
extern int Cmdline_cache_bitmaps;
|
||||
@@ -104,7 +103,6 @@ extern int Cmdline_stretch_menu;
|
||||
|
||||
// Audio related
|
||||
extern int Cmdline_query_speech;
|
||||
extern int Cmdline_snd_preload;
|
||||
extern int Cmdline_voice_recognition;
|
||||
extern int Cmdline_no_enhanced_sound;
|
||||
|
||||
@@ -137,6 +135,7 @@ extern bool Cmdline_no_geo_sdr_effects;
|
||||
extern bool Cmdline_set_cpu_affinity;
|
||||
extern bool Cmdline_nograb;
|
||||
extern bool Cmdline_noshadercache;
|
||||
extern bool Cmdline_no_bindless_textures;
|
||||
#ifdef WIN32
|
||||
extern bool Cmdline_alternate_registry_path;
|
||||
#endif
|
||||
@@ -146,8 +145,10 @@ extern char *Cmdline_start_mission;
|
||||
extern int Cmdline_old_collision_sys;
|
||||
extern int Cmdline_dis_collisions;
|
||||
extern int Cmdline_dis_weapons;
|
||||
extern bool Cmdline_output_sexp_info;
|
||||
extern int Cmdline_noparseerrors;
|
||||
extern int Cmdline_extra_warn;
|
||||
extern int Cmdline_bmpman_usage;
|
||||
extern int Cmdline_show_pos;
|
||||
extern int Cmdline_show_stats;
|
||||
extern int Cmdline_save_render_targets;
|
||||
|
||||
@@ -92,7 +92,7 @@ void cmeasure_maybe_alert_success(object *objp)
|
||||
|
||||
if ( objp->parent == OBJ_INDEX(Player_obj) ) {
|
||||
hud_start_text_flash(XSTR("Evaded", 1430), 800);
|
||||
snd_play(&Snds[ship_get_sound(Player_obj, SND_MISSILE_EVADED_POPUP)]);
|
||||
snd_play(gamesnd_get_game_sound(ship_get_sound(Player_obj, SND_MISSILE_EVADED_POPUP)));
|
||||
} else if ( Objects[objp->parent].flags[Object::Object_Flags::Player_ship] ) {
|
||||
send_countermeasure_success_packet( objp->parent );
|
||||
}
|
||||
|
||||
@@ -14,6 +14,11 @@ bool Decoder::tryPopAudioData(AudioFramePtr& out) {
|
||||
return res == queue_op_status::success;
|
||||
}
|
||||
|
||||
bool Decoder::tryPopSubtitleData(SubtitleFramePtr& out) {
|
||||
auto res = m_subtitleQueue->try_pull_front(out);
|
||||
return res == queue_op_status::success;
|
||||
}
|
||||
|
||||
bool Decoder::tryPopVideoFrame(VideoFramePtr& out) {
|
||||
auto res = m_videoQueue->try_pull_front(out);
|
||||
return res == queue_op_status::success;
|
||||
@@ -24,6 +29,7 @@ void Decoder::initializeQueues(size_t queueSize) {
|
||||
|
||||
m_videoQueue.reset(new sync_bounded_queue<VideoFramePtr>(m_queueSize));
|
||||
m_audioQueue.reset(new sync_bounded_queue<AudioFramePtr>(m_queueSize));
|
||||
m_subtitleQueue.reset(new sync_bounded_queue<SubtitleFramePtr>(m_queueSize));
|
||||
}
|
||||
|
||||
void Decoder::stopDecoder() {
|
||||
@@ -31,6 +37,7 @@ void Decoder::stopDecoder() {
|
||||
|
||||
m_videoQueue->close();
|
||||
m_audioQueue->close();
|
||||
m_subtitleQueue->close();
|
||||
}
|
||||
|
||||
bool Decoder::canPushAudioData() {
|
||||
@@ -48,6 +55,20 @@ void Decoder::pushAudioData(AudioFramePtr&& data) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Decoder::canPushSubtitleData() {
|
||||
return !isSubtitleQueueFull();
|
||||
}
|
||||
void Decoder::pushSubtitleData(SubtitleFramePtr&& data) {
|
||||
Assertion(data, "Invalid audio data passed!");
|
||||
|
||||
try {
|
||||
m_subtitleQueue->push_back(std::move(data));
|
||||
}
|
||||
catch (sync_queue_is_closed&) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
bool Decoder::canPushVideoData() {
|
||||
return !isVideoQueueFull();
|
||||
}
|
||||
|
||||
@@ -58,6 +58,14 @@ struct AudioFrame {
|
||||
};
|
||||
typedef std::unique_ptr<AudioFrame> AudioFramePtr;
|
||||
|
||||
struct SubtitleFrame {
|
||||
double displayStartTime = -1.0;
|
||||
double displayEndTime = -1.0;
|
||||
|
||||
SCP_string text;
|
||||
};
|
||||
typedef std::unique_ptr<SubtitleFrame> SubtitleFramePtr;
|
||||
|
||||
/**
|
||||
* @brief Abstract class for decoding a video or audio stream
|
||||
*
|
||||
@@ -68,6 +76,7 @@ class Decoder {
|
||||
private:
|
||||
std::unique_ptr<sync_bounded_queue<VideoFramePtr>> m_videoQueue;
|
||||
std::unique_ptr<sync_bounded_queue<AudioFramePtr>> m_audioQueue;
|
||||
std::unique_ptr<sync_bounded_queue<SubtitleFramePtr>> m_subtitleQueue;
|
||||
|
||||
bool m_decoding;
|
||||
size_t m_queueSize = 0;
|
||||
@@ -109,6 +118,8 @@ class Decoder {
|
||||
*/
|
||||
virtual bool hasAudio() = 0;
|
||||
|
||||
virtual bool hasSubtitles() = 0;
|
||||
|
||||
/**
|
||||
* @brief Ends decoding of the video file
|
||||
*/
|
||||
@@ -122,6 +133,14 @@ class Decoder {
|
||||
|
||||
bool tryPopAudioData(AudioFramePtr&);
|
||||
|
||||
bool isSubtitleQueueFull() { return m_subtitleQueue->size() == m_queueSize; }
|
||||
|
||||
bool isSubtitleFrameAvailable() { return !m_subtitleQueue->empty(); }
|
||||
|
||||
size_t getSubtitleQueueSize() { return m_subtitleQueue->size(); }
|
||||
|
||||
bool tryPopSubtitleData(SubtitleFramePtr&);
|
||||
|
||||
bool isVideoQueueFull() { return m_videoQueue->size() == m_queueSize; }
|
||||
|
||||
bool isVideoFrameAvailable() { return !m_videoQueue->empty(); }
|
||||
@@ -141,6 +160,10 @@ class Decoder {
|
||||
|
||||
void pushAudioData(AudioFramePtr&& data);
|
||||
|
||||
bool canPushSubtitleData();
|
||||
|
||||
void pushSubtitleData(SubtitleFramePtr&& data);
|
||||
|
||||
bool canPushVideoData();
|
||||
|
||||
void pushFrameData(VideoFramePtr&& frame);
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
#include "VideoPresenter.h"
|
||||
|
||||
#include "graphics/matrix.h"
|
||||
|
||||
using namespace cutscene;
|
||||
using namespace cutscene::player;
|
||||
|
||||
namespace {
|
||||
struct movie_vertex {
|
||||
vec2d pos;
|
||||
vec2d uv;
|
||||
};
|
||||
}
|
||||
|
||||
namespace cutscene {
|
||||
namespace player {
|
||||
VideoPresenter::VideoPresenter(const MovieProperties& props) {
|
||||
GR_DEBUG_SCOPE("Init video");
|
||||
|
||||
auto w = static_cast<int>(props.size.width);
|
||||
auto h = static_cast<int>(props.size.height);
|
||||
|
||||
// Create buffer textures
|
||||
_yTexBuffer.reset(new uint8_t[w * h]);
|
||||
_ytex = bm_create(8, w, h, _yTexBuffer.get(), BMP_AABITMAP);
|
||||
|
||||
_uTexBuffer.reset(new uint8_t[w / 2 * h / 2]);
|
||||
_utex = bm_create(8, w / 2, h / 2, _uTexBuffer.get(), BMP_AABITMAP);
|
||||
|
||||
_vTexBuffer.reset(new uint8_t[w / 2 * h / 2]);
|
||||
_vtex = bm_create(8, w / 2, h / 2, _vTexBuffer.get(), BMP_AABITMAP);
|
||||
|
||||
material_set_movie(&_render_material, _ytex, _utex, _vtex);
|
||||
}
|
||||
|
||||
VideoPresenter::~VideoPresenter() {
|
||||
GR_DEBUG_SCOPE("Deinit video");
|
||||
|
||||
bm_release(_ytex);
|
||||
bm_release(_utex);
|
||||
bm_release(_vtex);
|
||||
|
||||
_ytex = _utex = _vtex = -1;
|
||||
}
|
||||
|
||||
void VideoPresenter::uploadVideoFrame(const VideoFramePtr& frame) {
|
||||
GR_DEBUG_SCOPE("Update video frame");
|
||||
auto ptrs = frame->getDataPointers();
|
||||
|
||||
memcpy(_yTexBuffer.get(), ptrs.y, frame->ySize.stride * frame->ySize.height);
|
||||
memcpy(_uTexBuffer.get(), ptrs.u, frame->uvSize.stride * frame->uvSize.height);
|
||||
memcpy(_vTexBuffer.get(), ptrs.v, frame->uvSize.stride * frame->uvSize.height);
|
||||
|
||||
gr_update_texture(_ytex, 8, _yTexBuffer.get(), (int) frame->ySize.width, (int) frame->ySize.height);
|
||||
gr_update_texture(_utex, 8, _uTexBuffer.get(), (int) frame->uvSize.width, (int) frame->uvSize.height);
|
||||
gr_update_texture(_vtex, 8, _vTexBuffer.get(), (int) frame->uvSize.width, (int) frame->uvSize.height);
|
||||
}
|
||||
|
||||
void VideoPresenter::displayFrame(float x1, float y1, float x2, float y2) {
|
||||
GR_DEBUG_SCOPE("Draw video frame");
|
||||
|
||||
movie_vertex vertex_data[4];
|
||||
|
||||
vertex_data[0].pos.x = x1;
|
||||
vertex_data[0].pos.y = y1;
|
||||
vertex_data[0].uv.x = 0.0f;
|
||||
vertex_data[0].uv.y = 0.0f;
|
||||
|
||||
vertex_data[1].pos.x = x1;
|
||||
vertex_data[1].pos.y = y2;
|
||||
vertex_data[1].uv.x = 0.0f;
|
||||
vertex_data[1].uv.y = 1.0f;
|
||||
|
||||
vertex_data[2].pos.x = x2;
|
||||
vertex_data[2].pos.y = y1;
|
||||
vertex_data[2].uv.x = 1.0f;
|
||||
vertex_data[2].uv.y = 0.0f;
|
||||
|
||||
vertex_data[3].pos.x = x2;
|
||||
vertex_data[3].pos.y = y2;
|
||||
vertex_data[3].uv.x = 1.0f;
|
||||
vertex_data[3].uv.y = 1.0f;
|
||||
|
||||
// Use the immediate buffer for storing our data since that is exactly what we need
|
||||
auto offset = gr_add_to_immediate_buffer(sizeof(vertex_data), vertex_data);
|
||||
|
||||
vertex_layout layout;
|
||||
layout.add_vertex_component(vertex_format_data::POSITION2, sizeof(vertex_data[0]), offset + offsetof(movie_vertex, pos));
|
||||
layout.add_vertex_component(vertex_format_data::TEX_COORD2, sizeof(vertex_data[0]), offset + offsetof(movie_vertex, uv));
|
||||
|
||||
gr_render_movie(&_render_material, PRIM_TYPE_TRISTRIP, &layout, 4, gr_immediate_buffer_handle);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef FS2_OPEN_OPENGLVIDEOPRESENTER_H
|
||||
#define FS2_OPEN_OPENGLVIDEOPRESENTER_H
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "graphics/2d.h"
|
||||
#include "graphics/material.h"
|
||||
#include "cutscene/Decoder.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace cutscene {
|
||||
namespace player {
|
||||
|
||||
class VideoPresenter {
|
||||
int _ytex = -1;
|
||||
std::unique_ptr<uint8_t[]> _yTexBuffer;
|
||||
|
||||
int _utex = -1;
|
||||
std::unique_ptr<uint8_t[]> _uTexBuffer;
|
||||
|
||||
int _vtex = -1;
|
||||
std::unique_ptr<uint8_t[]> _vTexBuffer;
|
||||
|
||||
movie_material _render_material;
|
||||
public:
|
||||
explicit VideoPresenter(const MovieProperties& props);
|
||||
|
||||
// Disallow copying
|
||||
VideoPresenter(const VideoPresenter&) = delete;
|
||||
VideoPresenter& operator=(const VideoPresenter&) = delete;
|
||||
|
||||
virtual ~VideoPresenter();
|
||||
|
||||
void uploadVideoFrame(const VideoFramePtr& frame);
|
||||
|
||||
void displayFrame(float x1, float y1, float x2, float y2);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif //FS2_OPEN_OPENGLVIDEOPRESENTER_H
|
||||
@@ -9,6 +9,9 @@
|
||||
|
||||
#include "cutscene/ffmpeg/AudioDecoder.h"
|
||||
#include "cutscene/ffmpeg/VideoDecoder.h"
|
||||
#include "cutscene/ffmpeg/SubtitleDecoder.h"
|
||||
|
||||
#include "localization/localize.h"
|
||||
|
||||
using namespace libs::ffmpeg;
|
||||
|
||||
@@ -62,6 +65,8 @@ CodecContextParameters getCodecParameters(AVStream* stream) {
|
||||
paras.channel_layout = stream->codecpar->channel_layout;
|
||||
paras.sample_rate = stream->codecpar->sample_rate;
|
||||
paras.audio_format = (AVSampleFormat)stream->codecpar->format;
|
||||
|
||||
paras.codec_id = stream->codecpar->codec_id;
|
||||
#else
|
||||
paras.width = stream->codec->width;
|
||||
paras.height = stream->codec->height;
|
||||
@@ -70,9 +75,29 @@ CodecContextParameters getCodecParameters(AVStream* stream) {
|
||||
paras.channel_layout = stream->codec->channel_layout;
|
||||
paras.sample_rate = stream->codec->sample_rate;
|
||||
paras.audio_format = stream->codec->sample_fmt;
|
||||
|
||||
paras.codec_id = stream->codec->codec_id;
|
||||
#endif
|
||||
return paras;
|
||||
}
|
||||
|
||||
SCP_string normalizeLanguage(const char* langauge_name) {
|
||||
if (!stricmp(langauge_name, "eng")) {
|
||||
return "English";
|
||||
}
|
||||
if (!stricmp(langauge_name, "ger")) {
|
||||
return "German";
|
||||
}
|
||||
if (!stricmp(langauge_name, "spa")) {
|
||||
return "Spanish";
|
||||
}
|
||||
|
||||
// Print to log so that we can find the actual value more easily later
|
||||
mprintf(("FFmpeg log: Found unknown language value '%s'!\n", langauge_name));
|
||||
|
||||
// Default to english for everything else
|
||||
return "English";
|
||||
}
|
||||
}
|
||||
|
||||
namespace cutscene {
|
||||
@@ -155,7 +180,32 @@ std::unique_ptr<DecoderStatus> initializeStatus(std::unique_ptr<InputStream>& st
|
||||
status->audioStream = ctx->streams[audioStream];
|
||||
}
|
||||
|
||||
status->videoCodecPars = getCodecParameters(status->videoStream);
|
||||
auto& current_language = Lcl_languages[Lcl_current_lang];
|
||||
for (uint32_t i = 0; i < ctx->nb_streams; ++i) {
|
||||
auto test_stream = ctx->streams[i];
|
||||
|
||||
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(57, 24, 255)
|
||||
auto& pars = test_stream->codecpar;
|
||||
|
||||
if (!(pars->codec_type == AVMEDIA_TYPE_SUBTITLE)) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
AVDictionaryEntry* tag = nullptr;
|
||||
if ((tag = av_dict_get(test_stream->metadata, "language", nullptr, 0)) == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto normalized_language = normalizeLanguage(tag->value);
|
||||
|
||||
if (!stricmp(normalized_language.c_str(), current_language.lang_name)) {
|
||||
status->subtitleStreamIndex = i;
|
||||
status->subtitleStream = test_stream;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
status->videoCodecPars = getCodecParameters(status->videoStream);
|
||||
|
||||
int err;
|
||||
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(57, 24, 255)
|
||||
@@ -213,6 +263,37 @@ std::unique_ptr<DecoderStatus> initializeStatus(std::unique_ptr<InputStream>& st
|
||||
status->audioCodec->name ? status->audioCodec->name : "<Unknown>"));
|
||||
}
|
||||
|
||||
if (status->subtitleStreamIndex >= 0) {
|
||||
// Not really sure if this makes sense for subtitles but it shouldn't break anything...
|
||||
status->subtitleCodecPars = getCodecParameters(status->subtitleStream);
|
||||
|
||||
status->subtitleCodec = avcodec_find_decoder(status->subtitleCodecPars.codec_id);
|
||||
|
||||
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(57, 24, 255)
|
||||
status->subtitleCodecCtx = avcodec_alloc_context3(status->subtitleCodec);
|
||||
|
||||
err = avcodec_parameters_to_context(status->subtitleCodecCtx, status->subtitleStream->codecpar);
|
||||
if (err < 0) {
|
||||
char errorStr[512];
|
||||
av_strerror(err, errorStr, sizeof(errorStr));
|
||||
mprintf(("FFMPEG: Failed to copy context parameters! Error: %s\n", errorStr));
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
status->subtitleCodecCtx = status->subtitleStream->codec;
|
||||
#endif
|
||||
|
||||
err = avcodec_open2(status->subtitleCodecCtx, status->subtitleCodec, nullptr);
|
||||
if (err < 0) {
|
||||
char errorStr[512];
|
||||
av_strerror(err, errorStr, sizeof(errorStr));
|
||||
mprintf(("FFMPEG: Failed to open subtitle codec! Error: %s\n", errorStr));
|
||||
}
|
||||
|
||||
mprintf(("FFmpeg: Using subtitle codec %s (%s).\n", status->subtitleCodec->long_name ? status->subtitleCodec->long_name : "<Unknown>",
|
||||
status->subtitleCodec->name ? status->subtitleCodec->name : "<Unknown>"));
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -279,10 +360,14 @@ void FFMPEGDecoder::startDecoding() {
|
||||
std::unique_ptr<VideoDecoder> videoDecoder(new VideoDecoder(m_status.get()));
|
||||
|
||||
std::unique_ptr<AudioDecoder> audioDecoder;
|
||||
std::unique_ptr<SubtitleDecoder> subtitleDecoder;
|
||||
|
||||
if (hasAudio()) {
|
||||
audioDecoder.reset(new AudioDecoder(m_status.get()));
|
||||
}
|
||||
if (hasSubtitles()) {
|
||||
subtitleDecoder.reset(new SubtitleDecoder(m_status.get()));
|
||||
}
|
||||
|
||||
auto ctx = m_input->m_ctx->ctx();
|
||||
AVPacket packet;
|
||||
@@ -322,7 +407,14 @@ void FFMPEGDecoder::startDecoding() {
|
||||
while ((ptr = audioDecoder->getFrame()) != nullptr) {
|
||||
pushAudioData(std::move(ptr));
|
||||
}
|
||||
}
|
||||
} else if (subtitleDecoder && packet.stream_index == m_status->subtitleStreamIndex) {
|
||||
subtitleDecoder->decodePacket(&packet);
|
||||
|
||||
SubtitleFramePtr ptr;
|
||||
while((ptr = subtitleDecoder->getFrame()) != nullptr) {
|
||||
pushSubtitleData(std::move(ptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isDecoding()) {
|
||||
@@ -349,6 +441,10 @@ bool FFMPEGDecoder::hasAudio() {
|
||||
return m_status->audioStreamIndex >= 0;
|
||||
}
|
||||
|
||||
bool FFMPEGDecoder::hasSubtitles() {
|
||||
return m_status->subtitleStreamIndex >= 0;
|
||||
}
|
||||
|
||||
void FFMPEGDecoder::close() {
|
||||
if (m_status) {
|
||||
m_status = nullptr;
|
||||
|
||||
@@ -27,6 +27,8 @@ class FFMPEGDecoder: public Decoder {
|
||||
|
||||
bool hasAudio() SCP_OVERRIDE;
|
||||
|
||||
bool hasSubtitles() SCP_OVERRIDE;
|
||||
|
||||
void close() SCP_OVERRIDE;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
//
|
||||
//
|
||||
|
||||
#include "SubtitleDecoder.h"
|
||||
|
||||
namespace {
|
||||
|
||||
double getFrameTime(int64_t pts, AVRational time_base) {
|
||||
return pts * av_q2d(time_base);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace cutscene {
|
||||
namespace ffmpeg {
|
||||
|
||||
SubtitleDecoder::SubtitleDecoder(cutscene::ffmpeg::DecoderStatus* status) : FFMPEGStreamDecoder(status) {
|
||||
}
|
||||
SubtitleDecoder::~SubtitleDecoder() {
|
||||
|
||||
}
|
||||
void SubtitleDecoder::decodePacket(AVPacket* packet) {
|
||||
int finishedFrame = 0;
|
||||
AVSubtitle subtitle;
|
||||
auto result = avcodec_decode_subtitle2(m_status->subtitleCodecCtx, &subtitle, &finishedFrame, packet);
|
||||
|
||||
if (result >= 0 && finishedFrame) {
|
||||
pushSubtitleFrame(packet, &subtitle);
|
||||
|
||||
avsubtitle_free(&subtitle);
|
||||
}
|
||||
}
|
||||
void SubtitleDecoder::finishDecoding() {
|
||||
|
||||
}
|
||||
void SubtitleDecoder::pushSubtitleFrame(AVPacket* packet, AVSubtitle* subtitle) {
|
||||
if (subtitle->format != 1) {
|
||||
// Non-text subtitles are not supported yet.
|
||||
mprintf(("FFmpeg: Detected a non-text subtitle! This is not supported yet!\n"));
|
||||
return;
|
||||
}
|
||||
if (subtitle->num_rects < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t pts = subtitle->pts;
|
||||
if (pts == AV_NOPTS_VALUE) {
|
||||
pts = packet->pts;
|
||||
}
|
||||
if (pts == AV_NOPTS_VALUE) {
|
||||
// Still no valid timestamp...
|
||||
return;
|
||||
}
|
||||
auto packet_time = getFrameTime(pts, m_status->subtitleStream->time_base);
|
||||
|
||||
auto start_time = packet_time + (subtitle->start_display_time / 1000.0);
|
||||
auto end_time = packet_time + (subtitle->end_display_time / 1000.0);
|
||||
if (subtitle->end_display_time == 0 && m_status->subtitleStream->time_base.num != 0) {
|
||||
end_time = packet_time + getFrameTime(packet->duration, m_status->subtitleStream->time_base);
|
||||
}
|
||||
|
||||
SCP_string processed_text;
|
||||
// For now we only use the first subtitle rectangle
|
||||
auto subtitle_rect = subtitle->rects[0];
|
||||
if (subtitle_rect->type == SUBTITLE_BITMAP) {
|
||||
// Same as above, non-text subtitles are not supported yet.
|
||||
mprintf(("FFmpeg: Detected a non-text subtitle! This is not supported yet!\n"));
|
||||
return;
|
||||
} else if (subtitle_rect->type == SUBTITLE_TEXT) {
|
||||
// Subtitle does not need to be processed any further
|
||||
processed_text = subtitle_rect->text;
|
||||
} else if (subtitle_rect->type == SUBTITLE_ASS) {
|
||||
SCP_string ass_text = subtitle_rect->ass;
|
||||
|
||||
// We are not interested in any of the other information contained in the ASS line but we still need to figure
|
||||
// out where our text starts
|
||||
auto comma_pos = ass_text.find(',');
|
||||
for (auto i = 0; i < 8 && comma_pos != SCP_string::npos; ++i) {
|
||||
comma_pos = ass_text.find(',', comma_pos + 1);
|
||||
}
|
||||
Assertion(comma_pos != SCP_string::npos, "Received an ill-formed ASS line from FFmpeg! Text was '%s'.", subtitle_rect->ass);
|
||||
|
||||
// This + 1 is safe since comma_pos points to a valid character so the next character is at worst the end of the string
|
||||
processed_text = ass_text.substr(comma_pos + 1);
|
||||
|
||||
// ASS has a special sequence for new lines that needs to be converted to an actual new line
|
||||
auto newline_pos = processed_text.find("\\N");
|
||||
while(newline_pos != SCP_string::npos) {
|
||||
processed_text.replace(newline_pos, 2, "\n");
|
||||
|
||||
newline_pos = processed_text.find("\\N");
|
||||
}
|
||||
} else {
|
||||
mprintf(("FFmpeg: Detected unknown subtitle name in movie!\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove \r from string. Some subtitles use \r\n for line breaks and we only support \n
|
||||
auto ret_pos = processed_text.find('\r');
|
||||
while(ret_pos != SCP_string::npos) {
|
||||
processed_text.erase(ret_pos, 1);
|
||||
|
||||
ret_pos = processed_text.find('\r');
|
||||
}
|
||||
|
||||
SubtitleFramePtr frame(new SubtitleFrame());
|
||||
frame->text = processed_text;
|
||||
|
||||
frame->displayStartTime = start_time;
|
||||
frame->displayEndTime = end_time;
|
||||
|
||||
pushFrame(std::move(frame));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "cutscene/ffmpeg/internal.h"
|
||||
#include "cutscene/ffmpeg/FFMPEGDecoder.h"
|
||||
|
||||
namespace cutscene {
|
||||
namespace ffmpeg {
|
||||
|
||||
class SubtitleDecoder: public FFMPEGStreamDecoder<SubtitleFrame> {
|
||||
public:
|
||||
explicit SubtitleDecoder(DecoderStatus* status);
|
||||
|
||||
virtual ~SubtitleDecoder();
|
||||
|
||||
virtual void decodePacket(AVPacket* packet) SCP_OVERRIDE;
|
||||
|
||||
virtual void finishDecoding() SCP_OVERRIDE;
|
||||
void pushSubtitleFrame(AVPacket* subtitle, AVSubtitle* pSubtitle);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,18 @@ DecoderStatus::~DecoderStatus() {
|
||||
#endif
|
||||
audioCodecCtx = nullptr;
|
||||
}
|
||||
|
||||
subtitleStreamIndex = -1;
|
||||
subtitleStream = nullptr;
|
||||
subtitleCodec = nullptr;
|
||||
|
||||
if (subtitleCodecCtx != nullptr) {
|
||||
avcodec_close(subtitleCodecCtx);
|
||||
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(57, 24, 255)
|
||||
avcodec_free_context(&subtitleCodecCtx);
|
||||
#endif
|
||||
subtitleCodecCtx = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ffmpeg
|
||||
|
||||
@@ -16,6 +16,8 @@ struct CodecContextParameters {
|
||||
uint64_t channel_layout = 0;
|
||||
int sample_rate = -1;
|
||||
AVSampleFormat audio_format = AV_SAMPLE_FMT_NONE;
|
||||
|
||||
AVCodecID codec_id = AV_CODEC_ID_NONE;
|
||||
};
|
||||
|
||||
struct DecoderStatus {
|
||||
@@ -31,6 +33,12 @@ struct DecoderStatus {
|
||||
AVCodec* audioCodec = nullptr;
|
||||
AVCodecContext* audioCodecCtx = nullptr;
|
||||
|
||||
int subtitleStreamIndex = -1;
|
||||
AVStream* subtitleStream = nullptr;
|
||||
CodecContextParameters subtitleCodecPars;
|
||||
AVCodec* subtitleCodec = nullptr;
|
||||
AVCodecContext* subtitleCodecCtx = nullptr;
|
||||
|
||||
DecoderStatus();
|
||||
|
||||
~DecoderStatus();
|
||||
|
||||
+200
-1
@@ -14,6 +14,7 @@
|
||||
#endif
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "globalincs/alphacolors.h"
|
||||
#include "globalincs/systemvars.h"
|
||||
#include "io/cursor.h"
|
||||
#include "graphics/2d.h"
|
||||
@@ -21,10 +22,203 @@
|
||||
#include "cmdline/cmdline.h"
|
||||
#include "cutscene/cutscenes.h" // cutscene_mark_viewable()
|
||||
#include "cutscene/player.h" // cutscene_mark_viewable()
|
||||
#include "tracing/categories.h"
|
||||
#include "tracing/tracing.h"
|
||||
#include "io/timer.h"
|
||||
#include "io/key.h"
|
||||
#include "mod_table/mod_table.h"
|
||||
|
||||
extern int Game_mode;
|
||||
extern int Is_standalone;
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace cutscene;
|
||||
|
||||
struct PlaybackState {
|
||||
bool playing = true;
|
||||
|
||||
vec2d posTopLeft;
|
||||
vec2d posBottomRight;
|
||||
|
||||
int subtitle_font = -1;
|
||||
};
|
||||
|
||||
void processEvents(PlaybackState* state) {
|
||||
io::mouse::CursorManager::get()->showCursor(false);
|
||||
|
||||
os_poll();
|
||||
|
||||
int k = key_inkey();
|
||||
switch (k) {
|
||||
case KEY_ESC:
|
||||
case KEY_ENTER:
|
||||
case KEY_SPACEBAR:
|
||||
state->playing = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
float print_string(float x, float y, const char* fmt, Args... params) {
|
||||
SCP_string text;
|
||||
sprintf(text, fmt, params...);
|
||||
|
||||
gr_string(x, y, text.c_str(), GR_RESIZE_NONE);
|
||||
|
||||
return y + font::get_current_font()->getHeight();
|
||||
}
|
||||
|
||||
void showVideoInfo(const PlayerState& state) {
|
||||
gr_set_color_fast(&Color_white);
|
||||
font::set_font(font::FONT1);
|
||||
|
||||
float y = 200.f;
|
||||
float x = 100.f;
|
||||
y = print_string(x, y, "Movie FPS: %f", state.props.fps);
|
||||
y = print_string(x, y, "Size: %dx%d", state.props.size.width, state.props.size.height);
|
||||
|
||||
y = gr_screen.max_h - 200.f;
|
||||
|
||||
size_t audio_queue_size = state.decoder->getAudioQueueSize();
|
||||
if (state.hasAudio) {
|
||||
ALint queued;
|
||||
OpenAL_ErrorPrint(alGetSourcei(state.audioSid, AL_BUFFERS_QUEUED, &queued));
|
||||
audio_queue_size += queued;
|
||||
}
|
||||
|
||||
y = print_string(x, y, "Audio Queue size: " SIZE_T_ARG, audio_queue_size);
|
||||
y = print_string(x, y, "Video Queue size: " SIZE_T_ARG, state.decoder->getVideoQueueSize());
|
||||
y += font::get_current_font()->getHeight();
|
||||
// Estimate the size of the video buffer
|
||||
// We use YUV420p frames so one pixel uses 1.5 bytes of storage
|
||||
size_t single_frame_size = (size_t) (state.props.size.width * state.props.size.height * 1.5);
|
||||
size_t total_size = single_frame_size * state.decoder->getVideoQueueSize();
|
||||
print_string(x, y, "Video buffer size: " SIZE_T_ARG "B", total_size);
|
||||
}
|
||||
|
||||
void displayVideo(Player* player, PlaybackState* state) {
|
||||
TRACE_SCOPE(tracing::CutsceneDrawVideoFrame);
|
||||
GR_DEBUG_SCOPE("Display Video");
|
||||
|
||||
gr_clear();
|
||||
player->draw(state->posTopLeft.x, state->posTopLeft.y, state->posBottomRight.x, state->posBottomRight.y);
|
||||
|
||||
auto subtitle = player->getCurrentSubtitle();
|
||||
if (!subtitle.empty() && state->subtitle_font >= 0) {
|
||||
gr_set_color_fast(&Color_bright_white);
|
||||
font::set_font(state->subtitle_font);
|
||||
|
||||
int width;
|
||||
int height;
|
||||
gr_get_string_size(&width, &height, subtitle.c_str());
|
||||
|
||||
// Offset the subtitle by 20% of the viewport size from the bottom
|
||||
auto y_offset = static_cast<int>((state->posBottomRight.y - state->posTopLeft.y) * 0.1);
|
||||
|
||||
auto center_x = (state->posBottomRight.x - state->posTopLeft.x) / 2;
|
||||
|
||||
auto text_x = center_x - width / 2;
|
||||
auto text_y = state->posBottomRight.y - y_offset - height;
|
||||
|
||||
gr_string(text_x, text_y, subtitle.c_str(), GR_RESIZE_NONE);
|
||||
}
|
||||
|
||||
if (Cmdline_show_video_info) {
|
||||
showVideoInfo(player->getInternalState());
|
||||
}
|
||||
|
||||
gr_flip();
|
||||
}
|
||||
void determine_display_positions(Player* player, PlaybackState* state) {
|
||||
auto& props = player->getMovieProperties();
|
||||
|
||||
float screen_ratio = (float) gr_screen.center_w / (float) gr_screen.center_h;
|
||||
float movie_ratio = (float) props.size.width / (float) props.size.height;
|
||||
|
||||
float scale_by;
|
||||
if (screen_ratio > movie_ratio) {
|
||||
scale_by = (float) gr_screen.center_h / (float) props.size.height;
|
||||
} else {
|
||||
scale_by = (float) gr_screen.center_w / (float) props.size.width;
|
||||
}
|
||||
|
||||
float screenX;
|
||||
float screenY;
|
||||
|
||||
if (!Cmdline_noscalevid && (scale_by != 1.0f)) {
|
||||
screenX = ((gr_screen.center_w / 2.0f + gr_screen.center_offset_x) / scale_by) - (static_cast<int>(props.size.width) / 2.0f) + 0.5f;
|
||||
screenY = ((gr_screen.center_h / 2.0f + gr_screen.center_offset_y) / scale_by) - (static_cast<int>(props.size.height) / 2.0f) + 0.5f;
|
||||
} else {
|
||||
// centers on 1024x768, fills on 640x480
|
||||
screenX = i2fl(((gr_screen.center_w - static_cast<int>(props.size.width)) / 2) + gr_screen.center_offset_x);
|
||||
screenY = i2fl(((gr_screen.center_h - static_cast<int>(props.size.height)) / 2) + gr_screen.center_offset_y);
|
||||
}
|
||||
|
||||
// set additional values for screen width/height and UV coords
|
||||
float screenXW = screenX + static_cast<int>(props.size.width);
|
||||
float screenYH = screenY + static_cast<int>(props.size.height);
|
||||
|
||||
if (!Cmdline_noscalevid && (scale_by != 1.0f)) {
|
||||
// Apply scaling
|
||||
screenX *= scale_by;
|
||||
screenY *= scale_by;
|
||||
|
||||
screenXW *= scale_by;
|
||||
screenYH *= scale_by;
|
||||
}
|
||||
|
||||
state->posTopLeft.x = screenX;
|
||||
state->posTopLeft.y = screenY;
|
||||
|
||||
state->posBottomRight.x = screenXW;
|
||||
state->posBottomRight.y = screenYH;
|
||||
}
|
||||
|
||||
void initialize_player_state(Player* player, PlaybackState* state) {
|
||||
determine_display_positions(player, state);
|
||||
|
||||
state->subtitle_font = font::FontManager::getFontIndex(Movie_subtitle_font);
|
||||
|
||||
if (state->subtitle_font < 0) {
|
||||
Warning(LOCATION, "Failed to load subtitle font '%s'! Subtitles will be disabled.", Movie_subtitle_font.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void movie_display_loop(Player* player, PlaybackState* state) {
|
||||
// Compute the maximum time we will sleep to make sure we can still maintain the movie FPS
|
||||
// and not waste too much CPU time
|
||||
// We will sleep at most half the time a frame would be displayed
|
||||
auto sleepTime = static_cast<std::uint64_t>((1. / (4. * player->getMovieProperties().fps)) * 1000.);
|
||||
|
||||
auto lastDisplayTimestamp = timer_get_microseconds();
|
||||
while (state->playing) {
|
||||
TRACE_SCOPE(tracing::CutsceneStep);
|
||||
|
||||
auto timestamp = timer_get_microseconds();
|
||||
|
||||
auto passed = timestamp - lastDisplayTimestamp;
|
||||
lastDisplayTimestamp = timestamp;
|
||||
|
||||
// Play as long as the player reports that there is more to display
|
||||
state->playing = player->update(passed);
|
||||
|
||||
displayVideo(player, state);
|
||||
|
||||
processEvents(state);
|
||||
|
||||
if (passed < sleepTime) {
|
||||
auto sleep = sleepTime - passed;
|
||||
|
||||
os_sleep(static_cast<uint>(sleep));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace movie {
|
||||
// Play one movie
|
||||
bool play(const char* name) {
|
||||
@@ -57,7 +251,12 @@ bool play(const char* name) {
|
||||
|
||||
auto player = cutscene::Player::newPlayer(name);
|
||||
if (player) {
|
||||
player->startPlayback();
|
||||
PlaybackState state;
|
||||
initialize_player_state(player.get(), &state);
|
||||
|
||||
movie_display_loop(player.get(), &state);
|
||||
|
||||
player->stopPlayback();
|
||||
} else {
|
||||
// uh-oh, movie is invalid... Abory, Retry, Fail?
|
||||
mprintf(("MOVIE ERROR: Found invalid movie! (%s)\n", name));
|
||||
|
||||
+96
-176
@@ -1,7 +1,7 @@
|
||||
#include "cutscene/player.h"
|
||||
|
||||
#include "cutscene/Decoder.h"
|
||||
#include "cutscene/player/VideoPresenter.h"
|
||||
#include "cutscene/VideoPresenter.h"
|
||||
#include "cutscene/ffmpeg/FFMPEGDecoder.h"
|
||||
|
||||
#include "graphics/2d.h"
|
||||
@@ -19,62 +19,19 @@
|
||||
|
||||
#include "parse/parselo.h"
|
||||
|
||||
#include "cutscene/player/OpenGLVideoPresenter.h"
|
||||
|
||||
using namespace cutscene::player;
|
||||
|
||||
namespace cutscene {
|
||||
struct PlayerState {
|
||||
MovieProperties props;
|
||||
Decoder* decoder = nullptr;
|
||||
|
||||
bool playing = true;
|
||||
|
||||
bool playbackHasBegun = false;
|
||||
|
||||
// Timing state
|
||||
std::uint64_t playback_time = 0;
|
||||
|
||||
// Audio state
|
||||
bool audioInited = false;
|
||||
bool hasAudio = false;
|
||||
ALuint audioSid = 0;
|
||||
SCP_vector<ALuint> audioBuffers;
|
||||
SCP_queue<ALuint> unqueuedAudioBuffers;
|
||||
|
||||
// Graphics state following
|
||||
bool videoInited = false;
|
||||
|
||||
VideoFramePtr currentFrame;
|
||||
VideoFramePtr nextFrame;
|
||||
bool newFrameAdded = false;
|
||||
|
||||
std::unique_ptr<VideoPresenter> videoPresenter;
|
||||
|
||||
PlayerState() : decoder(nullptr), playing(true), playbackHasBegun(false),
|
||||
playback_time(0),
|
||||
audioInited(0), hasAudio(false), audioSid(0),
|
||||
videoInited(false), newFrameAdded(false) {}
|
||||
|
||||
private:
|
||||
PlayerState(const PlayerState&) SCP_DELETED_FUNCTION;
|
||||
|
||||
PlayerState& operator=(const PlayerState&) SCP_DELETED_FUNCTION;
|
||||
};
|
||||
}
|
||||
|
||||
namespace {
|
||||
using namespace cutscene;
|
||||
|
||||
const int MAX_AUDIO_BUFFERS = 15;
|
||||
|
||||
Decoder* findDecoder(const SCP_string& name) {
|
||||
std::unique_ptr<Decoder> findDecoder(const SCP_string& name) {
|
||||
{
|
||||
auto ffmpeg = new ffmpeg::FFMPEGDecoder();
|
||||
std::unique_ptr<Decoder> ffmpeg(new ffmpeg::FFMPEGDecoder());
|
||||
if (ffmpeg->initialize(name)) {
|
||||
return ffmpeg;
|
||||
}
|
||||
delete ffmpeg;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@@ -93,7 +50,7 @@ void videoPlaybackInit(PlayerState* state) {
|
||||
Assert(state != NULL);
|
||||
|
||||
if (gr_screen.mode == GR_OPENGL) {
|
||||
state->videoPresenter.reset(new OpenGLVideoPresenter(state->props));
|
||||
state->videoPresenter.reset(new VideoPresenter(state->props));
|
||||
}
|
||||
|
||||
state->videoInited = true;
|
||||
@@ -163,7 +120,7 @@ void processVideoData(PlayerState* state) {
|
||||
return;
|
||||
}
|
||||
|
||||
while(currentTime >= state->nextFrame->frameTime) {
|
||||
while (currentTime >= state->nextFrame->frameTime) {
|
||||
// Move the next frame to the current frame slot
|
||||
state->currentFrame = std::move(state->nextFrame);
|
||||
|
||||
@@ -184,6 +141,34 @@ void processVideoData(PlayerState* state) {
|
||||
}
|
||||
}
|
||||
|
||||
void processSubtitleData(PlayerState* state) {
|
||||
SubtitleFramePtr ptr;
|
||||
while (state->decoder->tryPopSubtitleData(ptr)) {
|
||||
// Take all subtitle frames from our queue
|
||||
state->queued_subtitles.push(std::move(ptr));
|
||||
}
|
||||
|
||||
if (state->currentSubtitle && playbackGetTime(state) <= state->currentSubtitle->displayEndTime) {
|
||||
// We currently have a frame and it it still being displayed so there is nothing for us to do here
|
||||
return;
|
||||
}
|
||||
|
||||
// Subtitle has expired so we don't need it anymore
|
||||
state->currentSubtitle = nullptr;
|
||||
|
||||
while(!state->queued_subtitles.empty()) {
|
||||
// Take a look at the first entry and check if it's time to display it yet
|
||||
auto& nextSubtitle = state->queued_subtitles.front();
|
||||
if (playbackGetTime(state) >= nextSubtitle->displayStartTime) {
|
||||
state->currentSubtitle = std::move(nextSubtitle);
|
||||
state->queued_subtitles.pop();
|
||||
} else {
|
||||
// The next subtitle should not be displayed yet so we are done here
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool processAudioData(PlayerState* state) {
|
||||
TRACE_SCOPE(tracing::CutsceneProcessAudioData);
|
||||
|
||||
@@ -191,7 +176,7 @@ bool processAudioData(PlayerState* state) {
|
||||
if (state->decoder->hasAudio()) {
|
||||
// Even if we don't play the sound we still need to remove it from the queue
|
||||
AudioFramePtr audioData;
|
||||
while(state->decoder->tryPopAudioData(audioData)) {
|
||||
while (state->decoder->tryPopAudioData(audioData)) {
|
||||
// Intentionally left empty
|
||||
}
|
||||
}
|
||||
@@ -216,7 +201,9 @@ bool processAudioData(PlayerState* state) {
|
||||
|
||||
ALenum format = (audioData->channels == 2) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;
|
||||
|
||||
OpenAL_ErrorCheck(alBufferData(buffer, format, audioData->audioData.data(),
|
||||
OpenAL_ErrorCheck(alBufferData(buffer,
|
||||
format,
|
||||
audioData->audioData.data(),
|
||||
static_cast<ALsizei>(audioData->audioData.size() * sizeof(short)),
|
||||
static_cast<ALsizei>(audioData->rate)), return false);
|
||||
|
||||
@@ -269,82 +256,10 @@ void videoPlaybackClose(PlayerState* state) {
|
||||
state->videoInited = false;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
float print_string(float x, float y, const char* fmt, Args... params) {
|
||||
SCP_string text;
|
||||
sprintf(text, fmt, params...);
|
||||
|
||||
gr_string(x, y, text.c_str(), GR_RESIZE_NONE);
|
||||
|
||||
return y + font::get_current_font()->getHeight();
|
||||
}
|
||||
|
||||
void showVideoInfo(PlayerState* state) {
|
||||
gr_set_color_fast(&Color_white);
|
||||
|
||||
float y = 200.f;
|
||||
float x = 100.f;
|
||||
y = print_string(x, y, "Movie FPS: %f", state->props.fps);
|
||||
y = print_string(x, y, "Size: %dx%d", state->props.size.width, state->props.size.height);
|
||||
|
||||
y = gr_screen.max_h - 200.f;
|
||||
|
||||
size_t audio_queue_size = state->decoder->getAudioQueueSize();
|
||||
if (state->hasAudio) {
|
||||
ALint queued;
|
||||
OpenAL_ErrorPrint(alGetSourcei(state->audioSid, AL_BUFFERS_QUEUED, &queued));
|
||||
audio_queue_size += queued;
|
||||
}
|
||||
|
||||
y = print_string(x, y, "Audio Queue size: " SIZE_T_ARG, audio_queue_size);
|
||||
y = print_string(x, y, "Video Queue size: " SIZE_T_ARG, state->decoder->getVideoQueueSize());
|
||||
y += font::get_current_font()->getHeight();
|
||||
// Estimate the size of the video buffer
|
||||
// We use YUV420p frames so one pixel uses 1.5 bytes of storage
|
||||
size_t single_frame_size = (size_t) (state->props.size.width * state->props.size.height * 1.5);
|
||||
size_t total_size = single_frame_size * state->decoder->getVideoQueueSize();
|
||||
print_string(x, y, "Video buffer size: " SIZE_T_ARG "B", total_size);
|
||||
}
|
||||
|
||||
void displayVideo(PlayerState* state) {
|
||||
if (!state->currentFrame) {
|
||||
return;
|
||||
}
|
||||
|
||||
TRACE_SCOPE(tracing::CutsceneDrawVideoFrame);
|
||||
|
||||
gr_clear();
|
||||
if (state->videoPresenter) {
|
||||
state->videoPresenter->displayFrame();
|
||||
}
|
||||
|
||||
if (Cmdline_show_video_info) {
|
||||
showVideoInfo(state);
|
||||
}
|
||||
|
||||
gr_flip();
|
||||
}
|
||||
|
||||
void processEvents(PlayerState* state) {
|
||||
io::mouse::CursorManager::get()->showCursor(false);
|
||||
|
||||
os_poll();
|
||||
|
||||
int k = key_inkey();
|
||||
switch (k) {
|
||||
case KEY_ESC:
|
||||
case KEY_ENTER:
|
||||
case KEY_SPACEBAR:
|
||||
state->playing = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool shouldBeginPlayback(Decoder* decoder) {
|
||||
auto video = decoder->isVideoQueueFull();
|
||||
auto audio = decoder->isAudioQueueFull();
|
||||
auto subtitles = decoder->isSubtitleQueueFull();
|
||||
|
||||
// Wait until one of the queues is full, that should make sure
|
||||
// that we have enough frames at the beginning of playback and that
|
||||
@@ -352,92 +267,79 @@ bool shouldBeginPlayback(Decoder* decoder) {
|
||||
// or video at the beginning
|
||||
// Also only wait while the decoder is still working. Otherwise we will wait indefinitely if the video
|
||||
// is very short.
|
||||
return (audio || video) || !decoder->isDecoding();
|
||||
return (audio || video || subtitles) || !decoder->isDecoding();
|
||||
}
|
||||
}
|
||||
|
||||
namespace cutscene {
|
||||
Player::Player() {
|
||||
Player::Player(std::unique_ptr<Decoder>&& decoder) : m_decoder(std::move(decoder)) {
|
||||
initPlayback();
|
||||
}
|
||||
|
||||
Player::~Player() {
|
||||
// If video is initialized it means that stopPlayback has not been called yet
|
||||
if (m_state.videoInited) {
|
||||
stopPlayback();
|
||||
}
|
||||
|
||||
if (m_decoder) {
|
||||
m_decoder->close();
|
||||
}
|
||||
}
|
||||
|
||||
void Player::processDecoderData(PlayerState* state) {
|
||||
if (!state->playbackHasBegun) {
|
||||
bool Player::processDecoderData() {
|
||||
if (!m_state.playbackHasBegun) {
|
||||
// Wait until video and audio are available
|
||||
// If we don't have audio, don't wait for it (obviously...)
|
||||
if (!shouldBeginPlayback(state->decoder)) {
|
||||
return;
|
||||
if (!shouldBeginPlayback(m_state.decoder)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
state->playbackHasBegun = true;
|
||||
m_state.playbackHasBegun = true;
|
||||
}
|
||||
|
||||
TRACE_SCOPE(tracing::CutsceneProcessDecoder);
|
||||
|
||||
processVideoData(state);
|
||||
processVideoData(&m_state);
|
||||
|
||||
auto audioPlaying = processAudioData(state);
|
||||
processSubtitleData(&m_state);
|
||||
|
||||
auto audioPlaying = processAudioData(&m_state);
|
||||
|
||||
// Set the playing flag if the decoder is still active or there is still data available
|
||||
auto decoding = m_decoder->isDecoding();
|
||||
|
||||
// Audio is pending if there is data left in the queue or of OpenAL is still playing audio
|
||||
// Audio is pending if there is data left in the queue or if OpenAL is still playing audio
|
||||
auto pendingAudio = (m_decoder->isAudioFrameAvailable() && m_decoder->hasAudio()) || audioPlaying;
|
||||
auto pendingVideo = m_decoder->isVideoFrameAvailable();
|
||||
|
||||
state->playing = decoding || pendingAudio || pendingVideo;
|
||||
return decoding || pendingAudio || pendingVideo;
|
||||
}
|
||||
|
||||
void Player::startPlayback() {
|
||||
void Player::initPlayback() {
|
||||
Assertion(!m_state.videoInited, "Internal State has been initialized before! Create a new player for replaying a movie.");
|
||||
|
||||
m_decoderThread.reset(new std::thread(std::bind(&Player::decoderThread, this)));
|
||||
|
||||
PlayerState state;
|
||||
state.props = m_decoder->getProperties();
|
||||
state.decoder = m_decoder.get();
|
||||
m_state.props = m_decoder->getProperties();
|
||||
m_state.decoder = m_decoder.get();
|
||||
|
||||
// Compute the maximum time we will sleep to make sure we can still maintain the movie FPS
|
||||
// and not waste too much CPU time
|
||||
// We will sleep at most half the time a frame would be displayed
|
||||
auto sleepTime = static_cast<std::uint64_t>((1. / (4. * state.props.fps)) * 1000.);
|
||||
videoPlaybackInit(&m_state);
|
||||
|
||||
videoPlaybackInit(&state);
|
||||
|
||||
audioPlaybackInit(&state);
|
||||
|
||||
auto lastDisplayTimestamp = timer_get_microseconds();
|
||||
while (state.playing) {
|
||||
TRACE_SCOPE(tracing::CutsceneStep);
|
||||
|
||||
processDecoderData(&state);
|
||||
|
||||
displayVideo(&state);
|
||||
|
||||
processEvents(&state);
|
||||
|
||||
auto timestamp = timer_get_microseconds();
|
||||
|
||||
auto passed = timestamp - lastDisplayTimestamp;
|
||||
lastDisplayTimestamp = timestamp;
|
||||
|
||||
if (state.playbackHasBegun) {
|
||||
state.playback_time += passed;
|
||||
}
|
||||
|
||||
if (passed < sleepTime) {
|
||||
auto sleep = sleepTime - passed;
|
||||
|
||||
os_sleep(static_cast<uint>(sleep));
|
||||
}
|
||||
audioPlaybackInit(&m_state);
|
||||
}
|
||||
bool Player::update(uint64_t diff_time_micro) {
|
||||
if (m_state.playbackHasBegun) {
|
||||
m_state.playback_time += diff_time_micro;
|
||||
}
|
||||
|
||||
return processDecoderData();
|
||||
}
|
||||
void Player::stopPlayback() {
|
||||
m_decoder->stopDecoder();
|
||||
|
||||
audioPlaybackClose(&state);
|
||||
videoPlaybackClose(&state);
|
||||
audioPlaybackClose(&m_state);
|
||||
videoPlaybackClose(&m_state);
|
||||
|
||||
m_decoderThread->join();
|
||||
}
|
||||
@@ -460,10 +362,28 @@ std::unique_ptr<Player> Player::newPlayer(const SCP_string& name) {
|
||||
if (decoder == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::unique_ptr<Player>(new Player(std::move(decoder)));
|
||||
}
|
||||
const PlayerState& Player::getInternalState() const {
|
||||
return m_state;
|
||||
}
|
||||
const MovieProperties& Player::getMovieProperties() const {
|
||||
return m_state.props;
|
||||
}
|
||||
void Player::draw(float x1, float y1, float x2, float y2) {
|
||||
if (!m_state.currentFrame) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<Player> player(new Player());
|
||||
player->m_decoder.reset(decoder);
|
||||
|
||||
return player;
|
||||
if (m_state.videoPresenter) {
|
||||
m_state.videoPresenter->displayFrame(x1, y1, x2, y2);
|
||||
}
|
||||
}
|
||||
SCP_string Player::getCurrentSubtitle() {
|
||||
if (!m_state.currentSubtitle) {
|
||||
return "";
|
||||
} else {
|
||||
return m_state.currentSubtitle->text;
|
||||
}
|
||||
}
|
||||
}
|
||||
+89
-8
@@ -5,11 +5,48 @@
|
||||
#include <thread>
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "sound/openal.h"
|
||||
|
||||
#include "Decoder.h"
|
||||
#include "VideoPresenter.h"
|
||||
|
||||
namespace cutscene {
|
||||
class Decoder;
|
||||
|
||||
struct PlayerState;
|
||||
struct PlayerState {
|
||||
MovieProperties props;
|
||||
Decoder* decoder = nullptr;
|
||||
|
||||
bool playbackHasBegun = false;
|
||||
|
||||
// Timing state
|
||||
std::uint64_t playback_time = 0;
|
||||
|
||||
// Audio state
|
||||
bool audioInited = false;
|
||||
bool hasAudio = false;
|
||||
ALuint audioSid = 0;
|
||||
SCP_vector<ALuint> audioBuffers;
|
||||
SCP_queue<ALuint> unqueuedAudioBuffers;
|
||||
|
||||
// Graphics state following
|
||||
bool videoInited = false;
|
||||
|
||||
VideoFramePtr currentFrame;
|
||||
VideoFramePtr nextFrame;
|
||||
bool newFrameAdded = false;
|
||||
|
||||
std::unique_ptr<player::VideoPresenter> videoPresenter;
|
||||
|
||||
SCP_queue<SubtitleFramePtr> queued_subtitles;
|
||||
SubtitleFramePtr currentSubtitle;
|
||||
|
||||
PlayerState() {
|
||||
}
|
||||
|
||||
PlayerState(const PlayerState&) = delete;
|
||||
PlayerState& operator=(const PlayerState&) = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A movie player
|
||||
@@ -20,22 +57,66 @@ class Player {
|
||||
|
||||
std::unique_ptr<std::thread> m_decoderThread;
|
||||
|
||||
void processDecoderData(PlayerState* state);
|
||||
PlayerState m_state;
|
||||
|
||||
bool processDecoderData();
|
||||
|
||||
private:
|
||||
Player(const Player&) = delete;
|
||||
|
||||
void decoderThread();
|
||||
|
||||
void initPlayback();
|
||||
|
||||
public:
|
||||
Player();
|
||||
explicit Player(std::unique_ptr<Decoder>&& decoder);
|
||||
|
||||
~Player();
|
||||
|
||||
Player(const Player&) = delete;
|
||||
Player& operator=(const Player&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Begin playing the previously selected movie
|
||||
*/
|
||||
void startPlayback();
|
||||
* @brief Update the player and increase display timestamp by specified delta time
|
||||
* @param diff_time_micro The time to increase the display timestamp in microseconds (not milliseconds!)
|
||||
* @return @c true if there is more content to display. @c false if the player reached the end of the movie and has
|
||||
* displayed the last frame.
|
||||
*/
|
||||
bool update(uint64_t diff_time_micro);
|
||||
|
||||
/**
|
||||
* @brief Gives access to the internal state of the player
|
||||
*
|
||||
* @warning This should only be used for debugging purposed!
|
||||
*
|
||||
* @return A reference to the internal state
|
||||
*/
|
||||
const PlayerState& getInternalState() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the properties of the movie that this player is displaying
|
||||
* @return A reference to the movie properties
|
||||
*/
|
||||
const MovieProperties& getMovieProperties() const;
|
||||
|
||||
/**
|
||||
* @brief Draws the current movie frame at the specified coordinates.
|
||||
*
|
||||
* This may not draw anything if there is currently not a frame to display.
|
||||
*
|
||||
* @param x1 The X coordinate of the top left corner
|
||||
* @param y1 The Y coordinate of the top left corner
|
||||
* @param x2 The X coordinate of the bottom right corner
|
||||
* @param y2 The Y coordinate of the bottom right corner
|
||||
*/
|
||||
void draw(float x1, float y1, float x2, float y2);
|
||||
|
||||
SCP_string getCurrentSubtitle();
|
||||
|
||||
/**
|
||||
* @brief Stops playback
|
||||
*
|
||||
* This is also automatically called when this instance is destroyed.
|
||||
*/
|
||||
void stopPlayback();
|
||||
|
||||
/**
|
||||
* @brief Creates a player
|
||||
|
||||
@@ -1,223 +0,0 @@
|
||||
|
||||
#include "OpenGLVideoPresenter.h"
|
||||
|
||||
#include "graphics/opengl/gropenglstate.h"
|
||||
#include "graphics/opengl/gropengltnl.h"
|
||||
#include "graphics/opengl/gropengldraw.h"
|
||||
|
||||
|
||||
using namespace cutscene;
|
||||
using namespace cutscene::player;
|
||||
|
||||
namespace cutscene {
|
||||
namespace player {
|
||||
OpenGLVideoPresenter::OpenGLVideoPresenter(const MovieProperties& props) : _scaleVideo(false),
|
||||
_ytex(0), _utex(0), _vtex(0) {
|
||||
GR_DEBUG_SCOPE("Init video");
|
||||
|
||||
opengl_set_texture_target(GL_TEXTURE_2D);
|
||||
|
||||
_vertexBuffer = gr_create_vertex_buffer(true);
|
||||
|
||||
auto w = static_cast<int>(props.size.width);
|
||||
auto h = static_cast<int>(props.size.height);
|
||||
|
||||
_sdr_handle = gr_maybe_create_shader(SDR_TYPE_VIDEO_PROCESS, 0);
|
||||
|
||||
glGenTextures(1, &_ytex);
|
||||
glGenTextures(1, &_utex);
|
||||
glGenTextures(1, &_vtex);
|
||||
|
||||
if (_ytex + _utex + _vtex == 0) {
|
||||
throw std::runtime_error("Can't create a GL texture");
|
||||
}
|
||||
opengl_shader_set_current(_sdr_handle);
|
||||
|
||||
gr_set_lighting(false, false);
|
||||
|
||||
GL_state.SetAlphaBlendMode(ALPHA_BLEND_NONE);
|
||||
GL_state.SetZbufferType(ZBUFFER_TYPE_NONE);
|
||||
|
||||
GL_state.Texture.SetActiveUnit(0);
|
||||
GL_state.Texture.SetTarget(GL_texture_target);
|
||||
GL_state.Texture.Enable(_ytex);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// NOTE: using NULL instead of pixelbuf crashes some drivers, but then so does pixelbuf
|
||||
glTexImage2D(GL_state.Texture.GetTarget(), 0, GL_RED, w, h, 0, GL_RED,
|
||||
GL_UNSIGNED_BYTE, NULL);
|
||||
|
||||
GL_state.Texture.SetActiveUnit(1);
|
||||
GL_state.Texture.SetTarget(GL_texture_target);
|
||||
GL_state.Texture.Enable(_utex);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// NOTE: using NULL instead of pixelbuf crashes some drivers, but then so does pixelbuf
|
||||
glTexImage2D(GL_state.Texture.GetTarget(), 0, GL_RED, w / 2, h / 2, 0, GL_RED,
|
||||
GL_UNSIGNED_BYTE, NULL);
|
||||
|
||||
GL_state.Texture.SetActiveUnit(2);
|
||||
GL_state.Texture.SetTarget(GL_texture_target);
|
||||
GL_state.Texture.Enable(_vtex);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// NOTE: using NULL instead of pixelbuf crashes some drivers, but then so does pixelbuf
|
||||
glTexImage2D(GL_state.Texture.GetTarget(), 0, GL_RED, w / 2, h / 2, 0, GL_RED,
|
||||
GL_UNSIGNED_BYTE, NULL);
|
||||
|
||||
float screen_ratio = (float) gr_screen.center_w / (float) gr_screen.center_h;
|
||||
float movie_ratio = (float) props.size.width / (float) props.size.height;
|
||||
|
||||
float scale_by;
|
||||
if (screen_ratio > movie_ratio) {
|
||||
scale_by = (float) gr_screen.center_h / (float) props.size.height;
|
||||
} else {
|
||||
scale_by = (float) gr_screen.center_w / (float) props.size.width;
|
||||
}
|
||||
|
||||
// don't bother setting anything if we aren't going to need it
|
||||
if (!Cmdline_noscalevid && (scale_by != 1.0f)) {
|
||||
vec3d scale;
|
||||
|
||||
scale.xyz.x = scale_by;
|
||||
scale.xyz.y = scale_by;
|
||||
scale.xyz.z = -1.0f;
|
||||
|
||||
gr_push_scale_matrix(&scale);
|
||||
_scaleVideo = true;
|
||||
}
|
||||
|
||||
int screenX;
|
||||
int screenY;
|
||||
|
||||
if (_scaleVideo) {
|
||||
screenX = fl2i(((gr_screen.center_w / 2.0f + gr_screen.center_offset_x) / scale_by) - (static_cast<int>(props.size.width) / 2.0f) + 0.5f);
|
||||
screenY = fl2i(((gr_screen.center_h / 2.0f + gr_screen.center_offset_y) / scale_by) - (static_cast<int>(props.size.height) / 2.0f) + 0.5f);
|
||||
} else {
|
||||
// centers on 1024x768, fills on 640x480
|
||||
screenX = ((gr_screen.center_w - static_cast<int>(props.size.width)) / 2) + gr_screen.center_offset_x;
|
||||
screenY = ((gr_screen.center_h - static_cast<int>(props.size.height)) / 2) + gr_screen.center_offset_y;
|
||||
}
|
||||
|
||||
// set additional values for screen width/height and UV coords
|
||||
int screenXW = screenX + static_cast<int>(props.size.width);
|
||||
int screenYH = screenY + static_cast<int>(props.size.height);
|
||||
|
||||
Current_shader->program->Uniforms.setUniformi("ytex", 0);
|
||||
Current_shader->program->Uniforms.setUniformi("utex", 1);
|
||||
Current_shader->program->Uniforms.setUniformi("vtex", 2);
|
||||
|
||||
Current_shader->program->Uniforms.setUniformMatrix4f("projMatrix", GL_projection_matrix);
|
||||
Current_shader->program->Uniforms.setUniformMatrix4f("modelViewMatrix", GL_model_view_matrix);
|
||||
|
||||
GLfloat glVertices[4][4] = {{0}};
|
||||
glVertices[0][0] = (GLfloat)screenX;
|
||||
glVertices[0][1] = (GLfloat)screenY;
|
||||
glVertices[0][2] = 0.0f;
|
||||
glVertices[0][3] = 0.0f;
|
||||
|
||||
glVertices[1][0] = (GLfloat)screenX;
|
||||
glVertices[1][1] = (GLfloat)screenYH;
|
||||
glVertices[1][2] = 0.0f;
|
||||
glVertices[1][3] = 1.0f;
|
||||
|
||||
glVertices[2][0] = (GLfloat)screenXW;
|
||||
glVertices[2][1] = (GLfloat)screenY;
|
||||
glVertices[2][2] = 1.0f;
|
||||
glVertices[2][3] = 0.0f;
|
||||
|
||||
glVertices[3][0] = (GLfloat)screenXW;
|
||||
glVertices[3][1] = (GLfloat)screenYH;
|
||||
glVertices[3][2] = 1.0f;
|
||||
glVertices[3][3] = 1.0f;
|
||||
|
||||
_vertexLayout.add_vertex_component(vertex_format_data::POSITION2, sizeof(glVertices[0]), 0);
|
||||
_vertexLayout.add_vertex_component(vertex_format_data::TEX_COORD, sizeof(glVertices[0]), sizeof(GLfloat) * 2);
|
||||
|
||||
gr_update_buffer_data(_vertexBuffer, sizeof(glVertices[0]) * 4, glVertices);
|
||||
}
|
||||
|
||||
OpenGLVideoPresenter::~OpenGLVideoPresenter() {
|
||||
GR_DEBUG_SCOPE("Deinit video");
|
||||
|
||||
if (_scaleVideo) {
|
||||
gr_pop_scale_matrix();
|
||||
}
|
||||
|
||||
gr_delete_buffer(_vertexBuffer);
|
||||
_vertexBuffer = -1;
|
||||
|
||||
GL_state.Texture.Delete(_ytex);
|
||||
GL_state.Texture.Delete(_utex);
|
||||
GL_state.Texture.Delete(_vtex);
|
||||
glDeleteTextures(1, &_ytex);
|
||||
glDeleteTextures(1, &_utex);
|
||||
glDeleteTextures(1, &_vtex);
|
||||
|
||||
opengl_set_texture_target();
|
||||
|
||||
_ytex = _utex = _vtex = 0;
|
||||
opengl_shader_set_current();
|
||||
}
|
||||
|
||||
void OpenGLVideoPresenter::uploadVideoFrame(const VideoFramePtr& frame) {
|
||||
GR_DEBUG_SCOPE("Update video frame");
|
||||
auto ptrs = frame->getDataPointers();
|
||||
|
||||
auto stride = static_cast<GLint>(frame->ySize.stride);
|
||||
auto width = static_cast<GLint>(frame->ySize.width);
|
||||
auto height = static_cast<GLint>(frame->ySize.height);
|
||||
|
||||
GL_state.Texture.SetActiveUnit(0);
|
||||
GL_state.Texture.Enable(_ytex);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
|
||||
glTexSubImage2D(GL_state.Texture.GetTarget(), 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE,
|
||||
ptrs.y);
|
||||
|
||||
stride = static_cast<int>(frame->uvSize.stride);
|
||||
width = static_cast<int>(frame->uvSize.width);
|
||||
height = static_cast<int>(frame->uvSize.height);
|
||||
|
||||
GL_state.Texture.SetActiveUnit(1);
|
||||
GL_state.Texture.Enable(_utex);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
|
||||
glTexSubImage2D(GL_state.Texture.GetTarget(), 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE,
|
||||
ptrs.u);
|
||||
|
||||
GL_state.Texture.SetActiveUnit(2);
|
||||
GL_state.Texture.Enable(_vtex);
|
||||
glTexSubImage2D(GL_state.Texture.GetTarget(), 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE,
|
||||
ptrs.v);
|
||||
|
||||
// Reset this back to default
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
}
|
||||
|
||||
void OpenGLVideoPresenter::displayFrame() {
|
||||
opengl_shader_set_current(_sdr_handle);
|
||||
|
||||
opengl_bind_buffer_object(_vertexBuffer);
|
||||
opengl_bind_vertex_layout(_vertexLayout);
|
||||
|
||||
// Bind textures
|
||||
GL_state.Texture.SetActiveUnit(0);
|
||||
GL_state.Texture.Enable(_ytex);
|
||||
GL_state.Texture.SetActiveUnit(1);
|
||||
GL_state.Texture.Enable(_utex);
|
||||
GL_state.Texture.SetActiveUnit(2);
|
||||
GL_state.Texture.Enable(_vtex);
|
||||
|
||||
GR_DEBUG_SCOPE("Draw video frame");
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
|
||||
#ifndef FS2_OPEN_OPENGLVIDEOPRESENTER_H
|
||||
#define FS2_OPEN_OPENGLVIDEOPRESENTER_H
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "cutscene/player/VideoPresenter.h"
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include <memory>
|
||||
#include <graphics/2d.h>
|
||||
|
||||
namespace cutscene {
|
||||
namespace player {
|
||||
class OpenGLVideoPresenter: public VideoPresenter {
|
||||
int _vertexBuffer = -1;
|
||||
vertex_layout _vertexLayout;
|
||||
int _sdr_handle = -1;
|
||||
|
||||
bool _scaleVideo = false;
|
||||
|
||||
GLuint _ytex = 0;
|
||||
GLuint _utex = 0;
|
||||
GLuint _vtex = 0;
|
||||
public:
|
||||
OpenGLVideoPresenter(const MovieProperties& props);
|
||||
|
||||
virtual ~OpenGLVideoPresenter();
|
||||
|
||||
virtual void uploadVideoFrame(const VideoFramePtr& frame) SCP_OVERRIDE;
|
||||
|
||||
virtual void displayFrame() SCP_OVERRIDE;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif //FS2_OPEN_OPENGLVIDEOPRESENTER_H
|
||||
@@ -1,21 +0,0 @@
|
||||
|
||||
#ifndef FS2_OPEN_VIDEOPRESENTER_H
|
||||
#define FS2_OPEN_VIDEOPRESENTER_H
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "cutscene/Decoder.h"
|
||||
|
||||
namespace cutscene {
|
||||
namespace player {
|
||||
class VideoPresenter {
|
||||
public:
|
||||
virtual ~VideoPresenter() {}
|
||||
|
||||
virtual void uploadVideoFrame(const VideoFramePtr& frame) = 0;
|
||||
|
||||
virtual void displayFrame() = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif //FS2_OPEN_VIDEOPRESENTER_H
|
||||
@@ -74,7 +74,7 @@ static void debris_start_death_roll(object *debris_obj, debris *debris_p)
|
||||
|
||||
// only play debris destroy sound if hull piece and it has been around for at least 2 seconds
|
||||
if ( Missiontime > debris_p->time_started + 2*F1_0 ) {
|
||||
snd_play_3d( &Snds[SND_MISSILE_IMPACT1], &debris_obj->pos, &View_position, debris_obj->radius );
|
||||
snd_play_3d( gamesnd_get_game_sound(SND_MISSILE_IMPACT1), &debris_obj->pos, &View_position, debris_obj->radius );
|
||||
|
||||
}
|
||||
}
|
||||
@@ -324,19 +324,19 @@ void debris_process_post(object * obj, float frame_time)
|
||||
//Play a sound effect
|
||||
if ( lifetime > 750 ) {
|
||||
// 1.00 second effect
|
||||
snd_play_3d( &Snds[SND_DEBRIS_ARC_05], &snd_pos, &View_position, obj->radius );
|
||||
snd_play_3d( gamesnd_get_game_sound(SND_DEBRIS_ARC_05), &snd_pos, &View_position, obj->radius );
|
||||
} else if ( lifetime > 500 ) {
|
||||
// 0.75 second effect
|
||||
snd_play_3d( &Snds[SND_DEBRIS_ARC_04], &snd_pos, &View_position, obj->radius );
|
||||
snd_play_3d( gamesnd_get_game_sound(SND_DEBRIS_ARC_04), &snd_pos, &View_position, obj->radius );
|
||||
} else if ( lifetime > 250 ) {
|
||||
// 0.50 second effect
|
||||
snd_play_3d( &Snds[SND_DEBRIS_ARC_03], &snd_pos, &View_position, obj->radius );
|
||||
snd_play_3d( gamesnd_get_game_sound(SND_DEBRIS_ARC_03), &snd_pos, &View_position, obj->radius );
|
||||
} else if ( lifetime > 100 ) {
|
||||
// 0.25 second effect
|
||||
snd_play_3d( &Snds[SND_DEBRIS_ARC_02], &snd_pos, &View_position, obj->radius );
|
||||
snd_play_3d( gamesnd_get_game_sound(SND_DEBRIS_ARC_02), &snd_pos, &View_position, obj->radius );
|
||||
} else {
|
||||
// 0.10 second effect
|
||||
snd_play_3d( &Snds[SND_DEBRIS_ARC_01], &snd_pos, &View_position, obj->radius );
|
||||
snd_play_3d( gamesnd_get_game_sound(SND_DEBRIS_ARC_01), &snd_pos, &View_position, obj->radius );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,541 @@
|
||||
#include <nebula/neb.h>
|
||||
#include <utility>
|
||||
#include "decals/decals.h"
|
||||
|
||||
#include "graphics/2d.h"
|
||||
#include "graphics/decal_draw_list.h"
|
||||
#include "graphics/util/uniform_structs.h"
|
||||
#include "parse/parselo.h"
|
||||
#include "tracing/tracing.h"
|
||||
#include "ship/ship.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class DecalDefinition {
|
||||
SCP_string _name;
|
||||
|
||||
SCP_string _diffuseFilename;
|
||||
bool _loopDiffuse = true;
|
||||
|
||||
SCP_string _glowFilename;
|
||||
bool _loopGlow = true;
|
||||
|
||||
SCP_string _normalMapFilename;
|
||||
bool _loopNormal = true;
|
||||
|
||||
int _diffuseBitmap = -1;
|
||||
int _glowBitmap = -1;
|
||||
int _normalBitmap = -1;
|
||||
|
||||
public:
|
||||
explicit DecalDefinition(SCP_string name) : _name(std::move(name)) {
|
||||
}
|
||||
|
||||
~DecalDefinition() {
|
||||
if (_diffuseBitmap >= 0) {
|
||||
bm_release(_diffuseBitmap);
|
||||
}
|
||||
if (_glowBitmap >= 0) {
|
||||
bm_release(_glowBitmap);
|
||||
}
|
||||
if (_normalBitmap >= 0) {
|
||||
bm_release(_normalBitmap);
|
||||
}
|
||||
}
|
||||
|
||||
// Disallow copying
|
||||
DecalDefinition(const DecalDefinition&) = delete;
|
||||
DecalDefinition& operator=(const DecalDefinition&) = delete;
|
||||
|
||||
// Move constructor and operator
|
||||
DecalDefinition(DecalDefinition&& other) {
|
||||
*this = std::move(other); // Use operator implementation
|
||||
}
|
||||
DecalDefinition& operator=(DecalDefinition&& other) {
|
||||
std::swap(_name, other._name);
|
||||
|
||||
std::swap(_diffuseFilename, other._diffuseFilename);
|
||||
std::swap(_glowFilename, other._glowFilename);
|
||||
std::swap(_normalMapFilename, other._normalMapFilename);
|
||||
|
||||
std::swap(_diffuseBitmap, other._diffuseBitmap);
|
||||
std::swap(_glowBitmap, other._glowBitmap);
|
||||
std::swap(_normalBitmap, other._normalBitmap);
|
||||
|
||||
std::swap(_loopDiffuse, other._loopDiffuse);
|
||||
std::swap(_loopGlow, other._loopGlow);
|
||||
std::swap(_loopNormal, other._loopNormal);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void parse() {
|
||||
if (optional_string("+Diffuse:")) {
|
||||
stuff_string(_diffuseFilename, F_FILESPEC);
|
||||
|
||||
if (!bm_validate_filename(_diffuseFilename, true, true)) {
|
||||
error_display(0, "Animation '%s' is not valid!", _diffuseFilename.c_str());
|
||||
_diffuseFilename = "";
|
||||
}
|
||||
|
||||
if (optional_string("+Loop:")) {
|
||||
stuff_boolean(&_loopDiffuse);
|
||||
}
|
||||
}
|
||||
if (optional_string("+Glow:")) {
|
||||
stuff_string(_glowFilename, F_FILESPEC);
|
||||
|
||||
if (!bm_validate_filename(_glowFilename, true, true)) {
|
||||
error_display(0, "Animation '%s' is not valid!", _glowFilename.c_str());
|
||||
_glowFilename = "";
|
||||
}
|
||||
|
||||
if (optional_string("+Loop:")) {
|
||||
stuff_boolean(&_loopGlow);
|
||||
}
|
||||
}
|
||||
if (optional_string("+Normal:")) {
|
||||
stuff_string(_normalMapFilename, F_FILESPEC);
|
||||
|
||||
if (!bm_validate_filename(_normalMapFilename, true, true)) {
|
||||
error_display(0, "Animation '%s' is not valid!", _normalMapFilename.c_str());
|
||||
_normalMapFilename = "";
|
||||
}
|
||||
|
||||
if (optional_string("+Loop:")) {
|
||||
stuff_boolean(&_loopNormal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loadBitmaps() {
|
||||
if (_diffuseBitmap == -1 && VALID_FNAME(_diffuseFilename)) {
|
||||
_diffuseBitmap = bm_load_either(_diffuseFilename.c_str());
|
||||
if (_diffuseBitmap == -1) {
|
||||
Warning(LOCATION,
|
||||
"Bitmap '%s' failed to load for decal definition %s!",
|
||||
_diffuseFilename.c_str(),
|
||||
_name.c_str());
|
||||
}
|
||||
}
|
||||
if (_glowBitmap == -1 && VALID_FNAME(_glowFilename)) {
|
||||
_glowBitmap = bm_load_either(_glowFilename.c_str());
|
||||
if (_glowBitmap == -1) {
|
||||
Warning(LOCATION,
|
||||
"Bitmap '%s' failed to load for decal definition %s!",
|
||||
_glowFilename.c_str(),
|
||||
_name.c_str());
|
||||
}
|
||||
}
|
||||
if (_normalBitmap == -1 && VALID_FNAME(_normalMapFilename)) {
|
||||
_normalBitmap = bm_load_either(_normalMapFilename.c_str());
|
||||
if (_normalBitmap == -1) {
|
||||
Warning(LOCATION,
|
||||
"Bitmap '%s' failed to load for decal definition %s!",
|
||||
_normalMapFilename.c_str(),
|
||||
_name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pageIn() {
|
||||
if (_diffuseBitmap >= 0) {
|
||||
bm_page_in_texture(_diffuseBitmap);
|
||||
}
|
||||
if (_glowBitmap >= 0) {
|
||||
bm_page_in_texture(_glowBitmap);
|
||||
}
|
||||
if (_normalBitmap >= 0) {
|
||||
bm_page_in_texture(_normalBitmap);
|
||||
}
|
||||
}
|
||||
|
||||
bool bitmapsLoaded() {
|
||||
// Since both bitmap types are optional we need to check if either is loaded to determine if any bitmap is loaded
|
||||
return _diffuseBitmap >= 0 || _glowBitmap >= 0 || _normalBitmap >= 0;
|
||||
}
|
||||
|
||||
const SCP_string& getName() const {
|
||||
return _name;
|
||||
}
|
||||
int getDiffuseBitmap() const {
|
||||
return _diffuseBitmap;
|
||||
}
|
||||
int getGlowBitmap() const {
|
||||
return _glowBitmap;
|
||||
}
|
||||
int getNormalBitmap() const {
|
||||
return _normalBitmap;
|
||||
}
|
||||
bool isDiffuseLooping() const {
|
||||
return _loopDiffuse;
|
||||
}
|
||||
bool isGlowLooping() const {
|
||||
return _loopGlow;
|
||||
}
|
||||
bool isNormalLooping() const {
|
||||
return _loopNormal;
|
||||
}
|
||||
};
|
||||
|
||||
SCP_vector<DecalDefinition> decalDefinitions;
|
||||
|
||||
// Variable to indicate if the system is able to work correctly on the current system
|
||||
bool decal_system_active = true;
|
||||
|
||||
void parse_decals_table(const char* filename) {
|
||||
try {
|
||||
read_file_text(filename, CF_TYPE_TABLES);
|
||||
reset_parse();
|
||||
|
||||
required_string("#Decals");
|
||||
|
||||
while (optional_string("$Decal:")) {
|
||||
SCP_string name;
|
||||
stuff_string(name, F_NAME);
|
||||
|
||||
DecalDefinition def(name);
|
||||
def.parse();
|
||||
|
||||
decalDefinitions.push_back(std::move(def));
|
||||
}
|
||||
|
||||
required_string("#End");
|
||||
} catch (const parse::ParseException& e) {
|
||||
mprintf(("TABLES: Unable to parse '%s'! Error message = %s.\n", filename, e.what()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gr_is_capable(CAPABILITY_DEFERRED_LIGHTING)) {
|
||||
// we need deferred lighting
|
||||
decal_system_active = false;
|
||||
mprintf(("Note: Decal system has been disabled due to lack of deferred lighting.\n"));
|
||||
}
|
||||
if (!gr_is_capable(CAPABILITY_NORMAL_MAP)) {
|
||||
// We need normal mapping for the full feature range
|
||||
decal_system_active = false;
|
||||
mprintf(("Note: Decal system has been disabled due to lack of normal mapping.\n"));
|
||||
}
|
||||
if (!gr_is_capable(CAPABILITY_SEPARATE_BLEND_FUNCTIONS)) {
|
||||
// WWe need separate blending functions for different color buffers
|
||||
decal_system_active = false;
|
||||
mprintf(("Note: Decal system has been disabled due to lack of separate color buffer blend functions.\n"));
|
||||
}
|
||||
}
|
||||
|
||||
struct Decal {
|
||||
int definition_handle = -1;
|
||||
object_h object;
|
||||
int submodel = -1;
|
||||
|
||||
float creation_time = -1.0f; //!< The mission time at which this decal was created
|
||||
float lifetime = -1.0f; //!< The time this decal is active. When negative it never expires
|
||||
|
||||
vec3d position = vmd_zero_vector;
|
||||
vec3d scale;
|
||||
matrix orientation = vmd_identity_matrix;
|
||||
|
||||
Decal() {
|
||||
vm_vec_make(&scale, 1.f, 1.f, 1.f);
|
||||
}
|
||||
|
||||
bool isValid() {
|
||||
if (!object.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lifetime > 0.0f) {
|
||||
if (f2fl(Missiontime) >= creation_time + lifetime) {
|
||||
// Decal has expired
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto objp = object.objp;
|
||||
if (objp->type == OBJ_SHIP) {
|
||||
auto shipp = &Ships[objp->instance];
|
||||
auto model_instance = model_get_instance(shipp->model_instance_num);
|
||||
|
||||
Assertion(submodel >= 0 && submodel < model_get(object_get_model(objp))->n_models,
|
||||
"Invalid submodel number detected!");
|
||||
auto smi = &model_instance->submodel[submodel];
|
||||
|
||||
if (smi->blown_off) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
SCP_vector<Decal> active_decals;
|
||||
|
||||
bool required_string_if_new(const char* token, bool new_entry) {
|
||||
if (!new_entry) {
|
||||
return optional_string(token) == 1;
|
||||
}
|
||||
|
||||
required_string(token);
|
||||
return true;
|
||||
}
|
||||
|
||||
float clamp(float x, float min, float max) {
|
||||
return std::min(std::max(x, min), max);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Produces a smoothstep value as specified by the GLSL specification
|
||||
* @param edge0
|
||||
* @param edge1
|
||||
* @param x
|
||||
* @return
|
||||
*/
|
||||
float smoothstep(float edge0, float edge1, float x) {
|
||||
auto t = clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
|
||||
return t * t * (3.0f - 2.0f * t);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace decals {
|
||||
|
||||
void initialize() {
|
||||
decalDefinitions.clear();
|
||||
graphics::decal_draw_list::globalInit();
|
||||
|
||||
parse_modular_table(NOX("*-dcl.tbm"), parse_decals_table);
|
||||
}
|
||||
void shutdown() {
|
||||
graphics::decal_draw_list::globalShutdown();
|
||||
|
||||
// Free allocated resources
|
||||
decalDefinitions.clear();
|
||||
}
|
||||
|
||||
int getDecalConfig(const SCP_string& name) {
|
||||
int index = 0;
|
||||
for (auto& def : decalDefinitions) {
|
||||
if (!stricmp(def.getName().c_str(), name.c_str())) {
|
||||
return index;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void parseDecalReference(creation_info& dest_info, bool is_new_entry) {
|
||||
SCP_string name;
|
||||
stuff_string(name, F_NAME);
|
||||
|
||||
auto decalRef = getDecalConfig(name);
|
||||
|
||||
if (decalRef < 0) {
|
||||
error_display(0, "Decal definition '%s' is unknown!", name.c_str());
|
||||
}
|
||||
dest_info.definition_handle = decalRef;
|
||||
|
||||
if (required_string_if_new("+Radius:", is_new_entry)) {
|
||||
stuff_float(&dest_info.radius);
|
||||
|
||||
if (dest_info.radius <= 0.0f) {
|
||||
error_display(0, "Invalid radius of %f! Must be a positive number.", dest_info.radius);
|
||||
dest_info.radius = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (required_string_if_new("+Lifetime:", is_new_entry)) {
|
||||
if (optional_string("Eternal")) {
|
||||
dest_info.lifetime = util::UniformFloatRange(-1.0f);
|
||||
} else {
|
||||
// Require at least a small lifetime so that the calculations don't have to deal with div-by-zero
|
||||
dest_info.lifetime = util::parseUniformRange(0.0001f);
|
||||
}
|
||||
}
|
||||
}
|
||||
void loadBitmaps(const creation_info& info) {
|
||||
if (!decal_system_active) {
|
||||
return;
|
||||
}
|
||||
// Silently ignore invalid definition handle since weapons use the default values if the decal option is not present
|
||||
if (info.definition_handle < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Assertion(info.definition_handle >= 0 && info.definition_handle < (int) decalDefinitions.size(),
|
||||
"Invalid decal handle detected!");
|
||||
|
||||
auto& def = decalDefinitions[info.definition_handle];
|
||||
|
||||
def.loadBitmaps();
|
||||
}
|
||||
void pageInDecal(const creation_info& info) {
|
||||
if (!decal_system_active) {
|
||||
return;
|
||||
}
|
||||
// Silently ignore invalid definition handle since weapons use the default values if the decal option is not present
|
||||
if (info.definition_handle < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Assertion(info.definition_handle >= 0 && info.definition_handle < (int) decalDefinitions.size(),
|
||||
"Invalid decal handle detected!");
|
||||
|
||||
decalDefinitions[info.definition_handle].pageIn();
|
||||
}
|
||||
|
||||
void initializeMission() {
|
||||
active_decals.clear();
|
||||
}
|
||||
|
||||
matrix4 getDecalTransform(Decal& decal) {
|
||||
Assertion(decal.object.objp->type == OBJ_SHIP, "Only ships are currently supported for decals!");
|
||||
|
||||
auto objp = decal.object.objp;
|
||||
auto ship = &Ships[objp->instance];
|
||||
|
||||
vec3d worldPos;
|
||||
model_instance_find_world_point(&worldPos,
|
||||
&decal.position,
|
||||
ship->model_instance_num,
|
||||
decal.submodel,
|
||||
&objp->orient,
|
||||
&objp->pos);
|
||||
|
||||
vec3d worldDir;
|
||||
model_instance_find_world_dir(&worldDir,
|
||||
&decal.orientation.vec.fvec,
|
||||
ship->model_instance_num,
|
||||
decal.submodel,
|
||||
&objp->orient);
|
||||
|
||||
vec3d worldUp;
|
||||
model_instance_find_world_dir(&worldUp,
|
||||
&decal.orientation.vec.fvec,
|
||||
ship->model_instance_num,
|
||||
decal.submodel,
|
||||
&objp->orient);
|
||||
|
||||
matrix worldOrient;
|
||||
vm_vector_2_matrix(&worldOrient, &worldDir, &worldUp);
|
||||
|
||||
// Apply scaling
|
||||
worldOrient.a2d[0][0] *= decal.scale.xyz.x;
|
||||
worldOrient.a2d[0][1] *= decal.scale.xyz.x;
|
||||
worldOrient.a2d[0][2] *= decal.scale.xyz.x;
|
||||
worldOrient.a2d[1][0] *= decal.scale.xyz.y;
|
||||
worldOrient.a2d[1][1] *= decal.scale.xyz.y;
|
||||
worldOrient.a2d[1][2] *= decal.scale.xyz.y;
|
||||
worldOrient.a2d[2][0] *= decal.scale.xyz.z;
|
||||
worldOrient.a2d[2][1] *= decal.scale.xyz.z;
|
||||
worldOrient.a2d[2][2] *= decal.scale.xyz.z;
|
||||
|
||||
matrix4 mat4;
|
||||
vm_matrix4_set_transform(&mat4, &worldOrient, &worldPos);
|
||||
|
||||
return mat4;
|
||||
}
|
||||
|
||||
void renderAll() {
|
||||
if (!decal_system_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear out any invalid decals
|
||||
for (auto iter = active_decals.begin(); iter != active_decals.end(); ++iter) {
|
||||
if (!iter->isValid()) {
|
||||
// if we're sitting on the very last element, popping-back will invalidate the iterator!
|
||||
if (iter + 1 == active_decals.end()) {
|
||||
active_decals.pop_back();
|
||||
break;
|
||||
}
|
||||
|
||||
*iter = active_decals.back();
|
||||
active_decals.pop_back();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (active_decals.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
graphics::decal_draw_list draw_list;
|
||||
auto mission_time = f2fl(Missiontime);
|
||||
|
||||
for (auto& decal : active_decals) {
|
||||
int diffuse_bm = -1;
|
||||
int glow_bm = -1;
|
||||
int normal_bm = -1;
|
||||
|
||||
Assertion(decal.definition_handle >= 0 && decal.definition_handle < (int) decalDefinitions.size(),
|
||||
"Invalid decal handle detected!");
|
||||
auto& decalDef = decalDefinitions[decal.definition_handle];
|
||||
|
||||
auto decal_time = mission_time - decal.creation_time;
|
||||
auto progress = decal_time / decal.lifetime;
|
||||
|
||||
float alpha = 1.0f;
|
||||
if (progress > 0.8) {
|
||||
// Fade the decal out for the last 20% of its lifetime
|
||||
alpha = 1.0f - smoothstep(0.8f, 1.0f, progress);
|
||||
}
|
||||
|
||||
if (decalDef.getDiffuseBitmap() >= 0) {
|
||||
diffuse_bm = decalDef.getDiffuseBitmap()
|
||||
+ bm_get_anim_frame(decalDef.getDiffuseBitmap(), decal_time, 0.0f, decalDef.isDiffuseLooping());
|
||||
}
|
||||
|
||||
if (decalDef.getGlowBitmap() >= 0) {
|
||||
glow_bm = decalDef.getGlowBitmap()
|
||||
+ bm_get_anim_frame(decalDef.getGlowBitmap(), decal_time, 0.0f, decalDef.isGlowLooping());
|
||||
}
|
||||
|
||||
if (decalDef.getNormalBitmap() >= 0) {
|
||||
normal_bm = decalDef.getNormalBitmap()
|
||||
+ bm_get_anim_frame(decalDef.getNormalBitmap(), decal_time, 0.0f, decalDef.isNormalLooping());
|
||||
}
|
||||
|
||||
draw_list.add_decal(diffuse_bm, glow_bm, normal_bm, decal_time, getDecalTransform(decal), alpha);
|
||||
}
|
||||
|
||||
draw_list.render();
|
||||
}
|
||||
void
|
||||
addDecal(creation_info& info, object* host, int submodel, const vec3d& local_pos, const matrix& local_orient) {
|
||||
if (!decal_system_active) {
|
||||
return;
|
||||
}
|
||||
// Silently ignore invalid definition handle since weapons use the default values if the decal option is not present
|
||||
if (info.definition_handle < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Assertion(info.definition_handle >= 0 && info.definition_handle < (int) decalDefinitions.size(),
|
||||
"Invalid decal handle detected!");
|
||||
auto& def = decalDefinitions[info.definition_handle];
|
||||
|
||||
if (!def.bitmapsLoaded()) {
|
||||
// If this decal was never used before then the bitmaps are not loaded so we need to do that here.
|
||||
def.loadBitmaps();
|
||||
}
|
||||
|
||||
Decal newDecal;
|
||||
newDecal.definition_handle = info.definition_handle;
|
||||
newDecal.object = object_h(host);
|
||||
newDecal.submodel = submodel;
|
||||
newDecal.creation_time = f2fl(Missiontime);
|
||||
newDecal.lifetime = info.lifetime.next();
|
||||
|
||||
newDecal.position = local_pos;
|
||||
newDecal.orientation = local_orient;
|
||||
newDecal.scale.xyz.x = info.radius;
|
||||
newDecal.scale.xyz.y = info.radius;
|
||||
newDecal.scale.xyz.z = info.radius;
|
||||
|
||||
active_decals.push_back(newDecal);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
|
||||
#include "object/object.h"
|
||||
|
||||
#include "utils/RandomRange.h"
|
||||
|
||||
namespace decals {
|
||||
|
||||
/**
|
||||
* @brief A reference to a decal definition
|
||||
*/
|
||||
typedef int DecalReference;
|
||||
|
||||
/**
|
||||
* @brief A structure containing all information for creating a decal
|
||||
*/
|
||||
struct creation_info {
|
||||
DecalReference definition_handle = -1;
|
||||
float radius = -1.0f;
|
||||
util::UniformFloatRange lifetime = ::util::UniformFloatRange(-1.0f);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Initializes the global state of the decal system. Call once at game init.
|
||||
*/
|
||||
void initialize();
|
||||
|
||||
|
||||
/**
|
||||
* @brief Parses the information for a decal reference
|
||||
*
|
||||
* @details This function expects that the initial token has already been consumed by the parse system.
|
||||
*
|
||||
* @param dest_info The creation information struct to parse the information into
|
||||
* @param is_new_entry Set to @c false if the currently parsed object used +nocreate
|
||||
*/
|
||||
void parseDecalReference(creation_info& dest_info, bool is_new_entry = true);
|
||||
|
||||
/**
|
||||
* @brief Loads the bitmaps specified by the creation info into texture memory
|
||||
*
|
||||
* This should be called when the bitmaps of the object using this decal are loaded.
|
||||
*
|
||||
* @param info The creation information containing the decal definition handle
|
||||
*/
|
||||
void loadBitmaps(const creation_info& info);
|
||||
|
||||
/**
|
||||
* @brief Page in the bitmap data of the specified creation information. Needs to be called after loadBitmaps
|
||||
* @param info The information to page in
|
||||
*/
|
||||
void pageInDecal(const creation_info& info);
|
||||
|
||||
/**
|
||||
* @brief Shut down the decal system. Must be called from game_shutdown
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief Initialize the decal system for the mission. Must be called before every mission is started
|
||||
*/
|
||||
void initializeMission();
|
||||
|
||||
/**
|
||||
* @brief Renders all currently active decals and removes invalid decals from the active list.
|
||||
*/
|
||||
void renderAll();
|
||||
|
||||
/**
|
||||
* @brief Creates a new decal on the specified object at the specified location
|
||||
*
|
||||
* @param info The decal to create
|
||||
* @param host The object host on which to create this decal. Must be a ship.
|
||||
* @param submodel The submodel of the object model on which to create the decal.
|
||||
* @param local_pos The position in the frame of reference of the submodel at which the decal should be created.
|
||||
* @param local_orient The orientation of the decal in the frame of reference of the submodel. The forward vector of the
|
||||
* orientation should look along the direction
|
||||
*/
|
||||
void addDecal(creation_info& info,
|
||||
object* host,
|
||||
int submodel,
|
||||
const vec3d& local_pos,
|
||||
const matrix& local_orient);
|
||||
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
#ControlConfigOverride
|
||||
#End
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
#include "gamma.sdr"
|
||||
|
||||
in vec4 fragTexCoord;
|
||||
in vec4 fragColor;
|
||||
out vec4 fragOut0;
|
||||
uniform sampler2DArray baseMap;
|
||||
uniform float intensity;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 baseColor = texture(baseMap, fragTexCoord.xyz);
|
||||
|
||||
baseColor.rgb = srgb_to_linear(baseColor.rgb);
|
||||
vec4 blendColor = vec4(srgb_to_linear(fragColor.rgb), fragColor.a);
|
||||
|
||||
fragOut0 = baseColor * blendColor * intensity;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
in vec4 vertPosition;
|
||||
in vec4 vertTexCoord;
|
||||
in vec4 vertColor;
|
||||
out vec4 fragTexCoord;
|
||||
out vec4 fragColor;
|
||||
uniform mat4 modelViewMatrix;
|
||||
uniform mat4 projMatrix;
|
||||
uniform vec4 color;
|
||||
void main()
|
||||
{
|
||||
fragTexCoord = vec4(vertTexCoord.xyz, 0.0);
|
||||
fragColor = vertColor * color;
|
||||
gl_Position = projMatrix * modelViewMatrix * vertPosition;
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,180 @@
|
||||
|
||||
// NOTE: The technique and some of this code is based on this tutorial:
|
||||
// http://martindevans.me/game-development/2015/02/27/Drawing-Stuff-On-Other-Stuff-With-Deferred-Screenspace-Decals/
|
||||
|
||||
#include "lighting.sdr"
|
||||
#include "normals.sdr"
|
||||
#include "gamma.sdr"
|
||||
|
||||
out vec4 fragOut0; // Diffuse buffer
|
||||
out vec4 fragOut1; // Normal buffer
|
||||
out vec4 fragOut2; // Emissive buffer
|
||||
|
||||
uniform sampler2D gDepthBuffer;
|
||||
uniform sampler2D gNormalBuffer;
|
||||
|
||||
uniform sampler2DArray diffuseMap;
|
||||
uniform sampler2DArray glowMap;
|
||||
uniform sampler2DArray normalMap;
|
||||
|
||||
layout (std140) uniform decalGlobalData {
|
||||
mat4 viewMatrix;
|
||||
mat4 projMatrix;
|
||||
mat4 invViewMatrix;
|
||||
mat4 invProjMatrix;
|
||||
|
||||
vec3 ambientLight;
|
||||
|
||||
vec2 viewportSize;
|
||||
};
|
||||
layout (std140) uniform decalInfoData {
|
||||
mat4 model_matrix;
|
||||
mat4 inv_model_matrix;
|
||||
|
||||
vec3 decal_direction;
|
||||
float normal_angle_cutoff;
|
||||
|
||||
int diffuse_index;
|
||||
int glow_index;
|
||||
int normal_index;
|
||||
float angle_fade_start;
|
||||
|
||||
float alpha_scale;
|
||||
int diffuse_blend_mode;
|
||||
int glow_blend_mode;
|
||||
};
|
||||
|
||||
vec3 computeViewPosition(vec2 textureCoord) {
|
||||
vec4 clipSpaceLocation;
|
||||
vec2 normalizedCoord = textureCoord / viewportSize;
|
||||
|
||||
clipSpaceLocation.xy = normalizedCoord * 2.0f - 1.0f;
|
||||
clipSpaceLocation.z = texelFetch(gDepthBuffer, ivec2(textureCoord), 0).r * 2.0f - 1.0f;
|
||||
clipSpaceLocation.w = 1.0f;
|
||||
|
||||
vec4 homogenousLocation = invProjMatrix * clipSpaceLocation;
|
||||
|
||||
return homogenousLocation.xyz / homogenousLocation.w;
|
||||
}
|
||||
|
||||
vec3 getPixelNormal(vec3 frag_position, vec2 tex_coord, inout float alpha, out vec3 binormal, out vec3 tangent) {
|
||||
#ifdef USE_NORMAL_MAP
|
||||
// If we can then we just use the existing normal buffer
|
||||
vec3 normal = texelFetch(gNormalBuffer, ivec2(tex_coord), 0).xyz;
|
||||
|
||||
// If we use the normal map then we don't really need these values so we don't need to compute them here
|
||||
binormal = vec3(0.0);
|
||||
tangent = vec3(0.0);
|
||||
#else
|
||||
// Use some fancy screen-space derivates to determine the normal of the current pixel by looking at the surrounding pixels
|
||||
vec3 pos_dx = dFdx(frag_position);
|
||||
vec3 pos_dy = dFdy(frag_position);
|
||||
|
||||
vec3 normal = normalize(cross(pos_dx, pos_dy));
|
||||
|
||||
binormal = normalize(pos_dx);
|
||||
tangent = normalize(pos_dy);
|
||||
#endif
|
||||
|
||||
//Calculate angle between surface normal and decal direction
|
||||
float angle = acos(dot(normal, decal_direction));
|
||||
|
||||
if (angle > normal_angle_cutoff) {
|
||||
// The angle between surface normal and decal direction is too big
|
||||
discard;
|
||||
}
|
||||
|
||||
// Make a smooth alpha transition leading up to an edge
|
||||
alpha = alpha * (1 - smoothstep(angle_fade_start, normal_angle_cutoff, angle));
|
||||
|
||||
return normal;
|
||||
}
|
||||
|
||||
vec2 getDecalTexCoord(vec3 view_pos, inout float alpha) {
|
||||
vec4 object_pos = inv_model_matrix * invViewMatrix * vec4(view_pos, 1.0);
|
||||
|
||||
bvec3 invalidComponents = greaterThan(abs(object_pos.xyz), vec3(0.5));
|
||||
bvec4 nanComponents = isnan(object_pos); // nan can happen some times if we have an infinite depth value
|
||||
|
||||
if (any(invalidComponents) || any(nanComponents)) {
|
||||
// Fragment is out of the box
|
||||
discard;
|
||||
}
|
||||
|
||||
// Fade out the texture when it gets close to the top or bottom of the decal box
|
||||
alpha = alpha * (1.0 - smoothstep(0.4, 0.5, abs(object_pos.z)));
|
||||
|
||||
return object_pos.xy + 0.5;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 frag_position = computeViewPosition(gl_FragCoord.xy);
|
||||
|
||||
float alpha = alpha_scale;
|
||||
vec2 tex_coord = getDecalTexCoord(frag_position, alpha);
|
||||
|
||||
vec3 binormal;
|
||||
vec3 tangent;
|
||||
vec3 normal = getPixelNormal(frag_position, gl_FragCoord.xy, alpha, binormal, tangent);
|
||||
|
||||
vec4 diffuse_out = vec4(0.0);
|
||||
vec4 emissive_out = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec3 normal_out = vec3(0.0);
|
||||
|
||||
if (diffuse_index >= 0) {
|
||||
// We have a valid diffuse map
|
||||
vec4 color = texture(diffuseMap, vec3(tex_coord, float(diffuse_index)));
|
||||
|
||||
color.rgb = srgb_to_linear(color.rgb);
|
||||
|
||||
if (diffuse_blend_mode == 0) {
|
||||
// Normal alpha blending
|
||||
diffuse_out = vec4(color.rgb, color.a * alpha);
|
||||
} else {
|
||||
// Additive blending
|
||||
diffuse_out = vec4(color.rgb * alpha, 1.0);
|
||||
}
|
||||
|
||||
// The main model shader applies ambient lighting by drawing the ambient part of the texture into the emissive
|
||||
// texture. We do the same here to make sure the decal material is applied correctly
|
||||
if (glow_blend_mode == 0) {
|
||||
// Normal alpha blending
|
||||
emissive_out = vec4(color.rgb * ambientLight, color.a * alpha);
|
||||
} else {
|
||||
// Additive blending
|
||||
emissive_out = vec4(alpha * color.rgb * ambientLight, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
if (glow_index >= 0) {
|
||||
// We have a valid diffuse map
|
||||
vec4 color = texture(glowMap, vec3(tex_coord, float(glow_index)));
|
||||
|
||||
color.rgb = srgb_to_linear(color.rgb) * GLOW_MAP_SRGB_MULTIPLIER;
|
||||
|
||||
color.rgb *= GLOW_MAP_INTENSITY;
|
||||
|
||||
if (glow_blend_mode == 0) {
|
||||
// Normal alpha blending
|
||||
emissive_out = vec4(color.rgb + emissive_out.rgb * emissive_out.a, color.a * alpha);
|
||||
} else {
|
||||
// Additive blending
|
||||
emissive_out.rgb += color.rgb * alpha;
|
||||
}
|
||||
}
|
||||
|
||||
if (normal_index >= 0) {
|
||||
vec3 decalNormal = unpackNormal(texture(normalMap, vec3(tex_coord, float(normal_index))).ag);
|
||||
|
||||
mat3 tangentToView;
|
||||
tangentToView[0] = tangent;
|
||||
tangentToView[1] = binormal;
|
||||
tangentToView[2] = normal;
|
||||
|
||||
normal_out = tangentToView * decalNormal * alpha;
|
||||
}
|
||||
|
||||
fragOut0 = diffuse_out;
|
||||
fragOut1 = vec4(normal_out, 0.0);
|
||||
fragOut2 = emissive_out;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
in vec4 vertPosition;
|
||||
|
||||
layout (std140) uniform decalGlobalData {
|
||||
mat4 viewMatrix;
|
||||
mat4 projMatrix;
|
||||
mat4 invViewMatrix;
|
||||
mat4 invProjMatrix;
|
||||
|
||||
vec3 ambientLight;
|
||||
|
||||
vec2 viewportSize;
|
||||
};
|
||||
layout (std140) uniform decalInfoData {
|
||||
mat4 model_matrix;
|
||||
mat4 inv_model_matrix;
|
||||
|
||||
vec3 decal_direction;
|
||||
float normal_angle_cutoff;
|
||||
|
||||
int diffuse_index;
|
||||
int glow_index;
|
||||
int normal_index;
|
||||
float angle_fade_start;
|
||||
|
||||
float alpha_scale;
|
||||
int diffuse_blend_mode;
|
||||
int glow_blend_mode;
|
||||
};
|
||||
|
||||
void main() {
|
||||
gl_Position = projMatrix * viewMatrix * model_matrix * vertPosition;
|
||||
}
|
||||
+11
-6
@@ -1,18 +1,23 @@
|
||||
|
||||
#include "gamma.sdr"
|
||||
|
||||
in vec4 fragTexCoord;
|
||||
in vec4 fragColor;
|
||||
out vec4 fragOut0;
|
||||
uniform sampler2D baseMap;
|
||||
|
||||
uniform sampler2DArray baseMap;
|
||||
uniform int baseMapIndex;
|
||||
uniform int alphaTexture;
|
||||
uniform int noTexturing;
|
||||
uniform int srgb;
|
||||
uniform float intensity;
|
||||
uniform float alphaThreshold;
|
||||
#define SRGB_GAMMA 2.2
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 baseColor = texture(baseMap, fragTexCoord.xy);
|
||||
vec4 baseColor = texture(baseMap, vec3(fragTexCoord.xy, float(baseMapIndex)));
|
||||
if(alphaThreshold > baseColor.a) discard;
|
||||
baseColor.rgb = (srgb == 1) ? pow(baseColor.rgb, vec3(SRGB_GAMMA)) : baseColor.rgb;
|
||||
vec4 blendColor = (srgb == 1) ? vec4(pow(fragColor.rgb, vec3(SRGB_GAMMA)), fragColor.a) : fragColor;
|
||||
baseColor.rgb = (srgb == 1) ? srgb_to_linear(baseColor.rgb) : baseColor.rgb;
|
||||
vec4 blendColor = (srgb == 1) ? vec4(srgb_to_linear(fragColor.rgb), fragColor.a) : fragColor;
|
||||
fragOut0 = mix(mix(baseColor * blendColor, vec4(blendColor.rgb, baseColor.r * blendColor.a), float(alphaTexture)), blendColor, float(noTexturing)) * intensity;
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -2,10 +2,12 @@ out vec4 fragOut0;
|
||||
out vec4 fragOut1;
|
||||
out vec4 fragOut2;
|
||||
out vec4 fragOut3;
|
||||
out vec4 fragOut4;
|
||||
void main()
|
||||
{
|
||||
fragOut0 = vec4(0.0, 0.0, 0.0, 1.0); // color
|
||||
fragOut1 = vec4(0.0, 0.0, -1000000.0, 1.0); // position
|
||||
fragOut2 = vec4(0.0, 0.0, 0.0, 1.0); // normal
|
||||
fragOut3 = vec4(0.0, 0.0, 0.0, 0.0); // specular
|
||||
}
|
||||
fragOut4 = vec4(0.0, 0.0, 0.0, 1.0); // emissive
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,138 @@
|
||||
|
||||
#include "lighting.sdr"
|
||||
|
||||
#include "shadows.sdr"
|
||||
|
||||
in vec3 beamVec;
|
||||
in vec3 lightPosition;
|
||||
out vec4 fragOut0;
|
||||
|
||||
uniform sampler2D ColorBuffer;
|
||||
uniform sampler2D NormalBuffer;
|
||||
uniform sampler2D PositionBuffer;
|
||||
uniform sampler2D SpecBuffer;
|
||||
uniform sampler2DArray shadow_map;
|
||||
|
||||
layout (std140) uniform globalDeferredData {
|
||||
mat4 shadow_mv_matrix;
|
||||
mat4 shadow_proj_matrix[4];
|
||||
|
||||
mat4 inv_view_matrix;
|
||||
|
||||
float veryneardist;
|
||||
float neardist;
|
||||
float middist;
|
||||
float fardist;
|
||||
|
||||
float specFactor;
|
||||
float invScreenWidth;
|
||||
float invScreenHeight;
|
||||
};
|
||||
|
||||
layout (std140) uniform lightData {
|
||||
vec3 diffuseLightColor;
|
||||
float coneAngle;
|
||||
|
||||
vec3 specLightColor;
|
||||
float coneInnerAngle;
|
||||
|
||||
vec3 coneDir;
|
||||
bool dualCone;
|
||||
|
||||
vec3 scale;
|
||||
float lightRadius;
|
||||
|
||||
vec3 lightDir;
|
||||
int lightType;
|
||||
|
||||
bool enable_shadows;
|
||||
};
|
||||
|
||||
void GetLightInfo(vec3 position, out vec3 lightDir, out float attenuation)
|
||||
{
|
||||
if (lightType == LT_DIRECTIONAL) {
|
||||
lightDir = normalize(lightPosition);
|
||||
attenuation = 1.0;
|
||||
} else {
|
||||
// Positional light source
|
||||
lightDir = lightPosition - position.xyz;
|
||||
float dist = length(lightDir);
|
||||
attenuation = 1.0 - clamp(dist / lightRadius, 0.0, 1.0);
|
||||
|
||||
if(dist > lightRadius && lightType != LT_TUBE) {
|
||||
discard;
|
||||
}
|
||||
|
||||
if (lightType == LT_TUBE) { // Tube light
|
||||
float beamLength = length(beamVec);
|
||||
vec3 beamDir = beamVec / beamLength;
|
||||
// Get nearest point on line
|
||||
float neardist = clamp(dot(lightDir, beamDir), 0.0, beamLength);
|
||||
// Move back from the endpoint of the beam along the beam by the distance we calculated
|
||||
vec3 nearest = lightPosition - beamDir * neardist;
|
||||
lightDir = nearest - position.xyz;
|
||||
dist = length(lightDir);
|
||||
if(dist > lightRadius) {
|
||||
discard;
|
||||
}
|
||||
} else if (lightType == LT_CONE) {
|
||||
float coneDot = dot(normalize(-lightDir), coneDir);
|
||||
if(dualCone) {
|
||||
if(abs(coneDot) < coneAngle) {
|
||||
discard;
|
||||
} else {
|
||||
attenuation *= smoothstep(coneAngle, coneInnerAngle, abs(coneDot));
|
||||
}
|
||||
} else {
|
||||
if (coneDot < coneAngle) {
|
||||
discard;
|
||||
} else {
|
||||
attenuation *= smoothstep(coneAngle, coneInnerAngle, coneDot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lightDir = normalize(lightDir);
|
||||
}
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 screenPos = gl_FragCoord.xy * vec2(invScreenWidth, invScreenHeight);
|
||||
vec3 position = texture(PositionBuffer, screenPos).xyz;
|
||||
|
||||
if(abs(dot(position, position)) < 0.1)
|
||||
discard;
|
||||
|
||||
vec3 color = texture(ColorBuffer, screenPos).rgb;
|
||||
vec4 normalData = texture(NormalBuffer, screenPos);
|
||||
vec4 specColor = texture(SpecBuffer, screenPos);
|
||||
// The vector in the normal buffer could be longer than the unit vector since decal rendering only adds to the normal buffer
|
||||
vec3 normal = normalize(normalData.xyz);
|
||||
float gloss = normalData.a;
|
||||
float fresnel = specColor.a;
|
||||
vec3 eyeDir = normalize(-position);
|
||||
|
||||
vec3 lightDir;
|
||||
float attenuation;
|
||||
|
||||
GetLightInfo(position, lightDir, attenuation);
|
||||
|
||||
if (enable_shadows) {
|
||||
vec4 fragShadowPos = shadow_mv_matrix * inv_view_matrix * vec4(position, 1.0);
|
||||
vec4 fragShadowUV[4];
|
||||
fragShadowUV[0] = transformToShadowMap(shadow_proj_matrix[0], 0, fragShadowPos);
|
||||
fragShadowUV[1] = transformToShadowMap(shadow_proj_matrix[1], 1, fragShadowPos);
|
||||
fragShadowUV[2] = transformToShadowMap(shadow_proj_matrix[2], 2, fragShadowPos);
|
||||
fragShadowUV[3] = transformToShadowMap(shadow_proj_matrix[3], 3, fragShadowPos);
|
||||
|
||||
attenuation *= getShadowValue(shadow_map, -position.z, fragShadowPos.z, fragShadowUV, fardist, middist,
|
||||
neardist, veryneardist);
|
||||
}
|
||||
|
||||
vec3 halfVec = normalize(lightDir + eyeDir);
|
||||
float NdotL = clamp(dot(normal, lightDir), 0.0, 1.0);
|
||||
vec4 fragmentColor = vec4(color * (diffuseLightColor * NdotL * attenuation), 1.0);
|
||||
fragmentColor.rgb += computeLighting(specColor.rgb, lightDir, normal.xyz, halfVec, eyeDir, gloss, fresnel, NdotL).rgb * specLightColor * attenuation;
|
||||
fragOut0 = max(fragmentColor, vec4(0.0));
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
#include "lighting.sdr"
|
||||
|
||||
in vec4 vertPosition;
|
||||
uniform mat4 modelViewMatrix;
|
||||
uniform mat4 projMatrix;
|
||||
|
||||
layout (std140) uniform lightData {
|
||||
vec3 diffuseLightColor;
|
||||
float coneAngle;
|
||||
|
||||
vec3 specLightColor;
|
||||
float coneInnerAngle;
|
||||
|
||||
vec3 coneDir;
|
||||
bool dualCone;
|
||||
|
||||
vec3 scale;
|
||||
float lightRadius;
|
||||
|
||||
vec3 lightDir;
|
||||
int lightType;
|
||||
|
||||
bool enable_shadows;
|
||||
};
|
||||
|
||||
out vec3 lightPosition;
|
||||
out vec3 beamVec;
|
||||
void main()
|
||||
{
|
||||
if (lightType == LT_DIRECTIONAL) {
|
||||
gl_Position = vec4(vertPosition.xyz, 1.0);
|
||||
lightPosition = lightDir;
|
||||
} else {
|
||||
gl_Position = projMatrix * modelViewMatrix * vec4(vertPosition.xyz * scale, 1.0);
|
||||
lightPosition = modelViewMatrix[3].xyz;
|
||||
if(lightType == LT_TUBE)
|
||||
beamVec = vec3(modelViewMatrix * vec4(0.0, 0.0, -scale.z, 0.0));
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -2,7 +2,7 @@ in vec4 fragTexCoord;
|
||||
in vec4 fragColor;
|
||||
in float fragOffset;
|
||||
out vec4 fragOut0;
|
||||
uniform sampler2D baseMap;
|
||||
uniform sampler2DArray baseMap;
|
||||
uniform sampler2D depthMap;
|
||||
uniform float window_width;
|
||||
uniform float window_height;
|
||||
@@ -11,10 +11,10 @@ uniform sampler2D frameBuffer;
|
||||
void main()
|
||||
{
|
||||
vec2 depthCoord = vec2(gl_FragCoord.x / window_width, gl_FragCoord.y / window_height);
|
||||
vec4 fragmentColor = texture(baseMap, fragTexCoord.xy) * fragColor.a;
|
||||
vec4 fragmentColor = texture(baseMap, fragTexCoord.xyz) * fragColor.a;
|
||||
vec2 distortion = texture(distMap, fragTexCoord.xy+vec2(0.0, fragOffset)).rg;
|
||||
float alpha = clamp(dot(fragmentColor.rgb,vec3(0.3333))*10.0,0.0,1.0);
|
||||
distortion = ((distortion - 0.5) * 0.01) * alpha;
|
||||
fragOut0 = texture(frameBuffer,depthCoord+distortion);
|
||||
fragOut0.a = alpha;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -12,6 +12,6 @@ void main()
|
||||
{
|
||||
gl_Position = projMatrix * modelViewMatrix * vertPosition;
|
||||
fragOffset = vertRadius * use_offset;
|
||||
fragTexCoord = vertTexCoord;
|
||||
fragTexCoord = vec4(vertTexCoord.xyz, 0.0);
|
||||
fragColor = vertColor;
|
||||
}
|
||||
}
|
||||
+9
-6
@@ -1,4 +1,7 @@
|
||||
uniform sampler2D baseMap;
|
||||
|
||||
#include "gamma.sdr"
|
||||
|
||||
uniform sampler2DArray baseMap;
|
||||
uniform sampler2D depthMap;
|
||||
uniform float window_width;
|
||||
uniform float window_height;
|
||||
@@ -12,12 +15,12 @@ in vec4 fragPosition;
|
||||
in vec4 fragTexCoord;
|
||||
in vec4 fragColor;
|
||||
out vec4 fragOut0;
|
||||
#define SRGB_GAMMA 2.2
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 fragmentColor = texture(baseMap, fragTexCoord.xy);
|
||||
fragmentColor.rgb = mix(fragmentColor.rgb, pow(fragmentColor.rgb, vec3(SRGB_GAMMA)), float(srgb));
|
||||
fragmentColor *= mix(fragColor, vec4(pow(fragColor.rgb, vec3(SRGB_GAMMA)), fragColor.a), float(srgb));
|
||||
vec4 fragmentColor = texture(baseMap, fragTexCoord.xyz);
|
||||
fragmentColor.rgb = mix(fragmentColor.rgb, srgb_to_linear(fragmentColor.rgb), float(srgb));
|
||||
fragmentColor *= mix(fragColor, vec4(srgb_to_linear(fragColor.rgb), fragColor.a), float(srgb));
|
||||
vec2 offset = vec2(fragRadius * abs(0.5 - fragTexCoord.x) * 2.0, fragRadius * abs(0.5 - fragTexCoord.y) * 2.0);
|
||||
float offset_len = length(offset);
|
||||
if ( offset_len > fragRadius ) {
|
||||
@@ -43,4 +46,4 @@ void main()
|
||||
fragmentColor.rgb *= (srgb == 1) ? 1.5 : 1.0;
|
||||
fragmentColor = (blend_alpha == 1) ? vec4(fragmentColor.rgb, fragmentColor.a * intensity) : vec4(fragmentColor.rgb * intensity, fragmentColor.a);
|
||||
fragOut0 = max(fragmentColor, vec4(0.0));
|
||||
}
|
||||
}
|
||||
+6
-5
@@ -3,6 +3,7 @@ layout (triangle_strip, max_vertices = 4) out;
|
||||
in vec3 geoUvec[];
|
||||
in float geoRadius[];
|
||||
in vec4 geoColor[];
|
||||
in float geoArrayIndex[];
|
||||
out float fragRadius;
|
||||
out vec4 fragPosition;
|
||||
out vec4 fragTexCoord;
|
||||
@@ -18,7 +19,7 @@ void main(void)
|
||||
pos = (gl_in[0].gl_Position - vec4(geoRadius[0] * up_vec, 0.0) - vec4(geoRadius[0] * right_vec, 0.0));
|
||||
gl_Position = projMatrix * pos;
|
||||
fragPosition = pos;
|
||||
fragTexCoord = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
fragTexCoord = vec4(0.0, 0.0, geoArrayIndex[0], 0.0);
|
||||
fragRadius = geoRadius[0];
|
||||
fragColor = geoColor[0];
|
||||
EmitVertex();
|
||||
@@ -26,7 +27,7 @@ void main(void)
|
||||
pos = (gl_in[0].gl_Position - vec4(geoRadius[0] * up_vec, 0.0) + vec4(geoRadius[0] * right_vec, 0.0));
|
||||
gl_Position = projMatrix * pos;
|
||||
fragPosition = pos;
|
||||
fragTexCoord = vec4(0.0, 1.0, 0.0, 0.0);
|
||||
fragTexCoord = vec4(0.0, 1.0, geoArrayIndex[0], 0.0);
|
||||
fragRadius = geoRadius[0];
|
||||
fragColor = geoColor[0];
|
||||
EmitVertex();
|
||||
@@ -34,7 +35,7 @@ void main(void)
|
||||
pos = (gl_in[0].gl_Position + vec4(geoRadius[0] * up_vec, 0.0) - vec4(geoRadius[0] * right_vec, 0.0));
|
||||
gl_Position = projMatrix * pos;
|
||||
fragPosition = pos;
|
||||
fragTexCoord = vec4(1.0, 0.0, 0.0, 0.0);
|
||||
fragTexCoord = vec4(1.0, 0.0, geoArrayIndex[0], 0.0);
|
||||
fragRadius = geoRadius[0];
|
||||
fragColor = geoColor[0];
|
||||
EmitVertex();
|
||||
@@ -42,9 +43,9 @@ void main(void)
|
||||
pos = (gl_in[0].gl_Position + vec4(geoRadius[0] * up_vec, 0.0) + vec4(geoRadius[0] * right_vec, 0.0));
|
||||
gl_Position = projMatrix * pos;
|
||||
fragPosition = pos;
|
||||
fragTexCoord = vec4(1.0, 1.0, 0.0, 0.0);
|
||||
fragTexCoord = vec4(1.0, 1.0, geoArrayIndex[0], 0.0);
|
||||
fragRadius = geoRadius[0];
|
||||
fragColor = geoColor[0];
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ in float vertRadius;
|
||||
out vec3 geoUvec;
|
||||
out float geoRadius;
|
||||
out vec4 geoColor;
|
||||
out float geoArrayIndex;
|
||||
#else
|
||||
out float fragRadius;
|
||||
out vec4 fragPosition;
|
||||
@@ -26,14 +27,15 @@ void main()
|
||||
geoUvec = vertUvec;
|
||||
gl_Position = modelViewMatrix * vertPosition;
|
||||
geoColor = vertColor;
|
||||
geoArrayIndex = vertTexCoord.z;
|
||||
#else
|
||||
fragRadius = vertRadius;
|
||||
gl_Position = projMatrix * modelViewMatrix * vertPosition;
|
||||
fragPosition = modelViewMatrix * vertPosition;
|
||||
fragTexCoord = vertTexCoord;
|
||||
fragTexCoord = vec4(vertTexCoord.xyz, 0.0);
|
||||
fragColor = vertColor;
|
||||
#endif
|
||||
#ifdef FLAG_EFFECT_DISTORTION
|
||||
fragOffset = vertRadius * use_offset;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
#include "gamma.sdr"
|
||||
|
||||
in vec4 fragTexCoord;
|
||||
out vec4 fragOut0;
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform sampler2D depth_tex;
|
||||
|
||||
uniform float fog_start;
|
||||
uniform float fog_scale;
|
||||
uniform vec3 fog_color;
|
||||
uniform float zNear;
|
||||
uniform float zFar;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 color_in = texture(tex, fragTexCoord.xy);
|
||||
|
||||
// If the scene is in fog then we change the color we operate on based on the depth of the pixel
|
||||
float depth_val = texture(depth_tex, fragTexCoord.xy).x;
|
||||
// Transform depth value from [-1, 1] to [0, 1]
|
||||
float depth_normalized = 2.0 * depth_val - 1.0;
|
||||
// Now we compute the depth value in projection space using the clipping plane information
|
||||
float view_depth = 2.0 * zNear * zFar / (zFar + zNear - depth_normalized * (zFar - zNear));
|
||||
|
||||
// This is the same formula that is also used by the main shader
|
||||
float fog_dist = isinf(view_depth) ? 1.0 : clamp((view_depth - fog_start) * 0.75 * fog_scale, 0.0, 1.0);
|
||||
|
||||
vec3 finalFogColor = srgb_to_linear(fog_color);
|
||||
|
||||
fragOut0.rgb = mix(color_in.rgb, finalFogColor, fog_dist);
|
||||
fragOut0.a = 1.0;
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,29 @@
|
||||
|
||||
const float SRGB_GAMMA = 2.2;
|
||||
const float SRGB_GAMMA_INVERSE = 1.0 / SRGB_GAMMA;
|
||||
|
||||
float srgb_to_linear(float val) {
|
||||
return pow(val, SRGB_GAMMA);
|
||||
}
|
||||
vec2 srgb_to_linear(vec2 val) {
|
||||
return pow(val, vec2(SRGB_GAMMA));
|
||||
}
|
||||
vec3 srgb_to_linear(vec3 val) {
|
||||
return pow(val, vec3(SRGB_GAMMA));
|
||||
}
|
||||
vec4 srgb_to_linear(vec4 val) {
|
||||
return pow(val, vec4(SRGB_GAMMA));
|
||||
}
|
||||
|
||||
float linear_to_srgb(float val) {
|
||||
return pow(val, SRGB_GAMMA_INVERSE);
|
||||
}
|
||||
vec2 linear_to_srgb(vec2 val) {
|
||||
return pow(val, vec2(SRGB_GAMMA_INVERSE));
|
||||
}
|
||||
vec3 linear_to_srgb(vec3 val) {
|
||||
return pow(val, vec3(SRGB_GAMMA_INVERSE));
|
||||
}
|
||||
vec4 linear_to_srgb(vec4 val) {
|
||||
return pow(val, vec4(SRGB_GAMMA_INVERSE));
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
|
||||
const int LT_DIRECTIONAL = 0; // A light like a sun
|
||||
const int LT_POINT = 1; // A point light, like an explosion
|
||||
const int LT_TUBE = 2; // A tube light, like a fluorescent light
|
||||
const int LT_CONE = 3; // A cone light, like a flood light
|
||||
|
||||
const float SPEC_INTENSITY_POINT = 5.3 ;// Point light
|
||||
const float SPEC_INTENSITY_DIRECTIONAL = 3.0 ;// Directional light
|
||||
const float SPECULAR_FACTOR = 1.75;
|
||||
const float SPECULAR_ALPHA = 0.1;
|
||||
const float SPEC_FACTOR_NO_SPEC_MAP = 0.6;
|
||||
const float ENV_ALPHA_FACTOR = 0.3;
|
||||
const float GLOW_MAP_INTENSITY = 1.5;
|
||||
const float AMBIENT_LIGHT_BOOST = 1.0;
|
||||
const float GLOW_MAP_SRGB_MULTIPLIER = 3.0;
|
||||
|
||||
vec3 FresnelSchlick(vec3 specColor, vec3 light, vec3 halfVec)
|
||||
{
|
||||
return specColor + (vec3(1.0) - specColor) * pow(1.0 - clamp(dot(light, halfVec), 0.0, 1.0), 5.0);
|
||||
}
|
||||
|
||||
vec3 SpecularBlinnPhong(vec3 specColor, vec3 light, vec3 normal, vec3 halfVec, float specPower, float fresnel, float dotNL)
|
||||
{
|
||||
return mix(specColor, FresnelSchlick(specColor, light, halfVec), fresnel) * ((specPower + 2.0) / 8.0 ) * pow(clamp(dot(normal, halfVec), 0.0, 1.0), specPower) * dotNL;
|
||||
}
|
||||
|
||||
vec3 SpecularGGX(vec3 specColor, vec3 light, vec3 normal, vec3 halfVec, vec3 view, float gloss, float fresnelFactor, float dotNL)
|
||||
{
|
||||
float roughness = clamp(1.0f - gloss, 0.0f, 1.0f);
|
||||
float alpha = roughness * roughness;
|
||||
|
||||
float dotNH = clamp(dot(normal, halfVec), 0.0f, 1.0f);
|
||||
float dotNV = clamp(dot(normal, view), 0.0f, 1.0f);
|
||||
|
||||
float alphaSqr = alpha * alpha;
|
||||
float pi = 3.14159f;
|
||||
float denom = dotNH * dotNH * (alphaSqr - 1.0f) + 1.0f;
|
||||
float distribution = alphaSqr / (pi * denom * denom);
|
||||
|
||||
vec3 fresnel = mix(specColor, FresnelSchlick(specColor, light, halfVec), fresnelFactor);
|
||||
|
||||
float alphaPrime = roughness + 1.0f;
|
||||
float k = alphaPrime * alphaPrime / 8.0f;
|
||||
float g1vNL = 1.0f / (dotNL * (1.0f - k) + k);
|
||||
float g1vNV = 1.0f / (dotNV * (1.0f - k) + k);
|
||||
float visibility = g1vNL * g1vNV;
|
||||
|
||||
return distribution * fresnel * visibility * dotNL;
|
||||
}
|
||||
|
||||
vec3 computeLighting(vec3 specColor, vec3 light, vec3 normal, vec3 halfVec, vec3 view, float gloss, float fresnelFactor, float dotNL)
|
||||
{
|
||||
#ifdef FLAG_LIGHT_MODEL_BLINN_PHONG
|
||||
return SpecularBlinnPhong(specColor, light, normal, halfVec, 32, fresnelFactor, dotNL);
|
||||
#else
|
||||
return SpecularGGX(specColor, light, normal, halfVec, view, gloss, fresnelFactor, dotNL);
|
||||
#endif
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,512 @@
|
||||
|
||||
#ifdef FEATURE_BINDLESS_TEXTURES
|
||||
#extension GL_ARB_bindless_texture : require
|
||||
#endif
|
||||
|
||||
#include "shadows.sdr"
|
||||
#include "lighting.sdr"
|
||||
#include "gamma.sdr"
|
||||
|
||||
#define MAX_LIGHTS 8
|
||||
#define LT_DIRECTIONAL 0
|
||||
#define LT_POINT 1
|
||||
#define LT_TUBE 2
|
||||
struct model_light {
|
||||
vec4 position;
|
||||
|
||||
vec3 diffuse_color;
|
||||
int light_type;
|
||||
|
||||
vec3 spec_color;
|
||||
float attenuation;
|
||||
|
||||
vec3 direction;
|
||||
};
|
||||
|
||||
#ifdef FEATURE_BINDLESS_TEXTURES
|
||||
layout (std140) uniform modelData {
|
||||
mat4 modelViewMatrix;
|
||||
mat4 modelMatrix;
|
||||
mat4 viewMatrix;
|
||||
mat4 projMatrix;
|
||||
mat4 textureMatrix;
|
||||
mat4 shadow_mv_matrix;
|
||||
mat4 shadow_proj_matrix[4];
|
||||
mat4 envMatrix;
|
||||
|
||||
vec4 color;
|
||||
|
||||
model_light lights[MAX_LIGHTS];
|
||||
|
||||
float extrudeWidth;
|
||||
float fogStart;
|
||||
float fogScale;
|
||||
int buffer_matrix_offset;
|
||||
|
||||
vec4 clip_equation;
|
||||
|
||||
float thruster_scale;
|
||||
bool use_clip_plane;
|
||||
int n_lights;
|
||||
float defaultGloss;
|
||||
|
||||
vec3 ambientFactor;
|
||||
int desaturate;
|
||||
|
||||
vec3 diffuseFactor;
|
||||
int blend_alpha;
|
||||
|
||||
vec3 emissionFactor;
|
||||
bool overrideDiffuse;
|
||||
|
||||
vec3 diffuseClr;
|
||||
bool overrideGlow;
|
||||
|
||||
vec3 glowClr;
|
||||
bool overrideSpec;
|
||||
|
||||
vec3 specClr;
|
||||
bool alphaGloss;
|
||||
|
||||
bool gammaSpec;
|
||||
bool envGloss;
|
||||
bool alpha_spec;
|
||||
int effect_num;
|
||||
|
||||
vec4 fogColor;
|
||||
|
||||
vec3 base_color;
|
||||
float anim_timer;
|
||||
|
||||
vec3 stripe_color;
|
||||
float vpwidth;
|
||||
|
||||
float vpheight;
|
||||
bool team_glow_enabled;
|
||||
float znear;
|
||||
float zfar;
|
||||
|
||||
float veryneardist;
|
||||
float neardist;
|
||||
float middist;
|
||||
float fardist;
|
||||
|
||||
vec2 normalAlphaMinMax;
|
||||
int sBasemapIndex;
|
||||
int sGlowmapIndex;
|
||||
|
||||
int sSpecmapIndex;
|
||||
int sNormalmapIndex;
|
||||
int sAmbientmapIndex;
|
||||
int sMiscmapIndex;
|
||||
|
||||
sampler2DArray sBasemap;
|
||||
sampler2DArray sGlowmap;
|
||||
|
||||
sampler2DArray sSpecmap;
|
||||
samplerCube sEnvmap;
|
||||
|
||||
sampler2DArray sNormalmap;
|
||||
sampler2DArray sAmbientmap;
|
||||
|
||||
sampler2DArray sMiscmap;
|
||||
sampler2DArray sHeightmap;
|
||||
};
|
||||
#else
|
||||
layout (std140) uniform modelData {
|
||||
mat4 modelViewMatrix;
|
||||
mat4 modelMatrix;
|
||||
mat4 viewMatrix;
|
||||
mat4 projMatrix;
|
||||
mat4 textureMatrix;
|
||||
mat4 shadow_mv_matrix;
|
||||
mat4 shadow_proj_matrix[4];
|
||||
mat4 envMatrix;
|
||||
|
||||
vec4 color;
|
||||
|
||||
model_light lights[MAX_LIGHTS];
|
||||
|
||||
float extrudeWidth;
|
||||
float fogStart;
|
||||
float fogScale;
|
||||
int buffer_matrix_offset;
|
||||
|
||||
vec4 clip_equation;
|
||||
|
||||
float thruster_scale;
|
||||
bool use_clip_plane;
|
||||
int n_lights;
|
||||
float defaultGloss;
|
||||
|
||||
vec3 ambientFactor;
|
||||
int desaturate;
|
||||
|
||||
vec3 diffuseFactor;
|
||||
int blend_alpha;
|
||||
|
||||
vec3 emissionFactor;
|
||||
bool overrideDiffuse;
|
||||
|
||||
vec3 diffuseClr;
|
||||
bool overrideGlow;
|
||||
|
||||
vec3 glowClr;
|
||||
bool overrideSpec;
|
||||
|
||||
vec3 specClr;
|
||||
bool alphaGloss;
|
||||
|
||||
bool gammaSpec;
|
||||
bool envGloss;
|
||||
bool alpha_spec;
|
||||
int effect_num;
|
||||
|
||||
vec4 fogColor;
|
||||
|
||||
vec3 base_color;
|
||||
float anim_timer;
|
||||
|
||||
vec3 stripe_color;
|
||||
float vpwidth;
|
||||
|
||||
float vpheight;
|
||||
bool team_glow_enabled;
|
||||
float znear;
|
||||
float zfar;
|
||||
|
||||
float veryneardist;
|
||||
float neardist;
|
||||
float middist;
|
||||
float fardist;
|
||||
|
||||
vec2 normalAlphaMinMax;
|
||||
int sBasemapIndex;
|
||||
int sGlowmapIndex;
|
||||
|
||||
int sSpecmapIndex;
|
||||
int sNormalmapIndex;
|
||||
int sAmbientmapIndex;
|
||||
int sMiscmapIndex;
|
||||
};
|
||||
uniform sampler2DArray sBasemap;
|
||||
uniform sampler2DArray sGlowmap;
|
||||
uniform sampler2DArray sSpecmap;
|
||||
uniform samplerCube sEnvmap;
|
||||
uniform sampler2DArray sNormalmap;
|
||||
uniform sampler2DArray sAmbientmap;
|
||||
uniform sampler2DArray sMiscmap;
|
||||
#endif
|
||||
|
||||
uniform sampler2D sFramebuffer;
|
||||
uniform sampler2DArray shadow_map;
|
||||
|
||||
in VertexOutput {
|
||||
#ifdef FLAG_ENV_MAP
|
||||
vec3 envReflect;
|
||||
#endif
|
||||
#ifdef FLAG_NORMAL_MAP
|
||||
mat3 tangentMatrix;
|
||||
#endif
|
||||
#ifdef FLAG_FOG
|
||||
float fogDist;
|
||||
#endif
|
||||
#ifdef WORKAROUND_CLIPPING_PLANES
|
||||
#ifdef FLAG_TRANSFORM
|
||||
float notVisible;
|
||||
#endif
|
||||
#endif
|
||||
vec4 position;
|
||||
vec3 normal;
|
||||
vec4 texCoord;
|
||||
#ifdef FLAG_SHADOWS
|
||||
vec4 shadowUV[4];
|
||||
vec4 shadowPos;
|
||||
#endif
|
||||
} vertIn;
|
||||
|
||||
#ifdef FLAG_TEAMCOLOR
|
||||
vec2 teamMask = vec2(0.0, 0.0);
|
||||
#endif
|
||||
|
||||
out vec4 fragOut0;
|
||||
out vec4 fragOut1;
|
||||
out vec4 fragOut2;
|
||||
out vec4 fragOut3;
|
||||
out vec4 fragOut4;
|
||||
|
||||
vec3 FresnelLazarovEnv(vec3 specColor, vec3 view, vec3 normal, float gloss)
|
||||
{
|
||||
// Fresnel for environment lighting
|
||||
// Equation referenced from Dimitar Lazarov's presentation titled Physically Based Rendering in Call of Duty: Black Ops
|
||||
return specColor + (vec3(1.0) - specColor) * pow(1.0 - clamp(dot(view, normal), 0.0, 1.0), 5.0) / (4.0 - 3.0 * gloss);
|
||||
}
|
||||
|
||||
void GetLightInfo(int i, out vec3 lightDir, out float attenuation)
|
||||
{
|
||||
lightDir = normalize(lights[i].position.xyz);
|
||||
attenuation = 1.0;
|
||||
if (lights[i].light_type != LT_DIRECTIONAL) {
|
||||
// Positional light source
|
||||
float dist = distance(lights[i].position.xyz, vertIn.position.xyz);
|
||||
lightDir = (lights[i].position.xyz - vertIn.position.xyz);
|
||||
|
||||
if (lights[i].light_type == LT_TUBE) { // Tube light
|
||||
float beamlength = length(lights[i].direction);
|
||||
vec3 beamDir = normalize(lights[i].direction);
|
||||
// Get nearest point on line
|
||||
float neardist = dot(vertIn.position.xyz - lights[i].position.xyz, beamDir);
|
||||
// Move back from the endpoint of the beam along the beam by the distance we calculated
|
||||
vec3 nearest = lights[i].position.xyz - beamDir * abs(neardist);
|
||||
lightDir = nearest - vertIn.position.xyz;
|
||||
dist = length(lightDir);
|
||||
}
|
||||
|
||||
lightDir = normalize(lightDir);
|
||||
attenuation = 1.0 / (1.0 + lights[i].attenuation * dist);
|
||||
}
|
||||
}
|
||||
vec3 CalculateLighting(vec3 normal, vec3 diffuseMaterial, vec3 specularMaterial, float gloss, float fresnel, float shadow, float aoFactor)
|
||||
{
|
||||
vec3 eyeDir = vec3(normalize(-vertIn.position).xyz);
|
||||
vec3 lightAmbient = (emissionFactor + ambientFactor * ambientFactor) * aoFactor; // ambientFactor^2 due to legacy OpenGL compatibility behavior
|
||||
vec3 lightDiffuse = vec3(0.0, 0.0, 0.0);
|
||||
vec3 lightSpecular = vec3(0.0, 0.0, 0.0);
|
||||
#pragma optionNV unroll all
|
||||
for (int i = 0; i < n_lights; ++i) {
|
||||
if(i > 0) {
|
||||
shadow = 1.0;
|
||||
}
|
||||
|
||||
vec3 lightDir;
|
||||
float attenuation;
|
||||
// gather light params
|
||||
GetLightInfo(i, lightDir, attenuation);
|
||||
vec3 halfVec = normalize(lightDir + eyeDir);
|
||||
float NdotL = clamp(dot(normal, lightDir), 0.0f, 1.0f);
|
||||
// Ambient, Diffuse, and Specular
|
||||
lightDiffuse += (lights[i].diffuse_color.rgb * diffuseFactor * NdotL * attenuation) * shadow;
|
||||
lightSpecular += lights[i].spec_color * computeLighting(specularMaterial, lightDir, normal, halfVec, eyeDir, gloss, fresnel, NdotL) * attenuation * shadow;
|
||||
}
|
||||
return diffuseMaterial * (lightAmbient + lightDiffuse) + lightSpecular;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
#ifdef WORKAROUND_CLIPPING_PLANES
|
||||
#ifdef FLAG_TRANSFORM
|
||||
if(vertIn.notVisible >= 0.9) { discard; }
|
||||
#endif
|
||||
#endif
|
||||
#ifdef FLAG_SHADOW_MAP
|
||||
// need depth and depth squared for variance shadow maps
|
||||
fragOut0 = vec4(vertIn.position.z, vertIn.position.z * vertIn.position.z * VARIANCE_SHADOW_SCALE_INV, 0.0, 1.0);
|
||||
if (true) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
vec3 eyeDir = vec3(normalize(-vertIn.position).xyz);
|
||||
vec2 texCoord = vertIn.texCoord.xy;
|
||||
vec4 baseColor = color;
|
||||
vec4 specColor = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
vec4 emissiveColor = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
float fresnelFactor = 0.0;
|
||||
float glossData = 0.6;
|
||||
#ifdef FLAG_LIGHT
|
||||
glossData = defaultGloss;
|
||||
#endif
|
||||
vec2 aoFactors = vec2(1.0, 1.0);
|
||||
#ifdef FLAG_AMBIENT_MAP
|
||||
// red channel is ambient occlusion factor which only affects ambient lighting.
|
||||
// green is cavity occlusion factor which only affects diffuse and specular lighting.
|
||||
aoFactors = texture(sAmbientmap, vec3(texCoord, float(sAmbientmapIndex))).xy;
|
||||
#endif
|
||||
vec3 unitNormal = normalize(vertIn.normal);
|
||||
vec3 normal = unitNormal;
|
||||
#ifdef FLAG_NORMAL_MAP
|
||||
// Normal map - convert from DXT5nm
|
||||
vec2 normalSample;
|
||||
normal.rg = normalSample = (texture(sNormalmap, vec3(texCoord, float(sNormalmapIndex))).ag * 2.0) - 1.0;
|
||||
normal.b = clamp(sqrt(1.0 - dot(normal.rg, normal.rg)), 0.0001, 1.0);
|
||||
normal = vertIn.tangentMatrix * normal;
|
||||
float norm = length(normal);
|
||||
// prevent breaking of normal maps
|
||||
if (norm > 0.0)
|
||||
normal /= norm;
|
||||
else
|
||||
normal = unitNormal;
|
||||
#endif
|
||||
|
||||
#ifdef FLAG_ANIMATED
|
||||
vec2 distort = vec2(cos(vertIn.position.x*vertIn.position.w*0.005+anim_timer*20.0)*sin(vertIn.position.y*vertIn.position.w*0.005),sin(vertIn.position.x*vertIn.position.w*0.005+anim_timer*20.0)*cos(vertIn.position.y*vertIn.position.w*0.005))*0.03;
|
||||
#endif
|
||||
|
||||
#ifdef FLAG_DIFFUSE_MAP
|
||||
vec2 diffuseTexCoord = texCoord;
|
||||
#ifdef FLAG_ANIMATED
|
||||
if (effect_num == 2) {
|
||||
diffuseTexCoord = texCoord + distort*(1.0-anim_timer);
|
||||
}
|
||||
#endif
|
||||
baseColor = texture(sBasemap, vec3(diffuseTexCoord, float(sBasemapIndex)));
|
||||
if ( blend_alpha == 0 && baseColor.a < 0.95 ) discard; // if alpha blending is not on, discard transparent pixels
|
||||
// premultiply alpha if blend_alpha is 1. assume that our blend function is srcColor + (1-Alpha)*destColor.
|
||||
// if blend_alpha is 2, assume blend func is additive and don't modify color
|
||||
if(blend_alpha == 1) baseColor.rgb = baseColor.rgb * baseColor.a;
|
||||
if(overrideDiffuse) baseColor.rgb = diffuseClr;
|
||||
#endif
|
||||
specColor = vec4(baseColor.rgb * SPEC_FACTOR_NO_SPEC_MAP, glossData);
|
||||
#ifdef FLAG_HDR
|
||||
baseColor.rgb = srgb_to_linear(baseColor.rgb);
|
||||
#endif
|
||||
#ifdef FLAG_SPEC_MAP
|
||||
specColor = texture(sSpecmap, vec3(texCoord, float(sSpecmapIndex)));
|
||||
if(alphaGloss) glossData = specColor.a;
|
||||
if(overrideSpec) {
|
||||
specColor.rgb = specClr;
|
||||
fresnelFactor = 1.0;
|
||||
}
|
||||
if(gammaSpec) {
|
||||
specColor.rgb = max(specColor.rgb, vec3(0.03f)); // hardcoded minimum specular value. read John Hable's blog post titled 'Everything Is Shiny'.
|
||||
#ifdef FLAG_HDR
|
||||
specColor.rgb = srgb_to_linear(specColor.rgb);
|
||||
#endif
|
||||
fresnelFactor = 1.0;
|
||||
}
|
||||
#endif
|
||||
baseColor.rgb *= aoFactors.y;
|
||||
specColor.rgb *= aoFactors.y;
|
||||
#ifdef FLAG_MISC_MAP
|
||||
#ifdef FLAG_TEAMCOLOR
|
||||
vec4 teamMask = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
teamMask = texture(sMiscmap, vec3(texCoord, float(sMiscmapIndex)));
|
||||
//For team colors applied to a diffuse or spec map, we assume that the base color of the diffuse
|
||||
//at this point is vec3(0.5). To get accurate results, we subtract 0.5 from the team colors
|
||||
vec3 team_color = ((base_color - vec3(0.5)) * teamMask.x) + ((stripe_color - vec3(0.5)) * teamMask.y);
|
||||
vec3 team_color_glow = (base_color * teamMask.b) + (stripe_color * teamMask.a);
|
||||
|
||||
#ifdef FLAG_HDR
|
||||
team_color = srgb_to_linear(team_color);
|
||||
team_color_glow = srgb_to_linear(team_color_glow) * GLOW_MAP_SRGB_MULTIPLIER;
|
||||
#endif
|
||||
|
||||
baseColor.rgb += team_color;
|
||||
baseColor.rgb = max(baseColor.rgb, vec3(0.0)); // We need to make sure that nothing here ever goes negative
|
||||
specColor.rgb += team_color;
|
||||
specColor.rgb = max(specColor.rgb, vec3(0.03));
|
||||
#endif
|
||||
#endif
|
||||
// Lights aren't applied when we are rendering to the G-buffers since that gets handled later
|
||||
#ifdef FLAG_DEFERRED
|
||||
#ifdef FLAG_LIGHT
|
||||
// Ambient lighting still needs to be done since that counts as an "emissive" color
|
||||
vec3 lightAmbient = (emissionFactor + ambientFactor * ambientFactor) * aoFactors.x; // ambientFactor^2 due to legacy OpenGL compatibility behavior
|
||||
emissiveColor.rgb += baseColor.rgb * lightAmbient;
|
||||
#else
|
||||
#ifdef FLAG_SPEC_MAP
|
||||
baseColor.rgb += pow(1.0 - clamp(dot(eyeDir, normal), 0.0, 1.0), 5.0 * clamp(glossData, 0.01, 1.0)) * specColor.rgb;
|
||||
#endif
|
||||
// If there is no lighting then we copy the color data so far into the
|
||||
emissiveColor.rgb += baseColor.rgb;
|
||||
#endif
|
||||
#else
|
||||
#ifdef FLAG_LIGHT
|
||||
float shadow = 1.0;
|
||||
#ifdef FLAG_SHADOWS
|
||||
shadow = getShadowValue(shadow_map, -vertIn.position.z, vertIn.shadowPos.z, vertIn.shadowUV, fardist, middist, neardist, veryneardist);
|
||||
#endif
|
||||
baseColor.rgb = CalculateLighting(normal, baseColor.rgb, specColor.rgb, glossData, fresnelFactor, shadow, aoFactors.x);
|
||||
#else
|
||||
#ifdef FLAG_SPEC_MAP
|
||||
baseColor.rgb += pow(1.0 - clamp(dot(eyeDir, normal), 0.0, 1.0), 5.0 * clamp(glossData, 0.01, 1.0)) * specColor.rgb;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#ifdef FLAG_ENV_MAP
|
||||
vec3 envReflectNM = vertIn.envReflect;
|
||||
#ifdef FLAG_NORMAL_MAP
|
||||
envReflectNM += vec3(normalSample, 0.0);
|
||||
envReflectNM = normalize(envReflectNM);
|
||||
#endif
|
||||
float mip = (envGloss) ? (1.0 - glossData) * 7.0 : 0.0;
|
||||
vec4 envColour = textureLod(sEnvmap, envReflectNM, mip);
|
||||
#ifdef FLAG_HDR
|
||||
envColour.rgb = srgb_to_linear(envColour.rgb);
|
||||
#endif
|
||||
envColour.rgb *= (envGloss) ? 1.0 : specColor.a;
|
||||
emissiveColor.rgb += envColour.rgb * FresnelLazarovEnv(specColor.rgb, eyeDir, normal, glossData);
|
||||
#endif
|
||||
#ifdef FLAG_GLOW_MAP
|
||||
vec3 glowColor = texture(sGlowmap, vec3(texCoord, float(sGlowmapIndex))).rgb;
|
||||
if(overrideGlow) glowColor = glowClr;
|
||||
#ifdef FLAG_HDR
|
||||
glowColor = srgb_to_linear(glowColor) * GLOW_MAP_SRGB_MULTIPLIER;
|
||||
#endif
|
||||
#ifdef FLAG_MISC_MAP
|
||||
#ifdef FLAG_TEAMCOLOR
|
||||
float glowColorLuminance = dot(glowColor, vec3(0.299, 0.587, 0.114));
|
||||
glowColor = team_glow_enabled ? mix(max(team_color_glow, vec3(0.0)), glowColor, clamp(glowColorLuminance - teamMask.b - teamMask.a, 0.0, 1.0)) : glowColor;
|
||||
#endif
|
||||
#endif
|
||||
emissiveColor.rgb += glowColor * GLOW_MAP_INTENSITY;
|
||||
#endif
|
||||
|
||||
#ifdef FLAG_FOG
|
||||
vec3 finalFogColor = fogColor.rgb;
|
||||
#ifdef FLAG_HDR
|
||||
finalFogColor = srgb_to_linear(finalFogColor);
|
||||
#endif
|
||||
#ifdef FLAG_DIFFUSE_MAP
|
||||
if(blend_alpha == 1) finalFogColor *= baseColor.a;
|
||||
#endif
|
||||
baseColor.rgb = mix(baseColor.rgb, finalFogColor, vertIn.fogDist);
|
||||
emissiveColor.rgb = mix(emissiveColor.rgb, finalFogColor, vertIn.fogDist);
|
||||
specColor.rgb *= vertIn.fogDist;
|
||||
#endif
|
||||
|
||||
#ifdef FLAG_DIFFUSE_MAP
|
||||
if(desaturate == 1) {
|
||||
baseColor.rgb = color.rgb * dot(vec3(1.0), baseColor.rgb) * 0.3333333;
|
||||
}
|
||||
#endif
|
||||
#ifdef FLAG_ANIMATED
|
||||
if (effect_num == 0) {
|
||||
float shinefactor = 1.0/(1.0 + pow(abs((fract(abs(texCoord.x))-anim_timer) * 1000.0), 2.0)) * 1000.0;
|
||||
emissiveColor.rgb += vec3(shinefactor);
|
||||
baseColor.a = baseColor.a * clamp(shinefactor * (fract(abs(texCoord.x))-anim_timer) * -10000.0,0.0,1.0);
|
||||
}
|
||||
if (effect_num == 1) {
|
||||
float shinefactor = 1.0/(1.0 + pow(abs(vertIn.position.y-anim_timer), 2.0));
|
||||
emissiveColor.rgb += vec3(shinefactor);
|
||||
#ifndef FLAG_LIGHT
|
||||
// ATI Wireframe fix *grumble*
|
||||
baseColor.a = clamp((vertIn.position.y-anim_timer) * 10000.0,0.0,1.0);
|
||||
#endif
|
||||
}
|
||||
if (effect_num == 2) {
|
||||
vec2 screenPos = gl_FragCoord.xy * vec2(vpwidth,vpheight);
|
||||
baseColor.a = baseColor.a;
|
||||
float cloak_interp = (sin(vertIn.position.x*vertIn.position.w*0.005+anim_timer*20.0)*sin(vertIn.position.y*vertIn.position.w*0.005)*0.5)-0.5;
|
||||
#ifdef FLAG_LIGHT
|
||||
baseColor.rgb = mix(texture(sFramebuffer, screenPos + distort*anim_timer + anim_timer*0.1*normal.xy).rgb,baseColor.rgb,clamp(cloak_interp+anim_timer*2.0,0.0,1.0));
|
||||
#else
|
||||
baseColor.rgb = mix(texture(sFramebuffer, screenPos + distort*anim_timer + anim_timer*0.1*vertIn.normal.xy).rgb,baseColor.rgb,clamp(cloak_interp+anim_timer*2.0,0.0,1.0));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#ifdef FLAG_NORMAL_ALPHA
|
||||
float normViewOffset = dot(vec3(0.0, 0.0, 1.0), normal);
|
||||
baseColor.a = smoothstep(min(normalAlphaMinMax.x, normalAlphaMinMax.y), max(normalAlphaMinMax.x, normalAlphaMinMax.y), clamp(normalAlphaMinMax.x > normalAlphaMinMax.y ? normViewOffset : 1.0 - normViewOffset, 0.0, 1.0));
|
||||
#endif
|
||||
#ifndef FLAG_DEFERRED
|
||||
// emissive colors won't be added later when we are using forward rendering so we need to do that here
|
||||
baseColor.rgb += emissiveColor.rgb;
|
||||
#endif
|
||||
fragOut0 = baseColor;
|
||||
#ifdef FLAG_DEFERRED
|
||||
fragOut1 = vec4(vertIn.position.xyz, 1.0);
|
||||
fragOut2 = vec4(normal, glossData);
|
||||
fragOut3 = vec4(specColor.rgb, fresnelFactor);
|
||||
fragOut4 = emissiveColor;
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
#extension GL_ARB_gpu_shader5: enable
|
||||
|
||||
#ifdef FEATURE_BINDLESS_TEXTURES
|
||||
#extension GL_ARB_bindless_texture : require
|
||||
#endif
|
||||
|
||||
layout (triangles) in;
|
||||
layout (triangle_strip, max_vertices = 3) out;
|
||||
#ifdef GL_ARB_gpu_shader5
|
||||
layout(invocations = 4) in;
|
||||
#endif
|
||||
|
||||
#define MAX_LIGHTS 8
|
||||
struct model_light {
|
||||
vec4 position;
|
||||
|
||||
vec3 diffuse_color;
|
||||
int light_type;
|
||||
|
||||
vec3 spec_color;
|
||||
float attenuation;
|
||||
|
||||
vec3 direction;
|
||||
};
|
||||
|
||||
#ifdef FEATURE_BINDLESS_TEXTURES
|
||||
layout (std140) uniform modelData {
|
||||
mat4 modelViewMatrix;
|
||||
mat4 modelMatrix;
|
||||
mat4 viewMatrix;
|
||||
mat4 projMatrix;
|
||||
mat4 textureMatrix;
|
||||
mat4 shadow_mv_matrix;
|
||||
mat4 shadow_proj_matrix[4];
|
||||
mat4 envMatrix;
|
||||
|
||||
vec4 color;
|
||||
|
||||
model_light lights[MAX_LIGHTS];
|
||||
|
||||
float extrudeWidth;
|
||||
float fogStart;
|
||||
float fogScale;
|
||||
int buffer_matrix_offset;
|
||||
|
||||
vec4 clip_equation;
|
||||
|
||||
float thruster_scale;
|
||||
bool use_clip_plane;
|
||||
int n_lights;
|
||||
float defaultGloss;
|
||||
|
||||
vec3 ambientFactor;
|
||||
int desaturate;
|
||||
|
||||
vec3 diffuseFactor;
|
||||
int blend_alpha;
|
||||
|
||||
vec3 emissionFactor;
|
||||
bool overrideDiffuse;
|
||||
|
||||
vec3 diffuseClr;
|
||||
bool overrideGlow;
|
||||
|
||||
vec3 glowClr;
|
||||
bool overrideSpec;
|
||||
|
||||
vec3 specClr;
|
||||
bool alphaGloss;
|
||||
|
||||
bool gammaSpec;
|
||||
bool envGloss;
|
||||
bool alpha_spec;
|
||||
int effect_num;
|
||||
|
||||
vec4 fogColor;
|
||||
|
||||
vec3 base_color;
|
||||
float anim_timer;
|
||||
|
||||
vec3 stripe_color;
|
||||
float vpwidth;
|
||||
|
||||
float vpheight;
|
||||
bool team_glow_enabled;
|
||||
float znear;
|
||||
float zfar;
|
||||
|
||||
float veryneardist;
|
||||
float neardist;
|
||||
float middist;
|
||||
float fardist;
|
||||
|
||||
vec2 normalAlphaMinMax;
|
||||
int sBasemapIndex;
|
||||
int sGlowmapIndex;
|
||||
|
||||
int sSpecmapIndex;
|
||||
int sNormalmapIndex;
|
||||
int sAmbientmapIndex;
|
||||
int sMiscmapIndex;
|
||||
|
||||
sampler2DArray sBasemap;
|
||||
sampler2DArray sGlowmap;
|
||||
|
||||
sampler2DArray sSpecmap;
|
||||
samplerCube sEnvmap;
|
||||
|
||||
sampler2DArray sNormalmap;
|
||||
sampler2DArray sAmbientmap;
|
||||
|
||||
sampler2DArray sMiscmap;
|
||||
sampler2DArray sHeightmap;
|
||||
};
|
||||
#else
|
||||
layout (std140) uniform modelData {
|
||||
mat4 modelViewMatrix;
|
||||
mat4 modelMatrix;
|
||||
mat4 viewMatrix;
|
||||
mat4 projMatrix;
|
||||
mat4 textureMatrix;
|
||||
mat4 shadow_mv_matrix;
|
||||
mat4 shadow_proj_matrix[4];
|
||||
mat4 envMatrix;
|
||||
|
||||
vec4 color;
|
||||
|
||||
model_light lights[MAX_LIGHTS];
|
||||
|
||||
float extrudeWidth;
|
||||
float fogStart;
|
||||
float fogScale;
|
||||
int buffer_matrix_offset;
|
||||
|
||||
vec4 clip_equation;
|
||||
|
||||
float thruster_scale;
|
||||
bool use_clip_plane;
|
||||
int n_lights;
|
||||
float defaultGloss;
|
||||
|
||||
vec3 ambientFactor;
|
||||
int desaturate;
|
||||
|
||||
vec3 diffuseFactor;
|
||||
int blend_alpha;
|
||||
|
||||
vec3 emissionFactor;
|
||||
bool overrideDiffuse;
|
||||
|
||||
vec3 diffuseClr;
|
||||
bool overrideGlow;
|
||||
|
||||
vec3 glowClr;
|
||||
bool overrideSpec;
|
||||
|
||||
vec3 specClr;
|
||||
bool alphaGloss;
|
||||
|
||||
bool gammaSpec;
|
||||
bool envGloss;
|
||||
bool alpha_spec;
|
||||
int effect_num;
|
||||
|
||||
vec4 fogColor;
|
||||
|
||||
vec3 base_color;
|
||||
float anim_timer;
|
||||
|
||||
vec3 stripe_color;
|
||||
float vpwidth;
|
||||
|
||||
float vpheight;
|
||||
bool team_glow_enabled;
|
||||
float znear;
|
||||
float zfar;
|
||||
|
||||
float veryneardist;
|
||||
float neardist;
|
||||
float middist;
|
||||
float fardist;
|
||||
|
||||
vec2 normalAlphaMinMax;
|
||||
int sBasemapIndex;
|
||||
int sGlowmapIndex;
|
||||
|
||||
int sSpecmapIndex;
|
||||
int sNormalmapIndex;
|
||||
int sAmbientmapIndex;
|
||||
int sMiscmapIndex;
|
||||
};
|
||||
#endif
|
||||
|
||||
in VertexOutput {
|
||||
#ifdef FLAG_ENV_MAP
|
||||
vec3 envReflect;
|
||||
#endif
|
||||
#ifdef FLAG_NORMAL_MAP
|
||||
mat3 tangentMatrix;
|
||||
#endif
|
||||
#ifdef FLAG_FOG
|
||||
float fogDist;
|
||||
#endif
|
||||
#ifdef WORKAROUND_CLIPPING_PLANES
|
||||
#ifdef FLAG_TRANSFORM
|
||||
float notVisible;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef FLAG_SHADOW_MAP
|
||||
#if !defined(GL_ARB_gpu_shader5)
|
||||
float instance;
|
||||
#endif
|
||||
#else
|
||||
vec4 position;
|
||||
#endif
|
||||
vec3 normal;
|
||||
vec4 texCoord;
|
||||
#ifdef FLAG_SHADOWS
|
||||
vec4 shadowUV[4];
|
||||
vec4 shadowPos;
|
||||
#endif
|
||||
} vertIn[];
|
||||
|
||||
out VertexOutput {
|
||||
#ifdef FLAG_ENV_MAP
|
||||
vec3 envReflect;
|
||||
#endif
|
||||
#ifdef FLAG_NORMAL_MAP
|
||||
mat3 tangentMatrix;
|
||||
#endif
|
||||
#ifdef FLAG_FOG
|
||||
float fogDist;
|
||||
#endif
|
||||
#ifdef WORKAROUND_CLIPPING_PLANES
|
||||
#ifdef FLAG_TRANSFORM
|
||||
float notVisible;
|
||||
#endif
|
||||
#endif
|
||||
vec4 position;
|
||||
vec3 normal;
|
||||
vec4 texCoord;
|
||||
#ifdef FLAG_SHADOWS
|
||||
vec4 shadowUV[4];
|
||||
vec4 shadowPos;
|
||||
#endif
|
||||
} vertOut;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
#ifdef GL_ARB_gpu_shader5
|
||||
int instanceID = gl_InvocationID;
|
||||
#else
|
||||
int instanceID = int(vertIn[0].instance);
|
||||
#endif
|
||||
for(int vert = 0; vert < gl_in.length(); vert++)
|
||||
{
|
||||
gl_Position = shadow_proj_matrix[instanceID] * gl_in[vert].gl_Position;
|
||||
if(gl_Position.z < -1.0)
|
||||
gl_Position.z = -1.0;
|
||||
vertOut.position = gl_in[vert].gl_Position;
|
||||
vertOut.normal = vertIn[vert].normal;
|
||||
vertOut.texCoord = vertIn[vert].texCoord;
|
||||
|
||||
gl_Layer = instanceID;
|
||||
#ifdef FLAG_ENV_MAP
|
||||
vertOut.envReflect = vertIn[vert].envReflect;
|
||||
#endif
|
||||
#ifdef FLAG_NORMAL_MAP
|
||||
vertOut.tangentMatrix = vertIn[vert].tangentMatrix;
|
||||
#endif
|
||||
#ifdef FLAG_FOG
|
||||
vertOut.fogDist = vertIn[vert].fogDist;
|
||||
#endif
|
||||
#ifdef FLAG_SHADOWS
|
||||
vertOut.shadowUV[0] = vertIn[vert].shadowUV[0];
|
||||
vertOut.shadowUV[1] = vertIn[vert].shadowUV[1];
|
||||
vertOut.shadowUV[2] = vertIn[vert].shadowUV[2];
|
||||
vertOut.shadowUV[3] = vertIn[vert].shadowUV[3];
|
||||
vertOut.shadowPos = vertIn[vert].shadowPos;
|
||||
#endif
|
||||
#ifdef WORKAROUND_CLIPPING_PLANES
|
||||
#ifdef FLAG_TRANSFORM
|
||||
vertOut.notVisible = vertIn[vert].notVisible;
|
||||
#endif
|
||||
#ifdef FLAG_CLIP
|
||||
gl_ClipDistance[0] = gl_in[vert].gl_ClipDistance[0];
|
||||
#endif
|
||||
#else
|
||||
#if defined(FLAG_CLIP) || defined(FLAG_TRANSFORM)
|
||||
gl_ClipDistance[0] = gl_in[vert].gl_ClipDistance[0];
|
||||
#endif
|
||||
#endif
|
||||
EmitVertex();
|
||||
}
|
||||
EndPrimitive();
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
#extension GL_ARB_gpu_shader5: enable
|
||||
#ifdef FEATURE_BINDLESS_TEXTURES
|
||||
#extension GL_ARB_bindless_texture : require
|
||||
#endif
|
||||
|
||||
#include "shadows.sdr"
|
||||
|
||||
in vec4 vertPosition;
|
||||
in vec4 vertTexCoord;
|
||||
in vec3 vertNormal;
|
||||
in vec4 vertTangent;
|
||||
in float vertModelID;
|
||||
|
||||
#define MAX_LIGHTS 8
|
||||
|
||||
struct model_light {
|
||||
vec4 position;
|
||||
|
||||
vec3 diffuse_color;
|
||||
int light_type;
|
||||
|
||||
vec3 spec_color;
|
||||
float attenuation;
|
||||
|
||||
vec3 direction;
|
||||
};
|
||||
|
||||
#ifdef FEATURE_BINDLESS_TEXTURES
|
||||
layout (std140) uniform modelData {
|
||||
mat4 modelViewMatrix;
|
||||
mat4 modelMatrix;
|
||||
mat4 viewMatrix;
|
||||
mat4 projMatrix;
|
||||
mat4 textureMatrix;
|
||||
mat4 shadow_mv_matrix;
|
||||
mat4 shadow_proj_matrix[4];
|
||||
mat4 envMatrix;
|
||||
|
||||
vec4 color;
|
||||
|
||||
model_light lights[MAX_LIGHTS];
|
||||
|
||||
float extrudeWidth;
|
||||
float fogStart;
|
||||
float fogScale;
|
||||
int buffer_matrix_offset;
|
||||
|
||||
vec4 clip_equation;
|
||||
|
||||
float thruster_scale;
|
||||
bool use_clip_plane;
|
||||
int n_lights;
|
||||
float defaultGloss;
|
||||
|
||||
vec3 ambientFactor;
|
||||
int desaturate;
|
||||
|
||||
vec3 diffuseFactor;
|
||||
int blend_alpha;
|
||||
|
||||
vec3 emissionFactor;
|
||||
bool overrideDiffuse;
|
||||
|
||||
vec3 diffuseClr;
|
||||
bool overrideGlow;
|
||||
|
||||
vec3 glowClr;
|
||||
bool overrideSpec;
|
||||
|
||||
vec3 specClr;
|
||||
bool alphaGloss;
|
||||
|
||||
bool gammaSpec;
|
||||
bool envGloss;
|
||||
bool alpha_spec;
|
||||
int effect_num;
|
||||
|
||||
vec4 fogColor;
|
||||
|
||||
vec3 base_color;
|
||||
float anim_timer;
|
||||
|
||||
vec3 stripe_color;
|
||||
float vpwidth;
|
||||
|
||||
float vpheight;
|
||||
bool team_glow_enabled;
|
||||
float znear;
|
||||
float zfar;
|
||||
|
||||
float veryneardist;
|
||||
float neardist;
|
||||
float middist;
|
||||
float fardist;
|
||||
|
||||
vec2 normalAlphaMinMax;
|
||||
int sBasemapIndex;
|
||||
int sGlowmapIndex;
|
||||
|
||||
int sSpecmapIndex;
|
||||
int sNormalmapIndex;
|
||||
int sAmbientmapIndex;
|
||||
int sMiscmapIndex;
|
||||
|
||||
sampler2DArray sBasemap;
|
||||
sampler2DArray sGlowmap;
|
||||
|
||||
sampler2DArray sSpecmap;
|
||||
samplerCube sEnvmap;
|
||||
|
||||
sampler2DArray sNormalmap;
|
||||
sampler2DArray sAmbientmap;
|
||||
|
||||
sampler2DArray sMiscmap;
|
||||
sampler2DArray sHeightmap;
|
||||
};
|
||||
#else
|
||||
layout (std140) uniform modelData {
|
||||
mat4 modelViewMatrix;
|
||||
mat4 modelMatrix;
|
||||
mat4 viewMatrix;
|
||||
mat4 projMatrix;
|
||||
mat4 textureMatrix;
|
||||
mat4 shadow_mv_matrix;
|
||||
mat4 shadow_proj_matrix[4];
|
||||
mat4 envMatrix;
|
||||
|
||||
vec4 color;
|
||||
|
||||
model_light lights[MAX_LIGHTS];
|
||||
|
||||
float extrudeWidth;
|
||||
float fogStart;
|
||||
float fogScale;
|
||||
int buffer_matrix_offset;
|
||||
|
||||
vec4 clip_equation;
|
||||
|
||||
float thruster_scale;
|
||||
bool use_clip_plane;
|
||||
int n_lights;
|
||||
float defaultGloss;
|
||||
|
||||
vec3 ambientFactor;
|
||||
int desaturate;
|
||||
|
||||
vec3 diffuseFactor;
|
||||
int blend_alpha;
|
||||
|
||||
vec3 emissionFactor;
|
||||
bool overrideDiffuse;
|
||||
|
||||
vec3 diffuseClr;
|
||||
bool overrideGlow;
|
||||
|
||||
vec3 glowClr;
|
||||
bool overrideSpec;
|
||||
|
||||
vec3 specClr;
|
||||
bool alphaGloss;
|
||||
|
||||
bool gammaSpec;
|
||||
bool envGloss;
|
||||
bool alpha_spec;
|
||||
int effect_num;
|
||||
|
||||
vec4 fogColor;
|
||||
|
||||
vec3 base_color;
|
||||
float anim_timer;
|
||||
|
||||
vec3 stripe_color;
|
||||
float vpwidth;
|
||||
|
||||
float vpheight;
|
||||
bool team_glow_enabled;
|
||||
float znear;
|
||||
float zfar;
|
||||
|
||||
float veryneardist;
|
||||
float neardist;
|
||||
float middist;
|
||||
float fardist;
|
||||
|
||||
vec2 normalAlphaMinMax;
|
||||
int sBasemapIndex;
|
||||
int sGlowmapIndex;
|
||||
|
||||
int sSpecmapIndex;
|
||||
int sNormalmapIndex;
|
||||
int sAmbientmapIndex;
|
||||
int sMiscmapIndex;
|
||||
};
|
||||
#endif
|
||||
|
||||
uniform samplerBuffer transform_tex;
|
||||
|
||||
out VertexOutput {
|
||||
#ifdef FLAG_ENV_MAP
|
||||
vec3 envReflect;
|
||||
#endif
|
||||
#ifdef FLAG_NORMAL_MAP
|
||||
mat3 tangentMatrix;
|
||||
#endif
|
||||
#ifdef FLAG_FOG
|
||||
float fogDist;
|
||||
#endif
|
||||
#ifdef WORKAROUND_CLIPPING_PLANES
|
||||
#ifdef FLAG_TRANSFORM
|
||||
float notVisible;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef FLAG_SHADOW_MAP
|
||||
#if !defined(GL_ARB_gpu_shader5)
|
||||
float instance;
|
||||
#endif
|
||||
#else
|
||||
vec4 position;
|
||||
#endif
|
||||
vec3 normal;
|
||||
vec4 texCoord;
|
||||
#ifdef FLAG_SHADOWS
|
||||
vec4 shadowUV[4];
|
||||
vec4 shadowPos;
|
||||
#endif
|
||||
} vertOut;
|
||||
|
||||
#ifdef FLAG_TRANSFORM
|
||||
#define TEXELS_PER_MATRIX 4
|
||||
void getModelTransform(inout mat4 transform, out bool invisible, int id, int matrix_offset)
|
||||
{
|
||||
transform[0] = texelFetch(transform_tex, (matrix_offset + id) * TEXELS_PER_MATRIX);
|
||||
transform[1] = texelFetch(transform_tex, (matrix_offset + id) * TEXELS_PER_MATRIX + 1);
|
||||
transform[2] = texelFetch(transform_tex, (matrix_offset + id) * TEXELS_PER_MATRIX + 2);
|
||||
transform[3] = texelFetch(transform_tex, (matrix_offset + id) * TEXELS_PER_MATRIX + 3);
|
||||
invisible = transform[3].w >= 0.9;
|
||||
transform[3].w = 1.0;
|
||||
}
|
||||
#endif
|
||||
void main()
|
||||
{
|
||||
vec4 position;
|
||||
vec3 normal;
|
||||
vec4 texCoord;
|
||||
mat4 orient = mat4(1.0);
|
||||
mat4 scale = mat4(1.0);
|
||||
#ifdef WORKAROUND_CLIPPING_PLANES
|
||||
#ifdef FLAG_TRANSFORM
|
||||
bool clipModel;
|
||||
getModelTransform(orient, clipModel, int(vertModelID), buffer_matrix_offset);
|
||||
vertOut.notVisible = clipModel ? 1.0 : 0.0;
|
||||
#endif
|
||||
#else
|
||||
#ifdef FLAG_TRANSFORM
|
||||
bool clipModel;
|
||||
getModelTransform(orient, clipModel, int(vertModelID), buffer_matrix_offset);
|
||||
#else
|
||||
bool clipModel = false;
|
||||
#endif
|
||||
#endif
|
||||
texCoord = textureMatrix * vertTexCoord;
|
||||
vec4 vertex = vertPosition;
|
||||
#ifdef FLAG_THRUSTER
|
||||
if(vertex.z < -1.5) {
|
||||
vertex.z *= thruster_scale;
|
||||
}
|
||||
#endif
|
||||
// Transform the normal into eye space and normalize the result.
|
||||
normal = normalize(mat3(modelViewMatrix) * mat3(orient) * vertNormal);
|
||||
position = modelViewMatrix * orient * vertex;
|
||||
#ifdef FLAG_SHADOW_MAP
|
||||
gl_Position = position;
|
||||
#if !defined(GL_ARB_gpu_shader5)
|
||||
#ifdef APPLE
|
||||
vertOut.instance = float(gl_InstanceIDARB);
|
||||
#else
|
||||
vertOut.instance = float(gl_InstanceID);
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
gl_Position = projMatrix * position;
|
||||
#endif
|
||||
#ifdef FLAG_NORMAL_EXTRUDE
|
||||
gl_Position.xy += (mat3(projMatrix) * normal.xyz).xy * gl_Position.z * extrudeWidth;
|
||||
#endif
|
||||
#ifdef FLAG_SHADOWS
|
||||
vec4 shadowPos = shadow_mv_matrix * modelMatrix * orient * vertPosition;
|
||||
vertOut.shadowPos = shadow_mv_matrix * modelMatrix * orient * vertPosition;
|
||||
vertOut.shadowUV[0] = transformToShadowMap(shadow_proj_matrix[0], 0, shadowPos);
|
||||
vertOut.shadowUV[1] = transformToShadowMap(shadow_proj_matrix[1], 1, shadowPos);
|
||||
vertOut.shadowUV[2] = transformToShadowMap(shadow_proj_matrix[2], 2, shadowPos);
|
||||
vertOut.shadowUV[3] = transformToShadowMap(shadow_proj_matrix[3], 3, shadowPos);
|
||||
#endif
|
||||
#ifdef FLAG_NORMAL_MAP
|
||||
// Setup stuff for normal maps
|
||||
vec3 t = normalize(mat3(modelViewMatrix) * mat3(orient) * vertTangent.xyz);
|
||||
vec3 b = cross(normal, t) * vertTangent.w;
|
||||
vertOut.tangentMatrix = mat3(t, b, normal);
|
||||
#endif
|
||||
#ifdef FLAG_ENV_MAP
|
||||
// Environment mapping reflection vector.
|
||||
vec3 envReflect = reflect(normalize(position.xyz), normal);
|
||||
vertOut.envReflect = vec3(envMatrix * vec4(envReflect, 0.0));
|
||||
#endif
|
||||
#ifdef FLAG_FOG
|
||||
vertOut.fogDist = clamp((gl_Position.z - fogStart) * 0.75 * fogScale, 0.0, 1.0);
|
||||
#endif
|
||||
#ifdef WORKAROUND_CLIPPING_PLANES
|
||||
if(use_clip_plane) {
|
||||
gl_ClipDistance[0] = dot(clip_equation, modelMatrix * orient * vertex);
|
||||
} else {
|
||||
gl_ClipDistance[0] = 1.0;
|
||||
}
|
||||
#else
|
||||
#ifdef FLAG_CLIP
|
||||
if (clipModel) {
|
||||
gl_ClipDistance[0] = -1.0;
|
||||
} else if(use_clip_plane) {
|
||||
gl_ClipDistance[0] = dot(clip_equation, modelMatrix * orient * vertex);
|
||||
} else {
|
||||
gl_ClipDistance[0] = 1.0;
|
||||
}
|
||||
#elif defined(FLAG_TRANSFORM)
|
||||
gl_ClipDistance[0] = clipModel ? -1.0 : 1.0;
|
||||
#endif
|
||||
#endif
|
||||
#ifndef FLAG_SHADOW_MAP
|
||||
vertOut.position = position;
|
||||
vertOut.normal = normal;
|
||||
vertOut.texCoord = texCoord;
|
||||
#else
|
||||
vertOut.normal = normal;
|
||||
vertOut.texCoord = texCoord;
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
|
||||
// Enabled at all times but the define is kept here in case this should be configurable in the future
|
||||
#define EDGE_AA
|
||||
|
||||
layout(std140) uniform NanoVGUniformData {
|
||||
mat3 scissorMat;
|
||||
|
||||
mat3 paintMat;
|
||||
|
||||
vec4 innerCol;
|
||||
|
||||
vec4 outerCol;
|
||||
|
||||
vec2 scissorExt;
|
||||
vec2 scissorScale;
|
||||
|
||||
vec2 extent;
|
||||
float radius;
|
||||
float feather;
|
||||
|
||||
float strokeMult;
|
||||
float strokeThr;
|
||||
int texType;
|
||||
int type;
|
||||
|
||||
vec2 viewSize;
|
||||
int texArrayIndex;
|
||||
};
|
||||
|
||||
uniform sampler2DArray nvg_tex;
|
||||
|
||||
in vec2 ftcoord;
|
||||
in vec2 fpos;
|
||||
out vec4 outColor;
|
||||
|
||||
float sdroundrect(vec2 pt, vec2 ext, float rad) {
|
||||
vec2 ext2 = ext - vec2(rad,rad);
|
||||
vec2 d = abs(pt) - ext2;
|
||||
return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rad;
|
||||
}
|
||||
|
||||
// Scissoring
|
||||
float scissorMask(vec2 p) {
|
||||
vec2 sc = (abs((scissorMat * vec3(p,1.0)).xy) - scissorExt);
|
||||
sc = vec2(0.5,0.5) - sc * scissorScale;
|
||||
return clamp(sc.x,0.0,1.0) * clamp(sc.y,0.0,1.0);
|
||||
}
|
||||
#ifdef EDGE_AA
|
||||
// Stroke - from [0..1] to clipped pyramid, where the slope is 1px.
|
||||
float strokeMask() {
|
||||
return min(1.0, (1.0-abs(ftcoord.x*2.0-1.0))*strokeMult) * min(1.0, ftcoord.y);
|
||||
}
|
||||
#endif
|
||||
|
||||
void main(void) {
|
||||
vec4 result;
|
||||
float scissor = scissorMask(fpos);
|
||||
#ifdef EDGE_AA
|
||||
float strokeAlpha = strokeMask();
|
||||
#else
|
||||
float strokeAlpha = 1.0;
|
||||
#endif
|
||||
#ifdef EDGE_AA
|
||||
if (strokeAlpha < strokeThr) {
|
||||
discard;
|
||||
}
|
||||
#endif
|
||||
if (type == 0) { // Gradient
|
||||
// Calculate gradient color using box gradient
|
||||
vec2 pt = (paintMat * vec3(fpos,1.0)).xy;
|
||||
float d = clamp((sdroundrect(pt, extent, radius) + feather*0.5) / feather, 0.0, 1.0);
|
||||
vec4 color = mix(innerCol,outerCol,d);
|
||||
// Combine alpha
|
||||
color *= strokeAlpha * scissor;
|
||||
result = color;
|
||||
} else if (type == 1) { // Image
|
||||
// Calculate color fron texture
|
||||
vec2 pt = (paintMat * vec3(fpos,1.0)).xy / extent;
|
||||
vec4 color = texture(nvg_tex, vec3(pt, float(texArrayIndex)));
|
||||
// Apply color tint and alpha.
|
||||
if (texType == 1) color = vec4(color.xyz*color.w,color.w);
|
||||
if (texType == 2) color = vec4(color.r);
|
||||
color *= innerCol;
|
||||
// Combine alpha
|
||||
color *= strokeAlpha * scissor;
|
||||
result = color;
|
||||
} else if (type == 2) { // Stencil fill
|
||||
result = vec4(1,1,1,1);
|
||||
} else if (type == 3) { // Textured tris
|
||||
vec4 color = texture(nvg_tex, vec3(ftcoord, float(texArrayIndex)));
|
||||
if (texType == 1) color = vec4(color.xyz*color.w,color.w); if (texType == 2) color = vec4(color.x); color *= scissor;
|
||||
result = color * innerCol;
|
||||
}
|
||||
outColor = result;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
|
||||
layout(std140) uniform NanoVGUniformData {
|
||||
mat3 scissorMat;
|
||||
|
||||
mat3 paintMat;
|
||||
|
||||
vec4 innerCol;
|
||||
|
||||
vec4 outerCol;
|
||||
|
||||
vec2 scissorExt;
|
||||
vec2 scissorScale;
|
||||
|
||||
vec2 extent;
|
||||
float radius;
|
||||
float feather;
|
||||
|
||||
float strokeMult;
|
||||
float strokeThr;
|
||||
int texType;
|
||||
int type;
|
||||
|
||||
vec2 viewSize;
|
||||
int texArrayIndex;
|
||||
};
|
||||
|
||||
in vec2 vertPosition;
|
||||
in vec2 vertTexCoord;
|
||||
|
||||
out vec2 ftcoord;
|
||||
out vec2 fpos;
|
||||
|
||||
void main(void) {
|
||||
ftcoord = vertTexCoord;
|
||||
fpos = vertPosition;
|
||||
gl_Position = vec4(2.0*vertPosition.x/viewSize.x - 1.0, 1.0 - 2.0*vertPosition.y/viewSize.y, 0, 1);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
/**
|
||||
* @brief Unpacks a DXT5nm encoded normal
|
||||
* @param normal_sample The Alpha and Green channel of the DXT5 texture
|
||||
* @return The unpacked normal
|
||||
*/
|
||||
vec3 unpackNormal(vec2 normal_sample) {
|
||||
vec3 normal;
|
||||
normal.xy = normal_sample * 2.0 - 1.0;
|
||||
normal.z = sqrt(1 - normal_sample.x * normal_sample.x - normal_sample.y * normal_sample.y);
|
||||
return normal;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
#include "gamma.sdr"
|
||||
|
||||
in vec4 fragTexCoord;
|
||||
in vec4 fragColor;
|
||||
out vec4 fragOut0;
|
||||
uniform sampler2D baseMap;
|
||||
uniform int noTexturing;
|
||||
uniform int srgb;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 baseColor = texture(baseMap, fragTexCoord.xy);
|
||||
|
||||
baseColor.rgb = (srgb == 1) ? srgb_to_linear(baseColor.rgb) : baseColor.rgb;
|
||||
vec4 blendColor = (srgb == 1) ? vec4(srgb_to_linear(fragColor.rgb), fragColor.a) : fragColor;
|
||||
fragOut0 = mix(baseColor * blendColor, blendColor, float(noTexturing));
|
||||
}
|
||||
File renamed without changes.
@@ -26,6 +26,9 @@ uniform float cutoff;
|
||||
#ifdef FLAG_DITH
|
||||
uniform float dither;
|
||||
#endif
|
||||
#ifdef FLAG_TINT
|
||||
uniform vec3 tint;
|
||||
#endif
|
||||
uniform sampler2D blurred_tex;
|
||||
uniform sampler2D depth_tex;
|
||||
void main()
|
||||
@@ -98,7 +101,10 @@ void main()
|
||||
float downsampling_factor = 4;
|
||||
float bias = 0.5;
|
||||
color_out.rgb = floor(color_out.rgb * downsampling_factor + bias) / downsampling_factor;
|
||||
#endif
|
||||
#ifdef FLAG_TINT
|
||||
color_out.rgb += tint;
|
||||
#endif
|
||||
color_out.a = 1.0;
|
||||
fragOut0 = color_out;
|
||||
}
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
Loaded 100 of 724 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user