From 9147a85dc32f06be2a4cfe4e422fdbc52679adc5 Mon Sep 17 00:00:00 2001 From: Brett Date: Mon, 8 Jan 2024 22:08:48 -0500 Subject: [PATCH 1/5] string_view trim --- include/blt/std/string.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/include/blt/std/string.h b/include/blt/std/string.h index 4f3e44f..d4b82b2 100755 --- a/include/blt/std/string.h +++ b/include/blt/std/string.h @@ -292,6 +292,29 @@ namespace blt::string return s; } + static inline BLT_CPP20_CONSTEXPR std::string_view ltrim(std::string_view s) + { + size_t start_pos = 0; + for (auto c = s.begin(); c != s.end() && std::isblank(*c); ++c, start_pos++); + return s.substr(start_pos); + } + + static inline BLT_CPP20_CONSTEXPR std::string_view rtrim(std::string_view s) + { + size_t end_pos = 0; + for (auto c = s.rbegin(); c != s.rend() && std::isblank(*c); ++c, end_pos++); + return s.substr(0, s.size() - end_pos); + } + + static inline BLT_CPP20_CONSTEXPR std::string_view trim(std::string_view s) + { + size_t start_pos = 0; + for (auto c = s.begin(); c != s.end() && std::isblank(*c); ++c, start_pos++); + size_t end_pos = s.size(); + for (auto c = s.rbegin(); c != s.rend() && std::isblank(*c); ++c, end_pos--); + return s.substr(start_pos, end_pos - start_pos); + } + // trim from both ends (in place) static inline BLT_CPP20_CONSTEXPR std::string& trim(std::string& s) { From 60ec2051d8422716e1d6fb8354c129731106f7ad Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Thu, 11 Jan 2024 11:16:46 -0500 Subject: [PATCH 2/5] nyahh --- include/blt/parse/obj_loader.h | 146 ++++++++++++++++++++++ src/blt/parse/obj_loader.cpp | 219 +++++++++++++++++++++++++++++++++ 2 files changed, 365 insertions(+) create mode 100644 include/blt/parse/obj_loader.h create mode 100644 src/blt/parse/obj_loader.cpp diff --git a/include/blt/parse/obj_loader.h b/include/blt/parse/obj_loader.h new file mode 100644 index 0000000..ad75b50 --- /dev/null +++ b/include/blt/parse/obj_loader.h @@ -0,0 +1,146 @@ +/* + * + * Copyright (C) 2024 Brett Terpstra + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef BLT_WITH_GRAPHICS_OBJ_LOADER_H +#define BLT_WITH_GRAPHICS_OBJ_LOADER_H + +#include "blt/math/vectors.h" +#include "blt/std/hashmap.h" +#include +#include +#include + +namespace blt::gfx +{ + + typedef blt::vec3f vertex_t; + typedef blt::vec2f uv_t; + typedef blt::vec3f normal_t; + + class model_data + { + public: + private: + std::vector vertices; + std::vector uvs; + std::vector normals; + }; + + struct face_t + { + std::int32_t vertex, uv, normal; + }; + + static inline bool operator==(const face_t& f1, const face_t& f2) + { + return f1.vertex == f2.vertex && f1.uv == f2.uv && f1.normal == f2.normal; + } + + struct face_hash + { + size_t operator()(const face_t& face) const + { + std::hash hasher; + return hasher(face.vertex) ^ hasher(face.uv) ^ hasher(face.normal); + } + }; + + struct face_eq + { + bool operator()(const face_t& f1, const face_t& f2) const + { + return f1 == f2; + } + }; + + struct constructed_vertex_t + { + vertex_t vertex; + uv_t uv; + normal_t normal; + }; + + struct triangle_t + { + std::int32_t v[3]; + }; + + struct quad_t + { + std::int32_t v[4]; + }; + + struct object_data + { + std::string object_name; + std::vector indices; + }; + + class obj_objects_t + { + private: + std::vector vertex_data_; + std::vector objects_; + public: + obj_objects_t(std::vector&& vertex_data, std::vector&& objects): + vertex_data_(vertex_data), objects_(objects) + {} + + inline const std::vector& vertex_data() + { + return vertex_data_; + }; + + inline const std::vector& objects() + { + return objects_; + }; + }; + + class char_tokenizer; + + class obj_loader + { + private: + std::vector vertices; + std::vector uvs; + std::vector normals; + + // maps between face (constructed vertex) -> vertex indices + HASHMAP vertex_map; + std::vector vertex_data; + object_data current_object; + std::vector data; + private: + bool handle_vertex_and_normals(float x, float y, float z, char type); + + void parse_vertex_line(char_tokenizer& tokenizer); + + void parse_face(char_tokenizer& tokenizer); + + void handle_face_vertex(const std::vector& face_list, std::int32_t* arr); + + public: + obj_objects_t parseFile(std::string_view file); + }; + + obj_objects_t quick_load(std::string_view file); + +} + +#endif //BLT_WITH_GRAPHICS_OBJ_LOADER_H diff --git a/src/blt/parse/obj_loader.cpp b/src/blt/parse/obj_loader.cpp new file mode 100644 index 0000000..11636f9 --- /dev/null +++ b/src/blt/parse/obj_loader.cpp @@ -0,0 +1,219 @@ +/* + * + * Copyright (C) 2024 Brett Terpstra + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include +#include +#include "blt/std/assert.h" +#include "blt/std/utility.h" + +namespace blt::gfx +{ + class char_tokenizer + { + private: + std::string_view string; + std::size_t current_pos = 0; + public: + explicit char_tokenizer(std::string_view view): string(view) + {} + + inline char advance() + { + return string[current_pos++]; + } + + inline bool has_next(size_t offset = 0) + { + return current_pos + offset < string.size(); + } + + inline std::string_view read_fully() + { + return blt::string::trim(string.substr(current_pos)); + } + }; + + template + T get(std::string_view str) + { + T x; + const auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), x); + // probably not needed. + if (ec != std::errc()) + { +// int i; +// const auto [ptr2, ec2] = std::from_chars(str.data(), str.data() + str.size(), i); +// if (ec2 == std::errc()) +// { +// x = static_cast(i); +// } else +// { + BLT_WARN("Unable to parse string '%s' into number!", std::string(str).c_str()); + x = 0; +// } + } + return x; + } + + void obj_loader::parse_vertex_line(char_tokenizer& tokenizer) + { + char type = tokenizer.advance(); + + if (type == 'p') + { + BLT_WARN("Unexpected type '%c' (not supported)", type); + return; + } + + auto elements = blt::string::split(std::string(tokenizer.read_fully()), " "); + BLT_ASSERT(elements.size() >= 2 && "Current line doesn't have enough arguments to process!"); + float x = get(elements[0]), y = get(elements[1]); + if (elements.size() < 3) + { + if (type == 't') + uvs.push_back(uv_t{x, y}); + else + BLT_ERROR("Unable to parse line '%s' type '%c' not recognized for arg count", std::string(tokenizer.read_fully()).c_str(), type); + } else + { + float z = get(elements[2]); + if (!handle_vertex_and_normals(x, y, z, type)) + BLT_ERROR("Unable to parse line '%s' type '%c' not recognized", std::string(tokenizer.read_fully()).c_str(), type); + } + } + + bool obj_loader::handle_vertex_and_normals(float x, float y, float z, char type) + { + if (std::isspace(type)) + { + vertices.push_back(vertex_t{x, y, z}); + } else if (type == 'n') + { + normals.push_back(normal_t{x, y, z}); + } else + return false; + return true; + } + + obj_objects_t quick_load(std::string_view file) + { + return obj_loader().parseFile(file); + } + + obj_objects_t obj_loader::parseFile(std::string_view file) + { + auto lines = blt::fs::getLinesFromFile(std::string(file)); + for (const auto& line : lines) + { + char_tokenizer token(line); + if (!token.has_next() || token.read_fully().empty()) + continue; + switch (token.advance()) + { + case '#': + continue; + case 'f': + parse_face(token); + break; + case 'v': + parse_vertex_line(token); + break; + case 'o': + { + if (!current_object.indices.empty()) + data.push_back(current_object); + current_object = {}; + current_object.object_name = token.read_fully(); + break; + } + case 'm': + { + BLT_TRACE("Material '%s' needs to be loaded!", std::string(token.read_fully()).c_str()); + break; + } + case 'u': + { + BLT_TRACE("Using material '%s'", std::string(token.read_fully()).c_str()); + break; + } + case 's': + BLT_TRACE("Using shading: %s", std::string(token.read_fully()).c_str()); + break; + } + } + data.push_back(current_object); + return {std::move(vertex_data), std::move(data)}; + } + + void obj_loader::parse_face(char_tokenizer& tokenizer) + { + auto faces = blt::string::split(std::string(tokenizer.read_fully()), ' '); + if (faces.size() == 3) + { + triangle_t triangle{}; + handle_face_vertex(faces, triangle.v); + current_object.indices.push_back(triangle); + } else if (faces.size() == 4) + { + quad_t quad{}; + handle_face_vertex(faces, quad.v); + triangle_t t1{}; + triangle_t t2{}; + + for (int i = 0; i < 3; i++) + t1.v[i] = quad.v[i]; + t2.v[0] = quad.v[0]; + t2.v[1] = quad.v[2]; + t2.v[2] = quad.v[3]; + + current_object.indices.push_back(t1); + current_object.indices.push_back(t2); + } else + BLT_WARN("Unsupported vertex count! %d", faces.size()); + } + + void obj_loader::handle_face_vertex(const std::vector& face_list, int32_t* arr) + { + for (const auto& pair : blt::enumerate(face_list)) + { + auto indices = blt::string::split(pair.second, '/'); + BLT_ASSERT(indices.size() == 3 && "Must have vertex, uv, and normal indices!!"); + + auto vi = get(indices[0]); + auto ui = get(indices[1]); + auto ni = get(indices[2]); + + face_t face{vi, ui, ni}; + + auto loc = vertex_map.find(face); + if (loc == vertex_map.end()) + { + auto index = static_cast(vertex_data.size()); + vertex_data.push_back({vertices[vi], uvs[ui], normals[ni]}); + vertex_map.insert({face, index}); + arr[pair.first] = index; + } else + { + arr[pair.first] = loc->second; + } + } + } +} \ No newline at end of file From a4f0b1e3d43efba95aa0f8ec0cbce8041ec818fe Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Thu, 11 Jan 2024 15:51:00 -0500 Subject: [PATCH 3/5] obj laoder --- include/blt/parse/obj_loader.h | 44 +++++++++++++++++++++----- src/blt/parse/obj_loader.cpp | 56 ++++++++++++++++++++++++---------- 2 files changed, 76 insertions(+), 24 deletions(-) diff --git a/include/blt/parse/obj_loader.h b/include/blt/parse/obj_loader.h index ad75b50..5d2c72d 100644 --- a/include/blt/parse/obj_loader.h +++ b/include/blt/parse/obj_loader.h @@ -31,6 +31,7 @@ namespace blt::gfx typedef blt::vec3f vertex_t; typedef blt::vec2f uv_t; typedef blt::vec3f normal_t; + typedef blt::vec3f color_t; class model_data { @@ -85,31 +86,55 @@ namespace blt::gfx std::int32_t v[4]; }; + struct material_t + { + std::string material_name; + color_t ambient; + color_t diffuse; + color_t specular; + float specular_exponent = 0.0f; + float transparency = 1.0f; + color_t transmission_filter_color{1, 1, 1}; + std::string texture_ambient; + std::string texture_diffuse; + std::string map_spec_color; + std::string map_spec_highlight; + std::string map_bump; + std::string map_displacement; + }; + struct object_data { - std::string object_name; + std::vector object_names; + std::string material; std::vector indices; }; - class obj_objects_t + class obj_model_t { private: std::vector vertex_data_; std::vector objects_; + HASHMAP materials_; public: - obj_objects_t(std::vector&& vertex_data, std::vector&& objects): - vertex_data_(vertex_data), objects_(objects) + obj_model_t(std::vector&& vertex_data, std::vector&& objects, HASHMAP&& mats): + vertex_data_(vertex_data), objects_(objects), materials_(mats) {} - inline const std::vector& vertex_data() + inline const auto& vertex_data() { return vertex_data_; }; - inline const std::vector& objects() + inline const auto& objects() { return objects_; }; + + inline const auto& materials() + { + return materials_; + } }; class char_tokenizer; @@ -126,6 +151,9 @@ namespace blt::gfx std::vector vertex_data; object_data current_object; std::vector data; + HASHMAP materials; + + size_t current_line = 0; private: bool handle_vertex_and_normals(float x, float y, float z, char type); @@ -136,10 +164,10 @@ namespace blt::gfx void handle_face_vertex(const std::vector& face_list, std::int32_t* arr); public: - obj_objects_t parseFile(std::string_view file); + obj_model_t parseFile(std::string_view file); }; - obj_objects_t quick_load(std::string_view file); + obj_model_t quick_load(std::string_view file); } diff --git a/src/blt/parse/obj_loader.cpp b/src/blt/parse/obj_loader.cpp index 11636f9..215dde3 100644 --- a/src/blt/parse/obj_loader.cpp +++ b/src/blt/parse/obj_loader.cpp @@ -15,14 +15,18 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#define BLT_DISABLE_TRACE +#define BLT_DISABLE_DEBUG + #include #include #include -#include #include #include #include "blt/std/assert.h" #include "blt/std/utility.h" +#include + namespace blt::gfx { @@ -86,6 +90,7 @@ namespace blt::gfx auto elements = blt::string::split(std::string(tokenizer.read_fully()), " "); BLT_ASSERT(elements.size() >= 2 && "Current line doesn't have enough arguments to process!"); float x = get(elements[0]), y = get(elements[1]); + BLT_DEBUG_STREAM << "Loaded value of (" << x << ", " << y << ")"; if (elements.size() < 3) { if (type == 't') @@ -95,9 +100,11 @@ namespace blt::gfx } else { float z = get(elements[2]); + BLT_DEBUG_STREAM << " with z: " << z; if (!handle_vertex_and_normals(x, y, z, type)) BLT_ERROR("Unable to parse line '%s' type '%c' not recognized", std::string(tokenizer.read_fully()).c_str(), type); } + BLT_DEBUG_STREAM << "\n"; } bool obj_loader::handle_vertex_and_normals(float x, float y, float z, char type) @@ -113,16 +120,18 @@ namespace blt::gfx return true; } - obj_objects_t quick_load(std::string_view file) + obj_model_t quick_load(std::string_view file) { return obj_loader().parseFile(file); } - obj_objects_t obj_loader::parseFile(std::string_view file) + obj_model_t obj_loader::parseFile(std::string_view file) { auto lines = blt::fs::getLinesFromFile(std::string(file)); - for (const auto& line : lines) + for (auto line_e : blt::enumerate(lines)) { + auto& line = line_e.second; + current_line = line_e.first; char_tokenizer token(line); if (!token.has_next() || token.read_fully().empty()) continue; @@ -138,29 +147,35 @@ namespace blt::gfx break; case 'o': { - if (!current_object.indices.empty()) - data.push_back(current_object); - current_object = {}; - current_object.object_name = token.read_fully(); + current_object.object_names.emplace_back(token.read_fully()); + BLT_TRACE("Setting object '%s'", std::string(current_object.object_name).c_str()); break; } case 'm': { - BLT_TRACE("Material '%s' needs to be loaded!", std::string(token.read_fully()).c_str()); + while (token.has_next() && token.advance() != ' ') + {} + BLT_WARN("Material '%s' needs to be loaded!", std::string(token.read_fully()).c_str()); break; } case 'u': { - BLT_TRACE("Using material '%s'", std::string(token.read_fully()).c_str()); + if (!current_object.indices.empty()) + data.push_back(current_object); + current_object = {}; + while (token.has_next() && token.advance() != ' ') + {} + current_object.material = token.read_fully(); + //BLT_WARN("Using material '%s'", std::string(token.read_fully()).c_str()); break; } case 's': - BLT_TRACE("Using shading: %s", std::string(token.read_fully()).c_str()); + //BLT_WARN("Using shading: %s", std::string(token.read_fully()).c_str()); break; } } data.push_back(current_object); - return {std::move(vertex_data), std::move(data)}; + return {std::move(vertex_data), std::move(data), std::move(materials)}; } void obj_loader::parse_face(char_tokenizer& tokenizer) @@ -187,7 +202,7 @@ namespace blt::gfx current_object.indices.push_back(t1); current_object.indices.push_back(t2); } else - BLT_WARN("Unsupported vertex count! %d", faces.size()); + BLT_WARN("Unsupported face vertex count of %d on line %d!", faces.size(), current_line); } void obj_loader::handle_face_vertex(const std::vector& face_list, int32_t* arr) @@ -197,21 +212,30 @@ namespace blt::gfx auto indices = blt::string::split(pair.second, '/'); BLT_ASSERT(indices.size() == 3 && "Must have vertex, uv, and normal indices!!"); - auto vi = get(indices[0]); - auto ui = get(indices[1]); - auto ni = get(indices[2]); + auto vi = get(indices[0]) - 1; + auto ui = get(indices[1]) - 1; + auto ni = get(indices[2]) - 1; + + BLT_DEBUG("Found vertex: %d, UV: %d, and normal: %d", vi, ui, ni); face_t face{vi, ui, ni}; auto loc = vertex_map.find(face); if (loc == vertex_map.end()) { + BLT_DEBUG("DID NOT FIND FACE!"); auto index = static_cast(vertex_data.size()); vertex_data.push_back({vertices[vi], uvs[ui], normals[ni]}); + BLT_DEBUG("Vertex: (%f, %f, %f), UV: (%f, %f), Normal: (%f, %f, %f)", vertices[vi].x(), vertices[vi].y(), vertices[vi].z(), + uvs[ui].x(), uvs[ui].y(), normals[ni].x(), normals[ni].y(), normals[ni].z()); vertex_map.insert({face, index}); arr[pair.first] = index; } else { + BLT_TRACE("Using cached data; %d; map size: %d", loc->second, vertex_data.size()); + const auto& d = vertex_data[loc->second]; + BLT_TRACE("Vertex: (%f, %f, %f), UV: (%f, %f), Normal: (%f, %f, %f)", d.vertex.x(), d.vertex.y(), d.vertex.z(), + d.uv.x(), d.uv.y(), d.normal.x(), d.normal.y(), d.normal.z()); arr[pair.first] = loc->second; } } From a2e8cacaf2be6f77bf27fac1ae97926b751ac003 Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Tue, 16 Jan 2024 14:24:41 -0500 Subject: [PATCH 4/5] we love warnings --- include/blt/std/allocator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/blt/std/allocator.h b/include/blt/std/allocator.h index a60bfa8..3b5bfa5 100644 --- a/include/blt/std/allocator.h +++ b/include/blt/std/allocator.h @@ -165,7 +165,7 @@ namespace blt * * ALLOCATORS RETURN UNINIT STORAGE!! THIS HAS BEEN DISABLED. */ - inline void allocate_in_block(pointer begin, size_t n) + inline void allocate_in_block(pointer, size_t) { // if constexpr (std::is_default_constructible_v && !std::is_trivially_default_constructible_v) // { From 0fbe3bf2281aaa091173d7acd1cc017b3d60bf7b Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Tue, 16 Jan 2024 14:31:58 -0500 Subject: [PATCH 5/5] BLT rust like types because std::.... is too long --- include/blt/std/types.h | 43 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 include/blt/std/types.h diff --git a/include/blt/std/types.h b/include/blt/std/types.h new file mode 100644 index 0000000..34dfa99 --- /dev/null +++ b/include/blt/std/types.h @@ -0,0 +1,43 @@ +/* + * + * Copyright (C) 2024 Brett Terpstra + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef BLT_TYPES_H +#define BLT_TYPES_H + +#include + +#ifndef NO_BLT_NAMESPACE_ON_TYPES +namespace blt +{ +#endif + using i8 = std::int8_t; + using i16 = std::int16_t; + using i32 = std::int32_t; + using i64 = std::int64_t; + + using u8 = std::uint8_t; + using u16 = std::uint16_t; + using u32 = std::uint32_t; + using u64 = std::uint64_t; + + using size_t = std::size_t; +#ifndef NO_BLT_NAMESPACE_ON_TYPES +} +#endif + +#endif //BLT_TYPES_H