Merge remote-tracking branch 'github/main'
commit
e219444d76
|
@ -0,0 +1,174 @@
|
||||||
|
/*
|
||||||
|
* <Short Description>
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 <utility>
|
||||||
|
#include <vector>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
private:
|
||||||
|
std::vector<vertex_t> vertices;
|
||||||
|
std::vector<uv_t> uvs;
|
||||||
|
std::vector<normal_t> 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<std::int32_t> 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 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::vector<std::string> object_names;
|
||||||
|
std::string material;
|
||||||
|
std::vector<triangle_t> indices;
|
||||||
|
};
|
||||||
|
|
||||||
|
class obj_model_t
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::vector<constructed_vertex_t> vertex_data_;
|
||||||
|
std::vector<object_data> objects_;
|
||||||
|
HASHMAP<std::string, material_t> materials_;
|
||||||
|
public:
|
||||||
|
obj_model_t(std::vector<constructed_vertex_t>&& vertex_data, std::vector<object_data>&& objects, HASHMAP<std::string, material_t>&& mats):
|
||||||
|
vertex_data_(vertex_data), objects_(objects), materials_(mats)
|
||||||
|
{}
|
||||||
|
|
||||||
|
inline const auto& vertex_data()
|
||||||
|
{
|
||||||
|
return vertex_data_;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline const auto& objects()
|
||||||
|
{
|
||||||
|
return objects_;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline const auto& materials()
|
||||||
|
{
|
||||||
|
return materials_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class char_tokenizer;
|
||||||
|
|
||||||
|
class obj_loader
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::vector<vertex_t> vertices;
|
||||||
|
std::vector<uv_t> uvs;
|
||||||
|
std::vector<normal_t> normals;
|
||||||
|
|
||||||
|
// maps between face (constructed vertex) -> vertex indices
|
||||||
|
HASHMAP<face_t, std::int32_t, face_hash, face_eq> vertex_map;
|
||||||
|
std::vector<constructed_vertex_t> vertex_data;
|
||||||
|
object_data current_object;
|
||||||
|
std::vector<object_data> data;
|
||||||
|
HASHMAP<std::string, material_t> materials;
|
||||||
|
|
||||||
|
size_t current_line = 0;
|
||||||
|
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<std::string>& face_list, std::int32_t* arr);
|
||||||
|
|
||||||
|
public:
|
||||||
|
obj_model_t parseFile(std::string_view file);
|
||||||
|
};
|
||||||
|
|
||||||
|
obj_model_t quick_load(std::string_view file);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //BLT_WITH_GRAPHICS_OBJ_LOADER_H
|
|
@ -165,7 +165,7 @@ namespace blt
|
||||||
*
|
*
|
||||||
* ALLOCATORS RETURN UNINIT STORAGE!! THIS HAS BEEN DISABLED.
|
* 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<T> && !std::is_trivially_default_constructible_v<T>)
|
// if constexpr (std::is_default_constructible_v<T> && !std::is_trivially_default_constructible_v<T>)
|
||||||
// {
|
// {
|
||||||
|
|
|
@ -292,6 +292,29 @@ namespace blt::string
|
||||||
return s;
|
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)
|
// trim from both ends (in place)
|
||||||
static inline BLT_CPP20_CONSTEXPR std::string& trim(std::string& s)
|
static inline BLT_CPP20_CONSTEXPR std::string& trim(std::string& s)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* <Short Description>
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BLT_TYPES_H
|
||||||
|
#define BLT_TYPES_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#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
|
|
@ -0,0 +1,243 @@
|
||||||
|
/*
|
||||||
|
* <Short Description>
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#define BLT_DISABLE_TRACE
|
||||||
|
#define BLT_DISABLE_DEBUG
|
||||||
|
|
||||||
|
#include <blt/parse/obj_loader.h>
|
||||||
|
#include <blt/std/loader.h>
|
||||||
|
#include <blt/std/string.h>
|
||||||
|
#include <cctype>
|
||||||
|
#include <charconv>
|
||||||
|
#include "blt/std/assert.h"
|
||||||
|
#include "blt/std/utility.h"
|
||||||
|
#include <blt/std/logging.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<typename T = float>
|
||||||
|
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<float>(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]);
|
||||||
|
BLT_DEBUG_STREAM << "Loaded value of (" << x << ", " << y << ")";
|
||||||
|
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]);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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_model_t quick_load(std::string_view file)
|
||||||
|
{
|
||||||
|
return obj_loader().parseFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
obj_model_t obj_loader::parseFile(std::string_view file)
|
||||||
|
{
|
||||||
|
auto lines = blt::fs::getLinesFromFile(std::string(file));
|
||||||
|
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;
|
||||||
|
switch (token.advance())
|
||||||
|
{
|
||||||
|
case '#':
|
||||||
|
continue;
|
||||||
|
case 'f':
|
||||||
|
parse_face(token);
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
parse_vertex_line(token);
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
{
|
||||||
|
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':
|
||||||
|
{
|
||||||
|
while (token.has_next() && token.advance() != ' ')
|
||||||
|
{}
|
||||||
|
BLT_WARN("Material '%s' needs to be loaded!", std::string(token.read_fully()).c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'u':
|
||||||
|
{
|
||||||
|
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_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), std::move(materials)};
|
||||||
|
}
|
||||||
|
|
||||||
|
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 face vertex count of %d on line %d!", faces.size(), current_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
void obj_loader::handle_face_vertex(const std::vector<std::string>& 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<std::int32_t>(indices[0]) - 1;
|
||||||
|
auto ui = get<std::int32_t>(indices[1]) - 1;
|
||||||
|
auto ni = get<std::int32_t>(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<std::int32_t>(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue