diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 95f2d53a..8e9c6607 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,7 +56,7 @@ jobs: include/vendored/ogg/include/ogg \ include/vendored/sparsehash; do cp -r $inc headers; done - + ls headers - name: pack headers mingw @@ -74,7 +74,7 @@ jobs: with: name: headers path: headers - + - name: build header-mingw artifact uses: actions/upload-artifact@v4 with: @@ -88,7 +88,7 @@ jobs: - uses: actions/checkout@v4 with: submodules: 'recursive' - + - name: Install dependencies run: sudo apt-get update && sudo apt-get install -y build-essential xorg-dev mesa-common-dev libx11-dev libxrandr-dev libgl1-mesa-dev libglu1-mesa-dev libfreetype6-dev libopenal-dev libsndfile1-dev libudev-dev libxinerama-dev libxcursor-dev vulkan-tools libvulkan-dev vulkan-validationlayers-dev spirv-tools @@ -96,7 +96,7 @@ jobs: run: | git submodule update --recursive ./build.sh -t -d -r - mkdir linuxbuild + mkdir linuxbuild cp build/libHop.a linuxbuild/libHop-linux-x86_64.a cp build/libjGL.a linuxbuild/libjGL-linux-x86_64.a cp LICENSE* linuxbuild/ @@ -126,7 +126,7 @@ jobs: with: name: demoLinux path: demos.zip - + linuxXwindows: runs-on: ubuntu-22.04 @@ -134,7 +134,7 @@ jobs: - uses: actions/checkout@v4 with: submodules: 'recursive' - + - name: Install dependencies run: sudo apt-get update && sudo apt-get install -y xorg-dev build-essential mesa-common-dev libx11-dev libxrandr-dev libgl1-mesa-dev libglu1-mesa-dev libfreetype6-dev libopenal-dev libsndfile1-dev libudev-dev g++-mingw-w64-x86-64-posix gcc-mingw-w64-x86-64-posix libxinerama-dev libxcursor-dev vulkan-tools libvulkan-dev vulkan-validationlayers-dev spirv-tools wine64 @@ -152,7 +152,7 @@ jobs: cp -r build/PerlinWorld demos/ cp build/*.dll demos/PerlinWorld/ cd demos && zip -r ../demos.zip * && cd .. - cp build/*.dll build/TestScriptPack/ + cp build/*.dll build/TestScriptPack/ - name: Tests (wine64) working-directory: ${{github.workspace}}/build @@ -164,7 +164,7 @@ jobs: sudo Xvfb :99 -screen 0 800x600x24 & sleep 5 MESA_GL_VERSION_OVERRIDE=3.3 ctest --output-on-failure --verbose --exclude-regex Vulkan - + - name: buildArtifact uses: actions/upload-artifact@v4 with: @@ -204,7 +204,7 @@ jobs: cp build/libHop.a macosbuild/libHop-macos.a cp build/libjGL.a macosbuild/libjGL-macos.a cp LICENSE* macosbuild/ - + mkdir demos cp -r build/PerlinWorld demos/ cd demos && zip -r ../demos.zip * && cd .. @@ -214,7 +214,7 @@ jobs: # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: MESA_GL_VERSION_OVERRIDE=3.3 ctest --output-on-failure --verbose --exclude-regex Vulkan --exclude-regex assetStore - + - name: buildArtifact uses: actions/upload-artifact@v4 with: @@ -226,11 +226,11 @@ jobs: with: name: demomacos path: demos.zip - + linuxRun: needs: linuxNative runs-on: ubuntu-20.04 - + steps: - name: Install dependencies run: sudo apt-get update && sudo apt-get install -y xvfb x11-apps imagemagick @@ -258,8 +258,7 @@ jobs: sleep 10 xwd -root -silent | convert xwd:- png:screenshot-map.png sleep 5 && kill $PID - - + - name: upload artifact uses: actions/upload-artifact@v4 with: @@ -280,13 +279,13 @@ jobs: - uses: actions/checkout@v4 with: submodules: 'recursive' - + - name: Install dependencies run: | sudo apt-get update && sudo apt-get install -y build-essential mesa-common-dev libx11-dev libxrandr-dev libgl1-mesa-dev libglu1-mesa-dev libfreetype6-dev libopenal-dev libsndfile1-dev libudev-dev vulkan-tools libvulkan-dev vulkan-validationlayers-dev spirv-tools wget https://dl.google.com/android/repository/android-ndk-r25c-linux.zip unzip android-ndk-r25c-linux.zip - wget https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip + wget https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip unzip commandlinetools-linux-9477386_latest.zip export ANDROID_SDK_ROOT=~/ mv cmdline-tools latest @@ -297,12 +296,12 @@ jobs: ./cmdline-tools/latest/bin/sdkmanager --install "system-images;android-34;google_apis;x86_64" ./cmdline-tools/latest/bin/sdkmanager "platform-tools" "platforms;android-34" echo no | ./cmdline-tools/latest/bin/avdmanager create avd --name android34 --package "system-images;android-34;google_apis;x86_64" - + - name: build hop run: | git submodule update --recursive ./build.sh -r --android android-ndk-r25c - mkdir androidbuild + mkdir androidbuild cp build/libHop* androidbuild/ cp build/libjGL* androidbuild/ cp LICENSE* androidbuild/ @@ -359,7 +358,6 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 with: submodules: 'true' @@ -396,7 +394,6 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 with: submodules: 'true' @@ -431,7 +428,6 @@ jobs: runs-on: macos-12 steps: - - uses: actions/checkout@v4 with: submodules: 'true' @@ -474,14 +470,14 @@ jobs: needs: [linuxNative, linuxXwindows, linuxXandroid, macosNative, headers] if: github.ref_type == 'tag' runs-on: ubuntu-20.04 - + steps: - name: get linux build uses: actions/download-artifact@v4 with: name: linuxbuild path: linux - + - name: get windows build uses: actions/download-artifact@v4 with: @@ -493,7 +489,7 @@ jobs: with: name: macosbuild path: macos - + - name: get android build uses: actions/download-artifact@v4 with: diff --git a/.github/workflows/build-and-run.yml b/.github/workflows/run-tests.yml similarity index 94% rename from .github/workflows/build-and-run.yml rename to .github/workflows/run-tests.yml index e4e66e9c..f8424759 100644 --- a/.github/workflows/build-and-run.yml +++ b/.github/workflows/run-tests.yml @@ -1,4 +1,4 @@ -name: Hop run tests +name: Hop demos on: pull_request: @@ -10,13 +10,13 @@ jobs: strategy: matrix: - example: [PerlinWorld, SoftBodyTetris, MeshEditor] + example: [PerlinWorld, SoftBodyTetris, Sprites, MeshEditor] steps: - uses: actions/checkout@v3 with: submodules: 'recursive' - + - name: Install dependencies run: sudo apt-get update && sudo apt-get install -y build-essential xorg-dev mesa-common-dev libx11-dev libxrandr-dev libgl1-mesa-dev libglu1-mesa-dev libfreetype6-dev libopenal-dev libsndfile1-dev libudev-dev libxinerama-dev libxcursor-dev xvfb x11-apps vulkan-tools libvulkan-dev vulkan-validationlayers-dev spirv-tools @@ -46,16 +46,16 @@ jobs: wineRunTest: if: ${{ github.event_name == 'workflow_dispatch' }} runs-on: ubuntu-22.04 - + strategy: matrix: - example: [PerlinWorld, SoftBodyTetris, MeshEditor] + example: [PerlinWorld, SoftBodyTetris, Sprites, MeshEditor] steps: - uses: actions/checkout@v3 with: submodules: 'recursive' - + - name: Install dependencies run: sudo apt-get update && sudo apt-get install -y xorg-dev build-essential mesa-common-dev libx11-dev libxrandr-dev libgl1-mesa-dev libglu1-mesa-dev libfreetype6-dev libopenal-dev libsndfile1-dev libudev-dev g++-mingw-w64-x86-64-posix gcc-mingw-w64-x86-64-posix libxinerama-dev libxcursor-dev xvfb x11-apps vulkan-tools libvulkan-dev vulkan-validationlayers-dev spirv-tools wine64 - name: linux-windows diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b4ff60b..97d2db31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -178,7 +178,7 @@ IF (TEST_SUITE) file(GLOB TEST_SRC "tests/*.cpp" "src/Maths/*.cpp") include_directories(include "tests/") - add_executable(testSuite + add_executable(testSuite ${TEST_SRC} "src/World/mapFile.cpp" "src/Util/z.cpp" @@ -187,7 +187,7 @@ IF (TEST_SUITE) target_compile_definitions(testSuite PUBLIC GLSL_VERSION="330") target_link_libraries(testSuite zlibstatic stduuid glm) - + include(CTest) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/tests/cmake/) include(Catch) @@ -195,7 +195,7 @@ IF (TEST_SUITE) add_subdirectory("${PROJECT_SOURCE_DIR}/tests/regression") endif() - + ENDIF(TEST_SUITE) endif() diff --git a/README.md b/README.md index 405d452e..794b489c 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,70 @@ # Hop -A lightweight 2D game engine, in C++ with embedded Lua +#### A lightweight 2D game engine, in C++ with embedded Lua ### Features -- Free, MIT open sourced -- Use as a standalone (GLFW based) engine or as a drop in component (e.g. with SFML) -- Entity component system (ECS) based -- Rigid and soft body physics (Discrete element method) -- OpenGL rendering (provided by jGL, Vulkan is a WIP) -- Perlin (marching squares) and Tile map, editable tile based worlds -- Lua console, interoperating with the ECS, physics, and rendering systems +- Free, MIT open sourced.. +- Use as a standalone engine or as a drop in component (e.g. with SFML).. +- Entity component system (ECS) based. +- Physics (Discrete element method): + - [x] collision primitives (circles, rects). + - [x] collision meshes built from primitives (rigid and soft). + - [x] configurable friction, gravity, drag, and restitution. + - [x] super-sampling. + - [ ] (good) multithreading. + - [x] Cell list collision detection. + - [ ] Quad tree collision detection. + - [x] Object-object and object-world collisions. + - [ ] collision islands and other optimisations. +- Rendering (provided by jGL, Vulkan is a WIP): + - [x] OpenGL. + - [ ] Vulkan. + - [x] sprites (atlasing, and animateable atlases). + - [x] collision primitives (circles/rects). + - [x] Freetype fonts. + - [ ] shapes (it is in jGL). + - [ ] lightmaps and shadows. + - [x] msaa. + - [ ] particle effects (it is in jGL). + - [x] texture management. + - [ ] (compressed) texture asset packs. + - [ ] Engine UI (Dear ImGui). +- Window management: + - [x] Single window creation. + - [ ] Multi-window. + - [x] Key and mouse events. + - [x] Frame limiting. + - [ ] Snapping and resizing. + - [x] Screenspace bbs. +- Worlds: + - [x] Perlin (marching squares). + - [x] Tile maps. + - [x] object-world collisions. + - [ ] editable. + - [x] saveable. + - [ ] tile textures. +- Lua console + - [x] ECS bindings (object management). + - [x] Compressed script asset archive support. + - [ ] Live in game console +- Cross platform: + - [x] Linux. + - [x] Windows. + - [x] macOS. + - [x] Android. + - [ ] IOS (mostly held up by jGL rendering). +- Tooling + - [x] mesh editor. + - [x] Lua script packer. + - [ ] World editor. + - [ ] Texture packer. -### Setup +### Documentation + +Checkout the docs [here](https://jerboaburrow.github.io/Hop/), and also the jGL docs [here](https://jerboaburrow.github.io/jGL/) + +### Setup - clone, and init the submodules (you can use ```submodules.sh```) - the ```build.sh``` can be used to build @@ -21,7 +73,7 @@ A lightweight 2D game engine, in C++ with embedded Lua - Freetype is licensed under the The FreeType Project LICENSE - GLEW is licensed under aModified BSD License, the Mesa 3-D License (MIT) and the Khronos License (MIT). -- GLFW is licensed under the zlib/libpng +- GLFW is licensed under the zlib/libpng - GLM is licensed under the MIT License (but also, no bunnies have been made unhappy) - Lua is licensed under the MIT license - Miniaudio is licensed under the MIT-0 license @@ -29,4 +81,4 @@ A lightweight 2D game engine, in C++ with embedded Lua - vorbis and ogg is licensed under a BSD license - zlib is licensed under the zlib license -We thank: David Turner, Robert Wilhelm, and Werner Lemberg (Freetype), Milan Ikits , Marcelo E. Magallon , and Lev Povalahev Brian Paul, The Khronos Group Inc (GLEW), Marcus Geelnard and Camilla Löwy (GLFW), G-Truc Creation (GLM), Lua.org, PUC-Rio (Lua), David Reid (Miniaudio), Marius Bancila https://github.com/mariusbancila/stduuid#MIT-1-ov-file (stduuid), Xiph.org Foundation (vorbis, ogg), and Jean-loup Gailly and Mark Adler (zlib). +Thanks to all the OSS developers: David Turner, Robert Wilhelm, and Werner Lemberg (Freetype), Milan Ikits , Marcelo E. Magallon , and Lev Povalahev Brian Paul, The Khronos Group Inc (GLEW), Marcus Geelnard and Camilla Löwy (GLFW), G-Truc Creation (GLM), Lua.org, PUC-Rio (Lua), David Reid (Miniaudio), Marius Bancila https://github.com/mariusbancila/stduuid#MIT-1-ov-file (stduuid), Xiph.org Foundation (vorbis, ogg), and Jean-loup Gailly and Mark Adler (zlib). diff --git a/demo/desktop/CMakeLists.txt b/demo/desktop/CMakeLists.txt index ebfe92e3..9e68d241 100644 --- a/demo/desktop/CMakeLists.txt +++ b/demo/desktop/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(perlinWorld) -add_subdirectory(softBodyTetris) \ No newline at end of file +add_subdirectory(softBodyTetris) +add_subdirectory(sprites) diff --git a/demo/desktop/perlinWorld/CMakeLists.txt b/demo/desktop/perlinWorld/CMakeLists.txt index 0f490bc4..61b5f825 100644 --- a/demo/desktop/perlinWorld/CMakeLists.txt +++ b/demo/desktop/perlinWorld/CMakeLists.txt @@ -1 +1,24 @@ -add_subdirectory(standalone) \ No newline at end of file +set(OUTPUT_NAME PerlinWorld) + +if (WINDOWS) + add_compile_definitions(WINDOWS) + + if (RELEASE) + # launch as windows, not console app - so cmd does not open as well + add_link_options(-mwindows) + endif () +else () + add_link_options(-no-pie) +endif() + +add_executable(${OUTPUT_NAME} "main.cpp") + +target_link_libraries(${OUTPUT_NAME} Hop) + +set_target_properties(${OUTPUT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${OUTPUT_NAME}/") + +file(GLOB LUA "*.lua") +file(GLOB MAP "*.hmap") + +file(COPY ${LUA} DESTINATION "${CMAKE_BINARY_DIR}/${OUTPUT_NAME}/") +file(COPY ${MAP} DESTINATION "${CMAKE_BINARY_DIR}/${OUTPUT_NAME}/") \ No newline at end of file diff --git a/demo/desktop/perlinWorld/standalone/main.cpp b/demo/desktop/perlinWorld/main.cpp similarity index 100% rename from demo/desktop/perlinWorld/standalone/main.cpp rename to demo/desktop/perlinWorld/main.cpp diff --git a/demo/desktop/perlinWorld/standalone/main.h b/demo/desktop/perlinWorld/main.h similarity index 100% rename from demo/desktop/perlinWorld/standalone/main.h rename to demo/desktop/perlinWorld/main.h diff --git a/demo/desktop/softBodyTetris/CMakeLists.txt b/demo/desktop/softBodyTetris/CMakeLists.txt index 0f490bc4..21e4658f 100644 --- a/demo/desktop/softBodyTetris/CMakeLists.txt +++ b/demo/desktop/softBodyTetris/CMakeLists.txt @@ -1 +1,33 @@ -add_subdirectory(standalone) \ No newline at end of file +set(OUTPUT_NAME SoftBodyTetris) + +include_directories(.) + +if (WINDOWS) + add_compile_definitions(WINDOWS) + + if (RELEASE) + # launch as windows, not console app - so cmd does not open as well + add_link_options(-mwindows) + endif () +else () + add_link_options(-no-pie) +endif() + +if (PROFILE) + # apt-get install google-perftools libgoogle-perftools-dev google-pprof + # CPUPROFILE=prof.out ./SoftBodyTetris + # google-pprof --pdf SoftBodyTetris prof.out > sbt.pdf + add_link_options("-Wl,--no-as-needed,-lprofiler,--as-needed") +endif() + +add_executable(${OUTPUT_NAME} "main.cpp") + +target_link_libraries(${OUTPUT_NAME} Hop) + +set_target_properties(${OUTPUT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${OUTPUT_NAME}") + +file(GLOB LUA "*.lua") +file(GLOB MAP "*.hmap") + +file(COPY ${LUA} DESTINATION "${CMAKE_BINARY_DIR}/${OUTPUT_NAME}/") +file(COPY ${MAP} DESTINATION "${CMAKE_BINARY_DIR}/${OUTPUT_NAME}/") \ No newline at end of file diff --git a/demo/desktop/softBodyTetris/standalone/main.cpp b/demo/desktop/softBodyTetris/main.cpp similarity index 100% rename from demo/desktop/softBodyTetris/standalone/main.cpp rename to demo/desktop/softBodyTetris/main.cpp diff --git a/demo/desktop/softBodyTetris/standalone/main.h b/demo/desktop/softBodyTetris/main.h similarity index 100% rename from demo/desktop/softBodyTetris/standalone/main.h rename to demo/desktop/softBodyTetris/main.h diff --git a/demo/desktop/softBodyTetris/standalone/CMakeLists.txt b/demo/desktop/softBodyTetris/standalone/CMakeLists.txt deleted file mode 100644 index a9fc8284..00000000 --- a/demo/desktop/softBodyTetris/standalone/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -set(OUTPUT_NAME SoftBodyTetris) - -include_directories(.) - -if (WINDOWS) - add_compile_definitions(WINDOWS) - - if (RELEASE) - # launch as windows, not console app - so cmd does not open as well - add_link_options(-mwindows) - endif () -else () - add_link_options(-no-pie) -endif() - -if (PROFILE) - # apt-get install google-perftools libgoogle-perftools-dev google-pprof - # CPUPROFILE=prof.out ./SoftBodyTetris - # google-pprof --pdf SoftBodyTetris prof.out > sbt.pdf - add_link_options("-Wl,--no-as-needed,-lprofiler,--as-needed") -endif() - -add_executable(${OUTPUT_NAME} "main.cpp") - -target_link_libraries(${OUTPUT_NAME} Hop) - -set_target_properties(${OUTPUT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${OUTPUT_NAME}") - -file(GLOB LUA "../*.lua") -file(GLOB MAP "../*.hmap") - -file(COPY ${LUA} DESTINATION "${CMAKE_BINARY_DIR}/${OUTPUT_NAME}/") -file(COPY ${MAP} DESTINATION "${CMAKE_BINARY_DIR}/${OUTPUT_NAME}/") \ No newline at end of file diff --git a/demo/desktop/perlinWorld/standalone/CMakeLists.txt b/demo/desktop/sprites/CMakeLists.txt similarity index 77% rename from demo/desktop/perlinWorld/standalone/CMakeLists.txt rename to demo/desktop/sprites/CMakeLists.txt index a3c01319..e2a0a34b 100644 --- a/demo/desktop/perlinWorld/standalone/CMakeLists.txt +++ b/demo/desktop/sprites/CMakeLists.txt @@ -1,4 +1,4 @@ -set(OUTPUT_NAME PerlinWorld) +set(OUTPUT_NAME Sprites) if (WINDOWS) add_compile_definitions(WINDOWS) @@ -17,8 +17,7 @@ target_link_libraries(${OUTPUT_NAME} Hop) set_target_properties(${OUTPUT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${OUTPUT_NAME}/") -file(GLOB LUA "../*.lua") -file(GLOB MAP "../*.hmap") +file(GLOB LUA "*.lua") file(COPY ${LUA} DESTINATION "${CMAKE_BINARY_DIR}/${OUTPUT_NAME}/") -file(COPY ${MAP} DESTINATION "${CMAKE_BINARY_DIR}/${OUTPUT_NAME}/") \ No newline at end of file +file(COPY "assets" DESTINATION "${CMAKE_BINARY_DIR}/${OUTPUT_NAME}/") \ No newline at end of file diff --git a/demo/desktop/sprites/assets/HEART.png b/demo/desktop/sprites/assets/HEART.png new file mode 100644 index 00000000..9d44ab97 Binary files /dev/null and b/demo/desktop/sprites/assets/HEART.png differ diff --git a/demo/desktop/sprites/assets/Pi.png b/demo/desktop/sprites/assets/Pi.png new file mode 100644 index 00000000..c811289d Binary files /dev/null and b/demo/desktop/sprites/assets/Pi.png differ diff --git a/demo/desktop/sprites/assets/random.png b/demo/desktop/sprites/assets/random.png new file mode 100644 index 00000000..8d5dc5b3 Binary files /dev/null and b/demo/desktop/sprites/assets/random.png differ diff --git a/demo/desktop/sprites/config.lua b/demo/desktop/sprites/config.lua new file mode 100644 index 00000000..407d2868 --- /dev/null +++ b/demo/desktop/sprites/config.lua @@ -0,0 +1,48 @@ +config = +{ + ["timeStep"] = 1.0/1800.0, + ["subSample"] = 5, + ["cofr"] = 0.5 +} + +hop.configure(config); + +objects = { + { + ["transform"] = {0.1, 0.1, 0.0, 0.1}, + ["colour"] = {200/255,200/255,250/255,1.0}, + ["texturePath"] = "HEART.png", + ["moveable"] = true, + ["collisionMesh"] = + { + {0.0,0.0,1.0} + }, + ["name"] = "heart" + }, + { + ["transform"] = {0.5, 0.5, 0.0, 0.1}, + ["colour"] = {200/255,200/255,250/255,1.0}, + ["texturePath"] = "Pi.png", + ["moveable"] = true, + ["collisionMesh"] = + { + {0.0,0.0,1.0} + }, + ["name"] = "pi" + }, + { + ["transform"] = {0.7, 0.25, 0.0, 0.1}, + ["colour"] = {200/255,200/255,250/255,0.5}, + ["texturePath"] = "random.png", + ["moveable"] = true, + ["collisionMesh"] = + { + {0.0,0.0,1.0} + }, + ["name"] = "random" + } +} + +for i = 1, #objects do + hop.loadObject(objects[i]) +end \ No newline at end of file diff --git a/demo/desktop/sprites/main.cpp b/demo/desktop/sprites/main.cpp new file mode 100644 index 00000000..78654566 --- /dev/null +++ b/demo/desktop/sprites/main.cpp @@ -0,0 +1,174 @@ +#include "main.h" + +int main(int argc, char ** argv) +{ + + jGL::DesktopDisplay::Config conf; + + conf.VULKAN = false; + conf.COCOA_RETINA = false; + + jGL::DesktopDisplay display(glm::ivec2(resX,resY),"Sprites", conf); + + glewInit(); + + jGLInstance = std::move(std::make_shared(display.getRes())); + + jGLInstance->setTextProjection(glm::ortho(0.0,double(resX),0.0,double(resY))); + jGLInstance->setMSAA(1); + + jGL::OrthoCam camera(resX, resY, glm::vec2(0.0,0.0)); + + EntityComponentSystem manager; + + jLog::Log log; + + Hop::Console console(log); + + std::unique_ptr world; + + Hop::World::FiniteBoundary mapBounds(0,0,16,16, true, true, true, true); + Hop::World::FixedSource mapSource; + + world = std::make_unique + ( + 2, + &camera, + 16, + 1, + &mapSource, + &mapBounds + ); + + sRender & rendering = manager.getSystem(); + auto assets = std::make_shared( + std::filesystem::path("assets"), jGLInstance + ); + auto spriteRenderer = jGLInstance->createSpriteRenderer(8); + rendering.setSpriteRenderer(spriteRenderer); + rendering.setTextureAssetStore(assets); + + // setup physics system + sPhysics & physics = manager.getSystem(); + physics.setTimeStep(deltaPhysics); + physics.setGravity(9.81, 0.0, -1.0); + + sCollision & collisions = manager.getSystem(); + + auto det = std::make_unique(world.get()); + + auto res = std::make_unique + ( + deltaPhysics*10.0, + 0.66, + 0.0 + ); + + collisions.setDetector(std::move(det)); + collisions.setResolver(std::move(res)); + + Hop::LuaExtraSpace luaStore; + + luaStore.ecs = &manager; + luaStore.world = world.get(); + luaStore.physics = &physics; + luaStore.resolver = &collisions; + + console.luaStore(&luaStore); + + console.runFile("config.lua"); + std::string status = console.luaStatus(); + if (status != "LUA_OK") { WARN(status) >> log; } + + high_resolution_clock::time_point t0, t1, tp0, tp1, tr0, tr1; + + while (display.isOpen()) + { + jGLInstance->beginFrame(); + + jGLInstance->clear(); + + t0 = high_resolution_clock::now(); + + tp0 = high_resolution_clock::now(); + + collisions.centreOn(world.get()->getMapCenter()); + + physics.step(&manager, &collisions, world.get()); + + tp1 = high_resolution_clock::now(); + + tr0 = high_resolution_clock::now(); + + rendering.setProjection(camera.getVP()); + rendering.update(&manager); + rendering.draw(jGLInstance, &manager, world.get()); + + tr1 = high_resolution_clock::now(); + + if (debug) + { + double delta = 0.0; + for (int n = 0; n < 60; n++) + { + delta += deltas[n]; + } + delta /= 60.0; + std::stringstream debugText; + + double pdt = duration_cast>(tp1 - tp0).count(); + double rdt = duration_cast>(tr1 - tr0).count(); + + double mouseX, mouseY; + display.mousePosition(mouseX,mouseY); + + float cameraX = camera.getPosition().x; + float cameraY = camera.getPosition().y; + + glm::vec4 worldPos = camera.screenToWorld(mouseX,mouseY); + + Hop::World::TileData tile = world->getTileData(worldPos[0],worldPos[1]); + + debugText << "Delta: " << fixedLengthNumber(delta,6) << + " (FPS: " << fixedLengthNumber(1.0/delta,4) << ")" << + "\n" << + "Mouse (" << fixedLengthNumber(mouseX,4) << "," << fixedLengthNumber(mouseY,4) << ")" << + "\n" << + "Mouse [world] (" << fixedLengthNumber(worldPos[0],4) << "," << fixedLengthNumber(worldPos[1],4) << ")" << + "\n" << + "Mouse cell (" << fixedLengthNumber(tile.x,4) << ", " << fixedLengthNumber(tile.y,4) << ", " << tile.tileType << + "\n" << + "Camera [world] (" << fixedLengthNumber(cameraX,4) << ", " << fixedLengthNumber(cameraY,4) << ")" << + "\n" << + "update time: " << fixedLengthNumber(pdt+rdt,6) << + "\n" << + "Phys update / draw time: " << fixedLengthNumber(pdt,6) << "/" << fixedLengthNumber(rdt,6); + + jGLInstance->text + ( + debugText.str(), + glm::vec2(64.0f,resY-64.0f), + 0.5f, + glm::vec4(0.0f,0.0f,0.0f, 1.0f) + ); + } + + if (frameId == 30) + { + if (log.size() > 0) + { + std::cout << log.get() << "\n"; + } + } + + jGLInstance->endFrame(); + + display.loop(); + + t1 = high_resolution_clock::now(); + + deltas[frameId] = duration_cast>(t1 - t0).count(); + frameId = (frameId+1) % 60; + } + return 0; +} diff --git a/demo/desktop/sprites/main.h b/demo/desktop/sprites/main.h new file mode 100644 index 00000000..24f5ab00 --- /dev/null +++ b/demo/desktop/sprites/main.h @@ -0,0 +1,102 @@ +#ifndef MAIN_H +#define MAIN_H + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +using namespace std::chrono; + +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#include + +const int resX = 1000; +const int resY = 1000; +const float MAX_SPEED = 1.0/60.0; + +// for smoothing delta numbers +uint8_t frameId = 0; +double deltas[60]; + +bool debug = true; +bool grid = false; +bool paused = true; + +double pointSize = 1.0; +double primitiveSize = 0.5; + +std::pair activeSite(0.0, 0.0); +double radialTheta = 0.0; + +const double deltaPhysics = 1.0/900.0; + +using Hop::Object::Component::cTransform; +using Hop::Object::Component::cPhysics; +using Hop::Object::Component::cRenderable; +using Hop::Object::Component::cCollideable; +using Hop::Object::EntityComponentSystem; +using Hop::Object::Id; +using Hop::Object::Component::CollisionPrimitive; + +using Hop::System::Rendering::sRender; + +using Hop::System::Physics::CollisionDetector; +using Hop::System::Physics::CollisionResolver; +using Hop::System::Physics::sPhysics; +using Hop::System::Physics::sCollision; + +using Hop::System::Signature; + +using Hop::World::MapSource; +using Hop::World::Boundary; +using Hop::World::AbstractWorld; +using Hop::World::MarchingWorld; +using Hop::World::TileWorld; +using jLog::INFO; +using jLog::WARN; + +std::string fixedLengthNumber(double x, unsigned length) +{ + std::string d = std::to_string(x); + std::string dtrunc(length,' '); + for (unsigned c = 0; c < dtrunc.length(); c++/*ayy lmao*/) + { + + if (c >= d.length()) + { + dtrunc[c] = '0'; + } + else + { + dtrunc[c] = d[c]; + } + } + return dtrunc; +} + +std::shared_ptr jGLInstance; + +#endif /* MAIN_H */ diff --git a/include/Component/cRenderable.h b/include/Component/cRenderable.h index 6e1fda7c..f326d5df 100644 --- a/include/Component/cRenderable.h +++ b/include/Component/cRenderable.h @@ -6,7 +6,7 @@ namespace Hop::Object::Component { - struct cRenderable + struct cRenderable { // offset in transform @@ -19,7 +19,7 @@ namespace Hop::Object::Component // atlas coord float ux; float uy; - + // extra? float vx; float vy; @@ -53,7 +53,7 @@ namespace Hop::Object::Component {} cRenderable( - std::string shader, + std::string shader, float r, float g, float b, float a, float ua, float ub, float uc, float ud, uint64_t p = 0 @@ -75,7 +75,6 @@ namespace Hop::Object::Component priority(p), shaderHandle(""),stale(true) {} - }; } diff --git a/include/Component/cSprite.h b/include/Component/cSprite.h new file mode 100644 index 00000000..9231aa2f --- /dev/null +++ b/include/Component/cSprite.h @@ -0,0 +1,39 @@ +#ifndef CSPRITE_H +#define CSPRITE_H + +#include +#include + +namespace Hop::Object::Component +{ + /** + * @brief A sprite component. + * Represents a relative path to a texture asset, and a rectangular + * region of the texture to draw (in pixel units). + */ + struct cSprite + { + + cSprite() + : texturePath(""), tx(0), ty(0), lx(0), ly(0) + {} + + cSprite + ( + std::string path, + uint16_t tx, + uint16_t ty, + uint16_t lx, + uint16_t ly + ) + : texturePath(path), tx(tx), ty(ty), lx(lx), ly(ly) + {} + + std::string texturePath; + uint16_t tx; + uint16_t ty; + uint16_t lx; + uint16_t ly; + }; +} +#endif /* CSPRITE_H */ diff --git a/include/Console/console.h b/include/Console/console.h index 4128e43a..c91dbc49 100644 --- a/include/Console/console.h +++ b/include/Console/console.h @@ -33,6 +33,11 @@ namespace Hop using jLog::Log; using jLog::ERRORCODE; + /** + * @brief Store for lua global state. + * For the console to have access to these classes they must + * be set into LuaExtraSpace and set via Console::luaStore. + */ struct LuaExtraSpace { EntityComponentSystem * ecs; @@ -43,7 +48,7 @@ namespace Hop Scriptz * scripts; }; - // ECS + // ECS typedef int (EntityComponentSystem::*EntityComponentSystemMember)(lua_State * lua); @@ -68,7 +73,7 @@ namespace Hop } // Physics - + typedef int (sPhysics::*sPhysicsMember)(lua_State * lua); template @@ -80,7 +85,7 @@ namespace Hop } // sCollision - + typedef int (sCollision::*sCollisionMember)(lua_State * lua); template @@ -92,7 +97,7 @@ namespace Hop } // Scriptz - + typedef int (Scriptz::*ScriptzMember)(lua_State * lua); template @@ -103,17 +108,49 @@ namespace Hop return ((*ptr).*function)(lua); } - + /** + * @brief Configure physics. + * @remark Takes a lua table: +{ + ["timeStep"] = 1.0/1800.0, + ["subSample"] = 5, + ["cofr"] = 0.9, + ["surfaceFriction"] = 0.5 +} + * @param lua lua_State. + * @return int lua return code. + */ int configure(lua_State * lua); + /** + * @brief Returns the current system clock time in lua. + * + * @param lua lua_State. + * @return int return code. + */ int timeMillis(lua_State * lua); + /** + * @brief Apply a force to an object. + * @remark Takes the string id, and forces in x and y. + * @param lua lua_State. + * @return int return code. + */ int lua_applyForce(lua_State * lua); - class Console + /** + * @brief Lua console. + * @remark Access to Hop classes is via LuaExtraSpace set by Console::luaStore. + */ + class Console { public: + /** + * @brief Construct a new Console with a logger. + * + * @param l Log outputting Lua's messages. + */ Console(Log & l) : lastCommandOrProgram(""), lastStatus(false), log(l) { @@ -125,6 +162,13 @@ namespace Hop ~Console(){ lua_close(lua); } + /** + * @brief Attempt to run a Lua script from a file on disc. + * + * @param file Lua script location. + * @return true Error occured. + * @return false OK. + */ bool runFile(std::string file) { if (luaIsOk()) @@ -141,6 +185,13 @@ namespace Hop return false; } + /** + * @brief Attempt to run a Lua script from std::string. + * + * @param file Lua script. + * @return true Error occured. + * @return false OK. + */ bool runString(std::string program) { if (luaIsOk()) @@ -153,6 +204,11 @@ namespace Hop bool luaIsOk(){ return lua_status(lua) == LUA_OK ? true : false; } + /** + * @brief Convert Lua's status to a std::string + * + * @return std::string Lua status name. + */ std::string luaStatus() { int s = lua_status(lua); @@ -186,26 +242,21 @@ namespace Hop return status; } - bool handleErrors() - { - if (lastStatus) - { - std::string msg = "Exited with error running "+lastCommandOrProgram+"\n"; - msg += stackTrace; - ERR(ERRORCODE::LUA_ERROR, msg) >> log; - return true; - } - else - { - return false; - } - } - + /** + * @brief Set the LuaExtraSpace holding Hop classes. + * @remark This gives Console access to Hop. + * @param ptr LuaExtraSpace. + */ void luaStore(LuaExtraSpace * ptr) { *static_cast(lua_getextraspace(lua)) = ptr; } + /** + * @brief Run a packed script from Scriptz in LuaExtraSpace. + * + * @param name The script name. + */ void runScript(std::string name) { @@ -221,14 +272,12 @@ namespace Hop } lastCommandOrProgram = name; - lastStatus = + lastStatus = ( luaL_loadstring(lua, body.c_str()) || lua_pcall(lua, 0, LUA_MULTRET, 0) ); - handleErrors(); - } double getNumber(const char * n) @@ -295,6 +344,22 @@ namespace Hop return 0; } + bool handleErrors() + { + if (lastStatus) + { + std::string msg = "Exited with error running "+lastCommandOrProgram+"\n"; + msg += stackTrace; + ERR(ERRORCODE::LUA_ERROR, msg) >> log; + return true; + } + else + { + return false; + } + } + + // register lib static int load_hopLib(lua_State * lua) diff --git a/include/Console/lua.h b/include/Console/lua.h index 47b3d08d..991b5a11 100644 --- a/include/Console/lua.h +++ b/include/Console/lua.h @@ -21,6 +21,15 @@ namespace Hop std::vector< std::vector > getLuaTableOfNumericLuaTable(lua_State * lua, int index); + /** + * @brief Check the argument count to the lua call. + * @param lua lua_State. + * @param expected argument count expected. + * @param msg Error string pushed if not LUA_OK. + * @return int return code. LUA_OK if the argument count matches. + */ + int lua_checkArgumentCount(lua_State * lua, int expected, std::string msg); + } #endif /* LUA */ diff --git a/include/Object/entityComponentSystem.h b/include/Object/entityComponentSystem.h index 0cc6f46f..aeb5eba2 100644 --- a/include/Object/entityComponentSystem.h +++ b/include/Object/entityComponentSystem.h @@ -1,6 +1,8 @@ #ifndef ENTITYCOMPONENTSYSTEM_H #define ENTITYCOMPONENTSYSTEM_H +#include + #include #include @@ -33,7 +35,7 @@ #include using namespace std::chrono; -namespace Hop +namespace Hop { class Console; } @@ -58,7 +60,7 @@ namespace Hop::Object { using namespace Hop::Object::Component; - + using namespace Hop::System; using Hop::System::Physics::sPhysics; using Hop::System::Physics::sCollision; @@ -66,48 +68,45 @@ namespace Hop::Object using Hop::System::Sound::sSound; using Hop::Console; - - /* - Stores an unordered map of objects that can be added to - and removed from - - Objects are associated with a string identity, which can be - the string form of Object/id.h or a user provided handle - subject to a uniqueness check - - Object dynamics is step by step(delta), and drawing - is dispatched with draw(debug) - - Callback is calle on collisions, can be user specified with - user logic, e.g: - "if collision between player and power up call player.collectPowerUp() and powerUp.delete()" - */ - - // define CollisionCallback as this func ptr - typedef void (*CollisionCallback)(Id & i, Id & j); - void identityCallback(Id & i, Id & j); - - const uint32_t MAX_OBJECTS = 100000; - - class EntityComponentSystem + /** + * @brief Stores objects (identity) dynamic components and systems. + * @remark An object is associated with a unique Id. + * @remark Components can be dynamically added, removed, and modified. + */ + class EntityComponentSystem { public: - EntityComponentSystem( - void (*callback)(Id & i, Id & j) = &identityCallback - ) - : collisionCallback(callback), - nextComponentIndex(0) + /** + * @brief Construct a new EntityComponentSystem + * + * @remark Constructs an default ECS with all Hop components and systems. + */ + EntityComponentSystem() + : nextComponentIndex(0) { initialiseBaseECS(); objects.clear(); } Id createObject(); + + /** + * @brief Create a Object returning its id and associating a user defined string name. + * + * @param handle user defined alias. + * @return Id the objects unique id. + */ Id createObject(std::string handle); void remove(Id id); + + /** + * @brief Remove an object by the users alias. + * + * @param handle user defined alias. + */ void remove(std::string handle); bool handleExists(std::string handle) const { return handleToId.find(handle) != handleToId.cend(); } @@ -119,10 +118,13 @@ namespace Hop::Object const std::unordered_map> & getObjects() { return objects; } - CollisionCallback collisionCallback; - // component interface + /** + * @brief Register a new component. + * + * @tparam T the component class to register. + */ template void registerComponent() { @@ -141,9 +143,15 @@ namespace Hop::Object registeredComponents[handle] = nextComponentIndex; nextComponentIndex++; componentData[handle] = std::make_shared>(MAX_OBJECTS); - } + /** + * @brief Add a component to an object. + * + * @tparam T the registered component. + * @param i the object Id. + * @param component the component value. + */ template void addComponent(Id i, T component) { @@ -162,6 +170,12 @@ namespace Hop::Object systemManager.objectSignatureChanged(i,idToSignature[i]); } + /** + * @brief Remove a component from an object. + * + * @tparam T the registered component class. + * @param i the object Id to remove from. + */ template void removeComponent(Id i) { @@ -180,54 +194,78 @@ namespace Hop::Object systemManager.objectSignatureChanged(i,idToSignature[i]); } + /** + * @brief Get a Component for an object. + * + * @tparam T the component class. + * @param i the object Id to get the component T from. + * @return T& the component values for the object Id; + */ template inline T & getComponent(const Id & i) { const char * handle = typeid(T).name(); - - // if (!componentRegistered(handle)){ - // throw ComponentNotRegistered(" Attempt to getComponent<"+i.idStr+")"); - // } return (std::static_pointer_cast>(componentData[handle]))->get(i); } - void objectFreed(Id i) - { - for (auto const& pair : componentData) - { - pair.second.get()->objectFreed(i); - } - } - - template - uint32_t getComponentId() - { - const char * handle = typeid(T).name(); - return registeredComponents[handle]; - } - // system interface + + /** + * @brief Register a class T as a new system. + * + * @tparam T the class to register. + */ template void registerSystem(){systemManager.registerSystem();} + /** + * @brief Set the Signature for the system T + * + * @tparam T the registered system class. + * @param signature the new Signature. + */ template void setSystemSignature(Signature signature){systemManager.setSignature(signature);} + /** + * @brief Get the System T + * + * @tparam T the registered system T to get.. + * @return T& the system. + */ template T & getSystem(){return systemManager.getSystem();} + /** + * @brief Check if the object has the given component. + * + * @tparam T the registered component class. + * @param i the object Id to check. + * @return true i has the component. + * @return false i does not have the component. + */ template bool hasComponent(const Id & i){return idToSignature[i][getComponentId()];} + /** + * @brief Get the full array of component T, as a copy; + * + * @tparam T the registered component + * @return ComponentArray all values of component T. + */ template ComponentArray getComponentArrayCopy() { const char * handle = typeid(T).name(); - return *(std::static_pointer_cast>(componentData[handle])); - } + /** + * @brief Get the full array of component T, as a copy; + * + * @tparam T the registered component + * @return ComponentArray & all values of component T. + */ template ComponentArray & getComponentArray() { @@ -237,14 +275,11 @@ namespace Hop::Object } - template - void updateMainComponents(); - // Lua bindings int lua_loadObject(lua_State * lua); int lua_deleteObject(lua_State * lua); - + int lua_getTransform(lua_State * lua); int lua_setTransform(lua_State * lua); @@ -255,6 +290,12 @@ namespace Hop::Object int lua_getColour(lua_State * lua); int lua_setColour(lua_State * lua); + int lua_setTextureRegion(lua_State * lua); + int lua_getTextureRegion(lua_State * lua); + + int lua_setTexturePath(lua_State * lua); + int lua_getTexturePath(lua_State * lua); + private: @@ -266,8 +307,6 @@ namespace Hop::Object void initialiseBaseECS(); - // components - bool componentRegistered(const char * h){return registeredComponents.find(h)!=registeredComponents.end();} uint32_t nextComponentIndex; @@ -276,6 +315,12 @@ namespace Hop::Object std::unordered_map> componentData; + template + uint32_t getComponentId() + { + const char * handle = typeid(T).name(); + return registeredComponents[handle]; + } }; } diff --git a/include/System/Rendering/sRender.h b/include/System/Rendering/sRender.h index d0f6fe34..9d91633b 100644 --- a/include/System/Rendering/sRender.h +++ b/include/System/Rendering/sRender.h @@ -3,13 +3,18 @@ #include #include +#include #include #include +#include +#include +#include #include #include #include +#include #include @@ -29,25 +34,30 @@ namespace Hop::System::Rendering using jLog::Log; using Hop::World::AbstractWorld; using Hop::Debugging::CollisionMeshDebug; - + using Hop::Util::Assets::TextureAssetStore; + using Hop::Object::Component::ComponentArray; + using Hop::Object::Component::cSprite; + using Hop::Object::Component::cRenderable; + using Hop::Object::Component::cTransform; + /** + * @brief System to handle rendering. + * + */ class sRender : public System { public: + /** + * @brief Construct a new Renderer. + * + */ sRender() - : accumulatedTime(0.0), - projection(0.0f), + : accumulatedTime(0.0), + projection(0.0f), drawCollisionMeshPoints(false), clock(std::chrono::high_resolution_clock::now()), - collisionMeshDebug(nullptr) - {} - - sRender(std::shared_ptr jgl) - : accumulatedTime(0.0), - projection(0.0f), - drawCollisionMeshPoints(false), - clock(std::chrono::high_resolution_clock::now()), - collisionMeshDebug(std::move(std::make_unique(jgl))) + collisionMeshDebug(nullptr), + textures(nullptr) {} void setProjection(glm::mat4 proj) { projection = proj; } @@ -55,25 +65,53 @@ namespace Hop::System::Rendering double draw ( std::shared_ptr jgl, - EntityComponentSystem * ecs = nullptr, + EntityComponentSystem * ecs = nullptr, AbstractWorld * world = nullptr ); + /** + * @brief Draw debug meshes. + * + * @param b switch to draw debug messhes. + */ void setDrawMeshes(bool b) { drawCollisionMeshPoints = b; } + /** + * @brief Set the TextureAssetStore for the rendering system. + * + * @param textureStore the TextureAssetStore. + * @remark draw will reference textures from here. + */ + void setTextureAssetStore(std::shared_ptr textureStore){ textures = textureStore; } + + /** + * @brief Set the SpriteRenderer for the rendering system. + * + * @param spriteRenderer a jGL::SpriteRenderer. + * @remark draw will reference sprites from here. + */ + void setSpriteRenderer(std::shared_ptr spriteRenderer) { sprites = spriteRenderer; } + + /** + * @brief Update the renderer from the ecs. + * + * @param ecs EntityComponentSystem. + * @remark Currently changes to Hop::Object::Component::cSprite, and Hop::Object::Component::cRenderable + * require updating manually. + * @remark Sprites already track Hop::Object::Component::cTransform. + */ + void update(EntityComponentSystem * ecs); + private: double accumulatedTime; glm::mat4 projection; - bool drawCollisionMeshPoints; - std::chrono::time_point clock; - std::unique_ptr collisionMeshDebug; - - void update(EntityComponentSystem * ecs); - + std::shared_ptr sprites = nullptr; + std::unique_ptr collisionMeshDebug = nullptr; + std::shared_ptr textures = nullptr; }; } diff --git a/include/System/systemManager.h b/include/System/systemManager.h index 55ed575b..c4ccea8e 100644 --- a/include/System/systemManager.h +++ b/include/System/systemManager.h @@ -1,7 +1,9 @@ #ifndef SYSTEMMANAGER_H #define SYSTEMMANAGER_H +#include #include + #include #include #include @@ -9,11 +11,9 @@ namespace Hop::System { - - const uint32_t MAX_COMPONENTS = 64; typedef std::bitset Signature; - class SystemNotRegistered: public std::exception + class SystemNotRegistered: public std::exception { public: @@ -33,14 +33,23 @@ namespace Hop::System }; - - class SystemManager + /** + * @brief Registers and manages systems signatures. + * + */ + class SystemManager { public: SystemManager(){} + /** + * @brief Register T as a system. + * + * @tparam T the system class. + * @remark Gives T the catch all Signature. + */ template void registerSystem() { @@ -55,6 +64,12 @@ namespace Hop::System systems[handle] = s; } + /** + * @brief Set the Signature for system T. + * + * @tparam T the registered System class. + * @param signature object Signature. + */ template void setSignature(Signature signature) { @@ -67,6 +82,12 @@ namespace Hop::System signatures[handle] = signature; } + /** + * @brief Return the given system T. + * + * @tparam T the registered system class. + * @return T& the system object. + */ template T & getSystem() { @@ -79,11 +100,15 @@ namespace Hop::System return *(std::static_pointer_cast(systems[handle]).get()); } - void objectFreed(Id i); - + /** + * @brief Refresh object i's Signature. + * + * @param i Id of the changed object. + * @param s the new Signature. + */ void objectSignatureChanged(Id i, Signature s); - + private: bool isRegistered(const char * s){return systems.find(s) != systems.end();} diff --git a/include/Util/assetStore.h b/include/Util/assetStore.h index 3da7c380..da9cdc7a 100644 --- a/include/Util/assetStore.h +++ b/include/Util/assetStore.h @@ -8,19 +8,36 @@ #include #include #include +#include namespace Hop::Util::Assets { + /** + * @brief A data store for a general asset T. + * + * @tparam T type of the asset. + * @remark Assets are loading from relative paths to a supplied root, seet AssetStore::AssetStore. + */ template - class AssetStore + class AssetStore { public: + /** + * @brief Construct a new AssetStore. + * + * @param root the relative path for all assets. + */ AssetStore(std::filesystem::path root) - : root(root) + : root(root) {} - void scan() + /** + * @brief Caches all matching asset paths relative to root. + * @remark Specialised AssetStores overloads a method to check if a path is an asset of type T. + * This is done by file extensions (.png, .mp3, ...). + */ + void scan() { if (std::filesystem::is_directory(root)) { @@ -28,9 +45,40 @@ namespace Hop::Util::Assets } }; - virtual void load(std::filesystem::path assetPath) = 0; + /** + * @brief Loads the asset at this relative path. + * + * @param relativePath relative path to an asset. + */ + virtual void load(std::filesystem::path relativePath) = 0; + + /** + * @brief Loads all assets in the asset store. + * @remark Use with scan. + */ + void loadAll() + { + for (const auto & p : assets) + { + load(p); + } + } - std::shared_ptr get(std::filesystem::path relative_path) { return assets[relative_path]; } + /** + * @brief Return the asset. + * + * @param relativePath path to asset. + * @return std::shared_ptr the asset. + * @remark Will attempt to load an unloaded asset. + */ + std::shared_ptr get(std::filesystem::path relativePath) + { + if (assets[relativePath] == nullptr) + { + load(relativePath); + } + return assets[relativePath]; + } typename std::map>::const_iterator begin() { return assets.cbegin(); } typename std::map>::const_iterator end() { return assets.cend(); } @@ -47,7 +95,8 @@ namespace Hop::Util::Assets { if (entry.is_regular_file()) { - if (matchesAssetType(entry)) { load(entry); } + std::filesystem::path relative = std::filesystem::relative(entry, root); + if (matchesAssetType(relative) && assets.find(relative) == assets.end()) { assets[relative] = nullptr; } } else { @@ -57,30 +106,56 @@ namespace Hop::Util::Assets } }; + /** + * @brief AssetStore specialisation to store texture assets. + * + */ class TextureAssetStore : public AssetStore { public: - TextureAssetStore(std::filesystem::path root, std::unique_ptr & instance) + /** + * @brief Construct a new TextureAssetStore + * + * @param root relative path for textures. + * @param instance jGLInstance for creating textures. + * @remark Currently supports .png, .jpg files. + */ + TextureAssetStore(std::filesystem::path root, std::shared_ptr & instance) : AssetStore(root), instance(instance) - {} + { + scan(); + } - void load(std::filesystem::path assetPath) + /** + * @brief Load a texture. + * + * @param relativePath path relative to root. + */ + void load(std::filesystem::path relativePath) { - std::filesystem::path relative = std::filesystem::relative(assetPath, root); - assets[relative] = instance->createTexture - ( - assetPath.generic_string(), - jGL::Texture::Type::RGBA - ); + if (matchesAssetType(relativePath)) + { + std::filesystem::path path = root / relativePath; + assets[relativePath] = instance->createTexture + ( + path.generic_string(), + jGL::Texture::Type::RGBA + ); + } }; + /** + * @brief Supported texture extension. + * @remark These will be loaded as RGBA textures. + */ + const std::array extensions = {".png", ".jpg"}; + protected: - std::unique_ptr & instance; + std::shared_ptr & instance; - bool matchesAssetType(std::filesystem::path file) - { - static std::vector extensions = {".png"}; + bool matchesAssetType(std::filesystem::path file) + { return std::find(extensions.begin(), extensions.end(), file.extension().generic_string()) != extensions.end(); } }; diff --git a/include/constants.h b/include/constants.h new file mode 100644 index 00000000..c1ad6c9c --- /dev/null +++ b/include/constants.h @@ -0,0 +1,20 @@ +#ifndef CONSTANTS_H +#define CONSTANTS_H + +#include + +namespace Hop +{ + /** + * @brief Maximum objects in the Hop::Object::EntityComponentSystem. + * + */ + const uint32_t MAX_OBJECTS = 100000; + /** + * @brief Maximum components in the Hop::Object::EntityComponentSystem. + * + */ + const uint32_t MAX_COMPONENTS = 64; +}; + +#endif /* CONSTANTS_H */ diff --git a/include/jGL b/include/jGL index 80147c4f..04563847 160000 --- a/include/jGL +++ b/include/jGL @@ -1 +1 @@ -Subproject commit 80147c4f20a08c4eb089967471581d28c26327d8 +Subproject commit 045638478207ab1a6e2ca952df62730c03c5fb17 diff --git a/src/Console/console.cpp b/src/Console/console.cpp index de0ca514..72402532 100644 --- a/src/Console/console.cpp +++ b/src/Console/console.cpp @@ -6,25 +6,19 @@ namespace Hop int configure(lua_State * lua) { - LuaExtraSpace * store = *static_cast(lua_getextraspace(lua)); - - sPhysics * phys = store->physics; - sCollision * col = store->resolver; - - LuaNumber dt, subSample, cor, sf; - - int n = lua_gettop(lua); - + int status = lua_checkArgumentCount(lua, 1, "expected table as argument"); + if (status != LUA_OK) { return status; } if (!lua_istable(lua, 1)) { lua_pushliteral(lua, "non table argument"); return lua_error(lua); } - else if (n != 1) - { - lua_pushliteral(lua, "more than one argument"); - return lua_error(lua); - } + LuaExtraSpace * store = *static_cast(lua_getextraspace(lua)); + + sPhysics * phys = store->physics; + sCollision * col = store->resolver; + + LuaNumber dt, subSample, cor, sf; if (dt.readField(lua, "timeStep")) { @@ -58,17 +52,11 @@ namespace Hop int lua_applyForce(lua_State * lua) { + int status = lua_checkArgumentCount(lua, 3, "expected id and force vector, fx, fy as argument"); + if (status != LUA_OK) { return status; } LuaNumber fx, fy; LuaString sid; - int n = lua_gettop(lua); - - if (n != 3) - { - lua_pushliteral(lua,"expected id and force vector, fx, fy as argument"); - return lua_error(lua); - } - sid.read(lua, 1); Hop::Object::Id id(sid.characters); diff --git a/src/Console/lua.cpp b/src/Console/lua.cpp index 6c68ca67..b9feef3d 100644 --- a/src/Console/lua.cpp +++ b/src/Console/lua.cpp @@ -2,7 +2,7 @@ namespace Hop { - + std::vector getNumericLuaTable(lua_State * lua, int index) { unsigned length = lua_rawlen(lua,index); @@ -42,4 +42,14 @@ namespace Hop return values; } + int lua_checkArgumentCount(lua_State * lua, int expected, std::string msg) + { + int n = lua_gettop(lua); + if (n != expected) + { + lua_pushlstring(lua, msg.c_str(), msg.length()); + return lua_error(lua); + } + return LUA_OK; + } } \ No newline at end of file diff --git a/src/Object/LuaBindings/lua_meshIO.cpp b/src/Object/LuaBindings/lua_meshIO.cpp index 415a5d8b..5e3f32b9 100644 --- a/src/Object/LuaBindings/lua_meshIO.cpp +++ b/src/Object/LuaBindings/lua_meshIO.cpp @@ -9,13 +9,8 @@ namespace Hop::Object { int EntityComponentSystem::lua_removeFromMeshByTag(lua_State * lua) { - int n = lua_gettop(lua); - - if (n != 2) - { - lua_pushliteral(lua, "expected id and tag as argument"); - return lua_error(lua); - } + int status = lua_checkArgumentCount(lua, 2, "expected id and tag as argument"); + if (status != LUA_OK) { return status; } LuaString sid; LuaNumber ltag; @@ -37,13 +32,8 @@ namespace Hop::Object int EntityComponentSystem::lua_meshBoundingBox(lua_State * lua) { - int n = lua_gettop(lua); - - if (n != 1) - { - lua_pushliteral(lua, "expected id as argument"); - return lua_error(lua); - } + int status = lua_checkArgumentCount(lua, 1, "expected id as argument"); + if (status != LUA_OK) { return status; } LuaString sid; @@ -96,13 +86,8 @@ namespace Hop::Object int EntityComponentSystem::lua_meshBoundingBoxByTag(lua_State * lua) { - int n = lua_gettop(lua); - - if (n != 2) - { - lua_pushliteral(lua, "expected id and tag as argument"); - return lua_error(lua); - } + int status = lua_checkArgumentCount(lua, 2, "expected id and tag as argument"); + if (status != LUA_OK) { return status; } LuaString sid; LuaNumber ltag; @@ -149,7 +134,6 @@ namespace Hop::Object lua_pushnumber(lua, bb.centre.y); lua_setfield(lua, -2, "y"); lua_setfield(lua, -2, "centre"); - return 1; } diff --git a/src/Object/LuaBindings/lua_objectIO.cpp b/src/Object/LuaBindings/lua_objectIO.cpp index 50721789..2c6527cc 100644 --- a/src/Object/LuaBindings/lua_objectIO.cpp +++ b/src/Object/LuaBindings/lua_objectIO.cpp @@ -7,6 +7,8 @@ ["transform"] = {x,y,0.0,1.0}, ["colour"] = {200/255,200/255,250/255,1.0}, + ["texturePath"] = "some/relative/path/to.png", + ["textureRegion"] = {0, 0, 16, 16}, ["shader"] = "circleObjectShader", ["moveable"] = false, ["collisionMesh"] = @@ -40,16 +42,10 @@ namespace Hop::Object int EntityComponentSystem::lua_deleteObject(lua_State * lua) { + int status = lua_checkArgumentCount(lua, 1, "expected id as argument"); + if (status != LUA_OK) { return status; } LuaString sid; - int n = lua_gettop(lua); - - if (n != 1) - { - lua_pushliteral(lua,"expected id as argument"); - return lua_error(lua); - } - sid.read(lua, 1); Id id(sid.characters); @@ -61,12 +57,21 @@ namespace Hop::Object int EntityComponentSystem::lua_loadObject(lua_State * lua) { - LuaArray<4> colour, transform, util; + int status = lua_checkArgumentCount(lua, 1, "expected table as argument"); + if (status != LUA_OK) { return status; } + + if (!lua_istable(lua,1)) + { + lua_pushliteral(lua,"non table argument"); + return lua_error(lua); + } + + LuaArray<4> colour, transform, util, textureRegion; LuaArray<3> meshParameters; LuaNumber transDrag, rotDrag, bodyMass, bodyInertia, bodyFriction, priority; - LuaString shader, name; + LuaString shader, name, texturePath; LuaBool isMoveable, isGhost; @@ -74,6 +79,8 @@ namespace Hop::Object bool hasColour = false; bool hasTransform = false; + bool hasTexture = false; + bool hasTextureRegion = false; bool isRenderable = false; bool isPhysics = false; @@ -91,34 +98,19 @@ namespace Hop::Object priority.n = 0; - // elements on stack - int n = lua_gettop(lua); - - if (!lua_istable(lua,1)) - { - lua_pushliteral(lua,"non table argument"); - return lua_error(lua); - } - else if (n != 1) - { - lua_pushliteral(lua,"requires single argument"); - return lua_error(lua); - } - hasColour = colour.readField(lua, "colour"); hasTransform = transform.readField(lua, "transform"); if (hasTransform && hasColour) { isRenderable = true; } shader.readField(lua, "shader"); - + hasTextureRegion = textureRegion.readField(lua, "textureRegion"); + hasTexture = texturePath.readField(lua, "texturePath"); priority.readField(lua, "renderPriority"); isMoveable.readField(lua, "moveable"); isGhost.readField(lua, "ghost"); - isPhysics = collisionMesh.readField(lua, "collisionMesh"); - meshParameters.readField(lua, "meshParameters"); name.readField(lua, "name"); @@ -132,7 +124,7 @@ namespace Hop::Object // now create the object Id pid; - + if (name.characters != "") { pid = createObject(name.characters); @@ -191,7 +183,7 @@ namespace Hop::Object ub = param[1]; uc = param[2]; ud = param[3]; - + } else if (returnType == LUA_TNONE || returnType == LUA_TNIL) { @@ -199,7 +191,6 @@ namespace Hop::Object } lua_pop(lua,1); - } addComponent @@ -207,6 +198,29 @@ namespace Hop::Object pid, cRenderable(shader.characters,r,g,b,a,ua,ub,uc,ud,priority.n) ); + + if (hasTexture) + { + jGL::TextureRegion region; + if (hasTextureRegion) + { + region.tx = textureRegion.elements[0]; + region.ty = textureRegion.elements[1]; + region.lx = textureRegion.elements[2]; + region.ly = textureRegion.elements[3]; + } + addComponent + ( + pid, + cSprite( + texturePath.characters, + region.tx, + region.ty, + region.lx, + region.ty + ) + ); + } } if (isPhysics) @@ -220,7 +234,6 @@ namespace Hop::Object cPhysics & data = getComponent(pid); data.isMoveable = isMoveable.bit; data.isGhost = isGhost.bit; - if (collisionMesh.size() > 0) { diff --git a/src/Object/LuaBindings/lua_renderableIO.cpp b/src/Object/LuaBindings/lua_renderableIO.cpp index 0a0f4859..3da87d32 100644 --- a/src/Object/LuaBindings/lua_renderableIO.cpp +++ b/src/Object/LuaBindings/lua_renderableIO.cpp @@ -10,14 +10,8 @@ namespace Hop::Object int EntityComponentSystem::lua_getColour(lua_State * lua) { LuaString sid; - - int n = lua_gettop(lua); - - if (n != 1) - { - lua_pushliteral(lua,"expected id as argument"); - return lua_error(lua); - } + int status = lua_checkArgumentCount(lua, 1, "expected id as argument"); + if (status != LUA_OK) { return status; } sid.read(lua, 1); @@ -37,14 +31,8 @@ namespace Hop::Object { LuaString sid; LuaNumber r, g, b, a; - - int n = lua_gettop(lua); - - if (n != 5) - { - lua_pushliteral(lua,"expected id, and r, g, b, a as argument"); - return lua_error(lua); - } + int status = lua_checkArgumentCount(lua, 5, "expected id, and r, g, b, a as argument"); + if (status != LUA_OK) { return status; } sid.read(lua, 1); diff --git a/src/Object/LuaBindings/lua_textureIO.cpp b/src/Object/LuaBindings/lua_textureIO.cpp new file mode 100644 index 00000000..1c1e0176 --- /dev/null +++ b/src/Object/LuaBindings/lua_textureIO.cpp @@ -0,0 +1,79 @@ +#include +#include + +#include + +namespace Hop::Object +{ + using Hop::Object::Component::cSprite; + + int EntityComponentSystem::lua_setTextureRegion(lua_State * lua) + { + int status = lua_checkArgumentCount(lua, 5, "expected id, tx, ty, lx, ly as argument"); + if (status != LUA_OK) { return status; } + LuaString sid; + LuaNumber tx, ty, lx, ly; + + sid.read(lua, 1); + + Id id(sid.characters); + cSprite & s = getComponent(id); + tx.read(lua, 2); + ty.read(lua, 3); + lx.read(lua, 4); + ly.read(lua, 5); + + s.tx = tx; + s.ty = ty; + s.lx = lx; + s.ly = ly; + + return 0; + } + + int EntityComponentSystem::lua_getTextureRegion(lua_State * lua) + { + int status = lua_checkArgumentCount(lua, 1, "expected id as argument"); + if (status != LUA_OK) { return status; } + LuaString sid; + + sid.read(lua, 1); + + Id id(sid.characters); + const cSprite & s = getComponent(id); + lua_pushnumber(lua, s.tx); + lua_pushnumber(lua, s.ty); + lua_pushnumber(lua, s.lx); + lua_pushnumber(lua, s.ly); + return 4; + } + + int EntityComponentSystem::lua_setTexturePath(lua_State * lua) + { + int status = lua_checkArgumentCount(lua, 2, "expected id and path as argument"); + if (status != LUA_OK) { return status; } + LuaString sid, path; + + sid.read(lua, 1); + path.read(lua, 2); + + Id id(sid.characters); + cSprite & s = getComponent(id); + s.texturePath = path; + return 0; + } + + int EntityComponentSystem::lua_getTexturePath(lua_State * lua) + { + int status = lua_checkArgumentCount(lua, 1, "expected id as argument"); + if (status != LUA_OK) { return status; } + LuaString sid; + + sid.read(lua, 1); + + Id id(sid.characters); + const cSprite & s = getComponent(id); + lua_pushstring(lua, s.texturePath.c_str()); + return 1; + } +} diff --git a/src/Object/LuaBindings/lua_transformIO.cpp b/src/Object/LuaBindings/lua_transformIO.cpp index efd5ef47..29ee28bc 100644 --- a/src/Object/LuaBindings/lua_transformIO.cpp +++ b/src/Object/LuaBindings/lua_transformIO.cpp @@ -9,17 +9,10 @@ namespace Hop::Object int EntityComponentSystem::lua_getTransform(lua_State * lua) { - + int status = lua_checkArgumentCount(lua, 1, "expected id as argument"); + if (status != LUA_OK) { return status; } LuaString sid; - int n = lua_gettop(lua); - - if (n != 1) - { - lua_pushliteral(lua,"expected id as argument"); - return lua_error(lua); - } - sid.read(lua, 1); Id id(sid.characters); diff --git a/src/Object/entityComponentSystem.cpp b/src/Object/entityComponentSystem.cpp index ba61dba1..65a0b30c 100644 --- a/src/Object/entityComponentSystem.cpp +++ b/src/Object/entityComponentSystem.cpp @@ -7,8 +7,6 @@ namespace Hop::Object std::shared_ptr o = std::make_shared(); objects[o->id] = o; idToSignature[o->id] = Signature(); - //handleToId[Hop::Object::to_string(o->id)] = o->id; - return o->id; } @@ -18,9 +16,7 @@ namespace Hop::Object objects[o->id] = o; idToSignature[o->id] = Signature(); - handleToId[handle] = o->id; - return o->id; } @@ -48,14 +44,12 @@ namespace Hop::Object void EntityComponentSystem::remove(std::string handle){} - // do nothing callback - void identityCallback(Id & i, Id & j){return;} - void EntityComponentSystem::initialiseBaseECS() { registerComponent(); registerComponent(); + registerComponent(); registerComponent(); registerComponent(); //registerComponent(); @@ -74,23 +68,19 @@ namespace Hop::Object uint32_t soundId = getComponentId(); Signature sRenderSig = Signature(); - sRenderSig.set ( rId, true ); - sRenderSig.set ( tId, true ); - systemManager.setSignature(sRenderSig); Signature sPhysicsSig = Signature(); - sPhysicsSig.set ( pId, @@ -101,11 +91,9 @@ namespace Hop::Object tId, true ); - systemManager.setSignature(sPhysicsSig); - - Signature sCollisionSig = Signature(); + Signature sCollisionSig = Signature(); sCollisionSig.set ( pId, @@ -116,19 +104,15 @@ namespace Hop::Object cId, true ); - systemManager.setSignature(sCollisionSig); Signature sSoundSig = Signature(); - sSoundSig.set ( soundId, true ); - systemManager.setSignature(sSoundSig); - } } @@ -137,4 +121,5 @@ namespace Hop::Object #include #include #include -#include \ No newline at end of file +#include +#include \ No newline at end of file diff --git a/src/System/Rendering/sRender.cpp b/src/System/Rendering/sRender.cpp index a17a2fb2..4f326601 100644 --- a/src/System/Rendering/sRender.cpp +++ b/src/System/Rendering/sRender.cpp @@ -2,37 +2,103 @@ namespace Hop::System::Rendering { - double sRender::draw - ( - std::shared_ptr jgl, - EntityComponentSystem * ecs, - AbstractWorld * world - ) + double sRender::draw + ( + std::shared_ptr jgl, + EntityComponentSystem * ecs, + AbstractWorld * world + ) + { + if (world != nullptr) { + world->draw(); + } - if (world != nullptr) + if (drawCollisionMeshPoints) + { + if (collisionMeshDebug == nullptr) { - world->draw(); + collisionMeshDebug = std::move(std::make_unique(jgl)); } + collisionMeshDebug->refreshMeshes(); + collisionMeshDebug->drawMeshes(ecs, projection); + } - if (drawCollisionMeshPoints) - { - if (collisionMeshDebug == nullptr) - { - collisionMeshDebug = std::move(std::make_unique(jgl)); - } - collisionMeshDebug->refreshMeshes(); - collisionMeshDebug->drawMeshes(ecs, projection); - } + if (sprites != nullptr) + { + sprites->setProjection(projection); + sprites->draw(); + } - auto t = std::chrono::high_resolution_clock::now(); - accumulatedTime += std::chrono::duration_cast>(t-clock).count(); - clock = t; + auto t = std::chrono::high_resolution_clock::now(); + accumulatedTime += std::chrono::duration_cast>(t-clock).count(); + clock = t; - return accumulatedTime; - } + return accumulatedTime; + } - void sRender::update(EntityComponentSystem * ecs) + void sRender::update(EntityComponentSystem * ecs) + { + if (sprites != nullptr) { + ComponentArray & spriteComponents = ecs->getComponentArray(); + ComponentArray & renderables = ecs->getComponentArray(); + ComponentArray & transforms = ecs->getComponentArray(); + for (const auto & object : objects) + { + if (renderables.hasComponent(object) && spriteComponents.hasComponent(object) && transforms.hasComponent(object)) + { + const cSprite & oSprite = spriteComponents.get(object); + const cRenderable & oRenderable = renderables.get(object); + const cTransform & oTransform = transforms.get(object); + std::string sid = to_string(object); + auto tex = textures->get(oSprite.texturePath); + auto region = jGL::TextureRegion + ( + oSprite.tx, + oSprite.ty, + oSprite.lx, + oSprite.ly + ); + + if (sprites->hasId(sid)) + { + auto & sprite = sprites->getSprite(sid); + if (!(tex->getId() == sprite.texture->getId())) + { + sprites->remove(sid); + sprites->add( + jGL::Sprite(oTransform, region, tex, oRenderable.a), + sid, + oRenderable.priority + ); + } + else + { + sprites->updatePriority(sid, oRenderable.priority); + sprite.setAlpha(oRenderable.a); + sprite.setTextureRegion + ( + jGL::TextureRegion + ( + oSprite.tx, + oSprite.ty, + oSprite.lx, + oSprite.ly + ) + ); + } + } + else + { + sprites->add( + jGL::Sprite(oTransform, region, tex, oRenderable.a), + sid, + oRenderable.priority + ); + } + } + } } + } } \ No newline at end of file diff --git a/src/System/systemManager.cpp b/src/System/systemManager.cpp index cc69bc32..2e6f1175 100644 --- a/src/System/systemManager.cpp +++ b/src/System/systemManager.cpp @@ -4,16 +4,6 @@ namespace Hop::System { - - void SystemManager::objectFreed(Id i) - { - for (auto const& pair : systems) - { - auto const& system = pair.second; - system->objects.erase(i); - } - } - void SystemManager::objectSignatureChanged(Id i, Signature es) { for (auto const& pair : systems) diff --git a/tests/regression/assets/main.cpp b/tests/regression/assets/main.cpp index afe44d1c..e063512c 100644 --- a/tests/regression/assets/main.cpp +++ b/tests/regression/assets/main.cpp @@ -10,7 +10,7 @@ int main(int argc, char ** argv) jGL::DesktopDisplay display(glm::ivec2(1,1),"Test TextureAssetStore", conf); glewInit(); - std::unique_ptr jGLInstance = std::move(std::make_unique(display.getRes())); + std::shared_ptr jGLInstance = std::move(std::make_shared(display.getRes())); Hop::Util::Assets::TextureAssetStore textureStore(std::filesystem::path("resource"), jGLInstance); textureStore.scan(); for (auto tex : textureStore)