2821 lines
78 KiB
Markdown
2821 lines
78 KiB
Markdown
### 文件:src/basic/math.cpp
|
||
|
||
```cpp
|
||
#include "basic/math.h"
|
||
|
||
namespace are {
|
||
|
||
Mat4 MathUtils::perspective(float fov, float aspect, float near, float far) {
|
||
return glm::perspective(fov, aspect, near, far);
|
||
}
|
||
|
||
Mat4 MathUtils::look_at(const Vec3& eye, const Vec3& center, const Vec3& up) {
|
||
return glm::lookAt(eye, center, up);
|
||
}
|
||
|
||
Vec3 MathUtils::normalize(const Vec3& v) {
|
||
return glm::normalize(v);
|
||
}
|
||
|
||
float MathUtils::dot(const Vec3& a, const Vec3& b) {
|
||
return glm::dot(a, b);
|
||
}
|
||
|
||
Vec3 MathUtils::cross(const Vec3& a, const Vec3& b) {
|
||
return glm::cross(a, b);
|
||
}
|
||
|
||
Vec3 MathUtils::reflect(const Vec3& incident, const Vec3& normal) {
|
||
return glm::reflect(incident, normal);
|
||
}
|
||
|
||
const float* MathUtils::value_ptr(const Mat4& mat) {
|
||
return glm::value_ptr(mat);
|
||
}
|
||
|
||
} // namespace are
|
||
```
|
||
|
||
### 文件:src/core/bvh.cpp
|
||
|
||
```cpp
|
||
#include "core/bvh.h"
|
||
#include "utils/logger.h"
|
||
#include "basic/constants.h"
|
||
#include <algorithm>
|
||
#include <limits>
|
||
|
||
namespace are {
|
||
|
||
// AABB implementation
|
||
void AABB::expand(const Vec3& point) {
|
||
min_ = glm::min(min_, point);
|
||
max_ = glm::max(max_, point);
|
||
}
|
||
|
||
void AABB::expand(const AABB& other) {
|
||
min_ = glm::min(min_, other.min_);
|
||
max_ = glm::max(max_, other.max_);
|
||
}
|
||
|
||
float AABB::surface_area() const {
|
||
Vec3 extent = max_ - min_;
|
||
return 2.0f * (extent.x * extent.y + extent.y * extent.z + extent.z * extent.x);
|
||
}
|
||
|
||
bool AABB::is_valid() const {
|
||
return min_.x <= max_.x && min_.y <= max_.y && min_.z <= max_.z;
|
||
}
|
||
|
||
// Triangle implementation
|
||
AABB Triangle::get_bounds() const {
|
||
AABB bounds(v0_, v0_);
|
||
bounds.expand(v1_);
|
||
bounds.expand(v2_);
|
||
return bounds;
|
||
}
|
||
|
||
Vec3 Triangle::get_centroid() const {
|
||
return (v0_ + v1_ + v2_) / 3.0f;
|
||
}
|
||
|
||
// BVH implementation
|
||
BVH::BVH() {
|
||
}
|
||
|
||
BVH::~BVH() {
|
||
clear();
|
||
}
|
||
|
||
bool BVH::build(const std::vector<std::shared_ptr<Mesh>>& meshes) {
|
||
clear();
|
||
|
||
Logger::info("Building BVH...");
|
||
|
||
// Extract all triangles from meshes
|
||
for (const auto& mesh : meshes) {
|
||
const auto& vertices = mesh->get_vertices();
|
||
const auto& indices = mesh->get_indices();
|
||
uint material_id = mesh->get_material();
|
||
Mat4 transform = mesh->get_transform();
|
||
|
||
for (size_t i = 0; i < indices.size(); i += 3) {
|
||
Triangle tri;
|
||
|
||
// Transform vertices
|
||
Vec4 v0 = transform * Vec4(vertices[indices[i]].position_, 1.0f);
|
||
Vec4 v1 = transform * Vec4(vertices[indices[i + 1]].position_, 1.0f);
|
||
Vec4 v2 = transform * Vec4(vertices[indices[i + 2]].position_, 1.0f);
|
||
|
||
tri.v0_ = Vec3(v0) / v0.w;
|
||
tri.v1_ = Vec3(v1) / v1.w;
|
||
tri.v2_ = Vec3(v2) / v2.w;
|
||
|
||
// Transform normals
|
||
Mat3 normal_matrix = glm::transpose(glm::inverse(Mat3(transform)));
|
||
tri.n0_ = glm::normalize(normal_matrix * vertices[indices[i]].normal_);
|
||
tri.n1_ = glm::normalize(normal_matrix * vertices[indices[i + 1]].normal_);
|
||
tri.n2_ = glm::normalize(normal_matrix * vertices[indices[i + 2]].normal_);
|
||
|
||
// Copy UVs
|
||
tri.uv0_ = vertices[indices[i]].texcoord_;
|
||
tri.uv1_ = vertices[indices[i + 1]].texcoord_;
|
||
tri.uv2_ = vertices[indices[i + 2]].texcoord_;
|
||
|
||
tri.material_id_ = material_id;
|
||
|
||
triangles_.push_back(tri);
|
||
}
|
||
}
|
||
|
||
if (triangles_.empty()) {
|
||
Logger::warning("No triangles to build BVH");
|
||
return false;
|
||
}
|
||
|
||
// Initialize triangle indices
|
||
triangle_indices_.resize(triangles_.size());
|
||
for (size_t i = 0; i < triangles_.size(); ++i) {
|
||
triangle_indices_[i] = static_cast<uint>(i);
|
||
}
|
||
|
||
// Reserve space for nodes (estimate)
|
||
nodes_.reserve(triangles_.size() * 2);
|
||
|
||
// Create root node
|
||
nodes_.emplace_back();
|
||
|
||
// Build BVH recursively
|
||
build_recursive_(0, 0, static_cast<uint>(triangles_.size()));
|
||
|
||
Logger::info("BVH built: " + std::to_string(nodes_.size()) + " nodes, " +
|
||
std::to_string(triangles_.size()) + " triangles");
|
||
|
||
return true;
|
||
}
|
||
|
||
void BVH::build_recursive_(uint node_idx, uint first_prim, uint prim_count) {
|
||
BVHNode& node = nodes_[node_idx];
|
||
|
||
// Calculate bounds
|
||
AABB bounds = calculate_bounds_(first_prim, prim_count);
|
||
node.aabb_min_ = bounds.min_;
|
||
node.aabb_max_ = bounds.max_;
|
||
|
||
// Leaf node threshold
|
||
const uint LEAF_SIZE = 4;
|
||
|
||
if (prim_count <= LEAF_SIZE) {
|
||
// Create leaf node
|
||
node.left_first_ = first_prim;
|
||
node.count_ = prim_count;
|
||
return;
|
||
}
|
||
|
||
// Find best split
|
||
int axis;
|
||
float split_pos;
|
||
float split_cost = find_best_split_(first_prim, prim_count, axis, split_pos);
|
||
|
||
// Check if split is beneficial
|
||
float no_split_cost = prim_count * bounds.surface_area();
|
||
if (split_cost >= no_split_cost) {
|
||
// Create leaf node
|
||
node.left_first_ = first_prim;
|
||
node.count_ = prim_count;
|
||
return;
|
||
}
|
||
|
||
// Partition primitives
|
||
uint mid = first_prim;
|
||
for (uint i = first_prim; i < first_prim + prim_count; ++i) {
|
||
Triangle& tri = triangles_[triangle_indices_[i]];
|
||
float centroid = tri.get_centroid()[axis];
|
||
|
||
if (centroid < split_pos) {
|
||
std::swap(triangle_indices_[i], triangle_indices_[mid]);
|
||
mid++;
|
||
}
|
||
}
|
||
|
||
// Ensure we have primitives on both sides
|
||
if (mid == first_prim || mid == first_prim + prim_count) {
|
||
mid = first_prim + prim_count / 2;
|
||
}
|
||
|
||
// Create interior node
|
||
uint left_count = mid - first_prim;
|
||
uint right_count = prim_count - left_count;
|
||
|
||
node.left_first_ = static_cast<uint>(nodes_.size());
|
||
node.count_ = 0;
|
||
|
||
// Create child nodes
|
||
nodes_.emplace_back();
|
||
nodes_.emplace_back();
|
||
|
||
// Recursively build children
|
||
build_recursive_(node.left_first_, first_prim, left_count);
|
||
build_recursive_(node.left_first_ + 1, mid, right_count);
|
||
}
|
||
|
||
float BVH::find_best_split_(uint first_prim, uint prim_count, int& axis, float& split_pos) {
|
||
float best_cost = std::numeric_limits<float>::max();
|
||
|
||
AABB centroid_bounds = calculate_centroid_bounds_(first_prim, prim_count);
|
||
|
||
// Try each axis
|
||
for (int a = 0; a < 3; ++a) {
|
||
float extent = centroid_bounds.max_[a] - centroid_bounds.min_[a];
|
||
if (extent < EPSILON) continue;
|
||
|
||
// Try multiple split positions
|
||
const int NUM_BINS = 16;
|
||
for (int i = 1; i < NUM_BINS; ++i) {
|
||
float t = static_cast<float>(i) / NUM_BINS;
|
||
float pos = centroid_bounds.min_[a] + t * extent;
|
||
|
||
// Count primitives and calculate bounds for each side
|
||
AABB left_bounds, right_bounds;
|
||
uint left_count = 0, right_count = 0;
|
||
|
||
for (uint j = first_prim; j < first_prim + prim_count; ++j) {
|
||
Triangle& tri = triangles_[triangle_indices_[j]];
|
||
float centroid = tri.get_centroid()[a];
|
||
|
||
if (centroid < pos) {
|
||
left_bounds.expand(tri.get_bounds());
|
||
left_count++;
|
||
} else {
|
||
right_bounds.expand(tri.get_bounds());
|
||
right_count++;
|
||
}
|
||
}
|
||
|
||
// Calculate SAH cost
|
||
if (left_count == 0 || right_count == 0) continue;
|
||
|
||
float cost = left_count * left_bounds.surface_area() +
|
||
right_count * right_bounds.surface_area();
|
||
|
||
if (cost < best_cost) {
|
||
best_cost = cost;
|
||
axis = a;
|
||
split_pos = pos;
|
||
}
|
||
}
|
||
}
|
||
|
||
return best_cost;
|
||
}
|
||
|
||
AABB BVH::calculate_bounds_(uint first_prim, uint prim_count) {
|
||
AABB bounds{Vec3(std::numeric_limits<float>::max()),
|
||
Vec3(std::numeric_limits<float>::lowest())};
|
||
|
||
for (uint i = first_prim; i < first_prim + prim_count; ++i) {
|
||
Triangle& tri = triangles_[triangle_indices_[i]];
|
||
bounds.expand(tri.get_bounds());
|
||
}
|
||
|
||
return bounds;
|
||
}
|
||
|
||
AABB BVH::calculate_centroid_bounds_(uint first_prim, uint prim_count) {
|
||
AABB bounds{Vec3(std::numeric_limits<float>::max()),
|
||
Vec3(std::numeric_limits<float>::lowest())};
|
||
|
||
for (uint i = first_prim; i < first_prim + prim_count; ++i) {
|
||
Triangle& tri = triangles_[triangle_indices_[i]];
|
||
bounds.expand(tri.get_centroid());
|
||
}
|
||
|
||
return bounds;
|
||
}
|
||
|
||
bool BVH::upload_to_gpu(Buffer& node_buffer, Buffer& triangle_buffer) {
|
||
if (nodes_.empty() || triangles_.empty()) {
|
||
Logger::error("Cannot upload empty BVH to GPU");
|
||
return false;
|
||
}
|
||
|
||
// Reorder triangles according to BVH layout
|
||
std::vector<Triangle> ordered_triangles;
|
||
ordered_triangles.reserve(triangles_.size());
|
||
|
||
for (uint idx : triangle_indices_) {
|
||
ordered_triangles.push_back(triangles_[idx]);
|
||
}
|
||
|
||
// Upload nodes
|
||
if (!node_buffer.create(BufferType::SHADER_STORAGE_BUFFER,
|
||
nodes_.size() * sizeof(BVHNode),
|
||
nodes_.data(),
|
||
BufferUsage::STATIC_DRAW)) {
|
||
Logger::error("Failed to upload BVH nodes to GPU");
|
||
return false;
|
||
}
|
||
|
||
// Upload triangles
|
||
if (!triangle_buffer.create(BufferType::SHADER_STORAGE_BUFFER,
|
||
ordered_triangles.size() * sizeof(Triangle),
|
||
ordered_triangles.data(),
|
||
BufferUsage::STATIC_DRAW)) {
|
||
Logger::error("Failed to upload BVH triangles to GPU");
|
||
return false;
|
||
}
|
||
|
||
Logger::info("BVH uploaded to GPU successfully");
|
||
return true;
|
||
}
|
||
|
||
void BVH::clear() {
|
||
nodes_.clear();
|
||
triangles_.clear();
|
||
triangle_indices_.clear();
|
||
}
|
||
|
||
} // namespace are
|
||
```
|
||
|
||
### 文件:src/core/gbuffer.cpp
|
||
|
||
```cpp
|
||
#include "core/gbuffer.h"
|
||
#include "utils/logger.h"
|
||
#include <glad/glad.h>
|
||
|
||
namespace are {
|
||
|
||
GBuffer::GBuffer(uint width, uint height)
|
||
: width_(width)
|
||
, height_(height)
|
||
, fbo_(INVALID_HANDLE)
|
||
, depth_texture_(INVALID_HANDLE)
|
||
, initialized_(false) {
|
||
for (int i = 0; i < GBUFFER_COUNT; ++i) {
|
||
textures_[i] = INVALID_HANDLE;
|
||
}
|
||
}
|
||
|
||
GBuffer::~GBuffer() {
|
||
release();
|
||
}
|
||
|
||
bool GBuffer::initialize() {
|
||
if (initialized_) {
|
||
Logger::warning("GBuffer already initialized");
|
||
return true;
|
||
}
|
||
|
||
// Create framebuffer
|
||
glGenFramebuffers(1, &fbo_);
|
||
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
|
||
|
||
// Create G-Buffer textures
|
||
textures_[GBUFFER_POSITION] = create_texture_(GL_RGBA32F, GL_RGBA, GL_FLOAT);
|
||
textures_[GBUFFER_NORMAL] = create_texture_(GL_RGBA32F, GL_RGBA, GL_FLOAT);
|
||
textures_[GBUFFER_ALBEDO] = create_texture_(GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE);
|
||
|
||
// Attach textures to framebuffer
|
||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_POSITION,
|
||
GL_TEXTURE_2D, textures_[GBUFFER_POSITION], 0);
|
||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_NORMAL,
|
||
GL_TEXTURE_2D, textures_[GBUFFER_NORMAL], 0);
|
||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_ALBEDO,
|
||
GL_TEXTURE_2D, textures_[GBUFFER_ALBEDO], 0);
|
||
|
||
// Create depth texture
|
||
glGenTextures(1, &depth_texture_);
|
||
glBindTexture(GL_TEXTURE_2D, depth_texture_);
|
||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width_, height_, 0,
|
||
GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||
GL_TEXTURE_2D, depth_texture_, 0);
|
||
|
||
// Set draw buffers
|
||
GLenum draw_buffers[GBUFFER_COUNT] = {
|
||
GL_COLOR_ATTACHMENT0 + GBUFFER_POSITION,
|
||
GL_COLOR_ATTACHMENT0 + GBUFFER_NORMAL,
|
||
GL_COLOR_ATTACHMENT0 + GBUFFER_ALBEDO
|
||
};
|
||
glDrawBuffers(GBUFFER_COUNT, draw_buffers);
|
||
|
||
// Check framebuffer completeness
|
||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||
Logger::error("GBuffer framebuffer is not complete");
|
||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||
return false;
|
||
}
|
||
|
||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||
|
||
initialized_ = true;
|
||
Logger::info("GBuffer initialized successfully");
|
||
return true;
|
||
}
|
||
|
||
void GBuffer::release() {
|
||
if (!initialized_) return;
|
||
|
||
if (fbo_ != INVALID_HANDLE) {
|
||
glDeleteFramebuffers(1, &fbo_);
|
||
fbo_ = INVALID_HANDLE;
|
||
}
|
||
|
||
for (int i = 0; i < GBUFFER_COUNT; ++i) {
|
||
if (textures_[i] != INVALID_HANDLE) {
|
||
glDeleteTextures(1, &textures_[i]);
|
||
textures_[i] = INVALID_HANDLE;
|
||
}
|
||
}
|
||
|
||
if (depth_texture_ != INVALID_HANDLE) {
|
||
glDeleteTextures(1, &depth_texture_);
|
||
depth_texture_ = INVALID_HANDLE;
|
||
}
|
||
|
||
initialized_ = false;
|
||
Logger::info("GBuffer released");
|
||
}
|
||
|
||
void GBuffer::render(const Scene& scene, const Shader& shader) {
|
||
if (!initialized_) {
|
||
Logger::error("GBuffer not initialized");
|
||
return;
|
||
}
|
||
|
||
// Bind framebuffer
|
||
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
|
||
glViewport(0, 0, width_, height_);
|
||
|
||
// Clear buffers
|
||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||
|
||
// Enable depth test
|
||
glEnable(GL_DEPTH_TEST);
|
||
glDepthFunc(GL_LESS);
|
||
|
||
// Use shader
|
||
shader.use();
|
||
|
||
// Set camera matrices
|
||
const Camera& camera = scene.get_camera();
|
||
Mat4 view = camera.get_view_matrix();
|
||
Mat4 projection = camera.get_projection_matrix();
|
||
|
||
shader.set_mat4("u_view", view);
|
||
shader.set_mat4("u_projection", projection);
|
||
|
||
// Render all meshes
|
||
const auto& meshes = scene.get_meshes();
|
||
const auto& materials = scene.get_materials();
|
||
|
||
for (const auto& mesh : meshes) {
|
||
if (!mesh->is_uploaded()) {
|
||
Logger::warning("Mesh not uploaded to GPU, skipping");
|
||
continue;
|
||
}
|
||
|
||
// Set model matrix
|
||
Mat4 model = mesh->get_transform();
|
||
shader.set_mat4("u_model", model);
|
||
|
||
// Set material properties
|
||
uint material_id = mesh->get_material();
|
||
if (material_id < materials.size()) {
|
||
const auto& material = materials[material_id];
|
||
|
||
shader.set_vec3("u_albedo", material->get_albedo());
|
||
shader.set_float("u_metallic", material->get_metallic());
|
||
shader.set_float("u_roughness", material->get_roughness());
|
||
shader.set_uint("u_material_id", material_id);
|
||
|
||
// Bind textures
|
||
auto albedo_tex = material->get_albedo_texture();
|
||
if (albedo_tex && albedo_tex->is_valid()) {
|
||
albedo_tex->bind(0);
|
||
shader.set_int("u_albedo_map", 0);
|
||
shader.set_int("u_has_albedo_map", 1);
|
||
} else {
|
||
shader.set_int("u_has_albedo_map", 0);
|
||
}
|
||
|
||
auto normal_tex = material->get_normal_texture();
|
||
if (normal_tex && normal_tex->is_valid()) {
|
||
normal_tex->bind(1);
|
||
shader.set_int("u_normal_map", 1);
|
||
shader.set_int("u_has_normal_map", 1);
|
||
} else {
|
||
shader.set_int("u_has_normal_map", 0);
|
||
}
|
||
}
|
||
|
||
// Draw mesh
|
||
glBindVertexArray(mesh->get_vao());
|
||
glDrawElements(GL_TRIANGLES, mesh->get_indices().size(), GL_UNSIGNED_INT, 0);
|
||
glBindVertexArray(0);
|
||
}
|
||
|
||
// Unbind framebuffer
|
||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||
}
|
||
|
||
void GBuffer::resize(uint width, uint height) {
|
||
if (width == width_ && height == height_) return;
|
||
|
||
width_ = width;
|
||
height_ = height;
|
||
|
||
if (initialized_) {
|
||
release();
|
||
initialize();
|
||
}
|
||
}
|
||
|
||
TextureHandle GBuffer::get_texture(int index) const {
|
||
if (index < 0 || index >= GBUFFER_COUNT) {
|
||
Logger::error("Invalid G-Buffer texture index");
|
||
return INVALID_HANDLE;
|
||
}
|
||
return textures_[index];
|
||
}
|
||
|
||
void GBuffer::get_dimensions(uint& width, uint& height) const {
|
||
width = width_;
|
||
height = height_;
|
||
}
|
||
|
||
TextureHandle GBuffer::create_texture_(uint internal_format, uint format, uint type) {
|
||
TextureHandle texture;
|
||
glGenTextures(1, &texture);
|
||
glBindTexture(GL_TEXTURE_2D, texture);
|
||
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width_, height_, 0, format, type, nullptr);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||
return texture;
|
||
}
|
||
|
||
} // namespace are
|
||
```
|
||
|
||
### 文件:src/core/raytracer.cpp
|
||
|
||
```cpp
|
||
#include "core/raytracer.h"
|
||
#include "utils/logger.h"
|
||
#include "basic/constants.h"
|
||
#include <glad/glad.h>
|
||
|
||
namespace are {
|
||
|
||
RayTracer::RayTracer(uint width, uint height, const RayTracerConfig& config)
|
||
: width_(width)
|
||
, height_(height)
|
||
, config_(config)
|
||
, accumulation_texture_(INVALID_HANDLE)
|
||
, scene_buffer_(INVALID_HANDLE)
|
||
, material_buffer_(INVALID_HANDLE)
|
||
, light_buffer_(INVALID_HANDLE)
|
||
, bvh_(nullptr)
|
||
, bvh_built_(false)
|
||
, frame_count_(0)
|
||
, initialized_(false) {
|
||
}
|
||
|
||
RayTracer::~RayTracer() {
|
||
release();
|
||
}
|
||
|
||
bool RayTracer::initialize() {
|
||
if (initialized_) {
|
||
Logger::warning("RayTracer already initialized");
|
||
return true;
|
||
}
|
||
|
||
// Create accumulation texture
|
||
glGenTextures(1, &accumulation_texture_);
|
||
glBindTexture(GL_TEXTURE_2D, accumulation_texture_);
|
||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width_, height_, 0, GL_RGBA, GL_FLOAT, nullptr);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||
|
||
// Create shader storage buffers
|
||
glGenBuffers(1, &material_buffer_);
|
||
glGenBuffers(1, &light_buffer_);
|
||
|
||
// Load compute shader
|
||
Logger::info("Loading ray tracing compute shader in RayTracer...");
|
||
if (!compute_shader_.load_compute("shaders/raytracing.comp")) {
|
||
Logger::error("Failed to load ray tracing compute shader in RayTracer");
|
||
return false;
|
||
}
|
||
Logger::info("Ray tracing compute shader loaded in RayTracer");
|
||
|
||
// Initialize BVH if enabled
|
||
if (config_.use_bvh_) {
|
||
bvh_ = std::make_unique<BVH>();
|
||
}
|
||
|
||
initialized_ = true;
|
||
Logger::info("RayTracer initialized successfully");
|
||
return true;
|
||
}
|
||
|
||
void RayTracer::release() {
|
||
if (!initialized_) return;
|
||
|
||
if (accumulation_texture_ != INVALID_HANDLE) {
|
||
glDeleteTextures(1, &accumulation_texture_);
|
||
accumulation_texture_ = INVALID_HANDLE;
|
||
}
|
||
|
||
if (material_buffer_ != INVALID_HANDLE) {
|
||
glDeleteBuffers(1, &material_buffer_);
|
||
material_buffer_ = INVALID_HANDLE;
|
||
}
|
||
|
||
if (light_buffer_ != INVALID_HANDLE) {
|
||
glDeleteBuffers(1, &light_buffer_);
|
||
light_buffer_ = INVALID_HANDLE;
|
||
}
|
||
|
||
bvh_node_buffer_.release();
|
||
bvh_triangle_buffer_.release();
|
||
|
||
compute_shader_.release();
|
||
|
||
bvh_.reset();
|
||
bvh_built_ = false;
|
||
|
||
initialized_ = false;
|
||
Logger::info("RayTracer released");
|
||
}
|
||
|
||
bool RayTracer::rebuild_bvh(const Scene& scene) {
|
||
if (!config_.use_bvh_) {
|
||
Logger::warning("BVH is disabled in configuration");
|
||
return false;
|
||
}
|
||
|
||
if (!bvh_) {
|
||
bvh_ = std::make_unique<BVH>();
|
||
}
|
||
|
||
Logger::info("Building BVH for ray tracing...");
|
||
|
||
if (!bvh_->build(scene.get_meshes())) {
|
||
Logger::error("Failed to build BVH");
|
||
return false;
|
||
}
|
||
|
||
if (!bvh_->upload_to_gpu(bvh_node_buffer_, bvh_triangle_buffer_)) {
|
||
Logger::error("Failed to upload BVH to GPU");
|
||
return false;
|
||
}
|
||
|
||
bvh_built_ = true;
|
||
Logger::info("BVH built and uploaded successfully");
|
||
return true;
|
||
}
|
||
|
||
void RayTracer::trace(const Scene& scene, const GBuffer& gbuffer, TextureHandle output_texture) {
|
||
if (!initialized_) {
|
||
Logger::error("RayTracer not initialized");
|
||
return;
|
||
}
|
||
|
||
if (!compute_shader_.is_valid()) {
|
||
Logger::error("Ray tracing compute shader not loaded");
|
||
return;
|
||
}
|
||
|
||
// Build BVH if enabled and not built yet
|
||
if (config_.use_bvh_ && !bvh_built_) {
|
||
rebuild_bvh(scene);
|
||
}
|
||
|
||
// Upload scene data
|
||
upload_scene_data_(scene);
|
||
|
||
// Use compute shader
|
||
compute_shader_.use();
|
||
|
||
// Bind G-Buffer textures
|
||
bind_gbuffer_(gbuffer);
|
||
|
||
// Bind output and accumulation textures
|
||
glBindImageTexture(3, output_texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F);
|
||
glBindImageTexture(4, accumulation_texture_, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F);
|
||
|
||
// Bind BVH buffers if enabled
|
||
if (config_.use_bvh_ && bvh_built_) {
|
||
bvh_node_buffer_.bind_base(2);
|
||
bvh_triangle_buffer_.bind_base(3);
|
||
compute_shader_.set_bool("u_use_bvh", true);
|
||
compute_shader_.set_uint("u_bvh_node_count", bvh_->get_node_count());
|
||
} else {
|
||
compute_shader_.set_bool("u_use_bvh", false);
|
||
}
|
||
|
||
// Set uniforms
|
||
compute_shader_.set_uint("u_frame_count", frame_count_);
|
||
compute_shader_.set_uint("u_samples_per_pixel", config_.samples_per_pixel_);
|
||
compute_shader_.set_uint("u_max_depth", config_.max_depth_);
|
||
compute_shader_.set_uint("u_light_count", static_cast<uint>(scene.get_lights().size()));
|
||
compute_shader_.set_bool("u_enable_accumulation", config_.enable_accumulation_);
|
||
|
||
// Set camera data
|
||
const Camera& camera = scene.get_camera();
|
||
compute_shader_.set_vec3("u_camera_position", camera.get_position());
|
||
|
||
Mat4 inv_vp = glm::inverse(camera.get_view_projection_matrix());
|
||
compute_shader_.set_mat4("u_inv_view_projection", inv_vp);
|
||
|
||
// Dispatch compute shader
|
||
uint num_groups_x = (width_ + COMPUTE_GROUP_SIZE_X - 1) / COMPUTE_GROUP_SIZE_X;
|
||
uint num_groups_y = (height_ + COMPUTE_GROUP_SIZE_Y - 1) / COMPUTE_GROUP_SIZE_Y;
|
||
|
||
glDispatchCompute(num_groups_x, num_groups_y, 1);
|
||
|
||
// Memory barrier
|
||
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
|
||
|
||
// Increment frame count for accumulation
|
||
if (config_.enable_accumulation_) {
|
||
frame_count_++;
|
||
}
|
||
}
|
||
|
||
void RayTracer::resize(uint width, uint height) {
|
||
if (width == width_ && height == height_) return;
|
||
|
||
width_ = width;
|
||
height_ = height;
|
||
|
||
if (initialized_) {
|
||
// Recreate accumulation texture
|
||
if (accumulation_texture_ != INVALID_HANDLE) {
|
||
glDeleteTextures(1, &accumulation_texture_);
|
||
}
|
||
|
||
glGenTextures(1, &accumulation_texture_);
|
||
glBindTexture(GL_TEXTURE_2D, accumulation_texture_);
|
||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width_, height_, 0, GL_RGBA, GL_FLOAT, nullptr);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||
|
||
reset_accumulation();
|
||
}
|
||
}
|
||
|
||
void RayTracer::reset_accumulation() {
|
||
frame_count_ = 0;
|
||
}
|
||
|
||
void RayTracer::set_config(const RayTracerConfig& config) {
|
||
bool bvh_changed = (config.use_bvh_ != config_.use_bvh_);
|
||
|
||
config_ = config;
|
||
reset_accumulation();
|
||
|
||
if (bvh_changed) {
|
||
if (config_.use_bvh_ && !bvh_) {
|
||
bvh_ = std::make_unique<BVH>();
|
||
bvh_built_ = false;
|
||
} else if (!config_.use_bvh_) {
|
||
bvh_.reset();
|
||
bvh_built_ = false;
|
||
}
|
||
}
|
||
}
|
||
|
||
void RayTracer::upload_scene_data_(const Scene& scene) {
|
||
// Upload materials
|
||
const auto& materials = scene.get_materials();
|
||
if (!materials.empty()) {
|
||
struct MaterialData {
|
||
Vec3 albedo;
|
||
float metallic;
|
||
Vec3 emission;
|
||
float roughness;
|
||
int type;
|
||
float ior;
|
||
Vec2 padding;
|
||
};
|
||
|
||
std::vector<MaterialData> material_data;
|
||
material_data.reserve(materials.size());
|
||
|
||
for (const auto& mat : materials) {
|
||
MaterialData data;
|
||
data.albedo = mat->get_albedo();
|
||
data.metallic = mat->get_metallic();
|
||
data.emission = mat->get_emission();
|
||
data.roughness = mat->get_roughness();
|
||
data.type = static_cast<int>(mat->get_type());
|
||
data.ior = mat->get_ior();
|
||
material_data.push_back(data);
|
||
}
|
||
|
||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, material_buffer_);
|
||
glBufferData(GL_SHADER_STORAGE_BUFFER,
|
||
material_data.size() * sizeof(MaterialData),
|
||
material_data.data(), GL_DYNAMIC_DRAW);
|
||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, material_buffer_);
|
||
}
|
||
|
||
// Upload lights
|
||
const auto& lights = scene.get_lights();
|
||
if (!lights.empty()) {
|
||
struct LightData {
|
||
Vec3 position;
|
||
int type;
|
||
Vec3 direction;
|
||
float intensity;
|
||
Vec3 color;
|
||
float range;
|
||
Vec2 spot_angles;
|
||
Vec2 padding;
|
||
};
|
||
|
||
std::vector<LightData> light_data;
|
||
light_data.reserve(lights.size());
|
||
|
||
for (const auto& light : lights) {
|
||
LightData data;
|
||
data.position = light->get_position();
|
||
data.type = static_cast<int>(light->get_type());
|
||
data.direction = light->get_direction();
|
||
data.intensity = light->get_intensity();
|
||
data.color = light->get_color();
|
||
data.range = light->get_range();
|
||
data.spot_angles = Vec2(light->get_inner_angle(), light->get_outer_angle());
|
||
light_data.push_back(data);
|
||
}
|
||
|
||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, light_buffer_);
|
||
glBufferData(GL_SHADER_STORAGE_BUFFER,
|
||
light_data.size() * sizeof(LightData),
|
||
light_data.data(), GL_DYNAMIC_DRAW);
|
||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, light_buffer_);
|
||
}
|
||
}
|
||
|
||
void RayTracer::bind_gbuffer_(const GBuffer& gbuffer) {
|
||
glBindImageTexture(0, gbuffer.get_texture(GBUFFER_POSITION), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);
|
||
glBindImageTexture(1, gbuffer.get_texture(GBUFFER_NORMAL), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);
|
||
glBindImageTexture(2, gbuffer.get_texture(GBUFFER_ALBEDO), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8);
|
||
}
|
||
|
||
void RayTracer::set_compute_shader(const Shader& shader) {
|
||
compute_shader_ = shader;
|
||
Logger::info("Compute shader set for RayTracer");
|
||
}
|
||
|
||
} // namespace are
|
||
```
|
||
|
||
### 文件:src/core/renderer.cpp
|
||
|
||
```cpp
|
||
#include "core/renderer.h"
|
||
#include "utils/logger.h"
|
||
#include <chrono>
|
||
#include <glad/glad.h>
|
||
|
||
namespace are {
|
||
|
||
Renderer::Renderer(const RendererConfig &config)
|
||
: config_(config)
|
||
, initialized_(false)
|
||
, frame_count_(0) {
|
||
}
|
||
|
||
Renderer::~Renderer() {
|
||
shutdown();
|
||
}
|
||
|
||
bool Renderer::initialize() {
|
||
if (initialized_) {
|
||
Logger::warning("Renderer already initialized");
|
||
return true;
|
||
}
|
||
|
||
Logger::info("Initializing Aurora Rendering Engine...");
|
||
|
||
// Initialize shader manager
|
||
shader_manager_ = std::make_unique<ShaderManager>();
|
||
if (!shader_manager_->initialize()) {
|
||
Logger::error("Failed to initialize shader manager");
|
||
return false;
|
||
}
|
||
|
||
// Initialize G-Buffer
|
||
gbuffer_ = std::make_unique<GBuffer>(config_.width_, config_.height_);
|
||
if (!gbuffer_->initialize()) {
|
||
Logger::error("Failed to initialize G-Buffer");
|
||
return false;
|
||
}
|
||
|
||
// Initialize ray tracer
|
||
RayTracerConfig rt_config;
|
||
rt_config.samples_per_pixel_ = config_.samples_per_pixel_;
|
||
rt_config.max_depth_ = config_.max_ray_depth_;
|
||
rt_config.enable_shadows_ = true;
|
||
rt_config.enable_reflections_ = true;
|
||
rt_config.enable_accumulation_ = config_.enable_accumulation_;
|
||
rt_config.use_bvh_ = true;
|
||
|
||
raytracer_ = std::make_unique<RayTracer>(config_.width_, config_.height_, rt_config);
|
||
if (!raytracer_->initialize()) {
|
||
Logger::error("Failed to initialize ray tracer");
|
||
return false;
|
||
}
|
||
|
||
// Pass compute shader to ray tracer
|
||
const Shader& rt_shader = shader_manager_->get_raytracing_shader();
|
||
if (!rt_shader.is_valid()) {
|
||
Logger::error("Ray tracing shader is invalid");
|
||
return false;
|
||
}
|
||
raytracer_->set_compute_shader(rt_shader);
|
||
|
||
// Initialize screen blit
|
||
screen_blit_ = std::make_unique<ScreenBlit>();
|
||
if (!screen_blit_->initialize()) {
|
||
Logger::error("Failed to initialize screen blit");
|
||
return false;
|
||
}
|
||
|
||
initialized_ = true;
|
||
Logger::info("Aurora Rendering Engine initialized successfully");
|
||
return true;
|
||
}
|
||
|
||
void Renderer::shutdown() {
|
||
if (!initialized_)
|
||
return;
|
||
|
||
Logger::info("Shutting down Aurora Rendering Engine...");
|
||
|
||
if (screen_blit_) {
|
||
screen_blit_->release();
|
||
screen_blit_.reset();
|
||
}
|
||
|
||
if (raytracer_) {
|
||
raytracer_->release();
|
||
raytracer_.reset();
|
||
}
|
||
|
||
if (gbuffer_) {
|
||
gbuffer_->release();
|
||
gbuffer_.reset();
|
||
}
|
||
|
||
if (shader_manager_) {
|
||
shader_manager_->release();
|
||
shader_manager_.reset();
|
||
}
|
||
|
||
initialized_ = false;
|
||
Logger::info("Aurora Rendering Engine shut down");
|
||
}
|
||
|
||
RenderStats Renderer::render(const Scene& scene, TextureHandle output_texture) {
|
||
RenderStats stats = {};
|
||
|
||
if (!initialized_) {
|
||
Logger::error("Renderer not initialized");
|
||
return stats;
|
||
}
|
||
|
||
// Start timing
|
||
auto start_time = std::chrono::high_resolution_clock::now();
|
||
|
||
// Phase 1: G-Buffer pass
|
||
auto gbuffer_start = std::chrono::high_resolution_clock::now();
|
||
|
||
const Shader& gbuffer_shader = shader_manager_->get_gbuffer_shader();
|
||
gbuffer_->render(scene, gbuffer_shader);
|
||
|
||
auto gbuffer_end = std::chrono::high_resolution_clock::now();
|
||
stats.gbuffer_time_ms_ = std::chrono::duration<float, std::milli>(gbuffer_end - gbuffer_start).count();
|
||
|
||
// Phase 2: Ray tracing pass
|
||
auto raytrace_start = std::chrono::high_resolution_clock::now();
|
||
|
||
// Create output texture if not provided
|
||
TextureHandle rt_output = output_texture;
|
||
bool created_temp_texture = false;
|
||
|
||
if (rt_output == 0) {
|
||
glGenTextures(1, &rt_output);
|
||
glBindTexture(GL_TEXTURE_2D, rt_output);
|
||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, config_.width_, config_.height_,
|
||
0, GL_RGBA, GL_FLOAT, nullptr);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||
created_temp_texture = true;
|
||
}
|
||
|
||
raytracer_->trace(scene, *gbuffer_, rt_output);
|
||
|
||
auto raytrace_end = std::chrono::high_resolution_clock::now();
|
||
stats.raytrace_time_ms_ = std::chrono::duration<float, std::milli>(raytrace_end - raytrace_start).count();
|
||
|
||
// Phase 3: Blit to screen if output is default framebuffer
|
||
if (created_temp_texture && output_texture == 0) {
|
||
screen_blit_->blit_fullscreen(rt_output);
|
||
glDeleteTextures(1, &rt_output);
|
||
}
|
||
|
||
// Calculate total frame time
|
||
auto end_time = std::chrono::high_resolution_clock::now();
|
||
stats.frame_time_ms_ = std::chrono::duration<float, std::milli>(end_time - start_time).count();
|
||
|
||
// Count triangles
|
||
const auto& meshes = scene.get_meshes();
|
||
for (const auto& mesh : meshes) {
|
||
stats.triangle_count_ += mesh->get_indices().size() / 3;
|
||
}
|
||
|
||
// Estimate ray count (very rough)
|
||
stats.ray_count_ = config_.width_ * config_.height_ * config_.samples_per_pixel_ * config_.max_ray_depth_;
|
||
|
||
frame_count_++;
|
||
|
||
return stats;
|
||
}
|
||
|
||
void Renderer::resize(uint width, uint height) {
|
||
if (width == config_.width_ && height == config_.height_)
|
||
return;
|
||
|
||
config_.width_ = width;
|
||
config_.height_ = height;
|
||
|
||
if (initialized_) {
|
||
gbuffer_->resize(width, height);
|
||
raytracer_->resize(width, height);
|
||
|
||
Logger::info("Renderer resized to " + std::to_string(width) + "x" + std::to_string(height));
|
||
}
|
||
}
|
||
|
||
void Renderer::set_config(const RendererConfig &config) {
|
||
bool size_changed = (config.width_ != config_.width_ || config.height_ != config_.height_);
|
||
|
||
config_ = config;
|
||
|
||
if (initialized_) {
|
||
if (size_changed) {
|
||
resize(config_.width_, config_.height_);
|
||
}
|
||
|
||
// Update ray tracer config
|
||
RayTracerConfig rt_config = raytracer_->get_config();
|
||
rt_config.samples_per_pixel_ = config_.samples_per_pixel_;
|
||
rt_config.max_depth_ = config_.max_ray_depth_;
|
||
rt_config.enable_accumulation_ = config_.enable_accumulation_;
|
||
raytracer_->set_config(rt_config);
|
||
}
|
||
}
|
||
|
||
} // namespace are
|
||
```
|
||
|
||
### 文件:src/core/screen_blit.cpp
|
||
|
||
```cpp
|
||
#include "core/screen_blit.h"
|
||
#include "utils/logger.h"
|
||
#include <glad/glad.h>
|
||
|
||
namespace are {
|
||
|
||
namespace {
|
||
const char* VERTEX_SHADER_SOURCE = R"(
|
||
#version 430 core
|
||
layout(location = 0) in vec2 a_position;
|
||
layout(location = 1) in vec2 a_texcoord;
|
||
|
||
out vec2 v_texcoord;
|
||
|
||
void main() {
|
||
v_texcoord = a_texcoord;
|
||
gl_Position = vec4(a_position, 0.0, 1.0);
|
||
}
|
||
)";
|
||
|
||
const char* FRAGMENT_SHADER_SOURCE = R"(
|
||
#version 430 core
|
||
in vec2 v_texcoord;
|
||
out vec4 frag_color;
|
||
|
||
uniform sampler2D u_texture;
|
||
|
||
void main() {
|
||
frag_color = texture(u_texture, v_texcoord);
|
||
}
|
||
)";
|
||
}
|
||
|
||
ScreenBlit::ScreenBlit()
|
||
: vao_(0)
|
||
, vbo_(0)
|
||
, initialized_(false) {
|
||
}
|
||
|
||
ScreenBlit::~ScreenBlit() {
|
||
release();
|
||
}
|
||
|
||
bool ScreenBlit::initialize() {
|
||
if (initialized_) {
|
||
Logger::warning("ScreenBlit already initialized");
|
||
return true;
|
||
}
|
||
|
||
// Compile shader
|
||
if (!shader_.compile(VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)) {
|
||
Logger::error("Failed to compile screen blit shader");
|
||
return false;
|
||
}
|
||
|
||
// Create fullscreen quad
|
||
create_quad_();
|
||
|
||
initialized_ = true;
|
||
Logger::info("ScreenBlit initialized successfully");
|
||
return true;
|
||
}
|
||
|
||
void ScreenBlit::release() {
|
||
if (!initialized_) return;
|
||
|
||
shader_.release();
|
||
|
||
if (vao_ != 0) {
|
||
glDeleteVertexArrays(1, &vao_);
|
||
vao_ = 0;
|
||
}
|
||
|
||
if (vbo_ != 0) {
|
||
glDeleteBuffers(1, &vbo_);
|
||
vbo_ = 0;
|
||
}
|
||
|
||
initialized_ = false;
|
||
}
|
||
|
||
void ScreenBlit::blit(TextureHandle texture, int x, int y, uint width, uint height) {
|
||
if (!initialized_) {
|
||
Logger::error("ScreenBlit not initialized");
|
||
return;
|
||
}
|
||
|
||
// Set viewport
|
||
glViewport(x, y, width, height);
|
||
|
||
// Disable depth test
|
||
glDisable(GL_DEPTH_TEST);
|
||
|
||
// Use shader
|
||
shader_.use();
|
||
shader_.set_int("u_texture", 0);
|
||
|
||
// Bind texture
|
||
glActiveTexture(GL_TEXTURE0);
|
||
glBindTexture(GL_TEXTURE_2D, texture);
|
||
|
||
// Draw quad
|
||
glBindVertexArray(vao_);
|
||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||
glBindVertexArray(0);
|
||
|
||
// Re-enable depth test
|
||
glEnable(GL_DEPTH_TEST);
|
||
}
|
||
|
||
void ScreenBlit::blit_fullscreen(TextureHandle texture) {
|
||
GLint viewport[4];
|
||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||
blit(texture, viewport[0], viewport[1], viewport[2], viewport[3]);
|
||
}
|
||
|
||
void ScreenBlit::create_quad_() {
|
||
// Fullscreen quad vertices (position + texcoord)
|
||
float vertices[] = {
|
||
// Position // TexCoord
|
||
-1.0f, -1.0f, 0.0f, 0.0f,
|
||
1.0f, -1.0f, 1.0f, 0.0f,
|
||
1.0f, 1.0f, 1.0f, 1.0f,
|
||
|
||
-1.0f, -1.0f, 0.0f, 0.0f,
|
||
1.0f, 1.0f, 1.0f, 1.0f,
|
||
-1.0f, 1.0f, 0.0f, 1.0f
|
||
};
|
||
|
||
glGenVertexArrays(1, &vao_);
|
||
glGenBuffers(1, &vbo_);
|
||
|
||
glBindVertexArray(vao_);
|
||
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
|
||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||
|
||
// Position attribute
|
||
glEnableVertexAttribArray(0);
|
||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
|
||
|
||
// TexCoord attribute
|
||
glEnableVertexAttribArray(1);
|
||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
|
||
|
||
glBindVertexArray(0);
|
||
}
|
||
|
||
} // namespace are
|
||
```
|
||
|
||
### 文件:src/core/shader_manager.cpp
|
||
|
||
```cpp
|
||
#include "core/shader_manager.h"
|
||
#include "utils/logger.h"
|
||
|
||
namespace are {
|
||
|
||
ShaderManager::ShaderManager()
|
||
: initialized_(false) {
|
||
}
|
||
|
||
ShaderManager::~ShaderManager() {
|
||
release();
|
||
}
|
||
|
||
bool ShaderManager::initialize() {
|
||
if (initialized_) {
|
||
Logger::warning("ShaderManager already initialized");
|
||
return true;
|
||
}
|
||
|
||
Logger::info("Loading built-in shaders...");
|
||
|
||
if (!load_builtin_shaders_()) {
|
||
Logger::error("Failed to load built-in shaders");
|
||
return false;
|
||
}
|
||
|
||
initialized_ = true;
|
||
Logger::info("ShaderManager initialized successfully");
|
||
return true;
|
||
}
|
||
|
||
void ShaderManager::release() {
|
||
if (!initialized_) return;
|
||
|
||
gbuffer_shader_.release();
|
||
raytracing_shader_.release();
|
||
|
||
for (auto& pair : shader_cache_) {
|
||
pair.second.release();
|
||
}
|
||
shader_cache_.clear();
|
||
|
||
initialized_ = false;
|
||
Logger::info("ShaderManager released");
|
||
}
|
||
|
||
Shader ShaderManager::load_shader(const std::string& name,
|
||
const std::string& vertex_path,
|
||
const std::string& fragment_path) {
|
||
// Check cache
|
||
auto it = shader_cache_.find(name);
|
||
if (it != shader_cache_.end()) {
|
||
Logger::info("Shader '" + name + "' loaded from cache");
|
||
return it->second;
|
||
}
|
||
|
||
// Load shader
|
||
Shader shader;
|
||
if (!shader.load(vertex_path, fragment_path)) {
|
||
Logger::error("Failed to load shader '" + name + "'");
|
||
return Shader();
|
||
}
|
||
|
||
shader_cache_[name] = shader;
|
||
Logger::info("Shader '" + name + "' loaded successfully");
|
||
return shader;
|
||
}
|
||
|
||
Shader ShaderManager::load_compute_shader(const std::string& name,
|
||
const std::string& compute_path) {
|
||
// Check cache
|
||
auto it = shader_cache_.find(name);
|
||
if (it != shader_cache_.end()) {
|
||
Logger::info("Compute shader '" + name + "' loaded from cache");
|
||
return it->second;
|
||
}
|
||
|
||
// Load shader
|
||
Shader shader;
|
||
if (!shader.load_compute(compute_path)) {
|
||
Logger::error("Failed to load compute shader '" + name + "'");
|
||
return Shader();
|
||
}
|
||
|
||
shader_cache_[name] = shader;
|
||
Logger::info("Compute shader '" + name + "' loaded successfully");
|
||
return shader;
|
||
}
|
||
|
||
Shader ShaderManager::get_shader(const std::string& name) const {
|
||
auto it = shader_cache_.find(name);
|
||
if (it != shader_cache_.end()) {
|
||
return it->second;
|
||
}
|
||
|
||
Logger::warning("Shader '" + name + "' not found in cache");
|
||
return Shader();
|
||
}
|
||
|
||
bool ShaderManager::load_builtin_shaders_() {
|
||
// Load G-Buffer shader
|
||
if (!gbuffer_shader_.load("shaders/gbuffer.vert", "shaders/gbuffer.frag")) {
|
||
Logger::error("Failed to load G-Buffer shader");
|
||
return false;
|
||
}
|
||
shader_cache_["gbuffer"] = gbuffer_shader_;
|
||
|
||
// Load ray tracing compute shader
|
||
if (!raytracing_shader_.load_compute("shaders/raytracing.comp")) {
|
||
Logger::error("Failed to load ray tracing shader");
|
||
return false;
|
||
}
|
||
shader_cache_["raytracing"] = raytracing_shader_;
|
||
|
||
// Load ray tracing compute shader
|
||
Logger::info("Loading ray tracing compute shader...");
|
||
if (!raytracing_shader_.load_compute("shaders/raytracing.comp")) {
|
||
Logger::error("Failed to load ray tracing shader");
|
||
return false;
|
||
}
|
||
shader_cache_["raytracing"] = raytracing_shader_;
|
||
Logger::info("Ray tracing shader loaded successfully");
|
||
|
||
return true;
|
||
}
|
||
|
||
} // namespace are
|
||
```
|
||
|
||
### 文件:src/scene/camera.cpp
|
||
|
||
```cpp
|
||
#include "scene/camera.h"
|
||
#include "basic/math.h"
|
||
#include <glm/gtc/matrix_transform.hpp>
|
||
|
||
namespace are {
|
||
|
||
Camera::Camera()
|
||
: position_(0.0f, 0.0f, 5.0f)
|
||
, target_(0.0f, 0.0f, 0.0f)
|
||
, up_(0.0f, 1.0f, 0.0f)
|
||
, projection_type_(ProjectionType::PERSPECTIVE)
|
||
, fov_(glm::radians(45.0f))
|
||
, aspect_(16.0f / 9.0f)
|
||
, left_(-1.0f)
|
||
, right_(1.0f)
|
||
, bottom_(-1.0f)
|
||
, top_(1.0f)
|
||
, near_(0.1f)
|
||
, far_(100.0f)
|
||
, view_dirty_(true)
|
||
, projection_dirty_(true) {
|
||
}
|
||
|
||
Camera::~Camera() {
|
||
}
|
||
|
||
void Camera::set_perspective(float fov, float aspect, float near, float far) {
|
||
projection_type_ = ProjectionType::PERSPECTIVE;
|
||
fov_ = glm::radians(fov);
|
||
aspect_ = aspect;
|
||
near_ = near;
|
||
far_ = far;
|
||
projection_dirty_ = true;
|
||
}
|
||
|
||
void Camera::set_orthographic(float left, float right, float bottom, float top, float near, float far) {
|
||
projection_type_ = ProjectionType::ORTHOGRAPHIC;
|
||
left_ = left;
|
||
right_ = right;
|
||
bottom_ = bottom;
|
||
top_ = top;
|
||
near_ = near;
|
||
far_ = far;
|
||
projection_dirty_ = true;
|
||
}
|
||
|
||
void Camera::set_position(const Vec3& position) {
|
||
position_ = position;
|
||
view_dirty_ = true;
|
||
}
|
||
|
||
void Camera::set_target(const Vec3& target) {
|
||
target_ = target;
|
||
view_dirty_ = true;
|
||
}
|
||
|
||
void Camera::set_up(const Vec3& up) {
|
||
up_ = up;
|
||
view_dirty_ = true;
|
||
}
|
||
|
||
Mat4 Camera::get_view_matrix() const {
|
||
if (view_dirty_) {
|
||
view_matrix_ = MathUtils::look_at(position_, target_, up_);
|
||
view_dirty_ = false;
|
||
}
|
||
return view_matrix_;
|
||
}
|
||
|
||
Mat4 Camera::get_projection_matrix() const {
|
||
if (projection_dirty_) {
|
||
if (projection_type_ == ProjectionType::PERSPECTIVE) {
|
||
projection_matrix_ = MathUtils::perspective(fov_, aspect_, near_, far_);
|
||
} else {
|
||
projection_matrix_ = glm::ortho(left_, right_, bottom_, top_, near_, far_);
|
||
}
|
||
projection_dirty_ = false;
|
||
}
|
||
return projection_matrix_;
|
||
}
|
||
|
||
Mat4 Camera::get_view_projection_matrix() const {
|
||
return get_projection_matrix() * get_view_matrix();
|
||
}
|
||
|
||
Vec3 Camera::get_forward() const {
|
||
return MathUtils::normalize(target_ - position_);
|
||
}
|
||
|
||
Vec3 Camera::get_right() const {
|
||
Vec3 forward = get_forward();
|
||
return MathUtils::normalize(MathUtils::cross(forward, up_));
|
||
}
|
||
|
||
Vec3 Camera::get_up() const {
|
||
Vec3 forward = get_forward();
|
||
Vec3 right = get_right();
|
||
return MathUtils::cross(right, forward);
|
||
}
|
||
|
||
} // namespace are
|
||
```
|
||
|
||
### 文件:src/scene/material.cpp
|
||
|
||
```cpp
|
||
#include "scene/material.h"
|
||
|
||
namespace are {
|
||
|
||
Material::Material()
|
||
: albedo_(1.0f, 1.0f, 1.0f)
|
||
, emission_(0.0f, 0.0f, 0.0f)
|
||
, metallic_(0.0f)
|
||
, roughness_(0.5f)
|
||
, ior_(1.5f)
|
||
, type_(MaterialType::DIFFUSE)
|
||
, albedo_texture_(nullptr)
|
||
, normal_texture_(nullptr) {
|
||
}
|
||
|
||
Material::~Material() {
|
||
}
|
||
|
||
void Material::set_albedo(const Vec3& albedo) {
|
||
albedo_ = albedo;
|
||
}
|
||
|
||
void Material::set_emission(const Vec3& emission) {
|
||
emission_ = emission;
|
||
}
|
||
|
||
void Material::set_metallic(float metallic) {
|
||
metallic_ = glm::clamp(metallic, 0.0f, 1.0f);
|
||
}
|
||
|
||
void Material::set_roughness(float roughness) {
|
||
roughness_ = glm::clamp(roughness, 0.0f, 1.0f);
|
||
}
|
||
|
||
void Material::set_ior(float ior) {
|
||
ior_ = ior;
|
||
}
|
||
|
||
void Material::set_type(MaterialType type) {
|
||
type_ = type;
|
||
}
|
||
|
||
void Material::set_albedo_texture(std::shared_ptr<Texture> texture) {
|
||
albedo_texture_ = texture;
|
||
}
|
||
|
||
void Material::set_normal_texture(std::shared_ptr<Texture> texture) {
|
||
normal_texture_ = texture;
|
||
}
|
||
|
||
} // namespace are
|
||
```
|
||
|
||
### 文件:src/scene/light.cpp
|
||
|
||
```cpp
|
||
#include "scene/light.h"
|
||
#include <glm/gtc/constants.hpp>
|
||
|
||
namespace are {
|
||
|
||
Light::Light()
|
||
: type_(LightType::POINT)
|
||
, position_(0.0f, 5.0f, 0.0f)
|
||
, direction_(0.0f, -1.0f, 0.0f)
|
||
, color_(1.0f, 1.0f, 1.0f)
|
||
, intensity_(1.0f)
|
||
, range_(10.0f)
|
||
, inner_angle_(glm::radians(30.0f))
|
||
, outer_angle_(glm::radians(45.0f)) {
|
||
}
|
||
|
||
Light::~Light() {
|
||
}
|
||
|
||
void Light::set_type(LightType type) {
|
||
type_ = type;
|
||
}
|
||
|
||
void Light::set_position(const Vec3& position) {
|
||
position_ = position;
|
||
}
|
||
|
||
void Light::set_direction(const Vec3& direction) {
|
||
direction_ = glm::normalize(direction);
|
||
}
|
||
|
||
void Light::set_color(const Vec3& color) {
|
||
color_ = color;
|
||
}
|
||
|
||
void Light::set_intensity(float intensity) {
|
||
intensity_ = intensity;
|
||
}
|
||
|
||
void Light::set_range(float range) {
|
||
range_ = range;
|
||
}
|
||
|
||
void Light::set_spot_angles(float inner_angle, float outer_angle) {
|
||
inner_angle_ = glm::radians(inner_angle);
|
||
outer_angle_ = glm::radians(outer_angle);
|
||
}
|
||
|
||
} // namespace are
|
||
```
|
||
|
||
### 文件:src/scene/scene.cpp
|
||
|
||
```cpp
|
||
#include "scene/scene.h"
|
||
|
||
namespace are {
|
||
|
||
Scene::Scene() {
|
||
// Create default camera
|
||
camera_ = std::make_shared<Camera>();
|
||
}
|
||
|
||
Scene::~Scene() {
|
||
clear();
|
||
}
|
||
|
||
uint Scene::add_mesh(std::shared_ptr<Mesh> mesh) {
|
||
meshes_.push_back(mesh);
|
||
return static_cast<uint>(meshes_.size() - 1);
|
||
}
|
||
|
||
uint Scene::add_material(std::shared_ptr<Material> material) {
|
||
materials_.push_back(material);
|
||
return static_cast<uint>(materials_.size() - 1);
|
||
}
|
||
|
||
uint Scene::add_light(std::shared_ptr<Light> light) {
|
||
lights_.push_back(light);
|
||
return static_cast<uint>(lights_.size() - 1);
|
||
}
|
||
|
||
void Scene::set_camera(std::shared_ptr<Camera> camera) {
|
||
camera_ = camera;
|
||
}
|
||
|
||
void Scene::clear() {
|
||
meshes_.clear();
|
||
materials_.clear();
|
||
lights_.clear();
|
||
}
|
||
|
||
void Scene::update(float delta_time) {
|
||
// Reserved for future animation/physics updates
|
||
(void)delta_time; // Suppress unused parameter warning
|
||
}
|
||
|
||
} // namespace are
|
||
```
|
||
|
||
### 文件:src/scene/mesh.cpp
|
||
|
||
```cpp
|
||
#include "scene/mesh.h"
|
||
#include "utils/logger.h"
|
||
#include <glad/glad.h>
|
||
|
||
namespace are {
|
||
|
||
Mesh::Mesh()
|
||
: material_id_(0)
|
||
, transform_(1.0f)
|
||
, vao_(0)
|
||
, vbo_(0)
|
||
, ebo_(0)
|
||
, uploaded_(false) {
|
||
}
|
||
|
||
Mesh::~Mesh() {
|
||
release_gpu_resources();
|
||
}
|
||
|
||
void Mesh::set_vertices(const std::vector<Vertex>& vertices) {
|
||
vertices_ = vertices;
|
||
uploaded_ = false;
|
||
}
|
||
|
||
void Mesh::set_indices(const std::vector<uint>& indices) {
|
||
indices_ = indices;
|
||
uploaded_ = false;
|
||
}
|
||
|
||
void Mesh::set_material(uint material_id) {
|
||
material_id_ = material_id;
|
||
}
|
||
|
||
void Mesh::set_transform(const Mat4& transform) {
|
||
transform_ = transform;
|
||
}
|
||
|
||
bool Mesh::upload_to_gpu() {
|
||
if (uploaded_) {
|
||
Logger::warning("Mesh already uploaded to GPU");
|
||
return true;
|
||
}
|
||
|
||
if (vertices_.empty()) {
|
||
Logger::error("Cannot upload mesh: no vertices");
|
||
return false;
|
||
}
|
||
|
||
if (indices_.empty()) {
|
||
Logger::error("Cannot upload mesh: no indices");
|
||
return false;
|
||
}
|
||
|
||
// Generate VAO
|
||
glGenVertexArrays(1, &vao_);
|
||
glBindVertexArray(vao_);
|
||
|
||
// Generate and upload VBO
|
||
glGenBuffers(1, &vbo_);
|
||
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
|
||
glBufferData(GL_ARRAY_BUFFER, vertices_.size() * sizeof(Vertex),
|
||
vertices_.data(), GL_STATIC_DRAW);
|
||
|
||
// Generate and upload EBO
|
||
glGenBuffers(1, &ebo_);
|
||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo_);
|
||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_.size() * sizeof(uint),
|
||
indices_.data(), GL_STATIC_DRAW);
|
||
|
||
// Set vertex attributes
|
||
// Location 0: Position
|
||
glEnableVertexAttribArray(0);
|
||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||
(void*)offsetof(Vertex, position_));
|
||
|
||
// Location 1: Normal
|
||
glEnableVertexAttribArray(1);
|
||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||
(void*)offsetof(Vertex, normal_));
|
||
|
||
// Location 2: TexCoord
|
||
glEnableVertexAttribArray(2);
|
||
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||
(void*)offsetof(Vertex, texcoord_));
|
||
|
||
// Location 3: Tangent
|
||
glEnableVertexAttribArray(3);
|
||
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||
(void*)offsetof(Vertex, tangent_));
|
||
|
||
glBindVertexArray(0);
|
||
|
||
uploaded_ = true;
|
||
Logger::info("Mesh uploaded to GPU successfully");
|
||
return true;
|
||
}
|
||
|
||
void Mesh::release_gpu_resources() {
|
||
if (!uploaded_) return;
|
||
|
||
if (vao_ != 0) {
|
||
glDeleteVertexArrays(1, &vao_);
|
||
vao_ = 0;
|
||
}
|
||
|
||
if (vbo_ != 0) {
|
||
glDeleteBuffers(1, &vbo_);
|
||
vbo_ = 0;
|
||
}
|
||
|
||
if (ebo_ != 0) {
|
||
glDeleteBuffers(1, &ebo_);
|
||
ebo_ = 0;
|
||
}
|
||
|
||
uploaded_ = false;
|
||
}
|
||
|
||
} // namespace are
|
||
```
|
||
|
||
### 文件:src/resource/buffer.cpp
|
||
|
||
```cpp
|
||
#include "resource/buffer.h"
|
||
#include "utils/logger.h"
|
||
#include <glad/glad.h>
|
||
|
||
namespace are {
|
||
|
||
namespace {
|
||
GLenum get_gl_buffer_type(BufferType type) {
|
||
switch (type) {
|
||
case BufferType::VERTEX_BUFFER: return GL_ARRAY_BUFFER;
|
||
case BufferType::INDEX_BUFFER: return GL_ELEMENT_ARRAY_BUFFER;
|
||
case BufferType::UNIFORM_BUFFER: return GL_UNIFORM_BUFFER;
|
||
case BufferType::SHADER_STORAGE_BUFFER: return GL_SHADER_STORAGE_BUFFER;
|
||
default: return GL_ARRAY_BUFFER;
|
||
}
|
||
}
|
||
|
||
GLenum get_gl_usage(BufferUsage usage) {
|
||
switch (usage) {
|
||
case BufferUsage::STATIC_DRAW: return GL_STATIC_DRAW;
|
||
case BufferUsage::DYNAMIC_DRAW: return GL_DYNAMIC_DRAW;
|
||
case BufferUsage::STREAM_DRAW: return GL_STREAM_DRAW;
|
||
default: return GL_STATIC_DRAW;
|
||
}
|
||
}
|
||
}
|
||
|
||
Buffer::Buffer()
|
||
: handle_(INVALID_HANDLE)
|
||
, type_(BufferType::VERTEX_BUFFER)
|
||
, size_(0)
|
||
, usage_(BufferUsage::STATIC_DRAW) {
|
||
}
|
||
|
||
Buffer::~Buffer() {
|
||
// Don't auto-release, let user control lifetime
|
||
}
|
||
|
||
bool Buffer::create(BufferType type, size_t size, const void* data, BufferUsage usage) {
|
||
if (handle_ != INVALID_HANDLE) {
|
||
Logger::warning("Buffer already created, releasing old buffer");
|
||
release();
|
||
}
|
||
|
||
type_ = type;
|
||
size_ = size;
|
||
usage_ = usage;
|
||
|
||
glGenBuffers(1, &handle_);
|
||
|
||
GLenum gl_type = get_gl_buffer_type(type);
|
||
GLenum gl_usage = get_gl_usage(usage);
|
||
|
||
glBindBuffer(gl_type, handle_);
|
||
glBufferData(gl_type, size, data, gl_usage);
|
||
glBindBuffer(gl_type, 0);
|
||
|
||
Logger::info("Buffer created successfully");
|
||
return true;
|
||
}
|
||
|
||
void Buffer::update(size_t offset, size_t size, const void* data) {
|
||
if (handle_ == INVALID_HANDLE) {
|
||
Logger::error("Cannot update invalid buffer");
|
||
return;
|
||
}
|
||
|
||
if (offset + size > size_) {
|
||
Logger::error("Buffer update out of bounds");
|
||
return;
|
||
}
|
||
|
||
GLenum gl_type = get_gl_buffer_type(type_);
|
||
|
||
glBindBuffer(gl_type, handle_);
|
||
glBufferSubData(gl_type, offset, size, data);
|
||
glBindBuffer(gl_type, 0);
|
||
}
|
||
|
||
void Buffer::bind() const {
|
||
if (handle_ == INVALID_HANDLE) {
|
||
Logger::warning("Attempting to bind invalid buffer");
|
||
return;
|
||
}
|
||
|
||
GLenum gl_type = get_gl_buffer_type(type_);
|
||
glBindBuffer(gl_type, handle_);
|
||
}
|
||
|
||
void Buffer::bind_base(uint binding_point) const {
|
||
if (handle_ == INVALID_HANDLE) {
|
||
Logger::warning("Attempting to bind invalid buffer");
|
||
return;
|
||
}
|
||
|
||
GLenum gl_type = get_gl_buffer_type(type_);
|
||
glBindBufferBase(gl_type, binding_point, handle_);
|
||
}
|
||
|
||
void Buffer::unbind() const {
|
||
GLenum gl_type = get_gl_buffer_type(type_);
|
||
glBindBuffer(gl_type, 0);
|
||
}
|
||
|
||
void Buffer::release() {
|
||
if (handle_ != INVALID_HANDLE) {
|
||
glDeleteBuffers(1, &handle_);
|
||
handle_ = INVALID_HANDLE;
|
||
}
|
||
|
||
size_ = 0;
|
||
}
|
||
|
||
} // namespace are
|
||
```
|
||
|
||
### 文件:src/resource/model_loader.cpp
|
||
|
||
```cpp
|
||
#include "resource/model_loader.h"
|
||
#include "utils/logger.h"
|
||
#include "resource/texture.h"
|
||
|
||
// Note: This is a simplified implementation without Assimp
|
||
// For full implementation, include Assimp and implement properly
|
||
|
||
namespace are {
|
||
|
||
bool ModelLoader::load(const std::string& path,
|
||
std::vector<std::shared_ptr<Mesh>>& meshes,
|
||
std::vector<std::shared_ptr<Material>>& materials,
|
||
bool flip_uvs) {
|
||
Logger::error("ModelLoader requires Assimp library (not implemented in this version)");
|
||
Logger::info("To implement: include <assimp/Importer.hpp>, <assimp/scene.h>, <assimp/postprocess.h>");
|
||
|
||
// Placeholder implementation
|
||
// TODO: Implement with Assimp
|
||
/*
|
||
Assimp::Importer importer;
|
||
const aiScene* scene = importer.ReadFile(path,
|
||
aiProcess_Triangulate |
|
||
aiProcess_GenNormals |
|
||
aiProcess_CalcTangentSpace |
|
||
(flip_uvs ? aiProcess_FlipUVs : 0));
|
||
|
||
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
|
||
Logger::error("Failed to load model: " + std::string(importer.GetErrorString()));
|
||
return false;
|
||
}
|
||
|
||
std::string directory = path.substr(0, path.find_last_of('/'));
|
||
process_node_(scene->mRootNode, scene, meshes, materials, directory);
|
||
|
||
Logger::info("Model loaded: " + path);
|
||
return true;
|
||
*/
|
||
|
||
return false;
|
||
}
|
||
|
||
bool ModelLoader::load_and_upload(const std::string& path,
|
||
std::vector<std::shared_ptr<Mesh>>& meshes,
|
||
std::vector<std::shared_ptr<Material>>& materials,
|
||
bool flip_uvs) {
|
||
if (!load(path, meshes, materials, flip_uvs)) {
|
||
return false;
|
||
}
|
||
|
||
// Upload all meshes to GPU
|
||
for (auto& mesh : meshes) {
|
||
if (!mesh->upload_to_gpu()) {
|
||
Logger::error("Failed to upload mesh to GPU");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
void ModelLoader::process_node_(void* node, void* scene,
|
||
std::vector<std::shared_ptr<Mesh>>& meshes,
|
||
std::vector<std::shared_ptr<Material>>& materials,
|
||
const std::string& directory) {
|
||
// TODO: Implement with Assimp
|
||
/*
|
||
aiNode* ai_node = static_cast<aiNode*>(node);
|
||
const aiScene* ai_scene = static_cast<const aiScene*>(scene);
|
||
|
||
// Process all meshes in this node
|
||
for (uint i = 0; i < ai_node->mNumMeshes; ++i) {
|
||
aiMesh* ai_mesh = ai_scene->mMeshes[ai_node->mMeshes[i]];
|
||
meshes.push_back(process_mesh_(ai_mesh, ai_scene, materials, directory));
|
||
}
|
||
|
||
// Process children recursively
|
||
for (uint i = 0; i < ai_node->mNumChildren; ++i) {
|
||
process_node_(ai_node->mChildren[i], ai_scene, meshes, materials, directory);
|
||
}
|
||
*/
|
||
}
|
||
|
||
std::shared_ptr<Mesh> ModelLoader::process_mesh_(void* mesh, void* scene,
|
||
std::vector<std::shared_ptr<Material>>& materials,
|
||
const std::string& directory) {
|
||
// TODO: Implement with Assimp
|
||
/*
|
||
aiMesh* ai_mesh = static_cast<aiMesh*>(mesh);
|
||
const aiScene* ai_scene = static_cast<const aiScene*>(scene);
|
||
|
||
std::vector<Vertex> vertices;
|
||
std::vector<uint> indices;
|
||
|
||
// Process vertices
|
||
for (uint i = 0; i < ai_mesh->mNumVertices; ++i) {
|
||
Vertex vertex;
|
||
|
||
vertex.position_ = Vec3(ai_mesh->mVertices[i].x,
|
||
ai_mesh->mVertices[i].y,
|
||
ai_mesh->mVertices[i].z);
|
||
|
||
if (ai_mesh->HasNormals()) {
|
||
vertex.normal_ = Vec3(ai_mesh->mNormals[i].x,
|
||
ai_mesh->mNormals[i].y,
|
||
ai_mesh->mNormals[i].z);
|
||
}
|
||
|
||
if (ai_mesh->mTextureCoords[0]) {
|
||
vertex.texcoord_ = Vec2(ai_mesh->mTextureCoords[0][i].x,
|
||
ai_mesh->mTextureCoords[0][i].y);
|
||
}
|
||
|
||
if (ai_mesh->HasTangentsAndBitangents()) {
|
||
vertex.tangent_ = Vec3(ai_mesh->mTangents[i].x,
|
||
ai_mesh->mTangents[i].y,
|
||
ai_mesh->mTangents[i].z);
|
||
}
|
||
|
||
vertices.push_back(vertex);
|
||
}
|
||
|
||
// Process indices
|
||
for (uint i = 0; i < ai_mesh->mNumFaces; ++i) {
|
||
aiFace face = ai_mesh->mFaces[i];
|
||
for (uint j = 0; j < face.mNumIndices; ++j) {
|
||
indices.push_back(face.mIndices[j]);
|
||
}
|
||
}
|
||
|
||
// Process material
|
||
uint material_id = materials.size();
|
||
if (ai_mesh->mMaterialIndex >= 0) {
|
||
aiMaterial* ai_material = ai_scene->mMaterials[ai_mesh->mMaterialIndex];
|
||
|
||
auto material = std::make_shared<Material>();
|
||
|
||
// Load diffuse color
|
||
aiColor3D color;
|
||
if (ai_material->Get(AI_MATKEY_COLOR_DIFFUSE, color) == AI_SUCCESS) {
|
||
material->set_albedo(Vec3(color.r, color.g, color.b));
|
||
}
|
||
|
||
// Load textures
|
||
load_material_textures_(ai_material, aiTextureType_DIFFUSE, material, directory);
|
||
load_material_textures_(ai_material, aiTextureType_NORMALS, material, directory);
|
||
|
||
materials.push_back(material);
|
||
}
|
||
|
||
auto mesh_obj = std::make_shared<Mesh>();
|
||
mesh_obj->set_vertices(vertices);
|
||
mesh_obj->set_indices(indices);
|
||
mesh_obj->set_material(material_id);
|
||
|
||
return mesh_obj;
|
||
*/
|
||
|
||
return nullptr;
|
||
}
|
||
|
||
void ModelLoader::load_material_textures_(void* material, int type,
|
||
std::shared_ptr<Material>& mat,
|
||
const std::string& directory) {
|
||
// TODO: Implement with Assimp
|
||
/*
|
||
aiMaterial* ai_material = static_cast<aiMaterial*>(material);
|
||
aiTextureType ai_type = static_cast<aiTextureType>(type);
|
||
|
||
for (uint i = 0; i < ai_material->GetTextureCount(ai_type); ++i) {
|
||
aiString str;
|
||
ai_material->GetTexture(ai_type, i, &str);
|
||
|
||
std::string filename = directory + "/" + std::string(str.C_Str());
|
||
|
||
auto texture = std::make_shared<Texture>();
|
||
if (texture->load_from_file(filename)) {
|
||
if (ai_type == aiTextureType_DIFFUSE) {
|
||
mat->set_albedo_texture(texture);
|
||
} else if (ai_type == aiTextureType_NORMALS) {
|
||
mat->set_normal_texture(texture);
|
||
}
|
||
}
|
||
}
|
||
*/
|
||
}
|
||
|
||
} // namespace are
|
||
```
|
||
|
||
### 文件:src/resource/shader.cpp
|
||
|
||
```cpp
|
||
#include "resource/shader.h"
|
||
#include "utils/logger.h"
|
||
#include "basic/math.h" // 修改为math.h
|
||
#include <glad/glad.h>
|
||
#include <fstream>
|
||
#include <sstream>
|
||
|
||
namespace are {
|
||
|
||
Shader::Shader()
|
||
: handle_(INVALID_HANDLE) {
|
||
}
|
||
|
||
Shader::~Shader() {
|
||
// Don't auto-release, let user control lifetime
|
||
}
|
||
|
||
bool Shader::load(const std::string& vertex_path, const std::string& fragment_path) {
|
||
std::string vertex_source = read_file_(vertex_path);
|
||
std::string fragment_source = read_file_(fragment_path);
|
||
|
||
if (vertex_source.empty() || fragment_source.empty()) {
|
||
Logger::error("Failed to read shader files");
|
||
return false;
|
||
}
|
||
|
||
return compile(vertex_source, fragment_source);
|
||
}
|
||
|
||
bool Shader::load_compute(const std::string& compute_path) {
|
||
std::string compute_source = read_file_(compute_path);
|
||
|
||
if (compute_source.empty()) {
|
||
Logger::error("Failed to read compute shader file");
|
||
return false;
|
||
}
|
||
|
||
return compile_compute(compute_source);
|
||
}
|
||
|
||
bool Shader::compile(const std::string& vertex_source, const std::string& fragment_source) {
|
||
uint vertex_shader = compile_shader_(vertex_source, GL_VERTEX_SHADER);
|
||
if (vertex_shader == 0) return false;
|
||
|
||
uint fragment_shader = compile_shader_(fragment_source, GL_FRAGMENT_SHADER);
|
||
if (fragment_shader == 0) {
|
||
glDeleteShader(vertex_shader);
|
||
return false;
|
||
}
|
||
|
||
uint shaders[] = { vertex_shader, fragment_shader };
|
||
bool success = link_program_(shaders, 2);
|
||
|
||
glDeleteShader(vertex_shader);
|
||
glDeleteShader(fragment_shader);
|
||
|
||
return success;
|
||
}
|
||
|
||
bool Shader::compile_compute(const std::string& compute_source) {
|
||
uint compute_shader = compile_shader_(compute_source, GL_COMPUTE_SHADER);
|
||
if (compute_shader == 0) return false;
|
||
|
||
uint shaders[] = { compute_shader };
|
||
bool success = link_program_(shaders, 1);
|
||
|
||
glDeleteShader(compute_shader);
|
||
|
||
return success;
|
||
}
|
||
|
||
void Shader::use() const { // 改为const
|
||
if (handle_ != INVALID_HANDLE) {
|
||
glUseProgram(handle_);
|
||
}
|
||
}
|
||
|
||
void Shader::release() {
|
||
if (handle_ != INVALID_HANDLE) {
|
||
glDeleteProgram(handle_);
|
||
handle_ = INVALID_HANDLE;
|
||
}
|
||
uniform_cache_.clear();
|
||
}
|
||
|
||
void Shader::set_bool(const std::string& name, bool value) const { // 新增
|
||
glUniform1i(get_uniform_location_(name), static_cast<int>(value));
|
||
}
|
||
|
||
void Shader::set_int(const std::string& name, int value) const { // 改为const
|
||
glUniform1i(get_uniform_location_(name), value);
|
||
}
|
||
|
||
void Shader::set_uint(const std::string& name, uint value) const { // 改为const
|
||
glUniform1ui(get_uniform_location_(name), value);
|
||
}
|
||
|
||
void Shader::set_float(const std::string& name, float value) const { // 改为const
|
||
glUniform1f(get_uniform_location_(name), value);
|
||
}
|
||
|
||
void Shader::set_vec2(const std::string& name, const Vec2& value) const { // 改为const
|
||
glUniform2fv(get_uniform_location_(name), 1, &value[0]);
|
||
}
|
||
|
||
void Shader::set_vec3(const std::string& name, const Vec3& value) const { // 改为const
|
||
glUniform3fv(get_uniform_location_(name), 1, &value[0]);
|
||
}
|
||
|
||
void Shader::set_vec4(const std::string& name, const Vec4& value) const { // 改为const
|
||
glUniform4fv(get_uniform_location_(name), 1, &value[0]);
|
||
}
|
||
|
||
void Shader::set_mat3(const std::string& name, const Mat3& value) const { // 改为const
|
||
glUniformMatrix3fv(get_uniform_location_(name), 1, GL_FALSE, &value[0][0]);
|
||
}
|
||
|
||
void Shader::set_mat4(const std::string& name, const Mat4& value) const { // 改为const
|
||
glUniformMatrix4fv(get_uniform_location_(name), 1, GL_FALSE, MathUtils::value_ptr(value));
|
||
}
|
||
|
||
int Shader::get_uniform_location_(const std::string& name) const { // 改为const
|
||
auto it = uniform_cache_.find(name);
|
||
if (it != uniform_cache_.end()) {
|
||
return it->second;
|
||
}
|
||
|
||
int location = glGetUniformLocation(handle_, name.c_str());
|
||
uniform_cache_[name] = location; // mutable允许修改
|
||
|
||
if (location == -1) {
|
||
Logger::warning("Uniform '" + name + "' not found in shader");
|
||
}
|
||
|
||
return location;
|
||
}
|
||
|
||
uint Shader::compile_shader_(const std::string& source, uint type) {
|
||
uint shader = glCreateShader(type);
|
||
const char* source_cstr = source.c_str();
|
||
glShaderSource(shader, 1, &source_cstr, nullptr);
|
||
glCompileShader(shader);
|
||
|
||
int success;
|
||
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||
if (!success) {
|
||
char info_log[512];
|
||
glGetShaderInfoLog(shader, 512, nullptr, info_log);
|
||
|
||
std::string type_str = (type == GL_VERTEX_SHADER) ? "VERTEX" :
|
||
(type == GL_FRAGMENT_SHADER) ? "FRAGMENT" : "COMPUTE";
|
||
Logger::error("Shader compilation failed (" + type_str + "): " + std::string(info_log));
|
||
|
||
glDeleteShader(shader);
|
||
return 0;
|
||
}
|
||
|
||
return shader;
|
||
}
|
||
|
||
bool Shader::link_program_(const uint* shaders, uint count) {
|
||
handle_ = glCreateProgram();
|
||
|
||
for (uint i = 0; i < count; ++i) {
|
||
glAttachShader(handle_, shaders[i]);
|
||
}
|
||
|
||
glLinkProgram(handle_);
|
||
|
||
int success;
|
||
glGetProgramiv(handle_, GL_LINK_STATUS, &success);
|
||
if (!success) {
|
||
char info_log[512];
|
||
glGetProgramInfoLog(handle_, 512, nullptr, info_log);
|
||
Logger::error("Shader linking failed: " + std::string(info_log));
|
||
|
||
glDeleteProgram(handle_);
|
||
handle_ = INVALID_HANDLE;
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
std::string Shader::read_file_(const std::string& path) {
|
||
std::ifstream file(path);
|
||
if (!file.is_open()) {
|
||
Logger::error("Failed to open file: " + path);
|
||
return "";
|
||
}
|
||
|
||
std::stringstream buffer;
|
||
buffer << file.rdbuf();
|
||
return buffer.str();
|
||
}
|
||
|
||
} // namespace are
|
||
```
|
||
|
||
### 文件:src/resource/texture.cpp
|
||
|
||
```cpp
|
||
#include "resource/texture.h"
|
||
#include "utils/logger.h"
|
||
#include <glad/glad.h>
|
||
#include <stb_image.h>
|
||
|
||
namespace are {
|
||
|
||
namespace {
|
||
GLenum get_gl_internal_format(TextureFormat format) {
|
||
switch (format) {
|
||
case TextureFormat::R8: return GL_R8;
|
||
case TextureFormat::RG8: return GL_RG8;
|
||
case TextureFormat::RGB8: return GL_RGB8;
|
||
case TextureFormat::RGBA8: return GL_RGBA8;
|
||
case TextureFormat::R16F: return GL_R16F;
|
||
case TextureFormat::RG16F: return GL_RG16F;
|
||
case TextureFormat::RGB16F: return GL_RGB16F;
|
||
case TextureFormat::RGBA16F: return GL_RGBA16F;
|
||
case TextureFormat::R32F: return GL_R32F;
|
||
case TextureFormat::RG32F: return GL_RG32F;
|
||
case TextureFormat::RGB32F: return GL_RGB32F;
|
||
case TextureFormat::RGBA32F: return GL_RGBA32F;
|
||
case TextureFormat::DEPTH24_STENCIL8: return GL_DEPTH24_STENCIL8;
|
||
default: return GL_RGBA8;
|
||
}
|
||
}
|
||
|
||
GLenum get_gl_format(TextureFormat format) {
|
||
switch (format) {
|
||
case TextureFormat::R8:
|
||
case TextureFormat::R16F:
|
||
case TextureFormat::R32F:
|
||
return GL_RED;
|
||
case TextureFormat::RG8:
|
||
case TextureFormat::RG16F:
|
||
case TextureFormat::RG32F:
|
||
return GL_RG;
|
||
case TextureFormat::RGB8:
|
||
case TextureFormat::RGB16F:
|
||
case TextureFormat::RGB32F:
|
||
return GL_RGB;
|
||
case TextureFormat::RGBA8:
|
||
case TextureFormat::RGBA16F:
|
||
case TextureFormat::RGBA32F:
|
||
return GL_RGBA;
|
||
case TextureFormat::DEPTH24_STENCIL8:
|
||
return GL_DEPTH_STENCIL;
|
||
default:
|
||
return GL_RGBA;
|
||
}
|
||
}
|
||
|
||
GLenum get_gl_type(TextureFormat format) {
|
||
switch (format) {
|
||
case TextureFormat::R8:
|
||
case TextureFormat::RG8:
|
||
case TextureFormat::RGB8:
|
||
case TextureFormat::RGBA8:
|
||
return GL_UNSIGNED_BYTE;
|
||
case TextureFormat::R16F:
|
||
case TextureFormat::RG16F:
|
||
case TextureFormat::RGB16F:
|
||
case TextureFormat::RGBA16F:
|
||
case TextureFormat::R32F:
|
||
case TextureFormat::RG32F:
|
||
case TextureFormat::RGB32F:
|
||
case TextureFormat::RGBA32F:
|
||
return GL_FLOAT;
|
||
case TextureFormat::DEPTH24_STENCIL8:
|
||
return GL_UNSIGNED_INT_24_8;
|
||
default:
|
||
return GL_UNSIGNED_BYTE;
|
||
}
|
||
}
|
||
|
||
GLenum get_gl_filter(TextureFilter filter) {
|
||
switch (filter) {
|
||
case TextureFilter::NEAREST: return GL_NEAREST;
|
||
case TextureFilter::LINEAR: return GL_LINEAR;
|
||
case TextureFilter::NEAREST_MIPMAP_NEAREST: return GL_NEAREST_MIPMAP_NEAREST;
|
||
case TextureFilter::LINEAR_MIPMAP_NEAREST: return GL_LINEAR_MIPMAP_NEAREST;
|
||
case TextureFilter::NEAREST_MIPMAP_LINEAR: return GL_NEAREST_MIPMAP_LINEAR;
|
||
case TextureFilter::LINEAR_MIPMAP_LINEAR: return GL_LINEAR_MIPMAP_LINEAR;
|
||
default: return GL_LINEAR;
|
||
}
|
||
}
|
||
|
||
GLenum get_gl_wrap(TextureWrap wrap) {
|
||
switch (wrap) {
|
||
case TextureWrap::REPEAT: return GL_REPEAT;
|
||
case TextureWrap::MIRRORED_REPEAT: return GL_MIRRORED_REPEAT;
|
||
case TextureWrap::CLAMP_TO_EDGE: return GL_CLAMP_TO_EDGE;
|
||
case TextureWrap::CLAMP_TO_BORDER: return GL_CLAMP_TO_BORDER;
|
||
default: return GL_REPEAT;
|
||
}
|
||
}
|
||
}
|
||
|
||
Texture::Texture()
|
||
: handle_(INVALID_HANDLE)
|
||
, width_(0)
|
||
, height_(0)
|
||
, format_(TextureFormat::RGBA8)
|
||
, has_mipmaps_(false) {
|
||
}
|
||
|
||
Texture::~Texture() {
|
||
// Don't auto-release, let user control lifetime
|
||
}
|
||
|
||
bool Texture::load_from_file(const std::string& path, bool generate_mipmaps) {
|
||
// Load image using stb_image
|
||
int width, height, channels;
|
||
stbi_set_flip_vertically_on_load(true);
|
||
unsigned char* data = stbi_load(path.c_str(), &width, &height, &channels, 0);
|
||
|
||
if (!data) {
|
||
Logger::error("Failed to load texture: " + path);
|
||
return false;
|
||
}
|
||
|
||
// Determine format based on channels
|
||
TextureFormat format;
|
||
switch (channels) {
|
||
case 1: format = TextureFormat::R8; break;
|
||
case 2: format = TextureFormat::RG8; break;
|
||
case 3: format = TextureFormat::RGB8; break;
|
||
case 4: format = TextureFormat::RGBA8; break;
|
||
default:
|
||
Logger::error("Unsupported channel count: " + std::to_string(channels));
|
||
stbi_image_free(data);
|
||
return false;
|
||
}
|
||
|
||
// Create texture
|
||
bool success = create(width, height, format);
|
||
if (!success) {
|
||
stbi_image_free(data);
|
||
return false;
|
||
}
|
||
|
||
// Upload data
|
||
success = upload(data, width, height, format);
|
||
stbi_image_free(data);
|
||
|
||
if (!success) {
|
||
return false;
|
||
}
|
||
|
||
// Generate mipmaps if requested
|
||
if (generate_mipmaps) {
|
||
this->generate_mipmaps();
|
||
}
|
||
|
||
Logger::info("Texture loaded successfully: " + path);
|
||
return true;
|
||
}
|
||
|
||
bool Texture::create(uint width, uint height, TextureFormat format) {
|
||
if (handle_ != INVALID_HANDLE) {
|
||
Logger::warning("Texture already created, releasing old texture");
|
||
release();
|
||
}
|
||
|
||
width_ = width;
|
||
height_ = height;
|
||
format_ = format;
|
||
|
||
glGenTextures(1, &handle_);
|
||
glBindTexture(GL_TEXTURE_2D, handle_);
|
||
|
||
GLenum internal_format = get_gl_internal_format(format);
|
||
GLenum gl_format = get_gl_format(format);
|
||
GLenum type = get_gl_type(format);
|
||
|
||
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, gl_format, type, nullptr);
|
||
|
||
// Set default parameters
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||
|
||
glBindTexture(GL_TEXTURE_2D, 0);
|
||
|
||
return true;
|
||
}
|
||
|
||
bool Texture::upload(const void* data, uint width, uint height, TextureFormat format) {
|
||
if (handle_ == INVALID_HANDLE) {
|
||
Logger::error("Cannot upload to invalid texture");
|
||
return false;
|
||
}
|
||
|
||
if (width != width_ || height != height_ || format != format_) {
|
||
Logger::warning("Upload parameters differ from texture creation, recreating texture");
|
||
create(width, height, format);
|
||
}
|
||
|
||
glBindTexture(GL_TEXTURE_2D, handle_);
|
||
|
||
GLenum gl_format = get_gl_format(format);
|
||
GLenum type = get_gl_type(format);
|
||
|
||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, gl_format, type, data);
|
||
|
||
glBindTexture(GL_TEXTURE_2D, 0);
|
||
|
||
return true;
|
||
}
|
||
|
||
void Texture::set_filter(TextureFilter min_filter, TextureFilter mag_filter) {
|
||
if (handle_ == INVALID_HANDLE) {
|
||
Logger::error("Cannot set filter on invalid texture");
|
||
return;
|
||
}
|
||
|
||
glBindTexture(GL_TEXTURE_2D, handle_);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, get_gl_filter(min_filter));
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, get_gl_filter(mag_filter));
|
||
glBindTexture(GL_TEXTURE_2D, 0);
|
||
}
|
||
|
||
void Texture::set_wrap(TextureWrap wrap_s, TextureWrap wrap_t) {
|
||
if (handle_ == INVALID_HANDLE) {
|
||
Logger::error("Cannot set wrap mode on invalid texture");
|
||
return;
|
||
}
|
||
|
||
glBindTexture(GL_TEXTURE_2D, handle_);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, get_gl_wrap(wrap_s));
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, get_gl_wrap(wrap_t));
|
||
glBindTexture(GL_TEXTURE_2D, 0);
|
||
}
|
||
|
||
void Texture::generate_mipmaps() {
|
||
if (handle_ == INVALID_HANDLE) {
|
||
Logger::error("Cannot generate mipmaps for invalid texture");
|
||
return;
|
||
}
|
||
|
||
glBindTexture(GL_TEXTURE_2D, handle_);
|
||
glGenerateMipmap(GL_TEXTURE_2D);
|
||
glBindTexture(GL_TEXTURE_2D, 0);
|
||
|
||
has_mipmaps_ = true;
|
||
}
|
||
|
||
void Texture::bind(uint unit) const {
|
||
if (handle_ == INVALID_HANDLE) {
|
||
Logger::warning("Attempting to bind invalid texture");
|
||
return;
|
||
}
|
||
|
||
glActiveTexture(GL_TEXTURE0 + unit);
|
||
glBindTexture(GL_TEXTURE_2D, handle_);
|
||
}
|
||
|
||
void Texture::unbind() const {
|
||
glBindTexture(GL_TEXTURE_2D, 0);
|
||
}
|
||
|
||
void Texture::release() {
|
||
if (handle_ != INVALID_HANDLE) {
|
||
glDeleteTextures(1, &handle_);
|
||
handle_ = INVALID_HANDLE;
|
||
}
|
||
|
||
width_ = 0;
|
||
height_ = 0;
|
||
has_mipmaps_ = false;
|
||
}
|
||
|
||
} // namespace are
|
||
```
|
||
|
||
### 文件:src/utils/config.cpp
|
||
|
||
```cpp
|
||
#include "utils/config.h"
|
||
#include "utils/logger.h"
|
||
#include <fstream>
|
||
#include <sstream>
|
||
#include <algorithm>
|
||
|
||
namespace are {
|
||
|
||
// Static storage
|
||
static std::unordered_map<std::string, std::string> g_config_map;
|
||
|
||
// Helper function to trim whitespace
|
||
static std::string trim(const std::string& str) {
|
||
size_t first = str.find_first_not_of(" \t\r\n");
|
||
if (first == std::string::npos) return "";
|
||
size_t last = str.find_last_not_of(" \t\r\n");
|
||
return str.substr(first, last - first + 1);
|
||
}
|
||
|
||
bool Config::load(const std::string& path) {
|
||
std::ifstream file(path);
|
||
if (!file.is_open()) {
|
||
Logger::error("Failed to open config file: " + path);
|
||
return false;
|
||
}
|
||
|
||
g_config_map.clear();
|
||
|
||
std::string line;
|
||
std::string current_section;
|
||
|
||
while (std::getline(file, line)) {
|
||
line = trim(line);
|
||
|
||
// Skip empty lines and comments
|
||
if (line.empty() || line[0] == '#' || line[0] == ';') {
|
||
continue;
|
||
}
|
||
|
||
// Section header
|
||
if (line[0] == '[' && line.back() == ']') {
|
||
current_section = line.substr(1, line.length() - 2);
|
||
continue;
|
||
}
|
||
|
||
// Key-value pair
|
||
size_t pos = line.find('=');
|
||
if (pos != std::string::npos) {
|
||
std::string key = trim(line.substr(0, pos));
|
||
std::string value = trim(line.substr(pos + 1));
|
||
|
||
// Add section prefix if in a section
|
||
if (!current_section.empty()) {
|
||
key = current_section + "." + key;
|
||
}
|
||
|
||
g_config_map[key] = value;
|
||
}
|
||
}
|
||
|
||
Logger::info("Config loaded: " + path + " (" + std::to_string(g_config_map.size()) + " entries)");
|
||
return true;
|
||
}
|
||
|
||
bool Config::save(const std::string& path) {
|
||
std::ofstream file(path);
|
||
if (!file.is_open()) {
|
||
Logger::error("Failed to open config file for writing: " + path);
|
||
return false;
|
||
}
|
||
|
||
for (const auto& pair : g_config_map) {
|
||
file << pair.first << "=" << pair.second << std::endl;
|
||
}
|
||
|
||
Logger::info("Config saved: " + path);
|
||
return true;
|
||
}
|
||
|
||
std::string Config::get_string(const std::string& key, const std::string& default_value) {
|
||
auto it = g_config_map.find(key);
|
||
if (it != g_config_map.end()) {
|
||
return it->second;
|
||
}
|
||
return default_value;
|
||
}
|
||
|
||
int Config::get_int(const std::string& key, int default_value) {
|
||
auto it = g_config_map.find(key);
|
||
if (it != g_config_map.end()) {
|
||
try {
|
||
return std::stoi(it->second);
|
||
} catch (...) {
|
||
Logger::warning("Failed to parse int for key: " + key);
|
||
}
|
||
}
|
||
return default_value;
|
||
}
|
||
|
||
float Config::get_float(const std::string& key, float default_value) {
|
||
auto it = g_config_map.find(key);
|
||
if (it != g_config_map.end()) {
|
||
try {
|
||
return std::stof(it->second);
|
||
} catch (...) {
|
||
Logger::warning("Failed to parse float for key: " + key);
|
||
}
|
||
}
|
||
return default_value;
|
||
}
|
||
|
||
bool Config::get_bool(const std::string& key, bool default_value) {
|
||
auto it = g_config_map.find(key);
|
||
if (it != g_config_map.end()) {
|
||
std::string value = it->second;
|
||
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
|
||
|
||
if (value == "true" || value == "1" || value == "yes" || value == "on") {
|
||
return true;
|
||
}
|
||
if (value == "false" || value == "0" || value == "no" || value == "off") {
|
||
return false;
|
||
}
|
||
}
|
||
return default_value;
|
||
}
|
||
|
||
void Config::set_string(const std::string& key, const std::string& value) {
|
||
g_config_map[key] = value;
|
||
}
|
||
|
||
void Config::set_int(const std::string& key, int value) {
|
||
g_config_map[key] = std::to_string(value);
|
||
}
|
||
|
||
void Config::set_float(const std::string& key, float value) {
|
||
g_config_map[key] = std::to_string(value);
|
||
}
|
||
|
||
void Config::set_bool(const std::string& key, bool value) {
|
||
g_config_map[key] = value ? "true" : "false";
|
||
}
|
||
|
||
} // namespace are
|
||
```
|
||
|
||
### 文件:src/utils/logger.cpp
|
||
|
||
```cpp
|
||
#include "utils/logger.h"
|
||
#include <iostream>
|
||
#include <fstream>
|
||
#include <ctime>
|
||
#include <iomanip>
|
||
#include <sstream>
|
||
|
||
namespace are {
|
||
|
||
// Static members
|
||
static LogLevel g_min_level = LogLevel::DEBUG;
|
||
static std::ofstream g_log_file;
|
||
static bool g_initialized = false;
|
||
|
||
bool Logger::initialize(const std::string& log_file) {
|
||
if (g_initialized) {
|
||
return true;
|
||
}
|
||
|
||
if (!log_file.empty()) {
|
||
g_log_file.open(log_file, std::ios::out | std::ios::app);
|
||
if (!g_log_file.is_open()) {
|
||
std::cerr << "Failed to open log file: " << log_file << std::endl;
|
||
return false;
|
||
}
|
||
}
|
||
|
||
g_initialized = true;
|
||
return true;
|
||
}
|
||
|
||
void Logger::shutdown() {
|
||
if (g_log_file.is_open()) {
|
||
g_log_file.close();
|
||
}
|
||
g_initialized = false;
|
||
}
|
||
|
||
static std::string get_current_time() {
|
||
auto now = std::time(nullptr);
|
||
auto tm = *std::localtime(&now);
|
||
std::ostringstream oss;
|
||
oss << std::put_time(&tm, "%H:%M:%S");
|
||
return oss.str();
|
||
}
|
||
|
||
static std::string level_to_string(LogLevel level) {
|
||
switch (level) {
|
||
case LogLevel::DEBUG: return "DEBUG";
|
||
case LogLevel::INFO: return "INFO";
|
||
case LogLevel::WARNING: return "WARN";
|
||
case LogLevel::ERROR: return "ERROR";
|
||
case LogLevel::FATAL: return "FATAL";
|
||
default: return "UNKNOWN";
|
||
}
|
||
}
|
||
|
||
void Logger::log(LogLevel level, const std::string& message) {
|
||
if (level < g_min_level) return;
|
||
|
||
std::string time_str = get_current_time();
|
||
std::string level_str = level_to_string(level);
|
||
std::string formatted = "[" + time_str + "] [" + level_str + "] " + message;
|
||
|
||
// Console output
|
||
if (level >= LogLevel::ERROR) {
|
||
std::cerr << formatted << std::endl;
|
||
} else {
|
||
std::cout << formatted << std::endl;
|
||
}
|
||
|
||
// File output
|
||
if (g_log_file.is_open()) {
|
||
g_log_file << formatted << std::endl;
|
||
g_log_file.flush();
|
||
}
|
||
}
|
||
|
||
void Logger::debug(const std::string& message) {
|
||
log(LogLevel::DEBUG, message);
|
||
}
|
||
|
||
void Logger::info(const std::string& message) {
|
||
log(LogLevel::INFO, message);
|
||
}
|
||
|
||
void Logger::warning(const std::string& message) {
|
||
log(LogLevel::WARNING, message);
|
||
}
|
||
|
||
void Logger::error(const std::string& message) {
|
||
log(LogLevel::ERROR, message);
|
||
}
|
||
|
||
void Logger::fatal(const std::string& message) {
|
||
log(LogLevel::FATAL, message);
|
||
}
|
||
|
||
void Logger::set_level(LogLevel level) {
|
||
g_min_level = level;
|
||
}
|
||
|
||
} // namespace are
|
||
```
|
||
|