From 62886065056c8cb5fbc3e63d2a12937fc8f177e5 Mon Sep 17 00:00:00 2001 From: Shu Date: Wed, 20 Jan 2021 17:13:15 +0100 Subject: [PATCH] New year reboot Switch to FastNoise2 Hierarchy including rotate And many more --- CMakeLists.txt | 152 +- Doxyfile | 2 +- README.md | 4 +- TODO.md | 65 +- deps/FastNoise2/.fix-include-path.patch | 27 + deps/FastNoise2/.update.sh | 16 + deps/{mini-yaml => FastNoise2}/LICENSE | 2 +- deps/FastNoise2/include/FastNoise/FastNoise.h | 33 + .../include/FastNoise/FastNoiseMetadata.h | 331 ++ .../include/FastNoise/FastNoise_BuildList.inl | 130 + .../include/FastNoise/FastNoise_Config.h | 20 + .../FastNoise/Generators/BasicGenerators.h | 109 + .../FastNoise/Generators/BasicGenerators.inl | 96 + .../include/FastNoise/Generators/Blends.h | 198 ++ .../include/FastNoise/Generators/Blends.inl | 174 ++ .../include/FastNoise/Generators/Cellular.h | 104 + .../include/FastNoise/Generators/Cellular.inl | 655 ++++ .../include/FastNoise/Generators/DomainWarp.h | 37 + .../FastNoise/Generators/DomainWarp.inl | 181 ++ .../FastNoise/Generators/DomainWarpFractal.h | 26 + .../Generators/DomainWarpFractal.inl | 71 + .../include/FastNoise/Generators/Fractal.h | 103 + .../include/FastNoise/Generators/Fractal.inl | 128 + .../include/FastNoise/Generators/Generator.h | 149 + .../FastNoise/Generators/Generator.inl | 343 ++ .../include/FastNoise/Generators/Modifiers.h | 321 ++ .../FastNoise/Generators/Modifiers.inl | 277 ++ .../include/FastNoise/Generators/Perlin.h | 16 + .../include/FastNoise/Generators/Perlin.inl | 109 + .../include/FastNoise/Generators/Simplex.h | 27 + .../include/FastNoise/Generators/Simplex.inl | 373 +++ .../include/FastNoise/Generators/Utils.inl | 306 ++ .../include/FastNoise/Generators/Value.h | 16 + .../include/FastNoise/Generators/Value.inl | 88 + deps/FastNoise2/include/FastNoise/VecN.h | 83 + deps/FastNoise2/include/FastSIMD/FastSIMD.h | 52 + .../include/FastSIMD/FastSIMD_Config.h | 29 + .../include/FastSIMD/FunctionList.h | 821 +++++ deps/FastNoise2/include/FastSIMD/InlInclude.h | 10 + deps/FastNoise2/include/FastSIMD/TypeList.h | 37 + deps/FastNoise2/src/CMakeLists.txt | 95 + deps/FastNoise2/src/FastNoise/Base64.h | 127 + .../src/FastNoise/FastNoiseMetadata.cpp | 390 +++ .../FastNoise2/src/FastSIMD/Example/Example.h | 17 + .../src/FastSIMD/Example/Example.inl | 125 + deps/FastNoise2/src/FastSIMD/FastSIMD.cpp | 239 ++ .../src/FastSIMD/FastSIMD_BuildList.inl | 10 + .../src/FastSIMD/FastSIMD_Level_AVX2.cpp | 17 + .../src/FastSIMD/FastSIMD_Level_AVX512.cpp | 17 + .../src/FastSIMD/FastSIMD_Level_NEON.cpp | 7 + .../src/FastSIMD/FastSIMD_Level_SSE2.cpp | 7 + .../src/FastSIMD/FastSIMD_Level_SSE3.cpp | 7 + .../src/FastSIMD/FastSIMD_Level_SSE41.cpp | 7 + .../src/FastSIMD/FastSIMD_Level_SSE42.cpp | 7 + .../src/FastSIMD/FastSIMD_Level_SSSE3.cpp | 7 + .../src/FastSIMD/FastSIMD_Level_Scalar.cpp | 7 + deps/FastNoise2/src/FastSIMD/Internal/AVX.h | 448 +++ .../FastNoise2/src/FastSIMD/Internal/AVX512.h | 516 +++ deps/FastNoise2/src/FastSIMD/Internal/NEON.h | 424 +++ deps/FastNoise2/src/FastSIMD/Internal/SSE.h | 541 ++++ .../FastNoise2/src/FastSIMD/Internal/Scalar.h | 429 +++ .../src/FastSIMD/Internal/SourceBuilder.inl | 22 + .../src/FastSIMD/Internal/VecTools.h | 66 + deps/FastNoiseSIMD/ARM/cpu-features.c | 1292 -------- deps/FastNoiseSIMD/ARM/cpu-features.h | 318 -- deps/FastNoiseSIMD/FastNoiseSIMD.cpp | 556 ---- deps/FastNoiseSIMD/FastNoiseSIMD.h | 365 --- deps/FastNoiseSIMD/FastNoiseSIMD_avx2.cpp | 49 - deps/FastNoiseSIMD/FastNoiseSIMD_avx512.cpp | 53 - deps/FastNoiseSIMD/FastNoiseSIMD_internal.cpp | 2419 -------------- deps/FastNoiseSIMD/FastNoiseSIMD_internal.h | 77 - deps/FastNoiseSIMD/FastNoiseSIMD_neon.cpp | 38 - deps/FastNoiseSIMD/FastNoiseSIMD_sse2.cpp | 41 - deps/FastNoiseSIMD/FastNoiseSIMD_sse41.cpp | 41 - deps/FastNoiseSIMD/LICENSE | 21 - deps/imgui/imgui_stdlib.cpp | 76 + deps/imgui/imgui_stdlib.h | 22 + deps/libguarded/LICENSE | 25 - deps/libguarded/mutex_guarded.h | 198 -- deps/libguarded/shared_mutex_guarded.h | 234 -- deps/mini-yaml/README.md | 76 - deps/mini-yaml/Yaml.cpp | 2773 ----------------- deps/mini-yaml/Yaml.hpp | 656 ---- deps/picoquic/CMakeLists.txt | 2 +- deps/picoquic/picotls/CMakeLists.txt | 2 +- .../{shaders-src => shaders/src}/Color.frag | 0 .../{shaders-src => shaders/src}/Color.vert | 0 .../{shaders-src => shaders/src}/Sky.frag | 0 .../{shaders-src => shaders/src}/Sky.vert | 0 .../{shaders-src => shaders/src}/Tris.frag | 0 .../{shaders-src => shaders/src}/Tris.vert | 0 .../{shaders-src => shaders/src}/Voxel.frag | 0 .../{shaders-src => shaders/src}/Voxel.geom | 0 .../{shaders-src => shaders/src}/Voxel.vert | 0 .../{shaders-src => shaders/src}/compile.sh | 2 +- .../{shaders-src => shaders/src}/frag.spv | 0 .../shaders => shaders/vk}/Color.fs.spv | 0 .../shaders => shaders/vk}/Color.vs.spv | 0 .../shaders => shaders/vk}/Sky.fs.spv | 0 .../shaders => shaders/vk}/Sky.vs.spv | 0 .../shaders => shaders/vk}/Tris.fs.spv | 0 .../shaders => shaders/vk}/Tris.vs.spv | 0 .../shaders => shaders/vk}/Voxel.fs.spv | 0 .../shaders => shaders/vk}/Voxel.geo.fs.spv | 0 .../shaders => shaders/vk}/Voxel.geo.gs.spv | 0 .../vk}/Voxel.geo.ins.fs.spv | 0 .../vk}/Voxel.geo.ins.gs.spv | 0 .../vk}/Voxel.geo.ins.vs.spv | 0 .../shaders => shaders/vk}/Voxel.geo.vs.spv | 0 .../shaders => shaders/vk}/Voxel.ins.fs.spv | 0 .../shaders => shaders/vk}/Voxel.ins.vs.spv | 0 .../shaders => shaders/vk}/Voxel.vs.spv | 0 .../1024-realistic/Abstract_006_COLOR.jpg | 0 .../1024-realistic/Abstract_006_DISP.png | 0 .../1024-realistic/Abstract_006_HOS.png | 0 .../1024-realistic/Abstract_006_NORM.jpg | 0 .../1024-realistic/Abstract_006_OCC.jpg | 0 .../1024-realistic/Abstract_006_SPEC.jpg | 0 .../1024-realistic/Bark_005_HOS.png | 0 .../1024-realistic/Bark_005_baseColor.jpg | 0 .../1024-realistic/Bark_005_normal.jpg | 0 .../1024-realistic/Debug.cube.back.png | 0 .../1024-realistic/Debug.cube.bottom.png | 0 .../1024-realistic/Debug.cube.front.png | 0 .../1024-realistic/Debug.cube.left.png | 0 .../1024-realistic/Debug.cube.right.png | 0 .../1024-realistic/Debug.cube.top.png | 0 .../1024-realistic/Debug_COLOR.jpg | 0 .../1024-realistic/Debug_HOS.png | 0 .../1024-realistic/Debug_NORM.jpg | 0 .../1024-realistic/Dirt_003_COLOR.png | 0 .../1024-realistic/Dirt_003_DISP.png | 0 .../1024-realistic/Dirt_003_HOS.png | 0 .../1024-realistic/Dirt_003_NRM.png | 0 .../1024-realistic/Dirt_003_OCC.png | 0 .../1024-realistic/Dirt_003_SPEC.png | 0 .../1024-realistic/Grass_001_COLOR.jpg | 0 .../1024-realistic/Grass_001_DISP.png | 0 .../1024-realistic/Grass_001_HOS.jpg | 0 .../1024-realistic/Grass_001_NORM.jpg | 0 .../1024-realistic/Grass_001_OCC.jpg | 0 .../1024-realistic/Grass_001_ROUGH.jpg | 0 .../Ground_Forest_002_ambientOcclusion.jpg | 0 .../Ground_Forest_002_baseColor.jpg | 0 .../Ground_Forest_002_height.png | 0 .../1024-realistic/Ground_Forest_002_hos.jpg | 0 .../Ground_Forest_002_normal.jpg | 0 .../Ground_Forest_002_roughness.jpg | 0 .../Ground_Forest_003_ROUGH.jpg | 0 .../Ground_Forest_003_ambientOcclusion.jpg | 0 .../Ground_Forest_003_baseColor.jpg | 0 .../Ground_Forest_003_height.png | 0 .../1024-realistic/Ground_Forest_003_hos.jpg | 0 .../Ground_Forest_003_normal.jpg | 0 .../1024-realistic/Rough_rock_019_COLOR.jpg | 0 .../1024-realistic/Rough_rock_019_DISP.jpg | 0 .../1024-realistic/Rough_rock_019_HOS.jpg | 0 .../1024-realistic/Rough_rock_019_NRM.jpg | 0 .../1024-realistic/Rough_rock_019_OCC.jpg | 0 .../1024-realistic/Rough_rock_019_SPEC.jpg | 0 .../Sand_005_ambientOcclusion.jpg | 0 .../1024-realistic/Sand_005_baseColor.jpg | 0 .../1024-realistic/Sand_005_height.png | 0 .../1024-realistic/Sand_005_hos.jpg | 0 .../1024-realistic/Sand_005_normal.jpg | 0 .../1024-realistic/Sand_005_roughness.jpg | 0 .../1024-realistic/Seaside_rocks_01_1K_AO.png | 0 .../Seaside_rocks_01_1K_Base_Color.png | 0 .../Seaside_rocks_01_1K_HOS.png | 0 .../Seaside_rocks_01_1K_Height.png | 0 .../Seaside_rocks_01_1K_Normal.png | 0 .../Seaside_rocks_01_1K_Roughness.png | 0 .../1024-realistic/Space_orange.cube.back.png | 0 .../Space_orange.cube.bottom.png | 0 .../Space_orange.cube.front.png | 0 .../1024-realistic/Space_orange.cube.left.png | 0 .../Space_orange.cube.right.png | 0 .../1024-realistic/Space_orange.cube.top.png | 0 .../1024-realistic/Space_tray.cube.back.png | 0 .../1024-realistic/Space_tray.cube.bottom.png | 0 .../1024-realistic/Space_tray.cube.front.png | 0 .../1024-realistic/Space_tray.cube.left.png | 0 .../1024-realistic/Space_tray.cube.right.png | 0 .../1024-realistic/Space_tray.cube.top.png | 0 .../1024-realistic/Stone_Path_004_HOS.jpg | 0 .../Stone_Path_004_ambientOcclusion.jpg | 0 .../Stone_Path_004_baseColor.jpg | 0 .../1024-realistic/Stone_Path_004_height.png | 0 .../1024-realistic/Stone_Path_004_normal.jpg | 0 .../Stone_Path_004_roughness.jpg | 0 .../1024-realistic/Stone_Wall_008_COLOR.jpg | 0 .../1024-realistic/Stone_Wall_008_DISP.png | 0 .../1024-realistic/Stone_Wall_008_HOS.jpg | 0 .../1024-realistic/Stone_Wall_008_NORM.jpg | 0 .../1024-realistic/Stone_Wall_008_OCC.jpg | 0 .../1024-realistic/Stone_Wall_008_ROUGH.jpg | 0 .../1024-realistic/Water_002_COLOR.png | 0 .../1024-realistic/Water_002_DISP.png | 0 .../1024-realistic/Water_002_HOS.jpg | 0 .../1024-realistic/Water_002_NORM.jpg | 0 .../1024-realistic/Water_002_OCC.jpg | 0 .../1024-realistic/Water_002_ROUGH.jpg | 0 .../1024-realistic/index.txt | 0 resource/{textures-src => textures}/Aim.png | 0 .../{textures-src => textures}/compile.sh | 0 resource/{textures-src => textures}/merge.py | 0 src/client.cpp | 19 +- src/client/Client.cpp | 519 +-- src/client/Client.hpp | 14 +- src/client/InputMap.cpp | 54 - src/client/config.hpp | 27 +- src/client/contouring/Abstract.hpp | 71 +- src/client/contouring/FlatDualMC.cpp | 434 +-- src/client/contouring/FlatDualMC.hpp | 75 +- src/client/contouring/dualmc.h | 6 +- src/client/contouring/notifier.hpp | 22 + src/client/contouring/surrounding.cpp | 22 +- src/client/contouring/surrounding.hpp | 27 +- src/client/control/Camera.cpp | 16 +- src/client/control/Camera.hpp | 14 +- src/client/control/Controllable.cpp | 87 +- src/client/control/Controllable.hpp | 12 +- src/client/control/InputMap.cpp | 80 + src/client/{ => control}/InputMap.hpp | 25 +- src/client/net/Client.cpp | 25 +- src/client/net/Client.hpp | 7 +- src/client/render/Renderer.hpp | 16 +- src/client/render/UI.cpp | 521 ++-- src/client/render/UI.hpp | 9 +- src/client/{ => render}/Window.cpp | 41 +- src/client/{ => render}/Window.hpp | 9 +- src/client/render/api/Models.hpp | 1 + src/client/render/api/common.hpp | 2 +- src/client/render/gl/pass/WorldProgram.cpp | 21 - src/client/render/gl/pass/WorldProgram.hpp | 20 - src/client/render/{ => impl}/gl/Renderer.cpp | 49 +- src/client/render/{ => impl}/gl/Renderer.hpp | 13 +- src/client/render/{ => impl}/gl/UI.cpp | 0 src/client/render/{ => impl}/gl/UI.hpp | 2 +- .../render/{ => impl}/gl/api/Images.cpp | 2 +- .../render/{ => impl}/gl/api/Images.hpp | 2 +- .../render/{ => impl}/gl/api/Models.cpp | 2 +- .../render/{ => impl}/gl/api/Models.hpp | 5 +- .../{ => impl}/gl/pass/ColorProgram.cpp | 4 +- .../{ => impl}/gl/pass/ColorProgram.hpp | 4 +- .../{ => impl}/gl/pass/EntityProgram.cpp | 8 +- .../{ => impl}/gl/pass/EntityProgram.hpp | 3 +- .../render/{ => impl}/gl/pass/Program.cpp | 31 +- .../render/{ => impl}/gl/pass/Program.hpp | 2 +- .../render/{ => impl}/gl/pass/Shader.cpp | 4 +- .../render/{ => impl}/gl/pass/Shader.hpp | 2 +- .../render/{ => impl}/gl/pass/SkyProgram.cpp | 6 +- .../render/{ => impl}/gl/pass/SkyProgram.hpp | 6 +- .../render/impl/gl/pass/TerrainProgram.cpp | 27 + .../render/impl/gl/pass/TerrainProgram.hpp | 22 + .../render/{ => impl}/gl/pass/UIProgram.cpp | 2 +- .../render/{ => impl}/gl/pass/UIProgram.hpp | 2 +- .../{ => impl}/gl/pass/VoxelProgram.cpp | 6 +- .../{ => impl}/gl/pass/VoxelProgram.hpp | 6 +- src/client/render/{ => impl}/vk/Allocator.cpp | 0 src/client/render/{ => impl}/vk/Allocator.hpp | 2 +- .../render/{ => impl}/vk/CommandCenter.cpp | 34 +- .../render/{ => impl}/vk/CommandCenter.hpp | 5 +- .../{ => impl}/vk/PhysicalDeviceInfo.cpp | 0 .../{ => impl}/vk/PhysicalDeviceInfo.hpp | 2 +- src/client/render/{ => impl}/vk/Pipeline.cpp | 134 +- src/client/render/{ => impl}/vk/Pipeline.hpp | 12 +- src/client/render/{ => impl}/vk/Renderer.cpp | 47 +- src/client/render/{ => impl}/vk/Renderer.hpp | 9 +- src/client/render/{ => impl}/vk/SwapChain.cpp | 0 src/client/render/{ => impl}/vk/SwapChain.hpp | 0 src/client/render/{ => impl}/vk/UI.cpp | 2 +- src/client/render/{ => impl}/vk/UI.hpp | 2 +- .../render/{ => impl}/vk/api/Buffers.cpp | 2 +- .../render/{ => impl}/vk/api/Buffers.hpp | 2 +- .../render/{ => impl}/vk/api/Images.cpp | 2 +- .../render/{ => impl}/vk/api/Images.hpp | 2 +- .../render/{ => impl}/vk/api/Memory.hpp | 0 .../render/{ => impl}/vk/api/Models.cpp | 0 .../render/{ => impl}/vk/api/Models.hpp | 2 +- src/client/render/{ => impl}/vk/forward.hpp | 3 + src/client/render/index.cpp | 23 +- src/client/{ => render}/logo.cxx | 0 src/client/srvContainer.hpp | 107 + src/client/state.hpp | 23 +- src/client/world/Area.hpp | 10 +- src/client/world/Chunk.hpp | 39 +- src/client/world/DistantUniverse.cpp | 627 ++-- src/client/world/DistantUniverse.hpp | 51 +- src/client/world/LocalUniverse.cpp | 140 +- src/client/world/LocalUniverse.hpp | 18 +- src/client/world/Universe.cpp | 25 + src/client/world/Universe.hpp | 30 +- src/client/world/index.cpp | 15 +- src/client/world/index.hpp | 4 +- src/core/data/circular_buffer.hpp | 28 - src/core/data/file.hpp | 42 - src/core/data/generational.hpp | 315 -- src/core/data/glm.cpp | 27 - src/core/data/glm.hpp | 49 - src/core/data/math.hpp | 85 - src/core/data/toml.cpp | 2 - src/core/generational/core.hpp | 131 + src/core/generational/hierarchy.hpp | 206 ++ src/core/generational/map.hpp | 51 + src/core/generational/vector.hpp | 342 ++ src/core/geometry/Box.hpp | 231 +- src/core/geometry/Faces.hpp | 10 +- src/core/geometry/Frustum.hpp | 8 +- src/core/geometry/IBox.hpp | 49 - src/core/geometry/Ray.hpp | 39 +- src/core/geometry/Shapes.hpp | 81 +- src/core/geometry/glm.hpp | 22 + src/core/geometry/math.hpp | 111 + src/core/{data/mem.hpp => memory.hpp} | 156 +- src/core/net/Context.cpp | 17 +- src/core/net/Context.hpp | 23 +- src/core/net/io.hpp | 56 - src/core/net/{data.hpp => protocol.hpp} | 97 +- src/core/{config.hpp => options_full.hpp} | 12 +- ...standalone_config.hpp => options_part.hpp} | 26 +- src/core/queue/circular_buffer.hpp | 38 + .../{data => queue}/safe_priority_queue.hpp | 20 +- src/core/{data => queue}/safe_queue.hpp | 21 +- .../{data => queue}/safe_unique_queue.hpp | 0 src/core/queue/sorted_queue.hpp | 68 + src/core/{data => queue}/unique_queue.hpp | 0 src/core/server_handle.hpp | 18 - src/core/utils/defer.hpp | 25 + src/core/{ => utils}/flags.hpp | 7 +- src/core/utils/io.hpp | 72 + src/core/utils/logger.hpp | 16 +- src/core/utils/mutex.hpp | 480 +++ src/core/utils/toml.cpp | 2 + src/core/utils/toml.hpp | 3 + src/core/utils/zctx.hpp | 5 +- src/core/world/Area.cpp | 23 + src/core/world/Area.hpp | 161 +- src/core/world/Chunk.cpp | 3 +- src/core/world/Chunk.hpp | 11 +- src/core/world/EdittableChunk.cpp | 38 +- src/core/world/EdittableChunk.hpp | 56 +- src/core/world/Elements.cpp | 105 + src/core/world/Elements.hpp | 115 + src/core/world/Node.cpp | 15 + src/core/world/Node.hpp | 181 ++ src/core/world/Registry.cpp | 191 ++ src/core/world/Registry.hpp | 98 + src/core/world/Universe.cpp | 204 +- src/core/world/Universe.hpp | 120 +- src/core/world/Voxel.hpp | 42 +- src/core/world/actions.hpp | 26 +- src/core/world/forward.h | 11 +- src/core/world/generator/Abstract.hpp | 31 + src/core/world/generator/Cave.hpp | 40 + src/core/world/generator/Noise.hpp | 87 + src/core/world/iterators.hpp | 125 +- src/core/world/materials.hpp | 44 - src/core/world/models.hpp | 8 - src/core/world/module_types.hpp | 129 + src/core/world/position.h | 55 - src/core/world/raycast.hpp | 49 - src/core/world/server_handle.hpp | 27 + src/core/world/units.hpp | 49 + src/main.cpp | 58 +- src/modules/core/Core.cpp | 49 + src/modules/core/Core.hpp | 36 + src/modules/core/PlanetGenerator.hpp | 103 + src/server.cpp | 13 +- src/server/Server.cpp | 502 ++- src/server/Server.hpp | 31 +- src/server/config.hpp | 47 +- src/server/net/Server.cpp | 41 +- src/server/net/Server.hpp | 14 +- src/server/world/Area.cpp | 19 +- src/server/world/Area.hpp | 33 +- src/server/world/Chunk.cpp | 9 +- src/server/world/Chunk.hpp | 32 +- src/server/world/Elements.cpp | 156 + src/server/world/Elements.hpp | 36 + src/server/world/Noise.hpp | 126 - src/server/world/Region.cpp | 264 ++ .../world/{region/Memory.hpp => Region.hpp} | 32 +- src/server/world/Serializer.cpp | 73 + src/server/world/Serializer.hpp | 38 + src/server/world/SharedChunk.hpp | 33 + src/server/world/SharedParts.hpp | 29 - src/server/world/SharedUniverse.cpp | 67 - src/server/world/SharedUniverse.hpp | 29 - src/server/world/StandaloneUniverse.cpp | 6 - src/server/world/StandaloneUniverse.hpp | 11 - src/server/world/Universe.cpp | 1117 +++---- src/server/world/Universe.hpp | 205 +- src/server/world/client.hpp | 31 + src/server/world/generator.hpp | 164 - src/server/world/region/File.cpp | 195 -- src/server/world/region/File.hpp | 65 - src/server/world/region/Memory.cpp | 183 -- src/server/world/region/index.hpp | 7 - src/tools/generate_models.cpp | 4 +- 400 files changed, 17217 insertions(+), 14045 deletions(-) create mode 100644 deps/FastNoise2/.fix-include-path.patch create mode 100755 deps/FastNoise2/.update.sh rename deps/{mini-yaml => FastNoise2}/LICENSE (96%) create mode 100644 deps/FastNoise2/include/FastNoise/FastNoise.h create mode 100644 deps/FastNoise2/include/FastNoise/FastNoiseMetadata.h create mode 100644 deps/FastNoise2/include/FastNoise/FastNoise_BuildList.inl create mode 100644 deps/FastNoise2/include/FastNoise/FastNoise_Config.h create mode 100644 deps/FastNoise2/include/FastNoise/Generators/BasicGenerators.h create mode 100644 deps/FastNoise2/include/FastNoise/Generators/BasicGenerators.inl create mode 100644 deps/FastNoise2/include/FastNoise/Generators/Blends.h create mode 100644 deps/FastNoise2/include/FastNoise/Generators/Blends.inl create mode 100644 deps/FastNoise2/include/FastNoise/Generators/Cellular.h create mode 100644 deps/FastNoise2/include/FastNoise/Generators/Cellular.inl create mode 100644 deps/FastNoise2/include/FastNoise/Generators/DomainWarp.h create mode 100644 deps/FastNoise2/include/FastNoise/Generators/DomainWarp.inl create mode 100644 deps/FastNoise2/include/FastNoise/Generators/DomainWarpFractal.h create mode 100644 deps/FastNoise2/include/FastNoise/Generators/DomainWarpFractal.inl create mode 100644 deps/FastNoise2/include/FastNoise/Generators/Fractal.h create mode 100644 deps/FastNoise2/include/FastNoise/Generators/Fractal.inl create mode 100644 deps/FastNoise2/include/FastNoise/Generators/Generator.h create mode 100644 deps/FastNoise2/include/FastNoise/Generators/Generator.inl create mode 100644 deps/FastNoise2/include/FastNoise/Generators/Modifiers.h create mode 100644 deps/FastNoise2/include/FastNoise/Generators/Modifiers.inl create mode 100644 deps/FastNoise2/include/FastNoise/Generators/Perlin.h create mode 100644 deps/FastNoise2/include/FastNoise/Generators/Perlin.inl create mode 100644 deps/FastNoise2/include/FastNoise/Generators/Simplex.h create mode 100644 deps/FastNoise2/include/FastNoise/Generators/Simplex.inl create mode 100644 deps/FastNoise2/include/FastNoise/Generators/Utils.inl create mode 100644 deps/FastNoise2/include/FastNoise/Generators/Value.h create mode 100644 deps/FastNoise2/include/FastNoise/Generators/Value.inl create mode 100644 deps/FastNoise2/include/FastNoise/VecN.h create mode 100644 deps/FastNoise2/include/FastSIMD/FastSIMD.h create mode 100644 deps/FastNoise2/include/FastSIMD/FastSIMD_Config.h create mode 100644 deps/FastNoise2/include/FastSIMD/FunctionList.h create mode 100644 deps/FastNoise2/include/FastSIMD/InlInclude.h create mode 100644 deps/FastNoise2/include/FastSIMD/TypeList.h create mode 100644 deps/FastNoise2/src/CMakeLists.txt create mode 100644 deps/FastNoise2/src/FastNoise/Base64.h create mode 100644 deps/FastNoise2/src/FastNoise/FastNoiseMetadata.cpp create mode 100644 deps/FastNoise2/src/FastSIMD/Example/Example.h create mode 100644 deps/FastNoise2/src/FastSIMD/Example/Example.inl create mode 100644 deps/FastNoise2/src/FastSIMD/FastSIMD.cpp create mode 100644 deps/FastNoise2/src/FastSIMD/FastSIMD_BuildList.inl create mode 100644 deps/FastNoise2/src/FastSIMD/FastSIMD_Level_AVX2.cpp create mode 100644 deps/FastNoise2/src/FastSIMD/FastSIMD_Level_AVX512.cpp create mode 100644 deps/FastNoise2/src/FastSIMD/FastSIMD_Level_NEON.cpp create mode 100644 deps/FastNoise2/src/FastSIMD/FastSIMD_Level_SSE2.cpp create mode 100644 deps/FastNoise2/src/FastSIMD/FastSIMD_Level_SSE3.cpp create mode 100644 deps/FastNoise2/src/FastSIMD/FastSIMD_Level_SSE41.cpp create mode 100644 deps/FastNoise2/src/FastSIMD/FastSIMD_Level_SSE42.cpp create mode 100644 deps/FastNoise2/src/FastSIMD/FastSIMD_Level_SSSE3.cpp create mode 100644 deps/FastNoise2/src/FastSIMD/FastSIMD_Level_Scalar.cpp create mode 100644 deps/FastNoise2/src/FastSIMD/Internal/AVX.h create mode 100644 deps/FastNoise2/src/FastSIMD/Internal/AVX512.h create mode 100644 deps/FastNoise2/src/FastSIMD/Internal/NEON.h create mode 100644 deps/FastNoise2/src/FastSIMD/Internal/SSE.h create mode 100644 deps/FastNoise2/src/FastSIMD/Internal/Scalar.h create mode 100644 deps/FastNoise2/src/FastSIMD/Internal/SourceBuilder.inl create mode 100644 deps/FastNoise2/src/FastSIMD/Internal/VecTools.h delete mode 100644 deps/FastNoiseSIMD/ARM/cpu-features.c delete mode 100644 deps/FastNoiseSIMD/ARM/cpu-features.h delete mode 100644 deps/FastNoiseSIMD/FastNoiseSIMD.cpp delete mode 100644 deps/FastNoiseSIMD/FastNoiseSIMD.h delete mode 100644 deps/FastNoiseSIMD/FastNoiseSIMD_avx2.cpp delete mode 100644 deps/FastNoiseSIMD/FastNoiseSIMD_avx512.cpp delete mode 100644 deps/FastNoiseSIMD/FastNoiseSIMD_internal.cpp delete mode 100644 deps/FastNoiseSIMD/FastNoiseSIMD_internal.h delete mode 100644 deps/FastNoiseSIMD/FastNoiseSIMD_neon.cpp delete mode 100644 deps/FastNoiseSIMD/FastNoiseSIMD_sse2.cpp delete mode 100644 deps/FastNoiseSIMD/FastNoiseSIMD_sse41.cpp delete mode 100644 deps/FastNoiseSIMD/LICENSE create mode 100644 deps/imgui/imgui_stdlib.cpp create mode 100644 deps/imgui/imgui_stdlib.h delete mode 100644 deps/libguarded/LICENSE delete mode 100644 deps/libguarded/mutex_guarded.h delete mode 100644 deps/libguarded/shared_mutex_guarded.h delete mode 100644 deps/mini-yaml/README.md delete mode 100644 deps/mini-yaml/Yaml.cpp delete mode 100644 deps/mini-yaml/Yaml.hpp rename resource/{shaders-src => shaders/src}/Color.frag (100%) rename resource/{shaders-src => shaders/src}/Color.vert (100%) rename resource/{shaders-src => shaders/src}/Sky.frag (100%) rename resource/{shaders-src => shaders/src}/Sky.vert (100%) rename resource/{shaders-src => shaders/src}/Tris.frag (100%) rename resource/{shaders-src => shaders/src}/Tris.vert (100%) rename resource/{shaders-src => shaders/src}/Voxel.frag (100%) rename resource/{shaders-src => shaders/src}/Voxel.geom (100%) rename resource/{shaders-src => shaders/src}/Voxel.vert (100%) rename resource/{shaders-src => shaders/src}/compile.sh (95%) rename resource/{shaders-src => shaders/src}/frag.spv (100%) rename resource/{content/shaders => shaders/vk}/Color.fs.spv (100%) rename resource/{content/shaders => shaders/vk}/Color.vs.spv (100%) rename resource/{content/shaders => shaders/vk}/Sky.fs.spv (100%) rename resource/{content/shaders => shaders/vk}/Sky.vs.spv (100%) rename resource/{content/shaders => shaders/vk}/Tris.fs.spv (100%) rename resource/{content/shaders => shaders/vk}/Tris.vs.spv (100%) rename resource/{content/shaders => shaders/vk}/Voxel.fs.spv (100%) rename resource/{content/shaders => shaders/vk}/Voxel.geo.fs.spv (100%) rename resource/{content/shaders => shaders/vk}/Voxel.geo.gs.spv (100%) rename resource/{content/shaders => shaders/vk}/Voxel.geo.ins.fs.spv (100%) rename resource/{content/shaders => shaders/vk}/Voxel.geo.ins.gs.spv (100%) rename resource/{content/shaders => shaders/vk}/Voxel.geo.ins.vs.spv (100%) rename resource/{content/shaders => shaders/vk}/Voxel.geo.vs.spv (100%) rename resource/{content/shaders => shaders/vk}/Voxel.ins.fs.spv (100%) rename resource/{content/shaders => shaders/vk}/Voxel.ins.vs.spv (100%) rename resource/{content/shaders => shaders/vk}/Voxel.vs.spv (100%) rename resource/{textures-src => textures}/1024-realistic/Abstract_006_COLOR.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Abstract_006_DISP.png (100%) rename resource/{textures-src => textures}/1024-realistic/Abstract_006_HOS.png (100%) rename resource/{textures-src => textures}/1024-realistic/Abstract_006_NORM.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Abstract_006_OCC.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Abstract_006_SPEC.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Bark_005_HOS.png (100%) rename resource/{textures-src => textures}/1024-realistic/Bark_005_baseColor.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Bark_005_normal.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Debug.cube.back.png (100%) rename resource/{textures-src => textures}/1024-realistic/Debug.cube.bottom.png (100%) rename resource/{textures-src => textures}/1024-realistic/Debug.cube.front.png (100%) rename resource/{textures-src => textures}/1024-realistic/Debug.cube.left.png (100%) rename resource/{textures-src => textures}/1024-realistic/Debug.cube.right.png (100%) rename resource/{textures-src => textures}/1024-realistic/Debug.cube.top.png (100%) rename resource/{textures-src => textures}/1024-realistic/Debug_COLOR.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Debug_HOS.png (100%) rename resource/{textures-src => textures}/1024-realistic/Debug_NORM.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Dirt_003_COLOR.png (100%) rename resource/{textures-src => textures}/1024-realistic/Dirt_003_DISP.png (100%) rename resource/{textures-src => textures}/1024-realistic/Dirt_003_HOS.png (100%) rename resource/{textures-src => textures}/1024-realistic/Dirt_003_NRM.png (100%) rename resource/{textures-src => textures}/1024-realistic/Dirt_003_OCC.png (100%) rename resource/{textures-src => textures}/1024-realistic/Dirt_003_SPEC.png (100%) rename resource/{textures-src => textures}/1024-realistic/Grass_001_COLOR.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Grass_001_DISP.png (100%) rename resource/{textures-src => textures}/1024-realistic/Grass_001_HOS.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Grass_001_NORM.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Grass_001_OCC.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Grass_001_ROUGH.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Ground_Forest_002_ambientOcclusion.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Ground_Forest_002_baseColor.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Ground_Forest_002_height.png (100%) rename resource/{textures-src => textures}/1024-realistic/Ground_Forest_002_hos.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Ground_Forest_002_normal.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Ground_Forest_002_roughness.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Ground_Forest_003_ROUGH.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Ground_Forest_003_ambientOcclusion.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Ground_Forest_003_baseColor.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Ground_Forest_003_height.png (100%) rename resource/{textures-src => textures}/1024-realistic/Ground_Forest_003_hos.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Ground_Forest_003_normal.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Rough_rock_019_COLOR.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Rough_rock_019_DISP.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Rough_rock_019_HOS.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Rough_rock_019_NRM.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Rough_rock_019_OCC.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Rough_rock_019_SPEC.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Sand_005_ambientOcclusion.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Sand_005_baseColor.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Sand_005_height.png (100%) rename resource/{textures-src => textures}/1024-realistic/Sand_005_hos.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Sand_005_normal.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Sand_005_roughness.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Seaside_rocks_01_1K_AO.png (100%) rename resource/{textures-src => textures}/1024-realistic/Seaside_rocks_01_1K_Base_Color.png (100%) rename resource/{textures-src => textures}/1024-realistic/Seaside_rocks_01_1K_HOS.png (100%) rename resource/{textures-src => textures}/1024-realistic/Seaside_rocks_01_1K_Height.png (100%) rename resource/{textures-src => textures}/1024-realistic/Seaside_rocks_01_1K_Normal.png (100%) rename resource/{textures-src => textures}/1024-realistic/Seaside_rocks_01_1K_Roughness.png (100%) rename resource/{textures-src => textures}/1024-realistic/Space_orange.cube.back.png (100%) rename resource/{textures-src => textures}/1024-realistic/Space_orange.cube.bottom.png (100%) rename resource/{textures-src => textures}/1024-realistic/Space_orange.cube.front.png (100%) rename resource/{textures-src => textures}/1024-realistic/Space_orange.cube.left.png (100%) rename resource/{textures-src => textures}/1024-realistic/Space_orange.cube.right.png (100%) rename resource/{textures-src => textures}/1024-realistic/Space_orange.cube.top.png (100%) rename resource/{textures-src => textures}/1024-realistic/Space_tray.cube.back.png (100%) rename resource/{textures-src => textures}/1024-realistic/Space_tray.cube.bottom.png (100%) rename resource/{textures-src => textures}/1024-realistic/Space_tray.cube.front.png (100%) rename resource/{textures-src => textures}/1024-realistic/Space_tray.cube.left.png (100%) rename resource/{textures-src => textures}/1024-realistic/Space_tray.cube.right.png (100%) rename resource/{textures-src => textures}/1024-realistic/Space_tray.cube.top.png (100%) rename resource/{textures-src => textures}/1024-realistic/Stone_Path_004_HOS.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Stone_Path_004_ambientOcclusion.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Stone_Path_004_baseColor.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Stone_Path_004_height.png (100%) rename resource/{textures-src => textures}/1024-realistic/Stone_Path_004_normal.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Stone_Path_004_roughness.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Stone_Wall_008_COLOR.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Stone_Wall_008_DISP.png (100%) rename resource/{textures-src => textures}/1024-realistic/Stone_Wall_008_HOS.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Stone_Wall_008_NORM.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Stone_Wall_008_OCC.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Stone_Wall_008_ROUGH.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Water_002_COLOR.png (100%) rename resource/{textures-src => textures}/1024-realistic/Water_002_DISP.png (100%) rename resource/{textures-src => textures}/1024-realistic/Water_002_HOS.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Water_002_NORM.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Water_002_OCC.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/Water_002_ROUGH.jpg (100%) rename resource/{textures-src => textures}/1024-realistic/index.txt (100%) rename resource/{textures-src => textures}/Aim.png (100%) rename resource/{textures-src => textures}/compile.sh (100%) rename resource/{textures-src => textures}/merge.py (100%) delete mode 100644 src/client/InputMap.cpp create mode 100644 src/client/contouring/notifier.hpp create mode 100644 src/client/control/InputMap.cpp rename src/client/{ => control}/InputMap.hpp (56%) rename src/client/{ => render}/Window.cpp (75%) rename src/client/{ => render}/Window.hpp (91%) delete mode 100644 src/client/render/gl/pass/WorldProgram.cpp delete mode 100644 src/client/render/gl/pass/WorldProgram.hpp rename src/client/render/{ => impl}/gl/Renderer.cpp (76%) rename src/client/render/{ => impl}/gl/Renderer.hpp (82%) rename src/client/render/{ => impl}/gl/UI.cpp (100%) rename src/client/render/{ => impl}/gl/UI.hpp (94%) rename src/client/render/{ => impl}/gl/api/Images.cpp (99%) rename src/client/render/{ => impl}/gl/api/Images.hpp (96%) rename src/client/render/{ => impl}/gl/api/Models.cpp (99%) rename src/client/render/{ => impl}/gl/api/Models.hpp (91%) rename src/client/render/{ => impl}/gl/pass/ColorProgram.cpp (86%) rename src/client/render/{ => impl}/gl/pass/ColorProgram.hpp (77%) rename src/client/render/{ => impl}/gl/pass/EntityProgram.cpp (94%) rename src/client/render/{ => impl}/gl/pass/EntityProgram.hpp (89%) rename src/client/render/{ => impl}/gl/pass/Program.cpp (53%) rename src/client/render/{ => impl}/gl/pass/Program.hpp (95%) rename src/client/render/{ => impl}/gl/pass/Shader.cpp (95%) rename src/client/render/{ => impl}/gl/pass/Shader.hpp (96%) rename src/client/render/{ => impl}/gl/pass/SkyProgram.cpp (90%) rename src/client/render/{ => impl}/gl/pass/SkyProgram.hpp (85%) create mode 100644 src/client/render/impl/gl/pass/TerrainProgram.cpp create mode 100644 src/client/render/impl/gl/pass/TerrainProgram.hpp rename src/client/render/{ => impl}/gl/pass/UIProgram.cpp (94%) rename src/client/render/{ => impl}/gl/pass/UIProgram.hpp (91%) rename src/client/render/{ => impl}/gl/pass/VoxelProgram.cpp (95%) rename src/client/render/{ => impl}/gl/pass/VoxelProgram.hpp (89%) rename src/client/render/{ => impl}/vk/Allocator.cpp (100%) rename src/client/render/{ => impl}/vk/Allocator.hpp (98%) rename src/client/render/{ => impl}/vk/CommandCenter.cpp (91%) rename src/client/render/{ => impl}/vk/CommandCenter.hpp (89%) rename src/client/render/{ => impl}/vk/PhysicalDeviceInfo.cpp (100%) rename src/client/render/{ => impl}/vk/PhysicalDeviceInfo.hpp (98%) rename src/client/render/{ => impl}/vk/Pipeline.cpp (83%) rename src/client/render/{ => impl}/vk/Pipeline.hpp (78%) rename src/client/render/{ => impl}/vk/Renderer.cpp (93%) rename src/client/render/{ => impl}/vk/Renderer.hpp (85%) rename src/client/render/{ => impl}/vk/SwapChain.cpp (100%) rename src/client/render/{ => impl}/vk/SwapChain.hpp (100%) rename src/client/render/{ => impl}/vk/UI.cpp (98%) rename src/client/render/{ => impl}/vk/UI.hpp (95%) rename src/client/render/{ => impl}/vk/api/Buffers.cpp (99%) rename src/client/render/{ => impl}/vk/api/Buffers.hpp (98%) rename src/client/render/{ => impl}/vk/api/Images.cpp (99%) rename src/client/render/{ => impl}/vk/api/Images.hpp (98%) rename src/client/render/{ => impl}/vk/api/Memory.hpp (100%) rename src/client/render/{ => impl}/vk/api/Models.cpp (100%) rename src/client/render/{ => impl}/vk/api/Models.hpp (99%) rename src/client/render/{ => impl}/vk/forward.hpp (78%) rename src/client/{ => render}/logo.cxx (100%) create mode 100644 src/client/srvContainer.hpp delete mode 100644 src/core/data/circular_buffer.hpp delete mode 100644 src/core/data/file.hpp delete mode 100644 src/core/data/generational.hpp delete mode 100644 src/core/data/glm.cpp delete mode 100644 src/core/data/glm.hpp delete mode 100644 src/core/data/math.hpp delete mode 100644 src/core/data/toml.cpp create mode 100644 src/core/generational/core.hpp create mode 100644 src/core/generational/hierarchy.hpp create mode 100644 src/core/generational/map.hpp create mode 100644 src/core/generational/vector.hpp delete mode 100644 src/core/geometry/IBox.hpp create mode 100644 src/core/geometry/glm.hpp create mode 100644 src/core/geometry/math.hpp rename src/core/{data/mem.hpp => memory.hpp} (59%) delete mode 100644 src/core/net/io.hpp rename src/core/net/{data.hpp => protocol.hpp} (64%) rename src/core/{config.hpp => options_full.hpp} (86%) rename src/core/{standalone_config.hpp => options_part.hpp} (61%) create mode 100644 src/core/queue/circular_buffer.hpp rename src/core/{data => queue}/safe_priority_queue.hpp (85%) rename src/core/{data => queue}/safe_queue.hpp (77%) rename src/core/{data => queue}/safe_unique_queue.hpp (100%) create mode 100644 src/core/queue/sorted_queue.hpp rename src/core/{data => queue}/unique_queue.hpp (100%) delete mode 100644 src/core/server_handle.hpp create mode 100644 src/core/utils/defer.hpp rename src/core/{ => utils}/flags.hpp (77%) create mode 100644 src/core/utils/io.hpp create mode 100644 src/core/utils/mutex.hpp create mode 100644 src/core/utils/toml.cpp create mode 100644 src/core/utils/toml.hpp create mode 100644 src/core/world/Area.cpp create mode 100644 src/core/world/Elements.cpp create mode 100644 src/core/world/Elements.hpp create mode 100644 src/core/world/Node.cpp create mode 100644 src/core/world/Node.hpp create mode 100644 src/core/world/Registry.cpp create mode 100644 src/core/world/Registry.hpp create mode 100644 src/core/world/generator/Abstract.hpp create mode 100644 src/core/world/generator/Cave.hpp create mode 100644 src/core/world/generator/Noise.hpp delete mode 100644 src/core/world/materials.hpp delete mode 100644 src/core/world/models.hpp create mode 100644 src/core/world/module_types.hpp delete mode 100644 src/core/world/position.h delete mode 100644 src/core/world/raycast.hpp create mode 100644 src/core/world/server_handle.hpp create mode 100644 src/core/world/units.hpp create mode 100644 src/modules/core/Core.cpp create mode 100644 src/modules/core/Core.hpp create mode 100644 src/modules/core/PlanetGenerator.hpp create mode 100644 src/server/world/Elements.cpp create mode 100644 src/server/world/Elements.hpp delete mode 100644 src/server/world/Noise.hpp create mode 100644 src/server/world/Region.cpp rename src/server/world/{region/Memory.hpp => Region.hpp} (53%) create mode 100644 src/server/world/Serializer.cpp create mode 100644 src/server/world/Serializer.hpp create mode 100644 src/server/world/SharedChunk.hpp delete mode 100644 src/server/world/SharedParts.hpp delete mode 100644 src/server/world/SharedUniverse.cpp delete mode 100644 src/server/world/SharedUniverse.hpp delete mode 100644 src/server/world/StandaloneUniverse.cpp delete mode 100644 src/server/world/StandaloneUniverse.hpp create mode 100644 src/server/world/client.hpp delete mode 100644 src/server/world/generator.hpp delete mode 100644 src/server/world/region/File.cpp delete mode 100644 src/server/world/region/File.hpp delete mode 100644 src/server/world/region/Memory.cpp delete mode 100644 src/server/world/region/index.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 49e82a7..a9a933e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,94 +1,100 @@ cmake_minimum_required(VERSION 3.11) -project (univerxel VERSION 0.0.1) +project (univerxel VERSION 0.0.2) option(PROFILING "Build with profiling" 0) -option(FIXED_WINDOW "Lock window size: Force floating on i3" 0) -set(SIMD_LEVEL "avx2" CACHE STRING "SIMD processor acceleration (sse2, sse4.1, avx2, avx512f)") -option(USE_FMA "Use fma" 1) option(LOG_DEBUG "Show debug logs" 0) option(LOG_TRACE "Show trace logs" 0) +option(FIXED_WINDOW "Lock window size: Force floating on i3" 0) +option(RENDER_VK "Include vulkan renderer" 1) option(NATIVE "Build with -march=native" 0) option(IPO "Link time optimisation" 1) +option(LD_GOLD "Use gold linker" 1) +option(CCACHE "Use code cache" 1) -find_program(CCACHE_FOUND ccache) -if(CCACHE_FOUND) - set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) -else(CCACHE_FOUND) - #TODO: set(CMAKE_UNITY_BUILD ON) -endif(CCACHE_FOUND) - -add_subdirectory("deps/glfw") -add_subdirectory("deps/glm") -add_subdirectory("deps/picoquic") -add_subdirectory("deps/zstd") +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() -if(IPO) -include(CheckIPOSupported) -check_ipo_supported(RESULT IPO_OK OUTPUT IPO_ERROR) -if(IPO_OK) - message(STATUS "IPO / LTO enabled") - set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) -else() - message(STATUS "IPO / LTO not supported: <${IPO_ERROR}>") +add_subdirectory("deps/glfw") +add_subdirectory("deps/glm") +add_subdirectory("deps/picoquic") +add_subdirectory("deps/zstd") +add_subdirectory("deps/FastNoise2/src") + +if (CCACHE) + find_program(CCACHE_FOUND ccache) + if(CCACHE_FOUND) + message(STATUS "CCACHE enabled") + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) + else(CCACHE_FOUND) + #MAYBE: set(CMAKE_UNITY_BUILD ON) + endif(CCACHE_FOUND) endif() + +if(IPO) + include(CheckIPOSupported) + check_ipo_supported(RESULT IPO_OK OUTPUT IPO_ERROR) + if(IPO_OK) + message(STATUS "IPO / LTO enabled") + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + else() + message(STATUS "IPO / LTO not supported: <${IPO_ERROR}>") + endif() +endif() + +set(LD_GOLD 1) +if(LD_GOLD) + execute_process(COMMAND ${CMAKE_CXX_COMPILER} -fuse-ld=gold -Wl,--version ERROR_QUIET OUTPUT_VARIABLE LD_VERSION) + if("${LD_VERSION}" MATCHES "GNU gold") + message(STATUS "Gold linker enabled") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=gold") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fuse-ld=gold") + endif() endif() if(MSVC) add_definitions(/std:c++latest) add_compile_definitions(WIN32_LEAN_AND_MEAN=) -else() - #FIXME: by target set(CMAKE_CXX_FLAGS "-Wall -Wextra") endif() -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -add_compile_definitions(FIXED_WINDOW=${FIXED_WINDOW} LOG_DEBUG=${LOG_DEBUG} LOG_TRACE=${LOG_TRACE} HN_USE_FILESYSTEM=1) if(PROFILING) -add_compile_definitions(TRACY_ENABLE=1) -endif(PROFILING) + add_compile_definitions(TRACY_ENABLE=1) +endif() +add_compile_definitions(FIXED_WINDOW=${FIXED_WINDOW} LOG_DEBUG=${LOG_DEBUG} LOG_TRACE=${LOG_TRACE}) if(NATIVE) add_definitions(-march=native) endif() -if(SIMD_LEVEL EQUAL "avx2") - add_compile_definitions(FN_COMPILE_AVX2=1) -elseif(SIMD_LEVEL EQUAL "avx512f") - add_compile_definitions(FN_COMPILE_AVX512=1) -endif() -if(MSVC) - add_definitions(/arch:AVX2) -else() - add_definitions(-m${SIMD_LEVEL}) -endif() -if(USE_FMA) - if(MSVC) - add_definitions(/GL /fp:fast) - else() - add_definitions(-mfma) - endif() -endif(USE_FMA) file(GLOB_RECURSE CORE_SOURCES "src/core/*.cpp" "deps/tracy/TracyClient.cpp") configure_file(src/version.h.in generated/version.h @ONLY) -set(CORE_HEADERS "${CMAKE_CURRENT_BINARY_DIR}/generated" "deps/toml++" "deps/robin_hood" "deps/libguarded" "deps/tracy") +set(CORE_HEADERS "src" "${CMAKE_CURRENT_BINARY_DIR}/generated" "deps/toml++" "deps/robin_hood" "deps/tracy") set(CORE_LIBS glm::glm_static zstd::zstd_static) # picoquic -file(GLOB_RECURSE CLIENT_SOURCES "src/client/*.cpp" "deps/imgui/*.cpp" "deps/meshoptimizer/*.cpp" "deps/gl3w/gl3w.c" "deps/volk/volk.c") -set(CLIENT_HEADERS "deps/imgui" "deps/meshoptimizer" "deps/gl3w" "deps/volk") +file(GLOB_RECURSE CLIENT_SOURCES "src/client/*.cpp" "deps/imgui/*.cpp" "deps/meshoptimizer/*.cpp" "deps/gl3w/gl3w.c") +set(CLIENT_HEADERS "deps/imgui" "deps/meshoptimizer" "deps/gl3w") set(CLIENT_LIBS glfw) +set(CLIENT_DEFS) +if(RENDER_VK) + list(APPEND CLIENT_SOURCES "deps/volk/volk.c") + list(APPEND CLIENT_HEADERS "deps/volk") + list(APPEND CLIENT_DEFS RENDER_VK=1) +else() + file(GLOB_RECURSE CLIENT_RENDER "src/client/render/impl/vk/*.cpp") + list(REMOVE_ITEM CLIENT_SOURCE ${CLIENT_RENDER}) +endif() -file(GLOB_RECURSE SERVER_SOURCES "src/server/*.cpp" "deps/FastNoiseSIMD/*.cpp") -set(SERVER_HEADERS "deps/FastNoiseSIMD") -set(SERVER_LINKED) +file(GLOB_RECURSE SERVER_SOURCES "src/server/*.cpp") +set(SERVER_HEADERS) +set(SERVER_LIBS FastNoise) if(WIN32) set(CLIENT_HEADERS ${CLIENT_HEADERS} $ENV{VULKAN_SDK}\\include) find_package(OpenSSL) - set(CORE_LIBS ${CORE_LIBS} ${CMAKE_BINARY_DIR}/libs/*.lib ${OPENSSL_LIBRARIES} ws2_32) + list(APPEND CORE_LIBS ${CMAKE_BINARY_DIR}/libs/*.lib ${OPENSSL_LIBRARIES} ws2_32) else() - set(CORE_LIBS ${CORE_LIBS} picoquic-core pthread dl) + list(APPEND CORE_LIBS picoquic-core pthread dl) endif() if (WIN32) @@ -99,32 +105,56 @@ elseif (APPLE) set(ICON univerxel.icns) endif() +list(APPEND CORE_HEADERS "src/modules") +list(APPEND CORE_SOURCES "src/modules/core/Core.cpp") + # All in one exec add_executable(univerxel "src/main.cpp" ${ICON} ${CORE_SOURCES} ${CLIENT_SOURCES} ${SERVER_SOURCES}) -target_compile_features(univerxel PUBLIC cxx_std_17) -target_link_libraries(univerxel ${CORE_LIBS} ${CLIENT_LIBS} ${SERVER_LIBS}) +target_link_libraries(univerxel PRIVATE ${CORE_LIBS} ${CLIENT_LIBS} ${SERVER_LIBS}) target_include_directories(univerxel PRIVATE ${CORE_HEADERS} ${CLIENT_HEADERS} ${SERVER_HEADERS}) +target_compile_definitions(univerxel PRIVATE ${CLIENT_DEFS}) +target_compile_features(univerxel PUBLIC cxx_std_17) +target_compile_options(univerxel PRIVATE + $<$,$,$>: + -Wall -Wextra> + $<$: + /WX /W4>) # Standalone server add_executable(univerxel-server EXCLUDE_FROM_ALL "src/server.cpp" ${ICON} ${CORE_SOURCES} ${SERVER_SOURCES}) -target_compile_features(univerxel-server PUBLIC cxx_std_17) -target_link_libraries(univerxel-server ${CORE_LIBS} ${SERVER_LIBS}) +target_link_libraries(univerxel-server PRIVATE ${CORE_LIBS} ${SERVER_LIBS}) target_include_directories(univerxel-server PRIVATE ${CORE_HEADERS} ${SERVER_HEADERS}) +target_compile_definitions(univerxel-server PRIVATE STANDALONE_SERVER=1) +target_compile_features(univerxel-server PUBLIC cxx_std_17) +target_compile_options(univerxel-server PRIVATE + $<$,$,$>: + -Wall -Wextra> + $<$: + /WX /W4>) # Dumb client add_executable(univerxel-client EXCLUDE_FROM_ALL "src/client.cpp" ${ICON} ${CORE_SOURCES} ${CLIENT_SOURCES}) -target_compile_features(univerxel-client PUBLIC cxx_std_17) -target_link_libraries(univerxel-client ${CORE_LIBS} ${CLIENT_LIBS}) +target_link_libraries(univerxel-client PRIVATE ${CORE_LIBS} ${CLIENT_LIBS}) target_include_directories(univerxel-client PRIVATE ${CORE_HEADERS} ${CLIENT_HEADERS}) +target_compile_definitions(univerxel-client PRIVATE LIGHT_CLIENT=1 ${CLIENT_DEFS}) +target_compile_features(univerxel-client PUBLIC cxx_std_17) +target_compile_options(univerxel-client PRIVATE + $<$,$,$>: + -Wall -Wextra> + $<$: + /WX /W4>) # Resource client files + default zstd.dict file(COPY resource/content DESTINATION ${CMAKE_BINARY_DIR}) +if(RENDER_VK) + file(COPY resource/shaders/vk DESTINATION ${CMAKE_BINARY_DIR}/content/shaders) +endif() # Serialize entity model file(GLOB_RECURSE MODELS_SOURCES "src/client/render/api/Models.cpp") add_executable(generate_models EXCLUDE_FROM_ALL "src/tools/generate_models.cpp" ${MODELS_SOURCES}) target_compile_features(generate_models PUBLIC cxx_std_17) -target_link_libraries(generate_models glm::glm_static) +target_link_libraries(generate_models PRIVATE glm::glm_static) target_include_directories(generate_models PRIVATE "deps/robin_hood" "deps/meshoptimizer") # Docs diff --git a/Doxyfile b/Doxyfile index 18f3613..a5f017c 100644 --- a/Doxyfile +++ b/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = Univerxel # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 0.0.1 +PROJECT_NUMBER = 0.0.2 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/README.md b/README.md index d19b787..bf3dd0f 100644 --- a/README.md +++ b/README.md @@ -96,13 +96,13 @@ CMake options: `-DKEY=VAL` Key | Usage | Default --- | --- | --- -SIMD_LEVEL | SIMD processor acceleration (sse2, sse4.1, avx2, avx512f) | `avx2` -USE_FMA | Fast math | `1` CMAKE_BUILD_TYPE | Level of optimization | `Release` PROFILING | Tracy profiling | `0` LOG_DEBUG | Debug logs | `0` LOG_TRACE | Trace logs | `0` +IPO | Link time optimisation | `1` NATIVE | Optimize for native CPU | `0` +RENDER_VK | Include Vulkan renderer | `1` 1. Compile ```sh diff --git a/TODO.md b/TODO.md index 31b9c43..edd1ad4 100644 --- a/TODO.md +++ b/TODO.md @@ -4,15 +4,16 @@ Released as `0.0.1`: `Pre alpha 1` -- [ ] From the ground up cleanup +- [x] From the ground up cleanup ## System - [~] Dependencies updater - [x] Tracy - [ ] ImGui + - [x] FastNoise - Compile time - - [ ] Warnings + - [x] Warnings - [ ] Unit build - [ ] Ninga - [ ] Clang @@ -28,60 +29,80 @@ Released as `0.0.1`: `Pre alpha 1` - [ ] Review documentation - [ ] Code cleanup - [ ] Proper wiki - - [ ] Yaml config + - [ ] Proper logger - [ ] I18L ## Data - - [ ] Element hierarchy - - [ ] Collide + - [~] Element hierarchy + - [x] Raycast - [ ] Soft part break - - [ ] Relative position - - [ ] Rotate - - [ ] Merge World and Entities + - [x] Relative position + - [x] Rotate + - [ ] Bounding hierarchy + - [x] Merge World and Entities - [ ] Galaxy - - [ ] Orbit system + - [~] Orbit system + - [x] Circular + - [ ] Elliptic - [ ] Gravity referential + - [ ] Collisions + - [ ] Point + - [ ] Step + - [ ] Ray + - [ ] Continuous + - Bilateral advancement + - GJK - Edits - [ ] More shapes + - [ ] Rotate - [ ] Anchor - [ ] Multi-block system - [ ] Cross chunk structure + - [ ] Lighting + - [ ] Passive chunk save - Generation - SIMD - - [ ] Dynamic SIMD level libs - - [ ] https://github.com/Auburn/FastNoise2/releases - [ ] Double precision - [ ] Surface features - [ ] Biomes - https://imgur.com/kM8b5Zq - https://imgur.com/a/bh2iy - https://speciesdevblog.files.wordpress.com/2012/11/biomemap.png - - [ ] ECS + - [x] Data oriented - [ ] Inventory - [ ] Octree - [ ] Surface aware LOD - - [ ] Level converter + - [ ] Area converter - [ ] Minecraft import - [ ] Base unit size change - [ ] Local generation prediction - - [ ] Modding API + - [~] Modding API + - [x] Define boundaries + - [ ] Version handling + - [ ] Client-Server negotiation + - [ ] Save file index + - [ ] List possibilities ## Graphics - - [ ] Disable VK for now + - [~] Disable VK for now - [ ] Slash screen - [ ] Proper UI - [ ] Pause menu - - [ ] Start menu + - [~] Start menu - [ ] Using texture pack - [ ] FontAwesome (https://github.com/juliettef/IconFontCppHeaders) - - [ ] Define standard unit size + - [x] Define standard unit sizes + - Terrain (area) 1:1m + - Objects (part) 1-8:1 + - Models (instance) ~16:1 manual - Visual debug - - [ ] Chunk / Region border - - [ ] Area box + - [x] Chunk / Region border + - [x] Area box - [ ] Collision overview - [ ] Effective occlusion culling + - [ ] Use average - [ ] Cast from chunk center - Curvature - CubeSphere @@ -99,11 +120,13 @@ Released as `0.0.1`: `Pre alpha 1` - https://assetstore.unity.com/packages/tools/terrain/microsplat-96478 - https://www.youtube.com/user/slipster216/videos - [x] Biplanar + - [ ] Fix stochastic (needs regionPosition bound in f16 range) - [ ] Distance resampling - [ ] Tesselation + - [ ] Better LOD selection + - Dynamic to target fps - [ ] Planet scale LOD (using chunk level average) - [ ] Hard shadow - - [ ] Ambiant occlusion - [ ] World lights - [ ] HDR - https://www.youtube.com/watch?v=iikdcAA7cww @@ -116,6 +139,7 @@ Released as `0.0.1`: `Pre alpha 1` - Bloom - [ ] Procedural Skybox - [ ] Deferred + - [ ] Ambiant occlusion - [ ] Cascaded shadow maps - [ ] Avoid transparent back-face - [ ] Translucency @@ -136,3 +160,4 @@ Released as `0.0.1`: `Pre alpha 1` - [ ] Commands - [~] Authentication - [ ] Clean kick + - [ ] Check certificate diff --git a/deps/FastNoise2/.fix-include-path.patch b/deps/FastNoise2/.fix-include-path.patch new file mode 100644 index 0000000..7ce1f61 --- /dev/null +++ b/deps/FastNoise2/.fix-include-path.patch @@ -0,0 +1,27 @@ +From 2b251207adef7ab9b9f243d4bcfa2cfc1e515114 Mon Sep 17 00:00:00 2001 +From: Shu +Date: Tue, 5 Jan 2021 18:11:53 +0100 +Subject: [PATCH] Fix: include path + +--- + src/CMakeLists.txt | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index c72a4be..f5d134e 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -55,8 +55,8 @@ set(install_targets ${install_targets} FastNoise PARENT_SCOPE) + set(install_fastnoise_headers ${FastNoise_headers} PARENT_SCOPE) + set(install_fastsimd_headers ${FastSIMD_headers} PARENT_SCOPE) + +-target_include_directories(FastNoise PUBLIC +- $ ++target_include_directories(FastNoise SYSTEM PUBLIC ++ $ + $ + ) + +-- +2.30.0 + diff --git a/deps/FastNoise2/.update.sh b/deps/FastNoise2/.update.sh new file mode 100755 index 0000000..64bed7f --- /dev/null +++ b/deps/FastNoise2/.update.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +BASEDIR=$(dirname "$0") +cd $BASEDIR + +git clone https://github.com/Auburn/FastNoise2 .up +cd .up +echo "Apply master" +#TAG=$(git describe --tags $(git rev-list --tags --max-count=1)) +#echo "Apply tag $TAG" +#git checkout $TAG +git apply ../.fix-include-path.patch +cp -rf src include LICENSE .. +cd .. +rm -rf .up +echo "Done" \ No newline at end of file diff --git a/deps/mini-yaml/LICENSE b/deps/FastNoise2/LICENSE similarity index 96% rename from deps/mini-yaml/LICENSE rename to deps/FastNoise2/LICENSE index e241c32..af70600 100644 --- a/deps/mini-yaml/LICENSE +++ b/deps/FastNoise2/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018 Jimmie Bergmann +Copyright (c) 2020 Jordan Peck Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/deps/FastNoise2/include/FastNoise/FastNoise.h b/deps/FastNoise2/include/FastNoise/FastNoise.h new file mode 100644 index 0000000..8a76306 --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/FastNoise.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +#include "FastSIMD/FastSIMD.h" +#include "FastNoise_Config.h" + +#include "Generators/BasicGenerators.h" +#include "Generators/Value.h" +#include "Generators/Perlin.h" +#include "Generators/Simplex.h" +#include "Generators/Cellular.h" +#include "Generators/Fractal.h" +#include "Generators/DomainWarp.h" +#include "Generators/DomainWarpFractal.h" +#include "Generators/Modifiers.h" +#include "Generators/Blends.h" + +namespace FastNoise +{ + template + inline SmartNode New( FastSIMD::eLevel maxLevel = FastSIMD::Level_Null ) + { + static_assert( std::is_base_of_v, "Use FastSIMD::New() to create non FastNoise classes" ); + + return SmartNode( FastSIMD::New( maxLevel ) ); + } + + inline SmartNode<> NewFromEncodedNodeTree( const char* encodedNodeTreeString, FastSIMD::eLevel maxLevel = FastSIMD::Level_Null ) + { + return Metadata::DeserialiseSmartNode( encodedNodeTreeString, maxLevel ); + } +} \ No newline at end of file diff --git a/deps/FastNoise2/include/FastNoise/FastNoiseMetadata.h b/deps/FastNoise2/include/FastNoise/FastNoiseMetadata.h new file mode 100644 index 0000000..71e06fd --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/FastNoiseMetadata.h @@ -0,0 +1,331 @@ +#pragma once +#include +#include +#include +#include +#include + +#include "FastNoise_Config.h" +#include "FastSIMD/FastSIMD.h" + +namespace FastNoise +{ + class Generator; + template + struct PerDimensionVariable; + struct NodeData; + + struct Metadata + { + Metadata( const char* className ) + { + name = className; + id = AddMetadataClass( this ); + } + + static const std::vector& GetMetadataClasses() + { + return sMetadataClasses; + } + + static const Metadata* GetMetadataClass( std::uint16_t nodeId ) + { + if( nodeId < sMetadataClasses.size() ) + { + return sMetadataClasses[nodeId]; + } + + return nullptr; + } + + static std::string SerialiseNodeData( NodeData* nodeData, bool fixUp = false ); + static SmartNode<> DeserialiseSmartNode( const char* serialisedBase64NodeData, FastSIMD::eLevel level = FastSIMD::Level_Null ); + static NodeData* DeserialiseNodeData( const char* serialisedBase64NodeData, std::vector>& nodeDataOut ); + + struct MemberVariable + { + enum eType + { + EFloat, + EInt, + EEnum + }; + + union ValueUnion + { + float f; + std::int32_t i; + + ValueUnion( float v = 0 ) + { + f = v; + } + + ValueUnion( std::int32_t v ) + { + i = v; + } + + operator float() + { + return f; + } + + operator std::int32_t() + { + return i; + } + + bool operator ==( const ValueUnion& rhs ) const + { + return i == rhs.i; + } + }; + + const char* name; + eType type; + int dimensionIdx = -1; + ValueUnion valueDefault, valueMin, valueMax; + std::vector enumNames; + + std::function setFunc; + }; + + template>> + void AddVariable( const char* name, T defaultV, U&& func, T minV = 0, T maxV = 0 ) + { + MemberVariable member; + member.name = name; + member.valueDefault = defaultV; + member.valueMin = minV; + member.valueMax = maxV; + + member.type = std::is_same_v ? MemberVariable::EFloat : MemberVariable::EInt; + + member.setFunc = [func]( Generator* g, MemberVariable::ValueUnion v ) { func( dynamic_cast>(g), v ); }; + + memberVariables.push_back( member ); + } + + template>> + void AddVariable( const char* name, T defaultV, void(U::* func)(T), T minV = 0, T maxV = 0 ) + { + MemberVariable member; + member.name = name; + member.valueDefault = defaultV; + member.valueMin = minV; + member.valueMax = maxV; + + member.type = std::is_same_v ? MemberVariable::EFloat : MemberVariable::EInt; + + member.setFunc = [func]( Generator* g, MemberVariable::ValueUnion v ) { (dynamic_cast(g)->*func)(v); }; + + memberVariables.push_back( member ); + } + + template>, typename... NAMES> + void AddVariableEnum( const char* name, T defaultV, void(U::* func)(T), NAMES... names ) + { + MemberVariable member; + member.name = name; + member.type = MemberVariable::EEnum; + member.valueDefault = (int32_t)defaultV; + member.enumNames = { names... }; + + member.setFunc = [func]( Generator* g, MemberVariable::ValueUnion v ) { (dynamic_cast(g)->*func)((T)v.i); }; + + memberVariables.push_back( member ); + } + + template>> + void AddPerDimensionVariable( const char* name, T defaultV, U&& func, T minV = 0, T maxV = 0 ) + { + for( int idx = 0; (size_t)idx < sizeof( PerDimensionVariable::varArray ) / sizeof( *PerDimensionVariable::varArray ); idx++ ) + { + MemberVariable member; + member.name = name; + member.valueDefault = defaultV; + member.valueMin = minV; + member.valueMax = maxV; + + member.type = std::is_same_v ? MemberVariable::EFloat : MemberVariable::EInt; + member.dimensionIdx = idx; + + member.setFunc = [func, idx]( Generator* g, MemberVariable::ValueUnion v ) { func( dynamic_cast>(g) ).get()[idx] = v; }; + + memberVariables.push_back( member ); + } + } + + struct MemberNode + { + const char* name; + int dimensionIdx = -1; + + std::function )> setFunc; + }; + + template + void AddGeneratorSource( const char* name, void(U::* func)(SmartNodeArg) ) + { + MemberNode member; + member.name = name; + + member.setFunc = [func]( Generator* g, SmartNodeArg<> s ) + { + SmartNode downCast = std::dynamic_pointer_cast(s); + if( downCast ) + { + (dynamic_cast(g)->*func)( downCast ); + } + return (bool)downCast; + }; + + memberNodes.push_back( member ); + } + + template + void AddPerDimensionGeneratorSource( const char* name, U&& func ) + { + using GeneratorSourceT = typename std::invoke_result_t>::type::Type; + using T = typename GeneratorSourceT::Type; + + for( int idx = 0; (size_t)idx < sizeof( PerDimensionVariable::varArray ) / sizeof( *PerDimensionVariable::varArray ); idx++ ) + { + MemberNode member; + member.name = name; + member.dimensionIdx = idx; + + member.setFunc = [func, idx]( auto* g, SmartNodeArg<> s ) + { + SmartNode downCast = std::dynamic_pointer_cast(s); + if( downCast ) + { + g->SetSourceMemberVariable( func( dynamic_cast>(g) ).get()[idx], downCast ); + } + return (bool)downCast; + }; + + memberNodes.push_back( member ); + } + } + + struct MemberHybrid + { + const char* name; + float valueDefault = 0.0f; + int dimensionIdx = -1; + + std::function setValueFunc; + std::function )> setNodeFunc; + }; + + template + void AddHybridSource( const char* name, float defaultValue, void(U::* funcNode)(SmartNodeArg), void(U::* funcValue)(float) ) + { + MemberHybrid member; + member.name = name; + member.valueDefault = defaultValue; + + member.setNodeFunc = [funcNode]( auto* g, SmartNodeArg<> s ) + { + SmartNode downCast = std::dynamic_pointer_cast(s); + if( downCast ) + { + (dynamic_cast(g)->*funcNode)( downCast ); + } + return (bool)downCast; + }; + + member.setValueFunc = [funcValue]( Generator* g, float v ) + { + (dynamic_cast(g)->*funcValue)(v); + }; + + memberHybrids.push_back( member ); + } + + template + void AddPerDimensionHybridSource( const char* name, float defaultV, U&& func ) + { + using HybridSourceT = typename std::invoke_result_t>::type::Type; + using T = typename HybridSourceT::Type; + + for( int idx = 0; (size_t)idx < sizeof( PerDimensionVariable::varArray ) / sizeof( *PerDimensionVariable::varArray ); idx++ ) + { + MemberHybrid member; + member.name = name; + member.valueDefault = defaultV; + member.dimensionIdx = idx; + + member.setNodeFunc = [func, idx]( auto* g, SmartNodeArg<> s ) + { + SmartNode downCast = std::dynamic_pointer_cast(s); + if( downCast ) + { + g->SetSourceMemberVariable( func( dynamic_cast>(g) ).get()[idx], downCast ); + } + return (bool)downCast; + }; + + member.setValueFunc = [func, idx]( Generator* g, float v ) { func( dynamic_cast>(g) ).get()[idx] = v; }; + + memberHybrids.push_back( member ); + } + } + + std::uint16_t id; + const char* name; + std::vector groups; + + std::vector memberVariables; + std::vector memberNodes; + std::vector memberHybrids; + + virtual Generator* NodeFactory( FastSIMD::eLevel level = FastSIMD::Level_Null ) const = 0; + + private: + template + static std::tuple GetArg_Helper( Ret( F::* )(Args...) const ); + + template + using GetArg = std::tuple_element_t; + + static std::uint16_t AddMetadataClass( const Metadata* newMetadata ) + { + sMetadataClasses.emplace_back( newMetadata ); + + return (std::uint16_t)sMetadataClasses.size() - 1; + } + + static std::vector sMetadataClasses; + }; + + struct NodeData + { + NodeData( const Metadata* metadata ); + + const Metadata* metadata; + std::vector variables; + std::vector nodes; + std::vector> hybrids; + + bool operator ==( const NodeData& rhs ) const + { + return metadata == rhs.metadata && + variables == rhs.variables && + nodes == rhs.nodes && + hybrids == rhs.hybrids; + } + }; +} + +#define FASTNOISE_METADATA( ... ) public:\ + FASTSIMD_LEVEL_SUPPORT( FastNoise::SUPPORTED_SIMD_LEVELS );\ + const FastNoise::Metadata* GetMetadata() const override;\ + struct Metadata : __VA_ARGS__::Metadata{\ + Generator* NodeFactory( FastSIMD::eLevel ) const override; + +#define FASTNOISE_METADATA_ABSTRACT( ... ) public:\ + struct Metadata : __VA_ARGS__::Metadata{ + diff --git a/deps/FastNoise2/include/FastNoise/FastNoise_BuildList.inl b/deps/FastNoise2/include/FastNoise/FastNoise_BuildList.inl new file mode 100644 index 0000000..03c50a1 --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/FastNoise_BuildList.inl @@ -0,0 +1,130 @@ +#pragma once + +#ifndef FASTSIMD_BUILD_CLASS +#error Do not include this file +#endif + +#ifndef FASTNOISE_CLASS +#define FASTNOISE_CLASS( CLASS ) FastNoise::CLASS +#endif + +#ifdef FASTSIMD_INCLUDE_HEADER_ONLY +#include "Generators/Generator.h" +#else +#include "Generators/Generator.inl" +#endif + +#ifdef FASTSIMD_INCLUDE_HEADER_ONLY +#include "Generators/BasicGenerators.h" +#else +#include "Generators/BasicGenerators.inl" +#endif + +#ifdef FASTSIMD_INCLUDE_HEADER_ONLY +#include "Generators/Value.h" +#else +#include "Generators/Value.inl" +#endif + +#ifdef FASTSIMD_INCLUDE_HEADER_ONLY +#include "Generators/Perlin.h" +#else +#include "Generators/Perlin.inl" +#endif + +#ifdef FASTSIMD_INCLUDE_HEADER_ONLY +#include "Generators/Simplex.h" +#else +#include "Generators/Simplex.inl" +#endif + +#ifdef FASTSIMD_INCLUDE_HEADER_ONLY +#include "Generators/Cellular.h" +#else +#include "Generators/Cellular.inl" +#endif + +#ifdef FASTSIMD_INCLUDE_HEADER_ONLY +#include "Generators/Fractal.h" +#else +#include "Generators/Fractal.inl" +#endif + +#ifdef FASTSIMD_INCLUDE_HEADER_ONLY +#include "Generators/DomainWarp.h" +#else +#include "Generators/DomainWarp.inl" +#endif + +#ifdef FASTSIMD_INCLUDE_HEADER_ONLY +#include "Generators/DomainWarpFractal.h" +#else +#include "Generators/DomainWarpFractal.inl" +#endif + +#ifdef FASTSIMD_INCLUDE_HEADER_ONLY +#include "Generators/Modifiers.h" +#else +#include "Generators/Modifiers.inl" +#endif + +#ifdef FASTSIMD_INCLUDE_HEADER_ONLY +#include "Generators/Blends.h" +#else +#include "Generators/Blends.inl" +#endif + +// Nodes +// Order is important! +// Always add to bottom of list, +// inserting will break existing encoded node trees + +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( Constant ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( White ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( Checkerboard ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( SineWave ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( PositionOutput ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( DistanceToOrigin ) ) + +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( Value ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( Perlin ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( Simplex ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( OpenSimplex2 ) ) + +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( CellularValue ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( CellularDistance ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( CellularLookup ) ) + +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( FractalFBm ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( FractalBillow ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( FractalRidged ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( FractalRidgedMulti ) ) + +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( DomainWarpGradient ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( DomainWarpFractalProgressive ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( DomainWarpFractalIndependant ) ) + +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( DomainScale ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( DomainOffset ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( DomainRotate ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( SeedOffset ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( Remap ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( ConvertRGBA8 ) ) + +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( Add ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( Subtract ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( Multiply ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( Divide ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( Min ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( Max ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( MinSmooth ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( MaxSmooth ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( Fade ) ) + +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( Terrace ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( PowFloat ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( PowInt ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( DomainAxisScale ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( AddDimension ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( RemoveDimension ) ) +FASTSIMD_BUILD_CLASS( FASTNOISE_CLASS( GeneratorCache ) ) diff --git a/deps/FastNoise2/include/FastNoise/FastNoise_Config.h b/deps/FastNoise2/include/FastNoise/FastNoise_Config.h new file mode 100644 index 0000000..f87be42 --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/FastNoise_Config.h @@ -0,0 +1,20 @@ +#pragma once +#include "FastSIMD/FastSIMD.h" + +#define FASTNOISE_CALC_MIN_MAX 1 + +namespace FastNoise +{ + const FastSIMD::Level_BitFlags SUPPORTED_SIMD_LEVELS = + FastSIMD::Level_Scalar | + FastSIMD::Level_SSE2 | + FastSIMD::Level_SSE41 | + FastSIMD::Level_AVX2 | + FastSIMD::Level_AVX512 ; + + template + using SmartNode = std::shared_ptr; + + template + using SmartNodeArg = const SmartNode&; +} \ No newline at end of file diff --git a/deps/FastNoise2/include/FastNoise/Generators/BasicGenerators.h b/deps/FastNoise2/include/FastNoise/Generators/BasicGenerators.h new file mode 100644 index 0000000..dc378a2 --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/BasicGenerators.h @@ -0,0 +1,109 @@ +#pragma once +#include "Generator.h" + +namespace FastNoise +{ + class Constant : public virtual Generator + { + public: + void SetValue( float value ) { mValue = value; } + + protected: + float mValue = 1.0f; + + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Basic Generators" ); + this->AddVariable( "Value", 1.0f, &Constant::SetValue ); + } + }; + }; + + class White : public virtual Generator + { + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Basic Generators" ); + } + }; + }; + + class Checkerboard : public virtual Generator + { + public: + void SetSize( float value ) { mSize = value; } + + protected: + float mSize = 1.0f; + + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Basic Generators" ); + this->AddVariable( "Size", 1.0f, &Checkerboard::SetSize ); + } + }; + }; + + class SineWave : public virtual Generator + { + public: + void SetScale( float value ) { mScale = value; } + + protected: + float mScale = 1.0f; + + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Basic Generators" ); + this->AddVariable( "Scale", 1.0f, &SineWave::SetScale ); + } + }; + }; + + class PositionOutput : public virtual Generator + { + public: + template + void Set( float multiplier, float offset = 0.0f ) { mMultiplier[(int)D] = multiplier; mOffset[(int)D] = offset; } + + protected: + PerDimensionVariable mMultiplier; + PerDimensionVariable mOffset; + + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Basic Generators" ); + this->AddPerDimensionVariable( "Multiplier", 0.0f, []( PositionOutput* p ) { return std::ref( p->mMultiplier ); } ); + this->AddPerDimensionVariable( "Offset", 0.0f, []( PositionOutput* p ) { return std::ref( p->mOffset ); } ); + } + }; + }; + + class DistanceToOrigin : public virtual Generator + { + public: + void SetDistanceFunction( DistanceFunction value ) { mDistanceFunction = value; } + + protected: + DistanceFunction mDistanceFunction = DistanceFunction::EuclideanSquared; + + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Basic Generators" ); + this->AddVariableEnum( "Distance Function", DistanceFunction::Euclidean, &DistanceToOrigin::SetDistanceFunction, "Euclidean", "Euclidean Squared", "Manhattan", "Hybrid" ); + } + }; + }; +} diff --git a/deps/FastNoise2/include/FastNoise/Generators/BasicGenerators.inl b/deps/FastNoise2/include/FastNoise/Generators/BasicGenerators.inl new file mode 100644 index 0000000..aa4eccb --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/BasicGenerators.inl @@ -0,0 +1,96 @@ +#include +#include "FastSIMD/InlInclude.h" + +#include "BasicGenerators.h" +#include "Utils.inl" + +template +class FS_T : public virtual FastNoise::Constant, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + return float32v( mValue ); + } +}; + +template +class FS_T : public virtual FastNoise::White, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + size_t idx = 0; + ((pos = FS_Casti32_f32( (FS_Castf32_i32( pos ) ^ (FS_Castf32_i32( pos ) >> 16)) * int32v( FnPrimes::Lookup[idx++] ) )), ...); + + return FnUtils::GetValueCoord( seed, FS_Castf32_i32( pos )... ); + } +}; + +template +class FS_T : public virtual FastNoise::Checkerboard, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + float32v multiplier = FS_Reciprocal_f32( float32v( mSize ) ); + + int32v value = (FS_Convertf32_i32( pos * multiplier ) ^ ...); + + return float32v( 1.0f ) ^ FS_Casti32_f32( value << 31 ); + } +}; + +template +class FS_T : public virtual FastNoise::SineWave, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + float32v multiplier = FS_Reciprocal_f32( float32v( mScale ) ); + + return (FS_Sin_f32( pos * multiplier ) * ...); + } +}; + +template +class FS_T : public virtual FastNoise::PositionOutput, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + size_t offsetIdx = 0; + size_t multiplierIdx = 0; + + (((pos += float32v( mOffset[offsetIdx++] )) *= float32v( mMultiplier[multiplierIdx++] )), ...); + return (pos + ...); + } +}; + +template +class FS_T : public virtual FastNoise::DistanceToOrigin, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + return FnUtils::CalcDistance( mDistanceFunction, pos... ); + } +}; diff --git a/deps/FastNoise2/include/FastNoise/Generators/Blends.h b/deps/FastNoise2/include/FastNoise/Generators/Blends.h new file mode 100644 index 0000000..dbf86a3 --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/Blends.h @@ -0,0 +1,198 @@ +#pragma once +#include "Generator.h" + +#include + +namespace FastNoise +{ + class OperatorSourceLHS : public virtual Generator + { + public: + void SetLHS( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mLHS, gen ); } + void SetRHS( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mRHS, gen ); } + void SetRHS( float value ) { mRHS = value; } + + protected: + GeneratorSource mLHS; + HybridSource mRHS; + + FASTNOISE_METADATA_ABSTRACT( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Blends" ); + this->AddGeneratorSource( "LHS", &OperatorSourceLHS::SetLHS ); + this->AddHybridSource( "RHS", 0.0f, &OperatorSourceLHS::SetRHS, &OperatorSourceLHS::SetRHS ); + } + }; + }; + + class OperatorHybridLHS : public virtual Generator + { + public: + void SetLHS( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mLHS, gen ); } + void SetLHS( float value ) { mLHS = value; } + void SetRHS( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mRHS, gen ); } + void SetRHS( float value ) { mRHS = value; } + + protected: + HybridSource mLHS; + HybridSource mRHS; + + FASTNOISE_METADATA_ABSTRACT( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Blends" ); + this->AddHybridSource( "LHS", 0.0f, &OperatorHybridLHS::SetLHS, &OperatorHybridLHS::SetLHS ); + this->AddHybridSource( "RHS", 0.0f, &OperatorHybridLHS::SetRHS, &OperatorHybridLHS::SetRHS ); + } + }; + }; + + class Add : public virtual OperatorSourceLHS + { + FASTNOISE_METADATA( OperatorSourceLHS ) + using OperatorSourceLHS::Metadata::Metadata; + }; + }; + + class Subtract : public virtual OperatorHybridLHS + { + FASTNOISE_METADATA( OperatorHybridLHS ) + using OperatorHybridLHS::Metadata::Metadata; + }; + }; + + class Multiply : public virtual OperatorSourceLHS + { + FASTNOISE_METADATA( OperatorSourceLHS ) + using OperatorSourceLHS::Metadata::Metadata; + }; + }; + + class Divide : public virtual OperatorHybridLHS + { + FASTNOISE_METADATA( OperatorHybridLHS ) + using OperatorHybridLHS::Metadata::Metadata; + }; + }; + + class Min : public virtual OperatorSourceLHS + { + FASTNOISE_METADATA( OperatorSourceLHS ) + using OperatorSourceLHS::Metadata::Metadata; + }; + }; + + class Max : public virtual OperatorSourceLHS + { + FASTNOISE_METADATA( OperatorSourceLHS ) + using OperatorSourceLHS::Metadata::Metadata; + }; + }; + + class PowFloat : public virtual Generator + { + public: + void SetValue( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mValue, gen ); } + void SetValue( float value ) { mValue = value; } + void SetPow( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mPow, gen ); } + void SetPow( float value ) { mPow = value; } + + protected: + HybridSource mValue; + HybridSource mPow; + + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Blends" ); + this->AddHybridSource( "Value", 2.0f, &PowFloat::SetValue, &PowFloat::SetValue ); + this->AddHybridSource( "Pow", 2.0f, &PowFloat::SetPow, &PowFloat::SetPow ); + } + }; + }; + + class PowInt : public virtual OperatorHybridLHS + { + public: + void SetValue( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mValue, gen ); } + void SetPow( int32_t value ) { mPow = value; } + + protected: + GeneratorSource mValue; + int32_t mPow; + + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Blends" ); + this->AddGeneratorSource( "Value", &PowInt::SetValue ); + this->AddVariable( "Pow", 2, &PowInt::SetPow, 2, INT_MAX ); + } + }; + }; + + class MinSmooth : public virtual OperatorSourceLHS + { + public: + void SetSmoothness( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mSmoothness, gen ); } + void SetSmoothness( float value ) { mSmoothness = value; } + + protected: + HybridSource mSmoothness = 0.1f; + + FASTNOISE_METADATA( OperatorSourceLHS ) + Metadata( const char* className ) : OperatorSourceLHS::Metadata( className ) + { + this->AddHybridSource( "Smoothness", 0.1f, &MinSmooth::SetSmoothness, &MinSmooth::SetSmoothness ); + } + }; + }; + + class MaxSmooth : public virtual OperatorSourceLHS + { + public: + void SetSmoothness( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mSmoothness, gen ); } + void SetSmoothness( float value ) { mSmoothness = value; } + + protected: + HybridSource mSmoothness = 0.1f; + + FASTNOISE_METADATA( OperatorSourceLHS ) + Metadata( const char* className ) : OperatorSourceLHS::Metadata( className ) + { + this->AddHybridSource( "Smoothness", 0.1f, &MaxSmooth::SetSmoothness, &MaxSmooth::SetSmoothness ); + } + }; + }; + + class Fade : public virtual Generator + { + public: + void SetA( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mA, gen ); } + void SetB( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mB, gen ); } + + void SetFade( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mFade, gen ); } + void SetFade( float value ) { mFade = value; } + + protected: + GeneratorSource mA; + GeneratorSource mB; + HybridSource mFade = 0.5f; + + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Blends" ); + this->AddGeneratorSource( "A", &Fade::SetA ); + this->AddGeneratorSource( "B", &Fade::SetB ); + this->AddHybridSource( "Fade", 0.5f, &Fade::SetFade, &Fade::SetFade ); + } + }; + }; +} diff --git a/deps/FastNoise2/include/FastNoise/Generators/Blends.inl b/deps/FastNoise2/include/FastNoise/Generators/Blends.inl new file mode 100644 index 0000000..b827db1 --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/Blends.inl @@ -0,0 +1,174 @@ +#include "FastSIMD/InlInclude.h" + +#include "Blends.h" + + +template +class FS_T : public virtual FastNoise::Add, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + return this->GetSourceValue( mLHS, seed, pos... ) + this->GetSourceValue( mRHS, seed, pos... ); + } +}; + +template +class FS_T : public virtual FastNoise::Subtract, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + return this->GetSourceValue( mLHS, seed, pos... ) - this->GetSourceValue( mRHS, seed, pos... ); + } +}; + +template +class FS_T : public virtual FastNoise::Multiply, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + return this->GetSourceValue( mLHS, seed, pos... ) * this->GetSourceValue( mRHS, seed, pos... ); + } +}; + +template +class FS_T : public virtual FastNoise::Divide, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + return this->GetSourceValue( mLHS, seed, pos... ) / this->GetSourceValue( mRHS, seed, pos... ); + } +}; + +template +class FS_T : public virtual FastNoise::PowFloat, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + return FS_Pow_f32( this->GetSourceValue( mValue, seed, pos... ), this->GetSourceValue( mPow, seed, pos... ) ); + } +}; + +template +class FS_T : public virtual FastNoise::PowInt, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + float32v value = this->GetSourceValue( mValue, seed, pos... ); + float32v pow = value * value; + + for( int32_t i = 2; i < mPow; i++ ) + { + pow *= value; + } + + return pow; + } +}; + +template +class FS_T : public virtual FastNoise::Min, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + return FS_Min_f32( this->GetSourceValue( mLHS, seed, pos... ), this->GetSourceValue( mRHS, seed, pos... ) ); + } +}; + +template +class FS_T : public virtual FastNoise::Max, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + return FS_Max_f32( this->GetSourceValue( mLHS, seed, pos... ), this->GetSourceValue( mRHS, seed, pos... ) ); + } +}; + +template +class FS_T : public virtual FastNoise::MinSmooth, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + float32v a = this->GetSourceValue( mLHS, seed, pos... ); + float32v b = this->GetSourceValue( mRHS, seed, pos... ); + float32v smoothness = FS_Max_f32( float32v( 1.175494351e-38f ), FS_Abs_f32( this->GetSourceValue( mSmoothness, seed, pos... ) ) ); + + float32v h = FS_Max_f32( smoothness - FS_Abs_f32( a - b ), float32v( 0.0f ) ); + + h *= FS_Reciprocal_f32( smoothness ); + + return FS_FNMulAdd_f32( float32v( 1.0f / 6.0f ), h * h * h * smoothness, FS_Min_f32( a, b ) ); + } +}; + +template +class FS_T : public virtual FastNoise::MaxSmooth, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + float32v a = -this->GetSourceValue( mLHS, seed, pos... ); + float32v b = -this->GetSourceValue( mRHS, seed, pos... ); + float32v smoothness = FS_Max_f32( float32v( 1.175494351e-38f ), FS_Abs_f32( this->GetSourceValue( mSmoothness, seed, pos... ) ) ); + + float32v h = FS_Max_f32( smoothness - FS_Abs_f32( a - b ), float32v( 0.0f ) ); + + h *= FS_Reciprocal_f32( smoothness ); + + return -FS_FNMulAdd_f32( float32v( 1.0f / 6.0f ), h * h * h * smoothness, FS_Min_f32( a, b ) ); + } +}; + +template +class FS_T : public virtual FastNoise::Fade, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + float32v fade = FS_Abs_f32( this->GetSourceValue( mFade, seed, pos... ) ); + + return FS_FMulAdd_f32( this->GetSourceValue( mA, seed, pos... ), float32v( 1 ) - fade, this->GetSourceValue( mB, seed, pos... ) * fade ); + } +}; + diff --git a/deps/FastNoise2/include/FastNoise/Generators/Cellular.h b/deps/FastNoise2/include/FastNoise/Generators/Cellular.h new file mode 100644 index 0000000..7fdeb42 --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/Cellular.h @@ -0,0 +1,104 @@ +#pragma once + +#include "Generator.h" + +namespace FastNoise +{ + class Cellular : public virtual Generator + { + public: + void SetJitterModifier( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mJitterModifier, gen ); } + void SetJitterModifier( float value ) { mJitterModifier = value; } + void SetDistanceFunction( DistanceFunction value ) { mDistanceFunction = value; } + + protected: + HybridSource mJitterModifier = 1.0f; + DistanceFunction mDistanceFunction = DistanceFunction::EuclideanSquared; + + const float kJitter2D = 0.437015f; + const float kJitter3D = 0.396143f; + const float kJitter4D = 0.366025f; + + FASTNOISE_METADATA_ABSTRACT( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Coherent Noise" ); + this->AddHybridSource( "Jitter Modifier", 1.0f, &Cellular::SetJitterModifier, &Cellular::SetJitterModifier ); + this->AddVariableEnum( "Distance Function", DistanceFunction::EuclideanSquared, &Cellular::SetDistanceFunction, "Euclidean", "Euclidean Squared", "Manhattan", "Hybrid" ); + } + }; + }; + + class CellularValue : public virtual Cellular + { + public: + void SetValueIndex( int value ) { mValueIndex = value; } + + protected: + static const int kMaxDistanceCount = 4; + + int mValueIndex = 0; + + FASTNOISE_METADATA( Cellular ) + Metadata( const char* className ) : Cellular::Metadata( className ) + { + this->AddVariable( "Value Index", 0, &CellularValue::SetValueIndex, 0, kMaxDistanceCount - 1 ); + } + }; + }; + + class CellularDistance : public virtual Cellular + { + public: + enum class ReturnType + { + Index0, + Index0Add1, + Index0Sub1, + Index0Mul1, + Index0Div1 + }; + + void SetDistanceIndex0( int value ) { mDistanceIndex0 = value; } + void SetDistanceIndex1( int value ) { mDistanceIndex1 = value; } + void SetReturnType( ReturnType value ) { mReturnType = value; } + + protected: + static const int kMaxDistanceCount = 4; + + ReturnType mReturnType = ReturnType::Index0; + int mDistanceIndex0 = 0; + int mDistanceIndex1 = 1; + + FASTNOISE_METADATA( Cellular ) + + Metadata( const char* className ) : Cellular::Metadata( className ) + { + this->AddVariable( "Distance Index 0", 0, &CellularDistance::SetDistanceIndex0, 0, kMaxDistanceCount - 1 ); + this->AddVariable( "Distance Index 1", 1, &CellularDistance::SetDistanceIndex1, 0, kMaxDistanceCount - 1 ); + this->AddVariableEnum( "Return Type", ReturnType::Index0, &CellularDistance::SetReturnType, "Index0", "Index0Add1", "Index0Sub1", "Index0Mul1", "Index0Div1" ); + } + }; + }; + + class CellularLookup : public virtual Cellular + { + public: + void SetLookup( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mLookup, gen ); } + void SetLookupFrequency( float freq ) { mLookupFreq = freq; } + + protected: + GeneratorSource mLookup; + float mLookupFreq = 0.1f; + + FASTNOISE_METADATA( Cellular ) + + Metadata( const char* className ) : Cellular::Metadata( className ) + { + this->AddGeneratorSource( "Lookup", &CellularLookup::SetLookup ); + this->AddVariable( "Lookup Frequency", 0.1f, &CellularLookup::SetLookupFrequency ); + } + }; + }; +} diff --git a/deps/FastNoise2/include/FastNoise/Generators/Cellular.inl b/deps/FastNoise2/include/FastNoise/Generators/Cellular.inl new file mode 100644 index 0000000..2cbf969 --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/Cellular.inl @@ -0,0 +1,655 @@ +#include "FastSIMD/InlInclude.h" + +#include +#include + +#include "Cellular.h" +#include "Utils.inl" + +template +class FS_T : public virtual FastNoise::Cellular, public FS_T +{ +}; + +template +class FS_T : public virtual FastNoise::CellularValue, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y ) const final + { + float32v jitter = float32v( kJitter2D ) * this->GetSourceValue( mJitterModifier, seed, x, y ); + std::array value; + std::array distance; + + value.fill( float32v( INFINITY ) ); + distance.fill( float32v( INFINITY ) ); + + int32v xc = FS_Convertf32_i32( x ) + int32v( -1 ); + int32v ycBase = FS_Convertf32_i32( y ) + int32v( -1 ); + + float32v xcf = FS_Converti32_f32( xc ) - x; + float32v ycfBase = FS_Converti32_f32( ycBase ) - y; + + xc *= int32v( FnPrimes::X ); + ycBase *= int32v( FnPrimes::Y ); + + for( int xi = 0; xi < 3; xi++ ) + { + float32v ycf = ycfBase; + int32v yc = ycBase; + for( int yi = 0; yi < 3; yi++ ) + { + int32v hash = FnUtils::HashPrimesHB( seed, xc, yc ); + float32v xd = FS_Converti32_f32( hash & int32v( 0xffff ) ) - float32v( 0xffff / 2.0f ); + float32v yd = FS_Converti32_f32( (hash >> 16) & int32v( 0xffff ) ) - float32v( 0xffff / 2.0f ); + + float32v invMag = jitter * FS_InvSqrt_f32( FS_FMulAdd_f32( xd, xd, yd * yd ) ); + xd = FS_FMulAdd_f32( xd, invMag, xcf ); + yd = FS_FMulAdd_f32( yd, invMag, ycf ); + + float32v newCellValue = float32v( (float)(1.0 / INT_MAX) ) * FS_Converti32_f32( hash ); + float32v newDistance = FnUtils::CalcDistance( mDistanceFunction, xd, yd ); + + for( int i = 0; ; i++ ) + { + mask32v closer = newDistance < distance[i]; + + float32v localDistance = distance[i]; + float32v localCellValue = value[i]; + + distance[i] = FS_Select_f32( closer, newDistance, distance[i] ); + value[i] = FS_Select_f32( closer, newCellValue, value[i] ); + + if( i > mValueIndex ) + { + break; + } + + newDistance = FS_Select_f32( closer, localDistance, newDistance ); + newCellValue = FS_Select_f32( closer, localCellValue, newCellValue ); + } + + ycf += float32v( 1 ); + yc += int32v( FnPrimes::Y ); + } + xcf += float32v( 1 ); + xc += int32v( FnPrimes::X ); + } + + return value[mValueIndex]; + } + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z ) const final + { + float32v jitter = float32v( kJitter3D ) * this->GetSourceValue( mJitterModifier, seed, x, y, z ); + std::array value; + std::array distance; + + value.fill( float32v( INFINITY ) ); + distance.fill( float32v( INFINITY ) ); + + int32v xc = FS_Convertf32_i32( x ) + int32v( -1 ); + int32v ycBase = FS_Convertf32_i32( y ) + int32v( -1 ); + int32v zcBase = FS_Convertf32_i32( z ) + int32v( -1 ); + + float32v xcf = FS_Converti32_f32( xc ) - x; + float32v ycfBase = FS_Converti32_f32( ycBase ) - y; + float32v zcfBase = FS_Converti32_f32( zcBase ) - z; + + xc *= int32v( FnPrimes::X ); + ycBase *= int32v( FnPrimes::Y ); + zcBase *= int32v( FnPrimes::Z ); + + for( int xi = 0; xi < 3; xi++ ) + { + float32v ycf = ycfBase; + int32v yc = ycBase; + for( int yi = 0; yi < 3; yi++ ) + { + float32v zcf = zcfBase; + int32v zc = zcBase; + for( int zi = 0; zi < 3; zi++ ) + { + int32v hash = FnUtils::HashPrimesHB( seed, xc, yc, zc ); + float32v xd = FS_Converti32_f32( hash & int32v( 0x3ff ) ) - float32v( 0x3ff / 2.0f ); + float32v yd = FS_Converti32_f32( ( hash >> 10 ) & int32v( 0x3ff ) ) - float32v( 0x3ff / 2.0f ); + float32v zd = FS_Converti32_f32( ( hash >> 20 ) & int32v( 0x3ff ) ) - float32v( 0x3ff / 2.0f ); + + float32v invMag = jitter * FS_InvSqrt_f32( FS_FMulAdd_f32( xd, xd, FS_FMulAdd_f32( yd, yd, zd * zd ) ) ); + xd = FS_FMulAdd_f32( xd, invMag, xcf ); + yd = FS_FMulAdd_f32( yd, invMag, ycf ); + zd = FS_FMulAdd_f32( zd, invMag, zcf ); + + float32v newCellValue = float32v( (float)(1.0 / INT_MAX) ) * FS_Converti32_f32( hash ); + float32v newDistance = FnUtils::CalcDistance( mDistanceFunction, xd, yd, zd ); + + for( int i = 0; ; i++ ) + { + mask32v closer = newDistance < distance[i]; + + float32v localDistance = distance[i]; + float32v localCellValue = value[i]; + + distance[i] = FS_Select_f32( closer, newDistance, distance[i] ); + value[i] = FS_Select_f32( closer, newCellValue, value[i] ); + + if( i > mValueIndex ) + { + break; + } + + newDistance = FS_Select_f32( closer, localDistance, newDistance ); + newCellValue = FS_Select_f32( closer, localCellValue, newCellValue ); + } + + zcf += float32v( 1 ); + zc += int32v( FnPrimes::Z ); + } + ycf += float32v( 1 ); + yc += int32v( FnPrimes::Y ); + } + xcf += float32v( 1 ); + xc += int32v( FnPrimes::X ); + } + + return value[mValueIndex]; + } + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z , float32v w ) const final + { + float32v jitter = float32v( kJitter4D ) * this->GetSourceValue( mJitterModifier, seed, x, y, z, w ); + std::array value; + std::array distance; + + value.fill( float32v( INFINITY ) ); + distance.fill( float32v( INFINITY ) ); + + int32v xc = FS_Convertf32_i32( x ) + int32v( -1 ); + int32v ycBase = FS_Convertf32_i32( y ) + int32v( -1 ); + int32v zcBase = FS_Convertf32_i32( z ) + int32v( -1 ); + int32v wcBase = FS_Convertf32_i32( w ) + int32v( -1 ); + + float32v xcf = FS_Converti32_f32( xc ) - x; + float32v ycfBase = FS_Converti32_f32( ycBase ) - y; + float32v zcfBase = FS_Converti32_f32( zcBase ) - z; + float32v wcfBase = FS_Converti32_f32( wcBase ) - w; + + xc *= int32v( FnPrimes::X ); + ycBase *= int32v( FnPrimes::Y ); + zcBase *= int32v( FnPrimes::Z ); + wcBase *= int32v( FnPrimes::W ); + + for( int xi = 0; xi < 3; xi++ ) + { + float32v ycf = ycfBase; + int32v yc = ycBase; + for( int yi = 0; yi < 3; yi++ ) + { + float32v zcf = zcfBase; + int32v zc = zcBase; + for( int zi = 0; zi < 3; zi++ ) + { + float32v wcf = wcfBase; + int32v wc = wcBase; + for( int wi = 0; wi < 3; wi++ ) + { + int32v hash = FnUtils::HashPrimesHB( seed, xc, yc, zc, wc ); + float32v xd = FS_Converti32_f32( hash & int32v( 0xff ) ) - float32v( 0xff / 2.0f ); + float32v yd = FS_Converti32_f32( (hash >> 8) & int32v( 0xff ) ) - float32v( 0xff / 2.0f ); + float32v zd = FS_Converti32_f32( (hash >> 16) & int32v( 0xff ) ) - float32v( 0xff / 2.0f ); + float32v wd = FS_Converti32_f32( (hash >> 24) & int32v( 0xff ) ) - float32v( 0xff / 2.0f ); + + float32v invMag = jitter * FS_InvSqrt_f32( FS_FMulAdd_f32( xd, xd, FS_FMulAdd_f32( yd, yd, FS_FMulAdd_f32( zd, zd, wd * wd ) ) ) ); + xd = FS_FMulAdd_f32( xd, invMag, xcf ); + yd = FS_FMulAdd_f32( yd, invMag, ycf ); + zd = FS_FMulAdd_f32( zd, invMag, zcf ); + wd = FS_FMulAdd_f32( wd, invMag, wcf ); + + float32v newCellValue = float32v( (float)(1.0 / INT_MAX) ) * FS_Converti32_f32( hash ); + float32v newDistance = FnUtils::CalcDistance( mDistanceFunction, xd, yd, zd, wd ); + + for( int i = 0; ; i++ ) + { + mask32v closer = newDistance < distance[i]; + + float32v localDistance = distance[i]; + float32v localCellValue = value[i]; + + distance[i] = FS_Select_f32( closer, newDistance, distance[i] ); + value[i] = FS_Select_f32( closer, newCellValue, value[i] ); + + if( i > mValueIndex ) + { + break; + } + + newDistance = FS_Select_f32( closer, localDistance, newDistance ); + newCellValue = FS_Select_f32( closer, localCellValue, newCellValue ); + } + + wcf += float32v( 1 ); + wc += int32v( FnPrimes::W ); + } + zcf += float32v( 1 ); + zc += int32v( FnPrimes::Z ); + } + ycf += float32v( 1 ); + yc += int32v( FnPrimes::Y ); + } + xcf += float32v( 1 ); + xc += int32v( FnPrimes::X ); + } + + return value[mValueIndex]; + } +}; + +template +class FS_T : public virtual FastNoise::CellularDistance, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y ) const final + { + float32v jitter = float32v( kJitter2D ) * this->GetSourceValue( mJitterModifier, seed, x, y ); + + std::array distance; + distance.fill( float32v( INFINITY ) ); + + int32v xc = FS_Convertf32_i32( x ) + int32v( -1 ); + int32v ycBase = FS_Convertf32_i32( y ) + int32v( -1 ); + + float32v xcf = FS_Converti32_f32( xc ) - x; + float32v ycfBase = FS_Converti32_f32( ycBase ) - y; + + xc *= int32v( FnPrimes::X ); + ycBase *= int32v( FnPrimes::Y ); + + for( int xi = 0; xi < 3; xi++ ) + { + float32v ycf = ycfBase; + int32v yc = ycBase; + for ( int yi = 0; yi < 3; yi++ ) + { + int32v hash = FnUtils::HashPrimesHB( seed, xc, yc ); + float32v xd = FS_Converti32_f32( hash & int32v( 0xffff ) ) - float32v( 0xffff / 2.0f ); + float32v yd = FS_Converti32_f32( (hash >> 16) & int32v( 0xffff ) ) - float32v( 0xffff / 2.0f ); + + float32v invMag = jitter * FS_InvSqrt_f32( FS_FMulAdd_f32( xd, xd, yd * yd ) ); + xd = FS_FMulAdd_f32( xd, invMag, xcf ); + yd = FS_FMulAdd_f32( yd, invMag, ycf ); + + float32v newDistance = FnUtils::CalcDistance( mDistanceFunction, xd, yd ); + + for( int i = kMaxDistanceCount - 1; i > 0; i-- ) + { + distance[i] = FS_Max_f32( FS_Min_f32( distance[i], newDistance ), distance[i - 1] ); + } + + distance[0] = FS_Min_f32( distance[0], newDistance ); + + ycf += float32v( 1 ); + yc += int32v( FnPrimes::Y ); + } + xcf += float32v( 1 ); + xc += int32v( FnPrimes::X ); + } + + return GetReturn( distance ); + } + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z ) const final + { + float32v jitter = float32v( kJitter3D ) * this->GetSourceValue( mJitterModifier, seed, x, y, z ); + + std::array distance; + distance.fill( float32v( INFINITY ) ); + + int32v xc = FS_Convertf32_i32( x ) + int32v( -1 ); + int32v ycBase = FS_Convertf32_i32( y ) + int32v( -1 ); + int32v zcBase = FS_Convertf32_i32( z ) + int32v( -1 ); + + float32v xcf = FS_Converti32_f32( xc ) - x; + float32v ycfBase = FS_Converti32_f32( ycBase ) - y; + float32v zcfBase = FS_Converti32_f32( zcBase ) - z; + + xc *= int32v( FnPrimes::X ); + ycBase *= int32v( FnPrimes::Y ); + zcBase *= int32v( FnPrimes::Z ); + + for( int xi = 0; xi < 3; xi++ ) + { + float32v ycf = ycfBase; + int32v yc = ycBase; + for( int yi = 0; yi < 3; yi++ ) + { + float32v zcf = zcfBase; + int32v zc = zcBase; + for( int zi = 0; zi < 3; zi++ ) + { + int32v hash = FnUtils::HashPrimesHB( seed, xc, yc, zc ); + float32v xd = FS_Converti32_f32( hash & int32v( 0x3ff ) ) - float32v( 0x3ff / 2.0f ); + float32v yd = FS_Converti32_f32( (hash >> 10) & int32v( 0x3ff ) ) - float32v( 0x3ff / 2.0f ); + float32v zd = FS_Converti32_f32( (hash >> 20) & int32v( 0x3ff ) ) - float32v( 0x3ff / 2.0f ); + + float32v invMag = jitter * FS_InvSqrt_f32( FS_FMulAdd_f32( xd, xd, FS_FMulAdd_f32( yd, yd, zd * zd ) ) ); + xd = FS_FMulAdd_f32( xd, invMag, xcf ); + yd = FS_FMulAdd_f32( yd, invMag, ycf ); + zd = FS_FMulAdd_f32( zd, invMag, zcf ); + + float32v newDistance = FnUtils::CalcDistance( mDistanceFunction, xd, yd, zd ); + + for( int i = kMaxDistanceCount - 1; i > 0; i-- ) + { + distance[i] = FS_Max_f32( FS_Min_f32( distance[i], newDistance ), distance[i - 1] ); + } + + distance[0] = FS_Min_f32( distance[0], newDistance ); + + zcf += float32v( 1 ); + zc += int32v( FnPrimes::Z ); + } + ycf += float32v( 1 ); + yc += int32v( FnPrimes::Y ); + } + xcf += float32v( 1 ); + xc += int32v( FnPrimes::X ); + } + + return GetReturn( distance ); + } + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z, float32v w ) const final + { + float32v jitter = float32v( kJitter4D ) * this->GetSourceValue( mJitterModifier, seed, x, y, z, w ); + + std::array distance; + distance.fill( float32v( INFINITY ) ); + + int32v xc = FS_Convertf32_i32( x ) + int32v( -1 ); + int32v ycBase = FS_Convertf32_i32( y ) + int32v( -1 ); + int32v zcBase = FS_Convertf32_i32( z ) + int32v( -1 ); + int32v wcBase = FS_Convertf32_i32( w ) + int32v( -1 ); + + float32v xcf = FS_Converti32_f32( xc ) - x; + float32v ycfBase = FS_Converti32_f32( ycBase ) - y; + float32v zcfBase = FS_Converti32_f32( zcBase ) - z; + float32v wcfBase = FS_Converti32_f32( wcBase ) - w; + + xc *= int32v( FnPrimes::X ); + ycBase *= int32v( FnPrimes::Y ); + zcBase *= int32v( FnPrimes::Z ); + wcBase *= int32v( FnPrimes::W ); + + for( int xi = 0; xi < 3; xi++ ) + { + float32v ycf = ycfBase; + int32v yc = ycBase; + for( int yi = 0; yi < 3; yi++ ) + { + float32v zcf = zcfBase; + int32v zc = zcBase; + for( int zi = 0; zi < 3; zi++ ) + { + float32v wcf = wcfBase; + int32v wc = wcBase; + for( int wi = 0; wi < 3; wi++ ) + { + int32v hash = FnUtils::HashPrimesHB( seed, xc, yc, zc, wc ); + float32v xd = FS_Converti32_f32( hash & int32v( 0xff ) ) - float32v( 0xff / 2.0f ); + float32v yd = FS_Converti32_f32( (hash >> 8) & int32v( 0xff ) ) - float32v( 0xff / 2.0f ); + float32v zd = FS_Converti32_f32( (hash >> 16) & int32v( 0xff ) ) - float32v( 0xff / 2.0f ); + float32v wd = FS_Converti32_f32( (hash >> 24) & int32v( 0xff ) ) - float32v( 0xff / 2.0f ); + + float32v invMag = jitter * FS_InvSqrt_f32( FS_FMulAdd_f32( xd, xd, FS_FMulAdd_f32( yd, yd, FS_FMulAdd_f32( zd, zd, wd * wd ) ) ) ); + xd = FS_FMulAdd_f32( xd, invMag, xcf ); + yd = FS_FMulAdd_f32( yd, invMag, ycf ); + zd = FS_FMulAdd_f32( zd, invMag, zcf ); + wd = FS_FMulAdd_f32( wd, invMag, wcf ); + + float32v newDistance = FnUtils::CalcDistance( mDistanceFunction, xd, yd, zd, wd ); + + for( int i = kMaxDistanceCount - 1; i > 0; i-- ) + { + distance[i] = FS_Max_f32( FS_Min_f32( distance[i], newDistance ), distance[i - 1] ); + } + + distance[0] = FS_Min_f32( distance[0], newDistance ); + + wcf += float32v( 1 ); + wc += int32v( FnPrimes::W ); + } + zcf += float32v( 1 ); + zc += int32v( FnPrimes::Z ); + } + ycf += float32v( 1 ); + yc += int32v( FnPrimes::Y ); + } + xcf += float32v( 1 ); + xc += int32v( FnPrimes::X ); + } + + return GetReturn( distance ); + } + + FS_INLINE float32v GetReturn( std::array& distance ) const + { + if( mDistanceFunction == FastNoise::DistanceFunction::Euclidean ) + { + distance[mDistanceIndex0] *= FS_InvSqrt_f32( distance[mDistanceIndex0] ); + distance[mDistanceIndex1] *= FS_InvSqrt_f32( distance[mDistanceIndex1] ); + } + + switch( mReturnType ) + { + default: + case ReturnType::Index0: + { + return distance[mDistanceIndex0]; + } + case ReturnType::Index0Add1: + { + return distance[mDistanceIndex0] + distance[mDistanceIndex1]; + } + case ReturnType::Index0Sub1: + { + return distance[mDistanceIndex0] - distance[mDistanceIndex1]; + } + case ReturnType::Index0Mul1: + { + return distance[mDistanceIndex0] * distance[mDistanceIndex1]; + } + case ReturnType::Index0Div1: + { + return distance[mDistanceIndex0] * FS_Reciprocal_f32( distance[mDistanceIndex1] ); + } + } + } +}; + +template +class FS_T : public virtual FastNoise::CellularLookup, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y ) const final + { + float32v jitter = float32v( kJitter2D ) * this->GetSourceValue( mJitterModifier, seed, x, y ); + float32v distance( FLT_MAX ); + float32v cellX, cellY; + + int32v xc = FS_Convertf32_i32( x ) + int32v( -1 ); + int32v ycBase = FS_Convertf32_i32( y ) + int32v( -1 ); + + float32v xcf = FS_Converti32_f32( xc ) - x; + float32v ycfBase = FS_Converti32_f32( ycBase ) - y; + + xc *= int32v( FnPrimes::X ); + ycBase *= int32v( FnPrimes::Y ); + + for( int xi = 0; xi < 3; xi++ ) + { + float32v ycf = ycfBase; + int32v yc = ycBase; + for( int yi = 0; yi < 3; yi++ ) + { + int32v hash = FnUtils::HashPrimesHB( seed, xc, yc ); + float32v xd = FS_Converti32_f32( hash & int32v( 0xffff ) ) - float32v( 0xffff / 2.0f ); + float32v yd = FS_Converti32_f32( (hash >> 16) & int32v( 0xffff ) ) - float32v( 0xffff / 2.0f ); + + float32v invMag = jitter * FS_InvSqrt_f32( FS_FMulAdd_f32( xd, xd, yd * yd ) ); + xd = FS_FMulAdd_f32( xd, invMag, xcf ); + yd = FS_FMulAdd_f32( yd, invMag, ycf ); + + float32v newDistance = FnUtils::CalcDistance( mDistanceFunction, xd, yd ); + + mask32v closer = newDistance < distance; + distance = FS_Min_f32( newDistance, distance ); + + cellX = FS_Select_f32( closer, xd + x, cellX ); + cellY = FS_Select_f32( closer, yd + y, cellY ); + + ycf += float32v( 1 ); + yc += int32v( FnPrimes::Y ); + } + xcf += float32v( 1 ); + xc += int32v( FnPrimes::X ); + } + + return this->GetSourceValue( mLookup, seed - int32v( -1 ), cellX * float32v( mLookupFreq ), cellY * float32v( mLookupFreq ) ); + } + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z ) const final + { + float32v jitter = float32v( kJitter3D ) * this->GetSourceValue( mJitterModifier, seed, x, y, z ); + float32v distance( FLT_MAX ); + float32v cellX, cellY, cellZ; + + int32v xc = FS_Convertf32_i32( x ) + int32v( -1 ); + int32v ycBase = FS_Convertf32_i32( y ) + int32v( -1 ); + int32v zcBase = FS_Convertf32_i32( z ) + int32v( -1 ); + + float32v xcf = FS_Converti32_f32( xc ) - x; + float32v ycfBase = FS_Converti32_f32( ycBase ) - y; + float32v zcfBase = FS_Converti32_f32( zcBase ) - z; + + xc *= int32v( FnPrimes::X ); + ycBase *= int32v( FnPrimes::Y ); + zcBase *= int32v( FnPrimes::Z ); + + for( int xi = 0; xi < 3; xi++ ) + { + float32v ycf = ycfBase; + int32v yc = ycBase; + for( int yi = 0; yi < 3; yi++ ) + { + float32v zcf = zcfBase; + int32v zc = zcBase; + for( int zi = 0; zi < 3; zi++ ) + { + int32v hash = FnUtils::HashPrimesHB( seed, xc, yc, zc ); + float32v xd = FS_Converti32_f32( hash & int32v( 0x3ff ) ) - float32v( 0x3ff / 2.0f ); + float32v yd = FS_Converti32_f32( (hash >> 10) & int32v( 0x3ff ) ) - float32v( 0x3ff / 2.0f ); + float32v zd = FS_Converti32_f32( (hash >> 20) & int32v( 0x3ff ) ) - float32v( 0x3ff / 2.0f ); + + float32v invMag = jitter * FS_InvSqrt_f32( FS_FMulAdd_f32( xd, xd, FS_FMulAdd_f32( yd, yd, zd * zd ) ) ); + xd = FS_FMulAdd_f32( xd, invMag, xcf ); + yd = FS_FMulAdd_f32( yd, invMag, ycf ); + zd = FS_FMulAdd_f32( zd, invMag, zcf ); + + float32v newDistance = FnUtils::CalcDistance( mDistanceFunction, xd, yd, zd ); + + mask32v closer = newDistance < distance; + distance = FS_Min_f32( newDistance, distance ); + + cellX = FS_Select_f32( closer, xd + x, cellX ); + cellY = FS_Select_f32( closer, yd + y, cellY ); + cellZ = FS_Select_f32( closer, zd + z, cellZ ); + + zcf += float32v( 1 ); + zc += int32v( FnPrimes::Z ); + } + ycf += float32v( 1 ); + yc += int32v( FnPrimes::Y ); + } + xcf += float32v( 1 ); + xc += int32v( FnPrimes::X ); + } + + return this->GetSourceValue( mLookup, seed - int32v( -1 ), cellX * float32v( mLookupFreq ), cellY * float32v( mLookupFreq ), cellZ * float32v( mLookupFreq ) ); + } + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z, float32v w ) const final + { + float32v jitter = float32v( kJitter4D ) * this->GetSourceValue( mJitterModifier, seed, x, y, z, w ); + float32v distance( FLT_MAX ); + float32v cellX, cellY, cellZ, cellW; + + int32v xc = FS_Convertf32_i32( x ) + int32v( -1 ); + int32v ycBase = FS_Convertf32_i32( y ) + int32v( -1 ); + int32v zcBase = FS_Convertf32_i32( z ) + int32v( -1 ); + int32v wcBase = FS_Convertf32_i32( w ) + int32v( -1 ); + + float32v xcf = FS_Converti32_f32( xc ) - x; + float32v ycfBase = FS_Converti32_f32( ycBase ) - y; + float32v zcfBase = FS_Converti32_f32( zcBase ) - z; + float32v wcfBase = FS_Converti32_f32( wcBase ) - w; + + xc *= int32v( FnPrimes::X ); + ycBase *= int32v( FnPrimes::Y ); + zcBase *= int32v( FnPrimes::Z ); + wcBase *= int32v( FnPrimes::W ); + + for( int xi = 0; xi < 3; xi++ ) + { + float32v ycf = ycfBase; + int32v yc = ycBase; + for( int yi = 0; yi < 3; yi++ ) + { + float32v zcf = zcfBase; + int32v zc = zcBase; + for( int zi = 0; zi < 3; zi++ ) + { + float32v wcf = wcfBase; + int32v wc = wcBase; + for( int wi = 0; wi < 3; wi++ ) + { + int32v hash = FnUtils::HashPrimesHB( seed, xc, yc, zc, wc ); + float32v xd = FS_Converti32_f32( hash & int32v( 0xff ) ) - float32v( 0xff / 2.0f ); + float32v yd = FS_Converti32_f32( (hash >> 8) & int32v( 0xff ) ) - float32v( 0xff / 2.0f ); + float32v zd = FS_Converti32_f32( (hash >> 16) & int32v( 0xff ) ) - float32v( 0xff / 2.0f ); + float32v wd = FS_Converti32_f32( (hash >> 24) & int32v( 0xff ) ) - float32v( 0xff / 2.0f ); + + float32v invMag = jitter * FS_InvSqrt_f32( FS_FMulAdd_f32( xd, xd, FS_FMulAdd_f32( yd, yd, FS_FMulAdd_f32( zd, zd, wd * wd ) ) ) ); + xd = FS_FMulAdd_f32( xd, invMag, xcf ); + yd = FS_FMulAdd_f32( yd, invMag, ycf ); + zd = FS_FMulAdd_f32( zd, invMag, zcf ); + wd = FS_FMulAdd_f32( wd, invMag, wcf ); + + float32v newDistance = FnUtils::CalcDistance( mDistanceFunction, xd, yd, zd, wd ); + + mask32v closer = newDistance < distance; + distance = FS_Min_f32( newDistance, distance ); + + cellX = FS_Select_f32( closer, xd + x, cellX ); + cellY = FS_Select_f32( closer, yd + y, cellY ); + cellZ = FS_Select_f32( closer, zd + z, cellZ ); + cellW = FS_Select_f32( closer, wd + w, cellW ); + + wcf += float32v( 1 ); + wc += int32v( FnPrimes::W ); + } + zcf += float32v( 1 ); + zc += int32v( FnPrimes::Z ); + } + ycf += float32v( 1 ); + yc += int32v( FnPrimes::Y ); + } + xcf += float32v( 1 ); + xc += int32v( FnPrimes::X ); + } + + return this->GetSourceValue( mLookup, seed - int32v( -1 ), cellX * float32v( mLookupFreq ), cellY * float32v( mLookupFreq ), cellZ * float32v( mLookupFreq ), cellW * float32v( mLookupFreq ) ); + } +}; diff --git a/deps/FastNoise2/include/FastNoise/Generators/DomainWarp.h b/deps/FastNoise2/include/FastNoise/Generators/DomainWarp.h new file mode 100644 index 0000000..203750d --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/DomainWarp.h @@ -0,0 +1,37 @@ +#pragma once +#include "Generator.h" + +namespace FastNoise +{ + class DomainWarp : public virtual Generator + { + public: + void SetSource( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mSource, gen ); } + void SetWarpAmplitude( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mWarpAmplitude, gen ); } + void SetWarpAmplitude( float value ) { mWarpAmplitude = value; } + void SetWarpFrequency( float value ) { mWarpFrequency = value; } + + protected: + GeneratorSource mSource; + HybridSource mWarpAmplitude = 1.0f; + float mWarpFrequency = 0.5f; + + FASTNOISE_METADATA_ABSTRACT( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Domain Warp" ); + this->AddGeneratorSource( "Source", &DomainWarp::SetSource ); + this->AddHybridSource( "Warp Amplitude", 1.0f, &DomainWarp::SetWarpAmplitude, &DomainWarp::SetWarpAmplitude ); + this->AddVariable( "Warp Frequency", 0.5f, &DomainWarp::SetWarpFrequency ); + } + }; + }; + + class DomainWarpGradient : public virtual DomainWarp + { + FASTNOISE_METADATA( DomainWarp ) + using DomainWarp::Metadata::Metadata; + }; + }; +} diff --git a/deps/FastNoise2/include/FastNoise/Generators/DomainWarp.inl b/deps/FastNoise2/include/FastNoise/Generators/DomainWarp.inl new file mode 100644 index 0000000..25f3fef --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/DomainWarp.inl @@ -0,0 +1,181 @@ +#include "FastSIMD/InlInclude.h" + +#include "DomainWarp.h" +#include "Utils.inl" + +template +class FS_T : public virtual FastNoise::DomainWarp, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + Warp( seed, this->GetSourceValue( mWarpAmplitude, seed, pos... ), (pos * float32v( mWarpFrequency ))..., pos... ); + + return this->GetSourceValue( mSource, seed, pos...); + } + +public: + float GetWarpFrequency() const { return mWarpFrequency; } + const FastNoise::HybridSource& GetWarpAmplitude() const { return mWarpAmplitude; } + const FastNoise::GeneratorSource& GetWarpSource() const { return mSource; } + + virtual void FS_VECTORCALL Warp( int32v seed, float32v warpAmp, float32v x, float32v y, float32v& xOut, float32v& yOut ) const = 0; + virtual void FS_VECTORCALL Warp( int32v seed, float32v warpAmp, float32v x, float32v y, float32v z, float32v& xOut, float32v& yOut, float32v& zOut ) const = 0; + virtual void FS_VECTORCALL Warp( int32v seed, float32v warpAmp, float32v x, float32v y, float32v z, float32v w, float32v& xOut, float32v& yOut, float32v& zOut, float32v& wOut ) const = 0; +}; + +template +class FS_T : public virtual FastNoise::DomainWarpGradient, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + +public: + void FS_VECTORCALL Warp( int32v seed, float32v warpAmp, float32v x, float32v y, float32v& xOut, float32v& yOut ) const final + { + float32v xs = FS_Floor_f32( x ); + float32v ys = FS_Floor_f32( y ); + + int32v x0 = FS_Convertf32_i32( xs ) * int32v( FnPrimes::X ); + int32v y0 = FS_Convertf32_i32( ys ) * int32v( FnPrimes::Y ); + int32v x1 = x0 + int32v( FnPrimes::X ); + int32v y1 = y0 + int32v( FnPrimes::Y ); + + xs = FnUtils::InterpHermite( x - xs ); + ys = FnUtils::InterpHermite( y - ys ); + + #define GRADIENT_COORD( _x, _y )\ + int32v hash##_x##_y = FnUtils::HashPrimesHB(seed, x##_x, y##_y );\ + float32v x##_x##_y = FS_Converti32_f32( hash##_x##_y & int32v( 0xffff ) );\ + float32v y##_x##_y = FS_Converti32_f32( (hash##_x##_y >> 16) & int32v( 0xffff ) ); + + GRADIENT_COORD( 0, 0 ); + GRADIENT_COORD( 1, 0 ); + GRADIENT_COORD( 0, 1 ); + GRADIENT_COORD( 1, 1 ); + + #undef GRADIENT_COORD + + float32v normalise = warpAmp * float32v( 1.0f / (0xffff / 2.0f) ); + + xOut = FS_FMulAdd_f32( FnUtils::Lerp( FnUtils::Lerp( x00, x10, xs ), FnUtils::Lerp( x01, x11, xs ), ys ) - float32v( 0xffff / 2.0f ), normalise, xOut ); + yOut = FS_FMulAdd_f32( FnUtils::Lerp( FnUtils::Lerp( y00, y10, xs ), FnUtils::Lerp( y01, y11, xs ), ys ) - float32v( 0xffff / 2.0f ), normalise, yOut ); + } + + void FS_VECTORCALL Warp( int32v seed, float32v warpAmp, float32v x, float32v y, float32v z, float32v& xOut, float32v& yOut, float32v& zOut ) const final + { + float32v xs = FS_Floor_f32( x ); + float32v ys = FS_Floor_f32( y ); + float32v zs = FS_Floor_f32( z ); + + int32v x0 = FS_Convertf32_i32( xs ) * int32v( FnPrimes::X ); + int32v y0 = FS_Convertf32_i32( ys ) * int32v( FnPrimes::Y ); + int32v z0 = FS_Convertf32_i32( zs ) * int32v( FnPrimes::Z ); + int32v x1 = x0 + int32v( FnPrimes::X ); + int32v y1 = y0 + int32v( FnPrimes::Y ); + int32v z1 = z0 + int32v( FnPrimes::Z ); + + xs = FnUtils::InterpHermite( x - xs ); + ys = FnUtils::InterpHermite( y - ys ); + zs = FnUtils::InterpHermite( z - zs ); + + #define GRADIENT_COORD( _x, _y, _z )\ + int32v hash##_x##_y##_z = FnUtils::HashPrimesHB( seed, x##_x, y##_y, z##_z );\ + float32v x##_x##_y##_z = FS_Converti32_f32( hash##_x##_y##_z & int32v( 0x3ff ) );\ + float32v y##_x##_y##_z = FS_Converti32_f32( (hash##_x##_y##_z >> 10) & int32v( 0x3ff ) );\ + float32v z##_x##_y##_z = FS_Converti32_f32( (hash##_x##_y##_z >> 20) & int32v( 0x3ff ) ); + + GRADIENT_COORD( 0, 0, 0 ); + GRADIENT_COORD( 1, 0, 0 ); + GRADIENT_COORD( 0, 1, 0 ); + GRADIENT_COORD( 1, 1, 0 ); + GRADIENT_COORD( 0, 0, 1 ); + GRADIENT_COORD( 1, 0, 1 ); + GRADIENT_COORD( 0, 1, 1 ); + GRADIENT_COORD( 1, 1, 1 ); + + #undef GRADIENT_COORD + + float32v x0z = FnUtils::Lerp( FnUtils::Lerp( x000, x100, xs ), FnUtils::Lerp( x010, x110, xs ), ys ); + float32v y0z = FnUtils::Lerp( FnUtils::Lerp( y000, y100, xs ), FnUtils::Lerp( y010, y110, xs ), ys ); + float32v z0z = FnUtils::Lerp( FnUtils::Lerp( z000, z100, xs ), FnUtils::Lerp( z010, z110, xs ), ys ); + + float32v x1z = FnUtils::Lerp( FnUtils::Lerp( x001, x101, xs ), FnUtils::Lerp( x011, x111, xs ), ys ); + float32v y1z = FnUtils::Lerp( FnUtils::Lerp( y001, y101, xs ), FnUtils::Lerp( y011, y111, xs ), ys ); + float32v z1z = FnUtils::Lerp( FnUtils::Lerp( z001, z101, xs ), FnUtils::Lerp( z011, z111, xs ), ys ); + + float32v normalise = warpAmp * float32v( 1.0f / (0x3ff / 2.0f) ); + + xOut = FS_FMulAdd_f32( FnUtils::Lerp( x0z, x1z, zs ) - float32v( 0x3ff / 2.0f ), normalise, xOut ); + yOut = FS_FMulAdd_f32( FnUtils::Lerp( y0z, y1z, zs ) - float32v( 0x3ff / 2.0f ), normalise, yOut ); + zOut = FS_FMulAdd_f32( FnUtils::Lerp( z0z, z1z, zs ) - float32v( 0x3ff / 2.0f ), normalise, zOut ); + } + + void FS_VECTORCALL Warp( int32v seed, float32v warpAmp, float32v x, float32v y, float32v z, float32v w, float32v& xOut, float32v& yOut, float32v& zOut, float32v& wOut ) const final + { + float32v xs = FS_Floor_f32( x ); + float32v ys = FS_Floor_f32( y ); + float32v zs = FS_Floor_f32( z ); + float32v ws = FS_Floor_f32( w ); + + int32v x0 = FS_Convertf32_i32( xs ) * int32v( FnPrimes::X ); + int32v y0 = FS_Convertf32_i32( ys ) * int32v( FnPrimes::Y ); + int32v z0 = FS_Convertf32_i32( zs ) * int32v( FnPrimes::Z ); + int32v w0 = FS_Convertf32_i32( ws ) * int32v( FnPrimes::W ); + int32v x1 = x0 + int32v( FnPrimes::X ); + int32v y1 = y0 + int32v( FnPrimes::Y ); + int32v z1 = z0 + int32v( FnPrimes::Z ); + int32v w1 = w0 + int32v( FnPrimes::W ); + + xs = FnUtils::InterpHermite( x - xs ); + ys = FnUtils::InterpHermite( y - ys ); + zs = FnUtils::InterpHermite( z - zs ); + ws = FnUtils::InterpHermite( w - ws ); + + #define GRADIENT_COORD( _x, _y, _z, _w )\ + int32v hash##_x##_y##_z##_w = FnUtils::HashPrimesHB( seed, x##_x, y##_y, z##_z, w##_w );\ + float32v x##_x##_y##_z##_w = FS_Converti32_f32( hash##_x##_y##_z##_w & int32v( 0xff ) );\ + float32v y##_x##_y##_z##_w = FS_Converti32_f32( (hash##_x##_y##_z##_w >> 8) & int32v( 0xff ) );\ + float32v z##_x##_y##_z##_w = FS_Converti32_f32( (hash##_x##_y##_z##_w >> 16) & int32v( 0xff ) );\ + float32v w##_x##_y##_z##_w = FS_Converti32_f32( (hash##_x##_y##_z##_w >> 24) & int32v( 0xff ) ); + + GRADIENT_COORD( 0, 0, 0, 0 ); + GRADIENT_COORD( 1, 0, 0, 0 ); + GRADIENT_COORD( 0, 1, 0, 0 ); + GRADIENT_COORD( 1, 1, 0, 0 ); + GRADIENT_COORD( 0, 0, 1, 0 ); + GRADIENT_COORD( 1, 0, 1, 0 ); + GRADIENT_COORD( 0, 1, 1, 0 ); + GRADIENT_COORD( 1, 1, 1, 0 ); + GRADIENT_COORD( 0, 0, 0, 1 ); + GRADIENT_COORD( 1, 0, 0, 1 ); + GRADIENT_COORD( 0, 1, 0, 1 ); + GRADIENT_COORD( 1, 1, 0, 1 ); + GRADIENT_COORD( 0, 0, 1, 1 ); + GRADIENT_COORD( 1, 0, 1, 1 ); + GRADIENT_COORD( 0, 1, 1, 1 ); + GRADIENT_COORD( 1, 1, 1, 1 ); + + #undef GRADIENT_COORD + + float32v x0w = FnUtils::Lerp( FnUtils::Lerp( FnUtils::Lerp( x0000, x1000, xs ), FnUtils::Lerp( x0100, x1100, xs ), ys ), FnUtils::Lerp( FnUtils::Lerp( x0010, x1010, xs ), FnUtils::Lerp( x0110, x1110, xs ), ys ), zs ); + float32v y0w = FnUtils::Lerp( FnUtils::Lerp( FnUtils::Lerp( y0000, y1000, xs ), FnUtils::Lerp( y0100, y1100, xs ), ys ), FnUtils::Lerp( FnUtils::Lerp( y0010, y1010, xs ), FnUtils::Lerp( y0110, y1110, xs ), ys ), zs ); + float32v z0w = FnUtils::Lerp( FnUtils::Lerp( FnUtils::Lerp( z0000, z1000, xs ), FnUtils::Lerp( z0100, z1100, xs ), ys ), FnUtils::Lerp( FnUtils::Lerp( z0010, z1010, xs ), FnUtils::Lerp( z0110, z1110, xs ), ys ), zs ); + float32v w0w = FnUtils::Lerp( FnUtils::Lerp( FnUtils::Lerp( w0000, w1000, xs ), FnUtils::Lerp( w0100, w1100, xs ), ys ), FnUtils::Lerp( FnUtils::Lerp( w0010, w1010, xs ), FnUtils::Lerp( w0110, w1110, xs ), ys ), zs ); + + float32v x1w = FnUtils::Lerp( FnUtils::Lerp( FnUtils::Lerp( x0001, x1001, xs ), FnUtils::Lerp( x0101, x1101, xs ), ys ), FnUtils::Lerp( FnUtils::Lerp( x0011, x1011, xs ), FnUtils::Lerp( x0111, x1111, xs ), ys ), zs ); + float32v y1w = FnUtils::Lerp( FnUtils::Lerp( FnUtils::Lerp( y0001, y1001, xs ), FnUtils::Lerp( y0101, y1101, xs ), ys ), FnUtils::Lerp( FnUtils::Lerp( y0011, y1011, xs ), FnUtils::Lerp( y0111, y1111, xs ), ys ), zs ); + float32v z1w = FnUtils::Lerp( FnUtils::Lerp( FnUtils::Lerp( z0001, z1001, xs ), FnUtils::Lerp( z0101, z1101, xs ), ys ), FnUtils::Lerp( FnUtils::Lerp( z0011, z1011, xs ), FnUtils::Lerp( z0111, z1111, xs ), ys ), zs ); + float32v w1w = FnUtils::Lerp( FnUtils::Lerp( FnUtils::Lerp( w0001, w1001, xs ), FnUtils::Lerp( w0101, w1101, xs ), ys ), FnUtils::Lerp( FnUtils::Lerp( w0011, w1011, xs ), FnUtils::Lerp( w0111, w1111, xs ), ys ), zs ); + + float32v normalise = warpAmp * float32v( 1.0f / (0xff / 2.0f) ); + + xOut = FS_FMulAdd_f32( FnUtils::Lerp( x0w, x1w, ws ) - float32v( 0xff / 2.0f ), normalise, xOut ); + yOut = FS_FMulAdd_f32( FnUtils::Lerp( y0w, y1w, ws ) - float32v( 0xff / 2.0f ), normalise, yOut ); + zOut = FS_FMulAdd_f32( FnUtils::Lerp( z0w, z1w, ws ) - float32v( 0xff / 2.0f ), normalise, zOut ); + wOut = FS_FMulAdd_f32( FnUtils::Lerp( w0w, w1w, ws ) - float32v( 0xff / 2.0f ), normalise, wOut ); + } +}; + diff --git a/deps/FastNoise2/include/FastNoise/Generators/DomainWarpFractal.h b/deps/FastNoise2/include/FastNoise/Generators/DomainWarpFractal.h new file mode 100644 index 0000000..1af4670 --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/DomainWarpFractal.h @@ -0,0 +1,26 @@ +#pragma once +#include "Fractal.h" +#include "DomainWarp.h" + +namespace FastNoise +{ + class DomainWarpFractalProgressive : public virtual Fractal + { + FASTNOISE_METADATA( Fractal ) + Metadata( const char* className ) : Fractal::Metadata( className, "Domain Warp Source" ) + { + groups.push_back( "Domain Warp" ); + } + }; + }; + + class DomainWarpFractalIndependant : public virtual Fractal + { + FASTNOISE_METADATA( Fractal ) + Metadata( const char* className ) : Fractal::Metadata( className, "Domain Warp Source" ) + { + groups.push_back( "Domain Warp" ); + } + }; + }; +} diff --git a/deps/FastNoise2/include/FastNoise/Generators/DomainWarpFractal.inl b/deps/FastNoise2/include/FastNoise/Generators/DomainWarpFractal.inl new file mode 100644 index 0000000..eb531aa --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/DomainWarpFractal.inl @@ -0,0 +1,71 @@ +#include "FastSIMD/InlInclude.h" + +#include "DomainWarpFractal.h" + +template +class FS_T : public virtual FastNoise::DomainWarpFractalProgressive, public FS_T, FS> +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + auto* warp = this->GetSourceSIMD( mSource ); + + float32v amp = float32v( mFractalBounding ) * this->GetSourceValue( warp->GetWarpAmplitude(), seed, pos... ); + float32v freq = float32v( warp->GetWarpFrequency() ); + int32v seedInc = seed; + + float32v gain = this->GetSourceValue( mGain, seed, pos... ); + float32v lacunarity( mLacunarity ); + + warp->Warp( seedInc, amp, (pos * freq)..., pos... ); + + for (int i = 1; i < mOctaves; i++) + { + seedInc -= int32v( -1 ); + freq *= lacunarity; + amp *= gain; + warp->Warp( seedInc, amp, (pos * freq)..., pos... ); + } + + return this->GetSourceValue( warp->GetWarpSource(), seed, pos... ); + } +}; + +template +class FS_T : public virtual FastNoise::DomainWarpFractalIndependant, public FS_T, FS> +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + return [this, seed] ( std::remove_reference_t

... noisePos, std::remove_reference_t

... warpPos ) + { + auto* warp = this->GetSourceSIMD( mSource ); + + float32v amp = float32v( mFractalBounding ) * this->GetSourceValue( warp->GetWarpAmplitude(), seed, noisePos... ); + float32v freq = float32v( warp->GetWarpFrequency() ); + int32v seedInc = seed; + + float32v gain = this->GetSourceValue( mGain, seed, noisePos... ); + float32v lacunarity( mLacunarity ); + + warp->Warp( seedInc, amp, (noisePos * freq)..., warpPos... ); + + for( int i = 1; i < mOctaves; i++ ) + { + seedInc -= int32v( -1 ); + freq *= lacunarity; + amp *= gain; + warp->Warp( seedInc, amp, (noisePos * freq)..., warpPos... ); + } + + return this->GetSourceValue( warp->GetWarpSource(), seed, warpPos... ); + + } ( pos..., pos... ); + } +}; diff --git a/deps/FastNoise2/include/FastNoise/Generators/Fractal.h b/deps/FastNoise2/include/FastNoise/Generators/Fractal.h new file mode 100644 index 0000000..2b3c95a --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/Fractal.h @@ -0,0 +1,103 @@ +#pragma once +#include "Generator.h" + +namespace FastNoise +{ + template + class Fractal : public virtual Generator + { + public: + void SetSource( SmartNodeArg gen ) { this->SetSourceMemberVariable( mSource, gen ); } + void SetGain( float value ) { mGain = value; CalculateFractalBounding(); } + void SetGain( SmartNodeArg<> gen ) { mGain = 1.0f; this->SetSourceMemberVariable( mGain, gen ); CalculateFractalBounding(); } + void SetOctaveCount( int32_t value ) { mOctaves = value; CalculateFractalBounding(); } + void SetLacunarity( float value ) { mLacunarity = value; } + + protected: + GeneratorSourceT mSource; + HybridSource mGain = 0.5f; + + int32_t mOctaves = 3; + float mLacunarity = 2.0f; + float mFractalBounding = 1.0f / 1.75f; + + virtual void CalculateFractalBounding() + { + float gain = std::abs( mGain.constant ); + float amp = gain; + float ampFractal = 1.0f; + for( int32_t i = 1; i < mOctaves; i++ ) + { + ampFractal += amp; + amp *= gain; + } + mFractalBounding = 1.0f / ampFractal; + } + + FASTNOISE_METADATA_ABSTRACT( Generator ) + + Metadata( const char* className, const char* sourceName = "Source" ) : Generator::Metadata( className ) + { + groups.push_back( "Fractal" ); + + this->AddGeneratorSource( sourceName, &Fractal::SetSource ); + this->AddHybridSource( "Gain", 0.5f, &Fractal::SetGain, &Fractal::SetGain ); + this->AddVariable( "Octaves", 3, &Fractal::SetOctaveCount, 2, 16 ); + this->AddVariable( "Lacunarity", 2.0f, &Fractal::SetLacunarity ); + } + }; + }; + + class FractalFBm : public virtual Fractal<> + { + FASTNOISE_METADATA( Fractal ) + using Fractal::Metadata::Metadata; + }; + }; + + class FractalBillow : public virtual Fractal<> + { + FASTNOISE_METADATA( Fractal ) + using Fractal::Metadata::Metadata; + }; + }; + + class FractalRidged : public virtual Fractal<> + { + FASTNOISE_METADATA( Fractal ) + using Fractal::Metadata::Metadata; + }; + }; + + class FractalRidgedMulti : public virtual Fractal<> + { + public: + void SetWeightAmplitude( float value ) { mWeightAmp = value; CalculateFractalBounding(); } + + protected: + float mWeightAmp = 2.0f; + float mWeightBounding = 2.0f / 1.75f; + + void CalculateFractalBounding() override + { + Fractal::CalculateFractalBounding(); + + float weight = 1.0f; + float totalWeight = weight; + for( int32_t i = 1; i < mOctaves; i++ ) + { + weight *= mWeightAmp; + totalWeight += 1.0f / weight; + } + mWeightBounding = 2.0f / totalWeight; + } + + FASTNOISE_METADATA( Fractal ) + + Metadata( const char* className ) : Fractal::Metadata( className ) + { + this->AddVariable( "Weight Amplitude", 2.0f, &FractalRidgedMulti::SetWeightAmplitude ); + } + }; + }; +} diff --git a/deps/FastNoise2/include/FastNoise/Generators/Fractal.inl b/deps/FastNoise2/include/FastNoise/Generators/Fractal.inl new file mode 100644 index 0000000..10c8b50 --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/Fractal.inl @@ -0,0 +1,128 @@ +#include "FastSIMD/InlInclude.h" + +#include "Fractal.h" + +template +class FS_T, FS> : public virtual FastNoise::Fractal, public FS_T +{ + +}; + +template +class FS_T : public virtual FastNoise::FractalFBm, public FS_T, FS> +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + float32v gain = this->GetSourceValue( mGain , seed, pos... ); + float32v sum = this->GetSourceValue( mSource, seed, pos... ); + + float32v lacunarity( mLacunarity ); + float32v amp( 1 ); + + for( int i = 1; i < mOctaves; i++ ) + { + seed -= int32v( -1 ); + amp *= gain; + sum += this->GetSourceValue( mSource, seed, (pos *= lacunarity)... ) * amp; + } + + return sum * float32v( mFractalBounding ); + } +}; + +template +class FS_T : public virtual FastNoise::FractalBillow, public FS_T, FS> +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + float32v sum = FS_Abs_f32( this->GetSourceValue( mSource, seed, pos... ) ) * float32v( 2 ) - float32v( 1 ); + float32v gain = this->GetSourceValue( mGain, seed, pos... ); + + float32v lacunarity( mLacunarity ); + float32v amp( 1 ); + + for( int i = 1; i < mOctaves; i++ ) + { + seed -= int32v( -1 ); + amp *= gain; + sum += (FS_Abs_f32(this->GetSourceValue( mSource, seed, (pos *= lacunarity)... ) ) * float32v( 2 ) - float32v( 1 )) * amp; + } + + return sum * float32v( mFractalBounding ); + } +}; + +template +class FS_T : public virtual FastNoise::FractalRidged, public FS_T, FS> +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT(int32v seed, P... pos) const + { + float32v sum = float32v( 1 ) - FS_Abs_f32( this->GetSourceValue( mSource, seed, pos... ) ); + float32v gain = this->GetSourceValue( mGain, seed, pos... ); + + float32v lacunarity( mLacunarity ); + float32v amp( 1 ); + + for( int i = 1; i < mOctaves; i++ ) + { + seed -= int32v( -1 ); + amp *= gain; + sum -= (float32v( 1 ) - FS_Abs_f32( this->GetSourceValue( mSource, seed, (pos *= lacunarity)... ) )) * amp; + } + + return sum; + } +}; + +template +class FS_T : public virtual FastNoise::FractalRidgedMulti, public FS_T, FS> +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + float32v offset( 1 ); + float32v sum = offset - FS_Abs_f32( this->GetSourceValue( mSource, seed, pos... ) ); + float32v gain = this->GetSourceValue( mGain, seed, pos... ) * float32v( 6 ); + + float32v lacunarity( mLacunarity ); + float32v amp = sum; + + float32v weightAmp( mWeightAmp ); + float32v weight = weightAmp; + float32v totalWeight( 1.0f ); + + for( int i = 1; i < mOctaves; i++ ) + { + amp *= gain; + amp = FS_Min_f32( FS_Max_f32( amp, float32v( 0 ) ), float32v( 1 ) ); + + seed -= int32v( -1 ); + float32v value = offset - FS_Abs_f32( this->GetSourceValue( mSource, seed, (pos *= lacunarity)... )); + + value *= amp; + amp = value; + + float32v weightRecip = FS_Reciprocal_f32( float32v( weight ) ); + sum += value * weightRecip; + totalWeight += weightRecip; + weight *= weightAmp; + } + + return sum * float32v( mWeightBounding ) - offset; + } +}; diff --git a/deps/FastNoise2/include/FastNoise/Generators/Generator.h b/deps/FastNoise2/include/FastNoise/Generators/Generator.h new file mode 100644 index 0000000..a4738c9 --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/Generator.h @@ -0,0 +1,149 @@ +#pragma once +#include +#include + +#include "FastNoise/FastNoiseMetadata.h" + +namespace FastNoise +{ + enum class Dim + { + X, Y, Z, W, + Count + }; + + enum class DistanceFunction + { + Euclidean, + EuclideanSquared, + Manhattan, + Hybrid, + }; + + struct OutputMinMax + { + float min = INFINITY; + float max = -INFINITY; + + OutputMinMax& operator <<( float v ) + { + min = fminf( min, v ); + max = fmaxf( max, v ); + return *this; + } + + OutputMinMax& operator <<( const OutputMinMax& v ) + { + min = fminf( min, v.min ); + max = fmaxf( max, v.max ); + return *this; + } + }; + + template + struct BaseSource + { + using Type = T; + + SmartNode base; + void* simdGeneratorPtr = nullptr; + + protected: + BaseSource() = default; + }; + + template + struct GeneratorSourceT : BaseSource + { }; + + template + struct HybridSourceT : BaseSource + { + float constant; + + HybridSourceT( float f = 0.0f ) + { + constant = f; + } + }; + + class Generator + { + public: + using Metadata = FastNoise::Metadata; + friend Metadata; + + virtual ~Generator() = default; + + virtual FastSIMD::eLevel GetSIMDLevel() const = 0; + virtual const Metadata* GetMetadata() const = 0; + + virtual OutputMinMax GenUniformGrid2D( float* noiseOut, + int32_t xStart, int32_t yStart, + int32_t xSize, int32_t ySize, + float frequency, int32_t seed ) const = 0; + + virtual OutputMinMax GenUniformGrid3D( float* noiseOut, + int32_t xStart, int32_t yStart, int32_t zStart, + int32_t xSize, int32_t ySize, int32_t zSize, + float frequency, int32_t seed ) const = 0; + + virtual OutputMinMax GenPositionArray2D( float* noiseOut, int32_t count, + const float* xPosArray, const float* yPosArray, + float xOffset, float yOffset, int32_t seed ) const = 0; + + virtual OutputMinMax GenPositionArray3D( float* noiseOut, int32_t count, + const float* xPosArray, const float* yPosArray, const float* zPosArray, + float xOffset, float yOffset, float zOffset, int32_t seed ) const = 0; + + virtual OutputMinMax GenTileable2D( float* noiseOut, + int32_t xSize, int32_t ySize, + float frequency, int32_t seed ) const = 0; + + + protected: + template + void SetSourceMemberVariable( BaseSource& memberVariable, SmartNodeArg gen ) + { + static_assert( std::is_base_of_v ); + assert( gen.get() ); + assert( GetSIMDLevel() == gen->GetSIMDLevel() ); // Ensure that all SIMD levels match + + memberVariable.base = gen; + SetSourceSIMDPtr( dynamic_cast( gen.get() ), &memberVariable.simdGeneratorPtr ); + } + + private: + virtual void SetSourceSIMDPtr( Generator* base, void** simdPtr ) = 0; + }; + + using GeneratorSource = GeneratorSourceT; + using HybridSource = HybridSourceT; + + template + struct PerDimensionVariable + { + using Type = T; + + T varArray[(int)Dim::Count]; + + template + PerDimensionVariable( U value = 0 ) + { + for( T& element : varArray ) + { + element = value; + } + } + + T& operator[]( size_t i ) + { + return varArray[i]; + } + + const T& operator[]( size_t i ) const + { + return varArray[i]; + } + }; +} diff --git a/deps/FastNoise2/include/FastNoise/Generators/Generator.inl b/deps/FastNoise2/include/FastNoise/Generators/Generator.inl new file mode 100644 index 0000000..894960d --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/Generator.inl @@ -0,0 +1,343 @@ +#include +#include +#include "FastSIMD/InlInclude.h" + +#include "Generator.h" + +#ifdef FS_SIMD_CLASS +#pragma warning( disable:4250 ) +#endif + +template +class FS_T : public virtual FastNoise::Generator +{ + FASTSIMD_DECLARE_FS_TYPES; + +public: + virtual float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y ) const = 0; + virtual float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z ) const = 0; + virtual float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z, float32v w ) const { return Gen( seed, x, y, z ); }; + +#define FASTNOISE_IMPL_GEN_T\ + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y ) const override { return GenT( seed, x, y ); }\ + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z ) const override { return GenT( seed, x, y, z ); }\ + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z, float32v w ) const override { return GenT( seed, x, y, z, w ); } + + FastSIMD::eLevel GetSIMDLevel() const final + { + return FS::SIMD_Level; + } + + using VoidPtrStorageType = FS_T*; + + void SetSourceSIMDPtr( Generator* base, void** simdPtr ) final + { + auto simd = dynamic_cast( base ); + assert( simd ); + *simdPtr = reinterpret_cast( simd ); + } + + template + FS_INLINE float32v FS_VECTORCALL GetSourceValue( const FastNoise::HybridSourceT& memberVariable, int32v seed, POS... pos ) const + { + if( memberVariable.simdGeneratorPtr ) + { + auto simdGen = reinterpret_cast( memberVariable.simdGeneratorPtr ); + + return simdGen->Gen( seed, pos... ); + } + return float32v( memberVariable.constant ); + } + + template + FS_INLINE float32v FS_VECTORCALL GetSourceValue( const FastNoise::GeneratorSourceT& memberVariable, int32v seed, POS... pos ) const + { + assert( memberVariable.simdGeneratorPtr ); + auto simdGen = reinterpret_cast( memberVariable.simdGeneratorPtr ); + + return simdGen->Gen( seed, pos... ); + } + + template + FS_INLINE const FS_T* GetSourceSIMD( const FastNoise::GeneratorSourceT& memberVariable ) const + { + assert( memberVariable.simdGeneratorPtr ); + auto simdGen = reinterpret_cast( memberVariable.simdGeneratorPtr ); + + auto simdT = static_cast*>( simdGen ); + return simdT; + } + + FastNoise::OutputMinMax GenUniformGrid2D( float* noiseOut, int32_t xStart, int32_t yStart, int32_t xSize, int32_t ySize, float frequency, int32_t seed ) const final + { + assert( xSize >= (int32_t)FS_Size_32() ); + + float32v min( INFINITY ); + float32v max( -INFINITY ); + + int32v xIdx( xStart ); + int32v yIdx( yStart ); + + float32v freqV( frequency ); + + int32v xSizeV( xSize ); + int32v xMax = xSizeV + xIdx + int32v( -1 ); + + size_t totalValues = xSize * ySize; + size_t index = 0; + + xIdx += int32v::FS_Incremented(); + + while( index < totalValues - FS_Size_32() ) + { + float32v xPos = FS_Converti32_f32( xIdx ) * freqV; + float32v yPos = FS_Converti32_f32( yIdx ) * freqV; + + float32v gen = Gen( int32v( seed ), xPos, yPos ); + FS_Store_f32( &noiseOut[index], gen ); + +#if FASTNOISE_CALC_MIN_MAX + min = FS_Min_f32( min, gen ); + max = FS_Max_f32( max, gen ); +#endif + + index += FS_Size_32(); + xIdx += int32v( FS_Size_32() ); + + mask32v xReset = xIdx > xMax; + yIdx = FS_MaskedIncrement_i32( yIdx, xReset ); + xIdx = FS_MaskedSub_i32( xIdx, xSizeV, xReset ); + } + + float32v xPos = FS_Converti32_f32( xIdx ) * freqV; + float32v yPos = FS_Converti32_f32( yIdx ) * freqV; + + float32v gen = Gen( int32v( seed ), xPos, yPos ); + + return DoRemaining( noiseOut, totalValues, index, min, max, gen ); + } + + FastNoise::OutputMinMax GenUniformGrid3D( float* noiseOut, int32_t xStart, int32_t yStart, int32_t zStart, int32_t xSize, int32_t ySize, int32_t zSize, float frequency, int32_t seed ) const final + { + assert( xSize >= (int32_t)FS_Size_32() ); + + float32v min( INFINITY ); + float32v max( -INFINITY ); + + int32v xIdx( xStart ); + int32v yIdx( yStart ); + int32v zIdx( zStart ); + + float32v freqV( frequency ); + + int32v xSizeV( xSize ); + int32v xMax = xSizeV + xIdx + int32v( -1 ); + int32v ySizeV( ySize ); + int32v yMax = ySizeV + yIdx + int32v( -1 ); + + size_t totalValues = xSize * ySize * zSize; + size_t index = 0; + + xIdx += int32v::FS_Incremented(); + + while( index < totalValues - FS_Size_32() ) + { + float32v xPos = FS_Converti32_f32( xIdx ) * freqV; + float32v yPos = FS_Converti32_f32( yIdx ) * freqV; + float32v zPos = FS_Converti32_f32( zIdx ) * freqV; + + float32v gen = Gen( int32v( seed ), xPos, yPos, zPos ); + FS_Store_f32( &noiseOut[index], gen ); + +#if FASTNOISE_CALC_MIN_MAX + min = FS_Min_f32( min, gen ); + max = FS_Max_f32( max, gen ); +#endif + + index += FS_Size_32(); + xIdx += int32v( FS_Size_32() ); + + mask32v xReset = xIdx > xMax; + yIdx = FS_MaskedIncrement_i32( yIdx, xReset ); + xIdx = FS_MaskedSub_i32( xIdx, xSizeV, xReset ); + + mask32v yReset = yIdx > yMax; + zIdx = FS_MaskedIncrement_i32( zIdx, yReset ); + yIdx = FS_MaskedSub_i32( yIdx, ySizeV, yReset ); + } + + float32v xPos = FS_Converti32_f32( xIdx ) * freqV; + float32v yPos = FS_Converti32_f32( yIdx ) * freqV; + float32v zPos = FS_Converti32_f32( zIdx ) * freqV; + + float32v gen = Gen( int32v( seed ), xPos, yPos, zPos ); + + return DoRemaining( noiseOut, totalValues, index, min, max, gen ); + } + + FastNoise::OutputMinMax GenPositionArray2D( float* noiseOut, int32_t count, const float* xPosArray, const float* yPosArray, float xOffset, float yOffset, int32_t seed ) const final + { + float32v min( INFINITY ); + float32v max( -INFINITY ); + + size_t index = 0; + while( index < count - FS_Size_32() ) + { + float32v xPos = float32v( xOffset ) + FS_Load_f32( &xPosArray[index] ); + float32v yPos = float32v( yOffset ) + FS_Load_f32( &yPosArray[index] ); + + float32v gen = Gen( int32v( seed ), xPos, yPos ); + FS_Store_f32( &noiseOut[index], gen ); + +#if FASTNOISE_CALC_MIN_MAX + min = FS_Min_f32( min, gen ); + max = FS_Max_f32( max, gen ); +#endif + index += FS_Size_32(); + } + + float32v xPos = float32v( xOffset ) + FS_Load_f32( &xPosArray[index] ); + float32v yPos = float32v( yOffset ) + FS_Load_f32( &yPosArray[index] ); + + float32v gen = Gen( int32v( seed ), xPos, yPos ); + + return DoRemaining( noiseOut, count, index, min, max, gen ); + } + + FastNoise::OutputMinMax GenPositionArray3D( float* noiseOut, int32_t count, const float* xPosArray, const float* yPosArray, const float* zPosArray, float xOffset, float yOffset, float zOffset, int32_t seed ) const final + { + float32v min( INFINITY ); + float32v max( -INFINITY ); + + int32_t index = 0; + while( index < int64_t(count) - FS_Size_32() ) + { + float32v xPos = float32v( xOffset ) + FS_Load_f32( &xPosArray[index] ); + float32v yPos = float32v( yOffset ) + FS_Load_f32( &yPosArray[index] ); + float32v zPos = float32v( zOffset ) + FS_Load_f32( &zPosArray[index] ); + + float32v gen = Gen( int32v( seed ), xPos, yPos, zPos ); + FS_Store_f32( &noiseOut[index], gen ); + +#if FASTNOISE_CALC_MIN_MAX + min = FS_Min_f32( min, gen ); + max = FS_Max_f32( max, gen ); +#endif + index += FS_Size_32(); + } + + float32v xPos = float32v( xOffset ) + FS_Load_f32( &xPosArray[index] ); + float32v yPos = float32v( yOffset ) + FS_Load_f32( &yPosArray[index] ); + float32v zPos = float32v( zOffset ) + FS_Load_f32( &zPosArray[index] ); + + float32v gen = Gen( int32v( seed ), xPos, yPos, zPos ); + + return DoRemaining( noiseOut, count, index, min, max, gen ); + } + + FastNoise::OutputMinMax GenTileable2D( float* noiseOut, int32_t xSize, int32_t ySize, float frequency, int32_t seed ) const final + { + assert( xSize >= (int32_t)FS_Size_32() ); + + float32v min( INFINITY ); + float32v max( -INFINITY ); + + int32v xIdx( 0 ); + int32v yIdx( 0 ); + + int32v xSizeV( xSize ); + int32v ySizeV( ySize ); + int32v xMax = xSizeV + xIdx + int32v( -1 ); + + size_t totalValues = xSize * ySize; + size_t index = 0; + + float pi2Recip( 0.15915493667f ); + float xSizePi = (float)xSize * pi2Recip; + float ySizePi = (float)ySize * pi2Recip; + float32v xFreq = float32v( frequency * xSizePi ); + float32v yFreq = float32v( frequency * ySizePi ); + float32v xMul = float32v( 1 / xSizePi ); + float32v yMul = float32v( 1 / ySizePi ); + + xIdx += int32v::FS_Incremented(); + + while( index < totalValues - FS_Size_32() ) + { + float32v xF = FS_Converti32_f32( xIdx ) * xMul; + float32v yF = FS_Converti32_f32( yIdx ) * yMul; + + float32v xPos = FS_Cos_f32( xF ) * xFreq; + float32v yPos = FS_Cos_f32( yF ) * yFreq; + float32v zPos = FS_Sin_f32( xF ) * xFreq; + float32v wPos = FS_Sin_f32( yF ) * yFreq; + + float32v gen = Gen( int32v( seed ), xPos, yPos, zPos, wPos ); + FS_Store_f32( &noiseOut[index], gen ); + +#if FASTNOISE_CALC_MIN_MAX + min = FS_Min_f32( min, gen ); + max = FS_Max_f32( max, gen ); +#endif + + index += FS_Size_32(); + xIdx += int32v( FS_Size_32() ); + + mask32v xReset = xIdx > xMax; + yIdx = FS_MaskedIncrement_i32( yIdx, xReset ); + xIdx = FS_MaskedSub_i32( xIdx, xSizeV, xReset ); + } + + float32v xF = FS_Converti32_f32( xIdx ) * xMul; + float32v yF = FS_Converti32_f32( yIdx ) * yMul; + + float32v xPos = FS_Cos_f32( xF ) * xFreq; + float32v yPos = FS_Cos_f32( yF ) * yFreq; + float32v zPos = FS_Sin_f32( xF ) * xFreq; + float32v wPos = FS_Sin_f32( yF ) * yFreq; + + float32v gen = Gen( int32v( seed ), xPos, yPos, zPos, wPos ); + + return DoRemaining( noiseOut, totalValues, index, min, max, gen ); + } + +private: + static FS_INLINE FastNoise::OutputMinMax DoRemaining( float* noiseOut, size_t totalValues, size_t index, float32v min, float32v max, float32v finalGen ) + { + FastNoise::OutputMinMax minMax; + size_t remaining = totalValues - index; + + if( remaining == FS_Size_32() ) + { + FS_Store_f32( &noiseOut[index], finalGen ); + +#if FASTNOISE_CALC_MIN_MAX + min = FS_Min_f32( min, finalGen ); + max = FS_Max_f32( max, finalGen ); +#endif + } + else + { + std::memcpy( &noiseOut[index], &finalGen, remaining * sizeof( int32_t ) ); + +#if FASTNOISE_CALC_MIN_MAX + do + { + minMax << noiseOut[index]; + } + while( ++index < totalValues ); +#endif + } + +#if FASTNOISE_CALC_MIN_MAX + float* minP = reinterpret_cast(&min); + float* maxP = reinterpret_cast(&max); + for( size_t i = 0; i < FS_Size_32(); i++ ) + { + minMax << FastNoise::OutputMinMax{ minP[i], maxP[i] }; + } +#endif + + return minMax; + } +}; diff --git a/deps/FastNoise2/include/FastNoise/Generators/Modifiers.h b/deps/FastNoise2/include/FastNoise/Generators/Modifiers.h new file mode 100644 index 0000000..9634ebf --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/Modifiers.h @@ -0,0 +1,321 @@ +#pragma once +#include "Generator.h" + +namespace FastNoise +{ + class DomainScale : public virtual Generator + { + public: + void SetSource( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mSource, gen ); } + void SetScale( float value ) { mScale = value; } + + protected: + GeneratorSource mSource; + float mScale = 1.0f; + + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Modifiers" ); + this->AddGeneratorSource( "Source", &DomainScale::SetSource ); + this->AddVariable( "Scale", 1.0f, &DomainScale::SetScale ); + } + }; + }; + + class DomainOffset : public virtual Generator + { + public: + void SetSource( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mSource, gen ); } + + template + void SetOffset( float value ) { mOffset[(int)D] = value; } + + template + void SetOffset( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mOffset[(int)D], gen ); } + + protected: + GeneratorSource mSource; + PerDimensionVariable mOffset; + + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Modifiers" ); + this->AddGeneratorSource( "Source", &DomainOffset::SetSource ); + this->AddPerDimensionHybridSource( "Offset", 0.0f, []( DomainOffset* p ) { return std::ref( p->mOffset ); } ); + } + }; + }; + + + class DomainRotate : public virtual Generator + { + public: + void SetSource( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mSource, gen ); } + + void SetYaw( float value ) { mYawCos = cosf( value ); mYawSin = sinf( value ); CalculateRotation(); } + void SetPitch( float value ) { mPitchCos = cosf( value ); mPitchSin = sinf( value ); CalculateRotation(); } + void SetRoll( float value ) { mRollCos = cosf( value ); mRollSin = sinf( value ); CalculateRotation(); } + + protected: + GeneratorSource mSource; + float mYawCos = 1.0f; + float mYawSin = 0.0f; + float mPitchCos = 1.0f; + float mPitchSin = 0.0f; + float mRollCos = 1.0f; + float mRollSin = 0.0f; + + float mXa = 1.0f; + float mXb = 0.0f; + float mXc = 0.0f; + float mYa = 0.0f; + float mYb = 1.0f; + float mYc = 0.0f; + float mZa = 0.0f; + float mZb = 0.0f; + float mZc = 1.0f; + + void CalculateRotation() + { + mXa = mYawCos * mPitchCos; + mXb = mYawCos * mPitchSin * mRollSin - mYawSin * mRollCos; + mXc = mYawCos * mPitchSin * mRollCos + mYawSin * mRollSin; + + mYa = mYawSin * mPitchCos; + mYb = mYawSin * mPitchSin * mRollSin + mYawCos * mRollCos; + mYc = mYawSin * mPitchSin * mRollCos - mYawCos * mRollSin; + + mZa = -mPitchSin; + mZb = mPitchCos * mRollSin; + mZc = mPitchCos * mRollCos; + } + + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Modifiers" ); + this->AddGeneratorSource( "Source", &DomainRotate::SetSource ); + this->AddVariable( "Yaw", 0.0f, &DomainRotate::SetYaw ); + this->AddVariable( "Pitch", 0.0f, &DomainRotate::SetPitch ); + this->AddVariable( "Roll", 0.0f, &DomainRotate::SetRoll ); + } + }; + }; + + class SeedOffset : public virtual Generator + { + public: + void SetSource( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mSource, gen ); } + void SetOffset( int32_t value ) { mOffset = value; } + + protected: + GeneratorSource mSource; + int32_t mOffset = 1; + + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Modifiers" ); + this->AddGeneratorSource( "Source", &SeedOffset::SetSource ); + this->AddVariable( "Seed Offset", 1, &SeedOffset::SetOffset ); + } + }; + }; + + class Remap : public virtual Generator + { + public: + void SetSource( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mSource, gen ); } + void SetRemap( float fromMin, float fromMax, float toMin, float toMax ) { mFromMin = fromMin; mFromMax = fromMax; mToMin = toMin; mToMax = toMax; } + + protected: + GeneratorSource mSource; + float mFromMin = -1.0f; + float mFromMax = 1.0f; + float mToMin = 0.0f; + float mToMax = 1.0f; + + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Modifiers" ); + this->AddGeneratorSource( "Source", &Remap::SetSource ); + + this->AddVariable( "From Min", -1.0f, + []( Remap* p, float f ) + { + p->mFromMin = f; + }); + + this->AddVariable( "From Max", 1.0f, + []( Remap* p, float f ) + { + p->mFromMax = f; + }); + + this->AddVariable( "To Min", 0.0f, + []( Remap* p, float f ) + { + p->mToMin = f; + }); + + this->AddVariable( "To Max", 1.0f, + []( Remap* p, float f ) + { + p->mToMax = f; + }); + } + }; + }; + + class ConvertRGBA8 : public virtual Generator + { + public: + void SetSource( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mSource, gen ); } + void SetMinMax( float min, float max ) { mMin = min; mMax = max; } + + protected: + GeneratorSource mSource; + float mMin = -1.0f; + float mMax = 1.0f; + + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Modifiers" ); + this->AddGeneratorSource( "Source", &ConvertRGBA8::SetSource ); + + this->AddVariable( "Min", -1.0f, + []( ConvertRGBA8* p, float f ) + { + p->mMin = f; + }); + + this->AddVariable( "Max", 1.0f, + []( ConvertRGBA8* p, float f ) + { + p->mMax = f; + }); + } + }; + }; + + class Terrace : public virtual Generator + { + public: + void SetSource( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mSource, gen ); } + void SetMultiplier( float multiplier ) { mMultiplier = multiplier; mMultiplierRecip = 1 / multiplier; } + void SetSmoothness( float smoothness ) { mSmoothness = smoothness; if( mSmoothness != 0.0f ) mSmoothnessRecip = 1 + 1 / smoothness; } + + protected: + GeneratorSource mSource; + float mMultiplier = 1.0f; + float mMultiplierRecip = 1.0f; + float mSmoothness = 0.0f; + float mSmoothnessRecip = 0.0f; + + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Modifiers" ); + this->AddGeneratorSource( "Source", &Terrace::SetSource ); + this->AddVariable( "Multiplier", 1.0f, &Terrace::SetMultiplier ); + this->AddVariable( "Smoothness", 0.0f, &Terrace::SetSmoothness ); + } + }; + }; + + class DomainAxisScale : public virtual Generator + { + public: + void SetSource( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mSource, gen ); } + + template + void SetScale( float value ) { mScale[(int)D] = value; } + + protected: + GeneratorSource mSource; + PerDimensionVariable mScale; + + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Modifiers" ); + this->AddGeneratorSource( "Source", &DomainAxisScale::SetSource ); + this->AddPerDimensionVariable( "Scale", 1.0f, []( DomainAxisScale* p ) { return std::ref( p->mScale ); } ); + } + }; + }; + + class AddDimension : public virtual Generator + { + public: + void SetSource( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mSource, gen ); } + void SetNewDimensionPosition( float value ) { mNewDimensionPosition = value; } + void SetNewDimensionPosition( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mNewDimensionPosition, gen ); } + + protected: + GeneratorSource mSource; + HybridSource mNewDimensionPosition; + + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Modifiers" ); + this->AddGeneratorSource( "Source", &AddDimension::SetSource ); + this->AddHybridSource( "New Dimension Position", 0.0f, &AddDimension::SetNewDimensionPosition, &AddDimension::SetNewDimensionPosition ); + } + }; + }; + + class RemoveDimension : public virtual Generator + { + public: + void SetSource( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mSource, gen ); } + void SetRemoveDimension( Dim dimension ) { mRemoveDimension = dimension; } + + protected: + GeneratorSource mSource; + Dim mRemoveDimension = Dim::Y; + + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Modifiers" ); + this->AddGeneratorSource( "Source", &RemoveDimension::SetSource ); + this->AddVariableEnum( "Remove Dimension", Dim::Y, &RemoveDimension::SetRemoveDimension, "X", "Y", "Z", "W" ); + } + }; + }; + + class GeneratorCache : public virtual Generator + { + public: + void SetSource( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mSource, gen ); } + + protected: + GeneratorSource mSource; + + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Modifiers" ); + this->AddGeneratorSource( "Source", &GeneratorCache::SetSource ); + } + }; + }; + +} diff --git a/deps/FastNoise2/include/FastNoise/Generators/Modifiers.inl b/deps/FastNoise2/include/FastNoise/Generators/Modifiers.inl new file mode 100644 index 0000000..99cd589 --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/Modifiers.inl @@ -0,0 +1,277 @@ +#include "FastSIMD/InlInclude.h" + +#include "Modifiers.h" + +template +class FS_T : public virtual FastNoise::DomainScale, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + return this->GetSourceValue( mSource, seed, (pos * float32v( mScale ))... ); + } +}; + +template +class FS_T : public virtual FastNoise::DomainOffset, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + return [this, seed]( std::remove_reference_t

... sourcePos, std::remove_reference_t

... offset ) + { + size_t idx = 0; + ((offset += this->GetSourceValue( mOffset[idx++], seed, sourcePos... )), ...); + + return this->GetSourceValue( mSource, seed, offset... ); + } (pos..., pos...); + } +}; + +template +class FS_T : public virtual FastNoise::DomainRotate, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y ) const final + { + if( mPitchSin == 0.0f && mRollSin == 0.0f ) + { + return this->GetSourceValue( mSource, seed, + FS_FNMulAdd_f32( y, float32v( mYawSin ), x * float32v( mYawCos ) ), + FS_FMulAdd_f32( x, float32v( mYawSin ), y * float32v( mYawCos ) ) ); + } + + return Gen( seed, x, y, float32v( 0 ) ); + } + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z ) const final + { + return this->GetSourceValue( mSource, seed, + FS_FMulAdd_f32( x, float32v( mXa ), FS_FMulAdd_f32( y, float32v( mXb ), z * float32v( mXc ) ) ), + FS_FMulAdd_f32( x, float32v( mYa ), FS_FMulAdd_f32( y, float32v( mYb ), z * float32v( mYc ) ) ), + FS_FMulAdd_f32( x, float32v( mZa ), FS_FMulAdd_f32( y, float32v( mZb ), z * float32v( mZc ) ) ) ); + } + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z, float32v w ) const final + { + // No rotation for 4D yet + return this->GetSourceValue( mSource, seed, x, y, z, w ); + } +}; + +template +class FS_T : public virtual FastNoise::SeedOffset, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + return this->GetSourceValue( mSource, seed + int32v( mOffset ), pos... ); + } +}; + +template +class FS_T : public virtual FastNoise::Remap, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + float32v source = this->GetSourceValue( mSource, seed, pos... ); + + return float32v( mToMin ) + (( source - float32v( mFromMin ) ) / float32v( mFromMax - mFromMin ) * float32v( mToMax - mToMin )); + } +}; + +template +class FS_T : public virtual FastNoise::ConvertRGBA8, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + float32v source = this->GetSourceValue( mSource, seed, pos... ); + + source = FS_Min_f32( source, float32v( mMax )); + source = FS_Max_f32( source, float32v( mMin )); + source -= float32v( mMin ); + + source *= float32v( 255.0f / (mMax - mMin) ); + + int32v byteVal = FS_Convertf32_i32( source ); + + int32v output = int32v( 255 << 24 ); + output |= byteVal; + output |= byteVal << 8; + output |= byteVal << 16; + + return FS_Casti32_f32( output ); + } +}; + +template +class FS_T : public virtual FastNoise::Terrace, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + float32v source = this->GetSourceValue( mSource, seed, pos... ); + + source *= float32v( mMultiplier ); + float32v rounded = FS_Round_f32( source ); + + if( mSmoothness != 0.0f ) + { + float32v diff = rounded - source; + mask32v diffSign = diff < float32v( 0 ); + + diff = FS_Abs_f32( diff ); + diff = float32v( 0.5f ) - diff; + + diff *= float32v( mSmoothnessRecip ); + diff = FS_Min_f32( diff, float32v( 0.5f ) ); + diff = FS_Select_f32( diffSign, float32v( 0.5f ) - diff, diff - float32v( 0.5f ) ); + + rounded += diff; + } + + return rounded * float32v( mMultiplierRecip ); + } +}; + +template +class FS_T : public virtual FastNoise::DomainAxisScale, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + size_t idx = 0; + ((pos *= float32v( mScale[idx++] )), ...); + + return this->GetSourceValue( mSource, seed, pos... ); + } +}; + +template +class FS_T : public virtual FastNoise::AddDimension, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + if constexpr( sizeof...(P) == (size_t)FastNoise::Dim::Count ) + { + return this->GetSourceValue( mSource, seed, pos... ); + } + else + { + return this->GetSourceValue( mSource, seed, pos..., this->GetSourceValue( mNewDimensionPosition, seed, pos... ) ); + } + } +}; + +template +class FS_T : public virtual FastNoise::RemoveDimension, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y ) const final + { + return this->GetSourceValue( mSource, seed, x, y ); + } + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z ) const final + { + switch( mRemoveDimension ) + { + case FastNoise::Dim::X: + return this->GetSourceValue( mSource, seed, y, z ); + case FastNoise::Dim::Y: + return this->GetSourceValue( mSource, seed, x, z ); + case FastNoise::Dim::Z: + return this->GetSourceValue( mSource, seed, x, y ); + default: + return this->GetSourceValue( mSource, seed, x, y, z ); + } + } + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z, float32v w ) const final + { + switch( mRemoveDimension ) + { + case FastNoise::Dim::X: + return this->GetSourceValue( mSource, seed, y, z, w ); + case FastNoise::Dim::Y: + return this->GetSourceValue( mSource, seed, x, z, w ); + case FastNoise::Dim::Z: + return this->GetSourceValue( mSource, seed, x, y, w ); + case FastNoise::Dim::W: + return this->GetSourceValue( mSource, seed, x, y, z ); + default: + return this->GetSourceValue( mSource, seed, x, y, z, w ); + } + } +}; + +template +class FS_T : public virtual FastNoise::GeneratorCache, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + FASTNOISE_IMPL_GEN_T; + + template + FS_INLINE float32v GenT( int32v seed, P... pos ) const + { + thread_local static void* CachedGenerator = nullptr; + thread_local static float32v CachedValue; + thread_local static float32v CachedPos[sizeof...( P )]; + // TLS is not always aligned, so use FS_Load/FS_Store to access SIMD types + + float32v arrayPos[] = { pos... }; + + bool isSame = (CachedGenerator == mSource.simdGeneratorPtr); + + for( size_t i = 0; i < sizeof...( P ); i++ ) + { + isSame &= !FS_AnyMask_bool( arrayPos[i] != FS_Load_f32( &CachedPos[i] ) ); + } + + if( !isSame ) + { + CachedGenerator = mSource.simdGeneratorPtr; + + float32v value = this->GetSourceValue( mSource, seed, pos... ); + FS_Store_f32( &CachedValue, value ); + + for( size_t i = 0; i < sizeof...(P); i++ ) + { + FS_Store_f32( &CachedPos[i], arrayPos[i] ); + } + + return value; + } + + return FS_Load_f32( &CachedValue ); + } +}; diff --git a/deps/FastNoise2/include/FastNoise/Generators/Perlin.h b/deps/FastNoise2/include/FastNoise/Generators/Perlin.h new file mode 100644 index 0000000..61dd849 --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/Perlin.h @@ -0,0 +1,16 @@ +#pragma once +#include "Generator.h" + +namespace FastNoise +{ + class Perlin : public virtual Generator + { + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Coherent Noise" ); + } + }; + }; +} diff --git a/deps/FastNoise2/include/FastNoise/Generators/Perlin.inl b/deps/FastNoise2/include/FastNoise/Generators/Perlin.inl new file mode 100644 index 0000000..7351be4 --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/Perlin.inl @@ -0,0 +1,109 @@ +#include "FastSIMD/InlInclude.h" + +#include "Perlin.h" +#include "Utils.inl" + +template +class FS_T : public virtual FastNoise::Perlin, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y ) const final + { + float32v xs = FS_Floor_f32( x ); + float32v ys = FS_Floor_f32( y ); + + int32v x0 = FS_Convertf32_i32( xs ) * int32v( FnPrimes::X ); + int32v y0 = FS_Convertf32_i32( ys ) * int32v( FnPrimes::Y ); + int32v x1 = x0 + int32v( FnPrimes::X ); + int32v y1 = y0 + int32v( FnPrimes::Y ); + + float32v xf0 = xs = x - xs; + float32v yf0 = ys = y - ys; + float32v xf1 = xf0 - float32v( 1 ); + float32v yf1 = yf0 - float32v( 1 ); + + xs = FnUtils::InterpQuintic( xs ); + ys = FnUtils::InterpQuintic( ys ); + + return float32v( 0.579106986522674560546875f ) * FnUtils::Lerp( + FnUtils::Lerp( FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x0, y0 ), xf0, yf0 ), FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x1, y0 ), xf1, yf0 ), xs ), + FnUtils::Lerp( FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x0, y1 ), xf0, yf1 ), FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x1, y1 ), xf1, yf1 ), xs ), ys ); + } + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z ) const final + { + float32v xs = FS_Floor_f32( x ); + float32v ys = FS_Floor_f32( y ); + float32v zs = FS_Floor_f32( z ); + + int32v x0 = FS_Convertf32_i32( xs ) * int32v( FnPrimes::X ); + int32v y0 = FS_Convertf32_i32( ys ) * int32v( FnPrimes::Y ); + int32v z0 = FS_Convertf32_i32( zs ) * int32v( FnPrimes::Z ); + int32v x1 = x0 + int32v( FnPrimes::X ); + int32v y1 = y0 + int32v( FnPrimes::Y ); + int32v z1 = z0 + int32v( FnPrimes::Z ); + + float32v xf0 = xs = x - xs; + float32v yf0 = ys = y - ys; + float32v zf0 = zs = z - zs; + float32v xf1 = xf0 - float32v( 1 ); + float32v yf1 = yf0 - float32v( 1 ); + float32v zf1 = zf0 - float32v( 1 ); + + xs = FnUtils::InterpQuintic( xs ); + ys = FnUtils::InterpQuintic( ys ); + zs = FnUtils::InterpQuintic( zs ); + + return float32v( 0.964921414852142333984375f ) * FnUtils::Lerp( FnUtils::Lerp( + FnUtils::Lerp( FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x0, y0, z0 ), xf0, yf0, zf0 ), FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x1, y0, z0 ), xf1, yf0, zf0 ), xs ), + FnUtils::Lerp( FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x0, y1, z0 ), xf0, yf1, zf0 ), FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x1, y1, z0 ), xf1, yf1, zf0 ), xs ), ys ), + FnUtils::Lerp( + FnUtils::Lerp( FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x0, y0, z1 ), xf0, yf0, zf1 ), FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x1, y0, z1 ), xf1, yf0, zf1 ), xs ), + FnUtils::Lerp( FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x0, y1, z1 ), xf0, yf1, zf1 ), FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x1, y1, z1 ), xf1, yf1, zf1 ), xs ), ys ), zs ); + } + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z, float32v w ) const final + { + float32v xs = FS_Floor_f32( x ); + float32v ys = FS_Floor_f32( y ); + float32v zs = FS_Floor_f32( z ); + float32v ws = FS_Floor_f32( w ); + + int32v x0 = FS_Convertf32_i32( xs ) * int32v( FnPrimes::X ); + int32v y0 = FS_Convertf32_i32( ys ) * int32v( FnPrimes::Y ); + int32v z0 = FS_Convertf32_i32( zs ) * int32v( FnPrimes::Z ); + int32v w0 = FS_Convertf32_i32( ws ) * int32v( FnPrimes::W ); + int32v x1 = x0 + int32v( FnPrimes::X ); + int32v y1 = y0 + int32v( FnPrimes::Y ); + int32v z1 = z0 + int32v( FnPrimes::Z ); + int32v w1 = w0 + int32v( FnPrimes::W ); + + float32v xf0 = xs = x - xs; + float32v yf0 = ys = y - ys; + float32v zf0 = zs = z - zs; + float32v wf0 = ws = w - ws; + float32v xf1 = xf0 - float32v( 1 ); + float32v yf1 = yf0 - float32v( 1 ); + float32v zf1 = zf0 - float32v( 1 ); + float32v wf1 = wf0 - float32v( 1 ); + + xs = FnUtils::InterpQuintic( xs ); + ys = FnUtils::InterpQuintic( ys ); + zs = FnUtils::InterpQuintic( zs ); + ws = FnUtils::InterpQuintic( ws ); + + return float32v( 0.964921414852142333984375f ) * FnUtils::Lerp( FnUtils::Lerp( FnUtils::Lerp( + FnUtils::Lerp( FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x0, y0, z0, w0 ), xf0, yf0, zf0, wf0 ), FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x1, y0, z0, w0 ), xf1, yf0, zf0, wf0 ), xs ), + FnUtils::Lerp( FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x0, y1, z0, w0 ), xf0, yf1, zf0, wf0 ), FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x1, y1, z0, w0 ), xf1, yf1, zf0, wf0 ), xs ), ys ), + FnUtils::Lerp( + FnUtils::Lerp( FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x0, y0, z1, w0 ), xf0, yf0, zf1, wf0 ), FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x1, y0, z1, w0 ), xf1, yf0, zf1, wf0 ), xs ), + FnUtils::Lerp( FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x0, y1, z1, w0 ), xf0, yf1, zf1, wf0 ), FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x1, y1, z1, w0 ), xf1, yf1, zf1, wf0 ), xs ), ys ), zs ), + FnUtils::Lerp( FnUtils::Lerp( + FnUtils::Lerp( FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x0, y0, z0, w1 ), xf0, yf0, zf0, wf1 ), FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x1, y0, z0, w1 ), xf1, yf0, zf0, wf1 ), xs ), + FnUtils::Lerp( FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x0, y1, z0, w1 ), xf0, yf1, zf0, wf1 ), FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x1, y1, z0, w1 ), xf1, yf1, zf0, wf1 ), xs ), ys ), + FnUtils::Lerp( + FnUtils::Lerp( FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x0, y0, z1, w1 ), xf0, yf0, zf1, wf1 ), FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x1, y0, z1, w1 ), xf1, yf0, zf1, wf1 ), xs ), + FnUtils::Lerp( FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x0, y1, z1, w1 ), xf0, yf1, zf1, wf1 ), FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, x1, y1, z1, w1 ), xf1, yf1, zf1, wf1 ), xs ), ys ), zs ), ws ); + } +}; diff --git a/deps/FastNoise2/include/FastNoise/Generators/Simplex.h b/deps/FastNoise2/include/FastNoise/Generators/Simplex.h new file mode 100644 index 0000000..5f7c981 --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/Simplex.h @@ -0,0 +1,27 @@ +#pragma once +#include "Generator.h" + +namespace FastNoise +{ + class Simplex : public virtual Generator + { + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Coherent Noise" ); + } + }; + }; + + class OpenSimplex2 : public virtual Generator + { + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Coherent Noise" ); + } + }; + }; +} diff --git a/deps/FastNoise2/include/FastNoise/Generators/Simplex.inl b/deps/FastNoise2/include/FastNoise/Generators/Simplex.inl new file mode 100644 index 0000000..422b3c3 --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/Simplex.inl @@ -0,0 +1,373 @@ +#include "FastSIMD/InlInclude.h" + +#include "Simplex.h" +#include "Utils.inl" + +template +class FS_T : public virtual FastNoise::Simplex, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y ) const final + { + const float SQRT3 = 1.7320508075688772935274463415059f; + const float F2 = 0.5f * (SQRT3 - 1.0f); + const float G2 = (3.0f - SQRT3) / 6.0f; + + float32v f = float32v( F2 ) * (x + y); + float32v x0 = FS_Floor_f32( x + f ); + float32v y0 = FS_Floor_f32( y + f ); + + int32v i = FS_Convertf32_i32( x0 ) * int32v( FnPrimes::X ); + int32v j = FS_Convertf32_i32( y0 ) * int32v( FnPrimes::Y ); + + float32v g = float32v( G2 ) * (x0 + y0); + x0 = x - (x0 - g); + y0 = y - (y0 - g); + + mask32v i1 = x0 > y0; + //mask32v j1 = ~i1; //NMasked funcs + + float32v x1 = FS_MaskedSub_f32( x0, float32v( 1.f ), i1 ) + float32v( G2 ); + float32v y1 = FS_NMaskedSub_f32( y0, float32v( 1.f ), i1 ) + float32v( G2 ); + + float32v x2 = x0 + float32v( G2 * 2 - 1 ); + float32v y2 = y0 + float32v( G2 * 2 - 1 ); + + float32v t0 = FS_FNMulAdd_f32( x0, x0, FS_FNMulAdd_f32( y0, y0, float32v( 0.5f ) ) ); + float32v t1 = FS_FNMulAdd_f32( x1, x1, FS_FNMulAdd_f32( y1, y1, float32v( 0.5f ) ) ); + float32v t2 = FS_FNMulAdd_f32( x2, x2, FS_FNMulAdd_f32( y2, y2, float32v( 0.5f ) ) ); + + t0 = FS_Max_f32( t0, float32v( 0 ) ); + t1 = FS_Max_f32( t1, float32v( 0 ) ); + t2 = FS_Max_f32( t2, float32v( 0 ) ); + + t0 *= t0; t0 *= t0; + t1 *= t1; t1 *= t1; + t2 *= t2; t2 *= t2; + + float32v n0 = FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, i, j ), x0, y0 ); + float32v n1 = FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, FS_MaskedAdd_i32( i, int32v( FnPrimes::X ), i1 ), FS_NMaskedAdd_i32( j, int32v( FnPrimes::Y ), i1 ) ), x1, y1 ); + float32v n2 = FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, i + int32v( FnPrimes::X ), j + int32v( FnPrimes::Y ) ), x2, y2 ); + + return float32v( 38.283687591552734375f ) * FS_FMulAdd_f32( n0, t0, FS_FMulAdd_f32( n1, t1, n2 * t2 ) ); + } + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z ) const final + { + const float F3 = 1.0f / 3.0f; + const float G3 = 1.0f / 2.0f; + + float32v s = float32v( F3 ) * (x + y + z); + x += s; + y += s; + z += s; + + float32v x0 = FS_Floor_f32( x ); + float32v y0 = FS_Floor_f32( y ); + float32v z0 = FS_Floor_f32( z ); + float32v xi = x - x0; + float32v yi = y - y0; + float32v zi = z - z0; + + int32v i = FS_Convertf32_i32( x0 ) * int32v( FnPrimes::X ); + int32v j = FS_Convertf32_i32( y0 ) * int32v( FnPrimes::Y ); + int32v k = FS_Convertf32_i32( z0 ) * int32v( FnPrimes::Z ); + + mask32v x_ge_y = xi >= yi; + mask32v y_ge_z = yi >= zi; + mask32v x_ge_z = xi >= zi; + + float32v g = float32v( G3 ) * (xi + yi + zi); + x0 = xi - g; + y0 = yi - g; + z0 = zi - g; + + mask32v i1 = x_ge_y & x_ge_z; + mask32v j1 = FS_BitwiseAndNot_m32( y_ge_z, x_ge_y ); + mask32v k1 = FS_BitwiseAndNot_m32( ~x_ge_z, y_ge_z ); + + mask32v i2 = x_ge_y | x_ge_z; + mask32v j2 = ~x_ge_y | y_ge_z; + mask32v k2 = x_ge_z & y_ge_z; //NMasked + + float32v x1 = FS_MaskedSub_f32( x0, float32v( 1 ), i1 ) + float32v( G3 ); + float32v y1 = FS_MaskedSub_f32( y0, float32v( 1 ), j1 ) + float32v( G3 ); + float32v z1 = FS_MaskedSub_f32( z0, float32v( 1 ), k1 ) + float32v( G3 ); + float32v x2 = FS_MaskedSub_f32( x0, float32v( 1 ), i2 ) + float32v( G3 * 2 ); + float32v y2 = FS_MaskedSub_f32( y0, float32v( 1 ), j2 ) + float32v( G3 * 2 ); + float32v z2 = FS_NMaskedSub_f32( z0, float32v( 1 ), k2 ) + float32v( G3 * 2 ); + float32v x3 = x0 + float32v( G3 * 3 - 1 ); + float32v y3 = y0 + float32v( G3 * 3 - 1 ); + float32v z3 = z0 + float32v( G3 * 3 - 1 ); + + float32v t0 = FS_FNMulAdd_f32( x0, x0, FS_FNMulAdd_f32( y0, y0, FS_FNMulAdd_f32( z0, z0, float32v( 0.6f ) ) ) ); + float32v t1 = FS_FNMulAdd_f32( x1, x1, FS_FNMulAdd_f32( y1, y1, FS_FNMulAdd_f32( z1, z1, float32v( 0.6f ) ) ) ); + float32v t2 = FS_FNMulAdd_f32( x2, x2, FS_FNMulAdd_f32( y2, y2, FS_FNMulAdd_f32( z2, z2, float32v( 0.6f ) ) ) ); + float32v t3 = FS_FNMulAdd_f32( x3, x3, FS_FNMulAdd_f32( y3, y3, FS_FNMulAdd_f32( z3, z3, float32v( 0.6f ) ) ) ); + + t0 = FS_Max_f32( t0, float32v( 0 ) ); + t1 = FS_Max_f32( t1, float32v( 0 ) ); + t2 = FS_Max_f32( t2, float32v( 0 ) ); + t3 = FS_Max_f32( t3, float32v( 0 ) ); + + t0 *= t0; t0 *= t0; + t1 *= t1; t1 *= t1; + t2 *= t2; t2 *= t2; + t3 *= t3; t3 *= t3; + + float32v n0 = FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, i, j, k ), x0, y0, z0 ); + float32v n1 = FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, FS_MaskedAdd_i32( i, int32v( FnPrimes::X ), i1 ), FS_MaskedAdd_i32( j, int32v( FnPrimes::Y ), j1 ), FS_MaskedAdd_i32( k, int32v( FnPrimes::Z ), k1 ) ), x1, y1, z1 ); + float32v n2 = FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, FS_MaskedAdd_i32( i, int32v( FnPrimes::X ), i2 ), FS_MaskedAdd_i32( j, int32v( FnPrimes::Y ), j2 ), FS_NMaskedAdd_i32( k, int32v( FnPrimes::Z ), k2 ) ), x2, y2, z2 ); + float32v n3 = FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, i + int32v( FnPrimes::X ), j + int32v( FnPrimes::Y ), k + int32v( FnPrimes::Z ) ), x3, y3, z3 ); + + return float32v( 32.69428253173828125f ) * FS_FMulAdd_f32( n0, t0, FS_FMulAdd_f32( n1, t1, FS_FMulAdd_f32( n2, t2, n3 * t3 ) ) ); + } + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z, float32v w ) const final + { + const float SQRT5 = 2.236067977499f; + const float F4 = (SQRT5 - 1.0f) / 4.0f; + const float G4 = (5.0f - SQRT5) / 20.0f; + + float32v s = float32v( F4 ) * (x + y + z + w); + x += s; + y += s; + z += s; + w += s; + + float32v x0 = FS_Floor_f32( x ); + float32v y0 = FS_Floor_f32( y ); + float32v z0 = FS_Floor_f32( z ); + float32v w0 = FS_Floor_f32( w ); + float32v xi = x - x0; + float32v yi = y - y0; + float32v zi = z - z0; + float32v wi = w - w0; + + int32v i = FS_Convertf32_i32( x0 ) * int32v( FnPrimes::X ); + int32v j = FS_Convertf32_i32( y0 ) * int32v( FnPrimes::Y ); + int32v k = FS_Convertf32_i32( z0 ) * int32v( FnPrimes::Z ); + int32v l = FS_Convertf32_i32( w0 ) * int32v( FnPrimes::W ); + + float32v g = float32v( G4 ) * (xi + yi + zi + wi); + x0 = xi - g; + y0 = yi - g; + z0 = zi - g; + w0 = wi - g; + + int32v rankx( 0 ); + int32v ranky( 0 ); + int32v rankz( 0 ); + int32v rankw( 0 ); + + mask32v x_ge_y = x0 >= y0; + rankx = FS_MaskedIncrement_i32( rankx, x_ge_y ); + ranky = FS_MaskedIncrement_i32( ranky, ~x_ge_y ); + + mask32v x_ge_z = x0 >= z0; + rankx = FS_MaskedIncrement_i32( rankx, x_ge_z ); + rankz = FS_MaskedIncrement_i32( rankz, ~x_ge_z ); + + mask32v x_ge_w = x0 >= w0; + rankx = FS_MaskedIncrement_i32( rankx, x_ge_w ); + rankw = FS_MaskedIncrement_i32( rankw, ~x_ge_w ); + + mask32v y_ge_z = y0 >= z0; + ranky = FS_MaskedIncrement_i32( ranky, y_ge_z ); + rankz = FS_MaskedIncrement_i32( rankz, ~y_ge_z ); + + mask32v y_ge_w = y0 >= w0; + ranky = FS_MaskedIncrement_i32( ranky, y_ge_w ); + rankw = FS_MaskedIncrement_i32( rankw, ~y_ge_w ); + + mask32v z_ge_w = z0 >= w0; + rankz = FS_MaskedIncrement_i32( rankz, z_ge_w ); + rankw = FS_MaskedIncrement_i32( rankw, ~z_ge_w ); + + mask32v i1 = rankx > int32v( 2 ); + mask32v j1 = ranky > int32v( 2 ); + mask32v k1 = rankz > int32v( 2 ); + mask32v l1 = rankw > int32v( 2 ); + + mask32v i2 = rankx > int32v( 1 ); + mask32v j2 = ranky > int32v( 1 ); + mask32v k2 = rankz > int32v( 1 ); + mask32v l2 = rankw > int32v( 1 ); + + mask32v i3 = rankx > int32v( 0 ); + mask32v j3 = ranky > int32v( 0 ); + mask32v k3 = rankz > int32v( 0 ); + mask32v l3 = rankw > int32v( 0 ); + + float32v x1 = FS_MaskedSub_f32( x0, float32v( 1 ), i1 ) + float32v( G4 ); + float32v y1 = FS_MaskedSub_f32( y0, float32v( 1 ), j1 ) + float32v( G4 ); + float32v z1 = FS_MaskedSub_f32( z0, float32v( 1 ), k1 ) + float32v( G4 ); + float32v w1 = FS_MaskedSub_f32( w0, float32v( 1 ), l1 ) + float32v( G4 ); + float32v x2 = FS_MaskedSub_f32( x0, float32v( 1 ), i2 ) + float32v( G4 * 2 ); + float32v y2 = FS_MaskedSub_f32( y0, float32v( 1 ), j2 ) + float32v( G4 * 2 ); + float32v z2 = FS_MaskedSub_f32( z0, float32v( 1 ), k2 ) + float32v( G4 * 2 ); + float32v w2 = FS_MaskedSub_f32( w0, float32v( 1 ), l2 ) + float32v( G4 * 2 ); + float32v x3 = FS_MaskedSub_f32( x0, float32v( 1 ), i3 ) + float32v( G4 * 3 ); + float32v y3 = FS_MaskedSub_f32( y0, float32v( 1 ), j3 ) + float32v( G4 * 3 ); + float32v z3 = FS_MaskedSub_f32( z0, float32v( 1 ), k3 ) + float32v( G4 * 3 ); + float32v w3 = FS_MaskedSub_f32( w0, float32v( 1 ), l3 ) + float32v( G4 * 3 ); + float32v x4 = x0 + float32v( G4 * 4 - 1 ); + float32v y4 = y0 + float32v( G4 * 4 - 1 ); + float32v z4 = z0 + float32v( G4 * 4 - 1 ); + float32v w4 = w0 + float32v( G4 * 4 - 1 ); + + float32v t0 = FS_FNMulAdd_f32( x0, x0, FS_FNMulAdd_f32( y0, y0, FS_FNMulAdd_f32( z0, z0, FS_FNMulAdd_f32( w0, w0, float32v( 0.6f ) ) ) ) ); + float32v t1 = FS_FNMulAdd_f32( x1, x1, FS_FNMulAdd_f32( y1, y1, FS_FNMulAdd_f32( z1, z1, FS_FNMulAdd_f32( w1, w1, float32v( 0.6f ) ) ) ) ); + float32v t2 = FS_FNMulAdd_f32( x2, x2, FS_FNMulAdd_f32( y2, y2, FS_FNMulAdd_f32( z2, z2, FS_FNMulAdd_f32( w2, w2, float32v( 0.6f ) ) ) ) ); + float32v t3 = FS_FNMulAdd_f32( x3, x3, FS_FNMulAdd_f32( y3, y3, FS_FNMulAdd_f32( z3, z3, FS_FNMulAdd_f32( w3, w3, float32v( 0.6f ) ) ) ) ); + float32v t4 = FS_FNMulAdd_f32( x4, x4, FS_FNMulAdd_f32( y4, y4, FS_FNMulAdd_f32( z4, z4, FS_FNMulAdd_f32( w4, w4, float32v( 0.6f ) ) ) ) ); + + t0 = FS_Max_f32( t0, float32v( 0 ) ); + t1 = FS_Max_f32( t1, float32v( 0 ) ); + t2 = FS_Max_f32( t2, float32v( 0 ) ); + t3 = FS_Max_f32( t3, float32v( 0 ) ); + t4 = FS_Max_f32( t4, float32v( 0 ) ); + + t0 *= t0; t0 *= t0; + t1 *= t1; t1 *= t1; + t2 *= t2; t2 *= t2; + t3 *= t3; t3 *= t3; + t4 *= t4; t4 *= t4; + + float32v n0 = FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, i, j, k, l ), x0, y0, z0, w0 ); + float32v n1 = FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, + FS_MaskedAdd_i32( i, int32v( FnPrimes::X ), i1 ), + FS_MaskedAdd_i32( j, int32v( FnPrimes::Y ), j1 ), + FS_MaskedAdd_i32( k, int32v( FnPrimes::Z ), k1 ), + FS_MaskedAdd_i32( l, int32v( FnPrimes::W ), l1 ) ), x1, y1, z1, w1 ); + float32v n2 = FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, + FS_MaskedAdd_i32( i, int32v( FnPrimes::X ), i2 ), + FS_MaskedAdd_i32( j, int32v( FnPrimes::Y ), j2 ), + FS_MaskedAdd_i32( k, int32v( FnPrimes::Z ), k2 ), + FS_MaskedAdd_i32( l, int32v( FnPrimes::W ), l2 ) ), x2, y2, z2, w2 ); + float32v n3 = FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, + FS_MaskedAdd_i32( i, int32v( FnPrimes::X ), i3 ), + FS_MaskedAdd_i32( j, int32v( FnPrimes::Y ), j3 ), + FS_MaskedAdd_i32( k, int32v( FnPrimes::Z ), k3 ), + FS_MaskedAdd_i32( l, int32v( FnPrimes::W ), l3 ) ), x3, y3, z3, w3 ); + float32v n4 = FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, i + int32v( FnPrimes::X ), j + int32v( FnPrimes::Y ), k + int32v( FnPrimes::Z ), l + int32v( FnPrimes::W ) ), x4, y4, z4, w4 ); + + return float32v( 27.f ) * FS_FMulAdd_f32( n0, t0, FS_FMulAdd_f32( n1, t1, FS_FMulAdd_f32( n2, t2, FS_FMulAdd_f32( n3, t3, n4 * t4 ) ) ) ); + } +}; + +template +class FS_T : public virtual FastNoise::OpenSimplex2, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y ) const final + { + const float SQRT3 = 1.7320508075f; + const float F2 = 0.5f * (SQRT3 - 1.0f); + const float G2 = (3.0f - SQRT3) / 6.0f; + + float32v f = float32v( F2 ) * (x + y); + float32v x0 = FS_Floor_f32( x + f ); + float32v y0 = FS_Floor_f32( y + f ); + + int32v i = FS_Convertf32_i32( x0 ) * int32v( FnPrimes::X ); + int32v j = FS_Convertf32_i32( y0 ) * int32v( FnPrimes::Y ); + + float32v g = float32v( G2 ) * (x0 + y0); + x0 = x - (x0 - g); + y0 = y - (y0 - g); + + mask32v i1 = x0 > y0; + //mask32v j1 = ~i1; //NMasked funcs + + float32v x1 = FS_MaskedSub_f32( x0, float32v( 1.f ), i1 ) + float32v( G2 ); + float32v y1 = FS_NMaskedSub_f32( y0, float32v( 1.f ), i1 ) + float32v( G2 ); + float32v x2 = x0 + float32v( (G2 * 2) - 1 ); + float32v y2 = y0 + float32v( (G2 * 2) - 1 ); + + float32v t0 = float32v( 0.5f ) - (x0 * x0) - (y0 * y0); + float32v t1 = float32v( 0.5f ) - (x1 * x1) - (y1 * y1); + float32v t2 = float32v( 0.5f ) - (x2 * x2) - (y2 * y2); + + t0 = FS_Max_f32( t0, float32v( 0 ) ); + t1 = FS_Max_f32( t1, float32v( 0 ) ); + t2 = FS_Max_f32( t2, float32v( 0 ) ); + + t0 *= t0; t0 *= t0; + t1 *= t1; t1 *= t1; + t2 *= t2; t2 *= t2; + + float32v n0 = FnUtils::GetGradientDotFancy( FnUtils::HashPrimes( seed, i, j ), x0, y0 ); + float32v n1 = FnUtils::GetGradientDotFancy( FnUtils::HashPrimes( seed, FS_MaskedAdd_i32( i, int32v( FnPrimes::X ), i1 ), FS_NMaskedAdd_i32( j, int32v( FnPrimes::Y ), i1 ) ), x1, y1 ); + float32v n2 = FnUtils::GetGradientDotFancy( FnUtils::HashPrimes( seed, i + int32v( FnPrimes::X ), j + int32v( FnPrimes::Y ) ), x2, y2 ); + + return float32v( 49.918426513671875f ) * FS_FMulAdd_f32( n0, t0, FS_FMulAdd_f32( n1, t1, n2 * t2 ) ); + } + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z ) const final + { + float32v f = float32v( 2.0f / 3.0f ) * (x + y + z); + float32v xr = f - x; + float32v yr = f - y; + float32v zr = f - z; + + float32v val( 0 ); + for( size_t i = 0; ; i++ ) + { + float32v v0xr = FS_Round_f32( xr ); + float32v v0yr = FS_Round_f32( yr ); + float32v v0zr = FS_Round_f32( zr ); + float32v d0xr = xr - v0xr; + float32v d0yr = yr - v0yr; + float32v d0zr = zr - v0zr; + + float32v score0xr = FS_Abs_f32( d0xr ); + float32v score0yr = FS_Abs_f32( d0yr ); + float32v score0zr = FS_Abs_f32( d0zr ); + mask32v dir0xr = FS_Max_f32( score0yr, score0zr ) <= score0xr; + mask32v dir0yr = FS_BitwiseAndNot_m32( FS_Max_f32( score0zr, score0xr ) <= score0yr, dir0xr ); + mask32v dir0zr = ~(dir0xr | dir0yr); + float32v v1xr = FS_MaskedAdd_f32( v0xr, float32v( 1.0f ) | ( float32v( -1.0f ) & d0xr ), dir0xr ); + float32v v1yr = FS_MaskedAdd_f32( v0yr, float32v( 1.0f ) | ( float32v( -1.0f ) & d0yr ), dir0yr ); + float32v v1zr = FS_MaskedAdd_f32( v0zr, float32v( 1.0f ) | ( float32v( -1.0f ) & d0zr ), dir0zr ); + float32v d1xr = xr - v1xr; + float32v d1yr = yr - v1yr; + float32v d1zr = zr - v1zr; + + int32v hv0xr = FS_Convertf32_i32( v0xr ) * int32v( FnPrimes::X ); + int32v hv0yr = FS_Convertf32_i32( v0yr ) * int32v( FnPrimes::Y ); + int32v hv0zr = FS_Convertf32_i32( v0zr ) * int32v( FnPrimes::Z ); + + int32v hv1xr = FS_Convertf32_i32( v1xr ) * int32v( FnPrimes::X ); + int32v hv1yr = FS_Convertf32_i32( v1yr ) * int32v( FnPrimes::Y ); + int32v hv1zr = FS_Convertf32_i32( v1zr ) * int32v( FnPrimes::Z ); + + float32v t0 = FS_FNMulAdd_f32( d0zr, d0zr, FS_FNMulAdd_f32( d0yr, d0yr, FS_FNMulAdd_f32( d0xr, d0xr, float32v( 0.6f ) ) ) ); + float32v t1 = FS_FNMulAdd_f32( d1zr, d1zr, FS_FNMulAdd_f32( d1yr, d1yr, FS_FNMulAdd_f32( d1xr, d1xr, float32v( 0.6f ) ) ) ); + t0 = FS_Max_f32( t0, float32v( 0 ) ); + t1 = FS_Max_f32( t1, float32v( 0 ) ); + t0 *= t0; t0 *= t0; + t1 *= t1; t1 *= t1; + + float32v v0 = FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, hv0xr, hv0yr, hv0zr ), d0xr, d0yr, d0zr ); + float32v v1 = FnUtils::GetGradientDot( FnUtils::HashPrimes( seed, hv1xr, hv1yr, hv1zr ), d1xr, d1yr, d1zr ); + + val = FS_FMulAdd_f32( v0, t0, FS_FMulAdd_f32( v1, t1, val ) ); + + if( i == 1 ) + { + break; + } + + xr += float32v( 0.5f ); + yr += float32v( 0.5f ); + zr += float32v( 0.5f ); + seed = ~seed; + } + + return float32v( 32.69428253173828125f ) * val; + } +}; + diff --git a/deps/FastNoise2/include/FastNoise/Generators/Utils.inl b/deps/FastNoise2/include/FastNoise/Generators/Utils.inl new file mode 100644 index 0000000..80f644f --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/Utils.inl @@ -0,0 +1,306 @@ +#pragma once +#include "FastSIMD/InlInclude.h" +#include + +namespace FastNoise +{ + namespace Primes + { + static constexpr int32_t X = 501125321; + static constexpr int32_t Y = 1136930381; + static constexpr int32_t Z = 1720413743; + static constexpr int32_t W = 1066037191; + + static constexpr int32_t Lookup[] = { X,Y,Z,W }; + } + + template + struct Utils + { + using float32v = typename FS::float32v; + using int32v = typename FS::int32v; + using mask32v = typename FS::mask32v; + + static constexpr float ROOT2 = 1.4142135623730950488f; + static constexpr float ROOT3 = 1.7320508075688772935f; + + template* = nullptr> + FS_INLINE static float32v GetGradientDotFancy( int32v hash, float32v fX, float32v fY ) + { + int32v index = FS_Convertf32_i32( FS_Converti32_f32( hash & int32v( 0x3FFFFF ) ) * float32v( 1.3333333333333333f ) ); + + // Bit-4 = Choose X Y ordering + mask32v xy; + + if constexpr( FS::SIMD_Level == FastSIMD::Level_Scalar ) + { + xy = int32_t( index & int32v( 1 << 2 ) ) != 0; + } + else + { + xy = index << 29; + + if constexpr( FS::SIMD_Level < FastSIMD::Level_SSE41 ) + { + xy >>= 31; + } + } + + float32v a = FS_Select_f32( xy, fY, fX ); + float32v b = FS_Select_f32( xy, fX, fY ); + + // Bit-1 = b flip sign + b ^= FS_Casti32_f32( index << 31 ); + + // Bit-2 = Mul a by 2 or Root3 + mask32v aMul2; + + if constexpr( FS::SIMD_Level == FastSIMD::Level_Scalar ) + { + aMul2 = int32_t( index & int32v( 1 << 1 ) ) != 0; + } + else + { + aMul2 = (index << 30) >> 31; + } + + a *= FS_Select_f32( aMul2, float32v( 2 ), float32v( ROOT3 ) ); + // b zero value if a mul 2 + b = FS_NMask_f32( b, aMul2 ); + + // Bit-8 = Flip sign of a + b + return ( a + b ) ^ FS_Casti32_f32( (index >> 3) << 31 ); + } + + template* = nullptr> + FS_INLINE static float32v GetGradientDotFancy( int32v hash, float32v fX, float32v fY ) + { + int32v index = FS_Convertf32_i32( FS_Converti32_f32( hash & int32v( 0x3FFFFF ) ) * float32v( 1.3333333333333333f ) ); + + float32v gX = _mm256_permutevar8x32_ps( float32v( ROOT3, ROOT3, 2, 2, 1, -1, 0, 0 ), index ); + float32v gY = _mm256_permutevar8x32_ps( float32v( 1, -1, 0, 0, ROOT3, ROOT3, 2, 2 ), index ); + + // Bit-8 = Flip sign of a + b + return FS_FMulAdd_f32( gX, fX, fY * gY ) ^ FS_Casti32_f32( (index >> 3) << 31 ); + } + + template* = nullptr> + FS_INLINE static float32v GetGradientDotFancy( int32v hash, float32v fX, float32v fY ) + { + int32v index = FS_Convertf32_i32( FS_Converti32_f32( hash & int32v( 0x3FFFFF ) ) * float32v( 1.3333333333333333f ) ); + + float32v gX = _mm512_permutexvar_ps( index, float32v( ROOT3, ROOT3, 2, 2, 1, -1, 0, 0, -ROOT3, -ROOT3, -2, -2, -1, 1, 0, 0 ) ); + float32v gY = _mm512_permutexvar_ps( index, float32v( 1, -1, 0, 0, ROOT3, ROOT3, 2, 2, -1, 1, 0, 0, -ROOT3, -ROOT3, -2, -2 ) ); + + return FS_FMulAdd_f32( gX, fX, fY * gY ); + } + + + template* = nullptr> + FS_INLINE static float32v GetGradientDot( int32v hash, float32v fX, float32v fY ) + { + // ( 1+R2, 1 ) ( -1-R2, 1 ) ( 1+R2, -1 ) ( -1-R2, -1 ) + // ( 1, 1+R2 ) ( 1, -1-R2 ) ( -1, 1+R2 ) ( -1, -1-R2 ) + + int32v bit1 = (hash << 31); + int32v bit2 = (hash >> 1) << 31; + mask32v bit4; + + if constexpr( FS::SIMD_Level == FastSIMD::Level_Scalar ) + { + bit4 = int32_t( hash & int32v( 1 << 2 ) ) != 0; + } + else + { + bit4 = hash << 29; + + if constexpr( FS::SIMD_Level < FastSIMD::Level_SSE41 ) + { + bit4 >>= 31; + } + } + + fX ^= FS_Casti32_f32( bit1 ); + fY ^= FS_Casti32_f32( bit2 ); + + float32v a = FS_Select_f32( bit4, fY, fX ); + float32v b = FS_Select_f32( bit4, fX, fY ); + + return FS_FMulAdd_f32( float32v( 1.0f + ROOT2 ), a, b ); + } + + template* = nullptr> + FS_INLINE static float32v GetGradientDot( int32v hash, float32v fX, float32v fY ) + { + float32v gX = _mm256_permutevar8x32_ps( float32v( 1 + ROOT2, -1 - ROOT2, 1 + ROOT2, -1 - ROOT2, 1, -1, 1, -1 ), hash ); + float32v gY = _mm256_permutevar8x32_ps( float32v( 1, 1, -1, -1, 1 + ROOT2, 1 + ROOT2, -1 - ROOT2, -1 - ROOT2 ), hash ); + + return FS_FMulAdd_f32( gX, fX, fY * gY ); + } + + template * = nullptr> + FS_INLINE static float32v GetGradientDot( int32v hash, float32v fX, float32v fY ) + { + float32v gX = _mm512_permutexvar_ps( hash, float32v( 1 + ROOT2, -1 - ROOT2, 1 + ROOT2, -1 - ROOT2, 1, -1, 1, -1, 1 + ROOT2, -1 - ROOT2, 1 + ROOT2, -1 - ROOT2, 1, -1, 1, -1 ) ); + float32v gY = _mm512_permutexvar_ps( hash, float32v( 1, 1, -1, -1, 1 + ROOT2, 1 + ROOT2, -1 - ROOT2, -1 - ROOT2, 1, 1, -1, -1, 1 + ROOT2, 1 + ROOT2, -1 - ROOT2, -1 - ROOT2 ) ); + + return FS_FMulAdd_f32( gX, fX, fY * gY ); + } + + template * = nullptr > + FS_INLINE static float32v GetGradientDot( int32v hash, float32v fX, float32v fY, float32v fZ ) + { + int32v hasha13 = hash & int32v( 13 ); + + //if h < 8 then x, else y + float32v u = FS_Select_f32( hasha13 < int32v( 8 ), fX, fY ); + + //if h < 4 then y else if h is 12 or 14 then x else z + float32v v = FS_Select_f32( hasha13 == int32v( 12 ), fX, fZ ); + v = FS_Select_f32( hasha13 < int32v( 2 ), fY, v ); + + //if h1 then -u else u + //if h2 then -v else v + float32v h1 = FS_Casti32_f32( hash << 31 ); + float32v h2 = FS_Casti32_f32( (hash & int32v( 2 )) << 30 ); + //then add them + return ( u ^ h1 ) + ( v ^ h2 ); + } + + template* = nullptr> + FS_INLINE static float32v GetGradientDot( int32v hash, float32v fX, float32v fY, float32v fZ ) + { + float32v gX = _mm512_permutexvar_ps( hash, float32v( 1, -1, 1, -1, 1, -1, 1, -1, 0, 0, 0, 0, 1, 0, -1, 0 ) ); + float32v gY = _mm512_permutexvar_ps( hash, float32v( 1, 1, -1, -1, 0, 0, 0, 0, 1, -1, 1, -1, 1, -1, 1, -1 ) ); + float32v gZ = _mm512_permutexvar_ps( hash, float32v( 0, 0, 0, 0, 1, 1, -1, -1, 1, 1, -1, -1, 0, 1, 0, -1 ) ); + + return FS_FMulAdd_f32( gX, fX, FS_FMulAdd_f32( fY, gY, fZ * gZ )); + } + + template* = nullptr > + FS_INLINE static float32v GetGradientDot( int32v hash, float32v fX, float32v fY, float32v fZ, float32v fW ) + { + int32v p = hash & int32v( 3 << 3 ); + + float32v a = FS_Select_f32( p > int32v( 0 ), fX, fY ); + float32v b; + if constexpr( FS::SIMD_Level <= FastSIMD::Level_SSE2 ) + { + b = FS_Select_f32( p > int32v( 1 << 3 ), fY, fZ ); + } + else + { + b = FS_Select_f32( hash << 27, fY, fZ ); + } + float32v c = FS_Select_f32( p > int32v( 2 << 3 ), fZ, fW ); + + float32v aSign = FS_Casti32_f32( hash << 31 ); + float32v bSign = FS_Casti32_f32( (hash << 30) & int32v( 0x80000000 ) ); + float32v cSign = FS_Casti32_f32( (hash << 29) & int32v( 0x80000000 ) ); + + return ( a ^ aSign ) + ( b ^ bSign ) + ( c ^ cSign ); + } + + template* = nullptr> + FS_INLINE static float32v GetGradientDot( int32v hash, float32v fX, float32v fY, float32v fZ, float32v fW ) + { + float32v gX = _mm512_permutex2var_ps( float32v( 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 1, -1, 1, -1, 1, -1 ), hash, float32v( 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 ) ); + float32v gY = _mm512_permutex2var_ps( float32v( 1, -1, 1, -1, 1, -1, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0 ), hash, float32v( 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1 ) ); + float32v gZ = _mm512_permutex2var_ps( float32v( 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1 ), hash, float32v( 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, -1, -1, -1, -1 ) ); + float32v gW = _mm512_permutex2var_ps( float32v( 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1 ), hash, float32v( 1, 1, 1, 1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 ) ); + + return FS_FMulAdd_f32( gX, fX, FS_FMulAdd_f32( fY, gY, FS_FMulAdd_f32( fZ, gZ, fW * gW ) )); + } + + template + FS_INLINE static int32v HashPrimes( int32v seed, P... primedPos ) + { + int32v hash = seed; + hash ^= (primedPos ^ ...); + + hash *= int32v( 0x27d4eb2d ); + return (hash >> 15) ^ hash; + } + + template + FS_INLINE static int32v HashPrimesHB( int32v seed, P... primedPos ) + { + int32v hash = seed; + hash ^= (primedPos ^ ...); + + hash *= int32v( 0x27d4eb2d ); + return hash; + } + + template + FS_INLINE static float32v GetValueCoord( int32v seed, P... primedPos ) + { + int32v hash = seed; + hash ^= (primedPos ^ ...); + + hash *= hash * int32v( 0x27d4eb2d ); + return FS_Converti32_f32( hash ) * float32v( 1.0f / (float)INT_MAX ); + } + + template + FS_INLINE static float32v Lerp( float32v a, float32v b, float32v t ) + { + return FS_FMulAdd_f32( t, b - a, a ); + } + + template + FS_INLINE static float32v InterpHermite( float32v t ) + { + return t * t * FS_FNMulAdd_f32( t, float32v( 2 ), float32v( 3 )); + } + + template + FS_INLINE static float32v InterpQuintic( float32v t ) + { + return t * t * t * FS_FMulAdd_f32( t, FS_FMulAdd_f32( t, float32v( 6 ), float32v( -15 )), float32v( 10 ) ); + } + + template + FS_INLINE static float32v CalcDistance( DistanceFunction distFunc, float32v dX, P... d ) + { + switch( distFunc ) + { + default: + case DistanceFunction::Euclidean: + { + float32v distSqr = dX * dX; + ((distSqr = FS_FMulAdd_f32( d, d, distSqr )), ...); + + return FS_InvSqrt_f32( distSqr ) * distSqr; + } + + case DistanceFunction::EuclideanSquared: + { + float32v distSqr = dX * dX; + ((distSqr = FS_FMulAdd_f32( d, d, distSqr )), ...); + + return distSqr; + } + + case DistanceFunction::Manhattan: + { + float32v dist = FS_Abs_f32( dX ); + dist += (FS_Abs_f32( d ) + ...); + + return dist; + } + + case DistanceFunction::Hybrid: + { + float32v both = FS_FMulAdd_f32( dX, dX, FS_Abs_f32( dX ) ); + ((both += FS_FMulAdd_f32( d, d, FS_Abs_f32( d ) )), ...); + + return both; + } + } + } + }; +} + +using FnUtils = FastNoise::Utils; +namespace FnPrimes = FastNoise::Primes; \ No newline at end of file diff --git a/deps/FastNoise2/include/FastNoise/Generators/Value.h b/deps/FastNoise2/include/FastNoise/Generators/Value.h new file mode 100644 index 0000000..4b40433 --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/Value.h @@ -0,0 +1,16 @@ +#pragma once +#include "Generator.h" + +namespace FastNoise +{ + class Value : public virtual Generator + { + FASTNOISE_METADATA( Generator ) + + Metadata( const char* className ) : Generator::Metadata( className ) + { + groups.push_back( "Coherent Noise" ); + } + }; + }; +} diff --git a/deps/FastNoise2/include/FastNoise/Generators/Value.inl b/deps/FastNoise2/include/FastNoise/Generators/Value.inl new file mode 100644 index 0000000..8c3565e --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/Generators/Value.inl @@ -0,0 +1,88 @@ +#include "FastSIMD/InlInclude.h" + +#include "Value.h" +#include "Utils.inl" + +template +class FS_T : public virtual FastNoise::Value, public FS_T +{ + FASTSIMD_DECLARE_FS_TYPES; + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y ) const final + { + float32v xs = FS_Floor_f32( x ); + float32v ys = FS_Floor_f32( y ); + + int32v x0 = FS_Convertf32_i32( xs ) * int32v( FnPrimes::X ); + int32v y0 = FS_Convertf32_i32( ys ) * int32v( FnPrimes::Y ); + int32v x1 = x0 + int32v( FnPrimes::X ); + int32v y1 = y0 + int32v( FnPrimes::Y ); + + xs = FnUtils::InterpHermite( x - xs ); + ys = FnUtils::InterpHermite( y - ys ); + + return FnUtils::Lerp( + FnUtils::Lerp( FnUtils::GetValueCoord( seed, x0, y0 ), FnUtils::GetValueCoord( seed, x1, y0 ), xs ), + FnUtils::Lerp( FnUtils::GetValueCoord( seed, x0, y1 ), FnUtils::GetValueCoord( seed, x1, y1 ), xs ), ys ); + } + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z ) const final + { + float32v xs = FS_Floor_f32( x ); + float32v ys = FS_Floor_f32( y ); + float32v zs = FS_Floor_f32( z ); + + int32v x0 = FS_Convertf32_i32( xs ) * int32v( FnPrimes::X ); + int32v y0 = FS_Convertf32_i32( ys ) * int32v( FnPrimes::Y ); + int32v z0 = FS_Convertf32_i32( zs ) * int32v( FnPrimes::Z ); + int32v x1 = x0 + int32v( FnPrimes::X ); + int32v y1 = y0 + int32v( FnPrimes::Y ); + int32v z1 = z0 + int32v( FnPrimes::Z ); + + xs = FnUtils::InterpHermite( x - xs ); + ys = FnUtils::InterpHermite( y - ys ); + zs = FnUtils::InterpHermite( z - zs ); + + return FnUtils::Lerp( FnUtils::Lerp( + FnUtils::Lerp( FnUtils::GetValueCoord( seed, x0, y0, z0 ), FnUtils::GetValueCoord( seed, x1, y0, z0 ), xs ), + FnUtils::Lerp( FnUtils::GetValueCoord( seed, x0, y1, z0 ), FnUtils::GetValueCoord( seed, x1, y1, z0 ), xs ), ys ), + FnUtils::Lerp( + FnUtils::Lerp( FnUtils::GetValueCoord( seed, x0, y0, z1 ), FnUtils::GetValueCoord( seed, x1, y0, z1 ), xs ), + FnUtils::Lerp( FnUtils::GetValueCoord( seed, x0, y1, z1 ), FnUtils::GetValueCoord( seed, x1, y1, z1 ), xs ), ys ), zs ); + } + + float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z, float32v w ) const final + { + float32v xs = FS_Floor_f32( x ); + float32v ys = FS_Floor_f32( y ); + float32v zs = FS_Floor_f32( z ); + float32v ws = FS_Floor_f32( w ); + + int32v x0 = FS_Convertf32_i32( xs ) * int32v( FnPrimes::X ); + int32v y0 = FS_Convertf32_i32( ys ) * int32v( FnPrimes::Y ); + int32v z0 = FS_Convertf32_i32( zs ) * int32v( FnPrimes::Z ); + int32v w0 = FS_Convertf32_i32( ws ) * int32v( FnPrimes::W ); + int32v x1 = x0 + int32v( FnPrimes::X ); + int32v y1 = y0 + int32v( FnPrimes::Y ); + int32v z1 = z0 + int32v( FnPrimes::Z ); + int32v w1 = w0 + int32v( FnPrimes::W ); + + xs = FnUtils::InterpHermite( x - xs ); + ys = FnUtils::InterpHermite( y - ys ); + zs = FnUtils::InterpHermite( z - zs ); + ws = FnUtils::InterpHermite( w - ws ); + + return FnUtils::Lerp( FnUtils::Lerp( FnUtils::Lerp( + FnUtils::Lerp( FnUtils::GetValueCoord( seed, x0, y0, z0, w0 ), FnUtils::GetValueCoord( seed, x1, y0, z0, w0 ), xs ), + FnUtils::Lerp( FnUtils::GetValueCoord( seed, x0, y1, z0, w0 ), FnUtils::GetValueCoord( seed, x1, y1, z0, w0 ), xs ), ys ), + FnUtils::Lerp( + FnUtils::Lerp( FnUtils::GetValueCoord( seed, x0, y0, z1, w0 ), FnUtils::GetValueCoord( seed, x1, y0, z1, w0 ), xs ), + FnUtils::Lerp( FnUtils::GetValueCoord( seed, x0, y1, z1, w0 ), FnUtils::GetValueCoord( seed, x1, y1, z1, w0 ), xs ), ys ), zs ), + FnUtils::Lerp( FnUtils::Lerp( + FnUtils::Lerp( FnUtils::GetValueCoord( seed, x0, y0, z0, w1 ), FnUtils::GetValueCoord( seed, x1, y0, z0, w1 ), xs ), + FnUtils::Lerp( FnUtils::GetValueCoord( seed, x0, y1, z0, w1 ), FnUtils::GetValueCoord( seed, x1, y1, z0, w1 ), xs ), ys ), + FnUtils::Lerp( + FnUtils::Lerp( FnUtils::GetValueCoord( seed, x0, y0, z1, w1 ), FnUtils::GetValueCoord( seed, x1, y0, z1, w1 ), xs ), + FnUtils::Lerp( FnUtils::GetValueCoord( seed, x0, y1, z1, w1 ), FnUtils::GetValueCoord( seed, x1, y1, z1, w1 ), xs ), ys ), zs ), ws ); + } +}; diff --git a/deps/FastNoise2/include/FastNoise/VecN.h b/deps/FastNoise2/include/FastNoise/VecN.h new file mode 100644 index 0000000..09b210c --- /dev/null +++ b/deps/FastNoise2/include/FastNoise/VecN.h @@ -0,0 +1,83 @@ +#pragma once +#include +#include +#include + +#include "FastSIMD/FunctionList.h" + +template +class VecN; + +template +class VecN +{ +protected: + template + constexpr VecN( A... ) {} + + template + void ForEach( A... ) const {} + + template + void ForEachR( A... ) const {} +}; + +template +class VecN : public VecN +{ +public: + static constexpr size_t Size = S; + + typedef std::integral_constant Index; + + constexpr VecN() : Base(), value() {} + + template + constexpr VecN( A... args ) : + Base( args... ), + value( std::get( std::make_tuple( args... ) ) ) + { + } + + template + FS_INLINE std::enable_if_t<(I < Size), T&> At() + { + return VecN::value; + } + + template + FS_INLINE std::enable_if_t<(I < Size), T> At() const + { + return VecN::value; + } + + template + FS_INLINE std::enable_if_t<(I >= Size), T&> At() const + { + throw std::out_of_range( "Index of of range" ); + } + + template + FS_INLINE void ForEach( F&& func, A&&... other ) + { + Base::ForEach( func, other... ); + + func( Index(), value, (other.template At())... ); + } + + template + FS_INLINE void ForEachR( F&& func, A&&... other ) + { + func( Index(), value, (other.template At())... ); + + Base::ForEachR( func, other... ); + } + +protected: + + typedef VecN Base; + + typedef std::integral_constant Index; + + T value; +}; \ No newline at end of file diff --git a/deps/FastNoise2/include/FastSIMD/FastSIMD.h b/deps/FastNoise2/include/FastSIMD/FastSIMD.h new file mode 100644 index 0000000..67884ca --- /dev/null +++ b/deps/FastNoise2/include/FastSIMD/FastSIMD.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include "FastSIMD_Config.h" + +namespace FastSIMD +{ + typedef uint32_t Level_BitFlags; + + enum eLevel : Level_BitFlags + { + Level_Null = 0, // Uninitilised + Level_Scalar = 1 << 0, // 80386 instruction set (Not SIMD) + Level_SSE = 1 << 1, // SSE (XMM) supported by CPU (not testing for O.S. support) + Level_SSE2 = 1 << 2, // SSE2 + Level_SSE3 = 1 << 3, // SSE3 + Level_SSSE3 = 1 << 4, // Supplementary SSE3 (SSSE3) + Level_SSE41 = 1 << 5, // SSE4.1 + Level_SSE42 = 1 << 6, // SSE4.2 + Level_AVX = 1 << 7, // AVX supported by CPU and operating system + Level_AVX2 = 1 << 8, // AVX2 + Level_AVX512 = 1 << 9, // AVX512, AVX512DQ supported by CPU and operating system + + Level_NEON = 1 << 16, // ARM NEON + }; + + const Level_BitFlags COMPILED_SIMD_LEVELS = + (FASTSIMD_COMPILE_SCALAR ? Level_Scalar : 0) | + (FASTSIMD_COMPILE_SSE ? Level_SSE : 0) | + (FASTSIMD_COMPILE_SSE2 ? Level_SSE2 : 0) | + (FASTSIMD_COMPILE_SSE3 ? Level_SSE3 : 0) | + (FASTSIMD_COMPILE_SSSE3 ? Level_SSSE3 : 0) | + (FASTSIMD_COMPILE_SSE41 ? Level_SSE41 : 0) | + (FASTSIMD_COMPILE_SSE42 ? Level_SSE42 : 0) | + (FASTSIMD_COMPILE_AVX ? Level_AVX : 0) | + (FASTSIMD_COMPILE_AVX2 ? Level_AVX2 : 0) | + (FASTSIMD_COMPILE_AVX512 ? Level_AVX512 : 0) | + (FASTSIMD_COMPILE_NEON ? Level_NEON : 0) ; + + + eLevel CPUMaxSIMDLevel(); + + template + T* New( eLevel maxSIMDLevel = Level_Null ); + + template + T* ClassFactory(); + +#define FASTSIMD_LEVEL_SUPPORT( ... ) \ + static const FastSIMD::Level_BitFlags Supported_SIMD_Levels = __VA_ARGS__ + +}; diff --git a/deps/FastNoise2/include/FastSIMD/FastSIMD_Config.h b/deps/FastNoise2/include/FastSIMD/FastSIMD_Config.h new file mode 100644 index 0000000..4e0ead3 --- /dev/null +++ b/deps/FastNoise2/include/FastSIMD/FastSIMD_Config.h @@ -0,0 +1,29 @@ +#pragma once + +#if defined(__arm__) || defined(__aarch64__) +#define FASTSIMD_x86 0 +#define FASTSIMD_ARM 1 +#else +#define FASTSIMD_x86 1 +#define FASTSIMD_ARM 0 +#endif + +#define FASTSIMD_64BIT (INTPTR_MAX == INT64_MAX) + +#define FASTSIMD_COMPILE_SCALAR (!(FASTSIMD_x86 && FASTSIMD_64BIT)) // Don't compile for x86 64bit since CPU is guaranteed SSE2 support + +#define FASTSIMD_COMPILE_SSE (FASTSIMD_x86 & 000) // Not supported +#define FASTSIMD_COMPILE_SSE2 (FASTSIMD_x86 & 1 ) +#define FASTSIMD_COMPILE_SSE3 (FASTSIMD_x86 & 1 ) +#define FASTSIMD_COMPILE_SSSE3 (FASTSIMD_x86 & 1 ) +#define FASTSIMD_COMPILE_SSE41 (FASTSIMD_x86 & 1 ) +#define FASTSIMD_COMPILE_SSE42 (FASTSIMD_x86 & 1 ) +#define FASTSIMD_COMPILE_AVX (FASTSIMD_x86 & 000) // Not supported +#define FASTSIMD_COMPILE_AVX2 (FASTSIMD_x86 & 1 ) +#define FASTSIMD_COMPILE_AVX512 (FASTSIMD_x86 & 1 ) + +#define FASTSIMD_COMPILE_NEON (FASTSIMD_ARM & 1 ) + +#define FASTSIMD_USE_FMA 1 +#define FASTSIMD_CONFIG_GENERATE_CONSTANTS 0 + diff --git a/deps/FastNoise2/include/FastSIMD/FunctionList.h b/deps/FastNoise2/include/FastSIMD/FunctionList.h new file mode 100644 index 0000000..d2b686f --- /dev/null +++ b/deps/FastNoise2/include/FastSIMD/FunctionList.h @@ -0,0 +1,821 @@ +#pragma once +#include +#include +#include + +#include "FastSIMD/FastSIMD.h" + +#ifdef _MSC_VER +#if defined( _M_IX86_FP ) && _M_IX86_FP < 2 +#define FS_VECTORCALL +#else +#define FS_VECTORCALL __vectorcall +#endif +#define FS_INLINE __forceinline +#else +#define FS_VECTORCALL +#define FS_INLINE __attribute__((always_inline)) inline +#endif + +#ifndef NDEBUG +#undef FS_INLINE +#define FS_INLINE inline +#endif + +///

+/// Number of 32 width elements that will fit into a vector +/// +/// +/// Compile time constant +/// +/// +/// size_t FS_Size_32() +/// +#define FS_Size_32() FS::template VectorSize<32> + + +// Vector builders + +/// +/// Vector with values incrementing from 0 based on element index {0, 1, 2, 3...} +/// +/// +/// example: int32v::FS_Incremented() +/// +#define FS_Incremented() Incremented() + + +// Load + +/// +/// Copies sizeof(float32v) bytes from given memory location into float32v +/// +/// +/// Memory does not need to be aligned +/// +/// +/// float32v FS_Load_f32( void const* ptr ) +/// +#define FS_Load_f32( ... ) FS::Load_f32( __VA_ARGS__ ) + + +/// +/// Copies sizeof(int32v) bytes from given memory location into int32v +/// +/// +/// Memory does not need to be aligned +/// +/// +/// int32v FS_Load_i32( void const* ptr ) +/// +#define FS_Load_i32( ... ) FS::Load_i32( __VA_ARGS__ ) + + +// Store + +/// +/// Copies all elements of float32v to given memory location +/// +/// +/// void FS_Store_f32( void* ptr, float32v f ) +/// +#define FS_Store_f32( ... ) FS::Store_f32( __VA_ARGS__ ) + +/// +/// Copies all elements of int32v to given memory location +/// +/// +/// void FS_Store_i32( void* ptr, int32v i ) +/// +#define FS_Store_i32( ... ) FS::Store_i32( __VA_ARGS__ ) + + +// Cast + +/// +/// Bitwise cast int to float +/// +/// +/// float32v FS_Casti32_f32( int32v i ) +/// +#define FS_Casti32_f32( ... ) FS::Casti32_f32( __VA_ARGS__ ) + +/// +/// Bitwise cast float to int +/// +/// +/// int32v FS_Castf32_i32( float32v f ) +/// +#define FS_Castf32_i32( ... ) FS::Castf32_i32( __VA_ARGS__ ) + + +// Convert + +/// +/// Convert int to float +/// +/// +/// Rounding: truncate +/// +/// +/// float32v FS_Converti32_f32( int32v i ) +/// +#define FS_Converti32_f32( ... ) FS::Converti32_f32( __VA_ARGS__ ) + +/// +/// Convert float to int +/// +/// +/// int32v FS_Convertf32_i32( float32v f ) +/// +#define FS_Convertf32_i32( ... ) FS::Convertf32_i32( __VA_ARGS__ ) + + +// Select + +/// +/// return ( m ? a : b ) +/// +/// +/// float32v FS_Select_f32( mask32v m, float32v a, float32v b ) +/// +#define FS_Select_f32( ... ) FS::Select_f32( __VA_ARGS__ ) + +/// +/// return ( m ? a : b ) +/// +/// +/// int32v FS_Select_i32( mask32v m, int32v a, int32v b ) +/// +#define FS_Select_i32( ... ) FS::Select_i32( __VA_ARGS__ ) + + +// Min, Max + +/// +/// return ( a < b ? a : b ) +/// +/// +/// float32v FS_Min_f32( float32v a, float32v b ) +/// +#define FS_Min_f32( ... ) FS::Min_f32( __VA_ARGS__ ) + +/// +/// return ( a > b ? a : b ) +/// +/// +/// float32v FS_Max_f32( float32v a, float32v b ) +/// +#define FS_Max_f32( ... ) FS::Max_f32( __VA_ARGS__ ) + +/// +/// return ( a < b ? a : b ) +/// +/// +/// int32v FS_Min_i32( int32v a, int32v b ) +/// +#define FS_Min_i32( ... ) FS::Min_i32( __VA_ARGS__ ) + +/// +/// return ( a > b ? a : b ) +/// +/// +/// int32v FS_Max_i32( int32v a, int32v b ) +/// +#define FS_Max_i32( ... ) FS::Max_i32( __VA_ARGS__ ) + + +// Bitwise + +/// +/// return ( a & ~b ) +/// +/// +/// float32v FS_BitwiseAndNot_f32( float32v a, float32v b ) +/// +#define FS_BitwiseAndNot_f32( ... ) FS::BitwiseAndNot_f32( __VA_ARGS__ ) + +/// +/// return ( a & ~b ) +/// +/// +/// int32v FS_BitwiseAndNot_i32( int32v a, int32v b ) +/// +#define FS_BitwiseAndNot_i32( ... ) FS::BitwiseAndNot_i32( __VA_ARGS__ ) + +/// +/// return ( a & ~b ) +/// +/// +/// mask32v FS_BitwiseAndNot_m32( mask32v a, mask32v b ) +/// +#define FS_BitwiseAndNot_m32( ... ) FastSIMD::BitwiseAndNot_m32( __VA_ARGS__ ) + + +/// +/// return ZeroExtend( a >> b ) +/// +/// +/// float32v FS_BitwiseShiftRightZX_f32( float32v a, int32_t b ) +/// +#define FS_BitwiseShiftRightZX_f32( ... ) FS::BitwiseShiftRightZX_f32( __VA_ARGS__ ) + +/// +/// return ZeroExtend( a >> b ) +/// +/// +/// float32v FS_BitwiseShiftRightZX_i32( int32v a, int32_t b ) +/// +#define FS_BitwiseShiftRightZX_i32( ... ) FS::BitwiseShiftRightZX_i32( __VA_ARGS__ ) + +// Abs + +/// +/// return ( a < 0 ? -a : a ) +/// +/// +/// float32v FS_Abs_f32( float32v a ) +/// +#define FS_Abs_f32( ... ) FS::Abs_f32( __VA_ARGS__ ) + +/// +/// return ( a < 0 ? -a : a ) +/// +/// +/// int32v FS_Abs_i32( int32v a ) +/// +#define FS_Abs_i32( ... ) FS::Abs_i32( __VA_ARGS__ ) + + +// Float math + +/// +/// return sqrt( a ) +/// +/// +/// float32v FS_Sqrt_f32( float32v a ) +/// +#define FS_Sqrt_f32( ... ) FS::Sqrt_f32( __VA_ARGS__ ) + +/// +/// return APPROXIMATE( 1.0 / sqrt( a ) ) +/// +/// +/// float32v FS_InvSqrt_f32( float32v a ) +/// +#define FS_InvSqrt_f32( ... ) FS::InvSqrt_f32( __VA_ARGS__ ) + +/// +/// return APPROXIMATE( 1.0 / a ) +/// +/// +/// float32v FS_Reciprocal_f32( float32v a ) +/// +#define FS_Reciprocal_f32( ... ) FS::Reciprocal_f32( __VA_ARGS__ ) + +// Floor, Ceil, Round + +/// +/// return floor( a ) +/// +/// +/// Rounding: Towards negative infinity +/// +/// +/// float32v FS_Floor_f32( float32v a ) +/// +#define FS_Floor_f32( ... ) FS::Floor_f32( __VA_ARGS__ ) + +/// +/// return ceil( a ) +/// +/// +/// Rounding: Towards positive infinity +/// +/// +/// float32v FS_Ceil_f32( float32v a ) +/// +#define FS_Ceil_f32( ... ) FS::Ceil_f32( __VA_ARGS__ ) + +/// +/// return round( a ) +/// +/// +/// Rounding: Banker's rounding +/// +/// +/// float32v FS_Round_f32( float32v a ) +/// +#define FS_Round_f32( ... ) FS::Round_f32( __VA_ARGS__ ) + +// Trig + +/// +/// return APPROXIMATE( cos( a ) ) +/// +/// +/// float32v FS_Cos_f32( float32v a ) +/// +#define FS_Cos_f32( ... ) FastSIMD::Cos_f32( __VA_ARGS__ ) + +/// +/// return APPROXIMATE( sin( a ) ) +/// +/// +/// float32v FS_Sin_f32( float32v a ) +/// +#define FS_Sin_f32( ... ) FastSIMD::Sin_f32( __VA_ARGS__ ) + +// Math + +/// +/// return pow( v, pow ) +/// +/// +/// float32v FS_Pow_f32( float32v v, float32v pow ) +/// +#define FS_Pow_f32( ... ) FastSIMD::Pow_f32( __VA_ARGS__ ) + +/// +/// return log( a ) +/// +/// +/// a <= 0 returns 0 +/// +/// +/// float32v FS_Log_f32( float32v a ) +/// +#define FS_Log_f32( ... ) FastSIMD::Log_f32( __VA_ARGS__ ) + +/// +/// return exp( a ) +/// +/// +/// a will be clamped to -88.376, 88.376 +/// +/// +/// float32v FS_Exp_f32( float32v a ) +/// +#define FS_Exp_f32( ... ) FastSIMD::Exp_f32( __VA_ARGS__ ) + + +// Mask + +/// +/// return ( m ? a : 0 ) +/// +/// +/// int32v FS_Mask_i32( int32v a, mask32v m ) +/// +#define FS_Mask_i32( ... ) FS::Mask_i32( __VA_ARGS__ ) + +/// +/// return ( m ? a : 0 ) +/// +/// +/// float32v FS_Mask_f32( float32v a, mask32v m ) +/// +#define FS_Mask_f32( ... ) FS::Mask_f32( __VA_ARGS__ ) + +/// +/// return ( m ? 0 : a ) +/// +/// +/// int32v FS_NMask_i32( int32v a, mask32v m ) +/// +#define FS_NMask_i32( ... ) FS::NMask_i32( __VA_ARGS__ ) + +/// +/// return ( m ? 0 : a ) +/// +/// +/// float32v FS_NMask_f32( float32v a, mask32v m ) +/// +#define FS_NMask_f32( ... ) FS::NMask_f32( __VA_ARGS__ ) + +/// +/// return m.contains( true ) +/// +/// +/// bool FS_AnyMask_bool( mask32v m ) +/// +#define FS_AnyMask_bool( ... ) FS::AnyMask_bool( __VA_ARGS__ ) + + +// FMA + +/// +/// return ( (a * b) + c ) +/// +/// +/// float32v FS_FMulAdd_f32( float32v a, float32v b, float32v c ) +/// +#define FS_FMulAdd_f32( ... ) FastSIMD::FMulAdd_f32( __VA_ARGS__ ) + +/// +/// return ( -(a * b) + c ) +/// +/// +/// float32v FS_FNMulAdd_f32( float32v a, float32v b, float32v c ) +/// +#define FS_FNMulAdd_f32( ... ) FastSIMD::FNMulAdd_f32( __VA_ARGS__ ) + + +// Masked float + +/// +/// return ( m ? (a + b) : a ) +/// +/// +/// float32v FS_MaskedAdd_f32( float32v a, float32v b, mask32v m ) +/// +#define FS_MaskedAdd_f32( ... ) FastSIMD::MaskedAdd_f32( __VA_ARGS__ ) + +/// +/// return ( m ? (a - b) : a ) +/// +/// +/// float32v FS_MaskedSub_f32( float32v a, float32v b, mask32v m ) +/// +#define FS_MaskedSub_f32( ... ) FastSIMD::MaskedSub_f32( __VA_ARGS__ ) + +/// +/// return ( m ? (a * b) : a ) +/// +/// +/// float32v FS_MaskedMul_f32( float32v a, float32v b, mask32v m ) +/// +#define FS_MaskedMul_f32( ... ) FastSIMD::MaskedMul_f32( __VA_ARGS__ ) + + +// Masked int32 + +/// +/// return ( m ? (a + b) : a ) +/// +/// +/// int32v FS_MaskedAdd_i32( int32v a, int32v b, mask32v m ) +/// +#define FS_MaskedAdd_i32( ... ) FastSIMD::MaskedAdd_i32( __VA_ARGS__ ) + +/// +/// return ( m ? (a - b) : a ) +/// +/// +/// int32v FS_MaskedSub_i32( int32v a, int32v b, mask32v m ) +/// +#define FS_MaskedSub_i32( ... ) FastSIMD::MaskedSub_i32( __VA_ARGS__ ) + +/// +/// return ( m ? (a * b) : a ) +/// +/// +/// int32v FS_MaskedMul_i32( int32v a, int32v b, mask32v m ) +/// +#define FS_MaskedMul_i32( ... ) FastSIMD::MaskedMul_i32( __VA_ARGS__ ) + +/// +/// return ( m ? (a + 1) : a ) +/// +/// +/// int32v FS_MaskedIncrement_i32( int32v a, mask32v m ) +/// +#define FS_MaskedIncrement_i32( ... ) FastSIMD::MaskedIncrement_i32( __VA_ARGS__ ) + +/// +/// return ( m ? (a - 1) : a ) +/// +/// +/// int32v FS_MaskedDecrement_i32( int32v a, mask32v m ) +/// +#define FS_MaskedDecrement_i32( ... ) FastSIMD::MaskedDecrement_i32( __VA_ARGS__ ) + + +// NMasked float + +/// +/// return ( m ? a : (a + b) ) +/// +/// +/// float32v FS_NMaskedAdd_f32( float32v a, float32v b, mask32v m ) +/// +#define FS_NMaskedAdd_f32( ... ) FastSIMD::NMaskedAdd_f32( __VA_ARGS__ ) + +/// +/// return ( m ? a : (a - b) ) +/// +/// +/// float32v FS_NMaskedSub_f32( float32v a, float32v b, mask32v m ) +/// +#define FS_NMaskedSub_f32( ... ) FastSIMD::NMaskedSub_f32( __VA_ARGS__ ) + +/// +/// return ( m ? a : (a * b) ) +/// +/// +/// float32v FS_NMaskedMul_f32( float32v a, float32v b, mask32v m ) +/// +#define FS_NMaskedMul_f32( ... ) FastSIMD::NMaskedMul_f32( __VA_ARGS__ ) + + +// NMasked int32 + +/// +/// return ( m ? a : (a + b) ) +/// +/// +/// int32v FS_NMaskedAdd_i32( int32v a, int32v b, mask32v m ) +/// +#define FS_NMaskedAdd_i32( ... ) FastSIMD::NMaskedAdd_i32( __VA_ARGS__ ) + +/// +/// return ( m ? a : (a - b) ) +/// +/// +/// int32v FS_NMaskedSub_i32( int32v a, int32v b, mask32v m ) +/// +#define FS_NMaskedSub_i32( ... ) FastSIMD::NMaskedSub_i32( __VA_ARGS__ ) + +/// +/// return ( m ? a : (a * b) ) +/// +/// +/// int32v FS_NMaskedMul_i32( int32v a, int32v b, mask32v m ) +/// +#define FS_NMaskedMul_i32( ... ) FastSIMD::NMaskedMul_i32( __VA_ARGS__ ) + + +namespace FastSIMD +{ + //FMA + + template + FS_INLINE typename FS::float32v FMulAdd_f32( typename FS::float32v a, typename FS::float32v b, typename FS::float32v c ) + { + return (a * b) + c; + } + + template + FS_INLINE typename FS::float32v FNMulAdd_f32( typename FS::float32v a, typename FS::float32v b, typename FS::float32v c ) + { + return -(a * b) + c; + } + + // Masked float + + template + FS_INLINE typename FS::float32v MaskedAdd_f32( typename FS::float32v a, typename FS::float32v b, typename FS::mask32v m ) + { + return a + FS::Mask_f32( b, m ); + } + + template + FS_INLINE typename FS::float32v MaskedSub_f32( typename FS::float32v a, typename FS::float32v b, typename FS::mask32v m ) + { + return a - FS::Mask_f32( b, m ); + } + + template + FS_INLINE typename FS::float32v MaskedMul_f32( typename FS::float32v a, typename FS::float32v b, typename FS::mask32v m ) + { + return a * FS::Mask_f32( b, m ); + } + + // Masked int32 + + template + FS_INLINE typename FS::int32v MaskedAdd_i32( typename FS::int32v a, typename FS::int32v b, typename FS::mask32v m ) + { + return a + FS::Mask_i32( b, m ); + } + + template + FS_INLINE typename FS::int32v MaskedSub_i32( typename FS::int32v a, typename FS::int32v b, typename FS::mask32v m ) + { + return a - FS::Mask_i32( b, m ); + } + + template + FS_INLINE typename FS::int32v MaskedMul_i32( typename FS::int32v a, typename FS::int32v b, typename FS::mask32v m ) + { + return a * FS::Mask_i32( b, m ); + } + + // NMasked float + + template + FS_INLINE typename FS::float32v NMaskedAdd_f32( typename FS::float32v a, typename FS::float32v b, typename FS::mask32v m ) + { + return a + FS::NMask_f32( b, m ); + } + + template + FS_INLINE typename FS::float32v NMaskedSub_f32( typename FS::float32v a, typename FS::float32v b, typename FS::mask32v m ) + { + return a - FS::NMask_f32( b, m ); + } + + template + FS_INLINE typename FS::float32v NMaskedMul_f32( typename FS::float32v a, typename FS::float32v b, typename FS::mask32v m ) + { + return a * FS::NMask_f32( b, m ); + } + + // NMasked int32 + + template + FS_INLINE typename FS::int32v NMaskedAdd_i32( typename FS::int32v a, typename FS::int32v b, typename FS::mask32v m ) + { + return a + FS::NMask_i32( b, m ); + } + + template + FS_INLINE typename FS::int32v NMaskedSub_i32( typename FS::int32v a, typename FS::int32v b, typename FS::mask32v m ) + { + return a - FS::NMask_i32( b, m ); + } + + template + FS_INLINE typename FS::int32v NMaskedMul_i32( typename FS::int32v a, typename FS::int32v b, typename FS::mask32v m ) + { + return a * FS::NMask_i32( b, m ); + } + + template>* = nullptr> + FS_INLINE typename FS::int32v MaskedIncrement_i32( typename FS::int32v a, typename FS::mask32v m ) + { + return a - m; + } + + template>* = nullptr> + FS_INLINE typename FS::int32v MaskedIncrement_i32( typename FS::int32v a, typename FS::mask32v m ) + { + return MaskedSub_i32( a, typename FS::int32v( -1 ), m ); + } + template>* = nullptr> + FS_INLINE typename FS::int32v MaskedDecrement_i32( typename FS::int32v a, typename FS::mask32v m ) + { + return a + m; + } + + template>* = nullptr> + FS_INLINE typename FS::int32v MaskedDecrement_i32( typename FS::int32v a, typename FS::mask32v m ) + { + return MaskedAdd_i32( a, typename FS::int32v( -1 ), m ); + } + + // Bitwise + + template>* = nullptr> + FS_INLINE typename FS::mask32v BitwiseAndNot_m32( typename FS::mask32v a, typename FS::mask32v b ) + { + return FS::BitwiseAndNot_i32( a, b ); + } + + template>* = nullptr> + FS_INLINE typename FS::mask32v BitwiseAndNot_m32( typename FS::mask32v a, typename FS::mask32v b ) + { + return a & (~b); + } + + // Trig + + template + FS_INLINE typename FS::float32v Cos_f32( typename FS::float32v value ) + { + typedef typename FS::int32v int32v; + typedef typename FS::float32v float32v; + typedef typename FS::mask32v mask32v; + + value = FS_Abs_f32( value ); + value -= FS_Floor_f32( value * float32v( 0.1591549f ) ) * float32v( 6.283185f ); + + mask32v geHalfPi = value >= float32v( 1.570796f ); + mask32v geHalfPi2 = value >= float32v( 3.141593f ); + mask32v geHalfPi3 = value >= float32v( 4.7123889f ); + + float32v cosAngle = value ^ FS_Mask_f32( ( value ^ float32v( 3.141593f ) - value ), geHalfPi ); + cosAngle = cosAngle ^ FS_Mask_f32( FS_Casti32_f32( int32v( 0x80000000 ) ), geHalfPi2 ); + cosAngle = cosAngle ^ FS_Mask_f32( cosAngle ^ ( float32v( 6.283185f ) - value ), geHalfPi3 ); + + cosAngle *= cosAngle; + + cosAngle = FS_FMulAdd_f32( cosAngle, FS_FMulAdd_f32( cosAngle, float32v( 0.03679168f ), float32v( -0.49558072f ) ), float32v( 0.99940307f ) ); + + return cosAngle ^ FS_Mask_f32( FS_Casti32_f32( int32v( 0x80000000 ) ), FS_BitwiseAndNot_m32( geHalfPi, geHalfPi3 ) ); + } + + template + FS_INLINE typename FS::float32v Sin_f32( typename FS::float32v value ) + { + return Cos_f32( typename FS::float32v( 1.570796f ) - value ); + } + + template + FS_INLINE typename FS::float32v Exp_f32( typename FS::float32v x ) + { + typedef typename FS::int32v int32v; + typedef typename FS::float32v float32v; + + x = FS_Min_f32( x, float32v( 88.3762626647949f ) ); + x = FS_Max_f32( x, float32v( -88.3762626647949f ) ); + + /* express exp(x) as exp(g + n*log(2)) */ + float32v fx = x * float32v( 1.44269504088896341f ); + fx += float32v( 0.5f ); + + float32v flr = FS_Floor_f32( fx ); + fx = FS_MaskedSub_f32( flr, float32v( 1 ), flr > fx ); + + x -= fx * float32v( 0.693359375f ); + x -= fx * float32v( -2.12194440e-4f ); + + float32v y( 1.9875691500E-4f ); + y *= x; + y += float32v( 1.3981999507E-3f ); + y *= x; + y += float32v( 8.3334519073E-3f ); + y *= x; + y += float32v( 4.1665795894E-2f ); + y *= x; + y += float32v( 1.6666665459E-1f ); + y *= x; + y += float32v( 5.0000001201E-1f ); + y *= x * x; + y += x + float32v( 1 ); + + /* build 2^n */ + int32v i = FS_Convertf32_i32( fx ); + // another two AVX2 instructions + i += int32v( 0x7f ); + i <<= 23; + float32v pow2n = FS_Casti32_f32( i ); + + return y * pow2n; + } + + template + FS_INLINE typename FS::float32v Log_f32( typename FS::float32v x ) + { + typedef typename FS::int32v int32v; + typedef typename FS::float32v float32v; + typedef typename FS::mask32v mask32v; + + mask32v validMask = x > float32v( 0 ); + + x = FS_Max_f32( x, FS_Casti32_f32( int32v( 0x00800000 ) ) ); /* cut off denormalized stuff */ + + // can be done with AVX2 + int32v i = FS_BitwiseShiftRightZX_i32( FS_Castf32_i32( x ), 23 ); + + /* keep only the fractional part */ + x &= FS_Casti32_f32( int32v( ~0x7f800000 ) ); + x |= float32v( 0.5f ); + + // this is again another AVX2 instruction + i -= int32v( 0x7f ); + float32v e = FS_Converti32_f32( i ); + + e += float32v( 1 ); + + mask32v mask = x < float32v( 0.707106781186547524f ); + x = FS_MaskedAdd_f32( x, x, mask ); + x -= float32v( 1 ); + e = FS_MaskedSub_f32( e, float32v( 1 ), mask ); + + float32v y = float32v( 7.0376836292E-2f ); + y *= x; + y += float32v( -1.1514610310E-1f ); + y *= x; + y += float32v( 1.1676998740E-1f ); + y *= x; + y += float32v( -1.2420140846E-1f ); + y *= x; + y += float32v( 1.4249322787E-1f ); + y *= x; + y += float32v( -1.6668057665E-1f ); + y *= x; + y += float32v( 2.0000714765E-1f ); + y *= x; + y += float32v( -2.4999993993E-1f ); + y *= x; + y += float32v( 3.3333331174E-1f ); + y *= x; + + float32v xx = x * x; + y *= xx; + y *= e * float32v( -2.12194440e-4f ); + y -= xx * float32v( 0.5f ); + + x += y; + x += e * float32v( 0.693359375f ); + + return FS_Mask_f32( x, validMask ); + } + + template + FS_INLINE typename FS::float32v Pow_f32( typename FS::float32v value, typename FS::float32v pow ) + { + return Exp_f32( pow * Log_f32( value ) ); + } +} diff --git a/deps/FastNoise2/include/FastSIMD/InlInclude.h b/deps/FastNoise2/include/FastSIMD/InlInclude.h new file mode 100644 index 0000000..b4f4ae1 --- /dev/null +++ b/deps/FastNoise2/include/FastSIMD/InlInclude.h @@ -0,0 +1,10 @@ +#pragma once +#include "FunctionList.h" + +template +class FS_T; + +#define FASTSIMD_DECLARE_FS_TYPES \ +using float32v = typename FS::float32v;\ +using int32v = typename FS::int32v;\ +using mask32v = typename FS::mask32v diff --git a/deps/FastNoise2/include/FastSIMD/TypeList.h b/deps/FastNoise2/include/FastSIMD/TypeList.h new file mode 100644 index 0000000..bb624b2 --- /dev/null +++ b/deps/FastNoise2/include/FastSIMD/TypeList.h @@ -0,0 +1,37 @@ +#pragma once + +#include "FastSIMD.h" + +namespace FastSIMD +{ + template + struct SIMDTypeContainer + { + static constexpr eLevel MinimumCompiled = Level_Null; + + template + static constexpr eLevel GetNextCompiledAfter = Level_Null; + }; + + template + struct SIMDTypeContainer + { + static constexpr eLevel MinimumCompiled = (HEAD & COMPILED_SIMD_LEVELS) != 0 ? HEAD : SIMDTypeContainer::MinimumCompiled; + + template + static constexpr eLevel GetNextCompiledAfter = (L == HEAD) ? SIMDTypeContainer::MinimumCompiled : SIMDTypeContainer::template GetNextCompiledAfter; + }; + + using SIMDTypeList = SIMDTypeContainer< + Level_Scalar, + Level_SSE, + Level_SSE2, + Level_SSE3, + Level_SSSE3, + Level_SSE41, + Level_SSE42, + Level_AVX, + Level_AVX2, + Level_AVX512, + Level_NEON>; +} \ No newline at end of file diff --git a/deps/FastNoise2/src/CMakeLists.txt b/deps/FastNoise2/src/CMakeLists.txt new file mode 100644 index 0000000..f5d134e --- /dev/null +++ b/deps/FastNoise2/src/CMakeLists.txt @@ -0,0 +1,95 @@ +set(CMAKE_CXX_STANDARD 17) + +file(GLOB_RECURSE FastSIMD_headers "../include/FastSIMD/*.h") +file(GLOB_RECURSE FastSIMD_include_inl "../include/FastSIMD/*.inl") +file(GLOB FastSIMD_inline "FastSIMD/*.inl") +file(GLOB_RECURSE FastSIMD_internal_headers "FastSIMD/Internal/*.h") +file(GLOB_RECURSE FastSIMD_internal_inl "FastSIMD/Internal/*.inl") + +list(APPEND FastSIMD_headers ${FastSIMD_inline}) +list(APPEND FastSIMD_headers ${FastSIMD_include_inl}) +list(APPEND FastSIMD_internal_headers ${FastSIMD_internal_inl}) + +set(FastSIMD_sources + FastSIMD/FastSIMD.cpp + FastSIMD/FastSIMD_Level_AVX2.cpp + FastSIMD/FastSIMD_Level_AVX512.cpp + FastSIMD/FastSIMD_Level_NEON.cpp + FastSIMD/FastSIMD_Level_Scalar.cpp + FastSIMD/FastSIMD_Level_SSE2.cpp + FastSIMD/FastSIMD_Level_SSE3.cpp + FastSIMD/FastSIMD_Level_SSE41.cpp + FastSIMD/FastSIMD_Level_SSE42.cpp + FastSIMD/FastSIMD_Level_SSSE3.cpp +) + +file(GLOB FastNoise_headers "../include/FastNoise/*.h") +file(GLOB FastNoise_inl "../include/FastNoise/*.inl") +file(GLOB_RECURSE FastNoise_generators_headers "../include/FastNoise/Generators/*.h") +file(GLOB_RECURSE FastNoise_generators_inl "../include/FastNoise/Generators/*.inl") + +list(APPEND FastNoise_headers ${FastNoise_inl}) +list(APPEND FastNoise_generators_headers ${FastNoise_generators_inl}) + +set(FastNoise_source + FastNoise/FastNoiseMetadata.cpp +) + +source_group("SIMD" FILES ${FastSIMD_headers}) +source_group("SIMD" FILES ${FastSIMD_sources}) +source_group("SIMD\\internals" FILES ${FastSIMD_internal_headers}) + +source_group("FastNoise" FILES ${FastNoise_headers}) +source_group("FastNoise" FILES ${FastNoise_source}) +source_group("FastNoise\\Generators" FILES ${FastNoise_generators_headers}) + +add_library(FastNoise + ${FastNoise_headers} + ${FastNoise_source} + ${FastNoise_generators_headers} + ${FastSIMD_headers} + ${FastSIMD_internal_headers} + ${FastSIMD_sources} +) +set(install_targets ${install_targets} FastNoise PARENT_SCOPE) +set(install_fastnoise_headers ${FastNoise_headers} PARENT_SCOPE) +set(install_fastsimd_headers ${FastSIMD_headers} PARENT_SCOPE) + +target_include_directories(FastNoise SYSTEM PUBLIC + $ + $ +) + +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + target_compile_options(FastNoise PRIVATE /GL- /GS- /fp:fast) + + if(CMAKE_SIZEOF_VOID_P EQUAL 4) + set_source_files_properties(FastSIMD/FastSIMD_Level_Scalar.cpp PROPERTIES COMPILE_FLAGS "/arch:SSE") + set_source_files_properties(FastSIMD/FastSIMD_Level_SSE2.cpp PROPERTIES COMPILE_FLAGS "/arch:SSE2") + set_source_files_properties(FastSIMD/FastSIMD_Level_SSE3.cpp PROPERTIES COMPILE_FLAGS "/arch:SSE2") + set_source_files_properties(FastSIMD/FastSIMD_Level_SSSE3.cpp PROPERTIES COMPILE_FLAGS "/arch:SSE2") + set_source_files_properties(FastSIMD/FastSIMD_Level_SSE41.cpp PROPERTIES COMPILE_FLAGS "/arch:SSE2") + set_source_files_properties(FastSIMD/FastSIMD_Level_SSE42.cpp PROPERTIES COMPILE_FLAGS "/arch:SSE2") + endif() + set_source_files_properties(FastSIMD/FastSIMD_Level_AVX2.cpp PROPERTIES COMPILE_FLAGS "/arch:AVX2") + set_source_files_properties(FastSIMD/FastSIMD_Level_AVX512.cpp PROPERTIES COMPILE_FLAGS "/arch:AVX512") + +elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") + if(MSVC) + target_compile_options(FastNoise PRIVATE /GS- /fp:fast) + else() + target_compile_options(FastNoise PRIVATE "-ffast-math") + endif() + + if(CMAKE_SIZEOF_VOID_P EQUAL 4 OR "${CMAKE_CXX_FLAGS}" MATCHES "-m32") + set_source_files_properties(FastSIMD/FastSIMD_Level_Scalar.cpp PROPERTIES COMPILE_FLAGS "-msse") + set_source_files_properties(FastSIMD/FastSIMD_Level_SSE2.cpp PROPERTIES COMPILE_FLAGS "-msse2") + endif() + set_source_files_properties(FastSIMD/FastSIMD_Level_SSE3.cpp PROPERTIES COMPILE_FLAGS "-msse3") + set_source_files_properties(FastSIMD/FastSIMD_Level_SSSE3.cpp PROPERTIES COMPILE_FLAGS "-mssse3") + set_source_files_properties(FastSIMD/FastSIMD_Level_SSE41.cpp PROPERTIES COMPILE_FLAGS "-msse4.1") + set_source_files_properties(FastSIMD/FastSIMD_Level_SSE42.cpp PROPERTIES COMPILE_FLAGS "-msse4.2") + set_source_files_properties(FastSIMD/FastSIMD_Level_AVX2.cpp PROPERTIES COMPILE_FLAGS "-mavx2 -mfma") + set_source_files_properties(FastSIMD/FastSIMD_Level_AVX512.cpp PROPERTIES COMPILE_FLAGS "-mavx512f -mavx512dq -mfma") +endif() + diff --git a/deps/FastNoise2/src/FastNoise/Base64.h b/deps/FastNoise2/src/FastNoise/Base64.h new file mode 100644 index 0000000..0eef545 --- /dev/null +++ b/deps/FastNoise2/src/FastNoise/Base64.h @@ -0,0 +1,127 @@ +#pragma once + +#include +#include +#include +#include + +namespace FastNoise +{ + /** https://gist.github.com/tomykaira/f0fd86b6c73063283afe550bc5d77594 + * The MIT License (MIT) + * Copyright (c) 2016 tomykaira + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + namespace Base64 + { + static std::string Encode( const std::vector& data ) + { + static constexpr char sEncodingTable[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/' + }; + + size_t in_len = data.size(); + size_t out_len = 4 * ((in_len + 2) / 3); + std::string ret( out_len, '\0' ); + size_t i; + char* p = const_cast(ret.c_str()); + + for( i = 0; i < in_len - 2; i += 3 ) + { + *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; + *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)]; + *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2) | ((int)(data[i + 2] & 0xC0) >> 6)]; + *p++ = sEncodingTable[data[i + 2] & 0x3F]; + } + if( i < in_len ) + { + *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; + if( i == (in_len - 1) ) + { + *p++ = sEncodingTable[((data[i] & 0x3) << 4)]; + *p++ = '='; + } + else + { + *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)]; + *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2)]; + } + *p++ = '='; + } + + return ret; + } + + static std::vector Decode( const char* input ) + { + static constexpr unsigned char kDecodingTable[] = { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, + 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, + 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 + }; + + size_t in_len = std::strlen( input ); + if( in_len % 4 != 0 ) return {}; + + size_t out_len = in_len / 4 * 3; + if( input[in_len - 1] == '=' ) out_len--; + if( input[in_len - 2] == '=' ) out_len--; + + std::vector out( out_len ); + + for( size_t i = 0, j = 0; i < in_len; ) + { + uint32_t a = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + uint32_t b = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + uint32_t c = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + uint32_t d = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + + uint32_t triple = (a << 3 * 6) + (b << 2 * 6) + (c << 1 * 6) + (d << 0 * 6); + + if( j < out_len ) out[j++] = (triple >> 2 * 8) & 0xFF; + if( j < out_len ) out[j++] = (triple >> 1 * 8) & 0xFF; + if( j < out_len ) out[j++] = (triple >> 0 * 8) & 0xFF; + } + + return out; + } + }; +} diff --git a/deps/FastNoise2/src/FastNoise/FastNoiseMetadata.cpp b/deps/FastNoise2/src/FastNoise/FastNoiseMetadata.cpp new file mode 100644 index 0000000..e91a71f --- /dev/null +++ b/deps/FastNoise2/src/FastNoise/FastNoiseMetadata.cpp @@ -0,0 +1,390 @@ +#include "FastNoise/FastNoiseMetadata.h" +#include "Base64.h" + +#include +#include +#include +#include + +using namespace FastNoise; + +std::vector Metadata::sMetadataClasses; + +NodeData::NodeData( const Metadata* data ) +{ + metadata = data; + + if( metadata ) + { + for( const auto& value : metadata->memberVariables ) + { + variables.push_back( value.valueDefault ); + } + + for( const auto& value : metadata->memberNodes ) + { + (void)value; + nodes.push_back( nullptr ); + } + + for( const auto& value : metadata->memberHybrids ) + { + hybrids.emplace_back( nullptr, value.valueDefault ); + } + } +} + +template +void AddToDataStream( std::vector& dataStream, T value ) +{ + for( size_t i = 0; i < sizeof( T ); i++ ) + { + dataStream.push_back( (uint8_t)(value >> (i * 8)) ); + } +} + +bool SerialiseNodeDataInternal( NodeData* nodeData, bool fixUp, std::vector& dataStream, std::unordered_map& referenceIds, std::unordered_set dependancies = {} ) +{ + const Metadata* metadata = nodeData->metadata; + + if( !metadata || + nodeData->variables.size() != metadata->memberVariables.size() || + nodeData->nodes.size() != metadata->memberNodes.size() || + nodeData->hybrids.size() != metadata->memberHybrids.size() ) + { + assert( 0 ); // Member size mismatch with metadata + return false; + } + + if( fixUp ) + { + dependancies.insert( nodeData ); + + for( auto& node : nodeData->nodes ) + { + if( dependancies.find( node ) != dependancies.end() ) + { + node = nullptr; + } + } + for( auto& hybrid : nodeData->hybrids ) + { + if( dependancies.find( hybrid.first ) != dependancies.end() ) + { + hybrid.first = nullptr; + } + } + } + + auto reference = referenceIds.find( nodeData ); + + if( reference != referenceIds.end() ) + { + AddToDataStream( dataStream, UINT16_MAX ); + AddToDataStream( dataStream, reference->second ); + return true; + } + + AddToDataStream( dataStream, metadata->id ); + + for( size_t i = 0; i < metadata->memberVariables.size(); i++ ) + { + AddToDataStream( dataStream, nodeData->variables[i].i ); + } + + for( size_t i = 0; i < metadata->memberNodes.size(); i++ ) + { + if( fixUp && nodeData->nodes[i] ) + { + std::unique_ptr gen( metadata->NodeFactory() ); + SmartNode<> node( nodeData->nodes[i]->metadata->NodeFactory() ); + + if( !metadata->memberNodes[i].setFunc( gen.get(), node ) ) + { + nodeData->nodes[i] = nullptr; + return false; + } + } + + if( !nodeData->nodes[i] || !SerialiseNodeDataInternal( nodeData->nodes[i], fixUp, dataStream, referenceIds, dependancies ) ) + { + return false; + } + } + + for( size_t i = 0; i < metadata->memberHybrids.size(); i++ ) + { + if( !nodeData->hybrids[i].first ) + { + AddToDataStream( dataStream, (uint8_t)0 ); + + Metadata::MemberVariable::ValueUnion v = nodeData->hybrids[i].second; + + AddToDataStream( dataStream, v.i ); + } + else + { + if( fixUp ) + { + std::unique_ptr gen( metadata->NodeFactory() ); + std::shared_ptr node( nodeData->hybrids[i].first->metadata->NodeFactory() ); + + if( !metadata->memberHybrids[i].setNodeFunc( gen.get(), node ) ) + { + nodeData->hybrids[i].first = nullptr; + return false; + } + } + + AddToDataStream( dataStream, (uint8_t)1 ); + if( !SerialiseNodeDataInternal( nodeData->hybrids[i].first, fixUp, dataStream, referenceIds, dependancies ) ) + { + return false; + } + } + } + + referenceIds.emplace( nodeData, (uint16_t)referenceIds.size() ); + + return true; +} + +std::string Metadata::SerialiseNodeData( NodeData* nodeData, bool fixUp ) +{ + std::vector serialData; + std::unordered_map referenceIds; + + if( !SerialiseNodeDataInternal( nodeData, fixUp, serialData, referenceIds ) ) + { + return ""; + } + return Base64::Encode( serialData ); +} + +template +bool GetFromDataStream( const std::vector& dataStream, size_t& idx, T& value ) +{ + if( dataStream.size() < idx + sizeof( T ) ) + { + return false; + } + + value = *reinterpret_cast( dataStream.data() + idx ); + + idx += sizeof( T ); + return true; +} + +SmartNode<> DeserialiseSmartNodeInternal( const std::vector& serialisedNodeData, size_t& serialIdx, std::unordered_map>& referenceNodes, FastSIMD::eLevel level = FastSIMD::Level_Null ) +{ + uint16_t nodeId; + if( !GetFromDataStream( serialisedNodeData, serialIdx, nodeId ) ) + { + return nullptr; + } + + if( nodeId == UINT16_MAX ) + { + uint16_t referenceId; + if( !GetFromDataStream( serialisedNodeData, serialIdx, referenceId ) ) + { + return nullptr; + } + + auto refNode = referenceNodes.find( referenceId ); + + if( refNode == referenceNodes.end() ) + { + return nullptr; + } + + return refNode->second; + } + + const Metadata* metadata = Metadata::GetMetadataClass( nodeId ); + + if( !metadata ) + { + return nullptr; + } + + SmartNode<> generator( metadata->NodeFactory( level ) ); + + for( const auto& var : metadata->memberVariables ) + { + Metadata::MemberVariable::ValueUnion v; + + if( !GetFromDataStream( serialisedNodeData, serialIdx, v ) ) + { + return nullptr; + } + + var.setFunc( generator.get(), v ); + } + + for( const auto& node : metadata->memberNodes ) + { + SmartNode<> nodeGen = DeserialiseSmartNodeInternal( serialisedNodeData, serialIdx, referenceNodes, level ); + + if( !nodeGen || !node.setFunc( generator.get(), nodeGen ) ) + { + return nullptr; + } + } + + for( const auto& hybrid : metadata->memberHybrids ) + { + uint8_t isGenerator; + if( !GetFromDataStream( serialisedNodeData, serialIdx, isGenerator ) || isGenerator > 1 ) + { + return nullptr; + } + + if( isGenerator ) + { + SmartNode<> nodeGen = DeserialiseSmartNodeInternal( serialisedNodeData, serialIdx, referenceNodes, level ); + + if( !nodeGen || !hybrid.setNodeFunc( generator.get(), nodeGen ) ) + { + return nullptr; + } + } + else + { + float v; + + if( !GetFromDataStream( serialisedNodeData, serialIdx, v ) ) + { + return nullptr; + } + + hybrid.setValueFunc( generator.get(), v ); + } + } + + referenceNodes.emplace( (uint16_t)referenceNodes.size(), generator ); + + return generator; +} + +SmartNode<> Metadata::DeserialiseSmartNode( const char* serialisedBase64NodeData, FastSIMD::eLevel level ) +{ + std::vector dataStream = Base64::Decode( serialisedBase64NodeData ); + size_t startIdx = 0; + + std::unordered_map> referenceNodes; + + return DeserialiseSmartNodeInternal( dataStream, startIdx, referenceNodes, level ); +} + +NodeData* DeserialiseNodeDataInternal( const std::vector& serialisedNodeData, std::vector>& nodeDataOut, size_t& serialIdx, std::unordered_map& referenceNodes ) +{ + uint16_t nodeId; + if( !GetFromDataStream( serialisedNodeData, serialIdx, nodeId ) ) + { + return nullptr; + } + + if( nodeId == UINT16_MAX ) + { + uint16_t referenceId; + if( !GetFromDataStream( serialisedNodeData, serialIdx, referenceId ) ) + { + return nullptr; + } + + auto refNode = referenceNodes.find( referenceId ); + + if( refNode == referenceNodes.end() ) + { + return nullptr; + } + + return refNode->second; + } + + const Metadata* metadata = Metadata::GetMetadataClass( nodeId ); + + if( !metadata ) + { + return nullptr; + } + + std::unique_ptr nodeData( new NodeData( metadata ) ); + + for( auto& var : nodeData->variables ) + { + if( !GetFromDataStream( serialisedNodeData, serialIdx, var ) ) + { + return nullptr; + } + } + + for( auto& node : nodeData->nodes ) + { + node = DeserialiseNodeDataInternal( serialisedNodeData, nodeDataOut, serialIdx, referenceNodes ); + + if( !node ) + { + return nullptr; + } + } + + for( auto& hybrid : nodeData->hybrids ) + { + uint8_t isGenerator; + if( !GetFromDataStream( serialisedNodeData, serialIdx, isGenerator ) || isGenerator > 1 ) + { + return nullptr; + } + + if( isGenerator ) + { + hybrid.first = DeserialiseNodeDataInternal( serialisedNodeData, nodeDataOut, serialIdx, referenceNodes ); + + if( !hybrid.first ) + { + return nullptr; + } + } + else + { + if( !GetFromDataStream( serialisedNodeData, serialIdx, hybrid.second ) ) + { + return nullptr; + } + } + } + + referenceNodes.emplace( (uint16_t)referenceNodes.size(), nodeData.get() ); + + return nodeDataOut.emplace_back( std::move( nodeData ) ).get(); +} + +NodeData* Metadata::DeserialiseNodeData( const char* serialisedBase64NodeData, std::vector>& nodeDataOut ) +{ + std::vector dataStream = Base64::Decode( serialisedBase64NodeData ); + size_t startIdx = 0; + + std::unordered_map referenceNodes; + + return DeserialiseNodeDataInternal( dataStream, nodeDataOut, startIdx, referenceNodes ); +} + +#define FASTSIMD_BUILD_CLASS2( CLASS ) \ +const CLASS::Metadata g ## CLASS ## Metadata( #CLASS );\ +const FastNoise::Metadata* CLASS::GetMetadata() const\ +{\ + return &g ## CLASS ## Metadata;\ +}\ +Generator* CLASS::Metadata::NodeFactory( FastSIMD::eLevel l ) const\ +{\ + return FastSIMD::New( l );\ +} + +#define FASTSIMD_BUILD_CLASS( CLASS ) FASTSIMD_BUILD_CLASS2( CLASS ) + +#define FASTNOISE_CLASS( CLASS ) CLASS + +#define FASTSIMD_INCLUDE_HEADER_ONLY +#include "FastNoise/FastNoise_BuildList.inl" \ No newline at end of file diff --git a/deps/FastNoise2/src/FastSIMD/Example/Example.h b/deps/FastNoise2/src/FastSIMD/Example/Example.h new file mode 100644 index 0000000..f64ed15 --- /dev/null +++ b/deps/FastNoise2/src/FastSIMD/Example/Example.h @@ -0,0 +1,17 @@ +#include "FS_Class.inl" +#ifdef FASTSIMD_INCLUDE_CHECK +#include __FILE__ +#endif +#include "FS_Class.inl" +#pragma once + +FASTSIMD_CLASS_DECLARATION( Example ) +{ + FASTSIMD_CLASS_SETUP( FastSIMD::Level_AVX2 | FastSIMD::Level_SSE41 | FastSIMD::Level_SSE2 | FastSIMD::Level_Scalar ); + +public: + + FS_EXTERNAL_FUNC( void DoStuff( int* data ) ); + + FS_EXTERNAL_FUNC( void DoArray( int* data0, int* data1, int size ) ); +}; diff --git a/deps/FastNoise2/src/FastSIMD/Example/Example.inl b/deps/FastNoise2/src/FastSIMD/Example/Example.inl new file mode 100644 index 0000000..c640a01 --- /dev/null +++ b/deps/FastNoise2/src/FastSIMD/Example/Example.inl @@ -0,0 +1,125 @@ +#define FASTSIMD_INTELLISENSE +#include "Example.h" + +//template// Generic function, used if no specialised function found +//FS_CLASS( Example ) < T, FS_SIMD_CLASS::SIMD_Level >::FS_CLASS( Example )() +//{ +// int test = 1; +// +// test += test; +//} + +template // Generic function, used if no specialised function found +void FS_CLASS( Example )::DoStuff( int* data ) +{ + int32v a = int32v( 1 ); + + FS_Store_i32( data, a ); +} + +//template // Different function for level SSE2 or AVX2 +//void FS_CLASS( Example )::DoStuff( int* data ) +//{ +// int32v a = _mm_loadu_si128( reinterpret_cast<__m128i const*>(data) ); +// +// a += _mm_set_epi32( 2, 3, 4, 5 ); +// +// a -= _mm_castps_si128( FS_VecZero_f32( ) ); +// +// FS_Store_i32( data, a ); +//} +// +// +//template +//void FS_CLASS( Example )::DoArray( int* data0, int* data1, int size ) +//{ +// for ( int i = 0; i < size; i += FS_VectorSize_i32() ) +// { +// int32v a = FS_Load_i32( &data0[i] ); +// int32v b = FS_Load_i32( &data1[i] ); +// +// a *= b; +// +// a <<= 1; +// +// a -= FS_VecZero_i32(); +// +// (~a); +// +// FS_Store_i32( &data0[i], a ); +// } +//} + +template +void FS_CLASS( Example )::DoArray( int* data0, int* data1, int size ) +{ + for ( size_t i = 0; i < size; i += int32v::FS_Size() ) + { + int32v a = FS_Load_i32( &data0[i] ); + int32v b = FS_Load_i32( &data1[i] ); + + a += b; + + a <<= 1; + + a *= b; + + a -= int32v::FS_Zero(); + + (~a); + + FS_Store_i32( &data0[i], a ); + } +} + +template +class FS_CLASS( Example ) : public FS_CLASS( Example ) +{ + //typedef FastSIMD_AVX2 T_FS; + FASTSIMD_CLASS_SETUP( FastSIMD::COMPILED_SIMD_LEVELS ); + +public: + void DoArray( int* data0, int* data1, int size ) + { + for ( size_t i = 0; i < size; i += int32v::FS_Size() ) + { + int32v a = FS_Load_i32( &data0[i] ); + int32v b = FS_Load_i32( &data1[i] ); + + //a += gfhfdghdfgh(); + + a += b; + + a <<= 2; + + a *= b; + + a -= int32v::FS_Zero(); + + (~a); + + FS_Store_i32( &data0[i], a ); + } + } +}; + +// +//template +//typename std::enable_if<(T::SIMD_Level <= 1)>::type FS_CLASS( Example )::DoArray( int* data0, int* data1, int size ) +//{ +// for ( int i = 0; i < size; i += FS_VectorSize_i32() ) +// { +// int32v a = FS_Load_i32( &data0[i] ); +// int32v b = FS_Load_i32( &data1[i] ); +// +// a += b; +// +// a <<= 1; +// +// a -= FS_VecZero_i32(); +// +// (~a); +// +// FS_Store_i32( &data0[i], a ); +// } +//} diff --git a/deps/FastNoise2/src/FastSIMD/FastSIMD.cpp b/deps/FastNoise2/src/FastSIMD/FastSIMD.cpp new file mode 100644 index 0000000..432764b --- /dev/null +++ b/deps/FastNoise2/src/FastSIMD/FastSIMD.cpp @@ -0,0 +1,239 @@ +#include "FastSIMD/FastSIMD.h" + +#include +#include + +#ifdef __GNUG__ +#include +#else +#include +#endif + +#include "FastSIMD/TypeList.h" + + +static FastSIMD::eLevel simdLevel = FastSIMD::Level_Null; + +static_assert(FastSIMD::SIMDTypeList::MinimumCompiled & FastSIMD::COMPILED_SIMD_LEVELS, "FASTSIMD_FALLBACK_SIMD_LEVEL is not a compiled SIMD level, check FastSIMD_Config.h"); + +#if FASTSIMD_x86 +// Define interface to cpuid instruction. +// input: eax = functionnumber, ecx = 0 +// output: eax = output[0], ebx = output[1], ecx = output[2], edx = output[3] +static void cpuid( int output[4], int functionnumber ) +{ +#if defined( __GNUC__ ) || defined( __clang__ ) // use inline assembly, Gnu/AT&T syntax + + int a, b, c, d; + __asm("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "a"(functionnumber), "c"(0) : ); + output[0] = a; + output[1] = b; + output[2] = c; + output[3] = d; + +#elif defined( _MSC_VER ) || defined ( __INTEL_COMPILER ) // Microsoft or Intel compiler, intrin.h included + + __cpuidex( output, functionnumber, 0 ); // intrinsic function for CPUID + +#else // unknown platform. try inline assembly with masm/intel syntax + + __asm + { + mov eax, functionnumber + xor ecx, ecx + cpuid; + mov esi, output + mov[esi], eax + mov[esi + 4], ebx + mov[esi + 8], ecx + mov[esi + 12], edx + } + +#endif +} + +// Define interface to xgetbv instruction +static int64_t xgetbv( int ctr ) +{ +#if (defined( _MSC_FULL_VER ) && _MSC_FULL_VER >= 160040000) || (defined( __INTEL_COMPILER ) && __INTEL_COMPILER >= 1200) // Microsoft or Intel compiler supporting _xgetbv intrinsic + + return _xgetbv( ctr ); // intrinsic function for XGETBV + +#elif defined( __GNUC__ ) // use inline assembly, Gnu/AT&T syntax + + uint32_t a, d; + __asm("xgetbv" : "=a"(a), "=d"(d) : "c"(ctr) : ); + return a | (uint64_t( d ) << 32); + +#else // #elif defined (_WIN32) // other compiler. try inline assembly with masm/intel/MS syntax + + uint32_t a, d; + __asm { + mov ecx, ctr + _emit 0x0f + _emit 0x01 + _emit 0xd0; // xgetbv + mov a, eax + mov d, edx + } + return a | (uint64_t( d ) << 32); + +#endif +} +#endif + + +FastSIMD::eLevel FastSIMD::CPUMaxSIMDLevel() +{ + if ( simdLevel > Level_Null ) + { + return simdLevel; + } + +#if FASTSIMD_x86 + int abcd[4] = { 0,0,0,0 }; // cpuid results + +#if !FASTSIMD_64BIT + simdLevel = Level_Scalar; // default value + + cpuid( abcd, 0 ); // call cpuid function 0 + if ( abcd[0] == 0 ) + return simdLevel; // no further cpuid function supported + + cpuid( abcd, 1 ); // call cpuid function 1 for feature flags + if ( (abcd[3] & (1 << 0)) == 0 ) + return simdLevel; // no floating point + if ( (abcd[3] & (1 << 23)) == 0 ) + return simdLevel; // no MMX + if ( (abcd[3] & (1 << 15)) == 0 ) + return simdLevel; // no conditional move + if ( (abcd[3] & (1 << 24)) == 0 ) + return simdLevel; // no FXSAVE + if ( (abcd[3] & (1 << 25)) == 0 ) + return simdLevel; // no SSE + simdLevel = Level_SSE; + // 1: SSE supported + + if ( (abcd[3] & (1 << 26)) == 0 ) + return simdLevel; // no SSE2 +#else + cpuid( abcd, 1 ); // call cpuid function 1 for feature flags +#endif + + simdLevel = Level_SSE2; // default value for 64bit + // 2: SSE2 supported + + if ( (abcd[2] & (1 << 0)) == 0 ) + return simdLevel; // no SSE3 + simdLevel = Level_SSE3; + // 3: SSE3 supported + + if ( (abcd[2] & (1 << 9)) == 0 ) + return simdLevel; // no SSSE3 + simdLevel = Level_SSSE3; + // 4: SSSE3 supported + + if ( (abcd[2] & (1 << 19)) == 0 ) + return simdLevel; // no SSE4.1 + simdLevel = Level_SSE41; + // 5: SSE4.1 supported + + if ( (abcd[2] & (1 << 23)) == 0 ) + return simdLevel; // no POPCNT + if ( (abcd[2] & (1 << 20)) == 0 ) + return simdLevel; // no SSE4.2 + simdLevel = Level_SSE42; + // 6: SSE4.2 supported + + if ( (abcd[2] & (1 << 26)) == 0 ) + return simdLevel; // no XSAVE + if ( (abcd[2] & (1 << 27)) == 0 ) + return simdLevel; // no OSXSAVE + if ( (abcd[2] & (1 << 28)) == 0 ) + return simdLevel; // no AVX + + uint64_t osbv = xgetbv( 0 ); + if ( (osbv & 6) != 6 ) + return simdLevel; // AVX not enabled in O.S. + simdLevel = Level_AVX; + // 7: AVX supported + + cpuid( abcd, 7 ); // call cpuid leaf 7 for feature flags + if ( (abcd[1] & (1 << 5)) == 0 ) + return simdLevel; // no AVX2 + simdLevel = Level_AVX2; + // 8: AVX2 supported + + if( (osbv & (0xE0)) != 0xE0 ) + return simdLevel; // AVX512 not enabled in O.S. + if ( (abcd[1] & (1 << 16)) == 0 ) + return simdLevel; // no AVX512 + cpuid( abcd, 0xD ); // call cpuid leaf 0xD for feature flags + if ( (abcd[0] & 0x60) != 0x60 ) + return simdLevel; // no AVX512 + // 9: AVX512 supported + + cpuid( abcd, 7 ); // call cpuid leaf 7 for feature flags + if ( (abcd[1] & (1 << 31)) == 0 ) + return simdLevel; // no AVX512VL + // 10: AVX512VL supported + + if ( (abcd[1] & 0x40020000) != 0x40020000 ) + return simdLevel; // no AVX512BW, AVX512DQ + simdLevel = Level_AVX512; + // 11: AVX512BW & AVX512DQ supported +#endif + +#if FASTSIMD_ARM + simdLevel = Level_NEON; +#endif + + return simdLevel; +} + +template +CLASS_T* SIMDLevelSelector( FastSIMD::eLevel maxSIMDLevel ) +{ + if constexpr( ( CLASS_T::Supported_SIMD_Levels & SIMD_LEVEL ) != 0 ) + { + CLASS_T* newClass = SIMDLevelSelector>( maxSIMDLevel ); + + if( !newClass && SIMD_LEVEL <= maxSIMDLevel ) + { + return FastSIMD::ClassFactory(); + } + + return newClass; + } + else + { + if constexpr( SIMD_LEVEL == FastSIMD::Level_Null ) + { + return nullptr; + } + + return SIMDLevelSelector>( maxSIMDLevel ); + } +} + +template +CLASS_T* FastSIMD::New( eLevel maxSIMDLevel ) +{ + if( maxSIMDLevel == Level_Null ) + { + maxSIMDLevel = CPUMaxSIMDLevel(); + } + else + { + maxSIMDLevel = std::min( maxSIMDLevel, CPUMaxSIMDLevel() ); + } + + static_assert(( CLASS_T::Supported_SIMD_Levels & FastSIMD::SIMDTypeList::MinimumCompiled ), "MinimumCompiled SIMD Level must be supported by this class" ); + return SIMDLevelSelector( maxSIMDLevel ); +} + +#define FASTSIMD_BUILD_CLASS( CLASS ) \ +template CLASS* FastSIMD::New( FastSIMD::eLevel ); + +#define FASTSIMD_INCLUDE_HEADER_ONLY +#include "FastSIMD_BuildList.inl" diff --git a/deps/FastNoise2/src/FastSIMD/FastSIMD_BuildList.inl b/deps/FastNoise2/src/FastSIMD/FastSIMD_BuildList.inl new file mode 100644 index 0000000..8e65ff2 --- /dev/null +++ b/deps/FastNoise2/src/FastSIMD/FastSIMD_BuildList.inl @@ -0,0 +1,10 @@ +#pragma once + +#ifndef FASTSIMD_BUILD_CLASS +#error Do not include this file +#endif + +//#include "Example/Example.inl" +//FASTSIMD_BUILD_CLASS( Example ) + +#include "FastNoise/FastNoise_BuildList.inl" \ No newline at end of file diff --git a/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_AVX2.cpp b/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_AVX2.cpp new file mode 100644 index 0000000..c8ae3ed --- /dev/null +++ b/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_AVX2.cpp @@ -0,0 +1,17 @@ +#include "FastSIMD/FastSIMD.h" + +#if FASTSIMD_COMPILE_AVX2 + +// To compile AVX2 support enable AVX(2) code generation compiler flags for this file +#ifndef __AVX__ +#ifdef _MSC_VER +#error To compile AVX set C++ code generation to use /arch:AVX on FastSIMD_Level_AVX2.cpp, or change "#define FASTSIMD_COMPILE_AVX2" in FastSIMD_Config.h +#else +#error To compile AVX add build command "-march=core-avx" on FastSIMD_Level_AVX2.cpp, or change "#define FASTSIMD_COMPILE_AVX2" in FastSIMD_Config.h +#endif +#endif + +#include "Internal/AVX.h" +#define FS_SIMD_CLASS FastSIMD::AVX2 +#include "Internal/SourceBuilder.inl" +#endif \ No newline at end of file diff --git a/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_AVX512.cpp b/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_AVX512.cpp new file mode 100644 index 0000000..1472d65 --- /dev/null +++ b/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_AVX512.cpp @@ -0,0 +1,17 @@ +#include "FastSIMD/FastSIMD.h" + +#if FASTSIMD_COMPILE_AVX512 + +// To compile AVX512 support enable AVX512 code generation compiler flags for this file +#ifndef __AVX512DQ__ +#ifdef _MSC_VER +#error To compile AVX512 set C++ code generation to use /arch:AVX512 on FastSIMD_Level_AVX512.cpp, or change "#define FASTSIMD_COMPILE_AVX512" in FastSIMD_Config.h +#else +#error To compile AVX512 add build command "-mavx512f -mavx512dq" on FastSIMD_Level_AVX512.cpp, or change "#define FASTSIMD_COMPILE_AVX512" in FastSIMD_Config.h +#endif +#endif + +#include "Internal/AVX512.h" +#define FS_SIMD_CLASS FastSIMD::AVX512 +#include "Internal/SourceBuilder.inl" +#endif \ No newline at end of file diff --git a/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_NEON.cpp b/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_NEON.cpp new file mode 100644 index 0000000..e804ace --- /dev/null +++ b/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_NEON.cpp @@ -0,0 +1,7 @@ +#include "FastSIMD/FastSIMD.h" + +#if FASTSIMD_COMPILE_NEON +#include "Internal/NEON.h" +#define FS_SIMD_CLASS FastSIMD::NEON +#include "Internal/SourceBuilder.inl" +#endif \ No newline at end of file diff --git a/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_SSE2.cpp b/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_SSE2.cpp new file mode 100644 index 0000000..a36c4f6 --- /dev/null +++ b/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_SSE2.cpp @@ -0,0 +1,7 @@ +#include "FastSIMD/FastSIMD.h" + +#if FASTSIMD_COMPILE_SSE2 +#include "Internal/SSE.h" +#define FS_SIMD_CLASS FastSIMD::SSE2 +#include "Internal/SourceBuilder.inl" +#endif diff --git a/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_SSE3.cpp b/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_SSE3.cpp new file mode 100644 index 0000000..a633767 --- /dev/null +++ b/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_SSE3.cpp @@ -0,0 +1,7 @@ +#include "FastSIMD/FastSIMD.h" + +#if FASTSIMD_COMPILE_SSE3 +#include "Internal/SSE.h" +#define FS_SIMD_CLASS FastSIMD::SSE3 +#include "Internal/SourceBuilder.inl" +#endif diff --git a/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_SSE41.cpp b/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_SSE41.cpp new file mode 100644 index 0000000..b33ba48 --- /dev/null +++ b/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_SSE41.cpp @@ -0,0 +1,7 @@ +#include "FastSIMD/FastSIMD.h" + +#if FASTSIMD_COMPILE_SSE41 +#include "Internal/SSE.h" +#define FS_SIMD_CLASS FastSIMD::SSE41 +#include "Internal/SourceBuilder.inl" +#endif diff --git a/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_SSE42.cpp b/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_SSE42.cpp new file mode 100644 index 0000000..140065e --- /dev/null +++ b/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_SSE42.cpp @@ -0,0 +1,7 @@ +#include "FastSIMD/FastSIMD.h" + +#if FASTSIMD_COMPILE_SSE42 +#include "Internal/SSE.h" +#define FS_SIMD_CLASS FastSIMD::SSE42 +#include "Internal/SourceBuilder.inl" +#endif diff --git a/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_SSSE3.cpp b/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_SSSE3.cpp new file mode 100644 index 0000000..f91de06 --- /dev/null +++ b/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_SSSE3.cpp @@ -0,0 +1,7 @@ +#include "FastSIMD/FastSIMD.h" + +#if FASTSIMD_COMPILE_SSSE3 +#include "Internal/SSE.h" +#define FS_SIMD_CLASS FastSIMD::SSSE3 +#include "Internal/SourceBuilder.inl" +#endif diff --git a/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_Scalar.cpp b/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_Scalar.cpp new file mode 100644 index 0000000..87aff72 --- /dev/null +++ b/deps/FastNoise2/src/FastSIMD/FastSIMD_Level_Scalar.cpp @@ -0,0 +1,7 @@ +#include "FastSIMD/FastSIMD.h" + +#if FASTSIMD_COMPILE_SCALAR +#include "Internal/Scalar.h" +#define FS_SIMD_CLASS FastSIMD::Scalar +#include "Internal/SourceBuilder.inl" +#endif \ No newline at end of file diff --git a/deps/FastNoise2/src/FastSIMD/Internal/AVX.h b/deps/FastNoise2/src/FastSIMD/Internal/AVX.h new file mode 100644 index 0000000..ea28119 --- /dev/null +++ b/deps/FastNoise2/src/FastSIMD/Internal/AVX.h @@ -0,0 +1,448 @@ +#pragma once + +#ifdef __GNUG__ +#include +#else +#include +#endif + +#include "VecTools.h" + +namespace FastSIMD +{ + struct AVX_f32x8 + { + FASTSIMD_INTERNAL_TYPE_SET( AVX_f32x8, __m256 ); + + FS_INLINE static AVX_f32x8 Incremented() + { + return _mm256_set_ps( 7.0f, 6.0f, 5.0f, 4.0f, 3.0f, 2.0f, 1.0f, 0.0f ); + } + + FS_INLINE explicit AVX_f32x8( float f ) + { + *this = _mm256_set1_ps( f ); + } + + FS_INLINE explicit AVX_f32x8( float f0, float f1, float f2, float f3, float f4, float f5, float f6, float f7 ) + { + *this = _mm256_set_ps( f7, f6, f5, f4, f3, f2, f1, f0 ); + } + + FS_INLINE AVX_f32x8& operator+=( const AVX_f32x8& rhs ) + { + *this = _mm256_add_ps( *this, rhs ); + return *this; + } + + FS_INLINE AVX_f32x8& operator-=( const AVX_f32x8& rhs ) + { + *this = _mm256_sub_ps( *this, rhs ); + return *this; + } + + FS_INLINE AVX_f32x8& operator*=( const AVX_f32x8& rhs ) + { + *this = _mm256_mul_ps( *this, rhs ); + return *this; + } + + FS_INLINE AVX_f32x8& operator/=( const AVX_f32x8& rhs ) + { + *this = _mm256_div_ps( *this, rhs ); + return *this; + } + + FS_INLINE AVX_f32x8& operator&=( const AVX_f32x8& rhs ) + { + *this = _mm256_and_ps( *this, rhs ); + return *this; + } + + FS_INLINE AVX_f32x8& operator|=( const AVX_f32x8& rhs ) + { + *this = _mm256_or_ps( *this, rhs ); + return *this; + } + + FS_INLINE AVX_f32x8& operator^=( const AVX_f32x8& rhs ) + { + *this = _mm256_xor_ps( *this, rhs ); + return *this; + } + + FS_INLINE AVX_f32x8 operator~() const + { +#if FASTSIMD_CONFIG_GENERATE_CONSTANTS + const __m256i neg1 = _mm256_cmpeq_epi32( _mm256_setzero_si256(), _mm256_setzero_si256() ); +#else + const __m256i neg1 = _mm256_set1_epi32( -1 ); +#endif + return _mm256_xor_ps( *this, _mm256_castsi256_ps( neg1 ) ); + } + + FS_INLINE AVX_f32x8 operator-() const + { +#if FASTSIMD_CONFIG_GENERATE_CONSTANTS + const __m256i minInt = _mm256_slli_epi32( _mm256_cmpeq_epi32( _mm256_setzero_si256(), _mm256_setzero_si256() ), 31 ); +#else + const __m256i minInt = _mm256_set1_epi32( 0x80000000 ); +#endif + return _mm256_xor_ps( *this, _mm256_castsi256_ps( minInt ) ); + } + + FS_INLINE __m256i operator==( const AVX_f32x8& rhs ) + { + return _mm256_castps_si256( _mm256_cmp_ps( *this, rhs, _CMP_EQ_OS ) ); + } + + FS_INLINE __m256i operator!=( const AVX_f32x8& rhs ) + { + return _mm256_castps_si256( _mm256_cmp_ps( *this, rhs, _CMP_NEQ_OS ) ); + } + + FS_INLINE __m256i operator>( const AVX_f32x8& rhs ) + { + return _mm256_castps_si256( _mm256_cmp_ps( *this, rhs, _CMP_GT_OS ) ); + } + + FS_INLINE __m256i operator<( const AVX_f32x8& rhs ) + { + return _mm256_castps_si256( _mm256_cmp_ps( *this, rhs, _CMP_LT_OS ) ); + } + + FS_INLINE __m256i operator>=( const AVX_f32x8& rhs ) + { + return _mm256_castps_si256( _mm256_cmp_ps( *this, rhs, _CMP_GE_OS ) ); + } + + FS_INLINE __m256i operator<=( const AVX_f32x8& rhs ) + { + return _mm256_castps_si256( _mm256_cmp_ps( *this, rhs, _CMP_LE_OS ) ); + } + }; + + FASTSIMD_INTERNAL_OPERATORS_FLOAT( AVX_f32x8 ) + + + struct AVX2_i32x8 + { + FASTSIMD_INTERNAL_TYPE_SET( AVX2_i32x8, __m256i ); + + FS_INLINE static AVX2_i32x8 Incremented() + { + return _mm256_set_epi32( 7, 6, 5, 4, 3, 2, 1, 0 ); + } + + FS_INLINE explicit AVX2_i32x8( int32_t f ) + { + *this = _mm256_set1_epi32( f ); + } + + FS_INLINE explicit AVX2_i32x8( int32_t i0, int32_t i1, int32_t i2, int32_t i3, int32_t i4, int32_t i5, int32_t i6, int32_t i7 ) + { + *this = _mm256_set_epi32( i7, i6, i5, i4, i3, i2, i1, i0 ); + } + + FS_INLINE AVX2_i32x8& operator+=( const AVX2_i32x8& rhs ) + { + *this = _mm256_add_epi32( *this, rhs ); + return *this; + } + + FS_INLINE AVX2_i32x8& operator-=( const AVX2_i32x8& rhs ) + { + *this = _mm256_sub_epi32( *this, rhs ); + return *this; + } + + FS_INLINE AVX2_i32x8& operator*=( const AVX2_i32x8& rhs ) + { + *this = _mm256_mullo_epi32( *this, rhs ); + return *this; + } + + FS_INLINE AVX2_i32x8& operator&=( const AVX2_i32x8& rhs ) + { + *this = _mm256_and_si256( *this, rhs ); + return *this; + } + + FS_INLINE AVX2_i32x8& operator|=( const AVX2_i32x8& rhs ) + { + *this = _mm256_or_si256( *this, rhs ); + return *this; + } + + FS_INLINE AVX2_i32x8& operator^=( const AVX2_i32x8& rhs ) + { + *this = _mm256_xor_si256( *this, rhs ); + return *this; + } + + FS_INLINE AVX2_i32x8& operator>>=( int32_t rhs ) + { + *this = _mm256_srai_epi32( *this, rhs ); + return *this; + } + + FS_INLINE AVX2_i32x8& operator<<=( int32_t rhs ) + { + *this = _mm256_slli_epi32( *this, rhs ); + return *this; + } + + FS_INLINE AVX2_i32x8 operator~() const + { +#if FASTSIMD_CONFIG_GENERATE_CONSTANTS + const __m256i neg1 = _mm256_cmpeq_epi32( _mm256_setzero_si256(), _mm256_setzero_si256() ); +#else + const __m256i neg1 = _mm256_set1_epi32( -1 ); +#endif + return _mm256_xor_si256( *this, neg1 ); + } + + FS_INLINE AVX2_i32x8 operator-() const + { + return _mm256_sub_epi32( _mm256_setzero_si256(), *this ); + } + + FS_INLINE AVX2_i32x8 operator==( const AVX2_i32x8& rhs ) + { + return _mm256_cmpeq_epi32( *this, rhs ); + } + + FS_INLINE AVX2_i32x8 operator>( const AVX2_i32x8& rhs ) + { + return _mm256_cmpgt_epi32( *this, rhs ); + } + + FS_INLINE AVX2_i32x8 operator<( const AVX2_i32x8& rhs ) + { + return _mm256_cmpgt_epi32( rhs, *this ); + } + }; + + FASTSIMD_INTERNAL_OPERATORS_INT( AVX2_i32x8, int32_t ) + + template + class AVX_T + { + public: + static_assert( LEVEL_T >= Level_AVX && LEVEL_T <= Level_AVX2, "Cannot create template with unsupported SIMD level" ); + + static constexpr eLevel SIMD_Level = LEVEL_T; + + template + static constexpr size_t VectorSize = 256 / ElementSize; + + typedef AVX_f32x8 float32v; + typedef AVX2_i32x8 int32v; + typedef AVX2_i32x8 mask32v; + + // Load + + FS_INLINE static float32v Load_f32( void const* p ) + { + return _mm256_loadu_ps( reinterpret_cast(p) ); + } + + FS_INLINE static int32v Load_i32( void const* p ) + { + return _mm256_loadu_si256( reinterpret_cast<__m256i const*>(p) ); + } + + // Store + + FS_INLINE static void Store_f32( void* p, float32v a ) + { + _mm256_storeu_ps( reinterpret_cast(p), a ); + } + + FS_INLINE static void Store_i32( void* p, int32v a ) + { + _mm256_storeu_si256( reinterpret_cast<__m256i*>(p), a ); + } + + // Cast + + FS_INLINE static float32v Casti32_f32( int32v a ) + { + return _mm256_castsi256_ps( a ); + } + + FS_INLINE static int32v Castf32_i32( float32v a ) + { + return _mm256_castps_si256( a ); + } + + // Convert + + FS_INLINE static float32v Converti32_f32( int32v a ) + { + return _mm256_cvtepi32_ps( a ); + } + + FS_INLINE static int32v Convertf32_i32( float32v a ) + { + return _mm256_cvtps_epi32( a ); + } + + // Select + + FS_INLINE static float32v Select_f32( mask32v m, float32v a, float32v b ) + { + return _mm256_blendv_ps( b, a, _mm256_castsi256_ps( m ) ); + } + + FS_INLINE static int32v Select_i32( mask32v m, int32v a, int32v b ) + { + return _mm256_castps_si256( _mm256_blendv_ps( _mm256_castsi256_ps( b ), _mm256_castsi256_ps( a ), _mm256_castsi256_ps( m ) ) ); + } + + // Min, Max + + FS_INLINE static float32v Min_f32( float32v a, float32v b ) + { + return _mm256_min_ps( a, b ); + } + + FS_INLINE static float32v Max_f32( float32v a, float32v b ) + { + return _mm256_max_ps( a, b ); + } + + FS_INLINE static int32v Min_i32( int32v a, int32v b ) + { + return _mm256_min_epi32( a, b ); + } + + FS_INLINE static int32v Max_i32( int32v a, int32v b ) + { + return _mm256_max_epi32( a, b ); + } + + // Bitwise + + FS_INLINE static float32v BitwiseAndNot_f32( float32v a, float32v b ) + { + return _mm256_andnot_ps( b, a ); + } + + FS_INLINE static int32v BitwiseAndNot_i32( int32v a, int32v b ) + { + return _mm256_andnot_si256( b, a ); + } + + FS_INLINE static float32v BitwiseShiftRightZX_f32( float32v a, int32_t b ) + { + return Casti32_f32( _mm256_srli_epi32( Castf32_i32( a ), b ) ); + } + + FS_INLINE static int32v BitwiseShiftRightZX_i32( int32v a, int32_t b ) + { + return _mm256_srli_epi32( a, b ); + } + + // Abs + + FS_INLINE static float32v Abs_f32( float32v a ) + { +#if FASTSIMD_CONFIG_GENERATE_CONSTANTS + const __m256i intMax = _mm256_srli_epi32( _mm256_cmpeq_epi32( _mm256_setzero_si256(), _mm256_setzero_si256() ), 1 ); +#else + const __m256i intMax = _mm256_set1_epi32( 0x7FFFFFFF ); +#endif + return _mm256_and_ps( a, _mm256_castsi256_ps( intMax ) ); + } + + FS_INLINE static int32v Abs_i32( int32v a ) + { + return _mm256_abs_epi32( a ); + } + + // Float math + + FS_INLINE static float32v Sqrt_f32( float32v a ) + { + return _mm256_sqrt_ps( a ); + } + + FS_INLINE static float32v InvSqrt_f32( float32v a ) + { + return _mm256_rsqrt_ps( a ); + } + + FS_INLINE static float32v Reciprocal_f32( float32v a ) + { + return _mm256_rcp_ps( a ); + } + + // Floor, Ceil, Round + + FS_INLINE static float32v Floor_f32( float32v a ) + { + return _mm256_round_ps( a, _MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC ); + } + + FS_INLINE static float32v Ceil_f32( float32v a ) + { + return _mm256_round_ps( a, _MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC ); + } + + FS_INLINE static float32v Round_f32( float32v a ) + { + return _mm256_round_ps( a, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC ); + } + + //Mask + + FS_INLINE static int32v Mask_i32( int32v a, mask32v m ) + { + return a & m; + } + + FS_INLINE static float32v Mask_f32( float32v a, mask32v m ) + { + return _mm256_and_ps( a, _mm256_castsi256_ps( m ) ); + } + + FS_INLINE static int32v NMask_i32( int32v a, mask32v m ) + { + return _mm256_andnot_si256( m, a ); + } + + FS_INLINE static float32v NMask_f32( float32v a, mask32v m ) + { + return _mm256_andnot_ps( _mm256_castsi256_ps( m ), a ); + } + + FS_INLINE static bool AnyMask_bool( mask32v m ) + { + return _mm256_movemask_ps( _mm256_castsi256_ps( m ) ); + } + }; + +#if FASTSIMD_COMPILE_AVX + typedef AVX_T AVX; +#endif + +#if FASTSIMD_COMPILE_AVX2 + typedef AVX_T AVX2; + +#if FASTSIMD_USE_FMA + template<> + FS_INLINE AVX2::float32v FMulAdd_f32( AVX2::float32v a, AVX2::float32v b, AVX2::float32v c ) + { + return _mm256_fmadd_ps( a, b, c ); + } + + template<> + FS_INLINE AVX2::float32v FNMulAdd_f32( AVX2::float32v a, AVX2::float32v b, AVX2::float32v c ) + { + return _mm256_fnmadd_ps( a, b, c ); + } +#endif +#endif + +} diff --git a/deps/FastNoise2/src/FastSIMD/Internal/AVX512.h b/deps/FastNoise2/src/FastSIMD/Internal/AVX512.h new file mode 100644 index 0000000..626d556 --- /dev/null +++ b/deps/FastNoise2/src/FastSIMD/Internal/AVX512.h @@ -0,0 +1,516 @@ +#pragma once + +#include + +#include "VecTools.h" + +namespace FastSIMD +{ + + struct AVX512_f32x16 + { + FASTSIMD_INTERNAL_TYPE_SET( AVX512_f32x16, __m512 ); + + FS_INLINE static AVX512_f32x16 Incremented() + { + return _mm512_set_ps( 15.0f, 14.0f, 13.0f, 12.0f, 11.0f, 10.0f, 9.0f, 8.0f, 7.0f, 6.0f, 5.0f, 4.0f, 3.0f, 2.0f, 1.0f, 0.0f ); + } + + FS_INLINE explicit AVX512_f32x16( float f ) + { + *this = _mm512_set1_ps( f ); + } + + FS_INLINE explicit AVX512_f32x16( float f0, float f1, float f2, float f3, float f4, float f5, float f6, float f7, float f8, float f9, float f10, float f11, float f12, float f13, float f14, float f15 ) + { + *this = _mm512_set_ps( f15, f14, f13, f12, f11, f10, f9, f8, f7, f6, f5, f4, f3, f2, f1, f0 ); + } + + FS_INLINE AVX512_f32x16& operator+=( const AVX512_f32x16& rhs ) + { + *this = _mm512_add_ps( *this, rhs ); + return *this; + } + + FS_INLINE AVX512_f32x16& operator-=( const AVX512_f32x16& rhs ) + { + *this = _mm512_sub_ps( *this, rhs ); + return *this; + } + + FS_INLINE AVX512_f32x16& operator*=( const AVX512_f32x16& rhs ) + { + *this = _mm512_mul_ps( *this, rhs ); + return *this; + } + + FS_INLINE AVX512_f32x16& operator/=( const AVX512_f32x16& rhs ) + { + *this = _mm512_div_ps( *this, rhs ); + return *this; + } + + FS_INLINE AVX512_f32x16& operator&=( const AVX512_f32x16& rhs ) + { + *this = _mm512_and_ps( *this, rhs ); + return *this; + } + + FS_INLINE AVX512_f32x16& operator|=( const AVX512_f32x16& rhs ) + { + *this = _mm512_or_ps( *this, rhs ); + return *this; + } + + FS_INLINE AVX512_f32x16& operator^=( const AVX512_f32x16& rhs ) + { + *this = _mm512_xor_ps( *this, rhs ); + return *this; + } + + FS_INLINE AVX512_f32x16 operator~() const + { +#if FASTSIMD_CONFIG_GENERATE_CONSTANTS + const __m512i neg1 = _mm512_cmpeq_epi32( _mm512_setzero_si512(), _mm512_setzero_si512() ); +#else + const __m512i neg1 = _mm512_set1_epi32( -1 ); +#endif + return _mm512_xor_ps( *this, _mm512_castsi512_ps( neg1 ) ); + } + + FS_INLINE AVX512_f32x16 operator-() const + { +#if FASTSIMD_CONFIG_GENERATE_CONSTANTS + const __m512i minInt = _mm512_slli_epi32( _mm512_cmpeq_epi32( _mm512_setzero_si512(), _mm512_setzero_si512() ), 31 ); +#else + const __m512i minInt = _mm512_set1_epi32( 0x80000000 ); +#endif + return _mm512_xor_ps( *this, _mm512_castsi512_ps( minInt ) ); + } + + FS_INLINE __mmask16 operator==( const AVX512_f32x16& rhs ) + { + return _mm512_cmp_ps_mask( *this, rhs, _CMP_EQ_OS ); + } + + FS_INLINE __mmask16 operator!=( const AVX512_f32x16& rhs ) + { + return _mm512_cmp_ps_mask( *this, rhs, _CMP_NEQ_OS ); + } + + FS_INLINE __mmask16 operator>( const AVX512_f32x16& rhs ) + { + return _mm512_cmp_ps_mask( *this, rhs, _CMP_GT_OS ); + } + + FS_INLINE __mmask16 operator<( const AVX512_f32x16& rhs ) + { + return _mm512_cmp_ps_mask( *this, rhs, _CMP_LT_OS ); + } + + FS_INLINE __mmask16 operator>=( const AVX512_f32x16& rhs ) + { + return _mm512_cmp_ps_mask( *this, rhs, _CMP_GE_OS ); + } + + FS_INLINE __mmask16 operator<=( const AVX512_f32x16& rhs ) + { + return _mm512_cmp_ps_mask( *this, rhs, _CMP_LE_OS ); + } + }; + + FASTSIMD_INTERNAL_OPERATORS_FLOAT( AVX512_f32x16 ) + + + struct AVX512_i32x16 + { + FASTSIMD_INTERNAL_TYPE_SET( AVX512_i32x16, __m512i ); + + FS_INLINE static AVX512_i32x16 Incremented() + { + return _mm512_set_epi32( 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ); + } + + FS_INLINE explicit AVX512_i32x16( int32_t i ) + { + *this = _mm512_set1_epi32( i ); + } + + FS_INLINE explicit AVX512_i32x16( int32_t i0, int32_t i1, int32_t i2, int32_t i3, int32_t i4, int32_t i5, int32_t i6, int32_t i7, int32_t i8, int32_t i9, int32_t i10, int32_t i11, int32_t i12, int32_t i13, int32_t i14, int32_t i15 ) + { + *this = _mm512_set_epi32( i15, i14, i13, i12, i11, i10, i9, i8, i7, i6, i5, i4, i3, i2, i1, i0 ); + } + + FS_INLINE AVX512_i32x16& operator+=( const AVX512_i32x16& rhs ) + { + *this = _mm512_add_epi32( *this, rhs ); + return *this; + } + + FS_INLINE AVX512_i32x16& operator-=( const AVX512_i32x16& rhs ) + { + *this = _mm512_sub_epi32( *this, rhs ); + return *this; + } + + FS_INLINE AVX512_i32x16& operator*=( const AVX512_i32x16& rhs ) + { + *this = _mm512_mullo_epi32( *this, rhs ); + return *this; + } + + FS_INLINE AVX512_i32x16& operator&=( const AVX512_i32x16& rhs ) + { + *this = _mm512_and_si512( *this, rhs ); + return *this; + } + + FS_INLINE AVX512_i32x16& operator|=( const AVX512_i32x16& rhs ) + { + *this = _mm512_or_si512( *this, rhs ); + return *this; + } + + FS_INLINE AVX512_i32x16& operator^=( const AVX512_i32x16& rhs ) + { + *this = _mm512_xor_si512( *this, rhs ); + return *this; + } + + FS_INLINE AVX512_i32x16& operator>>=( int32_t rhs ) + { + *this = _mm512_srai_epi32( *this, rhs ); + return *this; + } + + FS_INLINE AVX512_i32x16& operator<<=( int32_t rhs ) + { + *this = _mm512_slli_epi32( *this, rhs ); + return *this; + } + + FS_INLINE AVX512_i32x16 operator~() const + { +#if FASTSIMD_CONFIG_GENERATE_CONSTANTS + const __m512i neg1 = _mm512_cmpeq_epi32( _mm512_setzero_si512(), _mm512_setzero_si512() ); +#else + const __m512i neg1 = _mm512_set1_epi32( -1 ); +#endif + return _mm512_xor_si512( *this, neg1 ); + } + + FS_INLINE AVX512_i32x16 operator-() const + { + return _mm512_sub_epi32( _mm512_setzero_si512(), *this ); + } + + FS_INLINE __mmask16 operator==( const AVX512_i32x16& rhs ) + { + return _mm512_cmpeq_epi32_mask( *this, rhs ); + } + + FS_INLINE __mmask16 operator>( const AVX512_i32x16& rhs ) + { + return _mm512_cmpgt_epi32_mask( *this, rhs ); + } + + FS_INLINE __mmask16 operator<( const AVX512_i32x16& rhs ) + { + return _mm512_cmplt_epi32_mask( *this, rhs ); + } + }; + + FASTSIMD_INTERNAL_OPERATORS_INT( AVX512_i32x16, int32_t ) + + template + class AVX512_T + { + public: + static_assert( LEVEL_T == Level_AVX512, "Cannot create template with unsupported SIMD level" ); + + static constexpr eLevel SIMD_Level = LEVEL_T; + + template + static constexpr size_t VectorSize = 512 / ElementSize; + + typedef AVX512_f32x16 float32v; + typedef AVX512_i32x16 int32v; + typedef __mmask16 mask32v; + + // Load + + FS_INLINE static float32v Load_f32( void const* p ) + { + return _mm512_loadu_ps( p ); + } + + FS_INLINE static int32v Load_i32( void const* p ) + { + return _mm512_loadu_si512( p ); + } + + // Store + + FS_INLINE static void Store_f32( void* p, float32v a ) + { + _mm512_storeu_ps( p, a ); + } + + FS_INLINE static void Store_i32( void* p, int32v a ) + { + _mm512_storeu_si512( p, a ); + } + + // Cast + + FS_INLINE static float32v Casti32_f32( int32v a ) + { + return _mm512_castsi512_ps( a ); + } + + FS_INLINE static int32v Castf32_i32( float32v a ) + { + return _mm512_castps_si512( a ); + } + + // Convert + + FS_INLINE static float32v Converti32_f32( int32v a ) + { + return _mm512_cvtepi32_ps( a ); + } + + FS_INLINE static int32v Convertf32_i32( float32v a ) + { + return _mm512_cvtps_epi32( a ); + } + + // Select + + FS_INLINE static float32v Select_f32( mask32v m, float32v a, float32v b ) + { + return _mm512_mask_blend_ps( m, b, a ); + } + + FS_INLINE static int32v Select_i32( mask32v m, int32v a, int32v b ) + { + return _mm512_mask_blend_epi32( m, b, a ); + } + + // Min, Max + + FS_INLINE static float32v Min_f32( float32v a, float32v b ) + { + return _mm512_min_ps( a, b ); + } + + FS_INLINE static float32v Max_f32( float32v a, float32v b ) + { + return _mm512_max_ps( a, b ); + } + + FS_INLINE static int32v Min_i32( int32v a, int32v b ) + { + return _mm512_min_epi32( a, b ); + } + + FS_INLINE static int32v Max_i32( int32v a, int32v b ) + { + return _mm512_max_epi32( a, b ); + } + + // Bitwise + + FS_INLINE static float32v BitwiseAndNot_f32( float32v a, float32v b ) + { + return _mm512_andnot_ps( b, a ); + } + + FS_INLINE static int32v BitwiseAndNot_i32( int32v a, int32v b ) + { + return _mm512_andnot_si512( b, a ); + } + + FS_INLINE static float32v BitwiseShiftRightZX_f32( float32v a, int32_t b ) + { + return Casti32_f32( _mm512_srli_epi32( Castf32_i32( a ), b ) ); + } + + FS_INLINE static int32v BitwiseShiftRightZX_i32( int32v a, int32_t b ) + { + return _mm512_srli_epi32( a, b ); + } + + // Abs + + FS_INLINE static float32v Abs_f32( float32v a ) + { + return _mm512_abs_ps( a ); + } + + FS_INLINE static int32v Abs_i32( int32v a ) + { + return _mm512_abs_epi32( a ); + } + + // Float math + + FS_INLINE static float32v Sqrt_f32( float32v a ) + { + return _mm512_sqrt_ps( a ); + } + + FS_INLINE static float32v InvSqrt_f32( float32v a ) + { + return _mm512_rsqrt14_ps( a ); + } + + FS_INLINE static float32v Reciprocal_f32( float32v a ) + { + return _mm512_rcp14_ps( a ); + } + + // Floor, Ceil, Round + + FS_INLINE static float32v Floor_f32( float32v a ) + { + return _mm512_roundscale_ps( a, _MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC ); + } + + FS_INLINE static float32v Ceil_f32( float32v a ) + { + return _mm512_roundscale_ps( a, _MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC ); + } + + FS_INLINE static float32v Round_f32( float32v a ) + { + return _mm512_roundscale_ps( a, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC ); + } + + //Mask + + FS_INLINE static int32v Mask_i32( int32v a, mask32v m ) + { + return _mm512_maskz_mov_epi32( m, a ); + } + + FS_INLINE static float32v Mask_f32( float32v a, mask32v m ) + { + return _mm512_maskz_mov_ps( m, a ); + } + + FS_INLINE static int32v NMask_i32( int32v a, mask32v m ) + { + return _mm512_maskz_mov_epi32( ~m, a ); + } + + FS_INLINE static float32v NMask_f32( float32v a, mask32v m ) + { + return _mm512_maskz_mov_ps( ~m, a ); + } + + FS_INLINE static bool AnyMask_bool( mask32v m ) + { + return m; + } + }; + +#if FASTSIMD_COMPILE_AVX512 + typedef AVX512_T AVX512; + +#if FASTSIMD_USE_FMA + template<> + FS_INLINE AVX512::float32v FMulAdd_f32( AVX512::float32v a, AVX512::float32v b, AVX512::float32v c ) + { + return _mm512_fmadd_ps( a, b, c ); + } + + template<> + FS_INLINE AVX512::float32v FNMulAdd_f32( AVX512::float32v a, AVX512::float32v b, AVX512::float32v c ) + { + return _mm512_fnmadd_ps( a, b, c ); + } +#endif + + // Masked float + + template<> + FS_INLINE AVX512::float32v MaskedAdd_f32( AVX512::float32v a, AVX512::float32v b, AVX512::mask32v m ) + { + return _mm512_mask_add_ps( a, m, a, b ); + } + + template<> + FS_INLINE AVX512::float32v MaskedSub_f32( AVX512::float32v a, AVX512::float32v b, AVX512::mask32v m ) + { + return _mm512_mask_sub_ps( a, m, a, b ); + } + + template<> + FS_INLINE AVX512::float32v MaskedMul_f32( AVX512::float32v a, AVX512::float32v b, AVX512::mask32v m ) + { + return _mm512_mask_mul_ps( a, m, a, b ); + } + + // Masked int32 + + template<> + FS_INLINE AVX512::int32v MaskedAdd_i32( AVX512::int32v a, AVX512::int32v b, AVX512::mask32v m ) + { + return _mm512_mask_add_epi32( a, m, a, b ); + } + + template<> + FS_INLINE AVX512::int32v MaskedSub_i32( AVX512::int32v a, AVX512::int32v b, AVX512::mask32v m ) + { + return _mm512_mask_sub_epi32( a, m, a, b ); + } + + template<> + FS_INLINE AVX512::int32v MaskedMul_i32( AVX512::int32v a, AVX512::int32v b, AVX512::mask32v m ) + { + return _mm512_mask_mullo_epi32( a, m, a, b ); + } + + // NMasked float + + template<> + FS_INLINE AVX512::float32v NMaskedAdd_f32( AVX512::float32v a, AVX512::float32v b, AVX512::mask32v m ) + { + return _mm512_mask_add_ps( a, ~m, a, b ); + } + + template<> + FS_INLINE AVX512::float32v NMaskedSub_f32( AVX512::float32v a, AVX512::float32v b, AVX512::mask32v m ) + { + return _mm512_mask_sub_ps( a, ~m, a, b ); + } + + template<> + FS_INLINE AVX512::float32v NMaskedMul_f32( AVX512::float32v a, AVX512::float32v b, AVX512::mask32v m ) + { + return _mm512_mask_mul_ps( a, ~m, a, b ); + } + + // NMasked int32 + + template<> + FS_INLINE AVX512::int32v NMaskedAdd_i32( AVX512::int32v a, AVX512::int32v b, AVX512::mask32v m ) + { + return _mm512_mask_add_epi32( a, ~m, a, b ); + } + + template<> + FS_INLINE AVX512::int32v NMaskedSub_i32( AVX512::int32v a, AVX512::int32v b, AVX512::mask32v m ) + { + return _mm512_mask_sub_epi32( a, ~m, a, b ); + } + + template<> + FS_INLINE AVX512::int32v NMaskedMul_i32( AVX512::int32v a, AVX512::int32v b, AVX512::mask32v m ) + { + return _mm512_mask_mul_epi32( a, ~m, a, b ); + } +#endif + +} diff --git a/deps/FastNoise2/src/FastSIMD/Internal/NEON.h b/deps/FastNoise2/src/FastSIMD/Internal/NEON.h new file mode 100644 index 0000000..344f9fa --- /dev/null +++ b/deps/FastNoise2/src/FastSIMD/Internal/NEON.h @@ -0,0 +1,424 @@ +#pragma once + +#include + +#include "VecTools.h" + +struct NEON_f32x4 +{ + FASTSIMD_INTERNAL_TYPE_SET( NEON_f32x4, float32x4_t ); + + constexpr FS_INLINE static uint8_t Size() + { + return 4; + } + + FS_INLINE static NEON_f32x4 Zero() + { + return vdupq_n_f32( 0 ); + } + + FS_INLINE static NEON_f32x4 Incremented() + { + alignas(16) const float f[4]{ 0.0f, 1.0f, 2.0f, 3.0f }; + return vld1q_f32( f ); + } + + FS_INLINE explicit NEON_f32x4( float f ) + { + *this = vdupq_n_f32( f ); + } + + FS_INLINE explicit NEON_f32x4( float f0, float f1, float f2, float f3 ) + { + alignas(16) const float f[4]{ f0, f1, f2, f3 }; + *this = vld1q_f32( f ); + } + + FS_INLINE NEON_f32x4& operator+=( const NEON_f32x4& rhs ) + { + *this = vaddq_f32( *this, rhs ); + return *this; + } + + FS_INLINE NEON_f32x4& operator-=( const NEON_f32x4& rhs ) + { + *this = vsubq_f32( *this, rhs ); + return *this; + } + + FS_INLINE NEON_f32x4& operator*=( const NEON_f32x4& rhs ) + { + *this = vmulq_f32( *this, rhs ); + return *this; + } + + FS_INLINE NEON_f32x4& operator/=( const NEON_f32x4& rhs ) + { + float32x4_t reciprocal = vrecpeq_f32( rhs ); + // use a couple Newton-Raphson steps to refine the estimate. Depending on your + // application's accuracy requirements, you may be able to get away with only + // one refinement (instead of the two used here). Be sure to test! + reciprocal = vmulq_f32( vrecpsq_f32( rhs, reciprocal ), reciprocal ); + reciprocal = vmulq_f32( vrecpsq_f32( rhs, reciprocal ), reciprocal ); + + // and finally, compute a/b = a*(1/b) + *this = vmulq_f32( *this, reciprocal ); + return *this; + } + + FS_INLINE NEON_f32x4 operator-() const + { + return vnegq_f32( *this ); + } +}; + +FASTSIMD_INTERNAL_OPERATORS_FLOAT( NEON_f32x4 ) + + +struct NEON_i32x4 +{ + FASTSIMD_INTERNAL_TYPE_SET( NEON_i32x4, int32x4_t ); + + constexpr FS_INLINE static uint8_t Size() + { + return 4; + } + + FS_INLINE static NEON_i32x4 Zero() + { + return vdupq_n_s32( 0 ); + } + + FS_INLINE static NEON_i32x4 Incremented() + { + alignas(16) const int32_t f[4]{ 0, 1, 2, 3 }; + return vld1q_s32( f ); + } + + FS_INLINE explicit NEON_i32x4( int32_t i ) + { + *this = vdupq_n_s32( i ); + } + + FS_INLINE explicit NEON_i32x4( int32_t i0, int32_t i1, int32_t i2, int32_t i3 ) + { + alignas(16) const int32_t f[4]{ i0, i1, i2, i3 }; + *this = vld1q_s32( f ); + } + + FS_INLINE NEON_i32x4& operator+=( const NEON_i32x4& rhs ) + { + *this = vaddq_s32( *this, rhs ); + return *this; + } + + FS_INLINE NEON_i32x4& operator-=( const NEON_i32x4& rhs ) + { + *this = vsubq_s32( *this, rhs ); + return *this; + } + + FS_INLINE NEON_i32x4& operator*=( const NEON_i32x4& rhs ) + { + *this = vmulq_s32( *this, rhs ); + return *this; + } + + FS_INLINE NEON_i32x4& operator&=( const NEON_i32x4& rhs ) + { + *this = vandq_s32( *this, rhs ); + return *this; + } + + FS_INLINE NEON_i32x4& operator|=( const NEON_i32x4& rhs ) + { + *this = vorrq_s32( *this, rhs ); + return *this; + } + + FS_INLINE NEON_i32x4& operator^=( const NEON_i32x4& rhs ) + { + *this = veorq_s32( *this, rhs ); + return *this; + } + + FS_INLINE NEON_i32x4& operator>>=( const int32_t rhs ) + { + *this = vshrq_n_s32( *this, rhs ); + return *this; + } + + FS_INLINE NEON_i32x4& operator<<=( const int32_t rhs ) + { + *this = vshlq_n_s32( *this, rhs ); + return *this; + } + + FS_INLINE NEON_i32x4 operator~() const + { + return vmvnq_s32( *this ); + } + + FS_INLINE NEON_i32x4 operator-() const + { + return vnegq_s32( *this ); + } +}; + +FASTSIMD_INTERNAL_OPERATORS_INT( NEON_i32x4, int32_t ) + +template +class FastSIMD_NEON_T +{ +public: + static const FastSIMD::eLevel SIMD_Level = LEVEL_T; + static const size_t VectorSize = 128 / 8; + + typedef NEON_f32x4 float32v; + typedef NEON_i32x4 int32v; + typedef NEON_i32x4 mask32v; + + // Load + + FS_INLINE static float32v Load_f32( void const* p ) + { + return vld1q_f32( reinterpret_cast(p) ); + } + + FS_INLINE static int32v Load_i32( void const* p ) + { + return vld1q_s32( reinterpret_cast(p) ); + } + + // Store + + FS_INLINE static void Store_f32( void* p, float32v a ) + { + vst1q_f32( reinterpret_cast(p), a ); + } + + FS_INLINE static void Store_i32( void* p, int32v a ) + { + vst1q_s32( reinterpret_cast(p), a ); + } + + // Cast + + FS_INLINE static float32v Casti32_f32( int32v a ) + { + return vreinterpretq_f32_s32( a ); + } + + FS_INLINE static int32v Castf32_i32( float32v a ) + { + return vreinterpretq_s32_f32( a ); + } + + // Convert + + FS_INLINE static float32v Converti32_f32( int32v a ) + { + return vcvtq_f32_s32( a ); + } + + FS_INLINE static int32v Convertf32_i32( float32v a ) + { + return vcvtq_s32_f32( a ); + } + + // Comparisons + + FS_INLINE static mask32v Equal_f32( float32v a, float32v b ) + { + return vreinterpretq_s32_u32( vceq_f32( a, b ) ); + } + + FS_INLINE static mask32v GreaterThan_f32( float32v a, float32v b ) + { + return vreinterpretq_s32_u32( vcgtq_f32( a, b ) ); + } + + FS_INLINE static mask32v LessThan_f32( float32v a, float32v b ) + { + return vreinterpretq_s32_u32( vcltq_f32( a, b ) ); + } + + FS_INLINE static mask32v GreaterEqualThan_f32( float32v a, float32v b ) + { + return vreinterpretq_s32_u32( vcgeq_f32( a, b ) ); + } + + FS_INLINE static mask32v LessEqualThan_f32( float32v a, float32v b ) + { + return vreinterpretq_s32_u32( vcleq_f32( a, b ) ); + } + + FS_INLINE static mask32v Equal_i32( int32v a, int32v b ) + { + return vceq_s32( a, b ); + } + + FS_INLINE static mask32v GreaterThan_i32( int32v a, int32v b ) + { + return vcgtq_s32( a, b ); + } + + FS_INLINE static mask32v LessThan_i32( int32v a, int32v b ) + { + return vcltq_s32( a, b ); + } + + // Select + + FS_INLINE static float32v Select_f32( mask32v m, float32v a, float32v b ) + { + return vbslq_f32( vreinterpretq_u32_s32( mask ), b, a ); + } + + FS_INLINE static int32v Select_i32( mask32v m, int32v a, int32v b ) + { + return vbslq_s32( vreinterpretq_u32_s32( mask ), b, a ); + } + + // Min, Max + + FS_INLINE static float32v Min_f32( float32v a, float32v b ) + { + return vminq_f32( a, b ); + } + + FS_INLINE static float32v Max_f32( float32v a, float32v b ) + { + return vmaxq_f32( a, b ); + } + + FS_INLINE static int32v Min_i32( int32v a, int32v b ) + { + return vminq_s32( a, b ); + } + + FS_INLINE static int32v Max_i32( int32v a, int32v b ) + { + return vmaxq_s32( a, b ); + } + + // Bitwise + + FS_INLINE static float32v BitwiseAnd_f32( float32v a, float32v b ) + { + return vreinterpretq_f32_s32( vandq_s32( vreinterpretq_s32_f32( a ), vreinterpretq_s32_f32( b ) ) ); + } + + FS_INLINE static float32v BitwiseOr_f32( float32v a, float32v b ) + { + return vreinterpretq_f32_s32( vorrq_s32( vreinterpretq_s32_f32( a ), vreinterpretq_s32_f32( b ) ) ); + } + + FS_INLINE static float32v BitwiseXor_f32( float32v a, float32v b ) + { + return vreinterpretq_f32_s32( veorq_s32( vreinterpretq_s32_f32( a ), vreinterpretq_s32_f32( b ) ) ); + } + + FS_INLINE static float32v BitwiseNot_f32( float32v a ) + { + return vreinterpretq_f32_s32( vmvn_s32( vreinterpretq_s32_f32( a ), vreinterpretq_s32_f32( b ) ) ); + } + + FS_INLINE static float32v BitwiseAndNot_f32( float32v a, float32v b ) + { + return vreinterpretq_f32_s32( vandq_s32( vreinterpretq_s32_f32( a ), vmvn_s32( vreinterpretq_s32_f32( b ) ) ) ); + } + + FS_INLINE static int32v BitwiseAndNot_i32( int32v a, int32v b ) + { + return vandq_s32( a , vmvn_s32( b ) ); + } + + // Abs + + FS_INLINE static float32v Abs_f32( float32v a ) + { + return vabsq_f32( a ); + } + + FS_INLINE static int32v Abs_i32( int32v a ) + { + return vabsq_s32( a ); + } + + // Float math + + FS_INLINE static float32v Sqrt_f32( float32v a ) + { + return vsqrtq_f32( a ); + } + + FS_INLINE static float32v InvSqrt_f32( float32v a ) + { + return vrsqrteq_f32( a ); + } + + // Floor, Ceil, Round: http://dss.stephanierct.com/DevBlog/?p=8 + + FS_INLINE static float32v Floor_f32( float32v a ) + { +#if FASTSIMD_CONFIG_GENERATE_CONSTANTS + const float32x4_t f1 = vdupq_n_f32( 1.0f ); //_mm_castsi128_ps( _mm_slli_epi32( _mm_srli_epi32( _mm_cmpeq_epi32( _mm_setzero_si128(), _mm_setzero_si128() ), 25 ), 23 ) ); +#else + const float32x4_t f1 = vdupq_n_f32( 1.0f ); +#endif + float32x4_t fval = vrndmq_f32( a ); + + return vsubq_f32( fval, BitwiseAnd_f32( vcltq_f32( a, fval ), f1 ) ); + } + + FS_INLINE static float32v Ceil_f32( float32v a ) + { +#if FASTSIMD_CONFIG_GENERATE_CONSTANTS + const __m128 f1 = vdupq_n_f32( 1.0f ); //_mm_castsi128_ps( _mm_slli_epi32( _mm_srli_epi32( _mm_cmpeq_epi32( _mm_setzero_si128(), _mm_setzero_si128() ), 25 ), 23 ) ); +#else + const __m128 f1 = vdupq_n_f32( 1.0f ); +#endif + float32x4_t fval = vrndmq_f32( a ); + + return vaddq_f32( fval, BitwiseAnd_f32( vcltq_f32( a, fval ), f1 ) ); + } + + template + FS_INLINE static FS_ENABLE_IF( L < FastSIMD::ELevel_SSE41, float32v ) Round_f32( float32v a ) + { +#if FASTSIMD_CONFIG_GENERATE_CONSTANTS + const __m128 nearest2 = _mm_castsi128_ps( _mm_srli_epi32( _mm_cmpeq_epi32( _mm_setzero_si128(), _mm_setzero_si128() ), 2 ) ); +#else + const __m128 nearest2 = vdupq_n_f32( 1.99999988079071044921875f ); +#endif + __m128 aTrunc = _mm_cvtepi32_ps( _mm_cvttps_epi32( a ) ); // truncate a + __m128 rmd = _mm_sub_ps( a, aTrunc ); // get remainder + __m128 rmd2 = _mm_mul_ps( rmd, nearest2 ); // mul remainder by near 2 will yield the needed offset + __m128 rmd2Trunc = _mm_cvtepi32_ps( _mm_cvttps_epi32( rmd2 ) ); // after being truncated of course + return _mm_add_ps( aTrunc, rmd2Trunc ); + } + + template + FS_INLINE static FS_ENABLE_IF( L >= FastSIMD::ELevel_SSE41, float32v ) Round_f32( float32v a ) + { + return vrndnq_f32( a ); + } + + // Mask + + FS_INLINE static int32v Mask_i32( int32v a, mask32v m ) + { + return a & m; + } + + FS_INLINE static float32v Mask_f32( float32v a, mask32v m ) + { + return BitwiseAnd_f32( a, vreinterpretq_f32_s32( m ) ); + } +}; + +#if FASTSIMD_COMPILE_NEON +typedef FastSIMD_SSE_T FastSIMD_NEON; +#endif diff --git a/deps/FastNoise2/src/FastSIMD/Internal/SSE.h b/deps/FastNoise2/src/FastSIMD/Internal/SSE.h new file mode 100644 index 0000000..c176e40 --- /dev/null +++ b/deps/FastNoise2/src/FastSIMD/Internal/SSE.h @@ -0,0 +1,541 @@ +#pragma once + +#ifdef __GNUG__ +#include +#else +#include +#endif + +#include "VecTools.h" + +namespace FastSIMD +{ + struct SSE_f32x4 + { + FASTSIMD_INTERNAL_TYPE_SET( SSE_f32x4, __m128 ); + + FS_INLINE static SSE_f32x4 Incremented() + { + return _mm_set_ps( 3.0f, 2.0f, 1.0f, 0.0f ); + } + + FS_INLINE explicit SSE_f32x4( float f ) + { + *this = _mm_set1_ps( f ); + } + + FS_INLINE explicit SSE_f32x4( float f0, float f1, float f2, float f3 ) + { + *this = _mm_set_ps( f3, f2, f1, f0 ); + } + + FS_INLINE SSE_f32x4& operator+=( const SSE_f32x4& rhs ) + { + *this = _mm_add_ps( *this, rhs ); + return *this; + } + + FS_INLINE SSE_f32x4& operator-=( const SSE_f32x4& rhs ) + { + *this = _mm_sub_ps( *this, rhs ); + return *this; + } + + FS_INLINE SSE_f32x4& operator*=( const SSE_f32x4& rhs ) + { + *this = _mm_mul_ps( *this, rhs ); + return *this; + } + + FS_INLINE SSE_f32x4& operator/=( const SSE_f32x4& rhs ) + { + *this = _mm_div_ps( *this, rhs ); + return *this; + } + + FS_INLINE SSE_f32x4& operator&=( const SSE_f32x4& rhs ) + { + *this = _mm_and_ps( *this, rhs ); + return *this; + } + + FS_INLINE SSE_f32x4& operator|=( const SSE_f32x4& rhs ) + { + *this = _mm_or_ps( *this, rhs ); + return *this; + } + + FS_INLINE SSE_f32x4& operator^=( const SSE_f32x4& rhs ) + { + *this = _mm_xor_ps( *this, rhs ); + return *this; + } + + FS_INLINE SSE_f32x4 operator~() const + { +#if FASTSIMD_CONFIG_GENERATE_CONSTANTS + const __m128i neg1 = _mm_cmpeq_epi32( _mm_setzero_si128(), _mm_setzero_si128() ); +#else + const __m128i neg1 = _mm_set1_epi32( -1 ); +#endif + return _mm_xor_ps( *this, _mm_castsi128_ps( neg1 ) ); + } + + FS_INLINE SSE_f32x4 operator-() const + { +#if FASTSIMD_CONFIG_GENERATE_CONSTANTS + const __m128i minInt = _mm_slli_epi32( _mm_cmpeq_epi32( _mm_undefined_si128(), _mm_setzero_si128() ), 31 ); +#else + const __m128i minInt = _mm_set1_epi32( 0x80000000 ); +#endif + return _mm_xor_ps( *this, _mm_castsi128_ps( minInt ) ); + } + + FS_INLINE __m128i operator==( const SSE_f32x4& rhs ) + { + return _mm_castps_si128( _mm_cmpeq_ps( *this, rhs ) ); + } + + FS_INLINE __m128i operator!=( const SSE_f32x4& rhs ) + { + return _mm_castps_si128( _mm_cmpneq_ps( *this, rhs ) ); + } + + FS_INLINE __m128i operator>( const SSE_f32x4& rhs ) + { + return _mm_castps_si128( _mm_cmpgt_ps( *this, rhs ) ); + } + + FS_INLINE __m128i operator<( const SSE_f32x4& rhs ) + { + return _mm_castps_si128( _mm_cmplt_ps( *this, rhs ) ); + } + + FS_INLINE __m128i operator>=( const SSE_f32x4& rhs ) + { + return _mm_castps_si128( _mm_cmpge_ps( *this, rhs ) ); + } + + FS_INLINE __m128i operator<=( const SSE_f32x4& rhs ) + { + return _mm_castps_si128( _mm_cmple_ps( *this, rhs ) ); + } + }; + + FASTSIMD_INTERNAL_OPERATORS_FLOAT( SSE_f32x4 ) + + + template + struct SSE_i32x4 + { + FASTSIMD_INTERNAL_TYPE_SET( SSE_i32x4, __m128i ); + + FS_INLINE static SSE_i32x4 Incremented() + { + return _mm_set_epi32( 3, 2, 1, 0 ); + } + + FS_INLINE explicit SSE_i32x4( int32_t i ) + { + *this = _mm_set1_epi32( i ); + } + + FS_INLINE explicit SSE_i32x4( int32_t i0, int32_t i1, int32_t i2, int32_t i3 ) + { + *this = _mm_set_epi32( i3, i2, i1, i0 ); + } + + FS_INLINE SSE_i32x4& operator+=( const SSE_i32x4& rhs ) + { + *this = _mm_add_epi32( *this, rhs ); + return *this; + } + + FS_INLINE SSE_i32x4& operator-=( const SSE_i32x4& rhs ) + { + *this = _mm_sub_epi32( *this, rhs ); + return *this; + } + + template* = nullptr> + FS_INLINE SSE_i32x4& operator*=( const SSE_i32x4& rhs ) + { + __m128i tmp1 = _mm_mul_epu32( *this, rhs ); /* mul 2,0*/ + __m128i tmp2 = _mm_mul_epu32( _mm_srli_si128( *this, 4 ), _mm_srli_si128( rhs, 4 ) ); /* mul 3,1 */ + *this = _mm_unpacklo_epi32( _mm_shuffle_epi32( tmp1, _MM_SHUFFLE( 0, 0, 2, 0 ) ), _mm_shuffle_epi32( tmp2, _MM_SHUFFLE( 0, 0, 2, 0 ) ) ); /* shuffle results to [63..0] and pack */ + return *this; + } + + template= Level_SSE41)>* = nullptr> + FS_INLINE SSE_i32x4& operator*=( const SSE_i32x4& rhs ) + { + *this = _mm_mullo_epi32( *this, rhs ); + return *this; + } + + FS_INLINE SSE_i32x4& operator&=( const SSE_i32x4& rhs ) + { + *this = _mm_and_si128( *this, rhs ); + return *this; + } + + FS_INLINE SSE_i32x4& operator|=( const SSE_i32x4& rhs ) + { + *this = _mm_or_si128( *this, rhs ); + return *this; + } + + FS_INLINE SSE_i32x4& operator^=( const SSE_i32x4& rhs ) + { + *this = _mm_xor_si128( *this, rhs ); + return *this; + } + + FS_INLINE SSE_i32x4& operator>>=( int32_t rhs ) + { + *this = _mm_srai_epi32( *this, rhs ); + return *this; + } + + FS_INLINE SSE_i32x4& operator<<=( int32_t rhs ) + { + *this = _mm_slli_epi32( *this, rhs ); + return *this; + } + + FS_INLINE SSE_i32x4 operator~() const + { +#if FASTSIMD_CONFIG_GENERATE_CONSTANTS + const __m128i neg1 = _mm_cmpeq_epi32( _mm_setzero_si128(), _mm_setzero_si128() ); +#else + const __m128i neg1 = _mm_set1_epi32( -1 ); +#endif + return _mm_xor_si128( *this, neg1 ); + } + + FS_INLINE SSE_i32x4 operator-() const + { + return _mm_sub_epi32( _mm_setzero_si128(), *this ); + } + + FS_INLINE SSE_i32x4 operator==( const SSE_i32x4& rhs ) + { + return _mm_cmpeq_epi32( *this, rhs ); + } + + FS_INLINE SSE_i32x4 operator>( const SSE_i32x4& rhs ) + { + return _mm_cmpgt_epi32( *this, rhs ); + } + + FS_INLINE SSE_i32x4 operator<( const SSE_i32x4& rhs ) + { + return _mm_cmplt_epi32( *this, rhs ); + } + }; + + FASTSIMD_INTERNAL_OPERATORS_INT_TEMPLATED( SSE_i32x4, int32_t ) + + template + class SSE_T + { + public: + static_assert( LEVEL_T >= Level_SSE && LEVEL_T <= Level_SSE42, "Cannot create template with unsupported SIMD level" ); + + static constexpr eLevel SIMD_Level = LEVEL_T; + + template + static constexpr size_t VectorSize = 128 / ElementSize; + + typedef SSE_f32x4 float32v; + typedef SSE_i32x4 int32v; + typedef SSE_i32x4 mask32v; + + // Load + + FS_INLINE static float32v Load_f32( void const* p ) + { + return _mm_loadu_ps( reinterpret_cast(p) ); + } + + FS_INLINE static int32v Load_i32( void const* p ) + { + return _mm_loadu_si128( reinterpret_cast<__m128i const*>(p) ); + } + + // Store + + FS_INLINE static void Store_f32( void* p, float32v a ) + { + _mm_storeu_ps( reinterpret_cast(p), a ); + } + + FS_INLINE static void Store_i32( void* p, int32v a ) + { + _mm_storeu_si128( reinterpret_cast<__m128i*>(p), a ); + } + + // Cast + + FS_INLINE static float32v Casti32_f32( int32v a ) + { + return _mm_castsi128_ps( a ); + } + + FS_INLINE static int32v Castf32_i32( float32v a ) + { + return _mm_castps_si128( a ); + } + + // Convert + + FS_INLINE static float32v Converti32_f32( int32v a ) + { + return _mm_cvtepi32_ps( a ); + } + + FS_INLINE static int32v Convertf32_i32( float32v a ) + { + return _mm_cvtps_epi32( a ); + } + + // Select + + template* = nullptr> + FS_INLINE static float32v Select_f32( mask32v m, float32v a, float32v b ) + { + __m128 mf = _mm_castsi128_ps( m ); + + return _mm_xor_ps( b, _mm_and_ps( mf, _mm_xor_ps( a, b ) ) ); + } + + template= Level_SSE41)>* = nullptr> + FS_INLINE static float32v Select_f32( mask32v m, float32v a, float32v b ) + { + return _mm_blendv_ps( b, a, _mm_castsi128_ps( m ) ); + } + + template* = nullptr> + FS_INLINE static int32v Select_i32( mask32v m, int32v a, int32v b ) + { + return _mm_xor_si128( b, _mm_and_si128( m, _mm_xor_si128( a, b ) ) ); + } + + template= Level_SSE41)>* = nullptr> + FS_INLINE static int32v Select_i32( mask32v m, int32v a, int32v b ) + { + return _mm_castps_si128( _mm_blendv_ps( _mm_castsi128_ps( b ), _mm_castsi128_ps( a ), _mm_castsi128_ps( m ) ) ); + } + + // Min, Max + + FS_INLINE static float32v Min_f32( float32v a, float32v b ) + { + return _mm_min_ps( a, b ); + } + + FS_INLINE static float32v Max_f32( float32v a, float32v b ) + { + return _mm_max_ps( a, b ); + } + + template* = nullptr> + FS_INLINE static int32v Min_i32( int32v a, int32v b ) + { + return Select_i32( a < b, a, b ); + } + + template= Level_SSE41)>* = nullptr> + FS_INLINE static int32v Min_i32( int32v a, int32v b ) + { + return _mm_min_epi32( a, b ); + } + + template* = nullptr> + FS_INLINE static int32v Max_i32( int32v a, int32v b ) + { + return Select_i32( a > b, a, b ); + } + + template= Level_SSE41)>* = nullptr> + FS_INLINE static int32v Max_i32( int32v a, int32v b ) + { + return _mm_max_epi32( a, b ); + } + + // Bitwise + + FS_INLINE static float32v BitwiseAndNot_f32( float32v a, float32v b ) + { + return _mm_andnot_ps( b, a ); + } + + FS_INLINE static int32v BitwiseAndNot_i32( int32v a, int32v b ) + { + return _mm_andnot_si128( b, a ); + } + + FS_INLINE static float32v BitwiseShiftRightZX_f32( float32v a, int32_t b ) + { + return Casti32_f32( _mm_srli_epi32( Castf32_i32( a ), b ) ); + } + + FS_INLINE static int32v BitwiseShiftRightZX_i32( int32v a, int32_t b ) + { + return _mm_srli_epi32( a, b ); + } + + // Abs + + FS_INLINE static float32v Abs_f32( float32v a ) + { +#if FASTSIMD_CONFIG_GENERATE_CONSTANTS + const __m128i intMax = _mm_srli_epi32( _mm_cmpeq_epi32( _mm_setzero_si128(), _mm_setzero_si128() ), 1 ); +#else + const __m128i intMax = _mm_set1_epi32( 0x7FFFFFFF ); +#endif + return _mm_and_ps( a, _mm_castsi128_ps( intMax ) ); + } + + template* = nullptr> + FS_INLINE static int32v Abs_i32( int32v a ) + { + __m128i signMask = _mm_srai_epi32( a, 31 ); + return _mm_sub_epi32( _mm_xor_si128( a, signMask ), signMask ); + } + + template= Level_SSSE3)>* = nullptr> + FS_INLINE static int32v Abs_i32( int32v a ) + { + return _mm_abs_epi32( a ); + } + + // Float math + + FS_INLINE static float32v Sqrt_f32( float32v a ) + { + return _mm_sqrt_ps( a ); + } + + FS_INLINE static float32v InvSqrt_f32( float32v a ) + { + return _mm_rsqrt_ps( a ); + } + + FS_INLINE static float32v Reciprocal_f32( float32v a ) + { + return _mm_rcp_ps( a ); + } + + // Floor, Ceil, Round: http://dss.stephanierct.com/DevBlog/?p=8 + + template* = nullptr> + FS_INLINE static float32v Floor_f32( float32v a ) + { +#if FASTSIMD_CONFIG_GENERATE_CONSTANTS + const __m128 f1 = _mm_castsi128_ps( _mm_slli_epi32( _mm_srli_epi32( _mm_cmpeq_epi32( _mm_setzero_si128(), _mm_setzero_si128() ), 25 ), 23 ) ); +#else + const __m128 f1 = _mm_set1_ps( 1.0f ); +#endif + __m128 fval = _mm_cvtepi32_ps( _mm_cvttps_epi32( a ) ); + + return _mm_sub_ps( fval, _mm_and_ps( _mm_cmplt_ps( a, fval ), f1 ) ); + } + + template= Level_SSE41)>* = nullptr> + FS_INLINE static float32v Floor_f32( float32v a ) + { + return _mm_round_ps( a, _MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC ); + } + + template* = nullptr> + FS_INLINE static float32v Ceil_f32( float32v a ) + { +#if FASTSIMD_CONFIG_GENERATE_CONSTANTS + const __m128 f1 = _mm_castsi128_ps( _mm_slli_epi32( _mm_srli_epi32( _mm_cmpeq_epi32( _mm_setzero_si128(), _mm_setzero_si128() ), 25 ), 23 ) ); +#else + const __m128 f1 = _mm_set1_ps( 1.0f ); +#endif + __m128 fval = _mm_cvtepi32_ps( _mm_cvttps_epi32( a ) ); + __m128 cmp = _mm_cmplt_ps( fval, a ); + return _mm_add_ps( fval, _mm_and_ps( cmp, f1 ) ); + } + + template= Level_SSE41)>* = nullptr> + FS_INLINE static float32v Ceil_f32( float32v a ) + { + return _mm_round_ps( a, _MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC ); + } + + template* = nullptr> + FS_INLINE static float32v Round_f32( float32v a ) + { + __m128 aSign = _mm_and_ps( a, _mm_castsi128_ps( int32v( 0x80000000 ) ) ); + + return _mm_cvtepi32_ps( _mm_cvttps_epi32( a + float32v(_mm_or_ps( aSign, float32v( 0.5f ) ) ) ) ); + +#if FASTSIMD_CONFIG_GENERATE_CONSTANTS + const __m128 nearest2 = _mm_castsi128_ps( _mm_srli_epi32( _mm_cmpeq_epi32( _mm_setzero_si128(), _mm_setzero_si128() ), 2 ) ); +#else + const __m128 nearest2 = _mm_set1_ps( 1.99999988079071044921875f ); +#endif + __m128 aTrunc = _mm_cvtepi32_ps( _mm_cvttps_epi32( a ) ); // truncate a + __m128 rmd = _mm_sub_ps( a, aTrunc ); // get remainder + __m128 rmd2 = _mm_mul_ps( rmd, nearest2 ); // mul remainder by near 2 will yield the needed offset + __m128 rmd2Trunc = _mm_cvtepi32_ps( _mm_cvttps_epi32( rmd2 ) ); // after being truncated of course + return _mm_add_ps( aTrunc, rmd2Trunc ); + } + + template= Level_SSE41)>* = nullptr> + FS_INLINE static float32v Round_f32( float32v a ) + { + return _mm_round_ps( a, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC ); + } + + // Mask + + FS_INLINE static int32v Mask_i32( int32v a, mask32v m ) + { + return a & m; + } + + FS_INLINE static float32v Mask_f32( float32v a, mask32v m ) + { + return _mm_and_ps( a, _mm_castsi128_ps( m ) ); + } + + FS_INLINE static int32v NMask_i32( int32v a, mask32v m ) + { + return _mm_andnot_si128( m, a ); + } + + FS_INLINE static float32v NMask_f32( float32v a, mask32v m ) + { + return _mm_andnot_ps( _mm_castsi128_ps( m ), a ); + } + + FS_INLINE static bool AnyMask_bool( mask32v m ) + { + return _mm_movemask_ps( _mm_castsi128_ps( m ) ); + } + }; + +#if FASTSIMD_COMPILE_SSE + typedef SSE_T SSE; +#endif +#if FASTSIMD_COMPILE_SSE2 + typedef SSE_T SSE2; +#endif +#if FASTSIMD_COMPILE_SSE3 + typedef SSE_T SSE3; +#endif +#if FASTSIMD_COMPILE_SSSE3 + typedef SSE_T SSSE3; +#endif +#if FASTSIMD_COMPILE_SSE41 + typedef SSE_T SSE41; +#endif +#if FASTSIMD_COMPILE_SSE42 + typedef SSE_T SSE42; +#endif +} diff --git a/deps/FastNoise2/src/FastSIMD/Internal/Scalar.h b/deps/FastNoise2/src/FastSIMD/Internal/Scalar.h new file mode 100644 index 0000000..f297101 --- /dev/null +++ b/deps/FastNoise2/src/FastSIMD/Internal/Scalar.h @@ -0,0 +1,429 @@ +#pragma once + +#include "VecTools.h" +#include +#include + +namespace FastSIMD +{ + template + OUT ScalarCast( IN a ) + { + union + { + OUT o; + IN i; + } u; + + u.i = a; + return u.o; + } + + struct Scalar_Float + { + FASTSIMD_INTERNAL_TYPE_SET( Scalar_Float, float ); + + FS_INLINE static Scalar_Float Incremented() + { + return 0.0f; + } + + FS_INLINE Scalar_Float& operator+=( const Scalar_Float& rhs ) + { + vector += rhs; + return *this; + } + + FS_INLINE Scalar_Float& operator-=( const Scalar_Float& rhs ) + { + vector -= rhs; + return *this; + } + + FS_INLINE Scalar_Float& operator*=( const Scalar_Float& rhs ) + { + vector *= rhs; + return *this; + } + + FS_INLINE Scalar_Float& operator/=( const Scalar_Float& rhs ) + { + vector /= rhs; + return *this; + } + + FS_INLINE Scalar_Float& operator&=( const Scalar_Float& rhs ) + { + *this = ScalarCast( ScalarCast( *this ) & ScalarCast( rhs ) ); + return *this; + } + + FS_INLINE Scalar_Float& operator|=( const Scalar_Float& rhs ) + { + *this = ScalarCast( ScalarCast( *this ) | ScalarCast( rhs ) ); + return *this; + } + + FS_INLINE Scalar_Float& operator^=( const Scalar_Float& rhs ) + { + *this = ScalarCast( ScalarCast( *this ) ^ ScalarCast( rhs ) ); + return *this; + } + + FS_INLINE Scalar_Float operator~() const + { + return ScalarCast( ~ScalarCast( *this ) ); + } + + FS_INLINE Scalar_Float operator-() const + { + return -vector; + } + + FS_INLINE bool operator==( const Scalar_Float& rhs ) + { + return vector == rhs; + } + + FS_INLINE bool operator!=( const Scalar_Float& rhs ) + { + return vector != rhs; + } + + FS_INLINE bool operator>( const Scalar_Float& rhs ) + { + return vector > rhs; + } + + FS_INLINE bool operator<( const Scalar_Float& rhs ) + { + return vector < rhs; + } + + FS_INLINE bool operator>=( const Scalar_Float& rhs ) + { + return vector >= rhs; + } + + FS_INLINE bool operator<=( const Scalar_Float& rhs ) + { + return vector <= rhs; + } + }; + + FASTSIMD_INTERNAL_OPERATORS_FLOAT( Scalar_Float ) + + + struct Scalar_Int + { + FASTSIMD_INTERNAL_TYPE_SET( Scalar_Int, int32_t ); + + FS_INLINE static Scalar_Int Incremented() + { + return 0; + } + + FS_INLINE Scalar_Int& operator+=( const Scalar_Int& rhs ) + { + vector += rhs; + return *this; + } + + FS_INLINE Scalar_Int& operator-=( const Scalar_Int& rhs ) + { + vector -= rhs; + return *this; + } + + FS_INLINE Scalar_Int& operator*=( const Scalar_Int& rhs ) + { + vector *= rhs; + return *this; + } + + FS_INLINE Scalar_Int& operator&=( const Scalar_Int& rhs ) + { + vector &= rhs; + return *this; + } + + FS_INLINE Scalar_Int& operator|=( const Scalar_Int& rhs ) + { + vector |= rhs; + return *this; + } + + FS_INLINE Scalar_Int& operator^=( const Scalar_Int& rhs ) + { + vector ^= rhs; + return *this; + } + + FS_INLINE Scalar_Int& operator>>=( int32_t rhs ) + { + vector >>= rhs; + return *this; + } + + FS_INLINE Scalar_Int& operator<<=( int32_t rhs ) + { + vector <<= rhs; + return *this; + } + + FS_INLINE Scalar_Int operator~() const + { + return ~vector; + } + + FS_INLINE Scalar_Int operator-() const + { + return -vector; + } + + FS_INLINE bool operator==( const Scalar_Int& rhs ) + { + return vector == rhs; + } + + FS_INLINE bool operator>( const Scalar_Int& rhs ) + { + return vector > rhs; + } + + FS_INLINE bool operator<( const Scalar_Int& rhs ) + { + return vector < rhs; + } + }; + + FASTSIMD_INTERNAL_OPERATORS_INT( Scalar_Int, int32_t ) + + + struct Scalar_Mask + { + FASTSIMD_INTERNAL_TYPE_SET( Scalar_Mask, bool ); + + FS_INLINE Scalar_Mask operator~() const + { + return !vector; + } + + FS_INLINE Scalar_Mask& operator&=( const Scalar_Mask& rhs ) + { + vector = vector && rhs; + return *this; + } + + FS_INLINE Scalar_Mask& operator|=( const Scalar_Mask& rhs ) + { + vector = vector || rhs; + return *this; + } + + FS_INLINE Scalar_Mask operator&( const Scalar_Mask& rhs ) + { + return vector && rhs; + } + + FS_INLINE Scalar_Mask operator|( const Scalar_Mask& rhs ) + { + return vector || rhs; + } + }; + + class Scalar + { + public: + static constexpr eLevel SIMD_Level = FastSIMD::Level_Scalar; + + template + static constexpr size_t VectorSize = 32 / ElementSize; + + typedef Scalar_Float float32v; + typedef Scalar_Int int32v; + typedef Scalar_Mask mask32v; + + // Load + + FS_INLINE static float32v Load_f32( void const* p ) + { + return *reinterpret_cast(p); + } + + FS_INLINE static int32v Load_i32( void const* p ) + { + return *reinterpret_cast(p); + } + + // Store + + FS_INLINE static void Store_f32( void* p, float32v a ) + { + *reinterpret_cast(p) = a; + } + + FS_INLINE static void Store_i32( void* p, int32v a ) + { + *reinterpret_cast(p) = a; + } + + // Cast + + FS_INLINE static float32v Casti32_f32( int32v a ) + { + return ScalarCast( a ); + } + + FS_INLINE static int32v Castf32_i32( float32v a ) + { + return ScalarCast( a ); + } + + // Convert + + FS_INLINE static float32v Converti32_f32( int32v a ) + { + return static_cast(a); + } + + FS_INLINE static int32v Convertf32_i32( float32v a ) + { + return static_cast(nearbyint( a )); + } + + // Select + + FS_INLINE static float32v Select_f32( mask32v m, float32v a, float32v b ) + { + return m ? a : b; + } + + FS_INLINE static int32v Select_i32( mask32v m, int32v a, int32v b ) + { + return m ? a : b; + } + + // Min, Max + + FS_INLINE static float32v Min_f32( float32v a, float32v b ) + { + return fminf( a, b ); + } + + FS_INLINE static float32v Max_f32( float32v a, float32v b ) + { + return fmaxf( a, b ); + } + + FS_INLINE static int32v Min_i32( int32v a, int32v b ) + { + return std::min( a, b ); + } + + FS_INLINE static int32v Max_i32( int32v a, int32v b ) + { + return std::max( a, b ); + } + + // Bitwise + + FS_INLINE static float32v BitwiseAndNot_f32( float32v a, float32v b ) + { + return Casti32_f32( Castf32_i32( a ) & ~Castf32_i32( b ) ); + } + + FS_INLINE static int32v BitwiseAndNot_i32( int32v a, int32v b ) + { + return a & ~b; + } + + FS_INLINE static float32v BitwiseShiftRightZX_f32( float32v a, int32_t b ) + { + return Casti32_f32( int32_t( uint32_t( Castf32_i32( a ) ) >> b ) ); + } + + FS_INLINE static int32v BitwiseShiftRightZX_i32( int32v a, int32_t b ) + { + return int32_t( uint32_t( a ) >> b ); + } + + // Abs + + FS_INLINE static float32v Abs_f32( float32v a ) + { + return fabsf( a ); + } + + FS_INLINE static int32v Abs_i32( int32v a ) + { + return abs( a ); + } + + // Float math + + FS_INLINE static float32v Sqrt_f32( float32v a ) + { + return sqrtf( a ); + } + + FS_INLINE static float32v InvSqrt_f32( float32v a ) + { + float xhalf = 0.5f * (float)a; + a = Casti32_f32( 0x5f3759df - ((int32_t)Castf32_i32( a ) >> 1) ); + a *= (1.5f - xhalf * (float)a * (float)a); + return a; + } + + FS_INLINE static float32v Reciprocal_f32( float32v a ) + { + // pow( pow(x,-0.5), 2 ) = pow( x, -1 ) = 1.0 / x + a = Casti32_f32( (0xbe6eb3beU - (int32_t)Castf32_i32( a )) >> 1 ); + return a * a; + } + + // Floor, Ceil, Round + + FS_INLINE static float32v Floor_f32( float32v a ) + { + return floorf( a ); + } + + FS_INLINE static float32v Ceil_f32( float32v a ) + { + return ceilf( a ); + } + + FS_INLINE static float32v Round_f32( float32v a ) + { + return nearbyintf( a ); + } + + // Mask + + FS_INLINE static int32v Mask_i32( int32v a, mask32v m ) + { + return m ? a : int32v(0); + } + + FS_INLINE static float32v Mask_f32( float32v a, mask32v m ) + { + return m ? a : float32v(0); + } + + FS_INLINE static int32v NMask_i32( int32v a, mask32v m ) + { + return m ? int32v(0) : a; + } + + FS_INLINE static float32v NMask_f32( float32v a, mask32v m ) + { + return m ? float32v(0) : a; + } + + FS_INLINE static bool AnyMask_bool( mask32v m ) + { + return m; + } + }; +} diff --git a/deps/FastNoise2/src/FastSIMD/Internal/SourceBuilder.inl b/deps/FastNoise2/src/FastSIMD/Internal/SourceBuilder.inl new file mode 100644 index 0000000..6b8e424 --- /dev/null +++ b/deps/FastNoise2/src/FastSIMD/Internal/SourceBuilder.inl @@ -0,0 +1,22 @@ +#pragma once +#include "FastSIMD/FastSIMD.h" +#include "FastSIMD/TypeList.h" + +template +class FS_T; + +template +CLASS* FastSIMD::ClassFactory() +{ + if constexpr( ( CLASS::Supported_SIMD_Levels & LEVEL & FastSIMD::COMPILED_SIMD_LEVELS ) != 0 ) + { + static_assert( std::is_base_of_v> ); + return new FS_T; + } + return nullptr; +} + +#define FASTSIMD_BUILD_CLASS( CLASS ) \ +template CLASS* FastSIMD::ClassFactory(); + +#include "../FastSIMD_BuildList.inl" diff --git a/deps/FastNoise2/src/FastSIMD/Internal/VecTools.h b/deps/FastNoise2/src/FastSIMD/Internal/VecTools.h new file mode 100644 index 0000000..d3c4258 --- /dev/null +++ b/deps/FastNoise2/src/FastSIMD/Internal/VecTools.h @@ -0,0 +1,66 @@ +#pragma once + +#include + +#include "FastSIMD/FastSIMD.h" +#include "FastSIMD/FunctionList.h" + +#define FASTSIMD_INTERNAL_TYPE_SET( CLASS, TYPE ) \ +TYPE vector; \ +FS_INLINE CLASS() { } \ +FS_INLINE CLASS( const TYPE& v ) : vector(v) {}; \ +FS_INLINE CLASS& operator = ( const TYPE& v ) { vector = v; return *this; } \ +FS_INLINE operator TYPE() const { return vector; } + +#define FASTSIMD_INTERNAL_OPERATOR( TYPE, TYPE2, OPERATOR, OPERATOREQUALS ) \ +FS_INLINE static TYPE operator OPERATOR ( TYPE lhs, TYPE2 rhs ) \ +{ \ + lhs OPERATOREQUALS rhs; \ + return lhs; \ +} + +#define FASTSIMD_INTERNAL_OPERATOR_TEMPLATED( TYPE, TYPE2, OPERATOR, OPERATOREQUALS ) \ +template \ +FS_INLINE static TYPE operator OPERATOR ( TYPE lhs, TYPE2 rhs ) \ +{ \ + lhs OPERATOREQUALS rhs; \ + return lhs; \ +} + +#define FASTSIMD_INTERNAL_OPERATORS_FLOAT( TYPE ) \ +FASTSIMD_INTERNAL_OPERATOR( TYPE, const TYPE&, +, += ) \ +FASTSIMD_INTERNAL_OPERATOR( TYPE, const TYPE&, -, -= ) \ +FASTSIMD_INTERNAL_OPERATOR( TYPE, const TYPE&, *, *= ) \ +FASTSIMD_INTERNAL_OPERATOR( TYPE, const TYPE&, /, /= ) \ +FASTSIMD_INTERNAL_OPERATOR( TYPE, const TYPE&, &, &= ) \ +FASTSIMD_INTERNAL_OPERATOR( TYPE, const TYPE&, |, |= ) \ +FASTSIMD_INTERNAL_OPERATOR( TYPE, const TYPE&, ^, ^= ) + +#define FASTSIMD_INTERNAL_OPERATORS_FLOAT_TEMPLATED( TYPE ) \ +FASTSIMD_INTERNAL_OPERATOR_TEMPLATED( TYPE, const TYPE&, +, += ) \ +FASTSIMD_INTERNAL_OPERATOR_TEMPLATED( TYPE, const TYPE&, -, -= ) \ +FASTSIMD_INTERNAL_OPERATOR_TEMPLATED( TYPE, const TYPE&, *, *= ) \ +FASTSIMD_INTERNAL_OPERATOR_TEMPLATED( TYPE, const TYPE&, /, /= ) \ +FASTSIMD_INTERNAL_OPERATOR_TEMPLATED( TYPE, const TYPE&, &, &= ) \ +FASTSIMD_INTERNAL_OPERATOR_TEMPLATED( TYPE, const TYPE&, |, |= ) \ +FASTSIMD_INTERNAL_OPERATOR_TEMPLATED( TYPE, const TYPE&, ^, ^= ) + +#define FASTSIMD_INTERNAL_OPERATORS_INT( TYPE, TYPE2 ) \ +FASTSIMD_INTERNAL_OPERATOR( TYPE, const TYPE&, +, += ) \ +FASTSIMD_INTERNAL_OPERATOR( TYPE, const TYPE&, -, -= ) \ +FASTSIMD_INTERNAL_OPERATOR( TYPE, const TYPE&, *, *= ) \ +FASTSIMD_INTERNAL_OPERATOR( TYPE, const TYPE&, &, &= ) \ +FASTSIMD_INTERNAL_OPERATOR( TYPE, const TYPE&, |, |= ) \ +FASTSIMD_INTERNAL_OPERATOR( TYPE, const TYPE&, ^, ^= ) \ +FASTSIMD_INTERNAL_OPERATOR( TYPE, TYPE2, >>, >>= ) \ +FASTSIMD_INTERNAL_OPERATOR( TYPE, TYPE2, <<, <<= ) + +#define FASTSIMD_INTERNAL_OPERATORS_INT_TEMPLATED( TYPE, TYPE2 ) \ +FASTSIMD_INTERNAL_OPERATOR_TEMPLATED( TYPE, const TYPE&, +, += ) \ +FASTSIMD_INTERNAL_OPERATOR_TEMPLATED( TYPE, const TYPE&, -, -= ) \ +FASTSIMD_INTERNAL_OPERATOR_TEMPLATED( TYPE, const TYPE&, *, *= ) \ +FASTSIMD_INTERNAL_OPERATOR_TEMPLATED( TYPE, const TYPE&, &, &= ) \ +FASTSIMD_INTERNAL_OPERATOR_TEMPLATED( TYPE, const TYPE&, |, |= ) \ +FASTSIMD_INTERNAL_OPERATOR_TEMPLATED( TYPE, const TYPE&, ^, ^= ) \ +FASTSIMD_INTERNAL_OPERATOR_TEMPLATED( TYPE, TYPE2, >>, >>= ) \ +FASTSIMD_INTERNAL_OPERATOR_TEMPLATED( TYPE, TYPE2, <<, <<= ) diff --git a/deps/FastNoiseSIMD/ARM/cpu-features.c b/deps/FastNoiseSIMD/ARM/cpu-features.c deleted file mode 100644 index b2069c0..0000000 --- a/deps/FastNoiseSIMD/ARM/cpu-features.c +++ /dev/null @@ -1,1292 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* ChangeLog for this library: - * - * NDK r10e?: Add MIPS MSA feature. - * - * NDK r10: Support for 64-bit CPUs (Intel, ARM & MIPS). - * - * NDK r8d: Add android_setCpu(). - * - * NDK r8c: Add new ARM CPU features: VFPv2, VFP_D32, VFP_FP16, - * VFP_FMA, NEON_FMA, IDIV_ARM, IDIV_THUMB2 and iWMMXt. - * - * Rewrite the code to parse /proc/self/auxv instead of - * the "Features" field in /proc/cpuinfo. - * - * Dynamically allocate the buffer that hold the content - * of /proc/cpuinfo to deal with newer hardware. - * - * NDK r7c: Fix CPU count computation. The old method only reported the - * number of _active_ CPUs when the library was initialized, - * which could be less than the real total. - * - * NDK r5: Handle buggy kernels which report a CPU Architecture number of 7 - * for an ARMv6 CPU (see below). - * - * Handle kernels that only report 'neon', and not 'vfpv3' - * (VFPv3 is mandated by the ARM architecture is Neon is implemented) - * - * Handle kernels that only report 'vfpv3d16', and not 'vfpv3' - * - * Fix x86 compilation. Report ANDROID_CPU_FAMILY_X86 in - * android_getCpuFamily(). - * - * NDK r4: Initial release - */ - -#include "cpu-features.h" - -#include -#include -#include -#include -#include -#include -#include - -static pthread_once_t g_once; -static int g_inited; -static AndroidCpuFamily g_cpuFamily; -static uint64_t g_cpuFeatures; -static int g_cpuCount; - -#ifdef __arm__ -static uint32_t g_cpuIdArm; -#endif - -static const int android_cpufeatures_debug = 0; - -#define D(...) \ - do { \ - if (android_cpufeatures_debug) { \ - printf(__VA_ARGS__); fflush(stdout); \ - } \ - } while (0) - -#ifdef __i386__ -static __inline__ void x86_cpuid(int func, int values[4]) -{ - int a, b, c, d; - /* We need to preserve ebx since we're compiling PIC code */ - /* this means we can't use "=b" for the second output register */ - __asm__ __volatile__ ( \ - "push %%ebx\n" - "cpuid\n" \ - "mov %%ebx, %1\n" - "pop %%ebx\n" - : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ - : "a" (func) \ - ); - values[0] = a; - values[1] = b; - values[2] = c; - values[3] = d; -} -#elif defined(__x86_64__) -static __inline__ void x86_cpuid(int func, int values[4]) -{ - int64_t a, b, c, d; - /* We need to preserve ebx since we're compiling PIC code */ - /* this means we can't use "=b" for the second output register */ - __asm__ __volatile__ ( \ - "push %%rbx\n" - "cpuid\n" \ - "mov %%rbx, %1\n" - "pop %%rbx\n" - : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ - : "a" (func) \ - ); - values[0] = a; - values[1] = b; - values[2] = c; - values[3] = d; -} -#endif - -/* Get the size of a file by reading it until the end. This is needed - * because files under /proc do not always return a valid size when - * using fseek(0, SEEK_END) + ftell(). Nor can they be mmap()-ed. - */ -static int -get_file_size(const char* pathname) -{ - - int fd, result = 0; - char buffer[256]; - - fd = open(pathname, O_RDONLY); - if (fd < 0) { - D("Can't open %s: %s\n", pathname, strerror(errno)); - return -1; - } - - for (;;) { - int ret = read(fd, buffer, sizeof buffer); - if (ret < 0) { - if (errno == EINTR) - continue; - D("Error while reading %s: %s\n", pathname, strerror(errno)); - break; - } - if (ret == 0) - break; - - result += ret; - } - close(fd); - return result; -} - -/* Read the content of /proc/cpuinfo into a user-provided buffer. - * Return the length of the data, or -1 on error. Does *not* - * zero-terminate the content. Will not read more - * than 'buffsize' bytes. - */ -static int -read_file(const char* pathname, char* buffer, size_t buffsize) -{ - int fd, count; - - fd = open(pathname, O_RDONLY); - if (fd < 0) { - D("Could not open %s: %s\n", pathname, strerror(errno)); - return -1; - } - count = 0; - while (count < (int)buffsize) { - int ret = read(fd, buffer + count, buffsize - count); - if (ret < 0) { - if (errno == EINTR) - continue; - D("Error while reading from %s: %s\n", pathname, strerror(errno)); - if (count == 0) - count = -1; - break; - } - if (ret == 0) - break; - count += ret; - } - close(fd); - return count; -} - -#ifdef __arm__ -/* Extract the content of a the first occurence of a given field in - * the content of /proc/cpuinfo and return it as a heap-allocated - * string that must be freed by the caller. - * - * Return NULL if not found - */ -static char* -extract_cpuinfo_field(const char* buffer, int buflen, const char* field) -{ - int fieldlen = strlen(field); - const char* bufend = buffer + buflen; - char* result = NULL; - int len; - const char *p, *q; - - /* Look for first field occurence, and ensures it starts the line. */ - p = buffer; - for (;;) { - p = memmem(p, bufend-p, field, fieldlen); - if (p == NULL) - goto EXIT; - - if (p == buffer || p[-1] == '\n') - break; - - p += fieldlen; - } - - /* Skip to the first column followed by a space */ - p += fieldlen; - p = memchr(p, ':', bufend-p); - if (p == NULL || p[1] != ' ') - goto EXIT; - - /* Find the end of the line */ - p += 2; - q = memchr(p, '\n', bufend-p); - if (q == NULL) - q = bufend; - - /* Copy the line into a heap-allocated buffer */ - len = q-p; - result = malloc(len+1); - if (result == NULL) - goto EXIT; - - memcpy(result, p, len); - result[len] = '\0'; - -EXIT: - return result; -} - -/* Checks that a space-separated list of items contains one given 'item'. - * Returns 1 if found, 0 otherwise. - */ -static int -has_list_item(const char* list, const char* item) -{ - const char* p = list; - int itemlen = strlen(item); - - if (list == NULL) - return 0; - - while (*p) { - const char* q; - - /* skip spaces */ - while (*p == ' ' || *p == '\t') - p++; - - /* find end of current list item */ - q = p; - while (*q && *q != ' ' && *q != '\t') - q++; - - if (itemlen == q-p && !memcmp(p, item, itemlen)) - return 1; - - /* skip to next item */ - p = q; - } - return 0; -} -#endif /* __arm__ */ - -/* Parse a number starting from 'input', but not going further - * than 'limit'. Return the value into '*result'. - * - * NOTE: Does not skip over leading spaces, or deal with sign characters. - * NOTE: Ignores overflows. - * - * The function returns NULL in case of error (bad format), or the new - * position after the decimal number in case of success (which will always - * be <= 'limit'). - */ -static const char* -parse_number(const char* input, const char* limit, int base, int* result) -{ - const char* p = input; - int val = 0; - while (p < limit) { - int d = (*p - '0'); - if ((unsigned)d >= 10U) { - d = (*p - 'a'); - if ((unsigned)d >= 6U) - d = (*p - 'A'); - if ((unsigned)d >= 6U) - break; - d += 10; - } - if (d >= base) - break; - val = val*base + d; - p++; - } - if (p == input) - return NULL; - - *result = val; - return p; -} - -static const char* -parse_decimal(const char* input, const char* limit, int* result) -{ - return parse_number(input, limit, 10, result); -} - -#ifdef __arm__ -static const char* -parse_hexadecimal(const char* input, const char* limit, int* result) -{ - return parse_number(input, limit, 16, result); -} -#endif /* __arm__ */ - -/* This small data type is used to represent a CPU list / mask, as read - * from sysfs on Linux. See http://www.kernel.org/doc/Documentation/cputopology.txt - * - * For now, we don't expect more than 32 cores on mobile devices, so keep - * everything simple. - */ -typedef struct { - uint32_t mask; -} CpuList; - -static __inline__ void -cpulist_init(CpuList* list) { - list->mask = 0; -} - -static __inline__ void -cpulist_and(CpuList* list1, CpuList* list2) { - list1->mask &= list2->mask; -} - -static __inline__ void -cpulist_set(CpuList* list, int index) { - if ((unsigned)index < 32) { - list->mask |= (uint32_t)(1U << index); - } -} - -static __inline__ int -cpulist_count(CpuList* list) { - return __builtin_popcount(list->mask); -} - -/* Parse a textual list of cpus and store the result inside a CpuList object. - * Input format is the following: - * - comma-separated list of items (no spaces) - * - each item is either a single decimal number (cpu index), or a range made - * of two numbers separated by a single dash (-). Ranges are inclusive. - * - * Examples: 0 - * 2,4-127,128-143 - * 0-1 - */ -static void -cpulist_parse(CpuList* list, const char* line, int line_len) -{ - const char* p = line; - const char* end = p + line_len; - const char* q; - - /* NOTE: the input line coming from sysfs typically contains a - * trailing newline, so take care of it in the code below - */ - while (p < end && *p != '\n') - { - int val, start_value, end_value; - - /* Find the end of current item, and put it into 'q' */ - q = memchr(p, ',', end-p); - if (q == NULL) { - q = end; - } - - /* Get first value */ - p = parse_decimal(p, q, &start_value); - if (p == NULL) - goto BAD_FORMAT; - - end_value = start_value; - - /* If we're not at the end of the item, expect a dash and - * and integer; extract end value. - */ - if (p < q && *p == '-') { - p = parse_decimal(p+1, q, &end_value); - if (p == NULL) - goto BAD_FORMAT; - } - - /* Set bits CPU list bits */ - for (val = start_value; val <= end_value; val++) { - cpulist_set(list, val); - } - - /* Jump to next item */ - p = q; - if (p < end) - p++; - } - -BAD_FORMAT: - ; -} - -/* Read a CPU list from one sysfs file */ -static void -cpulist_read_from(CpuList* list, const char* filename) -{ - char file[64]; - int filelen; - - cpulist_init(list); - - filelen = read_file(filename, file, sizeof file); - if (filelen < 0) { - D("Could not read %s: %s\n", filename, strerror(errno)); - return; - } - - cpulist_parse(list, file, filelen); -} -#if defined(__aarch64__) -// see kernel header -#define HWCAP_FP (1 << 0) -#define HWCAP_ASIMD (1 << 1) -#define HWCAP_AES (1 << 3) -#define HWCAP_PMULL (1 << 4) -#define HWCAP_SHA1 (1 << 5) -#define HWCAP_SHA2 (1 << 6) -#define HWCAP_CRC32 (1 << 7) -#endif - -#if defined(__arm__) - -// See kernel header. -#define HWCAP_VFP (1 << 6) -#define HWCAP_IWMMXT (1 << 9) -#define HWCAP_NEON (1 << 12) -#define HWCAP_VFPv3 (1 << 13) -#define HWCAP_VFPv3D16 (1 << 14) -#define HWCAP_VFPv4 (1 << 16) -#define HWCAP_IDIVA (1 << 17) -#define HWCAP_IDIVT (1 << 18) - -// see kernel header -#define HWCAP2_AES (1 << 0) -#define HWCAP2_PMULL (1 << 1) -#define HWCAP2_SHA1 (1 << 2) -#define HWCAP2_SHA2 (1 << 3) -#define HWCAP2_CRC32 (1 << 4) - -// This is the list of 32-bit ARMv7 optional features that are _always_ -// supported by ARMv8 CPUs, as mandated by the ARM Architecture Reference -// Manual. -#define HWCAP_SET_FOR_ARMV8 \ - ( HWCAP_VFP | \ - HWCAP_NEON | \ - HWCAP_VFPv3 | \ - HWCAP_VFPv4 | \ - HWCAP_IDIVA | \ - HWCAP_IDIVT ) -#endif - -#if defined(__mips__) -// see kernel header -#define HWCAP_MIPS_R6 (1 << 0) -#define HWCAP_MIPS_MSA (1 << 1) -#endif - -#if defined(__arm__) || defined(__aarch64__) || defined(__mips__) - -#define AT_HWCAP 16 -#define AT_HWCAP2 26 - -// Probe the system's C library for a 'getauxval' function and call it if -// it exits, or return 0 for failure. This function is available since API -// level 20. -// -// This code does *NOT* check for '__ANDROID_API__ >= 20' to support the -// edge case where some NDK developers use headers for a platform that is -// newer than the one really targetted by their application. -// This is typically done to use newer native APIs only when running on more -// recent Android versions, and requires careful symbol management. -// -// Note that getauxval() can't really be re-implemented here, because -// its implementation does not parse /proc/self/auxv. Instead it depends -// on values that are passed by the kernel at process-init time to the -// C runtime initialization layer. -static uint32_t -get_elf_hwcap_from_getauxval(int hwcap_type) { - typedef unsigned long getauxval_func_t(unsigned long); - - dlerror(); - void* libc_handle = dlopen("libc.so", RTLD_NOW); - if (!libc_handle) { - D("Could not dlopen() C library: %s\n", dlerror()); - return 0; - } - - uint32_t ret = 0; - getauxval_func_t* func = (getauxval_func_t*) - dlsym(libc_handle, "getauxval"); - if (!func) { - D("Could not find getauxval() in C library\n"); - } else { - // Note: getauxval() returns 0 on failure. Doesn't touch errno. - ret = (uint32_t)(*func)(hwcap_type); - } - dlclose(libc_handle); - return ret; -} -#endif - -#if defined(__arm__) -// Parse /proc/self/auxv to extract the ELF HW capabilities bitmap for the -// current CPU. Note that this file is not accessible from regular -// application processes on some Android platform releases. -// On success, return new ELF hwcaps, or 0 on failure. -static uint32_t -get_elf_hwcap_from_proc_self_auxv(void) { - const char filepath[] = "/proc/self/auxv"; - int fd = TEMP_FAILURE_RETRY(open(filepath, O_RDONLY)); - if (fd < 0) { - D("Could not open %s: %s\n", filepath, strerror(errno)); - return 0; - } - - struct { uint32_t tag; uint32_t value; } entry; - - uint32_t result = 0; - for (;;) { - int ret = TEMP_FAILURE_RETRY(read(fd, (char*)&entry, sizeof entry)); - if (ret < 0) { - D("Error while reading %s: %s\n", filepath, strerror(errno)); - break; - } - // Detect end of list. - if (ret == 0 || (entry.tag == 0 && entry.value == 0)) - break; - if (entry.tag == AT_HWCAP) { - result = entry.value; - break; - } - } - close(fd); - return result; -} - -/* Compute the ELF HWCAP flags from the content of /proc/cpuinfo. - * This works by parsing the 'Features' line, which lists which optional - * features the device's CPU supports, on top of its reference - * architecture. - */ -static uint32_t -get_elf_hwcap_from_proc_cpuinfo(const char* cpuinfo, int cpuinfo_len) { - uint32_t hwcaps = 0; - long architecture = 0; - char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "CPU architecture"); - if (cpuArch) { - architecture = strtol(cpuArch, NULL, 10); - free(cpuArch); - - if (architecture >= 8L) { - // This is a 32-bit ARM binary running on a 64-bit ARM64 kernel. - // The 'Features' line only lists the optional features that the - // device's CPU supports, compared to its reference architecture - // which are of no use for this process. - D("Faking 32-bit ARM HWCaps on ARMv%ld CPU\n", architecture); - return HWCAP_SET_FOR_ARMV8; - } - } - - char* cpuFeatures = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "Features"); - if (cpuFeatures != NULL) { - D("Found cpuFeatures = '%s'\n", cpuFeatures); - - if (has_list_item(cpuFeatures, "vfp")) - hwcaps |= HWCAP_VFP; - if (has_list_item(cpuFeatures, "vfpv3")) - hwcaps |= HWCAP_VFPv3; - if (has_list_item(cpuFeatures, "vfpv3d16")) - hwcaps |= HWCAP_VFPv3D16; - if (has_list_item(cpuFeatures, "vfpv4")) - hwcaps |= HWCAP_VFPv4; - if (has_list_item(cpuFeatures, "neon")) - hwcaps |= HWCAP_NEON; - if (has_list_item(cpuFeatures, "idiva")) - hwcaps |= HWCAP_IDIVA; - if (has_list_item(cpuFeatures, "idivt")) - hwcaps |= HWCAP_IDIVT; - if (has_list_item(cpuFeatures, "idiv")) - hwcaps |= HWCAP_IDIVA | HWCAP_IDIVT; - if (has_list_item(cpuFeatures, "iwmmxt")) - hwcaps |= HWCAP_IWMMXT; - - free(cpuFeatures); - } - return hwcaps; -} -#endif /* __arm__ */ - -/* Return the number of cpus present on a given device. - * - * To handle all weird kernel configurations, we need to compute the - * intersection of the 'present' and 'possible' CPU lists and count - * the result. - */ -static int -get_cpu_count(void) -{ - CpuList cpus_present[1]; - CpuList cpus_possible[1]; - - cpulist_read_from(cpus_present, "/sys/devices/system/cpu/present"); - cpulist_read_from(cpus_possible, "/sys/devices/system/cpu/possible"); - - /* Compute the intersection of both sets to get the actual number of - * CPU cores that can be used on this device by the kernel. - */ - cpulist_and(cpus_present, cpus_possible); - - return cpulist_count(cpus_present); -} - -static void -android_cpuInitFamily(void) -{ -#if defined(__arm__) - g_cpuFamily = ANDROID_CPU_FAMILY_ARM; -#elif defined(__i386__) - g_cpuFamily = ANDROID_CPU_FAMILY_X86; -#elif defined(__mips64) -/* Needs to be before __mips__ since the compiler defines both */ - g_cpuFamily = ANDROID_CPU_FAMILY_MIPS64; -#elif defined(__mips__) - g_cpuFamily = ANDROID_CPU_FAMILY_MIPS; -#elif defined(__aarch64__) - g_cpuFamily = ANDROID_CPU_FAMILY_ARM64; -#elif defined(__x86_64__) - g_cpuFamily = ANDROID_CPU_FAMILY_X86_64; -#else - g_cpuFamily = ANDROID_CPU_FAMILY_UNKNOWN; -#endif -} - -static void -android_cpuInit(void) -{ - char* cpuinfo = NULL; - int cpuinfo_len; - - android_cpuInitFamily(); - - g_cpuFeatures = 0; - g_cpuCount = 1; - g_inited = 1; - - cpuinfo_len = get_file_size("/proc/cpuinfo"); - if (cpuinfo_len < 0) { - D("cpuinfo_len cannot be computed!"); - return; - } - cpuinfo = malloc(cpuinfo_len); - if (cpuinfo == NULL) { - D("cpuinfo buffer could not be allocated"); - return; - } - cpuinfo_len = read_file("/proc/cpuinfo", cpuinfo, cpuinfo_len); - D("cpuinfo_len is (%d):\n%.*s\n", cpuinfo_len, - cpuinfo_len >= 0 ? cpuinfo_len : 0, cpuinfo); - - if (cpuinfo_len < 0) /* should not happen */ { - free(cpuinfo); - return; - } - - /* Count the CPU cores, the value may be 0 for single-core CPUs */ - g_cpuCount = get_cpu_count(); - if (g_cpuCount == 0) { - g_cpuCount = 1; - } - - D("found cpuCount = %d\n", g_cpuCount); - -#ifdef __arm__ - { - /* Extract architecture from the "CPU Architecture" field. - * The list is well-known, unlike the the output of - * the 'Processor' field which can vary greatly. - * - * See the definition of the 'proc_arch' array in - * $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in - * same file. - */ - char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "CPU architecture"); - - if (cpuArch != NULL) { - char* end; - long archNumber; - int hasARMv7 = 0; - - D("found cpuArch = '%s'\n", cpuArch); - - /* read the initial decimal number, ignore the rest */ - archNumber = strtol(cpuArch, &end, 10); - - /* Note that ARMv8 is upwards compatible with ARMv7. */ - if (end > cpuArch && archNumber >= 7) { - hasARMv7 = 1; - } - - /* Unfortunately, it seems that certain ARMv6-based CPUs - * report an incorrect architecture number of 7! - * - * See http://code.google.com/p/android/issues/detail?id=10812 - * - * We try to correct this by looking at the 'elf_format' - * field reported by the 'Processor' field, which is of the - * form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for - * an ARMv6-one. - */ - if (hasARMv7) { - char* cpuProc = extract_cpuinfo_field(cpuinfo, cpuinfo_len, - "Processor"); - if (cpuProc != NULL) { - D("found cpuProc = '%s'\n", cpuProc); - if (has_list_item(cpuProc, "(v6l)")) { - D("CPU processor and architecture mismatch!!\n"); - hasARMv7 = 0; - } - free(cpuProc); - } - } - - if (hasARMv7) { - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_ARMv7; - } - - /* The LDREX / STREX instructions are available from ARMv6 */ - if (archNumber >= 6) { - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_LDREX_STREX; - } - - free(cpuArch); - } - - /* Extract the list of CPU features from ELF hwcaps */ - uint32_t hwcaps = 0; - hwcaps = get_elf_hwcap_from_getauxval(AT_HWCAP); - if (!hwcaps) { - D("Parsing /proc/self/auxv to extract ELF hwcaps!\n"); - hwcaps = get_elf_hwcap_from_proc_self_auxv(); - } - if (!hwcaps) { - // Parsing /proc/self/auxv will fail from regular application - // processes on some Android platform versions, when this happens - // parse proc/cpuinfo instead. - D("Parsing /proc/cpuinfo to extract ELF hwcaps!\n"); - hwcaps = get_elf_hwcap_from_proc_cpuinfo(cpuinfo, cpuinfo_len); - } - - if (hwcaps != 0) { - int has_vfp = (hwcaps & HWCAP_VFP); - int has_vfpv3 = (hwcaps & HWCAP_VFPv3); - int has_vfpv3d16 = (hwcaps & HWCAP_VFPv3D16); - int has_vfpv4 = (hwcaps & HWCAP_VFPv4); - int has_neon = (hwcaps & HWCAP_NEON); - int has_idiva = (hwcaps & HWCAP_IDIVA); - int has_idivt = (hwcaps & HWCAP_IDIVT); - int has_iwmmxt = (hwcaps & HWCAP_IWMMXT); - - // The kernel does a poor job at ensuring consistency when - // describing CPU features. So lots of guessing is needed. - - // 'vfpv4' implies VFPv3|VFP_FMA|FP16 - if (has_vfpv4) - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3 | - ANDROID_CPU_ARM_FEATURE_VFP_FP16 | - ANDROID_CPU_ARM_FEATURE_VFP_FMA; - - // 'vfpv3' or 'vfpv3d16' imply VFPv3. Note that unlike GCC, - // a value of 'vfpv3' doesn't necessarily mean that the D32 - // feature is present, so be conservative. All CPUs in the - // field that support D32 also support NEON, so this should - // not be a problem in practice. - if (has_vfpv3 || has_vfpv3d16) - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3; - - // 'vfp' is super ambiguous. Depending on the kernel, it can - // either mean VFPv2 or VFPv3. Make it depend on ARMv7. - if (has_vfp) { - if (g_cpuFeatures & ANDROID_CPU_ARM_FEATURE_ARMv7) - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3; - else - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv2; - } - - // Neon implies VFPv3|D32, and if vfpv4 is detected, NEON_FMA - if (has_neon) { - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3 | - ANDROID_CPU_ARM_FEATURE_NEON | - ANDROID_CPU_ARM_FEATURE_VFP_D32; - if (has_vfpv4) - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_NEON_FMA; - } - - // VFPv3 implies VFPv2 and ARMv7 - if (g_cpuFeatures & ANDROID_CPU_ARM_FEATURE_VFPv3) - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv2 | - ANDROID_CPU_ARM_FEATURE_ARMv7; - - if (has_idiva) - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_ARM; - if (has_idivt) - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2; - - if (has_iwmmxt) - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_iWMMXt; - } - - /* Extract the list of CPU features from ELF hwcaps2 */ - uint32_t hwcaps2 = 0; - hwcaps2 = get_elf_hwcap_from_getauxval(AT_HWCAP2); - if (hwcaps2 != 0) { - int has_aes = (hwcaps2 & HWCAP2_AES); - int has_pmull = (hwcaps2 & HWCAP2_PMULL); - int has_sha1 = (hwcaps2 & HWCAP2_SHA1); - int has_sha2 = (hwcaps2 & HWCAP2_SHA2); - int has_crc32 = (hwcaps2 & HWCAP2_CRC32); - - if (has_aes) - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_AES; - if (has_pmull) - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_PMULL; - if (has_sha1) - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_SHA1; - if (has_sha2) - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_SHA2; - if (has_crc32) - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_CRC32; - } - /* Extract the cpuid value from various fields */ - // The CPUID value is broken up in several entries in /proc/cpuinfo. - // This table is used to rebuild it from the entries. - static const struct CpuIdEntry { - const char* field; - char format; - char bit_lshift; - char bit_length; - } cpu_id_entries[] = { - { "CPU implementer", 'x', 24, 8 }, - { "CPU variant", 'x', 20, 4 }, - { "CPU part", 'x', 4, 12 }, - { "CPU revision", 'd', 0, 4 }, - }; - size_t i; - D("Parsing /proc/cpuinfo to recover CPUID\n"); - for (i = 0; - i < sizeof(cpu_id_entries)/sizeof(cpu_id_entries[0]); - ++i) { - const struct CpuIdEntry* entry = &cpu_id_entries[i]; - char* value = extract_cpuinfo_field(cpuinfo, - cpuinfo_len, - entry->field); - if (value == NULL) - continue; - - D("field=%s value='%s'\n", entry->field, value); - char* value_end = value + strlen(value); - int val = 0; - const char* start = value; - const char* p; - if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X')) { - start += 2; - p = parse_hexadecimal(start, value_end, &val); - } else if (entry->format == 'x') - p = parse_hexadecimal(value, value_end, &val); - else - p = parse_decimal(value, value_end, &val); - - if (p > (const char*)start) { - val &= ((1 << entry->bit_length)-1); - val <<= entry->bit_lshift; - g_cpuIdArm |= (uint32_t) val; - } - - free(value); - } - - // Handle kernel configuration bugs that prevent the correct - // reporting of CPU features. - static const struct CpuFix { - uint32_t cpuid; - uint64_t or_flags; - } cpu_fixes[] = { - /* The Nexus 4 (Qualcomm Krait) kernel configuration - * forgets to report IDIV support. */ - { 0x510006f2, ANDROID_CPU_ARM_FEATURE_IDIV_ARM | - ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2 }, - { 0x510006f3, ANDROID_CPU_ARM_FEATURE_IDIV_ARM | - ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2 }, - }; - size_t n; - for (n = 0; n < sizeof(cpu_fixes)/sizeof(cpu_fixes[0]); ++n) { - const struct CpuFix* entry = &cpu_fixes[n]; - - if (g_cpuIdArm == entry->cpuid) - g_cpuFeatures |= entry->or_flags; - } - - // Special case: The emulator-specific Android 4.2 kernel fails - // to report support for the 32-bit ARM IDIV instruction. - // Technically, this is a feature of the virtual CPU implemented - // by the emulator. Note that it could also support Thumb IDIV - // in the future, and this will have to be slightly updated. - char* hardware = extract_cpuinfo_field(cpuinfo, - cpuinfo_len, - "Hardware"); - if (hardware) { - if (!strcmp(hardware, "Goldfish") && - g_cpuIdArm == 0x4100c080 && - (g_cpuFamily & ANDROID_CPU_ARM_FEATURE_ARMv7) != 0) { - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_ARM; - } - free(hardware); - } - } -#endif /* __arm__ */ -#ifdef __aarch64__ - { - /* Extract the list of CPU features from ELF hwcaps */ - uint32_t hwcaps = 0; - hwcaps = get_elf_hwcap_from_getauxval(AT_HWCAP); - if (hwcaps != 0) { - int has_fp = (hwcaps & HWCAP_FP); - int has_asimd = (hwcaps & HWCAP_ASIMD); - int has_aes = (hwcaps & HWCAP_AES); - int has_pmull = (hwcaps & HWCAP_PMULL); - int has_sha1 = (hwcaps & HWCAP_SHA1); - int has_sha2 = (hwcaps & HWCAP_SHA2); - int has_crc32 = (hwcaps & HWCAP_CRC32); - - if(has_fp == 0) { - D("ERROR: Floating-point unit missing, but is required by Android on AArch64 CPUs\n"); - } - if(has_asimd == 0) { - D("ERROR: ASIMD unit missing, but is required by Android on AArch64 CPUs\n"); - } - - if (has_fp) - g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_FP; - if (has_asimd) - g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_ASIMD; - if (has_aes) - g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_AES; - if (has_pmull) - g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_PMULL; - if (has_sha1) - g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_SHA1; - if (has_sha2) - g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_SHA2; - if (has_crc32) - g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_CRC32; - } - } -#endif /* __aarch64__ */ - -#if defined(__i386__) || defined(__x86_64__) - int regs[4]; - -/* According to http://en.wikipedia.org/wiki/CPUID */ -#define VENDOR_INTEL_b 0x756e6547 -#define VENDOR_INTEL_c 0x6c65746e -#define VENDOR_INTEL_d 0x49656e69 - - x86_cpuid(0, regs); - int vendorIsIntel = (regs[1] == VENDOR_INTEL_b && - regs[2] == VENDOR_INTEL_c && - regs[3] == VENDOR_INTEL_d); - - x86_cpuid(1, regs); - if ((regs[2] & (1 << 9)) != 0) { - g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSSE3; - } - if ((regs[2] & (1 << 23)) != 0) { - g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_POPCNT; - } - if ((regs[2] & (1 << 19)) != 0) { - g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSE4_1; - } - if ((regs[2] & (1 << 20)) != 0) { - g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSE4_2; - } - if (vendorIsIntel && (regs[2] & (1 << 22)) != 0) { - g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_MOVBE; - } -#endif -#if defined( __mips__) - { /* MIPS and MIPS64 */ - /* Extract the list of CPU features from ELF hwcaps */ - uint32_t hwcaps = 0; - hwcaps = get_elf_hwcap_from_getauxval(AT_HWCAP); - if (hwcaps != 0) { - int has_r6 = (hwcaps & HWCAP_MIPS_R6); - int has_msa = (hwcaps & HWCAP_MIPS_MSA); - if (has_r6) - g_cpuFeatures |= ANDROID_CPU_MIPS_FEATURE_R6; - if (has_msa) - g_cpuFeatures |= ANDROID_CPU_MIPS_FEATURE_MSA; - } - } -#endif /* __mips__ */ - - free(cpuinfo); -} - - -AndroidCpuFamily -android_getCpuFamily(void) -{ - pthread_once(&g_once, android_cpuInit); - return g_cpuFamily; -} - - -uint64_t -android_getCpuFeatures(void) -{ - pthread_once(&g_once, android_cpuInit); - return g_cpuFeatures; -} - - -int -android_getCpuCount(void) -{ - pthread_once(&g_once, android_cpuInit); - return g_cpuCount; -} - -static void -android_cpuInitDummy(void) -{ - g_inited = 1; -} - -int -android_setCpu(int cpu_count, uint64_t cpu_features) -{ - /* Fail if the library was already initialized. */ - if (g_inited) - return 0; - - android_cpuInitFamily(); - g_cpuCount = (cpu_count <= 0 ? 1 : cpu_count); - g_cpuFeatures = cpu_features; - pthread_once(&g_once, android_cpuInitDummy); - - return 1; -} - -#ifdef __arm__ -uint32_t -android_getCpuIdArm(void) -{ - pthread_once(&g_once, android_cpuInit); - return g_cpuIdArm; -} - -int -android_setCpuArm(int cpu_count, uint64_t cpu_features, uint32_t cpu_id) -{ - if (!android_setCpu(cpu_count, cpu_features)) - return 0; - - g_cpuIdArm = cpu_id; - return 1; -} -#endif /* __arm__ */ - -/* - * Technical note: Making sense of ARM's FPU architecture versions. - * - * FPA was ARM's first attempt at an FPU architecture. There is no Android - * device that actually uses it since this technology was already obsolete - * when the project started. If you see references to FPA instructions - * somewhere, you can be sure that this doesn't apply to Android at all. - * - * FPA was followed by "VFP", soon renamed "VFPv1" due to the emergence of - * new versions / additions to it. ARM considers this obsolete right now, - * and no known Android device implements it either. - * - * VFPv2 added a few instructions to VFPv1, and is an *optional* extension - * supported by some ARMv5TE, ARMv6 and ARMv6T2 CPUs. Note that a device - * supporting the 'armeabi' ABI doesn't necessarily support these. - * - * VFPv3-D16 adds a few instructions on top of VFPv2 and is typically used - * on ARMv7-A CPUs which implement a FPU. Note that it is also mandated - * by the Android 'armeabi-v7a' ABI. The -D16 suffix in its name means - * that it provides 16 double-precision FPU registers (d0-d15) and 32 - * single-precision ones (s0-s31) which happen to be mapped to the same - * register banks. - * - * VFPv3-D32 is the name of an extension to VFPv3-D16 that provides 16 - * additional double precision registers (d16-d31). Note that there are - * still only 32 single precision registers. - * - * VFPv3xD is a *subset* of VFPv3-D16 that only provides single-precision - * registers. It is only used on ARMv7-M (i.e. on micro-controllers) which - * are not supported by Android. Note that it is not compatible with VFPv2. - * - * NOTE: The term 'VFPv3' usually designate either VFPv3-D16 or VFPv3-D32 - * depending on context. For example GCC uses it for VFPv3-D32, but - * the Linux kernel code uses it for VFPv3-D16 (especially in - * /proc/cpuinfo). Always try to use the full designation when - * possible. - * - * NEON, a.k.a. "ARM Advanced SIMD" is an extension that provides - * instructions to perform parallel computations on vectors of 8, 16, - * 32, 64 and 128 bit quantities. NEON requires VFPv32-D32 since all - * NEON registers are also mapped to the same register banks. - * - * VFPv4-D16, adds a few instructions on top of VFPv3-D16 in order to - * perform fused multiply-accumulate on VFP registers, as well as - * half-precision (16-bit) conversion operations. - * - * VFPv4-D32 is VFPv4-D16 with 32, instead of 16, FPU double precision - * registers. - * - * VPFv4-NEON is VFPv4-D32 with NEON instructions. It also adds fused - * multiply-accumulate instructions that work on the NEON registers. - * - * NOTE: Similarly, "VFPv4" might either reference VFPv4-D16 or VFPv4-D32 - * depending on context. - * - * The following information was determined by scanning the binutils-2.22 - * sources: - * - * Basic VFP instruction subsets: - * - * #define FPU_VFP_EXT_V1xD 0x08000000 // Base VFP instruction set. - * #define FPU_VFP_EXT_V1 0x04000000 // Double-precision insns. - * #define FPU_VFP_EXT_V2 0x02000000 // ARM10E VFPr1. - * #define FPU_VFP_EXT_V3xD 0x01000000 // VFPv3 single-precision. - * #define FPU_VFP_EXT_V3 0x00800000 // VFPv3 double-precision. - * #define FPU_NEON_EXT_V1 0x00400000 // Neon (SIMD) insns. - * #define FPU_VFP_EXT_D32 0x00200000 // Registers D16-D31. - * #define FPU_VFP_EXT_FP16 0x00100000 // Half-precision extensions. - * #define FPU_NEON_EXT_FMA 0x00080000 // Neon fused multiply-add - * #define FPU_VFP_EXT_FMA 0x00040000 // VFP fused multiply-add - * - * FPU types (excluding NEON) - * - * FPU_VFP_V1xD (EXT_V1xD) - * | - * +--------------------------+ - * | | - * FPU_VFP_V1 (+EXT_V1) FPU_VFP_V3xD (+EXT_V2+EXT_V3xD) - * | | - * | | - * FPU_VFP_V2 (+EXT_V2) FPU_VFP_V4_SP_D16 (+EXT_FP16+EXT_FMA) - * | - * FPU_VFP_V3D16 (+EXT_Vx3D+EXT_V3) - * | - * +--------------------------+ - * | | - * FPU_VFP_V3 (+EXT_D32) FPU_VFP_V4D16 (+EXT_FP16+EXT_FMA) - * | | - * | FPU_VFP_V4 (+EXT_D32) - * | - * FPU_VFP_HARD (+EXT_FMA+NEON_EXT_FMA) - * - * VFP architectures: - * - * ARCH_VFP_V1xD (EXT_V1xD) - * | - * +------------------+ - * | | - * | ARCH_VFP_V3xD (+EXT_V2+EXT_V3xD) - * | | - * | ARCH_VFP_V3xD_FP16 (+EXT_FP16) - * | | - * | ARCH_VFP_V4_SP_D16 (+EXT_FMA) - * | - * ARCH_VFP_V1 (+EXT_V1) - * | - * ARCH_VFP_V2 (+EXT_V2) - * | - * ARCH_VFP_V3D16 (+EXT_V3xD+EXT_V3) - * | - * +-------------------+ - * | | - * | ARCH_VFP_V3D16_FP16 (+EXT_FP16) - * | - * +-------------------+ - * | | - * | ARCH_VFP_V4_D16 (+EXT_FP16+EXT_FMA) - * | | - * | ARCH_VFP_V4 (+EXT_D32) - * | | - * | ARCH_NEON_VFP_V4 (+EXT_NEON+EXT_NEON_FMA) - * | - * ARCH_VFP_V3 (+EXT_D32) - * | - * +-------------------+ - * | | - * | ARCH_VFP_V3_FP16 (+EXT_FP16) - * | - * ARCH_VFP_V3_PLUS_NEON_V1 (+EXT_NEON) - * | - * ARCH_NEON_FP16 (+EXT_FP16) - * - * -fpu= values and their correspondance with FPU architectures above: - * - * {"vfp", FPU_ARCH_VFP_V2}, - * {"vfp9", FPU_ARCH_VFP_V2}, - * {"vfp3", FPU_ARCH_VFP_V3}, // For backwards compatbility. - * {"vfp10", FPU_ARCH_VFP_V2}, - * {"vfp10-r0", FPU_ARCH_VFP_V1}, - * {"vfpxd", FPU_ARCH_VFP_V1xD}, - * {"vfpv2", FPU_ARCH_VFP_V2}, - * {"vfpv3", FPU_ARCH_VFP_V3}, - * {"vfpv3-fp16", FPU_ARCH_VFP_V3_FP16}, - * {"vfpv3-d16", FPU_ARCH_VFP_V3D16}, - * {"vfpv3-d16-fp16", FPU_ARCH_VFP_V3D16_FP16}, - * {"vfpv3xd", FPU_ARCH_VFP_V3xD}, - * {"vfpv3xd-fp16", FPU_ARCH_VFP_V3xD_FP16}, - * {"neon", FPU_ARCH_VFP_V3_PLUS_NEON_V1}, - * {"neon-fp16", FPU_ARCH_NEON_FP16}, - * {"vfpv4", FPU_ARCH_VFP_V4}, - * {"vfpv4-d16", FPU_ARCH_VFP_V4D16}, - * {"fpv4-sp-d16", FPU_ARCH_VFP_V4_SP_D16}, - * {"neon-vfpv4", FPU_ARCH_NEON_VFP_V4}, - * - * - * Simplified diagram that only includes FPUs supported by Android: - * Only ARCH_VFP_V3D16 is actually mandated by the armeabi-v7a ABI, - * all others are optional and must be probed at runtime. - * - * ARCH_VFP_V3D16 (EXT_V1xD+EXT_V1+EXT_V2+EXT_V3xD+EXT_V3) - * | - * +-------------------+ - * | | - * | ARCH_VFP_V3D16_FP16 (+EXT_FP16) - * | - * +-------------------+ - * | | - * | ARCH_VFP_V4_D16 (+EXT_FP16+EXT_FMA) - * | | - * | ARCH_VFP_V4 (+EXT_D32) - * | | - * | ARCH_NEON_VFP_V4 (+EXT_NEON+EXT_NEON_FMA) - * | - * ARCH_VFP_V3 (+EXT_D32) - * | - * +-------------------+ - * | | - * | ARCH_VFP_V3_FP16 (+EXT_FP16) - * | - * ARCH_VFP_V3_PLUS_NEON_V1 (+EXT_NEON) - * | - * ARCH_NEON_FP16 (+EXT_FP16) - * - */ diff --git a/deps/FastNoiseSIMD/ARM/cpu-features.h b/deps/FastNoiseSIMD/ARM/cpu-features.h deleted file mode 100644 index c16d074..0000000 --- a/deps/FastNoiseSIMD/ARM/cpu-features.h +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -#ifndef CPU_FEATURES_H -#define CPU_FEATURES_H - -#include -#include - -__BEGIN_DECLS - -/* A list of valid values returned by android_getCpuFamily(). - * They describe the CPU Architecture of the current process. - */ -typedef enum { - ANDROID_CPU_FAMILY_UNKNOWN = 0, - ANDROID_CPU_FAMILY_ARM, - ANDROID_CPU_FAMILY_X86, - ANDROID_CPU_FAMILY_MIPS, - ANDROID_CPU_FAMILY_ARM64, - ANDROID_CPU_FAMILY_X86_64, - ANDROID_CPU_FAMILY_MIPS64, - - ANDROID_CPU_FAMILY_MAX /* do not remove */ - -} AndroidCpuFamily; - -/* Return the CPU family of the current process. - * - * Note that this matches the bitness of the current process. I.e. when - * running a 32-bit binary on a 64-bit capable CPU, this will return the - * 32-bit CPU family value. - */ -extern AndroidCpuFamily android_getCpuFamily(void); - -/* Return a bitmap describing a set of optional CPU features that are - * supported by the current device's CPU. The exact bit-flags returned - * depend on the value returned by android_getCpuFamily(). See the - * documentation for the ANDROID_CPU_*_FEATURE_* flags below for details. - */ -extern uint64_t android_getCpuFeatures(void); - -/* The list of feature flags for ANDROID_CPU_FAMILY_ARM that can be - * recognized by the library (see note below for 64-bit ARM). Value details - * are: - * - * VFPv2: - * CPU supports the VFPv2 instruction set. Many, but not all, ARMv6 CPUs - * support these instructions. VFPv2 is a subset of VFPv3 so this will - * be set whenever VFPv3 is set too. - * - * ARMv7: - * CPU supports the ARMv7-A basic instruction set. - * This feature is mandated by the 'armeabi-v7a' ABI. - * - * VFPv3: - * CPU supports the VFPv3-D16 instruction set, providing hardware FPU - * support for single and double precision floating point registers. - * Note that only 16 FPU registers are available by default, unless - * the D32 bit is set too. This feature is also mandated by the - * 'armeabi-v7a' ABI. - * - * VFP_D32: - * CPU VFP optional extension that provides 32 FPU registers, - * instead of 16. Note that ARM mandates this feature is the 'NEON' - * feature is implemented by the CPU. - * - * NEON: - * CPU FPU supports "ARM Advanced SIMD" instructions, also known as - * NEON. Note that this mandates the VFP_D32 feature as well, per the - * ARM Architecture specification. - * - * VFP_FP16: - * Half-width floating precision VFP extension. If set, the CPU - * supports instructions to perform floating-point operations on - * 16-bit registers. This is part of the VFPv4 specification, but - * not mandated by any Android ABI. - * - * VFP_FMA: - * Fused multiply-accumulate VFP instructions extension. Also part of - * the VFPv4 specification, but not mandated by any Android ABI. - * - * NEON_FMA: - * Fused multiply-accumulate NEON instructions extension. Optional - * extension from the VFPv4 specification, but not mandated by any - * Android ABI. - * - * IDIV_ARM: - * Integer division available in ARM mode. Only available - * on recent CPUs (e.g. Cortex-A15). - * - * IDIV_THUMB2: - * Integer division available in Thumb-2 mode. Only available - * on recent CPUs (e.g. Cortex-A15). - * - * iWMMXt: - * Optional extension that adds MMX registers and operations to an - * ARM CPU. This is only available on a few XScale-based CPU designs - * sold by Marvell. Pretty rare in practice. - * - * AES: - * CPU supports AES instructions. These instructions are only - * available for 32-bit applications running on ARMv8 CPU. - * - * CRC32: - * CPU supports CRC32 instructions. These instructions are only - * available for 32-bit applications running on ARMv8 CPU. - * - * SHA2: - * CPU supports SHA2 instructions. These instructions are only - * available for 32-bit applications running on ARMv8 CPU. - * - * SHA1: - * CPU supports SHA1 instructions. These instructions are only - * available for 32-bit applications running on ARMv8 CPU. - * - * PMULL: - * CPU supports 64-bit PMULL and PMULL2 instructions. These - * instructions are only available for 32-bit applications - * running on ARMv8 CPU. - * - * If you want to tell the compiler to generate code that targets one of - * the feature set above, you should probably use one of the following - * flags (for more details, see technical note at the end of this file): - * - * -mfpu=vfp - * -mfpu=vfpv2 - * These are equivalent and tell GCC to use VFPv2 instructions for - * floating-point operations. Use this if you want your code to - * run on *some* ARMv6 devices, and any ARMv7-A device supported - * by Android. - * - * Generated code requires VFPv2 feature. - * - * -mfpu=vfpv3-d16 - * Tell GCC to use VFPv3 instructions (using only 16 FPU registers). - * This should be generic code that runs on any CPU that supports the - * 'armeabi-v7a' Android ABI. Note that no ARMv6 CPU supports this. - * - * Generated code requires VFPv3 feature. - * - * -mfpu=vfpv3 - * Tell GCC to use VFPv3 instructions with 32 FPU registers. - * Generated code requires VFPv3|VFP_D32 features. - * - * -mfpu=neon - * Tell GCC to use VFPv3 instructions with 32 FPU registers, and - * also support NEON intrinsics (see ). - * Generated code requires VFPv3|VFP_D32|NEON features. - * - * -mfpu=vfpv4-d16 - * Generated code requires VFPv3|VFP_FP16|VFP_FMA features. - * - * -mfpu=vfpv4 - * Generated code requires VFPv3|VFP_FP16|VFP_FMA|VFP_D32 features. - * - * -mfpu=neon-vfpv4 - * Generated code requires VFPv3|VFP_FP16|VFP_FMA|VFP_D32|NEON|NEON_FMA - * features. - * - * -mcpu=cortex-a7 - * -mcpu=cortex-a15 - * Generated code requires VFPv3|VFP_FP16|VFP_FMA|VFP_D32| - * NEON|NEON_FMA|IDIV_ARM|IDIV_THUMB2 - * This flag implies -mfpu=neon-vfpv4. - * - * -mcpu=iwmmxt - * Allows the use of iWMMXt instrinsics with GCC. - * - * IMPORTANT NOTE: These flags should only be tested when - * android_getCpuFamily() returns ANDROID_CPU_FAMILY_ARM, i.e. this is a - * 32-bit process. - * - * When running a 64-bit ARM process on an ARMv8 CPU, - * android_getCpuFeatures() will return a different set of bitflags - */ -enum { - ANDROID_CPU_ARM_FEATURE_ARMv7 = (1 << 0), - ANDROID_CPU_ARM_FEATURE_VFPv3 = (1 << 1), - ANDROID_CPU_ARM_FEATURE_NEON = (1 << 2), - ANDROID_CPU_ARM_FEATURE_LDREX_STREX = (1 << 3), - ANDROID_CPU_ARM_FEATURE_VFPv2 = (1 << 4), - ANDROID_CPU_ARM_FEATURE_VFP_D32 = (1 << 5), - ANDROID_CPU_ARM_FEATURE_VFP_FP16 = (1 << 6), - ANDROID_CPU_ARM_FEATURE_VFP_FMA = (1 << 7), - ANDROID_CPU_ARM_FEATURE_NEON_FMA = (1 << 8), - ANDROID_CPU_ARM_FEATURE_IDIV_ARM = (1 << 9), - ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2 = (1 << 10), - ANDROID_CPU_ARM_FEATURE_iWMMXt = (1 << 11), - ANDROID_CPU_ARM_FEATURE_AES = (1 << 12), - ANDROID_CPU_ARM_FEATURE_PMULL = (1 << 13), - ANDROID_CPU_ARM_FEATURE_SHA1 = (1 << 14), - ANDROID_CPU_ARM_FEATURE_SHA2 = (1 << 15), - ANDROID_CPU_ARM_FEATURE_CRC32 = (1 << 16), -}; - -/* The bit flags corresponding to the output of android_getCpuFeatures() - * when android_getCpuFamily() returns ANDROID_CPU_FAMILY_ARM64. Value details - * are: - * - * FP: - * CPU has Floating-point unit. - * - * ASIMD: - * CPU has Advanced SIMD unit. - * - * AES: - * CPU supports AES instructions. - * - * CRC32: - * CPU supports CRC32 instructions. - * - * SHA2: - * CPU supports SHA2 instructions. - * - * SHA1: - * CPU supports SHA1 instructions. - * - * PMULL: - * CPU supports 64-bit PMULL and PMULL2 instructions. - */ -enum { - ANDROID_CPU_ARM64_FEATURE_FP = (1 << 0), - ANDROID_CPU_ARM64_FEATURE_ASIMD = (1 << 1), - ANDROID_CPU_ARM64_FEATURE_AES = (1 << 2), - ANDROID_CPU_ARM64_FEATURE_PMULL = (1 << 3), - ANDROID_CPU_ARM64_FEATURE_SHA1 = (1 << 4), - ANDROID_CPU_ARM64_FEATURE_SHA2 = (1 << 5), - ANDROID_CPU_ARM64_FEATURE_CRC32 = (1 << 6), -}; - -/* The bit flags corresponding to the output of android_getCpuFeatures() - * when android_getCpuFamily() returns ANDROID_CPU_FAMILY_X86 or - * ANDROID_CPU_FAMILY_X86_64. - */ -enum { - ANDROID_CPU_X86_FEATURE_SSSE3 = (1 << 0), - ANDROID_CPU_X86_FEATURE_POPCNT = (1 << 1), - ANDROID_CPU_X86_FEATURE_MOVBE = (1 << 2), - ANDROID_CPU_X86_FEATURE_SSE4_1 = (1 << 3), - ANDROID_CPU_X86_FEATURE_SSE4_2 = (1 << 4), -}; - -/* The bit flags corresponding to the output of android_getCpuFeatures() - * when android_getCpuFamily() returns ANDROID_CPU_FAMILY_MIPS - * or ANDROID_CPU_FAMILY_MIPS64. Values are: - * - * R6: - * CPU executes MIPS Release 6 instructions natively, and - * supports obsoleted R1..R5 instructions only via kernel traps. - * - * MSA: - * CPU supports Mips SIMD Architecture instructions. - */ -enum { - ANDROID_CPU_MIPS_FEATURE_R6 = (1 << 0), - ANDROID_CPU_MIPS_FEATURE_MSA = (1 << 1), -}; - - -/* Return the number of CPU cores detected on this device. */ -extern int android_getCpuCount(void); - -/* The following is used to force the CPU count and features - * mask in sandboxed processes. Under 4.1 and higher, these processes - * cannot access /proc, which is the only way to get information from - * the kernel about the current hardware (at least on ARM). - * - * It _must_ be called only once, and before any android_getCpuXXX - * function, any other case will fail. - * - * This function return 1 on success, and 0 on failure. - */ -extern int android_setCpu(int cpu_count, - uint64_t cpu_features); - -#ifdef __arm__ -/* Retrieve the ARM 32-bit CPUID value from the kernel. - * Note that this cannot work on sandboxed processes under 4.1 and - * higher, unless you called android_setCpuArm() before. - */ -extern uint32_t android_getCpuIdArm(void); - -/* An ARM-specific variant of android_setCpu() that also allows you - * to set the ARM CPUID field. - */ -extern int android_setCpuArm(int cpu_count, - uint64_t cpu_features, - uint32_t cpu_id); -#endif - -__END_DECLS - -#endif /* CPU_FEATURES_H */ diff --git a/deps/FastNoiseSIMD/FastNoiseSIMD.cpp b/deps/FastNoiseSIMD/FastNoiseSIMD.cpp deleted file mode 100644 index cbac32a..0000000 --- a/deps/FastNoiseSIMD/FastNoiseSIMD.cpp +++ /dev/null @@ -1,556 +0,0 @@ -// FastNoiseSIMD.cpp -// -// MIT License -// -// Copyright(c) 2017 Jordan Peck -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files(the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions : -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// The developer's email is jorzixdan.me2@gzixmail.com (for great email, take -// off every 'zix'.) -// - -#include "FastNoiseSIMD.h" -#include -#include -#include -#include - -#ifdef FN_COMPILE_NO_SIMD_FALLBACK -#define SIMD_LEVEL_H FN_NO_SIMD_FALLBACK -#include "FastNoiseSIMD_internal.h" -#endif - -#ifdef FN_COMPILE_SSE2 -#define SIMD_LEVEL_H FN_SSE2 -#include "FastNoiseSIMD_internal.h" -#endif - -#ifdef FN_COMPILE_SSE41 -#define SIMD_LEVEL_H FN_SSE41 -#include "FastNoiseSIMD_internal.h" -#endif - -#ifdef FN_COMPILE_AVX2 -#define SIMD_LEVEL_H FN_AVX2 -#include "FastNoiseSIMD_internal.h" -#endif - -#ifdef FN_COMPILE_AVX512 -#define SIMD_LEVEL_H FN_AVX512 -#include "FastNoiseSIMD_internal.h" -#endif - -#ifdef FN_COMPILE_NEON -#define SIMD_LEVEL_H FN_NEON -#include "FastNoiseSIMD_internal.h" -#endif - -// CPUid -#ifdef _WIN32 -#include -#elif defined(FN_ARM) -#if !defined(__aarch64__) && !defined(FN_IOS) -#include "ARM/cpu-features.h" -#endif -#else -#include -#include "inttypes.h" -#endif - -int FastNoiseSIMD::s_currentSIMDLevel = -1; - -#ifdef FN_ARM -int GetFastestSIMD() -{ -#if defined(__aarch64__) || defined(FN_IOS) - return FN_NEON; -#else - if (android_getCpuFamily() == ANDROID_CPU_FAMILY_ARM) - { - auto cpuFeatures = android_getCpuFeatures(); - - if (cpuFeatures & ANDROID_CPU_ARM_FEATURE_NEON) -#ifdef FN_USE_FMA - if (cpuFeatures & ANDROID_CPU_ARM_FEATURE_NEON_FMA) -#endif - return FN_NEON; - } - - return FN_NO_SIMD_FALLBACK; -#endif -} -#else - -#ifdef _WIN32 -void cpuid(int32_t out[4], int32_t x) { - __cpuidex(out, x, 0); -} -uint64_t xgetbv(unsigned int x) { - return _xgetbv(x); -} -#else -void cpuid(int32_t out[4], int32_t x) { - __cpuid_count(x, 0, out[0], out[1], out[2], out[3]); -} -uint64_t xgetbv(unsigned int index) { - uint32_t eax, edx; - __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index)); - return ((uint64_t)edx << 32) | eax; -} -#define _XCR_XFEATURE_ENABLED_MASK 0 -#endif - -int GetFastestSIMD() -{ - //https://github.com/Mysticial/FeatureDetector - - int cpuInfo[4]; - - cpuid(cpuInfo, 0); - int nIds = cpuInfo[0]; - - if (nIds < 0x00000001) - return FN_NO_SIMD_FALLBACK; - - cpuid(cpuInfo, 0x00000001); - - // SSE2 - if ((cpuInfo[3] & 1 << 26) == 0) - return FN_NO_SIMD_FALLBACK; - - // SSE41 - if ((cpuInfo[2] & 1 << 19) == 0) - return FN_SSE2; - - // AVX - bool cpuXSaveSuport = (cpuInfo[2] & 1 << 26) != 0; - bool osAVXSuport = (cpuInfo[2] & 1 << 27) != 0; - bool cpuAVXSuport = (cpuInfo[2] & 1 << 28) != 0; - - if (cpuXSaveSuport && osAVXSuport && cpuAVXSuport) - { - uint64_t xcrFeatureMask = xgetbv(_XCR_XFEATURE_ENABLED_MASK); - if ((xcrFeatureMask & 0x6) != 0x6) - return FN_SSE41; - } - else - return FN_SSE41; - - // AVX2 FMA3 - if (nIds < 0x00000007) - return FN_SSE41; - -#ifdef FN_USE_FMA - bool cpuFMA3Support = (cpuInfo[2] & 1 << 12) != 0; -#else - bool cpuFMA3Support = true; -#endif - - cpuid(cpuInfo, 0x00000007); - - bool cpuAVX2Support = (cpuInfo[1] & 1 << 5) != 0; - - if (!cpuFMA3Support || !cpuAVX2Support) - return FN_SSE41; - - // AVX512 - bool cpuAVX512Support = (cpuInfo[1] & 1 << 16) != 0; - bool oxAVX512Support = (xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0xe6) == 0xe6; - - if (!cpuAVX512Support || !oxAVX512Support) - return FN_AVX2; - - return FN_AVX512; -} -#endif - -FastNoiseSIMD* FastNoiseSIMD::NewFastNoiseSIMD(int seed) -{ - GetSIMDLevel(); - -#ifdef FN_COMPILE_NEON -#ifdef FN_COMPILE_NO_SIMD_FALLBACK - if (s_currentSIMDLevel >= FN_NEON) -#endif - return new FastNoiseSIMD_internal::FASTNOISE_SIMD_CLASS(FN_NEON)(seed); -#endif - -#ifdef FN_COMPILE_AVX512 - if (s_currentSIMDLevel >= FN_AVX512) - return new FastNoiseSIMD_internal::FASTNOISE_SIMD_CLASS(FN_AVX512)(seed); -#endif - -#ifdef FN_COMPILE_AVX2 - if (s_currentSIMDLevel >= FN_AVX2) - return new FastNoiseSIMD_internal::FASTNOISE_SIMD_CLASS(FN_AVX2)(seed); -#endif - -#ifdef FN_COMPILE_SSE41 - if (s_currentSIMDLevel >= FN_SSE41) - return new FastNoiseSIMD_internal::FASTNOISE_SIMD_CLASS(FN_SSE41)(seed); -#endif - -#ifdef FN_COMPILE_SSE2 -#ifdef FN_COMPILE_NO_SIMD_FALLBACK - if (s_currentSIMDLevel >= FN_SSE2) -#endif - return new FastNoiseSIMD_internal::FASTNOISE_SIMD_CLASS(FN_SSE2)(seed); -#endif - -#ifdef FN_COMPILE_NO_SIMD_FALLBACK - return new FastNoiseSIMD_internal::FASTNOISE_SIMD_CLASS(FN_NO_SIMD_FALLBACK)(seed); -#endif -} - -int FastNoiseSIMD::GetSIMDLevel() -{ - if (s_currentSIMDLevel < 0) - s_currentSIMDLevel = GetFastestSIMD(); - - return s_currentSIMDLevel; -} - -void FastNoiseSIMD::FreeNoiseSet(float* floatArray) -{ -#ifdef FN_ALIGNED_SETS - GetSIMDLevel(); - - if (s_currentSIMDLevel > FN_NO_SIMD_FALLBACK) -#ifdef _WIN32 - _aligned_free(floatArray); -#else - free(floatArray); -#endif - else -#endif - delete[] floatArray; -} - -int FastNoiseSIMD::AlignedSize(int size) -{ -#ifdef FN_ALIGNED_SETS - GetSIMDLevel(); - -#ifdef FN_COMPILE_NEON - if (s_currentSIMDLevel >= FN_NEON) - return FastNoiseSIMD_internal::FASTNOISE_SIMD_CLASS(FN_NEON)::AlignedSize(size); -#endif - -#ifdef FN_COMPILE_AVX512 - if (s_currentSIMDLevel >= FN_AVX512) - return FastNoiseSIMD_internal::FASTNOISE_SIMD_CLASS(FN_AVX512)::AlignedSize(size); -#endif - -#ifdef FN_COMPILE_AVX2 - if (s_currentSIMDLevel >= FN_AVX2) - return FastNoiseSIMD_internal::FASTNOISE_SIMD_CLASS(FN_AVX2)::AlignedSize(size); -#endif - -#ifdef FN_COMPILE_SSE2 - if (s_currentSIMDLevel >= FN_SSE2) - return FastNoiseSIMD_internal::FASTNOISE_SIMD_CLASS(FN_SSE2)::AlignedSize(size); -#endif -#endif - return size; -} - -float* FastNoiseSIMD::GetEmptySet(int size) -{ -#ifdef FN_ALIGNED_SETS - GetSIMDLevel(); - -#ifdef FN_COMPILE_NEON - if (s_currentSIMDLevel >= FN_NEON) - return FastNoiseSIMD_internal::FASTNOISE_SIMD_CLASS(FN_NEON)::GetEmptySet(size); -#endif - -#ifdef FN_COMPILE_AVX512 - if (s_currentSIMDLevel >= FN_AVX512) - return FastNoiseSIMD_internal::FASTNOISE_SIMD_CLASS(FN_AVX512)::GetEmptySet(size); -#endif - -#ifdef FN_COMPILE_AVX2 - if (s_currentSIMDLevel >= FN_AVX2) - return FastNoiseSIMD_internal::FASTNOISE_SIMD_CLASS(FN_AVX2)::GetEmptySet(size); -#endif - -#ifdef FN_COMPILE_SSE2 - if (s_currentSIMDLevel >= FN_SSE2) - return FastNoiseSIMD_internal::FASTNOISE_SIMD_CLASS(FN_SSE2)::GetEmptySet(size); -#endif -#endif - return new float[size]; -} - -FastNoiseVectorSet* FastNoiseSIMD::GetVectorSet(int xSize, int ySize, int zSize) -{ - FastNoiseVectorSet* vectorSet = new FastNoiseVectorSet(); - FillVectorSet(vectorSet, xSize, ySize, zSize); - return vectorSet; -} - -void FastNoiseSIMD::FillVectorSet(FastNoiseVectorSet* vectorSet, int xSize, int ySize, int zSize) -{ - assert(vectorSet); - - vectorSet->SetSize(xSize*ySize*zSize); - vectorSet->sampleScale = 0; - - int index = 0; - - for (int ix = 0; ix < xSize; ix++) - { - for (int iy = 0; iy < ySize; iy++) - { - for (int iz = 0; iz < zSize; iz++) - { - vectorSet->xSet[index] = float(ix); - vectorSet->ySet[index] = float(iy); - vectorSet->zSet[index] = float(iz); - index++; - } - } - } -} - -FastNoiseVectorSet* FastNoiseSIMD::GetSamplingVectorSet(int sampleScale, int xSize, int ySize, int zSize) -{ - FastNoiseVectorSet* vectorSet = new FastNoiseVectorSet(); - FillSamplingVectorSet(vectorSet, sampleScale, xSize, ySize, zSize); - return vectorSet; -} - -void FastNoiseSIMD::FillSamplingVectorSet(FastNoiseVectorSet* vectorSet, int sampleScale, int xSize, int ySize, int zSize) -{ - assert(vectorSet); - - if (sampleScale <= 0) - { - FillVectorSet(vectorSet, xSize, ySize, zSize); - return; - } - - vectorSet->sampleSizeX = xSize; - vectorSet->sampleSizeY = ySize; - vectorSet->sampleSizeZ = zSize; - - int sampleSize = 1 << sampleScale; - int sampleMask = sampleSize - 1; - - int xSizeSample = xSize; - int ySizeSample = ySize; - int zSizeSample = zSize; - - if (xSizeSample & sampleMask) - xSizeSample = (xSizeSample & ~sampleMask) + sampleSize; - - if (ySizeSample & sampleMask) - ySizeSample = (ySizeSample & ~sampleMask) + sampleSize; - - if (zSizeSample & sampleMask) - zSizeSample = (zSizeSample & ~sampleMask) + sampleSize; - - xSizeSample = (xSizeSample >> sampleScale) + 1; - ySizeSample = (ySizeSample >> sampleScale) + 1; - zSizeSample = (zSizeSample >> sampleScale) + 1; - - vectorSet->SetSize(xSizeSample*ySizeSample*zSizeSample); - vectorSet->sampleScale = sampleScale; - - int index = 0; - - for (int ix = 0; ix < xSizeSample; ix++) - { - for (int iy = 0; iy < ySizeSample; iy++) - { - for (int iz = 0; iz < zSizeSample; iz++) - { - vectorSet->xSet[index] = float(ix*sampleSize); - vectorSet->ySet[index] = float(iy*sampleSize); - vectorSet->zSet[index] = float(iz*sampleSize); - index++; - } - } - } -} - -float* FastNoiseSIMD::GetNoiseSet(int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier) -{ - float* noiseSet = GetEmptySet(xSize, ySize, zSize); - - FillNoiseSet(noiseSet, xStart, yStart, zStart, xSize, ySize, zSize, scaleModifier); - - return noiseSet; -} - -void FastNoiseSIMD::FillNoiseSet(float* noiseSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier) -{ - switch (m_noiseType) - { - case Value: - FillValueSet(noiseSet, xStart, yStart, zStart, xSize, ySize, zSize, scaleModifier); - break; - case ValueFractal: - FillValueFractalSet(noiseSet, xStart, yStart, zStart, xSize, ySize, zSize, scaleModifier); - break; - case Perlin: - FillPerlinSet(noiseSet, xStart, yStart, zStart, xSize, ySize, zSize, scaleModifier); - break; - case PerlinFractal: - FillPerlinFractalSet(noiseSet, xStart, yStart, zStart, xSize, ySize, zSize, scaleModifier); - break; - case Simplex: - FillSimplexSet(noiseSet, xStart, yStart, zStart, xSize, ySize, zSize, scaleModifier); - break; - case SimplexFractal: - FillSimplexFractalSet(noiseSet, xStart, yStart, zStart, xSize, ySize, zSize, scaleModifier); - break; - case WhiteNoise: - FillWhiteNoiseSet(noiseSet, xStart, yStart, zStart, xSize, ySize, zSize, scaleModifier); - break; - case Cellular: - FillCellularSet(noiseSet, xStart, yStart, zStart, xSize, ySize, zSize, scaleModifier); - break; - case Cubic: - FillCubicSet(noiseSet, xStart, yStart, zStart, xSize, ySize, zSize, scaleModifier); - break; - case CubicFractal: - FillCubicFractalSet(noiseSet, xStart, yStart, zStart, xSize, ySize, zSize, scaleModifier); - break; - default: - break; - } -} - -void FastNoiseSIMD::FillNoiseSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset, float yOffset, float zOffset) -{ - switch (m_noiseType) - { - case Value: - FillValueSet(noiseSet, vectorSet, xOffset, yOffset, zOffset); - break; - case ValueFractal: - FillValueFractalSet(noiseSet, vectorSet, xOffset, yOffset, zOffset); - break; - case Perlin: - FillPerlinSet(noiseSet, vectorSet, xOffset, yOffset, zOffset); - break; - case PerlinFractal: - FillPerlinFractalSet(noiseSet, vectorSet, xOffset, yOffset, zOffset); - break; - case Simplex: - FillSimplexSet(noiseSet, vectorSet, xOffset, yOffset, zOffset); - break; - case SimplexFractal: - FillSimplexFractalSet(noiseSet, vectorSet, xOffset, yOffset, zOffset); - break; - case WhiteNoise: - FillWhiteNoiseSet(noiseSet, vectorSet, xOffset, yOffset, zOffset); - break; - case Cellular: - FillCellularSet(noiseSet, vectorSet, xOffset, yOffset, zOffset); - break; - case Cubic: - FillCubicSet(noiseSet, vectorSet, xOffset, yOffset, zOffset); - break; - case CubicFractal: - FillCubicFractalSet(noiseSet, vectorSet, xOffset, yOffset, zOffset); - break; - default: - break; - } -} - -float* FastNoiseSIMD::GetSampledNoiseSet(int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, int sampleScale) -{ - float* noiseSet = GetEmptySet(xSize, ySize, zSize); - - FillSampledNoiseSet(noiseSet, xStart, yStart, zStart, xSize, ySize, zSize, sampleScale); - - return noiseSet; -} - -#define GET_SET(f) \ -float* FastNoiseSIMD::Get##f##Set(int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier)\ -{\ - float* noiseSet = GetEmptySet(xSize, ySize, zSize);\ - \ - Fill##f##Set(noiseSet, xStart, yStart, zStart, xSize, ySize, zSize, scaleModifier);\ - \ - return noiseSet;\ -} - -GET_SET(WhiteNoise) - -GET_SET(Value) -GET_SET(ValueFractal) - -GET_SET(Perlin) -GET_SET(PerlinFractal) - -GET_SET(Simplex) -GET_SET(SimplexFractal) - -GET_SET(Cellular) - -GET_SET(Cubic) -GET_SET(CubicFractal) - -float FastNoiseSIMD::CalculateFractalBounding(int octaves, float gain) -{ - float amp = gain; - float ampFractal = 1.0f; - for (int i = 1; i < octaves; i++) - { - ampFractal += amp; - amp *= gain; - } - return 1.0f / ampFractal; -} - -void FastNoiseSIMD::SetCellularDistance2Indicies(int cellularDistanceIndex0, int cellularDistanceIndex1) -{ - m_cellularDistanceIndex0 = std::min(cellularDistanceIndex0, cellularDistanceIndex1); - m_cellularDistanceIndex1 = std::max(cellularDistanceIndex0, cellularDistanceIndex1); - - m_cellularDistanceIndex0 = std::min(std::max(m_cellularDistanceIndex0, 0), FN_CELLULAR_INDEX_MAX); - m_cellularDistanceIndex1 = std::min(std::max(m_cellularDistanceIndex1, 0), FN_CELLULAR_INDEX_MAX); -} - -void FastNoiseVectorSet::Free() -{ - size = -1; - FastNoiseSIMD::FreeNoiseSet(xSet); - xSet = nullptr; - ySet = nullptr; - zSet = nullptr; -} - -void FastNoiseVectorSet::SetSize(int _size) -{ - Free(); - size = _size; - - int alignedSize = FastNoiseSIMD::AlignedSize(size); - - xSet = FastNoiseSIMD::GetEmptySet(alignedSize * 3); - ySet = xSet + alignedSize; - zSet = ySet + alignedSize; -} diff --git a/deps/FastNoiseSIMD/FastNoiseSIMD.h b/deps/FastNoiseSIMD/FastNoiseSIMD.h deleted file mode 100644 index 3918eaf..0000000 --- a/deps/FastNoiseSIMD/FastNoiseSIMD.h +++ /dev/null @@ -1,365 +0,0 @@ -// FastNoiseSIMD.h -// -// MIT License -// -// Copyright(c) 2017 Jordan Peck -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files(the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions : -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// The developer's email is jorzixdan.me2@gzixmail.com (for great email, take -// off every 'zix'.) -// - -// VERSION: 0.7.0 - -#ifndef FASTNOISE_SIMD_H -#define FASTNOISE_SIMD_H - -#if defined(__arm__) || defined(__aarch64__) -#define FN_ARM -//#define FN_IOS -#define FN_COMPILE_NEON -#else - -// Comment out lines to not compile for certain instruction sets -#define FN_COMPILE_SSE2 -#define FN_COMPILE_SSE41 - -// To compile AVX2 set C++ code generation to use /arch:AVX(2) on FastNoiseSIMD_avx2.cpp -// Note: This does not break support for pre AVX CPUs, AVX code is only run if support is detected -// #define FN_COMPILE_AVX2 - -// Only the latest compilers will support this -// #define FN_COMPILE_AVX512 - -// Using FMA instructions with AVX(51)2/NEON provides a small performance increase but can cause -// minute variations in noise output compared to other SIMD levels due to higher calculation precision -// Intel compiler will always generate FMA instructions, use /Qfma- or -no-fma to disable -#define FN_USE_FMA -#endif - -// Using aligned sets of memory for float arrays allows faster storing of SIMD data -// Comment out to allow unaligned float arrays to be used as sets -#define FN_ALIGNED_SETS - -// SSE2/NEON support is guaranteed on 64bit CPUs so no fallback is needed -#if !(defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__) || defined(__aarch64__) || defined(FN_IOS)) || defined(_DEBUG) -#define FN_COMPILE_NO_SIMD_FALLBACK -#endif - -/* -Tested Compilers: --MSVC v120/v140 --Intel 16.0 --GCC 4.7 Linux --Clang MacOSX - -CPU instruction support: - -SSE2 -Intel Pentium 4 - 2001 -AMD Opteron/Athlon - 2003 - -SEE4.1 -Intel Penryn - 2007 -AMD Bulldozer - Q4 2011 - -AVX -Intel Sandy Bridge - Q1 2011 -AMD Bulldozer - Q4 2011 - -AVX2 -Intel Haswell - Q2 2013 -AMD Carrizo - Q2 2015 - -FMA3 -Intel Haswell - Q2 2013 -AMD Piledriver - 2012 - -AVX-512F -Intel Skylake-X - Q2 2017 -*/ - -struct FastNoiseVectorSet; - -class FastNoiseSIMD -{ -public: - - enum NoiseType { Value, ValueFractal, Perlin, PerlinFractal, Simplex, SimplexFractal, WhiteNoise, Cellular, Cubic, CubicFractal }; - enum FractalType { FBM, Billow, RigidMulti }; - enum PerturbType { None, Gradient, GradientFractal, Normalise, Gradient_Normalise, GradientFractal_Normalise }; - - enum CellularDistanceFunction { Euclidean, Manhattan, Natural }; - enum CellularReturnType { CellValue, Distance, Distance2, Distance2Add, Distance2Sub, Distance2Mul, Distance2Div, NoiseLookup, Distance2Cave }; - - // Creates new FastNoiseSIMD for the highest supported instuction set of the CPU - static FastNoiseSIMD* NewFastNoiseSIMD(int seed = 1337); - - // Returns highest detected level of CPU support - // 5: ARM NEON - // 4: AVX-512F - // 3: AVX2 & FMA3 - // 2: SSE4.1 - // 1: SSE2 - // 0: Fallback, no SIMD support - static int GetSIMDLevel(void); - - // Sets the SIMD level for newly created FastNoiseSIMD objects - // 5: ARM NEON - // 4: AVX-512F - // 3: AVX2 & FMA3 - // 2: SSE4.1 - // 1: SSE2 - // 0: Fallback, no SIMD support - // -1: Auto-detect fastest supported (Default) - // Caution: Setting this manually can cause crashes on CPUs that do not support that level - // Caution: Changing this after creating FastNoiseSIMD objects has undefined behaviour - static void SetSIMDLevel(int level) { s_currentSIMDLevel = level; } - - // Free a noise set from memory - static void FreeNoiseSet(float* noiseSet); - - // Create an empty (aligned) noise set for use with FillNoiseSet() - static float* GetEmptySet(int size); - - // Create an empty (aligned) noise set for use with FillNoiseSet() - static float* GetEmptySet(int xSize, int ySize, int zSize) { return GetEmptySet(xSize*ySize*zSize); } - - // Rounds the size up to the nearest aligned size for the current SIMD level - static int AlignedSize(int size); - - - // Returns seed used for all noise types - int GetSeed(void) const { return m_seed; } - - // Sets seed used for all noise types - // Default: 1337 - void SetSeed(int seed) { m_seed = seed; } - - // Sets frequency for all noise types - // Default: 0.01 - void SetFrequency(float frequency) { m_frequency = frequency; } - - // Sets noise return type of (Get/Fill)NoiseSet() - // Default: Simplex - void SetNoiseType(NoiseType noiseType) { m_noiseType = noiseType; } - - // Sets scaling factor for individual axis - // Defaults: 1.0 - void SetAxisScales(float xScale, float yScale, float zScale) { m_xScale = xScale; m_yScale = yScale; m_zScale = zScale; } - - - // Sets octave count for all fractal noise types - // Default: 3 - void SetFractalOctaves(int octaves) { m_octaves = octaves; m_fractalBounding = CalculateFractalBounding(m_octaves, m_gain); } - - // Sets octave lacunarity for all fractal noise types - // Default: 2.0 - void SetFractalLacunarity(float lacunarity) { m_lacunarity = lacunarity; } - - // Sets octave gain for all fractal noise types - // Default: 0.5 - void SetFractalGain(float gain) { m_gain = gain; m_fractalBounding = CalculateFractalBounding(m_octaves, m_gain); } - - // Sets method for combining octaves in all fractal noise types - // Default: FBM - void SetFractalType(FractalType fractalType) { m_fractalType = fractalType; } - - - // Sets return type from cellular noise calculations - // Default: Distance - void SetCellularReturnType(CellularReturnType cellularReturnType) { m_cellularReturnType = cellularReturnType; } - - // Sets distance function used in cellular noise calculations - // Default: Euclidean - void SetCellularDistanceFunction(CellularDistanceFunction cellularDistanceFunction) { m_cellularDistanceFunction = cellularDistanceFunction; } - - // Sets the type of noise used if cellular return type is set the NoiseLookup - // Default: Simplex - void SetCellularNoiseLookupType(NoiseType cellularNoiseLookupType) { m_cellularNoiseLookupType = cellularNoiseLookupType; } - - // Sets relative frequency on the cellular noise lookup return type - // Default: 0.2 - void SetCellularNoiseLookupFrequency(float cellularNoiseLookupFrequency) { m_cellularNoiseLookupFrequency = cellularNoiseLookupFrequency; } - - // Sets the 2 distance indicies used for distance2 return types - // Default: 0, 1 - // Note: index0 should be lower than index1 - // Both indicies must be >= 0, index1 must be < 4 - void SetCellularDistance2Indicies(int cellularDistanceIndex0, int cellularDistanceIndex1); - - // Sets the maximum distance a cellular point can move from it's grid position - // Setting this high will make artifacts more common - // Default: 0.45 - void SetCellularJitter(float cellularJitter) { m_cellularJitter = cellularJitter; } - - - // Enables position perturbing for all noise types - // Default: None - void SetPerturbType(PerturbType perturbType) { m_perturbType = perturbType; } - - // Sets the maximum distance the input position can be perturbed - // Default: 1.0 - void SetPerturbAmp(float perturbAmp) { m_perturbAmp = perturbAmp / 511.5f; } - - // Set the relative frequency for the perturb gradient - // Default: 0.5 - void SetPerturbFrequency(float perturbFrequency) { m_perturbFrequency = perturbFrequency; } - - - // Sets octave count for perturb fractal types - // Default: 3 - void SetPerturbFractalOctaves(int perturbOctaves) { m_perturbOctaves = perturbOctaves; m_perturbFractalBounding = CalculateFractalBounding(m_perturbOctaves, m_perturbGain); } - - // Sets octave lacunarity for perturb fractal types - // Default: 2.0 - void SetPerturbFractalLacunarity(float perturbLacunarity) { m_perturbLacunarity = perturbLacunarity; } - - // Sets octave gain for perturb fractal types - // Default: 0.5 - void SetPerturbFractalGain(float perturbGain) { m_perturbGain = perturbGain; m_perturbFractalBounding = CalculateFractalBounding(m_perturbOctaves, m_perturbGain); } - - // Sets the length for vectors after perturb normalising - // Default: 1.0 - void SetPerturbNormaliseLength(float perturbNormaliseLength) { m_perturbNormaliseLength = perturbNormaliseLength; } - - - static FastNoiseVectorSet* GetVectorSet(int xSize, int ySize, int zSize); - static FastNoiseVectorSet* GetSamplingVectorSet(int sampleScale, int xSize, int ySize, int zSize); - static void FillVectorSet(FastNoiseVectorSet* vectorSet, int xSize, int ySize, int zSize); - static void FillSamplingVectorSet(FastNoiseVectorSet* vectorSet, int sampleScale, int xSize, int ySize, int zSize); - - float* GetNoiseSet(int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f); - void FillNoiseSet(float* noiseSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f); - void FillNoiseSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f); - - float* GetSampledNoiseSet(int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, int sampleScale); - virtual void FillSampledNoiseSet(float* noiseSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, int sampleScale) = 0; - virtual void FillSampledNoiseSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f) = 0; - - float* GetWhiteNoiseSet(int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f); - virtual void FillWhiteNoiseSet(float* noiseSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f) = 0; - virtual void FillWhiteNoiseSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f) = 0; - - float* GetValueSet(int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f); - float* GetValueFractalSet(int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f); - virtual void FillValueSet(float* noiseSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f) = 0; - virtual void FillValueFractalSet(float* noiseSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f) = 0; - virtual void FillValueSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f) = 0; - virtual void FillValueFractalSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f) = 0; - - float* GetPerlinSet(int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f); - float* GetPerlinFractalSet(int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f); - virtual void FillPerlinSet(float* noiseSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f) = 0; - virtual void FillPerlinFractalSet(float* noiseSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f) = 0; - virtual void FillPerlinSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f) = 0; - virtual void FillPerlinFractalSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f) = 0; - - float* GetSimplexSet(int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f); - float* GetSimplexFractalSet(int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f); - virtual void FillSimplexSet(float* noiseSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f) = 0; - virtual void FillSimplexFractalSet(float* noiseSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f) = 0; - virtual void FillSimplexSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f) = 0; - virtual void FillSimplexFractalSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f) = 0; - - float* GetCellularSet(int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f); - virtual void FillCellularSet(float* noiseSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f) = 0; - virtual void FillCellularSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f) = 0; - - float* GetCubicSet(int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f); - float* GetCubicFractalSet(int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f); - virtual void FillCubicSet(float* noiseSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f) = 0; - virtual void FillCubicFractalSet(float* noiseSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f) = 0; - virtual void FillCubicSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f) = 0; - virtual void FillCubicFractalSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f) = 0; - - virtual ~FastNoiseSIMD() { } - -protected: - int m_seed = 1337; - float m_frequency = 0.01f; - NoiseType m_noiseType = SimplexFractal; - - float m_xScale = 1.0f; - float m_yScale = 1.0f; - float m_zScale = 1.0f; - - int m_octaves = 3; - float m_lacunarity = 2.0f; - float m_gain = 0.5f; - FractalType m_fractalType = FBM; - float m_fractalBounding; - - CellularDistanceFunction m_cellularDistanceFunction = Euclidean; - CellularReturnType m_cellularReturnType = Distance; - NoiseType m_cellularNoiseLookupType = Simplex; - float m_cellularNoiseLookupFrequency = 0.2f; - int m_cellularDistanceIndex0 = 0; - int m_cellularDistanceIndex1 = 1; - float m_cellularJitter = 0.45f; - - PerturbType m_perturbType = None; - float m_perturbAmp = 1.0f; - float m_perturbFrequency = 0.5f; - - int m_perturbOctaves = 3; - float m_perturbLacunarity = 2.0f; - float m_perturbGain = 0.5f; - float m_perturbFractalBounding; - float m_perturbNormaliseLength = 1.0f; - - static int s_currentSIMDLevel; - static float CalculateFractalBounding(int octaves, float gain); -}; - -struct FastNoiseVectorSet -{ -public: - int size = -1; - float* xSet = nullptr; - float* ySet = nullptr; - float* zSet = nullptr; - - // Only used for sampled vector sets - int sampleScale = 0; - int sampleSizeX = -1; - int sampleSizeY = -1; - int sampleSizeZ = -1; - - FastNoiseVectorSet() {} - - FastNoiseVectorSet(int _size) { SetSize(_size); } - - ~FastNoiseVectorSet() { Free(); } - - void Free(); - - void SetSize(int _size); -}; - -#define FN_CELLULAR_INDEX_MAX 3 - -#define FN_NO_SIMD_FALLBACK 0 -#define FN_SSE2 1 -#define FN_SSE41 2 -#define FN_AVX2 3 -#define FN_AVX512 4 -#define FN_NEON 5 -#endif diff --git a/deps/FastNoiseSIMD/FastNoiseSIMD_avx2.cpp b/deps/FastNoiseSIMD/FastNoiseSIMD_avx2.cpp deleted file mode 100644 index e8b4d09..0000000 --- a/deps/FastNoiseSIMD/FastNoiseSIMD_avx2.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// FastNoiseSIMD_avx2.cpp -// -// MIT License -// -// Copyright(c) 2017 Jordan Peck -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files(the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions : -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// The developer's email is jorzixdan.me2@gzixmail.com (for great email, take -// off every 'zix'.) -// - -#include "FastNoiseSIMD.h" - -// DISABLE WHOLE PROGRAM OPTIMIZATION for this file when using MSVC - -// To compile AVX2 support enable AVX(2) code generation compiler flags for this file -#ifdef FN_COMPILE_AVX2 -#ifndef __AVX__ -#ifdef __GNUC__ -#error To compile AVX2 add build command "-march=core-avx2" on FastNoiseSIMD_avx2.cpp, or remove "#define FN_COMPILE_AVX2" from FastNoiseSIMD.h -#else -#error To compile AVX2 set C++ code generation to use /arch:AVX(2) on FastNoiseSIMD_avx2.cpp, or remove "#define FN_COMPILE_AVX2" from FastNoiseSIMD.h -#endif -#endif - -#define SIMD_LEVEL_H FN_AVX2 -#include "FastNoiseSIMD_internal.h" -#include //AVX2 FMA3 - -#define SIMD_LEVEL FN_AVX2 -#include "FastNoiseSIMD_internal.cpp" -#endif \ No newline at end of file diff --git a/deps/FastNoiseSIMD/FastNoiseSIMD_avx512.cpp b/deps/FastNoiseSIMD/FastNoiseSIMD_avx512.cpp deleted file mode 100644 index 78a00a7..0000000 --- a/deps/FastNoiseSIMD/FastNoiseSIMD_avx512.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// FastNoiseSIMD_avx512.cpp -// -// MIT License -// -// Copyright(c) 2017 Jordan Peck -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files(the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions : -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// The developer's email is jorzixdan.me2@gzixmail.com (for great email, take -// off every 'zix'.) -// - -#include "FastNoiseSIMD.h" - -// DISABLE WHOLE PROGRAM OPTIMIZATION for this file when using MSVC - -// To compile AVX512 support enable AVX(2) code generation compiler flags for this file -#ifdef FN_COMPILE_AVX512 -#ifndef __AVX__ -#ifdef __GNUC__ -#error To compile AVX512 add build command "-march=core-avx2" on FastNoiseSIMD_avx512.cpp, or remove "#define FN_COMPILE_AVX512" from FastNoiseSIMD.h -#else -#error To compile AVX512 set C++ code generation to use /arch:AVX(2) on FastNoiseSIMD_avx512.cpp, or remove "#define FN_COMPILE_AVX512" from FastNoiseSIMD.h -#endif -#endif - -#define SIMD_LEVEL_H FN_AVX512 -#include "FastNoiseSIMD_internal.h" -#ifdef _WIN32 -#include //AVX512 -#else -#include //AVX512 -#endif - -#define SIMD_LEVEL FN_AVX512 -#include "FastNoiseSIMD_internal.cpp" -#endif diff --git a/deps/FastNoiseSIMD/FastNoiseSIMD_internal.cpp b/deps/FastNoiseSIMD/FastNoiseSIMD_internal.cpp deleted file mode 100644 index 5f850bc..0000000 --- a/deps/FastNoiseSIMD/FastNoiseSIMD_internal.cpp +++ /dev/null @@ -1,2419 +0,0 @@ -// FastNoiseSIMD_internal.cpp -// -// MIT License -// -// Copyright(c) 2017 Jordan Peck -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files(the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions : -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// The developer's email is jorzixdan.me2@gzixmail.com (for great email, take -// off every 'zix'.) -// - -#include "FastNoiseSIMD.h" -#include - -#if defined(SIMD_LEVEL) || defined(FN_COMPILE_NO_SIMD_FALLBACK) - -#ifndef SIMD_LEVEL -#define SIMD_LEVEL FN_NO_SIMD_FALLBACK -#define SIMD_LEVEL_H FN_NO_SIMD_FALLBACK -#include "FastNoiseSIMD_internal.h" -#include -#define FN_ALIGNED_SETS -#endif - -// Per SIMD level var/function naming -#define L_VAR2(x, l) L##l##_##x -#define L_VAR(x, l) L_VAR2(x, l) -#define VAR(x) L_VAR(x, SIMD_LEVEL) -#define FUNC(x) VAR(FUNC_##x) -#define SIMDf_NUM(n) VAR(SIMDf_NUM_##n) -#define SIMDi_NUM(n) VAR(SIMDi_NUM_##n) - -#define SIMD_LEVEL_CLASS FastNoiseSIMD_internal::FASTNOISE_SIMD_CLASS(SIMD_LEVEL) - -#if defined(_WIN32) && SIMD_LEVEL > FN_NO_SIMD_FALLBACK -#define VECTORCALL __vectorcall -#else -#define VECTORCALL -#endif - -// Typedefs -#if SIMD_LEVEL == FN_NEON -#define VECTOR_SIZE 4 -#define MEMORY_ALIGNMENT 16 -typedef float32x4_t SIMDf; -typedef int32x4_t SIMDi; -#define SIMDf_SET(a) vdupq_n_f32(a) -#define SIMDf_SET_ZERO() vdupq_n_f32(0) -#define SIMDi_SET(a) vdupq_n_s32(a) -#define SIMDi_SET_ZERO() vdupq_n_s32(0) - -#elif SIMD_LEVEL == FN_AVX512 -#define VECTOR_SIZE 16 -#define MEMORY_ALIGNMENT 64 -typedef __m512 SIMDf; -typedef __m512i SIMDi; -#define SIMDf_SET(a) _mm512_set1_ps(a) -#define SIMDf_SET_ZERO() _mm512_setzero_ps() -#define SIMDi_SET(a) _mm512_set1_epi32(a) -#define SIMDi_SET_ZERO() _mm512_setzero_si512() - -#elif SIMD_LEVEL == FN_AVX2 -#define VECTOR_SIZE 8 -#define MEMORY_ALIGNMENT 32 -typedef __m256 SIMDf; -typedef __m256i SIMDi; -#define SIMDf_SET(a) _mm256_set1_ps(a) -#define SIMDf_SET_ZERO() _mm256_setzero_ps() -#define SIMDi_SET(a) _mm256_set1_epi32(a) -#define SIMDi_SET_ZERO() _mm256_setzero_si256() - -#elif SIMD_LEVEL >= FN_SSE2 -#define VECTOR_SIZE 4 -#define MEMORY_ALIGNMENT 16 -typedef __m128 SIMDf; -typedef __m128i SIMDi; -#define SIMDf_SET(a) _mm_set1_ps(a) -#define SIMDf_SET_ZERO() _mm_setzero_ps() -#define SIMDi_SET(a) _mm_set1_epi32(a) -#define SIMDi_SET_ZERO() _mm_setzero_si128() - -#else // Fallback to float/int -#define VECTOR_SIZE 1 -#define MEMORY_ALIGNMENT 4 -typedef float SIMDf; -typedef int SIMDi; -#define SIMDf_SET(a) (a) -#define SIMDf_SET_ZERO() (0) -#define SIMDi_SET(a) (a) -#define SIMDi_SET_ZERO() (0) -#endif - -// Memory Allocation -#if SIMD_LEVEL > FN_NO_SIMD_FALLBACK && defined(FN_ALIGNED_SETS) -#ifdef _WIN32 -#define SIMD_ALLOCATE_SET(floatP, floatCount) floatP = (float*)_aligned_malloc((floatCount)* sizeof(float), MEMORY_ALIGNMENT) -#else -#include -#define SIMD_ALLOCATE_SET(floatP, floatCount) posix_memalign((void**)&floatP, MEMORY_ALIGNMENT, (floatCount)* sizeof(float)) -#endif -#else -#define SIMD_ALLOCATE_SET(floatP, floatCount) floatP = new float[floatCount] -#endif - -union uSIMDf -{ - SIMDf m; - float a[VECTOR_SIZE]; -}; - -union uSIMDi -{ - SIMDi m; - int a[VECTOR_SIZE]; -}; - -#if SIMD_LEVEL == FN_AVX512 -typedef __mmask16 MASK; -#else -typedef SIMDi MASK; -#endif - - -static SIMDi SIMDi_NUM(0xffffffff); -static SIMDf SIMDf_NUM(1); - -// SIMD functions -#if SIMD_LEVEL == FN_NEON - -#define SIMDf_STORE(p,a) vst1q_f32(p, a) -#define SIMDf_LOAD(p) vld1q_f32(p) - -#define SIMDf_UNDEFINED() SIMDf_SET(0) -#define SIMDi_UNDEFINED() SIMDi_SET(0) - -#define SIMDf_CONVERT_TO_FLOAT(a) vcvtq_f32_s32(a) -#define SIMDf_CAST_TO_FLOAT(a) vreinterpretq_f32_s32(a) -#define SIMDi_CONVERT_TO_INT(a) vcvtq_s32_f32(a) -#define SIMDi_CAST_TO_INT(a) vreinterpretq_s32_f32(a) - -#define SIMDf_ADD(a,b) vaddq_f32(a,b) -#define SIMDf_SUB(a,b) vsubq_f32(a,b) -#define SIMDf_MUL(a,b) vmulq_f32(a,b) -#define SIMDf_DIV(a,b) FUNC(DIV)(a,b) - -static SIMDf VECTORCALL FUNC(DIV)(SIMDf a, SIMDf b) -{ - SIMDf reciprocal = vrecpeq_f32(b); - // use a couple Newton-Raphson steps to refine the estimate. Depending on your - // application's accuracy requirements, you may be able to get away with only - // one refinement (instead of the two used here). Be sure to test! - reciprocal = vmulq_f32(vrecpsq_f32(b, reciprocal), reciprocal); - - // and finally, compute a/b = a*(1/b) - return vmulq_f32(a, reciprocal); -} - -#define SIMDf_MIN(a,b) vminq_f32(a,b) -#define SIMDf_MAX(a,b) vmaxq_f32(a,b) -#define SIMDf_INV_SQRT(a) vrsqrteq_f32(a) - -#define SIMDf_LESS_THAN(a,b) vreinterpretq_s32_u32(vcltq_f32(a,b)) -#define SIMDf_GREATER_THAN(a,b) vreinterpretq_s32_u32(vcgtq_f32(a,b)) -#define SIMDf_LESS_EQUAL(a,b) vreinterpretq_s32_u32(vcleq_f32(a,b)) -#define SIMDf_GREATER_EQUAL(a,b) vreinterpretq_s32_u32(vcgeq_f32(a,b)) - -#define SIMDf_AND(a,b) SIMDf_CAST_TO_FLOAT(vandq_s32(vreinterpretq_s32_f32(a),vreinterpretq_s32_f32(b))) -#define SIMDf_AND_NOT(a,b) SIMDf_CAST_TO_FLOAT(vandq_s32(vmvnq_s32(vreinterpretq_s32_f32(a)),vreinterpretq_s32_f32(b))) -#define SIMDf_XOR(a,b) SIMDf_CAST_TO_FLOAT(veorq_s32(vreinterpretq_s32_f32(a),vreinterpretq_s32_f32(b))) - -#ifndef __aarch64__ -static SIMDf VECTORCALL FUNC(FLOOR)(SIMDf a) -{ - SIMDf fval = SIMDf_CONVERT_TO_FLOAT(SIMDi_CONVERT_TO_INT(a)); - - return vsubq_f32(fval, - SIMDf_CAST_TO_FLOAT(vandq_s32(SIMDf_LESS_THAN(a, fval), - SIMDi_CAST_TO_INT(SIMDf_NUM(1))))); -} -#define SIMDf_FLOOR(a) FUNC(FLOOR)(a) -#else - -#define SIMDf_FLOOR(a) vrndmq_f32(a) -#endif - -#define SIMDf_ABS(a) vabsq_f32(a) -#define SIMDf_BLENDV(a,b,mask) vbslq_f32(vreinterpretq_u32_s32(mask),b,a) - -#define SIMDi_ADD(a,b) vaddq_s32(a,b) -#define SIMDi_SUB(a,b) vsubq_s32(a,b) -#define SIMDi_MUL(a,b) vmulq_s32(a,b) - -#define SIMDi_AND(a,b) vandq_s32(a,b) -#define SIMDi_AND_NOT(a,b) vandq_s32(vmvnq_s32(a),b) -#define SIMDi_OR(a,b) vorrq_s32(a,b) -#define SIMDi_XOR(a,b) veorq_s32(a,b) -#define SIMDi_NOT(a) vmvnq_s32(a) - -#define SIMDi_SHIFT_R(a, b) vshrq_n_s32(a, b) -#define SIMDi_SHIFT_L(a, b) vshlq_n_s32(a, b) -#define SIMDi_VSHIFT_L(a, b) vshlq_s32(a, b) - -#define SIMDi_EQUAL(a,b) vreinterpretq_s32_u32(vceqq_s32(a,b)) -#define SIMDi_GREATER_THAN(a,b) vreinterpretq_s32_u32(vcgtq_s32(a,b)) -#define SIMDi_LESS_THAN(a,b) vreinterpretq_s32_u32(vcltq_s32(a,b)) - -#elif SIMD_LEVEL == FN_AVX512 - -#ifdef FN_ALIGNED_SETS -#define SIMDf_STORE(p,a) _mm512_store_ps(p,a) -#define SIMDf_LOAD(p) _mm512_load_ps(p) -#else -#define SIMDf_STORE(p,a) _mm512_storeu_ps(p,a) -#define SIMDf_LOAD(p) _mm512_loadu_ps(p) -#endif - -#define SIMDf_UNDEFINED() _mm512_undefined_ps() -#define SIMDi_UNDEFINED() _mm512_undefined_epi32() - -#define SIMDf_ADD(a,b) _mm512_add_ps(a,b) -#define SIMDf_SUB(a,b) _mm512_sub_ps(a,b) -#define SIMDf_MUL(a,b) _mm512_mul_ps(a,b) -#define SIMDf_DIV(a,b) _mm512_div_ps(a,b) - -#define SIMDf_MIN(a,b) _mm512_min_ps(a,b) -#define SIMDf_MAX(a,b) _mm512_max_ps(a,b) -#define SIMDf_INV_SQRT(a) _mm512_rsqrt14_ps(a) - -#define SIMDf_LESS_THAN(a,b) _mm512_cmp_ps_mask(a,b,_CMP_LT_OQ) -#define SIMDf_GREATER_THAN(a,b) _mm512_cmp_ps_mask(a,b,_CMP_GT_OQ) -#define SIMDf_LESS_EQUAL(a,b) _mm512_cmp_ps_mask(a,b,_CMP_LE_OQ) -#define SIMDf_GREATER_EQUAL(a,b) _mm512_cmp_ps_mask(a,b,_CMP_GE_OQ) - -#define SIMDf_AND(a,b) _mm512_and_ps(a,b) -#define SIMDf_AND_NOT(a,b) _mm512_andnot_ps(a,b) -#define SIMDf_XOR(a,b) _mm512_xor_ps(a,b) - -#define SIMDf_FLOOR(a) _mm512_floor_ps(a) -#define SIMDf_ABS(a) _mm512_abs_ps(a) -#define SIMDf_BLENDV(a,b,mask) _mm512_mask_blend_ps(mask,a,b) -#define SIMDf_GATHER(p,a) _mm512_i32gather_ps(a,p,4) -#define SIMDf_PERMUTE(a,b) _mm512_permutexvar_ps(b,a) - -#define SIMDi_ADD(a,b) _mm512_add_epi32(a,b) -#define SIMDi_SUB(a,b) _mm512_sub_epi32(a,b) -#define SIMDi_MUL(a,b) _mm512_mullo_epi32(a,b) - -#define SIMDi_AND(a,b) _mm512_and_si512(a,b) -#define SIMDi_AND_NOT(a,b) _mm512_andnot_si512(a,b) -#define SIMDi_OR(a,b) _mm512_or_si512(a,b) -#define SIMDi_XOR(a,b) _mm512_xor_si512(a,b) -#define SIMDi_NOT(a) SIMDi_XOR(a,SIMDi_NUM(0xffffffff)) - -#define SIMDi_SHIFT_R(a, b) _mm512_srai_epi32(a, b) -#define SIMDi_SHIFT_L(a, b) _mm512_slli_epi32(a, b) - -#define SIMDi_VSHIFT_R(a,b) _mm512_srl_epi32(a, b) -#define SIMDi_VSHIFT_L(a,b) _mm512_sll_epi32(a, b) - -#define SIMDi_EQUAL(a,b) _mm512_cmpeq_epi32_mask(a,b) -#define SIMDi_GREATER_THAN(a,b) _mm512_cmpgt_epi32_mask(a,b) -#define SIMDi_LESS_THAN(a,b) _mm512_cmpgt_epi32_mask(b,a) - -#define SIMDf_CONVERT_TO_FLOAT(a) _mm512_cvtepi32_ps(a) -#define SIMDf_CAST_TO_FLOAT(a) _mm512_castsi512_ps(a) -#define SIMDi_CONVERT_TO_INT(a) _mm512_cvtps_epi32(a) -#define SIMDi_CAST_TO_INT(a) _mm512_castps_si512(a) - -#elif SIMD_LEVEL == FN_AVX2 - -#ifdef FN_ALIGNED_SETS -#define SIMDf_STORE(p,a) _mm256_store_ps(p,a) -#define SIMDf_LOAD(p) _mm256_load_ps(p) -#else -#define SIMDf_STORE(p,a) _mm256_storeu_ps(p,a) -#define SIMDf_LOAD(p) _mm256_loadu_ps(p) -#endif - -#define SIMDf_UNDEFINED() _mm256_undefined_ps() -#define SIMDi_UNDEFINED() _mm256_undefined_si256() - -#define SIMDf_CONVERT_TO_FLOAT(a) _mm256_cvtepi32_ps(a) -#define SIMDf_CAST_TO_FLOAT(a) _mm256_castsi256_ps(a) -#define SIMDi_CONVERT_TO_INT(a) _mm256_cvtps_epi32(a) -#define SIMDi_CAST_TO_INT(a) _mm256_castps_si256(a) - -#define SIMDf_ADD(a,b) _mm256_add_ps(a,b) -#define SIMDf_SUB(a,b) _mm256_sub_ps(a,b) -#define SIMDf_MUL(a,b) _mm256_mul_ps(a,b) -#define SIMDf_DIV(a,b) _mm256_div_ps(a,b) - -#define SIMDf_MIN(a,b) _mm256_min_ps(a,b) -#define SIMDf_MAX(a,b) _mm256_max_ps(a,b) -#define SIMDf_INV_SQRT(a) _mm256_rsqrt_ps(a) - -#define SIMDf_LESS_THAN(a,b) SIMDi_CAST_TO_INT(_mm256_cmp_ps(a,b,_CMP_LT_OQ)) -#define SIMDf_GREATER_THAN(a,b) SIMDi_CAST_TO_INT(_mm256_cmp_ps(a,b,_CMP_GT_OQ)) -#define SIMDf_LESS_EQUAL(a,b) SIMDi_CAST_TO_INT(_mm256_cmp_ps(a,b,_CMP_LE_OQ)) -#define SIMDf_GREATER_EQUAL(a,b) SIMDi_CAST_TO_INT( _mm256_cmp_ps(a,b,_CMP_GE_OQ)) - -#define SIMDf_AND(a,b) _mm256_and_ps(a,b) -#define SIMDf_AND_NOT(a,b) _mm256_andnot_ps(a,b) -#define SIMDf_XOR(a,b) _mm256_xor_ps(a,b) - -#define SIMDf_FLOOR(a) _mm256_floor_ps(a) -#define SIMDf_ABS(a) SIMDf_AND(a,SIMDf_CAST_TO_FLOAT(SIMDi_NUM(0x7fffffff))) -#define SIMDf_BLENDV(a,b,mask) _mm256_blendv_ps(a,b,SIMDf_CAST_TO_FLOAT(mask)) -#define SIMDf_PERMUTE(a,b) _mm256_permutevar8x32_ps(a,b) - -#define SIMDi_ADD(a,b) _mm256_add_epi32(a,b) -#define SIMDi_SUB(a,b) _mm256_sub_epi32(a,b) -#define SIMDi_MUL(a,b) _mm256_mullo_epi32(a,b) - -#define SIMDi_AND(a,b) _mm256_and_si256(a,b) -#define SIMDi_AND_NOT(a,b) _mm256_andnot_si256(a,b) -#define SIMDi_OR(a,b) _mm256_or_si256(a,b) -#define SIMDi_XOR(a,b) _mm256_xor_si256(a,b) -#define SIMDi_NOT(a) SIMDi_XOR(a,SIMDi_NUM(0xffffffff)) - -#define SIMDi_SHIFT_R(a, b) _mm256_srai_epi32(a, b) -#define SIMDi_SHIFT_L(a, b) _mm256_slli_epi32(a, b) - -#define SIMDi_EQUAL(a,b) _mm256_cmpeq_epi32(a,b) -#define SIMDi_GREATER_THAN(a,b) _mm256_cmpgt_epi32(a,b) -#define SIMDi_LESS_THAN(a,b) _mm256_cmpgt_epi32(b,a) - -#elif SIMD_LEVEL >= FN_SSE2 - -#ifdef FN_ALIGNED_SETS -#define SIMDf_STORE(p,a) _mm_store_ps(p,a) -#define SIMDf_LOAD(p) _mm_load_ps(p) -#else -#define SIMDf_STORE(p,a) _mm_storeu_ps(p,a) -#define SIMDf_LOAD(p) _mm_loadu_ps(p) -#endif - -#define SIMDf_UNDEFINED() SIMDf_SET_ZERO() -#define SIMDi_UNDEFINED() SIMDi_SET_ZERO() - -#define SIMDf_CONVERT_TO_FLOAT(a) _mm_cvtepi32_ps(a) -#define SIMDf_CAST_TO_FLOAT(a) _mm_castsi128_ps(a) -#define SIMDi_CONVERT_TO_INT(a) _mm_cvtps_epi32(a) -#define SIMDi_CAST_TO_INT(a) _mm_castps_si128(a) - -#define SIMDf_ADD(a,b) _mm_add_ps(a,b) -#define SIMDf_SUB(a,b) _mm_sub_ps(a,b) -#define SIMDf_MUL(a,b) _mm_mul_ps(a,b) -#define SIMDf_DIV(a,b) _mm_div_ps(a,b) - -#define SIMDf_MIN(a,b) _mm_min_ps(a,b) -#define SIMDf_MAX(a,b) _mm_max_ps(a,b) -#define SIMDf_INV_SQRT(a) _mm_rsqrt_ps(a) - -#define SIMDf_LESS_THAN(a,b) SIMDi_CAST_TO_INT(_mm_cmplt_ps(a,b)) -#define SIMDf_GREATER_THAN(a,b) SIMDi_CAST_TO_INT(_mm_cmpgt_ps(a,b)) -#define SIMDf_LESS_EQUAL(a,b) SIMDi_CAST_TO_INT(_mm_cmple_ps(a,b)) -#define SIMDf_GREATER_EQUAL(a,b) SIMDi_CAST_TO_INT(_mm_cmpge_ps(a,b)) - -#define SIMDf_AND(a,b) _mm_and_ps(a,b) -#define SIMDf_AND_NOT(a,b) _mm_andnot_ps(a,b) -#define SIMDf_XOR(a,b) _mm_xor_ps(a,b) - -#define SIMDf_ABS(a) SIMDf_AND(a,SIMDf_CAST_TO_FLOAT(SIMDi_NUM(0x7fffffff))) - -#if SIMD_LEVEL == FN_SSE41 -#define SIMDi_MUL(a,b) _mm_mullo_epi32(a,b) -#define SIMDf_FLOOR(a) _mm_floor_ps(a) -#define SIMDf_BLENDV(a,b,mask) _mm_blendv_ps(a,b,SIMDf_CAST_TO_FLOAT(mask)) -#else -static SIMDi VECTORCALL FUNC(MUL)(SIMDi a, SIMDi b) -{ - __m128 tmp1 = _mm_castsi128_ps(_mm_mul_epu32(a, b)); /* mul 2,0*/ - __m128 tmp2 = _mm_castsi128_ps(_mm_mul_epu32(_mm_srli_si128(a, 4), _mm_srli_si128(b, 4))); /* mul 3,1 */ - return _mm_shuffle_epi32(_mm_castps_si128(_mm_shuffle_ps(tmp1, tmp2, _MM_SHUFFLE(2, 0, 2, 0))), _MM_SHUFFLE(3, 1, 2, 0)); -} -#define SIMDi_MUL(a,b) FUNC(MUL)(a,b) - -static SIMDf VECTORCALL FUNC(FLOOR)(SIMDf a) -{ - __m128 fval = _mm_cvtepi32_ps(_mm_cvttps_epi32(a)); - - return _mm_sub_ps(fval, _mm_and_ps(_mm_cmplt_ps(a, fval), SIMDf_NUM(1))); -} -#define SIMDf_FLOOR(a) FUNC(FLOOR)(a) - -#define SIMDf_BLENDV(a,b,mask) _mm_or_ps(_mm_andnot_ps(SIMDf_CAST_TO_FLOAT(mask), a), _mm_and_ps(SIMDf_CAST_TO_FLOAT(mask), b)) -#endif - -#define SIMDi_ADD(a,b) _mm_add_epi32(a,b) -#define SIMDi_SUB(a,b) _mm_sub_epi32(a,b) - -#define SIMDi_AND(a,b) _mm_and_si128(a,b) -#define SIMDi_AND_NOT(a,b) _mm_andnot_si128(a,b) -#define SIMDi_OR(a,b) _mm_or_si128(a,b) -#define SIMDi_XOR(a,b) _mm_xor_si128(a,b) -#define SIMDi_NOT(a) SIMDi_XOR(a,SIMDi_NUM(0xffffffff)) - -#define SIMDi_SHIFT_R(a,b) _mm_srai_epi32(a, b) -#define SIMDi_SHIFT_L(a,b) _mm_slli_epi32(a, b) - -#define SIMDi_EQUAL(a,b) _mm_cmpeq_epi32(a,b) -#define SIMDi_GREATER_THAN(a,b) _mm_cmpgt_epi32(a,b) -#define SIMDi_LESS_THAN(a,b) _mm_cmpgt_epi32(b,a) - -#else // Fallback - -static int FUNC(CAST_TO_INT)(float f) { return *reinterpret_cast(&f); } -static float FUNC(CAST_TO_FLOAT)(int i) { return *reinterpret_cast(&i); } -#define SIMDi_CAST_TO_INT(a) FUNC(CAST_TO_INT)(a) -#define SIMDf_CAST_TO_FLOAT(a) FUNC(CAST_TO_FLOAT)(a) - -#define SIMDf_STORE(p,a) (*(p) = a) -#define SIMDf_LOAD(p) (*p) - -#define SIMDf_UNDEFINED() (0) -#define SIMDi_UNDEFINED() (0) - -#define SIMDf_ADD(a,b) ((a) + (b)) -#define SIMDf_SUB(a,b) ((a) - (b)) -#define SIMDf_MUL(a,b) ((a) * (b)) -#define SIMDf_DIV(a,b) ((a) / (b)) - -#define SIMDf_MIN(a,b) fminf(a,b) -#define SIMDf_MAX(a,b) fmaxf(a,b) - -static float FUNC(INV_SQRT)(float x) -{ - float xhalf = 0.5f * x; - int i = *(int*)&x; - i = 0x5f3759df - (i >> 1); - x = *(float*)&i; - x = x*(1.5f - xhalf*x*x); - return x; -} -#define SIMDf_INV_SQRT(a) FUNC(INV_SQRT)(a) - -#define SIMDf_LESS_THAN(a,b) (((a) < (b)) ? 0xFFFFFFFF : 0) -#define SIMDf_GREATER_THAN(a,b) (((a) > (b)) ? 0xFFFFFFFF : 0) -#define SIMDf_LESS_EQUAL(a,b) (((a) <= (b)) ? 0xFFFFFFFF : 0) -#define SIMDf_GREATER_EQUAL(a,b) (((a) >= (b)) ? 0xFFFFFFFF : 0) - -#define SIMDf_AND(a,b) SIMDf_CAST_TO_FLOAT(SIMDi_CAST_TO_INT(a) & SIMDi_CAST_TO_INT(b)) -#define SIMDf_AND_NOT(a,b) SIMDf_CAST_TO_FLOAT(~SIMDi_CAST_TO_INT(a) & SIMDi_CAST_TO_INT(b)) -#define SIMDf_XOR(a,b) SIMDf_CAST_TO_FLOAT(SIMDi_CAST_TO_INT(a) ^ SIMDi_CAST_TO_INT(b)) - -#define SIMDf_FLOOR(a) floorf(a) -#define SIMDf_ABS(a) fabsf(a) -#define SIMDf_BLENDV(a,b,mask) (mask ? (b) : (a)) -#define SIMDf_GATHER(p,a) (*(reinterpret_cast(p)+(a))) - -#define SIMDi_ADD(a,b) ((a) + (b)) -#define SIMDi_SUB(a,b) ((a) - (b)) -#define SIMDi_MUL(a,b) ((a) * (b)) - -#define SIMDi_AND(a,b) ((a) & (b)) -#define SIMDi_AND_NOT(a,b) (~(a) & (b)) -#define SIMDi_OR(a,b) ((a) | (b)) -#define SIMDi_XOR(a,b) ((a) ^ (b)) -#define SIMDi_NOT(a) (~(a)) - -#define SIMDi_SHIFT_R(a, b) ((a) >> (b)) -#define SIMDi_SHIFT_L(a, b) ((a) << (b)) - -#define SIMDi_EQUAL(a,b) (((a) == (b)) ? 0xFFFFFFFF : 0) -#define SIMDi_GREATER_THAN(a,b) (((a) > (b)) ? 0xFFFFFFFF : 0) -#define SIMDi_LESS_THAN(a,b) (((a) < (b)) ? 0xFFFFFFFF : 0) - -#define SIMDi_CONVERT_TO_INT(a) static_cast(roundf(a)) -#define SIMDf_CONVERT_TO_FLOAT(a) static_cast(a) -#endif - -//#define SIMDf_SIGN_FLIP(a) SIMDf_XOR(a,SIMDf_NUM(neg0))) -//#define SIMDi_GREATER_EQUAL(a,b) SIMDi_NOT(SIMDi_LESS_THAN(a,b)) -//#define SIMDi_LESS_EQUAL(a,b) SIMDi_NOT(SIMDi_GREATER_THAN(a,b)) -//#define SIMDi_BLENDV(a,b, mask) SIMDi_CAST_TO_INT(SIMDf_BLENDV(SIMDf_CAST_TO_FLOAT(a),SIMDf_CAST_TO_FLOAT(b),SIMDf_CAST_TO_FLOAT(mask))) - -#if SIMD_LEVEL == FN_AVX512 - -#define MASK_OR(a,b) ((a)|(b)) -#define MASK_AND(a,b) ((a)&(b)) -#define MASK_AND_NOT(a,b) (~(a)&(b)) -#define MASK_NOT(a) (~(a)) - -#define SIMDf_MASK(m,a) _mm512_maskz_mov_ps(m,a) -#define SIMDf_MASK_ADD(m,a,b) _mm512_mask_add_ps(a,m,a,b) -#define SIMDf_MASK_SUB(m,a,b) _mm512_mask_sub_ps(a,m,a,b) - -#define SIMDi_MASK_ADD(m,a,b) _mm512_mask_add_epi32(a,m,a,b) -#define SIMDi_MASK_SUB(m,a,b) _mm512_mask_sub_epi32(a,m,a,b) - -#else - -#define MASK_OR(a,b) SIMDi_OR(a,b) -#define MASK_AND(a,b) SIMDi_AND(a,b) -#define MASK_AND_NOT(a,b) SIMDi_AND_NOT(a,b) -#define MASK_NOT(a) SIMDi_NOT(a) - -#define SIMDf_MASK(m,a) SIMDf_AND(SIMDf_CAST_TO_FLOAT(m),a) -#define SIMDf_MASK_ADD(m,a,b) SIMDf_ADD(a,SIMDf_AND(SIMDf_CAST_TO_FLOAT(m),b)) -#define SIMDf_MASK_SUB(m,a,b) SIMDf_SUB(a,SIMDf_AND(SIMDf_CAST_TO_FLOAT(m),b)) - -#define SIMDi_MASK_ADD(m,a,b) SIMDi_ADD(a,SIMDi_AND(m,b)) -#define SIMDi_MASK_SUB(m,a,b) SIMDi_SUB(a,SIMDi_AND(m,b)) - -#endif - -#if SIMD_LEVEL == FN_AVX512 -#elif SIMD_LEVEL == FN_NEON -#elif SIMD_LEVEL == FN_NO_SIMD_FALLBACK -#else -#endif - -#if SIMD_LEVEL == FN_AVX2 -#define SIMD_ZERO_ALL() //_mm256_zeroall() -#else -#define SIMD_ZERO_ALL() -#endif - -// FMA -#ifdef FN_USE_FMA -#if SIMD_LEVEL == FN_NEON -#define SIMDf_MUL_ADD(a,b,c) vmlaq_f32(b,c,a) -#define SIMDf_MUL_SUB(a,b,c) SIMDf_SUB(SIMDf_MUL(a,b),c) // Neon multiply sub swaps sides of minus compared to FMA making it unusable -#define SIMDf_NMUL_ADD(a,b,c) vmlaq_f32(b,c,a) -#elif SIMD_LEVEL == FN_AVX512 -#define SIMDf_MUL_ADD(a,b,c) _mm512_fmadd_ps(a,b,c) -#define SIMDf_MUL_SUB(a,b,c) _mm512_fmsub_ps(a,b,c) -#define SIMDf_NMUL_ADD(a,b,c) _mm512_fnmadd_ps(a,b,c) -#elif SIMD_LEVEL == FN_AVX2 -#define SIMDf_MUL_ADD(a,b,c) _mm256_fmadd_ps(a,b,c) -#define SIMDf_MUL_SUB(a,b,c) _mm256_fmsub_ps(a,b,c) -#define SIMDf_NMUL_ADD(a,b,c) _mm256_fnmadd_ps(a,b,c) -#endif -#endif - -#ifndef SIMDf_MUL_ADD -#define SIMDf_MUL_ADD(a,b,c) SIMDf_ADD(SIMDf_MUL(a,b),c) -#define SIMDf_MUL_SUB(a,b,c) SIMDf_SUB(SIMDf_MUL(a,b),c) -#define SIMDf_NMUL_ADD(a,b,c) SIMDf_SUB(c, SIMDf_MUL(a,b)) -#endif - -static bool VAR(SIMD_Values_Set) = false; - -static SIMDf SIMDf_NUM(incremental); -static SIMDf SIMDf_NUM(0); -static SIMDf SIMDf_NUM(2); -static SIMDf SIMDf_NUM(6); -static SIMDf SIMDf_NUM(10); -static SIMDf SIMDf_NUM(15); -static SIMDf SIMDf_NUM(32); -static SIMDf SIMDf_NUM(999999); - -static SIMDf SIMDf_NUM(0_5); -static SIMDf SIMDf_NUM(0_6); -static SIMDf SIMDf_NUM(15_5); -static SIMDf SIMDf_NUM(511_5); - -//static SIMDf SIMDf_NUM(cellJitter); -static SIMDf SIMDf_NUM(F3); -static SIMDf SIMDf_NUM(G3); -static SIMDf SIMDf_NUM(G33); -static SIMDf SIMDf_NUM(hash2Float); -static SIMDf SIMDf_NUM(vectorSize); -static SIMDf SIMDf_NUM(cubicBounding); - -#if SIMD_LEVEL == FN_AVX512 -static SIMDf SIMDf_NUM(X_GRAD); -static SIMDf SIMDf_NUM(Y_GRAD); -static SIMDf SIMDf_NUM(Z_GRAD); - -#else -static SIMDi SIMDi_NUM(8); -static SIMDi SIMDi_NUM(12); -static SIMDi SIMDi_NUM(13); -#endif - -static SIMDi SIMDi_NUM(incremental); -static SIMDi SIMDi_NUM(1); -static SIMDi SIMDi_NUM(2); -static SIMDi SIMDi_NUM(255); -static SIMDi SIMDi_NUM(60493); -static SIMDi SIMDi_NUM(0x7fffffff); - -//static SIMDi SIMDi_NUM(xGradBits); -//static SIMDi SIMDi_NUM(yGradBits); -//static SIMDi SIMDi_NUM(zGradBits); - -static SIMDi SIMDi_NUM(xPrime); -static SIMDi SIMDi_NUM(yPrime); -static SIMDi SIMDi_NUM(zPrime); -static SIMDi SIMDi_NUM(bit5Mask); -static SIMDi SIMDi_NUM(bit10Mask); -static SIMDi SIMDi_NUM(vectorSize); - -void FUNC(InitSIMDValues)() -{ - if (VAR(SIMD_Values_Set)) - return; - - uSIMDf incF; - uSIMDi incI; - for (int i = 0; i < VECTOR_SIZE; i++) - { - incF.a[i] = float(i); - incI.a[i] = i; - } - SIMDf_NUM(incremental) = incF.m; - SIMDi_NUM(incremental) = incI.m; - - SIMDf_NUM(0) = SIMDf_SET_ZERO(); - SIMDf_NUM(1) = SIMDf_SET(1.0f); - SIMDf_NUM(2) = SIMDf_SET(2.0f); - SIMDf_NUM(6) = SIMDf_SET(6.0f); - SIMDf_NUM(10) = SIMDf_SET(10.0f); - SIMDf_NUM(15) = SIMDf_SET(15.0f); - SIMDf_NUM(32) = SIMDf_SET(32.0f); - SIMDf_NUM(999999) = SIMDf_SET(999999.0f); - - SIMDf_NUM(0_5) = SIMDf_SET(0.5f); - SIMDf_NUM(0_6) = SIMDf_SET(0.6f); - SIMDf_NUM(15_5) = SIMDf_SET(15.5f); - SIMDf_NUM(511_5) = SIMDf_SET(511.5f); - - //SIMDf_NUM(cellJitter) = SIMDf_SET(0.39614f); - SIMDf_NUM(F3) = SIMDf_SET(1.f / 3.f); - SIMDf_NUM(G3) = SIMDf_SET(1.f / 6.f); - SIMDf_NUM(G33) = SIMDf_SET((3.f / 6.f) - 1.f); - SIMDf_NUM(hash2Float) = SIMDf_SET(1.f / 2147483648.f); - SIMDf_NUM(vectorSize) = SIMDf_SET(VECTOR_SIZE); - SIMDf_NUM(cubicBounding) = SIMDf_SET(1.f / (1.5f*1.5f*1.5f)); - -#if SIMD_LEVEL == FN_AVX512 - SIMDf_NUM(X_GRAD) = _mm512_set_ps(0, -1, 0, 1, 0, 0, 0, 0, -1, 1, -1, 1, -1, 1, -1, 1); - SIMDf_NUM(Y_GRAD) = _mm512_set_ps(-1, 1, -1, 1, -1, 1, -1, 1, 0, 0, 0, 0, -1, -1, 1, 1); - SIMDf_NUM(Z_GRAD) = _mm512_set_ps(-1, 0, 1, 0, -1, -1, 1, 1, -1, -1, 1, 1, 0, 0, 0, 0); - -#else - SIMDi_NUM(8) = SIMDi_SET(8); - SIMDi_NUM(12) = SIMDi_SET(12); - SIMDi_NUM(13) = SIMDi_SET(13); -#endif - - SIMDi_NUM(1) = SIMDi_SET(1); - SIMDi_NUM(2) = SIMDi_SET(2); - SIMDi_NUM(255) = SIMDi_SET(255); - SIMDi_NUM(60493) = SIMDi_SET(60493); - SIMDi_NUM(0x7fffffff) = SIMDi_SET(0x7fffffff); - - //SIMDi_NUM(xGradBits) = SIMDi_SET(1683327112); - //SIMDi_NUM(yGradBits) = SIMDi_SET(-2004331104); - //SIMDi_NUM(zGradBits) = SIMDi_SET(-1851744171); - - SIMDi_NUM(xPrime) = SIMDi_SET(1619); - SIMDi_NUM(yPrime) = SIMDi_SET(31337); - SIMDi_NUM(zPrime) = SIMDi_SET(6971); - SIMDi_NUM(bit5Mask) = SIMDi_SET(31); - SIMDi_NUM(bit10Mask) = SIMDi_SET(1023); - SIMDi_NUM(vectorSize) = SIMDi_SET(VECTOR_SIZE); - - SIMDi_NUM(0xffffffff) = SIMDi_SET(-1); - - VAR(SIMD_Values_Set) = true; -} - -static SIMDf VECTORCALL FUNC(Lerp)(SIMDf a, SIMDf b, SIMDf t) -{ - SIMDf r; - r = SIMDf_SUB(b, a); - r = SIMDf_MUL_ADD(r, t, a); - return r; -} - -static SIMDf VECTORCALL FUNC(InterpQuintic)(SIMDf t) -{ - SIMDf r; - r = SIMDf_MUL_SUB(t, SIMDf_NUM(6), SIMDf_NUM(15)); - r = SIMDf_MUL_ADD(r, t, SIMDf_NUM(10)); - r = SIMDf_MUL(r, t); - r = SIMDf_MUL(r, t); - r = SIMDf_MUL(r, t); - - return r; -} - -static SIMDf VECTORCALL FUNC(CubicLerp)(SIMDf a, SIMDf b, SIMDf c, SIMDf d, SIMDf t) -{ - SIMDf p = SIMDf_SUB(SIMDf_SUB(d, c), SIMDf_SUB(a, b)); - return SIMDf_MUL_ADD(t, SIMDf_MUL(t, SIMDf_MUL(t, p)), SIMDf_MUL_ADD(t, SIMDf_MUL(t, SIMDf_SUB(SIMDf_SUB(a, b), p)), SIMDf_MUL_ADD(t, SIMDf_SUB(c, a), b))); -} - -//static SIMDf VECTORCALL FUNC(InterpHermite)(SIMDf t) -//{ -// SIMDf r; -// r = SIMDf_MUL(t, SIMDf_NUM(2)); -// r = SIMDf_SUB(SIMDf_ADD(SIMDf_NUM(1), SIMDf_NUM(2)), r); -// r = SIMDf_MUL(r, t); -// r = SIMDf_MUL(r, t); -// -// return r; -//} - -static SIMDi VECTORCALL FUNC(Hash)(SIMDi seed, SIMDi x, SIMDi y, SIMDi z) -{ - SIMDi hash = seed; - - hash = SIMDi_XOR(x, hash); - hash = SIMDi_XOR(y, hash); - hash = SIMDi_XOR(z, hash); - - hash = SIMDi_MUL(SIMDi_MUL(SIMDi_MUL(hash, hash), SIMDi_NUM(60493)), hash); - hash = SIMDi_XOR(SIMDi_SHIFT_R(hash, 13), hash); - - return hash; -} - -static SIMDi VECTORCALL FUNC(HashHB)(SIMDi seed, SIMDi x, SIMDi y, SIMDi z) -{ - SIMDi hash = seed; - - hash = SIMDi_XOR(x, hash); - hash = SIMDi_XOR(y, hash); - hash = SIMDi_XOR(z, hash); - //hash = SIMDi_XOR(SIMDi_SHIFT_R(hash, 13), hash); - - hash = SIMDi_MUL(SIMDi_MUL(SIMDi_MUL(hash, hash), SIMDi_NUM(60493)), hash); - - return hash; -} - -static SIMDf VECTORCALL FUNC(ValCoord)(SIMDi seed, SIMDi x, SIMDi y, SIMDi z) -{ - // High bit hash - SIMDi hash = seed; - - hash = SIMDi_XOR(x, hash); - hash = SIMDi_XOR(y, hash); - hash = SIMDi_XOR(z, hash); - - hash = SIMDi_MUL(SIMDi_MUL(SIMDi_MUL(hash, hash), SIMDi_NUM(60493)), hash); - //hash = SIMDi_XOR(SIMDi_SHIFT_L(hash, 13), hash); - - return SIMDf_MUL(SIMDf_NUM(hash2Float), SIMDf_CONVERT_TO_FLOAT(hash)); -} - -#if SIMD_LEVEL == FN_AVX512 -static SIMDf VECTORCALL FUNC(GradCoord)(SIMDi seed, SIMDi xi, SIMDi yi, SIMDi zi, SIMDf x, SIMDf y, SIMDf z) -{ - SIMDi hash = FUNC(Hash)(seed, xi, yi, zi); - - SIMDf xGrad = SIMDf_PERMUTE(SIMDf_NUM(X_GRAD), hash); - SIMDf yGrad = SIMDf_PERMUTE(SIMDf_NUM(Y_GRAD), hash); - SIMDf zGrad = SIMDf_PERMUTE(SIMDf_NUM(Z_GRAD), hash); - - return SIMDf_MUL_ADD(x, xGrad, SIMDf_MUL_ADD(y, yGrad, SIMDf_MUL(z, zGrad))); -} -#else -static SIMDf VECTORCALL FUNC(GradCoord)(SIMDi seed, SIMDi xi, SIMDi yi, SIMDi zi, SIMDf x, SIMDf y, SIMDf z) -{ - SIMDi hash = FUNC(Hash)(seed, xi, yi, zi); - SIMDi hasha13 = SIMDi_AND(hash, SIMDi_NUM(13)); - - //if h < 8 then x, else y - MASK l8 = SIMDi_LESS_THAN(hasha13, SIMDi_NUM(8)); - SIMDf u = SIMDf_BLENDV(y, x, l8); - - //if h < 4 then y else if h is 12 or 14 then x else z - MASK l4 = SIMDi_LESS_THAN(hasha13, SIMDi_NUM(2)); - MASK h12o14 = SIMDi_EQUAL(SIMDi_NUM(12), hasha13); - SIMDf v = SIMDf_BLENDV(SIMDf_BLENDV(z, x, h12o14), y, l4); - - //if h1 then -u else u - //if h2 then -v else v - SIMDf h1 = SIMDf_CAST_TO_FLOAT(SIMDi_SHIFT_L(hash, 31)); - SIMDf h2 = SIMDf_CAST_TO_FLOAT(SIMDi_SHIFT_L(SIMDi_AND(hash, SIMDi_NUM(2)), 30)); - //then add them - return SIMDf_ADD(SIMDf_XOR(u, h1), SIMDf_XOR(v, h2)); -} -#endif - -static SIMDf VECTORCALL FUNC(WhiteNoiseSingle)(SIMDi seed, SIMDf x, SIMDf y, SIMDf z) -{ - return FUNC(ValCoord)(seed, - SIMDi_MUL(SIMDi_XOR(SIMDi_CAST_TO_INT(x), SIMDi_SHIFT_R(SIMDi_CAST_TO_INT(x), 16)), SIMDi_NUM(xPrime)), - SIMDi_MUL(SIMDi_XOR(SIMDi_CAST_TO_INT(y), SIMDi_SHIFT_R(SIMDi_CAST_TO_INT(y), 16)), SIMDi_NUM(yPrime)), - SIMDi_MUL(SIMDi_XOR(SIMDi_CAST_TO_INT(z), SIMDi_SHIFT_R(SIMDi_CAST_TO_INT(z), 16)), SIMDi_NUM(zPrime))); -} - -static SIMDf VECTORCALL FUNC(ValueSingle)(SIMDi seed, SIMDf x, SIMDf y, SIMDf z) -{ - SIMDf xs = SIMDf_FLOOR(x); - SIMDf ys = SIMDf_FLOOR(y); - SIMDf zs = SIMDf_FLOOR(z); - - SIMDi x0 = SIMDi_MUL(SIMDi_CONVERT_TO_INT(xs), SIMDi_NUM(xPrime)); - SIMDi y0 = SIMDi_MUL(SIMDi_CONVERT_TO_INT(ys), SIMDi_NUM(yPrime)); - SIMDi z0 = SIMDi_MUL(SIMDi_CONVERT_TO_INT(zs), SIMDi_NUM(zPrime)); - SIMDi x1 = SIMDi_ADD(x0, SIMDi_NUM(xPrime)); - SIMDi y1 = SIMDi_ADD(y0, SIMDi_NUM(yPrime)); - SIMDi z1 = SIMDi_ADD(z0, SIMDi_NUM(zPrime)); - - xs = FUNC(InterpQuintic)(SIMDf_SUB(x, xs)); - ys = FUNC(InterpQuintic)(SIMDf_SUB(y, ys)); - zs = FUNC(InterpQuintic)(SIMDf_SUB(z, zs)); - - return FUNC(Lerp)( - FUNC(Lerp)( - FUNC(Lerp)(FUNC(ValCoord)(seed, x0, y0, z0), FUNC(ValCoord)(seed, x1, y0, z0), xs), - FUNC(Lerp)(FUNC(ValCoord)(seed, x0, y1, z0), FUNC(ValCoord)(seed, x1, y1, z0), xs), ys), - FUNC(Lerp)( - FUNC(Lerp)(FUNC(ValCoord)(seed, x0, y0, z1), FUNC(ValCoord)(seed, x1, y0, z1), xs), - FUNC(Lerp)(FUNC(ValCoord)(seed, x0, y1, z1), FUNC(ValCoord)(seed, x1, y1, z1), xs), ys), zs); -} - -static SIMDf VECTORCALL FUNC(PerlinSingle)(SIMDi seed, SIMDf x, SIMDf y, SIMDf z) -{ - SIMDf xs = SIMDf_FLOOR(x); - SIMDf ys = SIMDf_FLOOR(y); - SIMDf zs = SIMDf_FLOOR(z); - - SIMDi x0 = SIMDi_MUL(SIMDi_CONVERT_TO_INT(xs), SIMDi_NUM(xPrime)); - SIMDi y0 = SIMDi_MUL(SIMDi_CONVERT_TO_INT(ys), SIMDi_NUM(yPrime)); - SIMDi z0 = SIMDi_MUL(SIMDi_CONVERT_TO_INT(zs), SIMDi_NUM(zPrime)); - SIMDi x1 = SIMDi_ADD(x0, SIMDi_NUM(xPrime)); - SIMDi y1 = SIMDi_ADD(y0, SIMDi_NUM(yPrime)); - SIMDi z1 = SIMDi_ADD(z0, SIMDi_NUM(zPrime)); - - SIMDf xf0 = xs = SIMDf_SUB(x, xs); - SIMDf yf0 = ys = SIMDf_SUB(y, ys); - SIMDf zf0 = zs = SIMDf_SUB(z, zs); - SIMDf xf1 = SIMDf_SUB(xf0, SIMDf_NUM(1)); - SIMDf yf1 = SIMDf_SUB(yf0, SIMDf_NUM(1)); - SIMDf zf1 = SIMDf_SUB(zf0, SIMDf_NUM(1)); - - xs = FUNC(InterpQuintic)(xs); - ys = FUNC(InterpQuintic)(ys); - zs = FUNC(InterpQuintic)(zs); - - return FUNC(Lerp)( - FUNC(Lerp)( - FUNC(Lerp)(FUNC(GradCoord)(seed, x0, y0, z0, xf0, yf0, zf0), FUNC(GradCoord)(seed, x1, y0, z0, xf1, yf0, zf0), xs), - FUNC(Lerp)(FUNC(GradCoord)(seed, x0, y1, z0, xf0, yf1, zf0), FUNC(GradCoord)(seed, x1, y1, z0, xf1, yf1, zf0), xs), ys), - FUNC(Lerp)( - FUNC(Lerp)(FUNC(GradCoord)(seed, x0, y0, z1, xf0, yf0, zf1), FUNC(GradCoord)(seed, x1, y0, z1, xf1, yf0, zf1), xs), - FUNC(Lerp)(FUNC(GradCoord)(seed, x0, y1, z1, xf0, yf1, zf1), FUNC(GradCoord)(seed, x1, y1, z1, xf1, yf1, zf1), xs), ys), zs); -} - -static SIMDf VECTORCALL FUNC(SimplexSingle)(SIMDi seed, SIMDf x, SIMDf y, SIMDf z) -{ - SIMDf f = SIMDf_MUL(SIMDf_NUM(F3), SIMDf_ADD(SIMDf_ADD(x, y), z)); - SIMDf x0 = SIMDf_FLOOR(SIMDf_ADD(x, f)); - SIMDf y0 = SIMDf_FLOOR(SIMDf_ADD(y, f)); - SIMDf z0 = SIMDf_FLOOR(SIMDf_ADD(z, f)); - - SIMDi i = SIMDi_MUL(SIMDi_CONVERT_TO_INT(x0), SIMDi_NUM(xPrime)); - SIMDi j = SIMDi_MUL(SIMDi_CONVERT_TO_INT(y0), SIMDi_NUM(yPrime)); - SIMDi k = SIMDi_MUL(SIMDi_CONVERT_TO_INT(z0), SIMDi_NUM(zPrime)); - - SIMDf g = SIMDf_MUL(SIMDf_NUM(G3), SIMDf_ADD(SIMDf_ADD(x0, y0), z0)); - x0 = SIMDf_SUB(x, SIMDf_SUB(x0, g)); - y0 = SIMDf_SUB(y, SIMDf_SUB(y0, g)); - z0 = SIMDf_SUB(z, SIMDf_SUB(z0, g)); - - MASK x0_ge_y0 = SIMDf_GREATER_EQUAL(x0, y0); - MASK y0_ge_z0 = SIMDf_GREATER_EQUAL(y0, z0); - MASK x0_ge_z0 = SIMDf_GREATER_EQUAL(x0, z0); - - MASK i1 = MASK_AND(x0_ge_y0, x0_ge_z0); - MASK j1 = MASK_AND_NOT(x0_ge_y0, y0_ge_z0); - MASK k1 = MASK_AND_NOT(x0_ge_z0, MASK_NOT(y0_ge_z0)); - - MASK i2 = MASK_OR(x0_ge_y0, x0_ge_z0); - MASK j2 = MASK_OR(MASK_NOT(x0_ge_y0), y0_ge_z0); - MASK k2 = MASK_NOT(MASK_AND(x0_ge_z0, y0_ge_z0)); - - SIMDf x1 = SIMDf_ADD(SIMDf_MASK_SUB(i1, x0, SIMDf_NUM(1)), SIMDf_NUM(G3)); - SIMDf y1 = SIMDf_ADD(SIMDf_MASK_SUB(j1, y0, SIMDf_NUM(1)), SIMDf_NUM(G3)); - SIMDf z1 = SIMDf_ADD(SIMDf_MASK_SUB(k1, z0, SIMDf_NUM(1)), SIMDf_NUM(G3)); - SIMDf x2 = SIMDf_ADD(SIMDf_MASK_SUB(i2, x0, SIMDf_NUM(1)), SIMDf_NUM(F3)); - SIMDf y2 = SIMDf_ADD(SIMDf_MASK_SUB(j2, y0, SIMDf_NUM(1)), SIMDf_NUM(F3)); - SIMDf z2 = SIMDf_ADD(SIMDf_MASK_SUB(k2, z0, SIMDf_NUM(1)), SIMDf_NUM(F3)); - SIMDf x3 = SIMDf_ADD(x0, SIMDf_NUM(G33)); - SIMDf y3 = SIMDf_ADD(y0, SIMDf_NUM(G33)); - SIMDf z3 = SIMDf_ADD(z0, SIMDf_NUM(G33)); - - SIMDf t0 = SIMDf_NMUL_ADD(z0, z0, SIMDf_NMUL_ADD(y0, y0, SIMDf_NMUL_ADD(x0, x0, SIMDf_NUM(0_6)))); - SIMDf t1 = SIMDf_NMUL_ADD(z1, z1, SIMDf_NMUL_ADD(y1, y1, SIMDf_NMUL_ADD(x1, x1, SIMDf_NUM(0_6)))); - SIMDf t2 = SIMDf_NMUL_ADD(z2, z2, SIMDf_NMUL_ADD(y2, y2, SIMDf_NMUL_ADD(x2, x2, SIMDf_NUM(0_6)))); - SIMDf t3 = SIMDf_NMUL_ADD(z3, z3, SIMDf_NMUL_ADD(y3, y3, SIMDf_NMUL_ADD(x3, x3, SIMDf_NUM(0_6)))); - - MASK n0 = SIMDf_GREATER_EQUAL(t0, SIMDf_NUM(0)); - MASK n1 = SIMDf_GREATER_EQUAL(t1, SIMDf_NUM(0)); - MASK n2 = SIMDf_GREATER_EQUAL(t2, SIMDf_NUM(0)); - MASK n3 = SIMDf_GREATER_EQUAL(t3, SIMDf_NUM(0)); - - t0 = SIMDf_MUL(t0, t0); - t1 = SIMDf_MUL(t1, t1); - t2 = SIMDf_MUL(t2, t2); - t3 = SIMDf_MUL(t3, t3); - - SIMDf v0 = SIMDf_MUL(SIMDf_MUL(t0, t0), FUNC(GradCoord)(seed, i, j, k, x0, y0, z0)); - SIMDf v1 = SIMDf_MUL(SIMDf_MUL(t1, t1), FUNC(GradCoord)(seed, SIMDi_MASK_ADD(i1, i, SIMDi_NUM(xPrime)), SIMDi_MASK_ADD(j1, j, SIMDi_NUM(yPrime)), SIMDi_MASK_ADD(k1, k, SIMDi_NUM(zPrime)), x1, y1, z1)); - SIMDf v2 = SIMDf_MUL(SIMDf_MUL(t2, t2), FUNC(GradCoord)(seed, SIMDi_MASK_ADD(i2, i, SIMDi_NUM(xPrime)), SIMDi_MASK_ADD(j2, j, SIMDi_NUM(yPrime)), SIMDi_MASK_ADD(k2, k, SIMDi_NUM(zPrime)), x2, y2, z2)); - SIMDf v3 = SIMDf_MASK(n3, SIMDf_MUL(SIMDf_MUL(t3, t3), FUNC(GradCoord)(seed, SIMDi_ADD(i, SIMDi_NUM(xPrime)), SIMDi_ADD(j, SIMDi_NUM(yPrime)), SIMDi_ADD(k, SIMDi_NUM(zPrime)), x3, y3, z3))); - - return SIMDf_MUL(SIMDf_NUM(32), SIMDf_MASK_ADD(n0, SIMDf_MASK_ADD(n1, SIMDf_MASK_ADD(n2, v3, v2), v1), v0)); -} - -static SIMDf VECTORCALL FUNC(CubicSingle)(SIMDi seed, SIMDf x, SIMDf y, SIMDf z) -{ - SIMDf xf1 = SIMDf_FLOOR(x); - SIMDf yf1 = SIMDf_FLOOR(y); - SIMDf zf1 = SIMDf_FLOOR(z); - - SIMDi x1 = SIMDi_MUL(SIMDi_CONVERT_TO_INT(xf1), SIMDi_NUM(xPrime)); - SIMDi y1 = SIMDi_MUL(SIMDi_CONVERT_TO_INT(yf1), SIMDi_NUM(yPrime)); - SIMDi z1 = SIMDi_MUL(SIMDi_CONVERT_TO_INT(zf1), SIMDi_NUM(zPrime)); - - SIMDi x0 = SIMDi_SUB(x1, SIMDi_NUM(xPrime)); - SIMDi y0 = SIMDi_SUB(y1, SIMDi_NUM(yPrime)); - SIMDi z0 = SIMDi_SUB(z1, SIMDi_NUM(zPrime)); - SIMDi x2 = SIMDi_ADD(x1, SIMDi_NUM(xPrime)); - SIMDi y2 = SIMDi_ADD(y1, SIMDi_NUM(yPrime)); - SIMDi z2 = SIMDi_ADD(z1, SIMDi_NUM(zPrime)); - SIMDi x3 = SIMDi_ADD(x2, SIMDi_NUM(xPrime)); - SIMDi y3 = SIMDi_ADD(y2, SIMDi_NUM(yPrime)); - SIMDi z3 = SIMDi_ADD(z2, SIMDi_NUM(zPrime)); - - SIMDf xs = SIMDf_SUB(x, xf1); - SIMDf ys = SIMDf_SUB(y, yf1); - SIMDf zs = SIMDf_SUB(z, zf1); - - return SIMDf_MUL(FUNC(CubicLerp)( - FUNC(CubicLerp)( - FUNC(CubicLerp)(FUNC(ValCoord)(seed, x0, y0, z0), FUNC(ValCoord)(seed, x1, y0, z0), FUNC(ValCoord)(seed, x2, y0, z0), FUNC(ValCoord)(seed, x3, y0, z0), xs), - FUNC(CubicLerp)(FUNC(ValCoord)(seed, x0, y1, z0), FUNC(ValCoord)(seed, x1, y1, z0), FUNC(ValCoord)(seed, x2, y1, z0), FUNC(ValCoord)(seed, x3, y1, z0), xs), - FUNC(CubicLerp)(FUNC(ValCoord)(seed, x0, y2, z0), FUNC(ValCoord)(seed, x1, y2, z0), FUNC(ValCoord)(seed, x2, y2, z0), FUNC(ValCoord)(seed, x3, y2, z0), xs), - FUNC(CubicLerp)(FUNC(ValCoord)(seed, x0, y3, z0), FUNC(ValCoord)(seed, x1, y3, z0), FUNC(ValCoord)(seed, x2, y3, z0), FUNC(ValCoord)(seed, x3, y3, z0), xs), - ys), - FUNC(CubicLerp)( - FUNC(CubicLerp)(FUNC(ValCoord)(seed, x0, y0, z1), FUNC(ValCoord)(seed, x1, y0, z1), FUNC(ValCoord)(seed, x2, y0, z1), FUNC(ValCoord)(seed, x3, y0, z1), xs), - FUNC(CubicLerp)(FUNC(ValCoord)(seed, x0, y1, z1), FUNC(ValCoord)(seed, x1, y1, z1), FUNC(ValCoord)(seed, x2, y1, z1), FUNC(ValCoord)(seed, x3, y1, z1), xs), - FUNC(CubicLerp)(FUNC(ValCoord)(seed, x0, y2, z1), FUNC(ValCoord)(seed, x1, y2, z1), FUNC(ValCoord)(seed, x2, y2, z1), FUNC(ValCoord)(seed, x3, y2, z1), xs), - FUNC(CubicLerp)(FUNC(ValCoord)(seed, x0, y3, z1), FUNC(ValCoord)(seed, x1, y3, z1), FUNC(ValCoord)(seed, x2, y3, z1), FUNC(ValCoord)(seed, x3, y3, z1), xs), - ys), - FUNC(CubicLerp)( - FUNC(CubicLerp)(FUNC(ValCoord)(seed, x0, y0, z2), FUNC(ValCoord)(seed, x1, y0, z2), FUNC(ValCoord)(seed, x2, y0, z2), FUNC(ValCoord)(seed, x3, y0, z2), xs), - FUNC(CubicLerp)(FUNC(ValCoord)(seed, x0, y1, z2), FUNC(ValCoord)(seed, x1, y1, z2), FUNC(ValCoord)(seed, x2, y1, z2), FUNC(ValCoord)(seed, x3, y1, z2), xs), - FUNC(CubicLerp)(FUNC(ValCoord)(seed, x0, y2, z2), FUNC(ValCoord)(seed, x1, y2, z2), FUNC(ValCoord)(seed, x2, y2, z2), FUNC(ValCoord)(seed, x3, y2, z2), xs), - FUNC(CubicLerp)(FUNC(ValCoord)(seed, x0, y3, z2), FUNC(ValCoord)(seed, x1, y3, z2), FUNC(ValCoord)(seed, x2, y3, z2), FUNC(ValCoord)(seed, x3, y3, z2), xs), - ys), - FUNC(CubicLerp)( - FUNC(CubicLerp)(FUNC(ValCoord)(seed, x0, y0, z3), FUNC(ValCoord)(seed, x1, y0, z3), FUNC(ValCoord)(seed, x2, y0, z3), FUNC(ValCoord)(seed, x3, y0, z3), xs), - FUNC(CubicLerp)(FUNC(ValCoord)(seed, x0, y1, z3), FUNC(ValCoord)(seed, x1, y1, z3), FUNC(ValCoord)(seed, x2, y1, z3), FUNC(ValCoord)(seed, x3, y1, z3), xs), - FUNC(CubicLerp)(FUNC(ValCoord)(seed, x0, y2, z3), FUNC(ValCoord)(seed, x1, y2, z3), FUNC(ValCoord)(seed, x2, y2, z3), FUNC(ValCoord)(seed, x3, y2, z3), xs), - FUNC(CubicLerp)(FUNC(ValCoord)(seed, x0, y3, z3), FUNC(ValCoord)(seed, x1, y3, z3), FUNC(ValCoord)(seed, x2, y3, z3), FUNC(ValCoord)(seed, x3, y3, z3), xs), - ys), - zs), SIMDf_NUM(cubicBounding)); -} - -#define GRADIENT_COORD(_x,_y,_z)\ -SIMDi hash##_x##_y##_z = FUNC(HashHB)(seed, x##_x, y##_y, z##_z); \ -SIMDf x##_x##_y##_z = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(SIMDi_AND(hash##_x##_y##_z, SIMDi_NUM(bit10Mask))), SIMDf_NUM(511_5)); \ -SIMDf y##_x##_y##_z = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(SIMDi_AND(SIMDi_SHIFT_R(hash##_x##_y##_z, 10), SIMDi_NUM(bit10Mask))), SIMDf_NUM(511_5)); \ -SIMDf z##_x##_y##_z = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(SIMDi_AND(SIMDi_SHIFT_R(hash##_x##_y##_z, 20), SIMDi_NUM(bit10Mask))), SIMDf_NUM(511_5)); - -//SIMDf invMag##_x##_y##_z = SIMDf_MUL(SIMDf_NUM(cellJitter), SIMDf_INV_SQRT(SIMDf_MUL_ADD(x##_x##_y##_z, x##_x##_y##_z, SIMDf_MUL_ADD(y##_x##_y##_z, y##_x##_y##_z, SIMDf_MUL(z##_x##_y##_z, z##_x##_y##_z))))); -//x##_x##_y##_z = SIMDf_MUL(x##_x##_y##_z, invMag##_x##_y##_z); -//y##_x##_y##_z = SIMDf_MUL(y##_x##_y##_z, invMag##_x##_y##_z); -//z##_x##_y##_z = SIMDf_MUL(z##_x##_y##_z, invMag##_x##_y##_z); - -static void VECTORCALL FUNC(GradientPerturbSingle)(SIMDi seed, SIMDf perturbAmp, SIMDf perturbFrequency, SIMDf& x, SIMDf& y, SIMDf& z) -{ - SIMDf xf = SIMDf_MUL(x, perturbFrequency); - SIMDf yf = SIMDf_MUL(y, perturbFrequency); - SIMDf zf = SIMDf_MUL(z, perturbFrequency); - - SIMDf xs = SIMDf_FLOOR(xf); - SIMDf ys = SIMDf_FLOOR(yf); - SIMDf zs = SIMDf_FLOOR(zf); - - SIMDi x0 = SIMDi_MUL(SIMDi_CONVERT_TO_INT(xs), SIMDi_NUM(xPrime)); - SIMDi y0 = SIMDi_MUL(SIMDi_CONVERT_TO_INT(ys), SIMDi_NUM(yPrime)); - SIMDi z0 = SIMDi_MUL(SIMDi_CONVERT_TO_INT(zs), SIMDi_NUM(zPrime)); - SIMDi x1 = SIMDi_ADD(x0, SIMDi_NUM(xPrime)); - SIMDi y1 = SIMDi_ADD(y0, SIMDi_NUM(yPrime)); - SIMDi z1 = SIMDi_ADD(z0, SIMDi_NUM(zPrime)); - - xs = FUNC(InterpQuintic)(SIMDf_SUB(xf, xs)); - ys = FUNC(InterpQuintic)(SIMDf_SUB(yf, ys)); - zs = FUNC(InterpQuintic)(SIMDf_SUB(zf, zs)); - - GRADIENT_COORD(0, 0, 0); - GRADIENT_COORD(0, 0, 1); - GRADIENT_COORD(0, 1, 0); - GRADIENT_COORD(0, 1, 1); - GRADIENT_COORD(1, 0, 0); - GRADIENT_COORD(1, 0, 1); - GRADIENT_COORD(1, 1, 0); - GRADIENT_COORD(1, 1, 1); - - SIMDf x0y = FUNC(Lerp)(FUNC(Lerp)(x000, x100, xs), FUNC(Lerp)(x010, x110, xs), ys); - SIMDf y0y = FUNC(Lerp)(FUNC(Lerp)(y000, y100, xs), FUNC(Lerp)(y010, y110, xs), ys); - SIMDf z0y = FUNC(Lerp)(FUNC(Lerp)(z000, z100, xs), FUNC(Lerp)(z010, z110, xs), ys); - - SIMDf x1y = FUNC(Lerp)(FUNC(Lerp)(x001, x101, xs), FUNC(Lerp)(x011, x111, xs), ys); - SIMDf y1y = FUNC(Lerp)(FUNC(Lerp)(y001, y101, xs), FUNC(Lerp)(y011, y111, xs), ys); - SIMDf z1y = FUNC(Lerp)(FUNC(Lerp)(z001, z101, xs), FUNC(Lerp)(z011, z111, xs), ys); - - x = SIMDf_MUL_ADD(FUNC(Lerp)(x0y, x1y, zs), perturbAmp, x); - y = SIMDf_MUL_ADD(FUNC(Lerp)(y0y, y1y, zs), perturbAmp, y); - z = SIMDf_MUL_ADD(FUNC(Lerp)(z0y, z1y, zs), perturbAmp, z); -} - -SIMD_LEVEL_CLASS::FASTNOISE_SIMD_CLASS(SIMD_LEVEL)(int seed) -{ - m_seed = seed; - m_fractalBounding = CalculateFractalBounding(m_octaves, m_gain); - m_perturbFractalBounding = CalculateFractalBounding(m_perturbOctaves, m_perturbGain); - FUNC(InitSIMDValues)(); - s_currentSIMDLevel = SIMD_LEVEL; -} - -int SIMD_LEVEL_CLASS::AlignedSize(int size) -{ -#ifdef FN_ALIGNED_SETS - // size must be a multiple of VECTOR_SIZE (8) - if ((size & (VECTOR_SIZE - 1)) != 0) - { - size &= ~(VECTOR_SIZE - 1); - size += VECTOR_SIZE; - } -#endif - return size; -} - -float* SIMD_LEVEL_CLASS::GetEmptySet(int size) -{ - size = AlignedSize(size); - - float* noiseSet; - SIMD_ALLOCATE_SET(noiseSet, size); - - return noiseSet; -} - -#define AXIS_RESET(_zSize, _start) for (int _i = (_zSize) * (_start); _i < VECTOR_SIZE; _i+=(_zSize)){\ -MASK _zReset = SIMDi_GREATER_THAN(z, zEndV);\ -y = SIMDi_MASK_ADD(_zReset, y, SIMDi_NUM(1));\ -z = SIMDi_MASK_SUB(_zReset, z, zSizeV);\ -\ -MASK _yReset = SIMDi_GREATER_THAN(y, yEndV);\ -x = SIMDi_MASK_ADD(_yReset, x, SIMDi_NUM(1));\ -y = SIMDi_MASK_SUB(_yReset, y, ySizeV);} - -#ifdef FN_ALIGNED_SETS -#define STORE_LAST_RESULT(_dest, _source) SIMDf_STORE(_dest, _source) -#else -#include -#define STORE_LAST_RESULT(_dest, _source) std::memcpy(_dest, &_source, (maxIndex - index) * 4) -#endif - -#define INIT_PERTURB_VALUES() \ -SIMDf perturbAmpV, perturbFreqV, perturbLacunarityV, perturbGainV, perturbNormaliseLengthV;\ -switch (m_perturbType)\ -{\ -case None:\ - break;\ -case Gradient_Normalise:\ - perturbNormaliseLengthV = SIMDf_SET(m_perturbNormaliseLength*m_frequency);\ -case Gradient:\ - perturbAmpV = SIMDf_SET(m_perturbAmp);\ - perturbFreqV = SIMDf_SET(m_perturbFrequency);\ - break;\ -case GradientFractal_Normalise:\ - perturbNormaliseLengthV = SIMDf_SET(m_perturbNormaliseLength*m_frequency);\ -case GradientFractal:\ - perturbAmpV = SIMDf_SET(m_perturbAmp*m_fractalBounding);\ - perturbFreqV = SIMDf_SET(m_perturbFrequency);\ - perturbLacunarityV = SIMDf_SET(m_perturbLacunarity);\ - perturbGainV = SIMDf_SET(m_perturbGain);\ - break;\ -case Normalise:\ - perturbNormaliseLengthV = SIMDf_SET(m_perturbNormaliseLength*m_frequency);\ - break;\ -} - -#define PERTURB_SWITCH()\ -switch (m_perturbType)\ -{\ -case None:\ - break;\ -case Gradient:\ - FUNC(GradientPerturbSingle)(SIMDi_SUB(seedV, SIMDi_NUM(1)), perturbAmpV, perturbFreqV, xF, yF, zF); \ - break; \ -case GradientFractal:\ - {\ - SIMDi seedF = SIMDi_SUB(seedV, SIMDi_NUM(1));\ - SIMDf freqF = perturbFreqV;\ - SIMDf ampF = perturbAmpV;\ - \ - FUNC(GradientPerturbSingle)(seedF, ampF, freqF, xF, yF, zF);\ - \ - int octaveIndex = 0;\ - \ - while (++octaveIndex < m_perturbOctaves)\ - {\ - freqF = SIMDf_MUL(freqF, perturbLacunarityV);\ - seedF = SIMDi_SUB(seedF, SIMDi_NUM(1));\ - ampF = SIMDf_MUL(ampF, perturbGainV);\ - \ - FUNC(GradientPerturbSingle)(seedF, ampF, freqF, xF, yF, zF);\ - }}\ - break;\ -case Gradient_Normalise:\ - FUNC(GradientPerturbSingle)(SIMDi_SUB(seedV, SIMDi_NUM(1)), perturbAmpV, perturbFreqV, xF, yF, zF); \ -case Normalise:\ - {\ - SIMDf invMag = SIMDf_MUL(perturbNormaliseLengthV, SIMDf_INV_SQRT(SIMDf_MUL_ADD(xF, xF, SIMDf_MUL_ADD(yF, yF, SIMDf_MUL(zF, zF)))));\ - xF = SIMDf_MUL(xF, invMag);\ - yF = SIMDf_MUL(yF, invMag);\ - zF = SIMDf_MUL(zF, invMag);\ - }break;\ -case GradientFractal_Normalise:\ - {\ - SIMDi seedF = SIMDi_SUB(seedV, SIMDi_NUM(1));\ - SIMDf freqF = perturbFreqV;\ - SIMDf ampF = perturbAmpV;\ - \ - FUNC(GradientPerturbSingle)(seedF, ampF, freqF, xF, yF, zF);\ - \ - int octaveIndex = 0;\ - \ - while (++octaveIndex < m_perturbOctaves)\ - {\ - freqF = SIMDf_MUL(freqF, perturbLacunarityV);\ - seedF = SIMDi_SUB(seedF, SIMDi_NUM(1));\ - ampF = SIMDf_MUL(ampF, perturbGainV);\ - \ - FUNC(GradientPerturbSingle)(seedF, ampF, freqF, xF, yF, zF);\ - }\ - SIMDf invMag = SIMDf_MUL(perturbNormaliseLengthV, SIMDf_INV_SQRT(SIMDf_MUL_ADD(xF, xF, SIMDf_MUL_ADD(yF, yF, SIMDf_MUL(zF, zF)))));\ - xF = SIMDf_MUL(xF, invMag);\ - yF = SIMDf_MUL(yF, invMag);\ - zF = SIMDf_MUL(zF, invMag);\ - }break;\ -} - -#define SET_BUILDER(f)\ -if ((zSize & (VECTOR_SIZE - 1)) == 0)\ -{\ - SIMDi yBase = SIMDi_SET(yStart);\ - SIMDi zBase = SIMDi_ADD(SIMDi_NUM(incremental), SIMDi_SET(zStart));\ - \ - SIMDi x = SIMDi_SET(xStart);\ - \ - int index = 0;\ - \ - for (int ix = 0; ix < xSize; ix++)\ - {\ - SIMDf xf = SIMDf_MUL(SIMDf_CONVERT_TO_FLOAT(x), xFreqV);\ - SIMDi y = yBase;\ - \ - for (int iy = 0; iy < ySize; iy++)\ - {\ - SIMDf yf = SIMDf_MUL(SIMDf_CONVERT_TO_FLOAT(y), yFreqV);\ - SIMDi z = zBase;\ - SIMDf xF = xf;\ - SIMDf yF = yf;\ - SIMDf zF = SIMDf_MUL(SIMDf_CONVERT_TO_FLOAT(z), zFreqV);\ - \ - PERTURB_SWITCH()\ - SIMDf result;\ - f;\ - SIMDf_STORE(&noiseSet[index], result);\ - \ - int iz = VECTOR_SIZE;\ - while (iz < zSize)\ - {\ - z = SIMDi_ADD(z, SIMDi_NUM(vectorSize));\ - index += VECTOR_SIZE;\ - iz += VECTOR_SIZE;\ - xF = xf;\ - yF = yf;\ - zF = SIMDf_MUL(SIMDf_CONVERT_TO_FLOAT(z), zFreqV);\ - \ - PERTURB_SWITCH()\ - SIMDf result;\ - f;\ - SIMDf_STORE(&noiseSet[index], result);\ - }\ - index += VECTOR_SIZE;\ - y = SIMDi_ADD(y, SIMDi_NUM(1));\ - }\ - x = SIMDi_ADD(x, SIMDi_NUM(1));\ - }\ -}\ -else\ -{\ - SIMDi ySizeV = SIMDi_SET(ySize); \ - SIMDi zSizeV = SIMDi_SET(zSize); \ - \ - SIMDi yEndV = SIMDi_SET(yStart + ySize - 1); \ - SIMDi zEndV = SIMDi_SET(zStart + zSize - 1); \ - \ - SIMDi x = SIMDi_SET(xStart); \ - SIMDi y = SIMDi_SET(yStart); \ - SIMDi z = SIMDi_ADD(SIMDi_SET(zStart), SIMDi_NUM(incremental)); \ - AXIS_RESET(zSize, 1)\ - \ - int index = 0; \ - int maxIndex = xSize * ySize * zSize; \ - \ - for (; index < maxIndex - VECTOR_SIZE; index += VECTOR_SIZE)\ - {\ - SIMDf xF = SIMDf_MUL(SIMDf_CONVERT_TO_FLOAT(x), xFreqV);\ - SIMDf yF = SIMDf_MUL(SIMDf_CONVERT_TO_FLOAT(y), yFreqV);\ - SIMDf zF = SIMDf_MUL(SIMDf_CONVERT_TO_FLOAT(z), zFreqV);\ - \ - PERTURB_SWITCH()\ - SIMDf result;\ - f;\ - SIMDf_STORE(&noiseSet[index], result);\ - \ - z = SIMDi_ADD(z, SIMDi_NUM(vectorSize));\ - \ - AXIS_RESET(zSize, 0)\ - }\ - \ - SIMDf xF = SIMDf_MUL(SIMDf_CONVERT_TO_FLOAT(x), xFreqV);\ - SIMDf yF = SIMDf_MUL(SIMDf_CONVERT_TO_FLOAT(y), yFreqV);\ - SIMDf zF = SIMDf_MUL(SIMDf_CONVERT_TO_FLOAT(z), zFreqV);\ - \ - PERTURB_SWITCH()\ - SIMDf result;\ - f;\ - STORE_LAST_RESULT(&noiseSet[index], result);\ -} - -// FBM SINGLE -#define FBM_SINGLE(f)\ - SIMDi seedF = seedV;\ - \ - result = FUNC(f##Single)(seedF, xF, yF, zF);\ - \ - SIMDf ampF = SIMDf_NUM(1);\ - int octaveIndex = 0;\ - \ - while (++octaveIndex < m_octaves)\ - {\ - xF = SIMDf_MUL(xF, lacunarityV);\ - yF = SIMDf_MUL(yF, lacunarityV);\ - zF = SIMDf_MUL(zF, lacunarityV);\ - seedF = SIMDi_ADD(seedF, SIMDi_NUM(1));\ - \ - ampF = SIMDf_MUL(ampF, gainV);\ - result = SIMDf_MUL_ADD(FUNC(f##Single)(seedF, xF, yF, zF), ampF, result);\ - }\ - result = SIMDf_MUL(result, fractalBoundingV) - -// BILLOW SINGLE -#define BILLOW_SINGLE(f)\ - SIMDi seedF = seedV;\ - \ - result = SIMDf_MUL_SUB(SIMDf_ABS(FUNC(f##Single)(seedF, xF, yF, zF)), SIMDf_NUM(2), SIMDf_NUM(1));\ - \ - SIMDf ampF = SIMDf_NUM(1);\ - int octaveIndex = 0;\ - \ - while (++octaveIndex < m_octaves)\ - {\ - xF = SIMDf_MUL(xF, lacunarityV);\ - yF = SIMDf_MUL(yF, lacunarityV);\ - zF = SIMDf_MUL(zF, lacunarityV);\ - seedF = SIMDi_ADD(seedF, SIMDi_NUM(1));\ - \ - ampF = SIMDf_MUL(ampF, gainV);\ - result = SIMDf_MUL_ADD(SIMDf_MUL_SUB(SIMDf_ABS(FUNC(f##Single)(seedF, xF, yF, zF)), SIMDf_NUM(2), SIMDf_NUM(1)), ampF, result);\ - }\ - result = SIMDf_MUL(result, fractalBoundingV) - -// RIGIDMULTI SINGLE -#define RIGIDMULTI_SINGLE(f)\ - SIMDi seedF = seedV;\ - \ - result = SIMDf_SUB(SIMDf_NUM(1), SIMDf_ABS(FUNC(f##Single)(seedF, xF, yF, zF)));\ - \ - SIMDf ampF = SIMDf_NUM(1);\ - int octaveIndex = 0;\ - \ - while (++octaveIndex < m_octaves)\ - {\ - xF = SIMDf_MUL(xF, lacunarityV);\ - yF = SIMDf_MUL(yF, lacunarityV);\ - zF = SIMDf_MUL(zF, lacunarityV);\ - seedF = SIMDi_ADD(seedF, SIMDi_NUM(1));\ - \ - ampF = SIMDf_MUL(ampF, gainV);\ - result = SIMDf_NMUL_ADD(SIMDf_SUB(SIMDf_NUM(1), SIMDf_ABS(FUNC(f##Single)(seedF, xF, yF, zF))), ampF, result);\ - } - -#define FILL_SET(func) \ -void SIMD_LEVEL_CLASS::Fill##func##Set(float* noiseSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier)\ -{\ - assert(noiseSet);\ - SIMD_ZERO_ALL();\ - SIMDi seedV = SIMDi_SET(m_seed); \ - INIT_PERTURB_VALUES();\ - \ - scaleModifier *= m_frequency;\ - \ - SIMDf xFreqV = SIMDf_SET(scaleModifier * m_xScale);\ - SIMDf yFreqV = SIMDf_SET(scaleModifier * m_yScale);\ - SIMDf zFreqV = SIMDf_SET(scaleModifier * m_zScale);\ - \ - SET_BUILDER(result = FUNC(func##Single)(seedV, xF, yF, zF))\ - \ - SIMD_ZERO_ALL();\ -} - -#define FILL_FRACTAL_SET(func) \ -void SIMD_LEVEL_CLASS::Fill##func##FractalSet(float* noiseSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier)\ -{\ - assert(noiseSet);\ - SIMD_ZERO_ALL();\ - \ - SIMDi seedV = SIMDi_SET(m_seed);\ - SIMDf lacunarityV = SIMDf_SET(m_lacunarity);\ - SIMDf gainV = SIMDf_SET(m_gain);\ - SIMDf fractalBoundingV = SIMDf_SET(m_fractalBounding);\ - INIT_PERTURB_VALUES();\ - \ - scaleModifier *= m_frequency;\ - \ - SIMDf xFreqV = SIMDf_SET(scaleModifier * m_xScale);\ - SIMDf yFreqV = SIMDf_SET(scaleModifier * m_yScale);\ - SIMDf zFreqV = SIMDf_SET(scaleModifier * m_zScale);\ - \ - switch(m_fractalType)\ - {\ - case FBM:\ - SET_BUILDER(FBM_SINGLE(func))\ - break;\ - case Billow:\ - SET_BUILDER(BILLOW_SINGLE(func))\ - break;\ - case RigidMulti:\ - SET_BUILDER(RIGIDMULTI_SINGLE(func))\ - break;\ - }\ - SIMD_ZERO_ALL();\ -} - -FILL_SET(Value) -FILL_FRACTAL_SET(Value) - -FILL_SET(Perlin) -FILL_FRACTAL_SET(Perlin) - -FILL_SET(Simplex) -FILL_FRACTAL_SET(Simplex) - -//FILL_SET(WhiteNoise) - -FILL_SET(Cubic) -FILL_FRACTAL_SET(Cubic) - -#ifdef FN_ALIGNED_SETS -#define SIZE_MASK -#define SAFE_LAST(f) -#else -#define SIZE_MASK & ~(VECTOR_SIZE - 1) -#define SAFE_LAST(f)\ -if (loopMax != vectorSet->size)\ -{\ - std::size_t remaining = (vectorSet->size - loopMax) * 4;\ - \ - SIMDf xF = SIMDf_LOAD(&vectorSet->xSet[loopMax]);\ - SIMDf yF = SIMDf_LOAD(&vectorSet->ySet[loopMax]);\ - SIMDf zF = SIMDf_LOAD(&vectorSet->zSet[loopMax]);\ - \ - xF = SIMDf_MUL_ADD(xF, xFreqV, xOffsetV);\ - yF = SIMDf_MUL_ADD(yF, yFreqV, yOffsetV);\ - zF = SIMDf_MUL_ADD(zF, zFreqV, zOffsetV);\ - \ - SIMDf result;\ - f;\ - std::memcpy(&noiseSet[index], &result, remaining);\ -} -#endif - -#define VECTOR_SET_BUILDER(f)\ -while (index < loopMax)\ -{\ - SIMDf xF = SIMDf_MUL_ADD(SIMDf_LOAD(&vectorSet->xSet[index]), xFreqV, xOffsetV);\ - SIMDf yF = SIMDf_MUL_ADD(SIMDf_LOAD(&vectorSet->ySet[index]), yFreqV, yOffsetV);\ - SIMDf zF = SIMDf_MUL_ADD(SIMDf_LOAD(&vectorSet->zSet[index]), zFreqV, zOffsetV);\ - \ - PERTURB_SWITCH()\ - SIMDf result;\ - f;\ - SIMDf_STORE(&noiseSet[index], result);\ - index += VECTOR_SIZE;\ -}\ -SAFE_LAST(f) - -#define FILL_VECTOR_SET(func)\ -void SIMD_LEVEL_CLASS::Fill##func##Set(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset, float yOffset, float zOffset)\ -{\ - assert(noiseSet);\ - assert(vectorSet);\ - assert(vectorSet->size >= 0);\ - SIMD_ZERO_ALL();\ - \ - SIMDi seedV = SIMDi_SET(m_seed);\ - SIMDf xFreqV = SIMDf_SET(m_frequency * m_xScale);\ - SIMDf yFreqV = SIMDf_SET(m_frequency * m_yScale);\ - SIMDf zFreqV = SIMDf_SET(m_frequency * m_zScale);\ - SIMDf xOffsetV = SIMDf_MUL(SIMDf_SET(xOffset), xFreqV);\ - SIMDf yOffsetV = SIMDf_MUL(SIMDf_SET(yOffset), yFreqV);\ - SIMDf zOffsetV = SIMDf_MUL(SIMDf_SET(zOffset), zFreqV);\ - INIT_PERTURB_VALUES();\ - \ - int index = 0;\ - int loopMax = vectorSet->size SIZE_MASK;\ - \ - VECTOR_SET_BUILDER(result = FUNC(func##Single)(seedV, xF, yF, zF))\ - SIMD_ZERO_ALL();\ -} - -#define FILL_FRACTAL_VECTOR_SET(func)\ -void SIMD_LEVEL_CLASS::Fill##func##FractalSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset, float yOffset, float zOffset)\ -{\ - assert(noiseSet);\ - assert(vectorSet);\ - assert(vectorSet->size >= 0);\ - SIMD_ZERO_ALL();\ - \ - SIMDi seedV = SIMDi_SET(m_seed);\ - SIMDf lacunarityV = SIMDf_SET(m_lacunarity);\ - SIMDf gainV = SIMDf_SET(m_gain);\ - SIMDf fractalBoundingV = SIMDf_SET(m_fractalBounding);\ - SIMDf xFreqV = SIMDf_SET(m_frequency * m_xScale);\ - SIMDf yFreqV = SIMDf_SET(m_frequency * m_yScale);\ - SIMDf zFreqV = SIMDf_SET(m_frequency * m_zScale);\ - SIMDf xOffsetV = SIMDf_MUL(SIMDf_SET(xOffset), xFreqV);\ - SIMDf yOffsetV = SIMDf_MUL(SIMDf_SET(yOffset), yFreqV);\ - SIMDf zOffsetV = SIMDf_MUL(SIMDf_SET(zOffset), zFreqV);\ - INIT_PERTURB_VALUES();\ - \ - int index = 0;\ - int loopMax = vectorSet->size SIZE_MASK;\ - \ - switch(m_fractalType)\ - {\ - case FBM:\ - VECTOR_SET_BUILDER(FBM_SINGLE(func))\ - break;\ - case Billow:\ - VECTOR_SET_BUILDER(BILLOW_SINGLE(func))\ - break;\ - case RigidMulti:\ - VECTOR_SET_BUILDER(RIGIDMULTI_SINGLE(func))\ - break;\ - }\ - SIMD_ZERO_ALL();\ -} - - FILL_VECTOR_SET(Value) - FILL_FRACTAL_VECTOR_SET(Value) - - FILL_VECTOR_SET(Perlin) - FILL_FRACTAL_VECTOR_SET(Perlin) - - FILL_VECTOR_SET(Simplex) - FILL_FRACTAL_VECTOR_SET(Simplex) - - FILL_VECTOR_SET(WhiteNoise) - - FILL_VECTOR_SET(Cubic) - FILL_FRACTAL_VECTOR_SET(Cubic) - - void SIMD_LEVEL_CLASS::FillWhiteNoiseSet(float* noiseSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier) -{ - assert(noiseSet); - SIMD_ZERO_ALL(); - SIMDi seedV = SIMDi_SET(m_seed); - - if ((zSize & (VECTOR_SIZE - 1)) == 0) - { - SIMDi x = SIMDi_MUL(SIMDi_SET(xStart), SIMDi_NUM(xPrime)); - SIMDi yBase = SIMDi_MUL(SIMDi_SET(yStart), SIMDi_NUM(yPrime)); - SIMDi zBase = SIMDi_MUL(SIMDi_ADD(SIMDi_NUM(incremental), SIMDi_SET(zStart)), SIMDi_NUM(zPrime)); - - SIMDi zStep = SIMDi_MUL(SIMDi_NUM(vectorSize), SIMDi_NUM(zPrime)); - - int index = 0; - - for (int ix = 0; ix < xSize; ix++) - { - SIMDi y = yBase; - - for (int iy = 0; iy < ySize; iy++) - { - SIMDi z = zBase; - - SIMDf_STORE(&noiseSet[index], FUNC(ValCoord)(seedV, x, y, z)); - - int iz = VECTOR_SIZE; - while (iz < zSize) - { - z = SIMDi_ADD(z, zStep); - index += VECTOR_SIZE; - iz += VECTOR_SIZE; - - SIMDf_STORE(&noiseSet[index], FUNC(ValCoord)(seedV, x, y, z)); - } - index += VECTOR_SIZE; - y = SIMDi_ADD(y, SIMDi_NUM(yPrime)); - } - x = SIMDi_ADD(x, SIMDi_NUM(xPrime)); - } - } - else - { - SIMDi ySizeV = SIMDi_SET(ySize); - SIMDi zSizeV = SIMDi_SET(zSize); - - SIMDi yEndV = SIMDi_SET(yStart + ySize - 1); - SIMDi zEndV = SIMDi_SET(zStart + zSize - 1); - - SIMDi x = SIMDi_SET(xStart); - SIMDi y = SIMDi_SET(yStart); - SIMDi z = SIMDi_ADD(SIMDi_SET(zStart), SIMDi_NUM(incremental)); - AXIS_RESET(zSize, 1); - - int index = 0; - int maxIndex = xSize * ySize * zSize; - - for (; index < maxIndex - VECTOR_SIZE; index += VECTOR_SIZE) - { - SIMDf_STORE(&noiseSet[index], FUNC(ValCoord)(seedV, SIMDi_MUL(x, SIMDi_NUM(xPrime)), SIMDi_MUL(y, SIMDi_NUM(yPrime)), SIMDi_MUL(z, SIMDi_NUM(zPrime)))); - - z = SIMDi_ADD(z, SIMDi_NUM(vectorSize)); - - AXIS_RESET(zSize, 0); - } - SIMDf result = FUNC(ValCoord)(seedV, SIMDi_MUL(x, SIMDi_NUM(xPrime)), SIMDi_MUL(y, SIMDi_NUM(yPrime)), SIMDi_MUL(z, SIMDi_NUM(zPrime))); - STORE_LAST_RESULT(&noiseSet[index], result); - } - SIMD_ZERO_ALL(); -} - -#define Euclidean_DISTANCE(_x, _y, _z) SIMDf_MUL_ADD(_x, _x, SIMDf_MUL_ADD(_y, _y, SIMDf_MUL(_z, _z))) -#define Manhattan_DISTANCE(_x, _y, _z) SIMDf_ADD(SIMDf_ADD(SIMDf_ABS(_x), SIMDf_ABS(_y)), SIMDf_ABS(_z)) -#define Natural_DISTANCE(_x, _y, _z) SIMDf_ADD(Euclidean_DISTANCE(_x,_y,_z), Manhattan_DISTANCE(_x,_y,_z)) - -#define Distance2_RETURN(_distance, _distance2) (_distance2) -#define Distance2Add_RETURN(_distance, _distance2) SIMDf_ADD(_distance, _distance2) -#define Distance2Sub_RETURN(_distance, _distance2) SIMDf_SUB(_distance2, _distance) -#define Distance2Mul_RETURN(_distance, _distance2) SIMDf_MUL(_distance, _distance2) -#define Distance2Div_RETURN(_distance, _distance2) SIMDf_DIV(_distance, _distance2) - -#define CELLULAR_VALUE_SINGLE(distanceFunc)\ -static SIMDf VECTORCALL FUNC(CellularValue##distanceFunc##Single)(SIMDi seed, SIMDf x, SIMDf y, SIMDf z, SIMDf cellJitter)\ -{\ - SIMDf distance = SIMDf_NUM(999999);\ - SIMDf cellValue = SIMDf_UNDEFINED();\ - \ - SIMDi xc = SIMDi_SUB(SIMDi_CONVERT_TO_INT(x), SIMDi_NUM(1));\ - SIMDi ycBase = SIMDi_SUB(SIMDi_CONVERT_TO_INT(y), SIMDi_NUM(1));\ - SIMDi zcBase = SIMDi_SUB(SIMDi_CONVERT_TO_INT(z), SIMDi_NUM(1));\ - \ - SIMDf xcf = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(xc), x);\ - SIMDf ycfBase = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(ycBase), y);\ - SIMDf zcfBase = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(zcBase), z);\ - \ - xc = SIMDi_MUL(xc, SIMDi_NUM(xPrime));\ - ycBase = SIMDi_MUL(ycBase, SIMDi_NUM(yPrime));\ - zcBase = SIMDi_MUL(zcBase, SIMDi_NUM(zPrime));\ - \ - for (int xi = 0; xi < 3; xi++)\ - {\ - SIMDf ycf = ycfBase;\ - SIMDi yc = ycBase;\ - for (int yi = 0; yi < 3; yi++)\ - {\ - SIMDf zcf = zcfBase;\ - SIMDi zc = zcBase;\ - for (int zi = 0; zi < 3; zi++)\ - {\ - SIMDi hash = FUNC(HashHB)(seed, xc, yc, zc);\ - SIMDf xd = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(SIMDi_AND(hash, SIMDi_NUM(bit10Mask))), SIMDf_NUM(511_5));\ - SIMDf yd = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(SIMDi_AND(SIMDi_SHIFT_R(hash,10), SIMDi_NUM(bit10Mask))), SIMDf_NUM(511_5));\ - SIMDf zd = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(SIMDi_AND(SIMDi_SHIFT_R(hash,20), SIMDi_NUM(bit10Mask))), SIMDf_NUM(511_5));\ - \ - SIMDf invMag = SIMDf_MUL(cellJitter, SIMDf_INV_SQRT(SIMDf_MUL_ADD(xd, xd, SIMDf_MUL_ADD(yd, yd, SIMDf_MUL(zd, zd)))));\ - \ - xd = SIMDf_MUL_ADD(xd, invMag, xcf);\ - yd = SIMDf_MUL_ADD(yd, invMag, ycf);\ - zd = SIMDf_MUL_ADD(zd, invMag, zcf);\ - \ - SIMDf newCellValue = SIMDf_MUL(SIMDf_NUM(hash2Float), SIMDf_CONVERT_TO_FLOAT(hash));\ - SIMDf newDistance = distanceFunc##_DISTANCE(xd, yd, zd);\ - \ - MASK closer = SIMDf_LESS_THAN(newDistance, distance);\ - \ - distance = SIMDf_MIN(newDistance, distance);\ - cellValue = SIMDf_BLENDV(cellValue, newCellValue, closer);\ - \ - zcf = SIMDf_ADD(zcf, SIMDf_NUM(1));\ - zc = SIMDi_ADD(zc, SIMDi_NUM(zPrime));\ - }\ - ycf = SIMDf_ADD(ycf, SIMDf_NUM(1));\ - yc = SIMDi_ADD(yc, SIMDi_NUM(yPrime));\ - }\ - xcf = SIMDf_ADD(xcf, SIMDf_NUM(1));\ - xc = SIMDi_ADD(xc, SIMDi_NUM(xPrime));\ - }\ - \ - return cellValue;\ -} - -struct NoiseLookupSettings -{ - FastNoiseSIMD::NoiseType type; - SIMDf frequency; - FastNoiseSIMD::FractalType fractalType; - int fractalOctaves; - SIMDf fractalLacunarity; - SIMDf fractalGain; - SIMDf fractalBounding; -}; - -#define CELLULAR_LOOKUP_FRACTAL_VALUE(noiseType){\ -SIMDf lacunarityV = noiseLookupSettings.fractalLacunarity;\ -SIMDf gainV = noiseLookupSettings.fractalGain;\ -SIMDf fractalBoundingV = noiseLookupSettings.fractalBounding;\ -int m_octaves = noiseLookupSettings.fractalOctaves;\ -switch(noiseLookupSettings.fractalType)\ -{\ - case FastNoiseSIMD::FBM:\ - {FBM_SINGLE(noiseType);}\ - break;\ - case FastNoiseSIMD::Billow:\ - {BILLOW_SINGLE(noiseType);}\ - break;\ - case FastNoiseSIMD::RigidMulti:\ - {RIGIDMULTI_SINGLE(noiseType);}\ - break;\ -}}\ - -#define CELLULAR_LOOKUP_SINGLE(distanceFunc)\ -static SIMDf VECTORCALL FUNC(CellularLookup##distanceFunc##Single)(SIMDi seedV, SIMDf x, SIMDf y, SIMDf z, SIMDf cellJitter, const NoiseLookupSettings& noiseLookupSettings)\ -{\ - SIMDf distance = SIMDf_NUM(999999);\ - SIMDf xCell = SIMDf_UNDEFINED();\ - SIMDf yCell = SIMDf_UNDEFINED();\ - SIMDf zCell = SIMDf_UNDEFINED();\ - \ - SIMDi xc = SIMDi_SUB(SIMDi_CONVERT_TO_INT(x), SIMDi_NUM(1));\ - SIMDi ycBase = SIMDi_SUB(SIMDi_CONVERT_TO_INT(y), SIMDi_NUM(1));\ - SIMDi zcBase = SIMDi_SUB(SIMDi_CONVERT_TO_INT(z), SIMDi_NUM(1));\ - \ - SIMDf xcf = SIMDf_CONVERT_TO_FLOAT(xc);\ - SIMDf ycfBase = SIMDf_CONVERT_TO_FLOAT(ycBase);\ - SIMDf zcfBase = SIMDf_CONVERT_TO_FLOAT(zcBase);\ - \ - xc = SIMDi_MUL(xc, SIMDi_NUM(xPrime));\ - ycBase = SIMDi_MUL(ycBase, SIMDi_NUM(yPrime));\ - zcBase = SIMDi_MUL(zcBase, SIMDi_NUM(zPrime));\ - \ - for (int xi = 0; xi < 3; xi++)\ - {\ - SIMDf ycf = ycfBase;\ - SIMDi yc = ycBase;\ - SIMDf xLocal = SIMDf_SUB(xcf, x);\ - for (int yi = 0; yi < 3; yi++)\ - {\ - SIMDf zcf = zcfBase;\ - SIMDi zc = zcBase;\ - SIMDf yLocal = SIMDf_SUB(ycf, y);\ - for (int zi = 0; zi < 3; zi++)\ - {\ - SIMDf zLocal = SIMDf_SUB(zcf, z);\ - \ - SIMDi hash = FUNC(HashHB)(seedV, xc, yc, zc);\ - SIMDf xd = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(SIMDi_AND(hash, SIMDi_NUM(bit10Mask))), SIMDf_NUM(511_5));\ - SIMDf yd = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(SIMDi_AND(SIMDi_SHIFT_R(hash,10), SIMDi_NUM(bit10Mask))), SIMDf_NUM(511_5));\ - SIMDf zd = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(SIMDi_AND(SIMDi_SHIFT_R(hash,20), SIMDi_NUM(bit10Mask))), SIMDf_NUM(511_5));\ - \ - SIMDf invMag = SIMDf_MUL(cellJitter, SIMDf_INV_SQRT(SIMDf_MUL_ADD(xd, xd, SIMDf_MUL_ADD(yd, yd, SIMDf_MUL(zd, zd)))));\ - \ - SIMDf xCellNew = SIMDf_MUL(xd, invMag);\ - SIMDf yCellNew = SIMDf_MUL(yd, invMag);\ - SIMDf zCellNew = SIMDf_MUL(zd, invMag);\ - \ - xd = SIMDf_ADD(xCellNew, xLocal);\ - yd = SIMDf_ADD(yCellNew, yLocal);\ - zd = SIMDf_ADD(zCellNew, zLocal);\ - \ - xCellNew = SIMDf_ADD(xCellNew, xcf); \ - yCellNew = SIMDf_ADD(yCellNew, ycf); \ - zCellNew = SIMDf_ADD(zCellNew, zcf); \ - \ - SIMDf newDistance = distanceFunc##_DISTANCE(xd, yd, zd);\ - \ - MASK closer = SIMDf_LESS_THAN(newDistance, distance);\ - \ - distance = SIMDf_MIN(newDistance, distance);\ - xCell = SIMDf_BLENDV(xCell, xCellNew, closer);\ - yCell = SIMDf_BLENDV(yCell, yCellNew, closer);\ - zCell = SIMDf_BLENDV(zCell, zCellNew, closer);\ - \ - zcf = SIMDf_ADD(zcf, SIMDf_NUM(1));\ - zc = SIMDi_ADD(zc, SIMDi_NUM(zPrime));\ - }\ - ycf = SIMDf_ADD(ycf, SIMDf_NUM(1));\ - yc = SIMDi_ADD(yc, SIMDi_NUM(yPrime));\ - }\ - xcf = SIMDf_ADD(xcf, SIMDf_NUM(1));\ - xc = SIMDi_ADD(xc, SIMDi_NUM(xPrime));\ - }\ - \ - SIMDf xF = SIMDf_MUL(xCell, noiseLookupSettings.frequency);\ - SIMDf yF = SIMDf_MUL(yCell, noiseLookupSettings.frequency);\ - SIMDf zF = SIMDf_MUL(zCell, noiseLookupSettings.frequency);\ - SIMDf result;\ - \ - switch(noiseLookupSettings.type)\ - {\ - default:\ - break;\ - case FastNoiseSIMD::Value:\ - result = FUNC(ValueSingle)(seedV, xF, yF, zF); \ - break;\ - case FastNoiseSIMD::ValueFractal:\ - CELLULAR_LOOKUP_FRACTAL_VALUE(Value);\ - break; \ - case FastNoiseSIMD::Perlin:\ - result = FUNC(PerlinSingle)(seedV, xF, yF, zF); \ - break;\ - case FastNoiseSIMD::PerlinFractal:\ - CELLULAR_LOOKUP_FRACTAL_VALUE(Perlin);\ - break; \ - case FastNoiseSIMD::Simplex:\ - result = FUNC(SimplexSingle)(seedV, xF, yF, zF); \ - break;\ - case FastNoiseSIMD::SimplexFractal:\ - CELLULAR_LOOKUP_FRACTAL_VALUE(Simplex);\ - break; \ - case FastNoiseSIMD::Cubic:\ - result = FUNC(CubicSingle)(seedV, xF, yF, zF); \ - break;\ - case FastNoiseSIMD::CubicFractal:\ - CELLULAR_LOOKUP_FRACTAL_VALUE(Cubic);\ - break; \ - }\ - \ - return result;\ -} - -#define CELLULAR_DISTANCE_SINGLE(distanceFunc)\ -static SIMDf VECTORCALL FUNC(CellularDistance##distanceFunc##Single)(SIMDi seed, SIMDf x, SIMDf y, SIMDf z, SIMDf cellJitter)\ -{\ - SIMDf distance = SIMDf_NUM(999999);\ - \ - SIMDi xc = SIMDi_SUB(SIMDi_CONVERT_TO_INT(x), SIMDi_NUM(1));\ - SIMDi ycBase = SIMDi_SUB(SIMDi_CONVERT_TO_INT(y), SIMDi_NUM(1));\ - SIMDi zcBase = SIMDi_SUB(SIMDi_CONVERT_TO_INT(z), SIMDi_NUM(1));\ - \ - SIMDf xcf = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(xc), x);\ - SIMDf ycfBase = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(ycBase), y);\ - SIMDf zcfBase = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(zcBase), z);\ - \ - xc = SIMDi_MUL(xc, SIMDi_NUM(xPrime));\ - ycBase = SIMDi_MUL(ycBase, SIMDi_NUM(yPrime));\ - zcBase = SIMDi_MUL(zcBase, SIMDi_NUM(zPrime));\ - \ - for (int xi = 0; xi < 3; xi++)\ - {\ - SIMDf ycf = ycfBase;\ - SIMDi yc = ycBase;\ - for (int yi = 0; yi < 3; yi++)\ - {\ - SIMDf zcf = zcfBase;\ - SIMDi zc = zcBase;\ - for (int zi = 0; zi < 3; zi++)\ - {\ - SIMDi hash = FUNC(HashHB)(seed, xc, yc, zc);\ - SIMDf xd = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(SIMDi_AND(hash, SIMDi_NUM(bit10Mask))), SIMDf_NUM(511_5));\ - SIMDf yd = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(SIMDi_AND(SIMDi_SHIFT_R(hash,10), SIMDi_NUM(bit10Mask))), SIMDf_NUM(511_5));\ - SIMDf zd = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(SIMDi_AND(SIMDi_SHIFT_R(hash,20), SIMDi_NUM(bit10Mask))), SIMDf_NUM(511_5));\ - \ - SIMDf invMag = SIMDf_MUL(cellJitter, SIMDf_INV_SQRT(SIMDf_MUL_ADD(xd, xd, SIMDf_MUL_ADD(yd, yd, SIMDf_MUL(zd, zd)))));\ - \ - xd = SIMDf_MUL_ADD(xd, invMag, xcf);\ - yd = SIMDf_MUL_ADD(yd, invMag, ycf);\ - zd = SIMDf_MUL_ADD(zd, invMag, zcf);\ - \ - SIMDf newDistance = distanceFunc##_DISTANCE(xd, yd, zd);\ - \ - distance = SIMDf_MIN(distance, newDistance);\ - \ - zcf = SIMDf_ADD(zcf, SIMDf_NUM(1));\ - zc = SIMDi_ADD(zc, SIMDi_NUM(zPrime));\ - }\ - ycf = SIMDf_ADD(ycf, SIMDf_NUM(1));\ - yc = SIMDi_ADD(yc, SIMDi_NUM(yPrime));\ - }\ - xcf = SIMDf_ADD(xcf, SIMDf_NUM(1));\ - xc = SIMDi_ADD(xc, SIMDi_NUM(xPrime));\ - }\ - \ - return distance;\ -} - -#define CELLULAR_DISTANCE2_SINGLE(distanceFunc, returnFunc)\ -static SIMDf VECTORCALL FUNC(Cellular##returnFunc##distanceFunc##Single)(SIMDi seed, SIMDf x, SIMDf y, SIMDf z, SIMDf cellJitter, int index0, int index1)\ -{\ - SIMDf distance[FN_CELLULAR_INDEX_MAX+1] = {SIMDf_NUM(999999),SIMDf_NUM(999999),SIMDf_NUM(999999),SIMDf_NUM(999999)};\ - \ - SIMDi xc = SIMDi_SUB(SIMDi_CONVERT_TO_INT(x), SIMDi_NUM(1));\ - SIMDi ycBase = SIMDi_SUB(SIMDi_CONVERT_TO_INT(y), SIMDi_NUM(1));\ - SIMDi zcBase = SIMDi_SUB(SIMDi_CONVERT_TO_INT(z), SIMDi_NUM(1));\ - \ - SIMDf xcf = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(xc), x);\ - SIMDf ycfBase = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(ycBase), y);\ - SIMDf zcfBase = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(zcBase), z);\ - \ - xc = SIMDi_MUL(xc, SIMDi_NUM(xPrime));\ - ycBase = SIMDi_MUL(ycBase, SIMDi_NUM(yPrime));\ - zcBase = SIMDi_MUL(zcBase, SIMDi_NUM(zPrime));\ - \ - for (int xi = 0; xi < 3; xi++)\ - {\ - SIMDf ycf = ycfBase;\ - SIMDi yc = ycBase;\ - for (int yi = 0; yi < 3; yi++)\ - {\ - SIMDf zcf = zcfBase;\ - SIMDi zc = zcBase;\ - for (int zi = 0; zi < 3; zi++)\ - {\ - SIMDi hash = FUNC(HashHB)(seed, xc, yc, zc);\ - SIMDf xd = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(SIMDi_AND(hash, SIMDi_NUM(bit10Mask))), SIMDf_NUM(511_5));\ - SIMDf yd = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(SIMDi_AND(SIMDi_SHIFT_R(hash,10), SIMDi_NUM(bit10Mask))), SIMDf_NUM(511_5));\ - SIMDf zd = SIMDf_SUB(SIMDf_CONVERT_TO_FLOAT(SIMDi_AND(SIMDi_SHIFT_R(hash,20), SIMDi_NUM(bit10Mask))), SIMDf_NUM(511_5));\ - \ - SIMDf invMag = SIMDf_MUL(cellJitter, SIMDf_INV_SQRT(SIMDf_MUL_ADD(xd, xd, SIMDf_MUL_ADD(yd, yd, SIMDf_MUL(zd, zd)))));\ - \ - xd = SIMDf_MUL_ADD(xd, invMag, xcf);\ - yd = SIMDf_MUL_ADD(yd, invMag, ycf);\ - zd = SIMDf_MUL_ADD(zd, invMag, zcf);\ - \ - SIMDf newDistance = distanceFunc##_DISTANCE(xd, yd, zd);\ - \ - for(int i = index1; i > 0; i--)\ - distance[i] = SIMDf_MAX(SIMDf_MIN(distance[i], newDistance), distance[i-1]);\ - distance[0] = SIMDf_MIN(distance[0], newDistance);\ - \ - zcf = SIMDf_ADD(zcf, SIMDf_NUM(1));\ - zc = SIMDi_ADD(zc, SIMDi_NUM(zPrime));\ - }\ - ycf = SIMDf_ADD(ycf, SIMDf_NUM(1));\ - yc = SIMDi_ADD(yc, SIMDi_NUM(yPrime));\ - }\ - xcf = SIMDf_ADD(xcf, SIMDf_NUM(1));\ - xc = SIMDi_ADD(xc, SIMDi_NUM(xPrime));\ - }\ - \ - return returnFunc##_RETURN(distance[index0], distance[index1]);\ -} - -#define CELLULAR_DISTANCE2CAVE_SINGLE(distanceFunc)\ -static SIMDf VECTORCALL FUNC(CellularDistance2Cave##distanceFunc##Single)(SIMDi seed, SIMDf x, SIMDf y, SIMDf z, SIMDf cellJitter, int index0, int index1)\ -{\ - SIMDf c0 = FUNC(CellularDistance2Div##distanceFunc##Single)(seed, x, y, z, cellJitter, index0, index1);\ - \ - x = SIMDf_ADD(x, SIMDf_NUM(0_5));\ - y = SIMDf_ADD(y, SIMDf_NUM(0_5));\ - z = SIMDf_ADD(z, SIMDf_NUM(0_5));\ - seed = SIMDi_ADD(seed, SIMDi_NUM(1));\ - \ - SIMDf c1 = FUNC(CellularDistance2Div##distanceFunc##Single)(seed, x, y, z, cellJitter, index0, index1);\ - \ - return SIMDf_MIN(c0,c1);\ -} - -CELLULAR_VALUE_SINGLE(Euclidean) -CELLULAR_VALUE_SINGLE(Manhattan) -CELLULAR_VALUE_SINGLE(Natural) - -CELLULAR_LOOKUP_SINGLE(Euclidean) -CELLULAR_LOOKUP_SINGLE(Manhattan) -CELLULAR_LOOKUP_SINGLE(Natural) - -#undef Natural_DISTANCE -#define Natural_DISTANCE(_x, _y, _z) SIMDf_MUL(Euclidean_DISTANCE(_x,_y,_z), Manhattan_DISTANCE(_x,_y,_z)) - -CELLULAR_DISTANCE_SINGLE(Euclidean) -CELLULAR_DISTANCE_SINGLE(Manhattan) -CELLULAR_DISTANCE_SINGLE(Natural) - -#define CELLULAR_DISTANCE2_MULTI(returnFunc)\ -CELLULAR_DISTANCE2_SINGLE(Euclidean, returnFunc)\ -CELLULAR_DISTANCE2_SINGLE(Manhattan, returnFunc)\ -CELLULAR_DISTANCE2_SINGLE(Natural, returnFunc) - -CELLULAR_DISTANCE2_MULTI(Distance2) -CELLULAR_DISTANCE2_MULTI(Distance2Add) -CELLULAR_DISTANCE2_MULTI(Distance2Sub) -CELLULAR_DISTANCE2_MULTI(Distance2Div) -CELLULAR_DISTANCE2_MULTI(Distance2Mul) - -CELLULAR_DISTANCE2CAVE_SINGLE(Euclidean) -CELLULAR_DISTANCE2CAVE_SINGLE(Manhattan) -CELLULAR_DISTANCE2CAVE_SINGLE(Natural) - -#define CELLULAR_MULTI(returnFunc)\ -switch(m_cellularDistanceFunction)\ -{\ -case Euclidean:\ - SET_BUILDER(result = FUNC(Cellular##returnFunc##EuclideanSingle)(seedV, xF, yF, zF, cellJitterV))\ - break;\ -case Manhattan:\ - SET_BUILDER(result = FUNC(Cellular##returnFunc##ManhattanSingle)(seedV, xF, yF, zF, cellJitterV))\ - break;\ -case Natural:\ - SET_BUILDER(result = FUNC(Cellular##returnFunc##NaturalSingle)(seedV, xF, yF, zF, cellJitterV))\ - break;\ -} - -#define CELLULAR_INDEX_MULTI(returnFunc)\ -switch(m_cellularDistanceFunction)\ -{\ -case Euclidean:\ - SET_BUILDER(result = FUNC(Cellular##returnFunc##EuclideanSingle)(seedV, xF, yF, zF, cellJitterV, m_cellularDistanceIndex0, m_cellularDistanceIndex1))\ - break;\ -case Manhattan:\ - SET_BUILDER(result = FUNC(Cellular##returnFunc##ManhattanSingle)(seedV, xF, yF, zF, cellJitterV, m_cellularDistanceIndex0, m_cellularDistanceIndex1))\ - break;\ -case Natural:\ - SET_BUILDER(result = FUNC(Cellular##returnFunc##NaturalSingle)(seedV, xF, yF, zF, cellJitterV, m_cellularDistanceIndex0, m_cellularDistanceIndex1))\ - break;\ -} - -void SIMD_LEVEL_CLASS::FillCellularSet(float* noiseSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier) -{ - assert(noiseSet); - SIMD_ZERO_ALL(); - SIMDi seedV = SIMDi_SET(m_seed); - INIT_PERTURB_VALUES(); - - scaleModifier *= m_frequency; - - SIMDf xFreqV = SIMDf_SET(scaleModifier * m_xScale); - SIMDf yFreqV = SIMDf_SET(scaleModifier * m_yScale); - SIMDf zFreqV = SIMDf_SET(scaleModifier * m_zScale); - SIMDf cellJitterV = SIMDf_SET(m_cellularJitter); - - NoiseLookupSettings nls; - - switch (m_cellularReturnType) - { - case CellValue: - CELLULAR_MULTI(Value); - break; - case Distance: - CELLULAR_MULTI(Distance); - break; - case Distance2: - CELLULAR_INDEX_MULTI(Distance2); - break; - case Distance2Add: - CELLULAR_INDEX_MULTI(Distance2Add); - break; - case Distance2Sub: - CELLULAR_INDEX_MULTI(Distance2Sub); - break; - case Distance2Mul: - CELLULAR_INDEX_MULTI(Distance2Mul); - break; - case Distance2Div: - CELLULAR_INDEX_MULTI(Distance2Div); - break; - case Distance2Cave: - CELLULAR_INDEX_MULTI(Distance2Cave); - break; - case NoiseLookup: - nls.type = m_cellularNoiseLookupType; - nls.frequency = SIMDf_SET(m_cellularNoiseLookupFrequency); - nls.fractalType = m_fractalType; - nls.fractalOctaves = m_octaves; - nls.fractalLacunarity = SIMDf_SET(m_lacunarity); - nls.fractalGain = SIMDf_SET(m_gain); - nls.fractalBounding = SIMDf_SET(m_fractalBounding); - - switch (m_cellularDistanceFunction) - { - case Euclidean: - SET_BUILDER(result = FUNC(CellularLookupEuclideanSingle)(seedV, xF, yF, zF, cellJitterV, nls)) - break; \ - case Manhattan: - SET_BUILDER(result = FUNC(CellularLookupManhattanSingle)(seedV, xF, yF, zF, cellJitterV, nls)) - break; \ - case Natural: - SET_BUILDER(result = FUNC(CellularLookupNaturalSingle)(seedV, xF, yF, zF, cellJitterV, nls)) - break; - } - break; - } - SIMD_ZERO_ALL(); -} - -#define CELLULAR_MULTI_VECTOR(returnFunc)\ -switch(m_cellularDistanceFunction)\ -{\ -case Euclidean:\ - VECTOR_SET_BUILDER(result = FUNC(Cellular##returnFunc##EuclideanSingle)(seedV, xF, yF, zF, cellJitterV))\ - break;\ -case Manhattan:\ - VECTOR_SET_BUILDER(result = FUNC(Cellular##returnFunc##ManhattanSingle)(seedV, xF, yF, zF, cellJitterV))\ - break;\ -case Natural:\ - VECTOR_SET_BUILDER(result = FUNC(Cellular##returnFunc##NaturalSingle)(seedV, xF, yF, zF, cellJitterV))\ - break;\ -} - -#define CELLULAR_INDEX_MULTI_VECTOR(returnFunc)\ -switch(m_cellularDistanceFunction)\ -{\ -case Euclidean:\ - VECTOR_SET_BUILDER(result = FUNC(Cellular##returnFunc##EuclideanSingle)(seedV, xF, yF, zF, cellJitterV, m_cellularDistanceIndex0, m_cellularDistanceIndex1))\ - break;\ -case Manhattan:\ - VECTOR_SET_BUILDER(result = FUNC(Cellular##returnFunc##ManhattanSingle)(seedV, xF, yF, zF, cellJitterV, m_cellularDistanceIndex0, m_cellularDistanceIndex1))\ - break;\ -case Natural:\ - VECTOR_SET_BUILDER(result = FUNC(Cellular##returnFunc##NaturalSingle)(seedV, xF, yF, zF, cellJitterV, m_cellularDistanceIndex0, m_cellularDistanceIndex1))\ - break;\ -} - -void SIMD_LEVEL_CLASS::FillCellularSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset, float yOffset, float zOffset) -{ - assert(noiseSet); - assert(vectorSet); - assert(vectorSet->size >= 0); - SIMD_ZERO_ALL(); - - SIMDi seedV = SIMDi_SET(m_seed); - SIMDf xFreqV = SIMDf_SET(m_frequency * m_xScale); - SIMDf yFreqV = SIMDf_SET(m_frequency * m_yScale); - SIMDf zFreqV = SIMDf_SET(m_frequency * m_zScale); - SIMDf xOffsetV = SIMDf_MUL(SIMDf_SET(xOffset), xFreqV); - SIMDf yOffsetV = SIMDf_MUL(SIMDf_SET(yOffset), yFreqV); - SIMDf zOffsetV = SIMDf_MUL(SIMDf_SET(zOffset), zFreqV); - SIMDf cellJitterV = SIMDf_SET(m_cellularJitter); - INIT_PERTURB_VALUES(); - - int index = 0; - int loopMax = vectorSet->size SIZE_MASK; - NoiseLookupSettings nls; - - switch (m_cellularReturnType) - { - case CellValue: - CELLULAR_MULTI_VECTOR(Value); - break; - case Distance: - CELLULAR_MULTI_VECTOR(Distance); - break; - case Distance2: - CELLULAR_INDEX_MULTI_VECTOR(Distance2); - break; - case Distance2Add: - CELLULAR_INDEX_MULTI_VECTOR(Distance2Add); - break; - case Distance2Sub: - CELLULAR_INDEX_MULTI_VECTOR(Distance2Sub); - break; - case Distance2Mul: - CELLULAR_INDEX_MULTI_VECTOR(Distance2Mul); - break; - case Distance2Div: - CELLULAR_INDEX_MULTI_VECTOR(Distance2Div); - break; - case Distance2Cave: - CELLULAR_INDEX_MULTI_VECTOR(Distance2Cave); - break; - case NoiseLookup: - nls.type = m_cellularNoiseLookupType; - nls.frequency = SIMDf_SET(m_cellularNoiseLookupFrequency); - nls.fractalType = m_fractalType; - nls.fractalOctaves = m_octaves; - nls.fractalLacunarity = SIMDf_SET(m_lacunarity); - nls.fractalGain = SIMDf_SET(m_gain); - nls.fractalBounding = SIMDf_SET(m_fractalBounding); - - switch (m_cellularDistanceFunction) - { - case Euclidean: - VECTOR_SET_BUILDER(result = FUNC(CellularLookupEuclideanSingle)(seedV, xF, yF, zF, cellJitterV, nls)); - break; - case Manhattan: - VECTOR_SET_BUILDER(result = FUNC(CellularLookupManhattanSingle)(seedV, xF, yF, zF, cellJitterV, nls)); - break; - case Natural: - VECTOR_SET_BUILDER(result = FUNC(CellularLookupNaturalSingle)(seedV, xF, yF, zF, cellJitterV, nls)); - break; - } - break; - } - SIMD_ZERO_ALL(); -} - -#define SAMPLE_INDEX(_x,_y,_z) ((_x) * yzSizeSample + (_y) * zSizeSample + (_z)) -#define SET_INDEX(_x,_y,_z) ((_x) * yzSize + (_y) * zSize + (_z)) - -void SIMD_LEVEL_CLASS::FillSampledNoiseSet(float* noiseSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, int sampleScale) -{ - assert(noiseSet); - SIMD_ZERO_ALL(); - - if (sampleScale <= 0) - { - FillNoiseSet(noiseSet, xStart, yStart, zStart, xSize, ySize, zSize); - return; - } - - int sampleSize = 1 << sampleScale; - int sampleMask = sampleSize - 1; - float scaleModifier = float(sampleSize); - - int xOffset = (sampleSize - (xStart & sampleMask)) & sampleMask; - int yOffset = (sampleSize - (yStart & sampleMask)) & sampleMask; - int zOffset = (sampleSize - (zStart & sampleMask)) & sampleMask; - - int xSizeSample = xSize + xOffset; - int ySizeSample = ySize + yOffset; - int zSizeSample = zSize + zOffset; - - if (xSizeSample & sampleMask) - xSizeSample = (xSizeSample & ~sampleMask) + sampleSize; - - if (ySizeSample & sampleMask) - ySizeSample = (ySizeSample & ~sampleMask) + sampleSize; - - if (zSizeSample & sampleMask) - zSizeSample = (zSizeSample & ~sampleMask) + sampleSize; - - xSizeSample = (xSizeSample >> sampleScale) + 1; - ySizeSample = (ySizeSample >> sampleScale) + 1; - zSizeSample = (zSizeSample >> sampleScale) + 1; - - float* noiseSetSample = GetEmptySet(xSizeSample * ySizeSample * zSizeSample); - FillNoiseSet(noiseSetSample, xStart >> sampleScale, yStart >> sampleScale, zStart >> sampleScale, xSizeSample, ySizeSample, zSizeSample, scaleModifier); - - int yzSizeSample = ySizeSample * zSizeSample; - int yzSize = ySize * zSize; - - SIMDi axisMask = SIMDi_SET(sampleMask); - SIMDf axisScale = SIMDf_SET(1.f / scaleModifier); - SIMDf axisOffset = SIMDf_MUL(axisScale, SIMDf_NUM(0_5)); - - SIMDi sampleSizeSIMD = SIMDi_SET(sampleSize); - SIMDi xSIMD = SIMDi_SET(-xOffset); - SIMDi yBase = SIMDi_SET(-yOffset); - SIMDi zBase = SIMDi_SET(-zOffset); - - int localCountMax = (1 << (sampleScale * 3)); - int vMax = VECTOR_SIZE; - -#if SIMD_LEVEL == FN_NEON - SIMDi sampleScaleV = SIMDi_SET(-sampleScale); - SIMDi sampleScale2V = SIMDi_MUL(sampleScaleV, SIMDi_NUM(2)); -#endif - - for (int x = 0; x < xSizeSample - 1; x++) - { - SIMDi ySIMD = yBase; - for (int y = 0; y < ySizeSample - 1; y++) - { - SIMDi zSIMD = zBase; - - SIMDf c001 = SIMDf_SET(noiseSetSample[SAMPLE_INDEX(x, y, 0)]); - SIMDf c101 = SIMDf_SET(noiseSetSample[SAMPLE_INDEX(x + 1, y, 0)]); - SIMDf c011 = SIMDf_SET(noiseSetSample[SAMPLE_INDEX(x, y + 1, 0)]); - SIMDf c111 = SIMDf_SET(noiseSetSample[SAMPLE_INDEX(x + 1, y + 1, 0)]); - for (int z = 0; z < zSizeSample - 1; z++) - { - SIMDf c000 = c001; - SIMDf c100 = c101; - SIMDf c010 = c011; - SIMDf c110 = c111; - c001 = SIMDf_SET(noiseSetSample[SAMPLE_INDEX(x, y, z + 1)]); - c101 = SIMDf_SET(noiseSetSample[SAMPLE_INDEX(x + 1, y, z + 1)]); - c011 = SIMDf_SET(noiseSetSample[SAMPLE_INDEX(x, y + 1, z + 1)]); - c111 = SIMDf_SET(noiseSetSample[SAMPLE_INDEX(x + 1, y + 1, z + 1)]); - - SIMDi localCountSIMD = SIMDi_NUM(incremental); - - int localCount = 0; - while (localCount < localCountMax) - { - uSIMDi xi, yi, zi; - -#if SIMD_LEVEL == FN_NEON - xi.m = SIMDi_AND(SIMDi_VSHIFT_L(localCountSIMD, sampleScale2V), axisMask); - yi.m = SIMDi_AND(SIMDi_VSHIFT_L(localCountSIMD, sampleScaleV), axisMask); -#else - xi.m = SIMDi_AND(SIMDi_SHIFT_R(localCountSIMD, sampleScale * 2), axisMask); - yi.m = SIMDi_AND(SIMDi_SHIFT_R(localCountSIMD, sampleScale), axisMask); -#endif - - zi.m = SIMDi_AND(localCountSIMD, axisMask); - - SIMDf xf = SIMDf_MUL_ADD(SIMDf_CONVERT_TO_FLOAT(xi.m), axisScale, axisOffset); - SIMDf yf = SIMDf_MUL_ADD(SIMDf_CONVERT_TO_FLOAT(yi.m), axisScale, axisOffset); - SIMDf zf = SIMDf_MUL_ADD(SIMDf_CONVERT_TO_FLOAT(zi.m), axisScale, axisOffset); - - xi.m = SIMDi_ADD(xi.m, xSIMD); - yi.m = SIMDi_ADD(yi.m, ySIMD); - zi.m = SIMDi_ADD(zi.m, zSIMD); - - uSIMDf sampledResults; - sampledResults.m = FUNC(Lerp)( - FUNC(Lerp)( - FUNC(Lerp)(c000, c100, xf), - FUNC(Lerp)(c010, c110, xf), yf), - FUNC(Lerp)( - FUNC(Lerp)(c001, c101, xf), - FUNC(Lerp)(c011, c111, xf), yf), zf); - - for (int i = 0; i < vMax; i++) - { - if (xi.a[i] >= 0 && xi.a[i] < xSize && - yi.a[i] >= 0 && yi.a[i] < ySize && - zi.a[i] >= 0 && zi.a[i] < zSize) - { - int index = SET_INDEX(xi.a[i], yi.a[i], zi.a[i]); - noiseSet[index] = sampledResults.a[i]; - } - } - - localCount += VECTOR_SIZE; - localCountSIMD = SIMDi_ADD(localCountSIMD, SIMDi_NUM(vectorSize)); - } - zSIMD = SIMDi_ADD(zSIMD, sampleSizeSIMD); - } - ySIMD = SIMDi_ADD(ySIMD, sampleSizeSIMD); - } - xSIMD = SIMDi_ADD(xSIMD, sampleSizeSIMD); - } - - FreeNoiseSet(noiseSetSample); - SIMD_ZERO_ALL(); -} - -void SIMD_LEVEL_CLASS::FillSampledNoiseSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset, float yOffset, float zOffset) -{ - assert(noiseSet); - assert(vectorSet); - assert(vectorSet->size >= 0); - SIMD_ZERO_ALL(); - - int sampleScale = vectorSet->sampleScale; - - if (sampleScale <= 0) - { - FillNoiseSet(noiseSet, vectorSet, xOffset, yOffset, zOffset); - return; - } - - int sampleSize = 1 << sampleScale; - int sampleMask = sampleSize - 1; - float scaleModifier = float(sampleSize); - - int xSize = vectorSet->sampleSizeX; - int ySize = vectorSet->sampleSizeY; - int zSize = vectorSet->sampleSizeZ; - - int xSizeSample = xSize; - int ySizeSample = ySize; - int zSizeSample = zSize; - - if (xSizeSample & sampleMask) - xSizeSample = (xSizeSample & ~sampleMask) + sampleSize; - - if (ySizeSample & sampleMask) - ySizeSample = (ySizeSample & ~sampleMask) + sampleSize; - - if (zSizeSample & sampleMask) - zSizeSample = (zSizeSample & ~sampleMask) + sampleSize; - - xSizeSample = (xSizeSample >> sampleScale) + 1; - ySizeSample = (ySizeSample >> sampleScale) + 1; - zSizeSample = (zSizeSample >> sampleScale) + 1; - - float* noiseSetSample = GetEmptySet(vectorSet->size); - FillNoiseSet(noiseSetSample, vectorSet, xOffset - 0.5f, yOffset - 0.5f, zOffset - 0.5f); - - int yzSizeSample = ySizeSample * zSizeSample; - int yzSize = ySize * zSize; - - SIMDi axisMask = SIMDi_SET(sampleMask); - SIMDf axisScale = SIMDf_SET(1.f / scaleModifier); - SIMDf axisOffset = SIMDf_MUL(axisScale, SIMDf_NUM(0_5)); - - SIMDi sampleSizeSIMD = SIMDi_SET(sampleSize); - SIMDi xSIMD = SIMDi_SET_ZERO(); - - int localCountMax = (1 << (sampleScale * 3)); - int vMax = VECTOR_SIZE; - -#if SIMD_LEVEL == FN_NEON - SIMDi sampleScaleV = SIMDi_SET(-sampleScale); - SIMDi sampleScale2V = SIMDi_MUL(sampleScaleV, SIMDi_NUM(2)); -#endif - - for (int x = 0; x < xSizeSample - 1; x++) - { - SIMDi ySIMD = SIMDi_SET_ZERO(); - for (int y = 0; y < ySizeSample - 1; y++) - { - SIMDi zSIMD = SIMDi_SET_ZERO(); - - SIMDf c001 = SIMDf_SET(noiseSetSample[SAMPLE_INDEX(x, y, 0)]); - SIMDf c101 = SIMDf_SET(noiseSetSample[SAMPLE_INDEX(x + 1, y, 0)]); - SIMDf c011 = SIMDf_SET(noiseSetSample[SAMPLE_INDEX(x, y + 1, 0)]); - SIMDf c111 = SIMDf_SET(noiseSetSample[SAMPLE_INDEX(x + 1, y + 1, 0)]); - for (int z = 0; z < zSizeSample - 1; z++) - { - SIMDf c000 = c001; - SIMDf c100 = c101; - SIMDf c010 = c011; - SIMDf c110 = c111; - c001 = SIMDf_SET(noiseSetSample[SAMPLE_INDEX(x, y, z + 1)]); - c101 = SIMDf_SET(noiseSetSample[SAMPLE_INDEX(x + 1, y, z + 1)]); - c011 = SIMDf_SET(noiseSetSample[SAMPLE_INDEX(x, y + 1, z + 1)]); - c111 = SIMDf_SET(noiseSetSample[SAMPLE_INDEX(x + 1, y + 1, z + 1)]); - - SIMDi localCountSIMD = SIMDi_NUM(incremental); - - int localCount = 0; - while (localCount < localCountMax) - { - uSIMDi xi, yi, zi; - -#if SIMD_LEVEL == FN_NEON - xi.m = SIMDi_AND(SIMDi_VSHIFT_L(localCountSIMD, sampleScale2V), axisMask); - yi.m = SIMDi_AND(SIMDi_VSHIFT_L(localCountSIMD, sampleScaleV), axisMask); -#else - xi.m = SIMDi_AND(SIMDi_SHIFT_R(localCountSIMD, sampleScale * 2), axisMask); - yi.m = SIMDi_AND(SIMDi_SHIFT_R(localCountSIMD, sampleScale), axisMask); -#endif - - zi.m = SIMDi_AND(localCountSIMD, axisMask); - - SIMDf xf = SIMDf_MUL_ADD(SIMDf_CONVERT_TO_FLOAT(xi.m), axisScale, axisOffset); - SIMDf yf = SIMDf_MUL_ADD(SIMDf_CONVERT_TO_FLOAT(yi.m), axisScale, axisOffset); - SIMDf zf = SIMDf_MUL_ADD(SIMDf_CONVERT_TO_FLOAT(zi.m), axisScale, axisOffset); - - xi.m = SIMDi_ADD(xi.m, xSIMD); - yi.m = SIMDi_ADD(yi.m, ySIMD); - zi.m = SIMDi_ADD(zi.m, zSIMD); - - uSIMDf sampledResults; - sampledResults.m = FUNC(Lerp)( - FUNC(Lerp)( - FUNC(Lerp)(c000, c100, xf), - FUNC(Lerp)(c010, c110, xf), yf), - FUNC(Lerp)( - FUNC(Lerp)(c001, c101, xf), - FUNC(Lerp)(c011, c111, xf), yf), zf); - - for (int i = 0; i < vMax; i++) - { - if (xi.a[i] < xSize && - yi.a[i] < ySize && - zi.a[i] < zSize) - { - int index = SET_INDEX(xi.a[i], yi.a[i], zi.a[i]); - noiseSet[index] = sampledResults.a[i]; - } - } - - localCount += VECTOR_SIZE; - localCountSIMD = SIMDi_ADD(localCountSIMD, SIMDi_NUM(vectorSize)); - } - zSIMD = SIMDi_ADD(zSIMD, sampleSizeSIMD); - } - ySIMD = SIMDi_ADD(ySIMD, sampleSizeSIMD); - } - xSIMD = SIMDi_ADD(xSIMD, sampleSizeSIMD); - } - - FreeNoiseSet(noiseSetSample); - SIMD_ZERO_ALL(); -} - -#undef SIMD_LEVEL -#endif diff --git a/deps/FastNoiseSIMD/FastNoiseSIMD_internal.h b/deps/FastNoiseSIMD/FastNoiseSIMD_internal.h deleted file mode 100644 index a20e02d..0000000 --- a/deps/FastNoiseSIMD/FastNoiseSIMD_internal.h +++ /dev/null @@ -1,77 +0,0 @@ -// FastNoiseSIMD_internal.h -// -// MIT License -// -// Copyright(c) 2017 Jordan Peck -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files(the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions : -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// The developer's email is jorzixdan.me2@gzixmail.com (for great email, take -// off every 'zix'.) -// - -#ifndef SIMD_LEVEL_H -#error Dont include this file without defining SIMD_LEVEL_H -#else -#define FASTNOISE_SIMD_CLASS2(x) FastNoiseSIMD_L##x -#define FASTNOISE_SIMD_CLASS(level) FASTNOISE_SIMD_CLASS2(level) - -namespace FastNoiseSIMD_internal -{ - class FASTNOISE_SIMD_CLASS(SIMD_LEVEL_H) : public FastNoiseSIMD - { - public: - // Do not call this, use SetSIMDLevel(int) to have NewFastNoiseSIMD() return the level you want - FASTNOISE_SIMD_CLASS(SIMD_LEVEL_H)(int seed = 1337); - - static float* GetEmptySet(int size); - static int AlignedSize(int size); - - void FillSampledNoiseSet(float* noiseSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, int sampleScale) override; - void FillSampledNoiseSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f) override; - - void FillWhiteNoiseSet(float* floatSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f) override; - void FillWhiteNoiseSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f) override; - - void FillValueSet(float* floatSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f) override; - void FillValueFractalSet(float* floatSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f) override; - void FillValueSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f) override; - void FillValueFractalSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f) override; - - void FillPerlinSet(float* floatSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f) override; - void FillPerlinFractalSet(float* floatSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f) override; - void FillPerlinSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f) override; - void FillPerlinFractalSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f) override; - - void FillSimplexSet(float* floatSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f) override; - void FillSimplexFractalSet(float* floatSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f) override; - void FillSimplexSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f) override; - void FillSimplexFractalSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f) override; - - void FillCellularSet(float* floatSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f) override; - void FillCellularSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f) override; - - void FillCubicSet(float* floatSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f) override; - void FillCubicFractalSet(float* floatSet, int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f) override; - void FillCubicSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f) override; - void FillCubicFractalSet(float* noiseSet, FastNoiseVectorSet* vectorSet, float xOffset = 0.0f, float yOffset = 0.0f, float zOffset = 0.0f) override; - }; -} -#undef SIMD_LEVEL_H -#endif diff --git a/deps/FastNoiseSIMD/FastNoiseSIMD_neon.cpp b/deps/FastNoiseSIMD/FastNoiseSIMD_neon.cpp deleted file mode 100644 index cd44b61..0000000 --- a/deps/FastNoiseSIMD/FastNoiseSIMD_neon.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// FastNoiseSIMD_neon.cpp -// -// MIT License -// -// Copyright(c) 2017 Jordan Peck -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files(the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions : -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// The developer's email is jorzixdan.me2@gzixmail.com (for great email, take -// off every 'zix'.) -// - -#include "FastNoiseSIMD.h" -#ifdef FN_COMPILE_NEON - -#define SIMD_LEVEL_H FN_NEON -#include "FastNoiseSIMD_internal.h" -#include - -#define SIMD_LEVEL FN_NEON -#include "FastNoiseSIMD_internal.cpp" -#endif diff --git a/deps/FastNoiseSIMD/FastNoiseSIMD_sse2.cpp b/deps/FastNoiseSIMD/FastNoiseSIMD_sse2.cpp deleted file mode 100644 index 21ac5a0..0000000 --- a/deps/FastNoiseSIMD/FastNoiseSIMD_sse2.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// FastNoiseSIMD_sse2.cpp -// -// MIT License -// -// Copyright(c) 2017 Jordan Peck -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files(the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions : -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// The developer's email is jorzixdan.me2@gzixmail.com (for great email, take -// off every 'zix'.) -// - -#include "FastNoiseSIMD.h" - -// DISABLE WHOLE PROGRAM OPTIMIZATION for this file when using MSVC - -// Depending on the compiler this file may need to have SSE2 code generation compiler flags enabled -#ifdef FN_COMPILE_SSE2 -#define SIMD_LEVEL_H FN_SSE2 -#include "FastNoiseSIMD_internal.h" -#include //SSE2 - -#define SIMD_LEVEL FN_SSE2 -#include "FastNoiseSIMD_internal.cpp" -#endif \ No newline at end of file diff --git a/deps/FastNoiseSIMD/FastNoiseSIMD_sse41.cpp b/deps/FastNoiseSIMD/FastNoiseSIMD_sse41.cpp deleted file mode 100644 index a61c22c..0000000 --- a/deps/FastNoiseSIMD/FastNoiseSIMD_sse41.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// FastNoiseSIMD_sse41.cpp -// -// MIT License -// -// Copyright(c) 2017 Jordan Peck -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files(the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions : -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// The developer's email is jorzixdan.me2@gzixmail.com (for great email, take -// off every 'zix'.) -// - -#include "FastNoiseSIMD.h" - -// DISABLE WHOLE PROGRAM OPTIMIZATION for this file when using MSVC - -// Depending on the compiler this file may need to have SSE4.1 code generation compiler flags enabled -#ifdef FN_COMPILE_SSE41 -#define SIMD_LEVEL_H FN_SSE41 -#include "FastNoiseSIMD_internal.h" -#include //SSE4.1 - -#define SIMD_LEVEL FN_SSE41 -#include "FastNoiseSIMD_internal.cpp" -#endif \ No newline at end of file diff --git a/deps/FastNoiseSIMD/LICENSE b/deps/FastNoiseSIMD/LICENSE deleted file mode 100644 index 8786a12..0000000 --- a/deps/FastNoiseSIMD/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016 Jordan Peck - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/deps/imgui/imgui_stdlib.cpp b/deps/imgui/imgui_stdlib.cpp new file mode 100644 index 0000000..cb1fe17 --- /dev/null +++ b/deps/imgui/imgui_stdlib.cpp @@ -0,0 +1,76 @@ +// dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.) +// This is also an example of how you may wrap your own similar types. + +// Compatibility: +// - std::string support is only guaranteed to work from C++11. +// If you try to use it pre-C++11, please share your findings (w/ info about compiler/architecture) + +// Changelog: +// - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string + +#include "imgui.h" +#include "imgui_stdlib.h" + +struct InputTextCallback_UserData +{ + std::string* Str; + ImGuiInputTextCallback ChainCallback; + void* ChainCallbackUserData; +}; + +static int InputTextCallback(ImGuiInputTextCallbackData* data) +{ + InputTextCallback_UserData* user_data = (InputTextCallback_UserData*)data->UserData; + if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) + { + // Resize string callback + // If for some reason we refuse the new length (BufTextLen) and/or capacity (BufSize) we need to set them back to what we want. + std::string* str = user_data->Str; + IM_ASSERT(data->Buf == str->c_str()); + str->resize(data->BufTextLen); + data->Buf = (char*)str->c_str(); + } + else if (user_data->ChainCallback) + { + // Forward to user callback, if any + data->UserData = user_data->ChainCallbackUserData; + return user_data->ChainCallback(data); + } + return 0; +} + +bool ImGui::InputText(const char* label, std::string* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) +{ + IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0); + flags |= ImGuiInputTextFlags_CallbackResize; + + InputTextCallback_UserData cb_user_data; + cb_user_data.Str = str; + cb_user_data.ChainCallback = callback; + cb_user_data.ChainCallbackUserData = user_data; + return InputText(label, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cb_user_data); +} + +bool ImGui::InputTextMultiline(const char* label, std::string* str, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) +{ + IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0); + flags |= ImGuiInputTextFlags_CallbackResize; + + InputTextCallback_UserData cb_user_data; + cb_user_data.Str = str; + cb_user_data.ChainCallback = callback; + cb_user_data.ChainCallbackUserData = user_data; + return InputTextMultiline(label, (char*)str->c_str(), str->capacity() + 1, size, flags, InputTextCallback, &cb_user_data); +} + +bool ImGui::InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) +{ + IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0); + flags |= ImGuiInputTextFlags_CallbackResize; + + InputTextCallback_UserData cb_user_data; + cb_user_data.Str = str; + cb_user_data.ChainCallback = callback; + cb_user_data.ChainCallbackUserData = user_data; + return InputTextWithHint(label, hint, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cb_user_data); +} diff --git a/deps/imgui/imgui_stdlib.h b/deps/imgui/imgui_stdlib.h new file mode 100644 index 0000000..f860b0c --- /dev/null +++ b/deps/imgui/imgui_stdlib.h @@ -0,0 +1,22 @@ +// dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.) +// This is also an example of how you may wrap your own similar types. + +// Compatibility: +// - std::string support is only guaranteed to work from C++11. +// If you try to use it pre-C++11, please share your findings (w/ info about compiler/architecture) + +// Changelog: +// - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string + +#pragma once + +#include + +namespace ImGui +{ + // ImGui::InputText() with std::string + // Because text input needs dynamic resizing, we need to setup a callback to grow the capacity + IMGUI_API bool InputText(const char* label, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); + IMGUI_API bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); + IMGUI_API bool InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); +} diff --git a/deps/libguarded/LICENSE b/deps/libguarded/LICENSE deleted file mode 100644 index 0772093..0000000 --- a/deps/libguarded/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2016, Ansel Sermersheim -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/deps/libguarded/mutex_guarded.h b/deps/libguarded/mutex_guarded.h deleted file mode 100644 index 50d9fcc..0000000 --- a/deps/libguarded/mutex_guarded.h +++ /dev/null @@ -1,198 +0,0 @@ -/*********************************************************************** -* -* Copyright (c) 2015-2020 Ansel Sermersheim -* -* This file is part of CsLibGuarded. -* -* CsLibGuarded is free software, released under the BSD 2-Clause license. -* For license details refer to LICENSE provided with this project. -* -* CopperSpice is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -* -* https://opensource.org/licenses/BSD-2-Clause -* -***********************************************************************/ - -#ifndef CSLIBGUARDED_PLAIN_GUARDED_H -#define CSLIBGUARDED_PLAIN_GUARDED_H - -#include -#include - -namespace libguarded -{ - -/** - \headerfile cs_plain_guarded.h - - This templated class wraps an object and allows only one thread at a - time to access the protected object. - - This class will use std::mutex for the internal locking mechanism by - default. Other classes which are useful for the mutex type are - std::recursive_mutex, std::timed_mutex, and - std::recursive_timed_mutex. - - The handle returned by the various lock methods is moveable but not - copyable. -*/ -template -class plain_guarded -{ - private: - class deleter; - - public: - using handle = std::unique_ptr; - - /** - Construct a guarded object. This constructor will accept any - number of parameters, all of which are forwarded to the - constructor of T. - */ - template - plain_guarded(Us &&... data); - - /** - Acquire a handle to the protected object. As a side effect, the - protected object will be locked from access by any other - thread. The lock will be automatically released when the handle - is destroyed. - */ - [[nodiscard]] handle lock(); - - /** - Attempt to acquire a handle to the protected object. Returns a - null handle if the object is already locked. As a side effect, - the protected object will be locked from access by any other - thread. The lock will be automatically released when the handle - is destroyed. - */ - [[nodiscard]] handle try_lock(); - - /** - Attempt to acquire a handle to the protected object. As a side - effect, the protected object will be locked from access by any - other thread. The lock will be automatically released when the - handle is destroyed. - - Returns a null handle if the object is already locked, and does - not become available for locking before the time duration has - elapsed. - - Calling this method requires that the underlying mutex type M - supports the try_lock_for method. This is not true if M is the - default std::mutex. - */ - template - [[nodiscard]] handle try_lock_for(const Duration &duration); - - /** - Attempt to acquire a handle to the protected object. As a side - effect, the protected object will be locked from access by any other - thread. The lock will be automatically released when the handle is - destroyed. - - Returns a null handle if the object is already locked, and does not - become available for locking before reaching the specified timepoint. - - Calling this method requires that the underlying mutex type M - supports the try_lock_until method. This is not true if M is the - default std::mutex. - */ - template - [[nodiscard]] handle try_lock_until(const TimePoint &timepoint); - - private: - T m_obj; - M m_mutex; -}; - -template -class plain_guarded::deleter -{ - public: - using pointer = T *; - - deleter(std::unique_lock lock); - - void operator()(T *ptr); - - private: - std::unique_lock m_lock; -}; - -template -plain_guarded::deleter::deleter(std::unique_lock lock) - : m_lock(std::move(lock)) -{ -} - -template -void plain_guarded::deleter::operator()(T *) -{ - if (m_lock.owns_lock()) { - m_lock.unlock(); - } -} - -template -template -plain_guarded::plain_guarded(Us &&... data) - : m_obj(std::forward(data)...) -{ -} - -template -auto plain_guarded::lock() -> handle -{ - std::unique_lock lock(m_mutex); - return handle(&m_obj, deleter(std::move(lock))); -} - -template -auto plain_guarded::try_lock() -> handle -{ - std::unique_lock lock(m_mutex, std::try_to_lock); - - if (lock.owns_lock()) { - return handle(&m_obj, deleter(std::move(lock))); - } else { - return handle(nullptr, deleter(std::move(lock))); - } -} - -template -template -auto plain_guarded::try_lock_for(const Duration &d) -> handle -{ - std::unique_lock lock(m_mutex, d); - - if (lock.owns_lock()) { - return handle(&m_obj, deleter(std::move(lock))); - } else { - return handle(nullptr, deleter(std::move(lock))); - } -} - -template -template -auto plain_guarded::try_lock_until(const TimePoint &tp) -> handle -{ - std::unique_lock lock(m_mutex, tp); - - if (lock.owns_lock()) { - return handle(&m_obj, deleter(std::move(lock))); - } else { - return handle(nullptr, deleter(std::move(lock))); - } -} - -template -using guarded [[deprecated("renamed to plain_guarded")]] = plain_guarded; - -} // namespace libguarded - -#endif diff --git a/deps/libguarded/shared_mutex_guarded.h b/deps/libguarded/shared_mutex_guarded.h deleted file mode 100644 index 976477d..0000000 --- a/deps/libguarded/shared_mutex_guarded.h +++ /dev/null @@ -1,234 +0,0 @@ -/*********************************************************************** -* -* Copyright (c) 2015-2020 Ansel Sermersheim -* -* This file is part of CsLibGuarded. -* -* CsLibGuarded is free software, released under the BSD 2-Clause license. -* For license details refer to LICENSE provided with this project. -* -* CopperSpice is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -* -* https://opensource.org/licenses/BSD-2-Clause -* -***********************************************************************/ - -#ifndef CSLIBGUARDED_SHARED_GUARDED_H -#define CSLIBGUARDED_SHARED_GUARDED_H - -#include -#include - -namespace libguarded -{ - -/** - \headerfile cs_shared_guarded.h - - This templated class wraps an object and allows only one thread at - a time to modify the protected object. - - This class will use std::shared_timed_mutex for the internal - locking mechanism by default. In C++17 the std::shared_mutex class - is also available. - - The handle returned by the various lock methods is moveable but not - copyable. -*/ -template > -class shared_guarded -{ - private: - class deleter; - class shared_deleter; - - public: - using handle = std::unique_ptr; - using shared_handle = std::unique_ptr; - - template - shared_guarded(Us &&... data); - - // exclusive access - [[nodiscard]] handle lock(); - [[nodiscard]] handle try_lock(); - - template - [[nodiscard]] handle try_lock_for(const Duration &duration); - - template - [[nodiscard]] handle try_lock_until(const TimePoint &timepoint); - - // shared access, note "shared" in method names - [[nodiscard]] shared_handle lock_shared() const; - [[nodiscard]] shared_handle try_lock_shared() const; - - template - [[nodiscard]] shared_handle try_lock_shared_for(const Duration &duration) const; - - template - [[nodiscard]] shared_handle try_lock_shared_until(const TimePoint &timepoint) const; - - private: - T m_obj; - mutable M m_mutex; -}; - -template -class shared_guarded::deleter -{ - public: - using pointer = T *; - - deleter(std::unique_lock lock); - - void operator()(T *ptr); - - private: - std::unique_lock m_lock; -}; - -template -shared_guarded::deleter::deleter(std::unique_lock lock) - : m_lock(std::move(lock)) -{ -} - -template -void shared_guarded::deleter::operator()(T *) -{ - if (m_lock.owns_lock()) { - m_lock.unlock(); - } -} - -template -class shared_guarded::shared_deleter -{ -public: - using pointer = const T *; - - shared_deleter(L lock); - - void operator()(const T *ptr); - -private: - L m_lock; -}; - -template -shared_guarded::shared_deleter::shared_deleter(L lock) - : m_lock(std::move(lock)) -{ -} - -template -void shared_guarded::shared_deleter::operator()(const T *) -{ - if (m_lock.owns_lock()) { - m_lock.unlock(); - } -} - -template -template -shared_guarded::shared_guarded(Us &&... data) - : m_obj(std::forward(data)...) -{ -} - -template -auto shared_guarded::lock() -> handle -{ - std::unique_lock lock(m_mutex); - return handle(&m_obj, deleter(std::move(lock))); -} - -template -auto shared_guarded::try_lock() -> handle -{ - std::unique_lock lock(m_mutex, std::try_to_lock); - - if (lock.owns_lock()) { - return handle(&m_obj, deleter(std::move(lock))); - } else { - return handle(nullptr, deleter(std::move(lock))); - } -} - -template -template -auto shared_guarded::try_lock_for(const Duration &duration) -> handle -{ - std::unique_lock lock(m_mutex, duration); - - if (lock.owns_lock()) { - return handle(&m_obj, deleter(std::move(lock))); - } else { - return handle(nullptr, deleter(std::move(lock))); - } -} - -template -template -auto shared_guarded::try_lock_until(const TimePoint &timepoint) -> handle -{ - std::unique_lock lock(m_mutex, timepoint); - - if (lock.owns_lock()) { - return handle(&m_obj, deleter(std::move(lock))); - } else { - return handle(nullptr, deleter(std::move(lock))); - } -} - -template -auto shared_guarded::lock_shared() const -> shared_handle -{ - L lock(m_mutex); - return shared_handle(&m_obj, shared_deleter(std::move(lock))); -} - -template -auto shared_guarded::try_lock_shared() const -> shared_handle -{ - L lock(m_mutex, std::try_to_lock); - - if (lock.owns_lock()) { - return shared_handle(&m_obj, shared_deleter(std::move(lock))); - } else { - return shared_handle(nullptr, shared_deleter(std::move(lock))); - } -} - -template -template -auto shared_guarded::try_lock_shared_for(const Duration &d) const -> shared_handle -{ - L lock(m_mutex, d); - - if (lock.owns_lock()) { - return shared_handle(&m_obj, shared_deleter(std::move(lock))); - } else { - return shared_handle(nullptr, shared_deleter(std::move(lock))); - } -} - -template -template -auto shared_guarded::try_lock_shared_until(const TimePoint &tp) const -> shared_handle -{ - L lock(m_mutex, tp); - - if (lock.owns_lock()) { - return shared_handle(&m_obj, shared_deleter(std::move(lock))); - } else { - return shared_handle(nullptr, shared_deleter(std::move(lock))); - } -} - -} // namespace libguarded - -#endif diff --git a/deps/mini-yaml/README.md b/deps/mini-yaml/README.md deleted file mode 100644 index a9fd2e3..0000000 --- a/deps/mini-yaml/README.md +++ /dev/null @@ -1,76 +0,0 @@ -# mini-yaml -[![Build Status](https://travis-ci.org/jimmiebergmann/mini-yaml.svg?branch=master)](https://github.com/jimmiebergmann/mini-yaml#build-status) -Single header YAML 1.0 C++11 serializer/deserializer. - -## Quickstart -#### file.txt -``` -key: foo bar -list: - - hello world - - integer: 123 - boolean: true -``` -#### .cpp -```cpp -Yaml::Node root; -Yaml::Parse(root, "file.txt"); - -// Print all scalars. -std::cout << root["key"].As() << std::endl; -std::cout << root["list"][0].As() << std::endl; -std::cout << root["list"][1]["integer"].As() << std::endl; -std::cout << root["list"][1]["boolean"].As() << std::endl; - -// Iterate second sequence item. -Node & item = root[1]; -for(auto it = item.Begin(); it != item.End(); it++) -{ - std::cout << (*it).first << ": " << (*it).second.As() << std::endl; -} -``` -#### Output -``` -foo bar -hello world -123 -1 -integer: 123 -boolean: true -``` - -See [Best practice](https://github.com/jimmiebergmann/mini-yaml#best-practice). - -## Usage -Put [/yaml](https://github.com/jimmiebergmann/mini-yaml/blob/master/yaml) in your project directory and simply #include "[yaml/Yaml.hpp](https://github.com/jimmiebergmann/mini-yaml/blob/master/yaml/Yaml.hpp)". -See [examples/FirstExample.cpp](https://github.com/jimmiebergmann/mini-yaml/blob/master/examples/FirstExample.cpp) for additional examples. - -## Best practice -Always use references when accessing node content, if not intended to make a copy. Modifying copied node wont affect the original node content. -See example: -```cpp -Yaml::Node root; - -Yaml::Node & ref = root; // The content of "root" is not being copied. -ref["key"] = "value"; // Modifying "root" node content. - -Yaml::Node copy = root; // The content of "root" is copied to "copy". - // Slow operation if "root" contains a lot of content. -copy["key"] = "value"; // Modifying "copy" node content. "root" is left untouched. -``` - -## Build status -Builds are passed if all tests are good and no memory leaks were found. - -| Branch | Status | -| ------ | ------ | -| master | [![Build Status](https://travis-ci.org/jimmiebergmann/mini-yaml.svg?branch=master)](https://travis-ci.org/jimmiebergmann/mini-yaml) | -| dev | [![Build Status](https://travis-ci.org/jimmiebergmann/mini-yaml.svg?branch=dev)](https://travis-ci.org/jimmiebergmann/mini-yaml)| - -## Todo -- Parse/serialize tags(!!type). -- Parse anchors. -- Parse flow sequences/maps. -- Parse complex keys. -- Parse sets. - diff --git a/deps/mini-yaml/Yaml.cpp b/deps/mini-yaml/Yaml.cpp deleted file mode 100644 index 70adec6..0000000 --- a/deps/mini-yaml/Yaml.cpp +++ /dev/null @@ -1,2773 +0,0 @@ -/* -* MIT License -* -* Copyright(c) 2018 Jimmie Bergmann -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files(the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions : -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -* -*/ - -#include "Yaml.hpp" -#include -#include -#include -#include -#include -#include -#include - - -// Implementation access definitions. -#define NODE_IMP static_cast(m_pImp) -#define NODE_IMP_EXT(node) static_cast(node.m_pImp) -#define TYPE_IMP static_cast(m_pImp)->m_pImp - - -#define IT_IMP static_cast(m_pImp) - - -namespace Yaml -{ - class ReaderLine; - - // Exception message definitions. - static const std::string g_ErrorInvalidCharacter = "Invalid character found."; - static const std::string g_ErrorKeyMissing = "Missing key."; - static const std::string g_ErrorKeyIncorrect = "Incorrect key."; - static const std::string g_ErrorValueIncorrect = "Incorrect value."; - static const std::string g_ErrorTabInOffset = "Tab found in offset."; - static const std::string g_ErrorBlockSequenceNotAllowed = "Sequence entries are not allowed in this context."; - static const std::string g_ErrorUnexpectedDocumentEnd = "Unexpected document end."; - static const std::string g_ErrorDiffEntryNotAllowed = "Different entry is not allowed in this context."; - static const std::string g_ErrorIncorrectOffset = "Incorrect offset."; - static const std::string g_ErrorSequenceError = "Error in sequence node."; - static const std::string g_ErrorCannotOpenFile = "Cannot open file."; - static const std::string g_ErrorIndentation = "Space indentation is less than 2."; - static const std::string g_ErrorInvalidBlockScalar = "Invalid block scalar."; - static const std::string g_ErrorInvalidQuote = "Invalid quote."; - static const std::string g_EmptyString = ""; - static Yaml::Node g_NoneNode; - - // Global function definitions. Implemented at end of this source file. - static std::string ExceptionMessage(const std::string & message, ReaderLine & line); - static std::string ExceptionMessage(const std::string & message, ReaderLine & line, const size_t errorPos); - static std::string ExceptionMessage(const std::string & message, const size_t errorLine, const size_t errorPos); - static std::string ExceptionMessage(const std::string & message, const size_t errorLine, const std::string & data); - - static bool FindQuote(const std::string & input, size_t & start, size_t & end, size_t searchPos = 0); - static size_t FindNotCited(const std::string & input, char token, size_t & preQuoteCount); - static size_t FindNotCited(const std::string & input, char token); - static bool ValidateQuote(const std::string & input); - static void CopyNode(const Node & from, Node & to); - static bool ShouldBeCited(const std::string & key); - static void AddEscapeTokens(std::string & input, const std::string & tokens); - static void RemoveAllEscapeTokens(std::string & input); - - // Exception implementations - Exception::Exception(const std::string & message, const eType type) : - std::runtime_error(message), - m_Type(type) - { - } - - Exception::eType Exception::Type() const - { - return m_Type; - } - - const char * Exception::Message() const - { - return what(); - } - - InternalException::InternalException(const std::string & message) : - Exception(message, InternalError) - { - - } - - ParsingException::ParsingException(const std::string & message) : - Exception(message, ParsingError) - { - - } - - OperationException::OperationException(const std::string & message) : - Exception(message, OperationError) - { - - } - - - class TypeImp - { - - public: - - virtual ~TypeImp() - { - } - - virtual const std::string & GetData() const = 0; - virtual bool SetData(const std::string & data) = 0; - virtual size_t GetSize() const = 0; - virtual Node * GetNode(const size_t index) = 0; - virtual Node * GetNode(const std::string & key) = 0; - virtual Node * Insert(const size_t index) = 0; - virtual Node * PushFront() = 0; - virtual Node * PushBack() = 0; - virtual void Erase(const size_t index) = 0; - virtual void Erase(const std::string & key) = 0; - - }; - - class SequenceImp : public TypeImp - { - - public: - - ~SequenceImp() - { - for(auto it = m_Sequence.begin(); it != m_Sequence.end(); it++) - { - delete it->second; - } - } - - virtual const std::string & GetData() const - { - return g_EmptyString; - } - - virtual bool SetData(const std::string & data) - { - return false; - } - - virtual size_t GetSize() const - { - return m_Sequence.size(); - } - - virtual Node * GetNode(const size_t index) - { - auto it = m_Sequence.find(index); - if(it != m_Sequence.end()) - { - return it->second; - } - return nullptr; - } - - virtual Node * GetNode(const std::string & key) - { - return nullptr; - } - - virtual Node * Insert(const size_t index) - { - if(m_Sequence.size() == 0) - { - Node * pNode = new Node; - m_Sequence.insert({0, pNode}); - return pNode; - } - - if(index >= m_Sequence.size()) - { - auto it = m_Sequence.end(); - --it; - Node * pNode = new Node; - m_Sequence.insert({it->first, pNode}); - return pNode; - } - - auto it = m_Sequence.cbegin(); - while(it != m_Sequence.cend()) - { - m_Sequence[it->first+1] = it->second; - - if(it->first == index) - { - break; - } - } - - Node * pNode = new Node; - m_Sequence.insert({index, pNode}); - return pNode; - } - - virtual Node * PushFront() - { - for(auto it = m_Sequence.cbegin(); it != m_Sequence.cend(); it++) - { - m_Sequence[it->first+1] = it->second; - } - - Node * pNode = new Node; - m_Sequence.insert({0, pNode}); - return pNode; - } - - virtual Node * PushBack() - { - size_t index = 0; - if(m_Sequence.size()) - { - auto it = m_Sequence.end(); - --it; - index = it->first + 1; - } - - Node * pNode = new Node; - m_Sequence.insert({index, pNode}); - return pNode; - } - - virtual void Erase(const size_t index) - { - auto it = m_Sequence.find(index); - if(it == m_Sequence.end()) - { - return; - } - delete it->second; - m_Sequence.erase(index); - } - - virtual void Erase(const std::string & key) - { - } - - std::map m_Sequence; - - }; - - class MapImp : public TypeImp - { - - public: - - ~MapImp() - { - for(auto it = m_Map.begin(); it != m_Map.end(); it++) - { - delete it->second; - } - } - - virtual const std::string & GetData() const - { - return g_EmptyString; - } - - virtual bool SetData(const std::string & data) - { - return false; - } - - virtual size_t GetSize() const - { - return m_Map.size(); - } - - virtual Node * GetNode(const size_t index) - { - return nullptr; - } - - virtual Node * GetNode(const std::string & key) - { - auto it = m_Map.find(key); - if(it == m_Map.end()) - { - Node * pNode = new Node; - m_Map.insert({key, pNode}); - return pNode; - } - return it->second; - } - - virtual Node * Insert(const size_t index) - { - return nullptr; - } - - virtual Node * PushFront() - { - return nullptr; - } - - virtual Node * PushBack() - { - return nullptr; - } - - virtual void Erase(const size_t index) - { - } - - virtual void Erase(const std::string & key) - { - auto it = m_Map.find(key); - if(it == m_Map.end()) - { - return; - } - delete it->second; - m_Map.erase(key); - } - - std::map m_Map; - - }; - - class ScalarImp : public TypeImp - { - - public: - - ~ScalarImp() - { - } - - virtual const std::string & GetData() const - { - return m_Value; - } - - virtual bool SetData(const std::string & data) - { - m_Value = data; - return true; - } - - virtual size_t GetSize() const - { - return 0; - } - - virtual Node * GetNode(const size_t index) - { - return nullptr; - } - - virtual Node * GetNode(const std::string & key) - { - return nullptr; - } - - virtual Node * Insert(const size_t index) - { - return nullptr; - } - - virtual Node * PushFront() - { - return nullptr; - } - - virtual Node * PushBack() - { - return nullptr; - } - - virtual void Erase(const size_t index) - { - } - - virtual void Erase(const std::string & key) - { - } - - std::string m_Value; - - }; - - - // Node implementations. - class NodeImp - { - - public: - - NodeImp() : - m_Type(Node::None), - m_pImp(nullptr) - { - } - - ~NodeImp() - { - Clear(); - } - - void Clear() - { - if(m_pImp != nullptr) - { - delete m_pImp; - m_pImp = nullptr; - } - m_Type = Node::None; - } - - void InitSequence() - { - if(m_Type != Node::SequenceType || m_pImp == nullptr) - { - if(m_pImp) - { - delete m_pImp; - } - m_pImp = new SequenceImp; - m_Type = Node::SequenceType; - } - } - - void InitMap() - { - if(m_Type != Node::MapType || m_pImp == nullptr) - { - if(m_pImp) - { - delete m_pImp; - } - m_pImp = new MapImp; - m_Type = Node::MapType; - } - } - - void InitScalar() - { - if(m_Type != Node::ScalarType || m_pImp == nullptr) - { - if(m_pImp) - { - delete m_pImp; - } - m_pImp = new ScalarImp; - m_Type = Node::ScalarType; - } - - } - - Node::eType m_Type; ///< Type of node. - TypeImp * m_pImp; ///< Imp of type. - - }; - - // Iterator implementation class - class IteratorImp - { - - public: - - virtual ~IteratorImp() - { - } - - virtual Node::eType GetType() const = 0; - virtual void InitBegin(SequenceImp * pSequenceImp) = 0; - virtual void InitEnd(SequenceImp * pSequenceImp) = 0; - virtual void InitBegin(MapImp * pMapImp) = 0; - virtual void InitEnd(MapImp * pMapImp) = 0; - - }; - - class SequenceIteratorImp : public IteratorImp - { - - public: - - virtual Node::eType GetType() const - { - return Node::SequenceType; - } - - virtual void InitBegin(SequenceImp * pSequenceImp) - { - m_Iterator = pSequenceImp->m_Sequence.begin(); - } - - virtual void InitEnd(SequenceImp * pSequenceImp) - { - m_Iterator = pSequenceImp->m_Sequence.end(); - } - - virtual void InitBegin(MapImp * pMapImp) - { - } - - virtual void InitEnd(MapImp * pMapImp) - { - } - - void Copy(const SequenceIteratorImp & it) - { - m_Iterator = it.m_Iterator; - } - - std::map::iterator m_Iterator; - - }; - - class MapIteratorImp : public IteratorImp - { - - public: - - virtual Node::eType GetType() const - { - return Node::MapType; - } - - virtual void InitBegin(SequenceImp * pSequenceImp) - { - } - - virtual void InitEnd(SequenceImp * pSequenceImp) - { - } - - virtual void InitBegin(MapImp * pMapImp) - { - m_Iterator = pMapImp->m_Map.begin(); - } - - virtual void InitEnd(MapImp * pMapImp) - { - m_Iterator = pMapImp->m_Map.end(); - } - - void Copy(const MapIteratorImp & it) - { - m_Iterator = it.m_Iterator; - } - - std::map::iterator m_Iterator; - - }; - - class SequenceConstIteratorImp : public IteratorImp - { - - public: - - virtual Node::eType GetType() const - { - return Node::SequenceType; - } - - virtual void InitBegin(SequenceImp * pSequenceImp) - { - m_Iterator = pSequenceImp->m_Sequence.begin(); - } - - virtual void InitEnd(SequenceImp * pSequenceImp) - { - m_Iterator = pSequenceImp->m_Sequence.end(); - } - - virtual void InitBegin(MapImp * pMapImp) - { - } - - virtual void InitEnd(MapImp * pMapImp) - { - } - - void Copy(const SequenceConstIteratorImp & it) - { - m_Iterator = it.m_Iterator; - } - - std::map::const_iterator m_Iterator; - - }; - - class MapConstIteratorImp : public IteratorImp - { - - public: - - virtual Node::eType GetType() const - { - return Node::MapType; - } - - virtual void InitBegin(SequenceImp * pSequenceImp) - { - } - - virtual void InitEnd(SequenceImp * pSequenceImp) - { - } - - virtual void InitBegin(MapImp * pMapImp) - { - m_Iterator = pMapImp->m_Map.begin(); - } - - virtual void InitEnd(MapImp * pMapImp) - { - m_Iterator = pMapImp->m_Map.end(); - } - - void Copy(const MapConstIteratorImp & it) - { - m_Iterator = it.m_Iterator; - } - - std::map::const_iterator m_Iterator; - - }; - - - // Iterator class - Iterator::Iterator() : - m_Type(None), - m_pImp(nullptr) - { - } - - Iterator::~Iterator() - { - if(m_pImp) - { - switch(m_Type) - { - case SequenceType: - delete static_cast(m_pImp); - break; - case MapType: - delete static_cast(m_pImp); - break; - default: - break; - } - - } - } - - Iterator::Iterator(const Iterator & it) : - m_Type(None), - m_pImp(nullptr) - { - *this = it; - } - - Iterator & Iterator::operator = (const Iterator & it) - { - if(m_pImp) - { - switch(m_Type) - { - case SequenceType: - delete static_cast(m_pImp); - break; - case MapType: - delete static_cast(m_pImp); - break; - default: - break; - } - m_pImp = nullptr; - m_Type = None; - } - - IteratorImp * pNewImp = nullptr; - - switch(it.m_Type) - { - case SequenceType: - m_Type = SequenceType; - pNewImp = new SequenceIteratorImp; - static_cast(pNewImp)->m_Iterator = static_cast(it.m_pImp)->m_Iterator; - break; - case MapType: - m_Type = MapType; - pNewImp = new MapIteratorImp; - static_cast(pNewImp)->m_Iterator = static_cast(it.m_pImp)->m_Iterator; - break; - default: - break; - } - - m_pImp = pNewImp; - return *this; - } - - std::pair Iterator::operator *() - { - switch(m_Type) - { - case SequenceType: - return { g_EmptyString, *(static_cast(m_pImp)->m_Iterator->second)}; - break; - case MapType: - return {static_cast(m_pImp)->m_Iterator->first, - *(static_cast(m_pImp)->m_Iterator->second)}; - break; - default: - break; - } - - g_NoneNode.Clear(); - return { g_EmptyString, g_NoneNode}; - } - - Iterator & Iterator::operator ++ (int dummy) - { - switch(m_Type) - { - case SequenceType: - static_cast(m_pImp)->m_Iterator++; - break; - case MapType: - static_cast(m_pImp)->m_Iterator++; - break; - default: - break; - } - return *this; - } - - Iterator & Iterator::operator -- (int dummy) - { - switch(m_Type) - { - case SequenceType: - static_cast(m_pImp)->m_Iterator--; - break; - case MapType: - static_cast(m_pImp)->m_Iterator--; - break; - default: - break; - } - return *this; - } - - bool Iterator::operator == (const Iterator & it) - { - if(m_Type != it.m_Type) - { - return false; - } - - switch(m_Type) - { - case SequenceType: - return static_cast(m_pImp)->m_Iterator == static_cast(it.m_pImp)->m_Iterator; - break; - case MapType: - return static_cast(m_pImp)->m_Iterator == static_cast(it.m_pImp)->m_Iterator; - break; - default: - break; - } - - return false; - } - - bool Iterator::operator != (const Iterator & it) - { - return !(*this == it); - } - - - // Const Iterator class - ConstIterator::ConstIterator() : - m_Type(None), - m_pImp(nullptr) - { - } - - ConstIterator::~ConstIterator() - { - if(m_pImp) - { - switch(m_Type) - { - case SequenceType: - delete static_cast(m_pImp); - break; - case MapType: - delete static_cast(m_pImp); - break; - default: - break; - } - - } - } - - ConstIterator::ConstIterator(const ConstIterator & it) : - m_Type(None), - m_pImp(nullptr) - { - *this = it; - } - - ConstIterator & ConstIterator::operator = (const ConstIterator & it) - { - if(m_pImp) - { - switch(m_Type) - { - case SequenceType: - delete static_cast(m_pImp); - break; - case MapType: - delete static_cast(m_pImp); - break; - default: - break; - } - m_pImp = nullptr; - m_Type = None; - } - - IteratorImp * pNewImp = nullptr; - - switch(it.m_Type) - { - case SequenceType: - m_Type = SequenceType; - pNewImp = new SequenceConstIteratorImp; - static_cast(pNewImp)->m_Iterator = static_cast(it.m_pImp)->m_Iterator; - break; - case MapType: - m_Type = MapType; - pNewImp = new MapConstIteratorImp; - static_cast(pNewImp)->m_Iterator = static_cast(it.m_pImp)->m_Iterator; - break; - default: - break; - } - - m_pImp = pNewImp; - return *this; - } - - std::pair ConstIterator::operator *() - { - switch(m_Type) - { - case SequenceType: - return { g_EmptyString, *(static_cast(m_pImp)->m_Iterator->second)}; - break; - case MapType: - return {static_cast(m_pImp)->m_Iterator->first, - *(static_cast(m_pImp)->m_Iterator->second)}; - break; - default: - break; - } - - g_NoneNode.Clear(); - return { g_EmptyString, g_NoneNode}; - } - - ConstIterator & ConstIterator::operator ++ (int dummy) - { - switch(m_Type) - { - case SequenceType: - static_cast(m_pImp)->m_Iterator++; - break; - case MapType: - static_cast(m_pImp)->m_Iterator++; - break; - default: - break; - } - return *this; - } - - ConstIterator & ConstIterator::operator -- (int dummy) - { - switch(m_Type) - { - case SequenceType: - static_cast(m_pImp)->m_Iterator--; - break; - case MapType: - static_cast(m_pImp)->m_Iterator--; - break; - default: - break; - } - return *this; - } - - bool ConstIterator::operator == (const ConstIterator & it) - { - if(m_Type != it.m_Type) - { - return false; - } - - switch(m_Type) - { - case SequenceType: - return static_cast(m_pImp)->m_Iterator == static_cast(it.m_pImp)->m_Iterator; - break; - case MapType: - return static_cast(m_pImp)->m_Iterator == static_cast(it.m_pImp)->m_Iterator; - break; - default: - break; - } - - return false; - } - - bool ConstIterator::operator != (const ConstIterator & it) - { - return !(*this == it); - } - - - // Node class - Node::Node() : - m_pImp(new NodeImp) - { - } - - Node::Node(const Node & node) : - Node() - { - *this = node; - } - - Node::Node(const std::string & value) : - Node() - { - *this = value; - } - - Node::Node(const char * value) : - Node() - { - *this = value; - } - - Node::~Node() - { - delete static_cast(m_pImp); - } - - Node::eType Node::Type() const - { - return NODE_IMP->m_Type; - } - - bool Node::IsNone() const - { - return NODE_IMP->m_Type == Node::None; - } - - bool Node::IsSequence() const - { - return NODE_IMP->m_Type == Node::SequenceType; - } - - bool Node::IsMap() const - { - return NODE_IMP->m_Type == Node::MapType; - } - - bool Node::IsScalar() const - { - return NODE_IMP->m_Type == Node::ScalarType; - } - - void Node::Clear() - { - NODE_IMP->Clear(); - } - - size_t Node::Size() const - { - if(TYPE_IMP == nullptr) - { - return 0; - } - - return TYPE_IMP->GetSize(); - } - - Node & Node::Insert(const size_t index) - { - NODE_IMP->InitSequence(); - return *TYPE_IMP->Insert(index); - } - - Node & Node::PushFront() - { - NODE_IMP->InitSequence(); - return *TYPE_IMP->PushFront(); - } - Node & Node::PushBack() - { - NODE_IMP->InitSequence(); - return *TYPE_IMP->PushBack(); - } - - Node & Node::operator[](const size_t index) - { - NODE_IMP->InitSequence(); - Node * pNode = TYPE_IMP->GetNode(index); - if(pNode == nullptr) - { - g_NoneNode.Clear(); - return g_NoneNode; - } - return *pNode; - } - - Node & Node::operator[](const std::string & key) - { - NODE_IMP->InitMap(); - return *TYPE_IMP->GetNode(key); - } - - void Node::Erase(const size_t index) - { - if(TYPE_IMP == nullptr || NODE_IMP->m_Type != Node::SequenceType) - { - return; - } - - return TYPE_IMP->Erase(index); - } - - void Node::Erase(const std::string & key) - { - if(TYPE_IMP == nullptr || NODE_IMP->m_Type != Node::MapType) - { - return; - } - - return TYPE_IMP->Erase(key); - } - - Node & Node::operator = (const Node & node) - { - NODE_IMP->Clear(); - CopyNode(node, *this); - return *this; - } - - Node & Node::operator = (const std::string & value) - { - NODE_IMP->InitScalar(); - TYPE_IMP->SetData(value); - return *this; - } - - Node & Node::operator = (const char * value) - { - NODE_IMP->InitScalar(); - TYPE_IMP->SetData(value ? std::string(value) : ""); - return *this; - } - - Iterator Node::Begin() - { - Iterator it; - - if(TYPE_IMP != nullptr) - { - IteratorImp * pItImp = nullptr; - - switch(NODE_IMP->m_Type) - { - case Node::SequenceType: - it.m_Type = Iterator::SequenceType; - pItImp = new SequenceIteratorImp; - pItImp->InitBegin(static_cast(TYPE_IMP)); - break; - case Node::MapType: - it.m_Type = Iterator::MapType; - pItImp = new MapIteratorImp; - pItImp->InitBegin(static_cast(TYPE_IMP)); - break; - default: - break; - } - - it.m_pImp = pItImp; - } - - return it; - } - - ConstIterator Node::Begin() const - { - ConstIterator it; - - if(TYPE_IMP != nullptr) - { - IteratorImp * pItImp = nullptr; - - switch(NODE_IMP->m_Type) - { - case Node::SequenceType: - it.m_Type = ConstIterator::SequenceType; - pItImp = new SequenceConstIteratorImp; - pItImp->InitBegin(static_cast(TYPE_IMP)); - break; - case Node::MapType: - it.m_Type = ConstIterator::MapType; - pItImp = new MapConstIteratorImp; - pItImp->InitBegin(static_cast(TYPE_IMP)); - break; - default: - break; - } - - it.m_pImp = pItImp; - } - - return it; - } - - Iterator Node::End() - { - Iterator it; - - if(TYPE_IMP != nullptr) - { - IteratorImp * pItImp = nullptr; - - switch(NODE_IMP->m_Type) - { - case Node::SequenceType: - it.m_Type = Iterator::SequenceType; - pItImp = new SequenceIteratorImp; - pItImp->InitEnd(static_cast(TYPE_IMP)); - break; - case Node::MapType: - it.m_Type = Iterator::MapType; - pItImp = new MapIteratorImp; - pItImp->InitEnd(static_cast(TYPE_IMP)); - break; - default: - break; - } - - it.m_pImp = pItImp; - } - - return it; - } - - ConstIterator Node::End() const - { - ConstIterator it; - - if(TYPE_IMP != nullptr) - { - IteratorImp * pItImp = nullptr; - - switch(NODE_IMP->m_Type) - { - case Node::SequenceType: - it.m_Type = ConstIterator::SequenceType; - pItImp = new SequenceConstIteratorImp; - pItImp->InitEnd(static_cast(TYPE_IMP)); - break; - case Node::MapType: - it.m_Type = ConstIterator::MapType; - pItImp = new MapConstIteratorImp; - pItImp->InitEnd(static_cast(TYPE_IMP)); - break; - default: - break; - } - - it.m_pImp = pItImp; - } - - return it; - } - - const std::string & Node::AsString() const - { - if(TYPE_IMP == nullptr) - { - return g_EmptyString; - } - - return TYPE_IMP->GetData(); - } - - - - // Reader implementations - /** - * @breif Line information structure. - * - */ - class ReaderLine - { - - public: - - /** - * @breif Constructor. - * - */ - ReaderLine(const std::string & data = "", - const size_t no = 0, - const size_t offset = 0, - const Node::eType type = Node::None, - const unsigned char flags = 0) : - Data(data), - No(no), - Offset(offset), - Type(type), - Flags(flags), - NextLine(nullptr) - { - } - - enum eFlag - { - LiteralScalarFlag, ///< Literal scalar type, defined as "|". - FoldedScalarFlag, ///< Folded scalar type, defined as "<". - ScalarNewlineFlag ///< Scalar ends with a newline. - }; - - /** - * @breif Set flag. - * - */ - void SetFlag(const eFlag flag) - { - Flags |= FlagMask[static_cast(flag)]; - } - - /** - * @breif Set flags by mask value. - * - */ - void SetFlags(const unsigned char flags) - { - Flags |= flags; - } - - /** - * @breif Unset flag. - * - */ - void UnsetFlag(const eFlag flag) - { - Flags &= ~FlagMask[static_cast(flag)]; - } - - /** - * @breif Unset flags by mask value. - * - */ - void UnsetFlags(const unsigned char flags) - { - Flags &= ~flags; - } - - /** - * @breif Get flag value. - * - */ - bool GetFlag(const eFlag flag) const - { - return Flags & FlagMask[static_cast(flag)]; - } - - /** - * @breif Copy and replace scalar flags from another ReaderLine. - * - */ - void CopyScalarFlags(ReaderLine * from) - { - if (from == nullptr) - { - return; - } - - unsigned char newFlags = from->Flags & (FlagMask[0] | FlagMask[1] | FlagMask[2]); - Flags |= newFlags; - } - - static const unsigned char FlagMask[3]; - - std::string Data; ///< Data of line. - size_t No; ///< Line number. - size_t Offset; ///< Offset to first character in data. - Node::eType Type; ///< Type of line. - unsigned char Flags; ///< Flags of line. - ReaderLine * NextLine; ///< Pointer to next line. - - - - }; - - const unsigned char ReaderLine::FlagMask[3] = { 0x01, 0x02, 0x04 }; - - - /** - * @breif Implementation class of Yaml parsing. - * Parsing incoming stream and outputs a root node. - * - */ - class ParseImp - { - - public: - - /** - * @breif Default constructor. - * - */ - ParseImp() - { - } - - /** - * @breif Destructor. - * - */ - ~ParseImp() - { - ClearLines(); - } - - /** - * @breif Run full parsing procedure. - * - */ - void Parse(Node & root, std::iostream & stream) - { - try - { - root.Clear(); - ReadLines(stream); - PostProcessLines(); - //Print(); - ParseRoot(root); - } - catch(Exception e) - { - root.Clear(); - throw; - } - } - - private: - - /** - * @breif Copy constructor. - * - */ - ParseImp(const ParseImp & copy) - { - - } - - /** - * @breif Read all lines. - * Ignoring: - * - Empty lines. - * - Comments. - * - Document start/end. - * - */ - void ReadLines(std::iostream & stream) - { - std::string line = ""; - size_t lineNo = 0; - bool documentStartFound = false; - bool foundFirstNotEmpty = false; - std::streampos streamPos = 0; - - // Read all lines, as long as the stream is ok. - while (!stream.eof() && !stream.fail()) - { - // Read line - streamPos = stream.tellg(); - std::getline(stream, line); - lineNo++; - - // Remove comment - const size_t commentPos = FindNotCited(line, '#'); - if(commentPos != std::string::npos) - { - line.resize(commentPos); - } - - // Start of document. - if (documentStartFound == false && line == "---") - { - // Erase all lines before this line. - ClearLines(); - documentStartFound = true; - continue; - } - - // End of document. - if (line == "...") - { - break; - } - else if(line == "---") - { - stream.seekg(streamPos); - break; - } - - // Remove trailing return. - if (line.size()) - { - if (line[line.size() - 1] == '\r') - { - line.resize(line.size() - 1); - } - } - - // Validate characters. - for (size_t i = 0; i < line.size(); i++) - { - if (line[i] != '\t' && (line[i] < 32 || line[i] > 125)) - { - throw ParsingException(ExceptionMessage(g_ErrorInvalidCharacter, lineNo, i + 1)); - } - } - - // Validate tabs - const size_t firstTabPos = line.find_first_of('\t'); - size_t startOffset = line.find_first_not_of(" \t"); - - // Make sure no tabs are in the very front. - if (startOffset != std::string::npos) - { - if(firstTabPos < startOffset) - { - throw ParsingException(ExceptionMessage(g_ErrorTabInOffset, lineNo, firstTabPos)); - } - - // Remove front spaces. - line = line.substr(startOffset); - } - else - { - startOffset = 0; - line = ""; - } - - // Add line. - if(foundFirstNotEmpty == false) - { - if(line.size()) - { - foundFirstNotEmpty = true; - } - else - { - continue; - } - } - - ReaderLine * pLine = new ReaderLine(line, lineNo, startOffset); - m_Lines.push_back(pLine); - } - } - - /** - * @breif Run post-processing on all lines. - * Basically split lines into multiple lines if needed, to follow the parsing algorithm. - * - */ - void PostProcessLines() - { - for (auto it = m_Lines.begin(); it != m_Lines.end();) - { - // Sequence. - if (PostProcessSequenceLine(it) == true) - { - continue; - } - - // Mapping. - if (PostProcessMappingLine(it) == true) - { - continue; - } - - // Scalar. - PostProcessScalarLine(it); - } - - // Set next line of all lines. - if (m_Lines.size()) - { - if (m_Lines.back()->Type != Node::ScalarType) - { - throw ParsingException(ExceptionMessage(g_ErrorUnexpectedDocumentEnd, *m_Lines.back())); - } - - if (m_Lines.size() > 1) - { - auto prevEnd = m_Lines.end(); - --prevEnd; - - for (auto it = m_Lines.begin(); it != prevEnd; it++) - { - auto nextIt = it; - ++nextIt; - - (*it)->NextLine = *nextIt; - } - } - } - } - - /** - * @breif Run post-processing and check for sequence. - * Split line into two lines if sequence token is not on it's own line. - * - * @return true if line is sequence, else false. - * - */ - bool PostProcessSequenceLine(std::list::iterator & it) - { - ReaderLine * pLine = *it; - - // Sequence split - if (IsSequenceStart(pLine->Data) == false) - { - return false; - } - - pLine->Type = Node::SequenceType; - - ClearTrailingEmptyLines(++it); - - const size_t valueStart = pLine->Data.find_first_not_of(" \t", 1); - if (valueStart == std::string::npos) - { - return true; - } - - // Create new line and insert - std::string newLine = pLine->Data.substr(valueStart); - it = m_Lines.insert(it, new ReaderLine(newLine, pLine->No, pLine->Offset + valueStart)); - pLine->Data = ""; - - return false; - } - - /** - * @breif Run post-processing and check for mapping. - * Split line into two lines if mapping value is not on it's own line. - * - * @return true if line is mapping, else move on to scalar parsing. - * - */ - bool PostProcessMappingLine(std::list::iterator & it) - { - ReaderLine * pLine = *it; - - // Find map key. - size_t preKeyQuotes = 0; - size_t tokenPos = FindNotCited(pLine->Data, ':', preKeyQuotes); - if (tokenPos == std::string::npos) - { - return false; - } - if(preKeyQuotes > 1) - { - throw ParsingException(ExceptionMessage(g_ErrorKeyIncorrect, *pLine)); - } - - pLine->Type = Node::MapType; - - // Get key - std::string key = pLine->Data.substr(0, tokenPos); - const size_t keyEnd = key.find_last_not_of(" \t"); - if (keyEnd == std::string::npos) - { - throw ParsingException(ExceptionMessage(g_ErrorKeyMissing, *pLine)); - } - key.resize(keyEnd + 1); - - // Handle cited key. - if(preKeyQuotes == 1) - { - if(key.front() != '"' || key.back() != '"') - { - throw ParsingException(ExceptionMessage(g_ErrorKeyIncorrect, *pLine)); - } - - key = key.substr(1, key.size() - 2); - } - RemoveAllEscapeTokens(key); - - // Get value - std::string value = ""; - size_t valueStart = std::string::npos; - if (tokenPos + 1 != pLine->Data.size()) - { - valueStart = pLine->Data.find_first_not_of(" \t", tokenPos + 1); - if (valueStart != std::string::npos) - { - value = pLine->Data.substr(valueStart); - } - } - - // Make sure the value is not a sequence start. - if (IsSequenceStart(value) == true) - { - throw ParsingException(ExceptionMessage(g_ErrorBlockSequenceNotAllowed, *pLine, valueStart)); - } - - pLine->Data = key; - - - // Remove all empty lines after map key. - ClearTrailingEmptyLines(++it); - - // Add new empty line? - size_t newLineOffset = valueStart; - if(newLineOffset == std::string::npos) - { - if(it != m_Lines.end() && (*it)->Offset > pLine->Offset) - { - return true; - } - - newLineOffset = tokenPos + 2; - } - else - { - newLineOffset += pLine->Offset; - } - - // Add new line with value. - unsigned char dummyBlockFlags = 0; - if(IsBlockScalar(value, pLine->No, dummyBlockFlags) == true) - { - newLineOffset = pLine->Offset; - } - ReaderLine * pNewLine = new ReaderLine(value, pLine->No, newLineOffset, Node::ScalarType); - it = m_Lines.insert(it, pNewLine); - - // Return false in order to handle next line(scalar value). - return false; - } - - /** - * @breif Run post-processing and check for scalar. - * Checking for multi-line scalars. - * - * @return true if scalar search should continue, else false. - * - */ - void PostProcessScalarLine(std::list::iterator & it) - { - ReaderLine * pLine = *it; - pLine->Type = Node::ScalarType; - - size_t parentOffset = pLine->Offset; - if(pLine != m_Lines.front()) - { - std::list::iterator lastIt = it; - --lastIt; - parentOffset = (*lastIt)->Offset; - } - - std::list::iterator lastNotEmpty = it++; - - // Find last empty lines - while(it != m_Lines.end()) - { - pLine = *it; - pLine->Type = Node::ScalarType; - if(pLine->Data.size()) - { - if(pLine->Offset <= parentOffset) - { - break; - } - else - { - lastNotEmpty = it; - } - } - ++it; - } - - ClearTrailingEmptyLines(++lastNotEmpty); - } - - /** - * @breif Process root node and start of document. - * - */ - void ParseRoot(Node & root) - { - // Get first line and start type. - auto it = m_Lines.begin(); - if(it == m_Lines.end()) - { - return; - } - Node::eType type = (*it)->Type; - ReaderLine * pLine = *it; - - // Handle next line. - switch(type) - { - case Node::SequenceType: - ParseSequence(root, it); - break; - case Node::MapType: - ParseMap(root, it); - break; - case Node::ScalarType: - ParseScalar(root, it); - break; - default: - break; - } - - if(it != m_Lines.end()) - { - throw InternalException(ExceptionMessage(g_ErrorUnexpectedDocumentEnd, *pLine)); - } - - } - - /** - * @breif Process sequence node. - * - */ - void ParseSequence(Node & node, std::list::iterator & it) - { - ReaderLine * pNextLine = nullptr; - while(it != m_Lines.end()) - { - ReaderLine * pLine = *it; - Node & childNode = node.PushBack(); - - // Move to next line, error check. - ++it; - if(it == m_Lines.end()) - { - throw InternalException(ExceptionMessage(g_ErrorUnexpectedDocumentEnd, *pLine)); - } - - // Handle value of map - Node::eType valueType = (*it)->Type; - switch(valueType) - { - case Node::SequenceType: - ParseSequence(childNode, it); - break; - case Node::MapType: - ParseMap(childNode, it); - break; - case Node::ScalarType: - ParseScalar(childNode, it); - break; - default: - break; - } - - // Check next line. if sequence and correct level, go on, else exit. - // If same level but but of type map = error. - if(it == m_Lines.end() || ((pNextLine = *it)->Offset < pLine->Offset)) - { - break; - } - if(pNextLine->Offset > pLine->Offset) - { - throw ParsingException(ExceptionMessage(g_ErrorIncorrectOffset, *pNextLine)); - } - if(pNextLine->Type != Node::SequenceType) - { - throw InternalException(ExceptionMessage(g_ErrorDiffEntryNotAllowed, *pNextLine)); - } - - } - } - - /** - * @breif Process map node. - * - */ - void ParseMap(Node & node, std::list::iterator & it) - { - ReaderLine * pNextLine = nullptr; - while(it != m_Lines.end()) - { - ReaderLine * pLine = *it; - Node & childNode = node[pLine->Data]; - - // Move to next line, error check. - ++it; - if(it == m_Lines.end()) - { - throw InternalException(ExceptionMessage(g_ErrorUnexpectedDocumentEnd, *pLine)); - } - - // Handle value of map - Node::eType valueType = (*it)->Type; - switch(valueType) - { - case Node::SequenceType: - ParseSequence(childNode, it); - break; - case Node::MapType: - ParseMap(childNode, it); - break; - case Node::ScalarType: - ParseScalar(childNode, it); - break; - default: - break; - } - - // Check next line. if map and correct level, go on, else exit. - // if same level but but of type map = error. - if(it == m_Lines.end() || ((pNextLine = *it)->Offset < pLine->Offset)) - { - break; - } - if(pNextLine->Offset > pLine->Offset) - { - throw ParsingException(ExceptionMessage(g_ErrorIncorrectOffset, *pNextLine)); - } - if(pNextLine->Type != pLine->Type) - { - throw InternalException(ExceptionMessage(g_ErrorDiffEntryNotAllowed, *pNextLine)); - } - - } - } - - /** - * @breif Process scalar node. - * - */ - void ParseScalar(Node & node, std::list::iterator & it) - { - std::string data = ""; - ReaderLine * pFirstLine = *it; - ReaderLine * pLine = *it; - - // Check if current line is a block scalar. - unsigned char blockFlags = 0; - bool isBlockScalar = IsBlockScalar(pLine->Data, pLine->No, blockFlags); - const bool newLineFlag = static_cast(blockFlags & ReaderLine::FlagMask[static_cast(ReaderLine::ScalarNewlineFlag)]); - const bool foldedFlag = static_cast(blockFlags & ReaderLine::FlagMask[static_cast(ReaderLine::FoldedScalarFlag)]); - const bool literalFlag = static_cast(blockFlags & ReaderLine::FlagMask[static_cast(ReaderLine::LiteralScalarFlag)]); - size_t parentOffset = 0; - - // Find parent offset - if(it != m_Lines.begin()) - { - std::list::iterator parentIt = it; - --parentIt; - parentOffset = (*parentIt)->Offset; - } - - // Move to next iterator/line if current line is a block scalar. - if(isBlockScalar) - { - ++it; - if(it == m_Lines.end() || (pLine = *it)->Type != Node::ScalarType) - { - return; - } - } - - // Not a block scalar, cut end spaces/tabs - if(isBlockScalar == false) - { - while(1) - { - pLine = *it; - - if(parentOffset != 0 && pLine->Offset <= parentOffset) - { - throw ParsingException(ExceptionMessage(g_ErrorIncorrectOffset, *pLine)); - } - - const size_t endOffset = pLine->Data.find_last_not_of(" \t"); - if(endOffset == std::string::npos) - { - data += "\n"; - } - else - { - data += pLine->Data.substr(0, endOffset + 1); - } - - // Move to next line - ++it; - if(it == m_Lines.end() || (*it)->Type != Node::ScalarType) - { - break; - } - - data += " "; - } - - if(ValidateQuote(data) == false) - { - throw ParsingException(ExceptionMessage(g_ErrorInvalidQuote, *pFirstLine)); - } - } - // Block scalar - else - { - pLine = *it; - size_t blockOffset = pLine->Offset; - if(blockOffset <= parentOffset) - { - throw ParsingException(ExceptionMessage(g_ErrorIncorrectOffset, *pLine)); - } - - bool addedSpace = false; - while(it != m_Lines.end() && (*it)->Type == Node::ScalarType) - { - pLine = *it; - - const size_t endOffset = pLine->Data.find_last_not_of(" \t"); - if(endOffset != std::string::npos && pLine->Offset < blockOffset) - { - throw ParsingException(ExceptionMessage(g_ErrorIncorrectOffset, *pLine)); - } - - if(endOffset == std::string::npos) - { - if(addedSpace) - { - data[data.size() - 1] = '\n'; - addedSpace = false; - } - else - { - data += "\n"; - } - - ++it; - continue; - } - else - { - if(blockOffset != pLine->Offset && foldedFlag) - { - if(addedSpace) - { - data[data.size() - 1] = '\n'; - addedSpace = false; - } - else - { - data += "\n"; - } - } - data += std::string(pLine->Offset - blockOffset, ' '); - data += pLine->Data; - } - - // Move to next line - ++it; - if(it == m_Lines.end() || (*it)->Type != Node::ScalarType) - { - if(newLineFlag) - { - data += "\n"; - } - break; - } - - if(foldedFlag) - { - data += " "; - addedSpace = true; - } - else if(literalFlag && endOffset != std::string::npos) - { - data += "\n"; - } - } - } - - if(data.size() && (data[0] == '"' || data[0] == '\'')) - { - data = data.substr(1, data.size() - 2 ); - } - - node = data; - } - - /** - * @breif Debug printing. - * - */ - void Print() - { - for (auto it = m_Lines.begin(); it != m_Lines.end(); it++) - { - - ReaderLine * pLine = *it; - - // Print type - if (pLine->Type == Node::SequenceType) - { - std::cout << "seq "; - } - else if (pLine->Type == Node::MapType) - { - std::cout << "map "; - } - else if (pLine->Type == Node::ScalarType) - { - std::cout << "sca "; - } - else - { - std::cout << " "; - } - - // Print flags - if (pLine->GetFlag(ReaderLine::FoldedScalarFlag)) - { - std::cout << "f"; - } - else - { - std::cout << "-"; - } - if (pLine->GetFlag(ReaderLine::LiteralScalarFlag)) - { - std::cout << "l"; - } - else - { - std::cout << "-"; - } - if (pLine->GetFlag(ReaderLine::ScalarNewlineFlag)) - { - std::cout << "n"; - } - else - { - std::cout << "-"; - } - if (pLine->NextLine == nullptr) - { - std::cout << "e"; - } - else - { - std::cout << "-"; - } - - - std::cout << "| "; - std::cout << pLine->No << " "; - std::cout << std::string(pLine->Offset, ' '); - - if (pLine->Type == Node::ScalarType) - { - std::string scalarValue = pLine->Data; - for (size_t i = 0; (i = scalarValue.find("\n", i)) != std::string::npos;) - { - scalarValue.replace(i, 1, "\\n"); - i += 2; - } - std::cout << scalarValue << std::endl; - } - else if (pLine->Type == Node::MapType) - { - std::cout << pLine->Data + ":" << std::endl; - } - else if (pLine->Type == Node::SequenceType) - { - std::cout << "-" << std::endl; - } - else - { - std::cout << "> UNKOWN TYPE <" << std::endl; - } - } - } - - /** - * @breif Clear all read lines. - * - */ - void ClearLines() - { - for (auto it = m_Lines.begin(); it != m_Lines.end(); it++) - { - delete *it; - } - m_Lines.clear(); - } - - void ClearTrailingEmptyLines(std::list::iterator & it) - { - while(it != m_Lines.end()) - { - ReaderLine * pLine = *it; - if(pLine->Data.size() == 0) - { - delete *it; - it = m_Lines.erase(it); - } - else - { - return; - } - - } - } - - static bool IsSequenceStart(const std::string & data) - { - if (data.size() == 0 || data[0] != '-') - { - return false; - } - - if (data.size() >= 2 && data[1] != ' ') - { - return false; - } - - return true; - } - - static bool IsBlockScalar(const std::string & data, const size_t line, unsigned char & flags) - { - flags = 0; - if(data.size() == 0) - { - return false; - } - - if(data[0] == '|') - { - if(data.size() >= 2) - { - if(data[1] != '-' && data[1] != ' ' && data[1] != '\t') - { - throw ParsingException(ExceptionMessage(g_ErrorInvalidBlockScalar, line, data)); - } - } - else - { - flags |= ReaderLine::FlagMask[static_cast(ReaderLine::ScalarNewlineFlag)]; - } - flags |= ReaderLine::FlagMask[static_cast(ReaderLine::LiteralScalarFlag)]; - return true; - } - - if(data[0] == '>') - { - if(data.size() >= 2) - { - if(data[1] != '-' && data[1] != ' ' && data[1] != '\t') - { - throw ParsingException(ExceptionMessage(g_ErrorInvalidBlockScalar, line, data)); - } - } - else - { - flags |= ReaderLine::FlagMask[static_cast(ReaderLine::ScalarNewlineFlag)]; - } - flags |= ReaderLine::FlagMask[static_cast(ReaderLine::FoldedScalarFlag)]; - return true; - } - - return false; - } - - std::list m_Lines; ///< List of lines. - - }; - - // Parsing functions - void Parse(Node & root, const char * filename) - { - std::ifstream f(filename, std::ifstream::binary); - if (f.is_open() == false) - { - throw OperationException(g_ErrorCannotOpenFile); - } - - f.seekg(0, f.end); - size_t fileSize = static_cast(f.tellg()); - f.seekg(0, f.beg); - - std::unique_ptr data(new char[fileSize]); - f.read(data.get(), fileSize); - f.close(); - - Parse(root, data.get(), fileSize); - } - - void Parse(Node & root, std::iostream & stream) - { - ParseImp * pImp = nullptr; - - try - { - pImp = new ParseImp; - pImp->Parse(root, stream); - delete pImp; - } - catch (const Exception e) - { - delete pImp; - throw; - } - } - - void Parse(Node & root, const std::string & string) - { - std::stringstream ss(string); - Parse(root, ss); - } - - void Parse(Node & root, const char * buffer, const size_t size) - { - std::stringstream ss(std::string(buffer, size)); - Parse(root, ss); - } - - - // Serialize configuration structure. - SerializeConfig::SerializeConfig(const size_t spaceIndentation, - const size_t scalarMaxLength, - const bool sequenceMapNewline, - const bool mapScalarNewline) : - SpaceIndentation(spaceIndentation), - ScalarMaxLength(scalarMaxLength), - SequenceMapNewline(sequenceMapNewline), - MapScalarNewline(mapScalarNewline) - { - } - - - // Serialization functions - void Serialize(const Node & root, const char * filename, const SerializeConfig & config) - { - std::stringstream stream; - Serialize(root, stream, config); - - std::ofstream f(filename); - if (f.is_open() == false) - { - throw OperationException(g_ErrorCannotOpenFile); - } - - f.write(stream.str().c_str(), stream.str().size()); - f.close(); - } - - size_t LineFolding(const std::string & input, std::vector & folded, const size_t maxLength) - { - folded.clear(); - if(input.size() == 0) - { - return 0; - } - - size_t currentPos = 0; - size_t lastPos = 0; - size_t spacePos = std::string::npos; - while(currentPos < input.size()) - { - currentPos = lastPos + maxLength; - - if(currentPos < input.size()) - { - spacePos = input.find_first_of(' ', currentPos); - } - - if(spacePos == std::string::npos || currentPos >= input.size()) - { - const std::string endLine = input.substr(lastPos); - if(endLine.size()) - { - folded.push_back(endLine); - } - - return folded.size(); - } - - folded.push_back(input.substr(lastPos, spacePos - lastPos)); - - lastPos = spacePos + 1; - } - - return folded.size(); - } - - static void SerializeLoop(const Node & node, std::iostream & stream, bool useLevel, const size_t level, const SerializeConfig & config) - { - const size_t indention = config.SpaceIndentation; - - switch(node.Type()) - { - case Node::SequenceType: - { - for(auto it = node.Begin(); it != node.End(); it++) - { - const Node & value = (*it).second; - if(value.IsNone()) - { - continue; - } - stream << std::string(level, ' ') << "- "; - useLevel = false; - if(value.IsSequence() || (value.IsMap() && config.SequenceMapNewline == true)) - { - useLevel = true; - stream << "\n"; - } - - SerializeLoop(value, stream, useLevel, level + 2, config); - } - - } - break; - case Node::MapType: - { - size_t count = 0; - for(auto it = node.Begin(); it != node.End(); it++) - { - const Node & value = (*it).second; - if(value.IsNone()) - { - continue; - } - - if(useLevel || count > 0) - { - stream << std::string(level, ' '); - } - - std::string key = (*it).first; - AddEscapeTokens(key, "\\\""); - if(ShouldBeCited(key)) - { - stream << "\"" << key << "\"" << ": "; - } - else - { - stream << key << ": "; - } - - - useLevel = false; - if(value.IsScalar() == false || (value.IsScalar() && config.MapScalarNewline)) - { - useLevel = true; - stream << "\n"; - } - - SerializeLoop(value, stream, useLevel, level + indention, config); - - useLevel = true; - count++; - } - - } - break; - case Node::ScalarType: - { - const std::string value = node.As(); - - // Empty scalar - if(value.size() == 0) - { - stream << "\n"; - break; - } - - // Get lines of scalar. - std::string line = ""; - std::vector lines; - std::istringstream iss(value); - while (iss.eof() == false) - { - std::getline(iss, line); - lines.push_back(line); - } - - // Block scalar - const std::string & lastLine = lines.back(); - const bool endNewline = lastLine.size() == 0; - if(endNewline) - { - lines.pop_back(); - } - - // Literal - if(lines.size() > 1) - { - stream << "|"; - } - // Folded/plain - else - { - const std::string frontLine = lines.front(); - if(config.ScalarMaxLength == 0 || lines.front().size() <= config.ScalarMaxLength || - LineFolding(frontLine, lines, config.ScalarMaxLength) == 1) - { - if(useLevel) - { - stream << std::string(level, ' '); - } - - if(ShouldBeCited(value)) - { - stream << "\"" << value << "\"\n"; - break; - } - stream << value << "\n"; - break; - } - else - { - stream << ">"; - } - } - - if(endNewline == false) - { - stream << "-"; - } - stream << "\n"; - - - for(auto it = lines.begin(); it != lines.end(); it++) - { - stream << std::string(level, ' ') << (*it) << "\n"; - } - } - break; - - default: - break; - } - } - - void Serialize(const Node & root, std::iostream & stream, const SerializeConfig & config) - { - if(config.SpaceIndentation < 2) - { - throw OperationException(g_ErrorIndentation); - } - - SerializeLoop(root, stream, false, 0, config); - } - - void Serialize(const Node & root, std::string & string, const SerializeConfig & config) - { - std::stringstream stream; - Serialize(root, stream, config); - string = stream.str(); - } - - - - // Static function implementations - std::string ExceptionMessage(const std::string & message, ReaderLine & line) - { - return message + std::string(" Line ") + std::to_string(line.No) + std::string(": ") + line.Data; - } - - std::string ExceptionMessage(const std::string & message, ReaderLine & line, const size_t errorPos) - { - return message + std::string(" Line ") + std::to_string(line.No) + std::string(" column ") + std::to_string(errorPos + 1) + std::string(": ") + line.Data; - } - - std::string ExceptionMessage(const std::string & message, const size_t errorLine, const size_t errorPos) - { - return message + std::string(" Line ") + std::to_string(errorLine) + std::string(" column ") + std::to_string(errorPos); - } - - std::string ExceptionMessage(const std::string & message, const size_t errorLine, const std::string & data) - { - return message + std::string(" Line ") + std::to_string(errorLine) + std::string(": ") + data; - } - - bool FindQuote(const std::string & input, size_t & start, size_t & end, size_t searchPos) - { - start = end = std::string::npos; - size_t qPos = searchPos; - bool foundStart = false; - - while(qPos != std::string::npos) - { - // Find first quote. - qPos = input.find_first_of("\"'", qPos); - if(qPos == std::string::npos) - { - return false; - } - - const char token = input[qPos]; - if(token == '"' && (qPos == 0 || input[qPos-1] != '\\')) - { - // Found start quote. - if(foundStart == false) - { - start = qPos; - foundStart = true; - } - // Found end quote - else - { - end = qPos; - return true; - } - } - - // Check if it's possible for another loop. - if(qPos + 1 == input.size()) - { - return false; - } - qPos++; - } - - return false; - } - - size_t FindNotCited(const std::string & input, char token, size_t & preQuoteCount) - { - preQuoteCount = 0; - size_t tokenPos = input.find_first_of(token); - if(tokenPos == std::string::npos) - { - return std::string::npos; - } - - // Find all quotes - std::vector> quotes; - - size_t quoteStart = 0; - size_t quoteEnd = 0; - while(FindQuote(input, quoteStart, quoteEnd, quoteEnd)) - { - quotes.push_back({quoteStart, quoteEnd}); - - if(quoteEnd + 1 == input.size()) - { - break; - } - quoteEnd++; - } - - if(quotes.size() == 0) - { - return tokenPos; - } - - size_t currentQuoteIndex = 0; - std::pair currentQuote = {0, 0}; - - while(currentQuoteIndex < quotes.size()) - { - currentQuote = quotes[currentQuoteIndex]; - - if(tokenPos < currentQuote.first) - { - return tokenPos; - } - preQuoteCount++; - if(tokenPos <= currentQuote.second) - { - // Find next token - if(tokenPos + 1 == input.size()) - { - return std::string::npos; - } - tokenPos = input.find_first_of(token, tokenPos + 1); - if(tokenPos == std::string::npos) - { - return std::string::npos; - } - } - - currentQuoteIndex++; - } - - return tokenPos; - } - - size_t FindNotCited(const std::string & input, char token) - { - size_t dummy = 0; - return FindNotCited(input, token, dummy); - } - - bool ValidateQuote(const std::string & input) - { - if(input.size() == 0) - { - return true; - } - - char token = 0; - size_t searchPos = 0; - if(input[0] == '\"' || input[0] == '\'') - { - if(input.size() == 1) - { - return false; - } - token = input[0]; - searchPos = 1; - } - - while(searchPos != std::string::npos && searchPos < input.size() - 1) - { - searchPos = input.find_first_of("\"'", searchPos + 1); - if(searchPos == std::string::npos) - { - break; - } - - const char foundToken = input[searchPos]; - - if(input[searchPos] == '\"' || input[searchPos] == '\'') - { - if(token == 0 && input[searchPos-1] != '\\') - { - return false; - } - //if(foundToken == token) - //{ - - /*if(foundToken == token && searchPos == input.size() - 1 && input[searchPos-1] != '\\') - { - return true; - if(searchPos == input.size() - 1) - { - return true; - } - return false; - } - else */ - if(foundToken == token && input[searchPos-1] != '\\') - { - if(searchPos == input.size() - 1) - { - return true; - } - return false; - } - //} - } - } - - return token == 0; - } - - void CopyNode(const Node & from, Node & to) - { - const Node::eType type = from.Type(); - - switch(type) - { - case Node::SequenceType: - for(auto it = from.Begin(); it != from.End(); it++) - { - const Node & currentNode = (*it).second; - Node & newNode = to.PushBack(); - CopyNode(currentNode, newNode); - } - break; - case Node::MapType: - for(auto it = from.Begin(); it != from.End(); it++) - { - const Node & currentNode = (*it).second; - Node & newNode = to[(*it).first]; - CopyNode(currentNode, newNode); - } - break; - case Node::ScalarType: - to = from.As(); - break; - case Node::None: - break; - } - } - - bool ShouldBeCited(const std::string & key) - { - return key.find_first_of("\":{}[],&*#?|-<>=!%@") != std::string::npos; - } - - void AddEscapeTokens(std::string & input, const std::string & tokens) - { - for(auto it = tokens.begin(); it != tokens.end(); it++) - { - const char token = *it; - const std::string replace = std::string("\\") + std::string(1, token); - size_t found = input.find_first_of(token); - while(found != std::string::npos) - { - input.replace(found, 1, replace); - found = input.find_first_of(token, found + 2); - } - } - } - - void RemoveAllEscapeTokens(std::string & input) - { - size_t found = input.find_first_of("\\"); - while(found != std::string::npos) - { - if(found + 1 == input.size()) - { - return; - } - - std::string replace(1, input[found + 1]); - input.replace(found, 2, replace); - found = input.find_first_of("\\", found + 1); - } - } - - -} diff --git a/deps/mini-yaml/Yaml.hpp b/deps/mini-yaml/Yaml.hpp deleted file mode 100644 index 586657f..0000000 --- a/deps/mini-yaml/Yaml.hpp +++ /dev/null @@ -1,656 +0,0 @@ -/* -* MIT License -* -* Copyright(c) 2018 Jimmie Bergmann -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files(the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions : -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -* -*/ - -/* -YAML documentation: -http://yaml.org/spec/1.0/index.html -https://www.codeproject.com/Articles/28720/YAML-Parser-in-C -*/ - -#pragma once - -#include -#include -#include -#include -#include -#include - -/** -* @breif Namespace wrapping mini-yaml classes. -* -*/ -namespace Yaml -{ - - /** - * @breif Forward declarations. - * - */ - class Node; - - - /** - * @breif Helper classes and functions - * - */ - namespace impl - { - - /** - * @breif Helper functionality, converting string to any data type. - * Strings are left untouched. - * - */ - template - struct StringConverter - { - static T Get(const std::string & data) - { - T type; - std::stringstream ss(data); - ss >> type; - return type; - } - - static T Get(const std::string & data, const T & defaultValue) - { - T type; - std::stringstream ss(data); - ss >> type; - - if(ss.fail()) - { - return defaultValue; - } - - return type; - } - }; - template<> - struct StringConverter - { - static std::string Get(const std::string & data) - { - return data; - } - - static std::string Get(const std::string & data, const std::string & defaultValue) - { - if(data.size() == 0) - { - return defaultValue; - } - return data; - } - }; - - template<> - struct StringConverter - { - static bool Get(const std::string & data) - { - std::string tmpData = data; - std::transform(tmpData.begin(), tmpData.end(), tmpData.begin(), ::tolower); - if(tmpData == "true" || tmpData == "yes" || tmpData == "1") - { - return true; - } - - return false; - } - - static bool Get(const std::string & data, const bool & defaultValue) - { - if(data.size() == 0) - { - return defaultValue; - } - - return Get(data); - } - }; - - } - - - /** - * @breif Exception class. - * - */ - class Exception : public std::runtime_error - { - - public: - - /** - * @breif Enumeration of exception types. - * - */ - enum eType - { - InternalError, ///< Internal error. - ParsingError, ///< Invalid parsing data. - OperationError ///< User operation error. - }; - - /** - * @breif Constructor. - * - * @param message Exception message. - * @param type Type of exception. - * - */ - Exception(const std::string & message, const eType type); - - /** - * @breif Get type of exception. - * - */ - eType Type() const; - - /** - * @breif Get message of exception. - * - */ - const char * Message() const; - - private: - - eType m_Type; ///< Type of exception. - - }; - - - /** - * @breif Internal exception class. - * - * @see Exception - * - */ - class InternalException : public Exception - { - - public: - - /** - * @breif Constructor. - * - * @param message Exception message. - * - */ - InternalException(const std::string & message); - - }; - - - /** - * @breif Parsing exception class. - * - * @see Exception - * - */ - class ParsingException : public Exception - { - - public: - - /** - * @breif Constructor. - * - * @param message Exception message. - * - */ - ParsingException(const std::string & message); - - }; - - - /** - * @breif Operation exception class. - * - * @see Exception - * - */ - class OperationException : public Exception - { - - public: - - /** - * @breif Constructor. - * - * @param message Exception message. - * - */ - OperationException(const std::string & message); - - }; - - - /** - * @breif Iterator class. - * - */ - class Iterator - { - - public: - - friend class Node; - - /** - * @breif Default constructor. - * - */ - Iterator(); - - /** - * @breif Copy constructor. - * - */ - Iterator(const Iterator & it); - - /** - * @breif Assignment operator. - * - */ - Iterator & operator = (const Iterator & it); - - /** - * @breif Destructor. - * - */ - ~Iterator(); - - /** - * @breif Get node of iterator. - * First pair item is the key of map value, empty if type is sequence. - * - */ - std::pair operator *(); - - /** - * @breif Post-increment operator. - * - */ - Iterator & operator ++ (int); - - /** - * @breif Post-decrement operator. - * - */ - Iterator & operator -- (int); - - /** - * @breif Check if iterator is equal to other iterator. - * - */ - bool operator == (const Iterator & it); - - /** - * @breif Check if iterator is not equal to other iterator. - * - */ - bool operator != (const Iterator & it); - - private: - - enum eType - { - None, - SequenceType, - MapType - }; - - eType m_Type; ///< Type of iterator. - void * m_pImp; ///< Implementation of iterator class. - - }; - - - /** - * @breif Constant iterator class. - * - */ - class ConstIterator - { - - public: - - friend class Node; - - /** - * @breif Default constructor. - * - */ - ConstIterator(); - - /** - * @breif Copy constructor. - * - */ - ConstIterator(const ConstIterator & it); - - /** - * @breif Assignment operator. - * - */ - ConstIterator & operator = (const ConstIterator & it); - - /** - * @breif Destructor. - * - */ - ~ConstIterator(); - - /** - * @breif Get node of iterator. - * First pair item is the key of map value, empty if type is sequence. - * - */ - std::pair operator *(); - - /** - * @breif Post-increment operator. - * - */ - ConstIterator & operator ++ (int); - - /** - * @breif Post-decrement operator. - * - */ - ConstIterator & operator -- (int); - - /** - * @breif Check if iterator is equal to other iterator. - * - */ - bool operator == (const ConstIterator & it); - - /** - * @breif Check if iterator is not equal to other iterator. - * - */ - bool operator != (const ConstIterator & it); - - private: - - enum eType - { - None, - SequenceType, - MapType - }; - - eType m_Type; ///< Type of iterator. - void * m_pImp; ///< Implementation of constant iterator class. - - }; - - - /** - * @breif Node class. - * - */ - class Node - { - - public: - - friend class Iterator; - - /** - * @breif Enumeration of node types. - * - */ - enum eType - { - None, - SequenceType, - MapType, - ScalarType - }; - - /** - * @breif Default constructor. - * - */ - Node(); - - /** - * @breif Copy constructor. - * - */ - Node(const Node & node); - - /** - * @breif Assignment constructors. - * Converts node to scalar type if needed. - * - */ - Node(const std::string & value); - Node(const char * value); - - /** - * @breif Destructor. - * - */ - ~Node(); - - /** - * @breif Functions for checking type of node. - * - */ - eType Type() const; - bool IsNone() const; - bool IsSequence() const; - bool IsMap() const; - bool IsScalar() const; - - /** - * @breif Completely clear node. - * - */ - void Clear(); - - /** - * @breif Get node as given template type. - * - */ - template - T As() const - { - return impl::StringConverter::Get(AsString()); - } - - /** - * @breif Get node as given template type. - * - */ - template - T As(const T & defaultValue) const - { - return impl::StringConverter::Get(AsString(), defaultValue); - } - - /** - * @breif Get size of node. - * Nodes of type None or Scalar will return 0. - * - */ - size_t Size() const; - - // Sequence operators - - /** - * @breif Insert sequence item at given index. - * Converts node to sequence type if needed. - * Adding new item to end of sequence if index is larger than sequence size. - * - */ - Node & Insert(const size_t index); - - /** - * @breif Add new sequence index to back. - * Converts node to sequence type if needed. - * - */ - Node & PushFront(); - - /** - * @breif Add new sequence index to front. - * Converts node to sequence type if needed. - * - */ - Node & PushBack(); - - /** - * @breif Get sequence/map item. - * Converts node to sequence/map type if needed. - * - * @param index Sequence index. Returns None type Node if index is unknown. - * @param key Map key. Creates a new node if key is unknown. - * - */ - Node & operator [] (const size_t index); - Node & operator [] (const std::string & key); - - /** - * @breif Erase item. - * No action if node is not a sequence or map. - * - */ - void Erase(const size_t index); - void Erase(const std::string & key); - - /** - * @breif Assignment operators. - * - */ - Node & operator = (const Node & node); - Node & operator = (const std::string & value); - Node & operator = (const char * value); - - /** - * @breif Get start iterator. - * - */ - Iterator Begin(); - ConstIterator Begin() const; - - /** - * @breif Get end iterator. - * - */ - Iterator End(); - ConstIterator End() const; - - - private: - - /** - * @breif Get as string. If type is scalar, else empty. - * - */ - const std::string & AsString() const; - - void * m_pImp; ///< Implementation of node class. - - }; - - - /** - * @breif Parsing functions. - * Population given root node with deserialized data. - * - * @param root Root node to populate. - * @param filename Path of input file. - * @param stream Input stream. - * @param string String of input data. - * @param buffer Char array of input data. - * @param size Buffer size. - * - * @throw InternalException An internal error occurred. - * @throw ParsingException Invalid input YAML data. - * @throw OperationException If filename or buffer pointer is invalid. - * - */ - void Parse(Node & root, const char * filename); - void Parse(Node & root, std::iostream & stream); - void Parse(Node & root, const std::string & string); - void Parse(Node & root, const char * buffer, const size_t size); - - - /** - * @breif Serialization configuration structure, - * describing output behavior. - * - */ - struct SerializeConfig - { - - /** - * @breif Constructor. - * - * @param spaceIndentation Number of spaces per indentation. - * @param scalarMaxLength Maximum length of scalars. Serialized as folder scalars if exceeded. - * Ignored if equal to 0. - * @param sequenceMapNewline Put maps on a new line if parent node is a sequence. - * @param mapScalarNewline Put scalars on a new line if parent node is a map. - * - */ - SerializeConfig(const size_t spaceIndentation = 2, - const size_t scalarMaxLength = 64, - const bool sequenceMapNewline = false, - const bool mapScalarNewline = false); - - size_t SpaceIndentation; ///< Number of spaces per indentation. - size_t ScalarMaxLength; ///< Maximum length of scalars. Serialized as folder scalars if exceeded. - bool SequenceMapNewline; ///< Put maps on a new line if parent node is a sequence. - bool MapScalarNewline; ///< Put scalars on a new line if parent node is a map. - }; - - - /** - * @breif Serialization functions. - * - * @param root Root node to serialize. - * @param filename Path of output file. - * @param stream Output stream. - * @param string String of output data. - * @param config Serialization configurations. - * - * @throw InternalException An internal error occurred. - * @throw OperationException If filename or buffer pointer is invalid. - * If config is invalid. - * - */ - void Serialize(const Node & root, const char * filename, const SerializeConfig & config = {2, 64, false, false}); - void Serialize(const Node & root, std::iostream & stream, const SerializeConfig & config = {2, 64, false, false}); - void Serialize(const Node & root, std::string & string, const SerializeConfig & config = {2, 64, false, false}); - -} diff --git a/deps/picoquic/CMakeLists.txt b/deps/picoquic/CMakeLists.txt index 76ad0f9..31acce9 100644 --- a/deps/picoquic/CMakeLists.txt +++ b/deps/picoquic/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.11) +#cmake_minimum_required(VERSION 2.8.11) cmake_policy(SET CMP0003 NEW) cmake_policy(SET CMP0048 NEW) project(picoquic VERSION 0.0.1 LANGUAGES C CXX) diff --git a/deps/picoquic/picotls/CMakeLists.txt b/deps/picoquic/picotls/CMakeLists.txt index b045b1c..a398833 100644 --- a/deps/picoquic/picotls/CMakeLists.txt +++ b/deps/picoquic/picotls/CMakeLists.txt @@ -1,4 +1,4 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 2.8.11) +#CMAKE_MINIMUM_REQUIRED(VERSION 2.8.11) CMAKE_POLICY(SET CMP0003 NEW) cmake_policy(SET CMP0048 NEW) project(picotls VERSION 0.0.1) diff --git a/resource/shaders-src/Color.frag b/resource/shaders/src/Color.frag similarity index 100% rename from resource/shaders-src/Color.frag rename to resource/shaders/src/Color.frag diff --git a/resource/shaders-src/Color.vert b/resource/shaders/src/Color.vert similarity index 100% rename from resource/shaders-src/Color.vert rename to resource/shaders/src/Color.vert diff --git a/resource/shaders-src/Sky.frag b/resource/shaders/src/Sky.frag similarity index 100% rename from resource/shaders-src/Sky.frag rename to resource/shaders/src/Sky.frag diff --git a/resource/shaders-src/Sky.vert b/resource/shaders/src/Sky.vert similarity index 100% rename from resource/shaders-src/Sky.vert rename to resource/shaders/src/Sky.vert diff --git a/resource/shaders-src/Tris.frag b/resource/shaders/src/Tris.frag similarity index 100% rename from resource/shaders-src/Tris.frag rename to resource/shaders/src/Tris.frag diff --git a/resource/shaders-src/Tris.vert b/resource/shaders/src/Tris.vert similarity index 100% rename from resource/shaders-src/Tris.vert rename to resource/shaders/src/Tris.vert diff --git a/resource/shaders-src/Voxel.frag b/resource/shaders/src/Voxel.frag similarity index 100% rename from resource/shaders-src/Voxel.frag rename to resource/shaders/src/Voxel.frag diff --git a/resource/shaders-src/Voxel.geom b/resource/shaders/src/Voxel.geom similarity index 100% rename from resource/shaders-src/Voxel.geom rename to resource/shaders/src/Voxel.geom diff --git a/resource/shaders-src/Voxel.vert b/resource/shaders/src/Voxel.vert similarity index 100% rename from resource/shaders-src/Voxel.vert rename to resource/shaders/src/Voxel.vert diff --git a/resource/shaders-src/compile.sh b/resource/shaders/src/compile.sh similarity index 95% rename from resource/shaders-src/compile.sh rename to resource/shaders/src/compile.sh index 4c62652..9848c3c 100755 --- a/resource/shaders-src/compile.sh +++ b/resource/shaders/src/compile.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash BASEDIR=$(dirname "$0") -TARGETDIR="$BASEDIR/../content/shaders" +TARGETDIR="$BASEDIR/../vk" GLSL=${GLSL:="glslc"} rm $TARGETDIR/*.spv diff --git a/resource/shaders-src/frag.spv b/resource/shaders/src/frag.spv similarity index 100% rename from resource/shaders-src/frag.spv rename to resource/shaders/src/frag.spv diff --git a/resource/content/shaders/Color.fs.spv b/resource/shaders/vk/Color.fs.spv similarity index 100% rename from resource/content/shaders/Color.fs.spv rename to resource/shaders/vk/Color.fs.spv diff --git a/resource/content/shaders/Color.vs.spv b/resource/shaders/vk/Color.vs.spv similarity index 100% rename from resource/content/shaders/Color.vs.spv rename to resource/shaders/vk/Color.vs.spv diff --git a/resource/content/shaders/Sky.fs.spv b/resource/shaders/vk/Sky.fs.spv similarity index 100% rename from resource/content/shaders/Sky.fs.spv rename to resource/shaders/vk/Sky.fs.spv diff --git a/resource/content/shaders/Sky.vs.spv b/resource/shaders/vk/Sky.vs.spv similarity index 100% rename from resource/content/shaders/Sky.vs.spv rename to resource/shaders/vk/Sky.vs.spv diff --git a/resource/content/shaders/Tris.fs.spv b/resource/shaders/vk/Tris.fs.spv similarity index 100% rename from resource/content/shaders/Tris.fs.spv rename to resource/shaders/vk/Tris.fs.spv diff --git a/resource/content/shaders/Tris.vs.spv b/resource/shaders/vk/Tris.vs.spv similarity index 100% rename from resource/content/shaders/Tris.vs.spv rename to resource/shaders/vk/Tris.vs.spv diff --git a/resource/content/shaders/Voxel.fs.spv b/resource/shaders/vk/Voxel.fs.spv similarity index 100% rename from resource/content/shaders/Voxel.fs.spv rename to resource/shaders/vk/Voxel.fs.spv diff --git a/resource/content/shaders/Voxel.geo.fs.spv b/resource/shaders/vk/Voxel.geo.fs.spv similarity index 100% rename from resource/content/shaders/Voxel.geo.fs.spv rename to resource/shaders/vk/Voxel.geo.fs.spv diff --git a/resource/content/shaders/Voxel.geo.gs.spv b/resource/shaders/vk/Voxel.geo.gs.spv similarity index 100% rename from resource/content/shaders/Voxel.geo.gs.spv rename to resource/shaders/vk/Voxel.geo.gs.spv diff --git a/resource/content/shaders/Voxel.geo.ins.fs.spv b/resource/shaders/vk/Voxel.geo.ins.fs.spv similarity index 100% rename from resource/content/shaders/Voxel.geo.ins.fs.spv rename to resource/shaders/vk/Voxel.geo.ins.fs.spv diff --git a/resource/content/shaders/Voxel.geo.ins.gs.spv b/resource/shaders/vk/Voxel.geo.ins.gs.spv similarity index 100% rename from resource/content/shaders/Voxel.geo.ins.gs.spv rename to resource/shaders/vk/Voxel.geo.ins.gs.spv diff --git a/resource/content/shaders/Voxel.geo.ins.vs.spv b/resource/shaders/vk/Voxel.geo.ins.vs.spv similarity index 100% rename from resource/content/shaders/Voxel.geo.ins.vs.spv rename to resource/shaders/vk/Voxel.geo.ins.vs.spv diff --git a/resource/content/shaders/Voxel.geo.vs.spv b/resource/shaders/vk/Voxel.geo.vs.spv similarity index 100% rename from resource/content/shaders/Voxel.geo.vs.spv rename to resource/shaders/vk/Voxel.geo.vs.spv diff --git a/resource/content/shaders/Voxel.ins.fs.spv b/resource/shaders/vk/Voxel.ins.fs.spv similarity index 100% rename from resource/content/shaders/Voxel.ins.fs.spv rename to resource/shaders/vk/Voxel.ins.fs.spv diff --git a/resource/content/shaders/Voxel.ins.vs.spv b/resource/shaders/vk/Voxel.ins.vs.spv similarity index 100% rename from resource/content/shaders/Voxel.ins.vs.spv rename to resource/shaders/vk/Voxel.ins.vs.spv diff --git a/resource/content/shaders/Voxel.vs.spv b/resource/shaders/vk/Voxel.vs.spv similarity index 100% rename from resource/content/shaders/Voxel.vs.spv rename to resource/shaders/vk/Voxel.vs.spv diff --git a/resource/textures-src/1024-realistic/Abstract_006_COLOR.jpg b/resource/textures/1024-realistic/Abstract_006_COLOR.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Abstract_006_COLOR.jpg rename to resource/textures/1024-realistic/Abstract_006_COLOR.jpg diff --git a/resource/textures-src/1024-realistic/Abstract_006_DISP.png b/resource/textures/1024-realistic/Abstract_006_DISP.png similarity index 100% rename from resource/textures-src/1024-realistic/Abstract_006_DISP.png rename to resource/textures/1024-realistic/Abstract_006_DISP.png diff --git a/resource/textures-src/1024-realistic/Abstract_006_HOS.png b/resource/textures/1024-realistic/Abstract_006_HOS.png similarity index 100% rename from resource/textures-src/1024-realistic/Abstract_006_HOS.png rename to resource/textures/1024-realistic/Abstract_006_HOS.png diff --git a/resource/textures-src/1024-realistic/Abstract_006_NORM.jpg b/resource/textures/1024-realistic/Abstract_006_NORM.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Abstract_006_NORM.jpg rename to resource/textures/1024-realistic/Abstract_006_NORM.jpg diff --git a/resource/textures-src/1024-realistic/Abstract_006_OCC.jpg b/resource/textures/1024-realistic/Abstract_006_OCC.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Abstract_006_OCC.jpg rename to resource/textures/1024-realistic/Abstract_006_OCC.jpg diff --git a/resource/textures-src/1024-realistic/Abstract_006_SPEC.jpg b/resource/textures/1024-realistic/Abstract_006_SPEC.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Abstract_006_SPEC.jpg rename to resource/textures/1024-realistic/Abstract_006_SPEC.jpg diff --git a/resource/textures-src/1024-realistic/Bark_005_HOS.png b/resource/textures/1024-realistic/Bark_005_HOS.png similarity index 100% rename from resource/textures-src/1024-realistic/Bark_005_HOS.png rename to resource/textures/1024-realistic/Bark_005_HOS.png diff --git a/resource/textures-src/1024-realistic/Bark_005_baseColor.jpg b/resource/textures/1024-realistic/Bark_005_baseColor.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Bark_005_baseColor.jpg rename to resource/textures/1024-realistic/Bark_005_baseColor.jpg diff --git a/resource/textures-src/1024-realistic/Bark_005_normal.jpg b/resource/textures/1024-realistic/Bark_005_normal.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Bark_005_normal.jpg rename to resource/textures/1024-realistic/Bark_005_normal.jpg diff --git a/resource/textures-src/1024-realistic/Debug.cube.back.png b/resource/textures/1024-realistic/Debug.cube.back.png similarity index 100% rename from resource/textures-src/1024-realistic/Debug.cube.back.png rename to resource/textures/1024-realistic/Debug.cube.back.png diff --git a/resource/textures-src/1024-realistic/Debug.cube.bottom.png b/resource/textures/1024-realistic/Debug.cube.bottom.png similarity index 100% rename from resource/textures-src/1024-realistic/Debug.cube.bottom.png rename to resource/textures/1024-realistic/Debug.cube.bottom.png diff --git a/resource/textures-src/1024-realistic/Debug.cube.front.png b/resource/textures/1024-realistic/Debug.cube.front.png similarity index 100% rename from resource/textures-src/1024-realistic/Debug.cube.front.png rename to resource/textures/1024-realistic/Debug.cube.front.png diff --git a/resource/textures-src/1024-realistic/Debug.cube.left.png b/resource/textures/1024-realistic/Debug.cube.left.png similarity index 100% rename from resource/textures-src/1024-realistic/Debug.cube.left.png rename to resource/textures/1024-realistic/Debug.cube.left.png diff --git a/resource/textures-src/1024-realistic/Debug.cube.right.png b/resource/textures/1024-realistic/Debug.cube.right.png similarity index 100% rename from resource/textures-src/1024-realistic/Debug.cube.right.png rename to resource/textures/1024-realistic/Debug.cube.right.png diff --git a/resource/textures-src/1024-realistic/Debug.cube.top.png b/resource/textures/1024-realistic/Debug.cube.top.png similarity index 100% rename from resource/textures-src/1024-realistic/Debug.cube.top.png rename to resource/textures/1024-realistic/Debug.cube.top.png diff --git a/resource/textures-src/1024-realistic/Debug_COLOR.jpg b/resource/textures/1024-realistic/Debug_COLOR.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Debug_COLOR.jpg rename to resource/textures/1024-realistic/Debug_COLOR.jpg diff --git a/resource/textures-src/1024-realistic/Debug_HOS.png b/resource/textures/1024-realistic/Debug_HOS.png similarity index 100% rename from resource/textures-src/1024-realistic/Debug_HOS.png rename to resource/textures/1024-realistic/Debug_HOS.png diff --git a/resource/textures-src/1024-realistic/Debug_NORM.jpg b/resource/textures/1024-realistic/Debug_NORM.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Debug_NORM.jpg rename to resource/textures/1024-realistic/Debug_NORM.jpg diff --git a/resource/textures-src/1024-realistic/Dirt_003_COLOR.png b/resource/textures/1024-realistic/Dirt_003_COLOR.png similarity index 100% rename from resource/textures-src/1024-realistic/Dirt_003_COLOR.png rename to resource/textures/1024-realistic/Dirt_003_COLOR.png diff --git a/resource/textures-src/1024-realistic/Dirt_003_DISP.png b/resource/textures/1024-realistic/Dirt_003_DISP.png similarity index 100% rename from resource/textures-src/1024-realistic/Dirt_003_DISP.png rename to resource/textures/1024-realistic/Dirt_003_DISP.png diff --git a/resource/textures-src/1024-realistic/Dirt_003_HOS.png b/resource/textures/1024-realistic/Dirt_003_HOS.png similarity index 100% rename from resource/textures-src/1024-realistic/Dirt_003_HOS.png rename to resource/textures/1024-realistic/Dirt_003_HOS.png diff --git a/resource/textures-src/1024-realistic/Dirt_003_NRM.png b/resource/textures/1024-realistic/Dirt_003_NRM.png similarity index 100% rename from resource/textures-src/1024-realistic/Dirt_003_NRM.png rename to resource/textures/1024-realistic/Dirt_003_NRM.png diff --git a/resource/textures-src/1024-realistic/Dirt_003_OCC.png b/resource/textures/1024-realistic/Dirt_003_OCC.png similarity index 100% rename from resource/textures-src/1024-realistic/Dirt_003_OCC.png rename to resource/textures/1024-realistic/Dirt_003_OCC.png diff --git a/resource/textures-src/1024-realistic/Dirt_003_SPEC.png b/resource/textures/1024-realistic/Dirt_003_SPEC.png similarity index 100% rename from resource/textures-src/1024-realistic/Dirt_003_SPEC.png rename to resource/textures/1024-realistic/Dirt_003_SPEC.png diff --git a/resource/textures-src/1024-realistic/Grass_001_COLOR.jpg b/resource/textures/1024-realistic/Grass_001_COLOR.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Grass_001_COLOR.jpg rename to resource/textures/1024-realistic/Grass_001_COLOR.jpg diff --git a/resource/textures-src/1024-realistic/Grass_001_DISP.png b/resource/textures/1024-realistic/Grass_001_DISP.png similarity index 100% rename from resource/textures-src/1024-realistic/Grass_001_DISP.png rename to resource/textures/1024-realistic/Grass_001_DISP.png diff --git a/resource/textures-src/1024-realistic/Grass_001_HOS.jpg b/resource/textures/1024-realistic/Grass_001_HOS.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Grass_001_HOS.jpg rename to resource/textures/1024-realistic/Grass_001_HOS.jpg diff --git a/resource/textures-src/1024-realistic/Grass_001_NORM.jpg b/resource/textures/1024-realistic/Grass_001_NORM.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Grass_001_NORM.jpg rename to resource/textures/1024-realistic/Grass_001_NORM.jpg diff --git a/resource/textures-src/1024-realistic/Grass_001_OCC.jpg b/resource/textures/1024-realistic/Grass_001_OCC.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Grass_001_OCC.jpg rename to resource/textures/1024-realistic/Grass_001_OCC.jpg diff --git a/resource/textures-src/1024-realistic/Grass_001_ROUGH.jpg b/resource/textures/1024-realistic/Grass_001_ROUGH.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Grass_001_ROUGH.jpg rename to resource/textures/1024-realistic/Grass_001_ROUGH.jpg diff --git a/resource/textures-src/1024-realistic/Ground_Forest_002_ambientOcclusion.jpg b/resource/textures/1024-realistic/Ground_Forest_002_ambientOcclusion.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Ground_Forest_002_ambientOcclusion.jpg rename to resource/textures/1024-realistic/Ground_Forest_002_ambientOcclusion.jpg diff --git a/resource/textures-src/1024-realistic/Ground_Forest_002_baseColor.jpg b/resource/textures/1024-realistic/Ground_Forest_002_baseColor.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Ground_Forest_002_baseColor.jpg rename to resource/textures/1024-realistic/Ground_Forest_002_baseColor.jpg diff --git a/resource/textures-src/1024-realistic/Ground_Forest_002_height.png b/resource/textures/1024-realistic/Ground_Forest_002_height.png similarity index 100% rename from resource/textures-src/1024-realistic/Ground_Forest_002_height.png rename to resource/textures/1024-realistic/Ground_Forest_002_height.png diff --git a/resource/textures-src/1024-realistic/Ground_Forest_002_hos.jpg b/resource/textures/1024-realistic/Ground_Forest_002_hos.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Ground_Forest_002_hos.jpg rename to resource/textures/1024-realistic/Ground_Forest_002_hos.jpg diff --git a/resource/textures-src/1024-realistic/Ground_Forest_002_normal.jpg b/resource/textures/1024-realistic/Ground_Forest_002_normal.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Ground_Forest_002_normal.jpg rename to resource/textures/1024-realistic/Ground_Forest_002_normal.jpg diff --git a/resource/textures-src/1024-realistic/Ground_Forest_002_roughness.jpg b/resource/textures/1024-realistic/Ground_Forest_002_roughness.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Ground_Forest_002_roughness.jpg rename to resource/textures/1024-realistic/Ground_Forest_002_roughness.jpg diff --git a/resource/textures-src/1024-realistic/Ground_Forest_003_ROUGH.jpg b/resource/textures/1024-realistic/Ground_Forest_003_ROUGH.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Ground_Forest_003_ROUGH.jpg rename to resource/textures/1024-realistic/Ground_Forest_003_ROUGH.jpg diff --git a/resource/textures-src/1024-realistic/Ground_Forest_003_ambientOcclusion.jpg b/resource/textures/1024-realistic/Ground_Forest_003_ambientOcclusion.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Ground_Forest_003_ambientOcclusion.jpg rename to resource/textures/1024-realistic/Ground_Forest_003_ambientOcclusion.jpg diff --git a/resource/textures-src/1024-realistic/Ground_Forest_003_baseColor.jpg b/resource/textures/1024-realistic/Ground_Forest_003_baseColor.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Ground_Forest_003_baseColor.jpg rename to resource/textures/1024-realistic/Ground_Forest_003_baseColor.jpg diff --git a/resource/textures-src/1024-realistic/Ground_Forest_003_height.png b/resource/textures/1024-realistic/Ground_Forest_003_height.png similarity index 100% rename from resource/textures-src/1024-realistic/Ground_Forest_003_height.png rename to resource/textures/1024-realistic/Ground_Forest_003_height.png diff --git a/resource/textures-src/1024-realistic/Ground_Forest_003_hos.jpg b/resource/textures/1024-realistic/Ground_Forest_003_hos.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Ground_Forest_003_hos.jpg rename to resource/textures/1024-realistic/Ground_Forest_003_hos.jpg diff --git a/resource/textures-src/1024-realistic/Ground_Forest_003_normal.jpg b/resource/textures/1024-realistic/Ground_Forest_003_normal.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Ground_Forest_003_normal.jpg rename to resource/textures/1024-realistic/Ground_Forest_003_normal.jpg diff --git a/resource/textures-src/1024-realistic/Rough_rock_019_COLOR.jpg b/resource/textures/1024-realistic/Rough_rock_019_COLOR.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Rough_rock_019_COLOR.jpg rename to resource/textures/1024-realistic/Rough_rock_019_COLOR.jpg diff --git a/resource/textures-src/1024-realistic/Rough_rock_019_DISP.jpg b/resource/textures/1024-realistic/Rough_rock_019_DISP.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Rough_rock_019_DISP.jpg rename to resource/textures/1024-realistic/Rough_rock_019_DISP.jpg diff --git a/resource/textures-src/1024-realistic/Rough_rock_019_HOS.jpg b/resource/textures/1024-realistic/Rough_rock_019_HOS.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Rough_rock_019_HOS.jpg rename to resource/textures/1024-realistic/Rough_rock_019_HOS.jpg diff --git a/resource/textures-src/1024-realistic/Rough_rock_019_NRM.jpg b/resource/textures/1024-realistic/Rough_rock_019_NRM.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Rough_rock_019_NRM.jpg rename to resource/textures/1024-realistic/Rough_rock_019_NRM.jpg diff --git a/resource/textures-src/1024-realistic/Rough_rock_019_OCC.jpg b/resource/textures/1024-realistic/Rough_rock_019_OCC.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Rough_rock_019_OCC.jpg rename to resource/textures/1024-realistic/Rough_rock_019_OCC.jpg diff --git a/resource/textures-src/1024-realistic/Rough_rock_019_SPEC.jpg b/resource/textures/1024-realistic/Rough_rock_019_SPEC.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Rough_rock_019_SPEC.jpg rename to resource/textures/1024-realistic/Rough_rock_019_SPEC.jpg diff --git a/resource/textures-src/1024-realistic/Sand_005_ambientOcclusion.jpg b/resource/textures/1024-realistic/Sand_005_ambientOcclusion.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Sand_005_ambientOcclusion.jpg rename to resource/textures/1024-realistic/Sand_005_ambientOcclusion.jpg diff --git a/resource/textures-src/1024-realistic/Sand_005_baseColor.jpg b/resource/textures/1024-realistic/Sand_005_baseColor.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Sand_005_baseColor.jpg rename to resource/textures/1024-realistic/Sand_005_baseColor.jpg diff --git a/resource/textures-src/1024-realistic/Sand_005_height.png b/resource/textures/1024-realistic/Sand_005_height.png similarity index 100% rename from resource/textures-src/1024-realistic/Sand_005_height.png rename to resource/textures/1024-realistic/Sand_005_height.png diff --git a/resource/textures-src/1024-realistic/Sand_005_hos.jpg b/resource/textures/1024-realistic/Sand_005_hos.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Sand_005_hos.jpg rename to resource/textures/1024-realistic/Sand_005_hos.jpg diff --git a/resource/textures-src/1024-realistic/Sand_005_normal.jpg b/resource/textures/1024-realistic/Sand_005_normal.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Sand_005_normal.jpg rename to resource/textures/1024-realistic/Sand_005_normal.jpg diff --git a/resource/textures-src/1024-realistic/Sand_005_roughness.jpg b/resource/textures/1024-realistic/Sand_005_roughness.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Sand_005_roughness.jpg rename to resource/textures/1024-realistic/Sand_005_roughness.jpg diff --git a/resource/textures-src/1024-realistic/Seaside_rocks_01_1K_AO.png b/resource/textures/1024-realistic/Seaside_rocks_01_1K_AO.png similarity index 100% rename from resource/textures-src/1024-realistic/Seaside_rocks_01_1K_AO.png rename to resource/textures/1024-realistic/Seaside_rocks_01_1K_AO.png diff --git a/resource/textures-src/1024-realistic/Seaside_rocks_01_1K_Base_Color.png b/resource/textures/1024-realistic/Seaside_rocks_01_1K_Base_Color.png similarity index 100% rename from resource/textures-src/1024-realistic/Seaside_rocks_01_1K_Base_Color.png rename to resource/textures/1024-realistic/Seaside_rocks_01_1K_Base_Color.png diff --git a/resource/textures-src/1024-realistic/Seaside_rocks_01_1K_HOS.png b/resource/textures/1024-realistic/Seaside_rocks_01_1K_HOS.png similarity index 100% rename from resource/textures-src/1024-realistic/Seaside_rocks_01_1K_HOS.png rename to resource/textures/1024-realistic/Seaside_rocks_01_1K_HOS.png diff --git a/resource/textures-src/1024-realistic/Seaside_rocks_01_1K_Height.png b/resource/textures/1024-realistic/Seaside_rocks_01_1K_Height.png similarity index 100% rename from resource/textures-src/1024-realistic/Seaside_rocks_01_1K_Height.png rename to resource/textures/1024-realistic/Seaside_rocks_01_1K_Height.png diff --git a/resource/textures-src/1024-realistic/Seaside_rocks_01_1K_Normal.png b/resource/textures/1024-realistic/Seaside_rocks_01_1K_Normal.png similarity index 100% rename from resource/textures-src/1024-realistic/Seaside_rocks_01_1K_Normal.png rename to resource/textures/1024-realistic/Seaside_rocks_01_1K_Normal.png diff --git a/resource/textures-src/1024-realistic/Seaside_rocks_01_1K_Roughness.png b/resource/textures/1024-realistic/Seaside_rocks_01_1K_Roughness.png similarity index 100% rename from resource/textures-src/1024-realistic/Seaside_rocks_01_1K_Roughness.png rename to resource/textures/1024-realistic/Seaside_rocks_01_1K_Roughness.png diff --git a/resource/textures-src/1024-realistic/Space_orange.cube.back.png b/resource/textures/1024-realistic/Space_orange.cube.back.png similarity index 100% rename from resource/textures-src/1024-realistic/Space_orange.cube.back.png rename to resource/textures/1024-realistic/Space_orange.cube.back.png diff --git a/resource/textures-src/1024-realistic/Space_orange.cube.bottom.png b/resource/textures/1024-realistic/Space_orange.cube.bottom.png similarity index 100% rename from resource/textures-src/1024-realistic/Space_orange.cube.bottom.png rename to resource/textures/1024-realistic/Space_orange.cube.bottom.png diff --git a/resource/textures-src/1024-realistic/Space_orange.cube.front.png b/resource/textures/1024-realistic/Space_orange.cube.front.png similarity index 100% rename from resource/textures-src/1024-realistic/Space_orange.cube.front.png rename to resource/textures/1024-realistic/Space_orange.cube.front.png diff --git a/resource/textures-src/1024-realistic/Space_orange.cube.left.png b/resource/textures/1024-realistic/Space_orange.cube.left.png similarity index 100% rename from resource/textures-src/1024-realistic/Space_orange.cube.left.png rename to resource/textures/1024-realistic/Space_orange.cube.left.png diff --git a/resource/textures-src/1024-realistic/Space_orange.cube.right.png b/resource/textures/1024-realistic/Space_orange.cube.right.png similarity index 100% rename from resource/textures-src/1024-realistic/Space_orange.cube.right.png rename to resource/textures/1024-realistic/Space_orange.cube.right.png diff --git a/resource/textures-src/1024-realistic/Space_orange.cube.top.png b/resource/textures/1024-realistic/Space_orange.cube.top.png similarity index 100% rename from resource/textures-src/1024-realistic/Space_orange.cube.top.png rename to resource/textures/1024-realistic/Space_orange.cube.top.png diff --git a/resource/textures-src/1024-realistic/Space_tray.cube.back.png b/resource/textures/1024-realistic/Space_tray.cube.back.png similarity index 100% rename from resource/textures-src/1024-realistic/Space_tray.cube.back.png rename to resource/textures/1024-realistic/Space_tray.cube.back.png diff --git a/resource/textures-src/1024-realistic/Space_tray.cube.bottom.png b/resource/textures/1024-realistic/Space_tray.cube.bottom.png similarity index 100% rename from resource/textures-src/1024-realistic/Space_tray.cube.bottom.png rename to resource/textures/1024-realistic/Space_tray.cube.bottom.png diff --git a/resource/textures-src/1024-realistic/Space_tray.cube.front.png b/resource/textures/1024-realistic/Space_tray.cube.front.png similarity index 100% rename from resource/textures-src/1024-realistic/Space_tray.cube.front.png rename to resource/textures/1024-realistic/Space_tray.cube.front.png diff --git a/resource/textures-src/1024-realistic/Space_tray.cube.left.png b/resource/textures/1024-realistic/Space_tray.cube.left.png similarity index 100% rename from resource/textures-src/1024-realistic/Space_tray.cube.left.png rename to resource/textures/1024-realistic/Space_tray.cube.left.png diff --git a/resource/textures-src/1024-realistic/Space_tray.cube.right.png b/resource/textures/1024-realistic/Space_tray.cube.right.png similarity index 100% rename from resource/textures-src/1024-realistic/Space_tray.cube.right.png rename to resource/textures/1024-realistic/Space_tray.cube.right.png diff --git a/resource/textures-src/1024-realistic/Space_tray.cube.top.png b/resource/textures/1024-realistic/Space_tray.cube.top.png similarity index 100% rename from resource/textures-src/1024-realistic/Space_tray.cube.top.png rename to resource/textures/1024-realistic/Space_tray.cube.top.png diff --git a/resource/textures-src/1024-realistic/Stone_Path_004_HOS.jpg b/resource/textures/1024-realistic/Stone_Path_004_HOS.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Stone_Path_004_HOS.jpg rename to resource/textures/1024-realistic/Stone_Path_004_HOS.jpg diff --git a/resource/textures-src/1024-realistic/Stone_Path_004_ambientOcclusion.jpg b/resource/textures/1024-realistic/Stone_Path_004_ambientOcclusion.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Stone_Path_004_ambientOcclusion.jpg rename to resource/textures/1024-realistic/Stone_Path_004_ambientOcclusion.jpg diff --git a/resource/textures-src/1024-realistic/Stone_Path_004_baseColor.jpg b/resource/textures/1024-realistic/Stone_Path_004_baseColor.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Stone_Path_004_baseColor.jpg rename to resource/textures/1024-realistic/Stone_Path_004_baseColor.jpg diff --git a/resource/textures-src/1024-realistic/Stone_Path_004_height.png b/resource/textures/1024-realistic/Stone_Path_004_height.png similarity index 100% rename from resource/textures-src/1024-realistic/Stone_Path_004_height.png rename to resource/textures/1024-realistic/Stone_Path_004_height.png diff --git a/resource/textures-src/1024-realistic/Stone_Path_004_normal.jpg b/resource/textures/1024-realistic/Stone_Path_004_normal.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Stone_Path_004_normal.jpg rename to resource/textures/1024-realistic/Stone_Path_004_normal.jpg diff --git a/resource/textures-src/1024-realistic/Stone_Path_004_roughness.jpg b/resource/textures/1024-realistic/Stone_Path_004_roughness.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Stone_Path_004_roughness.jpg rename to resource/textures/1024-realistic/Stone_Path_004_roughness.jpg diff --git a/resource/textures-src/1024-realistic/Stone_Wall_008_COLOR.jpg b/resource/textures/1024-realistic/Stone_Wall_008_COLOR.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Stone_Wall_008_COLOR.jpg rename to resource/textures/1024-realistic/Stone_Wall_008_COLOR.jpg diff --git a/resource/textures-src/1024-realistic/Stone_Wall_008_DISP.png b/resource/textures/1024-realistic/Stone_Wall_008_DISP.png similarity index 100% rename from resource/textures-src/1024-realistic/Stone_Wall_008_DISP.png rename to resource/textures/1024-realistic/Stone_Wall_008_DISP.png diff --git a/resource/textures-src/1024-realistic/Stone_Wall_008_HOS.jpg b/resource/textures/1024-realistic/Stone_Wall_008_HOS.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Stone_Wall_008_HOS.jpg rename to resource/textures/1024-realistic/Stone_Wall_008_HOS.jpg diff --git a/resource/textures-src/1024-realistic/Stone_Wall_008_NORM.jpg b/resource/textures/1024-realistic/Stone_Wall_008_NORM.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Stone_Wall_008_NORM.jpg rename to resource/textures/1024-realistic/Stone_Wall_008_NORM.jpg diff --git a/resource/textures-src/1024-realistic/Stone_Wall_008_OCC.jpg b/resource/textures/1024-realistic/Stone_Wall_008_OCC.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Stone_Wall_008_OCC.jpg rename to resource/textures/1024-realistic/Stone_Wall_008_OCC.jpg diff --git a/resource/textures-src/1024-realistic/Stone_Wall_008_ROUGH.jpg b/resource/textures/1024-realistic/Stone_Wall_008_ROUGH.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Stone_Wall_008_ROUGH.jpg rename to resource/textures/1024-realistic/Stone_Wall_008_ROUGH.jpg diff --git a/resource/textures-src/1024-realistic/Water_002_COLOR.png b/resource/textures/1024-realistic/Water_002_COLOR.png similarity index 100% rename from resource/textures-src/1024-realistic/Water_002_COLOR.png rename to resource/textures/1024-realistic/Water_002_COLOR.png diff --git a/resource/textures-src/1024-realistic/Water_002_DISP.png b/resource/textures/1024-realistic/Water_002_DISP.png similarity index 100% rename from resource/textures-src/1024-realistic/Water_002_DISP.png rename to resource/textures/1024-realistic/Water_002_DISP.png diff --git a/resource/textures-src/1024-realistic/Water_002_HOS.jpg b/resource/textures/1024-realistic/Water_002_HOS.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Water_002_HOS.jpg rename to resource/textures/1024-realistic/Water_002_HOS.jpg diff --git a/resource/textures-src/1024-realistic/Water_002_NORM.jpg b/resource/textures/1024-realistic/Water_002_NORM.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Water_002_NORM.jpg rename to resource/textures/1024-realistic/Water_002_NORM.jpg diff --git a/resource/textures-src/1024-realistic/Water_002_OCC.jpg b/resource/textures/1024-realistic/Water_002_OCC.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Water_002_OCC.jpg rename to resource/textures/1024-realistic/Water_002_OCC.jpg diff --git a/resource/textures-src/1024-realistic/Water_002_ROUGH.jpg b/resource/textures/1024-realistic/Water_002_ROUGH.jpg similarity index 100% rename from resource/textures-src/1024-realistic/Water_002_ROUGH.jpg rename to resource/textures/1024-realistic/Water_002_ROUGH.jpg diff --git a/resource/textures-src/1024-realistic/index.txt b/resource/textures/1024-realistic/index.txt similarity index 100% rename from resource/textures-src/1024-realistic/index.txt rename to resource/textures/1024-realistic/index.txt diff --git a/resource/textures-src/Aim.png b/resource/textures/Aim.png similarity index 100% rename from resource/textures-src/Aim.png rename to resource/textures/Aim.png diff --git a/resource/textures-src/compile.sh b/resource/textures/compile.sh similarity index 100% rename from resource/textures-src/compile.sh rename to resource/textures/compile.sh diff --git a/resource/textures-src/merge.py b/resource/textures/merge.py similarity index 100% rename from resource/textures-src/merge.py rename to resource/textures/merge.py diff --git a/src/client.cpp b/src/client.cpp index 4083cdf..1976999 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1,16 +1,16 @@ /** * \file client.cpp * \brief Univerxel client - * \author Maelys Bois - * \version 0.0.1 + * \author Shu + * \version 0.0.2 * - * Univerxel standalone client program. + * Univerxel light client program. */ -#define STANDALONE 1 #include "client/Client.hpp" -#include "core/standalone_config.hpp" +#include "core/options_part.hpp" #include "core/utils/tracy.hpp" +#include "core/utils/logger.hpp" #include "version.h" /// Entry point @@ -21,17 +21,20 @@ int main(int argc, char *argv[]){ LOG("Profiling !"); #endif - auto options = config::standalone_options(argc > 1 ? argv[1] : config::DEFAULT_FILE); + auto options = config::options_part(argc > 1 ? argv[1] : config::DEFAULT_FILE); options.save(); #if TRACY_ENABLE tracy::SetThreadName("Main"); #endif - auto client = Client(options.get()); - client.run(nullptr); + world::module::Registry::Load(); + + auto client = Client(options.get(), nullptr); + client.run(); options.save(); + world::module::Registry::Unload(); return 0; } \ No newline at end of file diff --git a/src/client/Client.cpp b/src/client/Client.cpp index 908d5ee..4447d86 100644 --- a/src/client/Client.cpp +++ b/src/client/Client.cpp @@ -3,221 +3,372 @@ #include "render/index.hpp" #include "render/UI.hpp" -#include "InputMap.hpp" +#include "control/InputMap.hpp" #include "world/index.hpp" +#include "core/world/Elements.hpp" #include -#include "../core/data/math.hpp" +#include "core/geometry/math.hpp" #include -Client::Client(config::client::options& options): options(options) { } -Client::~Client() { } +#undef LOG_PREFIX +#define LOG_PREFIX "Client: " -void Client::run(server_handle* const localHandle) { +Client::Client(config::client::options& options, world::AbstractServerFactory* srvContainer): + options(options) { state.srvContainer = srvContainer; } +Client::~Client() { + LOG_W("Stopped"); +} + +void Client::run() { if (!render::Load(window, options.preferVulkan, options.renderer, options.window)) return; - InputMap inputs(window.getPtr()); - Controllable player(window.getPtr(), inputs, options.control); - Camera camera(&player, options.camera); - + InputMap inputs(window.getPtr(), options.keys); auto pipeline = render::Renderer::Get(); pipeline->LightInvDir = glm::normalize(glm::vec3(-.5f, 2, -2)); render::Renderer::Get()->loadUI(window); - auto world = world::client::Load(options.connection, localHandle, options.world, options.contouring); - state.contouring = world->getContouring(); - world->onTeleport = [&](voxel_pos pos) { - state.position = player.position = pos; - }; - world->onMessage = [&](const std::string &text) { - // MAYBE: rolling buffer - const auto yellow = std::make_optional(0xFF43F5F5); - state.console.lines.push_back(state::state::line{text, text[0] == '>' ? yellow : std::nullopt}); - }; - - do { - window.startFrame(); - FrameMark; - { // Update - ZoneScopedN("Update"); - static double lastTime = window.getTime(); - const double partTime = window.getTime(); - const float deltaTime = partTime - lastTime; - inputs.toggle(state.capture_mouse, Input::Mouse); - inputs.toggle(options.debugMenu.bar, Input::Debug); - - player.capture(state.capture_mouse, !render::UI::IsFocus(), deltaTime); - if(player.velocity != glm::vec3(0) && ( - !options.control.collide || - !world->collide(player.position, player.velocity, options.voxel_density, options.voxel_density)) - ) { - player.position += player.velocity; - const auto pos = player.position.as_voxel(options.voxel_density); - if(pos != state.position.as_voxel(options.voxel_density)) { - world->emit(world::action::Move(pos)); - } - state.position = pos; - } - camera.update(); - pipeline->lookFrom(camera); - pipeline->LightInvDir = glm::vec3(glm::rotate(glm::mat4(1), deltaTime * .1f, glm::vec3(1, .5, .1)) * glm::vec4(pipeline->LightInvDir, 0)); - - { - const auto ray_result = world->raycast(camera.getRay() * options.voxel_density); - if(auto target = std::get_if(&ray_result)) { - state.look_at = *target; - const auto &tool = options.editor.tool; - state.can_fill = world->isAreaFree(target->pos, world::action::ToGeometry(tool.shape), tool.radius); - } else { - state.look_at = {}; - } - } - if (state.capture_mouse) { - if (state.look_at.has_value()) { - ZoneScopedN("Edit"); - const auto &tool = options.editor.tool; - if (inputs.isPressing(Mouse::Left)) - world->emit(world::action::FillShape( - state.look_at.value().pos, world::Voxel(world::materials::AIR, tool.emptyAir * world::Voxel::DENSITY_MAX), tool.shape, tool.radius)); - else if (const auto voxel = world::Voxel(tool.material, world::Voxel::DENSITY_MAX); - inputs.isPressing(Mouse::Right) && (state.can_fill || !voxel.is_solid())) - world->emit(world::action::FillShape( - state.look_at.value().pos, voxel, tool.shape, tool.radius)); - } - if (inputs.isDown(Input::Throw)) { - //FIXME: register entity type world->addEntity(entity_id(0), {state.position * options.voxel_density, glm::vec3(10, 0, 0)}); - } - } - world->update(state.position.as_voxel(options.voxel_density), deltaTime); - inputs.saveKeys(); - lastTime = partTime; + const auto applyOptions = [&] (const render::UI::Actions& actions) { + if (actions && render::UI::Actions::FPS) { + window.setTargetFPS(options.window.targetFPS); + pipeline->setVSync(options.window.targetFPS < Window::MIN_FPS); } + if (actions && render::UI::Actions::FullScreen) { + window.setFullscreen(options.window.fullscreen); + } + if(actions && render::UI::Actions::ClearColor) { + pipeline->setClearColor(options.renderer.clear_color); + } + if(actions && render::UI::Actions::RendererSharders) { + pipeline->reloadShaders(options.renderer.voxel); + } + if(actions && render::UI::Actions::RendererTextures) { + pipeline->reloadTextures(options.renderer.textures, options.renderer.getMipmapLodBias(), options.renderer.getAnisotropy()); + } + if(actions && render::UI::Actions::FillMode) { + pipeline->setFillMode(options.renderer.wireframe); + } + }; + const auto play = [&] { + auto *localHandle = state.srvContainer && (!options.connection.has_value() || state.useSrv) ? state.srvContainer->run() : nullptr; + + Controllable player(window.getPtr(), inputs, options.control); + Camera camera(&state.position.absolute.position, &player, options.camera); + + auto world = world::client::Load(state.login, options.connection, localHandle, options.world, options.contouring); + state.contouring = world->getContouring(); + world->onTeleport = [&](const world::relative_transform &rel, const world::transform &abs) { + state.position.relative = rel; + state.position.absolute = abs; + }; + world->onMessage = [&](const std::string &text) { + const auto yellow = std::make_optional(0xFF43F5F5); + state.console.lines.push_back(state::state::line{text, text[0] == '>' ? yellow : std::nullopt}); + }; + + do { - ZoneScopedN("UI"); - const auto actions = render::UI::Get()->draw(options, state, reports); - if (actions && render::UI::Actions::FPS) { - window.setTargetFPS(options.window.targetFPS); - pipeline->setVSync(options.window.targetFPS < Window::MIN_FPS); - } - if (actions && render::UI::Actions::FullScreen) { - window.setFullscreen(options.window.fullscreen); - } - if(actions && render::UI::Actions::ClearColor) { - pipeline->setClearColor(options.renderer.clear_color); - } - if(actions && render::UI::Actions::RendererSharders) { - pipeline->reloadShaders(options.renderer.voxel); - } - if(actions && render::UI::Actions::RendererTextures) { - pipeline->reloadTextures(options.renderer.textures, options.renderer.getMipmapLodBias(), options.renderer.getAnisotropy()); - } - if(actions && render::UI::Actions::World) { - //FIXME: server options world->setOptions(options.world); - } - if(actions && render::UI::Actions::Camera) { - camera.setOptions(options.camera); - } - if(actions && render::UI::Actions::Control) { - player.setOptions(options.control); - } - if(actions && render::UI::Actions::FillMode) { - pipeline->setFillMode(options.renderer.wireframe); - } - if(actions && render::UI::Actions::Message) { - char *s = state.console.buffer.data(); - //Strtrim(s); - //if (s[0]) - // ExecCommand(s); - world->emit(world::action::Message(s)); - strcpy(s, ""); - } - } - { // Rendering - ZoneScopedN("Render"); - pipeline->beginFrame(); + window.startFrame(); + FrameMark; + { // Update + ZoneScopedN("Update"); + static double lastTime = window.getTime(); + const double partTime = window.getTime(); + const float deltaTime = partTime - lastTime; + inputs.toggle(state.capture_mouse, Input::Mouse); + inputs.toggle(options.debugMenu.bar, Input::Debug); - reports.models_count = 0; - reports.tris_count = 0; - std::optional frustum; - if(options.culling <= 0) { - frustum = {camera.getFrustum()}; - } - const auto offset = state.position.raw_as_long(); - std::vector occlusion; - if (options.culling > 0) { - const auto ratio = options.culling * 2; - occlusion.reserve(glm::pow2(ratio * 2 - 1)); - const auto [ch, cv] = player.getAngles(); - const auto max_v = tan(options.camera.fov / 2.), max_h = Window::RATIO * max_v; - for(int iv = -ratio + 1; iv < ratio; iv++) { - const auto v = cv + max_v * iv / ratio; - for(int ih = -ratio + 1; ih < ratio; ih++) { - const auto h = ch + max_h * ih / ratio; - occlusion.emplace_back(cos(v) * sin(h), sin(v), cos(v) * cos(h)); + player.capture(state.capture_mouse, !render::UI::IsFocus(), deltaTime); + if(player.velocity != glm::vec3(0)) + { + auto &position = state.position.relative.relative.position; + const world::cell_pos old = position; + if (options.control.collide) + { + const auto rp = world->tryMovePlayer(state.position.relative, player.velocity); + state.position.relative.parent = rp.parent; + position = rp.relative.position; + } + else + { + position += player.velocity; + } + state.position.absolute = world->getAbsolute(state.position.relative); + + //TODO: throttle + if(old != (world::cell_pos)position) + { + world->emit(world::action::Move(world::relative_pos{state.position.relative.parent, position})); } } - } - { // Solid - const auto pass = pipeline->beginWorldPass(true); - const auto draw = [&](glm::mat4 model, render::LodModel *const buffer, const contouring::Abstract::area_info &area, const voxel_pos &pos) { - reports.models_count++; - reports.tris_count += pass(buffer, model, glm::vec4(pos, std::get<1>(area)), std::get<2>(area)); - }; - if (options.culling > 0) { - state.contouring->getModels(draw, player.position, options.camera.far_dist, occlusion, offset, options.voxel_density, true); - } else { - state.contouring->getModels(draw, frustum, offset, options.voxel_density, true); + camera.update(); + pipeline->lookFrom(camera); + // FIXME: Get from world + pipeline->LightInvDir = glm::vec3(glm::rotate(glm::mat4(1), deltaTime * .1f, glm::vec3(1, .5, .1)) * glm::vec4(pipeline->LightInvDir, 0)); + + { + state.look_at = world->raycast(camera.getRay()); + state.can_fill = [&] { + if(const auto target = std::get_if(&state.look_at)) { + if (world::Elements::Is(target->pos.node)) { + const auto &tool = options.editor.tool; + return world->isRangeFree(target->pos, world::action::Volume{tool.shape, (uint8_t)tool.radius}); + } + } + return false; + }(); } - } - { // Entities - const auto pass = pipeline->beginEntityPass(); - const auto draw = [&](size_t idx, const std::vector &models) { - if(const auto buffer = state.contouring->getEntityModel(idx)) { - reports.models_count += models.size(); - reports.tris_count += pass(buffer, models); + if (state.capture_mouse) + { + if (const auto target = std::get_if(&state.look_at)) + { + ZoneScopedN("Edit"); + const auto &tool = options.editor.tool; + constexpr world::Voxel::material_t AIR = 0; + if (inputs.isPressing(Mouse::Left)) + world->emit(world::action::FillShape( + target->pos, world::Voxel(AIR, tool.emptyAir * world::Voxel::DENSITY_MAX), tool.shape, tool.radius)); + else if (const auto voxel = world::Voxel(tool.material, world::Voxel::DENSITY_MAX); + inputs.isPressing(Mouse::Right) && (state.can_fill || !voxel.is_solid())) + world->emit(world::action::FillShape( + target->pos, voxel, tool.shape, tool.radius)); + } //TODO: else interact + if (inputs.isDown(Input::Throw)) + { + //FIXME: register entity type world->addEntity(entity_id(0), {state.position * options.voxel_density, glm::vec3(10, 0, 0)}); } - }; - world->getEntitiesModels(draw, frustum, offset, options.voxel_density); + } + world->update(state.position.absolute.position, deltaTime); + inputs.saveKeys(); + lastTime = partTime; } + { - const auto pass = pipeline->beginIndicatorPass(); - if(state.look_at.has_value()) { // Indicator - const auto model = glm::scale(glm::translate(glm::scale(glm::mat4(1), 1.f / glm::vec3(options.voxel_density)), glm::vec3(state.look_at.value().pos.second + state.look_at.value().offset - offset * glm::llvec3(options.voxel_density)) - glm::vec3(.5 + options.editor.tool.radius)), glm::vec3(1 + options.editor.tool.radius * 2)); - const auto color = state.can_fill ? glm::vec4(1, 1, 1, .5) : (world::materials::solidity[options.editor.tool.material] ? glm::vec4(1, 0, 0, .5) : glm::vec4(1, .5, 0, .5)); - reports.models_count++; - reports.tris_count += pass(model, options.editor.tool.shape, color); + ZoneScopedN("UI"); + const auto actions = render::UI::Get()->draw(options, state, reports); + applyOptions(actions); + if(actions && render::UI::Actions::World) + { + //FIXME: server options world->setOptions(options.world); + } + if(actions && render::UI::Actions::Camera) + { + camera.setOptions(options.camera); + } + if(actions && render::UI::Actions::Control) + { + player.setOptions(options.control); + } + if(actions && render::UI::Actions::Message) + { + char *s = state.console.buffer.data(); + //Strtrim(s); + //if (s[0]) + // ExecCommand(s); + world->emit(world::action::Message(s)); + strcpy(s, ""); + } + if (inputs.isDown(Input::Quit)) + { + state.playing = false; } } + renderFrame(*pipeline, options.culling >= 0 ? std::make_optional(camera.getFrustum()) : std::nullopt, + options.culling > 0 ? std::make_optional(player.getAngles()): std::nullopt, *world); + + { // Swap buffers + ZoneScopedN("Swap"); + pipeline->swapBuffer(window); + inputs.poll(); + } + + window.waitTargetFPS(); + } while (!window.shouldClose() && world->isRunning() && state.playing); + + player.capture(false, false, 0); // Restore mouse + options.contouring = state.contouring->getOptions(); + state.contouring = nullptr; + world.reset(); + }; + + Controllable player(window.getPtr(), inputs, options.control); + Camera camera(&state.position.absolute.position, &player, options.camera); + do { // Main menu + window.startFrame(); + inputs.saveKeys(); + player.capture(true, false, 0, true); + camera.update(); + pipeline->lookFrom(camera); + const auto actions = render::UI::Get()->draw(options, state, reports); + applyOptions(actions); + if (actions && render::UI::Actions::Quit) { + window.close(); + } + if (state.playing) { + play(); + state.playing = false; + } + pipeline->beginFrame(); + { //MAYBE: menu pipeline + pipeline->beginTerrainPass(true); + pipeline->beginUniquePass(); + pipeline->beginInstancedPass(); + pipeline->beginIndicatorPass(); if (options.renderer.voxel.transparency) { - const auto pass = pipeline->beginWorldPass(false); - const auto draw = [&](glm::mat4 model, render::LodModel *const buffer, const contouring::Abstract::area_info &area, const voxel_pos &pos) { - reports.models_count++; - reports.tris_count += pass(buffer, model, glm::vec4(pos, std::get<1>(area)), std::get<2>(area)); - }; - if (options.culling > 0) { - state.contouring->getModels(draw, player.position, options.camera.far_dist, occlusion, offset, options.voxel_density, false); - } else { - state.contouring->getModels(draw, frustum, offset, options.voxel_density, false); - } + pipeline->beginTerrainPass(false); } pipeline->postProcess(); - render::UI::Get()->render(); - pipeline->endFrame(); } - - { // Swap buffers - ZoneScopedN("Swap"); - pipeline->swapBuffer(window); - inputs.poll(); - } - + render::UI::Get()->render(); + pipeline->endFrame(); + pipeline->swapBuffer(window); + inputs.poll(); window.waitTargetFPS(); - } while (!(inputs.isDown(Input::Quit) || window.shouldClose() || !world->isRunning())); + } while (!window.shouldClose()); - options.contouring = state.contouring->getOptions(); - world.reset(); + inputs.exportKeys(options.keys); render::Renderer::Unload(); window.destroy(); +} + +void Client::renderFrame(render::Renderer& pipeline, const std::optional& frustum, const std::optional>& angles, const world::client::Universe& world) { + ZoneScopedN("Render"); + pipeline.beginFrame(); + + reports.models_count = 0; + reports.tris_count = 0; + const auto cull = [&]() -> culler { + if (frustum.has_value()) + return {frustum.value()}; + else if (angles.has_value()) { + std::vector occlusion; + const auto ratio = options.culling * 2; + occlusion.reserve(glm::pow2(ratio * 2 - 1)); + const auto [ch, cv] = angles.value(); + const auto max_v = tan(options.camera.fov / 2.), max_h = Window::RATIO * max_v; + for(int iv = -ratio + 1; iv < ratio; iv++) { + const auto v = cv + max_v * iv / ratio; + for(int ih = -ratio + 1; ih < ratio; ih++) { + const auto h = ch + max_h * ih / ratio; + occlusion.emplace_back(cos(v) * sin(h), sin(v), cos(v) * cos(h)); + } + } + return {occlusion}; + } else + return {}; + }(); + const auto offset = Camera::Split(state.position.absolute.position).first; + + renderTerrainPass(pipeline, cull, offset, true); // Solid areas + { + const auto self = world.getPlayerId(); + const auto elements = world.getElements(); + { // Unique elements (parts) + const auto pass = pipeline.beginUniquePass(); + const auto draw = [&](const glm::mat4& model, render::Model *const buffer) { + reports.models_count++; + reports.tris_count += pass(buffer, model); + }; + state.contouring->getUniqueModels(draw, *elements, frustum, offset); + } + { // Instanced elements (instances) + const auto pass = pipeline.beginInstancedPass(); + const auto draw = [&](const std::vector &models, render::Model *const buffer) { + reports.models_count += models.size(); + reports.tris_count += pass(buffer, models); + }; + state.contouring->getInstancedModels(draw, *elements, frustum, offset, self); + } + } + { // Indicators + const auto pass = pipeline.beginIndicatorPass(); + constexpr float ALPHA = .5; + if(const auto target = std::get_if(&state.look_at)) { + const auto tf = world.getAbsolute(world::relative_transform{target->pos.node, world::transform(target->pos.voxel)}); + const auto box = faabb::FromCenter(glm::vec3(0), glm::vec3(options.editor.tool.radius)); + const auto obbMat = glm::scale(glm::translate(glm::translate(glm::mat4(1), + glm::vec3(tf.position - (world::world_pos)offset)) * + glm::toMat4(tf.rotation), box.min), box.getSize()); + const auto color = [&] { + if (world::Elements::Is(target->pos.node)) { + if (state.can_fill) + return glm::vec4(1, 1, 1, ALPHA); + else if (world::Voxel::IsSolid(options.editor.tool.material)) + return glm::vec4(1, 0, 0, ALPHA); + else + return glm::vec4(1, .5, 0, ALPHA); + + } + return glm::vec4(.2, .2, 1, ALPHA); + } (); + reports.models_count++; + reports.tris_count += pass(obbMat, options.editor.tool.shape, color); + } + // TODO: handle ray_element_target + // MAYBE: hover entity + if (state.indicators.aabb || state.indicators.obb) { + const auto elements = world.getElements(); + elements->nodes.iter([&](const world::node_id&, const world::Node& node) { + if (!node.content) + return; + + const auto& tf = node.absolute; + const auto box = faabb::From(node.content->getBoundingBox(*elements)); + if (state.indicators.obb) { + reports.models_count++; + const auto obbMat = glm::scale(glm::translate(glm::translate(glm::mat4(1), + glm::vec3(tf.position - (world::world_pos)offset)) * + glm::toMat4(tf.rotation), box.min), box.getSize()); + reports.tris_count += pass(obbMat, world::action::Shape::Cube, glm::vec4(1, 1, 1, ALPHA)); + } + if (state.indicators.aabb) { + reports.models_count++; + const auto col = box.rotate(tf.rotation); + const auto aabbMat = glm::scale(glm::translate(glm::mat4(1), + glm::vec3(tf.position - (world::world_pos)offset) + col.min), col.getSize()); + reports.tris_count += pass(aabbMat, world::action::Shape::Cube, glm::vec4(ALPHA)); + } + }); + } + if (state.indicators.chunk) { + const auto elements = world.getElements(); + for (const auto& ref: elements->areas) { + const auto id = elements->withFlag(ref); + const auto node = elements->findArea(id.val); + if (state.indicators.chunk) { + for (const auto& ck: node->get()->getChunks()) { + const auto pos = node->absolute.computeChild(glm::multiply(ck.first)); + if (glm::length2(glm::vec3(pos - state.position.absolute.position)) > glm::pow2(world::CHUNK_LENGTH * 2)) + continue; + + reports.models_count++; + const auto obbMat = glm::scale(glm::translate(glm::mat4(1), glm::vec3(pos - (world::world_pos)offset)) * + glm::toMat4(node->absolute.rotation), glm::vec3(world::CHUNK_LENGTH)); + reports.tris_count += pass(obbMat, world::action::Shape::Cube, glm::vec4(1, .5, 0, ALPHA)); + } + } + } + } + } + if (options.renderer.voxel.transparency) { + renderTerrainPass(pipeline, cull, offset, false); // Transparent areas + } + pipeline.postProcess(); + render::UI::Get()->render(); + pipeline.endFrame(); +} +void Client::renderTerrainPass(render::Renderer& pipeline, const culler& culler, const world::cell_pos& offset, bool solid) { + const auto pass = pipeline.beginTerrainPass(solid); + const auto draw = [&](glm::mat4 model, render::LodModel *const buffer, const contouring::Abstract::area_info &area, const world::voxel_pos &area_offset) { + reports.models_count++; + reports.tris_count += pass(buffer, model, /*big precision lost*/glm::vec4(area_offset, area.radius), area.curvature); + }; + if (const auto occlusion = std::get_if>(&culler)) { + state.contouring->getTerrainModels(draw, state.position.absolute.position, options.camera.far_dist, *occlusion, offset, solid); + } else { + const auto frustum = std::get_if(&culler); + state.contouring->getTerrainModels(draw, frustum ? std::make_optional(*frustum) : std::nullopt, offset, solid); + } } \ No newline at end of file diff --git a/src/client/Client.hpp b/src/client/Client.hpp index 922a9f1..a7387e7 100644 --- a/src/client/Client.hpp +++ b/src/client/Client.hpp @@ -1,8 +1,7 @@ #pragma once #include "state.hpp" -#include "Window.hpp" -#include "../core/server_handle.hpp" +#include "render/Window.hpp" namespace render { class Renderer; @@ -11,14 +10,17 @@ namespace render { /// Client view class Client { public: - Client(config::client::options &options); + Client(config::client::options &options, world::AbstractServerFactory* srvContainer = nullptr); ~Client(); - void run(server_handle *const); - -protected: + void run(); private: + using culler = std::variant>; + + void renderFrame(render::Renderer&, const std::optional&, const std::optional>&, const world::client::Universe&); + void renderTerrainPass(render::Renderer&, const culler&, const world::cell_pos& offset, bool solid); + config::client::options &options; state::state state; state::reports reports; diff --git a/src/client/InputMap.cpp b/src/client/InputMap.cpp deleted file mode 100644 index 8c398f8..0000000 --- a/src/client/InputMap.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "InputMap.hpp" - -InputMap::InputMap(GLFWwindow* window): window(window) { } -InputMap::~InputMap() { } - -void InputMap::saveKeys() { - for (auto input : Toggles) { - Previous.insert_or_assign(input, isDown(input)); - } - PreviousLeft = isDown(Mouse::Left); - PreviousRight = isDown(Mouse::Right); -} - -bool InputMap::isDown(Input input) const { - return glfwGetKey(window, Map.at(input)) == GLFW_PRESS; -} -bool InputMap::wasDown(Input input) const { - return Previous.at(input); -} -bool InputMap::isPressing(Input input) const { - return isDown(input) && !wasDown(input); -} -bool InputMap::isReleasing(Input input) const { - return !isDown(input) && wasDown(input); -} - -void InputMap::toggle(bool &val, Input input) const { - if(isPressing(input)) - val = !val; -} - -bool InputMap::isDown(Mouse input) const { - return glfwGetMouseButton(window, static_cast(input)) == GLFW_PRESS; -} -bool InputMap::wasDown(Mouse input) const { - switch (input) { - case Mouse::Left: - return PreviousLeft; - - case Mouse::Right: - return PreviousRight; - - default: - return false; - } -} -bool InputMap::isPressing(Mouse input) const { - return isDown(input) && !wasDown(input); -} -bool InputMap::isReleasing(Mouse input) const { - return !isDown(input) && wasDown(input); -} - -void InputMap::poll() const { glfwPollEvents(); } \ No newline at end of file diff --git a/src/client/config.hpp b/src/client/config.hpp index 78b15a4..31d4332 100644 --- a/src/client/config.hpp +++ b/src/client/config.hpp @@ -1,14 +1,14 @@ #pragma once -#include +#include "core/utils/toml.hpp" #include #include #include #include "world/Universe.hpp" #include "render/Renderer.hpp" +#include "render/Window.hpp" #include "contouring/Abstract.hpp" #include "control/Camera.hpp" -#include "Window.hpp" namespace config::client { @@ -28,7 +28,7 @@ inline std::string toHexa(const glm::vec4& rgb) { struct options { public: options(toml::node_view config) { - assert(config["enabled"]); + assert(config["enabled"] && "probably a broken config file"); window.targetFPS = config["window"]["target_fps"].value_or(window.targetFPS); window.sampling = config["window"]["sampling"].value_or(window.sampling); @@ -70,9 +70,8 @@ public: world.editHandling = config["world"]["edit_handling"].value_or(world.editHandling); world.movePrediction = config["world"]["move_prediction"].value_or(world.movePrediction); world.moveCollision = config["world"]["move_collision"].value_or(world.moveCollision); - voxel_density = config["world"]["voxel_density"].value_or(voxel_density); -#ifndef STANDALONE +#ifndef LIGHT_CLIENT const auto useLocal = config["connection"]["use_local"].value_or(true); #else const auto useLocal = false; @@ -103,6 +102,12 @@ public: overlay.visible = config["overlay"]["visible"].value_or(overlay.visible); overlay.corner = config["overlay"]["corner"].value_or(overlay.corner); + + if (config["keys"].is_table()) { + for(auto entry: *config["keys"].as_table()) { + keys.emplace_back(std::stoi(entry.first), entry.second.value_or(-1)); + } + } } toml::table save() { @@ -151,13 +156,12 @@ public: {"edit_prediction", world.editPrediction}, {"edit_handling", world.editHandling}, {"move_prediction", world.movePrediction}, - {"move_collision", world.moveCollision}, - {"voxel_density", voxel_density} + {"move_collision", world.moveCollision} })); if(connection.has_value()) { const auto &ct = connection.value(); config.insert_or_assign("connection", toml::table({ -#ifndef STANDALONE +#ifndef LIGHT_CLIENT {"use_local", false}, #endif {"host", ct.host}, @@ -192,11 +196,14 @@ public: {"visible", overlay.visible}, {"corner", overlay.corner} })); + toml::table key_table; + for (const auto& key: keys) + key_table.insert_or_assign(std::to_string(key.first), key.second); + config.insert_or_assign("keys", key_table); return config; } world::client::Universe::options world; - int voxel_density = 1; struct { bool bar = false; @@ -239,5 +246,7 @@ public: } overlay; std::optional connection = {}; + + std::vector> keys; }; } \ No newline at end of file diff --git a/src/client/contouring/Abstract.hpp b/src/client/contouring/Abstract.hpp index d6051c3..f5984f4 100644 --- a/src/client/contouring/Abstract.hpp +++ b/src/client/contouring/Abstract.hpp @@ -1,10 +1,10 @@ #pragma once -#include "../../core/world/forward.h" -#include "../../core/geometry/Frustum.hpp" -#include "../../core/geometry/Ray.hpp" -#include "../../core/geometry/Faces.hpp" -#include "../../core/flags.hpp" +#include "core/world/Area.hpp" +#include "core/geometry/Frustum.hpp" +#include "core/geometry/Ray.hpp" +#include "core/geometry/Faces.hpp" +#include "core/utils/flags.hpp" #include namespace render { @@ -21,22 +21,27 @@ namespace contouring { /// Each frame ping. /// Mostly used for cleanup and to flush buffers data using main thread - virtual void update(const voxel_pos &pos, const world::client::area_map &areas) = 0; + virtual void update(const world::cell_pos &pos, const world::Elements &) = 0; /// Chunk data change /// @param offset priority position offset - virtual void onUpdate(const area_ &pos, const chunk_pos &offset, const world::ChunkContainer &data, geometry::Faces neighbors) = 0; + virtual void onUpdate(const world::area_chunk_pos &pos, glm::ll offset, const world::ChunkContainer &data, geometry::Faces neighbors) = 0; /// Chunk existante ping /// @note notify for chunks entering view while moving - virtual void onNotify(const area_ &pos, const chunk_pos &offset, const world::ChunkContainer &data) = 0; + /// @param offset priority position offset + virtual void onNotify(const world::area_chunk_pos &pos, glm::ll offset, const world::ChunkContainer &data) = 0; + + /// Part chunk data change + /// @param offset priority position offset + virtual void onNotify(const world::part_id&, glm::ll offset, const glm::ucvec3& size, const std::vector>&, bool update) = 0; + + /// Register model + virtual void onLoad(const world::model_id&, std::istream&) = 0; + /// Free model + virtual void onUnload(const world::model_id&) = 0; + /// Display ImGui config virtual void onGui() = 0; - /// Register entity from model - virtual void onEntityLoad(size_t id, std::istream&) = 0; - /// Register entity from area - virtual void onEntityLoad(size_t id, const glm::ucvec3& size, const std::vector>&) = 0; - /// Free entity model - virtual void onEntityUnload(size_t id) = 0; /// Get options virtual std::string getOptions() const = 0; /// Get camera recommended far_dist range @@ -45,28 +50,32 @@ namespace contouring { /// Get pending elements virtual size_t getQueueSize() = 0; - using area_info = std::tuple; - using draw_call = const std::function &; + struct area_info { + world::transform absolute; + world::cell_pos::value_type radius; + float curvature; + }; + using terrain_draw_call = const std::function &; + using unique_draw_call = const std::function &; + using instanced_draw_call = const std::function&, render::Model *const)> &; /// Get buffers in frustum with model matrices /// @note buffers invalidated after update - virtual void getModels(draw_call draw, const std::optional& frustum, const glm::llvec3& offset, int density, bool solid) = 0; + virtual void getTerrainModels(terrain_draw_call draw, const std::optional& frustum, const world::cell_pos& offset, bool solid) const = 0; /// Get buffers hitting occlusion rays with model matrices /// @note buffers invalidated after update - virtual void getModels(draw_call draw, const glm::ifvec3 &from, float far_dist, const std::vector &occlusion, const glm::llvec3 &offset, int density, bool solid) = 0; + virtual void getTerrainModels(terrain_draw_call draw, const world::world_pos &from, float far_dist, const std::vector &occlusion, const world::cell_pos&offset, bool solid) const = 0; - /// Get buffer corresponding to entity idx - virtual render::Model* getEntityModel(size_t) = 0; - /// Get entity instance model if in frustum - static _FORCE_INLINE_ bool CullEntity(glm::mat4& out, const glm::vec3& size, const glm::vec3& scale, const glm::ifvec3& pos, - const std::optional& frustum, const glm::llvec3& offset, int density) - { - const glm::vec3 fPos = (glm::vec3(pos.raw_as_long() - offset * glm::llvec3(density)) + pos.offset) / glm::vec3(density); - if (!frustum.has_value() || frustum.value().contains(geometry::Box::fromMin(fPos, size))) { - out = glm::scale(glm::translate(glm::mat4(1), fPos * (float)density), scale); - return true; - } - return false; - }; + /// Get buffer corresponding to part idx + virtual render::Model* getModel(const world::part_id&) const = 0; + /// Get buffers in frustum with model matrices + /// @note buffers invalidated after update + virtual void getUniqueModels(unique_draw_call draw, const world::Elements&, const std::optional& frustum, const world::cell_pos& offset) const = 0; + + /// Get buffer corresponding to model idx + virtual render::Model* getModel(const world::model_id&) const = 0; + /// Get buffers in frustum with model matrices + /// @note buffers invalidated after update + virtual void getInstancedModels(instanced_draw_call draw, const world::Elements&, const std::optional& frustum, const world::cell_pos& offset, world::node_id ignore) const = 0; }; } \ No newline at end of file diff --git a/src/client/contouring/FlatDualMC.cpp b/src/client/contouring/FlatDualMC.cpp index 86cb48f..d3dfc5d 100644 --- a/src/client/contouring/FlatDualMC.cpp +++ b/src/client/contouring/FlatDualMC.cpp @@ -1,12 +1,10 @@ #include "FlatDualMC.hpp" -#include "../../core/world/EdittableChunk.hpp" -#include "../../core/world/Area.hpp" -#include "../../core/world/materials.hpp" +#include "core/world/Elements.hpp" #include // NOLINT #include // NOLINT #include // NOLINT -#include +#include "core/utils/toml.hpp" #include "dualmc.h" #include "optimizer.hpp" @@ -49,42 +47,49 @@ namespace contouring { #endif std::vector tmp; while (running) { - if (entity_area_job_t job; entityLoadQueue.pop(job)) { - ZoneScopedN("Entity"); - render::Model::Data data; - render::Model::Data::indices_t idx; - for (uint8_t x = 0; x < job.size.x; x++) { - for (uint8_t y = 0; y < job.size.y; y++) { - for (uint8_t z = 0; z < job.size.z; z++) { - surrounding::corners sur; - surrounding::load(sur, job.size, glm::ucvec3(x, y, z), job.area); - idx.clear(); - render(sur, idx, tmp, Layer::Both); //MAYBE: write inplace with sub-vector - simplify(idx, tmp); + const auto area_priority = areaLoadQueue.currentWeight(); + const auto part_priority = partLoadQueue.currentWeight(); - data.indices.reserve(data.indices.size() + idx.size()); - const auto idx_start = data.vertices.size(); - std::transform(idx.begin(), idx.end(), std::back_inserter(data.indices), [&](glm::u16 i) { return i + idx_start; }); - data.vertices.reserve(data.vertices.size() + tmp.size()); - const auto vert_offset = glm::vec3(x * CHUNK_LENGTH, y * CHUNK_LENGTH, z * CHUNK_LENGTH); - std::transform(tmp.begin(), tmp.end(), std::back_inserter(data.vertices), [&](render::VertexData v) { - v.Position += vert_offset; return PACK(v); }); - }}} - optimize_fetch(data); - entityLoadedQueue.emplace(job.id, data); - } else if (std::pair, surrounding::corners> ctx; loadQueue.pop(ctx)) { - ZoneScopedN("Chunk"); - std::pair data; - if (transparency) { - render(ctx.second, data.first, tmp, Layer::Solid); - render(ctx.second, data.second, tmp, Layer::Transparent); - } else { - render(ctx.second, data.first, tmp, Layer::Both); + if (part_priority > area_priority) { + if (std::pair job; partLoadQueue.pop(job)) { + ZoneScopedN("Part"); + render::Model::Data data; + render::Model::Data::indices_t idx; + for (uint8_t x = 0; x < job.second.size.x; x++) { + for (uint8_t y = 0; y < job.second.size.y; y++) { + for (uint8_t z = 0; z < job.second.size.z; z++) { + surrounding::corners sur; + surrounding::load(sur, job.second.size, glm::ucvec3(x, y, z), job.second.chunks); + idx.clear(); + render(sur, idx, tmp, Layer::Both); //MAYBE: write inplace with sub-vector + simplify(idx, tmp); + + data.indices.reserve(data.indices.size() + idx.size()); + const auto idx_start = data.vertices.size(); + std::transform(idx.begin(), idx.end(), std::back_inserter(data.indices), [&](glm::u16 i) { return i + idx_start; }); + data.vertices.reserve(data.vertices.size() + tmp.size()); + const auto vert_offset = glm::vec3(x * world::CHUNK_LENGTH, y * world::CHUNK_LENGTH, z * world::CHUNK_LENGTH); + std::transform(tmp.begin(), tmp.end(), std::back_inserter(data.vertices), [&](render::VertexData v) { + v.Position += vert_offset; return PACK(v); }); + }}} + optimize_fetch(data); + partLoadedQueue.emplace(job.first, std::move(data)); + } + } else if (area_priority.has_value() && (!part_priority.has_value() || area_priority.value() >= part_priority.value())) { + if (std::pair ctx; areaLoadQueue.pop(ctx)) { + ZoneScopedN("Chunk"); + area_models::data data; + if (transparency) { + render(ctx.second, data.main, tmp, Layer::Solid); + render(ctx.second, data.transparent, tmp, Layer::Transparent); + } else { + render(ctx.second, data.main, tmp, Layer::Both); + } + //MAYBE: direct upload with vulkan + areaLoadedQueue.emplace(ctx.first, std::move(data)); } - //TODO: direct upload with vulkan - loadedQueue.emplace(ctx.first, data); } else { - loadQueue.wait(); + areaLoadQueue.wait(); } } }); @@ -92,20 +97,12 @@ namespace contouring { } FlatDualMC::~FlatDualMC() { running = false; - loadQueue.notify_all(); + areaLoadQueue.notify_all(); for(auto& worker: workers) { if (worker.joinable()) worker.join(); } - - //TODO: prefer unique_ptr - for(auto& buffer: buffers) { - for(auto& val: buffer.second.second) { - delete val.second.first; - delete val.second.second; - } - } } std::string FlatDualMC::getOptions() const { @@ -127,135 +124,145 @@ namespace contouring { return ss.str(); } std::pair FlatDualMC::getFarRange() const { - return std::make_pair((loadDistance - 1.5) * CHUNK_LENGTH, (keepDistance + .5) * CHUNK_LENGTH); + return std::make_pair((loadDistance - 1.5) * world::CHUNK_LENGTH, (keepDistance + .5) * world::CHUNK_LENGTH); } size_t FlatDualMC::getQueueSize() { - return loadQueue.size(); + return areaLoadQueue.size() + partLoadQueue.size(); } - void FlatDualMC::enqueue(const area_ &pos, const chunk_pos &offset, const world::ChunkContainer &data) - { + void FlatDualMC::enqueue(const world::area_chunk_pos &pos, glm::ll offset, const world::ChunkContainer &data) { ZoneScopedN("EnqueueContouring"); - const auto dist2 = glm::length2(offset - pos.second); - if (dist2 <= loadDistance * loadDistance) { + if (offset <= loadDistance * loadDistance) { surrounding::corners surrounding; - if(surrounding::load(surrounding, pos.second, data)) { - loadQueue.push(pos, surrounding, -dist2); + if(surrounding::load(surrounding, pos.chunk, data)) { + areaLoadQueue.push(pos, std::move(surrounding), -offset); } } - } - - void FlatDualMC::onUpdate(const area_ &pos, const chunk_pos &offset, const world::ChunkContainer &data, geometry::Faces neighbors) { + } + void FlatDualMC::onUpdate(const world::area_chunk_pos &pos, glm::ll offset, const world::ChunkContainer &data, geometry::Faces neighbors) { enqueue(pos, offset, data); if (neighbors && (geometry::Faces::Left | geometry::Faces::Down | geometry::Faces::Backward)) { for (size_t i = 1; i < 8; i++) { - enqueue(std::make_pair(pos.first, pos.second - surrounding::g_corner_offsets[i]), offset, data); + enqueue(world::area_chunk_pos{pos.area, pos.chunk - surrounding::g_corner_offsets[i]}, offset, data); } } } - - void FlatDualMC::onNotify(const area_ &pos, const chunk_pos &offset, const world::ChunkContainer &data) { - const auto it = buffers.find(pos.first); - if(it == buffers.end() || it->second.second.find(pos.second) == it->second.second.end()) { + void FlatDualMC::onNotify(const world::area_chunk_pos &pos, glm::ll offset, const world::ChunkContainer &data) { + const auto area = areas.find(pos.area); + if(area == areas.end() || !area->second.second.contains(pos.chunk)) { enqueue(pos, offset, data); } } - void FlatDualMC::onEntityLoad(size_t id, std::istream &in) { - if(!entities.get(id)) - entities.set_emplace(id, nullptr); - entityLoadedQueue.emplace(id, render::Model::Data(in)); - } - void FlatDualMC::onEntityLoad(size_t id, const glm::ucvec3& size, const std::vector>& area) { - if(!entities.get(id)) - entities.set_emplace(id, nullptr); - entityLoadQueue.emplace(entity_area_job_t{id, size, area}); - loadQueue.notify_one(); - } - void FlatDualMC::onEntityUnload(size_t id) { - entities.erase(id); + void FlatDualMC::onNotify(const world::part_id& id, glm::ll offset, const glm::ucvec3& size, const std::vector>& chunks, bool update) { + if (parts.contains(id)) { + if (!update) + return; + } else { + parts.set_emplace(id, nullptr); + } + if (offset <= loadDistance * loadDistance) { + partLoadQueue.push(id, part_job{size, chunks}, -offset); + } } - void FlatDualMC::update(const voxel_pos& pos, const world::client::area_map& areas) { + void FlatDualMC::onLoad(const world::model_id& id, std::istream& in) { + if (!models.contains(id)) { + models.set_emplace(id, nullptr); + modelLoadedQueue.emplace(id, render::Model::Data(in)); + } + } + void FlatDualMC::onUnload(const world::model_id& id) { + models.vanish(id); + } + + render::Model* FlatDualMC::getModel(const world::part_id& id) const { + if(const auto it = parts.directly_at(id)) + return it->get(); + + return nullptr; + } + render::Model* FlatDualMC::getModel(const world::model_id &id) const { + if(const auto it = models.directly_at(id)) + return it->get(); + + return nullptr; + } + + void FlatDualMC::update(const world::voxel_pos& pos, const world::Elements& l) { ZoneScopedN("Ct"); - TracyPlot("CtLoad", static_cast(loadQueue.size() + entityLoadQueue.size())); - //MAYBE: clear out of range loadQueue.trim(keepDistance * keepDistance) - TracyPlot("CtLoaded", static_cast(loadedQueue.size() + entityLoadedQueue.size())); + TracyPlot("CtLoad", static_cast(getQueueSize())); + TracyPlot("CtLoaded", static_cast(areaLoadedQueue.size() + partLoadedQueue.size() + modelLoadedQueue.size())); - { - std::pair out; - for(auto handle = entityLoadedQueue.extractor(); handle.first(out);) { - if (auto entity = entities.get(out.first)) - *entity = render::Model::Create(out.second); - } - } - - std::pair, std::pair> out; - for(auto handle = loadedQueue.extractor(); handle.first(out);) { - const auto bufferSolid = out.second.first.first.empty() ? NULL : render::LodModel::Create(out.second.first).release(); - const auto bufferTrans = out.second.second.first.empty() ? NULL : render::LodModel::Create(out.second.second).release(); - auto &bfs = buffers[out.first.first].second; //NOTE: buffer.first uninitialized (will be set in clear()) - if (const auto it = bfs.find(out.first.second); it != bfs.end()) { - if (it->second.first != NULL) - delete it->second.first; - if (it->second.second != NULL) - delete it->second.second; - - it->second.first = bufferSolid; - it->second.second = bufferTrans; - } else { - bfs.emplace(out.first.second, std::make_pair(bufferSolid, bufferTrans)); - } - } size_t buffer_count = 0; - { + { // Models + modelLoadedQueue.extractor([&](const std::pair& out) { + if (const auto entity = models.directly_at(out.first)) + *entity = render::Model::Create(out.second); + }); + models.extract([&](const world::model_id &id, const std::unique_ptr &) { + return !l.models.contains(id); }); + buffer_count += models.size(); + } + { // Parts + partLoadedQueue.extractor([&](const std::pair& out) { + if (const auto entity = parts.directly_at(out.first)) + *entity = render::Model::Create(out.second); + }); + parts.extract([&](const world::part_id &id, const std::unique_ptr &) { + return !l.nodes.contains(id.val); }); + //MAYBE: or too far + buffer_count += parts.size(); + } + { // Areas + areaLoadedQueue.extractor([&](const std::pair &out) { + area_models mds; + if (!out.second.main.first.empty()) + mds.main = render::LodModel::Create(out.second.main); + if (!out.second.transparent.first.empty()) + mds.transparent = render::LodModel::Create(out.second.transparent); + auto &chunks = areas[out.first.area].second; //NOTE: areas[n].first uninitialized: will be set by second part + chunks.erase(out.first.chunk); + chunks.emplace(out.first.chunk, std::move(mds)); + }); ZoneScopedN("CtUpdate"); const auto levelMax = loadedLevels.size(); - auto it_a = buffers.begin(); - while (it_a != buffers.end()) { // Remove out of range buffers - if (const auto area = areas.find(it_a->first); area != areas.end()) { - //Update - std::get<0>(it_a->second.first) = area->second->getOffset(); - std::get<1>(it_a->second.first) = area->second->getChunks().getRadius() * static_cast(CHUNK_LENGTH); - const auto centerV = pos - std::get<0>(it_a->second.first).as_voxel(); - if (const auto surface = area->second->getCurvature()) { - const auto dist = glm::max_axis(glm::abs(centerV)); - std::get<2>(it_a->second.first) = std::clamp((dist - surface.value()) / (std::get<1>(it_a->second.first) - surface.value()), -0.1f, 1.f); + for (auto it_a = areas.begin(); it_a != areas.end();) { + if (const auto node = world::NodeOf::Make(l.nodes.directly_at(it_a->first.val))) { + auto &infos = it_a->second.first; + infos.absolute = node->absolute; + const auto ptr = node->get(); + infos.radius = ptr->getChunks().getRadius() * (world::cell_pos::value_type)world::CHUNK_LENGTH; + const auto diff = pos - (world::cell_pos)infos.absolute.position; + if (const auto surface = ptr->getCurvature()) { + // MAYBE: move to area or generator + infos.curvature = std::clamp((glm::max_axis(glm::abs(diff)) - surface.value()) / (infos.radius - surface.value()), -0.1f, 1.f); } - const auto center = glm::divide(centerV); - auto &bfs = it_a->second.second; - auto it = bfs.begin(); - while(it != bfs.end()) { - if (const auto distRatio = glm::length(glm::dvec3(center - it->first)) / keepDistance; distRatio > 1) { - if(it->second.first != NULL) - delete it->second.first; - if (it->second.second != NULL) - delete it->second.second; - - it = bfs.erase(it); - } else { + const auto chunkDiff = glm::dvec3(glm::divide(diff)); + auto &chunks = it_a->second.second; + for (auto it_c = chunks.begin(); it_c != chunks.end();) { + const auto distRatio = glm::length(chunkDiff - (glm::qua)infos.absolute.rotation * glm::dvec3(it_c->first)) / keepDistance; + if (distRatio <= 1) { const auto level = std::clamp((1 + lod_quality - distRatio) * levelMax * (1 + lod_strength), 0, levelMax); - if (it->second.first != NULL) { - it->second.first->setLevel(level); + if (it_c->second.main) { + it_c->second.main->setLevel(level); buffer_count++; } - if (it->second.second != NULL) { - it->second.second->setLevel(level); + if (it_c->second.transparent) { + it_c->second.transparent->setLevel(level); buffer_count++; } - ++it; + ++it_c; + } else { + it_c = chunks.erase(it_c); } } - ++it_a; + } + if (it_a->second.second.empty()) { + it_a = areas.erase(it_a); } else { - for(auto& buffer: it_a->second.second) { - if (buffer.second.first != NULL) - delete buffer.second.first; - if(buffer.second.second != NULL) - delete buffer.second.second; - } - it_a = buffers.erase(it_a); + ++it_a; } } } @@ -285,46 +292,48 @@ namespace contouring { ImGui::NextColumn(); for(int i = LEVELS.size() - 1; i >= 0; i--) { const auto str = std::to_string(static_cast(1 / LEVELS[i].first)); - ImGui::Selectable(str.c_str(), &lod_levels[i]); + if (ImGui::Selectable(str.c_str(), &lod_levels[i])) { + loadedLevels.clear(); + for (size_t i = 0; i < LEVELS.size(); i++) { + if(lod_levels[i]) + loadedLevels.push_back(LEVELS[i]); + } + } ImGui::NextColumn(); } ImGui::Columns(1); } if (ImGui::Button("Flush buffers")) { - //TODO: prefer unique_ptr - for(auto& buffer: buffers) { - for(auto& val: buffer.second.second) { - delete val.second.first; - delete val.second.second; - } - } - buffers.clear(); + areas.clear(); + parts.extract([](const world::part_id &, const std::unique_ptr &) { return true; }); + models.extract([](const world::model_id &, const std::unique_ptr &) { return true; }); } } void FlatDualMC::render(const surrounding::corners &surrounding, render::Model::Data::indices_t &out, std::vector &tmp, Layer layer) const { - const int SIZE = CHUNK_LENGTH + 3; + const int SIZE = world::CHUNK_LENGTH + 3; std::array::Point, SIZE * SIZE * SIZE> grid; + const auto &materials = world::module::Registry::Get()->getMaterials(); { ZoneScopedN("Load"); const auto setCell = [&](int x, int y, int z, const world::Voxel &voxel) { auto &cell = grid[((z * SIZE) + y) * SIZE + x]; cell.w = voxel.material(); - cell.x = voxel.density_ratio() * (!world::materials::invisibility[cell.w] && - ((world::materials::transparency[cell.w] && (layer && Layer::Transparent)) || - (!world::materials::transparency[cell.w] && (layer && Layer::Solid)))); + cell.x = voxel.density_ratio() * (!materials.invisibilities[cell.w] && + ((materials.transparencies[cell.w] && (layer && Layer::Transparent)) || + (!materials.transparencies[cell.w] && (layer && Layer::Solid)))); }; for (int z = 0; z < SIZE; z++) { for (int y = 0; y < SIZE; y++) { for (int x = 0; x < SIZE; x++) { - const auto &chunk = surrounding[(z >= CHUNK_LENGTH) + (y >= CHUNK_LENGTH)*2 + (x >= CHUNK_LENGTH)*4]; - const auto &voxel = chunk->get(glm::toIdx(x % CHUNK_LENGTH, y % CHUNK_LENGTH, z % CHUNK_LENGTH)); + const auto &chunk = surrounding[(z >= world::CHUNK_LENGTH) + (y >= world::CHUNK_LENGTH)*2 + (x >= world::CHUNK_LENGTH)*4]; + const auto &voxel = chunk->get(glm::toIdx(x % world::CHUNK_LENGTH, y % world::CHUNK_LENGTH, z % world::CHUNK_LENGTH)); setCell(x, y, z, voxel); }}} for (size_t i = 0; i < surrounding.size(); i++) { - auto &edits = surrounding[i]->getEdits(); - auto offset = glm::ivec3(surrounding::g_corner_offsets[i]) * CHUNK_LENGTH; + auto &edits = surrounding[i]->getEdits().getEdits(); + auto offset = glm::ivec3(surrounding::g_corner_offsets[i]) * world::CHUNK_LENGTH; for (auto it = edits.begin(); it != edits.end(); ++it) { auto p = offset + glm::ivec3(glm::fromIdx(it->first)); if(p.x < SIZE && p.y < SIZE && p.z < SIZE) { @@ -338,7 +347,7 @@ namespace contouring { std::vector dmc_tris; dualmc::DualMC builder; - builder.buildTris(&grid.front(), SIZE, SIZE, SIZE, iso, world::materials::textures_map.data(), world::materials::roughness.data(), + builder.buildTris(&grid.front(), SIZE, SIZE, SIZE, iso, materials.texture_ids.data(), materials.roughnesses.data(), manifold, dmc_vertices, dmc_tris); tmp.clear(); @@ -383,45 +392,96 @@ namespace contouring { optimize_fetch(out.first); } - void FlatDualMC::getModels(draw_call out, const std::optional &frustum, const glm::llvec3& offset, int density, bool solid) { - const auto scaling = glm::scale(glm::mat4(1), glm::vec3(1.f / density)); - for (const auto [_, area] : buffers) { - for (const auto [pos, buf] : area.second) { - const auto vPos = glm::multiply(pos); - const glm::vec3 fPos = (glm::vec3(std::get<0>(area.first).raw_as_long() + vPos - offset * glm::llvec3(density)) + std::get<0>(area.first).offset) / glm::vec3(density); - const auto buffer = solid ? buf.first : buf.second; - if (buffer != NULL && (!frustum.has_value() || frustum.value().contains(geometry::Box::fromMin(fPos, glm::vec3(CHUNK_LENGTH / (float)density))))) - out(glm::translate(scaling, fPos * (float)density), buffer, area.first, vPos); - }} + void FlatDualMC::getTerrainModels(terrain_draw_call out, const std::optional &frustum, const world::cell_pos& offset, bool solid) const { + for (const auto& area: areas) { + const auto absolute = area.second.first.absolute; + const auto rotation = glm::toMat4(area.second.first.absolute.rotation); + const auto rot_abs = glm::abs(rotation); + for (const auto& chunk: area.second.second) { + const auto buffer = solid ? chunk.second.main.get() : chunk.second.transparent.get(); + if (!buffer) // TODO: Fix bad branch prediction + continue; + + const auto cPos = glm::multiply(chunk.first); + if (frustum) { + const glm::vec3 fCenterPos = absolute.computeChild(cPos + world::cell_pos(world::CHUNK_LENGTH / 2)) - (world::world_pos)offset; + if (!frustum.value().contains(geometry::faabb::FromCenterAbs(fCenterPos, glm::vec3(world::CHUNK_LENGTH / 2), rot_abs))) + continue; + } + const glm::vec3 fMinPos = absolute.computeChild(cPos) - (world::world_pos)offset; + out(glm::translate(glm::mat4(1), fMinPos) * rotation, buffer, area.second.first, cPos); + } + } } - void FlatDualMC::getModels(draw_call out, const glm::ifvec3& from, float far_dist, const std::vector &occlusion, const glm::llvec3 &offset, int density, bool solid) { - const auto scaling = glm::scale(glm::mat4(1), glm::vec3(1.f / density)); - const auto start = glm::ifvec3(glm::divide(from.as_voxel(density))); - const auto dist = far_dist * density / CHUNK_LENGTH; - for (const auto [_, area] : buffers) { - const auto area_offset = glm::divide(std::get<0>(area.first).as_voxel()); - robin_hood::unordered_set done; + void FlatDualMC::getTerrainModels(terrain_draw_call out, const world::world_pos& from, float far_dist, const std::vector &occlusion, const world::cell_pos &offset, bool solid) const { + const auto start = world::world_pos(glm::divide(from)); + const auto dist = far_dist / world::CHUNK_LENGTH; + for (const auto& area: areas) { + const auto &absolute = area.second.first.absolute; + const auto area_offset = glm::divide(absolute.position); + const auto rotation = glm::toMat4(absolute.rotation); + robin_hood::unordered_set done; for (const auto& occ: occlusion) { - geometry::Ray::iterator it(geometry::Ray(start, occ, dist)); + geometry::Ray::iterator it(geometry::Ray(start, absolute.rotation * occ, dist)); glm::llvec3 point; while (it.next(point)) { - auto it = area.second.find(glm::lvec3(point) - area_offset); - const auto buffer = solid ? it->second.first : it->second.second; - if(it != area.second.end() && buffer != NULL && done.insert(it->first).second) { - const auto vPos = glm::multiply(it->first); - const glm::vec3 fPos = glm::vec3(std::get<0>(area.first).raw_as_long() + vPos - offset * glm::llvec3(density)) + std::get<0>(area.first).offset; - out(glm::translate(scaling, fPos), buffer, area.first, vPos); - break; + const auto &chunks = area.second.second; + if (const auto it = chunks.find(glm::lvec3(point) - area_offset); it != chunks.end()) { + const auto buffer = solid ? it->second.main.get() : it->second.transparent.get(); + if (buffer != NULL && done.insert(it->first).second) { + const auto cPos = glm::multiply(it->first); + const glm::vec3 fPos = absolute.computeChild(cPos) - (world::world_pos)offset; + out(glm::translate(glm::mat4(1), fPos) * rotation, buffer, area.second.first, cPos); + break; + } } } } } } - render::Model* FlatDualMC::getEntityModel(size_t id) { - if (auto ptr = entities.get(id)) - return ptr->get(); - return nullptr; + void FlatDualMC::getUniqueModels(unique_draw_call out, const world::Elements& l, const std::optional& frustum, const world::cell_pos& offset) const { + for (const auto& ref: l.parts) { + const world::part_id id = l.withFlag(ref.val).val; + const auto node = l.findPart(id); + + if (const auto buffer = getModel(id)) { + const auto rotation = glm::toMat4(node->absolute.rotation); + const glm::vec3 fMinPos = node->absolute.position - (world::world_pos)offset; + if (frustum) { + if (!frustum.value().contains(geometry::faabb::FromMin(fMinPos, node->get()->size, rotation))) + continue; + } + out(glm::translate(glm::mat4(1), fMinPos) * rotation, buffer); + } + } } + + void FlatDualMC::getInstancedModels(instanced_draw_call out, const world::Elements& l, const std::optional& frustum, const world::cell_pos& offset, world::node_id ignore) const { + std::vector matrices; + l.models.iter([&] (const world::model_id& id, const world::Model& model) { + if (const auto buffer = getModel(id)) { + for (const auto& ref: model.instances) { + const auto iid = l.withFlag(ref.val); + if (iid == ignore) + continue; + + const auto node = world::NodeOf::Make(l.findNode(iid)); + const auto rotation = glm::toMat4(node->absolute.rotation); + const glm::vec3 fMinPos = node->absolute.position - (world::world_pos)offset; + if (frustum) { + if (!frustum.value().contains(model.collision.rotate(node->absolute.rotation) + fMinPos)) + continue; + } + matrices.push_back(glm::scale(glm::translate(glm::mat4(1), fMinPos) * rotation, model.scale)); + } + if (!matrices.empty()) { + out(matrices, buffer); + matrices.clear(); + } + } + }); + } + } diff --git a/src/client/contouring/FlatDualMC.hpp b/src/client/contouring/FlatDualMC.hpp index a745644..ae54e03 100644 --- a/src/client/contouring/FlatDualMC.hpp +++ b/src/client/contouring/FlatDualMC.hpp @@ -3,10 +3,12 @@ #include "Abstract.hpp" #include "surrounding.hpp" -#include "../../core/data/safe_queue.hpp" -#include "../../core/data/safe_priority_queue.hpp" +#include "core/queue/safe_queue.hpp" +#include "core/queue/safe_priority_queue.hpp" +#include "core/generational/vector.hpp" +#include "core/generational/map.hpp" #include "../render/api/Models.hpp" -#include "../../core/data/math.hpp" +#include "core/geometry/math.hpp" #include using namespace data; @@ -17,7 +19,7 @@ namespace contouring { FlatDualMC(const std::string&); virtual ~FlatDualMC(); - void update(const voxel_pos&, const world::client::area_map&) override; + void update(const world::cell_pos &pos, const world::Elements &) override; void onGui() override; @@ -25,44 +27,53 @@ namespace contouring { std::pair getFarRange() const override; size_t getQueueSize() override; - /// Chunk data change - void onUpdate(const area_ &, const chunk_pos &, const world::ChunkContainer &, geometry::Faces) override; - /// Chunk existante ping - /// @note notify for chunks entering view while moving - void onNotify(const area_ &, const chunk_pos &, const world::ChunkContainer &) override; + void onUpdate(const world::area_chunk_pos &pos, glm::ll offset, const world::ChunkContainer &data, geometry::Faces neighbors) override; + void onNotify(const world::area_chunk_pos &pos, glm::ll offset, const world::ChunkContainer &data) override; + void onNotify(const world::part_id&, glm::ll offset, const glm::ucvec3& size, const std::vector>&, bool update) override; + void onLoad(const world::model_id&, std::istream&) override; + void onUnload(const world::model_id&) override; - /// Register entity from model - void onEntityLoad(size_t id, std::istream&) override; - /// Register entity from area - void onEntityLoad(size_t id, const glm::ucvec3& size, const std::vector>&) override; - /// Free entity model - void onEntityUnload(size_t id) override; + void getTerrainModels(terrain_draw_call draw, const std::optional& frustum, const world::cell_pos& offset, bool solid) const override; + void getTerrainModels(terrain_draw_call draw, const world::world_pos &from, float far_dist, const std::vector &occlusion, const world::cell_pos&offset, bool solid) const override; - /// Get buffers in frustum with model matrices - /// @note buffers invalidated after update - void getModels(draw_call draw, const std::optional &frustum, const glm::llvec3 &offset, int density, bool solid) override; - /// Get buffers hitting occlusion rays with model matrices - /// @note buffers invalidated after update - void getModels(draw_call draw, const glm::ifvec3 &from, float far_dist, const std::vector &occlusion, const glm::llvec3 &offset, int density, bool solid) override; + render::Model* getModel(const world::part_id&) const override; + void getUniqueModels(unique_draw_call draw, const world::Elements&, const std::optional& frustum, const world::cell_pos& offset) const override; - render::Model* getEntityModel(size_t) override; + render::Model* getModel(const world::model_id&) const override; + void getInstancedModels(instanced_draw_call draw, const world::Elements&, const std::optional& frustum, const world::cell_pos& offset, world::node_id ignore) const override; - protected: - //FIXME: use unique_ptr - robin_hood::unordered_map>>> buffers; + private: + struct area_models { + std::unique_ptr main; + std::unique_ptr transparent; + struct data { + data() { } + data(data&& src): + main(std::move(src.main)), transparent(std::move(src.transparent)) { } - data::generational::view_vector> entities; + render::LodModel::LodData main; + render::LodModel::LodData transparent; + }; + }; + struct part_job { + glm::ucvec3 size; + std::vector> chunks; + }; - safe_priority_queue_map, surrounding::corners, int, area_hash> loadQueue; - safe_queue, std::pair>> loadedQueue; - struct entity_area_job_t { size_t id; glm::ucvec3 size; std::vector> area; }; - safe_queue entityLoadQueue; - safe_queue> entityLoadedQueue; + robin_hood::unordered_map>> areas; + generational::vector, world::Part> parts; + generational::vector, world::Model> models; + + safe_priority_queue_map areaLoadQueue; + safe_queue> areaLoadedQueue; + safe_priority_queue_map partLoadQueue; + safe_queue> partLoadedQueue; + safe_queue> modelLoadedQueue; bool running = true; std::vector workers; - void enqueue(const area_ &, const chunk_pos &offset, const world::ChunkContainer &); + void enqueue(const world::area_chunk_pos&, glm::ll offset, const world::ChunkContainer &); uint16_t loadDistance = 3; uint16_t keepDistance = 4; diff --git a/src/client/contouring/dualmc.h b/src/client/contouring/dualmc.h index 5996f99..7544b6e 100644 --- a/src/client/contouring/dualmc.h +++ b/src/client/contouring/dualmc.h @@ -106,7 +106,7 @@ public: Point const *data, int32_t const dimX, int32_t const dimY, int32_t const dimZ, VolumeDataType const iso, - uint16_t const *textures_map, + size_t const *textures_map, float const *roughness, bool const generateManifold, std::vector &vertices, @@ -193,7 +193,7 @@ protected: Point const *data; /// point to vertex property table - uint16_t const *textures_map; + size_t const *textures_map; /// property roughness table float const *roughness; @@ -548,7 +548,7 @@ void DualMC::buildTris( Point const * data, int32_t const dimX, int32_t const dimY, int32_t const dimZ, VolumeDataType const iso, - uint16_t const * textures_map, + size_t const * textures_map, float const * roughness, bool const generateManifold, std::vector & vertices, diff --git a/src/client/contouring/notifier.hpp b/src/client/contouring/notifier.hpp new file mode 100644 index 0000000..f5eb05f --- /dev/null +++ b/src/client/contouring/notifier.hpp @@ -0,0 +1,22 @@ +#pragma once + +namespace contouring { + + struct notifier { + world::chunk_pos last_pos; + float last_time = 0; + + static constexpr auto INTERVAL = 5; + bool mustNotify(const world::cell_pos& pos, float deltaTime) { + const auto cPos = glm::divide(pos); + if (last_pos != cPos || last_time < deltaTime) { + last_pos = cPos; + last_time = INTERVAL; + return true; + } + last_time -= deltaTime; + return false; + } + }; + +} \ No newline at end of file diff --git a/src/client/contouring/surrounding.cpp b/src/client/contouring/surrounding.cpp index 6aabbc1..c3544bd 100644 --- a/src/client/contouring/surrounding.cpp +++ b/src/client/contouring/surrounding.cpp @@ -1,24 +1,22 @@ #include "surrounding.hpp" -#include "../../core/world/Area.hpp" +#include "core/world/Area.hpp" #include "../world/Chunk.hpp" -#include "../../core/data/math.hpp" +#include "core/geometry/math.hpp" using namespace geometry; namespace contouring::surrounding { - bool load(corners &out, const chunk_pos &chunkPos, const world::ChunkContainer &chunks) { - { - const auto chunk = chunks.findInRange(chunkPos); - if(!chunk.has_value()) - return false; - - out[0] = std::dynamic_pointer_cast(chunk.value()); + bool load(corners &out, const world::chunk_pos &chunkPos, const world::ChunkContainer &chunks) { + if (const auto chunk = chunks.findInRange(chunkPos)) { + out[0] = chunk; + } else { + return false; } for (size_t i = 1; i < 8; i++) { const auto pos = chunkPos + g_corner_offsets[i]; if (chunks.inRange(pos)) { if(const auto chunk = chunks.findInRange(pos)) { - out[i] = std::dynamic_pointer_cast(chunk.value()); + out[i] = chunk; } else return false; } else { @@ -27,11 +25,11 @@ namespace contouring::surrounding { } return true; } - void load(corners &out, const glm::ucvec3 &areaSize, const glm::ucvec3 &chunkPos, const std::vector> &area) { + void load(corners &out, const glm::ucvec3 &areaSize, const glm::ucvec3 &chunkPos, const std::vector> &area) { for (size_t i = 0; i < 8; i++) { const auto pos = chunkPos + glm::ucvec3(g_corner_offsets[i]); if (pos.x < areaSize.x && pos.y < areaSize.y && pos.z < areaSize.z) { - out[i] = std::dynamic_pointer_cast(area.at(glm::toIdx(pos, areaSize))); + out[i] = area.at(glm::toIdx(pos, areaSize)); } else { out[i] = world::client::EMPTY_CHUNK; } diff --git a/src/client/contouring/surrounding.hpp b/src/client/contouring/surrounding.hpp index 0afa9f3..c073bb5 100644 --- a/src/client/contouring/surrounding.hpp +++ b/src/client/contouring/surrounding.hpp @@ -1,22 +1,23 @@ #pragma once -#include "../../core/world/forward.h" +#include "core/world/units.hpp" +#include "core/world/forward.h" #include #include namespace contouring::surrounding { - typedef std::array, 8> corners; - const chunk_pos g_corner_offsets[8] = { - chunk_pos(0, 0, 0), - chunk_pos(0, 0, 1), - chunk_pos(0, 1, 0), - chunk_pos(0, 1, 1), - chunk_pos(1, 0, 0), - chunk_pos(1, 0, 1), - chunk_pos(1, 1, 0), - chunk_pos(1, 1, 1), + typedef std::array, 8> corners; + const world::chunk_pos g_corner_offsets[8] = { + world::chunk_pos(0, 0, 0), + world::chunk_pos(0, 0, 1), + world::chunk_pos(0, 1, 0), + world::chunk_pos(0, 1, 1), + world::chunk_pos(1, 0, 0), + world::chunk_pos(1, 0, 1), + world::chunk_pos(1, 1, 0), + world::chunk_pos(1, 1, 1), }; - bool load(corners &out, const chunk_pos &chunkPos, const world::ChunkContainer &chunks); - void load(corners &out, const glm::ucvec3 &areaSize, const glm::ucvec3 &chunkPos, const std::vector> &area); + bool load(corners &out, const world::chunk_pos &chunkPos, const world::ChunkContainer &chunks); + void load(corners &out, const glm::ucvec3 &areaSize, const glm::ucvec3 &chunkPos, const std::vector> &area); } diff --git a/src/client/control/Camera.cpp b/src/client/control/Camera.cpp index 118f9d3..b9ae201 100644 --- a/src/client/control/Camera.cpp +++ b/src/client/control/Camera.cpp @@ -1,10 +1,18 @@ #include "Camera.hpp" #include -#include "../Window.hpp" +#include "../render/Window.hpp" +#include "core/geometry/math.hpp" -Camera::Camera(const Controllable* origin, const Camera::options& opt): origin(origin), o(opt) { - updateProjection(); +std::pair Camera::Split(const world::world_pos& p) { + constexpr auto MOD = glm::uvec3(glm::IDX_LENGTH2); + const auto offset = glm::divide(p, MOD); + return {offset, p - (world::world_pos)offset}; +} + +Camera::Camera(const world::world_pos* absolute, const Controllable* origin, const Camera::options& opt) { + setOrigin(absolute, origin); + setOptions(opt); } Camera::~Camera() { } @@ -13,7 +21,7 @@ void Camera::updateProjection() { } void Camera::update() { - const auto &offset = origin->position.offset; + const auto &offset = Split(*absolute).second; // FIXME: up inverted after backflip const auto axis = origin->getAxis(); // Camera matrix diff --git a/src/client/control/Camera.hpp b/src/client/control/Camera.hpp index 9327e4f..f6ff88b 100644 --- a/src/client/control/Camera.hpp +++ b/src/client/control/Camera.hpp @@ -1,19 +1,21 @@ #pragma once #include "Controllable.hpp" -#include "../../core/geometry/Frustum.hpp" -#include "../../core/geometry/Ray.hpp" +#include "core/geometry/Frustum.hpp" +#include "core/geometry/Ray.hpp" /// Moving perspective camera class Camera { public: + static std::pair Split(const world::world_pos&); + struct options { float fov = glm::radians(70.f); float near_dist = 0.1; float far_dist = 64; }; - Camera(const Controllable*, const options&); + Camera(const world::world_pos* absolute, const Controllable*, const options&); ~Camera(); void update(); @@ -21,18 +23,20 @@ public: o = options; updateProjection(); } - void setOrigin(const Controllable* ct) { + void setOrigin(const world::world_pos* abs, const Controllable* ct) { + absolute = abs; origin = ct; } inline geometry::Frustum getFrustum() const { return geometry::Frustum(ViewMatrix, ProjectionMatrix); } - inline geometry::Ray getRay() const { return geometry::Ray(origin->position, origin->getDirection(), o.far_dist); } + inline geometry::Ray getRay() const { return geometry::Ray(*absolute, origin->getDirection(), o.far_dist); } constexpr glm::mat4 getViewMatrix() const { return ViewMatrix; } constexpr glm::mat4 getProjectionMatrix() const { return ProjectionMatrix; } constexpr float getDepth() const { return o.far_dist; } private: + const world::world_pos *absolute; const Controllable* origin; glm::mat4 ViewMatrix; diff --git a/src/client/control/Controllable.cpp b/src/client/control/Controllable.cpp index 4a467de..00e9165 100644 --- a/src/client/control/Controllable.cpp +++ b/src/client/control/Controllable.cpp @@ -1,8 +1,11 @@ #include "Controllable.hpp" -Controllable::Controllable(GLFWwindow *window, const InputMap& inputs, const Controllable::options& opt): position(), window(window), inputs(inputs), o(opt){ } +Controllable::Controllable(GLFWwindow *window, const InputMap& inputs, const Controllable::options& opt): window(window), inputs(inputs), o(opt){ } Controllable::~Controllable() { } +glm::vec3 Controllable::getDirection() const { + return glm::vec3(cos(VerticalAngle) * sin(HorizontalAngle), sin(VerticalAngle), cos(VerticalAngle) * cos(HorizontalAngle)); +} Controllable::axis Controllable::getAxis() const { Controllable::axis a; // Direction : Spherical coordinates to Cartesian coordinates conversion @@ -16,59 +19,67 @@ Controllable::axis Controllable::getAxis() const { return a; } -void Controllable::capture(bool captureMouse, bool captureKeys, float deltaTime) { - // Get mouse position - if(captureMouse) { - int viewportX, viewportY; - glfwGetWindowSize(window, &viewportX, &viewportY); - if(capturingMouse) { - double xPos, yPos; - glfwGetCursorPos(window, &xPos, &yPos); +void Controllable::capture(bool readMouse, bool captureKeys, float deltaTime, bool freeMouse) { + // Get mouse position + if(readMouse) { + if(capturingMouse) { + double xPos, yPos; + glfwGetCursorPos(window, &xPos, &yPos); - // Compute new orientation - HorizontalAngle += o.sensibility * 0.0001f * float(viewportX / 2 - xPos); - VerticalAngle += o.sensibility * 0.0001f * float(viewportY / 2 - yPos); - } else { - glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - capturingMouse = true; - } - // Reset mouse position for next frame - glfwSetCursorPos(window, viewportX / 2, viewportY / 2); - } else if (capturingMouse) { - glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); - capturingMouse = false; - } + // Compute new orientation + HorizontalAngle += o.sensibility * 0.0001f * float(mouseX - xPos); + VerticalAngle += o.sensibility * 0.0001f * float(mouseY - yPos); + mouseX = xPos; + mouseY = yPos; + } else { + if (!freeMouse) + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + capturingMouse = true; + glfwGetCursorPos(window, &mouseX, &mouseY); + } + // Reset mouse position for next frame + if (!freeMouse) { + int viewportX, viewportY; + glfwGetWindowSize(window, &viewportX, &viewportY); + mouseX = viewportX / 2; + mouseY = viewportY / 2; + glfwSetCursorPos(window, mouseX, mouseY); + } + } else if (capturingMouse) { + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + capturingMouse = false; + } const auto axis = getAxis(); velocity = glm::vec3(0); if(captureKeys) { - // Move forward - if (inputs.isDown(Input::Forward)) { - velocity += axis.direction; - } - // Move backward - if (inputs.isDown(Input::Backward)) { + // Move forward + if (inputs.isDown(Input::Forward)) { + velocity += axis.direction; + } + // Move backward + if (inputs.isDown(Input::Backward)) { velocity -= axis.direction; } - // Strafe right - if (inputs.isDown(Input::Right)) { + // Strafe right + if (inputs.isDown(Input::Right)) { velocity += axis.right; } - // Strafe left - if (inputs.isDown(Input::Left)) { + // Strafe left + if (inputs.isDown(Input::Left)) { velocity -= axis.right; } - // Move up - if (inputs.isDown(Input::Up)) { + // Move up + if (inputs.isDown(Input::Up)) { velocity += axis.up; } - // Move down - if (inputs.isDown(Input::Down)) { + // Move down + if (inputs.isDown(Input::Down)) { velocity -= axis.up; } if(velocity != glm::vec3(0)) { - velocity = glm::normalize(velocity) * deltaTime * o.speed; + velocity = glm::normalize(velocity) * deltaTime * o.speed; } - } + } } \ No newline at end of file diff --git a/src/client/control/Controllable.hpp b/src/client/control/Controllable.hpp index d0400c2..54bc573 100644 --- a/src/client/control/Controllable.hpp +++ b/src/client/control/Controllable.hpp @@ -1,9 +1,9 @@ #pragma once -#include "../InputMap.hpp" +#include "InputMap.hpp" -#include "../../core/data/glm.hpp" -#include "../../core/world/position.h" +#include "core/geometry/glm.hpp" +#include "core/world/units.hpp" /// Player controller moving entity struct Controllable { @@ -17,13 +17,13 @@ public: Controllable(GLFWwindow*, const InputMap&, const options&); ~Controllable(); - void capture(bool captureMouse, bool captureKeys, float deltaTime); + void capture(bool readMouse, bool captureKeys, float deltaTime, bool captureMouse = false); void setOptions(const options &options) { o = options; } std::pair getAngles() const { return std::make_pair(HorizontalAngle, VerticalAngle); } - glm::vec3 getDirection() const { return glm::vec3(cos(VerticalAngle) * sin(HorizontalAngle), sin(VerticalAngle), cos(VerticalAngle) * cos(HorizontalAngle)); } + glm::vec3 getDirection() const; struct axis { glm::vec3 direction; glm::vec3 up; @@ -31,7 +31,6 @@ public: }; axis getAxis() const; - camera_pos position; glm::vec3 velocity; private: @@ -42,5 +41,6 @@ private: float VerticalAngle = 0.0f; bool capturingMouse = false; + double mouseX, mouseY; options o; }; diff --git a/src/client/control/InputMap.cpp b/src/client/control/InputMap.cpp new file mode 100644 index 0000000..228816c --- /dev/null +++ b/src/client/control/InputMap.cpp @@ -0,0 +1,80 @@ +#include "InputMap.hpp" + +const robin_hood::unordered_map DEFAULT_MAP = { + {Input::Forward, GLFW_KEY_W}, + {Input::Backward, GLFW_KEY_S}, + {Input::Left, GLFW_KEY_A}, + {Input::Right, GLFW_KEY_D}, + {Input::Up, GLFW_KEY_SPACE}, + {Input::Down, GLFW_KEY_LEFT_SHIFT}, + {Input::Mouse, GLFW_KEY_E}, + {Input::Debug, GLFW_KEY_F3}, + {Input::Throw, GLFW_KEY_B}, + {Input::Quit, GLFW_KEY_ESCAPE} +}; + +InputMap::InputMap(GLFWwindow* window, const std::vector>& keys): window(window), map(DEFAULT_MAP) { + for(const auto& key: keys) { + map.insert_or_assign(static_cast(key.first), key.second); + } +} +InputMap::~InputMap() { } + +void InputMap::exportKeys(std::vector>& keys) const { + keys.clear(); + for(const auto& key: map) { + if (const auto it = DEFAULT_MAP.find(key.first); it == DEFAULT_MAP.end() || it->second != key.second) { + keys.emplace_back((int)key.first, key.second); + } + } +} + +void InputMap::saveKeys() { + for (auto input : toggles) { + previous.insert_or_assign(input, isDown(input)); + } + previousLeft = isDown(Mouse::Left); + previousRight = isDown(Mouse::Right); +} + +bool InputMap::isDown(Input input) const { + return glfwGetKey(window, map.at(input)) == GLFW_PRESS; +} +bool InputMap::wasDown(Input input) const { + return previous.at(input); +} +bool InputMap::isPressing(Input input) const { + return isDown(input) && !wasDown(input); +} +bool InputMap::isReleasing(Input input) const { + return !isDown(input) && wasDown(input); +} + +void InputMap::toggle(bool &val, Input input) const { + if(isPressing(input)) + val = !val; +} + +bool InputMap::isDown(Mouse input) const { + return glfwGetMouseButton(window, static_cast(input)) == GLFW_PRESS; +} +bool InputMap::wasDown(Mouse input) const { + switch (input) { + case Mouse::Left: + return previousLeft; + + case Mouse::Right: + return previousRight; + + default: + return false; + } +} +bool InputMap::isPressing(Mouse input) const { + return isDown(input) && !wasDown(input); +} +bool InputMap::isReleasing(Mouse input) const { + return !isDown(input) && wasDown(input); +} + +void InputMap::poll() const { glfwPollEvents(); } \ No newline at end of file diff --git a/src/client/InputMap.hpp b/src/client/control/InputMap.hpp similarity index 56% rename from src/client/InputMap.hpp rename to src/client/control/InputMap.hpp index 41a568f..33f6751 100644 --- a/src/client/InputMap.hpp +++ b/src/client/control/InputMap.hpp @@ -17,7 +17,7 @@ enum class Mouse { /// Handle inputs with name to key mapping table class InputMap { public: - InputMap(GLFWwindow*); + InputMap(GLFWwindow*, const std::vector>& keys = {}); ~InputMap(); void saveKeys(); @@ -36,25 +36,16 @@ public: void poll() const; + void exportKeys(std::vector>&) const; + private: GLFWwindow *window; - robin_hood::unordered_map Map = { - {Input::Forward, GLFW_KEY_W}, - {Input::Backward, GLFW_KEY_S}, - {Input::Left, GLFW_KEY_A}, - {Input::Right, GLFW_KEY_D}, - {Input::Up, GLFW_KEY_SPACE}, - {Input::Down, GLFW_KEY_LEFT_SHIFT}, - {Input::Mouse, GLFW_KEY_E}, - {Input::Debug, GLFW_KEY_F3}, - {Input::Throw, GLFW_KEY_B}, - {Input::Quit, GLFW_KEY_ESCAPE} - }; - const std::vector Toggles = { + robin_hood::unordered_map map; + const std::vector toggles = { Input::Mouse, Input::Debug, Input::Throw }; - robin_hood::unordered_map Previous; + robin_hood::unordered_map previous; - bool PreviousLeft; - bool PreviousRight; + bool previousLeft; + bool previousRight; }; diff --git a/src/client/net/Client.cpp b/src/client/net/Client.cpp index 4092de3..4351b3e 100644 --- a/src/client/net/Client.cpp +++ b/src/client/net/Client.cpp @@ -2,6 +2,9 @@ #include +#undef LOG_PREFIX +#define LOG_PREFIX "Client: " + using namespace net::client; namespace net::client { @@ -18,7 +21,7 @@ namespace net::client { } Client::Client(const net::address& ct, - std::function onPacket): + std::function onPacket): Context(nullptr, nullptr), Connection(nullptr, true, queue::count), onPacket(onPacket) { const char *sni = NULL; @@ -46,7 +49,7 @@ Client::Client(const net::address& ct, } } Client::~Client() { - Connection::release((uint16_t)disconnect_reason::QUIT); + Connection::release(false); while (connected && !disconnected) { pull(); std::this_thread::sleep_for(std::chrono::milliseconds(1)); @@ -66,7 +69,7 @@ int Client::connectionCallback(uint64_t stream_id, uint8_t* bytes, size_t length if (stream_ctx == NULL) { if (is_fin) { // Single frame packet if (length > 0) { - onPacket(data::out_view(bytes, length), PacketFlags::TINY); + onPacket(memory::read_view(bytes, length), PacketFlags::TINY); } reset(stream_id); break; @@ -80,7 +83,7 @@ int Client::connectionCallback(uint64_t stream_id, uint8_t* bytes, size_t length } if (is_fin) { - if (onPacket(data::out_view(stream_ctx->buffer.data.data(), stream_ctx->buffer.data.size()), PacketFlags::NONE)) { + if (onPacket(memory::read_view(stream_ctx->buffer.data.data(), stream_ctx->buffer.data.size()), PacketFlags::NONE)) { close(stream_ctx); } else { LOG_E("??"); @@ -91,7 +94,7 @@ int Client::connectionCallback(uint64_t stream_id, uint8_t* bytes, size_t length } case picoquic_callback_datagram: - if(!onPacket(data::out_view(bytes, length), PacketFlags::DATAGRAM)) { + if(!onPacket(memory::read_view(bytes, length), PacketFlags::DATAGRAM)) { return -1; } break; @@ -122,14 +125,10 @@ int Client::connectionCallback(uint64_t stream_id, uint8_t* bytes, size_t length case picoquic_callback_stateless_reset: case picoquic_callback_close: /* Received connection close */ case picoquic_callback_application_close: /* Received application close */ { - struct quit_notify { server_packet_type type; bool is_application; uint16_t reason; }; - const auto is_app = fin_or_event == picoquic_callback_application_close; - const auto quit = quit_notify{server_packet_type::QUIT, is_app, getErrorCode(is_app)}; - if(onPacket(data::out_view((const uint8_t*)&quit, sizeof(quit)), PacketFlags::DATAGRAM)) { - LOG_W("Connection closed"); - disconnected = true; - setCallback(NULL, NULL); - } + struct quit_notify { server_packet_type type; uint16_t reason; }; + const auto quit = quit_notify{server_packet_type::QUIT, (uint16_t)disconnect_reason::UNEXPECTED}; + onPacket(memory::read_view((const uint8_t*)&quit, sizeof(quit)), PacketFlags::DATAGRAM); + disconnect(); break; } case picoquic_callback_version_negotiation: diff --git a/src/client/net/Client.hpp b/src/client/net/Client.hpp index 60fd1cc..3993ed8 100644 --- a/src/client/net/Client.hpp +++ b/src/client/net/Client.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../../core/net/Context.hpp" +#include "core/net/Context.hpp" #include @@ -16,13 +16,14 @@ enum queue: uint8_t { class Client final: public Context, public Connection { public: Client(const address& ct, - std::function onPacket); + std::function onPacket); ~Client(); bool isRunning() const override { return !disconnected; } /// Read-write on sockets and notify callbacks void pull() { Context::pull(1000, 50, 20); } + void disconnect() { disconnected = true; } int connectionCallback(uint64_t stream_id, uint8_t *bytes, size_t length, picoquic_call_back_event_t fin_or_event, void *v_stream_ctx); @@ -31,7 +32,7 @@ private: bool connected = false; bool disconnected = false; - std::function onPacket; + std::function onPacket; }; } \ No newline at end of file diff --git a/src/client/render/Renderer.hpp b/src/client/render/Renderer.hpp index af1531f..6b39b5f 100644 --- a/src/client/render/Renderer.hpp +++ b/src/client/render/Renderer.hpp @@ -1,7 +1,7 @@ #pragma once -#include "../../core/flags.hpp" -#include "../../core/world/actions.hpp" +#include "core/utils/flags.hpp" +#include "core/world/actions.hpp" #include #include #include @@ -76,13 +76,15 @@ public: /// Start new frame and setup virtual void beginFrame() = 0; - /// Get started world program + /// Get started terrain program /// (vertex buffer, model matrix, sphereProj, curvature) - virtual std::function beginWorldPass(bool solid) = 0; - /// Get started entity program - virtual std::function &)> beginEntityPass() = 0; + virtual std::function beginTerrainPass(bool solid) = 0; + /// Get started unique entity program + virtual std::function beginUniquePass() = 0; + /// Get started instanced entity program + virtual std::function&)> beginInstancedPass() = 0; /// Draw line indicator (model, shape, color) - virtual std::function beginIndicatorPass() = 0; + virtual std::function beginIndicatorPass() = 0; /// Apply postprocessing virtual void postProcess() = 0; /// Finalise frame diff --git a/src/client/render/UI.cpp b/src/client/render/UI.cpp index 11ef5d6..af77d57 100644 --- a/src/client/render/UI.cpp +++ b/src/client/render/UI.cpp @@ -1,9 +1,11 @@ #include "UI.hpp" #include +#include #include "../state.hpp" -#include "../Window.hpp" -#include "../../core/world/materials.hpp" +#include "Window.hpp" +#include "core/world/Elements.hpp" +#include "version.h" #include using namespace render; @@ -27,8 +29,7 @@ UI::UI() { style.WindowMenuButtonPosition = ImGuiDir_Right; // Purple dark theme - /*ImVec4* colors = style.Colors; - colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); + /*colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_TextDisabled] = ImVec4(0.56f, 0.56f, 0.56f, 1.00f); colors[ImGuiCol_WindowBg] = ImVec4(0.06f, 0.06f, 0.06f, 0.94f); colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); @@ -87,10 +88,130 @@ UI::~UI() { ImGui::DestroyContext(); } +UI::Actions drawCommon(config::client::options &options, state::state &state, const std::vector& packs); +UI::Actions drawMenu(config::client::options &options, state::state &state, const std::vector& packs); +UI::Actions drawInGame(config::client::options &options, state::state &state, const state::reports &reports, intptr_t aim, const std::vector& packs); + UI::Actions UI::draw(config::client::options &options, state::state &state, const state::reports &reports, intptr_t aim) { - auto actions = Actions::None; + Actions actions = Actions::None; ImGui::NewFrame(); + if (state.playing) { + actions = drawInGame(options, state, reports, aim, texturePacks); + } else { + actions = drawMenu(options, state, texturePacks); + } + + ImGui::Render(); + return actions; +} + +bool UI::IsFocus() { + return ImGui::IsAnyItemActive(); +} + +void UI::Unload() { + delete sInstance; + sInstance = nullptr; +} + +UI::Actions drawMenu(config::client::options &options, state::state &state, const std::vector& packs) { + const ImGuiIO &io = ImGui::GetIO(); + + if (options.overlay.visible) { + if (options.overlay.corner != -1) { + ImVec2 window_pos = ImVec2((options.overlay.corner & 1) ? io.DisplaySize.x - UI_MARGIN : UI_MARGIN, (options.overlay.corner & 2) ? io.DisplaySize.y - UI_MARGIN : UI_MARGIN); + ImVec2 window_pos_pivot = ImVec2((options.overlay.corner & 1) ? 1.0f : 0.0f, (options.overlay.corner & 2) ? 1.0f : 0.0f); + ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); + } + ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background + ImGui::Begin("Overlay", &options.overlay.visible, (options.overlay.corner != -1 ? ImGuiWindowFlags_NoMove : 0) | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav); + ImGui::Text("%.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + if (ImGui::BeginPopupContextWindow()) { + if (ImGui::MenuItem("Custom", NULL, options.overlay.corner == -1)) + options.overlay.corner = -1; + if (ImGui::MenuItem("Top-left", NULL, options.overlay.corner == 0)) + options.overlay.corner = 0; + if (ImGui::MenuItem("Top-right", NULL, options.overlay.corner == 1)) + options.overlay.corner = 1; + if (ImGui::MenuItem("Bottom-left", NULL, options.overlay.corner == 2)) + options.overlay.corner = 2; + if (ImGui::MenuItem("Bottom-right", NULL, options.overlay.corner == 3)) + options.overlay.corner = 3; + if (options.overlay.visible && ImGui::MenuItem("Close")) + options.overlay.visible = false; + ImGui::EndPopup(); + } + ImGui::End(); + } + + auto actions = drawCommon(options, state, packs); + + ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x / 2, io.DisplaySize.y / 2), ImGuiCond_Always, ImVec2(.5f, .5f)); + ImGui::Begin((std::string("Univerxel ") + UNIVERXEL_VERSION).c_str(), NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoCollapse); + state.login.name.resize(128); + state.login.token.resize(1024); + if (ImGui::InputText("Login", state.login.name.data(), state.login.name.size()-1, ImGuiInputTextFlags_EnterReturnsTrue)) + ImGui::SetKeyboardFocusHere(); + const auto tryPlay = ImGui::InputText("Token", state.login.token.data(), state.login.token.size() - 1, ImGuiInputTextFlags_Password | ImGuiInputTextFlags_EnterReturnsTrue); + ImGui::Separator(); + bool useLocal = !options.connection.has_value(); + if (state.srvContainer) { + ImGui::Columns(2, NULL, false); + if (ImGui::Checkbox("Use local", &useLocal)) { + options.connection = !useLocal ? std::make_optional(world::client::Universe::connection{}) : std::nullopt; + } + ImGui::NextColumn(); + if (!useLocal) { + ImGui::Checkbox("Run server", &state.useSrv); + } + ImGui::Columns(); + } + if (!useLocal) { + ImGui::InputText("Host", &options.connection.value().host); + ImGui::InputInt("Port", &options.connection.value().port); + } + { + const auto canPlay = std::string::traits_type::length(state.login.name.data()) > 0; + if (!canPlay) + ImGui::PushStyleVarDisabled(); + if ((ImGui::Button("Play", ImVec2(ImGui::GetWindowContentRegionWidth(), 30)) || tryPlay) && canPlay) { + state.login.name.resize(std::string::traits_type::length(state.login.name.data())); + state.login.token.resize(std::string::traits_type::length(state.login.token.data())); + state.playing = true; + } + if (!canPlay) + ImGui::PopStyleVar(); + } + ImGui::Separator(); + if (ImGui::CollapsingHeader("Options")) { + ImGui::Columns(3, NULL, false); + ImGui::Checkbox("Render", &options.debugMenu.render); + ImGui::NextColumn(); + ImGui::Checkbox("World", &options.debugMenu.world); + ImGui::NextColumn(); + ImGui::Checkbox("Console", &options.console.visible); + ImGui::Columns(); + } + if (ImGui::Button("Quit", ImVec2(ImGui::GetWindowContentRegionWidth(), 0))) { + actions |= UI::Actions::Quit; + } + ImGui::End(); + + return actions; +} + +#define ABS_POS_FMT "(%.2Lf, %.2Lf, %.2Lf)" +#define IDX_FMT "%d(%u, %u)" +#define REL_POS_FMT "(" IDX_FMT ": %.2Lf, %.2Lf, %.2Lf)" +#define REL_CPOS_FMT "(" IDX_FMT ": %lld, %lld, %lld)" +#define POS_VAL(v) v.x, v.y, v.z +#define ABS_POS_VAL(tf) POS_VAL(tf.position) +#define IDX_VAL(i) i.val.index(), i.val.generation(), i.val.flag() +#define REL_POS_VAL(rtf) IDX_VAL(rtf.parent), POS_VAL(rtf.relative.position) + +UI::Actions drawInGame(config::client::options &options, state::state &state, const state::reports &reports, intptr_t aim, const std::vector& texturePacks) { + UI::Actions actions = UI::Actions::None; const ImGuiIO &io = ImGui::GetIO(); if(state.capture_mouse) { @@ -117,120 +238,10 @@ UI::Actions UI::draw(config::client::options &options, state::state &state, cons ImGui::EndMainMenuBar(); } - if (options.debugMenu.render) { - ImGui::Begin("Debug: Render", &options.debugMenu.render, ImGuiWindowFlags_AlwaysAutoResize); - ImGui::Checkbox("Overlay", &options.overlay.visible); - { - ImGui::Combo("Driver", (int*)&options.preferVulkan, "OpenGL\0Vulkan\0"); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("Prefer Vulkan over OpenGL if available (requires restart)"); - auto samples = std::to_string(options.window.getSamples()) + "x"; - ImGui::SliderInt("MSAA", &options.window.sampling, -1, 7, options.window.sampling > 0 ? samples.c_str() : - (options.window.sampling < 0 ? "System default" : "Minimun")); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("Anti-aliasing (requires restart).\nActual value may be smaller than requested"); - - ImGui::Separator(); - } - - if (ImGui::SliderInt("FPS", &options.window.targetFPS, Window::MIN_FPS-1, Window::MAX_FPS+1, options.window.targetFPS > Window::MIN_FPS ? (options.window.targetFPS < Window::MAX_FPS ? "%d" : "UNLIMITED") : "VSYNC")){ - actions |= Actions::FPS; - } -#ifdef _WINDOWS - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("Due to windows clock inaccuracy prefer vsync or unlimited."); -#endif - - if (ImGui::Checkbox("Fullscreen", &options.window.fullscreen)){ - actions |= Actions::FullScreen; - } - - { - bool changeRenderer = false; - { - int planarIdx = static_cast(options.renderer.voxel.planar) - 1; - changeRenderer |= ImGui::Combo("Mapping", &planarIdx, "Cheap\0Biplanar\0Triplanar\0"); - options.renderer.voxel.planar = static_cast(planarIdx + 1); - } - changeRenderer |= ImGui::Checkbox("PBR", &options.renderer.voxel.pbr); - ImGui::SameLine(); - changeRenderer |= ImGui::Checkbox("Stochastic", &options.renderer.voxel.stochastic); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("Hide textures tiling\nMay cause visible inconsistances on chunk borders"); - - changeRenderer |= ImGui::Checkbox("Geometry", &options.renderer.voxel.geometry); - ImGui::SameLine(); - if(options.renderer.voxel.geometry) { - changeRenderer |= ImGui::Checkbox("Blend", &options.renderer.voxel.blend); - } else { - ImGui::TextDisabled("Blend"); - } - - changeRenderer |= ImGui::Checkbox("Curvature", &options.renderer.voxel.curvature); - ImGui::SameLine(); - if(options.renderer.voxel.curvature) { - changeRenderer |= ImGui::Checkbox("Depth", &options.renderer.voxel.curv_depth); - if(ImGui::IsItemHovered()) - ImGui::SetTooltip("Planets may look way bigger than expected without it"); - } else { - ImGui::TextDisabled("Depth"); - } - - changeRenderer |= ImGui::Checkbox("Transparency", &options.renderer.voxel.transparency); - - changeRenderer |= ImGui::Checkbox("Fog", &options.renderer.voxel.fog); - ImGui::SameLine(); - ImGui::Checkbox("Skybox", &options.renderer.skybox); - if (changeRenderer) { - actions |= Actions::RendererSharders; - } - } - if (ImGui::ColorEdit3("Fog color", &options.renderer.clear_color[0])) { - actions |= Actions::ClearColor; - } - if (ImGui::Checkbox("Wireframe", &options.renderer.wireframe)) { - actions |= Actions::FillMode; - } - if (ImGui::BeginCombo("Textures", options.renderer.textures.c_str())) { - for (auto& pack: texturePacks) { - const bool is_selected = (pack == options.renderer.textures); - if (ImGui::Selectable(pack.c_str(), is_selected)) { - options.renderer.textures = pack; - actions |= Actions::RendererTextures; - } - if (is_selected) - ImGui::SetItemDefaultFocus(); - } - ImGui::EndCombo(); - } - std::string anisotropy = std::to_string(options.renderer.getAnisotropy()) + "x"; - if (ImGui::SliderInt("Quality", &options.renderer.textureQuality, 0, 200, "%d%%") | - ImGui::SliderInt("Sharpness", &options.renderer.textureSharpness, 0, 8, anisotropy.c_str())) { - actions |= Actions::RendererTextures; - } - if(ImGui::IsItemHovered()) - ImGui::SetTooltip("Better texture quality especially at low angles"); - ImGui::End(); - } - - if (options.debugMenu.world) { - ImGui::Begin("Debug: World", &options.debugMenu.world, ImGuiWindowFlags_AlwaysAutoResize); - int load = options.world.loadDistance; - int keep = options.world.keepDistance; - if (ImGui::SliderInt("Load distance", &load, 1, options.world.keepDistance) | - ImGui::SliderInt("Keep distance", &keep, options.world.loadDistance + 1, 21)) { - options.world.loadDistance = load; - options.world.keepDistance = keep; - actions |= Actions::World; - } - if(ImGui::SliderInt("Voxel density", &options.voxel_density, 1, CHUNK_LENGTH * REGION_LENGTH)) { - options.voxel_density = pow(2, ceil(log(options.voxel_density) / log(2))); - } - ImGui::End(); - } + actions |= drawCommon(options, state, texturePacks); if (options.debugMenu.contouring) { - ImGui::Begin("Debug: Contouring", &options.debugMenu.contouring, ImGuiWindowFlags_AlwaysAutoResize); + ImGui::Begin("Options: Contouring", &options.debugMenu.contouring, ImGuiWindowFlags_AlwaysAutoResize); ImGui::SliderInt("Culling", &options.culling, -1, 10, options.culling > 0 ? "Occlusion %d" : (options.culling < 0 ? "None" : "Frustum")); state.contouring->onGui(); ImGui::End(); @@ -240,13 +251,14 @@ UI::Actions UI::draw(config::client::options &options, state::state &state, cons const auto farRange = state.contouring->getFarRange(); if (options.debugMenu.controls) { ImGui::Begin("Debug: Controls", &options.debugMenu.controls, ImGuiWindowFlags_AlwaysAutoResize); - const auto p = state.position.as_voxel(options.voxel_density); - ImGui::Text("Position: (%lld, %lld, %lld)", p.x, p.y, p.z); + //MAYBE: add rotation + ImGui::Text("Position: " ABS_POS_FMT "\n" REL_POS_FMT, + ABS_POS_VAL(state.position.absolute), REL_POS_VAL(state.position.relative)); ImGui::Separator(); { if (ImGui::SliderFloat("Move speed", &options.control.speed, 0.1, 50) | ImGui::SliderInt("Sensibility", &options.control.sensibility, 1, 100, "%d%%")) { - actions |= Actions::Control; + actions |= UI::Actions::Control; } ImGui::Checkbox("Collide", &options.control.collide); } @@ -255,37 +267,39 @@ UI::Actions UI::draw(config::client::options &options, state::state &state, cons bool changePerspective = false; changePerspective |= ImGui::SliderAngle("FoV", &options.camera.fov, 30, 110); changePerspective |= ImGui::SliderFloat("Near", &options.camera.near_dist, 0.01, 10); - changePerspective |= ImGui::SliderFloat("Far", &options.camera.far_dist, farRange.first / options.voxel_density, farRange.second / options.voxel_density); + changePerspective |= ImGui::SliderFloat("Far", &options.camera.far_dist, farRange.first, farRange.second); if(changePerspective) { - actions |= Actions::Camera; + actions |= UI::Actions::Camera; } } ImGui::End(); } - const auto far_dist = std::clamp(options.camera.far_dist, farRange.first / options.voxel_density, farRange.second / options.voxel_density); + const auto far_dist = std::clamp(options.camera.far_dist, farRange.first, farRange.second); if(far_dist != options.camera.far_dist) { options.camera.far_dist = far_dist; - actions |= Actions::Camera; + actions |= UI::Actions::Camera; } } if (options.editor.visible) { ImGui::Begin("Editor", &options.editor.visible, ImGuiWindowFlags_AlwaysAutoResize); - if (state.look_at.has_value()) { - const auto &look = state.look_at.value(); - ImGui::Text("Look at: (%ld: %lld, %lld, %lld) (%s, %.1f)", look.pos.first.index, look.pos.second.x, look.pos.second.y, look.pos.second.z, - world::materials::names[look.value.material()].c_str(), look.value.density_ratio()); - const auto w_pos = look.pos.second + look.offset; - ImGui::Text("(%.3f, %.3f, %.3f)", w_pos.x * 1. / options.voxel_density, w_pos.y * 1. / options.voxel_density, w_pos.z * 1. / options.voxel_density); + if (const auto voxel = std::get_if(&state.look_at)) { + ImGui::Text("Look at: voxel (%s, %.1f)\n" REL_CPOS_FMT, + voxel->value.name().c_str(), voxel->value.density_ratio(), + IDX_VAL(voxel->pos.node), POS_VAL(voxel->pos.voxel)); + } else if (const auto element = std::get_if(&state.look_at)) { + //TODO: if world::Elements::Is(element->parent) get model_id; + ImGui::Text("Look at: entity " IDX_FMT "\n" ABS_POS_FMT, + IDX_VAL(element->parent), POS_VAL(element->position)); } else { ImGui::Text("Look at: none"); } ImGui::Separator(); - if (ImGui::BeginCombo("Material", world::materials::names[options.editor.tool.material].c_str())) { - for (size_t i = 0; i < world::materials::names.size(); i++) { - if (!world::materials::invisibility[i]) { + if (ImGui::BeginCombo("Material", world::Voxel::Name(options.editor.tool.material).c_str())) { + for (size_t i = 0; i < world::module::Registry::Get()->getMaterials().names.size(); i++) { + if (world::Voxel::IsVisible(i)) { const bool is_selected = (options.editor.tool.material == i); - if (ImGui::Selectable(world::materials::names[i].c_str(), is_selected)) + if (ImGui::Selectable(world::Voxel::Name(i).c_str(), is_selected)) options.editor.tool.material = i; if (is_selected) ImGui::SetItemDefaultFocus(); @@ -313,11 +327,199 @@ UI::Actions UI::draw(config::client::options &options, state::state &state, cons ImGui::End(); } + if (options.overlay.visible) { + if (options.overlay.corner != -1) { + ImVec2 window_pos = ImVec2((options.overlay.corner & 1) ? io.DisplaySize.x - UI_MARGIN : UI_MARGIN, (options.overlay.corner & 2) ? io.DisplaySize.y - UI_MARGIN : UI_MARGIN); + ImVec2 window_pos_pivot = ImVec2((options.overlay.corner & 1) ? 1.0f : 0.0f, (options.overlay.corner & 2) ? 1.0f : 0.0f); + ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); + } + ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background + ImGui::Begin("Overlay", &options.overlay.visible, (options.overlay.corner != -1 ? ImGuiWindowFlags_NoMove : 0) | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav); + ImGui::Text("%.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + ImGui::Text("%ld tris(%ld models)", reports.tris_count, reports.models_count); + if (ImGui::BeginPopupContextWindow()) { + if (ImGui::MenuItem("Custom", NULL, options.overlay.corner == -1)) + options.overlay.corner = -1; + if (ImGui::MenuItem("Top-left", NULL, options.overlay.corner == 0)) + options.overlay.corner = 0; + if (ImGui::MenuItem("Top-right", NULL, options.overlay.corner == 1)) + options.overlay.corner = 1; + if (ImGui::MenuItem("Bottom-left", NULL, options.overlay.corner == 2)) + options.overlay.corner = 2; + if (ImGui::MenuItem("Bottom-right", NULL, options.overlay.corner == 3)) + options.overlay.corner = 3; + if (options.overlay.visible && ImGui::MenuItem("Close")) + options.overlay.visible = false; + ImGui::EndPopup(); + } + ImGui::End(); + } + + return actions; +} + +UI::Actions drawCommon(config::client::options &options, state::state& state, const std::vector& texturePacks) { + UI::Actions actions = UI::Actions::None; + + if (options.debugMenu.render) { + ImGui::Begin("Options: Render", &options.debugMenu.render, ImGuiWindowFlags_AlwaysAutoResize); + ImGui::Checkbox("Overlay", &options.overlay.visible); + { +#ifdef RENDER_VK + ImGui::Combo("Driver", (int*)&options.preferVulkan, "OpenGL\0Vulkan\0"); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("Vulkan driver is unrecommended and WIP (requires restart)"); +#endif + auto samples = std::to_string(options.window.getSamples()) + "x"; + ImGui::SliderInt("MSAA", &options.window.sampling, -1, 7, options.window.sampling > 0 ? samples.c_str() : + (options.window.sampling < 0 ? "System default" : "Minimun")); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("Anti-aliasing (requires restart).\nActual value may be smaller than requested"); + + ImGui::Separator(); + } + + if (ImGui::SliderInt("FPS", &options.window.targetFPS, Window::MIN_FPS-1, Window::MAX_FPS+1, options.window.targetFPS > Window::MIN_FPS ? (options.window.targetFPS < Window::MAX_FPS ? "%d" : "UNLIMITED") : "VSYNC")){ + actions |= UI::Actions::FPS; + } +#ifdef _WINDOWS + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("Due to windows clock inaccuracy prefer vsync or unlimited."); +#endif + + if (ImGui::Checkbox("Fullscreen", &options.window.fullscreen)){ + actions |= UI::Actions::FullScreen; + } + + { + bool changeRenderer = false; + { + int planarIdx = static_cast(options.renderer.voxel.planar) - 1; + changeRenderer |= ImGui::Combo("Mapping", &planarIdx, "Cheap\0Biplanar\0Triplanar\0"); + options.renderer.voxel.planar = static_cast(planarIdx + 1); + } + ImGui::Columns(2, NULL, false); + changeRenderer |= ImGui::Checkbox("PBR", &options.renderer.voxel.pbr); + ImGui::NextColumn(); + changeRenderer |= ImGui::Checkbox("Stochastic", &options.renderer.voxel.stochastic); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("Hide textures tiling\nMay cause visible inconsistances on chunk borders"); + ImGui::NextColumn(); + + changeRenderer |= ImGui::Checkbox("Geometry", &options.renderer.voxel.geometry); + ImGui::NextColumn(); + if (options.renderer.voxel.geometry) { + changeRenderer |= (ImGui::Checkbox("Blend", &options.renderer.voxel.blend) && options.renderer.voxel.geometry); + } else { + ImGui::TextDisabled("Blend"); + } + ImGui::NextColumn(); + + changeRenderer |= ImGui::Checkbox("Curvature", &options.renderer.voxel.curvature); + ImGui::NextColumn(); + if (options.renderer.voxel.curvature) { + changeRenderer |= ImGui::Checkbox("Depth", &options.renderer.voxel.curv_depth); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("Planets may look way bigger than expected without it"); + } else { + ImGui::TextDisabled("Depth"); + } + ImGui::NextColumn(); + + changeRenderer |= ImGui::Checkbox("Transparency", &options.renderer.voxel.transparency); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("Requires transparency option in Contouring"); + ImGui::NextColumn(); + ImGui::NextColumn(); + + changeRenderer |= ImGui::Checkbox("Fog", &options.renderer.voxel.fog); + ImGui::NextColumn(); + changeRenderer |= ImGui::Checkbox("Skybox", &options.renderer.skybox); + if (changeRenderer) { + actions |= UI::Actions::RendererSharders; + } + ImGui::Columns(); + } + if (ImGui::ColorEdit3("Fog color", &options.renderer.clear_color[0])) { + actions |= UI::Actions::ClearColor; + } + if (ImGui::Checkbox("Wireframe", &options.renderer.wireframe)) { + actions |= UI::Actions::FillMode; + } + if (ImGui::BeginCombo("Textures", options.renderer.textures.c_str())) { + for (auto& pack: texturePacks) { + const bool is_selected = (pack == options.renderer.textures); + if (ImGui::Selectable(pack.c_str(), is_selected)) { + options.renderer.textures = pack; + actions |= UI::Actions::RendererTextures; + } + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + std::string anisotropy = std::to_string(options.renderer.getAnisotropy()) + "x"; + if (ImGui::SliderInt("Quality", &options.renderer.textureQuality, 0, 200, "%d%%") | + ImGui::SliderInt("Sharpness", &options.renderer.textureSharpness, 0, 8, anisotropy.c_str())) { + actions |= UI::Actions::RendererTextures; + } + if(ImGui::IsItemHovered()) + ImGui::SetTooltip("Better texture quality especially at low angles"); + ImGui::End(); + } + + if (options.debugMenu.world) { + ImGui::Begin("Options: World", &options.debugMenu.world, ImGuiWindowFlags_AlwaysAutoResize); + const auto runServer = state.srvContainer && (!options.connection || state.useSrv); + if (runServer) { + state.srvContainer->onGui(); + } + bool useNet = options.connection.has_value(); + if (runServer && useNet) { + useNet = ImGui::CollapsingHeader("Local", ImGuiTreeNodeFlags_DefaultOpen); + } + if (useNet) { + { + int load = options.world.loadDistance; + int keep = options.world.keepDistance; + if (ImGui::SliderInt("Load distance", &load, 1, options.world.keepDistance) | + ImGui::SliderInt("Keep distance", &keep, options.world.loadDistance + 1, 21)) { + options.world.loadDistance = load; + options.world.keepDistance = keep; + actions |= UI::Actions::World; + } + } + ImGui::Columns(2, NULL, false); + ImGui::Checkbox("Use averages", &options.world.useAverages); + ImGui::NextColumn(); + ImGui::Checkbox("Trust majorant", &options.world.trustMajorant); + ImGui::NextColumn(); + ImGui::Checkbox("Edit prediction", &options.world.editPrediction); + ImGui::NextColumn(); + ImGui::Checkbox("Edit handling", &options.world.editHandling); + ImGui::NextColumn(); + ImGui::Checkbox("Move prediction", &options.world.movePrediction); + ImGui::NextColumn(); + ImGui::Checkbox("Move collision", &options.world.moveCollision); + ImGui::Columns(); + } + if (ImGui::CollapsingHeader("Indicators")) { // MAYBE: move to Debug: Indicators + ImGui::Columns(3, NULL, false); + ImGui::Checkbox("OBB", &state.indicators.obb); + ImGui::NextColumn(); + ImGui::Checkbox("AABB", &state.indicators.aabb); + ImGui::NextColumn(); + ImGui::Checkbox("Chunks", &state.indicators.chunk); + ImGui::Columns(); + } + ImGui::End(); + } + if (options.console.visible) { { const auto SIZE = ImVec2(300, 200); ImGui::SetNextWindowSize(SIZE, ImGuiCond_FirstUseEver); - ImGui::SetNextWindowPos(ImVec2(UI_MARGIN, io.DisplaySize.y - SIZE.y - UI_MARGIN), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowPos(ImVec2(UI_MARGIN, ImGui::GetIO().DisplaySize.y - SIZE.y - UI_MARGIN), ImGuiCond_FirstUseEver); } ImGui::PushStyleVar(ImGuiStyleVar_Alpha, options.console.opacity); ImGui::Begin("Console", &options.console.visible, ImGuiWindowFlags_MenuBar); @@ -328,7 +530,7 @@ UI::Actions UI::draw(config::client::options &options, state::state &state, cons ImGui::EndMenu(); } if (ImGui::Button("Clear")) - state.console.lines.clear(); + state.console.lines.fill(state::state::line{}); const bool copy_to_clipboard = ImGui::Button("Copy"); ImGui::EndMenuBar(); @@ -366,7 +568,9 @@ UI::Actions UI::draw(config::client::options &options, state::state &state, cons ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1)); // Tighten spacing if (copy_to_clipboard) ImGui::LogToClipboard(); - for (const auto& item: state.console.lines) { + state.console.lines.iter([&](const state::state::line& item) { + if (item.text.empty()) + return; /*if (!Filter.PassFilter(item)) continue;*/ @@ -375,7 +579,7 @@ UI::Actions UI::draw(config::client::options &options, state::state &state, cons ImGui::TextUnformatted(item.text.c_str()); if (item.color.has_value()) ImGui::PopStyleColor(); - } + }); if (copy_to_clipboard) ImGui::LogFinish(); @@ -391,53 +595,20 @@ UI::Actions UI::draw(config::client::options &options, state::state &state, cons // | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory; ImGui::GetColumnWidth(); if (ImGui::InputText("", state.console.buffer.data(), state.console.buffer.size(), flags /*TODO: completion (, callback, context)*/) && strlen(state.console.buffer.data()) > 0) - actions |= Actions::Message; + actions |= UI::Actions::Message; // Auto-focus on window apparition ImGui::SetItemDefaultFocus(); - if (actions && Actions::Message) + if (actions && UI::Actions::Message) ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget ImGui::End(); ImGui::PopStyleVar(); } - if (options.overlay.visible) { - if (options.overlay.corner != -1) { - ImVec2 window_pos = ImVec2((options.overlay.corner & 1) ? io.DisplaySize.x - UI_MARGIN : UI_MARGIN, (options.overlay.corner & 2) ? io.DisplaySize.y - UI_MARGIN : UI_MARGIN); - ImVec2 window_pos_pivot = ImVec2((options.overlay.corner & 1) ? 1.0f : 0.0f, (options.overlay.corner & 2) ? 1.0f : 0.0f); - ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); - } - ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background - ImGui::Begin("Overlay", &options.overlay.visible, (options.overlay.corner != -1 ? ImGuiWindowFlags_NoMove : 0) | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav); - ImGui::Text("%.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); - ImGui::Text("%ld tris(%ld models)", reports.tris_count, reports.models_count); - if (ImGui::BeginPopupContextWindow()) { - if (ImGui::MenuItem("Custom", NULL, options.overlay.corner == -1)) - options.overlay.corner = -1; - if (ImGui::MenuItem("Top-left", NULL, options.overlay.corner == 0)) - options.overlay.corner = 0; - if (ImGui::MenuItem("Top-right", NULL, options.overlay.corner == 1)) - options.overlay.corner = 1; - if (ImGui::MenuItem("Bottom-left", NULL, options.overlay.corner == 2)) - options.overlay.corner = 2; - if (ImGui::MenuItem("Bottom-right", NULL, options.overlay.corner == 3)) - options.overlay.corner = 3; - if (options.overlay.visible && ImGui::MenuItem("Close")) - options.overlay.visible = false; - ImGui::EndPopup(); - } - ImGui::End(); - } - ImGui::Render(); return actions; } -bool UI::IsFocus() { - return ImGui::IsAnyItemActive(); -} - -void UI::Unload() { - delete sInstance; - sInstance = nullptr; +void ImGui::PushStyleVarDisabled() { + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f); } \ No newline at end of file diff --git a/src/client/render/UI.hpp b/src/client/render/UI.hpp index 0e9775c..c3d70f1 100644 --- a/src/client/render/UI.hpp +++ b/src/client/render/UI.hpp @@ -4,7 +4,7 @@ #include #include #include -#include "../../core/flags.hpp" +#include "core/utils/flags.hpp" namespace config::client { struct options; } namespace state { @@ -38,6 +38,7 @@ public: FillMode = 1 << 8, Message = 1 << 9, Transparency = 1 << 10, + Quit = 1 << 11 }; friend inline void operator|=(Actions& a, Actions b) { a = static_cast(static_cast(a) | static_cast(b)); @@ -67,4 +68,8 @@ protected: static UI* sInstance; }; -}; +} + +namespace ImGui { + void PushStyleVarDisabled(); +} diff --git a/src/client/Window.cpp b/src/client/render/Window.cpp similarity index 75% rename from src/client/Window.cpp rename to src/client/render/Window.cpp index a10e389..b70fbc2 100644 --- a/src/client/Window.cpp +++ b/src/client/render/Window.cpp @@ -5,9 +5,12 @@ #include #include #include -#include "../core/utils/logger.hpp" +#include "core/utils/logger.hpp" #include "logo.cxx" +#undef LOG_PREFIX +#define LOG_PREFIX "GLFW: " + /// Default floating window width constexpr auto DEFAULT_WIDTH = 1280; /// Default floating window height @@ -19,14 +22,14 @@ constexpr auto MIN_HEIGHT = 480; constexpr auto APP_NAME = "Univerxel"; static void glfw_error_callback(int error, const char* description) { - LOG_E("[GLFW] " << error << ": " << description); + LOG_E(error << ": " << description); } Window::Window() : ptr(nullptr), targetFPS(60) { glfwSetErrorCallback(glfw_error_callback); if (!glfwInit()) { - FATAL("Failed to initialize GLFW"); + FATAL("Failed to initialize"); } } Window::~Window() { @@ -40,22 +43,16 @@ bool Window::create(const CreateInfo &opt) { } glfwWindowHint(GLFW_SAMPLES, opt.samples); - switch (opt.client.type) { - case CreateInfo::Client::Type::GL: - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, opt.client.major); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, opt.client.minor); - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed + if (opt.client) { + const auto &gl = opt.client.value(); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, gl.major); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, gl.minor); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed + if (gl.core) glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - break; - - case CreateInfo::Client::Type::VK: - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - break; - - default: - FATAL("Unsupported client type"); - break; + } else { + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); } #if FIXED_WINDOW @@ -64,15 +61,12 @@ bool Window::create(const CreateInfo &opt) { ptr = glfwCreateWindow(DEFAULT_WIDTH, DEFAULT_HEIGHT, APP_NAME, NULL, NULL); if (ptr == NULL) { - LOG_E("Failed to open GLFW window"); + LOG_E("Failed to open window"); return false; } - if (opt.client.type == CreateInfo::Client::Type::GL) + if (opt.client) glfwMakeContextCurrent(ptr); - // Hide the mouse and enable unlimited mouvement - // glfwSetInputMode(ptr, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - // Set the mouse at the center of the screen // TODO: move to input manager glfwPollEvents(); @@ -121,6 +115,7 @@ void Window::destroy() { ptr = nullptr; } +void Window::close() { glfwSetWindowShouldClose(ptr, GLFW_TRUE); } bool Window::shouldClose() { return glfwWindowShouldClose(ptr); } void Window::setFullscreen(bool val) { diff --git a/src/client/Window.hpp b/src/client/render/Window.hpp similarity index 91% rename from src/client/Window.hpp rename to src/client/render/Window.hpp index a62205d..7354c33 100644 --- a/src/client/Window.hpp +++ b/src/client/render/Window.hpp @@ -1,6 +1,7 @@ #pragma once -#include "../core/flags.hpp" +#include "core/utils/flags.hpp" +#include #include typedef struct GLFWwindow GLFWwindow; @@ -23,10 +24,11 @@ public: struct CreateInfo { GLFWframebuffersizefun pfnResize; struct Client { - enum class Type { GL, VK } type; int major; int minor; - } client; + bool core; + }; + std::optional client; int samples; int fps; bool fullscreen; @@ -43,6 +45,7 @@ public: void startFrame(); void waitTargetFPS(); + void close(); bool shouldClose(); double getTime() const; diff --git a/src/client/render/api/Models.hpp b/src/client/render/api/Models.hpp index f83db7e..8417a5b 100644 --- a/src/client/render/api/Models.hpp +++ b/src/client/render/api/Models.hpp @@ -69,6 +69,7 @@ public: std::vector vertices; Data() { } + Data(Data&& data): indices(std::move(data.indices)), vertices(std::move(data.vertices)) { } Data(const std::vector &vertices, const indices_t &indices); Data(const std::vector &vertices) { index(vertices); } Data(std::istream &in); diff --git a/src/client/render/api/common.hpp b/src/client/render/api/common.hpp index d0c371c..595829d 100644 --- a/src/client/render/api/common.hpp +++ b/src/client/render/api/common.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../../../core/flags.hpp" +#include "core/utils/flags.hpp" #include #include diff --git a/src/client/render/gl/pass/WorldProgram.cpp b/src/client/render/gl/pass/WorldProgram.cpp deleted file mode 100644 index 26fbddb..0000000 --- a/src/client/render/gl/pass/WorldProgram.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "WorldProgram.hpp" - -#include "../Renderer.hpp" - -using namespace pass; - -WorldProgram::WorldProgram(const WorldProgram::options& opts): VoxelProgram(opts) { - - ModelMatrixID = glGetUniformLocation(ProgramID, "Model"); -} - -WorldProgram::~WorldProgram() { } - -void WorldProgram::setup(glm::mat4 modelMatrix, glm::vec4 sphereProj, float curvature) { - setModel(&modelMatrix[0][0]); - VoxelProgram::setup(sphereProj, curvature); -} - -void WorldProgram::setModel(const GLfloat *matrix) { - glUniformMatrix4fv(ModelMatrixID, 1, GL_FALSE, matrix); -} diff --git a/src/client/render/gl/pass/WorldProgram.hpp b/src/client/render/gl/pass/WorldProgram.hpp deleted file mode 100644 index a7c4ec4..0000000 --- a/src/client/render/gl/pass/WorldProgram.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "VoxelProgram.hpp" - -namespace pass { - /// Unique voxels pass - class WorldProgram: public VoxelProgram { - public: - WorldProgram(const options &opts); - ~WorldProgram(); - - void setup(glm::mat4 modelMatrix, glm::vec4 sphereProj, float curvature); - - protected: - void setModel(const GLfloat *matrix); - - private: - GLuint ModelMatrixID; - }; -} \ No newline at end of file diff --git a/src/client/render/gl/Renderer.cpp b/src/client/render/impl/gl/Renderer.cpp similarity index 76% rename from src/client/render/gl/Renderer.cpp rename to src/client/render/impl/gl/Renderer.cpp index bdc1960..bc19206 100644 --- a/src/client/render/gl/Renderer.cpp +++ b/src/client/render/impl/gl/Renderer.cpp @@ -1,14 +1,16 @@ #include "Renderer.hpp" #include "UI.hpp" -#include "../../../core/world/materials.hpp" -#include "../../control/Camera.hpp" +#include "../../../control/Camera.hpp" #include "../../Window.hpp" -#include "../../../core/utils/logger.hpp" +#include "core/utils/logger.hpp" #include #include +#undef LOG_PREFIX +#define LOG_PREFIX "OpenGL: " + using namespace render::gl; constexpr auto GL_MAJOR = 4; @@ -47,7 +49,7 @@ void framebuffer_size_callback(GLFWwindow *, int width, int height) { bool Renderer::Load(Window& window, const render::renderOptions& opt, const windowOptions& windOpt) { Window::CreateInfo windowInfo; windowInfo.pfnResize = framebuffer_size_callback; - windowInfo.client = {Window::CreateInfo::Client::Type::GL, GL_MAJOR, GL_MINOR_MIN}; + windowInfo.client = {GL_MAJOR, GL_MINOR_MIN, true}; windowInfo.samples = windOpt.getSamples(); windowInfo.fps = windOpt.targetFPS; windowInfo.fullscreen = windOpt.fullscreen; @@ -61,13 +63,13 @@ bool Renderer::Load(Window& window, const render::renderOptions& opt, const wind } if (!gl3wIsSupported(GL_MAJOR, GL_MINOR_BEST)) { if (gl3wIsSupported(GL_MAJOR, GL_MINOR_MIN)) { - LOG_W("OpenGL " << GL_MAJOR << "." << GL_MINOR_BEST << " not supported. Some feature like texture sharpness may not work"); + LOG_W(GL_MAJOR << "." << GL_MINOR_BEST << " not supported. Some feature like texture sharpness may not work"); } else { - LOG_E("OpenGL " << GL_MAJOR << "." << GL_MINOR_MIN << " not supported"); + LOG_E(GL_MAJOR << "." << GL_MINOR_MIN << " not supported"); return false; } } - LOG_D("OpenGL " << glGetString(GL_VERSION) << ", GLSL " << glGetString(GL_SHADING_LANGUAGE_VERSION)); + LOG_D(glGetString(GL_VERSION) << ", GLSL " << glGetString(GL_SHADING_LANGUAGE_VERSION)); // Enable depth test glEnable(GL_DEPTH_TEST); @@ -108,18 +110,26 @@ void Renderer::beginFrame() { glEnable(GL_FRAMEBUFFER_SRGB); } -std::function Renderer::beginWorldPass(bool solid) { +std::function Renderer::beginTerrainPass(bool solid) { if (!solid) glEnable(GL_BLEND); - WorldPass->useIt(); - WorldPass->start(this); - return [&](render::LodModel *const buf, glm::mat4 model, glm::vec4 sph, float curv) { - WorldPass->setup(model, sph, curv); + TerrainPass->useIt(); + TerrainPass->start(this); + return [&](render::LodModel *const buf, const glm::mat4& model, const glm::vec4& sph, float curv) { + TerrainPass->setup(model, sph, curv); return dynamic_cast(buf)->draw(); }; } - -std::function &)> Renderer::beginEntityPass() { +std::function Renderer::beginUniquePass() { + //NOTE: assume solid terrain pass in use + //MAYBE: create specific pass without curvature + TerrainPass->unsetCurvature(); + return [&](render::Model *const buf, const glm::mat4 &model) { + TerrainPass->setup(model); + return dynamic_cast(buf)->draw(); + }; +} +std::function&)> Renderer::beginInstancedPass() { EntityPass->useIt(); EntityPass->start(this); return [&](render::Model *const buf, const std::vector &models) { @@ -127,10 +137,9 @@ std::function &)> Rend return dynamic_cast(buf)->drawInstanced(models.size()); }; } - -std::function Renderer::beginIndicatorPass() { +std::function Renderer::beginIndicatorPass() { IndicatorPass->useIt(); - return [&](glm::mat4 model, world::action::Shape shape, glm::vec4 color) { + return [&](const glm::mat4& model, world::action::Shape shape, const glm::vec4& color) { IndicatorPass->setup(this, model, color); return shape == world::action::Shape::Cube ? IndicatorCubeBuffer.draw(GL_LINES) : IndicatorSphereBuffer.draw(GL_LINES); @@ -153,7 +162,7 @@ void Renderer::swapBuffer(Window& w) { } void Renderer::reloadShaders(const pass::VoxelProgram::options& options) { - WorldPass = std::make_unique(options); + TerrainPass = std::make_unique(options); EntityPass = std::make_unique(options); } void Renderer::reloadTextures(const std::string& texturePath, float mipMapLOD, int anisotropy) { @@ -170,7 +179,9 @@ void Renderer::loadTextures(const std::string& texturePath, float mipMapLOD, int std::vector terrainTextures; auto makePaths = [&](const std::string& suffix) { terrainTextures.clear(); - for(const auto& texture: world::materials::textures) { + const auto& textures = world::module::Registry::Get()->getTextures(); + terrainTextures.reserve(textures.size()); + for(const auto& texture: textures) { terrainTextures.emplace_back(TEXTURES_DIR + texturePath + "/terrain/" + texture + suffix + ".dds"); } return terrainTextures; diff --git a/src/client/render/gl/Renderer.hpp b/src/client/render/impl/gl/Renderer.hpp similarity index 82% rename from src/client/render/gl/Renderer.hpp rename to src/client/render/impl/gl/Renderer.hpp index 85cdf1b..aa5f060 100644 --- a/src/client/render/gl/Renderer.hpp +++ b/src/client/render/impl/gl/Renderer.hpp @@ -1,8 +1,8 @@ #pragma once -#include "../Renderer.hpp" +#include "../../Renderer.hpp" #include #include -#include "pass/WorldProgram.hpp" +#include "pass/TerrainProgram.hpp" #include "pass/EntityProgram.hpp" #include "pass/SkyProgram.hpp" #include "pass/ColorProgram.hpp" @@ -44,9 +44,10 @@ public: } void beginFrame() override; - std::function beginWorldPass(bool solid) override; - std::function&)> beginEntityPass() override; - std::function beginIndicatorPass() override; + std::function beginTerrainPass(bool solid) override; + std::function beginUniquePass() override; + std::function&)> beginInstancedPass() override; + std::function beginIndicatorPass() override; void postProcess() override; void endFrame() override; void swapBuffer(Window&) override; @@ -69,7 +70,7 @@ private: GLuint VertexArrayID; - std::unique_ptr WorldPass; + std::unique_ptr TerrainPass; std::unique_ptr EntityPass; std::unique_ptr SkyPass; std::unique_ptr IndicatorPass; diff --git a/src/client/render/gl/UI.cpp b/src/client/render/impl/gl/UI.cpp similarity index 100% rename from src/client/render/gl/UI.cpp rename to src/client/render/impl/gl/UI.cpp diff --git a/src/client/render/gl/UI.hpp b/src/client/render/impl/gl/UI.hpp similarity index 94% rename from src/client/render/gl/UI.hpp rename to src/client/render/impl/gl/UI.hpp index faace9e..db0100c 100644 --- a/src/client/render/gl/UI.hpp +++ b/src/client/render/impl/gl/UI.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../UI.hpp" +#include "../../UI.hpp" #include "api/Images.hpp" struct GLFWwindow; class Window; diff --git a/src/client/render/gl/api/Images.cpp b/src/client/render/impl/gl/api/Images.cpp similarity index 99% rename from src/client/render/gl/api/Images.cpp rename to src/client/render/impl/gl/api/Images.cpp index 9c62e93..1f38ef2 100644 --- a/src/client/render/gl/api/Images.cpp +++ b/src/client/render/impl/gl/api/Images.cpp @@ -1,5 +1,5 @@ #include "Images.hpp" -#include "../../../../core/utils/logger.hpp" +#include "core/utils/logger.hpp" #include #include #include diff --git a/src/client/render/gl/api/Images.hpp b/src/client/render/impl/gl/api/Images.hpp similarity index 96% rename from src/client/render/gl/api/Images.hpp rename to src/client/render/impl/gl/api/Images.hpp index 26ceabe..e6323fe 100644 --- a/src/client/render/gl/api/Images.hpp +++ b/src/client/render/impl/gl/api/Images.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../../api/Images.hpp" +#include "../../../api/Images.hpp" #include namespace render::gl { diff --git a/src/client/render/gl/api/Models.cpp b/src/client/render/impl/gl/api/Models.cpp similarity index 99% rename from src/client/render/gl/api/Models.cpp rename to src/client/render/impl/gl/api/Models.cpp index c39c1bd..beb6f6e 100644 --- a/src/client/render/gl/api/Models.cpp +++ b/src/client/render/impl/gl/api/Models.cpp @@ -121,7 +121,7 @@ LodModel::~LodModel() { glDeleteBuffers(1, &vertexBufferId); glDeleteBuffers(1, &indexBufferId); } -size_t LodModel::draw() { +size_t LodModel::draw(size_t level) { glBindBuffer(GL_ARRAY_BUFFER, vertexBufferId); enableAttribs(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferId); diff --git a/src/client/render/gl/api/Models.hpp b/src/client/render/impl/gl/api/Models.hpp similarity index 91% rename from src/client/render/gl/api/Models.hpp rename to src/client/render/impl/gl/api/Models.hpp index 8d86003..ec375f6 100644 --- a/src/client/render/gl/api/Models.hpp +++ b/src/client/render/impl/gl/api/Models.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../../api/Models.hpp" +#include "../../../api/Models.hpp" #include #include #include @@ -59,7 +59,8 @@ public: static void MakeDefault(); - size_t draw(); + size_t draw(size_t level); + inline size_t draw() { return draw(level); } size_t drawInstanced(size_t count); private: diff --git a/src/client/render/gl/pass/ColorProgram.cpp b/src/client/render/impl/gl/pass/ColorProgram.cpp similarity index 86% rename from src/client/render/gl/pass/ColorProgram.cpp rename to src/client/render/impl/gl/pass/ColorProgram.cpp index 78eee13..e92be5a 100644 --- a/src/client/render/gl/pass/ColorProgram.cpp +++ b/src/client/render/impl/gl/pass/ColorProgram.cpp @@ -2,7 +2,7 @@ #include "../Renderer.hpp" -using namespace pass; +using namespace render::gl::pass; ColorProgram::ColorProgram(): Program() { @@ -21,7 +21,7 @@ ColorProgram::~ColorProgram() { } std::string ColorProgram::getName() const { return "Color"; } -void ColorProgram::setup(render::gl::Renderer *renderer, glm::mat4 modelMatrix, glm::vec4 color) { +void ColorProgram::setup(Renderer *renderer, const glm::mat4& modelMatrix, const glm::vec4& color) { const auto mvp = renderer->getProjectionMatrix() * renderer->getViewMatrix() * modelMatrix; setMVP(&mvp[0][0]); setCol(&color[0]); diff --git a/src/client/render/gl/pass/ColorProgram.hpp b/src/client/render/impl/gl/pass/ColorProgram.hpp similarity index 77% rename from src/client/render/gl/pass/ColorProgram.hpp rename to src/client/render/impl/gl/pass/ColorProgram.hpp index 70470f7..6b5bb93 100644 --- a/src/client/render/gl/pass/ColorProgram.hpp +++ b/src/client/render/impl/gl/pass/ColorProgram.hpp @@ -2,14 +2,14 @@ #include "Program.hpp" -namespace pass { +namespace render::gl::pass { /// Solid colors pass class ColorProgram: public Program { public: ColorProgram(); ~ColorProgram(); - void setup(render::gl::Renderer *, glm::mat4 modelMatrix, glm::vec4 color); + void setup(Renderer *, const glm::mat4& modelMatrix, const glm::vec4& color); protected: std::string getName() const override; diff --git a/src/client/render/gl/pass/EntityProgram.cpp b/src/client/render/impl/gl/pass/EntityProgram.cpp similarity index 94% rename from src/client/render/gl/pass/EntityProgram.cpp rename to src/client/render/impl/gl/pass/EntityProgram.cpp index e0d5aad..c8b4c06 100644 --- a/src/client/render/gl/pass/EntityProgram.cpp +++ b/src/client/render/impl/gl/pass/EntityProgram.cpp @@ -2,7 +2,7 @@ #include "../Renderer.hpp" -using namespace pass; +using namespace render::gl::pass; inline void createBuffer(GLuint *id, const GLfloat *data, size_t count) { glGenBuffers(1, id); @@ -17,9 +17,13 @@ EntityProgram::EntityProgram(const EntityProgram::options &opts) : VoxelProgram( } EntityProgram::~EntityProgram() { } +void EntityProgram::start(Renderer* renderer) { + VoxelProgram::start(renderer); + VoxelProgram::setup(glm::vec4(0), 0); +} + void EntityProgram::setup(const std::vector& modelsMatrices) { setModels(&modelsMatrices[0][0][0], modelsMatrices.size()); - VoxelProgram::setup(glm::vec4(0), 0); } void EntityProgram::setModels(const GLfloat *matrices, size_t count) { diff --git a/src/client/render/gl/pass/EntityProgram.hpp b/src/client/render/impl/gl/pass/EntityProgram.hpp similarity index 89% rename from src/client/render/gl/pass/EntityProgram.hpp rename to src/client/render/impl/gl/pass/EntityProgram.hpp index 64d4c72..e2532eb 100644 --- a/src/client/render/gl/pass/EntityProgram.hpp +++ b/src/client/render/impl/gl/pass/EntityProgram.hpp @@ -2,7 +2,7 @@ #include "VoxelProgram.hpp" -namespace pass { +namespace render::gl::pass { /// Instanced voxels pass class EntityProgram: public VoxelProgram { public: @@ -11,6 +11,7 @@ namespace pass { static constexpr auto LOCATION = 6; + void start(Renderer*); void setup(const std::vector &modelsMatrices); void disable(); diff --git a/src/client/render/gl/pass/Program.cpp b/src/client/render/impl/gl/pass/Program.cpp similarity index 53% rename from src/client/render/gl/pass/Program.cpp rename to src/client/render/impl/gl/pass/Program.cpp index 47b542b..4fc498d 100644 --- a/src/client/render/gl/pass/Program.cpp +++ b/src/client/render/impl/gl/pass/Program.cpp @@ -3,13 +3,13 @@ #include #include #include "../api/Images.hpp" -#include "../../../../core/utils/logger.hpp" +#include "core/utils/logger.hpp" #define CONTENT_DIR "content/" #define SHADER_DIR CONTENT_DIR "shaders/" #define TEXTURES_DIR CONTENT_DIR "textures/" -using namespace pass; +using namespace render::gl::pass; void Program::load(const std::vector& shaders) { ProgramID = glCreateProgram(); @@ -44,29 +44,4 @@ Shader* Program::loadShader(GLenum type, const std::vector& flags) void Program::useIt() { glUseProgram(ProgramID); -} - -/* -GLuint Program::loadTexture(const std::string& name, bool linear) { - auto img = render::gl::Texture::LoadFromFile(TEXTURES_DIR + name + ".dds", {linear, linear}); - auto id = img->getId(); - std::free(img.release()); - return id; -} -GLuint Program::loadTextureArray(const std::vector &names, const std::string& suffix, float mipMapLOD, float anisotropy) { - - std::vector paths; - std::transform(names.begin(), names.end(), std::back_inserter(paths), - [suffix](const std::string &name) -> std::string { return TEXTURES_DIR + name + suffix + ".dds"; }); - - auto img = render::gl::TextureArray::LoadFromFiles(paths, {true, true, render::Texture::Wrap::REPEAT, anisotropy, true, mipMapLOD}); - auto id = img->getId(); - std::free(img.release()); - return id; -} -GLuint Program::loadTextureCube(const std::string &name) { - auto img = render::gl::TextureCube::LoadFromFiles(TEXTURES_DIR + name + ".cube", {}); - auto id = img->getId(); - std::free(img.release()); - return id; -}*/ \ No newline at end of file +} \ No newline at end of file diff --git a/src/client/render/gl/pass/Program.hpp b/src/client/render/impl/gl/pass/Program.hpp similarity index 95% rename from src/client/render/gl/pass/Program.hpp rename to src/client/render/impl/gl/pass/Program.hpp index e078ee2..05177e8 100644 --- a/src/client/render/gl/pass/Program.hpp +++ b/src/client/render/impl/gl/pass/Program.hpp @@ -13,7 +13,7 @@ namespace render { namespace render::gl { class Renderer; } -namespace pass { +namespace render::gl::pass { /// OpenGL shaders pipeline class Program { public: diff --git a/src/client/render/gl/pass/Shader.cpp b/src/client/render/impl/gl/pass/Shader.cpp similarity index 95% rename from src/client/render/gl/pass/Shader.cpp rename to src/client/render/impl/gl/pass/Shader.cpp index 134ddad..622c571 100644 --- a/src/client/render/gl/pass/Shader.cpp +++ b/src/client/render/impl/gl/pass/Shader.cpp @@ -3,9 +3,9 @@ #include #include #include -#include "../../../../core/utils/logger.hpp" +#include "core/utils/logger.hpp" -using namespace pass; +using namespace render::gl::pass; Shader::Shader(GLenum type, const std::string& file_path, const std::vector& flags) { ShaderID = glCreateShader(type); diff --git a/src/client/render/gl/pass/Shader.hpp b/src/client/render/impl/gl/pass/Shader.hpp similarity index 96% rename from src/client/render/gl/pass/Shader.hpp rename to src/client/render/impl/gl/pass/Shader.hpp index deee185..05ae598 100644 --- a/src/client/render/gl/pass/Shader.hpp +++ b/src/client/render/impl/gl/pass/Shader.hpp @@ -4,7 +4,7 @@ #include #include -namespace pass { +namespace render::gl::pass { /// OpenGL shader class Shader { public: diff --git a/src/client/render/gl/pass/SkyProgram.cpp b/src/client/render/impl/gl/pass/SkyProgram.cpp similarity index 90% rename from src/client/render/gl/pass/SkyProgram.cpp rename to src/client/render/impl/gl/pass/SkyProgram.cpp index 6009ff3..951859b 100644 --- a/src/client/render/gl/pass/SkyProgram.cpp +++ b/src/client/render/impl/gl/pass/SkyProgram.cpp @@ -2,7 +2,7 @@ #include "../Renderer.hpp" -using namespace pass; +using namespace render::gl::pass; SkyProgram::SkyProgram(): Program(), CubeBuffer(render::gl::Shape::SKY_CUBE) { @@ -22,14 +22,14 @@ SkyProgram::~SkyProgram() { } std::string SkyProgram::getName() const { return "Sky"; } -void SkyProgram::start(render::gl::Renderer *renderer) { +void SkyProgram::start(Renderer *renderer) { const auto fixedView = glm::mat4(glm::mat3(renderer->getViewMatrix())); setView(&fixedView[0][0]); setProjection(&renderer->getProjectionMatrix()[0][0]); bindTexture(renderer->getSkyTexture()); } -void SkyProgram::draw(render::gl::Renderer *renderer) { +void SkyProgram::draw(Renderer *renderer) { useIt(); start(renderer); CubeBuffer.draw(GL_TRIANGLES); diff --git a/src/client/render/gl/pass/SkyProgram.hpp b/src/client/render/impl/gl/pass/SkyProgram.hpp similarity index 85% rename from src/client/render/gl/pass/SkyProgram.hpp rename to src/client/render/impl/gl/pass/SkyProgram.hpp index 3a74260..e48ba54 100644 --- a/src/client/render/gl/pass/SkyProgram.hpp +++ b/src/client/render/impl/gl/pass/SkyProgram.hpp @@ -3,17 +3,17 @@ #include "Program.hpp" #include "../api/Models.hpp" -namespace pass { +namespace render::gl::pass { /// Skybox pass class SkyProgram: public Program { public: SkyProgram(); ~SkyProgram(); - void start(render::gl::Renderer *); + void start(Renderer *); /// Direct draw using internal buffer - void draw(render::gl::Renderer *); + void draw(Renderer *); protected: std::string getName() const override; diff --git a/src/client/render/impl/gl/pass/TerrainProgram.cpp b/src/client/render/impl/gl/pass/TerrainProgram.cpp new file mode 100644 index 0000000..3b5a5e3 --- /dev/null +++ b/src/client/render/impl/gl/pass/TerrainProgram.cpp @@ -0,0 +1,27 @@ +#include "TerrainProgram.hpp" + +#include "../Renderer.hpp" + +using namespace render::gl::pass; + +TerrainProgram::TerrainProgram(const TerrainProgram::options& opts): VoxelProgram(opts) { + + ModelMatrixID = glGetUniformLocation(ProgramID, "Model"); +} + +TerrainProgram::~TerrainProgram() { } + +void TerrainProgram::setup(const glm::mat4& modelMatrix, const glm::vec4& sphereProj, float curvature) { + setModel(&modelMatrix[0][0]); + VoxelProgram::setup(sphereProj, curvature); +} +void TerrainProgram::unsetCurvature() { + VoxelProgram::setup(glm::vec4(0), 0); +} +void TerrainProgram::setup(const glm::mat4& modelMatrix) { + setModel(&modelMatrix[0][0]); +} + +void TerrainProgram::setModel(const GLfloat *matrix) { + glUniformMatrix4fv(ModelMatrixID, 1, GL_FALSE, matrix); +} diff --git a/src/client/render/impl/gl/pass/TerrainProgram.hpp b/src/client/render/impl/gl/pass/TerrainProgram.hpp new file mode 100644 index 0000000..68da40b --- /dev/null +++ b/src/client/render/impl/gl/pass/TerrainProgram.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "VoxelProgram.hpp" + +namespace render::gl::pass { + /// Unique voxels pass + class TerrainProgram: public VoxelProgram { + public: + TerrainProgram(const options &opts); + ~TerrainProgram(); + + void setup(const glm::mat4& modelMatrix, const glm::vec4& sphereProj, float curvature); + void unsetCurvature(); + void setup(const glm::mat4 &modelMatrix); + + protected: + void setModel(const GLfloat *matrix); + + private: + GLuint ModelMatrixID; + }; +} \ No newline at end of file diff --git a/src/client/render/gl/pass/UIProgram.cpp b/src/client/render/impl/gl/pass/UIProgram.cpp similarity index 94% rename from src/client/render/gl/pass/UIProgram.cpp rename to src/client/render/impl/gl/pass/UIProgram.cpp index fa3a83e..f638313 100644 --- a/src/client/render/gl/pass/UIProgram.cpp +++ b/src/client/render/impl/gl/pass/UIProgram.cpp @@ -1,6 +1,6 @@ #include "UIProgram.hpp" -using namespace pass; +using namespace render::gl::pass; UIProgram::UIProgram() : Program() { std::vector shaders; diff --git a/src/client/render/gl/pass/UIProgram.hpp b/src/client/render/impl/gl/pass/UIProgram.hpp similarity index 91% rename from src/client/render/gl/pass/UIProgram.hpp rename to src/client/render/impl/gl/pass/UIProgram.hpp index 0e02828..c984293 100644 --- a/src/client/render/gl/pass/UIProgram.hpp +++ b/src/client/render/impl/gl/pass/UIProgram.hpp @@ -2,7 +2,7 @@ #include "Program.hpp" -namespace pass { +namespace render::gl::pass { /// To screen texture pass class UIProgram: public Program { public: diff --git a/src/client/render/gl/pass/VoxelProgram.cpp b/src/client/render/impl/gl/pass/VoxelProgram.cpp similarity index 95% rename from src/client/render/gl/pass/VoxelProgram.cpp rename to src/client/render/impl/gl/pass/VoxelProgram.cpp index 0899191..cca757a 100644 --- a/src/client/render/gl/pass/VoxelProgram.cpp +++ b/src/client/render/impl/gl/pass/VoxelProgram.cpp @@ -2,7 +2,7 @@ #include "../Renderer.hpp" -using namespace pass; +using namespace render::gl::pass; VoxelProgram::VoxelProgram(const VoxelProgram::options& opts, std::vector flags): Program() { @@ -55,7 +55,7 @@ VoxelProgram::~VoxelProgram() { } std::string VoxelProgram::getName() const { return "Voxel"; } -void VoxelProgram::start(render::gl::Renderer *renderer) { +void VoxelProgram::start(Renderer *renderer) { bindTexture(renderer->getTextureAtlas()); bindNormal(renderer->getNormalAtlas()); bindHOS(renderer->getHOSAtlas()); @@ -64,7 +64,7 @@ void VoxelProgram::start(render::gl::Renderer *renderer) { setView(&renderer->getViewMatrix()[0][0]); setProj(&renderer->getProjectionMatrix()[0][0]); } -void VoxelProgram::setup(glm::vec4 sphereProj, float curvature) { +void VoxelProgram::setup(const glm::vec4& sphereProj, float curvature) { setSphereProj(&sphereProj[0]); setCurvature(curvature); } diff --git a/src/client/render/gl/pass/VoxelProgram.hpp b/src/client/render/impl/gl/pass/VoxelProgram.hpp similarity index 89% rename from src/client/render/gl/pass/VoxelProgram.hpp rename to src/client/render/impl/gl/pass/VoxelProgram.hpp index 3061c17..22843cf 100644 --- a/src/client/render/gl/pass/VoxelProgram.hpp +++ b/src/client/render/impl/gl/pass/VoxelProgram.hpp @@ -2,7 +2,7 @@ #include "Program.hpp" -namespace pass { +namespace render::gl::pass { /// Abstract voxels pass class VoxelProgram: public Program { public: @@ -11,10 +11,10 @@ namespace pass { VoxelProgram(const options &opts, std::vector flags = {}); ~VoxelProgram(); - void start(render::gl::Renderer *); + void start(Renderer *); protected: - void setup(glm::vec4 sphereProj, float curvature); + void setup(const glm::vec4& sphereProj, float curvature); std::string getName() const override; void setView(const GLfloat *matrix); diff --git a/src/client/render/vk/Allocator.cpp b/src/client/render/impl/vk/Allocator.cpp similarity index 100% rename from src/client/render/vk/Allocator.cpp rename to src/client/render/impl/vk/Allocator.cpp diff --git a/src/client/render/vk/Allocator.hpp b/src/client/render/impl/vk/Allocator.hpp similarity index 98% rename from src/client/render/vk/Allocator.hpp rename to src/client/render/impl/vk/Allocator.hpp index c4b8980..ddc830e 100644 --- a/src/client/render/vk/Allocator.hpp +++ b/src/client/render/impl/vk/Allocator.hpp @@ -1,7 +1,7 @@ #pragma once #include "forward.hpp" -#include "../../../core/flags.hpp" +#include "core/utils/flags.hpp" #include "api/Memory.hpp" #include #include diff --git a/src/client/render/vk/CommandCenter.cpp b/src/client/render/impl/vk/CommandCenter.cpp similarity index 91% rename from src/client/render/vk/CommandCenter.cpp rename to src/client/render/impl/vk/CommandCenter.cpp index 317bfa1..93e450c 100644 --- a/src/client/render/vk/CommandCenter.cpp +++ b/src/client/render/impl/vk/CommandCenter.cpp @@ -51,8 +51,7 @@ CommandCenter::~CommandCenter() { vkDestroyCommandPool(device, graphicsPool, ALLOC); } -#include "../../../core/world/materials.hpp" -void CommandCenter::loadAtlases(const std::string& textures, int anisotropy, float lodBias) { +void CommandCenter::loadAtlases(const std::string& pack, int anisotropy, float lodBias) { voxelTextureAtlas.reset(); voxelNormalAtlas.reset(); voxelHOSAtlas.reset(); @@ -60,9 +59,10 @@ void CommandCenter::loadAtlases(const std::string& textures, int anisotropy, flo std::vector paths; auto makePaths = [&](const std::string &suffix) { paths.clear(); - paths.reserve(world::materials::textures.size()); - for (auto& texture: world::materials::textures) { - paths.push_back(TEXTURES_DIR + textures + "/terrain/" + texture + suffix + ".dds"); + const auto &textures = world::module::Registry::Get()->getTextures(); + paths.reserve(textures.size()); + for (auto& texture: textures) { + paths.push_back(TEXTURES_DIR + pack + "/terrain/" + texture + suffix + ".dds"); } return paths; }; @@ -301,12 +301,12 @@ void CommandCenter::startRecording(uint32_t idx, VkRenderPass renderPass, VkExte vkCmdBeginRenderPass(graphicsBuffers[idx], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); } -void CommandCenter::startWorldPass(uint32_t idx, const Subpass &worldPass) { - vkCmdBindPipeline(graphicsBuffers[idx], VK_PIPELINE_BIND_POINT_GRAPHICS, worldPass.pipeline); - vkCmdBindDescriptorSets(graphicsBuffers[idx], VK_PIPELINE_BIND_POINT_GRAPHICS, worldPass.layout, 0, 1, &voxelDescriptorSets[idx], 0, nullptr); +void CommandCenter::startTerrainPass(uint32_t idx, const Subpass &terrainPass) { + vkCmdBindPipeline(graphicsBuffers[idx], VK_PIPELINE_BIND_POINT_GRAPHICS, terrainPass.pipeline); + vkCmdBindDescriptorSets(graphicsBuffers[idx], VK_PIPELINE_BIND_POINT_GRAPHICS, terrainPass.layout, 0, 1, &voxelDescriptorSets[idx], 0, nullptr); } -size_t CommandCenter::recordModel(uint32_t i, const Subpass &worldPass, const UniqueCurvaturePush& push, const LodModel *const modelBuffer) { - vkCmdPushConstants(graphicsBuffers[i], worldPass.layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(push), &push); +size_t CommandCenter::recordModel(uint32_t i, const Subpass &terrainPass, const UniqueCurvaturePush& push, const LodModel *const modelBuffer) { + vkCmdPushConstants(graphicsBuffers[i], terrainPass.layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(push), &push); VkBuffer vertexBuffers[] = {modelBuffer->getVertex()}; VkDeviceSize offsets[] = {0}; @@ -317,6 +317,20 @@ size_t CommandCenter::recordModel(uint32_t i, const Subpass &worldPass, const Un return size; } void CommandCenter::startEntityPass(uint32_t, const Subpass&) { } +size_t CommandCenter::recordModel(uint32_t i, const Subpass &entityPass, const glm::mat4& matrix, const Model *const modelBuffer) { + UniqueCurvaturePush push; + push.curvature = 0; + push.model = matrix; + vkCmdPushConstants(graphicsBuffers[i], entityPass.layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(push), &push); + + VkBuffer vertexBuffers[] = {modelBuffer->getVertex()}; + VkDeviceSize offsets[] = {0}; + vkCmdBindVertexBuffers(graphicsBuffers[i], 0, 1, vertexBuffers, offsets); + vkCmdBindIndexBuffer(graphicsBuffers[i], modelBuffer->getIndex(), 0, VK_INDEX_TYPE_UINT16); + auto size = modelBuffer->getIndexSize(); + vkCmdDrawIndexed(graphicsBuffers[i], size, 1, 0, 0, 0); + return size; +} size_t CommandCenter::recordModels(uint32_t i, const Subpass &entityPass, const std::vector &matrices, const Model *const modelBuffer) { VkBuffer vertexBuffers[] = {modelBuffer->getVertex()}; VkDeviceSize offsets[] = {0}; diff --git a/src/client/render/vk/CommandCenter.hpp b/src/client/render/impl/vk/CommandCenter.hpp similarity index 89% rename from src/client/render/vk/CommandCenter.hpp rename to src/client/render/impl/vk/CommandCenter.hpp index 1c23c79..b2f4bb7 100644 --- a/src/client/render/vk/CommandCenter.hpp +++ b/src/client/render/impl/vk/CommandCenter.hpp @@ -24,9 +24,10 @@ public: ~CommandCenter(); void startRecording(uint32_t idx, VkRenderPass, VkExtent2D, const VoxelUBO&); - void startWorldPass(uint32_t idx, const Subpass&); - size_t recordModel(uint32_t idx, const Subpass &worldPass, const UniqueCurvaturePush&, const LodModel *const); + void startTerrainPass(uint32_t idx, const Subpass&); + size_t recordModel(uint32_t idx, const Subpass &terrainPass, const UniqueCurvaturePush&, const LodModel *const); void startEntityPass(uint32_t idx, const Subpass &entityPass); + size_t recordModel(uint32_t idx, const Subpass &entityPass, const glm::mat4&, const Model *const); size_t recordModels(uint32_t idx, const Subpass &entityPass, const std::vector&, const Model *const); void startIndicPass(uint32_t idx, const Subpass&); size_t recordIndicator(uint32_t idx, const Subpass&, const ModelColorPush&, bool isCube); diff --git a/src/client/render/vk/PhysicalDeviceInfo.cpp b/src/client/render/impl/vk/PhysicalDeviceInfo.cpp similarity index 100% rename from src/client/render/vk/PhysicalDeviceInfo.cpp rename to src/client/render/impl/vk/PhysicalDeviceInfo.cpp diff --git a/src/client/render/vk/PhysicalDeviceInfo.hpp b/src/client/render/impl/vk/PhysicalDeviceInfo.hpp similarity index 98% rename from src/client/render/vk/PhysicalDeviceInfo.hpp rename to src/client/render/impl/vk/PhysicalDeviceInfo.hpp index 39c6bcd..61ee786 100644 --- a/src/client/render/vk/PhysicalDeviceInfo.hpp +++ b/src/client/render/impl/vk/PhysicalDeviceInfo.hpp @@ -1,7 +1,7 @@ #pragma once #include "forward.hpp" -#include "../../../core/utils/logger.hpp" +#include "core/utils/logger.hpp" #include #include diff --git a/src/client/render/vk/Pipeline.cpp b/src/client/render/impl/vk/Pipeline.cpp similarity index 83% rename from src/client/render/vk/Pipeline.cpp rename to src/client/render/impl/vk/Pipeline.cpp index ca15fee..b759951 100644 --- a/src/client/render/vk/Pipeline.cpp +++ b/src/client/render/impl/vk/Pipeline.cpp @@ -1,12 +1,13 @@ #include "Pipeline.hpp" #include "PhysicalDeviceInfo.hpp" -#include "../../../core/data/file.hpp" -#include "../Renderer.hpp" +#include "core/utils/io.hpp" +#include "core/utils/defer.hpp" +#include "../../Renderer.hpp" #include "api/Models.hpp" #define CONTENT_DIR "content/" -#define SHADER_DIR CONTENT_DIR "shaders/" +#define SHADER_DIR CONTENT_DIR "shaders/vk/" using namespace render::vk; @@ -65,64 +66,42 @@ Pipeline::Pipeline(VkDevice device, const PhysicalDeviceInfo &info, const render if (hasSamples) { colorDepthSubpass.pResolveAttachments = &colorAttachmentResolveRef; } - std::array subpasses = {colorDepthSubpass, colorDepthSubpass, colorDepthSubpass, colorDepthSubpass}; + std::vector subpasses = {colorDepthSubpass, colorDepthSubpass, colorDepthSubpass}; - std::array dependencies{}; - dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; - dependencies[0].dstSubpass = 0; - dependencies[0].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[0].srcAccessMask = 0; - dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dependencies[0].dependencyFlags = 0; + auto colorDependency = [](uint32_t src, uint32_t dst) { + VkSubpassDependency dependency{}; + dependency.srcSubpass = src; + dependency.dstSubpass = dst; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependency.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + return dependency; + }; + auto depthDependency = [](uint32_t src, uint32_t dst) { + VkSubpassDependency dependency{}; + dependency.srcSubpass = src; + dependency.dstSubpass = dst; + dependency.srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + dependency.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + dependency.dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + dependency.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + dependency.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + return dependency; + }; - dependencies[1].srcSubpass = 0; - dependencies[1].dstSubpass = 1; - dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dependencies[1].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[1].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + std::vector dependencies{ + colorDependency(VK_SUBPASS_EXTERNAL, 0), depthDependency(VK_SUBPASS_EXTERNAL, 0), + colorDependency(0, 1), depthDependency(0, 1), + colorDependency(1, 2), depthDependency(1, 2) + }; - dependencies[2].srcSubpass = 0; - dependencies[2].dstSubpass = 1; - dependencies[2].srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - dependencies[2].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - dependencies[2].dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - dependencies[2].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - dependencies[2].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; - - dependencies[3].srcSubpass = 1; - dependencies[3].dstSubpass = 2; - dependencies[3].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[3].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dependencies[3].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[3].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dependencies[3].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; - - dependencies[4].srcSubpass = 1; - dependencies[4].dstSubpass = 2; - dependencies[4].srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - dependencies[4].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - dependencies[4].dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - dependencies[4].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - dependencies[4].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; - - dependencies[5].srcSubpass = 2; - dependencies[5].dstSubpass = 3; - dependencies[5].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[5].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dependencies[5].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[5].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dependencies[5].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; - - dependencies[6].srcSubpass = 2; - dependencies[6].dstSubpass = 3; - dependencies[6].srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - dependencies[6].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - dependencies[6].dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - dependencies[6].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - dependencies[6].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + if (options.voxel.transparency) { + subpasses.push_back(colorDepthSubpass); + dependencies.push_back(colorDependency(2, 3)); + dependencies.push_back(depthDependency(2, 3)); + } VkRenderPassCreateInfo renderPassInfo{}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; @@ -243,7 +222,7 @@ Pipeline::Pipeline(VkDevice device, const PhysicalDeviceInfo &info, const render // Common pipeline settings auto setShaders = [&](Subpass &pass, const std::string &shaderName, bool geometry = false, const VkSpecializationInfo* specialization = nullptr) -> std::vector { - auto createShaderModule = [&](const data::file_content &code) { + auto createShaderModule = [&](const io::file_content &code) { VkShaderModuleCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); @@ -259,7 +238,7 @@ Pipeline::Pipeline(VkDevice device, const PhysicalDeviceInfo &info, const render VkPipelineShaderStageCreateInfo vertShaderStageInfo{}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; - data::file_content vsFile({SHADER_DIR + shaderName + ".vs.spv"}); + io::file_content vsFile({SHADER_DIR + shaderName + ".vs.spv"}); vertShaderStageInfo.module = pass.vsShader = createShaderModule(vsFile); vertShaderStageInfo.pName = "main"; vertShaderStageInfo.pSpecializationInfo = specialization; @@ -267,7 +246,7 @@ Pipeline::Pipeline(VkDevice device, const PhysicalDeviceInfo &info, const render VkPipelineShaderStageCreateInfo fragShaderStageInfo{}; fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; - data::file_content fsFile({SHADER_DIR + shaderName + ".fs.spv"}); + io::file_content fsFile({SHADER_DIR + shaderName + ".fs.spv"}); fragShaderStageInfo.module = pass.fsShader = createShaderModule(fsFile); fragShaderStageInfo.pName = "main"; fragShaderStageInfo.pSpecializationInfo = specialization; @@ -276,7 +255,7 @@ Pipeline::Pipeline(VkDevice device, const PhysicalDeviceInfo &info, const render VkPipelineShaderStageCreateInfo geomShaderStageInfo{}; geomShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; geomShaderStageInfo.stage = VK_SHADER_STAGE_GEOMETRY_BIT; - data::file_content gsFile({SHADER_DIR + shaderName + ".gs.spv"}); + io::file_content gsFile({SHADER_DIR + shaderName + ".gs.spv"}); geomShaderStageInfo.module = pass.gsShader = createShaderModule(gsFile); geomShaderStageInfo.pName = "main"; geomShaderStageInfo.pSpecializationInfo = specialization; @@ -328,7 +307,7 @@ Pipeline::Pipeline(VkDevice device, const PhysicalDeviceInfo &info, const render VkPipelineColorBlendStateCreateInfo colorBlending{}; colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; colorBlending.attachmentCount = 1; //NOTE: For multitarget - colorBlending.pAttachments = options.voxel.transparency ? &blendColorBlendAttachment : &solidColorBlendAttachment; + colorBlending.pAttachments = &solidColorBlendAttachment; colorBlending.logicOpEnable = VK_FALSE; colorBlending.logicOp = VK_LOGIC_OP_COPY; colorBlending.blendConstants[0] = 0.0f; @@ -438,15 +417,15 @@ Pipeline::Pipeline(VkDevice device, const PhysicalDeviceInfo &info, const render voxelSpecialization.mapEntryCount = speIndex.size(); voxelSpecialization.pMapEntries = speIndex.data(); - { // World pipeline + { // Terrain pipeline VkPushConstantRange pushRange{}; pushRange.offset = 0; pushRange.size = sizeof(UniqueCurvaturePush); pushRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - setLayout(worldPass, {voxelDescriptorSet}, {pushRange}); + setLayout(terrainPass, {voxelDescriptorSet}, {pushRange}); auto withGeometry = options.voxel.geometry && info.features.geometryShader; - auto shaderStages = setShaders(worldPass, withGeometry ? "Voxel.geo" : "Voxel", withGeometry, &voxelSpecialization); + auto shaderStages = setShaders(terrainPass, withGeometry ? "Voxel.geo" : "Voxel", withGeometry, &voxelSpecialization); VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; @@ -471,14 +450,14 @@ Pipeline::Pipeline(VkDevice device, const PhysicalDeviceInfo &info, const render pipelineInfo.pColorBlendState = &colorBlending; pipelineInfo.pDynamicState = nullptr; - pipelineInfo.layout = worldPass.layout; + pipelineInfo.layout = terrainPass.layout; pipelineInfo.renderPass = renderPass; pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; pipelineInfo.basePipelineIndex = -1; - if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, ALLOC, &worldPass.pipeline) != VK_SUCCESS) { + if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, ALLOC, &terrainPass.pipeline) != VK_SUCCESS) { FATAL("Failed to create graphics pipeline!"); } } @@ -528,15 +507,18 @@ Pipeline::Pipeline(VkDevice device, const PhysicalDeviceInfo &info, const render FATAL("Failed to create graphics pipeline!"); } } - { // Transparent World pipeline + if (options.voxel.transparency) { // Transparent Terrain pipeline VkPushConstantRange pushRange{}; pushRange.offset = 0; pushRange.size = sizeof(UniqueCurvaturePush); pushRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - setLayout(transparentWorldPass, {voxelDescriptorSet}, {pushRange}); + setLayout(transparentTerrainPass, {voxelDescriptorSet}, {pushRange}); auto withGeometry = options.voxel.geometry && info.features.geometryShader; - auto shaderStages = setShaders(transparentWorldPass, withGeometry ? "Voxel.geo" : "Voxel", withGeometry, &voxelSpecialization); + auto shaderStages = setShaders(transparentTerrainPass, withGeometry ? "Voxel.geo" : "Voxel", withGeometry, &voxelSpecialization); + + colorBlending.pAttachments = &blendColorBlendAttachment; + DEFER({colorBlending.pAttachments = &solidColorBlendAttachment;}) VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; @@ -561,14 +543,14 @@ Pipeline::Pipeline(VkDevice device, const PhysicalDeviceInfo &info, const render pipelineInfo.pColorBlendState = &colorBlending; pipelineInfo.pDynamicState = nullptr; - pipelineInfo.layout = transparentWorldPass.layout; + pipelineInfo.layout = transparentTerrainPass.layout; pipelineInfo.renderPass = renderPass; pipelineInfo.subpass = 2; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; pipelineInfo.basePipelineIndex = -1; - if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, ALLOC, &transparentWorldPass.pipeline) != VK_SUCCESS) { + if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, ALLOC, &transparentTerrainPass.pipeline) != VK_SUCCESS) { FATAL("Failed to create graphics pipeline!"); } } @@ -602,7 +584,7 @@ Pipeline::Pipeline(VkDevice device, const PhysicalDeviceInfo &info, const render pipelineInfo.layout = skyPass.layout; pipelineInfo.renderPass = renderPass; - pipelineInfo.subpass = 3; + pipelineInfo.subpass = 2 + options.voxel.transparency; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; pipelineInfo.basePipelineIndex = -1; @@ -625,8 +607,10 @@ Pipeline::~Pipeline() { vkDestroyRenderPass(device, uiRenderPass, ALLOC); - destroy(worldPass); + destroy(terrainPass); destroy(indicPass); + if (transparentTerrainPass.pipeline) + destroy(transparentTerrainPass); destroy(skyPass); vkDestroyDescriptorSetLayout(device, voxelDescriptorSet, ALLOC); diff --git a/src/client/render/vk/Pipeline.hpp b/src/client/render/impl/vk/Pipeline.hpp similarity index 78% rename from src/client/render/vk/Pipeline.hpp rename to src/client/render/impl/vk/Pipeline.hpp index 76c907b..f32e697 100644 --- a/src/client/render/vk/Pipeline.hpp +++ b/src/client/render/impl/vk/Pipeline.hpp @@ -9,7 +9,7 @@ struct Subpass { VkShaderModule gsShader = NULL; VkShaderModule fsShader; VkPipelineLayout layout; - VkPipeline pipeline; + VkPipeline pipeline = NULL; }; class Pipeline final { @@ -22,11 +22,11 @@ public: constexpr VkRenderPass getRenderPass() const { return renderPass; } // Voxels (world & entity) passes descriptor set constexpr VkDescriptorSetLayout getVoxelDescriptorSet() const { return voxelDescriptorSet; } - constexpr const Subpass& getWorldPass() const { return worldPass; } - constexpr const Subpass &getEntitiesPass() const { return worldPass; } + constexpr const Subpass& getTerrainPass() const { return terrainPass; } + constexpr const Subpass &getEntitiesPass() const { return terrainPass; } constexpr VkDescriptorSetLayout getIndicDescriptorSet() const { return indicDescriptorSet; } constexpr const Subpass &getIndicPass() const { return indicPass; } - constexpr const Subpass& getTransparentWorldPass() const { return transparentWorldPass; } + constexpr const Subpass& getTransparentTerrainPass() const { return transparentTerrainPass; } constexpr VkDescriptorSetLayout getSkyDescriptorSet() const { return skyDescriptorSet; } constexpr const Subpass& getSkyPass() const { return skyPass; } @@ -41,9 +41,9 @@ private: VkDescriptorSetLayout indicDescriptorSet; VkDescriptorSetLayout skyDescriptorSet; - Subpass worldPass; + Subpass terrainPass; Subpass indicPass; - Subpass transparentWorldPass; + Subpass transparentTerrainPass; Subpass skyPass; VkRenderPass uiRenderPass; diff --git a/src/client/render/vk/Renderer.cpp b/src/client/render/impl/vk/Renderer.cpp similarity index 93% rename from src/client/render/vk/Renderer.cpp rename to src/client/render/impl/vk/Renderer.cpp index 65afddf..c504320 100644 --- a/src/client/render/vk/Renderer.cpp +++ b/src/client/render/impl/vk/Renderer.cpp @@ -2,7 +2,7 @@ #include "UI.hpp" #include "../../Window.hpp" -#include "../../control/Camera.hpp" +#include "../../../control/Camera.hpp" #include "PhysicalDeviceInfo.hpp" #include "Allocator.hpp" #include "SwapChain.hpp" @@ -140,23 +140,23 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugValidationCallback(VkDebugUtilsMessageSeveri { switch (messageSeverity) { case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: - LOG_E("[VK] " << pCallbackData->pMessage); + LOG_E(pCallbackData->pMessage); break; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: - LOG_W("[VK] " << pCallbackData->pMessage); + LOG_W(pCallbackData->pMessage); break; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: - LOG_D("[VK] " << pCallbackData->pMessage); + LOG_D(pCallbackData->pMessage); break; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: - LOG_T("[VK] " << pCallbackData->pMessage); + LOG_T(pCallbackData->pMessage); break; default: - LOG_I("[VK] " << pCallbackData->pMessage); + LOG_I(pCallbackData->pMessage); break; } @@ -166,7 +166,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugValidationCallback(VkDebugUtilsMessageSeveri bool Renderer::Load(Window& window, const render::renderOptions& opt, const windowOptions& windOpt) { Window::CreateInfo windowInfo; windowInfo.pfnResize = on_resize_callback; - windowInfo.client = {Window::CreateInfo::Client::Type::VK, 0, 0}; + windowInfo.client = std::nullopt; windowInfo.samples = -1; windowInfo.fps = windOpt.targetFPS; windowInfo.fullscreen = windOpt.fullscreen; @@ -417,6 +417,7 @@ bool Renderer::Load(Window& window, const render::renderOptions& opt, const wind sInstance = new Renderer(instance, device, physicalInfo, opt); Model::MakeDefault(); LodModel::MakeDefault(); + LOG_W("Vulkan driver is unrecommended and WIP"); return true; } Renderer::UICtx Renderer::getUICtx() const { @@ -451,12 +452,12 @@ void Renderer::beginFrame() { } } -std::function Renderer::beginWorldPass(bool solid) { +std::function Renderer::beginTerrainPass(bool solid) { assert(currentImage < swapChain->getImageViews().size()); - auto& pass = solid ? pipeline->getWorldPass() : pipeline->getTransparentWorldPass(); - commandCenter->startWorldPass(currentImage, pass); - return [&](render::LodModel *const rBuffer, glm::mat4 model, glm::vec4 sphere, float curv) { + const auto& pass = solid ? pipeline->getTerrainPass() : pipeline->getTransparentTerrainPass(); + commandCenter->startTerrainPass(currentImage, pass); + return [&](render::LodModel *const rBuffer, const glm::mat4& model, const glm::vec4& sphere, float curv) { auto buffer = dynamic_cast(rBuffer); buffer->setLastUse(currentImage); UniqueCurvaturePush push{}; @@ -466,26 +467,34 @@ std::function Rend return commandCenter->recordModel(currentImage, pass, push, buffer); }; } - -std::function &)> Renderer::beginEntityPass() { +std::function Renderer::beginUniquePass() { assert(currentImage < swapChain->getImageViews().size()); - auto& pass = pipeline->getEntitiesPass(); + //NOTE: assume current pass is terrain + const auto& pass = pipeline->getTerrainPass(); + return [&](render::Model *const rBuffer, const glm::mat4& model) { + auto buffer = dynamic_cast(rBuffer); + buffer->setLastUse(currentImage); + return commandCenter->recordModel(currentImage, pass, model, buffer); + }; +} +std::function&)> Renderer::beginInstancedPass() { + assert(currentImage < swapChain->getImageViews().size()); + + const auto& pass = pipeline->getEntitiesPass(); commandCenter->startEntityPass(currentImage, pass); return [&](render::Model *const rBuffer, const std::vector& models) { auto buffer = dynamic_cast(rBuffer); buffer->setLastUse(currentImage); return commandCenter->recordModels(currentImage, pass, models, buffer); }; - return [](render::Model *const, const std::vector &) { return 0; }; } - -std::function Renderer::beginIndicatorPass() { +std::function Renderer::beginIndicatorPass() { assert(currentImage < swapChain->getImageViews().size()); - auto &pass = pipeline->getIndicPass(); + const auto &pass = pipeline->getIndicPass(); commandCenter->startIndicPass(currentImage, pass); - return [&](glm::mat4 model, world::action::Shape shape, glm::vec4 color) { + return [&](const glm::mat4& model, world::action::Shape shape, const glm::vec4& color) { ModelColorPush push{}; push.model = model; push.color = color; diff --git a/src/client/render/vk/Renderer.hpp b/src/client/render/impl/vk/Renderer.hpp similarity index 85% rename from src/client/render/vk/Renderer.hpp rename to src/client/render/impl/vk/Renderer.hpp index d8a2c35..feec139 100644 --- a/src/client/render/vk/Renderer.hpp +++ b/src/client/render/impl/vk/Renderer.hpp @@ -1,5 +1,5 @@ #pragma once -#include "../Renderer.hpp" +#include "../../Renderer.hpp" #include "forward.hpp" struct windowOptions; @@ -22,9 +22,10 @@ public: static _FORCE_INLINE_ Renderer *Get() { return static_cast(render::Renderer::Get()); } void beginFrame() override; - std::function beginWorldPass(bool solid) override; - std::function &)> beginEntityPass() override; - std::function beginIndicatorPass() override; + std::function beginTerrainPass(bool solid) override; + std::function beginUniquePass() override; + std::function&)> beginInstancedPass() override; + std::function beginIndicatorPass() override; void postProcess() override; void recordUI(std::function); void endFrame() override; diff --git a/src/client/render/vk/SwapChain.cpp b/src/client/render/impl/vk/SwapChain.cpp similarity index 100% rename from src/client/render/vk/SwapChain.cpp rename to src/client/render/impl/vk/SwapChain.cpp diff --git a/src/client/render/vk/SwapChain.hpp b/src/client/render/impl/vk/SwapChain.hpp similarity index 100% rename from src/client/render/vk/SwapChain.hpp rename to src/client/render/impl/vk/SwapChain.hpp diff --git a/src/client/render/vk/UI.cpp b/src/client/render/impl/vk/UI.cpp similarity index 98% rename from src/client/render/vk/UI.cpp rename to src/client/render/impl/vk/UI.cpp index d863e3e..0965101 100644 --- a/src/client/render/vk/UI.cpp +++ b/src/client/render/impl/vk/UI.cpp @@ -5,7 +5,7 @@ #include "Allocator.hpp" #include #include -#include "../../../core/utils/logger.hpp" +#include "core/utils/logger.hpp" using namespace render::vk; diff --git a/src/client/render/vk/UI.hpp b/src/client/render/impl/vk/UI.hpp similarity index 95% rename from src/client/render/vk/UI.hpp rename to src/client/render/impl/vk/UI.hpp index 89f970e..bb65a76 100644 --- a/src/client/render/vk/UI.hpp +++ b/src/client/render/impl/vk/UI.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../UI.hpp" +#include "../../UI.hpp" #include "forward.hpp" #include "api/Images.hpp" struct GLFWwindow; diff --git a/src/client/render/vk/api/Buffers.cpp b/src/client/render/impl/vk/api/Buffers.cpp similarity index 99% rename from src/client/render/vk/api/Buffers.cpp rename to src/client/render/impl/vk/api/Buffers.cpp index af1236f..5b01e65 100644 --- a/src/client/render/vk/api/Buffers.cpp +++ b/src/client/render/impl/vk/api/Buffers.cpp @@ -1,6 +1,6 @@ #include "Buffers.hpp" #include "../Allocator.hpp" -#include "../../../../core/utils/logger.hpp" +#include "core/utils/logger.hpp" #include using namespace render::vk; diff --git a/src/client/render/vk/api/Buffers.hpp b/src/client/render/impl/vk/api/Buffers.hpp similarity index 98% rename from src/client/render/vk/api/Buffers.hpp rename to src/client/render/impl/vk/api/Buffers.hpp index baa36b2..726e046 100644 --- a/src/client/render/vk/api/Buffers.hpp +++ b/src/client/render/impl/vk/api/Buffers.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../../api/Buffers.hpp" +#include "../../../api/Buffers.hpp" #include "Memory.hpp" #include diff --git a/src/client/render/vk/api/Images.cpp b/src/client/render/impl/vk/api/Images.cpp similarity index 99% rename from src/client/render/vk/api/Images.cpp rename to src/client/render/impl/vk/api/Images.cpp index cbd2fe6..db4fccc 100644 --- a/src/client/render/vk/api/Images.cpp +++ b/src/client/render/impl/vk/api/Images.cpp @@ -1,7 +1,7 @@ #include "Images.hpp" #include "Buffers.hpp" #include "../Allocator.hpp" -#include "../../../../core/utils/logger.hpp" +#include "core/utils/logger.hpp" #include using namespace render::vk; diff --git a/src/client/render/vk/api/Images.hpp b/src/client/render/impl/vk/api/Images.hpp similarity index 98% rename from src/client/render/vk/api/Images.hpp rename to src/client/render/impl/vk/api/Images.hpp index 2ae7261..a1cfb70 100644 --- a/src/client/render/vk/api/Images.hpp +++ b/src/client/render/impl/vk/api/Images.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../../api/Images.hpp" +#include "../../../api/Images.hpp" #include "Memory.hpp" namespace render::vk { diff --git a/src/client/render/vk/api/Memory.hpp b/src/client/render/impl/vk/api/Memory.hpp similarity index 100% rename from src/client/render/vk/api/Memory.hpp rename to src/client/render/impl/vk/api/Memory.hpp diff --git a/src/client/render/vk/api/Models.cpp b/src/client/render/impl/vk/api/Models.cpp similarity index 100% rename from src/client/render/vk/api/Models.cpp rename to src/client/render/impl/vk/api/Models.cpp diff --git a/src/client/render/vk/api/Models.hpp b/src/client/render/impl/vk/api/Models.hpp similarity index 99% rename from src/client/render/vk/api/Models.hpp rename to src/client/render/impl/vk/api/Models.hpp index 7bb99f8..018ac5c 100644 --- a/src/client/render/vk/api/Models.hpp +++ b/src/client/render/impl/vk/api/Models.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../../api/Models.hpp" +#include "../../../api/Models.hpp" #include "Buffers.hpp" namespace render::vk { diff --git a/src/client/render/vk/forward.hpp b/src/client/render/impl/vk/forward.hpp similarity index 78% rename from src/client/render/vk/forward.hpp rename to src/client/render/impl/vk/forward.hpp index 1712637..674d88b 100644 --- a/src/client/render/vk/forward.hpp +++ b/src/client/render/impl/vk/forward.hpp @@ -1,6 +1,9 @@ #pragma once #include + +#undef LOG_PREFIX +#define LOG_PREFIX "Vulkan: " namespace render { struct renderOptions; } namespace render::vk { struct PhysicalDeviceInfo; diff --git a/src/client/render/index.cpp b/src/client/render/index.cpp index 2dd18fb..6c4e25b 100644 --- a/src/client/render/index.cpp +++ b/src/client/render/index.cpp @@ -1,11 +1,16 @@ -#include "gl/Renderer.hpp" -#include "vk/Renderer.hpp" -#include "../../core/utils/logger.hpp" -#include "../Window.hpp" +#include "impl/gl/Renderer.hpp" +#ifdef RENDER_VK +#include "impl/vk/Renderer.hpp" +#endif +#include "core/utils/logger.hpp" +#include "Window.hpp" +#undef LOG_PREFIX +#define LOG_PREFIX "Render: " namespace render { bool Load(Window& window, bool preferVulkan, const renderOptions& options, const windowOptions& windOpt) { + #ifdef RENDER_VK if(!preferVulkan) { LOG_T("Trying OpenGL"); if(gl::Renderer::Load(window, options, windOpt)) @@ -16,11 +21,15 @@ bool Load(Window& window, bool preferVulkan, const renderOptions& options, const if(vk::Renderer::Load(window, options, windOpt)) return true; window.destroy(); + LOG_I("Fallback to OpenGL"); + #else if(preferVulkan) { - LOG_I("Fallback to OpenGL"); - if(gl::Renderer::Load(window, options, windOpt)) - return true; + LOG_W("Vk render isn't available. Consider compiling with RENDER_VK=1"); + LOG_T("Trying OpenGL"); } + #endif + if(gl::Renderer::Load(window, options, windOpt)) + return true; LOG_E("No available graphics library"); return false; } diff --git a/src/client/logo.cxx b/src/client/render/logo.cxx similarity index 100% rename from src/client/logo.cxx rename to src/client/render/logo.cxx diff --git a/src/client/srvContainer.hpp b/src/client/srvContainer.hpp new file mode 100644 index 0000000..568973d --- /dev/null +++ b/src/client/srvContainer.hpp @@ -0,0 +1,107 @@ +#include "Client.hpp" +#include "../server/config.hpp" +#include +#include +#include "render/UI.hpp" + +struct ServerFactory: public world::AbstractServerFactory { + ServerFactory(config::server::options& options): + options(options) { } + ~ServerFactory() { + if (serverTask.joinable()) + serverTask.join(); + + if (server != nullptr) + delete server; + } + + config::server::options& options; + std::thread serverTask; + Server* server = nullptr; + + world::server_handle* run() override { + if (server != nullptr) { + LOG_E("Previous server still running"); + return server->getHandle(); + } + if (serverTask.joinable()) + serverTask.join(); + + LOG_W("Starting local server"); + server = new Server(options); + serverTask = std::thread([&] { +#if TRACY_ENABLE + tracy::SetThreadName("Server"); +#endif + server->run(); + delete server; + server = nullptr; + }); + return server->getHandle(); + } + + void onGui() override { + const auto running = server && server->getHandle()->running; + { + ImGui::Columns(2, NULL, false); + ImGui::Text("Status: %s", server ? (running ? "running" : "stopping") : "stopped"); + ImGui::NextColumn(); + const auto btnSize = ImVec2(ImGui::GetWindowContentRegionWidth()/2, 0); + if (running) { + if (ImGui::Button("Stop", btnSize)) + server->getHandle()->running = false; + } else if (!server) { + if (ImGui::Button("Start", btnSize)) + run(); + } + ImGui::Columns(); + } + ImGui::Separator(); + { + int load = options.world.loadDistance; + int keep = options.world.keepDistance; + if (ImGui::SliderInt("Load distance", &load, 1, options.world.keepDistance) | + ImGui::SliderInt("Keep distance", &keep, options.world.loadDistance + 1, 21)) { + options.world.loadDistance = load; + options.world.keepDistance = keep; + } + ImGui::InputText("Path", &options.world.folderPath); + int flood = options.world.floodFillLimit; + if (ImGui::InputInt("Max part size", &flood, 1, 128)) { + options.world.floodFillLimit = flood; + } + } + if (ImGui::CollapsingHeader("Connection", ImGuiTreeNodeFlags_DefaultOpen)) { + if (running) { + ImGui::PushStyleVarDisabled(); + } + const auto textFlags = running ? ImGuiInputTextFlags_ReadOnly : 0; + ImGui::InputText("Host", &options.connection.host, textFlags); + ImGui::InputInt("Port", &options.connection.port, textFlags); + ImGui::InputText("Key", &options.connection.key, textFlags); + ImGui::InputText("Cert", &options.connection.cert, textFlags); + int max_con = options.connection.max_connections; + if (ImGui::InputInt("Max connections", &max_con) && !running) { + options.connection.max_connections = max_con; + } + ImGui::Checkbox("Allow local", &options.allowLocal); + if (running) { + ImGui::PopStyleVar(); + } + } + if (ImGui::CollapsingHeader("Clock", ImGuiTreeNodeFlags_DefaultOpen)) { + int tps = options.tps; + if (ImGui::SliderInt("Tick per second", &tps, 1, 60)) { + options.tps = tps; + } + int upt = options.upt; + if (ImGui::SliderInt("Update per tick", &upt, 1, 10)) { + options.upt = upt; + } + int rng = options.world.randomTick; + if (ImGui::SliderInt("Random tick", &rng, 1, 1024)) { + options.world.randomTick = rng; + } + } + } +}; \ No newline at end of file diff --git a/src/client/state.hpp b/src/client/state.hpp index c675e5a..100fcbe 100644 --- a/src/client/state.hpp +++ b/src/client/state.hpp @@ -1,14 +1,25 @@ #pragma once #include "config.hpp" +#include "core/world/server_handle.hpp" +#include "core/queue/circular_buffer.hpp" namespace state { /// Live state struct state { + bool playing = false; + world::client::Universe::login login{}; + + bool useSrv = false; + world::AbstractServerFactory *srvContainer = nullptr; + bool capture_mouse = true; - camera_pos position = camera_pos(voxel_pos(0), 1); - std::optional look_at = {}; + struct { + world::relative_transform relative = world::relative_transform{generational::id(), world::transform()}; + world::transform absolute = world::transform(); + } position; + world::Universe::query_result look_at = {}; bool can_fill = true; contouring::Abstract* contouring; @@ -19,8 +30,14 @@ struct state { }; struct { std::array buffer = {'\0'}; - std::vector lines; + data::circular_buffer lines; } console; + + struct { + bool aabb = false; + bool obb = false; + bool chunk = false; + } indicators; }; /// Readonly metrics diff --git a/src/client/world/Area.hpp b/src/client/world/Area.hpp index 3a574d8..b1414a5 100644 --- a/src/client/world/Area.hpp +++ b/src/client/world/Area.hpp @@ -1,21 +1,21 @@ #pragma once -#include "../../core/world/Area.hpp" +#include "core/world/Area.hpp" namespace world::client { - /// Area (aka big group of client::Chunk) + /// Client size Area (aka big group of client::Chunk) struct Area final: public world::Area { public: - Area(const params& p): world::Area(p.center, p.radius), curvature(p.curvature) { } + Area(const params& p): world::Area(p.radius), curvature(p.curvature) { } std::optional getCurvature() const override { return curvature; } void update(const params& p) { - assert(getChunks().getRadius() == p.radius); - center = p.center; + setChunks().resize(p.radius); curvature = p.curvature; } + /// Same as server::Region averages of a server::Area robin_hood::unordered_map> regionCache; private: diff --git a/src/client/world/Chunk.hpp b/src/client/world/Chunk.hpp index be376ec..a1c371a 100644 --- a/src/client/world/Chunk.hpp +++ b/src/client/world/Chunk.hpp @@ -1,16 +1,17 @@ #pragma once -#include "../../core/world/EdittableChunk.hpp" +#include "core/world/EdittableChunk.hpp" namespace world::client { -class Chunk final: public EdittableChunk { +/// ChunkEdits with passive rollback +class ChunkFutureEdits: public ChunkEdits { public: - Chunk(std::istream &is): world::Chunk(is) { } - /// Create from average - Chunk(Voxel val): EdittableChunk(), isAverage(true), isMajorant(val.swap()) { voxels.fill(Voxel(val.material(), val.density())); } - Chunk(): world::Chunk(), EdittableChunk() { } + ChunkFutureEdits(world::EdittableChunk* ptr): ChunkEdits(ptr) { } + ~ChunkFutureEdits() { } + /// Update voxels + /// @return if modified neighbors to update std::optional update(float deltaTime, bool animate) override { for (auto it = futureEdits.begin(); it != futureEdits.end();) { it->second.second -= deltaTime; @@ -22,26 +23,38 @@ public: ++it; } } - return EdittableChunk::update(deltaTime, animate); + return ChunkEdits::update(deltaTime, animate); } + /// Add future edit + void addFutureEdit(const ChunkEdits::Edit& edit, float delay) { futureEdits.insert_or_assign(edit.idx, std::make_pair(EditBody(edit), delay)); } + +private: + /// Temporary animated changes + robin_hood::unordered_map> futureEdits; +}; + +/// Special chunk with additional flags for client only +class Chunk final: public world::EdittableChunk { +public: + Chunk(std::istream &is): EdittableChunk(new ChunkFutureEdits(this), is) { } + /// Create from average + Chunk(Voxel val): EdittableChunk(new ChunkFutureEdits(this)), isAverage(true), isMajorant(val.swap()) { voxels.fill(Voxel(val.material(), val.density())); } + Chunk(): EdittableChunk(new ChunkFutureEdits(this)) { } + constexpr bool isTrusted(bool allowMajorant) const { return !isAverage || (allowMajorant && isMajorant); } void unsetMajorant() { assert(isMajorant); isMajorant = false; } - /// Add future edit - void addFutureEdit(Chunk::Edit edit, float delay) { futureEdits.insert_or_assign(edit.idx, std::make_pair(EditBody(edit), delay)); } + ChunkFutureEdits &setEdits() { assert(edits); return *(ChunkFutureEdits*)edits.get(); } -protected: +private: /// Is temporary average const bool isAverage = false; /// Is temporary full valued bool isMajorant = false; - - /// Temporary animated changes - robin_hood::unordered_map> futureEdits; }; /// Chunk full of air diff --git a/src/client/world/DistantUniverse.cpp b/src/client/world/DistantUniverse.cpp index da765a5..530f672 100644 --- a/src/client/world/DistantUniverse.cpp +++ b/src/client/world/DistantUniverse.cpp @@ -4,93 +4,116 @@ #include "Area.hpp" #include "../contouring/Abstract.hpp" -#include "../../core/world/raycast.hpp" -#include "../../core/world/iterators.hpp" -#include "../../core/net/io.hpp" -#include "../../core/utils/logger.hpp" +#include "core/world/iterators.hpp" +#include "core/utils/logger.hpp" +#include "core/utils/io.hpp" #include "Chunk.hpp" #include #include +#undef LOG_PREFIX +#define LOG_PREFIX "Client: " + using namespace world::client; -DistantUniverse::DistantUniverse(const connection& ct, const Universe::options& opt, const std::string& contouring): +DistantUniverse::DistantUniverse(const Universe::login& login, const connection& ct, const Universe::options& opt, const std::string& contouring): Universe(contouring), options(opt), serverDistance(0), - peer(ct, [&](const data::out_view& buf, net::PacketFlags flags){ return onPacket(buf, flags); }) { } + peer(ct, [&](const memory::read_view& buf, net::PacketFlags flags){ return onPacket(buf, flags); }) +{ + { + auto packet = memory::write_buffer(net::client_packet_type::HELLO, sizeof(uint8_t) + (login.name.length()+1) + login.token.length()); + packet.write(login.token.empty() ? 0 : 64); + packet.write(login.name.c_str(), login.name.length()+1); + if (!login.token.empty()) + packet.write(login.token.data(), login.token.size()); + + peer.send(packet.finish()); + } +} DistantUniverse::~DistantUniverse() { } void DistantUniverse::update(voxel_pos pos, float deltaTime) { - const auto cur_chunk = glm::divide(pos); - const auto chunkChange = cur_chunk != last_chunk; - last_chunk = cur_chunk; - { //NOTE: triggers onPacket ZoneScopedN("Pull"); peer.pull(); } + if (options.movePrediction) { + elements.hierarchy.for_each([&](Elements::hierarchy_entry &entry) { + //vel += acc * deltaTime + entry.relative.position += entry.vel.position * deltaTime; + entry.relative.rotation = glm::slerp(identity_pivot, entry.vel.rotation, deltaTime) * entry.relative.rotation; + + const auto node = elements.nodes.directly_at(entry.self); + node->absolute = elements.computeAbsolute(entry.parent, entry.relative); + //TODO: full physics + }); + } + + const auto notifyTick = contouringNotifier.mustNotify(pos, deltaTime); + { // Update alive areas ZoneScopedN("World"); auto rng = std::mt19937(std::rand()); const auto contouringThreshold = UINT32_MAX / (1 + contouring->getQueueSize()); - for (auto& area: areas) { + for (auto& ref: elements.areas) { ZoneScopedN("Area"); - const bool chunkChangeArea = (false && area.second->move(glm::vec3(deltaTime))) || chunkChange; // TODO: area.velocity - const chunk_pos diff = glm::divide(pos - area.second->getOffset().as_voxel()); - auto &chunks = area.second->setChunks(); - if (glm::length2(diff) <= glm::pow2(options.keepDistance + area.second->getChunks().getRadius())) { + const area_id id = elements.withFlag(ref).val; + const auto node = elements.findArea(id); + const chunk_pos areaOff = glm::divide((lpivot)glm::conjugate(node->absolute.rotation) * ((world_pos)pos - node->absolute.position)); + auto &chunks = node->get()->setChunks(); + if (glm::length2(areaOff) <= glm::pow2(options.keepDistance + chunks.getRadius())) { ZoneScopedN("Alive"); - auto it_c = chunks.begin(); - while (it_c != chunks.end()) { - if (glm::length2(diff - it_c->first) > glm::pow2(options.keepDistance)) { - it_c = chunks.erase(it_c); - } else { - if(const auto neighbors = std::dynamic_pointer_cast(it_c->second)->update(deltaTime, rng() < contouringThreshold)) { - contouring->onUpdate(std::make_pair(area.first, it_c->first), diff, chunks, neighbors.value()); - } + for (auto it_c = chunks.begin(); it_c != chunks.end();) { + const cell_pos chunkPos = node->absolute.computeChild(glm::multiply(it_c->first)); + const auto chunkDist = glm::length2(glm::divide(chunkPos - pos)); + if (chunkDist <= glm::pow2(options.keepDistance)) { + if (const auto neighbors = it_c->second->setEdits()->update(deltaTime, rng() < contouringThreshold)) { + contouring->onUpdate(area_chunk_pos{id.val, it_c->first}, chunkDist, chunks, neighbors.value()); + } //MAYBE: if notifyTick ++it_c; + } else { + it_c = chunks.erase(it_c); } } - //FIXME: Produce important duplicated (chunkChangeArea) //MAYBE: if dist to unloaded chunk < X - if (chunkChangeArea || mayQueryChunks) { // Request missing chunks + if (mayQueryChunks && notifyTick) { // Request missing chunks ZoneScopedN("Missing"); std::vector missingChunks; std::vector missingRegions; std::vector missingDist; - const auto cl_area = std::dynamic_pointer_cast(area.second); + const auto area = std::static_pointer_cast(node->get()); //TODO: use easy sphere fill - const auto queryDistance = std::min(options.loadDistance, serverDistance); + const auto queryDistance = getQueryDistance(); for (int x = -queryDistance; x <= queryDistance; x++) { for (int y = -queryDistance; y <= queryDistance; y++) { for (int z = -queryDistance; z <= queryDistance; z++) { const auto dist2 = x * x + y * y + z * z; - const auto p = diff + chunk_pos(x, y, z); - if (dist2 <= queryDistance * queryDistance && chunks.inRange(p)) { - if (auto it = chunks.find(p); it != chunks.end() && - std::dynamic_pointer_cast(it->second)->isTrusted(options.trustMajorant)) - //TODO: config accept perfect average + const auto p = areaOff + chunk_pos(x, y, z); + if (dist2 <= glm::pow2(queryDistance) && chunks.inRange(p)) { + if (const auto it = chunks.find(p); it != chunks.end() && + std::static_pointer_cast(it->second)->isTrusted(options.trustMajorant)) { - contouring->onNotify(std::make_pair(area.first, p), diff, chunks); + contouring->onNotify(area_chunk_pos{id.val, p}, dist2, chunks); continue; } const auto rcPos = glm::split(p); - if(auto it_r = cl_area->regionCache.find(rcPos.first); it_r != cl_area->regionCache.end()) { + if(auto it_r = area->regionCache.find(rcPos.first); it_r != area->regionCache.end()) { if (auto it_rc = it_r->second.find(rcPos.second); it_rc != it_r->second.end() && (it_rc->second.swap() || options.useAverages)) { auto ck = std::make_shared(it_rc->second); - ck->invalidate(geometry::Faces::All); - chunks.emplace(p, std::dynamic_pointer_cast(ck)); - contouring->onNotify(std::make_pair(area.first, p), diff, chunks); + ck->setEdits().invalidate(geometry::Faces::All); + chunks.emplace(p, ck); + contouring->onNotify(area_chunk_pos{id.val, p}, dist2, chunks); if (ck->isTrusted(options.trustMajorant)) continue; } } - const region_pos rPos = glm::divide(p); - if (!cl_area->regionCache.contains(rPos)) { - cl_area->regionCache.emplace(rPos, 0); - missingRegions.push_back(rPos); + if (!area->regionCache.contains(rcPos.first) && + std::find(missingRegions.begin(), missingRegions.end(), region_pos(rcPos.first)) == missingRegions.end()) + { + missingRegions.push_back(rcPos.first); } if (missingDist.size() >= net::MAX_PENDING_CHUNK_COUNT) { if (dist2 > missingDist.front()) @@ -112,43 +135,83 @@ void DistantUniverse::update(voxel_pos pos, float deltaTime) { } }}} if(!missingChunks.empty()) { - auto packet = net::PacketWriter(net::client_packet_type::MISSING_CHUNKS, sizeof(area_id) + missingChunks.size() * sizeof(chunk_pos)); - packet.write(area.first); + LOG_T("Missing chunks " << missingChunks.size()); + auto packet = memory::write_buffer(net::client_packet_type::REQUEST_CHUNKS, sizeof(node_id) + missingChunks.size() * sizeof(chunk_pos)); + packet.write(id.val); packet.write(missingChunks.data(), missingChunks.size() * sizeof(chunk_pos)); peer.send(packet.finish()); + mayQueryChunks = false; } if(!missingRegions.empty()) { - auto packet = net::PacketWriter(net::client_packet_type::MISSING_REGIONS, sizeof(area_id) + missingRegions.size() * sizeof(region_pos)); - packet.write(area.first); + auto packet = memory::write_buffer(net::client_packet_type::REQUEST_REGIONS, sizeof(area_id) + missingRegions.size() * sizeof(region_pos)); + packet.write(id.val); packet.write(missingRegions.data(), missingRegions.size() * sizeof(region_pos)); peer.send(packet.finish()); } - mayQueryChunks = false; } + } else if(notifyTick) { + chunks.clear(); + chunks.rehash(0); + auto& regions = std::static_pointer_cast(node->get())->regionCache; + regions.clear(); + regions.rehash(0); } } } - if (options.movePrediction) { - entities.remove([&](size_t eId, Entity &entity) { - entity.instances.remove([&](size_t iId, Universe::Entity::Instance &inst) { - if (eId == PLAYER_ENTITY_ID.index && iId == playerId) - return false; - - // MAYBE: leap after entities packet - inst.pos += inst.velocity * deltaTime; - return false; - }); - return false; + //TODO: remove out of range (instances, parts, null node) + if (notifyTick) { + const auto queryDist2 = glm::pow2(getQueryDistance()); + for(const auto& ref: elements.parts) { + const part_id id = elements.withFlag(ref).val; + const auto node = elements.findPart(id); + const auto dist = glm::length2(glm::divide((cell_pos)node->absolute.position - pos)); + if (dist <= glm::pow2(options.keepDistance)) { + const auto part = node->get(); + if (part->loaded()) { + contouring->onNotify(id, dist, part->chunkSize(), part->chunks, false); + } else if (dist <= queryDist2) { + const auto fullCount = part->chunkCount(); + const auto &chunks = part->chunks; + size_t size = 0; + for (size_t i = 0; i < fullCount; i++) { + if (i >= chunks.size() || chunks[i] == nullptr) + size++; + } + LOG_T("Missing chunks in part " << size); + auto packet = memory::write_buffer(net::client_packet_type::REQUEST_CHUNKS, sizeof(node_id) + size * sizeof(chunk_pos)); + packet.write(id.val); + for (size_t i = 0; i < fullCount; i++) { + if (i >= chunks.size() || chunks[i] == nullptr) { + packet.write(part->getPos(i)); + } + } + peer.send(packet.finish()); + } + } else { + auto &chunks = node->get()->chunks; + chunks.clear(); + chunks.shrink_to_fit(); + } + } + //TODO: remove temporary on last instance removal + elements.models.iter([&] (const model_id& id, const Model& model) { + if (!model.instances.empty()) { + io::vec_istream idata(model.dt); + std::istream iss(&idata); + contouring->onLoad(id, iss); + } }); } - contouring->update(pos, areas); + contouring->update(pos, elements); } -bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) { +bool DistantUniverse::onPacket(const memory::read_view& buf, net::PacketFlags) { using namespace net; - auto packet = PacketReader(buf); + //MAYBE: client::Serializer + + auto packet = memory::read_buffer(buf, nullptr); server_packet_type type; if(!packet.read(type)) { LOG_D("Empty packet"); @@ -156,15 +219,10 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) { } switch (type) { case server_packet_type::QUIT: { - bool is_app = false; - is_app &= packet.read(is_app); - uint16_t reason = 0; + uint16_t reason = 42; packet.read(reason); - if (is_app) { - LOG_E("Disconnected from server (" << reason << ')'); - } else { - LOG_D("Connection lost (" << reason << ')'); - } + LOG_E("Disconnected from server (" << reason << ')'); + peer.disconnect(); break; } @@ -178,7 +236,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) { options.editHandling = false; options.editPrediction = false; } - auto packet = PacketWriter(client_packet_type::CAPABILITIES, sizeof(bool)); + auto packet = memory::write_buffer(client_packet_type::CAPABILITIES, sizeof(bool)); packet.write(options.editHandling); peer.send(packet.finish()); break; @@ -193,9 +251,13 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) { } case server_packet_type::TELEPORT: { - voxel_pos pos; - if (packet.read(playerId) && packet.read(pos)) - onTeleport(pos); + relative_transform pos; + if (packet.read(playerId) && packet.read(pos)) { + if (!elements.withFlag(playerId)) { + LOG_W("FIXME: store if instance not reserved yet"); + } + onTeleport(pos, getAbsolute(pos)); + } break; } @@ -205,42 +267,27 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) { break; } - case server_packet_type::AREAS: { - while(!packet.isDone()) { - area_id id; - world::Area::params p; - if(!packet.read(id) || !packet.read(p)) - break; - - if (auto it = areas.find(id); it != areas.end()) { - std::dynamic_pointer_cast(it->second)->update(p); - } else { - areas.emplace(id, std::make_shared(p)); - } - } - break; - } - case server_packet_type::REGION: { ZoneScopedN("Region"); - area_ pos; + area_region_ref pos(area_ref(), region_pos(0)); if(!packet.read(pos)) break; - auto it = areas.find(pos.first); - if(it == areas.end()) { - LOG_W("Region area not found " << pos.first.index); + const auto id = elements.nodes.with_flag(pos.area.val); + if (!Elements::Is(id)) + break; + + const auto node = NodeOf::Make(elements.nodes.directly_at(id)); + if (!node) { + LOG_W("Region area not found " << pos.area.val); break; } - auto area = std::dynamic_pointer_cast(it->second); - auto it_r = area->regionCache.find(pos.second); - if (it_r == area->regionCache.end()) { - it_r = area->regionCache.emplace(pos.second, 0).first; + auto ®ions = node->get()->regionCache; + auto it_r = regions.find(pos.region); + if (it_r == regions.end()) { + it_r = regions.emplace(pos.region, 0).first; } - // MAYBE: use Voxel swag bit to flag full void/air chunks to avoiding MISSING_CHUNKS - // MAYBE: then create virtual or non chunk of single material - uint16_t full = 0; - uint16_t total = 0; + // MAYBE: create virtual Chunk without voxels of single material while (!packet.isDone()) { region_chunk_pos cpos; Voxel voxel; @@ -248,9 +295,6 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) { break; it_r->second.insert_or_assign(cpos, voxel); - total++; - if (voxel.swap()) - full++; } break; } @@ -265,35 +309,56 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) { break; } - area_ pos; + node_chunk_ref pos{node_ref(), chunk_pos(0)}; if(!packet.read(pos)) break; - auto it = areas.find(pos.first); - if(it == areas.end()) { - LOG_W("Chunk area not found " << pos.first.index); - break; - } - if(!it->second->getChunks().inRange(pos.second)) { - LOG_W("Chunk out of area " << pos.first.index); + const auto id = elements.nodes.with_flag(pos.node.val); + const auto node = elements.nodes.directly_at(id); + if (!node) { + LOG_W("node not found " << pos.node.val); break; } - std::vector buffer; - if(auto err = dict.value().decompress(packet.readRemaining(), buffer)) { - LOG_E("Corrupted chunk packet " << err.value()); + const auto readChunk = [&] { + std::vector buffer; + if (auto err = dict.value().decompress(packet.readRemaining(), buffer)) { + LOG_E("Corrupted chunk packet " << err.value()); + return std::shared_ptr(nullptr); + } + io::vec_istream idata(buffer); + std::istream iss(&idata); + return std::make_shared(iss); + }; + + switch (Elements::GetType(id)) { + case Elements::Type::Area: { + const auto area = NodeOf::Make(node)->get(); + if(!area->getChunks().inRange(pos.chunk)) { + LOG_W("Chunk out of area " << pos.node.val); + break; + } + if (auto ck = readChunk()) { + area->setChunks().insert_or_assign(pos.chunk, ck); + } break; } - data::vec_istream idata(buffer); - std::istream iss(&idata); - auto ck = std::make_shared(iss); - ck->invalidate(geometry::Faces::All); - auto ptr = std::dynamic_pointer_cast(ck); - auto &chunks = it->second->setChunks(); - if (auto it_c = chunks.find(pos.second); it_c != chunks.end()) { - it_c->second = ptr; - } else { - chunks.emplace(pos.second, ptr); + case Elements::Type::Part: { + const auto part = NodeOf::Make(node)->get(); + if(!part->inRange(pos.chunk)) { + LOG_W("Chunk out of part " << pos.node.val); + break; + } + if (auto ck = readChunk()) { + part->allocate(); + part->chunks.at(part->getIdx(pos.chunk)) = ck; + } + break; + } + + default: + LOG_W("Not chunk holding node type"); + break; } break; } @@ -309,149 +374,163 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) { if (!fill) break; - world::iterator::ApplySplit(areas, *fill, floodfillLimit, [](std::shared_ptr &ck, chunk_pos, chunk_voxel_idx idx, - Voxel, Voxel next, float delay) { ck->apply(Chunk::Edit{next, delay, idx}); }); + const auto anchor_id = elements.withFlag(fill->pos.node); + const auto node = elements.findNode(anchor_id); + if (!node) + break; + //MAYBE: make it void after air propagation setup + const auto cleanVoxel = Voxel(0, Voxel::DENSITY_MAX); + switch (Elements::GetType(anchor_id)) { + case Elements::Type::Area: { + const auto& chunks = NodeOf::Make(node)->get()->getChunks(); + world::iterator::ApplyEraseSplitted(chunks, *fill, floodfillLimit, cleanVoxel, + [&](std::shared_ptr& ck, const chunk_pos&, chunk_voxel_idx idx, Voxel/*prev*/, Voxel next, float delay) { + return ck->setEdits().apply(ChunkEdits::Edit{next, delay, idx}); + }); + break; + } + /* TODO: case Elements::Type::Part: { + const auto& chunks = *NodeOf::Make(node)->get()->chunks; + world::iterator::ApplyEraseSplitted(chunks, *fill, floodfillLimit, cleanVoxel, + [&](std::shared_ptr& ck, const chunk_pos&, chunk_voxel_idx idx, Voxel prev, Voxel next, float delay) { + return ck->replace(ChunkEdits::Edit{next, delay, idx}); + }); + break; + }*/ + default: + LOG_E("Unhandled fill target type " << (int)Elements::GetType(anchor_id)); + break; + } break; } case server_packet_type::RAW_EDITS: { ZoneScopedN("Raw Edits"); - area_id id; - if(!packet.read(id)) + node_ref ref; + if(!packet.read(ref)) break; - auto it = areas.find(id); - if(it == areas.end()) { - LOG_W("Edit area not found " << id.index); + const auto id = elements.nodes.with_flag(ref.val); + const auto node = elements.nodes.directly_at(id); + if (!node) { + LOG_W("node not found " << ref.val); break; } - while(!packet.isDone()) { - chunk_pos pos = chunk_pos(INT_MAX); - packet.read(pos); - chunk_voxel_idx count = 0; - packet.read(count); - if (auto ptr = it->second->setChunks().findInRange(pos)) { - auto chunk = std::dynamic_pointer_cast(ptr.value()); - for (auto i = 0; i < count && !packet.isDone(); i++) { - Chunk::Edit edit; - packet.read(edit); - chunk->apply(edit); + const auto readEdits = [&](std::function(const chunk_pos&)> findInRange) { + while(!packet.isDone()) { + chunk_pos pos = chunk_pos(INT_MAX); + packet.read(pos); + chunk_voxel_idx count = 0; + packet.read(count); + if (auto chunk = findInRange(pos)) { + const auto edits = chunk->setEdits(); + for (auto i = 0; i < count && !packet.isDone(); i++) { + ChunkEdits::Edit edit; + packet.read(edit); + edits->apply(edit); + } + } else { + packet.skip(count * sizeof(ChunkEdits::Edit)); } - } else { - packet.skip(count * sizeof(Chunk::Edit)); } - } - break; - } + }; - case server_packet_type::ENTITY_TYPES: { - entities.clear(); - while(!packet.isDone()) { - size_t index; - glm::usvec3 size; - glm::vec3 scale; - uint8_t flags; - if(packet.read(index) && packet.read(size) && packet.read(scale) && packet.read(flags)) { - const bool permanant = (flags & (1 << 0)) != 0; - const bool area = (flags & (1 << 1)) != 0; - entities.set(index, Entity(size, scale, permanant, area)); - //MAYBE: just update - } + switch (Elements::GetType(id)) { + case Elements::Type::Area: { + const auto area = NodeOf::Make(node)->get(); + const auto& chunks = area->setChunks(); + readEdits([&](const world::chunk_pos &pos) { + return chunks.findInRange(pos); + }); + break; + } + case Elements::Type::Part: { + const auto part = NodeOf::Make(node)->get(); + readEdits([&](const world::chunk_pos &pos) { + return part->findInRange(pos); + }); + break; + } + + default: + LOG_W("Not chunk holding node type"); + break; } break; } case server_packet_type::ENTITIES: { - entities.remove([](size_t, Entity &entity) { entity.instances.clear(); return false; }); while(!packet.isDone()) { - size_t entity_idx; - size_t count; - if (!(packet.read(entity_idx) && packet.read(count))) + node_id id; + if (!packet.read(id)) break; - if (auto entity = entities.get(entity_idx)) { - if (count == 0) - continue; - for (size_t i = 0; i < count && !packet.isDone(); i++) { - size_t idx; - glm::ifvec3 pos; - glm::vec3 vel; - if (packet.read(idx) && packet.read(pos) && packet.read(vel)) - entity->instances.set_emplace(idx, Universe::Entity::Instance{pos, vel}); - } - if (dict.has_value()) { - if (auto area = std::get_if(&entity->shape)) { - if (area->capacity()) - continue; - - const auto scale = glm::lvec3(1) + glm::divide(entity->size); - area->reserve(scale.x * scale.y * scale.z); - } else if (auto model = std::get_if(&entity->shape)) { - if (model->capacity()) - continue; - - model->reserve(8); - } - peer.send(PacketWriter::Of(client_packet_type::MISSING_ENTITY, entity_idx)); - } - } else { - LOG_W("Unknown entity type " << entity_idx); - packet.skip(count * (sizeof(size_t) + sizeof(glm::ifvec3) + sizeof(glm::vec3))); - } - } - entities.remove([&](size_t id, const Entity &entity) { - if (!entity.permanant && entity.instances.empty()) { - contouring->onEntityUnload(id); - return true; - } - return false; - }); - break; - } - - case server_packet_type::ENTITY_SHAPE: { - assert(dict.has_value()); - - size_t id; - bool is_area; - if (!packet.read(id) || !packet.read(is_area)) - break; - - if (auto it = entities.get(id)) { - if (is_area) { - auto area = std::get_if(&it->shape); - assert(area); - area->clear(); - while (!packet.isDone()) { - std::vector buffer; - if(auto err = dict.value().decompress(packet.readSizedPart(), buffer)) { - LOG_E("Corrupted entity chunk packet " << err.value()); - break; - } - data::vec_istream idata(buffer); - std::istream iss(&idata); - area->push_back(std::make_shared(iss)); - } - auto scale = glm::lvec3(1) + glm::divide(it->size); - if ((long)area->size() != scale.x * scale.y * scale.z) { - LOG_E("Corrupted entity area " << area->size() << "!=" << (scale.x * scale.y * scale.z)); + const auto added = Elements::Has(id); + if (Elements::Has(id) || added) { + MyElements::start_point state = MyElements::start_point(generational::id(), transform()); + if (!packet.read(state)) break; + + if (added) { + switch (Elements::GetType(id)) { + case Elements::Type::Area: { + Area::params params; + if (packet.read(params)) { + if (elements.withFlag(id)) { + LOG_D("Added entity already exists " << id.val.val); + break; + } + id = elements.createAt(id, state, std::make_shared(params)); + elements.areas.push_back(id); + } + break; + } + case Elements::Type::Part: { + glm::usvec3 size; + if (packet.read(size)) { + if (elements.withFlag(id)) { + LOG_D("Added entity already exists " << id.val.val); + break; + } + id = elements.createAt(id, state, std::make_shared(size)); + elements.parts.push_back(id); + } + break; + } + case Elements::Type::Instance: { + model_id mid; + if (packet.read(mid)) { + const auto model = elements.models.directly_at(mid); + if (!model) { + LOG_E("FIXME: must query model"); + break; + } + if (elements.withFlag(id)) { + LOG_D("Added entity already exists " << id.val.val); + break; + } + id = elements.createAt(id, state, model->origin ? model->origin : std::make_shared(mid)); + model->instances.push_back(id.val.index()); + } + break; + } + + default: + LOG_W("Unhandled entity type " << id.val.val); + break; + } + } else if (elements.withFlag(id)) { + elements.move(id, state); + } else { + LOG_W("Must query content " << id.val.val); } - contouring->onEntityLoad(id, scale, *area); + } else if (Elements::Has(id)) { + elements.remove(id); } else { - auto model = std::get_if(&it->shape); - assert(model); - auto remain = packet.readRemaining(); - model->resize(remain.size()); - //MAYBE: avoid storing model - memcpy(model->data(), remain.data(), remain.size()); - data::vec_istream idata(*model); - std::istream iss(&idata); - contouring->onEntityLoad(id, iss); + LOG_W("Corrupted entity with id " << id.val.val); + break; } - } else { - LOG_W("Shape for unknown entity type " << id); } break; } @@ -465,54 +544,42 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) { void DistantUniverse::emit(const world::action::packet &action) { if(const auto move = std::get_if(&action)) { - peer.send(net::PacketWriter::Of(net::client_packet_type::MOVE, move->pos), net::client::queue::MOVE, 1); + peer.send(memory::read_buffer::Of(net::client_packet_type::MOVE, move->pos), net::client::queue::MOVE, 1); } else if(const auto message = std::get_if(&action)) { - peer.send(net::PacketWriter::Of(net::client_packet_type::MESSAGE, message->text.data(), message->text.size())); + peer.send(memory::read_buffer::Of(net::client_packet_type::MESSAGE, message->text.data(), message->text.size())); } else if(const auto fill = std::get_if(&action)) { - peer.send(net::PacketWriter::Of(net::client_packet_type::FILL_SHAPE, *fill)); + peer.send(memory::read_buffer::Of(net::client_packet_type::FILL_SHAPE, *fill)); if (options.editPrediction) { ZoneScopedN("Fill"); const auto keepDelay = 5 + (peer.getRTT() / 20000.f); // 5s + 50RTT - world::iterator::ApplySplit(areas, *fill, floodfillLimit, [&](std::shared_ptr &ck, chunk_pos, chunk_voxel_idx idx, - Voxel, Voxel next, float delay) { ck->addFutureEdit(Chunk::Edit{next, keepDelay - delay * 2, idx}, delay); }); + const auto anchor_id = elements.withFlag(fill->pos.node); + const auto node = elements.findNode(anchor_id); + if (!node) + return; + + //MAYBE: make it void after air propagation setup + const auto cleanVoxel = Voxel(0, Voxel::DENSITY_MAX); + switch (Elements::GetType(anchor_id)) { + case Elements::Type::Area: { + const auto& chunks = NodeOf::Make(node)->get()->getChunks(); + world::iterator::ApplyEraseSplitted(chunks, *fill, floodfillLimit, cleanVoxel, + [&](std::shared_ptr& ck, const chunk_pos&, chunk_voxel_idx idx, Voxel/*prev*/, Voxel next, float delay) { + return ck->setEdits().addFutureEdit(ChunkEdits::Edit{next, keepDelay - delay * 2, idx}, delay); + }); + break; + } + default: + LOG_E("Unhandled fill target type " << (int)Elements::GetType(anchor_id)); + break; + } } } else { LOG_W("Bad action " << action.index()); } } -Universe::ray_result DistantUniverse::raycast(const geometry::Ray &ray) const { - return Raycast(ray, areas); -} +DistantUniverse::MyElements::MyElements(): EdittableElements(true) { } -bool DistantUniverse::isAreaFree(const area_ &pos, const geometry::Shape shape, const uint16_t radius) const { - if (const auto it = areas.find(pos.first); it != areas.end()) { - const auto center = pos.second + it->second->getOffset().as_voxel(); - return !entities.contains([&](size_t, const Entity &entity) { - return entity.instances.contains([&](size_t, const Universe::Entity::Instance &inst) { - return geometry::InShape(shape, center, radius, inst.pos.as_voxel(), entity.size); - }); - }); - } else - return false; -} - -void DistantUniverse::getEntitiesModels(const std::function&)>& call, - const std::optional& frustum, const glm::llvec3& offset, int density) -{ - std::vector mats; - entities.iter([&](size_t eId, const Entity &entity) { - entity.instances.iter([&](size_t iId, const Universe::Entity::Instance &inst) { - if (eId == PLAYER_ENTITY_ID.index && iId == playerId) - return; - - glm::mat4 tmp; - if (contouring::Abstract::CullEntity(tmp, entity.size, entity.scale, inst.pos, frustum, offset, density)) - mats.push_back(tmp); - }); - if(!mats.empty()) { - call(eId, mats); - mats.resize(0); - } - }); +world::node_id DistantUniverse::MyElements::createAt(node_id id, const start_point& sp, const std::shared_ptr& el) { + return EdittableElements::createAt(id, sp, el); } \ No newline at end of file diff --git a/src/client/world/DistantUniverse.hpp b/src/client/world/DistantUniverse.hpp index 355f97d..bacae19 100644 --- a/src/client/world/DistantUniverse.hpp +++ b/src/client/world/DistantUniverse.hpp @@ -2,7 +2,8 @@ #include "Universe.hpp" #include "../net/Client.hpp" -#include "../../core/utils/zctx.hpp" +#include "core/utils/zctx.hpp" +#include "core/world/Elements.hpp" namespace world::client { class Area; @@ -10,52 +11,36 @@ namespace world::client { /// Whole universe container in network client class DistantUniverse final: public Universe { public: - DistantUniverse(const connection&, const options &, const std::string&); + DistantUniverse(const login&, const connection&, const options &, const std::string&); ~DistantUniverse(); void update(voxel_pos, float) override; void emit(const action::packet &) override; - ray_result raycast(const geometry::Ray &) const override; - - bool isAreaFree(const area_ &pos, geometry::Shape shape, uint16_t radius) const override; - - void getEntitiesModels(const std::function&)>&, - const std::optional& frustum, const glm::llvec3& offset, int density) override; - bool isRunning() const override { return peer.isRunning(); } - size_t getPlayerId() const override { return playerId; } + world::node_id getPlayerId() const override { return playerId; } + virtual mutex::abstract_shared_guard::handle getElements() const override { return mutex::not_guard::OfShared(&elements); }; protected: - bool onPacket(const data::out_view&, net::PacketFlags); + class MyElements final: public world::EdittableElements { + public: + MyElements(); - /// Alive areas containing chunks - area_map areas; - - /// Entities without generation - struct Entity { - Entity(glm::usvec3 size, glm::vec3 scale, bool permanant, bool area): size(size), scale(scale), permanant(permanant) { - if (area) { - shape = Universe::Entity::area_t(); - } else { - shape = Universe::Entity::model_t(); - } - } - - std::variant shape; - glm::usvec3 size; - glm::vec3 scale; - bool permanant; - - data::generational::view_vector instances; + node_id createAt(node_id, const start_point&, const std::shared_ptr&); }; - data::generational::view_vector entities; + + constexpr uint16_t getQueryDistance() const { return std::min(options.loadDistance, serverDistance); } + + bool onPacket(const memory::read_view&, net::PacketFlags); + + /// Universe client size view + MyElements elements; + /// Player entity instance index - size_t playerId; + world::node_id playerId; std::optional dict; - chunk_pos last_chunk = chunk_pos(INT_MAX); bool mayQueryChunks = false; uint64_t moveCounter = 0; diff --git a/src/client/world/LocalUniverse.cpp b/src/client/world/LocalUniverse.cpp index ce93bbb..784658e 100644 --- a/src/client/world/LocalUniverse.cpp +++ b/src/client/world/LocalUniverse.cpp @@ -1,106 +1,96 @@ #include "LocalUniverse.hpp" -#include "../../core/data/math.hpp" -#include "../../core/data/mem.hpp" +#include "core/geometry/math.hpp" #include "../contouring/Abstract.hpp" -#include "../Window.hpp" -#include "../../core/utils/logger.hpp" -#include "../../core/world/Area.hpp" +#include "../render/Window.hpp" +#include "core/utils/logger.hpp" +#include "core/world/Elements.hpp" +#include "core/utils/io.hpp" +#include + +#undef LOG_PREFIX +#define LOG_PREFIX "Client: " using namespace world::client; -LocalUniverse::LocalUniverse(server_handle *const handle, const std::string& contour): Universe(contour), handle(handle) { +LocalUniverse::LocalUniverse(const Universe::login& login, server_handle *const handle, const std::string& contour): Universe(contour), handle(handle) { assert(handle != nullptr); + handle->playerName = login.name; for (auto i = 0; i < 500 && !handle->running; i++) { LOG_D("Waiting local server startup"); Window::wait(); } - handle->onUpdate = std::function([&](const area_ &pos, const chunk_pos &offset, const world::ChunkContainer &data, geometry::Faces neighbors) { - contouring->onUpdate(pos, this->last_chunk - offset, data, neighbors); - }); + assert(handle->running); } LocalUniverse::~LocalUniverse() { handle->running = false; } -void LocalUniverse::update(voxel_pos pos, float) { +void LocalUniverse::update(cell_pos pos, float deltaTime) { if (handle->teleport.has_value() && onTeleport) { - pos = handle->teleport.value(); - onTeleport(pos); + const auto &rtf = handle->teleport.value(); + const auto abs = getAbsolute(rtf); + pos = abs.position; + onTeleport(rtf, abs); handle->teleport = std::nullopt; } - if (handle->message.has_value() && onMessage) { - onMessage(handle->message.value()); - handle->message = std::nullopt; - } - if (handle->entityChange) { - handle->entities->iter([&](entity_id eId, const Entity &entity) { - if (entity.instances.size() == 0) - return; - - if (auto area = std::get_if(&entity.shape)) { - contouring->onEntityLoad(eId.index, glm::lvec3(1) + glm::divide(entity.size), *area); - } else { - auto model = std::get_if(&entity.shape); - assert(model); - data::vec_istream idata(*model); - std::istream iss(&idata); - contouring->onEntityLoad(eId.index, iss); - } - }); - handle->entityChange = false; + if (onMessage) { + handle->messages.extractor(onMessage); } - const auto cur_chunk = glm::divide(pos); - const auto chunkChange = cur_chunk != last_chunk; - last_chunk = cur_chunk; + const auto notifyTick = contouringNotifier.mustNotify(pos, deltaTime); - if(chunkChange) { - for(const auto& area: *handle->areas) { - const chunk_pos diff = glm::divide(pos - area.second->getOffset().as_voxel()); - for(const auto& chunk: area.second->getChunks()) { - contouring->onNotify(std::make_pair(area.first, chunk.first), diff, area.second->getChunks()); + const auto lock = getElements(); + const auto &elements = *lock; + { // Update alive areas + ZoneScopedN("World"); + auto rng = std::mt19937(std::rand()); + const auto contouringThreshold = UINT32_MAX / (1 + contouring->getQueueSize()); + for (auto& ref: elements.areas) { + const area_id id = elements.withFlag(ref).val; + const auto node = elements.findArea(id); + assert(node); + //MAYBE: if in load range + const auto area_tf = node->absolute; + + const auto area = node->get(); + const auto& chunks = area->getChunks(); + //MAYBE: chunk area change + for(const auto& chunk: chunks) { + //MAYBE: compute absolute manually from chunk_pos(1) * pivot + const cell_pos chunkPos = area_tf.computeChild(glm::multiply(chunk.first)); + const auto chunkDist = glm::length2(glm::divide(chunkPos - pos)); + //MAYBE: if in load range + + if(const auto neighbors = chunk.second->setEdits()->update(deltaTime, rng() < contouringThreshold)) { + contouring->onUpdate(area_chunk_pos{id, chunk.first}, chunkDist, chunks, neighbors.value()); + } else if (notifyTick) { + contouring->onNotify(area_chunk_pos{id, chunk.first}, chunkDist, chunks); + } } } } + if (notifyTick) { + for(const auto& ref: elements.parts) { + const part_id id = elements.withFlag(ref).val; + const auto node = elements.findPart(id); + if (node->get()->loaded()) { + const auto partDist = glm::length2(glm::divide((cell_pos)node->absolute.position - pos)); + contouring->onNotify(id, partDist, node->get()->chunkSize(), node->get()->chunks, false); + } + } + elements.models.iter([&] (const model_id& id, const Model& model) { + if (!model.instances.empty()) { + io::vec_istream idata(model.dt); + std::istream iss(&idata); + contouring->onLoad(id, iss); + } + }); + } - contouring->update(pos, *handle->areas); + contouring->update(pos, elements); } void LocalUniverse::emit(const world::action::packet &packet) { handle->emit(packet); } -Universe::ray_result LocalUniverse::raycast(const geometry::Ray& ray) const { - return handle->raycast(ray); -} -bool LocalUniverse::isAreaFree(const area_ &pos, const geometry::Shape shape, const uint16_t radius) const { - if (const auto it = handle->areas->find(pos.first); it != handle->areas->end()) { - const auto center = pos.second + it->second->getOffset().as_voxel(); - return !handle->entities->contains([&](entity_id, const Entity &entity) { - return entity.instances.contains([&](entity_id, const Entity::Instance &inst) { - return geometry::InShape(shape, center, radius, inst.pos.as_voxel(), entity.size); - }); - }); - } else - return false; -} - -void LocalUniverse::getEntitiesModels(const std::function&)>& call, - const std::optional& frustum, const glm::llvec3& offset, int density) -{ - std::vector mats; - handle->entities->iter([&](entity_id eId, const Entity &entity) { - entity.instances.iter([&](entity_id iId, const Entity::Instance &inst) { - if (eId.index == PLAYER_ENTITY_ID.index && iId.index == PLAYER_ENTITY_ID.index) - return; - - glm::mat4 tmp; - if (contouring::Abstract::CullEntity(tmp, entity.size, entity.scale, inst.pos, frustum, offset, density)) - mats.push_back(tmp); - }); - if(!mats.empty()) { - call(eId.index, mats); - mats.resize(0); - } - }); -} \ No newline at end of file diff --git a/src/client/world/LocalUniverse.hpp b/src/client/world/LocalUniverse.hpp index a0a7f7b..47e2f49 100644 --- a/src/client/world/LocalUniverse.hpp +++ b/src/client/world/LocalUniverse.hpp @@ -1,32 +1,24 @@ #pragma once #include "Universe.hpp" -#include "../../core/server_handle.hpp" +#include "core/world/server_handle.hpp" namespace world::client { /// Whole universe container in client with in-memory server class LocalUniverse final: public Universe { public: - LocalUniverse(server_handle *const handle, const std::string& contouring); + LocalUniverse(const login&, server_handle *const handle, const std::string& contouring); ~LocalUniverse(); - void update(voxel_pos pos, float deltaTime) override; + void update(cell_pos pos, float deltaTime) override; void emit(const action::packet &) override; - ray_result raycast(const geometry::Ray &ray) const override; - - bool isAreaFree(const area_ &pos, geometry::Shape shape, uint16_t radius) const override; - - void getEntitiesModels(const std::function&)>&, - const std::optional& frustum, const glm::llvec3& offset, int density) override; + node_id getPlayerId() const override { return handle->entityId; } + virtual mutex::abstract_shared_guard::handle getElements() const override { return handle->elements.lock_shared_abstract(); }; bool isRunning() const override { return handle->running; } - size_t getPlayerId() const override { return PLAYER_ENTITY_ID.index; } - protected: server_handle *const handle; - - chunk_pos last_chunk = chunk_pos(INT_MAX); }; } \ No newline at end of file diff --git a/src/client/world/Universe.cpp b/src/client/world/Universe.cpp index 4c8d5c1..18b8430 100644 --- a/src/client/world/Universe.cpp +++ b/src/client/world/Universe.cpp @@ -1,8 +1,33 @@ #include "Universe.hpp" #include "../contouring/FlatDualMC.hpp" +#include "core/world/Elements.hpp" using namespace world::client; Universe::Universe(const std::string& ct): world::Universe(), contouring(std::make_unique(ct)) { } Universe::~Universe() { } + +world::transform Universe::getAbsolute(const relative_transform& rtf) const { + return getElements()->computeAbsolute(rtf.parent, rtf.relative); +} + +world::Universe::query_result Universe::raycast(const geometry::Ray &ray) const { + return Raycast(*getElements(), ray, true, getPlayerId()); +} +/// Check for entities in range +bool Universe::isRangeFree(const node_voxel_ref ¢er, const action::Volume& volume) const { + return IsRangeFree(*getElements(), center, geometry::Volume{action::ToGeometry(volume.shape), volume.radius}); +} + +world::relative_transform Universe::tryMovePlayer(const relative_transform& from, const offset_pos& move) const { + const auto dir = glm::normalize(move); + const auto l = getElements(); + const auto abs = l->computeAbsolute(from.parent, from.relative); + //MAYBE: abs.rotation * dir + const auto collide = Raycast(*l, geometry::Ray(abs.position + (world_pos)dir, dir, glm::length(move) + 1), true, getPlayerId()); + if (std::holds_alternative(collide) || std::holds_alternative(collide)) { + return from; + } + return relative_transform{from.parent, transform(from.relative.position + (world_pos)move, from.relative.rotation)}; +} \ No newline at end of file diff --git a/src/client/world/Universe.hpp b/src/client/world/Universe.hpp index 8f26716..578ac63 100644 --- a/src/client/world/Universe.hpp +++ b/src/client/world/Universe.hpp @@ -1,8 +1,10 @@ #pragma once -#include "../../core/world/Universe.hpp" -#include "../../core/world/actions.hpp" -#include "../../core/net/data.hpp" +#include "core/world/Universe.hpp" +#include "core/world/actions.hpp" +#include "core/net/protocol.hpp" +#include "core/utils/mutex.hpp" +#include "../contouring/notifier.hpp" #include namespace contouring { @@ -37,14 +39,18 @@ namespace world::client { struct connection: net::address { connection(): net::address{"127.0.0.1", 4242} { } }; + struct login { + std::string name; + std::string token; + }; /// Update edits and contouring - virtual void update(voxel_pos pos, float deltaTime) = 0; + virtual void update(cell_pos pos, float deltaTime) = 0; /// Send action to ServerUniverse virtual void emit(const action::packet &) = 0; /// When server teleport player - std::function onTeleport; + std::function onTeleport; /// On chat message std::function onMessage; @@ -53,16 +59,22 @@ namespace world::client { return contouring.get(); } - /// Get entities in view - virtual void getEntitiesModels(const std::function&)>&, - const std::optional& frustum, const glm::llvec3& offset, int density) = 0; + transform getAbsolute(const relative_transform&) const; + /// Get nearest thing colliding ray + query_result raycast(const geometry::Ray &ray) const; + /// Check for entities in range + bool isRangeFree(const node_voxel_ref ¢er, const action::Volume&) const; - virtual size_t getPlayerId() const = 0; + relative_transform tryMovePlayer(const relative_transform& from, const offset_pos& move) const; + virtual node_id getPlayerId() const = 0; + virtual mutex::abstract_shared_guard::handle getElements() const = 0; virtual bool isRunning() const = 0; protected: /// Contouring worker std::unique_ptr contouring; + /// Contouring notify tracker + contouring::notifier contouringNotifier; }; } \ No newline at end of file diff --git a/src/client/world/index.cpp b/src/client/world/index.cpp index c857f85..55c7fbb 100644 --- a/src/client/world/index.cpp +++ b/src/client/world/index.cpp @@ -1,18 +1,21 @@ #include "index.hpp" -#ifndef STANDALONE +#ifndef LIGHT_CLIENT #include "LocalUniverse.hpp" #endif #include "DistantUniverse.hpp" -#include "../../core/utils/logger.hpp" +#include "core/utils/logger.hpp" + +#undef LOG_PREFIX +#define LOG_PREFIX "Client: " namespace world::client { - std::unique_ptr Load(const std::optional& ct, server_handle *const localHandle, const Universe::options &distOpts, const std::string& contouring) { + std::unique_ptr Load(const Universe::login& login, const std::optional& ct, server_handle *const localHandle, const Universe::options &distOpts, const std::string& contouring) { if(ct.has_value()) { - return std::make_unique(ct.value(), distOpts, contouring); -#ifndef STANDALONE + return std::make_unique(login, ct.value(), distOpts, contouring); +#ifndef LIGHT_CLIENT } else if(localHandle != nullptr) { LOG_D("Using local universe"); - return std::make_unique(localHandle, contouring); + return std::make_unique(login, localHandle, contouring); #endif } else { FATAL("Must enable server.allow_local or define client.connection"); diff --git a/src/client/world/index.hpp b/src/client/world/index.hpp index 6cf690c..5ed5cff 100644 --- a/src/client/world/index.hpp +++ b/src/client/world/index.hpp @@ -1,8 +1,8 @@ #pragma once #include "Universe.hpp" -#include "../../core/server_handle.hpp" +#include "core/world/server_handle.hpp" namespace world::client { - std::unique_ptr Load(const std::optional& ct, server_handle *const localHandle, const Universe::options& distOpts, const std::string& contouring); + std::unique_ptr Load(const Universe::login& login, const std::optional& ct, world::server_handle *const localHandle, const Universe::options& distOpts, const std::string& contouring); } diff --git a/src/core/data/circular_buffer.hpp b/src/core/data/circular_buffer.hpp deleted file mode 100644 index c0b9598..0000000 --- a/src/core/data/circular_buffer.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include - -/// Generic containers -namespace data { - /// Looping array - template - struct circular_buffer { - circular_buffer(size_t size): size(size), buffer(std::unique_ptr(new T[size])) { } - circular_buffer(size_t size, const T& zero): circular_buffer(size) { - for (size_t i = 0; i < size; i++) { - buffer[i] = zero; - } - } - - size_t last = 0; - size_t size; - std::unique_ptr buffer; - - void push(T in) { - last = (last + 1) % size; - buffer[last] = in; - } - T current() const { - return buffer[last]; - } - }; -} diff --git a/src/core/data/file.hpp b/src/core/data/file.hpp deleted file mode 100644 index 27f627b..0000000 --- a/src/core/data/file.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include -#include "../data/mem.hpp" -#include "../utils/logger.hpp" - -namespace data { - -/// File loader -class file_content: public std::vector { -public: - /// Read first - file_content(const std::vector& paths, data::out_view prefix = data::out_view(nullptr, 0)): - std::vector(), prefix_size(prefix.size()) - { - std::ifstream is = [&]() { - for(auto& path: paths) { - std::ifstream is(path, std::ios::in | std::ios::binary | std::ios::ate); - if(is.good()) { - return is; - } - is.close(); - } - FATAL("File not found " << paths.back()); - }(); - const auto end = is.tellg(); - is.seekg(0, std::ios::beg); - resize(end - is.tellg() + prefix_size); - memcpy(data(), prefix.ptr, prefix_size); - is.read(data() + prefix_size, size()); - is.close(); - } - - size_t prefix_size; - - /// Data after prefix - data::out_view content() const { - return data::out_view((const uint8_t*)data() + prefix_size, size() - prefix_size); - } -}; - -} \ No newline at end of file diff --git a/src/core/data/generational.hpp b/src/core/data/generational.hpp deleted file mode 100644 index ffe0c2c..0000000 --- a/src/core/data/generational.hpp +++ /dev/null @@ -1,315 +0,0 @@ -#pragma once - -#include -#include -#include - -/// Generation indexing and containers (index, generation) -namespace data::generational { - /// Generation element identifier - struct id { - id(size_t index, size_t generation = 0): index(index), generation(generation) { } - id(): id(0) { } - size_t index; - size_t generation; - - bool operator==(const id &i) const { return index == i.index && generation == i.generation; } - }; - - /// Closed generational allocator - class allocator { - public: - allocator() { } - template - allocator(const C& map) { - for(const auto& [key, _]: map) { - if (const auto size = entries.size(); key >= size) { - entries.resize(key + 1); - for (size_t i = size; i < entries.size(); i++) { - entries[i].is_live = false; - freed.push_back(i); - } - } - assert(!entries[key].is_live); - entries[key].is_live = true; - } - } - id alloc() { - if(freed.empty()) { - const auto idx = entries.size(); - entries.emplace_back(); - return id(idx); - } else { - const auto idx = freed.back(); - freed.pop_back(); - auto &entry = entries[idx]; - assert(!entry.is_live); - entry.is_live = true; - return id(idx, ++entries[idx].generation); - } - } - bool is_live(id id) const { - return id.index < entries.size() && - entries[id.index].is_live && - entries[id.index].generation == id.generation; - } - bool free(id id) { - if(!is_live(id)) - return false; - - entries[id.index].is_live = false; - freed.push_back(id.index); - return true; - } - - private: - struct entry { - bool is_live = true; - size_t generation = 0; - }; - std::vector entries; - std::vector freed; - }; - - /// Generational container accessible by id - template - class vector { - public: - vector() { } - template - vector(const C& map) { - for(const auto& [key, value]: map) { - if (const auto size = entries.size(); key >= size) { - entries.resize(key + 1); - } - entries[key].value = value; - } - for (size_t i = 0; i < entries.size(); i++) { - if(!entries[i].value.has_value()) - freed.push_back(i); - } - } - - id push(const T& in) { - if(freed.empty()) { - const auto idx = entries.size(); - entries.emplace_back(in); - return id(idx); - } else { - const auto idx = freed.back(); - freed.pop_back(); - auto &entry = entries[idx]; - assert(!entry.value.has_value()); - entry.value = in; - return id(idx, ++entries[idx].generation); - } - } - template - id emplace(_Args &&... __args) { - if(freed.empty()) { - const auto idx = entries.size(); - entries.emplace_back(std::forward<_Args>(__args)...); - return id(idx); - } else { - const auto idx = freed.back(); - freed.pop_back(); - auto &entry = entries[idx]; - assert(!entry.value.has_value()); - entry.value.emplace(std::forward<_Args>(__args)...); - return id(idx, ++entries[idx].generation); - } - } - bool put(id idx, const T& in) { - if(idx.index >= entries.size()) - return false; - - if(entries[idx.index].generation != idx.generation || entries[idx.index].value.has_value()) - return false; - - entries[idx.index].value = in; - return true; - } - T& at(id idx) { - assert(contains(idx)); - return entries[idx.index].value.value(); - } - T* directly_at(size_t index) { - if (index >= entries.size() || !entries.at(index).value.has_value()) - return nullptr; - - return &entries[index].value.value(); - } - - bool contains(id idx) const { - return idx.index < entries.size() && - entries[idx.index].generation == idx.generation && - entries[idx.index].value.has_value(); - } - - bool free(id idx) { - if(!contains(idx)) - return false; - - entries[idx.index].value = std::nullopt; - freed.push_back(idx.index); - return true; - } - - bool empty() const { - for(auto& v: entries) { - if(v.value.has_value()) - return false; - } - return true; - } - - template - void iter(apply fn) const { - for (size_t i = 0; i < entries.size(); i++) { - const auto &entry = entries[i]; - if(entry.value.has_value()) { - fn(id(i, entry.generation), entry.value.value()); - } - } - } - - template - void for_each(apply fn) { - for (size_t i = 0; i < entries.size(); i++) { - auto &entry = entries[i]; - if(entry.value.has_value()) { - fn(id(i, entry.generation), entry.value.value()); - } - } - } - - template - bool contains(predicate fn) const { - for (size_t i = 0; i < entries.size(); i++) { - const auto &entry = entries[i]; - if(entry.value.has_value() && fn(id(i, entry.generation), entry.value.value())) { - return true; - } - } - return false; - } - - template - void extract(extractor fn) { - for (size_t i = 0; i < entries.size(); i++) { - auto &entry = entries[i]; - if(entry.value.has_value()) { - if(fn(id(i, entry.generation), entry.value.value())) - entry.value = std::nullopt; - } - } - } - - template - void remove(extractor fn) { - for (size_t i = 0; i < entries.size(); i++) { - auto &entry = entries[i]; - if(entry.value.has_value()) { - if(fn(id(i, entry.generation), entry.value.value())) { - entry.value = std::nullopt; - freed.push_back(i); - } - } - } - } - - size_t size() const { - return std::count_if(entries.begin(), entries.end(), - [](const entry &e) { return e.value.has_value(); }); - } - - private: - struct entry { - entry(): value(std::nullopt), generation(0) { } - entry(const T &in, size_t gen = 0): - value(in), generation(gen) { } - template - entry(_Args &&... __args): generation(0) { - value.emplace(std::forward<_Args>(__args)...); - } - std::optional value; - size_t generation; - }; - std::vector entries; - std::vector freed; - }; - - /// Dummy generational container without allocation nor validation. A view to vector - template - class view_vector: public std::vector> { - public: - view_vector(): std::vector>() { } - - template - void set_emplace(size_t i, _Args &&... __args) { - if(i >= this->size()) - this->resize(i + 1); - - this->at(i).emplace(std::forward<_Args>(__args)...); - } - void set(size_t i, const T& in) { - if(i >= this->size()) - this->resize(i + 1); - - this->at(i) = in; - } - - T* get(size_t i) { - if (i >= this->size() || !this->at(i).has_value()) - return nullptr; - - return &this->at(i).value(); - } - - template - void iter(apply fn) const { - for (size_t i = 0; i < this->size(); i++) { - if(this->at(i).has_value()) { - fn(i, this->at(i).value()); - } - } - } - - template - bool contains(predicate fn) const { - for (size_t i = 0; i < this->size(); i++) { - if(this->at(i).has_value() && fn(i, this->at(i).value())) { - return true; - } - } - return false; - } - - void erase(size_t i) { - if(i < this->size()) - this->at(i) = std::nullopt; - } - template - void remove(extractor fn) { - for (size_t i = 0; i < this->size(); i++) { - if(this->at(i).has_value() && fn(i, this->at(i).value())) { - erase(i); - } - } - } - - size_t count() const { - return std::count_if(this->begin(), this->end(), - [](const std::optional &e) { return e.has_value(); }); - } - }; -} - -namespace std { - template<> - struct hash { - std::size_t operator()(const data::generational::id& i) const noexcept { - return std::hash{}(i.index); - } - }; -} \ No newline at end of file diff --git a/src/core/data/glm.cpp b/src/core/data/glm.cpp deleted file mode 100644 index d8585fb..0000000 --- a/src/core/data/glm.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "glm.hpp" -#include "math.hpp" - -using namespace glm; -ifvec3::ifvec3(const llvec3 &pos, u32 density) { - const auto d = IDX_LENGTH2 * density; - raw = glm::divide(pos, glm::uvec3(d)); - offset = glm::vec3(rem(pos.x, d), rem(pos.y, d), rem(pos.z, d)); - if(density > 1) center(); -} -llvec3 ifvec3::raw_as_long() const { - return llvec3(raw) * llvec3(IDX_LENGTH2); -} -llvec3 ifvec3::as_voxel(int density) const { - return raw_as_long() * llvec3(density) + llvec3(offset * vec3(density)); -} -void ifvec3::center() { - const auto diff = glm::divide(glm::llvec3(offset), uvec3(IDX_LENGTH2)); - raw += diff; - offset -= diff * static_cast(IDX_LENGTH2); -} -double ifvec3::dist(const ifvec3& p) const { - return glm::length(glm::dvec3(raw - p.raw)) + glm::length(offset - p.offset); -} -ifvec3 ifvec3::divide(u32 m) const { - return ifvec3(glm::divide(raw, glm::ucvec3(m)), glm::divide(offset, glm::uvec3(m)), false); -} \ No newline at end of file diff --git a/src/core/data/glm.hpp b/src/core/data/glm.hpp deleted file mode 100644 index 78172eb..0000000 --- a/src/core/data/glm.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include - -namespace glm { - typedef vec<3, long long> llvec3; - typedef vec<3, long> lvec3; - typedef vec<3, uint16_t> usvec3; - typedef vec<3, unsigned char> ucvec3; - - const auto IDX_LENGTH = 32; - const auto IDX_LENGTH2 = IDX_LENGTH * IDX_LENGTH; - const auto IDX_SIZE = IDX_LENGTH2 * IDX_LENGTH; - using idx = glm::u16; - - /// Combination of ivec3 and vec3 as a big and high precision - /// Scale to IDX_LENGTH2 - struct ifvec3 { - using raw_t = glm::ivec3; - using offset_t = glm::vec3; - - ifvec3(): raw(0), offset(0) { } - ifvec3(const raw_t &raw, const offset_t &offset, bool recenter = true) : raw(raw), offset(offset) { - if(recenter) center(); - } - ifvec3(const glm::llvec3 &pos, glm::u32 density = 1); - - raw_t raw; - offset_t offset; - - glm::llvec3 as_voxel(int density = 1) const; - void center(); - glm::llvec3 raw_as_long() const; - double dist(const ifvec3 &p) const; - - ifvec3 divide(glm::u32 m = IDX_LENGTH) const; - - inline const ifvec3 &operator+=(const offset_t &v) { - offset += v; - center(); - return *this; - } - inline ifvec3 operator+(const offset_t& v) const { return ifvec3(raw, offset + v); } - inline ifvec3 operator-(const offset_t &v) const { return ifvec3(raw, offset - v); } - inline ifvec3 operator-(const ifvec3 &v) const { return ifvec3(raw - v.raw, offset - v.offset); } - inline ifvec3 operator/(int i) const { return ifvec3(raw / i, offset / (i * 1.f), false); } - inline ifvec3 operator*(int i) const { return ifvec3(raw * i, offset * (i * 1.f)); } - }; -} diff --git a/src/core/data/math.hpp b/src/core/data/math.hpp deleted file mode 100644 index c5ab075..0000000 --- a/src/core/data/math.hpp +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once - -#include "glm.hpp" -#include - -namespace glm { - constexpr ivec3 inline iround(const vec3& p) { - return ivec3(std::round(p.x), std::round(p.y), std::round(p.z)); - } - constexpr float inline length2(const vec3& a) { - return a.x * a.x + a.y * a.y + a.z * a.z; - } - constexpr long inline length2(const lvec3& a) { - return a.x * a.x + a.y * a.y + a.z * a.z; - } - constexpr long long inline length2(const llvec3& a) { - return a.x * a.x + a.y * a.y + a.z * a.z; - } - constexpr long inline pow2(int v) { - return v * v; - } - constexpr long long inline pow2(long v) { - return v * v; - } - constexpr ivec3 inline diff(const ivec3& a, const ivec3& b) { - return glm::abs(glm::abs(a) - glm::abs(b)); - } - - constexpr glm::u32 inline rem(long long value, glm::u32 m) { - return value < 0 ? ((value+1) % (long long)m) + m - 1 : value % (long long)m; - } - constexpr long inline div(long long value, glm::u32 m) { - return value < 0 ? ((value+1) / (long long)m) - 1 : value / (long long)m; - } - constexpr float inline div(float value, glm::u32 m) { - return value < 0 ? ((value+1) / m) - 1 : value / m; - } - constexpr ucvec3 inline modulo(const llvec3& value, const ucvec3& m = ucvec3(IDX_LENGTH)) { - return ucvec3(rem(value.x, m.x), rem(value.y, m.y), rem(value.z, m.z)); - } - constexpr lvec3 inline divide(const llvec3 &value, const uvec3 &m) { - return lvec3(div(value.x, m.x), div(value.y, m.y), div(value.z, m.z)); - } - constexpr lvec3 inline divide(const llvec3 &value, const ucvec3 &m = ucvec3(IDX_LENGTH)) { - return lvec3(div(value.x, m.x), div(value.y, m.y), div(value.z, m.z)); - } - constexpr vec3 inline divide(const vec3 &value, const uvec3 &m) { - return vec3(div(value.x, m.x), div(value.y, m.y), div(value.z, m.z)); - } - constexpr llvec3 inline multiply(const lvec3 &value, const ucvec3 &m = ucvec3(IDX_LENGTH)) { - return llvec3(value) * llvec3(m); - } - constexpr std::pair inline split(const llvec3 &value, const ucvec3 &m = ucvec3(IDX_LENGTH)) { - return {divide(value, m), modulo(value, m)}; - } - constexpr ucvec3 inline fromIdx(idx idx) { - assert(idx < IDX_SIZE); - return ucvec3(idx / IDX_LENGTH2, (idx / IDX_LENGTH) % IDX_LENGTH, idx % IDX_LENGTH); - } - constexpr idx inline toIdx(glm::u8 x, glm::u8 y, glm::u8 z) { - return (x * IDX_LENGTH + y) * IDX_LENGTH + z; - } - constexpr idx inline toIdx(glm::u8 x, glm::u8 y, glm::u8 z, glm::u8 sy, glm::u8 sz) { - return x * sy * sz + y * sz + z; - } - constexpr idx inline toIdx(ucvec3 pos) { - return toIdx(pos.x, pos.y, pos.z); - } - constexpr idx inline toIdx(ucvec3 pos, ucvec3 size) { - return toIdx(pos.x, pos.y, pos.z, size.y, size.z); - } - constexpr std::pair inline splitIdx(const llvec3 &value, const ucvec3 &m = ucvec3(IDX_LENGTH)) { - return {divide(value, m), toIdx(rem(value.x, m.x), rem(value.y, m.y), rem(value.z, m.z))}; - } - template - constexpr T inline max_axis(const vec<3, T> &v) { - return std::max({v.x, v.y, v.z}); - } - template - ivec3 inline inormalize(const vec<3, T> &v) { - const auto a = glm::abs(v); - return a.x >= a.y ? (a.x >= a.z ? ivec3(glm::sign(v.x), 0, 0) : ivec3(0, 0, glm::sign(v.z))) : - (a.y >= a.z ? ivec3(0, glm::sign(v.y), 0) : ivec3(0, 0, glm::sign(v.z))); - } -} diff --git a/src/core/data/toml.cpp b/src/core/data/toml.cpp deleted file mode 100644 index 199bd2f..0000000 --- a/src/core/data/toml.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#define TOML_HEADER_ONLY 0 -#include \ No newline at end of file diff --git a/src/core/generational/core.hpp b/src/core/generational/core.hpp new file mode 100644 index 0000000..3b27b51 --- /dev/null +++ b/src/core/generational/core.hpp @@ -0,0 +1,131 @@ +#pragma once + +#include +#include +#include +#include + +/// Generation indexing and containers (index, generation) +namespace generational { + /// Generation element identifier + struct id { + using type = uint64_t; + using idx_t = uint32_t; + //24bits + using gen_t = uint32_t; + using flag_t = uint_fast8_t; + + id(type v): val(v) { } + id(idx_t index, gen_t generation, flag_t flag = 0): id(merge(index, generation, flag)) { } + /// Null id + id(): id(std::numeric_limits::max()) { } + type val; + + static constexpr size_t TYPE_BITS = sizeof(type) * 8; + static_assert(TYPE_BITS == 64, "Expect generational::id::type to be 64bit"); + static constexpr size_t HALF_BITS = TYPE_BITS / 2; + static constexpr type INDEX_MASK = (1l << HALF_BITS) - 1; + static constexpr size_t INDEX_OFFSET = 0; + static constexpr type GENERATION_MASK = (1l << 3*HALF_BITS/4) - 1; + static constexpr size_t GENERATION_OFFSET = HALF_BITS; + static constexpr type FLAG_MASK = (1l << HALF_BITS/4) - 1; + static constexpr size_t FLAG_OFFSET = 7*HALF_BITS/4; + + constexpr idx_t index() const { return val & INDEX_MASK; } + constexpr gen_t generation() const { return (val >> GENERATION_OFFSET) & GENERATION_MASK; } + constexpr flag_t flag() const { return (val >> FLAG_OFFSET) & FLAG_MASK; } + + static constexpr type merge(idx_t index, gen_t generation = 0, flag_t flag = 0) { + return (index & INDEX_MASK) | ((generation & GENERATION_MASK) << GENERATION_OFFSET) | ((flag & FLAG_MASK) << FLAG_OFFSET); + } + + inline bool operator==(const id &i) const { return index() == i.index() && generation() == i.generation(); } + inline bool operator!=(const id &i) const { return !operator==(i); } + inline operator bool() const { return index() != id().index(); } + }; + + /// Identifier for target element + template + struct key { + key(id i): val(i) { } + key(): key(id()) { } + id val; + + inline bool operator==(const key &k) const { return val == k.val; } + inline bool operator!=(const key &k) const { return val != k.val; } + inline operator bool() const { return val; } + }; + /// Index for target element + template + struct ref { + ref(id::idx_t index): val(index) { } + ref(const key& k): ref(k.val.index()) { } + ref(): ref(id().index()) { } + id::idx_t val; + + inline bool operator==(const ref &r) const { return val == r.val; } + inline bool operator!=(const ref &r) const { return val != r.val; } + inline operator bool() const { return val != id().index(); } + }; + + /// Closed generational allocator + class allocator { + public: + allocator() { } + template + allocator(const C& map) { + for(const auto& [key, _]: map) { + if (const auto size = entries.size(); key >= size) { + entries.resize(key + 1); + for (size_t i = size; i < entries.size(); i++) { + entries[i].set_live(false); + freed.push_back(i); + } + } + assert(!entries[key].is_alive()); + entries[key].set_live(true); + } + } + id alloc() { + if(freed.empty()) { + const auto idx = entries.size(); + entries.emplace_back(); + return id(idx, 0); + } else { + const auto idx = freed.back(); + freed.pop_back(); + auto &entry = entries[idx]; + assert(!entry.is_alive()); + entry.set_live(true); + return id(idx, ++entries[idx]); + } + } + bool is_live(id id) const { + return id.index() < entries.size() && + entries[id.index()].is_alive() && + entries[id.index()].generation() == id.generation(); + } + bool free(id id) { + if(!is_live(id)) + return false; + + entries[id.index()].set_live(false); + freed.push_back(id.index()); + return true; + } + + private: + struct entry { + uint_fast32_t val = 0; + + constexpr id::type generation() const { return val & id::GENERATION_MASK; } + inline id::type operator++() { return (++val) & id::GENERATION_MASK; } + static constexpr size_t DEAD_OFFSET = id::FLAG_OFFSET - id::GENERATION_OFFSET; + static constexpr uint_fast32_t DEAD_MASK = 1l << DEAD_OFFSET; + constexpr bool is_alive() const { return (val & DEAD_MASK) == DEAD_MASK; } + inline void set_live(bool alive) { val = (val & ~DEAD_MASK) | (static_cast(!alive) << DEAD_OFFSET); } + }; + std::vector entries; + std::vector freed; + }; +} diff --git a/src/core/generational/hierarchy.hpp b/src/core/generational/hierarchy.hpp new file mode 100644 index 0000000..5d9a440 --- /dev/null +++ b/src/core/generational/hierarchy.hpp @@ -0,0 +1,206 @@ +#pragma once + +#include "./vector.hpp" + +namespace generational { + + /// Mostly complete reorder of F to T (bidirectional) + template + class heavy_reorder { + public: + struct entry: public T { + entry(ref r, const T &in): T(in), self(r) { } + template + entry(ref r, _Args &&... __args): + T(std::forward<_Args>(__args)...), self(r) { } + + ref self; + }; + + std::optional> find(ref r) const { + if (const auto idx = map.get(r)) { + return *idx; + } + return std::nullopt; + } + entry* get(ref r) { + if (const auto idx = map.get(r)) { + assert(entries[*idx].self == r); + return &entries[*idx]; + } + return nullptr; + } + const entry* get(ref r) const { + if (const auto idx = map.get(r)) { + assert(entries[*idx].self == r); + return &entries[*idx]; + } + return nullptr; + } + entry* at(ref r) { + if (r.val < entries.size()) { + return &entries[r.val]; + } + return nullptr; + } + const entry* at(ref r) const { + if (r.val < entries.size()) { + return &entries[r.val]; + } + return nullptr; + } + + template + std::pair emplace_back(ref r, _Args &&... __args) { + assert(!find(r)); + const generational::id::idx_t idx = entries.size(); + map.set(r, idx); + auto it = entries.emplace_back(r, std::forward<_Args>(__args)...); + return std::make_pair(idx, &it); + } + /// Place at idx moving childs if needed + template + entry& set_emplace(ref r, generational::id::idx_t at, _Args &&... __args) { + assert(!find(r) && at <= entries.size()); + map.set(r, at); + auto it = entries.emplace(entries.begin()+at, r, std::forward<_Args>(__args)...); + for (auto next = it+1; next != entries.end(); ++next) { + auto& cell = map.at(next->self.val); + assert(cell.has_value()); + ++cell.value(); + } + return *it; + } + + void erase(ref r) { + if (const auto idx = find(r)) { + map.erase(r); + auto it = entries.erase(entries.begin() + idx.value().val); + for (; it != entries.end(); ++it) { + auto& cell = map.at(it->self.val); + assert(cell.has_value()); + --cell.value(); + } + } + } + void erase(ref from, size_t count) { + if (const auto idx = find(from)) { + auto it = entries.begin() + idx.value().val; + const auto start = it; + const auto last = it + count; + for (; it != last; ++it) + map.erase(it->self.val); + for (; it != entries.end(); ++it) { + auto& cell = map.at(it->self.val); + assert(cell.has_value()); + cell.value() -= count; + } + entries.erase(start, last); + } + } + + template + void iter(apply fn) const { + for (const auto& entry: entries) { + fn(entry); + } + } + template + void for_each(apply fn) { + for (auto& entry: entries) { + fn(entry); + } + } + + size_t size() const { return entries.size(); } + void reserve(size_t n) { + map.reserve(n); + entries.reserve(n); + } + + private: + std::vector entries; + view_vector map; + }; + + template + struct hierarchy_entry: public T { + using offset = generational::id::idx_t; + hierarchy_entry(const key &p, offset n, const T &in) : T(in), parent(p), childs(n) {} + template + hierarchy_entry(const key& p, offset n, _Args &&... __args): + T(std::forward<_Args>(__args)...), parent(p), childs(n) { } + + key parent; + offset childs; + }; + + /// Sorted hierarchy with parent and next child idx + template + class hierarchy: public heavy_reorder> { + public: + using parent = heavy_reorder>; + using entry = typename parent::entry; + + /// Add single root node + template + std::pair emplace_back(ref r, _Args &&... __args) { + return parent::emplace_back(r, generational::id(), 0, std::forward<_Args>(__args)...); + } + /// Add node with parent + template + entry& set_emplace(ref r, const key& parent, _Args &&... __args) { + assert(!parent::find(r)); + if (const auto pid = parent::find(parent.val.index())) { + const ref> cid = pid.value().val+1; + auto &it = parent::set_emplace(r, cid, parent, 0, std::forward<_Args>(__args)...); + move_childs(it.parent, 1); + return it; + } else { + assert(!parent); + return *emplace_back(r, std::forward<_Args>(__args)...).second; + } + } + + template + void erase(ref r, const fn& childApply) { + if (const auto idx = parent::find(r)) { + auto self = parent::at(idx.value()); + const auto parent = self->parent; + const auto childs = self->childs; + parent::erase(r); + move_childs(parent, -1); + for (size_t i = 0; i < childs; i++) { + auto child = parent::at(idx.value().val + i); + child->parent = parent; + childApply(child); + } + } + } + template + void erase_recursive(ref r, const fn& apply) { + if (const auto idx = parent::find(r)) { + auto self = parent::at(idx.value()); + const auto parent = self->parent; + const auto to_delete = self->childs+1; + for (size_t i = 0; i < to_delete; i++) { + apply(parent::at(idx.value().val + i)); + } + parent::erase(r, to_delete); + move_childs(parent, -to_delete); + } + } + + private: + void move_childs(const key& from, int of) { + ref cur = from.val.index(); + while(cur) { + auto it = parent::get(cur); + assert(it); + it->childs = of + it->childs; + cur = it->parent.val.index(); + } + } + }; + +} \ No newline at end of file diff --git a/src/core/generational/map.hpp b/src/core/generational/map.hpp new file mode 100644 index 0000000..f37019f --- /dev/null +++ b/src/core/generational/map.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include "./core.hpp" + +namespace std { + using namespace generational; + + template<> + struct hash { + std::size_t operator()(const id& i) const noexcept { + return std::hash{}(i.index()); + } + }; + + template + struct hash> { + std::size_t operator()(const generational::ref& r) const noexcept { + return std::hash{}(r.val); + } + }; + + template + struct hash> { + std::size_t operator()(const generational::ref& r) const noexcept { + return std::hash{}(r.val); + } + }; +} + +namespace generational { + // Unordered subset of a vector + template + class flag: public robin_hood::unordered_set> { }; + + /// Subset or reordering of a vector + template + class map: public std::vector> { + public: + /// O(n) + bool erase(const ref& r) { + for (auto it = this->begin(); it != this->end(); ++it) { + if (*it == r) { + std::vector>::erase(it); + return true; + } + } + return false; + } + }; +} \ No newline at end of file diff --git a/src/core/generational/vector.hpp b/src/core/generational/vector.hpp new file mode 100644 index 0000000..7c0d022 --- /dev/null +++ b/src/core/generational/vector.hpp @@ -0,0 +1,342 @@ +#pragma once + +#include +#include "./core.hpp" + +namespace generational { + + /// Generational container accessible by id + template + class vector { + public: + vector() { } + template + vector(const C& map) { + for(const auto& [key, value]: map) { + if (const auto size = entries.size(); key >= size) { + entries.resize(key + 1); + } + entries[key].value = value; + } + for (size_t i = 0; i < entries.size(); i++) { + if(!entries[i].value.has_value()) + freed.push_back(i); + } + } + + key push(const T& in) { + if(freed.empty()) { + const auto idx = entries.size(); + entries.emplace_back(in); + return id(idx, 0); + } else { + const auto idx = freed.back(); + freed.pop_back(); + auto &entry = entries[idx]; + assert(!entry.value.has_value()); + entry.value = in; + return entry.next(idx, 0); + } + } + template + std::pair, T*> emplace(uint8_t flag, _Args &&... __args) { + if(freed.empty()) { + const auto idx = entries.size(); + auto& entry = entries.emplace_back(entry::merge(0, flag), std::forward<_Args>(__args)...); + return std::make_pair(id(idx, 0, flag), &entry.value.value()); + } else { + const auto idx = freed.back(); + freed.pop_back(); + auto &entry = entries[idx]; + assert(!entry.value.has_value()); + entry.value.emplace(std::forward<_Args>(__args)...); + return std::make_pair(entry.next(idx, flag), &entry.value.value()); + } + } + template + T& set_emplace(key at, _Args &&... __args) { + const auto idx = at.val.index(); + if (idx >= entries.size()) + entries.resize(idx+1); + + auto& entry = entries.at(idx); + assert(!entry.value.has_value()); + entry.val = entry::merge(at.val.generation(), at.val.flag()); + entry.value.emplace(std::forward<_Args>(__args)...); + return entry.value.value(); + } + bool put(key k, const T& in) { + const auto idx = k.val.index(); + if (idx >= entries.size()) + return false; + + if (entries[idx].generation() != k.val.generation() || entries[idx].value.has_value()) + return false; + + entries[idx].value = in; + return true; + } + T& at(key k) { + assert(contains(k)); + return entries[k.val.index()].value.value(); + } + T* directly_at(key k) { + if (k.val.index() >= entries.size()) + return nullptr; + + auto &cell = entries.at(k.val.index()); + if (!cell.value.has_value() || cell.generation() != k.val.generation()) + return nullptr; + + return &cell.value.value(); + } + const T* directly_at(key k) const { + const auto idx = k.val.index(); + if (idx >= entries.size()) + return nullptr; + + auto &cell = entries.at(idx); + if (!cell.value.has_value() || cell.generation() != k.val.generation()) + return nullptr; + + return &cell.value.value(); + } + T* directly_at(ref index) { + if (index.val >= entries.size() || !entries.at(index.val).value.has_value()) + return nullptr; + + return &entries[index.val].value.value(); + } + const T* directly_at(ref index) const { + if (index.val >= entries.size() || !entries.at(index.val).value.has_value()) + return nullptr; + + return &entries[index.val].value.value(); + } + + bool contains(key k) const { + const auto idx = k.val.index(); + return idx < entries.size() && + entries.at(idx).generation() == k.val.generation() && + entries.at(idx).value.has_value(); + } + + /// Vanished idx will not be considered free (and so not reused) + bool vanish(key k) { + if(!contains(k)) + return false; + + const auto idx = k.val.index(); + entries[idx].value = std::nullopt; + return true; + } + bool free(key k) { + if(!contains(k)) + return false; + + const auto idx = k.val.index(); + entries[idx].value = std::nullopt; + freed.push_back(idx); + return true; + } + + bool empty() const { + for(auto& v: entries) { + if(v.value.has_value()) + return false; + } + return true; + } + + template + void iter(apply fn) const { + for (size_t i = 0; i < entries.size(); i++) { + const auto &entry = entries[i]; + if(entry.value.has_value()) { + fn(key(id(i, entry.generation(), entry.flag())), entry.value.value()); + } + } + } + + template + void iter_empty(apply fn) const { + for (size_t i = 0; i < entries.size(); i++) { + auto &entry = entries[i]; + if(!entry.value.has_value()) { + fn(key(id(i, entry.generation(), entry.flag()))); + } + } + } + + template + bool any(predicate fn) const { + for (size_t i = 0; i < entries.size(); i++) { + const auto &entry = entries[i]; + if(entry.value.has_value() && fn(key(id(i, entry.generation(), entry.flag())), entry.value.value())) { + return true; + } + } + return false; + } + + /// See vanish + template + void extract(extractor fn) { + for (size_t i = 0; i < entries.size(); i++) { + auto &entry = entries[i]; + if(entry.value.has_value()) { + if(fn(key(id(i, entry.generation(), entry.flag())), entry.value.value())) + entry.value = std::nullopt; + } + } + } + + template + void remove(extractor fn) { + for (size_t i = 0; i < entries.size(); i++) { + auto &entry = entries[i]; + if(entry.value.has_value()) { + if(fn(key(id(i, entry.generation(), entry.flag())), entry.value.value())) { + entry.value = std::nullopt; + freed.push_back(i); + } + } + } + } + + size_t size() const { + return std::count_if(entries.begin(), entries.end(), + [](const entry &e) { return e.value.has_value(); }); + } + void reserve(size_t n) { entries.reserve(n); } + + key with_flag(const ref& r) const { + if (r.val >= entries.size()) + return id(); + + const auto &cell = entries.at(r.val); + if (!cell.value.has_value()) + return id(); + + return id(r.val, cell.generation(), cell.flag()); + } + key with_flag(const key& k) const { + const auto idx = k.val.index(); + if (idx >= entries.size()) + return id(); + + const auto &cell = entries.at(idx); + const auto gen = k.val.generation(); + if (!cell.value.has_value() || cell.generation() != gen) + return id(); + + return id(idx, gen, cell.flag()); + } + void set_flag(const key& k) { + const auto idx = k.val.index(); + if (idx >= entries.size()) + return; + + entries.at(idx).set_flag(k.val.flag()); + } + + private: + struct entry { + entry(): value(std::nullopt), val(0) { } + entry(const T &in, uint_fast32_t val = 0): + value(in), val(val) { } + template + entry(int_fast32_t val, _Args &&... __args): val(val) { + value.emplace(std::forward<_Args>(__args)...); + } + std::optional value; + uint_fast32_t val = 0; + + static constexpr uint32_t merge(uint_fast32_t gen, uint_fast8_t flag) { + return (gen & id::GENERATION_MASK) | ((flag & id::FLAG_MASK) << FLAG_OFFSET); + } + constexpr id::type generation() const { return val & id::GENERATION_MASK; } + static constexpr size_t FLAG_OFFSET = id::FLAG_OFFSET - id::GENERATION_OFFSET; + constexpr uint_fast8_t flag() const { return (val >> FLAG_OFFSET) & id::FLAG_MASK; } + inline constexpr void set_flag(uint_fast8_t flag) { val = merge(val & ~id::FLAG_MASK, flag); } + inline key next(size_t idx, uint8_t flag) { + val = merge(++val & ~id::FLAG_MASK, flag); + return id(idx, generation(), flag); + } + }; + std::vector entries; + std::vector freed; + }; + + /// Dummy generational container without allocation nor validation. A view to vector + template + class view_vector: public std::vector> { + public: + view_vector(): std::vector>() { } + + template + void set_emplace(ref r, _Args &&... __args) { + if(r.val >= this->size()) + this->resize(r.val + 1); + + this->at(r.val).emplace(std::forward<_Args>(__args)...); + } + void set(ref r, const T& in) { + if(r.val >= this->size()) + this->resize(r.val + 1); + + this->at(r.val) = in; + } + + T* get(ref r) { + if (r.val >= this->size() || !this->at(r.val).has_value()) + return nullptr; + + return &this->at(r.val).value(); + } + const T* get(ref r) const { + if (r.val >= this->size() || !this->at(r.val).has_value()) + return nullptr; + + return &this->at(r.val).value(); + } + + template + void iter(apply fn) const { + for (size_t i = 0; i < this->size(); i++) { + if(this->at(i).has_value()) { + fn(ref(i), this->at(i).value()); + } + } + } + + template + bool contains(predicate fn) const { + for (size_t i = 0; i < this->size(); i++) { + if(this->at(i).has_value() && fn(ref(i), this->at(i).value())) { + return true; + } + } + return false; + } + + void erase(ref r) { + if(r.val < this->size()) + this->at(r.val) = std::nullopt; + } + template + void remove(extractor fn) { + for (size_t i = 0; i < this->size(); i++) { + if(this->at(i).has_value() && fn(ref(i), this->at(i).value())) { + erase(i); + } + } + } + + size_t count() const { + return std::count_if(this->begin(), this->end(), + [](const std::optional &e) { return e.has_value(); }); + } + }; + +} \ No newline at end of file diff --git a/src/core/geometry/Box.hpp b/src/core/geometry/Box.hpp index bda89ca..2629f5f 100644 --- a/src/core/geometry/Box.hpp +++ b/src/core/geometry/Box.hpp @@ -1,43 +1,218 @@ #pragma once -#include +#include "./math.hpp" namespace geometry { - /// Axis Aligned Floating Box - struct Box { + template + struct OBB; + + /// Abstract Axis Aligned Box + template + struct AABB { + using vec = glm::vec<3, U>; + + AABB(const vec& min, const vec& max): min(min), max(max) { + assert(glm::all(glm::greaterThanEqual(max, min)) && "min > min"); + } + inline static AABB FromCenter(const vec& center, const vec& extent) { return AABB(center - extent, center + extent); } + inline static AABB FromMin(const vec& min, const vec& size) { return AABB(min, min + size); } + template + inline static AABB From(const AABB &v) { return AABB(v.min, v.max); } + + vec min; + vec max; + + inline AABB operator+(const vec& v) const { return AABB(min + v, max + v); } + + constexpr vec getSize() const { return max - min; } + constexpr vec getCenter() const { return (min + max) / (U)2; } + constexpr vec getExtent() const { return getSize() / (U)2; } + + /// Biggest box after pivot around zero + AABB rotate(const glm::quat& pivot) const { + const auto mat = glm::toMat4(pivot); + return FromCenter(mat * glm::vec<4, U>(getCenter(), 1), glm::abs(mat) * glm::vec<4, U>(getExtent(), 0)); + } + /// Pivot around center not zero + /// Take glm::abs(rot) + inline static AABB FromCenterAbs(const vec& center, const vec& extent, const glm::mat4& rot_abs) { + const vec new_extent = rot_abs * glm::vec<4, U>(extent, 0); + return AABB(center - new_extent, center + new_extent); + } + /// Pivot around center not zero + inline static AABB FromCenter(const vec& center, const vec& extent, const glm::mat4& rot) { + return FromCenterAbs(center, extent, glm::abs(rot)); + } + /// Pivot around center not zero + inline static AABB FromCenter(const vec& center, const vec& extent, const glm::quat& pivot) { + return FromCenter(center, extent, glm::toMat4(pivot)); + } + /// Pivot around center not zero + /// Take glm::abs(rot) + inline static AABB FromMinAbs(const vec& min, const vec& size, const glm::mat4& rot_abs) { + const auto extent = size / (U)2; + return FromCenterAbs(min + extent, extent, rot_abs); + } + /// Pivot around center not zero + inline static AABB FromMin(const vec& min, const vec& size, const glm::mat4& rot) { + return FromMinAbs(min, size, glm::abs(rot)); + } + /// Pivot around center not zero + inline static AABB FromMin(const vec& min, const vec& size, const glm::quat& pivot) { + return FromMin(min, size, glm::toMat4(pivot)); + } + /// Pivot around center not zero + template + inline static AABB From(const AABB &v, const glm::quat& pivot) { + return FromMin(v.min, v.getSize(), pivot); + } + + constexpr bool contains(const vec& pos) const { + return pos.x >= min.x && pos.x <= max.x && + pos.y >= min.y && pos.y <= max.y && + pos.z >= min.z && pos.z <= max.z; + } + constexpr bool contains(const AABB& box) const { + return box.min.x >= min.x && box.max.x <= max.x && + box.min.y >= min.y && box.max.y <= max.y && + box.min.z >= min.z && box.max.z <= max.z; + } + + /// Intersects or contains box + constexpr bool intersects(const AABB& box) const { + return box.max.x >= min.x && box.min.x <= max.x && + box.max.y >= min.y && box.min.y <= max.y && + box.max.z >= min.z && box.min.z <= max.z; + } + /// Intersects or contained by sphere + constexpr bool intersected(const vec& center, U radius) const { + return distance2(center) <= glm::pow2(radius); + } + /// Intersects or contains sphere + constexpr bool intersects(const vec& center, U radius) const { + return glm::length2(center - closest(center)) <= glm::pow2(radius); + } + /// Intersects or contains box + template + constexpr bool intersects(const OBB& box) const { + return box.intersected(*this); + } + enum class ContainmentType { Disjoint = false, Intersects, Contains }; - - Box(glm::vec3 min, glm::vec3 max): Min(min), Max(max) { - assert((max.x >= min.x && max.y >= min.y && max.z >= min.z) && "Min > Max"); - } - inline static Box fromCenter(glm::vec3 center, float radius) { return Box(center - radius, center + radius); } - inline static Box fromMin(glm::vec3 min, glm::vec3 size) { return Box(min, min + size); } - - glm::vec3 Min; - glm::vec3 Max; - - ContainmentType contains(const Box& box) const { - //test if all corner is in the same side of a face by just checking min and max - if (box.Max.x < Min.x - || box.Min.x > Max.x - || box.Max.y < Min.y - || box.Min.y > Max.y - || box.Max.z < Min.z - || box.Min.z > Max.z) + ContainmentType containment(const AABB& box) const { + if (!intersects(box)) return ContainmentType::Disjoint; - - if (box.Min.x >= Min.x - && box.Max.x <= Max.x - && box.Min.y >= Min.y - && box.Max.y <= Max.y - && box.Min.z >= Min.z - && box.Max.z <= Max.z) + if (contains(box)) return ContainmentType::Contains; return ContainmentType::Intersects; } + + inline U distance2(const vec& pos) const { + U dmin = 0; + for (size_t i = 0; i < vec::length(); i++) { + if (pos[i] < min[i]) + dmin += glm::pow2(pos[i] - min[i]); + else if(pos[i] > max[i]) + dmin += glm::pow2(pos[i] - max[i]); + } + return dmin; + } + inline vec closest(const vec& pos) const { + auto p = vec(0); + for (size_t i = 0; i < vec::length(); i++) { + if (pos[i] > max[i]) + p[i] = max[i]; + else if (pos[i] < min[i]) + p[i] = min[i]; + else + p[i] = pos[i]; + } + return p; + } + + + }; + + using faabb = AABB; + using lfaabb = AABB; + using llaabb = AABB; + + /// Abstract Oriented Box + template + struct OBB { + using vec = glm::vec<3, U>; + + vec center; + /// Half size (always positive) + vec extent; + /// Extent rotation around center + glm::quat pivot; + + OBB(const vec& center, const vec& extent, const glm::quat& pivot): center(center), extent(extent), pivot(pivot) { + assert(glm::all(glm::greaterThanEqual(extent, vec(0))) && "positive extent"); + assert(pivot != glm::quat() && "uninitialized pivot"); + } + OBB(const AABB& box, const glm::quat& pivot): OBB(box.getCenter(), box.getExtent(), pivot) { } + + inline OBB operator+(const vec& v) const { return OBB(center + v, extent, pivot); } + + constexpr vec getSize() const { return extent * (U)2; } + constexpr inline glm::qua

getPivot() const { return (glm::qua

)pivot; } + + /// Intersects or contains sphere + constexpr bool intersects(const vec& sp_c, U radius) const { + const vec local_center = center + (glm::conjugate(getPivot()) * (sp_c-center)); + return AABB::FromCenter(vec(0), extent).intersects(local_center, radius); + } + /// Intersects or contained by sphere + constexpr bool intersected(const vec& sp_c, U radius) const { + const vec local_center = center + (glm::conjugate(getPivot()) * (sp_c-center)); + return AABB::FromCenter(vec(0), extent).intersected(local_center, radius); + } + /// Intersects or contained box + constexpr bool intersected(const AABB& box_aa) const { + const auto this_sat = getBounding(); + const auto this_aa = AABB::FromCenter(vec(0), extent); + const auto box_sat = AABB::FromCenter(box_aa.getCenter() - center, box_aa.getExtent(), glm::conjugate(pivot)); + return this_sat.intersects(box_aa) && this_aa.intersects(box_sat); + } + /// Intersects or contains box + constexpr bool intersects(const AABB& box_aa) const { + const auto this_sat = getBounding(); + const auto this_aa = AABB::FromCenter(vec(0), extent); + const auto box_sat = AABB::FromCenter(box_aa.getCenter() - center, box_aa.getExtent(), glm::conjugate(pivot)); + return box_aa.intersects(this_sat) && box_sat.intersects(this_aa); + } + /* + /// Intersects or contains box + constexpr bool intersects(const OBB& box) const { + return false; //FIXME: full sat + }*/ + + constexpr inline std::array getPoints() const { + std::array ps; + ps[0] = getPivot() * (center - extent); + ps[1] = getPivot() * (center + extent * vec(-1, -1, +1)); + ps[2] = getPivot() * (center + extent * vec(-1, +1, -1)); + ps[3] = getPivot() * (center + extent * vec(-1, +1, +1)); + ps[4] = getPivot() * (center + extent * vec(+1, -1, -1)); + ps[5] = getPivot() * (center + extent * vec(+1, -1, +1)); + ps[6] = getPivot() * (center + extent * vec(+1, +1, -1)); + ps[7] = getPivot() * (center + extent); + return ps; + } + AABB getBounding() const { + return AABB::FromCenter(center, extent, pivot); + } + }; + + using fobb = OBB; + using lfobb = OBB; + using llobb = OBB; + } \ No newline at end of file diff --git a/src/core/geometry/Faces.hpp b/src/core/geometry/Faces.hpp index 3db527b..f6588f3 100644 --- a/src/core/geometry/Faces.hpp +++ b/src/core/geometry/Faces.hpp @@ -1,16 +1,16 @@ #pragma once -#include "../world/position.h" +#include /// Math utils namespace geometry { enum class Face { Right, Left, Up, Down, Forward, Backward }; - const chunk_pos g_face_offsets[6] = { - chunk_pos(1,0,0), chunk_pos(-1,0,0), - chunk_pos(0,1,0), chunk_pos(0,-1,0), - chunk_pos(0,0,1), chunk_pos(0,0,-1), + const glm::ivec3 g_face_offsets[6] = { + glm::ivec3(1,0,0), glm::ivec3(-1,0,0), + glm::ivec3(0,1,0), glm::ivec3(0,-1,0), + glm::ivec3(0,0,1), glm::ivec3(0,0,-1), }; enum class Faces { diff --git a/src/core/geometry/Frustum.hpp b/src/core/geometry/Frustum.hpp index 7de6d7c..30bf4eb 100644 --- a/src/core/geometry/Frustum.hpp +++ b/src/core/geometry/Frustum.hpp @@ -67,15 +67,15 @@ namespace geometry { } /// Check if box is contained - inline bool contains(const Box &box) const { + inline bool contains(const faabb &box) const { bool inside = true; //test all 6 frustum planes for (int i = 0; i<6; i++) { //pick closest point to plane and check if it behind the plane //if yes - object outside frustum - float d = std::max(box.Min.x * planes[i].x, box.Max.x * planes[i].x) - + std::max(box.Min.y * planes[i].y, box.Max.y * planes[i].y) - + std::max(box.Min.z * planes[i].z, box.Max.z * planes[i].z) + float d = std::max(box.min.x * planes[i].x, box.max.x * planes[i].x) + + std::max(box.min.y * planes[i].y, box.max.y * planes[i].y) + + std::max(box.min.z * planes[i].z, box.max.z * planes[i].z) + planes[i].w; inside &= d > 0; //return false; //with flag works faster diff --git a/src/core/geometry/IBox.hpp b/src/core/geometry/IBox.hpp deleted file mode 100644 index d949a7a..0000000 --- a/src/core/geometry/IBox.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include - -namespace geometry { - /// Axis Aligned Long Box - struct IBox { - using pos_t = glm::llvec3; - enum class ContainmentType { - Disjoint = false, Intersects, Contains - }; - - IBox(pos_t min, pos_t max): Min(min), Max(max) { - assert((max.x >= min.x && max.y >= min.y && max.z >= min.z) && "Min > Max"); - } - inline static IBox fromCenter(pos_t center, pos_t::value_type radius) { return IBox(center - radius, center + radius); } - inline static IBox fromMin(pos_t min, pos_t size) { return IBox(min, min + size); } - - pos_t Min; - pos_t Max; - - ContainmentType contains(const IBox& box) const { - //test if all corner is in the same side of a face by just checking min and max - if (box.Max.x < Min.x - || box.Min.x > Max.x - || box.Max.y < Min.y - || box.Min.y > Max.y - || box.Max.z < Min.z - || box.Min.z > Max.z) - return ContainmentType::Disjoint; - - - if (box.Min.x >= Min.x - && box.Max.x <= Max.x - && box.Min.y >= Min.y - && box.Max.y <= Max.y - && box.Min.z >= Min.z - && box.Max.z <= Max.z) - return ContainmentType::Contains; - - return ContainmentType::Intersects; - } - constexpr bool contains(const pos_t& pos) const { - return pos.x >= Min.x && pos.x <= Max.x && - pos.y >= Min.y && pos.y <= Max.y && - pos.z >= Min.z && pos.z <= Max.z; - } - }; -} \ No newline at end of file diff --git a/src/core/geometry/Ray.hpp b/src/core/geometry/Ray.hpp index 40cd29e..d44de52 100644 --- a/src/core/geometry/Ray.hpp +++ b/src/core/geometry/Ray.hpp @@ -1,26 +1,26 @@ #pragma once -#include "IBox.hpp" -#include "../world/position.h" +#include "./Box.hpp" +#include namespace geometry { /// Raycast with distance struct Ray { - camera_pos from; + glm::lfvec3 from; glm::vec3 dir; float dist; // MAYBE: Ray(const glm::mat4& view_matrix) { } - Ray(const camera_pos& from, const glm::vec3& dir, float dist): from(from), dir(glm::normalize(dir)), dist(dist) { } + Ray(const glm::lfvec3& from, const glm::vec3& dir, float dist): from(from), dir(glm::normalize(dir)), dist(dist) { } inline Ray operator*(float scale) const noexcept { - return Ray(from * scale, dir, dist * scale); + return Ray(from * (glm::lf)scale, dir, dist * scale); } /// Get path points in integer grid /// @note not precise enough inline void grid(std::vector& points) const { - glm::llvec3 current = from.as_voxel(); + glm::llvec3 current = from; const glm::llvec3 d = dir * dist; const glm::llvec3 inc = glm::llvec3((d.x < 0) ? -1 : 1, (d.y < 0) ? -1 : 1, (d.z < 0) ? -1 : 1); const glm::llvec3 size = glm::abs(d); @@ -87,7 +87,7 @@ namespace geometry { struct iterator { enum class axis { X, Y, Z }; - iterator(const Ray& r): current(r.from.as_voxel()), + iterator(const Ray& r): current(r.from), d(r.dir * r.dist), l(glm::abs(d)), dir((l.x >= l.y) && (l.x >= l.z) ? axis::X : ((l.y >= l.x) && (l.y >= l.z) ? axis::Y : axis::Z)), count(dir == axis::X ? l.x : (dir == axis::Y ? l.y : l.z)), @@ -133,19 +133,20 @@ namespace geometry { int err_2; }; - IBox::ContainmentType intersect(const IBox& box) const { - const glm::llvec3 start = from.as_voxel(); + template + typename AABB::ContainmentType intersect(const AABB& box) const { + const glm::llvec3 start = from; if(box.contains(start)) - return IBox::ContainmentType::Contains; + return AABB::ContainmentType::Contains; const auto inv = 1. / glm::dvec3(dir); - glm::f64 tmin = ((inv.x < 0 ? box.Max : box.Min).x - start.x) * inv.x; - glm::f64 tmax = ((inv.x < 0 ? box.Min : box.Max).x - start.x) * inv.x; - glm::f64 tymin = ((inv.y < 0 ? box.Max : box.Min).y - start.y) * inv.y; - glm::f64 tymax = ((inv.y < 0 ? box.Min : box.Max).y - start.y) * inv.y; + glm::f64 tmin = ((inv.x < 0 ? box.max : box.min).x - start.x) * inv.x; + glm::f64 tmax = ((inv.x < 0 ? box.min : box.max).x - start.x) * inv.x; + glm::f64 tymin = ((inv.y < 0 ? box.max : box.min).y - start.y) * inv.y; + glm::f64 tymax = ((inv.y < 0 ? box.min : box.max).y - start.y) * inv.y; if ((tmin > tymax) || (tymin > tmax)){ - return IBox::ContainmentType::Disjoint; + return AABB::ContainmentType::Disjoint; } if (tymin > tmin) { tmin = tymin; @@ -154,11 +155,11 @@ namespace geometry { tmax = tymax; } - glm::f64 tzmin = ((inv.z < 0 ? box.Max : box.Min).z - start.z) * inv.z; - glm::f64 tzmax = ((inv.z < 0 ? box.Min : box.Max).z - start.z) * inv.z; + glm::f64 tzmin = ((inv.z < 0 ? box.max : box.min).z - start.z) * inv.z; + glm::f64 tzmax = ((inv.z < 0 ? box.min : box.max).z - start.z) * inv.z; if ((tmin > tzmax) || (tzmin > tmax)){ - return IBox::ContainmentType::Disjoint; + return AABB::ContainmentType::Disjoint; } if (tzmin > tmin){ tmin = tzmin; @@ -169,7 +170,7 @@ namespace geometry { // this last check is different from the 'ray' case in below references: // we need to check that the segment is on the span of the line // that intersects the box - return tmax<0.0f || tmin> 1.0f ? IBox::ContainmentType::Intersects : IBox::ContainmentType::Disjoint; + return tmax<0.0f || tmin> 1.0f ? AABB::ContainmentType::Intersects : AABB::ContainmentType::Disjoint; } }; } diff --git a/src/core/geometry/Shapes.hpp b/src/core/geometry/Shapes.hpp index dee20c8..d545617 100644 --- a/src/core/geometry/Shapes.hpp +++ b/src/core/geometry/Shapes.hpp @@ -1,8 +1,8 @@ #pragma once -#include "../flags.hpp" -#include "../data/math.hpp" -#include "IBox.hpp" +#include "../utils/flags.hpp" +#include "./math.hpp" +#include "./Box.hpp" namespace geometry { @@ -11,18 +11,73 @@ enum class Shape { Sphere }; -static _FORCE_INLINE_ bool InShape(Shape shape, glm::llvec3 center, int radius, glm::llvec3 pos, glm::vec3 size) { - switch (shape) { - case Shape::Cube: - return IBox::fromCenter(center, radius).contains( - IBox::fromMin(pos, size)) != IBox::ContainmentType::Disjoint; +struct Volume { + Shape shape; + int radius; - case Shape::Sphere: - return glm::length2(center - pos) < glm::pow2(radius + (int)size.x); - default: - return false; + /// AABB bounding box + template + _FORCE_INLINE_ AABB getBoundingBox(const glm::vec<3, U>& center) const { + return AABB::FromCenter(center, glm::vec<3, U>(radius)); } -} + + /// AABB bounding box after rotation + template + _FORCE_INLINE_ AABB getBoundingBox(const glm::vec<3, U>& center, const glm::quat& pivot) const { + switch (shape) { + case Shape::Cube: + return AABB::FromCenter(center, glm::vec<3, U>(radius), pivot); + + case Shape::Sphere: + return getBoundingBox(center); + } + } + + /// Volume intersect or contains box + template + _FORCE_INLINE_ bool intersects(const glm::vec<3, U>& center, const AABB& box) const { + switch (shape) { + case Shape::Cube: + return AABB::FromCenter(center, glm::vec<3, U>(radius)).intersects(box); + + case Shape::Sphere: + return box.intersected(center, radius); + + default: + return false; + } + } + + /// Volume intersect or contains box + template + _FORCE_INLINE_ bool intersects(const glm::vec<3, U>& center, const OBB& box) const { + switch (shape) { + case Shape::Cube: + return AABB::FromCenter(center, glm::vec<3, U>(radius)).intersects(box); + + case Shape::Sphere: + return box.intersected(center, radius); + + default: + return false; + } + } + + /// Volume intersect or contains box after rotation + template + _FORCE_INLINE_ bool intersects(const glm::vec<3, U>& center, const glm::quat& pivot, const OBB& box) const { + switch (shape) { + case Shape::Cube: + return OBB::FromCenter(center, glm::vec<3, U>(radius), pivot).intersects(box); + + case Shape::Sphere: + return box.intersected(center, radius); + + default: + return false; + } + } +}; }; diff --git a/src/core/geometry/glm.hpp b/src/core/geometry/glm.hpp new file mode 100644 index 0000000..606f836 --- /dev/null +++ b/src/core/geometry/glm.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace glm { + typedef long double lf; + typedef long long ll; + typedef int_fast32_t l; + typedef uint16_t us; + typedef uint8_t uc; + + typedef vec<3, lf> lfvec3; + typedef vec<3, ll> llvec3; + typedef vec<3, l> lvec3; + typedef vec<3, us> usvec3; + typedef vec<3, uc> ucvec3; + + const auto IDX_LENGTH = 32; + const auto IDX_LENGTH2 = IDX_LENGTH * IDX_LENGTH; + const auto IDX_SIZE = IDX_LENGTH2 * IDX_LENGTH; + using idx = glm::u16; +} diff --git a/src/core/geometry/math.hpp b/src/core/geometry/math.hpp new file mode 100644 index 0000000..be35acf --- /dev/null +++ b/src/core/geometry/math.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include "glm.hpp" +#include +#include +#include + +namespace glm { + template + constexpr vec<3, T> inline round(const vec3& p) { + return vec<3, T>(std::round(p.x), std::round(p.y), std::round(p.z)); + } + + template + constexpr U inline pow2(U v) { + return v * v; + } + constexpr ll inline length2(const llvec3& v) { + return pow2(v.x) + pow2(v.y) + pow2(v.z); + } + constexpr ll inline length2(const lvec3& v) { + return length2((llvec3)v); + } + + constexpr lfvec3 inline abs(const lfvec3& v) { + return lfvec3(fabsl(v.x), fabsl(v.y), fabsl(v.z)); + } + constexpr mat4 inline abs(const mat4& mat) { + mat4 m(0); + for (int i = 0; i < mat.length(); i++) + m[i] = abs(mat[i]); + return m; + } + + template + constexpr vec<3, T> inline diff(const vec<3, T>& a, const vec<3, T>& b) { + return glm::abs(a - b); + } + + constexpr us inline rem(ll value, us m) { + return value < 0 ? ((value+1) % (ll)m) + m - 1 : value % (ll)m; + } + constexpr l inline div(ll value, us m) { + return value < 0 ? ((value+1) / (ll)m) - 1 : value / (ll)m; + } + constexpr float inline remf(lf value, us m) { + return value < 0 ? fmodl(value+1, m) + m - 1 : fmodl(value, m); + } + constexpr ucvec3 inline modulo(const llvec3& value, const ucvec3& m = ucvec3(IDX_LENGTH)) { + return ucvec3(rem(value.x, m.x), rem(value.y, m.y), rem(value.z, m.z)); + } + constexpr vec3 inline modulo(const lfvec3& value, const usvec3& m = usvec3(IDX_LENGTH)) { + return vec3(remf(value.x, m.x), remf(value.y, m.y), remf(value.z, m.z)); + } + constexpr lvec3 inline divide(const llvec3 &value, const uvec3 &m) { + return lvec3(div(value.x, m.x), div(value.y, m.y), div(value.z, m.z)); + } + constexpr lvec3 inline divide(const llvec3 &value, const ucvec3 &m = ucvec3(IDX_LENGTH)) { + return lvec3(div(value.x, m.x), div(value.y, m.y), div(value.z, m.z)); + } + constexpr llvec3 inline multiply(const lvec3 &value, const ucvec3 &m = ucvec3(IDX_LENGTH)) { + return llvec3(value) * llvec3(m); + } + constexpr std::pair inline split(const llvec3 &value, const ucvec3 &m = ucvec3(IDX_LENGTH)) { + return {divide(value, m), modulo(value, m)}; + } + constexpr ucvec3 inline fromIdx(idx idx) { + assert(idx < IDX_SIZE); + return ucvec3(idx / IDX_LENGTH2, (idx / IDX_LENGTH) % IDX_LENGTH, idx % IDX_LENGTH); + } + constexpr usvec3 inline fromIdx(size_t idx, lvec3 size) { + assert(size.x>=0 && size.y>=0 && size.z>=0); + assert((lvec3::value_type)idx < size.x * size.y * size.z); + return usvec3(idx / (size.y * size.z), (idx / size.z) % size.y, idx % size.z); + } + constexpr idx inline toIdx(glm::uc x, glm::uc y, glm::uc z) { + return (x * IDX_LENGTH + y) * IDX_LENGTH + z; + } + constexpr idx inline toIdx(glm::uc x, glm::uc y, glm::uc z, glm::uc sy, glm::uc sz) { + return x * sy * sz + y * sz + z; + } + constexpr idx inline toIdx(ucvec3 pos) { + return toIdx(pos.x, pos.y, pos.z); + } + constexpr idx inline toIdx(ucvec3 pos, ucvec3 size) { + return toIdx(pos.x, pos.y, pos.z, size.y, size.z); + } + constexpr l inline toIdx(lvec3 pos) { + return toIdx(pos.x, pos.y, pos.z); + } + constexpr l inline toIdx(lvec3 pos, lvec3 size) { + return toIdx(pos.x, pos.y, pos.z, size.y, size.z); + } + constexpr std::pair inline splitIdx(const llvec3 &value, const ucvec3 &m = ucvec3(IDX_LENGTH)) { + return {divide(value, m), toIdx(rem(value.x, m.x), rem(value.y, m.y), rem(value.z, m.z))}; + } + template + constexpr T inline min_axis(const vec<3, T> &v) { + return std::max({v.x, v.y, v.z}); + } + template + constexpr T inline max_axis(const vec<3, T> &v) { + return std::max({v.x, v.y, v.z}); + } + template + ivec3 inline inormalize(const vec<3, T> &v) { + const auto a = glm::abs(v); + return a.x >= a.y ? (a.x >= a.z ? ivec3(glm::sign(v.x), 0, 0) : ivec3(0, 0, glm::sign(v.z))) : + (a.y >= a.z ? ivec3(0, glm::sign(v.y), 0) : ivec3(0, 0, glm::sign(v.z))); + } +} diff --git a/src/core/data/mem.hpp b/src/core/memory.hpp similarity index 59% rename from src/core/data/mem.hpp rename to src/core/memory.hpp index 4650a47..27bf048 100644 --- a/src/core/data/mem.hpp +++ b/src/core/memory.hpp @@ -3,34 +3,32 @@ #include #include #include +#include #include -namespace data { - -/// Map vector to istream -struct vec_istream: std::streambuf { - vec_istream(const std::vector &vec) { - this->setg((char*)&vec[0], (char*)&vec[0], (char*)&vec[0] + vec.size()); - } -}; +/// Bytes manipulation +namespace memory { /// Abstract data with moving cursor -struct view { - view(size_t size): siz(size) { } - - size_t siz; - size_t cur = 0; +struct abstract_view { +public: + abstract_view(size_t size): siz(size) { } constexpr size_t size() const { return siz; } constexpr bool isDone() const { return cur >= siz; } + +protected: + size_t siz; + size_t cur = 0; }; + // Abstract data writer -struct in_view: view { - in_view(uint8_t *ptr, size_t size): view(size), ptr(ptr) { +struct write_view: abstract_view { +public: + write_view(uint8_t *ptr, size_t size): abstract_view(size), ptr(ptr) { assert(ptr != nullptr); } - uint8_t* ptr; uint8_t* writeTo(size_t len) { assert(cur + len <= siz); auto p = ptr + cur; @@ -40,9 +38,20 @@ struct in_view: view { void write(const uint8_t* data, size_t len) { memcpy(writeTo(len), data, len); } + template + void write(const D& d) { + write((const uint8_t*)&d, sizeof(d)); + } + + constexpr size_t current() const { return cur; } + +protected: + uint8_t* ptr; }; + +struct read_buffer; /// Writer with Dynamic allocation and subparts -struct in_writer: public in_view { +struct write_buffer: public write_view { private: static constexpr auto EMPTY_OFFSET = 0x1000; static uint8_t* alloc(size_t size) { return (uint8_t*)(size > 0 ? ::malloc(size) : reinterpret_cast(EMPTY_OFFSET)); } @@ -50,13 +59,20 @@ private: static void free(uint8_t* ptr) { ::free(ptr); } inline void auto_size(size_t len) { - if (cur + len > siz) - resize(cur + len); + const auto target = cur + len; + if (target > vis_siz) { + reserve(target * 1.5); + vis_siz = target; + } } public: - in_writer(size_t init_size = 0): in_view(alloc(init_size), init_size), vis_siz(init_size) { } - ~in_writer() { if (ptr != reinterpret_cast(EMPTY_OFFSET)) free(ptr); } + write_buffer(size_t init_size = 0): write_view(alloc(init_size), init_size), vis_siz(init_size) { } + template + write_buffer(const P& prefix, size_t additional_size): write_buffer(sizeof(prefix) + additional_size) { + write(prefix); + } + ~write_buffer() { if (ptr != reinterpret_cast(EMPTY_OFFSET)) free(ptr); } size_t vis_siz = 0; @@ -64,7 +80,7 @@ public: constexpr size_t size() const { return vis_siz; } void write(const void* data, size_t len) { - in_view::write((uint8_t*)data, len); + write_view::write((uint8_t*)data, len); } /// Write without resize template @@ -78,14 +94,20 @@ public: } void push(const void* data, size_t len) { auto_size(len); - in_view::write((uint8_t*)data, len); + write_view::write((uint8_t*)data, len); } template void push(const D& d) { push(&d, sizeof(d)); } - size_t getCursor() const { return cur; } + void pop(size_t len) { + assert(cur >= len); + cur -= len; + vis_siz = cur; + } + + constexpr size_t getCursor() const { return cur; } void writeAt(size_t pos, const void* data, size_t len) { assert(pos + len <= vis_siz); memcpy(ptr + pos, data, len); @@ -109,6 +131,8 @@ public: bool isFull() const { return cur >= vis_siz; } + [[nodiscard]] + read_buffer finish(); // Must take ptr ownership before void reset() { @@ -119,8 +143,8 @@ public: } struct part { - part(in_writer* parent): up(parent), start(parent->cur) { } - in_writer* up; + part(write_buffer* parent): up(parent), start(parent->cur) { } + write_buffer* up; size_t start; uint8_t* data() { return up->data() + start; } @@ -149,12 +173,13 @@ public: return size; } }; -/// Vector with in_view interface -struct in_vector { + +/// Vector with write_view interface +struct write_vector { /// 512 Mébibits static constexpr size_t MAX_SIZE = 1ul << 26; - in_vector(size_t max_size = MAX_SIZE): max_size(max_size) {} + write_vector(size_t max_size = MAX_SIZE): max_size(max_size) {} std::vector data; size_t max_size; @@ -168,11 +193,21 @@ struct in_vector { void write(const uint8_t* data, size_t len) { memcpy(writeTo(len), data, len); } + + template + void write(const D& d) { + write(&d, sizeof(d)); + } }; + /// Abstract data reader -struct out_view: view { - out_view(): out_view(nullptr, 0) { } - out_view(const uint8_t *ptr, size_t size): view(size), ptr(ptr) { } +struct read_view: abstract_view { + read_view(): read_view(nullptr, 0) { } + read_view(const uint8_t *ptr, size_t size): abstract_view(size), ptr(ptr) { } + template + static read_view Of(const D& ref) { + return read_view((uint8_t*)&ref, sizeof(ref)); + } const uint8_t* ptr; const uint8_t* readFrom(size_t len) { @@ -187,10 +222,6 @@ struct out_view: view { constexpr const uint8_t* data() const { return ptr; } constexpr size_t remaining() const { return size() - cur; } -}; -/// Helper to read from out_view -struct out_reader: public out_view { - out_reader(const out_view& buf): out_view(buf.ptr, buf.siz) { cur = buf.cur; } template const D* read() { @@ -199,11 +230,10 @@ struct out_reader: public out_view { } template bool read(D& out) { - const auto ptr = read(); - if (ptr == nullptr) + if (cur + sizeof(D) > size()) return false; - out = *ptr; + read((uint8_t*)&out, sizeof(D)); return true; } @@ -212,34 +242,54 @@ struct out_reader: public out_view { return !isDone(); } - data::out_view readPart(size_t size) { - return data::out_view(readFrom(size), size); + read_view readPart(size_t size) { + return read_view(readFrom(size), size); } - data::out_view readSizedPart() { + read_view readSizedPart() { size_t size = 0; read(size); return readPart(size); } - data::out_view readRemaining() { + read_view readRemaining() { return readPart(remaining()); } }; + /// Pointer to opaque owned memory using handle_t = std::shared_ptr; -/// out_view with owned data -struct out_buffer: out_view { - out_buffer(): out_view() { } - out_buffer(const out_view& view, const handle_t& handle): - out_view(view), handle(handle) { } +/// read_view with owned data +struct read_buffer: read_view { + read_buffer(): read_view() { } + read_buffer(const read_view& view, const handle_t& handle): + read_view(view), handle(handle) { } /// Take view ownership - out_buffer(const out_view& view): - out_buffer(view, std::shared_ptr(view.ptr)) { } + read_buffer(const read_view& view): + read_buffer(view, std::shared_ptr(view.ptr)) { } /// Take ptr ownership - out_buffer(const uint8_t *ptr, size_t size): out_buffer(out_view(ptr, size)) { } + read_buffer(const uint8_t *ptr, size_t size): read_buffer(read_view(ptr, size)) { } /// Take ptr ownership (release writer) - out_buffer(in_writer& writer): - out_buffer(writer.data(), writer.size()) { writer.reset(); } + read_buffer(write_buffer& writer): + read_buffer(writer.data(), writer.size()) { writer.reset(); } handle_t handle = nullptr; + + template + [[nodiscard]] + static read_buffer Of(const P& prefix, const void* data, size_t size) { + auto writer = write_buffer(prefix, size); + writer.write(data, size); + return writer.finish(); + } + template + [[nodiscard]] + static read_buffer Of(const P& prefix, const D& data) { + return Of(prefix, &data, sizeof(data)); + } }; + +inline read_buffer write_buffer::finish() { + assert(isFull()); + return read_buffer(*this); +} + } \ No newline at end of file diff --git a/src/core/net/Context.cpp b/src/core/net/Context.cpp index 4d94587..b4d69af 100644 --- a/src/core/net/Context.cpp +++ b/src/core/net/Context.cpp @@ -283,7 +283,7 @@ void Context::pull(const uint64_t MAX_DELAY, const uint64_t MAX_LOOPS, const uin Connection::~Connection() { if (cnx != nullptr) - release(0); + release(false); } void Connection::setup(picoquic_quic_t* ctx, sockaddr* addr, const char* sni, picoquic_stream_data_cb_fn cb_fn, void *cb_ctx) { auto cnx = picoquic_create_cnx(ctx, picoquic_null_connection_id, picoquic_null_connection_id, @@ -314,11 +314,11 @@ void Connection::setCallback(picoquic_stream_data_cb_fn cb_fn, void *ctx) { if (cnx != nullptr) picoquic_set_callback(cnx, cb_fn, ctx); } -void Connection::release(uint16_t reason) { +void Connection::release(bool is_app) { if (cnx != nullptr) { - picoquic_close(cnx, reason); - cnx = nullptr; + picoquic_close(cnx, !is_app); } + cnx = nullptr; } uint64_t Connection::getRTT() { @@ -334,6 +334,7 @@ std::string Connection::getAddress() { picoquic_get_peer_addr(cnx, &addr); str.resize(128, '\0'); picoquic_addr_text(addr, str.data(), str.size()); + str.resize(std::char_traits::length(str.c_str())); } return str; } @@ -342,12 +343,12 @@ void Connection::sendCopy(const uint8_t* ptr, size_t len, uint8_t queue, size_t auto data = (uint8_t*)malloc(len); assert(data != nullptr); memcpy(data, ptr, len); - send(data::out_buffer(data, len), queue, queue_size); + send(read_buffer(data, len), queue, queue_size); } -void Connection::send(const data::out_view &view, uint8_t queue, size_t queue_size) { - send(data::out_buffer(view), queue, queue_size); +void Connection::send(const read_view &view, uint8_t queue, size_t queue_size) { + send(read_buffer(view), queue, queue_size); } -void Connection::send(const data::out_buffer& buffer, uint8_t queue_id, size_t queue_size) { +void Connection::send(const read_buffer& buffer, uint8_t queue_id, size_t queue_size) { if (cnx == nullptr) return; diff --git a/src/core/net/Context.hpp b/src/core/net/Context.hpp index f581ad6..1c264f7 100644 --- a/src/core/net/Context.hpp +++ b/src/core/net/Context.hpp @@ -1,12 +1,14 @@ #pragma once -#include "../../core/net/data.hpp" -#include "../../core/data/mem.hpp" +#include "../memory.hpp" +#include "./protocol.hpp" #include #include namespace net { +using namespace memory; + /// Abstract stream struct stream_ctx { stream_ctx(uint64_t id): stream_id(id) { } @@ -20,16 +22,16 @@ struct stream_ctx { }; /// Outgoing stream struct out_stream_ctx: stream_ctx { - out_stream_ctx(uint64_t id, uint8_t queue_id, const data::out_buffer& buf): + out_stream_ctx(uint64_t id, uint8_t queue_id, const read_buffer& buf): stream_ctx(id), buffer(buf), queue_id(queue_id) { } - data::out_buffer buffer; + read_buffer buffer; uint8_t queue_id; }; /// Incoming stream struct in_stream_ctx: stream_ctx { in_stream_ctx(uint64_t id): stream_ctx(id) { } - data::in_vector buffer; + write_vector buffer; }; /// Informations about packets enum class PacketFlags { @@ -59,7 +61,6 @@ public: virtual bool isRunning() const = 0; constexpr picoquic_quic_t *getHandle() { - assert(quic != nullptr); return quic; } @@ -69,6 +70,8 @@ protected: /// Read-write on sockets and notify callbacks void pull(uint64_t maxDelay, uint64_t maxIn, uint64_t maxOut); + /// Write remaining data on sockets + void pushRemaining(); private: picoquic_quic_t *quic = nullptr; @@ -99,7 +102,7 @@ public: constexpr bool operator==(const Connection &other) const { return other.contains(cnx); } /// Close connection - void release(uint16_t reason); + void release(bool is_app); void setCallback(picoquic_stream_data_cb_fn cb_fn, void *ctx); @@ -114,10 +117,10 @@ public: void sendCopy(const uint8_t* ptr, size_t size, uint8_t queue = 0, size_t queue_size = 0); /// Send reliable data /// take view ownership - void send(const data::out_view &view, uint8_t queue = 0, size_t queue_size = 0); + void send(const read_view &view, uint8_t queue = 0, size_t queue_size = 0); /// Send reliable data /// buffer must stay valid until handle is freed - void send(const data::out_buffer& buffer, uint8_t queue = 0, size_t queue_size = 0); + void send(const read_buffer& buffer, uint8_t queue = 0, size_t queue_size = 0); template void emit(const D &data) { emit((const uint8_t*)&data, sizeof(D)); } @@ -150,7 +153,7 @@ private: bool is_client; struct queue_ctx { - data::out_buffer pending = data::out_buffer(); + read_buffer pending = read_buffer(); std::forward_list streams; }; std::vector outgoing; diff --git a/src/core/net/io.hpp b/src/core/net/io.hpp deleted file mode 100644 index 7583897..0000000 --- a/src/core/net/io.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include "data.hpp" -#include "../data/mem.hpp" - -namespace net { - -//TODO: preallocate static data pool - -/// Helper to write allocated packets -class PacketWriter final: public data::in_writer { -public: - PacketWriter(size_t size): data::in_writer(size) { } - PacketWriter(uint8_t type, size_t data_size): PacketWriter(sizeof(type) + data_size) { - write(type); - } - PacketWriter(net::client_packet_type type, size_t data_size): PacketWriter((uint8_t)type, data_size) { } - PacketWriter(net::server_packet_type type, size_t data_size): PacketWriter((uint8_t)type, data_size) { } - - [[nodiscard]] - data::out_buffer finish() { - assert(isFull()); - return data::out_buffer(*this); - } - - [[nodiscard]] - static data::out_buffer Of(net::client_packet_type type, const void* data, size_t size) { - auto packet = PacketWriter(type, size); - packet.write(data, size); - return packet.finish(); - } - [[nodiscard]] - static data::out_buffer Of(net::server_packet_type type, const void* data, size_t size) { - auto packet = PacketWriter(type, size); - packet.write(data, size); - return packet.finish(); - } - template - [[nodiscard]] - static data::out_buffer Of(net::client_packet_type type, const D& data) { - return Of(type, &data, sizeof(data)); - } - template - [[nodiscard]] - static data::out_buffer Of(net::server_packet_type type, const D& data) { - return Of(type, &data, sizeof(data)); - } -}; - -/// Helper to read out_view -class PacketReader final: public data::out_reader { -public: - PacketReader(const data::out_view& buf): data::out_reader(buf) { } -}; - -} \ No newline at end of file diff --git a/src/core/net/data.hpp b/src/core/net/protocol.hpp similarity index 64% rename from src/core/net/data.hpp rename to src/core/net/protocol.hpp index 7fa7282..1b57d6b 100644 --- a/src/core/net/data.hpp +++ b/src/core/net/protocol.hpp @@ -13,56 +13,58 @@ constexpr auto ALPN = "univerxel_quic"; /// Reasons for client server connection to break enum class disconnect_reason: uint16_t { UNEXPECTED = 0, - // Client quit + /// Client quit QUIT = 1, - // Server close + /// Server close CLOSE = 2, - // Server full + /// Server full FULL = 3, + /// Bad authentication + UNAUTHORIZED = 4, PROTOCOL_VIOLATION = 15, }; /// Packets from server to client enum class server_packet_type: uint8_t { - /// MAYBE: HELLO = 0, + //MAYBE: HELLO = 0, /// Must close connection - /// bool(is_application) reason(uint16_t) + /// reason(uint16_t) QUIT = UINT8_MAX, /// Set client position and instance id - /// size_t(playerId) voxel_pos + /// node_id(entityId) relative_transform TELEPORT = 1, - /// List all areas - /// {area_id, world::Area::params}[] - AREAS = 16, /// Average of preloaded chunks in region - /// {area_, {region_chunk_pos, Voxel}[]} + /// {area_region_ref, {region_chunk_pos, Voxel}[]} /// Voxel swap bit indicate a chunk full of Voxel without flag bit REGION = 17, /// Full chunk update - /// {area_, zstd} + /// {node_chunk_ref, zstd} /// empty: all sent CHUNK = 18, /// Uncompressed chunk changes - /// {area_id, {chunk_pos, uint16_t(count), Chunk::Edit[]}[]} + /// {node_ref, {chunk_pos, uint16_t(count), Chunk::Edit[]}[]} RAW_EDITS = 19, /// Chunk changes instruction /// action::FillShape EDITS = 20, - /// Declare entities types - /// {size_t(index), usvec3(size), vec3(scale), flags(000000, area, permanant)}[] - ENTITY_TYPES = 32, - - /// Update entities instances position and velocity - /// {size_t(entity), size_t(count), {size_t(index), ifvec3(pos), vec3(velocity)}[]}[] + /// Update entities instances hierarchy, position, velocity and content + /// {node_id(id), ...}[] + /// if Has: {start_point, ...} + /// -- if Is: model_id + /// -- if Is: usvec3(size) + /// -- if Is: Area::params + /// -- else {} + /// if Has: start_point + /// if Has: {} ENTITIES = 33, - /// Entity type model data - /// {size_t(entity), bool(area), render::Data::Model || {size_t(size), zstd}[]} - ENTITY_SHAPE = 34, + /// Model data + /// model_id, char(data)[] (not null terminated) + MODEL = 42, /// World compression dictionary /// zstd dict @@ -72,40 +74,53 @@ enum class server_packet_type: uint8_t { //MAYBE: use uint8_t flags CAPABILITIES = 65, + /// Provide authentication token + /// char[] (not null terminated) + REGISTERED = 127, + /// Public chat message /// char[] (not null terminated) MESSAGE = 129, }; /// Packets from client to server enum class client_packet_type: uint8_t { - /// Interact with voxels - /// actions::FillShape - FILL_SHAPE = 0, - - /// Request entity chunks - /// size_t(id) - MISSING_ENTITY = 6, - - /// Request missing regions - /// area_id, region_pos[] - MISSING_REGIONS = 7, - - /// Request missing chunks - /// area_id, chunk_pos[max: MAX_PENDING_CHUNK_COUNT] - MISSING_CHUNKS = 8, - - /// Send public text message - /// char[] (not null terminated) - MESSAGE = 9, + /// Authenticate + /// uint8_t(type), payload + /// Register: 0, char(name)[] + /// Login: 64, char(name)[] char[] (not null terminated) + HELLO = 0, /// Position update (unreliable) - /// (sequence)uint64_t voxel_pos + /// relative_pos MOVE = 16, + /// Request missing regions + /// area_id, region_pos[max: MAX_PENDING_CHUNK_COUNT] + REQUEST_REGIONS = 17, + /// Request missing chunks + /// node_id, chunk_pos[max: MAX_PENDING_CHUNK_COUNT] + REQUEST_CHUNKS = 18, + + /// Interact with voxels + /// actions::FillShape + FILL_SHAPE = 20, + + /// Request entity hierarchy + /// node_id(root) + REQUEST_ENTITIES = 33, + + /// Request model data + /// model_id + REQUEST_MODEL = 42, + /// Client capabilities /// bool(edit_handling) //MAYBE: use uint8_t flags CAPABILITIES = 65, + + /// Send public text message + /// char[] (not null terminated) + MESSAGE = 129, }; constexpr auto MAX_PENDING_CHUNK_COUNT = 256; diff --git a/src/core/config.hpp b/src/core/options_full.hpp similarity index 86% rename from src/core/config.hpp rename to src/core/options_full.hpp index 9c6b3cc..79db84a 100644 --- a/src/core/config.hpp +++ b/src/core/options_full.hpp @@ -1,8 +1,7 @@ #pragma once -#include #include -#include +#include "./utils/toml.hpp" #include #include "../server/config.hpp" #include "../client/config.hpp" @@ -17,11 +16,12 @@ public: /// Load from path options(const std::string &path): path(path) { auto config = [&] { - if(std::filesystem::exists(path)) + try { return toml::parse_file(path); - - LOG_E("Config file " << path << " not found. Creating default"); - return toml::table(); + } catch(const toml::parse_error& e) { + LOG_E("Failed to read config at " << path << ". " << e.description()); + return toml::table(); + } }(); if (config["server"]["enabled"].value_or(true)) { _server = server::options(config["server"]); diff --git a/src/core/standalone_config.hpp b/src/core/options_part.hpp similarity index 61% rename from src/core/standalone_config.hpp rename to src/core/options_part.hpp index 29eb48f..3807ce9 100644 --- a/src/core/standalone_config.hpp +++ b/src/core/options_part.hpp @@ -1,9 +1,8 @@ #pragma once -#include +#include "./utils/logger.hpp" #include -#include -#include +#include "./utils/toml.hpp" namespace config { @@ -11,22 +10,20 @@ constexpr auto DEFAULT_FILE = "config.toml"; /// Standalone options template -struct standalone_options { +struct options_part { public: /// Load from path - standalone_options(const std::string &path): path(path) { + options_part(const std::string &path): path(path) { auto config = toml::table({{"c", [&] { - if(std::filesystem::exists(path)) + try { return toml::parse_file(path); - - LOG_E("Config file " << path << " not found. Creating default"); - return toml::table(); + } catch(const toml::parse_error& e) { + LOG_E("Failed to read config at " << path << ". " << e.description()); + return toml::table(); + } }()}}); val = new O(config["c"]); } - ~standalone_options() { - delete val; - } /// Write to path void save() { std::ofstream out; @@ -34,11 +31,14 @@ public: out << val->save() << "\n\n"; out.close(); } + ~options_part() { + delete val; + } O &get() { return *val; } private: std::string path; - O* val; + owner val; }; } \ No newline at end of file diff --git a/src/core/queue/circular_buffer.hpp b/src/core/queue/circular_buffer.hpp new file mode 100644 index 0000000..f5970bb --- /dev/null +++ b/src/core/queue/circular_buffer.hpp @@ -0,0 +1,38 @@ +#pragma once +#include + +/// Generic containers +namespace data { + /// Looping array + template + struct circular_buffer: public std::array { + using buffer = std::array; + + circular_buffer(): buffer() { } + circular_buffer(const T& zero): buffer() { + buffer::fill(zero); + } + + size_t next = 0; + + void move_back() { + next++; + if (next >= buffer::size()) + next -= buffer::size(); + } + void push_back(const T& in) { + buffer::at(next) = in; + move_back(); + } + + template + void iter(F f) const { + for (size_t i = next; i < buffer::size(); i++) { + f(buffer::at(i)); + } + for (size_t i = 0; i < next; i++) { + f(buffer::at(i)); + } + } + }; +} diff --git a/src/core/data/safe_priority_queue.hpp b/src/core/queue/safe_priority_queue.hpp similarity index 85% rename from src/core/data/safe_priority_queue.hpp rename to src/core/queue/safe_priority_queue.hpp index 711204c..848be7b 100644 --- a/src/core/data/safe_priority_queue.hpp +++ b/src/core/queue/safe_priority_queue.hpp @@ -23,7 +23,8 @@ namespace data { std::condition_variable_any cv; public: - std::pair, std::unique_lock> inserter() { + using inserter_t = std::pair, std::unique_lock>; + inserter_t inserter() { return std::make_pair([&](const K& key, const V& val, const W& weight) { heap.emplace_back(key, weight); std::push_heap(heap.begin(), heap.end(), cmpByWeight); @@ -38,6 +39,13 @@ namespace data { map.insert_or_assign(key, val); cv.notify_one(); } + void push(const K& key, V&& val, const W& weight) { + std::unique_lock lock(mutex); + heap.emplace_back(key, weight); + std::push_heap(heap.begin(), heap.end(), cmpByWeight); + map.insert_or_assign(key, std::move(val)); + cv.notify_one(); + } bool pop(std::pair& out) { std::unique_lock lock(mutex); @@ -52,11 +60,19 @@ namespace data { if(it == map.end()) return false; - out = std::make_pair(it->first, it->second); + out = std::move(std::make_pair(std::move(it->first), std::move(it->second))); map.erase(it); return true; } + std::optional currentWeight() { + std::unique_lock lock(mutex); + if (heap.empty()) + return std::nullopt; + + return heap.front().second; + } + bool empty() { std::unique_lock lock(mutex); return heap.empty(); diff --git a/src/core/data/safe_queue.hpp b/src/core/queue/safe_queue.hpp similarity index 77% rename from src/core/data/safe_queue.hpp rename to src/core/queue/safe_queue.hpp index d0272a6..8ebef91 100644 --- a/src/core/data/safe_queue.hpp +++ b/src/core/queue/safe_queue.hpp @@ -15,16 +15,21 @@ namespace data { TracyLockableN(std::mutex, mutex, "Queue"); std::condition_variable_any cv; + struct extractor_t { + std::function pop; + std::function empty; + std::function front; + std::function next; + std::unique_lock lock; + }; public: - std::pair, std::unique_lock> extractor() { - return std::make_pair([&](T& out) { - if (queue.empty()) - return false; - - out = queue.front(); + template + void extractor(F apply) { + std::unique_lock lock(mutex); + while (!queue.empty()) { + apply(queue.front()); queue.pop(); - return true; - }, std::unique_lock(mutex)); + } } void push(const T& in) { diff --git a/src/core/data/safe_unique_queue.hpp b/src/core/queue/safe_unique_queue.hpp similarity index 100% rename from src/core/data/safe_unique_queue.hpp rename to src/core/queue/safe_unique_queue.hpp diff --git a/src/core/queue/sorted_queue.hpp b/src/core/queue/sorted_queue.hpp new file mode 100644 index 0000000..983fd57 --- /dev/null +++ b/src/core/queue/sorted_queue.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include +#include +#include + +namespace data { + +/// Queue always sorted by incressing priority (pop: max priority) +/// K: key, W: weight, Unique: remove duplicates +/// Size: Optional size bound, Reverse: descressing priority (pop: min) +///NOTE: Not thread safe +template +class sorted_queue { +private: + std::vector> vec; + + static bool cmpByWeight(const W &a, const W &b) { + return (a < b) != Reverse; + } + +public: + void push(const K& key, const W& weight) { + if constexpr (Unique) { + // Remove previous + for (auto it = vec.begin(); it != vec.end(); ++it) { + if (it->first == key) { + vec.erase(it); + break; + } + } + } + + if constexpr (Size > 0) { + // Keep under Size + if (size() >= Size) { + if (cmpByWeight(weight, vec.front().second)) + return; + vec.erase(vec.begin()); + } + } + + auto it = vec.begin(); + while (it != vec.end() && !cmpByWeight(weight, it->second)) { + ++it; + } + vec.insert(it, std::make_pair(key, weight)); + } + + bool pop(K& out) { + if (empty()) + return false; + + out = vec.back().first; + vec.pop_back(); + return true; + } + + bool empty() const { + return vec.empty(); + } + + size_t size() const { + return vec.size(); + } + +}; +} \ No newline at end of file diff --git a/src/core/data/unique_queue.hpp b/src/core/queue/unique_queue.hpp similarity index 100% rename from src/core/data/unique_queue.hpp rename to src/core/queue/unique_queue.hpp diff --git a/src/core/server_handle.hpp b/src/core/server_handle.hpp deleted file mode 100644 index 0adf54a..0000000 --- a/src/core/server_handle.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "world/Universe.hpp" -#include "world/actions.hpp" -#include "geometry/Faces.hpp" - -/// Link between world::server::SharedUniverse and world::client::LocalUniverse -struct server_handle { - bool running = false; - const world::client::area_map *areas; - const data::generational::vector *entities; - std::function &pos, const chunk_pos &offset, const world::ChunkContainer &data, geometry::Faces neighbors)> onUpdate; - std::function emit; - std::function raycast; - std::optional teleport; - std::optional message; - bool entityChange = false; -}; diff --git a/src/core/utils/defer.hpp b/src/core/utils/defer.hpp new file mode 100644 index 0000000..62ac998 --- /dev/null +++ b/src/core/utils/defer.hpp @@ -0,0 +1,25 @@ +#pragma once + +template class defer_handle { +public: + defer_handle(defer_handle &&) = delete; + defer_handle(const defer_handle &) = delete; + defer_handle &operator=(const defer_handle &) = delete; + defer_handle &operator=(defer_handle &&) = delete; + + template defer_handle(FF &&f) : clean(std::forward(f)) {} + ~defer_handle() { clean(); } + +private: + F clean; +}; + +template defer_handle defer(F &&f) { + return {std::forward(f)}; +} + +#ifdef __COUNTER__ +#define DEFER(lambda__) const auto &defer_object_##__COUNTER__ = defer([&]() lambda__); +#else +#define DEFER(lambda__) const auto &defer_object_##__LINE__ = defer([&]() lambda__); +#endif \ No newline at end of file diff --git a/src/core/flags.hpp b/src/core/utils/flags.hpp similarity index 77% rename from src/core/flags.hpp rename to src/core/utils/flags.hpp index de69b1e..f534320 100644 --- a/src/core/flags.hpp +++ b/src/core/utils/flags.hpp @@ -21,4 +21,9 @@ #define _FORCE_INLINE_ _ALWAYS_INLINE_ #endif -#endif \ No newline at end of file +#endif + +#include +/// Mark ownership of raw pointers +template ::value>> +using owner = T; \ No newline at end of file diff --git a/src/core/utils/io.hpp b/src/core/utils/io.hpp new file mode 100644 index 0000000..7334c8f --- /dev/null +++ b/src/core/utils/io.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include "../memory.hpp" +#include "./logger.hpp" + +namespace io { + +/// Map vector to istream +struct vec_istream: std::streambuf { + vec_istream(const std::vector &vec) { + this->setg((char*)&vec[0], (char*)&vec[0], (char*)&vec[0] + vec.size()); + } +}; + +using namespace memory; +/// File loader +class file_content: public std::vector { +public: + /// Read first + file_content(const std::vector& paths, read_view prefix = read_view(nullptr, 0)): + std::vector(), prefix_size(prefix.size()) + { + std::ifstream is = [&]() { + for(auto& path: paths) { + std::ifstream is(path, std::ios::in | std::ios::binary | std::ios::ate); + if(is.good()) { + return is; + } + is.close(); + } + FATAL("File not found " << paths.back()); + }(); + const auto end = is.tellg(); + is.seekg(0, std::ios::beg); + resize(end - is.tellg() + prefix_size); + memcpy(data(), prefix.ptr, prefix_size); + is.read(data() + prefix_size, size()); + is.close(); + } + + size_t prefix_size; + + /// Data after prefix + read_view content() const { + return read_view((const uint8_t*)data() + prefix_size, size() - prefix_size); + } +}; + +class ifstream: public std::ifstream { +public: + ifstream(const std::string& path, std::ios_base::openmode mode = std::ios_base::in): + std::ifstream(path, mode) { } + + template + void read_cast(D& d) { + std::ifstream::read(reinterpret_cast(&d), sizeof(d)); + } +}; + +class ofstream: public std::ofstream { +public: + ofstream(const std::string& path, std::ios_base::openmode mode = std::ios_base::out): + std::ofstream(path, mode) { } + + template + void write_cast(const D& d) { + std::ofstream::write(reinterpret_cast(&d), sizeof(d)); + } +}; + +} \ No newline at end of file diff --git a/src/core/utils/logger.hpp b/src/core/utils/logger.hpp index 6454f9e..c4e1e69 100644 --- a/src/core/utils/logger.hpp +++ b/src/core/utils/logger.hpp @@ -7,18 +7,22 @@ #include #include +#ifndef LOG_PREFIX +#define LOG_PREFIX "" +#endif #define _OUT(expr) {std::ostringstream oss; oss << expr << std::endl; std::cout << oss.str();} -#define LOG(expr) _OUT("[" << BOLD << logger::now() << END_COLOR << "] " << BOLD << expr << END_COLOR) -#define LOG_E(expr) _OUT("[" << RED << logger::now() << END_COLOR << "] " << expr) -#define LOG_W(expr) _OUT("[" << YELLOW << logger::now() << END_COLOR << "] " << expr) -#define LOG_I(expr) _OUT("[" << GREEN << logger::now() << END_COLOR << "] " << expr) +#define _LOG(expr, color) _OUT("[" color << logger::now() << END_COLOR "] " LOG_PREFIX << expr) +#define LOG(expr) _LOG(BOLD << expr << END_COLOR, BOLD) +#define LOG_E(expr) _LOG(expr, RED) +#define LOG_W(expr) _LOG(expr, YELLOW) +#define LOG_I(expr) _LOG(expr, GREEN) #if LOG_DEBUG -#define LOG_D(expr) _OUT("[" << END_COLOR << logger::now() << END_COLOR << "] " << expr) +#define LOG_D(expr) _LOG(expr, END_COLOR) #else #define LOG_D(expr) #endif #if LOG_TRACE -#define LOG_T(expr) _OUT("[" << GREY << logger::now() << END_COLOR << "] " << expr) +#define LOG_T(expr) _LOG(expr, GREY) #else #define LOG_T(expr) #endif diff --git a/src/core/utils/mutex.hpp b/src/core/utils/mutex.hpp new file mode 100644 index 0000000..c3abd02 --- /dev/null +++ b/src/core/utils/mutex.hpp @@ -0,0 +1,480 @@ +#pragma once + +#include +#include + +namespace mutex { + +/// Base for virtual unique guards +template +class abstract_unique_guard { +public: + class deleter; + class impl; + using handle = std::unique_ptr; + + virtual ~abstract_unique_guard() { } + + [[nodiscard]] virtual handle lock() = 0; + + /// May return a null handle + [[nodiscard]] virtual handle try_lock() = 0; +}; + +template +class abstract_unique_guard::impl { +public: + using pointer = T *; + + virtual ~impl() { } + + virtual void operator()(pointer) = 0; +}; + +template +class abstract_unique_guard::deleter { +public: + using pointer = T *; + + deleter(std::unique_ptr lock): + lock(std::move(lock)) { } + + void operator()(pointer ptr) { + if (lock) (*lock)(ptr); + } + + private: + std::unique_ptr lock; +}; + +/// Base for virtual shared guards +template +class abstract_shared_guard { +public: + class deleter; + class impl; + using handle = std::unique_ptr; + + virtual ~abstract_shared_guard() { } + + [[nodiscard]] virtual handle lock_shared() const = 0; + + /// May return a null handle + [[nodiscard]] virtual handle try_lock_shared() const = 0; +}; + +template +class abstract_shared_guard::impl { +public: + using pointer = const T *; + + virtual ~impl() { } + + virtual void operator()(pointer) = 0; +}; + +template +class abstract_shared_guard::deleter { +public: + using pointer = const T *; + + deleter(std::unique_ptr lock): + lock(std::move(lock)) { } + + void operator()(pointer ptr) { + if (lock) (*lock)(ptr); + } + + private: + std::unique_ptr lock; +}; + +template +class unique_guard { +private: + class deleter; + class impl; + +public: + using handle = std::unique_ptr; + using virtual_handle = typename abstract_unique_guard::handle; + + template + unique_guard(_Args &&... __args): + data(std::forward<_Args>(__args)...) { } + + [[nodiscard]] handle lock(); + [[nodiscard]] virtual_handle lock_abstract(); + + /// May return a null handle + [[nodiscard]] handle try_lock(); + + /// May return a null handle + /// Requires try_lock_for method on mutex type M + template + [[nodiscard]] handle try_lock_for(const Duration &duration); + +private: + T data; + M mutex; +}; + +template +class unique_guard::deleter { +public: + using pointer = T *; + + deleter(std::unique_lock lock): + lock(std::move(lock)) { } + + void operator()(pointer) { + if (lock.owns_lock()) + lock.unlock(); + } + + private: + std::unique_lock lock; +}; + +template +class unique_guard::impl final: + public abstract_unique_guard::impl { +public: + using pointer = T *; + + impl(std::unique_lock lock): + lock(std::move(lock)) { } + + void operator()(pointer) override { + if (lock.owns_lock()) + lock.unlock(); + } + + private: + std::unique_lock lock; +}; + +template +auto unique_guard::lock() -> handle { + std::unique_lock lock(mutex); + return handle(&data, deleter(std::move(lock))); +} +template +auto unique_guard::lock_abstract() -> virtual_handle { + std::unique_lock lock(mutex); + auto impl = std::make_unique::impl>(std::move(lock)); + return virtual_handle(&data, abstract_unique_guard::deleter(std::move(impl))); +} + +template +auto unique_guard::try_lock() -> handle { + std::unique_lock lock(mutex, std::try_to_lock); + return handle(lock.owns_lock() ? &data : nullptr, + deleter(std::move(lock))); +} + +template +template +auto unique_guard::try_lock_for(const Duration &d) -> handle { + std::unique_lock lock(mutex, d); + return handle(lock.owns_lock() ? &data : nullptr, + deleter(std::move(lock))); +} + +template > +class shared_guard_ref; + +template > +class shared_guard { +private: + class deleter; + class impl; + class shared_deleter; + class shared_impl; + +public: + using handle = std::unique_ptr; + using shared_handle = std::unique_ptr; + using virtual_handle = typename abstract_unique_guard::handle; + using virtual_shared_handle = typename abstract_shared_guard::handle; + + template + shared_guard(_Args &&... __args): + data(std::forward<_Args>(__args)...) { } + + [[nodiscard]] handle lock(); + [[nodiscard]] virtual_handle lock_abstract(); + + /// May return a null handle + [[nodiscard]] handle try_lock(); + + /// May return a null handle + /// Requires try_lock_for method on mutex type M + template + [[nodiscard]] handle try_lock_for(const Duration &duration); + + + [[nodiscard]] shared_handle lock_shared() const; + [[nodiscard]] virtual_shared_handle lock_shared_abstract() const; + + /// May return a null handle + [[nodiscard]] shared_handle try_lock_shared() const; + + /// May return a null handle + /// Requires try_lock_for method on mutex type M + template + [[nodiscard]] shared_handle try_lock_shared_for(const Duration &duration) const; + + /// Shared only ref + template + [[nodiscard]] shared_guard_ref make_ref() const; + +private: + T data; + mutable M mutex; +}; + +template +class shared_guard::deleter { +public: + using pointer = T *; + + deleter(std::unique_lock lock): + lock(std::move(lock)) { } + + void operator()(pointer) { + if (lock.owns_lock()) + lock.unlock(); + } + + private: + std::unique_lock lock; +}; + +template +class shared_guard::impl final: + public abstract_unique_guard::impl { +public: + using pointer = T *; + + impl(std::unique_lock lock): + lock(std::move(lock)) { } + + void operator()(pointer) override { + if (lock.owns_lock()) + lock.unlock(); + } + + private: + std::unique_lock lock; +}; + +template +class shared_guard::shared_deleter { +public: + using pointer = const T *; + + shared_deleter(L lock): + lock(std::move(lock)) { } + + void operator()(pointer) { + if (lock.owns_lock()) + lock.unlock(); + } + + private: + L lock; +}; + +template +class shared_guard::shared_impl final: + public abstract_shared_guard::impl { +public: + using pointer = const T *; + + shared_impl(L lock): + lock(std::move(lock)) { } + + void operator()(pointer) override { + if (lock.owns_lock()) + lock.unlock(); + } + + private: + L lock; +}; + +template +auto shared_guard::lock() -> handle { + std::unique_lock lock(mutex); + return handle(&data, deleter(std::move(lock))); +} +template +auto shared_guard::lock_abstract() -> virtual_handle { + std::unique_lock lock(mutex); + auto impl = std::make_unique::impl>(std::move(lock)); + return virtual_handle(&data, abstract_unique_guard::deleter(std::move(impl))); +} + +template +auto shared_guard::try_lock() -> handle { + std::unique_lock lock(mutex, std::try_to_lock); + return handle(lock.owns_lock() ? &data : nullptr, + deleter(std::move(lock))); +} + +template +template +auto shared_guard::try_lock_for(const Duration &d) -> handle { + std::unique_lock lock(mutex, d); + return handle(lock.owns_lock() ? &data : nullptr, + deleter(std::move(lock))); +} + +template +auto shared_guard::lock_shared() const -> shared_handle { + L lock(mutex); + return shared_handle(&data, shared_deleter(std::move(lock))); +} +template +auto shared_guard::lock_shared_abstract() const -> virtual_shared_handle { + L lock(mutex); + auto impl = std::make_unique::shared_impl>(std::move(lock)); + return virtual_shared_handle(&data, abstract_shared_guard::deleter(std::move(impl))); +} + +template +auto shared_guard::try_lock_shared() const -> shared_handle { + L lock(mutex, std::try_to_lock); + return shared_handle(lock.owns_lock() ? &data : nullptr, + shared_deleter(std::move(lock))); +} + +template +template +auto shared_guard::try_lock_shared_for(const Duration &d) const -> shared_handle { + L lock(mutex, d); + return shared_handle(lock.owns_lock() ? &data : nullptr, + shared_deleter(std::move(lock))); +} + +template +class shared_guard_ref { +private: + class shared_deleter; + class shared_impl; + +public: + using shared_handle = std::unique_ptr; + using virtual_shared_handle = typename abstract_shared_guard::handle; + + shared_guard_ref(const T* data, M* mutex): + data(data), mutex(mutex) { } + shared_guard_ref(): shared_guard_ref(nullptr, nullptr) { } + + [[nodiscard]] shared_handle lock_shared() const; + [[nodiscard]] virtual_shared_handle lock_shared_abstract() const; + + /// May return a null handle + [[nodiscard]] shared_handle try_lock_shared() const; + + /// May return a null handle + /// Requires try_lock_for method on mutex type M + template + [[nodiscard]] shared_handle try_lock_shared_for(const Duration &duration) const; + +private: + const T* data; + mutable M* mutex; +}; + +template +template +auto shared_guard::make_ref() const -> shared_guard_ref { + return shared_guard_ref((const Tr*)&data, &mutex); +} + +template +class shared_guard_ref::shared_deleter { +public: + using pointer = const T *; + + shared_deleter(L lock): + lock(std::move(lock)) { } + + void operator()(pointer) { + if (lock.owns_lock()) + lock.unlock(); + } + + private: + L lock; +}; + +template +class shared_guard_ref::shared_impl final: + public abstract_shared_guard::impl { +public: + using pointer = const T *; + + shared_impl(L lock): + lock(std::move(lock)) { } + + void operator()(pointer) override { + if (lock.owns_lock()) + lock.unlock(); + } + + private: + L lock; +}; + +template +auto shared_guard_ref::lock_shared() const -> shared_handle { + L lock(*mutex); + return shared_handle(data, shared_deleter(std::move(lock))); +} +template +auto shared_guard_ref::lock_shared_abstract() const -> virtual_shared_handle { + L lock(*mutex); + auto impl = std::make_unique(std::move(lock)); + return virtual_shared_handle(data, typename abstract_shared_guard::deleter(std::move(impl))); +} + +template +auto shared_guard_ref::try_lock_shared() const -> shared_handle { + L lock(*mutex, std::try_to_lock); + return shared_handle(lock.owns_lock() ? data : nullptr, + shared_deleter(std::move(lock))); +} + +template +template +auto shared_guard_ref::try_lock_shared_for(const Duration &d) const -> shared_handle { + L lock(*mutex, d); + return shared_handle(lock.owns_lock() ? data : nullptr, + shared_deleter(std::move(lock))); +} + +/// Fake lock for abstract guards +template +class not_guard { +public: + using handle = typename abstract_unique_guard::handle; + using shared_handle = typename abstract_shared_guard::handle; + + static handle OfUnique(T*); + static shared_handle OfShared(const T*); +}; + +template +auto not_guard::OfUnique(T* ptr) -> handle { + return handle(ptr, typename abstract_unique_guard::deleter(nullptr)); +} + +template +auto not_guard::OfShared(const T* ptr) -> shared_handle { + return shared_handle(ptr, typename abstract_shared_guard::deleter(nullptr)); +} + +}; \ No newline at end of file diff --git a/src/core/utils/toml.cpp b/src/core/utils/toml.cpp new file mode 100644 index 0000000..c412985 --- /dev/null +++ b/src/core/utils/toml.cpp @@ -0,0 +1,2 @@ +#define TOML_IMPLEMENTATION +#include "./toml.hpp" \ No newline at end of file diff --git a/src/core/utils/toml.hpp b/src/core/utils/toml.hpp new file mode 100644 index 0000000..e85ca0a --- /dev/null +++ b/src/core/utils/toml.hpp @@ -0,0 +1,3 @@ +#pragma once +#define TOML_HEADER_ONLY 0 +#include \ No newline at end of file diff --git a/src/core/utils/zctx.hpp b/src/core/utils/zctx.hpp index cec8c27..82e6129 100644 --- a/src/core/utils/zctx.hpp +++ b/src/core/utils/zctx.hpp @@ -1,7 +1,8 @@ #pragma once #include -#include "../data/mem.hpp" +#include +#include "../memory.hpp" #include "logger.hpp" namespace zstd { @@ -72,7 +73,7 @@ namespace zstd { class dict_set { public: dict_set(const std::vector& data) { load(data.data(), data.size()); } - dict_set(const data::out_view& data) { load(data.data(), data.size()); } + dict_set(const memory::read_view& data) { load(data.data(), data.size()); } ~dict_set() { ZSTD_freeCDict(c); ZSTD_freeDDict(d); diff --git a/src/core/world/Area.cpp b/src/core/world/Area.cpp new file mode 100644 index 0000000..52bb155 --- /dev/null +++ b/src/core/world/Area.cpp @@ -0,0 +1,23 @@ +#include "Area.hpp" + +#include "../geometry/math.hpp" +#include "../geometry/Box.hpp" +#include "Elements.hpp" + +using namespace world; + +bool ChunkContainer::inRange(const chunk_pos& pos) const { + return glm::all(glm::lessThan(glm::abs(pos), chunk_pos(radius))); +} +std::shared_ptr ChunkContainer::findInRange(const chunk_pos &pos) const { + const auto it = find(pos); + return it != end() ? it->second : nullptr; +} + +void Area::free(const node_id& i, Elements& l) { + l.areas.erase(i); +} +aabb Area::getBoundingBox(const Elements&) const { + return aabb(glm::lfvec3(-CHUNK_LENGTH * (chunks.getRadius() - 1)), + glm::lfvec3(CHUNK_LENGTH * chunks.getRadius())); +} \ No newline at end of file diff --git a/src/core/world/Area.hpp b/src/core/world/Area.hpp index abe287f..7a6ec70 100644 --- a/src/core/world/Area.hpp +++ b/src/core/world/Area.hpp @@ -1,64 +1,121 @@ #pragma once -#include "../../core/world/forward.h" -#include "../../core/geometry/IBox.hpp" -#include "../../core/data/math.hpp" +#include +#include "./Node.hpp" +#include "./EdittableChunk.hpp" namespace world { - /// Chunk map with restricted access - struct ChunkContainer: robin_hood::unordered_map> { - private: + +/// Chunk map with restricted access +struct ChunkContainer: robin_hood::unordered_map> { +private: + int radius; + +public: + /// Area radius in chunks + constexpr inline int getRadius() const { return radius; } + ChunkContainer(int radius) { + resize(radius); + } + void resize(int radius) { + assert(radius > 0 && radius < (1 << 22) / CHUNK_LENGTH); + this->radius = radius; + } + bool inRange(const chunk_pos& pos) const; + std::shared_ptr findInRange(const chunk_pos &pos) const; +}; + +/// Multi region element (aka big group of Chunk) +class Area: public Element { +public: + /// radius: size in chunk (length = radius * 2 + 1) + Area(int radius): chunks(radius) { } + + void free(const node_id &i, Elements &l) override; + aabb getBoundingBox(const Elements &l) const override; + + inline const ChunkContainer &getChunks() const { return chunks; } + inline ChunkContainer &setChunks() { return chunks; } + + virtual std::optional getCurvature() const = 0; + + struct params { int radius; - - public: - /// Area radius in chunks - constexpr inline int getRadius() const { return radius; } - ChunkContainer(int radius): radius(radius) { - assert(radius > 0 && radius < (1 << 22) / CHUNK_LENGTH); - } - inline bool inRange(const chunk_pos& pos) const { - return glm::abs(pos.x) < radius && glm::abs(pos.y) < radius && glm::abs(pos.z) < radius; - } - std::optional> findInRange(const chunk_pos &pos) const { - const auto it = find(pos); - return it != end() ? std::make_optional(it->second) : std::nullopt; - } + std::optional curvature; }; - /// Area (aka big group of Chunk) - struct Area { - public: - /// radius: size in chunk (length = radius * 2 + 1) - Area(voxel_pos center, int radius): center(center), chunks(radius) { } +private: + ChunkContainer chunks; +}; - inline const area_pos &getOffset() const { return center; } - inline geometry::IBox getBounding() const { - const auto c = center.as_voxel(); - return geometry::IBox(c - voxel_pos(CHUNK_LENGTH * (chunks.getRadius() - 1)), - c + voxel_pos(CHUNK_LENGTH * chunks.getRadius())); - } - /// Move offset return if chunk_change - bool inline move(const area_pos::offset_t &offset) { - const auto prev = glm::divide(center.as_voxel()); - center = center + offset; - return prev != glm::divide(center.as_voxel()); - } +using area_id = generational::key; +using area_ref = generational::ref; - inline const ChunkContainer &getChunks() const { return chunks; } - inline ChunkContainer &setChunks() { return chunks; } +/// Absolute area region +struct area_region_pos { + area_id area; + region_pos region; +}; +/// Absolute area region without generation +struct area_region_ref { + area_region_ref(area_ref r, region_pos p): area(r), region(p) { } + area_region_ref(const area_region_pos& p): area_region_ref(p.area, p.region) { } + area_ref area; + region_pos region; +}; +/// Absolute area chunk +struct area_chunk_pos { + area_id area; + chunk_pos chunk; + inline bool operator==(const area_chunk_pos& a) const { + return area == a.area && chunk == a.chunk; + } +}; - virtual std::optional getCurvature() const = 0; +/// Absolute area voxel +struct area_voxel_pos { + area_id area; + voxel_pos voxel; +}; +/// Absolute chunk inside area or part +struct node_chunk_pos { + node_id node; + chunk_pos chunk; + inline bool operator==(const node_chunk_pos& a) const { + return node == a.node && chunk == a.chunk; + } +}; +/// Absolute chunk inside area or part without generation +struct node_chunk_ref { + node_chunk_ref(node_ref r, chunk_pos p): node(r), chunk(p) { } + node_chunk_ref(const node_chunk_pos& p): node_chunk_ref(p.node, p.chunk) { } + node_ref node; + chunk_pos chunk; +}; +/// Absolute voxel inside area or part +struct node_voxel_pos { + node_id node; + voxel_pos voxel; +}; +/// Absolute voxel inside area or part without generation +struct node_voxel_ref { + node_voxel_ref(node_ref r, voxel_pos p): node(r), voxel(p) { } + node_voxel_ref(const node_voxel_pos& p): node_voxel_ref(p.node, p.voxel) { } + node_ref node; + voxel_pos voxel; +}; - struct params { - voxel_pos center; - int radius; - std::optional curvature; - }; - - protected: - area_pos center; - - private: - ChunkContainer chunks; - }; } + +namespace std { + +template<> +struct hash { + std::size_t operator()(const world::area_chunk_pos& a) const noexcept { + std::size_t h1 = std::hash{}(a.area.val); + std::size_t h2 = std::hash{}(a.chunk); + return h1 ^ (h2 << 1); + } +}; + +} \ No newline at end of file diff --git a/src/core/world/Chunk.cpp b/src/core/world/Chunk.cpp index eed7797..62c40f2 100644 --- a/src/core/world/Chunk.cpp +++ b/src/core/world/Chunk.cpp @@ -1,8 +1,7 @@ #include "Chunk.hpp" #include -#include -#include "../../core/data/math.hpp" +#include "../geometry/math.hpp" using namespace world; diff --git a/src/core/world/Chunk.hpp b/src/core/world/Chunk.hpp index 0ee2d49..2ab5592 100644 --- a/src/core/world/Chunk.hpp +++ b/src/core/world/Chunk.hpp @@ -1,7 +1,6 @@ #pragma once -#include -#include "forward.h" +#include #include "Voxel.hpp" namespace world { @@ -13,14 +12,6 @@ namespace world { Chunk(std::istream& str, bool rle = RLE); virtual ~Chunk(); - struct EditBody { - Voxel value; - float delay; - }; - struct Edit: EditBody { - chunk_voxel_idx idx; - }; - /// Get voxel from index inline const Voxel& get(chunk_voxel_idx idx) const { return voxels[idx]; diff --git a/src/core/world/EdittableChunk.cpp b/src/core/world/EdittableChunk.cpp index a53cff4..e921fe0 100644 --- a/src/core/world/EdittableChunk.cpp +++ b/src/core/world/EdittableChunk.cpp @@ -2,14 +2,19 @@ #include #include -#include "../../core/data/math.hpp" +#include "core/geometry/math.hpp" -using namespace world::client; +using namespace world; -EdittableChunk::EdittableChunk(): world::Chunk() { } +EdittableChunk::EdittableChunk(owner edits, std::istream& str, bool rle): world::Chunk(str, rle), edits(edits) { } +EdittableChunk::EdittableChunk(owner edits): world::Chunk(), edits(edits) { } EdittableChunk::~EdittableChunk() { } -std::optional EdittableChunk::update(float deltaTime, bool animate) { +void EdittableChunk::setAt(const chunk_voxel_pos &pos, const Voxel& voxel) { + return set(glm::toIdx(pos), voxel); +} + +std::optional ChunkEdits::update(float deltaTime, bool animate) { ZoneScopedN("Chunk"); for(auto it = edits.begin(); it != edits.end();) { it->second.delay -= deltaTime; @@ -29,7 +34,7 @@ std::optional EdittableChunk::update(float deltaTime, bool animate) { } } -void EdittableChunk::invalidate(uint16_t idx) { +void ChunkEdits::invalidate(uint16_t idx) { invalidate( ((!getNeighborIdx(idx, Face::Up).has_value()) & Faces::Up) | ((!getNeighborIdx(idx, Face::Down).has_value()) & Faces::Down) | @@ -38,20 +43,23 @@ void EdittableChunk::invalidate(uint16_t idx) { ((!getNeighborIdx(idx, Face::Forward).has_value()) & Faces::Forward) | ((!getNeighborIdx(idx, Face::Backward).has_value()) & Faces::Backward)); } -void EdittableChunk::apply(const Edit& edit) { - const auto prev = voxels[edit.idx]; +void ChunkEdits::add(const Edit& edit) { + edits.erase(edit.idx); + if(edit.delay > 0) { + edits.emplace(edit.idx, EditBody{edit.value, edit.delay}); + } else { + invalidate(edit.idx); + } +} +void ChunkEdits::apply(const Edit& edit) { + const auto prev = parent->get(edit.idx); if(prev.value != edit.value.value) { - voxels[edit.idx] = edit.value; - edits.erase(edit.idx); - if(edit.delay > 0) { - edits.emplace(edit.idx, EditBody{prev, edit.delay}); - } else { - invalidate(edit.idx); - } + parent->set(edit.idx, edit.value); + add(Edit{prev, edit.delay, edit.idx}); } } -std::optional EdittableChunk::getNeighborIdx(chunk_voxel_idx idx, Face dir) { +std::optional ChunkEdits::getNeighborIdx(chunk_voxel_idx idx, Face dir) { switch (dir) { case Face::Forward: if (idx % glm::IDX_LENGTH >= glm::IDX_LENGTH - 1) diff --git a/src/core/world/EdittableChunk.hpp b/src/core/world/EdittableChunk.hpp index c45bd43..e0832ac 100644 --- a/src/core/world/EdittableChunk.hpp +++ b/src/core/world/EdittableChunk.hpp @@ -4,10 +4,13 @@ #include "../geometry/Faces.hpp" using namespace geometry; -namespace world::client { - class EdittableChunk: public virtual world::Chunk { +namespace world { + class EdittableChunk; + class ChunkEdits { public: - virtual ~EdittableChunk(); + ChunkEdits(EdittableChunk* ptr, bool upToDate = false): + parent(ptr), upToDate(upToDate), toUpdate(upToDate ? Faces::None : Faces::All) { } + virtual ~ChunkEdits() { } /// Update voxels /// @return if modified neighbors to update @@ -20,22 +23,53 @@ namespace world::client { } void invalidate(chunk_voxel_idx idx); - void apply(const Chunk::Edit &edit); + struct EditBody { + Voxel value; + float delay; + }; + struct Edit: EditBody { + chunk_voxel_idx idx; + }; - using edits_t = robin_hood::unordered_map; - /// Get pending changes - const edits_t &getEdits() const { return edits; } + /// Save edits without chunk change + void add(const Edit &edit); + /// Change chunk and save edit + void apply(const Edit &edit); static std::optional getNeighborIdx(chunk_voxel_idx idx, Face dir); - protected: - EdittableChunk(); + using edits_t = robin_hood::unordered_map; + /// Get visible changes + const edits_t &getEdits() const { return edits; } + protected: /// Animated changes edits_t edits; + /// Data ptr + EdittableChunk *const parent; /// Require update - bool upToDate = true; + bool upToDate; /// Neighbors to update - Faces toUpdate = Faces::None; + Faces toUpdate; + }; + + class EdittableChunk: public world::Chunk { + public: + virtual ~EdittableChunk(); + + ChunkEdits* setEdits() { assert(edits); return edits.get(); } + const ChunkEdits& getEdits() const { assert(edits); return *edits.get(); } + + /// Direct set without update of voxel at index + inline void set(chunk_voxel_idx idx, const Voxel& voxel) { + voxels[idx] = voxel; + } + /// Direct set without update of voxel at position + void setAt(const chunk_voxel_pos &pos, const Voxel& voxel); + + protected: + EdittableChunk(owner, std::istream& str, bool rle = RLE); + EdittableChunk(owner); + const std::unique_ptr edits; }; } \ No newline at end of file diff --git a/src/core/world/Elements.cpp b/src/core/world/Elements.cpp new file mode 100644 index 0000000..52822aa --- /dev/null +++ b/src/core/world/Elements.cpp @@ -0,0 +1,105 @@ +#include "Elements.hpp" + +using namespace world; + +transform Elements::computeAbsolute(const node_id &pid, const transform& rel) const { + //FIXME: include angle + if (auto parent = nodes.directly_at(pid)) { + return parent->absolute.computeChild(rel); + } + return rel; +} +world_pos Elements::computeAbsolute(const node_ref &pref, const voxel_pos& rel) const { + if (auto parent = nodes.directly_at(pref)) { + return parent->absolute.computeChild(rel); + } + return rel; +} + +node_id Elements::findParent(const node_id& id) const { + if (const auto it = hierarchy.get(id)) { + return it->parent; + } + return generational::id(); +} + +EdittableElements::EdittableElements(bool loadModels) { + if (loadModels) { + module::Registry::Get()->extractModels([&](module::registered_model& model) { + const auto res = models.emplace(0, std::move(model.dt), model.collision, model.scale); + res.second->origin = std::make_shared(res.first); + }); + } +} + +void EdittableElements::remove(node_id i) { + auto id = nodes.with_flag(i); + extract(id.val.index()); + nodes.at(id).content->free(id, *this); + nodes.free(id); + nodes.set_flag(Set(id)); +} +void EdittableElements::removeRecursive(node_id i) { + hierarchy.erase_recursive(i.val.index(), [&](generational::hierarchy::entry *entry) { + auto id = nodes.with_flag(entry->self); + nodes.at(id).content->free(id, *this); + nodes.free(id); + nodes.set_flag(Set(id)); + }); +} + +void EdittableElements::move(node_id i, const relative_pos &p) { + const auto idx = i.val.index(); + auto it = hierarchy.get(idx); + assert(it); + if (it->parent != p.parent) { + //FIXME: keep velocity and rotation (make absolute) + extract(idx); //FIXME: move recursive + insert(idx, p); + } else if (it->relative.position != p.position) { + nodes.set_flag(Set(nodes.with_flag(i))); + it->relative.position = p.position; + } + nodes.at(i).absolute = computeAbsolute(p.parent, it->relative); +} +void EdittableElements::move(node_id i, const start_point &p) { + const auto idx = i.val.index(); + auto it = hierarchy.get(idx); + assert(it); + if (it->parent != p.parent) { + extract(idx); //FIXME: move recursive + insert(idx, p); + } else if (it->relative != p.relative || it->vel != p.vel) { + nodes.set_flag(Set(nodes.with_flag(i))); + it->relative = p.relative; + it->vel = p.vel; + } + nodes.at(i).absolute = computeAbsolute(p.parent, it->relative); +} + +void EdittableElements::extract(node_ref r) { + hierarchy.erase(r, [&](generational::hierarchy::entry *child) { + nodes.set_flag(Set(nodes.with_flag(child->self))); + }); +} +void EdittableElements::insert(node_ref r, const start_point& sp) { + hierarchy.set_emplace(r, sp.parent, sp.relative, sp.vel); +} + +world::node_id EdittableElements::create(const start_point& sp, uint8_t flag, const std::shared_ptr& el) { + const auto id = nodes.emplace(flag, computeAbsolute(sp.parent, sp.relative), el).first; + assert(Has(id)); + if (!Has(id)) + insert(id, sp); + return id; +} +world::node_id EdittableElements::createAt(node_id i, const start_point& sp, const std::shared_ptr& el) { + const auto id = Set(i); + nodes.set_emplace(id, computeAbsolute(sp.parent, sp.relative), el); + if (!Has(id)) + insert(id, sp); + return id; +} +world::node_id EdittableElements::createAt(generational::id::idx_t i, const start_point& sp, uint8_t flag, const std::shared_ptr& el) { + return createAt(generational::id(i, 0, flag), sp, el); +} \ No newline at end of file diff --git a/src/core/world/Elements.hpp b/src/core/world/Elements.hpp new file mode 100644 index 0000000..ca11a45 --- /dev/null +++ b/src/core/world/Elements.hpp @@ -0,0 +1,115 @@ +#pragma once + +#include "Area.hpp" +#include "../generational/map.hpp" +#include "../generational/hierarchy.hpp" + +namespace world { + + /// ECS data (aka all universe readable knowledge) + class Elements { + public: + /// All element in universe + generational::vector nodes; + /// Relations (orbits) of nodes + generational::hierarchy hierarchy; + //MAYBE: split this big struct in internal SoA + using hierarchy_entry = generational::hierarchy::entry; + generational::map areas; + generational::map parts; + generational::vector models; + + // MAYBE: move to Node + static constexpr size_t TYPE_SIZE = 2; + static constexpr uint8_t TYPE_MASK = ((1 << TYPE_SIZE)-1); + enum class Type: uint8_t { + None = 0, + Instance = 1, + Part = 2, + Area = 3, + }; + static constexpr Type GetType(const node_id &i) { return static_cast(i.val.flag() & TYPE_MASK); } + template + static constexpr bool Is(const node_id &i) { return GetType(i) == T; } + + enum class Flag: uint8_t { + Moved = 0, /// Nodes with modified state (transform, velocity or hierarchy) + Removed = 1, /// Freed nodes + Added = 2, /// New node or modified content + Fixed = 3, /// Without hierarchy + ChunkLoader = 4, /// Load chunks around + MAX = 8-TYPE_SIZE-1 + }; + template + static constexpr bool Has(const node_id &i) { return (i.val.flag() >> (TYPE_SIZE + static_cast(F))) & 1; } + template + static constexpr uint8_t Set(uint8_t flag) { + constexpr uint8_t IDX = TYPE_SIZE + static_cast(F); + return (flag & ~static_cast(1 << IDX)) | (V << IDX); + } + template + static constexpr node_id Set(const node_id &i) { + return generational::id(i.val.index(), i.val.generation(), Set(i.val.flag())); + } + + node_id withFlag(node_ref r) const { return nodes.with_flag(r); } + Node *findNode(node_id i) { return nodes.directly_at(i); } + const Node* findNode(node_id i) const { return nodes.directly_at(i); } + Node* findNode(node_ref r) { return nodes.directly_at(r); } + NodeOf* findArea(area_id i) { + assert(Is(i.val)); + return NodeOf::Make(findNode(i.val)); + } + const NodeOf* findArea(area_id i) const { + assert(Is(i.val)); + return NodeOf::Make(findNode(i.val)); + } + NodeOf* findPart(part_id i) { + assert(Is(i.val)); + return NodeOf::Make(findNode(i.val)); + } + const NodeOf* findPart(part_id i) const { + assert(Is(i.val)); + return NodeOf::Make(findNode(i.val)); + } + + world_pos computeAbsolute(const node_ref &pref, const voxel_pos& rel) const; + transform computeAbsolute(const node_id &pid, const transform& rel) const; + /// Maybe null + node_id findParent(const node_id &id) const; + }; + + /// Edittable Elements container + class EdittableElements: public Elements { + public: + EdittableElements(bool loadModels = true); + + struct start_point { + start_point(node_id p, const transform &r, const velocity &vel = FIXED): + parent(p), relative(r), vel(vel) { } + start_point(const relative_pos& p, const velocity &vel = FIXED): + start_point(p.parent, transform(p.position), vel) { } + + node_id parent; + transform relative; + velocity vel; + }; + + /// Children are link to parent + void remove(node_id); + /// Also delete childrens + void removeRecursive(node_id); + /// Set parent and positions + void move(node_id id, const relative_pos &p); + /// Set parent, position and velocity + void move(node_id id, const start_point &p); + + protected: + void extract(node_ref); + void insert(node_ref, const start_point& sp); + + node_id create(const start_point&, uint8_t flag, const std::shared_ptr&); + node_id createAt(node_id, const start_point&, const std::shared_ptr&); + node_id createAt(generational::id::idx_t, const start_point&, uint8_t flag, const std::shared_ptr&); + }; +} \ No newline at end of file diff --git a/src/core/world/Node.cpp b/src/core/world/Node.cpp new file mode 100644 index 0000000..7e1f3b8 --- /dev/null +++ b/src/core/world/Node.cpp @@ -0,0 +1,15 @@ +#include "./Node.hpp" + +#include "Elements.hpp" + +using namespace world; + +void Part::free(const node_id& i, Elements& l) { + l.parts.erase(i); +} +void Instance::free(const node_id& i, Elements& l) { + l.models.at(type).instances.erase(i.val.index()); +} +aabb Instance::getBoundingBox(const Elements& l) const { + return aabb::From(l.models.directly_at(type)->collision); +} \ No newline at end of file diff --git a/src/core/world/Node.hpp b/src/core/world/Node.hpp new file mode 100644 index 0000000..80e3b00 --- /dev/null +++ b/src/core/world/Node.hpp @@ -0,0 +1,181 @@ +#pragma once + +#include "forward.h" +#include "../generational/map.hpp" +#include "./Registry.hpp" +#include "../geometry/math.hpp" +#include + +namespace world { + class Chunk; + struct Elements; + + struct transform { + transform(const world_pos& p = world_pos(0), const pivot& r = identity_pivot): + position(p), rotation(r) { } + world_pos position; + pivot rotation; + + world_pos computeChild(const world_pos& relative) const { + return position + (lpivot)rotation * relative; + } + transform computeChild(const transform& relative) const { + return transform(computeChild(relative.position), relative.rotation * rotation); + } + + inline bool operator==(const transform &t) const { return position == t.position && rotation == t.rotation; } + inline bool operator!=(const transform &t) const { return !operator==(t); } + }; + struct velocity { + velocity(const offset_pos& p = offset_pos(0), const pivot& r = identity_pivot): + position(p), rotation(r) { } + offset_pos position; + pivot rotation; + + inline bool operator==(const velocity &v) const { return position == v.position && rotation == v.rotation; } + inline bool operator!=(const velocity &v) const { return !operator==(v); } + }; + const auto FIXED = velocity(); + + + /// Element position + struct NodeBody { + public: + transform absolute; + }; + + //MAYBE: separate NodeBody and dense maps of Elements + class Element; + struct Node: NodeBody { + Node(const transform& t, const std::shared_ptr& c): content(c) { + absolute = t; + } + std::shared_ptr content; + }; + using node_id = generational::key; + using node_ref = generational::ref; + + /// Abstract element content + class Element { + public: + virtual ~Element() { } + /// Remove itself from specific maps + virtual void free(const node_id& i, Elements&) = 0; + + /// Axis-align bounding box relative to center + virtual aabb getBoundingBox(const Elements& l) const = 0; + + //TODO: add physics queries + //TODO: getMass + }; + + template + struct NodeOf: Node { + static_assert(std::is_base_of::value); + static constexpr NodeOf *Make(Node *n) { return reinterpret_cast*>(n); } + static constexpr const NodeOf *Make(const Node *n) { return reinterpret_cast*>(n); } + inline constexpr std::shared_ptr get() const { return std::static_pointer_cast(content); } + }; + + /// Element hierarchical relative data + struct Hierarchy { + public: + Hierarchy(const transform& relative, const velocity& vel): + relative(relative), vel(vel) { } + + transform relative; + velocity vel; + }; + + struct relative_pos { + node_id parent; + world_pos position; + }; + struct relative_transform { + node_id parent; + transform relative; + }; + + /// Single region element + class Part final : public Element { + public: + Part(glm::usvec3 s): size(s) { } + //TODO: add scale + + void free(const node_id& i, Elements&) override; + + aabb getBoundingBox(const Elements&) const override { + return aabb(glm::lfvec3(0), size); + } + + /// Linear 3d grid of Chunk + /// Size: chunkSize() + using region_t = std::vector>; + region_t chunks; + + bool empty() const { return chunks.empty(); } + bool loaded() const { + return chunks.size() >= static_cast(chunkCount()) && std::all_of(chunks.begin(), chunks.end(), + [](const std::shared_ptr &ptr) {return ptr != nullptr; }); + } + void allocate() { + chunks.resize(chunkCount()); + } + static constexpr chunk_pos ChunkSize(const glm::usvec3& s) { return chunk_pos(1) + glm::divide(s); } + constexpr chunk_pos chunkSize() const { return ChunkSize(size); } + size_t chunkCount() const { + const auto size = chunkSize(); + return size.x * size.y * size.z; + } + constexpr bool inRange(const chunk_pos& c) const { + return glm::all(glm::greaterThanEqual(c, chunk_pos(0))) && + glm::all(glm::lessThan(c, chunkSize())); + } + constexpr size_t getIdx(const chunk_pos& c) const { + return glm::toIdx(c, chunkSize()); + } + constexpr region_chunk_pos getPos(size_t i) const { + return glm::fromIdx(i, chunkSize()); + } + std::shared_ptr findInRange(const chunk_pos& c) const { + assert(inRange(c)); + const auto idx = getIdx(c); + if (idx < chunks.size()) + return chunks.at(idx); + + return nullptr; + } + + glm::usvec3 size; + }; + using part_id = generational::key; + using part_ref = generational::ref; + + class Instance; + struct Model: world::module::model { + Model(const data& m, const geometry::faabb& col, glm::vec3 sc = glm::vec3(1), bool t = false): + model("", m, col, sc), temporary(t) { } + + /// Is removed with last instance + bool temporary; + + generational::map instances; + /// Shared value for models without unique parameters + std::shared_ptr origin; + }; + using model_id = generational::key; + /// Instanced model element + class Instance final: public Element { + public: + Instance(const model_id& t): type(t) { } + + /// Model id + model_id type; + + void free(const node_id& i, Elements&) override; + + aabb getBoundingBox(const Elements &l) const override; + }; + using instance_id = generational::key; + using instance_ref = generational::ref; +} diff --git a/src/core/world/Registry.cpp b/src/core/world/Registry.cpp new file mode 100644 index 0000000..fac1c1f --- /dev/null +++ b/src/core/world/Registry.cpp @@ -0,0 +1,191 @@ +#include "./Registry.hpp" +#include +#include +#include +#include +#include "modules/core/Core.hpp" +#include "../utils/logger.hpp" + +using namespace world::module; + +owner Registry::sInstance = nullptr; +void Registry::Load() { + assert(sInstance == nullptr); + sInstance = new Registry(); +} +Registry::Registry() { + if (sInstance == nullptr) + sInstance = this; + + modules.emplace_back(nullptr, new module::core::Core()); + + std::filesystem::create_directories("./modules"); + for(const auto& entry: std::filesystem::directory_iterator("./modules")) { + if(entry.path().extension() == ".mod") { + const auto handle = dlopen(entry.path().c_str(), RTLD_NOW); + if (!handle) { + LOG_E("Failed to open library" << entry.path() << " : " << dlerror()); + continue; + } + + const auto loader = (Module::load_fn*) dlsym(handle, "loadModule"); + if (!loader) { + LOG_E("Failed to load library" << entry.path() << " : " << dlerror()); + continue; + } + [[maybe_unused]] + const auto instance = modules.emplace_back(handle, loader()).instance; + LOG_T("Module " << instance->getName() << " registred"); + } + } + + auto loaded = std::vector(modules.size(), false); + bool progress = false; + do { + progress = false; + for (size_t id = 0; id < modules.size(); id++) { + if (loaded[id]) + continue; + + const auto inst = modules.at(id).instance; + if([&]{ + auto reqs = inst->getRequirements(); + while (*reqs) { + const auto req = std::string(reqs); + auto mod_id = findModuleId(req); + if (!mod_id) { + LOG_E("Module " << inst->getName() << " requires missing module " << req); + return false; + } + if (!loaded[mod_id.value().val]) { + return false; + } + reqs += req.length() + 1; + } + return true; + }()) { + inst->setup(Loader{id, this}); + LOG_D("Module " << inst->getName() << " loaded"); + loaded[id] = true; + progress = true; + } + } + } while (progress); + { + std::string cycle = ""; + for (size_t id = 0; id < modules.size(); id++) { + if (!loaded[id]) { + cycle += modules[id].instance->getName(); + cycle += ','; + } + } + if (!cycle.empty()) { + cycle.pop_back(); + LOG_E("Modules circular dependencies between " << cycle); + } + } +} + +void Registry::Unload() { + assert(sInstance != nullptr); + delete sInstance; + sInstance = nullptr; +} +Registry::ModHandle::~ModHandle() { + if(instance) + delete instance; + if(handle) { + dlclose(handle); + handle = nullptr; + } +} + +std::optional Registry::findModuleId(const std::string& name) const { + for (size_t id = 0; id < modules.size(); id++) { + if (modules[id].instance->getName() == name) + return id; + } + return std::nullopt; +} +std::optional Registry::findMaterialId(module_id mod_id, const std::string& mat) const { + for (size_t id = 0; id < _materials.size(); id++) { + if (_materials.module_ids[id] == mod_id.val && _materials.names[id] == mat) + return id; + } + return std::nullopt; +} +std::optional Registry::findMaterialId(const std::string& mod, const std::string& mat) const { + if (const auto mod_id = findModuleId(mod)) + return findMaterialId(mod_id.value(), mat); + return std::nullopt; +} +std::optional Registry::findTextureId(const std::string& path) const { + for (size_t id = 0; id < _textures.size(); id++) { + if (_textures[id] == path) + return id; + } + return std::nullopt; +} +std::optional Registry::findTextureId(material_id mat_id) const { + if (mat_id.val < _materials.size()) { + return _materials.texture_ids[mat_id.val]; + } + return std::nullopt; +} +std::optional Registry::findModelId(module_id mod_id, const std::string& model) const { + for (size_t id = 0; id < _models.size(); id++) { + if (_models[id].module_id == mod_id.val && _models[id].name == model) + return id; + } + return std::nullopt; +} +std::optional Registry::findModelId(const std::string& module, const std::string& model) const { + if (const auto mod_id = findModuleId(module)) + return findModelId(mod_id.value(), model); + return std::nullopt; +} +std::optional Registry::findGeneratorId(module_id mod_id, const std::string& gen) const { + for (size_t id = 0; id < _generators.size(); id++) { + if (_generators[id].module_id == mod_id.val && _generators[id].name == gen) + return id; + } + return std::nullopt; +} +std::optional Registry::findGeneratorId(const std::string& mod, const std::string& gen) const { + if (const auto mod_id = findModuleId(mod)) + return findGeneratorId(mod_id.value(), gen); + return std::nullopt; +} +owner Registry::createGenerator(generator_id genId, generator::params ps) const { + if (genId.val < _generators.size()) { + return _generators[genId.val].factory(ps); + } + return nullptr; +} + +void Registry::reserveMaterials(size_t n) { + assert(n < (std::numeric_limits::max() >> 4) && "for byte packing see Voxel"); + _materials.reserve(n); +} +material_id Registry::addMaterial(module_id mod_id, const material& m) { + assert(!findMaterialId(mod_id, m.name)); + auto tex_id = findTextureId(m.texture); + if (!tex_id) { + tex_id = _textures.size(); + _textures.push_back(m.texture); + } + return _materials.push(m, mod_id.val, tex_id.value().val); +} + +model_id Registry::addModel(module_id mod_id, const model& m) { + assert(!findModelId(mod_id, m.name)); + auto id = _models.size(); + _models.emplace_back(m, mod_id.val); + return id; +} +generator_id Registry::addGenerator(module_id mod_id, const generator& g) { + assert(!findGeneratorId(mod_id, g.name)); + auto id = _generators.size(); + _generators.emplace_back(g, mod_id.val); + return id; +} \ No newline at end of file diff --git a/src/core/world/Registry.hpp b/src/core/world/Registry.hpp new file mode 100644 index 0000000..0f876eb --- /dev/null +++ b/src/core/world/Registry.hpp @@ -0,0 +1,98 @@ +#pragma once + +#include +#include +#include "./module_types.hpp" + +namespace world::module { + +class Module; +class Registry { +public: + static _FORCE_INLINE_ Registry *Get() { + assert(sInstance != nullptr && "Uninitialized registry"); + return sInstance; + } + + /// Load mods and populate registry + static void Load(); + static void Unload(); + + std::optional findModuleId(const std::string& mod) const; + + void reserveMaterials(size_t n); + std::optional findMaterialId(module_id mod_id, const std::string& mat) const; + std::optional findMaterialId(const std::string& mod, const std::string& mat) const; + std::optional findTextureId(const std::string& path) const; + std::optional findTextureId(material_id mat_id) const; + material_id addMaterial(module_id mod_id, const material&); + std::optional findModelId(module_id module_id, const std::string& model) const; + std::optional findModelId(const std::string& module, const std::string& model) const; + model_id addModel(module_id mod_id, const model&); + generator_id addGenerator(module_id mod_id, const generator&); + std::optional findGeneratorId(module_id mod_id, const std::string& gen) const; + std::optional findGeneratorId(const std::string& mod, const std::string& gen) const; + owner createGenerator(generator_id id, generator::params) const; + + struct Loader { + const module_id id; + Registry *const reg; + + void reserveMaterials(size_t n) { reg->reserveMaterials(n + reg->getMaterials().size()); } + material_id addMaterial(const material &m) { return reg->addMaterial(id, m); } + model_id addModel(const model &m) { return reg->addModel(id, m); } + generator_id addGenerator(const generator &g) { return reg->addGenerator(id, g); } + }; + + using textures = std::vector; + _FORCE_INLINE_ const textures& getTextures() { + return _textures; + } + _FORCE_INLINE_ const materials& getMaterials() { + return _materials; + } + + using models = std::vector; + template + void extractModels(const F& f) { + for (auto& model: _models) + f(model); + } + + using generators = std::vector; + +private: + Registry(); + ~Registry() {} + + static owner sInstance; + + struct ModHandle { + ModHandle(void* h, Module* i): handle(h), instance(i) { } + ~ModHandle(); + owner handle; + owner instance; + }; + std::vector modules; + + materials _materials; + textures _textures; + models _models; + generators _generators; +}; + +class Module { +public: + // default constructor + virtual ~Module(){}; + + /// Module unique name + virtual const char* getName() const = 0; + /// List or required modules (zero separated) + virtual const char *getRequirements() const = 0; + virtual void setup(Registry::Loader loader) = 0; + + /// Signature of loadModule static function + typedef Module* load_fn(); +}; +} diff --git a/src/core/world/Universe.cpp b/src/core/world/Universe.cpp index d5f56ba..ecac4da 100644 --- a/src/core/world/Universe.cpp +++ b/src/core/world/Universe.cpp @@ -1,20 +1,209 @@ #include "Universe.hpp" -#include "../data/math.hpp" + +#include "Elements.hpp" using namespace world; -using namespace geometry; -bool Universe::move(glm::ifvec3 &pos, const glm::vec3 &vel, int density, float radius) const { +inline bool intersectRay(const geometry::Ray& ray, const Node& node, const Elements& l) { + assert(node.content); + // MAYBE: check obb + return ray.intersect(aabb::From(node.content->getBoundingBox(l), node.absolute.rotation) + node.absolute.position) + != lfaabb::ContainmentType::Disjoint; +} +inline bool containPoint(const world_pos& point, const Node& node, const Elements& l) { + assert(node.content); + // MAYBE: check obb + return (aabb::From(node.content->getBoundingBox(l), node.absolute.rotation) + node.absolute.position).contains(point); +} + +template +inline void raycastVoxel(const geometry::Ray& ray, bool solid_only, const node_id& id, const transform& absolute, const CC& chunks, Rs& target, float& targetDist) { + if (chunks.empty()) + return; + + const auto inv_rot = glm::conjugate(absolute.rotation); + const auto localRay = geometry::Ray((lpivot)inv_rot * (ray.from - absolute.position), inv_rot * ray.dir, targetDist); + std::shared_ptr chunk = nullptr; + chunk_pos chunk_vec(INT_MAX); + geometry::Ray::iterator it(localRay); + glm::llvec3 pos; + const auto setTargetDist = [&] { + const auto pDist = glm::length((glm::lfvec3)pos - localRay.from); + if (pDist < targetDist) { + targetDist = pDist; + return true; + } + return false; + }; + // MAYBE: fastforward with chunk iterator + while (it.next(pos)) { + const chunk_pos cPos = glm::divide(pos); + if (cPos != chunk_vec) { + if (chunks.inRange(cPos)) { + if (chunk = chunks.findInRange(cPos); chunk) { + chunk_vec = cPos; + } else { + if (setTargetDist()) + target = Universe::area_query_out_of_range{id.val, cPos}; + + break; + } + } else { + chunk = nullptr; + } + } + if (chunk != nullptr) { + const auto voxel = chunk->getAt(glm::modulo(pos)); + if (voxel.is_material() && (!solid_only || voxel.is_solid())) { + if (setTargetDist()) + target = Tr(Vp{id.val, pos}, voxel); + + break; + } + } + } +} + +template +inline std::optional voxelAt(const world_pos& point, bool solid_only, const node_id& id, const transform& absolute, const CC& chunks) { + if (chunks.empty()) + return std::nullopt; + + const auto localPoint = (lpivot)glm::conjugate(absolute.rotation) * (point - absolute.position); + const chunk_pos cPos = glm::divide(localPoint); + if (const auto chunk = chunks.findInRange(cPos)) { + const auto voxel = chunk->getAt(glm::modulo(localPoint)); + if (voxel.is_material() && (!solid_only || voxel.is_solid())) { + return Universe::voxel_query_target(node_voxel_pos{id, localPoint}, voxel); + } + } + return std::nullopt; +} + +Universe::query_result Universe::Raycast(const Elements& l, const geometry::Ray &in, bool solid_only, const node_id& ignore) { + geometry::Ray ray = in; + query_result target; + // TODO: use bounding hierarchy + l.nodes.iter([&](const node_id &id, const Node &node) { + const auto type = Elements::GetType(id); + if (id == ignore || type == Elements::Type::None || node.content == nullptr || !intersectRay(ray, node, l)) + return; + + switch (type) { + case Elements::Type::Area: + raycastVoxel(ray, solid_only, id, node.absolute, NodeOf::Make(&node)->get()->getChunks(), target, ray.dist); + break; + + case Elements::Type::Part: + raycastVoxel(ray, solid_only, id, node.absolute, *NodeOf::Make(&node)->get(), target, ray.dist); + break; + + default: { + // TODO: get intersect point OBB ray -> point + target = element_query_target{id, world_pos(0)}; + const auto pDist = glm::length(node.absolute.position - ray.from); + //FIXME: include bounding assert(pDist <= ray.dist); + if (pDist < ray.dist) + ray.dist = pDist; + break; + } + } + }); + return target; +} + +Universe::element_query_result Universe::Raycast(const Elements& l, const geometry::Ray &in, const node_id& ignore) { + const auto result = Raycast(l, in, false, ignore); + if (const auto voxel = std::get_if(&result)) { + return element_query_target{voxel->pos.node, voxel->pos.voxel}; + } else if (const auto element = std::get_if(&result)) { + return *element; + } else { + return std::nullopt; + } +} + +Universe::area_query_result Universe::RaycastArea(const Elements& l, const geometry::Ray& in, bool solid_only) { + geometry::Ray ray = in; + area_query_result target; + for (const auto& ref: l.areas) { + const auto id = l.withFlag(ref); + const auto node = l.findArea(id.val); + assert(node); + if (!intersectRay(ray, *node, l)) + continue; + + raycastVoxel(ray, solid_only, id, node->absolute, node->get()->getChunks(), target, ray.dist); + } + return target; +} + +Universe::query_result Universe::At(const Elements& l, const world_pos &point, bool solid_only, const node_id& ignore) { + query_result target; + // TODO: use bounding hierarchy + l.nodes.any([&](const node_id &id, const Node &node) { + const auto type = Elements::GetType(id); + if (id == ignore || type == Elements::Type::None || node.content == nullptr || !containPoint(point, node, l)) + return false; + + switch (type) { + case Elements::Type::Area: { + if (const auto result = voxelAt(point, solid_only, id, node.absolute, NodeOf::Make(&node)->get()->getChunks())) { + target = result.value(); + return true; + } + return false; + } + case Elements::Type::Part: { + if (const auto result = voxelAt(point, solid_only, id, node.absolute, *NodeOf::Make(&node)->get())) { + target = result.value(); + return true; + } + return false; + } + default: { + target = element_query_target{id, (lpivot)glm::conjugate(node.absolute.rotation) * (point - node.absolute.position)}; + return true; + } + } + }); + return target; +} + +Universe::element_query_result Universe::At(const Elements& l, const world_pos &point, const node_id& ignore) { + const auto result = At(l, point, false, ignore); + if (const auto voxel = std::get_if(&result)) { + return element_query_target{voxel->pos.node, voxel->pos.voxel}; + } else if (const auto element = std::get_if(&result)) { + return *element; + } else { + return std::nullopt; + } +} + +bool Universe::IsRangeFree(const Elements& l, const node_voxel_ref ¢er_ref, const geometry::Volume& volume) { + //TODO: handle transform (needs OBB-OBB intersect) + const auto center = l.computeAbsolute(center_ref.node, center_ref.voxel); + + //FIXME: use hierarchy bounding box + return !l.nodes.any([&](const node_id& id, const Node& node) { + return id.val.index() != center_ref.node.val && volume.intersects(center, + obb(node.content->getBoundingBox(l), node.absolute.rotation) + node.absolute.position); + }); +} + +/* +bool Universe::move(element_pos &pos, const glm::vec3 &vel, int density, float radius) const { const auto dir = glm::normalize(vel); const auto velocity = vel * glm::vec3(density); const auto from = pos * density + dir; const auto result = raycast(Ray(from, dir, glm::length(velocity) + radius)); if (auto target = std::get_if(&result)) { - const auto target_dist = from.dist(glm::ifvec3(target->offset + target->pos.second, density)) - radius; + const auto target_dist = from.dist(element_pos(target->offset + target->pos.second, density)) - radius; pos += vel * glm::vec3(target_dist / glm::length(vel)); return true; } else if(auto out = std::get_if(&result)) { - const auto out_dist = from.dist(glm::ifvec3(out->offset + glm::multiply(out->pos.second), density)) - radius - CHUNK_LENGTH; + const auto out_dist = from.dist(element_pos(out->offset + glm::multiply(out->pos.second), density)) - radius - CHUNK_LENGTH; pos += vel * glm::vec3(out_dist / glm::length(vel)); return true; } @@ -22,9 +211,10 @@ bool Universe::move(glm::ifvec3 &pos, const glm::vec3 &vel, int density, float r pos += vel; return false; } -bool Universe::collide(const glm::ifvec3 &pos, const glm::vec3 &vel, int density, float radius) const { +bool Universe::collide(const element_pos &pos, const glm::vec3 &vel, int density, float radius) const { const auto dir = glm::normalize(vel); const auto velocity = vel * glm::vec3(density); const auto from = pos * density + dir; return !std::holds_alternative(raycast(Ray(from, dir, glm::length(velocity) + radius))); -} \ No newline at end of file +} +*/ \ No newline at end of file diff --git a/src/core/world/Universe.hpp b/src/core/world/Universe.hpp index 41ffe12..44842ca 100644 --- a/src/core/world/Universe.hpp +++ b/src/core/world/Universe.hpp @@ -1,81 +1,61 @@ #pragma once #include -#include "forward.h" -#include "Voxel.hpp" -#include "position.h" +#include "./Area.hpp" +#include "./Voxel.hpp" #include "../geometry/Ray.hpp" #include "../geometry/Shapes.hpp" namespace world { - /// Whole abstract universe container - class Universe { - public: - virtual ~Universe() {} +class Elements; - /// Distance management - struct options { - /// Radius in chunks to load if missing - uint16_t loadDistance = 5; - /// Radius in chunks to keep in memory - uint16_t keepDistance = 6; - }; +/// Whole abstract universe container +class Universe { +public: + virtual ~Universe() {} - /// Universe voxel ray intersection - struct ray_target { - ray_target(area_ pos, Voxel value, voxel_pos offset): - pos(pos), value(value), offset(offset) { } - area_ pos; - Voxel value; - voxel_pos offset; - }; - /// Universe unloaded chunk ray intersection - struct ray_out_of_range { - ray_out_of_range(area_ pos, voxel_pos offset): - pos(pos), offset(offset) { } - area_ pos; - voxel_pos offset; - }; - /// Universe ray intersection - /// @return target voxel, unloaded chunk or none - using ray_result = std::variant; - - /// Get nearest voxel colliding ray - /// @note ray in world scale - virtual ray_result raycast(const geometry::Ray &ray) const = 0; - - /// Check for entity in shape - /// MAYBE: multiarea - virtual bool isAreaFree(const area_ &pos, geometry::Shape shape, uint16_t radius) const = 0; - - /// Check for collision on movement - bool collide(const glm::ifvec3 &pos, const glm::vec3 &vel, int density, float radius = 0) const; - /// Move with collision check - /// @note must remove velocity after colision - bool move(glm::ifvec3 &pos, const glm::vec3 &vel, int density, float radius = 0) const; - - /// Entities commun properties - struct Entity { - /// Linear cube of Chunk - /// Size: 1+divide(size) - using area_t = std::vector>; - /// render::Model::Data serialized - using model_t = std::vector; - - Entity(const glm::usvec3& size = glm::usvec3(1), const glm::vec3& scale = glm::vec3(1), bool permanant = true): - size(size), scale(scale), permanant(permanant) { } - Entity(const model_t& model, const glm::usvec3& size = glm::usvec3(1), const glm::vec3& scale = glm::vec3(1), bool permanant = true): - size(size), scale(scale), permanant(permanant) { shape = model; } - - std::variant shape; - glm::usvec3 size; - glm::vec3 scale; - bool permanant; - struct Instance { - glm::ifvec3 pos; - glm::vec3 velocity; - }; - data::generational::vector instances; - }; + /// Distance management + struct options { + /// Radius in chunks to load if missing + uint16_t loadDistance = 5; + /// Radius in chunks to keep in memory + uint16_t keepDistance = 6; }; + + /// Universe element ray intersection + using element_query_target = relative_pos; + using element_query_result = std::optional; + /// Universe voxel ray intersection + struct area_query_target { + area_query_target(const area_voxel_pos& pos, Voxel value): + pos(pos), value(value) { } + area_voxel_pos pos; + Voxel value; + }; + /// Universe unloaded chunk ray intersection + using area_query_out_of_range = area_chunk_pos; + using area_query_result = std::variant; + + struct voxel_query_target { + voxel_query_target(const node_voxel_pos& pos, Voxel value): + pos(pos), value(value) { } + node_voxel_pos pos; + Voxel value; + }; + using query_result = std::variant; + + /// Get nearest element colliding ray + static element_query_result Raycast(const Elements&, const geometry::Ray &ray, const node_id& ignore = generational::id()); + /// Get nearest area voxel colliding ray + static area_query_result RaycastArea(const Elements&, const geometry::Ray &ray, bool solid_only = true); + /// Get nearest thing colliding ray + static query_result Raycast(const Elements&, const geometry::Ray &ray, bool solid_only, const node_id& ignore = generational::id()); + /// Get first element colliding point + static element_query_result At(const Elements&, const world_pos &point, const node_id& ignore = generational::id()); + /// Get first thing colliding point + static query_result At(const Elements&, const world_pos &point, bool solid_only, const node_id& ignore = generational::id()); + /// Check for entities in range + static bool IsRangeFree(const Elements&, const node_voxel_ref ¢er, const geometry::Volume&); +}; + } \ No newline at end of file diff --git a/src/core/world/Voxel.hpp b/src/core/world/Voxel.hpp index 8ac8e3f..3d94498 100644 --- a/src/core/world/Voxel.hpp +++ b/src/core/world/Voxel.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include "../../core/world/materials.hpp" +#include "./Registry.hpp" #include namespace world { @@ -33,8 +33,11 @@ namespace world { return (value & MATERIAL_MASK) >> 3; } /// Texture idx - constexpr inline uint16_t texture() const { - return materials::textures_map[material()]; + static _FORCE_INLINE_ uint16_t Texture(material_t mat) { + return module::Registry::Get()->getMaterials().texture_ids[mat]; + } + _FORCE_INLINE_ uint16_t texture() const { + return Texture(material()); } /// Quantity of element @@ -43,7 +46,7 @@ namespace world { } /// Quantity of element on [0, 1] constexpr inline float density_ratio() const { - return density() * (1.f / world::Voxel::DENSITY_MAX); + return density() * (1.f / DENSITY_MAX); } /// Swap value @@ -52,21 +55,36 @@ namespace world { return (value & SWAP_MASK) != 0; } + static _FORCE_INLINE_ const std::string& Name(material_t mat) { + return module::Registry::Get()->getMaterials().names[mat]; + } + _FORCE_INLINE_ const std::string& name() const { + return Name(material()); + } + static _FORCE_INLINE_ bool IsSolid(material_t mat) { + return module::Registry::Get()->getMaterials().solidities[mat]; + } /// Is solid - constexpr inline bool is_solid() const { - return density() > 0 && materials::solidity[material()]; + _FORCE_INLINE_ bool is_solid() const { + return density() > 0 && IsSolid(material()); + } + static _FORCE_INLINE_ bool IsVisible(material_t mat) { + return !module::Registry::Get()->getMaterials().invisibilities[mat]; } /// Is visible matter - constexpr inline bool is_visible() const { - return !materials::invisibility[material()]; + _FORCE_INLINE_ bool is_visible() const { + return IsVisible(material()); } /// Contains matter - constexpr inline bool is_material() const { + _FORCE_INLINE_ bool is_material() const { return density() > 0 && is_visible(); } + static _FORCE_INLINE_ bool IsTransparent(material_t mat) { + return module::Registry::Get()->getMaterials().transparencies[mat]; + } /// Is full - constexpr inline bool is_full() const { - return density() == DENSITY_MAX && !materials::transparency[material()]; + _FORCE_INLINE_ bool is_full() const { + return density() == DENSITY_MAX && !IsTransparent(material()); } /// Value after fill operation @@ -76,7 +94,7 @@ namespace world { const world::Voxel::density_t dst = in.density() * ratio; if (!in.is_material()) - return world::Voxel(material(), world::Voxel::DENSITY_MAX-dst, swap()); + return world::Voxel(material(), DENSITY_MAX-dst, swap()); if (is_material() && density() > dst) return Voxel(value); diff --git a/src/core/world/actions.hpp b/src/core/world/actions.hpp index b37eb85..31f5dd9 100644 --- a/src/core/world/actions.hpp +++ b/src/core/world/actions.hpp @@ -1,9 +1,8 @@ #pragma once -#include "forward.h" -#include "Voxel.hpp" #include -#include "../flags.hpp" +#include "./Area.hpp" +#include "./Voxel.hpp" #include "../geometry/Shapes.hpp" /// Client action on world @@ -14,9 +13,9 @@ struct Ping { }; } struct Fill: part::Ping { - Fill(const area_ &pos, const Voxel &val): pos(pos), val(val) {} + Fill(const node_voxel_ref &pos, const Voxel &val): pos(pos), val(val) {} - const area_ pos; + const node_voxel_ref pos; const Voxel val; }; @@ -47,17 +46,22 @@ static _FORCE_INLINE_ geometry::Shape ToGeometry(Shape shape) { } } -struct FillShape: Fill { - FillShape(const area_ &pos, const Voxel &val, Shape shape, uint8_t radius): - Fill(pos, val), shape(shape), radius(radius) {} +struct Volume { + Volume(Shape shape, uint8_t radius): + shape(shape), radius(radius) { } const Shape shape; const uint8_t radius; }; -struct Move: part::Ping { - Move(const voxel_pos& pos): pos(pos) { } - const voxel_pos pos; +struct FillShape: Fill, Volume { + FillShape(const node_voxel_ref &pos, const Voxel &val, Shape shape, uint8_t radius): + Fill(pos, val), Volume(shape, radius) { } +}; +struct Move: part::Ping { + Move(const relative_pos& pos): pos(pos) { } + + const relative_pos pos; }; struct Message: part::Ping { Message(const std::string& text): text(text) { } diff --git a/src/core/world/forward.h b/src/core/world/forward.h index 14e8ab9..8e6c08d 100644 --- a/src/core/world/forward.h +++ b/src/core/world/forward.h @@ -1,14 +1,13 @@ #pragma once -#include -#include "../../core/world/position.h" +#include "./units.hpp" namespace world { class Chunk; class ChunkContainer; - class Area; -} -namespace world::client { class EdittableChunk; - using area_map = robin_hood::unordered_map>; + class Area; + struct area_region_pos; + struct area_chunk_pos; + struct area_voxel_pos; } \ No newline at end of file diff --git a/src/core/world/generator/Abstract.hpp b/src/core/world/generator/Abstract.hpp new file mode 100644 index 0000000..adfc2ec --- /dev/null +++ b/src/core/world/generator/Abstract.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include "./Noise.hpp" +#include "../Voxel.hpp" + +namespace world::generator { + + /// Abstract Noise generator + class Abstract { + public: + /// Generate chunk voxels + virtual void generate(const chunk_pos &at, std::array &out) = 0; + /// Get gravity vector at given point + virtual glm::vec3 getGravity(const voxel_pos& point) const = 0; + /// Area visual curvature + virtual std::optional getCurvature() const { return std::nullopt; } + }; + + // Generate empty space + class Void: public Abstract { + public: + struct Params { }; + Void() = default; + + void generate(const chunk_pos &, std::array &out) override { + out.fill(Voxel()); + } + glm::vec3 getGravity(const voxel_pos&) const override { return glm::vec3(0); } + }; + +} \ No newline at end of file diff --git a/src/core/world/generator/Cave.hpp b/src/core/world/generator/Cave.hpp new file mode 100644 index 0000000..bf4af39 --- /dev/null +++ b/src/core/world/generator/Cave.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include "./Abstract.hpp" +#include "../Registry.hpp" + +namespace world::generator { + /// Endless cave network + class Cave: public Abstract { + public: + struct Params { + Params(int seed = 42, float density = 0, float gran = 30): seed(seed), density(density), granularity(gran) { } + + /// Random generator start + int seed; + /// Offset density overrage + float density; + /// Speed of density change + float granularity; + }; + Cave(const Params p): params(p), density(Noise::SimplexFractal(p.seed)), material(Noise::Cellular(p.seed * 5, .1)) { } + + void generate(const chunk_pos &pos, std::array &out) override { + const auto densitySet = density.getBlock(pos, CHUNK_LENGTH); + const auto materialSet = material.getBlock(pos, CHUNK_LENGTH); + constexpr auto MATERIAL_SKIP = 2; + const int materialMax = module::Registry::Get()->getMaterials().names.size() - (MATERIAL_SKIP + 1); + for (size_t i = 0; i < CHUNK_SIZE; i++) { + const auto density = std::clamp((densitySet.get()[i] + params.density) * params.granularity, 0.f, 1.f) * Voxel::DENSITY_MAX; + const auto material = density > 0 ? MATERIAL_SKIP + std::clamp(static_cast(std::lrint((materialSet.get()[i] + 1) / 2 * materialMax)), + 0, materialMax) : 1; //NOTE: map (approx -1, 1) to (1, mat_max) + out[i] = Voxel(material, density); + } + } + glm::vec3 getGravity(const voxel_pos&) const override { return glm::vec3(-1, 0, 0); } + private: + Params params; + Noise density; + Noise material; + }; +} \ No newline at end of file diff --git a/src/core/world/generator/Noise.hpp b/src/core/world/generator/Noise.hpp new file mode 100644 index 0000000..c9f4f38 --- /dev/null +++ b/src/core/world/generator/Noise.hpp @@ -0,0 +1,87 @@ +#pragma once + +#include +#include "../units.hpp" + +namespace world::generator { + /// Noise handler + class Noise { + public: + using handle = FastNoise::SmartNode<>; + using set = std::unique_ptr; + + struct Core { + Core(int seed, float frequency): seed(seed), frequency(frequency) { } + int seed; + float frequency; + }; + struct Simplex: Core { + Simplex(int seed, float frequency = .01f): Core(seed, frequency) { } + }; + struct Fractal { + enum class Type { + FBm, Billow, Ridged, RidgedMulti + }; + + Fractal(int octaves, float lacunarity, float gain, Type type): + octaves(octaves), lacunarity(lacunarity), gain(gain), type(type) { } + int octaves; + float lacunarity; + float gain; + Type type; + }; + struct SimplexFractal: Simplex, Fractal { + SimplexFractal(int seed, float frequency = .01f, int octaves = 3, float lacunarity = 2.0f, float gain = .5f, Type type = Type::FBm): + Simplex(seed, frequency), Fractal(octaves, lacunarity, gain, type) { } + }; + struct Cellular: Core { + Cellular(int seed, float frequency = .01f, float jitter = 1.0f): Core(seed, frequency), jitter(jitter) { } + float jitter; + //MAYBE: add options + }; + + Noise(handle h, Core s): ptr(h), state(s) { } + Noise(Simplex s): Noise(FastNoise::New(), s) { } + Noise(handle src, Core s, Fractal f): state(s) { + const auto frac = [](Fractal::Type type) -> FastNoise::SmartNode> { + switch (type) { + case Fractal::Type::FBm: + return FastNoise::New(); + case Fractal::Type::Billow: + return FastNoise::New(); + case Fractal::Type::Ridged: + return FastNoise::New(); + case Fractal::Type::RidgedMulti: + return FastNoise::New(); + default: + return nullptr; + } + }(f.type); + frac->SetSource(src); + frac->SetGain(f.gain); + frac->SetLacunarity(f.lacunarity); + frac->SetOctaveCount(f.octaves); + ptr = frac; + } + Noise(SimplexFractal sf): Noise(FastNoise::New(), sf, sf) { } + Noise(Cellular c): state(c) { + auto cel = FastNoise::New(); + cel->SetJitterModifier(c.jitter); + ptr = cel; + } + + inline set getRaw(const glm::i32vec3& start, const glm::i32vec3& size) const { + auto out = std::make_unique(size.x * size.y * size.z); + ptr->GenUniformGrid3D(out.get(), start.x, start.y, start.z, size.x, size.y, size.z, state.frequency, state.seed); + return out; + } + /// Get 3D block of given size with index pos. + inline set getBlock(const glm::i32vec3& idx, int size) { + return getRaw(idx * glm::i32vec3(size), glm::i32vec3(size)); + } + + private: + handle ptr; + Core state; + }; +} diff --git a/src/core/world/iterators.hpp b/src/core/world/iterators.hpp index 61975cb..8bf37fc 100644 --- a/src/core/world/iterators.hpp +++ b/src/core/world/iterators.hpp @@ -1,6 +1,5 @@ #pragma once -#include "position.h" #include "actions.hpp" #include @@ -25,86 +24,82 @@ std::unique_ptr Get(action::Shape, uint16_t radius); /// Border sampling from -radius to radius std::unique_ptr GetBorder(action::Shape, uint16_t radius); -template -inline bool GetChunk(Area& chunks, const std::pair& split, Voxel& out, - typename std::shared_ptr& ck = nullptr, chunk_pos& ck_pos = chunk_pos(INT32_MAX) -) { - if (split.first != ck_pos && chunks.inRange(split.first)) { - if(auto it = chunks.find(split.first); it != chunks.end()) { - ck = std::dynamic_pointer_cast(it->second); - ck_pos = split.first; +template +struct last_chunk { + typename std::shared_ptr ptr = nullptr; + chunk_pos pos; +}; + +template +inline bool GetVoxel(CC &chunks, const std::pair &split, Voxel &out, last_chunk& ck) { + if (split.first != ck.pos) { + if (chunks.inRange(split.first)) { + if(const auto ptr = chunks.findInRange(split.first)) { + ck.ptr = std::static_pointer_cast(ptr); + ck.pos = split.first; + } //TODO: handle not loaded chunks + } else { + ck.ptr = nullptr; } } - if (split.first == ck_pos) { - out = ck->get(split.second); + if (ck.ptr != nullptr) { + out = ck.ptr->get(split.second); return true; } return false; } -/// Apply shape on areas -template -inline void Apply(area_map &areas, action::FillShape fill, const CB& callback) { - const auto it = areas.find(fill.pos.first); - if (it == areas.end()) - return; - - auto &chunks = it->second->setChunks(); - auto iterator = Get(fill.shape, fill.radius); - pair point; - typename std::shared_ptr ck = nullptr; - chunk_pos ck_pos = chunk_pos(INT32_MAX); - while (iterator->next(point)) { +constexpr auto FILL_DELAY = .05f; +/// Apply shape on single chunk container +/// TODO: multi-part +template +inline void Apply(CC &chunks, action::FillShape fill, const CB& callback) { + const auto iterator = world::iterator::Get(fill.shape, fill.radius); //TODO: rotation + last_chunk ck; + Voxel prev; + for (pair point; iterator->next(point);) { const voxel_pos offset = point.first; - const auto split = glm::splitIdx(fill.pos.second + offset); - if (Voxel prev; GetChunk(chunks, split, prev, ck, ck_pos)) { + const auto split = glm::splitIdx(fill.pos.voxel + offset); + if (GetVoxel(chunks, split, prev, ck)) { const auto next = prev.filled(fill.val, point.second); if (prev.value != next.value) { - callback(ck, ck_pos, split.second, prev, next, glm::length2(offset) / fill.radius * .05f); + callback(ck.ptr, ck.pos, split.second, prev, next, glm::length2(offset) / fill.radius * FILL_DELAY); } } } } /// Find parts using flood fill -template -inline bool Split(area_map &areas, action::FillShape fill, size_t floodFillLimit, const CB& callback) { +template +inline void Split(CC &chunks, action::FillShape fill, size_t floodFillLimit, const CB& callback) { if (floodFillLimit == 0 || fill.val.is_solid()) - return false; - - const auto it = areas.find(fill.pos.first); - if (it == areas.end()) - return false; + return; + last_chunk ck; + Voxel cur; // Check for subpart break robin_hood::unordered_set joints; - auto &chunks = it->second->setChunks(); - auto iterator = GetBorder(fill.shape, fill.radius+1); - glm::ivec3 point; - typename std::shared_ptr ck = nullptr; - chunk_pos ck_pos = chunk_pos(INT32_MAX); - const auto cleanVoxel = Voxel(world::materials::AIR, fill.val.density()); - while (iterator->next(point)) { - const auto full = fill.pos.second + voxel_pos(point); - if (Voxel v; GetChunk(chunks, glm::splitIdx(full), v, ck, ck_pos) && v.is_solid()) - joints.insert(full); + { + const auto iterator = GetBorder(fill.shape, fill.radius+1); + for (glm::ivec3 point; iterator->next(point);) { + const auto full = fill.pos.voxel + voxel_pos(point); + if (GetVoxel(chunks, glm::splitIdx(full), cur, ck) && cur.is_solid()) + joints.insert(full); + } } - bool added = false; + constexpr std::array DIRS = { //MAYBE: diags + voxel_pos(1, 0, 0), voxel_pos(-1, 0, 0), voxel_pos(0, 1, 0), + voxel_pos(0, -1, 0), voxel_pos(0, 0, 1), voxel_pos(0, 0, -1)}; while (!joints.empty()) { std::queue todo; todo.push(*joints.begin()); robin_hood::unordered_set part; part.insert(todo.front()); - size_t i = 0; - for (; !todo.empty() && i < floodFillLimit; i++) { + for (size_t i = 0; !todo.empty() && i < floodFillLimit; i++) { const auto full = todo.front(); todo.pop(); joints.erase(full); - if (Voxel v; GetChunk(chunks, glm::splitIdx(full), v, ck, ck_pos) && v.is_solid()) { - constexpr std::array DIRS = { - voxel_pos(1, 0, 0), voxel_pos(-1, 0, 0), voxel_pos(0, 1, 0), - voxel_pos(0, -1, 0), voxel_pos(0, 0, 1), voxel_pos(0, 0, -1)}; - //MAYBE: diag - for (auto dir : DIRS) { + if (GetVoxel(chunks, glm::splitIdx(full), cur, ck) && cur.is_solid()) { + for (const auto& dir: DIRS) { const auto off = full + dir; if (part.insert(off).second) todo.push(off); @@ -112,24 +107,22 @@ inline bool Split(area_map &areas, action::FillShape fill, size_t floodFillLimit } } if (todo.empty()) { - added = true; - callback(part, cleanVoxel, chunks, ck, ck_pos, it->second); + callback(part); } } - return added; } -/// Call Apply then Split -template -inline void ApplySplit(area_map &areas, action::FillShape fill, size_t floodFillLimit, const CB& callback) { - Apply(areas, fill, callback); - Split(areas, fill, floodFillLimit, [&](const robin_hood::unordered_set& part, Voxel next, - world::ChunkContainer& chunks, std::shared_ptr& ck, chunk_pos& ck_pos, std::shared_ptr&) { - for(auto full: part) { +/// Call Apply then clean Splitted +template +inline void ApplyEraseSplitted(CC &chunks, action::FillShape fill, size_t floodFillLimit, const Voxel& cleanVoxel, const CB& callback) { + Apply(chunks, fill, callback); + Split(chunks, fill, floodFillLimit, [&](const robin_hood::unordered_set& part) { + last_chunk ck; Voxel prev; + for(const auto& full: part) { const auto split = glm::splitIdx(full); - if (Voxel prev; world::iterator::GetChunk(chunks, split, prev, ck, ck_pos) && prev.is_solid()) { - if (prev.value != next.value) { - callback(ck, ck_pos, split.second, prev, next, fill.radius * .05f); + if (world::iterator::GetVoxel(chunks, split, prev, ck) && prev.is_solid()) { + if (prev.value != cleanVoxel.value) { + callback(ck.ptr, ck.pos, split.second, prev, cleanVoxel, fill.radius * FILL_DELAY); } } } diff --git a/src/core/world/materials.hpp b/src/core/world/materials.hpp deleted file mode 100644 index f420a53..0000000 --- a/src/core/world/materials.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace world::materials { - //TODO: AoS to SoA compile time - struct Material { - std::string name; - std::string texture; - float roughness; - bool solid; - //uint16_t break_to - }; - - //MAYBE: index name enum - constexpr auto AIR = 0; - constexpr auto DIRT = 1; - constexpr auto GRASS = 2; - constexpr auto SAND = 3; - constexpr auto ROCK = 4; - constexpr auto WATER = 8; - - /// Materials count - static const auto count = 9; - static_assert(count < (USHRT_MAX >> 4), "for byte packing see Voxel"); - /// Materials names - static const std::array names = {{"Air", "Dirt", "Grass", "Sand", "Rock", "Wall", "Path", "Alien metal", "Water"}}; - /// Materials textures - static const std::array textures_map = {{0, 2, 9, 1, 7, 6, 3, 8, 12}}; - /// Materials roughness. - /// -1: slope, 0: normal, 1: cube - static const std::array roughness = {{0, 0, 0, 0, 0, 0, -1, .8, 0}}; - /// Materials interactive - static const std::array invisibility = {{true, false, false, false, false, false, false, false, false}}; - /// Materials see throw - static const std::array transparency = {{true, false, false, false, false, false, false, false, true}}; - /// Materials is solid - static const std::array solidity = {{false, true, true, true, true, true, true, true, false}}; - - /// All textures - static const std::array textures = {{"Debug", "Sand", "Dirt", "Stone_path", "Mapl", "Seaside_rock", "Stone_wall", "Rough_rock", "Alien", "Grass", "Plain_grass", "Forest_grass", "Water"}}; -} diff --git a/src/core/world/models.hpp b/src/core/world/models.hpp deleted file mode 100644 index 2a43ccf..0000000 --- a/src/core/world/models.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include -#include - -namespace world::models { - static const std::vector player = {24,0,0,0,0,0,0,0,0,0,1,0,2,0,0,0,5,0,1,0,0,0,2,0,4,0,0,0,4,0,5,0,1,0,3,0,2,0,5,0,3,0,1,0,2,0,3,0,4,0,4,0,3,0,5,0,6,0,0,0,0,0,0,0,0,60,0,0,0,0,8,0,0,60,0,0,0,0,0,0,0,0,0,60,0,0,8,0,0,0,0,60,0,0,0,0,0,0,0,0,0,60,8,0,0,0,0,0,0,60,0,0,0,-68,0,0,0,0,8,0,0,-68,0,0,0,0,0,0,0,0,0,-68,0,0,8,0,0,0,0,-68,0,0,0,0,0,0,0,0,0,-68,8,0,0,0,0,0,0,-68,0,0}; -} \ No newline at end of file diff --git a/src/core/world/module_types.hpp b/src/core/world/module_types.hpp new file mode 100644 index 0000000..5faab17 --- /dev/null +++ b/src/core/world/module_types.hpp @@ -0,0 +1,129 @@ +#pragma once + +#include +#include +#include "./units.hpp" +#include "../utils/flags.hpp" + +namespace world::generator { class Abstract; } +namespace world::module { + +/// Index to registry element +template +struct id { + id(size_t index): val(index) { } + size_t val; +}; + +struct _module{ }; +using module_id = id<_module>; + +/// Material properties without texture +struct raw_material { + raw_material(const std::string& name, float roughness = 0, bool invisibility = false, bool transparency = false, bool solidity = true): + name(name), roughness(roughness), invisibility(invisibility), transparency(transparency), solidity(solidity) { } + + /// Material in-module name + std::string name; + /// Material roughness + /// -1: slope, 0: normal, 1: cube + float roughness; + /// Material is interactive + bool invisibility; + /// Material is see throw + bool transparency; + /// Material is solid + bool solidity; + //uint16_t break_to +}; + +/// Material properties +struct material: raw_material { + material(const std::string& name, const std::string& texture, float roughness = 0, bool invisibility = false, bool transparency = false, bool solidity = true): + raw_material(name, roughness, invisibility, transparency, solidity), texture(texture) { } + material(const std::string& name): material(name, name) { } + + std::string texture; +}; +using material_id = id; +struct _texture{ }; +using texture_id = id<_texture>; + +// MAYBE: templatized +struct materials { + std::vector names; + std::vector module_ids; + std::vector texture_ids; + std::vector roughnesses; + std::vector invisibilities; + std::vector transparencies; + std::vector solidities; + + size_t size() const { return names.size(); } + void reserve(size_t n) { + names.reserve(n); + module_ids.reserve(n); + texture_ids.reserve(n); + roughnesses.reserve(n); + invisibilities.reserve(n); + transparencies.reserve(n); + solidities.reserve(n); + } + size_t push(const material& m, size_t mod_id, size_t tex_id) { + const auto i = size(); + names.push_back(m.name); + module_ids.push_back(mod_id); + texture_ids.push_back(tex_id); + roughnesses.push_back(m.roughness); + invisibilities.push_back(m.invisibility); + transparencies.push_back(m.transparency); + solidities.push_back(m.solidity); + return i; + } +}; + +// Model properties +struct model { + /// render::Model::Data serialized + using data = std::vector; + + model(const std::string& n, const data& m, const geometry::faabb& col, glm::vec3 sc = glm::vec3(1)): + name(n), dt(m), collision(col), scale(sc) { } + model(const std::string& n, data&& m, const geometry::faabb& col, glm::vec3 sc = glm::vec3(1)): + name(n), dt(std::move(m)), collision(col), scale(sc) { } + + std::string name; + data dt; + /// Collision box + geometry::faabb collision; + /// Visual model size ratio + glm::vec3 scale; +}; +struct registered_model: model { + registered_model(const model& m, size_t mod_id): + model(m), module_id(mod_id) { } + + size_t module_id; +}; +using model_id = id; + +// Generator properties +struct generator { + using params = const void*; + typedef owner factory_fn(params); + + generator(const std::string& n, factory_fn* f): + name(n), factory(f) { } + + std::string name; + factory_fn * factory; +}; + +struct registered_generator: generator { + registered_generator(const generator& g, size_t mod_id): + generator(g), module_id(mod_id) { } + + size_t module_id; +}; +using generator_id = id; +} \ No newline at end of file diff --git a/src/core/world/position.h b/src/core/world/position.h deleted file mode 100644 index 2202e3f..0000000 --- a/src/core/world/position.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * chunk_voxel_pos: u8 (contains u5: CHUNK_LENGTH) - * chunk_voxel_idx: u16 - * chunk_pos: i56 - * region_chunk_pos: u8 (contains u5: REGION_LENGTH) - * region_chunk_idx: u16 - * region_pos: i48 (NOTE: trimmed to i32) - * voxel_pos: i64 - * - * camera_offset: f32 - * camera_pos: region_pos + camera_offset - * - * known limits: - * - noise: f32 2^24 spaghettification - * - noise: i32 2^32 loop - * - position: i32(chunk_pos)+u5(CHUNK_LENGTH) 2^36 no rendering (NOTE: only with chunk_pos as int) - * - position: i32(region_pos)+u5(REGION_LENGTH)+u5(CHUNK_LENGTH) 2^42 loop - */ - -#pragma once - -#include -#include "../data/glm.hpp" -#include "../data/generational.hpp" - -const auto CHUNK_LENGTH = glm::IDX_LENGTH; -const auto CHUNK_SIZE = CHUNK_LENGTH * CHUNK_LENGTH * CHUNK_LENGTH; -const auto REGION_LENGTH = glm::IDX_LENGTH; - -using voxel_pos = glm::llvec3; -using chunk_pos = glm::lvec3; -using chunk_voxel_pos = glm::ucvec3; -using chunk_voxel_idx = glm::u16; -using region_pos = glm::ivec3; -using region_chunk_pos = glm::ucvec3; -using region_chunk_idx = glm::u16; - -using area_id = data::generational::id; -template -using area_ = std::pair; -struct area_hash { - template - std::size_t operator()(area_ const& a) const noexcept { - std::size_t h1 = std::hash{}(a.first); - std::size_t h2 = std::hash{}(a.second); - return h1 ^ (h2 << 1); - } -}; -using area_pos = glm::ifvec3; - -using entity_id = data::generational::id; -using entity_instance_id = std::pair; -static const auto PLAYER_ENTITY_ID = data::generational::id(0); - -using camera_pos = glm::ifvec3; diff --git a/src/core/world/raycast.hpp b/src/core/world/raycast.hpp deleted file mode 100644 index b40efdd..0000000 --- a/src/core/world/raycast.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include "Universe.hpp" -#include "Chunk.hpp" - -namespace world { - -template -Universe::ray_result Raycast(const geometry::Ray& ray, const Areas& areas) { - //MAYBE: ray + offset to get float precision - Universe::ray_result target; - size_t dist = UINT32_MAX - 1; - for(auto& area: areas) { - if(ray.intersect(area.second->getBounding()) != geometry::IBox::ContainmentType::Disjoint) { - const auto &offset = area.second->getOffset().as_voxel(); - const auto &chunks = area.second->getChunks(); - std::shared_ptr chunk = nullptr; - chunk_pos chunk_vec(INT_MAX); - geometry::Ray::iterator it(ray); - glm::llvec3 point; - for (size_t i = 0; i < dist && it.next(point); i++) { - const auto pos = point - offset; - const chunk_pos cPos = glm::divide(pos); - if(cPos != chunk_vec) { - if (const auto it = chunks.find(cPos); it != chunks.end()) { - chunk = it->second; - chunk_vec = cPos; - } else if(chunks.inRange(cPos)) { - target.emplace(std::make_pair(area.first, cPos), offset); - break; - } else { - chunk = nullptr; - } - } - if(chunk != nullptr) { - const auto voxel = chunk->getAt(glm::modulo(pos)); - if(voxel.is_solid()) { - target.emplace(std::make_pair(area.first, pos), voxel, offset); - dist = i; - break; - } - } - } - } - } - return target; -} - -} \ No newline at end of file diff --git a/src/core/world/server_handle.hpp b/src/core/world/server_handle.hpp new file mode 100644 index 0000000..6335a9e --- /dev/null +++ b/src/core/world/server_handle.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "./actions.hpp" +#include "../geometry/Faces.hpp" +#include "../queue/safe_queue.hpp" +#include "../utils/mutex.hpp" + +namespace world { + +/// Link between world::server::SharedUniverse and world::client::LocalUniverse +struct server_handle { + bool running = false; + node_id entityId; + mutex::shared_guard_ref elements; + + std::string playerName; + std::function emit; + std::optional teleport; + data::safe_queue messages; +}; + +struct AbstractServerFactory { + virtual ~AbstractServerFactory() { } + virtual server_handle* run() = 0; + virtual void onGui() = 0; +}; +} \ No newline at end of file diff --git a/src/core/world/units.hpp b/src/core/world/units.hpp new file mode 100644 index 0000000..768aa1d --- /dev/null +++ b/src/core/world/units.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include "../geometry/glm.hpp" +#include "../geometry/Box.hpp" + +namespace world { + +const auto CHUNK_LENGTH = glm::IDX_LENGTH; +const auto CHUNK_SIZE = glm::IDX_SIZE; +const auto REGION_LENGTH = glm::IDX_LENGTH; + +/// Absolute floating position +using world_pos = glm::lfvec3; +/// Absolute integer position +using cell_pos = glm::llvec3; + +using offset_pos = glm::vec3; +using pivot = glm::quat; +constexpr auto identity_pivot = pivot(1, 0, 0, 0); +using lpivot = glm::qua; + +/// Relative region position inside given area +/// CHUNK_LENGTH * REGION_LENGTH voxels long +/// Just needed 56 bits (i64 - u8) +using region_pos = glm::llvec3; +/// Relative chunk position inside given region +/// Just needed 5 bits (log2(REGION_LENGTH)) +using region_chunk_pos = glm::usvec3; +/// Chunk index inside given region +/// Just needed 15 bits (log2(REGION_SIZE)) +using region_chunk_idx = glm::us; +/// Relative chunk position inside given area +/// Needs 48 bits (i64 - u8 - u8) +using chunk_pos = glm::lvec3; +/// Relative voxel position inside given chunk +/// Just needed 5 bits (log2(CHUNK_LENGTH)) +using chunk_voxel_pos = glm::ucvec3; +/// Voxel index inside given chunk +/// Just needed 15 bits (log2(REGION_SIZE)) +using chunk_voxel_idx = glm::us; +/// Relative voxel position inside given area +using voxel_pos = glm::llvec3; + +/// Axis Aligned Bounding box +using aabb = geometry::lfaabb; +/// Oriented Bounding box +using obb = geometry::lfobb; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 491ab15..556e706 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,18 +1,21 @@ /** * \file main.cpp * \brief Univerxel game - * \author Maelys Bois - * \version 0.0.1 + * \author Shu + * \version 0.0.2 * * Univerxel main program. */ #include "client/Client.hpp" #include "server/Server.hpp" -#include "core/config.hpp" +#include "client/srvContainer.hpp" +#include "core/options_full.hpp" #include "core/utils/tracy.hpp" +#include "core/utils/logger.hpp" #include "version.h" + /// Entry point int main(int argc, char *argv[]){ LOG("Univerxel " << UNIVERXEL_VERSION); @@ -24,46 +27,33 @@ int main(int argc, char *argv[]){ auto options = config::options(argc > 1 ? argv[1] : config::DEFAULT_FILE); options.save(); - std::optional server; - if (options.hasServer()) - server.emplace(options.getServer()); - std::optional client; - if (options.hasClient()) - client.emplace(options.getClient()); + world::module::Registry::Load(); - const auto serverTask = [&] { -#if TRACY_ENABLE - tracy::SetThreadName("Server"); -#endif - server.value().run(); - }; - const auto clientTask = [&](server_handle* const handle) { + if (options.hasClient()) { #if TRACY_ENABLE tracy::SetThreadName("Client"); #endif - client.value().run(handle); - }; + ServerFactory* srvContainer = options.hasServer() ? + new ServerFactory(options.getServer()) : nullptr; - if (server.has_value()) { - if (client.has_value()) { - auto serverThread = std::thread(serverTask); + auto client = Client(options.getClient(), srvContainer); + client.run(); - clientTask(server.value().getHandle()); - - if(serverThread.joinable()) - serverThread.join(); - } else { - serverTask(); - } + delete srvContainer; + } else if (options.hasServer()) { +#if TRACY_ENABLE + tracy::SetThreadName("Server"); +#endif + LOG_I("Running only server"); + auto server = Server(options.getServer()); + server.run(); } else { - if (client.has_value()) { - clientTask(nullptr); - } else { - options.save(); - FATAL("Nothing to start"); - } + FATAL("Nothing to start"); } + options.save(); + world::module::Registry::Unload(); + options.save(); return 0; diff --git a/src/modules/core/Core.cpp b/src/modules/core/Core.cpp new file mode 100644 index 0000000..e638195 --- /dev/null +++ b/src/modules/core/Core.cpp @@ -0,0 +1,49 @@ +#include "./Core.hpp" + +using namespace world::module; +using namespace world::module::core; +using namespace world::generator; + +Core* Core::sInstance = nullptr; +static world::module::model::data PLAYER_MODEL = {24,0,0,0,0,0,0,0,0,0,1,0,2,0,0,0,5,0,1,0,0,0,2,0,4,0,0,0,4,0,5,0,1,0,3,0,2,0,5,0,3,0,1,0,2,0,3,0,4,0,4,0,3,0,5,0,6,0,0,0,0,0,0,0,0,60,0,0,0,0,-128,-128,0,60,0,0,0,0,0,0,0,0,0,60,0,0,-128,-128,0,0,0,60,0,0,0,0,0,0,0,0,0,60,-128,-128,0,0,0,0,0,60,0,0,0,-68,0,0,0,0,-128,-128,0,-68,0,0,0,0,0,0,0,0,0,-68,0,0,-128,-128,0,0,0,-68,0,0,0,0,0,0,0,0,0,-68,-128,-128,0,0,0,0,0,-68,0,0}; + +#ifndef LIGHT_CLIENT +#include "./PlanetGenerator.hpp" +owner RoundPlanetFactory(generator::params p) { + return new RoundPlanet(*(const RoundPlanet::Params*)p); +} +owner CubicPlanetFactory(generator::params p) { + return new CubicPlanet(*(const CubicPlanet::Params*)p); +} +#endif + +void Core::setup(Registry::Loader reg) { + reg.reserveMaterials(16); + ids.AIR = reg.addMaterial(material("Air", "Debug", 0, true, true, false)).val; + ids.DIRT = reg.addMaterial(material("Dirt")).val; + ids.GRASS = reg.addMaterial(material("Grass")).val; + ids.SAND = reg.addMaterial(material("Sand")).val; + ids.ROCK = reg.addMaterial(material("Rock", "Rough_rock")).val; + reg.addMaterial(material("Wall", "Stone_wall")); + reg.addMaterial(material("Path", "Stone_path", -1)); + const auto ALIEN = reg.addMaterial(material("Alien metal", "Alien", .8)); + ids.WATER = reg.addMaterial(material("Water", "Water", 0, false, true, false)).val; + + { // Replace -128,-128 with alien texture + const uint16_t ALIEN_TEX = reg.reg->findTextureId(ALIEN).value().val; + char const* ALIEN_CHARS = reinterpret_cast(&ALIEN_TEX); + for (size_t i = 1; i < PLAYER_MODEL.size(); i++) { + if (PLAYER_MODEL[i-1] == -128 && PLAYER_MODEL[i] == -128) { + PLAYER_MODEL[i-1] = *ALIEN_CHARS; + PLAYER_MODEL[i] = *(ALIEN_CHARS+1); + } + } + ids.PLAYER = reg.addModel(model("Player", PLAYER_MODEL, geometry::faabb::FromCenter(glm::vec3(0), glm::vec3(.5)))).val; + } + + sInstance = this; +#ifndef LIGHT_CLIENT + reg.addGenerator(generator("round_planet", RoundPlanetFactory)); + reg.addGenerator(generator("cubic_planet", CubicPlanetFactory)); +#endif +} \ No newline at end of file diff --git a/src/modules/core/Core.hpp b/src/modules/core/Core.hpp new file mode 100644 index 0000000..6d47d47 --- /dev/null +++ b/src/modules/core/Core.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "core/world/Registry.hpp" + +namespace world::module::core { + +/// Minimal root module +class Core final: public Module { +public: + Core() { } + ~Core() { } + + const char *getName() const override { return "core"; } + const char *getRequirements() const override { return ""; } + void setup(Registry::Loader); + + struct IDS { + size_t AIR; + size_t DIRT; + size_t GRASS; + size_t ROCK; + size_t SAND; + size_t WATER; + + size_t PLAYER; + } ids; + + static _FORCE_INLINE_ Core *Get() { + assert(sInstance != nullptr && "Uninitialized module"); + return sInstance; + } + +private: + static Core *sInstance; +}; +} diff --git a/src/modules/core/PlanetGenerator.hpp b/src/modules/core/PlanetGenerator.hpp new file mode 100644 index 0000000..8fbe077 --- /dev/null +++ b/src/modules/core/PlanetGenerator.hpp @@ -0,0 +1,103 @@ +#pragma once + +#include "core/world/generator/Cave.hpp" +#include "core/geometry/math.hpp" +#include "./Core.hpp" + +namespace world::generator { + + enum class PlanetShape { Cube, Sphere }; + + /// Abstract shaped planet generator + template + class Planet: public Abstract { + public: + struct Params: Cave::Params { + Params(voxel_pos::value_type height, int seed = 42, float surface_roughness = .1, float depth_roughness = .05, float density = 0, float gran = 30): + Cave::Params(seed, density, gran), height(height), surface_roughness(surface_roughness), depth_roughness(depth_roughness) { } + + /// Sea level (minimal density) + voxel_pos::value_type height; + /// Density decrease over height + float surface_roughness; + /// Density decrease under height + float depth_roughness; + /// Sea border depth + float beach_depth = .01f; + /// Sea ground displacement + float beach_displacement = .01f; + }; + Planet(const Params& p) : params(p), density(Noise::SimplexFractal(p.seed)), displacement(Noise::Simplex(p.seed * 5, .01)) {} + + void generate(const chunk_pos &pos, std::array &out) override { + auto densitySet = density.getBlock(pos, CHUNK_LENGTH); + auto displacementSet = displacement.getBlock(pos, CHUNK_LENGTH); + const auto& IDS = world::module::core::Core::Get()->ids; + for (size_t i = 0; i < CHUNK_SIZE; i++) { + const auto vPos = glm::fromIdx(i); + const auto noise_i = glm::toIdx(vPos.z, vPos.y, vPos.x); + + const auto heightRatio = ((float)getHeight(glm::multiply(pos) + glm::llvec3(vPos)) - params.height) / params.height; + + const auto verticalDensityOffset = heightRatio / (heightRatio >= 0 ? params.surface_roughness : params.depth_roughness); + const auto density = std::min((densitySet.get()[noise_i] + params.density - verticalDensityOffset) * params.granularity, 1.f) * Voxel::DENSITY_MAX; + + out[i] = [&]() -> Voxel { + if (density > 0) { + const auto material = [&]() -> int { + const auto noisedHeightRatio = heightRatio - displacementSet.get()[noise_i] * params.beach_displacement; + if(noisedHeightRatio >= 0) { + return densitySet.get()[noise_i] + params.density < ((heightRatio + 0.007f) / params.surface_roughness) ? IDS.GRASS : IDS.DIRT; + } else { + return noisedHeightRatio >= -params.beach_depth ? IDS.SAND : IDS.ROCK; + } + }(); + return Voxel(material, density); + } else { + if (heightRatio >= 0) { + return Voxel(IDS.AIR, (heightRatio < params.surface_roughness) * Voxel::DENSITY_MAX); + } else { + return Voxel(IDS.WATER, std::clamp(-heightRatio * params.height, 0.f, 1.f) * Voxel::DENSITY_MAX); + } + } + }(); + } + } + glm::vec3 getGravity(const voxel_pos& pos) const override { + const auto heightRatio = static_cast((getHeight(pos) - params.height) / params.height); + const auto scale = params.height >> 7; + const auto mass = scale * scale * scale; //FIXME: average material density + return -getSurfaceDir(pos) * std::max(0, 1.f - heightRatio * heightRatio) * glm::vec3(mass); + } + std::optional getCurvature() const override { + if constexpr (shape == PlanetShape::Cube) { + return params.height * (1.1 + params.surface_roughness); + } + return {}; + } + private: + static constexpr PlanetShape shape = PS; + static constexpr glm::f64 getHeight(const voxel_pos &p) { + if constexpr(shape == PlanetShape::Cube) { + return glm::max_axis(glm::abs(p)); + } else { + return glm::length(glm::dvec3(p)); + } + } + /// Centre inverse direction + // NOTE: So center point is consider mass center + static constexpr glm::vec3 getSurfaceDir(const voxel_pos &p) { + if constexpr(shape == PlanetShape::Cube) { + return glm::inormalize(p); + } else { + return glm::normalize(glm::dvec3(p)); + } + } + Params params; + Noise density; + Noise displacement; + }; + using CubicPlanet = Planet; + using RoundPlanet = Planet; + +} \ No newline at end of file diff --git a/src/server.cpp b/src/server.cpp index c7b690a..61ea919 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1,16 +1,16 @@ /** * \file server.cpp * \brief Univerxel server - * \author Maelys Bois - * \version 0.0.1 + * \author Shu + * \version 0.0.2 * * Univerxel standalone server program. */ -#define STANDALONE 1 #include "server/Server.hpp" -#include "core/standalone_config.hpp" +#include "core/options_part.hpp" #include "core/utils/tracy.hpp" +#include "core/utils/logger.hpp" #include "version.h" /// Entry point @@ -21,17 +21,20 @@ int main(int argc, char *argv[]){ LOG("Profiling !"); #endif - auto options = config::standalone_options(argc > 1 ? argv[1] : config::DEFAULT_FILE); + auto options = config::options_part(argc > 1 ? argv[1] : config::DEFAULT_FILE); options.save(); #if TRACY_ENABLE tracy::SetThreadName("Main"); #endif + world::module::Registry::Load(); + auto server = Server(options.get()); server.run(); options.save(); + world::module::Registry::Unload(); return 0; } \ No newline at end of file diff --git a/src/server/Server.cpp b/src/server/Server.cpp index f5bcdf3..8bf4f1e 100644 --- a/src/server/Server.cpp +++ b/src/server/Server.cpp @@ -1,43 +1,489 @@ -#include "Server.hpp" -#include "world/StandaloneUniverse.hpp" -#ifndef STANDALONE -#include "world/SharedUniverse.hpp" -#endif +#include "./Server.hpp" +#include "world/Universe.hpp" #include #include +#include +#include +#include "./world/client.hpp" -Server::Server(config::server::options& options): options(options), localHandle(options.allowLocal ? new server_handle() : nullptr) { } -Server::~Server() { } +#undef LOG_PREFIX +#define LOG_PREFIX "Server: " -const auto TPS = 10; +#ifndef STANDALONE_SERVER +constexpr bool CAN_SHARE = true; +#else +constexpr bool CAN_SHARE = false; +#endif + +using namespace world; +namespace ws = world::server; + +Server::Server(config::server::options& options): options(options), host(options.connection, + [&](net::server::Peer* peer) { return onConnect(peer); }, + [&](net::server::Peer* peer, bool is_app, uint16_t reason) { return onDisconnect(peer, is_app, reason); }, + [&](net::server::Peer* peer, const memory::read_view &buf, net::PacketFlags flags) { return onPacket(peer, buf, flags); } + ), serializer(options.world.folderPath), + localHandle(CAN_SHARE && options.allowLocal ? new server_handle() : nullptr) { } +Server::~Server() { + LOG_W("Stopped"); +} static bool running = true; void handle_signal(int) { running = false; } void Server::run() { - universe = [&]() -> std::unique_ptr { -#ifndef STANDALONE - if(options.allowLocal) - return std::make_unique(options.world, localHandle); -#endif - - return std::make_unique(options.world); - }(); - signal(SIGINT, handle_signal); signal(SIGTERM, handle_signal); - auto lastTick = std::chrono::steady_clock::now(); - while(running && (localHandle == nullptr || localHandle->running)) { - FrameMarkStart("Server"); - auto startTick = std::chrono::steady_clock::now(); - const std::chrono::duration> deltaTime = startTick - lastTick; - universe->update(deltaTime.count()); - FrameMarkEnd("Server"); - while (std::chrono::steady_clock::now() < startTick + std::chrono::milliseconds(1000 / TPS)) { - universe->pull(); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); + universe = std::make_unique(options.world, &serializer, localHandle); + if (localHandle) { + localHandle->emit = [&](const world::action::packet &packet) { onPacket(packet); }; + localHandle->entityId = universe->addPlayer(); + { + const auto entry = universe->findHierarchy(localHandle->entityId); assert(entry); + localHandle->teleport = world::relative_transform{entry->parent, entry->relative}; } - lastTick = startTick; + localHandle->running = true; } + + auto lastUpdate = std::chrono::steady_clock::now(); + const auto deltaTime = [&](const std::chrono::_V2::steady_clock::time_point &now) { + std::chrono::duration> delta = now - lastUpdate; + lastUpdate = now; + return delta.count(); + }; + while (running && (!localHandle || localHandle->running)) { + FrameMarkStart("Server"); + const auto tickStart = std::chrono::steady_clock::now(); + universe->update(deltaTime(tickStart)); + universe->upgrade(); + broadcastEntitiesChanges(*universe->setElements()); + { + size_t upt = 1; + const size_t UPT = options.upt; + const auto nextTick = tickStart + std::chrono::milliseconds(1000 / options.tps); + const auto uptDelay = std::chrono::milliseconds(1000 / options.tps / UPT); + do { + pull(); + const auto now = std::chrono::steady_clock::now(); + if (now >= nextTick) + break; + if (upt < UPT && now >= lastUpdate + uptDelay) { + universe->update(deltaTime(now)); + upt++; + } + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } while (true); + } + FrameMarkEnd("Server"); + } + broadcastMessage("> Server is closing"); + host.sendBroadcast(memory::read_buffer::Of(net::server_packet_type::QUIT, net::disconnect_reason::CLOSE)); + for (size_t i = 0; i < 20; i++) { + std::this_thread::sleep_for(std::chrono::milliseconds(1000 / 20)); + host.pull(); + } + host.close((uint16_t)net::disconnect_reason::CLOSE); + if (localHandle) { + universe->removePlayer(localHandle->entityId); + } + universe.reset(); +} + +void Server::pull() { + ZoneScopedN("Network"); + host.pull(); + // Send pending data + host.iterPeers([&](net::server::Peer *peer) { + auto ctx = peer->getCtx(); + if (!ctx) + return; + + auto& edits = ctx->pending.edits; + constexpr auto PARALLEL_EDITS = 1; + if (!edits.empty() && peer->queueSize(net::server::queue::EDIT) <= PARALLEL_EDITS - 1) { + peer->send(memory::read_buffer::Of(net::server_packet_type::EDITS, edits.front())); + edits.pop(); + } + + auto& chunks = ctx->pending.chunks; + if (!chunks.empty()) { + //TODO: use congestion + constexpr auto PARALLEL_CHUNKS = 5; + size_t i = peer->queueSize(net::server::queue::CHUNK); + node_chunk_pos pending{generational::id(), chunk_pos(0)}; + while(i <= (PARALLEL_CHUNKS - 1) && chunks.pop(pending)) { + if (const auto chunk = universe->findChunk(pending)) { + peer->send(serializer.writeChunk(pending, chunk), net::server::queue::CHUNK); + i++; + } + } + + if (chunks.empty()) + peer->send(serializer.writeChunkReset()); //FIXME: must be received after last chunk + } + }); +} + +bool Server::onConnect(net::server::Peer* peer) { + ZoneScopedN("Connect"); + LOG_I("Client connect from " << peer->getAddress()); + return true; +} +bool Server::onDisconnect(net::server::Peer *peer, bool is_app, uint16_t reason) { + ZoneScopedN("Disconnect"); + LOG_I((is_app ? "Client disconnect from " : "Client connection lost from ") << peer->getAddress() << " (" << reason << ')'); + if (auto ctx = peer->getCtx()) { + //FIXME: save inventory, pos... + universe->removePlayer(ctx->entityId); + broadcastMessage("> " + ctx->name + " has left our universe"); + delete ctx; + peer->ctx = nullptr; + } + return true; +} + +bool Server::onPacket(net::server::Peer *peer, const memory::read_view &buf, net::PacketFlags) { + ZoneScopedN("Packet"); + + using namespace net; + + auto packet = memory::read_buffer(buf, nullptr); + + client_packet_type type; + if(!packet.read(type)) { + LOG_D("Empty packet from " << peer->getAddress()); + return false; + } + + const auto ctx = peer->getCtx(); + if (!ctx) { // Not logged in + //TODO: disconnect unauthorized + if (type != net::client_packet_type::HELLO) + return false; + + uint8_t type; + if (!packet.read(type)) + return false; + + switch (type) { + case 0: { // Register + //TODO: check player index and get id + const auto rem = packet.readRemaining(); + std::string name((const char*)rem.data()); + if (name.size() == 0 || (name.size() != rem.size() && name.size() != rem.size()-1)) + return false; + + peer->ctx = new ws::client(0, name, universe->addPlayer()); + break; + } + + //TODO: handle login + default: + return false; + } + + peer->send(serializer.writeCapabilities(options.world.loadDistance, options.world.floodFillLimit)); + //TODO: lock while not received + peer->send(serializer.writeDict(), net::server::queue::CHUNK); + const auto ctx = peer->getCtx(); + { + const auto entry = universe->findHierarchy(ctx->entityId); assert(entry); + peer->send(serializer.writeTeleport(ctx->entityId, world::relative_transform{entry->parent, entry->relative})); + } + broadcastMessage("> " + ctx->name + " has joined us"); + if (const auto entities = serializeEntitiesFrom(*universe->getElements()); entities.ptr) { + peer->send(entities, net::server::queue::ENTITY); + } + return true; + } + + switch (type) { + case client_packet_type::CAPABILITIES: { + packet.read(ctx->caps.handleEdits); + /*if (!PREDICTABLE && ctx->handleEdits) { + LOG_E("Client misread capabilities"); + }*/ + break; + } + case client_packet_type::MOVE: { + if (relative_pos pos; !packet.read(pos) || + !universe->movePlayer(ctx->entityId, pos)) { + LOG_T("Bad move"); + const auto entry = universe->findHierarchy(ctx->entityId); assert(entry); + peer->send(serializer.writeTeleport(ctx->entityId, world::relative_transform{entry->parent, entry->relative})); + } + break; + } + case client_packet_type::FILL_SHAPE: { + if(const auto fill = packet.read()) { + fillShape(*fill); + } else { + LOG_T("Bad fill"); + } + break; + } + case client_packet_type::MESSAGE: { + const auto ref = packet.readRemaining(); + broadcastMessage(ctx->name + ": " + std::string((const char*)ref.data(), ref.size())); + break; + } + case client_packet_type::REQUEST_REGIONS: { + if (auto player = universe->getPlayer(ctx->entityId)) { + area_id id; + if (!packet.read(id)) + break; + if (auto node = universe->getElements()->findArea(id)) { + region_pos rpos; + auto regions = node->get()->getRegions(); + const auto pos = player->absolute.position; + while (!packet.isDone() && packet.read(rpos)) { + if (auto it_r = regions->find(rpos); it_r != regions->end()) { + const auto off = glm::divide(node->absolute.computeChild(glm::multiply(it_r->first, glm::lvec3(REGION_LENGTH * CHUNK_LENGTH))) - pos); + if (glm::length2(off) <= glm::pow2(options.world.loadDistance + REGION_LENGTH * 2)) { + peer->send(serializer.writeRegion(area_region_ref(id, rpos), it_r->second), net::server::CHUNK); + } else { + LOG_T("Request out of range region"); + } + } else { + LOG_T("Requested region not found"); + } + } + } else { + LOG_T("Bad region request"); + } + } + break; + } + case client_packet_type::REQUEST_CHUNKS: { + if (auto player = universe->getPlayer(ctx->entityId)) { + const auto pos = player->absolute.position; + + node_id id; + if (!packet.read(id)) + break; + + const auto elements = universe->getElements(); + if (ws::Elements::Is(elements->withFlag(id))) { + if (auto area = elements->findArea(id.val)) { + auto &chunks = area->get()->getChunks(); + chunk_pos cpos; + for (size_t i = 0; !packet.isDone() && i < MAX_PENDING_CHUNK_COUNT && packet.read(cpos); i++) { + const auto dist = glm::length2(glm::divide(area->absolute.computeChild(glm::multiply(cpos)) - pos)); + if (dist <= glm::pow2(options.world.loadDistance) && chunks.inRange(cpos)) { + if (chunks.findInRange(cpos) != nullptr) + ctx->pending.chunks.push(node_chunk_pos{id, cpos}, dist); + //MAYBE: else notify client + } else { + LOG_T("Request out of range chunk"); + } + } + } else { + LOG_T("Bad chunk request"); + } + } else if (auto part = elements->findPart(id.val)) { + auto size = part->get(); + const auto dist = glm::length2(glm::divide(part->absolute.position - pos)); + if (dist <= glm::pow2(options.world.loadDistance)) { + chunk_pos cpos; + for (size_t i = 0; !packet.isDone() && i < MAX_PENDING_CHUNK_COUNT && packet.read(cpos); i++) { + if (dist <= glm::pow2(options.world.loadDistance) && size->inRange(cpos)) { + ctx->pending.chunks.push(node_chunk_pos{id, cpos}, dist); + } else { + LOG_T("Request out of range chunk"); + } + } + } else { + LOG_T("Request out of range part"); + } + } else { + LOG_T("Bad chunk request"); + } + if (ctx->pending.chunks.empty()) + peer->send(serializer.writeChunkReset()); + } + break; + } + case client_packet_type::REQUEST_ENTITIES: { + node_id root; + if (!packet.read(root)) + break; + + LOG_E("Unimplemented"); + break; + } + default: + LOG_T("Bad packet from " << peer->getAddress() << " " << (int)type); + break; + } + return true; +} + +void Server::onPacket(const world::action::packet &packet) { + ZoneScopedN("Local Packet"); + assert(localHandle); + + if(const auto fill = std::get_if(&packet)) { + fillShape(*fill); + } else if(const auto message = std::get_if(&packet)) { + broadcastMessage(localHandle->playerName + ": " + message->text); + } else if(const auto move = std::get_if(&packet)) { + if(!universe->movePlayer(localHandle->entityId, move->pos)) { + LOG_W("Bad move of local player"); + } + } else { + LOG_W("Bad packet from local client " << packet.index()); + } +} + +void Server::broadcastMessage(const std::string& msg) { + if (CAN_SHARE && localHandle) { + localHandle->messages.emplace(msg); + } + host.sendBroadcast(serializer.writerMessage(msg)); +} + +void Server::broadcastEntitiesChanges(world::server::Elements& l) { + memory::write_buffer packet(net::server_packet_type::ENTITIES, sizeof(node_id)); + l.nodes.iter_empty([&] (const node_id& id) { + if (Elements::Has(id)) { + const auto flagId = Elements::Set( + Elements::Set(id)); + packet.push(flagId); + l.nodes.set_flag(Elements::Set(id)); + } + }); + l.hierarchy.iter([&](const Elements::hierarchy_entry &entry) { + const auto id = l.withFlag(entry.self); + assert(id); + const auto added = Elements::Has(id); + if (Elements::Has(id) || added) { + packet.push(id); + packet.push(ws::Elements::start_point(entry.parent, entry.relative, entry.vel)); + l.nodes.set_flag(Elements::Set( + Elements::Set(id))); + } + if (added) { + switch (Elements::GetType(id)) { + case Elements::Type::Area: { + const auto area = l.findArea(id.val)->get(); + packet.push(Area::params{area->getParams().radius, area->getCurvature()}); + break; + } + case Elements::Type::Part: + packet.push(l.findPart(id.val)->get()->size); + break; + + case Elements::Type::Instance: + packet.push(NodeOf::Make(l.findNode(id))->get()->type); + break; + + default: + break; + } + } + }); + if (packet.isFull()) + host.sendBroadcast(packet.finish(), net::server::queue::ENTITY); + +#if TRACY_ENABLE + int64_t queued = 0; + host.iterPeers([&](net::server::Peer *peer) { queued += peer->queueSize(net::server::queue::ENTITY); }); + TracyPlot("QueuedEntities", queued); +#endif +} + +memory::read_buffer Server::serializeEntitiesFrom(const world::server::Elements& l, node_id root) { + memory::write_buffer packet(net::server_packet_type::ENTITIES, sizeof(node_id)); + generational::id::idx_t from = 0; + generational::id::idx_t count = l.hierarchy.size(); + if (const auto root_entry = l.hierarchy.get(root)) { + for (node_id cur = root_entry->parent; cur;) { // Read parents in correct order + const auto node = l.nodes.directly_at(cur); + if (!node) + break; + + const auto entry = l.hierarchy.get(cur); + if (!entry) + break; + + const auto flagCur = Elements::Set( + Elements::Set(cur)); + packet.push(flagCur); + packet.push(ws::Elements::start_point{entry->parent, entry->relative, entry->vel}); + cur = entry->parent; + } + from = l.hierarchy.find(root).value().val; + count = root_entry->childs; + } else if (root) { // Not in hierarchy + return memory::read_buffer(nullptr, 0); + } + + for (size_t i = 0; i < count; i++) { + const auto entry = l.hierarchy.at(from + i); + assert(entry); + const auto id = l.withFlag(entry->self); + const auto node = l.nodes.directly_at(id); + assert(node); + const auto flagId = Elements::Set( + Elements::Set(id)); + packet.push(flagId); + packet.push(ws::Elements::start_point(entry->parent, entry->relative, entry->vel)); + switch (Elements::GetType(id)) { + case Elements::Type::Area: { + const auto area = NodeOf::Make(node)->get(); + packet.push(Area::params{area->getParams().radius, area->getCurvature()}); + break; + } + case Elements::Type::Part: + packet.push(NodeOf::Make(node)->get()->size); + break; + + case Elements::Type::Instance: + packet.push(NodeOf::Make(node)->get()->type); + break; + + default: + break; + } + } + return packet.finish(); +} + +void Server::fillShape(const world::action::FillShape& fill) { + //TODO: check ray and tool + if (fill.val.is_solid() && !universe->isRangeFree(fill.pos, geometry::Volume{action::ToGeometry(fill.shape), fill.radius})) { + LOG_T("Entity in solid fill area"); + return; + } + if (const auto id = universe->getElements()->withFlag(fill.pos.node); !Elements::Is(id)) { + LOG_W("Part edit not supported for now"); + } + bool stupidClient = false; + host.iterPeers([&](net::server::Peer *peer) { + //MAYBE: only in range + if (const auto ctx = peer->getCtx()) { + if (ctx->caps.handleEdits) + ctx->pending.edits.push(fill); + else + stupidClient = true; + } + }); + if (stupidClient) { + ws::fill_edits edits; + universe->fill(fill, &edits); + if (!edits.empty()) { + ZoneScopedN("Stupidity"); + auto buffer = serializer.writeEdits(fill.pos.node, edits); + host.iterPeers([&](net::server::Peer *peer) { + //MAYBE: only in range + const auto ctx = peer->getCtx(); + if (ctx && !ctx->caps.handleEdits) + peer->send(buffer, net::server::queue::CHUNK); + }); + } + } else { + universe->fill(fill); + } + //TODO: handle inventory } \ No newline at end of file diff --git a/src/server/Server.hpp b/src/server/Server.hpp index caebc95..9fd7637 100644 --- a/src/server/Server.hpp +++ b/src/server/Server.hpp @@ -1,11 +1,14 @@ #pragma once #include -#include "../core/server_handle.hpp" -#include "config.hpp" +#include "core/world/server_handle.hpp" +#include "./net/Server.hpp" +#include "./world/Serializer.hpp" +#include "./config.hpp" namespace world::server { class Universe; + class Elements; } class Server { public: @@ -14,10 +17,30 @@ public: void run(); - server_handle *getHandle() { return localHandle; } + world::server_handle *getHandle() { return localHandle; } private: + /// Send-Receive packets + //NOTE: triggers onPacket, onConnect & onDisconnect + void pull(); + + /// Handle networking requests + bool onConnect(net::server::Peer*); + bool onDisconnect(net::server::Peer*, bool, uint16_t); + bool onPacket(net::server::Peer*, const memory::read_view&, net::PacketFlags); + + void broadcastMessage(const std::string &); + void broadcastEntitiesChanges(world::server::Elements&); + memory::read_buffer serializeEntitiesFrom(const world::server::Elements&, world::node_id root = generational::id()); + + /// Handle local requests + void onPacket(const world::action::packet &); + + void fillShape(const world::action::FillShape&); + config::server::options& options; + net::server::Server host; + world::server::Serializer serializer; std::unique_ptr universe; - server_handle *localHandle; + world::server_handle *localHandle; }; \ No newline at end of file diff --git a/src/server/config.hpp b/src/server/config.hpp index d8d0578..8274b1d 100644 --- a/src/server/config.hpp +++ b/src/server/config.hpp @@ -1,7 +1,8 @@ #pragma once -#include -#include "world/Universe.hpp" +#include "core/utils/toml.hpp" +#include "./world/Universe.hpp" +#include "core/net/protocol.hpp" namespace config::server { @@ -9,50 +10,62 @@ namespace config::server { struct options { public: options(toml::node_view config): world() { - assert(config["enabled"]); -#ifndef STANDALONE + assert(config["enabled"] && "probably a broken config file"); +#ifndef STANDALONE_SERVER allowLocal = config["allow_local"].value_or(allowLocal); #else allowLocal = false; #endif - world.connection.host = config["connection"]["hosts"].value_or(world.connection.host); - world.connection.port = config["connection"]["port"].value_or(world.connection.port); - world.connection.key = config["connection"]["key"].value_or(world.connection.key); - world.connection.cert = config["connection"]["cert"].value_or(world.connection.cert); - world.connection.max_connections = config["max_players"].value_or(world.connection.max_connections); + connection.host = config["connection"]["hosts"].value_or(connection.host); + connection.port = config["connection"]["port"].value_or(connection.port); + connection.key = config["connection"]["key"].value_or(connection.key); + connection.cert = config["connection"]["cert"].value_or(connection.cert); + connection.max_connections = config["max_players"].value_or(connection.max_connections); world.loadDistance = config["world"]["load_distance"].value_or(world.loadDistance); world.keepDistance = config["world"]["keep_distance"].value_or(world.keepDistance); world.folderPath = config["world"]["path"].value_or(world.folderPath); world.floodFillLimit = config["world"]["max_part_size"].value_or((int64_t)world.floodFillLimit); + world.randomTick = config["clock"]["random_tick"].value_or(world.randomTick); + + tps = config["clock"]["tick_per_second"].value_or(tps); + upt = config["clock"]["update_per_tick"].value_or(upt); } toml::table save() { return toml::table({ {"enabled", true}, -#ifndef STANDALONE +#ifndef STANDALONE_SERVER {"allow_local", allowLocal}, #endif {"connection", toml::table({ - {"hosts", world.connection.host}, - {"port", world.connection.port}, - {"key", world.connection.key}, - {"cert", world.connection.cert} + {"hosts", connection.host}, + {"port", connection.port}, + {"key", connection.key}, + {"cert", connection.cert} })}, - {"max_players", world.connection.max_connections}, + {"max_players", connection.max_connections}, {"world", toml::table({ {"load_distance", world.loadDistance}, {"keep_distance", world.keepDistance}, {"path", world.folderPath}, {"max_part_size", (int64_t)world.floodFillLimit} + })}, + {"clock", toml::table({ + {"tick_per_second", tps}, + {"update_per_tick", upt}, + {"random_tick", world.randomTick} })} }); } - /// enable SharedServerUniverse and LocalClientUniverse bool allowLocal = true; world::server::Universe::options world; + net::exposure connection = net::exposure{"", 4242, 0, "content/key.pem", "content/cert.pem"}; + /// Ticks per second + uint tps = 10; + /// Physics update per tick + uint upt = 4; }; - } \ No newline at end of file diff --git a/src/server/net/Server.cpp b/src/server/net/Server.cpp index 20afd92..b8ae3a7 100644 --- a/src/server/net/Server.cpp +++ b/src/server/net/Server.cpp @@ -1,5 +1,8 @@ #include "Server.hpp" +#undef LOG_PREFIX +#define LOG_PREFIX "Server: " + using namespace net::server; namespace net::server { @@ -8,6 +11,9 @@ namespace net::server { picoquic_call_back_event_t fin_or_event, void* callback_ctx, void* v_stream_ctx) { auto server = (Server*)picoquic_get_default_callback_context(picoquic_get_quic_ctx(cnx)); + if (!server) + return -1; + auto peer = (Peer*)callback_ctx; if (fin_or_event != picoquic_callback_almost_ready) { if (peer == nullptr || peer == (void*)server) { @@ -26,9 +32,9 @@ namespace net::server { } Server::Server(const net::exposure& ct, - std::function(Peer*)> onConnect, + std::function onConnect, std::function onDisconnect, - std::function onPacket): + std::function onPacket): Context(ct.max_connections, ct.cert.c_str(), ct.key.c_str(), connection_callback, this), onConnect(onConnect), onDisconnect(onDisconnect), onPacket(onPacket), max_connections(ct.max_connections) { @@ -36,16 +42,27 @@ Server::Server(const net::exposure& ct, openSockets(ct.port, 0); LOG_I("Listening on port " << ct.port); } +#ifdef STANDALONE_SERVER + else + LOG_W("Define max_players config to open connection"); +#endif } Server::~Server() { - for(auto& peer: peers) { - constexpr auto reason = (uint16_t)disconnect_reason::CLOSE; - if (onDisconnect(&peer, true, reason)) - peer.release(reason); - } + close(0); peers.clear(); } +void Server::close(uint16_t reason) { + if (const auto quic = getHandle()) { + picoquic_set_default_callback(quic, connection_callback, nullptr); + for(auto& peer: peers) { + if (onDisconnect(&peer, true, reason)) + peer.release(true); + } + peers.clear(); + } +} + void Server::emitBroadcast(const uint8_t* ptr, size_t size) { for(auto& peer: peers) { peer.emit(ptr, size); @@ -65,7 +82,7 @@ int Server::connectionCallback(Peer* peer, uint64_t stream_id, uint8_t* bytes, s if (stream_ctx == NULL) { if (is_fin) { // Single frame packet if (length > 0) { - onPacket(peer, data::out_view(bytes, length), PacketFlags::TINY); + onPacket(peer, memory::read_view(bytes, length), PacketFlags::TINY); } peer->reset(stream_id); break; @@ -79,7 +96,7 @@ int Server::connectionCallback(Peer* peer, uint64_t stream_id, uint8_t* bytes, s } if (is_fin) { - if (onPacket(peer, data::out_view(stream_ctx->buffer.data.data(), stream_ctx->buffer.data.size()), PacketFlags::NONE)) { + if (onPacket(peer, memory::read_view(stream_ctx->buffer.data.data(), stream_ctx->buffer.data.size()), PacketFlags::NONE)) { peer->close(stream_ctx); } else { return -1; @@ -89,7 +106,7 @@ int Server::connectionCallback(Peer* peer, uint64_t stream_id, uint8_t* bytes, s } case picoquic_callback_datagram: - if(!onPacket(peer, data::out_view(bytes, length), PacketFlags::DATAGRAM)) { + if(!onPacket(peer, memory::read_view(bytes, length), PacketFlags::DATAGRAM)) { return -1; } break; @@ -181,8 +198,8 @@ int Server::connectionCallback(Peer* peer, uint64_t stream_id, uint8_t* bytes, s break; case picoquic_callback_ready: /* TODO: Check that the transport parameters are what the sample expects */ - if (auto reason = onConnect(peer)) { - peer->release(reason.value()); + if (!onConnect(peer)) { + peer->release(true); peers.remove(*peer); return -1; } diff --git a/src/server/net/Server.hpp b/src/server/net/Server.hpp index 0af62a3..937e299 100644 --- a/src/server/net/Server.hpp +++ b/src/server/net/Server.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../../core/net/Context.hpp" +#include "core/net/Context.hpp" #include @@ -21,7 +21,6 @@ public: template constexpr C *getCtx() { - assert(ctx != nullptr); return (C *)ctx; } void *ctx = nullptr; @@ -29,15 +28,15 @@ public: class Server final: Context { public: Server(const exposure& ct, - std::function(Peer*)> onConnect, + std::function onConnect, std::function onDisconnect, - std::function onPacket); + std::function onPacket); ~Server(); /// Send unreliable data to all peers void emitBroadcast(const uint8_t *ptr, size_t size); /// Send reliable data to all peers - void sendBroadcast(const data::out_buffer& buffer, uint8_t queue = 0, size_t queue_size = 0) { + void sendBroadcast(const read_buffer& buffer, uint8_t queue = 0, size_t queue_size = 0) { iterPeers([&](Peer* peer){ peer->send(buffer, queue, queue_size); }); } @@ -47,6 +46,9 @@ public: /// Read-write on sockets and notify callbacks void pull() { Context::pull(1000, 50, 20); } + /// Close server + void close(uint16_t reason); + int connectionCallback(Peer* client, uint64_t stream_id, uint8_t *bytes, size_t length, picoquic_call_back_event_t fin_or_event, void *v_stream_ctx); @@ -72,7 +74,7 @@ private: std::function(Peer *)> onConnect; std::function onDisconnect; - std::function onPacket; + std::function onPacket; uint32_t max_connections; }; diff --git a/src/server/world/Area.cpp b/src/server/world/Area.cpp index 2e11272..b0cda55 100644 --- a/src/server/world/Area.cpp +++ b/src/server/world/Area.cpp @@ -1,25 +1,16 @@ -#include "Area.hpp" - -#include "Chunk.hpp" +#include "./Area.hpp" using namespace world::server; -std::shared_ptr Area::getRegion(const std::string& folderPath, const area_& pos) { +std::shared_ptr Area::getRegion(const std::string& folderPath, const area_region_ref& id) { { // Found const auto shared = regions.lock_shared(); - const auto it = shared->find(pos.second); + const auto it = shared->find(id.region); if(it != shared->end()) return it->second; } // Reading - const auto reg = std::make_shared(folderPath, pos); + const auto reg = std::make_shared(folderPath, id); const auto unique = regions.lock(); - return unique->insert({pos.second, reg}).first->second; + return unique->insert({id.region, reg}).first->second; } - -std::optional Area::GetCurvature(const world::generator::params& params) { - if (auto planet = std::get_if(¶ms)) { - return planet->height * (1.1 + planet->surface_roughness); - } - return {}; -} \ No newline at end of file diff --git a/src/server/world/Area.hpp b/src/server/world/Area.hpp index f07fd70..40be66e 100644 --- a/src/server/world/Area.hpp +++ b/src/server/world/Area.hpp @@ -1,41 +1,42 @@ #pragma once -#include "../../core/world/Area.hpp" -#include -using namespace libguarded; -#include "region/index.hpp" -#include "generator.hpp" +#include "core/world/Area.hpp" +#include "core/utils/mutex.hpp" +#include "./Region.hpp" +#include "core/world/Registry.hpp" +#include "core/world/generator/Abstract.hpp" namespace world::server { + /// Area (aka big group of server::Chunk) struct Area final: public world::Area { public: using regions_t = robin_hood::unordered_map>; struct params { - voxel_pos center; int radius; - generator::params properties; + size_t genId; + std::vector params; }; - Area(const params& p): world::Area(p.center, p.radius), generator(generator::load(p.properties)), generator_properties(p.properties) { } + Area(const params& p): world::Area(p.radius), generator(world::module::Registry::Get()->createGenerator(p.genId, p.params.data())), copy(p) { } - std::shared_ptr getRegion(const std::string& folderPath, const area_ &); - shared_guarded::handle getRegions() { return regions.lock(); } + std::shared_ptr getRegion(const std::string& folderPath, const area_region_ref &); + mutex::shared_guard::handle getRegions() { return regions.lock(); } + mutex::shared_guard::shared_handle getRegions() const { return regions.lock_shared(); } - inline const std::unique_ptr &getGenerator() { return generator; } + inline const std::unique_ptr& getGenerator() const { return generator; } - inline params getParams() const { return params{getOffset().as_voxel(), getChunks().getRadius(), generator_properties}; } + inline params getParams() const { return copy; } - std::optional getCurvature() const override { return GetCurvature(generator_properties); } - static std::optional GetCurvature(const generator::params &); + std::optional getCurvature() const override { return generator->getCurvature(); } inline const glm::vec3 getGravity(const voxel_pos& p) const { return generator->getGravity(p); } private: - shared_guarded regions; + mutex::shared_guard regions; std::unique_ptr generator; - const generator::params generator_properties; //MAYBE: useless copy + const params copy; }; } \ No newline at end of file diff --git a/src/server/world/Chunk.cpp b/src/server/world/Chunk.cpp index a4f49f6..dea1a84 100644 --- a/src/server/world/Chunk.cpp +++ b/src/server/world/Chunk.cpp @@ -1,16 +1,17 @@ #include "Chunk.hpp" #include -#include "../../core/data/math.hpp" +#include "core/geometry/math.hpp" #include +#include using namespace world::server; -Chunk::Chunk(const chunk_pos& pos, const std::unique_ptr& rnd): world::Chunk() { +Chunk::Chunk(owner edits): world::EdittableChunk(edits) { } +Chunk::Chunk(owner edits, const chunk_pos &pos, const std::unique_ptr &rnd): world::EdittableChunk(edits) { rnd->generate(pos, voxels); } -#include -Chunk::Chunk(std::istream& str, bool rle): world::Chunk(str, rle) { } +Chunk::Chunk(owner edits, std::istream& str, bool rle): world::EdittableChunk(edits, str, rle) { } Chunk::~Chunk() { } world::Voxel Chunk::write(std::ostream& str, bool rle) const { diff --git a/src/server/world/Chunk.hpp b/src/server/world/Chunk.hpp index 123427f..a11d184 100644 --- a/src/server/world/Chunk.hpp +++ b/src/server/world/Chunk.hpp @@ -1,17 +1,19 @@ #pragma once -#include "../../core/world/Chunk.hpp" +#include "core/world/EdittableChunk.hpp" #include -#include "generator.hpp" +#include "core/world/generator/Abstract.hpp" namespace world::server { - /// Server size chunk - class Chunk: public virtual world::Chunk { + using fill_edits = robin_hood::unordered_map>; + + /// Server side chunk + class Chunk: public world::EdittableChunk { public: - Chunk(): world::Chunk() { } - Chunk(const chunk_pos &pos, const std::unique_ptr &rnd); - Chunk(std::istream& str, bool rle = RLE); + Chunk(owner); + Chunk(owner, const chunk_pos &pos, const std::unique_ptr &rnd); + Chunk(owner, std::istream& str, bool rle = RLE); virtual ~Chunk(); /// Set voxel from index @@ -32,4 +34,20 @@ namespace world::server { /// Modified by player bool modified = false; }; + + struct ChunkFactory { + virtual std::shared_ptr create(const chunk_pos &pos, const std::unique_ptr &rnd) const = 0; + virtual std::shared_ptr create(std::istream &str) const = 0; + virtual std::shared_ptr create() const = 0; + }; + + /// Build for server only chunk + /// Not really edittable (get/set)Edits will fail + struct StandaloneChunkFactory final: public ChunkFactory { + std::shared_ptr create(const chunk_pos &pos, const std::unique_ptr &rnd) const override { + return std::make_shared(nullptr, pos, rnd); + } + std::shared_ptr create(std::istream &str) const override { return std::make_shared(nullptr, str); } + std::shared_ptr create() const override { return std::make_shared(nullptr); } + }; } diff --git a/src/server/world/Elements.cpp b/src/server/world/Elements.cpp new file mode 100644 index 0000000..6a784e0 --- /dev/null +++ b/src/server/world/Elements.cpp @@ -0,0 +1,156 @@ +#include "./Elements.hpp" +#include "core/utils/io.hpp" + +using namespace world::server; + +#undef LOG_PREFIX +#define LOG_PREFIX "Server: " + +Elements::Elements(): EdittableElements(true) { } + +void Elements::readIndex(io::ifstream& index) { + size_t count = 0; + index.read_cast(count); + nodes.reserve(count); + hierarchy.reserve(count); + for (size_t i = 0; i < count && !index.eof(); i++) { + generational::id::idx_t idx; + index.read_cast(idx); + uint8_t flag; + index.read_cast(flag); + const node_id id = generational::id(idx, 0, flag); + const Type type = GetType(id); + const auto readStartPoint = [&] { + const auto fixed = Has(id); + start_point start(generational::id(), transform(), FIXED); + if (!fixed) { + generational::id::idx_t parent; + index.read_cast(parent); + start.parent = generational::id(parent, 0); + } + index.read_cast(start.relative); + if (!fixed) { + index.read_cast(start.vel); + } + return start; + }; + switch(type) { + case Type::Instance: { + const auto start = readStartPoint(); + generational::id::idx_t model; + index.read_cast(model); + createInstanceAt(idx, flag, start, generational::id(model)); + break; + } + case Type::Part: { + const auto start = readStartPoint(); + glm::usvec3 size; + index.read_cast(size); + createPartAt(idx, flag, start, size); + break; + } + case Type::Area: {; + const auto start = readStartPoint(); + world::server::Area::params ps{}; + index.read_cast(ps.radius); + index.read_cast(ps.genId); + { + size_t len; + index.read_cast(len); + ps.params.resize(len); + index.read(ps.params.data(), ps.params.size()); + } + createAreaAt(idx, flag, start, ps); + break; + } + default: + LOG_W("Load bad node type " << (int)type); + break; + } + } + LOG_T(nodes.size() << " nodes loaded"); +} +void Elements::writeIndex(io::ofstream& index) const { + index.write_cast(nodes.size()); + nodes.iter([&](const node_id& id, const Node& node) { + index.write_cast(id.val.index()); + const uint8_t flag = id.val.flag(); + index.write_cast(flag); + const Type type = GetType(id); + const auto writeStartPoint = [&] { + const auto fixed = Has(id); + if (fixed) { + index.write_cast(node.absolute); + } else { + const auto it = hierarchy.get(id); + assert(it != nullptr); + index.write_cast(it->parent.val.index()); + index.write_cast(it->relative); + index.write_cast(it->vel); + } + }; + switch(type) { + case Type::Instance: { + writeStartPoint(); + index.write_cast(NodeOf::Make(&node)->get()->type.val.index()); + break; + } + case Type::Part: { + writeStartPoint(); + index.write_cast(NodeOf::Make(&node)->get()->size); + break; + } + case Type::Area: { + writeStartPoint(); + const auto& ps = NodeOf::Make(&node)->get()->getParams(); + index.write_cast(ps.radius); + index.write_cast(ps.genId); + index.write_cast(ps.params.size()); + index.write(ps.params.data(), ps.params.size()); + break; + } + default: + LOG_W("Write bad node type " << (int)type); + break; + } + }); +} + +world::instance_id Elements::createInstance(const start_point& sp, model_id m, uint8_t flag) { + flag = Set(flag | static_cast(Type::Instance)); + auto& model = models.at(m); + const auto id = create(sp, flag, model.origin ? model.origin : std::make_shared(m)); + model.instances.push_back(id.val.index()); + return id.val; +} +world::instance_id Elements::createInstanceAt(generational::id::idx_t i, uint8_t flag, const start_point& sp, model_id m) { + assert(Is(generational::id(i, 0, flag))); + auto& model = models.at(m); + const auto id = createAt(i, sp, flag, model.origin ? model.origin : std::make_shared(m)); + model.instances.push_back(id.val.index()); + return id.val; +} +world::part_id Elements::createPart(const start_point& sp, glm::usvec3 size) { + constexpr auto FLAG = Set(static_cast(Type::Part)); + const auto id = create(sp, FLAG, std::make_shared(size)); + parts.push_back(id.val.index()); + return id.val; +} +world::part_id Elements::createPartAt(generational::id::idx_t i, uint8_t flag, const start_point& sp, glm::usvec3 size) { + assert(Is(generational::id(i, 0, flag))); + const auto id = createAt(i, sp, flag, std::make_shared(size)); + parts.push_back(id.val.index()); + return id.val; +} +world::area_id Elements::createArea(const start_point& sp, const Area::params& ps) { + constexpr auto FLAG = Set(static_cast(Type::Area)); + const auto id = create(sp, FLAG, std::make_shared(ps)); + areas.push_back(id.val.index()); + return id.val; +} +world::area_id Elements::createAreaAt(generational::id::idx_t i, uint8_t flag, const start_point& sp, const world::server::Area::params& ps) { + assert(Is(generational::id(i, 0, flag))); + const auto id = createAt(i, sp, flag, std::make_shared(ps)); + areas.push_back(id.val.index()); + return id.val; +} \ No newline at end of file diff --git a/src/server/world/Elements.hpp b/src/server/world/Elements.hpp new file mode 100644 index 0000000..635f550 --- /dev/null +++ b/src/server/world/Elements.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "./Area.hpp" +#include "core/world/Elements.hpp" +#include + +namespace io { class ifstream; class ofstream; } +namespace world::server { + +/// Handle elements pseudo ECS +class Elements final: public world::EdittableElements { +public: + Elements(); + + void readIndex(io::ifstream&); + void writeIndex(io::ofstream&) const; + + area_id createArea(const start_point&, const Area::params& params); + part_id createPart(const start_point&, glm::usvec3 size); + instance_id createInstance(const start_point&, model_id model, uint8_t flag = 0); + + NodeOf* findArea(area_id i) { + assert(Is(i.val)); + return NodeOf::Make(findNode(i.val)); + } + const NodeOf* findArea(area_id i) const { + assert(Is(i.val)); + return NodeOf::Make(findNode(i.val)); + } + +private: + area_id createAreaAt(generational::id::idx_t, uint8_t flag, const start_point&, const Area::params& params); + part_id createPartAt(generational::id::idx_t, uint8_t flag, const start_point&, glm::usvec3 size); + instance_id createInstanceAt(generational::id::idx_t i, uint8_t flag, const start_point&, model_id model); +}; +} diff --git a/src/server/world/Noise.hpp b/src/server/world/Noise.hpp deleted file mode 100644 index 6d48d44..0000000 --- a/src/server/world/Noise.hpp +++ /dev/null @@ -1,126 +0,0 @@ -#pragma once - -#if HASTY -#include -#else -#include -#endif -#include -#include "../../core/world/position.h" - -namespace world { - /// Noise handler - class Noise { - public: -#if HASTY - using handle = HastyNoise::NoiseSIMD; - using set = std::unique_ptr; -#else - using handle = FastNoiseSIMD; - struct fast_set_deleter { - constexpr fast_set_deleter() noexcept = default; - - void operator()(float* set) const { - FastNoiseSIMD::FreeNoiseSet(set); - } - }; - using set = std::unique_ptr; -#endif - - struct Base { - int seed = 1337; - float frequency = .01f; - }; - struct Simplex: Base { - Simplex(int _seed, float _frequency) { - seed = _seed; - frequency = _frequency; - } - }; - struct SimplexFractal: Simplex { - SimplexFractal(int seed, float frequency = .01f, int octaves = 3, float lacunarity = 2.0f, float gain = .5f): - Simplex(seed, frequency), octaves(octaves), lacunarity(lacunarity), gain(gain) { } - int octaves; - float lacunarity; - float gain; - //FractalType m_fractalType = FBM; - }; - struct Cellular: Base { - Cellular(int _seed, float _frequency) { - seed = _seed; - frequency = _frequency; - } - //MAYBE: add options - }; - - Noise(Simplex s): Noise(s, type::Simplex) { } - Noise(SimplexFractal sf): Noise(sf, type::SimplexFractal) { - noise->SetFractalGain(sf.gain); - noise->SetFractalLacunarity(sf.lacunarity); - noise->SetFractalOctaves(sf.octaves); - //noise->SetFractalType(); - } - Noise(Cellular c): Noise(c, type::Cellular) { -#if HASTY - noise->SetCellularReturnType(HastyNoise::CellularReturnType::Value); - noise->SetCellularDistanceFunction(HastyNoise::CellularDistance::Natural); -#else - noise->SetCellularReturnType(FastNoiseSIMD::CellValue); - noise->SetCellularDistanceFunction(FastNoiseSIMD::Natural); -#endif - } - - /// Get block of given size with index pos. - inline set Get(const chunk_pos& pos, int size) { -#if HASTY - return noise->GetNoiseSet(pos.x * size, pos.y * size, pos.z * size, size, size, size); -#else - return std::unique_ptr(noise->GetNoiseSet(pos.x * size, pos.y * size, pos.z * size, size, size, size), fast_set_deleter()); -#endif - } - - private: - enum class type { - SimplexFractal, Simplex, Cellular - }; - -#if HASTY - static HastyNoise::NoiseType ConvertType(type t) { - switch(t) { - case type::Simplex: - return HastyNoise::NoiseType::Simplex; - case type::SimplexFractal: - return HastyNoise::NoiseType::SimplexFractal; - case type::Cellular: - return HastyNoise::NoiseType::Cellular; - } - return HastyNoise::NoiseType::Simplex; - } -#else - static FastNoiseSIMD::NoiseType ConvertType(type t) { - switch(t) { - case type::Simplex: - return FastNoiseSIMD::NoiseType::Simplex; - case type::SimplexFractal: - return FastNoiseSIMD::NoiseType::SimplexFractal; - case type::Cellular: - return FastNoiseSIMD::NoiseType::Cellular; - } - return FastNoiseSIMD::NoiseType::Simplex; - } -#endif - - Noise(Base p, type t) { -#if HASTY - const size_t fastestSimd = HastyNoise::GetFastestSIMD(); - noise = HastyNoise::CreateNoise(p.seed, fastestSimd); -#else - noise = std::unique_ptr(FastNoiseSIMD::NewFastNoiseSIMD(p.seed)); -#endif - noise->SetNoiseType(ConvertType(t)); - noise->SetFrequency(p.frequency); - } - - std::unique_ptr noise; - }; -} diff --git a/src/server/world/Region.cpp b/src/server/world/Region.cpp new file mode 100644 index 0000000..5f17661 --- /dev/null +++ b/src/server/world/Region.cpp @@ -0,0 +1,264 @@ +#include "./Region.hpp" + +#include +#include + +#undef LOG_PREFIX +#define LOG_PREFIX "Server: " + +using namespace world::server; + +#define REMOVE_CORRUPTED 1 +#define LAZYNESS 8 + +Region::Region(const std::string &folderPath, const area_region_ref &id) { + path = folderPath + '/' + std::to_string(id.area.val) + '.' + std::to_string(id.region.x) + '.' + + std::to_string(id.region.y) + '.' + std::to_string(id.region.z) + ".map"; + + load(); +} +Region::~Region() { + if(!content.empty()) + save(changed); +} + +size_t Region::Read(const std::string &path, const std::function &, std::vector &&)>& out) { + std::ifstream file; + file.open(path, std::ios::in | std::ios::binary); + if(!file.good()) { + return 0; + } + + // Read header + uint16_t chunkCount; //NOTE: pretty useless + file.read(reinterpret_cast(&chunkCount), sizeof(chunkCount)); + + while (!file.eof()) { + // Read pos + region_chunk_pos pos; + file.read(reinterpret_cast(&pos.x), sizeof(region_chunk_pos::value_type)); + file.read(reinterpret_cast(&pos.y), sizeof(region_chunk_pos::value_type)); + file.read(reinterpret_cast(&pos.z), sizeof(region_chunk_pos::value_type)); + + // Read average if present + std::optional avg; + Flags flags = Flags::ZERO; + file.read(reinterpret_cast(&flags), 1); + if (flags & Flags::HAS_AVERAGE) { + Voxel v; + file.read(reinterpret_cast(&v), sizeof(v)); + avg = v; + } + + // Read size + uint16_t size = 0; + if (!(flags & Flags::EMPTY)) { + file.read(reinterpret_cast(&size), sizeof(size)); + } + + // Read content + std::vector data; + if (size > 0) { + data.resize(size); + file.read(data.data(), data.size()); + } + out(pos, avg, std::move(data)); + file.peek(); + } + + if(file.bad()) { + LOG_E("Region corrupted read " << path); + } + file.close(); + return chunkCount; +} + +void Region::Read(const std::string& folder, const part_ref& id, const zstd::read_ctx &ctx, const std::function&)>& out) { + const auto path = folder + '/' + std::to_string(id.val) + ".map"; + std::vector buffer; + Read(path, [&](const region_chunk_pos &pos, const std::optional &, const std::vector &data) { + if(auto err = ctx.decompress(data, buffer)) { + LOG_E("Corrupted region chunk: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z << " " + << err.value()); + } else { + out(pos, buffer); + } + }); +} +void Region::Write(const std::string& folder, const part_ref& id, const zstd::write_ctx &ctx, size_t size, const std::function()>& next) { + const auto path = folder + '/' + std::to_string(id.val) + ".map"; + + std::ofstream file(path, std::ios::out | std::ios::binary); + if (!file.good()) { + LOG_E("Corrupted region path: " << path); + return; + } + + { // Write header + uint16_t size = (uint16_t)size; + file.write(reinterpret_cast(&size), sizeof(size)); + } + + std::vector buffer; + for (size_t i = 0; i < size; i++) { + const auto out = next(); + if (out.second.empty()) { + buffer.clear(); + } else { + if (auto err = ctx.compress(out.second, buffer)) { + LOG_E("Corrupted chunk save: " << path << ":" << (int)out.first.x << "." << (int)out.first.y << "." << (int)out.first.z << " " + << err.value()); + continue; + } + } + + { // Write pos + region_chunk_pos pos = out.first; + file.write(reinterpret_cast(&pos.x), sizeof(region_chunk_pos::value_type)); + file.write(reinterpret_cast(&pos.y), sizeof(region_chunk_pos::value_type)); + file.write(reinterpret_cast(&pos.z), sizeof(region_chunk_pos::value_type)); + } + + // Write flags + Flags flags = Flags::ZERO; + if (buffer.empty()) + flags = (Flags)(flags | Flags::EMPTY); + file.write(reinterpret_cast(&flags), sizeof(flags)); + + if (!(flags & Flags::EMPTY)) { + assert(buffer.size() < USHRT_MAX); + auto size = (uint16_t)buffer.size(); + + // Write size + file.write(reinterpret_cast(&size), sizeof(size)); + + // Write content + file.write(buffer.data(), size); + } + } + + if (!file.good()) { + LOG_E("Region corrupted write " << path); + } + file.close(); +} + +void Region::load() { + std::unique_lock lock(mutex); + [[maybe_unused]] + const auto chunkCount = Read(path, [&](const region_chunk_pos &pos, const std::optional &avg, std::vector &&data) { + if(!content.emplace(pos, node(avg, std::move(data))).second) { + LOG_E("Duplicated chunk: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z); + } + }); + if (content.size() != chunkCount) + LOG_E("Probably lost some chunks " << content.size() << "/" << chunkCount); +} +bool Region::read(const region_chunk_pos& pos, const zstd::read_ctx& ctx, std::vector& out) { + std::shared_lock lock(mutex); + + const auto it = content.find(pos); + if (it == content.end() || it->second.data.empty()) + return false; + + if(auto err = ctx.decompress(it->second.data, out)) { + LOG_E("Corrupted region chunk: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z << " " + << err.value()); +#ifdef REMOVE_CORRUPTED + LOG_W("Removing"); + lock.unlock(); + { + std::unique_lock ulock(mutex); + content.erase(it); + } + save(true); +#endif + return false; + } + return true; +} +void Region::write(const region_chunk_pos& pos, const zstd::write_ctx& ctx, const std::string_view& in, const std::optional& avg) { + std::vector buffer; + if (!in.empty()) { + if (auto err = ctx.compress(in, buffer)) { + LOG_E("Corrupted chunk save: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z << " " + << err.value()); + return; + } + } + { + std::unique_lock lock(mutex); + + // Save buffer + const auto it = content.find(pos); + if (it != content.end()) { + it->second.average = avg; + it->second.data = std::move(buffer); + } else { + content.emplace(pos, node(avg, std::move(buffer))); + } + changed = true; + } + save(false); +} + +void Region::save(bool force) { + if(!force && rand() % LAZYNESS == 0) + return; + + std::unique_lock lock(mutex); + + std::ofstream file(path, std::ios::out | std::ios::binary); + if (!file.good()) { + LOG_E("Corrupted region path: " << path); + return; + } + + { // Write header + uint16_t size = (uint16_t)content.size(); + file.write(reinterpret_cast(&size), sizeof(size)); + } + + for(const auto& chunk: content) { + { // Write pos + region_chunk_pos pos = chunk.first; + file.write(reinterpret_cast(&pos.x), sizeof(region_chunk_pos::value_type)); + file.write(reinterpret_cast(&pos.y), sizeof(region_chunk_pos::value_type)); + file.write(reinterpret_cast(&pos.z), sizeof(region_chunk_pos::value_type)); + } + + // Write average if present + Flags flags = Flags::ZERO; + if (chunk.second.average.has_value()) + flags = (Flags)(flags | Flags::HAS_AVERAGE); + if (chunk.second.data.empty()) + flags = (Flags)(flags | Flags::EMPTY); + file.write(reinterpret_cast(&flags), sizeof(flags)); + if (flags & Flags::HAS_AVERAGE) { + Voxel v = chunk.second.average.value(); + file.write(reinterpret_cast(&v), sizeof(v)); + } + + if (!(flags & Flags::EMPTY)) { + assert(chunk.second.data.size() < USHRT_MAX); + auto size = (uint16_t)chunk.second.data.size(); + const auto out = chunk.second.data.data(); + + // Write size + file.write(reinterpret_cast(&size), sizeof(size)); + + // Write content + file.write(out, size); + } + } + + file.sync_with_stdio(true); + file.flush(); + if (!file.good()) { + LOG_E("Region corrupted write " << path); + file.close(); + return; + } + file.close(); + changed = false; +} \ No newline at end of file diff --git a/src/server/world/region/Memory.hpp b/src/server/world/Region.hpp similarity index 53% rename from src/server/world/region/Memory.hpp rename to src/server/world/Region.hpp index 96c0d30..7da06de 100644 --- a/src/server/world/region/Memory.hpp +++ b/src/server/world/Region.hpp @@ -2,37 +2,39 @@ #include #include -#include -#include "../../../core/world/forward.h" -#include "../../../core/world/Voxel.hpp" -#include "../../../core/data/mem.hpp" -#include "../../../core/utils/zctx.hpp" -#include "../../../core/data/math.hpp" +#include "core/world/Area.hpp" +#include "core/world/Voxel.hpp" +#include "core/memory.hpp" +#include "core/utils/zctx.hpp" namespace world::server { ///Group of chunks saved as a single file in memory - class MemoryRegion { + class Region { public: - MemoryRegion(const std::string& folderPath, const area_ &pos); - ~MemoryRegion(); + Region(const std::string& folderPath, const area_region_ref &id); + ~Region(); template void averages(D &out) { std::shared_lock lock(mutex); out.resize(content.size() * (sizeof(region_chunk_pos) + sizeof(Voxel))); - data::in_view in((uint8_t*)out.data(), out.size()); + memory::write_view in((uint8_t*)out.data(), out.size() * sizeof(D)); for(const auto& r: content) { if (r.second.average.has_value()) { - in.write((const uint8_t*)&r.first, sizeof(r.first)); - in.write((const uint8_t*)&r.second.average.value(), sizeof(r.second.average.value())); + in.write(r.first); + in.write(r.second.average.value()); } } - out.resize(in.cur); + out.resize(in.current()); } bool read(const region_chunk_pos &pos, const zstd::read_ctx &ctx, std::vector &out); void write(const region_chunk_pos &pos, const zstd::write_ctx& ctx, const std::string_view &in, const std::optional& average); + static void Read(const std::string& folder, const part_ref& id, const zstd::read_ctx &ctx, const std::function&)>& out); + static void Write(const std::string& folder, const part_ref& id, const zstd::write_ctx &ctx, size_t size, const std::function()>& next); + private: + static size_t Read(const std::string &path, const std::function &, std::vector &&)> &out); void save(bool force = true); std::string path; @@ -46,10 +48,10 @@ namespace world::server { std::shared_mutex mutex; struct node { - node(const std::optional& a, std::unique_ptr> d): + node(const std::optional& a, std::vector&& d): average(a), data(std::move(d)) { } std::optional average; - std::unique_ptr> data; + std::vector data; }; robin_hood::unordered_map content; bool changed = false; diff --git a/src/server/world/Serializer.cpp b/src/server/world/Serializer.cpp new file mode 100644 index 0000000..30e09ee --- /dev/null +++ b/src/server/world/Serializer.cpp @@ -0,0 +1,73 @@ +#include "./Serializer.hpp" +#include "./Universe.hpp" +#include "./Chunk.hpp" + +using namespace world::server; + +constexpr auto PREDICTABLE = true; +constexpr auto COMPRESSION_PREFIX = net::server_packet_type::COMPRESSION; +constexpr auto CHUNK_PREFIX = net::server_packet_type::CHUNK; + +Serializer::Serializer(const std::string& folderPath): dict_content({folderPath + "/zstd.dict", "content/zstd.dict"}, memory::read_view::Of(COMPRESSION_PREFIX)), + dicts(dict_content.content()), dict_write_ctx(dicts.make_writer()) { } + +Serializer::out Serializer::writeRegion(const area_region_ref &id, const std::shared_ptr &r) const { + auto packet = memory::write_buffer(net::server_packet_type::REGION, sizeof(id)); + packet.write(id); + packet.pushPart([&](memory::write_buffer::part part) { + r->averages(part); }); + return packet.finish(); +} +Serializer::out Serializer::writeChunk(const node_chunk_ref& id, const std::shared_ptr& ck) const { + std::ostringstream out; + ck->write(out); + auto packet = memory::write_buffer(net::server_packet_type::CHUNK, sizeof(id)); + packet.write(id); + packet.pushPart([&](memory::write_buffer::part part){ + dict_write_ctx.compress(out.str(), part); }); + return packet.finish(); +} +Serializer::out Serializer::writeEdits(node_ref id, const fill_edits& edits) const { + size_t size = sizeof(area_id); + for(const auto& part: edits) { + size += sizeof(chunk_pos); + size += sizeof(chunk_voxel_idx); + size += sizeof(ChunkEdits::Edit) * part.second.size(); + } + auto packet = memory::write_buffer(net::server_packet_type::RAW_EDITS, size); + packet.write(id); + for(const auto& part: edits) { + packet.write(part.first); + packet.write(part.second.size()); + packet.write(part.second.data(), part.second.size() * sizeof(ChunkEdits::Edit)); + } + return packet.finish(); +} +Serializer::out Serializer::writeChunkReset() const { + return out(memory::read_view::Of(CHUNK_PREFIX), nullptr); +} + +Serializer::out Serializer::writeCapabilities(uint16_t loadDist, size_t flood) const { + auto packet = memory::write_buffer(net::server_packet_type::CAPABILITIES, sizeof(loadDist) + sizeof(bool) + sizeof(flood) * PREDICTABLE); + packet.write(loadDist); + packet.write(PREDICTABLE); + if constexpr (PREDICTABLE) { + packet.write(flood); + } + return packet.finish(); +} + +Serializer::out Serializer::writeDict() const { + return out(memory::read_view((uint8_t*)dict_content.data(), dict_content.size()), nullptr); +} + +Serializer::out Serializer::writeTeleport(node_id id, const relative_transform& tf) const { + auto packet = memory::write_buffer(net::server_packet_type::TELEPORT, sizeof(node_id) + sizeof(tf)); + packet.write(id); + packet.write(tf); + return packet.finish(); +} + +Serializer::out Serializer::writerMessage(const std::string& msg) const { + return out::Of(net::server_packet_type::MESSAGE, msg.data(), msg.size()); +} \ No newline at end of file diff --git a/src/server/world/Serializer.hpp b/src/server/world/Serializer.hpp new file mode 100644 index 0000000..a06037f --- /dev/null +++ b/src/server/world/Serializer.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include "core/utils/zctx.hpp" +#include "core/utils/io.hpp" +#include "core/net/protocol.hpp" +#include "./Chunk.hpp" +#include "./Area.hpp" + +namespace world::server { +class Region; + +/// Handle compression and packets io +class Serializer { +public: + Serializer(const std::string &folderPath); + + const zstd::dict_set &Dicts() const { return dicts; } + + using out = memory::read_buffer; + + out writeCapabilities(uint16_t loadDist, size_t flood) const; + + out writeDict() const; + out writeRegion(const area_region_ref &, const std::shared_ptr &) const; + out writeChunk(const node_chunk_ref &, const std::shared_ptr &) const; + out writeChunkReset() const; + out writeEdits(node_ref, const fill_edits &) const; + + out writeTeleport(node_id, const relative_transform &) const; + out writerMessage(const std::string &) const; + +private: + io::file_content dict_content; + zstd::dict_set dicts; + zstd::write_ctx dict_write_ctx; +}; + +} \ No newline at end of file diff --git a/src/server/world/SharedChunk.hpp b/src/server/world/SharedChunk.hpp new file mode 100644 index 0000000..91a0e42 --- /dev/null +++ b/src/server/world/SharedChunk.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "./Chunk.hpp" +#include "core/world/EdittableChunk.hpp" + +namespace world::server { + // Server and Client merged chunk + class SharedChunk final: public Chunk { + public: + SharedChunk(): Chunk(new world::ChunkEdits(this)) { } + SharedChunk(const chunk_pos &pos, const std::unique_ptr &rnd): Chunk(new world::ChunkEdits(this), pos, rnd) { } + SharedChunk(std::istream &str, bool rle = RLE): Chunk(new world::ChunkEdits(this), str, rle) { } + + /// Break voxel + std::optional replace(chunk_voxel_idx idx, const Voxel &val, float delay = 0) override { + const auto res = voxels[idx]; + set(idx, val); + if (res.value != voxels[idx].value) { + setEdits()->add(world::ChunkEdits::Edit{res, delay, idx}); + } + return {Item{res.density(), res.material()}}; + } + }; + + /// Build really EdittableChunk for internal client + struct SharedChunkFactory final: public ChunkFactory { + std::shared_ptr create(const chunk_pos &pos, const std::unique_ptr &rnd) const override { + return std::make_shared(pos, rnd); + } + std::shared_ptr create(std::istream &str) const override { return std::make_shared(str); } + std::shared_ptr create() const override { return std::make_shared(); } + }; +} \ No newline at end of file diff --git a/src/server/world/SharedParts.hpp b/src/server/world/SharedParts.hpp deleted file mode 100644 index da5edb6..0000000 --- a/src/server/world/SharedParts.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "Chunk.hpp" -#include "Area.hpp" -#include "../../core/world/EdittableChunk.hpp" - -namespace world::server { - -// Server and Client merged chunk -class SharedChunk final: public Chunk, public world::client::EdittableChunk { -public: - SharedChunk(): world::Chunk(), Chunk(), world::client::EdittableChunk() { } - SharedChunk(const chunk_pos &pos, const std::unique_ptr &rnd): world::Chunk(), Chunk(pos, rnd), world::client::EdittableChunk() { } - SharedChunk(std::istream &str, bool rle = RLE): world::Chunk(str, rle), Chunk(), world::client::EdittableChunk() { } - - /// Break voxel - std::optional replace(chunk_voxel_idx idx, const Voxel &val, float delay = 0) override { - const auto res = voxels[idx]; - set(idx, val); - edits.erase(idx); - if(delay > 0) { - edits.emplace(idx, EditBody{res, delay}); - } else { - invalidate(idx); - } - return {Item{res.density(), res.material()}}; - } -}; -} \ No newline at end of file diff --git a/src/server/world/SharedUniverse.cpp b/src/server/world/SharedUniverse.cpp deleted file mode 100644 index 60ed7e3..0000000 --- a/src/server/world/SharedUniverse.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "SharedUniverse.hpp" -#include "SharedParts.hpp" - -using namespace world::server; - -SharedUniverse::SharedUniverse(const options &o, server_handle *const localHandle): Universe(o), localHandle(localHandle) { - // Local player - [[maybe_unused]] - const auto id = entities.at(PLAYER_ENTITY_ID).instances.emplace(Entity::Instance{spawnPoint, glm::vec3(0)}); - assert(id == PLAYER_ENTITY_ID); - localHandle->teleport = spawnPoint; - movedPlayers.insert(id); - - localHandle->areas = (world::client::area_map*)(&areas); //WONT FIX: templated area - localHandle->entities = &entities; - localHandle->emit = std::function([&](const world::action::packet &packet) { - if(const auto fill = std::get_if(&packet)) { - //NOTE: LocalUniverse had already check for entities - this->set(fill->pos, fill->radius, fill->shape, fill->val); - } else if(const auto message = std::get_if(&packet)) { - this->broadcastMessage("Player" + std::to_string(id.index) + ": " + message->text); - } else if(const auto move = std::get_if(&packet)) { - if(!movePlayer(PLAYER_ENTITY_ID, move->pos)) { - LOG_W("Bad move of player " << PLAYER_ENTITY_ID.index); - } - } else { - LOG_W("Bad packet"); - } - }); - localHandle->raycast = std::function([&](const geometry::Ray& ray){ - return this->raycast(ray); }); - localHandle->running = true; - localHandle->entityChange = true; -} -SharedUniverse::~SharedUniverse() { - LOG_D("Breaking shared universe"); - saveAll(true); //NOTE: save thread requires some virtual function calls -} - -void SharedUniverse::loadChunk(area_ area, chunk_pos diff, const world::ChunkContainer& chunks) { - if(localHandle->onUpdate) - localHandle->onUpdate(area, diff, chunks, Faces::All); -} -void SharedUniverse::updateChunk(area_map::iterator &it, world::ChunkContainer::iterator &it_c, chunk_pos diff, float deltaTime) { - if (const auto neighbors = std::dynamic_pointer_cast(it_c->second)->update(deltaTime, true)) { - localHandle->onUpdate(std::make_pair(it->first, it_c->first), diff, it->second->getChunks(), neighbors.value()); - } -} - -void SharedUniverse::broadcastMessage(const std::string& text) { - localHandle->message = text; - Universe::broadcastMessage(text); -} -void SharedUniverse::broadcastEntities() { - localHandle->entityChange = true; - Universe::broadcastEntities(); -} - -std::shared_ptr SharedUniverse::createChunk(const chunk_pos &pos, const std::unique_ptr &rnd) const { - return std::make_shared(pos, rnd); -} -std::shared_ptr SharedUniverse::createChunk(std::istream &str) const { - return std::make_shared(str); -} -std::shared_ptr SharedUniverse::createChunk() const { - return std::make_shared(); -} \ No newline at end of file diff --git a/src/server/world/SharedUniverse.hpp b/src/server/world/SharedUniverse.hpp deleted file mode 100644 index 05c1204..0000000 --- a/src/server/world/SharedUniverse.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "Universe.hpp" -#include "../../core/server_handle.hpp" - -namespace world::server { - class SharedArea; - - /// Server with data for LocalClientUniverse binding - class SharedUniverse final: public Universe { - public: - SharedUniverse(const options &, server_handle *const); - virtual ~SharedUniverse(); - - protected: - std::shared_ptr createChunk(const chunk_pos &pos, const std::unique_ptr &rnd) const override; - std::shared_ptr createChunk(std::istream &str) const override; - std::shared_ptr createChunk() const override; - - void loadChunk(area_, chunk_pos, const world::ChunkContainer &) override; - void updateChunk(area_map::iterator &, world::ChunkContainer::iterator &, chunk_pos, float deltaTime) override; - - void broadcastMessage(const std::string&) override; - void broadcastEntities() override; - - private: - server_handle *const localHandle; - }; -} \ No newline at end of file diff --git a/src/server/world/StandaloneUniverse.cpp b/src/server/world/StandaloneUniverse.cpp deleted file mode 100644 index 765cac4..0000000 --- a/src/server/world/StandaloneUniverse.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "StandaloneUniverse.hpp" -#include "Chunk.hpp" - -using namespace world::server; - -StandaloneUniverse::StandaloneUniverse(const options &o): Universe(o) { } \ No newline at end of file diff --git a/src/server/world/StandaloneUniverse.hpp b/src/server/world/StandaloneUniverse.hpp deleted file mode 100644 index 10058fb..0000000 --- a/src/server/world/StandaloneUniverse.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "Universe.hpp" - -namespace world::server { - /// Logic only server::Universe - class StandaloneUniverse final: public Universe { - public: - StandaloneUniverse(const options &); - }; -} \ No newline at end of file diff --git a/src/server/world/Universe.cpp b/src/server/world/Universe.cpp index db23737..539104a 100644 --- a/src/server/world/Universe.cpp +++ b/src/server/world/Universe.cpp @@ -1,127 +1,134 @@ -#include "Universe.hpp" +#include "./Universe.hpp" -#include //NOLINT -#include // NOLINT +#include +#include #include #include +#include "./Serializer.hpp" +#include "./SharedChunk.hpp" +#include "modules/core/PlanetGenerator.hpp" +#include "core/world/iterators.hpp" -#include "Chunk.hpp" -#include "../../core/world/raycast.hpp" -#include "../../core/world/iterators.hpp" -#include "../../core/world/actions.hpp" -#include "../../core/world/models.hpp" -#include "../../core/net/io.hpp" -#include +#undef LOG_PREFIX +#define LOG_PREFIX "Server: " using namespace world::server; -constexpr auto AREAS_FILE = "/areas.idx"; -constexpr auto COMPRESSION_PREFIX = (uint8_t)net::server_packet_type::COMPRESSION; -constexpr auto PREDICTABLE = true; - -Universe::Universe(const Universe::options &options): host(options.connection, - [&](net::server::Peer* peer) { return onConnect(peer); }, - [&](net::server::Peer* peer, bool is_app, uint16_t reason) { return onDisconnect(peer, is_app, reason); }, - [&](net::server::Peer* peer, const data::out_view &buf, net::PacketFlags flags) { return onPacket(peer, buf, flags); } - ), dict_content({options.folderPath + "/zstd.dict", "content/zstd.dict"}, data::out_view(&COMPRESSION_PREFIX, sizeof(COMPRESSION_PREFIX))), - dicts(dict_content.content()), dict_write_ctx(dicts.make_writer()) +Universe::Universe(const Universe::options&o, Serializer* serializer, world::server_handle *const h): + handle(h), opts(o), + chunkFactory(h ? (ChunkFactory*)new SharedChunkFactory() : new StandaloneChunkFactory()) { - setOptions(options); - folderPath = options.folderPath; running = true; - std::filesystem::create_directories(folderPath); - { - std::ifstream index(folderPath + AREAS_FILE); - if(index.good()) { - size_t size = 0; - index.read(reinterpret_cast(&size), sizeof(size)); - robin_hood::unordered_map tmp; - while(!index.eof()) { - size_t id = UINT32_MAX; - index.read(reinterpret_cast(&id), sizeof(size_t)); - Area::params params{voxel_pos(0), 0, generator::params()}; - index.read(reinterpret_cast(¶ms.center.x), sizeof(voxel_pos::value_type)); - index.read(reinterpret_cast(¶ms.center.y), sizeof(voxel_pos::value_type)); - index.read(reinterpret_cast(¶ms.center.z), sizeof(voxel_pos::value_type)); - index.read(reinterpret_cast(¶ms.radius), sizeof(int)); - index.read(reinterpret_cast(¶ms.properties), sizeof(generator::params)); - [[maybe_unused]] - auto ok = tmp.emplace(id, params).second; - assert(ok && "Duplicated area"); - index.peek(); - } - assert(tmp.size() == size && "Corrupted areas index"); - far_areas = data::generational::vector(tmp); - LOG_T(far_areas.size() << " areas loaded"); - } else { - LOG_E("No index file!!! Probably a new world..."); - //TODO: generate universe - const auto radius = 1 << 4; - far_areas.emplace(Area::params{glm::multiply(voxel_pos(radius, 0, 0)), radius, - generator::params(std::in_place_type, radius * CHUNK_LENGTH * 3 / 4, 42)}); - //far_areas.emplace(Area::params{voxel_pos(0), 1 << 20, generator::params(std::in_place_type, 42)}); - } - spawnPoint = voxel_pos(100, 0, 0); //TODO: save in index - index.close(); - } - - { - [[maybe_unused]] - const auto type_id = entities.emplace(world::models::player, glm::usvec3(1), glm::vec3(2)); - assert(type_id == PLAYER_ENTITY_ID); - } + std::filesystem::create_directories(opts.folderPath); + loadIndex(); // Workers for (size_t i = 0; i < std::max(1, std::thread::hardware_concurrency() / 2 - 1); i++) { - workers.emplace_back([&] { + workers.emplace_back([&, serializer] { #if TRACY_ENABLE tracy::SetThreadName("Chunks"); #endif - const auto read_ctx = dicts.make_reader(); - const auto write_ctx = dicts.make_writer(); + const auto read_ctx = serializer->Dicts().make_reader(); + const auto write_ctx = serializer->Dicts().make_writer(); while (running) { - if(save_task_t task; saveQueue.pop(task)) { + if(save_task_t task; saveAreaQueue.pop(task)) { //NOTE: must always save before load to avoid chunk regen //MAYBE: queue.take to avoid concurent write or duplicated work on fast move - ZoneScopedN("ProcessSave"); + ZoneScopedN("ProcessSaveArea"); std::ostringstream out; if (!task.second.second->isModified()) { out.setstate(std::ios_base::badbit); } const auto average = task.second.second->write(out); const auto rcPos = glm::split(task.second.first); - const auto reg = task.first.second->getRegion(folderPath, std::make_pair(task.first.first, rcPos.first)); + const auto reg = task.first.second->getRegion(opts.folderPath, area_region_ref(task.first.first.val.index(), rcPos.first)); reg->write(rcPos.second, write_ctx, out.str(), std::make_optional(average)); - } else if (std::pair, std::shared_ptr> task; loadQueue.pop(task)) { + } else if (std::pair> task; loadPartQueue.pop(task)) { + ZoneScopedN("ProcessLoadPart"); + Region::Read(opts.folderPath, task.first.val.index(), read_ctx, [&] (const region_chunk_pos& pos, const std::vector& data) { + ZoneScopedN("ProcessRead"); + io::vec_istream idata(data); + std::istream iss(&idata); + loadedQueue.push({node_chunk_pos{task.first.val, pos}, chunkFactory->create(iss)}); + }); + } else if(std::pair> task; savePartQueue.pop(task)) { + //NOTE: must always fully load parts before save to avoid lost chunks + ZoneScopedN("ProcessSavePart"); + const auto SIZE = task.second->chunks.size(); + const auto alive = std::count_if(task.second->chunks.begin(), task.second->chunks.end(), + [](const std::shared_ptr &ptr) -> bool { return ptr != nullptr; }); + size_t i = 0; + Region::Write(opts.folderPath, task.first.val.index(), write_ctx, alive, [&] { + while (i < SIZE && !task.second->chunks[i]) { + i++; + } + assert(i < SIZE); + std::ostringstream out; + std::static_pointer_cast(task.second->chunks[i])->write(out); + const auto pos = task.second->getPos(i); + i++; + return std::make_pair(pos, out.str()); + }); + } else if (std::pair> task; loadAreaQueue.pop(task)) { //MAYBE: loadQueue.take to avoid duplicated work on fast move - ZoneScopedN("ProcessLoad"); + ZoneScopedN("ProcessLoadArea"); const auto &pos = task.first; - const auto rcPos = glm::split(pos.second); - const auto reg = task.second->getRegion(folderPath, std::make_pair(pos.first, rcPos.first)); + const auto rcPos = glm::split(pos.chunk); + const auto reg = task.second->getRegion(opts.folderPath, area_region_ref(pos.area.val.index(), rcPos.first)); std::vector data; if(reg->read(rcPos.second, read_ctx, data)) { ZoneScopedN("ProcessRead"); - vec_istream idata(data); + io::vec_istream idata(data); std::istream iss(&idata); - loadedQueue.push({pos, createChunk(iss)}); + loadedQueue.push({node_chunk_pos{pos.area.val, pos.chunk}, chunkFactory->create(iss)}); } else { ZoneScopedN("ProcessGenerate"); - loadedQueue.push({pos, createChunk(pos.second, task.second->getGenerator())}); + loadedQueue.push({node_chunk_pos{pos.area.val, pos.chunk}, chunkFactory->create(pos.chunk, task.second->getGenerator())}); } } else { - loadQueue.wait(); + loadAreaQueue.wait(); } } }); } +#ifndef STANDALONE_SERVER + if (handle) + handle->elements = elements.make_ref(); +#endif } Universe::~Universe() { - saveAll(false); - saveAreas(); + { + const auto elements = getElements(); + for(auto& ref: elements->areas) { + const auto id = elements->withFlag(ref); + const auto area = elements->findArea(id.val)->get(); + auto &chunks = area->setChunks(); + for (auto it_c = chunks.begin(); it_c != chunks.end(); it_c = chunks.erase(it_c)) { + saveAreaQueue.emplace(std::make_pair(id.val, area), std::make_pair(it_c->first, std::static_pointer_cast(it_c->second))); + } + } + for(auto& ref: elements->parts) { + const auto id = elements->withFlag(ref); + const auto part = elements->findPart(id.val)->get(); + if (!part->chunks.empty()) + savePartQueue.emplace(id.val, part); + } + } + + if (auto size = saveAreaQueue.size() + savePartQueue.size(); size > 0) { + const auto SAVE_CHECK_TIME = 1000; + do { + loadAreaQueue.notify_all(); + LOG_I("Saving " << size << " chunks"); + std::this_thread::sleep_for(std::chrono::milliseconds(SAVE_CHECK_TIME)); + size = saveAreaQueue.size() + savePartQueue.size(); + } while (size > 0); + } + writeIndex(); running = false; - loadQueue.notify_all(); + loadAreaQueue.notify_all(); for (auto &worker: workers) { if (worker.joinable()) @@ -130,223 +137,100 @@ Universe::~Universe() { LOG_D("Universe disappeared"); } -struct net_client { - net_client(data::generational::id id): instanceId(id) { } - data::generational::id instanceId; +void Universe::update(float deltaTime) { + ZoneScopedN("Update"); - std::vector, long>> pendingChunks; - uint32_t chunkEmitRate = 0; - uint32_t chunkRateEpoch = 0; - bool popPendingChunk(area_ &out) { - if (pendingChunks.empty()) - return false; + const auto lock = setElements(); + auto &elements = *lock; - out = pendingChunks.back().first; - pendingChunks.pop_back(); - return true; - } - void pushChunk(const area_& in, long dist) { - for (auto it = pendingChunks.begin(); it != pendingChunks.end(); ++it) { - if (it->first == in) { - pendingChunks.erase(it); - break; - } - } + elements.hierarchy.for_each([&](Elements::hierarchy_entry &entry) { + //vel += acc * deltaTime + entry.relative.position += entry.vel.position * deltaTime; + entry.relative.rotation = glm::slerp(identity_pivot, entry.vel.rotation, deltaTime) * entry.relative.rotation; - if (pendingChunks.size() >= net::MAX_PENDING_CHUNK_COUNT) { - if (dist > pendingChunks.front().second) - return; - pendingChunks.erase(pendingChunks.begin()); - } - - auto it = pendingChunks.begin(); - while (it != pendingChunks.end()) { - if (dist > it->second) - break; - ++it; - } - pendingChunks.insert(it, std::make_pair(in, dist)); - } - - bool handleEdits = false; - // MAYBE: client size ordering - std::queue pendingEdits; -}; - -void Universe::saveAll(bool remove) { - for(auto& area: areas) { - auto& chunks = area.second->setChunks(); - for (auto it_c = chunks.begin(); it_c != chunks.end(); remove ? it_c = chunks.erase(it_c) : ++it_c) { - saveQueue.emplace(area, std::make_pair(it_c->first, std::dynamic_pointer_cast(it_c->second))); - } - } - loadQueue.notify_all(); - - if (auto size = saveQueue.size(); size > 0) { - LOG_I("Saving " << size << " chunks"); - const auto SAVE_CHECK_TIME = 500; - do { - loadQueue.notify_all(); - std::cout << "\rSaving... " << size << " " << std::flush; - std::this_thread::sleep_for(std::chrono::microseconds(SAVE_CHECK_TIME)); - size = saveQueue.size(); - } while (size > 0); - std::cout << std::endl; - } -} - -// Write areas index (warn: file io) -void Universe::saveAreas() const { - std::ofstream index(folderPath + AREAS_FILE, std::ios::out | std::ios::binary); - if(!index.good()) { - LOG_E("Areas index write error"); - return; - } - { - size_t size = areas.size() + far_areas.size(); - index.write(reinterpret_cast(&size), sizeof(size)); - } - std::function write = [&](area_id id, Area::params params) { - auto idx = id.index; - index.write(reinterpret_cast(&idx), sizeof(size_t)); - index.write(reinterpret_cast(¶ms.center.x), sizeof(voxel_pos::value_type)); - index.write(reinterpret_cast(¶ms.center.y), sizeof(voxel_pos::value_type)); - index.write(reinterpret_cast(¶ms.center.z), sizeof(voxel_pos::value_type)); - index.write(reinterpret_cast(¶ms.radius), sizeof(int)); - index.write(reinterpret_cast(¶ms.properties), sizeof(generator::params)); // MAYBE: use binary structured format (protobuf/msgpack) - }; - for(const auto& area: areas) { - write(area.first, area.second->getParams()); - } - far_areas.iter(write); - if(!index.good()) - LOG_E("Areas index write error"); - - index.close(); -} - -void Universe::pull() { - ZoneScopedN("Network"); - host.pull(); - host.iterPeers([&](net::server::Peer *peer) { - auto data = peer->getCtx(); - if (data == nullptr) - return; - - if (!data->pendingEdits.empty() && peer->queueSize(net::server::queue::EDIT) == 0) { - peer->send(net::PacketWriter::Of(net::server_packet_type::EDITS, data->pendingEdits.front())); - data->pendingEdits.pop(); - } - - if (!data->pendingChunks.empty()) { - //TODO: use congestion - area_ pending; - size_t i = peer->queueSize(net::server::queue::CHUNK); - while(i <= 4 && data->popPendingChunk(pending)) { - if (const auto it = areas.find(pending.first); it != areas.end()) { - if (const auto chunk = it->second->getChunks().findInRange(pending.second)) { - peer->send(serializeChunk(pending, std::dynamic_pointer_cast(chunk.value())), net::server::queue::CHUNK); - i++; - } - } - } - - if (data->pendingChunks.empty()) - peer->send(net::PacketWriter(net::server_packet_type::CHUNK, 0).finish()); //FIXME: must be received after last chunk - } + const auto node = elements.nodes.directly_at(entry.self); + node->absolute = elements.computeAbsolute(entry.parent, entry.relative); + //TODO: full physics }); } -void Universe::update(float deltaTime) { - ZoneScopedN("Universe"); +void Universe::upgrade() { + ZoneScopedN("Upgrade"); - std::vector moves; - { - moves.reserve(movedPlayers.size()); - for (const auto& id: movedPlayers) { - if (auto player = findEntity(PLAYER_ENTITY_ID, id)) - moves.push_back(player->pos.as_voxel()); - } - movedPlayers.clear(); - } + const auto lock = setElements(); + auto &elements = *lock; - if (!moves.empty()) { - ZoneScopedN("Far"); - bool extracted = false; - far_areas.extract([&](area_id id, Area::params params) { - for(const auto& move: moves) { - if(const chunk_pos diff = glm::divide(move - params.center); - glm::length2(diff) <= glm::pow2(loadDistance + params.radius)) { - - LOG_I("Load area " << id.index); - areas.emplace(id, std::make_shared(params)); - extracted = true; - return true; - } + { // Random tick + const auto size = elements.nodes.size(); + auto rng = std::mt19937(std::random_device()()); + const auto randomTick = opts.randomTick; + auto dist = std::uniform_int_distribution(0, randomTick); + for (size_t part = 0; part <= size / randomTick; part++) { + const node_ref ref = part * randomTick + dist(rng); + if (const auto id = elements.withFlag(ref)) { + elements.nodes.set_flag(Elements::Set(id)); } - return false; - }); - if(extracted) - broadcastAreas(); + } } - const auto &players = entities.at(PLAYER_ENTITY_ID).instances; - { // Update alive areas + + std::vector movedLoaders; + elements.nodes.iter([&](const node_id &id, const Node &node) { + if (Elements::Has(id) && ( + Elements::Has(id) || Elements::Has(id))) + movedLoaders.push_back(node.absolute.position); + }); + + { // Update areas && parts ZoneScopedN("World"); #if TRACY_ENABLE size_t chunk_count = 0; size_t region_count = 0; -#endif - const bool queuesEmpty = loadQueue.empty() && saveQueue.empty(); bool allLazy = true; - auto it = areas.begin(); - while (it != areas.end()) { +#endif + const auto keepDist2 = glm::pow2(opts.keepDistance); + const auto regionKeepDist2 = glm::pow2(opts.keepDistance + REGION_LENGTH * 2); + const bool queuesEmpty = loadAreaQueue.empty() && saveAreaQueue.empty(); + for (const auto& ref: elements.areas) { ZoneScopedN("Area"); - //FIXME: const auto areaChunkChange = it->second->move(glm::vec3(deltaTime)); - const auto areaRange = glm::pow2(keepDistance + it->second->getChunks().getRadius()); - const auto areaDiff = glm::divide(it->second->getOffset().as_voxel()); + const auto id = elements.withFlag(ref.val); + const auto a_node = elements.findArea(id.val); + assert(a_node); + const auto area = a_node->get(); + const auto areaRange = glm::pow2(opts.keepDistance + area->getChunks().getRadius()); - std::vector inAreaPlayers; - players.iter([&](const entity_id&, const Entity::Instance& player) { - const chunk_pos diff = glm::divide(player.pos.as_voxel() - it->second->getOffset().as_voxel()); - if (glm::length2(diff) <= areaRange) - inAreaPlayers.push_back(diff); + //MAYBE: use hierarchy + std::vector inRangeLoaders; + elements.nodes.iter([&](const node_id& id, const Node& cur) { + if (Elements::Has(id)) { + const chunk_pos dist = glm::divide(cur.absolute.position - a_node->absolute.position); + if (glm::length2(dist) <= areaRange) //MAYBE: unique + inRangeLoaders.push_back(dist); + } }); - auto &chunks = it->second->setChunks(); - if (inAreaPlayers.empty()) { - auto it_c = chunks.begin(); - while(it_c != chunks.end()) { - saveQueue.emplace(*it, std::make_pair(it_c->first, std::dynamic_pointer_cast(it_c->second))); - it_c = chunks.erase(it_c); + auto &chunks = area->setChunks(); + bool lazyArea = queuesEmpty; + if (inRangeLoaders.empty()) { + if (!chunks.empty()) { + for (auto it_c = chunks.begin(); it_c != chunks.end();) { + saveAreaQueue.emplace(std::make_pair(id.val, area), std::make_pair(it_c->first, std::static_pointer_cast(it_c->second))); + lazyArea = false; + it_c = chunks.erase(it_c); + } } - loadQueue.notify_all(); - LOG_I("Unload area " << it->first.index); - [[maybe_unused]] - auto ok = far_areas.put(it->first, it->second->getParams()); - assert(ok); - it = areas.erase(it); - saveAreas(); } else { - bool lazyArea = queuesEmpty; - { // Update alive chunks + { // Check alive chunks ZoneScopedN("Alive"); - auto it_c = chunks.begin(); - while(it_c != chunks.end()) { - if ([&] { - const auto keepDist = glm::pow2(keepDistance); - for(const auto& diff: inAreaPlayers) { - if (glm::length2(diff - it_c->first) < keepDist) - return true; - } - return false; - }()) { - updateChunk(it, it_c, areaDiff, deltaTime); + for (auto it_c = chunks.begin(); it_c != chunks.end();) { + if (std::any_of(inRangeLoaders.begin(), inRangeLoaders.end(), [&] (const chunk_pos& dist) { + return glm::length2(dist - chunk_pos((glm::qua)a_node->absolute.rotation * glm::dvec3(it_c->first))) <= keepDist2; + })) { ++it_c; #if TRACY_ENABLE chunk_count++; #endif } else { - saveQueue.emplace(*it, std::make_pair(it_c->first, std::dynamic_pointer_cast(it_c->second))); //MAYBE: take look + saveAreaQueue.emplace(std::make_pair(id.val, area), std::make_pair(it_c->first, std::static_pointer_cast(it_c->second))); lazyArea = false; it_c = chunks.erase(it_c); } @@ -354,51 +238,82 @@ void Universe::update(float deltaTime) { } { // Enqueue missing chunks ZoneScopedN("Missing"); - auto handle = loadQueue.inserter(); - for (const auto& to: moves) { - const chunk_pos diff = glm::divide(to - it->second->getOffset().as_voxel()); - if (glm::length2(diff) > areaRange) + std::optional handle; + const lpivot inv_rot = glm::conjugate(a_node->absolute.rotation); + for (const auto& to: movedLoaders) { + const chunk_pos dist = glm::divide(inv_rot * (to - a_node->absolute.position)); + if (glm::length2(dist) > areaRange) continue; //TODO: need dist so no easy sphere fill + const auto loadDistance = opts.loadDistance; for (int x = -loadDistance; x <= loadDistance; x++) { for (int y = -loadDistance; y <= loadDistance; y++) { for (int z = -loadDistance; z <= loadDistance; z++) { const auto dist2 = x * x + y * y + z * z; if (dist2 <= loadDistance * loadDistance) { - const auto p = diff + chunk_pos(x, y, z); + const auto p = dist + chunk_pos(x, y, z); if (chunks.inRange(p) && chunks.find(p) == chunks.end()) { - handle.first(std::make_pair(it->first, p), it->second, -dist2); + if (!handle) { handle.emplace(loadAreaQueue.inserter()); } + handle.value().first(area_chunk_pos{id.val, p}, area, -dist2); lazyArea = false; } } }}} } - if(!lazyArea) - loadQueue.notify_all(); } - allLazy &= lazyArea; - if (lazyArea) { // Clear un-used regions - ZoneScopedN("Region"); - const auto unique = it->second->getRegions(); // MAYBE: shared then unique + } #if TRACY_ENABLE - region_count += unique->size(); + allLazy &= lazyArea; #endif - for (auto it_r = unique->begin(); it_r != unique->end(); ++it_r) { - if([&] { - const auto keepDist = glm::pow2(keepDistance + REGION_LENGTH * 2); - for(const auto& diff: inAreaPlayers) { - if (glm::length2(diff - glm::lvec3(it_r->first) * glm::lvec3(REGION_LENGTH)) <= keepDist) - return false; - } - return true; - }()) { - unique->erase(it_r); //FIXME: may wait for os file access (long) - break; //NOTE: save one only max per frame - } + if (lazyArea) { // Clear un-used regions + ZoneScopedN("Region"); + const auto unique = area->getRegions(); // MAYBE: shared then unique +#if TRACY_ENABLE + region_count += unique->size(); +#endif + for (auto it_r = unique->begin(); it_r != unique->end(); ++it_r) { + if(std::none_of(inRangeLoaders.begin(), inRangeLoaders.end(), [&] (const chunk_pos& dist) { + return glm::length2(dist - chunk_pos(it_r->first) * chunk_pos(REGION_LENGTH)) <= regionKeepDist2; + })) { + unique->erase(it_r); //FIXME: may wait for os file access (long) + break; //NOTE: save one only max per update } } - ++it; + } else { + loadAreaQueue.notify_all(); + } + } + // PARTS + for (const auto& ref: elements.parts) { + ZoneScopedN("Part"); + const auto id = elements.withFlag(ref.val); + const auto r_node = elements.findPart(id.val); + assert(r_node); + const auto part = r_node->get(); + + //FIXME: pivot point + const auto partOffset = part->chunkSize() / chunk_pos(2); + + //MAYBE: use hierarchy + const auto inRangeLoaders = elements.nodes.any([&](const node_id& id, const Node& cur) { + if (Elements::Has(id)) { + const chunk_pos dist = glm::divide(cur.absolute.position - r_node->absolute.position); + return glm::length2(dist + partOffset) <= keepDist2; + } + return false; + }); + + if (!inRangeLoaders) { + if (!part->chunks.empty()) { + savePartQueue.emplace(id.val, part); + r_node->content = std::make_shared(part->size); + } + } else { + if (part->chunks.empty()) { + loadPartQueue.emplace(id.val, part); + part->allocate(); + } } } #if TRACY_ENABLE @@ -406,11 +321,14 @@ void Universe::update(float deltaTime) { if(allLazy) { TracyPlot("Region", static_cast(region_count)); } - TracyPlot("ChunkLoad", static_cast(loadQueue.size())); - TracyPlot("ChunkUnload", static_cast(saveQueue.size())); + TracyPlot("AreaLoad", static_cast(loadAreaQueue.size())); + TracyPlot("AreaUnload", static_cast(saveAreaQueue.size())); + TracyPlot("PartLoad", static_cast(loadPartQueue.size())); + TracyPlot("PartUnload", static_cast(savePartQueue.size())); #endif + loadAreaQueue.notify_all(); } - { // Update entities + /* FIXME: { // Update entities ZoneScopedN("Entities"); size_t item_count = 0; entities.remove([&](entity_id type, Entity &val) { @@ -428,7 +346,7 @@ void Universe::update(float deltaTime) { auto pos = inst.pos + OFFSET; for(const auto& area: areas) { //NOTE: consider entity mass negligible - inst.velocity += area.second->getGravity((pos - area.second->getOffset()).as_voxel()) * deltaTime; + inst.velocity += area.second->getGravity((pos - area.second->getOffset()).as_cell()) * deltaTime; } //MAYBE: friction if (move(pos, inst.velocity, 1, glm::max_axis(OFFSET))) { @@ -436,7 +354,7 @@ void Universe::update(float deltaTime) { } inst.pos = pos - OFFSET; if (entities.at(PLAYER_ENTITY_ID).instances.contains([&](const entity_id&, const Entity::Instance& player) { - return glm::length2(glm::divide((player.pos - inst.pos).as_voxel())) <= DIST2; + return glm::length2(glm::divide((player.pos - inst.pos).as_cell())) <= DIST2; })) { item_count++; return false; @@ -448,445 +366,196 @@ void Universe::update(float deltaTime) { }); TracyPlot("EntityCount", static_cast(item_count)); - constexpr auto CAT_SIZE = sizeof(entity_id::index) + sizeof(size_t); - constexpr auto ITEM_SIZE = sizeof(entity_id::index) + sizeof(glm::ifvec3) + sizeof(glm::vec3); - //TODO: only moved (pos, vel modified) - //TODO: reduce rate with distance - auto packet = net::PacketWriter(net::server_packet_type::ENTITIES, CAT_SIZE * entities.size() + ITEM_SIZE * item_count); - entities.iter([&](entity_id id, const Entity &entity) { - packet.write(id.index); - packet.write(entity.instances.size()); - entity.instances.iter([&](entity_id i, const Entity::Instance &inst) { - packet.write(i.index); - packet.write(inst.pos); - packet.write(inst.velocity); - }); - }); - host.sendBroadcast(packet.finish(), net::server::queue::ENTITY, 1); - } + }*/ { // Store loaded chunks ZoneScopedN("Load"); - robin_hood::pair, std::shared_ptr> loaded; - for (auto handle = loadedQueue.extractor(); handle.first(loaded);) { - if (const auto it = areas.find(loaded.first.first); it != areas.end()) { - it->second->setChunks().emplace(loaded.first.second, loaded.second); - const auto areaOffset = glm::divide(it->second->getOffset().as_voxel()); - loadChunk(loaded.first, areaOffset, it->second->getChunks()); - } - } - } -} - -std::optional Universe::onConnect(net::server::Peer* peer) { - ZoneScopedN("Connect"); - using namespace net; - LOG_I("Client connect from " << peer->getAddress()); - net_client* client = new net_client(entities.at(PLAYER_ENTITY_ID).instances.emplace(Entity::Instance{spawnPoint, glm::vec3(0)})); - peer->ctx = client; - - { - auto packet = PacketWriter(server_packet_type::CAPABILITIES, sizeof(loadDistance) + sizeof(bool) + sizeof(floodFillLimit) * PREDICTABLE); - packet.write(loadDistance); - packet.write(PREDICTABLE); - if constexpr (PREDICTABLE) { - packet.write(floodFillLimit); - } - peer->send(packet.finish()); - } - - //TODO: lock while not received - peer->send(data::out_buffer(data::out_view((uint8_t*)dict_content.data(), dict_content.size()), nullptr), net::server::queue::CHUNK); - { - auto player = findEntity(PLAYER_ENTITY_ID, client->instanceId); - auto packet = PacketWriter(server_packet_type::TELEPORT, sizeof(size_t) + sizeof(voxel_pos)); - packet.write(client->instanceId.index); - packet.write(player->pos.as_voxel()); - peer->send(packet.finish()); - movedPlayers.insert(client->instanceId); - } - broadcastEntities(); - broadcastMessage("> Player" + std::to_string(client->instanceId.index) + " has joined us"); - broadcastAreas(); - return std::nullopt; -} -bool Universe::onDisconnect(net::server::Peer *peer, bool is_app, uint16_t reason) { - ZoneScopedN("Disconnect"); - LOG_I((is_app ? "Client disconnect from " : "Client connection lost from ") << peer->getAddress() << " (" << reason << ')'); - if (auto data = peer->getCtx()) { - broadcastMessage("> Player" + std::to_string(data->instanceId.index) + " has left our universe"); - entities.at(PLAYER_ENTITY_ID).instances.free(data->instanceId); - delete data; - peer->ctx = nullptr; - } - return true; -} - -bool Universe::onPacket(net::server::Peer *peer, const data::out_view &buf, net::PacketFlags) { - ZoneScopedN("Packet"); - - using namespace net; - - auto packet = PacketReader(buf); - client_packet_type type; - if(!packet.read(type)) { - LOG_D("Empty packet from " << peer->getAddress()); - return false; - } - - switch (type) { - case client_packet_type::CAPABILITIES: { - auto data = peer->getCtx(); - packet.read(data->handleEdits); - if (!PREDICTABLE && data->handleEdits) { - LOG_E("Client misread capabilities"); - } - break; - } - case client_packet_type::MOVE: { - auto data = peer->getCtx(); - if (voxel_pos pos; !packet.read(pos) || - !movePlayer(data->instanceId, pos)) { - LOG_T("Bad move"); - } - break; - } - case client_packet_type::FILL_SHAPE: { - if(const auto fill = packet.read()) { - //TODO: check ray - if (fill->val.is_solid() && !isAreaFree(fill->pos, world::action::ToGeometry(fill->shape), fill->radius)) { - LOG_T("Entity in solid fill area"); - break; - } - host.iterPeers([&](net::server::Peer *peer) { - auto data = peer->getCtx(); - //MAYBE: only in range - if (data && data->handleEdits) - data->pendingEdits.push(*fill); - }); - set(fill->pos, fill->radius, fill->shape, fill->val); - //TODO: handle inventory + loadedQueue.extractor([&](const std::pair> &loaded) { + if (Elements::Is(loaded.first.node)) { + if (const auto area = elements.findArea(loaded.first.node.val)) + area->get()->setChunks().emplace(loaded.first.chunk, loaded.second); } else { - LOG_T("Bad fill"); - } - break; - } - case client_packet_type::MESSAGE: { - const auto ref = packet.readRemaining(); - broadcastMessage("Player" + std::to_string(peer->getCtx()->instanceId.index) - + ": " + std::string((const char*)ref.data(), ref.size())); - break; - } - case client_packet_type::MISSING_REGIONS: { - auto data = peer->getCtx(); - if (auto player = findEntity(PLAYER_ENTITY_ID, data->instanceId)) { - const auto pos = player->pos.as_voxel(); - - area_id id; - if (!packet.read(id)) - break; - if (auto area = areas.find(id); area != areas.end()) { - const chunk_pos areaOffset = glm::divide(pos - area->second->getOffset().as_voxel()); - while (!packet.isDone()) { - region_pos rpos; - if (!packet.read(rpos)) - break; - - if (auto it_r = area->second->getRegions()->find(rpos); it_r != area->second->getRegions()->end()) { - if (glm::length2(areaOffset - glm::lvec3(it_r->first) * glm::lvec3(REGION_LENGTH)) <= glm::pow2(loadDistance + REGION_LENGTH * 2)) { - auto packet = PacketWriter(server_packet_type::REGION, sizeof(area_)); - packet.write>(std::make_pair(id, rpos)); - packet.pushPart([&](PacketWriter::part part) { - it_r->second->averages(part); }); - peer->send(packet.finish(), net::server::CHUNK); - } else { - LOG_T("Request out of range region"); - } - - } - } - } else { - LOG_T("Bad region request"); + if (const auto part = elements.findPart(loaded.first.node.val)) { + part->get()->chunks[part->get()->getIdx(loaded.first.chunk)] = loaded.second; } } - break; - } - case client_packet_type::MISSING_CHUNKS: { - auto data = peer->getCtx(); - if (auto player = findEntity(PLAYER_ENTITY_ID, data->instanceId )) { - const auto pos = player->pos.as_voxel(); - - area_id id; - if (!packet.read(id)) - break; - if (auto area = areas.find(id); area != areas.end()) { - auto &chunks = area->second->getChunks(); - const chunk_pos areaOffset = glm::divide(pos - area->second->getOffset().as_voxel()); - - for (size_t i = 0; !packet.isDone() && i < MAX_PENDING_CHUNK_COUNT; i++) { - chunk_pos cpos; - if (!packet.read(cpos)) - break; - const auto dist = glm::length2(areaOffset - cpos); - if (dist <= glm::pow2(loadDistance) && chunks.inRange(cpos)) { - if (chunks.findInRange(cpos).has_value()) - data->pushChunk(std::make_pair(id, cpos), dist); - } else { - LOG_T("Request out of range chunk"); - } - } - } else { - LOG_T("Bad chunk request"); - } - } - break; - } - case client_packet_type::MISSING_ENTITY: { - size_t id; - if (!packet.read(id)) - break; - - if (auto entity = entities.directly_at(id)) { - if (const auto area = std::get_if(&entity->shape)) { - auto packet = PacketWriter(server_packet_type::ENTITY_SHAPE, sizeof(id) + sizeof(bool) + sizeof(size_t)); - packet.write(id); - packet.write(true); - for (auto it = area->begin(); it != area->end(); ++it) { - std::ostringstream out; - std::dynamic_pointer_cast(*it)->write(out); - packet.pushSizedPart([&](PacketWriter::part part) { - dict_write_ctx.compress(out.str(), part); }); - } - peer->send(packet.finish(), net::server::CHUNK); - } else { - const auto model = std::get_if(&entity->shape); - assert(model); - auto packet = PacketWriter(server_packet_type::ENTITY_SHAPE, sizeof(id) + sizeof(bool) + model->size()); - //MAYBE: prefix model with id+false - packet.write(id); - packet.write(false); - packet.write(model->data(), model->size()); - peer->send(packet.finish(), net::server::CHUNK); - } - } else { - LOG_T("Bad entity request " << id); - } - break; - } - default: - LOG_T("Bad packet from " << peer->getAddress()); - break; - } - return true; -} -void Universe::broadcastAreas() { - constexpr size_t ITEM_SIZE = sizeof(area_id) + sizeof(world::Area::params); - - auto packet = net::PacketWriter(net::server_packet_type::AREAS, ITEM_SIZE * areas.size()); - for(const auto& area: areas) { - const auto params = area.second->getParams(); - packet.write(area.first); - packet.write(world::Area::params{params.center, params.radius, area.second->getCurvature()}); - } - host.sendBroadcast(packet.finish()); -} -data::out_buffer Universe::serializeChunk(area_ id, const std::shared_ptr &data) { - ZoneScopedN("Chunk"); - std::ostringstream out; - data->write(out); - auto packet = net::PacketWriter(net::server_packet_type::CHUNK, sizeof(id)); - packet.write(id); - packet.pushPart([&](net::PacketWriter::part part){ - dict_write_ctx.compress(out.str(), part); }); - return packet.finish(); -} -void Universe::broadcastMessage(const std::string& text) { - host.sendBroadcast(net::PacketWriter::Of(net::server_packet_type::MESSAGE, text.data(), text.size())); -} -void Universe::broadcastEntities() { - constexpr auto ITEM_SIZE = sizeof(entity_id::index) + sizeof(glm::usvec3) + sizeof(glm::vec3) + sizeof(uint8_t); - auto packet = net::PacketWriter(net::server_packet_type::ENTITY_TYPES, ITEM_SIZE * entities.size()); - entities.iter([&](entity_id id, const Entity &entity) { - packet.write(id.index); - packet.write(entity.size); - packet.write(entity.scale); - uint8_t flags = 0; - if (entity.permanant) - flags |= 1; - if (std::holds_alternative(entity.shape)) - flags |= 2; - packet.write(flags); - }); - host.sendBroadcast(packet.finish(), net::server::queue::ENTITY); -} - -void Universe::updateChunk(area_map::iterator &, world::ChunkContainer::iterator &, chunk_pos, float /*deltaTime*/) {} -void Universe::loadChunk(area_, chunk_pos, const world::ChunkContainer &) {} - -void Universe::setOptions(const Universe::options& options) { - loadDistance = options.loadDistance; - keepDistance = options.keepDistance; - floodFillLimit = options.floodFillLimit; -} - -Universe::ray_result Universe::raycast(const geometry::Ray &ray) const { - return Raycast(ray, areas); -} - -bool Universe::isAreaFree(const area_ &pos, const geometry::Shape shape, const uint16_t radius) const { - if (const auto it = areas.find(pos.first); it != areas.end()) { - const auto center = pos.second + it->second->getOffset().as_voxel(); - return !entities.contains([&](entity_id, const Entity &entity) { - return entity.instances.contains([&](entity_id, const Entity::Instance &inst) { - return geometry::InShape(shape, center, radius, inst.pos.as_voxel(), entity.size); - }); }); - } else - return false; + } } -world::ItemList Universe::set(const area_& pos, int radius, world::action::Shape shape, const world::Voxel& val) { - ZoneScopedN("Fill"); - ItemList list; - const bool stupidClient = host.anyPeer([&](net::server::Peer *peer) { - auto data = peer->getCtx(); - return data && !data->handleEdits; - }); - robin_hood::unordered_map> edits; - const auto fill = world::action::FillShape(pos, val, shape, radius); - world::iterator::Apply(areas, fill, - [&](std::shared_ptr& ck, chunk_pos ck_pos, chunk_voxel_idx idx, Voxel /*prev*/, Voxel next, float delay) { - if (stupidClient) - edits[ck_pos].push_back(Chunk::Edit{next, delay, idx}); - //TODO: apply break table - //TODO: inventory - ck->replace(idx, next, delay); - }); - if (world::iterator::Split(areas, fill, floodFillLimit, [&](const robin_hood::unordered_set& part, Voxel cleanVoxel, - world::ChunkContainer& chunks, std::shared_ptr& ck, chunk_pos& ck_pos, std::shared_ptr& it - ){ - voxel_pos min = voxel_pos(INT64_MAX); - voxel_pos max = voxel_pos(INT64_MIN); - for(auto full: part) { - min = glm::min<3, long long>(min, full); - max = glm::max<3, long long>(max, full); +constexpr auto INDEX_FILE = "/nodes.idx"; +void Universe::loadIndex() { + const auto elements = setElements(); + assert(elements->nodes.empty()); + io::ifstream index(opts.folderPath + INDEX_FILE); + if(index.good()) { + { // Spawn + node_ref spawnParent; + index.read_cast(spawnParent); + spawnPoint.parent = generational::id(spawnParent.val, 0); + index.read_cast(spawnPoint.position); } - const auto size = max - min; - const chunk_pos scale = chunk_pos(1) + glm::divide(size); - const auto type = entities.emplace(size, glm::vec3(1), false); - auto &entity = entities.at(type); - auto &area = std::get(entity.shape); + elements->readIndex(index); + } else { + LOG_E("No index file!!! Probably a new world..."); + spawnPoint.position = world_pos(100, 0, 0); + const auto p = elements->createInstance(Elements::start_point(generational::id(), transform(), velocity(offset_pos(), glm::angleAxis(.1f, glm::vec3(0, 0, 1)))), generational::id(0)); + elements->createInstance(Elements::start_point(generational::id(), transform(), velocity(offset_pos(1))), generational::id(0)); + elements->createInstance(Elements::start_point(p.val, transform(glm::vec3(1000, 0, 0))), generational::id(0)); + //TODO: generate universe { - const auto chunk_count = scale.x * scale.y * scale.z; - area.reserve(chunk_count); - for (long i = 0; i < chunk_count; i++) - area.push_back(createChunk()); + const auto radius = 1 << 4; + generator::RoundPlanet::Params opts(radius * CHUNK_LENGTH * 3 / 4); + Area::params params{radius, 0, std::vector(sizeof(opts))}; + memcpy(params.params.data(), &opts, params.params.size()); + const auto a = elements->createArea(Elements::start_point(generational::id(), transform(glm::multiply(voxel_pos(radius, 0, 0)), glm::angleAxis(.5f, glm::vec3(0, 1, 0))), velocity(offset_pos(), glm::angleAxis(.1f, glm::vec3(0, 1, 0)))), params); + elements->createInstance(Elements::start_point(a.val, transform(glm::vec3(-radius * CHUNK_LENGTH + 100, 0, 0))), generational::id(0)); } - std::shared_ptr ck_dest = nullptr; - chunk_pos ck_pos_dest = chunk_pos(INT32_MAX); - for(auto full: part) { - const auto split = glm::splitIdx(full); - if (Voxel v; world::iterator::GetChunk(chunks, split, v, ck, ck_pos) && v.is_solid()) { - if (stupidClient) - edits[ck_pos].push_back(Chunk::Edit{cleanVoxel, fill.radius * .05f, split.second}); - - ck->replace(split.second, cleanVoxel); - const auto spl = glm::splitIdx(full - min); - if (spl.first != ck_pos_dest) { - ck_dest = std::dynamic_pointer_cast(area.at(glm::toIdx(spl.first, scale))); - assert(ck_dest); - ck_pos_dest = spl.first; - } - ck_dest->set(spl.second, v); - } - } - entity.instances.emplace(Entity::Instance{it->getOffset() + min, glm::vec3(0)}); - })) { broadcastEntities(); } - - if (stupidClient && !edits.empty()) { - ZoneScopedN("Packet"); - size_t size = sizeof(area_id); - for(const auto& part: edits) { - size += sizeof(chunk_pos); - size += sizeof(chunk_voxel_idx); - size += sizeof(Chunk::Edit) * part.second.size(); - } - auto packet = net::PacketWriter(net::server_packet_type::EDITS, size); - packet.write(pos.first); - for(const auto& part: edits) { - packet.write(part.first); - packet.write(part.second.size()); - packet.write(part.second.data(), part.second.size() * sizeof(Chunk::Edit)); - } - auto buffer = packet.finish(); - host.iterPeers([&](net::server::Peer *peer) { - //MAYBE: only in range - auto data = peer->getCtx(); - if (data && !data->handleEdits) - peer->send(buffer, net::server::queue::CHUNK); - }); } - return list; + index.close(); +} +void Universe::writeIndex() { + io::ofstream index(opts.folderPath + INDEX_FILE, std::ios::out | std::ios::binary); + if(index.good()) { + index.write_cast(spawnPoint.parent.val.index()); + index.write_cast(spawnPoint.position); + getElements()->writeIndex(index); + if(!index.good()) + LOG_E("Index file write error"); + } else { + LOG_E("Failed to open index file"); + } + + index.close(); } -bool Universe::collide_end(const glm::ifvec3 &pos, const glm::vec3 &vel, int density, float radius) const { - return std::holds_alternative(raycast(geometry::Ray((pos + vel) * density, vel, radius))); -} -bool Universe::collide_point(const glm::ifvec3 &pos, const glm::vec3 &vel, int density) const { - const auto target = ((pos + vel) * density).as_voxel(); - for(auto& area: areas) { - if(area.second->getBounding().contains(target)) { - const auto &offset = area.second->getOffset().as_voxel(); - const auto &chunks = area.second->getChunks(); - const auto pos = target - offset; - const chunk_pos cPos = glm::divide(pos); - if (const auto it = chunks.find(cPos); it != chunks.end()) { - const auto voxel = it->second->getAt(glm::modulo(pos)); - if (voxel.is_solid()) { - return true; - } - } else if(chunks.inRange(cPos)) { - return true; - } +std::shared_ptr Universe::findChunk(const node_chunk_pos& p) const { + const auto lock = getElements(); + if (Elements::Is(p.node)) { + if (auto area = lock->findArea(p.node.val)) { + return std::static_pointer_cast(area->get()->getChunks().findInRange(p.chunk)); } + } else { + if (auto part = lock->findPart(p.node.val)) { + return std::static_pointer_cast(part->get()->findInRange(p.chunk)); + } + } + return nullptr; +} +const world::Node* Universe::getPlayer(node_id id) const { + //FIXME: assert(IsPlayer(id)) + return getElements()->findNode(id); +} +world::Node* Universe::setPlayer(node_id id) { + //FIXME: assert(IsPlayer(id)) + return setElements()->findNode(id); +} + +void Universe::fill(const action::FillShape& fill, fill_edits *const edits) { + const auto lock = setElements(); + ZoneScopedN("Fill"); + //TODO: ItemList inventory; + //TODO: multipart + const auto anchor_id = lock->withFlag(fill.pos.node); + const auto node = lock->findNode(anchor_id); + if (!node) + return; + + const auto applyCB = [&](std::shared_ptr& ck, chunk_pos ck_pos, chunk_voxel_idx idx, Voxel/*prev*/, Voxel next, float delay) { + if (edits) + (*edits)[ck_pos].push_back(ChunkEdits::Edit{next, delay, idx}); + //TODO: apply break table + //TODO: inventory + ck->replace(idx, next, delay); + }; + const auto splitCBer = [&](const Elements::start_point& rtf, const auto& getter) { + return [&] (const robin_hood::unordered_set &voxels) { + //MAYBE: make it void after air propagation setup + const auto cleanVoxel = Voxel(0, Voxel::DENSITY_MAX); + voxel_pos min = voxel_pos(INT64_MAX); + voxel_pos max = voxel_pos(INT64_MIN); + for (const auto& full: voxels) { + min = glm::min<3, long long>(min, full); + max = glm::max<3, long long>(max, full); + } + auto start = rtf; + start.relative.position += min; + const auto part_id = lock->createPart(start, max - min); + const auto node = lock->findPart(part_id); + auto& part = *node->get(); + { + const auto chunk_size = part.chunkSize(); + const auto chunk_count = chunk_size.x * chunk_size.y * chunk_size.z; + part.chunks.reserve(chunk_count); + for (long i = 0; i < chunk_count; i++) + part.chunks.push_back(chunkFactory->create()); + } + world::iterator::last_chunk src_ck; + world::iterator::last_chunk dst_ck{nullptr, chunk_pos(-1)}; + Voxel cur; + for (const auto& full: voxels) { + const auto src_split = glm::splitIdx(full); + if (getter(src_split, cur, src_ck) && cur.is_solid()) { + if (edits) + (*edits)[src_ck.pos].push_back(ChunkEdits::Edit{cleanVoxel, fill.radius * world::iterator::FILL_DELAY, src_split.second}); + + src_ck.ptr->replace(src_split.second, cleanVoxel); + const auto dst_split = glm::splitIdx(full - min); + if (dst_split.first != dst_ck.pos) { + dst_ck.ptr = std::static_pointer_cast(part.findInRange(dst_split.first)); + assert(dst_ck.ptr); + dst_ck.pos = dst_split.first; + } + dst_ck.ptr->set(dst_split.second, cur); + } + } + }; + }; + switch (Elements::GetType(anchor_id)) { + case Elements::Type::Area: { + const auto& chunks = NodeOf::Make(node)->get()->getChunks(); + const auto rtf = Elements::start_point(anchor_id, transform()); + world::iterator::Apply(chunks, fill, applyCB); + world::iterator::Split(chunks, fill, opts.floodFillLimit, splitCBer(rtf, + [&](const std::pair& split, Voxel& out, world::iterator::last_chunk& ck) { + return world::iterator::GetVoxel(chunks, split, out, ck); + } + )); + break; + } + /* TODO: case Elements::Type::Part: { + const auto& chunks = *NodeOf::Make(node)->get()->chunks; + const auto entry = lock->findHierarchy(anchor_id); + const auto rtf = start_point(entry->parent, entry->relative, entry->velocity); + world::iterator::Apply(chunks, fill, applyCB); + world::iterator::Split(chunks, fill, opts.floodFillLimit, splitCBer(rtf, + [&](const std::pair& split, Voxel& out, world::iterator::last_chunk& ck) { + return world::iterator::GetVoxel(chunks, split, out, ck); + } + )); + break; + }*/ + default: + LOG_E("Unhandled fill target type " << (int)Elements::GetType(anchor_id)); + break; + } +} + +world::node_id Universe::addPlayer(std::optional spawn) { + const auto &sp = spawn.value_or(spawnPoint); + const auto CHUNK_LOADER = Elements::Set(0); + return setElements()->createInstance(Elements::start_point(sp), generational::id(0, 0), CHUNK_LOADER).val; +} +void Universe::removePlayer(node_id id) { + //FIXME: save player infos ? + setElements()->remove(id); +} +bool Universe::movePlayer(node_id id, const relative_pos& to) { + if (auto player = getPlayer(id)) { + //auto target = elements.computeAbsolute(elements.findParent(id), world::transform(to.position)); + //TODO: check dist + collision from a to b + setElements()->move(id, to); + return true; } return false; -} - -entity_instance_id Universe::addEntity(entity_id type, const Entity::Instance &instance) { - return std::make_pair(type, entities.at(type).instances.push(instance)); -} - -Universe::Entity::Instance* Universe::findEntity(entity_id type, entity_id id) { - if(!entities.contains(type)) - return nullptr; - - if(!entities.at(type).instances.contains(id)) - return nullptr; - - return &entities.at(type).instances.at(id); -} - -bool Universe::movePlayer(data::generational::id id, glm::ifvec3 pos) { - if (auto player = findEntity(PLAYER_ENTITY_ID, id)) { - const auto initialPos = player->pos.as_voxel(); - if (initialPos == pos.as_voxel()) - return true; - - //TODO: check dist + collision from a to b - movedPlayers.insert(id); - player->pos = pos; - return true; - } else - return false; -} - -std::shared_ptr Universe::createChunk(const chunk_pos &pos, const std::unique_ptr &rnd) const { - return std::make_shared(pos, rnd); -} -std::shared_ptr Universe::createChunk(std::istream &str) const { - return std::make_shared(str); -} -std::shared_ptr Universe::createChunk() const { - return std::make_shared(); } \ No newline at end of file diff --git a/src/server/world/Universe.hpp b/src/server/world/Universe.hpp index fc59fe1..0a9b1b3 100644 --- a/src/server/world/Universe.hpp +++ b/src/server/world/Universe.hpp @@ -1,125 +1,102 @@ #pragma once -#include #include -#include "../../core/world/Universe.hpp" -#include "../../core/world/actions.hpp" -#include "../../core/data/math.hpp" -#include "../../core/data/safe_queue.hpp" -#include "../../core/data/safe_priority_queue.hpp" -#include "../../core/data/file.hpp" -#include "../../core/data/mem.hpp" -#include "../net/Server.hpp" -#include "Area.hpp" +#include "core/world/Universe.hpp" +#include "core/world/actions.hpp" +#include "core/queue/safe_priority_queue.hpp" +#include "core/queue/safe_queue.hpp" +#include "core/world/server_handle.hpp" +#include "./Elements.hpp" +#include "./Chunk.hpp" -namespace buffer { - class Abstract; -} - -using namespace data; /// Universe data namespace world::server { - class Chunk; +class Serializer; +class ChunkFactory; - /// Whole universe container in abstract server - class Universe: public world::Universe { - public: - /// Server config - struct options: world::Universe::options { - /// Storage path - std::string folderPath = "world"; +/// Whole universe container in abstract server +class Universe final: public world::Universe { +public: + /// Server config + struct options: world::Universe::options { + /// Storage path + std::string folderPath = "world"; - net::exposure connection = net::exposure{"", 4242, 1, "content/key.pem", "content/cert.pem"}; + /// Iteration count in part break search + size_t floodFillLimit = CHUNK_SIZE; - /// Iteration count in part break search - size_t floodFillLimit = CHUNK_SIZE; - }; - - Universe(const options &); - virtual ~Universe(); - - /// Update physics - void update(float deltaTime); - /// Send-Receive packets - //NOTE: triggers onPacket, onConnect & onDisconnect - void pull(); - /// Apply new options - void setOptions(const options &); - - /// Set volume of voxel with pos as center - /// MAYBE: allow set multi area - ItemList set(const area_ &pos, int radius, action::Shape shape, const Voxel &val); - - /// Instante entity - entity_instance_id addEntity(entity_id type, const Entity::Instance &instance); - Entity::Instance* findEntity(entity_id type, entity_id id); - - /// Move player - bool movePlayer(data::generational::id id, glm::ifvec3 pos); - - /// Get nearest voxel colliding ray - /// @note ray in world scale - ray_result raycast(const geometry::Ray &ray) const override; - - bool isAreaFree(const area_ &pos, geometry::Shape shape, uint16_t radius) const override; - - /// Check for collision on destination - bool collide_end(const glm::ifvec3 &pos, const glm::vec3 &vel, int density, float radius) const; - /// Check for collision at position - bool collide_point(const glm::ifvec3 &pos, const glm::vec3 &vel, int density) const; - - protected: - /// Save all chunks (saveThread uses virtual calls) - void saveAll(bool remove); - - /// Handle networking requests - std::optional onConnect(net::server::Peer*); - bool onDisconnect(net::server::Peer*, bool, uint16_t); - bool onPacket(net::server::Peer*, const data::out_view&, net::PacketFlags); - void broadcastAreas(); - data::out_buffer serializeChunk(area_, const std::shared_ptr&); - virtual void broadcastMessage(const std::string &); - virtual void broadcastEntities(); - - using area_map = robin_hood::unordered_map>; - - virtual std::shared_ptr createChunk(const chunk_pos &pos, const std::unique_ptr &rnd) const; - virtual std::shared_ptr createChunk(std::istream &str) const; - virtual std::shared_ptr createChunk() const; - - virtual void updateChunk(area_map::iterator&, world::ChunkContainer::iterator&, chunk_pos, float deltaTime); - virtual void loadChunk(area_, chunk_pos, const world::ChunkContainer &); - - robin_hood::unordered_set movedPlayers; - voxel_pos spawnPoint; - - /// Alive areas containing chunks - area_map areas; - - using area_it_t = robin_hood::pair>; - /// Dead areas - data::generational::vector far_areas; - void saveAreas() const; - - data::generational::vector entities; - - bool running = true; - std::vector workers; - safe_priority_queue_map, std::shared_ptr, int, area_hash> loadQueue; //NOTE: consider Area const (getRegion uses mutex) - safe_queue, std::shared_ptr>> loadedQueue; - - using save_task_t = std::pair>>; - data::safe_queue saveQueue; //NOTE: consider Area and Chunk const - - uint16_t loadDistance; - uint16_t keepDistance; - std::string folderPath; - size_t floodFillLimit; - - net::server::Server host; - - data::file_content dict_content; - zstd::dict_set dicts; - zstd::write_ctx dict_write_ctx; + /// Invert probability of random tick + uint randomTick = 256; // each ~30s }; + + Universe(const options&, Serializer *const, world::server_handle *const = nullptr); + virtual ~Universe(); + +#ifdef STANDALONE_SERVER + const Elements* getElements() const { return &elements; } + Elements* setElements() { return &elements; } +#else + mutex::shared_guard::shared_handle getElements() const { return elements.lock_shared(); } + mutex::shared_guard::handle setElements() { return elements.lock(); } +#endif + + /// Update physics + void update(float deltaTime); + /// Save and load from disk + void upgrade(); + + /// Try to extract chunk from area or part + std::shared_ptr findChunk(const node_chunk_pos&) const; + const Node* getPlayer(node_id) const; + Node* setPlayer(node_id); + const Elements::hierarchy_entry* findHierarchy(node_ref r) const { + return getElements()->hierarchy.get(r); + } + + bool isRangeFree(const node_voxel_ref ¢er, const geometry::Volume& volume) const { + return IsRangeFree(*getElements(), center, volume); + } + void fill(const action::FillShape &shape, fill_edits *const edits = nullptr); + + node_id addPlayer(std::optional spawn = std::nullopt); + /// Save and delete + void removePlayer(node_id); + bool movePlayer(node_id, const relative_pos&); + + /// Set volume of voxel with pos as center + //TODO: ItemList set(const cell_pos &pos, int radius, action::Shape shape, const Voxel &val); + //FIXME: ItemList set(const area_voxel_pos &pos, int radius, action::Shape shape, const Voxel &val); + +private: + void loadIndex(); + void writeIndex(); + + world::server_handle *const handle; + const options& opts; + + /// Create custom chunks + const std::unique_ptr chunkFactory; + + /// All element in universe +#ifdef STANDALONE_SERVER + Elements elements; +#else + mutex::shared_guard elements; +#endif + + bool running = true; + std::vector workers; + using load_queue_t = data::safe_priority_queue_map, int>; + load_queue_t loadAreaQueue; //NOTE: consider Area const (getRegion uses mutex) + data::safe_queue>> loadPartQueue; + data::safe_queue>> loadedQueue; + + using area_it_t = std::pair>; + using save_task_t = std::pair>>; + data::safe_queue saveAreaQueue; //NOTE: consider Area and Chunk const + data::safe_queue>> savePartQueue; + + relative_pos spawnPoint{generational::id(), world::world_pos()}; +}; + } \ No newline at end of file diff --git a/src/server/world/client.hpp b/src/server/world/client.hpp new file mode 100644 index 0000000..c385d7a --- /dev/null +++ b/src/server/world/client.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include "core/world/Area.hpp" +#include "core/world/actions.hpp" +#include "core/net/protocol.hpp" +#include "core/queue/sorted_queue.hpp" +#include + +namespace world::server { + +/// Network client state +struct client { + client(size_t a, const std::string& n, node_id e): + accountId(a), name(n), entityId(e) { } + + size_t accountId; + std::string name; + node_id entityId; + + /// List of packet to stream to client + struct { + std::queue edits; + data::sorted_queue chunks; + } pending; + + struct capabilities { + bool handleEdits = false; + } caps; +}; + +} \ No newline at end of file diff --git a/src/server/world/generator.hpp b/src/server/world/generator.hpp deleted file mode 100644 index 7990276..0000000 --- a/src/server/world/generator.hpp +++ /dev/null @@ -1,164 +0,0 @@ -#pragma once - -#include -#include "Noise.hpp" -#include "../../core/world/Voxel.hpp" -#include "../../core/data/math.hpp" - -namespace world::generator { - - /// Abstract Noise generator - class Abstract { - public: - /// Generate chunk voxels - /// MAYBE: use template to avoid virtual - virtual void generate(const chunk_pos &at, std::array &out) = 0; - /// Get gravity vector at given point - virtual glm::vec3 getGravity(const voxel_pos& point) const = 0; - }; - - // Generate empty space - class Void: public Abstract { - public: - struct Params { }; - Void() = default; - - void generate(const chunk_pos &, std::array &out) override { - out.fill(Voxel()); - } - glm::vec3 getGravity(const voxel_pos&) const override { return glm::vec3(0); } - }; - - /// Endless cave network - class Cave: public Abstract { - public: - struct Params { - Params(int seed = 42, float density = 0, float gran = 30): seed(seed), density(density), granularity(gran) { } - - /// Random generator start - int seed; - /// Offset density overrage - float density; - /// Speed of density change - float granularity; - }; - Cave(const Params p): params(p), density(Noise::SimplexFractal(p.seed)), material(Noise::Cellular(p.seed * 5, .1)) { } - - void generate(const chunk_pos &pos, std::array &out) override { - auto densitySet = density.Get(pos, CHUNK_LENGTH); - auto materialSet = material.Get(pos, CHUNK_LENGTH); - for (size_t i = 0; i < CHUNK_SIZE; i++) { - const auto density = std::clamp((densitySet.get()[i] + params.density) * params.granularity, 0.f, 1.f) * Voxel::DENSITY_MAX; - const auto material = density > 0 ? 2 + std::clamp(static_cast(std::lrint((materialSet.get()[i] + 1) / 2 * (materials::count - 3))), - 0, materials::count - 3) : 1; //NOTE: map (approx -1, 1) to (1, mat_max) - out[i] = Voxel(material, density); - } - } - glm::vec3 getGravity(const voxel_pos&) const override { return glm::vec3(-1, 0, 0); } - private: - Params params; - Noise density; - Noise material; - }; - - enum class PlanetShape { Cube, Sphere }; - - /// Abstract shaped planet generator - template - class Planet: public Abstract { - public: - struct Params: Cave::Params { - Params(voxel_pos::value_type height, int seed = 42, float surface_roughness = .1, float depth_roughness = .05, float density = 0, float gran = 30): - Cave::Params(seed, density, gran), height(height), surface_roughness(surface_roughness), depth_roughness(depth_roughness) { } - - /// Sea level (minimal density) - voxel_pos::value_type height; - /// Density decrease over height - float surface_roughness; - /// Density decrease under height - float depth_roughness; - /// Sea border depth - float beach_depth = .01f; - /// Sea ground displacement - float beach_displacement = .01f; - }; - Planet(const Params p) : params(p), density(Noise::SimplexFractal(p.seed)), displacement(Noise::Simplex(p.seed * 5, .01)) {} - - void generate(const chunk_pos &pos, std::array &out) override { - auto densitySet = density.Get(pos, CHUNK_LENGTH); - auto displacementSet = displacement.Get(pos, CHUNK_LENGTH); - for (size_t i = 0; i < CHUNK_SIZE; i++) { - const auto heightRatio = static_cast(getHeight(glm::multiply(pos) + glm::llvec3(glm::fromIdx(i))) - params.height) / params.height; - - const auto verticalDensityOffset = heightRatio / (heightRatio >= 0 ? params.surface_roughness : params.depth_roughness); - const auto density = std::clamp((densitySet.get()[i] + params.density - verticalDensityOffset) * params.granularity, 0.f, 1.f) * Voxel::DENSITY_MAX; - - out[i] = [&]() -> Voxel { - if (density > 0) { - const auto material = [&]() -> int { - const auto noisedHeightRatio = heightRatio - displacementSet.get()[i] * params.beach_displacement; - if(noisedHeightRatio >= 0) { - return densitySet.get()[i] + params.density < ((heightRatio + 0.007f) / params.surface_roughness) ? materials::GRASS : materials::DIRT; - } else { - return noisedHeightRatio >= -params.beach_depth ? materials::SAND : materials::ROCK; - } - }(); - return Voxel(material, density); - } else { - if (heightRatio >= 0) { - return Voxel(materials::AIR, (heightRatio < params.surface_roughness) * Voxel::DENSITY_MAX); - } else { - return Voxel(materials::WATER, std::clamp(-heightRatio * params.height, 0.f, 1.f) * Voxel::DENSITY_MAX); - } - } - }(); - } - } - glm::vec3 getGravity(const voxel_pos& pos) const override { - const auto heightRatio = static_cast((getHeight(pos) - params.height) / params.height); - const auto scale = params.height >> 7; - const auto mass = scale * scale * scale; //FIXME: average material density - return -getSurfaceDir(pos) * std::max(0, 1.f - heightRatio * heightRatio) * glm::vec3(mass); - } - private: - static constexpr PlanetShape shape = PS; - static constexpr glm::f64 getHeight(const voxel_pos &p) { - if constexpr(shape == PlanetShape::Cube) { - return glm::max_axis(glm::abs(p)); - } else { - return glm::length(glm::dvec3(p)); - } - } - /// Centre inverse direction - // NOTE: So center point is consider mass center - static constexpr glm::vec3 getSurfaceDir(const voxel_pos &p) { - if constexpr(shape == PlanetShape::Cube) { - return glm::inormalize(p); - } else { - return glm::normalize(glm::dvec3(p)); - } - } - Params params; - Noise density; - Noise displacement; - }; - using CubicPlanet = Planet; - using RoundPlanet = Planet; - - using params = std::variant; - inline std::unique_ptr load(const params &p) { - const auto get_new = [](const params &p) -> Abstract* { - if(std::holds_alternative(p)) { - return new Void(); - } else if(const auto c = std::get_if(&p)) { - return new Cave(*c); - } else if(const auto pc = std::get_if(&p)) { - return new CubicPlanet(*pc); - } else if(const auto ps = std::get_if(&p)) { - return new RoundPlanet(*ps); - } - return nullptr; - }; - return std::unique_ptr(get_new(p)); - } -} \ No newline at end of file diff --git a/src/server/world/region/File.cpp b/src/server/world/region/File.cpp deleted file mode 100644 index a838031..0000000 --- a/src/server/world/region/File.cpp +++ /dev/null @@ -1,195 +0,0 @@ -#include "File.hpp" - -#include -#include - -using namespace world::server; - -#define REMOVE_CORRUPTED 1 - -FileRegion::FileRegion(const std::string &folderPath, const area_ &pos) { - path = folderPath + '/' + std::to_string(pos.first.index) + '.' + std::to_string(pos.second.x) + '.' + - std::to_string(pos.second.y) + '.' + std::to_string(pos.second.z) + ".map"; - - load(); -} -FileRegion::~FileRegion() { - file.close(); -} - -void FileRegion::load() { - std::unique_lock lock(mutex); - - file.open(path, std::ios::in | std::ios::binary); - if(!file.good()) { - return; - } - // Read header - uint16_t chunkCount; //NOTE: pretty useless - file.read(reinterpret_cast(&chunkCount), sizeof(chunkCount)); - - while (!file.eof()) { - // Read pos - region_chunk_pos pos; - file.read(reinterpret_cast(&pos.x), sizeof(region_chunk_pos::value_type)); - file.read(reinterpret_cast(&pos.y), sizeof(region_chunk_pos::value_type)); - file.read(reinterpret_cast(&pos.z), sizeof(region_chunk_pos::value_type)); - - std::optional avg; - Flags flags = Flags::ZERO; - file.read(reinterpret_cast(&flags), 1); - if (flags & Flags::HAS_AVERAGE) { - Voxel v; - file.read(reinterpret_cast(&v), sizeof(v)); - avg = v; - } - - // Read size - uint16_t size = 0; - if (!(flags & Flags::EMPTY)) { - file.read(reinterpret_cast(&size), sizeof(size)); - } - - // Ignore content - if(!index.emplace(pos, node{avg, size, file.tellg()}).second) { - LOG_E("Duplicated chunk: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z); - } - file.ignore(size); - file.peek(); - } - - if(file.bad()) { - LOG_E("region corrupted read " << path); - } - - assert(index.size() == chunkCount); -} -bool FileRegion::read(const region_chunk_pos& pos, const zstd::read_ctx& ctx, std::vector& out) { - std::unique_lock lock(mutex); - - const auto it = index.find(pos); - if (it == index.end() || it->second.size == 0) - return false; - - std::vector in; - in.resize(it->second.size); - file.seekg(it->second.offset); - file.read(in.data(), in.size()); - - if (auto err = ctx.decompress(in, out)) { - LOG_E("Corrupted region chunk: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z << " " - << err.value()); -#ifdef REMOVE_CORRUPTED - LOG_W("Removing"); - index.erase(it); - lock.unlock(); - save(std::nullopt); -#endif - return false; - } - return true; -} -void FileRegion::write(const region_chunk_pos& pos, const zstd::write_ctx& ctx, const std::string_view& in, const std::optional& avg) { - std::unique_ptr> buffer = nullptr; - if (!in.empty()) { - buffer = std::make_unique>(); - if (auto err = ctx.compress(in, *buffer)) { - LOG_E("Corrupted chunk save: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z << " " - << err.value()); - return; - } - } - save(std::make_optional(to_save{pos, std::move(buffer), avg})); -} - -void FileRegion::save(std::optional added) { - std::unique_lock lock(mutex); - - const auto tmpPath = path + ".tmp"; - - std::ofstream tmpFile(tmpPath, std::ios::out | std::ios::binary); - if (!tmpFile.good()) { - LOG_E("Corrupted region path: " << tmpPath); - return; - } - - { // Write header - uint16_t size = index.size() + (added.has_value() ? 1 : 0); - tmpFile.write(reinterpret_cast(&size), sizeof(size)); - } - - std::vector tmp; - for(const auto& chunk: index) { - { // Write pos - region_chunk_pos pos = chunk.first; - tmpFile.write(reinterpret_cast(&pos.x), sizeof(region_chunk_pos::value_type)); - tmpFile.write(reinterpret_cast(&pos.y), sizeof(region_chunk_pos::value_type)); - tmpFile.write(reinterpret_cast(&pos.z), sizeof(region_chunk_pos::value_type)); - } - - // Write average if present - Flags flags = Flags::ZERO; - if (chunk.second.average.has_value()) - flags = (Flags)(flags | Flags::HAS_AVERAGE); - if (chunk.second.size == 0) - flags = (Flags)(flags | Flags::EMPTY); - tmpFile.write(reinterpret_cast(&flags), 1); - if (flags & Flags::HAS_AVERAGE) { - Voxel v = chunk.second.average.value(); - tmpFile.write(reinterpret_cast(&v), sizeof(v)); - } - - if (!(flags & Flags::EMPTY)) { - // Write size - auto size = chunk.second.size; - tmpFile.write(reinterpret_cast(&size), sizeof(size)); - - // Write content - tmp.resize(size); - file.seekg(chunk.second.offset); - file.read(tmp.data(), size); - tmpFile.write(tmp.data(), size); - } - } - if(added.has_value()) { - { // Write pos - region_chunk_pos pos = added.value().pos; - tmpFile.write(reinterpret_cast(&pos.x), sizeof(region_chunk_pos::value_type)); - tmpFile.write(reinterpret_cast(&pos.y), sizeof(region_chunk_pos::value_type)); - tmpFile.write(reinterpret_cast(&pos.z), sizeof(region_chunk_pos::value_type)); - } - - // Write average if present - Flags flags = Flags::ZERO; - if (added.value().average.has_value()) - flags = (Flags)(flags | Flags::HAS_AVERAGE); - if (added.value().data == nullptr) - flags = (Flags)(flags | Flags::EMPTY); - tmpFile.write(reinterpret_cast(&flags), 1); - if (flags & Flags::HAS_AVERAGE) { - Voxel v = added.value().average.value(); - tmpFile.write(reinterpret_cast(&v), sizeof(v)); - } - - if (!(flags & Flags::EMPTY)) { - // Write size - auto size = added.value().data->size(); - tmpFile.write(reinterpret_cast(&size), sizeof(size)); - - // Write content - tmpFile.write(added.value().data->data(), size); - } - } - - if (!tmpFile.good()) { - LOG_E("Region corrupted write " << tmpPath); - tmpFile.close(); - return; - } - tmpFile.close(); - - index.clear(); - file.close(); - std::filesystem::rename(tmpPath, path); - load(); -} \ No newline at end of file diff --git a/src/server/world/region/File.hpp b/src/server/world/region/File.hpp deleted file mode 100644 index 5b23e50..0000000 --- a/src/server/world/region/File.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#include -#include -#include -#include "../../../core/world/forward.h" -#include "../../../core/world/Voxel.hpp" -#include "../../../core/data/mem.hpp" -#include "../../../core/utils/zctx.hpp" -#include "../../../core/data/math.hpp" - -namespace world::server { - ///Group of chunks saved as a single file only pointer - class FileRegion { - public: - FileRegion(const std::string& folderPath, const area_ &pos); - ~FileRegion(); - - template - void averages(D &out) { - std::shared_lock lock(mutex); - out.resize(index.size() * (sizeof(region_chunk_pos) + sizeof(Voxel))); - data::in_view in((uint8_t*)out.data(), out.size()); - for(const auto& r: index) { - if (r.second.average.has_value()) { - in.write((const uint8_t*)&r.first, sizeof(r.first)); - in.write((const uint8_t*)&r.second.average.value(), sizeof(r.second.average.value())); - } - } - out.resize(in.cur); - } - bool read(const region_chunk_pos &pos, const zstd::read_ctx& ctx, std::vector &out); - void write(const region_chunk_pos &pos, const zstd::write_ctx& ctx, const std::string_view &in, const std::optional& average); - - private: - struct to_save { - region_chunk_pos pos; - std::unique_ptr> data; - std::optional average; - }; - void save(std::optional added); - - std::string path; - //TODO: use tickets to remove unused regions - - enum Flags: unsigned char { - ZERO = 0, - HAS_AVERAGE = 1 << 0, - EMPTY = 1 << 1 - }; - - std::shared_mutex mutex; - std::ifstream file; - struct node { - node(const std::optional& a, uint16_t s, std::streampos o): - average(a), size(s), offset(o) { } - std::optional average; - uint16_t size; - std::streampos offset; - }; - robin_hood::unordered_map index; - - void load(); - }; -} \ No newline at end of file diff --git a/src/server/world/region/Memory.cpp b/src/server/world/region/Memory.cpp deleted file mode 100644 index f918c08..0000000 --- a/src/server/world/region/Memory.cpp +++ /dev/null @@ -1,183 +0,0 @@ -#include "Memory.hpp" - -#include - -using namespace world::server; - -#define REMOVE_CORRUPTED 1 -#define LAZYNESS 8 - -MemoryRegion::MemoryRegion(const std::string &folderPath, const area_ &pos) { - path = folderPath + '/' + std::to_string(pos.first.index) + '.' + std::to_string(pos.second.x) + '.' + - std::to_string(pos.second.y) + '.' + std::to_string(pos.second.z) + ".map"; - - load(); -} -MemoryRegion::~MemoryRegion() { - if(!content.empty()) - save(changed); -} - -void MemoryRegion::load() { - std::unique_lock lock(mutex); - - std::ifstream file; - file.open(path, std::ios::in | std::ios::binary); - if(!file.good()) { - return; - } - - // Read header - uint16_t chunkCount; //NOTE: pretty useless - file.read(reinterpret_cast(&chunkCount), sizeof(chunkCount)); - - while (!file.eof()) { - // Read pos - region_chunk_pos pos; - file.read(reinterpret_cast(&pos.x), sizeof(region_chunk_pos::value_type)); - file.read(reinterpret_cast(&pos.y), sizeof(region_chunk_pos::value_type)); - file.read(reinterpret_cast(&pos.z), sizeof(region_chunk_pos::value_type)); - - // Read average if present - std::optional avg; - Flags flags = Flags::ZERO; - file.read(reinterpret_cast(&flags), 1); - if (flags & Flags::HAS_AVERAGE) { - Voxel v; - file.read(reinterpret_cast(&v), sizeof(v)); - avg = v; - } - - // Read size - uint16_t size = 0; - if (!(flags & Flags::EMPTY)) { - file.read(reinterpret_cast(&size), sizeof(size)); - } - - // Read content - std::unique_ptr> data = nullptr; - if (size > 0) { - data = std::make_unique>(); - data->resize(size); - file.read(data->data(), data->size()); - } - if(!content.emplace(pos, node(avg, std::move(data))).second) { - LOG_E("Duplicated chunk: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z); - } - file.peek(); - } - - if(file.bad()) { - LOG_E("Region corrupted read " << path); - } - assert(content.size() == chunkCount); - file.close(); -} -bool MemoryRegion::read(const region_chunk_pos& pos, const zstd::read_ctx& ctx, std::vector& out) { - std::shared_lock lock(mutex); - - const auto it = content.find(pos); - if (it == content.end() || it->second.data == nullptr) - return false; - - if(auto err = ctx.decompress(*it->second.data.get(), out)) { - LOG_E("Corrupted region chunk: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z << " " - << err.value()); -#ifdef REMOVE_CORRUPTED - LOG_W("Removing"); - lock.unlock(); - { - std::unique_lock ulock(mutex); - content.erase(it); - } - save(true); -#endif - return false; - } - return true; -} -void MemoryRegion::write(const region_chunk_pos& pos, const zstd::write_ctx& ctx, const std::string_view& in, const std::optional& avg) { - std::unique_ptr> buffer = nullptr; - if (!in.empty()) { - buffer = std::make_unique>(); - if (auto err = ctx.compress(in, *buffer.get())) { - LOG_E("Corrupted chunk save: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z << " " - << err.value()); - return; - } - } - { - std::unique_lock lock(mutex); - - // Save buffer - const auto it = content.find(pos); - if (it != content.end()) { - if (buffer == nullptr) { - buffer = std::move(it->second.data); - } - content.erase(it); - } - content.emplace(pos, node(avg, std::move(buffer))); - changed = true; - } - save(false); -} - -void MemoryRegion::save(bool force) { - if(!force && rand() % LAZYNESS == 0) - return; - - std::unique_lock lock(mutex); - - std::ofstream file(path, std::ios::out | std::ios::binary); - if (!file.good()) { - LOG_E("Corrupted region path: " << path); - return; - } - - { // Write header - uint16_t size = (uint16_t)content.size(); - file.write(reinterpret_cast(&size), sizeof(size)); - } - - for(const auto& chunk: content) { - { // Write pos - region_chunk_pos pos = chunk.first; - file.write(reinterpret_cast(&pos.x), sizeof(region_chunk_pos::value_type)); - file.write(reinterpret_cast(&pos.y), sizeof(region_chunk_pos::value_type)); - file.write(reinterpret_cast(&pos.z), sizeof(region_chunk_pos::value_type)); - } - - // Write average if present - Flags flags = Flags::ZERO; - if (chunk.second.average.has_value()) - flags = (Flags)(flags | Flags::HAS_AVERAGE); - if (chunk.second.data == nullptr) - flags = (Flags)(flags | Flags::EMPTY); - file.write(reinterpret_cast(&flags), sizeof(flags)); - if (flags & Flags::HAS_AVERAGE) { - Voxel v = chunk.second.average.value(); - file.write(reinterpret_cast(&v), sizeof(v)); - } - - if (!(flags & Flags::EMPTY)) { - assert(chunk.second.data->size() < USHRT_MAX); - auto size = (uint16_t)chunk.second.data->size(); - const auto out = chunk.second.data->data(); - - // Write size - file.write(reinterpret_cast(&size), sizeof(size)); - - // Write content - file.write(out, size); - } - } - - if (!file.good()) { - LOG_E("Region corrupted write " << path); - file.close(); - return; - } - file.close(); - changed = false; -} \ No newline at end of file diff --git a/src/server/world/region/index.hpp b/src/server/world/region/index.hpp deleted file mode 100644 index 111b823..0000000 --- a/src/server/world/region/index.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#ifdef LOW_MEMORY -#include "File.hpp" -namespace world::server {typedef FileRegion Region;} -#else -#include "Memory.hpp" -namespace world::server {typedef MemoryRegion Region;} -#endif \ No newline at end of file diff --git a/src/tools/generate_models.cpp b/src/tools/generate_models.cpp index a017e89..c05700c 100644 --- a/src/tools/generate_models.cpp +++ b/src/tools/generate_models.cpp @@ -3,9 +3,7 @@ * \brief Serialize model for core/world/models.hpp */ -#define STANDALONE 1 #include "../client/render/api/Models.hpp" -#include "../core/world/materials.hpp" #include #include #include @@ -14,7 +12,7 @@ int main(int, char *[]){ const auto pack = [](const glm::vec3 &pos) { return render::PackedVertexData(meshopt_quantizeHalf(pos.x), meshopt_quantizeHalf(pos.y), - meshopt_quantizeHalf(pos.z), world::materials::textures_map[7], + meshopt_quantizeHalf(pos.z), 0, meshopt_quantizeHalf(pos.x), meshopt_quantizeHalf(pos.y), meshopt_quantizeHalf(pos.z)); };