├── Foundation ├── include │ ├── cmake │ │ └── ArcherEngineConfig.cmake │ └── ArcherEngine │ │ ├── Scene │ │ ├── Viewport.h │ │ ├── SunLight.h │ │ ├── Scene.h │ │ └── Camera.h │ │ ├── Version.h │ │ ├── Support.h │ │ ├── Utility │ │ └── FileDialog.h │ │ ├── Controls │ │ └── Input.h │ │ ├── WindowOptions.h │ │ └── OS │ │ └── Interface │ │ └── Window.h ├── samples │ ├── CMakeLists.txt │ ├── editor │ │ ├── CMakeLists.txt │ │ └── main.cpp │ └── common │ │ └── scripts │ │ ├── file_menu.h │ │ └── fpp_fly.h ├── src │ ├── Sound │ │ ├── Speaker.cpp │ │ ├── SoundEffect.h │ │ ├── ErrorCheck.h │ │ ├── LongSound.h │ │ ├── SoundDevice.h │ │ ├── SoundListener.h │ │ ├── Speaker.h │ │ ├── SoundEffect.cpp │ │ ├── SoundListener.cpp │ │ ├── LongSound.cpp │ │ └── SoundDevice.cpp │ ├── Base │ │ ├── UniqueInstance.cpp │ │ └── UniqueInstance.h │ ├── Mesh │ │ ├── AnimationData.h │ │ ├── Skeleton.cpp │ │ ├── Skeleton.h │ │ ├── Spacial3D.h │ │ ├── Vertex.cpp │ │ ├── MeshInfo.cpp │ │ ├── Animator.h │ │ ├── MeshInfo.h │ │ ├── TextureTypes.h │ │ ├── Vertex.h │ │ ├── Animation.h │ │ ├── Spacial3D.cpp │ │ ├── Animator.cpp │ │ ├── Material.h │ │ ├── Bone.h │ │ ├── Animation.cpp │ │ └── Bone.cpp │ ├── OS │ │ └── OpenGL │ │ │ ├── PrimativeMaker.h │ │ │ ├── InternalShaders │ │ │ ├── Fog.h │ │ │ ├── Uber.h │ │ │ ├── Basic.h │ │ │ ├── Shadow.h │ │ │ ├── Stencil.h │ │ │ ├── Skycube.h │ │ │ ├── Init.h │ │ │ ├── Fog.cpp │ │ │ ├── Basic.cpp │ │ │ ├── Skycube.cpp │ │ │ ├── Shadow.cpp │ │ │ ├── Stencil.cpp │ │ │ └── Uber.cpp │ │ │ ├── Loaders │ │ │ ├── AssimpSceneLoader.cpp │ │ │ ├── AssimpSceneLoader.h │ │ │ ├── MaterialLoader.cpp │ │ │ ├── Cache.h │ │ │ ├── Cache.cpp │ │ │ └── TextureLoader.cpp │ │ │ ├── OGLShader.h │ │ │ ├── Graphics.h │ │ │ ├── OGLShader.cpp │ │ │ └── PrimativeMaker.cpp │ ├── GUI │ │ ├── imGUI.h │ │ └── imGUI.cpp │ ├── Scene │ │ ├── Skybox.h │ │ ├── Lights.cpp │ │ ├── Skybox.cpp │ │ ├── Lights.h │ │ ├── SunLight.cpp │ │ ├── Scene.cpp │ │ └── Camera.cpp │ ├── Utility │ │ └── FileDialog.cpp │ ├── Math │ │ └── Conversions.h │ └── IntefaceRunetime.cpp ├── testsuite │ ├── pch.cpp │ ├── CMakeLists.txt │ ├── pch.h │ ├── DownloadTestResources │ │ └── DownloadTestResources.cpp │ ├── test_globals.h │ ├── sun_light_script.h │ └── fly_cam_script.h └── CMakeLists.txt ├── linux_run_editor.sh ├── linux_build.sh ├── vcpkg.json ├── CMakeLists.txt ├── license.md ├── doc └── readme.md ├── readme.md └── .gitattributes /Foundation/include/cmake/ArcherEngineConfig.cmake: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Foundation/samples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(editor) 2 | -------------------------------------------------------------------------------- /linux_run_editor.sh: -------------------------------------------------------------------------------- 1 | 2 | ./build/Foundation/samples/editor/editor_example 3 | -------------------------------------------------------------------------------- /linux_build.sh: -------------------------------------------------------------------------------- 1 | 2 | cmake -D ENABLE_TESTS=ON -B build/ -S . 3 | 4 | make -C build/ 5 | -------------------------------------------------------------------------------- /Foundation/src/Sound/Speaker.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattearly/ArcherEngine/HEAD/Foundation/src/Sound/Speaker.cpp -------------------------------------------------------------------------------- /Foundation/include/ArcherEngine/Scene/Viewport.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | namespace AA { 3 | struct Viewport { 4 | int BottomLeft[2]; 5 | int Width; 6 | int Height; 7 | }; 8 | } -------------------------------------------------------------------------------- /Foundation/include/ArcherEngine/Version.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | namespace AA { 3 | constexpr unsigned int ENGINEVERSIONMAJOR = 2; 4 | constexpr unsigned int ENGINEVERSIONMINOR = 2; 5 | constexpr unsigned int ENGINEVERSIONPATCH = 0; 6 | } 7 | -------------------------------------------------------------------------------- /Foundation/testsuite/pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: source file corresponding to the pre-compiled header 2 | 3 | #include "pch.h" 4 | 5 | // When you are using pre-compiled headers, this source file is necessary for compilation to succeed. 6 | -------------------------------------------------------------------------------- /Foundation/testsuite/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | #todo(mje): convert tests to something other than Microsoft::VisualStudio::CppUnitTestFramework - such as gtest or ctest or 4 | #add_executable(UnitTest.cpp) 5 | 6 | -------------------------------------------------------------------------------- /Foundation/src/Base/UniqueInstance.cpp: -------------------------------------------------------------------------------- 1 | #include "UniqueInstance.h" 2 | namespace AA { 3 | static uidtype next_uid = 0; 4 | uidtype UniqueInstance::GetUID() const noexcept { return UniqueID; } 5 | UniqueInstance::UniqueInstance() { 6 | UniqueID = ++next_uid; 7 | }; 8 | } -------------------------------------------------------------------------------- /Foundation/src/Base/UniqueInstance.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | namespace AA { 4 | 5 | typedef uint32_t uidtype; 6 | 7 | class UniqueInstance { 8 | public: 9 | virtual uidtype GetUID() const noexcept; 10 | UniqueInstance(); 11 | private: 12 | uidtype UniqueID; 13 | }; 14 | } // end namespace AA 15 | -------------------------------------------------------------------------------- /Foundation/src/Mesh/AnimationData.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "Animator.h" 4 | #include 5 | #include "Skeleton.h" 6 | namespace AA { 7 | struct AnimationData { 8 | glm::mat4 mGlobalInverseTransform; 9 | std::unique_ptr mAnimator; 10 | Skeleton m_Skeleton; 11 | }; 12 | } -------------------------------------------------------------------------------- /Foundation/include/ArcherEngine/Support.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | namespace AA { 4 | const std::vector valid_model_handles = { ".fbx", ".obj", ".glb", ".dae" }; 5 | const std::vector valid_sound_handles = { ".wav", ".ogg" }; 6 | const std::vector valid_texture_handles = { ".png", ".jpeg", ".bmp", ".tga" }; 7 | } -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "archerengine", 3 | "version": "2.2", 4 | "dependencies": [ 5 | "glm", 6 | "assimp", 7 | "glfw3", 8 | "glew", 9 | "openal-soft", 10 | "libsndfile", 11 | { 12 | "name": "imgui", 13 | "features": [ 14 | "glfw-binding", 15 | "opengl3-binding" 16 | ] 17 | }, 18 | "stb", 19 | "nativefiledialog-extended" 20 | ] 21 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | Project("ArcherEngine" VERSION 1.0) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 6 | 7 | option(ENABLE_FOUNDATION "Enable Foundation" ON) 8 | 9 | if(ENABLE_FOUNDATION) 10 | add_subdirectory(Foundation) 11 | endif() 12 | 13 | option(ENABLE_TESTS "Set to OFF|ON (default is OFF) to control build of ArcherEngine tests & samples" OFF) 14 | 15 | -------------------------------------------------------------------------------- /Foundation/src/Mesh/Skeleton.cpp: -------------------------------------------------------------------------------- 1 | #include "Skeleton.h" 2 | namespace AA { 3 | 4 | Bone* Skeleton::FindBone(const std::string& name) { 5 | auto iter = std::find_if(m_Bones.begin(), m_Bones.end(), [&](const Bone& Bone) { return Bone.GetBoneName() == name; }); 6 | if (iter == m_Bones.end()) 7 | return nullptr; 8 | else 9 | return &(*iter); 10 | } 11 | 12 | 13 | const std::map& Skeleton::GetBoneIDMap() { 14 | return m_BoneInfoMap; 15 | } 16 | 17 | 18 | 19 | } -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/PrimativeMaker.h: -------------------------------------------------------------------------------- 1 | // only OpenGL class can use this extension 2 | #pragma once 3 | namespace AA { 4 | class PrimativeMaker { 5 | private: 6 | friend class OpenGL; 7 | static unsigned int load_cone(); 8 | static void unload_cone(); 9 | static unsigned int load_cube(); 10 | static void unload_cube(); 11 | static unsigned int load_plane(); 12 | static void unload_plane(); 13 | static void unload_all(); 14 | private: 15 | PrimativeMaker() = delete; 16 | }; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /Foundation/src/Sound/SoundEffect.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Base/UniqueInstance.h" 3 | #include 4 | #include 5 | namespace AA { 6 | /// 7 | /// does not support .mp3, most other common file types are fine. 8 | /// 9 | class SoundEffect : public UniqueInstance { 10 | public: 11 | SoundEffect() = delete; 12 | SoundEffect(const char* filename); 13 | ~SoundEffect(); 14 | ALuint _Buffer; 15 | std::string _FilePath; 16 | private: 17 | }; 18 | } // namespace AA 19 | -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/InternalShaders/Fog.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../OGLShader.h" 3 | 4 | namespace AA { 5 | namespace InternalShaders { 6 | class Fog { 7 | 8 | public: 9 | static void Init(); 10 | static OGLShader* Get(); 11 | static void Shutdown(); 12 | static bool IsActive(); 13 | private: 14 | Fog() = delete; 15 | Fog(Fog&) = delete; 16 | Fog(const Fog&) = delete; 17 | Fog(Fog&&) = delete; 18 | Fog(const Fog&&) = delete; 19 | }; 20 | 21 | } // end namespace InternalShaders 22 | } // end namespace AA 23 | 24 | -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/InternalShaders/Uber.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../OGLShader.h" 3 | namespace AA { 4 | namespace InternalShaders { 5 | class Uber final { 6 | public: 7 | static void Init(); 8 | static OGLShader* Get(); 9 | static void Shutdown(); 10 | static bool IsActive(); 11 | private: 12 | Uber() = delete; 13 | Uber(Uber&) = delete; 14 | Uber(const Uber&) = delete; 15 | Uber(Uber&&) = delete; 16 | Uber(const Uber&&) = delete; 17 | }; 18 | } // end namespace InternalShaders 19 | } // end namespace AA 20 | 21 | -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/InternalShaders/Basic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../OGLShader.h" 3 | namespace AA { 4 | namespace InternalShaders { 5 | class Basic final { 6 | public: 7 | static void Init(); 8 | static OGLShader* Get(); 9 | static void Shutdown(); 10 | static bool IsActive(); 11 | private: 12 | Basic() = delete; 13 | Basic(Basic&) = delete; 14 | Basic(const Basic&) = delete; 15 | Basic(Basic&&) = delete; 16 | Basic(const Basic&&) = delete; 17 | }; 18 | } // end namespace InternalShaders 19 | } // end namespace AA 20 | -------------------------------------------------------------------------------- /Foundation/samples/editor/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB SCRIPTS ../common/scripts/*.h) 2 | 3 | add_executable(editor_example main.cpp ${SCRIPTS}) 4 | 5 | target_link_libraries(editor_example PRIVATE archer) 6 | 7 | target_include_directories(editor_example 8 | PRIVATE 9 | "${CMAKE_SOURCE_DIR}/Foundation/samples/common/scripts" 10 | "${CMAKE_SOURCE_DIR}/Foundation/include") 11 | 12 | if (UNIX) 13 | #put special unix commands here 14 | elseif (WIN32) 15 | target_link_libraries(editor_example PRIVATE glm::glm imgui::imgui) 16 | endif() 17 | -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/InternalShaders/Shadow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../OGLShader.h" 3 | namespace AA { 4 | namespace InternalShaders { 5 | class Shadow final { 6 | public: 7 | static void Init(); 8 | static OGLShader* Get(); 9 | static void Shutdown(); 10 | static bool IsActive(); 11 | private: 12 | Shadow() = delete; 13 | Shadow(Shadow&) = delete; 14 | Shadow(const Shadow&) = delete; 15 | Shadow(Shadow&&) = delete; 16 | Shadow(const Shadow&&) = delete; 17 | }; 18 | } // end namespace InternalShaders 19 | } // end namespace AA 20 | -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/InternalShaders/Stencil.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../OGLShader.h" 3 | namespace AA { 4 | namespace InternalShaders { 5 | class Stencil final { 6 | public: 7 | static void Init(); 8 | static OGLShader* Get(); 9 | static void Shutdown(); 10 | static bool IsActive(); 11 | private: 12 | Stencil() = delete; 13 | Stencil(Stencil&) = delete; 14 | Stencil(const Stencil&) = delete; 15 | Stencil(Stencil&&) = delete; 16 | Stencil(const Stencil&&) = delete; 17 | }; 18 | } // end namespace InternalShaders 19 | } // end namespace AA -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/InternalShaders/Skycube.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../OGLShader.h" 3 | namespace AA { 4 | namespace InternalShaders { 5 | class Skycube final { 6 | public: 7 | static void Init(); 8 | static OGLShader* Get(); 9 | static void Shutdown(); 10 | static bool IsActive(); 11 | private: 12 | Skycube() = delete; 13 | Skycube(Skycube&) = delete; 14 | Skycube(const Skycube&) = delete; 15 | Skycube(Skycube&&) = delete; 16 | Skycube(const Skycube&&) = delete; 17 | }; 18 | } // end namespace InternalShaders 19 | } // end namespace AA 20 | -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/InternalShaders/Init.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Shadow.h" 3 | #include "Uber.h" 4 | #include "Stencil.h" 5 | #include "Skycube.h" 6 | #include "Basic.h" 7 | namespace AA { 8 | namespace InternalShaders { 9 | static void Init() { 10 | Uber::Get(); 11 | Skycube::Get(); 12 | Stencil::Get(); 13 | Shadow::Get(); 14 | Basic::Get(); 15 | } 16 | 17 | static void Shutdown() { 18 | Uber::Shutdown(); 19 | Skycube::Shutdown(); 20 | Stencil::Shutdown(); 21 | Shadow::Shutdown(); 22 | Basic::Shutdown(); 23 | } 24 | } // end namespace InternalShaders 25 | } // end namespace AA 26 | 27 | -------------------------------------------------------------------------------- /Foundation/src/GUI/imGUI.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __linux__ 4 | #include 5 | #include 6 | #include 7 | #elif _WIN32 8 | #include 9 | #include 10 | #include 11 | #endif 12 | 13 | namespace AA { 14 | class imGUI { 15 | public: 16 | imGUI(); 17 | void InitOpenGL(GLFWwindow* window); 18 | void Shutdown(); 19 | void NewFrame(); 20 | //void Update(); // updates done elsewhere 21 | void Render(); 22 | private: 23 | bool isInit; 24 | }; 25 | 26 | } // end namespace AA 27 | -------------------------------------------------------------------------------- /Foundation/src/Mesh/Skeleton.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Bone.h" 3 | #include 4 | #include 5 | namespace AA{ 6 | 7 | struct BoneInfo { 8 | /*id is index in finalBoneMatrices*/ 9 | int id; 10 | 11 | /*offset matrix transforms vertex from model space to bone space*/ 12 | glm::mat4 offset; 13 | }; 14 | 15 | typedef const std::string BONE_MAP_KEY; 16 | 17 | struct Skeleton { 18 | 19 | Bone* FindBone(const std::string& name); 20 | 21 | const std::map& GetBoneIDMap(); 22 | 23 | std::vector m_Bones; 24 | 25 | std::map m_BoneInfoMap; 26 | 27 | unsigned int m_BoneCounter = 0; 28 | 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /Foundation/src/Mesh/Spacial3D.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | //#include 4 | namespace AA { 5 | class Spacial3D final { 6 | public: 7 | Spacial3D(); 8 | void MoveTo(const glm::vec3& location); 9 | void ScaleTo(const glm::vec3& scale); 10 | void RotateTo(const glm::vec3& rot); 11 | void ProcessModifications(); 12 | private: 13 | //physx::PxRigidDynamic* mRigidBody; 14 | glm::vec3 mCurrentLocation; 15 | glm::vec3 mCurrentScale; 16 | glm::vec3 mCurrentRot; // expressed as 3 radians that corrispond to xyz rotation amounts 17 | bool has_unprocessed_modifications; 18 | glm::mat4 mFinalModelMatrix; 19 | friend class Interface; 20 | friend class Scene; 21 | }; 22 | 23 | } -------------------------------------------------------------------------------- /Foundation/testsuite/pch.h: -------------------------------------------------------------------------------- 1 | // pch.h: This is a precompiled header file. 2 | // Files listed below are compiled only once, improving build performance for future builds. 3 | // This also affects IntelliSense performance, including code completion and many code browsing features. 4 | // However, files listed here are ALL re-compiled if any one of them is updated between builds. 5 | // Do not add files here that you will be updating frequently as this negates the performance advantage. 6 | 7 | #ifndef PCH_H 8 | #define PCH_H 9 | 10 | // add headers that you want to pre-compile here 11 | 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #endif //PCH_H 19 | -------------------------------------------------------------------------------- /Foundation/src/Mesh/Vertex.cpp: -------------------------------------------------------------------------------- 1 | #include "Vertex.h" 2 | #include 3 | using glm::vec3; 4 | using glm::vec2; 5 | namespace AA { 6 | 7 | UnlitVertex::UnlitVertex(glm::vec3 pos, glm::vec2 uv) : Position(pos), TexCoords(uv) {} 8 | 9 | LitVertex::LitVertex(glm::vec3 pos, glm::vec2 uv, glm::vec3 normal) : UnlitVertex(pos,uv), Normal(normal) {} 10 | 11 | TanVertex::TanVertex(glm::vec3 pos, glm::vec2 uv, glm::vec3 normal, glm::vec3 tan, glm::vec3 bitan) : LitVertex(pos, uv, normal), Tangent(tan), BiTangent(bitan) {} 12 | 13 | AnimVertex::AnimVertex(glm::vec3 pos, glm::vec2 uv, glm::vec3 normal) 14 | : LitVertex(pos, uv, normal), m_BoneIDs{-1,-1,-1,-1}, m_Weights{0.f,0.f,0.f,0.f} 15 | {} 16 | 17 | } // end namespace AA 18 | -------------------------------------------------------------------------------- /Foundation/include/ArcherEngine/Utility/FileDialog.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | namespace AA { 4 | 5 | /// 6 | /// Opens your Operating System's Native File Dialog and allows you to navigate to a file, clicking Okay or Cancel. 7 | /// 8 | /// a filled out string of the path you navigated to. 9 | /// file type endings (after .), comma seperated. 10 | /// a path to attempt to start at. if failed, should start at last accessed path on windows. 11 | /// true if okay was pressed, false if cancel was pressed 12 | bool NavigateFileSystem(std::string& out, const char* filetypereg, const char* starting_pref); 13 | 14 | } -------------------------------------------------------------------------------- /Foundation/include/ArcherEngine/Scene/SunLight.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | namespace AA { 4 | class SunLight { 5 | public: 6 | SunLight(glm::vec3 dir, glm::vec3 amb, glm::vec3 diff, glm::vec3 spec); 7 | ~SunLight(); 8 | 9 | glm::vec3 Direction, Ambient, Diffuse, Specular; 10 | 11 | // directional shadows 12 | bool Shadows; 13 | 14 | // oglshadersettings 15 | const unsigned int& GetFBO() const; 16 | const unsigned int& GetTexID() const; 17 | void SetShadowBiasMin(float min) noexcept; 18 | void SetShadowBiasMax(float max) noexcept; 19 | 20 | unsigned int ShadowWidth, ShadowHeight; 21 | float ShadowNearPlane, ShadowFarPlane; 22 | float ShadowOrthoSize; 23 | 24 | private: 25 | float ShadowBiasMin, ShadowBiasMax; 26 | 27 | }; 28 | } // end namespace AA -------------------------------------------------------------------------------- /Foundation/src/Sound/ErrorCheck.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | namespace AA { 4 | static void local_alErrorCheck() { 5 | auto error = alGetError(); 6 | switch (error) { 7 | case AL_INVALID_NAME: 8 | throw("AL_INVALID_NAME: a bad name (ID) was passed to an OpenAL function"); 9 | case AL_INVALID_ENUM: 10 | throw("AL_INVALID_ENUM: an invalid enum value was passed to an OpenAL function"); 11 | case AL_INVALID_VALUE: 12 | throw("AL_INVALID_VALUE: an invalid value was passed to an OpenAL function"); 13 | case AL_INVALID_OPERATION: 14 | throw("AL_INVALID_OPERATION: the requested operation is not valid"); 15 | case AL_OUT_OF_MEMORY: 16 | throw("AL_OUT_OF_MEMORY: the requested operation resulted in OpenAL running out of memory"); 17 | default: 18 | break; 19 | } 20 | } 21 | } // end namespace AA -------------------------------------------------------------------------------- /Foundation/src/Sound/LongSound.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | namespace AA { 6 | class LongSound { 7 | public: 8 | 9 | LongSound(const char* filename); 10 | ~LongSound(); 11 | 12 | void Play(); 13 | void Pause(); 14 | void Resume(); 15 | void Stop(); 16 | 17 | void SetVolume(const float& val); 18 | 19 | bool IsPlaying(); 20 | 21 | void UpdatePlayBuffer(); 22 | 23 | private: 24 | 25 | ALint GetPlayingState(); 26 | 27 | static const constexpr uint16_t BUFFER_COUNT = 4; 28 | static const constexpr uint16_t BUFFER_SAMPLES = 8192; 29 | 30 | ALuint p_Source; 31 | ALuint p_Buffers[BUFFER_COUNT]; 32 | SNDFILE* p_Sndfile; 33 | SF_INFO p_Sfinfo; 34 | short* p_Membuf; 35 | ALenum p_Format; 36 | 37 | LongSound() = delete; 38 | }; 39 | 40 | } -------------------------------------------------------------------------------- /Foundation/src/Sound/SoundDevice.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace AA { 7 | 8 | /// 9 | /// By calling get() the first time, SoundDevice sets up the sound device and context for the device. 10 | /// 11 | class SoundDevice { 12 | public: 13 | static SoundDevice* Get(); 14 | static void Init(); 15 | static void Init(const char* devicename); 16 | static void Shutdown(); 17 | 18 | void SwitchDevice(const char* devicename); 19 | 20 | void PopulateDeviceVec(std::vector& devicesVec); 21 | 22 | void SuspendContext(); 23 | void ResumeContext(); 24 | 25 | protected: 26 | SoundDevice(); 27 | SoundDevice(const char* devicename); 28 | ~SoundDevice(); 29 | 30 | private: 31 | 32 | 33 | 34 | }; 35 | 36 | 37 | } //end namespace AA 38 | -------------------------------------------------------------------------------- /Foundation/src/Mesh/MeshInfo.cpp: -------------------------------------------------------------------------------- 1 | #include "MeshInfo.h" 2 | #include 3 | namespace AA { 4 | MeshInfo::MeshInfo(unsigned int a, unsigned int elcount) 5 | : vao(a), numElements(elcount) { 6 | } 7 | 8 | MeshInfo::MeshInfo(unsigned int a, unsigned int elcount, glm::mat4 trans) 9 | : vao(a), numElements(elcount), local_transform(trans) { 10 | } 11 | 12 | MeshInfo::MeshInfo(unsigned int a, unsigned int elcount, TextureMapType t_id, glm::mat4 local_trans) 13 | : vao(a), numElements(elcount), textureDrawIds(t_id), local_transform(local_trans) { 14 | } 15 | 16 | MeshInfo::MeshInfo(unsigned int a, unsigned int elcount, TextureMapType t_id, Material mat, glm::mat4 local_trans) 17 | : vao(a), numElements(elcount), textureDrawIds(t_id), material(mat), local_transform(local_trans) { 18 | } 19 | 20 | } // end namespace AA 21 | -------------------------------------------------------------------------------- /Foundation/src/Scene/Skybox.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../OS/OpenGL/OGLShader.h" 3 | #include 4 | #include 5 | #include 6 | namespace AA { 7 | // sticking with this pattern: 8 | // https://learnopengl.com/Advanced-OpenGL/Cubemaps 9 | // although many skybox texture sets found online seem to be in differing order 10 | // you may need to adapt accordingly. 11 | class Skybox { 12 | public: 13 | Skybox(std::vector incomingSkymapFiles); 14 | ~Skybox(); 15 | const unsigned int GetCubeMapTexureID() const; 16 | const unsigned int GetVAO() const; 17 | private: 18 | unsigned int mCubemapTexId = 0; 19 | unsigned int mVAO = 0; // setup once via setup_cube_geometry operation 20 | void setup_cube_geometry(); 21 | void setup_incoming_textures(std::vector& incomingSkymapFiles); 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/Loaders/AssimpSceneLoader.cpp: -------------------------------------------------------------------------------- 1 | #include "AssimpSceneLoader.h" 2 | namespace AA { 3 | 4 | bool AssimpSceneLoader::doLogging = false; 5 | 6 | static C_STRUCT aiLogStream logstream; 7 | 8 | void AssimpSceneLoader::Logging(const bool& on_off, const bool& to_file) noexcept { 9 | if (on_off) { 10 | if (to_file) { 11 | /*this stream now writes the log messages e assimp_log.txt */ 12 | logstream = aiGetPredefinedLogStream(aiDefaultLogStream_FILE, "ae_model_loader_logs.txt"); 13 | aiAttachLogStream(&logstream); 14 | } else { 15 | /*this stream now writes the log messages e assimp_log.txt */ 16 | logstream = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT, NULL); 17 | aiAttachLogStream(&logstream); 18 | } 19 | } else { 20 | // turn off 21 | aiDetachAllLogStreams(); 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Foundation/src/Sound/SoundListener.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | namespace AA { 5 | class SoundListener { 6 | public: 7 | static SoundListener* Get(); 8 | 9 | void SetMasterGain(const float& val); 10 | 11 | void SetPosition(const glm::vec3& pos); 12 | void SetPosition(const float& x, const float& y, const float& z); 13 | void SetLocation(const glm::vec3& pos); 14 | void SetLocation(const float& x, const float& y, const float& z); 15 | 16 | void SetOrientation(const glm::vec3& at, const glm::vec3& up = glm::vec3(0, 1, 0)); 17 | void SetOrientation( 18 | const float& xat, const float& yat, const float& zat, 19 | const float& xup, const float& yup, const float& zup); 20 | 21 | void SetDistanceModel(ALint textureType); 22 | 23 | protected: 24 | SoundListener(); 25 | 26 | }; 27 | 28 | } // end namespace AA -------------------------------------------------------------------------------- /Foundation/src/Scene/Lights.cpp: -------------------------------------------------------------------------------- 1 | #include "Lights.h" 2 | 3 | namespace AA { 4 | 5 | SpotLight::SpotLight(glm::vec3 pos, glm::vec3 dir, float inner, 6 | float outer, float constant, float linear, float quad, 7 | glm::vec3 amb, glm::vec3 diff, glm::vec3 spec) 8 | : Position(pos), Direction(dir), CutOff(inner), OuterCutOff(outer), 9 | Constant(constant), Linear(linear), Quadratic(quad), Ambient(amb), 10 | Diffuse(diff), Specular(spec) { 11 | static int uniqueSpotId = 0; 12 | id = uniqueSpotId; 13 | uniqueSpotId++; 14 | } 15 | 16 | PointLight::PointLight(glm::vec3 pos, float constant, float linear, float quad, glm::vec3 amb, glm::vec3 diff, glm::vec3 spec) 17 | : Position(pos), Constant(constant), Linear(linear), Quadratic(quad), Ambient(amb), Diffuse(diff), Specular(spec) { 18 | static int uniquePointId = 0; 19 | id = uniquePointId; 20 | uniquePointId++; 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /Foundation/src/Mesh/Animator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Animation.h" 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | namespace AA { 9 | 10 | class Animation; 11 | 12 | struct AnimationNodeTree; 13 | 14 | class Scene; 15 | 16 | class Animator { 17 | 18 | public: 19 | 20 | Animator() = delete; 21 | 22 | Animator(std::shared_ptr anim, glm::mat4 inv_trans); 23 | 24 | ~Animator(); 25 | 26 | void UpdateAnimation(float dt); 27 | 28 | void CalculateBoneTransform(const AnimationNodeTree* node, glm::mat4 parentTransform); 29 | 30 | std::vector GetFinalBoneMatrices() const; 31 | 32 | private: 33 | 34 | std::vector m_FinalBoneMatrices; 35 | 36 | std::shared_ptr m_CurrentAnimation; 37 | 38 | float m_CurrentTime; 39 | 40 | float m_DeltaTime; 41 | 42 | glm::mat4 cached_global_inverse_transform; 43 | 44 | }; 45 | 46 | } 47 | -------------------------------------------------------------------------------- /Foundation/src/Mesh/MeshInfo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "TextureTypes.h" 5 | #include "Material.h" 6 | namespace AA { 7 | /// 8 | /// Basic Struct of a Mesh within a Scene 9 | /// 10 | struct MeshInfo { 11 | MeshInfo() = delete; 12 | MeshInfo(unsigned int a, unsigned int elcount); 13 | MeshInfo(unsigned int a, unsigned int elcount, glm::mat4 trans); 14 | MeshInfo(unsigned int a, unsigned int elcount, TextureMapType t_id, glm::mat4 local_trans); 15 | MeshInfo(unsigned int a, unsigned int elcount, TextureMapType t_id, Material mat, glm::mat4 local_trans); 16 | unsigned int vao = 0; 17 | unsigned int numElements = 0; 18 | TextureMapType textureDrawIds{}; // map of 19 | Material material{}; 20 | float shininess = 0.5f; 21 | glm::mat4 local_transform = glm::mat4(1.0f); 22 | }; 23 | } // end namespace AA 24 | 25 | -------------------------------------------------------------------------------- /Foundation/src/Mesh/TextureTypes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | namespace AA { 6 | 7 | enum class TextureType : int { UNKNOWN = 0, ALBEDO, SPECULAR, NORMAL, EMISSION }; 8 | 9 | struct Texture2DInfo { 10 | Texture2DInfo() = default; 11 | unsigned int accessId = 0; // id to access it on the video mem (drawId) 12 | TextureType textureType = TextureType::UNKNOWN; 13 | std::string path = ""; 14 | int ref_count = 1; 15 | }; 16 | 17 | typedef std::vector TextureMapType; 18 | 19 | static inline std::string toString(const TextureType& t) { 20 | switch (t) { 21 | case TextureType::ALBEDO: return std::string("Albedo"); 22 | case TextureType::EMISSION: return std::string("Emission"); 23 | case TextureType::NORMAL: return std::string("Normal"); 24 | case TextureType::SPECULAR: return std::string("Specular"); 25 | case TextureType::UNKNOWN: return std::string("Unknown"); 26 | default: return std::string("Undefined"); 27 | } 28 | 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | Copyright 2021 Matthew J. Early 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. -------------------------------------------------------------------------------- /Foundation/src/Mesh/Vertex.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | namespace AA { 4 | 5 | struct UnlitVertex { 6 | UnlitVertex(glm::vec3 pos, glm::vec2 uv); 7 | glm::vec3 Position{}; 8 | glm::vec2 TexCoords{}; 9 | }; 10 | 11 | struct LitVertex : public UnlitVertex { 12 | LitVertex(glm::vec3 pos, glm::vec2 uv, glm::vec3 normal); 13 | glm::vec3 Normal{}; 14 | }; 15 | 16 | struct TanVertex :public LitVertex { 17 | TanVertex(glm::vec3 pos, glm::vec2 uv, glm::vec3 normal, glm::vec3 tan, glm::vec3 bitan); 18 | glm::vec3 Tangent{}; 19 | glm::vec3 BiTangent{}; 20 | }; 21 | 22 | // These must also match on the shader code when processing bones 23 | //constexpr auto MAX_BONES = 100; // todo: we don't seem to be using this but we should, somewhere 24 | constexpr auto MAX_BONE_INFLUENCE = 4; 25 | 26 | struct AnimVertex : public LitVertex { 27 | AnimVertex(glm::vec3 pos, glm::vec2 uv, glm::vec3 normal); 28 | int m_BoneIDs[MAX_BONE_INFLUENCE]; //> bone indexes which will influence this vertex 29 | float m_Weights[MAX_BONE_INFLUENCE]; //> weights from each bone 30 | }; 31 | 32 | } // end namespace AA 33 | -------------------------------------------------------------------------------- /Foundation/src/Scene/Skybox.cpp: -------------------------------------------------------------------------------- 1 | #include "Skybox.h" 2 | #include "../OS/OpenGL/Graphics.h" 3 | #include "../OS/OpenGL/InternalShaders/Skycube.h" 4 | #include "../OS/OpenGL/Loaders/AssimpSceneLoader.h" 5 | namespace AA { 6 | 7 | Skybox::Skybox(std::vector incomingSkymapFiles) { 8 | setup_cube_geometry(); 9 | setup_incoming_textures(incomingSkymapFiles); 10 | InternalShaders::Skycube::Get()->SetInt("u_skybox", 0); 11 | } 12 | 13 | Skybox::~Skybox() { 14 | if (mCubemapTexId != 0) { 15 | OpenGL::GetGL()->DeleteTex(1u, mCubemapTexId); // todo: cache textures and reuse 16 | } 17 | } 18 | 19 | const unsigned int Skybox::GetCubeMapTexureID() const { 20 | return mCubemapTexId; 21 | } 22 | 23 | const unsigned int Skybox::GetVAO() const { 24 | return mVAO; 25 | } 26 | 27 | void Skybox::setup_cube_geometry() { 28 | if (mVAO == 0) { 29 | mVAO = OpenGL::GetGL()->MakeCube(); 30 | } 31 | } 32 | 33 | void Skybox::setup_incoming_textures(std::vector& incomingSkymapFiles) { 34 | mCubemapTexId = AssimpSceneLoader::LoadCubeMapTexture(incomingSkymapFiles); // todo: load up debug box if skymap files are wrong 35 | } 36 | 37 | } // end namespace AA -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/Loaders/AssimpSceneLoader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../../../../include/ArcherEngine/Scene/Scene.h" 3 | #include 4 | #include 5 | #include 6 | #include "Cache.h" 7 | #include 8 | #include 9 | #include // for aiLogStream 10 | namespace AA { 11 | class Scene; 12 | class AssimpSceneLoader { 13 | public: 14 | static void Logging(const bool& on_off, const bool& to_file) noexcept; 15 | 16 | static TextureMapType LoadAllTextures(const aiScene* scene, const aiMaterial* ai_material, const std::string& orig_filepath); 17 | 18 | static unsigned int LoadCubeMapTexture(const std::vector& six_texture_paths); 19 | 20 | //static void UnloadTexture(const TextureMapType& textures_to_remove); 21 | 22 | static Material LoadAll(const aiScene* scene, const aiMaterial* ai_material); 23 | 24 | static int Load_MeshesTexturesMaterials(Scene& out_model, const std::string& path_to_load); 25 | 26 | static int Load_MeshesTexturesMaterialsBones(Scene& out_model, const std::string& path_to_load); 27 | 28 | static void Unload(const std::string& path_to_unload); 29 | 30 | private: 31 | static bool doLogging; 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/InternalShaders/Fog.cpp: -------------------------------------------------------------------------------- 1 | #include "Fog.h" 2 | #include 3 | namespace AA::InternalShaders { 4 | 5 | static OGLShader* FOGSHADER = NULL; // setup once 6 | 7 | void Fog::Init() { 8 | if (FOGSHADER) 9 | return; 10 | 11 | const std::string VERT_CODE = 12 | R"( 13 | #version 430 core 14 | layout (location = 0) in vec3 inPos; 15 | uniform mat4 u_view_matrix; 16 | uniform mat4 u_projection_matrix; 17 | uniform mat4 u_model_matrix; 18 | void main() { 19 | gl_Position = u_projection_matrix * u_view_matrix * u_model_matrix * vec4(inPos, 1.0); 20 | } 21 | )"; 22 | 23 | const std::string FRAG_CODE = 24 | R"( 25 | #version 430 core 26 | out vec4 out_Color; 27 | void main() { 28 | out_Color = vec4(1.0,1.0,1.0,1.0); 29 | } 30 | )"; 31 | 32 | FOGSHADER = new OGLShader(VERT_CODE.c_str(), FRAG_CODE.c_str()); 33 | } 34 | 35 | OGLShader* Fog::Get() { 36 | if (!FOGSHADER) { 37 | Init(); 38 | } else { 39 | FOGSHADER->Use(); 40 | } 41 | return FOGSHADER; 42 | } 43 | 44 | void Fog::Shutdown() { 45 | if (FOGSHADER) { 46 | delete FOGSHADER; 47 | FOGSHADER = nullptr; 48 | } 49 | } 50 | 51 | bool Fog::IsActive() { 52 | return FOGSHADER; 53 | } 54 | 55 | } // end namespace AA::InternalShaders 56 | -------------------------------------------------------------------------------- /Foundation/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | file(GLOB_RECURSE ARCHER_SOURCES "src/*.cpp") 4 | file(GLOB_RECURSE ARCHER_PUBLIC_HEADERS "include/ArcherEngine/*.h") 5 | 6 | add_library(archer ${ARCHER_SOURCES} ${ARCHER_PUBLIC_HEADERS}) 7 | 8 | target_include_directories(archer PUBLIC "include/ArcherEngine") 9 | 10 | if (UNIX) 11 | find_package(GLEW REQUIRED) 12 | target_link_libraries(archer glfw assimp imgui openal sndfile stb dl GL GLEW::GLEW) 13 | target_include_directories(archer PRIVATE "/usr/include/imgui") 14 | elseif (WIN32) 15 | find_package(imgui CONFIG REQUIRED) 16 | find_package(glm CONFIG REQUIRED) 17 | find_package(glew REQUIRED) 18 | find_package(glfw3 CONFIG REQUIRED) 19 | find_package(assimp CONFIG REQUIRED) 20 | find_package(OpenAL CONFIG REQUIRED) 21 | find_package(SndFile CONFIG REQUIRED) 22 | find_package(nfd CONFIG REQUIRED) 23 | target_link_libraries(archer PRIVATE glm::glm glfw assimp::assimp imgui::imgui OpenAL::OpenAL SndFile::sndfile GLEW::GLEW nfd::nfd) 24 | find_path(STB_INCLUDE_DIRS "stb_c_lexer.h") 25 | target_include_directories(archer PRIVATE ${STB_INCLUDE_DIRS} ${imgui_INCLUDE_DIRS}) 26 | endif() 27 | 28 | if (ENABLE_TESTS) 29 | add_subdirectory(testsuite) 30 | add_subdirectory(samples) 31 | endif() 32 | 33 | -------------------------------------------------------------------------------- /Foundation/src/GUI/imGUI.cpp: -------------------------------------------------------------------------------- 1 | #include "imGUI.h" 2 | #include "../OS/OpenGL/OGLShader.h" 3 | #include "../OS/OpenGL/Graphics.h" 4 | namespace AA { 5 | imGUI::imGUI() { 6 | isInit = false; 7 | } 8 | // run InitOpenGL only after AA::Init() has been called (or 9 | void imGUI::InitOpenGL(GLFWwindow* window) { 10 | if (!isInit) { 11 | // todo: use return types and set isInit with more 'thought' 12 | ImGui::CreateContext(); 13 | ImGui_ImplGlfw_InitForOpenGL(window, true); 14 | ImGui_ImplOpenGL3_Init(OGLShader::get_glsl_version()); 15 | isInit = true; 16 | } 17 | } 18 | 19 | void imGUI::Shutdown() { 20 | ImGui_ImplOpenGL3_Shutdown(); 21 | ImGui_ImplGlfw_Shutdown(); 22 | ImGui::DestroyContext(); 23 | } 24 | 25 | void imGUI::NewFrame() { 26 | ImGui_ImplOpenGL3_NewFrame(); 27 | ImGui_ImplGlfw_NewFrame(); 28 | ImGui::NewFrame(); 29 | } 30 | 31 | //void imGUI::Update() { 32 | //ImGui::Text("Hello, world!"); 33 | //static float color[4] = { 1.f, 1.f, 1.f, 1.f }; 34 | //ImGui::ColorEdit3("color", color); 35 | //} 36 | 37 | void imGUI::Render() { 38 | OpenGL::GetGL()->SetDepthTest(false); 39 | ImGui::Render(); 40 | ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); 41 | OpenGL::GetGL()->SetDepthTest(true); 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /Foundation/src/Sound/Speaker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Base/UniqueInstance.h" 3 | #include 4 | #include 5 | #include 6 | 7 | namespace AA { 8 | /// 9 | /// Source of playback for a buffer. 10 | /// 11 | class Speaker : public UniqueInstance { 12 | public: 13 | void PlayInterrupt(); 14 | void PlayNoOverlap(); 15 | void StopPlay(); 16 | void AssociateBuffer(const ALuint& buffer); 17 | 18 | //void SetPosition(const float& x, const float& y, const float& z); 19 | //void SetPosition(const glm::vec3& loc); 20 | //void SetLocation(const float& x, const float& y, const float& z); 21 | //void SetLocation(const glm::vec3& loc); 22 | 23 | //void SetVelocity(float velocity); 24 | 25 | //void SetDirection(const float& x, const float& y, const float& z); 26 | //void SetDirection(const glm::vec3& dir); 27 | 28 | void SetVolume(const float gain); 29 | 30 | //void SetLooping(const bool& opt); 31 | //void SetRelative(const bool& opt); 32 | 33 | Speaker(); 34 | Speaker(const Speaker& old_speaker); // copy 35 | Speaker& operator = (const Speaker& t); 36 | ~Speaker(); 37 | 38 | private: 39 | bool isPlaying(); 40 | bool p_BufferSet = false; 41 | ALuint p_Source = 0; 42 | }; 43 | 44 | } // end namespace AA 45 | -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/InternalShaders/Basic.cpp: -------------------------------------------------------------------------------- 1 | #include "Basic.h" 2 | #include 3 | namespace AA { 4 | namespace InternalShaders { 5 | 6 | static OGLShader* BASICSHADER = NULL; // setup once 7 | 8 | void Basic::Init() { 9 | if (BASICSHADER) 10 | return; 11 | 12 | const std::string VERT_CODE = 13 | R"( 14 | #version 430 core 15 | layout (location = 0) in vec3 inPos; 16 | uniform mat4 u_view_matrix; 17 | uniform mat4 u_projection_matrix; 18 | uniform mat4 u_model_matrix; 19 | void main() { 20 | gl_Position = u_projection_matrix * u_view_matrix * u_model_matrix * vec4(inPos, 1.0); 21 | } 22 | )"; 23 | 24 | const std::string FRAG_CODE = 25 | R"( 26 | #version 430 core 27 | out vec4 out_Color; 28 | void main() { 29 | out_Color = vec4(1.0,1.0,1.0,1.0); 30 | } 31 | )"; 32 | 33 | BASICSHADER = new OGLShader(VERT_CODE.c_str(), FRAG_CODE.c_str()); 34 | } 35 | 36 | OGLShader* Basic::Get() { 37 | if (!BASICSHADER) { 38 | Init(); 39 | } else { 40 | BASICSHADER->Use(); 41 | } 42 | return BASICSHADER; 43 | } 44 | 45 | void Basic::Shutdown() { 46 | if (BASICSHADER) { 47 | delete BASICSHADER; 48 | BASICSHADER = nullptr; 49 | } 50 | } 51 | 52 | bool Basic::IsActive() { 53 | return BASICSHADER; 54 | } 55 | 56 | } // end namespace InternalShaders 57 | } // end namespace AA 58 | -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/Loaders/MaterialLoader.cpp: -------------------------------------------------------------------------------- 1 | #include "AssimpSceneLoader.h" 2 | namespace AA { 3 | Material AssimpSceneLoader::LoadAll(const aiScene* scene, const aiMaterial* ai_material) { 4 | aiString name{}; 5 | ai_material->Get(AI_MATKEY_NAME, name); 6 | 7 | // find color/tint/albedo 8 | aiColor3D color(0.f, 0.f, 0.f); 9 | ai_material->Get(AI_MATKEY_COLOR_DIFFUSE, color); 10 | 11 | // find ambient 12 | aiColor3D ambient(0.f, 0.f, 0.f); 13 | ai_material->Get(AI_MATKEY_COLOR_AMBIENT, ambient); 14 | 15 | // find specular 16 | aiColor3D specular(0.f, 0.f, 0.f); 17 | ai_material->Get(AI_MATKEY_COLOR_SPECULAR, specular); 18 | 19 | // find emission 20 | aiColor3D emission(0.f, 0.f, 0.f); 21 | ai_material->Get(AI_MATKEY_COLOR_EMISSIVE, emission); 22 | 23 | Material out_mat; 24 | out_mat.Diffuse.x = color.r; 25 | out_mat.Diffuse.y = color.g; 26 | out_mat.Diffuse.z = color.b; 27 | 28 | out_mat.Ambient.x = ambient.r; 29 | out_mat.Ambient.y = ambient.g; 30 | out_mat.Ambient.z = ambient.b; 31 | 32 | out_mat.SpecularColor.x = specular.r; 33 | out_mat.SpecularColor.y = specular.g; 34 | out_mat.SpecularColor.z = specular.b; 35 | 36 | out_mat.Emission.x = emission.r; 37 | out_mat.Emission.y = emission.g; 38 | out_mat.Emission.z = emission.b; 39 | 40 | return out_mat; 41 | } 42 | } // end namespace AA 43 | -------------------------------------------------------------------------------- /Foundation/include/ArcherEngine/Controls/Input.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | namespace AA { 3 | struct MouseScrollWheel final { 4 | double xOffset; 5 | double yOffset; // normal mouse 6 | }; 7 | struct MouseCursorPos final { 8 | double xOffset; 9 | double yOffset; 10 | }; 11 | struct KeyboardButtons final { 12 | //keyboard 13 | bool esc, f1, f2, f3, f4, f5, f6, f7, f8, f9, 14 | f10, f11, f12; 15 | 16 | bool graveAccent, n1, n2, n3, n4, n5, n6, n7, 17 | n8, n9, n0, minus, equal, backspace; 18 | 19 | bool a, b, c, d, e, f, g, h, i, j, k, l, m, 20 | n, o, p, q, r, s, t, u, v, w, x, y, z; 21 | 22 | bool tab, leftShift, rightShift, leftControl, 23 | rightControl, leftAlt, rightAlt, spacebar; 24 | 25 | bool leftSquareBracket, rightSquareBracket; 26 | 27 | bool backslash, semiColon, apostrophe, enter; 28 | 29 | bool comma, period, forwardSlash; 30 | 31 | bool printScreen, scrollLock, pauseBreak, insert, 32 | del, home, end, pageUp, pageDown; 33 | 34 | bool upArrow, downArrow, leftArrow, rightArrow; 35 | 36 | bool menu, leftSuper, rightSuper; 37 | 38 | // todo 39 | // bool keypad_keys; 40 | }; 41 | 42 | struct MouseButtons final { 43 | //mouse buttons 44 | bool mouseButton1, mouseButton2, mouseButton3, 45 | mouseButton4, mousebutton5, mouseButton6, 46 | mousebutton7, mouseButton8; 47 | }; 48 | } // end namespace AA -------------------------------------------------------------------------------- /Foundation/src/Scene/Lights.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace AA { 5 | 6 | /* Point Light Examples 7 | * Distance Constant Linear Quadratic 8 | * 7 1.0 0.7 1.8 9 | * 13 1.0 0.35 0.44 10 | * 20 1.0 0.22 0.20 11 | * 32 1.0 0.14 0.07 12 | * 50 1.0 0.09 0.032 13 | * 65 1.0 0.07 0.017 14 | * 100 1.0 0.045 0.0075 15 | * 160 1.0 0.027 0.0028 16 | * 200 1.0 0.022 0.0019 17 | * 325 1.0 0.014 0.0007 18 | * 600 1.0 0.007 0.0002 19 | * 3250 1.0 0.0014 0.000007 20 | */ 21 | struct PointLight { 22 | PointLight(glm::vec3 pos, float constant, float linear, float quad, glm::vec3 amb, 23 | glm::vec3 diff, glm::vec3 spec); 24 | 25 | glm::vec3 Position; 26 | float Constant, Linear, Quadratic; 27 | glm::vec3 Ambient, Diffuse, Specular; 28 | 29 | int id; 30 | }; 31 | 32 | struct SpotLight { 33 | SpotLight(glm::vec3 pos, glm::vec3 dir, float inner, float outer, float constant, 34 | float linear, float quad, glm::vec3 amb, glm::vec3 diff, glm::vec3 spec); 35 | glm::vec3 Position, Direction; 36 | float CutOff, OuterCutOff; 37 | float Constant, Linear, Quadratic; 38 | glm::vec3 Ambient, Diffuse, Specular; 39 | 40 | int id; 41 | 42 | }; 43 | 44 | } // end namespace AA -------------------------------------------------------------------------------- /Foundation/src/Mesh/Animation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Math/Conversions.h" 3 | #include "../Base/UniqueInstance.h" 4 | #include "Vertex.h" 5 | #include "Bone.h" 6 | #include "Skeleton.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace AA { 19 | 20 | struct AnimationNodeTree { 21 | glm::mat4 transformation; 22 | std::string name; 23 | int childrenCount; 24 | std::vector children; 25 | }; 26 | 27 | class AnimProp; 28 | 29 | struct AnimationData; 30 | 31 | class Animation : public UniqueInstance { 32 | 33 | public: 34 | 35 | Animation(); 36 | 37 | Animation(const std::string& animationPath, Skeleton& in_out_skele); 38 | 39 | ~Animation(); 40 | 41 | float GetTicksPerSecond(); 42 | 43 | float GetDuration(); 44 | 45 | const AnimationNodeTree& GetRootNode(); 46 | 47 | private: 48 | friend class Animator; 49 | void ReadMissingBones(const aiAnimation* animation, Skeleton& in_out_skele); 50 | 51 | void ReadHeirarchyData(AnimationNodeTree& dest, const aiNode* src); 52 | 53 | Skeleton m_Skeleton; 54 | 55 | float m_Duration = 0.0f; 56 | 57 | float m_TicksPerSecond = 0.0f; 58 | 59 | AnimationNodeTree m_RootNode; 60 | 61 | }; 62 | 63 | } 64 | -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/InternalShaders/Skycube.cpp: -------------------------------------------------------------------------------- 1 | #include "Skycube.h" 2 | #include 3 | namespace AA::InternalShaders { 4 | 5 | static OGLShader* SKYCUBESHADER = NULL; // setup once 6 | 7 | //todo: optimization - uniform buffers https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL 8 | void Skycube::Init() { 9 | if (SKYCUBESHADER) 10 | return; 11 | 12 | const std::string VERT_CODE = 13 | R"( 14 | #version 430 core 15 | layout(location = 0) in vec3 inPos; 16 | out vec3 TexCoords; 17 | uniform mat4 u_projection_matrix; 18 | uniform mat4 u_view_matrix; 19 | void main(){ 20 | TexCoords = inPos; 21 | vec4 pos = u_projection_matrix * u_view_matrix * vec4(inPos, 1.0); 22 | gl_Position = pos.xyww; 23 | } 24 | )"; 25 | 26 | const std::string FRAG_CODE = 27 | R"( 28 | #version 430 core 29 | in vec3 TexCoords; 30 | out vec4 FragColor; 31 | uniform samplerCube u_skybox; 32 | void main() { 33 | FragColor = texture(u_skybox, TexCoords); 34 | } 35 | )"; 36 | 37 | SKYCUBESHADER = new OGLShader(VERT_CODE.c_str(), FRAG_CODE.c_str()); 38 | } 39 | 40 | OGLShader* Skycube::Get() { 41 | if (!SKYCUBESHADER) { 42 | Init(); 43 | } else { 44 | SKYCUBESHADER->Use(); 45 | } 46 | return SKYCUBESHADER; 47 | } 48 | 49 | void Skycube::Shutdown() { 50 | if (SKYCUBESHADER) { 51 | delete SKYCUBESHADER; 52 | SKYCUBESHADER = nullptr; 53 | } 54 | } 55 | bool Skycube::IsActive() { 56 | return SKYCUBESHADER; 57 | } 58 | } -------------------------------------------------------------------------------- /Foundation/src/Utility/FileDialog.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #ifdef __linux__ 3 | #include 4 | bool AA::NavigateFileSystem(std::string& out, const char* filetypereg, const char* starting_pref) 5 | { 6 | out.clear(); 7 | bool func_result = true; 8 | const int PATH_MAX = 1024; 9 | int status; 10 | char path[PATH_MAX]; 11 | 12 | FILE *fp; 13 | fp = popen("zenity --file-selection", "r"); 14 | if (fp == NULL) { 15 | func_result = false; 16 | /* Handle error */ 17 | } else { 18 | fgets(path, PATH_MAX, fp); 19 | std::string string_ver = path; 20 | auto end_pos = string_ver.find_first_of('\n'); 21 | out = string_ver.substr(0, end_pos); 22 | } 23 | status = pclose(fp); 24 | if (status == -1) { 25 | /* Error reported by pclose() */ 26 | } else { 27 | /* Use macros described under wait() to inspect `status' in order 28 | to determine success/failure of command executed by popen() */ 29 | } 30 | return func_result; 31 | } 32 | 33 | #elif _WIN32 34 | #include 35 | bool AA::NavigateFileSystem(std::string& out, const char* filetypereg, const char* starting_pref) 36 | { 37 | out.clear(); 38 | bool func_result = true; 39 | char* file_hunted = NULL; 40 | nfdresult_t result = NFD_OpenDialog(filetypereg, starting_pref, &file_hunted); 41 | if (result == NFD_OKAY) { 42 | out = file_hunted; 43 | } 44 | else if (result == NFD_CANCEL) { 45 | func_result = false; 46 | } 47 | else { 48 | func_result = false; 49 | } 50 | return func_result; 51 | } 52 | #endif 53 | -------------------------------------------------------------------------------- /Foundation/samples/editor/main.cpp: -------------------------------------------------------------------------------- 1 | // this example sets up a base run with drag and drop enable. 2 | // drop in a model file to see it. this is not a finished example. 3 | 4 | #include "fpp_fly.h" 5 | #include "file_menu.h" 6 | static AA::Interface archer_engine_interface{}; 7 | static unsigned int editor_cam{}; 8 | int main() { 9 | 10 | // local stuff for init, in it's own scope because we have no reason 11 | // to keep this stuff in memory after the init is done. optionally 12 | // this could all go in a function. 13 | { 14 | AA::WindowOptions winopts; 15 | winopts._windowing_mode = AA::WINDOW_MODE::MAXIMIZED; 16 | winopts._editor_drag_and_drop = true; 17 | winopts._title = "AA Editor"; 18 | if (!archer_engine_interface.Init(winopts)) return -1; 19 | archer_engine_interface.SetLogStream(true, false); // log to console 20 | 21 | editor_cam = archer_engine_interface.AddCamera(); // add camera 22 | archer_engine_interface.SetSunLight(glm::vec3(-.33f, -1.f, -.33f), glm::vec3(.8f), glm::vec3(0.1f), glm::vec3(0.5f)); 23 | auto strong_cam_ref = archer_engine_interface.GetCamera(editor_cam).lock(); 24 | strong_cam_ref->SetPosition(glm::vec3(0.0f, 65.0f, -65.f)); 25 | strong_cam_ref->SetYaw(90.f); 26 | strong_cam_ref->SetPitch(-35.f); 27 | archer_engine_interface.GetSunLight().lock()->Shadows = false; // no shadows 28 | setup_fpp_fly(editor_cam, archer_engine_interface); 29 | setup_file_menu(editor_cam, archer_engine_interface); 30 | archer_engine_interface.AddToOnQuit([]() { turn_off_fly(); remove_file_menu(); }); 31 | 32 | } 33 | 34 | return archer_engine_interface.Run(); 35 | } 36 | -------------------------------------------------------------------------------- /Foundation/src/Mesh/Spacial3D.cpp: -------------------------------------------------------------------------------- 1 | #include "Spacial3D.h" 2 | #include 3 | #include 4 | 5 | namespace AA { 6 | 7 | Spacial3D::Spacial3D() /*: mRigidBody() */{ 8 | mCurrentLocation = glm::vec3(0); 9 | mCurrentRot = glm::vec3(0); 10 | mCurrentScale = glm::vec3(1); 11 | mFinalModelMatrix = glm::mat4(1); 12 | has_unprocessed_modifications = true; 13 | } 14 | 15 | void Spacial3D::MoveTo(const glm::vec3& location) { 16 | mCurrentLocation = location; 17 | has_unprocessed_modifications = true; 18 | } 19 | 20 | void Spacial3D::ScaleTo(const glm::vec3& scale) { 21 | mCurrentScale = scale; 22 | has_unprocessed_modifications = true; 23 | } 24 | 25 | // rotates on respective xyz axii by radian amounts given in rot 26 | void Spacial3D::RotateTo(const glm::vec3& rot) { 27 | mCurrentRot = rot; 28 | has_unprocessed_modifications = true; 29 | } 30 | 31 | void Spacial3D::ProcessModifications() { 32 | if (has_unprocessed_modifications) { 33 | mFinalModelMatrix = glm::mat4(1); 34 | mFinalModelMatrix = glm::translate(mFinalModelMatrix, mCurrentLocation); 35 | mFinalModelMatrix = glm::scale(mFinalModelMatrix, mCurrentScale); 36 | 37 | // best order to avoid gimble lock the majority of the time https://youtu.be/zc8b2Jo7mno?t=449 38 | mFinalModelMatrix = glm::rotate(mFinalModelMatrix, mCurrentRot.y, glm::vec3(0, 1, 0)); 39 | mFinalModelMatrix = glm::rotate(mFinalModelMatrix, mCurrentRot.z, glm::vec3(0, 0, 1)); 40 | mFinalModelMatrix = glm::rotate(mFinalModelMatrix, mCurrentRot.x, glm::vec3(1, 0, 0)); 41 | 42 | has_unprocessed_modifications = false; 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/OGLShader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | namespace AA { 5 | class OGLShader { 6 | public: 7 | static const char* get_glsl_version(); 8 | 9 | OGLShader(const char* vert_src, const char* frag_src); 10 | ~OGLShader(); 11 | const int GetID() const; 12 | 13 | void Use() const noexcept; 14 | 15 | //uniform setters 16 | void SetBool(const std::string& name, bool value) const; 17 | void SetInt(const std::string& name, int value) const; 18 | void SetUint(const std::string& name, unsigned int value) const; 19 | void SetFloat(const std::string& name, float value) const; 20 | 21 | void SetVec2(const std::string& name, const glm::vec2& value) const; 22 | void SetVec2(const std::string& name, float x, float y) const; 23 | 24 | void SetVec3(const std::string& name, const glm::vec3& value) const; 25 | void SetVec3(const std::string& name, float x, float y, float z) const; 26 | 27 | void SetVec4(const std::string& name, const glm::vec4& value) const; 28 | void SetVec4(const std::string& name, float x, float y, float z, float w) const; 29 | 30 | void SetMat2(const std::string& name, const glm::mat2& mat) const; 31 | void SetMat3(const std::string& name, const glm::mat3& mat) const; 32 | void SetMat4(const std::string& name, const glm::mat4& mat) const; 33 | 34 | private: 35 | 36 | void stop() const noexcept; // probably not used 37 | 38 | int getAndCheckShaderUniform(const std::string& name) const; 39 | 40 | OGLShader() = delete; 41 | 42 | unsigned int ID; 43 | 44 | void loadShader(const char* vert, const char* frag); 45 | 46 | 47 | }; 48 | } // end namespace AA 49 | -------------------------------------------------------------------------------- /doc/readme.md: -------------------------------------------------------------------------------- 1 | # ArcherEngine Guide 2 | 3 | archer lib will need to be linked to your C++ project to use. This should be pretty common practice for an experienced C++ user. Build it, link it, put the public headers in your include space, and you can use the lib. 4 | 5 | ## Building for your OS 6 | 7 | ### LINUX 8 | 9 | External Libraries will be required to build Archer. 10 | 11 | #### Linux Mint: 12 | 13 | ##### required libs: 14 | 15 | ```bash 16 | sudo apt-get install libimgui-dev libglfw3-dev libglm-dev libopengl-dev libsndfile-dev libopenal-dev libcurl-dev libassimp-dev libglew-dev 17 | ``` 18 | 19 | ##### build commands: 20 | 21 | ```bash 22 | chmod +x linux_build.sh 23 | ./linux_build.sh 24 | ``` 25 | 26 | By default the examples will be built. You can run the fpp_demo by going to build/examples/fpp and typing ./fpp_demo 27 | the demo does not have any models loaded by default, so you will see a black screen. Drag a model file on the screen to load it. 28 | You can fly around with wasd, move down with c, move up with spacebar, look with the mouse, toggle fpp control with right click. 29 | That is all the demo does for now. 30 | 31 | ### MACOS 32 | 33 | TODO 34 | 35 | ### WINDOWS 36 | 37 | #### requires vcpkg to handle libs 38 | 39 | see [https://github.com/microsoft/vcpkg](https://github.com/microsoft/vcpkg) 40 | should work via the vcpkg.json manifest once vcpkg is set up 41 | 42 | use cmake to prepare your build. 43 | 44 | ## Library Function Definitions 45 | 46 | - Generate via Doxygen 47 | - See [Interface.h](AAEngine/include/AAEngine/Interface.h) Header for core user access functions. 48 | - Old ones (todo - new version): [Old Docs Webpage](https://mattearly.github.io/AncientArcher/index.html) 49 | -------------------------------------------------------------------------------- /Foundation/src/Mesh/Animator.cpp: -------------------------------------------------------------------------------- 1 | #include "Animator.h" 2 | namespace AA { 3 | 4 | Animator::Animator(std::shared_ptr anim, glm::mat4 inv_trans) { 5 | cached_global_inverse_transform = inv_trans; 6 | m_CurrentTime = 0.0; 7 | m_CurrentAnimation = anim; 8 | 9 | m_FinalBoneMatrices.reserve(100); 10 | 11 | for (int i = 0; i < 100; i++) 12 | m_FinalBoneMatrices.push_back(glm::mat4(1.0f)); 13 | } 14 | 15 | Animator::~Animator() { 16 | m_FinalBoneMatrices.clear(); 17 | m_CurrentAnimation.reset(); 18 | } 19 | 20 | void Animator::UpdateAnimation(float dt) { 21 | m_DeltaTime = dt; 22 | if (m_CurrentAnimation) { 23 | m_CurrentTime += m_CurrentAnimation->GetTicksPerSecond() * dt; 24 | m_CurrentTime = fmod(m_CurrentTime, m_CurrentAnimation->GetDuration()); 25 | CalculateBoneTransform(&m_CurrentAnimation->GetRootNode(), glm::mat4(1.0f)); 26 | } 27 | } 28 | 29 | void Animator::CalculateBoneTransform(const AnimationNodeTree* node, glm::mat4 parentTransform) { 30 | BONE_MAP_KEY nodeName = node->name; 31 | glm::mat4 nodeTransform = node->transformation; 32 | 33 | Bone* Bone = m_CurrentAnimation->m_Skeleton.FindBone(nodeName); 34 | 35 | if (Bone) { 36 | Bone->Update(m_CurrentTime); 37 | nodeTransform = Bone->GetLocalTransform(); 38 | } 39 | 40 | glm::mat4 accum_transform = parentTransform * nodeTransform; 41 | 42 | auto boneInfoMap = m_CurrentAnimation->m_Skeleton.GetBoneIDMap(); 43 | 44 | if (boneInfoMap.find(nodeName) != boneInfoMap.end()) { // if bone found 45 | int index = boneInfoMap[nodeName].id; 46 | glm::mat4 offset = boneInfoMap[nodeName].offset; 47 | m_FinalBoneMatrices[index] = cached_global_inverse_transform * accum_transform * offset; 48 | } 49 | 50 | for (int i = 0; i < node->childrenCount; i++) 51 | CalculateBoneTransform(&node->children[i], accum_transform); 52 | } 53 | 54 | std::vector Animator::GetFinalBoneMatrices() const{ 55 | return m_FinalBoneMatrices; 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/InternalShaders/Shadow.cpp: -------------------------------------------------------------------------------- 1 | #include "Shadow.h" 2 | #include 3 | namespace AA::InternalShaders { 4 | 5 | static OGLShader* SHADOWSHADER = NULL; // setup once 6 | 7 | //todo: optimization - uniform buffers https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL 8 | void Shadow::Init() { 9 | if (SHADOWSHADER) 10 | return; 11 | 12 | const std::string VERT_CODE = 13 | R"( 14 | #version 430 core 15 | layout (location = 0) in vec3 inPos; 16 | layout (location = 3) in ivec4 inBoneIds; 17 | layout (location = 4) in vec4 inWeights; 18 | 19 | uniform mat4 u_light_space_matrix; 20 | uniform mat4 u_model_matrix; 21 | 22 | const int MAX_BONES = 100; 23 | const int MAX_BONE_INFLUENCE = 4; 24 | 25 | uniform mat4 u_final_bone_mats[MAX_BONES]; 26 | uniform int u_is_animating; 27 | 28 | void main() 29 | { 30 | vec4 totalPosition = vec4(0.0); 31 | 32 | if (u_is_animating > 0) { 33 | for(int i = 0 ; i < MAX_BONE_INFLUENCE ; i++) { 34 | if(inBoneIds[i] == -1) continue; 35 | if(inBoneIds[i] >= MAX_BONES) { 36 | totalPosition = vec4(inPos, 1.0f); 37 | break; 38 | } 39 | vec4 localPosition = u_final_bone_mats[inBoneIds[i]] * vec4(inPos, 1.0); 40 | totalPosition += localPosition * inWeights[i]; 41 | } 42 | } else { // Not Animating 43 | //vs_out.Pos = (u_model_matrix * vec4(inPos, 1.0)).xyz; 44 | totalPosition = vec4(inPos, 1.0); 45 | } 46 | 47 | gl_Position = u_light_space_matrix * u_model_matrix * totalPosition; 48 | } 49 | )"; 50 | 51 | const std::string FRAG_CODE = 52 | R"( 53 | #version 430 core 54 | void main() {} 55 | )"; 56 | 57 | SHADOWSHADER = new OGLShader(VERT_CODE.c_str(), FRAG_CODE.c_str()); 58 | } 59 | 60 | OGLShader* Shadow::Get() { 61 | if (!SHADOWSHADER) { 62 | Init(); 63 | } else { 64 | SHADOWSHADER->Use(); 65 | } 66 | return SHADOWSHADER; 67 | } 68 | 69 | void Shadow::Shutdown() { 70 | if (SHADOWSHADER) { 71 | delete SHADOWSHADER; 72 | SHADOWSHADER = nullptr; 73 | } 74 | } 75 | bool Shadow::IsActive() { 76 | return SHADOWSHADER; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/Loaders/Cache.h: -------------------------------------------------------------------------------- 1 | // The purpose of this singleton class is to keep track of the resources we have uploaded 2 | // via OpenGL to the graphics card while supporting re-use were possible. 3 | 4 | #pragma once 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "../../../Mesh/TextureTypes.h" 13 | #include "../../../Mesh/Material.h" 14 | #include "../../../Mesh/MeshInfo.h" 15 | #include "../../../Mesh/Skeleton.h" 16 | 17 | namespace AA { 18 | 19 | /// 20 | /// Holds info for reusing models that are already loaded. 21 | /// 22 | struct SceneInfo { 23 | std::vector meshes; // vao and texture refs 24 | std::string path = ""; // load path and Unique Identifier Key 25 | int ref_count = 1; 26 | Skeleton* skelly = nullptr; 27 | glm::mat4 inverse_transform = glm::mat4(1); 28 | 29 | friend bool operator==(const SceneInfo& lhs, const SceneInfo& rhs); 30 | 31 | friend bool operator!=(const SceneInfo& lhs, const SceneInfo& rhs); 32 | }; 33 | 34 | /// 35 | /// Holds models and textures that are already loaded. 36 | /// 37 | 38 | class Cache { 39 | public: 40 | static Cache* Get(); 41 | 42 | // scene 43 | int try_load_from_cache(std::vector& out_meshes, const std::string& path); 44 | 45 | // scene with skeleton 46 | int try_load_from_cache(std::vector& out_meshes, Skeleton& out_skel, glm::mat4& inv_trans, const std::string& path_key); 47 | 48 | // already loaded textures 49 | int try_load_from_cache(TextureMapType& out_textures, const std::string& path); 50 | 51 | void remove_scene_at_path(const std::string& path_to_remove); 52 | 53 | void add(const SceneInfo& si); 54 | void add(const Texture2DInfo& ti); 55 | void add(const TextureMapType& list); 56 | 57 | void remove_sceneinfo(const SceneInfo& matching); 58 | 59 | private: 60 | 61 | std::forward_list all_loaded_scenes_{}; 62 | std::forward_list all_loaded_2d_textures_{}; 63 | 64 | Cache() = default; 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /Foundation/include/ArcherEngine/WindowOptions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "Version.h" 4 | namespace AA { 5 | enum class WINDOW_MODE { FULLSCREEN, WINDOWED, WINDOWED_DEFAULT, FULLSCREEN_BORDERLESS, MAXIMIZED }; 6 | enum class RENDER_TECH { OPENGL4, D3D11, VULKAN1 }; 7 | // hidden is the same as normal, just not drawn 8 | // disabled is raw motion (doesn't apply desktop cursor settings) - check if it is supported with glfwRawMouseMotionSupported() 9 | enum class CURSOR_MODE { HIDDEN = 0x00034002, DISABLED = 0x00034003, NORMAL = 0x00034001 }; //glfw hidden, disabled, normal 10 | 11 | struct WindowOptions final { 12 | // min size is the size of a gameboy screen http://www2.hawaii.edu/~dkm/project2/color.html#:~:text=The%20screen%20resolution%20was%20the,communications%20port%20for%20wireless%20linking. 13 | static constexpr int ENGINE_MIN_WIDTH = 160, ENGINE_MIN_HEIGHT = 144; 14 | int _min_width = ENGINE_MIN_WIDTH, _min_height = ENGINE_MIN_HEIGHT; 15 | int _width = 800, _height = 600; 16 | std::string _title = "default title"; 17 | WINDOW_MODE _windowing_mode = WINDOW_MODE::WINDOWED_DEFAULT; 18 | RENDER_TECH _rendering_tech = RENDER_TECH::OPENGL4; 19 | CURSOR_MODE _cursor_mode = CURSOR_MODE::NORMAL; 20 | int _msaa_samples = -1; 21 | unsigned int _stencil_bits = 8; 22 | bool _vsync = false; 23 | bool _gamma_correction = false; 24 | bool _editor_drag_and_drop = false; 25 | }; 26 | 27 | static void SetDefaults(WindowOptions& winopts) { 28 | winopts._min_width = WindowOptions::ENGINE_MIN_WIDTH; 29 | winopts._min_height = WindowOptions::ENGINE_MIN_HEIGHT; 30 | winopts._width = 800; 31 | winopts._height = 600; 32 | winopts._title = "ArcherEngine v" + std::to_string(ENGINEVERSIONMAJOR) + '.' + std::to_string(ENGINEVERSIONMINOR) + '.' + std::to_string(ENGINEVERSIONPATCH); 33 | winopts._windowing_mode = WINDOW_MODE::WINDOWED_DEFAULT; 34 | winopts._rendering_tech = RENDER_TECH::OPENGL4; 35 | winopts._cursor_mode = CURSOR_MODE::NORMAL; 36 | winopts._msaa_samples = -1; // GLFW_DONT_CARE 37 | winopts._stencil_bits = 8; 38 | winopts._vsync = false; 39 | winopts._gamma_correction = false; 40 | winopts._editor_drag_and_drop = false; 41 | } 42 | } // end namespace 43 | -------------------------------------------------------------------------------- /Foundation/src/Mesh/Material.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | namespace AA { 3 | 4 | /// 5 | /// Structure of our supported Materials 6 | /// 7 | struct Material { 8 | public: 9 | // Kd: specifies diffuse color, which typically contributes most of the 10 | // color to an object [see Wikipedia entry for Diffuse Reflection]. will get modified by a 11 | // colored texture map specified in the map_Kd statement 12 | glm::vec3 Diffuse{}; 13 | 14 | // Ka: specifies ambient color, to account for light that is scattered 15 | // about the entire scene[see Wikipedia entry for Phong Reflection Model] using values 16 | // between 0 and 1 for the RGB components. 17 | glm::vec3 Ambient{}; 18 | 19 | // Ks: specifies specular color, the color seen where the surface 20 | // is shiny and mirror-like [see Wikipedia entry for Specular Reflection]. 21 | glm::vec3 SpecularColor{}; 22 | 23 | // color of the emission 24 | glm::vec3 Emission{}; 25 | 26 | // float SpecularHightlights{}; // Ns: defines the focus of specular highlights in the material. Ns values normally range from 0 to 1000, with a high value resulting in a tight, concentrated highlight. 27 | 28 | // float OpticalDensity{}; // Ni: defines the optical density (aka index of refraction) in the current material. The values can range from 0.001 to 10. A value of 1.0 means that light does not bend as it passes through an object. 29 | 30 | // float Dissolve{}; // d: specifies a factor for dissolve, how much this material dissolves into the background. A factor of 1.0 is fully opaque. A factor of 0.0 is completely transparent. 31 | 32 | // int IlluminationModel{}; // specifies an illumination model, using a numeric value. See Notes below for more detail on the illum keyword. The value 0 represents the simplest illumination model, relying on the Kd for the material modified by a texture map specified in a map_Kd statement if present. The compilers of this resource believe that the choice of illumination model is irrelevant for 3D printing use and is ignored on import by some software applications. For example, the MTL Loader in the threejs Javascript library appears to ignore illum statements 33 | 34 | // texture maps to blend with - data not saved, see TextureInfo.h 35 | }; 36 | 37 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # ArcherEngine 2 | 3 | *Archer Engine is a Multimedia C++ library for setting up and interfacing with 3D environments. All of the functionality is accessed through C++ functions in archer lib* 4 | 5 | ## Use Context 6 | 7 | - Windowing 8 | - 3D Scene Loader and transform controls 9 | - OpenGL Render Pipeline 10 | - Basic Texturing, Color Emissions 11 | - Directional, Point, and Spot Lights 12 | - Skeletal Animation Loader & Player 13 | - Sunlight Shadows 14 | - Cameras 15 | - 3D SoundEffects 16 | - Ambient/Music background music 17 | - Run Time scripts C++ Lambdas 18 | 19 | ## Build & Run 20 | 21 | ### [doc/readme.md](doc/readme.md) 22 | 23 | ### Unit Tests 24 | 25 | TODO 26 | 27 | ### Examples 28 | 29 | | SYNTAX | DESCRIPTION | 30 | | ------ | ----------- | 31 | | ../build/samples/editor/editor_example.exe | Example Game Engine Front End Editor Starting Point | 32 | 33 | ## Example Code 34 | 35 | ```cpp 36 | #include 37 | AA::Interface instance; // global so you can access it in lambda scripting 38 | int main(int argc, char** argv) { 39 | WindowOptions opts; 40 | instance.Init(opts); // Initializes Required Hardware Access 41 | /* 42 | * add camera, lights, models, sounds, animations, controls, lambda scripts, etc 43 | */ 44 | return instance.Run(); // starts the simulation and stays running until Shutdown() or Window.Close() 45 | } 46 | ``` 47 | 48 | ## Development 49 | 50 | Currently being developed as a continuation of [the original project](https://github.com/mattearly/AncientArcher). This new version should build on any OS. Making a game engine library is a huge task and I could use all the help I can get. 51 | 52 | ### Development Schedule 53 | 54 | Currently the aim is to put out monthly updates. You can find these in video form on the Code, Tech, and Tutorials YouTube Channel. 55 | 56 | ### Branch Rules 57 | 58 | - `main` should only be updated via pull requests from `dev`. Done once a month, with an accompanied release. 59 | - `dev` branch should only be updated via Pull Requests from `other-branches` 60 | - As for `other-branches`, create them from starting point `dev` to do work, and merge them into `dev` when complete. 61 | 62 | These rules are somewhat loose but we need some structure to allow an understanding of how others might be trying to work on things. 63 | -------------------------------------------------------------------------------- /Foundation/src/Scene/SunLight.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../OS/OpenGL/Graphics.h" 3 | #include "../OS/OpenGL/InternalShaders/Uber.h" 4 | namespace AA { 5 | 6 | unsigned int ShadowDepthMapFBO = 0; 7 | unsigned int ShadowDepthMapTextureId = 0; 8 | 9 | SunLight::SunLight(glm::vec3 dir, glm::vec3 amb, glm::vec3 diff, glm::vec3 spec) 10 | : Direction(dir), Ambient(amb), Diffuse(diff), Specular(spec) { 11 | 12 | Shadows = true; 13 | 14 | ShadowNearPlane = 1.0f; 15 | ShadowFarPlane = 7.5f * 100; 16 | ShadowOrthoSize = 500.f; 17 | 18 | ShadowWidth = 1024; 19 | ShadowHeight = 1024; 20 | 21 | ShadowBiasMin = 0.025f; 22 | ShadowBiasMax = 0.0005f; 23 | 24 | auto uber_shader = InternalShaders::Uber::Get(); 25 | uber_shader->SetInt("u_is_dir_light_on", 1); 26 | uber_shader->SetVec3("u_dir_light.Direction", Direction); 27 | uber_shader->SetVec3("u_dir_light.Ambient", Ambient); 28 | uber_shader->SetVec3("u_dir_light.Diffuse", Diffuse); 29 | uber_shader->SetVec3("u_dir_light.Specular", Specular); 30 | uber_shader->SetBool("u_dir_light.Shadows", Shadows); 31 | uber_shader->SetFloat("u_dir_light.ShadowBiasMin", ShadowBiasMin); 32 | uber_shader->SetFloat("u_dir_light.ShadowBiasMax", ShadowBiasMax); 33 | 34 | 35 | if (ShadowDepthMapFBO != 0 && ShadowDepthMapTextureId != 0) { 36 | return; // ALREADY SET 37 | } 38 | 39 | // init if needed 40 | ShadowDepthMapFBO = OpenGL::GetGL()->CreateDepthMapFBO(ShadowWidth, ShadowHeight, ShadowDepthMapTextureId); 41 | } 42 | 43 | SunLight::~SunLight() { 44 | // remove from graphics card on destroy 45 | if (ShadowDepthMapFBO != 0) { 46 | OpenGL::GetGL()->DeleteFramebuffer(1u, ShadowDepthMapFBO); 47 | ShadowDepthMapFBO = 0; 48 | } 49 | if (ShadowDepthMapTextureId != 0) { 50 | OpenGL::GetGL()->DeleteTex(1u, ShadowDepthMapTextureId); 51 | ShadowDepthMapTextureId = 0; 52 | } 53 | } 54 | 55 | const unsigned int& SunLight::GetFBO() const { 56 | return ShadowDepthMapFBO; 57 | } 58 | 59 | const unsigned int& SunLight::GetTexID() const { 60 | return ShadowDepthMapTextureId; 61 | } 62 | void SunLight::SetShadowBiasMin(float min) noexcept { 63 | ShadowBiasMin = min; 64 | InternalShaders::Uber::Get()->SetFloat("u_dir_light.ShadowBiasMin", min); 65 | } 66 | void SunLight::SetShadowBiasMax(float max) noexcept { 67 | ShadowBiasMax = max; 68 | InternalShaders::Uber::Get()->SetFloat("u_dir_light.ShadowBiasMax", max); 69 | } 70 | } // end namespace AA -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/InternalShaders/Stencil.cpp: -------------------------------------------------------------------------------- 1 | #include "Stencil.h" 2 | #include 3 | namespace AA::InternalShaders { 4 | 5 | static OGLShader* STENCILSHADER = nullptr; 6 | 7 | //todo: optimization - uniform buffers https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL 8 | void Stencil::Init() { 9 | if (STENCILSHADER) 10 | return; 11 | 12 | const std::string VERT_CODE = 13 | R"( 14 | #version 430 core 15 | layout(location=0)in vec3 inPos; 16 | layout(location=2)in vec3 inNorm; 17 | layout(location=3)in ivec4 inBoneIds; 18 | layout(location=4)in vec4 inWeights; 19 | 20 | const int MAX_BONES = 100; 21 | const int MAX_BONE_INFLUENCE = 4; 22 | 23 | uniform mat4 u_projection_matrix; 24 | uniform mat4 u_view_matrix; 25 | uniform mat4 u_model_matrix; 26 | 27 | uniform mat4 u_final_bone_mats[MAX_BONES]; 28 | uniform int u_stencil_with_normals; 29 | uniform float u_stencil_scale; 30 | uniform int u_is_animating; 31 | 32 | void main(){ 33 | vec4 totalPosition = vec4(0.0); 34 | if (u_is_animating > 0) { 35 | for(int i = 0 ; i < MAX_BONE_INFLUENCE ; i++) { 36 | if(inBoneIds[i] == -1) continue; 37 | if(inBoneIds[i] >= MAX_BONES) { 38 | totalPosition = vec4(inPos, 1.0f); 39 | break; 40 | } 41 | vec4 localPosition = u_final_bone_mats[inBoneIds[i]] * vec4(inPos,1.0); 42 | totalPosition += localPosition * inWeights[i]; 43 | } 44 | } else { // Not Animating 45 | totalPosition = vec4(inPos, 1.0); 46 | } 47 | mat4 viewMatrix = u_view_matrix * u_model_matrix; 48 | if (u_stencil_with_normals > 0) { 49 | gl_Position = u_projection_matrix * viewMatrix * vec4(totalPosition.xyz + inNorm * (u_stencil_scale - 1.0), 1.0); 50 | } else { 51 | gl_Position = u_projection_matrix * viewMatrix * totalPosition; 52 | } 53 | } 54 | )"; 55 | 56 | const std::string FRAG_CODE = R"( 57 | #version 430 58 | out vec4 FragColor; 59 | uniform vec3 u_stencil_color; 60 | void main() { 61 | FragColor = vec4(u_stencil_color, 1.0); 62 | } 63 | )"; 64 | 65 | STENCILSHADER = new OGLShader(VERT_CODE.c_str(), FRAG_CODE.c_str()); 66 | } 67 | 68 | OGLShader* Stencil::Get() { 69 | if (!STENCILSHADER) { 70 | Init(); 71 | } else { 72 | STENCILSHADER->Use(); 73 | } 74 | return STENCILSHADER; 75 | } 76 | 77 | void Stencil::Shutdown() { 78 | if (STENCILSHADER) { 79 | delete STENCILSHADER; 80 | STENCILSHADER = nullptr; 81 | } 82 | } 83 | bool Stencil::IsActive() { 84 | return STENCILSHADER; 85 | } 86 | } -------------------------------------------------------------------------------- /Foundation/src/Mesh/Bone.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Math/Conversions.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | namespace AA { 8 | 9 | struct KeyPosition { 10 | glm::vec3 position; 11 | float timeStamp; 12 | }; 13 | 14 | struct KeyRotation { 15 | glm::quat orientation; 16 | float timeStamp; 17 | }; 18 | 19 | struct KeyScale { 20 | glm::vec3 scale; 21 | float timeStamp; 22 | }; 23 | 24 | class Bone { 25 | private: 26 | std::vector m_Positions; 27 | std::vector m_Rotations; 28 | std::vector m_Scales; 29 | int m_NumPositions; 30 | int m_NumRotations; 31 | int m_NumScalings; 32 | 33 | glm::mat4 m_LocalTransform; 34 | std::string m_Name; 35 | int m_ID; 36 | 37 | public: 38 | 39 | /*reads keyframes from aiNodeAnim*/ 40 | Bone(const std::string& name, int ID, const aiNodeAnim* channel); 41 | 42 | /*interpolates b/w positions, rotations & scaling keys based on the curren time of 43 | the animation and prepares the local transformation matrix by combining all keys 44 | tranformations*/ 45 | void Update(float animationTime); 46 | 47 | glm::mat4 GetLocalTransform(); 48 | std::string GetBoneName() const; 49 | int GetBoneID(); 50 | 51 | /* Gets the current index on mKeyPositions to interpolate to based on 52 | the current animation time*/ 53 | int GetPositionIndex(float animationTime); 54 | 55 | /* Gets the current index on mKeyRotations to interpolate to based on the 56 | current animation time*/ 57 | int GetRotationIndex(float animationTime); 58 | 59 | /* Gets the current index on mKeyScalings to interpolate to based on the 60 | current animation time */ 61 | int GetScaleIndex(float animationTime); 62 | 63 | private: 64 | 65 | /* Gets normalized value for Lerp & Slerp*/ 66 | float GetScaleFactor(float lastTimeStamp, float nextTimeStamp, float animationTime); 67 | 68 | /*figures out which position keys to interpolate b/w and performs the interpolation 69 | and returns the translation matrix*/ 70 | glm::mat4 InterpolatePosition(float animationTime); 71 | 72 | /*figures out which rotations keys to interpolate b/w and performs the interpolation 73 | and returns the rotation matrix*/ 74 | glm::mat4 InterpolateRotation(float animationTime); 75 | 76 | /*figures out which scaling keys to interpolate b/w and performs the interpolation 77 | and returns the scale matrix*/ 78 | glm::mat4 InterpolateScaling(float animationTime); 79 | }; 80 | 81 | } 82 | 83 | -------------------------------------------------------------------------------- /Foundation/include/ArcherEngine/OS/Interface/Window.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../../Controls/Input.h" 3 | #include "../../WindowOptions.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace AA { 13 | 14 | class Window final { 15 | public: 16 | 17 | Window(); 18 | Window(WindowOptions winopts); 19 | Window(std::shared_ptr winopts); 20 | ~Window(); 21 | 22 | /// 23 | /// Hides the mouse cursor. 24 | /// Used for when you are drawing your own cursor. 25 | /// 26 | void SetCursorToHidden() noexcept; 27 | 28 | /// 29 | /// Disables the cursor. See disabled in glfw documentation. https://www.glfw.org/docs/3.3/input_guide.html 30 | /// 31 | void SetCursorToDisabled() noexcept; 32 | 33 | /// 34 | /// Sets the Cursor the the standard OS Cursor. 35 | /// 36 | void SetCursorToNormal() noexcept; 37 | 38 | bool GetShouldClose(); 39 | 40 | int GetCurrentWidth(); 41 | int GetCurrentHeight(); 42 | 43 | void SetCurrentWidthAndHeight(int w, int h) noexcept; 44 | void SetMinWidthAndHeight(int w, int h) noexcept; 45 | 46 | int GetCurrentMinWidth() const; 47 | int GetCurrentMinHeight() const; 48 | 49 | void SetGammaCorrection(const bool enabled); 50 | bool GetGammaCorrection() const; 51 | 52 | void Close(); 53 | 54 | void SetDragAndDrop(const bool enabled); 55 | bool IsDragAndDropEnabled() const; 56 | private: 57 | 58 | std::weak_ptr get_and_note_window_options(); 59 | void apply_new_window_option_changes(); 60 | void default_init(); 61 | void set_default_callbacks(); 62 | void apply_window_sizings_from_current_options() noexcept; 63 | 64 | GLFWwindow* mGLFWwindow = nullptr; 65 | std::shared_ptr mWindowOptions; 66 | WindowOptions mPrevWindowOptions; // used for comparisons when applying new window options changes 67 | bool settings_applied_at_least_once = false; 68 | 69 | // any callback that casts the glfwwindow to user_ptr and then accesses private members needs to be a friend 70 | friend void FRAMEBUFFERSIZESETCALLBACK(GLFWwindow* window, int w, int h); 71 | friend void ONWINDOWFOCUSCALLBACK(GLFWwindow* window, int focused); 72 | friend void ONDRAGANDDROP(GLFWwindow* window, int count, const char** paths); 73 | 74 | friend class Interface; // to call swap_buffers when needed, todo: come up with a better solution, as we don't really want the interface to have full access to the window but it is okay for now. 75 | void swap_buffers(); 76 | std::queue dropped_paths; 77 | }; 78 | 79 | } // end namespace AA 80 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /Foundation/src/Mesh/Animation.cpp: -------------------------------------------------------------------------------- 1 | #include "Animation.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Animator.h" 7 | #include "AnimationData.h" 8 | 9 | namespace AA { 10 | 11 | Animation::Animation() = default; 12 | 13 | /// 14 | /// Loads the first animation in the file (todo: multiload). 15 | /// Applies Missing Bones to animated prop 16 | /// 17 | /// path to file 18 | /// shared pointer to animated prop 19 | Animation::Animation(const std::string& animationPath, Skeleton& in_out_skele) { 20 | Assimp::Importer importer; 21 | const aiScene* scene = importer.ReadFile(animationPath, aiProcess_Triangulate); 22 | assert(scene && scene->mRootNode); 23 | auto animation = scene->mAnimations[0]; 24 | m_Duration = static_cast(animation->mDuration); 25 | m_TicksPerSecond = static_cast(animation->mTicksPerSecond); 26 | ReadHeirarchyData(m_RootNode, scene->mRootNode); 27 | ReadMissingBones(animation, in_out_skele); 28 | } 29 | 30 | Animation::~Animation() {} 31 | 32 | float Animation::GetTicksPerSecond() { return m_TicksPerSecond; } 33 | 34 | float Animation::GetDuration() { return m_Duration; } 35 | 36 | const AnimationNodeTree& Animation::GetRootNode() { return m_RootNode; } 37 | 38 | void Animation::ReadMissingBones(const aiAnimation* animation, Skeleton& in_out_skele) { 39 | int num_animation_channels = animation->mNumChannels; 40 | 41 | //reading channels(bones engaged in an animation and their keyframes) 42 | for (int i = 0; i < num_animation_channels; i++) { 43 | auto channel = animation->mChannels[i]; 44 | BONE_MAP_KEY boneName = channel->mNodeName.data; 45 | 46 | if (in_out_skele.m_BoneInfoMap.find(boneName) == in_out_skele.m_BoneInfoMap.end()) { 47 | in_out_skele.m_BoneInfoMap[boneName].id = in_out_skele.m_BoneCounter; 48 | in_out_skele.m_BoneCounter++; 49 | } 50 | m_Skeleton.m_Bones.emplace_back(channel->mNodeName.data, in_out_skele.m_BoneInfoMap[boneName].id, channel); 51 | } 52 | 53 | m_Skeleton.m_BoneInfoMap.merge(in_out_skele.m_BoneInfoMap); // merge was added in c++17 54 | //m_Skeleton.m_BoneInfoMap = in_out_skele.m_BoneInfoMap; 55 | } 56 | 57 | void Animation::ReadHeirarchyData(AnimationNodeTree& dest, const aiNode* src) { 58 | assert(src); 59 | 60 | dest.name = src->mName.data; 61 | dest.transformation = ConvertMatrixToGLMFormat(src->mTransformation); 62 | dest.childrenCount = src->mNumChildren; 63 | 64 | for (unsigned int i = 0; i < src->mNumChildren; i++) { 65 | AnimationNodeTree newData; 66 | ReadHeirarchyData(newData, src->mChildren[i]); 67 | dest.children.push_back(newData); 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /Foundation/src/Math/Conversions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | // #include 10 | 11 | namespace AA { 12 | 13 | inline glm::mat4 aiMat4_to_glmMat4(const aiMatrix4x4& inMat) { 14 | glm::mat4 outMat{}; 15 | outMat[0][0] = inMat.a1; 16 | outMat[1][0] = inMat.b1; 17 | outMat[2][0] = inMat.c1; 18 | outMat[3][0] = inMat.d1; 19 | outMat[0][1] = inMat.a2; 20 | outMat[1][1] = inMat.b2; 21 | outMat[2][1] = inMat.c2; 22 | outMat[3][1] = inMat.d2; 23 | outMat[0][2] = inMat.a3; 24 | outMat[1][2] = inMat.b3; 25 | outMat[2][2] = inMat.c3; 26 | outMat[3][2] = inMat.d3; 27 | outMat[0][3] = inMat.a4; 28 | outMat[1][3] = inMat.b4; 29 | outMat[2][3] = inMat.c4; 30 | outMat[3][3] = inMat.d4; 31 | //glm::transpose(outMat); 32 | return outMat; 33 | } 34 | 35 | inline glm::vec3 aiVec3_to_glmVec3(const aiVector3D& inVec) noexcept { 36 | glm::vec3 outVec{}; 37 | outVec.x = inVec.x; 38 | outVec.y = inVec.y; 39 | outVec.z = inVec.z; 40 | return outVec; 41 | } 42 | 43 | inline glm::vec4 aiColor4_to_glmVec4(const aiColor4D& inVec) noexcept { 44 | glm::vec4 outVec{}; 45 | outVec.x = inVec.r; 46 | outVec.y = inVec.g; 47 | outVec.z = inVec.b; 48 | outVec.w = inVec.a; 49 | 50 | return outVec; 51 | } 52 | 53 | inline glm::quat aiQuat_to_glmQuat(const aiQuaternion& inQuat) noexcept { 54 | glm::quat outQuat{}; 55 | outQuat.w = inQuat.w; 56 | outQuat.x = inQuat.x; 57 | outQuat.y = inQuat.y; 58 | outQuat.z = inQuat.z; 59 | return outQuat; 60 | } 61 | 62 | // inline void physxTransform_to_glmMat4(const physx::PxTransform& in, glm::mat4& out) { 63 | // glm::vec3 pos = glm::vec3(in.p.x, in.p.y, in.p.z); 64 | // glm::quat ori = glm::quat(in.q.x, in.q.y, in.q.z, in.q.w); 65 | // 66 | // glm::mat4 rot = glm::toMat4(ori); 67 | // glm::mat4 trans = glm::translate(glm::mat4(1), pos); 68 | // 69 | // out = trans * rot; 70 | // 71 | // } 72 | 73 | static inline glm::mat4 ConvertMatrixToGLMFormat(const aiMatrix4x4& from) { 74 | glm::mat4 to; 75 | //the a,b,c,d in assimp is the row ; the 1,2,3,4 is the column 76 | to[0][0] = from.a1; to[1][0] = from.a2; to[2][0] = from.a3; to[3][0] = from.a4; 77 | to[0][1] = from.b1; to[1][1] = from.b2; to[2][1] = from.b3; to[3][1] = from.b4; 78 | to[0][2] = from.c1; to[1][2] = from.c2; to[2][2] = from.c3; to[3][2] = from.c4; 79 | to[0][3] = from.d1; to[1][3] = from.d2; to[2][3] = from.d3; to[3][3] = from.d4; 80 | return to; 81 | } 82 | 83 | static inline glm::vec3 GetGLMVec(const aiVector3D& vec) { 84 | return glm::vec3(vec.x, vec.y, vec.z); 85 | } 86 | 87 | static inline glm::quat GetGLMQuat(const aiQuaternion& pOrientation) { 88 | return glm::quat(pOrientation.w, pOrientation.x, pOrientation.y, pOrientation.z); 89 | } 90 | 91 | 92 | }// namespace AA 93 | -------------------------------------------------------------------------------- /Foundation/src/Sound/SoundEffect.cpp: -------------------------------------------------------------------------------- 1 | #include "SoundEffect.h" 2 | #include 3 | #include 4 | #include "ErrorCheck.h" 5 | #include 6 | #include 7 | #include 8 | 9 | namespace AA { 10 | 11 | SoundEffect::SoundEffect(const char* filename) { 12 | ALenum err, format; 13 | ALuint buffer; 14 | SNDFILE* sndfile; 15 | SF_INFO sfinfo; 16 | short* membuf; 17 | sf_count_t num_frames; 18 | ALsizei num_bytes; 19 | 20 | /* Open the audio file and check that it's usable. */ 21 | sndfile = sf_open(filename, SFM_READ, &sfinfo); 22 | if (!sndfile) { 23 | fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(sndfile)); 24 | throw("short soundfile could not be loaded"); 25 | } 26 | if (sfinfo.frames < 1 || sfinfo.frames >(sf_count_t)(INT_MAX / sizeof(short)) / sfinfo.channels) { 27 | fprintf(stderr, "Bad sample count in %s (%" PRId64 ")\n", filename, sfinfo.frames); 28 | sf_close(sndfile); 29 | throw("bad sample count"); 30 | } 31 | 32 | /* Get the sound format, and figure out the OpenAL format */ 33 | format = AL_NONE; 34 | if (sfinfo.channels == 1) 35 | format = AL_FORMAT_MONO16; 36 | else if (sfinfo.channels == 2) 37 | format = AL_FORMAT_STEREO16; 38 | else if (sfinfo.channels == 3) { 39 | if (sf_command(sndfile, SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT) 40 | format = AL_FORMAT_BFORMAT2D_16; 41 | } else if (sfinfo.channels == 4) { 42 | if (sf_command(sndfile, SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT) 43 | format = AL_FORMAT_BFORMAT3D_16; 44 | } 45 | if (!format) { 46 | fprintf(stderr, "Unsupported channel count: %d\n", sfinfo.channels); 47 | sf_close(sndfile); 48 | throw("unsupported channel count"); 49 | } 50 | 51 | /* Decode the whole audio file to a buffer. */ 52 | membuf = static_cast(malloc((size_t)(sfinfo.frames * sfinfo.channels) * sizeof(short))); 53 | 54 | num_frames = sf_readf_short(sndfile, membuf, sfinfo.frames); 55 | if (num_frames < 1) { 56 | free(membuf); 57 | sf_close(sndfile); 58 | fprintf(stderr, "Failed to read samples in %s (%" PRId64 ")\n", filename, num_frames); 59 | throw("failed to read samples"); 60 | } 61 | num_bytes = (ALsizei)(num_frames * sfinfo.channels) * (ALsizei)sizeof(short); 62 | 63 | /* Buffer the audio data into a new buffer object, then free the data and 64 | * close the file. 65 | */ 66 | buffer = 0; 67 | alGenBuffers(1, &buffer); 68 | alBufferData(buffer, format, membuf, num_bytes, sfinfo.samplerate); 69 | 70 | free(membuf); 71 | sf_close(sndfile); 72 | 73 | /* Check if an error occured, and clean up if so. */ 74 | err = alGetError(); 75 | if (err != AL_NO_ERROR) { 76 | fprintf(stderr, "OpenAL Error: %s\n", alGetString(err)); 77 | if (buffer && alIsBuffer(buffer)) 78 | alDeleteBuffers(1, &buffer); 79 | throw("openALerror"); 80 | } 81 | 82 | if (buffer == 0) 83 | throw("buffer is 0, unable to load"); 84 | 85 | _Buffer = buffer; 86 | _FilePath = filename; 87 | } 88 | 89 | SoundEffect::~SoundEffect() { 90 | alDeleteBuffers(1, &_Buffer); 91 | } 92 | 93 | } // end namespace AA 94 | -------------------------------------------------------------------------------- /Foundation/src/Sound/SoundListener.cpp: -------------------------------------------------------------------------------- 1 | #include "SoundListener.h" 2 | #include "SoundDevice.h" 3 | 4 | namespace AA { 5 | SoundListener* SoundListener::Get() { 6 | // make sure sound device is initialized 7 | SoundDevice::Init(); 8 | 9 | SoundListener* snd_listener = new SoundListener(); 10 | return snd_listener; 11 | } 12 | 13 | void SoundListener::SetMasterGain(const float& gain) { 14 | // consider gain clamps 15 | float newVol = gain; 16 | if (newVol < 0.f) { 17 | newVol = 0.f; 18 | } else if (newVol > 5.f) { 19 | // now thats flippin loud, lets cap it 20 | newVol = 5.f; 21 | } 22 | 23 | alListenerf(AL_GAIN, newVol); 24 | 25 | if (alGetError() != AL_NO_ERROR) { 26 | throw("error setting gain"); 27 | } 28 | } 29 | 30 | void SoundListener::SetPosition(const glm::vec3& pos) { 31 | SetLocation(pos); 32 | } 33 | 34 | void SoundListener::SetLocation(const glm::vec3& pos) { 35 | alListener3f(AL_POSITION, pos.x, pos.y, pos.z); 36 | if (alGetError() != AL_NO_ERROR) { 37 | throw("error setting listener location"); 38 | } 39 | } 40 | 41 | void SoundListener::SetPosition(const float& x, const float& y, const float& z) { 42 | SetLocation(x, y, z); 43 | } 44 | 45 | void SoundListener::SetLocation(const float& x, const float& y, const float& z) { 46 | alListener3f(AL_POSITION, x, y, z); 47 | if (alGetError() != AL_NO_ERROR) { 48 | throw("error setting listener location"); 49 | } 50 | } 51 | 52 | void SoundListener::SetOrientation(const glm::vec3& at, const glm::vec3& up) { 53 | std::vector ori; 54 | ori.push_back(at.x); 55 | ori.push_back(at.y); 56 | ori.push_back(at.z); 57 | ori.push_back(up.x); 58 | ori.push_back(up.y); 59 | ori.push_back(up.z); 60 | alListenerfv(AL_ORIENTATION, ori.data()); 61 | if (alGetError() != AL_NO_ERROR) { 62 | throw("error setting gain"); 63 | } 64 | } 65 | 66 | void SoundListener::SetOrientation(const float& atx, const float& aty, const float& atz, const float& upx, const float& upy, const float& upz) { 67 | float ori[6] = { atx, aty, atz, upx, upy, upz }; 68 | alListenerfv(AL_ORIENTATION, &ori[0]); 69 | if (alGetError() != AL_NO_ERROR) { 70 | throw("error setting gain"); 71 | } 72 | } 73 | 74 | /// 75 | /// AL_INVERSE_DISTANCE, AL_INVERSE_DISTANCE_CLAMPED, AL_LINEAR_DISTANCE, 76 | /// AL_LINEAR_DISTANCE_CLAMPED, AL_EXPONENT_DISTANCE, 77 | /// AL_EXPONENT_DISTANCE_CLAMPED, or AL_NONE. 78 | /// 79 | /// option 80 | void SoundListener::SetDistanceModel(ALint textureType) { 81 | switch (textureType) { 82 | case AL_INVERSE_DISTANCE: 83 | case AL_INVERSE_DISTANCE_CLAMPED: 84 | case AL_LINEAR_DISTANCE: 85 | case AL_LINEAR_DISTANCE_CLAMPED: 86 | case AL_EXPONENT_DISTANCE: 87 | case AL_EXPONENT_DISTANCE_CLAMPED: 88 | case AL_NONE: 89 | break; 90 | default: 91 | throw("invalid distance model"); 92 | } 93 | alDistanceModel(textureType); 94 | if (alGetError() != AL_NO_ERROR) { 95 | throw("error setting listener distance model"); 96 | } 97 | } 98 | 99 | SoundListener::SoundListener() {} 100 | 101 | 102 | 103 | } // end namespace AA 104 | -------------------------------------------------------------------------------- /Foundation/testsuite/DownloadTestResources/DownloadTestResources.cpp: -------------------------------------------------------------------------------- 1 | /* Written to download the files necessary to run all the tests. 2 | Run this before running all the tests to have it download your assets. 3 | You then need to unzip the assets into the 'RuntimeFiles' directory. 4 | - MJE 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace std; 12 | 13 | size_t write_data(void* ptr, size_t size, size_t nmemb, FILE* stream) { 14 | return fwrite(ptr, size, nmemb, stream); 15 | } 16 | 17 | int main() { 18 | CURL* curl; 19 | curl = curl_easy_init(); 20 | 21 | const char* destination = "../RuntimeFiles.zip"; 22 | FILE* filepath; 23 | // check if file exists already 24 | auto test_if_exists_err_code = fopen_s(&filepath, destination, "r"); 25 | if (test_if_exists_err_code == 0) { 26 | std::cout << "file already exits, unzip it to RuntimeFiles dir\n"; 27 | return EXIT_SUCCESS; 28 | } 29 | else { 30 | std::cout << "creating output file...\n"; 31 | auto fopen_err_code = fopen_s(&filepath, destination, "wb"); 32 | if (fopen_err_code != 0) { 33 | std::cout << "error creating output file\n"; 34 | return EXIT_FAILURE; 35 | } 36 | } 37 | 38 | 39 | 40 | 41 | /* A long parameter set to 1 tells the library to follow any Location: header 42 | * that the server sends as part of an HTTP header in a 3xx response. The 43 | *Location: header can specify a relative or an absolute URL to follow. 44 | */ 45 | curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); 46 | curl_easy_setopt(curl, CURLOPT_URL, "https://www.dropbox.com/s/sfi0ouype3d7xdh/RuntimeFiles-1-5.zip?dl=1"); // "dl=0"changed to "dl=1" to force download 47 | 48 | // disabe the SSL peer certificate verification allowing the program to download the file from dropbox shared link 49 | // in case it is not used it displays an error message stating "SSL peer certificate or SSH remote key was not OK" 50 | curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE); 51 | 52 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); 53 | 54 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, filepath); 55 | 56 | CURLcode res; 57 | std::cout << "performing download...\n"; 58 | res = curl_easy_perform(curl); 59 | std::cout << "cleaning up curl...\n"; 60 | curl_easy_cleanup(curl); 61 | fclose(filepath); 62 | 63 | if (res == CURLE_OK) { 64 | // good 65 | } else { 66 | cout << curl_easy_strerror(res); 67 | return EXIT_FAILURE; 68 | } 69 | 70 | // make the required directory 71 | auto mkdir_return_code = _mkdir("..\\RuntimeFiles"); 72 | if (mkdir_return_code != 0) { 73 | switch (errno) { 74 | case EEXIST: 75 | cout << "directory already exists, unzip to RuntimeFiles/ folder\n"; 76 | //system("unzip ../RuntimeFiles.zip -d ../RuntimeFiles/"); 77 | break; 78 | case ENOENT: 79 | cout << "mkdir path was not found\n"; 80 | break; 81 | default: 82 | cout << "unspecified mkdir error\n"; 83 | break; 84 | } 85 | } else { 86 | cout << "directory created, unzip to RuntimeFiles/ folder\n"; 87 | //system("unzip ../RuntimeFiles.zip -d ../RuntimeFiles/"); 88 | } 89 | 90 | return EXIT_SUCCESS; 91 | } -------------------------------------------------------------------------------- /Foundation/testsuite/test_globals.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | static class Globals { 7 | public: 8 | const std::string runtime_dir = "../../RuntimeFiles/"; 9 | 10 | // test globals 11 | AA::Interface g_aa_interface; 12 | unsigned int g_imgui_func = 0; 13 | unsigned int g_begin_func = 0; 14 | unsigned int g_update_func = 0; 15 | unsigned int g_cam_id = 0; 16 | std::weak_ptr g_camera_ref; 17 | std::weak_ptr g_window_ref; 18 | 19 | // resources 20 | // skymap files storage 21 | std::vector NightSkyTextures; 22 | std::vector DaySkyTextures; 23 | std::vector CaveTextures; 24 | 25 | // model paths 26 | // test glb 27 | const std::string cube_runtime_dir = runtime_dir + "3dmodels/cube.glb"; 28 | // test obj 29 | const std::string ground_plane_runtime_dir_path = runtime_dir + "3dmodels/large_green_plane.obj"; 30 | const std::string fireplace_room_runtime_dir_path = runtime_dir + "3dmodels/fireplace_room.obj"; 31 | // test fbx 32 | const std::string peasant_man_runtime_dir_path = runtime_dir + "3dmodels/peasant_man.fbx"; 33 | const std::string peasant_girl_runtime_dir_path = runtime_dir + "3dmodels/peasant_girl.fbx"; 34 | const std::string zombie_runtime_dir_path = runtime_dir + "3dmodels/zombie_punching.fbx"; // contains a model and animation data 35 | const std::string vanguard_runtime_dir_path = runtime_dir + "3dmodels/vanguard_neutral_idle.fbx"; // contains a model and animation data 36 | // test fbx animation 37 | const std::string idle_anim_runtime_dir_path = runtime_dir + "3dmodels/animations/breathing_idle.fbx"; // contains animation data 38 | 39 | 40 | unsigned int se_coins1 = 0; 41 | unsigned int se_flashlight = 0; 42 | unsigned int se_gunshot = 0; 43 | unsigned int se_mgsalert = 0; 44 | const std::string path_se_coins1 = runtime_dir + "soundeffects/coins.ogg"; 45 | const std::string path_se_flashlightclick = runtime_dir + "soundeffects/flashlightclick.wav"; 46 | const std::string path_se_gunshot = runtime_dir + "soundeffects/GunShotSingle.wav"; 47 | const std::string path_se_mgsalert = runtime_dir + "soundeffects/Metal Gear Solid Alert.ogg"; 48 | 49 | unsigned int g_untextured_cube_id[3] = { 0,0,0 }; 50 | unsigned int g_ground_plane_id = 0; 51 | unsigned int g_peasant_man_id = 0; 52 | unsigned int g_peasant_girl_id = 0; 53 | unsigned int g_vanguard_id = 0; 54 | 55 | unsigned int g_zombie_id[2] = { 0, 0 }; 56 | unsigned int g_punching_anim_id = 0; 57 | 58 | unsigned int g_idle_anim_id = 0; 59 | 60 | bool g_Yes = false, g_No = false; 61 | 62 | unsigned int g_plight1_id = 0; 63 | float point_light_loc[3] = { -194.0f, 125.0f, -32.0f }; 64 | float* point_light_constant = new float(1.0f); 65 | float* point_light_linear = new float(0.027f); 66 | float* point_light_quadratic = new float(0.0028f); 67 | float* point_light_ambient = new float(0.3f); 68 | float* point_light_diff = new float(0.5f); 69 | float* point_light_spec = new float(0.3f); 70 | bool debug_point_light = true; 71 | 72 | unsigned int g_slight1_id = 0; 73 | float spot_light_loc[3] = { 0.0f, 0.0f, 0.0f }; 74 | float spot_light_dir[3] = { 1.f, 1.f, 1.f }; 75 | float* spot_light_inner = new float(0.05f); 76 | float* spot_light_outer = new float(1.50f); 77 | float* spot_light_constant = new float(3.0f); 78 | float* spot_light_linear = new float(0.0027f); 79 | float* spot_light_quadratic = new float(0.00028f); 80 | float* spot_light_ambient = new float(0.0f); 81 | float* spot_light_diff = new float(1.0f); 82 | float* spot_light_spec = new float(1.0f); 83 | 84 | float* cam_fov = new float(75.f); 85 | 86 | bool gamma_correction = false; 87 | } *tg; 88 | 89 | namespace TestGlobals { 90 | 91 | static void init() { 92 | if (!tg) { 93 | tg = new Globals(); 94 | } 95 | } 96 | 97 | static void reset() { 98 | if (tg) { 99 | delete tg; 100 | tg = nullptr; 101 | } 102 | } 103 | 104 | } -------------------------------------------------------------------------------- /Foundation/include/ArcherEngine/Scene/Scene.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../../../src/Base/UniqueInstance.h" 3 | #include "../../../src/Mesh/Spacial3D.h" 4 | #include "../../../src/Mesh/MeshInfo.h" 5 | #include "../../../src/Mesh/Skeleton.h" 6 | #include "../../../src/OS/OpenGL/Loaders/AssimpSceneLoader.h" 7 | #include 8 | #include 9 | #include 10 | 11 | namespace AA { 12 | 13 | class Spacial3D; 14 | 15 | class UniqueInstance; 16 | 17 | class Animation; 18 | 19 | struct SceneData; 20 | 21 | struct AnimationData; 22 | 23 | /// 24 | /// AKA Static Mesh 25 | /// 26 | class Scene : public UniqueInstance { 27 | public: 28 | Scene(); 29 | Scene(const char* path, const bool& get_bones); 30 | Scene(const char* path); 31 | ~Scene(); 32 | 33 | const glm::mat4 GetFMM() const; 34 | 35 | const bool IsStenciled() const; 36 | 37 | const bool IsStenciledWithNormals() const; 38 | 39 | const bool IsAnimated() const; 40 | 41 | const bool HasAnimation() const; 42 | 43 | const float GetStencilScale() const; 44 | 45 | const glm::vec3 GetStencilColor() const; 46 | 47 | const bool GetRenderShadows() const; 48 | 49 | const bool GetCullFrontFaceForShadows() const; 50 | 51 | const bool GetBackFaceCull() const; 52 | 53 | const glm::vec3& GetLocation() const; 54 | 55 | /// 56 | /// Moves a prop to a location 57 | /// 58 | /// desired location 59 | void SetLocation(const glm::vec3& loc); 60 | 61 | /// 62 | /// Scales a prop 63 | /// 64 | /// desired scale 65 | void SetScale(const glm::vec3& scale); 66 | 67 | /// 68 | /// Rotates a prop 69 | /// 70 | /// rotation axis values, x, y, z should be radians -PI to PI 71 | void SetRotation(const glm::vec3& rot); 72 | 73 | /// 74 | /// Sets whether the prop should have a stencil around it. 75 | /// 76 | /// true or false for on or off 77 | void SetStencil(const bool& tf); 78 | 79 | /// 80 | /// Sets the color of the stencil. 81 | /// 82 | /// stencil color in rgb 83 | void SetStencilColor(const glm::vec3& color); 84 | 85 | /// 86 | /// Calculate stencil with Normals or Not. Normals work better for objects that do not have a centered origin. 87 | /// 88 | /// true to calculate stencil with normal, false to calculate normally. If the stencil is offcentered, try true. 89 | void SetStencilWithNormals(const bool& tf); 90 | 91 | /// 92 | /// Scale of the stencil. These scale differently depending on whether calculated with Normals or not. 93 | /// 94 | /// scale should be > 1 or you probably won't see the stencil 95 | void SetStencilScale(const float& scale); 96 | 97 | void SetRenderShadows(const bool tf); 98 | 99 | void SetFrontFaceCullForShadows(const bool tf); 100 | 101 | void SetBackfaceCull(const bool tf); 102 | 103 | void UpdateAnim(float dt); 104 | 105 | const std::vector GetVAOList() noexcept; 106 | 107 | private: 108 | 109 | // mesh loader helps set all these 110 | friend class AssimpSceneLoader; 111 | friend class Interface; 112 | friend class OpenGL; 113 | 114 | void Update(); 115 | const std::vector& GetMeshes(); 116 | 117 | void ClearAnimator(); 118 | void SetAnimator(std::shared_ptr& anim); 119 | void SetGlobalInverseTransform(const glm::mat4& inv_trans); 120 | void SetMeshes(const std::vector& meshes); 121 | void SetPathID(const std::string& path); 122 | void SetSkeleton(const Skeleton& skel); 123 | Skeleton* GetSkeleton(); 124 | std::vector GetFinalBoneMatrices() const; 125 | 126 | void Load(const std::string& path, const bool& getanimdata); 127 | 128 | std::unique_ptr scenedata_; 129 | std::unique_ptr animdata_; 130 | }; 131 | } // end namespace AA 132 | -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/Graphics.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "../../Mesh/Vertex.h" 5 | #include "../../Scene/Lights.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "../../Scene/Skybox.h" 12 | 13 | namespace AA { 14 | 15 | class Scene; 16 | //class AnimProp; 17 | //class Skybox; 18 | 19 | class OpenGL { 20 | public: 21 | static OpenGL* GetGL(); 22 | 23 | public: 24 | GLuint UploadStatic3DMesh(const std::vector& verts, const std::vector& elems); 25 | GLuint UploadStatic3DMesh(const std::vector& verts, const std::vector& elems); 26 | GLuint UploadStatic3DMesh(const std::vector& verts, const std::vector& elems); 27 | GLuint Upload3DPositionsMesh(const float* points, const int num_points, const GLuint* indices, const int ind_count); 28 | GLuint Upload2DVerts(const std::vector& points); 29 | void DeleteMesh(const GLsizei num_to_del, const GLuint& VAO); 30 | 31 | GLuint Upload2DTex(const unsigned char* tex_data, int width, int height, int format, aiTextureMapMode map_mode); 32 | GLuint UploadCubeMapTex(std::vector tex_data, int width, int height, int format); 33 | void DeleteTex(const GLsizei num_to_del, const GLuint& tex_d); 34 | 35 | GLuint CreateDepthMapFBO(GLuint shadow_width, GLuint shadow_height, GLuint& out_depth_map_tex_id); 36 | void DeleteFramebuffer(const GLsizei num_to_del, GLuint& fbo); 37 | 38 | void SetSamplerCube(int which, const unsigned int& cubetexID); 39 | void SetTexture(int which, const unsigned int& textureID); 40 | void DrawElements(unsigned int vao, unsigned int numElements); 41 | void DrawStrip(unsigned int vao, const int& count); 42 | void SetViewportSize(GLint x, GLint y, GLsizei w, GLsizei h); 43 | void SetViewportClearColor(glm::vec3 color) noexcept; 44 | void SetLogStream(const bool& on_or_off, const bool& file_or_stdout); 45 | 46 | void ClearScreen() noexcept; 47 | 48 | void NewFrame() noexcept; 49 | 50 | void SetBlend(const bool enabled); 51 | void SetCullFace(const bool enabled); 52 | void SetCullMode(int mode); 53 | void SetDepthTest(const bool enabled); 54 | void SetDepthMode(int mode); 55 | void SetMultiSampling(const bool enabled); 56 | void SetGammaCorrection(const bool enabled); 57 | 58 | void SetDepthMask(const bool enabled); 59 | void SetStencil(const bool enabled); 60 | void SetStencilMask(const bool enabled); 61 | void SetStencilOpDepthPassToReplace(); 62 | void SetStencilFuncToAlways(); 63 | void SetStencilFuncToNotEqual(); 64 | 65 | unsigned int MakeCube(); 66 | 67 | unsigned int MakeCone(); 68 | 69 | void DeletePrimatives(); 70 | 71 | // void Proc( void(* (*)(const char*))() ); 72 | void Proc(void* proc); 73 | 74 | // 75 | // render graph stuff 76 | // 77 | void ResetToDefault(); 78 | 79 | /// 80 | /// render depth of scene to texture from light perspective 81 | /// 82 | /// 83 | /// 84 | /// 85 | /// 86 | /// 87 | void BatchRenderShadows( 88 | const glm::vec3& view_pos, 89 | const SunLight& dir_light, 90 | const std::vector >& render_objects); 91 | 92 | void BatchRenderToViewport( 93 | const std::vector >& render_objects, 94 | const Viewport& vp); 95 | 96 | // no changes to shaders before rendering the meshes of the object 97 | void RenderAsIs(const std::shared_ptr& render_object); 98 | 99 | void RenderProp(const std::shared_ptr& render_object); 100 | 101 | void RenderAnimProp(const std::shared_ptr& render_object); 102 | 103 | void RenderSkybox(const Skybox* skybox_target, const Viewport& vp); 104 | 105 | void RenderStenciled(const std::shared_ptr& render_object); 106 | 107 | void RenderAnimStenciled(const std::shared_ptr& render_object); 108 | 109 | /// 110 | /// Debug 111 | /// 112 | void RenderDebugCube(glm::vec3 loc); 113 | 114 | /// 115 | /// Debug 116 | /// todo:: test and fix 117 | /// 118 | void RenderSunLightArrowIcon(glm::vec3 dir_from_00); 119 | 120 | /// 121 | /// Debug RenderSpotLightIcon 122 | /// todo: test and fix 123 | /// 124 | void RenderSpotLightIcon(glm::vec3 location, glm::vec3 direction); 125 | 126 | private: 127 | OpenGL(); 128 | }; 129 | } // end namespace AA 130 | -------------------------------------------------------------------------------- /Foundation/src/Mesh/Bone.cpp: -------------------------------------------------------------------------------- 1 | #include "Bone.h" 2 | namespace AA { 3 | 4 | Bone::Bone(const std::string& name, int ID, const aiNodeAnim* channel) 5 | : 6 | m_Name(name), 7 | m_ID(ID), 8 | m_LocalTransform(1.0f) { 9 | m_NumPositions = channel->mNumPositionKeys; 10 | 11 | for (int positionIndex = 0; positionIndex < m_NumPositions; ++positionIndex) { 12 | aiVector3D aiPosition = channel->mPositionKeys[positionIndex].mValue; 13 | float timeStamp = static_cast(channel->mPositionKeys[positionIndex].mTime); 14 | KeyPosition data; 15 | data.position = GetGLMVec(aiPosition); 16 | data.timeStamp = timeStamp; 17 | m_Positions.push_back(data); 18 | } 19 | 20 | m_NumRotations = channel->mNumRotationKeys; 21 | for (int rotationIndex = 0; rotationIndex < m_NumRotations; ++rotationIndex) { 22 | aiQuaternion aiOrientation = channel->mRotationKeys[rotationIndex].mValue; 23 | float timeStamp = static_cast(channel->mRotationKeys[rotationIndex].mTime); 24 | KeyRotation data; 25 | data.orientation = GetGLMQuat(aiOrientation); 26 | data.timeStamp = timeStamp; 27 | m_Rotations.push_back(data); 28 | } 29 | 30 | m_NumScalings = channel->mNumScalingKeys; 31 | for (int keyIndex = 0; keyIndex < m_NumScalings; ++keyIndex) { 32 | aiVector3D scale = channel->mScalingKeys[keyIndex].mValue; 33 | float timeStamp = static_cast(channel->mScalingKeys[keyIndex].mTime); 34 | KeyScale data; 35 | data.scale = GetGLMVec(scale); 36 | data.timeStamp = timeStamp; 37 | m_Scales.push_back(data); 38 | } 39 | } 40 | 41 | void Bone::Update(float animationTime) { 42 | glm::mat4 translation = InterpolatePosition(animationTime); 43 | glm::mat4 rotation = InterpolateRotation(animationTime); 44 | glm::mat4 scale = InterpolateScaling(animationTime); 45 | m_LocalTransform = translation * rotation * scale; 46 | } 47 | 48 | glm::mat4 Bone::GetLocalTransform() { return m_LocalTransform; } 49 | 50 | std::string Bone::GetBoneName() const { return m_Name; } 51 | 52 | int Bone::GetBoneID() { return m_ID; } 53 | 54 | int Bone::GetPositionIndex(float animationTime) { 55 | for (int i = 0; i < m_NumPositions - 1; ++i) { 56 | if (animationTime < m_Positions[i + 1].timeStamp) 57 | return i; 58 | } 59 | assert(0); 60 | return -1; 61 | } 62 | 63 | int Bone::GetRotationIndex(float animationTime) { 64 | for (int i = 0; i < m_NumRotations - 1; ++i) { 65 | if (animationTime < m_Rotations[i + 1].timeStamp) 66 | return i; 67 | } 68 | assert(0); 69 | return -1; 70 | } 71 | 72 | int Bone::GetScaleIndex(float animationTime) { 73 | for (int i = 0; i < m_NumScalings - 1; ++i) { 74 | if (animationTime < m_Scales[i + 1].timeStamp) 75 | return i; 76 | } 77 | assert(0); 78 | return -1; 79 | } 80 | 81 | float Bone::GetScaleFactor(float lastTimeStamp, float nextTimeStamp, float animationTime) { 82 | float scaleFactor = 0.0f; 83 | float midWayLength = animationTime - lastTimeStamp; 84 | float framesDiff = nextTimeStamp - lastTimeStamp; 85 | scaleFactor = midWayLength / framesDiff; 86 | return scaleFactor; 87 | } 88 | 89 | glm::mat4 Bone::InterpolatePosition(float animationTime) { 90 | if (1 == m_NumPositions) 91 | return glm::translate(glm::mat4(1.0f), m_Positions[0].position); 92 | 93 | int p0Index = GetPositionIndex(animationTime); 94 | int p1Index = p0Index + 1; 95 | float scaleFactor = GetScaleFactor(m_Positions[p0Index].timeStamp, 96 | m_Positions[p1Index].timeStamp, animationTime); 97 | glm::vec3 finalPosition = glm::mix(m_Positions[p0Index].position, 98 | m_Positions[p1Index].position, scaleFactor); 99 | return glm::translate(glm::mat4(1.0f), finalPosition); 100 | } 101 | 102 | glm::mat4 Bone::InterpolateRotation(float animationTime) { 103 | if (1 == m_NumRotations) { 104 | auto rotation = glm::normalize(m_Rotations[0].orientation); 105 | return glm::toMat4(rotation); 106 | } 107 | 108 | int p0Index = GetRotationIndex(animationTime); 109 | int p1Index = p0Index + 1; 110 | float scaleFactor = GetScaleFactor(m_Rotations[p0Index].timeStamp, 111 | m_Rotations[p1Index].timeStamp, animationTime); 112 | glm::quat finalRotation = glm::slerp(m_Rotations[p0Index].orientation, 113 | m_Rotations[p1Index].orientation, scaleFactor); 114 | finalRotation = glm::normalize(finalRotation); 115 | return glm::toMat4(finalRotation); 116 | } 117 | glm::mat4 Bone::InterpolateScaling(float animationTime) { 118 | if (1 == m_NumScalings) 119 | return glm::scale(glm::mat4(1.0f), m_Scales[0].scale); 120 | 121 | int p0Index = GetScaleIndex(animationTime); 122 | int p1Index = p0Index + 1; 123 | float scaleFactor = GetScaleFactor(m_Scales[p0Index].timeStamp, 124 | m_Scales[p1Index].timeStamp, animationTime); 125 | glm::vec3 finalScale = glm::mix(m_Scales[p0Index].scale, m_Scales[p1Index].scale 126 | , scaleFactor); 127 | return glm::scale(glm::mat4(1.0f), finalScale); 128 | } 129 | 130 | } -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/OGLShader.cpp: -------------------------------------------------------------------------------- 1 | #include "OGLShader.h" 2 | #include 3 | #include 4 | #include 5 | namespace AA { 6 | 7 | 8 | 9 | const char* OGLShader::get_glsl_version() { 10 | return (char*)glGetString(GL_NUM_SHADING_LANGUAGE_VERSIONS); 11 | } 12 | OGLShader::OGLShader(const char* vert_src, const char* frag_src) { 13 | loadShader(vert_src, frag_src); 14 | } 15 | OGLShader::~OGLShader() { 16 | glDeleteProgram(ID); 17 | } 18 | 19 | void OGLShader::Use() const noexcept { 20 | glUseProgram(ID); 21 | } 22 | 23 | void OGLShader::stop() const noexcept { 24 | glUseProgram(0); 25 | } 26 | 27 | void OGLShader::SetBool(const std::string& name, bool value) const { 28 | glUniform1i(getAndCheckShaderUniform(name), (int)value); 29 | } 30 | 31 | void OGLShader::SetInt(const std::string& name, int value) const { 32 | glUniform1i(getAndCheckShaderUniform(name), value); 33 | } 34 | 35 | void OGLShader::SetUint(const std::string& name, unsigned int value) const { 36 | glUniform1ui(getAndCheckShaderUniform(name), value); 37 | } 38 | 39 | void OGLShader::SetFloat(const std::string& name, float value) const { 40 | glUniform1f(getAndCheckShaderUniform(name), value); 41 | } 42 | 43 | void OGLShader::SetVec2(const std::string& name, const glm::vec2& value) const { 44 | glUniform2fv(getAndCheckShaderUniform(name), 1, &value[0]); 45 | } 46 | 47 | void OGLShader::SetVec2(const std::string& name, float x, float y) const { 48 | glUniform2f(getAndCheckShaderUniform(name), x, y); 49 | } 50 | 51 | void OGLShader::SetVec3(const std::string& name, const glm::vec3& value) const { 52 | glUniform3fv(getAndCheckShaderUniform(name), 1, &value[0]); 53 | } 54 | 55 | void OGLShader::SetVec3(const std::string& name, float x, float y, float z) const { 56 | glUniform3f(getAndCheckShaderUniform(name), x, y, z); 57 | } 58 | 59 | void OGLShader::SetVec4(const std::string& name, const glm::vec4& value) const { 60 | glUniform4fv(getAndCheckShaderUniform(name), 1, &value[0]); 61 | } 62 | 63 | void OGLShader::SetVec4(const std::string& name, float x, float y, float z, float w) const { 64 | glUniform4f(getAndCheckShaderUniform(name), x, y, z, w); 65 | } 66 | 67 | void OGLShader::SetMat2(const std::string& name, const glm::mat2& mat) const { 68 | glUniformMatrix2fv(getAndCheckShaderUniform(name), 1, GL_FALSE, &mat[0][0]); 69 | } 70 | 71 | void OGLShader::SetMat3(const std::string& name, const glm::mat3& mat) const { 72 | glUniformMatrix3fv(getAndCheckShaderUniform(name), 1, GL_FALSE, &mat[0][0]); 73 | } 74 | 75 | void OGLShader::SetMat4(const std::string& name, const glm::mat4& mat) const { 76 | glUniformMatrix4fv(getAndCheckShaderUniform(name), 1, GL_FALSE, &mat[0][0]); 77 | } 78 | 79 | const int OGLShader::GetID() const { 80 | return ID; 81 | } 82 | 83 | int OGLShader::getAndCheckShaderUniform(const std::string& name) const { 84 | const int shader_var_id = glGetUniformLocation(ID, name.c_str()); 85 | 86 | #ifdef _DEBUG 87 | if (shader_var_id < 0) { 88 | throw(std::_Xruntime_error, "shader_var_id is invalid (bad name?)"); 89 | } 90 | #endif 91 | 92 | return shader_var_id; 93 | } 94 | 95 | void OGLShader::loadShader(const char* vert_source, const char* frag_source) { 96 | /* compile vertex (points) shader */ 97 | int vertexShader = glCreateShader(GL_VERTEX_SHADER); 98 | glShaderSource(vertexShader, 1, &vert_source, nullptr); 99 | glCompileShader(vertexShader); 100 | 101 | /* determine if vertex shader was successful in compiling */ 102 | int v_success; 103 | glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &v_success); 104 | #ifdef _DEBUG 105 | if (!v_success) { 106 | char v_infoLog[512]; 107 | glGetShaderInfoLog(vertexShader, 512, nullptr, v_infoLog); 108 | throw(std::_Xruntime_error, v_infoLog); 109 | } 110 | #endif 111 | 112 | 113 | /* compile fragment shader */ 114 | int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); 115 | glShaderSource(fragmentShader, 1, &frag_source, nullptr); 116 | glCompileShader(fragmentShader); 117 | 118 | /* determine if fragment shader was successful in compiling */ 119 | int f_success; 120 | glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &f_success); 121 | #ifdef _DEBUG 122 | if (!f_success) { 123 | char f_infoLog[512]; 124 | glGetShaderInfoLog(fragmentShader, 512, nullptr, f_infoLog); 125 | throw(std::_Xruntime_error, f_infoLog); 126 | } 127 | #endif 128 | 129 | 130 | /* make the shader program */ 131 | ID = glCreateProgram(); 132 | glAttachShader(ID, vertexShader); 133 | glAttachShader(ID, fragmentShader); 134 | glLinkProgram(ID); 135 | 136 | /* check that the ID was successful */ 137 | int p_success; 138 | glGetProgramiv(ID, GL_LINK_STATUS, &p_success); 139 | #ifdef _DEBUG 140 | if (!p_success) { 141 | char p_infoLog[512]; 142 | glGetProgramInfoLog(ID, 512, nullptr, p_infoLog); 143 | throw(std::_Xruntime_error, p_infoLog); 144 | } 145 | #endif 146 | 147 | /* we don't need them anymore */ 148 | glDeleteShader(vertexShader); 149 | glDeleteShader(fragmentShader); 150 | 151 | 152 | // old debug, todo if needed 153 | //QueryInputAttribs(ID); 154 | //QueryUniforms(ID); 155 | } 156 | 157 | } // end namespace AA 158 | -------------------------------------------------------------------------------- /Foundation/samples/common/scripts/file_menu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #ifdef __linux__ 5 | #include 6 | #elif _WIN32 7 | #include 8 | #endif 9 | #include 10 | #include 11 | #include 12 | 13 | // globals local to this file 14 | static AA::Interface* aa_engine_ref = nullptr; 15 | static int file_menu_imgui_id = -1; 16 | static bool show_controls = false; 17 | static bool show_model_panel = false; 18 | static unsigned int cam_id_ref = 0; 19 | // setup function 20 | void setup_file_menu(const unsigned int& cam_id, AA::Interface& interface) { 21 | if (aa_engine_ref) return; // already setup, don't do it twice 22 | aa_engine_ref = &interface; 23 | cam_id_ref = cam_id; 24 | file_menu_imgui_id = aa_engine_ref->AddToImGuiUpdate([]() { 25 | // THE TOP MAIN MENU BAR 26 | if (ImGui::BeginMainMenuBar()) 27 | { 28 | // MAIN MENU BAR OPTION FILE 29 | if (ImGui::BeginMenu("File")) { 30 | if (ImGui::MenuItem("Import Model File...", "", nullptr)) { 31 | std::string file{}; 32 | bool ok = AA::NavigateFileSystem(file, "fbx,glb,obj,dae", ""); 33 | if (ok) 34 | aa_engine_ref->AddProp(file.c_str(), false); 35 | } 36 | if (ImGui::MenuItem("Import Skybox Files (order:udlfrb)", "", nullptr)) { 37 | // todo: this does a no-existext job oof telling the user which to load. 38 | // they should be picked in order of up down left front right back 39 | std::string up{}, down{}, left{}, front{}, right{}, back{}; 40 | const char* types = "png,tga,jpg,jpeg,gif,tiff,bmp"; 41 | bool has_up = AA::NavigateFileSystem(up, types, ""); 42 | bool has_down = AA::NavigateFileSystem(down, types, ""); 43 | bool has_left = AA::NavigateFileSystem(left, types, ""); 44 | bool has_front = AA::NavigateFileSystem(front, types, ""); 45 | bool has_right = AA::NavigateFileSystem(right, types, ""); 46 | bool has_back = AA::NavigateFileSystem(back, types, ""); 47 | if (has_up && 48 | has_down && 49 | has_left && 50 | has_front && 51 | has_right && 52 | has_back) { 53 | //however internally are loaded in order of right, left, up, down, front, back 54 | std::vector skybox_files{ right, left, up, down, front, back }; 55 | aa_engine_ref->GetCamera(cam_id_ref).lock()->SetSkybox(skybox_files); 56 | } 57 | } 58 | ImGui::Separator(); 59 | if (ImGui::MenuItem("Exit", "alt+f4", nullptr)) { aa_engine_ref->Shutdown(); } 60 | ImGui::EndMenu(); 61 | } 62 | // MAIN MENU BAR OPTION PANEL 63 | 64 | if (ImGui::BeginMenu("Panels")) { 65 | ImGui::MenuItem("Show Model Details Pane", "", &show_model_panel); 66 | ImGui::EndMenu(); 67 | } 68 | 69 | // MAIN MENU BAR OPTION HELP 70 | if (ImGui::BeginMenu("Help")) { 71 | ImGui::MenuItem("Show Controls", "", &show_controls); 72 | ImGui::EndMenu(); 73 | } 74 | // END MAIN MENU BAR 75 | ImGui::EndMainMenuBar(); 76 | } 77 | 78 | // CONDITIONALS 79 | if (show_controls) { 80 | ImGui::Begin("Controls"); 81 | ImGui::Text("TOGGLE MOUSE: right click"); 82 | ImGui::Text("MOVE: wasd"); 83 | ImGui::Text("LOOK: mouse (when toggled)"); 84 | ImGui::Text("FLY UP: spacebar"); 85 | ImGui::Text("FLY DOWN: c"); 86 | ImGui::End(); 87 | } 88 | if (show_model_panel) { 89 | ImGui::Begin("Models"); 90 | // test to see if we can get a list of models showing 91 | std::vector prop_ids = aa_engine_ref->GetAllPropIds(); 92 | for (const auto& id : prop_ids) { 93 | std::stringstream ss; 94 | 95 | // model id (unique identifier) 96 | ss << id; 97 | std::string id_text = "ID: " + ss.str(); 98 | ImGui::Text(id_text.c_str()); 99 | 100 | // get other stats 101 | auto model_ref = aa_engine_ref->GetProp(id).lock(); 102 | 103 | // changable model location 104 | ss.str(std::string()); 105 | auto& loc = model_ref->GetLocation(); 106 | ss << "Location: ";// << loc.x << ", " << loc.y << ", " << loc.z; 107 | std::string loc_string = ss.str(); 108 | ImGui::Text(loc_string.c_str()); 109 | float pos[3] = { 0,0,0 }; 110 | pos[0] = loc.x; pos[1] = loc.y; pos[2] = loc.z; 111 | static const float MIN = -3000.f, MAX = 3000.f; 112 | bool loc_changed = ImGui::SliderFloat3("xyz", pos, MIN, MAX, "%.2f"); 113 | if (loc_changed) { 114 | model_ref->SetLocation(glm::vec3(pos[0], pos[1], pos[2])); 115 | } 116 | 117 | 118 | } 119 | ImGui::End(); 120 | } 121 | }); 122 | } 123 | 124 | // teardown function 125 | void remove_file_menu() { 126 | if (!aa_engine_ref) return; // not setup, don't run 127 | aa_engine_ref->RemoveFromImGuiUpdate(file_menu_imgui_id); 128 | aa_engine_ref = nullptr; 129 | file_menu_imgui_id = -1; 130 | show_model_panel = false; 131 | show_controls = false; 132 | } 133 | -------------------------------------------------------------------------------- /Foundation/src/Sound/LongSound.cpp: -------------------------------------------------------------------------------- 1 | #include "../Sound/LongSound.h" 2 | #include "../Sound/SoundDevice.h" 3 | #include 4 | #include 5 | #include 6 | #include "ErrorCheck.h" 7 | 8 | namespace AA { 9 | 10 | void LongSound::Play() { 11 | // if already playing, don't do anything 12 | if (GetPlayingState() == AL_PLAYING) 13 | return; 14 | 15 | /* Rewind the source position and clear the buffer queue */ 16 | alSourceRewind(p_Source); 17 | local_alErrorCheck(); 18 | 19 | alSourcei(p_Source, AL_BUFFER, 0); 20 | local_alErrorCheck(); 21 | 22 | /* Fill the buffer queue */ 23 | ALsizei i; 24 | for (i = 0; i < BUFFER_COUNT; i++) { 25 | /* Get some data to give it to the buffer */ 26 | sf_count_t slen = sf_readf_short(p_Sndfile, p_Membuf, BUFFER_SAMPLES); 27 | if (slen < 1) break; 28 | 29 | slen *= p_Sfinfo.channels * (sf_count_t)sizeof(short); 30 | alBufferData(p_Buffers[i], p_Format, p_Membuf, (ALsizei)slen, p_Sfinfo.samplerate); 31 | local_alErrorCheck(); 32 | } 33 | 34 | /* Now queue and start playback! */ 35 | alSourceQueueBuffers(p_Source, i, p_Buffers); 36 | local_alErrorCheck(); 37 | 38 | alSourcePlay(p_Source); 39 | local_alErrorCheck(); 40 | } 41 | 42 | void LongSound::Pause() { 43 | // if not currently Playing, pause does nothing 44 | if (GetPlayingState() != AL_PLAYING) 45 | return; 46 | 47 | alSourcePause(p_Source); 48 | local_alErrorCheck(); 49 | } 50 | 51 | void LongSound::Resume() { 52 | // do nothing if not paused 53 | if (GetPlayingState() != AL_PAUSED) 54 | return; 55 | 56 | alSourcePlay(p_Source); 57 | local_alErrorCheck(); 58 | } 59 | 60 | void LongSound::Stop() { 61 | // if already stopped, do nothing 62 | if (GetPlayingState() == AL_STOPPED) 63 | return; 64 | 65 | alSourceStop(p_Source); 66 | local_alErrorCheck(); 67 | } 68 | 69 | void LongSound::SetVolume(const float& val) { 70 | float newvolume = val; 71 | if (newvolume < 0.f) { 72 | newvolume = 0.f; 73 | } 74 | alSourcef(p_Source, AL_GAIN, newvolume); 75 | local_alErrorCheck(); 76 | } 77 | 78 | void LongSound::UpdatePlayBuffer() { 79 | if (GetPlayingState() != AL_PLAYING) 80 | return; 81 | 82 | ALint processed; 83 | alGetSourcei(p_Source, AL_BUFFERS_PROCESSED, &processed); 84 | local_alErrorCheck(); 85 | 86 | /* Unqueue and handle each processed buffer */ 87 | while (processed > 0) { 88 | ALuint bufid; 89 | sf_count_t slen; 90 | 91 | alSourceUnqueueBuffers(p_Source, 1, &bufid); 92 | local_alErrorCheck(); 93 | 94 | processed--; 95 | 96 | /* Read the next chunk of data, refill the buffer, and queue it back on the source */ 97 | slen = sf_readf_short(p_Sndfile, p_Membuf, BUFFER_SAMPLES); 98 | if (slen > 0) { 99 | slen *= p_Sfinfo.channels * (sf_count_t)sizeof(short); 100 | alBufferData(bufid, p_Format, p_Membuf, (ALsizei)slen, p_Sfinfo.samplerate); 101 | local_alErrorCheck(); 102 | 103 | alSourceQueueBuffers(p_Source, 1, &bufid); 104 | local_alErrorCheck(); 105 | } 106 | } 107 | } 108 | 109 | ALint LongSound::GetPlayingState() { 110 | ALint curr_state; 111 | alGetSourcei(p_Source, AL_SOURCE_STATE, &curr_state); 112 | local_alErrorCheck(); 113 | return curr_state; 114 | } 115 | 116 | bool LongSound::IsPlaying() { 117 | if (GetPlayingState() == AL_PLAYING) 118 | return true; 119 | 120 | return false; 121 | } 122 | 123 | LongSound::LongSound(const char* filename) { 124 | alGenSources(1, &p_Source); 125 | local_alErrorCheck(); 126 | 127 | alGenBuffers(BUFFER_COUNT, p_Buffers); 128 | local_alErrorCheck(); 129 | 130 | std::size_t frame_size; 131 | 132 | p_Sndfile = sf_open(filename, SFM_READ, &p_Sfinfo); 133 | if (!p_Sndfile) { 134 | //fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(NULL)); 135 | throw("Could not open audio file"); 136 | } 137 | 138 | /* Get the sound format, and figure out the OpenAL format */ 139 | p_Format = 0; 140 | if (p_Sfinfo.channels == 1) 141 | p_Format = AL_FORMAT_MONO16; 142 | else if (p_Sfinfo.channels == 2) 143 | p_Format = AL_FORMAT_STEREO16; 144 | else if (p_Sfinfo.channels == 3) { 145 | if (sf_command(p_Sndfile, SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT) 146 | p_Format = AL_FORMAT_BFORMAT2D_16; 147 | } else if (p_Sfinfo.channels == 4) { 148 | if (sf_command(p_Sndfile, SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT) 149 | p_Format = AL_FORMAT_BFORMAT3D_16; 150 | } 151 | 152 | if (!p_Format) { 153 | //fprintf(stderr, "Unsupported channel count: %d\n", p_Sfinfo.channels); 154 | sf_close(p_Sndfile); 155 | p_Sndfile = NULL; 156 | throw("Unsupported channel count in opening new sound file"); 157 | } 158 | 159 | frame_size = ((size_t)BUFFER_SAMPLES * (size_t)p_Sfinfo.channels) * sizeof(short); 160 | p_Membuf = static_cast(malloc(frame_size)); 161 | 162 | } 163 | 164 | LongSound::~LongSound() { 165 | if (IsPlaying()) 166 | Stop(); 167 | 168 | //routine: delete all buffer data and snd file stuff 169 | //close player file 170 | if (p_Sndfile) { 171 | sf_close(p_Sndfile); 172 | } 173 | p_Sndfile = nullptr; 174 | free(p_Membuf); 175 | p_Membuf = nullptr; 176 | 177 | //delete sources 178 | alDeleteSources(1, &p_Source); 179 | 180 | //delete buffers 181 | alDeleteBuffers(BUFFER_COUNT, p_Buffers); 182 | } 183 | 184 | }; 185 | -------------------------------------------------------------------------------- /Foundation/src/IntefaceRunetime.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "OS/OpenGL/Graphics.h" 6 | #include "Scene/Lights.h" 7 | #include "Scene/Skybox.h" 8 | #include "Sound/SoundDevice.h" 9 | #include "Sound/Speaker.h" 10 | #include "Sound/SoundEffect.h" 11 | #include "Sound/LongSound.h" 12 | #include "GUI/imGUI.h" 13 | #include "OS/OpenGL/InternalShaders/Init.h" 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace AA { 21 | 22 | extern void g_poll_input_events(); 23 | extern bool g_new_key_reads; 24 | extern bool g_os_window_resized; 25 | extern KeyboardButtons g_keyboard_input_status; 26 | extern MouseScrollWheel g_scroll_input_status; 27 | extern MouseCursorPos g_mouse_input_status; 28 | extern MouseButtons g_mouse_button_status; 29 | 30 | // runs once after a new run start 31 | void Interface::begin() { 32 | for (const auto& oB : onStart) { 33 | oB.second(); 34 | } 35 | } 36 | 37 | // Runs core and all user defined functionality 38 | void Interface::update() { 39 | 40 | // init delta clock on first tap into update 41 | static float currTime; 42 | static float lastTime = (float)glfwGetTime(); 43 | float elapsedTime; 44 | 45 | // update engine run delta times 46 | currTime = (float)glfwGetTime(); 47 | elapsedTime = currTime - lastTime; 48 | lastTime = currTime; 49 | 50 | for (auto& oDU : onUpdate) { 51 | oDU.second(elapsedTime); 52 | } 53 | 54 | for (auto& p : mProps) { 55 | p->Update(); 56 | if (p->HasAnimation()) 57 | p->UpdateAnim(elapsedTime); 58 | } 59 | 60 | if (mMusic) { 61 | static float music_rebuffer_cd = 0; 62 | music_rebuffer_cd += elapsedTime; 63 | if (music_rebuffer_cd > .5f) { // todo: math with file size and stuff to figure out how long this cd should actually be 64 | mMusic->UpdatePlayBuffer(); 65 | music_rebuffer_cd = 0; 66 | } 67 | } 68 | 69 | if (g_new_key_reads) { 70 | for (auto& oKH : onKeyHandling) { oKH.second(g_keyboard_input_status); } 71 | for (auto& oMH : onMouseButtonHandling) { oMH.second(g_mouse_button_status); } 72 | for (auto& oSH : onScrollHandling) { oSH.second(g_scroll_input_status); } 73 | g_scroll_input_status.yOffset = g_scroll_input_status.xOffset = 0; 74 | g_new_key_reads = false; 75 | } 76 | 77 | for (auto& oMH : onMouseHandling) { oMH.second(g_mouse_input_status); } 78 | } 79 | 80 | void Interface::settle_window_resize_flag() { 81 | if (!mCameras.empty()) { 82 | for (auto& cam : mCameras) { 83 | if (cam->GetIsAlwaysScreenSize()) { 84 | cam->SetBottomLeft(0, 0); 85 | cam->SetDimensions(mWindow->GetCurrentWidth(), mWindow->GetCurrentHeight()); 86 | } 87 | cam->ProjectionChanged(); 88 | } 89 | } 90 | g_os_window_resized = false; 91 | } 92 | 93 | void Interface::pre_render() { 94 | if (g_os_window_resized) { 95 | settle_window_resize_flag(); 96 | } 97 | OpenGL::GetGL()->NewFrame(); 98 | 99 | } 100 | 101 | // Renders visable props every frame 102 | void Interface::render() { 103 | if (!mCameras.empty()) { 104 | if (mSunLight) { OpenGL::GetGL()->BatchRenderShadows(mCameras.front()->GetPosition(), *mSunLight, mProps); } 105 | for (const auto& cam : mCameras) { 106 | cam->NewFrame(); 107 | OpenGL::GetGL()->RenderSkybox(cam->GetSkybox(), cam->GetViewport()); 108 | OpenGL::GetGL()->BatchRenderToViewport(mProps, cam->GetViewport()); 109 | if (mDebugLightIndicators) { 110 | for (const auto& pl : mPointLights) 111 | OpenGL::GetGL()->RenderDebugCube(pl->Position); 112 | //for (const auto& sl : mSpotLights) 113 | // OpenGL::GetGL()->RenderSpotLightIcon(sl->Position, sl->Direction); 114 | //if (mSunLight) 115 | // OpenGL::GetGL()->RenderDirectionalLightArrowIcon(mSunLight->Direction); 116 | } 117 | } 118 | } 119 | 120 | // render imgui interface 121 | if (mIMGUI) { 122 | mIMGUI->NewFrame(); 123 | for (auto& oIU : onImGuiUpdate) { oIU.second(); } 124 | mIMGUI->Render(); 125 | } 126 | } 127 | 128 | void Interface::post_render() { 129 | mWindow->swap_buffers(); 130 | g_poll_input_events(); 131 | if (mWindow) if (mWindow->mWindowOptions->_editor_drag_and_drop) if (!mWindow->dropped_paths.empty()) { 132 | AddProp(mWindow->dropped_paths.front().c_str(), false); // process queue item 133 | mWindow->dropped_paths.pop(); // remove from queue 134 | } 135 | } 136 | 137 | // Runs Once on Engine Shutdown 138 | void Interface::teardown() { 139 | // run user preferred functions first 140 | for (auto& oTD : onQuit) { 141 | oTD.second(); 142 | } 143 | 144 | mCameras.clear(); 145 | 146 | OpenGL::GetGL()->DeletePrimatives(); 147 | 148 | ClearAllRuntimeLamdaFunctions(); 149 | 150 | InternalShaders::Shutdown(); 151 | 152 | // delete all the meshes and textures from GPU memory 153 | mProps.clear(); 154 | 155 | for (auto& anim : mAnimation) { 156 | anim.reset(); 157 | } 158 | mAnimation.clear(); 159 | 160 | mSunLight.reset(); 161 | mPointLights.clear(); 162 | mSpotLights.clear(); 163 | mDebugLightIndicators = false; 164 | 165 | // delete imgui 166 | if (mIMGUI) { 167 | mIMGUI->Shutdown(); 168 | delete mIMGUI; 169 | mIMGUI = nullptr; 170 | } 171 | 172 | mSpeakers.clear(); 173 | 174 | mSoundEffects.clear(); 175 | 176 | if (mMusic) { 177 | RemoveMusic(); 178 | } 179 | 180 | SoundDevice::Shutdown(); 181 | 182 | mWindow.reset(); 183 | 184 | isInit = false; 185 | } 186 | 187 | } // end namespace AA 188 | -------------------------------------------------------------------------------- /Foundation/testsuite/sun_light_script.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | static struct Sunlight { 6 | AA::Interface* interface_ref = nullptr; 7 | float light_direction[3] = { -.33f, -1.0f, -.33f }; 8 | float light_ambient[3] = { .099f, .099f, .099f }; 9 | float light_diffuse[3] = { .43f, .43f, .43f }; 10 | float light_specular[3] = { .5f, .5f, .5f }; 11 | 12 | bool shadow_on = true; 13 | 14 | float shadow_far_plane = 1202.f; 15 | 16 | float shadow_ortho_size = 321.5f; 17 | 18 | float shadow_bias_min = 0.00050f; 19 | float shadow_bias_max_multi = 0.005637f; 20 | 21 | } *sun; 22 | 23 | void load_sun(AA::Interface& interface, bool daycyclehack = false) { 24 | if (!sun) { 25 | sun = new Sunlight(); 26 | sun->interface_ref = &interface; 27 | } else { return; } 28 | 29 | // a sunlight 30 | interface.SetSunLight( 31 | glm::vec3(sun->light_direction[0], sun->light_direction[1], sun->light_direction[2]), 32 | glm::vec3(sun->light_ambient[0], sun->light_ambient[1], sun->light_ambient[2]), 33 | glm::vec3(sun->light_diffuse[0], sun->light_diffuse[1], sun->light_diffuse[2]), 34 | glm::vec3(sun->light_specular[0], sun->light_specular[1], sun->light_specular[2]) 35 | ); 36 | 37 | // set shadow defaults 38 | { 39 | auto strong = sun->interface_ref->GetSunLight().lock(); 40 | strong->Shadows = sun->shadow_on; 41 | strong->ShadowFarPlane = sun->shadow_far_plane; 42 | strong->ShadowOrthoSize = sun->shadow_ortho_size; 43 | strong->SetShadowBiasMin(sun->shadow_bias_min); 44 | strong->SetShadowBiasMax(sun->shadow_bias_max_multi); 45 | } 46 | 47 | sun->interface_ref->AddToImGuiUpdate([]() { 48 | ImGui::Begin("SUNLIGHT"); 49 | bool updated_dir = ImGui::SliderFloat3("Direction", sun->light_direction, -1.f, 1.f, "%f", 1.f); 50 | 51 | static bool vec_lock = true; 52 | bool updated_update_veclock = ImGui::Checkbox("vec lock", &vec_lock); 53 | 54 | bool updated_amb = false; 55 | bool updated_diff = false; 56 | bool updated_spec = false; 57 | if (vec_lock) { 58 | updated_amb = ImGui::SliderFloat("Ambient", sun->light_ambient, .003f, 1.f, "%f", 1.f); 59 | updated_diff = ImGui::SliderFloat("Diffuse", sun->light_diffuse, .003f, 1.f, "%f", 1.f); 60 | updated_spec = ImGui::SliderFloat("Specular", sun->light_specular, .003f, 1.f, "%f", 1.f); 61 | } else { 62 | updated_amb = ImGui::SliderFloat3("Ambient", sun->light_ambient, .003f, 1.f, "%f", 1.f); 63 | updated_diff = ImGui::SliderFloat3("Diffuse", sun->light_diffuse, .003f, 1.f, "%f", 1.f); 64 | updated_spec = ImGui::SliderFloat3("Specular", sun->light_specular, .003f, 1.f, "%f", 1.f); 65 | } 66 | bool updated_shadows = ImGui::Checkbox("RenderSunShadows", &sun->shadow_on); 67 | bool updated_shadow_far = ImGui::SliderFloat("ShadowFarPlane", &sun->shadow_far_plane, 10.f, 2048.f, "%f", 1.0f); 68 | bool updated_shadow_bias_min = ImGui::SliderFloat("ShadowBiasMin", &sun->shadow_bias_min, 0.00005f, 0.06f, "%f", 1.0f); 69 | bool updated_shadow_bias_max = ImGui::SliderFloat("ShadowBiasMax", &sun->shadow_bias_max_multi, 0.001f, 0.06f, "%f", 1.0f); 70 | bool updated_shadow_ortho_size = ImGui::SliderFloat("ShadowOrthoSize", &sun->shadow_ortho_size, 10.f, 2048.f, "%f", 1.0f); 71 | ImGui::End(); 72 | 73 | // update as needed 74 | if (updated_dir || updated_amb || updated_diff || updated_spec) { 75 | if (vec_lock) { 76 | sun->light_ambient[2] = sun->light_ambient[1] = sun->light_ambient[0]; 77 | sun->light_diffuse[2] = sun->light_diffuse[1] = sun->light_diffuse[0]; 78 | sun->light_specular[2] = sun->light_specular[1] = sun->light_specular[0]; 79 | } 80 | 81 | sun->interface_ref->SetSunLight( 82 | glm::vec3(sun->light_direction[0], sun->light_direction[1], sun->light_direction[2]), 83 | glm::vec3(sun->light_ambient[0], sun->light_ambient[1], sun->light_ambient[2]), 84 | glm::vec3(sun->light_diffuse[0], sun->light_diffuse[1], sun->light_diffuse[2]), 85 | glm::vec3(sun->light_specular[0], sun->light_specular[1], sun->light_specular[2]) 86 | ); 87 | 88 | } 89 | if (updated_shadows) { 90 | sun->interface_ref->GetSunLight().lock()->Shadows = sun->shadow_on; 91 | } 92 | if (updated_shadow_far) { 93 | sun->interface_ref->GetSunLight().lock()->ShadowFarPlane = sun->shadow_far_plane; 94 | } 95 | if (updated_shadow_bias_min) { 96 | sun->interface_ref->GetSunLight().lock()->SetShadowBiasMin(sun->shadow_bias_min); 97 | } 98 | if (updated_shadow_bias_max) { 99 | sun->interface_ref->GetSunLight().lock()->SetShadowBiasMax(sun->shadow_bias_max_multi); 100 | } 101 | if (updated_shadow_ortho_size) { 102 | sun->interface_ref->GetSunLight().lock()->ShadowOrthoSize = sun->shadow_ortho_size; 103 | } 104 | }); 105 | 106 | if (daycyclehack) { 107 | sun->interface_ref->AddToUpdate([](float dt) { 108 | static float passed_time = 0.f; 109 | passed_time += (dt/4.f); 110 | 111 | sun->light_direction[0] = sin(passed_time); 112 | sun->light_direction[2] = cos(passed_time); 113 | 114 | sun->interface_ref->SetSunLight( 115 | glm::vec3(sun->light_direction[0], sun->light_direction[1], sun->light_direction[2]), 116 | glm::vec3(sun->light_ambient[0], sun->light_ambient[1], sun->light_ambient[2]), 117 | glm::vec3(sun->light_diffuse[0], sun->light_diffuse[1], sun->light_diffuse[2]), 118 | glm::vec3(sun->light_specular[0], sun->light_specular[1], sun->light_specular[2]) 119 | ); 120 | }); 121 | } 122 | } 123 | 124 | void unload_sun() { 125 | delete sun; 126 | sun = nullptr; 127 | } 128 | -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/PrimativeMaker.cpp: -------------------------------------------------------------------------------- 1 | #include "PrimativeMaker.h" 2 | #include "Graphics.h" 3 | namespace AA { 4 | 5 | static unsigned int vao_to_the_cube = 0; 6 | // creates and returns vao to a -1 by 1 cube 7 | unsigned int PrimativeMaker::load_cube() { 8 | if (vao_to_the_cube != 0) { 9 | return vao_to_the_cube; 10 | } 11 | 12 | const float cubeVertices[] = { 13 | // front 14 | -1.0, -1.0, 1.0, 15 | 1.0, -1.0, 1.0, 16 | 1.0, 1.0, 1.0, 17 | -1.0, 1.0, 1.0, 18 | // back 19 | -1.0, -1.0, -1.0, 20 | 1.0, -1.0, -1.0, 21 | 1.0, 1.0, -1.0, 22 | -1.0, 1.0, -1.0 23 | }; 24 | 25 | const unsigned int faces[] = { 26 | // front 27 | 0, 1, 2, 28 | 2, 3, 0, 29 | // right 30 | 1, 5, 6, 31 | 6, 2, 1, 32 | // back 33 | 7, 6, 5, 34 | 5, 4, 7, 35 | // left 36 | 4, 0, 3, 37 | 3, 7, 4, 38 | // bottom 39 | 4, 5, 1, 40 | 1, 0, 4, 41 | // top 42 | 3, 2, 6, 43 | 6, 7, 3 }; 44 | 45 | vao_to_the_cube = OpenGL::GetGL()->Upload3DPositionsMesh(cubeVertices, sizeof(cubeVertices) / sizeof(cubeVertices[0]), faces, sizeof(faces) / sizeof(faces[0])); 46 | 47 | return vao_to_the_cube; 48 | } 49 | void PrimativeMaker::unload_cube() { 50 | if (vao_to_the_cube != 0) { 51 | OpenGL::GetGL()->DeleteMesh(1u, vao_to_the_cube); 52 | vao_to_the_cube = 0; 53 | } 54 | } 55 | 56 | static unsigned int vao_to_the_plane = 0; 57 | // creates and returns vao to a -1 by 1 plane 58 | unsigned int PrimativeMaker::load_plane() { 59 | 60 | if (vao_to_the_plane != 0) { 61 | return vao_to_the_plane; 62 | } 63 | const float SIZE = 1.f; 64 | const float planeVertices[] = { 65 | // positions 66 | -SIZE, 0, SIZE, 67 | SIZE, 0, SIZE, 68 | -SIZE, 0, -SIZE, 69 | SIZE, 0, -SIZE 70 | }; 71 | 72 | unsigned int faces[] = { //indices 73 | 1,2,0,1,3,2 74 | }; 75 | 76 | vao_to_the_plane = OpenGL::GetGL()->Upload3DPositionsMesh(planeVertices, sizeof(planeVertices) / sizeof(planeVertices[0]), faces, sizeof(faces) / sizeof(faces[0])); 77 | return vao_to_the_plane; 78 | } 79 | void PrimativeMaker::unload_plane() { 80 | if (vao_to_the_plane != 0) { 81 | OpenGL::GetGL()->DeleteMesh(1u, vao_to_the_plane); 82 | vao_to_the_plane = 0; 83 | } 84 | } 85 | 86 | static unsigned int vao_to_the_cone = 0; 87 | // creates and returns vao to a botched cone 88 | unsigned int PrimativeMaker::load_cone() { 89 | 90 | // todo: fix? 91 | unsigned int faces[] = { //indices 92 | 1,1,1,33,2,1,2,3,1, 93 | 2,3,2,33,2,2,3,4,2, 94 | 3,4,3,33,2,3,4,5,3, 95 | 4,5,4,33,2,4,5,6,4, 96 | 5,6,5,33,2,5,6,7,5, 97 | 6,7,6,33,2,6,7,8,6, 98 | 7,8,7,33,2,7,8,9,7, 99 | 8,9,8,33,2,8,9,10,8, 100 | 9,10,9,33,2,9,10,11,9, 101 | 10,11,10,33,2,10,11,12,10, 102 | 11,12,11,33,2,11,12,13,11, 103 | 12,13,12,33,2,12,13,14,12, 104 | 13,14,13,33,2,13,14,15,13, 105 | 14,15,14,33,2,14,15,16,14, 106 | 15,16,15,33,2,15,16,17,15, 107 | 16,17,16,33,2,16,17,18,16, 108 | 17,18,17,33,2,17,18,19,17, 109 | 18,19,18,33,2,18,19,20,18, 110 | 19,20,19,33,2,19,20,21,19, 111 | 20,21,20,33,2,20,21,22,20, 112 | 21,22,21,33,2,21,22,23,21, 113 | 22,23,22,33,2,22,23,24,22, 114 | 23,24,23,33,2,23,24,25,23, 115 | 24,25,24,33,2,24,25,26,24, 116 | 25,26,25,33,2,25,26,27,25, 117 | 26,27,26,33,2,26,27,28,26, 118 | 27,28,27,33,2,27,28,29,27, 119 | 28,29,28,33,2,28,29,30,28, 120 | 29,30,29,33,2,29,30,31,29, 121 | 30,31,30,33,2,30,31,32,30, 122 | 1,33,31,2,34,31,3,35,31,4,36,31,5,37,31,6,38,31,7,39,31,8,40,31,9,41,31,10,42,31,11,43,31,12,44,31,13,45,31,14,46,31,15,47,31,16,48,31,17,49,31,18,50,31,19,51,31,20,52,31,21,53,31,22,54,31,23,55,31,24,56,31,25,57,31,26,58,31,27,59,31,28,60,31,29,61,31,30,62,31,31,63,31,32,64,31, 123 | 31,32,32,33,2,32,32,65,32, 124 | 32,65,33,33,2,33,1,1,33 125 | 126 | 127 | }; 128 | 129 | unsigned int out_num_elements = sizeof(faces) / sizeof(faces[0]); 130 | 131 | if (vao_to_the_cone == 0) { 132 | // needs loaded 133 | const float coneVertices[] = { 134 | // positions 135 | 1.000000f,0.000007f,-1.000000f, 136 | 0.999999f,0.195098f,-0.980785f, 137 | 0.999997f,0.382691f,-0.923880f, 138 | 0.999996f,0.555578f,-0.831470f, 139 | 0.999995f,0.707114f,-0.707107f, 140 | 0.999994f,0.831477f,-0.555570f, 141 | 0.999993f,0.923887f,-0.382683f, 142 | 0.999993f,0.980793f,-0.195090f, 143 | 0.999993f,1.000007f,0.000000f, 144 | 0.999993f,0.980793f,0.195090f, 145 | 0.999993f,0.923887f,0.382683f, 146 | 0.999994f,0.831477f,0.555570f, 147 | 0.999995f,0.707114f,0.707107f, 148 | 0.999996f,0.555578f,0.831470f, 149 | 0.999997f,0.382691f,0.923880f, 150 | 0.999999f,0.195098f,0.980785f, 151 | 1.000000f,0.000007f,1.000000f, 152 | 1.000001f,-0.195083f,0.980785f, 153 | 1.000003f,-0.382676f,0.923880f, 154 | 1.000004f,-0.555563f,0.831470f, 155 | 1.000005f,-0.707099f,0.707107f, 156 | 1.000006f,-0.831462f,0.555570f, 157 | 1.000007f,-0.923872f,0.382684f, 158 | 1.000007f,-0.980778f,0.195090f, 159 | 1.000007f,-0.999993f,-0.000000f, 160 | 1.000007f,-0.980778f,-0.195090f, 161 | 1.000007f,-0.923872f,-0.382684f, 162 | 1.000006f,-0.831462f,-0.555570f, 163 | 1.000005f,-0.707099f,-0.707107f, 164 | 1.000004f,-0.555563f,-0.831470f, 165 | 1.000003f,-0.382676f,-0.923880f, 166 | 1.000001f,-0.195083f,-0.980785f, 167 | -1.000000f,-0.000007f,0.000000f 168 | 169 | }; 170 | unsigned int num_verts = sizeof(coneVertices) / sizeof(coneVertices[0]); 171 | vao_to_the_cone = OpenGL::GetGL()->Upload3DPositionsMesh(coneVertices, sizeof(coneVertices) / sizeof(coneVertices[0]), faces, sizeof(faces) / sizeof(faces[0])); 172 | } 173 | 174 | return vao_to_the_cone; 175 | } 176 | void PrimativeMaker::unload_cone() { 177 | if (vao_to_the_cone != 0) { 178 | OpenGL::GetGL()->DeleteMesh(1u, vao_to_the_cone); 179 | vao_to_the_cone = 0; 180 | } 181 | } 182 | 183 | //utility 184 | void PrimativeMaker::unload_all() { 185 | unload_cone(); 186 | unload_cube(); 187 | unload_plane(); 188 | } 189 | 190 | } -------------------------------------------------------------------------------- /Foundation/src/Sound/SoundDevice.cpp: -------------------------------------------------------------------------------- 1 | #include "SoundDevice.h" 2 | #include 3 | #include 4 | namespace AA { 5 | 6 | static SoundDevice* p_SoundDevice_instance = nullptr; 7 | 8 | ALCdevice* p_ALCDevice; 9 | 10 | ALCcontext* p_ALCContext; 11 | 12 | SoundDevice* SoundDevice::Get() { 13 | Init(); // uses default device unless device was custom init 14 | return p_SoundDevice_instance; 15 | } 16 | 17 | void SoundDevice::Init() { 18 | if (!p_SoundDevice_instance) { 19 | p_SoundDevice_instance = new SoundDevice(); 20 | } 21 | } 22 | 23 | void SoundDevice::Init(const char* devicename) { 24 | if (!p_SoundDevice_instance) { 25 | p_SoundDevice_instance = new SoundDevice(devicename); 26 | } 27 | } 28 | 29 | void SoundDevice::Shutdown() { 30 | if (p_SoundDevice_instance) { 31 | delete p_SoundDevice_instance; 32 | p_SoundDevice_instance = nullptr; 33 | } 34 | } 35 | 36 | void SoundDevice::SwitchDevice(const char* devicename) { 37 | //close previous device 38 | if (!alcMakeContextCurrent(nullptr)) 39 | throw("failed to set context to nullptr"); 40 | 41 | alcDestroyContext(p_ALCContext); 42 | if (p_ALCContext) 43 | throw("failed to unset during close"); 44 | 45 | if (!alcCloseDevice(p_ALCDevice)) 46 | throw("failed to close sound device"); 47 | 48 | //open new device 49 | p_ALCDevice = alcOpenDevice(devicename); 50 | if (!p_ALCDevice) 51 | throw("failed to get sound device"); 52 | 53 | p_ALCContext = alcCreateContext(p_ALCDevice, nullptr); 54 | if (!p_ALCContext) 55 | throw("failed to set sound context"); 56 | 57 | if (!alcMakeContextCurrent(p_ALCContext)) 58 | throw("failed to make context current"); 59 | 60 | 61 | //#ifdef _DEBUG 62 | // //verbose 63 | // const ALCchar* name = NULL; 64 | // if (alcIsExtensionPresent(p_ALCDevice, "ALC_ENUMERATE_ALL_EXT")) 65 | // name = alcGetString(p_ALCDevice, ALC_ALL_DEVICES_SPECIFIER); 66 | // if (!name || alcGetError(p_ALCDevice) != AL_NO_ERROR) 67 | // name = alcGetString(p_ALCDevice, ALC_DEVICE_SPECIFIER); 68 | // printf("Opened \"%s\"\n", name); 69 | //#endif 70 | } 71 | 72 | void SoundDevice::PopulateDeviceVec(std::vector& devicesVec) { 73 | const ALCchar* devicenames; 74 | //devicenames = alcGetString(p_ALCDevice, ALC_DEVICE_SPECIFIER); // get current without windows tag 75 | devicenames = alcGetString(nullptr, ALC_DEVICE_SPECIFIER); // get all without windows tag 76 | ////devicenames = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER); // get all with windows tag 77 | //devicenames = alcGetString(p_ALCDevice, ALC_ALL_DEVICES_SPECIFIER); // get current with windows tag 78 | 79 | const char* ptr = devicenames; 80 | 81 | devicesVec.clear(); 82 | 83 | do { 84 | devicesVec.push_back(std::string(ptr)); 85 | ptr += devicesVec.back().size() + 1; 86 | } while (*(ptr + 1) != '\0'); 87 | 88 | } 89 | 90 | void SoundDevice::SuspendContext() { 91 | alcSuspendContext(p_ALCContext); 92 | } 93 | 94 | void SoundDevice::ResumeContext() { 95 | alcProcessContext(p_ALCContext); 96 | } 97 | 98 | SoundDevice::SoundDevice() { 99 | p_ALCDevice = alcOpenDevice(nullptr); // get default device 100 | if (!p_ALCDevice) 101 | throw("failed to get sound device"); 102 | 103 | p_ALCContext = alcCreateContext(p_ALCDevice, nullptr); 104 | if (!p_ALCContext) 105 | throw("failed to set sound context"); 106 | 107 | if (!alcMakeContextCurrent(p_ALCContext)) 108 | throw("failed to make context current"); 109 | 110 | //#ifdef _DEBUG 111 | //verbose say device name opened 112 | //const ALCchar* name = NULL; 113 | //if (alcIsExtensionPresent(p_ALCDevice, "ALC_ENUMERATE_ALL_EXT")) 114 | //name = alcGetString(p_ALCDevice, ALC_ALL_DEVICES_SPECIFIER); 115 | //if (!name || alcGetError(p_ALCDevice) != AL_NO_ERROR) 116 | //name = alcGetString(p_ALCDevice, ALC_DEVICE_SPECIFIER); 117 | //printf("Opened \"%s\"\n", name); 118 | //#endif 119 | 120 | // todo: check for surround sound devices/set options for hrtf https://github.com/kcat/openal-soft/blob/master/examples/alhrtf.c 121 | 122 | //ALCint attri[5]; 123 | //ALC_FREQUENCY 124 | // ALC_MONO_SOURCES 125 | // ALC_REFRESH 126 | // ALC_STEREO_SOURCES 127 | //ALC_SYNC 128 | //ALC_TRUE; 129 | //ALC_HRTF_SOFT; 130 | //attri[0] = 0; 131 | //attri[1] = ALC_HRTF_SOFT; 132 | //p_ALCContext = alcCreateContext(p_ALCDevice, attri); 133 | 134 | 135 | } 136 | 137 | SoundDevice::SoundDevice(const char* devicename) { 138 | p_ALCDevice = alcOpenDevice(devicename); 139 | if (!p_ALCDevice) 140 | throw("failed to get sound device"); 141 | 142 | p_ALCContext = alcCreateContext(p_ALCDevice, nullptr); 143 | if (!p_ALCContext) 144 | throw("failed to set sound context"); 145 | 146 | if (!alcMakeContextCurrent(p_ALCContext)) 147 | throw("failed to make context current"); 148 | 149 | // 150 | //#ifdef _DEBUG 151 | // //verbose say device name opened 152 | // const ALCchar* name = NULL; 153 | // if (alcIsExtensionPresent(p_ALCDevice, "ALC_ENUMERATE_ALL_EXT")) 154 | // name = alcGetString(p_ALCDevice, ALC_ALL_DEVICES_SPECIFIER); 155 | // if (!name || alcGetError(p_ALCDevice) != AL_NO_ERROR) 156 | // name = alcGetString(p_ALCDevice, ALC_DEVICE_SPECIFIER); 157 | // printf("Opened \"%s\"\n", name); 158 | //#endif 159 | } 160 | 161 | 162 | SoundDevice::~SoundDevice() { 163 | //if (!alcMakeContextCurrent(nullptr)) 164 | // throw("failed to set context to nullptr"); 165 | 166 | //alcDestroyContext(p_ALCContext); 167 | //if (p_ALCContext) 168 | // throw("failed to unset during close"); 169 | 170 | //if (!alcCloseDevice(p_ALCDevice)) 171 | // throw("failed to close sound device"); 172 | 173 | alcMakeContextCurrent(nullptr); 174 | alcDestroyContext(p_ALCContext); 175 | alcCloseDevice(p_ALCDevice); 176 | } 177 | 178 | } // end namespace AA 179 | -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/Loaders/Cache.cpp: -------------------------------------------------------------------------------- 1 | #include "Cache.h" 2 | #include "../../../Mesh/Skeleton.h" 3 | #include 4 | namespace AA { 5 | 6 | Cache* Cache::Get() { 7 | static Cache* cache = new Cache(); 8 | return cache; 9 | } 10 | 11 | void Cache::add(const SceneInfo& si) { 12 | for (auto& cached_scene : all_loaded_scenes_) // up ref count if scene exists already 13 | if (cached_scene.path == si.path) { 14 | cached_scene.ref_count++; 15 | return; 16 | } 17 | 18 | all_loaded_scenes_.push_front(si); 19 | } 20 | 21 | void Cache::add(const Texture2DInfo& ti) { 22 | all_loaded_2d_textures_.push_front(ti); 23 | } 24 | 25 | void Cache::add(const TextureMapType& texture_list) { 26 | for (const auto& tex : texture_list) { 27 | bool added = false; 28 | for (auto& t : all_loaded_2d_textures_) { 29 | if (tex.accessId == t.accessId) { 30 | t.ref_count++; 31 | added = true; 32 | continue; 33 | } 34 | } 35 | if (!added) { 36 | all_loaded_2d_textures_.push_front(tex); 37 | added = true; 38 | continue; 39 | } 40 | } 41 | } 42 | 43 | void Cache::remove_sceneinfo(const SceneInfo& matching) { 44 | for (auto& scene : all_loaded_scenes_) { 45 | if (scene == matching) 46 | scene.ref_count--; 47 | 48 | } 49 | } 50 | 51 | void Cache::remove_scene_at_path(const std::string& path_to_remove) { 52 | 53 | // step1: decrement loaded count of meshes and textures 54 | for (auto& ref_model_info : all_loaded_scenes_) { 55 | // no path, continue 56 | if (ref_model_info.path == "") continue; 57 | // path matches, dec ref count 58 | if (path_to_remove == ref_model_info.path.data()) { 59 | if (ref_model_info.ref_count > 0) { 60 | ref_model_info.ref_count--; 61 | } 62 | // go through all the meshes and textures and dec their ref count 63 | for (const auto& mesh : ref_model_info.meshes) { 64 | for (const auto& texid : mesh.textureDrawIds) { 65 | for (auto& ref_tex_info : all_loaded_2d_textures_) { 66 | if (texid.accessId == ref_tex_info.accessId) { 67 | ref_tex_info.ref_count--; 68 | } 69 | } 70 | } 71 | } 72 | 73 | } 74 | } 75 | 76 | // step2: remove if ref count is 0 (or less, somehow) 77 | 78 | // a) if we were using a vector instead of a forward_list 79 | //AllLoadedModels.erase(std::remove_if( 80 | // AllLoadedModels.begin(), AllLoadedModels.end(), 81 | // [](const RefModelInfo& ref) { 82 | // return ref.ref_count < 1; 83 | // }), AllLoadedModels.end()); 84 | 85 | // b) C++20 method that should work on a forward texture_list, but we are using C++17 86 | //std::erase_if(AllLoadedModels, [](const RefModelInfo& ref) { 87 | // return ref.ref_count < 1; 88 | // }); 89 | 90 | // c) C++17 (and maybe some older) method to remove from foward_list 91 | 92 | { // remove models if ref_count < 1 93 | auto before = all_loaded_scenes_.before_begin(); 94 | for (auto it = all_loaded_scenes_.begin(); it != all_loaded_scenes_.end(); ) { 95 | if (it->ref_count < 1) { 96 | it = all_loaded_scenes_.erase_after(before); 97 | } else { 98 | before = it; 99 | ++it; 100 | } 101 | } 102 | } 103 | 104 | { // remove textures if ref_count < 1 105 | auto before = all_loaded_2d_textures_.before_begin(); 106 | for (auto it = all_loaded_2d_textures_.begin(); it != all_loaded_2d_textures_.end(); ) { 107 | if (it->ref_count < 1) { 108 | it = all_loaded_2d_textures_.erase_after(before); 109 | } else { 110 | before = it; 111 | ++it; 112 | } 113 | } 114 | } 115 | 116 | } 117 | 118 | int Cache::try_load_from_cache(std::vector& out_meshes, const std::string& path) { 119 | for (auto& ref_model_info : all_loaded_scenes_) { 120 | if (ref_model_info.path == "") 121 | continue; 122 | if (path == ref_model_info.path.data()) { 123 | ref_model_info.ref_count++; 124 | for (const auto& m : ref_model_info.meshes) { 125 | add(m.textureDrawIds); 126 | out_meshes.emplace_back(MeshInfo(m.vao, m.numElements, m.textureDrawIds, m.material, m.local_transform)); 127 | } 128 | return 1; // already loaded 129 | } 130 | } 131 | return 0; // not already loaded 132 | } 133 | 134 | int Cache::try_load_from_cache(std::vector& out_meshes, Skeleton& out_skel, glm::mat4& inv_trans, const std::string& path_key) { 135 | for (auto& ref_model_info : all_loaded_scenes_) { 136 | if (ref_model_info.path == "") 137 | continue; 138 | if (path_key == ref_model_info.path.data()) { 139 | ref_model_info.ref_count++; 140 | 141 | for (const auto& m : ref_model_info.meshes) { 142 | add(m.textureDrawIds); 143 | out_meshes.emplace_back(MeshInfo(m.vao, m.numElements, m.textureDrawIds, m.material, m.local_transform)); 144 | } 145 | out_skel = *ref_model_info.skelly; 146 | inv_trans = ref_model_info.inverse_transform; 147 | return true; // already loaded 148 | } 149 | } 150 | return false; // not already loaded 151 | } 152 | 153 | int Cache::try_load_from_cache(TextureMapType& out_textures, const std::string& path_key) { 154 | for (auto& tex_info : all_loaded_2d_textures_) { 155 | if (tex_info.path == "") 156 | continue; 157 | if (path_key == tex_info.path.data()) { 158 | tex_info.ref_count++; 159 | out_textures.emplace_back(tex_info); 160 | return 1; // already loaded 161 | } 162 | } 163 | return 0; // not already loaded 164 | } 165 | 166 | 167 | bool operator==(const SceneInfo& lhs, const SceneInfo& rhs) { 168 | return (lhs.path == rhs.path); 169 | } 170 | 171 | 172 | 173 | bool operator!=(const SceneInfo& lhs, const SceneInfo& rhs) { 174 | return !(lhs.path == rhs.path); 175 | } 176 | 177 | } 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /Foundation/testsuite/fly_cam_script.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // globals local to this file 4 | static bool fly_setup = false; 5 | struct Move { 6 | bool forward = false; 7 | bool backwards = false; 8 | bool left = false; 9 | bool right = false; 10 | bool up = false; 11 | bool down = false; 12 | bool sprint = false; 13 | } move; 14 | bool is_cursor_on = true; 15 | glm::vec3 move_diff{ 0 }; 16 | const float DEFAULTMOVESPEED = 116.f; 17 | double FPP_SENSITIVITY = .1f; 18 | double lastX{ 0 }, lastY{ 0 }; 19 | double xDelta{ 0 }, yDelta{ 0 }; 20 | bool UnprocessedMovements = false; 21 | bool snap_to_center = false; 22 | unsigned int cam_id_set_to = 0; 23 | unsigned int fly_mouse_button_func = 0; 24 | unsigned int fly_kb_button_func = 0; 25 | unsigned int fly_mouse_handling_func = 0; 26 | unsigned int fly_update_func = 0; 27 | std::weak_ptr fly_window; 28 | std::weak_ptr fly_camera; 29 | static AA::Interface* interface_ref = nullptr; 30 | 31 | void setup_fpp_fly(unsigned int cam_id_to_fly, AA::Interface& interface) { 32 | if (fly_setup) return; 33 | 34 | cam_id_set_to = cam_id_to_fly; 35 | interface_ref = &interface; 36 | 37 | fly_mouse_button_func = interface.AddToMouseButtonHandling([](AA::MouseButtons& mb) { 38 | if (mb.mouseButton2) { 39 | fly_window = interface_ref->GetWindow(); 40 | std::shared_ptr local_fly_window = fly_window.lock(); 41 | if (!is_cursor_on) { 42 | local_fly_window->SetCursorToNormal(); 43 | is_cursor_on = !is_cursor_on; 44 | } else { 45 | local_fly_window->SetCursorToDisabled(); 46 | is_cursor_on = !is_cursor_on; 47 | snap_to_center = true; 48 | } 49 | } 50 | }); 51 | 52 | fly_kb_button_func = interface.AddToKeyHandling([](AA::KeyboardButtons& kb) { 53 | if (kb.w) { move.forward = true; } else { move.forward = false; } 54 | if (kb.s) { move.backwards = true; } else { move.backwards = false; } 55 | if (kb.a) { move.left = true; } else { move.left = false; } 56 | if (kb.d) { move.right = true; } else { move.right = false; } 57 | if (kb.spacebar) { move.up = true; } else { move.up = false; } 58 | if (kb.c) { move.down = true; } else { move.down = false; } 59 | if (kb.leftShift) { move.sprint = true; } else { move.sprint = false; } 60 | }); 61 | 62 | fly_mouse_handling_func = interface.AddToMouseHandling([](AA::MouseCursorPos& cursor) { 63 | if (is_cursor_on) return; 64 | 65 | if (snap_to_center) { 66 | lastX = cursor.xOffset; 67 | lastY = cursor.yOffset; 68 | snap_to_center = false; 69 | } 70 | 71 | xDelta = yDelta = 0; 72 | xDelta = cursor.xOffset - lastX; 73 | yDelta = lastY - cursor.yOffset; 74 | 75 | if (xDelta != 0.0 || yDelta != 0.0) { 76 | fly_camera = interface_ref->GetCamera(cam_id_set_to); 77 | std::shared_ptr local_fly_cam = fly_camera.lock(); 78 | local_fly_cam->ShiftPitchAndYaw(yDelta * FPP_SENSITIVITY, xDelta * FPP_SENSITIVITY); 79 | 80 | lastX = cursor.xOffset; 81 | lastY = cursor.yOffset; 82 | } 83 | 84 | }); 85 | 86 | fly_update_func = interface.AddToUpdate([](float dt) { 87 | fly_camera = interface_ref->GetCamera(cam_id_set_to); 88 | if (move.forward) { 89 | std::shared_ptr local_fly_cam = fly_camera.lock(); 90 | move_diff += local_fly_cam->GetFront(); 91 | UnprocessedMovements = true; 92 | } 93 | if (move.backwards) { 94 | std::shared_ptr local_fly_cam = fly_camera.lock(); 95 | move_diff -= local_fly_cam->GetFront(); 96 | UnprocessedMovements = true; 97 | } 98 | if (move.left) { 99 | std::shared_ptr local_fly_cam = fly_camera.lock(); 100 | move_diff -= local_fly_cam->GetRight(); 101 | UnprocessedMovements = true; 102 | } 103 | if (move.right) { 104 | std::shared_ptr local_fly_cam = fly_camera.lock(); 105 | move_diff += local_fly_cam->GetRight(); 106 | UnprocessedMovements = true; 107 | } 108 | if (move.up || move.down) { 109 | UnprocessedMovements = true; 110 | } 111 | 112 | // movement detected - do the move logic 113 | if (UnprocessedMovements) { 114 | 115 | // walking around movements 116 | glm::vec3 amount_shifted(0); 117 | amount_shifted.x += move_diff.x * dt * DEFAULTMOVESPEED; 118 | amount_shifted.z += move_diff.z * dt * DEFAULTMOVESPEED; 119 | if (move.sprint) { 120 | amount_shifted.x *= 2.f; 121 | amount_shifted.z *= 2.f; 122 | } 123 | 124 | // going up or down logic 125 | if (move.up) { 126 | amount_shifted.y += dt * DEFAULTMOVESPEED; 127 | } else if (move.down) { 128 | amount_shifted.y -= dt * DEFAULTMOVESPEED; 129 | } 130 | 131 | // apply final positions 132 | std::shared_ptr local_fly_cam = fly_camera.lock(); 133 | local_fly_cam->ShiftPosition(glm::vec3(amount_shifted.x, 0, amount_shifted.z)); 134 | local_fly_cam->ShiftPosition(glm::vec3(0, amount_shifted.y, 0)); 135 | 136 | // reset movement for next frame 137 | UnprocessedMovements = !UnprocessedMovements; 138 | move_diff = glm::vec3(0); 139 | } 140 | }); 141 | 142 | fly_setup = true; 143 | } 144 | 145 | void turn_off_fly() { 146 | cam_id_set_to = 0; 147 | fly_setup = false; 148 | move.forward = false; 149 | move.backwards = false; 150 | move.left = false; 151 | move.right = false; 152 | move.up = false; 153 | move.down = false; 154 | move.sprint = false; 155 | is_cursor_on = true; 156 | move_diff = glm::vec3(0); 157 | FPP_SENSITIVITY = .1f; 158 | lastX = lastY = xDelta = yDelta = 0; 159 | UnprocessedMovements = false; 160 | snap_to_center = false; 161 | 162 | if (interface_ref->RemoveFromMouseButtonHandling(fly_mouse_button_func)) { 163 | fly_mouse_button_func = 0; 164 | } 165 | if (interface_ref->RemoveFromKeyHandling(fly_kb_button_func)) { 166 | fly_kb_button_func = 0; 167 | } 168 | if (interface_ref->RemoveFromMouseHandling(fly_mouse_handling_func)) { 169 | fly_mouse_handling_func = 0; 170 | } 171 | if (interface_ref->RemoveFromOnUpdate(fly_update_func)) { 172 | fly_update_func = 0; 173 | } 174 | 175 | fly_window.reset(); 176 | fly_camera.reset(); 177 | interface_ref = nullptr; 178 | 179 | } -------------------------------------------------------------------------------- /Foundation/samples/common/scripts/fpp_fly.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | // globals local to this file 7 | struct Move { 8 | bool forward = false; 9 | bool backwards = false; 10 | bool left = false; 11 | bool right = false; 12 | bool up = false; 13 | bool down = false; 14 | bool sprint = false; 15 | } move; 16 | bool is_cursor_on = true; 17 | glm::vec3 move_diff{ 0 }; 18 | const float DEFAULTMOVESPEED = 116.f; 19 | double FPP_SENSITIVITY = .1f; 20 | double lastX{ 0 }, lastY{ 0 }; 21 | double xDelta{ 0 }, yDelta{ 0 }; 22 | bool UnprocessedMovements = false; 23 | bool snap_to_center = false; 24 | unsigned int cam_id_set_to = 0; 25 | unsigned int fly_mouse_button_func = 0; 26 | unsigned int fly_kb_button_func = 0; 27 | unsigned int fly_mouse_handling_func = 0; 28 | unsigned int fly_update_func = 0; 29 | std::weak_ptr fly_window; 30 | std::weak_ptr fly_camera; 31 | AA::Interface* fly_script_interface_ref = nullptr; 32 | 33 | void setup_fpp_fly(unsigned int cam_id_to_fly, AA::Interface& interface) { 34 | if (fly_script_interface_ref) return; // already setup 35 | 36 | cam_id_set_to = cam_id_to_fly; 37 | fly_script_interface_ref = &interface; 38 | 39 | fly_mouse_button_func = interface.AddToMouseButtonHandling([](AA::MouseButtons& mb) { 40 | if (mb.mouseButton2) { 41 | fly_window = fly_script_interface_ref->GetWindow(); 42 | std::shared_ptr local_fly_window = fly_window.lock(); 43 | if (!is_cursor_on) { 44 | local_fly_window->SetCursorToNormal(); 45 | is_cursor_on = !is_cursor_on; 46 | } 47 | else { 48 | local_fly_window->SetCursorToDisabled(); 49 | is_cursor_on = !is_cursor_on; 50 | snap_to_center = true; 51 | } 52 | } 53 | }); 54 | 55 | fly_kb_button_func = interface.AddToKeyHandling([](AA::KeyboardButtons& kb) { 56 | if (kb.w) { move.forward = true; } 57 | else { move.forward = false; } 58 | if (kb.s) { move.backwards = true; } 59 | else { move.backwards = false; } 60 | if (kb.a) { move.left = true; } 61 | else { move.left = false; } 62 | if (kb.d) { move.right = true; } 63 | else { move.right = false; } 64 | if (kb.spacebar) { move.up = true; } 65 | else { move.up = false; } 66 | if (kb.c) { move.down = true; } 67 | else { move.down = false; } 68 | if (kb.leftShift) { move.sprint = true; } 69 | else { move.sprint = false; } 70 | }); 71 | 72 | fly_mouse_handling_func = interface.AddToMouseHandling([](AA::MouseCursorPos& cursor) { 73 | if (is_cursor_on) return; 74 | 75 | if (snap_to_center) { 76 | lastX = cursor.xOffset; 77 | lastY = cursor.yOffset; 78 | snap_to_center = false; 79 | } 80 | 81 | xDelta = yDelta = 0; 82 | xDelta = cursor.xOffset - lastX; 83 | yDelta = lastY - cursor.yOffset; 84 | 85 | if (xDelta != 0.0 || yDelta != 0.0) { 86 | fly_camera = fly_script_interface_ref->GetCamera(cam_id_set_to); 87 | std::shared_ptr local_fly_cam = fly_camera.lock(); 88 | local_fly_cam->ShiftPitchAndYaw(yDelta * FPP_SENSITIVITY, xDelta * FPP_SENSITIVITY); 89 | 90 | lastX = cursor.xOffset; 91 | lastY = cursor.yOffset; 92 | } 93 | 94 | }); 95 | 96 | fly_update_func = interface.AddToUpdate([](float dt) { 97 | fly_camera = fly_script_interface_ref->GetCamera(cam_id_set_to); 98 | if (move.forward) { 99 | std::shared_ptr local_fly_cam = fly_camera.lock(); 100 | move_diff += local_fly_cam->GetFront(); 101 | UnprocessedMovements = true; 102 | } 103 | if (move.backwards) { 104 | std::shared_ptr local_fly_cam = fly_camera.lock(); 105 | move_diff -= local_fly_cam->GetFront(); 106 | UnprocessedMovements = true; 107 | } 108 | if (move.left) { 109 | std::shared_ptr local_fly_cam = fly_camera.lock(); 110 | move_diff -= local_fly_cam->GetRight(); 111 | UnprocessedMovements = true; 112 | } 113 | if (move.right) { 114 | std::shared_ptr local_fly_cam = fly_camera.lock(); 115 | move_diff += local_fly_cam->GetRight(); 116 | UnprocessedMovements = true; 117 | } 118 | if (move.up || move.down) { 119 | UnprocessedMovements = true; 120 | } 121 | 122 | // movement detected - do the move logic 123 | if (UnprocessedMovements) { 124 | 125 | // walking around movements 126 | glm::vec3 amount_shifted(0); 127 | amount_shifted.x += move_diff.x * dt * DEFAULTMOVESPEED; 128 | amount_shifted.z += move_diff.z * dt * DEFAULTMOVESPEED; 129 | if (move.sprint) { 130 | amount_shifted.x *= 2.f; 131 | amount_shifted.z *= 2.f; 132 | } 133 | 134 | // going up or down logic 135 | if (move.up) { 136 | amount_shifted.y += dt * DEFAULTMOVESPEED; 137 | } 138 | else if (move.down) { 139 | amount_shifted.y -= dt * DEFAULTMOVESPEED; 140 | } 141 | 142 | // apply final positions 143 | std::shared_ptr local_fly_cam = fly_camera.lock(); 144 | local_fly_cam->ShiftPosition(glm::vec3(amount_shifted.x, 0, amount_shifted.z)); 145 | local_fly_cam->ShiftPosition(glm::vec3(0, amount_shifted.y, 0)); 146 | 147 | // reset movement for next frame 148 | UnprocessedMovements = !UnprocessedMovements; 149 | move_diff = glm::vec3(0); 150 | } 151 | }); 152 | 153 | } 154 | 155 | void turn_off_fly() { 156 | if(!fly_script_interface_ref) return; // isn't setup 157 | 158 | cam_id_set_to = 0; 159 | move.forward = false; 160 | move.backwards = false; 161 | move.left = false; 162 | move.right = false; 163 | move.up = false; 164 | move.down = false; 165 | move.sprint = false; 166 | is_cursor_on = true; 167 | move_diff = glm::vec3(0); 168 | FPP_SENSITIVITY = .1f; 169 | lastX = lastY = xDelta = yDelta = 0; 170 | UnprocessedMovements = false; 171 | snap_to_center = false; 172 | 173 | if (fly_script_interface_ref->RemoveFromMouseButtonHandling(fly_mouse_button_func)) { 174 | fly_mouse_button_func = 0; 175 | } 176 | if (fly_script_interface_ref->RemoveFromKeyHandling(fly_kb_button_func)) { 177 | fly_kb_button_func = 0; 178 | } 179 | if (fly_script_interface_ref->RemoveFromMouseHandling(fly_mouse_handling_func)) { 180 | fly_mouse_handling_func = 0; 181 | } 182 | if (fly_script_interface_ref->RemoveFromOnUpdate(fly_update_func)) { 183 | fly_update_func = 0; 184 | } 185 | 186 | fly_window.reset(); 187 | fly_camera.reset(); 188 | fly_script_interface_ref = nullptr; 189 | 190 | } 191 | -------------------------------------------------------------------------------- /Foundation/src/Scene/Scene.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "../Mesh/Animator.h" 8 | #include "../Mesh/AnimationData.h" 9 | #include "../OS/OpenGL/Loaders/AssimpSceneLoader.h" 10 | 11 | namespace AA { 12 | 13 | struct SceneData { 14 | bool stenciled{ false }; 15 | glm::vec3 stencil_color{ glm::vec3(0.0f) }; 16 | bool stenciled_with_normals{ false }; 17 | float stencil_scale{ 1.1f }; 18 | bool cull_backface{ false }; 19 | bool render_shadows{ true }; 20 | bool cull_frontface_for_shadows{ false }; 21 | Spacial3D spacial_data{}; 22 | std::vector mMeshes{}; 23 | std::string cached_load_path{}; 24 | }; 25 | 26 | Scene::Scene() { 27 | scenedata_ = std::make_unique(); 28 | } 29 | 30 | Scene::Scene(const char* path, const bool& do_get_bones) { 31 | scenedata_ = std::make_unique(); 32 | if (do_get_bones) { 33 | animdata_ = std::make_unique(); 34 | } 35 | Load(path, do_get_bones); 36 | } 37 | 38 | Scene::Scene(const char* path) { 39 | scenedata_ = std::make_unique(); 40 | Load(path, false); 41 | } 42 | 43 | Scene::~Scene() { 44 | if (scenedata_) { 45 | if (!scenedata_->mMeshes.empty()) { 46 | AssimpSceneLoader::Unload(scenedata_->cached_load_path); 47 | } 48 | scenedata_.reset(); 49 | } 50 | if (animdata_) { 51 | animdata_.reset(); 52 | } 53 | } 54 | 55 | void Scene::Update() { 56 | if (scenedata_) 57 | scenedata_->spacial_data.ProcessModifications(); 58 | } 59 | 60 | // copy meshes 61 | void Scene::SetMeshes(const std::vector& meshes) { 62 | scenedata_->mMeshes = meshes; 63 | } 64 | 65 | void Scene::SetSkeleton(const Skeleton& skel) { 66 | if (animdata_) { 67 | animdata_->m_Skeleton = skel; 68 | } 69 | } 70 | 71 | Skeleton* Scene::GetSkeleton() { 72 | if (!animdata_) 73 | return nullptr; 74 | else 75 | return &animdata_->m_Skeleton; 76 | } 77 | 78 | std::vector Scene::GetFinalBoneMatrices() const { 79 | if (HasAnimation()) 80 | return animdata_->mAnimator->GetFinalBoneMatrices(); 81 | else return std::vector(); 82 | } 83 | 84 | const std::vector& Scene::GetMeshes() { 85 | return scenedata_->mMeshes; 86 | } 87 | 88 | 89 | 90 | /// 91 | /// if this doesn't throw, assume load successful 92 | /// 93 | /// path to model to load and cache 94 | void Scene::Load(const std::string& path, const bool& getanimdata) { 95 | if (!scenedata_->mMeshes.empty()) 96 | throw("Meshes are loaded already. Remove cache first."); 97 | 98 | int load_code = 0; 99 | 100 | if (getanimdata) { 101 | load_code = AssimpSceneLoader::Load_MeshesTexturesMaterialsBones(*this, path); 102 | } else { 103 | load_code = AssimpSceneLoader::Load_MeshesTexturesMaterials(*this, path); 104 | } 105 | 106 | if (load_code == 0) { // loaded from path 107 | scenedata_->cached_load_path = path; 108 | } else if (load_code == 1) { // reused already loaded cache 109 | scenedata_->cached_load_path = path; 110 | } else if (load_code == -1) { 111 | throw("scene was null"); 112 | } else if (load_code == -2) { 113 | throw("scene has incomplete flags"); 114 | } else if (load_code == -3) { 115 | throw("scene has incomplete flags"); 116 | } 117 | } 118 | 119 | void Scene::SetPathID(const std::string& path) { 120 | scenedata_->cached_load_path; 121 | } 122 | 123 | const glm::mat4 Scene::GetFMM() const { 124 | return scenedata_->spacial_data.mFinalModelMatrix; 125 | } 126 | 127 | const bool Scene::IsStenciled() const { 128 | return scenedata_->stenciled; 129 | } 130 | 131 | const bool Scene::IsStenciledWithNormals() const { 132 | return scenedata_->stenciled_with_normals; 133 | } 134 | 135 | const bool Scene::IsAnimated() const { 136 | return (animdata_) ? true : false; 137 | } 138 | 139 | const bool Scene::HasAnimation() const { 140 | if (animdata_) 141 | if (animdata_->mAnimator) 142 | return true; 143 | 144 | return false; 145 | } 146 | 147 | const float Scene::GetStencilScale() const { 148 | return scenedata_->stencil_scale; 149 | } 150 | 151 | const glm::vec3 Scene::GetStencilColor() const { 152 | return scenedata_->stencil_color; 153 | } 154 | 155 | const bool Scene::GetRenderShadows() const { return scenedata_->render_shadows; } 156 | 157 | const bool Scene::GetCullFrontFaceForShadows() const { 158 | return scenedata_->cull_frontface_for_shadows; 159 | } 160 | 161 | const bool Scene::GetBackFaceCull() const { 162 | return scenedata_->cull_backface; 163 | } 164 | 165 | const glm::vec3& Scene::GetLocation() const { 166 | return scenedata_->spacial_data.mCurrentLocation; 167 | } 168 | 169 | void Scene::SetLocation(const glm::vec3& loc) { 170 | scenedata_->spacial_data.MoveTo(loc); 171 | } 172 | 173 | void Scene::SetScale(const glm::vec3& scale) { 174 | scenedata_->spacial_data.ScaleTo(scale); 175 | } 176 | 177 | void Scene::SetRotation(const glm::vec3& rot) { 178 | scenedata_->spacial_data.RotateTo(rot); 179 | } 180 | 181 | void Scene::SetStencil(const bool& tf) { 182 | scenedata_->stenciled = tf; 183 | } 184 | 185 | void Scene::SetStencilColor(const glm::vec3& color) { 186 | scenedata_->stencil_color = color; 187 | } 188 | 189 | void Scene::SetStencilWithNormals(const bool& tf) { 190 | scenedata_->stenciled_with_normals = tf; 191 | } 192 | 193 | void Scene::SetStencilScale(const float& scale) { 194 | scenedata_->stencil_scale = scale; 195 | } 196 | 197 | void Scene::SetRenderShadows(const bool tf) { 198 | scenedata_->render_shadows = tf; 199 | } 200 | 201 | void Scene::SetFrontFaceCullForShadows(const bool tf) { 202 | scenedata_->cull_frontface_for_shadows = tf; 203 | } 204 | 205 | void Scene::SetBackfaceCull(const bool tf) { 206 | scenedata_->cull_backface = tf; 207 | } 208 | 209 | void Scene::UpdateAnim(float dt) { 210 | if (animdata_) { 211 | if (animdata_->mAnimator) 212 | animdata_->mAnimator->UpdateAnimation(dt); 213 | } 214 | } 215 | 216 | void Scene::ClearAnimator() { 217 | if (animdata_) 218 | if (animdata_->mAnimator) 219 | animdata_->mAnimator.reset(); 220 | } 221 | 222 | void Scene::SetAnimator(std::shared_ptr& anim) { 223 | if (!animdata_) 224 | return; 225 | animdata_->mAnimator = std::make_unique(anim, animdata_->mGlobalInverseTransform); 226 | 227 | } 228 | 229 | void Scene::SetGlobalInverseTransform(const glm::mat4& inv_trans) { 230 | if (animdata_) { 231 | animdata_->mGlobalInverseTransform = inv_trans; 232 | } 233 | } 234 | 235 | const std::vector Scene::GetVAOList() noexcept { 236 | std::vector ret_list; 237 | 238 | ret_list.reserve(scenedata_->mMeshes.size()); 239 | for (const auto& mesh : scenedata_->mMeshes) { 240 | ret_list.push_back(mesh.vao); 241 | } 242 | 243 | return ret_list; 244 | } 245 | 246 | } // end namespace AA 247 | -------------------------------------------------------------------------------- /Foundation/include/ArcherEngine/Scene/Camera.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../../../src/Scene/Skybox.h" 3 | #include "../../../src/Base/UniqueInstance.h" 4 | #include 5 | #include "Viewport.h" 6 | #include 7 | #include 8 | #include 9 | 10 | namespace AA { 11 | class Skybox; 12 | enum class ProjectionType { ORTHO, PERSPECTIVE }; 13 | class Camera : public UniqueInstance { 14 | public: 15 | Camera(int width, int height); 16 | ~Camera(); 17 | 18 | void ProjectionChanged(); 19 | 20 | /// 21 | /// Sets where the viewport starts starting from the bottom left. 22 | /// 23 | /// x location 24 | /// y location 25 | void SetBottomLeft(const int& x, const int& y); 26 | 27 | /// 28 | /// Set the Render Distance on a camera. 29 | /// 30 | /// a positive value to set the render distance to. negatives will be treated as a positive via absolute value. 31 | void SetMaxRenderDistance(const float& amt); 32 | 33 | /// 34 | /// Change camera to perspective view mode. 35 | /// 36 | void SetToPerspective(); 37 | 38 | /// 39 | /// Change camera to orthographic mode. 40 | /// Note that this doesn't work as expect sometimes and needs work. 41 | /// 42 | void SetToOrtho_testing(); 43 | 44 | /// 45 | /// Changes the field of view of a camera. 46 | /// 47 | /// positive value field of view to set. Bound between 1 and 360. 48 | void SetFOV(float new_fov); 49 | 50 | /// 51 | /// Sets the camera width and height. 52 | /// desired width 53 | /// desired height 54 | void SetDimensions(int w, int h); 55 | 56 | /// 57 | /// Moves the camera to the coordinates 58 | /// 59 | /// desired location 60 | void SetPosition(glm::vec3 new_loc); 61 | 62 | /// 63 | /// Set the pitch of a camera 64 | /// 65 | /// pitch in degrees 66 | void SetPitch(float new_pitch_degrees); 67 | 68 | /// 69 | /// Set the yaw of a camera 70 | /// 71 | /// yaw in degrees 72 | void SetYaw(float new_yaw_degrees); 73 | 74 | /// 75 | /// Moves the camera to an offset of its current location 76 | /// 77 | /// offset to apply 78 | void ShiftPosition(glm::vec3 offset); 79 | 80 | /// 81 | /// Rotates the Camera by an offset. 82 | /// 83 | /// 84 | /// 85 | void ShiftPitchAndYaw(double pitch_offset, double yaw_offset); 86 | 87 | /// 88 | /// set to true to make the camera auto-snap its viewport to the size of the window 89 | /// 90 | /// true or false 91 | void SetKeepCameraToWindowSize(bool tf); 92 | 93 | /// 94 | /// Sets the render order. 95 | /// 96 | /// depth of the cam 97 | void SetRenderDepth(int new_depth); 98 | 99 | /// 100 | /// Sets a skybox with 6 textures 101 | /// 102 | /// 6 textures 103 | /// true is images have and alpha channel, false otherwise 104 | void SetSkybox(std::vector incomingSkymapFiles) noexcept; 105 | 106 | /// 107 | /// Removes current skybox. You will see the clear screen color instead. 108 | /// Call SetSkybox with new parameters to set up the skybox again. 109 | /// 110 | void RemoveSkybox() noexcept; 111 | 112 | [[nodiscard]] const Skybox* GetSkybox() const; 113 | 114 | /// 115 | /// gets the render order number 116 | /// 117 | /// render depth variable 118 | const int GetRenderDepth() const; 119 | 120 | const float GetRenderDistance() const; 121 | 122 | /// 123 | /// Gets the snapping current option 124 | /// 125 | /// true or false 126 | const bool GetIsAlwaysScreenSize() const; 127 | 128 | /// 129 | /// Gets a copy of the front vec3 130 | /// 131 | /// front vec3 copy 132 | const glm::vec3 GetFront() const; 133 | 134 | /// 135 | /// Gets a copy of the right vec3 136 | /// 137 | /// right vec3 copy 138 | const glm::vec3 GetRight() const; 139 | 140 | /// 141 | /// Gets a copy of the cam position vec3 142 | /// 143 | /// location vec3 copy 144 | const glm::vec3 GetPosition() const; 145 | 146 | /// 147 | /// Get a copy of the cam pitch. 148 | /// 149 | /// camera pitch copy 150 | const float GetPitch() const; 151 | 152 | /// 153 | /// Get a copy of the camera yaw 154 | /// 155 | /// camera yaw copy 156 | const float GetYaw() const; 157 | 158 | /// 159 | /// Get a vec2 of the pitch and yaw. 160 | /// 161 | /// vec2 pitch,yaw copy 162 | const glm::vec2 GetPitchAndYaw() const; 163 | 164 | /// 165 | /// Get the projection matrix of a camera 166 | /// 167 | /// mat4 copy of the projection 168 | const glm::mat4 GetProjectionMatrix() const; 169 | 170 | /// 171 | /// Get the view matrix of a camera 172 | /// 173 | /// mat4 copy of the view 174 | const glm::mat4 GetViewMatrix() const; 175 | 176 | const Viewport GetViewport() const; 177 | 178 | /// 179 | /// Sets camera to default init values. 180 | /// 181 | void ResetToDefault(); 182 | 183 | /// 184 | /// Call this once per frame before using this camera's values for rendering. 185 | /// 186 | void NewFrame(); 187 | 188 | 189 | protected: 190 | Viewport mViewport; 191 | 192 | glm::vec3 Front; 193 | glm::vec3 Right; 194 | glm::vec3 Up; 195 | glm::vec3 Position; 196 | 197 | glm::mat4 mViewMatrix; 198 | 199 | float FOV; 200 | float Yaw; 201 | float Pitch; 202 | float MaxRenderDistance; 203 | 204 | glm::mat4 mProjectionMatrix; 205 | 206 | ProjectionType mProjectionType; 207 | 208 | bool isAlwaysScreenSize; 209 | int RenderDepth; // lower renders first 210 | 211 | std::unique_ptr mSkybox; 212 | 213 | private: 214 | 215 | bool camera_projection_changed; 216 | void update_cached_projection_matrix(); 217 | 218 | bool camera_vectors_changed; 219 | void update_camera_vectors_tick(); 220 | void update_cached_view_matrix(); 221 | 222 | }; 223 | } // end namespace AA 224 | -------------------------------------------------------------------------------- /Foundation/src/Scene/Camera.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../OS/OpenGL/InternalShaders/Skycube.h" 3 | #include "../OS/OpenGL/InternalShaders/Stencil.h" 4 | #include "../OS/OpenGL/InternalShaders/Uber.h" 5 | #include "../OS/OpenGL/InternalShaders/Basic.h" 6 | #include 7 | #include 8 | 9 | namespace AA { 10 | 11 | #define UP glm::vec3(0,1,0) 12 | 13 | Camera::Camera(int width, int height) { 14 | ResetToDefault(); 15 | if (width == 0 || height == 0) { 16 | SetKeepCameraToWindowSize(true); 17 | } 18 | mViewport.Width = width; 19 | mViewport.Height = height; 20 | static int LastRenderDepth = 0; 21 | RenderDepth = LastRenderDepth++; 22 | } 23 | 24 | Camera::~Camera() { 25 | if (mSkybox) 26 | mSkybox.reset(); 27 | } 28 | 29 | void Camera::ProjectionChanged() { 30 | camera_projection_changed = true; 31 | } 32 | 33 | void Camera::SetBottomLeft(const int& x, const int& y) { 34 | mViewport.BottomLeft[0] = x; 35 | mViewport.BottomLeft[1] = y; 36 | } 37 | 38 | void Camera::SetMaxRenderDistance(const float& amt) { 39 | if (amt < 0.f) 40 | MaxRenderDistance = abs(amt); 41 | else 42 | MaxRenderDistance = amt; 43 | camera_projection_changed = true; 44 | } 45 | 46 | void Camera::SetToPerspective() { 47 | mProjectionType = ProjectionType::PERSPECTIVE; 48 | camera_projection_changed = true; 49 | } 50 | 51 | void Camera::SetToOrtho_testing() { 52 | mProjectionType = ProjectionType::ORTHO; 53 | camera_projection_changed = true; 54 | } 55 | 56 | void Camera::SetFOV(float new_fov) { 57 | if (new_fov < 1.f) new_fov = 1.f; 58 | if (new_fov > 360.f) new_fov = 360.f; 59 | FOV = new_fov; 60 | camera_projection_changed = true; 61 | } 62 | 63 | void Camera::SetDimensions(int w, int h) { 64 | if (w < 1 || h < 1) 65 | return; 66 | mViewport.Width = w; 67 | mViewport.Height = h; 68 | camera_projection_changed = true; 69 | } 70 | 71 | void Camera::SetPosition(glm::vec3 new_loc) { 72 | Position = new_loc; 73 | camera_vectors_changed = true; 74 | } 75 | 76 | void Camera::SetPitch(float new_pitch_degrees) { 77 | if (new_pitch_degrees > 89.9f) 78 | new_pitch_degrees = 89.9f; 79 | else if (new_pitch_degrees < -89.9f) 80 | new_pitch_degrees = -89.9f; 81 | Pitch = new_pitch_degrees; 82 | camera_vectors_changed = true; 83 | } 84 | 85 | void Camera::SetYaw(float new_yaw_degrees) { 86 | if (new_yaw_degrees > 360.0f) 87 | new_yaw_degrees -= 360.f; 88 | else if (new_yaw_degrees < 0.f) 89 | new_yaw_degrees += 360.f; 90 | Yaw = new_yaw_degrees; 91 | camera_vectors_changed = true; 92 | } 93 | 94 | void Camera::ShiftPosition(glm::vec3 offset) { 95 | Position += offset; 96 | camera_vectors_changed = true; 97 | } 98 | 99 | void Camera::ShiftPitchAndYaw(double pitch_offset_degrees, double yaw_offset_degrees) { 100 | double new_pitch_degrees = Pitch + pitch_offset_degrees; 101 | if (new_pitch_degrees > 89.9f) 102 | new_pitch_degrees = 89.9f; 103 | else if (new_pitch_degrees < -89.9f) 104 | new_pitch_degrees = -89.9f; 105 | Pitch = static_cast(new_pitch_degrees); 106 | 107 | double new_yaw_degrees = Yaw + yaw_offset_degrees; 108 | if (new_yaw_degrees > 360.0f) 109 | new_yaw_degrees -= 360.f; 110 | else if (new_yaw_degrees < 0.f) 111 | new_yaw_degrees += 360.f; 112 | Yaw = static_cast(new_yaw_degrees); 113 | 114 | camera_vectors_changed = true; 115 | } 116 | 117 | void Camera::SetKeepCameraToWindowSize(bool tf) { 118 | isAlwaysScreenSize = tf; 119 | } 120 | 121 | void Camera::SetRenderDepth(int new_depth) { 122 | RenderDepth = new_depth; 123 | } 124 | 125 | void Camera::SetSkybox(std::vector incomingSkymapFiles) noexcept { 126 | if (incomingSkymapFiles.size() != 6) 127 | return; // invalid size for a skybox 128 | 129 | if (mSkybox) RemoveSkybox(); 130 | 131 | mSkybox = std::make_unique(incomingSkymapFiles); 132 | camera_projection_changed = true; // to update the skybox shader 133 | } 134 | 135 | void Camera::RemoveSkybox() noexcept { 136 | if (mSkybox) 137 | mSkybox.reset(); 138 | } 139 | 140 | const Skybox* Camera::GetSkybox() const { 141 | return mSkybox.get(); 142 | } 143 | 144 | const int Camera::GetRenderDepth() const { 145 | return RenderDepth; 146 | } 147 | 148 | const float Camera::GetRenderDistance() const { 149 | return MaxRenderDistance; 150 | } 151 | 152 | const bool Camera::GetIsAlwaysScreenSize() const { 153 | return isAlwaysScreenSize; 154 | } 155 | 156 | const glm::vec3 Camera::GetFront() const { 157 | return Front; 158 | } 159 | 160 | const glm::vec3 Camera::GetRight() const { 161 | return Right; 162 | } 163 | 164 | const glm::vec3 Camera::GetPosition() const { 165 | return Position; 166 | } 167 | 168 | const float Camera::GetPitch() const { 169 | return Pitch; 170 | } 171 | 172 | const float Camera::GetYaw() const { 173 | return Yaw; 174 | } 175 | 176 | const glm::vec2 Camera::GetPitchAndYaw() const { 177 | return glm::vec2(Pitch, Yaw); 178 | } 179 | 180 | const glm::mat4 Camera::GetProjectionMatrix() const { 181 | return mProjectionMatrix; 182 | } 183 | 184 | const glm::mat4 Camera::GetViewMatrix() const { 185 | return mViewMatrix; 186 | } 187 | 188 | const Viewport Camera::GetViewport() const { 189 | return mViewport; 190 | } 191 | 192 | void Camera::ResetToDefault() { 193 | mViewport.BottomLeft[0] = mViewport.BottomLeft[1] = 0; 194 | Position = glm::vec3(0.f); 195 | FOV = 45.f; 196 | Yaw = -90.f; // look down the -z axis 197 | Pitch = 0.f; 198 | MaxRenderDistance = 3000.f; 199 | mProjectionType = ProjectionType::PERSPECTIVE; 200 | isAlwaysScreenSize = false; 201 | camera_vectors_changed = true; 202 | camera_projection_changed = true; 203 | } 204 | 205 | void Camera::NewFrame() { 206 | if (camera_vectors_changed) { 207 | update_camera_vectors_tick(); 208 | if (InternalShaders::Uber::IsActive()) { 209 | InternalShaders::Uber::Get()->SetVec3("u_view_pos", Position); 210 | InternalShaders::Uber::Get()->SetMat4("u_view_matrix", mViewMatrix); 211 | } 212 | if (InternalShaders::Stencil::IsActive()) InternalShaders::Stencil::Get()->SetMat4("u_view_matrix", mViewMatrix); 213 | if (InternalShaders::Skycube::IsActive()) InternalShaders::Skycube::Get()->SetMat4("u_view_matrix", glm::mat4(glm::mat3(mViewMatrix))); 214 | if (InternalShaders::Basic::IsActive()) InternalShaders::Basic::Get()->SetMat4("u_view_matrix", glm::mat4(mViewMatrix)); 215 | camera_vectors_changed = false; 216 | } 217 | if (camera_projection_changed) { 218 | update_cached_projection_matrix(); 219 | if (InternalShaders::Uber::IsActive()) InternalShaders::Uber::Get()->SetMat4("u_projection_matrix", mProjectionMatrix); 220 | if (InternalShaders::Stencil::IsActive()) InternalShaders::Stencil::Get()->SetMat4("u_projection_matrix", mProjectionMatrix); 221 | if (InternalShaders::Skycube::IsActive()) InternalShaders::Skycube::Get()->SetMat4("u_projection_matrix", mProjectionMatrix); 222 | if (InternalShaders::Basic::IsActive()) InternalShaders::Basic::Get()->SetMat4("u_projection_matrix", mProjectionMatrix); 223 | camera_projection_changed = false; 224 | } 225 | } 226 | 227 | void Camera::update_camera_vectors_tick() { 228 | glm::vec3 front{}; 229 | front.x = cos(glm::radians(Yaw)) * cos(glm::radians(Pitch)); 230 | front.y = sin(glm::radians(Pitch)); 231 | front.z = sin(glm::radians(Yaw)) * cos(glm::radians(Pitch)); 232 | Front = glm::normalize(front); 233 | Right = glm::normalize(glm::cross(Front, UP)); 234 | Up = glm::normalize(glm::cross(Right, Front)); 235 | update_cached_view_matrix(); 236 | } 237 | 238 | // updates the cached projection 239 | void Camera::update_cached_projection_matrix() { 240 | float w = static_cast(mViewport.Width); 241 | float h = static_cast(mViewport.Height); 242 | if (w < 1.f || h < 1.f) 243 | return; 244 | switch (mProjectionType) { 245 | case ProjectionType::PERSPECTIVE: 246 | { 247 | float aspectRatio = w / h; 248 | mProjectionMatrix = glm::perspective(glm::radians(FOV), aspectRatio, 0.0167f, MaxRenderDistance); 249 | } 250 | break; 251 | case ProjectionType::ORTHO: 252 | { 253 | // we are going to use an aspect corrected view 254 | float aspect_ratio = w * h; 255 | float ortho_height = h / 2.f; // or ortho size 256 | float ortho_width = w * ortho_height; 257 | mProjectionMatrix = glm::ortho( 258 | -ortho_width, 259 | ortho_width, 260 | -ortho_height, 261 | ortho_height, 262 | 0.0167f, 263 | MaxRenderDistance 264 | ); 265 | } 266 | break; 267 | default: 268 | break; 269 | } 270 | } 271 | 272 | // update cached view matrix 273 | // you need to update the shader somewhere else 274 | void Camera::update_cached_view_matrix() { 275 | mViewMatrix = glm::lookAt(Position, Position + Front, Up); 276 | } 277 | 278 | } // end namespace AA 279 | -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/Loaders/TextureLoader.cpp: -------------------------------------------------------------------------------- 1 | #include "AssimpSceneLoader.h" 2 | #include "../Graphics.h" 3 | #include 4 | #include 5 | #include 6 | 7 | #define STB_IMAGE_IMPLEMENTATION 8 | 9 | // png, jpegs, and bmp only 10 | 11 | //#define STBI_NO_JPEG 12 | //#define STBI_NO_PNG 13 | //#define STBI_NO_BMP 14 | #define STBI_NO_PSD 15 | //#define STBI_NO_TGA 16 | #define STBI_NO_GIF 17 | #define STBI_NO_HDR 18 | #define STBI_NO_PIC 19 | #define STBI_NO_PNM (.ppm and .pgm) 20 | 21 | #define STBI_WINDOWS_UTF8 22 | 23 | #define STBI_NO_FAILURE_STRINGS 24 | 25 | //#define STBI_ONLY_PNG 26 | 27 | //#define STBI_ASSERT(x) 28 | 29 | //#define STBI_MALLOC malloc 30 | //#define STBI_REALLOC realloc 31 | //#define STBI_FREE free 32 | 33 | #ifdef __linux__ 34 | #include 35 | #elif _WIN32 36 | #include 37 | #endif 38 | 39 | namespace AA { 40 | 41 | inline int helper_get_ogl_of_stb_internal_format(int stbi_internal_format) { 42 | int ogl_internal_format(0); 43 | switch (stbi_internal_format) { 44 | case 1: 45 | ogl_internal_format = GL_RED; 46 | break; 47 | case 2: 48 | ogl_internal_format = GL_RG; 49 | break; 50 | case 3: 51 | ogl_internal_format = GL_RGB; 52 | break; 53 | case 4: 54 | ogl_internal_format = GL_RGBA; 55 | break; 56 | } 57 | return ogl_internal_format; 58 | } 59 | 60 | // caches textures that are loaded 61 | static int load_textures_from_scene( 62 | const aiScene* scn, 63 | const aiMaterial* mat, 64 | aiTextureType textureType, 65 | TextureType typeName, 66 | std::string orginalFilePath, 67 | TextureMapType& out_texInfo) { 68 | 69 | unsigned int num_textures = mat->GetTextureCount(textureType); 70 | if (num_textures == 0) { return -3; } 71 | 72 | int width(0), height(0), nrComponents(0); 73 | 74 | for (unsigned int i = 0; i < num_textures; ++i) { 75 | // make sure texture exists 76 | aiString aiTmpStr; 77 | auto tex_success = mat->GetTexture(textureType, i, &aiTmpStr); 78 | switch (tex_success) { 79 | case aiReturn_SUCCESS: 80 | break; 81 | case aiReturn_FAILURE: 82 | return -1; 83 | break; 84 | case aiReturn_OUTOFMEMORY: 85 | return -2; 86 | break; 87 | } 88 | 89 | // store the uv map mode 90 | aiTextureMapMode enum_map_mode{}; 91 | if (AI_SUCCESS != mat->Get(AI_MATKEY_MAPPING(textureType, i), enum_map_mode)) { 92 | // no mapping mode - probably default 93 | } 94 | 95 | // pathing options we use later to help finding textures based on model path 96 | const std::size_t the_last_slash = orginalFilePath.find_last_of("/\\") + 1; 97 | const std::size_t the_last_dot = orginalFilePath.find_last_of("."); 98 | const std::string model_dir = orginalFilePath.substr(0, the_last_slash); // path to filename's dir 99 | const std::string model_file_extension = orginalFilePath.substr(the_last_dot + 1u); // get the file extension (textureType of file) 100 | const std::string model_file_name = orginalFilePath.substr(the_last_slash, the_last_dot - the_last_slash); // get the name of the file 101 | 102 | // Begin: attempts at loading textures from embedded or path 103 | // Method 1. try from embedded 104 | const aiTexture* ai_embedded_texture = scn->GetEmbeddedTexture(aiTmpStr.C_Str()); 105 | // when this happens we don't need to load it from a texture path, but from the memory 106 | if (ai_embedded_texture) { 107 | std::string embedded_filename = ai_embedded_texture->mFilename.C_Str(); 108 | 109 | // todo: fix path for glb (and fbx?) embedded textures and not having to reload them, as path is often our reuse already loaded key 110 | // todo: multiple textres of the same type may be using the same typename 111 | if (embedded_filename == "") { embedded_filename = model_file_name + "." + model_file_extension + "/" + toString(typeName); } 112 | 113 | // see if it has already been loaded previously to reuse 114 | auto reused_tex_info = Cache::Get()->try_load_from_cache(out_texInfo, embedded_filename); 115 | 116 | // not already loaded, lets try from embedded, this should succeed if it gets here 117 | if (reused_tex_info == 0) { 118 | Texture2DInfo a_new_texture_info; 119 | //bool compressed = (ai_embedded_texture->mHeight == 0) ? true : false; 120 | // for unflipped opengl coords, flip vertically on load to true 121 | stbi_set_flip_vertically_on_load(0); 122 | int texture_size = ai_embedded_texture->mWidth * std::max(ai_embedded_texture->mHeight, 1u); 123 | unsigned char* data = stbi_load_from_memory(reinterpret_cast(ai_embedded_texture->pcData), texture_size, &width, &height, &nrComponents, 0); 124 | if (data) { 125 | int format = helper_get_ogl_of_stb_internal_format(nrComponents); 126 | a_new_texture_info.accessId = OpenGL::GetGL()->Upload2DTex(data, width, height, format, enum_map_mode); 127 | if (a_new_texture_info.accessId != 0) { 128 | // add the new one to our list of loaded textures 129 | a_new_texture_info.path = embedded_filename; 130 | a_new_texture_info.textureType = typeName; 131 | // update our list of loaded textures 132 | Cache::Get()->add(a_new_texture_info); 133 | // to return for draw info on this current mesh 134 | out_texInfo.emplace_back(a_new_texture_info); 135 | } 136 | } 137 | stbi_image_free(data); 138 | } 139 | } 140 | // ELSE: textures are not embedded 141 | // try from 3 most likely paths as detailed below 142 | // regular file, check if it exists and read it 143 | // the 3 paths to try 144 | else { 145 | std::vector potential_paths; 146 | // 1. the direct string (will probably fail) 147 | potential_paths.emplace_back(aiTmpStr.C_Str()); 148 | // 2. the path based on where the model was loaded from (might work) 149 | std::string literal_path = model_dir + aiTmpStr.C_Str(); 150 | potential_paths.emplace_back(literal_path); 151 | // 3. the last part of the given path (after '/' or '\\') appended to the path based on were the model was loaded from 152 | std::string from_model_path = model_dir + literal_path.substr(literal_path.find_last_of("/\\") + 1); // all the way to the end 153 | potential_paths.emplace_back(from_model_path); 154 | // routine to see if we already have this texture loaded 155 | 156 | for (const auto& a_path : potential_paths) { 157 | auto already_loaded = Cache::Get()->try_load_from_cache(out_texInfo, a_path); 158 | if (already_loaded == 0) 159 | continue; 160 | else 161 | return 0; // success 162 | } 163 | 164 | // wasn't already loaded, lets try to load it 165 | Texture2DInfo a_new_texture_info; 166 | // for unflipped opengl coords, flip vertically on load to true 167 | stbi_set_flip_vertically_on_load(0); 168 | // try 169 | unsigned char* data = nullptr; 170 | for (const auto& a_path : potential_paths) { 171 | data = stbi_load(a_path.c_str(), &width, &height, &nrComponents, 0); 172 | if (data) { 173 | // we have data that goes to the graphics card 174 | int format = helper_get_ogl_of_stb_internal_format(nrComponents); 175 | a_new_texture_info.accessId = OpenGL::GetGL()->Upload2DTex(data, width, height, format, enum_map_mode); 176 | if (a_new_texture_info.accessId != 0) { 177 | // add the new one to our list of loaded textures for management 178 | a_new_texture_info.path = a_path; 179 | a_new_texture_info.textureType = typeName; 180 | Cache::Get()->add(a_new_texture_info); 181 | // to return for draw info on this current mesh 182 | out_texInfo.emplace_back(a_new_texture_info); 183 | break; // break out of for loop 184 | } 185 | } 186 | } 187 | stbi_image_free(data); 188 | } 189 | } 190 | 191 | // went through the above loop without error, AllLoadedTextures & out_texInfo should be updated 192 | return 0; 193 | } 194 | 195 | TextureMapType AssimpSceneLoader::LoadAllTextures(const aiScene* scene, const aiMaterial* ai_material, const std::string& orig_filepath) { 196 | TextureMapType all_loaded_textures; 197 | 198 | // get the albedo (diffuse) textures 199 | TextureMapType albedo_textures; 200 | if (load_textures_from_scene(scene, ai_material, aiTextureType_DIFFUSE, TextureType::ALBEDO, orig_filepath, albedo_textures) == 0) { 201 | for (auto& a_tex : albedo_textures) { 202 | all_loaded_textures.insert(all_loaded_textures.end(), a_tex); 203 | } 204 | } 205 | 206 | TextureMapType specular_textures; 207 | if (load_textures_from_scene(scene, ai_material, aiTextureType_SPECULAR, TextureType::SPECULAR, orig_filepath, specular_textures) == 0) { 208 | for (auto& s_tex : specular_textures) { 209 | all_loaded_textures.insert(all_loaded_textures.end(), s_tex); 210 | } 211 | } 212 | 213 | TextureMapType normal_textures; 214 | if (load_textures_from_scene(scene, ai_material, aiTextureType_NORMALS, TextureType::NORMAL, orig_filepath, normal_textures) == 0) { 215 | for (auto& n_tex : normal_textures) { 216 | all_loaded_textures.insert(all_loaded_textures.end(), n_tex); 217 | } 218 | } 219 | 220 | // if finding normal textures failed, see if it is actually called a heightmap (.obj files soemtimes have this according to https://learnopengl.com/Advanced-Lighting/Normal-Mapping) 221 | if (normal_textures.empty()) { 222 | if (load_textures_from_scene(scene, ai_material, aiTextureType_HEIGHT, TextureType::NORMAL, orig_filepath, normal_textures) == 0) { 223 | for (auto& n_tex : normal_textures) { 224 | all_loaded_textures.insert(all_loaded_textures.end(), n_tex); 225 | } 226 | } 227 | } 228 | 229 | // emissive textures will glow on the lit shader 230 | TextureMapType emissive_textures; 231 | if (load_textures_from_scene(scene, ai_material, aiTextureType_EMISSIVE, TextureType::EMISSION, orig_filepath, emissive_textures) == 0) { 232 | for (auto& e_tex : emissive_textures) { 233 | all_loaded_textures.insert(all_loaded_textures.end(), e_tex); 234 | } 235 | } 236 | 237 | return all_loaded_textures; 238 | } 239 | 240 | unsigned int AssimpSceneLoader::LoadCubeMapTexture(const std::vector& six_texture_paths) { 241 | if (six_texture_paths.size() != 6) 242 | throw("wrong number of textures"); 243 | int width(0), height(0), nrChannel(0); 244 | unsigned int return_id(0u); 245 | std::vector data; 246 | data.resize(6); 247 | stbi_set_flip_vertically_on_load(0); // tell stb_image.h to not flip loaded texture's on the y-axis. 248 | for (auto i = 0; i < 6; ++i) { 249 | data[i] = stbi_load(six_texture_paths[i].c_str(), &width, &height, &nrChannel, 0); 250 | } 251 | if (data[0] && data[5]) { // ensure first and last data pics are there (middle not checked but assumed) 252 | int format = helper_get_ogl_of_stb_internal_format(nrChannel); 253 | return_id = OpenGL::GetGL()->UploadCubeMapTex(data, width, height, format); 254 | } 255 | for (auto i = 0; i < 6; ++i) { 256 | stbi_image_free(data[i]); 257 | } 258 | return return_id; 259 | } 260 | 261 | } // end namespace AA 262 | -------------------------------------------------------------------------------- /Foundation/src/OS/OpenGL/InternalShaders/Uber.cpp: -------------------------------------------------------------------------------- 1 | #include "Uber.h" 2 | #include 3 | namespace AA::InternalShaders { 4 | 5 | static OGLShader* UBERSHADER = nullptr; 6 | 7 | //todo: optimization - uniform buffers https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL 8 | void Uber::Init() { 9 | if (UBERSHADER) 10 | return; 11 | 12 | const std::string UBERSHADER_VERT_CODE = R"( 13 | #version 460 core 14 | 15 | layout(location=0)in vec3 inPos; 16 | layout(location=1)in vec2 inTexUV; 17 | layout(location=2)in vec3 inNorm; 18 | layout(location=3)in ivec4 inBoneIds; 19 | layout(location=4)in vec4 inWeights; 20 | 21 | out VS_OUT { 22 | vec3 Pos; 23 | vec2 TexUV; 24 | vec3 Norm; 25 | } vs_out; 26 | 27 | // must match in animation code 28 | const int MAX_BONES = 100; 29 | const int MAX_BONE_INFLUENCE = 4; 30 | 31 | uniform mat4 u_projection_matrix; 32 | uniform mat4 u_view_matrix; 33 | uniform mat4 u_model_matrix; 34 | 35 | uniform mat4 u_final_bone_mats[MAX_BONES]; 36 | uniform int u_is_animating; 37 | 38 | void main(){ 39 | vs_out.TexUV = inTexUV; 40 | vec4 totalPosition = vec4(0.0); 41 | 42 | if (u_is_animating > 0) { 43 | for(int i = 0 ; i < MAX_BONE_INFLUENCE ; i++) { 44 | if(inBoneIds[i] == -1) continue; 45 | if(inBoneIds[i] >= MAX_BONES) { 46 | totalPosition = vec4(inPos, 1.0f); 47 | break; 48 | } 49 | vec4 localPosition = u_final_bone_mats[inBoneIds[i]] * vec4(inPos, 1.0); 50 | totalPosition += localPosition * inWeights[i]; 51 | } 52 | vs_out.Pos = (u_model_matrix * totalPosition).xyz; 53 | } else { // Not Animating 54 | vs_out.Pos = (u_model_matrix * vec4(inPos, 1.0)).xyz; 55 | totalPosition = vec4(inPos, 1.0); 56 | } 57 | 58 | mat3 normalMatrix = transpose(inverse(mat3(u_model_matrix))); 59 | vs_out.Norm = normalize(normalMatrix * inNorm); 60 | 61 | mat4 viewMatrix = u_view_matrix * u_model_matrix; 62 | gl_Position = u_projection_matrix * viewMatrix * totalPosition; 63 | } 64 | )"; 65 | 66 | const std::string UBERSHADER_FRAG_CODE = R"( 67 | #version 460 core 68 | layout(location=0)out vec4 out_Color; 69 | 70 | in VS_OUT { 71 | vec3 Pos; 72 | vec2 TexUV; 73 | vec3 Norm; 74 | } fs_in; 75 | 76 | struct Material { 77 | int has_albedo_tex; 78 | sampler2D Albedo; 79 | 80 | int has_specular_tex; 81 | sampler2D Specular; 82 | 83 | int has_normal_tex; 84 | sampler2D Normal; 85 | 86 | int has_emission_tex; 87 | sampler2D Emission; 88 | 89 | vec3 Tint; 90 | vec3 Ambient; 91 | vec3 SpecularColor; 92 | vec3 EmissionColor; 93 | 94 | float Shininess; 95 | }; 96 | 97 | struct DirectionalLight { 98 | vec3 Direction; 99 | vec3 Ambient; 100 | vec3 Diffuse; 101 | vec3 Specular; 102 | int Shadows; // true or false 103 | float ShadowBiasMin, ShadowBiasMax; 104 | }; 105 | 106 | struct PointLight { 107 | vec3 Position; 108 | float Constant, Linear, Quadratic; 109 | vec3 Ambient, Diffuse, Specular; 110 | }; 111 | 112 | struct SpotLight { 113 | vec3 Position, Direction; 114 | float CutOff, OuterCutOff; 115 | float Constant, Linear, Quadratic; 116 | vec3 Ambient, Diffuse, Specular; 117 | }; 118 | 119 | struct ReflectionModel { 120 | bool Phong; 121 | bool BlinnPhong; 122 | }; 123 | 124 | uniform int u_is_dir_light_on; 125 | uniform int u_mesh_does_shadow; 126 | 127 | uniform Material u_material; // textures 0 to 3 128 | uniform sampler2D u_shadow_map; // texture 4 129 | 130 | uniform ReflectionModel u_reflection_model; 131 | uniform vec3 u_view_pos; 132 | uniform mat4 u_light_space_matrix; 133 | 134 | uniform DirectionalLight u_dir_light; 135 | 136 | const int MAXPOINTLIGHTS = 24; // if changed, needs to match on light controllers 137 | uniform int u_num_point_lights_in_use; 138 | uniform PointLight u_point_lights[MAXPOINTLIGHTS]; 139 | 140 | const int MAXSPOTLIGHTS = 12; 141 | uniform SpotLight u_spot_lights[MAXSPOTLIGHTS]; 142 | uniform int u_num_spot_lights_in_use; 143 | 144 | vec3 CalculateDirLight(vec3 normal, vec3 viewDir); 145 | vec3 CalculatePointLights(PointLight light, vec3 normal, vec3 viewDir); 146 | vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 viewDir); 147 | 148 | //const vec3 DEFAULTCOLOR = vec3(1.0, 0.0, 0.0); 149 | 150 | void main() { 151 | vec3 normal; 152 | if (u_material.has_normal_tex > 0) { 153 | normal = texture(u_material.Normal, fs_in.TexUV).rgb; 154 | normal = normalize(normal * 2.0 - 1.0); 155 | } else { 156 | //normal = normalize(fs_in.Norm * 2.0 - 1.0); 157 | normal = fs_in.Norm; 158 | } 159 | vec3 view_dir = normalize(u_view_pos - fs_in.Pos); 160 | vec3 result; 161 | if (u_is_dir_light_on > 0) { result += CalculateDirLight(normal, view_dir); } 162 | int i = 0; 163 | for (i; i < u_num_point_lights_in_use; i++) { result += CalculatePointLights(u_point_lights[i], normal, view_dir); } 164 | for (i = 0; i < u_num_spot_lights_in_use; i++) { result += CalcSpotLight(u_spot_lights[i], normal, view_dir); } 165 | if (u_material.has_emission_tex > 0) { 166 | vec3 emission = texture(u_material.Emission, fs_in.TexUV).rgb; 167 | result += emission; 168 | } else { 169 | result += u_material.EmissionColor; 170 | } 171 | out_Color = vec4(result, 1.0); 172 | } 173 | 174 | // Light Calculations 175 | vec3 CalculateDirLight(vec3 normal, vec3 viewDir) { 176 | vec3 lightDir = normalize(-u_dir_light.Direction); 177 | // diffuse shading 178 | float diff = max(dot(normal, lightDir), 0.); 179 | float dotLightNormal = dot(-u_dir_light.Direction, normal); 180 | 181 | // shadows 182 | float shadow = 0.0; 183 | if (u_dir_light.Shadows > 0 && u_mesh_does_shadow > 0) { 184 | vec4 FragPosLightSpace = u_light_space_matrix * vec4(fs_in.Pos, 1.0); 185 | vec3 projCoords = FragPosLightSpace.xyz / FragPosLightSpace.w; 186 | projCoords = projCoords * 0.5 + 0.5; 187 | float closestDepth = texture(u_shadow_map, projCoords.xy).r; 188 | float currentDepth = projCoords.z; 189 | vec3 normal = normalize(fs_in.Norm); 190 | float bias = max(u_dir_light.ShadowBiasMax * (1.0 - dot(normal, lightDir)), u_dir_light.ShadowBiasMin); 191 | vec2 texelSize = 1.0 / textureSize(u_shadow_map, 0); 192 | for(int x = -1; x <= 1; ++x) { 193 | for(int y = -1; y <= 1; ++y) { 194 | float pcfDepth = texture(u_shadow_map, projCoords.xy + vec2(x, y) * texelSize).r; 195 | shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; 196 | } 197 | } 198 | shadow /= 9.0; 199 | if(projCoords.z > 1.0) { 200 | shadow = 0.0; 201 | } 202 | } 203 | 204 | // specular shading 205 | float spec; 206 | if (u_reflection_model.BlinnPhong) { 207 | vec3 halfwayDir = normalize(lightDir + viewDir); 208 | spec = pow(max(dot(normal, halfwayDir), 0.0), u_material.Shininess * 4); 209 | } else if (u_reflection_model.Phong) { 210 | vec3 reflectDir = reflect(-lightDir, normal); 211 | spec = pow(max(dot(viewDir, reflectDir), 0.), u_material.Shininess); 212 | } else { 213 | spec = 0.0; 214 | } 215 | 216 | // combine results 217 | vec3 ambient; 218 | vec3 diffuse; 219 | if (u_material.has_albedo_tex > 0) { 220 | ambient = u_dir_light.Ambient * texture(u_material.Albedo, fs_in.TexUV).rgb; 221 | diffuse = u_dir_light.Diffuse * diff * texture(u_material.Albedo, fs_in.TexUV).rgb; 222 | } else { 223 | ambient = u_dir_light.Ambient * mix(u_material.Ambient, u_material.Tint, 0.5); 224 | diffuse = u_dir_light.Diffuse * diff * u_material.Tint; 225 | } 226 | 227 | vec3 specular = vec3(0.0); 228 | if (u_material.has_specular_tex > 0) { 229 | specular = u_dir_light.Specular * spec * texture(u_material.Specular, fs_in.TexUV).r; 230 | } else { 231 | specular = u_dir_light.Specular * spec * u_material.SpecularColor.r; 232 | } 233 | 234 | return (ambient + (1.0 - shadow) * (diffuse + specular)); 235 | } 236 | 237 | vec3 CalculatePointLights(PointLight light, vec3 normal, vec3 viewDir) { 238 | vec3 lightDir = normalize(light.Position - fs_in.Pos); 239 | float diff = max(dot(lightDir, normal), 0.0); 240 | 241 | // specular shading 242 | float spec; 243 | if (u_reflection_model.BlinnPhong) { 244 | vec3 halfwayDir = normalize(lightDir + viewDir); 245 | spec = pow(max(dot(normal, halfwayDir), 0.0), u_material.Shininess * 4); 246 | } else if (u_reflection_model.Phong) { 247 | vec3 reflectDir = reflect(-lightDir, normal); 248 | spec = pow(max(dot(viewDir, reflectDir), 0.), u_material.Shininess); 249 | } else { 250 | spec = 0.0; 251 | } 252 | 253 | float dist = length(light.Position - fs_in.Pos); 254 | float attenuation = 1.0 / (light.Constant + light.Linear * dist + light.Quadratic * (dist * dist)); 255 | vec3 ambient; 256 | vec3 diffuse; 257 | if (u_material.has_albedo_tex > 0) { 258 | ambient = light.Ambient * texture(u_material.Albedo, fs_in.TexUV).rgb; 259 | diffuse = light.Diffuse * diff * texture(u_material.Albedo, fs_in.TexUV).rgb; 260 | } else { 261 | ambient = light.Ambient * mix(u_material.Ambient, u_material.Tint, 0.5); 262 | diffuse = light.Diffuse * diff * u_material.Tint; 263 | } 264 | 265 | vec3 specular = vec3(0.0); 266 | if (u_material.has_specular_tex > 0) { 267 | specular = light.Specular * spec * texture(u_material.Specular, fs_in.TexUV).r; 268 | } else { 269 | specular = light.Specular * spec * u_material.SpecularColor.r; 270 | } 271 | 272 | ambient *= attenuation; 273 | diffuse *= attenuation; 274 | specular *= attenuation; 275 | return (ambient + diffuse + specular); 276 | } 277 | 278 | vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 viewDir) { 279 | vec3 lightDir = normalize(light.Position - fs_in.Pos); 280 | // diffuse shading 281 | float diff = max(dot(normal, lightDir), 0.0); 282 | 283 | // specular shading power 284 | float spec; 285 | if (u_reflection_model.BlinnPhong) { 286 | vec3 halfwayDir = normalize(lightDir + viewDir); 287 | spec = pow(max(dot(normal, halfwayDir), 0.0), u_material.Shininess * 4); 288 | } else if (u_reflection_model.Phong) { 289 | vec3 reflectDir = reflect(-lightDir, normal); 290 | spec = pow(max(dot(viewDir, reflectDir), 0.), u_material.Shininess); 291 | } else { 292 | spec = 0.0; 293 | } 294 | 295 | // attenuation 296 | float dist = length(light.Position - fs_in.Pos); 297 | float attenuation = 1.0 / (light.Constant + light.Linear * dist + light.Quadratic * (dist * dist)); 298 | // cone of light 299 | float theta = dot(lightDir, normalize(-light.Direction)); 300 | float epsilon = light.CutOff - light.OuterCutOff; 301 | float intensity = clamp((theta - light.OuterCutOff) / epsilon, 0.0, 1.0); 302 | // combine results 303 | vec3 ambient; 304 | vec3 diffuse; 305 | if (u_material.has_albedo_tex > 0) { 306 | ambient = light.Ambient * texture(u_material.Albedo, fs_in.TexUV).rgb; 307 | diffuse = light.Diffuse * diff * texture(u_material.Albedo, fs_in.TexUV).rgb; 308 | } else { 309 | ambient = light.Ambient * mix(u_material.Ambient, u_material.Tint, 0.5); 310 | diffuse = light.Diffuse * diff * u_material.Tint; 311 | } 312 | 313 | ambient *= attenuation * intensity; 314 | diffuse *= attenuation * intensity; 315 | 316 | vec3 specular = vec3(0.0); 317 | if (u_material.has_specular_tex > 0) { 318 | specular = light.Specular * spec * texture(u_material.Specular, fs_in.TexUV).r; 319 | specular *= attenuation * intensity; 320 | } else { 321 | specular = light.Specular * spec * u_material.SpecularColor.r; 322 | } 323 | 324 | return (ambient + diffuse + specular); 325 | } 326 | )"; 327 | 328 | UBERSHADER = new OGLShader(UBERSHADER_VERT_CODE.c_str(), UBERSHADER_FRAG_CODE.c_str()); 329 | } 330 | 331 | OGLShader* Uber::Get() { 332 | if (!UBERSHADER) { 333 | Init(); 334 | } else { 335 | UBERSHADER->Use(); 336 | } 337 | return UBERSHADER; 338 | } 339 | 340 | void Uber::Shutdown() { 341 | if (UBERSHADER) { 342 | delete UBERSHADER; 343 | UBERSHADER = nullptr; 344 | } 345 | } 346 | 347 | bool Uber::IsActive() { 348 | return UBERSHADER; 349 | } 350 | 351 | } 352 | --------------------------------------------------------------------------------