276 lines
10 KiB
C++
276 lines
10 KiB
C++
#include <iostream>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <tuple>
|
|
#include <memory>
|
|
#include <stdexcept>
|
|
#include <variant>
|
|
|
|
#include "scan.hpp"
|
|
#include "parse.hpp"
|
|
#include "compile.hpp"
|
|
#include "settings.hpp"
|
|
#include "output.hpp"
|
|
|
|
static void usage(const char* prog) {
|
|
std::cerr << "Usage: " << prog
|
|
<< " [options] <input_file|- for stdin> <output_file|- for stdout>\n"
|
|
"Options:\n"
|
|
" --float64 output double instead of float\n"
|
|
" --object <name> select an object (can be given multiple times)\n"
|
|
" --group <name> select a group (can be given multiple times)\n"
|
|
" --with-normals extract normal vectors\n"
|
|
" --with-texcoords extract texture coordinates\n"
|
|
" -- end of options\n";
|
|
}
|
|
|
|
#define CATCH_AND_RETURN(variable, exception, return_value, initializer) \
|
|
decltype(initializer) variable; \
|
|
try { \
|
|
variable = initializer; \
|
|
} catch (exception __ex__) { \
|
|
std::cerr << __ex__.what() << std::endl; \
|
|
return return_value; \
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
using namespace wavefront;
|
|
|
|
if (argc < 3) {
|
|
usage(argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
SettingsBuilder settings_builder;
|
|
int arg_index = 1;
|
|
bool end_of_options = false;
|
|
std::vector<std::string> positional;
|
|
|
|
while (arg_index < argc) {
|
|
std::string arg = argv[arg_index];
|
|
|
|
if (!end_of_options && arg == "--") {
|
|
end_of_options = true;
|
|
++arg_index;
|
|
continue;
|
|
}
|
|
|
|
if (!end_of_options && arg.size() > 0 && arg[0] == '-' && arg != "-") {
|
|
if (arg == "--float64") {
|
|
settings_builder.use_float64(true);
|
|
++arg_index;
|
|
continue;
|
|
} else if (arg == "--with-normals") {
|
|
settings_builder.with_normals(true);
|
|
++arg_index;
|
|
continue;
|
|
} else if (arg == "--with-texcoords") {
|
|
settings_builder.with_texcoords(true);
|
|
++arg_index;
|
|
continue;
|
|
} else if (arg == "--object") {
|
|
if (arg_index + 1 >= argc) {
|
|
std::cerr << "--object requires an argument\n";
|
|
return 2;
|
|
}
|
|
settings_builder.selected_objects().emplace_back(argv[arg_index + 1]);
|
|
arg_index += 2;
|
|
continue;
|
|
} else if (arg == "--group") {
|
|
if (arg_index + 1 >= argc) {
|
|
std::cerr << "--group requires an argument\n";
|
|
return 2;
|
|
}
|
|
settings_builder.selected_groups().emplace_back(argv[arg_index + 1]);
|
|
arg_index += 2;
|
|
continue;
|
|
} else {
|
|
std::cerr << "Unknown option: " << arg << "\n";
|
|
usage(argv[0]);
|
|
return 2;
|
|
}
|
|
} else {
|
|
positional.emplace_back(arg);
|
|
++arg_index;
|
|
}
|
|
}
|
|
|
|
if (positional.size() != 2) {
|
|
std::cerr << "Expected exactly two positional arguments: input_file output_file\n";
|
|
usage(argv[0]);
|
|
return 3;
|
|
}
|
|
|
|
const std::string& input_file = positional[0];
|
|
if (input_file != "-") {
|
|
settings_builder.input(input_file);
|
|
}
|
|
|
|
// Open output
|
|
const std::string& output_file = positional[1];
|
|
if (output_file != "-") {
|
|
settings_builder.output(output_file);
|
|
}
|
|
|
|
Settings settings = settings_builder.build();
|
|
|
|
using FloatType = float;
|
|
using IndexType = std::uint32_t;
|
|
|
|
auto scan_data = wavefront::scan(
|
|
settings.input(),
|
|
settings.selected_objects(),
|
|
settings.selected_groups()
|
|
);
|
|
|
|
std::cerr << "Scanned " << scan_data.total_lines << " lines" << std::endl;
|
|
std::cerr << "Found " << scan_data.category_map["v"].size() << " vertices" << std::endl;
|
|
std::cerr << "Found " << scan_data.category_map["vn"].size() << " normals" << std::endl;
|
|
std::cerr << "Found " << scan_data.category_map["vt"].size() << " texture coordinates" << std::endl;
|
|
std::cerr << "Found " << scan_data.category_map["f"].size() << " faces" << std::endl;
|
|
|
|
CATCH_AND_RETURN(face_data, wavefront::parse_error, 1, wavefront::parse_face_data(scan_data));
|
|
|
|
std::cerr << "Selected " << face_data.triangle_list.size() << " triangles" << std::endl;
|
|
std::cerr << "Selected " << face_data.index_position_set.size() << " vertex positions" << std::endl;
|
|
std::cerr << "Selected " << face_data.index_normal_set.size() << " vertex normals" << std::endl;
|
|
std::cerr << "Selected " << face_data.index_texcoord_set.size() << " vertex texture coordinates" << std::endl;
|
|
|
|
CATCH_AND_RETURN(coordinate_data, wavefront::parse_error, 1, wavefront::parse_coordinate_data<FloatType>(scan_data, face_data));
|
|
|
|
auto number_list = create_number_list<FloatType>(coordinate_data);
|
|
|
|
std::cerr << "Generated number list with " << number_list.size() << " values" << std::endl;
|
|
|
|
auto coordinate_index_data = create_coordinate_index<FloatType, IndexType>(coordinate_data, number_list);
|
|
|
|
auto coordinate_storage = compile_coordinate_storage(coordinate_index_data);
|
|
|
|
if (std::get<1>(coordinate_storage).size() > 0) {
|
|
std::cerr << "Indexed " << 1 << "D coordinates: " << std::get<1>(coordinate_storage).size() << std::endl;
|
|
}
|
|
|
|
if (std::get<2>(coordinate_storage).size() > 0) {
|
|
std::cerr << "Indexed " << 2 << "D coordinates: " << std::get<2>(coordinate_storage).size() << std::endl;
|
|
}
|
|
|
|
if (std::get<3>(coordinate_storage).size() > 0) {
|
|
std::cerr << "Indexed " << 3 << "D coordinates: " << std::get<3>(coordinate_storage).size() << std::endl;
|
|
}
|
|
|
|
auto coordinate_storage_index = compile_coordinate_storage_line_mapping<IndexType>(coordinate_index_data, coordinate_storage);
|
|
|
|
auto index_storage = compile_vertex_storage<IndexType>(scan_data, face_data, coordinate_storage_index);
|
|
|
|
std::visit([&](const auto &vertex_storage) {
|
|
std::cerr << "Compiled " << vertex_storage.size() << " vertices" << std::endl;
|
|
}, index_storage.vertices);
|
|
|
|
std::cerr << "Compiled " << index_storage.triangles.size() << " triangles" << std::endl;
|
|
|
|
auto &output = settings.output();
|
|
|
|
// 0:4: Signature
|
|
settings.output().write("\x7F" "3DG", 4);
|
|
// 4:8: Version
|
|
wavefront::write_number<std::uint32_t>(output, 1);
|
|
|
|
std::uint32_t flags = 0;
|
|
|
|
if (coordinate_storage_index.normal_coordinate_index.size() > 0) {
|
|
flags |= 1 << 1;
|
|
}
|
|
|
|
std::visit([&](const auto &texture_coordinates) {
|
|
if constexpr (std::ranges::range<std::decay_t<decltype(texture_coordinates)>>) {
|
|
using mapped_type = typename std::decay_t<decltype(texture_coordinates)>::mapped_type;
|
|
if constexpr (std::ranges::range<mapped_type>) {
|
|
flags |= std::tuple_size_v<mapped_type> << 2;
|
|
} else {
|
|
flags |= 1 << 2;
|
|
}
|
|
}
|
|
}, coordinate_data.texture_coordinates);
|
|
|
|
// 8:12: flags<0: float_size, 1: has_normals, 2&3: texcoord_ndims (0 = no),
|
|
wavefront::write_number<std::uint32_t>(output, flags);
|
|
|
|
// 12:16: number_list item count
|
|
wavefront::write_number<std::uint32_t>(output, number_list.size());
|
|
|
|
// 16:20: 2D storage item count
|
|
wavefront::write_number<std::uint32_t>(output, std::get<2>(coordinate_storage).size());
|
|
// 20:24: 3D storage item count
|
|
wavefront::write_number<std::uint32_t>(output, std::get<3>(coordinate_storage).size());
|
|
|
|
// 24:28: Vertex storage item count
|
|
std::visit([&](const auto &vertex_data) {
|
|
using value_t = std::decay_t<decltype(vertex_data)>::value_type;
|
|
if constexpr (std::tuple_size_v<value_t> > 1) {
|
|
wavefront::write_number<std::uint32_t>(output, vertex_data.size());
|
|
} else {
|
|
wavefront::write_number<std::uint32_t>(output, 0);
|
|
}
|
|
}, index_storage.vertices);
|
|
wavefront::write_number<std::uint32_t>(output, index_storage.triangles.size());
|
|
|
|
output.write(reinterpret_cast<const char *>(number_list.data()), number_list.size() * sizeof(decltype(number_list)::value_type));
|
|
|
|
std::array<char, 16> padding;
|
|
padding.fill(0);
|
|
std::size_t required_padding;
|
|
|
|
required_padding = (16 - (number_list.size() * sizeof(decltype(number_list)::value_type) % 16)) % 16;
|
|
output.write(padding.data(), required_padding);
|
|
|
|
if (std::get<2>(coordinate_storage).size() > 0) {
|
|
const auto &value_list = std::get<2>(coordinate_storage);
|
|
using value_t = std::decay_t<decltype(value_list)>::value_type;
|
|
output.write(reinterpret_cast<const char *>(value_list.data()), value_list.size() * sizeof(value_t));
|
|
required_padding = (16 - (value_list.size() * sizeof(value_t) % 16)) % 16;
|
|
if (required_padding > 0) {
|
|
output.write(padding.data(), required_padding);
|
|
}
|
|
}
|
|
|
|
if (std::get<3>(coordinate_storage).size() > 0) [[likely]] {
|
|
const auto &value_list = std::get<3>(coordinate_storage);
|
|
using value_t = std::decay_t<decltype(value_list)>::value_type;
|
|
output.write(reinterpret_cast<const char *>(value_list.data()), value_list.size() * sizeof(value_t));
|
|
required_padding = (16 - (value_list.size() * sizeof(value_t) % 16)) % 16;
|
|
if (required_padding > 0) {
|
|
output.write(padding.data(), required_padding);
|
|
}
|
|
}
|
|
|
|
std::visit([&](const auto &vertex_data) {
|
|
using value_t = std::decay_t<decltype(vertex_data)>::value_type;
|
|
if constexpr (std::tuple_size_v<value_t> > 1) {
|
|
output.write(reinterpret_cast<const char *>(vertex_data.data()), vertex_data.size() * sizeof(value_t));
|
|
required_padding = (16 - (vertex_data.size() * sizeof(value_t) % 16)) % 16;
|
|
if (required_padding > 0) {
|
|
output.write(padding.data(), required_padding);
|
|
}
|
|
}
|
|
}, index_storage.vertices);
|
|
|
|
{
|
|
const auto &value_list = index_storage.triangles;
|
|
using value_t = std::decay_t<decltype(value_list)>::value_type;
|
|
output.write(reinterpret_cast<const char *>(value_list.data()), value_list.size() * sizeof(value_t));
|
|
required_padding = (16 - (value_list.size() * sizeof(value_t) % 16)) % 16;
|
|
if (required_padding > 0) {
|
|
output.write(padding.data(), required_padding);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|