Here’s a comprehensive folder structure and source code outline for a 3D video game engine using OpenGL and AMD ROCm API calls:
## Folder Structure
“`
game_engine/
├── src/
│ ├── main.cpp
│ ├── engine/
│ │ ├── engine.cpp
│ │ └── engine.h
│ ├── graphics/
│ │ ├── renderer.cpp
│ │ ├── renderer.h
│ │ ├── shader.cpp
│ │ ├── shader.h
│ │ ├── mesh.cpp
│ │ ├── mesh.h
│ │ ├── texture.cpp
│ │ └── texture.h
│ ├── core/
│ │ ├── camera.cpp
│ │ ├── camera.h
│ │ ├── scene.cpp
│ │ └── scene.h
│ ├── systems/
│ │ ├── input_system.cpp
│ │ ├── input_system.h
│ │ ├── physics_system.cpp
│ │ └── physics_system.h
│ ├── utils/
│ │ ├── file_loader.cpp
│ │ └── file_loader.h
│ └── platforms/
│ ├── amd/
│ │ ├── rocm_context.cpp
│ │ ├── rocm_context.h
│ │ ├── compute_shader.cpp
│ │ └── compute_shader.h
│ └── opengl/
│ ├── opengl_context.cpp
│ ├── opengl_context.h
│ ├── buffer.cpp
│ └── buffer.h
├── assets/
│ ├── shaders/
│ │ ├── vertex.glsl
│ │ ├── fragment.glsl
│ │ └── compute.glsl
│ ├── models/
│ └── textures/
├── include/
│ ├── engine/
│ ├── graphics/
│ ├── core/
│ └── platforms/
├── CMakeLists.txt
└── README.md
“`
## Source Code Implementation
### src/main.cpp
“`cpp
#include “engine/engine.h”
#include <iostream>
int main() {
try {
Engine engine;
engine.initialize();
engine.run();
engine.cleanup();
} catch (const std::exception& e) {
std::cerr << “Error: ” << e.what() << std::endl;
return -1;
}
return 0;
}
“`
### src/engine/engine.h
“`cpp
#ifndef ENGINE_H
#define ENGINE_H
#include <memory>
#include <string>
class Renderer;
class InputSystem;
class PhysicsSystem;
class Scene;
class Engine {
public:
Engine();
~Engine();
void initialize();
void run();
void cleanup();
private:
std::unique_ptr<Renderer> renderer_;
std::unique_ptr<InputSystem> input_system_;
std::unique_ptr<PhysicsSystem> physics_system_;
std::unique_ptr<Scene> scene_;
bool is_running_;
};
#endif // ENGINE_H
“`
### src/engine/engine.cpp
“`cpp
#include “engine.h”
#include “../graphics/renderer.h”
#include “../systems/input_system.h”
#include “../systems/physics_system.h”
#include “../core/scene.h”
#include “../platforms/amd/rocm_context.h”
#include “../platforms/opengl/opengl_context.h”
Engine::Engine() : is_running_(false) {}
Engine::~Engine() = default;
void Engine::initialize() {
// Initialize OpenGL context
auto* gl_context = new OpenGLContext();
gl_context->initialize();
// Initialize ROCm context for compute shaders
auto* rocm_context = new ROCmContext();
rocm_context->initialize();
// Create systems
renderer_ = std::make_unique<Renderer>();
input_system_ = std::make_unique<InputSystem>();
physics_system_ = std::make_unique<PhysicsSystem>();
scene_ = std::make_unique<Scene>();
is_running_ = true;
}
void Engine::run() {
while (is_running_) {
// Handle input
input_system_->update();
// Update physics
physics_system_->update();
// Render scene
renderer_->render(*scene_);
// Swap buffers (OpenGL)
// This would be called from OpenGL context
}
}
void Engine::cleanup() {
is_running_ = false;
// Cleanup resources
}
“`
### src/platforms/amd/rocm_context.h
“`cpp
#ifndef ROCM_CONTEXT_H
#define ROCM_CONTEXT_H
#include <hip/hip_runtime.h>
#include <vector>
class ROCmContext {
public:
ROCmContext();
~ROCmContext();
bool initialize();
void cleanup();
// Compute shader management
hipError_t createComputeShader(const std::string& kernel_source,
const std::string& kernel_name,
hipModule_t* module);
hipError_t launchKernel(hipModule_t module,
const std::string& kernel_name,
void** args,
int num_args,
dim3 grid_size,
dim3 block_size);
private:
bool is_initialized_;
hipContext_t context_;
hipDevice_t device_;
};
#endif // ROCM_CONTEXT_H
“`
### src/platforms/amd/rocm_context.cpp
“`cpp
#include “rocm_context.h”
#include <iostream>
#include <fstream>
#include <sstream>
ROCmContext::ROCmContext() : is_initialized_(false), context_(nullptr), device_(0) {}
ROCmContext::~ROCmContext() {
cleanup();
}
bool ROCmContext::initialize() {
hipError_t error;
// Initialize HIP runtime
error = hipInit(0);
if (error != hipSuccess) {
std::cerr << “Failed to initialize HIP: ” << hipGetErrorString(error) << std::endl;
return false;
}
// Get device count
int device_count = 0;
error = hipGetDeviceCount(&device_count);
if (error != hipSuccess || device_count == 0) {
std::cerr << “No HIP devices found” << std::endl;
return false;
}
// Set device to use
error = hipSetDevice(0);
if (error != hipSuccess) {
std::cerr << “Failed to set HIP device: ” << hipGetErrorString(error) << std::endl;
return false;
}
// Get device properties
hipDeviceProp_t props;
error = hipGetDeviceProperties(&props, 0);
if (error != hipSuccess) {
std::cerr << “Failed to get device properties: ” << hipGetErrorString(error) << std::endl;
return false;
}
std::cout << “Using HIP device: ” << props.name << std::endl;
is_initialized_ = true;
return true;
}
void ROCmContext::cleanup() {
if (is_initialized_) {
hipDeviceSynchronize();
hipDeviceReset();
is_initialized_ = false;
}
}
hipError_t ROCmContext::createComputeShader(const std::string& kernel_source,
const std::string& kernel_name,
hipModule_t* module) {
if (!is_initialized_) return hipErrorNotInitialized;
// Compile the kernel source
hipError_t error = hipModuleLoadData(module, kernel_source.c_str());
if (error != hipSuccess) {
std::cerr << “Failed to load HIP module: ” << hipGetErrorString(error) << std::endl;
return error;
}
return hipSuccess;
}
hipError_t ROCmContext::launchKernel(hipModule_t module,
const std::string& kernel_name,
void** args,
int num_args,
dim3 grid_size,
dim3 block_size) {
if (!is_initialized_) return hipErrorNotInitialized;
hipFunction_t kernel;
hipError_t error = hipModuleGetFunction(&kernel, module, kernel_name.c_str());
if (error != hipSuccess) {
std::cerr << “Failed to get HIP function: ” << hipGetErrorString(error) << std::endl;
return error;
}
// Launch kernel
error = hipModuleLaunchKernel(kernel,
grid_size.x, grid_size.y, grid_size.z,
block_size.x, block_size.y, block_size.z,
0, // shared memory size
nullptr, // stream
args,
nullptr);
if (error != hipSuccess) {
std::cerr << “Failed to launch HIP kernel: ” << hipGetErrorString(error) << std::endl;
}
return error;
}
“`
### src/platforms/opengl/opengl_context.h
“`cpp
#ifndef OPENGL_CONTEXT_H
#define OPENGL_CONTEXT_H
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <string>
class OpenGLContext {
public:
OpenGLContext();
~OpenGLContext();
bool initialize(int width = 1920, int height = 1080);
void cleanup();
void swapBuffers();
void clearScreen(float r, float g, float b, float a = 1.0f);
GLFWwindow* getWindow() const { return window_; }
private:
GLFWwindow* window_;
int width_, height_;
};
#endif // OPENGL_CONTEXT_H
“`
### src/platforms/opengl/opengl_context.cpp
“`cpp
#include “opengl_context.h”
#include <iostream>
OpenGLContext::OpenGLContext() : window_(nullptr), width_(1920), height_(1080) {}
OpenGLContext::~OpenGLContext() {
cleanup();
}
bool OpenGLContext::initialize(int width, int height) {
width_ = width;
height_ = height;
// Initialize GLFW
if (!glfwInit()) {
std::cerr << “Failed to initialize GLFW” << std::endl;
return false;
}
// Configure GLFW
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
// Create window
window_ = glfwCreateWindow(width_, height_, “Game Engine”, nullptr, nullptr);
if (!window_) {
std::cerr << “Failed to create GLFW window” << std::endl;
cleanup();
return false;
}
glfwMakeContextCurrent(window_);
// Initialize GLEW
if (glewInit() != GLEW_OK) {
std::cerr << “Failed to initialize GLEW” << std::endl;
cleanup();
return false;
}
// Enable depth testing and face culling
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
std::cout << “OpenGL initialized successfully” << std::endl;
return true;
}
void OpenGLContext::cleanup() {
if (window_) {
glfwDestroyWindow(window_);
window_ = nullptr;
}
glfwTerminate();
}
void OpenGLContext::swapBuffers() {
if (window_) {
glfwSwapBuffers(window_);
}
}
void OpenGLContext::clearScreen(float r, float g, float b, float a) {
glClearColor(r, g, b, a);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
“`
### src/graphics/renderer.h
“`cpp
#ifndef RENDERER_H
#define RENDERER_H
#include <memory>
#include <vector>
#include “../core/scene.h”
class Shader;
class Mesh;
class Texture;
class Renderer {
public:
Renderer();
~Renderer();
void render(const Scene& scene);
private:
void setupShaders();
void renderMesh(const Mesh& mesh, const Shader& shader);
std::vector<std::unique_ptr<Shader>> shaders_;
std::vector<std::unique_ptr<Mesh>> meshes_;
std::vector<std::unique_ptr<Texture>> textures_;
};
#endif // RENDERER_H
“`
### src/graphics/renderer.cpp
“`cpp
#include “renderer.h”
#include “shader.h”
#include “mesh.h”
#include “../core/scene.h”
#include “../platforms/opengl/opengl_context.h”
Renderer::Renderer() = default;
Renderer::~Renderer() = default;
void Renderer::render(const Scene& scene) {
// Clear screen
OpenGLContext* context = /* get current context */;
context->clearScreen(0.2f, 0.3f, 0.4f);
// Setup shaders
setupShaders();
// Render objects in scene
for (const auto& object : scene.getObjects()) {
if (object.mesh) {
renderMesh(*object.mesh, *shaders_[0]);
}
}
}
void Renderer::setupShaders() {
// Load and compile shaders
shaders_.clear();
shaders_.push_back(std::make_unique<Shader>(“assets/shaders/vertex.glsl”,
“assets/shaders/fragment.glsl”));
}
void Renderer::renderMesh(const Mesh& mesh, const Shader& shader) {
// Bind shader
shader.use();
// Set uniforms (model, view, projection matrices)
// This would be done through the OpenGL context
// Bind VAO and draw
glBindVertexArray(mesh.getVAO());
glDrawElements(GL_TRIANGLES, mesh.getIndexCount(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
“`
### src/graphics/shader.h
“`cpp
#ifndef SHADER_H
#define SHADER_H
#include <string>
#include <unordered_map>
#include <GL/glew.h>
class Shader {
public:
Shader(const std::string& vertex_path, const std::string& fragment_path);
~Shader();
void use() const;
void setUniform(const std::string& name, int value) const;
void setUniform(const std::string& name, float value) const;
void setUniform(const std::string& name, const float* values, int count) const;
private:
GLuint program_;
GLuint vertex_shader_;
GLuint fragment_shader_;
bool compileShader(const std::string& path, GLenum type, GLuint* shader);
bool linkProgram();
};
#endif // SHADER_H
“`
### src/graphics/shader.cpp
“`cpp
#include “shader.h”
#include <iostream>
#include <fstream>
#include <sstream>
Shader::Shader(const std::string& vertex_path, const std::string& fragment_path) {
// Compile shaders
compileShader(vertex_path, GL_VERTEX_SHADER, &vertex_shader_);
compileShader(fragment_path, GL_FRAGMENT_SHADER, &fragment_shader_);
// Link program
linkProgram();
}
Shader::~Shader() {
glDeleteProgram(program_);
glDeleteShader(vertex_shader_);
glDeleteShader(fragment_shader_);
}
void Shader::use() const {
glUseProgram(program_);
}
void Shader::setUniform(const std::string& name, int value) const {
glUniform1i(glGetUniformLocation(program_, name.c_str()), value);
}
void Shader::setUniform(const std::string& name, float value) const {
glUniform1f(glGetUniformLocation(program_, name.c_str()), value);
}
void Shader::setUniform(const std::string& name, const float* values, int count) const {
glUniform1fv(glGetUniformLocation(program_, name.c_str()), count, values);
}
bool Shader::compileShader(const std::string& path, GLenum type, GLuint* shader) {
std::ifstream file(path);
if (!file.is_open()) {
std::cerr << “Failed to open shader file: ” << path << std::endl;
return false;
}
std::stringstream buffer;
buffer << file.rdbuf();
std::string source = buffer.str();
const char* src_ptr = source.c_str();
*shader = glCreateShader(type);
glShaderSource(*shader, 1, &src_ptr, nullptr);
glCompileShader(*shader);
// Check for compilation errors
GLint success;
glGetShaderiv(*shader, GL_COMPILE_STATUS, &success);
if (!success) {
char info_log[512];
glGetShaderInfoLog(*shader, 512, nullptr, info_log);
std::cerr << “Shader compilation error: ” << info_log << std::endl;
return false;
}
return true;
}
bool Shader::linkProgram() {
program_ = glCreateProgram();
glAttachShader(program_, vertex_shader_);
glAttachShader(program_, fragment_shader_);
glLinkProgram(program_);
// Check for linking errors
GLint success;
glGetProgramiv(program_, GL_LINK_STATUS, &success);
if (!success) {
char info_log[512];
glGetProgramInfoLog(program_, 512, nullptr, info_log);
std::cerr << “Shader program linking error: ” << info_log << std::endl;
return false;
}
return true;
}
“`
### assets/shaders/vertex.glsl
“`glsl
#version 450 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
out vec2 TexCoord;
out vec3 Normal;
out vec3 FragPos;
void main() {
FragPos = vec3(model * vec4(aPos, 1.0));
Normal = mat3(transpose(inverse(model)))) * aNormal;
TexCoord = aTexCoord;
gl_Position = projection * view * vec4(FragPos, 1.0);
}
“`
### assets/shaders/fragment.glsl
“`glsl
#version 450 core
in vec2 TexCoord;
in vec3 Normal;
in vec3 FragPos;
out vec4 FragColor;
uniform vec3 lightPos;
uniform vec3 viewPos;
uniform vec3 lightColor;
uniform sampler2D diffuseTexture;
void main() {
// Ambient lighting
float ambientStrength = 0.1;
vec3 ambient = ambientStrength * lightColor;
// Diffuse lighting
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos – FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;
// Combine lighting
vec3 result = ambient + diffuse;
vec4 texColor = texture(diffuseTexture, TexCoord);
FragColor = vec4(result * texColor.rgb, texColor.a);
}
“`
### CMakeLists.txt (example)
“`cmake
cmake_minimum_required(VERSION 3.10)
project(GameEngine)
set(CMAKE_CXX_STANDARD 17)
# Find required packages
find_package(glfw3 REQUIRED)
find_package(GLEW REQUIRED)
find_package(OpenGL REQUIRED)
# Add executable
add_executable(${PROJECT_NAME}
src/main.cpp
src/graphics/shader.cpp
src/graphics/renderer.cpp
src/platforms/opengl/opengl_context.cpp
src/graphics/mesh.cpp
src/core/scene.cpp
)
# Link libraries
target_link_libraries(${PROJECT_NAME}
glfw
GLEW::GLEW
OpenGL::GL
)
“`
This implementation provides a basic framework for a 3D game engine that integrates OpenGL with AMD’s ROCm platform, using modern C++17 features and following good software engineering practices. The code is organized into logical modules with clear separation of concerns between rendering, shaders, graphics objects, and core systems.