I took the featured photograph in a trekking event. It shows a pine tree forest which is near the country I live, and I like the photo a lot! 🙂

I am really excited to start implementing my own game engine I have been dreaming for a long time.

The name of the engine(Göknar) is coming from the Turkish translation of fir which is a very beneficial tree for our health and the nature.

I started with project setup, engine class, window, input managers, and renderer. Basically the workplace has two projects which are Goknar exported as a dll file, and GameProject exported as an executable that uses Goknar.dll.

Firstly the Engine class is responsible for all the engine capabilities. It will create WindowManager, InputManager, and Renderer. It also has a global Engine variable for other classes to be able to reach it with ease.

Engine

Engine class is where every core operation is created. WindowManager, InputManager, GraphicsManager, ObjectManager, and Renderer are some examples of the managers for now. Furthermore, we have initializing function Init(), Run() function to control managers and core operations, BeginGame() function to call every registered object’s BeginGame() function, and Tick(float) function to call all the tickable object’s same named function.

#ifndef __ENGINE_H__
#define __ENGINE_H__

#include <vector>

#include "Application.h"

class Editor;
class GraphicsManager;
class InputManager;
class ObjectBase;
class ObjectManager;
class Renderer;
class WindowManager;

// Global Engine variable
extern class Engine *engine;

class GOKNAR_API Engine
{
public:
	Engine();
	~Engine();

	void Init() const;
	void BeginGame();
	void Run();
	void Tick(float deltaTime);

	WindowManager* GetWindowManager() const
	{
		return windowManager_;
	}

	InputManager* GetInputManager() const
	{
		return inputManager_;
	}

	GraphicsManager* GetGraphicsManager() const
	{
		return graphicsManager_;
	}

	Renderer* GetRenderer() const
	{
		return renderer_;
	}

	Editor* GetEditor() const
	{
		return editor_;
	}

	void SetApplication(Application* application);

	void RegisterObject(ObjectBase *object);
	void AddToTickableObjects(ObjectBase *object);

private:
	GraphicsManager *graphicsManager_;
	InputManager *inputManager_;
	ObjectManager *objectManager_;
	Renderer *renderer_;
	WindowManager *windowManager_;
	Editor *editor_;

	Application *application_;

	std::vector<ObjectBase *> registeredObjects_;
	std::vector<ObjectBase *> tickableObjects_;
};

#endif
#include "pch.h"

#include "Engine.h"

#include "GLFW/glfw3.h"

#include "Application.h"
#include "Editor/ImGuiEditor/ImGuiEditor.h"
#include "GraphicsManager.h"
#include "InputManager.h"
#include "Log.h"
#include "ObjectBase.h"
#include "ObjectManager.h"
#include "Renderer.h"
#include "Scene.h"
#include "WindowManager.h"

Engine *engine;

Engine::Engine()
{
	engine = this;

	Log::Init();

	windowManager_ = new WindowManager();
	windowManager_->SetWindowWidth(1024);
	windowManager_->SetWindowHeight(768);
	windowManager_->SetContextVersion(4, 3);
	windowManager_->SetWindowTitle("Goknar Engine");

	inputManager_ = new InputManager();
	graphicsManager_ = new GraphicsManager();
	objectManager_ = new ObjectManager();
	renderer_ = new Renderer();
	editor_ = new ImGuiEditor();
}

Engine::~Engine()
{
	delete editor_;
	delete renderer_;
	delete objectManager_;
	delete graphicsManager_;
	delete inputManager_;
	delete windowManager_;
	delete application_;
	
	glfwTerminate();
}

void Engine::Init() const
{
	graphicsManager_->Init();
	windowManager_->Init();
	inputManager_->Init();
	objectManager_->Init();
	renderer_->Init();
	Scene::mainScene->Init();

	editor_->Init();
}

void Engine::Run()
{
	while (!windowManager_->GetWindowShouldBeClosed())
	{
		// TODO Temp Tick Call
		Tick(0.016f);

		// TODO Renderer
		renderer_->Render();
		glfwPollEvents();
	}
}

void Engine::BeginGame()
{
	for (ObjectBase* object : registeredObjects_)
	{
		object->BeginGame();
	}
}

void Engine::Tick(float deltaTime)
{
	for (ObjectBase* object : tickableObjects_)
	{
		object->Tick(deltaTime);
	}

	renderer_->Render();
	editor_->Tick(deltaTime);
	windowManager_->Update();
}

void Engine::RegisterObject(ObjectBase* object)
{
	registeredObjects_.push_back(object);
}

void Engine::AddToTickableObjects(ObjectBase* object)
{
	tickableObjects_.push_back(object);
}

void Engine::SetApplication(Application* application)
{
	application_ = application;
}

Window Manager

WindowManager manages all the window operations such as opening a window, resizing it, making it fullscreen, setting the multi sampling anti aliasing value etc. This is the first manager to be built since we need a window to use other managers.

#ifndef __WINDOWMANAGER_H__
#define __WINDOWMANAGER_H__

#include "Core.h"
#include "Math.h"

struct GLFWwindow;
struct GLFWmonitor;

class GOKNAR_API WindowManager
{
public:
	WindowManager();
	~WindowManager();

	static inline void WindowSizeCallback(GLFWwindow *window, int w, int h);

	void Init();
	
	inline GLFWwindow* GetWindow() const
	{
		return mainWindow_;
	}

	void Update();

	void SetWindowWidth(int w);
	void SetWindowHeight(int h);
	void SetWindowTitle(const char *title);
	void SetMSAA(int MSAAValue);
	void SetContextVersion(int major, int minor);
	void SetOpenGLProfile(int profile1, int profile2);
	void SetVSync(bool isEnable);

	void ToggleFullscreen();

	bool GetWindowShouldBeClosed();

	void SetWindowSize(int w, int h)
	{
		windowWidth_ = w;
		windowHeight_ = h;
	}

	Vector2i GetWindowSize() const
	{
		return Vector2i(windowWidth_, windowHeight_);
	}

private:
	GLFWwindow *mainWindow_;
	GLFWmonitor *mainMonitor_;
	const char *windowTitle_;
	
	// Defines the multisample anti-aliasing level
	int MSAAValue_;

	int contextVersionMajor_;
	int contextVersionMinor_;

	int windowWidth_, windowHeight_;

	bool isInFullscreen_;
};

#endif
#include "pch.h"

#include "WindowManager.h"
#include "Core.h"
#include "Log.h"

#include <iostream>

#include "glad/glad.h"
#include "GLFW/glfw3.h"
#include "Engine.h"

WindowManager::WindowManager()
{
	windowWidth_ = 1024;
	windowHeight_ = 768;
	windowTitle_ = "Goknar Engine";
	mainMonitor_ = nullptr;
	MSAAValue_ = 0;
	isInFullscreen_ = false;
}

WindowManager::~WindowManager()
{
	glfwDestroyWindow(mainWindow_);
	glfwTerminate();
}

void WindowManager::WindowSizeCallback(GLFWwindow* window, int w, int h)
{
	engine->GetWindowManager()->SetWindowSize(w, h);
}

void WindowManager::Init()
{
	const int glfwResult = glfwInit();
	GOKNAR_CORE_ASSERT(glfwResult, "GLFW failed to initialize");

	mainWindow_ = glfwCreateWindow(windowWidth_, windowHeight_, windowTitle_, mainMonitor_, 0);

	glfwSetWindowSizeCallback(mainWindow_, WindowSizeCallback);

	glfwSetInputMode(mainWindow_, GLFW_STICKY_KEYS, GL_TRUE);
	SetVSync(true);
	glfwMakeContextCurrent(mainWindow_);

	const int gladResult = gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
	GOKNAR_CORE_ASSERT(gladResult, "Failed to initialize GLAD!.");

	if (!mainWindow_)
	{
		std::cerr << "Window could not be created.\n" << std::endl;
		glfwTerminate();
		exit(EXIT_FAILURE);
	}
}

bool WindowManager::GetWindowShouldBeClosed()
{
	return glfwWindowShouldClose(mainWindow_);
}

void WindowManager::SetWindowWidth(int w)
{
	windowWidth_ = w;
}

void WindowManager::SetWindowHeight(int h)
{
	windowHeight_ = h;
}

void WindowManager::SetWindowTitle(const char *title)
{
	windowTitle_ = title;
}

void WindowManager::SetMSAA(int MMAAValue)
{
	MSAAValue_ = MMAAValue;
	glfwWindowHint(GLFW_SAMPLES, MMAAValue);
}

void WindowManager::SetContextVersion(int major, int minor)
{
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, major);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, minor);
}

void WindowManager::SetOpenGLProfile(int profile1, int profile2)
{
	glfwWindowHint(profile1, profile2);
}

void WindowManager::SetVSync(bool isEnable)
{
	glfwSwapInterval(isEnable ? 1 : 0);
}

void WindowManager::ToggleFullscreen()
{
	// TODO: Implement
}

void WindowManager::Update()
{
	glfwPollEvents();
	glfwSwapBuffers(mainWindow_);
}
#pragma once
#include "Engine.h"

int main(int argc, char **argv)
{
	Engine *mainEngine = new Engine();
	mainEngine->Init();
	mainEngine->BeginGame();
	mainEngine->Run();
	delete mainEngine;
	return 0;
}

Result:

Source code is available on Github.