mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-02-02 13:23:27 +08:00
Merge branch 'efroemling:master' into master
This commit is contained in:
commit
36127a9021
130
.efrocachemap
generated
130
.efrocachemap
generated
@ -421,42 +421,42 @@
|
||||
"build/assets/ba_data/audio/zoeOw.ogg": "74befe45a8417e95b6a2233c51992a26",
|
||||
"build/assets/ba_data/audio/zoePickup01.ogg": "48ab8cddfcde36a750856f3f81dd20c8",
|
||||
"build/assets/ba_data/audio/zoeScream01.ogg": "2b468aedfa8741090247f04eb9e6df55",
|
||||
"build/assets/ba_data/data/langdata.json": "992c5c5ce292132c4f011f39e0d13de8",
|
||||
"build/assets/ba_data/data/languages/arabic.json": "d1f900ab5aa2433d402bd46ed1149cc7",
|
||||
"build/assets/ba_data/data/langdata.json": "2a2c4783fddc4b24d07b4ce0d8a74393",
|
||||
"build/assets/ba_data/data/languages/arabic.json": "295c559911fa251f401f8cdcad91c226",
|
||||
"build/assets/ba_data/data/languages/belarussian.json": "e151808b6b4f6dc159cf55ee62adad3c",
|
||||
"build/assets/ba_data/data/languages/chinese.json": "8d889accdd49334591209bdaf6eaf02f",
|
||||
"build/assets/ba_data/data/languages/chinese.json": "0a9d9534e7329d1e886adae6fdc007c4",
|
||||
"build/assets/ba_data/data/languages/chinesetraditional.json": "f858da49be0a5374157c627857751078",
|
||||
"build/assets/ba_data/data/languages/croatian.json": "766532c67af5bd0144c2d63cab0516fa",
|
||||
"build/assets/ba_data/data/languages/czech.json": "93c5fe0d884d95435da6c675f64e30e0",
|
||||
"build/assets/ba_data/data/languages/danish.json": "3fd69080783d5c9dcc0af737f02b6f1e",
|
||||
"build/assets/ba_data/data/languages/dutch.json": "22b44a33bf81142ba2befad14eb5746e",
|
||||
"build/assets/ba_data/data/languages/english.json": "b38d54aecf3ac47b8d8ca97d8bab3006",
|
||||
"build/assets/ba_data/data/languages/english.json": "6fb6ec37e79064edb4b8864eabdd024d",
|
||||
"build/assets/ba_data/data/languages/esperanto.json": "0e397cfa5f3fb8cef5f4a64f21cda880",
|
||||
"build/assets/ba_data/data/languages/filipino.json": "347f38524816691170d266708fe25894",
|
||||
"build/assets/ba_data/data/languages/french.json": "d8527da977a563185de25ef02bacf826",
|
||||
"build/assets/ba_data/data/languages/german.json": "549754d2a530d825200c6126be56df5c",
|
||||
"build/assets/ba_data/data/languages/gibberish.json": "837423db378b3e7679683805826aa26e",
|
||||
"build/assets/ba_data/data/languages/greek.json": "a65d78f912e9a89f98de004405167a6a",
|
||||
"build/assets/ba_data/data/languages/hindi.json": "88ee0cda537bab9ac827def5e236fe1a",
|
||||
"build/assets/ba_data/data/languages/filipino.json": "2efdfb879135b196a272dd47fc2039a2",
|
||||
"build/assets/ba_data/data/languages/french.json": "4e218dcd488fa63e7db5b4da2261b9e1",
|
||||
"build/assets/ba_data/data/languages/german.json": "450fa41ae264f29a5d1af22143d0d0ad",
|
||||
"build/assets/ba_data/data/languages/gibberish.json": "63c6212c774622346f3ad0d87ff31e80",
|
||||
"build/assets/ba_data/data/languages/greek.json": "287c0ec437b38772284ef9d3e4fb2fc3",
|
||||
"build/assets/ba_data/data/languages/hindi.json": "8ea0c58a44a24edb131d0e53b074d1f6",
|
||||
"build/assets/ba_data/data/languages/hungarian.json": "796a290a8c44a1e7635208c2ff5fdc6e",
|
||||
"build/assets/ba_data/data/languages/indonesian.json": "bff88ce57744a639810b93a1d1dd79f4",
|
||||
"build/assets/ba_data/data/languages/italian.json": "338e7a03dff47f4eefc0ca3a995cd4f4",
|
||||
"build/assets/ba_data/data/languages/indonesian.json": "53961b1484a1831f32bec2cc2941e672",
|
||||
"build/assets/ba_data/data/languages/italian.json": "58ecf53a963dbeca1bbf3605e5ab6a2f",
|
||||
"build/assets/ba_data/data/languages/korean.json": "ca1122a9ee551da3f75ae632012bd0e2",
|
||||
"build/assets/ba_data/data/languages/malay.json": "832562ce997fc70704b9234c95fb2e38",
|
||||
"build/assets/ba_data/data/languages/persian.json": "71cc5b33abda0f285b970b8cc4a014a8",
|
||||
"build/assets/ba_data/data/languages/polish.json": "e1a1a801851924748ad38fa68216439a",
|
||||
"build/assets/ba_data/data/languages/portuguese.json": "9fcd6b4da9e5d0dc0e337ab00b5debe2",
|
||||
"build/assets/ba_data/data/languages/persian.json": "a391d80ff58ea22926499e4b19d2c0d0",
|
||||
"build/assets/ba_data/data/languages/polish.json": "7a4a6cb882cf90dad32e6607215525bf",
|
||||
"build/assets/ba_data/data/languages/portuguese.json": "51e362956f89da3eec980f587c092253",
|
||||
"build/assets/ba_data/data/languages/romanian.json": "aeebdd54f65939c2facc6ac50c117826",
|
||||
"build/assets/ba_data/data/languages/russian.json": "910cf653497654a16d5c4f067d6def22",
|
||||
"build/assets/ba_data/data/languages/russian.json": "561504cca28eb3204ac194950029e565",
|
||||
"build/assets/ba_data/data/languages/serbian.json": "d7452dd72ac0e51680cb39b5ebaa1c69",
|
||||
"build/assets/ba_data/data/languages/slovak.json": "27962d53dc3f7dd4e877cd40faafeeef",
|
||||
"build/assets/ba_data/data/languages/spanish.json": "0122b0b24aa111ab259af02bbae9b7b6",
|
||||
"build/assets/ba_data/data/languages/spanish.json": "5a4dbd505060dd02d634bd4de4d5faab",
|
||||
"build/assets/ba_data/data/languages/swedish.json": "77d671f10613291ebf9c71da66f18a18",
|
||||
"build/assets/ba_data/data/languages/tamil.json": "b9d4b4e107456ea6420ee0f9d9d7a03e",
|
||||
"build/assets/ba_data/data/languages/tamil.json": "65ab7798d637fa62a703750179eeb723",
|
||||
"build/assets/ba_data/data/languages/thai.json": "33f63753c9af9a5b238d229a0bf23fbc",
|
||||
"build/assets/ba_data/data/languages/turkish.json": "9d7e58c9062dc517c3779c255a9b3142",
|
||||
"build/assets/ba_data/data/languages/turkish.json": "776d1a0c9ef2333a9110d93558ab19e2",
|
||||
"build/assets/ba_data/data/languages/ukrainian.json": "f72eb51abfbbb56e27866895d7e947d2",
|
||||
"build/assets/ba_data/data/languages/venetian.json": "88595b7ee696b4094d7874c3c4188852",
|
||||
"build/assets/ba_data/data/languages/venetian.json": "9fe1a58d9e5dfb00f31ce3b2eb9993f4",
|
||||
"build/assets/ba_data/data/languages/vietnamese.json": "921cd1e50f60fe3e101f246e172750ba",
|
||||
"build/assets/ba_data/data/maps/big_g.json": "1dd301d490643088a435ce75df971054",
|
||||
"build/assets/ba_data/data/maps/bridgit.json": "6aea74805f4880cc11237c5734a24422",
|
||||
@ -4056,53 +4056,53 @@
|
||||
"build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1",
|
||||
"build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae",
|
||||
"build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "8ea626f6dd70d998ee77c58fffc51545",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "d7ad10904fe7c4d4555366ccb1feedcb",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "a9eea92d521e97b1772b5e44b402ce8a",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "57043f40971d800d27ee6d646aae8d61",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "3e4009d7fa4b90abc526f56361ecab05",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "334cdc0688e66a1bc75cd05bae1729c7",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "95278d80378be5b61026253492cbfa70",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "0a3da8e5264a7b733960e83a0e8c4bba",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "a089d4aaa39e18553cf0a70a77b4cfcd",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "e66c4eceb79710b8fda2bfea781e241a",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "6b3021b9a7584da86bbb95324e81e851",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "6fe90d50f905a0da9fa52c39a458d1e3",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "afc7ed826486aec82613832865177570",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "ae8b6dad770793188aad8e2966189bb3",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "7622dde1a021152cac42e7db3e803392",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "8a4b1e521bf668cc6ec6a65519defb12",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "9a69c0d4c9ae319595843b16f14795fc",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "89c02260fb4781f5e293658cecbb363f",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "3165d4230069c22300abfff8abf1d714",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "41e46dfdbb542e3e823f6aee87e93ac9",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "20cc128ff9d44f9d74e4301c6d49f48f",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "f93bc8f98ee31f39b54ab46264eccb22",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "20cc128ff9d44f9d74e4301c6d49f48f",
|
||||
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "f93bc8f98ee31f39b54ab46264eccb22",
|
||||
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "1565f3b227843827d692cb3ef65847b6",
|
||||
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "d8d74c6c40db43054ccc7d27920cfbfe",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "1565f3b227843827d692cb3ef65847b6",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "d8d74c6c40db43054ccc7d27920cfbfe",
|
||||
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "43bdfa8acd84e9cf2e443ce8e923c229",
|
||||
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "d935dc21becdfd65bec51c5f5b2fd770",
|
||||
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "43bdfa8acd84e9cf2e443ce8e923c229",
|
||||
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "d935dc21becdfd65bec51c5f5b2fd770",
|
||||
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "6d49ad39f194480da458b431405f5a2b",
|
||||
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "c8715c85010ea431d7346f40f5421819",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "49775819d4ba9af15061080d17377a18",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "c8715c85010ea431d7346f40f5421819",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "750c2964308cd3f3e5986fcda9a25706",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "772af7da6a115f53b0b3f6a4afd3baec",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "178c1a53a7ad50297aed68d0ca3a1476",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "b011bc2b6437995f2d33f5215b4ffa36",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "a758a4f98336208381b093aacb735878",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "6c86545fab2327105114676a20ca5e68",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "f75525fdc9f7db4a81ca9bae6a79add5",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "2e297069baec404d43ccdb18abeef658",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "ad13d636bcb25150044a7644846b8a09",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "7c5df955611590ef491bf614fbd60179",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "ae38bd212ae64b51482a2ccb9c1cbfd3",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "d0bcee2dd5567719aa35667c5206dffc",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "eb3cd4f86175afcf8ffa2749afa32fa3",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "6b86ba36c3719773008feaa6cdc0d0f8",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "877c9ae4532fef809a3dcbd8ffea343c",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "7df313c48c87460f56fa837502965088",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "bc40bb549d26437fb8679c1e9d088272",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "47aa08cb9f5e660023f0f3c0e4ffd65e",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "bbb0a8383d6ce1ca887190ea49223f4f",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "7dd91e3407d49981c1c975d4f01ac205",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "c87883aa2f832e792e945fd9208d712a",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "fea8fd84d8c060f2f82f402902b8c54e",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "0a454a8be47f37231655761d15e3f7e5",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "4c79db3a882eb0b8b225a8df0339b1cc",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "43b9ef321f8e80da29ddb19a760dbd77",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "6f891004f2f07c452dea29bd53f29d30",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "bf7a1ce0e7a2015d538406c6f6df761c",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "cf213dce81901a67c9970b3befdaa320",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "473e7e6c0cf90b9e6ac653552b18f68d",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "4e11b895cbf2e1339cf34bc06c54a4ea",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "473e7e6c0cf90b9e6ac653552b18f68d",
|
||||
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "4e11b895cbf2e1339cf34bc06c54a4ea",
|
||||
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "d9af1a429cff9346e0cad6fcea017e5b",
|
||||
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "ae5f87286947575463c386cfe1c443e4",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "d9af1a429cff9346e0cad6fcea017e5b",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "ae5f87286947575463c386cfe1c443e4",
|
||||
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "110eef3dc285a35a1899510e368c73b1",
|
||||
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "2692dc69f7cb2501f0aaa8675f559987",
|
||||
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "110eef3dc285a35a1899510e368c73b1",
|
||||
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "2692dc69f7cb2501f0aaa8675f559987",
|
||||
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "344954c4f788d7d9b4d7035ebb6131d8",
|
||||
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "48c4873dae2344c1d4092a1d85dab424",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "abcede4e60fa8877f18e66e086fb7387",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "48c4873dae2344c1d4092a1d85dab424",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "6149911c660a9864b651cc1a8e50eec1",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "57cef68ab703ba819bd0fbe9e4b1c331",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "a47bab28b86c7cefce891b8e5c8b687a",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "d68ebb1139363d711b044de65e17b204",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "ea1349137f64f3d662b9a95278ca4c02",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "c8731ff226716cee3d1e46027ead1cfe",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "4ee6b633a99c5bcbea4f5dee5bda186e",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "2c3bd4952b30d88247229ad309f73092",
|
||||
"src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
|
||||
"src/assets/ba_data/python/babase/_mgen/enums.py": "28323912b56ec07701eda3d41a6a4101",
|
||||
"src/ballistica/base/mgen/pyembed/binding_base.inc": "ba8ce3ca3858b4c2d20db68f99b788b2",
|
||||
"src/ballistica/base/mgen/pyembed/binding_base.inc": "6df0f34207346d89a72924249ddd4706",
|
||||
"src/ballistica/base/mgen/pyembed/binding_base_app.inc": "00f81f9bd92386ec12a6e60170678a98",
|
||||
"src/ballistica/classic/mgen/pyembed/binding_classic.inc": "3ceb412513963f0818ab39c58bf292e3",
|
||||
"src/ballistica/core/mgen/pyembed/binding_core.inc": "9d0a3c9636138e35284923e0c8311c69",
|
||||
|
||||
11
CHANGELOG.md
11
CHANGELOG.md
@ -1,4 +1,4 @@
|
||||
### 1.7.28 (build 21465, api 8, 2023-10-14)
|
||||
### 1.7.28 (build 21491, api 8, 2023-10-22)
|
||||
|
||||
- Massively cleaned up code related to rendering and window systems (OpenGL,
|
||||
SDL, etc). This code had been growing into a nasty tangle for 15 years
|
||||
@ -146,7 +146,14 @@
|
||||
rates are high.
|
||||
- Added a proper graceful shutdown process for the audio server. This should
|
||||
result in fewer ugly pops and warning messages when the app is quit.
|
||||
|
||||
- Tidied up some keyboard shortcuts to be more platform-appropriate. For
|
||||
example, toggling fullscreen on Windows is now Alt+Enter or F11.
|
||||
- Fancy rebuilt Mac build should now automatically sync its frame rate to the
|
||||
display its running on (using CVDisplayLinks, not VSync).
|
||||
- Mac build is now relying solely on Apple's Game Controller Framework, which
|
||||
seems pretty awesome these days. It should support most stuff SDL does and
|
||||
with less configuring involved. Please holler if you come across something
|
||||
that doesn't work.
|
||||
|
||||
### 1.7.27 (build 21282, api 8, 2023-08-30)
|
||||
|
||||
|
||||
@ -353,6 +353,10 @@ set(BALLISTICA_SOURCES
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/support/camera.h
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/support/frame_def.cc
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/support/frame_def.h
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/support/graphics_client_context.cc
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/support/graphics_client_context.h
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/support/graphics_settings.cc
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/support/graphics_settings.h
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/support/net_graph.cc
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/support/net_graph.h
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/support/render_command_buffer.h
|
||||
@ -688,6 +692,7 @@ set(BALLISTICA_SOURCES
|
||||
${BA_SRC_ROOT}/ballistica/shared/generic/lambda_runnable.h
|
||||
${BA_SRC_ROOT}/ballistica/shared/generic/runnable.cc
|
||||
${BA_SRC_ROOT}/ballistica/shared/generic/runnable.h
|
||||
${BA_SRC_ROOT}/ballistica/shared/generic/snapshot.h
|
||||
${BA_SRC_ROOT}/ballistica/shared/generic/timer_list.cc
|
||||
${BA_SRC_ROOT}/ballistica/shared/generic/timer_list.h
|
||||
${BA_SRC_ROOT}/ballistica/shared/generic/utf8.cc
|
||||
|
||||
@ -345,6 +345,10 @@
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\camera.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\frame_def.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\frame_def.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_settings.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_settings.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\net_graph.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\net_graph.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\render_command_buffer.h" />
|
||||
@ -680,6 +684,7 @@
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\lambda_runnable.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\shared\generic\runnable.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\runnable.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\snapshot.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\shared\generic\timer_list.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\timer_list.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\shared\generic\utf8.cc" />
|
||||
|
||||
@ -469,6 +469,18 @@
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\frame_def.h">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.cc">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.h">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_settings.cc">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_settings.h">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\net_graph.cc">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClCompile>
|
||||
@ -1474,6 +1486,9 @@
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\runnable.h">
|
||||
<Filter>ballistica\shared\generic</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\snapshot.h">
|
||||
<Filter>ballistica\shared\generic</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\shared\generic\timer_list.cc">
|
||||
<Filter>ballistica\shared\generic</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@ -340,6 +340,10 @@
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\camera.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\frame_def.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\frame_def.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_settings.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_settings.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\net_graph.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\net_graph.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\render_command_buffer.h" />
|
||||
@ -675,6 +679,7 @@
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\lambda_runnable.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\shared\generic\runnable.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\runnable.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\snapshot.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\shared\generic\timer_list.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\timer_list.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\shared\generic\utf8.cc" />
|
||||
|
||||
@ -469,6 +469,18 @@
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\frame_def.h">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.cc">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.h">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_settings.cc">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_settings.h">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\net_graph.cc">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClCompile>
|
||||
@ -1474,6 +1486,9 @@
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\runnable.h">
|
||||
<Filter>ballistica\shared\generic</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\snapshot.h">
|
||||
<Filter>ballistica\shared\generic</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\shared\generic\timer_list.cc">
|
||||
<Filter>ballistica\shared\generic</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@ -27,7 +27,10 @@ from _babase import (
|
||||
apptime,
|
||||
apptimer,
|
||||
AppTimer,
|
||||
can_toggle_fullscreen,
|
||||
fullscreen_control_available,
|
||||
fullscreen_control_get,
|
||||
fullscreen_control_key_shortcut,
|
||||
fullscreen_control_set,
|
||||
charstr,
|
||||
clipboard_get_text,
|
||||
clipboard_has_text,
|
||||
@ -200,7 +203,10 @@ __all__ = [
|
||||
'apptimer',
|
||||
'AppTimer',
|
||||
'Call',
|
||||
'can_toggle_fullscreen',
|
||||
'fullscreen_control_available',
|
||||
'fullscreen_control_get',
|
||||
'fullscreen_control_key_shortcut',
|
||||
'fullscreen_control_set',
|
||||
'charstr',
|
||||
'clipboard_get_text',
|
||||
'clipboard_has_text',
|
||||
|
||||
@ -895,10 +895,18 @@ class App:
|
||||
import asyncio
|
||||
|
||||
# Spin and wait for anything blocking shutdown to complete.
|
||||
starttime = _babase.apptime()
|
||||
_babase.lifecyclelog('shutdown-suppress wait begin')
|
||||
while _babase.shutdown_suppress_count() > 0:
|
||||
await asyncio.sleep(0.001)
|
||||
_babase.lifecyclelog('shutdown-suppress wait end')
|
||||
duration = _babase.apptime() - starttime
|
||||
if duration > 1.0:
|
||||
logging.warning(
|
||||
'Shutdown-suppressions lasted longer than ideal '
|
||||
'(%.2f seconds).',
|
||||
duration,
|
||||
)
|
||||
|
||||
async def _fade_and_shutdown_graphics(self) -> None:
|
||||
import asyncio
|
||||
|
||||
@ -185,10 +185,8 @@ def _feed_logs_to_babase(log_handler: LogHandler) -> None:
|
||||
def _on_log(entry: LogEntry) -> None:
|
||||
# Forward this along to the engine to display in the in-app
|
||||
# console, in the Android log, etc.
|
||||
_babase.display_log(
|
||||
name=entry.name,
|
||||
level=entry.level.name,
|
||||
message=entry.message,
|
||||
_babase.emit_log(
|
||||
name=entry.name, level=entry.level.name, message=entry.message
|
||||
)
|
||||
|
||||
# We also want to feed some logs to the old v1-cloud-log system.
|
||||
|
||||
@ -33,18 +33,30 @@ def reset_to_main_menu() -> None:
|
||||
logging.warning('reset_to_main_menu: no-op due to classic not present.')
|
||||
|
||||
|
||||
def set_config_fullscreen_on() -> None:
|
||||
def store_config_fullscreen_on() -> None:
|
||||
"""The OS has changed our fullscreen state and we should take note."""
|
||||
_babase.app.config['Fullscreen'] = True
|
||||
_babase.app.config.commit()
|
||||
|
||||
|
||||
def set_config_fullscreen_off() -> None:
|
||||
def store_config_fullscreen_off() -> None:
|
||||
"""The OS has changed our fullscreen state and we should take note."""
|
||||
_babase.app.config['Fullscreen'] = False
|
||||
_babase.app.config.commit()
|
||||
|
||||
|
||||
def set_config_fullscreen_on() -> None:
|
||||
"""Set and store fullscreen state"""
|
||||
_babase.app.config['Fullscreen'] = True
|
||||
_babase.app.config.apply_and_commit()
|
||||
|
||||
|
||||
def set_config_fullscreen_off() -> None:
|
||||
"""The OS has changed our fullscreen state and we should take note."""
|
||||
_babase.app.config['Fullscreen'] = False
|
||||
_babase.app.config.apply_and_commit()
|
||||
|
||||
|
||||
def not_signed_in_screen_message() -> None:
|
||||
from babase._language import Lstr
|
||||
|
||||
@ -377,3 +389,17 @@ def string_edit_adapter_can_be_replaced(adapter: StringEditAdapter) -> bool:
|
||||
def get_dev_console_tab_names() -> list[str]:
|
||||
"""Return the current set of dev-console tab names."""
|
||||
return [t.name for t in _babase.app.devconsole.tabs]
|
||||
|
||||
|
||||
def unsupported_controller_message(name: str) -> None:
|
||||
"""Print a message when an unsupported controller is connected."""
|
||||
from babase._language import Lstr
|
||||
|
||||
# Ick; this can get called early in the bootstrapping process
|
||||
# before we're allowed to load assets. Guard against that.
|
||||
if _babase.asset_loads_allowed():
|
||||
_babase.getsimplesound('error').play()
|
||||
_babase.screenmessage(
|
||||
Lstr(resource='unsupportedControllerText', subs=[('${NAME}', name)]),
|
||||
color=(1, 0, 0),
|
||||
)
|
||||
|
||||
@ -40,7 +40,7 @@ if TYPE_CHECKING:
|
||||
# the last load. Either way, however, multiple execs will happen in some
|
||||
# form.
|
||||
#
|
||||
# So we need to do a few things to handle that situation gracefully.
|
||||
# To handle that situation gracefully, we need to do a few things:
|
||||
#
|
||||
# - First, we need to store any mutable global state in the __main__
|
||||
# module; not in ourself. This way, alternate versions of ourself will
|
||||
@ -48,11 +48,11 @@ if TYPE_CHECKING:
|
||||
#
|
||||
# - Second, we should avoid the use of isinstance and similar calls for
|
||||
# our types. An EnvConfig we create would technically be a different
|
||||
# type than that created by an alternate baenv.
|
||||
# type than an EnvConfig created by an alternate baenv.
|
||||
|
||||
# Build number and version of the ballistica binary we expect to be
|
||||
# using.
|
||||
TARGET_BALLISTICA_BUILD = 21465
|
||||
TARGET_BALLISTICA_BUILD = 21491
|
||||
TARGET_BALLISTICA_VERSION = '1.7.28'
|
||||
|
||||
|
||||
|
||||
@ -31,7 +31,10 @@ from babase import (
|
||||
apptimer,
|
||||
AppTimer,
|
||||
Call,
|
||||
can_toggle_fullscreen,
|
||||
fullscreen_control_available,
|
||||
fullscreen_control_get,
|
||||
fullscreen_control_key_shortcut,
|
||||
fullscreen_control_set,
|
||||
charstr,
|
||||
clipboard_is_supported,
|
||||
clipboard_set_text,
|
||||
@ -139,7 +142,10 @@ __all__ = [
|
||||
'buttonwidget',
|
||||
'Call',
|
||||
'can_show_ad',
|
||||
'can_toggle_fullscreen',
|
||||
'fullscreen_control_available',
|
||||
'fullscreen_control_get',
|
||||
'fullscreen_control_key_shortcut',
|
||||
'fullscreen_control_set',
|
||||
'charstr',
|
||||
'checkboxwidget',
|
||||
'clipboard_is_supported',
|
||||
|
||||
@ -704,7 +704,7 @@ class AccountSettingsWindow(bui.Window):
|
||||
position=((self._sub_width - button_width) * 0.5, v + 30),
|
||||
autoselect=True,
|
||||
size=(button_width, 60),
|
||||
label=bui.Lstr(resource=self._r + '.manageAccountText'),
|
||||
label=bui.Lstr(resource=f'{self._r}.manageAccountText'),
|
||||
color=(0.55, 0.5, 0.6),
|
||||
icon=bui.gettexture('settingsIcon'),
|
||||
textcolor=(0.75, 0.7, 0.8),
|
||||
|
||||
@ -98,9 +98,11 @@ class ControlsSettingsWindow(bui.Window):
|
||||
# made-for-iOS/Mac systems
|
||||
# (we can run into problems where devices register as one of each
|
||||
# type otherwise)..
|
||||
# UPDATE: We always use the apple system these days (which should
|
||||
# support older controllers). So no need for a switch.
|
||||
show_mac_controller_subsystem = False
|
||||
if platform == 'mac' and bui.is_xcode_build():
|
||||
show_mac_controller_subsystem = True
|
||||
# if platform == 'mac' and bui.is_xcode_build():
|
||||
# show_mac_controller_subsystem = True
|
||||
|
||||
if show_mac_controller_subsystem:
|
||||
height += spacing * 1.5
|
||||
@ -311,6 +313,7 @@ class ControlsSettingsWindow(bui.Window):
|
||||
maxwidth=width * 0.8,
|
||||
)
|
||||
v -= spacing
|
||||
|
||||
if show_mac_controller_subsystem:
|
||||
PopupMenu(
|
||||
parent=self._root_widget,
|
||||
|
||||
@ -29,16 +29,16 @@ def gamepad_configure_callback(event: dict[str, Any]) -> None:
|
||||
logging.exception('Error transitioning out main_menu_window.')
|
||||
bui.getsound('activateBeep').play()
|
||||
bui.getsound('swish').play()
|
||||
inputdevice = event['input_device']
|
||||
assert isinstance(inputdevice, bs.InputDevice)
|
||||
if inputdevice.allows_configuring:
|
||||
device = event['input_device']
|
||||
assert isinstance(device, bs.InputDevice)
|
||||
if device.allows_configuring:
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
gamepad.GamepadSettingsWindow(inputdevice).get_root_widget()
|
||||
gamepad.GamepadSettingsWindow(device).get_root_widget()
|
||||
)
|
||||
else:
|
||||
width = 700
|
||||
height = 200
|
||||
button_width = 100
|
||||
button_width = 80
|
||||
uiscale = bui.app.ui_v1.uiscale
|
||||
dlg = bui.containerwidget(
|
||||
scale=(
|
||||
@ -52,8 +52,13 @@ def gamepad_configure_callback(event: dict[str, Any]) -> None:
|
||||
transition='in_right',
|
||||
)
|
||||
bui.app.ui_v1.set_main_menu_window(dlg)
|
||||
device_name = inputdevice.name
|
||||
if device_name == 'iDevice':
|
||||
|
||||
if device.allows_configuring_in_system_settings:
|
||||
msg = bui.Lstr(
|
||||
resource='configureDeviceInSystemSettingsText',
|
||||
subs=[('${DEVICE}', device.name)],
|
||||
)
|
||||
elif device.is_controller_app:
|
||||
msg = bui.Lstr(
|
||||
resource='bsRemoteConfigureInAppText',
|
||||
subs=[('${REMOTE_APP_NAME}', bui.get_remote_app_name())],
|
||||
@ -61,7 +66,7 @@ def gamepad_configure_callback(event: dict[str, Any]) -> None:
|
||||
else:
|
||||
msg = bui.Lstr(
|
||||
resource='cantConfigureDeviceText',
|
||||
subs=[('${DEVICE}', device_name)],
|
||||
subs=[('${DEVICE}', device.name)],
|
||||
)
|
||||
bui.textwidget(
|
||||
parent=dlg,
|
||||
|
||||
@ -52,7 +52,7 @@ class GraphicsSettingsWindow(bui.Window):
|
||||
self._show_fullscreen = False
|
||||
fullscreen_spacing_top = spacing * 0.2
|
||||
fullscreen_spacing = spacing * 1.2
|
||||
if bui.can_toggle_fullscreen():
|
||||
if bui.fullscreen_control_available():
|
||||
self._show_fullscreen = True
|
||||
height += fullscreen_spacing + fullscreen_spacing_top
|
||||
|
||||
@ -122,21 +122,29 @@ class GraphicsSettingsWindow(bui.Window):
|
||||
self._fullscreen_checkbox: bui.Widget | None = None
|
||||
if self._show_fullscreen:
|
||||
v -= fullscreen_spacing_top
|
||||
self._fullscreen_checkbox = ConfigCheckBox(
|
||||
# Fullscreen control does not necessarily talk to the
|
||||
# app config so we have to wrangle it manually instead of
|
||||
# using a config-checkbox.
|
||||
label = bui.Lstr(resource=f'{self._r}.fullScreenText')
|
||||
|
||||
# Show keyboard shortcut alongside the control if they
|
||||
# provide one.
|
||||
shortcut = bui.fullscreen_control_key_shortcut()
|
||||
if shortcut is not None:
|
||||
label = bui.Lstr(
|
||||
value='$(NAME) [$(SHORTCUT)]',
|
||||
subs=[('$(NAME)', label), ('$(SHORTCUT)', shortcut)],
|
||||
)
|
||||
self._fullscreen_checkbox = bui.checkboxwidget(
|
||||
parent=self._root_widget,
|
||||
position=(100, v),
|
||||
maxwidth=200,
|
||||
value=bui.fullscreen_control_get(),
|
||||
on_value_change_call=bui.fullscreen_control_set,
|
||||
maxwidth=250,
|
||||
size=(300, 30),
|
||||
configkey='Fullscreen',
|
||||
displayname=bui.Lstr(
|
||||
resource=self._r
|
||||
+ (
|
||||
'.fullScreenCmdText'
|
||||
if app.classic.platform == 'mac'
|
||||
else '.fullScreenCtrlText'
|
||||
)
|
||||
),
|
||||
).widget
|
||||
text=label,
|
||||
)
|
||||
|
||||
if not self._have_selected_child:
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget,
|
||||
@ -528,8 +536,10 @@ class GraphicsSettingsWindow(bui.Window):
|
||||
and bui.apptime() - self._last_max_fps_set_time > 1.0
|
||||
):
|
||||
self._apply_max_fps()
|
||||
|
||||
if self._show_fullscreen:
|
||||
# Keep the fullscreen checkbox up to date with the current value.
|
||||
bui.checkboxwidget(
|
||||
edit=self._fullscreen_checkbox,
|
||||
value=bui.app.config.resolve('Fullscreen'),
|
||||
value=bui.fullscreen_control_get(),
|
||||
)
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
#include "ballistica/base/app_adapter/app_adapter.h"
|
||||
|
||||
#if BA_OSTYPE_ANDROID
|
||||
#if BA_OSTYPE_ANDROID // Remove conditional once android sources are public.
|
||||
#include "ballistica/base/app_adapter/app_adapter_android.h"
|
||||
#endif
|
||||
#include "ballistica/base/app_adapter/app_adapter_apple.h"
|
||||
@ -15,6 +15,8 @@
|
||||
#include "ballistica/base/networking/network_reader.h"
|
||||
#include "ballistica/base/networking/networking.h"
|
||||
#include "ballistica/base/platform/base_platform.h"
|
||||
#include "ballistica/base/python/base_python.h"
|
||||
#include "ballistica/base/support/app_config.h"
|
||||
#include "ballistica/base/support/stress_test.h"
|
||||
#include "ballistica/base/ui/ui.h"
|
||||
#include "ballistica/shared/foundation/event_loop.h"
|
||||
@ -239,7 +241,7 @@ void AppAdapter::DoExitMainThreadEventLoop() {
|
||||
FatalError("DoExitMainThreadEventLoop is not implemented here.");
|
||||
}
|
||||
|
||||
auto AppAdapter::CanToggleFullscreen() -> bool const { return false; }
|
||||
auto AppAdapter::FullscreenControlAvailable() const -> bool { return false; }
|
||||
|
||||
auto AppAdapter::SupportsVSync() -> bool const { return false; }
|
||||
|
||||
@ -253,6 +255,29 @@ void AppAdapter::DoPushGraphicsContextRunnable(Runnable* runnable) {
|
||||
DoPushMainThreadRunnable(runnable);
|
||||
}
|
||||
|
||||
auto AppAdapter::FullscreenControlGet() const -> bool {
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
// By default, just go through config (assume we have full control over
|
||||
// the fullscreen state ourself).
|
||||
return g_base->app_config->Resolve(AppConfig::BoolID::kFullscreen);
|
||||
}
|
||||
|
||||
void AppAdapter::FullscreenControlSet(bool fullscreen) {
|
||||
assert(g_base->InLogicThread());
|
||||
// By default, just set these in the config and apply it (assumes config
|
||||
// changes get plugged into actual fullscreen state).
|
||||
g_base->python->objs()
|
||||
.Get(fullscreen ? BasePython::ObjID::kSetConfigFullscreenOnCall
|
||||
: BasePython::ObjID::kSetConfigFullscreenOffCall)
|
||||
.Call();
|
||||
}
|
||||
|
||||
auto AppAdapter::FullscreenControlKeyShortcut() const
|
||||
-> std::optional<std::string> {
|
||||
return {};
|
||||
}
|
||||
|
||||
void AppAdapter::CursorPositionForDraw(float* x, float* y) {
|
||||
assert(x && y);
|
||||
|
||||
@ -271,14 +296,23 @@ auto AppAdapter::ShouldUseCursor() -> bool { return true; }
|
||||
|
||||
auto AppAdapter::HasHardwareCursor() -> bool { return false; }
|
||||
|
||||
void AppAdapter::SetHardwareCursorVisible(bool visible) {
|
||||
printf("SHOULD SET VIS %d\n", static_cast<int>(visible));
|
||||
}
|
||||
void AppAdapter::SetHardwareCursorVisible(bool visible) {}
|
||||
|
||||
auto AppAdapter::CanSoftQuit() -> bool { return false; }
|
||||
auto AppAdapter::CanBackQuit() -> bool { return false; }
|
||||
void AppAdapter::DoBackQuit() { FatalError("Fixme unimplemented."); }
|
||||
void AppAdapter::DoSoftQuit() { FatalError("Fixme unimplemented."); }
|
||||
void AppAdapter::TerminateApp() { FatalError("Fixme unimplemented."); }
|
||||
auto AppAdapter::HasDirectKeyboardInput() -> bool { return false; }
|
||||
|
||||
void AppAdapter::ApplyGraphicsSettings(const GraphicsSettings* settings) {}
|
||||
|
||||
auto AppAdapter::GetGraphicsSettings() -> GraphicsSettings* {
|
||||
return new GraphicsSettings();
|
||||
}
|
||||
|
||||
auto AppAdapter::GetGraphicsClientContext() -> GraphicsClientContext* {
|
||||
return new GraphicsClientContext();
|
||||
}
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -30,6 +30,16 @@ class AppAdapter {
|
||||
virtual void OnScreenSizeChange();
|
||||
virtual void DoApplyAppConfig();
|
||||
|
||||
/// When called, should allocate an instance of a GraphicsSettings
|
||||
/// subclass using 'new', fill it out, and return it. Runs in the logic
|
||||
/// thread.
|
||||
virtual auto GetGraphicsSettings() -> GraphicsSettings*;
|
||||
|
||||
/// When called, should allocate an instance of a GraphicsClientContext
|
||||
/// subclass using 'new', fill it out, and return it. Runs in the graphics
|
||||
/// context.
|
||||
virtual auto GetGraphicsClientContext() -> GraphicsClientContext*;
|
||||
|
||||
/// Return whether this class manages the main thread event loop itself.
|
||||
/// Default is true. If this is true, RunMainThreadEventLoopToCompletion()
|
||||
/// will be called to run the app. This should return false on builds
|
||||
@ -113,9 +123,27 @@ class AppAdapter {
|
||||
auto app_suspended() const { return app_suspended_; }
|
||||
|
||||
/// Return whether this AppAdapter supports a 'fullscreen' toggle for its
|
||||
/// display. This currently will simply affect whether that option is
|
||||
/// available in display settings or via a hotkey.
|
||||
virtual auto CanToggleFullscreen() -> bool const;
|
||||
/// display. This will affect whether that option is available in display
|
||||
/// settings or via a hotkey. Must be called from the logic thread.
|
||||
virtual auto FullscreenControlAvailable() const -> bool;
|
||||
|
||||
/// AppAdapters supporting a 'fullscreen' control should return the
|
||||
/// current fullscreen state here. By default this simply returns the
|
||||
/// app-config fullscreen value (so assumes the actual state is synced to
|
||||
/// that). Must be called from the logic thread.
|
||||
virtual auto FullscreenControlGet() const -> bool;
|
||||
|
||||
/// AppAdapters supporting a 'fullscreen' control should set the
|
||||
/// current fullscreen state here. By default this simply sets the
|
||||
/// app-config fullscreen value (so assumes the actual state is synced to
|
||||
/// that). Must be called from the logic thread.
|
||||
virtual void FullscreenControlSet(bool fullscreen);
|
||||
|
||||
/// AppAdapters supporting a 'fullscreen' control can return a key name
|
||||
/// here to display if they support toggling via key ('ctrl-F', etc.).
|
||||
/// Must be called from the logic thread.
|
||||
virtual auto FullscreenControlKeyShortcut() const
|
||||
-> std::optional<std::string>;
|
||||
|
||||
/// Return whether this AppAdapter supports vsync controls for its display.
|
||||
virtual auto SupportsVSync() -> bool const;
|
||||
@ -153,6 +181,22 @@ class AppAdapter {
|
||||
/// this point.
|
||||
virtual void TerminateApp();
|
||||
|
||||
/// Should return whether there is a keyboard attached that will deliver
|
||||
/// direct text-editing related events to the app. When this is false,
|
||||
/// alternate entry methods such as keyboard-entry-dialogs and on-screen
|
||||
/// keyboards will be used. This value can change based on conditions such
|
||||
/// as a hardware keyboard getting attached or detached or the language
|
||||
/// changing (it may be preferable to rely on dialogs for non-english
|
||||
/// languages/etc.). Default implementation returns false. This function
|
||||
/// should be callable from any thread.
|
||||
virtual auto HasDirectKeyboardInput() -> bool;
|
||||
|
||||
/// Called in the graphics context to apply new settings coming in from
|
||||
/// the logic subsystem. This will be called initially to jump-start the
|
||||
/// graphics system as well as before frame draws to update any new
|
||||
/// settings coming in.
|
||||
virtual void ApplyGraphicsSettings(const GraphicsSettings* settings);
|
||||
|
||||
protected:
|
||||
AppAdapter();
|
||||
virtual ~AppAdapter();
|
||||
|
||||
@ -44,58 +44,27 @@ void AppAdapterApple::DoPushMainThreadRunnable(Runnable* runnable) {
|
||||
BallisticaKit::FromCppPushRawRunnableToMain(runnable);
|
||||
}
|
||||
|
||||
void AppAdapterApple::DoApplyAppConfig() {
|
||||
assert(g_base->InLogicThread());
|
||||
void AppAdapterApple::DoApplyAppConfig() { assert(g_base->InLogicThread()); }
|
||||
|
||||
g_base->graphics_server->PushSetScreenPixelScaleCall(
|
||||
g_base->app_config->Resolve(AppConfig::FloatID::kScreenPixelScale));
|
||||
void AppAdapterApple::ApplyGraphicsSettings(const GraphicsSettings* settings) {
|
||||
auto* graphics_server = g_base->graphics_server;
|
||||
|
||||
auto graphics_quality_requested =
|
||||
g_base->graphics->GraphicsQualityFromAppConfig();
|
||||
|
||||
auto texture_quality_requested =
|
||||
g_base->graphics->TextureQualityFromAppConfig();
|
||||
|
||||
g_base->app_adapter->PushGraphicsContextCall([=] {
|
||||
SetScreen_(texture_quality_requested, graphics_quality_requested);
|
||||
});
|
||||
}
|
||||
|
||||
void AppAdapterApple::SetScreen_(
|
||||
TextureQualityRequest texture_quality_requested,
|
||||
GraphicsQualityRequest graphics_quality_requested) {
|
||||
// If we know what we support, filter our request types to what is
|
||||
// supported. This will keep us from rebuilding contexts if request type
|
||||
// is flipping between different types that we don't support.
|
||||
if (g_base->graphics->has_supports_high_quality_graphics_value()) {
|
||||
if (!g_base->graphics->supports_high_quality_graphics()
|
||||
&& graphics_quality_requested > GraphicsQualityRequest::kMedium) {
|
||||
graphics_quality_requested = GraphicsQualityRequest::kMedium;
|
||||
}
|
||||
}
|
||||
|
||||
auto* gs = g_base->graphics_server;
|
||||
// We need a full renderer reload if quality values have changed
|
||||
// or if we don't have a renderer yet.
|
||||
bool need_full_reload = ((graphics_server->texture_quality_requested()
|
||||
!= settings->texture_quality)
|
||||
|| (graphics_server->graphics_quality_requested()
|
||||
!= settings->graphics_quality));
|
||||
|
||||
// We need a full renderer reload if quality values have changed or if we
|
||||
// don't have one yet.
|
||||
bool need_full_reload =
|
||||
((gs->texture_quality_requested() != texture_quality_requested)
|
||||
|| (gs->graphics_quality_requested() != graphics_quality_requested)
|
||||
|| !gs->texture_quality_set() || !gs->graphics_quality_set());
|
||||
// don't yet have a renderer.
|
||||
|
||||
if (need_full_reload) {
|
||||
ReloadRenderer_(graphics_quality_requested, texture_quality_requested);
|
||||
ReloadRenderer_(settings);
|
||||
}
|
||||
|
||||
// Let the logic thread know we've got a graphics system up and running.
|
||||
// It may use this cue to kick off asset loads and other bootstrapping.
|
||||
g_base->logic->event_loop()->PushCall(
|
||||
[] { g_base->logic->OnGraphicsReady(); });
|
||||
}
|
||||
|
||||
void AppAdapterApple::ReloadRenderer_(
|
||||
GraphicsQualityRequest graphics_quality_requested,
|
||||
TextureQualityRequest texture_quality_requested) {
|
||||
void AppAdapterApple::ReloadRenderer_(const GraphicsSettings* settings) {
|
||||
auto* gs = g_base->graphics_server;
|
||||
|
||||
if (gs->renderer() && gs->renderer_loaded()) {
|
||||
@ -105,15 +74,9 @@ void AppAdapterApple::ReloadRenderer_(
|
||||
gs->set_renderer(new RendererGL());
|
||||
}
|
||||
|
||||
// Set a dummy screen resolution to start with. The main thread will kick
|
||||
// along the latest real resolution just before each frame draw, but we
|
||||
// need *something* here or else we'll get errors due to framebuffers
|
||||
// getting made at size 0/etc.
|
||||
g_base->graphics_server->SetScreenResolution(320.0, 240.0);
|
||||
|
||||
// Update graphics quality based on request.
|
||||
gs->set_graphics_quality_requested(graphics_quality_requested);
|
||||
gs->set_texture_quality_requested(texture_quality_requested);
|
||||
gs->set_graphics_quality_requested(settings->graphics_quality);
|
||||
gs->set_texture_quality_requested(settings->texture_quality);
|
||||
|
||||
// (Re)load stuff with these latest quality settings.
|
||||
gs->LoadRenderer();
|
||||
@ -123,12 +86,6 @@ void AppAdapterApple::UpdateScreenSizes_() {
|
||||
assert(g_base->app_adapter->InGraphicsContext());
|
||||
}
|
||||
|
||||
void AppAdapterApple::SetScreenResolution(float pixel_width,
|
||||
float pixel_height) {
|
||||
auto allow = ScopedAllowGraphics_(this);
|
||||
g_base->graphics_server->SetScreenResolution(pixel_width, pixel_height);
|
||||
}
|
||||
|
||||
auto AppAdapterApple::TryRender() -> bool {
|
||||
auto allow = ScopedAllowGraphics_(this);
|
||||
|
||||
@ -146,10 +103,45 @@ auto AppAdapterApple::TryRender() -> bool {
|
||||
call->RunAndLogErrors();
|
||||
delete call;
|
||||
}
|
||||
// Lastly render.
|
||||
return g_base->graphics_server->TryRender();
|
||||
|
||||
return true;
|
||||
// Lastly, render.
|
||||
auto result = g_base->graphics_server->TryRender();
|
||||
|
||||
// A little trick to make mac resizing look a lot smoother. Because we
|
||||
// render in a background thread, we often don't render at the most up to
|
||||
// date window size during a window resize. Normally this makes our image
|
||||
// jerk around in an ugly way, but if we just re-render once or twice in
|
||||
// those cases we mostly always get the most up to date window size.
|
||||
if (result && resize_friendly_frames_ > 0) {
|
||||
// Leave this enabled for just a few frames every time it is set.
|
||||
// (so just in case it breaks we won't draw each frame serveral times for
|
||||
// eternity).
|
||||
resize_friendly_frames_ -= 1;
|
||||
|
||||
// Keep on drawing until the drawn window size
|
||||
// matches what we have (or until we try for too long or fail at drawing).
|
||||
seconds_t start_time = g_core->GetAppTimeSeconds();
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
if (((std::abs(resize_target_resolution_.x
|
||||
- g_base->graphics_server->screen_pixel_width())
|
||||
> 0.01f)
|
||||
|| (std::abs(resize_target_resolution_.y
|
||||
- g_base->graphics_server->screen_pixel_height())
|
||||
> 0.01f))
|
||||
&& g_core->GetAppTimeSeconds() - start_time < 0.1 && result) {
|
||||
result = g_base->graphics_server->TryRender();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void AppAdapterApple::EnableResizeFriendlyMode(int width, int height) {
|
||||
resize_friendly_frames_ = 5;
|
||||
resize_target_resolution_ = Vector2f(width, height);
|
||||
}
|
||||
|
||||
auto AppAdapterApple::InGraphicsContext() -> bool {
|
||||
@ -202,6 +194,36 @@ void AppAdapterApple::TerminateApp() {
|
||||
#endif
|
||||
}
|
||||
|
||||
auto AppAdapterApple::FullscreenControlAvailable() const -> bool {
|
||||
// Currently Mac only. Any window-management stuff elsewhere such as
|
||||
// iPadOS is out of our hands.
|
||||
if (g_buildconfig.ostype_macos()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto AppAdapterApple::FullscreenControlGet() const -> bool {
|
||||
#if BA_OSTYPE_MACOS
|
||||
return BallisticaKit::CocoaFromCppGetMainWindowIsFullscreen();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void AppAdapterApple::FullscreenControlSet(bool fullscreen) {
|
||||
#if BA_OSTYPE_MACOS
|
||||
return BallisticaKit::CocoaFromCppSetMainWindowFullscreen(fullscreen);
|
||||
#endif
|
||||
}
|
||||
|
||||
auto AppAdapterApple::FullscreenControlKeyShortcut() const
|
||||
-> std::optional<std::string> {
|
||||
return "fn+F";
|
||||
}
|
||||
|
||||
auto AppAdapterApple::HasDirectKeyboardInput() -> bool { return true; };
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
#endif // BA_XCODE_BUILD
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
|
||||
#include "ballistica/base/app_adapter/app_adapter.h"
|
||||
#include "ballistica/shared/generic/runnable.h"
|
||||
#include "ballistica/shared/math/vector2f.h"
|
||||
|
||||
namespace ballistica::base {
|
||||
|
||||
@ -31,8 +32,14 @@ class AppAdapterApple : public AppAdapter {
|
||||
/// Called by FromSwift.
|
||||
auto TryRender() -> bool;
|
||||
|
||||
/// Called by FromSwift.
|
||||
void SetScreenResolution(float pixel_width, float pixel_height);
|
||||
auto FullscreenControlAvailable() const -> bool override;
|
||||
auto FullscreenControlGet() const -> bool override;
|
||||
void FullscreenControlSet(bool fullscreen) override;
|
||||
auto FullscreenControlKeyShortcut() const
|
||||
-> std::optional<std::string> override;
|
||||
|
||||
auto HasDirectKeyboardInput() -> bool override;
|
||||
void EnableResizeFriendlyMode(int width, int height);
|
||||
|
||||
protected:
|
||||
void DoPushMainThreadRunnable(Runnable* runnable) override;
|
||||
@ -42,16 +49,18 @@ class AppAdapterApple : public AppAdapter {
|
||||
auto HasHardwareCursor() -> bool override;
|
||||
void SetHardwareCursorVisible(bool visible) override;
|
||||
void TerminateApp() override;
|
||||
void ApplyGraphicsSettings(const GraphicsSettings* settings) override;
|
||||
|
||||
private:
|
||||
void UpdateScreenSizes_();
|
||||
class ScopedAllowGraphics_;
|
||||
void SetScreen_(TextureQualityRequest texture_quality_requested,
|
||||
GraphicsQualityRequest graphics_quality_requested);
|
||||
void ReloadRenderer_(GraphicsQualityRequest graphics_quality_requested,
|
||||
TextureQualityRequest texture_quality_requested);
|
||||
|
||||
void UpdateScreenSizes_();
|
||||
void ReloadRenderer_(const GraphicsSettings* settings);
|
||||
|
||||
std::thread::id graphics_thread_{};
|
||||
bool graphics_allowed_;
|
||||
bool graphics_allowed_ : 1 {};
|
||||
uint8_t resize_friendly_frames_{};
|
||||
Vector2f resize_target_resolution_{-1.0f, -1.0f};
|
||||
std::mutex graphics_calls_mutex_;
|
||||
std::vector<Runnable*> graphics_calls_;
|
||||
};
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include "ballistica/base/app_adapter/app_adapter_headless.h"
|
||||
|
||||
#include "ballistica/base/graphics/graphics_server.h"
|
||||
#include "ballistica/base/graphics/support/graphics_client_context.h"
|
||||
#include "ballistica/shared/ballistica.h"
|
||||
|
||||
namespace ballistica::base {
|
||||
@ -19,12 +20,7 @@ void AppAdapterHeadless::OnMainThreadStartApp() {
|
||||
new EventLoop(EventLoopID::kMain, ThreadSource::kWrapCurrent);
|
||||
}
|
||||
|
||||
void AppAdapterHeadless::DoApplyAppConfig() {
|
||||
// Normal graphical app-adapters kick off screen creation here
|
||||
// which then leads to remaining app bootstrapping. We create
|
||||
// a 'null' screen purely for the same effect.
|
||||
PushMainThreadCall([] { g_base->graphics_server->SetNullGraphics(); });
|
||||
}
|
||||
void AppAdapterHeadless::DoApplyAppConfig() {}
|
||||
|
||||
void AppAdapterHeadless::RunMainThreadEventLoopToCompletion() {
|
||||
assert(g_core->InMainThread());
|
||||
@ -40,6 +36,11 @@ void AppAdapterHeadless::DoExitMainThreadEventLoop() {
|
||||
main_event_loop_->Exit();
|
||||
}
|
||||
|
||||
auto AppAdapterHeadless::GetGraphicsClientContext() -> GraphicsClientContext* {
|
||||
// Special dummy form.
|
||||
return new GraphicsClientContext(0);
|
||||
}
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
#endif // BA_HEADLESS_BUILD
|
||||
|
||||
@ -17,6 +17,8 @@ class AppAdapterHeadless : public AppAdapter {
|
||||
|
||||
void DoApplyAppConfig() override;
|
||||
|
||||
auto GetGraphicsClientContext() -> GraphicsClientContext* override;
|
||||
|
||||
protected:
|
||||
void DoPushMainThreadRunnable(Runnable* runnable) override;
|
||||
void RunMainThreadEventLoopToCompletion() override;
|
||||
|
||||
@ -104,32 +104,115 @@ void AppAdapterSDL::OnMainThreadStartApp() {
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
}
|
||||
|
||||
/// Our particular flavor of graphics settings.
|
||||
struct AppAdapterSDL::GraphicsSettings_ : public GraphicsSettings {
|
||||
bool fullscreen = g_base->app_config->Resolve(AppConfig::BoolID::kFullscreen);
|
||||
VSyncRequest vsync = g_base->graphics->VSyncFromAppConfig();
|
||||
int max_fps = g_base->app_config->Resolve(AppConfig::IntID::kMaxFPS);
|
||||
};
|
||||
|
||||
void AppAdapterSDL::DoApplyAppConfig() {
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
g_base->graphics_server->PushSetScreenPixelScaleCall(
|
||||
g_base->app_config->Resolve(AppConfig::FloatID::kScreenPixelScale));
|
||||
|
||||
auto graphics_quality_requested =
|
||||
g_base->graphics->GraphicsQualityFromAppConfig();
|
||||
|
||||
auto texture_quality_requested =
|
||||
g_base->graphics->TextureQualityFromAppConfig();
|
||||
|
||||
// Android res string.
|
||||
// std::string android_res =
|
||||
// g_base->app_config->Resolve(AppConfig::StringID::kResolutionAndroid);
|
||||
}
|
||||
|
||||
bool fullscreen = g_base->app_config->Resolve(AppConfig::BoolID::kFullscreen);
|
||||
auto AppAdapterSDL::GetGraphicsSettings() -> GraphicsSettings* {
|
||||
assert(g_base->InLogicThread());
|
||||
return new GraphicsSettings_();
|
||||
}
|
||||
|
||||
auto vsync = g_base->graphics->VSyncFromAppConfig();
|
||||
int max_fps = g_base->app_config->Resolve(AppConfig::IntID::kMaxFPS);
|
||||
void AppAdapterSDL::ApplyGraphicsSettings(
|
||||
const GraphicsSettings* settings_base) {
|
||||
assert(g_core->InMainThread());
|
||||
assert(!g_core->HeadlessMode());
|
||||
|
||||
// Tell the main thread to set up the screen with these settings.
|
||||
g_base->app_adapter->PushMainThreadCall([=] {
|
||||
SetScreen_(fullscreen, max_fps, vsync, texture_quality_requested,
|
||||
graphics_quality_requested);
|
||||
});
|
||||
// In strict mode, allow graphics stuff while in here.
|
||||
auto allow = ScopedAllowGraphics_(this);
|
||||
|
||||
// Settings will always be our subclass (since we created it).
|
||||
auto* settings = static_cast<const GraphicsSettings_*>(settings_base);
|
||||
|
||||
// Apply any changes.
|
||||
bool do_toggle_fs{};
|
||||
bool do_set_existing_fullscreen{};
|
||||
|
||||
auto* graphics_server = g_base->graphics_server;
|
||||
|
||||
// We need a full renderer reload if quality values have changed
|
||||
// or if we don't have a renderer yet.
|
||||
bool need_full_reload = ((sdl_window_ == nullptr
|
||||
|| graphics_server->texture_quality_requested()
|
||||
!= settings->texture_quality)
|
||||
|| (graphics_server->graphics_quality_requested()
|
||||
!= settings->graphics_quality));
|
||||
|
||||
if (need_full_reload) {
|
||||
ReloadRenderer_(settings);
|
||||
} else if (settings->fullscreen != fullscreen_) {
|
||||
SDL_SetWindowFullscreen(
|
||||
sdl_window_, settings->fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
||||
fullscreen_ = settings->fullscreen;
|
||||
}
|
||||
|
||||
// VSync always gets set independent of the screen (though we set it down
|
||||
// here to make sure we have a screen when its set).
|
||||
VSync vsync;
|
||||
switch (settings->vsync) {
|
||||
case VSyncRequest::kNever:
|
||||
vsync = VSync::kNever;
|
||||
break;
|
||||
case VSyncRequest::kAlways:
|
||||
vsync = VSync::kAlways;
|
||||
break;
|
||||
case VSyncRequest::kAuto:
|
||||
vsync = VSync::kAdaptive;
|
||||
break;
|
||||
default:
|
||||
vsync = VSync::kNever;
|
||||
break;
|
||||
}
|
||||
if (vsync != vsync_) {
|
||||
switch (vsync) {
|
||||
case VSync::kUnset:
|
||||
case VSync::kNever: {
|
||||
SDL_GL_SetSwapInterval(0);
|
||||
vsync_actually_enabled_ = false;
|
||||
break;
|
||||
}
|
||||
case VSync::kAlways: {
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
vsync_actually_enabled_ = true;
|
||||
break;
|
||||
}
|
||||
case VSync::kAdaptive: {
|
||||
// In this case, let's try setting to 'adaptive' and turn it off if
|
||||
// that is unsupported.
|
||||
auto result = SDL_GL_SetSwapInterval(-1);
|
||||
if (result == 0) {
|
||||
vsync_actually_enabled_ = true;
|
||||
} else {
|
||||
SDL_GL_SetSwapInterval(0);
|
||||
vsync_actually_enabled_ = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
vsync_ = vsync;
|
||||
}
|
||||
|
||||
// This we can set anytime. Probably could have just set it from the logic
|
||||
// thread where we read it, but let's be pedantic and keep everything to
|
||||
// the main thread.
|
||||
max_fps_ = settings->max_fps;
|
||||
|
||||
// Take -1 to mean no max. Otherwise clamp to a reasonable range.
|
||||
if (max_fps_ != -1) {
|
||||
max_fps_ = std::max(10, max_fps_);
|
||||
max_fps_ = std::min(99999, max_fps_);
|
||||
}
|
||||
}
|
||||
|
||||
void AppAdapterSDL::RunMainThreadEventLoopToCompletion() {
|
||||
@ -158,7 +241,7 @@ void AppAdapterSDL::RunMainThreadEventLoopToCompletion() {
|
||||
|
||||
auto AppAdapterSDL::TryRender() -> bool {
|
||||
if (strict_graphics_context_) {
|
||||
// In strict mode, allow graphics stuff in here. Normally we allow it
|
||||
// In strict mode, allow graphics stuff in here. Otherwise we allow it
|
||||
// anywhere in the main thread.
|
||||
auto allow = ScopedAllowGraphics_(this);
|
||||
|
||||
@ -179,7 +262,7 @@ auto AppAdapterSDL::TryRender() -> bool {
|
||||
// Lastly render.
|
||||
return g_base->graphics_server->TryRender();
|
||||
} else {
|
||||
// Simple path; just render.
|
||||
// Simpler path; just render.
|
||||
return g_base->graphics_server->TryRender();
|
||||
}
|
||||
}
|
||||
@ -188,7 +271,7 @@ void AppAdapterSDL::SleepUntilNextEventCycle_(microsecs_t cycle_start_time) {
|
||||
// Special case: if we're hidden, we simply sleep for a long bit; no fancy
|
||||
// timing.
|
||||
if (hidden_) {
|
||||
g_core->platform->SleepMillisecs(100);
|
||||
g_core->platform->SleepSeconds(0.1);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -359,12 +442,14 @@ void AppAdapterSDL::HandleSDLEvent_(const SDL_Event& event) {
|
||||
break;
|
||||
|
||||
case SDL_QUIT:
|
||||
if (g_core->GetAppTimeMillisecs() - last_windowevent_close_time_ < 100) {
|
||||
if (g_core->GetAppTimeSeconds() - last_windowevent_close_time_ < 0.1) {
|
||||
// If they hit the window close button, skip the confirm.
|
||||
g_base->QuitApp(false);
|
||||
} else {
|
||||
// By default, confirm before quitting.
|
||||
g_base->QuitApp(true);
|
||||
// For all other quits we might want to default to a confirm dialog.
|
||||
// Update: going to try without confirm for a bit and see how that
|
||||
// feels.
|
||||
g_base->QuitApp(false);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -378,7 +463,7 @@ void AppAdapterSDL::HandleSDLEvent_(const SDL_Event& event) {
|
||||
case SDL_WINDOWEVENT_CLOSE: {
|
||||
// Simply note that this happened. We use this to adjust our
|
||||
// SDL_QUIT behavior (quit is called right after this).
|
||||
last_windowevent_close_time_ = g_core->GetAppTimeMillisecs();
|
||||
last_windowevent_close_time_ = g_core->GetAppTimeSeconds();
|
||||
break;
|
||||
}
|
||||
|
||||
@ -394,7 +479,7 @@ void AppAdapterSDL::HandleSDLEvent_(const SDL_Event& event) {
|
||||
fullscreen_ = true;
|
||||
g_base->logic->event_loop()->PushCall([] {
|
||||
g_base->python->objs()
|
||||
.Get(BasePython::ObjID::kSetConfigFullscreenOnCall)
|
||||
.Get(BasePython::ObjID::kStoreConfigFullscreenOnCall)
|
||||
.Call();
|
||||
});
|
||||
}
|
||||
@ -407,7 +492,7 @@ void AppAdapterSDL::HandleSDLEvent_(const SDL_Event& event) {
|
||||
fullscreen_ = false;
|
||||
g_base->logic->event_loop()->PushCall([] {
|
||||
g_base->python->objs()
|
||||
.Get(BasePython::ObjID::kSetConfigFullscreenOffCall)
|
||||
.Get(BasePython::ObjID::kStoreConfigFullscreenOffCall)
|
||||
.Call();
|
||||
});
|
||||
}
|
||||
@ -544,114 +629,7 @@ auto AppAdapterSDL::GetSDLJoystickInput_(int sdl_joystick_id) const
|
||||
return nullptr; // Epic fail.
|
||||
}
|
||||
|
||||
void AppAdapterSDL::SetScreen_(
|
||||
bool fullscreen, int max_fps, VSyncRequest vsync_requested,
|
||||
TextureQualityRequest texture_quality_requested,
|
||||
GraphicsQualityRequest graphics_quality_requested) {
|
||||
assert(g_core->InMainThread());
|
||||
assert(!g_core->HeadlessMode());
|
||||
|
||||
// In strict mode, allow graphics stuff in here.
|
||||
auto allow = ScopedAllowGraphics_(this);
|
||||
|
||||
// If we know what we support, filter our request types to what is
|
||||
// supported. This will keep us from rebuilding contexts if request type
|
||||
// is flipping between different types that we don't support.
|
||||
if (g_base->graphics->has_supports_high_quality_graphics_value()) {
|
||||
if (!g_base->graphics->supports_high_quality_graphics()
|
||||
&& graphics_quality_requested > GraphicsQualityRequest::kMedium) {
|
||||
graphics_quality_requested = GraphicsQualityRequest::kMedium;
|
||||
}
|
||||
}
|
||||
|
||||
bool do_toggle_fs{};
|
||||
bool do_set_existing_fullscreen{};
|
||||
|
||||
auto* gs = g_base->graphics_server;
|
||||
|
||||
// We need a full renderer reload if quality values have changed
|
||||
// or if we don't have one yet.
|
||||
bool need_full_reload =
|
||||
((sdl_window_ == nullptr
|
||||
|| gs->texture_quality_requested() != texture_quality_requested)
|
||||
|| (gs->graphics_quality_requested() != graphics_quality_requested)
|
||||
|| !gs->texture_quality_set() || !gs->graphics_quality_set());
|
||||
|
||||
if (need_full_reload) {
|
||||
ReloadRenderer_(fullscreen, graphics_quality_requested,
|
||||
texture_quality_requested);
|
||||
} else if (fullscreen != fullscreen_) {
|
||||
SDL_SetWindowFullscreen(sdl_window_,
|
||||
fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
||||
fullscreen_ = fullscreen;
|
||||
}
|
||||
|
||||
// VSync always gets set independent of the screen (though we set it down
|
||||
// here to make sure we have a screen when its set).
|
||||
VSync vsync;
|
||||
switch (vsync_requested) {
|
||||
case VSyncRequest::kNever:
|
||||
vsync = VSync::kNever;
|
||||
break;
|
||||
case VSyncRequest::kAlways:
|
||||
vsync = VSync::kAlways;
|
||||
break;
|
||||
case VSyncRequest::kAuto:
|
||||
vsync = VSync::kAdaptive;
|
||||
break;
|
||||
default:
|
||||
vsync = VSync::kNever;
|
||||
break;
|
||||
}
|
||||
if (vsync != vsync_) {
|
||||
switch (vsync) {
|
||||
case VSync::kUnset:
|
||||
case VSync::kNever: {
|
||||
SDL_GL_SetSwapInterval(0);
|
||||
vsync_actually_enabled_ = false;
|
||||
break;
|
||||
}
|
||||
case VSync::kAlways: {
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
vsync_actually_enabled_ = true;
|
||||
break;
|
||||
}
|
||||
case VSync::kAdaptive: {
|
||||
// In this case, let's try setting to 'adaptive' and turn it off if
|
||||
// that is unsupported.
|
||||
auto result = SDL_GL_SetSwapInterval(-1);
|
||||
if (result == 0) {
|
||||
vsync_actually_enabled_ = true;
|
||||
} else {
|
||||
SDL_GL_SetSwapInterval(0);
|
||||
vsync_actually_enabled_ = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
vsync_ = vsync;
|
||||
}
|
||||
|
||||
// This we can set anytime. Probably could have just set it from the logic
|
||||
// thread where we read it, but let's be pedantic and keep everything to
|
||||
// the main thread.
|
||||
max_fps_ = max_fps;
|
||||
|
||||
// Take -1 to mean no max. Otherwise clamp to a reasonable range.
|
||||
if (max_fps_ != -1) {
|
||||
max_fps_ = std::max(10, max_fps_);
|
||||
max_fps_ = std::min(99999, max_fps_);
|
||||
}
|
||||
|
||||
// Let the logic thread know we've got a graphics system up and running.
|
||||
// It may use this cue to kick off asset loads and other bootstrapping.
|
||||
g_base->logic->event_loop()->PushCall(
|
||||
[] { g_base->logic->OnGraphicsReady(); });
|
||||
}
|
||||
|
||||
void AppAdapterSDL::ReloadRenderer_(
|
||||
bool fullscreen, GraphicsQualityRequest graphics_quality_requested,
|
||||
TextureQualityRequest texture_quality_requested) {
|
||||
void AppAdapterSDL::ReloadRenderer_(const GraphicsSettings_* settings) {
|
||||
assert(g_base->app_adapter->InGraphicsContext());
|
||||
|
||||
auto* gs = g_base->graphics_server;
|
||||
@ -662,7 +640,7 @@ void AppAdapterSDL::ReloadRenderer_(
|
||||
|
||||
// If we don't haven't yet, create our window and renderer.
|
||||
if (!sdl_window_) {
|
||||
fullscreen_ = fullscreen;
|
||||
fullscreen_ = settings->fullscreen;
|
||||
|
||||
// A reasonable default window size.
|
||||
auto width = static_cast<int>(kBaseVirtualResX * 0.8f);
|
||||
@ -670,7 +648,7 @@ void AppAdapterSDL::ReloadRenderer_(
|
||||
|
||||
uint32_t flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN
|
||||
| SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE;
|
||||
if (fullscreen) {
|
||||
if (settings->fullscreen) {
|
||||
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
}
|
||||
|
||||
@ -727,15 +705,16 @@ void AppAdapterSDL::ReloadRenderer_(
|
||||
}
|
||||
}
|
||||
|
||||
// Update graphics quality based on request.
|
||||
gs->set_graphics_quality_requested(graphics_quality_requested);
|
||||
gs->set_texture_quality_requested(texture_quality_requested);
|
||||
// Update graphics-server's qualities based on request.
|
||||
gs->set_graphics_quality_requested(settings->graphics_quality);
|
||||
gs->set_texture_quality_requested(settings->texture_quality);
|
||||
|
||||
gs->LoadRenderer();
|
||||
}
|
||||
|
||||
void AppAdapterSDL::UpdateScreenSizes_() {
|
||||
assert(g_base->app_adapter->InGraphicsContext());
|
||||
// This runs in the main thread in response to SDL events.
|
||||
assert(g_core->InMainThread());
|
||||
|
||||
// Grab logical window dimensions (points?). This is the coordinate space
|
||||
// SDL's events deal in.
|
||||
@ -747,8 +726,13 @@ void AppAdapterSDL::UpdateScreenSizes_() {
|
||||
// dimensions.
|
||||
int pixels_x, pixels_y;
|
||||
SDL_GL_GetDrawableSize(sdl_window_, &pixels_x, &pixels_y);
|
||||
g_base->graphics_server->SetScreenResolution(static_cast<float>(pixels_x),
|
||||
static_cast<float>(pixels_y));
|
||||
|
||||
// Push this over to the logic thread which owns the canonical value
|
||||
// for this.
|
||||
g_base->logic->event_loop()->PushCall([pixels_x, pixels_y] {
|
||||
g_base->graphics->SetScreenResolution(static_cast<float>(pixels_x),
|
||||
static_cast<float>(pixels_y));
|
||||
});
|
||||
}
|
||||
|
||||
auto AppAdapterSDL::InGraphicsContext() -> bool {
|
||||
@ -818,10 +802,36 @@ void AppAdapterSDL::CursorPositionForDraw(float* x, float* y) {
|
||||
*y = immediate_y;
|
||||
}
|
||||
|
||||
auto AppAdapterSDL::CanToggleFullscreen() -> bool const { return true; }
|
||||
auto AppAdapterSDL::FullscreenControlAvailable() const -> bool { return true; }
|
||||
auto AppAdapterSDL::FullscreenControlKeyShortcut() const
|
||||
-> std::optional<std::string> {
|
||||
if (g_buildconfig.ostype_windows()) {
|
||||
// On Windows we support F11 and Alt+Enter to toggle fullscreen. Let's
|
||||
// mention Alt+Enter which seems like it might be more commonly used
|
||||
return "Alt+Enter";
|
||||
}
|
||||
if (g_buildconfig.ostype_macos()) {
|
||||
// The Mac+SDL situation is a bit of a mess. By default, there is 'Enter
|
||||
// Full Screen' in the window menu which is mapped to fn-F, but that
|
||||
// will only work if a window was created in SDL as windowed. If we
|
||||
// fullscreen that window and restart the app, we'll then have a *real*
|
||||
// fullscreen sdl window and that shortcut won't work anymore. So to
|
||||
// keep things consistent we advertise ctrl-f which we always handle
|
||||
// ourselves. Maybe this situation will be cleaned up in SDL 3, but its
|
||||
// not a huge deal anyway since our Cocoa Mac version behaves cleanly.
|
||||
return "Ctrl+F";
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
auto AppAdapterSDL::SupportsVSync() -> bool const { return true; }
|
||||
auto AppAdapterSDL::SupportsMaxFPS() -> bool const { return true; }
|
||||
|
||||
auto AppAdapterSDL::HasDirectKeyboardInput() -> bool {
|
||||
// We always provide direct keyboard events.
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
#endif // BA_SDL_BUILD
|
||||
|
||||
@ -34,10 +34,17 @@ class AppAdapterSDL : public AppAdapter {
|
||||
|
||||
auto TryRender() -> bool;
|
||||
|
||||
auto CanToggleFullscreen() -> bool const override;
|
||||
auto FullscreenControlAvailable() const -> bool override;
|
||||
auto FullscreenControlKeyShortcut() const
|
||||
-> std::optional<std::string> override;
|
||||
auto SupportsVSync() -> bool const override;
|
||||
auto SupportsMaxFPS() -> bool const override;
|
||||
|
||||
auto HasDirectKeyboardInput() -> bool override;
|
||||
void ApplyGraphicsSettings(const GraphicsSettings* settings) override;
|
||||
|
||||
auto GetGraphicsSettings() -> GraphicsSettings* override;
|
||||
|
||||
protected:
|
||||
void DoPushMainThreadRunnable(Runnable* runnable) override;
|
||||
void RunMainThreadEventLoopToCompletion() override;
|
||||
@ -48,14 +55,11 @@ class AppAdapterSDL : public AppAdapter {
|
||||
|
||||
private:
|
||||
class ScopedAllowGraphics_;
|
||||
void SetScreen_(bool fullscreen, int max_fps, VSyncRequest vsync_requested,
|
||||
TextureQualityRequest texture_quality_requested,
|
||||
GraphicsQualityRequest graphics_quality_requested);
|
||||
struct GraphicsSettings_;
|
||||
|
||||
void HandleSDLEvent_(const SDL_Event& event);
|
||||
void UpdateScreenSizes_();
|
||||
void ReloadRenderer_(bool fullscreen,
|
||||
GraphicsQualityRequest graphics_quality_requested,
|
||||
TextureQualityRequest texture_quality_requested);
|
||||
void ReloadRenderer_(const GraphicsSettings_* settings);
|
||||
void OnSDLJoystickAdded_(int index);
|
||||
void OnSDLJoystickRemoved_(int index);
|
||||
// Given an SDL joystick ID, returns our Ballistica input for it.
|
||||
@ -66,6 +70,7 @@ class AppAdapterSDL : public AppAdapter {
|
||||
void RemoveSDLInputDevice_(int index);
|
||||
void SleepUntilNextEventCycle_(microsecs_t cycle_start_time);
|
||||
|
||||
int max_fps_{60};
|
||||
bool done_ : 1 {};
|
||||
bool fullscreen_ : 1 {};
|
||||
bool vsync_actually_enabled_ : 1 {};
|
||||
@ -81,17 +86,16 @@ class AppAdapterSDL : public AppAdapter {
|
||||
/// that require such a setup.
|
||||
bool strict_graphics_context_ : 1 {};
|
||||
bool strict_graphics_allowed_ : 1 {};
|
||||
std::mutex strict_graphics_calls_mutex_;
|
||||
std::vector<Runnable*> strict_graphics_calls_;
|
||||
VSync vsync_{VSync::kUnset};
|
||||
uint32_t sdl_runnable_event_id_{};
|
||||
int max_fps_{60};
|
||||
std::mutex strict_graphics_calls_mutex_;
|
||||
std::vector<Runnable*> strict_graphics_calls_;
|
||||
microsecs_t oversleep_{};
|
||||
std::vector<JoystickInput*> sdl_joysticks_;
|
||||
Vector2f window_size_{1.0f, 1.0f};
|
||||
SDL_Window* sdl_window_{};
|
||||
void* sdl_gl_context_{};
|
||||
millisecs_t last_windowevent_close_time_{};
|
||||
seconds_t last_windowevent_close_time_{};
|
||||
};
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -40,7 +40,8 @@ void AppAdapterVR::PushVRSimpleRemoteStateCall(
|
||||
}
|
||||
|
||||
void AppAdapterVR::VRSetDrawDimensions(int w, int h) {
|
||||
g_base->graphics_server->SetScreenResolution(w, h);
|
||||
FatalError("FIXME UPDATE SET-SCREEN-RESOLUTION");
|
||||
// g_base->graphics_server->SetScreenResolution(w, h);
|
||||
}
|
||||
|
||||
void AppAdapterVR::VRPreDraw() {
|
||||
|
||||
@ -36,8 +36,6 @@ void AppMode::HandleGameQuery(const char* buffer, size_t size,
|
||||
|
||||
auto AppMode::DoesWorldFillScreen() -> bool { return false; }
|
||||
|
||||
void AppMode::GraphicsQualityChanged(GraphicsQuality quality) {}
|
||||
|
||||
void AppMode::DrawWorld(FrameDef* frame_def) {}
|
||||
|
||||
void AppMode::ChangeGameSpeed(int offs) {}
|
||||
|
||||
@ -62,8 +62,6 @@ class AppMode {
|
||||
|
||||
virtual void DrawWorld(FrameDef* frame_def);
|
||||
|
||||
virtual void GraphicsQualityChanged(GraphicsQuality quality);
|
||||
|
||||
/// Called whenever screen size changes.
|
||||
virtual void OnScreenSizeChange();
|
||||
|
||||
|
||||
@ -82,10 +82,10 @@ void Assets::StartLoading() {
|
||||
assert(g_base);
|
||||
assert(g_base->audio_server && g_base->assets_server
|
||||
&& g_base->graphics_server);
|
||||
assert(g_base->graphics_server->texture_compression_types_are_set());
|
||||
assert(g_base->graphics_server->texture_quality_set());
|
||||
assert(g_base->graphics->has_client_context());
|
||||
|
||||
assert(!asset_loads_allowed_); // We should only be called once.
|
||||
// We should only be called once.
|
||||
assert(!asset_loads_allowed_);
|
||||
asset_loads_allowed_ = true;
|
||||
|
||||
// Just grab the lock once for all this stuff for efficiency.
|
||||
@ -1102,10 +1102,13 @@ auto Assets::FindAssetFile(FileType type, const std::string& name)
|
||||
}
|
||||
}
|
||||
|
||||
assert(g_base->graphics_server
|
||||
&& g_base->graphics_server->texture_compression_types_are_set());
|
||||
assert(g_base->graphics_server
|
||||
&& g_base->graphics_server->texture_quality_set());
|
||||
// Make sure we know what compression/quality to use.
|
||||
assert(g_base->graphics->has_client_context());
|
||||
// assert(g_base->graphics_server
|
||||
// &&
|
||||
// g_base->graphics_server->texture_compression_types_are_set());
|
||||
// assert(g_base->graphics_server
|
||||
// && g_base->graphics_server->texture_quality_set());
|
||||
prefix = "textures/";
|
||||
|
||||
#if BA_OSTYPE_ANDROID && !BA_ANDROID_DDS_BUILD
|
||||
|
||||
@ -117,6 +117,8 @@ class Assets {
|
||||
|
||||
auto language_state() const { return language_state_; }
|
||||
|
||||
auto asset_loads_allowed() const { return asset_loads_allowed_; }
|
||||
|
||||
private:
|
||||
static void MarkAssetForLoad(Asset* c);
|
||||
void LoadSystemTexture(SysTextureID id, const char* name);
|
||||
@ -136,20 +138,21 @@ class Assets {
|
||||
std::unordered_map<std::string, Object::Ref<T> >* c_list)
|
||||
-> Object::Ref<T>;
|
||||
|
||||
std::vector<std::string> asset_paths_;
|
||||
int language_state_{};
|
||||
bool have_pending_loads_[static_cast<int>(AssetType::kLast)]{};
|
||||
|
||||
// Will be true while a AssetListLock exists. Good to debug-verify this
|
||||
// during any asset list access.
|
||||
bool asset_lists_locked_ : 1 {};
|
||||
bool asset_loads_allowed_ : 1 {};
|
||||
bool sys_assets_loaded_ : 1 {};
|
||||
|
||||
std::vector<std::string> asset_paths_;
|
||||
std::unordered_map<std::string, std::string> packages_;
|
||||
|
||||
// For use by AssetListLock; don't manually acquire.
|
||||
std::mutex asset_lists_mutex_;
|
||||
|
||||
// Will be true while a AssetListLock exists. Good to debug-verify this
|
||||
// during any asset list access.
|
||||
bool asset_lists_locked_{};
|
||||
|
||||
// 'hard-wired' internal assets
|
||||
bool asset_loads_allowed_{};
|
||||
bool sys_assets_loaded_{};
|
||||
std::vector<Object::Ref<TextureAsset> > system_textures_;
|
||||
std::vector<Object::Ref<TextureAsset> > system_cube_map_textures_;
|
||||
std::vector<Object::Ref<SoundAsset> > system_sounds_;
|
||||
@ -177,7 +180,6 @@ class Assets {
|
||||
// Text & Language (need to mold this into more asset-like concepts).
|
||||
std::mutex language_mutex_;
|
||||
std::unordered_map<std::string, std::string> language_;
|
||||
int language_state_{};
|
||||
std::mutex special_char_mutex_;
|
||||
std::unordered_map<SpecialChar, std::string> special_char_strings_;
|
||||
};
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
#include "ballistica/base/assets/asset.h"
|
||||
#include "ballistica/base/assets/assets.h"
|
||||
#include "ballistica/base/graphics/graphics.h"
|
||||
#include "ballistica/base/graphics/graphics_server.h"
|
||||
#include "ballistica/base/support/huffman.h"
|
||||
#include "ballistica/shared/foundation/event_loop.h"
|
||||
@ -221,12 +222,18 @@ void AssetsServer::WriteReplayMessages() {
|
||||
void AssetsServer::Process() {
|
||||
// Make sure we don't do any loading until we know what kind/quality of
|
||||
// textures we'll be loading.
|
||||
if (!g_base->assets || !g_base->graphics_server
|
||||
|| !g_base->graphics_server
|
||||
->texture_compression_types_are_set() // NOLINT
|
||||
|| !g_base->graphics_server->texture_quality_set()) {
|
||||
|
||||
// FIXME - we'll need to revisit this when adding support for
|
||||
// renderer switches, since this is not especially thread-safe.
|
||||
|
||||
if (!g_base->graphics->has_client_context()) {
|
||||
return;
|
||||
}
|
||||
// if (!g_base->assets ||
|
||||
// || !g_base->graphics->texture_compression_types_are_set() // NOLINT
|
||||
// || !g_base->graphics_server->texture_quality_set()) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// Process exactly 1 preload item. Empty out our non-audio list first
|
||||
// (audio is less likely to cause noticeable hitches if it needs to be loaded
|
||||
|
||||
@ -93,11 +93,14 @@ auto TextureAsset::GetNameFull() const -> std::string {
|
||||
void TextureAsset::DoPreload() {
|
||||
assert(valid_);
|
||||
|
||||
assert(g_base->graphics_server
|
||||
&& g_base->graphics_server->texture_compression_types_are_set());
|
||||
// Make sure we're not loading without knowing what texture types we
|
||||
// support.
|
||||
// assert(g_base->graphics->has_client_context());
|
||||
// assert(g_base->graphics_server
|
||||
// && g_base->graphics_server->texture_compression_types_are_set());
|
||||
|
||||
// We figure out which LOD should be our base level based on quality.
|
||||
TextureQuality texture_quality = g_base->graphics_server->texture_quality();
|
||||
// Figure out which LOD should be our base level based on texture quality.
|
||||
auto texture_quality = g_base->graphics->placeholder_texture_quality();
|
||||
|
||||
// If we're a text-texture.
|
||||
if (packer_.Exists()) {
|
||||
@ -218,12 +221,14 @@ void TextureAsset::DoPreload() {
|
||||
&preload_datas_[0].base_level);
|
||||
|
||||
// We should only be loading this if we support etc1 in hardware.
|
||||
assert(g_base->graphics_server->SupportsTextureCompressionType(
|
||||
TextureCompressionType::kETC1));
|
||||
assert(g_base->graphics->placeholder_client_context()
|
||||
->SupportsTextureCompressionType(
|
||||
TextureCompressionType::kETC1));
|
||||
|
||||
// Decompress dxt1/dxt5 ones if we don't natively support S3TC.
|
||||
if (!g_base->graphics_server->SupportsTextureCompressionType(
|
||||
TextureCompressionType::kS3TC)) {
|
||||
if (!g_base->graphics->placeholder_client_context()
|
||||
->SupportsTextureCompressionType(
|
||||
TextureCompressionType::kS3TC)) {
|
||||
if ((preload_datas_[0].formats[preload_datas_[0].base_level]
|
||||
== TextureFormat::kDXT5)
|
||||
|| (preload_datas_[0].formats[preload_datas_[0].base_level]
|
||||
@ -241,8 +246,9 @@ void TextureAsset::DoPreload() {
|
||||
&preload_datas_[0].base_level);
|
||||
|
||||
// Decompress dxt1/dxt5 if we don't natively support it.
|
||||
if (!g_base->graphics_server->SupportsTextureCompressionType(
|
||||
TextureCompressionType::kS3TC)) {
|
||||
if (!g_base->graphics->placeholder_client_context()
|
||||
->SupportsTextureCompressionType(
|
||||
TextureCompressionType::kS3TC)) {
|
||||
preload_datas_[0].ConvertToUncompressed(this);
|
||||
}
|
||||
} else if (!strcmp(file_name_full_.c_str() + file_name_size - 4,
|
||||
@ -264,16 +270,18 @@ void TextureAsset::DoPreload() {
|
||||
== TextureFormat::kETC2_RGB)
|
||||
|| (preload_datas_[0].formats[preload_datas_[0].base_level]
|
||||
== TextureFormat::kETC2_RGBA))
|
||||
&& (!g_base->graphics_server->SupportsTextureCompressionType(
|
||||
TextureCompressionType::kETC2))) {
|
||||
&& (!g_base->graphics->placeholder_client_context()
|
||||
->SupportsTextureCompressionType(
|
||||
TextureCompressionType::kETC2))) {
|
||||
preload_datas_[0].ConvertToUncompressed(this);
|
||||
}
|
||||
|
||||
// Decompress etc1 if we don't natively support it.
|
||||
if ((preload_datas_[0].formats[preload_datas_[0].base_level]
|
||||
== TextureFormat::kETC1)
|
||||
&& (!g_base->graphics_server->SupportsTextureCompressionType(
|
||||
TextureCompressionType::kETC1))) {
|
||||
&& (!g_base->graphics->placeholder_client_context()
|
||||
->SupportsTextureCompressionType(
|
||||
TextureCompressionType::kETC1))) {
|
||||
preload_datas_[0].ConvertToUncompressed(this);
|
||||
}
|
||||
|
||||
@ -287,8 +295,9 @@ void TextureAsset::DoPreload() {
|
||||
&preload_datas_[0].base_level);
|
||||
|
||||
// We should only be loading this if we support pvr in hardware.
|
||||
assert(g_base->graphics_server->SupportsTextureCompressionType(
|
||||
TextureCompressionType::kPVR));
|
||||
assert(
|
||||
g_base->graphics->placeholder_client_context()
|
||||
->SupportsTextureCompressionType(TextureCompressionType::kPVR));
|
||||
} else if (!strcmp(file_name_full_.c_str() + file_name_size - 4,
|
||||
".nop")) {
|
||||
// Dummy path for headless; nothing to do here.
|
||||
@ -342,12 +351,14 @@ void TextureAsset::DoPreload() {
|
||||
}
|
||||
|
||||
// We should only be loading this if we support etc1 in hardware.
|
||||
assert(g_base->graphics_server->SupportsTextureCompressionType(
|
||||
TextureCompressionType::kETC1));
|
||||
assert(g_base->graphics->placeholder_client_context()
|
||||
->SupportsTextureCompressionType(
|
||||
TextureCompressionType::kETC1));
|
||||
|
||||
// Decompress dxt1/dxt5 ones if we don't natively support S3TC.
|
||||
if (!g_base->graphics_server->SupportsTextureCompressionType(
|
||||
TextureCompressionType::kS3TC)) {
|
||||
if (!g_base->graphics->placeholder_client_context()
|
||||
->SupportsTextureCompressionType(
|
||||
TextureCompressionType::kS3TC)) {
|
||||
if ((preload_datas_[d].formats[preload_datas_[d].base_level]
|
||||
== TextureFormat::kDXT5)
|
||||
|| (preload_datas_[d].formats[preload_datas_[d].base_level]
|
||||
@ -365,8 +376,9 @@ void TextureAsset::DoPreload() {
|
||||
&preload_datas_[d].base_level);
|
||||
|
||||
// Decompress dxt1/dxt5 if we don't natively support it.
|
||||
if (!g_base->graphics_server->SupportsTextureCompressionType(
|
||||
TextureCompressionType::kS3TC)) {
|
||||
if (!g_base->graphics->placeholder_client_context()
|
||||
->SupportsTextureCompressionType(
|
||||
TextureCompressionType::kS3TC)) {
|
||||
preload_datas_[d].ConvertToUncompressed(this);
|
||||
}
|
||||
} else if (!strcmp(file_name_full_.c_str() + file_name_size - 4,
|
||||
@ -383,16 +395,18 @@ void TextureAsset::DoPreload() {
|
||||
== TextureFormat::kETC2_RGB)
|
||||
|| (preload_datas_[d].formats[preload_datas_[d].base_level]
|
||||
== TextureFormat::kETC2_RGBA))
|
||||
&& (!g_base->graphics_server->SupportsTextureCompressionType(
|
||||
TextureCompressionType::kETC2))) {
|
||||
&& (!g_base->graphics->placeholder_client_context()
|
||||
->SupportsTextureCompressionType(
|
||||
TextureCompressionType::kETC2))) {
|
||||
preload_datas_[d].ConvertToUncompressed(this);
|
||||
}
|
||||
|
||||
// Decompress etc1 if we don't natively support it.
|
||||
if ((preload_datas_[d].formats[preload_datas_[d].base_level]
|
||||
== TextureFormat::kETC1)
|
||||
&& (!g_base->graphics_server->SupportsTextureCompressionType(
|
||||
TextureCompressionType::kETC1))) {
|
||||
&& (!g_base->graphics->placeholder_client_context()
|
||||
->SupportsTextureCompressionType(
|
||||
TextureCompressionType::kETC1))) {
|
||||
preload_datas_[d].ConvertToUncompressed(this);
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include "ballistica/base/assets/sound_asset.h"
|
||||
#include "ballistica/base/audio/audio_server.h"
|
||||
#include "ballistica/base/audio/audio_source.h"
|
||||
#include "ballistica/base/graphics/graphics.h"
|
||||
#include "ballistica/base/support/app_config.h"
|
||||
#include "ballistica/shared/foundation/event_loop.h"
|
||||
|
||||
@ -12,6 +13,19 @@ namespace ballistica::base {
|
||||
|
||||
Audio::Audio() = default;
|
||||
|
||||
auto Audio::UseLowQualityAudio() -> bool {
|
||||
assert(g_base->InLogicThread());
|
||||
// Currently just piggybacking off graphics quality here.
|
||||
if (g_core->HeadlessMode() || g_base->graphics->has_client_context()) {
|
||||
return true;
|
||||
}
|
||||
// We don't have a frame-def to look at so need to calc this ourself; ugh.
|
||||
auto quality = Graphics::GraphicsQualityFromRequest(
|
||||
g_base->graphics->settings()->graphics_quality,
|
||||
g_base->graphics->client_context()->auto_graphics_quality);
|
||||
return quality < GraphicsQuality::kMedium;
|
||||
}
|
||||
|
||||
void Audio::Reset() {
|
||||
assert(g_base->InLogicThread());
|
||||
g_base->audio_server->PushResetCall();
|
||||
|
||||
@ -29,36 +29,41 @@ class Audio {
|
||||
virtual void OnScreenSizeChange();
|
||||
virtual void StepDisplayTime();
|
||||
|
||||
/// Can be keyed off of to cut corners in audio (leaving sounds out, etc.)
|
||||
/// Currently just piggybacks off graphics quality settings but this logic
|
||||
/// may get fancier in the future.
|
||||
auto UseLowQualityAudio() -> bool;
|
||||
|
||||
void SetVolumes(float music_volume, float sound_volume);
|
||||
|
||||
void SetListenerPosition(const Vector3f& p);
|
||||
void SetListenerOrientation(const Vector3f& forward, const Vector3f& up);
|
||||
void SetSoundPitch(float pitch);
|
||||
|
||||
// Return a pointer to a locked sound source, or nullptr if they're all busy.
|
||||
// The sound source will be reset to standard settings (no loop, fade 1, pos
|
||||
// 0,0,0, etc.).
|
||||
// Send the source any immediate commands and then unlock it.
|
||||
// For later modifications, re-retrieve the sound with GetPlayingSound()
|
||||
/// Return a pointer to a locked sound source, or nullptr if they're all busy.
|
||||
/// The sound source will be reset to standard settings (no loop, fade 1, pos
|
||||
/// 0,0,0, etc.).
|
||||
/// Send the source any immediate commands and then unlock it.
|
||||
/// For later modifications, re-retrieve the sound with GetPlayingSound()
|
||||
auto SourceBeginNew() -> AudioSource*;
|
||||
|
||||
// If a sound play id is playing, locks and returns its sound source.
|
||||
// on success, you must unlock the source once done with it.
|
||||
/// If a sound play id is playing, locks and returns its sound source.
|
||||
/// on success, you must unlock the source once done with it.
|
||||
auto SourceBeginExisting(uint32_t play_id, int debug_id) -> AudioSource*;
|
||||
|
||||
// Return true if the sound id is currently valid. This is not guaranteed
|
||||
// to be super accurate, but can be used to determine if a sound is still
|
||||
// playing.
|
||||
/// Return true if the sound id is currently valid. This is not guaranteed
|
||||
/// to be super accurate, but can be used to determine if a sound is still
|
||||
/// playing.
|
||||
auto IsSoundPlaying(uint32_t play_id) -> bool;
|
||||
|
||||
// Simple one-shot play functions.
|
||||
/// Simple one-shot play functions.
|
||||
auto PlaySound(SoundAsset* s, float volume = 1.0f) -> std::optional<uint32_t>;
|
||||
auto PlaySoundAtPosition(SoundAsset* sound, float volume, float x, float y,
|
||||
float z) -> std::optional<uint32_t>;
|
||||
|
||||
// Call this if you want to prevent repeated plays of the same sound. It'll
|
||||
// tell you if the sound has been played recently. The one-shot sound-play
|
||||
// functions use this under the hood. (PlaySound, PlaySoundAtPosition).
|
||||
/// Call this if you want to prevent repeated plays of the same sound. It'll
|
||||
/// tell you if the sound has been played recently. The one-shot sound-play
|
||||
/// functions use this under the hood. (PlaySound, PlaySoundAtPosition).
|
||||
auto ShouldPlay(SoundAsset* s) -> bool;
|
||||
|
||||
// Hmm; shouldn't these be accessed through the Source class?
|
||||
@ -73,15 +78,15 @@ class Audio {
|
||||
}
|
||||
|
||||
private:
|
||||
// Flat list of client sources indexed by id.
|
||||
/// Flat list of client sources indexed by id.
|
||||
std::vector<AudioSource*> client_sources_;
|
||||
|
||||
// List of sources that are ready to use.
|
||||
// This is kept filled by the audio thread
|
||||
// and used by the client.
|
||||
/// List of sources that are ready to use.
|
||||
/// This is kept filled by the audio thread
|
||||
/// and used by the client.
|
||||
std::vector<AudioSource*> available_sources_;
|
||||
|
||||
// This must be locked whenever accessing the availableSources list.
|
||||
/// This must be locked whenever accessing the availableSources list.
|
||||
std::mutex available_sources_mutex_;
|
||||
};
|
||||
|
||||
|
||||
@ -420,6 +420,10 @@ auto BaseFeatureSet::IsUnmodifiedBlessedBuild() -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto BaseFeatureSet::InMainThread() const -> bool {
|
||||
return g_core->InMainThread();
|
||||
}
|
||||
|
||||
auto BaseFeatureSet::InAssetsThread() const -> bool {
|
||||
if (auto* loop = assets_server->event_loop()) {
|
||||
return loop->ThreadIsCurrent();
|
||||
@ -658,6 +662,10 @@ void BaseFeatureSet::DoPushObjCall(const PythonObjectSetBase* objset, int id,
|
||||
|
||||
auto BaseFeatureSet::IsAppStarted() const -> bool { return app_started_; }
|
||||
|
||||
auto BaseFeatureSet::IsAppBootstrapped() const -> bool {
|
||||
return logic->app_bootstrapping_complete();
|
||||
}
|
||||
|
||||
auto BaseFeatureSet::ShutdownSuppressBegin() -> bool {
|
||||
std::scoped_lock lock(shutdown_suppress_lock_);
|
||||
|
||||
|
||||
@ -59,6 +59,8 @@ class DataAsset;
|
||||
class FrameDef;
|
||||
class Graphics;
|
||||
class GraphicsServer;
|
||||
struct GraphicsSettings;
|
||||
struct GraphicsClientContext;
|
||||
class Huffman;
|
||||
class ImageMesh;
|
||||
class Input;
|
||||
@ -662,6 +664,7 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
|
||||
/// allowing certain functionality before this time.
|
||||
auto IsBaseCompletelyImported() -> bool;
|
||||
|
||||
auto InMainThread() const -> bool;
|
||||
auto InAssetsThread() const -> bool override;
|
||||
auto InLogicThread() const -> bool override;
|
||||
auto InAudioThread() const -> bool override;
|
||||
@ -672,11 +675,18 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
|
||||
/// High level screen-message call usable from any thread.
|
||||
void ScreenMessage(const std::string& s, const Vector3f& color) override;
|
||||
|
||||
/// Has StartApp been called (and completely finished its work)?
|
||||
/// Code that sends calls/messages to other threads or otherwise uses
|
||||
/// app functionality may want to check this to avoid crashes.
|
||||
/// Has StartApp been called (and completely finished its work)? Code that
|
||||
/// sends calls/messages to other threads or otherwise uses app
|
||||
/// functionality may want to check this to avoid crashes. Note that some
|
||||
/// app functionality such as loading assets is not available until
|
||||
/// IsAppBootstrapped returns true. This call is thread safe.
|
||||
auto IsAppStarted() const -> bool override;
|
||||
|
||||
/// Has the app bootstrapping phase completed? The bootstrapping phase
|
||||
/// involves initial screen/graphics setup. Asset loading is not allowed
|
||||
/// until it is complete.
|
||||
auto IsAppBootstrapped() const -> bool override;
|
||||
|
||||
void PlusDirectSendV1CloudLogs(const std::string& prefix,
|
||||
const std::string& suffix, bool instant,
|
||||
int* result) override;
|
||||
|
||||
@ -39,10 +39,15 @@ void BGDynamics::Emit(const BGDynamicsEmission& e) {
|
||||
void BGDynamics::Step(const Vector3f& cam_pos, int step_millisecs) {
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
// Don't actually start doing anything until there's a
|
||||
// client-graphics-context. We need this to calculate qualities/etc.
|
||||
if (!g_base->graphics->has_client_context()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The BG dynamics thread just processes steps as fast as it can;
|
||||
// we need to throttle what we send or tell it to cut back if its behind
|
||||
int step_count = g_base->bg_dynamics_server->step_count();
|
||||
// printf("STEP COUNT %d\n", step_count);
|
||||
|
||||
// If we're really getting behind, start pruning stuff.
|
||||
if (step_count > 3) {
|
||||
@ -62,6 +67,9 @@ void BGDynamics::Step(const Vector3f& cam_pos, int step_millisecs) {
|
||||
// Pass a newly allocated raw pointer to the bg-dynamics thread; it takes care
|
||||
// of disposing it when done.
|
||||
auto d = Object::NewDeferred<BGDynamicsServer::StepData>();
|
||||
d->graphics_quality = Graphics::GraphicsQualityFromRequest(
|
||||
g_base->graphics->settings()->graphics_quality,
|
||||
g_base->graphics->client_context()->auto_graphics_quality);
|
||||
d->step_millisecs = step_millisecs;
|
||||
d->cam_pos = cam_pos;
|
||||
|
||||
@ -174,7 +182,7 @@ void BGDynamics::Draw(FrameDef* frame_def) {
|
||||
|
||||
// In high-quality, we draw in the overlay pass so that we don't get wiped
|
||||
// out by depth-of-field.
|
||||
bool draw_in_overlay = (frame_def->quality() >= GraphicsQuality::kHigh);
|
||||
bool draw_in_overlay = frame_def->quality() >= GraphicsQuality::kHigh;
|
||||
SpriteComponent c(draw_in_overlay ? frame_def->overlay_3d_pass()
|
||||
: frame_def->beauty_pass());
|
||||
c.SetCameraAligned(true);
|
||||
@ -232,7 +240,7 @@ void BGDynamics::Draw(FrameDef* frame_def) {
|
||||
tendrils_mesh_->SetIndexData(ds->tendril_indices);
|
||||
tendrils_mesh_->SetData(
|
||||
Object::Ref<MeshBuffer<VertexSmokeFull>>(ds->tendril_vertices));
|
||||
bool draw_in_overlay = (frame_def->quality() >= GraphicsQuality::kHigh);
|
||||
bool draw_in_overlay = frame_def->quality() >= GraphicsQuality::kHigh;
|
||||
SmokeComponent c(draw_in_overlay ? frame_def->overlay_3d_pass()
|
||||
: frame_def->beauty_pass());
|
||||
c.SetOverlay(draw_in_overlay);
|
||||
|
||||
@ -2282,7 +2282,8 @@ void BGDynamicsServer::Step(StepData* step_data) {
|
||||
auto ref(Object::CompleteDeferred(step_data));
|
||||
|
||||
// Keep our quality in sync with the graphics thread's.
|
||||
graphics_quality_ = g_base->graphics_server->graphics_quality();
|
||||
graphics_quality_ = step_data->graphics_quality;
|
||||
assert(graphics_quality_ != GraphicsQuality::kUnset);
|
||||
|
||||
cam_pos_ = step_data->cam_pos;
|
||||
|
||||
|
||||
@ -73,6 +73,7 @@ class BGDynamicsServer {
|
||||
auto GetDefaultOwnerThread() const -> EventLoopID override {
|
||||
return EventLoopID::kBGDynamics;
|
||||
}
|
||||
GraphicsQuality graphics_quality{};
|
||||
int step_millisecs{};
|
||||
Vector3f cam_pos{0.0f, 0.0f, 0.0f};
|
||||
|
||||
|
||||
@ -238,10 +238,10 @@ class RendererGL::ProgramGL {
|
||||
|
||||
// Update matrices as necessary.
|
||||
|
||||
uint32_t mvpState =
|
||||
int mvp_state =
|
||||
g_base->graphics_server->GetModelViewProjectionMatrixState();
|
||||
if (mvpState != mvp_state_) {
|
||||
mvp_state_ = mvpState;
|
||||
if (mvp_state != mvp_state_) {
|
||||
mvp_state_ = mvp_state;
|
||||
glUniformMatrix4fv(
|
||||
mvp_uniform_, 1, 0,
|
||||
g_base->graphics_server->GetModelViewProjectionMatrix().m);
|
||||
@ -251,7 +251,7 @@ class RendererGL::ProgramGL {
|
||||
if (pflags_ & PFLAG_USES_MODEL_WORLD_MATRIX) {
|
||||
// With world space points this would be identity; don't waste time.
|
||||
assert(!(pflags_ & PFLAG_WORLD_SPACE_PTS));
|
||||
uint32_t state = g_base->graphics_server->GetModelWorldMatrixState();
|
||||
int state = g_base->graphics_server->GetModelWorldMatrixState();
|
||||
if (state != model_world_matrix_state_) {
|
||||
model_world_matrix_state_ = state;
|
||||
glUniformMatrix4fv(model_world_matrix_uniform_, 1, 0,
|
||||
@ -264,8 +264,7 @@ class RendererGL::ProgramGL {
|
||||
// With world space points this would be identity; don't waste time.
|
||||
assert(!(pflags_ & PFLAG_WORLD_SPACE_PTS));
|
||||
// There's no state for just modelview but this works.
|
||||
uint32_t state =
|
||||
g_base->graphics_server->GetModelViewProjectionMatrixState();
|
||||
int state = g_base->graphics_server->GetModelViewProjectionMatrixState();
|
||||
if (state != model_view_matrix_state_) {
|
||||
model_view_matrix_state_ = state;
|
||||
glUniformMatrix4fv(model_view_matrix_uniform_, 1, 0,
|
||||
@ -275,7 +274,7 @@ class RendererGL::ProgramGL {
|
||||
BA_DEBUG_CHECK_GL_ERROR;
|
||||
|
||||
if (pflags_ & PFLAG_USES_CAM_POS) {
|
||||
uint32_t state = g_base->graphics_server->cam_pos_state();
|
||||
int state = g_base->graphics_server->cam_pos_state();
|
||||
if (state != cam_pos_state_) {
|
||||
cam_pos_state_ = state;
|
||||
const Vector3f& p(g_base->graphics_server->cam_pos());
|
||||
@ -285,7 +284,7 @@ class RendererGL::ProgramGL {
|
||||
BA_DEBUG_CHECK_GL_ERROR;
|
||||
|
||||
if (pflags_ & PFLAG_USES_CAM_ORIENT_MATRIX) {
|
||||
uint32_t state = g_base->graphics_server->GetCamOrientMatrixState();
|
||||
int state = g_base->graphics_server->GetCamOrientMatrixState();
|
||||
if (state != cam_orient_matrix_state_) {
|
||||
cam_orient_matrix_state_ = state;
|
||||
glUniformMatrix4fv(cam_orient_matrix_uniform_, 1, 0,
|
||||
@ -295,7 +294,7 @@ class RendererGL::ProgramGL {
|
||||
BA_DEBUG_CHECK_GL_ERROR;
|
||||
|
||||
if (pflags_ & PFLAG_USES_SHADOW_PROJECTION_MATRIX) {
|
||||
uint32_t state =
|
||||
int state =
|
||||
g_base->graphics_server->light_shadow_projection_matrix_state();
|
||||
if (state != light_shadow_projection_matrix_state_) {
|
||||
light_shadow_projection_matrix_state_ = state;
|
||||
@ -336,19 +335,19 @@ class RendererGL::ProgramGL {
|
||||
Object::Ref<VertexShaderGL> vertex_shader_;
|
||||
std::string name_;
|
||||
GLuint program_{};
|
||||
int pflags_{};
|
||||
uint32_t mvp_state_{};
|
||||
GLint mvp_uniform_{};
|
||||
GLint model_world_matrix_uniform_{};
|
||||
GLint model_view_matrix_uniform_{};
|
||||
GLint light_shadow_projection_matrix_uniform_{};
|
||||
uint32_t light_shadow_projection_matrix_state_{};
|
||||
uint32_t model_world_matrix_state_{};
|
||||
uint32_t model_view_matrix_state_{};
|
||||
GLint cam_pos_uniform_{};
|
||||
uint32_t cam_pos_state_{};
|
||||
GLint cam_orient_matrix_uniform_{};
|
||||
GLuint cam_orient_matrix_state_{};
|
||||
int cam_orient_matrix_state_{};
|
||||
int light_shadow_projection_matrix_state_{};
|
||||
int pflags_{};
|
||||
int mvp_state_{};
|
||||
int cam_pos_state_{};
|
||||
int model_world_matrix_state_{};
|
||||
int model_view_matrix_state_{};
|
||||
BA_DISALLOW_CLASS_COPIES(ProgramGL);
|
||||
};
|
||||
|
||||
|
||||
@ -366,7 +366,7 @@ void RendererGL::CheckGLCapabilities_() {
|
||||
|
||||
// Both GL 3 and GL ES 3.0 support depth textures (and thus our high
|
||||
// quality mode) as a core feature.
|
||||
g_base->graphics->SetSupportsHighQualityGraphics(true);
|
||||
// g_base->graphics->SetSupportsHighQualityGraphics(true);
|
||||
|
||||
// Store the tex-compression type we support.
|
||||
BA_DEBUG_CHECK_GL_ERROR;
|
||||
@ -2598,7 +2598,8 @@ void RendererGL::RetainShader_(ProgramGL* p) { shaders_.emplace_back(p); }
|
||||
void RendererGL::Load() {
|
||||
assert(g_base->app_adapter->InGraphicsContext());
|
||||
assert(!data_loaded_);
|
||||
assert(g_base->graphics_server->graphics_quality_set());
|
||||
assert(g_base->graphics_server->graphics_quality()
|
||||
!= GraphicsQuality::kUnset);
|
||||
BA_DEBUG_CHECK_GL_ERROR;
|
||||
if (!got_screen_framebuffer_) {
|
||||
got_screen_framebuffer_ = true;
|
||||
|
||||
@ -115,9 +115,14 @@ void Graphics::OnAppShutdownComplete() { assert(g_base->InLogicThread()); }
|
||||
void Graphics::DoApplyAppConfig() {
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
// Any time we load the config we ship a new graphics-settings to
|
||||
// the graphics server since something likely changed.
|
||||
graphics_settings_dirty_ = true;
|
||||
|
||||
show_fps_ = g_base->app_config->Resolve(AppConfig::BoolID::kShowFPS);
|
||||
show_ping_ = g_base->app_config->Resolve(AppConfig::BoolID::kShowPing);
|
||||
tv_border_ = g_base->app_config->Resolve(AppConfig::BoolID::kEnableTVBorder);
|
||||
// tv_border_ =
|
||||
// g_base->app_config->Resolve(AppConfig::BoolID::kEnableTVBorder);
|
||||
|
||||
bool disable_camera_shake =
|
||||
g_base->app_config->Resolve(AppConfig::BoolID::kDisableCameraShake);
|
||||
@ -126,6 +131,52 @@ void Graphics::DoApplyAppConfig() {
|
||||
bool disable_camera_gyro =
|
||||
g_base->app_config->Resolve(AppConfig::BoolID::kDisableCameraGyro);
|
||||
set_camera_gyro_explicitly_disabled(disable_camera_gyro);
|
||||
|
||||
applied_app_config_ = true;
|
||||
|
||||
// At this point we may want to send initial graphics settings to the
|
||||
// graphics server if we haven't.
|
||||
UpdateInitialGraphicsSettingsSend_();
|
||||
}
|
||||
|
||||
void Graphics::UpdateInitialGraphicsSettingsSend_() {
|
||||
assert(g_base->InLogicThread());
|
||||
if (sent_initial_graphics_settings_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to send an initial graphics-settings to the server to kick
|
||||
// things off, but we need a few things to be in place first.
|
||||
auto app_config_ready = applied_app_config_;
|
||||
// At some point we may want to wait to know our actual screen res before
|
||||
// sending. This won't apply everywhere though since on some platforms the
|
||||
// screen doesn't exist until we send this.
|
||||
auto screen_resolution_ready = true;
|
||||
|
||||
if (app_config_ready && screen_resolution_ready) {
|
||||
// Update/grab the current settings snapshot.
|
||||
auto* settings = GetGraphicsSettingsSnapshot();
|
||||
|
||||
// We need to explicitly push settings to the graphics server to kick
|
||||
// things off. We need to keep this settings instance alive until
|
||||
// handled by the graphics context (which might be in another thread
|
||||
// where we're not allowed to muck with settings' refs from). So let's
|
||||
// explicitly increment its refcount here in the logic thread now and
|
||||
// then push a call back here to decrement it when we're done.
|
||||
settings->ObjectIncrementStrongRefCount();
|
||||
// auto* s = settings_.Get();
|
||||
g_base->app_adapter->PushGraphicsContextCall([settings] {
|
||||
assert(g_base->app_adapter->InGraphicsContext());
|
||||
g_base->graphics_server->ApplySettings(settings->Get());
|
||||
g_base->logic->event_loop()->PushCall([settings] {
|
||||
// Release our strong ref back here in the logic thread.
|
||||
assert(g_base->InLogicThread());
|
||||
settings->ObjectDecrementStrongRefCount();
|
||||
});
|
||||
});
|
||||
|
||||
sent_initial_graphics_settings_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::StepDisplayTime() { assert(g_base->InLogicThread()); }
|
||||
@ -976,6 +1027,20 @@ auto Graphics::GetEmptyFrameDef() -> FrameDef* {
|
||||
return frame_def;
|
||||
}
|
||||
|
||||
auto Graphics::GetGraphicsSettingsSnapshot() -> Snapshot<GraphicsSettings>* {
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
// If need be, ask the app-adapter to build us a new settings instance.
|
||||
if (graphics_settings_dirty_) {
|
||||
auto* new_settings = g_base->app_adapter->GetGraphicsSettings();
|
||||
new_settings->index = next_settings_index_++;
|
||||
settings_snapshot_ = Object::New<Snapshot<GraphicsSettings>>(new_settings);
|
||||
graphics_settings_dirty_ = false;
|
||||
}
|
||||
assert(settings_snapshot_.Exists());
|
||||
return settings_snapshot_.Get();
|
||||
}
|
||||
|
||||
void Graphics::ClearFrameDefDeleteList() {
|
||||
assert(g_base->InLogicThread());
|
||||
std::scoped_lock lock(frame_def_delete_list_mutex_);
|
||||
@ -1120,6 +1185,8 @@ void Graphics::DrawDevUI(FrameDef* frame_def) {
|
||||
|
||||
void Graphics::BuildAndPushFrameDef() {
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
BA_PRECONDITION_FATAL(g_base->logic->app_bootstrapping_complete());
|
||||
assert(camera_.Exists());
|
||||
assert(!g_core->HeadlessMode());
|
||||
|
||||
@ -1128,10 +1195,6 @@ void Graphics::BuildAndPushFrameDef() {
|
||||
assert(!building_frame_def_);
|
||||
building_frame_def_ = true;
|
||||
|
||||
// We should not be building/pushing any frames until the native
|
||||
// layer is fully bootstrapped.
|
||||
BA_PRECONDITION_FATAL(g_base->logic->app_bootstrapping_complete());
|
||||
|
||||
microsecs_t app_time_microsecs = g_core->GetAppTimeMicrosecs();
|
||||
|
||||
// Store how much time this frame_def represents.
|
||||
@ -1187,13 +1250,6 @@ void Graphics::BuildAndPushFrameDef() {
|
||||
internal_components_inited_ = true;
|
||||
}
|
||||
|
||||
// If graphics quality has changed since our last draw, inform anyone who
|
||||
// wants to know.
|
||||
if (last_frame_def_graphics_quality_ != frame_def->quality()) {
|
||||
last_frame_def_graphics_quality_ = frame_def->quality();
|
||||
g_base->app_mode()->GraphicsQualityChanged(frame_def->quality());
|
||||
}
|
||||
|
||||
ApplyCamera(frame_def);
|
||||
|
||||
if (progress_bar_) {
|
||||
@ -1254,7 +1310,7 @@ void Graphics::BuildAndPushFrameDef() {
|
||||
RunCleanFrameCommands();
|
||||
}
|
||||
|
||||
frame_def->Finalize();
|
||||
frame_def->Complete();
|
||||
|
||||
// Include all mesh-data loads and unloads that have accumulated up to
|
||||
// this point the graphics thread will have to handle these before
|
||||
@ -1465,10 +1521,10 @@ void Graphics::DoDrawFade(FrameDef* frame_def, float amt) {
|
||||
void Graphics::DrawCursor(FrameDef* frame_def) {
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
millisecs_t app_time_millisecs = frame_def->app_time_millisecs();
|
||||
auto app_time = frame_def->app_time();
|
||||
|
||||
bool can_show_cursor = g_base->app_adapter->ShouldUseCursor();
|
||||
bool should_show_cursor =
|
||||
auto can_show_cursor = g_base->app_adapter->ShouldUseCursor();
|
||||
auto should_show_cursor =
|
||||
camera_->manual() || g_base->input->IsCursorVisible();
|
||||
|
||||
if (g_base->app_adapter->HasHardwareCursor()) {
|
||||
@ -1482,9 +1538,9 @@ void Graphics::DrawCursor(FrameDef* frame_def) {
|
||||
// Ship this state when it changes and also every now and then just in
|
||||
// case things go wonky.
|
||||
if (new_cursor_visibility != hardware_cursor_visible_
|
||||
|| app_time_millisecs - last_cursor_visibility_event_time_ > 2000) {
|
||||
|| app_time - last_cursor_visibility_event_time_ > 2.137) {
|
||||
hardware_cursor_visible_ = new_cursor_visibility;
|
||||
last_cursor_visibility_event_time_ = app_time_millisecs;
|
||||
last_cursor_visibility_event_time_ = app_time;
|
||||
g_base->app_adapter->PushMainThreadCall([this] {
|
||||
assert(g_core && g_core->InMainThread());
|
||||
g_base->app_adapter->SetHardwareCursorVisible(hardware_cursor_visible_);
|
||||
@ -1555,11 +1611,6 @@ void Graphics::DrawBlotches(FrameDef* frame_def) {
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::SetSupportsHighQualityGraphics(bool s) {
|
||||
supports_high_quality_graphics_ = s;
|
||||
has_supports_high_quality_graphics_value_ = true;
|
||||
}
|
||||
|
||||
void Graphics::ClearScreenMessageTranslations() {
|
||||
assert(g_base && g_base->InLogicThread());
|
||||
for (auto&& i : screen_messages_) {
|
||||
@ -1922,20 +1973,55 @@ auto Graphics::ScreenMessageEntry::GetText() -> TextGroup& {
|
||||
|
||||
void Graphics::OnScreenSizeChange() {}
|
||||
|
||||
void Graphics::SetScreenSize(float virtual_width, float virtual_height,
|
||||
float pixel_width, float pixel_height) {
|
||||
void Graphics::CalcVirtualRes_(float* x, float* y) {
|
||||
float x_in = *x;
|
||||
float y_in = *y;
|
||||
if (*x / *y > static_cast<float>(kBaseVirtualResX)
|
||||
/ static_cast<float>(kBaseVirtualResY)) {
|
||||
*y = kBaseVirtualResY;
|
||||
*x = *y * (x_in / y_in);
|
||||
} else {
|
||||
*x = kBaseVirtualResX;
|
||||
*y = *x * (y_in / x_in);
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::SetScreenResolution(float x, float y) {
|
||||
assert(g_base->InLogicThread());
|
||||
res_x_virtual_ = virtual_width;
|
||||
res_y_virtual_ = virtual_height;
|
||||
res_x_ = pixel_width;
|
||||
res_y_ = pixel_height;
|
||||
|
||||
// Ignore redundant sets.
|
||||
if (res_x_ == x && res_y_ == y) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We'll need to ship a new settings to the server with this change.
|
||||
graphics_settings_dirty_ = true;
|
||||
|
||||
res_x_ = x;
|
||||
res_y_ = y;
|
||||
|
||||
// Calc virtual res. In vr mode our virtual res is independent of our
|
||||
// screen size (since it gets drawn to an overlay).
|
||||
if (g_core->IsVRMode()) {
|
||||
res_x_virtual_ = kBaseVirtualResX;
|
||||
res_y_virtual_ = kBaseVirtualResY;
|
||||
} else {
|
||||
res_x_virtual_ = res_x_;
|
||||
res_y_virtual_ = res_y_;
|
||||
CalcVirtualRes_(&res_x_virtual_, &res_y_virtual_);
|
||||
}
|
||||
|
||||
// Need to rebuild internal components (some are sized to the screen).
|
||||
internal_components_inited_ = false;
|
||||
|
||||
// This will inform all applicable logic thread subsystems.
|
||||
g_base->logic->OnScreenSizeChange(virtual_width, virtual_height, pixel_width,
|
||||
pixel_height);
|
||||
// Inform all our logic thread buddies of this change.
|
||||
g_base->logic->OnScreenSizeChange(res_x_virtual_, res_y_virtual_, res_x_,
|
||||
res_y_);
|
||||
|
||||
// This may trigger us sending initial graphics settings to the
|
||||
// graphics-server to kick off drawing.
|
||||
got_screen_resolution_ = true;
|
||||
UpdateInitialGraphicsSettingsSend_();
|
||||
}
|
||||
|
||||
void Graphics::ScreenMessageEntry::UpdateTranslation() {
|
||||
@ -2030,4 +2116,61 @@ void Graphics::LanguageChanged() {
|
||||
ClearScreenMessageTranslations();
|
||||
}
|
||||
|
||||
auto Graphics::GraphicsQualityFromRequest(GraphicsQualityRequest request,
|
||||
GraphicsQuality auto_val)
|
||||
-> GraphicsQuality {
|
||||
switch (request) {
|
||||
case GraphicsQualityRequest::kLow:
|
||||
return GraphicsQuality::kLow;
|
||||
case GraphicsQualityRequest::kMedium:
|
||||
return GraphicsQuality::kMedium;
|
||||
case GraphicsQualityRequest::kHigh:
|
||||
return GraphicsQuality::kHigh;
|
||||
case GraphicsQualityRequest::kHigher:
|
||||
return GraphicsQuality::kHigher;
|
||||
case GraphicsQualityRequest::kAuto:
|
||||
return auto_val;
|
||||
default:
|
||||
Log(LogLevel::kError, "Unhandled GraphicsQualityRequest value: "
|
||||
+ std::to_string(static_cast<int>(request)));
|
||||
return GraphicsQuality::kLow;
|
||||
}
|
||||
}
|
||||
auto Graphics::TextureQualityFromRequest(TextureQualityRequest request,
|
||||
TextureQuality auto_val)
|
||||
-> TextureQuality {
|
||||
switch (request) {
|
||||
case TextureQualityRequest::kLow:
|
||||
return TextureQuality::kLow;
|
||||
case TextureQualityRequest::kMedium:
|
||||
return TextureQuality::kMedium;
|
||||
case TextureQualityRequest::kHigh:
|
||||
return TextureQuality::kHigh;
|
||||
case TextureQualityRequest::kAuto:
|
||||
return auto_val;
|
||||
default:
|
||||
Log(LogLevel::kError, "Unhandled TextureQualityRequest value: "
|
||||
+ std::to_string(static_cast<int>(request)));
|
||||
return TextureQuality::kLow;
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::set_client_context(Snapshot<GraphicsClientContext>* context) {
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
// Currently we only expect this to be set once. That will change
|
||||
// once we support renderer swapping/etc.
|
||||
assert(!g_base->logic->graphics_ready());
|
||||
assert(!client_context_snapshot_.Exists());
|
||||
client_context_snapshot_ = context;
|
||||
|
||||
// Update our static placeholder value (we don't want to calc it dynamically
|
||||
// since it can be accessed from other threads).
|
||||
texture_quality_placeholder_ = TextureQualityFromRequest(
|
||||
settings()->texture_quality, client_context()->auto_texture_quality);
|
||||
|
||||
// Let the logic system know its free to proceed beyond bootstrapping.
|
||||
g_base->logic->OnGraphicsReady();
|
||||
}
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -6,13 +6,15 @@
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "ballistica/base/base.h"
|
||||
#include "ballistica/base/graphics/support/graphics_client_context.h"
|
||||
#include "ballistica/base/graphics/support/graphics_settings.h"
|
||||
#include "ballistica/shared/foundation/object.h"
|
||||
#include "ballistica/shared/foundation/types.h"
|
||||
#include "ballistica/shared/generic/snapshot.h"
|
||||
#include "ballistica/shared/math/matrix44f.h"
|
||||
#include "ballistica/shared/math/rect.h"
|
||||
#include "ballistica/shared/math/vector2f.h"
|
||||
@ -62,10 +64,10 @@ class Graphics {
|
||||
void OnScreenSizeChange();
|
||||
void DoApplyAppConfig();
|
||||
|
||||
/// Called by the graphics server to keep us up to date in the logic
|
||||
/// thread. Dispatches the news to all logic subsystems that care.
|
||||
void SetScreenSize(float virtual_width, float virtual_height,
|
||||
float physical_width, float physical_height);
|
||||
/// Should be called by the app-adapter to keep the engine informed
|
||||
/// on the drawable area it has to work with (in pixels).
|
||||
void SetScreenResolution(float x, float y);
|
||||
|
||||
void StepDisplayTime();
|
||||
|
||||
auto TextureQualityFromAppConfig() -> TextureQualityRequest;
|
||||
@ -97,13 +99,25 @@ class Graphics {
|
||||
// Called when the GraphicsServer has sent us a frame-def for deletion.
|
||||
void ReturnCompletedFrameDef(FrameDef* frame_def);
|
||||
|
||||
auto screen_pixel_width() const { return res_x_; }
|
||||
auto screen_pixel_height() const { return res_y_; }
|
||||
auto screen_pixel_width() const {
|
||||
assert(g_base->InLogicThread());
|
||||
return res_x_;
|
||||
}
|
||||
auto screen_pixel_height() const {
|
||||
assert(g_base->InLogicThread());
|
||||
return res_y_;
|
||||
}
|
||||
|
||||
// Return the size of the virtual screen. This value should always
|
||||
// Return the current size of the virtual screen. This value should always
|
||||
// be used for interface positioning, etc.
|
||||
auto screen_virtual_width() const { return res_x_virtual_; }
|
||||
auto screen_virtual_height() const { return res_y_virtual_; }
|
||||
auto screen_virtual_width() const {
|
||||
assert(g_base->InLogicThread());
|
||||
return res_x_virtual_;
|
||||
}
|
||||
auto screen_virtual_height() const {
|
||||
assert(g_base->InLogicThread());
|
||||
return res_y_virtual_;
|
||||
}
|
||||
|
||||
void ClearScreenMessageTranslations();
|
||||
|
||||
@ -226,10 +240,10 @@ class Graphics {
|
||||
float upper_top);
|
||||
void ReleaseFadeEndCommand();
|
||||
|
||||
auto tv_border() const {
|
||||
assert(g_base->InLogicThread());
|
||||
return tv_border_;
|
||||
}
|
||||
// auto tv_border() const {
|
||||
// assert(g_base->InLogicThread());
|
||||
// return tv_border_;
|
||||
// }
|
||||
|
||||
// Nodes that draw flat stuff into the overlay pass should query this z value
|
||||
// for where to draw in z.
|
||||
@ -270,17 +284,6 @@ class Graphics {
|
||||
return y * (res_y_virtual_ / res_y_);
|
||||
}
|
||||
|
||||
// FIXME: This should probably move to Renderer or AppAdapter once we
|
||||
// support switching renderers.
|
||||
auto supports_high_quality_graphics() const {
|
||||
assert(has_supports_high_quality_graphics_value_);
|
||||
return supports_high_quality_graphics_;
|
||||
}
|
||||
void SetSupportsHighQualityGraphics(bool s);
|
||||
auto has_supports_high_quality_graphics_value() const {
|
||||
return has_supports_high_quality_graphics_value_;
|
||||
}
|
||||
|
||||
void set_internal_components_inited(bool val) {
|
||||
internal_components_inited_ = val;
|
||||
}
|
||||
@ -322,19 +325,65 @@ class Graphics {
|
||||
camera_gyro_explicitly_disabled_ = disabled;
|
||||
}
|
||||
|
||||
auto* settings() const {
|
||||
assert(g_base->InLogicThread());
|
||||
assert(settings_snapshot_.Exists());
|
||||
return settings_snapshot_.Get()->Get();
|
||||
}
|
||||
|
||||
auto GetGraphicsSettingsSnapshot() -> Snapshot<GraphicsSettings>*;
|
||||
|
||||
/// Called by the graphics-server when a new client context is ready.
|
||||
void set_client_context(Snapshot<GraphicsClientContext>* context);
|
||||
|
||||
auto has_client_context() -> bool {
|
||||
return client_context_snapshot_.Exists();
|
||||
}
|
||||
|
||||
auto client_context() const -> const GraphicsClientContext* {
|
||||
assert(g_base->InLogicThread());
|
||||
assert(client_context_snapshot_.Exists());
|
||||
return client_context_snapshot_.Get()->Get();
|
||||
}
|
||||
|
||||
static auto GraphicsQualityFromRequest(GraphicsQualityRequest request,
|
||||
GraphicsQuality auto_val)
|
||||
-> GraphicsQuality;
|
||||
static auto TextureQualityFromRequest(TextureQualityRequest request,
|
||||
TextureQuality auto_val)
|
||||
-> TextureQuality;
|
||||
|
||||
/// For temporary use from arbitrary threads. This should be removed when
|
||||
/// possible and replaced with proper safe thread-specific access patterns
|
||||
/// (so we can support switching renderers/etc.).
|
||||
auto placeholder_texture_quality() const {
|
||||
assert(client_context_snapshot_.Exists());
|
||||
return texture_quality_placeholder_;
|
||||
}
|
||||
|
||||
/// For temporary use in arbitrary threads. This should be removed when
|
||||
/// possible and replaced with proper safe thread-specific access
|
||||
/// patterns (so we can support switching renderers/etc.).
|
||||
auto placeholder_client_context() const -> const GraphicsClientContext* {
|
||||
// Using this from arbitrary threads is currently ok currently since
|
||||
// context never changes once set. Will need to kill this call once that
|
||||
// can happen though.
|
||||
assert(client_context_snapshot_.Exists());
|
||||
return client_context_snapshot_.Get()->Get();
|
||||
}
|
||||
|
||||
protected:
|
||||
class ScreenMessageEntry;
|
||||
|
||||
Graphics();
|
||||
virtual ~Graphics();
|
||||
virtual void DoDrawFade(FrameDef* frame_def, float amt);
|
||||
|
||||
private:
|
||||
class ScreenMessageEntry;
|
||||
static void CalcVirtualRes_(float* x, float* y);
|
||||
void DrawBoxingGlovesTest(FrameDef* frame_def);
|
||||
void DrawBlotches(FrameDef* frame_def);
|
||||
void DrawCursor(FrameDef* frame_def);
|
||||
void DrawFades(FrameDef* frame_def);
|
||||
void DrawDebugBuffers(RenderPass* pass);
|
||||
|
||||
void UpdateAndDrawProgressBar(FrameDef* frame_def);
|
||||
void DoDrawBlotch(std::vector<uint16_t>* indices,
|
||||
std::vector<VertexSprite>* verts, const Vector3f& pos,
|
||||
@ -347,44 +396,47 @@ class Graphics {
|
||||
void DrawProgressBar(RenderPass* pass, float opacity);
|
||||
void UpdateProgressBarProgress(float target);
|
||||
void UpdateGyro(microsecs_t time, microsecs_t elapsed);
|
||||
void UpdateInitialGraphicsSettingsSend_();
|
||||
|
||||
bool drawing_transparent_only_{};
|
||||
bool drawing_opaque_only_{};
|
||||
bool has_supports_high_quality_graphics_value_{};
|
||||
bool supports_high_quality_graphics_{};
|
||||
bool internal_components_inited_{};
|
||||
bool fade_out_{true};
|
||||
bool progress_bar_{};
|
||||
bool progress_bar_fade_in_{};
|
||||
bool debug_draw_{};
|
||||
bool network_debug_display_enabled_{};
|
||||
bool hardware_cursor_visible_{};
|
||||
bool camera_shake_disabled_{};
|
||||
bool camera_gyro_explicitly_disabled_{};
|
||||
bool gyro_enabled_{true};
|
||||
bool show_fps_{};
|
||||
bool show_ping_{};
|
||||
bool show_net_info_{};
|
||||
bool tv_border_{};
|
||||
bool floor_reflection_{};
|
||||
bool building_frame_def_{};
|
||||
bool shadow_ortho_{};
|
||||
bool fetched_overlay_node_z_depth_{};
|
||||
bool gyro_broken_{};
|
||||
GraphicsQuality last_frame_def_graphics_quality_{GraphicsQuality::kUnset};
|
||||
std::list<Object::Ref<PythonContextCall>> clean_frame_commands_;
|
||||
std::vector<MeshData*> mesh_data_creates_;
|
||||
std::vector<MeshData*> mesh_data_destroys_;
|
||||
microsecs_t last_create_frame_def_time_microsecs_{};
|
||||
millisecs_t last_create_frame_def_time_millisecs_{};
|
||||
int last_total_frames_rendered_{};
|
||||
int last_fps_{};
|
||||
int progress_bar_loads_{};
|
||||
int frame_def_count_{};
|
||||
int frame_def_count_filtered_{};
|
||||
int next_settings_index_{};
|
||||
TextureQuality texture_quality_placeholder_{};
|
||||
bool drawing_transparent_only_ : 1 {};
|
||||
bool drawing_opaque_only_ : 1 {};
|
||||
bool internal_components_inited_ : 1 {};
|
||||
bool fade_out_ : 1 {true};
|
||||
bool progress_bar_ : 1 {};
|
||||
bool progress_bar_fade_in_ : 1 {};
|
||||
bool debug_draw_ : 1 {};
|
||||
bool network_debug_display_enabled_ : 1 {};
|
||||
bool hardware_cursor_visible_ : 1 {};
|
||||
bool camera_shake_disabled_ : 1 {};
|
||||
bool camera_gyro_explicitly_disabled_ : 1 {};
|
||||
bool gyro_enabled_ : 1 {true};
|
||||
bool show_fps_ : 1 {};
|
||||
bool show_ping_ : 1 {};
|
||||
bool show_net_info_ : 1 {};
|
||||
bool tv_border_ : 1 {};
|
||||
bool floor_reflection_ : 1 {};
|
||||
bool building_frame_def_ : 1 {};
|
||||
bool shadow_ortho_ : 1 {};
|
||||
bool fetched_overlay_node_z_depth_ : 1 {};
|
||||
bool gyro_broken_ : 1 {};
|
||||
bool set_fade_start_on_next_draw_ : 1 {};
|
||||
bool graphics_settings_dirty_ : 1 {true};
|
||||
bool applied_app_config_ : 1 {};
|
||||
bool sent_initial_graphics_settings_ : 1 {};
|
||||
bool got_screen_resolution_ : 1 {};
|
||||
Vector3f shadow_offset_{0.0f, 0.0f, 0.0f};
|
||||
Vector2f shadow_scale_{1.0f, 1.0f};
|
||||
Vector3f tint_{1.0f, 1.0f, 1.0f};
|
||||
Vector3f ambient_color_{1.0f, 1.0f, 1.0f};
|
||||
Vector3f vignette_outer_{0.0f, 0.0f, 0.0f};
|
||||
Vector3f vignette_inner_{1.0f, 1.0f, 1.0f};
|
||||
std::vector<FrameDef*> recycle_frame_defs_;
|
||||
millisecs_t last_jitter_update_time_{};
|
||||
Vector3f jitter_{0.0f, 0.0f, 0.0f};
|
||||
Vector3f accel_smoothed_{0.0f, 0.0f, 0.0f};
|
||||
Vector3f accel_smoothed2_{0.0f, 0.0f, 0.0f};
|
||||
@ -394,8 +446,50 @@ class Graphics {
|
||||
Vector3f tilt_smoothed_{0.0f, 0.0f, 0.0f};
|
||||
Vector3f tilt_vel_{0.0f, 0.0f, 0.0f};
|
||||
Vector3f tilt_pos_{0.0f, 0.0f, 0.0f};
|
||||
Vector3f gyro_vals_{0.0f, 0.0, 0.0f};
|
||||
std::string fps_string_;
|
||||
std::string ping_string_;
|
||||
std::string net_info_string_;
|
||||
std::map<std::string, Object::Ref<NetGraph>> debug_graphs_;
|
||||
std::mutex frame_def_delete_list_mutex_;
|
||||
std::list<Object::Ref<PythonContextCall>> clean_frame_commands_;
|
||||
std::list<ScreenMessageEntry> screen_messages_;
|
||||
std::list<ScreenMessageEntry> screen_messages_top_;
|
||||
std::vector<FrameDef*> recycle_frame_defs_;
|
||||
std::vector<uint16_t> blotch_indices_;
|
||||
std::vector<VertexSprite> blotch_verts_;
|
||||
std::vector<uint16_t> blotch_soft_indices_;
|
||||
std::vector<VertexSprite> blotch_soft_verts_;
|
||||
std::vector<uint16_t> blotch_soft_obj_indices_;
|
||||
std::vector<VertexSprite> blotch_soft_obj_verts_;
|
||||
std::vector<FrameDef*> frame_def_delete_list_;
|
||||
std::vector<MeshData*> mesh_data_creates_;
|
||||
std::vector<MeshData*> mesh_data_destroys_;
|
||||
float fade_{};
|
||||
float res_x_{256.0f};
|
||||
float res_y_{256.0f};
|
||||
float res_x_virtual_{256.0f};
|
||||
float res_y_virtual_{256.0f};
|
||||
float gyro_mag_test_{};
|
||||
float overlay_node_z_depth_{};
|
||||
float progress_bar_progress_{};
|
||||
float screen_gamma_{1.0f};
|
||||
float shadow_lower_bottom_{-4.0f};
|
||||
float shadow_lower_top_{4.0f};
|
||||
float shadow_upper_bottom_{30.0f};
|
||||
float shadow_upper_top_{40.0f};
|
||||
millisecs_t fade_start_{};
|
||||
millisecs_t fade_time_{};
|
||||
millisecs_t next_stat_update_time_{};
|
||||
millisecs_t progress_bar_end_time_{-9999};
|
||||
millisecs_t last_progress_bar_draw_time_{};
|
||||
millisecs_t last_progress_bar_start_time_{};
|
||||
microsecs_t last_suppress_gyro_time_{};
|
||||
seconds_t last_cursor_visibility_event_time_{};
|
||||
microsecs_t next_frame_number_filtered_increment_time_{};
|
||||
microsecs_t last_create_frame_def_time_microsecs_{};
|
||||
millisecs_t last_create_frame_def_time_millisecs_{};
|
||||
millisecs_t last_jitter_update_time_{};
|
||||
Object::Ref<ImageMesh> screen_mesh_;
|
||||
Object::Ref<ImageMesh> progress_bar_bottom_mesh_;
|
||||
Object::Ref<ImageMesh> progress_bar_top_mesh_;
|
||||
@ -406,49 +500,10 @@ class Graphics {
|
||||
Object::Ref<SpriteMesh> shadow_blotch_mesh_;
|
||||
Object::Ref<SpriteMesh> shadow_blotch_soft_mesh_;
|
||||
Object::Ref<SpriteMesh> shadow_blotch_soft_obj_mesh_;
|
||||
std::string fps_string_;
|
||||
std::string ping_string_;
|
||||
std::string net_info_string_;
|
||||
std::vector<uint16_t> blotch_indices_;
|
||||
std::vector<VertexSprite> blotch_verts_;
|
||||
std::vector<uint16_t> blotch_soft_indices_;
|
||||
std::vector<VertexSprite> blotch_soft_verts_;
|
||||
std::vector<uint16_t> blotch_soft_obj_indices_;
|
||||
std::vector<VertexSprite> blotch_soft_obj_verts_;
|
||||
std::map<std::string, Object::Ref<NetGraph>> debug_graphs_;
|
||||
std::mutex frame_def_delete_list_mutex_;
|
||||
std::vector<FrameDef*> frame_def_delete_list_;
|
||||
Object::Ref<Camera> camera_;
|
||||
millisecs_t next_stat_update_time_{};
|
||||
int last_total_frames_rendered_{};
|
||||
int last_fps_{};
|
||||
std::list<ScreenMessageEntry> screen_messages_;
|
||||
std::list<ScreenMessageEntry> screen_messages_top_;
|
||||
bool set_fade_start_on_next_draw_{};
|
||||
millisecs_t fade_start_{};
|
||||
millisecs_t fade_time_{};
|
||||
float fade_{};
|
||||
Vector3f gyro_vals_{0.0f, 0.0, 0.0f};
|
||||
float res_x_{100};
|
||||
float res_y_{100};
|
||||
float res_x_virtual_{100};
|
||||
float res_y_virtual_{100};
|
||||
int progress_bar_loads_{};
|
||||
millisecs_t progress_bar_end_time_{-9999};
|
||||
millisecs_t last_progress_bar_draw_time_{};
|
||||
millisecs_t last_progress_bar_start_time_{};
|
||||
float progress_bar_progress_{};
|
||||
float screen_gamma_{1.0f};
|
||||
float shadow_lower_bottom_{-4.0f};
|
||||
float shadow_lower_top_{4.0f};
|
||||
float shadow_upper_bottom_{30.0f};
|
||||
float shadow_upper_top_{40.0f};
|
||||
millisecs_t last_cursor_visibility_event_time_{};
|
||||
microsecs_t next_frame_number_filtered_increment_time_{};
|
||||
int64_t frame_def_count_{};
|
||||
int64_t frame_def_count_filtered_{};
|
||||
microsecs_t last_suppress_gyro_time_{};
|
||||
Object::Ref<PythonContextCall> fade_end_call_;
|
||||
Object::Ref<Snapshot<GraphicsSettings>> settings_snapshot_;
|
||||
Object::Ref<Snapshot<GraphicsClientContext>> client_context_snapshot_;
|
||||
};
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -32,15 +32,88 @@ void GraphicsServer::EnqueueFrameDef(FrameDef* framedef) {
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsServer::ApplySettings(const GraphicsSettings* settings) {
|
||||
assert(g_base->InGraphicsContext());
|
||||
|
||||
// Only push each unique settings instance through once.
|
||||
if (settings->index == settings_index_) {
|
||||
return;
|
||||
}
|
||||
settings_index_ = settings->index;
|
||||
|
||||
assert(settings->resolution.x >= 0.0f && settings->resolution.y >= 0.0f
|
||||
&& settings->resolution_virtual.x >= 0.0f
|
||||
&& settings->resolution_virtual.y >= 0.0f);
|
||||
|
||||
// Pull a few things out ourself such as screen resolution.
|
||||
tv_border_ = settings->tv_border;
|
||||
if (renderer_) {
|
||||
renderer_->set_pixel_scale(settings->pixel_scale);
|
||||
}
|
||||
// Note: not checking virtual res here; assuming it only changes when
|
||||
// actual res changes.
|
||||
if (res_x_ != settings->resolution.x || res_y_ != settings->resolution.y) {
|
||||
res_x_ = settings->resolution.x;
|
||||
res_y_ = settings->resolution.y;
|
||||
res_x_virtual_ = settings->resolution_virtual.x;
|
||||
res_y_virtual_ = settings->resolution_virtual.y;
|
||||
if (renderer_) {
|
||||
renderer_->OnScreenSizeChange();
|
||||
}
|
||||
}
|
||||
|
||||
// Kick this over to the app-adapter to apply whatever settings they
|
||||
// gathered for themself.
|
||||
g_base->app_adapter->ApplyGraphicsSettings(settings);
|
||||
|
||||
// Lastly, if we've not yet sent a context to the client, do so.
|
||||
if (client_context_ == nullptr) {
|
||||
set_client_context(g_base->app_adapter->GetGraphicsClientContext());
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsServer::set_client_context(GraphicsClientContext* context) {
|
||||
assert(g_base->InGraphicsContext());
|
||||
|
||||
// We have to do a bit of a song and dance with these context pointers.
|
||||
// We wrap the context in an immutable object wrapper which is owned by
|
||||
// the logic thread and that takes care of killing it when no longer
|
||||
// used there, but we also need to keep it alive here in our thread.
|
||||
// (which may not be the logic thread). So to accomplish that, we
|
||||
// immediately ship a refcount increment over to the logic thread, and
|
||||
// once we're done with an obj we ship a decrement.
|
||||
|
||||
auto* old_wrapper = client_context_;
|
||||
auto* new_wrapper =
|
||||
Object::NewDeferred<Snapshot<GraphicsClientContext>>(context);
|
||||
|
||||
client_context_ = new_wrapper;
|
||||
|
||||
g_base->logic->event_loop()->PushCall([old_wrapper, new_wrapper] {
|
||||
// (This has to happen in logic thread).
|
||||
auto ref = Object::CompleteDeferred(new_wrapper);
|
||||
|
||||
// Free the old one which the graphics server doesn't need anymore.
|
||||
if (old_wrapper) {
|
||||
old_wrapper->ObjectDecrementStrongRefCount();
|
||||
}
|
||||
|
||||
// Keep the new one alive for the graphics server.
|
||||
ref->ObjectIncrementStrongRefCount();
|
||||
|
||||
// Plug the new one in for logic to start using.
|
||||
g_base->graphics->set_client_context(new_wrapper);
|
||||
});
|
||||
}
|
||||
|
||||
auto GraphicsServer::TryRender() -> bool {
|
||||
assert(g_base->app_adapter->InGraphicsContext());
|
||||
|
||||
bool success{};
|
||||
|
||||
if (FrameDef* frame_def = WaitForRenderFrameDef_()) {
|
||||
// Apply settings such as tv-mode that were passed along via the
|
||||
// frame-def.
|
||||
ApplyFrameDefSettings(frame_def);
|
||||
// Apply any new graphics settings passed along via the frame-def.
|
||||
ApplySettings(frame_def->settings());
|
||||
|
||||
// Note: we run mesh-updates on each frame-def that comes through even
|
||||
// if we don't actually render the frame.
|
||||
@ -58,6 +131,7 @@ auto GraphicsServer::TryRender() -> bool {
|
||||
// Send this frame_def back to the logic thread for deletion or recycling.
|
||||
g_base->graphics->ReturnCompletedFrameDef(frame_def);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
@ -113,11 +187,6 @@ auto GraphicsServer::WaitForRenderFrameDef_() -> FrameDef* {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GraphicsServer::ApplyFrameDefSettings(FrameDef* frame_def) {
|
||||
assert(g_base->app_adapter->InGraphicsContext());
|
||||
tv_border_ = frame_def->tv_border();
|
||||
}
|
||||
|
||||
// Runs any mesh updates contained in the frame-def.
|
||||
void GraphicsServer::RunFrameDefMeshUpdates(FrameDef* frame_def) {
|
||||
assert(g_base->app_adapter->InGraphicsContext());
|
||||
@ -242,24 +311,6 @@ void GraphicsServer::ReloadLostRenderer() {
|
||||
});
|
||||
}
|
||||
|
||||
void GraphicsServer::SetNullGraphics() {
|
||||
// We don't actually make or update a renderer in headless, but we
|
||||
// still need to set our list of supported textures types/etc. to avoid
|
||||
// complaints.
|
||||
std::list<TextureCompressionType> c_types;
|
||||
SetTextureCompressionTypes(c_types);
|
||||
graphics_quality_requested_ = GraphicsQualityRequest::kLow;
|
||||
graphics_quality_ = GraphicsQuality::kLow;
|
||||
graphics_quality_set_ = true;
|
||||
texture_quality_requested_ = TextureQualityRequest::kLow;
|
||||
texture_quality_ = TextureQuality::kLow;
|
||||
texture_quality_set_ = true;
|
||||
|
||||
// Let the logic thread know screen creation is done (or lack thereof).
|
||||
g_base->logic->event_loop()->PushCall(
|
||||
[] { g_base->logic->OnGraphicsReady(); });
|
||||
}
|
||||
|
||||
void GraphicsServer::set_renderer(Renderer* renderer) {
|
||||
assert(g_base->app_adapter->InGraphicsContext());
|
||||
assert(!renderer_loaded_);
|
||||
@ -279,59 +330,43 @@ void GraphicsServer::LoadRenderer() {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (graphics_quality_requested_) {
|
||||
case GraphicsQualityRequest::kLow:
|
||||
graphics_quality_ = GraphicsQuality::kLow;
|
||||
break;
|
||||
case GraphicsQualityRequest::kMedium:
|
||||
graphics_quality_ = GraphicsQuality::kMedium;
|
||||
break;
|
||||
case GraphicsQualityRequest::kHigh:
|
||||
graphics_quality_ = GraphicsQuality::kHigh;
|
||||
break;
|
||||
case GraphicsQualityRequest::kHigher:
|
||||
graphics_quality_ = GraphicsQuality::kHigher;
|
||||
break;
|
||||
case GraphicsQualityRequest::kAuto:
|
||||
graphics_quality_ = renderer_->GetAutoGraphicsQuality();
|
||||
break;
|
||||
default:
|
||||
Log(LogLevel::kError,
|
||||
"Unhandled GraphicsQualityRequest value: "
|
||||
+ std::to_string(static_cast<int>(graphics_quality_requested_)));
|
||||
graphics_quality_ = GraphicsQuality::kLow;
|
||||
}
|
||||
graphics_quality_ = Graphics::GraphicsQualityFromRequest(
|
||||
graphics_quality_requested_, renderer_->GetAutoGraphicsQuality());
|
||||
|
||||
texture_quality_ = Graphics::TextureQualityFromRequest(
|
||||
texture_quality_requested_, renderer_->GetAutoTextureQuality());
|
||||
|
||||
// If we don't support high quality graphics, make sure we're no higher than
|
||||
// medium.
|
||||
BA_PRECONDITION(g_base->graphics->has_supports_high_quality_graphics_value());
|
||||
if (!g_base->graphics->supports_high_quality_graphics()
|
||||
&& graphics_quality_ > GraphicsQuality::kMedium) {
|
||||
graphics_quality_ = GraphicsQuality::kMedium;
|
||||
}
|
||||
graphics_quality_set_ = true;
|
||||
// BA_PRECONDITION(g_base->graphics->has_supports_high_quality_graphics_value());
|
||||
// if (!g_base->graphics->supports_high_quality_graphics()
|
||||
// && graphics_quality_ > GraphicsQuality::kMedium) {
|
||||
// graphics_quality_ = GraphicsQuality::kMedium;
|
||||
// }
|
||||
// graphics_quality_set_ = true;
|
||||
|
||||
// Update texture quality based on request.
|
||||
switch (texture_quality_requested_) {
|
||||
case TextureQualityRequest::kLow:
|
||||
texture_quality_ = TextureQuality::kLow;
|
||||
break;
|
||||
case TextureQualityRequest::kMedium:
|
||||
texture_quality_ = TextureQuality::kMedium;
|
||||
break;
|
||||
case TextureQualityRequest::kHigh:
|
||||
texture_quality_ = TextureQuality::kHigh;
|
||||
break;
|
||||
case TextureQualityRequest::kAuto:
|
||||
texture_quality_ = renderer_->GetAutoTextureQuality();
|
||||
break;
|
||||
default:
|
||||
Log(LogLevel::kError,
|
||||
"Unhandled TextureQualityRequest value: "
|
||||
+ std::to_string(static_cast<int>(texture_quality_requested_)));
|
||||
texture_quality_ = TextureQuality::kLow;
|
||||
}
|
||||
texture_quality_set_ = true;
|
||||
// switch (texture_quality_requested_) {
|
||||
// case TextureQualityRequest::kLow:
|
||||
// texture_quality_ = TextureQuality::kLow;
|
||||
// break;
|
||||
// case TextureQualityRequest::kMedium:
|
||||
// texture_quality_ = TextureQuality::kMedium;
|
||||
// break;
|
||||
// case TextureQualityRequest::kHigh:
|
||||
// texture_quality_ = TextureQuality::kHigh;
|
||||
// break;
|
||||
// case TextureQualityRequest::kAuto:
|
||||
// texture_quality_ = renderer_->GetAutoTextureQuality();
|
||||
// break;
|
||||
// default:
|
||||
// Log(LogLevel::kError,
|
||||
// "Unhandled TextureQualityRequest value: "
|
||||
// +
|
||||
// std::to_string(static_cast<int>(texture_quality_requested_)));
|
||||
// texture_quality_ = TextureQuality::kLow;
|
||||
// }
|
||||
// texture_quality_set_ = true;
|
||||
|
||||
// Ok we've got our qualities figured out; now load/update the renderer.
|
||||
renderer_->Load();
|
||||
@ -389,56 +424,56 @@ void GraphicsServer::UnloadRenderer() {
|
||||
}
|
||||
|
||||
// Given physical res, calculate virtual res.
|
||||
void GraphicsServer::CalcVirtualRes_(float* x, float* y) {
|
||||
float x_in = *x;
|
||||
float y_in = *y;
|
||||
if (*x / *y > static_cast<float>(kBaseVirtualResX)
|
||||
/ static_cast<float>(kBaseVirtualResY)) {
|
||||
*y = kBaseVirtualResY;
|
||||
*x = *y * (x_in / y_in);
|
||||
} else {
|
||||
*x = kBaseVirtualResX;
|
||||
*y = *x * (y_in / x_in);
|
||||
}
|
||||
}
|
||||
// void GraphicsServer::CalcVirtualRes_(float* x, float* y) {
|
||||
// float x_in = *x;
|
||||
// float y_in = *y;
|
||||
// if (*x / *y > static_cast<float>(kBaseVirtualResX)
|
||||
// / static_cast<float>(kBaseVirtualResY)) {
|
||||
// *y = kBaseVirtualResY;
|
||||
// *x = *y * (x_in / y_in);
|
||||
// } else {
|
||||
// *x = kBaseVirtualResX;
|
||||
// *y = *x * (y_in / x_in);
|
||||
// }
|
||||
// }
|
||||
|
||||
void GraphicsServer::UpdateVirtualScreenRes_() {
|
||||
assert(g_base->app_adapter->InGraphicsContext());
|
||||
// void GraphicsServer::UpdateVirtualScreenRes_() {
|
||||
// assert(g_base->app_adapter->InGraphicsContext());
|
||||
|
||||
// In vr mode our virtual res is independent of our screen size.
|
||||
// (since it gets drawn to an overlay)
|
||||
if (g_core->IsVRMode()) {
|
||||
res_x_virtual_ = kBaseVirtualResX;
|
||||
res_y_virtual_ = kBaseVirtualResY;
|
||||
} else {
|
||||
res_x_virtual_ = res_x_;
|
||||
res_y_virtual_ = res_y_;
|
||||
CalcVirtualRes_(&res_x_virtual_, &res_y_virtual_);
|
||||
}
|
||||
}
|
||||
// // In vr mode our virtual res is independent of our screen size.
|
||||
// // (since it gets drawn to an overlay)
|
||||
// if (g_core->IsVRMode()) {
|
||||
// res_x_virtual_ = kBaseVirtualResX;
|
||||
// res_y_virtual_ = kBaseVirtualResY;
|
||||
// } else {
|
||||
// res_x_virtual_ = res_x_;
|
||||
// res_y_virtual_ = res_y_;
|
||||
// CalcVirtualRes_(&res_x_virtual_, &res_y_virtual_);
|
||||
// }
|
||||
// }
|
||||
|
||||
void GraphicsServer::SetScreenResolution(float h, float v) {
|
||||
assert(g_base->app_adapter->InGraphicsContext());
|
||||
// void GraphicsServer::SetScreenResolution(float h, float v) {
|
||||
// assert(g_base->app_adapter->InGraphicsContext());
|
||||
|
||||
// Ignore redundant sets.
|
||||
if (res_x_ == h && res_y_ == v) {
|
||||
return;
|
||||
}
|
||||
res_x_ = h;
|
||||
res_y_ = v;
|
||||
UpdateVirtualScreenRes_();
|
||||
// // Ignore redundant sets.
|
||||
// if (res_x_ == h && res_y_ == v) {
|
||||
// return;
|
||||
// }
|
||||
// res_x_ = h;
|
||||
// res_y_ = v;
|
||||
// // UpdateVirtualScreenRes_();
|
||||
|
||||
// Inform renderer of the change.
|
||||
if (renderer_) {
|
||||
renderer_->OnScreenSizeChange();
|
||||
}
|
||||
// // Inform renderer of the change.
|
||||
// if (renderer_) {
|
||||
// renderer_->OnScreenSizeChange();
|
||||
// }
|
||||
|
||||
// Inform all logic thread bits of this change.
|
||||
g_base->logic->event_loop()->PushCall(
|
||||
[vx = res_x_virtual_, vy = res_y_virtual_, x = res_x_, y = res_y_] {
|
||||
g_base->graphics->SetScreenSize(vx, vy, x, y);
|
||||
});
|
||||
}
|
||||
// // Inform all logic thread bits of this change.
|
||||
// g_base->logic->event_loop()->PushCall(
|
||||
// [vx = res_x_virtual_, vy = res_y_virtual_, x = res_x_, y = res_y_] {
|
||||
// g_base->graphics->SetScreenSize(vx, vy, x, y);
|
||||
// });
|
||||
// }
|
||||
|
||||
// FIXME: Shouldn't have android-specific code in here.
|
||||
void GraphicsServer::HandlePushAndroidRes(const std::string& android_res) {
|
||||
@ -579,15 +614,15 @@ void GraphicsServer::PushReloadMediaCall() {
|
||||
g_base->app_adapter->PushGraphicsContextCall([this] { ReloadMedia_(); });
|
||||
}
|
||||
|
||||
void GraphicsServer::PushSetScreenPixelScaleCall(float pixel_scale) {
|
||||
g_base->app_adapter->PushGraphicsContextCall([this, pixel_scale] {
|
||||
assert(g_base->app_adapter->InGraphicsContext());
|
||||
if (!renderer_) {
|
||||
return;
|
||||
}
|
||||
renderer_->set_pixel_scale(pixel_scale);
|
||||
});
|
||||
}
|
||||
// void GraphicsServer::PushSetScreenPixelScaleCall(float pixel_scale) {
|
||||
// g_base->app_adapter->PushGraphicsContextCall([this, pixel_scale] {
|
||||
// assert(g_base->app_adapter->InGraphicsContext());
|
||||
// if (!renderer_) {
|
||||
// return;
|
||||
// }
|
||||
// renderer_->set_pixel_scale(pixel_scale);
|
||||
// });
|
||||
// }
|
||||
|
||||
void GraphicsServer::PushComponentUnloadCall(
|
||||
const std::vector<Object::Ref<Asset>*>& components) {
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
|
||||
#include "ballistica/base/base.h"
|
||||
#include "ballistica/shared/foundation/object.h"
|
||||
#include "ballistica/shared/generic/snapshot.h"
|
||||
#include "ballistica/shared/math/matrix44f.h"
|
||||
|
||||
namespace ballistica::base {
|
||||
@ -51,16 +52,8 @@ class GraphicsServer {
|
||||
return renderer_loaded_;
|
||||
}
|
||||
|
||||
/// The AppAdapter should call this to inform the engine of screen size
|
||||
/// changes. Changes will be applied to the server and then sent to the
|
||||
/// logic thread to apply to various app systems (ui, etc.).
|
||||
void SetScreenResolution(float h, float v);
|
||||
void ApplySettings(const GraphicsSettings* settings);
|
||||
|
||||
/// Used by headless builds to init the graphics-server into a
|
||||
/// non-functional state.
|
||||
void SetNullGraphics();
|
||||
|
||||
void PushSetScreenPixelScaleCall(float pixel_scale);
|
||||
void PushReloadMediaCall();
|
||||
void PushRemoveRenderHoldCall();
|
||||
void PushComponentUnloadCall(
|
||||
@ -71,8 +64,6 @@ class GraphicsServer {
|
||||
/// rendering.
|
||||
void EnqueueFrameDef(FrameDef* framedef);
|
||||
|
||||
void ApplyFrameDefSettings(FrameDef* frame_def);
|
||||
|
||||
void RunFrameDefMeshUpdates(FrameDef* frame_def);
|
||||
|
||||
// Renders shadow passes and other common parts of a frame_def.
|
||||
@ -108,9 +99,7 @@ class GraphicsServer {
|
||||
projection_matrix_state_++;
|
||||
}
|
||||
|
||||
auto projection_matrix_state() -> uint32_t {
|
||||
return projection_matrix_state_;
|
||||
}
|
||||
auto projection_matrix_state() { return projection_matrix_state_; }
|
||||
|
||||
void SetLightShadowProjectionMatrix(const Matrix44f& p) {
|
||||
// This will generally get repeatedly set to the same value
|
||||
@ -121,61 +110,57 @@ class GraphicsServer {
|
||||
}
|
||||
}
|
||||
|
||||
auto light_shadow_projection_matrix_state() const -> uint32_t {
|
||||
auto light_shadow_projection_matrix_state() const {
|
||||
return light_shadow_projection_matrix_state_;
|
||||
}
|
||||
|
||||
auto light_shadow_projection_matrix() const -> const Matrix44f& {
|
||||
const auto& light_shadow_projection_matrix() const {
|
||||
return light_shadow_projection_matrix_;
|
||||
}
|
||||
|
||||
// Return the modelview * projection matrix.
|
||||
auto GetModelViewProjectionMatrix() -> const Matrix44f& {
|
||||
const auto& GetModelViewProjectionMatrix() {
|
||||
UpdateModelViewProjectionMatrix_();
|
||||
return model_view_projection_matrix_;
|
||||
}
|
||||
|
||||
auto GetModelViewProjectionMatrixState() -> uint32_t {
|
||||
auto GetModelViewProjectionMatrixState() {
|
||||
UpdateModelViewProjectionMatrix_();
|
||||
return model_view_projection_matrix_state_;
|
||||
}
|
||||
|
||||
auto GetModelWorldMatrix() -> const Matrix44f& {
|
||||
const auto& GetModelWorldMatrix() {
|
||||
UpdateModelWorldMatrix_();
|
||||
return model_world_matrix_;
|
||||
}
|
||||
|
||||
auto GetModelWorldMatrixState() -> uint32_t {
|
||||
auto GetModelWorldMatrixState() {
|
||||
UpdateModelWorldMatrix_();
|
||||
return model_world_matrix_state_;
|
||||
}
|
||||
|
||||
auto cam_pos() -> const Vector3f& { return cam_pos_; }
|
||||
const auto& cam_pos() { return cam_pos_; }
|
||||
|
||||
auto cam_pos_state() -> uint32_t { return cam_pos_state_; }
|
||||
auto cam_pos_state() { return cam_pos_state_; }
|
||||
|
||||
auto GetCamOrientMatrix() -> const Matrix44f& {
|
||||
const auto& GetCamOrientMatrix() {
|
||||
UpdateCamOrientMatrix_();
|
||||
return cam_orient_matrix_;
|
||||
}
|
||||
|
||||
auto GetCamOrientMatrixState() -> uint32_t {
|
||||
auto GetCamOrientMatrixState() {
|
||||
UpdateCamOrientMatrix_();
|
||||
return cam_orient_matrix_state_;
|
||||
}
|
||||
|
||||
auto model_view_matrix() const -> const Matrix44f& {
|
||||
return model_view_matrix_;
|
||||
}
|
||||
const auto& model_view_matrix() const { return model_view_matrix_; }
|
||||
|
||||
void SetModelViewMatrix(const Matrix44f& m) {
|
||||
model_view_matrix_ = m;
|
||||
model_view_projection_matrix_dirty_ = model_world_matrix_dirty_ = true;
|
||||
}
|
||||
|
||||
auto projection_matrix() const -> const Matrix44f& {
|
||||
return projection_matrix_;
|
||||
}
|
||||
const auto& projection_matrix() const { return projection_matrix_; }
|
||||
|
||||
void PushTransform() {
|
||||
model_view_stack_.push_back(model_view_matrix_);
|
||||
@ -209,32 +194,34 @@ class GraphicsServer {
|
||||
model_view_projection_matrix_dirty_ = model_world_matrix_dirty_ = true;
|
||||
}
|
||||
|
||||
auto quality() const -> GraphicsQuality {
|
||||
assert(graphics_quality_set_);
|
||||
auto quality() const {
|
||||
assert(InGraphicsContext_());
|
||||
assert(graphics_quality_ != GraphicsQuality::kUnset);
|
||||
return graphics_quality_;
|
||||
}
|
||||
|
||||
auto texture_quality() const -> TextureQuality {
|
||||
assert(texture_quality_set_);
|
||||
auto texture_quality() const {
|
||||
assert(InGraphicsContext_());
|
||||
assert(texture_quality_ != TextureQuality::kUnset);
|
||||
return texture_quality_;
|
||||
}
|
||||
|
||||
auto screen_pixel_width() const -> float {
|
||||
auto screen_pixel_width() const {
|
||||
assert(InGraphicsContext_());
|
||||
return res_x_;
|
||||
}
|
||||
|
||||
auto screen_pixel_height() const -> float {
|
||||
auto screen_pixel_height() const {
|
||||
assert(InGraphicsContext_());
|
||||
return res_y_;
|
||||
}
|
||||
|
||||
auto screen_virtual_width() const -> float {
|
||||
auto screen_virtual_width() const {
|
||||
assert(InGraphicsContext_());
|
||||
return res_x_virtual_;
|
||||
}
|
||||
|
||||
auto screen_virtual_height() const -> float {
|
||||
auto screen_virtual_height() const {
|
||||
assert(InGraphicsContext_());
|
||||
return res_y_virtual_;
|
||||
}
|
||||
@ -244,45 +231,69 @@ class GraphicsServer {
|
||||
return tv_border_;
|
||||
}
|
||||
|
||||
auto graphics_quality_set() const { return graphics_quality_set_; }
|
||||
// auto graphics_quality_set() const {
|
||||
// return graphics_quality_ != GraphicsQuality::kUnset;
|
||||
// }
|
||||
|
||||
auto texture_quality_set() const { return texture_quality_set_; }
|
||||
// auto texture_quality_set() const {
|
||||
// return texture_quality_ != TextureQuality::kUnset;
|
||||
// }
|
||||
|
||||
auto SupportsTextureCompressionType(TextureCompressionType t) const -> bool {
|
||||
assert(InGraphicsContext_());
|
||||
assert(texture_compression_types_set_);
|
||||
return ((texture_compression_types_ & (0x01u << static_cast<uint32_t>(t)))
|
||||
!= 0u);
|
||||
}
|
||||
|
||||
void SetTextureCompressionTypes(
|
||||
const std::list<TextureCompressionType>& types);
|
||||
|
||||
auto texture_compression_types_are_set() const {
|
||||
return texture_compression_types_set_;
|
||||
}
|
||||
// auto texture_compression_types_are_set() const {
|
||||
// return texture_compression_types_set_;
|
||||
// }
|
||||
|
||||
void set_renderer_context_lost(bool lost) { renderer_context_lost_ = lost; }
|
||||
|
||||
auto renderer_context_lost() const { return renderer_context_lost_; }
|
||||
|
||||
auto graphics_quality_requested() const {
|
||||
assert(InGraphicsContext_());
|
||||
return graphics_quality_requested_;
|
||||
}
|
||||
|
||||
void set_graphics_quality_requested(GraphicsQualityRequest val) {
|
||||
assert(InGraphicsContext_());
|
||||
graphics_quality_requested_ = val;
|
||||
}
|
||||
|
||||
void set_texture_quality_requested(TextureQualityRequest val) {
|
||||
assert(InGraphicsContext_());
|
||||
texture_quality_requested_ = val;
|
||||
}
|
||||
|
||||
auto graphics_quality() const { return graphics_quality_; }
|
||||
auto graphics_quality() const {
|
||||
assert(InGraphicsContext_());
|
||||
return graphics_quality_;
|
||||
}
|
||||
|
||||
auto texture_quality_requested() const { return texture_quality_requested_; }
|
||||
auto texture_quality_requested() const {
|
||||
assert(InGraphicsContext_());
|
||||
return texture_quality_requested_;
|
||||
}
|
||||
|
||||
void HandlePushAndroidRes(const std::string& android_res);
|
||||
|
||||
auto texture_compression_types() const {
|
||||
assert(texture_compression_types_set_);
|
||||
return texture_compression_types_;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Pass a freshly allocated GraphicsContext instance, which the graphics
|
||||
/// system will take ownership of.
|
||||
void set_client_context(GraphicsClientContext* context);
|
||||
|
||||
// So we don't have to include app_adapter.h here for asserts.
|
||||
auto InGraphicsContext_() const -> bool;
|
||||
|
||||
@ -293,8 +304,8 @@ class GraphicsServer {
|
||||
auto WaitForRenderFrameDef_() -> FrameDef*;
|
||||
|
||||
// Update virtual screen dimensions based on the current physical ones.
|
||||
static void CalcVirtualRes_(float* x, float* y);
|
||||
void UpdateVirtualScreenRes_();
|
||||
// static void CalcVirtualRes_(float* x, float* y);
|
||||
// void UpdateVirtualScreenRes_();
|
||||
void UpdateCamOrientMatrix_();
|
||||
void ReloadMedia_();
|
||||
void UpdateModelViewProjectionMatrix_() {
|
||||
@ -314,23 +325,17 @@ class GraphicsServer {
|
||||
}
|
||||
|
||||
bool renderer_loaded_ : 1 {};
|
||||
bool v_sync_ : 1 {};
|
||||
bool auto_vsync_ : 1 {};
|
||||
bool model_view_projection_matrix_dirty_ : 1 {true};
|
||||
bool model_world_matrix_dirty_ : 1 {true};
|
||||
bool graphics_quality_set_ : 1 {};
|
||||
bool texture_quality_set_ : 1 {};
|
||||
bool tv_border_ : 1 {};
|
||||
bool renderer_context_lost_ : 1 {};
|
||||
bool texture_compression_types_set_ : 1 {};
|
||||
bool cam_orient_matrix_dirty_ : 1 {true};
|
||||
TextureQualityRequest texture_quality_requested_{
|
||||
TextureQualityRequest::kUnset};
|
||||
TextureQuality texture_quality_{TextureQuality::kLow};
|
||||
GraphicsQualityRequest graphics_quality_requested_{
|
||||
GraphicsQualityRequest::kUnset};
|
||||
GraphicsQuality graphics_quality_{GraphicsQuality::kUnset};
|
||||
int render_hold_{};
|
||||
Snapshot<GraphicsClientContext>* client_context_{};
|
||||
TextureQualityRequest texture_quality_requested_{};
|
||||
TextureQuality texture_quality_{};
|
||||
GraphicsQualityRequest graphics_quality_requested_{};
|
||||
GraphicsQuality graphics_quality_{};
|
||||
float res_x_{};
|
||||
float res_y_{};
|
||||
float res_x_virtual_{};
|
||||
@ -340,20 +345,21 @@ class GraphicsServer {
|
||||
Matrix44f projection_matrix_{kMatrix44fIdentity};
|
||||
Matrix44f model_view_projection_matrix_{kMatrix44fIdentity};
|
||||
Matrix44f model_world_matrix_{kMatrix44fIdentity};
|
||||
std::vector<Matrix44f> model_view_stack_;
|
||||
uint32_t texture_compression_types_{};
|
||||
uint32_t projection_matrix_state_{1};
|
||||
uint32_t model_view_projection_matrix_state_{1};
|
||||
uint32_t model_world_matrix_state_{1};
|
||||
uint32_t light_shadow_projection_matrix_state_{1};
|
||||
uint32_t cam_pos_state_{1};
|
||||
uint32_t cam_orient_matrix_state_{1};
|
||||
int render_hold_{};
|
||||
int projection_matrix_state_{};
|
||||
int model_view_projection_matrix_state_{};
|
||||
int model_world_matrix_state_{};
|
||||
int light_shadow_projection_matrix_state_{};
|
||||
int cam_pos_state_{};
|
||||
int cam_orient_matrix_state_{};
|
||||
int settings_index_{-1};
|
||||
Vector3f cam_pos_{0.0f, 0.0f, 0.0f};
|
||||
Vector3f cam_target_{0.0f, 0.0f, 0.0f};
|
||||
Matrix44f light_shadow_projection_matrix_{kMatrix44fIdentity};
|
||||
Matrix44f cam_orient_matrix_ = kMatrix44fIdentity;
|
||||
std::vector<Matrix44f> model_view_stack_;
|
||||
std::list<MeshData*> mesh_datas_;
|
||||
Timer* render_timer_{};
|
||||
Renderer* renderer_{};
|
||||
FrameDef* frame_def_{};
|
||||
std::mutex frame_def_mutex_{};
|
||||
|
||||
@ -487,7 +487,7 @@ void RenderPass::SetFrustum(float near_val, float far_val) {
|
||||
g_base->graphics_server->SetProjectionMatrix(projection_matrix_);
|
||||
}
|
||||
|
||||
void RenderPass::Finalize() {
|
||||
void RenderPass::Complete() {
|
||||
if (UsesWorldLists()) {
|
||||
for (auto& command : commands_) {
|
||||
command->Finalize();
|
||||
|
||||
@ -112,7 +112,7 @@ class RenderPass {
|
||||
return model_view_projection_matrix_;
|
||||
}
|
||||
auto HasDrawCommands() const -> bool;
|
||||
void Finalize();
|
||||
void Complete();
|
||||
void Reset();
|
||||
|
||||
// Whether this pass draws stuff from the per-shader command lists
|
||||
|
||||
@ -35,10 +35,12 @@ void Renderer::PreprocessFrameDef(FrameDef* frame_def) {
|
||||
|
||||
// If this frame_def was made in a different quality mode than we're
|
||||
// currently in, don't attempt to render it.
|
||||
if (frame_def->quality() != g_base->graphics_server->quality()) {
|
||||
frame_def->set_rendering(false);
|
||||
return;
|
||||
}
|
||||
// UPDATE - scratch that; we now set our quality FROM the frame def.
|
||||
// if (frame_def->quality() != g_base->graphics_server->quality()) {
|
||||
// frame_def->set_rendering(false);
|
||||
// return;
|
||||
// }
|
||||
|
||||
frame_def->set_rendering(true);
|
||||
|
||||
// Some VR environments muck with render states before/after
|
||||
|
||||
@ -82,7 +82,6 @@ class Renderer {
|
||||
auto light_pitch() const -> float { return light_pitch_; }
|
||||
auto light_heading() const -> float { return light_heading_; }
|
||||
void set_pixel_scale(float s) { pixel_scale_requested_ = s; }
|
||||
// void set_screen_gamma(float val) { screen_gamma_requested_ = val; }
|
||||
void set_debug_draw_mode(bool debugModeIn) { debug_draw_mode_ = debugModeIn; }
|
||||
auto debug_draw_mode() -> bool { return debug_draw_mode_; }
|
||||
|
||||
@ -269,7 +268,6 @@ class Renderer {
|
||||
Vector3f vignette_outer_{0.0f, 0.0f, 0.0f};
|
||||
Vector3f vignette_inner_{1.0f, 1.0f, 1.0f};
|
||||
int shadow_res_{-1};
|
||||
// float screen_gamma_requested_{1.0f};
|
||||
float screen_gamma_{1.0f};
|
||||
float pixel_scale_requested_{1.0f};
|
||||
float pixel_scale_{1.0f};
|
||||
|
||||
@ -1013,7 +1013,7 @@ void Camera::ApplyToFrameDef(FrameDef* frame_def) {
|
||||
up_, 4, 1000.0f,
|
||||
-1.0f, // Auto x fov.
|
||||
final_fov_y
|
||||
* (g_base->graphics->tv_border() ? (1.0f + kTVBorder) : 1.0f),
|
||||
* (frame_def->settings()->tv_border ? (1.0f + kTVBorder) : 1.0f),
|
||||
false, 0, 0, 0, 0, // Not using tangent fovs.
|
||||
area_of_interest_points_);
|
||||
}
|
||||
|
||||
@ -49,6 +49,13 @@ auto FrameDef::GetOverlayFlatPass() -> RenderPass* {
|
||||
|
||||
void FrameDef::Reset() {
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
// Update & grab the current settings.
|
||||
settings_snapshot_ = g_base->graphics->GetGraphicsSettingsSnapshot();
|
||||
|
||||
auto* settings = settings_snapshot_->Get();
|
||||
auto* client_context = g_base->graphics->client_context();
|
||||
|
||||
app_time_microsecs_ = 0;
|
||||
display_time_microsecs_ = 0;
|
||||
display_time_elapsed_microsecs_ = 0;
|
||||
@ -68,11 +75,17 @@ void FrameDef::Reset() {
|
||||
mesh_index_sizes_.clear();
|
||||
mesh_buffers_.clear();
|
||||
|
||||
quality_ = g_base->graphics_server->quality();
|
||||
quality_ = Graphics::GraphicsQualityFromRequest(
|
||||
settings->graphics_quality, client_context->auto_graphics_quality);
|
||||
|
||||
assert(g_base->graphics->has_supports_high_quality_graphics_value());
|
||||
texture_quality_ = Graphics::TextureQualityFromRequest(
|
||||
settings->texture_quality, client_context->auto_texture_quality);
|
||||
|
||||
// pixel_scale_ = g_base->graphics->settings()->pixel_scale;
|
||||
|
||||
// assert(g_base->graphics->has_supports_high_quality_graphics_value());
|
||||
orbiting_ = (g_base->graphics->camera()->mode() == CameraMode::kOrbit);
|
||||
tv_border_ = g_base->graphics->tv_border();
|
||||
// tv_border_ = g_base->graphics->tv_border();
|
||||
|
||||
shadow_offset_ = g_base->graphics->shadow_offset();
|
||||
shadow_scale_ = g_base->graphics->shadow_scale();
|
||||
@ -99,21 +112,21 @@ void FrameDef::Reset() {
|
||||
beauty_pass_->set_floor_reflection(g_base->graphics->floor_reflection());
|
||||
}
|
||||
|
||||
void FrameDef::Finalize() {
|
||||
void FrameDef::Complete() {
|
||||
assert(!defining_component_);
|
||||
light_pass_->Finalize();
|
||||
light_shadow_pass_->Finalize();
|
||||
beauty_pass_->Finalize();
|
||||
beauty_pass_bg_->Finalize();
|
||||
overlay_pass_->Finalize();
|
||||
overlay_front_pass_->Finalize();
|
||||
light_pass_->Complete();
|
||||
light_shadow_pass_->Complete();
|
||||
beauty_pass_->Complete();
|
||||
beauty_pass_bg_->Complete();
|
||||
overlay_pass_->Complete();
|
||||
overlay_front_pass_->Complete();
|
||||
if (g_core->IsVRMode()) {
|
||||
overlay_fixed_pass_->Finalize();
|
||||
overlay_flat_pass_->Finalize();
|
||||
vr_cover_pass_->Finalize();
|
||||
overlay_fixed_pass_->Complete();
|
||||
overlay_flat_pass_->Complete();
|
||||
vr_cover_pass_->Complete();
|
||||
}
|
||||
overlay_3d_pass_->Finalize();
|
||||
blit_pass_->Finalize();
|
||||
overlay_3d_pass_->Complete();
|
||||
blit_pass_->Complete();
|
||||
}
|
||||
|
||||
void FrameDef::AddMesh(Mesh* mesh) {
|
||||
|
||||
@ -7,13 +7,14 @@
|
||||
#include <vector>
|
||||
|
||||
#include "ballistica/base/assets/asset.h"
|
||||
#include "ballistica/shared/generic/snapshot.h"
|
||||
#include "ballistica/shared/math/matrix44f.h"
|
||||
#include "ballistica/shared/math/vector2f.h"
|
||||
|
||||
namespace ballistica::base {
|
||||
|
||||
/// A flattened representation of a frame; generated by the logic thread and
|
||||
/// sent to the graphics thread to render.
|
||||
/// sent to the graphics server to render.
|
||||
class FrameDef {
|
||||
public:
|
||||
auto light_pass() -> RenderPass* { return light_pass_.get(); }
|
||||
@ -43,21 +44,21 @@ class FrameDef {
|
||||
return app_time_microsecs_ / 1000;
|
||||
}
|
||||
auto app_time_microsecs() const -> microsecs_t { return app_time_microsecs_; }
|
||||
auto app_time() const -> double {
|
||||
return static_cast<double>(app_time_microsecs_) / 1000000.0;
|
||||
auto app_time() const {
|
||||
return static_cast<seconds_t>(app_time_microsecs_) / 1000000.0;
|
||||
}
|
||||
|
||||
// A number incremented for each frame renderered. Note that graphics code
|
||||
// should not plug this directly into things like flash calculations since
|
||||
// frame-rates will vary a lot these days. A 30hz flash will look a lot
|
||||
// frame-rates vary a lot these days. A 30hz flash will look a lot
|
||||
// different than a 240hz flash. Use frame_number_filtered() for such
|
||||
// purposes.
|
||||
auto frame_number() const { return frame_number_; }
|
||||
|
||||
// A number incremented for each frame rendered, but a maximum of 60 times
|
||||
// per second. Code for drawing flashes or other exact effects should use
|
||||
// this value instead of regular frame_number so that things don't turn
|
||||
// muddy at extremely high frame rates.
|
||||
// per second. Code for drawing flashes or other crisp blink-y effects
|
||||
// should use this value instead of regular frame_number so that things
|
||||
// don't turn muddy at extremely high frame rates.
|
||||
auto frame_number_filtered() const { return frame_number_filtered_; }
|
||||
|
||||
// Returns the display-time this frame-def was created at (tries to match
|
||||
@ -66,14 +67,19 @@ class FrameDef {
|
||||
auto display_time_millisecs() const -> millisecs_t {
|
||||
return display_time_microsecs_ / 1000;
|
||||
}
|
||||
|
||||
auto display_time_microsecs() const -> microsecs_t {
|
||||
return display_time_microsecs_;
|
||||
}
|
||||
auto display_time() const -> double {
|
||||
return static_cast<double>(display_time_microsecs_) / 1000000.0;
|
||||
|
||||
auto display_time() const -> seconds_t {
|
||||
return static_cast<seconds_t>(display_time_microsecs_) / 1000000.0;
|
||||
}
|
||||
|
||||
auto display_time_elapsed() const -> seconds_t {
|
||||
return static_cast<seconds_t>(display_time_elapsed_microsecs_) / 1000000.0;
|
||||
}
|
||||
|
||||
// How much display time does this frame-def represent.
|
||||
auto display_time_elapsed_millisecs() const -> millisecs_t {
|
||||
return display_time_elapsed_millisecs_;
|
||||
}
|
||||
@ -82,7 +88,9 @@ class FrameDef {
|
||||
return display_time_elapsed_microsecs_;
|
||||
}
|
||||
|
||||
auto quality() const -> GraphicsQuality { return quality_; }
|
||||
auto quality() const { return quality_; }
|
||||
auto texture_quality() const { return texture_quality_; }
|
||||
|
||||
auto orbiting() const -> bool { return orbiting_; }
|
||||
auto shadow_offset() const -> const Vector3f& { return shadow_offset_; }
|
||||
auto shadow_scale() const -> const Vector2f& { return shadow_scale_; }
|
||||
@ -115,11 +123,12 @@ class FrameDef {
|
||||
vr_overlay_screen_matrix_fixed_ = mat;
|
||||
}
|
||||
|
||||
// Effects requiring availability of a depth texture should
|
||||
// check this to determine whether they should draw.
|
||||
auto has_depth_texture() const -> bool {
|
||||
// Effects requiring availability of a depth texture should check this to
|
||||
// determine whether they should draw.
|
||||
auto HasDepthTexture() const -> bool {
|
||||
return (quality_ >= GraphicsQuality::kHigh);
|
||||
}
|
||||
|
||||
void AddComponent(const Object::Ref<Asset>& component) {
|
||||
// Add a reference to this component only if we havn't yet.
|
||||
if (component->last_frame_def_num() != frame_number_) {
|
||||
@ -134,7 +143,7 @@ class FrameDef {
|
||||
FrameDef();
|
||||
~FrameDef();
|
||||
void Reset();
|
||||
void Finalize();
|
||||
void Complete();
|
||||
|
||||
void set_display_time_elapsed_microsecs(microsecs_t val) {
|
||||
display_time_elapsed_microsecs_ = val;
|
||||
@ -187,7 +196,7 @@ class FrameDef {
|
||||
auto media_components() const -> const std::vector<Object::Ref<Asset>>& {
|
||||
return media_components_;
|
||||
}
|
||||
auto tv_border() const { return tv_border_; }
|
||||
// auto tv_border() const { return tv_border_; }
|
||||
|
||||
void set_camera_mode(CameraMode val) { camera_mode_ = val; }
|
||||
void set_rendering(bool val) { rendering_ = val; }
|
||||
@ -204,18 +213,27 @@ class FrameDef {
|
||||
}
|
||||
#endif
|
||||
|
||||
// auto pixel_scale() const { return pixel_scale_; }
|
||||
|
||||
auto* settings() const {
|
||||
assert(settings_snapshot_.Exists());
|
||||
return settings_snapshot_->Get();
|
||||
}
|
||||
|
||||
private:
|
||||
bool needs_clear_{};
|
||||
bool rendering_{};
|
||||
bool orbiting_{};
|
||||
bool tv_border_{};
|
||||
bool shadow_ortho_{};
|
||||
Object::Ref<Snapshot<GraphicsSettings>> settings_snapshot_;
|
||||
bool needs_clear_ : 1 {};
|
||||
bool rendering_ : 1 {};
|
||||
bool orbiting_ : 1 {};
|
||||
// bool tv_border_ : 1 {};
|
||||
bool shadow_ortho_ : 1 {};
|
||||
BenchmarkType benchmark_type_{BenchmarkType::kNone};
|
||||
CameraMode camera_mode_{CameraMode::kFollow};
|
||||
Vector3f cam_original_{0.0f, 0.0f, 0.0f};
|
||||
Vector3f cam_target_original_{0.0f, 0.0f, 0.0f};
|
||||
Vector3f shake_original_{0.0f, 0.0f, 0.0f};
|
||||
float vr_near_clip_{};
|
||||
// float pixel_scale_{};
|
||||
Matrix44f vr_overlay_screen_matrix_ = kMatrix44fIdentity;
|
||||
Matrix44f vr_overlay_screen_matrix_fixed_ = kMatrix44fIdentity;
|
||||
std::vector<MeshData*> mesh_data_creates_;
|
||||
@ -245,7 +263,8 @@ class FrameDef {
|
||||
std::unique_ptr<RenderPass> vr_cover_pass_;
|
||||
std::unique_ptr<RenderPass> overlay_3d_pass_;
|
||||
std::unique_ptr<RenderPass> blit_pass_;
|
||||
GraphicsQuality quality_{GraphicsQuality::kLow};
|
||||
GraphicsQuality quality_{};
|
||||
TextureQuality texture_quality_{};
|
||||
microsecs_t app_time_microsecs_{};
|
||||
microsecs_t display_time_microsecs_{};
|
||||
microsecs_t display_time_elapsed_microsecs_{};
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
// Released under the MIT License. See LICENSE for details.
|
||||
|
||||
#include "ballistica/base/graphics/support/graphics_client_context.h"
|
||||
|
||||
#include "ballistica/base/graphics/graphics_server.h"
|
||||
#include "ballistica/base/graphics/renderer/renderer.h"
|
||||
|
||||
namespace ballistica::base {
|
||||
|
||||
GraphicsClientContext::GraphicsClientContext()
|
||||
: auto_graphics_quality{g_base->graphics_server->renderer()
|
||||
->GetAutoGraphicsQuality()},
|
||||
auto_texture_quality{
|
||||
g_base->graphics_server->renderer()->GetAutoTextureQuality()},
|
||||
texture_compression_types{
|
||||
g_base->graphics_server->texture_compression_types()} {}
|
||||
|
||||
GraphicsClientContext::GraphicsClientContext(int dummy)
|
||||
: auto_graphics_quality{GraphicsQuality::kLow},
|
||||
auto_texture_quality{TextureQuality::kLow},
|
||||
texture_compression_types{0} {}
|
||||
|
||||
} // namespace ballistica::base
|
||||
@ -0,0 +1,31 @@
|
||||
// Released under the MIT License. See LICENSE for details.
|
||||
|
||||
#ifndef BALLISTICA_BASE_GRAPHICS_SUPPORT_GRAPHICS_CLIENT_CONTEXT_H_
|
||||
#define BALLISTICA_BASE_GRAPHICS_SUPPORT_GRAPHICS_CLIENT_CONTEXT_H_
|
||||
|
||||
#include "ballistica/base/base.h"
|
||||
|
||||
namespace ballistica::base {
|
||||
|
||||
/// Represents a valid graphics setup delivered by the graphics server to
|
||||
/// the logic thread. It contains various info about concrete graphics
|
||||
/// settings and capabilities.
|
||||
struct GraphicsClientContext {
|
||||
GraphicsClientContext();
|
||||
|
||||
/// Special constructor to create a dummy context (used by headless builds).
|
||||
explicit GraphicsClientContext(int dummy);
|
||||
|
||||
auto SupportsTextureCompressionType(TextureCompressionType t) const -> bool {
|
||||
return ((texture_compression_types & (0x01u << static_cast<uint32_t>(t)))
|
||||
!= 0u);
|
||||
}
|
||||
|
||||
GraphicsQuality auto_graphics_quality;
|
||||
TextureQuality auto_texture_quality;
|
||||
uint32_t texture_compression_types;
|
||||
};
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
#endif // BALLISTICA_BASE_GRAPHICS_SUPPORT_GRAPHICS_CLIENT_CONTEXT_H_
|
||||
27
src/ballistica/base/graphics/support/graphics_settings.cc
Normal file
27
src/ballistica/base/graphics/support/graphics_settings.cc
Normal file
@ -0,0 +1,27 @@
|
||||
// Released under the MIT License. See LICENSE for details.
|
||||
|
||||
#include "ballistica/base/graphics/support/graphics_settings.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "ballistica/base/graphics/graphics.h"
|
||||
#include "ballistica/base/support/app_config.h"
|
||||
#include "ballistica/shared/foundation/object.h"
|
||||
|
||||
namespace ballistica::base {
|
||||
|
||||
GraphicsSettings::GraphicsSettings()
|
||||
|
||||
: resolution{g_base->graphics->screen_pixel_width(),
|
||||
g_base->graphics->screen_pixel_height()},
|
||||
resolution_virtual{g_base->graphics->screen_virtual_width(),
|
||||
g_base->graphics->screen_virtual_height()},
|
||||
pixel_scale{std::clamp(
|
||||
g_base->app_config->Resolve(AppConfig::FloatID::kScreenPixelScale),
|
||||
0.1f, 1.0f)},
|
||||
graphics_quality{g_base->graphics->GraphicsQualityFromAppConfig()},
|
||||
texture_quality{g_base->graphics->TextureQualityFromAppConfig()},
|
||||
tv_border{
|
||||
g_base->app_config->Resolve(AppConfig::BoolID::kEnableTVBorder)} {}
|
||||
|
||||
} // namespace ballistica::base
|
||||
33
src/ballistica/base/graphics/support/graphics_settings.h
Normal file
33
src/ballistica/base/graphics/support/graphics_settings.h
Normal file
@ -0,0 +1,33 @@
|
||||
// Released under the MIT License. See LICENSE for details.
|
||||
|
||||
#ifndef BALLISTICA_BASE_GRAPHICS_SUPPORT_GRAPHICS_SETTINGS_H_
|
||||
#define BALLISTICA_BASE_GRAPHICS_SUPPORT_GRAPHICS_SETTINGS_H_
|
||||
|
||||
#include "ballistica/base/base.h"
|
||||
#include "ballistica/shared/math/vector2f.h"
|
||||
|
||||
namespace ballistica::base {
|
||||
|
||||
/// A set of settings for graphics, covering things like screen
|
||||
/// resolution, texture quality, etc. These are filled out by the
|
||||
/// AppAdapter in the logic thread and passed up to the GraphicsServer
|
||||
/// either through standalone calls or attached to a FrameDef. Generally
|
||||
/// AppAdapters define their own subclass of this containing additional
|
||||
/// settings specific to themselves or the renderer(s) they use.
|
||||
struct GraphicsSettings {
|
||||
GraphicsSettings();
|
||||
// Each new settings instance will be assigned a unique incrementing index.
|
||||
int index{-1};
|
||||
|
||||
// Some standard settings used by most renderers.
|
||||
Vector2f resolution;
|
||||
Vector2f resolution_virtual;
|
||||
float pixel_scale;
|
||||
GraphicsQualityRequest graphics_quality;
|
||||
TextureQualityRequest texture_quality;
|
||||
bool tv_border;
|
||||
};
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
#endif // BALLISTICA_BASE_GRAPHICS_SUPPORT_GRAPHICS_SETTINGS_H_
|
||||
@ -15,6 +15,7 @@
|
||||
#include "ballistica/base/support/app_config.h"
|
||||
#include "ballistica/base/ui/dev_console.h"
|
||||
#include "ballistica/base/ui/ui.h"
|
||||
#include "ballistica/shared/buildconfig/buildconfig_common.h"
|
||||
#include "ballistica/shared/foundation/event_loop.h"
|
||||
#include "ballistica/shared/generic/utils.h"
|
||||
|
||||
@ -158,16 +159,16 @@ void Input::AnnounceConnects_() {
|
||||
|
||||
// For the first announcement just say "X controllers detected" and don't
|
||||
// have a sound.
|
||||
if (first_print && g_core->GetAppTimeMillisecs() < 10000) {
|
||||
if (first_print && g_core->GetAppTimeSeconds() < 5.0) {
|
||||
first_print = false;
|
||||
|
||||
// Disabling this completely for now; being more lenient with devices
|
||||
// allowed on Android means this will often come back with large
|
||||
// numbers.
|
||||
bool do_print{false};
|
||||
// Disabling this completely on Android for now; we often get large
|
||||
// numbers of devices there that aren't actually devices.
|
||||
|
||||
bool do_print_initial_counts{!g_buildconfig.ostype_android()};
|
||||
|
||||
// If there's been several connected, just give a number.
|
||||
if (explicit_bool(do_print)) {
|
||||
if (explicit_bool(do_print_initial_counts)) {
|
||||
if (newly_connected_controllers_.size() > 1) {
|
||||
std::string s =
|
||||
g_base->assets->GetResourceString("controllersDetectedText");
|
||||
@ -532,23 +533,28 @@ void Input::UpdateEnabledControllerSubsystems_() {
|
||||
|
||||
// First off, on mac, let's update whether we want to completely ignore
|
||||
// either the classic or the iOS/Mac controller subsystems.
|
||||
if (g_buildconfig.ostype_macos()) {
|
||||
std::string sys = g_base->app_config->Resolve(
|
||||
AppConfig::StringID::kMacControllerSubsystem);
|
||||
if (sys == "Classic") {
|
||||
ignore_mfi_controllers_ = true;
|
||||
ignore_sdl_controllers_ = false;
|
||||
} else if (sys == "MFi") {
|
||||
ignore_mfi_controllers_ = false;
|
||||
ignore_sdl_controllers_ = true;
|
||||
} else if (sys == "Both") {
|
||||
ignore_mfi_controllers_ = false;
|
||||
ignore_sdl_controllers_ = false;
|
||||
} else {
|
||||
BA_LOG_ONCE(LogLevel::kError,
|
||||
"Invalid mac-controller-subsystem value: '" + sys + "'");
|
||||
}
|
||||
}
|
||||
//
|
||||
// UPDATE - these days we're mfi-only on our xcode builds (which should
|
||||
// support older controllers too). So we don't need to touch ignore vals
|
||||
// anywhere since we'll not get sdl ones on those builds.
|
||||
|
||||
// if (g_buildconfig.ostype_macos()) {
|
||||
// std::string sys = g_base->app_config->Resolve(
|
||||
// AppConfig::StringID::kMacControllerSubsystem);
|
||||
// if (sys == "Classic") {
|
||||
// ignore_mfi_controllers_ = true;
|
||||
// ignore_sdl_controllers_ = false;
|
||||
// } else if (sys == "MFi") {
|
||||
// ignore_mfi_controllers_ = false;
|
||||
// ignore_sdl_controllers_ = true;
|
||||
// } else if (sys == "Both") {
|
||||
// ignore_mfi_controllers_ = false;
|
||||
// ignore_sdl_controllers_ = false;
|
||||
// } else {
|
||||
// BA_LOG_ONCE(LogLevel::kError,
|
||||
// "Invalid mac-controller-subsystem value: '" + sys + "'");
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
void Input::OnAppStart() { assert(g_base->InLogicThread()); }
|
||||
@ -809,10 +815,35 @@ void Input::PushTextInputEvent(const std::string& text) {
|
||||
g_base->logic->event_loop()->PushCall([this, text] {
|
||||
MarkInputActive();
|
||||
|
||||
// Ignore if input is locked.
|
||||
// If if the app doesn't want direct text input right now.
|
||||
if (!g_base->app_adapter->HasDirectKeyboardInput()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore if input is locked.
|
||||
if (IsInputLocked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We try to handle char filtering here (to keep it consistent across
|
||||
// platforms) but make a stink if they sent us something that we can't
|
||||
// at least translate to unicode.
|
||||
if (!Utils::IsValidUTF8(text)) {
|
||||
Log(LogLevel::kWarning, "PushTextInputEvent passed invalid utf-8 text.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Now scan through unicode vals and ignore stuff like tabs and newlines
|
||||
// and backspaces. We want to limit this mechanism to direct simple
|
||||
// lines of text. Anything needing something fancier should go through a
|
||||
// proper OS-managed text input dialog or whatnot.
|
||||
auto univals = Utils::UnicodeFromUTF8(text, "80ff83");
|
||||
for (auto&& unival : univals) {
|
||||
if (unival < 32) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_base && g_base->ui->dev_console() != nullptr
|
||||
&& g_base->ui->dev_console()->HandleTextEditing(text)) {
|
||||
return;
|
||||
@ -997,10 +1028,26 @@ void Input::HandleKeyPress_(const SDL_Keysym& keysym) {
|
||||
}
|
||||
}
|
||||
|
||||
// Command-F or Control-F toggles full-screen if the app-adapter supports it.
|
||||
if (g_base->app_adapter->CanToggleFullscreen()) {
|
||||
if (!repeat_press && keysym.sym == SDLK_f
|
||||
&& ((keysym.mod & KMOD_CTRL) || (keysym.mod & KMOD_GUI))) {
|
||||
// Explicitly handle fullscreen-toggles in some cases.
|
||||
if (g_base->app_adapter->FullscreenControlAvailable()) {
|
||||
bool do_toggle{};
|
||||
// On our Mac SDL builds we support ctrl+F for toggling fullscreen.
|
||||
// On our nice Cocoa build, fullscreening happens magically through the
|
||||
// view menu fullscreen controls.
|
||||
if (g_buildconfig.ostype_macos() && !g_buildconfig.xcode_build()) {
|
||||
if (!repeat_press && keysym.sym == SDLK_f && ((keysym.mod & KMOD_CTRL))) {
|
||||
do_toggle = true;
|
||||
}
|
||||
}
|
||||
// On Windows we support both F11 and Alt+Enter for toggling fullscreen.
|
||||
if (g_buildconfig.ostype_windows()) {
|
||||
if (!repeat_press
|
||||
&& (keysym.sym == SDLK_F11
|
||||
|| (keysym.sym == SDLK_RETURN && ((keysym.mod & KMOD_ALT))))) {
|
||||
do_toggle = true;
|
||||
}
|
||||
}
|
||||
if (do_toggle) {
|
||||
g_base->python->objs()
|
||||
.Get(BasePython::ObjID::kToggleFullscreenCall)
|
||||
.Call();
|
||||
@ -1008,12 +1055,15 @@ void Input::HandleKeyPress_(const SDL_Keysym& keysym) {
|
||||
}
|
||||
}
|
||||
|
||||
// Control-Q quits. On Mac, the usual Cmd-Q gets handled
|
||||
// by the app-adapter implicitly.
|
||||
if (!repeat_press && keysym.sym == SDLK_q && (keysym.mod & KMOD_CTRL)) {
|
||||
g_base->QuitApp(true);
|
||||
return;
|
||||
}
|
||||
// Control-Q quits. On Mac, the usual Cmd-Q gets handled implicitly by the
|
||||
// app-adapter.
|
||||
// UPDATE: Disabling this for now. Looks like standard OS shortcuts like
|
||||
// Alt+F4 on windows or Cmd-Q on Mac are doing the right thing with SDL
|
||||
// builds these days so these are not needed.
|
||||
// if (!repeat_press && keysym.sym == SDLK_q && (keysym.mod & KMOD_CTRL)) {
|
||||
// g_base->QuitApp(true);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// Let the console intercept stuff if it wants at this point.
|
||||
if (auto* console = g_base->ui->dev_console()) {
|
||||
@ -1247,7 +1297,7 @@ void Input::HandleSmoothMouseScroll_(const Vector2f& velocity, bool momentum) {
|
||||
WidgetMessage(WidgetMessage::Type::kMouseWheelVelocityH, nullptr,
|
||||
cursor_pos_x_, cursor_pos_y_, velocity.x, momentum));
|
||||
|
||||
last_mouse_move_time_ = g_core->GetAppTimeMillisecs();
|
||||
last_mouse_move_time_ = g_core->GetAppTimeSeconds();
|
||||
mouse_move_count_++;
|
||||
|
||||
Camera* camera = g_base->graphics->camera();
|
||||
@ -1283,7 +1333,7 @@ void Input::HandleMouseMotion_(const Vector2f& position) {
|
||||
cursor_pos_y_ = g_base->graphics->PixelToVirtualY(
|
||||
position.y * g_base->graphics->screen_pixel_height());
|
||||
|
||||
last_mouse_move_time_ = g_core->GetAppTimeMillisecs();
|
||||
last_mouse_move_time_ = g_core->GetAppTimeSeconds();
|
||||
mouse_move_count_++;
|
||||
|
||||
// If we have a touch-input in editing mode, pass along events to it.
|
||||
@ -1324,7 +1374,7 @@ void Input::HandleMouseDown_(int button, const Vector2f& position) {
|
||||
return;
|
||||
}
|
||||
|
||||
last_mouse_move_time_ = g_core->GetAppTimeMillisecs();
|
||||
last_mouse_move_time_ = g_core->GetAppTimeSeconds();
|
||||
mouse_move_count_++;
|
||||
|
||||
// Convert normalized view coords to our virtual ones.
|
||||
@ -1389,8 +1439,8 @@ void Input::HandleMouseUp_(int button, const Vector2f& position) {
|
||||
position.y * g_base->graphics->screen_pixel_height());
|
||||
|
||||
// If we have a touch-input in editing mode, pass along events to it.
|
||||
// (it usually handles its own events but here we want it to play nice
|
||||
// with stuff under it by blocking touches, etc)
|
||||
// It usually handles its own events but here we want it to play nice
|
||||
// with stuff under it by blocking touches, etc.
|
||||
if (touch_input_ && touch_input_->editing()) {
|
||||
touch_input_->HandleTouchUp(reinterpret_cast<void*>(1), cursor_pos_x_,
|
||||
cursor_pos_y_);
|
||||
@ -1431,9 +1481,6 @@ void Input::HandleTouchEvent_(const TouchEvent& e) {
|
||||
|
||||
MarkInputActive();
|
||||
|
||||
// float x = e.x;
|
||||
// float y = e.y;
|
||||
|
||||
if (g_buildconfig.ostype_ios_tvos()) {
|
||||
printf("FIXME: update touch handling\n");
|
||||
}
|
||||
@ -1463,8 +1510,8 @@ void Input::HandleTouchEvent_(const TouchEvent& e) {
|
||||
}
|
||||
}
|
||||
|
||||
// We keep track of one 'single' touch which we pass along as
|
||||
// mouse events which covers most UI stuff.
|
||||
// We keep track of one 'single' touch which we pass along as mouse events
|
||||
// which covers most UI stuff.
|
||||
if (e.type == TouchEvent::Type::kDown && single_touch_ == nullptr) {
|
||||
single_touch_ = e.touch;
|
||||
HandleMouseDown_(SDL_BUTTON_LEFT, Vector2f(e.x, e.y));
|
||||
@ -1474,8 +1521,8 @@ void Input::HandleTouchEvent_(const TouchEvent& e) {
|
||||
HandleMouseMotion_(Vector2f(e.x, e.y));
|
||||
}
|
||||
|
||||
// Currently just applying touch-cancel the same as touch-up here;
|
||||
// perhaps should be smarter in the future.
|
||||
// Currently just applying touch-cancel the same as touch-up here; perhaps
|
||||
// should be smarter in the future.
|
||||
if ((e.type == TouchEvent::Type::kUp || e.type == TouchEvent::Type::kCanceled)
|
||||
&& (e.touch == single_touch_ || e.overall)) {
|
||||
single_touch_ = nullptr;
|
||||
@ -1529,13 +1576,9 @@ auto Input::IsCursorVisible() const -> bool {
|
||||
}
|
||||
bool val;
|
||||
|
||||
// Show our cursor if any dialogs/windows are up or else if its been moved
|
||||
// very recently.
|
||||
if (g_base->ui->MainMenuVisible()) {
|
||||
val = (g_core->GetAppTimeMillisecs() - last_mouse_move_time_ < 5000);
|
||||
} else {
|
||||
val = (g_core->GetAppTimeMillisecs() - last_mouse_move_time_ < 1000);
|
||||
}
|
||||
// Show our cursor only if its been moved recently.
|
||||
val = (g_core->GetAppTimeSeconds() - last_mouse_move_time_ < 2.071);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
|
||||
#include "ballistica/base/base.h"
|
||||
#include "ballistica/shared/foundation/object.h"
|
||||
#include "ballistica/shared/foundation/types.h"
|
||||
|
||||
namespace ballistica::base {
|
||||
|
||||
@ -179,49 +180,49 @@ class Input {
|
||||
void DestroyKeyboardInputDevices_();
|
||||
void AddFakeMods_(SDL_Keysym* sym);
|
||||
|
||||
HandleKeyPressCall* keyboard_input_capture_press_{};
|
||||
HandleKeyReleaseCall* keyboard_input_capture_release_{};
|
||||
HandleJoystickEventCall* joystick_input_capture_{};
|
||||
bool input_active_{};
|
||||
millisecs_t input_idle_time_{};
|
||||
int local_active_input_device_count_{};
|
||||
millisecs_t last_get_local_active_input_device_count_check_time_{};
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, int> >
|
||||
reserved_identifiers_;
|
||||
int max_controller_count_so_far_{};
|
||||
std::list<std::string> newly_connected_controllers_;
|
||||
std::list<std::string> newly_disconnected_controllers_;
|
||||
int connect_print_timer_id_{};
|
||||
int disconnect_print_timer_id_{};
|
||||
bool have_button_using_inputs_{};
|
||||
bool have_start_activated_default_button_inputs_{};
|
||||
bool have_non_touch_inputs_{};
|
||||
int max_controller_count_so_far_{};
|
||||
int local_active_input_device_count_{};
|
||||
int mouse_move_count_{};
|
||||
int input_lock_count_temp_{};
|
||||
int input_lock_count_permanent_{};
|
||||
bool input_active_ : 1 {};
|
||||
bool have_button_using_inputs_ : 1 {};
|
||||
bool have_start_activated_default_button_inputs_ : 1 {};
|
||||
bool have_non_touch_inputs_ : 1 {};
|
||||
bool ignore_mfi_controllers_ : 1 {};
|
||||
bool ignore_sdl_controllers_ : 1 {};
|
||||
millisecs_t input_idle_time_{};
|
||||
millisecs_t last_get_local_active_input_device_count_check_time_{};
|
||||
float cursor_pos_x_{};
|
||||
float cursor_pos_y_{};
|
||||
millisecs_t last_click_time_{};
|
||||
millisecs_t double_click_time_{200};
|
||||
millisecs_t last_mouse_move_time_{};
|
||||
int mouse_move_count_{};
|
||||
std::vector<Object::Ref<InputDevice> > input_devices_;
|
||||
KeyboardInput* keyboard_input_{};
|
||||
KeyboardInput* keyboard_input_2_{};
|
||||
TouchInput* touch_input_{};
|
||||
int input_lock_count_temp_{};
|
||||
int input_lock_count_permanent_{};
|
||||
seconds_t last_mouse_move_time_{};
|
||||
std::list<std::string> input_lock_temp_labels_;
|
||||
std::list<std::string> input_unlock_temp_labels_;
|
||||
std::list<std::string> input_lock_permanent_labels_;
|
||||
std::list<std::string> input_unlock_permanent_labels_;
|
||||
std::list<std::string> recent_input_locks_unlocks_;
|
||||
std::list<TestInput*> test_inputs_;
|
||||
std::list<std::string> newly_connected_controllers_;
|
||||
std::list<std::string> newly_disconnected_controllers_;
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, int> >
|
||||
reserved_identifiers_;
|
||||
std::vector<Object::Ref<InputDevice> > input_devices_;
|
||||
std::set<int> keys_held_;
|
||||
millisecs_t last_input_device_count_update_time_{};
|
||||
millisecs_t last_input_temp_lock_time_{};
|
||||
bool ignore_mfi_controllers_{};
|
||||
bool ignore_sdl_controllers_{};
|
||||
std::list<TestInput*> test_inputs_;
|
||||
millisecs_t stress_test_time_{};
|
||||
millisecs_t stress_test_last_leave_time_{};
|
||||
void* single_touch_{};
|
||||
KeyboardInput* keyboard_input_{};
|
||||
KeyboardInput* keyboard_input_2_{};
|
||||
TouchInput* touch_input_{};
|
||||
HandleKeyPressCall* keyboard_input_capture_press_{};
|
||||
HandleKeyReleaseCall* keyboard_input_capture_release_{};
|
||||
HandleJoystickEventCall* joystick_input_capture_{};
|
||||
};
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -76,11 +76,11 @@ void Logic::OnAppStart() {
|
||||
|
||||
void Logic::OnGraphicsReady() {
|
||||
assert(g_base->InLogicThread());
|
||||
if (on_initial_screen_creation_complete_called_) {
|
||||
if (graphics_ready_) {
|
||||
// Only want to fire this logic the first time.
|
||||
return;
|
||||
}
|
||||
on_initial_screen_creation_complete_called_ = true;
|
||||
graphics_ready_ = true;
|
||||
|
||||
// Ok; graphics-server is telling us we've got a screen (or no screen in
|
||||
// the case of headless-mode). We use this as a cue to kick off our
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
#define BALLISTICA_BASE_LOGIC_LOGIC_H_
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "ballistica/shared/foundation/object.h"
|
||||
@ -118,6 +117,8 @@ class Logic {
|
||||
auto shutting_down() const { return shutting_down_; }
|
||||
auto shutdown_completed() const { return shutdown_completed_; }
|
||||
|
||||
auto graphics_ready() const { return graphics_ready_; }
|
||||
|
||||
private:
|
||||
void UpdateDisplayTimeForFrameDraw_();
|
||||
void UpdateDisplayTimeForHeadlessMode_();
|
||||
@ -127,31 +128,31 @@ class Logic {
|
||||
void UpdatePendingWorkTimer_();
|
||||
void StepDisplayTime_();
|
||||
|
||||
double display_time_{};
|
||||
double display_time_increment_{1.0 / 60.0};
|
||||
seconds_t display_time_{};
|
||||
seconds_t display_time_increment_{1.0 / 60.0};
|
||||
microsecs_t display_time_microsecs_{};
|
||||
microsecs_t display_time_increment_microsecs_{1000000 / 60};
|
||||
|
||||
// GUI scheduling.
|
||||
double last_display_time_update_app_time_{-1.0};
|
||||
double recent_display_time_increments_[kDisplayTimeSampleCount]{};
|
||||
int recent_display_time_increments_index_{-1};
|
||||
|
||||
// Headless scheduling.
|
||||
Timer* headless_display_time_step_timer_{};
|
||||
|
||||
Timer* process_pending_work_timer_{};
|
||||
Timer* asset_prune_timer_{};
|
||||
Timer* debug_timer_{};
|
||||
EventLoop* event_loop_{};
|
||||
std::unique_ptr<TimerList> display_timers_;
|
||||
// GUI scheduling.
|
||||
seconds_t last_display_time_update_app_time_{-1.0};
|
||||
seconds_t recent_display_time_increments_[kDisplayTimeSampleCount]{};
|
||||
int recent_display_time_increments_index_{-1};
|
||||
|
||||
bool app_bootstrapping_complete_ : 1 {};
|
||||
bool have_pending_loads_ : 1 {};
|
||||
bool debug_log_display_time_ : 1 {};
|
||||
bool applied_app_config_ : 1 {};
|
||||
bool shutting_down_ : 1 {};
|
||||
bool shutdown_completed_ : 1 {};
|
||||
bool on_initial_screen_creation_complete_called_ : 1 {};
|
||||
bool graphics_ready_ : 1 {};
|
||||
Timer* process_pending_work_timer_{};
|
||||
Timer* asset_prune_timer_{};
|
||||
Timer* debug_timer_{};
|
||||
EventLoop* event_loop_{};
|
||||
std::unique_ptr<TimerList> display_timers_;
|
||||
};
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -4,9 +4,8 @@
|
||||
#include "ballistica/base/platform/apple/base_platform_apple.h"
|
||||
|
||||
#if BA_XCODE_BUILD
|
||||
#include <unistd.h>
|
||||
#include <BallisticaKit-Swift.h>
|
||||
#endif
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#if BA_XCODE_BUILD
|
||||
#include "ballistica/base/platform/apple/apple_utils.h"
|
||||
@ -14,12 +13,7 @@
|
||||
|
||||
namespace ballistica::base {
|
||||
|
||||
#if BA_OSTYPE_MACOS && BA_XCODE_BUILD && BA_SDL_BUILD
|
||||
extern void DoSetCursor(bool show);
|
||||
#endif
|
||||
|
||||
BasePlatformApple::BasePlatformApple() { // NOLINT: trivial constructor false
|
||||
// positive
|
||||
BasePlatformApple::BasePlatformApple() {
|
||||
// On iOS, keep the device from falling asleep in our app
|
||||
#if BA_OSTYPE_IOS_TVOS
|
||||
AppleUtils::DisableIdleTimer();
|
||||
@ -53,24 +47,17 @@ void BasePlatformApple::PurchaseAck(const std::string& purchase,
|
||||
|
||||
void BasePlatformApple::DoOpenURL(const std::string& url) {
|
||||
#if BA_XCODE_BUILD
|
||||
// Go ahead and do this ourself. Though perhaps the default
|
||||
// Python path would be fine.
|
||||
AppleUtils::OpenURL(url.c_str());
|
||||
#if BA_OSTYPE_MACOS
|
||||
BallisticaKit::CocoaFromCppOpenURL(url);
|
||||
#else
|
||||
// Otherwise go with the default (Python webbrowser module).
|
||||
BasePlatform::DoOpenURL(url);
|
||||
#endif
|
||||
}
|
||||
BallisticaKit::UIKitFromCppOpenURL(url);
|
||||
#endif // BA_OSTYPE_MACOS
|
||||
|
||||
// void BasePlatformApple::SetHardwareCursorVisible(bool visible) {
|
||||
// // Set our nifty custom hardware cursor on mac;
|
||||
// // otherwise fall back to default.
|
||||
// #if BA_OSTYPE_MACOS && BA_XCODE_BUILD && !BA_HEADLESS_BUILD && BA_SDL_BUILD
|
||||
// base::DoSetCursor(visible);
|
||||
// #else
|
||||
// return BasePlatform::SetHardwareCursorVisible(visible);
|
||||
// #endif
|
||||
// }
|
||||
#else
|
||||
// For non-xcode builds, go with the default (Python webbrowser module).
|
||||
BasePlatform::DoOpenURL(url);
|
||||
#endif // BA_XCODE_BUILD
|
||||
}
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
|
||||
@ -11,15 +11,11 @@ namespace ballistica::base {
|
||||
class BasePlatformApple : public BasePlatform {
|
||||
public:
|
||||
BasePlatformApple();
|
||||
|
||||
void DoPurchase(const std::string& item) override;
|
||||
void RestorePurchases() override;
|
||||
void PurchaseAck(const std::string& purchase,
|
||||
const std::string& order_id) override;
|
||||
|
||||
void DoOpenURL(const std::string& url) override;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -38,6 +38,8 @@ class BasePython {
|
||||
kConfig,
|
||||
kAppOnNativeBootstrappingCompleteCall,
|
||||
kResetToMainMenuCall,
|
||||
kStoreConfigFullscreenOnCall,
|
||||
kStoreConfigFullscreenOffCall,
|
||||
kSetConfigFullscreenOnCall,
|
||||
kSetConfigFullscreenOffCall,
|
||||
kNotSignedInScreenMessageCall,
|
||||
@ -108,6 +110,7 @@ class BasePython {
|
||||
kDevConsoleStringEditAdapterClass,
|
||||
kGetDevConsoleTabNamesCall,
|
||||
kAppDevConsoleDoRefreshTabCall,
|
||||
kUnsupportedControllerMessageCall,
|
||||
kLast // Sentinel; must be at end.
|
||||
};
|
||||
|
||||
|
||||
@ -810,9 +810,9 @@ static PyMethodDef PySetStressTestingDef = {
|
||||
"(internal)",
|
||||
};
|
||||
|
||||
// ------------------------------ display_log ----------------------------------
|
||||
// -------------------------------- emit_log -----------------------------------
|
||||
|
||||
static auto PyDisplayLog(PyObject* self, PyObject* args, PyObject* keywds)
|
||||
static auto PyEmitLog(PyObject* self, PyObject* args, PyObject* keywds)
|
||||
-> PyObject* {
|
||||
BA_PYTHON_TRY;
|
||||
static const char* kwlist[] = {"name", "level", "message", nullptr};
|
||||
@ -839,25 +839,25 @@ static auto PyDisplayLog(PyObject* self, PyObject* args, PyObject* keywds)
|
||||
level = LogLevel::kCritical;
|
||||
} else {
|
||||
// Assume we should avoid Log() calls here since it could infinite loop.
|
||||
fprintf(stderr, "Invalid log level to display_log(): %s\n", levelstr);
|
||||
fprintf(stderr, "Invalid log level to emit_log(): %s\n", levelstr);
|
||||
level = LogLevel::kInfo;
|
||||
}
|
||||
Logging::DisplayLog(name, level, message);
|
||||
Logging::EmitLog(name, level, message);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
BA_PYTHON_CATCH;
|
||||
}
|
||||
|
||||
static PyMethodDef PyDisplayLogDef = {
|
||||
"display_log", // name
|
||||
(PyCFunction)PyDisplayLog, // method
|
||||
static PyMethodDef PyEmitLogDef = {
|
||||
"emit_log", // name
|
||||
(PyCFunction)PyEmitLog, // method
|
||||
METH_VARARGS | METH_KEYWORDS, // flags
|
||||
|
||||
"display_log(name: str, level: str, message: str) -> None\n"
|
||||
"emit_log(name: str, level: str, message: str) -> None\n"
|
||||
"\n"
|
||||
"(internal)\n"
|
||||
"\n"
|
||||
"Sends a log message to the in-game console and any per-platform\n"
|
||||
"Sends a log message to the in-app console and any per-platform\n"
|
||||
"log destinations (Android log, etc.). This generally is not called\n"
|
||||
"directly and should instead be fed Python logging output.",
|
||||
};
|
||||
@ -1654,7 +1654,7 @@ auto PythonMethodsApp::GetMethods() -> std::vector<PyMethodDef> {
|
||||
PyAppNameUpperDef,
|
||||
PyIsXCodeBuildDef,
|
||||
PyCanDisplayFullUnicodeDef,
|
||||
PyDisplayLogDef,
|
||||
PyEmitLogDef,
|
||||
PyV1CloudLogDef,
|
||||
PySetStressTestingDef,
|
||||
PyEnvDef,
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include "ballistica/base/python/support/python_context_call_runnable.h"
|
||||
#include "ballistica/core/core.h"
|
||||
#include "ballistica/core/platform/core_platform.h"
|
||||
#include "ballistica/shared/foundation/macros.h"
|
||||
#include "ballistica/shared/generic/utils.h"
|
||||
#include "ballistica/shared/python/python.h"
|
||||
#include "ballistica/shared/python/python_sys.h"
|
||||
@ -345,23 +346,23 @@ static PyMethodDef PySafeColorDef = {
|
||||
|
||||
// ------------------------ get_max_graphics_quality ---------------------------
|
||||
|
||||
static auto PyGetMaxGraphicsQuality(PyObject* self, PyObject* args)
|
||||
-> PyObject* {
|
||||
static auto PyGetMaxGraphicsQuality(PyObject* self) -> PyObject* {
|
||||
BA_PYTHON_TRY;
|
||||
if (g_base->graphics
|
||||
&& g_base->graphics->has_supports_high_quality_graphics_value()
|
||||
&& g_base->graphics->supports_high_quality_graphics()) {
|
||||
return Py_BuildValue("s", "High");
|
||||
} else {
|
||||
return Py_BuildValue("s", "Medium");
|
||||
}
|
||||
// if (g_base->graphics
|
||||
// && g_base->graphics->has_supports_high_quality_graphics_value()
|
||||
// && g_base->graphics->supports_high_quality_graphics()) {
|
||||
// return Py_BuildValue("s", "High");
|
||||
// } else {
|
||||
// return Py_BuildValue("s", "Medium");
|
||||
// }
|
||||
return Py_BuildValue("s", "Higher");
|
||||
BA_PYTHON_CATCH;
|
||||
}
|
||||
|
||||
static PyMethodDef PyGetMaxGraphicsQualityDef = {
|
||||
"get_max_graphics_quality", // name
|
||||
PyGetMaxGraphicsQuality, // method
|
||||
METH_VARARGS, // flags
|
||||
"get_max_graphics_quality", // name
|
||||
(PyCFunction)PyGetMaxGraphicsQuality, // method
|
||||
METH_NOARGS, // flags
|
||||
|
||||
"get_max_graphics_quality() -> str\n"
|
||||
"\n"
|
||||
@ -619,24 +620,105 @@ static PyMethodDef PyGetDisplayResolutionDef = {
|
||||
"display. Returns None if resolutions cannot be directly set.",
|
||||
};
|
||||
|
||||
// ------------------------- can_toggle_fullscreen ----------------------------
|
||||
// ---------------------- fullscreen_control_available -------------------------
|
||||
|
||||
static auto PyCanToggleFullscreen(PyObject* self) -> PyObject* {
|
||||
static auto PyFullscreenControlAvailable(PyObject* self) -> PyObject* {
|
||||
BA_PYTHON_TRY;
|
||||
|
||||
if (g_base->app_adapter->CanToggleFullscreen()) {
|
||||
BA_PRECONDITION(g_base->InLogicThread());
|
||||
if (g_base->app_adapter->FullscreenControlAvailable()) {
|
||||
Py_RETURN_TRUE;
|
||||
}
|
||||
Py_RETURN_FALSE;
|
||||
BA_PYTHON_CATCH;
|
||||
}
|
||||
|
||||
static PyMethodDef PyCanToggleFullscreenDef = {
|
||||
"can_toggle_fullscreen", // name
|
||||
(PyCFunction)PyCanToggleFullscreen, // method
|
||||
METH_NOARGS, // flags
|
||||
static PyMethodDef PyFullscreenControlAvailableDef = {
|
||||
"fullscreen_control_available", // name
|
||||
(PyCFunction)PyFullscreenControlAvailable, // method
|
||||
METH_NOARGS, // flags
|
||||
|
||||
"can_toggle_fullscreen() -> bool\n"
|
||||
"fullscreen_control_available() -> bool\n"
|
||||
"\n"
|
||||
"(internal)\n",
|
||||
};
|
||||
|
||||
// --------------------- fullscreen_control_key_shortcut -----------------------
|
||||
|
||||
static auto PyFullscreenControlKeyShortcut(PyObject* self) -> PyObject* {
|
||||
BA_PYTHON_TRY;
|
||||
|
||||
BA_PRECONDITION(g_base->InLogicThread());
|
||||
BA_PRECONDITION(g_base->app_adapter->FullscreenControlAvailable());
|
||||
|
||||
auto val = g_base->app_adapter->FullscreenControlKeyShortcut();
|
||||
if (val.has_value()) {
|
||||
return PyUnicode_FromString(val->c_str());
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
BA_PYTHON_CATCH;
|
||||
}
|
||||
|
||||
static PyMethodDef PyFullscreenControlKeyShortcutDef = {
|
||||
"fullscreen_control_key_shortcut", // name
|
||||
(PyCFunction)PyFullscreenControlKeyShortcut, // method
|
||||
METH_NOARGS, // flags
|
||||
|
||||
"fullscreen_control_key_shortcut() -> str | None\n"
|
||||
"\n"
|
||||
"(internal)\n",
|
||||
};
|
||||
|
||||
// ------------------------ fullscreen_control_get -----------------------------
|
||||
|
||||
static auto PyFullscreenControlGet(PyObject* self) -> PyObject* {
|
||||
BA_PYTHON_TRY;
|
||||
|
||||
BA_PRECONDITION(g_base->InLogicThread());
|
||||
if (g_base->app_adapter->FullscreenControlGet()) {
|
||||
Py_RETURN_TRUE;
|
||||
}
|
||||
Py_RETURN_FALSE;
|
||||
BA_PYTHON_CATCH;
|
||||
}
|
||||
|
||||
static PyMethodDef PyFullscreenControlGetDef = {
|
||||
"fullscreen_control_get", // name
|
||||
(PyCFunction)PyFullscreenControlGet, // method
|
||||
METH_NOARGS, // flags
|
||||
|
||||
"fullscreen_control_get() -> bool\n"
|
||||
"\n"
|
||||
"(internal)\n",
|
||||
};
|
||||
|
||||
// ------------------------ fullscreen_control_set -----------------------------
|
||||
|
||||
static auto PyFullscreenControlSet(PyObject* self, PyObject* args,
|
||||
PyObject* keywds) -> PyObject* {
|
||||
BA_PYTHON_TRY;
|
||||
|
||||
BA_PRECONDITION(g_base->InLogicThread());
|
||||
|
||||
int val{};
|
||||
static const char* kwlist[] = {"val", nullptr};
|
||||
if (!PyArg_ParseTupleAndKeywords(args, keywds, "p",
|
||||
const_cast<char**>(kwlist), &val)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
g_base->app_adapter->FullscreenControlSet(val);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
BA_PYTHON_CATCH;
|
||||
}
|
||||
|
||||
static PyMethodDef PyFullscreenControlSetDef = {
|
||||
"fullscreen_control_set", // name
|
||||
(PyCFunction)PyFullscreenControlSet, // method
|
||||
METH_VARARGS | METH_KEYWORDS, // flags
|
||||
|
||||
"fullscreen_control_set(val: bool) -> None\n"
|
||||
"\n"
|
||||
"(internal)\n",
|
||||
};
|
||||
@ -728,10 +810,13 @@ auto PythonMethodsGraphics::GetMethods() -> std::vector<PyMethodDef> {
|
||||
PyGetMaxGraphicsQualityDef,
|
||||
PySafeColorDef,
|
||||
PyCharStrDef,
|
||||
PyCanToggleFullscreenDef,
|
||||
PyFullscreenControlAvailableDef,
|
||||
PySupportsVSyncDef,
|
||||
PySupportsMaxFPSDef,
|
||||
PyShowProgressBarDef,
|
||||
PyFullscreenControlKeyShortcutDef,
|
||||
PyFullscreenControlGetDef,
|
||||
PyFullscreenControlSetDef,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -33,6 +33,8 @@ static auto PyGetSimpleSound(PyObject* self, PyObject* args, PyObject* keywds)
|
||||
const_cast<char**>(kwlist), &name)) {
|
||||
return nullptr;
|
||||
}
|
||||
BA_PRECONDITION(g_base->InLogicThread());
|
||||
BA_PRECONDITION(g_base->assets->asset_loads_allowed());
|
||||
{
|
||||
Assets::AssetListLock lock;
|
||||
return PythonClassSimpleSound::Create(g_base->assets->GetSound(name));
|
||||
@ -1658,6 +1660,27 @@ static PyMethodDef PyDevConsoleRequestRefreshDef = {
|
||||
"(internal)",
|
||||
};
|
||||
|
||||
// -------------------------- asset_loads_allowed ------------------------------
|
||||
|
||||
static auto PyAssetLoadsAllowed(PyObject* self) -> PyObject* {
|
||||
BA_PYTHON_TRY;
|
||||
if (g_base->assets->asset_loads_allowed()) {
|
||||
Py_RETURN_TRUE;
|
||||
}
|
||||
Py_RETURN_FALSE;
|
||||
BA_PYTHON_CATCH;
|
||||
}
|
||||
|
||||
static PyMethodDef PyAssetLoadsAllowedDef = {
|
||||
"asset_loads_allowed", // name
|
||||
(PyCFunction)PyAssetLoadsAllowed, // method
|
||||
METH_NOARGS, // flags
|
||||
|
||||
"asset_loads_allowed() -> bool\n"
|
||||
"\n"
|
||||
"(internal)",
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
auto PythonMethodsMisc::GetMethods() -> std::vector<PyMethodDef> {
|
||||
@ -1720,6 +1743,7 @@ auto PythonMethodsMisc::GetMethods() -> std::vector<PyMethodDef> {
|
||||
PyDevConsoleTabHeightDef,
|
||||
PyDevConsoleBaseScaleDef,
|
||||
PyDevConsoleRequestRefreshDef,
|
||||
PyAssetLoadsAllowedDef,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include "ballistica/base/ui/ui.h"
|
||||
|
||||
#include "ballistica/base/app_adapter/app_adapter.h"
|
||||
#include "ballistica/base/audio/audio.h"
|
||||
#include "ballistica/base/graphics/component/simple_component.h"
|
||||
#include "ballistica/base/input/device/keyboard_input.h"
|
||||
@ -184,15 +185,14 @@ void UI::HandleMouseUp(int button, float x, float y) {
|
||||
}
|
||||
|
||||
auto UI::UIHasDirectKeyboardInput() const -> bool {
|
||||
// Currently limiting this to desktop operating systems, but should
|
||||
// generalize this, as situations such as tablets with hardware keyboards
|
||||
// should behave similarly to desktop PCs. Though perhaps we should make
|
||||
// this optional everywhere (or language dependent) since direct keyboard
|
||||
// input might not work well for some languages even on desktops.
|
||||
if (g_buildconfig.ostype_macos() || g_buildconfig.ostype_windows()
|
||||
|| g_buildconfig.ostype_linux()) {
|
||||
// Return true if we've got a keyboard attached and either it or nothing
|
||||
// is active.
|
||||
// As a first gate, ask the app-adapter if it is providing keyboard
|
||||
// events at all.
|
||||
if (g_base->app_adapter->HasDirectKeyboardInput()) {
|
||||
// Ok, direct keyboard input is a thing.
|
||||
// Now let's also require the keyboard (or nothing) to be currently
|
||||
// driving the UI. If something like a game-controller is driving,
|
||||
// we'll probably want to pop up a controller-centric on-screen-keyboard
|
||||
// thingie instead.
|
||||
auto* ui_input_device = g_base->ui->GetUIInputDevice();
|
||||
if (auto* keyboard = g_base->ui->GetUIInputDevice()) {
|
||||
if (ui_input_device == keyboard || ui_input_device == nullptr) {
|
||||
|
||||
@ -147,17 +147,17 @@ auto CorePlatformApple::IsRunningOnDesktop() -> bool {
|
||||
#endif
|
||||
}
|
||||
|
||||
void CorePlatformApple::DisplayLog(const std::string& name, LogLevel level,
|
||||
const std::string& msg) {
|
||||
void CorePlatformApple::EmitPlatformLog(const std::string& name, LogLevel level,
|
||||
const std::string& msg) {
|
||||
#if BA_XCODE_BUILD && !BA_HEADLESS_BUILD
|
||||
|
||||
// HMM: do we want to use proper logging APIs here or simple printing?
|
||||
// base::AppleUtils::NSLogStr(msg);
|
||||
CorePlatform::DisplayLog(name, level, msg);
|
||||
CorePlatform::EmitPlatformLog(name, level, msg);
|
||||
#else
|
||||
|
||||
// Fall back to default handler...
|
||||
CorePlatform::DisplayLog(name, level, msg);
|
||||
CorePlatform::EmitPlatformLog(name, level, msg);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -26,8 +26,8 @@ class CorePlatformApple : public CorePlatform {
|
||||
auto DoHasTouchScreen() -> bool override;
|
||||
auto GetDefaultUIScale() -> UIScale override;
|
||||
auto IsRunningOnDesktop() -> bool override;
|
||||
void DisplayLog(const std::string& name, LogLevel level,
|
||||
const std::string& msg) override;
|
||||
void EmitPlatformLog(const std::string& name, LogLevel level,
|
||||
const std::string& msg) override;
|
||||
void GetTextBoundsAndWidth(const std::string& text, Rect* r,
|
||||
float* width) override;
|
||||
void FreeTextTexture(void* tex) override;
|
||||
|
||||
@ -465,12 +465,17 @@ auto CorePlatform::IsRunningOnDesktop() -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
void CorePlatform::SleepMillisecs(millisecs_t ms) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
|
||||
void CorePlatform::SleepSeconds(seconds_t duration) {
|
||||
std::this_thread::sleep_for(
|
||||
std::chrono::microseconds(static_cast<microsecs_t>(duration * 1000000)));
|
||||
}
|
||||
|
||||
void CorePlatform::SleepMicrosecs(millisecs_t ms) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(ms));
|
||||
void CorePlatform::SleepMillisecs(millisecs_t duration) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(duration));
|
||||
}
|
||||
|
||||
void CorePlatform::SleepMicrosecs(millisecs_t duration) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(duration));
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
@ -481,8 +486,8 @@ auto CorePlatform::GetDefaultUIScale() -> UIScale {
|
||||
return UIScale::kLarge;
|
||||
}
|
||||
|
||||
void CorePlatform::DisplayLog(const std::string& name, LogLevel level,
|
||||
const std::string& msg) {
|
||||
void CorePlatform::EmitPlatformLog(const std::string& name, LogLevel level,
|
||||
const std::string& msg) {
|
||||
// Do nothing by default.
|
||||
}
|
||||
|
||||
|
||||
@ -99,8 +99,8 @@ class CorePlatform {
|
||||
/// Display a message to any default log for the platform (android log,
|
||||
/// etc.) Note that this can be called from any thread. Default
|
||||
/// implementation does nothing.
|
||||
virtual void DisplayLog(const std::string& name, LogLevel level,
|
||||
const std::string& msg);
|
||||
virtual void EmitPlatformLog(const std::string& name, LogLevel level,
|
||||
const std::string& msg);
|
||||
|
||||
#pragma mark ENVIRONMENT -------------------------------------------------------
|
||||
|
||||
@ -381,9 +381,9 @@ class CorePlatform {
|
||||
/// to not go backwards.
|
||||
static auto GetCurrentWholeSeconds() -> int64_t;
|
||||
|
||||
static void SleepMillisecs(millisecs_t ms);
|
||||
|
||||
static void SleepMicrosecs(microsecs_t ms);
|
||||
static void SleepSeconds(seconds_t duration);
|
||||
static void SleepMillisecs(millisecs_t duration);
|
||||
static void SleepMicrosecs(microsecs_t duration);
|
||||
|
||||
/// Given a C++ symbol, attempt to return a pretty one.
|
||||
virtual auto DemangleCXXSymbol(const std::string& s) -> std::string;
|
||||
|
||||
@ -827,11 +827,12 @@ std::string CorePlatformWindows::DoGetDeviceName() {
|
||||
|
||||
bool CorePlatformWindows::DoHasTouchScreen() { return false; }
|
||||
|
||||
void CorePlatformWindows::DisplayLog(const std::string& name, LogLevel level,
|
||||
const std::string& msg) {
|
||||
void CorePlatformWindows::EmitPlatformLog(const std::string& name,
|
||||
LogLevel level,
|
||||
const std::string& msg) {
|
||||
// if (have_stdin_stdout_) {
|
||||
// // On headless builds we use default handler (simple stdout).
|
||||
// return CorePlatform::DisplayLog(msg);
|
||||
// return CorePlatform::EmitPlatformLog(msg);
|
||||
// }
|
||||
|
||||
// Also spit this out as a debug-string for when running from msvc.
|
||||
|
||||
@ -41,8 +41,8 @@ class CorePlatformWindows : public CorePlatform {
|
||||
auto GetLocale() -> std::string override;
|
||||
auto DoGetDeviceName() -> std::string override;
|
||||
auto DoHasTouchScreen() -> bool override;
|
||||
void DisplayLog(const std::string& name, LogLevel level,
|
||||
const std::string& msg) override;
|
||||
void EmitPlatformLog(const std::string& name, LogLevel level,
|
||||
const std::string& msg) override;
|
||||
void SetEnv(const std::string& name, const std::string& value) override;
|
||||
auto GetEnv(const std::string& name) -> std::optional<std::string> override;
|
||||
auto GetIsStdinATerminal() -> bool override;
|
||||
|
||||
@ -307,8 +307,8 @@ void CorePython::LoggingCall(LogLevel loglevel, const std::string& msg) {
|
||||
"CorePython::LoggingCall() called before Python"
|
||||
" logging available."};
|
||||
if (g_core->platform) {
|
||||
g_core->platform->DisplayLog("root", LogLevel::kError, errmsg);
|
||||
g_core->platform->DisplayLog("root", loglevel, msg);
|
||||
g_core->platform->EmitPlatformLog("root", LogLevel::kError, errmsg);
|
||||
g_core->platform->EmitPlatformLog("root", loglevel, msg);
|
||||
}
|
||||
fprintf(stderr, "%s\n%s\n", errmsg, msg.c_str());
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ class CorePython {
|
||||
|
||||
/// Calls Python logging function (logging.error, logging.warning, etc.)
|
||||
/// Can be called from any thread at any time. If called before Python
|
||||
/// logging is available, logs locally using Logging::DisplayLog()
|
||||
/// logging is available, logs locally using Logging::EmitPlatformLog()
|
||||
/// (with an added warning).
|
||||
void LoggingCall(LogLevel loglevel, const std::string& msg);
|
||||
void ImportPythonObjs();
|
||||
|
||||
@ -42,6 +42,7 @@ class BaseSoftInterface {
|
||||
virtual void DoPushObjCall(const PythonObjectSetBase* objset, int id,
|
||||
const std::string& arg) = 0;
|
||||
virtual auto IsAppStarted() const -> bool = 0;
|
||||
virtual auto IsAppBootstrapped() const -> bool = 0;
|
||||
virtual auto GetReturnValue() const -> int = 0;
|
||||
};
|
||||
|
||||
|
||||
@ -12,11 +12,12 @@
|
||||
|
||||
namespace ballistica::scene_v1 {
|
||||
|
||||
// Stores info about an occurring collision.
|
||||
// Note than just because a collision exists between two parts doesn't mean
|
||||
// they're physically colliding in the simulation. It is just a shortcut to
|
||||
// determine what behavior, if any, exists between two parts which are currently
|
||||
// overlapping in the simulation.
|
||||
/// Stores info about an occurring collision.
|
||||
///
|
||||
/// Note than just because a collision exists between two parts doesn't mean
|
||||
/// they're physically colliding in the simulation. It is just a shortcut to
|
||||
/// determine what behavior, if any, exists between two parts which are
|
||||
/// currently overlapping in the simulation.
|
||||
class Collision : public Object {
|
||||
public:
|
||||
explicit Collision(Scene* scene) : src_context(scene), dst_context(scene) {}
|
||||
|
||||
@ -66,77 +66,77 @@ void do_dBodyGetLocalFeedback(dBodyID b, dReal px, dReal py, dReal pz,
|
||||
|
||||
// Stores info about a collision needing a reset
|
||||
// (used when parts change materials).
|
||||
class Dynamics::CollisionReset {
|
||||
class Dynamics::CollisionReset_ {
|
||||
public:
|
||||
int node1;
|
||||
int node2;
|
||||
int part1;
|
||||
int part2;
|
||||
CollisionReset(int node1_in, int part1_in, int node2_in, int part2_in)
|
||||
CollisionReset_(int node1_in, int part1_in, int node2_in, int part2_in)
|
||||
: node1(node1_in), node2(node2_in), part1(part1_in), part2(part2_in) {}
|
||||
};
|
||||
|
||||
class Dynamics::CollisionEvent {
|
||||
class Dynamics::CollisionEvent_ {
|
||||
public:
|
||||
Object::Ref<MaterialAction> action;
|
||||
Object::Ref<Collision> collision;
|
||||
Object::WeakRef<Node> node1; // first event node
|
||||
Object::WeakRef<Node> node2; // second event node
|
||||
CollisionEvent(Node* node1_in, Node* node2_in,
|
||||
const Object::Ref<MaterialAction>& action_in,
|
||||
const Object::Ref<Collision>& collision_in)
|
||||
CollisionEvent_(Node* node1_in, Node* node2_in,
|
||||
const Object::Ref<MaterialAction>& action_in,
|
||||
const Object::Ref<Collision>& collision_in)
|
||||
: node1(node1_in),
|
||||
node2(node2_in),
|
||||
action(action_in),
|
||||
collision(collision_in) {}
|
||||
};
|
||||
|
||||
class Dynamics::SrcPartCollideMap {
|
||||
class Dynamics::SrcPartCollideMap_ {
|
||||
public:
|
||||
std::unordered_map<int, Object::Ref<Collision> > dst_part_collisions;
|
||||
};
|
||||
|
||||
class Dynamics::DstNodeCollideMap {
|
||||
class Dynamics::DstNodeCollideMap_ {
|
||||
public:
|
||||
std::unordered_map<int, SrcPartCollideMap> src_parts;
|
||||
std::unordered_map<int, SrcPartCollideMap_> src_parts;
|
||||
int collideDisabled;
|
||||
DstNodeCollideMap() : collideDisabled(0) {}
|
||||
~DstNodeCollideMap() = default;
|
||||
DstNodeCollideMap_() : collideDisabled(0) {}
|
||||
~DstNodeCollideMap_() = default;
|
||||
};
|
||||
|
||||
class Dynamics::SrcNodeCollideMap {
|
||||
class Dynamics::SrcNodeCollideMap_ {
|
||||
public:
|
||||
std::unordered_map<int64_t, DstNodeCollideMap> dst_nodes;
|
||||
std::unordered_map<int64_t, DstNodeCollideMap_> dst_nodes;
|
||||
};
|
||||
|
||||
class Dynamics::Impl {
|
||||
class Dynamics::Impl_ {
|
||||
public:
|
||||
explicit Impl(Dynamics* dynamics) : dynamics_(dynamics) {}
|
||||
explicit Impl_(Dynamics* dynamics) : dynamics_(dynamics) {}
|
||||
|
||||
// NOTE: we need to implement this here in an Impl class because
|
||||
// gcc currently chokes on unordered_maps with forward-declared types,
|
||||
// so we can't have this in our header without pushing all our map/collision
|
||||
// types there too.
|
||||
void HandleDisconnect(
|
||||
const std::unordered_map<int64_t, Dynamics::SrcNodeCollideMap>::iterator&
|
||||
const std::unordered_map<int64_t, Dynamics::SrcNodeCollideMap_>::iterator&
|
||||
i,
|
||||
const std::unordered_map<int64_t, Dynamics::DstNodeCollideMap>::iterator&
|
||||
const std::unordered_map<int64_t, Dynamics::DstNodeCollideMap_>::iterator&
|
||||
j,
|
||||
const std::unordered_map<int, SrcPartCollideMap>::iterator& k,
|
||||
const std::unordered_map<int, SrcPartCollideMap_>::iterator& k,
|
||||
const std::unordered_map<int, Object::Ref<Collision> >::iterator& l);
|
||||
|
||||
private:
|
||||
Dynamics* dynamics_{};
|
||||
// Contains in-progress collisions for current nodes.
|
||||
std::unordered_map<int64_t, SrcNodeCollideMap> node_collisions_;
|
||||
std::unordered_map<int64_t, SrcNodeCollideMap_> node_collisions_;
|
||||
friend class Dynamics;
|
||||
};
|
||||
|
||||
Dynamics::Dynamics(Scene* scene_in)
|
||||
: scene_(scene_in),
|
||||
collision_cache_(new base::CollisionCache()),
|
||||
impl_(std::make_unique<Impl>(this)) {
|
||||
ResetODE();
|
||||
impl_(std::make_unique<Impl_>(this)) {
|
||||
ResetODE_();
|
||||
}
|
||||
|
||||
Dynamics::~Dynamics() {
|
||||
@ -145,7 +145,7 @@ Dynamics::~Dynamics() {
|
||||
"Dynamics going down within Process() call;"
|
||||
" should not happen.");
|
||||
}
|
||||
ShutdownODE();
|
||||
ShutdownODE_();
|
||||
}
|
||||
|
||||
void Dynamics::Draw(base::FrameDef* frame_def) {
|
||||
@ -203,7 +203,7 @@ void Dynamics::RemoveTrimesh(dGeomID g) {
|
||||
throw Exception("trimesh not found");
|
||||
}
|
||||
|
||||
auto Dynamics::AreColliding(const Part& p1_in, const Part& p2_in) -> bool {
|
||||
auto Dynamics::AreColliding_(const Part& p1_in, const Part& p2_in) -> bool {
|
||||
const Part* p1;
|
||||
const Part* p2;
|
||||
if (IsInStoreOrder(p1_in.node()->id(), p1_in.id(), p2_in.node()->id(),
|
||||
@ -279,7 +279,7 @@ auto Dynamics::GetCollision(Part* p1_in, Part* p2_in, MaterialContext** cc1,
|
||||
p2->ApplyMaterials(*cc2, p2, p1);
|
||||
|
||||
// If either disabled collisions between these two nodes, store that.
|
||||
DstNodeCollideMap* dncm =
|
||||
DstNodeCollideMap_* dncm =
|
||||
&impl_->node_collisions_[p1->node()->id()].dst_nodes[p2->node()->id()];
|
||||
if (!(*cc1)->node_collide || !(*cc2)->node_collide) {
|
||||
dncm->collideDisabled = true;
|
||||
@ -319,10 +319,12 @@ auto Dynamics::GetCollision(Part* p1_in, Part* p2_in, MaterialContext** cc1,
|
||||
return &(*(i.first->second));
|
||||
}
|
||||
|
||||
void Dynamics::Impl::HandleDisconnect(
|
||||
const std::unordered_map<int64_t, Dynamics::SrcNodeCollideMap>::iterator& i,
|
||||
const std::unordered_map<int64_t, Dynamics::DstNodeCollideMap>::iterator& j,
|
||||
const std::unordered_map<int, SrcPartCollideMap>::iterator& k,
|
||||
void Dynamics::Impl_::HandleDisconnect(
|
||||
const std::unordered_map<int64_t, Dynamics::SrcNodeCollideMap_>::iterator&
|
||||
i,
|
||||
const std::unordered_map<int64_t, Dynamics::DstNodeCollideMap_>::iterator&
|
||||
j,
|
||||
const std::unordered_map<int, SrcPartCollideMap_>::iterator& k,
|
||||
const std::unordered_map<int, Object::Ref<Collision> >::iterator& l) {
|
||||
// Handle disconnect equivalents if they were colliding.
|
||||
if (l->second->collide) {
|
||||
@ -367,7 +369,7 @@ void Dynamics::Impl::HandleDisconnect(
|
||||
k->second.dst_part_collisions.erase(l);
|
||||
}
|
||||
|
||||
void Dynamics::ProcessCollisions() {
|
||||
void Dynamics::ProcessCollision_() {
|
||||
processing_collisions_ = true;
|
||||
|
||||
collision_count_ = 0;
|
||||
@ -441,10 +443,10 @@ void Dynamics::ProcessCollisions() {
|
||||
// Process all standard collisions. This will trigger our callback which
|
||||
// do the real work (add collisions to list, store commands to be
|
||||
// called, etc).
|
||||
dSpaceCollide(ode_space_, this, &DoCollideCallback);
|
||||
dSpaceCollide(ode_space_, this, &DoCollideCallback_);
|
||||
|
||||
// Collide our trimeshes against everything.
|
||||
collision_cache_->CollideAgainstSpace(ode_space_, this, &DoCollideCallback);
|
||||
collision_cache_->CollideAgainstSpace(ode_space_, this, &DoCollideCallback_);
|
||||
|
||||
// Do a bit of precalc each cycle.
|
||||
collision_cache_->Precalc();
|
||||
@ -453,9 +455,9 @@ void Dynamics::ProcessCollisions() {
|
||||
// setting parts' currently-colliding-with lists
|
||||
// based on current info,
|
||||
// removing unclaimed collisions and empty groups.
|
||||
std::unordered_map<int64_t, SrcNodeCollideMap>::iterator i_next;
|
||||
std::unordered_map<int64_t, DstNodeCollideMap>::iterator j_next;
|
||||
std::unordered_map<int, SrcPartCollideMap>::iterator k_next;
|
||||
std::unordered_map<int64_t, SrcNodeCollideMap_>::iterator i_next;
|
||||
std::unordered_map<int64_t, DstNodeCollideMap_>::iterator j_next;
|
||||
std::unordered_map<int, SrcPartCollideMap_>::iterator k_next;
|
||||
std::unordered_map<int, Object::Ref<Collision> >::iterator l_next;
|
||||
for (auto i = impl_->node_collisions_.begin();
|
||||
i != impl_->node_collisions_.end(); i = i_next) {
|
||||
@ -507,26 +509,26 @@ void Dynamics::ProcessCollisions() {
|
||||
collision_events_.clear();
|
||||
}
|
||||
|
||||
void Dynamics::process() {
|
||||
void Dynamics::Process() {
|
||||
in_process_ = true;
|
||||
// Update this once so we can recycle results.
|
||||
real_time_ = g_core->GetAppTimeMillisecs();
|
||||
ProcessCollisions();
|
||||
ProcessCollision_();
|
||||
dWorldQuickStep(ode_world_, kGameStepSeconds);
|
||||
dJointGroupEmpty(ode_contact_group_);
|
||||
in_process_ = false;
|
||||
}
|
||||
|
||||
void Dynamics::DoCollideCallback(void* data, dGeomID o1, dGeomID o2) {
|
||||
void Dynamics::DoCollideCallback_(void* data, dGeomID o1, dGeomID o2) {
|
||||
auto* d = static_cast<Dynamics*>(data);
|
||||
d->CollideCallback(o1, o2);
|
||||
d->CollideCallback_(o1, o2);
|
||||
}
|
||||
|
||||
// Run collisions for everything. Store any callbacks that will need to be made
|
||||
// and run them after all collision constraints are made.
|
||||
// This way we know all bodies and their associated nodes, etc are valid
|
||||
// throughout collision processing.
|
||||
void Dynamics::CollideCallback(dGeomID o1, dGeomID o2) {
|
||||
void Dynamics::CollideCallback_(dGeomID o1, dGeomID o2) {
|
||||
dBodyID b1 = dGeomGetBody(o1);
|
||||
dBodyID b2 = dGeomGetBody(o2);
|
||||
|
||||
@ -1103,7 +1105,7 @@ void Dynamics::CollideCallback(dGeomID o1, dGeomID o2) {
|
||||
}
|
||||
}
|
||||
|
||||
void Dynamics::ShutdownODE() {
|
||||
void Dynamics::ShutdownODE_() {
|
||||
if (ode_space_) {
|
||||
dSpaceDestroy(ode_space_);
|
||||
ode_space_ = nullptr;
|
||||
@ -1118,8 +1120,8 @@ void Dynamics::ShutdownODE() {
|
||||
}
|
||||
}
|
||||
|
||||
void Dynamics::ResetODE() {
|
||||
ShutdownODE();
|
||||
void Dynamics::ResetODE_() {
|
||||
ShutdownODE_();
|
||||
ode_world_ = dWorldCreate();
|
||||
assert(ode_world_);
|
||||
dWorldSetGravity(ode_world_, 0, -20, 0);
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
#define BALLISTICA_SCENE_V1_DYNAMICS_DYNAMICS_H_
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "ballistica/base/base.h"
|
||||
@ -16,12 +15,12 @@ namespace ballistica::scene_v1 {
|
||||
|
||||
class Dynamics : public Object {
|
||||
public:
|
||||
explicit Dynamics(Scene* scene_in);
|
||||
explicit Dynamics(Scene* scene);
|
||||
~Dynamics() override;
|
||||
void Draw(base::FrameDef* frame_def); // Draw any debug stuff, etc.
|
||||
auto ode_world() -> dWorldID { return ode_world_; }
|
||||
auto getContactGroup() -> dJointGroupID { return ode_contact_group_; }
|
||||
auto space() -> dSpaceID { return ode_space_; }
|
||||
auto ode_contact_group() -> dJointGroupID { return ode_contact_group_; }
|
||||
auto ode_space() -> dSpaceID { return ode_space_; }
|
||||
|
||||
// Discontinues a collision. Used by parts when changing materials
|
||||
// so that new collisions may enter effect.
|
||||
@ -37,6 +36,7 @@ class Dynamics : public Object {
|
||||
: active_collide_src_node_)
|
||||
.Get();
|
||||
}
|
||||
|
||||
// Used by collision callbacks - internal.
|
||||
auto GetActiveCollideDstNode() -> Node* {
|
||||
assert(active_collision_);
|
||||
@ -49,19 +49,19 @@ class Dynamics : public Object {
|
||||
}
|
||||
|
||||
// Used by collide message handlers.
|
||||
void set_collide_message_state(bool inCollideMessageIn,
|
||||
bool target_other_in = false) {
|
||||
in_collide_message_ = inCollideMessageIn;
|
||||
collide_message_reverse_order_ = target_other_in;
|
||||
void set_collide_message_state(bool in_collide_message,
|
||||
bool target_other = false) {
|
||||
in_collide_message_ = in_collide_message;
|
||||
collide_message_reverse_order_ = target_other;
|
||||
}
|
||||
auto in_collide_message() const -> bool { return in_collide_message_; }
|
||||
void process();
|
||||
void increment_skid_sound_count() { skid_sound_count_++; }
|
||||
void decrement_skid_sound_count() { skid_sound_count_--; }
|
||||
auto skid_sound_count() const -> int { return skid_sound_count_; }
|
||||
void incrementRollSoundCount() { roll_sound_count_++; }
|
||||
void decrement_roll_sound_count() { roll_sound_count_--; }
|
||||
auto getRollSoundCount() const -> int { return roll_sound_count_; }
|
||||
auto in_collide_message() const { return in_collide_message_; }
|
||||
void Process();
|
||||
void IncrementSkidSoundCount() { skid_sound_count_++; }
|
||||
void DecrementSkidSoundCount() { skid_sound_count_--; }
|
||||
auto skid_sound_count() const { return skid_sound_count_; }
|
||||
void IncrementRollSoundCount() { roll_sound_count_++; }
|
||||
void DecrementRollSoundCount() { roll_sound_count_--; }
|
||||
auto roll_sound_count() const { return roll_sound_count_; }
|
||||
|
||||
// We do some fancy collision testing stuff for trimeshes instead
|
||||
// of going through regular ODE space collision testing.. so we have
|
||||
@ -69,55 +69,52 @@ class Dynamics : public Object {
|
||||
void AddTrimesh(dGeomID g);
|
||||
void RemoveTrimesh(dGeomID g);
|
||||
|
||||
auto collision_count() const -> int { return collision_count_; }
|
||||
auto process_real_time() const -> millisecs_t { return real_time_; }
|
||||
auto last_impact_sound_time() const -> millisecs_t {
|
||||
return last_impact_sound_time_;
|
||||
}
|
||||
auto in_process() const -> bool { return in_process_; }
|
||||
auto collision_count() const { return collision_count_; }
|
||||
auto process_real_time() const { return real_time_; }
|
||||
auto last_impact_sound_time() const { return last_impact_sound_time_; }
|
||||
auto in_process() const { return in_process_; }
|
||||
|
||||
private:
|
||||
auto AreColliding(const Part& p1, const Part& p2) -> bool;
|
||||
class SrcNodeCollideMap;
|
||||
class DstNodeCollideMap;
|
||||
class SrcPartCollideMap;
|
||||
class CollisionEvent;
|
||||
class CollisionReset;
|
||||
class Impl;
|
||||
std::vector<CollisionReset> collision_resets_;
|
||||
auto AreColliding_(const Part& p1, const Part& p2) -> bool;
|
||||
class SrcNodeCollideMap_;
|
||||
class DstNodeCollideMap_;
|
||||
class SrcPartCollideMap_;
|
||||
class CollisionEvent_;
|
||||
class CollisionReset_;
|
||||
class Impl_;
|
||||
std::vector<CollisionReset_> collision_resets_;
|
||||
|
||||
// Return a collision object between these two parts,
|
||||
// creating a new one if need be.
|
||||
auto GetCollision(Part* p1, Part* p2, MaterialContext** cc1,
|
||||
MaterialContext** cc2) -> Collision*;
|
||||
|
||||
std::vector<CollisionEvent> collision_events_;
|
||||
void ResetODE();
|
||||
void ShutdownODE();
|
||||
static void DoCollideCallback(void* data, dGeomID o1, dGeomID o2);
|
||||
void CollideCallback(dGeomID o1, dGeomID o2);
|
||||
void ProcessCollisions();
|
||||
std::vector<CollisionEvent_> collision_events_;
|
||||
void ResetODE_();
|
||||
void ShutdownODE_();
|
||||
static void DoCollideCallback_(void* data, dGeomID o1, dGeomID o2);
|
||||
void CollideCallback_(dGeomID o1, dGeomID o2);
|
||||
void ProcessCollision_();
|
||||
|
||||
std::unique_ptr<Impl> impl_;
|
||||
bool processing_collisions_{};
|
||||
int skid_sound_count_{};
|
||||
int roll_sound_count_{};
|
||||
int collision_count_{};
|
||||
bool in_process_ : 1 {};
|
||||
bool in_collide_message_ : 1 {};
|
||||
bool collide_message_reverse_order_ : 1 {};
|
||||
bool processing_collisions_ : 1 {};
|
||||
dWorldID ode_world_{};
|
||||
dJointGroupID ode_contact_group_{};
|
||||
dSpaceID ode_space_{};
|
||||
millisecs_t real_time_{};
|
||||
bool in_process_{};
|
||||
std::vector<dGeomID> trimeshes_;
|
||||
millisecs_t last_impact_sound_time_{};
|
||||
int skid_sound_count_{};
|
||||
int roll_sound_count_{};
|
||||
int collision_count_{};
|
||||
Scene* scene_{};
|
||||
bool in_collide_message_{};
|
||||
bool collide_message_reverse_order_{};
|
||||
Collision* active_collision_{};
|
||||
Object::WeakRef<Node> active_collide_src_node_;
|
||||
Object::WeakRef<Node> active_collide_dst_node_;
|
||||
std::vector<dGeomID> trimeshes_;
|
||||
std::unique_ptr<Impl_> impl_;
|
||||
std::unique_ptr<base::CollisionCache> collision_cache_;
|
||||
friend class Impl;
|
||||
};
|
||||
|
||||
} // namespace ballistica::scene_v1
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
#include "ballistica/scene_v1/dynamics/material/impact_sound_material_action.h"
|
||||
|
||||
#include "ballistica/base/graphics/graphics_server.h"
|
||||
#include "ballistica/base/audio/audio.h"
|
||||
#include "ballistica/scene_v1/dynamics/dynamics.h"
|
||||
#include "ballistica/scene_v1/dynamics/material/material_context.h"
|
||||
#include "ballistica/scene_v1/support/client_session.h"
|
||||
@ -50,10 +50,8 @@ void ImpactSoundMaterialAction::Apply(MaterialContext* context,
|
||||
assert(context->dynamics.Exists());
|
||||
assert(context->dynamics->in_process());
|
||||
|
||||
// For now lets avoid this in low-quality graphics mode (should we make
|
||||
// a low-quality sound mode?)
|
||||
if (g_base->graphics_server
|
||||
&& g_base->graphics_server->quality() < base::GraphicsQuality::kMedium) {
|
||||
// Avoid this if we're cutting corners.
|
||||
if (g_base->audio->UseLowQualityAudio()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -42,7 +42,7 @@ MaterialContext::SkidSoundEntry::SkidSoundEntry(
|
||||
assert(context->dynamics.Exists());
|
||||
#endif
|
||||
assert(context->dynamics->in_process());
|
||||
context->dynamics->increment_skid_sound_count();
|
||||
context->dynamics->IncrementSkidSoundCount();
|
||||
}
|
||||
|
||||
MaterialContext::SkidSoundEntry::SkidSoundEntry(MaterialContext* context_in,
|
||||
@ -57,13 +57,13 @@ MaterialContext::SkidSoundEntry::SkidSoundEntry(MaterialContext* context_in,
|
||||
assert(context);
|
||||
assert(context->dynamics.Exists());
|
||||
assert(context->dynamics->in_process());
|
||||
context->dynamics->increment_skid_sound_count();
|
||||
context->dynamics->IncrementSkidSoundCount();
|
||||
}
|
||||
|
||||
MaterialContext::SkidSoundEntry::~SkidSoundEntry() {
|
||||
assert(context);
|
||||
assert(context->dynamics.Exists());
|
||||
context->dynamics->decrement_skid_sound_count();
|
||||
context->dynamics->DecrementSkidSoundCount();
|
||||
if (playing) {
|
||||
g_base->audio->PushSourceFadeOutCall(play_id, 200);
|
||||
}
|
||||
@ -81,7 +81,7 @@ MaterialContext::RollSoundEntry::RollSoundEntry(MaterialContext* context_in,
|
||||
assert(context);
|
||||
assert(context->dynamics.Exists());
|
||||
assert(context->dynamics->in_process());
|
||||
context->dynamics->incrementRollSoundCount();
|
||||
context->dynamics->IncrementRollSoundCount();
|
||||
}
|
||||
|
||||
MaterialContext::RollSoundEntry::RollSoundEntry(
|
||||
@ -90,13 +90,13 @@ MaterialContext::RollSoundEntry::RollSoundEntry(
|
||||
assert(context);
|
||||
assert(context->dynamics.Exists());
|
||||
assert(context->dynamics->in_process());
|
||||
context->dynamics->incrementRollSoundCount();
|
||||
context->dynamics->IncrementRollSoundCount();
|
||||
}
|
||||
|
||||
MaterialContext::RollSoundEntry::~RollSoundEntry() {
|
||||
assert(context);
|
||||
assert(context->dynamics.Exists());
|
||||
context->dynamics->decrement_roll_sound_count();
|
||||
context->dynamics->DecrementRollSoundCount();
|
||||
if (playing) {
|
||||
g_base->audio->PushSourceFadeOutCall(play_id, 200);
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
#include "ballistica/scene_v1/dynamics/material/roll_sound_material_action.h"
|
||||
|
||||
#include "ballistica/base/graphics/graphics_server.h"
|
||||
#include "ballistica/base/audio/audio.h"
|
||||
#include "ballistica/scene_v1/dynamics/dynamics.h"
|
||||
#include "ballistica/scene_v1/dynamics/material/material_context.h"
|
||||
#include "ballistica/scene_v1/support/client_session.h"
|
||||
@ -34,16 +34,14 @@ void RollSoundMaterialAction::Apply(MaterialContext* context,
|
||||
assert(context->dynamics.Exists());
|
||||
assert(context->dynamics->in_process());
|
||||
|
||||
// For now lets avoid this in low-quality graphics mode
|
||||
// (should we make a low-quality sound mode?)
|
||||
if (g_base->graphics
|
||||
&& g_base->graphics_server->quality() < base::GraphicsQuality::kMedium) {
|
||||
// Avoid this if we're cutting corners.
|
||||
if (g_base->audio->UseLowQualityAudio()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Let's limit the amount of skid-sounds we spawn, otherwise we'll
|
||||
// start using up all our sound resources on skids when things get messy
|
||||
if (context->dynamics->getRollSoundCount() < 2) {
|
||||
if (context->dynamics->roll_sound_count() < 2) {
|
||||
context->roll_sounds.emplace_back(context, sound.Get(), target_impulse,
|
||||
volume);
|
||||
context->complex_sound = true;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
#include "ballistica/scene_v1/dynamics/material/skid_sound_material_action.h"
|
||||
|
||||
#include "ballistica/base/graphics/graphics_server.h"
|
||||
#include "ballistica/base/audio/audio.h"
|
||||
#include "ballistica/scene_v1/dynamics/dynamics.h"
|
||||
#include "ballistica/scene_v1/dynamics/material/material_context.h"
|
||||
#include "ballistica/scene_v1/support/client_session.h"
|
||||
@ -34,10 +34,8 @@ void SkidSoundMaterialAction::Apply(MaterialContext* context,
|
||||
assert(context->dynamics.Exists());
|
||||
assert(context->dynamics->in_process());
|
||||
|
||||
// For now lets avoid this in low-quality graphics mode
|
||||
// (should we make a low-quality sound mode?).
|
||||
if (g_base->graphics_server
|
||||
&& g_base->graphics_server->quality() < base::GraphicsQuality::kMedium) {
|
||||
// Avoid this if we're cutting corners.
|
||||
if (g_base->audio->UseLowQualityAudio()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -65,23 +65,23 @@ RigidBody::RigidBody(int id_in, Part* part_in, Type type_in, Shape shape_in,
|
||||
case Shape::kSphere: {
|
||||
dimensions_[0] = dimensions_[1] = dimensions_[2] = 0.3f;
|
||||
geoms_.resize(1);
|
||||
geoms_[0] = dCreateSphere(dynamics_->space(), dimensions_[0]);
|
||||
geoms_[0] = dCreateSphere(dynamics_->ode_space(), dimensions_[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
case Shape::kBox: {
|
||||
dimensions_[0] = dimensions_[1] = dimensions_[2] = 0.6f;
|
||||
geoms_.resize(1);
|
||||
geoms_[0] = dCreateBox(dynamics_->space(), dimensions_[0], dimensions_[1],
|
||||
dimensions_[2]);
|
||||
geoms_[0] = dCreateBox(dynamics_->ode_space(), dimensions_[0],
|
||||
dimensions_[1], dimensions_[2]);
|
||||
break;
|
||||
}
|
||||
|
||||
case Shape::kCapsule: {
|
||||
dimensions_[0] = dimensions_[1] = 0.3f;
|
||||
geoms_.resize(1);
|
||||
geoms_[0] =
|
||||
dCreateCCylinder(dynamics_->space(), dimensions_[0], dimensions_[1]);
|
||||
geoms_[0] = dCreateCCylinder(dynamics_->ode_space(), dimensions_[0],
|
||||
dimensions_[1]);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -98,14 +98,15 @@ RigidBody::RigidBody(int id_in, Part* part_in, Type type_in, Shape shape_in,
|
||||
Vector3f p =
|
||||
Matrix44fRotate(Vector3f(0, 1, 0), static_cast<float>(i) * inc)
|
||||
* Vector3f(offset, 0, 0);
|
||||
geoms_[i * 2] = dCreateGeomTransform(dynamics_->space());
|
||||
geoms_[i * 2] = dCreateGeomTransform(dynamics_->ode_space());
|
||||
geoms_[i * 2 + 1] = dCreateSphere(nullptr, sub_rad);
|
||||
dGeomTransformSetGeom(geoms_[i * 2], geoms_[i * 2 + 1]);
|
||||
dGeomSetPosition(geoms_[i * 2 + 1], p.v[0], p.v[1], p.v[2]);
|
||||
}
|
||||
|
||||
// One last center sphere to keep stuff from getting stuck in our middle.
|
||||
geoms_[geoms_.size() - 1] = dCreateSphere(dynamics_->space(), sub_rad);
|
||||
geoms_[geoms_.size() - 1] =
|
||||
dCreateSphere(dynamics_->ode_space(), sub_rad);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -129,9 +129,6 @@ FlagNode::FlagNode(Scene* scene) : Node(scene, node_type), part_(this) {
|
||||
|
||||
mesh_.SetIndexData(indices);
|
||||
mesh_.SetStaticData(v_static);
|
||||
|
||||
// Create our shadow set.
|
||||
UpdateForGraphicsQuality(g_base->graphics_server->quality());
|
||||
}
|
||||
|
||||
auto FlagNode::getPosition() const -> std::vector<float> {
|
||||
@ -257,6 +254,11 @@ void FlagNode::HandleMessage(const char* data_in) {
|
||||
}
|
||||
|
||||
void FlagNode::Draw(base::FrameDef* frame_def) {
|
||||
if (graphics_quality_ != frame_def->quality()) {
|
||||
graphics_quality_ = frame_def->quality();
|
||||
UpdateForGraphicsQuality(graphics_quality_);
|
||||
}
|
||||
|
||||
// Flag cloth.
|
||||
{
|
||||
// Update the dynamic portion of our mesh data.
|
||||
@ -309,47 +311,60 @@ void FlagNode::Draw(base::FrameDef* frame_def) {
|
||||
c.SetTexture(g_base->assets->SysTexture(base::SysTextureID::kShadow));
|
||||
c.SetTransparent(true);
|
||||
|
||||
FullShadowSet* full_shadows = full_shadow_set_.Get();
|
||||
// Update our shadow objects.
|
||||
if (!g_core->HeadlessMode()) {
|
||||
dBodyID b = body_->body();
|
||||
assert(b);
|
||||
dVector3 p;
|
||||
if (FullShadowSet* full_shadows = full_shadow_set_.Get()) {
|
||||
full_shadows->shadow_flag_.SetPosition(
|
||||
flag_points_[kFlagSizeX * (kFlagSizeY / 2) + (kFlagSizeX / 2)]);
|
||||
dBodyGetRelPointPos(b, 0, 0, kFlagHeight * -0.4f, p);
|
||||
full_shadows->shadow_pole_bottom_.SetPosition(Vector3f(p));
|
||||
full_shadows->shadow_pole_middle_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(b)));
|
||||
dBodyGetRelPointPos(b, 0, 0, kFlagHeight * 0.4f, p);
|
||||
full_shadows->shadow_pole_top_.SetPosition(Vector3f(p));
|
||||
// Pole bottom.
|
||||
{
|
||||
full_shadows->shadow_pole_bottom_.GetValues(&s_scale, &s_density);
|
||||
const Vector3f& p(full_shadows->shadow_pole_bottom_.GetPosition());
|
||||
g_base->graphics->DrawBlotch(p, 0.4f * s_scale, 0, 0, 0,
|
||||
s_density * 0.25f);
|
||||
}
|
||||
|
||||
if (full_shadows) {
|
||||
// Pole bottom.
|
||||
{
|
||||
full_shadows->shadow_pole_bottom_.GetValues(&s_scale, &s_density);
|
||||
const Vector3f& p(full_shadows->shadow_pole_bottom_.GetPosition());
|
||||
g_base->graphics->DrawBlotch(p, 0.4f * s_scale, 0, 0, 0,
|
||||
s_density * 0.25f);
|
||||
}
|
||||
// Pole middle.
|
||||
{
|
||||
full_shadows->shadow_pole_middle_.GetValues(&s_scale, &s_density);
|
||||
const Vector3f& p(full_shadows->shadow_pole_middle_.GetPosition());
|
||||
g_base->graphics->DrawBlotch(p, 0.4f * s_scale, 0, 0, 0,
|
||||
s_density * 0.25f);
|
||||
}
|
||||
|
||||
// Pole middle.
|
||||
{
|
||||
full_shadows->shadow_pole_middle_.GetValues(&s_scale, &s_density);
|
||||
const Vector3f& p(full_shadows->shadow_pole_middle_.GetPosition());
|
||||
g_base->graphics->DrawBlotch(p, 0.4f * s_scale, 0, 0, 0,
|
||||
s_density * 0.25f);
|
||||
}
|
||||
// Pole top.
|
||||
{
|
||||
full_shadows->shadow_pole_middle_.GetValues(&s_scale, &s_density);
|
||||
const Vector3f& p(full_shadows->shadow_pole_top_.GetPosition());
|
||||
g_base->graphics->DrawBlotch(p, 0.4f * s_scale, 0, 0, 0,
|
||||
s_density * 0.25f);
|
||||
}
|
||||
|
||||
// Pole top.
|
||||
{
|
||||
full_shadows->shadow_pole_middle_.GetValues(&s_scale, &s_density);
|
||||
const Vector3f& p(full_shadows->shadow_pole_top_.GetPosition());
|
||||
g_base->graphics->DrawBlotch(p, 0.4f * s_scale, 0, 0, 0,
|
||||
s_density * 0.25f);
|
||||
}
|
||||
// Flag center.
|
||||
{
|
||||
full_shadows->shadow_flag_.GetValues(&s_scale, &s_density);
|
||||
const Vector3f& p(full_shadows->shadow_flag_.GetPosition());
|
||||
g_base->graphics->DrawBlotch(p, 0.8f * s_scale, 0, 0, 0,
|
||||
s_density * 0.3f);
|
||||
}
|
||||
|
||||
// Flag center.
|
||||
{
|
||||
full_shadows->shadow_flag_.GetValues(&s_scale, &s_density);
|
||||
const Vector3f& p(full_shadows->shadow_flag_.GetPosition());
|
||||
} else if (SimpleShadowSet* simple_shadows = simple_shadow_set_.Get()) {
|
||||
dBodyGetRelPointPos(b, 0, 0, kFlagHeight * -0.3f, p);
|
||||
simple_shadows->shadow_.SetPosition(Vector3f(p));
|
||||
simple_shadows->shadow_.GetValues(&s_scale, &s_density);
|
||||
const Vector3f& p(simple_shadows->shadow_.GetPosition());
|
||||
g_base->graphics->DrawBlotch(p, 0.8f * s_scale, 0, 0, 0,
|
||||
s_density * 0.3f);
|
||||
s_density * 0.5f);
|
||||
}
|
||||
} else {
|
||||
SimpleShadowSet* simple_shadows = simple_shadow_set_.Get();
|
||||
assert(simple_shadows);
|
||||
simple_shadows->shadow_.GetValues(&s_scale, &s_density);
|
||||
const Vector3f& p(simple_shadows->shadow_.GetPosition());
|
||||
g_base->graphics->DrawBlotch(p, 0.8f * s_scale, 0, 0, 0,
|
||||
s_density * 0.5f);
|
||||
}
|
||||
c.Submit();
|
||||
}
|
||||
@ -398,30 +413,6 @@ void FlagNode::Step() {
|
||||
// FIXME: This should probably happen for RBDs automatically?
|
||||
body_->UpdateBlending();
|
||||
|
||||
// Update our shadow objects.
|
||||
dBodyID b = body_->body();
|
||||
assert(b);
|
||||
|
||||
if (!g_core->HeadlessMode()) {
|
||||
dVector3 p;
|
||||
FullShadowSet* full_shadows = full_shadow_set_.Get();
|
||||
if (full_shadows) {
|
||||
full_shadows->shadow_flag_.SetPosition(
|
||||
flag_points_[kFlagSizeX * (kFlagSizeY / 2) + (kFlagSizeX / 2)]);
|
||||
dBodyGetRelPointPos(b, 0, 0, kFlagHeight * -0.4f, p);
|
||||
full_shadows->shadow_pole_bottom_.SetPosition(Vector3f(p));
|
||||
full_shadows->shadow_pole_middle_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(b)));
|
||||
dBodyGetRelPointPos(b, 0, 0, kFlagHeight * 0.4f, p);
|
||||
full_shadows->shadow_pole_top_.SetPosition(Vector3f(p));
|
||||
} else {
|
||||
SimpleShadowSet* simple_shadows = simple_shadow_set_.Get();
|
||||
assert(simple_shadows);
|
||||
dBodyGetRelPointPos(b, 0, 0, kFlagHeight * -0.3f, p);
|
||||
simple_shadows->shadow_.SetPosition(Vector3f(p));
|
||||
}
|
||||
}
|
||||
|
||||
if (dBodyIsEnabled(body_->body())) {
|
||||
// Try to keep upright by pushing the top of the
|
||||
// flag to be above the bottom.
|
||||
@ -675,10 +666,6 @@ void FlagNode::GetRigidBodyPickupLocations(int id, float* obj, float* character,
|
||||
hand1[2] = -0.05f;
|
||||
}
|
||||
|
||||
void FlagNode::OnGraphicsQualityChanged(base::GraphicsQuality q) {
|
||||
UpdateForGraphicsQuality(q);
|
||||
}
|
||||
|
||||
void FlagNode::UpdateForGraphicsQuality(base::GraphicsQuality quality) {
|
||||
if (!g_core->HeadlessMode()) {
|
||||
if (quality >= base::GraphicsQuality::kMedium) {
|
||||
|
||||
@ -44,10 +44,13 @@ class FlagNode : public Node {
|
||||
void UpdateDimensions();
|
||||
void ResetFlagMesh();
|
||||
void UpdateFlagMesh();
|
||||
void OnGraphicsQualityChanged(base::GraphicsQuality q) override;
|
||||
void UpdateForGraphicsQuality(base::GraphicsQuality q);
|
||||
void UpdateSpringPoint(int p1, int p2, float rest_length);
|
||||
base::AreaOfInterest* area_of_interest_ = nullptr;
|
||||
|
||||
base::GraphicsQuality graphics_quality_{};
|
||||
bool light_weight_ : 1 {};
|
||||
bool have_flag_impulse_ : 1 {};
|
||||
base::AreaOfInterest* area_of_interest_{};
|
||||
Part part_;
|
||||
std::vector<float> color_ = {1.0f, 1.0f, 1.0f};
|
||||
Object::Ref<RigidBody> body_;
|
||||
@ -56,15 +59,13 @@ class FlagNode : public Node {
|
||||
Object::Ref<FullShadowSet> full_shadow_set_;
|
||||
Object::Ref<SimpleShadowSet> simple_shadow_set_;
|
||||
int wind_rand_{};
|
||||
int footing_{};
|
||||
float wind_rand_x_{};
|
||||
float wind_rand_y_{};
|
||||
float wind_rand_z_{};
|
||||
float flag_impulse_add_x_{};
|
||||
float flag_impulse_add_y_{};
|
||||
float flag_impulse_add_z_{};
|
||||
bool have_flag_impulse_{};
|
||||
int footing_{};
|
||||
bool light_weight_{};
|
||||
Vector3f flag_points_[25]{};
|
||||
Vector3f flag_normals_[25]{};
|
||||
Vector3f flag_velocities_[25]{};
|
||||
|
||||
@ -40,7 +40,6 @@ class Node : public Object {
|
||||
|
||||
/// Called when the language changes.
|
||||
virtual void OnLanguageChange() {}
|
||||
virtual void OnGraphicsQualityChanged(base::GraphicsQuality q) {}
|
||||
|
||||
/// The node can rule out collisions between particular bodies using this.
|
||||
virtual auto PreFilterCollision(RigidBody* b1, RigidBody* r2) -> bool {
|
||||
|
||||
@ -576,7 +576,7 @@ auto PropNode::CollideCallback(dContact* c, int count,
|
||||
dBodyGetMass(b2, &m);
|
||||
dJointID j =
|
||||
dJointCreateFixed(scene()->dynamics()->ode_world(),
|
||||
scene()->dynamics()->getContactGroup());
|
||||
scene()->dynamics()->ode_contact_group());
|
||||
dJointAttach(j, b1, b2);
|
||||
dJointSetFixed(j);
|
||||
dJointSetFixedSpringMode(j, 1, 1, false);
|
||||
|
||||
@ -260,7 +260,7 @@ void ShieldNode::Draw(base::FrameDef* frame_def) {
|
||||
c.Submit();
|
||||
|
||||
// Nifty intersection effects in fancy graphics mode.
|
||||
if (frame_def->has_depth_texture()) {
|
||||
if (frame_def->HasDepthTexture()) {
|
||||
base::ShieldComponent c2(frame_def->overlay_3d_pass());
|
||||
{
|
||||
auto xf = c2.ScopedTransform();
|
||||
@ -273,7 +273,7 @@ void ShieldNode::Draw(base::FrameDef* frame_def) {
|
||||
}
|
||||
c2.Submit();
|
||||
}
|
||||
if (frame_def->has_depth_texture()) {
|
||||
if (frame_def->HasDepthTexture()) {
|
||||
base::PostProcessComponent c2(frame_def->blit_pass());
|
||||
c2.SetNormalDistort(distort);
|
||||
{
|
||||
|
||||
@ -916,9 +916,6 @@ SpazNode::SpazNode(Scene* scene)
|
||||
// Give joints initial vals.
|
||||
UpdateJoints();
|
||||
|
||||
// FIXME: should do this on draw.
|
||||
UpdateForGraphicsQuality(g_base->graphics_server->quality());
|
||||
|
||||
// We want to have an area of interest by default.
|
||||
SetIsAreaOfInterest(true);
|
||||
|
||||
@ -2079,40 +2076,6 @@ void SpazNode::Step() {
|
||||
}
|
||||
}
|
||||
|
||||
// Update shadows.
|
||||
#if !BA_HEADLESS_BUILD
|
||||
FullShadowSet* full_shadows = full_shadow_set_.Get();
|
||||
if (full_shadows) {
|
||||
full_shadows->torso_shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(body_torso_->body())));
|
||||
full_shadows->head_shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(body_head_->body())));
|
||||
full_shadows->pelvis_shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(body_pelvis_->body())));
|
||||
full_shadows->lower_left_leg_shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(lower_left_leg_body_->body())));
|
||||
full_shadows->lower_right_leg_shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(lower_right_leg_body_->body())));
|
||||
full_shadows->upper_left_leg_shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(upper_left_leg_body_->body())));
|
||||
full_shadows->upper_right_leg_shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(upper_right_leg_body_->body())));
|
||||
full_shadows->lower_right_arm_shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(lower_right_arm_body_->body())));
|
||||
full_shadows->upper_right_arm_shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(upper_right_arm_body_->body())));
|
||||
full_shadows->lower_left_arm_shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(lower_left_arm_body_->body())));
|
||||
full_shadows->upper_left_arm_shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(upper_left_arm_body_->body())));
|
||||
} else {
|
||||
SimpleShadowSet* simple_shadows = simple_shadow_set_.Get();
|
||||
assert(simple_shadows);
|
||||
simple_shadows->shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(body_pelvis_->body())));
|
||||
}
|
||||
#endif // !BA_HEADLESS_BUILD
|
||||
|
||||
// Update wings if we've got 'em.
|
||||
if (wings_) {
|
||||
float maxDist = 0.8f;
|
||||
@ -4547,6 +4510,11 @@ static void DrawRadialMeter(base::MeshIndexedSimpleFull* m,
|
||||
void SpazNode::Draw(base::FrameDef* frame_def) {
|
||||
#if !BA_HEADLESS_BUILD
|
||||
|
||||
if (graphics_quality_ != frame_def->quality()) {
|
||||
graphics_quality_ = frame_def->quality();
|
||||
UpdateForGraphicsQuality(graphics_quality_);
|
||||
}
|
||||
|
||||
#if BA_OSTYPE_MACOS
|
||||
if (g_base->graphics_server->renderer()->debug_draw_mode()) {
|
||||
base::SimpleComponent c(frame_def->overlay_3d_pass());
|
||||
@ -5356,49 +5324,68 @@ void SpazNode::Draw(base::FrameDef* frame_def) {
|
||||
sc[2] = weight * freeze_color[2] + (1.0f - weight) * sc[2];
|
||||
}
|
||||
|
||||
FullShadowSet* full_shadows = full_shadow_set_.Get();
|
||||
if (full_shadows) {
|
||||
DrawBrightSpot(full_shadows->lower_left_leg_shadow_, 0.3f * death_scale,
|
||||
death_fade * (frozen_ ? 0.3f : 0.2f), sc);
|
||||
DrawBrightSpot(full_shadows->lower_right_leg_shadow_, 0.3f * death_scale,
|
||||
death_fade * (frozen_ ? 0.3f : 0.2f), sc);
|
||||
DrawBrightSpot(full_shadows->head_shadow_, 0.45f * death_scale,
|
||||
death_fade * (frozen_ ? 0.8f : 0.14f), sc);
|
||||
}
|
||||
// Update and draw shadows.
|
||||
if (!g_core->HeadlessMode()) {
|
||||
if (FullShadowSet* full_shadows = full_shadow_set_.Get()) {
|
||||
full_shadows->torso_shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(body_torso_->body())));
|
||||
full_shadows->head_shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(body_head_->body())));
|
||||
full_shadows->pelvis_shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(body_pelvis_->body())));
|
||||
full_shadows->lower_left_leg_shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(lower_left_leg_body_->body())));
|
||||
full_shadows->lower_right_leg_shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(lower_right_leg_body_->body())));
|
||||
full_shadows->upper_left_leg_shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(upper_left_leg_body_->body())));
|
||||
full_shadows->upper_right_leg_shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(upper_right_leg_body_->body())));
|
||||
full_shadows->lower_right_arm_shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(lower_right_arm_body_->body())));
|
||||
full_shadows->upper_right_arm_shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(upper_right_arm_body_->body())));
|
||||
full_shadows->lower_left_arm_shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(lower_left_arm_body_->body())));
|
||||
full_shadows->upper_left_arm_shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(upper_left_arm_body_->body())));
|
||||
|
||||
if (full_shadows) {
|
||||
DrawShadow(full_shadows->torso_shadow_, 0.19f * death_scale, 0.9f, sc);
|
||||
DrawShadow(full_shadows->head_shadow_, 0.15f * death_scale, 0.7f, sc);
|
||||
DrawShadow(full_shadows->pelvis_shadow_, 0.15f * death_scale, 0.7f, sc);
|
||||
DrawShadow(full_shadows->lower_left_leg_shadow_, 0.08f * death_scale,
|
||||
1.0f, sc);
|
||||
DrawShadow(full_shadows->lower_right_leg_shadow_, 0.08f * death_scale,
|
||||
1.0f, sc);
|
||||
DrawShadow(full_shadows->upper_left_leg_shadow_, 0.08f * death_scale,
|
||||
1.0f, sc);
|
||||
DrawShadow(full_shadows->upper_right_leg_shadow_, 0.08f * death_scale,
|
||||
1.0f, sc);
|
||||
DrawShadow(full_shadows->upper_left_arm_shadow_, 0.08f * death_scale,
|
||||
0.5f, sc);
|
||||
DrawShadow(full_shadows->lower_left_arm_shadow_, 0.08f * death_scale,
|
||||
0.3f, sc);
|
||||
DrawShadow(full_shadows->lower_right_arm_shadow_, 0.08f * death_scale,
|
||||
0.3f, sc);
|
||||
DrawShadow(full_shadows->upper_right_arm_shadow_, 0.08f * death_scale,
|
||||
0.5f, sc);
|
||||
} else {
|
||||
SimpleShadowSet* simple_shadows = simple_shadow_set_.Get();
|
||||
assert(simple_shadows);
|
||||
DrawShadow(simple_shadows->shadow_, 0.2f * death_scale, 2.0f, sc);
|
||||
DrawBrightSpot(full_shadows->lower_left_leg_shadow_, 0.3f * death_scale,
|
||||
death_fade * (frozen_ ? 0.3f : 0.2f), sc);
|
||||
DrawBrightSpot(full_shadows->lower_right_leg_shadow_,
|
||||
0.3f * death_scale, death_fade * (frozen_ ? 0.3f : 0.2f),
|
||||
sc);
|
||||
DrawBrightSpot(full_shadows->head_shadow_, 0.45f * death_scale,
|
||||
death_fade * (frozen_ ? 0.8f : 0.14f), sc);
|
||||
DrawShadow(full_shadows->torso_shadow_, 0.19f * death_scale, 0.9f, sc);
|
||||
DrawShadow(full_shadows->head_shadow_, 0.15f * death_scale, 0.7f, sc);
|
||||
DrawShadow(full_shadows->pelvis_shadow_, 0.15f * death_scale, 0.7f, sc);
|
||||
DrawShadow(full_shadows->lower_left_leg_shadow_, 0.08f * death_scale,
|
||||
1.0f, sc);
|
||||
DrawShadow(full_shadows->lower_right_leg_shadow_, 0.08f * death_scale,
|
||||
1.0f, sc);
|
||||
DrawShadow(full_shadows->upper_left_leg_shadow_, 0.08f * death_scale,
|
||||
1.0f, sc);
|
||||
DrawShadow(full_shadows->upper_right_leg_shadow_, 0.08f * death_scale,
|
||||
1.0f, sc);
|
||||
DrawShadow(full_shadows->upper_left_arm_shadow_, 0.08f * death_scale,
|
||||
0.5f, sc);
|
||||
DrawShadow(full_shadows->lower_left_arm_shadow_, 0.08f * death_scale,
|
||||
0.3f, sc);
|
||||
DrawShadow(full_shadows->lower_right_arm_shadow_, 0.08f * death_scale,
|
||||
0.3f, sc);
|
||||
DrawShadow(full_shadows->upper_right_arm_shadow_, 0.08f * death_scale,
|
||||
0.5f, sc);
|
||||
} else if (SimpleShadowSet* simple_shadows = simple_shadow_set_.Get()) {
|
||||
simple_shadows->shadow_.SetPosition(
|
||||
Vector3f(dBodyGetPosition(body_pelvis_->body())));
|
||||
DrawShadow(simple_shadows->shadow_, 0.2f * death_scale, 2.0f, sc);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // !BA_HEADLESS_BUILD
|
||||
} // NOLINT (yes i know this is too big)
|
||||
|
||||
void SpazNode::OnGraphicsQualityChanged(base::GraphicsQuality q) {
|
||||
UpdateForGraphicsQuality(q);
|
||||
}
|
||||
|
||||
void SpazNode::UpdateForGraphicsQuality(base::GraphicsQuality quality) {
|
||||
#if !BA_HEADLESS_BUILD
|
||||
if (quality >= base::GraphicsQuality::kMedium) {
|
||||
|
||||
@ -277,7 +277,6 @@ class SpazNode : public Node {
|
||||
|
||||
// Reset to a standing, non-moving state at the given point.
|
||||
void Stand(float x, float y, float z, float angle);
|
||||
void OnGraphicsQualityChanged(base::GraphicsQuality q) override;
|
||||
void UpdateForGraphicsQuality(base::GraphicsQuality q);
|
||||
void UpdateAreaOfInterest();
|
||||
auto CollideCallback(dContact* c, int count, RigidBody* colliding_body,
|
||||
@ -515,6 +514,7 @@ class SpazNode : public Node {
|
||||
bool has_eyelids_{true};
|
||||
bool running_{};
|
||||
bool billboard_cross_out_{};
|
||||
base::GraphicsQuality graphics_quality_{};
|
||||
Object::Ref<RigidBody> hair_front_right_body_;
|
||||
JointFixedEF* hair_front_right_joint_{};
|
||||
Object::Ref<RigidBody> hair_front_left_body_;
|
||||
|
||||
@ -32,7 +32,12 @@ void PythonClassInputDevice::SetupType(PyTypeObject* cls) {
|
||||
"Attributes:\n"
|
||||
"\n"
|
||||
" allows_configuring (bool):\n"
|
||||
" Whether the input-device can be configured.\n"
|
||||
" Whether the input-device can be configured in the app.\n"
|
||||
"\n"
|
||||
" allows_configuring_in_system_settings (bool):\n"
|
||||
" Whether the input-device can be configured in the system.\n"
|
||||
" setings app. This can be used to redirect the user to go there\n"
|
||||
" if they attempt to configure the device.\n"
|
||||
"\n"
|
||||
" has_meaningful_button_names (bool):\n"
|
||||
" Whether button names returned by this instance match labels\n"
|
||||
@ -189,6 +194,16 @@ auto PythonClassInputDevice::tp_getattro(PythonClassInputDevice* self,
|
||||
} else {
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
} else if (!strcmp(s, "allows_configuring_in_system_settings")) {
|
||||
auto* d = self->input_device_delegate_->Get();
|
||||
if (!d) {
|
||||
throw Exception(PyExcType::kInputDeviceNotFound);
|
||||
}
|
||||
if (d->input_device().IsMFiController()) {
|
||||
Py_RETURN_TRUE;
|
||||
} else {
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
} else if (!strcmp(s, "has_meaningful_button_names")) {
|
||||
auto* d = self->input_device_delegate_->Get();
|
||||
if (!d) {
|
||||
|
||||
@ -483,9 +483,6 @@ void HostActivity::PruneSessionBaseTimers() {
|
||||
void HostActivity::OnScreenSizeChange() { scene()->OnScreenSizeChange(); }
|
||||
void HostActivity::LanguageChanged() { scene()->LanguageChanged(); }
|
||||
void HostActivity::DebugSpeedMultChanged() { UpdateStepTimerLength(); }
|
||||
void HostActivity::GraphicsQualityChanged(base::GraphicsQuality q) {
|
||||
scene()->GraphicsQualityChanged(q);
|
||||
}
|
||||
|
||||
void HostActivity::Draw(base::FrameDef* frame_def) {
|
||||
if (!started_) {
|
||||
|
||||
@ -57,7 +57,6 @@ class HostActivity : public SceneV1Context {
|
||||
void OnScreenSizeChange();
|
||||
void LanguageChanged();
|
||||
void DebugSpeedMultChanged();
|
||||
void GraphicsQualityChanged(base::GraphicsQuality q);
|
||||
|
||||
// Used to register python calls created in this context so we can make sure
|
||||
// they got properly cleaned up.
|
||||
|
||||
@ -154,16 +154,6 @@ void HostSession::LanguageChanged() {
|
||||
}
|
||||
}
|
||||
|
||||
void HostSession::GraphicsQualityChanged(base::GraphicsQuality q) {
|
||||
// Let our internal scene know.
|
||||
scene()->GraphicsQualityChanged(q);
|
||||
|
||||
// Let all our activities know.
|
||||
for (auto&& i : host_activities_) {
|
||||
i->GraphicsQualityChanged(q);
|
||||
}
|
||||
}
|
||||
|
||||
auto HostSession::DoesFillScreen() const -> bool {
|
||||
// FIXME not necessarily the case.
|
||||
return true;
|
||||
|
||||
@ -70,7 +70,6 @@ class HostSession : public Session {
|
||||
void Draw(base::FrameDef* f) override;
|
||||
void OnScreenSizeChange() override;
|
||||
void LanguageChanged() override;
|
||||
void GraphicsQualityChanged(base::GraphicsQuality q) override;
|
||||
void DebugSpeedMultChanged() override;
|
||||
auto GetHostSession() -> HostSession* override;
|
||||
auto GetMutableScene() -> Scene* override;
|
||||
|
||||
@ -166,7 +166,7 @@ void Scene::Step() {
|
||||
}
|
||||
|
||||
// Lastly step our sim.
|
||||
dynamics_->process();
|
||||
dynamics_->Process();
|
||||
|
||||
time_ += kGameStepMilliseconds;
|
||||
stepnum_++;
|
||||
@ -227,13 +227,6 @@ void Scene::DeleteNode(Node* node) {
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::GraphicsQualityChanged(base::GraphicsQuality q) {
|
||||
assert(g_base->InLogicThread());
|
||||
for (auto&& i : nodes_) {
|
||||
i->OnGraphicsQualityChanged(q);
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::OnScreenSizeChange() {
|
||||
assert(g_base->InLogicThread());
|
||||
for (auto&& i : nodes_) {
|
||||
|
||||
@ -43,7 +43,6 @@ class Scene : public Object {
|
||||
void SetMapBounds(float x, float y, float z, float X, float Y, float Z);
|
||||
void OnScreenSizeChange();
|
||||
void LanguageChanged();
|
||||
void GraphicsQualityChanged(base::GraphicsQuality q);
|
||||
auto out_of_bounds_nodes() -> const std::vector<Object::WeakRef<Node> >& {
|
||||
return out_of_bounds_nodes_;
|
||||
}
|
||||
|
||||
@ -1312,15 +1312,6 @@ void SceneV1AppMode::SetPublicPartyStatsURL(const std::string& url) {
|
||||
}
|
||||
}
|
||||
|
||||
void SceneV1AppMode::GraphicsQualityChanged(base::GraphicsQuality quality) {
|
||||
for (auto&& i : sessions_) {
|
||||
if (!i.Exists()) {
|
||||
continue;
|
||||
}
|
||||
i->GraphicsQualityChanged(quality);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneV1AppMode::SetPublicPartyPlayerCount(int count) {
|
||||
assert(g_base->InLogicThread());
|
||||
if (count == public_party_player_count_) {
|
||||
|
||||
@ -172,7 +172,6 @@ class SceneV1AppMode : public base::AppMode {
|
||||
sockaddr_storage* from) override;
|
||||
void DrawWorld(base::FrameDef* frame_def) override;
|
||||
auto DoesWorldFillScreen() -> bool override;
|
||||
void GraphicsQualityChanged(base::GraphicsQuality quality) override;
|
||||
void RunMainMenu();
|
||||
|
||||
auto dynamics_sync_time() const { return dynamics_sync_time_; }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user