ADD: Asset Loading
This commit is contained in:
parent
6e37fda356
commit
eeb7c9c00f
@ -60,6 +60,13 @@ CPMAddPackage(
|
||||
GIT_TAG v1.91.5
|
||||
DOWNLOAD_ONLY YES
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME spdlog
|
||||
GITHUB_REPOSITORY gabime/spdlog
|
||||
GIT_TAG v1.15.3
|
||||
)
|
||||
|
||||
add_library(imgui STATIC
|
||||
${imgui_SOURCE_DIR}/imgui.cpp
|
||||
${imgui_SOURCE_DIR}/imgui_draw.cpp
|
||||
@ -103,9 +110,33 @@ add_library(Engine STATIC
|
||||
engine/src/utils/openglWrapper/shader/UniformValue.h
|
||||
engine/src/layer/Scene.cpp
|
||||
engine/src/layer/Scene.h
|
||||
engine/src/loader/AssetLoader.cpp
|
||||
engine/src/loader/AssetLoader.h
|
||||
engine/src/loader/assets/AssetTypes.h
|
||||
engine/src/loader/assets/AssetRequests.h
|
||||
engine/src/loader/assets/AssetRequests.cpp
|
||||
engine/src/loader/assets/RawAssetData.h
|
||||
engine/src/loader/assets/AssetFormatters.h
|
||||
engine/src/loader/textures/TextureUploader.cpp
|
||||
engine/src/loader/textures/TextureUploader.h
|
||||
engine/src/loader/textures/TextureImporter.cpp
|
||||
engine/src/loader/textures/TextureImporter.h
|
||||
engine/src/loader/textures/Texture2D.cpp
|
||||
engine/src/loader/textures/Texture2D.h
|
||||
engine/src/loader/models/ModelImporter.cpp
|
||||
engine/src/loader/models/ModelImporter.h
|
||||
engine/src/loader/models/ModelUploader.cpp
|
||||
engine/src/loader/models/ModelUploader.h
|
||||
engine/src/loader/models/Material.cpp
|
||||
engine/src/loader/models/Material.h
|
||||
engine/src/loader/assets/LoadedAssets.h
|
||||
engine/src/loader/models/Model.cpp
|
||||
engine/src/loader/models/Model.h
|
||||
engine/src/loader/models/MeshSection.cpp
|
||||
engine/src/loader/models/MeshSection.h
|
||||
)
|
||||
target_include_directories(Engine PUBLIC engine/src)
|
||||
target_link_libraries(Engine PUBLIC OpenGL::GL glfw glad glm::glm imgui)
|
||||
target_link_libraries(Engine PUBLIC OpenGL::GL glfw glad glm::glm imgui spdlog::spdlog tinyobjloader stb_image)
|
||||
|
||||
# --- Executable ---
|
||||
add_executable(ColorRace
|
||||
@ -151,6 +182,4 @@ add_executable(ColorRace
|
||||
|
||||
target_link_libraries(ColorRace PRIVATE
|
||||
Engine
|
||||
tinyobjloader
|
||||
stb_image
|
||||
)
|
||||
89
engine/src/loader/AssetLoader.cpp
Normal file
89
engine/src/loader/AssetLoader.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#include "AssetLoader.h"
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "textures/TextureImporter.h"
|
||||
|
||||
void engine::AssetLoader::scheduleAsset(const AssetRequest &request) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(pendingMutex);
|
||||
pendingQueue.push(request);
|
||||
++total;
|
||||
}
|
||||
pendingCondition.notify_one();
|
||||
std::visit([]<typename T>(const T& req) -> void {
|
||||
using Type = std::decay_t<T>;
|
||||
if constexpr (std::is_same_v<Type, TextureRequest>) {
|
||||
spdlog::debug("Scheduled Texture Request: {} -> {}", req.name, req.path);
|
||||
} else if constexpr (std::is_same_v<Type, ModelRequest>) {
|
||||
spdlog::debug("Scheduled Model Request: {} -> {}", req.name, req.path);
|
||||
}
|
||||
}, request);
|
||||
}
|
||||
|
||||
void engine::AssetLoader::start() {
|
||||
if (!running) {
|
||||
running = true;
|
||||
loadingThread = std::thread(&AssetLoader::loadingThreadFunc, this);
|
||||
}
|
||||
}
|
||||
|
||||
void engine::AssetLoader::stop() {
|
||||
running = false;
|
||||
pendingCondition.notify_all();
|
||||
if (loadingThread.joinable()) {
|
||||
loadingThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
engine::LoadingProgress engine::AssetLoader::getProgress() const {
|
||||
return {total.load(), loaded.load()};
|
||||
}
|
||||
|
||||
void engine::AssetLoader::loadingThreadFunc() {
|
||||
while (running) {
|
||||
AssetRequest request;
|
||||
{
|
||||
std::unique_lock lock(pendingMutex);
|
||||
pendingCondition.wait(lock, [this] {
|
||||
return !pendingQueue.empty() || !running;
|
||||
});
|
||||
|
||||
if (!running && pendingQueue.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
request = pendingQueue.front();
|
||||
pendingQueue.pop();
|
||||
}
|
||||
|
||||
IntermediateAsset result = std::visit([]<typename T0>(T0& req) -> IntermediateAsset {
|
||||
using T = std::decay_t<T0>;
|
||||
if constexpr (std::is_same_v<T, TextureRequest>) {
|
||||
return processTextureRequest(req);
|
||||
} else if constexpr (std::is_same_v<T, ModelRequest>) {
|
||||
return processModelRequest(req);
|
||||
}
|
||||
}, request);
|
||||
|
||||
{
|
||||
std::lock_guard lock(readyMutex);
|
||||
readyQueue.push(std::move(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
engine::RawTextureData engine::AssetLoader::processTextureRequest(const TextureRequest &request) {
|
||||
spdlog::debug("Processing Texture Request: {} -> {}", request.name, request.path);
|
||||
RawTextureData textureData = TextureImporter::import(request.path, request.flipY);
|
||||
textureData.name = request.name;
|
||||
return textureData;
|
||||
}
|
||||
|
||||
engine::RawModelData engine::AssetLoader::processModelRequest(const ModelRequest &request) {
|
||||
spdlog::debug("Processing Model Request: {} -> {}", request.name, request.path);
|
||||
|
||||
}
|
||||
62
engine/src/loader/AssetLoader.h
Normal file
62
engine/src/loader/AssetLoader.h
Normal file
@ -0,0 +1,62 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#ifndef COLORRACE_ASSETLOADER_H
|
||||
#define COLORRACE_ASSETLOADER_H
|
||||
#include <condition_variable>
|
||||
#include <format>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "assets/AssetRequests.h"
|
||||
#include "assets/RawAssetData.h"
|
||||
|
||||
namespace engine {
|
||||
struct LoadingProgress {
|
||||
int total;
|
||||
int loaded;
|
||||
float fraction() const {
|
||||
return total > 0 ? static_cast<float>(loaded) / total : 1.f;
|
||||
}
|
||||
bool isDone() const {
|
||||
return loaded >= total;
|
||||
}
|
||||
};
|
||||
|
||||
class AssetLoader {
|
||||
public:
|
||||
void scheduleAsset(const AssetRequest& request);
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
void processUploadQueue(int maxPerFrame);
|
||||
LoadingProgress getProgress() const;
|
||||
|
||||
private:
|
||||
void loadingThreadFunc();
|
||||
|
||||
static RawTextureData processTextureRequest(const TextureRequest& request);
|
||||
static RawModelData processModelRequest(const ModelRequest& request);
|
||||
|
||||
std::queue<AssetRequest> pendingQueue;
|
||||
std::mutex pendingMutex;
|
||||
std::condition_variable pendingCondition;
|
||||
|
||||
std::queue<IntermediateAsset> readyQueue;
|
||||
std::mutex readyMutex;
|
||||
|
||||
std::atomic<int> total{0};
|
||||
std::atomic<int> loaded{0};
|
||||
|
||||
std::thread loadingThread;
|
||||
std::atomic<bool> running{false};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif //COLORRACE_ASSETLOADER_H
|
||||
59
engine/src/loader/assets/AssetFormatters.h
Normal file
59
engine/src/loader/assets/AssetFormatters.h
Normal file
@ -0,0 +1,59 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#ifndef COLORRACE_ASSETFORMATTERS_H
|
||||
#define COLORRACE_ASSETFORMATTERS_H
|
||||
#pragma once
|
||||
|
||||
#include "AssetRequests.h"
|
||||
#include "AssetTypes.h"
|
||||
|
||||
#include <format>
|
||||
|
||||
|
||||
template<>
|
||||
struct std::formatter<engine::AssetRequest>
|
||||
{
|
||||
constexpr auto parse(
|
||||
std::format_parse_context& ctx)
|
||||
{
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
|
||||
auto format(
|
||||
const engine::AssetRequest& request,
|
||||
std::format_context& ctx) const
|
||||
{
|
||||
return std::format_to(
|
||||
ctx.out(),
|
||||
"{}",
|
||||
engine::assetRequestToString(request)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<>
|
||||
struct std::formatter<engine::TextureType>
|
||||
{
|
||||
constexpr auto parse(
|
||||
std::format_parse_context& ctx)
|
||||
{
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
|
||||
auto format(
|
||||
engine::TextureType type,
|
||||
std::format_context& ctx) const
|
||||
{
|
||||
return std::format_to(
|
||||
ctx.out(),
|
||||
"{}",
|
||||
engine::toString(type)
|
||||
);
|
||||
}
|
||||
};
|
||||
#endif //COLORRACE_ASSETFORMATTERS_H
|
||||
43
engine/src/loader/assets/AssetRequests.cpp
Normal file
43
engine/src/loader/assets/AssetRequests.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
// AssetRequests.cpp
|
||||
|
||||
#include "AssetRequests.h"
|
||||
|
||||
#include <format>
|
||||
#include <type_traits>
|
||||
|
||||
|
||||
namespace engine {
|
||||
|
||||
std::string assetRequestToString(
|
||||
const AssetRequest& request)
|
||||
{
|
||||
return std::visit(
|
||||
[](auto const& req)
|
||||
{
|
||||
using Type = std::decay_t<decltype(req)>;
|
||||
|
||||
|
||||
if constexpr(std::is_same_v<Type, TextureRequest>)
|
||||
{
|
||||
return std::format(
|
||||
"TextureRequest{{name='{}', path='{}'}}",
|
||||
req.name,
|
||||
req.path
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::format(
|
||||
"ModelRequest{{name='{}', path='{}'}}",
|
||||
req.name,
|
||||
req.path
|
||||
);
|
||||
}
|
||||
},
|
||||
request);
|
||||
}
|
||||
|
||||
}
|
||||
39
engine/src/loader/assets/AssetRequests.h
Normal file
39
engine/src/loader/assets/AssetRequests.h
Normal file
@ -0,0 +1,39 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#ifndef COLORRACE_ASSEZREQUESTS_H
|
||||
#define COLORRACE_ASSEZREQUESTS_H
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
namespace engine {
|
||||
|
||||
struct TextureRequest
|
||||
{
|
||||
std::string name;
|
||||
std::string path;
|
||||
bool flipY = true;
|
||||
};
|
||||
|
||||
|
||||
struct ModelRequest
|
||||
{
|
||||
std::string name;
|
||||
std::string path;
|
||||
};
|
||||
|
||||
|
||||
using AssetRequest =
|
||||
std::variant<
|
||||
TextureRequest,
|
||||
ModelRequest
|
||||
>;
|
||||
|
||||
|
||||
std::string assetRequestToString(const AssetRequest& request);
|
||||
|
||||
}
|
||||
#endif //COLORRACE_ASSEZREQUESTS_H
|
||||
75
engine/src/loader/assets/AssetTypes.h
Normal file
75
engine/src/loader/assets/AssetTypes.h
Normal file
@ -0,0 +1,75 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#ifndef COLORRACE_ASSETTYPES_H
|
||||
#define COLORRACE_ASSETTYPES_H
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
namespace engine {
|
||||
|
||||
enum class AssetType : uint8_t
|
||||
{
|
||||
Texture,
|
||||
Model
|
||||
};
|
||||
|
||||
|
||||
enum class TextureType : uint8_t
|
||||
{
|
||||
Diffuse,
|
||||
Specular,
|
||||
Normal,
|
||||
Emissive,
|
||||
Opacity,
|
||||
|
||||
Count
|
||||
};
|
||||
|
||||
|
||||
enum class ModelSplitPolicy : uint8_t
|
||||
{
|
||||
Combined,
|
||||
ByMaterial,
|
||||
ByObject
|
||||
};
|
||||
|
||||
|
||||
constexpr std::string_view toString(TextureType type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case TextureType::Diffuse: return "Diffuse";
|
||||
case TextureType::Specular: return "Specular";
|
||||
case TextureType::Normal: return "Normal";
|
||||
case TextureType::Emissive: return "Emissive";
|
||||
case TextureType::Opacity: return "Opacity";
|
||||
case TextureType::Count: break;
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
|
||||
constexpr std::string_view toString(ModelSplitPolicy policy)
|
||||
{
|
||||
switch(policy)
|
||||
{
|
||||
case ModelSplitPolicy::Combined:
|
||||
return "Combined";
|
||||
|
||||
case ModelSplitPolicy::ByMaterial:
|
||||
return "ByMaterial";
|
||||
|
||||
case ModelSplitPolicy::ByObject:
|
||||
return "ByObject";
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
}
|
||||
#endif //COLORRACE_ASSETTYPES_H
|
||||
30
engine/src/loader/assets/LoadedAssets.h
Normal file
30
engine/src/loader/assets/LoadedAssets.h
Normal file
@ -0,0 +1,30 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#ifndef COLORRACE_LOADEDASSETS_H
|
||||
#define COLORRACE_LOADEDASSETS_H
|
||||
// engine/src/loader/assets/LoadedAssets.h
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include "loader/textures/Texture2D.h"
|
||||
#include "loader/models/Model.h"
|
||||
|
||||
namespace engine {
|
||||
|
||||
struct LoadedTexture {
|
||||
std::string name;
|
||||
std::shared_ptr<Texture2D> texture;
|
||||
};
|
||||
|
||||
struct LoadedModel {
|
||||
std::string name;
|
||||
std::shared_ptr<Model> model;
|
||||
};
|
||||
|
||||
using LoadedAsset = std::variant<LoadedTexture, LoadedModel>;
|
||||
|
||||
}
|
||||
#endif //COLORRACE_LOADEDASSETS_H
|
||||
81
engine/src/loader/assets/RawAssetData.h
Normal file
81
engine/src/loader/assets/RawAssetData.h
Normal file
@ -0,0 +1,81 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#ifndef COLORRACE_RAWASSETDATA_H
|
||||
#define COLORRACE_RAWASSETDATA_H
|
||||
#pragma once
|
||||
|
||||
#include "AssetTypes.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
|
||||
namespace engine {
|
||||
|
||||
|
||||
struct RawTextureData
|
||||
{
|
||||
std::string name;
|
||||
|
||||
std::vector<unsigned char> pixels;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
int channels;
|
||||
};
|
||||
|
||||
|
||||
struct RawMaterialData
|
||||
{
|
||||
std::string name;
|
||||
|
||||
std::unordered_map<TextureType, std::string> texturePaths;
|
||||
|
||||
float shininess;
|
||||
float opacity;
|
||||
};
|
||||
|
||||
|
||||
struct RawMeshSection
|
||||
{
|
||||
std::vector<float> vertices;
|
||||
std::vector<float> normals;
|
||||
std::vector<float> textureCoords;
|
||||
|
||||
std::vector<int> indices;
|
||||
|
||||
std::string materialName;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
|
||||
struct RawObjectData
|
||||
{
|
||||
std::string name;
|
||||
|
||||
std::vector<RawMeshSection> subModels;
|
||||
};
|
||||
|
||||
|
||||
struct RawModelData
|
||||
{
|
||||
std::string name;
|
||||
|
||||
std::vector<RawObjectData> objects;
|
||||
|
||||
ModelSplitPolicy splitPolicy;
|
||||
};
|
||||
|
||||
|
||||
using IntermediateAsset =
|
||||
std::variant<
|
||||
RawTextureData,
|
||||
RawModelData
|
||||
>;
|
||||
|
||||
|
||||
}
|
||||
#endif //COLORRACE_RAWASSETDATA_H
|
||||
5
engine/src/loader/models/Material.cpp
Normal file
5
engine/src/loader/models/Material.cpp
Normal file
@ -0,0 +1,5 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#include "Material.h"
|
||||
41
engine/src/loader/models/Material.h
Normal file
41
engine/src/loader/models/Material.h
Normal file
@ -0,0 +1,41 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#ifndef COLORRACE_MATERIAL_H
|
||||
#define COLORRACE_MATERIAL_H
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include "loader/assets/AssetTypes.h"
|
||||
#include "loader/textures/Texture2D.h"
|
||||
|
||||
|
||||
namespace engine {
|
||||
|
||||
class Material {
|
||||
public:
|
||||
Material() = default;
|
||||
Material(std::array<std::shared_ptr<Texture2D>, static_cast<size_t>(TextureType::Count)> textures,
|
||||
float shininess, float opacity)
|
||||
: m_textures(std::move(textures)), m_shininess(shininess), m_opacity(opacity) {}
|
||||
|
||||
void bind() const {
|
||||
for (size_t i = 0; i < m_textures.size(); ++i) {
|
||||
if (m_textures[i]) m_textures[i]->bind(static_cast<unsigned int>(i));
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] float getShininess() const { return m_shininess; }
|
||||
[[nodiscard]] float getOpacity() const { return m_opacity; }
|
||||
|
||||
private:
|
||||
std::array<std::shared_ptr<Texture2D>, static_cast<size_t>(TextureType::Count)> m_textures;
|
||||
float m_shininess = 1.0f;
|
||||
float m_opacity = 1.0f;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //COLORRACE_MATERIAL_H
|
||||
5
engine/src/loader/models/MeshSection.cpp
Normal file
5
engine/src/loader/models/MeshSection.cpp
Normal file
@ -0,0 +1,5 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#include "MeshSection.h"
|
||||
28
engine/src/loader/models/MeshSection.h
Normal file
28
engine/src/loader/models/MeshSection.h
Normal file
@ -0,0 +1,28 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#ifndef COLORRACE_MESHSECTION_H
|
||||
#define COLORRACE_MESHSECTION_H
|
||||
|
||||
|
||||
// engine/src/renderer/model/MeshSection.h
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "Material.h"
|
||||
|
||||
namespace engine {
|
||||
|
||||
struct MeshSection {
|
||||
std::string name;
|
||||
unsigned int indexOffset; // in Indizes, nicht Bytes
|
||||
unsigned int indexCount;
|
||||
std::shared_ptr<Material> material;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //COLORRACE_MESHSECTION_H
|
||||
52
engine/src/loader/models/Model.cpp
Normal file
52
engine/src/loader/models/Model.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#include "Model.h"
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
using namespace engine;
|
||||
Model::Model(std::unique_ptr<Vao> vao, int vertexCount, bool isIndexed) : m_vao(std::move(vao)), m_vertexCount(vertexCount), m_isIndexed(isIndexed) {}
|
||||
|
||||
Model::Model(std::unique_ptr<Vao> vao, int vertexCount) : Model(std::move(vao), vertexCount, false) {}
|
||||
|
||||
void Model::bind() const {
|
||||
m_vao->bind();
|
||||
}
|
||||
|
||||
void Model::unbind() const {
|
||||
m_vao->unbind();
|
||||
}
|
||||
|
||||
Model::Model(std::unique_ptr<Vao> vao, int vertexCount, std::vector<MeshSection> sections)
|
||||
: m_vao(std::move(vao)), m_vertexCount(vertexCount), m_isIndexed(true), m_sections(std::move(sections)) {}
|
||||
|
||||
void Model::draw() const {
|
||||
bind();
|
||||
if (m_sections.empty()) {
|
||||
// Legacy-Pfad: unverändert wie bisher, z.B. für CubeFactory-Primitive
|
||||
if (m_isIndexed) glDrawElements(GL_TRIANGLES, m_vertexCount, GL_UNSIGNED_INT, nullptr);
|
||||
else glDrawArrays(GL_TRIANGLES, 0, m_vertexCount);
|
||||
} else {
|
||||
for (const auto& section : m_sections) {
|
||||
section.material->bind();
|
||||
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(section.indexCount), GL_UNSIGNED_INT,
|
||||
reinterpret_cast<const void*>(section.indexOffset * sizeof(unsigned int)));
|
||||
}
|
||||
}
|
||||
unbind();
|
||||
}
|
||||
|
||||
void Model::drawSection(const std::string& name) const {
|
||||
auto it = std::ranges::find_if(m_sections,
|
||||
[&](const MeshSection& s) { return s.name == name; });
|
||||
if (it == m_sections.end()) {
|
||||
spdlog::warn("MeshSection '{}' nicht gefunden", name);
|
||||
return;
|
||||
}
|
||||
it->material->bind();
|
||||
bind();
|
||||
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(it->indexCount), GL_UNSIGNED_INT,
|
||||
reinterpret_cast<const void*>(it->indexOffset * sizeof(unsigned int)));
|
||||
unbind();
|
||||
}
|
||||
37
engine/src/loader/models/Model.h
Normal file
37
engine/src/loader/models/Model.h
Normal file
@ -0,0 +1,37 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#ifndef COLORRACE_MODEL_H
|
||||
#define COLORRACE_MODEL_H
|
||||
#include <memory>
|
||||
|
||||
#include "MeshSection.h"
|
||||
#include "utils/openglWrapper/openglObjects/Vao.h"
|
||||
|
||||
|
||||
namespace engine {
|
||||
class Model {
|
||||
public:
|
||||
Model(std::unique_ptr<Vao> vao, int vertexCount, bool isIndexed);
|
||||
Model(std::unique_ptr<Vao> vao, int vertexCount);
|
||||
Model(std::unique_ptr<Vao> vao, int vertexCount, std::vector<MeshSection> sections); // neu
|
||||
|
||||
void bind() const;
|
||||
void unbind() const;
|
||||
void draw() const;
|
||||
void drawSection(const std::string& name) const; // neu
|
||||
|
||||
[[nodiscard]] int getVertexCount() const { return m_vertexCount; }
|
||||
[[nodiscard]] bool isIndexed() const { return m_isIndexed; }
|
||||
[[nodiscard]] bool hasSections() const { return !m_sections.empty(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<Vao> m_vao;
|
||||
int m_vertexCount;
|
||||
bool m_isIndexed;
|
||||
std::vector<MeshSection> m_sections; // leer bei den alten Konstruktoren
|
||||
};
|
||||
}
|
||||
|
||||
#endif //COLORRACE_MODEL_H
|
||||
104
engine/src/loader/models/ModelImporter.cpp
Normal file
104
engine/src/loader/models/ModelImporter.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#include "ModelImporter.h"
|
||||
#define TINYOBJLOADER_IMPLEMENTATION
|
||||
#include <filesystem>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "tiny_obj_loader.h"
|
||||
#include "loader/textures/TextureImporter.h"
|
||||
|
||||
|
||||
engine::RawModelBundle engine::ModelImporter::importFromFile(const std::string& objPath) {
|
||||
tinyobj::ObjReaderConfig config;
|
||||
config.mtl_search_path = std::filesystem::path(objPath).parent_path().string();
|
||||
config.triangulate = true; // GL braucht Dreiecke, keine n-Ecke
|
||||
|
||||
tinyobj::ObjReader reader;
|
||||
if (!reader.ParseFromFile(objPath, config)) {
|
||||
throw std::runtime_error("OBJ-Import fehlgeschlagen '" + objPath + "': " + reader.Error());
|
||||
}
|
||||
|
||||
const auto& attrib = reader.GetAttrib();
|
||||
const auto& shapes = reader.GetShapes();
|
||||
const auto& tinyMaterials = reader.GetMaterials();
|
||||
|
||||
RawModelBundle bundle;
|
||||
bundle.model.name = std::filesystem::path(objPath).stem().string();
|
||||
|
||||
// 1. Materialien übersetzen
|
||||
for (const auto& mat : tinyMaterials) {
|
||||
RawMaterialData material;
|
||||
material.name = mat.name;
|
||||
if (!mat.diffuse_texname.empty()) material.texturePaths[TextureType::Diffuse] = mat.diffuse_texname;
|
||||
if (!mat.specular_texname.empty()) material.texturePaths[TextureType::Specular] = mat.specular_texname;
|
||||
if (!mat.bump_texname.empty()) material.texturePaths[TextureType::Normal] = mat.bump_texname;
|
||||
if (!mat.emissive_texname.empty()) material.texturePaths[TextureType::Emissive] = mat.emissive_texname;
|
||||
if (!mat.alpha_texname.empty()) material.texturePaths[TextureType::Opacity] = mat.alpha_texname;
|
||||
material.shininess = mat.shininess;
|
||||
material.opacity = mat.dissolve; // 'd' in der .mtl
|
||||
bundle.materials.push_back(std::move(material));
|
||||
}
|
||||
|
||||
// 2. Referenzierte Texturen dekodieren (dedupliziert innerhalb dieses Imports)
|
||||
std::unordered_set<std::string> seen;
|
||||
auto mtlDir = std::filesystem::path(objPath).parent_path();
|
||||
for (const auto& material : bundle.materials) {
|
||||
for (const auto& [type, relPath] : material.texturePaths) {
|
||||
if (seen.insert(relPath).second) {
|
||||
bundle.textures.push_back(TextureImporter::import((mtlDir / relPath).string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Shapes -> Objekte, Faces nach Material gruppieren
|
||||
for (const auto& shape : shapes) {
|
||||
RawObjectData object;
|
||||
object.name = shape.name.empty() ? "default" : shape.name;
|
||||
|
||||
std::unordered_map<int, RawMeshSection> sectionsByMaterial;
|
||||
|
||||
size_t indexOffset = 0;
|
||||
for (size_t f = 0; f < shape.mesh.num_face_vertices.size(); ++f) {
|
||||
int faceVertexCount = shape.mesh.num_face_vertices[f];
|
||||
int materialId = shape.mesh.material_ids[f]; // -1 = kein Material zugewiesen
|
||||
|
||||
auto& section = sectionsByMaterial[materialId];
|
||||
if (materialId >= 0) section.materialName = tinyMaterials[materialId].name;
|
||||
|
||||
for (int v = 0; v < faceVertexCount; ++v) {
|
||||
tinyobj::index_t idx = shape.mesh.indices[indexOffset + v];
|
||||
|
||||
section.vertices.insert(section.vertices.end(), {
|
||||
attrib.vertices[3*idx.vertex_index + 0],
|
||||
attrib.vertices[3*idx.vertex_index + 1],
|
||||
attrib.vertices[3*idx.vertex_index + 2]
|
||||
});
|
||||
if (idx.normal_index >= 0) {
|
||||
section.normals.insert(section.normals.end(), {
|
||||
attrib.normals[3*idx.normal_index + 0],
|
||||
attrib.normals[3*idx.normal_index + 1],
|
||||
attrib.normals[3*idx.normal_index + 2]
|
||||
});
|
||||
}
|
||||
if (idx.texcoord_index >= 0) {
|
||||
section.textureCoords.insert(section.textureCoords.end(), {
|
||||
attrib.texcoords[2*idx.texcoord_index + 0],
|
||||
attrib.texcoords[2*idx.texcoord_index + 1]
|
||||
});
|
||||
}
|
||||
section.indices.push_back(static_cast<int>(section.indices.size()));
|
||||
}
|
||||
indexOffset += faceVertexCount;
|
||||
}
|
||||
|
||||
for (auto& [materialId, section] : sectionsByMaterial) {
|
||||
object.subModels.push_back(std::move(section));
|
||||
}
|
||||
bundle.model.objects.push_back(std::move(object));
|
||||
}
|
||||
|
||||
return bundle;
|
||||
}
|
||||
24
engine/src/loader/models/ModelImporter.h
Normal file
24
engine/src/loader/models/ModelImporter.h
Normal file
@ -0,0 +1,24 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#ifndef COLORRACE_MODELIMPORTER_H
|
||||
#define COLORRACE_MODELIMPORTER_H
|
||||
#include "loader/assets/RawAssetData.h"
|
||||
|
||||
|
||||
namespace engine {
|
||||
struct RawModelBundle {
|
||||
RawModelData model;
|
||||
std::vector<RawMaterialData> materials;
|
||||
std::vector<RawTextureData> textures;
|
||||
};
|
||||
|
||||
class ModelImporter {
|
||||
public:
|
||||
static RawModelBundle importFromFile(const std::string& objPath);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif //COLORRACE_MODELIMPORTER_H
|
||||
148
engine/src/loader/models/ModelUploader.cpp
Normal file
148
engine/src/loader/models/ModelUploader.cpp
Normal file
@ -0,0 +1,148 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#include "ModelUploader.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "Material.h"
|
||||
#include "loader/textures/TextureUploader.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
std::vector<engine::LoadedAsset> engine::ModelUploader::upload(const engine::RawModelBundle &bundle, engine::ModelSplitPolicy policy) {
|
||||
std::vector<engine::LoadedAsset> results;
|
||||
|
||||
auto uploadedTextures = uploadTextures(bundle.textures, results);
|
||||
auto materials = buildMaterials(bundle.materials, uploadedTextures);
|
||||
|
||||
switch (policy) {
|
||||
case ModelSplitPolicy::ByObject:
|
||||
for (const auto& object : bundle.model.objects) {
|
||||
auto model = std::make_shared<Model>(uploadSections(object.subModels, materials));
|
||||
results.push_back(LoadedModel{bundle.model.name + "." + object.name, model});
|
||||
}
|
||||
break;
|
||||
case ModelSplitPolicy::Combined:
|
||||
results.push_back(LoadedModel{
|
||||
bundle.model.name,
|
||||
std::make_shared<Model>(buildCombinedByObject(bundle.model.objects, materials))
|
||||
});
|
||||
break;
|
||||
case ModelSplitPolicy::ByMaterial:
|
||||
results.push_back(LoadedModel{
|
||||
bundle.model.name,
|
||||
std::make_shared<Model>(buildCombinedByMaterial(bundle.model.objects, materials))
|
||||
});
|
||||
break;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<engine::Texture2D>> engine::ModelUploader::uploadTextures(
|
||||
const std::vector<RawTextureData> &textures, std::vector<LoadedAsset> &results) {
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<engine::Texture2D>> uploadedTextures;
|
||||
for (const auto& rawTexture : textures) {
|
||||
auto texture = std::make_shared<engine::Texture2D>(TextureUploader::upload(rawTexture));
|
||||
uploadedTextures[rawTexture.name] = texture;
|
||||
results.emplace_back(LoadedTexture{rawTexture.name, texture});
|
||||
}
|
||||
return uploadedTextures;
|
||||
}
|
||||
|
||||
engine::ModelUploader::MaterialMap engine::ModelUploader::buildMaterials(const std::vector<RawMaterialData> &materials, const std::unordered_map<std::string, std::shared_ptr<Texture2D> > &textures) {
|
||||
MaterialMap result;
|
||||
|
||||
for (const auto& raw : materials) {
|
||||
std::array<std::shared_ptr<Texture2D>, static_cast<size_t>(TextureType::Count)> resolved;
|
||||
for (const auto& [type, texName] : raw.texturePaths) {
|
||||
if (auto it = textures.find(texName); it != textures.end()) {
|
||||
resolved[static_cast<size_t>(type)] = it->second;
|
||||
} else {
|
||||
spdlog::warn("Textur '{}' für Material '{}' nicht gefunden", texName, raw.name);
|
||||
}
|
||||
}
|
||||
result[raw.name] = std::make_shared<Material>(resolved, raw.shininess, raw.opacity);
|
||||
}
|
||||
|
||||
result[""] = std::make_shared<Material>(); // Fallback für Faces ohne usemtl
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
engine::Model engine::ModelUploader::uploadSections(const std::vector<RawMeshSection> §ions, const MaterialMap &materials) {
|
||||
std::vector<float> vertices, normals, texCoords;
|
||||
std::vector<unsigned int> indices;
|
||||
std::vector<MeshSection> meshSections;
|
||||
|
||||
unsigned int vertexOffset = 0;
|
||||
for (const auto& section : sections) {
|
||||
MeshSection meshSection;
|
||||
meshSection.name = section.materialName.empty() ? section.name : section.materialName;
|
||||
meshSection.indexOffset = static_cast<unsigned int>(indices.size());
|
||||
meshSection.indexCount = static_cast<unsigned int>(section.indices.size());
|
||||
|
||||
auto it = materials.find(section.materialName);
|
||||
meshSection.material = (it != materials.end()) ? it->second : materials.at("");
|
||||
|
||||
vertices.insert(vertices.end(), section.vertices.begin(), section.vertices.end());
|
||||
normals.insert(normals.end(), section.normals.begin(), section.normals.end());
|
||||
texCoords.insert(texCoords.end(), section.textureCoords.begin(), section.textureCoords.end());
|
||||
for (int localIndex : section.indices) indices.push_back(vertexOffset + static_cast<unsigned int>(localIndex));
|
||||
|
||||
vertexOffset += static_cast<unsigned int>(section.vertices.size() / 3);
|
||||
meshSections.push_back(std::move(meshSection));
|
||||
}
|
||||
|
||||
auto vao = Vao::create();
|
||||
vao->bind();
|
||||
|
||||
|
||||
vao->initDataFeed(vertices.data(), vertices.size() * sizeof(float), GL_STATIC_DRAW, /* Attribute für Location 0, 3 Komponenten */ makeAttributeList(std::make_unique<Vec3Attribute>(0)));
|
||||
vao->initDataFeed(normals.data(), normals.size() * sizeof(float), GL_STATIC_DRAW, /* Attribute für Location 1, 3 Komponenten */ makeAttributeList(std::make_unique<Vec3Attribute>(1)));
|
||||
vao->initDataFeed(texCoords.data(), texCoords.size() * sizeof(float), GL_STATIC_DRAW, /* Attribute für Location 2, 2 Komponenten */ makeAttributeList(std::make_unique<Vec2Attribute>(2)));
|
||||
vao->createIndexBuffer(indices.data(), indices.size());
|
||||
|
||||
vao->unbind();
|
||||
|
||||
return Model(std::move(vao), static_cast<int>(indices.size()), std::move(meshSections));
|
||||
}
|
||||
|
||||
engine::Model engine::ModelUploader::buildCombinedByObject(const std::vector<RawObjectData> &objects, const MaterialMap &materials) {
|
||||
std::vector<RawMeshSection> all;
|
||||
for (const auto& object : objects) {
|
||||
for (auto section : object.subModels) {
|
||||
section.materialName = object.name;
|
||||
all.push_back(std::move(section));
|
||||
}
|
||||
}
|
||||
return uploadSections(all, materials);
|
||||
}
|
||||
|
||||
engine::Model engine::ModelUploader::buildCombinedByMaterial(const std::vector<RawObjectData> &objects, const MaterialMap &materials) {
|
||||
std::unordered_map<std::string, RawMeshSection> merged;
|
||||
|
||||
for (const auto& object : objects) {
|
||||
for (auto section : object.subModels) {
|
||||
auto& target = merged[section.materialName];
|
||||
|
||||
target.materialName = section.materialName;
|
||||
unsigned int offset = static_cast<unsigned int>(target.vertices.size() / 3);
|
||||
target.vertices.insert(target.vertices.end(), section.vertices.begin(), section.vertices.end());
|
||||
target.normals.insert(target.normals.end(), section.normals.begin(), section.normals.end());
|
||||
target.textureCoords.insert(target.textureCoords.end(), section.textureCoords.begin(), section.textureCoords.end());
|
||||
for (int idx : section.indices) target.indices.push_back(static_cast<int>(offset) + idx);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<RawMeshSection> all;
|
||||
for (auto& [name, section] : merged) all.push_back(std::move(section));
|
||||
return uploadSections(all, materials);
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<engine::Attribute>> engine::ModelUploader::makeAttributeList(std::unique_ptr<Attribute> attr) {
|
||||
std::vector<std::unique_ptr<engine::Attribute>> result;
|
||||
result.push_back(std::move(attr));
|
||||
return result;
|
||||
};
|
||||
33
engine/src/loader/models/ModelUploader.h
Normal file
33
engine/src/loader/models/ModelUploader.h
Normal file
@ -0,0 +1,33 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#ifndef COLORRACE_MODELUPLOADER_H
|
||||
#define COLORRACE_MODELUPLOADER_H
|
||||
#include <vector>
|
||||
|
||||
#include "ModelImporter.h"
|
||||
#include "loader/assets/LoadedAssets.h"
|
||||
|
||||
|
||||
namespace engine {
|
||||
class Material;
|
||||
|
||||
class ModelUploader {
|
||||
public:
|
||||
static std::vector<engine::LoadedAsset> upload(const engine::RawModelBundle& bundle, engine::ModelSplitPolicy policy);
|
||||
private:
|
||||
using MaterialMap = std::unordered_map<std::string, std::shared_ptr<Material>>;
|
||||
|
||||
static std::unordered_map<std::string, std::shared_ptr<Texture2D>> uploadTextures(const std::vector<RawTextureData>& textures, std::vector<LoadedAsset>& results);
|
||||
static MaterialMap buildMaterials(const std::vector<RawMaterialData>& materials, const std::unordered_map<std::string, std::shared_ptr<Texture2D>>& textures);
|
||||
static Model uploadSections(const std::vector<RawMeshSection>& sections, const MaterialMap& materials);
|
||||
static Model buildCombinedByObject(const std::vector<RawObjectData> &objects, const MaterialMap& materials);
|
||||
static Model buildCombinedByMaterial(const std::vector<RawObjectData> &objects, const MaterialMap& materials);
|
||||
|
||||
static std::vector<std::unique_ptr<Attribute>> makeAttributeList(std::unique_ptr<Attribute> attr);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif //COLORRACE_MODELUPLOADER_H
|
||||
46
engine/src/loader/textures/Texture2D.cpp
Normal file
46
engine/src/loader/textures/Texture2D.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#include "Texture2D.h"
|
||||
|
||||
#include "glad/glad.h"
|
||||
|
||||
namespace engine {
|
||||
Texture2D::~Texture2D() {release();}
|
||||
|
||||
Texture2D::Texture2D(Texture2D&& other) noexcept
|
||||
: m_id(other.m_id), m_width(other.m_width), m_height(other.m_height), m_channels(other.m_channels) {
|
||||
other.m_id = 0; // Ownership übertragen: das Original darf beim Zerstören nichts mehr löschen
|
||||
}
|
||||
|
||||
|
||||
Texture2D& Texture2D::operator=(Texture2D&& other) noexcept {
|
||||
if (this != &other) {
|
||||
release();
|
||||
m_id = other.m_id;
|
||||
m_width = other.m_width;
|
||||
m_height = other.m_height;
|
||||
m_channels = other.m_channels;
|
||||
other.m_id = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Texture2D::release() {
|
||||
if (m_id != 0) {
|
||||
glDeleteTextures(1, &m_id);
|
||||
m_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Texture2D::bind(unsigned int unit) const {
|
||||
glActiveTexture(GL_TEXTURE0 + unit);
|
||||
glBindTexture(GL_TEXTURE_2D, m_id);
|
||||
}
|
||||
|
||||
void Texture2D::unbind(unsigned int unit) const {
|
||||
glActiveTexture(GL_TEXTURE0 + unit);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
38
engine/src/loader/textures/Texture2D.h
Normal file
38
engine/src/loader/textures/Texture2D.h
Normal file
@ -0,0 +1,38 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#ifndef COLORRACE_TEXTURE2D_H
|
||||
#define COLORRACE_TEXTURE2D_H
|
||||
#include "../../../../cmake-build-debug/_deps/glfw-src/src/internal.h"
|
||||
|
||||
|
||||
namespace engine {
|
||||
class Texture2D {
|
||||
public:
|
||||
Texture2D() = default;
|
||||
~Texture2D();
|
||||
|
||||
Texture2D(const Texture2D&) = delete;
|
||||
Texture2D& operator=(const Texture2D&) = delete;
|
||||
|
||||
Texture2D(Texture2D&& other) noexcept;
|
||||
Texture2D& operator=(Texture2D&& other) noexcept;
|
||||
|
||||
void bind(unsigned int unit = 0) const;
|
||||
void unbind(unsigned int unit = 0) const;
|
||||
|
||||
private:
|
||||
GLuint m_id = 0;
|
||||
int m_width = 0;
|
||||
int m_height = 0;
|
||||
int m_channels = 0;
|
||||
|
||||
void release();
|
||||
|
||||
friend class TextureUploader;
|
||||
Texture2D(GLuint id, int width, int height, int channels) : m_id(id), m_width(width), m_height(height), m_channels(channels) {};
|
||||
};
|
||||
}
|
||||
|
||||
#endif //COLORRACE_TEXTURE2D_H
|
||||
45
engine/src/loader/textures/TextureImporter.cpp
Normal file
45
engine/src/loader/textures/TextureImporter.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#include "TextureImporter.h"
|
||||
|
||||
#include <stb_image.h>
|
||||
|
||||
engine::RawTextureData engine::TextureImporter::import(const std::filesystem::path &path, bool flipY) {
|
||||
stbi_set_flip_vertically_on_load(flipY);
|
||||
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int channels = 0;
|
||||
|
||||
stbi_uc* pixels = stbi_load(path.string().c_str(), &width, &height, &channels, 0);
|
||||
|
||||
if (!pixels) {
|
||||
throw std::runtime_error(
|
||||
std::string("Failed to load texture: ")
|
||||
+ path.string()
|
||||
+ " ("
|
||||
+ stbi_failure_reason()
|
||||
+ ")");
|
||||
}
|
||||
|
||||
RawTextureData data;
|
||||
data.name = path.stem().string();
|
||||
data.width = width;
|
||||
data.height = height;
|
||||
data.channels = channels;
|
||||
|
||||
const auto size =
|
||||
static_cast<size_t>(width)
|
||||
* height
|
||||
* channels;
|
||||
|
||||
data.pixels.assign(
|
||||
pixels,
|
||||
pixels + size);
|
||||
|
||||
stbi_image_free(pixels);
|
||||
|
||||
return data;
|
||||
}
|
||||
20
engine/src/loader/textures/TextureImporter.h
Normal file
20
engine/src/loader/textures/TextureImporter.h
Normal file
@ -0,0 +1,20 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#ifndef COLORRACE_TEXTUREIMPORTER_H
|
||||
#define COLORRACE_TEXTUREIMPORTER_H
|
||||
#include <filesystem>
|
||||
|
||||
#include "loader/assets/RawAssetData.h"
|
||||
|
||||
|
||||
namespace engine {
|
||||
class TextureImporter {
|
||||
public:
|
||||
static RawTextureData import(const std::filesystem::path& path, bool flipY = true);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //COLORRACE_TEXTUREIMPORTER_H
|
||||
23
engine/src/loader/textures/TextureUploader.cpp
Normal file
23
engine/src/loader/textures/TextureUploader.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#include "TextureUploader.h"
|
||||
|
||||
#include "glad/glad.h"
|
||||
|
||||
engine::Texture2D engine::TextureUploader::upload(const RawTextureData &data) {
|
||||
GLuint id;
|
||||
glGenTextures(1, &id);
|
||||
glBindTexture(GL_TEXTURE_2D, id);
|
||||
|
||||
GLenum format = data.channels == 4 ? GL_RGBA : data.channels == 3 ? GL_RGB : GL_RED;
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, format, data.width, data.height, 0, format, GL_UNSIGNED_BYTE, data.pixels.data());
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
return {id, data.width, data.height, data.channels}; // privater Ctor, TextureUploader ist friend
|
||||
}
|
||||
19
engine/src/loader/textures/TextureUploader.h
Normal file
19
engine/src/loader/textures/TextureUploader.h
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// Created by sebastian on 30.07.26.
|
||||
//
|
||||
|
||||
#ifndef COLORRACE_TEXTUREUPLOADER_H
|
||||
#define COLORRACE_TEXTUREUPLOADER_H
|
||||
#include "Texture2D.h"
|
||||
#include "loader/assets/RawAssetData.h"
|
||||
|
||||
|
||||
namespace engine {
|
||||
class TextureUploader {
|
||||
public:
|
||||
static Texture2D upload(const RawTextureData& data);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif //COLORRACE_TEXTUREUPLOADER_H
|
||||
Loading…
Reference in New Issue
Block a user