diff --git a/.clang-tidy b/.clang-tidy index 2062324..bd2c7af 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,211 +1,211 @@ -Checks: " - -*, - bugprone-*, - cert-*, - clang-analyzer-*, - clang-diagnostic-*, - concurrency-*, - cppcoreguidelines-*, - darwin-*, - google-*, - llvm-*, - misc-*, - modernize-*, - performance-*, - readability-*, - hicpp-exception-baseclass, - hicpp-multiway-paths-covered, - hicpp-no-assembler, - portability-std-allocator-const, - -bugprone-easily-swappable-parameters, - -cppcoreguidelines-avoid-magic-numbers, - -cppcoreguidelines-non-private-member-variables-in-classes, - -cppcoreguidelines-avoid-non-const-global-variables, - -cppcoreguidelines-owning-memory, - -google-readability-todo, - -google-readability-braces-around-statements, - -llvm-include-order, - -llvm-header-guard, - -misc-non-private-member-variables-in-classes, - -modernize-use-trailing-return-type, - -readability-braces-around-statements, - -readability-function-cognitive-complexity, - -readability-function-size, - -readability-identifier-length, - -readability-implicit-bool-conversion, - -readability-magic-numbers, - -bugprone-narrowing-conversions, - -cert-con36-c, - -cert-con54-cpp, - -cert-dcl03-c, - -cert-dcl16-c, - -cert-dcl37-c, - -cert-dcl51-cpp, - -cert-dcl54-cpp, - -cert-dcl59-cpp, - -cert-err09-cpp, - -cert-err61-cpp, - -cert-exp42-c, - -cert-fio38-c, - -cert-flp37-c, - -cert-msc30-c, - -cert-msc32-c, - -cert-msc54-cpp, - -cert-oop11-cpp, - -cert-oop54-cpp, - -cert-pos44-c, - -cert-pos47-c, - -cert-sig30-c, - -cert-str34-c, - -clang-analyzer-core.CallAndMessage, - -clang-analyzer-core.DivideZero, - -clang-analyzer-core.NonNullParamChecker, - -clang-analyzer-core.NullDereference, - -clang-analyzer-core.StackAddressEscape, - -clang-analyzer-core.UndefinedBinaryOperatorResult, - -clang-analyzer-core.VLASize, - -clang-analyzer-core.uninitialized.ArraySubscript, - -clang-analyzer-core.uninitialized.Assign, - -clang-analyzer-core.uninitialized.Branch, - -clang-analyzer-core.uninitialized.UndefReturn, - -clang-analyzer-cplusplus.Move, - -clang-analyzer-cplusplus.NewDelete, - -clang-analyzer-cplusplus.NewDeleteLeaks, - -clang-analyzer-deadcode.DeadStores, - -clang-analyzer-nullability.NullPassedToNonnull, - -clang-analyzer-nullability.NullReturnedFromNonnull, - -clang-analyzer-nullability.NullableDereferenced, - -clang-analyzer-nullability.NullablePassedToNonnull, - -clang-analyzer-optin.cplusplus.UninitializedObject, - -clang-analyzer-optin.cplusplus.VirtualCall, - -clang-analyzer-optin.mpi.MPI-Checker, - -clang-analyzer-optin.osx.cocoa.localizability.EmptyLocalizationContextChecker, - -clang-analyzer-optin.osx.cocoa.localizability.NonLocalizedStringChecker, - -clang-analyzer-osx.API, - -clang-analyzer-osx.SecKeychainAPI, - -clang-analyzer-osx.cocoa.AtSync, - -clang-analyzer-osx.cocoa.ClassRelease, - -clang-analyzer-osx.cocoa.Dealloc, - -clang-analyzer-osx.cocoa.IncompatibleMethodTypes, - -clang-analyzer-osx.cocoa.NSAutoreleasePool, - -clang-analyzer-osx.cocoa.NSError, - -clang-analyzer-osx.cocoa.NilArg, - -clang-analyzer-osx.cocoa.ObjCGenerics, - -clang-analyzer-osx.cocoa.RetainCount, - -clang-analyzer-osx.cocoa.SelfInit, - -clang-analyzer-osx.cocoa.SuperDealloc, - -clang-analyzer-osx.cocoa.UnusedIvars, - -clang-analyzer-osx.cocoa.VariadicMethodTypes, - -clang-analyzer-osx.coreFoundation.CFError, - -clang-analyzer-osx.coreFoundation.CFNumber, - -clang-analyzer-osx.coreFoundation.CFRetainRelease, - -clang-analyzer-osx.coreFoundation.containers.OutOfBounds, - -clang-analyzer-osx.coreFoundation.containers.PointerSizedValues, - -clang-analyzer-security.FloatLoopCounter, - -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling, - -clang-analyzer-security.insecureAPI.UncheckedReturn, - -clang-analyzer-security.insecureAPI.bcmp, - -clang-analyzer-security.insecureAPI.bcopy, - -clang-analyzer-security.insecureAPI.bzero, - -clang-analyzer-security.insecureAPI.getpw, - -clang-analyzer-security.insecureAPI.gets, - -clang-analyzer-security.insecureAPI.mkstemp, - -clang-analyzer-security.insecureAPI.mktemp, - -clang-analyzer-security.insecureAPI.rand, - -clang-analyzer-security.insecureAPI.strcpy, - -clang-analyzer-security.insecureAPI.vfork, - -clang-analyzer-unix.API, - -clang-analyzer-unix.Malloc, - -clang-analyzer-unix.MallocSizeof, - -clang-analyzer-unix.MismatchedDeallocator, - -clang-analyzer-unix.Vfork, - -clang-analyzer-unix.cstring.BadSizeArg, - -clang-analyzer-unix.cstring.NullArg, - -cppcoreguidelines-avoid-c-arrays, - -cppcoreguidelines-avoid-magic-numbers, - -cppcoreguidelines-c-copy-assignment-signature, - -cppcoreguidelines-explicit-virtual-functions, - -cppcoreguidelines-macro-to-enum, - -cppcoreguidelines-non-private-member-variables-in-classes, - -fuchsia-header-anon-namespaces, - -google-readability-braces-around-statements, - -google-readability-function-size, - -google-readability-namespace-comments, - -hicpp-avoid-c-arrays, - -hicpp-braces-around-statements, - -hicpp-deprecated-headers, - -hicpp-explicit-conversions, - -hicpp-function-size, - -hicpp-invalid-access-moved, - -hicpp-member-init, - -hicpp-move-const-arg, - -hicpp-named-parameter, - -hicpp-new-delete-operators, - -hicpp-no-array-decay, - -hicpp-no-malloc, - -hicpp-noexcept-move, - -hicpp-special-member-functions, - -hicpp-static-assert, - -hicpp-undelegated-constructor, - -hicpp-uppercase-literal-suffix, - -hicpp-use-auto, - -hicpp-use-emplace, - -hicpp-use-equals-default, - -hicpp-use-equals-delete, - -hicpp-use-noexcept, - -hicpp-use-nullptr, - -hicpp-use-override, - -hicpp-vararg, - -llvm-else-after-return, - -llvm-qualified-auto, - misc-definitions-in-headers, - -modernize-avoid-c-arrays, - -readability-uppercase-literal-suffix - " -CheckOptions: - # - key: readability-identifier-naming.ClassCase - # value: CamelCase - # - key: readability-identifier-naming.EnumCase - # value: CamelCase - # - key: readability-identifier-naming.StructCase - # value: CamelCase - # - key: readability-identifier-naming.UnionCase - # value: CamelCase - # - key: readability-identifier-naming.FunctionCase - # value: camelBack - # - key: readability-identifier-naming.MethodCase - # value: camelBack - # - key: readability-identifier-naming.MemberCase - # value: lower_case - # - key: readability-identifier-naming.ParameterCase - # value: lower_case - # - key: readability-identifier-naming.VariableCase - # value: lower_case - # - key: readability-identifier-naming.TemplateParameterCase - # value: CamelCase - # - key: readability-identifier-naming.TypedefCase - # value: CamelCase - # - key: readability-identifier-naming.TypeAliasCase - # value: CamelCase - # - key: readability-identifier-naming.ConstantCase - # value: CamelCase - # - key: readability-identifier-naming.ConstantPrefix - # value: k - # - key: readability-identifier-naming.LocalConstantCase - # value: lower_case - # - key: readability-identifier-naming.LocalConstantPrefix - # value: "" - # - key: readability-identifier-naming.NamespaceCase - # value: lower_case - # - key: readability-identifier-naming.MacroDefinitionCase - # value: UPPER_CASE - # - key: readability-identifier-naming.PrivateMemberPrefix - # value: _ - - key: HeaderFileExtensions - value: ["x" - - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor - value: true - -DoNo -FormatStyle: "file" -WarningsAsErrors: "*" +Checks: " + -*, + bugprone-*, + cert-*, + clang-analyzer-*, + clang-diagnostic-*, + concurrency-*, + cppcoreguidelines-*, + darwin-*, + google-*, + llvm-*, + misc-*, + modernize-*, + performance-*, + readability-*, + hicpp-exception-baseclass, + hicpp-multiway-paths-covered, + hicpp-no-assembler, + portability-std-allocator-const, + -bugprone-easily-swappable-parameters, + -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-non-private-member-variables-in-classes, + -cppcoreguidelines-avoid-non-const-global-variables, + -cppcoreguidelines-owning-memory, + -google-readability-todo, + -google-readability-braces-around-statements, + -llvm-include-order, + -llvm-header-guard, + -misc-non-private-member-variables-in-classes, + -modernize-use-trailing-return-type, + -readability-braces-around-statements, + -readability-function-cognitive-complexity, + -readability-function-size, + -readability-identifier-length, + -readability-implicit-bool-conversion, + -readability-magic-numbers, + -bugprone-narrowing-conversions, + -cert-con36-c, + -cert-con54-cpp, + -cert-dcl03-c, + -cert-dcl16-c, + -cert-dcl37-c, + -cert-dcl51-cpp, + -cert-dcl54-cpp, + -cert-dcl59-cpp, + -cert-err09-cpp, + -cert-err61-cpp, + -cert-exp42-c, + -cert-fio38-c, + -cert-flp37-c, + -cert-msc30-c, + -cert-msc32-c, + -cert-msc54-cpp, + -cert-oop11-cpp, + -cert-oop54-cpp, + -cert-pos44-c, + -cert-pos47-c, + -cert-sig30-c, + -cert-str34-c, + -clang-analyzer-core.CallAndMessage, + -clang-analyzer-core.DivideZero, + -clang-analyzer-core.NonNullParamChecker, + -clang-analyzer-core.NullDereference, + -clang-analyzer-core.StackAddressEscape, + -clang-analyzer-core.UndefinedBinaryOperatorResult, + -clang-analyzer-core.VLASize, + -clang-analyzer-core.uninitialized.ArraySubscript, + -clang-analyzer-core.uninitialized.Assign, + -clang-analyzer-core.uninitialized.Branch, + -clang-analyzer-core.uninitialized.UndefReturn, + -clang-analyzer-cplusplus.Move, + -clang-analyzer-cplusplus.NewDelete, + -clang-analyzer-cplusplus.NewDeleteLeaks, + -clang-analyzer-deadcode.DeadStores, + -clang-analyzer-nullability.NullPassedToNonnull, + -clang-analyzer-nullability.NullReturnedFromNonnull, + -clang-analyzer-nullability.NullableDereferenced, + -clang-analyzer-nullability.NullablePassedToNonnull, + -clang-analyzer-optin.cplusplus.UninitializedObject, + -clang-analyzer-optin.cplusplus.VirtualCall, + -clang-analyzer-optin.mpi.MPI-Checker, + -clang-analyzer-optin.osx.cocoa.localizability.EmptyLocalizationContextChecker, + -clang-analyzer-optin.osx.cocoa.localizability.NonLocalizedStringChecker, + -clang-analyzer-osx.API, + -clang-analyzer-osx.SecKeychainAPI, + -clang-analyzer-osx.cocoa.AtSync, + -clang-analyzer-osx.cocoa.ClassRelease, + -clang-analyzer-osx.cocoa.Dealloc, + -clang-analyzer-osx.cocoa.IncompatibleMethodTypes, + -clang-analyzer-osx.cocoa.NSAutoreleasePool, + -clang-analyzer-osx.cocoa.NSError, + -clang-analyzer-osx.cocoa.NilArg, + -clang-analyzer-osx.cocoa.ObjCGenerics, + -clang-analyzer-osx.cocoa.RetainCount, + -clang-analyzer-osx.cocoa.SelfInit, + -clang-analyzer-osx.cocoa.SuperDealloc, + -clang-analyzer-osx.cocoa.UnusedIvars, + -clang-analyzer-osx.cocoa.VariadicMethodTypes, + -clang-analyzer-osx.coreFoundation.CFError, + -clang-analyzer-osx.coreFoundation.CFNumber, + -clang-analyzer-osx.coreFoundation.CFRetainRelease, + -clang-analyzer-osx.coreFoundation.containers.OutOfBounds, + -clang-analyzer-osx.coreFoundation.containers.PointerSizedValues, + -clang-analyzer-security.FloatLoopCounter, + -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling, + -clang-analyzer-security.insecureAPI.UncheckedReturn, + -clang-analyzer-security.insecureAPI.bcmp, + -clang-analyzer-security.insecureAPI.bcopy, + -clang-analyzer-security.insecureAPI.bzero, + -clang-analyzer-security.insecureAPI.getpw, + -clang-analyzer-security.insecureAPI.gets, + -clang-analyzer-security.insecureAPI.mkstemp, + -clang-analyzer-security.insecureAPI.mktemp, + -clang-analyzer-security.insecureAPI.rand, + -clang-analyzer-security.insecureAPI.strcpy, + -clang-analyzer-security.insecureAPI.vfork, + -clang-analyzer-unix.API, + -clang-analyzer-unix.Malloc, + -clang-analyzer-unix.MallocSizeof, + -clang-analyzer-unix.MismatchedDeallocator, + -clang-analyzer-unix.Vfork, + -clang-analyzer-unix.cstring.BadSizeArg, + -clang-analyzer-unix.cstring.NullArg, + -cppcoreguidelines-avoid-c-arrays, + -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-c-copy-assignment-signature, + -cppcoreguidelines-explicit-virtual-functions, + -cppcoreguidelines-macro-to-enum, + -cppcoreguidelines-non-private-member-variables-in-classes, + -fuchsia-header-anon-namespaces, + -google-readability-braces-around-statements, + -google-readability-function-size, + -google-readability-namespace-comments, + -hicpp-avoid-c-arrays, + -hicpp-braces-around-statements, + -hicpp-deprecated-headers, + -hicpp-explicit-conversions, + -hicpp-function-size, + -hicpp-invalid-access-moved, + -hicpp-member-init, + -hicpp-move-const-arg, + -hicpp-named-parameter, + -hicpp-new-delete-operators, + -hicpp-no-array-decay, + -hicpp-no-malloc, + -hicpp-noexcept-move, + -hicpp-special-member-functions, + -hicpp-static-assert, + -hicpp-undelegated-constructor, + -hicpp-uppercase-literal-suffix, + -hicpp-use-auto, + -hicpp-use-emplace, + -hicpp-use-equals-default, + -hicpp-use-equals-delete, + -hicpp-use-noexcept, + -hicpp-use-nullptr, + -hicpp-use-override, + -hicpp-vararg, + -llvm-else-after-return, + -llvm-qualified-auto, + misc-definitions-in-headers, + -modernize-avoid-c-arrays, + -readability-uppercase-literal-suffix + " +CheckOptions: + # - key: readability-identifier-naming.ClassCase + # value: CamelCase + # - key: readability-identifier-naming.EnumCase + # value: CamelCase + # - key: readability-identifier-naming.StructCase + # value: CamelCase + # - key: readability-identifier-naming.UnionCase + # value: CamelCase + # - key: readability-identifier-naming.FunctionCase + # value: camelBack + # - key: readability-identifier-naming.MethodCase + # value: camelBack + # - key: readability-identifier-naming.MemberCase + # value: lower_case + # - key: readability-identifier-naming.ParameterCase + # value: lower_case + # - key: readability-identifier-naming.VariableCase + # value: lower_case + # - key: readability-identifier-naming.TemplateParameterCase + # value: CamelCase + # - key: readability-identifier-naming.TypedefCase + # value: CamelCase + # - key: readability-identifier-naming.TypeAliasCase + # value: CamelCase + # - key: readability-identifier-naming.ConstantCase + # value: CamelCase + # - key: readability-identifier-naming.ConstantPrefix + # value: k + # - key: readability-identifier-naming.LocalConstantCase + # value: lower_case + # - key: readability-identifier-naming.LocalConstantPrefix + # value: "" + # - key: readability-identifier-naming.NamespaceCase + # value: lower_case + # - key: readability-identifier-naming.MacroDefinitionCase + # value: UPPER_CASE + # - key: readability-identifier-naming.PrivateMemberPrefix + # value: _ + - key: HeaderFileExtensions + value: ["x" + - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor + value: true + +DoNo +FormatStyle: "file" +WarningsAsErrors: "*" diff --git a/.gitignore b/.gitignore index cff2ee6..673f44b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,7 @@ build .vscode # imgui -imgui.ini \ No newline at end of file +imgui.ini + +# trace +*.trace \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 8335da4..01335f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,11 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED True) # Add source files -file(GLOB SOURCES "src/*.cpp") +file(GLOB SOURCES "src/main.cpp") + +if(WIN32) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Xlinker /errorlimit:0") +endif() # Add GLAD add_library(glad libs/glad/src/glad.c) diff --git a/README.md b/README.md index 658bc7c..7b4e6d0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,11 @@ # Voxel Engine -![frustum-demo (1)](https://github.com/user-attachments/assets/d3c4ed18-d3ca-42a8-bc0a-ac5dd6c6debc) +![preview](https://github.com/user-attachments/assets/f4f6eb2c-5e47-4f15-8a8e-db896befeffd) +## Overview +![overview](assets/overview.png) + +### Instructions Make sure to clone this repository recursively! ``` diff --git a/assets/overview.png b/assets/overview.png new file mode 100644 index 0000000..6b279e4 Binary files /dev/null and b/assets/overview.png differ diff --git a/src/Camera.h b/src/Camera.h new file mode 100644 index 0000000..a168954 --- /dev/null +++ b/src/Camera.h @@ -0,0 +1,78 @@ +#ifndef CAMERA_H +#define CAMERA_H + +#include "Frustum.h" + +// Frustum createFrustumFromCamera(Camera camera); + +struct Camera { + // TODO: figure out camera following + // Entity* attachedTo; + bool isFree = true; + // camera + float cameraSpeedMultiplier = 20.0f; + glm::vec3 cameraPos = glm::vec3(0.0f, 2.0f, 1.0f); + glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f); + glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f); + + glm::vec3 cameraRight = glm::vec3(1.0f, 0.0f, 0.0f); + + // Calculate the left vector (opposite of right) + glm::vec3 cameraLeft = -cameraRight; + + // The top vector is the same as the up vector in this case + glm::vec3 cameraTop = cameraUp; + + bool firstMouse = true; + float yaw = -90.0f; // yaw is initialized to -90.0 degrees since a yaw of + // 0.0 results in a direction vector pointing to the + // right so we initially rotate a bit to the left. + float pitch = 0.0f; + float lastX = 800.0f / 2.0; + float lastY = 600.0 / 2.0; + float fov = 45.0f; + + float zNear = 0.1f; + float zFar = 1000.0f; + + Frustum frustum; + + Camera(); +}; + +Frustum createFrustumFromCamera(Camera camera) { + Frustum frustum; + + float aspect = (float)SCR_WIDTH / (float)SCR_HEIGHT; + const float halfVSide = + camera.zFar * tanf((float)glm::radians(camera.fov) * .5f); + const float halfHSide = halfVSide * aspect; + const glm::vec3 frontMultFar = camera.zFar * camera.cameraFront; + + frustum.planes[Frustum::FRUSTUM_NEAR] = + Plane3(camera.cameraPos + camera.zNear * camera.cameraFront, + camera.cameraFront); + camera.frustum.planes[Frustum::FRUSTUM_FAR] = + Plane3(camera.cameraPos + frontMultFar, -camera.cameraFront); + camera.frustum.planes[Frustum::FRUSTUM_RIGHT] = + Plane3(camera.cameraPos, + glm::cross(frontMultFar - camera.cameraRight * halfHSide, + camera.cameraUp)); + frustum.planes[Frustum::FRUSTUM_LEFT] = + Plane3(camera.cameraPos, + glm::cross(camera.cameraUp, + frontMultFar + camera.cameraRight * halfHSide)); + frustum.planes[Frustum::FRUSTUM_TOP] = + Plane3(camera.cameraPos, + glm::cross(camera.cameraRight, + frontMultFar - camera.cameraUp * halfVSide)); + frustum.planes[Frustum::FRUSTUM_BOTTOM] = Plane3( + camera.cameraPos, glm::cross(frontMultFar + camera.cameraUp * halfVSide, + camera.cameraRight)); + + return frustum; +} + +Camera::Camera() { frustum = createFrustumFromCamera(*this); } + +#endif // CAMERA_H \ No newline at end of file diff --git a/src/Chunk.h b/src/Chunk.h index 1167135..5a8a859 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -4,7 +4,6 @@ #include "ChunkMesh.h" #include #include -#include /* TODO LIST: @@ -39,7 +38,7 @@ struct Chunk { void unload(); void rebuildMesh(); void setup(); - void render(); + void render(Camera camera); // BoundingBox getBoundingBox(); void initialize(); void AddCubeFace(ChunkMesh *mesh, int p1, int p2, int p3, int p4, @@ -148,7 +147,7 @@ void Chunk::setup() { } // renders the chunk -void Chunk::render() { DrawChunkMesh(mesh, material, chunkPosition); } +void Chunk::render(Camera camera) { DrawChunkMesh(camera, mesh, material, chunkPosition); } // BoundingBox Chunk::getBoundingBox() { // glm::vec3 max = {chunkPosition.x + CHUNK_SIZE * Block::BLOCK_RENDER_SIZE, @@ -164,8 +163,10 @@ void Chunk::initialize() { for (int y = 0; y < CHUNK_SIZE; y++) { for (int z = 0; z < CHUNK_SIZE; z++) { int index = getIndex(x, y, z); - blocks[index].isActive = (rand() % 2 == 0) ? false : true; - blocks[index].blockType = (rand() % 2 == 0) ? BlockType::Grass : BlockType::Sand; + // NOTE: there seems to be a "pattern" in some chunks - is the same seed be initted across threads? + // seems like it does: https://en.cppreference.com/w/cpp/numeric/random/rand + blocks[index].isActive = (std::rand() % 2 == 0) ? false : true; + blocks[index].blockType = (std::rand() % 2 == 0) ? BlockType::Grass : BlockType::Sand; // blocks[index].isActive = true; } } diff --git a/src/ChunkManager.h b/src/ChunkManager.h index 949a0ae..e84221c 100644 --- a/src/ChunkManager.h +++ b/src/ChunkManager.h @@ -1,6 +1,8 @@ #ifndef CHUNKMANAGER_H #define CHUNKMANAGER_H + #include "Chunk.h" + #include #include #include @@ -73,16 +75,15 @@ struct ChunkManager { ChunkManager(unsigned int _chunkGenDistance, unsigned int _chunkRenderDistance, Shader *_terrainShader); ~ChunkManager(); - void update(float dt, glm::vec3 newCameraPosition, - glm::vec3 newCameraLookAt); - void updateAsyncChunker(glm::vec3 newCameraPosition); + void update(float dt, Camera newCamera); + void updateAsyncChunker(Camera newCamera); void updateLoadList(); void updateSetupList(); void updateRebuildList(); void updateFlagsList(); void updateUnloadList(glm::vec3 newCameraPosition); void updateVisibilityList(glm::vec3 newCameraPosition); - void updateRenderList(glm::vec3 newCameraPosition); + void updateRenderList(glm::vec3 newCameraPosition, Frustum frustum); void pregenerateChunks(); @@ -91,7 +92,7 @@ struct ChunkManager { GetChunkGenRange(glm::vec3 newCameraPosition); std::pair GetChunkRenderRange(glm::vec3 newCameraPosition); - void render(); + void render(Camera newCamera); Shader *terrainShader; @@ -104,8 +105,7 @@ struct ChunkManager { bool genChunk; bool forceVisibilityupdate; - glm::vec3 cameraPosition; - glm::vec3 cameraLookAt; + Camera camera; unsigned int chunkGenDistance; unsigned int chunkRenderDistance; @@ -130,8 +130,8 @@ ChunkManager::ChunkManager(unsigned int _chunkGenDistance, ChunkManager::~ChunkManager() {} -void ChunkManager::update(float dt, glm::vec3 newCameraPosition, - glm::vec3 newCameraLookAt) { +// TODO: surely we can just pass the camera right? +void ChunkManager::update(float dt, Camera newCamera) { // if (genChunk) { // updateAsyncChunker(newCameraPosition); // // asyncChunkFuture = std::async(&ChunkManager::updateAsyncChunker, @@ -145,10 +145,11 @@ void ChunkManager::update(float dt, glm::vec3 newCameraPosition, updateRebuildList(); // updateFlagsList(); // updateUnloadList(newCameraPosition); - updateVisibilityList(newCameraPosition); - updateRenderList(newCameraPosition); - cameraPosition = newCameraPosition; - cameraLookAt = newCameraLookAt; + updateVisibilityList(newCamera.cameraPos); + updateRenderList(newCamera.cameraPos, newCamera.frustum); + camera = newCamera; + // cameraPosition = camera.cameraPos; + // cameraLookAt = newCameraLookAt; } float roundUp(float number, float fixedBase) { @@ -222,7 +223,7 @@ ChunkManager::GetChunkRenderRange(glm::vec3 newCameraPosition) { void ChunkManager::pregenerateChunks() { int halfWorldSize = - (WORLD_SIZE * (Chunk::CHUNK_SIZE * Block::BLOCK_RENDER_SIZE)) / 2; + (WORLD_SIZE * (Chunk::CHUNK_SIZE * Block::BLOCK_RENDER_SIZE)) / 2; std::vector> futures; // Store futures to manage threads @@ -267,13 +268,13 @@ void ChunkManager::pregenerateChunks() { } } -void ChunkManager::updateAsyncChunker(glm::vec3 newCameraPosition) { - if (newCameraPosition == cameraPosition) { +void ChunkManager::updateAsyncChunker(Camera newCamera) { + if (newCamera.cameraPos == camera.cameraPos) { return; } std::pair chunkRange = - GetChunkGenRange(newCameraPosition); + GetChunkGenRange(camera.cameraPos); glm::vec3 start = chunkRange.first; glm::vec3 end = chunkRange.second; @@ -444,7 +445,8 @@ void ChunkManager::updateRebuildList() { // chunkUnloadList.clear(); // } -void ChunkManager::updateRenderList(glm::vec3 newCameraPosition) { +void ChunkManager::updateRenderList(glm::vec3 newCameraPosition, + Frustum frustum) { // Clear the render list each frame BEFORE we do our tests to see what // chunks should be rendered chunkRenderList.clear(); @@ -497,9 +499,9 @@ void ChunkManager::updateVisibilityList(glm::vec3 newCameraPosition) { } } -void ChunkManager::render() { +void ChunkManager::render(Camera newCamera) { for (Chunk *chunk : chunkRenderList) { - chunk->render(); + chunk->render(newCamera); } } diff --git a/src/ChunkMesh.h b/src/ChunkMesh.h index 751b88f..097abcb 100644 --- a/src/ChunkMesh.h +++ b/src/ChunkMesh.h @@ -1,7 +1,9 @@ #ifndef MESH_H #define MESH_H -#include "smolgl.h" +// #include "smolgl.h" +#include "Camera.h" + #include #include #include @@ -124,14 +126,14 @@ void UnloadChunkMesh(ChunkMesh mesh) { free(mesh.indices); } -void DrawChunkMesh(ChunkMesh mesh, Material material, glm::vec3 position) { +void DrawChunkMesh(Camera camera, ChunkMesh mesh, Material material, glm::vec3 position) { material.shader->use(); glm::mat4 projection = glm::perspective( - glm::radians(fov), (float)SCR_WIDTH / SCR_HEIGHT, zNear, zFar); + glm::radians(camera.fov), (float)SCR_WIDTH / SCR_HEIGHT, camera.zNear, camera.zFar); material.shader->setMat4("projection", projection); - glm::mat4 view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp); + glm::mat4 view = glm::lookAt(camera.cameraPos, camera.cameraPos + camera.cameraFront, camera.cameraUp); material.shader->setMat4("view", view); glm::mat4 model = glm::mat4(1.0f); diff --git a/src/Component.h b/src/Component.h new file mode 100644 index 0000000..0fbf748 --- /dev/null +++ b/src/Component.h @@ -0,0 +1,34 @@ +#ifndef COMPONENT_H +#define COMPONENT_H + +#include +#include + +#include +#include +#include + +using ComponentType = std::uint8_t; + +const ComponentType MAX_COMPONENTS = 32; +using Signature = std::bitset; + +struct Gravity +{ + glm::vec3 force; +}; + +struct RigidBody +{ + glm::vec3 velocity; + glm::vec3 acceleration; +}; + +struct Transform +{ + glm::vec3 position; + glm::quat rotation; + glm::vec3 scale; +}; + +#endif // COMPONENT_H \ No newline at end of file diff --git a/src/Ecs.h b/src/Ecs.h new file mode 100644 index 0000000..ef765d5 --- /dev/null +++ b/src/Ecs.h @@ -0,0 +1,357 @@ +#ifndef ECS_H +#define ECS_H + +#include +#include +#include +#include +#include +#include +#include + +#include "Component.h" +#include "ChunkManager.h" + +using Entity = std::uint32_t; +const Entity MAX_ENTITIES = 5000; + +struct EntityManager { + // Queue of unused entity IDs + std::queue mAvailableEntities{}; + + // Array of signatures where the index corresponds to the entity ID + std::array mSignatures{}; + + // Total living entities - used to keep limits on how many exist + uint32_t mLivingEntityCount{}; + EntityManager() { + // Initialize the queue with all possible entity IDs + for (Entity entity = 0; entity < MAX_ENTITIES; ++entity) { + mAvailableEntities.push(entity); + } + } + + Entity CreateEntity() { + assert(mLivingEntityCount < MAX_ENTITIES && + "Too many entities in existence."); + + // Take an ID from the front of the queue + Entity id = mAvailableEntities.front(); + mAvailableEntities.pop(); + ++mLivingEntityCount; + + return id; + } + + void DestroyEntity(Entity entity) { + assert(entity < MAX_ENTITIES && "Entity out of range."); + + // Invalidate the destroyed entity's signature + mSignatures[entity].reset(); + + // Put the destroyed ID at the back of the queue + mAvailableEntities.push(entity); + --mLivingEntityCount; + } + + void SetSignature(Entity entity, Signature signature) { + assert(entity < MAX_ENTITIES && "Entity out of range."); + + // Put this entity's signature into the array + mSignatures[entity] = signature; + } + + Signature GetSignature(Entity entity) { + assert(entity < MAX_ENTITIES && "Entity out of range."); + + // Get this entity's signature from the array + return mSignatures[entity]; + } +}; + +struct IComponentArray { + public: + ~IComponentArray() = default; + void EntityDestroyed(Entity entity); +}; + +template struct ComponentArray : public IComponentArray { + void InsertData(Entity entity, T component) { + assert(mEntityToIndexMap.find(entity) == mEntityToIndexMap.end() && + "Component added to same entity more than once."); + + // Put new entry at end and update the maps + size_t newIndex = mSize; + mEntityToIndexMap[entity] = newIndex; + mIndexToEntityMap[newIndex] = entity; + mComponentArray[newIndex] = component; + ++mSize; + } + + void RemoveData(Entity entity) { + assert(mEntityToIndexMap.find(entity) != mEntityToIndexMap.end() && + "Removing non-existent component."); + + // Copy element at end into deleted element's place to maintain density + size_t indexOfRemovedEntity = mEntityToIndexMap[entity]; + size_t indexOfLastElement = mSize - 1; + mComponentArray[indexOfRemovedEntity] = + mComponentArray[indexOfLastElement]; + + // Update map to point to moved spot + Entity entityOfLastElement = mIndexToEntityMap[indexOfLastElement]; + mEntityToIndexMap[entityOfLastElement] = indexOfRemovedEntity; + mIndexToEntityMap[indexOfRemovedEntity] = entityOfLastElement; + + mEntityToIndexMap.erase(entity); + mIndexToEntityMap.erase(indexOfLastElement); + + --mSize; + } + + T &GetData(Entity entity) { + assert(mEntityToIndexMap.find(entity) != mEntityToIndexMap.end() && + "Retrieving non-existent component."); + + // Return a reference to the entity's component + return mComponentArray[mEntityToIndexMap[entity]]; + } + + void EntityDestroyed(Entity entity) { + if (mEntityToIndexMap.find(entity) != mEntityToIndexMap.end()) { + // Remove the entity's component if it existed + RemoveData(entity); + } + } + + // The packed array of components (of generic type T), + // set to a specified maximum amount, matching the maximum number + // of entities allowed to exist simultaneously, so that each entity + // has a unique spot. + std::array mComponentArray; + + // Map from an entity ID to an array index. + std::unordered_map mEntityToIndexMap; + + // Map from an array index to an entity ID. + std::unordered_map mIndexToEntityMap; + + // Total size of valid entries in the array. + size_t mSize; +}; + +struct ComponentManager { + template void RegisterComponent() { + const char *typeName = typeid(T).name(); + + assert(mComponentTypes.find(typeName) == mComponentTypes.end() && + "Registering component type more than once."); + + // Add this component type to the component type map + mComponentTypes.insert({typeName, mNextComponentType}); + + // Create a ComponentArray pointer and add it to the component arrays + // map + mComponentArrays.insert( + {typeName, std::make_shared>()}); + + // Increment the value so that the next component registered will be + // different + ++mNextComponentType; + } + + template ComponentType GetComponentType() { + const char *typeName = typeid(T).name(); + + assert(mComponentTypes.find(typeName) != mComponentTypes.end() && + "Component not registered before use."); + + // Return this component's type - used for creating signatures + return mComponentTypes[typeName]; + } + + template void AddComponent(Entity entity, T component) { + // Add a component to the array for an entity + GetComponentArray()->InsertData(entity, component); + } + + template void RemoveComponent(Entity entity) { + // Remove a component from the array for an entity + GetComponentArray()->RemoveData(entity); + } + + template T &GetComponent(Entity entity) { + // Get a reference to a component from the array for an entity + return GetComponentArray()->GetData(entity); + } + + void EntityDestroyed(Entity entity) { + // Notify each component array that an entity has been destroyed + // If it has a component for that entity, it will remove it + for (auto const &pair : mComponentArrays) { + auto const &component = pair.second; + + component->EntityDestroyed(entity); + } + } + + // Map from type string pointer to a component type + std::unordered_map mComponentTypes{}; + + // Map from type string pointer to a component array + std::unordered_map> + mComponentArrays{}; + + // The component type to be assigned to the next registered component - + // starting at 0 + ComponentType mNextComponentType{}; + + // Convenience function to get the statically casted pointer to the + // ComponentArray of type T. + template + std::shared_ptr> GetComponentArray() { + const char *typeName = typeid(T).name(); + + assert(mComponentTypes.find(typeName) != mComponentTypes.end() && + "Component not registered before use."); + + return std::static_pointer_cast>( + mComponentArrays[typeName]); + } +}; + +struct System { + public: + std::set mEntities; +}; + +struct SystemManager { + template std::shared_ptr RegisterSystem() { + const char *typeName = typeid(T).name(); + + assert(mSystems.find(typeName) == mSystems.end() && + "Registering system more than once."); + + // Create a pointer to the system and return it so it can be used + // externally + auto system = std::make_shared(); + mSystems.insert({typeName, system}); + return system; + } + + template void SetSignature(Signature signature) { + const char *typeName = typeid(T).name(); + + assert(mSystems.find(typeName) != mSystems.end() && + "System used before registered."); + + // Set the signature for this system + mSignatures.insert({typeName, signature}); + } + + void EntityDestroyed(Entity entity) { + // Erase a destroyed entity from all system lists + // mEntities is a set so no check needed + for (auto const &pair : mSystems) { + auto const &system = pair.second; + + system->mEntities.erase(entity); + } + } + + void EntitySignatureChanged(Entity entity, Signature entitySignature) { + // Notify each system that an entity's signature changed + for (auto const &pair : mSystems) { + auto const &type = pair.first; + auto const &system = pair.second; + auto const &systemSignature = mSignatures[type]; + + // Entity signature matches system signature - insert into set + if ((entitySignature & systemSignature) == systemSignature) { + system->mEntities.insert(entity); + } + // Entity signature does not match system signature - erase from set + else { + system->mEntities.erase(entity); + } + } + } + + // Map from system type string pointer to a signature + std::unordered_map mSignatures{}; + + // Map from system type string pointer to a system pointer + std::unordered_map> mSystems{}; +}; + +struct Coordinator { + void Init(ChunkManager* chunkManager) { + // Create pointers to each manager + mComponentManager = std::make_unique(); + mEntityManager = std::make_unique(); + mSystemManager = std::make_unique(); + mChunkManager = chunkManager; + mCamera = Camera(); + } + + // Entity methods + Entity CreateEntity() { return mEntityManager->CreateEntity(); } + + void DestroyEntity(Entity entity) { + mEntityManager->DestroyEntity(entity); + + mComponentManager->EntityDestroyed(entity); + + mSystemManager->EntityDestroyed(entity); + } + + // Component methods + template void RegisterComponent() { + mComponentManager->RegisterComponent(); + } + + template void AddComponent(Entity entity, T component) { + mComponentManager->AddComponent(entity, component); + + auto signature = mEntityManager->GetSignature(entity); + signature.set(mComponentManager->GetComponentType(), true); + mEntityManager->SetSignature(entity, signature); + + mSystemManager->EntitySignatureChanged(entity, signature); + } + + template void RemoveComponent(Entity entity) { + mComponentManager->RemoveComponent(entity); + + auto signature = mEntityManager->GetSignature(entity); + signature.set(mComponentManager->GetComponentType(), false); + mEntityManager->SetSignature(entity, signature); + + mSystemManager->EntitySignatureChanged(entity, signature); + } + + template T &GetComponent(Entity entity) { + return mComponentManager->GetComponent(entity); + } + + template ComponentType GetComponentType() { + return mComponentManager->GetComponentType(); + } + + // System methods + template std::shared_ptr RegisterSystem() { + return mSystemManager->RegisterSystem(); + } + + template void SetSystemSignature(Signature signature) { + mSystemManager->SetSignature(signature); + } + + std::unique_ptr mComponentManager; + std::unique_ptr mEntityManager; + std::unique_ptr mSystemManager; + ChunkManager* mChunkManager; + Camera mCamera; +}; + +#endif // ECS_H \ No newline at end of file diff --git a/src/Frustum.h b/src/Frustum.h index da99415..eebe26d 100644 --- a/src/Frustum.h +++ b/src/Frustum.h @@ -1,7 +1,9 @@ #ifndef FRUSTUM_H #define FRUSTUM_H +// #include "Camera.h" #include "smolgl.h" + #include #include #include @@ -54,7 +56,7 @@ struct Frustum { int SphereInFrustum(const glm::vec3 &point, float radius); int CubeInFrustum(const glm::vec3 ¢er, float x, float y, float z); - static enum { + enum { FRUSTUM_TOP = 0, FRUSTUM_BOTTOM, FRUSTUM_LEFT, @@ -63,7 +65,7 @@ struct Frustum { FRUSTUM_FAR, }; - static enum { + enum { FRUSTUM_OUTSIDE = 0, FRUSTUM_INTERSECT, FRUSTUM_INSIDE, @@ -80,33 +82,6 @@ struct Frustum { Frustum::Frustum() {} Frustum::~Frustum() {} -Frustum createFrustumFromCamera(float aspect, float fovY, float zNear, - float zFar) { - Frustum frustum; - const float halfVSide = zFar * tanf((float)glm::radians(fovY) * .5f); - const float halfHSide = halfVSide * aspect; - const glm::vec3 frontMultFar = zFar * cameraFront; - - frustum.planes[Frustum::FRUSTUM_NEAR] = - Plane3(cameraPos + zNear * cameraFront, cameraFront); - frustum.planes[Frustum::FRUSTUM_FAR] = - Plane3(cameraPos + frontMultFar, -cameraFront); - frustum.planes[Frustum::FRUSTUM_RIGHT] = - Plane3(cameraPos, - glm::cross(frontMultFar - cameraRight * halfHSide, cameraUp)); - frustum.planes[Frustum::FRUSTUM_LEFT] = - Plane3(cameraPos, - glm::cross(cameraUp, frontMultFar + cameraRight * halfHSide)); - frustum.planes[Frustum::FRUSTUM_TOP] = - Plane3(cameraPos, - glm::cross(cameraRight, frontMultFar - cameraUp * halfVSide)); - frustum.planes[Frustum::FRUSTUM_BOTTOM] = - Plane3(cameraPos, - glm::cross(frontMultFar + cameraUp * halfVSide, cameraRight)); - - return frustum; -} - int Frustum::SphereInFrustum(const glm::vec3 &point, float radius) { int result = FRUSTUM_INSIDE; float distance; diff --git a/src/PhysicsSystem.h b/src/PhysicsSystem.h new file mode 100644 index 0000000..d61166c --- /dev/null +++ b/src/PhysicsSystem.h @@ -0,0 +1,39 @@ +#ifndef PHYSICSSYSTEM_H +#define PHYSICSSYSTEM_H + +#include "Ecs.h" + +#include "Component.h" +// #include "Components/Gravity.hpp" +// #include "Components/RigidBody.hpp" +// #include "Components/Thrust.hpp" +// #include "Components/Transform.hpp" +// #include "Core/Coordinator.hpp" + +class PhysicsSystem : public System +{ +public: + void Init(); + + void Update(float dt); +}; + +extern Coordinator gCoordinator; + +void PhysicsSystem::Init() {} + +void PhysicsSystem::Update(float dt) { + for (auto const &entity : mEntities) { + auto &rigidBody = gCoordinator.GetComponent(entity); + auto &transform = gCoordinator.GetComponent(entity); + + // Forces + auto const &gravity = gCoordinator.GetComponent(entity); + + transform.position += rigidBody.velocity * dt; + + rigidBody.velocity += gravity.force * dt; + } +} + +#endif // PHYSICSSYSTEM_H \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 375f6e3..83a8d42 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,11 +11,10 @@ #include -#include "ChunkManager.h" -#include "smolgl.h" +#include "Ecs.h" +#include "PhysicsSystem.h" + #include -#include -#include #include "utils.h" void framebuffer_size_callback(GLFWwindow *window, int width, int height); @@ -35,7 +34,11 @@ float lastFrame = 0.0f; // key press map std::unordered_map keyPressMap; -ChunkManager chunkManager; +// Terrain chunk manager +ChunkManager *chunkManager; +// Global coordinator +Coordinator gCoordinator; + const int FPS_HISTORY_CAP = 5000; const int MEM_HISTORY_CAP = 5000; std::vector fpsHistory; @@ -107,22 +110,58 @@ int main() { // build and compile shader programs // ------------------------------------ Shader *ourShader = - new Shader("src/shaders/camera.vert", "src/shaders/camera.frag"); + new Shader("src/shaders/terrain.vert", "src/shaders/terrain.frag"); + Shader *defaultShader = + new Shader("src/shaders/shader.vert", "src/shaders/shader.frag"); // glm::vec3 pos = glm::vec3(0, 0, 0); // Chunk chunk = Chunk(pos, ourShader); // chunk.load(); // chunk.setup(); - chunkManager = ChunkManager(4, 3, ourShader); char fpsStr[32] = "FPS: 0"; char memStr[32]; + // initialize coordinator + chunkManager = new ChunkManager(4, 3, ourShader); + gCoordinator.Init(chunkManager); + + // generate terrain + gCoordinator.mChunkManager->pregenerateChunks(); + + gCoordinator.RegisterComponent(); + gCoordinator.RegisterComponent(); + gCoordinator.RegisterComponent(); + + auto physicsSystem = gCoordinator.RegisterSystem(); + + Signature signature; + signature.set(gCoordinator.GetComponentType()); + signature.set(gCoordinator.GetComponentType()); + signature.set(gCoordinator.GetComponentType()); + gCoordinator.SetSystemSignature(signature); + + std::vector entities(MAX_ENTITIES); + + // create a dummy "player entity" + entities[0] = gCoordinator.CreateEntity(); + gCoordinator.AddComponent(entities[0], + Gravity{glm::vec3(0.0f, -0.05f, 0.0f)}); + gCoordinator.AddComponent( + entities[0], + RigidBody{glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 0.0f)}); + + // place the entity in front of us + gCoordinator.AddComponent( + entities[0], Transform{.position = glm::vec3(0.0f, 10.0f, -5.0f), + .rotation = glm::vec3(0.0f, 0.0f, 0.0f), + .scale = glm::vec3(1.0f, 1.0f, 1.0f)}); + + auto player = entities[0]; + // render loop // ----------- - chunkManager.pregenerateChunks(); - while (!glfwWindowShouldClose(window)) { // per-frame time logic // -------------------- @@ -130,6 +169,8 @@ int main() { deltaTime = currentFrame - lastFrame; lastFrame = currentFrame; + physicsSystem->Update(deltaTime); + ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); @@ -143,13 +184,18 @@ int main() { glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - // chunk.render(); - chunkManager.update(deltaTime, cameraPos, cameraPos + cameraFront); - chunkManager.render(); + // update + gCoordinator.mChunkManager->update(deltaTime, gCoordinator.mCamera); + // get player deets + RigidBody playerRB = gCoordinator.GetComponent(player); + Transform playerTrans = gCoordinator.GetComponent(player); - // Rendering - // (Your code clears your framebuffer, renders your other stuff etc.) - // (Your code calls glfwSwapBuffers() etc.) + // render + gCoordinator.mChunkManager->render(gCoordinator.mCamera); + + // TODO: render the "player" entity + defaultShader->use(); + glUseProgram(0); // Calculate FPS int fps = calculateFPS(deltaTime); @@ -159,53 +205,83 @@ int main() { } std::sprintf(memStr, "RAM: %f MB", mem / 1000000); - ImGui::Begin("Stats"); - + ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f), ImGuiCond_Always, + ImVec2(0.0f, 0.0f)); + ImGuiWindowFlags statsFlags = + ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_NoSavedSettings | + ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; + statsFlags |= ImGuiWindowFlags_NoMove; + bool active = true; + ImGui::Begin("Stats", &active, statsFlags); ImGui::Text("%s", fpsStr); ImGui::Text("%s", memStr); - - // Slider that appears in the window + ImGui::Separator(); // Ends the window ImGui::End(); + ImGui::Begin("Player"); + ImGui::Text("velocity: (%.2f, %.3f, %.3f)", playerRB.velocity.x, + playerRB.velocity.y, playerRB.velocity.z); + ImGui::Text("position: (%.2f, %.3f, %.3f)", playerTrans.position.x, + playerTrans.position.y, playerTrans.position.z); + ImGui::End(); + ImGui::Begin("Camera"); - ImGui::Text("fov: %.2f", fov); - ImGui::Text("pos: (%.2f, %.3f, %.3f)", cameraPos.x, cameraPos.y, - cameraPos.z); - ImGui::Text("left: (%.2f, %.3f, %.3f)", cameraLeft.x, cameraLeft.y, - cameraLeft.z); - ImGui::Text("right: (%.2f, %.3f, %.3f)", cameraRight.x, cameraRight.y, - cameraRight.z); - ImGui::Text("up: (%.2f, %.3f, %.3f)", cameraUp.x, cameraUp.y, - cameraUp.z); + ImGui::Text("fov: %.2f", gCoordinator.mCamera.fov); + ImGui::Text("pos: (%.2f, %.3f, %.3f)", gCoordinator.mCamera.cameraPos.x, + gCoordinator.mCamera.cameraPos.y, + gCoordinator.mCamera.cameraPos.z); + ImGui::Text("left: (%.2f, %.3f, %.3f)", + gCoordinator.mCamera.cameraLeft.x, + gCoordinator.mCamera.cameraLeft.y, + gCoordinator.mCamera.cameraLeft.z); + ImGui::Text("right: (%.2f, %.3f, %.3f)", + gCoordinator.mCamera.cameraRight.x, + gCoordinator.mCamera.cameraRight.y, + gCoordinator.mCamera.cameraRight.z); + ImGui::Text("up: (%.2f, %.3f, %.3f)", gCoordinator.mCamera.cameraUp.x, + gCoordinator.mCamera.cameraUp.y, + gCoordinator.mCamera.cameraUp.z); ImGui::Text("frustum:"); ImGui::Text("left d = %.2f", - frustum.planes[Frustum::FRUSTUM_LEFT].distance); + gCoordinator.mCamera.frustum.planes[Frustum::FRUSTUM_LEFT] + .distance); ImGui::Text("right d = %.2f", - frustum.planes[Frustum::FRUSTUM_RIGHT].distance); - ImGui::Text("left: n:(%.2f, %.3f, %.3f)\nright: n:(%.3f, %.3f, %.3f)", - frustum.planes[Frustum::FRUSTUM_LEFT].normal.x, - frustum.planes[Frustum::FRUSTUM_LEFT].normal.y, - frustum.planes[Frustum::FRUSTUM_LEFT].normal.z, - frustum.planes[Frustum::FRUSTUM_RIGHT].normal.x, - frustum.planes[Frustum::FRUSTUM_RIGHT].normal.y, - frustum.planes[Frustum::FRUSTUM_RIGHT].normal.z); - - ImGui::Text("near: n:(%.2f, %.3f, %.3f)\nfar: n:(%.3f, %.3f, %.3f)", - frustum.planes[Frustum::FRUSTUM_NEAR].normal.x, - frustum.planes[Frustum::FRUSTUM_NEAR].normal.y, - frustum.planes[Frustum::FRUSTUM_NEAR].normal.z, - frustum.planes[Frustum::FRUSTUM_FAR].normal.x, - frustum.planes[Frustum::FRUSTUM_FAR].normal.y, - frustum.planes[Frustum::FRUSTUM_FAR].normal.z); - - ImGui::Text("bottom: n:(%.2f, %.3f, %.3f)\ntop: n:(%.3f, %.3f, %.3f)", - frustum.planes[Frustum::FRUSTUM_BOTTOM].normal.x, - frustum.planes[Frustum::FRUSTUM_BOTTOM].normal.y, - frustum.planes[Frustum::FRUSTUM_BOTTOM].normal.z, - frustum.planes[Frustum::FRUSTUM_TOP].normal.x, - frustum.planes[Frustum::FRUSTUM_TOP].normal.y, - frustum.planes[Frustum::FRUSTUM_TOP].normal.z); + gCoordinator.mCamera.frustum.planes[Frustum::FRUSTUM_RIGHT] + .distance); + ImGui::Text( + "left: n:(%.2f, %.3f, %.3f)\nright: n:(%.3f, %.3f, %.3f)", + gCoordinator.mCamera.frustum.planes[Frustum::FRUSTUM_LEFT].normal.x, + gCoordinator.mCamera.frustum.planes[Frustum::FRUSTUM_LEFT].normal.y, + gCoordinator.mCamera.frustum.planes[Frustum::FRUSTUM_LEFT].normal.z, + gCoordinator.mCamera.frustum.planes[Frustum::FRUSTUM_RIGHT] + .normal.x, + gCoordinator.mCamera.frustum.planes[Frustum::FRUSTUM_RIGHT] + .normal.y, + gCoordinator.mCamera.frustum.planes[Frustum::FRUSTUM_RIGHT] + .normal.z); + + ImGui::Text( + "near: n:(%.2f, %.3f, %.3f)\nfar: n:(%.3f, %.3f, %.3f)", + gCoordinator.mCamera.frustum.planes[Frustum::FRUSTUM_NEAR].normal.x, + gCoordinator.mCamera.frustum.planes[Frustum::FRUSTUM_NEAR].normal.y, + gCoordinator.mCamera.frustum.planes[Frustum::FRUSTUM_NEAR].normal.z, + gCoordinator.mCamera.frustum.planes[Frustum::FRUSTUM_FAR].normal.x, + gCoordinator.mCamera.frustum.planes[Frustum::FRUSTUM_FAR].normal.y, + gCoordinator.mCamera.frustum.planes[Frustum::FRUSTUM_FAR].normal.z); + + ImGui::Text( + "bottom: n:(%.2f, %.3f, %.3f)\ntop: n:(%.3f, %.3f, %.3f)", + gCoordinator.mCamera.frustum.planes[Frustum::FRUSTUM_BOTTOM] + .normal.x, + gCoordinator.mCamera.frustum.planes[Frustum::FRUSTUM_BOTTOM] + .normal.y, + gCoordinator.mCamera.frustum.planes[Frustum::FRUSTUM_BOTTOM] + .normal.z, + gCoordinator.mCamera.frustum.planes[Frustum::FRUSTUM_TOP].normal.x, + gCoordinator.mCamera.frustum.planes[Frustum::FRUSTUM_TOP].normal.y, + gCoordinator.mCamera.frustum.planes[Frustum::FRUSTUM_TOP].normal.z); ImGui::End(); if (cursorOn) { @@ -214,20 +290,27 @@ int main() { // ImGui::DragInt("tps", &g.ticksPerSecond, 1, 0, 1000); // } // Text that appears in the window - ImGui::Checkbox("generate chunks", &chunkManager.genChunk); + ImGui::Checkbox("generate chunks", + &gCoordinator.mChunkManager->genChunk); ImGui::LabelText("##moveSpeedLabel", "Movement Speed"); - ImGui::SliderFloat("##moveSpeedSlider", &cameraSpeedMultiplier, + ImGui::SliderFloat("##moveSpeedSlider", + &gCoordinator.mCamera.cameraSpeedMultiplier, 1.0f, 1000.0f); ImGui::LabelText("##chunkGenDistanceLabel", "Chunk Gen Distance"); - ImGui::SliderInt("##chunkGenDistanceSlider", - (int *)&chunkManager.chunkGenDistance, 1, 16); + ImGui::SliderInt( + "##chunkGenDistanceSlider", + (int *)&(gCoordinator.mChunkManager->chunkGenDistance), 1, 16); ImGui::LabelText("##renderDistanceLabel", "Render Distance"); - ImGui::SliderInt("##renderDistanceSlider", - (int *)&chunkManager.chunkRenderDistance, 1, 16); + ImGui::SliderInt( + "##renderDistanceSlider", + (int *)&(gCoordinator.mChunkManager->chunkRenderDistance), 1, + 16); ImGui::LabelText("##zFarLabel", "zFar"); - ImGui::SliderFloat("##zFarSlider", &zFar, 1.0f, 2000.0f); + ImGui::SliderFloat("##zFarSlider", &gCoordinator.mCamera.zFar, 1.0f, + 2000.0f); ImGui::LabelText("##fovSliderLabel", "FOV"); - ImGui::SliderFloat("##fovSlider", &fov, 25.0f, 105.0f); + ImGui::SliderFloat("##fovSlider", &gCoordinator.mCamera.fov, 25.0f, + 105.0f); // Slider that appears in the window // Ends the window ImGui::End(); @@ -262,22 +345,29 @@ void processInput(GLFWwindow *window, bool *cursorOn) { if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, true); - float cameraSpeed = static_cast(cameraSpeedMultiplier * deltaTime); + float cameraSpeed = static_cast( + gCoordinator.mCamera.cameraSpeedMultiplier * deltaTime); constexpr glm::vec3 up = glm::vec3(0.0, 1.0, 0.0); if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) - cameraPos += cameraSpeed * cameraFront; + gCoordinator.mCamera.cameraPos += + cameraSpeed * gCoordinator.mCamera.cameraFront; if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) - cameraPos -= cameraSpeed * cameraFront; + gCoordinator.mCamera.cameraPos -= + cameraSpeed * gCoordinator.mCamera.cameraFront; if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) - cameraPos -= - glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed; + gCoordinator.mCamera.cameraPos -= + glm::normalize(glm::cross(gCoordinator.mCamera.cameraFront, + gCoordinator.mCamera.cameraUp)) * + cameraSpeed; if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) - cameraPos += - glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed; + gCoordinator.mCamera.cameraPos += + glm::normalize(glm::cross(gCoordinator.mCamera.cameraFront, + gCoordinator.mCamera.cameraUp)) * + cameraSpeed; if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS) - cameraPos += up * cameraSpeed; + gCoordinator.mCamera.cameraPos += up * cameraSpeed; if (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) { - cameraPos -= up * cameraSpeed; + gCoordinator.mCamera.cameraPos -= up * cameraSpeed; } // insert into key press map @@ -303,12 +393,14 @@ void processInput(GLFWwindow *window, bool *cursorOn) { if (glfwGetKey(window, GLFW_KEY_X) == GLFW_RELEASE && keyPressMap[GLFW_KEY_X]) { - chunkManager.genChunk = !chunkManager.genChunk; + gCoordinator.mChunkManager->genChunk = + !gCoordinator.mChunkManager->genChunk; keyPressMap[GLFW_KEY_X] = false; } - frustum = createFrustumFromCamera((float)SCR_WIDTH / (float)SCR_HEIGHT, fov, - zNear, zFar); + // TODO: a better way to do this? + gCoordinator.mCamera.frustum = + createFrustumFromCamera(gCoordinator.mCamera); } // glfw: whenever the window size changed (by OS or user resize) this callback @@ -352,54 +444,57 @@ void mouse_callback(GLFWwindow *window, double xposIn, double yposIn) { float xpos = static_cast(xposIn); float ypos = static_cast(yposIn); - if (firstMouse) { - lastX = xpos; - lastY = ypos; - firstMouse = false; + if (gCoordinator.mCamera.firstMouse) { + gCoordinator.mCamera.lastX = xpos; + gCoordinator.mCamera.lastY = ypos; + gCoordinator.mCamera.firstMouse = false; } - float xoffset = xpos - lastX; - float yoffset = - lastY - ypos; // reversed since y-coordinates go from bottom to top - lastX = xpos; - lastY = ypos; + float xoffset = xpos - gCoordinator.mCamera.lastX; + float yoffset = gCoordinator.mCamera.lastY - + ypos; // reversed since y-coordinates go from bottom to top + gCoordinator.mCamera.lastX = xpos; + gCoordinator.mCamera.lastY = ypos; float sensitivity = 0.1f; // change this value to your liking xoffset *= sensitivity; yoffset *= sensitivity; - yaw += xoffset; - pitch += yoffset; + gCoordinator.mCamera.yaw += xoffset; + gCoordinator.mCamera.pitch += yoffset; // make sure that when pitch is out of bounds, screen doesn't get // flipped - if (pitch > 89.0f) - pitch = 89.0f; - if (pitch < -89.0f) - pitch = -89.0f; + if (gCoordinator.mCamera.pitch > 89.0f) + gCoordinator.mCamera.pitch = 89.0f; + if (gCoordinator.mCamera.pitch < -89.0f) + gCoordinator.mCamera.pitch = -89.0f; glm::vec3 front; - front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch)); - front.y = sin(glm::radians(pitch)); - front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch)); + front.x = cos(glm::radians(gCoordinator.mCamera.yaw)) * + cos(glm::radians(gCoordinator.mCamera.pitch)); + front.y = sin(glm::radians(gCoordinator.mCamera.pitch)); + front.z = sin(glm::radians(gCoordinator.mCamera.yaw)) * + cos(glm::radians(gCoordinator.mCamera.pitch)); - cameraFront = glm::normalize(front); + gCoordinator.mCamera.cameraFront = glm::normalize(front); // Calculate the right vector - cameraRight = - glm::normalize(glm::cross(cameraFront, glm::vec3(0.0f, 1.0f, 0.0f))); + gCoordinator.mCamera.cameraRight = glm::normalize(glm::cross( + gCoordinator.mCamera.cameraFront, glm::vec3(0.0f, 1.0f, 0.0f))); // Calculate the up vector - cameraUp = glm::normalize(glm::cross(cameraRight, cameraFront)); + gCoordinator.mCamera.cameraUp = glm::normalize(glm::cross( + gCoordinator.mCamera.cameraRight, gCoordinator.mCamera.cameraFront)); // Calculate the left vector (opposite of right) - cameraLeft = -cameraRight; + gCoordinator.mCamera.cameraLeft = -gCoordinator.mCamera.cameraRight; // The top vector is the same as the up vector in this case - cameraTop = cameraUp; + gCoordinator.mCamera.cameraTop = gCoordinator.mCamera.cameraUp; - frustum = createFrustumFromCamera((float)SCR_WIDTH / (float)SCR_HEIGHT, fov, - zNear, zFar); + gCoordinator.mCamera.frustum = + createFrustumFromCamera(gCoordinator.mCamera); } void imgui_mouse_callback(GLFWwindow *window, double xposIn, double yposIn) { @@ -412,12 +507,12 @@ void imgui_mouse_callback(GLFWwindow *window, double xposIn, double yposIn) { // glfw: whenever the mouse scroll wheel scrolls, this callback is called // ---------------------------------------------------------------------- void scroll_callback(GLFWwindow *window, double xoffset, double yoffset) { - cameraSpeedMultiplier -= (float)yoffset; - if (cameraSpeedMultiplier < 1.0f) - cameraSpeedMultiplier = 1.0f; - if (cameraSpeedMultiplier > 1000.0f) - cameraSpeedMultiplier = 1000.0f; - - frustum = createFrustumFromCamera((float)SCR_WIDTH / (float)SCR_HEIGHT, fov, - zNear, zFar); + gCoordinator.mCamera.cameraSpeedMultiplier -= (float)yoffset; + if (gCoordinator.mCamera.cameraSpeedMultiplier < 1.0f) + gCoordinator.mCamera.cameraSpeedMultiplier = 1.0f; + if (gCoordinator.mCamera.cameraSpeedMultiplier > 1000.0f) + gCoordinator.mCamera.cameraSpeedMultiplier = 1000.0f; + + gCoordinator.mCamera.frustum = + createFrustumFromCamera(gCoordinator.mCamera); } diff --git a/src/shaders/shader.frag b/src/shaders/shader.frag index b6c7a1d..e2906ab 100644 --- a/src/shaders/shader.frag +++ b/src/shaders/shader.frag @@ -1,9 +1,9 @@ #version 330 core out vec4 FragColor; -in vec3 ourColor; +in vec4 ourColor; void main() { - FragColor = vec4(ourColor, 1.0f); -} + FragColor = ourColor; +} \ No newline at end of file diff --git a/src/shaders/shader.vert b/src/shaders/shader.vert index 871ba1c..5bb8611 100644 --- a/src/shaders/shader.vert +++ b/src/shaders/shader.vert @@ -1,11 +1,12 @@ #version 330 core layout (location = 0) in vec3 aPos; -layout (location = 1) in vec3 aColor; +layout (location = 1) in vec2 aTexCoord; -out vec3 ourColor; +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; void main() { - gl_Position = vec4(aPos, 1.0); - ourColor = aColor; -} + gl_Position = projection * view * model * vec4(aPos, 1.0f); +} \ No newline at end of file diff --git a/src/shaders/camera.frag b/src/shaders/terrain.frag similarity index 100% rename from src/shaders/camera.frag rename to src/shaders/terrain.frag diff --git a/src/shaders/camera.vert b/src/shaders/terrain.vert similarity index 100% rename from src/shaders/camera.vert rename to src/shaders/terrain.vert diff --git a/src/smolgl.h b/src/smolgl.h index d9e804f..eac33e5 100644 --- a/src/smolgl.h +++ b/src/smolgl.h @@ -10,37 +10,6 @@ const unsigned int SCR_WIDTH = 1280; const unsigned int SCR_HEIGHT = 720; -// camera -float cameraSpeedMultiplier = 20.0f; -glm::vec3 cameraPos = glm::vec3(0.0f, 2.0f, 1.0f); -glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f); -glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f); - -glm::vec3 cameraRight = glm::vec3(1.0f, 0.0f, 0.0f); - -// Calculate the left vector (opposite of right) -glm::vec3 cameraLeft = -cameraRight; - -// The top vector is the same as the up vector in this case -glm::vec3 cameraTop = cameraUp; - -bool firstMouse = true; -float yaw = -90.0f; // yaw is initialized to -90.0 degrees since a yaw of 0.0 - // results in a direction vector pointing to the right so we - // initially rotate a bit to the left. -float pitch = 0.0f; -float lastX = 800.0f / 2.0; -float lastY = 600.0 / 2.0; -float fov = 45.0f; - -float zNear = 0.1f; -float zFar = 1000.0f; - -#include "Frustum.h" - -Frustum frustum = - createFrustumFromCamera((float)SCR_WIDTH / (float)SCR_HEIGHT, fov, zNear, zFar); - #include #ifndef SMOLGL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION