{"id":20123083,"url":"https://github.com/stwe/sgogl","last_synced_at":"2025-05-06T16:33:39.894Z","repository":{"id":70780177,"uuid":"191398741","full_name":"stwe/SgOgl","owner":"stwe","description":"A GameEngine library for OpenGL developed for educational purposes.","archived":false,"fork":false,"pushed_at":"2023-02-04T17:59:04.000Z","size":29891,"stargazers_count":21,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-04-09T14:40:39.332Z","etag":null,"topics":["cpp17","deferred-rendering","deferred-shading","entt","game-development","game-engine","gamestate","lightning","lod","lua","normalmap","opengl","quadtree","scripting","sol2","sponza-playground","state-machine","terrain","tessellation","water"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stwe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2019-06-11T15:20:49.000Z","updated_at":"2024-06-03T04:09:26.000Z","dependencies_parsed_at":null,"dependency_job_id":"d6c86045-8241-4410-8781-2961e0ad47e6","html_url":"https://github.com/stwe/SgOgl","commit_stats":{"total_commits":359,"total_committers":13,"mean_commits":"27.615384615384617","dds":0.5766016713091922,"last_synced_commit":"f26959a08a65654dbf417f81810bc7b2a4f828ea"},"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stwe%2FSgOgl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stwe%2FSgOgl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stwe%2FSgOgl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stwe%2FSgOgl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stwe","download_url":"https://codeload.github.com/stwe/SgOgl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252721106,"owners_count":21793752,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp17","deferred-rendering","deferred-shading","entt","game-development","game-engine","gamestate","lightning","lod","lua","normalmap","opengl","quadtree","scripting","sol2","sponza-playground","state-machine","terrain","tessellation","water"],"created_at":"2024-11-13T19:43:21.363Z","updated_at":"2025-05-06T16:33:38.593Z","avatar_url":"https://github.com/stwe.png","language":"C++","readme":"# SgOgl\n\n[![Codacy Badge](https://api.codacy.com/project/badge/Grade/499a4726db5844449126e0101ac1074f)](https://app.codacy.com/app/stwe/SgOgl?utm_source=github.com\u0026utm_medium=referral\u0026utm_content=stwe/SgOgl\u0026utm_campaign=Badge_Grade_Dashboard)\n\n![Terrain](https://github.com/stwe/SgOgl/blob/master/Sandbox/res/devlog/terrainWithTrees.png)\n\n\n[![](http://img.youtube.com/vi/LKAUq4dDoZE/0.jpg)](http://www.youtube.com/watch?v=LKAUq4dDoZE \"SgOgl Sponza Playground with an animated model.\")\n\n\n![Sponza](https://github.com/stwe/SgOgl/blob/master/Sandbox/res/devlog/SponzaLight.png)\n\n\n***\n\n1. [What is does](#1-what-is-does)\n2. [Installing](#2-installing)\n3. [Features](#3-features)\n4. [Getting started](#4-getting-started)\n5. [Create scene objects with Lua](#5-create-scene-objects-with-lua)\n\n***\n\n## 1. What it does\n\nA GameEngine library for OpenGL developed for educational purposes - a hobby project that I program in my free time.\n\nThe original goal was only to create a large terrain with a kind of LOD technique. Over time, elements have been added that can be found in GameEngines. But of course there is still a lot missing to create a real game with this library. However, a scene with appealing lighting can be created in just a few steps. Models can be loaded and a beautiful water surface can be generated. To keep things as simple as possible, all game objects can be configured with Lua.\n\n**You just have to:**\n\n* Create an `Application` and a `State` class. \n* Use a Lua file for the description of all the scene objects (Models, Cameras, Lights etc), which is then loaded with our `State` class. \n\nThere are some examples in the Sandbox:\n\n[Start example](https://github.com/stwe/SgOgl/blob/master/Sandbox/res/scene/start.lua)\n\n[Sponza Scene](https://github.com/stwe/SgOgl/blob/master/Sandbox/res/scene/sponza.lua)\n\n[Water](https://github.com/stwe/SgOgl/blob/master/Sandbox/res/scene/water.lua)\n\n[Terrain](https://github.com/stwe/SgOgl/blob/master/Sandbox/res/scene/terrain.lua)\n\n[Particles](https://github.com/stwe/SgOgl/blob/master/Sandbox/res/scene/particles.lua)\n\n\n## 2. Installing\n\n#### Vs2019 \u0026\u0026 Premake\n\nFirst: Use the Conan Package Manager to install the thirdparty libraries. The project already includes a `conanfile.txt`.\nIt is important that `premake` is specified as a generator in this file.\n\n```txt\n#conanfile.txt\n\n# ...\n\n[generators]\npremake\n```\n\nFor a release build:\n\n```bash\nconan install . -s build_type=Release\n```\n\nOr when need to debug:\n\n```bash\nconan install . -s build_type=Debug\n```\n\nAfter that use Premake5 and the `premake5.lua` file to create the project files.\n\n```bash\npremake5 vs2019\n```\n\n\n#### Gcc \u0026\u0026 CMake\n\nI work with Win10 and MinGW and have created two new Conan profiles.\n\n```txt\n# debug_gcc\n\n[settings]\nos=Windows\nos_build=Windows\narch=x86_64\narch_build=x86_64\ncompiler=gcc\ncompiler.version=9.3\ncompiler.libcxx=libstdc++11\nbuild_type=Debug\n[options]\n[build_requires]\n[env]\n\n```\n\n\n\n```txt\n# release_gcc\n\n[settings]\nos=Windows\nos_build=Windows\narch=x86_64\narch_build=x86_64\ncompiler=gcc\ncompiler.version=9.3\ncompiler.libcxx=libstdc++11\nbuild_type=Release\n[options]\n[build_requires]\n[env]\n```\n\n\nCLion users can now, for example, assign the Conan profiles to the CLion profiles.\nThe Conan plugin does the rest. Before that the generator has to be changed.\n\n\n```txt\n#conanfile.txt\n\n# ...\n\n[generators]\ncmake\n```\n\n\n***It may be necessary to compile the packages. Do this with `--build missing`.***\n\n\n## 3. Features\n\n- Configuration of the scene and all entities with Lua\n- Entity component system from [EnTT](https://github.com/skypjack/entt)\n- Deferred rendering / Forward rendering\n- Advanced Terrain rendering (Quadtree and Tessellation, 16-bit Heightmaps)\n- Water rendering\n- Gui rendering\n- Skydome / Skybox\n- Particle systems\n- Instanced rendering\n- Mouse picking\n- Model file loading\n- Animations loading\n- Different camera types (Third person, First person)\n- Different light types (Ambient, Point, Directional)\n- Normal mapping\n\n## 4. Getting started\n\nThe project already contains a Sandbox and it is a good idea to start with it.\n\nIf you want to create it yourself, do this as follows:\n\nWe need to create a class that inherits from `sg::ogl::Application`. Here we call this class `Sandbox`.\nThe header `SgOgl.h` must be included.\n\n```cpp\n// File: Sandbox.cpp\n\n#include \"SgOgl.h\"\n\nclass Sandbox final : public sg::ogl::Application\n{\npublic:\n    Sandbox() = delete;\n\n    explicit Sandbox(const std::string\u0026 t_configFileName)\n        : Application{ t_configFileName }\n    {\n    }\n\nprotected:\n    void RegisterStates() override\n    {\n        // ...\n    }\n\n    void Init() override\n    {\n        // ...\n    }\n\nprivate:\n\n};\n```\n\nNext, a game state must be created. The class is named `GameState` and inherits from `sg::ogl::state::State`.\n\n```cpp\n// File: GameState.h\n\n#pragma once\n\n#include \"SgOgl.h\"\n\nclass GameState : public sg::ogl::state::State\n{\npublic:\n    using LuaScriptUniquePtr = std::unique_ptr\u003csg::ogl::LuaScript\u003e;\n\n    GameState() = delete;\n\n    explicit GameState(sg::ogl::state::StateStack* t_stateStack)\n        : State{ t_stateStack, \"GameState\" }\n    {\n        Init();\n    }\n\n    bool Input() override;\n    bool Update(double t_dt) override;\n    void Render() override;\n\nprotected:\n\nprivate:\n    LuaScriptUniquePtr m_luaScript;\n\n    void Init();\n};\n\n```\n\nThe cpp file contains nothing special so far.\n\n```cpp\n// File: GameState.cpp\n\n#include \"GameState.h\"\n\nbool GameState::Input()\n{\n    return true;\n}\n\nbool GameState::Update(const double t_dt)\n{\n    return true;\n}\n\nvoid GameState::Render()\n{\n}\n\nvoid GameState::Init()\n{\n}\n```\n\nThe newly created game state must be registered. This is done by the `Sandbox` class in the `RegisterStates()` function.\nIn addition, the game state must be pushed to the state stack. We do this in the `Init()` function of the `Sandbox` class.\nThe `RegisterStates()` and `Init()` functions are called later before the game loop.\n\n```cpp\n// File: Sandbox.cpp\n\n#include \"SgOgl.h\"\n#include \"GameState.h\"\n\nclass Sandbox final : public sg::ogl::Application\n{\npublic:\n    // ...\n\nprotected:\n    void RegisterStates() override\n    {\n        GetStateStack().RegisterState\u003cGameState\u003e(sg::ogl::state::GAME);\n    }\n\n    void Init() override\n    {\n        GetStateStack().PushState(sg::ogl::state::GAME);\n    }\n\nprivate:\n\n};\n```\n\nThe `main()` function of the lib calls `sg::ogl::create_application()` to create an `sg::ogl::Application`.\nIn our case, an instance of the `Sandbox` class should be created.\n\nIn the `Sandbox` class, the header `SgOglEntryPoint.h` must be included as well.\n\n```cpp\n// File: Sandbox.cpp\n\n#include \"SgOgl.h\"\n#include \"SgOglEntryPoint.h\"\n#include \"GameState.h\"\n\nclass Sandbox final : public sg::ogl::Application\n{\npublic:\n    // ...\n\nprotected:\n    // ...\n\nprivate:\n\n};\n\nstd::unique_ptr\u003csg::ogl::Application\u003e sg::ogl::create_application()\n{\n    return std::make_unique\u003cSandbox\u003e(\"res/config/Config.lua\");\n}\n\n```\n\nWe can see that the constructor of the `Application` class or `Sandbox` class takes the path to a config lua-file as an argument.\nThe config file can look like this.\n\nThe `libResFolder` option contains the full path to the library's built-in assets.\n\n```lua\n-- File: Config.lua\n\nlibResFolder = \"E:/Dev/SgOgl/SgOglLib/res\"\n\nwindow = {\n    title = \"Sandbox\",\n    compatibleProfile = false,\n    debugContext = true,\n    antialiasing = true,\n    printFrameRate = true,\n    glMajor = 4,\n    glMinor = 3,\n    fps = 60.0\n}\n\nprojection = {\n    fovDeg = 70.0,\n    width = 1024,\n    height = 768,\n    near = 0.1,\n    far = 10000.0\n}\n```\n\n## 5. Create scene objects with Lua\n\n\nHere is an example:\n\n\n```lua\n------------------\n-- Create Scene --\n------------------\n\nscene = Scene.new(applicationContext)\n\n-----------------------------\n-- Create and add Renderer --\n-----------------------------\n\n-- forward with skybox\n\nSkyboxRenderer.new(4, scene)\nSunRenderer.new(3, scene)\nForwardRenderer.new(2, scene)\nSkeletalModelRenderer.new(1, scene)\nGuiRenderer.new(0, scene)\nTextRenderer.new(0, scene, \"res/font/calibri.ttf\")\n\n-- deferred with skybox\n\n--[[\nDeferredRenderer.new(3, scene)\nSkyboxRenderer.new(2, scene)\nSunRenderer.new(1, scene)\nGuiRenderer.new(0, scene)\n]]\n\n-- forward with skydome\n\n--[[\nSkydomeRenderer.new(4, scene)\nSunRenderer.new(3, scene)\nForwardRenderer.new(2, scene)\nSkeletalModelRenderer.new(1, scene)\nGuiRenderer.new(0, scene)\n]]\n\n-- deferred with skydome\n\n--[[\nDeferredRenderer.new(3, scene)\nSkydomeRenderer.new(2, scene)\nSunRenderer.new(1, scene)\nGuiRenderer.new(0, scene)\nTextRenderer.new(0, scene, \"res/font/calibri.ttf\")\n]]\n\n----------------------------\n-- Create and add Cameras --\n----------------------------\n\nfirstPersonCamera = FirstPersonCamera.new(\"first_person_camera1\", applicationContext, Vec3.new(-375.0, 313.0, -417.0), -147.0, -16.0, scene)\nfirstPersonCamera:SetCameraVelocity(128.0)\nfirstPersonCamera:SetMouseSensitivity(0.025)\n--print(firstPersonCamera:GetCameraVelocity())\n--print(firstPersonCamera:GetMouseSensitivity())\n\nthirdPersonCamera = ThirdPersonCamera.new(\"third_person_camera1\", applicationContext, Vec3.new(1334.0, 820.0, 227.0), scene)\nthirdPersonCamera:SetPlayerRotationY(45.0)\n--print(thirdPersonCamera:GetPlayerRotationY())\n\n------------------\n-- Config Scene --\n------------------\n\nscene:SetCurrentCamera(\"first_person_camera1\")\n--scene:SetCurrentCamera(\"third_person_camera1\")\n\nscene:SetAmbientIntensity(Vec3.new(0.2, 0.2, 0.2))\n\n--------------------\n-- Load resources --\n--------------------\n\nplane = modelManager:GetModel(\"res/primitive/plane1/plane1.obj\")\nsphere = modelManager:GetModel(\"res/primitive/sphere/sphere.obj\")\ndome = modelManager:GetModel(\"res/model/Dome/dome.obj\")\nlamp = modelManager:GetModel(\"res/model/Streetlamp/streetlamp.obj\")\njade = modelManager:GetMaterialByName(\"jade\")\ngold = modelManager:GetMaterialByName(\"gold\")\nsunTextureId = textureManager:LoadTexture(\"res/sun/sun.png\")\nfoodGuiId = textureManager:LoadTexture(\"res/gui/foodIcon.png\")\nhealthGuiId = textureManager:LoadTexture(\"res/gui/healthIcon.png\")\nhero = modelManager:GetSkeletalModel(\"res/model/CastleGuard01/Idle.dae\")\n\na = {}\na[1] = \"res/skybox/sky1/sRight.png\"\na[2] = \"res/skybox/sky1/sLeft.png\"\na[3] = \"res/skybox/sky1/sUp.png\"\na[4] = \"res/skybox/sky1/sDown.png\"\na[5] = \"res/skybox/sky1/sBack.png\"\na[6] = \"res/skybox/sky1/sFront.png\"\n\nskyboxCubemapId = textureManager:GetCubemapId(a)\n\n---------------------\n-- Create Entities --\n---------------------\n\n-- plane\n\nplaneEntity = ecs:CreateEntity()\necs:AddModelComponent(planeEntity, plane, false)\necs:AddTransformComponent(planeEntity, Vec3.new(0.0, 150.0, 0.0), Vec3.new(0.0, 0.0, 0.0), Vec3.new(5000.0, 1.0, 5000.0))\n\n-- hero\n\nheroEntity = ecs:CreateEntity()\necs:AddSkeletalModelComponent(heroEntity, hero, false)\necs:AddTransformComponent(heroEntity, Vec3.new(-500.0, 150.0, -500.0), Vec3.new(0.0, 0.0, 0.0), Vec3.new(1.0, 1.0, 1.0))\n\n-- sphere\n\nsphereEntity = ecs:CreateEntity()\necs:AddModelComponent(sphereEntity, sphere, false)\necs:AddTransformComponent(sphereEntity, Vec3.new(0.0, 240.0, 380.0), Vec3.new(0.0, 0.0, 0.0), Vec3.new(4.0, 4.0, 4.0))\necs:AddMaterialComponent(sphereEntity, gold)\n\n-- skydome\n\nskydomeEntity = ecs:CreateEntity()\necs:AddModelComponent(skydomeEntity, dome, false)\necs:AddTransformComponent(skydomeEntity, Vec3.new(0.0, 0.0, 0.0), Vec3.new(0.0, 0.0, 0.0), Vec3.new(5000.0, 5000.0, 5000.0))\necs:AddSkydomeComponent(skydomeEntity, \"skydome\")\n\n-- a point light\n\nplightEntity0 = ecs:CreateEntity()\necs:AddPointLightComponent(plightEntity0,\n    Vec3.new(30.0, 240.0, 657.0), -- position\n    Vec3.new(0.2, 0.2, 0.2),      -- ambientIntensity\n    Vec3.new(10.0, 1.0, 1.0),     -- diffuseIntensity\n    Vec3.new(1.0, 1.0, 1.0),      -- specularIntensity\n    1.0, 0.0014, 0.000007         -- constant, linear, quadratic\n)\necs:AddUpdateComponent(plightEntity0, \"UpdatePointLight\")\n\n-- another point light\n\nplightEntity1 = ecs:CreateEntity()\necs:AddPointLightComponent(plightEntity1,\n    Vec3.new(204.0, 240.0, -319.0), -- position\n    Vec3.new(0.2, 0.2, 0.2),        -- ambientIntensity\n    Vec3.new(1.0, 1.0, 10.0),       -- diffuseIntensity\n    Vec3.new(1.0, 1.0, 1.0),        -- specularIntensity\n    1.0, 0.0014, 0.000007           -- constant, linear, quadratic\n)\necs:AddUpdateComponent(plightEntity1, \"UpdatePointLight\")\n\n-- the sun\n\n--[[\ndlightEntity = ecs:CreateEntity()\necs:AddDirectionalLightComponent(dlightEntity,\n    Vec3.new(1.0, -0.2, -0.4), -- direction\n    Vec3.new(1.0, 0.8, 0.6),   -- diffuseIntensity\n    Vec3.new(1.0, 0.8, 0.6)    -- specularIntensity\n)\n]]\n\nsunEntity = ecs:CreateEntity()\necs:AddSunComponent(sunEntity,\n    Vec3.new(1.0, -0.2, -0.4), -- direction\n    Vec3.new(1.0, 0.8, 0.6),   -- diffuseIntensity\n    Vec3.new(1.0, 0.8, 0.6),   -- specularIntensity\n    sunTextureId,\n    10.0\n)\n\n-- skybox\n\nskyboxEntity = ecs:CreateEntity()\necs:AddCubemapComponent(skyboxEntity, skyboxCubemapId)\n\n-- gui\n\nfoodEntity = ecs:CreateEntity()\necs:AddGuiComponent(foodEntity, foodGuiId)\necs:AddTransformComponent(foodEntity, Vec3.new(0.9, 0.9, 0.0), Vec3.new(0.0, 0.0, 0.0), Vec3.new(0.031, 0.031, 1.0))\n\n-- another gui\n\nhealthEntity = ecs:CreateEntity()\necs:AddGuiComponent(healthEntity, healthGuiId)\necs:AddTransformComponent(healthEntity, Vec3.new(0.9, 0.8, 0.0), Vec3.new(0.0, 0.0, 0.0), Vec3.new(0.031, 0.031, 1.0))\n\n-- point light with model\n\nplightModelEntity = ecs:CreateEntity()\necs:AddModelComponent(plightModelEntity, lamp, false)\necs:AddTransformComponent(plightModelEntity, Vec3.new(-1400.0, 150.0, 11.0), Vec3.new(0.0, 0.0, 0.0), Vec3.new(40.0, 40.0, 40.0))\necs:AddPointLightComponent(plightModelEntity,\n    Vec3.new(-1400.0, 400.0, 11.0), -- position\n    Vec3.new(0.2, 0.2, 0.2),        -- ambientIntensity\n    Vec3.new(1.0, 1.0, 10.0),       -- diffuseIntensity\n    Vec3.new(1.0, 1.0, 1.0),        -- specularIntensity\n    1.0, 0.0014, 0.000007           -- constant, linear, quadratic\n)\n\n-- text\n\ntextEntity0 = ecs:CreateEntity()\necs:AddTextComponent(textEntity0, \"SgOgl Test\", 40.0, 750.0, 0.35, Vec3.new(0.1, 0.2, 0.2))\n\ntextEntity1 = ecs:CreateEntity()\necs:AddTextComponent(textEntity1, \"Version: dev-master\", 40.0, 730.0, 0.35, Vec3.new(0.1, 0.2, 0.2))\n\n---------------\n-- Functions --\n---------------\n\nval = 0.0\n\nfunction UpdatePointLight(entity, dt)\n    val = val + dt\n    p = ecs:GetPointLightComponent(entity)\n    p.position.x = p.position.x + (math.sin(val) * 4.0)\nend\n```\n\nIn our GameState now a `LuaScript` object has to be created. The script creates a Scene object that can be reached via `GetApplicationContext()-\u003ecurrentScene`.\n\n\n```cpp\n// File: GameState.cpp\n\nbool GameState::Input()\n{\n    if (GetApplicationContext()-\u003ecurrentScene)\n    {\n        GetApplicationContext()-\u003ecurrentScene-\u003eInput();\n    }\n\n    return true;\n}\n\nbool GameState::Update(const double t_dt)\n{\n    if (GetApplicationContext()-\u003ecurrentScene)\n    {\n        GetApplicationContext()-\u003ecurrentScene-\u003eUpdate(t_dt);\n    }\n\n    return true;\n}\n\nvoid GameState::Render()\n{\n    if (GetApplicationContext()-\u003ecurrentScene)\n    {\n        GetApplicationContext()-\u003ecurrentScene-\u003eRender();\n    }\n\n    RenderImGui();\n}\n\nvoid GameState::Init()\n{\n    // ...\n\n    m_luaScript = std::make_unique\u003csg::ogl::LuaScript\u003e(GetApplicationContext(), \"res/scene/start.lua\");\n}\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstwe%2Fsgogl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstwe%2Fsgogl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstwe%2Fsgogl/lists"}