Compare commits

...

110 Commits

Author SHA1 Message Date
Attila Uygun 214ae33246 Fix for compiler warning 2023-11-04 00:12:55 +01:00
Attila Uygun 3c54bc474c Add support for texture update without extra copy 2023-11-03 23:58:21 +01:00
Attila Uygun 0da56dd552 Move Image::Format enum to renderer_types.h
And make it enum class
2023-11-03 23:58:18 +01:00
Attila Uygun b86a27531b Use std::binary_semaphore 2023-11-02 22:23:43 +01:00
Attila Uygun 8b149bf8f4 Fix for flickering imgui when switching renderer
Use the geometry from last frame in ImguiBackend::CreateRenderResources if
available.
2023-11-02 21:49:03 +01:00
Attila Uygun 61a96c7988 Fix for compiler warning 2023-11-02 21:24:53 +01:00
Attila Uygun 0d64c98854 Add Renderer::UpdateGeometry()
Also add its implementations in Vulkan and OpenGL renderers. Use it in
ImguiBackend instead of recreating geometry buffers every frame.
2023-11-02 21:15:20 +01:00
Attila Uygun a02d1ba71e OpenGL: fixed-point data values should be normalized 2023-11-01 20:43:06 +01:00
Attila Uygun 849599afd8 Reformat 2023-10-31 21:39:59 +01:00
Attila Uygun 874fec434a Implement ImguiBackend::Render()
Create geometry in Render() before calling Draw()
2023-10-29 23:21:54 +01:00
Attila Uygun e4f020d359 Add support for set/reset viewport 2023-10-29 23:13:17 +01:00
Attila Uygun f144bfdb2d Call ImGui::NewFrame() early in the main loop 2023-10-28 00:44:36 +02:00
Attila Uygun 75b5046684 Arbitrary scale-up 2023-10-25 23:40:07 +02:00
Attila Uygun 669ed5e098 Update for ImguiBackend
- Use RobotoMono-Regular.ttf instead of the default font
- Use delta-time instead of elapsed-time
- Fix for scissor/clipping rectangle
2023-10-25 20:03:26 +02:00
Attila Uygun 8b47314e72 Fix for compiler warnings 2023-10-24 00:48:46 +02:00
Attila Uygun b8e7957d91 Revert "Fix for compile warnings"
This reverts commit 2eb571b1ff.
2023-10-24 00:39:02 +02:00
Attila Uygun 2eb571b1ff Fix for compile warnings 2023-10-24 00:32:23 +02:00
Attila Uygun 010c6b097c Add support for texture units 2023-10-24 00:01:20 +02:00
Attila Uygun 3125bb9c95 Activate imgui shader only once. 2023-10-22 23:39:29 +02:00
Attila Uygun 4cc2c03afe Vulkan: Fix for activate-texture 2023-10-22 23:34:38 +02:00
Attila Uygun 06e42ead57 Use imgui to show stats 2023-10-15 10:12:09 +02:00
Attila Uygun a23dbdfe15 Add imgui to the list of third-party libraries 2023-10-14 21:07:05 +02:00
Attila Uygun 7b8d2db565 Implement ImguiBackend 2023-10-14 21:04:11 +02:00
Attila Uygun eed6d4751c Add imgui to third_party 2023-10-14 21:04:07 +02:00
Attila Uygun 2bafd5dbd7 Add support for scissor 2023-10-14 00:51:29 +02:00
Attila Uygun 14b2d22fbd Fix for index buffer
- Fix index data offset in Vulkan geometry buffer.
- Normalize vertex colors.
- Add support for drawing from a given index offset.
2023-10-11 20:04:11 +02:00
Attila Uygun 2823aa3197 Implement AudioDeviceWASAPI 2023-10-04 19:58:56 +02:00
Attila Uygun 261e7f41d6 Move custom shader from ImageQuad to Drawable 2023-09-30 23:30:34 +02:00
Attila Uygun 02418aa42a Rename AudioSink to AudioDevice 2023-09-30 22:43:28 +02:00
Attila Uygun e08f6e2022 cleanup build.gradle 2023-09-29 14:05:38 +02:00
Attila Uygun dd14ed30ea Use gn target name for the native lib 2023-09-27 23:35:00 +02:00
Attila Uygun 35958b13f7 Move assets to assets/demo 2023-09-27 21:33:00 +02:00
Attila Uygun 5b57d55c28 Fix for build (Windows) 2023-09-26 22:32:44 +02:00
Attila Uygun d779d17b3d Add separate gn configs for renderer, audio and platform 2023-09-23 22:49:10 +02:00
Attila Uygun 5e80aa1a04 Workaround for what seems to be a bug in the Android Gradle plugin
Error:
Could not determine the dependencies of task ':app:lintVitalReportDemoAllArchsRelease'.
> Could not create task ':app:lintVitalAnalyzeDemoAllArchsRelease'.
   > No such property: productFlavors for class: org.gradle.api.internal.provider.DefaultProperty
2023-09-12 21:31:16 +02:00
Attila Uygun f75a469e38 merge -> generate 2023-09-12 21:27:54 +02:00
Attila Uygun 8c66e7aa7a Move VMA_STATIC_VULKAN_FUNCTIONS to vma/BUILD.gn 2023-09-12 19:57:11 +02:00
Attila Uygun 3f13440acb Update Android Gradle plugin to 8.1.0 2023-09-12 19:21:23 +02:00
Attila Uygun c637b407c8 Cleanup gn config and .gitignore 2023-09-11 19:32:36 +02:00
Attila Uygun f2d6b04782 Add a separate gn config for each third-party library 2023-09-11 19:32:33 +02:00
Attila Uygun 85b5184d29 Remove forEachBuildType 2023-09-09 22:49:36 +02:00
Attila Uygun 64c63d4184 Remove provider_name resValue from build.gradle 2023-09-06 20:26:31 +02:00
Attila Uygun 915b896636 Use manifestPlaceholders for app icon 2023-09-06 20:15:00 +02:00
Attila Uygun d61b2c45d6 Rearrange code in build.gradle and add comments 2023-09-06 19:05:43 +02:00
Attila Uygun e9be4d45d2 Avoid adding gn tasks for each game 2023-09-05 22:54:06 +02:00
Attila Uygun ba7c823c91 Update hello world 2023-09-05 22:09:47 +02:00
Attila Uygun 0b1220c114 Revert "Unify arch types in gradle and gn"
This reverts commit 05252bfae8.
2023-09-05 21:40:04 +02:00
Attila Uygun 5f8f70ed53 Get ABI codes from productFlavors and remove hard-coded strings 2023-09-05 19:21:04 +02:00
Attila Uygun 05252bfae8 Unify arch types in gradle and gn 2023-09-05 18:04:47 +02:00
Attila Uygun ea93f80aa1 Move globals into Utils 2023-09-05 17:50:32 +02:00
Attila Uygun 48fb1589c6 Remove hard-coded build variant strings 2023-09-05 17:26:17 +02:00
Attila Uygun 01bffc71ac Add archs to productFlavors 2023-09-05 17:07:48 +02:00
Attila Uygun e6d89add57 Move product specific values to productFlavors 2023-09-04 20:07:59 +02:00
Attila Uygun 37afc006d2 Use productFlavors 2023-09-04 19:29:30 +02:00
Attila Uygun 10d411bafd Use shared_library build config 2023-09-04 19:29:30 +02:00
Attila Uygun ddecaddccc Rename utils.gni to rules.gni 2023-09-04 19:29:30 +02:00
Attila Uygun ae825faf32 Always run ninja 2023-09-02 17:14:39 +02:00
Attila Uygun a07d32e44a Add support for GN to gradle config and remove cmake 2023-08-31 22:05:57 +02:00
Attila Uygun 9ff2e51ff1 Add support for Android build to GN config 2023-08-31 22:05:53 +02:00
Attila Uygun 2ca0bb9b5b Update build files 2023-08-15 18:48:34 +02:00
Attila Uygun 169e05c970 Cleanup 2023-08-14 00:14:34 +02:00
Attila Uygun 001b3b60ec Support for Windows platform 2023-08-13 00:06:04 +02:00
Attila Uygun 6e952e511e Add Platform::CreateMainWindow 2023-08-10 10:03:28 +02:00
Attila Uygun 8503c549d7 Use aligned memory in stb_image 2023-08-09 01:16:15 +02:00
Attila Uygun 96d6a52a74 Update timer.h
Remove Timer class. Add ElapsedTimer and DeltaTimer classes that use
std::chrono::high_resolution_clock
2023-08-08 00:32:07 +02:00
Attila Uygun 10823cd459 Fix memory issue in VulkanContext::SwapBuffers 2023-08-08 00:02:20 +02:00
Attila Uygun 26514fd142 Use base::PIf 2023-08-07 23:58:10 +02:00
Attila Uygun fb7f91185d Update glew to 2.2.0 2023-08-06 21:37:26 +02:00
Attila Uygun f56cc119bf Code cleanup 2023-08-05 01:52:07 +02:00
Attila Uygun 478bb8ecb1 Update readme 2023-08-04 10:30:12 +02:00
Attila Uygun 79482d1c4d Update readme 2023-08-03 22:06:07 +02:00
Attila Uygun ecd2a8d738 Logo 2023-08-03 12:04:14 +02:00
Attila Uygun 2c1d55fc30 Replace the build system with gn
No support for APKs, Java code etc. Keep using gradle for Android for now.
2023-08-03 11:32:28 +02:00
Attila Uygun 571a21914c Fix typo 2023-07-29 00:47:18 +02:00
Attila Uygun 26a1270702 Add Engine::Print method
Also add a hello world example to readme
2023-07-28 18:37:51 +02:00
Attila Uygun e65d37e846 Make member variables in MixerInput private 2023-07-16 12:42:07 +02:00
Attila Uygun 19910be27e Refactoring and comments for MixerInput 2023-07-14 00:35:20 +02:00
Attila Uygun 34a73082a6 Fix typo 2023-07-13 10:56:51 +02:00
Attila Uygun 38813f98b4 Move AudioMixer::Resource to MixerInput 2023-07-13 10:49:42 +02:00
Attila Uygun c5171ffc03 Hash uniform name strings 2023-07-12 02:32:22 +02:00
Attila Uygun e509588f61 Optimization for AudioMixer
Remove resource map and use std::shared_ptr<void>
2023-07-11 23:29:47 +02:00
Attila Uygun 6bad0ac9c0 Use a vector instead of unordered_map for shader variables 2023-07-11 23:22:43 +02:00
Attila Uygun cccc6ca8c3 Cleanup 2023-07-10 00:17:09 +02:00
Attila Uygun 0398903585 Fix for opengl renderer 2023-07-04 22:46:39 +02:00
Attila Uygun 392c4eaf8f Update for vulkan renderer
- Create staging buffers before starting bg thread
- Remove VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT
2023-07-01 23:25:37 +02:00
Attila Uygun 3dc48ef05e Implement window resize 2023-07-01 21:36:26 +02:00
Attila Uygun c6546b43b5 Add log verbosity level 2023-06-28 22:25:39 +02:00
Attila Uygun 1148e48085 Fix typo 2023-06-26 20:03:22 +02:00
Attila Uygun ce440f2913 Remove OpenGL threaded rendering 2023-06-26 20:03:22 +02:00
Attila Uygun b07ce8514e Do not reset source index when streaming is in progress 2023-06-26 20:03:22 +02:00
Attila Uygun 467e73d3a8 Fix for looping audio 2023-06-26 20:03:15 +02:00
Attila Uygun 1b9faac310 Music should be streamed 2023-06-26 10:25:22 +02:00
Attila Uygun 615c39a4fe Move asset loaders to engine/asset 2023-06-25 18:40:58 +02:00
Attila Uygun 709029f22c Async-load support for assets 2023-06-25 16:48:56 +02:00
Attila Uygun 8e6589ec67 Fix memory leak when canceling tasks 2023-06-24 11:04:10 +02:00
Attila Uygun 69a05c00e9 Support for adding tasks in front of the task queue 2023-06-24 11:04:07 +02:00
Attila Uygun d7e444fa81 Fix compile error 2023-06-21 14:31:35 +02:00
Attila Uygun 8a87597911 Update third_party/stb
stb_image - v2.28
stb_truetype - v1.26
2023-06-21 14:00:21 +02:00
Attila Uygun 22d80d6152 Cleanup & refactor 2023-06-21 13:59:11 +02:00
Attila Uygun 67632ff144 Fix typo 2023-06-17 21:05:05 +02:00
Attila Uygun 8fd24a3c1a Update TaskRunner
Remove SingleConsumerRun
Rename MultiConsumerRun to RunTasks
Refactor CancelTasks
2023-06-13 23:27:10 +02:00
Attila Uygun 305b23738c Make thread-local-task-runner a shared_ptr 2023-06-13 23:27:10 +02:00
Attila Uygun 83400a0b52 Use string for callback location info 2023-06-13 23:27:10 +02:00
Attila Uygun fdbbb2a6fd Fix for ReplyAdapter in TaskRunner 2023-06-12 00:35:57 +02:00
Attila Uygun ab4c0c7e57 Cleanup 2023-06-12 00:34:29 +02:00
Attila Uygun 325b07d844 Do not refresh image when texture is not in use 2023-06-12 00:34:29 +02:00
Attila Uygun 05b86a38c1 Add comments 2023-06-11 22:42:34 +02:00
Attila Uygun 0876203d82 Cancel tasks on shutdown 2023-06-11 22:42:34 +02:00
Attila Uygun f54835e5f3 Consume input events on ContextLost 2023-06-11 22:42:34 +02:00
Attila Uygun ba246eca7f Revert "Consume input events when switching between renderers"
This reverts commit 5c6e414a15.
2023-06-06 23:00:55 +02:00
216 changed files with 103009 additions and 12668 deletions

View File

@ -1,7 +1,2 @@
BasedOnStyle: Chromium
Standard: Cpp11
MacroBlockBegin: "^\
RENDER_COMMAND_BEGIN$"
MacroBlockEnd: "^\
RENDER_COMMAND_END$"

4
.gitignore vendored
View File

@ -4,8 +4,4 @@ build/android/*.idsig
build/android/app/.cxx
build/android/app/build
build/android/build
build/linux/demo_x86_64_debug
build/linux/demo_x86_64_release
build/linux/obj
build/linux/woom
out

2
.gn Normal file
View File

@ -0,0 +1,2 @@
# The location of the build configuration file.
buildconfig = "//build/BUILDCONFIG.gn"

36
.vscode/c_cpp_properties.json vendored Normal file
View File

@ -0,0 +1,36 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [
"OS_LINUX"
]
},
{
"name": "Mac",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [
"OS_MAC"
],
"macFrameworkPath": [
"/System/Library/Frameworks",
"/Library/Frameworks"
]
},
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [
"OS_WIN"
]
}
],
"version": 4
}

21
.vscode/launch.json vendored
View File

@ -5,13 +5,13 @@
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"name": "Debug demo - Linux",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/linux/demo_x86_64_debug",
"program": "${workspaceFolder}/out/debug/demo",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/build/linux",
"cwd": "${workspaceFolder}/out/debug",
"environment": [],
"console": "externalTerminal",
"MIMode": "gdb",
@ -22,6 +22,19 @@
"ignoreFailures": true
}
]
"preLaunchTask": "Build project",
},
{
"name": "Debug demo - Windows",
"type": "cppvsdbg",
"request": "launch",
"program": "${workspaceFolder}\\out\\debug\\demo.exe",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}\\out\\debug",
"environment": [],
"externalConsole": false,
"preLaunchTask": "Build project",
}
]
]
}

82
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,82 @@
{
"files.associations": {
"algorithm": "cpp",
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"*.tcc": "cpp",
"cctype": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"codecvt": "cpp",
"compare": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"list": "cpp",
"map": "cpp",
"set": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"vector": "cpp",
"exception": "cpp",
"functional": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"optional": "cpp",
"random": "cpp",
"ratio": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"fstream": "cpp",
"future": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"mutex": "cpp",
"new": "cpp",
"numbers": "cpp",
"ostream": "cpp",
"semaphore": "cpp",
"shared_mutex": "cpp",
"span": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"thread": "cpp",
"cinttypes": "cpp",
"typeinfo": "cpp",
"variant": "cpp"
}
"git.ignoreLimitWarning": true,
// These settings should really be in github sync or something, if only it
// worked in linux...
"debug.openExplorerOnEnd": true,
"debug.console.closeOnEnd": true,
"editor.tabSize": 2,
"editor.rulers": [
80,
],
"workbench.editor.closeEmptyGroups": false,
}

39
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,39 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Build project",
"type": "shell",
"linux": {
"command": "~/code/work/chromium/src/third_party/ninja/ninja",
"problemMatcher": [
"$gcc"
]
},
"osx": {
"command": "ninja",
"problemMatcher": [
"$gcc"
]
},
"windows": {
"command": "ninja.exe",
"problemMatcher": [
"$msCompile"
]
},
"args": [
"-C",
"${workspaceFolder}/out/debug"
],
"options": {
"cwd": "${workspaceFolder}"
},
"group": {
"kind": "build",
"isDefault": true
},
"detail": "Build the project with ninja."
}
]
}

6
BUILD.gn Normal file
View File

@ -0,0 +1,6 @@
group("all") {
deps = [
"//src/demo",
"//src/hello_world",
]
}

View File

@ -1,20 +1,87 @@
# Kaliber
A simple, cross-platform 2D game engine with OpenGL and Vulkan renderers.
Supports Linux and Android platforms.
Supports Linux, Windows and Android platforms.
This is a personal hobby project. I've published a little game on
[Google Play](https://play.google.com/store/apps/details?id=com.woom.game)
based on this engine. Full game code and assets are included in this repository.
#### Building the demo
Linux:
## Pre-requisites:
GN build system is required for all platforms:\
https://gn.googlesource.com/gn/
## Building from the command-line:
### All platforms except Android:
#### Setup:
Generate build files for Ninja in release and debug modes.
```text
cd build/linux
make
gn gen out/release
gn gen --args='is_debug=true' out/debug
```
Android:
#### Building and running:
Build all games in release mode and run "hello_world".
```text
ninja -C out/release
./out/release/hello_world
```
Build "demo" in debug mode and run.
```text
ninja -C out/debug demo
./out/debug/demo
```
### Android:
Build "hello_world" in debug mode for all ABIs and install. GN will be run by
Gradle so no setup is required. Location of gn and ninja executables can be
specified via "gn" and "ninja" properties (-Pgn="path/gn").
```text
cd build/android
./gradlew :app:assembleRelease
./gradlew :app:installHelloWorldAllArchsDebug
```
#### Third-party libraries:
Build "demo" in debug mode for x86_64 ABI and install. Valid ABI targets are
Arm7, Arm8, X86, X86_64, AllArchs, ArmOnly, X86Only.
```text
./gradlew :app:installDemoX86_64Debug
```
### Generate Visual Studio solution:
```text
gn.exe gen --args="is_debug=true" --ide=vs2022 out\vs
devenv out\vs\all.sln
```
## Hello World example:
Shows a smoothly rotating "Hello World".
```cpp
class HelloWorld final : public eng::Game {
public:
bool Initialize() final {
eng::Engine::Get().SetImageSource(
"hello_world_image",
std::bind(&eng::Engine::Print, &eng::Engine::Get(), "Hello World",
/*bg_color*/ base::Vector4f(1, 1, 1, 0)));
hello_world_.Create("hello_world_image").SetVisible(true);
animator_.Attach(&hello_world_);
animator_.SetRotation(base::PI2f, /*duration*/ 3,
std::bind(base::SmootherStep, std::placeholders::_1));
animator_.Play(eng::Animator::kRotation, /*loop*/ true);
return true;
}
private:
eng::ImageQuad hello_world_;
eng::Animator animator_;
};
GAME_FACTORIES{GAME_CLASS(HelloWorld)};
```
## Third-party libraries:
[glew](https://github.com/nigels-com/glew),
[jsoncpp](https://github.com/open-source-parsers/jsoncpp),
[minimp3](https://github.com/lieff/minimp3),
@ -26,4 +93,5 @@ cd build/android
[spirv-reflect](https://github.com/KhronosGroup/SPIRV-Reflect),
[vma](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator),
[vulkan-sdk](https://vulkan.lunarg.com),
[volk](https://github.com/zeux/volk)
[volk](https://github.com/zeux/volk),
[imgui](https://github.com/ocornut/imgui)

44
assets/demo/BUILD.gn Normal file
View File

@ -0,0 +1,44 @@
copy("demo") {
sources = [
"bead.png",
"boss_explosion.mp3",
"boss_intro.mp3",
"Boss_ok_lvl2.png",
"Boss_ok_lvl3.png",
"Boss_ok.png",
"chromatic_aberration.glsl_fragment",
"chromatic_aberration.glsl_vertex",
"enemy_anims_01_frames_ok.png",
"enemy_anims_02_frames_ok.png",
"enemy_anims_blast_ok.png",
"enemy_anims_flare_ok.png",
"enemy_ray_ok.png",
"enemy_target_single_ok.png",
"explosion.mp3",
"Game_2_Boss.mp3",
"Game_2_Main.mp3",
"hit.mp3",
"laser.mp3",
"menu_click.mp3",
"menu_icons.png",
"no_nuke.mp3",
"nuke_frames.png",
"nuke_pack_OK.png",
"nuke.mp3",
"PixelCaps!.ttf",
"powerup-pick.mp3",
"powerup-spawn.mp3",
"renderer_logo.png",
"shield.mp3",
"sky_without_nebula.glsl_fragment",
"sky_without_nebula.glsl_vertex",
"sky.glsl_fragment",
"sky.glsl_vertex",
"stealth.mp3",
"woom_enemy_shield.png",
"woom_logo_start_frames_01.png",
"woom_logo_start_frames_02-03.png",
]
outputs = ["$root_out_dir/assets/demo/{{source_file_part}}"]
}

View File

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 106 KiB

View File

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 103 KiB

View File

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 105 KiB

View File

Before

Width:  |  Height:  |  Size: 507 B

After

Width:  |  Height:  |  Size: 507 B

View File

Before

Width:  |  Height:  |  Size: 332 KiB

After

Width:  |  Height:  |  Size: 332 KiB

View File

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

View File

Before

Width:  |  Height:  |  Size: 156 KiB

After

Width:  |  Height:  |  Size: 156 KiB

View File

Before

Width:  |  Height:  |  Size: 219 KiB

After

Width:  |  Height:  |  Size: 219 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

View File

Before

Width:  |  Height:  |  Size: 143 KiB

After

Width:  |  Height:  |  Size: 143 KiB

View File

Before

Width:  |  Height:  |  Size: 255 KiB

After

Width:  |  Height:  |  Size: 255 KiB

13
assets/engine/BUILD.gn Normal file
View File

@ -0,0 +1,13 @@
copy("engine") {
sources = [
"RobotoMono-Regular.ttf",
"imgui.glsl_fragment",
"imgui.glsl_vertex",
"pass_through.glsl_fragment",
"pass_through.glsl_vertex",
"solid.glsl_fragment",
"solid.glsl_vertex",
]
outputs = [ "$root_out_dir/assets/engine/{{source_file_part}}" ]
}

View File

@ -0,0 +1,18 @@
#ifdef GL_ES
precision mediump float;
#endif
IN(0) vec2 tex_coord_0;
IN(1) vec4 color;
UNIFORM_BEGIN
UNIFORM_V(mat4 projection)
UNIFORM_END
SAMPLER(0, sampler2D texture_0)
FRAG_COLOR_OUT(frag_color)
void main() {
FRAG_COLOR(frag_color) = TEXTURE(texture_0, tex_coord_0) * color;
}

View File

@ -0,0 +1,17 @@
IN(0) vec2 in_position;
IN(1) vec2 in_tex_coord_0;
IN(2) vec4 in_color;
UNIFORM_BEGIN
UNIFORM_V(mat4 projection)
UNIFORM_END
OUT(0) vec2 tex_coord_0;
OUT(1) vec4 color;
void main() {
tex_coord_0 = in_tex_coord_0;
color = in_color;
gl_Position = PARAM(projection) * vec4(in_position, 0.0, 1.0);
}

280
build/BUILD.gn Normal file
View File

@ -0,0 +1,280 @@
config("default") {
if (is_android) {
defines = [ "OS_ANDROID" ]
} else if (is_ios) {
defines = [ "OS_IOS" ]
} else if (is_linux) {
defines = [ "OS_LINUX" ]
} else if (is_mac) {
defines = [ "OS_MAC" ]
} else if (is_win) {
defines = [ "OS_WIN" ]
}
if (is_ios || is_mac) {
defines += [ "OS_APPLE" ]
}
cflags_cc = []
if (is_win) {
cflags_cc += [ "/std:c++20" ]
} else {
cflags_cc += [ "-std=c++20" ]
}
cflags = []
if (is_win) {
cflags += [
"/permissive-", # Don't enable nonconforming code to compile.
"/EHsc", # Use C++ exception handling.
"/Gd", # __cdecl calling convention.
"/utf-8", # Set Source and Executable character set.
]
defines += [
# Windows headers from MS and third party expects these to be set.
"_WINDOWS",
"_UNICODE",
"UNICODE",
# Don't want MS specific extensions for C stdlib functions.
"_CRT_SECURE_NO_WARNINGS",
# Clean up the Windows headers.
"WIN32_LEAN_AND_MEAN",
"NOMINMAX",
]
} else {
# cflags += [
# "-fstrict-aliasing", # Optimizations allowed for type aliasing.
# "-fvisibility=hidden", # Only let symbols be visible inside it's own
# # library.
# ]
# cflags_cc += [
# "-fvisibility-inlines-hidden", # Inline methods may not generate symbols.
# # "-stdlib=libc++",
# ]
if (is_linux) {
cflags += [
"-fPIC", # Emit position-independent code suitable for dynamic linking.
]
libs = [
"pthread", # Use the POSIX thread library.
]
} else if (is_mac) {
asmflags = [
"-target",
"arm64-apple-macos11",
]
cflags += [
# "-fPIC", # Emit position-independent code suitable for dynamic linking.
"-target",
"arm64-apple-macos11",
]
ldflags = [
"-target",
"arm64-apple-macos11",
]
} else if (is_android) {
cflags += [
"--sysroot=$ndk/toolchains/llvm/prebuilt/$ndk_host/sysroot",
"-fPIC", # Emit position-independent code suitable for dynamic linking.
]
ldflags = [ "-static-libstdc++" ]
}
}
# Avoid relative paths everywhere.
include_dirs = [
"//src",
]
}
config("debug") {
defines = [ "_DEBUG" ]
if (is_win) {
cflags = [
"/GS", # Buffer security checks (buffer overruns).
"/sdl", # Enable additional security features and warnings.
"/RTC1", # Enable fast run-time checks.
"/Od", # Disable optimizations.
"/fp:precise", # Floating points are predictable.
"/MDd", # Link with multithread debug DLL run-time libraries.
"/Z7", # Produce obj files that contain full symbolic debugging
# information.
]
ldflags = [ "/DEBUG:FASTLINK" ] # Reference the debug symbols in the obj
# files instead of a PDB file.
} else {
cflags = [
"-g", # Debug symbols.
# TODO: This makes it hard to step through code on Linux.
# "-Og", # Optimizations that works well with debugger.
]
if (is_unix) {
defines += [
"_GLIBCXX_DEBUG=1", # Enable asserts in stl headers.
]
}
}
}
config("release") {
defines = [ "NDEBUG" ]
if (is_win) {
cflags = [
"/GL", # Link-time code generation / Whole program optimization.
"/Gy", # Allows converting inline functions to separate functions.
"/O2", # Optimize for maximum speed.
"/Oi", # Enable intrinsics.
"/fp:fast", # Floating points are fast.
"/MD", # Link with multithread release DLL run-time libraries.
# "/arch:AVX2", # Minimum CPU architecture.
]
ldflags = [
"/LTCG", # Link time code generation.
"/OPT:ICF", # COMDAT (common data) folding, e.g. vtables and big inline
# functions that can't be inlined, but also if identical code
# happens to be generated though common math operations or
# similar.
"/OPT:REF", # Eliminate unreferenced code and data.
]
} else {
cflags = [
"-Ofast", # Full optimization and disregard strict standard compliance.
"-fno-math-errno", # Do not check or set errno for invalid input to sqrt
# and other math functions.
]
if (is_apple) {
ldflags = [ "-dead-strip" ]
} else {
# Place both data and functions in it's their own sections.
# Linker optimization that allows for smaller binaries.
cflags += [
"-fdata-sections",
"-ffunction-sections",
]
ldflags = [ "-Wl,--gc-sections" ]
}
}
}
config("warnings") {
if (is_win) {
cflags = [
"/WX", # Treat warnings as errors.
"/Wall", # Enable all warnings.
# Disable the following warnings:
"/wd4061", # enumerator in switch with a default case not explicitly
# handled by a case label.
"/wd4100", # nonstandard extension used: nameless struct/union.
"/wd4201", # enumerator in switch with a default case not explicitly
# handled by a case label.
"/wd4324", # structure was padded due to alignment specifier
"/wd4371", # layout of class may have changed from a previous version of
# the compiler due to better packing of member
"/wd4514", # unreferenced inline function has been removed.
"/wd4710", # function not inlined
"/wd4711", # function selected for automatic inline expansion
"/wd4820", # padding added after data member.
"/wd5045", # compiler will insert Spectre mitigation for memory load if
# switch specified.
"/wd4355", # 'this': used in base member initializer list
# TODO: Not sure how I feel about these conversion warnings.
"/Wv:18",
"/wd4191", # 'type cast': unsafe conversion
"/wd4244", # conversion, possible loss of data. 'int' to 'float'
"/wd4245", # conversion from 'int' to 'const unsigned int'
"/wd4305", # truncation from 'double' to 'float'.
"/wd4365", # conversion, signed/unsigned mismatch.
"/wd4722", # destructor never returns, potential memory leak
"/wd4702", # unreachable code
"/wd4625", # copy constructor was implicitly defined as deleted
"/wd4626", # assignment operator was implicitly defined as deleted
# Possible compiler bug? Needs investigation.
"/wd4668", # '__STDC_WANT_SECURE_LIB__' is not defined as a preprocessor
# macro, replacing with '0' for '#if/#elif'
]
defines = [
"_CRT_NONSTDC_NO_DEPRECATE",
"_SILENCE_CXX17_STRSTREAM_DEPRECATION_WARNING",
"_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS",
]
} else if (is_mac) {
cflags = [
"-Wno-deprecated-declarations",
"-Wno-inconsistent-missing-override",
"-Wno-pointer-sign",
]
} else {
cflags = [
# Enable as much warnings as possible.
"-Werror", # Make all warnings into errors.
"-Wall", # Enable (almost) all warnings.
"-Wextra", # Enable even more warnings.
# forbidden extensions.
"-Wvla", # Warn if variable-length array is used in code.
# Disable the following warnings:
"-Wno-unused-parameter",
"-Wno-sign-compare",
]
}
}
config("executable") {
if (is_android) {
ldflags = [
"-pie",
"-rdynamic",
]
} else if (is_linux) {
ldflags = [
"-Wl,-rpath,\$ORIGIN", # Add directory to runtime library search path.
]
} else if (is_mac) {
ldflags = [ "-Wl,-rpath,@loader_path/." ]
}
}
config("shared_library") {
if (is_android) {
ldflags = [
"-Wl,--build-id=sha1",
"-Wl,--no-rosegment",
"-Wl,--fatal-warnings",
"-Wl,--no-undefined",
"-Qunused-arguments",
"-u ANativeActivity_onCreate",
]
} else if (is_linux) {
ldflags = [
"-Wl,-rpath,\$ORIGIN", # Add directory to runtime library search path.
]
} else if (is_mac) {
ldflags = [ "-Wl,-rpath,@loader_path/." ]
}
}
# TODO: This needs some more investigation.
# Is it possible to avoid setting it and rely on defaults?
# Some tools will be console apps while games will be gui apps.
# GLFW seems to be using console in debug mode and gui in release mode.
if (is_win) {
config("win_gui") {
ldflags = [ "/SUBSYSTEM:WINDOWS" ]
}
config("win_console") {
ldflags = [ "/SUBSYSTEM:CONSOLE" ]
}
}

113
build/BUILDCONFIG.gn Normal file
View File

@ -0,0 +1,113 @@
declare_args() {
is_debug = false
# Note that this uses the 'cc' and 'c++' links that should map to GCC on linux
# systems and clang on macs.
ar = "ar"
cc = "cc"
cxx = "c++"
ndk = ""
ndk_api = 24
}
# Platform detection
if (target_os == "") {
target_os = host_os
}
if (current_os == "") {
current_os = target_os
}
is_android = current_os == "android"
is_ios = current_os == "ios"
is_linux = current_os == "linux"
is_mac = current_os == "mac"
is_win = current_os == "win"
is_apple = is_mac || is_ios
is_desktop = is_mac || is_linux || is_win
is_mobile = is_android || is_ios
is_unix = is_android || is_linux
if (target_cpu == "") {
if (is_mobile) {
target_cpu = "arm64"
} else {
target_cpu = host_cpu
}
}
if (target_cpu == "x86_64") {
target_cpu = "x64"
}
if (current_cpu == "") {
current_cpu = target_cpu
}
if (is_android) {
assert(ndk != "", "NDK path argument is empty")
ndk_host = ""
ndk_target = ""
if (host_os == "linux") {
ndk_host = "linux-x86_64"
} else if (host_os == "mac") {
ndk_host = "darwin-x86_64"
} else if (host_os == "win") {
ndk_host = "windows-x86_64"
}
if (target_cpu == "arm64") {
ndk_target = "aarch64-linux-android"
} else if (target_cpu == "arm") {
ndk_target = "armv7a-linux-androideabi"
} else if (target_cpu == "x64") {
ndk_target = "x86_64-linux-android"
} else if (target_cpu == "x86") {
ndk_target = "i686-linux-android"
}
_prefix = "$ndk/toolchains/llvm/prebuilt/$ndk_host/bin"
if (host_os == "win") {
ar = "$_prefix/llvm-ar.exe"
cc = "$_prefix/clang.exe --target=$ndk_target$ndk_api -fno-addrsig"
cxx = "$_prefix/clang++.exe --target=$ndk_target$ndk_api -fno-addrsig"
} else {
ar = "$_prefix/llvm-ar"
cc = "$_prefix/$ndk_target$ndk_api-clang"
cxx = "$_prefix/$ndk_target$ndk_api-clang++"
}
}
# Set defaults
_default_config = [
"//build:default",
"//build:warnings",
]
if (is_debug) {
_default_config += [ "//build:debug" ]
} else {
_default_config += [ "//build:release" ]
}
set_defaults("executable") {
configs = [ "//build:executable" ] + _default_config
}
set_defaults("static_library") {
configs = _default_config
}
set_defaults("shared_library") {
configs = [ "//build:shared_library" ] + _default_config
}
set_defaults("source_set") {
configs = _default_config
}
if (is_win) {
set_default_toolchain("//build/toolchain/msvc")
} else {
# Clang behaves like GCC in most cases.
set_default_toolchain("//build/toolchain/gcc")
}

View File

@ -1,171 +0,0 @@
cmake_minimum_required(VERSION 3.22.1)
# common_config
add_library(common_config INTERFACE)
target_compile_options(common_config INTERFACE
$<$<COMPILE_LANGUAGE:CXX>:-std=c++20>
#$<$<NOT:$<CONFIG:DEBUG>>:-Ofast>
-Wall
-Werror
-Wno-unused-but-set-variable
-Wno-deprecated-enum-enum-conversion
-Wno-unsequenced
-Wno-nullability-completeness
)
target_compile_definitions(common_config INTERFACE
VK_USE_PLATFORM_ANDROID_KHR
VMA_STATIC_VULKAN_FUNCTIONS=1
$<$<CONFIG:DEBUG>:_DEBUG>
)
target_include_directories(common_config INTERFACE
../../../src
../../../src/third_party/glslang
../../../src/third_party/vulkan/include
)
# oboe
set (OBOE_DIR ../../../src/third_party/oboe)
add_subdirectory(${OBOE_DIR} ./oboe-bin)
# cpufeatures
add_library(cpufeatures STATIC ${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c)
target_include_directories(cpufeatures PUBLIC ${ANDROID_NDK}/sources/android/cpufeatures)
# native_app_glue
add_library(native_app_glue STATIC ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c)
target_include_directories(native_app_glue PUBLIC ${ANDROID_NDK}/sources/android/native_app_glue)
set_property(TARGET native_app_glue PROPERTY POSITION_INDEPENDENT_CODE ON)
# Export ANativeActivity_onCreate(),
# Refer to: https://github.com/android-ndk/ndk/issues/381.
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
# kaliber
project(kaliber)
add_library(kaliber SHARED
../../../src/base/collusion_test.cc
../../../src/base/log.cc
../../../src/base/task_runner.cc
../../../src/base/thread_pool.cc
../../../src/base/timer.cc
../../../src/demo/credits.cc
../../../src/demo/demo.cc
../../../src/demo/enemy.cc
../../../src/demo/hud.cc
../../../src/demo/menu.cc
../../../src/demo/player.cc
../../../src/demo/sky_quad.cc
../../../src/engine/animatable.cc
../../../src/engine/animator.cc
../../../src/engine/audio/audio_bus.cc
../../../src/engine/audio/audio_mixer.cc
../../../src/engine/audio/audio_sink_oboe.cc
../../../src/engine/audio/sinc_resampler.cc
../../../src/engine/drawable.cc
../../../src/engine/engine.cc
../../../src/engine/font.cc
../../../src/engine/image_quad.cc
../../../src/engine/image.cc
../../../src/engine/mesh.cc
../../../src/engine/persistent_data.cc
../../../src/engine/platform/asset_file_android.cc
../../../src/engine/platform/asset_file.cc
../../../src/engine/platform/platform_android.cc
../../../src/engine/renderer/geometry.cc
../../../src/engine/renderer/opengl/render_command.cc
../../../src/engine/renderer/opengl/renderer_opengl_android.cc
../../../src/engine/renderer/opengl/renderer_opengl.cc
../../../src/engine/renderer/renderer_types.cc
../../../src/engine/renderer/shader.cc
../../../src/engine/renderer/texture.cc
../../../src/engine/renderer/vulkan/renderer_vulkan_android.cc
../../../src/engine/renderer/vulkan/renderer_vulkan.cc
../../../src/engine/renderer/vulkan/vulkan_context_android.cc
../../../src/engine/renderer/vulkan/vulkan_context.cc
../../../src/engine/shader_source.cc
../../../src/engine/solid_quad.cc
../../../src/engine/sound_player.cc
../../../src/engine/sound.cc
../../../src/third_party/android/gl3stub.c
../../../src/third_party/android/GLContext.cpp
../../../src/third_party/glslang/glslang/GenericCodeGen/CodeGen.cpp
../../../src/third_party/glslang/glslang/GenericCodeGen/Link.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/attribute.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/Constant.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/glslang_tab.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/InfoSink.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/Initialize.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/Intermediate.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/intermOut.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/IntermTraverse.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/iomapper.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/limits.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/linkValidate.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/parseConst.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/ParseContextBase.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/ParseHelper.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/PoolAlloc.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/preprocessor/Pp.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/preprocessor/PpAtom.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/preprocessor/PpContext.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/propagateNoContraction.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/reflection.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/RemoveTree.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/Scan.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/ShaderLang.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/SpirvIntrinsics.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/SymbolTable.cpp
../../../src/third_party/glslang/glslang/MachineIndependent/Versions.cpp
../../../src/third_party/glslang/glslang/OSDependent/Unix/ossource.cpp
../../../src/third_party/glslang/OGLCompilersDLL/InitializeDll.cpp
../../../src/third_party/glslang/SPIRV/disassemble.cpp
../../../src/third_party/glslang/SPIRV/doc.cpp
../../../src/third_party/glslang/SPIRV/GlslangToSpv.cpp
../../../src/third_party/glslang/SPIRV/InReadableOrder.cpp
../../../src/third_party/glslang/SPIRV/Logger.cpp
../../../src/third_party/glslang/SPIRV/SpvBuilder.cpp
../../../src/third_party/glslang/SPIRV/SpvPostProcess.cpp
../../../src/third_party/glslang/SPIRV/SPVRemapper.cpp
../../../src/third_party/glslang/SPIRV/SpvTools.cpp
../../../src/third_party/jsoncpp/jsoncpp.cpp
../../../src/third_party/minizip/ioapi.c
../../../src/third_party/minizip/unzip.c
../../../src/third_party/spirv-reflect/spirv_reflect.c
../../../src/third_party/stb/stb_image.c
../../../src/third_party/texture_compressor/dxt_encoder_internals.cc
../../../src/third_party/texture_compressor/dxt_encoder.cc
../../../src/third_party/texture_compressor/texture_compressor_etc1.cc
../../../src/third_party/texture_compressor/texture_compressor.cc
../../../src/third_party/vma/vk_mem_alloc.cpp
../../../src/third_party/volk/volk.c
)
if (ANDROID_ABI STREQUAL armeabi-v7a)
target_sources(kaliber PRIVATE ../../../src/third_party/texture_compressor/dxt_encoder_neon.cc)
target_sources(kaliber PRIVATE ../../../src/third_party/texture_compressor/texture_compressor_etc1_neon.cc)
set_source_files_properties(../../../src/third_party/texture_compressor/dxt_encoder_neon.cc PROPERTIES COMPILE_FLAGS -mfpu=neon)
set_source_files_properties(../../../src/third_party/texture_compressor/texture_compressor_etc1_neon.cc PROPERTIES COMPILE_FLAGS -mfpu=neon)
endif()
if (ANDROID_ABI STREQUAL arm64-v8a)
target_sources(kaliber PRIVATE ../../../src/third_party/texture_compressor/dxt_encoder_neon.cc)
target_sources(kaliber PRIVATE ../../../src/third_party/texture_compressor/texture_compressor_etc1_neon.cc)
endif()
target_link_libraries(kaliber PRIVATE
common_config
android
native_app_glue
oboe
cpufeatures
EGL
GLESv2
log
z
)

View File

@ -1,45 +1,131 @@
apply plugin: 'com.android.application'
apply plugin: Utils
android {
compileSdk 33
ndkVersion '25.2.9519653'
compileSdk rootProject.ext.compileSdk
ndkVersion rootProject.ext.ndkVersion
defaultConfig {
applicationId = 'com.woom.game'
minSdk 24
targetSdk 33
externalNativeBuild {
cmake {
arguments '-DANDROID_STL=c++_static'
}
}
minSdk rootProject.ext.minSdk
targetSdk rootProject.ext.targetSdk
ndk {
abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64'
abiFilters = []
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
flavorDimensions 'game', 'arch'
productFlavors {
helloWorld {
dimension 'game'
applicationId 'com.kaliber.helloworld'
resValue "string", "app_name", "Kaliber Hello World"
manifestPlaceholders = [appIcon: "@mipmap/ic_launcher"]
ext {
gnTarget = "hello_world"
}
}
demo {
dimension 'game'
applicationId 'com.kaliber.woom'
resValue "string", "app_name", "Kaliber Demo"
manifestPlaceholders = [appIcon: "@mipmap/ic_launcher"]
ext {
gnTarget = "demo"
}
}
woom {
dimension 'game'
applicationId 'com.woom.game'
resValue "string", "app_name", "woom"
resValue "string", "interstitial_ad_unit_id", "ca-app-pub-1321063817979967/8373182022"
resValue "string", "admob_application_id", "ca-app-pub-1321063817979967~1100949243"
manifestPlaceholders = [appIcon: "@mipmap/ic_launcher"]
ext {
gnTarget = "demo"
}
}
arm7 {
dimension 'arch'
ndk {
debugSymbolLevel 'FULL'
abiFilters = ["armeabi-v7a"]
}
}
arm8 {
dimension 'arch'
ndk {
abiFilters = ["arm64-v8a"]
}
}
x86 {
dimension 'arch'
ndk {
abiFilters = ["x86"]
}
}
x86_64 {
dimension 'arch'
ndk {
abiFilters = ["x86_64"]
}
}
allArchs {
dimension 'arch'
ndk {
abiFilters = ["armeabi-v7a", "arm64-v8a", "x86_64", "x86"]
}
}
armOnly {
dimension 'arch'
ndk {
abiFilters = ["armeabi-v7a", "arm64-v8a"]
}
}
x86Only {
dimension 'arch'
ndk {
abiFilters = ["x86_64", "x86"]
}
}
// Native library name is same as GN target name.
android.productFlavors.each { flavor ->
if (flavor.dimension == 'game') {
"${flavor.name}" {
resValue "string", "lib_name", flavor.ext.gnTarget
buildConfigField 'String', 'NATIVE_LIBRARY', "\"${flavor.ext.gnTarget}\""
}
}
}
}
externalNativeBuild {
cmake {
version '3.22.1'
path 'CMakeLists.txt'
}
}
sourceSets {
main {
java.srcDirs += ['../../../src/engine/platform/java/com/kaliber/base']
assets.srcDirs = ['../../../assets']
android.buildTypes.each { buildType ->
"${buildType.name}" {
assets.srcDirs = ["${utils.getGnOutDir(buildType.name)}/assets"]
}
}
}
android.buildTypes.each { buildType ->
"${buildType.name}" {
jniLibs.srcDirs = ["${utils.getGnOutDir(buildType.name)}/jniLibs"]
}
}
}
namespace 'com.woom.game'
namespace "com.kaliber.base"
}
dependencies {
@ -48,3 +134,247 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'com.google.android.gms:play-services-ads:22.0.0'
}
//
// Tasks for GN build
//
// Generate `args.gn` which is used by GN to take in build arguments.
utils.addTask('generateGnArgsFor') { String taskName, String buildType, String arch ->
task(taskName, type: WriteFileTask) {
content = utils.generateGnArgsContent(buildType, arch)
target = project.layout.file(provider { new File("${utils.getGnOutDir(buildType)}/${arch}", 'args.gn') })
}
}
// Run `gn gen` to generate ninja files.
utils.addTask('runGnFor') { String taskName, String buildType, String arch ->
task(taskName, type: Exec) {
dependsOn "generateGnArgsFor${arch}${buildType}"
executable rootProject.ext.gn
args '--fail-on-unused-args', 'gen', "${utils.getGnOutDir(buildType)}/${arch}"
// Need to run gn only once unless the configuration in `args.gn` changes.
inputs.file(new File("${utils.getGnOutDir(buildType)}/${arch}", 'args.gn'))
outputs.file(new File("${utils.getGnOutDir(buildType)}/${arch}", 'build.ninja'))
}
}
// Build the native library for the target game using ninja.
utils.addGameTask('runNinjaFor') { String taskName, String buildType, String arch, String game ->
task(taskName, type: Exec) {
dependsOn "runGnFor${arch}${buildType}"
executable rootProject.ext.ninja
args '-C', "${utils.getGnOutDir(buildType)}/${arch}", "src/${utils.getGnTargetFor(game)}"
// Always run ninja and let it figure out what needs to be compiled.
outputs.upToDateWhen { false }
}
}
// Assets can be obtained from the output directory of any arch but it would be good to combine them
// in a single directory in case we are buildig multi-arch and some build config have different assets.
utils.addGameTask('copyAssetsFor') { String taskName, String buildType, String arch, String game ->
task(taskName, type: Copy) {
dependsOn "runNinjaFor${game}${arch}${buildType}"
from "${utils.getGnOutDir(buildType)}/${arch}/assets"
into "${utils.getGnOutDir(buildType)}/assets"
}
}
// Copy the native library to a directory denoting its arch code as the Android Gradle plugin expects.
utils.addGameTask('copyJniLibsFor') { String taskName, String buildType, String arch, String game ->
task(taskName, type: Copy) {
dependsOn "runNinjaFor${game}${arch}${buildType}"
from("${utils.getGnOutDir(buildType)}/${arch}") {
include "lib${utils.getGnTargetFor(game)}.so"
}
into "${utils.getGnOutDir(buildType)}/jniLibs/${utils.getAbiCodeFor(arch)}"
}
}
tasks.configureEach { task ->
def variantPattern = /(\w+)(${utils.getArchTypesRegExp()})(${utils.getBuildTypesRegExp()})/
def match = task.name =~ /^merge/ + variantPattern + /JniLibFolders$/
if (match) {
utils.project.android.productFlavors.find { arch ->
if (arch.dimension == 'arch' && arch.name.capitalize() == match.group(2)) {
// Depends on each arch type for multi-arch build flavors.
arch.ndk.abiFilters.each { abi ->
task.dependsOn "copyJniLibsFor${match.group(1)}${utils.ARCH_CODES[abi]}${match.group(3)}"
}
return true
}
}
return
}
match = task.name =~ /^generate/ + variantPattern + /Assets$/
if (match) {
utils.project.android.productFlavors.find { arch ->
if (arch.dimension == 'arch' && arch.name.capitalize() == match.group(2)) {
// Depends on each arch type for multi-arch build flavors.
arch.ndk.abiFilters.each { abi ->
task.dependsOn "copyAssetsFor${match.group(1)}${utils.ARCH_CODES[abi]}${match.group(3)}"
}
return true
}
}
return
}
match = task.name =~ /^lintVitalAnalyze/ + variantPattern + /$/
if (match) {
utils.project.android.productFlavors.find { arch ->
if (arch.dimension == 'arch' && arch.name.capitalize() == match.group(2)) {
// Depends on each arch type for multi-arch build flavors.
arch.ndk.abiFilters.each { abi ->
task.dependsOn "copyAssetsFor${match.group(1)}${utils.ARCH_CODES[abi]}${match.group(3)}"
}
return true
}
}
return
}
}
//
// Utils plugin
//
class Utils implements Plugin<Project> {
final def ARCH_CODES = ["armeabi-v7a": "Arm7",
"arm64-v8a": "Arm8",
"x86_64": "X86_64",
"x86": "X86"].asImmutable()
final def GN_CPU_CODES = ["Arm7": "arm",
"Arm8": "arm64",
"X86_64": "x64",
"X86": "x86"].asImmutable()
def project
@Inject
Utils(Project project) {
this.project = project
}
void apply(Project project) {
project.extensions.create('utils', Utils)
}
// Add a task for archs and buildTypes variants.
void addTask(String prefix, Closure taskClosure) {
forEachBuildVariant { String arch, String buildType ->
def taskName = "${prefix}${arch}${buildType}"
taskClosure(taskName, buildType, arch)
}
}
// Add a task for games, archs and buildTypes variants.
void addGameTask(String prefix, Closure taskClosure) {
forEachGameBuildVariant { String game, String arch, String buildType ->
def taskName = "${prefix}${game}${arch}${buildType}"
taskClosure(taskName, buildType, arch, game)
}
}
void forEachBuildVariant(Closure callback) {
project.android.productFlavors.each { arch ->
// Only need to add tasks for arch types which maps to a single ABI
if (arch.dimension == 'arch' && arch.ndk.abiFilters.size() == 1) {
project.android.buildTypes.each { buildType ->
callback(arch.name.capitalize(), buildType.name.capitalize())
}
}
}
}
void forEachGameBuildVariant(Closure callback) {
project.android.productFlavors.each { game ->
if (game.dimension == 'game') {
project.android.productFlavors.each { arch ->
// Only need to add tasks for arch types which maps to a single ABI
if (arch.dimension == 'arch' && arch.ndk.abiFilters.size() == 1) {
project.android.buildTypes.each { buildType ->
callback(game.name.capitalize(), arch.name.capitalize(), buildType.name.capitalize())
}
}
}
}
}
}
def getBuildTypesRegExp() {
def outList = []
project.android.buildTypes.each { buildType ->
outList += buildType.name.capitalize()
}
return outList.join('|')
}
def getArchTypesRegExp() {
def outList = []
project.android.productFlavors.each { flavor ->
if (flavor.dimension == 'arch') {
outList += flavor.name.capitalize()
}
}
return outList.join('|')
}
def getAbiCodeFor(String arch) {
def outStr = ''
project.android.productFlavors.find { flavor ->
if (flavor.name.capitalize() == arch) {
outStr = flavor.ndk.abiFilters.first()
return true
}
}
return outStr
}
def getGnTargetFor(String game) {
def outStr = ''
project.android.productFlavors.find { flavor ->
if (flavor.dimension == 'game' && flavor.name.capitalize() == game) {
outStr = flavor.ext.gnTarget
return true
}
}
return outStr
}
def generateGnArgsContent(String buildType, String arch) {
def content = 'target_os="android"\n'
content += 'target_cpu="' + GN_CPU_CODES[arch] + '"\n'
content += "is_debug=${buildType != 'Release'}\n"
content += 'ndk="' + project.android.ndkDirectory + '"\n'
content += "ndk_api=${project.rootProject.ext.minSdk}\n"
return content
}
def getGnOutDir(String buildType) {
return "${project.buildDir}/gn_out/${buildType.toLowerCase()}"
}
}
abstract class WriteFileTask extends DefaultTask {
@Input
abstract Property<String> getContent()
@OutputFile
abstract RegularFileProperty getTarget()
@TaskAction
void run() {
def file = target.get().asFile
file.parentFile.mkdirs()
def text = content.get()
if (!file.exists() || text != file.text)
file.text = text
}
}

View File

@ -6,7 +6,7 @@
<application
android:allowBackup="false"
android:fullBackupContent="false"
android:icon="@mipmap/ic_launcher"
android:icon="${appIcon}"
android:label="@string/app_name">
<activity
@ -27,16 +27,16 @@
</intent-filter>
<meta-data
android:name="android.app.lib_name"
android:value="kaliber" />
android:value="@string/lib_name" />
</activity>
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-1321063817979967~1100949243" />
android:value="@string/admob_application_id" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.woom.game.fileprovider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">woom</string>
<string name="interstitial_ad_unit_id">ca-app-pub-1321063817979967/8373182022</string>
<string name="app_name">Kaliber app</string>
<string name="interstitial_ad_unit_id"></string>
<string name="admob_application_id">ca-app-pub-3940256099942544~3347511713</string>
</resources>

View File

@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.0.1'
classpath 'com.android.tools.build:gradle:8.1.0'
}
}
@ -19,3 +19,17 @@ allprojects {
task clean(type: Delete) {
delete rootProject.buildDir
}
ext {
ndkVersion = '25.2.9519653'
minSdk = 24
compileSdk = 33
targetSdk = 33
if (!project.hasProperty('gn')) {
gn = "gn"
}
if (!project.hasProperty('ninja')) {
ninja = "ninja"
}
}

89
build/android/gradlew.bat vendored Normal file
View File

@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -1,283 +0,0 @@
.DEFAULT_GOAL := all
# --- Input variables ---
BUILD ?= release
ifeq ($(findstring $(BUILD),debug release),)
$(error BUILD must be set to debug or release)
endif
# Build all executables by default.
APPS ?= demo
# If the VERBOSE flag isn't set, then mute superfluous output.
ifeq ($(VERBOSE),)
HUSH_COMPILE = @echo "Compiling $<";
HUSH_LINK = @echo "Linking $@";
HUSH_GENERATE = @echo "Generating $@";
HUSH_CLEAN = @
endif
# --- Internal variables ---
ARCH := $(shell uname -p)
SRC_ROOT := $(abspath ../../src)
OUTPUT_DIR := $(abspath .)
INTERMEDIATE_DIR := $(OUTPUT_DIR)/obj
BUILD_DIR := $(INTERMEDIATE_DIR)/$(BUILD)
ARFLAGS = r
LDFLAGS = -lX11 -lGL -pthread -lasound -ldl
# Always enable debug information.
CFLAGS += -g
# Enable extra warnings and make all warnings into errors.
CFLAGS += -Wextra -Werror
# Flags to generate dependency information.
CFLAGS += -MD -MP -MT $@
# Predefined flags.
ifeq ($(BUILD), debug)
CFLAGS += -D_DEBUG
CFLAGS += -D_GLIBCXX_DEBUG
endif
# Enable compiler optimizations for everything except debug.
# Note that a very aggresssive optimization level is used and it may not be
# valid for all standard compliant programs. Reduce this level on individual
# files or modules as needed.
ifneq ($(BUILD), debug)
CFLAGS += -Ofast
endif
# Flag to turn on extended instruction sets for the compiler.
CFLAGS += -msse2
# Let C++ inherit all C flags.
CXXFLAGS = $(CFLAGS) -I$(SRC_ROOT)
# Enable C++20
CXXFLAGS += -std=c++20
# Vulkan config
CFLAGS += -DVK_USE_PLATFORM_XLIB_KHR
CFLAGS += -DVMA_STATIC_VULKAN_FUNCTIONS=1
CFLAGS += -I$(SRC_ROOT)/third_party/vulkan/include
CXXFLAGS += -I$(SRC_ROOT)/third_party/glslang
# --- Internal functions ---
app_exe = $(OUTPUT_DIR)/$(1)_$(ARCH)_$(BUILD)
objs_from_src = $(patsubst $(SRC_ROOT)/%, $(BUILD_DIR)/%.o, $(basename $(1)))
objs_from_src_in = $(call objs_from_src, $(shell find $(1) -name "*.cc" -o -name "*.cpp" -o -name "*.c"))
# --- Base ---
BASE_SRC := \
$(SRC_ROOT)/base/collusion_test.cc \
$(SRC_ROOT)/base/log.cc \
$(SRC_ROOT)/base/task_runner.cc \
$(SRC_ROOT)/base/thread_pool.cc \
$(SRC_ROOT)/base/timer.cc
BASE_LIB := $(BUILD_DIR)/libengine.a
BASE_OBJS := $(call objs_from_src, $(BASE_SRC))
LIBS += $(BASE_LIB)
OBJS += $(BASE_OBJS)
$(BASE_LIB): $(BASE_OBJS)
# --- Engine ---
ENGINE_SRC := \
$(SRC_ROOT)/engine/animatable.cc \
$(SRC_ROOT)/engine/animator.cc \
$(SRC_ROOT)/engine/audio/audio_bus.cc \
$(SRC_ROOT)/engine/audio/audio_mixer.cc \
$(SRC_ROOT)/engine/audio/audio_sink_alsa.cc \
$(SRC_ROOT)/engine/audio/sinc_resampler.cc \
$(SRC_ROOT)/engine/drawable.cc \
$(SRC_ROOT)/engine/engine.cc \
$(SRC_ROOT)/engine/font.cc \
$(SRC_ROOT)/engine/image_quad.cc \
$(SRC_ROOT)/engine/image.cc \
$(SRC_ROOT)/engine/mesh.cc \
$(SRC_ROOT)/engine/persistent_data.cc \
$(SRC_ROOT)/engine/platform/asset_file_linux.cc \
$(SRC_ROOT)/engine/platform/asset_file.cc \
$(SRC_ROOT)/engine/platform/platform_linux.cc \
$(SRC_ROOT)/engine/renderer/geometry.cc \
$(SRC_ROOT)/engine/renderer/opengl/render_command.cc \
$(SRC_ROOT)/engine/renderer/opengl/renderer_opengl_linux.cc \
$(SRC_ROOT)/engine/renderer/opengl/renderer_opengl.cc \
$(SRC_ROOT)/engine/renderer/renderer_types.cc \
$(SRC_ROOT)/engine/renderer/shader.cc \
$(SRC_ROOT)/engine/renderer/texture.cc \
$(SRC_ROOT)/engine/renderer/vulkan/renderer_vulkan_linux.cc \
$(SRC_ROOT)/engine/renderer/vulkan/renderer_vulkan.cc \
$(SRC_ROOT)/engine/renderer/vulkan/vulkan_context_linux.cc \
$(SRC_ROOT)/engine/renderer/vulkan/vulkan_context.cc \
$(SRC_ROOT)/engine/shader_source.cc \
$(SRC_ROOT)/engine/solid_quad.cc \
$(SRC_ROOT)/engine/sound_player.cc \
$(SRC_ROOT)/engine/sound.cc
ENGINE_LIB := $(BUILD_DIR)/libengine.a
ENGINE_OBJS := $(call objs_from_src, $(ENGINE_SRC))
LIBS += $(ENGINE_LIB)
OBJS += $(ENGINE_OBJS)
$(ENGINE_LIB): $(ENGINE_OBJS)
# --- Third-party ---
# Ignore warnings.
THIRD_PARTY_CFLAGS = $(CFLAGS)
THIRD_PARTY_CFLAGS := $(filter-out -Wextra -Werror, $(THIRD_PARTY_CFLAGS))
THIRD_PARTY_CXXFLAGS = $(CXXFLAGS)
THIRD_PARTY_CXXFLAGS := $(filter-out -Wextra -Werror, $(THIRD_PARTY_CXXFLAGS))
THIRD_PARTY_SRC := \
$(SRC_ROOT)/third_party/glew/glew.c \
$(SRC_ROOT)/third_party/glslang/glslang/GenericCodeGen/CodeGen.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/GenericCodeGen/Link.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/attribute.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/Constant.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/glslang_tab.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/InfoSink.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/Initialize.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/Intermediate.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/intermOut.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/IntermTraverse.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/iomapper.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/limits.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/linkValidate.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/parseConst.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/ParseContextBase.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/ParseHelper.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/PoolAlloc.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/preprocessor/Pp.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/preprocessor/PpAtom.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/preprocessor/PpContext.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/propagateNoContraction.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/reflection.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/RemoveTree.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/Scan.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/ShaderLang.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/SpirvIntrinsics.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/SymbolTable.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/Versions.cpp \
$(SRC_ROOT)/third_party/glslang/glslang/OSDependent/Unix/ossource.cpp \
$(SRC_ROOT)/third_party/glslang/OGLCompilersDLL/InitializeDll.cpp \
$(SRC_ROOT)/third_party/glslang/SPIRV/disassemble.cpp \
$(SRC_ROOT)/third_party/glslang/SPIRV/doc.cpp \
$(SRC_ROOT)/third_party/glslang/SPIRV/GlslangToSpv.cpp \
$(SRC_ROOT)/third_party/glslang/SPIRV/InReadableOrder.cpp \
$(SRC_ROOT)/third_party/glslang/SPIRV/Logger.cpp \
$(SRC_ROOT)/third_party/glslang/SPIRV/SpvBuilder.cpp \
$(SRC_ROOT)/third_party/glslang/SPIRV/SpvPostProcess.cpp \
$(SRC_ROOT)/third_party/glslang/SPIRV/SPVRemapper.cpp \
$(SRC_ROOT)/third_party/glslang/SPIRV/SpvTools.cpp \
$(SRC_ROOT)/third_party/jsoncpp/jsoncpp.cpp \
$(SRC_ROOT)/third_party/spirv-reflect/spirv_reflect.c \
$(SRC_ROOT)/third_party/stb/stb_image.c \
$(SRC_ROOT)/third_party/texture_compressor/dxt_encoder_internals.cc \
$(SRC_ROOT)/third_party/texture_compressor/dxt_encoder.cc \
$(SRC_ROOT)/third_party/texture_compressor/texture_compressor_etc1.cc \
$(SRC_ROOT)/third_party/texture_compressor/texture_compressor.cc \
$(SRC_ROOT)/third_party/vma/vk_mem_alloc.cpp \
$(SRC_ROOT)/third_party/volk/volk.c
$(BUILD_DIR)/third_party/%.o: $(SRC_ROOT)/third_party/%.c
@mkdir -p $(@D)
$(HUSH_COMPILE) $(CC) -c $(THIRD_PARTY_CFLAGS) -o $@ $<
$(BUILD_DIR)/third_party/%.o: $(SRC_ROOT)/third_party/%.cc
@mkdir -p $(@D)
$(HUSH_COMPILE) $(CXX) -c $(THIRD_PARTY_CXXFLAGS) -o $@ $<
$(BUILD_DIR)/third_party/%.o: $(SRC_ROOT)/third_party/%.cpp
@mkdir -p $(@D)
$(HUSH_COMPILE) $(CXX) -c $(THIRD_PARTY_CXXFLAGS) -o $@ $<
THIRD_PARTY_LIB := $(BUILD_DIR)/libthirdparty.a
THIRD_PARTY_OBJS := $(call objs_from_src, $(THIRD_PARTY_SRC))
LIBS += $(THIRD_PARTY_LIB)
OBJS += $(THIRD_PARTY_OBJS)
$(THIRD_PARTY_LIB): $(THIRD_PARTY_OBJS)
# --- demo application ---
ifneq ($(filter demo,$(APPS)),)
DEMO_SRC := \
$(SRC_ROOT)/demo/credits.cc \
$(SRC_ROOT)/demo/demo.cc \
$(SRC_ROOT)/demo/enemy.cc \
$(SRC_ROOT)/demo/hud.cc \
$(SRC_ROOT)/demo/menu.cc \
$(SRC_ROOT)/demo/player.cc \
$(SRC_ROOT)/demo/sky_quad.cc
DEMO_EXE := $(call app_exe,demo)
DEMO_OBJS := $(call objs_from_src, $(DEMO_SRC))
EXES += $(DEMO_EXE)
OBJS += $(DEMO_OBJS)
$(DEMO_EXE): $(DEMO_OBJS) $(LIBS)
endif
# --- Build rules ---
# Dependencies.
DEPS = $(OBJS:.o=.d)
-include $(DEPS)
.PHONY: all clean cleanall help
all: $(EXES)
clean:
@echo "Cleaning..."
$(HUSH_CLEAN) $(RM) -r $(BUILD_DIR)
cleanall:
@echo "Cleaning all..."
$(HUSH_CLEAN) $(RM) -r $(INTERMEDIATE_DIR)
help:
@echo "BUILD = Build mode. One of:"
@echo " debug (no optimizations)"
@echo " release (optimizations, the default)"
@echo "APPS = Applications to build. Defaults to all."
@echo "VERBOSE = Full output from commands if set."
# It's important that libraries are specified last as Ubuntu uses "ld --as-needed" by default.
# Only the static libraries referenced by the object files will be linked into the executable.
# Beware that circular dependencies doesn't work with this flag.
$(EXES):
@mkdir -p $(@D)
$(HUSH_LINK) $(CXX) -o $@ $^ $(LDFLAGS)
$(BUILD_DIR)/%.a:
@mkdir -p $(@D)
$(HUSH_GENERATE) $(AR) $(ARFLAGS) $@ $^
$(BUILD_DIR)/%.o: $(SRC_ROOT)/%.c
@mkdir -p $(@D)
$(HUSH_COMPILE) $(CC) -c $(CFLAGS) -o $@ $<
$(BUILD_DIR)/%.o: $(SRC_ROOT)/%.cc
@mkdir -p $(@D)
$(HUSH_COMPILE) $(CXX) -c $(CXXFLAGS) -o $@ $<
$(BUILD_DIR)/%.o: $(SRC_ROOT)/%.cpp
@mkdir -p $(@D)
$(HUSH_COMPILE) $(CXX) -c $(CXXFLAGS) -o $@ $<

11
build/rules.gni Normal file
View File

@ -0,0 +1,11 @@
# Build a shared library for Android. Build an executable for other platforms.
template("game") {
if (target_os == "android") {
_target_type = "shared_library"
} else {
_target_type = "executable"
}
target(_target_type, target_name) {
forward_variables_from(invoker, "*")
}
}

21
build/toolchain/copy.py Normal file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env python
import os
import shutil
import sys
src, dst = sys.argv[1:]
if os.path.exists(dst):
if os.path.isdir(dst):
shutil.rmtree(dst)
else:
os.remove(dst)
if os.path.isdir(src):
shutil.copytree(src, dst)
else:
shutil.copy2(src, dst)
# https://github.com/ninja-build/ninja/issues/1554
os.utime(dst, None)

View File

@ -0,0 +1,130 @@
toolchain("gcc") {
lib_switch = "-l"
lib_dir_switch = "-L"
tool("asm") {
depfile = "{{output}}.d"
command = "$cc -MD -MF $depfile {{defines}} {{include_dirs}} {{asmflags}} -c {{source}} -o {{output}}"
depsformat = "gcc"
outputs =
[ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
description = "assemble {{source}}"
}
tool("cc") {
depfile = "{{output}}.d"
command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}"
depsformat = "gcc"
outputs =
[ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
description = "compile {{output}}"
}
tool("cxx") {
depfile = "{{output}}.d"
command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}"
depsformat = "gcc"
outputs =
[ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
description = "compile {{output}}"
}
tool("objc") {
depfile = "{{output}}.d"
command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{framework_dirs}} {{cflags}} {{cflags_c}} {{cflags_objc}} -c {{source}} -o {{output}}"
depsformat = "gcc"
outputs =
[ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
description = "compile {{output}}"
}
tool("objcxx") {
depfile = "{{output}}.d"
command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{framework_dirs}} {{cflags}} {{cflags_cc}} {{cflags_objcc}} -c {{source}} -o {{output}}"
depsformat = "gcc"
outputs =
[ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
description = "compile {{output}}"
}
if (is_apple) {
not_needed([ "ar" ]) # libtool is used instead.
}
tool("alink") {
if (is_apple) {
command =
"libtool -static -o {{output}} -no_warning_for_no_symbols {{inputs}}"
} else {
command = "rm -f {{output}} && $ar rcs {{output}} {{inputs}}"
}
outputs =
[ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
default_output_extension = ".a"
output_prefix = "lib"
description = "link {{target_output_name}}{{output_extension}}"
}
tool("solink") {
soname = "{{target_output_name}}{{output_extension}}"
sofile = "{{output_dir}}/$soname"
rspfile = soname + ".rsp"
if (is_apple) {
os_specific_option = "-install_name @executable_path/$sofile"
rspfile_content = "{{inputs}} {{solibs}} {{libs}}"
} else {
os_specific_option = "-Wl,-soname=$soname"
rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}}"
}
command = "$cxx -shared {{ldflags}} {{frameworks}} -o $sofile $os_specific_option @$rspfile"
default_output_extension = ".so"
default_output_dir = "{{root_out_dir}}"
outputs = [ sofile ]
link_output = sofile
depend_output = sofile
output_prefix = "lib"
description = "link $soname"
}
tool("link") {
outfile = "{{target_output_name}}{{output_extension}}"
rspfile = "$outfile.rsp"
rspfile_content = "{{inputs}}"
if (is_apple) {
command = "$cxx {{ldflags}} {{solibs}} {{libs}} {{frameworks}} -o $outfile @$rspfile"
} else {
command = "$cxx {{ldflags}} -o $outfile -Wl,--start-group @$rspfile {{solibs}} -Wl,--end-group {{libs}}"
}
default_output_dir = "{{root_out_dir}}"
outputs = [ outfile ]
description = "link $outfile"
}
if (host_os == "win") {
tool("stamp") {
command = "cmd.exe /c echo > {{output}}"
description = "stamp {{output}}"
}
tool("copy") {
# Note: The build in copy command can't handle forward slashes as path separators.
# Use a python script as a work around.
# command = "cmd.exe /c copy /Y {{source}} {{output}}"
copy_cmd = rebase_path("../copy.py")
command = "python \"$copy_cmd\" {{source}} {{output}}"
description = "copy {{source}} {{output}}"
}
} else {
tool("stamp") {
command = "touch {{output}}"
description = "stamp {{output}}"
}
tool("copy") {
command = "cp -af {{source}} {{output}}"
description = "copy {{source}} {{output}}"
}
}
}

View File

@ -0,0 +1,90 @@
toolchain("msvc") {
lib_switch = ""
lib_dir_switch = "/LIBPATH:"
tool("asm") {
command = "ml.exe {{asmflags}} /nologo /c /Fo {{output}} {{source}}"
outputs =
[ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.obj" ]
description = "assemble {{source}}"
}
tool("cc") {
pdbname = "{{target_out_dir}}/{{label_name}}_c.pdb"
command = "cl.exe /nologo /showIncludes /FC {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} /c {{source}} /Fo{{output}} /Fd\"$pdbname\""
depsformat = "msvc"
outputs =
[ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.obj" ]
description = "compile {{source}}"
}
tool("cxx") {
pdbname = "{{target_out_dir}}/{{label_name}}_c.pdb"
command = "cl.exe /nologo /showIncludes /FC {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} /c {{source}} /Fo{{output}} /Fd\"$pdbname\""
depsformat = "msvc"
outputs =
[ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.obj" ]
description = "compile {{source}}"
}
tool("alink") {
rspfile = "{{output}}.rsp"
command = "lib.exe /nologo {{arflags}} /OUT:{{output}} @$rspfile"
outputs = [ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" ]
default_output_extension = ".lib"
default_output_dir = "{{target_out_dir}}"
rspfile_content = "{{inputs_newline}}"
description = "link {{output}}"
}
tool("solink") {
dllname = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
libname = "${dllname}.lib"
pdbname = "${dllname}.pdb"
rspfile = "${dllname}.rsp"
command = "link.exe /nologo /IMPLIB:$libname /DLL /OUT:$dllname /PDB:$pdbname @$rspfile"
outputs = [
dllname,
libname,
pdbname,
]
default_output_extension = ".dll"
default_output_dir = "{{root_out_dir}}"
link_output = libname
depend_output = libname
runtime_outputs = [
dllname,
pdbname,
]
rspfile_content = "{{inputs_newline}} {{libs}} {{solibs}} {{ldflags}}"
description = "link {{output}}"
}
tool("link") {
exename = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"
pdbname = "$exename.pdb"
rspfile = "$exename.rsp"
command = "link.exe /nologo /OUT:$exename /PDB:$pdbname @$rspfile"
default_output_extension = ".exe"
default_output_dir = "{{root_out_dir}}"
outputs = [ exename ]
rspfile_content = "{{inputs_newline}} {{libs}} {{solibs}} {{ldflags}}"
description = "link {{output}}"
}
tool("stamp") {
command = "cmd.exe /c echo > {{output}}"
description = "stamp {{output}}"
}
tool("copy") {
# Note: The build in copy command can't handle forward slashes as path separators.
# Use a python script as a work around.
# command = "cmd.exe /c copy /Y {{source}} {{output}}"
copy_cmd = rebase_path("../copy.py")
command = "python \"$copy_cmd\" {{source}} {{output}}"
description = "copy {{source}} {{output}}"
}
}

24
src/base/BUILD.gn Normal file
View File

@ -0,0 +1,24 @@
source_set("base") {
sources = [
"closure.h",
"collusion_test.cc",
"collusion_test.h",
"file.h",
"hash.h",
"interpolation.h",
"log.cc",
"log.h",
"mem.cc",
"mem.h",
"misc.h",
"random.h",
"task_runner.cc",
"task_runner.h",
"thread_pool.cc",
"thread_pool.h",
"timer.h",
"vecmath.h",
]
deps = []
}

View File

@ -10,14 +10,13 @@
#define HERE std::make_tuple(__func__, __FILE__, __LINE__)
// Helper for logging location info, e.g. LOG << LOCATION(from)
// Helper for logging location info, e.g. LOG(0) << LOCATION(from)
#define LOCATION(from) \
std::get<0>(from) << "() [" << [](const char* path) -> std::string { \
std::string file_name(path); \
size_t last_slash_pos = file_name.find_last_of("\\/"); \
std::get<0>(from) << "() [" << [](std::string path) -> std::string { \
size_t last_slash_pos = path.find_last_of("\\/"); \
if (last_slash_pos != std::string::npos) \
file_name = file_name.substr(last_slash_pos + 1); \
return file_name; \
path = path.substr(last_slash_pos + 1); \
return path; \
}(std::get<1>(from)) << ":" \
<< std::get<2>(from) << "]"
@ -36,7 +35,7 @@ using Closure = std::function<void()>;
// Provides location info (function name, file name and line number) where of a
// Closure was constructed.
using Location = std::tuple<const char*, const char*, int>;
using Location = std::tuple<std::string, std::string, int>;
#else

View File

@ -6,17 +6,15 @@
namespace base {
// Compile-time string hashing function.
template <size_t N>
constexpr inline size_t Hash(const char (&str)[N], size_t Len = N - 1) {
constexpr inline size_t KR2Hash(const char (&str)[N], size_t Len = N - 1) {
size_t hash_value = 0;
for (int i = 0; str[i] != '\0'; ++i)
hash_value = str[i] + 31 * hash_value;
return hash_value;
}
// The same hashing function for run-time.
inline size_t Hash(const std::string& str) {
inline size_t KR2Hash(const std::string& str) {
size_t hash_value = 0;
for (std::string::value_type c : str)
hash_value = c + 31 * hash_value;

View File

@ -2,52 +2,74 @@
#if defined(__ANDROID__)
#include <android/log.h>
#elif defined(_WIN32)
#include <windows.h>
#include <format>
#else
#include <cstdio>
#endif
#include <cstdlib>
#include <string>
namespace base {
namespace {
int g_max_log_verbosity_level = 0;
} // namespace
// This is never instantiated, it's just used for EAT_STREAM_PARAMETERS to have
// an object of the correct type on the LHS of the unused part of the ternary
// operator.
std::ostream* LogMessage::swallow_stream;
LogMessage::LogMessage(const char* file, int line) : file_(file), line_(line) {}
int GlobalMaxLogVerbosityLevel() {
return g_max_log_verbosity_level;
}
LogMessage::LogMessage(const char* file, int line, int verbosity_level)
: file_(file), line_(line), verbosity_level_(verbosity_level) {}
LogMessage::~LogMessage() {
stream_ << std::endl;
std::string text(stream_.str());
std::string message(stream_.str());
std::string filename(file_);
size_t last_slash_pos = filename.find_last_of("\\/");
if (last_slash_pos != std::string::npos)
filename = filename.substr(last_slash_pos + 1);
#if defined(__ANDROID__)
__android_log_print(ANDROID_LOG_ERROR, "kaliber", "[%s:%d] %s",
filename.c_str(), line_, text.c_str());
__android_log_print(ANDROID_LOG_ERROR, "kaliber", "%d [%s:%d] %s",
verbosity_level_, filename.c_str(), line_,
message.c_str());
#elif defined(_WIN32)
OutputDebugStringA(
std::format("{} [{}:{}] {}", verbosity_level_, filename, line_, message)
.c_str());
#else
printf("[%s:%d] %s", filename.c_str(), line_, text.c_str());
printf("%d [%s:%d] %s", verbosity_level_, filename.c_str(), line_,
message.c_str());
#endif
}
// static
LogAbort LogAbort::Check(const char* file, int line, const char* expr) {
LogAbort instance(new LogMessage(file, line));
instance.GetLog().stream() << "CHECK: "
<< "(" << expr << ") ";
LogAbort instance(new LogMessage(file, line, 0));
instance.GetLog().stream() << "CHECK(" << expr << ") ";
return instance;
}
// static
LogAbort LogAbort::DCheck(const char* file, int line, const char* expr) {
LogAbort instance(new LogMessage(file, line));
instance.GetLog().stream() << "DCHECK: "
<< "(" << expr << ") ";
LogAbort instance(new LogMessage(file, line, 0));
instance.GetLog().stream() << "DCHECK(" << expr << ") ";
return instance;
}
// static
LogAbort LogAbort::NotReached(const char* file, int line) {
LogAbort instance(new LogMessage(file, line));
instance.GetLog().stream() << "NOTREACHED ";
LogAbort instance(new LogMessage(file, line, 0));
instance.GetLog().stream() << "NOTREACHED() ";
return instance;
}

View File

@ -11,45 +11,66 @@
// CHECK(condition) terminates the process if the condition is false.
// NOTREACHED annotates unreachable codepaths and terminates the process if
// reached.
#define LOG base::LogMessage(__FILE__, __LINE__).stream()
#define LOG_IF(condition) \
LAZY_STREAM(condition, base::LogMessage(__FILE__, __LINE__).stream())
#define CHECK(condition) \
LAZY_STREAM( \
!(condition), \
base::LogAbort::Check(__FILE__, __LINE__, #condition).GetLog().stream())
#define LOG(verbosity_level) \
LAZY_STREAM( \
LOG_IS_ON(verbosity_level), \
::base::LogMessage(__FILE__, __LINE__, verbosity_level).stream())
#define LOG_IF(verbosity_level, condition) \
LAZY_STREAM( \
LOG_IS_ON(verbosity_level) && (condition), \
::base::LogMessage(__FILE__, __LINE__, verbosity_level).stream())
#define CHECK(condition) \
LAZY_STREAM(!(condition), \
::base::LogAbort::Check(__FILE__, __LINE__, #condition) \
.GetLog() \
.stream())
#define NOTREACHED \
base::LogAbort::NotReached(__FILE__, __LINE__).GetLog().stream()
#define NOTREACHED() \
::base::LogAbort::NotReached(__FILE__, __LINE__).GetLog().stream()
// Macros for logging which are active only in debug builds.
#ifdef _DEBUG
#define DLOG base::LogMessage(__FILE__, __LINE__).stream()
#define DLOG_IF(condition) \
LAZY_STREAM(condition, base::LogMessage(__FILE__, __LINE__).stream())
#define DCHECK(condition) \
LAZY_STREAM(!(condition), \
base::LogAbort::DCheck(__FILE__, __LINE__, #condition) \
.GetLog() \
#define DLOG(verbosity_level) \
LAZY_STREAM( \
LOG_IS_ON(verbosity_level), \
::base::LogMessage(__FILE__, __LINE__, verbosity_level).stream())
#define DLOG_IF(verbosity_level, condition) \
LAZY_STREAM( \
LOG_IS_ON(verbosity_level) && (condition), \
::base::LogMessage(__FILE__, __LINE__, verbosity_level).stream())
#define DCHECK(condition) \
LAZY_STREAM(!(condition), \
::base::LogAbort::DCheck(__FILE__, __LINE__, #condition) \
.GetLog() \
.stream())
#else
// "debug mode" logging is compiled away to nothing for release builds.
#define DLOG EAT_STREAM_PARAMETERS
#define DLOG_IF(condition) EAT_STREAM_PARAMETERS
#define DLOG(verbosity_level) EAT_STREAM_PARAMETERS
#define DLOG_IF(verbosity_level, condition) EAT_STREAM_PARAMETERS
#define DCHECK(condition) EAT_STREAM_PARAMETERS
#endif
// Helper macro which avoids evaluating the arguments to a stream if
// the condition doesn't hold.
#define LAZY_STREAM(condition, stream) \
!(condition) ? (void)0 : base::LogMessage::Voidify() & (stream)
!(condition) ? (void)0 : ::base::LogMessage::Voidify() & (stream)
// Avoid any pointless instructions to be emitted by the compiler.
#define EAT_STREAM_PARAMETERS \
LAZY_STREAM(false, *base::LogMessage::swallow_stream)
LAZY_STREAM(false, *::base::LogMessage::swallow_stream)
#if defined(MAX_LOG_VERBOSITY_LEVEL)
#define LOG_IS_ON(verbosity_level) \
((verbosity_level) <= MAX_LOG_VERBOSITY_LEVEL)
#else
#define LOG_IS_ON(verbosity_level) \
((verbosity_level) <= ::base::GlobalMaxLogVerbosityLevel())
#endif
namespace base {
int GlobalMaxLogVerbosityLevel();
class LogMessage {
public:
class Voidify {
@ -61,18 +82,17 @@ class LogMessage {
void operator&(std::ostream&) {}
};
LogMessage(const char* file, int line);
LogMessage(const char* file, int line, int verbosity_level);
~LogMessage();
LogMessage& base() { return *this; }
std::ostream& stream() { return stream_; }
static std::ostream* swallow_stream;
protected:
const char* file_;
const int line_;
int line_;
int verbosity_level_;
std::ostringstream stream_;
};

46
src/base/mem.cc Normal file
View File

@ -0,0 +1,46 @@
#include "base/mem.h"
#include <cstring>
#if defined(__ANDROID__)
#include <malloc.h>
#endif
namespace base {
void* AlignedAlloc(size_t size, size_t alignment) {
DCHECK(size > 0U);
DCHECK(IsPow2(alignment));
DCHECK((alignment % sizeof(void*)) == 0U);
void* ptr = nullptr;
#if defined(_WIN32)
ptr = _aligned_malloc(size, alignment);
#elif defined(__ANDROID__)
ptr = memalign(alignment, size);
#else
int ret = posix_memalign(&ptr, alignment, size);
if (ret != 0) {
DLOG(0) << "posix_memalign() returned with error " << ret;
ptr = nullptr;
}
#endif
// Aligned allocations may fail for non-memory related reasons.
CHECK(ptr) << "Aligned allocation failed. "
<< "size=" << size << ", alignment=" << alignment;
DCHECK(IsAligned(ptr, alignment));
return ptr;
}
void* AlignedRealloc(void* ptr,
size_t old_size,
size_t new_size,
size_t alignment) {
auto* new_ptr = AlignedAlloc(new_size, alignment);
memmove(new_ptr, ptr, old_size);
AlignedFree(ptr);
return new_ptr;
}
} // namespace base

View File

@ -1,26 +1,33 @@
#ifndef BASE_MEM_H
#define BASE_MEM_H
#include <cstdlib>
#include <stddef.h>
#include <stdint.h>
#include <memory>
#if defined(__ANDROID__)
#if defined(_WIN32)
#include <malloc.h>
#else
#include <stdlib.h>
#endif
#include "base/log.h"
#define ALIGN_MEM(alignment) __attribute__((aligned(alignment)))
#include "base/misc.h"
namespace base {
inline void AlignedFree(void* mem) {
#if defined(_WIN32)
_aligned_free(mem);
#else
free(mem);
#endif
}
namespace internal {
struct ScopedAlignedFree {
inline void operator()(void* x) const {
if (x)
free(x);
}
inline void operator()(void* x) const { AlignedFree(x); }
};
} // namespace internal
@ -28,27 +35,16 @@ struct ScopedAlignedFree {
template <typename T>
using AlignedMemPtr = std::unique_ptr<T, internal::ScopedAlignedFree>;
template <int kAlignment>
inline void* AlignedAlloc(size_t size) {
void* ptr = NULL;
#if defined(__ANDROID__)
ptr = memalign(kAlignment, size);
#else
if (posix_memalign(&ptr, kAlignment, size))
ptr = NULL;
#endif
DCHECK(ptr);
// DCHECK(((unsigned)ptr & (kAlignment - 1)) == 0);
return ptr;
}
void* AlignedAlloc(size_t size, size_t alignment);
inline void AlignedFree(void* mem) {
free(mem);
}
void* AlignedRealloc(void* ptr,
size_t old_size,
size_t new_size,
size_t alignment);
template <int kAlignment>
inline bool IsAligned(void* ptr) {
return (reinterpret_cast<uintptr_t>(ptr) & (kAlignment - 1)) == 0U;
inline bool IsAligned(const void* val, size_t alignment) {
DCHECK(IsPow2(alignment)) << alignment << " is not a power of 2";
return (reinterpret_cast<uintptr_t>(val) & (alignment - 1)) == 0;
}
} // namespace base

View File

@ -1,31 +1,42 @@
#ifndef BASE_MISC_H
#define BASE_MISC_H
#include <cstdint>
#define CRASH *((int*)nullptr) = 0;
namespace base {
// ToDo: x86 has the bsr instruction.
inline int GetHighestBitPos(int value) {
return (0xFFFF0000 & value ? value &= 0xFFFF0000, 1 : 0) * 0x10 +
(0xFF00FF00 & value ? value &= 0xFF00FF00, 1 : 0) * 0x08 +
(0xF0F0F0F0 & value ? value &= 0xF0F0F0F0, 1 : 0) * 0x04 +
(0xCCCCCCCC & value ? value &= 0xCCCCCCCC, 1 : 0) * 0x02 +
(0xAAAAAAAA & value ? 1 : 0) * 0x01;
inline uint32_t GetHighestBitPos(uint32_t value) {
uint32_t result = 0;
if (0xFFFF0000 & value) {
value &= 0xFFFF0000;
result += 0x10;
}
if (0xFF00FF00 & value) {
value &= 0xFF00FF00;
result += 0x8;
}
if (0xF0F0F0F0 & value) {
value &= 0xF0F0F0F0;
result += 0x04;
}
if (0xCCCCCCCC & value) {
value &= 0xCCCCCCCC;
result += 0x02;
}
if (0xAAAAAAAA & value) {
result += 0x01;
}
return result;
}
// Get the highest set bit in an integer number
inline int GetHighestBit(int value) {
return 0x1 << GetHighestBitPos(value);
}
// Check if the given integer is a power of two, ie if only one bit is set.
inline bool IsPow2(int value) {
return GetHighestBit(value) == value;
return ((value & (value - 1)) == 0);
}
inline int RoundUpToPow2(int val) {
int i = GetHighestBit(val);
inline uint32_t RoundUpToPow2(uint32_t val) {
uint32_t i = 1 << GetHighestBitPos(val);
return val == i ? val : i << 1;
}

View File

@ -4,55 +4,75 @@
#include "base/log.h"
namespace base {
namespace {
void PostTaskAndReplyRelay(base::Location from,
base::Closure task_cb,
base::Closure reply_cb,
base::TaskRunner* destination) {
void PostTaskAndReplyRelay(Location from,
Closure task_cb,
Closure reply_cb,
std::shared_ptr<TaskRunner> destination,
bool front) {
task_cb();
if (reply_cb)
destination->PostTask(from, std::move(reply_cb));
destination->PostTask(from, std::move(reply_cb), front);
}
} // namespace
namespace base {
thread_local std::unique_ptr<TaskRunner> TaskRunner::thread_local_task_runner;
// The task runner that belongs to the thread it's created in. Tasks to be run
// on a specific thread can be posted to this task runner.
// TaskRunner::GetThreadLocalTaskRunner()->RunTasks() is expected to be
// periodically called.
thread_local std::shared_ptr<TaskRunner> TaskRunner::thread_local_task_runner;
void TaskRunner::CreateThreadLocalTaskRunner() {
DCHECK(!thread_local_task_runner);
thread_local_task_runner = std::make_unique<TaskRunner>();
thread_local_task_runner = std::make_shared<TaskRunner>();
}
TaskRunner* TaskRunner::GetThreadLocalTaskRunner() {
return thread_local_task_runner.get();
std::shared_ptr<TaskRunner> TaskRunner::GetThreadLocalTaskRunner() {
return thread_local_task_runner;
}
void TaskRunner::PostTask(const Location& from, Closure task) {
void TaskRunner::PostTask(Location from, Closure task, bool front) {
DCHECK(task) << LOCATION(from);
task_count_.fetch_add(1, std::memory_order_relaxed);
std::lock_guard<std::mutex> scoped_lock(lock_);
queue_.emplace_back(from, std::move(task));
if (front)
queue_.emplace_front(from, std::move(task));
else
queue_.emplace_back(from, std::move(task));
}
void TaskRunner::PostTaskAndReply(const Location& from,
void TaskRunner::PostTaskAndReply(Location from,
Closure task,
Closure reply) {
Closure reply,
bool front) {
DCHECK(task) << LOCATION(from);
DCHECK(reply) << LOCATION(from);
DCHECK(thread_local_task_runner) << LOCATION(from);
auto relay = std::bind(::PostTaskAndReplyRelay, from, std::move(task),
std::move(reply), thread_local_task_runner.get());
PostTask(from, std::move(relay));
auto relay = std::bind(PostTaskAndReplyRelay, from, std::move(task),
std::move(reply), thread_local_task_runner, front);
PostTask(from, std::move(relay), front);
}
void TaskRunner::MultiConsumerRun() {
void TaskRunner::CancelTasks() {
std::lock_guard<std::mutex> scoped_lock(lock_);
task_count_.fetch_sub(queue_.size(), std::memory_order_release);
queue_.clear();
}
void TaskRunner::WaitForCompletion() {
while (task_count_.load(std::memory_order_acquire) > 0)
std::this_thread::yield();
}
void TaskRunner::RunTasks() {
for (;;) {
Task task;
{
@ -66,60 +86,12 @@ void TaskRunner::MultiConsumerRun() {
auto [from, task_cb] = task;
#if 0
LOG << __func__ << " from: " << LOCATION(from);
LOG(0) << __func__ << " from: " << LOCATION(from);
#endif
task_cb();
task_count_.fetch_sub(1, std::memory_order_release);
if (cancel_tasks_.load(std::memory_order_relaxed)) {
CancelTasksInternal();
break;
}
}
}
void TaskRunner::SingleConsumerRun() {
std::deque<Task> queue;
{
std::lock_guard<std::mutex> scoped_lock(lock_);
if (queue_.empty())
return;
queue.swap(queue_);
}
while (!queue.empty()) {
auto [from, task_cb] = queue.front();
queue.pop_front();
#if 0
LOG << __func__ << " from: " << LOCATION(from);
#endif
task_cb();
task_count_.fetch_sub(1, std::memory_order_release);
if (cancel_tasks_.load(std::memory_order_relaxed)) {
CancelTasksInternal();
break;
}
}
}
void TaskRunner::CancelTasks() {
cancel_tasks_.store(true, std::memory_order_relaxed);
}
void TaskRunner::WaitForCompletion() {
while (task_count_.load(std::memory_order_acquire) > 0)
std::this_thread::yield();
}
void TaskRunner::CancelTasksInternal() {
cancel_tasks_.store(false, std::memory_order_relaxed);
task_count_.store(0, std::memory_order_relaxed);
std::lock_guard<std::mutex> scoped_lock(lock_);
queue_.clear();
}
} // namespace base

View File

@ -18,55 +18,66 @@ namespace internal {
// one that returns via an output parameter.
template <typename ReturnType>
void ReturnAsParamAdapter(std::function<ReturnType()> func,
ReturnType* result) {
std::shared_ptr<ReturnType> result) {
*result = func();
}
// Adapts a ReturnType* result to a callblack that expects a ReturnType.
// Adapts a ReturnType* result to a callback that expects a ReturnType.
template <typename ReturnType>
void ReplyAdapter(std::function<void(ReturnType)> callback,
ReturnType* result) {
callback(*result);
delete result;
std::shared_ptr<ReturnType> result) {
callback(std::move(*result));
}
} // namespace internal
// Runs queued tasks (in the form of Closure objects). All methods are
// thread-safe and can be called on any thread.
// Tasks run in FIFO order. When consumed concurrently by multiple threads, it
// doesn't guarantee whether tasks overlap, or whether they run on a particular
// thread.
// Tasks run in FIFO order when consumed by a single thread. When consumed
// concurrently by multiple threads, it doesn't guarantee whether tasks overlap,
// or whether they run on a particular thread.
class TaskRunner {
public:
TaskRunner() = default;
~TaskRunner() = default;
void PostTask(const Location& from, Closure task);
static void CreateThreadLocalTaskRunner();
static std::shared_ptr<TaskRunner> GetThreadLocalTaskRunner();
void PostTaskAndReply(const Location& from, Closure task, Closure reply);
void PostTask(Location from, Closure task, bool front = false);
void PostTaskAndReply(Location from,
Closure task,
Closure reply,
bool front = false);
template <typename ReturnType>
void PostTaskAndReplyWithResult(const Location& from,
void PostTaskAndReplyWithResult(Location from,
std::function<ReturnType()> task,
std::function<void(ReturnType)> reply) {
auto* result = new ReturnType;
std::function<void(ReturnType)> reply,
bool front = false) {
auto result = std::make_shared<ReturnType>();
return PostTaskAndReply(
from,
std::bind(internal::ReturnAsParamAdapter<ReturnType>, std::move(task),
result),
std::bind(internal::ReplyAdapter<ReturnType>, std::move(reply),
result));
std::bind(internal::ReplyAdapter<ReturnType>, std::move(reply), result),
front);
}
void MultiConsumerRun();
void SingleConsumerRun();
// Posts a task that deletes the given object.
template <class T>
void Delete(Location from, std::unique_ptr<T> object) {
// std::function target must be copy-constructible
std::shared_ptr<T> owned = std::move(object);
PostTask(HERE, [owned]() {});
}
void CancelTasks();
void WaitForCompletion();
static void CreateThreadLocalTaskRunner();
static TaskRunner* GetThreadLocalTaskRunner();
void RunTasks();
private:
using Task = std::tuple<Location, Closure>;
@ -74,11 +85,8 @@ class TaskRunner {
std::deque<Task> queue_;
mutable std::mutex lock_;
std::atomic<size_t> task_count_{0};
std::atomic<bool> cancel_tasks_{false};
static thread_local std::unique_ptr<TaskRunner> thread_local_task_runner;
void CancelTasksInternal();
static thread_local std::shared_ptr<TaskRunner> thread_local_task_runner;
TaskRunner(TaskRunner const&) = delete;
TaskRunner& operator=(TaskRunner const&) = delete;

View File

@ -40,30 +40,30 @@ void ThreadPool::Shutdown() {
threads_.clear();
}
void ThreadPool::PostTask(const Location& from, Closure task) {
DCHECK((!threads_.empty()));
task_runner_.PostTask(from, std::move(task));
void ThreadPool::PostTask(Location from, Closure task, bool front) {
task_runner_.PostTask(from, std::move(task), front);
semaphore_.release();
}
void ThreadPool::PostTaskAndReply(const Location& from,
void ThreadPool::PostTaskAndReply(Location from,
Closure task,
Closure reply) {
DCHECK((!threads_.empty()));
task_runner_.PostTaskAndReply(from, std::move(task), std::move(reply));
Closure reply,
bool front) {
task_runner_.PostTaskAndReply(from, std::move(task), std::move(reply), front);
semaphore_.release();
}
void ThreadPool::CancelTasks() {
task_runner_.CancelTasks();
}
void ThreadPool::WorkerMain() {
for (;;) {
semaphore_.acquire();
if (quit_.load(std::memory_order_relaxed))
return;
task_runner_.MultiConsumerRun();
task_runner_.RunTasks();
}
}

View File

@ -11,8 +11,6 @@
namespace base {
class TaskRunner;
// Feed the ThreadPool tasks (in the form of Closure objects) and they will be
// called on any thread from the pool.
class ThreadPool {
@ -26,19 +24,25 @@ class ThreadPool {
void Shutdown();
void PostTask(const Location& from, Closure task);
void PostTask(Location from, Closure task, bool front = false);
void PostTaskAndReply(const Location& from, Closure task, Closure reply);
void PostTaskAndReply(Location from,
Closure task,
Closure reply,
bool front = false);
template <typename ReturnType>
void PostTaskAndReplyWithResult(const Location& from,
void PostTaskAndReplyWithResult(Location from,
std::function<ReturnType()> task,
std::function<void(ReturnType)> reply) {
std::function<void(ReturnType)> reply,
bool front = false) {
task_runner_.PostTaskAndReplyWithResult(from, std::move(task),
std::move(reply));
std::move(reply), front);
semaphore_.release();
}
void CancelTasks();
private:
std::vector<std::thread> threads_;

View File

@ -1,46 +0,0 @@
#include "base/timer.h"
#include <thread>
namespace base {
Timer::Timer() {
Reset();
}
void Timer::Reset() {
gettimeofday(&last_time_, nullptr);
seconds_passed_ = 0.0f;
seconds_accumulated_ = 0.0f;
}
void Timer::Update() {
timeval currentTime;
gettimeofday(&currentTime, nullptr);
seconds_passed_ =
(float)(currentTime.tv_sec - last_time_.tv_sec) +
0.000001f * (float)(currentTime.tv_usec - last_time_.tv_usec);
last_time_ = currentTime;
seconds_accumulated_ += seconds_passed_;
}
void Timer::Sleep(float duration) {
Timer timer;
float accumulator = 0.0;
constexpr float epsilon = 0.0001f;
while (accumulator < duration) {
timer.Update();
accumulator += timer.GetSecondsPassed();
if (duration - accumulator > epsilon) {
float sleep_time = duration - accumulator - epsilon;
std::this_thread::sleep_for(
std::chrono::microseconds((int)(sleep_time * 1000000.0f)));
}
};
}
} // namespace base

View File

@ -1,31 +1,47 @@
#ifndef BASE_TIMER_H
#define BASE_TIMER_H
#include <sys/time.h>
#include <chrono>
#include <thread>
namespace base {
class Timer {
class ElapsedTimer {
public:
Timer();
~Timer() = default;
ElapsedTimer() { time_ = std::chrono::high_resolution_clock::now(); }
void Reset();
void Update();
static void Sleep(float duration);
float GetSecondsPassed() const { return seconds_passed_; }
float GetSecondsAccumulated() const { return seconds_accumulated_; }
// Return seconds passed since creating the object.
double Elapsed() const {
auto current_time = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> diff = current_time - time_;
return diff.count();
}
private:
float seconds_passed_ = 0.0f;
float seconds_accumulated_ = 0.0f;
timeval last_time_;
std::chrono::time_point<std::chrono::high_resolution_clock> time_;
};
class DeltaTimer {
public:
DeltaTimer() { time_ = std::chrono::high_resolution_clock::now(); }
// Return seconds passed since the last call to this function.
double Delta() {
auto current_time = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> diff = current_time - time_;
time_ = current_time;
return diff.count();
}
private:
std::chrono::time_point<std::chrono::high_resolution_clock> time_;
};
inline void Sleep(double seconds) {
std::this_thread::sleep_for(
std::chrono::duration<double, std::milli>(seconds * 1000));
}
} // namespace base
#endif // BASE_TIMER_H

View File

@ -1247,8 +1247,8 @@ class Matrix4 {
T fov_aspect,
T width,
T height,
T near,
T far) {
T near_plane,
T far_plane) {
// Calc x and y scale from FOV.
T scale =
T(2.0) /
@ -1257,8 +1257,8 @@ class Matrix4 {
T x_scale = y_scale / (width / height);
_M_SET_ROW(0, x_scale / T(2.0), 0, 0, 0);
_M_SET_ROW(1, 0, (-y_scale) / T(2.0), 0, 0);
_M_SET_ROW(2, 0, 0, far / (far - near), 1);
_M_SET_ROW(3, 0, 0, -near * far / (far - near), 0);
_M_SET_ROW(2, 0, 0, far_plane / (far_plane - near_plane), 1);
_M_SET_ROW(3, 0, 0, -near_plane * far_plane / (far_plane - near_plane), 0);
}
void CreateTranslation(const Vector3<T>& t) {
@ -1343,7 +1343,7 @@ class Matrix4 {
M_x_RotZ(angles[2]);
break;
default:
NOTREACHED;
NOTREACHED();
}
}
@ -1534,7 +1534,7 @@ class Matrix4 {
break;
}
default:
NOTREACHED;
NOTREACHED();
}
return -angles;
}

26
src/demo/BUILD.gn Normal file
View File

@ -0,0 +1,26 @@
import("//build/rules.gni")
game("demo") {
sources = [
"credits.cc",
"credits.h",
"damage_type.h",
"demo.cc",
"demo.h",
"enemy.cc",
"enemy.h",
"hud.cc",
"hud.h",
"menu.cc",
"menu.h",
"player.cc",
"player.h",
"sky_quad.cc",
"sky_quad.h",
]
deps = [
"//assets/demo",
"//src/base",
"//src/engine",
]
}

View File

@ -3,9 +3,9 @@
#include "base/log.h"
#include "base/vecmath.h"
#include "demo/demo.h"
#include "engine/asset/font.h"
#include "engine/asset/image.h"
#include "engine/engine.h"
#include "engine/font.h"
#include "engine/image.h"
#include "engine/input_event.h"
using namespace base;

View File

@ -13,11 +13,8 @@
#include "engine/engine.h"
#include "engine/game_factory.h"
#include "engine/input_event.h"
#include "engine/sound.h"
DECLARE_GAME_BEGIN
DECLARE_GAME(Demo)
DECLARE_GAME_END
GAME_FACTORIES{GAME_CLASS(Demo)};
// #define RECORD 15
// #define REPLAY
@ -47,59 +44,73 @@ Demo::~Demo() {
saved_data_.Save();
}
bool Demo::PreInitialize() {
if (!font_.Load("demo/PixelCaps!.ttf"))
return false;
Engine::Get().SetShaderSource("sky_without_nebula",
"demo/sky_without_nebula.glsl");
Engine::Get().SetShaderSource("sky", "demo/sky.glsl");
Engine::Get().AsyncLoadSound("music", "demo/Game_2_Main.mp3", true);
Engine::Get().AsyncLoadSound("boss_music", "demo/Game_2_Boss.mp3", true);
if (!enemy_.PreInitialize()) {
LOG(0) << "Failed to create the enemy.";
return false;
}
if (!player_.PreInitialize()) {
LOG(0) << "Failed to create the enemy.";
return false;
}
if (!menu_.PreInitialize()) {
LOG(0) << "Failed to create the menu.";
return false;
}
return true;
}
bool Demo::Initialize() {
saved_data_.Load(kSaveFileName);
Engine::Get().LoadCustomShader("sky_without_nebula",
"sky_without_nebula.glsl");
Engine::Get().LoadCustomShader("sky", "sky.glsl");
if (!font_.Load("PixelCaps!.ttf"))
return false;
if (!sky_.Create(false)) {
LOG << "Could not create the sky.";
LOG(0) << "Could not create the sky.";
return false;
}
if (!enemy_.Initialize()) {
LOG << "Failed to create the enemy.";
LOG(0) << "Failed to create the enemy.";
return false;
}
if (!player_.Initialize()) {
LOG << "Failed to create the enemy.";
LOG(0) << "Failed to create the enemy.";
return false;
}
if (!hud_.Initialize()) {
LOG << "Failed to create the hud.";
LOG(0) << "Failed to create the hud.";
return false;
}
if (!menu_.Initialize()) {
LOG << "Failed to create the menu.";
LOG(0) << "Failed to create the menu.";
return false;
}
if (!credits_.Initialize()) {
LOG << "Failed to create the credits.";
LOG(0) << "Failed to create the credits.";
return false;
}
auto sound = std::make_unique<Sound>();
if (!sound->Load("Game_2_Main.mp3", true))
return false;
music_.SetSound("music");
music_.SetMaxAmplitude(0.5f);
auto boss_sound = std::make_unique<Sound>();
if (!boss_sound->Load("Game_2_Boss.mp3", true))
return false;
music_.SetSound(std::move(sound));
music_.SetMaxAplitude(0.5f);
boss_music_.SetSound(std::move(boss_sound));
boss_music_.SetMaxAplitude(0.5f);
boss_music_.SetSound("boss_music");
boss_music_.SetMaxAmplitude(0.5f);
if (!saved_data_.root().get("audio", Json::Value(true)).asBool())
Engine::Get().SetEnableAudio(false);
@ -172,12 +183,13 @@ void Demo::ContextLost() {
num_benchmark_samples_ = 0;
avarage_fps_ = 0;
}
menu_.SetRendererType();
}
void Demo::LostFocus() {}
void Demo::GainedFocus(bool from_interstitial_ad) {
DLOG << __func__ << " from_interstitial_ad: " << from_interstitial_ad;
DLOG(0) << __func__ << " from_interstitial_ad: " << from_interstitial_ad;
if (!from_interstitial_ad) {
// if (saved_data_.root().get(kLaunchCount, Json::Value(0)).asInt() >
// kLaunchCountBeforeAd)
@ -313,7 +325,7 @@ void Demo::UpdateMenuState(float delta_time) {
Engine::Get().Exit();
break;
default:
NOTREACHED << "- Unknown menu option: " << menu_.selected_option();
NOTREACHED() << "- Unknown menu option: " << menu_.selected_option();
}
}
@ -397,7 +409,7 @@ void Demo::StartNextStage(bool boss) {
waiting_for_next_wave_ = true;
hud_.SetProgress(wave_ > 0 ? 0 : 1);
DLOG_IF(wave_ > 0 && stage_time_ > 0)
DLOG_IF(0, wave_ > 0 && stage_time_ > 0)
<< "wave: " << wave_ << " time: " << stage_time_ / 60.0f;
stage_time_ = 0;
@ -418,7 +430,7 @@ void Demo::StartNextStage(bool boss) {
music_.Stop(10);
}
boss_fight_ = true;
DLOG << "Boss fight.";
DLOG(0) << "Boss fight.";
} else {
size_t bonus_factor = [&]() -> size_t {
if (wave_ <= 3)
@ -430,8 +442,8 @@ void Demo::StartNextStage(bool boss) {
return 100;
}();
size_t bonus_score = wave_score_ * (bonus_factor - 1);
DLOG << "total_score_" << total_score_ << " wave " << wave_
<< " score: " << wave_score_ << " bonus: " << bonus_score;
DLOG(0) << "total_score_" << total_score_ << " wave " << wave_
<< " score: " << wave_score_ << " bonus: " << bonus_score;
if (bonus_score > 0) {
delta_score_ += bonus_score;
@ -470,7 +482,7 @@ void Demo::StartNextStage(bool boss) {
total_enemies_ = 23.0897f * log((float)wave_ + 1.0f) - 10.0f;
last_num_enemies_killed_ = 0;
boss_fight_ = false;
DLOG << "wave: " << wave_ << " total_enemies_: " << total_enemies_;
DLOG(0) << "wave: " << wave_ << " total_enemies_: " << total_enemies_;
}
hud_.SetScore(total_score_, true);
@ -521,7 +533,7 @@ void Demo::SetDelayedWork(float seconds, base::Closure cb) {
}
void Demo::BenchmarkResult(int avarage_fps) {
LOG << __func__ << " avarage_fps: " << avarage_fps;
LOG(0) << __func__ << " avarage_fps: " << avarage_fps;
if (avarage_fps < 30)
sky_.Create(true);
}

View File

@ -3,7 +3,7 @@
#include "base/closure.h"
#include "engine/animator.h"
#include "engine/font.h"
#include "engine/asset/font.h"
#include "engine/game.h"
#include "engine/persistent_data.h"
#include "engine/solid_quad.h"
@ -23,14 +23,12 @@ class Demo final : public eng::Game {
Demo();
~Demo() final;
// Game interface
bool PreInitialize() final;
bool Initialize() final;
void Update(float delta_time) final;
void ContextLost() final;
void LostFocus() final;
void GainedFocus(bool from_interstitial_ad) final;
void AddScore(size_t score);

View File

@ -9,11 +9,10 @@
#include "base/collusion_test.h"
#include "base/interpolation.h"
#include "base/log.h"
#include "engine/asset/font.h"
#include "engine/asset/image.h"
#include "engine/engine.h"
#include "engine/font.h"
#include "engine/image.h"
#include "engine/renderer/geometry.h"
#include "engine/sound.h"
#include "demo/demo.h"
@ -78,46 +77,50 @@ Enemy::Enemy() = default;
Enemy::~Enemy() = default;
bool Enemy::PreInitialize() {
Engine::Get().SetImageSource("skull_tex", "demo/enemy_anims_01_frames_ok.png",
true);
Engine::Get().SetImageSource("bug_tex", "demo/enemy_anims_02_frames_ok.png",
true);
Engine::Get().SetImageSource("boss_tex1", "demo/Boss_ok.png", true);
Engine::Get().SetImageSource("boss_tex2", "demo/Boss_ok_lvl2.png", true);
Engine::Get().SetImageSource("boss_tex3", "demo/Boss_ok_lvl3.png", true);
Engine::Get().SetImageSource("target_tex", "demo/enemy_target_single_ok.png",
true);
Engine::Get().SetImageSource("blast_tex", "demo/enemy_anims_blast_ok.png",
true);
Engine::Get().SetImageSource("shield_tex", "demo/woom_enemy_shield.png",
true);
Engine::Get().SetImageSource("crate_tex", "demo/nuke_pack_OK.png", true);
for (int i = 0; i < kEnemyType_Max; ++i) {
if (i == kEnemyType_PowerUp)
continue;
Engine::Get().SetImageSource(
"score_tex"s + std::to_string(i),
std::bind(&Enemy::GetScoreImage, this, (EnemyType)i), true);
}
Engine::Get().SetShaderSource("chromatic_aberration",
"demo/chromatic_aberration.glsl");
Engine::Get().AsyncLoadSound("boss_intro", "demo/boss_intro.mp3");
Engine::Get().AsyncLoadSound("boss_explosion", "demo/boss_explosion.mp3");
Engine::Get().AsyncLoadSound("explosion", "demo/explosion.mp3");
Engine::Get().AsyncLoadSound("stealth", "demo/stealth.mp3");
Engine::Get().AsyncLoadSound("shield", "demo/shield.mp3");
Engine::Get().AsyncLoadSound("hit", "demo/hit.mp3");
Engine::Get().AsyncLoadSound("powerup-spawn", "demo/powerup-spawn.mp3");
Engine::Get().AsyncLoadSound("powerup-pick", "demo/powerup-pick.mp3");
return true;
}
bool Enemy::Initialize() {
boss_intro_sound_ = std::make_shared<Sound>();
if (!boss_intro_sound_->Load("boss_intro.mp3", false))
return false;
boss_explosion_sound_ = std::make_shared<Sound>();
if (!boss_explosion_sound_->Load("boss_explosion.mp3", false))
return false;
explosion_sound_ = std::make_shared<Sound>();
if (!explosion_sound_->Load("explosion.mp3", false))
return false;
stealth_sound_ = std::make_shared<Sound>();
if (!stealth_sound_->Load("stealth.mp3", false))
return false;
shield_on_sound_ = std::make_shared<Sound>();
if (!shield_on_sound_->Load("shield.mp3", false))
return false;
hit_sound_ = std::make_shared<Sound>();
if (!hit_sound_->Load("hit.mp3", false))
return false;
power_up_spawn_sound_ = std::make_shared<Sound>();
if (!power_up_spawn_sound_->Load("powerup-spawn.mp3", false))
return false;
power_up_pick_sound_ = std::make_shared<Sound>();
if (!power_up_pick_sound_->Load("powerup-pick.mp3", false))
return false;
if (!CreateRenderResources())
return false;
boss_.SetZOrder(10);
boss_animator_.Attach(&boss_);
boss_intro_.SetSound(boss_intro_sound_);
boss_intro_.SetSound("boss_intro");
boss_intro_.SetVariate(false);
boss_intro_.SetSimulateStereo(false);
@ -513,7 +516,7 @@ void Enemy::OnWaveStarted(int wave, bool boss_fight) {
return 1.0f;
return 1.6f;
}();
DLOG << "boss_spawn_time_factor_: " << boss_spawn_time_factor_;
DLOG(0) << "boss_spawn_time_factor_: " << boss_spawn_time_factor_;
SpawnBoss();
}
}
@ -604,7 +607,7 @@ void Enemy::SpawnUnit(EnemyType enemy_type,
e.sprite.Create("crate_tex", {8, 3});
break;
default:
NOTREACHED << "- Unkown enemy type: " << enemy_type;
NOTREACHED() << "- Unkown enemy type: " << enemy_type;
}
e.sprite.SetZOrder(11);
@ -650,11 +653,13 @@ void Enemy::SpawnUnit(EnemyType enemy_type,
e.health_bar.PlaceToBottomOf(e.sprite);
e.health_bar.SetColor({0.161f, 0.89f, 0.322f, 1});
e.score.Create("score_tex"s + std::to_string(e.enemy_type));
e.score.SetZOrder(12);
e.score.Scale(1.1f);
e.score.SetColor({1, 1, 1, 1});
e.score.SetPosition(spawn_pos);
if (enemy_type != kEnemyType_PowerUp) {
e.score.Create("score_tex"s + std::to_string(e.enemy_type));
e.score.SetZOrder(12);
e.score.Scale(1.1f);
e.score.SetColor({1, 1, 1, 1});
e.score.SetPosition(spawn_pos);
}
e.target_animator.Attach(&e.target);
@ -729,32 +734,32 @@ void Enemy::SpawnUnit(EnemyType enemy_type,
e.movement_animator.Play(Animator::kMovement, false);
if (e.enemy_type == kEnemyType_PowerUp) {
e.explosion.SetSound(power_up_pick_sound_);
e.explosion.SetSound("powerup-pick");
e.spawn.SetSound(power_up_spawn_sound_);
e.spawn.SetMaxAplitude(2.0f);
e.spawn.SetSound("powerup-spawn");
e.spawn.SetMaxAmplitude(2.0f);
e.spawn.Play(false);
} else {
e.explosion.SetSound(explosion_sound_);
e.explosion.SetSound("explosion");
e.explosion.SetVariate(true);
e.explosion.SetSimulateStereo(true);
e.explosion.SetMaxAplitude(0.9f);
e.explosion.SetMaxAmplitude(0.9f);
}
e.stealth.SetSound(stealth_sound_);
e.stealth.SetSound("stealth");
e.stealth.SetVariate(false);
e.stealth.SetSimulateStereo(false);
e.stealth.SetMaxAplitude(0.7f);
e.stealth.SetMaxAmplitude(0.7f);
e.shield_on.SetSound(shield_on_sound_);
e.shield_on.SetSound("shield");
e.shield_on.SetVariate(false);
e.shield_on.SetSimulateStereo(false);
e.shield_on.SetMaxAplitude(0.5f);
e.shield_on.SetMaxAmplitude(0.5f);
e.hit.SetSound(hit_sound_);
e.hit.SetSound("hit");
e.hit.SetVariate(true);
e.hit.SetSimulateStereo(false);
e.hit.SetMaxAplitude(0.5f);
e.hit.SetMaxAmplitude(0.5f);
}
void Enemy::SpawnBoss() {
@ -781,7 +786,7 @@ void Enemy::SpawnBoss() {
e.enemy_type = kEnemyType_Boss;
e.damage_type = kDamageType_Any;
e.total_health = e.hit_points = 41.1283f * log((float)game->wave()) - 20.0f;
DLOG << " Boss health: " << e.total_health;
DLOG(0) << " Boss health: " << e.total_health;
Vector2f hit_box_pos =
boss_.GetPosition() - boss_.GetSize() * Vector2f(0, 0.2f);
@ -825,14 +830,14 @@ void Enemy::SpawnBoss() {
Animator::kMovement, [&]() -> void { e.marked_for_removal = true; });
e.score_animator.Attach(&e.score);
e.explosion.SetSound(boss_explosion_sound_);
e.explosion.SetSound("boss_explosion");
e.explosion.SetVariate(false);
e.explosion.SetSimulateStereo(false);
e.hit.SetSound(hit_sound_);
e.hit.SetSound("hit");
e.hit.SetVariate(true);
e.hit.SetSimulateStereo(false);
e.hit.SetMaxAplitude(0.5f);
e.hit.SetMaxAmplitude(0.5f);
});
boss_animator_.Play(Animator::kFrames, true);
boss_animator_.Play(Animator::kMovement, false);
@ -1167,11 +1172,7 @@ int Enemy::GetScore(EnemyType enemy_type) {
std::unique_ptr<Image> Enemy::GetScoreImage(EnemyType enemy_type) {
const Font& font = static_cast<Demo*>(Engine::Get().GetGame())->GetFont();
int score = GetScore(enemy_type);
if (!score)
return nullptr;
std::string text = std::to_string(score);
std::string text = std::to_string(GetScore(enemy_type));
int width, height;
font.CalculateBoundingBox(text.c_str(), width, height);
@ -1185,30 +1186,6 @@ std::unique_ptr<Image> Enemy::GetScoreImage(EnemyType enemy_type) {
return image;
}
bool Enemy::CreateRenderResources() {
Engine::Get().SetImageSource("skull_tex", "enemy_anims_01_frames_ok.png",
true);
Engine::Get().SetImageSource("bug_tex", "enemy_anims_02_frames_ok.png", true);
Engine::Get().SetImageSource("boss_tex1", "Boss_ok.png", true);
Engine::Get().SetImageSource("boss_tex2", "Boss_ok_lvl2.png", true);
Engine::Get().SetImageSource("boss_tex3", "Boss_ok_lvl3.png", true);
Engine::Get().SetImageSource("target_tex", "enemy_target_single_ok.png",
true);
Engine::Get().SetImageSource("blast_tex", "enemy_anims_blast_ok.png", true);
Engine::Get().SetImageSource("shield_tex", "woom_enemy_shield.png", true);
Engine::Get().SetImageSource("crate_tex", "nuke_pack_OK.png", true);
for (int i = 0; i < kEnemyType_Max; ++i)
Engine::Get().SetImageSource(
"score_tex"s + std::to_string(i),
std::bind(&Enemy::GetScoreImage, this, (EnemyType)i), true);
Engine::Get().LoadCustomShader("chromatic_aberration",
"chromatic_aberration.glsl");
return true;
}
void Enemy::TranslateEnemyUnit(EnemyUnit& e, const Vector2f& delta) {
e.sprite.Translate(delta);
e.target.Translate(delta);

View File

@ -15,7 +15,6 @@
namespace eng {
class Image;
class Sound;
} // namespace eng
class Enemy {
@ -23,6 +22,7 @@ class Enemy {
Enemy();
~Enemy();
bool PreInitialize();
bool Initialize();
void Update(float delta_time);
@ -109,15 +109,6 @@ class Enemy {
eng::Animator boss_animator_;
eng::SoundPlayer boss_intro_;
std::shared_ptr<eng::Sound> boss_intro_sound_;
std::shared_ptr<eng::Sound> boss_explosion_sound_;
std::shared_ptr<eng::Sound> explosion_sound_;
std::shared_ptr<eng::Sound> stealth_sound_;
std::shared_ptr<eng::Sound> shield_on_sound_;
std::shared_ptr<eng::Sound> hit_sound_;
std::shared_ptr<eng::Sound> power_up_spawn_sound_;
std::shared_ptr<eng::Sound> power_up_pick_sound_;
std::list<EnemyUnit> enemies_;
int num_enemies_killed_in_current_wave_ = 0;
@ -164,8 +155,6 @@ class Enemy {
std::unique_ptr<eng::Image> GetScoreImage(EnemyType enemy_type);
bool CreateRenderResources();
void TranslateEnemyUnit(EnemyUnit& e, const base::Vector2f& delta);
};

View File

@ -3,9 +3,9 @@
#include "base/interpolation.h"
#include "base/log.h"
#include "base/vecmath.h"
#include "engine/asset/font.h"
#include "engine/asset/image.h"
#include "engine/engine.h"
#include "engine/font.h"
#include "engine/image.h"
#include "demo/demo.h"
@ -198,7 +198,6 @@ void Hud::ShowMessage(const std::string& text, float duration) {
message_text_ = text;
Engine::Get().RefreshImage("message");
message_.AutoScale();
message_.Scale(1.5f);
message_.SetColor({1, 1, 1, 0});
message_.SetVisible(true);
@ -215,7 +214,6 @@ void Hud::ShowMessage(const std::string& text, float duration) {
void Hud::ShowBonus(size_t bonus) {
bonus_score_ = bonus;
Engine::Get().RefreshImage("bonus_tex");
bonus_.AutoScale();
bonus_.Scale(1.3f);
bonus_.SetColor({1, 1, 1, 1});
bonus_.SetVisible(true);
@ -245,26 +243,20 @@ std::unique_ptr<Image> Hud::CreateMessageImage() {
font.Print(x, 0, message_text_.c_str(), image->GetBuffer(),
image->GetWidth());
image->Compress();
return image;
}
std::unique_ptr<Image> Hud::CreateBonusImage() {
const Font& font = static_cast<Demo*>(Engine::Get().GetGame())->GetFont();
if (bonus_score_ == 0)
return nullptr;
auto image = CreateImage();
std::string text = std::to_string(bonus_score_);
int width, height;
font.CalculateBoundingBox(text.c_str(), width, height);
auto image = std::make_unique<Image>();
image->Create(width, height);
image->Clear({1, 1, 1, 0});
font.Print(0, 0, text.c_str(), image->GetBuffer(), image->GetWidth());
int w, h;
font.CalculateBoundingBox(text.c_str(), w, h);
float x = (image->GetWidth() - w) / 2;
font.Print(x, 0, text.c_str(), image->GetBuffer(), image->GetWidth());
image->Compress();
return image;
}
@ -282,7 +274,7 @@ std::unique_ptr<Image> Hud::Print(int i, const std::string& text) {
}
font.Print(x, 0, text.c_str(), image->GetBuffer(), image->GetWidth());
image->Compress();
return image;
}

View File

@ -7,12 +7,12 @@
#include "base/collusion_test.h"
#include "base/interpolation.h"
#include "base/log.h"
#include "engine/asset/font.h"
#include "engine/asset/image.h"
#include "engine/asset/sound.h"
#include "engine/engine.h"
#include "engine/font.h"
#include "engine/image.h"
#include "engine/input_event.h"
#include "engine/renderer/renderer.h"
#include "engine/sound.h"
#include "demo/demo.h"
@ -53,9 +53,9 @@ Menu::Menu() = default;
Menu::~Menu() = default;
bool Menu::Initialize() {
bool Menu::PreInitialize() {
click_sound_ = std::make_shared<Sound>();
if (!click_sound_->Load("menu_click.mp3", false))
if (!click_sound_->Load("demo/menu_click.mp3", false))
return false;
Demo* game = static_cast<Demo*>(Engine::Get().GetGame());
@ -70,8 +70,59 @@ bool Menu::Initialize() {
max_text_width_ = width;
}
if (!CreateRenderResources())
return false;
Engine::Get().SetImageSource("menu_tex",
std::bind(&Menu::CreateMenuImage, this), true);
Engine::Get().SetImageSource("logo_tex0",
"demo/woom_logo_start_frames_01.png", true);
Engine::Get().SetImageSource("logo_tex1",
"demo/woom_logo_start_frames_02-03.png", true);
Engine::Get().SetImageSource("buttons_tex", "demo/menu_icons.png", true);
Engine::Get().SetImageSource("renderer_logo", "demo/renderer_logo.png", true);
Engine::Get().SetImageSource(
"version_tex",
[]() -> std::unique_ptr<Image> {
const Font* font = Engine::Get().GetSystemFont();
int w, h;
font->CalculateBoundingBox(kVersionStr, w, h);
auto image = std::make_unique<Image>();
image->Create(w, font->GetLineHeight());
image->Clear({1, 1, 1, 0});
font->Print(0, 0, kVersionStr, image->GetBuffer(), image->GetWidth());
image->Compress();
return image;
},
true);
Engine::Get().SetImageSource("high_score_tex",
std::bind(&Menu::CreateHighScoreImage, this));
Engine::Get().SetImageSource("wave_up_tex", []() -> std::unique_ptr<Image> {
const Font& font = static_cast<Demo*>(Engine::Get().GetGame())->GetFont();
constexpr char btn_text[] = "[ ]";
int w, h;
font.CalculateBoundingBox(btn_text, w, h);
auto image = std::make_unique<Image>();
image->Create(w, h);
image->Clear({1, 1, 1, 0});
font.Print(0, 0, btn_text, image->GetBuffer(), image->GetWidth());
image->Compress();
return image;
});
return true;
}
bool Menu::Initialize() {
Demo* game = static_cast<Demo*>(Engine::Get().GetGame());
for (int i = 0; i < kOption_Max; ++i) {
items_[i].text.Create("menu_tex", {1, 4});
@ -98,7 +149,7 @@ bool Menu::Initialize() {
click_.SetSound(click_sound_);
click_.SetVariate(false);
click_.SetSimulateStereo(false);
click_.SetMaxAplitude(1.5f);
click_.SetMaxAmplitude(1.5f);
logo_[0].Create("logo_tex0", {3, 8});
logo_[0].SetZOrder(41);
@ -199,9 +250,6 @@ bool Menu::Initialize() {
Engine::Get().CreateRenderer(renderer_type_.enabled()
? RendererType::kVulkan
: RendererType::kOpenGL);
renderer_type_.SetEnabled(
(Engine::Get().GetRendererType() == RendererType::kVulkan));
Engine::Get().ConsumeInputEvents();
},
true, Engine::Get().GetRendererType() == RendererType::kVulkan,
kColorFadeOut, {Vector4f{1, 1, 1, 1}, Vector4f{1, 1, 1, 1}});
@ -321,6 +369,11 @@ void Menu::SetOptionEnabled(Option o, bool enable) {
}
}
void Menu::SetRendererType() {
renderer_type_.SetEnabled(
(Engine::Get().GetRendererType() == RendererType::kVulkan));
}
void Menu::Show() {
logo_[1].SetColor(kColorNormal);
logo_animator_[0].SetVisible(true);
@ -441,53 +494,6 @@ void Menu::Hide(Closure cb) {
}
}
bool Menu::CreateRenderResources() {
Engine::Get().SetImageSource("menu_tex",
std::bind(&Menu::CreateMenuImage, this));
Engine::Get().SetImageSource("logo_tex0", "woom_logo_start_frames_01.png");
Engine::Get().SetImageSource("logo_tex1", "woom_logo_start_frames_02-03.png");
Engine::Get().SetImageSource("buttons_tex", "menu_icons.png");
Engine::Get().SetImageSource("high_score_tex",
std::bind(&Menu::CreateHighScoreImage, this));
Engine::Get().SetImageSource("renderer_logo", "renderer_logo.png");
Engine::Get().SetImageSource("wave_up_tex", []() -> std::unique_ptr<Image> {
const Font& font = static_cast<Demo*>(Engine::Get().GetGame())->GetFont();
constexpr char btn_text[] = "[ ]";
int w, h;
font.CalculateBoundingBox(btn_text, w, h);
auto image = std::make_unique<Image>();
image->Create(w, h);
image->Clear({1, 1, 1, 0});
font.Print(0, 0, btn_text, image->GetBuffer(), image->GetWidth());
image->Compress();
return image;
});
Engine::Get().SetImageSource("version_tex", []() -> std::unique_ptr<Image> {
const Font* font = Engine::Get().GetSystemFont();
int w, h;
font->CalculateBoundingBox(kVersionStr, w, h);
auto image = std::make_unique<Image>();
image->Create(w, font->GetLineHeight());
image->Clear({1, 1, 1, 0});
font->Print(0, 0, kVersionStr, image->GetBuffer(), image->GetWidth());
image->Compress();
return image;
});
return true;
}
std::unique_ptr<Image> Menu::CreateMenuImage() {
const Font& font = static_cast<Demo*>(Engine::Get().GetGame())->GetFont();

View File

@ -30,11 +30,13 @@ class Menu {
Menu();
~Menu();
bool PreInitialize();
bool Initialize();
void OnInputEvent(std::unique_ptr<eng::InputEvent> event);
void SetOptionEnabled(Option o, bool enable);
void SetRendererType();
void Show();
void Hide(base::Closure cb = nullptr);
@ -145,9 +147,6 @@ class Menu {
Radio starting_wave_;
Button wave_up_;
Button wave_down_;
bool CreateRenderResources();
std::unique_ptr<eng::Image> CreateMenuImage();
std::unique_ptr<eng::Image> CreateHighScoreImage();

View File

@ -2,10 +2,10 @@
#include "base/interpolation.h"
#include "base/log.h"
#include "base/vecmath.h"
#include "engine/asset/font.h"
#include "engine/engine.h"
#include "engine/font.h"
#include "engine/input_event.h"
#include "engine/sound.h"
#include "demo/demo.h"
@ -29,22 +29,21 @@ Player::Player() = default;
Player::~Player() = default;
bool Player::PreInitialize() {
Engine::Get().SetImageSource("weapon_tex", "demo/enemy_anims_flare_ok.png",
true);
Engine::Get().SetImageSource("beam_tex", "demo/enemy_ray_ok.png", true);
Engine::Get().SetImageSource("nuke_symbol_tex", "demo/nuke_frames.png", true);
Engine::Get().SetImageSource("health_bead", "demo/bead.png", true);
Engine::Get().AsyncLoadSound("laser", "demo/laser.mp3");
Engine::Get().AsyncLoadSound("nuke", "demo/nuke.mp3");
Engine::Get().AsyncLoadSound("no_nuke", "demo/no_nuke.mp3");
return true;
}
bool Player::Initialize() {
if (!CreateRenderResources())
return false;
laser_shot_sound_ = std::make_shared<Sound>();
if (!laser_shot_sound_->Load("laser.mp3", false))
return false;
nuke_explosion_sound_ = std::make_shared<Sound>();
if (!nuke_explosion_sound_->Load("nuke.mp3", false))
return false;
no_nuke_sound_ = std::make_shared<Sound>();
if (!no_nuke_sound_->Load("no_nuke.mp3", false))
return false;
SetupWeapons();
Vector2f hb_pos = Engine::Get().GetScreenSize() / Vector2f(2, -2) +
@ -75,12 +74,12 @@ bool Player::Initialize() {
nuke_symbol_animator_.Attach(&nuke_symbol_);
nuke_explosion_.SetSound(nuke_explosion_sound_);
nuke_explosion_.SetSound("nuke");
nuke_explosion_.SetVariate(false);
nuke_explosion_.SetSimulateStereo(false);
nuke_explosion_.SetMaxAplitude(0.8f);
nuke_explosion_.SetMaxAmplitude(0.8f);
no_nuke_.SetSound(no_nuke_sound_);
no_nuke_.SetSound("no_nuke");
return true;
}
@ -153,7 +152,7 @@ void Player::AddNuke(int n) {
if (!nuke_symbol_animator_.IsPlaying(Animator::kRotation)) {
nuke_symbol_animator_.SetRotation(
M_PI * 5, 2, std::bind(SmootherStep, std::placeholders::_1));
PIf * 5, 2, std::bind(SmootherStep, std::placeholders::_1));
nuke_symbol_animator_.Play(Animator::kRotation, false);
}
}
@ -223,7 +222,7 @@ void Player::Fire(DamageType type, Vector2f dir) {
dir.Normalize();
float cos_theta = dir.DotProduct(Vector2f(1, 0));
float theta = acos(cos_theta) + M_PI;
float theta = acos(cos_theta) + PIf;
beam_[type].SetTheta(theta);
auto offset = beam_[type].GetRotation() * (len / 2);
beam_[type].Translate({offset.y, -offset.x});
@ -288,7 +287,7 @@ void Player::SetupWeapons() {
weapon_[i].SetFrame(wepon_warmup_frame[i]);
warmup_animator_[i].SetFrames(wepon_warmup_frame_count, wepon_anim_speed);
warmup_animator_[i].SetRotation(M_PI * 2, 20.0f);
warmup_animator_[i].SetRotation(PIf * 2, 20.0f);
warmup_animator_[i].Attach(&weapon_[i]);
warmup_animator_[i].Play(Animator::kRotation, true);
@ -306,10 +305,10 @@ void Player::SetupWeapons() {
beam_animator_[i].SetBlending({1, 1, 1, 0}, 0.16f);
beam_animator_[i].Attach(&beam_[i]);
laser_shot_[i].SetSound(laser_shot_sound_);
laser_shot_[i].SetSound("laser");
laser_shot_[i].SetVariate(true);
laser_shot_[i].SetSimulateStereo(false);
laser_shot_[i].SetMaxAplitude(0.4f);
laser_shot_[i].SetMaxAmplitude(0.4f);
}
}
@ -485,12 +484,3 @@ void Player::NavigateBack() {
Engine& engine = Engine::Get();
static_cast<Demo*>(engine.GetGame())->EnterMenuState();
}
bool Player::CreateRenderResources() {
Engine::Get().SetImageSource("weapon_tex", "enemy_anims_flare_ok.png", true);
Engine::Get().SetImageSource("beam_tex", "enemy_ray_ok.png", true);
Engine::Get().SetImageSource("nuke_symbol_tex", "nuke_frames.png", true);
Engine::Get().SetImageSource("health_bead", "bead.png", true);
return true;
}

View File

@ -13,7 +13,6 @@
namespace eng {
class InputEvent;
class Sound;
} // namespace eng
class Player {
@ -21,6 +20,7 @@ class Player {
Player();
~Player();
bool PreInitialize();
bool Initialize();
void Update(float delta_time);
@ -41,10 +41,6 @@ class Player {
int nuke_count() { return nuke_count_; }
private:
std::shared_ptr<eng::Sound> nuke_explosion_sound_;
std::shared_ptr<eng::Sound> no_nuke_sound_;
std::shared_ptr<eng::Sound> laser_shot_sound_;
eng::ImageQuad drag_sign_[2];
eng::ImageQuad weapon_[2];
eng::ImageQuad beam_[2];
@ -101,8 +97,6 @@ class Player {
bool ValidateDrag(int i);
void NavigateBack();
bool CreateRenderResources();
};
#endif // DEMO_PLAYER_H

View File

@ -21,8 +21,8 @@ SkyQuad::~SkyQuad() = default;
bool SkyQuad::Create(bool without_nebula) {
without_nebula_ = without_nebula;
scale_ = Engine::Get().GetScreenSize();
shader_ = Engine::Get().GetCustomShader(
without_nebula ? "sky_without_nebula" : "sky");
shader_ =
Engine::Get().GetShader(without_nebula ? "sky_without_nebula" : "sky");
color_animator_.Attach(this);

View File

@ -5,11 +5,6 @@
#include "engine/animatable.h"
#include "engine/animator.h"
#include <array>
#include <memory>
#include <string>
#include <vector>
namespace eng {
class Shader;
} // namespace eng

48
src/engine/BUILD.gn Normal file
View File

@ -0,0 +1,48 @@
source_set("engine") {
sources = [
"animatable.cc",
"animatable.h",
"animator.cc",
"animator.h",
"asset/font.cc",
"asset/font.h",
"asset/image.cc",
"asset/image.h",
"asset/mesh.cc",
"asset/mesh.h",
"asset/shader_source.cc",
"asset/shader_source.h",
"asset/sound.cc",
"asset/sound.h",
"drawable.cc",
"drawable.h",
"engine.cc",
"engine.h",
"game.h",
"game_factory.h",
"image_quad.cc",
"image_quad.h",
"imgui_backend.cc",
"imgui_backend.h",
"input_event.h",
"persistent_data.cc",
"persistent_data.h",
"solid_quad.cc",
"solid_quad.h",
"sound_player.cc",
"sound_player.h",
]
deps = [
"//assets/engine",
"//src/base",
"//src/engine/audio",
"//src/engine/platform",
"//src/engine/renderer",
"//src/third_party/imgui",
"//src/third_party/jsoncpp",
"//src/third_party/minimp3",
"//src/third_party/stb",
"//src/third_party/texture_compressor",
]
}

View File

@ -51,7 +51,7 @@ class Animatable : public Drawable {
protected:
base::Vector2f position_ = {0, 0};
base::Vector2f size_ = {1, 1};
base::Vector2f size_ = {0, 0};
base::Vector2f scale_ = {1, 1};
base::Vector2f rotation_ = {0, 1};
float theta_ = 0;

View File

@ -1,4 +1,4 @@
#include "engine/font.h"
#include "engine/asset/font.h"
#include <codecvt>
#include <locale>
@ -8,7 +8,8 @@
#include "engine/platform/asset_file.h"
#define STB_TRUETYPE_IMPLEMENTATION
#include "../third_party/stb/stb_truetype.h"
#define STBTT_STATIC
#include "third_party/stb/stb_truetype.h"
namespace eng {
@ -18,7 +19,7 @@ bool Font::Load(const std::string& file_name) {
auto buffer = AssetFile::ReadWholeFile(
file_name.c_str(), Engine::Get().GetRootPath().c_str(), &buffer_size);
if (!buffer) {
LOG << "Failed to read font file.";
LOG(0) << "Failed to read font file.";
return false;
}
@ -28,7 +29,7 @@ bool Font::Load(const std::string& file_name) {
// It's tighly packed.
glyph_cache_ = std::make_unique<uint8_t[]>(kGlyphSize * kGlyphSize);
if (!glyph_cache_) {
LOG << "Failed to allocate glyph cache.";
LOG(0) << "Failed to allocate glyph cache.";
break;
}
@ -37,7 +38,7 @@ bool Font::Load(const std::string& file_name) {
if (stbtt_BakeFontBitmap((unsigned char*)buffer.get(), 0, kFontHeight,
glyph_cache_.get(), kGlyphSize, kGlyphSize,
kFirstChar, kNumChars, glyph_info_) <= 0) {
LOG << "Failed to bake the glyph cache: ";
LOG(0) << "Failed to bake the glyph cache: ";
glyph_cache_.reset();
break;
}
@ -67,10 +68,10 @@ static void StretchBlit_I8_to_RGBA32(int dst_x0,
int dst_pitch,
const uint8_t* src_i,
int src_pitch) {
// LOG << "-- StretchBlit: --";
// LOG << "dst: rect(" << dst_x0 << ", " << dst_y0 << ")..("
// LOG(0) << "-- StretchBlit: --";
// LOG(0) << "dst: rect(" << dst_x0 << ", " << dst_y0 << ")..("
// << dst_x1 << ".." << dst_y1 << "), pitch(" << dst_pitch << ")";
// LOG << "src: rect(" << src_x0 << ", " << src_y0 << ")..("
// LOG(0) << "src: rect(" << src_x0 << ", " << src_y0 << ")..("
// << src_x1 << ".." << src_y1 << "), pitch(" << src_pitch << ")";
int dst_width = dst_x1 - dst_x0, dst_height = dst_y1 - dst_y0,
@ -79,8 +80,8 @@ static void StretchBlit_I8_to_RGBA32(int dst_x0,
// int dst_dx = dst_width > 0 ? 1 : -1,
// dst_dy = dst_height > 0 ? 1 : -1;
// LOG << "dst_width = " << dst_width << ", dst_height = " << dst_height;
// LOG << "src_width = " << src_width << ", src_height = " << src_height;
// LOG(0) << "dst_width = " << dst_width << ", dst_height = " << dst_height;
// LOG(0) << "src_width = " << src_width << ", src_height = " << src_height;
uint8_t* dst = dst_rgba + (dst_x0 + dst_y0 * dst_pitch) * 4;
const uint8_t* src = src_i + (src_x0 + src_y0 * src_pitch) * 1;
@ -158,7 +159,7 @@ void Font::CalculateBoundingBox(const std::string& text,
CalculateBoundingBox(text, x0, y0, x1, y1);
width = x1 - x0;
height = y1 - y0;
// LOG << "width = " << width << ", height = " << height;
// LOG(0) << "width = " << width << ", height = " << height;
}
void Font::Print(int x,
@ -166,7 +167,7 @@ void Font::Print(int x,
const std::string& text,
uint8_t* buffer,
int width) const {
// LOG("Font::Print() = %s\n", text);
// LOG(0)("Font::Print() = %s\n", text);
if (!glyph_cache_)
return;
@ -183,7 +184,7 @@ void Font::Print(int x,
stbtt_GetBakedQuad(glyph_info_, kGlyphSize, kGlyphSize, *ptr - kFirstChar,
&fx, &fy, &q, 1);
// LOG("-- glyph --\nxy = (%f %f) .. (%f %f)\nuv = (%f %f) .. (%f %f)\n",
// LOG(0)("-- glyph --\nxy = (%f %f) .. (%f %f)\nuv = (%f %f) .. (%f %f)\n",
// q.x0, q.y0, q.x1, q.y1, q.s0, q.t0, q.s1, q.t1);
int ix0 = (int)q.x0, iy0 = (int)q.y0, ix1 = (int)q.x1, iy1 = (int)q.y1,

View File

@ -1,5 +1,5 @@
#ifndef ENGINE_FONT_H
#define ENGINE_FONT_H
#ifndef ENGINE_ASSET_FONT_H
#define ENGINE_ASSET_FONT_H
#include <cstdint>
#include <memory>
@ -51,4 +51,4 @@ class Font {
} // namespace eng
#endif // ENGINE_FONT_H
#endif // ENGINE_ASSET_FONT_H

View File

@ -1,19 +1,19 @@
#include "engine/image.h"
#include "engine/asset/image.h"
#include <algorithm>
#include <cmath>
#include "base/interpolation.h"
#include "base/log.h"
#include "base/mem.h"
#include "base/misc.h"
#include "engine/engine.h"
#include "engine/platform/asset_file.h"
#include "third_party/texture_compressor/texture_compressor.h"
// This 3rd party library is written in C and uses malloc, which means that we
// have to do the same.
// Use aligned memory for SIMD in texture compressor.
#define STBI_NO_STDIO
#include "../third_party/stb/stb_image.h"
#include "third_party/stb/stb_image.h"
using namespace base;
@ -78,7 +78,7 @@ bool Image::Create(int w, int h) {
width_ = w;
height_ = h;
buffer_.reset((uint8_t*)AlignedAlloc<16>(w * h * 4 * sizeof(uint8_t)));
buffer_.reset((uint8_t*)AlignedAlloc(w * h * 4 * sizeof(uint8_t), 16));
return true;
}
@ -86,7 +86,7 @@ bool Image::Create(int w, int h) {
void Image::Copy(const Image& other) {
if (other.buffer_) {
int size = other.GetSize();
buffer_.reset((uint8_t*)AlignedAlloc<16>(size));
buffer_.reset((uint8_t*)AlignedAlloc(size, 16));
memcpy(buffer_.get(), other.buffer_.get(), size);
}
width_ = other.width_;
@ -95,14 +95,15 @@ void Image::Copy(const Image& other) {
}
bool Image::CreateMip(const Image& other) {
if (other.width_ <= 1 || other.height_ <= 1 || other.GetFormat() != kRGBA32)
if (other.width_ <= 1 || other.height_ <= 1 ||
other.GetFormat() != ImageFormat::kRGBA32)
return false;
// Reduce the dimensions.
width_ = std::max(other.width_ >> 1, 1);
height_ = std::max(other.height_ >> 1, 1);
format_ = kRGBA32;
buffer_.reset((uint8_t*)AlignedAlloc<16>(GetSize()));
format_ = ImageFormat::kRGBA32;
buffer_.reset((uint8_t*)AlignedAlloc(GetSize(), 16));
// If the width isn't perfectly divisable with two, then we end up skewing
// the image because the source offset isn't updated properly.
@ -139,7 +140,7 @@ bool Image::Load(const std::string& file_name) {
auto file_buffer = AssetFile::ReadWholeFile(
file_name.c_str(), Engine::Get().GetRootPath().c_str(), &buffer_size);
if (!file_buffer) {
LOG << "Failed to read file: " << file_name;
LOG(0) << "Failed to read file: " << file_name;
return false;
}
@ -147,19 +148,19 @@ bool Image::Load(const std::string& file_name) {
buffer_.reset((uint8_t*)stbi_load_from_memory(
(const stbi_uc*)file_buffer.get(), buffer_size, &w, &h, &c, 0));
if (!buffer_) {
LOG << "Failed to load image file: " << file_name;
LOG(0) << "Failed to load image file: " << file_name;
return false;
}
LOG << "Loaded " << file_name << ". number of color components: " << c;
LOG(0) << "Loaded " << file_name << ". number of color components: " << c;
uint8_t* converted_buffer = NULL;
switch (c) {
case 1:
// LOG("Converting image from 1 to 4 channels.\n");
// LOG(0)("Converting image from 1 to 4 channels.\n");
// Assume it's an intensity, duplicate it to RGB and fill A with opaque.
converted_buffer =
(uint8_t*)AlignedAlloc<16>(w * h * 4 * sizeof(uint8_t));
(uint8_t*)AlignedAlloc(w * h * 4 * sizeof(uint8_t), 16);
for (int i = 0; i < w * h; ++i) {
converted_buffer[i * 4 + 0] = buffer_[i];
converted_buffer[i * 4 + 1] = buffer_[i];
@ -169,10 +170,10 @@ bool Image::Load(const std::string& file_name) {
break;
case 3:
// LOG("Converting image from 3 to 4 channels.\n");
// LOG(0)("Converting image from 3 to 4 channels.\n");
// Add an opaque channel.
converted_buffer =
(uint8_t*)AlignedAlloc<16>(w * h * 4 * sizeof(uint8_t));
(uint8_t*)AlignedAlloc(w * h * 4 * sizeof(uint8_t), 16);
for (int i = 0; i < w * h; ++i) {
converted_buffer[i * 4 + 0] = buffer_[i * 3 + 0];
converted_buffer[i * 4 + 1] = buffer_[i * 3 + 1];
@ -186,8 +187,8 @@ bool Image::Load(const std::string& file_name) {
case 2:
default:
LOG << "Image had unsuitable number of color components: " << c << " "
<< file_name;
LOG(0) << "Image had unsuitable number of color components: " << c << " "
<< file_name;
buffer_.reset();
return false;
}
@ -214,32 +215,23 @@ bool Image::Load(const std::string& file_name) {
return !!buffer_;
}
bool Image::IsCompressed() const {
return IsCompressedFormat(format_);
}
size_t Image::GetSize() const {
switch (format_) {
case kRGBA32:
return width_ * height_ * 4;
case kDXT1:
case kATC:
return ((width_ + 3) / 4) * ((height_ + 3) / 4) * 8;
case kDXT5:
case kATCIA:
return ((width_ + 3) / 4) * ((height_ + 3) / 4) * 16;
case kETC1:
return (width_ * height_ * 4) / 8;
default:
return 0;
}
return GetImageSize(width_, height_, format_);
}
void Image::ConvertToPow2() {
int new_width = RoundUpToPow2(width_);
int new_height = RoundUpToPow2(height_);
if ((new_width != width_) || (new_height != height_)) {
LOG << "Converting image from (" << width_ << ", " << height_ << ") to ("
<< new_width << ", " << new_height << ")";
LOG(0) << "Converting image from (" << width_ << ", " << height_ << ") to ("
<< new_width << ", " << new_height << ")";
int bigger_size = new_width * new_height * 4 * sizeof(uint8_t);
uint8_t* bigger_buffer = (uint8_t*)AlignedAlloc<16>(bigger_size);
uint8_t* bigger_buffer = (uint8_t*)AlignedAlloc(bigger_size, 16);
// Fill it with black.
memset(bigger_buffer, 0, bigger_size);
@ -275,29 +267,29 @@ bool Image::Compress() {
switch (tc->format()) {
case TextureCompressor::kFormatATC:
format_ = kATC;
format_ = ImageFormat::kATC;
break;
case TextureCompressor::kFormatATCIA:
format_ = kATCIA;
format_ = ImageFormat::kATCIA;
break;
case TextureCompressor::kFormatDXT1:
format_ = kDXT1;
format_ = ImageFormat::kDXT1;
break;
case TextureCompressor::kFormatDXT5:
format_ = kDXT5;
format_ = ImageFormat::kDXT5;
break;
case TextureCompressor::kFormatETC1:
format_ = kETC1;
format_ = ImageFormat::kETC1;
break;
default:
return false;
}
LOG << "Compressing image. Format: " << format_;
LOG(0) << "Compressing image. Format: " << ImageFormatToString(format_);
unsigned compressedSize = GetSize();
uint8_t* compressedBuffer =
(uint8_t*)AlignedAlloc<16>(compressedSize * sizeof(uint8_t));
(uint8_t*)AlignedAlloc(compressedSize * sizeof(uint8_t), 16);
const uint8_t* src = buffer_.get();
uint8_t* dst = compressedBuffer;

View File

@ -1,18 +1,17 @@
#ifndef ENGINE_IMAGE_H
#define ENGINE_IMAGE_H
#ifndef ENGINE_ASSET_IMAGE_H
#define ENGINE_ASSET_IMAGE_H
#include <stdint.h>
#include <string>
#include "base/mem.h"
#include "base/vecmath.h"
#include "engine/renderer/renderer_types.h"
namespace eng {
class Image {
public:
enum Format { kRGBA32, kDXT1, kDXT5, kETC1, kATC, kATCIA };
Image();
Image(const Image& other);
~Image();
@ -31,8 +30,8 @@ class Image {
int GetWidth() const { return width_; }
int GetHeight() const { return height_; }
Format GetFormat() const { return format_; }
bool IsCompressed() const { return format_ > kRGBA32; }
ImageFormat GetFormat() const { return format_; }
bool IsCompressed() const;
size_t GetSize() const;
@ -51,9 +50,9 @@ class Image {
base::AlignedMemPtr<uint8_t[]> buffer_;
int width_ = 0;
int height_ = 0;
Format format_ = kRGBA32;
ImageFormat format_ = ImageFormat::kRGBA32;
};
} // namespace eng
#endif // ENGINE_IMAGE_H
#endif // ENGINE_ASSET_IMAGE_H

View File

@ -1,4 +1,4 @@
#include "engine/mesh.h"
#include "engine/asset/mesh.h"
#include <string.h>
@ -22,7 +22,7 @@ bool Mesh::Create(Primitive primitive,
num_indices_ = num_indices;
if (!ParseVertexDescription(vertex_description, vertex_description_)) {
LOG << "Failed to parse vertex description.";
LOG(0) << "Failed to parse vertex description.";
return false;
}
@ -50,7 +50,7 @@ bool Mesh::Load(const std::string& file_name) {
Engine::Get().GetRootPath().c_str(),
&buffer_size, true);
if (!json_mesh) {
LOG << "Failed to read file: " << file_name;
LOG(0) << "Failed to read file: " << file_name;
return false;
}
@ -60,7 +60,7 @@ bool Mesh::Load(const std::string& file_name) {
const std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
if (!reader->parse(json_mesh.get(), json_mesh.get() + buffer_size, &root,
&err)) {
LOG << "Failed to load mesh. Json parser error: " << err;
LOG(0) << "Failed to load mesh. Json parser error: " << err;
return false;
}
@ -70,7 +70,7 @@ bool Mesh::Load(const std::string& file_name) {
} else if (primitive_str == "TriangleStrip") {
primitive_ = kPrimitive_TriangleStrip;
} else {
LOG << "Failed to load mesh. Invalid primitive: " << primitive_str;
LOG(0) << "Failed to load mesh. Invalid primitive: " << primitive_str;
return false;
}
@ -78,7 +78,7 @@ bool Mesh::Load(const std::string& file_name) {
if (!ParseVertexDescription(root["vertex_description"].asString(),
vertex_description_)) {
LOG << "Failed to parse vertex description.";
LOG(0) << "Failed to parse vertex description.";
return false;
}
@ -90,18 +90,19 @@ bool Mesh::Load(const std::string& file_name) {
const Json::Value vertices = root["vertices"];
if (vertices.size() != array_size) {
LOG << "Failed to load mesh. Vertex array size: " << vertices.size()
<< ", expected " << array_size;
LOG(0) << "Failed to load mesh. Vertex array size: " << vertices.size()
<< ", expected " << array_size;
return false;
}
int vertex_buffer_size = GetVertexSize() * num_vertices_;
if (vertex_buffer_size <= 0) {
LOG << "Failed to load mesh. Invalid vertex size.";
LOG(0) << "Failed to load mesh. Invalid vertex size.";
return false;
}
LOG << "Loaded " << file_name << ". Vertex array size: " << vertices.size();
LOG(0) << "Loaded " << file_name
<< ". Vertex array size: " << vertices.size();
vertices_ = std::make_unique<char[]>(vertex_buffer_size);
@ -131,35 +132,23 @@ bool Mesh::Load(const std::string& file_name) {
*((unsigned short*)dst) = (unsigned short)vertices[i].asUInt();
break;
default:
NOTREACHED << "- Unknown data type: " << data_type;
NOTREACHED() << "- Unknown data type: " << data_type;
}
dst += type_size;
++i;
}
}
}
// TODO: Load indices
return true;
}
size_t Mesh::GetVertexSize() const {
unsigned int size = 0;
for (auto& attr : vertex_description_) {
size += std::get<2>(attr) * std::get<3>(attr);
}
return size;
return eng::GetVertexSize(vertex_description_);
}
size_t Mesh::GetIndexSize() const {
switch (index_description_) {
case kDataType_Byte:
return sizeof(char);
case kDataType_UShort:
return sizeof(unsigned short);
case kDataType_UInt:
return sizeof(unsigned int);
default:
return 0;
}
return eng::GetIndexSize(index_description_);
}
} // namespace eng

View File

@ -1,5 +1,5 @@
#ifndef ENGINE_MESH_H
#define ENGINE_MESH_H
#ifndef ENGINE_ASSET_MESH_H
#define ENGINE_ASSET_MESH_H
#include <memory>
#include <string>
@ -30,7 +30,7 @@ class Mesh {
size_t GetIndexSize() const;
Primitive primitive() const { return primitive_; }
const VertexDescripton& vertex_description() const {
const VertexDescription& vertex_description() const {
return vertex_description_;
}
size_t num_vertices() const { return num_vertices_; }
@ -41,7 +41,7 @@ class Mesh {
private:
Primitive primitive_ = kPrimitive_TriangleStrip;
VertexDescripton vertex_description_;
VertexDescription vertex_description_;
size_t num_vertices_ = 0;
DataType index_description_ = kDataType_Invalid;
size_t num_indices_ = 0;
@ -51,4 +51,4 @@ class Mesh {
} // namespace eng
#endif // ENGINE_MESH_H
#endif // ENGINE_ASSET_MESH_H

Some files were not shown because too many files have changed in this diff Show More