Merge branch 'efroemling:master' into master

This commit is contained in:
Vishal 2023-10-23 10:00:24 +05:30 committed by GitHub
commit 36127a9021
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
118 changed files with 2305 additions and 1444 deletions

130
.efrocachemap generated
View File

@ -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",

View File

@ -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)

View File

@ -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

View File

@ -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" />

View File

@ -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>

View File

@ -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" />

View File

@ -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>

View File

@ -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',

View File

@ -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

View File

@ -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.

View File

@ -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),
)

View File

@ -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'

View File

@ -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',

View File

@ -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),

View File

@ -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,

View File

@ -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,

View File

@ -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(),
)

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -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_;
};

View File

@ -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

View File

@ -17,6 +17,8 @@ class AppAdapterHeadless : public AppAdapter {
void DoApplyAppConfig() override;
auto GetGraphicsClientContext() -> GraphicsClientContext* override;
protected:
void DoPushMainThreadRunnable(Runnable* runnable) override;
void RunMainThreadEventLoopToCompletion() override;

View File

@ -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

View File

@ -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

View File

@ -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() {

View File

@ -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) {}

View File

@ -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();

View File

@ -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

View File

@ -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_;
};

View File

@ -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

View File

@ -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);
}

View File

@ -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();

View File

@ -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_;
};

View File

@ -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_);

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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};

View File

@ -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);
};

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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_{};

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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};

View File

@ -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_);
}

View File

@ -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) {

View File

@ -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_{};

View File

@ -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

View File

@ -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_

View 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

View 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_

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.
};

View File

@ -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,

View File

@ -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,
};
}

View File

@ -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,
};
}

View File

@ -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) {

View File

@ -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
}

View File

@ -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;

View File

@ -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.
}

View File

@ -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;

View File

@ -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.

View File

@ -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;

View File

@ -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());
}

View File

@ -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();

View File

@ -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;
};

View File

@ -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) {}

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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) {

View File

@ -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]{};

View File

@ -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 {

View File

@ -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);

View File

@ -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);
{

View File

@ -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) {

View File

@ -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_;

View File

@ -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) {

View File

@ -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_) {

View File

@ -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.

View File

@ -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;

View File

@ -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;

View File

@ -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_) {

View File

@ -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_;
}

View File

@ -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_) {

View File

@ -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