
In this introductory “zero” lecture, you’ll prepare your development environment and build the foundation for your engine project. You’ll configure CMake, organize your folder structure, and integrate essential third-party libraries — GLFW and GLEW — to get your very first build running.
You will:
Create a clean, organized project layout using CMake
Add GLFW and GLEW as source-based dependencies with proper include paths
Understand how CMake handles targets, directories, and linking
Explore every key line in the CMakeLists.txt to see exactly how it works
Verify your setup with a minimal main.cpp and confirm everything compiles correctly
By the end of this lecture, you’ll have a fully working C++ + OpenGL workspace, ready to start building your engine step by step — with a solid understanding of what’s happening behind the scenes.
In this lecture, you’ll take your first practical step into game development by creating a basic application window using GLFW. You’ll include the necessary headers, initialize the library properly, and handle potential errors like a professional.
We’ll break down how glfwCreateWindow() works, discuss what each parameter does, and clarify the purpose of the GLFWwindow* pointer. You’ll also learn about the main event loop and why every real-time application relies on it.
By the end of this lecture, you won’t just see a window appear — you’ll understand exactly how and why it happens. This is the first visible milestone of your game engine coming to life. Let’s get that window on the screen!
In this lecture, we take the next big step: bringing OpenGL to life inside our window. You’ll start by including the GLEW library, set the desired OpenGL 3.3 Core Profile, and bind the rendering context to your window so that OpenGL knows where to draw.
Then, we’ll properly initialize GLEW, discuss how color values are represented in graphics programming, and execute your very first rendering command by setting a background color.
You’ll also discover what double buffering is and why rendering happens in the back buffer instead of directly on screen — a core concept in modern graphics that ensures smooth visuals without flickering.
By the end of this lecture, you’ll see your first OpenGL-driven result on screen — a colored window — and understand exactly what happens behind the scenes when a single line like glClearColor() is executed.
In this lecture, we move from just displaying a window to actually defining something to render. You’ll learn what vertices are, how 3D space is structured with X, Y, Z axes, and what it means to work in a right-handed vs left-handed coordinate system.
We introduce Normalized Device Coordinates (NDC) — the coordinate space where OpenGL expects your geometry — and explain why values range strictly from -1 to 1.
To put this into practice, you’ll define the simplest possible 3D object: a triangle, also known as the “Hello World” of computer graphics. You’ll understand:
How vertex positions describe geometry
Why winding order matters and how OpenGL determines which side of a triangle is “facing” the camera
How to store vertex data efficiently using std::vector<float>
By the end of this lecture, you’ll have created a clean data structure containing your very first set of vertices — ready to be sent to the GPU for rendering in the next steps.
Now that we have our first geometry defined, it’s time to understand how that data actually becomes an image on the screen. In this lecture, you’ll explore the graphics pipeline — the sequence of stages your vertex data travels through before it appears as visible pixels.
We’ll walk through each stage of the pipeline:
How the Vertex Shader transforms raw coordinates into screen space
What happens during Primitive Assembly when vertices become actual triangles
How Rasterization converts shapes into fragments (future pixels)
The role of the Fragment Shader in defining each pixel’s final color
How testing and blending ensure smooth, correct rendering on screen
You’ll discover which parts of the pipeline are programmable — the stages where you as a developer can inject custom logic with shaders to create effects, lighting, stylized visuals, and more.
By the end of this lecture, you’ll clearly understand the full journey your vertices take — from abstract data to a rendered image — setting the stage for working with shaders in the upcoming lessons.
In this lecture, you’ll take full control over the rendering process by writing your very first vertex and fragment shaders — the programmable stages of the graphics pipeline.
You’ll learn how to:
Write GLSL code for a vertex shader to transform vertex positions into clip space
Create a simple fragment shader that outputs a solid color
Compile both shaders with error handling to catch compilation issues like a real graphics engineer
Link shaders into a single shader program that OpenGL can execute on the GPU
Clean up unused shader objects to keep GPU memory tidy and efficient
All shader source code will be stored using std::string, allowing you to embed clean multiline GLSL code directly in C++.
By the end of this lecture, you won’t just understand what shaders are — you’ll have your first fully compiled and linked GPU shader program, ready to be used for real rendering in the upcoming steps.
In this lecture, you’ll take a crucial step toward real-time rendering by sending your triangle’s vertex data to the GPU and issuing your very first draw call.
Here’s what you’ll learn and implement:
Creating a Vertex Buffer Object (VBO) to store vertex data in GPU memory
Understanding how glBufferData transfers data and what GL_STATIC_DRAW really implies
Using a Vertex Array Object (VAO) to define how vertex attributes map to shader inputs
Configuring vertex attribute pointers with glVertexAttribPointer and enabling them properly
Binding the shader program and VAO during the render loop
Calling glDrawArrays(GL_TRIANGLES, 0, 3) — the iconic OpenGL command that renders your first visible triangle
By the end of this lecture, you won’t just prepare data — you’ll actually see your first triangle rendered on screen, powered by your own shaders and GPU-side buffers.
This is a major milestone — your rendering pipeline is officially alive!
In this lecture, you’ll bring your geometry to life with per-vertex colors, and take your rendering pipeline to the next level by learning how to draw shapes efficiently using indices.
Here’s what you’ll accomplish:
Extend your vertex and fragment shaders to handle color attributes
Pass color data from the vertex shader to the fragment shader using varying variables
Add RGB color values to each vertex and see real-time color interpolation across the triangle surface — creating smooth gradients automatically handled by OpenGL
Learn why duplicating vertices is inefficient and how EBOs (Element Buffer Objects) help reuse vertex data
Convert your single triangle into a rectangle composed of two triangles
Set up and bind an index buffer and switch from glDrawArrays() to glDrawElements() for optimized rendering
Introduce uniforms to globally modify color tint — opening the door to dynamic effects and real-time shader control
By the end of this lecture, you’ll not only render a colored rectangle with smooth gradients, but you’ll also understand how modern graphics APIs avoid unnecessary duplication and make rendering more efficient and flexible.
In this lecture, you’ll make your rendered shape interactive for the first time — turning static graphics into something that responds to user input.
Here’s what you’ll achieve:
Set up a key callback using glfwSetKeyCallback to listen for keyboard events
Understand how input events work in real-time applications
Detect arrow key presses and log them for debugging
Introduce a uniform vec2 in the vertex shader to control positional offset
Update the uniform value dynamically from C++ based on keyboard input
Send new values to the GPU every frame using glUniform2f
Experience your first moment of interactivity: moving a shape on screen with arrow keys
By the end of this lecture, your rendering pipeline evolves from a simple visual output into an interactive system, setting the stage for camera control, character movement, and more advanced gameplay mechanics later on.
In this milestone lecture, you’ll put everything you've learned into practice by building a fully playable Snake game from scratch using OpenGL and modern C++.
This is not just a coding exercise — it’s your first real game project featuring:
Game loop architecture — handling updates, rendering, and timing
Keyboard input and direction logic with GLFW callbacks
Game state management — score, movement, speed increase, restart logic
Fruit spawning and collision detection
Dynamic rendering using shaders, uniforms, and quads
A simple bitmap font system to draw score and messages directly with OpenGL
Visual feedback: start screen, game over screen, and score display — all rendered manually
You’ll combine vertex buffers, shaders, uniforms, input handling, and rendering — transforming low-level graphics primitives into a complete interactive experience.
By the end of this lecture, you’ll not only play a game you created entirely yourself, but also fully understand how every part of it works — from GPU buffers to game logic flow.
This marks a huge step: you've officially gone from drawing shapes to developing a real OpenGL game.
In this lecture, we transition from writing standalone game code to building a real reusable game engine. You’ll structure your project into proper modules and begin designing the two core components of any engine: the Engine class, which controls initialization, the game loop, and cleanup — and the Application class, which will serve as the developer-facing layer for creating games and interactive projects.
You’ll organize the project into a dedicated engine module, convert it into a library, and relocate third-party dependencies so that the engine manages them internally. With a clean project structure and a proper CMake setup, you'll compile your engine for the first time — marking the moment your game code officially evolves into an engine framework.
In this lecture, you’ll begin writing the foundational code that powers your custom game engine. You’ll implement the core Engine and Application classes, define their responsibilities, and establish a clean lifecycle structure that all future game logic will rely on.
You will:
Create the Engine class with clear lifecycle methods: Init(), Run(), and Destroy()
Define the Application interface using pure virtual methods to ensure every project built on the engine follows a consistent structure
Use std::unique_ptr to manage application ownership and enforce safe memory handling
Connect the engine to the application and implement a clean main game loop with time tracking using std::chrono
Prepare the foundation for expandable and maintainable engine architecture
By the end of this lesson, your engine will have a working lifecycle and a structurally sound entry point for all upcoming features — a proper backbone for any real game or interactive application.
In this lecture, you’ll connect your newly created engine module to an actual game project for the first time. This is where your architecture transforms from isolated engine code into a working framework that can run real applications.
You will:
Expose engine functionality through a clean public header (eng.h)
Configure the main project to include and link against the engine module
Create your own Game class by inheriting from eng::Application
Implement the entry point (main.cpp) that initializes the engine, links it to your game, and starts the main loop
Confirm successful integration by printing live deltaTime values from the update loop
By the end of this lecture, your engine won’t just compile — it will run a real application class through a proper engine-driven lifecycle, paving the way for future features like rendering, input, scenes, and more.
In this lecture, you’ll take an essential step toward making your engine fully functional by moving window creation from manual game code into the Engine’s initialization process. Instead of creating a window in main.cpp, your engine will now handle it internally as a true framework should.
You will:
Extend Engine::Init() to accept resolution parameters and handle GLFW window creation
Store the window as a class member so it can be managed across the engine
Initialize OpenGL context and prepare GLFW/GLEW for rendering
Update the main game loop to process window events and swap buffers properly
Clean up window resources in Engine::Destroy() to ensure safe shutdowns
By the end of this lecture, your engine will no longer just run logic — it will open and manage its own rendering window, making it ready for real graphics and game content.
In this lecture, you turn your engine into a truly interactive framework by adding a dedicated input system. Instead of directly checking GLFW key events inside game code, you’ll introduce a clean and reusable InputManager component — fully integrated into the engine core.
You will:
Create a proper InputManager class with safe access patterns and engine-only ownership
Integrate it directly into the engine and expose it through a clean interface
Convert the Engine into a singleton, allowing global access anywhere in the codebase
Register a GLFW key callback that updates key states inside the engine
Query input cleanly inside your Game::Update() method using an intuitive API like IsKeyPressed()
By the end of this lesson, your engine will support real-time input tracking in a clean, engine-driven way — just like professional engines do. From now on, gameplay systems will interact with engine-level input, not raw GLFW calls.
In this lecture, your engine evolves from simple logic management into a graphics-capable framework. Instead of calling raw OpenGL functions directly, you’ll introduce a structured graphics layer that makes rendering safer, more efficient, and easier to maintain.
You will:
Create a dedicated graphics module in the engine
Implement a ShaderProgram class that wraps OpenGL shader IDs, tracks uniform locations with caching, and provides a clean Bind() interface
Add GPU-safe construction and destruction logic using RAII principles to prevent leaks
Introduce a central GraphicsAPI class that serves as the unified entry point for graphics operations within the engine
Integrate GraphicsAPI into the Engine and expose it through GetGraphicsAPI() for use in gameplay code
Test shader creation directly from Game::Init() using clean engine-level APIs like CreateShaderProgram()
By the end of this lesson, your engine gains its first graphics abstraction layer, moving away from raw OpenGL calls toward a professional, modular rendering architecture — just like real game engines.
In this lecture, you take an important conceptual step toward modern rendering workflows by introducing the Material system. Instead of directly binding shaders and uniforms every time you render, you’ll encapsulate these properties into a reusable, high-level object — just like professional game engines do.
You will:
Learn the core idea behind materials as a combination of a shader program and its unique rendering parameters
Implement a clean Material class capable of storing a shader and uniform parameters
Add support for setting per-material values like float parameters that are automatically pushed to the GPU during Bind()
Integrate material handling into the GraphicsAPI with a dedicated BindMaterial() method
Initialize and apply your first material inside game code — laying the foundation for future features like textures, colors, and lighting
By the end of this lecture, your engine will move one step closer to a true component-based rendering pipeline, where visual configuration is clean, modular, and fully reusable.
In this lecture, your engine gains the ability to handle real geometry through a dedicated Mesh system. Instead of manually pushing vertex data to OpenGL each time, you’ll implement a reusable abstraction that represents renderable objects in GPU memory — just like professional engines do.
You will:
Learn the purpose of a Mesh as the container for vertex and index geometry
Create the Mesh class with its own VAO, VBO, and EBO stored as engine-managed resources
Introduce VertexLayout and VertexElement — a clean way to describe how vertex data is structured for the GPU
Implement proper binding and draw calls (Bind() and Draw()) with support for both indexed and non-indexed rendering
Integrate Mesh creation through the engine's GraphicsAPI, centralizing all buffer creation logic
Set up attribute pointers based on layout definitions instead of hardcoded OpenGL calls
By the end of this lecture, your engine will be capable of taking arbitrary vertex data, storing it in GPU memory, and rendering it efficiently using a clean, extensible abstraction — a major step toward a full rendering pipeline.
In this lecture, you’ll finally bring your Mesh system to life by creating an actual renderable geometry instance directly in your game code. This is where your engine goes from abstract systems to real, drawable content on screen.
You will:
Declare a std::unique_ptr<Mesh> inside your Game class to store renderable geometry
Define vertex and index buffers for a colored rectangle using the same data format as before
Create a proper VertexLayout describing how the GPU should interpret position and color attributes
Instantiate a Mesh using your engine’s resource pipeline, automatically initializing VBO, VAO, and EBO under the hood
Extend GraphicsAPI with BindMesh() and DrawMesh() helper methods to maintain a clean and unified rendering interface
By the end of this lesson, your engine will successfully load geometry into GPU memory and prepare it for rendering — marking a major step toward your first fully drawn object using your custom engine pipeline.
In this lecture, your engine performs its first real rendering pass using a clean and extensible architecture. Instead of drawing objects directly from game logic, you introduce a Render Queue — a structured system used by real game engines to batch and organize render commands before execution.
You will:
Create a RenderQueue class that stores RenderCommand entries containing a Mesh + Material pair
Learn how the update phase submits draw requests while the render phase processes and executes them
Add utility methods to GraphicsAPI for clearing buffers and setting background color
Integrate the render phase into the Engine loop, placing it between logic updates and buffer swapping
Submit render commands directly from your Game::Update() method to queue objects for drawing
By the end of this lecture, your engine will perform a complete draw cycle: clear screen → apply material → bind mesh → issue draw call — all through a modular, engine-level system built for scalability.
In this lecture, you enhance your rendering pipeline with interactive input control, finally connecting your custom engine systems — ShaderProgram, Material, Mesh, InputManager, and RenderQueue — into a responsive, real-time experience.
You will:
Extend your ShaderProgram to support vec2 uniforms using glUniform2f
Upgrade the Material system to store and apply 2D uniform parameters cleanly
Add position offset variables inside your Game class to track object movement
Modify the vertex shader to apply positional offset using uOffset
Use the engine’s InputManager to capture keyboard input with W, A, S, D keys
Dynamically update material uniforms each frame to move the rendered mesh on screen
By the end of this lecture, your engine won’t just render static geometry — it will respond to player input in real time through a clean, engine-level API, showcasing the power of your architecture.
In this lecture, you begin transforming your engine from a rendering framework into a true game engine by introducing two core concepts used in all modern engines — Scene and GameObject.
You will:
Understand the idea of a Scene as a container that manages all objects in a game world or level
Learn how GameObjects form a hierarchical tree, allowing parent-child relationships and grouped transformations (just like Unity, Unreal, and Godot)
Implement a base GameObject class with lifecycle control, naming, hierarchy management, and safe destruction handling
Create a Scene class responsible for updating all existing GameObjects and spawning new ones through clean factory methods
Prepare the system for dynamic object creation (CreateObject) and inheritance, laying the groundwork for components and behaviors later
By the end of this lesson, your engine will gain its first scene graph system, making it capable of managing multiple objects logically — a foundational step toward building playable worlds.
In this lecture, you add one of the most important features of a true scene system — hierarchical parenting. This allows game objects to be dynamically attached to each other, forming a structured scene graph, where moving a parent affects all its children — just like in professional engines such as Unity, Unreal, or Godot.
You will:
Implement the SetParent() logic inside the Scene system
Support moving objects to the root or under another GameObject
Safely reparent objects that already belong to someone else
Prevent circular parenting, ensuring a valid tree structure at all times
Ensure objects are properly transferred between root-level storage (m_objects) and child lists (m_children)
Handle both newly spawned objects and existing ones during parent assignment
By the end of this lesson, your engine will support a full scene hierarchy, enabling complex structures like UI trees, bone rigs, grouped transformations, and dynamic object organization — all driven by clean, engine-level logic.
In this lecture, you put your Scene and GameObject architecture into real use by creating your first gameplay entity — TestObject. Instead of keeping logic inside the Game class, you’ll move rendering, update, and input behavior into a proper GameObject-derived class and let the Scene system manage it automatically.
You will:
Create a TestObject class derived from eng::GameObject
Move all previous mesh, material, and shader setup into the object's constructor
Transfer real update logic — including input and uniform manipulation — into TestObject::Update()
Introduce a Scene instance into the game and let it spawn and manage objects
Call m_scene.Update(deltaTime) instead of directly controlling rendering from the game loop
By the end of this lesson, your engine will officially be working like a modern game engine: objects exist inside a scene, update themselves, and render via engine-managed flow, instead of relying on procedural code.
In this lecture, you transition from simply drawing objects to placing them meaningfully in 3D space. To do that, we introduce one of the most fundamental concepts in game development — the Transform.
You’ll learn:
Why every scene object needs position, rotation, and scale
What happens if all objects stay at the world origin
How real engines represent movement and orientation using matrices, not just words like "move" or "rotate"
Why 3D engines use 4×4 transformation matrices
How tools like GLM allow us to build model matrices using translate, rotate, and scale functions
By the end of this lesson, you’ll understand how transformations are expressed mathematically and why we need a Transform component inside each GameObject. This sets the stage for adding real spatial behaviour to your engine — enabling objects to move, rotate, scale, and inherit transforms from their parents.
In this lecture, you upgrade your engine from simple 2D-style offsets to a true transformation system built around position, rotation, and scale — just like real 3D engines.
You will:
Extend GameObject with glm::vec3 fields for position, rotation, and scale
Implement GetLocalTransform() to build a model matrix using GLM’s translate, rotate, and scale
Implement GetWorldTransform() to apply hierarchical transformations based on parent-child relationships
Update the shader to use a mat4 uModel uniform instead of primitive offsets
Enhance ShaderProgram to upload matrices via glUniformMatrix4fv
Extend RenderCommand to carry a model matrix per object
Pass the transformation matrix from game logic to the render pipeline cleanly
Remove temporary offset hacks and migrate fully to engine-level transform math
By the end of this lesson, every object in your scene will have a proper transform component, and your rendering pipeline will officially support full world-space matrix transformations — bringing your engine architecture closer to the structure used in Unity, Unreal, and Godot.
In this lecture, you evolve your engine architecture by introducing a Component-Based System, the same approach used in modern engines like Unity and Unreal. Instead of hardcoding behaviour into each GameObject, you give objects the ability to attach modular components that define functionality.
You will:
Create a base Component class with a clean lifecycle and reference to its owning GameObject
Extend GameObject to store and update an arbitrary number of components
Implement a reusable AddComponent() API for attaching behaviour dynamically
Build your first real component — MeshComponent — responsible for sending render commands to the engine
Refactor TestObject to use components instead of manually handling rendering logic
By the end of this lesson, your engine will support modular, flexible behaviours, allowing you to compose objects by attaching features instead of writing long, monolithic object classes. This is a major architectural milestone — your engine is now ready for scalable gameplay systems.
In this lecture, you introduce one of the most essential systems in 3D rendering — the Camera. Until now, objects were rendered directly to screen space with only a model matrix. Now, you’ll take a major step forward by implementing proper View and Projection matrices, enabling real scene navigation and perspective.
You will:
Understand why a camera is necessary when working with larger worlds and multiple objects
Learn the difference between Model, View, and Projection matrices — and how they form the MVP pipeline
Implement a CameraComponent that uses a GameObject’s world transform to generate a View Matrix via inverse transformation
Prepare for Perspective or Orthographic projection by adding a GetProjectionMatrix() method
Add active camera support to the Scene and ensure rendering happens through the camera’s perspective
Update your rendering code and shaders to accept uView and uProjection uniforms and apply uProjection * uView * uModel in the vertex stage
By the end of this lesson, your engine will render objects as seen through a camera, just like in real game engines — unlocking the ability to navigate space, fly through environments, and build real 3D worlds.
In this lecture, you complete the camera system and make it fully functional inside your engine by integrating it with the rendering pipeline and your new component architecture.
You will:
Implement a type-safe GetComponent<T>() system without using RTTI, using custom type IDs for fast component lookup
Use a macro-based pattern to assign unique IDs to each component type cleanly
Build support for FOV, aspect ratio, near and far planes, and understand their role in projection
Implement perspective projection via glm::perspective() inside CameraComponent
Retrieve the active camera from the scene and pass View and Projection matrices into the shader every frame
Update your vertex shader to use the full MVP matrix chain: uProjection * uView * uModel
Spawn a camera object in the scene, attach a CameraComponent, position it, and set it as the main rendering camera
By the end of this lesson, your engine will render objects from a real 3D camera perspective, using a fully modular component system — just like modern game engines such as Unity and Unreal.
In this lecture, you bring your camera to life by adding a PlayerControllerComponent, allowing you to fly through the scene just like in game editors or first-person camera modes.
You will:
Create a PlayerControllerComponent that can be attached to any GameObject — including one with a CameraComponent
Expand InputManager to support mouse buttons and mouse movement tracking
Register GLFW mouse callbacks to detect mouse delta every frame
Implement mouse look (pitch and yaw rotation) with sensitivity scaling
Implement smooth WASD camera movement, calculated relative to the camera’s current facing direction using forward and right vectors
Use delta time to ensure consistent motion speed regardless of FPS
By the end of this lesson, your engine will allow full First-Person Camera control:
✅ Look around by holding the mouse button and moving the mouse
✅ Move forward/backward with W/S and strafe with A/D
✅ Experience Unity/Unreal-style free camera navigation — fully powered by your custom engine
In this lecture, your engine officially transitions from 2D-style rendering into full 3D space. Instead of drawing a flat rectangle, you’ll evolve it into a 3D cube by expanding vertex data and defining proper index sets for all faces.
You will:
Convert your existing quad into a 3D mesh with eight vertices and indexed triangles for all six sides of a cube
Understand and apply correct index ordering to define each face efficiently
Enable depth testing (Z-buffer) in OpenGL so that objects no longer render over each other incorrectly
Update the rendering pipeline to clear both color and depth buffers each frame
Test your cube in real 3D using the PlayerController camera to freely move around it
Learn how to efficiently spawn multiple objects using shared Mesh and Material resources to avoid redundant GPU allocations
By the end of this lesson, your engine won’t just simulate a 3D world — it will render it properly, with correct depth perception, reusable resources, and support for multiple objects in the scene.
In this lecture, you solve one of the most common 3D engine pitfalls: rotation order problems. Up to this point, rotations in your engine were applied as three separate Euler angles (X, Y, Z), which causes issues like unexpected orientation shifts and gimbal lock depending on the rotation sequence.
To fix this, you will transition to using quaternions — the same method used in professional engines like Unreal, Unity, and Godot for stable, smooth rotations.
You will:
Understand why rotation order with Euler angles is problematic
Replace vec3 rotation in GameObject with a quaternion-based system
Update transformation logic using glm::mat4_cast() for clean quaternion rotation matrices
Adjust CameraComponent to generate correct View matrices using quaternion-based orientation
Rewrite the PlayerControllerComponent to apply camera rotation using glm::angleAxis() on global Y (yaw) and local right axis (pitch)
Normalize quaternion rotation for smooth FPS-style camera movement
By the end of this lesson, your camera will rotate just like in a proper first-person engine — responsive, stable, and without any axis locking or rotation glitches.
In this lecture, you’ll create the foundation for your engine’s resource loading system — a unified, cross-platform way to access game assets such as textures, models, and sounds. This marks the beginning of turning your project into a full-featured, data-driven engine.
You will:
Learn the concept of engine assets and how to organize them inside a dedicated /assets directory
Configure CMake to define a global ASSETS_DIR variable and generate a config.h header with a build-time path macro
Ensure your assets are automatically copied next to the executable during the build process
Implement a FileSystem class that retrieves paths to both the executable folder and the assets directory, using platform-specific APIs for Windows, macOS, and Linux
Integrate the new system into the Engine class and use it to load files consistently in both development and runtime modes
Test it by loading a sample image (brick.png) through the stb_image library to confirm your file system works properly
By the end of this lesson, your engine will have a robust, platform-independent file access layer — a key building block for loading textures, models, and any other resources in the upcoming lessons.
In this lecture, you bring visual richness to your 3D objects by introducing textures — images that wrap around meshes to define their surface appearance. You’ll implement texture loading, GPU management, and UV coordinate mapping, completing one of the core pillars of modern rendering.
You will:
Learn what textures are and how they’re used to add detail and realism to 3D objects
Create a Texture class that handles loading, binding, and GPU storage of images
Configure wrapping (repeat, mirrored repeat, clamp) and filtering (linear, mipmaps) parameters for smooth scaling
Connect textures to materials so they can be passed automatically to shaders
Expand your vertex format to include UV coordinates and update the vertex and fragment shaders to sample from a texture
Learn how texture units work and how to bind multiple textures to different slots during rendering
Implement texture unit management inside ShaderProgram for predictable multi-texture binding
By the end of this lecture, your engine will render textured 3D objects with smooth filtering and proper UV mapping — a huge leap forward toward realistic materials and asset-driven rendering.
In this lecture, you’ll make your engine smarter and more modular by introducing a unified resource loading system. Instead of manually loading data in game code, your engine will now handle textures, shaders, and other assets automatically through a clean, centralized API.
You will:
Refactor the Texture class to include a static Load() method that loads textures directly from file paths
Integrate the FileSystem to automatically resolve asset locations relative to the /assets directory
Implement a generic file loading API using std::ifstream for binary data and text
Add helper methods like LoadAssetFile() and LoadAssetFileText() to simplify access to different asset types
Treat shaders as resources by moving their source code into standalone .glsl files inside /assets/shaders
Refactor the engine to load shader code dynamically at runtime rather than hardcoding it in Game.cpp
By the end of this lecture, your engine will load both textures and shaders through a unified, high-level system — a huge leap toward a scalable, data-driven engine architecture where assets live outside your code.
In this lecture, you’ll take another major step toward a data-driven engine by making materials fully external and editable. Instead of defining them in code, your engine will now load materials from JSON files, just like real game engines do.
You will:
Add the nlohmann/json library to your project for easy JSON parsing
Define a clear material asset structure containing shaders, numeric parameters, and textures
Implement Material::Load() to read, parse, and build a complete material from a .mat file
Use the FileSystem to resolve shader and texture paths inside the /assets directory
Automatically load vertex and fragment shaders from external .glsl files
Assign parameters such as floats, float2 vectors, and textures directly from JSON data
Replace all hardcoded material setup in Game.cpp with a single call to Material::Load("materials/brick.mat")
By the end of this lecture, your engine will support fully external material definitions, making it easier to tweak shaders, colors, and textures — without touching the source code. This marks a big leap toward a flexible and professional-grade content pipeline.
In this lecture, your engine takes a huge step forward — it learns how to load real 3D models from files instead of generating geometry manually. You’ll implement a robust mesh loader using the modern GLTF format, which is widely used across the game industry for its efficiency and compatibility.
You will:
Learn what the GLTF format is and why it’s the standard for modern 3D engines
Add the cgltf library — a lightweight, header-only GLTF parser
Implement Mesh::Load() to parse .gltf files and extract vertex and index data from buffers
Understand how accessors and buffer views work in GLTF to describe geometry layout
Build a flexible VertexLayout by mapping POSITION, COLOR, and TEXCOORD attributes
Read vertex and index data into engine-friendly buffers and upload them to the GPU
Add Suzanne (the Blender monkey head) as your first loaded 3D model
Create a matching material and texture for Suzanne and render her alongside your cube
By the end of this lecture, your engine will support external 3D assets — capable of loading, rendering, and managing real models with materials and textures. From now on, you’re no longer limited to procedural shapes — welcome to the world of full 3D content pipelines.
In this lecture, your 3D world finally comes to life — with lighting. Up until now, all rendered objects looked flat and lifeless. Now you’ll implement diffuse lighting, giving them real shape, depth, and presence.
You will:
Understand the concept of diffuse reflection — how surface brightness depends on the angle between light and surface normal
Create a new LightComponent with color and position handled through its parent GameObject
Extend your Scene system to collect and manage multiple active lights recursively
Add normal attributes to your meshes and pass them to shaders
Modify vertex and fragment shaders to calculate lighting using dot products between normals and light direction
Implement proper normal transformation using mat3(transpose(inverse(uModel))) for world-space accuracy
Introduce a Light struct in GLSL and pass light data from C++ via uniforms
Add normals to procedural meshes like the cube so they interact correctly with light
Build your first real-time lit scene — with Suzanne and cubes shaded beautifully under a point light
By the end of this lecture, your engine will render objects with true depth and shading. Surfaces will respond to light, highlighting their volume and orientation — turning your flat geometry into a vibrant, illuminated 3D world.
In this lecture, you’ll elevate your engine’s content pipeline to a professional level by implementing complete glTF scene loading — not just individual meshes, but full hierarchies with transformations, materials, and textures.
You will:
Expand your asset pipeline to load entire scenes directly from .gltf files
Add a static GameObject::LoadGLTF() method that parses full scene hierarchies
Learn how glTF stores nodes, each representing a transformable object that may contain meshes, materials, and children
Implement recursive parsing of nodes via ParseGLTFNode() to build a GameObject hierarchy automatically
Support both transformation formats: matrices and separate position / rotation / scale components
Load multiple primitives per mesh, each with its own material and texture
Handle PBR material workflows — Metallic-Roughness and Specular-Glossiness — and automatically assign the right textures
Integrate default shaders through the GraphicsAPI for materials that lack custom definitions
Cleanly map glTF nodes into the engine’s Scene and GameObject system, preserving structure and parent-child relationships
By the end of this lesson, your engine will be able to import entire 3D scenes from standard glTF files — complete with hierarchy, transformations, materials, and textures — making it compatible with real production assets from Blender, Maya, and beyond.
In this lecture, you’ll make your scene more interactive — and a lot more realistic. You’ll load and attach a 3D weapon model to the camera, fix texture handling issues, optimize resource management, and enhance your lighting system with specular highlights for shiny reflections.
You will:
Organize your assets into proper folders for models and textures
Load a weapon model from glTF and attach it to the camera as a child object to simulate an FPS-style view
Adjust its position, rotation, and scale to align it naturally in front of the player’s perspective
Fix texture artifacts by adding support for RGBA textures and dynamic internal format detection
Implement a TextureManager to cache and reuse textures across multiple objects, reducing redundant GPU loads
Extend your lighting model with Phong specular highlights, calculating reflections based on light direction, surface normal, and camera position
Update the fragment shader to include uCameraPos and compute specular intensity for realistic, glossy surfaces
By the end of this lecture, you’ll have a working first-person prototype — complete with smooth camera control, a visible weapon, and beautiful, specularly lit objects that react naturally to movement and light.
In this lecture, your engine takes a massive leap forward — it finally learns how to load and play animations. You’ll implement a full animation system capable of driving object transformations over time, using data imported directly from glTF files.
You will:
Understand what animations are — keyframes of position, rotation, and scale evolving over time
Implement a new AnimationComponent that stores clips, manages playback, and applies transformations each frame
Define essential data structures: keyframes, transform tracks, animation clips, and object bindings
Write interpolation helpers to blend between frames smoothly (linear for vectors, slerp for quaternions)
Parse animation data from glTF files and build hierarchical track-to-object bindings automatically
Add animation playback control — play, loop, stop — and integrate it with the engine’s update cycle
Extend the GameObject system with FindChildByName() and SetActive() for managing animated sub-objects
Load and play your first animation — the gun’s “shoot” sequence — with selective activation of effect nodes
By the end of this lecture, your engine will not only load and render 3D scenes, but also animate them dynamically, breathing real motion into your models — a key step toward full real-time gameplay.
In this lecture, your engine gains the power of real-world physics through the integration of the Bullet Physics engine, a professional-grade, open-source physics library used across games, film, and simulations. You’ll set up a complete physics pipeline and connect it seamlessly with your scene and component systems.
You will:
Add and configure Bullet Physics in your build system for lightweight, static linking
Implement a dedicated PhysicsManager responsible for world creation, gravity setup, and simulation updates
Design clean abstractions for core physics concepts:
Colliders (Box, Sphere, Capsule) for collision shapes
RigidBody for mass, friction, and motion control
Create a PhysicsComponent to link rigid bodies with GameObjects and sync transformations automatically
Support all body types — Static, Dynamic, and Kinematic — with proper simulation behavior
Integrate the physics update step directly into the engine’s main loop
Build and test your first physical scene with a static ground and a falling dynamic box
By the end of this lecture, your engine will simulate gravity, collisions, and motion in real time — introducing real-world dynamics into your virtual worlds. From here, your 3D scenes will not only look alive — they’ll behave like it too.
In this lecture, you’ll transform your free-flying camera into a fully physical, FPS-style player controller — complete with gravity, jumping, and proper collision against the world. By combining input, physics, and animation, you’ll create your first true player character.
You will:
Enable continuous mouse rotation for smooth FPS-style camera control
Lock and hide the system cursor using GLFW’s input modes
Extend InputManager to detect per-frame mouse movement for precise rotation handling
Rework the player rotation logic with clamped pitch and yaw using quaternions for smooth orientation
Integrate Bullet’s Kinematic Character Controller and wrap it in a custom KinematicCharacterController class
Handle walking, jumping, and ground detection through physics
Create a dedicated Player GameObject combining Camera, PlayerController, and a weapon model
Trigger weapon animations when clicking the left mouse button
By the end of this lecture, you’ll control a real first-person character that moves and collides with the environment, reacts to physics, and even plays weapon animations — the foundation of a fully playable prototype.
In this lecture, you begin implementing a full scene loading system, allowing your engine to load an entire world from a single JSON file — complete with objects, transforms, and components. This is the foundation of your content pipeline, where game worlds become data-driven rather than hardcoded.
You will:
Introduce Scene::Load() to read and parse .sc files (scene definitions in JSON)
Define a simple and clear scene format with fields for name, objects, and hierarchical structure
Implement recursive object creation via LoadObject() to construct all scene objects and their children
Add transform data — position, rotation, and scale — to the JSON structure and parse it for each object
Build a Component Factory capable of creating components by type name using registration and templated creators
Implement automatic type registration for built-in components (MeshComponent, CameraComponent, etc.)
Add per-component loading through Component::LoadProperties(), making each component configurable through JSON
Refactor all components to support default constructors and dynamic setup with setters
By the end of this lesson, your engine will be able to load complete scene structures from JSON, automatically create objects and components, and set up their transforms and properties — marking the start of a powerful, data-driven scene system.
In this lecture, you’ll complete your scene loading system, turning it into a fully functional data-driven world builder. You’ll add support for custom objects, physics components, lights, and a complete scene structure loaded entirely from JSON.
You will:
Extend the JSON format to describe MeshComponents with materials, geometry, and parameters
Implement per-component LoadProperties() — letting components like MeshComponent, PhysicsComponent, and LightComponent load themselves from data
Add property loading for GameObject and recursive child creation, allowing hierarchical scene construction
Create a GameObjectFactory to register and instantiate custom object types such as "Player" directly from JSON
Implement JSON-based loading for GLTF models, automatically attaching them to parent objects
Build and register user-defined objects through Application::RegisterTypes()
Support full physics definition in JSON, including colliders, rigid bodies, and body types
Add light objects to the scene and load color parameters dynamically
Introduce automatic camera assignment via JSON, making it easy to mark any object as the main camera
By the end of this lecture, your engine will be capable of loading an entire playable level — with hierarchy, materials, physics, lighting, animations, and player setup — all from a single JSON scene file. You’ve just built the backbone of a modern, data-driven game engine.
In this lecture, you’ll make your engine come alive — not just visually, but audibly. By integrating a lightweight audio system with miniaudio, you’ll add sound playback, spatial audio, and full 3D sound behavior directly into your engine’s component system.
You will:
Integrate the miniaudio library and set up a cross-platform AudioManager inside the engine
Implement initialization, listener updates, and global sound management
Create an Audio resource class capable of loading sounds from memory, decoding audio files, and managing playback
Add support for 3D spatialization, volume control, looping, and per-sound position updates
Implement a flexible AudioComponent to attach sounds to any GameObject and automatically update positions every frame
Create an AudioListenerComponent that represents the player’s “ears” and synchronizes listener position with the camera
Extend the JSON scene format to load and configure sounds directly from .sc files
Connect sound playback to gameplay events — shooting, jumping, and walking — using the Player class logic
By the end of this lecture, your engine will support real-time, spatial 3D audio, seamlessly integrated with the scene and gameplay logic. Your game world will finally sound as alive as it looks.
In this lecture, you’ll transform your prototype into an interactive shooter by adding physical bullets and improving your scene management system to safely handle object creation and destruction during runtime.
You will:
Improve the rendering pipeline by adding proper mesh unbinding for cleaner draw calls
Enhance the scene update loop with safety checks and deferred object creation to prevent crashes when spawning new objects mid-update
Implement safe removal of destroyed objects with MarkForDestroy() and automatic cleanup logic
Create a reusable Bullet class — a lightweight GameObject that exists for a short lifetime and destroys itself automatically
Add a sphere mesh generator to build bullet geometry procedurally
Spawn bullets directly from the player’s weapon position using scene hierarchy lookups
Attach PhysicsComponents to bullets for real-time collision and motion
Extend your physics system with an ApplyImpulse() method for dynamic movement
Implement shooting logic inside the Player class, combining animation, sound, and physics impulses for realistic projectile firing
By the end of this lesson, your engine will support real-time projectile spawning, collision, and cleanup — giving you the core of a physical shooting mechanic built directly into your engine’s architecture.
In this lecture, you’ll make your scene look more realistic, readable, and vibrant by improving your lighting model, expanding your material system, and introducing proper directional sunlight. This step marks the transition from a functional engine to a visually expressive one.
You will:
Add ambient lighting to soften shadows and eliminate fully black areas in unlit regions
Improve the Mesh::CreateBox() function to generate proper UV coordinates for consistent texture mapping
Introduce a checker texture for testing — a staple in game engines for debugging materials and geometry
Build a larger and more interesting scene with multiple boxes, platforms, and walls using physics
Extend your Material system with float3 parameters to support custom colors and tinting
Update shaders to use a vec3 color uniform, allowing material overrides directly from JSON files
Modify MeshComponent::LoadProperties() to support parameterized material definitions
Switch from point lighting to directional lighting, simulating sunlight with parallel light rays
Combine ambient, diffuse, and specular lighting into a balanced, realistic shading model
By the end of this lecture, your engine will render bright, dynamic, and colorful scenes — complete with ambient light, colored materials, and realistic sun illumination. Your 3D world will finally look alive and expressive.
In this lecture, you’ll make your physics world interactive by detecting collisions between objects and reacting to them in real time. You’ll build a complete collision event system and use it to create a fun, dynamic gameplay object — a Jump Platform that launches the player upward when stepped on.
You will:
Extend the PhysicsManager to detect collisions by iterating over Bullet’s contact manifolds
Create a new base class, CollisionObject, to unify all physics-driven entities
Implement IContactListener, allowing objects to react to collisions through callback events
Make both RigidBody and KinematicCharacterController inherit from CollisionObject and register user pointers for tracking
Build the JumpPlatform GameObject — a static, red physics object that implements OnContact() to catapult the player upward
Register the platform in the engine and define it directly in your scene JSON file
Extend your mesh and physics systems to support spheres for more visual variety and rolling interactions
By the end of this lesson, your engine will support real-time collision events, allowing you to design interactive elements like trampolines, jump pads, and other physics-based gameplay mechanics — bringing your world to life with dynamic reactions.
In this lecture, we step back from 3D and reintroduce the simplicity of 2D space, but this time with all the power of your engine’s modern rendering pipeline. You’ll build the cornerstone of every 2D game — the Sprite Component — and extend your engine to support real 2D rendering for UI, effects, and full 2D games.
You will:
Learn what a sprite is — a textured 2D rectangle with size, UVs, pivot, and color tint
Implement a new SpriteComponent with fields for texture, size, pivot, and visibility control
Add JSON loading for sprite data, allowing external configuration of textures, UVs, and colors
Introduce helper methods in GameObject for 2D transformations (position, rotation around Z, and scale)
Create the first version of your 2D RenderQueue, capable of queuing and drawing sprites
Build a reusable plane mesh and a dedicated 2D shader program for sprite rendering
By the end of this lecture, your engine will officially support 2D rendering — ready to display textured sprites and paving the way for UI systems, 2D animation, and hybrid 2D/3D gameplay.
In this lecture, you’ll bring your sprite system to life by creating a dedicated 2D shader, integrating it into the rendering pipeline, and adding crucial features like orthographic projection, depth control, and alpha transparency.
You will:
Write a custom vertex and fragment shader for sprite rendering with pivot, size, and UV coordinate handling
Implement an orthographic camera to correctly project 2D sprites on screen space
Extend the RenderQueue to handle 2D commands separately from 3D rendering
Add blending modes (Alpha, Additive, Multiply) to the GraphicsAPI for transparency and visual effects
Toggle depth testing dynamically to ensure 2D and 3D layers don’t interfere
Build a reusable SetUniform system for vector, matrix, and texture data
Render and test your first sprite on screen — complete with rotation, scaling, and custom UVs
By the end of this lesson, your engine will support a fully functional 2D rendering pipeline with proper layering, transparency, and independent shaders — ready for UI, HUDs, and 2D gameplay.
In this lecture, you’ll lay the groundwork for a complete UI system inside your engine — introducing Canvas, UIElementComponent, and a flexible type hierarchy system that allows proper inheritance between components. This will serve as the backbone for all user interface elements — buttons, text, images, and beyond.
You will:
Create a UIElementComponent base class with pivot handling for consistent positioning
Implement a CanvasComponent, the root of all UI rendering, responsible for recursively drawing elements in hierarchy order
Build the logic to traverse the scene graph and render every child UI element automatically
Design a virtual Render() function for UI components, enabling each element to define its own drawing behavior
Create your first UI component — TextComponent — to represent text on screen
Introduce a type hierarchy system in the ComponentFactory, allowing the engine to recognize inheritance relationships dynamically
Add recursive HasParent() logic, making GetComponent<UIElementComponent>() correctly return derived components like TextComponent
Implement a new macro COMPONENT_2 for parent-child registration between component types
By the end of this lesson, your engine will support a data-driven, hierarchical UI system — complete with parent-child logic, type-safe inheritance, and a structure ready to render actual interface elements in future lessons.
In this lecture, your engine learns how to render text — one of the most essential features for any UI system. You’ll implement a complete font management pipeline using FreeType, enabling the engine to load, rasterize, and cache fonts as texture atlases ready for on-screen rendering.
You will:
Learn what a font really is — a collection of mathematical glyph descriptions that must be rasterized into bitmaps for GPU rendering
Integrate the FreeType library and initialize it inside the engine
Create a Font class to store glyph descriptions, advance metrics, and a texture atlas containing all rasterized ASCII characters
Implement a FontManager that loads .ttf fonts, rasterizes glyphs, packs them into a texture, and caches them by path and size
Connect FontManager to the engine for easy global access via Engine::GetFontManager()
Extend the TextComponent to include fields for text, font, and color, as well as JSON-based configuration
Add helper methods for pivot-based text placement and precompute the drawing start position
By the end of this lecture, your engine will be fully capable of loading and managing fonts, preparing rasterized glyph data, and positioning text in 2D space — ready for the next step: actually drawing text on the screen.
In this lecture, you’ll bring your text rendering system to life — turning raw font data into actual, crisp letters on screen. You’ll build a performant 2D rendering path that batches thousands of glyphs efficiently and introduces proper UI rendering inside your engine.
You will:
Implement TextComponent::Render() to draw text by placing one quad per glyph using the font atlas
Add pivot-based alignment for text placement and handle top-left coordinate systems
Extend CanvasComponent with dynamic vertex and index buffers that rebuild every frame
Build a UI batching system that merges geometry by texture to minimize draw calls
Create a lightweight UI shader handling color, UVs, and alpha blending
Introduce the orthographic projection matrix for screen-space rendering
Implement BeginRendering() and Flush() to accumulate all UI draw calls into a single GPU upload
Add viewport tracking and resizing callbacks for responsive UI
Integrate the new RenderCommandUI into the RenderQueue, creating a dedicated UI pass after 3D rendering
By the end of this lesson, your engine will support efficient, dynamic text rendering — with GPU batching, proper transparency, and real-time updates. This marks the completion of a full-fledged, professional-grade UI rendering pipeline.
In this lecture, you’ll give your user interface real interactivity by implementing clickable buttons and building the foundation for a universal UI input system. You’ll expand your existing UI architecture so that elements can detect mouse events, react to hover and click states, and trigger callbacks in response.
You will:
Extend the UIElementComponent base class with hit detection and event methods (HitTest, OnPointerEnter, OnPointerExit, etc.)
Create the ButtonComponent, a fully interactive element that changes color when hovered or pressed and calls a function on click
Implement the visual part of the button with color states (default, hover, pressed) and rendering logic inside CanvasComponent::DrawRect()
Build a new UIInputSystem responsible for cursor tracking, hit testing, and dispatching pointer events to UI elements
Enhance the InputManager to detect and track mouse button presses, releases, and movement across frames
Connect GLFW input callbacks directly to your new UI input pipeline
Integrate the UI system update loop into the engine’s main cycle
By the end of this lecture, your engine will support fully interactive UI components, reacting dynamically to mouse hover, click, and release — paving the way for real menus, HUDs, and in-game interaction panels.
In this lecture, you’ll complete your UI interaction system by making buttons — and any other UI elements — react naturally to user input. You’ll implement the full event lifecycle for mouse interactions, enabling real-time hover effects, click detection, and cursor-based control inside your custom UI.
You will:
Build a recursive system to collect all UI elements in the active canvas for hit-testing
Implement hover and press tracking inside the UIInputSystem using m_hovered and m_pressed references
Create a full interaction flow with OnPointerEnter, OnPointerExit, OnPointerDown, OnPointerUp, and OnClick
Use HitTest to detect which element is under the mouse each frame and trigger events accordingly
Re-enable and manage the system cursor through GLFW’s input modes for intuitive UI interaction
Add JSON-driven configuration for the ButtonComponent, including size, color, hover, and pressed states
Set up a working test with a button and text label on the canvas — fully interactive and visually reactive
By the end of this lesson, your engine’s UI system will fully support dynamic, mouse-driven interactivity, with buttons that respond to hover and click events just like in modern game engines and editors.
In this lecture, you’ll integrate all your UI systems into a single, functional gameplay flow — creating a working main menu with Play and Quit buttons, smooth transitions between 2D and 3D worlds, and even shader caching for better performance.
You will:
Build a main menu scene with a Canvas containing Play and Quit buttons, each with a ButtonComponent and a TextComponent child
Implement active canvas detection so the engine knows which UI layer to process input for
Add activation logic to CanvasComponent, allowing UI elements to be toggled on and off dynamically
Update the UIInputSystem to respect active canvas states for input safety
Introduce a root 3DRoot object to group and toggle the 3D world
Implement Scene::FindObjectByName() for easy runtime lookups
Expand the InputManager to support higher key codes (like Escape)
Improve font rendering alignment using glyph offsets for cleaner text appearance
Implement a shader cache inside GraphicsAPI to reuse existing shaders efficiently
Add dynamic scene switching and memory-safe scene management with shared_ptr
Connect UI and game logic: Play activates the 3D world, Quit exits the game, and Escape returns to the menu
By the end of this lecture, your engine will support a fully interactive game menu with real buttons, scene toggling, and input flow — just like in professional games. You’ll have completed the bridge between gameplay and user interface inside your custom engine.
In this lecture, you’ll implement the RectTransformComponent, the cornerstone of every modern UI system. This component introduces anchors and pivots, giving your engine a professional-grade layout system that adapts automatically to screen size and parent hierarchy — just like in Unity or Unreal.
You will:
Learn the difference between pivot (local origin of the element) and anchor (reference point in the parent element)
Implement the RectTransformComponent with fields for size, anchor, and pivot, and a method to resolve screen-space positions dynamically
Refactor the existing UI components (ButtonComponent, TextComponent, and UIElementComponent) to use RectTransform as the single source of layout data
Update hit-testing and rendering logic for buttons and text to use anchor/pivot-based positioning
Modify the CanvasComponent to automatically adapt to the viewport each frame for responsive UI scaling
Extend JSON scene definitions to configure anchors, pivots, and element sizes for every UI element
Create a responsive main menu where Play and Quit buttons remain perfectly centered regardless of screen resolution
By the end of this lecture, your engine will feature a fully functional responsive UI layout system, driven by anchors and pivots — making your interface scale beautifully and behave consistently across all resolutions.
Have you ever dreamed of creating your own game engine — not just using Unity or Unreal, but actually building one yourself?
This course takes you on a complete journey from a blank C++ project to a fully functional 3D game engine capable of rendering real-time graphics, handling physics, audio, animation, and UI — all designed and coded from scratch.
You’ll start by building the foundation: creating windows, initializing OpenGL, and rendering your first triangles. Then, step by step, you’ll evolve your framework into a full-fledged engine — complete with scene management, materials and shaders, component systems, and object hierarchies.
As the course progresses, you’ll implement real gameplay features such as lighting, textures, camera control, physics simulation, audio playback, UI buttons, menus, and even 3D model animation using the glTF format. By the end, you’ll have a working engine that supports both 2D and 3D rendering, interactive gameplay logic, and data-driven content loading from JSON files.
This is not a “toy” example — it’s a professional, modern C++ architecture, inspired by the structure of Unity and Unreal, but simplified and built from the ground up for learning and full understanding.
Whether you’re a game developer, graphics programmer, or engine enthusiast, this course will teach you how game engines really work — under the hood.