3 Commits

Author SHA1 Message Date
1089861aa3 feat: implement libraries in main 2025-10-13 18:25:55 +02:00
49219fc597 feat: assignment 3 2025-10-13 18:23:01 +02:00
db6e548476 feat: add assignment 3 files 2025-10-13 17:51:00 +02:00
18 changed files with 7562 additions and 65 deletions

View File

@@ -1,5 +0,0 @@
varying vec3 fcolor;
void main() {
gl_FragColor = vec4(fcolor, 1);
}

View File

@@ -0,0 +1,12 @@
uniform int useTexture;
uniform sampler2D texSampler;
varying vec3 fcolor;
varying vec2 ftexcoord;
void main() {
if (useTexture == 1) {
gl_FragColor = texture2D(texSampler, ftexcoord);
} else {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
}

View File

@@ -1,9 +1,12 @@
uniform mat4 mvp;
attribute vec3 vpos;
attribute vec3 vcolor;
attribute vec2 vtexcoord;
varying vec3 fcolor;
varying vec2 ftexcoord;
void main() {
gl_Position = mvp * vec4(vpos, 1);
fcolor = vcolor;
ftexcoord = vtexcoord;
}

BIN
data/textures/front.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
data/textures/top.png (Stored with Git LFS) Normal file

Binary file not shown.

7187
lib/stb/stb_image.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -10,10 +10,12 @@
#include "buffer.h"
#include "camera.h"
#include "logger.h"
#include "material.h"
#include "mesh.h"
#include "model.h"
#include "shader.h"
#include "state.h"
#include "texture.h"
#include "vertex.h"
#include "world.h"
@@ -83,8 +85,8 @@ void Engine::initialize()
// Initialize default shader
Logger::info("Loading default shaders...");
state::default_shader =
std::make_shared<Shader>("data/vertex.glsl", "data/fragment.glsl");
state::default_shader = std::make_shared<Shader>(
"data/shaders/vertex.glsl", "data/shaders/fragment.glsl");
if (std::strlen(state::default_shader->error()) > 0) {
Logger::error(sstr("Failed to initialize shaders: ",
state::default_shader->error()));
@@ -140,9 +142,10 @@ void Engine::setup()
world_ = std::make_unique<World>();
Logger::info("World created");
// Create camera
// Create camera at position (0, 1, 3) with -20 degrees X rotation
camera_ = std::make_shared<Camera>();
camera_->set_position(glm::vec3(0.0f, 0.0f, 6.0f));
camera_->set_position(glm::vec3(0.0f, 1.0f, 3.0f));
camera_->set_rotation(glm::vec3(glm::radians(-20.0f), 0.0f, 0.0f));
camera_->set_projection(
glm::perspective(glm::radians(45.0f),
static_cast<float>(screen_width_)
@@ -153,31 +156,79 @@ void Engine::setup()
world_->add_entity(camera_);
Logger::info("Camera created and added to world");
// Create triangle mesh
std::vector<Vertex> vertices = {
{{0.0f, 0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}},
{{-0.5f, -0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}},
{{0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}}};
std::vector<uint16_t> indices = {0, 1, 2};
// Load textures
auto top_texture = Texture::load("data/textures/top.png");
auto front_texture = Texture::load("data/textures/front.png");
auto buffer = std::make_shared<Buffer>(vertices, indices);
// Create cube mesh with two buffers
mesh_ = std::make_shared<Mesh>();
mesh_->add_buffer(buffer);
Logger::info(sstr("Mesh created with ", vertices.size(),
" vertices and ", indices.size(), " indices"));
// Create 9 models in a 3x3 grid
for (int row = 0; row < 3; ++row) {
for (int col = 0; col < 3; ++col) {
// Buffer 1: Top and bottom faces
std::vector<Vertex> top_bottom_vertices = {
// Top face (y = 0.5)
{{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
{{0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}},
{{0.5f, 0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}},
{{-0.5f, 0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},
// Bottom face (y = -0.5)
{{-0.5f, -0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
{{0.5f, -0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}},
{{0.5f, -0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}},
{{-0.5f, -0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}};
std::vector<uint16_t> top_bottom_indices = {// Top face
0, 1, 2, 0, 2, 3,
// Bottom face
4, 6, 5, 4, 7, 6};
auto top_bottom_buffer =
std::make_shared<Buffer>(top_bottom_vertices, top_bottom_indices);
Material top_material(top_texture);
mesh_->add_buffer(top_bottom_buffer, top_material);
// Buffer 2: Front, back, left, right faces
std::vector<Vertex> side_vertices = {
// Front face (z = 0.5)
{{-0.5f, -0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
{{0.5f, -0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}},
{{0.5f, 0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}},
{{-0.5f, 0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},
// Back face (z = -0.5)
{{-0.5f, -0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}},
{{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}},
{{0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},
{{0.5f, -0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
// Left face (x = -0.5)
{{-0.5f, -0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
{{-0.5f, -0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}},
{{-0.5f, 0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}},
{{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},
// Right face (x = 0.5)
{{0.5f, -0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}},
{{0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}},
{{0.5f, 0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},
{{0.5f, -0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}};
std::vector<uint16_t> side_indices = {// Front face
0, 1, 2, 0, 2, 3,
// Back face
4, 5, 6, 4, 6, 7,
// Left face
8, 9, 10, 8, 10, 11,
// Right face
12, 13, 14, 12, 14, 15};
auto side_buffer =
std::make_shared<Buffer>(side_vertices, side_indices);
Material side_material(front_texture);
mesh_->add_buffer(side_buffer, side_material);
Logger::info("Cube mesh created with two buffers");
// Create model at origin
auto model = std::make_shared<Model>(mesh_);
model->set_position(
glm::vec3(-3.0f + static_cast<float>(col) * 3.0f,
0.0f, static_cast<float>(-row) * 3.0f));
model->set_position(glm::vec3(0.0f, 0.0f, 0.0f));
models_.push_back(model);
world_->add_entity(model);
}
}
Logger::info(sstr("Created ", models_.size(), " models in scene"));
Logger::info("Scene setup complete");
}

View File

@@ -2,6 +2,9 @@
#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
#endif*/
#define STB_IMAGE_IMPLEMENTATION
#include "../lib/stb/stb_image.h"
#include "engine.h"
int main()

61
src/material.cpp Normal file
View File

@@ -0,0 +1,61 @@
#include "material.h"
#include "../lib/glm/glm.hpp"
#include "shader.h"
#include "state.h"
#include "texture.h"
Material::Material(const std::shared_ptr<Texture>& tex,
const std::shared_ptr<Shader>& shader)
: shader_(shader), texture_(tex)
{
}
const std::shared_ptr<Shader>& Material::shader() const
{
return shader_ ? shader_ : state::default_shader;
}
std::shared_ptr<Shader>& Material::shader()
{
return shader_ ? shader_ : state::default_shader;
}
void Material::set_shader(const std::shared_ptr<Shader>& shader)
{
shader_ = shader;
}
void Material::prepare()
{
// Activate shader
std::shared_ptr<Shader> active_shader = shader();
if (!active_shader)
return;
active_shader->use();
// Set uniform variables
const glm::mat4 mvp =
state::projection_matrix * state::view_matrix * state::model_matrix;
const int mvp_loc = active_shader->uniform_location("mvp");
Shader::set_mat4(mvp_loc, mvp);
// Set texture-related uniforms
const int use_texture_loc =
active_shader->uniform_location("useTexture");
if (texture_) {
Shader::set_int(use_texture_loc, 1);
const int sampler_loc =
active_shader->uniform_location("texSampler");
Shader::set_int(sampler_loc, 0); // Texture unit 0
} else {
Shader::set_int(use_texture_loc, 0);
}
// Bind texture if available
if (texture_) {
texture_->bind();
}
}

35
src/material.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef MATERIAL_H_
#define MATERIAL_H_
#include <memory>
class Shader;
class Texture;
class Material {
public:
Material(const std::shared_ptr<Texture>& tex = nullptr,
const std::shared_ptr<Shader>& shader = nullptr);
~Material() = default;
[[nodiscard]] const std::shared_ptr<Shader>& shader() const;
[[nodiscard]] std::shared_ptr<Shader>& shader();
void set_shader(const std::shared_ptr<Shader>& shader);
[[nodiscard]] const std::shared_ptr<Texture>& texture() const
{
return texture_;
}
void set_texture(const std::shared_ptr<Texture>& tex)
{
texture_ = tex;
}
void prepare();
private:
std::shared_ptr<Shader> shader_;
std::shared_ptr<Texture> texture_;
};
#endif // MATERIAL_H_

View File

@@ -1,33 +1,20 @@
#include "mesh.h"
#include "buffer.h"
#include "shader.h"
#include "state.h"
#include "material.h"
void Mesh::add_buffer(const std::shared_ptr<Buffer>& buffer,
const std::shared_ptr<Shader>& shader)
const Material& material)
{
buffers_.push_back(buffer);
shaders_.push_back(shader);
materials_.push_back(material);
}
void Mesh::draw()
{
// Calculate MVP matrix
glm::mat4 mvp = state::projection_matrix * state::view_matrix
* state::model_matrix;
// Draw each buffer with its shader
// Draw each buffer with its material
for (size_t i = 0; i < buffers_.size(); ++i) {
// Use buffer's shader if available, otherwise use default
// shader
std::shared_ptr<Shader> shader =
shaders_[i] ? shaders_[i] : state::default_shader;
if (shader) {
shader->use();
int mvpLoc = shader->uniform_location("mvp");
Shader::set_mat4(mvpLoc, mvp);
buffers_[i]->draw(*shader);
}
materials_[i].prepare();
buffers_[i]->draw(*materials_[i].shader());
}
}

View File

@@ -4,8 +4,9 @@
#include <memory>
#include <vector>
#include "material.h"
class Buffer;
class Shader;
class Mesh {
public:
@@ -13,7 +14,7 @@ public:
~Mesh() = default;
void add_buffer(const std::shared_ptr<Buffer>& buffer,
const std::shared_ptr<Shader>& shader = nullptr);
const Material& material);
[[nodiscard]] size_t num_buffers() const
{
@@ -28,11 +29,20 @@ public:
return buffers_[index];
}
[[nodiscard]] const Material& material(size_t index) const
{
return materials_[index];
}
[[nodiscard]] Material& material(size_t index)
{
return materials_[index];
}
void draw();
private:
std::vector<std::shared_ptr<Buffer>> buffers_;
std::vector<std::shared_ptr<Shader>> shaders_;
std::vector<Material> materials_;
};
#endif // MESH_H_

View File

@@ -10,11 +10,11 @@
#include "logger.h"
#include "vertex.h"
Shader::Shader(const std::string& vertexPath, const std::string& fragmentPath)
Shader::Shader(const std::string& vertex_path, const std::string& fragment_path)
{
const uint32_t vshader = compile_shader(GL_VERTEX_SHADER, vertexPath);
const uint32_t vshader = compile_shader(GL_VERTEX_SHADER, vertex_path);
const uint32_t fshader =
compile_shader(GL_FRAGMENT_SHADER, fragmentPath);
compile_shader(GL_FRAGMENT_SHADER, fragment_path);
if (vshader == 0 || fshader == 0) {
program_id_ = 0;
@@ -65,6 +65,14 @@ void Shader::setup_attribs() const
loc, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
reinterpret_cast<const void*>(offsetof(Vertex, color)));
}
loc = glGetAttribLocation(program_id_, "vtexcoord");
if (loc != -1) {
glEnableVertexAttribArray(loc);
glVertexAttribPointer(
loc, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
reinterpret_cast<const void*>(offsetof(Vertex, tex_coord)));
}
}
int Shader::uniform_location(const char* key) const
@@ -141,9 +149,8 @@ uint32_t Shader::compile_shader(uint32_t type, const std::string& source_path)
char buffer[1024];
glGetShaderInfoLog(shader, sizeof(buffer), nullptr, buffer);
error_ = buffer;
Logger::error(
sstr("Shader compilation failed (", shader_type_name, "): ",
buffer));
Logger::error(sstr("Shader compilation failed (",
shader_type_name, "): ", buffer));
glDeleteShader(shader);
return 0;
}

81
src/texture.cpp Normal file
View File

@@ -0,0 +1,81 @@
#include "texture.h"
#include <algorithm>
#include "../lib/glew/GL/glew.h"
#include "../lib/glfw/glfw3.h"
#include "../lib/stb/stb_image.h"
#include "logger.h"
Texture::Texture(uint32_t id, const glm::ivec2& size)
: id_(id), size_(size)
{
}
Texture::~Texture()
{
if (id_ != 0) {
glDeleteTextures(1, &id_);
Logger::info(sstr("Texture ", id_, " destroyed"));
}
}
std::shared_ptr<Texture> Texture::load(const char* filename)
{
Logger::info(sstr("Loading texture: ", filename));
// Load image
int width, height, channels;
stbi_uc* pixels =
stbi_load(filename, &width, &height, &channels, STBI_rgb_alpha);
if (!pixels) {
Logger::error(sstr("Failed to load texture: ", filename, " - ",
stbi_failure_reason()));
return nullptr;
}
// Flip image vertically (OpenGL expects bottom row first)
const int row_size = width * 4; // 4 channels (RGBA)
auto* row_buffer = new stbi_uc[row_size];
for (int y = 0; y < height / 2; ++y) {
stbi_uc* row1 = pixels + y * row_size;
stbi_uc* row2 = pixels + (height - 1 - y) * row_size;
std::copy(row1, row1 + row_size, row_buffer);
std::copy(row2, row2 + row_size, row1);
std::copy(row_buffer, row_buffer + row_size, row2);
}
delete[] row_buffer;
// Generate OpenGL texture
GLuint texture_id;
glGenTextures(1, &texture_id);
glBindTexture(GL_TEXTURE_2D, texture_id);
// Upload texture data
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,
GL_UNSIGNED_BYTE, pixels);
// Set trilinear filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Generate mipmaps
glGenerateMipmap(GL_TEXTURE_2D);
// Free image data
stbi_image_free(pixels);
Logger::info(sstr("Texture loaded successfully: ", filename, " (",
width, "x", height, ", ", channels, " channels)"));
return std::shared_ptr<Texture>(
new Texture(texture_id, glm::ivec2(width, height)));
}
void Texture::bind() const
{
glBindTexture(GL_TEXTURE_2D, id_);
}

35
src/texture.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef TEXTURE_H_
#define TEXTURE_H_
#include <cstdint>
#include <memory>
#include <string>
#include "../lib/glm/glm.hpp"
class Texture {
public:
Texture() = delete;
~Texture();
static std::shared_ptr<Texture> load(const char* filename);
[[nodiscard]] uint32_t id() const
{
return id_;
}
[[nodiscard]] const glm::ivec2& size() const
{
return size_;
}
void bind() const;
private:
Texture(uint32_t id, const glm::ivec2& size);
uint32_t id_;
glm::ivec2 size_;
};
#endif // TEXTURE_H_

View File

@@ -6,6 +6,7 @@
struct Vertex {
glm::vec3 position{0.0f, 0.0f, 0.0f};
glm::vec3 color{0.0f, 0.0f, 0.0f};
glm::vec2 tex_coord{0.0f, 0.0f};
};
#endif // VERTEX_H_

View File

@@ -159,15 +159,18 @@
<ClInclude Include="lib\glew\GL\glew.h" />
<ClInclude Include="lib\glfw\glfw3.h" />
<ClInclude Include="lib\glfw\glfw3native.h" />
<ClInclude Include="lib\stb\stb_image.h" />
<ClInclude Include="src\buffer.h" />
<ClInclude Include="src\camera.h" />
<ClInclude Include="src\engine.h" />
<ClInclude Include="src\entity.h" />
<ClInclude Include="src\logger.h" />
<ClInclude Include="src\material.h" />
<ClInclude Include="src\mesh.h" />
<ClInclude Include="src\model.h" />
<ClInclude Include="src\shader.h" />
<ClInclude Include="src\state.h" />
<ClInclude Include="src\texture.h" />
<ClInclude Include="src\vertex.h" />
<ClInclude Include="src\world.h" />
</ItemGroup>
@@ -179,15 +182,17 @@
<ClCompile Include="src\entity.cpp" />
<ClCompile Include="src\logger.cpp" />
<ClCompile Include="src\main.cpp" />
<ClCompile Include="src\material.cpp" />
<ClCompile Include="src\mesh.cpp" />
<ClCompile Include="src\model.cpp" />
<ClCompile Include="src\shader.cpp" />
<ClCompile Include="src\texture.cpp" />
<ClCompile Include="src\vertex.cpp" />
<ClCompile Include="src\world.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="data\fragment.glsl" />
<None Include="data\vertex.glsl" />
<None Include="data\shaders\fragment.glsl" />
<None Include="data\shaders\vertex.glsl" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

View File

@@ -19,6 +19,9 @@
<Filter Include="glew">
<UniqueIdentifier>{296e1ba9-28bc-4e0b-8d4e-413551edca96}</UniqueIdentifier>
</Filter>
<Filter Include="stb">
<UniqueIdentifier>{e3c5a7f2-1d4b-4a9c-8f2e-9b5c8d7a6e4f}</UniqueIdentifier>
</Filter>
<Filter Include="Shaders">
<UniqueIdentifier>{0628083b-a31c-4825-822c-11b6f933e7bd}</UniqueIdentifier>
</Filter>
@@ -33,6 +36,9 @@
<ClInclude Include="lib\glew\GL\glew.h">
<Filter>glew</Filter>
</ClInclude>
<ClInclude Include="lib\stb\stb_image.h">
<Filter>stb</Filter>
</ClInclude>
<ClInclude Include="src\vertex.h">
<Filter>Source Files</Filter>
</ClInclude>
@@ -66,6 +72,12 @@
<ClInclude Include="src\logger.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="src\material.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="src\texture.h">
<Filter>Source Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\main.cpp">
@@ -104,12 +116,18 @@
<ClCompile Include="src\logger.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\material.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\texture.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="data\fragment.glsl">
<None Include="data\shaders\fragment.glsl">
<Filter>Shaders</Filter>
</None>
<None Include="data\vertex.glsl">
<None Include="data\shaders\vertex.glsl">
<Filter>Shaders</Filter>
</None>
</ItemGroup>