Skip to content

Nero-TheThrill/JinEngine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

148 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Latest release Changelog

JinEngine

A lightweight C++ game engine (sample framework) that bundles 2D rendering, input, sound, text, collision, and state management in one place. Built on OpenGL 4.6, it supports batched rendering and instancing, and handles text via runtime glyph baking (FreeType). It provides a dependency/tag–based resource system so you can pull in only the modules your project needs.

sn

Key Features

  • State transition framework: Separates the lifecycle into Load/Init/Update/Draw/Free/Unload per GameState.
  • Camera & frustum culling: Camera2D coordinate system + automatic exclusion of off–screen objects.
  • Batching & instancing: Groups identical Mesh/Material pairs; instancing based on the i_Model attribute.
  • Runtime text engine: Bakes glyphs on demand via FreeType; supports alignment and multi-line text (font size range 4–64).
  • Input utilities: Compare current/previous key/mouse states, convert between screen/world coordinates, read scroll delta.
  • Collision & broad phase: AABB/Circle colliders + spatial hash grid to minimize pair comparisons.
  • Collision groups/masks: Tag-based collision filtering for selective checks.
  • Sound: miniaudio-based play/pause/stop, instance ID management, tag-level controls.
  • Async resource loading: Background loading with progress tracking via LoadingState.
  • Compute shader support: Dispatch compute shaders for post-processing (e.g., water ripple, glitch).
  • Window utilities: Fullscreen toggle, resize restriction, cursor visibility control.
  • Debug draw: Line-drawing API (with camera/projection) to visualize colliders, etc.
  • Built-in logger: Unified Log, Warn, Error system with configurable log levels.
  • Resource registry: Tag-based register/lookup/release for Shader/Texture/Mesh/Material/Font/SpriteSheet.
  • Window interaction handling: Update loop is automatically paused while dragging or resizing the window to prevent unstable behavior.
  • Stable timing: Delta time is internally clamped to avoid spikes, and input states are reset when large frame delays occur.
  • Dynamic text mesh update: TextObjects automatically rebuild their mesh when the font atlas changes (runtime glyph baking).
  • Collision system: Double-dispatch based collision handling with spatial hash grid broad phase and large-object optimization.
  • Offscreen rendering: Built-in framebuffer rendering pipeline with post-processing support.
  • Deterministic object lifecycle: Objects added during Init() are guaranteed to be initialized within the same frame (Init → LateInit pipeline).
  • Advanced sound system: Supports instance ID reuse and completion callbacks for sound playback.
  • Frame-consistent input system: Uses staged input buffering to ensure correct key/mouse transitions per frame.
  • Safe resource release: Resources cannot be unregistered while still in use (reference-count protected).

Engine Architecture Overview

JinEngine
 ├─ WindowManager : GLFW/GLAD init, event callbacks, swap/clear
 ├─ InputManager  : Key/mouse state, scroll, world-coordinate conversion
 ├─ SoundManager  : miniaudio system, sound loading/playback/control
 ├─ RenderManager : Resource registry, batching/instancing, debug draw
 │   ├─ RenderLayerManager : Manages 0–16 layer tags
 │   └─ FrustumCuller      : Camera-based visibility filtering
 ├─ StateManager  : Switch/update/draw for GameState
 └─ (GameState)   : Owns ObjectManager + CameraManager
      └─ ObjectManager : Object creation/lifetime/collision/draw submit
          ├─ Object    : Transform2D, Mesh/Material/Animator/Collider
          ├─ TextObject: Font + text mesh generation
          └─ Collider  : Circle/AABB + SpatialHashGrid

→ Rendering is batched, layer-sorted, and optionally instanced before submission.


Requirements

  • C++17 or later
  • OpenGL 4.6 driver
  • Platform libraries: GLFW, GLAD, GLM, stb_image, FreeType, miniaudio

Usage by System

1) Rendering / Resources

  • Tag register/lookup: Use Register* / Get*ByTag. Re-registering the same tag warns and is ignored.

  • Material uniforms/textures:

    Material* m = engineContext.renderManager->GetMaterialByTag("mat.brick");
    m->SetUniform("u_Color", glm::vec4(1,1,1,1));
    m->SetTexture("u_Texture", engineContext.renderManager->GetTextureByTag("brick"));
  • Instancing: Your shader must expose the i_Model attribute. After enabling, same Mesh/Material pairs are drawn in one call.

    Material* m = engineContext.renderManager->GetMaterialByTag("mat.instanced");
    Mesh* mesh = engineContext.renderManager->GetMeshByTag("[EngineMesh]default");
    m->EnableInstancing(true, mesh);

2) Text / Fonts

engineContext.renderManager->RegisterFont("NotoSans16", "fonts/NotoSans-Regular.ttf", 16);
Font* font = engineContext.renderManager->GetFontByTag("NotoSans16");
auto text = std::make_unique<TextObject>(font, "Hello, World!", TextAlignH::Center, TextAlignV::Middle);
text->SetColor({0.4,0.7,1.0,0.7});
text->SetIgnoreCamera(true, engineContext.stateManager->GetCurrentState()->GetActiveCamera());
objects.AddObject(std::move(text), "title");
  • Text bakes only the needed glyphs into the atlas at runtime, and supports alignment/multi-line.
  • Text meshes are automatically rebuilt when new glyphs are baked into the atlas at runtime.

3) Animation (Sprite Sheets)

engineContext.renderManager->RegisterTexture("hero", "Textures/hero.jpg");
engineContext.renderManager->RegisterSpriteSheet("heroSpriteSheet", "hero", 32, 32);
SpriteSheet* sheet = engineContext.renderManager->GetSpriteSheetByTag("heroSpriteSheet");
sheet->AddClip("sidewalk",  { 0,1,2,3,4,5,6,7,8 }, 0.08f, true);
sheet->AddClip("frontwalk", { 86,87,88,89,90,91 }, 0.08f, true);
sheet->AddClip("idle",      { 9 },                 0.08f, false);
AttachAnimator(sheet, 0.08f);
GetSpriteAnimator()->PlayClip("idle");

4) Input

if (engineContext.inputManager->IsKeyPressed(GLFW_KEY_SPACE)) { /* ... */ }

5) Camera / Layers

engineContext.renderManager->RegisterRenderLayer("UI", 2);

Camera2D* cam = cameraManager.GetCamera("main"); // default camera
cam->SetZoom(1.25f);
cam->SetPosition({100,50});

obj->SetRenderLayer("UI");

6) Collision

obj->SetCollider(std::make_unique<AABBCollider>(obj, glm::vec2(1.f, 1.f)));
obj->GetCollider()->SetUseTransformScale(true);
obj->SetCollision(engineContext.stateManager->GetCurrentState()->GetObjectManager(),
                  "button", { "player" });
  • Supports circle/box colliders, point tests, collision groups/masks, double-dispatch collision handling, and spatial hash grid broad phase optimization.

7) Sound

engineContext.soundManager->LoadSound("bgm", "audio/bgm.ogg", /*loop=*/true);
auto id = engineContext.soundManager->Play("bgm", 0.7f);
engineContext.soundManager->ControlByID(SoundManager::SoundControlType::Pause, id);
engineContext.soundManager->ControlByTag(SoundManager::SoundControlType::Resume, "bgm");

8) Async Loading

// In LoadingState
loading->QueueTexture(engineContext, "tex.bg", "Textures/bg.png");
loading->QueueFont(engineContext, "font.ui", "Fonts/NotoSans.ttf", 32);
loading->QueueSound("bgm", "audio/bgm.ogg", true);

9) Compute Shaders

ComputeMaterial* mat = engineContext.renderManager->GetMaterialByTag("waterDrop");
engineContext.renderManager->DispatchCompute(mat); // assumes 8x8 workgroup size

10) Debug Draw

engineContext.renderManager->DrawDebugLine({-50,-50}, {50,50}, GetActiveCamera(), {1,0,0,1}, 2.0f);
// StateManager flushes every frame; shown when the engine allows debug draws

Performance Tips

  • Maximize identical Mesh/Material combinations to reduce draw calls.
  • Use instancing whenever possible.
  • For large maps, spatial hashing and frustum culling provide significant gains in collision/visibility.
  • Unload unused textures/shaders/materials via Unregister* to free GPU memory.
  • Offscreen rendering is used internally, so resolution changes may trigger framebuffer reallocation.

Troubleshooting

  • Seeing a black/yellow checker texture? Your material doesn’t have a texture bound.
  • Garbled fonts? Check the TTF path/pixel size range (4–64) and logs to ensure the atlas expands dynamically.
  • State transition stops BGM? Engine automatically stops all sounds when switching states.
  • Sudden input loss after window drag? → Engine resets input to prevent invalid states (expected behavior).

Third-Party

  • GLFW — window/input
  • GLAD — OpenGL loader
  • OpenGL 4.6 — rendering
  • GLM — math
  • stb_image — image loader
  • FreeType — fonts/glyphs
  • miniaudio — audio

Please comply with each library’s license.


License

This project is licensed under the MIT License.
You are free to use, modify, and distribute this engine in both personal and commercial projects, as long as you include the original license notice.


Building as a Library

If you want to build JinEngine as a separate library and link it to your project:

  1. Build the Engine

    • Use Visual Studio to compile the engine as a static library (.lib) with the EngineOnly configuration.
    • The build will produce:
      • JinEngine.lib
      • Headers/ folder (contains engine-facing headers)
      • Thirdparty/ folder (external dependencies: GLFW, GLM, stb_image, FreeType, miniaudio)
  2. Setup in Your Project (Visual Studio example)

    • Go to Project Properties → C/C++ → General → Additional Include Directories
      • Add $(JinEngineRoot)/public
      • Add $(JinEngineRoot)/thirdparty/include
    • Go to Project Properties → Linker → General → Additional Library Directories
      • Add the path to your built JinEngine.lib
    • Go to Project Properties → Linker → Input → Additional Dependencies
      • Add JinEngine.lib
  3. Notes

    • When distributing, provide the license notices for all included third-party libraries.

Sponsor this project

 

Packages

 
 
 

Contributors