104 lines
3.7 KiB
C++
104 lines
3.7 KiB
C++
#include <basalt/export_macro.hpp>
|
|
#include <basalt/object_loader.hpp>
|
|
#include <tiny_obj_loader.h>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
|
|
namespace object_loader = ::basalt::shared::object_loader;
|
|
using object_loader::IObjectLoader;
|
|
using object_loader::Object;
|
|
using object_loader::ObjectLoaderConfig;
|
|
namespace math = ::basalt::shared::math;
|
|
using math::FloatPoint;
|
|
using math::Index;
|
|
using math::Triangle;
|
|
using math::Vector3;
|
|
|
|
class ObjObjectLoader : public IObjectLoader {
|
|
public:
|
|
ObjObjectLoader() {}
|
|
virtual ~ObjObjectLoader() {}
|
|
|
|
public:
|
|
virtual void load(ObjectLoaderConfig&& config) override {
|
|
IObjectLoader::load(std::move(config));
|
|
|
|
// Load the OBJ file using tinyobjloader
|
|
tinyobj::attrib_t attrib;
|
|
std::vector<tinyobj::shape_t> shapes;
|
|
std::vector<tinyobj::material_t> materials;
|
|
std::string err;
|
|
|
|
// Load the OBJ file
|
|
std::ifstream fs;
|
|
fs.open(std::filesystem::path(this->config.filename));
|
|
if (!fs.is_open()) {
|
|
throw std::runtime_error("fail to open obj file");
|
|
}
|
|
if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &err, &fs)) {
|
|
throw std::runtime_error("fail to load obj file");
|
|
}
|
|
fs.close();
|
|
|
|
// Create one object from all shapes in the OBJ file
|
|
std::vector<Vector3> vertices;
|
|
std::vector<Triangle> triangles;
|
|
|
|
// Extract all vertices from the OBJ file
|
|
// In tinyobjloader, vertices are stored in a flat array [x,y,z,x,y,z,...]
|
|
size_t num_vertices = attrib.vertices.size() / 3;
|
|
vertices.reserve(num_vertices);
|
|
|
|
for (size_t i = 0; i < attrib.vertices.size(); i += 3) {
|
|
Vector3 vertex;
|
|
vertex.x = attrib.vertices[i + 0]; // x coordinate
|
|
vertex.y = attrib.vertices[i + 1]; // y coordinate
|
|
vertex.z = attrib.vertices[i + 2]; // z coordinate
|
|
vertices.emplace_back(vertex);
|
|
}
|
|
|
|
// Process each shape and add its triangles
|
|
for (const auto& shape : shapes) {
|
|
// Add triangles from this shape to our collection
|
|
// Each face in the shape has been triangulated by tinyobjloader
|
|
for (size_t i = 0; i < shape.mesh.indices.size(); i += 3) {
|
|
if (i + 2 < shape.mesh.indices.size()) {
|
|
Triangle triangle;
|
|
|
|
// Get the three indices that form the triangle
|
|
// vertex_index is the index into the attrib.vertices array
|
|
int idx0 = shape.mesh.indices[i + 0].vertex_index;
|
|
int idx1 = shape.mesh.indices[i + 1].vertex_index;
|
|
int idx2 = shape.mesh.indices[i + 2].vertex_index;
|
|
|
|
// Make sure indices are valid
|
|
if (idx0 >= 0 && idx1 >= 0 && idx2 >= 0) {
|
|
triangle.i = static_cast<Index>(idx0);
|
|
triangle.j = static_cast<Index>(idx1);
|
|
triangle.k = static_cast<Index>(idx2);
|
|
|
|
triangles.emplace_back(triangle);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create an Object and add it to our objects vector
|
|
if (vertices.empty() || triangles.empty()) {
|
|
throw std::runtime_error("empty scene is not allowed");
|
|
} else {
|
|
this->objects.emplace_back(std::move(vertices), std::move(triangles));
|
|
}
|
|
}
|
|
};
|
|
|
|
BS_EXPORT void* BSCreateInstance() {
|
|
return static_cast<IObjectLoader*>(new ObjObjectLoader());
|
|
}
|
|
|
|
BS_EXPORT void BSDestroyInstance(void* instance) {
|
|
delete dynamic_cast<ObjObjectLoader*>(static_cast<IObjectLoader*>(instance));
|
|
}
|