diff --git a/.gitignore b/.gitignore index 329b8ca..2ac2739 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,8 @@ BenchmarkDotNet.Artifacts/ project.lock.json project.fragment.lock.json artifacts/ +/sample/AnnetteWorld/ +/sample/random_stuff/ # StyleCop StyleCopReport.xml @@ -380,3 +382,4 @@ _deps #clangd .cache/ +TremblingFantasy/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 41127c1..e7c5411 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,9 +11,12 @@ if(DOENGINE_COVERAGE) endif() # set(CMAKE_EXPORT_COMPILE_COMMANDS true) - + +set(CMAKE_CXX_FLAGS_DEBUG "-g -O0") set(FETCH_SDL_FROM_GIT true) -set(DOENGINE_PREFERED_LANGUAGE 11) +set(FETCH_GLM_FROM_GIT true) +set(BUILD_SAMPLE_ACTIVE false) +set(DOENGINE_PREFERED_LANGUAGE 17) set(CMAKE_BUILD_TYPE Debug) set(CMAKE_CXX_STANDARD ${DOENGINE_PREFERED_LANGUAGE}) @@ -23,11 +26,18 @@ set(SDL2_DISABLE_INSTALL OFF) set(SDL2TTF_VENDORED TRUE) if(FETCH_SDL_FROM_GIT) - include(cmake-module/fetchSDLProjectFromSource.cmake) + include(cmake-module/fetchSDL3ProjectFromSource.cmake) else() message("WARNING: SDL Would the Machine Installed Version") endif() +if(FETCH_GLM_FROM_GIT) + include(cmake-module/fetchGLMProjectFromSource.cmake) + else() + message("WARNING: SDL Would the Machine Installed Version") +endif() + + set(ASSETS_DIR ${CMAKE_SOURCE_DIR}/assets) if(DOENGINE_TESTS) @@ -50,6 +60,10 @@ set(DOENGINE_SRC src/Application/OpenglLoader.cpp src/Application/Geometric.cpp src/Application/Logger.cpp + src/Application/opengl_decls.cpp + src/Application/opengl_decls.h + src/Application/SDLOpenglWindow.cpp + src/Application/SDLOpenglRenderer.cpp src/Application/SDLWindowManager.cpp src/Application/WindowManager.cpp src/Application/SDLWindowManager.h @@ -57,31 +71,45 @@ set(DOENGINE_SRC src/Application/SDLRenderer.h src/Application/SDLTTFText.h src/Application/SDLTTFText.cpp + src/Application/Timer.cpp src/Application/Application.cpp src/Application/StringUtils.cpp src/Timer/FPSManager.cpp src/GameState/GameStateManager.cpp src/Timer/FPSManager.cpp src/Tilemap/Tilemap.cpp + src/Tilemap/TileMapEditor.cpp src/Texture/Texture.cpp src/Event/SDLMouse.cpp src/Event/SDLKeyboard.cpp src/Music/SDLMusicHandler.cpp - src/Music/SDLSoundHandler.cpp - src/Event/SDLJoypad.cpp - src/Music/SDLMusicHandler.cpp - src/GameObject/GameObject.cpp - src/Font/TTFText.cpp - src/Event/Event.cpp - src/Drawable/Gui.cpp - src/Application/SDLTexture.cpp - src/Application/SDLTexture.h - includes/NativeStructs.h - includes/Geometric.h - includes/Color.h - includes/defines.h - includes/TTFText.h - includes/Camera.h + src/Music/SDLSoundHandler.cpp + src/Event/SDLJoypad.cpp + src/Music/SDLMusicHandler.cpp + src/GameObject/GameObject.cpp + src/Font/TTFText.cpp + src/Font/FontCache.cpp + src/Event/Event.cpp + src/Event/EventHandler.cpp + src/Drawable/Gui.cpp + src/Drawable/UI.cpp + src/Drawable/TextField.cpp + src/Application/GameState.cpp + src/Application/Camera.cpp + src/Application/SDLTexture.cpp + src/Application/SDLTexture.h + src/Drawable/GameObject.cpp + src/Drawable/Draggable.cpp + src/Drawable/DraggableGameObject.cpp + src/Drawable/SelectionRect.cpp + src/Drawable/MessageBox.cpp + includes/NativeStructs.h + includes/Geometric.h + includes/Color.h + includes/defines.h + includes/TTFText.h + includes/FontCache.h + includes/Camera.h includes/Event.h includes/DOEngine.h includes/DOEngine_SDL_includes.h @@ -91,22 +119,30 @@ set(DOENGINE_SRC includes/GameState.h includes/GameStateManager.h includes/Grids.h - includes/LinkList.h - includes/sort.h - includes/Sprite.h - includes/Utils.h - includes/Texture.h - includes/Tile.h - includes/Tilemap.h - includes/UDPPacket.h - includes/Vector.h - includes/Application.h - includes/Mouse.h - includes/Keyboard.h - includes/Joypad.h - includes/MusicHandler.h - includes/SDLMusicHandler.h - includes/SDLSoundHandler.h + includes/LinkList.h + includes/sort.h + includes/Sprite.h + includes/Utils.h + includes/Texture.h + includes/Tile.h + includes/Tilemap.h + includes/TileMapEditor.h + includes/UDPPacket.h + includes/Vector.h + includes/Application.h + includes/Mouse.h + includes/Keyboard.h + includes/Joypad.h + includes/MusicHandler.h + includes/SDLMusicHandler.h + includes/SDLSoundHandler.h + includes/UI.h + includes/Draggable.h + includes/DraggableGameObject.h + includes/SelectionRect.h + includes/SpriteOffset.h + includes/TextField.h + includes/MessageBox.h ) add_library(${PROJECT_NAME} @@ -116,11 +152,12 @@ add_library(${PROJECT_NAME} set(DOENGINE_LIBRARIES ) if(FETCH_SDL_FROM_GIT) set(DOENGINE_LIBRARIES ${DOENGINE_LIBRARIES} - SDL2::SDL2 - SDL2::SDL2main - SDL2_ttf - SDL2_image + SDL3::SDL3 + # SDL2::SDL2main + SDL3_ttf::SDL3_ttf + SDL3_image::SDL3_image SDL2_mixer + GL ) else() set(DOENGINE_LIBRARIES ${DOENGINE_LIBRARIES} @@ -135,6 +172,13 @@ endif() target_link_libraries(${PROJECT_NAME} PRIVATE ${DOENGINE_LIBRARIES}) +message("Copying asssets to buils dir.") +file(COPY ${ASSETS_DIR} + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) +# install(TARGETS ${PROJECT_NAME} DESTINATION /home/afl/Desktop/doengine ) + + +if(${BUILD_SAMPLE_ACTIVE}) #add_executable(${PROJECT_NAME}-sample # sample/ignored_main.cpp #) @@ -153,7 +197,24 @@ add_executable(${PROJECT_NAME}-Tetris-sample add_executable(${PROJECT_NAME}-MusicPlayer-sample sample/MusicPlayer/MusicPlayer.cpp ) +add_executable(BattleSpawn + sample/BattleSpawn/BattleSpawn.cpp +) +add_executable(PacmanClone + sample/Pacman/Pacman.cpp +) + +add_executable(BattleCityRecreation + sample/BattleCityRecreation/BattleCityRecreation.cpp +) + +add_executable(Tetris + sample/tetris/Tetris.cpp +) +add_executable(SideScroller + sample/SideScroller/SideScroller.cpp +) # to build sample needs # Zip support, ubuntu sudo apt-get install libzip-dev @@ -181,7 +242,31 @@ target_link_libraries(${PROJECT_NAME}-MusicPlayer-sample PRIVATE ${PROJECT_NAME} ${DOENGINE_LIBRARIES} ) -message("Copying asssets to buils dir.") -file(COPY ${ASSETS_DIR} - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) -# install(TARGETS ${PROJECT_NAME} DESTINATION /home/afl/Desktop/doengine ) + +target_link_libraries(BattleSpawn PRIVATE + ${PROJECT_NAME} + ${DOENGINE_LIBRARIES} +) + +target_link_libraries(PacmanClone PRIVATE + ${PROJECT_NAME} + ${DOENGINE_LIBRARIES} +) + + +target_link_libraries(BattleCityRecreation PRIVATE + ${PROJECT_NAME} + ${DOENGINE_LIBRARIES} +) + +target_link_libraries(Tetris PRIVATE + ${PROJECT_NAME} + ${DOENGINE_LIBRARIES} +) + + +target_link_libraries(SideScroller PRIVATE + ${PROJECT_NAME} + ${DOENGINE_LIBRARIES} +) +endif() \ No newline at end of file diff --git a/README.md b/README.md index 98ae645..46b343a 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,122 @@ # DOEngine -another 2D game engine. + +another 2D game engine library base on SDL2 and Opengl. #### What is it? why? -##### Simple - -tileid -rows -colums -tilesize -src -layercount -cache -tileset[] -Data=[ - [ - x.x.x.x.x.x.x.x - x.x.x.x. - ], - [], - [] -] -eventos +this is a basically a gameengine library on top of SDL2 and opengl(next) +there are tons of Game Engine, Frameworks, Tools and other Software that +is use to create Games, is ok, we are not competing here. this library +is create to see if we are able to run a Game without third party and by +learning at our own pace. + +#### Authors + +- Aneury Perez -> https://github.com/aneury1 -> linkedin: https://www.linkedin.com/in/aneury-p-06902a3a/ +- Victor D. Montero -> https://github.com/victordmontero -> linkedin: https://www.linkedin.com/in/victor-d-montero-adames-888170144/ + +feel free to contact us and feel to leave comments, bug reports and anything that can +make it better. this software is suppose to be open source as now. + +#### Setup + +this is a C++ project, use CMake, there are some config for using installed SDL2(and sublibraries) Version +or fetch from github.com please check CMakeLists.txt to see it. probably some would be listed bellow. + +#### Ussage. + +you can check the samples in the sample folders. but is basically setting up a State and add to the statemachine +and work from it, using library common function for Drawing, Loading images, Loading fonts, Loading Music. + +please see in samples simplepong.cpp +```cpp +#include "Application.h" +#include "GameObject.h" +#include "Grids.h" +#include "TTFText.h" + +struct Paddle : public GameObject +{ + ... + virtual void Update(float timer = 0)override{} + virtual void Render()override{} + virtual bool isColliding(GameObject* other)override{} +}; + +struct PongState : public GameState, public KeyDownEvent +{ + ... + virtual void OnKeydown(const Keyboard&) override{...} + virtual void OnEnter() override + { + ... + ///do setup + } + virtual void OnExit() override + { + + ... + ///do a cleanup + } + virtual void Update(float elapsed) + { + ... + /// process Game State Action + } + virtual void Render() + { + ... + ///update Screen if necesary.. + } +}; + +... +enum StatesId{ + Playstate = 1 +}; + + +int main(int argc, char* argv[]) +{ + auto app = doengine::Application::getApplication(); + doengine::Rect rect{1200, 900}; + app->createWindow(rect); + auto pongState = new PongState(); + app->addState(pongState, Playstate); + app->setState(Playstate); + + while (app->IsRunning()) + { + app->PollEvent(); + app->Update(); + app->Render(); + } + app->Quit(); + return 0; +} + + +``` +##### Issue when Compiling or running the samples. +as this is on going you would have to issues compiling or running the project. if this is a compilation issue +please head to CMake and check if you are fetch or using local installation probably clean up and do cmake .. +would make it. + +if there are some issue running the samples, please check the path for assets, probably there would be missing +some assets due to they are for privately use. in the future we would have own assets but by the time this +is the state of it. + +##### WIP + +- Testing Android Setup(no yet ready) +- Testing Opengl currently we have only SDL2 fallback +- TileMap creating, font wrapping, blending, math, ... + + +#### Docs. + +we dont have docs written, but we have a github project where you can see the status and board like kanban board +we have samples and Test folders which its suppose to be very clear and explanatory in general, in addition of +any doubts you can create and issue for letting us know that something is missing. +Thanks for reach this place... diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..9ce2d5b --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,2 @@ +build-android/ +AndroidGame/ \ No newline at end of file diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt new file mode 100644 index 0000000..7746db1 --- /dev/null +++ b/android/CMakeLists.txt @@ -0,0 +1,349 @@ +cmake_minimum_required(VERSION 3.18.1) + +project(Doengine) + +# Set C/C++ standards +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Define paths relative to the Android folder +set(PROJECT_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/..") +set(INCLUDES_DIR "${PROJECT_ROOT}/includes") +set(SRC_DIR "${PROJECT_ROOT}/src") +set(TEST_DIR "${SRC_DIR}/test") + +# Option to build as shared or static library +option(BUILD_SHARED_LIBS "Build shared library instead of static" ON) +option(BUILD_TESTS "Build test executable" FALSE) + +# Include FetchContent module for downloading dependencies +include(FetchContent) + +# Set SDL2 build options - force shared libraries for all SDL components +set(SDL_SHARED ON CACHE BOOL "" FORCE) +set(SDL_STATIC OFF CACHE BOOL "" FORCE) +set(SDL_TEST OFF CACHE BOOL "" FORCE) +set(SDL_TESTS OFF CACHE BOOL "" FORCE) +set(BUILD_SHARED_LIBS ON CACHE BOOL "" FORCE) + +# SDL2_image options +set(SDL2IMAGE_INSTALL OFF CACHE BOOL "" FORCE) +set(SDL2IMAGE_SAMPLES OFF CACHE BOOL "" FORCE) + +# SDL2_mixer options +set(SDL2MIXER_INSTALL OFF CACHE BOOL "" FORCE) +set(SDL2MIXER_SAMPLES OFF CACHE BOOL "" FORCE) + +# SDL2_ttf options +set(SDL2TTF_INSTALL OFF CACHE BOOL "" FORCE) +set(SDL2TTF_SAMPLES OFF CACHE BOOL "" FORCE) + +# SDL2_net options +set(SDL2NET_INSTALL OFF CACHE BOOL "" FORCE) +set(SDL2NET_SAMPLES OFF CACHE BOOL "" FORCE) + +# Fetch SDL2 from GitHub +message(STATUS "Fetching SDL2 from GitHub...") +FetchContent_Declare( + SDL2 + GIT_REPOSITORY https://github.com/libsdl-org/SDL.git + GIT_TAG release-2.30.10 + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE +) + +# Fetch SDL2_image from GitHub +message(STATUS "Fetching SDL2_image from GitHub...") +FetchContent_Declare( + SDL2_image + GIT_REPOSITORY https://github.com/libsdl-org/SDL_image.git + GIT_TAG release-2.8.4 + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE +) + +# Fetch SDL2_mixer from GitHub +message(STATUS "Fetching SDL2_mixer from GitHub...") +FetchContent_Declare( + SDL2_mixer + GIT_REPOSITORY https://github.com/libsdl-org/SDL_mixer.git + GIT_TAG release-2.8.0 + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE +) + +# Fetch SDL2_ttf from GitHub +message(STATUS "Fetching SDL2_ttf from GitHub...") +FetchContent_Declare( + SDL2_ttf + GIT_REPOSITORY https://github.com/libsdl-org/SDL_ttf.git + GIT_TAG release-2.22.0 + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE +) + +# Fetch SDL2_net from GitHub +message(STATUS "Fetching SDL2_net from GitHub...") +FetchContent_Declare( + SDL2_net + GIT_REPOSITORY https://github.com/libsdl-org/SDL_net.git + GIT_TAG release-2.2.0 + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE +) + +# Fetch Google Test from GitHub +message(STATUS "Fetching Google Test from GitHub...") +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG v1.14.0 + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE +) + +# Disable Google Test installation and samples +set(INSTALL_GTEST ON CACHE BOOL "" FORCE) +set(BUILD_GMOCK ON CACHE BOOL "" FORCE) +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + +# Make SDL2 and family available +message(STATUS "Making SDL2 libraries available...") +FetchContent_MakeAvailable(SDL2 SDL2_image SDL2_mixer SDL2_ttf SDL2_net googletest) + +# Collect all source files (excluding test directory) +file(GLOB_RECURSE DOENGINE_SOURCES + "${SRC_DIR}/*.c" + "${SRC_DIR}/*.cpp" +) + +# Remove test files from main library sources +list(FILTER DOENGINE_SOURCES EXCLUDE REGEX "${TEST_DIR}/.*") + +# Collect all header files +file(GLOB_RECURSE DOENGINE_HEADERS + "${INCLUDES_DIR}/*.h" + "${INCLUDES_DIR}/*.hpp" +) + +# Collect test sources +if(BUILD_TESTS) + file(GLOB_RECURSE TEST_SOURCES + "${TEST_DIR}/fixtures/*.c" + "${TEST_DIR}/fixtures/*.cpp" + "${TEST_DIR}/mocks/*.c" + "${TEST_DIR}/mocks/*.cpp" + "${TEST_DIR}/units/*.c" + "${TEST_DIR}/units/*.cpp" + ) +endif() + +# Create the library target +if(BUILD_SHARED_LIBS) + add_library(${PROJECT_NAME} SHARED ${DOENGINE_SOURCES}) + message(STATUS "Building LibDoengine as SHARED library") +else() + add_library(${PROJECT_NAME} STATIC ${DOENGINE_SOURCES}) + message(STATUS "Building LibDoengine as STATIC library") +endif() + +# Include directories +target_include_directories(${PROJECT_NAME} + PUBLIC + ${INCLUDES_DIR} + PRIVATE + ${SRC_DIR} +) + +# Link SDL2 libraries +target_link_libraries(${PROJECT_NAME} + PUBLIC + SDL2::SDL2 + SDL2_image::SDL2_image + SDL2_mixer::SDL2_mixer + SDL2_ttf::SDL2_ttf + SDL2_net::SDL2_net +) + +# Link standard Android libraries +find_library(LOG_LIBRARY log) +find_library(ANDROID_LIBRARY android) +find_library(GLESV2_LIBRARY GLESv2) +find_library(EGL_LIBRARY EGL) + +target_link_libraries(${PROJECT_NAME} + PUBLIC + ${LOG_LIBRARY} + ${ANDROID_LIBRARY} + ${GLESV2_LIBRARY} + ${EGL_LIBRARY} +) + +# Set compiler flags for Android +target_compile_options(${PROJECT_NAME} PRIVATE + -Wall + -Wextra + -fPIC +) + +# Define Android-specific macros +target_compile_definitions(${PROJECT_NAME} + PRIVATE + ANDROID + __ANDROID__ +) + +# ======================================== +# Test Executable Configuration +# ======================================== +if(BUILD_TESTS AND TEST_SOURCES) + message(STATUS "Building test executable...") + + # Create test executable + add_executable(${PROJECT_NAME}_tests ${TEST_SOURCES}) + + # Include directories for tests + target_include_directories(${PROJECT_NAME}_tests + PRIVATE + ${INCLUDES_DIR} + ${SRC_DIR} + ${TEST_DIR} + ${TEST_DIR}/fixtures + ${TEST_DIR}/mocks + ${TEST_DIR}/units + ${googletest_SOURCE_DIR}/googletest/include + ${googletest_SOURCE_DIR}/googlemock/include + ) + + # Link libraries for tests + target_link_libraries(${PROJECT_NAME}_tests + PRIVATE + ${PROJECT_NAME} + gtest + gtest_main + gmock + gmock_main + ) + + # Set compiler flags for tests + target_compile_options(${PROJECT_NAME}_tests PRIVATE + -Wall + -Wextra + ) + + # Define Android-specific macros for tests + target_compile_definitions(${PROJECT_NAME}_tests + PRIVATE + ANDROID + __ANDROID__ + ) + + # Set output directory for test executable + set_target_properties(${PROJECT_NAME}_tests PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin/${ANDROID_ABI}" + ) + + message(STATUS "Test executable will be built: ${PROJECT_NAME}_tests") +else() + if(BUILD_TESTS) + message(STATUS "No test sources found in ${TEST_DIR}") + else() + message(STATUS "Test building disabled") + endif() +endif() + +# Set output directory +set_target_properties(${PROJECT_NAME} PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib/${ANDROID_ABI}" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib/${ANDROID_ABI}" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin/${ANDROID_ABI}" +) + +# Set output directories for SDL2 libraries as well +set_target_properties(SDL2 PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib/${ANDROID_ABI}" +) + +if(TARGET SDL2_image) + set_target_properties(SDL2_image PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib/${ANDROID_ABI}" + ) +endif() + +if(TARGET SDL2_mixer) + set_target_properties(SDL2_mixer PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib/${ANDROID_ABI}" + ) +endif() + +if(TARGET SDL2_ttf) + set_target_properties(SDL2_ttf PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib/${ANDROID_ABI}" + ) +endif() + +if(TARGET SDL2_net) + set_target_properties(SDL2_net PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib/${ANDROID_ABI}" + ) +endif() + +# Installation rules +install(TARGETS ${PROJECT_NAME} + LIBRARY DESTINATION lib/${ANDROID_ABI} + ARCHIVE DESTINATION lib/${ANDROID_ABI} +) + +install(DIRECTORY ${INCLUDES_DIR}/ + DESTINATION include + FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp" +) + +# Install SDL2 shared libraries +install(TARGETS SDL2 + LIBRARY DESTINATION lib/${ANDROID_ABI} + RUNTIME DESTINATION lib/${ANDROID_ABI} +) + +if(TARGET SDL2_image) + install(TARGETS SDL2_image + LIBRARY DESTINATION lib/${ANDROID_ABI} + RUNTIME DESTINATION lib/${ANDROID_ABI} + ) +endif() + +if(TARGET SDL2_mixer) + install(TARGETS SDL2_mixer + LIBRARY DESTINATION lib/${ANDROID_ABI} + RUNTIME DESTINATION lib/${ANDROID_ABI} + ) +endif() + +if(TARGET SDL2_ttf) + install(TARGETS SDL2_ttf + LIBRARY DESTINATION lib/${ANDROID_ABI} + RUNTIME DESTINATION lib/${ANDROID_ABI} + ) +endif() + +if(TARGET SDL2_net) + install(TARGETS SDL2_net + LIBRARY DESTINATION lib/${ANDROID_ABI} + RUNTIME DESTINATION lib/${ANDROID_ABI} + ) +endif() + +# Print configuration summary +message(STATUS "=== LibDoengine Build Configuration ===") +message(STATUS "Build Type: ${CMAKE_BUILD_TYPE}") +message(STATUS "Android ABI: ${ANDROID_ABI}") +message(STATUS "Android Platform: ${ANDROID_PLATFORM}") +message(STATUS "Library Type: ${BUILD_SHARED_LIBS}") +message(STATUS "Build Tests: ${BUILD_TESTS}") +message(STATUS "Source Directory: ${SRC_DIR}") +message(STATUS "Include Directory: ${INCLUDES_DIR}") +message(STATUS "Test Directory: ${TEST_DIR}") +message(STATUS "SDL2 Libraries: Fetched from GitHub") +message(STATUS "Google Test: Fetched from GitHub (v1.14.0)") +message(STATUS "========================================") \ No newline at end of file diff --git a/android/CMakeLists_prev.txt b/android/CMakeLists_prev.txt new file mode 100644 index 0000000..256f446 --- /dev/null +++ b/android/CMakeLists_prev.txt @@ -0,0 +1,181 @@ +cmake_minimum_required(VERSION 3.18.1) + +project(LibDoengine) + +# Set C/C++ standards +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Define paths relative to the Android folder +set(PROJECT_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/..") +set(INCLUDES_DIR "${PROJECT_ROOT}/includes") +set(SRC_DIR "${PROJECT_ROOT}/src") + +# Option to build as shared or static library +option(BUILD_SHARED_LIBS "Build shared library instead of static" ON) + +# Include FetchContent module for downloading dependencies +include(FetchContent) + +# Set SDL2 build options - disable unnecessary features for mobile +set(SDL_SHARED ON CACHE BOOL "" FORCE) +set(SDL_STATIC OFF CACHE BOOL "" FORCE) +set(SDL_TEST OFF CACHE BOOL "" FORCE) +set(SDL_TESTS OFF CACHE BOOL "" FORCE) + +# Fetch SDL2 from GitHub +message(STATUS "Fetching SDL2 from GitHub...") +FetchContent_Declare( + SDL2 + GIT_REPOSITORY https://github.com/libsdl-org/SDL.git + GIT_TAG release-2.30.10 + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE +) + +# Fetch SDL2_image from GitHub +message(STATUS "Fetching SDL2_image from GitHub...") +FetchContent_Declare( + SDL2_image + GIT_REPOSITORY https://github.com/libsdl-org/SDL_image.git + GIT_TAG release-2.8.4 + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE +) + +# Fetch SDL2_mixer from GitHub +message(STATUS "Fetching SDL2_mixer from GitHub...") +FetchContent_Declare( + SDL2_mixer + GIT_REPOSITORY https://github.com/libsdl-org/SDL_mixer.git + GIT_TAG release-2.8.0 + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE +) + +# Fetch SDL2_ttf from GitHub +message(STATUS "Fetching SDL2_ttf from GitHub...") +FetchContent_Declare( + SDL2_ttf + GIT_REPOSITORY https://github.com/libsdl-org/SDL_ttf.git + GIT_TAG release-2.22.0 + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE +) + +# Fetch SDL2_net from GitHub +message(STATUS "Fetching SDL2_net from GitHub...") +FetchContent_Declare( + SDL2_net + GIT_REPOSITORY https://github.com/libsdl-org/SDL_net.git + GIT_TAG release-2.2.0 + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE +) + +# Make SDL2 and family available +message(STATUS "Making SDL2 libraries available...") +FetchContent_MakeAvailable(SDL2 SDL2_image SDL2_mixer SDL2_ttf SDL2_net) + +# Collect all source files +file(GLOB_RECURSE DOENGINE_SOURCES + "${SRC_DIR}/*.c" + "${SRC_DIR}/*.cpp" +) + +# Collect all header files +file(GLOB_RECURSE DOENGINE_HEADERS + "${INCLUDES_DIR}/*.h" + "${INCLUDES_DIR}/*.hpp" +) + +# Create the library target +if(BUILD_SHARED_LIBS) + add_library(${PROJECT_NAME} SHARED ${DOENGINE_SOURCES}) + message(STATUS "Building LibDoengine as SHARED library") +else() + add_library(${PROJECT_NAME} STATIC ${DOENGINE_SOURCES}) + message(STATUS "Building LibDoengine as STATIC library") +endif() + +# Include directories +target_include_directories(${PROJECT_NAME} + PUBLIC + ${INCLUDES_DIR} + PRIVATE + ${SRC_DIR} +) + +# Link SDL2 libraries +target_link_libraries(${PROJECT_NAME} + PUBLIC + SDL2::SDL2 + SDL2_image::SDL2_image + SDL2_mixer::SDL2_mixer + SDL2_ttf::SDL2_ttf + SDL2_net::SDL2_net +) + +# Link standard Android libraries +find_library(LOG_LIBRARY log) +find_library(ANDROID_LIBRARY android) +find_library(GLESV2_LIBRARY GLESv2) +find_library(EGL_LIBRARY EGL) + +target_link_libraries(${PROJECT_NAME} + PUBLIC + ${LOG_LIBRARY} + ${ANDROID_LIBRARY} + ${GLESV2_LIBRARY} + ${EGL_LIBRARY} +) + +# Set compiler flags for Android +target_compile_options(${PROJECT_NAME} PRIVATE + -Wall + -Wextra + -fPIC +) + +# Define Android-specific macros +target_compile_definitions(${PROJECT_NAME} + PRIVATE + ANDROID + __ANDROID__ +) + +# Set output directory +set_target_properties(${PROJECT_NAME} PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib/${ANDROID_ABI}" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib/${ANDROID_ABI}" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin/${ANDROID_ABI}" +) + +# Installation rules +install(TARGETS ${PROJECT_NAME} + LIBRARY DESTINATION lib/${ANDROID_ABI} + ARCHIVE DESTINATION lib/${ANDROID_ABI} +) + +install(DIRECTORY ${INCLUDES_DIR}/ + DESTINATION include + FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp" +) + +# Install SDL2 libraries as well +install(TARGETS SDL2 SDL2_image SDL2_mixer SDL2_ttf SDL2_net + LIBRARY DESTINATION lib/${ANDROID_ABI} + ARCHIVE DESTINATION lib/${ANDROID_ABI} +) + +# Print configuration summary +message(STATUS "=== LibDoengine Build Configuration ===") +message(STATUS "Build Type: ${CMAKE_BUILD_TYPE}") +message(STATUS "Android ABI: ${ANDROID_ABI}") +message(STATUS "Android Platform: ${ANDROID_PLATFORM}") +message(STATUS "Library Type: ${BUILD_SHARED_LIBS}") +message(STATUS "Source Directory: ${SRC_DIR}") +message(STATUS "Include Directory: ${INCLUDES_DIR}") +message(STATUS "SDL2 Libraries: Fetched from GitHub") +message(STATUS "========================================") \ No newline at end of file diff --git a/android/boiler-plate-gen/generator.sh b/android/boiler-plate-gen/generator.sh new file mode 100755 index 0000000..7ea8962 --- /dev/null +++ b/android/boiler-plate-gen/generator.sh @@ -0,0 +1,154 @@ +#!/bin/bash + +# Setup Android Project for DoEngine Game +# This script creates the complete Android project structure + +set -e + +# Colors +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +echo -e "${GREEN}=== DoEngine Android Project Setup ===${NC}" +echo "" + +# Get script directory +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +DOENGINE_ROOT="$(dirname "$SCRIPT_DIR")" +ANDROID_PROJECT="${DOENGINE_ROOT}/AndroidGame" + +# Configuration +APP_NAME="DoEngineGame" +PACKAGE_NAME="com.yourgame.doengine" + +echo -e "${BLUE}Project will be created at:${NC} ${ANDROID_PROJECT}" +echo -e "${BLUE}DoEngine root:${NC} ${DOENGINE_ROOT}" +echo "" + +# Create directory structure +echo -e "${YELLOW}Creating project structure...${NC}" + +mkdir -p "${ANDROID_PROJECT}" +mkdir -p "${ANDROID_PROJECT}/app/src/main/java/com/yourgame/doengine" +mkdir -p "${ANDROID_PROJECT}/app/src/main/java/org/libsdl/app" +mkdir -p "${ANDROID_PROJECT}/app/src/main/cpp" +mkdir -p "${ANDROID_PROJECT}/app/src/main/res/values" +mkdir -p "${ANDROID_PROJECT}/app/src/main/res/mipmap-hdpi" +mkdir -p "${ANDROID_PROJECT}/app/src/main/res/mipmap-mdpi" +mkdir -p "${ANDROID_PROJECT}/app/src/main/res/mipmap-xhdpi" +mkdir -p "${ANDROID_PROJECT}/app/src/main/res/mipmap-xxhdpi" +mkdir -p "${ANDROID_PROJECT}/app/src/main/res/mipmap-xxxhdpi" +mkdir -p "${ANDROID_PROJECT}/app/libs/arm64-v8a" +mkdir -p "${ANDROID_PROJECT}/app/libs/armeabi-v7a" + +echo -e "${GREEN}✓${NC} Directory structure created" + +# Create strings.xml +cat > "${ANDROID_PROJECT}/app/src/main/res/values/strings.xml" << 'EOF' + + + DoEngine Game + +EOF + +echo -e "${GREEN}✓${NC} Created strings.xml" + +# Create settings.gradle +cat > "${ANDROID_PROJECT}/settings.gradle" << 'EOF' +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "DoEngineGame" +include ':app' +EOF + +echo -e "${GREEN}✓${NC} Created settings.gradle" + +# Create gradle.properties +cat > "${ANDROID_PROJECT}/gradle.properties" << 'EOF' +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +android.useAndroidX=true +android.enableJetifier=true +android.nonTransitiveRClass=false +EOF + +echo -e "${GREEN}✓${NC} Created gradle.properties" + +# Create local.properties with SDK path +if [ -n "$ANDROID_HOME" ]; then + cat > "${ANDROID_PROJECT}/local.properties" << EOF +sdk.dir=${ANDROID_HOME} +EOF + echo -e "${GREEN}✓${NC} Created local.properties with SDK path" +else + echo -e "${YELLOW}⚠${NC} ANDROID_HOME not set, you'll need to set sdk.dir in local.properties manually" +fi + +# Create .gitignore +cat > "${ANDROID_PROJECT}/.gitignore" << 'EOF' +*.iml +.gradle +/local.properties +/.idea +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +*.apk +*.ap_ +*.dex +*.class +bin/ +gen/ +out/ +EOF + +echo -e "${GREEN}✓${NC} Created .gitignore" + +# Create proguard-rules.pro +cat > "${ANDROID_PROJECT}/app/proguard-rules.pro" << 'EOF' +# Add project specific ProGuard rules here. +-keep class org.libsdl.app.** { *; } +EOF + +echo -e "${GREEN}✓${NC} Created proguard-rules.pro" + +echo "" +echo -e "${GREEN}=== Project Structure Created ===${NC}" +echo "" +echo "Next steps:" +echo "1. Copy the generated artifacts to the appropriate locations:" +echo " - AndroidManifest.xml → ${ANDROID_PROJECT}/app/src/main/" +echo " - build.gradle (app) → ${ANDROID_PROJECT}/app/" +echo " - build.gradle (project) → ${ANDROID_PROJECT}/" +echo " - MainActivity.java → ${ANDROID_PROJECT}/app/src/main/java/com/yourgame/doengine/" +echo " - SDLActivity.java → ${ANDROID_PROJECT}/app/src/main/java/org/libsdl/app/" +echo " - main.cpp → ${ANDROID_PROJECT}/app/src/main/cpp/" +echo " - CMakeLists.txt (app) → ${ANDROID_PROJECT}/app/src/main/cpp/" +echo "" +echo "2. Build DoEngine libraries:" +echo " cd ${DOENGINE_ROOT}/Android" +echo " ./build-android.sh" +echo "" +echo "3. Copy DoEngine libraries to the Android project:" +echo " cp ${DOENGINE_ROOT}/Android/build/libs/*/*.so ${ANDROID_PROJECT}/app/libs/" +echo "" +echo "4. Open project in Android Studio:" +echo " studio ${ANDROID_PROJECT}" +echo "" +echo -e "${GREEN}Setup complete!${NC}" \ No newline at end of file diff --git a/android/boiler-plate-gen/selectable-gen.sh b/android/boiler-plate-gen/selectable-gen.sh new file mode 100755 index 0000000..83ea90e --- /dev/null +++ b/android/boiler-plate-gen/selectable-gen.sh @@ -0,0 +1,895 @@ +#!/bin/bash + +# Complete Android Project Generator for DoEngine +# This script creates all necessary files and project structure + +set -e + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +NC='\033[0m' + +# Banner +echo -e "${CYAN}" +echo "╔════════════════════════════════════════════════════════════╗" +echo "║ ║" +echo "║ DoEngine Android Project Generator ║" +echo "║ Complete Boilerplate Creator ║" +echo "║ ║" +echo "╚════════════════════════════════════════════════════════════╝" +echo -e "${NC}" +echo "" + +# Get script directory +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# Default values +DEFAULT_DOENGINE_PATH="$(dirname "$SCRIPT_DIR")" +DEFAULT_PROJECT_NAME="DoEngineGame" +DEFAULT_PACKAGE_NAME="com.yourgame.doengine" +DEFAULT_APP_NAME="DoEngine Game" + +# Prompt for configuration +echo -e "${BLUE}=== Project Configuration ===${NC}" +echo "" + +# DoEngine path +read -p "Enter DoEngine root path [${DEFAULT_DOENGINE_PATH}]: " DOENGINE_ROOT +DOENGINE_ROOT=${DOENGINE_ROOT:-$DEFAULT_DOENGINE_PATH} + +# Validate DoEngine path +if [ ! -d "$DOENGINE_ROOT" ]; then + echo -e "${RED}Error: DoEngine path does not exist: ${DOENGINE_ROOT}${NC}" + exit 1 +fi + +if [ ! -d "$DOENGINE_ROOT/includes" ] || [ ! -d "$DOENGINE_ROOT/src" ]; then + echo -e "${YELLOW}Warning: DoEngine path doesn't contain 'includes' and 'src' directories${NC}" + read -p "Continue anyway? (y/n): " CONTINUE + if [ "$CONTINUE" != "y" ]; then + exit 1 + fi +fi + +# Project name +read -p "Enter project name [${DEFAULT_PROJECT_NAME}]: " PROJECT_NAME +PROJECT_NAME=${PROJECT_NAME:-$DEFAULT_PROJECT_NAME} + +# Package name +read -p "Enter package name (e.g., com.company.game) [${DEFAULT_PACKAGE_NAME}]: " PACKAGE_NAME +PACKAGE_NAME=${PACKAGE_NAME:-$DEFAULT_PACKAGE_NAME} + +# Validate package name +if ! [[ $PACKAGE_NAME =~ ^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$ ]]; then + echo -e "${RED}Error: Invalid package name format. Use lowercase letters and dots (e.g., com.company.game)${NC}" + exit 1 +fi + +# App display name +read -p "Enter app display name [${DEFAULT_APP_NAME}]: " APP_NAME +APP_NAME=${APP_NAME:-$DEFAULT_APP_NAME} + +# Output directory +read -p "Enter output directory for Android project [${DOENGINE_ROOT}/AndroidGame]: " ANDROID_PROJECT +ANDROID_PROJECT=${ANDROID_PROJECT:-${DOENGINE_ROOT}/AndroidGame} + +# Convert package name to path +PACKAGE_PATH=$(echo $PACKAGE_NAME | tr '.' '/') + +echo "" +echo -e "${GREEN}=== Configuration Summary ===${NC}" +echo -e "${CYAN}DoEngine Path:${NC} $DOENGINE_ROOT" +echo -e "${CYAN}Project Name:${NC} $PROJECT_NAME" +echo -e "${CYAN}Package Name:${NC} $PACKAGE_NAME" +echo -e "${CYAN}App Name:${NC} $APP_NAME" +echo -e "${CYAN}Output Directory:${NC} $ANDROID_PROJECT" +echo "" +read -p "Continue with this configuration? (y/n): " CONFIRM +if [ "$CONFIRM" != "y" ]; then + echo "Cancelled." + exit 0 +fi + +echo "" +echo -e "${YELLOW}Creating project structure...${NC}" + +# Create directory structure +mkdir -p "${ANDROID_PROJECT}" +mkdir -p "${ANDROID_PROJECT}/app/src/main/java/${PACKAGE_PATH}" +mkdir -p "${ANDROID_PROJECT}/app/src/main/java/org/libsdl/app" +mkdir -p "${ANDROID_PROJECT}/app/src/main/cpp" +mkdir -p "${ANDROID_PROJECT}/app/src/main/res/values" +mkdir -p "${ANDROID_PROJECT}/app/src/main/res/mipmap-hdpi" +mkdir -p "${ANDROID_PROJECT}/app/src/main/res/mipmap-mdpi" +mkdir -p "${ANDROID_PROJECT}/app/src/main/res/mipmap-xhdpi" +mkdir -p "${ANDROID_PROJECT}/app/src/main/res/mipmap-xxhdpi" +mkdir -p "${ANDROID_PROJECT}/app/src/main/res/mipmap-xxxhdpi" +mkdir -p "${ANDROID_PROJECT}/app/libs/arm64-v8a" +mkdir -p "${ANDROID_PROJECT}/app/libs/armeabi-v7a" + +echo -e "${GREEN}✓${NC} Directory structure created" + +# Create AndroidManifest.xml +echo -e "${YELLOW}Generating AndroidManifest.xml...${NC}" +cat > "${ANDROID_PROJECT}/app/src/main/AndroidManifest.xml" << EOF + + + + + + + + + + + + + + + + + + + + + + + + + + + + +EOF +echo -e "${GREEN}✓${NC} AndroidManifest.xml created" + +# Create MainActivity.java +echo -e "${YELLOW}Generating MainActivity.java...${NC}" +cat > "${ANDROID_PROJECT}/app/src/main/java/${PACKAGE_PATH}/MainActivity.java" << EOF +package ${PACKAGE_NAME}; + +import org.libsdl.app.SDLActivity; + +public class MainActivity extends SDLActivity { + + /** + * This method is called by SDL before loading the native shared libraries. + * It can be overridden to provide names of shared libraries to be loaded. + * The default implementation returns the defaults. It never returns null. + * An array returned by a new implementation must at least contain "SDL2". + * Also keep in mind that the order the libraries are loaded may matter. + * @return names of shared libraries to be loaded (e.g. "SDL2", "main"). + */ + @Override + protected String[] getLibraries() { + return new String[] { + "SDL2", + "SDL2_image", + "SDL2_mixer", + "SDL2_ttf", + "SDL2_net", + "LibDoengine", + "${PROJECT_NAME}" + }; + } + + /** + * This method is called by SDL using JNI. + * @return the name of the main function to call from the native library + */ + @Override + protected String getMainFunction() { + return "SDL_main"; + } +} +EOF +echo -e "${GREEN}✓${NC} MainActivity.java created" + +# Create SDLActivity.java +echo -e "${YELLOW}Generating SDLActivity.java...${NC}" +cat > "${ANDROID_PROJECT}/app/src/main/java/org/libsdl/app/SDLActivity.java" << 'EOF' +package org.libsdl.app; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +/** + * SDL Activity - Base class for SDL applications on Android + * This is a minimal implementation. For production, use the full SDLActivity from SDL2. + */ +public class SDLActivity extends Activity { + private static final String TAG = "SDL"; + + // Main SDL surface + protected static SDLActivity mSingleton; + protected static SDLSurface mSurface; + + // Load the .so files + public static void loadLibraries() { + for (String lib : mSingleton.getLibraries()) { + System.loadLibrary(lib); + } + } + + /** + * This method returns the name of the shared library to be loaded + */ + protected String[] getLibraries() { + return new String[] { + "SDL2", + "main" + }; + } + + /** + * This method returns the name of the main function + */ + protected String getMainFunction() { + return "SDL_main"; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Keep singleton + mSingleton = this; + + // Load libraries + loadLibraries(); + + // Set up the surface + mSurface = new SDLSurface(getApplication()); + setContentView(mSurface); + + // Keep screen on + mSurface.setKeepScreenOn(true); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + // Cleanup + nativeQuit(); + } + + // Native methods + public static native int nativeInit(Object arguments); + public static native void nativeQuit(); + public static native void onNativeResize(); + public static native void onNativeSurfaceChanged(); + public static native void onNativeSurfaceDestroyed(); + + /** + * SDLSurface - Surface view for rendering + */ + static class SDLSurface extends SurfaceView implements SurfaceHolder.Callback { + + public SDLSurface(Context context) { + super(context); + getHolder().addCallback(this); + setFocusable(true); + setFocusableInTouchMode(true); + requestFocus(); + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + // Start native thread + new Thread(new Runnable() { + @Override + public void run() { + nativeInit(null); + } + }).start(); + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + onNativeResize(); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + onNativeSurfaceDestroyed(); + } + } +} +EOF +echo -e "${GREEN}✓${NC} SDLActivity.java created" + +# Create main.cpp +echo -e "${YELLOW}Generating main.cpp...${NC}" +cat > "${ANDROID_PROJECT}/app/src/main/cpp/main.cpp" << 'EOF' +#include +#include +#include + +#define LOG_TAG "DoEngineGame" +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) + +// Screen dimensions +const int SCREEN_WIDTH = 1280; +const int SCREEN_HEIGHT = 720; + +// Game class using DoEngine (you'll implement this with your DoEngine) +class Game { +private: + SDL_Window* window; + SDL_Renderer* renderer; + bool isRunning; + +public: + Game() : window(nullptr), renderer(nullptr), isRunning(false) {} + + bool initialize() { + LOGI("Initializing game..."); + + // Initialize SDL + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER) < 0) { + LOGE("SDL initialization failed: %s", SDL_GetError()); + return false; + } + + // Initialize SDL_image + int imgFlags = IMG_INIT_PNG | IMG_INIT_JPG; + if (!(IMG_Init(imgFlags) & imgFlags)) { + LOGE("SDL_image initialization failed: %s", IMG_GetError()); + return false; + } + + // Create window + window = SDL_CreateWindow( + "DoEngine Game", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + SCREEN_WIDTH, + SCREEN_HEIGHT, + SDL_WINDOW_SHOWN | SDL_WINDOW_FULLSCREEN + ); + + if (!window) { + LOGE("Window creation failed: %s", SDL_GetError()); + return false; + } + + // Create renderer + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); + if (!renderer) { + LOGE("Renderer creation failed: %s", SDL_GetError()); + return false; + } + + SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF); + + LOGI("Game initialized successfully!"); + isRunning = true; + return true; + } + + void handleEvents() { + SDL_Event event; + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + isRunning = false; + break; + + case SDL_FINGERDOWN: + LOGI("Touch down at: %f, %f", event.tfinger.x, event.tfinger.y); + break; + + case SDL_FINGERUP: + LOGI("Touch up at: %f, %f", event.tfinger.x, event.tfinger.y); + break; + + case SDL_APP_TERMINATING: + LOGI("App terminating"); + isRunning = false; + break; + + case SDL_APP_LOWMEMORY: + LOGI("Low memory warning"); + break; + + case SDL_APP_WILLENTERBACKGROUND: + LOGI("App will enter background"); + break; + + case SDL_APP_DIDENTERBACKGROUND: + LOGI("App entered background"); + break; + + case SDL_APP_WILLENTERFOREGROUND: + LOGI("App will enter foreground"); + break; + + case SDL_APP_DIDENTERFOREGROUND: + LOGI("App entered foreground"); + break; + } + } + } + + void update() { + // TODO: Update game logic using DoEngine + // This is where you'll use your DoEngine API + } + + void render() { + // Clear screen + SDL_SetRenderDrawColor(renderer, 0x20, 0x20, 0x30, 0xFF); + SDL_RenderClear(renderer); + + // TODO: Render game using DoEngine + // This is where you'll use your DoEngine rendering + + // Example: Draw a colored rectangle + SDL_Rect fillRect = { SCREEN_WIDTH / 4, SCREEN_HEIGHT / 4, + SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 }; + SDL_SetRenderDrawColor(renderer, 0xFF, 0x00, 0x00, 0xFF); + SDL_RenderFillRect(renderer, &fillRect); + + // Present + SDL_RenderPresent(renderer); + } + + void run() { + LOGI("Starting game loop..."); + + const float targetFPS = 60.0f; + const float frameDelay = 1000.0f / targetFPS; + + while (isRunning) { + Uint32 frameStart = SDL_GetTicks(); + + handleEvents(); + update(); + render(); + + Uint32 frameTime = SDL_GetTicks() - frameStart; + if (frameDelay > frameTime) { + SDL_Delay(frameDelay - frameTime); + } + + // Calculate FPS (optional logging) + static int frameCount = 0; + static Uint32 fpsTimer = SDL_GetTicks(); + frameCount++; + if (SDL_GetTicks() - fpsTimer > 1000) { + LOGI("FPS: %d", frameCount); + frameCount = 0; + fpsTimer = SDL_GetTicks(); + } + } + + LOGI("Game loop ended"); + } + + void cleanup() { + LOGI("Cleaning up..."); + + if (renderer) { + SDL_DestroyRenderer(renderer); + renderer = nullptr; + } + + if (window) { + SDL_DestroyWindow(window); + window = nullptr; + } + + IMG_Quit(); + SDL_Quit(); + + LOGI("Cleanup complete"); + } + + ~Game() { + cleanup(); + } +}; + +// Main entry point +int main(int argc, char* argv[]) { + LOGI("DoEngine Game starting..."); + + Game game; + + if (!game.initialize()) { + LOGE("Failed to initialize game!"); + return 1; + } + + game.run(); + + LOGI("DoEngine Game exiting normally"); + return 0; +} +EOF +echo -e "${GREEN}✓${NC} main.cpp created" + +# Create CMakeLists.txt +echo -e "${YELLOW}Generating CMakeLists.txt...${NC}" +cat > "${ANDROID_PROJECT}/app/src/main/cpp/CMakeLists.txt" << EOF +cmake_minimum_required(VERSION 3.18.1) + +project(${PROJECT_NAME}) + +# Set C/C++ standards +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Define paths +set(DOENGINE_ROOT "${DOENGINE_ROOT}") +set(DOENGINE_ANDROID "\${DOENGINE_ROOT}/Android") +set(GAME_SRC_DIR "\${CMAKE_CURRENT_SOURCE_DIR}") + +# Add DoEngine library build +add_subdirectory(\${DOENGINE_ANDROID} \${CMAKE_CURRENT_BINARY_DIR}/doengine) + +# Collect game source files +file(GLOB_RECURSE GAME_SOURCES + "\${GAME_SRC_DIR}/*.c" + "\${GAME_SRC_DIR}/*.cpp" +) + +# Create shared library for Android (required by SDL2) +add_library(\${PROJECT_NAME} SHARED \${GAME_SOURCES}) + +# Include directories +target_include_directories(\${PROJECT_NAME} + PRIVATE + \${GAME_SRC_DIR} + \${DOENGINE_ROOT}/includes +) + +# Link libraries +target_link_libraries(\${PROJECT_NAME} + PRIVATE + LibDoengine + SDL2::SDL2main +) + +# Set compiler flags +target_compile_options(\${PROJECT_NAME} PRIVATE + -Wall + -Wextra +) + +# Android-specific macros +target_compile_definitions(\${PROJECT_NAME} + PRIVATE + ANDROID + __ANDROID__ +) + +# Export SDL_main for Android +set_target_properties(\${PROJECT_NAME} PROPERTIES + LINK_FLAGS "-u Java_org_libsdl_app_SDLActivity_nativeInit" +) + +message(STATUS "=== DoEngine Game Configuration ===") +message(STATUS "Game Sources: \${GAME_SRC_DIR}") +message(STATUS "DoEngine Path: \${DOENGINE_ROOT}") +message(STATUS "==================================") +EOF +echo -e "${GREEN}✓${NC} CMakeLists.txt created" + +# Create build.gradle (app) +echo -e "${YELLOW}Generating build.gradle (app)...${NC}" +cat > "${ANDROID_PROJECT}/app/build.gradle" << EOF +plugins { + id 'com.android.application' +} + +android { + namespace '${PACKAGE_NAME}' + compileSdk 34 + + defaultConfig { + applicationId "${PACKAGE_NAME}" + minSdk 21 + targetSdk 34 + versionCode 1 + versionName "1.0" + + ndk { + abiFilters 'arm64-v8a', 'armeabi-v7a' + } + + externalNativeBuild { + cmake { + arguments "-DANDROID_STL=c++_shared" + cppFlags "-std=c++17 -Wall -Wextra" + } + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + debug { + debuggable true + } + } + + externalNativeBuild { + cmake { + path file('src/main/cpp/CMakeLists.txt') + version '3.18.1' + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + sourceSets { + main { + jniLibs.srcDirs = ['libs'] + } + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'com.google.android.material:material:1.11.0' +} +EOF +echo -e "${GREEN}✓${NC} build.gradle (app) created" + +# Create build.gradle (project) +echo -e "${YELLOW}Generating build.gradle (project)...${NC}" +cat > "${ANDROID_PROJECT}/build.gradle" << 'EOF' +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id 'com.android.application' version '8.2.0' apply false +} +EOF +echo -e "${GREEN}✓${NC} build.gradle (project) created" + +# Create settings.gradle +echo -e "${YELLOW}Generating settings.gradle...${NC}" +cat > "${ANDROID_PROJECT}/settings.gradle" << EOF +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "${PROJECT_NAME}" +include ':app' +EOF +echo -e "${GREEN}✓${NC} settings.gradle created" + +# Create gradle.properties +echo -e "${YELLOW}Generating gradle.properties...${NC}" +cat > "${ANDROID_PROJECT}/gradle.properties" << 'EOF' +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +android.useAndroidX=true +android.enableJetifier=true +android.nonTransitiveRClass=false +EOF +echo -e "${GREEN}✓${NC} gradle.properties created" + +# Create local.properties +if [ -n "$ANDROID_HOME" ]; then + echo -e "${YELLOW}Generating local.properties...${NC}" + cat > "${ANDROID_PROJECT}/local.properties" << EOF +sdk.dir=${ANDROID_HOME} +EOF + echo -e "${GREEN}✓${NC} local.properties created with SDK path" +else + echo -e "${YELLOW}⚠${NC} ANDROID_HOME not set, skipping local.properties" +fi + +# Create strings.xml +echo -e "${YELLOW}Generating strings.xml...${NC}" +cat > "${ANDROID_PROJECT}/app/src/main/res/values/strings.xml" << EOF + + + ${APP_NAME} + +EOF +echo -e "${GREEN}✓${NC} strings.xml created" + +# Create proguard-rules.pro +echo -e "${YELLOW}Generating proguard-rules.pro...${NC}" +cat > "${ANDROID_PROJECT}/app/proguard-rules.pro" << 'EOF' +# Add project specific ProGuard rules here. +-keep class org.libsdl.app.** { *; } +EOF +echo -e "${GREEN}✓${NC} proguard-rules.pro created" + +# Create .gitignore +echo -e "${YELLOW}Generating .gitignore...${NC}" +cat > "${ANDROID_PROJECT}/.gitignore" << 'EOF' +*.iml +.gradle +/local.properties +/.idea +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +*.apk +*.ap_ +*.dex +*.class +bin/ +gen/ +out/ +app/libs/ +EOF +echo -e "${GREEN}✓${NC} .gitignore created" + +# Create README +echo -e "${YELLOW}Generating README.md...${NC}" +cat > "${ANDROID_PROJECT}/README.md" << EOF +# ${APP_NAME} + +Android game project using DoEngine and SDL2. + +## Project Information + +- **Package Name**: ${PACKAGE_NAME} +- **Project Name**: ${PROJECT_NAME} +- **DoEngine Path**: ${DOENGINE_ROOT} + +## Build Instructions + +### 1. Build DoEngine Libraries + +\`\`\`bash +cd ${DOENGINE_ROOT}/Android +./build-android.sh +\`\`\` + +### 2. Copy Libraries to Android Project + +\`\`\`bash +cp ${DOENGINE_ROOT}/Android/build/libs/arm64-v8a/*.so ${ANDROID_PROJECT}/app/libs/arm64-v8a/ +cp ${DOENGINE_ROOT}/Android/build/libs/armeabi-v7a/*.so ${ANDROID_PROJECT}/app/libs/armeabi-v7a/ +\`\`\` + +### 3. Open in Android Studio + +\`\`\`bash +studio ${ANDROID_PROJECT} +\`\`\` + +Or open Android Studio and select "Open an Existing Project", then navigate to: +\`${ANDROID_PROJECT}\` + +### 4. Build and Run + +- Connect an Android device or start an emulator +- Click the "Run" button in Android Studio + +## Project Structure + +\`\`\` +${ANDROID_PROJECT}/ +├── app/ +│ ├── src/main/ +│ │ ├── java/${PACKAGE_PATH}/ +│ │ │ └── MainActivity.java +│ │ ├── cpp/ +│ │ │ ├── main.cpp +│ │ │ └── CMakeLists.txt +│ │ ├── res/ +│ │ └── AndroidManifest.xml +│ ├── libs/ (SDL2 & DoEngine .so files) +│ └── build.gradle +├── build.gradle +└── settings.gradle +\`\`\` + +## Development + +The main game code is in \`app/src/main/cpp/main.cpp\`. This is where you'll integrate your DoEngine API calls. + +## Troubleshooting + +### Libraries not found +Make sure you've built DoEngine and copied the .so files to the \`app/libs/\` directory. + +### NDK not found +Set the ANDROID_NDK_ROOT environment variable or update \`local.properties\`. + +### Build errors +Clean and rebuild: +\`\`\`bash +./gradlew clean +./gradlew build +\`\`\` +EOF +echo -e "${GREEN}✓${NC} README.md created" + +# Create build helper script +echo -e "${YELLOW}Generating build-and-deploy.sh...${NC}" +cat > "${ANDROID_PROJECT}/build-and-deploy.sh" << EOF +#!/bin/bash + +# Build and Deploy Script for ${APP_NAME} + +set -e + +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +echo -e "\${GREEN}=== Building DoEngine Libraries ===\${NC}" +cd "${DOENGINE_ROOT}/Android" +./build-android.sh + +echo "" +echo -e "\${GREEN}=== Copying Libraries to Android Project ===\${NC}" +cp -v "${DOENGINE_ROOT}/Android/build/libs/arm64-v8a/"*.so "${ANDROID_PROJECT}/app/libs/arm64-v8a/" +cp -v "${DOENGINE_ROOT}/Android/build/libs/armeabi-v7a/"*.so "${ANDROID_PROJECT}/app/libs/armeabi-v7a/" + +echo "" +echo -e "\${GREEN}=== Libraries Deployed ===\${NC}" +echo "You can now build the Android project in Android Studio" +echo "" +echo "To open in Android Studio:" +echo " studio ${ANDROID_PROJECT}" +EOF +chmod +x "${ANDROID_PROJECT}/build-and-deploy.sh" +echo -e "${GREEN}✓${NC} build-and-deploy.sh created" + +# Summary +echo "" +echo -e "${GREEN}╔════════════════════════════════════════════════════════════╗${NC}" +echo -e "${GREEN}║ ║${NC}" +echo -e "${GREEN}║ Project Created Successfully! ║${NC}" +echo -e "${GREEN}║ ║${NC}" +echo -e "${GREEN}╚════════════════════════════════════════════════════════════╝${NC}" +echo "" +echo -e "${CYAN}Project Location:${NC} ${ANDROID_PROJECT}" +echo "" +echo -e "${BLUE}=== Next Steps ===${NC}" +echo "" +echo -e "${YELLOW}1.${NC} Build DoEngine libraries:" +echo -e " cd ${DOENGINE_ROOT}/Android" +echo -e " ./build-android.sh" +echo "" +echo -e "${YELLOW}2.${NC} Deploy libraries to Android project:" +echo -e " cd ${ANDROID_PROJECT}" +echo -e " ./build-and-deploy.sh" \ No newline at end of file diff --git a/android/build-android-2.sh b/android/build-android-2.sh new file mode 100644 index 0000000..8d838ef --- /dev/null +++ b/android/build-android-2.sh @@ -0,0 +1,169 @@ +#!/bin/bash + +# Android Build Script for LibDoengine +# This script builds the library for multiple Android ABIs + +set -e # Exit on error + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Configuration +NDK_PATH="${ANDROID_NDK_ROOT:-$ANDROID_NDK}" + +# If NDK_PATH points to the ndk directory (not a specific version), find the latest version +if [ -d "$NDK_PATH" ] && [ ! -f "$NDK_PATH/build/cmake/android.toolchain.cmake" ]; then + echo -e "${YELLOW}NDK path appears to be the base directory, searching for installed versions...${NC}" + # Find the latest NDK version + LATEST_NDK=$(ls -1 "$NDK_PATH" | grep -E '^[0-9]+\.' | sort -V | tail -n 1) + if [ -n "$LATEST_NDK" ]; then + NDK_PATH="$NDK_PATH/$LATEST_NDK" + echo -e "${GREEN}Found NDK version: ${NC}$LATEST_NDK" + fi +fi + +MIN_SDK_VERSION=21 +TARGET_SDK_VERSION=33 +BUILD_TYPE="Release" +LIBRARY_TYPE="SHARED" # Change to "STATIC" for static library + +# Android ABIs to build (uncomment the ones you need) +ABIS=( + "arm64-v8a" + "armeabi-v7a" + # "x86" + # "x86_64" +) + +# Get script directory +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" +BUILD_DIR="${SCRIPT_DIR}/build" + +echo -e "${GREEN}=== LibDoengine Android Build Script ===${NC}" +echo "" + +# Check if NDK is set +if [ -z "$NDK_PATH" ]; then + echo -e "${RED}Error: Android NDK not found!${NC}" + echo "Please set ANDROID_NDK_ROOT or ANDROID_NDK environment variable" + echo "Example: export ANDROID_NDK_ROOT=/path/to/android-ndk" + exit 1 +fi + +# Verify toolchain file exists +if [ ! -f "$NDK_PATH/build/cmake/android.toolchain.cmake" ]; then + echo -e "${RED}Error: Android NDK toolchain not found at: $NDK_PATH${NC}" + echo "The NDK path should point to a complete NDK installation." + echo "Please check your NDK installation or set the correct path." + echo "" + echo "If your NDK is at /home/neonland/Android/Sdk/ndk, try:" + echo " export ANDROID_NDK_ROOT=/home/neonland/Android/Sdk/ndk/[version]" + echo "" + echo "Available NDK versions:" + if [ -d "$(dirname "$NDK_PATH")" ]; then + ls -1 "$(dirname "$NDK_PATH")" 2>/dev/null | grep -E '^[0-9]+\.' || echo " No NDK versions found" + fi + exit 1 +fi + +echo -e "${GREEN}NDK Path: ${NC}$NDK_PATH" +echo -e "${GREEN}Min SDK: ${NC}$MIN_SDK_VERSION" +echo -e "${GREEN}Target SDK: ${NC}$TARGET_SDK_VERSION" +echo -e "${GREEN}Build Type: ${NC}$BUILD_TYPE" +echo -e "${GREEN}Library Type: ${NC}$LIBRARY_TYPE" +echo "" + +# Function to build for a specific ABI +build_abi() { + local ABI=$1 + echo -e "${YELLOW}Building for $ABI...${NC}" + + local ABI_BUILD_DIR="${BUILD_DIR}/${ABI}" + mkdir -p "$ABI_BUILD_DIR" + + cd "$ABI_BUILD_DIR" + + # Configure with CMake + cmake "${SCRIPT_DIR}" \ + -DCMAKE_TOOLCHAIN_FILE="${NDK_PATH}/build/cmake/android.toolchain.cmake" \ + -DANDROID_ABI="${ABI}" \ + -DANDROID_PLATFORM="android-${MIN_SDK_VERSION}" \ + -DANDROID_STL=c++_shared \ + -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ + -DBUILD_SHARED_LIBS=$([ "$LIBRARY_TYPE" = "SHARED" ] && echo "ON" || echo "OFF") \ + -DCMAKE_INSTALL_PREFIX="${BUILD_DIR}/install/${ABI}" + + # Build + cmake --build . --config ${BUILD_TYPE} -- -j$(nproc 2>/dev/null || echo 4) + + # Install + cmake --install . + + echo -e "${GREEN}✓ Successfully built for $ABI${NC}" + echo "" + + cd "$SCRIPT_DIR" +} + +# Clean build directory if requested +if [ "$1" = "clean" ]; then + echo -e "${YELLOW}Cleaning build directory...${NC}" + rm -rf "$BUILD_DIR" + echo -e "${GREEN}✓ Clean complete${NC}" + exit 0 +fi + +# Create output directory +mkdir -p "$BUILD_DIR" + +# Build for each ABI +for ABI in "${ABIS[@]}"; do + build_abi "$ABI" +done + +# Create a summary +echo -e "${GREEN}=== Build Summary ===${NC}" +echo "" +echo "Libraries built for:" +for ABI in "${ABIS[@]}"; do + LIB_PATH="${BUILD_DIR}/install/${ABI}/lib/${ABI}" + if [ -d "$LIB_PATH" ]; then + echo -e " ${GREEN}✓${NC} $ABI" + echo " Libraries:" + ls -lh "$LIB_PATH"/*.so 2>/dev/null | awk '{print " - " $9 " (" $5 ")"}' || true + ls -lh "$LIB_PATH"/*.a 2>/dev/null | awk '{print " - " $9 " (" $5 ")"}' || true + fi + echo "" +done + +# Create a libs directory with all shared libraries organized by ABI +LIBS_OUTPUT="${BUILD_DIR}/libs" +mkdir -p "$LIBS_OUTPUT" + +echo -e "${YELLOW}Copying all shared libraries to: ${NC}${LIBS_OUTPUT}" +for ABI in "${ABIS[@]}"; do + SRC_LIB_PATH="${BUILD_DIR}/install/${ABI}/lib/${ABI}" + DEST_ABI_PATH="${LIBS_OUTPUT}/${ABI}" + + if [ -d "$SRC_LIB_PATH" ]; then + mkdir -p "$DEST_ABI_PATH" + cp -v "$SRC_LIB_PATH"/*.so "$DEST_ABI_PATH/" 2>/dev/null || true + echo -e " ${GREEN}✓${NC} Copied libraries for $ABI" + fi +done + +echo "" +echo -e "${GREEN}=== Build Complete ===${NC}" +echo "Output directories:" +echo " - Install dir: ${BUILD_DIR}/install" +echo " - Libs dir: ${BUILD_DIR}/libs (organized by ABI)" +echo "" +echo "All shared libraries (.so files) including SDL2 and LibDoengine are in:" +echo " ${BUILD_DIR}/libs/arm64-v8a/" +echo " ${BUILD_DIR}/libs/armeabi-v7a/" +echo "" +echo "To clean build files, run: $0 clean" \ No newline at end of file diff --git a/android/build-android.sh b/android/build-android.sh new file mode 100755 index 0000000..4ce3c37 --- /dev/null +++ b/android/build-android.sh @@ -0,0 +1,144 @@ +#!/bin/bash + +# Android Build Script for LibDoengine +# This script builds the library for multiple Android ABIs + +set -e # Exit on error + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Configuration +NDK_PATH="${ANDROID_NDK_ROOT:-$ANDROID_NDK}" + +# If NDK_PATH points to the ndk directory (not a specific version), find the latest version +if [ -d "$NDK_PATH" ] && [ ! -f "$NDK_PATH/build/cmake/android.toolchain.cmake" ]; then + echo -e "${YELLOW}NDK path appears to be the base directory, searching for installed versions...${NC}" + # Find the latest NDK version + LATEST_NDK=$(ls -1 "$NDK_PATH" | grep -E '^[0-9]+\.' | sort -V | tail -n 1) + if [ -n "$LATEST_NDK" ]; then + NDK_PATH="$NDK_PATH/$LATEST_NDK" + echo -e "${GREEN}Found NDK version: ${NC}$LATEST_NDK" + fi +fi + +MIN_SDK_VERSION=21 +TARGET_SDK_VERSION=33 +BUILD_TYPE="Release" +LIBRARY_TYPE="SHARED" # Change to "STATIC" for static library + +# Android ABIs to build (uncomment the ones you need) +ABIS=( + "arm64-v8a" + "armeabi-v7a" + # "x86" + # "x86_64" +) + +# Get script directory +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" +BUILD_DIR="${SCRIPT_DIR}/build" + +echo -e "${GREEN}=== LibDoengine Android Build Script ===${NC}" +echo "" + +# Check if NDK is set +if [ -z "$NDK_PATH" ]; then + echo -e "${RED}Error: Android NDK not found!${NC}" + echo "Please set ANDROID_NDK_ROOT or ANDROID_NDK environment variable" + echo "Example: export ANDROID_NDK_ROOT=/path/to/android-ndk" + exit 1 +fi + +# Verify toolchain file exists +if [ ! -f "$NDK_PATH/build/cmake/android.toolchain.cmake" ]; then + echo -e "${RED}Error: Android NDK toolchain not found at: $NDK_PATH${NC}" + echo "The NDK path should point to a complete NDK installation." + echo "Please check your NDK installation or set the correct path." + echo "" + echo "If your NDK is at /home/neonland/Android/Sdk/ndk, try:" + echo " export ANDROID_NDK_ROOT=/home/neonland/Android/Sdk/ndk/[version]" + echo "" + echo "Available NDK versions:" + if [ -d "$(dirname "$NDK_PATH")" ]; then + ls -1 "$(dirname "$NDK_PATH")" 2>/dev/null | grep -E '^[0-9]+\.' || echo " No NDK versions found" + fi + exit 1 +fi + +echo -e "${GREEN}NDK Path: ${NC}$NDK_PATH" +echo -e "${GREEN}Min SDK: ${NC}$MIN_SDK_VERSION" +echo -e "${GREEN}Target SDK: ${NC}$TARGET_SDK_VERSION" +echo -e "${GREEN}Build Type: ${NC}$BUILD_TYPE" +echo -e "${GREEN}Library Type: ${NC}$LIBRARY_TYPE" +echo "" + +# Function to build for a specific ABI +build_abi() { + local ABI=$1 + echo -e "${YELLOW}Building for $ABI...${NC}" + + local ABI_BUILD_DIR="${BUILD_DIR}/${ABI}" + mkdir -p "$ABI_BUILD_DIR" + + cd "$ABI_BUILD_DIR" + + # Configure with CMake + cmake "${SCRIPT_DIR}" \ + -DCMAKE_TOOLCHAIN_FILE="${NDK_PATH}/build/cmake/android.toolchain.cmake" \ + -DANDROID_ABI="${ABI}" \ + -DANDROID_PLATFORM="android-${MIN_SDK_VERSION}" \ + -DANDROID_STL=c++_shared \ + -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ + -DBUILD_SHARED_LIBS=$([ "$LIBRARY_TYPE" = "SHARED" ] && echo "ON" || echo "OFF") \ + -DCMAKE_INSTALL_PREFIX="${BUILD_DIR}/install/${ABI}" + + # Build + cmake --build . --config ${BUILD_TYPE} -- -j$(nproc 2>/dev/null || echo 4) + + # Install + cmake --install . + + echo -e "${GREEN}✓ Successfully built for $ABI${NC}" + echo "" + + cd "$SCRIPT_DIR" +} + +# Clean build directory if requested +if [ "$1" = "clean" ]; then + echo -e "${YELLOW}Cleaning build directory...${NC}" + rm -rf "$BUILD_DIR" + echo -e "${GREEN}✓ Clean complete${NC}" + exit 0 +fi + +# Create output directory +mkdir -p "$BUILD_DIR" + +# Build for each ABI +for ABI in "${ABIS[@]}"; do + build_abi "$ABI" +done + +# Create a summary +echo -e "${GREEN}=== Build Summary ===${NC}" +echo "" +echo "Libraries built for:" +for ABI in "${ABIS[@]}"; do + LIB_PATH="${BUILD_DIR}/install/${ABI}/lib/${ABI}" + if [ -d "$LIB_PATH" ]; then + echo -e " ${GREEN}✓${NC} $ABI" + ls -lh "$LIB_PATH"/*.so 2>/dev/null || ls -lh "$LIB_PATH"/*.a 2>/dev/null || true + fi +done + +echo "" +echo -e "${GREEN}=== Build Complete ===${NC}" +echo "Output directory: ${BUILD_DIR}/install" +echo "" +echo "To clean build files, run: $0 clean" \ No newline at end of file diff --git a/assets/background/background_single.png b/assets/background/background_single.png new file mode 100644 index 0000000..f08a0f2 Binary files /dev/null and b/assets/background/background_single.png differ diff --git a/assets/background/full_map.png b/assets/background/full_map.png new file mode 100644 index 0000000..6c59ad8 Binary files /dev/null and b/assets/background/full_map.png differ diff --git a/assets/background/heaven1.png b/assets/background/heaven1.png new file mode 100644 index 0000000..3b1db46 Binary files /dev/null and b/assets/background/heaven1.png differ diff --git a/assets/background/heaven2.png b/assets/background/heaven2.png new file mode 100644 index 0000000..9a032c6 Binary files /dev/null and b/assets/background/heaven2.png differ diff --git a/assets/background/layer-1.png b/assets/background/layer-1.png new file mode 100644 index 0000000..5fd0e49 Binary files /dev/null and b/assets/background/layer-1.png differ diff --git a/assets/background/layer-2.png b/assets/background/layer-2.png new file mode 100644 index 0000000..d8f225f Binary files /dev/null and b/assets/background/layer-2.png differ diff --git a/assets/background/layer-3.png b/assets/background/layer-3.png new file mode 100644 index 0000000..0d864bd Binary files /dev/null and b/assets/background/layer-3.png differ diff --git a/assets/background/layer-4.png b/assets/background/layer-4.png new file mode 100644 index 0000000..7a16cea Binary files /dev/null and b/assets/background/layer-4.png differ diff --git a/assets/background/layer-5.png b/assets/background/layer-5.png new file mode 100644 index 0000000..67703eb Binary files /dev/null and b/assets/background/layer-5.png differ diff --git a/assets/gfx/Comida.bmp b/assets/gfx/Comida.bmp new file mode 100644 index 0000000..7160daa Binary files /dev/null and b/assets/gfx/Comida.bmp differ diff --git a/assets/gfx/battle_city_tileset_1.png b/assets/gfx/battle_city_tileset_1.png new file mode 100644 index 0000000..c505bae Binary files /dev/null and b/assets/gfx/battle_city_tileset_1.png differ diff --git a/assets/gfx/enemigo.bmp b/assets/gfx/enemigo.bmp new file mode 100644 index 0000000..1d33fce Binary files /dev/null and b/assets/gfx/enemigo.bmp differ diff --git a/assets/gfx/muerte.bmp b/assets/gfx/muerte.bmp new file mode 100644 index 0000000..aeb71c1 Binary files /dev/null and b/assets/gfx/muerte.bmp differ diff --git a/assets/gfx/pacman.bmp b/assets/gfx/pacman.bmp new file mode 100644 index 0000000..14f64a8 Binary files /dev/null and b/assets/gfx/pacman.bmp differ diff --git a/assets/gfx/roca.bmp b/assets/gfx/roca.bmp new file mode 100644 index 0000000..81c0708 Binary files /dev/null and b/assets/gfx/roca.bmp differ diff --git a/assets/gfx/save.png b/assets/gfx/save.png new file mode 100644 index 0000000..9623a04 Binary files /dev/null and b/assets/gfx/save.png differ diff --git a/assets/gfx/shadow_dog.png b/assets/gfx/shadow_dog.png new file mode 100644 index 0000000..220b7b7 Binary files /dev/null and b/assets/gfx/shadow_dog.png differ diff --git a/assets/gfx/sprites/Resources.rar b/assets/gfx/sprites/Resources.rar new file mode 100644 index 0000000..e96263c Binary files /dev/null and b/assets/gfx/sprites/Resources.rar differ diff --git a/assets/gfx/sprites/battle_city.jpg b/assets/gfx/sprites/battle_city.jpg new file mode 100644 index 0000000..2798d14 Binary files /dev/null and b/assets/gfx/sprites/battle_city.jpg differ diff --git a/assets/gfx/sprites/clouds_sprites.png b/assets/gfx/sprites/clouds_sprites.png new file mode 100644 index 0000000..3b2623d Binary files /dev/null and b/assets/gfx/sprites/clouds_sprites.png differ diff --git a/assets/gfx/sprites/enemy_1.png b/assets/gfx/sprites/enemy_1.png new file mode 100644 index 0000000..a18f0e1 Binary files /dev/null and b/assets/gfx/sprites/enemy_1.png differ diff --git a/assets/gfx/sprites/map.png b/assets/gfx/sprites/map.png new file mode 100644 index 0000000..bbfbc24 Binary files /dev/null and b/assets/gfx/sprites/map.png differ diff --git a/assets/gfx/sprites/player.png b/assets/gfx/sprites/player.png new file mode 100644 index 0000000..5a5eeba Binary files /dev/null and b/assets/gfx/sprites/player.png differ diff --git a/assets/gfx/sprites/playersprites.gif b/assets/gfx/sprites/playersprites.gif new file mode 100644 index 0000000..0e38c49 Binary files /dev/null and b/assets/gfx/sprites/playersprites.gif differ diff --git a/assets/gfx/sprites/snake.png b/assets/gfx/sprites/snake.png new file mode 100644 index 0000000..2d982ba Binary files /dev/null and b/assets/gfx/sprites/snake.png differ diff --git a/assets/gfx/sprites/snake1b.png b/assets/gfx/sprites/snake1b.png new file mode 100644 index 0000000..323eab9 Binary files /dev/null and b/assets/gfx/sprites/snake1b.png differ diff --git a/assets/gfx/tileset/heaven_1.kra b/assets/gfx/tileset/heaven_1.kra new file mode 100644 index 0000000..1abcd02 Binary files /dev/null and b/assets/gfx/tileset/heaven_1.kra differ diff --git a/assets/gfx/tileset/heaven_1.png b/assets/gfx/tileset/heaven_1.png new file mode 100644 index 0000000..a5d9795 Binary files /dev/null and b/assets/gfx/tileset/heaven_1.png differ diff --git a/assets/gfx/tileset/platform_ts.png b/assets/gfx/tileset/platform_ts.png new file mode 100644 index 0000000..b8bb982 Binary files /dev/null and b/assets/gfx/tileset/platform_ts.png differ diff --git a/assets/gfx/tileset/project_ts.kra b/assets/gfx/tileset/project_ts.kra new file mode 100644 index 0000000..ebfd23a Binary files /dev/null and b/assets/gfx/tileset/project_ts.kra differ diff --git a/assets/gfx/tileset/tilemap1.png b/assets/gfx/tileset/tilemap1.png new file mode 100644 index 0000000..ca70f36 Binary files /dev/null and b/assets/gfx/tileset/tilemap1.png differ diff --git a/assets/maps/testmap.map b/assets/maps/testmap.map index 9813154..73ea13e 100644 --- a/assets/maps/testmap.map +++ b/assets/maps/testmap.map @@ -1,22 +1,17 @@ -20 -20 -1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 \ No newline at end of file +60 +15 +1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +4 4 4 3 0 1 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 +2 2 2 2 0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 \ No newline at end of file diff --git a/assets/maps/testmap_2.map b/assets/maps/testmap_2.map new file mode 100644 index 0000000..a35457e --- /dev/null +++ b/assets/maps/testmap_2.map @@ -0,0 +1,33 @@ +30 +30 +48 +bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +bgggggggggggggbbbbbbbbbbbbbbbb +bgwwwwwwwwwwwgbbbbbbbbbbbbbbbb +bgwbbbbbbbbwgbbbbbbbbbbbbbbbbb +bgwbgttttgbwgbbbbbbbbbbbbbbbbb +bgwbgttttgbwgbbbbbbbbbbbbbbbbb +bgwbbbbbbbbwgbbbbbbbbbbbbbbbbb +bgwwwwwwwwwwwgbbbbbbbbbbbbbbbb +bgggggggggggggbbbbbbbbbbbbbbbb +bbbbbbbBbbbbbbbbbbbbbbbbbbbbbb +bgggggggggggggbbbbbbbbbbbbbbbb +bgsssssssssssbgbbbbbbbbbbbbbbb +bgsgggggggggsbgbbbbbbbbbbbbbbb +bgsssssssssssbgbbbbbbbbbbbbbbb +bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +bgggggggggggggbbbbbbbbbbbbbbbb +bgwwwwwwwwwwwgbbbbbbbbbbbbbbbb +bgwbbbbbbbbwgbbbbbbbbbbbbbbbbb +bgwbgttttgbwgbbbbbbbbbbbbbbbbb +bgwbgttttgbwgbbbbbbbbbbbbbbbbb +bgwbbbbbbbbwgbbbbbbbbbbbbbbbbb +bgwwwwwwwwwwwgbbbbbbbbbbbbbbbb +bgggggggggggggbbbbbbbbbbbbbbbb +bbbbbbbBbbbbbbbbbbbbbbbbbbbbbb +bgggggggggggggbbbbbbbbbbbbbbbb +bgsssssssssssbgbbbbbbbbbbbbbbb +bgsgggggggggsbgbbbbbbbbbbbbbbb +bgsssssssssssbgbbbbbbbbbbbbbbb +bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \ No newline at end of file diff --git a/assets/settings/path.txt b/assets/settings/path.txt new file mode 100644 index 0000000..7b652b6 --- /dev/null +++ b/assets/settings/path.txt @@ -0,0 +1,6 @@ +[images] + +[fonts] + +[sounds] + diff --git a/cmake-module/fetchGLMProjectFromSource.cmake b/cmake-module/fetchGLMProjectFromSource.cmake new file mode 100644 index 0000000..ea258b5 --- /dev/null +++ b/cmake-module/fetchGLMProjectFromSource.cmake @@ -0,0 +1,19 @@ + +include(FetchContent) +message("Fecthing GLM Source code") +# SDL2 +# SET(SDL_SHARED FALSE) +FetchContent_Declare( + glm_lib + GIT_REPOSITORY https://github.com/g-truc/glm.git + GIT_TAG 1.0.3 + GIT_SHALLOW TRUE +) + +FetchContent_MakeAvailable(glm_lib) + + + + +message("END Fecthing GLM Source code") + diff --git a/cmake-module/fetchSDL3ProjectFromSource.cmake b/cmake-module/fetchSDL3ProjectFromSource.cmake new file mode 100644 index 0000000..f5cd2a0 --- /dev/null +++ b/cmake-module/fetchSDL3ProjectFromSource.cmake @@ -0,0 +1,44 @@ + +include(FetchContent) +message("Fecthing SDL Source code") +# SDL2 +# SET(SDL_SHARED FALSE) +FetchContent_Declare( + sdl_lib + GIT_REPOSITORY https://github.com/libsdl-org/SDL.git + GIT_TAG release-3.4.0 +) + +FetchContent_MakeAvailable(sdl_lib) + +# SDL2_image +FetchContent_Declare( + sdl_image + GIT_REPOSITORY https://github.com/libsdl-org/SDL_image.git + GIT_SUBMODULES external/ + GIT_TAG release-3.2.6 +) + +FetchContent_MakeAvailable(sdl_image) + +# SDL2_ttf +FetchContent_Declare( + sdl_ttf + GIT_REPOSITORY https://github.com/libsdl-org/SDL_ttf.git + GIT_TAG release-3.2.2 +) + +FetchContent_MakeAvailable(sdl_ttf) + +# SDL2_mixer +SET(SDL2MIXER_VENDORED TRUE) +FetchContent_Declare( + sdl_mixer + GIT_REPOSITORY https://github.com/libsdl-org/SDL_mixer.git + GIT_TAG release-2.6.3 +) + +FetchContent_MakeAvailable(sdl_mixer) + +message("END Fecthing SDL Source code") + diff --git a/sample/old_projects/CanvasTestState.h b/custom_project/CMakeLists.txt similarity index 100% rename from sample/old_projects/CanvasTestState.h rename to custom_project/CMakeLists.txt diff --git a/docs/ecs.md b/docs/ecs.md new file mode 100644 index 0000000..2789be8 --- /dev/null +++ b/docs/ecs.md @@ -0,0 +1,1099 @@ +# DOEngine Experiment 101 - trying an Entity Component System (ECS) + +## Overview + +This is a lightweight **Entity Component System (ECS)** implementation for the `doengine` game engine. The ECS pattern is an architectural pattern commonly used in game development that favors composition over inheritance, allowing for flexible and scalable game object management. + +--- + +## Table of Contents + +1. [Architecture Overview](#architecture-overview) +2. [Core Components](#core-components) +3. [Current Issues & Bugs](#current-issues--bugs) +4. [API Reference](#api-reference) +5. [Usage Examples](#usage-examples) +6. [Improvements & Recommendations](#improvements--recommendations) + +--- + +## Architecture Overview + +### What is ECS? + +The Entity Component System pattern separates: +- **Entities**: Containers/IDs that group components together +- **Components**: Pure data structures (position, velocity, sprite, etc.) +- **Systems**: Logic that operates on entities with specific component combinations + +### Design Pattern + +``` +Entity (Container) + └── Component Array (Fast lookup by type) + └── Component Vector (Storage) + └── Bitset (Quick component presence checks) +``` + +--- + +## Core Components + +### 1. Component Type System + +```cpp +using ComponentId = int; + +inline ComponentId getNextComponentId() +{ + static ComponentId lastId = 0; + return ++lastId; +} + +template +inline ComponentId getComponentTypeId() noexcept +{ + static ComponentId typeID = getNextComponentId(); + return typeID; +} +``` + +**Purpose**: Assigns unique IDs to each component type at compile-time using template specialization. + +**How it works**: Each template instantiation creates a static variable with its own ID. + +--- + +### 2. Component Base Class + +```cpp +struct Component +{ + Entity *entity; // Back-reference to owning entity + + virtual void init(){} // Called after component added + virtual void Update(){} // Per-frame logic + virtual void Render(){} // Rendering logic + virtual ~Component(){} // Virtual destructor for polymorphism +}; +``` + +**Purpose**: Base class that all components inherit from, providing common interface. + +--- + +### 3. Entity Class + +```cpp +struct Entity +{ + bool active = true; + + std::vector> components; // Ownership + ComponentArray componentArray; // Fast lookup + ComponentBitset componentBitset; // Presence flags + + // Methods: isActive, Update, Render, destroy + // addComponent, getComponent, hasComponent +}; +``` + +**Storage Strategy**: +- **components vector**: Owns all components via shared_ptr +- **componentArray**: Direct pointers indexed by component type ID (O(1) lookup) +- **componentBitset**: Fast boolean checks for component presence + +--- + +### 4. ECSManager + +```cpp +struct ECSManager +{ + std::vector> entities; + + void Update(); // Updates all entities + void Render(); // Renders all entities + void Refresh(); // Removes inactive entities + Entity& addEntity(); +}; +``` + +**Purpose**: Central manager for all entities in the game world. + +--- + +## Current Issues & Bugs + +### 🔴 Critical Bugs + +1. **Typo in `addComponent`**: + ```cpp + // WRONG: + T* c(new T(std::fordward(targs)...)); + + // CORRECT: + T* c(new T(std::forward(targs)...)); + ``` + +2. **Syntax Error in `getComponent`**: + ```cpp + // WRONG: + auto ptr[componentArray[getComponentTypeId()]]; + + // CORRECT: + auto ptr = componentArray[getComponentTypeId()]; + ``` + +3. **Missing `const` correctness in `getComponent`**: + - Method is marked `const` but accesses non-const array + +--- + +### ⚠️ Design Issues + +1. **Memory Safety**: + - Raw pointer in `componentArray` alongside `shared_ptr` in `components` + - Potential dangling pointers if component is removed + +2. **No Component Removal**: + - No `removeComponent()` method + +3. **Thread Safety**: + - Not thread-safe (not suitable for multi-threaded game loops) + +4. **Limited Scalability**: + - `maxComponents = 32` is hardcoded + - No entity pooling/recycling + +5. **Unused Parameter**: + - `Entity::Update(float elapsed = 0)` doesn't use `elapsed` + +--- + +## API Reference + +### Entity Methods + +#### `addComponent(Args...)` +Adds a component of type T to the entity. + +**Parameters**: Constructor arguments for the component +**Returns**: Reference to the created component +**Example**: +```cpp +auto& pos = entity.addComponent(100, 200); +``` + +#### `getComponent()` +Retrieves a component of type T. + +**Returns**: Reference to the component +**Throws**: Undefined behavior if component doesn't exist +**Example**: +```cpp +auto& pos = entity.getComponent(); +``` + +#### `hasComponent()` +Checks if entity has a component of type T. + +**Returns**: `bool` +**Example**: +```cpp +if (entity.hasComponent()) { ... } +``` + +#### `destroy()` +Marks entity as inactive (removed on next `Refresh()`). + +--- + +### ECSManager Methods + +#### `addEntity()` +Creates and registers a new entity. + +**Returns**: Reference to the new entity + +#### `Refresh()` +Removes all inactive entities from the manager. + +**Important**: Call this after destroying entities to free memory. + +--- + +## Usage Examples + +### Example 1: Basic Entity Creation + +```cpp +#include "ECS.h" + +doengine::ECSManager manager; + +// Create entity +doengine::Entity& player = manager.addEntity(); + +// Add components +auto& pos = player.addComponent(); +pos.setPosition({100, 100}); + +// Game loop +while (running) { + manager.Update(); + manager.Render(); + manager.Refresh(); +} +``` + +--- + +### Example 2: Creating Custom Components + +```cpp +// Velocity component +struct VelocityComponent : public doengine::Component +{ + float vx = 0.0f; + float vy = 0.0f; + + void init() override { + // Initialize velocity + } + + void Update() override { + // Apply physics + auto& pos = entity->getComponent(); + pos.point.x += static_cast(vx); + pos.point.y += static_cast(vy); + } +}; + +// Sprite component +struct SpriteComponent : public doengine::Component +{ + std::string texturePath; + int width, height; + + void init() override { + // Load texture + } + + void Render() override { + auto& pos = entity->getComponent(); + // Draw sprite at pos.X(), pos.Y() + } +}; +``` + +--- + +### Example 3: Game Object with Multiple Components + +```cpp +// Create player entity +doengine::Entity& player = manager.addEntity(); + +// Position +auto& pos = player.addComponent(); +pos.setPosition({400, 300}); + +// Velocity +auto& vel = player.addComponent(); +vel.vx = 5.0f; +vel.vy = 0.0f; + +// Sprite +auto& sprite = player.addComponent(); +sprite.texturePath = "assets/player.png"; +sprite.width = 64; +sprite.height = 64; + +// Input handling (outside ECS) +void handleInput() { + if (entity.hasComponent()) { + auto& vel = entity.getComponent(); + if (keyPressed(KEY_RIGHT)) vel.vx = 5.0f; + if (keyPressed(KEY_LEFT)) vel.vx = -5.0f; + } +} +``` + +--- + +### Example 4: Entity Destruction + +```cpp +// Mark enemy as dead +enemy.destroy(); + +// Clean up inactive entities +manager.Refresh(); // Enemy is now removed +``` + +--- + +### Example 5: System-like Pattern + +```cpp +// Physics System (updates all entities with position + velocity) +void PhysicsSystem(doengine::ECSManager& manager) +{ + for (auto& entity : manager.entities) { + if (entity->hasComponent() && + entity->hasComponent()) + { + auto& pos = entity->getComponent(); + auto& vel = entity->getComponent(); + + pos.point.x += static_cast(vel.vx); + pos.point.y += static_cast(vel.vy); + } + } +} + +// Call in game loop +PhysicsSystem(manager); +``` + +--- + +### Example 6: Collision Detection System + +```cpp +struct ColliderComponent : public doengine::Component +{ + int width, height; + bool isTrigger = false; + + bool intersects(const ColliderComponent& other) { + auto& pos1 = entity->getComponent(); + auto& pos2 = other.entity->getComponent(); + + return (pos1.X() < pos2.X() + other.width && + pos1.X() + width > pos2.X() && + pos1.Y() < pos2.Y() + other.height && + pos1.Y() + height > pos2.Y()); + } +}; + +// Collision System +void CollisionSystem(doengine::ECSManager& manager) +{ + for (size_t i = 0; i < manager.entities.size(); ++i) { + if (!manager.entities[i]->hasComponent()) + continue; + + auto& col1 = manager.entities[i]->getComponent(); + + for (size_t j = i + 1; j < manager.entities.size(); ++j) { + if (!manager.entities[j]->hasComponent()) + continue; + + auto& col2 = manager.entities[j]->getComponent(); + + if (col1.intersects(col2)) { + // Handle collision + OnCollision(manager.entities[i], manager.entities[j]); + } + } + } +} +``` + +--- + +## Improvements & Recommendations + +### 1. Fix Critical Bugs + +```cpp +// Fixed addComponent +template +T& addComponent(Targs&&... targs) +{ + T* c = new T(std::forward(targs)...); // FIX: forward + c->entity = this; + std::shared_ptr uPtr{c}; + components.emplace_back(uPtr); + + componentArray[getComponentTypeId()] = c; + componentBitset[getComponentTypeId()] = true; + + c->init(); + return *c; +} + +// Fixed getComponent +template +T& getComponent() const +{ + auto ptr = componentArray[getComponentTypeId()]; // FIX: syntax + return *static_cast(ptr); +} +``` + +--- + +### 2. Add Component Removal + +```cpp +template +void removeComponent() +{ + if (!hasComponent()) return; + + ComponentId id = getComponentTypeId(); + + // Remove from array + componentArray[id] = nullptr; + componentBitset[id] = false; + + // Remove from vector + components.erase( + std::remove_if(components.begin(), components.end(), + [id](const std::shared_ptr& c) { + return dynamic_cast(c.get()) != nullptr; + }), + components.end() + ); +} +``` + +--- + +### 3. Add Safety Checks + +```cpp +template +T& getComponent() const +{ + if (!hasComponent()) { + throw std::runtime_error("Component not found"); + } + auto ptr = componentArray[getComponentTypeId()]; + return *static_cast(ptr); +} +``` + +--- + +### 4. Use Delta Time + +```cpp +struct Entity +{ + void Update(float deltaTime) + { + for(auto& it : components) + it->Update(deltaTime); + } +}; + +struct Component +{ + virtual void Update(float deltaTime) {} +}; +``` + +--- + +### 5. Entity Pooling + +```cpp +struct ECSManager +{ + std::vector> entities; + std::vector> entityPool; + + Entity& addEntity() + { + std::shared_ptr p; + + if (!entityPool.empty()) { + p = std::move(entityPool.back()); + entityPool.pop_back(); + p->active = true; + } else { + p = std::make_shared(); + } + + entities.emplace_back(p); + return *p; + } + + void Refresh() + { + for (auto it = entities.begin(); it != entities.end();) { + if (!(*it)->isActive()) { + entityPool.push_back(*it); + it = entities.erase(it); + } else { + ++it; + } + } + } +}; +``` + +--- + +### 6. Component Groups (Systems Pattern) + +```cpp +template +std::vector getEntitiesWith() +{ + std::vector result; + + for (auto& entity : entities) { + if ((entity->hasComponent() && ...)) { + result.push_back(entity.get()); + } + } + + return result; +} + +// Usage: +auto movingEntities = manager.getEntitiesWith(); +``` + +--- + +### 7. Performance: Reserve Capacity + +```cpp +ECSManager() +{ + entities.reserve(1000); // Pre-allocate +} +``` + +--- + +### 8. Better Memory Management + +```cpp +// Use make_shared for better performance +Entity& addEntity() +{ + entities.emplace_back(std::make_shared()); + return *entities.back(); +} +``` + +--- + +## Performance Considerations + +| Operation | Complexity | Notes | +|-----------|-----------|-------| +| Add Component | O(1) | Direct array indexing | +| Get Component | O(1) | Array lookup by type ID | +| Has Component | O(1) | Bitset check | +| Remove Component | O(n) | Vector erase | +| Iterate Entities | O(n) | Linear scan | +| Refresh (cleanup) | O(n) | Erase-remove idiom | + +--- + +## Best Practices + +1. **Always call `Refresh()`** after destroying entities to prevent memory leaks +2. **Check `hasComponent()`** before `getComponent()` to avoid crashes +3. **Use composition** over deep inheritance hierarchies +4. **Keep components data-only** when possible +5. **Implement Systems** as free functions operating on component groups +6. **Profile before optimizing** - ECS is already quite fast for small-medium games + +--- + +## Limitations + +- Maximum 32 component types (can be increased by changing `maxComponents`) +- Not thread-safe +- No built-in serialization +- No component dependencies/requirements +- No event system + +--- + +## Conclusion + +This ECS implementation provides a solid foundation for a component-based game engine. With the suggested improvements, it would be production-ready for small to medium-sized games. For larger projects, consider libraries like EnTT or FLECS which offer more advanced features. + +--- + +## Additional Resources + +- [Game Programming Patterns - Component Pattern](http://gameprogrammingpatterns.com/component.html) +- [EnTT - Modern C++ ECS](https://github.com/skypjack/entt) +- [Data-Oriented Design](https://www.dataorienteddesign.com/dodbook/) + + +#### Sample Usage... + +```cpp + + +#include "EntityComponentSystem.h" +#include +#include + +using namespace doengine; + +// ============================================================================ +// EXAMPLE 1: Basic Entity Creation and Component Management +// ============================================================================ + +void example1_basic_usage() +{ + std::cout << "=== Example 1: Basic Usage ===" << std::endl; + + ECSManager manager; + + // Create a simple entity with position + Entity& player = manager.addEntity(); + auto& pos = player.addComponent(100, 200); + + std::cout << "Player position: (" << pos.X() << ", " << pos.Y() << ")" << std::endl; + + // Move the player + pos.move(50, 30); + std::cout << "After move: (" << pos.X() << ", " << pos.Y() << ")" << std::endl; + + // Check for components + if (player.hasComponent()) { + std::cout << "Player has PositionComponent" << std::endl; + } + + // Game loop simulation + for (int frame = 0; frame < 3; ++frame) { + manager.Update(0.016f); // ~60 FPS + manager.Render(); + } + + std::cout << std::endl; +} + + +// ============================================================================ +// EXAMPLE 2: Multiple Components - Moving Entity +// ============================================================================ + +void example2_moving_entity() +{ + std::cout << "=== Example 2: Moving Entity ===" << std::endl; + + ECSManager manager; + + // Create entity with position and velocity + Entity& bullet = manager.addEntity(); + bullet.addComponent(0, 0); + bullet.addComponent(100.0f, 50.0f); // pixels per second + + // Simulate 5 frames + float deltaTime = 0.016f; // 60 FPS + for (int i = 0; i < 5; ++i) { + manager.Update(deltaTime); + + auto& pos = bullet.getComponent(); + std::cout << "Frame " << i << ": Position (" + << pos.X() << ", " << pos.Y() << ")" << std::endl; + } + + std::cout << std::endl; +} + + +// ============================================================================ +// EXAMPLE 3: Complete Game Entity (Player) +// ============================================================================ + +void example3_complete_player() +{ + std::cout << "=== Example 3: Complete Player Entity ===" << std::endl; + + ECSManager manager; + + // Create player with all components + Entity& player = manager.addEntity(); + + auto& pos = player.addComponent(400, 300); + auto& vel = player.addComponent(0.0f, 0.0f); + auto& sprite = player.addComponent("assets/player.png", 64, 64); + auto& collider = player.addComponent(64, 64, "player"); + + std::cout << "Created player entity with:" << std::endl; + std::cout << " - Position: (" << pos.X() << ", " << pos.Y() << ")" << std::endl; + std::cout << " - Sprite: " << sprite.texturePath << std::endl; + std::cout << " - Collider: " << collider.width << "x" << collider.height << std::endl; + + // Simulate player input (move right) + vel.vx = 200.0f; // Move right at 200 px/s + + // Update a few frames + for (int i = 0; i < 3; ++i) { + manager.Update(0.016f); + auto& currentPos = player.getComponent(); + std::cout << "Frame " << i << ": Player at (" + << currentPos.X() << ", " << currentPos.Y() << ")" << std::endl; + } + + std::cout << std::endl; +} + + +// ============================================================================ +// EXAMPLE 4: Component Removal +// ============================================================================ + +void example4_component_removal() +{ + std::cout << "=== Example 4: Component Removal ===" << std::endl; + + ECSManager manager; + Entity& entity = manager.addEntity(); + + // Add components + entity.addComponent(10, 20); + entity.addComponent(5.0f, 5.0f); + + std::cout << "Has Position: " << entity.hasComponent() << std::endl; + std::cout << "Has Velocity: " << entity.hasComponent() << std::endl; + + // Remove velocity + entity.removeComponent(); + + std::cout << "After removing velocity:" << std::endl; + std::cout << "Has Position: " << entity.hasComponent() << std::endl; + std::cout << "Has Velocity: " << entity.hasComponent() << std::endl; + + std::cout << std::endl; +} + + +// ============================================================================ +// EXAMPLE 5: Entity Destruction and Cleanup +// ============================================================================ + +void example5_entity_lifecycle() +{ + std::cout << "=== Example 5: Entity Lifecycle ===" << std::endl; + + ECSManager manager; + + // Create multiple entities + for (int i = 0; i < 5; ++i) { + Entity& ent = manager.addEntity(); + ent.addComponent(i * 10, i * 10); + } + + std::cout << "Created " << manager.getEntityCount() << " entities" << std::endl; + + // Destroy some entities + manager.entities[1]->destroy(); + manager.entities[3]->destroy(); + + std::cout << "Marked 2 entities for destruction" << std::endl; + std::cout << "Before refresh: " << manager.getEntityCount() << " entities" << std::endl; + + // Clean up + manager.Refresh(); + + std::cout << "After refresh: " << manager.getEntityCount() << " entities" << std::endl; + + std::cout << std::endl; +} + + +// ============================================================================ +// EXAMPLE 6: Query Entities by Components (System Pattern) +// ============================================================================ + +void example6_entity_queries() +{ + std::cout << "=== Example 6: Entity Queries ===" << std::endl; + + ECSManager manager; + + // Create different types of entities + // Static objects (position only) + for (int i = 0; i < 3; ++i) { + Entity& wall = manager.addEntity(); + wall.addComponent(i * 100, 0); + } + + // Moving objects (position + velocity) + for (int i = 0; i < 2; ++i) { + Entity& enemy = manager.addEntity(); + enemy.addComponent(i * 50, 100); + enemy.addComponent(10.0f, 0.0f); + } + + std::cout << "Total entities: " << manager.getEntityCount() << std::endl; + + // Query entities with both position and velocity + auto movingEntities = manager.getEntitiesWith(); + std::cout << "Moving entities (Position + Velocity): " << movingEntities.size() << std::endl; + + // Process only moving entities + for (auto* entity : movingEntities) { + auto& pos = entity->getComponent(); + auto& vel = entity->getComponent(); + std::cout << " Moving entity at (" << pos.X() << ", " << pos.Y() + << ") with velocity (" << vel.vx << ", " << vel.vy << ")" << std::endl; + } + + std::cout << std::endl; +} + + +// ============================================================================ +// EXAMPLE 7: Collision Detection System +// ============================================================================ + +void example7_collision_system() +{ + std::cout << "=== Example 7: Collision Detection ===" << std::endl; + + ECSManager manager; + + // Create player + Entity& player = manager.addEntity(); + player.addComponent(100, 100); + player.addComponent(32, 32, "player"); + + // Create enemies + Entity& enemy1 = manager.addEntity(); + enemy1.addComponent(120, 120); // Overlapping + enemy1.addComponent(32, 32, "enemy"); + + Entity& enemy2 = manager.addEntity(); + enemy2.addComponent(300, 300); // Far away + enemy2.addComponent(32, 32, "enemy"); + + // Check collisions + auto collidables = manager.getEntitiesWith(); + + std::cout << "Checking collisions for " << collidables.size() << " entities" << std::endl; + + for (size_t i = 0; i < collidables.size(); ++i) { + auto& col1 = collidables[i]->getComponent(); + + for (size_t j = i + 1; j < collidables.size(); ++j) { + auto& col2 = collidables[j]->getComponent(); + + if (col1.intersects(col2)) { + std::cout << "COLLISION: " << col1.tag << " hit " << col2.tag << std::endl; + } + } + } + + std::cout << std::endl; +} + + +// ============================================================================ +// EXAMPLE 8: Custom Physics System +// ============================================================================ + +// Custom component for gravity +struct GravityComponent : public Component +{ + float gravity = 9.8f; // pixels per second squared + + void Update(float deltaTime) override + { + if (entity->hasComponent()) { + auto& vel = entity->getComponent(); + vel.vy += gravity * deltaTime; + } + } +}; + +void example8_physics_system() +{ + std::cout << "=== Example 8: Physics System ===" << std::endl; + + ECSManager manager; + + // Create falling object + Entity& ball = manager.addEntity(); + ball.addComponent(100, 0); + ball.addComponent(0.0f, 0.0f); + ball.addComponent(); + + std::cout << "Simulating falling ball:" << std::endl; + + // Simulate 5 frames + for (int i = 0; i < 5; ++i) { + manager.Update(0.1f); // 10 FPS for visible effect + + auto& pos = ball.getComponent(); + auto& vel = ball.getComponent(); + + std::cout << "Frame " << i << ": Y=" << pos.Y() + << ", Velocity=" << vel.vy << std::endl; + } + + std::cout << std::endl; +} + + +// ============================================================================ +// EXAMPLE 9: Entity Pooling Performance +// ============================================================================ + +void example9_entity_pooling() +{ + std::cout << "=== Example 9: Entity Pooling ===" << std::endl; + + ECSManager manager; + + // Create and destroy entities multiple times + std::cout << "Creating 100 entities..." << std::endl; + for (int i = 0; i < 100; ++i) { + Entity& ent = manager.addEntity(); + ent.addComponent(i, i); + } + std::cout << "Active entities: " << manager.getEntityCount() << std::endl; + + // Destroy half + std::cout << "Destroying 50 entities..." << std::endl; + for (size_t i = 0; i < 50; ++i) { + manager.entities[i]->destroy(); + } + manager.Refresh(); + std::cout << "Active entities after refresh: " << manager.getEntityCount() << std::endl; + + // Create new entities (will reuse from pool) + std::cout << "Creating 50 new entities (from pool)..." << std::endl; + for (int i = 0; i < 50; ++i) { + Entity& ent = manager.addEntity(); + ent.addComponent(i * 2, i * 2); + } + std::cout << "Active entities: " << manager.getEntityCount() << std::endl; + + std::cout << std::endl; +} + + +// ============================================================================ +// EXAMPLE 10: Complete Mini-Game +// ============================================================================ + +struct HealthComponent : public Component +{ + int health = 100; + int maxHealth = 100; + + void takeDamage(int amount) { + health -= amount; + if (health <= 0) { + health = 0; + entity->destroy(); + } + } +}; + +void example10_mini_game() +{ + std::cout << "=== Example 10: Mini Shooter Game ===" << std::endl; + + ECSManager manager; + + // Create player + Entity& player = manager.addEntity(); + player.addComponent(400, 500); + player.addComponent(0.0f, 0.0f); + player.addComponent("player.png", 32, 32); + player.addComponent(32, 32, "player"); + player.addComponent(); + + // Create enemies + for (int i = 0; i < 3; ++i) { + Entity& enemy = manager.addEntity(); + enemy.addComponent(100 + i * 150, 100); + enemy.addComponent(0.0f, 50.0f); // Move down + enemy.addComponent("enemy.png", 32, 32); + enemy.addComponent(32, 32, "enemy"); + auto& health = enemy.addComponent(); + health.health = 50; + } + + std::cout << "Game initialized with:" << std::endl; + std::cout << " - 1 Player" << std::endl; + std::cout << " - 3 Enemies" << std::endl; + + // Simulate game loop + for (int frame = 0; frame < 10; ++frame) { + float deltaTime = 0.016f; + + // Update all entities + manager.Update(deltaTime); + + // Check collisions + auto collidables = manager.getEntitiesWith(); + for (size_t i = 0; i < collidables.size(); ++i) { + for (size_t j = i + 1; j < collidables.size(); ++j) { + auto& col1 = collidables[i]->getComponent(); + auto& col2 = collidables[j]->getComponent(); + + if (col1.intersects(col2)) { + // Handle collision + if (col1.tag == "player" && col2.tag == "enemy") { + std::cout << "Frame " << frame << ": Player hit by enemy!" << std::endl; + + if (collidables[i]->hasComponent()) { + auto& health = collidables[i]->getComponent(); + health.takeDamage(10); + std::cout << " Player health: " << health.health << std::endl; + } + } + } + } + } + + // Clean up destroyed entities + manager.Refresh(); + } + + std::cout << "Game ended. Remaining entities: " << manager.getEntityCount() << std::endl; + std::cout << std::endl; +} + + +// ============================================================================ +// MAIN +// ============================================================================ + +int main() +{ + std::cout << "ECS System - Usage Examples" << std::endl; + std::cout << "======================================" << std::endl << std::endl; + + try { + example1_basic_usage(); + example2_moving_entity(); + example3_complete_player(); + example4_component_removal(); + example5_entity_lifecycle(); + example6_entity_queries(); + example7_collision_system(); + example8_physics_system(); + example9_entity_pooling(); + example10_mini_game(); + + std::cout << "All examples completed successfully!" << std::endl; + } + catch (const std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; + return 1; + } + + return 0; +} +``` \ No newline at end of file diff --git a/includes/AnimateGameObject.h b/includes/AnimateGameObject.h new file mode 100644 index 0000000..4ea8722 --- /dev/null +++ b/includes/AnimateGameObject.h @@ -0,0 +1,40 @@ +#ifndef ANIMATED_GAMEOBJECT_H_DEFINED +#define ANIMATED_GAMEOBJECT_H_DEFINED +#include "GameObject.h" +#include "Timer.h" +namespace doengine +{ + +class AnimateGameObject : public GameObject +{ + + public: + + struct Animation + { + Timer timer; + int frameCount; + + int getCurrentFrame() + { + return static_cast(timer.getTime() / timer.getLength() * frameCount); + } + + void step(float fl) + { + timer.Update(fl); + } + }; + + AnimateGameObject(); + virtual void Update(float elapsed) = 0; + virtual void Render() = 0; + void step(float elapsed); /// call it manually on Update...todo Put automatica on merge it GameState. +}; + + + + +}; + +#endif \ No newline at end of file diff --git a/includes/Application.h b/includes/Application.h index 730be6b..b777aa3 100644 --- a/includes/Application.h +++ b/includes/Application.h @@ -8,7 +8,7 @@ namespace doengine { - +class GameState; class FpsManager; class GameStateManager; class WindowManager; @@ -60,56 +60,18 @@ class Application void Update(); void Render(); void Quit(); - - long getElapsedTime() - { - return fps_handler->getElapsedTime(); - } - - uint32_t getDeltaTime() - { - return fps_handler->getDeltaTime(); - } - - void setW(int w) - { - window_rect.w = w; - _internalResize(); - } - void setH(int h) - { - window_rect.h = h; - _internalResize(); - } - void setSize(int w, int h) - { - window_rect.w = w; - window_rect.h = h; - _internalResize(); - } - int getH() - { - return window_rect.h; - } - int getW() - { - return window_rect.w; - } - + long getElapsedTime(); + uint32_t getDeltaTime(); + void setW(int w); + void setH(int h); + void setSize(int w, int h); + int getH(); + int getW(); + Rect getDisplayMode(int m=0); void SetWindowPencilColor(const Color& color); - void clearScreen(const Color& color); - - void addState(GameState* state, int id) - { - gsm->AddState(id, state); - } - - void setState(int id) - { - gsm->SetState(id); - } - + void addState(GameState* state, int id); + void setState(int id); private: void destroy(); }; diff --git a/includes/Camera.h b/includes/Camera.h index 3341bb9..f4010bc 100644 --- a/includes/Camera.h +++ b/includes/Camera.h @@ -1,25 +1,240 @@ #pragma once - +#include #include "Camera.h" +#include "Geometric.h" namespace doengine { + class Camera { - // Vector camera_pos; - - public: - enum class CameraMovement : unsigned char +public: + enum class CameraMovement { CameraUp, CameraDown, CameraLeft, - CameraRight, - CameraNotMovement + CameraRight }; - virtual Camera* Update(CameraMovement where) = 0; +private: + Point position; // Camera position (usually negative for scrolling) + Point targetPosition; // Target position for smooth following + int viewportWidth; // Screen width + int viewportHeight; // Screen height + int tileSize; // Size of each tile + + // Bounds + bool boundsEnabled; + int minX, minY; // Minimum camera position (usually negative) + int maxX, maxY; // Maximum camera position (usually 0) + + // Smoothing + float smoothingFactor; // Interpolation factor (0.0 - 1.0) + bool smoothingEnabled; + + // Cached values for optimization + int rowOffset; + int colOffset; + int rowsToRender; + int colsToRender; + +public: + Camera(int viewportW, int viewportH, int tileSize, float smoothing = 0.1f) + : position(0, 0), + targetPosition(0, 0), + viewportWidth(viewportW), + viewportHeight(viewportH), + tileSize(tileSize), + boundsEnabled(false), + minX(0), minY(0), + maxX(0), maxY(0), + smoothingFactor(smoothing), + smoothingEnabled(true), + rowOffset(0), + colOffset(0), + rowsToRender(0), + colsToRender(0) + { + updateCachedValues(); + } + + void update(float deltaTime) + { + if (smoothingEnabled) + { + // Smooth interpolation towards target + float factor = std::min(1.0f, smoothingFactor * deltaTime * 60.0f); // Normalize for 60 FPS + position.x += (int)((targetPosition.x - position.x) * factor); + position.y += (int)((targetPosition.y - position.y) * factor); + } + else + { + // Instant movement + position = targetPosition; + } + + // Apply bounds + if (boundsEnabled) + { + applyBounds(); + } + + // Update cached values + updateCachedValues(); + } + + void centerOn(const Point& worldPos) + { + // Center camera on a world position + targetPosition.x = -(worldPos.x - viewportWidth / 2); + targetPosition.y = -(worldPos.y - viewportHeight / 2); + + if (boundsEnabled) + { + targetPosition.x = std::max(minX, std::min(maxX, targetPosition.x)); + targetPosition.y = std::max(minY, std::min(maxY, targetPosition.y)); + } + } + + void centerOn(int worldX, int worldY) + { + centerOn(Point(worldX, worldY)); + } + + void moveCamera(CameraMovement direction, int speed) + { + switch (direction) + { + case CameraMovement::CameraUp: + targetPosition.y += speed; + break; + case CameraMovement::CameraDown: + targetPosition.y -= speed; + break; + case CameraMovement::CameraLeft: + targetPosition.x += speed; + break; + case CameraMovement::CameraRight: + targetPosition.x -= speed; + break; + } + + if (boundsEnabled) + { + targetPosition.x = std::max(minX, std::min(maxX, targetPosition.x)); + targetPosition.y = std::max(minY, std::min(maxY, targetPosition.y)); + } + } + + void setPosition(const Point& pos) + { + position = pos; + targetPosition = pos; + + if (boundsEnabled) + { + applyBounds(); + } + + updateCachedValues(); + } + + void setPosition(int x, int y) + { + setPosition(Point(x, y)); + } + + Point getPosition() const + { + return position; + } + + void setBounds(int minX, int minY, int maxX, int maxY) + { + this->minX = minX; + this->minY = minY; + this->maxX = maxX; + this->maxY = maxY; + } + + void enableBounds(bool enabled) + { + boundsEnabled = enabled; + } + + void setSmoothing(float factor) + { + smoothingFactor = std::max(0.0f, std::min(1.0f, factor)); + } + + void enableSmoothing(bool enabled) + { + smoothingEnabled = enabled; + } + + // Get visible tile range + int getRowOffset() const { return rowOffset; } + int getColOffset() const { return colOffset; } + int getRowsToRender() const { return rowsToRender; } + int getColsToRender() const { return colsToRender; } + + // Viewport dimensions + int getViewportWidth() const { return viewportWidth; } + int getViewportHeight() const { return viewportHeight; } + + // Coordinate conversions + Point worldToScreen(int worldX, int worldY) const + { + return Point(worldX + position.x, worldY + position.y); + } + + Point screenToWorld(int screenX, int screenY) const + { + return Point(screenX - position.x, screenY - position.y); + } + + // Check if a world rectangle is visible + bool isVisible(int worldX, int worldY, int width, int height) const + { + Point screenPos = worldToScreen(worldX, worldY); + return !(screenPos.x + width < 0 || + screenPos.x > viewportWidth || + screenPos.y + height < 0 || + screenPos.y > viewportHeight); + } + + // Check if a tile is visible + bool isTileVisible(int tileRow, int tileCol) const + { + return tileCol >= colOffset && + tileCol < colOffset + colsToRender && + tileRow >= rowOffset && + tileRow < rowOffset + rowsToRender; + } + +private: + void applyBounds() + { + position.x = std::max(minX, std::min(maxX, position.x)); + position.y = std::max(minY, std::min(maxY, position.y)); + targetPosition.x = std::max(minX, std::min(maxX, targetPosition.x)); + targetPosition.y = std::max(minY, std::min(maxY, targetPosition.y)); + } + + void updateCachedValues() + { + // Calculate which tiles are visible + colOffset = std::max(0, -position.x / tileSize); + rowOffset = std::max(0, -position.y / tileSize); + + // Add 2 extra tiles for safety (partial tiles at edges) + colsToRender = (viewportWidth / tileSize) + 2; + rowsToRender = (viewportHeight / tileSize) + 2; + } }; + + } // namespace doengine diff --git a/includes/Color.h b/includes/Color.h index 03bbb38..66da962 100644 --- a/includes/Color.h +++ b/includes/Color.h @@ -19,21 +19,27 @@ struct Color ColorT a; }; -static const Color red(255, 0, 0, 255); -static const Color white(255, 255, 255, 255); -static const Color green(10, 255, 0, 255); -static const Color blue(0, 0, 255, 255); -static const Color yellow(243, 216, 64, 255); -static const Color yellow1(100, 155, 0, 255); -static const Color purple(0, 255, 244, 255); -static const Color black(0, 0, 0, 255); -static const Color gold(255, 215, 0, 255); -static const Color gray(100, 100, 100, 255); -static const Color blue5(19, 26, 50, 255); -static const Color orange(226,116,17,255); -static const Color cyan(21,204,209,255); -static const Color skyBlue(77, 216, 255, 255); - +namespace Colors{ + static const Color red(255, 0, 0, 255); + static const Color white(255, 255, 255, 255); + static const Color green(10, 255, 0, 255); + static const Color blue(0, 0, 255, 255); + static const Color yellow(243, 216, 64, 255); + static const Color yellow1(100, 155, 0, 255); + static const Color purple(0, 255, 244, 255); + static const Color purple1(166, 0, 247, 255); + static const Color black(0, 0, 0, 255); + static const Color black1(0x43, 0x43, 0x43, 255); + static const Color black2(0x60, 0x60, 0x60, 255); + static const Color black3(0x90, 0x90, 0x90, 255); + static const Color gold(255, 215, 0, 255); + static const Color gray(100, 100, 100, 255); + static const Color darkGrey(26, 31, 40, 255); + static const Color blue5(19, 26, 50, 255); + static const Color orange(226,116,17,255); + static const Color cyan(21,204,209,255); + static const Color skyBlue(77, 216, 255, 255); +} template D toColor(const Color& o){ diff --git a/includes/CustomContainer.h b/includes/CustomContainer.h new file mode 100644 index 0000000..0fd86e6 --- /dev/null +++ b/includes/CustomContainer.h @@ -0,0 +1,24 @@ +#ifndef CUSTOM_CONTAINER_H_DEFINED +#define CUSTOM_CONTAINER_H_DEFINED +#include +namespace doengine +{ + +template +class VectorBag +{ + + public: + typedef const T traits_type; + + + +}; + + + +}; + + + +#endif CUSTOM_CONTAINER_H_DEFINED \ No newline at end of file diff --git a/includes/DOEngine_SDL_includes.h b/includes/DOEngine_SDL_includes.h index 865c113..5e5225a 100644 --- a/includes/DOEngine_SDL_includes.h +++ b/includes/DOEngine_SDL_includes.h @@ -1,16 +1,33 @@ #ifndef DOENGINE_SDL_INCLUDES_HPP_DEFINED #define DOENGINE_SDL_INCLUDES_HPP_DEFINED -extern "C" -{ +//#define FETCHED_CONTENT 0 +//#warning "Using fetched content" + +#ifndef FETCHED_CONTENT + #include #include #include #include #include +#include +#include +#include +#include +#include +#include +#else + +#include +#include +#include +#include +#include #include #include #include -} +#include +#endif #endif diff --git a/includes/Draggable.h b/includes/Draggable.h new file mode 100644 index 0000000..81e19b4 --- /dev/null +++ b/includes/Draggable.h @@ -0,0 +1,17 @@ +#ifndef DRAGGABLE_H_DEFINED +#define DRAGGABLE_H_DEFINED +#include "Renderer.h" +#include "Geometric.h" + +namespace doengine{ + +struct Draggable{ + virtual ~Draggable(); + Draggable(); + virtual void startDraggingPoint(const doengine::Rect& start)= 0; + virtual void updateCoords(const doengine::Rect& rect) = 0; + virtual void Render() = 0; +}; + +} +#endif diff --git a/includes/DraggableGameObject.h b/includes/DraggableGameObject.h new file mode 100644 index 0000000..401f29a --- /dev/null +++ b/includes/DraggableGameObject.h @@ -0,0 +1,77 @@ +#ifndef DRAGGABLE_GAMEOBJECT_H_DEFINED +#define DRAGGABLE_GAMEOBJECT_H_DEFINED +#include +#include "Draggable.h" +#include "Geometric.h" +#include "Renderer.h" +#include "Event.h" +#include +#include +namespace doengine +{ + + +class DraggableObject : + public Draggable, + public MouseEvent +{ + + private: + std::vector> onSelectedHappened; + std::vector> onSelectedStopped; + std::vector> onMovementHappened; + std::vector> onMovementStopped; + + protected: + virtual void startDraggingPoint(const doengine::Rect& start); + virtual void updateCoords(const doengine::Rect& rect); + virtual void render(doengine::Renderer *render); + virtual void MouseMove(const Mouse&); + virtual void MouseButtonDown(const Mouse&); + virtual void MouseButtonUp(const Mouse&); + virtual void MouseWheel(const Mouse&){} + void setSelected(bool selected); + + Rect offset; + bool selected; + + + public: + + + void registerOnSelectedHappenedCallback(std::function fn); + void registerOnSelectedStoppedCallback(std::function fn); + void registerOnMovementHappenedCallback(std::function fn); + void registerOnMovementStoppedCallback(std::function fn); + + Rect getDragablePosition(); + void setObjectDimensionAndPosition(const doengine::Rect rect); + virtual ~DraggableObject() = default; + virtual void Update(float elapse) = 0; + virtual void Render() = 0; +}; + +class DraggableGameObject : + public Draggable +{ + bool selected; + int sprite; + int sprite_id; + int sprite_offset; + doengine::Rect offset; + std::unordered_map spriteOffsets; + public: + virtual ~DraggableGameObject(); + DraggableGameObject(); + DraggableGameObject(int x, int y); + DraggableGameObject(int x, int y, int spriteId, int spOffset); + void setSpriteOffset(std::unordered_map sprites); + void setSelectedIfCollided(const doengine::Rect& rect); + void setSelected(bool selected); + virtual void startDraggingPoint(const doengine::Rect& start); + virtual void updateCoords(const doengine::Rect& o); + virtual void render(doengine::Renderer *render); + +}; +} +#endif diff --git a/includes/EntityComponentSystem.h b/includes/EntityComponentSystem.h new file mode 100644 index 0000000..27da553 --- /dev/null +++ b/includes/EntityComponentSystem.h @@ -0,0 +1,189 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +/// Test Components.... +#include "Geometric.h" + +namespace doengine +{ + +using ComponentId = int; +class Component; +class Entity; + +inline ComponentId getNextComponentId() +{ + static ComponentId lastId = 0; + return ++lastId; +} + +template inline ComponentId getComponentTypeId() noexcept +{ + static ComponentId typeID = getNextComponentId(); + return typeID; +} + +constexpr int maxComponents = 32; + +using ComponentBitset = std::bitset; +using ComponentArray = std::array; + + +struct Component +{ + public: + Entity *entity; + + virtual void init(){} + virtual void Update(){} + virtual void Render(){} + virtual ~Component(){} +}; + +struct Entity +{ + + bool active = true; + + std::vector> components; + ComponentArray componentArray; + ComponentBitset componentBitset; + + bool isActive() + { + return active; + } + + void Update(float elapsed = 0) + { + for(auto it : components) it->Update(); + } + + void Render() + { + for(auto it : components) it->Render(); + } + + void destroy(){active = false;} + + template bool hasComponent() const + { + return componentArray[getComponentTypeId]; + } + + template + T& addComponent(Targs&&... targs) + { + T* c(new T(std::forward(targs)...)); + c->entity = this; + std::shared_ptr uPtr{c}; + components.emplace_back(uPtr); + + componentArray[getComponentTypeId()] = c; + componentBitset[getComponentTypeId()] = true; + + c->init(); + + return *c; + } + + template T& getComponent()const + { + auto ptr = componentArray[getComponentTypeId()]; + return *static_cast(ptr); + } +}; + + +struct ECSManager +{ + std::vector> entities; + + + static ECSManager* getECSManager() + { + static ECSManager* instance =nullptr; + if(instance == nullptr) + instance = new ECSManager(); + return instance; + } + + void Update() + { + for(auto it : entities) + { + it->Update(); + } + } + + void Render() + { + for(auto it : entities) + { + it->Render(); + } + } + + void Refresh() + { + entities.erase( + std::remove_if( + entities.begin(), + entities.end(), + [](const std::shared_ptr &ent){ + return !ent->isActive(); + }) + ); + } + + Entity* addEntity() + { + Entity* ent = new Entity(); + std::shared_ptr p{ent}; + entities.emplace_back(std::move(p)); + return ent; + } +}; + + + +struct PositionComponent : public Component +{ + Point point; + + int X(){return point.x;} + int Y(){return point.y;} + + Point getPosition() + { + return point; + } + void setPosition(Point p) + { + point.x = p.x; + point.y = p.y; + } + virtual void init() override{} + virtual void Update() override{} + virtual void Render() override{} +}; + + + + + + + + + + + + + +} \ No newline at end of file diff --git a/includes/Event.h b/includes/Event.h index 7ce5df4..8eef04b 100644 --- a/includes/Event.h +++ b/includes/Event.h @@ -14,10 +14,13 @@ struct Event static std::unordered_map keys_pressed; static std::vector keydown; static std::vector keyup; + static std::vector TextInputList; static std::vector mouseEvent; static std::vector joyButtonUpList; static std::vector joyButtonDownList; static std::vector joyButtonTriggerList; + static std::vector keyboardHandlingEventList; + static std::map joypadsConnected; static int getMousePosition(int* x, int* y); @@ -28,6 +31,7 @@ struct Event static void PollEvent(); static void AddKeyPressEventListener(KeyUpEvent* ev); static void AddKeyPressEventListener(KeyDownEvent* ev); + static void AddKeyboardEvent(KeyboardInputhandlingEvent* ev); static void RemoveKeyPressEventListener(KeyUpEvent* ev); static void RemoveKeyPressEventListener(KeyDownEvent* ev); static void AddMouseEvent(MouseEvent* event); @@ -38,5 +42,415 @@ struct Event static void RemoveJoypadEventListener(JoyButtonUpEvent* ev); static void RemoveJoypadEventListener(JoyButtonDownEvent* ev); static void RemoveJoypadEventListener(JoyButtonTriggerEvent* ev); + static void RemoveKeyboardEvent(KeyboardInputhandlingEvent* ev); + static void AddTextInputEvent(TextInputEvent *event); + static void RemoveTextInputEvent(TextInputEvent *event); }; + + +///Wrapper for Scancode +typedef enum Scancode +{ + SCANCODE_UNKNOWN = 0, + + /** + * \name Usage page 0x07 + * + * These values are from usage page 0x07 (USB keyboard page). + */ + /* @{ */ + + SCANCODE_A = 4, + SCANCODE_B = 5, + SCANCODE_C = 6, + SCANCODE_D = 7, + SCANCODE_E = 8, + SCANCODE_F = 9, + SCANCODE_G = 10, + SCANCODE_H = 11, + SCANCODE_I = 12, + SCANCODE_J = 13, + SCANCODE_K = 14, + SCANCODE_L = 15, + SCANCODE_M = 16, + SCANCODE_N = 17, + SCANCODE_O = 18, + SCANCODE_P = 19, + SCANCODE_Q = 20, + SCANCODE_R = 21, + SCANCODE_S = 22, + SCANCODE_T = 23, + SCANCODE_U = 24, + SCANCODE_V = 25, + SCANCODE_W = 26, + SCANCODE_X = 27, + SCANCODE_Y = 28, + SCANCODE_Z = 29, + + SCANCODE_1 = 30, + SCANCODE_2 = 31, + SCANCODE_3 = 32, + SCANCODE_4 = 33, + SCANCODE_5 = 34, + SCANCODE_6 = 35, + SCANCODE_7 = 36, + SCANCODE_8 = 37, + SCANCODE_9 = 38, + SCANCODE_0 = 39, + + SCANCODE_RETURN = 40, + SCANCODE_ESCAPE = 41, + SCANCODE_BACKSPACE = 42, + SCANCODE_TAB = 43, + SCANCODE_SPACE = 44, + + SCANCODE_MINUS = 45, + SCANCODE_EQUALS = 46, + SCANCODE_LEFTBRACKET = 47, + SCANCODE_RIGHTBRACKET = 48, + SCANCODE_BACKSLASH = 49, /**< Located at the lower left of the return + * key on ISO keyboards and at the right end + * of the QWERTY row on ANSI keyboards. + * Produces REVERSE SOLIDUS (backslash) and + * VERTICAL LINE in a US layout, REVERSE + * SOLIDUS and VERTICAL LINE in a UK Mac + * layout, NUMBER SIGN and TILDE in a UK + * Windows layout, DOLLAR SIGN and POUND SIGN + * in a Swiss German layout, NUMBER SIGN and + * APOSTROPHE in a German layout, GRAVE + * ACCENT and POUND SIGN in a French Mac + * layout, and ASTERISK and MICRO SIGN in a + * French Windows layout. + */ + SCANCODE_NONUSHASH = 50, /**< ISO USB keyboards actually use this code + * instead of 49 for the same key, but all + * OSes I've seen treat the two codes + * identically. So, as an implementor, unless + * your keyboard generates both of those + * codes and your OS treats them differently, + * you should generate SCANCODE_BACKSLASH + * instead of this code. As a user, you + * should not rely on this code because SDL + * will never generate it with most (all?) + * keyboards. + */ + SCANCODE_SEMICOLON = 51, + SCANCODE_APOSTROPHE = 52, + SCANCODE_GRAVE = 53, /**< Located in the top left corner (on both ANSI + * and ISO keyboards). Produces GRAVE ACCENT and + * TILDE in a US Windows layout and in US and UK + * Mac layouts on ANSI keyboards, GRAVE ACCENT + * and NOT SIGN in a UK Windows layout, SECTION + * SIGN and PLUS-MINUS SIGN in US and UK Mac + * layouts on ISO keyboards, SECTION SIGN and + * DEGREE SIGN in a Swiss German layout (Mac: + * only on ISO keyboards), CIRCUMFLEX ACCENT and + * DEGREE SIGN in a German layout (Mac: only on + * ISO keyboards), SUPERSCRIPT TWO and TILDE in a + * French Windows layout, COMMERCIAL AT and + * NUMBER SIGN in a French Mac layout on ISO + * keyboards, and LESS-THAN SIGN and GREATER-THAN + * SIGN in a Swiss German, German, or French Mac + * layout on ANSI keyboards. + */ + SCANCODE_COMMA = 54, + SCANCODE_PERIOD = 55, + SCANCODE_SLASH = 56, + + SCANCODE_CAPSLOCK = 57, + + SCANCODE_F1 = 58, + SCANCODE_F2 = 59, + SCANCODE_F3 = 60, + SCANCODE_F4 = 61, + SCANCODE_F5 = 62, + SCANCODE_F6 = 63, + SCANCODE_F7 = 64, + SCANCODE_F8 = 65, + SCANCODE_F9 = 66, + SCANCODE_F10 = 67, + SCANCODE_F11 = 68, + SCANCODE_F12 = 69, + + SCANCODE_PRINTSCREEN = 70, + SCANCODE_SCROLLLOCK = 71, + SCANCODE_PAUSE = 72, + SCANCODE_INSERT = 73, /**< insert on PC, help on some Mac keyboards (but + does send code 73, not 117) */ + SCANCODE_HOME = 74, + SCANCODE_PAGEUP = 75, + SCANCODE_DELETE = 76, + SCANCODE_END = 77, + SCANCODE_PAGEDOWN = 78, + SCANCODE_RIGHT = 79, + SCANCODE_LEFT = 80, + SCANCODE_DOWN = 81, + SCANCODE_UP = 82, + + SCANCODE_NUMLOCKCLEAR = 83, /**< num lock on PC, clear on Mac keyboards + */ + SCANCODE_KP_DIVIDE = 84, + SCANCODE_KP_MULTIPLY = 85, + SCANCODE_KP_MINUS = 86, + SCANCODE_KP_PLUS = 87, + SCANCODE_KP_ENTER = 88, + SCANCODE_KP_1 = 89, + SCANCODE_KP_2 = 90, + SCANCODE_KP_3 = 91, + SCANCODE_KP_4 = 92, + SCANCODE_KP_5 = 93, + SCANCODE_KP_6 = 94, + SCANCODE_KP_7 = 95, + SCANCODE_KP_8 = 96, + SCANCODE_KP_9 = 97, + SCANCODE_KP_0 = 98, + SCANCODE_KP_PERIOD = 99, + + SCANCODE_NONUSBACKSLASH = 100, /**< This is the additional key that ISO + * keyboards have over ANSI ones, + * located between left shift and Y. + * Produces GRAVE ACCENT and TILDE in a + * US or UK Mac layout, REVERSE SOLIDUS + * (backslash) and VERTICAL LINE in a + * US or UK Windows layout, and + * LESS-THAN SIGN and GREATER-THAN SIGN + * in a Swiss German, German, or French + * layout. */ + SCANCODE_APPLICATION = 101, /**< windows contextual menu, compose */ + SCANCODE_POWER = 102, /**< The USB document says this is a status flag, + * not a physical key - but some Mac keyboards + * do have a power key. */ + SCANCODE_KP_EQUALS = 103, + SCANCODE_F13 = 104, + SCANCODE_F14 = 105, + SCANCODE_F15 = 106, + SCANCODE_F16 = 107, + SCANCODE_F17 = 108, + SCANCODE_F18 = 109, + SCANCODE_F19 = 110, + SCANCODE_F20 = 111, + SCANCODE_F21 = 112, + SCANCODE_F22 = 113, + SCANCODE_F23 = 114, + SCANCODE_F24 = 115, + SCANCODE_EXECUTE = 116, + SCANCODE_HELP = 117, /**< AL Integrated Help Center */ + SCANCODE_MENU = 118, /**< Menu (show menu) */ + SCANCODE_SELECT = 119, + SCANCODE_STOP = 120, /**< AC Stop */ + SCANCODE_AGAIN = 121, /**< AC Redo/Repeat */ + SCANCODE_UNDO = 122, /**< AC Undo */ + SCANCODE_CUT = 123, /**< AC Cut */ + SCANCODE_COPY = 124, /**< AC Copy */ + SCANCODE_PASTE = 125, /**< AC Paste */ + SCANCODE_FIND = 126, /**< AC Find */ + SCANCODE_MUTE = 127, + SCANCODE_VOLUMEUP = 128, + SCANCODE_VOLUMEDOWN = 129, +/* not sure whether there's a reason to enable these */ +/* SCANCODE_LOCKINGCAPSLOCK = 130, */ +/* SCANCODE_LOCKINGNUMLOCK = 131, */ +/* SCANCODE_LOCKINGSCROLLLOCK = 132, */ + SCANCODE_KP_COMMA = 133, + SCANCODE_KP_EQUALSAS400 = 134, + + SCANCODE_INTERNATIONAL1 = 135, /**< used on Asian keyboards, see + footnotes in USB doc */ + SCANCODE_INTERNATIONAL2 = 136, + SCANCODE_INTERNATIONAL3 = 137, /**< Yen */ + SCANCODE_INTERNATIONAL4 = 138, + SCANCODE_INTERNATIONAL5 = 139, + SCANCODE_INTERNATIONAL6 = 140, + SCANCODE_INTERNATIONAL7 = 141, + SCANCODE_INTERNATIONAL8 = 142, + SCANCODE_INTERNATIONAL9 = 143, + SCANCODE_LANG1 = 144, /**< Hangul/English toggle */ + SCANCODE_LANG2 = 145, /**< Hanja conversion */ + SCANCODE_LANG3 = 146, /**< Katakana */ + SCANCODE_LANG4 = 147, /**< Hiragana */ + SCANCODE_LANG5 = 148, /**< Zenkaku/Hankaku */ + SCANCODE_LANG6 = 149, /**< reserved */ + SCANCODE_LANG7 = 150, /**< reserved */ + SCANCODE_LANG8 = 151, /**< reserved */ + SCANCODE_LANG9 = 152, /**< reserved */ + + SCANCODE_ALTERASE = 153, /**< Erase-Eaze */ + SCANCODE_SYSREQ = 154, + SCANCODE_CANCEL = 155, /**< AC Cancel */ + SCANCODE_CLEAR = 156, + SCANCODE_PRIOR = 157, + SCANCODE_RETURN2 = 158, + SCANCODE_SEPARATOR = 159, + SCANCODE_OUT = 160, + SCANCODE_OPER = 161, + SCANCODE_CLEARAGAIN = 162, + SCANCODE_CRSEL = 163, + SCANCODE_EXSEL = 164, + + SCANCODE_KP_00 = 176, + SCANCODE_KP_000 = 177, + SCANCODE_THOUSANDSSEPARATOR = 178, + SCANCODE_DECIMALSEPARATOR = 179, + SCANCODE_CURRENCYUNIT = 180, + SCANCODE_CURRENCYSUBUNIT = 181, + SCANCODE_KP_LEFTPAREN = 182, + SCANCODE_KP_RIGHTPAREN = 183, + SCANCODE_KP_LEFTBRACE = 184, + SCANCODE_KP_RIGHTBRACE = 185, + SCANCODE_KP_TAB = 186, + SCANCODE_KP_BACKSPACE = 187, + SCANCODE_KP_A = 188, + SCANCODE_KP_B = 189, + SCANCODE_KP_C = 190, + SCANCODE_KP_D = 191, + SCANCODE_KP_E = 192, + SCANCODE_KP_F = 193, + SCANCODE_KP_XOR = 194, + SCANCODE_KP_POWER = 195, + SCANCODE_KP_PERCENT = 196, + SCANCODE_KP_LESS = 197, + SCANCODE_KP_GREATER = 198, + SCANCODE_KP_AMPERSAND = 199, + SCANCODE_KP_DBLAMPERSAND = 200, + SCANCODE_KP_VERTICALBAR = 201, + SCANCODE_KP_DBLVERTICALBAR = 202, + SCANCODE_KP_COLON = 203, + SCANCODE_KP_HASH = 204, + SCANCODE_KP_SPACE = 205, + SCANCODE_KP_AT = 206, + SCANCODE_KP_EXCLAM = 207, + SCANCODE_KP_MEMSTORE = 208, + SCANCODE_KP_MEMRECALL = 209, + SCANCODE_KP_MEMCLEAR = 210, + SCANCODE_KP_MEMADD = 211, + SCANCODE_KP_MEMSUBTRACT = 212, + SCANCODE_KP_MEMMULTIPLY = 213, + SCANCODE_KP_MEMDIVIDE = 214, + SCANCODE_KP_PLUSMINUS = 215, + SCANCODE_KP_CLEAR = 216, + SCANCODE_KP_CLEARENTRY = 217, + SCANCODE_KP_BINARY = 218, + SCANCODE_KP_OCTAL = 219, + SCANCODE_KP_DECIMAL = 220, + SCANCODE_KP_HEXADECIMAL = 221, + + SCANCODE_LCTRL = 224, + SCANCODE_LSHIFT = 225, + SCANCODE_LALT = 226, /**< alt, option */ + SCANCODE_LGUI = 227, /**< windows, command (apple), meta */ + SCANCODE_RCTRL = 228, + SCANCODE_RSHIFT = 229, + SCANCODE_RALT = 230, /**< alt gr, option */ + SCANCODE_RGUI = 231, /**< windows, command (apple), meta */ + + SCANCODE_MODE = 257, /**< I'm not sure if this is really not covered + * by any of the above, but since there's a + * special KMOD_MODE for it I'm adding it here + */ + + /* @} *//* Usage page 0x07 */ + + /** + * \name Usage page 0x0C + * + * These values are mapped from usage page 0x0C (USB consumer page). + * See https://usb.org/sites/default/files/hut1_2.pdf + * + * There are way more keys in the spec than we can represent in the + * current scancode range, so pick the ones that commonly come up in + * real world usage. + */ + /* @{ */ + + SCANCODE_AUDIONEXT = 258, + SCANCODE_AUDIOPREV = 259, + SCANCODE_AUDIOSTOP = 260, + SCANCODE_AUDIOPLAY = 261, + SCANCODE_AUDIOMUTE = 262, + SCANCODE_MEDIASELECT = 263, + SCANCODE_WWW = 264, /**< AL Internet Browser */ + SCANCODE_MAIL = 265, + SCANCODE_CALCULATOR = 266, /**< AL Calculator */ + SCANCODE_COMPUTER = 267, + SCANCODE_AC_SEARCH = 268, /**< AC Search */ + SCANCODE_AC_HOME = 269, /**< AC Home */ + SCANCODE_AC_BACK = 270, /**< AC Back */ + SCANCODE_AC_FORWARD = 271, /**< AC Forward */ + SCANCODE_AC_STOP = 272, /**< AC Stop */ + SCANCODE_AC_REFRESH = 273, /**< AC Refresh */ + SCANCODE_AC_BOOKMARKS = 274, /**< AC Bookmarks */ + + /* @} *//* Usage page 0x0C */ + + /** + * \name Walther keys + * + * These are values that Christian Walther added (for mac keyboard?). + */ + /* @{ */ + + SCANCODE_BRIGHTNESSDOWN = 275, + SCANCODE_BRIGHTNESSUP = 276, + SCANCODE_DISPLAYSWITCH = 277, /**< display mirroring/dual display + switch, video mode switch */ + SCANCODE_KBDILLUMTOGGLE = 278, + SCANCODE_KBDILLUMDOWN = 279, + SCANCODE_KBDILLUMUP = 280, + SCANCODE_EJECT = 281, + SCANCODE_SLEEP = 282, /**< SC System Sleep */ + + SCANCODE_APP1 = 283, + SCANCODE_APP2 = 284, + + /* @} *//* Walther keys */ + + /** + * \name Usage page 0x0C (additional media keys) + * + * These values are mapped from usage page 0x0C (USB consumer page). + */ + /* @{ */ + + SCANCODE_AUDIOREWIND = 285, + SCANCODE_AUDIOFASTFORWARD = 286, + + /* @} *//* Usage page 0x0C (additional media keys) */ + + /** + * \name Mobile keys + * + * These are values that are often used on mobile phones. + */ + /* @{ */ + + SCANCODE_SOFTLEFT = 287, /**< Usually situated below the display on phones and + used as a multi-function feature key for selecting + a software defined function shown on the bottom left + of the display. */ + SCANCODE_SOFTRIGHT = 288, /**< Usually situated below the display on phones and + used as a multi-function feature key for selecting + a software defined function shown on the bottom right + of the display. */ + SCANCODE_CALL = 289, /**< Used for accepting phone calls. */ + SCANCODE_ENDCALL = 290, /**< Used for rejecting phone calls. */ + + /* @} *//* Mobile keys */ + + /* Add any other keys here. */ + + NUM_SCANCODES = 512 /**< not a key, just marks the number of scancodes + for array bounds */ +} Scancode; + + + + + + + + + + + + } // namespace doengine \ No newline at end of file diff --git a/includes/EventHandler.h b/includes/EventHandler.h index ce737ed..84eedf1 100644 --- a/includes/EventHandler.h +++ b/includes/EventHandler.h @@ -11,6 +11,16 @@ using doengine::devices::Mouse; namespace doengine { + +struct KeyboardInputhandlingEvent +{ + KeyboardInputhandlingEvent(); + virtual ~KeyboardInputhandlingEvent(); + virtual void OnKeydown(const Keyboard&) = 0; + virtual void OnKeyup(const Keyboard&) = 0; +}; + + class KeyDownEvent { public: @@ -113,13 +123,22 @@ class OnMessageQueue struct MouseEvent { - virtual ~MouseEvent() - { - } - + virtual ~MouseEvent(); + MouseEvent(); + virtual void MouseWheel(const Mouse&) = 0; virtual void MouseMove(const Mouse&) = 0; virtual void MouseButtonDown(const Mouse&) = 0; virtual void MouseButtonUp(const Mouse&) = 0; }; + +struct TextInputEvent +{ + virtual ~TextInputEvent(); + TextInputEvent(); + virtual void OnTextInput(const std::string& text) = 0; +}; + + + } // namespace doengine \ No newline at end of file diff --git a/includes/FPSManager.h b/includes/FPSManager.h index 723e638..19a66e1 100644 --- a/includes/FPSManager.h +++ b/includes/FPSManager.h @@ -1,14 +1,18 @@ #pragma once #include +#include +#include +#include namespace doengine { class FpsManager { - uint32_t start, elapsed, wait, fps; - + uint32_t start, elapsed, wait, fps, last_elapsed; + uint64_t lastCounter; + uint64_t frequency; public: - FpsManager() : start(0L), elapsed(0L), fps(60L) + FpsManager() : start(0L), elapsed(0L), fps(60L), last_elapsed(0.0f) { } @@ -21,6 +25,28 @@ class FpsManager virtual void Handle(); virtual uint32_t getDeltaTime(); virtual float getElapsedTime(); + virtual float getLastElapsedTime(); + void beginFrame(); + double endFrame(); + static double getTicks(); }; + + + +/* +class TimerManager { +public: + void update(float deltaSeconds); + + Timer& addTimer(float seconds, Timer::Callback cb, bool repeat = false); + void clear(); + +private: + std::vector> m_timers; +}; + +*/ + + } // namespace doengine \ No newline at end of file diff --git a/includes/FontCache.h b/includes/FontCache.h new file mode 100644 index 0000000..b927788 --- /dev/null +++ b/includes/FontCache.h @@ -0,0 +1,28 @@ +#ifndef FONTCACHE_H_DEFINED +#define FONTCACHE_H_DEFINED +#include +#include +namespace doengine +{ + +constexpr const char *defaultGlyph = +" !\"#$%&\'()*+\'-/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_'abcdefghijklmnopqrstuvwyz{|}~"; + +struct FontCache +{ + Texture *glyph_texture; + + FontCache(const std::string& fontname); + + void createGlyph(); + + void DrawText(int x, int y, int max_width); + + +}; + + + +} + +#endif \ No newline at end of file diff --git a/includes/GameObject.h b/includes/GameObject.h index 1f370d3..f4b0a43 100644 --- a/includes/GameObject.h +++ b/includes/GameObject.h @@ -1,11 +1,40 @@ #pragma once +#include "Application.h" +#include "Geometric.h" +#include "Renderer.h" #include +#include namespace doengine { + +class Renderer; + +enum ObjecType +{ + Player = 0, + Level, + Tile, + AnimateTile, + Npc, + Ui, + DebugObject, + UknownObject, + LastDefaultUnused +}; + +static unsigned long long id_pool = 0; class GameObject { + private: + long long id; + protected: + bool debuging_enable; + + Rect position; + Renderer* renderer; + bool renderable; bool solid; int layer; @@ -14,16 +43,17 @@ class GameObject std::string texture_id; public: - GameObject() - { - } + GameObject(); + virtual ~GameObject(); + virtual void Update(float timer = 0) = 0; + virtual void Render() = 0; + Renderer* getRenderer(); + Rect getPosition(); - virtual ~GameObject() + inline const int getId() { + return id; } - virtual void Update(float timer = 0) = 0; - virtual void Render() = 0; - virtual bool isColliding(GameObject* other); }; } // namespace doengine \ No newline at end of file diff --git a/includes/GameState.h b/includes/GameState.h index c1b733a..aff87a2 100644 --- a/includes/GameState.h +++ b/includes/GameState.h @@ -1,9 +1,27 @@ #pragma once +#include +#include +#include +#include "Application.h" +#include "Renderer.h" +#include "GameObject.h" namespace doengine { + +class GameObject; +typedef std::variant ArgValue; + class GameState { + protected: + std::vector registered_game_object; + Renderer *renderer; public: + GameState(); + virtual ~GameState(); + std::optional queryGameObject(long long id); + void registerGameObject(GameObject *object); + void unregisterGameObject(long long id); virtual void OnEnter() = 0; virtual void OnExit() = 0; virtual void Update(float elapsed) = 0; diff --git a/includes/GameStateManager.h b/includes/GameStateManager.h index 7142bb5..0b7b2fe 100644 --- a/includes/GameStateManager.h +++ b/includes/GameStateManager.h @@ -5,6 +5,8 @@ #include namespace doengine { + +class GameState; class GameStateManager { diff --git a/includes/Geometric.h b/includes/Geometric.h index 780aaa6..33713b3 100644 --- a/includes/Geometric.h +++ b/includes/Geometric.h @@ -1,20 +1,141 @@ #pragma once - #include "Color.h" +#include "Logger.h" #include - +#include +#include namespace doengine { + +struct Mat4 { + float m[16]; + + Mat4() { + for (int i = 0; i < 16; i++) m[i] = 0.0f; + m[0] = m[5] = m[10] = m[15] = 1.0f; + } + + static Mat4 ortho(float left, float right, float bottom, float top, float near, float far) { + Mat4 result; + result.m[0] = 2.0f / (right - left); + result.m[5] = 2.0f / (top - bottom); + result.m[10] = -2.0f / (far - near); + result.m[12] = -(right + left) / (right - left); + result.m[13] = -(top + bottom) / (top - bottom); + result.m[14] = -(far + near) / (far - near); + return result; + } + + static Mat4 translate(float x, float y) { + Mat4 result; + result.m[12] = x; + result.m[13] = y; + return result; + } + + static Mat4 scale(float x, float y) { + Mat4 result; + result.m[0] = x; + result.m[5] = y; + return result; + } + + Mat4 operator*(const Mat4& other) const { + Mat4 result; + for (int row = 0; row < 4; row++) { + for (int col = 0; col < 4; col++) { + result.m[row * 4 + col] = 0.0f; + for (int i = 0; i < 4; i++) { + result.m[row * 4 + col] += m[row * 4 + i] * other.m[i * 4 + col]; + } + } + } + return result; + } +}; + + + struct Point { int x; int y; - Point()=default; - Point(int x, int y){ + Point() = default; + Point(int x, int y) + { this->x = x; this->y = y; } + + const Point& operator=(const Point& p) + { + x = p.x; + y = p.y; + return (*this); + } + const Point& operator=(const int p) + { + x = p; + y = p; + return (*this); + } + + const Point& operator+(float pxy) + { + x += pxy; + y += pxy; + return (*this); + + } + + const Point& operator+(int pxy) + { + x += pxy; + y += pxy; + return (*this); + + } + const Point& operator+(const Point& pxy) + { + x += pxy.x; + y += pxy.y; + return (*this); + + } + const Point& operator+=(const Point& pxy) + { + x += pxy.x; + y += pxy.y; + return (*this); + + } + + const Point& operator*(float pxy) + { + x += pxy; + y += pxy; + return (*this); + } + + const Point& operator*(int pxy) + { + x += pxy; + y += pxy; + return (*this); + } + const Point& operator*(const Point& pxy) + { + x += pxy.x; + y += pxy.y; + return (*this); + } + const Point& operator*=(const Point& pxy) + { + x += pxy.x; + y += pxy.y; + return (*this); + } }; struct Rect @@ -30,8 +151,17 @@ struct Rect Rect(int w, int h) : x{0}, y{0}, w{w}, h{h} { } - Rect(int x, int y,int w, int h) : x{x}, y{y}, w{w}, h{h} + Rect(int x, int y, int w, int h) : x{x}, y{y}, w{w}, h{h} + { + } + + Rect& operator=(const Rect& other) { + x = other.x; + y = other.y; + w = other.w; + h = other.h; + return (*this); } inline int left() const @@ -59,6 +189,10 @@ struct Rect { return reinterpret_cast(*this); } + inline void LogIt() + { + LogOuput(logger_type::Information, "[%d, %d, %d %d]", x, y, w, h); + } }; void DrawPoint(const Point& point, const Color& color); @@ -69,7 +203,40 @@ void FillCircle(int x, int y, int radius, const Color& color); void DrawTriangle(int x1, int y1, int x2, int y2, int x3, int y3, const Color& p); +bool checkPointOnRect(Point point, Rect rect); bool checkCollision(const Rect& rect1, const Rect& rect2); bool checkCollisionPointVsRect(const Rect& rect, const Point point); -bool checkCollisionCircleRec(const Point& cicle, float radius, const Rect& rect ); +bool checkCollisionCircleRec(const Point& cicle, float radius, + const Rect& rect); +bool isPointOnRectBorder(int mx, int my, const doengine::Rect& r, + int thickness = 1); + +void generateCirclePoints(Point center, double r, int steps, Point* out); + + +enum CollisionPosition +{ + Top, + Bottom, + Left, + Right, + NoCollision +}; + +std::vector checkCollisionBySide(const Rect& src, const Rect& object); + + +/// @brief mention, idea taken from +/// https://www.youtube.com/watch?v=3CWsy4kP6wU&t=24s +struct CircleGenerator +{ + float radius = 0.0f; + uint32_t quality; + float const da = 0.0f; + + CircleGenerator(float rad, uint32_t quality); + + Point getPoint(uint32_t i); +}; + } // namespace doengine \ No newline at end of file diff --git a/includes/Joypad.h b/includes/Joypad.h index a53b37c..3018af3 100644 --- a/includes/Joypad.h +++ b/includes/Joypad.h @@ -12,7 +12,7 @@ namespace devices enum class PowerLevel : char { - Unknown = -1, + Unknown = 0, Empty, Low, Medium, diff --git a/includes/Logger.h b/includes/Logger.h index 2488133..e7c51df 100644 --- a/includes/Logger.h +++ b/includes/Logger.h @@ -9,9 +9,12 @@ namespace doengine enum class logger_type{ Information, Warning, - Error + Error, + None }; +/// void setLog(logger_type type); + void LogOuput(const logger_type& type,const char *msg, ...); diff --git a/includes/MenuStringList.h b/includes/MenuStringList.h new file mode 100644 index 0000000..5e20889 --- /dev/null +++ b/includes/MenuStringList.h @@ -0,0 +1,110 @@ +#ifndef MENUSTRING_LIST_H_DEFINED +#define MENUSTRING_LIST_H_DEFINED +#include +#include +#include +#include "GameObject.h" +#include "Event.h" +#include "TTFText.h" + +namespace doengine +{ +struct MenuStringList : std::vector, + public GameObject, + public KeyboardInputhandlingEvent +{ + doengine::TTFText* text; + + std::string font_id; + + bool is_focused = true; + + std::function on_accepted; + + + + enum MenuOrientation + { + Vertical, + Horiozontal + } orientation; + + std::vector offsets; + int current_selection = 0; + + void createOffsetVector() + { + offsets.clear(); + for (size_t i = 0; i < size(); i++) + { + std::string value = at(i); + offsets.emplace_back(Rect{position.x, position.y + (i * 64), 0, + text->getFontHeight()}); + } + } + + MenuStringList(std::initializer_list str) + : std::vector(str) + { + text = new doengine::TTFText(); + text->setFont( + "/home/neonland/Documents/DOEngine/assets/fonts/DroidSans.ttf", 18); + position.x = Application::getApplication()->getW() / 2 - 100; + position.y = Application::getApplication()->getH() / 2 - 100; + createOffsetVector(); + // bg = new MenuPanelBackground(this); + } + virtual ~MenuStringList() + { + } + virtual void Update(float timer = 0) + { + } + virtual void Render() + { + + for (size_t i = 0; i < offsets.size(); i++) + { + std::string value = at(i); + text->DrawText(offsets[i].x, offsets[i].y, value.c_str()); + } + + offsets[current_selection].w = 200; + + renderer->DrawRect(offsets[current_selection], doengine::Colors::red); + } + virtual void OnKeydown(const Keyboard& keyboard) + { + auto keys = keyboard.getKeysBitset(); + if (keys[doengine::SCANCODE_DOWN]) + { + current_selection++; + } + if (keys[doengine::SCANCODE_UP]) + { + current_selection--; + } + if (keys[doengine::SCANCODE_KP_ENTER] || + keys[doengine::SCANCODE_RETURN]) + { + if (on_accepted) + on_accepted(current_selection); + } + if (current_selection > size() - 1) + current_selection = 0; + if (current_selection < 0) + current_selection = size() - 2; + } + virtual void OnKeyup(const Keyboard&) + { + } + + void setAcceptedCallback(std::function cb) + { + on_accepted=cb; + } +}; + +} + +#endif /// MENUSTRING_LIST_H_DEFINED \ No newline at end of file diff --git a/includes/MessageBox.h b/includes/MessageBox.h new file mode 100644 index 0000000..3f38012 --- /dev/null +++ b/includes/MessageBox.h @@ -0,0 +1,56 @@ +#ifndef MESSAGE_BOX_H_DEFINED +#define MESSAGE_BOX_H_DEFINED +#include "Application.h" +#include "Renderer.h" +#include "Geometric.h" +#include "Event.h" +#include "Logger.h" +#include "TTFText.h" +#include +#include +#include +namespace doengine +{ + +enum class MessageBoxFlag : int +{ + AcceptOnly, + YesOrNot, + YesOrNotOrCancel +}; + +struct MessageBox : public doengine::MouseEvent +{ + doengine::Renderer* renderer; + doengine::Rect position; + doengine::Rect tposition; + doengine::Rect bposition; + doengine::Rect cposition; + bool visible = true; + std::string text; + std::unique_ptr font; + std::function onClick; + MessageBoxFlag flags; + + MessageBox(const std::string& text, + const std::string& fontsrc, + std::function onClick); + + void setFlags(MessageBoxFlag flag) + { + flags = flag; + } + virtual void MouseMove(const Mouse&) override {} + virtual void MouseButtonDown(const Mouse&) override {} + virtual void MouseButtonUp(const Mouse&) override; + void show(){ + visible = true; + } + void hide(){ + visible = false; + } + void render(); +}; +} + +#endif \ No newline at end of file diff --git a/includes/Mouse.h b/includes/Mouse.h index 862a4c4..bb8d7eb 100644 --- a/includes/Mouse.h +++ b/includes/Mouse.h @@ -30,7 +30,16 @@ class Mouse virtual bool isMiddleClick() const = 0; virtual void getPosition(Point&) = 0; virtual const Point& getPosition() const = 0; + virtual const Point& getMotionPosition() const = 0; virtual void getButtonStateBitset(std::bitset&) const = 0; + virtual const Point& getWheelScroll() const = 0; + enum MouseButtonDown : uint8_t{ + Left = 1, + Right = 2, + Center = 4 + }; + static Rect getMousePosition(); + static uint8_t getButtonPressed(); }; } // namespace devices diff --git a/includes/MusicHandler.h b/includes/MusicHandler.h index 5a0921f..8fae59d 100644 --- a/includes/MusicHandler.h +++ b/includes/MusicHandler.h @@ -17,7 +17,7 @@ class MusicHandler virtual void pause(const int) = 0; virtual void stop(const int) = 0; virtual bool isPlaying(const int) const = 0; - + virtual std::string getPlayMusicName(){return "";} enum class Repeat { OnlyThis = -1, diff --git a/includes/NativeStructs.h b/includes/NativeStructs.h index d2101e5..aef4ef2 100644 --- a/includes/NativeStructs.h +++ b/includes/NativeStructs.h @@ -1,20 +1,28 @@ #pragma once #include "Geometric.h" #include "Texture.h" - +#include "Geometric.h" namespace doengine { +struct GlyphInfo +{ + Rect src; + int advance; +}; class Texture; struct NativeTexture { + int id; + Rect rectSize; virtual ~NativeTexture(){} NativeTexture(){} virtual NativeTexture* subTexture(Rect clipset) = 0; virtual NativeTexture* setNativeTexture(void *text) = 0; virtual NativeTexture* loadFromFile(const char* src) = 0; + virtual void Draw(int x, int y) = 0; virtual void Draw(const Rect& offset) = 0; virtual void Draw(const Rect& offset, const Rect& clipset) = 0; virtual void SetTransparentColor(const Color& color) =0; @@ -23,10 +31,14 @@ struct NativeTexture virtual int getWidth() = 0; virtual int getHeight() = 0; virtual bool validTexture() = 0; + + virtual void *getNativeBuffer() = 0; }; struct NativeTextRenderer { + virtual int getFontHeight() = 0; + virtual Rect getTextSize(const char* str) =0; virtual void setColor(doengine::Color fg, doengine::Color bg) = 0; virtual void setColor(doengine::Color color) = 0; virtual void setFont(const std::string& path, int fntsize) = 0; @@ -40,4 +52,17 @@ struct NativeTextRenderer virtual Texture* createBitmapFont(const std::string& font_path,const doengine::Color& bg,const doengine::Color& fg)= 0; }; +struct NativeBitmapTextRenderer +{ + enum class Alignment + { + Left, + Center, + Right + }; +}; + + + + } // namespace doengine \ No newline at end of file diff --git a/includes/PrimitivesGraphics.h b/includes/PrimitivesGraphics.h index 01ed3ac..03bb7b4 100644 --- a/includes/PrimitivesGraphics.h +++ b/includes/PrimitivesGraphics.h @@ -1,6 +1,6 @@ #pragma once #include "Window.h" -#include +#include "DOEngine_SDL_includes.h" namespace doengine { void DrawRect(SDL_Renderer* render, SDL_Rect rect); diff --git a/includes/ReactiveNative.h b/includes/ReactiveNative.h new file mode 100644 index 0000000..e8fcf4d --- /dev/null +++ b/includes/ReactiveNative.h @@ -0,0 +1,33 @@ +#ifndef REACTIVE_NATIVE_H_DEFINED +#define REACTIVE_NATIVE_H_DEFINED + +#include +#include + +namespace doengine +{ + +struct Int +{ + std::function callback; + std::atomic value; + + const int operator()() + { + return value; + } + int operator=(const int v) + { + value = v; + callback(v); + } +}; + + + +}; + + + +#endif + diff --git a/includes/Renderer.h b/includes/Renderer.h index 3423f0e..2fb4c04 100644 --- a/includes/Renderer.h +++ b/includes/Renderer.h @@ -6,7 +6,8 @@ namespace doengine { - +class NativeTexture; +class NativeTextRenderer; class Renderer { public: @@ -17,10 +18,14 @@ class Renderer virtual void setDrawColor(const Color&) = 0; virtual void updateScreen() = 0; + virtual void RenderSetClipRect(const Rect& ) = 0; + virtual void ResetRenderSetClipRect() = 0; + virtual void DrawPoint(const Point& point, const Color& color) = 0; virtual void DrawLine(const Point& p1, const Point& p2, const Color& color) = 0; virtual void DrawRect(const Rect& rect, const Color& color) = 0; + virtual void DrawRect(const Rect& rect, const Color& color, int thickness) = 0; virtual void DrawFillRect(const Rect& rect, const Color& color) = 0; virtual void FillCircle(int x, int y, int radius, const Color& color) = 0; virtual void DrawTriangle(int x1, int y1, int x2, int y2, int x3, int y3, @@ -29,6 +34,7 @@ class Renderer Color color) = 0; virtual NativeTexture* loadTextureFromImageFile(const char* src, Color color) = 0; + virtual NativeTexture* loadTextureFromImageFile(const char* src) = 0; virtual NativeTextRenderer* getTextRenderer() = 0; virtual NativeTexture* createTexture() =0; diff --git a/includes/RendererQueue.h b/includes/RendererQueue.h new file mode 100644 index 0000000..536d4cb --- /dev/null +++ b/includes/RendererQueue.h @@ -0,0 +1,29 @@ +#ifndef RENDERER_QUEUE_DEFINED +#define RENDERER_QUEUE_DEFINED + +#include "constant.h" +#include +#include +#include +/// @brief System to paint through layers.next +namespace doengine +{ + +struct Drawable +{ + virtual ~Drawable() = default; + protected: + LayerId id; + std::string guid; + std::string name; +}; + +struct RendererQueue +{ + std::unordered_map> drawable_layers; +}; + + +} + +#endif \ No newline at end of file diff --git a/includes/RequestAnimationFrame.h b/includes/RequestAnimationFrame.h new file mode 100644 index 0000000..d091002 --- /dev/null +++ b/includes/RequestAnimationFrame.h @@ -0,0 +1,40 @@ +#pragma once + + +#include +#include +#include +#include + +class RequestAnimationFrame { +public: + using Callback = std::function; + + explicit RequestAnimationFrame(double fps = 60.0) + : frameDuration(std::chrono::duration(1.0 / fps)), + running(false) {} + + void start(Callback cb) { + running = true; + auto startTime = std::chrono::steady_clock::now(); + + while (running) { + auto now = std::chrono::steady_clock::now(); + double timestamp = + std::chrono::duration(now - startTime).count(); + + cb(timestamp); + + auto nextFrame = now + frameDuration; + std::this_thread::sleep_until(nextFrame); + } + } + + void stop() { + running = false; + } + +private: + std::chrono::duration frameDuration; + std::atomic running; +}; \ No newline at end of file diff --git a/includes/SDLJoypad.h b/includes/SDLJoypad.h index 7581728..5de5f35 100644 --- a/includes/SDLJoypad.h +++ b/includes/SDLJoypad.h @@ -18,6 +18,8 @@ class SDLJoypad : public Joypad SDLJoypad(SDL_Joystick* joystick); + virtual ~SDLJoypad() = default; + virtual int getId() const override; virtual void getName(std::string& name) override; virtual PowerLevel getPowerLevel() const override; diff --git a/includes/SDLMouse.h b/includes/SDLMouse.h index d31e5ca..ccda081 100644 --- a/includes/SDLMouse.h +++ b/includes/SDLMouse.h @@ -15,16 +15,20 @@ class SDLMouse : public Mouse bool isLeftClick() const override; bool isMiddleClick() const override; void getPosition(Point&) override; + const Point& getMotionPosition() const override; const Point& getPosition() const override; void getButtonStateBitset( std::bitset&) const override; - + virtual const Point& getWheelScroll() const override; void updateValues(); - + void setScroll(int x, int y); + Point motion; private: unsigned int buttonMask; unsigned int which; Point position; + + Point scroll; }; } // namespace devices } // namespace doengine \ No newline at end of file diff --git a/includes/SDLMusicHandler.h b/includes/SDLMusicHandler.h index 5aa429a..f145ac8 100644 --- a/includes/SDLMusicHandler.h +++ b/includes/SDLMusicHandler.h @@ -20,10 +20,12 @@ class SDLMusicHandler : public MusicHandler virtual void stop(const int) override; virtual void setRepeat(Repeat repeat) override; virtual bool isPlaying(const int) const override; + virtual std::string getPlayMusicName() override; bool setPosition(const double pos); int setVolume(const int newVol); double getMusicDuration(const unsigned int index) const; double getMusicPosition(const unsigned int index) const; + private: virtual void setChannel(const int) override {}; diff --git a/includes/ScrollableList.h b/includes/ScrollableList.h new file mode 100644 index 0000000..7cda530 --- /dev/null +++ b/includes/ScrollableList.h @@ -0,0 +1,360 @@ +#ifndef SCROLLABLE_LIST_H_DEFINED +#define SCROLLABLE_LIST_H_DEFINED +#include +#include + +#include "Event.h" +#include "Geometric.h" +#include "Renderer.h" + +/// Todo remove +#include "DOEngine_SDL_includes.h" + +namespace doengine +{ + +class ScrollableContainer : public MouseEvent +{ + private: + + Renderer* renderer = nullptr; + Rect containerRect; // The visible container area + std::vector items; // List of items to display + + int scrollOffset; // Current scroll position + int maxScrollOffset; // Maximum scroll value + int itemSpacing; // Padding between items + int containerPadding; // Padding inside container + + // Scrollbar properties + Rect scrollbarTrack; + Rect scrollbarThumb; + bool isScrollbarDragging; + int dragStartY; + int dragStartOffset; + + // Mouse/touch state + bool isDragging; + int lastMouseY; + int dragVelocity; + + void calculateScrollbar() + { + int contentHeight = getContentHeight(); + int visibleHeight = containerRect.h - (2 * containerPadding); + + if (contentHeight <= visibleHeight) + { + scrollbarThumb.h = 0; // No scrollbar needed + return; + } + + // Scrollbar track + scrollbarTrack.x = containerRect.x + containerRect.w - 10; + scrollbarTrack.y = containerRect.y; + scrollbarTrack.w = 10; + scrollbarTrack.h = containerRect.h; + + // Scrollbar thumb + float thumbRatio = (float)visibleHeight / contentHeight; + scrollbarThumb.w = 8; + scrollbarThumb.h = std::max(20, (int)(scrollbarTrack.h * thumbRatio)); + scrollbarThumb.x = scrollbarTrack.x + 1; + + // Calculate thumb position based on scroll offset + float scrollRatio = (float)scrollOffset / maxScrollOffset; + int maxThumbY = scrollbarTrack.h - scrollbarThumb.h; + scrollbarThumb.y = containerRect.y + (int)(scrollRatio * maxThumbY); + } + + int getContentHeight() const + { + if (items.empty()) + return 0; + return items.size() * (items[0].h + itemSpacing) - itemSpacing; + } + + public: + ScrollableContainer(int x, int y, int width, int height, int padding = 5, + int spacing = 5) + : scrollOffset(0), maxScrollOffset(0), itemSpacing(spacing), + containerPadding(padding), isScrollbarDragging(false), + isDragging(false), lastMouseY(0), dragVelocity(0) + { + + containerRect = {x, y, width, height}; + renderer = Application::getApplication()->getRender(); + } + + void setItems(const std::vector& itemList) + { + items = itemList; + + // Position items vertically + int currentY = containerPadding; + for (auto& item : items) + { + item.y = currentY; + currentY += item.h + itemSpacing; + } + + // Calculate max scroll offset + int contentHeight = getContentHeight(); + int visibleHeight = containerRect.h - (2 * containerPadding); + maxScrollOffset = std::max(0, contentHeight - visibleHeight); + scrollOffset = std::min(scrollOffset, maxScrollOffset); + + calculateScrollbar(); + } + + virtual void MouseWheel(const Mouse& mouse) + { + scroll(-mouse.getWheelScroll().y * + 15); // Scroll 15 pixels per wheel tick + } + virtual void MouseMove(const Mouse& mouse) + { + Point motion; + motion.x = mouse.getMotionPosition().x; + motion.y = mouse.getMotionPosition().y; + if (isScrollbarDragging) + { + int deltaY = motion.y - dragStartY; + int contentHeight = getContentHeight(); + int visibleHeight = containerRect.h - (2 * containerPadding); + int maxThumbY = scrollbarTrack.h - scrollbarThumb.h; + + if (maxThumbY > 0) + { + float scrollRatio = (float)deltaY / maxThumbY; + int newOffset = + dragStartOffset + (int)(scrollRatio * maxScrollOffset); + scrollOffset = std::clamp(newOffset, 0, maxScrollOffset); + calculateScrollbar(); + } + } + else if (isDragging) + { + int deltaY = motion.y - lastMouseY; + dragVelocity = deltaY; + scroll(-deltaY); // Invert for natural scrolling + lastMouseY = motion.y; + } + } + virtual void MouseButtonDown(const Mouse& mouse) + { + if (mouse.getButtonPressed() == Mouse::MouseButtonDown::Left) + { + + Rect mousePosition = mouse.getMousePosition(); + int mx = mousePosition.x; /// event.button.x; + int my = mousePosition.y; /// event.button.y; + Point of{mx, my}; + // Check if clicking on scrollbar thumb + if (checkPointOnRect(of, scrollbarThumb)) + { + isScrollbarDragging = true; + dragStartY = my; + dragStartOffset = scrollOffset; + } + // Check if clicking in container + else if (checkPointOnRect(of, containerRect)) + { + isDragging = true; + lastMouseY = my; + dragVelocity = 0; + } + } + } + virtual void MouseButtonUp(const Mouse& mouse) + { + if (mouse.getButtonPressed() == Mouse::MouseButtonDown::Left) + { + isScrollbarDragging = false; + isDragging = false; + } + } +#if 0 + void handleEvent(const SDL_Event& event) + { + switch (event.type) + { + case SDL_MOUSEWHEEL: + scroll(-event.wheel.y * 15); // Scroll 15 pixels per wheel tick + break; + + case SDL_MOUSEBUTTONDOWN: + if (event.button.button == SDL_BUTTON_LEFT) + { + int mx = event.button.x; + int my = event.button.y; + SDL_Point of{mx, my}; + // Check if clicking on scrollbar thumb + if (SDL_PointInRect(&of, &scrollbarThumb)) + { + isScrollbarDragging = true; + dragStartY = my; + dragStartOffset = scrollOffset; + } + // Check if clicking in container + else if (SDL_PointInRect(&of, &containerRect)) + { + isDragging = true; + lastMouseY = my; + dragVelocity = 0; + } + } + break; + + case SDL_MOUSEBUTTONUP: + if (event.button.button == SDL_BUTTON_LEFT) + { + isScrollbarDragging = false; + isDragging = false; + } + break; + + case SDL_MOUSEMOTION: + if (isScrollbarDragging) + { + int deltaY = event.motion.y - dragStartY; + int contentHeight = getContentHeight(); + int visibleHeight = containerRect.h - (2 * containerPadding); + int maxThumbY = scrollbarTrack.h - scrollbarThumb.h; + + if (maxThumbY > 0) + { + float scrollRatio = (float)deltaY / maxThumbY; + int newOffset = + dragStartOffset + (int)(scrollRatio * maxScrollOffset); + scrollOffset = std::clamp(newOffset, 0, maxScrollOffset); + calculateScrollbar(); + } + } + else if (isDragging) + { + int deltaY = event.motion.y - lastMouseY; + dragVelocity = deltaY; + scroll(-deltaY); // Invert for natural scrolling + lastMouseY = event.motion.y; + } + break; + + case SDL_FINGERDOWN: + isDragging = true; + lastMouseY = (int)(event.tfinger.y * containerRect.h); + dragVelocity = 0; + break; + + case SDL_FINGERUP: + isDragging = false; + break; + + case SDL_FINGERMOTION: + if (isDragging) + { + int currentY = (int)(event.tfinger.y * containerRect.h); + int deltaY = currentY - lastMouseY; + dragVelocity = deltaY; + scroll(-deltaY * 2); // Touch is often more sensitive + lastMouseY = currentY; + } + break; + + case SDL_KEYDOWN: + switch (event.key.keysym.sym) + { + case SDLK_UP: + scroll(-20); + break; + case SDLK_DOWN: + scroll(20); + break; + case SDLK_PAGEUP: + scroll(-containerRect.h); + break; + case SDLK_PAGEDOWN: + scroll(containerRect.h); + break; + case SDLK_HOME: + scrollOffset = 0; + calculateScrollbar(); + break; + case SDLK_END: + scrollOffset = maxScrollOffset; + calculateScrollbar(); + break; + } + break; + } + } + #endif + void scroll(int delta) + { + scrollOffset = std::clamp(scrollOffset + delta, 0, maxScrollOffset); + calculateScrollbar(); + } + + void Render() + { + if(!renderer)return ; + // Draw container background + ////SDL_SetRenderDrawColor(renderer, 40, 40, 40, 255); + ////SDL_RenderFillRect(renderer, &containerRect); + renderer->DrawFillRect(containerRect,{40,40,40,255}); + // Set up clipping rectangle for scrollable area + Rect clipRect = {containerRect.x + containerPadding, + containerRect.y + containerPadding, + containerRect.w - (2 * containerPadding) - + 12, // Leave room for scrollbar + containerRect.h - (2 * containerPadding)}; + + renderer->RenderSetClipRect(clipRect); + + // Draw items + ////SDL_SetRenderDrawColor(renderer, 80, 120, 200, 255); + for (const auto& item : items) + { + Rect renderRect = item; + renderRect.x = containerRect.x + containerPadding + 5; + renderRect.y = + containerRect.y + containerPadding + item.y - scrollOffset; + + // Only render if visible + if (renderRect.y + renderRect.h >= clipRect.y && + renderRect.y <= clipRect.y + clipRect.h) + { + ///SDL_RenderFillRect(renderer, &renderRect); + renderer->DrawFillRect(renderRect, {80,120,220,255}); + // Draw border + ////SDL_SetRenderDrawColor(renderer, 120, 160, 240, 255); + ////SDL_RenderDrawRect(renderer, &renderRect); + renderer->DrawRect(renderRect, {120,160,240,255}); + ////SDL_SetRenderDrawColor(renderer, 80, 120, 200, 255); + } + } + + // Clear clipping + renderer->ResetRenderSetClipRect(); + + // Draw scrollbar track + if (scrollbarThumb.h > 0) + { + ///SDL_SetRenderDrawColor(renderer, 60, 60, 60, 255); + ///SDL_RenderFillRect(renderer, &scrollbarTrack); + renderer->DrawFillRect(scrollbarTrack, {60,60,60,255}); + // Draw scrollbar thumb + ///SDL_SetRenderDrawColor(renderer, 120, 120, 120, 255); + ///SDL_RenderFillRect(renderer, &scrollbarThumb); + renderer->DrawFillRect(scrollbarThumb, {120,120,120,255}); + + } + + // Draw container border + ////SDL_SetRenderDrawColor(renderer, 100, 100, 100, 255); + ///SDL_RenderDrawRect(renderer, &containerRect); + renderer->DrawRect(containerRect, {100,100,100,255}); + } +}; +} // namespace doengine +#endif \ No newline at end of file diff --git a/includes/SelectionRect.h b/includes/SelectionRect.h new file mode 100644 index 0000000..0055a03 --- /dev/null +++ b/includes/SelectionRect.h @@ -0,0 +1,60 @@ +#ifndef SELECTION_RECT_H_DEFINED +#define SELECTION_RECT_H_DEFINED + +#include +#include "Application.h" +#include "Renderer.h" +#include "TTFText.h" +#include "Event.h" +#include "EventHandler.h" +#include "Geometric.h" +#include "Draggable.h" + +#include + + +namespace doengine +{ + + +class SelectionRect : + public Draggable, + public doengine::KeyboardInputhandlingEvent, + public doengine::MouseEvent, + public doengine::GameObject +{ + + private: + std::string fontsrc; + doengine::TTFText *font; + bool function_active = true; + bool active = false; + doengine::Rect start{}; + doengine::Rect current{}; + std::function onSelectionFinished; + void setup(); + + virtual void OnKeydown(const Keyboard&keyboard); + virtual void OnKeyup(const Keyboard&keyboard); + virtual void MouseMove(const Mouse& mouse); + virtual void MouseWheel(const Mouse&){} + virtual void MouseButtonDown(const Mouse& mouse); + virtual void MouseButtonUp(const Mouse& mouse); + virtual void startDraggingPoint(const doengine::Rect& start); + virtual void updateCoords(const doengine::Rect& rect); + + public: + SelectionRect(); + doengine::Rect NormalizeRect(const doengine::Rect& a, const doengine::Rect& b); + void setFont(const std::string& font); + virtual ~SelectionRect(); + virtual void Render(); + virtual void Update(float elapsed){} + void setFontColor(const doengine::Color& color); + void registerSelectionRectFinished(std::function fn); + +}; + +} + +#endif diff --git a/includes/Sprite.h b/includes/Sprite.h index 7cb3bd6..b26c5c1 100644 --- a/includes/Sprite.h +++ b/includes/Sprite.h @@ -1,11 +1,24 @@ #pragma once #include #include +#include +#include "GameObject.h" +#include "SpriteOffset.h" +#include "Geometric.h" + namespace doengine { -class Sprite +class Sprite : public GameObject { - protected: + Rect offset; + Rect clipset; + SpriteAnimationOffset *animations; + std::variant texture_id; + public: + virtual ~Sprite() = default; + virtual void Update(float timer = 0) = 0; + virtual void Render() = 0; + virtual bool isColliding(GameObject* other) = 0; }; } // namespace doengine \ No newline at end of file diff --git a/includes/SpriteOffset.h b/includes/SpriteOffset.h new file mode 100644 index 0000000..55df5ba --- /dev/null +++ b/includes/SpriteOffset.h @@ -0,0 +1,84 @@ +#ifndef SPRITE_OFFSET_H_DEFINED +#define SPRITE_OFFSET_H_DEFINED +#include +#include "Geometric.h" +#include "Logger.h" + + +namespace doengine +{ + +struct SpriteAnimationOffset +{ + int id; + std::vector spriteOffset; + + int current = 0; + bool ended = false; + bool rewind = true; + + double frameDuration = 0.1; // seconds per frame + double accumulator = 0.0; + + SpriteAnimationOffset(int id): id{id} + { + } + + explicit SpriteAnimationOffset(int id, std::vector offsets) + : id{id}, + spriteOffset{std::move(offsets)} + { + } + + const int getSizeOfOffset()const + { + return spriteOffset.size(); + } + + void update(double deltaTime) + { + doengine::LogOuput(doengine::logger_type::Information, "detal Time %lf", deltaTime); + if ((ended && !rewind) || spriteOffset.empty()) + return; + if(ended && rewind) + reset(); + + accumulator += deltaTime; + + while (accumulator >= frameDuration) + { + accumulator -= frameDuration; + current++; + + if (current >= static_cast(spriteOffset.size())) + { + current = static_cast(spriteOffset.size()) - 1; + ended = true; + break; + } + } + } + + doengine::Rect getCurrentOffset() const + { + return spriteOffset[current]; + } + + void reset() + { + current = 0; + accumulator = 0.0; + ended = false; + } + + bool finish() const + { + return ended; + } +}; + + +} + + +#endif diff --git a/includes/StringUtils.h b/includes/StringUtils.h index d830f38..36d79a2 100644 --- a/includes/StringUtils.h +++ b/includes/StringUtils.h @@ -1,6 +1,7 @@ #pragma once #include "StringUtils.h" -#include +#include "DOEngine_SDL_includes.h" +#include #include #include #include @@ -14,4 +15,14 @@ std::vector splitChar(const std::string& original, const std::string& ignoredChars); std::string vectorOfCharToString(const std::vector& chars); int StringAsInt(const std::string& str); -} // namespace doengine \ No newline at end of file +template +std::string to_string(T value) +{ + std::ostringstream os; + os << value; + return os.str(); +} + +bool isCharInGroup(char ch, const std::string group); + +} // namespace doengine diff --git a/includes/TTFText.h b/includes/TTFText.h index e447655..b3111e9 100644 --- a/includes/TTFText.h +++ b/includes/TTFText.h @@ -11,22 +11,29 @@ using std::string; namespace doengine { - +class NativeBitmapTextRenderer; +class NativeTextRenderer; class Texture; class Renderer; class TTFText { NativeTextRenderer* nativeRenderer; + NativeBitmapTextRenderer* nativeBitmapRenderer; public: TTFText(); void setColor(Color color); + void setForegroundColor(const Color& color); void setFont(const std::string& path, int fntsize); void DrawText(const char* text, int x, int y); + void DrawText(int x, int y, const char *fmt, ...); Texture* createText(const std::string& text); void wrapText(const char* text, int maxWidth, char* wrappedText); Texture* createBitmapFont(const std::string& font_path,const doengine::Color& bg,const doengine::Color& fg); + + int getFontHeight(); }; -} // namespace doengine \ No newline at end of file +} // namespace doengine + diff --git a/includes/TextField.h b/includes/TextField.h new file mode 100644 index 0000000..5aac669 --- /dev/null +++ b/includes/TextField.h @@ -0,0 +1,55 @@ +#ifndef TEXTFIELD_UI_H_DEFINED +#define TEXTFIELD_UI_H_DEFINED + +#include "Application.h" +#include "Event.h" +#include "Geometric.h" +#include "Renderer.h" +#include "TTFText.h" +#include +#include + +namespace doengine +{ + +class TextField : public MouseEvent, + public KeyboardInputhandlingEvent, + public TextInputEvent +{ + private: + virtual void MouseMove(const Mouse& mouse); + virtual void MouseButtonDown(const Mouse& mouse); + virtual void MouseButtonUp(const Mouse&); + virtual void MouseWheel(const Mouse&); + + virtual void OnKeydown(const Keyboard& keyboard); + virtual void OnKeyup(const Keyboard&); + virtual void OnTextInput(const std::string& text); + + public: + TextField(int x, int y, int w, int h, const std::string& font); + + void Render(); + + std::string getText() const; + + void setOnEnterKeyPressed(std::function fn); + + private: + Rect rect; + Color textColor; + Color bgColor; + Color borderColor; + std::string text; + bool focused; + + std::string fontsrc; + TTFText* font; + Renderer* renderer; + + std::function onEnterPressed; +}; + +} // namespace doengine + +#endif \ No newline at end of file diff --git a/includes/Texture.h b/includes/Texture.h index e2ac507..3ad6c3f 100644 --- a/includes/Texture.h +++ b/includes/Texture.h @@ -1,17 +1,19 @@ #pragma once #include #include +#include +#include using std::map; using std::string; - +#include "TTFText.h" #include "Geometric.h" #include "NativeStructs.h" #include namespace doengine { - +class TTFText; class NativeTexture; class Texture @@ -24,6 +26,7 @@ class Texture Texture(std::string path); ~Texture(); void SetTransparentColor(const Color& color); + void Draw(int x, int y); void Draw(const Rect& offset); void Draw(const Rect& offset, const Rect& clipset); void ModulateColor(const Color& color); @@ -32,6 +35,8 @@ class Texture bool validTexture(); Texture* setNativeTexture(void *); Texture* subTexture(const Rect& clipset); + + void* getNativeBuffer(); }; class TextureManager @@ -43,21 +48,37 @@ class TextureManager static TextureManager* instance; - std::map textures; + std::map, Texture*> textures; + std::map, TTFText*> fonts; public: static TextureManager* getTextureManager(); - void loadTextureFromFile(string id, string src); + void loadTextureFromFile(const std::variant& key, string src); + void loadFont(const std::variant& key, string src, int pts); + TTFText* getFont(const std::variant& id); void loadTextureFromTexture(string id, Texture* texture, const Rect& clipset); void addTexture(string id, Texture* texture); + void addTexture(const std::variant& key, Texture* texture); void removeTexture(string id); - Texture* getTexture(string id); + Texture* getTexture(const std::variant& id); + Texture* getTextureOr(const std::variant& id, std::function orCall); + + + enum class TextureStatus{ + Success, + Error, + TextureIdInvalid + }; + TextureStatus drawTexture(const std::string,const Rect offset ,const Rect clipset); + TextureStatus drawTexture(const std::string id,const Rect offset); + + }; -} // namespace doengine \ No newline at end of file +} // namespace doengine diff --git a/includes/Tile.h b/includes/Tile.h index f84881a..2003d9c 100644 --- a/includes/Tile.h +++ b/includes/Tile.h @@ -1,18 +1,29 @@ #pragma once #include "Geometric.h" +#include +#include namespace doengine { const int default_tile_size = 32; + +struct TileOffset{ + doengine::Rect textureOffset; + doengine::Color color; + std::variant texture_id; +}; + + struct Tile { + Rect offset; int layer; - int type; + char type; Tile() { } - Tile(int x, int y, int s, int type) + Tile(int x, int y, int s, char type) { offset.x = x; offset.y = y; @@ -20,7 +31,7 @@ struct Tile offset.h = s; this->type = type; } - Tile(int x, int y, int s) + Tile(int x, int y, char s) { offset.x = x; offset.y = y; diff --git a/includes/TileMapEditor.h b/includes/TileMapEditor.h new file mode 100644 index 0000000..4c5a3cb --- /dev/null +++ b/includes/TileMapEditor.h @@ -0,0 +1,85 @@ +#ifndef TILEMAP_EDITOR_H_DEFINED +#define TILEMAP_EDITOR_H_DEFINED + +#include "Application.h" +#include "Event.h" +#include "Geometric.h" +#include "Logger.h" +#include "Renderer.h" +#include "TTFText.h" +#include +#include + + +namespace doengine +{ +struct TileCharTextureOffset +{ + char type; + doengine::Rect offset; + TileCharTextureOffset() + { + } + TileCharTextureOffset(char t, doengine::Rect rect) : type{t}, offset{rect} + { + } +}; + +struct InternalTileCharTextureOffset +{ + doengine::Rect position_in_panel; + TileCharTextureOffset clipset; +}; +struct TileMapEditor : public doengine::MouseEvent, + public doengine::KeyboardInputhandlingEvent +{ + + std::vector> selected_type_for_char_type; + + bool visible = true; + doengine::Texture* saveIcon = nullptr; + doengine::Texture* mapTexture = nullptr; + doengine::Renderer* renderer; + std::unordered_map offset_of_drawing; + std::vector tiles; + doengine::Rect panelPosition; + std::vector tileset_display; + + /// Tile Layout + int paddingTop = 10; + int paddingLeft = 10; + + /// Editor Controls + bool inEditorArea = false; + doengine::Rect debug_area; + doengine::Rect cursor; + char selected_type; + bool selected = false; + + /// Debug Options + bool mousePositionDebug = false; + doengine::Rect mouseDebugText; + doengine::TTFText* font; + + TileMapEditor(const std::string& fontsrc); + TileCharTextureOffset getSelectedEditorTileTextureOffset(); + bool isSelectect(); + void setup(const std::string& textId, + std::unordered_map clipList); + void update(double elapsed); + void render(); + void MouseMove(const Mouse& mouse); + void MouseButtonDown(const Mouse& mouse); + void MouseButtonUp(const Mouse&); + virtual void OnKeydown(const Keyboard& keyboard); + virtual void OnKeyup(const Keyboard& keyboard); + void registerSelectedTileByChar( std::function fn); + + inline const bool isVisible()const { + return visible; + } +}; + +} // namespace doengine + +#endif \ No newline at end of file diff --git a/includes/Tilemap.h b/includes/Tilemap.h index 2638bbd..e69de29 100644 --- a/includes/Tilemap.h +++ b/includes/Tilemap.h @@ -1,123 +0,0 @@ -#pragma once - -#include "Geometric.h" -#include "Texture.h" -#include "Tile.h" -#include -#include -#include - -namespace doengine -{ - -#define VALUE_IN_ARRAY(X, Y, W, A) A[Y * W + X] - -struct MapRenderer -{ - virtual void RenderTile(Rect rect) = 0; - virtual void RenderTile(int x, int y, int w, int h, Color color) = 0; -}; - -struct TileMap -{ - - protected: - MapRenderer* mapRenderer; - - public: - TileMap() : mapRenderer(nullptr) - { - } - virtual ~TileMap() - { - } - virtual void setMapRenderer(MapRenderer* render) = 0; - virtual void loadTileMapFromFile(const char* file) = 0; - virtual void saveLoadedTileMap() = 0; - virtual void render() = 0; - virtual void update() = 0; -}; - -/* - the simplest Tilemap the DOEngine Handler, support not layer - single layer, allow from 0 - 255 tiles types for map, - definition must be implemented or provide by developers. - - it could be - - file structure would be. - C = Columns - R = Rows - - tilesetimage\n - tilesizew\n - tilesizeh\n - ColumnCount\n - RowCount\n - C C C C...C+n - C C C C... - ... - ... - R+n - - example: - 10 - 3 - 1 1 1 1 1 1 1 1 1 1 - 1 0 0 0 0 0 0 0 0 1 - 1 1 1 1 1 1 1 1 1 1 -*/ -struct SimpleLayerTileMap : public TileMap -{ - - protected: - Texture* texture; - std::vector thisMap; - - public: - SimpleLayerTileMap(); - virtual ~SimpleLayerTileMap(); - virtual void loadTileMapFromFile(const char* file); - virtual void saveLoadedTileMap(); - virtual void setTileType(int r, int c, char t); - virtual char getTileType(int r, int c); - virtual void setMapRenderer(MapRenderer* render); - virtual void render(); - virtual void update(); - - int margin_left; - int margin_right; - int margin_bottom; - int margin_top; - int rows; - int cols; - - int getMarginLeft() - { - return margin_left; - } - int getMarginTop() - { - return margin_top; - } - - int setMarginLeft(int s) - { - return (margin_left = s); - } - int getMarginTop(int s) - { - return (margin_top = s); - } - - int getRowCount() - { - return rows; - } - int getColumnCount() - { - return rows; - } -}; - -}; // namespace doengine \ No newline at end of file diff --git a/includes/TimeTools.h b/includes/TimeTools.h new file mode 100644 index 0000000..127c7db --- /dev/null +++ b/includes/TimeTools.h @@ -0,0 +1,9 @@ +#ifndef TIMETOOLS_H_DEFINED +#define TIMETOOLS_H_DEFINED + +namespace doengine +{ + +} + +#endif \ No newline at end of file diff --git a/includes/Timer.h b/includes/Timer.h new file mode 100644 index 0000000..5d9a88d --- /dev/null +++ b/includes/Timer.h @@ -0,0 +1,36 @@ +#ifndef TIMER_H_DEFINED +#define TIMER_H_DEFINED +#include +namespace doengine +{ + +class Timer +{ + private: + bool pause = false; + bool timeout= false; + float len =0; + float current_time = 0; + std::function timeout_callback; + public: + Timer(float length); + ~Timer(); + int getLength(); + float getTime(); + bool isTimeout(); + void Update(float t); + void reset(); + void addTimerCallback(std::function cb); + inline float getElapsedTime() + { + return current_time; + } + +}; + + + + +}; + +#endif \ No newline at end of file diff --git a/includes/UI.h b/includes/UI.h new file mode 100644 index 0000000..86a838a --- /dev/null +++ b/includes/UI.h @@ -0,0 +1,69 @@ +#pragma once +#include "Color.h" +#include "Renderer.h" +#include "Geometric.h" +namespace doengine +{ + +enum class Border +{ + None, + Left, + Right, + Top, + Bottom +}; + +enum class MouseBtn +{ + None, + Left, + Middle, + Right +}; + +struct BorderPress +{ + bool pressed; // true if border hit + button pressed + Border border; // which border + MouseBtn button; // which mouse button + int x, y; // mouse position +}; + + + enum class ComponentType{ + container, + child + }; + + struct UIElement + { + Renderer *getRenderer(){ + return renderer; + } + UIElement(){ + // auto manager = Application::getApplication() + // ->getWindow() + // ->getGUIManager() + // ->getLastIndex(); + } + + protected: + int index; + Renderer *renderer; + ComponentType type; + }; + + struct UIPanel : UIElement + { + bool mouseOverPanelBorder; + Color backgroundColor; + Rect size; + UIPanel(); + virtual ~UIPanel(); + void Draw(); + void Update(); + }; + + +}; \ No newline at end of file diff --git a/includes/Utils.h b/includes/Utils.h index 0f0d6c2..f4f9824 100644 --- a/includes/Utils.h +++ b/includes/Utils.h @@ -46,4 +46,126 @@ constexpr T clamp(const T v, const T min, const T max) { return std::min(max, std::max(v, min)); } + +double DegreesToRadians(double degree) +{ + return (degree * (M_PI/180.0)); +} + +#include + +#if defined(_WIN32) + #include + #define getcwd _getcwd + #define PATH_MAX _MAX_PATH +#else + #include + #include +#endif + +static inline std::string getCurrentPath() +{ + char buffer[PATH_MAX]; + return getcwd(buffer, sizeof(buffer)) ? buffer : ""; +} + + +template +bool isItemInGroup(const T type, std::vector f) +{ + for(auto i : f) + if( i == type) + return true; + return false; +} + +Rect CenterRect(const Rect& outer, const Rect& inner) +{ + Rect result = inner; + + result.x = outer.x + (outer.w - inner.w) / 2; + result.y = outer.y + (outer.h - inner.h) / 2; + + return result; +} + +Rect RectSizeFromPercentage(const Rect& reference, + float percentW, + float percentH) +{ + Rect result; + + result.x = 0; + result.y = 0; + result.w = static_cast(reference.w * (percentW / 100.0f)); + result.h = static_cast(reference.h * (percentH / 100.0f)); + + return result; +} +Rect RectSizeFromPercentageUniform(const Rect& reference, + float percent) +{ + Rect result; + int size = static_cast( + std::min(reference.w, reference.h) * (percent / 100.0f)); + + result = { 0, 0, size, size }; + return result; +} + + +// Template function to handle any data type (int, float, double) +template +T map_value(T value, T in_min, T in_max, T out_min, T out_max) { + + // Check for division by zero to avoid crashes + if (in_min == in_max) { + return out_min; // or handle error as needed + } + + // The Standard Formula + // We cast to double for the calculation to preserve precision, + // then cast back to T (the original type) for the return value. + return static_cast( + out_min + (static_cast(value) - in_min) * (out_max - out_min) / (in_max - in_min) + ); +} + + + + +#if 0 +template +T map_constrained(T value, T in_min, T in_max, T out_min, T out_max) { + + // 1. Handle division by zero + if (in_min == in_max) { + return out_min; + } + + // 2. Perform the standard map calculation + // We cast to double for precision, then back to T + T result = static_cast( + out_min + (static_cast(value) - in_min) * (out_max - out_min) / (in_max - in_min) + ); + + // 3. Determine the true lower and upper bounds of the output range + // We do this because out_min might be larger than out_max (inverted range) + T lower_bound = std::min(out_min, out_max); + T upper_bound = std::max(out_min, out_max); + + // 4. Clamp the result + // If you are using C++17 or newer, you can use: return std::clamp(result, lower_bound, upper_bound); + + if (result < lower_bound) return lower_bound; + if (result > upper_bound) return upper_bound; + return result; +} +#endif +template +T Distance(T t1, T t2) +{ + return sqrt(t1-t2); +} + } // namespace doengine \ No newline at end of file diff --git a/includes/Vector.h b/includes/Vector.h index 5b44076..31c587b 100644 --- a/includes/Vector.h +++ b/includes/Vector.h @@ -14,6 +14,46 @@ class Vector public: float x; float y; + + Vector(float x, float y) + { + this->x = x; + this->y = y; + } + + Vector() + { + this->x = 0.0f; + this->y = 0.0f; + } + Vector operator*(Vector f) + { + Vector ret; + ret.x *= f.x; + ret.y *= f.y; + return ret; + } + Vector operator*(float f) + { + Vector ret; + ret.x *= f; + ret.y *= f; + return ret; + } + Vector operator+(Vector f) + { + Vector ret; + ret.x += f.x; + ret.y += f.y; + return ret; + } + Vector operator+(float f) + { + Vector ret; + ret.x += f; + ret.y += f; + return ret; + } }; template <> @@ -23,5 +63,140 @@ class Vector int x; int y; }; +#if 0 +Vector2::Vector2() +{ + this->x = this->y = 0; +} + +Vector2::Vector2(float x, float y) +{ + this->x = x; + this->y = y; +} + +void Vector2::add(Vector2 v) +{ + this->x += v.x; + this->y += v.y; +} + +void Vector2::add(float x, float y) +{ + this->x += x; + this->y += y; +} + +void Vector2::sub(Vector2 v) +{ + this->x -= v.x; + this->y -= v.y; +} + +void Vector2::sub(float x, float y) +{ + this->x -= x; + this->y -= y; +} + +void Vector2::mul(float f) +{ + this->x *= f; + this->y *= f; +} +Vector2 Vector2::mul_N(float f) +{ + Vector2 ret; + ret.x = this->x * f; + ret.y = this->y * f; + return ret; +} + +float Vector2::length() +{ + return sqrt(x * x + y * y); +} + +void Vector2::normalize() +{ + float l = length(); + x = x / l; + y = y / l; +} + +double Vector2::angle(Vector2 v) +{ // Note that this function returns DEG + double dot = this->x * v.x + this->y * v.y; + double det = this->x * v.y - this->y * v.x; + double ret = atan2(det, dot); + ret *= (180.0 / PI); + while (ret < 0) + ret += 360; + while (ret > 360) + ret -= 360; + return ret; +} + +void Vector2::set(float x, float y) +{ + this->x = x; + this->y = y; +} + +void Vector2::fromAngle(double angle) +{ // Note that this function takes DEG arguments + this->x = round_3dec(sin(angle * (PI / 180.0))); + this->y = round_3dec( + cos((180 - angle) * + (PI / 180.0))); // I honestly have no clue why I have to do + // 180-angle here, but it only works that way :D + this->normalize(); +} + +void Vector2::rotate(double angle) +{ // takes deg argument + angle = angle * (PI / 180.0); + float newX, newY; + newX = x * cos(angle) - y * sin(angle); + newY = x * sin(angle) + y * cos(angle); + x = newX; + y = newY; +} + +// This is just a basic bezier function: +// http://answers.unity3d.com/questions/12689/moving-an-object-along-a-bezier-curve.html +Vector2 Bezier(Vector2 start, Vector2 control, Vector2 end, float t) +{ + start.mul(((1 - t) * (1 - t))); + control.mul(2 * t * (1 - t)); + end.mul((t * t)); + start.add(control); + start.add(end); + return start; +} + +// Interpolates a Vector by value t +Vector2 Lerp(Vector2 start, Vector2 end, float t) +{ + if (t <= 0) + return start; + if (t >= 1) + return end; + end.sub(start); // Vector from start to end + end.mul(t); + end.add(start); + return end; +} + +// Interpolates a float by value t +float Lerp(float start, float end, float t) +{ + if (t <= 0) + return start; + if (t >= 1) + return end; + return (end - start) * t + start; +} +#endif } // namespace doengine \ No newline at end of file diff --git a/includes/WindowManager.h b/includes/WindowManager.h index 3403acb..8e7f2fd 100644 --- a/includes/WindowManager.h +++ b/includes/WindowManager.h @@ -12,40 +12,35 @@ class Renderer; class WindowManager { - public: WindowManager() { } WindowManager(const WindowManager&) = default; - virtual ~WindowManager() { } - virtual bool createWindow(const Rect& rect) = 0; - virtual bool createWindow() = 0; - virtual Renderer* getRenderer() = 0; - virtual bool isValid() = 0; - virtual void clearScreen(const Color& color) = 0; - virtual void setPincelColor(const Color& color) = 0; - virtual void updateScreen() = 0; - virtual void setFullScreen() = 0; - virtual void setWindowMode() = 0; - virtual void setSize(const Rect& rect) = 0; - virtual void* getNativeWindowFormatBuffer() = 0; - + virtual Rect getWindowDisplayMode(int m) = 0; static WindowManager* getWindowManager(); + + protected: + Renderer* render; + + bool run; + Rect window_rect; + Rect screen_rect; + bool support_opengl = false; }; } // namespace doengine \ No newline at end of file diff --git a/includes/constant.h b/includes/constant.h new file mode 100644 index 0000000..0c0c56b --- /dev/null +++ b/includes/constant.h @@ -0,0 +1,34 @@ +#ifndef CONSTANT_H_DEFINED +#define CONSTANT_H_DEFINED + +namespace doengine +{ + +enum class LayerId : int{ + BeginnerLayer=-1, + Background1=0, + Background2=1, + Background3=2, + Background4=3, + Background5=4, + BackgroundEffect1=5, + Tile1, + Tile2, + Tile3, + Tile4, + + Event1, + Event2, + Event3, + + UiContainer, + UiChild, + UiSubChild, + DebugScreen, + UltrHighEvent, + LastEvent +}; + +} + +#endif diff --git a/rerun.sh b/rerun.sh new file mode 100755 index 0000000..7cf635a --- /dev/null +++ b/rerun.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash + +# ========================= +# Configuration +# remember install inotify-tools +# ========================= + +# Folders to watch (add as many as needed) +WATCH_DIRS=( + "./src" + "./includes" + # "./third_party" +) + +BUILD_CMD="cmake --build ./build" # or: cmake --build build +EXECUTABLE="./build/DOENGINE-BattleSpawn" # path to your executable +PID_FILE="/tmp/app.pid" + +# ========================= +# Functions +# ========================= + +stop_app() { + if [[ -f "$PID_FILE" ]]; then + PID=$(cat "$PID_FILE") + if kill -0 "$PID" 2>/dev/null; then + echo "Stopping app (PID $PID)..." + kill "$PID" + wait "$PID" 2>/dev/null + fi + rm -f "$PID_FILE" + fi +} + +build_and_run() { + echo "Building..." + if $BUILD_CMD; then + echo "Build successful. Starting app..." + $EXECUTABLE & + echo $! > "$PID_FILE" + else + echo "Build failed." + fi +} + +cleanup() { + echo "Shutting down..." + stop_app + exit 0 +} + +trap cleanup SIGINT SIGTERM + +# ========================= +# Initial run +# ========================= +build_and_run + +# ========================= +# Watch loop +# ========================= +echo "Watching directories:" +printf ' %s\n' "${WATCH_DIRS[@]}" + +inotifywait -m -r \ + -e modify,create,delete,move \ + --format '%w%f' \ + "${WATCH_DIRS[@]}" | while read file; do + + if [[ "$file" =~ \.(c|cpp|h|hpp)$ ]]; then + echo "Change detected: $file" + stop_app + build_and_run + fi +done diff --git a/sample/BattleCityRecreation/BattleCityRecreation.cpp b/sample/BattleCityRecreation/BattleCityRecreation.cpp new file mode 100644 index 0000000..21a4637 --- /dev/null +++ b/sample/BattleCityRecreation/BattleCityRecreation.cpp @@ -0,0 +1,188 @@ +#include "GameObject.h" +#include "MessageBox.h" +#include "Application.h" +#include "DraggableGameObject.h" +#include "Event.h" +#include "FontCache.h" +#include "GameState.h" +#include "Logger.h" +#include "Mouse.h" +#include "SelectionRect.h" +#include "SpriteOffset.h" +#include "TTFText.h" +#include "Texture.h" +#include "Tile.h" +#include "Tilemap.h" +#include "UI.h" +#include "TextField.h" +#include "TileMapEditor.h" +#include "DOEngine_SDL_includes.h" +#include +#include +#include + + +using doengine::Application; +using doengine::Rect; +using doengine::Renderer; +using doengine::GameState; +using doengine::SpriteAnimationOffset; +using doengine::Color; +using doengine::KeyDownEvent; +using doengine::MouseEvent; +using doengine::KeyboardInputhandlingEvent; +using doengine::TextInputEvent; +using doengine::TileMapEditor; +using doengine::TileMap; +using doengine::NonScrollCharTileMap; +using doengine::TextureManager; +using doengine::TileCharTextureOffset; +using doengine::TileDef; +using doengine::Texture; +using doengine::SelectionRect; +using doengine::GameObject; +using doengine::checkCollision; + + + +struct BattleCityMainPlayer : + public GameObject//, + /////public KeyboardInputhandlingEvent +{ + virtual void Update(float timer = 0); + virtual void Render(); + +}; + +void BattleCityMainPlayer::Update(float timer) +{ + +} + +void BattleCityMainPlayer::Render() +{ + +} + + + + + + +struct PlayState : + public GameState, + public KeyboardInputhandlingEvent +{ + + NonScrollCharTileMap* tilemap; + Texture* texture; + SelectionRect *helperSelection; + + /// Player + BattleCityMainPlayer *player; + + void loadTileMapResource(); + void loadDebugResource(); + + + virtual void OnEnter(); + virtual void OnExit(); + virtual void Update(float elapsed); + virtual void Render(); + virtual void OnKeydown(const Keyboard&); + virtual void OnKeyup(const Keyboard&); +}; + +void PlayState::loadDebugResource() +{ + helperSelection = new SelectionRect(); + helperSelection->setFont("./assets/fonts/DroidSans.ttf"); + helperSelection->setFontColor(doengine::Colors::red); +} + +void PlayState::loadTileMapResource() +{ + tilemap = new NonScrollCharTileMap(); + TextureManager::getTextureManager()->loadTextureFromFile("battle_city_sprites", "/home/neonland/Documents/DOEngine/assets/gfx/sprites/battle_city.jpg"); + tilemap->loadTileMapFromFile("./assets/maps/testmap.map"); + tilemap->setTileSetId("battle_city_sprites"); + texture = TextureManager::getTextureManager()->getTexture("battle_city_sprites"); + std::unordered_map battleCitySpriteSheet1 + { + {'b', {'b', doengine::Rect{257, 00, 15, 15}}}, + {'w', {'w', doengine::Rect{257, 17, 15, 15}}}, + {'g', {'g', doengine::Rect{257, 36, 15, 15}}}, + {'B', {'B', doengine::Rect{257, 49, 15, 15}}}, + {'M', {'M', doengine::Rect{257, 97, 15, 15}}}, + {'C', {'C', doengine::Rect{273, 33, 15, 15}}}, + {'s', {'s', doengine::Rect{289, 33, 15, 15}}}, + {'t', {'t', doengine::Rect{259, 42, 15, 15}}}, + }; + std::unordered_map defs; + for(auto it : battleCitySpriteSheet1) + { + defs[it.first] = doengine::TileDef(); + defs[it.first].layer = 0; + defs[it.first].src = it.second.offset; + defs[it.first].textureId = "battle_city_sprites"; + } + tilemap->setTileDef(defs); +} + +void PlayState::OnEnter() +{ + loadTileMapResource(); + loadDebugResource(); + + player = new BattleCityMainPlayer(); + +} +void PlayState::OnExit() +{ + +} +void PlayState::Update(float elapsed) +{ + + player->Update(elapsed); +} +void PlayState::Render() +{ + tilemap->Draw(); + ///texture->Draw({0,0,texture->getWidth(),texture->getHeight()}); + ///helperSelection->render(nullptr); + player->Render(); +} +void PlayState::OnKeydown(const Keyboard&) +{ + +} +void PlayState::OnKeyup(const Keyboard&) +{ + +} + + +int main(int argc, char *argv[]) +{ + auto app = doengine::Application::getApplication(); + auto resoltion = doengine::Application::getApplication()->getDisplayMode(0); + doengine::Rect rect{resoltion.w/2, resoltion.h}; + app->createWindow(rect); + auto playstate = new PlayState(); + app->addState(playstate, 1); + app->setState(1); + + while (app->IsRunning()) + { + app->PollEvent(); + app->Update(); + app->clearScreen(doengine::Colors::black); + app->Render(); + } + app->Quit(); + return 0; + return 0; +} + + diff --git a/sample/BattleSpawn/BattleSpawn.cpp b/sample/BattleSpawn/BattleSpawn.cpp new file mode 100644 index 0000000..1927f3b --- /dev/null +++ b/sample/BattleSpawn/BattleSpawn.cpp @@ -0,0 +1,414 @@ + + +#include "Application.h" +#include "DraggableGameObject.h" +#include "Event.h" +#include "FontCache.h" +#include "GameState.h" +#include "Logger.h" +#include "Mouse.h" +#include "SelectionRect.h" +#include "SpriteOffset.h" +#include "TTFText.h" +#include "Texture.h" +#include "Tile.h" +#include "Tilemap.h" +#include "UI.h" +#include "TextField.h" +#include "TileMapEditor.h" +#include "DOEngine_SDL_includes.h" +#include +#include +#include +#include "GameObject.h" +#include "MessageBox.h" + + +enum State +{ + playing = 0 +}; + +struct Layer +{ + std::string spriteid; + doengine::Rect position; + doengine::Rect position2; + Layer(const std::string& sp) + { + spriteid = sp; + position.x = 0; + position.y = 0; + position.w = 2400; + position.h = 700; + position2.x=position.w; + + } +}; + +#define max_rows 20 +#define max_cols 31 +#define tilesize 32 +char this_map[max_rows][max_cols]= +{ + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "X o|o o o XX o o o| o X", + "X XXX XXXXX XXXXX XXXXX XXX X", + "XoXXX XXXXX XXXXX XXXXX XXXoX", + "X o|o ooooo o|o X", + "XoXXXoXX XXXXXXXXXXX XXoXXXoX", + "X |XX |XXX| XX X", + "XoXXXoXXXXXX XXX XXXXXXoXXXoX", + "X XXXoXX ooo|ooo|ooo XXoXXX X", + " o |XX XXXXXXXXXXX XX|ooo ", + "XoXXXoXX XXXXXXXXXXX XXoXXX X", + "XoXXXoXX oo |ooo|ooo XXoXXXoX", + "XoXXXoXXXXXX XXX XXXXXXoXXX X", + "X XXP XXX XX X", + "X XXXoXX XXXXXXXXXXX XXoXXX X", + "XoXXX| o| o o o o o |o |XXXoX", + "X XXXoXXXX XXXXXXXX XXX XXX X", + "XoXXXoXXXX XXX XXXoX", + "X o |o oX XXXXXXXX o o| o X", + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +}; + +constexpr const int FaceSize = 32; + +struct Player : + public doengine::GameObject, + public doengine::KeyboardInputhandlingEvent +{ + doengine::Rect position; + doengine::Rect clipset; + doengine::Renderer* renderer; + + int pointGathered = 0; + doengine::TTFText *font; + + + + enum class PlayerPosition{ + Up,Down,Left,Right,None, Stopped + }; + PlayerPosition current = PlayerPosition::None; + PlayerPosition previous = PlayerPosition::None; + doengine::TextureManager *textureManager = nullptr; + + Player() + { + renderer = doengine::Application::getApplication()->getRender(); + textureManager = doengine::TextureManager::getTextureManager(); + font = new doengine::TTFText(); + font->setFont("./assets/fonts/NirmalaB.ttf",20); + font->setColor(doengine::Colors::red); + } + + void setPosition(doengine::Rect rect) + { + position.x = rect.x; + position.y = rect.y; + position.w = rect.w; + position.h = rect.h; + } + + virtual void Update(float timer = 0) + { + switch (current) + { + case PlayerPosition::Up: + { + clipset.x = 2 * FaceSize; + clipset.y = 0 * FaceSize; + clipset.w = FaceSize; + clipset.h = FaceSize; + + int prev_row = (position.y / FaceSize) - 1; + int prev_col = (position.x / FaceSize); + char prevElement = this_map[prev_row][prev_col]; + /// if(prevElement == ' ' || prevElement =='o') + { + position.y -= FaceSize; + previous = current; + current = PlayerPosition::Stopped; + if(prevElement =='o') + { + this_map[prev_row][prev_col] = ' '; + pointGathered+=1; + } + } + } + break; + case PlayerPosition::Down: + { + clipset.x = 3 * FaceSize; + clipset.y = 0 * FaceSize; + clipset.w = FaceSize; + clipset.h = FaceSize; + int prev_row = (position.y / FaceSize) + 1; + int prev_col = (position.x / FaceSize); + char prevElement = this_map[prev_row][prev_col]; + /// if(prevElement == ' '||prevElement =='o') + { + position.y += FaceSize; + previous = current; + current = PlayerPosition::Stopped; + if(prevElement =='o') + { + this_map[prev_row][prev_col] = ' '; + pointGathered+=1; + } + } + } + break; + case PlayerPosition::Left: + { + clipset.x = 0 * FaceSize; + clipset.y = 0 * FaceSize; + clipset.w = FaceSize; + clipset.h = FaceSize; + int prev_row = (position.y / FaceSize); + int prev_col = (position.x / FaceSize) - 1; + char prevElement = this_map[prev_row][prev_col]; + // if(prevElement == ' '||prevElement =='o') + { + position.x -= FaceSize; + previous = current; + current = PlayerPosition::Stopped; + if(prevElement =='o') + { + this_map[prev_row][prev_col] = ' '; + pointGathered+=1; + } + } + } + break; + case PlayerPosition::Right: + { + clipset.x = 1 * FaceSize; + clipset.y = 0 * FaceSize; + clipset.w = FaceSize; + clipset.h = FaceSize; + int prev_row = (position.y / FaceSize); + int prev_col = (position.x / FaceSize) + 1; + char prevElement = this_map[prev_row][prev_col]; + /// if(prevElement == ' '||prevElement =='o') + { + position.x += FaceSize; + previous = current; + current = PlayerPosition::Stopped; + if(prevElement =='o') + { + this_map[prev_row][prev_col] = ' '; + pointGathered+=1; + } + } + } + break; + case PlayerPosition::None: + { + clipset.x = 3 * FaceSize; + clipset.y = 0 * FaceSize; + clipset.w = FaceSize; + clipset.h = FaceSize; + } + break; + } + previous = current; + current = PlayerPosition::Stopped; + } + virtual void Render() + { + textureManager->getTexture("pacman_player_1")->Draw(position,clipset); + font->DrawText(1000,100,"Score:"); + font->setColor(doengine::Colors::yellow1); + font->DrawText(1090,100," %d", this->pointGathered); + } + + virtual void OnKeydown(const Keyboard& keys) + { + auto k = keys.getKeysBitset(); + if(k[doengine::SCANCODE_LEFT]) + { + previous = current; + current = PlayerPosition::Left; + } + if(k[doengine::SCANCODE_RIGHT]) + { + previous = current; + current = PlayerPosition::Right; + } + if(k[doengine::SCANCODE_UP]) + { + previous = current; + current = PlayerPosition::Up; + + + } + if(k[doengine::SCANCODE_DOWN]) + { + previous = current; + current = PlayerPosition::Down; + } + + } + virtual void OnKeyup(const Keyboard&) + { + + } +}; + + + +struct PlayState : public doengine::GameState, + public doengine::KeyboardInputhandlingEvent, + public doengine::MouseEvent +{ + doengine::Renderer* renderer; + + doengine::TextureManager *textureManager = nullptr; + + + Player* player= nullptr; + + PlayState() + { + } + + virtual ~PlayState() + { + } + + + + + virtual void OnEnter() + { + + textureManager = doengine::TextureManager::getTextureManager(); + doengine::TextureManager::getTextureManager()->loadTextureFromFile("pacman_block_1","./assets/gfx/roca.bmp"); + doengine::TextureManager::getTextureManager()->loadTextureFromFile("pacman_food_1","./assets/gfx/Comida.bmp"); + doengine::TextureManager::getTextureManager()->loadTextureFromFile("pacman_player_1","./assets/gfx/pacman.bmp"); + player = new Player; + + + //doengine::TextureManager::getTextureManager()->loadTextureFromFile("layer1","./assets/backgroud/layer-1.png"); + //doengine::TextureManager::getTextureManager()->loadTextureFromFile("layer2","./assets/bakground/layer-2.png"); + //doengine::TextureManager::getTextureManager()->loadTextureFromFile("layer3","./assets/bakground/layer-3.png"); + //doengine::TextureManager::getTextureManager()->loadTextureFromFile("layer4","./assets/bakground/layer-4.png"); + //doengine::TextureManager::getTextureManager()->loadTextureFromFile("layer5","./assets/bakground/layer-5.png"); + + } + + void DrawLayer1() + { + textureManager->getTexture("layer1")->Draw(0,0); + } + + void DrawLayer2() + { + + } + + void DrawLayer3() + { + + + } + virtual void MouseWheel(const Mouse&) + {} + virtual void MouseMove(const Mouse& mouse) + { + } + + virtual void MouseButtonDown(const Mouse& mouse) + { + } + + virtual void MouseButtonUp(const Mouse& mouse) + { + } + + virtual void OnKeydown(const Keyboard& keyboard) + { + } + + virtual void OnKeyup(const Keyboard&) + { + } + + virtual void OnExit() + { + } + + virtual void Update(float elapsed) + { + player->Update(elapsed); + } + + + void RenderMap() + { + + for(int r=0;rgetTexture("pacman_block_1")->Draw(rect); + /* code */ + break; + case 'o': + case 'O': + textureManager->getTexture("pacman_food_1")->Draw(rect); + /* code */ + break; + case 'P': + { + this_map[r][c] = 'R'; + player->setPosition(rect); + } + default: + break; + } + + ///pacman_block_1 + } + } +} + virtual void Render() + { + RenderMap(); + player->Render(); + + } +}; + +int main(int argc, char* argv[]) +{ + auto app = doengine::Application::getApplication(); + auto resoltion = doengine::Application::getApplication()->getDisplayMode(0); + doengine::Rect rect{1320, 900}; + app->createWindow(rect); + auto playstate = new PlayState(); + app->addState(playstate, playing); + app->setState(playing); + + while (app->IsRunning()) + { + app->PollEvent(); + app->Update(); + app->clearScreen(doengine::Colors::black); + app->Render(); + } + app->Quit(); + return 0; +} diff --git a/sample/BattleSpawn/BattleSpawn_ok.cpp b/sample/BattleSpawn/BattleSpawn_ok.cpp new file mode 100644 index 0000000..4133550 --- /dev/null +++ b/sample/BattleSpawn/BattleSpawn_ok.cpp @@ -0,0 +1,289 @@ + + +#include "Application.h" +#include "DraggableGameObject.h" +#include "Event.h" +#include "FontCache.h" +#include "GameState.h" +#include "Logger.h" +#include "Mouse.h" +#include "SelectionRect.h" +#include "SpriteOffset.h" +#include "TTFText.h" +#include "Texture.h" +#include "Tile.h" +#include "Tilemap.h" +#include "UI.h" +#include "TextField.h" +#include "TileMapEditor.h" +#include "DOEngine_SDL_includes.h" +#include +#include +#include +#include "GameObject.h" +#include "MessageBox.h" + + +enum State +{ + playing = 0 +}; +const std::string sprite = + "/home/neon/Desktop/SimpleServer/DOEngine/assets/gfx/sprites/spriterpg.png"; +const std::string fontsrc = + "/home/neon/Desktop/SimpleServer/DOEngine/assets/fonts/NirmalaB.ttf"; + +enum class TextureId +{ + FontSpriteAtlas = 1 +}; + +enum SpriteOffsetPosition +{ + FW, + BW +}; + +doengine::SpriteAnimationOffset sampleSprite{FW, + { + {580 * 1, 0, 580, 532}, + {580 * 1, 0, 580, 532}, + {580 * 2, 0, 580, 532}, + {580 * 3, 0, 580, 532}, + {580 * 4, 0, 580, 532}, + {580 * 5, 0, 580, 532}, + }}; + + + + + +std::unordered_map battleCitySpriteSheet1 +{ + {'b', {'b', doengine::Rect{45, 44, 123, 113}}}, + {'w', {'w', doengine::Rect{56, 401, 124, 103}}}, + {'g', {'g', doengine::Rect{49, 212, 135, 114}}}, + {'B', {'B', doengine::Rect{298, 219, 125, 112}}}, + {'M', {'M', doengine::Rect{530, 197, 220, 151}}}, + {'C', {'C', doengine::Rect{786, 196, 194, 138}}}, + {'s', {'s', doengine::Rect{786, 196, 194, 138}}}, + {'t', {'t', doengine::Rect{786, 196, 194, 138}}}, +}; + + +std::unordered_map battle_city_original_sheet +{ + {'b', {'b', doengine::Rect{256, 0, 15, 15}}}, + {'b', {'b', doengine::Rect{256, 0, 15, 15}}}, + {'b', {'b', doengine::Rect{256, 0, 15, 15}}}, + {'b', {'b', doengine::Rect{256, 0, 15, 15}}}, + {'b', {'b', doengine::Rect{256, 0, 15, 15}}}, + {'b', {'b', doengine::Rect{256, 0, 15, 15}}}, + +}; + + + + +struct Player : + public doengine::GameObject, + public doengine::KeyboardInputhandlingEvent +{ + doengine::Rect playerPos; + doengine::Color playerColor; + doengine::Texture *sprite; + + Player() + { + + } + + void setPlayerPosition(doengine::Rect pos) + { + playerPos = pos; + } + + virtual void Update(float timer = 0) + { + + } + virtual void Render() + { + + } + + virtual void OnKeydown(const Keyboard& keyboard) + { + + } + virtual void OnKeyup(const Keyboard&) + { + + } +}; + + + + +struct PlayState : public doengine::GameState, + public doengine::KeyboardInputhandlingEvent, + public doengine::MouseEvent +{ + doengine::Renderer* renderer; + + doengine::SelectionRect recti; + + doengine::TileMapEditor* mapEditor; + + doengine::NonScrollCharTileMap *tilemap; + + doengine::TextField* field; + + doengine::MessageBox* msgBox; + + + doengine::TTFText *textCoords; + doengine::Rect clipped; + + PlayState() + { + } + + virtual ~PlayState() + { + } + + + + + virtual void OnEnter() + { + + + doengine::TextureManager::getTextureManager()->loadTextureFromFile( + "battle_city_sprites", + "./assets/gfx/sprites/battle_city.jpg"); + textCoords = new doengine::TTFText(); + textCoords->setFont("/home/neonland/Documents/DOEngine/build/assets/fonts/NirmalaB.ttf", 20); + ////home/neonland/Documents/DOEngine/assets/fonts/NirmalaB.ttf + recti.setFont("/home/neonland/Documents/DOEngine/build/assets/fonts/NirmalaB.ttf"); + renderer = doengine::Application::getApplication()->getRender(); + doengine::TextureManager::getTextureManager()->loadTextureFromFile( + "sprite1", "/home/neonland/Pictures/shadow_dog.png"); + doengine::TextureManager::getTextureManager()->loadTextureFromFile( + "save1", "./assets/gfx/save.png"); + doengine::TextureManager::getTextureManager()->loadTextureFromFile( + "tileset1", "./assets/gfx/battle_city_tileset_1.png"); + mapEditor = new doengine::TileMapEditor("/home/neonland/Documents/DOEngine/build/assets/fonts/NirmalaB.ttf"); + mapEditor->setup("tileset1", battle_city_original_sheet); + tilemap = new doengine::NonScrollCharTileMap(); + tilemap->loadTileMapFromFile("./assets/maps/testmap.map"); + tilemap->setTileMapEditor(mapEditor); + std::unordered_map defs; + for(auto it : battleCitySpriteSheet1) + { + defs[it.first] = doengine::TileDef(); + defs[it.first].layer = 0; + defs[it.first].src = it.second.offset; + defs[it.first].textureId = "tileset1"; + } + tilemap->setTileDef(defs); + + recti.registerSelectionRectFinished([this](doengine::Rect rect){ + tilemap->changeGroupOfTileByRectCollision(tilemap->getEditorType(), rect); + clipped = rect; + }); + + msgBox = new doengine::MessageBox("QUIERES GUARDAR ESTO? ", + "/home/neonland/Documents/DOEngine/build/assets/fonts/NirmalaB.ttf", + [this](int id){ + msgBox->hide(); + }); + + textCoords->setForegroundColor(doengine::Colors::red); + } + + void DrawLayer1() + { + + } + + void DrawLayer2() + { + + } + + void DrawLayer3() + { + tilemap->Draw(); + //// mapEditor->render(); + // + /// auto battle_city_text = + ///doengine::TextureManager::getTextureManager()->getTexture("battle_city_sprites"); + ////battle_city_text->Draw({0,0, battle_city_text->getWidth()*2, battle_city_text->getHeight()*2}); + //// if(!mapEditor->isVisible()) + ///rect.setFontColor(doengine::Colors::green); + ///rect.render(renderer); + //// + ///// msgBox->render(); + + ////textCoords->DrawText(300,600,"[%d, %d, %d %d]", clipped.x/2, clipped.y/2, clipped.w/2, clipped.h/2); + } + + virtual void MouseMove(const Mouse& mouse) + { + } + + virtual void MouseButtonDown(const Mouse& mouse) + { + } + + virtual void MouseButtonUp(const Mouse& mouse) + { + } + + virtual void OnKeydown(const Keyboard& keyboard) + { + } + + virtual void OnKeyup(const Keyboard&) + { + } + + virtual void OnExit() + { + } + + virtual void Update(float elapsed) + { + tilemap->Update(elapsed); + } + + virtual void Render() + { + DrawLayer1(); + DrawLayer2(); + DrawLayer3(); + } +}; + +int main(int argc, char* argv[]) +{ + auto app = doengine::Application::getApplication(); + auto resoltion = doengine::Application::getApplication()->getDisplayMode(0); + doengine::Rect rect{resoltion.w, resoltion.h}; + app->createWindow(rect); + auto playstate = new PlayState(); + app->addState(playstate, playing); + app->setState(playing); + + while (app->IsRunning()) + { + app->PollEvent(); + app->Update(); + app->clearScreen(doengine::Color(144, 240, 252, 255)); + app->Render(); + } + app->Quit(); + return 0; +} diff --git a/sample/BattleSpawn/BattleSpawn_with_Selection_added.cpp b/sample/BattleSpawn/BattleSpawn_with_Selection_added.cpp new file mode 100644 index 0000000..4649969 --- /dev/null +++ b/sample/BattleSpawn/BattleSpawn_with_Selection_added.cpp @@ -0,0 +1,210 @@ + +#include +#include "TTFText.h" +#include "Texture.h" +#include "Application.h" +#include "GameState.h" +#include "Mouse.h" +#include "UI.h" +#include "Event.h" +#include "Tile.h" +#include "TTFText.h" +#include +#include "DOEngine_SDL_includes.h" +#include "DraggableGameObject.h" +#include "SelectionRect.h" + +enum State +{ + playing = 0 +}; +const std::string sprite = "/home/neon/Desktop/SimpleServer/DOEngine/assets/gfx/sprites/spriterpg.png"; +const std::string fontsrc = "/home/neon/Desktop/SimpleServer/DOEngine/assets/fonts/NirmalaB.ttf"; + +enum class TextureId{ + FoodSpriteAtlas = 1 +}; + +enum SpriteOffset{ + Sandwich = 1, + Bistec +}; + + + +std::unordered_map spriteOffsets{ + { SpriteOffset::Sandwich , doengine::Rect{ 147, 17, 82, 48} }, + { SpriteOffset::Bistec , doengine::Rect{ 186, 128, 55, 31} } +}; + + + + +struct PlayState : + public doengine::GameState, + public doengine::KeyboardInputhandlingEvent, + public doengine::MouseEvent +{ + + std::vector gamesamples; + doengine::Texture *bg; + doengine::Texture *stuff1; + + + doengine::SelectionRect *selected; + + + + PlayState() + { + + } + + virtual ~PlayState() + { + + } + + doengine::Renderer* renderer; + + virtual void OnEnter() + { + renderer = doengine::Application::getApplication()->getRender(); + + doengine::TextureManager::getTextureManager()->loadTextureFromFile( + ((int)(TextureId::FoodSpriteAtlas)), + "/home/neon/Desktop/SimpleServer/DOEngine/assets/tocaboca_spite_test.jpg" + ); + + for(int i= 0; i < 100; i++) + { + + auto dGameObject = new doengine::DraggableGameObject( + rand()%600+1, + rand()%500+1, + 1, + 1 + ); + dGameObject->setSpriteOffset(spriteOffsets); + gamesamples.push_back(dGameObject); + } + + + bg = new doengine::Texture("/home/neon/Desktop/SimpleServer/DOEngine/assets/homebg1.jpg"); + stuff1 = new doengine::Texture("/home/neon/Desktop/SimpleServer/DOEngine/assets/tocaboca_spite_test.jpg"); + + selected = new doengine::SelectionRect(); + selected->setFont(fontsrc); + + } + + virtual void MouseMove(const Mouse& mouse){ + for(auto it : gamesamples){ + it->updateCoords(mouse.getMousePosition()); + } + } + + virtual void MouseButtonDown(const Mouse& mouse){ + for(auto it : gamesamples){ + it->setSelectedIfCollided(mouse.getMousePosition()); + } + } + + virtual void MouseButtonUp(const Mouse& mouse){ + for(auto it : gamesamples){ + it->setSelected(false); + } + } + + virtual void OnKeydown(const Keyboard& keyboard) + { + + auto key = keyboard.getKeysBitset(); + + if(key[SDL_SCANCODE_ESCAPE]) + { + doengine::Application::getApplication()->Quit(); + } + + if(key[SDL_SCANCODE_Q]) + { + + } + + if(key[SDL_SCANCODE_W]) + { + + } + + if(key[SDL_SCANCODE_DOWN]) + { + + } + if(key[SDL_SCANCODE_RIGHT]) + { + + } + + if(key[SDL_SCANCODE_UP]) + { + + } + if(key[SDL_SCANCODE_LEFT]) + { + + } + + } + + virtual void OnKeyup(const Keyboard&){} + + virtual void OnExit(){} + + virtual void Update(float elapsed) + { + ///panel.Update(); + } + + virtual void Render() + { + doengine::Rect bgRect{ + 10, + 200, + doengine::Application::getApplication()->getW(), + doengine::Application::getApplication()->getH()/2 + }; + + renderer->DrawFillRect({0,0,100,100}, doengine::Colors::blue5); + //// bg->Draw(bgRect); + stuff1->Draw({0,0,499,347}); + + for(auto it : gamesamples){ + it->render(renderer); + } + selected->render(renderer); + } + + +}; + + +int main(int argc, char *argv[]) +{ + auto app = doengine::Application::getApplication(); + doengine::Rect rect{1200, 980}; + app->createWindow(rect); + auto playstate = new PlayState(); + app->addState( playstate, playing); + app->setState(playing ); + + while (app->IsRunning()) + { + app->PollEvent(); + app->Update(); + app->clearScreen(doengine::Color(144, 240, 252, 255)); + app->Render(); + + } + app->Quit(); + return 0; +} \ No newline at end of file diff --git a/sample/CMakeLists.txt b/sample/CMakeLists.txt index 3dbcc44..e747896 100644 --- a/sample/CMakeLists.txt +++ b/sample/CMakeLists.txt @@ -17,6 +17,24 @@ add_executable(Sound Sound.cpp ) +add_executable(Particles + Particles/Particles.cpp +) +add_executable(VideoPlayer + VideoPlayer/VideoPlayer.cpp +) + +add_executable(SnakeGame + SnakeGame/SnakeGame.cpp +) + +add_executable(AnnetteWorld + AnnetteWorld/AnnetteWorld.cpp +) + + + + file(COPY ${ASSETS_DIR} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) @@ -40,4 +58,41 @@ target_link_libraries(Sound PRIVATE SDL2 SDL2main SDL2_ttf SDL2_image - SDL2_mixer) \ No newline at end of file + SDL2_mixer) + +target_link_libraries(Particles PRIVATE + DOENGINE + SDL2 + SDL2main + SDL2_ttf SDL2_image + SDL2_mixer) + + +target_link_libraries(SnakeGame PRIVATE + DOENGINE + SDL2 + SDL2main + SDL2_ttf SDL2_image + SDL2_mixer) + + +target_link_libraries(AnnetteWorld PRIVATE + DOENGINE + SDL2 + SDL2main + SDL2_ttf SDL2_image + SDL2_mixer) + +# sudo apt install libsdl2-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev +target_link_libraries(VideoPlayer PRIVATE + DOENGINE + SDL2 + SDL2main + SDL2_ttf + SDL2_image + SDL2_mixer + avformat + avcodec + avutil + swscale + ) \ No newline at end of file diff --git a/sample/MusicPlayer/MusicPlayer.cpp b/sample/MusicPlayer/MusicPlayer.cpp index d147604..7661a00 100644 --- a/sample/MusicPlayer/MusicPlayer.cpp +++ b/sample/MusicPlayer/MusicPlayer.cpp @@ -4,7 +4,7 @@ #include "TTFText.h" #include #include -#include +#include "DOEngine_SDL_includes.h" #include #include #include @@ -42,7 +42,9 @@ struct MusicPlayerState : public doengine::GameState /// parcial = ps->subTexture(Rect{20,10,10,10}); text =new TTFText(); /// text->setFont("./assets/fonts/DroidSans.ttf", 23); - fontRenderer = text->createBitmapFont("./assets/fonts/DroidSans.ttf", yellow, red); + fontRenderer = text->createBitmapFont("./assets/fonts/DroidSans.ttf", + doengine::Colors::yellow, + doengine::Colors::red); } virtual void OnExit() { @@ -59,7 +61,7 @@ struct MusicPlayerState : public doengine::GameState } virtual void Render() { - renderer->setDrawColor(skyBlue); + renderer->setDrawColor(doengine::Colors::skyBlue); renderer->clear(); // ps->Draw(Rect{100,100,100,100}); // parcial->Draw(Rect{10,10,100,100}); diff --git a/sample/Pacman/Pacman.cpp b/sample/Pacman/Pacman.cpp new file mode 100644 index 0000000..2223e30 --- /dev/null +++ b/sample/Pacman/Pacman.cpp @@ -0,0 +1,505 @@ + + +#include "Application.h" +#include "StringUtils.h" +#include "DraggableGameObject.h" +#include "Event.h" +#include "FontCache.h" +#include "GameState.h" +#include "Logger.h" +#include "Mouse.h" +#include "SelectionRect.h" +#include "SpriteOffset.h" +#include "TTFText.h" +#include "Texture.h" +#include "Tile.h" +#include "Tilemap.h" +#include "UI.h" +#include "TextField.h" +#include "TileMapEditor.h" +#include "Utils.h" +#include "GameObject.h" +#include "MessageBox.h" +#include "DOEngine_SDL_includes.h" +#include +#include +#include + +enum State +{ + playing = 0 +}; + +struct Layer +{ + std::string spriteid; + doengine::Rect position; + doengine::Rect position2; + Layer(const std::string& sp) + { + spriteid = sp; + position.x = 0; + position.y = 0; + position.w = 2400; + position.h = 700; + position2.x=position.w; + + } +}; + +#define max_rows 20 +#define max_cols 31 +#define tilesize 32 +char this_map[max_rows][max_cols]= +{ + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "Xeo|o o o XXe o o o| oe XX", + "X XXX XXXXX XXXXXXXXXXXXXXXXX", + "XoXXX XXXXX XXXXX XXXXX XXXoX", + "X o|o ooooo o|o X", + "XoXXXoXX XXXXXXXXXXX XXoXXXoX", + "X | |XXX| XX X", + "XoXXXoXX XXX XX XXoXXXoX", + "X XXXoXX ooo|ooo|ooo XXoXXX X", + "Xo |XX XXXXXXXXXXX XX|oooX", + "XoXXXoXX XXXXXXXXXXX XXoXXX X", + "XoXXXoXX oo |ooo|ooo XXoXXXoX", + "XoXXXoXX XXX XXX XXX XXoXXX X", + "X X P XXX X", + "X XXXoXX XXXXXXXXXXX XXoXXX X", + "XoXXX| o| o o o o o |o |XXXoX", + "X XXXoX XX XXXXXXXX XXX ooooX", + "XoXXXoX XXooooooooooXXX Xoo X", + "X o |o oX XXXXXXXX ooo|ooooX", + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +}; + +constexpr const int FaceSize = 32; + +struct Player : + public doengine::GameObject, + public doengine::KeyboardInputhandlingEvent +{ + doengine::Rect position; + doengine::Rect clipset; + doengine::Renderer* renderer; + + int pointGathered = 0; + doengine::TTFText *font; + + + + enum class PlayerPosition{ + Up,Down,Left,Right,None, Stopped + }; + PlayerPosition current = PlayerPosition::None; + PlayerPosition previous = PlayerPosition::None; + doengine::TextureManager *textureManager = nullptr; + + Player() + { + renderer = doengine::Application::getApplication()->getRender(); + textureManager = doengine::TextureManager::getTextureManager(); + font = new doengine::TTFText(); + font->setFont("./assets/fonts/NirmalaB.ttf",20); + font->setColor(doengine::Colors::red); + } + + void setPosition(doengine::Rect rect) + { + position.x = rect.x; + position.y = rect.y; + position.w = rect.w; + position.h = rect.h; + } + + virtual void Update(float timer = 0) + { + switch (current) + { + case PlayerPosition::Up: + { + clipset.x = 2 * FaceSize; + clipset.y = 0 * FaceSize; + clipset.w = FaceSize; + clipset.h = FaceSize; + + int prev_row = (position.y / FaceSize) - 1; + int prev_col = (position.x / FaceSize); + char prevElement = this_map[prev_row][prev_col]; + //if(prevElement == ' ' || prevElement =='o'||prevElement =='|') + if(doengine::isCharInGroup(prevElement," oPp|EeRr")) + { + position.y -= FaceSize; + previous = current; + current = PlayerPosition::Stopped; + if(prevElement =='o') + { + this_map[prev_row][prev_col] = ' '; + pointGathered+=1; + } + } + } + break; + case PlayerPosition::Down: + { + clipset.x = 3 * FaceSize; + clipset.y = 0 * FaceSize; + clipset.w = FaceSize; + clipset.h = FaceSize; + int prev_row = (position.y / FaceSize) + 1; + int prev_col = (position.x / FaceSize); + char prevElement = this_map[prev_row][prev_col]; + //if(prevElement == ' '||prevElement =='o'||prevElement =='|') + if(doengine::isCharInGroup(prevElement," oPp|EeRr")) + { + position.y += FaceSize; + previous = current; + current = PlayerPosition::Stopped; + if(prevElement =='o') + { + this_map[prev_row][prev_col] = ' '; + pointGathered+=1; + } + } + } + break; + case PlayerPosition::Left: + { + clipset.x = 0 * FaceSize; + clipset.y = 0 * FaceSize; + clipset.w = FaceSize; + clipset.h = FaceSize; + int prev_row = (position.y / FaceSize); + int prev_col = (position.x / FaceSize) - 1; + char prevElement = this_map[prev_row][prev_col]; + ///if(prevElement == ' '||prevElement =='o'||prevElement =='|') + if(doengine::isCharInGroup(prevElement," oPp|EeRr")) + { + position.x -= FaceSize; + previous = current; + current = PlayerPosition::Stopped; + if(prevElement =='o') + { + this_map[prev_row][prev_col] = ' '; + pointGathered+=1; + } + } + } + break; + case PlayerPosition::Right: + { + clipset.x = 1 * FaceSize; + clipset.y = 0 * FaceSize; + clipset.w = FaceSize; + clipset.h = FaceSize; + int prev_row = (position.y / FaceSize); + int prev_col = (position.x / FaceSize) + 1; + char prevElement = this_map[prev_row][prev_col]; + //if(prevElement == ' '||prevElement =='o' ||prevElement =='|') + if(doengine::isCharInGroup(prevElement," oPp|EeRr")) + { + position.x += FaceSize; + previous = current; + current = PlayerPosition::Stopped; + if(prevElement =='o') + { + this_map[prev_row][prev_col] = ' '; + pointGathered+=1; + } + } + } + break; + case PlayerPosition::None: + { + clipset.x = 3 * FaceSize; + clipset.y = 0 * FaceSize; + clipset.w = FaceSize; + clipset.h = FaceSize; + } + break; + } + previous = current; + current = PlayerPosition::Stopped; + } + virtual void Render() + { + textureManager->getTexture("pacman_player_1")->Draw(position,clipset); + font->DrawText(1000,100,"Score:"); + font->setColor(doengine::Colors::yellow1); + font->DrawText(1090,100," %d", this->pointGathered); + } + + virtual void OnKeydown(const Keyboard& keys) + { + auto k = keys.getKeysBitset(); + if(k[doengine::SCANCODE_LEFT]) + { + previous = current; + current = PlayerPosition::Left; + } + if(k[doengine::SCANCODE_RIGHT]) + { + previous = current; + current = PlayerPosition::Right; + } + if(k[doengine::SCANCODE_UP]) + { + previous = current; + current = PlayerPosition::Up; + + + } + if(k[doengine::SCANCODE_DOWN]) + { + previous = current; + current = PlayerPosition::Down; + } + + } + virtual void OnKeyup(const Keyboard&) + { + + } +}; + +struct Enemy : + public doengine::GameObject +{ + bool alive; + doengine::Rect offset; + doengine::TextureManager *textureManager = nullptr; + float currentSpeed = 0.2f; + enum class EnemyPosition{ + Up,Down,Left,Right,None,Stopped,Frozen + }; + EnemyPosition prevMovement; + EnemyPosition current; + EnemyPosition getNextPosition(){ + int rnd = rand()%4+0; + switch (rnd) + { + case 0: return EnemyPosition::Down; + case 1: return EnemyPosition::Up; + case 2: return EnemyPosition::Left; + case 3: return EnemyPosition::Right; + } + return EnemyPosition::Stopped; + } + + Enemy(doengine::Rect offset) + { + textureManager = doengine::TextureManager::getTextureManager(); + this->offset = offset; + current = getNextPosition(); + } + virtual void Update(float timer ) + { + int row = offset.y / tilesize; + int col = offset.x / tilesize; + int max_x = max_rows * tilesize; + prevMovement = current; + if(current == EnemyPosition::Right) + { + if(this_map[row][col+1] =='X' || this_map[row][col+1] =='|') + current = getNextPosition(); + else + { + offset.x += tilesize; + } + } + if(current == EnemyPosition::Left) + { + if(this_map[row][col-1] =='X' || this_map[row][col-1] =='|') + current = getNextPosition(); + else + { + offset.x -= tilesize; + } + } + if(current == EnemyPosition::Up) + { + if(this_map[row-1][col] =='X' || this_map[row-1][col] =='|') + current = getNextPosition(); + else + { + offset.y -= tilesize; + } + } + if(current == EnemyPosition::Down) + { + if(this_map[row+1][col] =='X' || this_map[row+1][col] =='|') + current = getNextPosition(); + else + { + offset.y += tilesize; + } + } + + } + virtual void Render() + { + textureManager->getTexture("pacman_enemy_1")->Draw(offset); + } + +}; + + + +struct PlayState : public doengine::GameState, + public doengine::KeyboardInputhandlingEvent, + public doengine::MouseEvent +{ + doengine::Renderer* renderer; + + doengine::TextureManager *textureManager = nullptr; + + + Player* player= nullptr; + std::vector enemies; + + PlayState() + { + } + + virtual ~PlayState() + { + } + + + + + virtual void OnEnter() + { + + textureManager = doengine::TextureManager::getTextureManager(); + doengine::TextureManager::getTextureManager()->loadTextureFromFile("pacman_block_1" ,"./assets/gfx/roca.bmp"); + doengine::TextureManager::getTextureManager()->loadTextureFromFile("pacman_food_1" ,"./assets/gfx/Comida.bmp"); + doengine::TextureManager::getTextureManager()->loadTextureFromFile("pacman_enemy_1" ,"./assets/gfx/enemigo.bmp"); + doengine::TextureManager::getTextureManager()->loadTextureFromFile("pacman_player_1","./assets/gfx/pacman.bmp"); + player = new Player; + + } + + void DrawLayer1() + { + textureManager->getTexture("layer1")->Draw(0,0); + } + + void DrawLayer2() + { + + } + + void DrawLayer3() + { + + + } + virtual void MouseWheel(const Mouse&) + {} + virtual void MouseMove(const Mouse& mouse) + { + } + + virtual void MouseButtonDown(const Mouse& mouse) + { + } + + virtual void MouseButtonUp(const Mouse& mouse) + { + } + + virtual void OnKeydown(const Keyboard& keyboard) + { + } + + virtual void OnKeyup(const Keyboard&) + { + } + + virtual void OnExit() + { + } + + virtual void Update(float elapsed) + { + player->Update(elapsed); + for(auto enemy : enemies) + enemy->Update(elapsed); + } + + + void RenderMap() + { + + for(int r=0;renemies.push_back(enemy); + this_map[r][c]='E'; + } + + case 'x': + case 'X': + textureManager->getTexture("pacman_block_1")->Draw(rect); + /* code */ + break; + case 'o': + case 'O': + textureManager->getTexture("pacman_food_1")->Draw(rect); + /* code */ + break; + case 'P': + { + this_map[r][c] = 'R'; + player->setPosition(rect); + } + default: + break; + } + + ///pacman_block_1 + } + } +} + virtual void Render() + { + RenderMap(); + player->Render(); + for(auto e : enemies) + e->Render(); + } +}; + +int main(int argc, char* argv[]) +{ + auto app = doengine::Application::getApplication(); + auto resoltion = doengine::Application::getApplication()->getDisplayMode(0); + doengine::Rect rect{1320, 900}; + app->createWindow(rect); + auto playstate = new PlayState(); + app->addState(playstate, playing); + app->setState(playing); + + while (app->IsRunning()) + { + app->PollEvent(); + app->Update(); + app->clearScreen(doengine::Colors::black); + app->Render(); + } + app->Quit(); + return 0; +} diff --git a/sample/Particles/Particles.cpp b/sample/Particles/Particles.cpp new file mode 100644 index 0000000..195ce44 --- /dev/null +++ b/sample/Particles/Particles.cpp @@ -0,0 +1,9 @@ + + + +int main(int argc, char *argv[]) +{ + + + return 0; +} \ No newline at end of file diff --git a/sample/SDL2O/SDL2O.cpp b/sample/SDL2O/SDL2O.cpp new file mode 100644 index 0000000..42dfd7c --- /dev/null +++ b/sample/SDL2O/SDL2O.cpp @@ -0,0 +1,203 @@ +#include "DOEngine_SDL_includes.h" +//// g++ ./SDL2O.cpp -lSDL2 -lGL + +static PFNGLGENVERTEXARRAYSPROC glGenVertexArrays; +static PFNGLBINDVERTEXARRAYPROC glBindVertexArray; +static PFNGLGENBUFFERSPROC glGenBuffers; +static PFNGLBINDBUFFERPROC glBindBuffer; +static PFNGLBUFFERDATAPROC glBufferData; +static PFNGLBUFFERSUBDATAPROC glBufferSubData; +static PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; +static PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray; +static PFNGLCREATESHADERPROC glCreateShader; +static PFNGLSHADERSOURCEPROC glShaderSource; +static PFNGLCOMPILESHADERPROC glCompileShader; +static PFNGLCREATEPROGRAMPROC glCreateProgram; +static PFNGLATTACHSHADERPROC glAttachShader; +static PFNGLLINKPROGRAMPROC glLinkProgram; +static PFNGLUSEPROGRAMPROC glUseProgram; +static PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation; +static PFNGLUNIFORM4FPROC glUniform4f; +static PFNGLUNIFORM2FPROC glUniform2f; +//// static PFNGLDRAARRAYSPROC glDrawArrays; +static PFNGLDELETESHADERPROC glDeleteShader; + +bool LoadMinimalGL() +{ +#define LOAD_GL(fn) \ + fn = (decltype(fn))SDL_GL_GetProcAddress(#fn); \ + if (!fn) return false; + + + LOAD_GL(glGenVertexArrays) + LOAD_GL(glBindVertexArray) + LOAD_GL(glGenBuffers) + LOAD_GL(glBindBuffer) + LOAD_GL(glBufferData) + LOAD_GL(glBufferSubData) + LOAD_GL(glVertexAttribPointer) + LOAD_GL(glEnableVertexAttribArray) + LOAD_GL(glCreateShader) + LOAD_GL(glShaderSource) + LOAD_GL(glCompileShader) + LOAD_GL(glCreateProgram) + LOAD_GL(glAttachShader) + LOAD_GL(glLinkProgram) + LOAD_GL(glUseProgram) + LOAD_GL(glGetUniformLocation) + LOAD_GL(glUniform4f) + LOAD_GL(glUniform2f) + /// LOAD_GL(glDrawArrays) + LOAD_GL(glDeleteShader) + +#undef LOAD_GL + return true; +} + +struct RectRenderer +{ + GLuint vao; + GLuint vbo; + GLuint program; + GLint uColor; + GLint uScreen; +}; + +void InitRectRenderer(RectRenderer& r) +{ + const char* vs = R"( + #version 330 core + layout (location = 0) in vec2 aPos; + uniform vec2 uScreen; + void main() + { + vec2 ndc = vec2( + (aPos.x / uScreen.x) * 2.0 - 1.0, + 1.0 - (aPos.y / uScreen.y) * 2.0 + ); + gl_Position = vec4(ndc, 0.0, 1.0); + } + )"; + + const char* fs = R"( + #version 330 core + out vec4 FragColor; + uniform vec4 uColor; + void main() + { + FragColor = uColor; + } + )"; + + GLuint vsId = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vsId, 1, &vs, nullptr); + glCompileShader(vsId); + + GLuint fsId = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fsId, 1, &fs, nullptr); + glCompileShader(fsId); + + r.program = glCreateProgram(); + glAttachShader(r.program, vsId); + glAttachShader(r.program, fsId); + glLinkProgram(r.program); + + glDeleteShader(vsId); + glDeleteShader(fsId); + + glGenVertexArrays(1, &r.vao); + glGenBuffers(1, &r.vbo); + + glBindVertexArray(r.vao); + glBindBuffer(GL_ARRAY_BUFFER, r.vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 12, nullptr, GL_DYNAMIC_DRAW); + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + glEnableVertexAttribArray(0); + + r.uColor = glGetUniformLocation(r.program, "uColor"); + r.uScreen = glGetUniformLocation(r.program, "uScreen"); +} + + +void BeginRectRender(const RectRenderer& r, int w, int h) +{ + glUseProgram(r.program); + glBindVertexArray(r.vao); + + glUniform2f(r.uScreen, (float)w, (float)h); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + + +void renderFillRect(const RectRenderer& r, SDL_Rect rect, SDL_Color color) +{ + float v[12] = { + (float)rect.x, (float)rect.y, + (float)(rect.x + rect.w), (float)rect.y, + (float)(rect.x + rect.w), (float)(rect.y + rect.h), + (float)rect.x, (float)rect.y, + (float)(rect.x + rect.w), (float)(rect.y + rect.h), + (float)rect.x, (float)(rect.y + rect.h) + }; + + glBindBuffer(GL_ARRAY_BUFFER, r.vbo); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(v), v); + + glUniform4f( + r.uColor, + color.r / 255.0f, + color.g / 255.0f, + color.b / 255.0f, + color.a / 255.0f + ); + + glDrawArrays(GL_TRIANGLES, 0, 6); +} + +int main() +{ + SDL_Init(SDL_INIT_VIDEO); + SDL_Window* window = SDL_CreateWindow( + "Minimal Rect Renderer", + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + 800, 600, SDL_WINDOW_OPENGL + ); + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + + SDL_GLContext glContext = SDL_GL_CreateContext(window); + LoadMinimalGL(); + + RectRenderer renderer; + InitRectRenderer(renderer); + + bool running = true; + SDL_Event e; + + while (running) + { + while (SDL_PollEvent(&e)) + if (e.type == SDL_QUIT) running = false; + + int w, h; + SDL_GL_GetDrawableSize(window, &w, &h); + BeginRectRender(renderer, w, h); + + glClearColor(0.1f, 0.1f, 0.1f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + renderFillRect(renderer, {50,50,200,100}, {255,0,0,255}); + renderFillRect(renderer, {300,200,150,150}, {0,255,128,200}); + + SDL_GL_SwapWindow(window); + } + + SDL_GL_DeleteContext(glContext); + SDL_DestroyWindow(window); + SDL_Quit(); +} diff --git a/sample/SideScroller/SideScroller.cpp b/sample/SideScroller/SideScroller.cpp new file mode 100644 index 0000000..a75bbab --- /dev/null +++ b/sample/SideScroller/SideScroller.cpp @@ -0,0 +1,156 @@ + + +#include "Application.h" +#include "DraggableGameObject.h" +#include "Event.h" +#include "FontCache.h" +#include "GameState.h" +#include "Logger.h" +#include "Mouse.h" +#include "SelectionRect.h" +#include "SpriteOffset.h" +#include "TTFText.h" +#include "Texture.h" +#include "Tile.h" +#include "Tilemap.h" +#include "UI.h" +#include "TextField.h" +#include "TileMapEditor.h" +#include "DOEngine_SDL_includes.h" +#include +#include +#include +#include "GameObject.h" +#include "MessageBox.h" +#include "Sprite.h" +#include "Utils.h" + + + + +struct Player : + public doengine::KeyboardInputhandlingEvent +{ + doengine::Application *app; + doengine::Renderer *renderer; + doengine::Rect offset; + double speedx = 0; + double speedy = 0; + bool is_jumping = false; + + Player(); + + void Update(double elapsed); + + void Render(); + + virtual void OnKeydown(const Keyboard& keyboard) + { + doengine::LogOuput( + doengine::logger_type::Information, + "Key pressed %d", keyboard.getLastKeyPressed() + ); + } + + virtual void OnKeyup(const Keyboard& keyboard) + { + + } +}; + +Player::Player() +{ + app = doengine::Application::getApplication(); + renderer = app->getRender(); + this->offset.x = 10; + offset.h = 200; + offset.w = 200; + this->offset.y = app->getH() - offset.h; +} + +void Player::Update(double elapsed) +{ +} +void Player::Render() +{ + renderer->DrawFillRect(offset, doengine::Colors::white); +} + + +struct Background {}; + + + +struct Enemy {}; + +struct PlayState : public doengine::GameState +{ + doengine::TextureManager *textureManager; + Player *player; + + void createInstance() + { + textureManager = doengine::TextureManager::getTextureManager(); + player = new Player(); + } + void loadTextures() + { + textureManager->loadTextureFromFile("player", "./assets/gfx/sprites/player.png"); + textureManager->loadTextureFromFile("enemy", "./assets/gfx/sprites/enemy_1.png"); + textureManager->loadTextureFromFile("bg", "./assets/background/background_single.png"); + } + + virtual void OnEnter() + { + createInstance(); + loadTextures(); + } + virtual void OnExit() {} + virtual void Update(float elapsed) + { + player->Update(elapsed); + } + virtual void Render() + { + drawLayer1(); + drawLayer2(); + drawLayer3(); + } + void drawLayer1() + { + textureManager->getTexture("bg")->Draw({0,0,800, 600}); + } + void drawLayer2() + { + + } + void drawLayer3() + { + player->Render(); + } +}; + + + +int main(int argc, char* argv[]) +{ + auto app = doengine::Application::getApplication(); + auto resoltion = doengine::Application::getApplication()->getDisplayMode(0); + doengine::Rect rect{800, 600}; + app->createWindow(rect); + auto playstate = new PlayState(); + app->addState(playstate, 1); + app->setState(1); + + while (app->IsRunning()) + { + app->PollEvent(); + app->Update(); + app->clearScreen(doengine::Colors::black); + app->Render(); + } + app->Quit(); + return 0; +} + + diff --git a/sample/SnakeGame/SnakeGame.cpp b/sample/SnakeGame/SnakeGame.cpp new file mode 100644 index 0000000..06a1cb6 --- /dev/null +++ b/sample/SnakeGame/SnakeGame.cpp @@ -0,0 +1,266 @@ +/* + +AnnetteWorld: Allow Gamers to build Homes +WIP: Create MainScreen State. +TODO: Change Graphics Depends on Seasons and Having Fallback + +TODO: Create Login/Entry +TODO: Create Setting State +TODO: Create PArent Setting State. + +... More to add + +*/ + +#include "Application.h" +#include "DraggableGameObject.h" +#include "Event.h" +#include "FontCache.h" +#include "GameObject.h" +#include "GameState.h" +#include "Logger.h" +#include "MenuStringList.h" +#include "MessageBox.h" +#include "Mouse.h" +#include "SelectionRect.h" +#include "Sprite.h" +#include "SpriteOffset.h" +#include "TTFText.h" +#include "TextField.h" +#include "Texture.h" +#include "Tile.h" +#include "TileMapEditor.h" +#include "Tilemap.h" +#include "UI.h" +#include "Utils.h" + +#include +#include +#include +#include +#include + +using doengine::Application; +using doengine::checkCollision; +using doengine::Color; +using doengine::GameObject; +using doengine::GameState; +using doengine::KeyboardInputhandlingEvent; +using doengine::Point; +using doengine::Rect; +using doengine::Renderer; + +enum class Movement +{ + Right, + Left, + Up, + Down +}; +constexpr int MAP_COLUMN_COUNT =16; +constexpr int MAP_ROW_COUNT =16; +constexpr int SCALE_FACTOR_PER_SIZE = 32; + +Rect getRandomPos() +{ + Rect rect{(rand() % MAP_COLUMN_COUNT + 0) * 32, (rand() % MAP_ROW_COUNT + 0) * 32, 32, 32}; + return rect; +} + +struct Snake : public GameObject, public KeyboardInputhandlingEvent +{ + std::vector snake; // Just store rectangles + Rect fruit; + float currentTimeStamp = 0.0f; + Movement movement = Movement::Right; + float accum = 0; + float speed = 0.6f; + int score = 0; + Snake() + { + doengine::TextureManager::getTextureManager()->loadTextureFromFile("snake", "/home/neonland/Documents/DOEngine/assets/gfx/sprites/snake1b.png"); + snake.push_back(getRandomPos()); + fruit = getRandomPos(); + score++; + if((score%3)==0) + { + speed--; + } + } + + void checkCollisionItSelf() + { + auto head = snake[0]; + for(size_t i = 1;i 0) ? nextHead.x - 32 : 15 * 32; + break; + case Movement::Up: + nextHead.y = (nextHead.y > 0) ? nextHead.y - 32 : 15 * 32; + break; + case Movement::Down: + nextHead.y = (nextHead.y < 15 * 32) ? nextHead.y + 32 : 0; + break; + } + + // Check if eating fruit + if (doengine::checkCollision(nextHead, fruit)) + { + snake.insert(snake.begin(), nextHead); // Add new head + fruit = getRandomPos(); + return; + } + + checkCollisionItSelf(); + + // Move body: each segment takes position of the one in front + for (int i = snake.size() - 1; i > 0; i--) + { + snake[i] = snake[i - 1]; + } + + // Move head to next position + snake[0] = nextHead; + } + + virtual void Render() override + { + renderer->DrawFillRect(fruit, doengine::Colors::green); + renderer->DrawRect(fruit, doengine::Colors::white, 2); + for (auto segment : snake) + { + segment.w =SCALE_FACTOR_PER_SIZE; + segment.h =SCALE_FACTOR_PER_SIZE; + renderer->DrawFillRect(segment, doengine::Colors::red); + } + } + + virtual void OnKeydown(const Keyboard& keyboard) + { + auto keys = keyboard.getKeysBitset(); + Movement newMovement = movement; + + if (keys[doengine::SCANCODE_LEFT]) + newMovement = Movement::Left; + else if (keys[doengine::SCANCODE_RIGHT]) + newMovement = Movement::Right; + else if (keys[doengine::SCANCODE_UP]) + newMovement = Movement::Up; + else if (keys[doengine::SCANCODE_DOWN]) + newMovement = Movement::Down; + + // Prevent 180-degree turns + if ((newMovement == Movement::Left && movement == Movement::Right) || + (newMovement == Movement::Right && movement == Movement::Left) || + (newMovement == Movement::Down && movement == Movement::Up) || + (newMovement == Movement::Up && movement == Movement::Down)) + { + return; // Ignore invalid direction change + } + + movement = newMovement; + } + virtual void OnKeyup(const Keyboard&) + { + } + + + virtual void Update(float elapsed) + { + accum += elapsed+0.1f; + if (accum < speed) + return; + Move(); + accum = 0.0f; + } +}; + +struct SnakeState : public GameState, + public KeyboardInputhandlingEvent +{ + + + Snake snake; + virtual void OnEnter(); + virtual void OnExit(); + virtual void Update(float elapsed); + virtual void Render(); + virtual void OnKeydown(const Keyboard&); + virtual void OnKeyup(const Keyboard&); +}; + +void SnakeState::OnEnter() +{ + +} +void SnakeState::OnExit() +{ +} +void SnakeState::Update(float elapsed) +{ + snake.Update(elapsed); +} +void SnakeState::Render() +{ + + for (int r = 0; r < 16; r++) + { + for (int c = 0; c < 16; c++) + { + Rect rect{c * SCALE_FACTOR_PER_SIZE, r * SCALE_FACTOR_PER_SIZE, SCALE_FACTOR_PER_SIZE, SCALE_FACTOR_PER_SIZE}; + snake.getRenderer()->DrawRect(rect, doengine::Colors::white, 1); + } + } + + snake.Render(); +} +void SnakeState::OnKeydown(const Keyboard&) +{ +} +void SnakeState::OnKeyup(const Keyboard&) +{ +} + +int main(int argc, char* argv[]) +{ + + auto app = doengine::Application::getApplication(); + + doengine::Rect rect{1200, 900}; + app->createWindow(rect); + app->setSize(rect.w, rect.h); + + auto playstate = new SnakeState(); + app->addState(playstate, 1); + app->setState(1); + + while (app->IsRunning()) + { + app->PollEvent(); + app->Update(); + app->clearScreen(doengine::Colors::black1); + app->Render(); + } + app->Quit(); + return 0; +} diff --git a/sample/SpaceInvader/SpaceInvader.cpp b/sample/SpaceInvader/SpaceInvader.cpp index 15f3516..eaf8607 100644 --- a/sample/SpaceInvader/SpaceInvader.cpp +++ b/sample/SpaceInvader/SpaceInvader.cpp @@ -39,11 +39,11 @@ struct Alien { void Draw(){ switch(type){ case AlienType::Type1: - renderer->DrawFillRect(offset, doengine::yellow1);break; + renderer->DrawFillRect(offset, doengine::Colors::yellow1);break; case AlienType::Type2: - renderer->DrawFillRect(offset, doengine::red);break; + renderer->DrawFillRect(offset, doengine::Colors::red);break; case AlienType::Type3: - renderer->DrawFillRect(offset, doengine::blue5);break; + renderer->DrawFillRect(offset, doengine::Colors::blue5);break; } } @@ -62,7 +62,7 @@ struct Block{ void Draw(){ doengine::Renderer *render = doengine::Application::getApplication()->getRender(); - render->DrawFillRect(offset, doengine::yellow); + render->DrawFillRect(offset, doengine::Colors::yellow); } Block(doengine::Rect o){ @@ -164,7 +164,7 @@ struct Laser void draw() { if (renderer && active) - renderer->DrawRect(offset, doengine::yellow); + renderer->DrawRect(offset, doengine::Colors::yellow); } }; @@ -200,7 +200,7 @@ void Spacership::Draw() } else { - renderer->DrawFillRect(offset, doengine::red); + renderer->DrawFillRect(offset, doengine::Colors::red); } for (auto beam : firebeam) diff --git a/sample/VideoPlayer/VideoPlayer.cpp b/sample/VideoPlayer/VideoPlayer.cpp new file mode 100644 index 0000000..7a7ed27 --- /dev/null +++ b/sample/VideoPlayer/VideoPlayer.cpp @@ -0,0 +1,102 @@ +extern "C" { +#include +#include +#include +} + +#include "DOEngine_SDL_includes.h" +#include +int main(int argc, char* argv[]) +{ + + + const char* filename = "/home/neonland/snap/obs-studio/1316/1.mp4";/// argv[1]; + + avformat_network_init(); + + AVFormatContext* formatCtx = nullptr; + avformat_open_input(&formatCtx, filename, nullptr, nullptr); + avformat_find_stream_info(formatCtx, nullptr); + + int videoStream = -1; + for (unsigned i = 0; i < formatCtx->nb_streams; i++) { + if (formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + videoStream = i; + break; + } + } + + AVCodecParameters* codecPar = formatCtx->streams[videoStream]->codecpar; + const AVCodec* codec = avcodec_find_decoder(codecPar->codec_id); + + AVCodecContext* codecCtx = avcodec_alloc_context3(codec); + avcodec_parameters_to_context(codecCtx, codecPar); + avcodec_open2(codecCtx, codec, nullptr); + + SDL_Init(SDL_INIT_VIDEO); + + SDL_Window* window = SDL_CreateWindow( + "SDL2 Video Player", + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + codecCtx->width, + codecCtx->height, + 0 + ); + + SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0); + + SDL_Texture* texture = SDL_CreateTexture( + renderer, + SDL_PIXELFORMAT_IYUV, + SDL_TEXTUREACCESS_STREAMING, + codecCtx->width, + codecCtx->height + ); + + AVPacket packet; + AVFrame* frame = av_frame_alloc(); + + bool running = true; + SDL_Event event; + + while (running && av_read_frame(formatCtx, &packet) >= 0) { + if (packet.stream_index == videoStream) { + avcodec_send_packet(codecCtx, &packet); + + while (avcodec_receive_frame(codecCtx, frame) == 0) { + + SDL_UpdateYUVTexture( + texture, + nullptr, + frame->data[0], frame->linesize[0], + frame->data[1], frame->linesize[1], + frame->data[2], frame->linesize[2] + ); + + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, texture, nullptr, nullptr); + SDL_RenderPresent(renderer); + + SDL_Delay(33); // ~30 FPS + } + } + + av_packet_unref(&packet); + + while (SDL_PollEvent(&event)) { + if (event.type == SDL_QUIT) + running = false; + } + } + av_frame_free(&frame); + avcodec_free_context(&codecCtx); + avformat_close_input(&formatCtx); + + SDL_DestroyTexture(texture); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + + return 0; +} diff --git a/sample/ignored_main_template.cpp b/sample/ignored_main_template.cpp index c365ec1..74fff1d 100644 --- a/sample/ignored_main_template.cpp +++ b/sample/ignored_main_template.cpp @@ -3,7 +3,7 @@ #include "Grids.h" #include #include -#include +#include "DOEngine_SDL_includes.h" #include #include #include diff --git a/sample/old_projects/CMakeLists.txt b/sample/old_projects/CMakeLists.txt deleted file mode 100644 index 3dac965..0000000 --- a/sample/old_projects/CMakeLists.txt +++ /dev/null @@ -1,48 +0,0 @@ -cmake_minimum_required(VERSION 3.22.1) - -project(DOSAMPLE) - -include_directories(../includes) - -add_executable(DOSAMPLE - ignored_main.cpp -) - -add_executable(Music - Music.cpp -) - -add_executable(Sound - Sound.cpp -) - -file(COPY ${ASSETS_DIR} - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) - -target_link_libraries(DOSAMPLE PUBLIC - DOENGINE - SDL2::SDL2 - SDL2::SDL2main - SDL2_ttf - SDL2_image - SDL2_mixer -) - -target_link_libraries(Music PRIVATE - DOENGINE - SDL2::SDL2 - SDL2::SDL2main - SDL2_ttf - SDL2_image - SDL2_mixer -) - -target_link_libraries(Sound PRIVATE - DOENGINE - SDL2::SDL2 - SDL2::SDL2main - SDL2_ttf - SDL2_image - SDL2_mixer - ) - diff --git a/sample/old_projects/CanvasTestState.cpp b/sample/old_projects/CanvasTestState.cpp deleted file mode 100644 index fb32bb0..0000000 --- a/sample/old_projects/CanvasTestState.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include "CanvasTestState.h" -#include "Event.h" -#include "Utils.h" - -#include - -struct Particle -{ - SDL_Point rect; - Canvas* ctx; - double size; - - double sx; - double sy; - - Particle(Canvas* canvas) - { - - sx = getRandomInt(1, 4); - sy = getRandomInt(1, 4); - - ctx = canvas; - /// rect.x = getRandomInt(10, canvas->getCanvasSize().w ); - // rect.y = getRandomInt(10, canvas->getCanvasSize().h ); - size = getRandomInt(10, 32); - } - - void Draw() - { - - SDL_Color penColor = {255, 255, 255, 255}; - - if (size > 24) - { - penColor.r = 255; - penColor.g = 0; - penColor.b = 0; - } - else if (size > 20) - { - penColor.r = 255; - penColor.g = 50; - penColor.b = 0; - } - else - { - penColor.r = 255; - penColor.g = 100; - penColor.b = 120; - } - ctx->fillColor(penColor); - - ctx->FillCircle(rect.x, rect.y, size); - } - - void update() - { - - if (size > 0.2) - size -= 0.3; - - rect.x += sx; - rect.y += sy; - } -}; - -std::vector particles; - -void CanvasTestState::OnEnter() -{ - pencil1 = new Canvas(this->window); - pencil1->fillColor({255, 0, 0, 255}); - pencil1->setCanvasBackgroundColor({0, 0, 0, 255}); - - Event::AddMouseEvent(this); - - // for(int i=0;i<10;i++) - // { - // particles.emplace_back(new Particle(pencil1)); - // } -} -void CanvasTestState::OnExit() -{ -} -void CanvasTestState::Update(float elapsed) -{ - - //// SDL_Log("Particle Size = %ld", particles.size()); - - for (auto it : particles) - { - it->update(); - } -} -void CanvasTestState::Render() -{ - for (auto it : particles) - { - if (it->size < 0.3) - { - SDL_Log("thIS SHOULD BE REMOVED"); - /// particles.erase(*it); - /// std::remove(particles.begin(), particles.end(), it); - // particles.erase(); - /// auto it = std::find(v.begin(), v.end(), 5); - // if(it != v.end()) - // v.erase(it); - } - else - { - it->Draw(); - } - } - pencil1->update(); - pencil1->clearCanvas(); -} diff --git a/sample/old_projects/MainMenuState.cpp b/sample/old_projects/MainMenuState.cpp deleted file mode 100644 index 8bb1873..0000000 --- a/sample/old_projects/MainMenuState.cpp +++ /dev/null @@ -1,16 +0,0 @@ - - -#include "MainMenuState.h" - -void MainMenuState::OnEnter() -{ -} -void MainMenuState::OnExit() -{ -} -void MainMenuState::Update(float elapsed) -{ -} -void MainMenuState::Render() -{ -} \ No newline at end of file diff --git a/sample/old_projects/MainMenuState.h b/sample/old_projects/MainMenuState.h deleted file mode 100644 index cea84d2..0000000 --- a/sample/old_projects/MainMenuState.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once -#include -#include - -class MainMenuState : public GameState -{ - - public: - MainMenuState(Window* w) : GameState(w) - { - } - virtual void OnEnter(); - virtual void OnExit(); - virtual void Update(float elapsed); - virtual void Render(); -}; \ No newline at end of file diff --git a/sample/old_projects/Mario.zip b/sample/old_projects/Mario.zip deleted file mode 100644 index 0db4a0e..0000000 Binary files a/sample/old_projects/Mario.zip and /dev/null differ diff --git a/sample/old_projects/MatrixTestState.cpp b/sample/old_projects/MatrixTestState.cpp deleted file mode 100644 index 6f264b5..0000000 --- a/sample/old_projects/MatrixTestState.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include "MatrixTestState.h" -#include "Canvas.h" -#include "TTFText.h" -#include "Utils.h" - -const char* charset = - "QWERTYUIOPASDFGHJKLZXCVBNM,./1234567890-=!@#$%^&*()_+*/.\"\'`"; - -class Symbol -{ - - int canvas_h; - SDL_Point point; - int fnt_size; - Canvas* canvas; - - SDL_Color color; - - int inty = 0; - - public: - Symbol(int fontsize, int x, int y, int canvasHeight) - { - color = {0x0A, 0xFF, 0x0a, 255}; - canvas_h = canvasHeight; - point.x = x; - point.y = inty = y; - fnt_size = fontsize; - } - - void draw(Canvas* canvas) - { - std::string str; - str += charset[getRandomInt(0, 59)]; - canvas->fillColor(color); - canvas->FillText(str.c_str(), point.x * fnt_size, point.y * fnt_size); - if (point.y * fnt_size > canvas_h) - point.y = inty; - else - point.y += 1; - - if (point.y > 0) - { - color.g -= 1; - } - else - { - color.g = 0xff; - } - } -}; - -class Effect -{ - - int width; - int height; - int fontsize; - int columns; - std::vector symbols; - Canvas* ctx; - - public: - Effect(Canvas* canvas) - { - ctx = canvas; - fontsize = 25; - width = canvas->getCanvasSize().w; - height = canvas->getCanvasSize().h; - columns = width / fontsize; - - for (int i = 0; i < columns - 5; i++) - { - symbols.push_back(new Symbol(fontsize, i + 4, 10, height)); - } - } - - void Draw() - { - for (auto it : symbols) - { - it->draw(ctx); - } - } -}; - -Effect* effect = nullptr; - -void MatrixTestState::OnEnter() -{ - canvas = new Canvas(this->window); - canvas->setCanvasBackgroundColor({0, 0, 0, 255}); - canvas->fillColor({0x0A, 0xFF, 0x0a, 255}); - effect = new Effect(canvas); -} -void MatrixTestState::OnExit() -{ -} -void MatrixTestState::Update(float elapsed) -{ - canvas->clearCanvas(); -} -void MatrixTestState::Render() -{ - //// canvas->clearCanvas(); - effect->Draw(); - canvas->update(); -} \ No newline at end of file diff --git a/sample/old_projects/SFML_menu.rar b/sample/old_projects/SFML_menu.rar deleted file mode 100644 index 2db7425..0000000 Binary files a/sample/old_projects/SFML_menu.rar and /dev/null differ diff --git a/sample/old_projects/SFML_text.zip b/sample/old_projects/SFML_text.zip deleted file mode 100644 index ab7932b..0000000 Binary files a/sample/old_projects/SFML_text.zip and /dev/null differ diff --git a/sample/old_projects/SortedState.cpp b/sample/old_projects/SortedState.cpp deleted file mode 100644 index 1eca2c7..0000000 --- a/sample/old_projects/SortedState.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "SortedState.h" -#include "Utils.h" - -void SortedState::OnEnter() -{ - - this->ctx = new Canvas(window); - this->ctx->setCanvasBackgroundColor(Canvas::black); - - this->height = window->getH() - 100; - - width = 48; - - int nTimes = (window->getW() / width) - 4; - - for (int i = 0; i < nTimes; i++) - { - ColumnEntry* entry = new ColumnEntry(); - entry->offset.x = i * width; - entry->offset.y = 0; - entry->offset.w = width; - entry->value = getRandomInt(10, 1000); - entry->offset.h = getPercentageFrom(entry->value, 1000); - this->entries.push_back(entry); - /// SDL_Log("value = %ld, height=%ld", entry->value, entry->offset.h); - } - - ctx->fillColor( - {200, 200, static_cast(getRandomInt(0, 234)), 255}); -} -void SortedState::OnExit() -{ -} -void SortedState::Update(float elapsed) -{ -} - -void SortedState::Render() -{ - - for (auto it : entries) - { - ctx->DrawRect(it->offset.x + 100, it->offset.y + 100, it->offset.w, - it->offset.h, false); - - ctx->DrawRect(it->offset.x + 108, it->offset.y + 104, it->offset.w - 12, - it->offset.h - 14, true); - } - ctx->update(); -} diff --git a/sample/old_projects/TestState.h b/sample/old_projects/TestState.h deleted file mode 100644 index d9c8210..0000000 --- a/sample/old_projects/TestState.h +++ /dev/null @@ -1,15 +0,0 @@ - -#include -#include - -class TestState : public GameState -{ - public: - TestState(Window* w) : GameState(w) - { - } - virtual void OnEnter(); - virtual void OnExit(); - virtual void Update(float elapsed); - virtual void Render(); -}; diff --git a/sample/old_projects/TileMapTestState.cpp b/sample/old_projects/TileMapTestState.cpp deleted file mode 100644 index a8b716b..0000000 --- a/sample/old_projects/TileMapTestState.cpp +++ /dev/null @@ -1,228 +0,0 @@ -#include -#include -#include -int w = 400; -int h = 600; -Canvas* pencil; -std::string tetromino[7]; -int nFieldWidth = 12; -int nFieldHeight = 18; -int nScreenWidth = 80; // Console Screen Size X (columns) -int nScreenHeight = 30; // Console Screen Size Y (rows) -// Game Logic -bool bKey[4]; -int nCurrentPiece = 0; -int nCurrentRotation = 0; -int nCurrentX = nFieldWidth / 2; -int nCurrentY = 0; -int nSpeed = 20; -int nSpeedCount = 0; -bool bForceDown = false; -bool bRotateHold = true; -int nPieceCount = 0; -int nScore = 0; -std::vector vLines; -bool bGameOver = false; -unsigned char* pField = nullptr; -wchar_t* screen; /// = new wchar_t[nScreenWidth*nScreenHeight]; -int Rotate(int px, int py, int r) -{ - int pi = 0; - switch (r % 4) - { - case 0: // 0 degrees // 0 1 2 3 - pi = py * 4 + px; // 4 5 6 7 - break; // 8 9 10 11 - // 12 13 14 15 - - case 1: // 90 degrees //12 8 4 0 - pi = 12 + py - (px * 4); // 13 9 5 1 - break; // 14 10 6 2 - // 15 11 7 3 - - case 2: // 180 degrees //15 14 13 12 - pi = 15 - (py * 4) - px; // 11 10 9 8 - break; // 7 6 5 4 - // 3 2 1 0 - - case 3: // 270 degrees // 3 7 11 15 - pi = 3 - py + (px * 4); // 2 6 10 14 - break; // 1 5 9 13 - } // 0 4 8 12 - - return pi; -} -bool DoesPieceFit(int nTetromino, int nRotation, int nPosX, int nPosY) -{ - // All Field cells >0 are occupied - for (int px = 0; px < 4; px++) - for (int py = 0; py < 4; py++) - { - // Get index into piece - int pi = Rotate(px, py, nRotation); - - // Get index into field - int fi = (nPosY + py) * nFieldWidth + (nPosX + px); - - // Check that test is in bounds. Note out of bounds does - // not necessarily mean a fail, as the long vertical piece - // can have cells that lie outside the boundary, so we'll - // just ignore them - if (nPosX + px >= 0 && nPosX + px < nFieldWidth) - { - if (nPosY + py >= 0 && nPosY + py < nFieldHeight) - { - // In Bounds so do collision check - if (tetromino[nTetromino][pi] != L'.' && pField[fi] != 0) - return false; // fail on first hit - } - } - } - - return true; -} - -void TileMapTestState::OnEnter() -{ - Event::AddKeydownEventListener(this); - tetromino[0].append("..X...X...X...X."); // Tetronimos 4x4 - tetromino[1].append("..X..XX...X....."); - tetromino[2].append(".....XX..XX....."); - tetromino[3].append("..X..XX..X......"); - tetromino[4].append(".X...XX...X....."); - tetromino[5].append(".X...X...XX....."); - tetromino[6].append("..X...X..XX....."); - pencil = new Canvas(window); - pencil->setRect({window->getW() / 2 - w, window->getH() - h - 100, w, h}); - pencil->setCanvasBackgroundColor({255, 255, 0, 255}); - pField = new unsigned char[nFieldWidth * - nFieldHeight]; // Create play field buffer - for (int x = 0; x < nFieldWidth; x++) // Board Boundary - for (int y = 0; y < nFieldHeight; y++) - pField[y * nFieldWidth + x] = - (x == 0 || x == nFieldWidth - 1 || y == nFieldHeight - 1) ? 9 - : 0; - wchar_t* screen = new wchar_t[nScreenWidth * nScreenHeight]; -} -void TileMapTestState::OnExit() -{ -} -void TileMapTestState::Update(float elapsed) -{ - if (bForceDown) - { - // Update difficulty every 50 pieces - nSpeedCount = 0; - nPieceCount++; - if (nPieceCount % 50 == 0) - if (nSpeed >= 10) - nSpeed--; - - // Test if piece can be moved down - if (DoesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX, - nCurrentY + 1)) - nCurrentY++; // It can, so do it! - else - { - // It can't! Lock the piece in place - for (int px = 0; px < 4; px++) - for (int py = 0; py < 4; py++) - if (tetromino[nCurrentPiece] - [Rotate(px, py, nCurrentRotation)] != L'.') - pField[(nCurrentY + py) * nFieldWidth + - (nCurrentX + px)] = nCurrentPiece + 1; - - // Check for lines - for (int py = 0; py < 4; py++) - if (nCurrentY + py < nFieldHeight - 1) - { - bool bLine = true; - for (int px = 1; px < nFieldWidth - 1; px++) - bLine &= - (pField[(nCurrentY + py) * nFieldWidth + px]) != 0; - - if (bLine) - { - // Remove Line, set to = - for (int px = 1; px < nFieldWidth - 1; px++) - pField[(nCurrentY + py) * nFieldWidth + px] = 8; - vLines.push_back(nCurrentY + py); - } - } - - nScore += 25; - if (!vLines.empty()) - nScore += (1 << vLines.size()) * 100; - - // Pick New Piece - nCurrentX = nFieldWidth / 2; - nCurrentY = 0; - nCurrentRotation = 0; - nCurrentPiece = rand() % 7; - - // If piece does not fit straight away, game over! - bGameOver = !DoesPieceFit(nCurrentPiece, nCurrentRotation, - nCurrentX, nCurrentY); - } - } -} -void TileMapTestState::Render() -{ - - for (int x = 0; x < nFieldWidth; x++) - for (int y = 0; y < nFieldHeight; y++) - { - int iter1 = (y + 2) * nScreenWidth + (x + 2); - int iter2 = pField[y * nFieldWidth + x]; - ////SDL_Log("%ld %ld", iter1, iter2); - SDL_Log("%c", " ABCDEFG=#"[pField[y * nFieldWidth + x]]); - /// screen[] = L" ABCDEFG=#"[pField[y*nFieldWidth + x]]; - } -#if 0 - // Draw Current Piece - for (int px = 0; px < 4; px++) - for (int py = 0; py < 4; py++) - if (tetromino[nCurrentPiece][Rotate(px, py, nCurrentRotation)] != L'.') - screen[(nCurrentY + py + 2)*nScreenWidth + (nCurrentX + px + 2)] = nCurrentPiece + 65; - -#endif - - pencil->clearCanvas(); - pencil->update(); -} - -void TileMapTestState::OnKeydown(int code) -{ - SDL_Log("%ld", code); - if (code == 27) - this->window->Quit(); - - // Rotate, but latch to stop wild spinning - if (code == 1073741906) - { - nCurrentRotation += - (bRotateHold && DoesPieceFit(nCurrentPiece, nCurrentRotation + 1, - nCurrentX, nCurrentY)) - ? 1 - : 0; - bRotateHold = false; - } - else - bRotateHold = true; - - nCurrentX += - (code == 1073741904 && DoesPieceFit(nCurrentPiece, nCurrentRotation, - nCurrentX + 1, nCurrentY)) - ? 1 - : 0; - nCurrentX -= - (code == 1073741904 && DoesPieceFit(nCurrentPiece, nCurrentRotation, - nCurrentX - 1, nCurrentY)) - ? 1 - : 0; - nCurrentY += - (code == 1073741905 && DoesPieceFit(nCurrentPiece, nCurrentRotation, - nCurrentX, nCurrentY + 1)) - ? 1 - : 0; -} \ No newline at end of file diff --git a/sample/old_projects/TileMapTestState.h b/sample/old_projects/TileMapTestState.h deleted file mode 100644 index 3dfb650..0000000 --- a/sample/old_projects/TileMapTestState.h +++ /dev/null @@ -1,17 +0,0 @@ - -#pragma once -#include - -class TileMapTestState : public GameState, KeyDownEvent -{ - - public: - TileMapTestState(Window* w) : GameState(w) - { - } - virtual void OnEnter(); - virtual void OnExit(); - virtual void Update(float elapsed); - virtual void Render(); - virtual void OnKeydown(int code); -}; \ No newline at end of file diff --git a/sample/old_projects/Window.cpp b/sample/old_projects/Window.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/sample/old_projects/Window.h b/sample/old_projects/Window.h deleted file mode 100644 index e69de29..0000000 diff --git a/sample/old_projects/droid-sans.zip b/sample/old_projects/droid-sans.zip deleted file mode 100644 index 624e27a..0000000 Binary files a/sample/old_projects/droid-sans.zip and /dev/null differ diff --git a/sample/old_projects/main.cpp b/sample/old_projects/main.cpp deleted file mode 100644 index b1909d2..0000000 --- a/sample/old_projects/main.cpp +++ /dev/null @@ -1,65 +0,0 @@ - -#include "Application.h" -#include -#include -#include -#include -#include -struct PongState : public GameState -{ - virtual void OnEnter() - { - } - virtual void OnExit() - { - } - virtual void Update(float elapsed) - { - } - virtual void Render() - { - } -}; - -enum SampleStateId : int -{ - pongStateID = 1 -}; - -struct PongState : public GameState -{ - virtual void OnEnter() - { - } - virtual void OnExit() - { - } - virtual void Update(float elapsed) - { - } - virtual void Render() - { - } -}; - -enum SampleStateId : int -{ - pongStateID = 1 -}; - -int main(int argc, char* argv[]) -{ - auto app = Application::getApplication(); - app->setSize(800, 600); - auto pongState = new PongState(); - app->addState(pongState, pongStateID); - app->setState(pongStateID); - while (app->IsRunning()) - { - app->PollEvent(); - - app->Render(); - } - app->Quit(); - return 0; -} diff --git a/sample/old_projects/ttfwrapp.cpp b/sample/old_projects/ttfwrapp.cpp deleted file mode 100644 index 2bf920c..0000000 --- a/sample/old_projects/ttfwrapp.cpp +++ /dev/null @@ -1,132 +0,0 @@ - -#include -#include -#include -#include - -// Function to wrap text -void wrapText(const char* text, TTF_Font* font, int maxWidth, char* wrappedText) { - const char* current = text; - const char* wordStart; - char line[256] = ""; - char temp[256] = ""; - int width = 0; - - while (*current) { - wordStart = current; - - // Find the end of the current word - while (*current && *current != ' ' && *current != '\n') { - current++; - } - - // Copy the word to a temporary buffer - strncpy(temp, wordStart, current - wordStart); - temp[current - wordStart] = '\0'; - - // Check the width of the line if the word is added - char testLine[256]; - snprintf(testLine, sizeof(testLine), "%s %s", line, temp); - TTF_SizeText(font, testLine, &width, NULL); - - if (width > maxWidth) { - // Add the current line to the wrapped text - strcat(wrappedText, line); - strcat(wrappedText, "\n"); - - // Start a new line with the current word - strcpy(line, temp); - } else { - // Add the word to the current line - if (*line) { - strcat(line, " "); - } - strcat(line, temp); - } - - // Move to the next word - if (*current == ' ') { - current++; - } - } - - // Add the last line to the wrapped text - if (*line) { - strcat(wrappedText, line); - strcat(wrappedText, "\n"); - } -} - -int main() { - if (SDL_Init(SDL_INIT_VIDEO) < 0) { - printf("Failed to initialize SDL: %s\n", SDL_GetError()); - return 1; - } - - if (TTF_Init() == -1) { - printf("Failed to initialize SDL_ttf: %s\n", TTF_GetError()); - SDL_Quit(); - return 1; - } - - SDL_Window* window = SDL_CreateWindow("Text Wrapper", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN); - if (!window) { - printf("Failed to create window: %s\n", SDL_GetError()); - TTF_Quit(); - SDL_Quit(); - return 1; - } - - SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); - if (!renderer) { - printf("Failed to create renderer: %s\n", SDL_GetError()); - SDL_DestroyWindow(window); - TTF_Quit(); - SDL_Quit(); - return 1; - } - - TTF_Font* font = TTF_OpenFont("/home/neon/Documents/projects/DOEngine/sample/DroidSans.ttf", 24); - if (!font) { - printf("Failed to load font: %s\n", TTF_GetError()); - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - TTF_Quit(); - SDL_Quit(); - return 1; - } - - const char* text = "This is a long paragraph that needs to be wrapped into multiple lines so it can be displayed properly in a limited space."; - char wrappedText[1024] = ""; - wrapText(text, font, 500, wrappedText); - - SDL_Color textColor = {255, 255, 255, 255}; - SDL_Rect renderQuad = {200, 100, 0, 0}; - char* line = strtok(wrappedText, "\n"); - while (line != NULL) { - - SDL_Log("%s", line); - // SDL_Surface* textSurface = TTF_RenderText_Blended(font, line, textColor); - // SDL_Texture* textTexture = SDL_CreateTextureFromSurface(renderer, textSurface); - // renderQuad.w = textSurface->w; - // renderQuad.h = textSurface->h; - // SDL_RenderCopy(renderer, textTexture, NULL, &renderQuad); - // renderQuad.y += renderQuad.h + 5; // Add some spacing between lines - // SDL_FreeSurface(textSurface); - // SDL_DestroyTexture(textTexture); - line = strtok(NULL, "\n"); - } - - SDL_RenderPresent(renderer); - - SDL_Delay(5000); - - TTF_CloseFont(font); - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - TTF_Quit(); - SDL_Quit(); - - return 0; -} - diff --git "a/sample/old_projects/\320\232\320\276\320\264 \321\201 \320\277\321\200\320\270\320\274\320\265\321\200\320\276\320\262 \320\275\320\260 \320\262\320\270\320\264\320\265\320\276/fang.png" "b/sample/old_projects/\320\232\320\276\320\264 \321\201 \320\277\321\200\320\270\320\274\320\265\321\200\320\276\320\262 \320\275\320\260 \320\262\320\270\320\264\320\265\320\276/fang.png" deleted file mode 100644 index 11f0528..0000000 Binary files "a/sample/old_projects/\320\232\320\276\320\264 \321\201 \320\277\321\200\320\270\320\274\320\265\321\200\320\276\320\262 \320\275\320\260 \320\262\320\270\320\264\320\265\320\276/fang.png" and /dev/null differ diff --git "a/sample/old_projects/\320\232\320\276\320\264 \321\201 \320\277\321\200\320\270\320\274\320\265\321\200\320\276\320\262 \320\275\320\260 \320\262\320\270\320\264\320\265\320\276/main.cpp" "b/sample/old_projects/\320\232\320\276\320\264 \321\201 \320\277\321\200\320\270\320\274\320\265\321\200\320\276\320\262 \320\275\320\260 \320\262\320\270\320\264\320\265\320\276/main.cpp" deleted file mode 100644 index 4f9a689..0000000 --- "a/sample/old_projects/\320\232\320\276\320\264 \321\201 \320\277\321\200\320\270\320\274\320\265\321\200\320\276\320\262 \320\275\320\260 \320\262\320\270\320\264\320\265\320\276/main.cpp" +++ /dev/null @@ -1,176 +0,0 @@ -#include - -using namespace sf; - - -float offsetX=0, offsetY=0; - - -const int H = 12; -const int W = 40; - - -String TileMap[H] = { - -"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", -"B B B", -"B B B", -"B B B", -"B B B", -"B 0000 BBBB B", -"B B B", -"BBB B B", -"B BB BB B", -"B BB B", -"B B BB BB B", -"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", - -}; - -class PLAYER { - -public: - -float dx,dy; -FloatRect rect; -bool onGround; -Sprite sprite; -float currentFrame; - - PLAYER(Texture &image) - { - sprite.setTexture(image); - rect = FloatRect(7*32,9*32,40,50); - - dx=dy=0.1; - currentFrame = 0; - } - - - void update(float time) - { - - rect.left += dx * time; - Collision(0); - - if (!onGround) dy=dy+0.0005*time; - rect.top += dy*time; - onGround=false; - Collision(1); - - - currentFrame += 0.005*time; - if (currentFrame > 6) currentFrame -=6 ; - - if (dx>0) sprite.setTextureRect(IntRect(40*int(currentFrame),244,40,50)); - if (dx<0) sprite.setTextureRect(IntRect(40*int(currentFrame)+40,244,-40,50)); - - - sprite.setPosition(rect.left - offsetX, rect.top - offsetY); - - dx=0; - } - - - - void Collision(int dir) - { - for (int i = rect.top/32 ; i<(rect.top+rect.height)/32; i++) - for (int j = rect.left/32; j<(rect.left+rect.width)/32; j++) - { - if (TileMap[i][j]=='B') - { - if ((dx>0) && (dir==0)) rect.left = j*32 - rect.width; - if ((dx<0) && (dir==0)) rect.left = j*32 + 32; - if ((dy>0) && (dir==1)) { rect.top = i*32 - rect.height; dy=0; onGround=true; } - if ((dy<0) && (dir==1)) { rect.top = i*32 + 32; dy=0;} - } - - if (TileMap[i][j]=='0') - { - TileMap[i][j]=' '; - } - - } - - } -}; - -int main() -{ - RenderWindow window( VideoMode(600, 400), "Test!"); - - Texture t; - t.loadFromFile("fang.png"); - - float currentFrame=0; - - PLAYER p(t); - - Clock clock; - - RectangleShape rectangle( Vector2f(32,32)); - - while (window.isOpen()) - { - float time = clock.getElapsedTime().asMicroseconds(); - clock.restart(); - - time = time/700; - - if (time>20) time = 20; - - Event event; - while (window.pollEvent(event)) - { - if (event.type == Event::Closed) - window.close(); - } - - if (Keyboard::isKeyPressed(Keyboard::Left)) - { - p.dx = -0.1; - - } - - if (Keyboard::isKeyPressed(Keyboard::Right)) - { - p.dx = 0.1; - } - - if (Keyboard::isKeyPressed(Keyboard::Up)) - { - if (p.onGround) { p.dy=-0.35; p.onGround=false;} - } - - p.update(time); - - if (p.rect.left>300) offsetX = p.rect.left - 300; - offsetY = p.rect.top - 200; - - window.clear(Color::White); - - - for (int i=0; i -#include - -using namespace sf; - - -float offsetX=0, offsetY=0; - - -const int H = 17; -const int W = 150; - - -String TileMap[H] = { -"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", -"0 0", -"0 w 0", -"0 w w w 0", -"0 w kk 0", -"0 k k k k 0", -"0 c k kkk kkk w 0", -"0 r k k k 0", -"0 rr k k 0", -"0 rrr kk 0", -"0 c kckck rrrr 0", -"0 t0 rrrrr 0", -"0G 00 t0 rrrrrr G 0", -"0 d g d 00 00 rrrrrrr 0", -"PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP", -"PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP", -"PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP", -}; - - - - -class PLAYER { - -public: - -float dx,dy; -FloatRect rect; -bool onGround; -Sprite sprite; -float currentFrame; - - PLAYER(Texture &image) - { - sprite.setTexture(image); - rect = FloatRect(100,180,16,16); - - dx=dy=0.1; - currentFrame = 0; - } - - - void update(float time) - { - - rect.left += dx * time; - Collision(0); - - - if (!onGround) dy=dy+0.0005*time; - rect.top += dy*time; - onGround=false; - Collision(1); - - - currentFrame += time * 0.005; - if (currentFrame > 3) currentFrame -= 3; - - - if (dx>0) sprite.setTextureRect(IntRect(112+31*int(currentFrame),144,16,16)); - if (dx<0) sprite.setTextureRect(IntRect(112+31*int(currentFrame)+16,144,-16,16)); - - - sprite.setPosition(rect.left - offsetX, rect.top - offsetY); - - dx=0; - } - - - void Collision(int num) -{ - - for (int i = rect.top/16 ; i<(rect.top+rect.height)/16; i++) - for (int j = rect.left/16; j<(rect.left+rect.width)/16; j++) - { - if ((TileMap[i][j]=='P') || (TileMap[i][j]=='k') || (TileMap[i][j]=='0') || (TileMap[i][j]=='r') || (TileMap[i][j]=='t')) - { - if (dy>0 && num==1) - { rect.top = i*16 - rect.height; dy=0; onGround=true; } - if (dy<0 && num==1) - { rect.top = i*16 + 16; dy=0;} - if (dx>0 && num==0) - { rect.left = j*16 - rect.width; } - if (dx<0 && num==0) - { rect.left = j*16 +16;} - } - - if (TileMap[i][j]=='c') { - // TileMap[i][j]=' '; - } - } - -} - -}; - - - -class ENEMY -{ - -public: -float dx,dy; -FloatRect rect; -Sprite sprite; -float currentFrame; -bool life; - - - void set(Texture &image, int x, int y) - { - sprite.setTexture(image); - rect = FloatRect(x,y,16,16); - - dx=0.05; - currentFrame = 0; - life=true; - } - - void update(float time) - { - rect.left += dx * time; - - Collision(); - - - currentFrame += time * 0.005; - if (currentFrame > 2) currentFrame -= 2; - - sprite.setTextureRect(IntRect(18*int(currentFrame), 0, 16,16)); - if (!life) sprite.setTextureRect(IntRect(58, 0, 16,16)); - - - sprite.setPosition(rect.left - offsetX, rect.top - offsetY); - - } - - - void Collision() - { - - for (int i = rect.top/16 ; i<(rect.top+rect.height)/16; i++) - for (int j = rect.left/16; j<(rect.left+rect.width)/16; j++) - if ((TileMap[i][j]=='P') || (TileMap[i][j]=='0')) - { - if (dx>0) - { rect.left = j*16 - rect.width; dx*=-1; } - else if (dx<0) - { rect.left = j*16 + 16; dx*=-1; } - - } - } - -}; - - - -int main() -{ - - RenderWindow window(VideoMode(400, 250), "SFML works!"); - - Texture tileSet; - tileSet.loadFromFile("Mario_Tileset.png"); - - - PLAYER Mario(tileSet); - ENEMY enemy; - enemy.set(tileSet,48*16,13*16); - - - Sprite tile(tileSet); - - SoundBuffer buffer; - buffer.loadFromFile("Jump.ogg"); - Sound sound(buffer); - - Music music; - music.openFromFile("Mario_Theme.ogg"); - music.play(); - - Clock clock; - - while (window.isOpen()) - { - - float time = clock.getElapsedTime().asMicroseconds(); - clock.restart(); - - time = time/500; // - - if (time > 20) time = 20; - - Event event; - while (window.pollEvent(event)) - { - if (event.type == Event::Closed) - window.close(); - } - - - if (Keyboard::isKeyPressed(Keyboard::Left)) Mario.dx=-0.1; - - if (Keyboard::isKeyPressed(Keyboard::Right)) Mario.dx=0.1; - - if (Keyboard::isKeyPressed(Keyboard::Up)) if (Mario.onGround) { Mario.dy=-0.27; Mario.onGround=false; sound.play();} - - - - Mario.update(time); - enemy.update(time); - - - if (Mario.rect.intersects( enemy.rect ) ) - { - if (enemy.life) { - if (Mario.dy>0) { enemy.dx=0; Mario.dy=-0.2; enemy.life=false;} - else Mario.sprite.setColor(Color::Red); - } - } - - - if (Mario.rect.left>200) offsetX = Mario.rect.left-200; // - - - - - window.clear(Color(107,140,255)); - - for (int i=0; i -#include - -using namespace sf; - - -float offsetX=0, offsetY=0; - - -const int H = 17; -const int W = 150; - - -String TileMap[H] = { -"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", -"0 0", -"0 w 0", -"0 w w w 0", -"0 w kk 0", -"0 k k k k 0", -"0 c k kkk kkk w 0", -"0 r k k k 0", -"0 rr k k 0", -"0 rrr kk 0", -"0 c kckck rrrr 0", -"0 t0 rrrrr 0", -"0G 00 t0 rrrrrr G 0", -"0 d g d 00 00 rrrrrrr 0", -"PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP", -"PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP", -"PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP", -}; - - - - -class PLAYER { - -public: - -float dx,dy; -FloatRect rect; -bool onGround; -Sprite sprite; -float currentFrame; - - PLAYER(Texture &image) - { - sprite.setTexture(image); - rect = FloatRect(100,180,16,16); - - dx=dy=0.1; - currentFrame = 0; - } - - - void update(float time) - { - - rect.left += dx * time; - Collision(0); - - - if (!onGround) dy=dy+0.0005*time; - rect.top += dy*time; - onGround=false; - Collision(1); - - - currentFrame += time * 0.005; - if (currentFrame > 3) currentFrame -= 3; - - - if (dx>0) sprite.setTextureRect(IntRect(112+31*int(currentFrame),144,16,16)); - if (dx<0) sprite.setTextureRect(IntRect(112+31*int(currentFrame)+16,144,-16,16)); - - - sprite.setPosition(rect.left - offsetX, rect.top - offsetY); - - dx=0; - } - - - void Collision(int num) -{ - - for (int i = rect.top/16 ; i<(rect.top+rect.height)/16; i++) - for (int j = rect.left/16; j<(rect.left+rect.width)/16; j++) - { - if ((TileMap[i][j]=='P') || (TileMap[i][j]=='k') || (TileMap[i][j]=='0') || (TileMap[i][j]=='r') || (TileMap[i][j]=='t')) - { - if (dy>0 && num==1) - { rect.top = i*16 - rect.height; dy=0; onGround=true; } - if (dy<0 && num==1) - { rect.top = i*16 + 16; dy=0;} - if (dx>0 && num==0) - { rect.left = j*16 - rect.width; } - if (dx<0 && num==0) - { rect.left = j*16 +16;} - } - - if (TileMap[i][j]=='c') { - // TileMap[i][j]=' '; - } - } - -} - -}; - - - -class ENEMY -{ - -public: -float dx,dy; -FloatRect rect; -Sprite sprite; -float currentFrame; -bool life; - - - void set(Texture &image, int x, int y) - { - sprite.setTexture(image); - rect = FloatRect(x,y,16,16); - - dx=0.05; - currentFrame = 0; - life=true; - } - - void update(float time) - { - rect.left += dx * time; - - Collision(); - - - currentFrame += time * 0.005; - if (currentFrame > 2) currentFrame -= 2; - - sprite.setTextureRect(IntRect(18*int(currentFrame), 0, 16,16)); - if (!life) sprite.setTextureRect(IntRect(58, 0, 16,16)); - - - sprite.setPosition(rect.left - offsetX, rect.top - offsetY); - - } - - - void Collision() - { - - for (int i = rect.top/16 ; i<(rect.top+rect.height)/16; i++) - for (int j = rect.left/16; j<(rect.left+rect.width)/16; j++) - if ((TileMap[i][j]=='P') || (TileMap[i][j]=='0')) - { - if (dx>0) - { rect.left = j*16 - rect.width; dx*=-1; } - else if (dx<0) - { rect.left = j*16 + 16; dx*=-1; } - - } - } - -}; - - - -int main() -{ - - RenderWindow window(VideoMode(400, 250), "SFML works!"); - - Texture tileSet; - tileSet.loadFromFile("Mario_Tileset.png"); - - - PLAYER Mario(tileSet); - ENEMY enemy; - enemy.set(tileSet,48*16,13*16); - - - Sprite tile(tileSet); - - SoundBuffer buffer; - buffer.loadFromFile("Jump.ogg"); - Sound sound(buffer); - - Music music; - music.openFromFile("Mario_Theme.ogg"); - music.play(); - - Clock clock; - - while (window.isOpen()) - { - - float time = clock.getElapsedTime().asMicroseconds(); - clock.restart(); - - time = time/500; // - - if (time > 20) time = 20; - - Event event; - while (window.pollEvent(event)) - { - if (event.type == Event::Closed) - window.close(); - } - - - if (Keyboard::isKeyPressed(Keyboard::Left)) Mario.dx=-0.1; - - if (Keyboard::isKeyPressed(Keyboard::Right)) Mario.dx=0.1; - - if (Keyboard::isKeyPressed(Keyboard::Up)) if (Mario.onGround) { Mario.dy=-0.27; Mario.onGround=false; sound.play();} - - - - Mario.update(time); - enemy.update(time); - - - if (Mario.rect.intersects( enemy.rect ) ) - { - if (enemy.life) { - if (Mario.dy>0) { enemy.dx=0; Mario.dy=-0.2; enemy.life=false;} - else Mario.sprite.setColor(Color::Red); - } - } - - - if (Mario.rect.left>200) offsetX = Mario.rect.left-200; // - - - - - window.clear(Color(107,140,255)); - - for (int i=0; i - - - - Debug - Win32 - - - Release - Win32 - - - - {81742766-07B6-414F-9BB1-1AB9454F1B4E} - ssssss - Mario - - - - Application - true - MultiByte - - - Application - false - true - MultiByte - - - - - - - - - - - - - - - Level3 - Disabled - D:\SFML-2.0\include - SFML_STATIC;%(PreprocessorDefinitions) - - - true - D:\SFML-2.0\lib - "sfml-graphics-s-d.lib";"sfml-window-s-d.lib";"sfml-system-s-d.lib";"sfml-audio-s-d.lib";%(AdditionalDependencies) - - - - - - - Level3 - MaxSpeed - true - true - - - true - true - true - - - - - - - - - \ No newline at end of file diff --git "a/sample/old_projects/\320\277\321\200\320\276\320\265\320\272\321\202 Visual Studio 2010/ssssss/ssssss.vcxproj.filters" "b/sample/old_projects/\320\277\321\200\320\276\320\265\320\272\321\202 Visual Studio 2010/ssssss/ssssss.vcxproj.filters" deleted file mode 100644 index 2a14256..0000000 --- "a/sample/old_projects/\320\277\321\200\320\276\320\265\320\272\321\202 Visual Studio 2010/ssssss/ssssss.vcxproj.filters" +++ /dev/null @@ -1,22 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - \ No newline at end of file diff --git a/sample/pong/SimplePong.cpp b/sample/pong/SimplePong.cpp index 1ec610f..e3a1efa 100644 --- a/sample/pong/SimplePong.cpp +++ b/sample/pong/SimplePong.cpp @@ -5,7 +5,7 @@ #include "TTFText.h" #include #include -#include +#include "DOEngine_SDL_includes.h" #include #include #include @@ -90,7 +90,7 @@ struct Paddle : public GameObject } virtual void Render() { - renderer->DrawFillRect(position, white); + renderer->DrawFillRect(position, doengine::Colors::white); } }; @@ -135,7 +135,7 @@ struct Ball : public GameObject } virtual void Render() { - renderer->FillCircle(point.x, point.y, radius, white); + renderer->FillCircle(point.x, point.y, radius, doengine::Colors::white); } }; @@ -156,7 +156,7 @@ struct PongState : public GameState, public KeyDownEvent { textHandler = new TTFText(); textHandler->setFont("/home/neon/Documents/projects/myprojects/DOEngine/assets/fonts/NirmalaB.ttf", 18); - textHandler->setColor(white); + textHandler->setColor(doengine::Colors::white); Event::AddKeyPressEventListener(this); renderer = Application::getApplication()->getRender(); ball = new Ball(renderer); @@ -196,14 +196,14 @@ struct PongState : public GameState, public KeyDownEvent virtual void Render() { - renderer->setDrawColor(black); + renderer->setDrawColor(doengine::Colors::black); renderer->clear(); textHandler->DrawText(std::to_string(leftScore).c_str(), 100, 0); textHandler->DrawText(std::to_string(rightScore).c_str(),Application::getApplication()->getW() / 2 + 40 , 0); renderer->DrawLine(Point{Application::getApplication()->getW() / 2, 0}, Point{Application::getApplication()->getW() / 2, Application::getApplication()->getH()}, - white); + doengine::Colors::white); ball->Render(); left->Render(); right->Render(); diff --git a/sample/tetris/Tetris.cpp b/sample/tetris/Tetris.cpp index d7a69a6..9e4a904 100644 --- a/sample/tetris/Tetris.cpp +++ b/sample/tetris/Tetris.cpp @@ -4,7 +4,7 @@ #include "Grids.h" #include #include -#include +#include "DOEngine_SDL_includes.h" #include #include #include @@ -14,29 +14,242 @@ using namespace doengine; constexpr int ROWS = 20; constexpr int COLUMNS = 10; +std::vector GetCellColors() +{ + return { + doengine::Colors::blue, doengine::Colors::darkGrey, + doengine::Colors::green, doengine::Colors::red, + doengine::Colors::orange, doengine::Colors::yellow1, + doengine::Colors::purple1, doengine::Colors::cyan, + doengine::Colors::blue, doengine::Colors::black, + }; +} +struct Position +{ + int row; + int col; -struct Block{ - int id; - Block(){ + Position(int r, int c) + { + row = r; + col = c; + } +}; - } +struct Block +{ + + doengine::Renderer* renderer; + Block(); + void Draw(int offsetX, int offsetY); + void Move(int rows, int columns); + std::vector GetCellPositions(); + void Rotate(); + void UndoRotation(); + int id; + std::map> cells; + int cellSize; + int rotationState; + std::vector colors; + int rowOffset; + int columnOffset; }; +Block::Block() +{ + cellSize = 30; + rotationState = 0; + colors = GetCellColors(); + rowOffset = 0; + columnOffset = 0; +} +void Block::Draw(int offsetX, int offsetY) +{ + std::vector tiles = GetCellPositions(); + for (Position item : tiles) + { + renderer->DrawFillRect({item.col * cellSize + offsetX, + item.row * cellSize + offsetY, cellSize - 1, + cellSize - 1}, + colors[id]); + } +} -struct TetrisGrid : public GameObject +void Block::Move(int rows, int columns) { + rowOffset += rows; + columnOffset += columns; +} - std::vector colors; +std::vector Block::GetCellPositions() +{ + std::vector tiles = cells[rotationState]; + std::vector movedTiles; + for (Position item : tiles) + { + Position newPos = + Position(item.row + rowOffset, item.col + columnOffset); + movedTiles.push_back(newPos); + } + return movedTiles; +} + +void Block::Rotate() +{ + rotationState++; + if (rotationState == (int)cells.size()) + { + rotationState = 0; + } +} + +void Block::UndoRotation() +{ + rotationState--; + if (rotationState == -1) + { + rotationState = cells.size() - 1; + } +} +class LBlock : public Block +{ + public: + LBlock() + { + id = 1; + cells[0] = {Position(0, 2), Position(1, 0), Position(1, 1), + Position(1, 2)}; + cells[1] = {Position(0, 1), Position(1, 1), Position(2, 1), + Position(2, 2)}; + cells[2] = {Position(1, 0), Position(1, 1), Position(1, 2), + Position(2, 0)}; + cells[3] = {Position(0, 0), Position(0, 1), Position(1, 1), + Position(2, 1)}; + Move(0, 3); + } +}; + +class JBlock : public Block +{ + public: + JBlock() + { + id = 2; + cells[0] = {Position(0, 0), Position(1, 0), Position(1, 1), + Position(1, 2)}; + cells[1] = {Position(0, 1), Position(0, 2), Position(1, 1), + Position(2, 1)}; + cells[2] = {Position(1, 0), Position(1, 1), Position(1, 2), + Position(2, 2)}; + cells[3] = {Position(0, 1), Position(1, 1), Position(2, 0), + Position(2, 1)}; + Move(0, 3); + } +}; + +class IBlock : public Block +{ + public: + IBlock() + { + id = 3; + cells[0] = {Position(1, 0), Position(1, 1), Position(1, 2), + Position(1, 3)}; + cells[1] = {Position(0, 2), Position(1, 2), Position(2, 2), + Position(3, 2)}; + cells[2] = {Position(2, 0), Position(2, 1), Position(2, 2), + Position(2, 3)}; + cells[3] = {Position(0, 1), Position(1, 1), Position(2, 1), + Position(3, 1)}; + Move(-1, 3); + } +}; + +class OBlock : public Block +{ + public: + OBlock() + { + id = 4; + cells[0] = {Position(0, 0), Position(0, 1), Position(1, 0), + Position(1, 1)}; + Move(0, 4); + } +}; + +class SBlock : public Block +{ + public: + SBlock() + { + id = 5; + cells[0] = {Position(0, 1), Position(0, 2), Position(1, 0), + Position(1, 1)}; + cells[1] = {Position(0, 1), Position(1, 1), Position(1, 2), + Position(2, 2)}; + cells[2] = {Position(1, 1), Position(1, 2), Position(2, 0), + Position(2, 1)}; + cells[3] = {Position(0, 0), Position(1, 0), Position(1, 1), + Position(2, 1)}; + Move(0, 3); + } +}; + +class TBlock : public Block +{ + public: + TBlock() + { + id = 6; + cells[0] = {Position(0, 1), Position(1, 0), Position(1, 1), + Position(1, 2)}; + cells[1] = {Position(0, 1), Position(1, 1), Position(1, 2), + Position(2, 1)}; + cells[2] = {Position(1, 0), Position(1, 1), Position(1, 2), + Position(2, 1)}; + cells[3] = {Position(0, 1), Position(1, 0), Position(1, 1), + Position(2, 1)}; + Move(0, 3); + } +}; + +class ZBlock : public Block +{ + public: + ZBlock() + { + id = 7; + cells[0] = {Position(0, 0), Position(0, 1), Position(1, 1), + Position(1, 2)}; + cells[1] = {Position(0, 2), Position(1, 1), Position(1, 2), + Position(2, 1)}; + cells[2] = {Position(1, 0), Position(1, 1), Position(2, 1), + Position(2, 2)}; + cells[3] = {Position(0, 1), Position(1, 0), Position(1, 1), + Position(2, 0)}; + Move(0, 3); + } +}; + +struct Grid +{ + doengine::Renderer* renderer; + int rows; + int cols; + int cell_size; int grid[ROWS][COLUMNS]; + bool IsRowFull(int row); + void ClearRow(int row); + void MoveRowDown(int row, int numRows); int numRows; int numCols; - int cellsize; - Renderer* renderer; - - std::vector getColors() + int cellSize; + std::vector colors; + Grid() { - return {gray, green, red, orange, yellow, purple, cyan, blue}; + renderer = doengine::Application::getApplication()->getRender(); + cell_size = 32; } void reset() @@ -45,46 +258,243 @@ struct TetrisGrid : public GameObject { for (int c = 0; c < COLUMNS; c++) { - grid[r][c] = 0; + grid[r][c] = 1; } } } + void Render() + { + for (int r = 0; r < ROWS; r++) + { + for (int c = 0; c < COLUMNS; c++) + { + doengine::Rect offset; + doengine::LogOuput(doengine::logger_type::Error, "(%d, %d)", r, + c); + offset.x = c * cell_size; + offset.y = r * cell_size; + offset.w = cell_size; + offset.h = cell_size; - TetrisGrid(Renderer *r) + int cell_value = grid[r][c]; + + renderer->DrawFillRect(offset, GetCellColors()[cell_value]); + offset.LogIt(); + renderer->DrawRect(offset, doengine::Colors::white); + } + } + } + bool IsCellOutside(int row, int column); + bool IsCellEmpty(int row, int column); + int ClearFullRows(); +}; +bool Grid::IsCellOutside(int row, int column) +{ + if (row >= 0 && row < numRows && column >= 0 && column < numCols) { - renderer = r; - numRows = ROWS; - numCols = COLUMNS; - cellsize = 32; - colors = getColors(); - reset(); + return false; } - virtual void Update(float timer = 0) override + return true; +} + +bool Grid::IsCellEmpty(int row, int column) +{ + if (grid[row][column] == 0) { + return true; } + return false; +} - virtual void Render() override +int Grid::ClearFullRows() +{ + int completed = 0; + for (int row = numRows - 1; row >= 0; row--) { - for (int r = 0; r < ROWS; r++) + if (IsRowFull(row)) { - for (int c = 0; c < COLUMNS; c++) - { - int value = grid[r][c]; - Rect rect{c*cellsize+1, r*cellsize+1, cellsize-1, cellsize-1}; - renderer->DrawRect(rect, colors[value]); - } + ClearRow(row); + completed++; + } + else if (completed > 0) + { + MoveRowDown(row, completed); } } -}; + return completed; +} + +bool Grid::IsRowFull(int row) +{ + for (int column = 0; column < numCols; column++) + { + if (grid[row][column] == 0) + { + return false; + } + } + return true; +} + +void Grid::ClearRow(int row) +{ + for (int column = 0; column < numCols; column++) + { + grid[row][column] = 0; + } +} + +void Grid::MoveRowDown(int row, int numRows) +{ + for (int column = 0; column < numCols; column++) + { + grid[row + numRows][column] = grid[row][column]; + grid[row][column] = 0; + } +} struct TetrisState : public doengine::GameState { doengine::Renderer* render; - TetrisGrid* tetris= nullptr; + bool gameOver; + int score; + void MoveBlockLeft() + { + if (!gameOver) + { + currentBlock.Move(0, -1); + if (IsBlockOutside() || BlockFits() == false) + { + currentBlock.Move(0, 1); + } + } + } + void MoveBlockRight() + { + if (!gameOver) + { + currentBlock.Move(0, 1); + if (IsBlockOutside() || BlockFits() == false) + { + currentBlock.Move(0, -1); + } + } + } + Block GetRandomBlock() + { + if (blocks.empty()) + { + blocks = GetAllBlocks(); + } + int randomIndex = rand() % blocks.size(); + Block block = blocks[randomIndex]; + blocks.erase(blocks.begin() + randomIndex); + return block; + } + std::vector GetAllBlocks() + { + return {IBlock(), JBlock(), LBlock(), OBlock(), + SBlock(), TBlock(), ZBlock()}; + } + bool IsBlockOutside() + { + std::vector tiles = currentBlock.GetCellPositions(); + for (Position item : tiles) + { + if (grid.IsCellOutside(item.row, item.col)) + { + return true; + } + } + return false; + } + void RotateBlock() + { + if (!gameOver) + { + currentBlock.Rotate(); + if (IsBlockOutside() || BlockFits() == false) + { + currentBlock.UndoRotation(); + } + else + { + /// PlaySound(rotateSound); + } + } + } + void LockBlock() + { + std::vector tiles = currentBlock.GetCellPositions(); + for (Position item : tiles) + { + grid.grid[item.row][item.col] = currentBlock.id; + } + currentBlock = nextBlock; + if (BlockFits() == false) + { + gameOver = true; + } + nextBlock = GetRandomBlock(); + int rowsCleared = grid.ClearFullRows(); + if (rowsCleared > 0) + { + ///PlaySound(clearSound); + UpdateScore(rowsCleared, 0); + } + } + bool BlockFits() + { + std::vector tiles = currentBlock.GetCellPositions(); + for (Position item : tiles) + { + if (grid.IsCellEmpty(item.row, item.col) == false) + { + return false; + } + } + return true; + } + void Reset() + { + grid.reset(); + blocks = GetAllBlocks(); + currentBlock = GetRandomBlock(); + nextBlock = GetRandomBlock(); + score = 0; + } + void UpdateScore(int linesCleared, int moveDownPoints) + { + switch (linesCleared) + { + case 1: + score += 100; + break; + case 2: + score += 300; + break; + case 3: + score += 500; + break; + default: + break; + } + + score += moveDownPoints; + } + Grid grid; + std::vector blocks; + Block currentBlock; + Block nextBlock; virtual void OnEnter() { render = Application::getApplication()->getRender(); - tetris = new TetrisGrid(render); + grid.reset(); + blocks = GetAllBlocks(); + currentBlock = GetRandomBlock(); + nextBlock = GetRandomBlock(); + gameOver = false; + score = 0; } virtual void OnExit() { @@ -92,28 +502,57 @@ struct TetrisState : public doengine::GameState virtual void Update(float elapsed) { - - if (doengine::Event::getLastKeyPressed(SDL_SCANCODE_A)) + if (doengine::Event::getLastKeyPressed(SDL_SCANCODE_LEFT)) { + MoveBlockLeft(); } - if (doengine::Event::getLastKeyPressed(SDL_SCANCODE_B)) + if (doengine::Event::getLastKeyPressed(SDL_SCANCODE_RIGHT)) { + MoveBlockRight(); } - if (doengine::Event::getLastKeyPressed(SDL_SCANCODE_S)) + if (doengine::Event::getLastKeyPressed(SDL_SCANCODE_DOWN)) { + MoveBlockDown(); + UpdateScore(0, 1); } - if (doengine::Event::getLastKeyPressed(SDL_SCANCODE_D)) + if (doengine::Event::getLastKeyPressed(SDL_SCANCODE_UP)) { + RotateBlock(); } - tetris->Update(); } virtual void Render() { - render->setDrawColor(black); + render->setDrawColor(doengine::Colors::black); render->clear(); - tetris->Render(); + grid.Render(); + currentBlock.Draw(11, 11); + switch (nextBlock.id) + { + case 3: + nextBlock.Draw(255, 290); + break; + case 4: + nextBlock.Draw(255, 280); + break; + default: + nextBlock.Draw(270, 270); + break; + } + } + /// void HandleInput(); + void MoveBlockDown() + { + if (!gameOver) + { + currentBlock.Move(1, 0); + if (IsBlockOutside() || BlockFits() == false) + { + currentBlock.Move(-1, 0); + LockBlock(); + } + } } }; diff --git a/src/Application/Application.cpp b/src/Application/Application.cpp index 51eb177..8949601 100644 --- a/src/Application/Application.cpp +++ b/src/Application/Application.cpp @@ -17,7 +17,6 @@ Application::Application() gsm = new GameStateManager(); fps_handler = new FpsManager(); fps_handler->setFPS(60); - } void Application::destroy() @@ -45,17 +44,19 @@ void Application::setFullScreen() void Application::setWindowMode() { - windowManager->setFullScreen(); + windowManager->setWindowMode(); } void Application::PollEvent() { fps_handler->Start(); + fps_handler->beginFrame(); Event::PollEvent(); } void Application::Update() { - gsm->Update(fps_handler->getDeltaTime()); + auto deltaTime = fps_handler->endFrame(); + gsm->Update(deltaTime); } void Application::Render() @@ -80,9 +81,50 @@ Renderer* Application::getRender() const const bool Application::IsRunning() const { + /// LogOuput(logger_type::Information, "Is running %d", run); return run; } +long Application::getElapsedTime() +{ + return fps_handler->getElapsedTime(); +} + +uint32_t Application::getDeltaTime() +{ + return fps_handler->getDeltaTime(); +} + +void Application::setW(int w) +{ + window_rect.w = w; + _internalResize(); +} +void Application::setH(int h) +{ + window_rect.h = h; + _internalResize(); +} +void Application::setSize(int w, int h) +{ + window_rect.w = w; + window_rect.h = h; + _internalResize(); +} +int Application::getH() +{ + return window_rect.h; +} +int Application::getW() +{ + return window_rect.w; +} + +Rect Application::getDisplayMode(int m) +{ + return windowManager->getWindowDisplayMode(m); +} + void Application::SetWindowPencilColor(const Color& color) { windowManager->setPincelColor(color); @@ -94,9 +136,19 @@ void Application::clearScreen(const Color& color) void Application::createWindow(const Rect& rect) { - run = windowManager->createWindow(rect); this->setW(rect.w); this->setH(rect.h); + run = windowManager->createWindow(rect); +} + +void Application::addState(GameState* state, int id) +{ + gsm->AddState(id, state); +} + +void Application::setState(int id) +{ + gsm->SetState(id); } } // namespace doengine \ No newline at end of file diff --git a/sample/old_projects/MatrixTestState.h b/src/Application/Camera.cpp similarity index 100% rename from sample/old_projects/MatrixTestState.h rename to src/Application/Camera.cpp diff --git a/src/Application/GameState.cpp b/src/Application/GameState.cpp new file mode 100644 index 0000000..13db2b4 --- /dev/null +++ b/src/Application/GameState.cpp @@ -0,0 +1,41 @@ +#include "GameState.h" +#include "Application.h" +#include "Renderer.h" +namespace doengine +{ +GameState::GameState() +{ + renderer = Application::getApplication()->getRender(); +} +GameState::~GameState() +{ +} + +std::optional GameState::queryGameObject(long long id) +{ + for (auto obj : this->registered_game_object) + { + if (obj->getId() == id) + return obj; + } + return std::nullopt; +} + +void GameState::registerGameObject(GameObject* object) +{ + if (object && queryGameObject(object->getId()) == std::nullopt) + { + registered_game_object.emplace_back(object); + } +} + +void GameState::unregisterGameObject(long long id) +{ + registered_game_object.erase( + std::remove_if(registered_game_object.begin(), + registered_game_object.end(), + [id](auto x) { return x->getId() == id; }), + registered_game_object.end()); +} + +} // namespace doengine \ No newline at end of file diff --git a/src/Application/Geometric.cpp b/src/Application/Geometric.cpp index 4d6cbc0..805cf5d 100644 --- a/src/Application/Geometric.cpp +++ b/src/Application/Geometric.cpp @@ -14,6 +14,13 @@ bool checkCollisionPointVsRect(const Rect& rect, const Point point) point.y <= rect.y + rect.h); } +bool checkPointOnRect(Point point, Rect rect) +{ + SDL_Point p{point.x, point.y}; + SDL_Rect r{rect.x, rect.y, rect.w, rect.h}; + return SDL_PointInRect(&p, &r); +} + bool checkCollision(const Rect& rect1, const Rect& rect2) { @@ -64,5 +71,56 @@ bool checkCollisionCircleRec(const Point& circle, float radius, const Rect& rect return (dx * dx + dy * dy) <= (radius * radius); } +bool isPointOnRectBorder(int mx, int my, const doengine::Rect& r, int thickness) +{ + bool insideX = (mx >= r.x && mx <= r.x + r.w); + bool insideY = (my >= r.y && my <= r.y + r.h); + + if (!insideX || !insideY) + return false; + + bool onLeft = (mx >= r.x && mx <= r.x + thickness); + bool onRight = (mx >= r.x + r.w - thickness && mx <= r.x + r.w); + bool onTop = (my >= r.y && my <= r.y + thickness); + bool onBottom = (my >= r.y + r.h - thickness && my <= r.y + r.h); + + return onLeft || onRight || onTop || onBottom; +} + +void generateCirclePoints(Point center, double r, + int steps, Point *out) +{ + for (int i = 0; i < steps; ++i) { + double theta = (2.0 * M_PI * i) / steps; + out[i].x = center.x + r * cos(theta); + out[i].y = center.y + r * sin(theta); + } +} + +std::vector checkCollisionBySide(const Rect& src, const Rect& object) +{ + std::vector ret; + if(checkCollision(src, object)) + { + + + } + return ret; +} + + +CircleGenerator::CircleGenerator(float rad, uint32_t quality): + radius{rad}, + quality{quality}, + da{static_cast(2.0f * M_PI)/static_cast(quality)} +{ +} + +Point CircleGenerator::getPoint(uint32_t i) +{ + float angle{da * static_cast(i)}; + return {static_cast(radius * cos(angle)),static_cast(radius * sin(angle)) }; +} + }; // namespace doengine \ No newline at end of file diff --git a/src/Application/OpenglLoader.cpp b/src/Application/OpenglLoader.cpp index f0c166c..d1cff82 100644 --- a/src/Application/OpenglLoader.cpp +++ b/src/Application/OpenglLoader.cpp @@ -53,4 +53,57 @@ void* OpenGLLoader::loadFunction(const char* name) { } -#endif \ No newline at end of file +#endif +#include "DOEngine_SDL_includes.h" + +static PFNGLGENVERTEXARRAYSPROC glGenVertexArrays; +static PFNGLBINDVERTEXARRAYPROC glBindVertexArray; +static PFNGLGENBUFFERSPROC glGenBuffers; +static PFNGLBINDBUFFERPROC glBindBuffer; +static PFNGLBUFFERDATAPROC glBufferData; +static PFNGLBUFFERSUBDATAPROC glBufferSubData; +static PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; +static PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray; +static PFNGLCREATESHADERPROC glCreateShader; +static PFNGLSHADERSOURCEPROC glShaderSource; +static PFNGLCOMPILESHADERPROC glCompileShader; +static PFNGLCREATEPROGRAMPROC glCreateProgram; +static PFNGLATTACHSHADERPROC glAttachShader; +static PFNGLLINKPROGRAMPROC glLinkProgram; +static PFNGLUSEPROGRAMPROC glUseProgram; +static PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation; +static PFNGLUNIFORM4FPROC glUniform4f; +static PFNGLUNIFORM2FPROC glUniform2f; +///static PFNGLDRAWARRAYSEXTPROC glDrawArrays; +static PFNGLDELETESHADERPROC glDeleteShader; + +bool LoadMinimalGL() +{ +#define LOAD_GL(fn) \ + fn = (decltype(fn))SDL_GL_GetProcAddress(#fn); \ + if (!fn) return false; + + LOAD_GL(glGenVertexArrays) + LOAD_GL(glBindVertexArray) + LOAD_GL(glGenBuffers) + LOAD_GL(glBindBuffer) + LOAD_GL(glBufferData) + LOAD_GL(glBufferSubData) + LOAD_GL(glVertexAttribPointer) + LOAD_GL(glEnableVertexAttribArray) + LOAD_GL(glCreateShader) + LOAD_GL(glShaderSource) + LOAD_GL(glCompileShader) + LOAD_GL(glCreateProgram) + LOAD_GL(glAttachShader) + LOAD_GL(glLinkProgram) + LOAD_GL(glUseProgram) + LOAD_GL(glGetUniformLocation) + LOAD_GL(glUniform4f) + LOAD_GL(glUniform2f) + ///OAD_GL(glDrawArrays) + LOAD_GL(glDeleteShader) + +#undef LOAD_GL + return true; +} \ No newline at end of file diff --git a/src/Application/SDLOpenglRenderer.cpp b/src/Application/SDLOpenglRenderer.cpp new file mode 100644 index 0000000..3604037 --- /dev/null +++ b/src/Application/SDLOpenglRenderer.cpp @@ -0,0 +1,196 @@ +#include "SDLOpenglRenderer.h" +#include "Application.h" +#include "SDLTTFText.h" +#include "SDLTexture.h" +#include +#include +#include "opengl_decls.h" + +namespace doengine +{ + + +SDLOpenglRenderer::SDLOpenglRenderer(SDL_Window* window) + : window(window), context(nullptr) +{ + mgr = Application::getApplication()->getWindow(); + + context = SDL_GL_CreateContext(window); +/// + /// LoadMinimalGL(); + + if (!context) + { + LogOuput(logger_type::Error, "Failed to create GL context: %s", + SDL_GetError()); + return; + } + LogOuput(logger_type::Error, "Info: %s", + SDL_GetError()); + + SDL_GL_MakeCurrent(window, context); + + + ///glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + ///GLint profile; + + ////glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &profile); + + primitiveGLRenderer = new PrimitiveGLRenderer(); + ////Mat4 projection = Mat4::ortho(0.0f, 800.0f, 600.0f, 0.0f, -1.0f, 1.0f); + LogOuput(logger_type::Information, "GL context created successfully %s", SDL_GetError()); + ///primitiveGLRenderer->setProjection(projection); +} + +bool SDLOpenglRenderer::isRenderOk() +{ + return context != nullptr; +} + +void* SDLOpenglRenderer::getNativeRenderer() +{ + return context; +} + +void SDLOpenglRenderer::destroy() +{ + if (context) + { + SDL_GL_DeleteContext(context); + context = nullptr; + } +} + +void SDLOpenglRenderer::clear() +{ + glClearColor(this->current_color.r / 255, current_color.g / 255, current_color.b/255, + current_color.a / 255); + glClear(GL_COLOR_BUFFER_BIT); +} + +void SDLOpenglRenderer::setDrawColor(const Color& color) +{ + current_color = color; +} + +void SDLOpenglRenderer::updateScreen() +{ + SDL_GL_SwapWindow(window); +} + +void SDLOpenglRenderer::RenderSetClipRect(const Rect& rect) +{ + +} + +void SDLOpenglRenderer::ResetRenderSetClipRect() +{ +} + +void SDLOpenglRenderer::DrawPoint(const Point& point, const Color& color) +{ + primitiveGLRenderer->drawPoint(point, Colors::green); +} + +void SDLOpenglRenderer::DrawLine(const Point& p1, const Point& p2, + const Color& color) +{ + primitiveGLRenderer->drawLine(p1,p2,color, 5); +} + +void SDLOpenglRenderer::DrawRect(const Rect& rect, const Color& color) +{ + primitiveGLRenderer->drawRect(rect,color, false); +} + +void SDLOpenglRenderer::DrawRect(const Rect& rect, const Color& color, + int thickness) +{ + primitiveGLRenderer->drawRect(rect,color, false); +} + +void SDLOpenglRenderer::DrawFillRect(const Rect& rect, const Color& color) +{ + primitiveGLRenderer->drawRect(rect,color, true); +} + +void SDLOpenglRenderer::FillCircle(int x, int y, int radius, const Color& color) +{ + primitiveGLRenderer->drawCircle({x,y}, radius,color,32,true); +} + +void SDLOpenglRenderer::DrawTriangle(int x1, int y1, int x2, int y2, int x3, + int y3, const Color& color) +{ + +} + +void SDLOpenglRenderer::DrawRoundedRect(int x, int y, int w, int h, + int cornerRadius, Color color) +{ +} + +NativeTexture* SDLOpenglRenderer::loadTextureFromImageFile(const char* src, + Color color) +{ + SDL_Surface* surface = IMG_Load(src); + if (!surface) + return nullptr; + + SDL_SetColorKey(surface, SDL_TRUE, + SDL_MapRGB(surface->format, color.r, color.g, color.b)); + + GLuint textureID; + glGenTextures(1, &textureID); + glBindTexture(GL_TEXTURE_2D, textureID); + + GLenum format = (surface->format->BytesPerPixel == 4) ? GL_RGBA : GL_RGB; + glTexImage2D(GL_TEXTURE_2D, 0, format, surface->w, surface->h, 0, format, + GL_UNSIGNED_BYTE, surface->pixels); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + SDLTexture* texture = new SDLTexture(); + texture->id = textureID; + + SDL_FreeSurface(surface); + return texture; +} + +NativeTexture* SDLOpenglRenderer::loadTextureFromImageFile(const char* src) +{ + SDL_Surface* surface = IMG_Load(src); + if (!surface) + return nullptr; + + GLuint textureID; + glGenTextures(1, &textureID); + glBindTexture(GL_TEXTURE_2D, textureID); + + GLenum format = (surface->format->BytesPerPixel == 4) ? GL_RGBA : GL_RGB; + glTexImage2D(GL_TEXTURE_2D, 0, format, surface->w, surface->h, 0, format, + GL_UNSIGNED_BYTE, surface->pixels); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + SDLTexture* texture = new SDLTexture(); + texture->id = textureID; + + SDL_FreeSurface(surface); + return texture; +} + +NativeTextRenderer* SDLOpenglRenderer::getTextRenderer() +{ + return new SDLTTFText(); +} + +NativeTexture* SDLOpenglRenderer::createTexture() +{ + return new SDLTexture(); +} + +} // namespace doengine \ No newline at end of file diff --git a/src/Application/SDLOpenglRenderer.h b/src/Application/SDLOpenglRenderer.h new file mode 100644 index 0000000..e10f3a1 --- /dev/null +++ b/src/Application/SDLOpenglRenderer.h @@ -0,0 +1,53 @@ +#ifndef SDLOPENGL_RENDERER_H_DEFINED +#define SDLOPENGL_RENDERER_H_DEFINED +#include "Color.h" +#include "SDLRenderer.h" +#include "WindowManager.h" + +#include +#include "DOEngine_SDL_includes.h" +#include "opengl_decls.h" + + +namespace doengine +{ + +struct SDLOpenglRenderer : Renderer +{ + PrimitiveGLRenderer *primitiveGLRenderer; + + SDL_GLContext context; + WindowManager* mgr; + Color current_color; + SDL_Window* window; + + public: + SDLOpenglRenderer(SDL_Window* window); + virtual bool isRenderOk(); + virtual void* getNativeRenderer(); + virtual void destroy(); + virtual void clear(); + virtual void setDrawColor(const Color&); + virtual void updateScreen(); + + virtual void RenderSetClipRect(const Rect&); + virtual void ResetRenderSetClipRect(); + virtual void DrawPoint(const Point& point, const Color& color); + virtual void DrawLine(const Point& p1, const Point& p2, const Color& color); + virtual void DrawRect(const Rect& rect, const Color& color); + virtual void DrawRect(const Rect& rect, const Color& color, int thickness); + virtual void DrawFillRect(const Rect& rect, const Color& color); + virtual void FillCircle(int x, int y, int radius, const Color& color); + virtual void DrawTriangle(int x1, int y1, int x2, int y2, int x3, int y3, + const Color& p); + virtual void DrawRoundedRect(int x, int y, int w, int h, int cornerRadius, + Color color); + virtual NativeTexture* loadTextureFromImageFile(const char* src, + Color color); + virtual NativeTexture* loadTextureFromImageFile(const char* src); + virtual NativeTextRenderer* getTextRenderer(); + virtual NativeTexture* createTexture(); +}; +} // namespace doengine + +#endif \ No newline at end of file diff --git a/src/Application/SDLOpenglWindow.cpp b/src/Application/SDLOpenglWindow.cpp new file mode 100644 index 0000000..c87752e --- /dev/null +++ b/src/Application/SDLOpenglWindow.cpp @@ -0,0 +1,131 @@ +#include "Logger.h" +#include "SDLOpenglWindowManager.h" +#include "SDLOpenglRenderer.h" +#include "DOEngine_SDL_includes.h" + +namespace doengine +{ + +SDLOpenglWindowManager::SDLOpenglWindowManager() +{ + +} + +void SDLOpenglWindowManager::initEngine() +{ + window = nullptr; + render = nullptr; + + window_rect.w = 500; + window_rect.h = 500; + + SDL_Init(SDL_INIT_EVERYTHING); + + int img_flags = IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF | IMG_INIT_WEBP | IMG_INIT_JXL | IMG_INIT_AVIF; + + IMG_Init(img_flags); + + TTF_Init(); + + + LogOuput(logger_type::Information, "SDL Created Window %s", SDL_GetError()); + // Set OpenGL version (4.5 Core) + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + + + + window = SDL_CreateWindow(" ", SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, window_rect.w, window_rect.h, + SDL_WINDOW_SHOWN | SDL_WINDOW_BORDERLESS |SDL_WINDOW_OPENGL); + + this->support_opengl = true; + + this->render = new SDLOpenglRenderer(window); + + + LogOuput(logger_type::Information, "SDL Created Window %s", SDL_GetError()); + + + setSize(window_rect); + + // Enable blending for alpha transparency + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glViewport(0, 0, 500,500); + + LogOuput(logger_type::Information, "SDL Created Window %s", SDL_GetError()); + + run = render->isRenderOk(); + + LogOuput(logger_type::Information, "SDL Created Window %s", SDL_GetError()); +} + + +SDLOpenglWindowManager::~SDLOpenglWindowManager() +{ +} + +bool SDLOpenglWindowManager::createWindow(const Rect& rect) +{ + initEngine(); + return run; +} + +bool SDLOpenglWindowManager::createWindow() +{ + initEngine(); + return run; +} + +Renderer* SDLOpenglWindowManager::getRenderer() +{ + return render; +} +bool SDLOpenglWindowManager::isValid() +{ + return render!=nullptr; +} + +void SDLOpenglWindowManager::clearScreen(const Color& color) +{ + render->setDrawColor(color); + render->clear(); +} + +void SDLOpenglWindowManager::setPincelColor(const Color& color) +{ + render->setDrawColor(color); +} +void SDLOpenglWindowManager::updateScreen() +{ + render->updateScreen(); +} +void SDLOpenglWindowManager::setFullScreen() +{ + SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN); +} +void SDLOpenglWindowManager::setWindowMode() +{ + /// SDL_SetWindowFullscreen(window, 0); +} +void SDLOpenglWindowManager::setSize(const Rect& rect) +{ + if(window!=nullptr) + SDL_SetWindowSize(window, rect.w, rect.h); + window_rect.w = rect.w; + window_rect.h = rect.h; +} +Rect SDLOpenglWindowManager::getWindowDisplayMode(int m) +{ + SDL_DisplayMode mode; + SDL_GetCurrentDisplayMode(0, &mode); + return Rect{0,0,mode.w,mode.h}; +} +void* SDLOpenglWindowManager::getNativeWindowFormatBuffer() +{ + LogOuput(logger_type::Information, "Returning a window"); + return window; +} +} // namespace doengine \ No newline at end of file diff --git a/src/Application/SDLOpenglWindowManager.h b/src/Application/SDLOpenglWindowManager.h new file mode 100644 index 0000000..c098661 --- /dev/null +++ b/src/Application/SDLOpenglWindowManager.h @@ -0,0 +1,34 @@ +#ifndef SDL_OPENGL_WINDOW_MANAGER_H_DEFINED +#define SDL_OPENGL_WINDOW_MANAGER_H_DEFINED +#include "DOEngine_SDL_includes.h" + +#include "SDLOpenglRenderer.h" +#include "SDLWindowManager.h" + +namespace doengine +{ + class SDLOpenglWindowManager : public WindowManager + { + + private: + SDL_Window *window; + void initEngine(); + public: + SDLOpenglWindowManager(); + virtual ~SDLOpenglWindowManager(); + virtual bool createWindow(const Rect& rect) override; + virtual bool createWindow() override; + virtual Renderer* getRenderer() override; + virtual bool isValid() override; + virtual void clearScreen(const Color& color) override; + virtual void setPincelColor(const Color& color) override; + virtual void updateScreen() override; + virtual void setFullScreen() override; + virtual void setWindowMode() override; + virtual void setSize(const Rect& rect) override; + virtual Rect getWindowDisplayMode(int m); + virtual void* getNativeWindowFormatBuffer() override; + }; +} + +#endif /// SDL_OPENGL_WINDOW_MANAGER_H_DEFINED \ No newline at end of file diff --git a/src/Application/SDLRenderer.cpp b/src/Application/SDLRenderer.cpp index 4145970..7c521b0 100644 --- a/src/Application/SDLRenderer.cpp +++ b/src/Application/SDLRenderer.cpp @@ -45,6 +45,17 @@ void SDLRenderer::updateScreen() SDL_RenderPresent(renderer); } +void SDLRenderer::RenderSetClipRect(const Rect& rect) +{ + SDL_Rect rectx{rect.x, rect.y, rect.w,rect.h}; + SDL_RenderSetClipRect(renderer, &rectx); +} + +void SDLRenderer::ResetRenderSetClipRect() +{ + SDL_RenderSetClipRect(renderer, nullptr); +} + void SDLRenderer::DrawPoint(const Point& point, const Color& color) { this->setDrawColor(color); @@ -83,6 +94,38 @@ void SDLRenderer::DrawFillRect(const Rect& rect, const Color& color) rect1.y = rect.y; SDL_RenderFillRect(renderer, &rect1); } + +void SDLRenderer::DrawRect(const Rect& rect, const Color& color, int thickness) +{ + if (thickness <= 0) + return; + + // Set draw color + SDL_SetRenderDrawColor( + renderer, + color.r, + color.g, + color.b, + color.a + ); + + // Draw rectangle borders by expanding inward + for (int i = 0; i < thickness; ++i) + { + SDL_Rect r; + r.x = rect.x + i; + r.y = rect.y + i; + r.w = rect.w - (i * 2); + r.h = rect.h - (i * 2); + + // Avoid invalid rects + if (r.w <= 0 || r.h <= 0) + break; + + SDL_RenderDrawRect(renderer, &r); + } +} + void SDLRenderer::FillCircle(int x, int y, int radius, const Color& color) { this->setDrawColor(color); @@ -125,6 +168,12 @@ NativeTexture* SDLRenderer::loadTextureFromImageFile(const char* src, auto texture = new SDLTexture(); return texture->loadFromFile(src); } + +NativeTexture* SDLRenderer::loadTextureFromImageFile(const char* src) +{ + auto texture = new SDLTexture(); + return texture->loadFromFile(src); +} NativeTexture* SDLRenderer::createTexture() { @@ -135,7 +184,6 @@ NativeTextRenderer* SDLRenderer::getTextRenderer() { auto textRenderer = new doengine::SDLTTFText(); return textRenderer; - /// return nullptr; } void renderFilledCircle(SDL_Renderer* renderer, int x, int y, int radius) diff --git a/src/Application/SDLRenderer.h b/src/Application/SDLRenderer.h index 9e7b720..6a60bfd 100644 --- a/src/Application/SDLRenderer.h +++ b/src/Application/SDLRenderer.h @@ -1,9 +1,6 @@ #pragma once -extern "C" -{ -#include -} +#include "DOEngine_SDL_includes.h" #include "Geometric.h" #include "Renderer.h" @@ -21,10 +18,14 @@ class SDLRenderer : public Renderer virtual void clear() override; virtual void setDrawColor(const Color& color) override; virtual void updateScreen() override; + virtual void RenderSetClipRect(const Rect& ) override; + virtual void ResetRenderSetClipRect() override; + virtual void DrawPoint(const Point& point, const Color& color); virtual void DrawLine(const Point& p1, const Point& p2, const Color& color); virtual void DrawRect(const Rect& rect, const Color& color); + virtual void DrawRect(const Rect& rect, const Color& color, int thickness); virtual void DrawFillRect(const Rect& rect, const Color& color); virtual void FillCircle(int x, int y, int radius, const Color& color); virtual void DrawTriangle(int x1, int y1, int x2, int y2, int x3, int y3, @@ -35,6 +36,7 @@ class SDLRenderer : public Renderer virtual NativeTexture* loadTextureFromImageFile(const char* src, Color color) override; + virtual NativeTexture* loadTextureFromImageFile(const char* src) override; virtual NativeTextRenderer* getTextRenderer() override; virtual NativeTexture* createTexture()override; diff --git a/src/Application/SDLTTFText.cpp b/src/Application/SDLTTFText.cpp index 440e137..b805ac5 100644 --- a/src/Application/SDLTTFText.cpp +++ b/src/Application/SDLTTFText.cpp @@ -1,7 +1,7 @@ #include "SDLTTFText.h" #include "Application.h" +#include "DOEngine_SDL_includes.h" #include "Logger.h" -#include #include #include @@ -22,19 +22,45 @@ std::map memoryBitMapFonts; int current_index = 0; } // namespace +SDLTTFText::SDLTTFText() : font{nullptr} +{ +} +SDLTTFText::~SDLTTFText() +{ + if (glyph_texture) + SDL_DestroyTexture(glyph_texture); + glyph_texture = nullptr; + if (font) + TTF_CloseFont(font); + font = nullptr; + if (this->glyphTexture) + delete glyphTexture; +} + void SDLTTFText::setColor(Color fg, Color bg) { this->fg_color = fg; this->bg_color = bg; + SDL_SetTextureColorMod(glyph_texture, fg_color.r, fg_color.g, fg_color.b); + SDL_SetTextureAlphaMod(glyph_texture, fg_color.a); } void SDLTTFText::setColor(Color color) { this->fg_color = color; + auto glyph = createGlyph(); + if(glyph) + { + SDL_DestroyTexture(this->glyph_texture); + glyph_texture = nullptr; + glyph_texture = (SDL_Texture *)(glyph->getNativeBuffer()); + } } void SDLTTFText::setForegroundColor(Color color) { this->fg_color = color; + SDL_SetTextureColorMod(glyph_texture, fg_color.r, fg_color.g, fg_color.b); + SDL_SetTextureAlphaMod(glyph_texture, fg_color.a); } void SDLTTFText::setBackgroundColor(Color color) { @@ -54,10 +80,17 @@ void SDLTTFText::setFont(const std::string& path, int fntsize) TTF_CloseFont(font); font = nullptr; } + LogOuput(logger_type::Information, "FontSRc=%s", path.c_str()); font = TTF_OpenFont(path.c_str(), fntsize); if (font) { this->path = path; + this->glyphTexture = createGlyph(); + } + else + { + LogOuput(logger_type::Error, "font could not be opened %s", + SDL_GetError()); } } @@ -73,7 +106,7 @@ static void drawText(SDL_Renderer* renderer, const std::string& text, int x, SDL_Rect srcRect = charMap[c]; SDL_Rect destRect = {x + static_cast(i * CHAR_WIDTH), y, - CHAR_WIDTH , CHAR_HEIGHT*2}; + CHAR_WIDTH, CHAR_HEIGHT * 2}; int result = SDL_RenderCopy(renderer, fontTexture, &srcRect, &destRect); /// SDL_Log("TRying....%d %s", result, SDL_GetError()); @@ -84,14 +117,17 @@ void SDLTTFText::DrawText(const char* text, int x, int y) { auto renderer = Application::getApplication()->getRender(); auto nativeRenderer = (SDL_Renderer*)renderer->getNativeRenderer(); - SDL_Log("Current Index for Drawing=%d", current_index); + /// SDL_Log("Current Index for Drawing=%d", current_index); - drawText(nativeRenderer, std::string(text), x, y); + /// drawText(nativeRenderer, std::string(text), x, y); - SDL_Log("----XXXXX"); + /// SDL_Log("----XXXXX"); - return; + /// return; + if (DrawTextByGlyphs(x, y, text)) + return; + LogOuput(logger_type::Information, "Default Behavour"); SDL_Color bg; bg.a = bg_color.a; bg.r = bg_color.r; @@ -102,8 +138,8 @@ void SDLTTFText::DrawText(const char* text, int x, int y) scolor.g = fg_color.g; scolor.b = fg_color.b; scolor.a = fg_color.a; - SDL_Surface* sf = - TTF_RenderText(font, text, scolor, toColor(black)); + SDL_Surface* sf = TTF_RenderText( + font, text, scolor, toColor(doengine::Colors::black)); SDL_Texture* texture = SDL_CreateTextureFromSurface(nativeRenderer, sf); if (texture) { @@ -138,8 +174,8 @@ void SDLTTFText::wrapText(const char* text, int maxWidth, char* wrappedText) { const char* current = text; const char* wordStart; - char line[256] = ""; - char temp[256] = ""; + char line[255] = ""; + char temp[255] = ""; int width = 0; while (*current) @@ -195,8 +231,9 @@ void SDLTTFText::wrapText(const char* text, int maxWidth, char* wrappedText) } } - -void replacePixels(SDL_Texture* texture, SDL_Renderer* renderer, int width, int height, SDL_Color newc, SDL_Color bg) { +void replacePixels(SDL_Texture* texture, SDL_Renderer* renderer, int width, + int height, SDL_Color newc, SDL_Color bg) +{ // Allocate memory for pixel data Uint32* pixels = new Uint32[width * height]; @@ -206,15 +243,18 @@ void replacePixels(SDL_Texture* texture, SDL_Renderer* renderer, int width, int SDL_PixelFormat* mappingFormat = SDL_AllocFormat(format); // Copy the current pixel data - SDL_RenderReadPixels(renderer, NULL, format, pixels, width * sizeof(Uint32)); + SDL_RenderReadPixels(renderer, NULL, format, pixels, + width * sizeof(Uint32)); // Define the yellow color to replace (0xFFFF00 in RGB) Uint32 yellow = SDL_MapRGB(mappingFormat, bg.r, bg.g, bg.b); Uint32 newColor = SDL_MapRGB(mappingFormat, newc.r, newc.g, newc.b); // Modify only yellow pixels - for (int i = 0; i < width * height; i++) { - if (pixels[i] == yellow) { + for (int i = 0; i < width * height; i++) + { + if (pixels[i] == yellow) + { pixels[i] = newColor; } } @@ -227,14 +267,12 @@ void replacePixels(SDL_Texture* texture, SDL_Renderer* renderer, int width, int SDL_FreeFormat(mappingFormat); } - - Texture* SDLTTFText::createBitmapFont(const std::string& font_path, const doengine::Color& bg, const doengine::Color& fg) { int w = 0, h = 0; - + SDL_Log("Starting bitmap font creation..."); // Get SDL Renderer @@ -246,9 +284,6 @@ Texture* SDLTTFText::createBitmapFont(const std::string& font_path, return nullptr; } - - - // Load TTF Font TTF_Font* font = TTF_OpenFont(font_path.c_str(), CHAR_HEIGHT); if (!font) @@ -275,7 +310,7 @@ Texture* SDLTTFText::createBitmapFont(const std::string& font_path, SDL_SetRenderTarget(renderer, texture); SDL_SetRenderDrawColor(renderer, bg.r, bg.g, bg.b, bg.a); - // SDL_SetRenderDrawColor(renderer, 255,255,255,255); + // SDL_SetRenderDrawColor(renderer, 255,255,255,255); SDL_RenderClear(renderer); // Render each character to the texture @@ -286,19 +321,16 @@ Texture* SDLTTFText::createBitmapFont(const std::string& font_path, char c = static_cast(i); SDL_Surface* charSurface = - ///TTF_RenderGlyph_Solid - TTF_RenderGlyph_Blended - (font, c, {fg.r, fg.g, fg.b, fg.a}); + /// TTF_RenderGlyph_Solid + TTF_RenderGlyph_Blended(font, c, {fg.r, fg.g, fg.b, fg.a}); if (!charSurface) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to render glyph %c: %s", c, TTF_GetError()); continue; } - SDL_SetColorKey(charSurface, SDL_TRUE, - SDL_MapRGB(charSurface->format, 0, - 0, - 0)); + SDL_SetColorKey(charSurface, SDL_TRUE, + SDL_MapRGB(charSurface->format, 0, 0, 0)); SDL_Texture* charTexture = SDL_CreateTextureFromSurface(renderer, charSurface); if (!charTexture) @@ -341,23 +373,373 @@ Texture* SDLTTFText::createBitmapFont(const std::string& font_path, SDL_QueryTexture(texture, nullptr, nullptr, &w, &h); SDL_Log("Created Bitmap Font Texture [%d, %d]", w, h); - SDL_Color yellows; - yellows.r = yellow.r; - yellows.g = yellow.g; - yellows.b = yellow.b; - yellows.a = yellow.a; + yellows.r = doengine::Colors::yellow.r; + yellows.g = doengine::Colors::yellow.g; + yellows.b = doengine::Colors::yellow.b; + yellows.a = doengine::Colors::yellow.a; SDL_Color newc; newc.r = 0; newc.b = 200; newc.g = 100; newc.a = 100; - replacePixels(texture,renderer, w,h, newc, yellows); + replacePixels(texture, renderer, w, h, newc, yellows); // Assign the texture to a Texture object current_index = 1; memoryBitMapFonts[1] = texture; Texture* ret = new Texture(); return ret->setNativeTexture(texture); } +constexpr const char* defaultGlyph = + " !\"#$%&'()*+,-./" + "0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`" + "abcdefghijklmnopqrstuvwxyz{|}~"; + +Texture* SDLTTFText::createGlyph() +{ + auto rrenderer = Application::getApplication()->getRender(); + auto renderer = static_cast(rrenderer->getNativeRenderer()); + if (!renderer) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Renderer is null!"); + LogOuput(logger_type::Error, "GlyphTexture is Null"); + return nullptr; + } + SDL_Color white = {fg_color.r, fg_color.g, fg_color.b, 255}; + glyph_height = TTF_FontHeight(font); + + int atlas_width = 0; + + /* First pass: calculate atlas width */ + for (const char* c = defaultGlyph; *c; ++c) + { + int w, h; + TTF_SizeText(font, std::string(1, *c).c_str(), &w, &h); + atlas_width += w; + } + + SDL_Surface* atlas = SDL_CreateRGBSurfaceWithFormat( + 0, atlas_width, glyph_height, 32, SDL_PIXELFORMAT_RGBA32); + + SDL_FillRect(atlas, nullptr, SDL_MapRGBA(atlas->format, 0, 0, 0, 0)); + + int x_offset = 0; + + /* Second pass: render glyphs */ + for (const char* c = defaultGlyph; *c; ++c) + { + char ch = *c; + + SDL_Surface* glyph_surface = TTF_RenderGlyph_Blended(font, ch, white); + + if (!glyph_surface) + continue; + + Rect dst{x_offset, 0, glyph_surface->w, glyph_surface->h}; + + SDL_BlitSurface(glyph_surface, nullptr, atlas, + reinterpret_cast(&dst)); + + int minx, maxx, miny, maxy, advance; + TTF_GlyphMetrics(font, ch, &minx, &maxx, &miny, &maxy, &advance); + + glyphs[ch] = {dst, advance}; + + x_offset += glyph_surface->w; + SDL_FreeSurface(glyph_surface); + } + + glyph_texture = SDL_CreateTextureFromSurface(renderer, atlas); + SDL_SetTextureBlendMode(glyph_texture, SDL_BLENDMODE_BLEND); + + SDL_FreeSurface(atlas); + Texture* ret = new Texture(); + if (glyphTexture) + { + delete glyphTexture; + glyphTexture = nullptr; + } + LogOuput(logger_type::Error, "GlyphTexture is Created"); + + glyphTexture = new Texture(); + glyphTexture->setNativeTexture(glyph_texture); + return ret->setNativeTexture(glyph_texture); +} + +bool SDLTTFText::DrawTextByGlyphs(int x, int y, const std::string& text, + int max_width) +{ + auto rrenderer = Application::getApplication()->getRender(); + auto renderer = static_cast(rrenderer->getNativeRenderer()); + if (!renderer) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Renderer is null!"); + LogOuput(logger_type::Error, "GlyphTexture is Null"); + return false; + } + if (!glyphTexture) + { + LogOuput(logger_type::Error, "GlyphTexture is Null"); + return false; + } + int cursor_x = x; + int cursor_y = y; + + // Apply text color + + for (char c : text) + { + if (c == '\n') + { + cursor_x = x; + cursor_y += glyph_height; + continue; + } + + auto it = glyphs.find(c); + if (it == glyphs.end()) + continue; + + const GlyphInfo& g = it->second; + + if (max_width > 0 && cursor_x + g.src.w > x + max_width) + { + cursor_x = x; + cursor_y += glyph_height; + } + + SDL_Rect src{ + g.src.x, + g.src.y, + g.src.w, + g.src.h, + }; + SDL_Rect dest{cursor_x, cursor_y, g.src.w, g.src.h}; + + SDL_RenderCopy(renderer, glyph_texture, &src, &dest); + /// SDL_RenderCopy(renderer, glyph_texture, &g.src, &dst); + /// glyphTexture->Draw(g.src,dest); + cursor_x += g.advance; + } + return true; +} + +int SDLTTFText::getFontHeight() +{ + if (font) + return TTF_FontHeight(font); + return 0; +} + +doengine::Rect SDLTTFText::getTextSize(const char* str) +{ + doengine::Rect rect; + + return rect; +} + +#include + +BitmapTextRenderer::BitmapTextRenderer() +{ + renderer = (SDL_Renderer*)Application::getApplication() + ->getRender() + ->getNativeRenderer(); +} + +BitmapTextRenderer::~BitmapTextRenderer() +{ + clearCache(); + if (font) + { + TTF_CloseFont(font); + } +} + +bool BitmapTextRenderer::setFont(const std::string& fontPath, int fontSize) +{ + if (font) + { + TTF_CloseFont(font); + } + font = TTF_OpenFont(fontPath.c_str(), fontSize); + dirty = true; + return font != nullptr; +} + +void BitmapTextRenderer::setColor(SDL_Color color) +{ + color = color; + dirty = true; +} + +void BitmapTextRenderer::setLineSpacing(int pixels) +{ + lineSpacing = pixels; + dirty = true; +} + +void BitmapTextRenderer::setAlignment(Alignment align) +{ + alignment = align; + dirty = true; +} + +void BitmapTextRenderer::setText(const std::string& text) +{ + this->text = text; + dirty = true; +} + +void BitmapTextRenderer::setConstraints(int maxWidth, int maxHeight) +{ + maxWidth = maxWidth; + maxHeight = maxHeight; + dirty = true; +} + +void BitmapTextRenderer::nextPage() +{ + if (currentPage + 1 < pages.size()) + { + currentPage++; + } +} + +void BitmapTextRenderer::prevPage() +{ + if (currentPage > 0) + { + currentPage--; + } +} + +void BitmapTextRenderer::setPage(size_t page) +{ + if (page < pages.size()) + { + currentPage = page; + } +} + +size_t BitmapTextRenderer::getCurrentPage() const +{ + return currentPage; +} + +size_t BitmapTextRenderer::getTotalPages() const +{ + return pages.size(); +} + +void BitmapTextRenderer::render(int x, int y) +{ + if (dirty) + { + rebuild(); + } + + if (pages.empty()) + return; + + int cursorY = y; + for (const auto& line : pages[currentPage]) + { + SDL_Rect dst{x, cursorY, line.width, line.height}; + + if (alignment != Alignment::Left) + { + int offset = maxWidth - line.width; + if (alignment == Alignment::Center) + offset /= 2; + dst.x += offset; + } + + SDL_RenderCopy(renderer, line.texture, nullptr, &dst); + cursorY += line.height + lineSpacing; + } +} + +void BitmapTextRenderer::clearCache() +{ + for (auto& page : pages) + { + for (auto& line : page) + { + SDL_DestroyTexture(line.texture); + } + } + pages.clear(); +} + +std::vector BitmapTextRenderer::wordWrap(const std::string& text) +{ + std::vector lines; + std::istringstream words(text); + std::string word, line; + + while (words >> word) + { + std::string test = line.empty() ? word : line + " " + word; + int w; + TTF_SizeUTF8(font, test.c_str(), &w, nullptr); + + if (w > maxWidth && !line.empty()) + { + lines.push_back(line); + line = word; + } + else + { + line = test; + } + } + if (!line.empty()) + lines.push_back(line); + return lines; +} + +void BitmapTextRenderer::buildPages(const std::vector& lines) +{ + std::vector page; + int usedHeight = 0; + + for (const auto& textLine : lines) + { + SDL_Surface* surface = + TTF_RenderUTF8_Blended(font, textLine.c_str(), color); + SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface); + + Line line{texture, surface->w, surface->h}; + SDL_FreeSurface(surface); + + if (usedHeight + line.height > maxHeight && !page.empty()) + { + pages.push_back(page); + page.clear(); + usedHeight = 0; + } + + page.push_back(line); + usedHeight += line.height + lineSpacing; + } + + if (!page.empty()) + { + pages.push_back(page); + } +} + +void BitmapTextRenderer::rebuild() +{ + clearCache(); + if (!font || text.empty()) + return; + + auto lines = wordWrap(text); + buildPages(lines); + currentPage = 0; + dirty = false; +} }; // namespace doengine \ No newline at end of file diff --git a/src/Application/SDLTTFText.h b/src/Application/SDLTTFText.h index 06d7198..026b88e 100644 --- a/src/Application/SDLTTFText.h +++ b/src/Application/SDLTTFText.h @@ -2,6 +2,7 @@ #include "DOEngine_SDL_includes.h" #include +#include #include "Color.h" #include "NativeStructs.h" @@ -15,18 +16,18 @@ namespace doengine class SDLTTFText : public NativeTextRenderer { + std::unordered_map glyphs; + int glyph_height; Color bg_color; Color fg_color; TTF_Font* font; - ::std::string path; + string path; + Texture *glyphTexture = nullptr; + SDL_Texture *glyph_texture; public: - SDLTTFText() - { - } - virtual ~SDLTTFText() - { - } + SDLTTFText(); + virtual ~SDLTTFText(); virtual void setColor(Color fg, Color bg) override; virtual void setColor(Color color) override; virtual void setFontSize(int fntSize) override; @@ -38,6 +39,73 @@ class SDLTTFText : public NativeTextRenderer virtual void getTextSize(const std::string& text, int* w, int* h); virtual void wrapText(const char* text, int maxWidth, char* wrappedText); virtual Texture* createBitmapFont(const std::string& font_path,const doengine::Color& bg,const doengine::Color& fg)override; + virtual int getFontHeight() override; + virtual Rect getTextSize(const char* str) override; + + + Texture* createGlyph(); + bool DrawTextByGlyphs(int x, int y, const std::string& text, int max_width = -1); +}; + +class BitmapTextRenderer : public NativeBitmapTextRenderer{ +public: + + + BitmapTextRenderer(); + ~BitmapTextRenderer(); + + // Configuration + bool setFont(const std::string& fontPath, int fontSize); + void setColor(SDL_Color color); + void setLineSpacing(int pixels); + void setAlignment(Alignment align); + + // Text & layout + void setText(const std::string& text); + void setConstraints(int maxWidth, int maxHeight); + + // Pagination API + void nextPage(); + void prevPage(); + void setPage(size_t page); + size_t getCurrentPage() const; + size_t getTotalPages() const; + + // Rendering + void render(int x, int y); + +private: + struct Line { + SDL_Texture* texture; + int width; + int height; + }; + + void rebuild(); + void clearCache(); + std::vector wordWrap(const std::string& text); + void buildPages(const std::vector& lines); + + SDL_Renderer* renderer; + TTF_Font* font{nullptr}; + SDL_Color color{255, 255, 255, 255}; + + std::string text; + int maxWidth{0}; + int maxHeight{0}; + int lineSpacing{4}; + Alignment alignment{Alignment::Left}; + + std::vector> pages; + size_t currentPage{0}; + + bool dirty{true}; }; + + + + + + }; // namespace doengine diff --git a/src/Application/SDLTexture.cpp b/src/Application/SDLTexture.cpp index 5a2f812..4b1ac2b 100644 --- a/src/Application/SDLTexture.cpp +++ b/src/Application/SDLTexture.cpp @@ -1,5 +1,4 @@ -#include -#include +#include "DOEngine_SDL_includes.h" #include #include "Application.h" @@ -29,8 +28,9 @@ SDL_Surface* loadSurface(const char* src, SDL_Renderer* r, { LogOuput(logger_type::Information, "Surface and Window Ready"); SDL_Surface* screen = SDL_GetWindowSurface(w); - SDL_Surface* r = SDL_ConvertSurface(surface, screen->format, 0); - SDL_FreeSurface(surface); + //// SDL_Surface* r = SDL_ConvertSurface(surface, screen->format, 0); + ///SDL_FreeSurface(surface); + auto r = surface; surface = nullptr; if (r && transparentColor) { @@ -85,30 +85,45 @@ SDL_Texture* ExtractSubTexture(SDL_Renderer* renderer, SDL_Texture* source, SDL_ return nullptr; } - // Get the texture format + if (!renderer || !source) + { + std::cerr << "Error: Renderer or source texture is null!" << std::endl; + return nullptr; + } + + // Save renderer state + SDL_Texture* oldTarget = SDL_GetRenderTarget(renderer); + Uint32 format; int access, width, height; SDL_QueryTexture(source, &format, &access, &width, &height); - // Create a new texture with the same format as the source - SDL_Texture* subTexture = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_TARGET, region.w, region.h); - if (!subTexture) { - std::cerr << "Failed to create subTexture: " << SDL_GetError() << std::endl; + // Create target texture + SDL_Texture* subTexture = + SDL_CreateTexture(renderer, + format, + SDL_TEXTUREACCESS_TARGET, + region.w, + region.h); + + if (!subTexture) + { + std::cerr << "Failed to create subTexture: " + << SDL_GetError() << std::endl; return nullptr; } - // Set the new texture as the rendering target - SDL_SetRenderTarget(renderer, subTexture); - - // Clear the subTexture with transparency SDL_SetTextureBlendMode(subTexture, SDL_BLENDMODE_BLEND); + + // Render into subTexture + SDL_SetRenderTarget(renderer, subTexture); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); SDL_RenderClear(renderer); - - // Copy the selected region from the source texture + SDL_RenderCopy(renderer, source, ®ion, nullptr); - - // Reset render target to default - SDL_SetRenderTarget(renderer, nullptr); + + // Restore renderer target + SDL_SetRenderTarget(renderer, oldTarget); return subTexture; } @@ -117,34 +132,64 @@ SDL_Texture* ExtractSubTexture(SDL_Renderer* renderer, SDL_Texture* source, SDL_ SDL_Texture* CopyTexture(SDL_Renderer* renderer, SDL_Texture* srcTexture, int width, int height) { - // Create a new target texture + if (!renderer || !srcTexture) + return nullptr; + const SDL_Color* colorMod = nullptr; + // Save renderer state + SDL_Texture* oldTarget = SDL_GetRenderTarget(renderer); + + SDL_BlendMode oldBlend; + SDL_GetTextureBlendMode(srcTexture, &oldBlend); + + Uint8 r, g, b, a; + SDL_GetTextureColorMod(srcTexture, &r, &g, &b); + SDL_GetTextureAlphaMod(srcTexture, &a); + + // Create target texture SDL_Texture* targetTexture = - SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, - SDL_TEXTUREACCESS_TARGET, width, height); + SDL_CreateTexture(renderer, + SDL_PIXELFORMAT_RGBA8888, + SDL_TEXTUREACCESS_TARGET, + width, + height); + if (!targetTexture) { - std::cerr << "Failed to create target texture: " << SDL_GetError() - << std::endl; + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "Failed to create target texture: %s", + SDL_GetError()); return nullptr; } - // Set render target to the new texture - SDL_SetRenderTarget(renderer, targetTexture); + SDL_SetTextureBlendMode(targetTexture, SDL_BLENDMODE_BLEND); + + // Apply color modulation if requested + if (colorMod) + { + SDL_SetTextureColorMod(srcTexture, + colorMod->r, + colorMod->g, + colorMod->b); + SDL_SetTextureAlphaMod(srcTexture, colorMod->a); + } - // Clear the texture (optional) + // Render to target + SDL_SetRenderTarget(renderer, targetTexture); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, srcTexture, nullptr, nullptr); - // Copy the source texture to the target texture - SDL_RenderCopy(renderer, srcTexture, NULL, NULL); - - // Reset render target to default (screen) - SDL_SetRenderTarget(renderer, NULL); + // Restore renderer + texture state + SDL_SetRenderTarget(renderer, oldTarget); + SDL_SetTextureBlendMode(srcTexture, oldBlend); + SDL_SetTextureColorMod(srcTexture, r, g, b); + SDL_SetTextureAlphaMod(srcTexture, a); return targetTexture; } -void GetColorMod(SDL_Texture* texture, SDL_Color* color){ +void GetColorMod(SDL_Texture* texture, SDL_Color* color) +{ SDL_GetTextureColorMod(texture, &color->r, &color->g, &color->b); } @@ -198,9 +243,6 @@ SDLTexture* SDLTexture::loadFromFile(const char* src) valid = this_texture != nullptr; GetColorMod(this_texture, &originalColor); SDL_QueryTexture(this_texture, nullptr, nullptr, &size.x, &size.y); - LogOuput(logger_type::Information, - "Is The texture created from SF %d Size[%d, %d]", valid, - size.x, size.y); } return this; } @@ -227,6 +269,10 @@ NativeTexture* SDLTexture::subTexture(Rect clipset) return this; } +void *SDLTexture::getNativeBuffer() +{ + return this_texture; +} NativeTexture* SDLTexture::setNativeTexture(void* text) { @@ -263,6 +309,17 @@ void SDLTexture::Destroy() } } +void SDLTexture::Draw(int x, int y) +{ + SDL_Rect rect{ + x, + y, + this->size.x, + this->size.y + }; + SDL_RenderCopy(renderer, this_texture, NULL, &rect); +} + void SDLTexture::Draw(const Rect& offset) { SDL_Rect rect{ @@ -298,13 +355,12 @@ void SDLTexture::ModulateColor(const Color& color) int SDLTexture::getWidth() { - return 0; + return this->size.x; } int SDLTexture::getHeight() { - return 0; + return this->size.y; } - } // namespace doengine \ No newline at end of file diff --git a/src/Application/SDLTexture.h b/src/Application/SDLTexture.h index eabc418..ada2e02 100644 --- a/src/Application/SDLTexture.h +++ b/src/Application/SDLTexture.h @@ -1,7 +1,7 @@ #include "Geometric.h" #include "Texture.h" -#include +#include "DOEngine_SDL_includes.h" #include namespace doengine @@ -9,6 +9,7 @@ namespace doengine struct SDLTexture : public NativeTexture { + int texture = -1; std::string path; SDL_Renderer *renderer; bool valid = false; @@ -20,6 +21,7 @@ struct SDLTexture : public NativeTexture virtual SDLTexture* loadFromFile(const char* src)override; virtual void SetTransparentColor(const Color& color)override; virtual bool validTexture() override; + virtual void Draw(int x, int y) override; virtual void Draw(const Rect& offset) override; virtual void Draw(const Rect& offset, const Rect& clipset) override; virtual void ModulateColor(const Color& color) override; @@ -28,7 +30,7 @@ struct SDLTexture : public NativeTexture virtual int getHeight() override; virtual NativeTexture* subTexture(Rect clipset) override; virtual NativeTexture* setNativeTexture(void *text)override; - + virtual void *getNativeBuffer() override; }; diff --git a/src/Application/SDLWindowManager.cpp b/src/Application/SDLWindowManager.cpp index 9e23b9a..13509b1 100644 --- a/src/Application/SDLWindowManager.cpp +++ b/src/Application/SDLWindowManager.cpp @@ -1,6 +1,6 @@ #include "SDLWindowManager.h" -#include -#include +#include "Logger.h" + namespace doengine { bool SDLWindowManager::createWindow() @@ -28,12 +28,24 @@ bool SDLWindowManager::createWindow() return run; } -bool SDLWindowManager::createWindow(const Rect& rect) +SDLWindowManager::~SDLWindowManager() { +} + +SDLWindowManager::SDLWindowManager() : WindowManager() +{ + support_opengl = false; + LogOuput(logger_type::Information, "Simple SDL Window Not Opengl Support"); SDL_Init(SDL_INIT_EVERYTHING); int img_flags = IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF | IMG_INIT_WEBP | IMG_INIT_JXL | IMG_INIT_AVIF; IMG_Init(img_flags); +} + +bool SDLWindowManager::createWindow(const Rect& rect) +{ + + SDL_GetCurrentDisplayMode(0, &mode); window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED, @@ -102,4 +114,30 @@ void* SDLWindowManager::getNativeWindowFormatBuffer() return (void*)window; } +Rect SDLWindowManager::getWindowDisplayMode(int m) +{ + /*int w, h; + ///// SDL_GetDesktopDisplayMode(m, &(SDL_DisplayMode){}); + + SDL_DisplayMode dm; + if (SDL_GetDesktopDisplayMode(m, &dm) == 0) + { + w = dm.w; + h = dm.h; + } + return Rect{ + 0,0,w,h + };*/ + + SDL_Rect usable; + SDL_GetDisplayUsableBounds(0, &usable); + + int usableW = usable.w; + int usableH = usable.h; + return Rect{ + 0,0,usableW,usableH-100 + }; +} + + } // namespace doengine \ No newline at end of file diff --git a/src/Application/SDLWindowManager.h b/src/Application/SDLWindowManager.h index 5473d7f..28e1069 100644 --- a/src/Application/SDLWindowManager.h +++ b/src/Application/SDLWindowManager.h @@ -1,8 +1,8 @@ #pragma once +#include "DOEngine_SDL_includes.h" #include "Renderer.h" #include "SDLRenderer.h" #include "WindowManager.h" -#include "DOEngine_SDL_includes.h" namespace doengine { @@ -10,24 +10,13 @@ namespace doengine /// @brief this class is for internal use only it will instance by Application class SDLWindowManager : public WindowManager { - - SDLRenderer* render; - - SDL_Window* window; - - bool run; - - SDL_Rect window_rect; - + protected: SDL_DisplayMode mode; + SDL_Window* window; public: - SDLWindowManager() : WindowManager() - { - } - virtual ~SDLWindowManager() - { - } + SDLWindowManager(); + virtual ~SDLWindowManager(); virtual bool createWindow(const Rect& rect) override; virtual bool createWindow() override; virtual Renderer* getRenderer() override; @@ -38,7 +27,7 @@ class SDLWindowManager : public WindowManager virtual void setFullScreen() override; virtual void setWindowMode() override; virtual void setSize(const Rect& rect) override; - + virtual Rect getWindowDisplayMode(int m); virtual void* getNativeWindowFormatBuffer() override; }; diff --git a/src/Application/StringUtils.cpp b/src/Application/StringUtils.cpp index f6ed719..ddae1a7 100644 --- a/src/Application/StringUtils.cpp +++ b/src/Application/StringUtils.cpp @@ -72,4 +72,9 @@ int StringAsInt(const std::string& str) return std::stoi(str); } +bool isCharInGroup(char ch, const std::string group) +{ + return (group.find(ch,0) != std::string::npos); +} + }; // namespace doengine \ No newline at end of file diff --git a/src/Application/Timer.cpp b/src/Application/Timer.cpp new file mode 100644 index 0000000..5680191 --- /dev/null +++ b/src/Application/Timer.cpp @@ -0,0 +1,48 @@ +#include "Timer.h" + +namespace doengine +{ +Timer::Timer(float length) +{ + current_time = 0; + len = length; + timeout_callback = nullptr; +} +Timer::~Timer() +{ + +} +int Timer::getLength() +{ + return len; +} +bool Timer::isTimeout() +{ + return timeout; +} +void Timer::Update(float t) +{ + current_time += t; + if(current_time >= len) + { + current_time -= len; + timeout = true; + if(timeout_callback) + timeout_callback(); + } +} +void Timer::reset() +{ + current_time = 0; + timeout = false; +} +float Timer::getTime() +{ + return current_time; +} +void Timer::addTimerCallback(std::function cb) +{ + timeout_callback = cb; +} + +} // namespace doengine \ No newline at end of file diff --git a/src/Application/WindowManager.cpp b/src/Application/WindowManager.cpp index 0e320aa..7432618 100644 --- a/src/Application/WindowManager.cpp +++ b/src/Application/WindowManager.cpp @@ -1,5 +1,6 @@ #include "WindowManager.h" #include "SDLWindowManager.h" +#include "SDLOpenglWindowManager.h" #include namespace doengine @@ -7,8 +8,18 @@ namespace doengine WindowManager* WindowManager::getWindowManager() { - SDLWindowManager* manager = new SDLWindowManager(); - return manager; + bool opengl_support = false; + if(!opengl_support) + { + SDLWindowManager* manager = new SDLWindowManager(); + return manager; + } + else + { + LogOuput(logger_type::Information," Opengl Renderer"); + WindowManager* manager = new SDLOpenglWindowManager(); /// + return manager; + } } }; // namespace doengine diff --git a/src/Application/opengl_decls.cpp b/src/Application/opengl_decls.cpp new file mode 100644 index 0000000..6667443 --- /dev/null +++ b/src/Application/opengl_decls.cpp @@ -0,0 +1,677 @@ +#include "opengl_decls.h" +#include +#include "DOEngine_SDL_includes.h" +#include "Application.h" +#include "Renderer.h" +namespace doengine +{ +#ifdef DESKTOP_DEV +// Shader sources (OpenGL 4.5 Core Profile) +static const char* VERTEX_SHADER = R"( +#version 450 core + +layout (location = 0) in vec2 aPos; + +uniform mat4 uProjection; + +void main() +{ + gl_Position = uProjection * vec4(aPos, 0.0, 1.0); +} +)"; + +static const char* FRAGMENT_SHADER = R"( +#version 450 core + +uniform vec4 uColor; +out vec4 FragColor; + +void main() +{ + FragColor = uColor; +} +)"; + +// Rectangle renderer state +struct RectRenderer +{ + GLuint VAO; + GLuint VBO; + GLuint shaderProgram; + GLint projectionLoc; + GLint colorLoc; + bool initialized; +}; + +static RectRenderer g_rectRenderer = {0, 0, 0, -1, -1, false}; + +// Load OpenGL functions +bool LoadMinimalGL() +{ +#define LOAD_GL_FUNC(name) \ + name = (decltype(name))SDL_GL_GetProcAddress(#name); \ + if (!name) \ + { \ + std::cerr << "Failed to load " << #name << std::endl; \ + return false; \ + } + + LOAD_GL_FUNC(glGenVertexArrays); + LOAD_GL_FUNC(glBindVertexArray); + LOAD_GL_FUNC(glDeleteVertexArrays); + LOAD_GL_FUNC(glGenBuffers); + LOAD_GL_FUNC(glBindBuffer); + LOAD_GL_FUNC(glDeleteBuffers); + LOAD_GL_FUNC(glBufferData); + LOAD_GL_FUNC(glBufferSubData); + LOAD_GL_FUNC(glVertexAttribPointer); + LOAD_GL_FUNC(glEnableVertexAttribArray); + LOAD_GL_FUNC(glCreateShader); + LOAD_GL_FUNC(glShaderSource); + LOAD_GL_FUNC(glCompileShader); + LOAD_GL_FUNC(glCreateProgram); + LOAD_GL_FUNC(glAttachShader); + LOAD_GL_FUNC(glLinkProgram); + LOAD_GL_FUNC(glUseProgram); + LOAD_GL_FUNC(glDeleteProgram); + LOAD_GL_FUNC(glGetUniformLocation); + LOAD_GL_FUNC(glUniform4f); + LOAD_GL_FUNC(glUniformMatrix4fv); + LOAD_GL_FUNC(glDeleteShader); + LOAD_GL_FUNC(glGetShaderiv); + LOAD_GL_FUNC(glGetShaderInfoLog); + LOAD_GL_FUNC(glGetProgramiv); + LOAD_GL_FUNC(glGetProgramInfoLog); + +#undef LOAD_GL_FUNC + return true; +} + +// Compile shader +GLuint CompileShader(GLenum type, const char* source) +{ + GLuint shader =0; + shader = glCreateShader(type); + glShaderSource(shader, 1, &source, nullptr); + glCompileShader(shader); + + // Check compilation status + GLint success; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) + { + char infoLog[512]; + glGetShaderInfoLog(shader, 512, nullptr, infoLog); + std::cerr << "Shader compilation failed:\n" << infoLog << std::endl; + return 0; + } + + return shader; +} + +// Create shader program +GLuint CreateShaderProgram() +{ + GLuint vertexShader = CompileShader(GL_VERTEX_SHADER, VERTEX_SHADER); + GLuint fragmentShader = CompileShader(GL_FRAGMENT_SHADER, FRAGMENT_SHADER); + + if (!vertexShader || !fragmentShader) + { + return 0; + } + + GLuint program = glCreateProgram(); + glAttachShader(program, vertexShader); + glAttachShader(program, fragmentShader); + glLinkProgram(program); + + // Check linking status + GLint success; + glGetProgramiv(program, GL_LINK_STATUS, &success); + if (!success) + { + char infoLog[512]; + glGetProgramInfoLog(program, 512, nullptr, infoLog); + std::cerr << "Shader linking failed:\n" << infoLog << std::endl; + } + + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + + return program; + + +} + +// Initialize rectangle renderer +bool InitRectRenderer() +{ + if (g_rectRenderer.initialized) + { + return true; + } + + // Create shader program + g_rectRenderer.shaderProgram = CreateShaderProgram(); + if (!g_rectRenderer.shaderProgram) + { + std::cerr << "Failed to create shader program" << std::endl; + return false; + } + + // Get uniform locations + g_rectRenderer.projectionLoc = + glGetUniformLocation(g_rectRenderer.shaderProgram, "uProjection"); + g_rectRenderer.colorLoc = + glGetUniformLocation(g_rectRenderer.shaderProgram, "uColor"); + + // Create VAO and VBO + glGenVertexArrays(1, &g_rectRenderer.VAO); + glGenBuffers(1, &g_rectRenderer.VBO); + + glBindVertexArray(g_rectRenderer.VAO); + glBindBuffer(GL_ARRAY_BUFFER, g_rectRenderer.VBO); + + // Allocate buffer for 6 vertices (2 triangles) with 2 floats each + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 12, nullptr, GL_DYNAMIC_DRAW); + + // Set vertex attribute pointer + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), + (void*)0); + glEnableVertexAttribArray(0); + + // Unbind + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + g_rectRenderer.initialized = true; + std::cout << "Rectangle renderer initialized successfully" << std::endl; + return true; +} + +// Create orthographic projection matrix +void CreateOrthoMatrix(float left, float right, float bottom, float top, + float* matrix) +{ + // Initialize to identity + for (int i = 0; i < 16; i++) + { + matrix[i] = 0.0f; + } + + // Orthographic projection + matrix[0] = 2.0f / (right - left); + matrix[5] = 2.0f / (top - bottom); + matrix[10] = -1.0f; + matrix[12] = -(right + left) / (right - left); + matrix[13] = -(top + bottom) / (top - bottom); + matrix[15] = 1.0f; +} + +// Draw filled rectangle using OpenGL 4.x +void GL_DrawFillRect(SDL_Window* window, const Rect& rect, const Color& color) +{ + if (!g_rectRenderer.initialized) + { + std::cerr << "Rectangle renderer not initialized!" << std::endl; + return; + } + + // Get window dimensions + int windowWidth, windowHeight; + SDL_GetWindowSize(window, &windowWidth, &windowHeight); + + // Create orthographic projection matrix (pixel coordinates) + float projMatrix[16]; + CreateOrthoMatrix(0.0f, (float)windowWidth, (float)windowHeight, 0.0f, + projMatrix); + + // Calculate vertex positions (two triangles forming a rectangle) + float x1 = rect.x; + float y1 = rect.y; + float x2 = rect.x + rect.w; + float y2 = rect.y + rect.h; + + float vertices[] = { + // First triangle + x1, y1, // Top-left + x2, y1, // Top-right + x2, y2, // Bottom-right + + // Second triangle + x1, y1, // Top-left + x2, y2, // Bottom-right + x1, y2 // Bottom-left + }; + + // Use shader program + glUseProgram(g_rectRenderer.shaderProgram); + + // Set projection matrix + glUniformMatrix4fv(g_rectRenderer.projectionLoc, 1, GL_FALSE, projMatrix); + + // Set color (convert from 0-255 to 0.0-1.0 range) + glUniform4f(g_rectRenderer.colorLoc, color.r / 255.0f, color.g / 255.0f, + color.b / 255.0f, color.a / 255.0f); + + // Bind VAO and update VBO + glBindVertexArray(g_rectRenderer.VAO); + glBindBuffer(GL_ARRAY_BUFFER, g_rectRenderer.VBO); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); + + // Draw the rectangle + glDrawArrays(GL_TRIANGLES, 0, 6); + + // Unbind + glBindVertexArray(0); + glUseProgram(0); +} + +void GL_DrawPoint(SDL_Window* window,const Point& point, const Color& color) +{ + if (!g_rectRenderer.initialized) + { + std::cerr << "Rectangle renderer not initialized!" << std::endl; + return; + } +} + + +void GL_DrawRect(SDL_Window* window, const Rect& rect, const Color& color, float thickness = 1.0f) +{ + if (!g_rectRenderer.initialized) + { + std::cerr << "Rectangle renderer not initialized!" << std::endl; + return; + } + + int windowWidth, windowHeight; + SDL_GetWindowSize(window, &windowWidth, &windowHeight); + + float projMatrix[16]; + CreateOrthoMatrix(0.0f, (float)windowWidth, (float)windowHeight, 0.0f, projMatrix); + + float x1 = rect.x; + float y1 = rect.y; + float x2 = rect.x + rect.w; + float y2 = rect.y + rect.h; + + // Four rectangles for each border + float vertices[4][12] = { + // Top border + {x1, y1, x2, y1, x2, y1 + thickness, x1, y1, x2, y1 + thickness, x1, y1 + thickness}, + // Right border + {x2 - thickness, y1, x2, y1, x2, y2, x2 - thickness, y1, x2, y2, x2 - thickness, y2}, + // Bottom border + {x1, y2 - thickness, x2, y2 - thickness, x2, y2, x1, y2 - thickness, x2, y2, x1, y2}, + // Left border + {x1, y1, x1 + thickness, y1, x1 + thickness, y2, x1, y1, x1 + thickness, y2, x1, y2} + }; + + glUseProgram(g_rectRenderer.shaderProgram); + glUniformMatrix4fv(g_rectRenderer.projectionLoc, 1, GL_FALSE, projMatrix); + glUniform4f(g_rectRenderer.colorLoc, color.r / 255.0f, color.g / 255.0f, + color.b / 255.0f, color.a / 255.0f); + + glBindVertexArray(g_rectRenderer.VAO); + glBindBuffer(GL_ARRAY_BUFFER, g_rectRenderer.VBO); + + for (int i = 0; i < 4; i++) + { + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices[i]), vertices[i]); + glDrawArrays(GL_TRIANGLES, 0, 6); + } + + glBindVertexArray(0); + glUseProgram(0); +} + +// Draw filled circle +void GL_DrawFilledCircle(SDL_Window* window, float centerX, float centerY, float radius, const Color& color, int segments = 32) +{ + if (!g_rectRenderer.initialized) + { + std::cerr << "Rectangle renderer not initialized!" << std::endl; + return; + } + + int windowWidth, windowHeight; + SDL_GetWindowSize(window, &windowWidth, &windowHeight); + + float projMatrix[16]; + CreateOrthoMatrix(0.0f, (float)windowWidth, (float)windowHeight, 0.0f, projMatrix); + + // Create triangle fan vertices + std::vector vertices; + vertices.reserve((segments + 2) * 2); + + // Center point + vertices.push_back(centerX); + vertices.push_back(centerY); + + // Circle points + for (int i = 0; i <= segments; i++) + { + float angle = 2.0f * M_PI * i / segments; + vertices.push_back(centerX + radius * cosf(angle)); + vertices.push_back(centerY + radius * sinf(angle)); + } + + glUseProgram(g_rectRenderer.shaderProgram); + glUniformMatrix4fv(g_rectRenderer.projectionLoc, 1, GL_FALSE, projMatrix); + glUniform4f(g_rectRenderer.colorLoc, color.r / 255.0f, color.g / 255.0f, + color.b / 255.0f, color.a / 255.0f); + + glBindVertexArray(g_rectRenderer.VAO); + glBindBuffer(GL_ARRAY_BUFFER, g_rectRenderer.VBO); + glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_DYNAMIC_DRAW); + + glDrawArrays(GL_TRIANGLE_FAN, 0, segments + 2); + + // Restore buffer size + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 12, nullptr, GL_DYNAMIC_DRAW); + + glBindVertexArray(0); + glUseProgram(0); +} + +// Draw circle outline +void GL_DrawCircle(SDL_Window* window, float centerX, float centerY, float radius, const Color& color, float thickness = 1.0f, int segments = 32) +{ + if (!g_rectRenderer.initialized) + { + std::cerr << "Rectangle renderer not initialized!" << std::endl; + return; + } + + int windowWidth, windowHeight; + SDL_GetWindowSize(window, &windowWidth, &windowHeight); + + float projMatrix[16]; + CreateOrthoMatrix(0.0f, (float)windowWidth, (float)windowHeight, 0.0f, projMatrix); + + std::vector vertices; + vertices.reserve(segments * 12); + + for (int i = 0; i < segments; i++) + { + float angle1 = 2.0f * M_PI * i / segments; + float angle2 = 2.0f * M_PI * (i + 1) / segments; + + float x1_outer = centerX + radius * cosf(angle1); + float y1_outer = centerY + radius * sinf(angle1); + float x1_inner = centerX + (radius - thickness) * cosf(angle1); + float y1_inner = centerY + (radius - thickness) * sinf(angle1); + + float x2_outer = centerX + radius * cosf(angle2); + float y2_outer = centerY + radius * sinf(angle2); + float x2_inner = centerX + (radius - thickness) * cosf(angle2); + float y2_inner = centerY + (radius - thickness) * sinf(angle2); + + // First triangle + vertices.push_back(x1_outer); vertices.push_back(y1_outer); + vertices.push_back(x2_outer); vertices.push_back(y2_outer); + vertices.push_back(x1_inner); vertices.push_back(y1_inner); + + // Second triangle + vertices.push_back(x2_outer); vertices.push_back(y2_outer); + vertices.push_back(x2_inner); vertices.push_back(y2_inner); + vertices.push_back(x1_inner); vertices.push_back(y1_inner); + } + + glUseProgram(g_rectRenderer.shaderProgram); + glUniformMatrix4fv(g_rectRenderer.projectionLoc, 1, GL_FALSE, projMatrix); + glUniform4f(g_rectRenderer.colorLoc, color.r / 255.0f, color.g / 255.0f, + color.b / 255.0f, color.a / 255.0f); + + glBindVertexArray(g_rectRenderer.VAO); + glBindBuffer(GL_ARRAY_BUFFER, g_rectRenderer.VBO); + glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_DYNAMIC_DRAW); + + glDrawArrays(GL_TRIANGLES, 0, vertices.size() / 2); + + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 12, nullptr, GL_DYNAMIC_DRAW); + + glBindVertexArray(0); + glUseProgram(0); +} + +// Draw line +void GL_DrawLine(SDL_Window* window, float x1, float y1, float x2, float y2, const Color& color, float thickness = 1.0f) +{ + if (!g_rectRenderer.initialized) + { + std::cerr << "Rectangle renderer not initialized!" << std::endl; + return; + } + + int windowWidth, windowHeight; + SDL_GetWindowSize(window, &windowWidth, &windowHeight); + + float projMatrix[16]; + CreateOrthoMatrix(0.0f, (float)windowWidth, (float)windowHeight, 0.0f, projMatrix); + + // Calculate perpendicular vector for thickness + float dx = x2 - x1; + float dy = y2 - y1; + float len = sqrtf(dx * dx + dy * dy); + if (len < 0.0001f) return; + + float nx = -dy / len * thickness * 0.5f; + float ny = dx / len * thickness * 0.5f; + + float vertices[] = { + x1 + nx, y1 + ny, + x2 + nx, y2 + ny, + x2 - nx, y2 - ny, + x1 + nx, y1 + ny, + x2 - nx, y2 - ny, + x1 - nx, y1 - ny + }; + + glUseProgram(g_rectRenderer.shaderProgram); + glUniformMatrix4fv(g_rectRenderer.projectionLoc, 1, GL_FALSE, projMatrix); + glUniform4f(g_rectRenderer.colorLoc, color.r / 255.0f, color.g / 255.0f, + color.b / 255.0f, color.a / 255.0f); + + glBindVertexArray(g_rectRenderer.VAO); + glBindBuffer(GL_ARRAY_BUFFER, g_rectRenderer.VBO); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + glBindVertexArray(0); + glUseProgram(0); +} + +// Draw filled triangle +void GL_DrawTriangle(SDL_Window* window, float x1, float y1, float x2, float y2, float x3, float y3, const Color& color) +{ + if (!g_rectRenderer.initialized) + { + std::cerr << "Rectangle renderer not initialized!" << std::endl; + return; + } + + int windowWidth, windowHeight; + SDL_GetWindowSize(window, &windowWidth, &windowHeight); + + float projMatrix[16]; + CreateOrthoMatrix(0.0f, (float)windowWidth, (float)windowHeight, 0.0f, projMatrix); + + float vertices[] = {x1, y1, x2, y2, x3, y3}; + + glUseProgram(g_rectRenderer.shaderProgram); + glUniformMatrix4fv(g_rectRenderer.projectionLoc, 1, GL_FALSE, projMatrix); + glUniform4f(g_rectRenderer.colorLoc, color.r / 255.0f, color.g / 255.0f, + color.b / 255.0f, color.a / 255.0f); + + glBindVertexArray(g_rectRenderer.VAO); + glBindBuffer(GL_ARRAY_BUFFER, g_rectRenderer.VBO); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); + + glDrawArrays(GL_TRIANGLES, 0, 3); + + glBindVertexArray(0); + glUseProgram(0); +} + +// Draw rectangle with thick border +void GL_DrawRectWithThickBorder(SDL_Window* window, const Rect& rect, const Color& fillColor, const Color& borderColor, float borderThickness) +{ + // Draw filled rectangle first + GL_DrawFillRect(window, rect, fillColor); + + // Draw border on top + GL_DrawRect(window, rect, borderColor, borderThickness); +} + +// Draw rectangle with rounded corners +void GL_DrawRectWithBorderRadius(SDL_Window* window, const Rect& rect, const Color& color, float radius, int segments = 8) +{ + if (!g_rectRenderer.initialized) + { + std::cerr << "Rectangle renderer not initialized!" << std::endl; + return; + } + + int windowWidth, windowHeight; + SDL_GetWindowSize(window, &windowWidth, &windowHeight); + + float projMatrix[16]; + CreateOrthoMatrix(0.0f, (float)windowWidth, (float)windowHeight, 0.0f, projMatrix); + + radius = std::min(radius, std::min(rect.w, rect.h) / 2.0f); + + std::vector vertices; + vertices.reserve(1000); + + // Center rectangle + float cx = rect.x + rect.w / 2.0f; + float cy = rect.y + rect.h / 2.0f; + + // Add center triangles (main body) + float left = rect.x + radius; + float right = rect.x + rect.w - radius; + float top = rect.y + radius; + float bottom = rect.y + rect.h - radius; + + // Center quad + vertices.insert(vertices.end(), {left, top, right, top, right, bottom}); + vertices.insert(vertices.end(), {left, top, right, bottom, left, bottom}); + + // Top rectangle + vertices.insert(vertices.end(), {left, (float)rect.y, right, (float)rect.y, right, top}); + vertices.insert(vertices.end(), {left, (float)rect.y, right, top, left, top}); + + // Bottom rectangle + vertices.insert(vertices.end(), {left, bottom, right, bottom, right, (float)rect.y + rect.h}); + vertices.insert(vertices.end(), {left, bottom, right, (float)rect.y + rect.h, left, (float)rect.y + rect.h}); + + // Left rectangle + vertices.insert(vertices.end(), {(float)rect.x, top, left, top, left, bottom}); + vertices.insert(vertices.end(), {(float)rect.x, top, left, bottom, (float)rect.x, bottom}); + + // Right rectangle + vertices.insert(vertices.end(), {right, top, (float)rect.x + rect.w, top, (float)rect.x + rect.w, bottom}); + vertices.insert(vertices.end(), {right, top, (float)rect.x + rect.w, bottom, right, bottom}); + + // Four corner circles (triangle fans) + float corners[4][2] = { + {left, top}, // Top-left + {right, top}, // Top-right + {right, bottom}, // Bottom-right + {left, bottom} // Bottom-left + }; + + float startAngles[4] = {M_PI, M_PI * 1.5f, 0.0f, M_PI * 0.5f}; + + for (int c = 0; c < 4; c++) + { + float centerX = corners[c][0]; + float centerY = corners[c][1]; + float startAngle = startAngles[c]; + + for (int i = 0; i < segments; i++) + { + float angle1 = startAngle + (M_PI * 0.5f) * i / segments; + float angle2 = startAngle + (M_PI * 0.5f) * (i + 1) / segments; + + vertices.push_back(centerX); + vertices.push_back(centerY); + vertices.push_back(centerX + radius * cosf(angle1)); + vertices.push_back(centerY + radius * sinf(angle1)); + vertices.push_back(centerX + radius * cosf(angle2)); + vertices.push_back(centerY + radius * sinf(angle2)); + } + } + + glUseProgram(g_rectRenderer.shaderProgram); + glUniformMatrix4fv(g_rectRenderer.projectionLoc, 1, GL_FALSE, projMatrix); + glUniform4f(g_rectRenderer.colorLoc, color.r / 255.0f, color.g / 255.0f, + color.b / 255.0f, color.a / 255.0f); + + glBindVertexArray(g_rectRenderer.VAO); + glBindBuffer(GL_ARRAY_BUFFER, g_rectRenderer.VBO); + glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_DYNAMIC_DRAW); + + glDrawArrays(GL_TRIANGLES, 0, vertices.size() / 2); + + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 12, nullptr, GL_DYNAMIC_DRAW); + + glBindVertexArray(0); + glUseProgram(0); +} + +// Cleanup function +void CleanupRectRenderer() +{ + if (g_rectRenderer.initialized) + { + glDeleteBuffers(1, &g_rectRenderer.VBO); + glDeleteVertexArrays(1, &g_rectRenderer.VAO); + glDeleteProgram(g_rectRenderer.shaderProgram); + g_rectRenderer.initialized = false; + } +} +#endif +////// Rect Renderer + +PrimitiveGLRenderer::PrimitiveGLRenderer() +{ + init(); +} +void PrimitiveGLRenderer::init() +{ + /// InitRectRenderer(); +} + +void PrimitiveGLRenderer::quit(){} + +void PrimitiveGLRenderer::setProjection(const Mat4& projection){} + +void PrimitiveGLRenderer::drawPoint(const Point& point, const Color& color, float size) +{ + SDL_Window *window = (SDL_Window *)Application::getApplication()->getWindow()->getNativeWindowFormatBuffer(); + +} +void PrimitiveGLRenderer::drawLine(const Point& start, const Point& end, const Color& color, + float width ) +{ + SDL_Window *window = (SDL_Window *)Application::getApplication()->getWindow()->getNativeWindowFormatBuffer(); + ///GL_DrawLine(window,start.x, start.y, end.x, end.y, color,width); +} +void PrimitiveGLRenderer::drawCircle(const Point& center, float radius, const Color& color, + int segments, bool filled) +{ + SDL_Window *window = (SDL_Window *)Application::getApplication()->getWindow()->getNativeWindowFormatBuffer(); + /// GL_DrawCircle(window,center.x, center.y, radius,color,2,segments); +} + +void PrimitiveGLRenderer::drawRect(const Rect& rect, const Color& color, bool filled) +{ + SDL_Window *window = (SDL_Window *)Application::getApplication()->getWindow()->getNativeWindowFormatBuffer(); + /// GL_DrawRect(window, rect, color); +} // namespace doengine + +} \ No newline at end of file diff --git a/src/Application/opengl_decls.h b/src/Application/opengl_decls.h new file mode 100644 index 0000000..7604e45 --- /dev/null +++ b/src/Application/opengl_decls.h @@ -0,0 +1,72 @@ +#ifndef OPENGL_DECL_H_DEFINED +#define OPENGL_DECL_H_DEFINED +#include +#include + +#include +#include "Renderer.h" +#include "Color.h" +#include "Geometric.h" +///#define DESKTOP_DEV +namespace doengine +{ +#ifdef DESKTOP_DEV +// OpenGL 4.x function pointers +static PFNGLGENVERTEXARRAYSPROC glGenVertexArrays = nullptr; +static PFNGLBINDVERTEXARRAYPROC glBindVertexArray = nullptr; +static PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays = nullptr; +static PFNGLGENBUFFERSPROC glGenBuffers = nullptr; +static PFNGLBINDBUFFERPROC glBindBuffer = nullptr; +static PFNGLDELETEBUFFERSPROC glDeleteBuffers = nullptr; +static PFNGLBUFFERDATAPROC glBufferData = nullptr; +static PFNGLBUFFERSUBDATAPROC glBufferSubData = nullptr; +static PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = nullptr; +static PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray = nullptr; +static PFNGLCREATESHADERPROC glCreateShader = nullptr; +static PFNGLSHADERSOURCEPROC glShaderSource = nullptr; +static PFNGLCOMPILESHADERPROC glCompileShader = nullptr; +static PFNGLCREATEPROGRAMPROC glCreateProgram = nullptr; +static PFNGLATTACHSHADERPROC glAttachShader = nullptr; +static PFNGLLINKPROGRAMPROC glLinkProgram = nullptr; +static PFNGLUSEPROGRAMPROC glUseProgram = nullptr; +static PFNGLDELETEPROGRAMPROC glDeleteProgram = nullptr; +static PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = nullptr; +static PFNGLUNIFORM4FPROC glUniform4f = nullptr; +static PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv = nullptr; +static PFNGLDELETESHADERPROC glDeleteShader = nullptr; +static PFNGLGETSHADERIVPROC glGetShaderiv = nullptr; +static PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog = nullptr; +static PFNGLGETPROGRAMIVPROC glGetProgramiv = nullptr; +static PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog = nullptr; +#endif +struct BaseGLRenderer +{ + virtual ~BaseGLRenderer() = default; + + virtual void init() = 0; + virtual void quit() = 0; +}; + +bool LoadMinimalGL(); + +struct PrimitiveGLRenderer : public BaseGLRenderer +{ + + + PrimitiveGLRenderer(); + virtual void init(); + virtual void quit(); + void setProjection(const Mat4& projection); + void drawPoint(const Point& point, const Color& color, float size = 5.0f); + void drawLine(const Point& start, const Point& end, const Color& color, + float width = 1.0f); + void drawCircle(const Point& center, float radius, const Color& color, + int segments = 32, bool filled = true); + + void drawRect(const Rect& rect, const Color& color, bool filled = true); +}; + +} + + +#endif ///OPENGL_DECL_H_DEFINED \ No newline at end of file diff --git a/src/Drawable/Canvas.cpp b/src/Drawable/Canvas.cpp index eb681e9..b920203 100644 --- a/src/Drawable/Canvas.cpp +++ b/src/Drawable/Canvas.cpp @@ -2,6 +2,7 @@ #include "Geometric.h" #include "TTFText.h" #include "Window.h" +#include "DOEngine_SDL_includes.h" namespace doengine { #if 0 @@ -278,7 +279,7 @@ void DrawRect(SDL_Renderer *render, SDL_Rect rect){ void DrawRect(Window *window, SDL_Rect rect){ SDL_RenderDrawRect(static_cast(window->getRender()->getNativeRenderer()), &rect); } -#endif + void DrawSinglePoint(const Rect& rect, const Color& color, doengine::WindowManager* window) { @@ -310,7 +311,7 @@ void DrawFillRect(const Rect& rect, const Color& color, &rect); } -#if 0 + // Create a gradient color between two colors at a specific position SDL_Color GradientColor(SDL_Color startColor, SDL_Color endColor, float position) { SDL_Color result; @@ -357,6 +358,7 @@ void DrawGradientCircle(SDL_Renderer* renderer, int centerX, int centerY, int ra } #endif +#if 0 void FillCircle(int x, int y, int radius, const Color& color, doengine::WindowManager* window) { @@ -536,5 +538,5 @@ Canvas* Canvas::clearCanvas() commands_to_draw.clear(); return this; } - +#endif } // namespace doengine \ No newline at end of file diff --git a/src/Drawable/Draggable.cpp b/src/Drawable/Draggable.cpp new file mode 100644 index 0000000..97fb8ab --- /dev/null +++ b/src/Drawable/Draggable.cpp @@ -0,0 +1,8 @@ +#include "Draggable.h" + + +namespace doengine +{ +Draggable::~Draggable(){} +Draggable::Draggable(){} +} \ No newline at end of file diff --git a/src/Drawable/DraggableGameObject.cpp b/src/Drawable/DraggableGameObject.cpp new file mode 100644 index 0000000..39f2ecc --- /dev/null +++ b/src/Drawable/DraggableGameObject.cpp @@ -0,0 +1,159 @@ +#include "DraggableGameObject.h" +#include "Geometric.h" +#include "Event.h" +#include "Logger.h" +namespace doengine +{ + +void DraggableObject::setSelected(bool selected) +{ + this->selected = selected; +} + +void DraggableObject::startDraggingPoint(const doengine::Rect& start) +{ + if (this->selected) + { + offset.x = start.x; + offset.y = start.y; + } +} +void DraggableObject::registerOnSelectedHappenedCallback(std::function fn) +{ + onSelectedHappened.emplace_back(fn); +} +void DraggableObject::registerOnSelectedStoppedCallback(std::function fn) +{ + onSelectedStopped.emplace_back(fn); +} +void DraggableObject::registerOnMovementHappenedCallback(std::function fn) +{ + onMovementHappened.emplace_back(fn); +} +void DraggableObject::registerOnMovementStoppedCallback(std::function fn) +{ + onMovementStopped.emplace_back(fn); +} + +Rect DraggableObject::getDragablePosition() +{ + return offset; +} +void DraggableObject::setObjectDimensionAndPosition(const doengine::Rect rect) +{ + offset = rect; +} + +void DraggableObject::updateCoords(const doengine::Rect& rect) +{ + if (this->selected) + { + offset.x = rect.x; + offset.y = rect.y; + } +} +void DraggableObject::render(doengine::Renderer *render) +{ + //// does nothing.... as protected +} +void DraggableObject::MouseMove(const Mouse& mouse) +{ + updateCoords(mouse.getMousePosition()); +} +void DraggableObject::MouseButtonDown(const Mouse& mouse) +{ + this->selected = doengine::checkCollision(offset, mouse.getMousePosition()); + LogOuput(logger_type::Information, "Selected %d", selected); +} +void DraggableObject::MouseButtonUp(const Mouse& mouse) +{ + selected = false; +} + + + + + + + + +DraggableGameObject::~DraggableGameObject() +{ +} + +DraggableGameObject::DraggableGameObject() +{ + offset.x = 400; + offset.y = 500; + offset.w = 32; + offset.h = 32; +} +DraggableGameObject::DraggableGameObject(int x, int y) +{ + offset.x = x; + offset.y = y; + offset.w = 32; + offset.h = 32; +} + +DraggableGameObject::DraggableGameObject(int x, int y, int spriteId, int spOffset) +{ + offset.x = x; + offset.y = y; + offset.w = 64; + offset.h = 64; + sprite_id = spriteId; + sprite_offset = spOffset; +} + +void DraggableGameObject::setSpriteOffset(std::unordered_map sprites) +{ + this->spriteOffsets = sprites; +} + + +void DraggableGameObject::setSelectedIfCollided(const doengine::Rect& rect) +{ + this->selected = doengine::checkCollision(offset, rect); +} + +void DraggableGameObject::setSelected(bool selected) +{ + this->selected = selected; +} + +void DraggableGameObject::startDraggingPoint(const doengine::Rect& start) +{ + if (this->selected) + { + offset.x = start.x; + offset.y = start.y; + } +} + +void DraggableGameObject::updateCoords(const doengine::Rect& o) +{ + if (this->selected) + { + offset.x = o.x; + offset.y = o.y; + offset.w = 32; /// o.w; + offset.h = 32; /// o.h; + } +} + +void DraggableGameObject::render(doengine::Renderer* render) +{ + + auto texture = + doengine::TextureManager::getTextureManager()->getTexture(sprite_id); + if (texture) + { + auto rect = spriteOffsets[sprite_offset]; + this->offset.w = rect.w; + this->offset.h = rect.h; + texture->Draw(this->offset, rect); + } +} + +} // namespace doengine \ No newline at end of file diff --git a/src/Drawable/GameObject.cpp b/src/Drawable/GameObject.cpp new file mode 100644 index 0000000..f7f97a7 --- /dev/null +++ b/src/Drawable/GameObject.cpp @@ -0,0 +1,25 @@ +#include "GameObject.h" +#include "Renderer.h" +#include "Application.h" +namespace doengine +{ +GameObject::GameObject() +{ + renderer= Application::getApplication()->getRender(); + id = ++id_pool; +} +GameObject::~GameObject() +{ +} +Rect GameObject::getPosition() +{ + return position; +} + +Renderer* GameObject::getRenderer() +{ + return renderer; +} + + +}; \ No newline at end of file diff --git a/src/Drawable/MessageBox.cpp b/src/Drawable/MessageBox.cpp new file mode 100644 index 0000000..aec3b4c --- /dev/null +++ b/src/Drawable/MessageBox.cpp @@ -0,0 +1,93 @@ + +#include "MessageBox.h" + + +namespace doengine +{ + +MessageBox::MessageBox(const std::string& text, + const std::string& fontsrc, + std::function onClick) + : text(text), + onClick(onClick) +{ + auto* app = doengine::Application::getApplication(); + + position.w = app->getW() / 2; + position.h = 200; + position.x = (app->getW() - position.w) / 2; + position.y = (app->getH() - position.h) / 2; + + font = std::make_unique(); + font->setFont(fontsrc, 19); + font->setForegroundColor(doengine::Colors::red); + + renderer = app->getRender(); + + // Text position + tposition.x = position.x + 32; + tposition.y = position.y + 32; + + // Main button position (bottom-right) + bposition.w = 200; + bposition.h = 64; + bposition.x = position.x + position.w - bposition.w - 32; + bposition.y = position.y + position.h - bposition.h - 32; + + // Close button position (top-right) + cposition.w = 32; + cposition.h = 32; + cposition.x = position.x + position.w - cposition.w - 16; + cposition.y = position.y + 16; +} + +void MessageBox::render() +{ + if(!visible) + return ; + // Draw message box + renderer->DrawFillRect(position, doengine::Color(0x43, 0x43, 0x43, 255)); + + // Draw text + font->setColor(doengine::Colors::white); + font->DrawText(tposition.x, tposition.y, text.c_str()); + + // Draw main button + renderer->DrawFillRect(bposition, doengine::Colors::green); + font->DrawText(((bposition.w/2) + bposition.x) - 32, (bposition.y+(bposition.h/2)-16), "Accept"); + + + // Draw close button + renderer->DrawFillRect(cposition, doengine::Colors::red); +} + +void MessageBox::MouseButtonUp(const Mouse& m) +{ + if(!visible) + return ; + doengine::Rect mouse = m.getMousePosition(); + // Check main button click + if (mouse.x >= bposition.x && mouse.x <= bposition.x + bposition.w && + mouse.y >= bposition.y && mouse.y <= bposition.y + bposition.h) + { + doengine::LogOuput(doengine::logger_type::Information, "CLICked Accpeted"); + + if (onClick) + onClick(1); // 1 = main button + visible = false; + } + + // Check close button click + if (mouse.x >= cposition.x && mouse.x <= cposition.x + cposition.w && + mouse.y >= cposition.y && mouse.y <= cposition.y + cposition.h) + { + doengine::LogOuput(doengine::logger_type::Information, "CLICked Cancel"); + if (onClick) + onClick(0); // 0 = close button + visible = false; + + } +} + + +} diff --git a/src/Drawable/SelectionRect.cpp b/src/Drawable/SelectionRect.cpp new file mode 100644 index 0000000..e42d1fe --- /dev/null +++ b/src/Drawable/SelectionRect.cpp @@ -0,0 +1,137 @@ +#include "SelectionRect.h" + +namespace doengine +{ + + void SelectionRect::setup() + { + if(!font) + font = new doengine::TTFText(); + if(!fontsrc.empty()) + font->setFont(fontsrc, 18); + else + { + //// + } + } + +SelectionRect::SelectionRect(): + font{nullptr} +{ + setup(); +} + +SelectionRect::~SelectionRect() +{ +} + +doengine::Rect SelectionRect::NormalizeRect(const doengine::Rect& a, + const doengine::Rect& b) +{ + doengine::Rect r; + r.x = std::min(a.x, b.x); + r.y = std::min(a.y, b.y); + r.w = std::abs(a.x - b.x); + r.h = std::abs(a.y - b.y); + return r; +} + +void SelectionRect::setFont(const std::string& font) +{ + this->fontsrc = font; + + if(!this->font) + setup(); + else + { + this->font->setFont(font, 18); + } +} + +void SelectionRect::startDraggingPoint(const doengine::Rect& start) +{ + if (!function_active) + return; + this->start.x = start.x; + this->start.y = start.y; +} + +void SelectionRect::updateCoords(const doengine::Rect& rect) +{ + if (function_active && active) + { + this->current.x = rect.x; + this->current.y = rect.y; + } +} + +void SelectionRect::Render() +{ + if (!function_active) + { + return; + } + auto rect = NormalizeRect(start, current); + renderer->DrawRect(rect, doengine::Colors::red); + /// if(active) + { + int y = rect.y - 32; + if (y <= 16) + y = rect.y + 64; + + font->DrawText(rect.x, y, "(%d,%d, %d, %d)", rect.x, rect.y, rect.w, + rect.h); + if(onSelectionFinished) + { + onSelectionFinished(rect); + } + } +} + +void SelectionRect::OnKeydown(const Keyboard& keyboard) +{ + + auto key = keyboard.getKeysBitset(); + + if (key[SCANCODE_ESCAPE]) + { + function_active = (!function_active); + } +} + +void SelectionRect::OnKeyup(const Keyboard& keyboard) +{ +} + +void SelectionRect::MouseMove(const Mouse& mouse) +{ + if (function_active && active) + { + updateCoords({mouse.getMousePosition()}); + } +} +void SelectionRect::MouseButtonDown(const Mouse& mouse) +{ + if (function_active) + { + active = true; + startDraggingPoint(mouse.getMousePosition()); + updateCoords({mouse.getMousePosition()}); + } +} +void SelectionRect::MouseButtonUp(const Mouse& mouse) +{ + if (function_active) + active = false; +} +void SelectionRect::registerSelectionRectFinished(std::function fn) +{ + onSelectionFinished = fn; +} +void SelectionRect::setFontColor(const doengine::Color& color) +{ + if(font) + font->setForegroundColor(color); +} + +} // namespace doengine \ No newline at end of file diff --git a/src/Drawable/TextField.cpp b/src/Drawable/TextField.cpp new file mode 100644 index 0000000..6e5d53b --- /dev/null +++ b/src/Drawable/TextField.cpp @@ -0,0 +1,106 @@ + +#include "TextField.h" +#include "Color.h" +#include "Geometric.h" +#include "Logger.h" + +namespace doengine +{ + +TextField::TextField(int x, int y, int w, int h, const std::string& font) + : rect{x, y, w, h}, fontsrc(font), renderer(renderer), focused(false) +{ + textColor = {255, 0, 0, 255}; // Black text + bgColor = {255, 255, 255, 255}; // White background + borderColor = {0, 0, 0, 255}; // Black border + text = ""; + this->font = new TTFText(); + this->font->setFont(font, 19); + this->font->setColor(textColor); + renderer = Application::getApplication()->getRender(); +} + +void TextField::MouseMove(const Mouse& mouse) +{ +} +void TextField::MouseButtonDown(const Mouse& mouse) +{ + auto button = mouse.getMousePosition(); + int mx = button.x; + int my = button.y; + focused = (mx >= rect.x && mx <= rect.x + rect.w && my >= rect.y && + my <= rect.y + rect.h); +} +void TextField::MouseButtonUp(const Mouse&) +{ +} + void TextField::MouseWheel(const Mouse&){} +void TextField::OnKeydown(const Keyboard& keyboard) +{ + auto keys = keyboard.getKeysBitset(); + if (keys[SCANCODE_BACKSPACE] && !text.empty()) + { + text.pop_back(); + } + if (keys[SCANCODE_RETURN] && onEnterPressed) + { + onEnterPressed(getText()); + } +} + +void TextField::OnKeyup(const Keyboard&) +{ +} +#include "Logger.h" +void TextField::OnTextInput(const std::string& text) +{ + ////LogOuput(logger_type::Info,"HE"); + if (focused) + { + this->text += text; + } +} + +void TextField::Render() +{ + LogOuput(logger_type::Information,"TextField::Render"); + // Draw background + /// SDL_SetRenderDrawColor(renderer, bgColor.r, bgColor.g, bgColor.b, + /// bgColor.a); SDL_RenderFillRect(renderer, &rect); + renderer->DrawFillRect(rect, bgColor); + // Draw border + /// SDL_SetRenderDrawColor(renderer, borderColor.r, borderColor.g, + /// borderColor.b, borderColor.a); SDL_RenderDrawRect(renderer, &rect); + renderer->DrawRect(rect, borderColor, 1); + // Draw text + if (!text.empty()) + { + // SDL_Surface* textSurface = TTF_RenderText_Blended(font, + // text.c_str(), textColor); SDL_Texture* textTexture = + // SDL_CreateTextureFromSurface(renderer, textSurface); + + int textSurfaceH = 100; + int textSurfaceW = 100; + + Rect textRect = {rect.x + 5, rect.y + (rect.h - textSurfaceH) / 2, + textSurfaceW, textSurfaceH}; + + font->setColor(Colors::red); + font->DrawText(rect.x + 15, rect.y + 14, text.c_str()); + /// SDL_RenderCopy(renderer, textTexture, nullptr, &textRect); + /// SDL_FreeSurface(textSurface); + /// SDL_DestroyTexture(textTexture); + } +} + +void TextField::setOnEnterKeyPressed(std::function fn) +{ + this->onEnterPressed = fn; +} + +std::string TextField::getText() const +{ + return text; +} + +} // namespace doengine \ No newline at end of file diff --git a/src/Drawable/UI.cpp b/src/Drawable/UI.cpp new file mode 100644 index 0000000..05aa35d --- /dev/null +++ b/src/Drawable/UI.cpp @@ -0,0 +1,106 @@ +#include "UI.h" +#include "Mouse.h" +#include "Application.h" + +#include "DOEngine_SDL_includes.h" + +namespace doengine +{ + +MouseBtn ToMouseBtn(Uint8 sdlButton) +{ + switch (sdlButton) + { + case SDL_BUTTON_LEFT: return MouseBtn::Left; + case SDL_BUTTON_RIGHT: return MouseBtn::Right; + case SDL_BUTTON_MIDDLE: return MouseBtn::Middle; + default: return MouseBtn::None; + } +} + +BorderPress GetBorderPressState( + const SDL_Rect& r, + int thickness = 2 +) +{ + BorderPress result{}; + result.pressed = false; + result.border = Border::None; + result.button = MouseBtn::None; + + int mx, my; + Uint32 buttons = SDL_GetMouseState(&mx, &my); + + // No button pressed at all + if (buttons == 0) + return result; + + // Inside rectangle? + if (mx < r.x || mx > r.x + r.w || + my < r.y || my > r.y + r.h) + return result; + + bool left = mx >= r.x && mx <= r.x + thickness; + bool right = mx >= r.x + r.w - thickness && mx <= r.x + r.w; + bool top = my >= r.y && my <= r.y + thickness; + bool bottom = my >= r.y + r.h - thickness && my <= r.y + r.h; + + // Optional: exclude corners + if ((left || right) && (top || bottom)) + return result; + + if (left) result.border = Border::Left; + else if (right) result.border = Border::Right; + else if (top) result.border = Border::Top; + else if (bottom) result.border = Border::Bottom; + else return result; + + // Detect which button is pressed + if (buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) + result.button = MouseBtn::Left; + else if (buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) + result.button = MouseBtn::Right; + else if (buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) + result.button = MouseBtn::Middle; + else + return result; + + result.pressed = true; + result.x = mx; + result.y = my; + + return result; +} + + +UIPanel::UIPanel() +{ + backgroundColor.r = 0x43; + backgroundColor.g = 0x43; + backgroundColor.b = 0x43; + backgroundColor.a = 255; + this->size.w = 200; + this->size.h = 200; + renderer = Application::getApplication()->getRender(); +} + +UIPanel::~UIPanel() +{ + +} + +void UIPanel::Draw() +{ + renderer->DrawFillRect(size, backgroundColor); + if(mouseOverPanelBorder){ + getRenderer()->DrawFillRect(size, doengine::Colors::yellow1); + getRenderer()->DrawRect(size, doengine::Colors::red); + } +} + +void UIPanel::Update() +{ + mouseOverPanelBorder = false; +} + +} \ No newline at end of file diff --git a/src/Event/Event.cpp b/src/Event/Event.cpp index 16d7e8a..e78bd23 100644 --- a/src/Event/Event.cpp +++ b/src/Event/Event.cpp @@ -16,9 +16,11 @@ namespace doengine std::vector Event::keydown; std::vector Event::keyup; std::vector Event::mouseEvent; +std::vector Event::TextInputList; std::vector Event::joyButtonUpList; std::vector Event::joyButtonDownList; std::vector Event::joyButtonTriggerList; +std::vector Event::keyboardHandlingEventList; std::map Event::joypadsConnected; std::unordered_map Event::keys_pressed; @@ -26,6 +28,10 @@ uint32_t Event::timeElapsed = 0; void Event::PollEvent() { + + ////todo add validtio for this call + SDL_StartTextInput(); + SDL_Event event; while (SDL_PollEvent(&event)) { @@ -35,18 +41,23 @@ void Event::PollEvent() Application::getApplication()->Quit(); break; } + case SDL_KEYDOWN: { - SDL_Log("SDL_KEYDOWN"); + ////SDL_Log("SDL_KEYDOWN %ld",event.key.keysym.scancode); keys_pressed[event.key.keysym.scancode] = true; ///(event.key.keysym.scancode); SDLKeyboard keyboard(event.key.keysym.scancode); + for(auto keyboardPressed : Event::keyboardHandlingEventList) + keyboardPressed->OnKeydown(keyboard); + for (auto itKeyboard : Event::keydown) itKeyboard->OnKeydown(keyboard); + } break; case SDL_KEYUP: { ///// SDL_Log("SDL_KEYUP"); - keys_pressed[event.key.keysym.scancode] = false; + /// keys_pressed[event.key.keysym.scancode] = false; SDLKeyboard keyboard; for (auto itKeyboard : Event::keyup) @@ -54,11 +65,16 @@ void Event::PollEvent() } break; /**< Key released */ case SDL_TEXTEDITING: { - /// SDL_Log("SDL_TEXTEDITING"); + ////SDL_Log("SDL_TEXTEDITING"); } break; /**< Keyboard text editing (composition) */ case SDL_TEXTINPUT: { - /// SDL_Log("SDL_TEXTEDITING"); + //// SDL_Log("SDL_TEXTINPUT %s",event.text.text); + for(auto events : Event::TextInputList) + { + std::string text = event.text.text; + events->OnTextInput(text); + } } break; /**< Keyboard text input */ case SDL_MOUSEMOTION: { @@ -66,9 +82,12 @@ void Event::PollEvent() auto mask = getMousePosition(&mousePos.x, &mousePos.y); doengine::devices::SDLMouse mouse(event.motion.which, mask, mousePos); - - ////SDL_Log("SDL_MOUSEMOTION x: %ld, y:%ld", mouse.x, mouse.y); - /// SDL_Log("Mouse Count = %ld", Event::mouseEvent.size()); + doengine::Rect mouseOffset{mousePos.x, mousePos.y, 1,1}; + mouse.motion.x = event.motion.x; + mouse.motion.y = event.motion.y; + ///SDL_Log("SDL_MOUSEMOTION x: %ld, y:%ld", mousePos.x, mousePos.y); + /////SDL_Log("Mouse Count = %ld", Event::mouseEvent.size()); + for (auto itMouse : Event::mouseEvent) { @@ -85,7 +104,7 @@ void Event::PollEvent() mouse.getButtonStateBitset(buttonPressed); /// SDL_Log("SDL_MOUSEBUTTONDOWN %X", buttonPressed.to_ulong()); - ////SDL_Log("mousePos Count = %ld", Event::mouseEvent.size()); + SDL_Log("mousePos Count = %ld", Event::mouseEvent.size()); for (auto it : Event::mouseEvent) it->MouseButtonDown(mouse); @@ -108,6 +127,18 @@ void Event::PollEvent() break; case SDL_MOUSEWHEEL: { SDL_Log("SDL_MOUSEWHEEL"); + Point mousePos; + std::bitset buttonPressed; + auto mask = getMousePosition(&mousePos.x, &mousePos.y); + doengine::devices::SDLMouse mouse(event.button.which, mask, + mousePos); + mouse.getButtonStateBitset(buttonPressed); + mouse.setScroll(event.wheel.x,event.wheel.y); + + /// SDL_Log("SDL_MOUSEBUTTONDOWN %X", buttonPressed.to_ulong()); + SDL_Log("mousePos Count = %ld", Event::mouseEvent.size()); + for (auto it : Event::mouseEvent) + it->MouseWheel(mouse); } break; case SDL_JOYAXISMOTION: @@ -200,11 +231,29 @@ void Event::AddKeyPressEventListener(KeyDownEvent* ev) Event::keydown.push_back(ev); } +void Event::AddKeyboardEvent(KeyboardInputhandlingEvent* ev) +{ + Event::keyboardHandlingEventList.push_back(ev); +} + bool Event::getLastKeyPressed(int scancode) { return keys_pressed[scancode]; } + +void Event::RemoveKeyboardEvent(KeyboardInputhandlingEvent* ev) +{ + auto& evts = Event::keyboardHandlingEventList; + for (auto it = evts.begin(); it != evts.end();) + { + if (*it == ev) + it = evts.erase(it); + else + ++it; + } +} + void Event::RemoveKeyPressEventListener(KeyUpEvent* ev) { auto& evts = Event::keyup; @@ -231,6 +280,7 @@ void Event::RemoveKeyPressEventListener(KeyDownEvent* ev) void Event::AddMouseEvent(MouseEvent* event) { + SDL_Log("MOuse event added"); Event::mouseEvent.push_back(event); } @@ -246,6 +296,24 @@ void Event::RemovedMouseEvent(MouseEvent* event) } } +void Event::RemoveTextInputEvent(TextInputEvent* event) +{ + auto& evts = Event::TextInputList; + for (auto it = evts.begin(); it != evts.end();) + { + if (*it == event) + it = evts.erase(it); + else + ++it; + } +} + +void Event::AddTextInputEvent(TextInputEvent *event) +{ + Event::TextInputList.push_back(event); +} + + void Event::AddJoypadEventListener(JoyButtonUpEvent* ev) { Event::joyButtonUpList.push_back(ev); diff --git a/src/Event/EventHandler.cpp b/src/Event/EventHandler.cpp new file mode 100644 index 0000000..44deec7 --- /dev/null +++ b/src/Event/EventHandler.cpp @@ -0,0 +1,33 @@ +#include "EventHandler.h" +#include "Event.h" +#include "Logger.h" + +namespace doengine +{ + KeyboardInputhandlingEvent::KeyboardInputhandlingEvent() + { + doengine::Event::AddKeyboardEvent(this); + } + KeyboardInputhandlingEvent::~KeyboardInputhandlingEvent() + { + doengine::Event::RemoveKeyboardEvent(this); + } + + MouseEvent::MouseEvent() + { + doengine::Event::AddMouseEvent(this); + } + MouseEvent::~MouseEvent() + { + doengine::Event::RemovedMouseEvent(this); + } + + TextInputEvent::TextInputEvent() + { + doengine::Event::AddTextInputEvent(this); + } + TextInputEvent::~TextInputEvent() + { + doengine::Event::RemoveTextInputEvent(this); + } +} \ No newline at end of file diff --git a/src/Event/SDLKeyboard.cpp b/src/Event/SDLKeyboard.cpp index 2ea5c6c..ea2fbca 100644 --- a/src/Event/SDLKeyboard.cpp +++ b/src/Event/SDLKeyboard.cpp @@ -19,7 +19,7 @@ const unsigned char* SDLKeyboard::getKeysBitset() const return static_cast(SDL_GetKeyboardState(nullptr)); } -const unsigned char SDLKeyboard::getLastKeyPressed() const +const unsigned char SDLKeyboard:: getLastKeyPressed() const { return key; } diff --git a/src/Event/SDLMouse.cpp b/src/Event/SDLMouse.cpp index 6b1395b..0b9c250 100644 --- a/src/Event/SDLMouse.cpp +++ b/src/Event/SDLMouse.cpp @@ -38,6 +38,11 @@ void SDLMouse::getPosition(Point& point) point.y = position.y; } +const Point& SDLMouse::getMotionPosition()const +{ + return motion; +} + const Point& SDLMouse::getPosition() const { return position; @@ -54,5 +59,43 @@ void SDLMouse::updateValues() buttonMask = SDL_GetMouseState(&position.x, &position.y); } +const Point& SDLMouse::getWheelScroll() const +{ + return scroll; +} + +void SDLMouse::setScroll(int x, int y) +{ + scroll.x =x; + scroll.y =y; +} + +Rect Mouse::getMousePosition() +{ + Rect rect; + rect.w = 1; + rect.h = 1; + SDL_GetMouseState(&rect.x, &rect.y); + return rect; +} + +uint8_t Mouse::getButtonPressed() +{ + unsigned char ret; + Rect rect; + rect.w = 1; + rect.h = 1; + auto mouse = SDL_GetMouseState(&rect.x, &rect.y); + if(mouse&SDL_BUTTON(SDL_BUTTON_LEFT)) + ret |= (uint8_t)Mouse::MouseButtonDown::Left; + if(mouse&SDL_BUTTON(SDL_BUTTON_RIGHT)) + ret |= (uint8_t)Mouse::MouseButtonDown::Right; + if(mouse&SDL_BUTTON(SDL_BUTTON_MIDDLE)) + ret |= (uint8_t)Mouse::MouseButtonDown::Center; + + return ret; +} + + } // namespace devices } // namespace doengine \ No newline at end of file diff --git a/src/Font/FontCache.cpp b/src/Font/FontCache.cpp new file mode 100644 index 0000000..bc70295 --- /dev/null +++ b/src/Font/FontCache.cpp @@ -0,0 +1,6 @@ +#include "FontCache.h" + +namespace doengine +{ + +} \ No newline at end of file diff --git a/src/Font/TTFText.cpp b/src/Font/TTFText.cpp index 76ac26f..ee78588 100644 --- a/src/Font/TTFText.cpp +++ b/src/Font/TTFText.cpp @@ -1,11 +1,12 @@ #include "TTFText.h" -#include "Renderer.h" #include "Logger.h" +#include "Renderer.h" +#include namespace doengine { TTFText::TTFText() { - LogOuput(logger_type::Information,"--TTFText--"); + LogOuput(logger_type::Information, "--TTFText--"); nativeRenderer = Application::getApplication()->getRender()->getTextRenderer(); } @@ -17,12 +18,35 @@ void TTFText::setColor(Color color) void TTFText::setFont(const std::string& path, int fntsize) { + LogOuput(logger_type::Information, "TTFText::setFont FontSRc=%s", + path.c_str()); nativeRenderer->setFont(path, fntsize); } +void TTFText::setForegroundColor(const Color& color) +{ + nativeRenderer->setForegroundColor(color); +} + +void TTFText::DrawText(int x, int y, const char* fmt, ...) +{ + // Start variable argument handling + va_list args; + va_start(args, fmt); + + // Buffer for formatted message + char buffer[4096]; + vsnprintf(buffer, sizeof(buffer), fmt, args); + + // End argument handling + va_end(args); + this->DrawText(buffer, x, y); +} + void TTFText::DrawText(const char* text, int x, int y) { - nativeRenderer->DrawText(text, x, y); + if (nativeRenderer) + nativeRenderer->DrawText(text, x, y); } Texture* TTFText::createText(const std::string& text) @@ -32,17 +56,31 @@ Texture* TTFText::createText(const std::string& text) void TTFText::wrapText(const char* text, int maxWidth, char* wrappedText) { - nativeRenderer->wrapText(text,maxWidth,wrappedText); - return ; + nativeRenderer->wrapText(text, maxWidth, wrappedText); + return; } -Texture* TTFText::createBitmapFont(const std::string& font_path,const doengine::Color& bg,const doengine::Color& fg){ - - if(nativeRenderer){ - LogOuput(logger_type::Information,"Create Bitmap font"); - return nativeRenderer->createBitmapFont(font_path,bg,fg); - } - return nullptr; +Texture* TTFText::createBitmapFont(const std::string& font_path, + const doengine::Color& bg, + const doengine::Color& fg) +{ + + if (nativeRenderer) + { + LogOuput(logger_type::Information, "Create Bitmap font"); + return nativeRenderer->createBitmapFont(font_path, bg, fg); + } + return nullptr; +} + +int TTFText::getFontHeight() +{ + if (nativeRenderer) + { + LogOuput(logger_type::Information, "Create Bitmap font"); + return nativeRenderer->getFontHeight(); + } + return 0; } } // namespace doengine \ No newline at end of file diff --git a/src/GameObject/GameObject.cpp b/src/GameObject/GameObject.cpp index 01bdfc5..3b8cb71 100644 --- a/src/GameObject/GameObject.cpp +++ b/src/GameObject/GameObject.cpp @@ -2,9 +2,6 @@ namespace doengine { -bool GameObject::isColliding(GameObject* other) -{ - return false; -} + } // namespace doengine \ No newline at end of file diff --git a/src/GameState/GameStateManager.cpp b/src/GameState/GameStateManager.cpp index 3ed49bc..457ef53 100644 --- a/src/GameState/GameStateManager.cpp +++ b/src/GameState/GameStateManager.cpp @@ -1,6 +1,7 @@ #include "GameStateManager.h" #include "Event.h" // #include "TTFText.h" +#include "Logger.h" namespace doengine { void GameStateManager::AddState(int state_id, GameState* object) @@ -28,7 +29,7 @@ void GameStateManager::SetState(int state_id) states[current_state]->OnEnter(); } } -#include "Logger.h" + void GameStateManager::Update(float elapsed) { if (states[current_state]) diff --git a/src/Music/SDLMusicHandler.cpp b/src/Music/SDLMusicHandler.cpp index c7ec0c6..467313e 100644 --- a/src/Music/SDLMusicHandler.cpp +++ b/src/Music/SDLMusicHandler.cpp @@ -113,6 +113,11 @@ bool SDLMusicHandler::isPlaying(const int) const return Mix_PlayingMusic(); } +std::string SDLMusicHandler::getPlayMusicName() +{ + return std::string("X"); +} + void SDLMusicHandler::setRepeat(Repeat repeat) { repeatTimes = repeat; diff --git a/src/Music/SDLSoundHandler.cpp b/src/Music/SDLSoundHandler.cpp index 431fd4a..b76d32a 100644 --- a/src/Music/SDLSoundHandler.cpp +++ b/src/Music/SDLSoundHandler.cpp @@ -65,6 +65,7 @@ int SDLSoundHandler::addToList(const std::string& src) return sounds.size() - 1; } } + return -1; } void SDLSoundHandler::playFirst() diff --git a/src/Texture/Texture.cpp b/src/Texture/Texture.cpp index ff8a0f1..801724b 100644 --- a/src/Texture/Texture.cpp +++ b/src/Texture/Texture.cpp @@ -1,6 +1,6 @@ #include "Texture.h" #include "Application.h" - +#include namespace doengine { Texture::Texture() @@ -10,7 +10,7 @@ Texture::Texture() void Texture::SetTransparentColor(const Color& color) { - realNativeTexture->SetTransparentColor(color); + realNativeTexture->SetTransparentColor(color); } Texture::Texture(std::string path) @@ -27,10 +27,15 @@ Texture::Texture(std::string path) Texture::~Texture() { } +void Texture::Draw(int x, int y) +{ + realNativeTexture->Draw(x, y); +} void Texture::Draw(const Rect& offset) { realNativeTexture->Draw(offset); } + void Texture::Draw(const Rect& offset, const Rect& clipset) { this->realNativeTexture->Draw(offset, clipset); @@ -70,7 +75,7 @@ Texture* Texture::setNativeTexture(void* t) TextureManager* TextureManager::instance; -std::map textures; +std::map, Texture*> textures; TextureManager* TextureManager::getTextureManager() { @@ -79,7 +84,8 @@ TextureManager* TextureManager::getTextureManager() return instance; } -void TextureManager::loadTextureFromFile(std::string id, std::string src) +void TextureManager::loadTextureFromFile( + const std::variant& id, string src) { Texture* texture = new Texture(src); addTexture(id, texture); @@ -106,14 +112,76 @@ void TextureManager::addTexture(std::string id, Texture* texture) } } } +void TextureManager::addTexture(const std::variant& id, + Texture* texture) +{ + auto it = textures.find(id); + if (texture->validTexture()) + { + if (it != textures.end()) + { + // removeTexture(id); + } + else + { + textures[id] = texture; + } + } +} + +void* Texture::getNativeBuffer() +{ + return this->realNativeTexture->getNativeBuffer(); +} void TextureManager::removeTexture(std::string id) { } -Texture* TextureManager::getTexture(std::string id) +Texture* TextureManager::getTexture(const std::variant& id) +{ + auto find = textures.find(id); + if (find == textures.end()) + return nullptr; + return find->second; +} + +Texture* TextureManager::getTextureOr(const std::variant& id, + std::function orCall) { return textures[id]; } +void TextureManager::loadFont(const std::variant& key, + string src, int pts) +{ + auto pf = new TTFText(); + pf->setFont(src, pts); + fonts[key] = pf; +} +TTFText* TextureManager::getFont(const std::variant& id) +{ + return fonts[id]; +} + +TextureManager::TextureStatus TextureManager::drawTexture(const std::string id, + const Rect offset, + const Rect clipset) +{ + auto text = getTexture(id); + if (text == nullptr) + return TextureManager::TextureStatus::TextureIdInvalid; + text->Draw(offset, clipset); + return TextureManager::TextureStatus::Success; +} +TextureManager::TextureStatus TextureManager::drawTexture(const std::string id, + const Rect offset) +{ + auto text = getTexture(id); + if (text == nullptr) + return TextureManager::TextureStatus::TextureIdInvalid; + text->Draw(offset); + return TextureManager::TextureStatus::Success; +} + } // namespace doengine \ No newline at end of file diff --git a/src/Tilemap/TileMapEditor.cpp b/src/Tilemap/TileMapEditor.cpp new file mode 100644 index 0000000..cb3c6e3 --- /dev/null +++ b/src/Tilemap/TileMapEditor.cpp @@ -0,0 +1,185 @@ +#include "TileMapEditor.h" +#include "Texture.h" +namespace doengine +{ + + TileMapEditor::TileMapEditor(const std::string& fontsrc) + { + renderer = doengine::Application::getApplication()->getRender(); + panelPosition.x = 0; + panelPosition.y = 0; + panelPosition.w = 600; + panelPosition.h = 1000; + font = new doengine::TTFText(); + font->setFont(fontsrc, 20); + font->setColor(doengine::Colors::yellow1); + /// font->(doengine::Colors::yellow1); + visible = false; + saveIcon = TextureManager::getTextureManager()->getTexture("save1"); + } + + TileCharTextureOffset TileMapEditor::getSelectedEditorTileTextureOffset() + { + return offset_of_drawing[selected_type]; + } + + bool TileMapEditor::isSelectect() + { + return selected; + } + + void TileMapEditor::setup(const std::string& textId, + std::unordered_map clipList) + { + constexpr const int ts = 48; + mapTexture = + doengine::TextureManager::getTextureManager()->getTexture(textId); + doengine::Rect current_rect = {0, 0, ts, ts}; + for (auto clip : clipList) + { + offset_of_drawing[clip.first] = clip.second; + InternalTileCharTextureOffset tileEntry; + tileEntry.position_in_panel = current_rect; + tileEntry.clipset = clip.second; + tileset_display.emplace_back(tileEntry); + current_rect.x += (ts + paddingLeft); + + if (current_rect.x > panelPosition.w - 10) + { + current_rect.x = 0; + current_rect.y += (ts + paddingTop); + } + } + selected_type = clipList.begin()->first; + } + + void TileMapEditor::update(double elapsed) + { + } + + void TileMapEditor::render() + { + /////font->DrawText(200, 400, "Cursor[%d, %d]", 10, 100); + if (visible && mapTexture && renderer) + { + + renderer->DrawFillRect(this->panelPosition, + (inEditorArea + ? doengine::Color(0x43, 0x43, 0x43, 255) + : doengine::Colors::white)); + for (auto it : tileset_display) + { + Rect adjusted_position_in_editor + { + it.position_in_panel.x + 24, + it.position_in_panel.y + 32, + it.position_in_panel.w, + it.position_in_panel.h, + }; + mapTexture->Draw(adjusted_position_in_editor, it.clipset.offset); + //// mapTexture->Draw(it.position_in_panel, it.clipset.offset); + } + + if (inEditorArea) + { + renderer->DrawRect(this->panelPosition, doengine::Colors::red, + 3); + /// if(cursor.w > 0) + { + renderer->DrawRect(this->cursor, doengine::Colors::green, + 3); + } + } + else + { + renderer->DrawRect(this->cursor, doengine::Colors::black, 3); + } + + if (selected) + { + doengine::LogOuput(doengine::logger_type::Error, + "Drawing Selected %c", selected_type); + font->DrawText(30, 96,"Selected Tile:"); + doengine::Rect offset_rect = + offset_of_drawing[selected_type].offset; + mapTexture->Draw({30, 124, 100, 100}, offset_rect); + + saveIcon->Draw({168, 124, 100, 100}); + } + } + } + + void TileMapEditor::MouseMove(const Mouse& mouse) + { + this->inEditorArea = doengine::checkCollision(mouse.getMousePosition(), + this->panelPosition); + + this->cursor = mouse.getMousePosition(); + } + + void TileMapEditor::MouseButtonDown(const Mouse& mouse) + { + if (inEditorArea) + { + doengine::Rect _position = mouse.getMousePosition(); + + for (auto tile_to_check : this->tileset_display) + { + Rect adjusted_position_in_editor + { + tile_to_check.position_in_panel.x + 24, + tile_to_check.position_in_panel.y + 32, + tile_to_check.position_in_panel.w, + tile_to_check.position_in_panel.h, + }; + bool collide = doengine::checkCollision( + _position, adjusted_position_in_editor); + + + if (collide && visible) + { + doengine::LogOuput(doengine::logger_type::Error, + "Selected Type %c", + tile_to_check.clipset.type); + this->cursor = tile_to_check.position_in_panel; + this->cursor.x +=24; + this->cursor.y += 32; + this->selected_type = tile_to_check.clipset.type; + selected = true; + for(auto it : selected_type_for_char_type){ + it(this->selected_type); + } + } + else + { + + ////selected = false; + } + } + } + } + void TileMapEditor::MouseButtonUp(const Mouse&) + { + } + void TileMapEditor::OnKeydown(const Keyboard& keyboard) + { + auto keys = keyboard.getKeysBitset(); + if (keys[doengine::SCANCODE_E]) + { + visible = !visible; + } + if (keys[doengine::SCANCODE_M]) + { + mousePositionDebug = !mousePositionDebug; + } + } + void TileMapEditor::OnKeyup(const Keyboard& keyboard) + { + (void)keyboard; + } + + void TileMapEditor::registerSelectedTileByChar(std::function fn) + { + this->selected_type_for_char_type.emplace_back(fn); + } +}; \ No newline at end of file diff --git a/src/Tilemap/Tilemap.cpp b/src/Tilemap/Tilemap.cpp index 1d25843..e69de29 100644 --- a/src/Tilemap/Tilemap.cpp +++ b/src/Tilemap/Tilemap.cpp @@ -1,71 +0,0 @@ -#include "Tilemap.h" -#include "Logger.h" -#include "StringUtils.h" -#include -#include -#include - -namespace doengine -{ - -SimpleLayerTileMap::SimpleLayerTileMap() : TileMap() -{ - - std::cout << "HERER"; - texture = nullptr; - std::cout << "HERER"; - thisMap.clear(); - std::cout << "HERER"; -} -SimpleLayerTileMap::~SimpleLayerTileMap() -{ - std::cout << "HERER"; -} - -void SimpleLayerTileMap::setMapRenderer(MapRenderer* render) -{ - this->mapRenderer = render; -} -void SimpleLayerTileMap::loadTileMapFromFile(const char* file) -{ - /// readMapFile("../sample/assets/maps/simple-map1.map", thisMap); - - for (int r = 0; r <= rows; r++) - { - /// std::string row = thisMapFile[r + 2]; - /// DO_TRACE("Row=%s", row.c_str()); - // std::vector chars = splitChar(row, " \r\n\t\0"); - // thisMapRows.emplace_back(chars); - } -} - -void SimpleLayerTileMap::saveLoadedTileMap() -{ -} - -void SimpleLayerTileMap::setTileType(int r, int c, char t) -{ - if ((r >= 0 || r <= this->rows) && (c >= 0 || c <= this->cols)) - { - /// DO_TRACE("CHANGE ROW=%d, COL=%d FROM %c TO %d", r, c, - /// thisMapRows[r][c], - // t); - // thisMapRows[r][c] = t; - } -} -char SimpleLayerTileMap::getTileType(int r, int c) -{ - if ((r >= 0 || r <= this->rows) && (c >= 0 || c <= this->cols)) - { - //// return thisMapRows[r][c]; - } - return (char)0xFF; -} - -void SimpleLayerTileMap::render() -{ -} -void SimpleLayerTileMap::update() -{ -} -} // namespace doengine diff --git a/src/Timer/FPSManager.cpp b/src/Timer/FPSManager.cpp index 90a9978..c5ef6d8 100644 --- a/src/Timer/FPSManager.cpp +++ b/src/Timer/FPSManager.cpp @@ -18,26 +18,61 @@ uint32_t FpsManager::getDeltaTime() float FpsManager::getElapsedTime() { - return (float)(elapsed - start); + last_elapsed = (float)(elapsed - start); + return last_elapsed; +} + +float FpsManager::getLastElapsedTime() +{ + return static_cast(last_elapsed); } void FpsManager::Start() { start = SDL_GetTicks(); + frequency = SDL_GetPerformanceFrequency(); + lastCounter = SDL_GetPerformanceCounter(); } void FpsManager::Handle() { - elapsed = SDL_GetTicks(); - uint32_t ticks = 1000 / fps; - if (ticks > (elapsed - start)) + const uint32_t frameDelay = 1000 / fps; + + uint32_t frameTime = SDL_GetTicks() - start; + + if (frameTime < frameDelay) + { + SDL_Delay(frameDelay - frameTime); + } + + start = SDL_GetTicks(); +} + +void FpsManager::beginFrame() +{ + lastCounter = SDL_GetPerformanceCounter(); +} + +double FpsManager::endFrame() +{ + uint64_t now = SDL_GetPerformanceCounter(); + double deltaTime = static_cast(now - lastCounter) / frequency; + + const double targetFrameTime = 1.0 / fps; + + if (deltaTime < targetFrameTime) { - wait = ticks - (elapsed - start); - ////SDL_Log("FPS %ld", wait); - if (wait > 0) - SDL_Delay(wait); - start = elapsed; - } + SDL_Delay(static_cast((targetFrameTime - deltaTime) * 1000.0)); + + now = SDL_GetPerformanceCounter(); + deltaTime = static_cast(now - lastCounter) / frequency; + } + + return deltaTime; } -} // namespace doengine \ No newline at end of file +double FpsManager::getTicks() +{ + return SDL_GetTicks(); +} +} \ No newline at end of file diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index d077bcf..bfe1af2 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -34,6 +34,7 @@ add_executable(${PROJECT_NAME} units/utSDLKeyboard.cpp units/utSDLMouse.cpp units/utSDLJoypad.cpp + units/utSpriteOffset.cpp ../GameState/GameStateManager.cpp ../Network/UDPPacket.cpp diff --git a/src/test/mocks/SDLMock.h b/src/test/mocks/SDLMock.h index 3e2540d..ab1bfb2 100644 --- a/src/test/mocks/SDLMock.h +++ b/src/test/mocks/SDLMock.h @@ -1,7 +1,7 @@ #pragma once -#include "gmock/gmock.h" -#include +#include "DOEngine_SDL_includes.h" + namespace doengine { diff --git a/src/test/units/utEvents.cpp b/src/test/units/utEvents.cpp new file mode 100644 index 0000000..746ff06 --- /dev/null +++ b/src/test/units/utEvents.cpp @@ -0,0 +1,21 @@ +#include "Event.h" +#include "EventHandler.h" +#include "fixtures/DoEngineFixture.h" +#include "mocks/EventHandlerMock.h" +#include "mocks/JoypadMock.h" +#include "mocks/MouseMock.h" +#include "mocks/SDLMock.h" +#include "gmock/gmock.h" +#include +#include +#include +#include +#include +#include + +namespace doengine::ut +{ + + + +} \ No newline at end of file diff --git a/src/test/units/utSpriteOffset.cpp b/src/test/units/utSpriteOffset.cpp new file mode 100644 index 0000000..1ce240d --- /dev/null +++ b/src/test/units/utSpriteOffset.cpp @@ -0,0 +1,35 @@ +#include "SpriteOffset.h" +#include "mocks/FPSManagerMock.h" +#include "mocks/SDLMock.h" +#include "gmock/gmock.h" +#include + +namespace doengine +{ +namespace ut +{ + +using testing::_; +using testing::Return; + +class utSpriteOffset : public testing::Test +{ + public: + utFPSManager() : xsdlMock() + { + } + + SpriteAnimationOffset _sut; + void SetUp() override + { + testing::Test::SetUp(); + SetSDLMock(&xsdlMock); + ON_CALL(xsdlMock, SDL_GetTicks()).WillByDefault(Return(0)); + } + + testing::NiceMock xsdlMock; +}; + +} + +} \ No newline at end of file diff --git a/sample/old_projects/SortedState.h b/src/test/units/utTileMapEditor.cpp similarity index 100% rename from sample/old_projects/SortedState.h rename to src/test/units/utTileMapEditor.cpp