diff --git a/apps/yimgproc.cpp b/apps/yimgproc.cpp index c48850a69..b49c2254a 100644 --- a/apps/yimgproc.cpp +++ b/apps/yimgproc.cpp @@ -100,7 +100,7 @@ image filter_bilateral( } // namespace yocto -int main(int argc, const char* argv[]) { +void run_app(int argc, const char* argv[]) { // command line parameters auto tonemap_on = false; auto tonemap_exposure = 0; @@ -142,32 +142,26 @@ int main(int argc, const char* argv[]) { add_cli_option(cli, "--diff-threshold,", diff_threshold, "diff threshold"); add_cli_option(cli, "--output,-o", output, "output image filename", true); add_cli_option(cli, "filename", filename, "input image filename", true); - if (!parse_cli(cli, argc, argv)) exit(1); + parse_cli(cli, argc, argv); + + // error string buffer + auto error = ""s; // load - auto img = image(); - if (!load_image(filename, img)) { - print_fatal("cannor load " + filename); - } + auto img = load_image(filename); // set alpha if (alpha_filename != "") { - auto alpha = image(); - if (!load_image(alpha_filename, alpha)) { - print_fatal("cannor load " + alpha_filename); - } - if (img.size() != alpha.size()) print_fatal("bad image size"); + auto alpha = load_image(alpha_filename); + if (img.size() != alpha.size()) throw std::runtime_error("bad image size"); for (auto j = 0; j < img.size().y; j++) for (auto i = 0; i < img.size().x; i++) img[{i, j}].w = alpha[{i, j}].w; } // set alpha if (coloralpha_filename != "") { - auto alpha = image(); - if (!load_image(coloralpha_filename, alpha)) { - print_fatal("cannor load " + coloralpha_filename); - } - if (img.size() != alpha.size()) print_fatal("bad image size"); + auto alpha = load_image(coloralpha_filename); + if (img.size() != alpha.size()) throw std::runtime_error("bad image size"); for (auto j = 0; j < img.size().y; j++) for (auto i = 0; i < img.size().x; i++) img[{i, j}].w = mean(xyz(alpha[{i, j}])); @@ -175,11 +169,9 @@ int main(int argc, const char* argv[]) { // diff if (diff_filename != "") { - auto diff = image(); - if (!load_image(diff_filename, diff)) { - print_fatal("cannor load " + diff_filename); - } - if (img.size() != diff.size()) print_fatal("image sizes are different"); + auto diff = load_image(diff_filename); + if (img.size() != diff.size()) + throw std::runtime_error("image sizes are different"); img = image_difference(img, diff, true); } @@ -199,17 +191,23 @@ int main(int argc, const char* argv[]) { } // save - if (!save_image(output, logo ? add_logo(img) : img)) { - print_fatal("cannor save " + output); - } + save_image(output, logo ? add_logo(img) : img); // check diff if (diff_filename != "" && diff_signal) { for (auto& c : img) { - if (max(xyz(c)) > diff_threshold) print_fatal("image content differs"); + if (max(xyz(c)) > diff_threshold) + throw std::runtime_error("image content differs"); } } +} - // done - return 0; +int main(int argc, const char* argv[]) { + try { + run_app(argc, argv); + return 0; + } catch (std::exception& e) { + print_fatal(e.what()); + return 1; + } } diff --git a/apps/yimgview.cpp b/apps/yimgview.cpp index e856d6ae4..683b2cfb6 100644 --- a/apps/yimgview.cpp +++ b/apps/yimgview.cpp @@ -71,13 +71,6 @@ struct app_state { string error = ""; }; -// load state -struct load_state { - string filename = ""; - shared_ptr app = {}; - imageio_status status = {}; -}; - // app states struct app_states { // data @@ -85,7 +78,7 @@ struct app_states { int selected = -1; // loading - deque> loaders = {}; + deque>> loaders = {}; // default options float exposure = 0; @@ -149,7 +142,7 @@ void update_display(shared_ptr app) { // add a new image void load_image_async(shared_ptr apps, const string& filename) { apps->loaders.push_back( - async(launch::async, [apps, filename]() -> load_state { + async(launch::async, [apps, filename]() -> shared_ptr { auto app = make_shared(); app->filename = filename; app->outname = replace_extension(filename, ".display.png"); @@ -158,9 +151,7 @@ void load_image_async(shared_ptr apps, const string& filename) { app->filmic = apps->filmic; app->params = apps->params; apps->selected = (int)apps->states.size() - 1; - if (auto ret = load_image(app->filename, app->source); !ret) { - return {filename, nullptr, ret}; - } + load_image(app->filename, app->source); compute_stats( app->source_stats, app->source, is_hdr_filename(app->filename)); if (app->colorgrade) { @@ -169,7 +160,7 @@ void load_image_async(shared_ptr apps, const string& filename) { app->display = tonemap_image(app->source, app->exposure, app->filmic); } compute_stats(app->display_stats, app->display, false); - return {filename, app, {}}; + return app; })); } @@ -306,27 +297,27 @@ void draw(const opengl_window& win, shared_ptr apps, } void update(const opengl_window& win, shared_ptr apps) { - auto is_ready = [](const future& result) -> bool { + auto is_ready = [](const future>& result) -> bool { return result.valid() && result.wait_for(chrono::microseconds(0)) == future_status::ready; }; while (!apps->loaders.empty() && is_ready(apps->loaders.front())) { - auto [filename, app, status] = apps->loaders.front().get(); - apps->loaders.pop_front(); - if (!status) { - push_glmessage(win, "cannot load image " + filename); - log_glinfo(win, "cannot load image " + filename); - log_glinfo(win, status.error); - } else { + try { + auto app = apps->loaders.front().get(); + apps->loaders.pop_front(); apps->states.push_back(app); update_display(app); if (apps->selected < 0) apps->selected = (int)apps->states.size() - 1; + } catch (std::exception& e) { + apps->loaders.pop_front(); + push_glmessage(win, e.what()); + log_glinfo(win, e.what()); } } } -int main(int argc, const char* argv[]) { +void run_app(int argc, const char* argv[]) { // prepare application auto apps = make_shared(); auto filenames = vector{}; @@ -334,7 +325,7 @@ int main(int argc, const char* argv[]) { // command line options auto cli = make_cli("yimgview", "view images"); add_cli_option(cli, "images", filenames, "image filenames", true); - if (!parse_cli(cli, argc, argv)) exit(1); + parse_cli(cli, argc, argv); // loading images for (auto filename : filenames) load_image_async(apps, filename); @@ -380,7 +371,14 @@ int main(int argc, const char* argv[]) { // cleanup clear_glwindow(win); +} - // done - return 0; +int main(int argc, const char* argv[]) { + try { + run_app(argc, argv); + return 0; + } catch (std::exception& e) { + print_fatal(e.what()); + return 1; + } } diff --git a/apps/yimgviews.cpp b/apps/yimgviews.cpp index ea51db860..c59b49016 100644 --- a/apps/yimgviews.cpp +++ b/apps/yimgviews.cpp @@ -84,7 +84,7 @@ void update_display(shared_ptr app) { }); } -int main(int argc, const char* argv[]) { +void run_app(int argc, const char* argv[]) { // prepare application auto app = make_shared(); auto filenames = vector{}; @@ -93,11 +93,10 @@ int main(int argc, const char* argv[]) { auto cli = make_cli("yimgview", "view images"); add_cli_option(cli, "--output,-o", app->outname, "image output"); add_cli_option(cli, "image", app->filename, "image filename", true); - if (!parse_cli(cli, argc, argv)) exit(1); + parse_cli(cli, argc, argv); // load image - if (!load_image(app->filename, app->source)) - print_fatal("cannot load " + app->filename); + load_image(app->filename, app->source); // update display update_display(app); @@ -136,7 +135,14 @@ int main(int argc, const char* argv[]) { // cleanup clear_glwindow(win); +} - // done - return 0; +int main(int argc, const char* argv[]) { + try { + run_app(argc, argv); + return 0; + } catch (std::exception& e) { + print_fatal(e.what()); + return 1; + } } diff --git a/apps/yimshproc.cpp b/apps/yimshproc.cpp index 24fab330c..5079b23db 100644 --- a/apps/yimshproc.cpp +++ b/apps/yimshproc.cpp @@ -130,13 +130,13 @@ void my_draw_glwidgets( } } -int main(int num_args, const char* args[]) { +void run_app(int argc, const char* argv[]) { string input_filename = "model.obj"; // Parse command line. auto cli = make_cli("yimshproc", "interactive viewer for mesh processing"); add_cli_option(cli, "model", input_filename, "model filenames", true); - if (!parse_cli(cli, num_args, args)) exit(1); + parse_cli(cli, argc, argv); auto data = my_data{}; @@ -161,3 +161,13 @@ int main(int num_args, const char* args[]) { yimshproc(input_filename, init, key_callback, click_callback, draw_glwidgets); } + +int main(int argc, const char* argv[]) { + try { + run_app(argc, argv); + return 0; + } catch (std::exception& e) { + print_fatal(e.what()); + return 1; + } +} diff --git a/apps/ymshproc.cpp b/apps/ymshproc.cpp index 4f3ea2b2c..beba064ab 100644 --- a/apps/ymshproc.cpp +++ b/apps/ymshproc.cpp @@ -85,7 +85,7 @@ int main(int argc, const char** argv) { add_cli_option(cli, "--info,-i", info, "print mesh info"); add_cli_option(cli, "--output,-o", output, "output mesh", true); add_cli_option(cli, "mesh", filename, "input mesh", true); - if (!parse_cli(cli, argc, argv)) exit(1); + parse_cli(cli, argc, argv); // mesh data auto positions = vector{}; @@ -104,15 +104,11 @@ int main(int argc, const char** argv) { // load mesh auto load_timer = print_timed("loading shape"); if (!facevarying) { - if (auto ret = load_shape(filename, points, lines, triangles, quads, - positions, normals, texcoords, colors, radius); - !ret) - print_fatal(ret.error); + load_shape(filename, points, lines, triangles, quads, positions, normals, + texcoords, colors, radius); } else { - if (auto ret = load_fvshape(filename, quadspos, quadsnorm, quadstexcoord, - positions, normals, texcoords); - !ret) - print_fatal(ret.error); + load_fvshape(filename, quadspos, quadsnorm, quadstexcoord, positions, + normals, texcoords); } print_elapsed(load_timer); @@ -266,15 +262,11 @@ int main(int argc, const char** argv) { // save mesh auto save_timer = print_timed("saving shape"); if (!quadspos.empty()) { - if (auto ret = save_fvshape(output, quadspos, quadsnorm, quadstexcoord, - positions, normals, texcoords); - !ret) - print_fatal(ret.error); + save_fvshape(output, quadspos, quadsnorm, quadstexcoord, positions, normals, + texcoords); } else { - if (auto ret = save_shape(output, points, lines, triangles, quads, - positions, normals, texcoords, colors, radius); - !ret) - print_fatal(ret.error); + save_shape(output, points, lines, triangles, quads, positions, normals, + texcoords, colors, radius); } print_elapsed(save_timer); diff --git a/apps/yscnitrace.cpp b/apps/yscnitrace.cpp index a2d0409cb..2d335a48d 100644 --- a/apps/yscnitrace.cpp +++ b/apps/yscnitrace.cpp @@ -83,13 +83,6 @@ struct app_state { } }; -// Load state -struct load_state { - string filename = ""; - shared_ptr app = nullptr; - sceneio_status status = {}; -}; - // Application state struct app_states { // data @@ -97,7 +90,7 @@ struct app_states { int selected = -1; // loading - deque> loaders = {}; + deque>> loaders = {}; // default options trace_params params = {}; @@ -234,7 +227,7 @@ void reset_display(shared_ptr app) { void load_scene_async(shared_ptr apps, const string& filename) { apps->loaders.push_back( - async(launch::async, [apps, filename]() -> load_state { + async(launch::async, [apps, filename]() -> shared_ptr { auto app = make_shared(); app->filename = filename; app->imagename = replace_extension(filename, ".png"); @@ -242,8 +235,7 @@ void load_scene_async(shared_ptr apps, const string& filename) { app->name = get_filename(app->filename); app->params = app->params; app->add_skyenv = app->add_skyenv; - if (auto ret = load_scene(app->filename, app->ioscene); !ret) - return {filename, nullptr, ret}; + load_scene(app->filename, app->ioscene); init_scene(app->scene, app->ioscene); init_bvh(app->scene, app->params); init_lights(app->scene); @@ -256,7 +248,7 @@ void load_scene_async(shared_ptr apps, const string& filename) { app->name = get_filename(app->filename) + " [" + to_string(app->render.size().x) + "x" + to_string(app->render.size().y) + " @ 0]"; - return {filename, app, {}}; + return app; })); } @@ -300,9 +292,11 @@ bool draw_glwidgets_texture( to_string(texture.ldr.size().x) + " x " + to_string(texture.ldr.size().y)); if (edited && old_filename != texture.filename) { - if (auto ret = load_texture(app->filename, texture); !ret) { - push_glmessage(win, ret.error); - log_glinfo(win, ret.error); + try { + load_texture(app->filename, texture); + } catch (std::exception& e) { + push_glmessage(win, e.what()); + log_glinfo(win, e.what()); } } return edited; @@ -366,9 +360,11 @@ bool draw_glwidgets_shape( draw_gllabel(win, "radius", to_string(shape.radius.size())); draw_gllabel(win, "tangsp", to_string(shape.tangents.size())); if (edited && old_filename != shape.filename) { - if (auto ret = load_shape(app->filename, shape); !ret) { - push_glmessage(win, "cannot load " + shape.filename); - log_glinfo(win, "cannot load " + shape.filename); + try { + load_shape(app->filename, shape); + } catch (std::exception& e) { + push_glmessage(win, e.what()); + log_glinfo(win, e.what()); } } return edited; @@ -395,9 +391,11 @@ bool draw_glwidgets_subdiv( draw_gllabel(win, "radius", to_string(subdiv.radius.size())); draw_gllabel(win, "tangsp", to_string(subdiv.tangents.size())); if (edited && old_filename != subdiv.filename) { - if (auto ret = load_subdiv(app->filename, subdiv); !ret) { - push_glmessage(win, "cannot load " + subdiv.filename); - log_glinfo(win, "cannot load " + subdiv.filename); + try { + load_subdiv(app->filename, subdiv); + } catch (std::exception& e) { + push_glmessage(win, e.what()); + log_glinfo(win, e.what()); } } return edited; @@ -449,10 +447,11 @@ void draw_glwidgets(const opengl_window& win, shared_ptr apps, "*.yaml;*.obj;*.pbrt")) { auto app = apps->states[apps->selected]; app->outname = save_path; - if (auto ret = save_scene(app->outname, app->ioscene); !ret) { - push_glmessage(win, "cannot save " + app->outname); - log_glinfo(win, "cannot save " + app->outname); - log_glinfo(win, ret.error); + try { + save_scene(app->outname, app->ioscene); + } catch (std::exception& e) { + push_glmessage(win, e.what()); + log_glinfo(win, e.what()); } save_path = ""; } @@ -462,9 +461,11 @@ void draw_glwidgets(const opengl_window& win, shared_ptr apps, "*.png;*.jpg;*.tga;*.bmp;*.hdr;*.exr")) { auto app = apps->states[apps->selected]; app->outname = save_path; - if (auto ret = save_image(app->imagename, app->display); !ret) { - push_glmessage(win, "cannot save " + app->outname); - log_glinfo(win, "cannot save " + app->outname); + try { + save_image(app->imagename, app->display); + } catch (std::exception& e) { + push_glmessage(win, e.what()); + log_glinfo(win, e.what()); } save_path = ""; } @@ -708,27 +709,27 @@ void draw(const opengl_window& win, shared_ptr apps, } void update(const opengl_window& win, shared_ptr apps) { - auto is_ready = [](const future& result) -> bool { + auto is_ready = [](const future>& result) -> bool { return result.valid() && result.wait_for(chrono::microseconds(0)) == future_status::ready; }; while (!apps->loaders.empty() && is_ready(apps->loaders.front())) { - auto [filename, app, status] = apps->loaders.front().get(); - apps->loaders.pop_front(); - if (!status) { - push_glmessage(win, "cannot load scene " + filename); - log_glinfo(win, "cannot load scene " + filename); - log_glinfo(win, status.error); - } else { + try { + auto app = apps->loaders.front().get(); + apps->loaders.pop_front(); apps->states.push_back(app); reset_display(app); if (apps->selected < 0) apps->selected = (int)apps->states.size() - 1; + } catch (std::exception& e) { + apps->loaders.pop_front(); + push_glmessage(win, e.what()); + log_glinfo(win, e.what()); } } } -int main(int argc, const char* argv[]) { +void run_app(int argc, const char* argv[]) { // application auto apps = make_shared(); auto filenames = vector{}; @@ -754,7 +755,7 @@ int main(int argc, const char* argv[]) { cli, "--bvh", (int&)apps->params.bvh, "Bvh type", trace_bvh_names); add_cli_option(cli, "--add-skyenv", apps->add_skyenv, "Add sky envmap"); add_cli_option(cli, "scenes", filenames, "Scene filenames", true); - if (!parse_cli(cli, argc, argv)) exit(1); + parse_cli(cli, argc, argv); // loading images for (auto filename : filenames) load_scene_async(apps, filename); @@ -832,7 +833,14 @@ int main(int argc, const char* argv[]) { // clear clear_glwindow(win); +} - // done - return 0; +int main(int argc, const char* argv[]) { + try { + run_app(argc, argv); + return 0; + } catch (std::exception& e) { + print_fatal(e.what()); + return 1; + } } diff --git a/apps/yscnitraces.cpp b/apps/yscnitraces.cpp index a8181c8d5..7717f74f5 100644 --- a/apps/yscnitraces.cpp +++ b/apps/yscnitraces.cpp @@ -209,7 +209,7 @@ void reset_display(shared_ptr app) { }); } -int main(int argc, const char* argv[]) { +void run_app(int argc, const char* argv[]) { // application auto app = make_shared(); @@ -235,14 +235,12 @@ int main(int argc, const char* argv[]) { add_cli_option(cli, "--add-skyenv", app->add_skyenv, "Add sky envmap"); add_cli_option(cli, "--output,-o", app->imagename, "Image output", false); add_cli_option(cli, "scene", app->filename, "Scene filename", true); - if (!parse_cli(cli, argc, argv)) exit(1); + parse_cli(cli, argc, argv); // scene loading auto ioscene = sceneio_model{}; auto load_timer = print_timed("loading scene"); - if (auto ret = load_scene(app->filename, ioscene); !ret) { - print_fatal(ret.error); - } + load_scene(app->filename, ioscene); print_elapsed(load_timer); // conversion @@ -314,7 +312,14 @@ int main(int argc, const char* argv[]) { // clear clear_glwindow(win); +} - // done - return 0; +int main(int argc, const char* argv[]) { + try { + run_app(argc, argv); + return 0; + } catch (std::exception& e) { + print_fatal(e.what()); + return 1; + } } diff --git a/apps/yscnproc.cpp b/apps/yscnproc.cpp index a930416d2..0dfb25070 100644 --- a/apps/yscnproc.cpp +++ b/apps/yscnproc.cpp @@ -47,7 +47,7 @@ bool mkdir(const string& dir) { #endif } -int main(int argc, const char** argv) { +void run_app(int argc, const char** argv) { // command line parameters auto mesh_filenames = false; auto shape_directory = "shapes/"s; @@ -75,14 +75,12 @@ int main(int argc, const char** argv) { add_cli_option(cli, "--validate", validate, "Validate scene"); add_cli_option(cli, "--output,-o", output, "output scene", true); add_cli_option(cli, "scene", filename, "input scene", true); - if (!parse_cli(cli, argc, argv)) exit(1); + parse_cli(cli, argc, argv); // load scene auto scene = sceneio_model{}; auto load_timer = print_timed("loading scene"); - if (auto ret = load_scene(filename, scene); !ret) { - print_fatal(ret.error); - } + load_scene(filename, scene); print_elapsed(load_timer); // validate scene @@ -147,11 +145,16 @@ int main(int argc, const char** argv) { // save scene auto save_timer = print_timed("saving scene"); - if (auto ret = save_scene(output, scene, obj_instances); !ret) { - print_fatal(ret.error); - } + save_scene(output, scene, obj_instances); print_elapsed(save_timer); +} - // done - return 0; +int main(int argc, const char* argv[]) { + try { + run_app(argc, argv); + return 0; + } catch (std::exception& e) { + print_fatal(e.what()); + return 1; + } } diff --git a/apps/yscntrace.cpp b/apps/yscntrace.cpp index a1c6f16ab..4e5875f19 100644 --- a/apps/yscntrace.cpp +++ b/apps/yscntrace.cpp @@ -111,7 +111,7 @@ void init_scene(trace_scene& scene, sceneio_model& ioscene) { ioscene = {}; } -int main(int argc, const char* argv[]) { +void run_app(int argc, const char* argv[]) { // options auto params = trace_params{}; auto batch = 16; @@ -144,14 +144,12 @@ int main(int argc, const char* argv[]) { add_cli_option(cli, "--output-image,-o", imfilename, "Image filename"); add_cli_option(cli, "--validate", validate, "Validate scene"); add_cli_option(cli, "scene", filename, "Scene filename", true); - if (!parse_cli(cli, argc, argv)) exit(1); + parse_cli(cli, argc, argv); // scene loading auto ioscene = sceneio_model{}; auto load_timer = print_timed("loading scene"); - if (auto ret = load_scene(filename, ioscene); !ret) { - print_fatal(ret.error); - } + load_scene(filename, ioscene); print_elapsed(load_timer); // add components @@ -200,16 +198,22 @@ int main(int argc, const char* argv[]) { if (save_batch) { auto outfilename = replace_extension(imfilename, "-s" + std::to_string(sample + nsamples) + get_extension(imfilename)); - if (auto ret = save_image(outfilename, render); !ret) - print_fatal(ret.error); + save_image(outfilename, render); } } // save image auto save_timer = print_timed("saving image"); - if (auto ret = save_image(imfilename, render); !ret) print_fatal(ret.error); + save_image(imfilename, render); print_elapsed(save_timer); +} - // done - return 0; +int main(int argc, const char* argv[]) { + try { + run_app(argc, argv); + return 0; + } catch (std::exception& e) { + print_fatal(e.what()); + return 1; + } } diff --git a/apps/yscnview.cpp b/apps/yscnview.cpp index 2e5d4d7bb..753d3de97 100644 --- a/apps/yscnview.cpp +++ b/apps/yscnview.cpp @@ -77,13 +77,6 @@ struct app_state { string error = ""; }; -// Load state -struct load_state { - string filename = ""; - shared_ptr app = nullptr; - sceneio_status status = {}; -}; - // Application state struct app_states { // data @@ -91,7 +84,7 @@ struct app_states { int selected = -1; // loading - deque> loaders = {}; + deque>> loaders = {}; // default options draw_glscene_params drawgl_prms = {}; @@ -113,18 +106,17 @@ vec2f compute_animation_range( void load_scene_async(shared_ptr apps, const string& filename) { apps->loaders.push_back( - async(launch::async, [apps, filename]() -> load_state { + async(launch::async, [apps, filename]() -> shared_ptr { auto app = make_shared(); app->filename = filename; app->imagename = replace_extension(filename, ".png"); app->outname = replace_extension(filename, ".edited.yaml"); app->name = get_filename(app->filename); app->drawgl_prms = apps->drawgl_prms; - if (auto ret = load_scene(app->filename, app->scene); !ret) - return {filename, nullptr, ret}; + load_scene(app->filename, app->scene); app->time_range = compute_animation_range(app->scene); app->time = app->time_range.x; - return {filename, app, {}}; + return app; })); } @@ -263,9 +255,11 @@ bool draw_glwidgets_texture( to_string(texture.ldr.size().x) + " x " + to_string(texture.ldr.size().y)); if (edited && old_filename != texture.filename) { - if (auto ret = load_texture(app->filename, texture); !ret) { - push_glmessage(win, ret.error); - log_glinfo(win, ret.error); + try { + load_texture(app->filename, texture); + } catch (std::exception& e) { + push_glmessage(win, e.what()); + log_glinfo(win, e.what()); } } return edited; @@ -329,9 +323,11 @@ bool draw_glwidgets_shape( draw_gllabel(win, "radius", to_string(shape.radius.size())); draw_gllabel(win, "tangsp", to_string(shape.tangents.size())); if (edited && old_filename != shape.filename) { - if (auto ret = load_shape(app->filename, shape); !ret) { - push_glmessage(win, ret.error); - log_glinfo(win, ret.error); + try { + load_shape(app->filename, shape); + } catch (std::exception& e) { + push_glmessage(win, e.what()); + log_glinfo(win, e.what()); } } return edited; @@ -365,9 +361,11 @@ bool draw_glwidgets_subdiv( app->scene.textures, true); edited += draw_glslider(win, "displacement", subdiv.displacement, 0, 1); if (edited && old_filename != subdiv.filename) { - if (auto ret = load_subdiv(app->filename, subdiv); !ret) { - push_glmessage(win, ret.error); - log_glinfo(win, ret.error); + try { + load_subdiv(app->filename, subdiv); + } catch (std::exception& e) { + push_glmessage(win, e.what()); + log_glinfo(win, e.what()); } } return edited; @@ -421,10 +419,11 @@ void draw_glwidgets(const opengl_window& win, shared_ptr apps, "*.yaml;*.obj;*.pbrt")) { auto app = apps->states[apps->selected]; app->outname = save_path; - if (auto ret = save_scene(app->outname, app->scene); !ret) { - push_glmessage(win, "cannot save " + app->outname); - log_glinfo(win, "cannot save " + app->outname); - log_glinfo(win, ret.error); + try { + save_scene(app->outname, app->scene); + } catch (std::exception& e) { + push_glmessage(win, e.what()); + log_glinfo(win, e.what()); } save_path = ""; } @@ -603,29 +602,28 @@ void draw(const opengl_window& win, shared_ptr apps, // update void update(const opengl_window& win, shared_ptr apps) { - auto is_ready = [](const future& result) -> bool { + auto is_ready = [](const future>& result) -> bool { return result.valid() && result.wait_for(chrono::microseconds(0)) == future_status::ready; }; while (!apps->loaders.empty() && is_ready(apps->loaders.front())) { - auto [filename, app, status] = apps->loaders.front().get(); - apps->loaders.pop_front(); - if (!status) { - push_glmessage(win, "cannot load scene " + filename); - log_glinfo(win, "cannot load scene " + filename); - log_glinfo(win, status.error); - break; - } else { + try { + auto app = apps->loaders.front().get(); + apps->loaders.pop_front(); apps->states.push_back(app); init_scene(app->glscene, app->scene); update_lights(app->glscene, app->scene); if (apps->selected < 0) apps->selected = (int)apps->states.size() - 1; + } catch (std::exception& e) { + apps->loaders.pop_front(); + push_glmessage(win, e.what()); + log_glinfo(win, e.what()); } } } -int main(int argc, const char* argv[]) { +void run_app(int argc, const char* argv[]) { // initialize app auto apps = make_shared(); auto filenames = vector{}; @@ -641,7 +639,7 @@ int main(int argc, const char* argv[]) { add_cli_option( cli, "--noparallel", noparallel, "Disable parallel execution."); add_cli_option(cli, "scenes", filenames, "Scene filenames", true); - if (!parse_cli(cli, argc, argv)) exit(1); + parse_cli(cli, argc, argv); // loading images for (auto filename : filenames) load_scene_async(apps, filename); @@ -712,7 +710,14 @@ int main(int argc, const char* argv[]) { // clear clear_glwindow(win); +} - // done - return 0; +int main(int argc, const char* argv[]) { + try { + run_app(argc, argv); + return 0; + } catch (std::exception& e) { + print_fatal(e.what()); + return 1; + } } diff --git a/yocto/yocto_commonio.h b/yocto/yocto_commonio.h index 5c002c84f..b5b634c20 100644 --- a/yocto/yocto_commonio.h +++ b/yocto/yocto_commonio.h @@ -143,8 +143,11 @@ namespace yocto { // Initialize a command line parser. struct cli_state; inline cli_state make_cli(const string& cmd, const string& usage); -// check if any error occurred and exit appropriately -inline bool parse_cli(cli_state& cli, int argc, const char** argv); +// check if any error occurred and throws cli_error in that case +inline void parse_cli(cli_state& cli, int argc, const char** argv); +// check if any error occurred returning the error +inline bool parse_cli( + cli_state& cli, int argc, const char** argv, string& usage); // Parse an int, float, string, and bool option or positional argument. // Options's names starts with "--" or "-", otherwise they are arguments. @@ -206,20 +209,19 @@ inline bool exists_file(const string& filename); // ----------------------------------------------------------------------------- namespace yocto { -// Result of io operations -struct fileio_status { - string error = {}; - explicit operator bool() const { return error.empty(); } -}; - // Load/save a text file -inline fileio_status load_text(const string& filename, string& str); -inline fileio_status save_text(const string& filename, const string& str); +inline bool load_text(const string& filename, string& str, string& error); +inline bool save_text(const string& filename, const string& str, string& error); +inline void load_text(const string& filename, string& str); +inline void save_text(const string& filename, const string& str); // Load/save a binary file -inline fileio_status load_binary(const string& filename, vector& data); -inline fileio_status save_binary( - const string& filename, const vector& data); +inline bool load_binary( + const string& filename, vector& data, string& error); +inline bool save_binary( + const string& filename, const vector& data, string& error); +inline void load_binary(const string& filename, vector& data); +inline void save_binary(const string& filename, const vector& data); } // namespace yocto @@ -386,58 +388,98 @@ inline bool exists_file(const string& filename) { namespace yocto { // Load a text file -inline fileio_status load_text(const string& filename, string& str) { +inline bool load_text(const string& filename, string& str, string& error) { // https://stackoverflow.com/questions/174531/how-to-read-the-content-of-a-file-to-a-string-in-c auto fs = fopen(filename.c_str(), "rt"); - if (!fs) return {filename + ": file not found"}; + if (!fs) { + error = filename + ": file not found"; + return false; + } auto fs_guard = std::unique_ptr{fs, fclose}; fseek(fs, 0, SEEK_END); auto length = ftell(fs); fseek(fs, 0, SEEK_SET); str.resize(length); - if (fread(str.data(), 1, length, fs) != length) - return {filename + ": read error"}; - return {}; + if (fread(str.data(), 1, length, fs) != length) { + error = filename + ": read error"; + return false; + } + return true; } // Save a text file -inline fileio_status save_text(const string& filename, const string& str) { +inline bool save_text( + const string& filename, const string& str, string& error) { auto fs = fopen(filename.c_str(), "wt"); - if (!fs) return {filename + ": file not found"}; + if (!fs) { + error = filename + ": file not found"; + return false; + } auto fs_guard = std::unique_ptr{fs, fclose}; - if (fprintf(fs, "%s", str.c_str()) < 0) return {filename + ": write error"}; + if (fprintf(fs, "%s", str.c_str()) < 0) { + error = filename + ": write error"; + return false; + } fclose(fs); - return {}; + return true; +} + +inline void load_text(const string& filename, string& str) { + auto error = string{}; + if (!load_text(filename, str, error)) throw std::runtime_error(error); +} +inline void save_text(const string& filename, const string& str) { + auto error = string{}; + if (!save_text(filename, str, error)) throw std::runtime_error(error); } // Load a binary file -inline fileio_status load_binary(const string& filename, vector& data) { +inline bool load_binary( + const string& filename, vector& data, string& error) { // https://stackoverflow.com/questions/174531/how-to-read-the-content-of-a-file-to-a-string-in-c auto fs = fopen(filename.c_str(), "rb"); - if (!fs) return {filename + ": file not found"}; + if (!fs) { + error = filename + ": file not found"; + return false; + } auto fs_guard = std::unique_ptr{fs, fclose}; fseek(fs, 0, SEEK_END); auto length = ftell(fs); fseek(fs, 0, SEEK_SET); data.resize(length); - if (fread(data.data(), 1, length, fs) != length) - return {filename + ": read error"}; + if (fread(data.data(), 1, length, fs) != length) { + error = filename + ": read error"; + return false; + } fclose(fs); - return {}; + return true; } // Save a binary file -inline fileio_status save_binary( - const string& filename, const vector& data) { +inline bool save_binary( + const string& filename, const vector& data, string& error) { auto fs = fopen(filename.c_str(), "wb"); - if (!fs) return {filename + ": file not found"}; + if (!fs) { + error = filename + ": file not found"; + return false; + } auto fs_guard = std::unique_ptr{fs, fclose}; - if (fwrite(data.data(), 1, data.size(), fs) != data.size()) - return {filename + ": rewritead error"}; + if (fwrite(data.data(), 1, data.size(), fs) != data.size()) { + error = filename + ": rewritead error"; + return false; + } fclose(fs); - return {}; + return true; } +inline void load_binary(const string& filename, vector& data) { + auto error = string{}; + if (!load_binary(filename, data, error)) throw std::runtime_error(error); +} +inline void save_binary(const string& filename, const vector& data) { + auto error = string{}; + if (!save_binary(filename, data, error)) throw std::runtime_error(error); +} } // namespace yocto // ----------------------------------------------------------------------------- @@ -573,16 +615,21 @@ inline void add_cli_option(cli_state& cli, const string& name, int& value, cli, name, cli_type::enum_, &value, usage, req, choices); } -inline bool print_cli_help(cli_state& cli, const string& error) { - if (error != "") printf("error: %s\n\n", error.c_str()); - printf("usage: %s%s%s%s\n\n", cli.name.c_str(), - cli.usage_options.empty() ? "" : " [options]", - cli.usage_arguments.empty() ? "" : " ", cli.usage.c_str()); +struct cli_error : std::runtime_error { + cli_error(const string& message) : std::runtime_error{message} {} +}; + +inline void throw_cli_error(cli_state& cli, const string& error) { + auto message = string{}; + if (error != "") message += "error: " + error + "\n\n"; + message += + "usage: " + cli.name + (cli.usage_options.empty() ? "" : " [options]") + + (cli.usage_arguments.empty() ? "" : " ") + cli.usage + "\n\n"; if (!cli.usage_options.empty()) - printf("options:\n%s\n", cli.usage_options.c_str()); + message += "options:\n" + cli.usage_options + "\n"; if (!cli.usage_options.empty()) - printf("arguments:\n%s\n", cli.usage_arguments.c_str()); - return error.empty(); + message += "arguments:\n" + cli.usage_arguments + "\n"; + throw cli_error{message}; } inline bool parse_cmdline_value(const string& str, int& value) { @@ -607,7 +654,7 @@ inline bool parse_cmdline_value(const string& str, bool& value) { } } -inline bool parse_cli(cli_state& cli, int argc, const char** argv) { +inline void parse_cli(cli_state& cli, int argc, const char** argv) { // check for errors auto used = unordered_map{}; for (auto& option : cli.options) { @@ -636,7 +683,7 @@ inline bool parse_cli(cli_state& cli, int argc, const char** argv) { args.erase(args.begin() + pos); } else { if (pos + 1 >= args.size()) - return print_cli_help(cli, "missing value for " + name); + throw_cli_error(cli, "missing value for " + name); auto value = args[pos + 1]; args.erase(args.begin() + pos, args.begin() + pos + 2); if (option.type == cli_type::string_) { @@ -644,21 +691,21 @@ inline bool parse_cli(cli_state& cli, int argc, const char** argv) { option.set = true; } else if (option.type == cli_type::int_) { if (!parse_cmdline_value(value, *(int*)option.value)) - return print_cli_help(cli, "incorrect value for " + name); + throw_cli_error(cli, "incorrect value for " + name); option.set = true; } else if (option.type == cli_type::float_) { if (!parse_cmdline_value(value, *(float*)option.value)) - return print_cli_help(cli, "incorrect value for " + name); + throw_cli_error(cli, "incorrect value for " + name); option.set = true; } else if (option.type == cli_type::bool_) { if (!parse_cmdline_value(value, *(bool*)option.value)) - return print_cli_help(cli, "incorrect value for " + name); + throw_cli_error(cli, "incorrect value for " + name); option.set = true; } else if (option.type == cli_type::enum_) { auto pos = std::find( option.choices.begin(), option.choices.end(), value); if (pos == option.choices.end()) - return print_cli_help(cli, "incorrect value for " + name); + throw_cli_error(cli, "incorrect value for " + name); else *(int*)option.value = (int)(pos - option.choices.begin()); option.set = true; @@ -668,19 +715,18 @@ inline bool parse_cli(cli_state& cli, int argc, const char** argv) { } } if (option.req && !option.set) { - print_cli_help(cli, "missing value for " + option.name); + throw_cli_error(cli, "missing value for " + option.name); } } // check unknown options for (auto& arg : args) { - if (arg.find("-") == 0) return print_cli_help(cli, "unknown option " + arg); + if (arg.find("-") == 0) throw_cli_error(cli, "unknown option " + arg); } // parse positional for (auto& option : cli.options) { if (option.name[0] == '-') continue; if (args.empty()) { - if (option.req) - return print_cli_help(cli, "missing value for " + option.name); + if (option.req) throw_cli_error(cli, "missing value for " + option.name); } else if (option.type == cli_type::string_vector_) { *(vector*)option.value = args; option.set = true; @@ -693,15 +739,15 @@ inline bool parse_cli(cli_state& cli, int argc, const char** argv) { option.set = true; } else if (option.type == cli_type::int_) { if (!parse_cmdline_value(value, *(int*)option.value)) - return print_cli_help(cli, "incorrect value for " + option.name); + throw_cli_error(cli, "incorrect value for " + option.name); option.set = true; } else if (option.type == cli_type::float_) { if (!parse_cmdline_value(value, *(float*)option.value)) - return print_cli_help(cli, "incorrect value for " + option.name); + throw_cli_error(cli, "incorrect value for " + option.name); option.set = true; } else if (option.type == cli_type::bool_) { if (!parse_cmdline_value(value, *(bool*)option.value)) - return print_cli_help(cli, "incorrect value for " + option.name); + throw_cli_error(cli, "incorrect value for " + option.name); option.set = true; } else { throw std::runtime_error("unsupported type"); @@ -710,8 +756,7 @@ inline bool parse_cli(cli_state& cli, int argc, const char** argv) { } // check remaining if (!args.empty()) - return print_cli_help(cli, "mismatched value for " + args.front()); - return true; + throw_cli_error(cli, "mismatched value for " + args.front()); } } // namespace yocto diff --git a/yocto/yocto_image.cpp b/yocto/yocto_image.cpp index b3501d6ca..a4fa63a16 100644 --- a/yocto/yocto_image.cpp +++ b/yocto/yocto_image.cpp @@ -1626,142 +1626,138 @@ bool is_hdr_filename(const string& filename) { return ext == ".hdr" || ext == ".exr" || ext == ".pfm"; } +// Helpers for throwing +static void throw_read_error(const string& filename) { + throw std::runtime_error{filename + ": read error"}; +} +static void throw_write_error(const string& filename) { + throw std::runtime_error{filename + ": write error"}; +} +static void throw_preset_error(const string& filename) { + throw std::runtime_error{filename + ": unknown preset"}; +} +static void throw_format_error(const string& filename) { + throw std::runtime_error{filename + ": unknown format"}; +} + // Loads an hdr image. image load_image(const string& filename) { auto img = image{}; - if (!load_image(filename, img)) return {}; + load_image(filename, img); return img; } // Loads an hdr image. -imageio_status load_image(const string& filename, image& img) { +void load_image(const string& filename, image& img) { auto ext = get_extension(filename); if (ext == ".ypreset") { img = make_image_preset(get_basename(filename)); - if (img.empty()) return {filename + ": unknown preset"}; - return {}; + if (img.empty()) throw_preset_error(filename); } if (ext == ".exr" || ext == ".EXR") { auto width = 0, height = 0; auto pixels = (float*)nullptr; if (LoadEXR(&pixels, &width, &height, filename.c_str(), nullptr) < 0) - return {filename + ": read error"}; - if (!pixels) return {filename + ": read error"}; + throw_read_error(filename); + if (!pixels) throw_read_error(filename); img = image{{width, height}, (const vec4f*)pixels}; free(pixels); - return {}; } else if (ext == ".pfm" || ext == ".PFM") { auto width = 0, height = 0, ncomp = 0; auto pixels = load_pfm(filename.c_str(), &width, &height, &ncomp, 4); - if (!pixels) return {filename + ": read error"}; + if (!pixels) throw_read_error(filename); img = image{{width, height}, (const vec4f*)pixels}; delete[] pixels; - return {}; } else if (ext == ".hdr" || ext == ".HDR") { auto width = 0, height = 0, ncomp = 0; auto pixels = stbi_loadf(filename.c_str(), &width, &height, &ncomp, 4); - if (!pixels) return {filename + ": read error"}; + if (!pixels) throw_read_error(filename); img = image{{width, height}, (const vec4f*)pixels}; free(pixels); - return {}; } else if (!is_hdr_filename(filename)) { auto imgb = image{}; - if (!load_imageb(filename, imgb)) return {filename + ": read error"}; + load_imageb(filename, imgb); img = srgb_to_rgb(imgb); - return {}; } else { - return {filename + ": unsupported format"}; + throw_format_error(filename); } } // Saves an hdr image. -imageio_status save_image(const string& filename, const image& img) { +void save_image(const string& filename, const image& img) { auto ext = get_extension(filename); if (ext == ".hdr" || ext == ".HDR") { if (!stbi_write_hdr(filename.c_str(), img.size().x, img.size().y, 4, (float*)img.data())) - return {filename + ": write error"}; - return {}; + throw_write_error(filename); } else if (ext == ".pfm" || ext == ".PFM") { if (!save_pfm(filename.c_str(), img.size().x, img.size().y, 4, (float*)img.data())) - return {filename + ": write error"}; - return {}; + throw_write_error(filename); } else if (ext == ".exr" || ext == ".EXR") { if (SaveEXR((float*)img.data(), img.size().x, img.size().y, 4, filename.c_str()) < 0) - return {filename + ": write error"}; - return {}; + throw_write_error(filename); } else if (!is_hdr_filename(filename)) { - if (!save_imageb(filename, rgb_to_srgbb(img))) - return {filename + ": write error"}; - return {}; + save_imageb(filename, rgb_to_srgbb(img)); } else { - return {filename + ": unsupported format"}; + throw_format_error(filename); } } // Loads an ldr image. image load_imageb(const string& filename) { auto img = image{}; - if (!load_imageb(filename, img)) return {}; + load_imageb(filename, img); return img; } // Loads an ldr image. -imageio_status load_imageb(const string& filename, image& img) { +void load_imageb(const string& filename, image& img) { auto ext = get_extension(filename); if (ext == ".ypreset") { img = make_image_presetb(get_basename(filename)); - if (img.empty()) - return {filename + ": unknown preset " + get_basename(filename)}; - return {}; + if (img.empty()) throw_preset_error(filename); } if (ext == ".png" || ext == ".PNG" || ext == ".jpg" || ext == ".JPG" || ext == ".tga" || ext == ".TGA" || ext == ".bmp" || ext == ".BMP") { auto width = 0, height = 0, ncomp = 0; auto pixels = stbi_load(filename.c_str(), &width, &height, &ncomp, 4); - if (!pixels) return {filename + ": read error"}; + if (!pixels) throw_read_error(filename); img = image{{width, height}, (const vec4b*)pixels}; free(pixels); - return {}; } else if (is_hdr_filename(filename)) { auto imgf = image{}; - if (auto ret = load_image(filename, imgf); !ret) return ret; + load_image(filename, imgf); img = rgb_to_srgbb(imgf); - return {}; } else { - return {filename + ": unsupported format"}; + throw_format_error(filename); } } // Saves an ldr image. -imageio_status save_imageb(const string& filename, const image& img) { +void save_imageb(const string& filename, const image& img) { auto ext = get_extension(filename); if (ext == ".png" || ext == ".PNG") { if (!stbi_write_png(filename.c_str(), img.size().x, img.size().y, 4, img.data(), img.size().x * 4)) - return {filename + ": write wrror"}; - return {}; + throw_write_error(filename); } else if (ext == ".jpg" || ext == ".JPG") { if (!stbi_write_jpg( filename.c_str(), img.size().x, img.size().y, 4, img.data(), 75)) - return {filename + ": write wrror"}; - return {}; + throw_write_error(filename); } else if (ext == ".tga" || ext == ".TGA") { if (!stbi_write_tga( filename.c_str(), img.size().x, img.size().y, 4, img.data())) - return {filename + ": write wrror"}; - return {}; + throw_write_error(filename); } else if (ext == ".bmp" || ext == ".BMP") { if (!stbi_write_bmp( filename.c_str(), img.size().x, img.size().y, 4, img.data())) - return {filename + ": write wrror"}; - return {}; + throw_write_error(filename); } else if (is_hdr_filename(filename)) { - return save_image(filename, srgb_to_rgb(img)); + save_image(filename, srgb_to_rgb(img)); } else { - return {filename + ": unsupported format"}; + throw_format_error(filename); } } @@ -1915,9 +1911,7 @@ static inline bool save_yvol( void load_volume(const string& filename, volume& vol) { auto width = 0, height = 0, depth = 0, ncomp = 0; auto voxels = load_yvol(filename.c_str(), &width, &height, &depth, &ncomp, 1); - if (!voxels) { - throw std::runtime_error("error loading volume " + filename); - } + if (!voxels) throw_read_error(filename); vol = volume{{width, height, depth}, (const float*)voxels}; delete[] voxels; } @@ -1925,9 +1919,8 @@ void load_volume(const string& filename, volume& vol) { // Saves volume data in binary format. void save_volume(const string& filename, const volume& vol) { if (!save_yvol(filename.c_str(), vol.size().x, vol.size().y, vol.size().z, 1, - vol.data())) { - throw std::runtime_error("error saving volume " + filename); - } + vol.data())) + throw_write_error(filename); } } // namespace impl diff --git a/yocto/yocto_image.h b/yocto/yocto_image.h index a06555e90..6aaca5ff0 100644 --- a/yocto/yocto_image.h +++ b/yocto/yocto_image.h @@ -247,19 +247,14 @@ namespace yocto { // Check if an image is HDR based on filename. bool is_hdr_filename(const string& filename); -// Result of io operations -struct imageio_status { - string error = {}; - explicit operator bool() const { return error.empty(); } -}; - // Loads/saves a 4 channels float/byte image in linear/srgb color space. -image load_image(const string& filename); -imageio_status load_image(const string& filename, image& img); -imageio_status save_image(const string& filename, const image& img); -image load_imageb(const string& filename); -imageio_status load_imageb(const string& filename, image& img); -imageio_status save_imageb(const string& filename, const image& img); +// Throws exception on error. +image load_image(const string& filename); +void load_image(const string& filename, image& img); +void save_image(const string& filename, const image& img); +image load_imageb(const string& filename); +void load_imageb(const string& filename, image& img); +void save_imageb(const string& filename, const image& img); } // namespace yocto diff --git a/yocto/yocto_modelio.cpp b/yocto/yocto_modelio.cpp index c110619df..0f06631cf 100644 --- a/yocto/yocto_modelio.cpp +++ b/yocto/yocto_modelio.cpp @@ -148,167 +148,141 @@ static bool is_whitespace(string_view str) { } // Parse values from a string -static bool parse_value(string_view& str, string_view& value) { +static void parse_value(string_view& str, string_view& value) { skip_whitespace(str); - if (str.empty()) return false; + if (str.empty()) throw std::invalid_argument{"string expected"}; if (str.front() != '"') { auto cpy = str; while (!cpy.empty() && !is_space(cpy.front())) cpy.remove_prefix(1); value = str; value.remove_suffix(cpy.size()); str.remove_prefix(str.size() - cpy.size()); - return true; } else { - if (str.front() != '"') return false; + if (str.front() != '"') throw std::invalid_argument{"string expected"}; str.remove_prefix(1); - if (str.empty()) return false; + if (str.empty()) throw std::invalid_argument{"string expected"}; auto cpy = str; while (!cpy.empty() && cpy.front() != '"') cpy.remove_prefix(1); - if (cpy.empty()) return false; + if (cpy.empty()) throw std::invalid_argument{"string expected"}; value = str; value.remove_suffix(cpy.size()); str.remove_prefix(str.size() - cpy.size()); str.remove_prefix(1); - return true; } } -static bool parse_value(string_view& str, string& value) { +static void parse_value(string_view& str, string& value) { auto valuev = string_view{}; - if (!parse_value(str, valuev)) return false; + parse_value(str, valuev); value = string{valuev}; - return true; } -static bool parse_value(string_view& str, int8_t& value) { +static void parse_value(string_view& str, int8_t& value) { char* end = nullptr; value = (int8_t)strtol(str.data(), &end, 10); - if (str.data() == end) return false; + if (str.data() == end) throw std::invalid_argument{"int expected"}; str.remove_prefix(end - str.data()); - return true; } -static bool parse_value(string_view& str, int16_t& value) { +static void parse_value(string_view& str, int16_t& value) { char* end = nullptr; value = (int16_t)strtol(str.data(), &end, 10); - if (str.data() == end) return false; + if (str.data() == end) throw std::invalid_argument{"int expected"}; str.remove_prefix(end - str.data()); - return true; } -static bool parse_value(string_view& str, int32_t& value) { +static void parse_value(string_view& str, int32_t& value) { char* end = nullptr; value = (int32_t)strtol(str.data(), &end, 10); - if (str.data() == end) return false; + if (str.data() == end) throw std::invalid_argument{"int expected"}; str.remove_prefix(end - str.data()); - return true; } -static bool parse_value(string_view& str, int64_t& value) { +static void parse_value(string_view& str, int64_t& value) { char* end = nullptr; value = (int64_t)strtoll(str.data(), &end, 10); - if (str.data() == end) return false; + if (str.data() == end) throw std::invalid_argument{"int expected"}; str.remove_prefix(end - str.data()); - return true; } -static bool parse_value(string_view& str, uint8_t& value) { +static void parse_value(string_view& str, uint8_t& value) { char* end = nullptr; value = (uint8_t)strtoul(str.data(), &end, 10); - if (str.data() == end) return false; + if (str.data() == end) throw std::invalid_argument{"uint expected"}; str.remove_prefix(end - str.data()); - return true; } -static bool parse_value(string_view& str, uint16_t& value) { +static void parse_value(string_view& str, uint16_t& value) { char* end = nullptr; value = (uint16_t)strtoul(str.data(), &end, 10); - if (str.data() == end) return false; + if (str.data() == end) throw std::invalid_argument{"uint expected"}; str.remove_prefix(end - str.data()); - return true; } -static bool parse_value(string_view& str, uint32_t& value) { +static void parse_value(string_view& str, uint32_t& value) { char* end = nullptr; value = (uint32_t)strtoul(str.data(), &end, 10); - if (str.data() == end) return false; + if (str.data() == end) throw std::invalid_argument{"uint expected"}; str.remove_prefix(end - str.data()); - return true; } -static bool parse_value(string_view& str, uint64_t& value) { +static void parse_value(string_view& str, uint64_t& value) { char* end = nullptr; value = (uint64_t)strtoull(str.data(), &end, 10); - if (str.data() == end) return false; + if (str.data() == end) throw std::invalid_argument{"uint expected"}; str.remove_prefix(end - str.data()); - return true; } -static bool parse_value(string_view& str, bool& value) { +static void parse_value(string_view& str, bool& value) { auto valuei = 0; - if (!parse_value(str, valuei)) return false; + parse_value(str, valuei); value = (bool)valuei; - return true; } -static bool parse_value(string_view& str, float& value) { +static void parse_value(string_view& str, float& value) { char* end = nullptr; value = strtof(str.data(), &end); - if (str.data() == end) return false; + if (str.data() == end) throw std::invalid_argument{"float expected"}; str.remove_prefix(end - str.data()); - return true; } -static bool parse_value(string_view& str, double& value) { +static void parse_value(string_view& str, double& value) { char* end = nullptr; value = strtod(str.data(), &end); - if (str.data() == end) return false; + if (str.data() == end) throw std::invalid_argument{"double expected"}; str.remove_prefix(end - str.data()); - return true; } #ifdef __APPLE__ -static bool parse_value(string_view& str, size_t& value) { +static void parse_value(string_view& str, size_t& value) { char* end = nullptr; value = (size_t)strtoull(str.data(), &end, 10); - if (str.data() == end) return false; + if (str.data() == end) throw std::invalid_argument{"uint expected"}; str.remove_prefix(end - str.data()); - return true; } #endif -static bool parse_value(string_view& str, vec2f& value) { - for (auto i = 0; i < 2; i++) - if (!parse_value(str, value[i])) return false; - return true; +static void parse_value(string_view& str, vec2f& value) { + for (auto i = 0; i < 2; i++) parse_value(str, value[i]); } -static bool parse_value(string_view& str, vec3f& value) { - for (auto i = 0; i < 3; i++) - if (!parse_value(str, value[i])) return false; - return true; +static void parse_value(string_view& str, vec3f& value) { + for (auto i = 0; i < 3; i++) parse_value(str, value[i]); } -static bool parse_value(string_view& str, vec4f& value) { - for (auto i = 0; i < 4; i++) - if (!parse_value(str, value[i])) return false; - return true; +static void parse_value(string_view& str, vec4f& value) { + for (auto i = 0; i < 4; i++) parse_value(str, value[i]); } -static bool parse_value(string_view& str, frame3f& value) { - for (auto i = 0; i < 4; i++) - if (!parse_value(str, value[i])) return false; - return true; +static void parse_value(string_view& str, frame3f& value) { + for (auto i = 0; i < 4; i++) parse_value(str, value[i]); } -static bool parse_value(string_view& str, mat4f& value) { - for (auto i = 0; i < 4; i++) - if (!parse_value(str, value[i])) return false; - return true; +static void parse_value(string_view& str, mat4f& value) { + for (auto i = 0; i < 4; i++) parse_value(str, value[i]); } // Parse values from a string -static bool parse_value_or_empty(string_view& str, string& value) { +static void parse_value_or_empty(string_view& str, string& value) { skip_whitespace(str); if (str.empty()) { value = ""; - return true; } else { - return parse_value(str, value); + parse_value(str, value); } } template -static bool parse_value( +static void parse_value( string_view& str, T& value, unordered_map& value_names) { auto value_name = ""s; - if (!parse_value(str, value_name)) return false; - if (value_names.find(value_name) == value_names.end()) return false; + parse_value(str, value_name); + if (value_names.find(value_name) == value_names.end()) + throw std::invalid_argument{"wrong value"}; value = value_names.at(value_name); - return true; } } // namespace yocto @@ -413,37 +387,87 @@ template static void format_values( string& str, const string& fmt, const Arg& arg, const Args&... args) { auto pos = fmt.find("{}"); - if (pos == string::npos) throw std::runtime_error("bad format string"); + if (pos == string::npos) throw std::invalid_argument("bad format string"); str += fmt.substr(0, pos); format_value(str, arg); format_values(str, fmt.substr(pos + 2), args...); } template -static bool format_values(FILE* fs, const string& fmt, const Args&... args) { +static void format_values(FILE* fs, const string& fmt, const Args&... args) { auto str = ""s; format_values(str, fmt, args...); - return fputs(str.c_str(), fs) >= 0; + if (fputs(str.c_str(), fs) < 0) throw std::invalid_argument{"write error"}; } template -static bool format_value(FILE* fs, const T& value) { +static void format_value(FILE* fs, const T& value) { auto str = ""s; format_value(str, value); - return fputs(str.c_str(), fs) >= 0; + if (fputs(str.c_str(), fs) < 0) throw std::invalid_argument{"write error"}; } } // namespace yocto // ----------------------------------------------------------------------------- -// PLY CONVERSION +// FILE WRAPPER // ----------------------------------------------------------------------------- namespace yocto { -static void remove_ply_comment(string_view& str, char comment_char = '#') { - while (!str.empty() && is_newline(str.back())) str.remove_suffix(1); - auto cpy = str; - while (!cpy.empty() && cpy.front() != comment_char) cpy.remove_prefix(1); - str.remove_suffix(cpy.size()); +// file wrapper with RIIA +struct file_wrapper { + file_wrapper() {} + ~file_wrapper() { + if (fs) fclose(fs); + } + file_wrapper(const file_wrapper&) = delete; + file_wrapper& operator=(const file_wrapper&) = delete; + file_wrapper(file_wrapper&& other) { + if (this == &other) return; + std::swap(filename, other.filename); + std::swap(filename, other.filename); + } + file_wrapper& operator=(file_wrapper&& other) { + if (this == &other) return *this; + std::swap(filename, other.filename); + std::swap(filename, other.filename); + return *this; + } + + string filename = ""s; + FILE* fs = nullptr; +}; + +file_wrapper open_file(const string& filename, const string& mode) { + auto fs = file_wrapper{}; + fs.fs = fopen(filename.c_str(), mode.c_str()); + if (!fs.fs) throw std::runtime_error{filename + ": file not found"}; + fs.filename = filename; + return fs; +} + +bool read_line(file_wrapper& fs, char* buffer, int size) { + return (bool)fgets(buffer, size, fs.fs); +} + +template +void parse_value(file_wrapper& fs, string_view& str, T& value) { + try { + parse_value(str, value); + } catch (std::exception& e) { + throw std::runtime_error{fs.filename + ": parse error [" + e.what() + "]"}; + } +} + +template +void read_value(file_wrapper& fs, T& value) { + if (fread(&value, sizeof(value), 1, fs.fs) != 1) + throw std::runtime_error{fs.filename + ": read error"}; +} + +template +void write_value(file_wrapper& fs, const T& value) { + if (fwrite(&value, sizeof(value), 1, fs) != 1) + throw std::runtime_error{fs.filename + ": write error"}; } template @@ -460,8 +484,62 @@ static T swap_endian(T value) { return dest.value; } +template +void read_value(file_wrapper& fs, T& value, bool big_endian) { + if (fread(&value, sizeof(value), 1, fs.fs) != 1) + throw std::runtime_error{fs.filename + ": read error"}; + if (big_endian) value = swap_endian(value); +} + +template +void write_value(file_wrapper& fs, const T& value_, bool big_endian) { + auto value = big_endian ? swap_endian(value_) : value_; + if (fwrite(&value, sizeof(value), 1, fs.fs) != 1) + throw std::runtime_error{fs.filename + ": write error"}; +} + +template +static void format_values( + file_wrapper& fs, const string& fmt, const Args&... args) { + auto str = ""s; + format_values(str, fmt, args...); + if (fputs(str.c_str(), fs.fs) < 0) + throw std::runtime_error{fs.filename + ": write error"}; +} +template +static void format_value(file_wrapper& fs, const T& value) { + auto str = ""s; + format_value(str, value); + if (fputs(str.c_str(), fs.fs) < 0) + throw std::runtime_error{fs.filename + ": write error"}; +} + +static void throw_dependent_error(file_wrapper& fs, const string& err) { + throw std::runtime_error{fs.filename + ": error in resource (" + err + ")"}; +} +static void throw_parse_error(file_wrapper& fs, const string& err) { + throw std::runtime_error{fs.filename + ": parse error [" + err + "]"}; +} +static void throw_read_error(file_wrapper& fs) { + throw std::runtime_error{fs.filename + ": read error"}; +} + +} // namespace yocto + +// ----------------------------------------------------------------------------- +// PLY CONVERSION +// ----------------------------------------------------------------------------- +namespace yocto { + +static void remove_ply_comment(string_view& str, char comment_char = '#') { + while (!str.empty() && is_newline(str.back())) str.remove_suffix(1); + auto cpy = str; + while (!cpy.empty() && cpy.front() != comment_char) cpy.remove_prefix(1); + str.remove_suffix(cpy.size()); +} + // Load ply -plyio_status load_ply(const string& filename, ply_model& ply) { +void load_ply(const string& filename, ply_model& ply) { // ply type names static auto type_map = unordered_map{{"char", ply_type::i8}, {"short", ply_type::i16}, {"int", ply_type::i32}, {"long", ply_type::i64}, @@ -482,13 +560,11 @@ plyio_status load_ply(const string& filename, ply_model& ply) { auto end_header = false; // open file - auto fs = fopen(filename.c_str(), "rb"); - if (!fs) return {filename + ": file not found"}; - auto fs_guard = std::unique_ptr{fs, fclose}; + auto fs = open_file(filename, "rb"); // read header --------------------------------------------- char buffer[4096]; - while (fgets(buffer, sizeof(buffer), fs)) { + while (read_line(fs, buffer, sizeof(buffer))) { // str auto str = string_view{buffer}; remove_ply_comment(str); @@ -497,22 +573,22 @@ plyio_status load_ply(const string& filename, ply_model& ply) { // get command auto cmd = ""s; - if (!parse_value(str, cmd)) return {filename + ": parse error"}; + parse_value(fs, str, cmd); if (cmd == "") continue; // check magic number if (first_line) { - if (cmd != "ply") return {filename + ": not a ply file"}; + if (cmd != "ply") throw_parse_error(fs, "bad header"); first_line = false; continue; } // possible token values if (cmd == "ply") { - if (!first_line) return {filename + ": corrupt header"}; + if (!first_line) throw_parse_error(fs, "bad header"); } else if (cmd == "format") { auto fmt = ""s; - if (!parse_value(str, fmt)) return {filename + ": parse error"}; + parse_value(fs, str, fmt); if (fmt == "ascii") { ply.format = ply_format::ascii; } else if (fmt == "binary_little_endian") { @@ -520,7 +596,7 @@ plyio_status load_ply(const string& filename, ply_model& ply) { } else if (fmt == "binary_big_endian") { ply.format = ply_format::binary_big_endian; } else { - return {filename + ": unknown format " + fmt}; + throw_parse_error(fs, "bad header"); } } else if (cmd == "comment") { skip_whitespace(str); @@ -530,40 +606,40 @@ plyio_status load_ply(const string& filename, ply_model& ply) { // comment is the rest of the str } else if (cmd == "element") { auto& elem = ply.elements.emplace_back(); - if (!parse_value(str, elem.name)) return {filename + ": parse error"}; - if (!parse_value(str, elem.count)) return {filename + ": parse error"}; + parse_value(fs, str, elem.name); + parse_value(fs, str, elem.count); } else if (cmd == "property") { - if (ply.elements.empty()) return {filename + ": corrupt header"}; + if (ply.elements.empty()) throw_parse_error(fs, "bad header"); auto& prop = ply.elements.back().properties.emplace_back(); auto tname = ""s; - if (!parse_value(str, tname)) return {filename + ": parse error"}; + parse_value(fs, str, tname); if (tname == "list") { prop.is_list = true; - if (!parse_value(str, tname)) return {filename + ": parse error"}; + parse_value(fs, str, tname); auto itype = type_map.at(tname); if (itype != ply_type::u8) - return {filename + ": unsupported list size type " + tname}; - if (!parse_value(str, tname)) return {filename + ": parse error"}; + throw_parse_error(fs, "unknown type" + tname); + parse_value(fs, str, tname); if (type_map.find(tname) == type_map.end()) - return {filename + ": unknown type " + tname}; + throw_parse_error(fs, "unknown type" + tname); prop.type = type_map.at(tname); } else { prop.is_list = false; if (type_map.find(tname) == type_map.end()) - return {filename + ": unknown type " + tname}; + throw_parse_error(fs, "unknown type" + tname); prop.type = type_map.at(tname); } - if (!parse_value(str, prop.name)) return {filename + ": parse error"}; + parse_value(fs, str, prop.name); } else if (cmd == "end_header") { end_header = true; break; } else { - return {filename + ": unknown command " + cmd}; + throw_parse_error(fs, "unknown command " + cmd); } } // check exit - if (!end_header) return {filename + ": incomplete header"}; + if (!end_header) throw std::invalid_argument{"bad header"}; // allocate data --------------------------------- for (auto& element : ply.elements) { @@ -585,66 +661,49 @@ plyio_status load_ply(const string& filename, ply_model& ply) { } } - auto read_value = [](FILE* fs, auto& value, bool big_endian) -> bool { - auto ok = fread(&value, sizeof(value), 1, fs) == 1; - if (big_endian) value = swap_endian(value); - return ok; - }; - // read data ------------------------------------- if (ply.format == ply_format::ascii) { + char buffer[4096]; for (auto& elem : ply.elements) { for (auto idx = 0; idx < elem.count; idx++) { - if (!fgets(buffer, sizeof(buffer), fs)) - return {filename + ": read error"}; + if (!read_line(fs, buffer, sizeof(buffer))) throw_read_error(fs); auto str = string_view{buffer}; for (auto& prop : elem.properties) { if (prop.is_list) { - if (!parse_value(str, prop.ldata_u8.emplace_back())) - return {filename + ": parse error"}; + parse_value(fs, str, prop.ldata_u8.emplace_back()); } auto vcount = prop.is_list ? prop.ldata_u8.back() : 1; for (auto i = 0; i < vcount; i++) { switch (prop.type) { case ply_type::i8: - if (!parse_value(str, prop.data_i8.emplace_back())) - return {filename + ": parse error"}; + parse_value(fs, str, prop.data_i8.emplace_back()); break; case ply_type::i16: - if (!parse_value(str, prop.data_i16.emplace_back())) - return {filename + ": parse error"}; + parse_value(fs, str, prop.data_i16.emplace_back()); break; case ply_type::i32: - if (!parse_value(str, prop.data_i32.emplace_back())) - return {filename + ": parse error"}; + parse_value(fs, str, prop.data_i32.emplace_back()); break; case ply_type::i64: - if (!parse_value(str, prop.data_i64.emplace_back())) - return {filename + ": parse error"}; + parse_value(fs, str, prop.data_i64.emplace_back()); break; case ply_type::u8: - if (!parse_value(str, prop.data_u8.emplace_back())) - return {filename + ": parse error"}; + parse_value(fs, str, prop.data_u8.emplace_back()); break; case ply_type::u16: - if (!parse_value(str, prop.data_u16.emplace_back())) - return {filename + ": parse error"}; + parse_value(fs, str, prop.data_u16.emplace_back()); break; case ply_type::u32: - if (!parse_value(str, prop.data_u32.emplace_back())) - return {filename + ": parse error"}; + parse_value(fs, str, prop.data_u32.emplace_back()); break; case ply_type::u64: - if (!parse_value(str, prop.data_u64.emplace_back())) - return {filename + ": parse error"}; + parse_value(fs, str, prop.data_u64.emplace_back()); break; case ply_type::f32: - if (!parse_value(str, prop.data_f32.emplace_back())) - return {filename + ": parse error"}; + parse_value(fs, str, prop.data_f32.emplace_back()); break; case ply_type::f64: - if (!parse_value(str, prop.data_f64.emplace_back())) - return {filename + ": parse error"}; + parse_value(fs, str, prop.data_f64.emplace_back()); break; } } @@ -657,51 +716,40 @@ plyio_status load_ply(const string& filename, ply_model& ply) { for (auto idx = 0; idx < elem.count; idx++) { for (auto& prop : elem.properties) { if (prop.is_list) { - if (!read_value(fs, prop.ldata_u8.emplace_back(), big_endian)) - return {filename + ": read error"}; + read_value(fs, prop.ldata_u8.emplace_back(), big_endian); } auto vcount = prop.is_list ? prop.ldata_u8.back() : 1; for (auto i = 0; i < vcount; i++) { switch (prop.type) { case ply_type::i8: - if (!read_value(fs, prop.data_i8.emplace_back(), big_endian)) - return {filename + ": read error"}; + read_value(fs, prop.data_i8.emplace_back(), big_endian); break; case ply_type::i16: - if (!read_value(fs, prop.data_i16.emplace_back(), big_endian)) - return {filename + ": read error"}; + read_value(fs, prop.data_i16.emplace_back(), big_endian); break; case ply_type::i32: - if (!read_value(fs, prop.data_i32.emplace_back(), big_endian)) - return {filename + ": read error"}; + read_value(fs, prop.data_i32.emplace_back(), big_endian); break; case ply_type::i64: - if (!read_value(fs, prop.data_i64.emplace_back(), big_endian)) - return {filename + ": read error"}; + read_value(fs, prop.data_i64.emplace_back(), big_endian); break; case ply_type::u8: - if (!read_value(fs, prop.data_u8.emplace_back(), big_endian)) - return {filename + ": read error"}; + read_value(fs, prop.data_u8.emplace_back(), big_endian); break; case ply_type::u16: - if (!read_value(fs, prop.data_u16.emplace_back(), big_endian)) - return {filename + ": read error"}; + read_value(fs, prop.data_u16.emplace_back(), big_endian); break; case ply_type::u32: - if (!read_value(fs, prop.data_u32.emplace_back(), big_endian)) - return {filename + ": read error"}; + read_value(fs, prop.data_u32.emplace_back(), big_endian); break; case ply_type::u64: - if (!read_value(fs, prop.data_u64.emplace_back(), big_endian)) - return {filename + ": read error"}; + read_value(fs, prop.data_u64.emplace_back(), big_endian); break; case ply_type::f32: - if (!read_value(fs, prop.data_f32.emplace_back(), big_endian)) - return {filename + ": read error"}; + read_value(fs, prop.data_f32.emplace_back(), big_endian); break; case ply_type::f64: - if (!read_value(fs, prop.data_f64.emplace_back(), big_endian)) - return {filename + ": read error"}; + read_value(fs, prop.data_f64.emplace_back(), big_endian); break; } } @@ -709,15 +757,11 @@ plyio_status load_ply(const string& filename, ply_model& ply) { } } } - - return {}; } // Save ply -plyio_status save_ply(const string& filename, const ply_model& ply) { - auto fs = fopen(filename.c_str(), "wb"); - if (!fs) return {filename + ": file not found"}; - auto fs_guard = std::unique_ptr{fs, fclose}; +void save_ply(const string& filename, const ply_model& ply) { + auto fs = open_file(filename, "wb"); // ply type names static auto type_map = unordered_map{{ply_type::i8, "char"}, @@ -731,37 +775,24 @@ plyio_status save_ply(const string& filename, const ply_model& ply) { {ply_format::binary_big_endian, "binary_big_endian"}}; // header - if (!format_values(fs, "ply\n")) return {filename + ": write error"}; - if (!format_values(fs, "format {} 1.0\n", format_map.at(ply.format))) - return {filename + ": write error"}; - if (!format_values(fs, "comment Written by Yocto/GL\n")) - return {filename + ": write error"}; - if (!format_values(fs, "comment https://github.com/xelatihy/yocto-gl\n")) - return {filename + ": write error"}; - for (auto& comment : ply.comments) - if (!format_values(fs, "comment {}\n", comment)) - return {filename + ": write error"}; + format_values(fs, "ply\n"); + format_values(fs, "format {} 1.0\n", format_map.at(ply.format)); + format_values(fs, "comment Written by Yocto/GL\n"); + format_values(fs, "comment https://github.com/xelatihy/yocto-gl\n"); + for (auto& comment : ply.comments) format_values(fs, "comment {}\n", comment); for (auto& elem : ply.elements) { - if (!format_values(fs, "element {} {}\n", elem.name, (uint64_t)elem.count)) - return {filename + ": write error"}; + format_values(fs, "element {} {}\n", elem.name, (uint64_t)elem.count); for (auto& prop : elem.properties) { if (prop.is_list) { - if (!format_values(fs, "property list uchar {} {}\n", - type_map[prop.type], prop.name)) - return {filename + ": write error"}; + format_values( + fs, "property list uchar {} {}\n", type_map[prop.type], prop.name); } else { - if (!format_values( - fs, "property {} {}\n", type_map[prop.type], prop.name)) - return {filename + ": write error"}; + format_values(fs, "property {} {}\n", type_map[prop.type], prop.name); } } } - if (!format_values(fs, "end_header\n")) return {filename + ": write error"}; - auto write_value = [](FILE* fs, auto value_, bool big_endian) -> bool { - auto value = big_endian ? swap_endian(value_) : value_; - return fwrite(&value, sizeof(value), 1, fs) == 1; - }; + format_values(fs, "end_header\n"); // properties if (ply.format == ply_format::ascii) { @@ -770,55 +801,43 @@ plyio_status save_ply(const string& filename, const ply_model& ply) { for (auto idx = 0; idx < elem.count; idx++) { for (auto pidx = 0; pidx < elem.properties.size(); pidx++) { auto& prop = elem.properties[pidx]; - if (prop.is_list) - if (!format_values(fs, "{} ", (int)prop.ldata_u8[idx])) - return {filename + ": write error"}; + if (prop.is_list) format_values(fs, "{} ", (int)prop.ldata_u8[idx]); auto vcount = prop.is_list ? prop.ldata_u8[idx] : 1; for (auto i = 0; i < vcount; i++) { switch (prop.type) { case ply_type::i8: - if (!format_values(fs, "{} ", prop.data_i8[cur[idx]++])) - return {filename + ": write error"}; + format_values(fs, "{} ", prop.data_i8[cur[idx]++]); break; case ply_type::i16: - if (!format_values(fs, "{} ", prop.data_i16[cur[idx]++])) - return {filename + ": write error"}; + format_values(fs, "{} ", prop.data_i16[cur[idx]++]); break; case ply_type::i32: - if (!format_values(fs, "{} ", prop.data_i32[cur[idx]++])) - return {filename + ": write error"}; + format_values(fs, "{} ", prop.data_i32[cur[idx]++]); break; case ply_type::i64: - if (!format_values(fs, "{} ", prop.data_i64[cur[idx]++])) - return {filename + ": write error"}; + format_values(fs, "{} ", prop.data_i64[cur[idx]++]); break; case ply_type::u8: - if (!format_values(fs, "{} ", prop.data_u8[cur[idx]++])) - return {filename + ": write error"}; + format_values(fs, "{} ", prop.data_u8[cur[idx]++]); break; case ply_type::u16: - if (!format_values(fs, "{} ", prop.data_u16[cur[idx]++])) - return {filename + ": write error"}; + format_values(fs, "{} ", prop.data_u16[cur[idx]++]); break; case ply_type::u32: - if (!format_values(fs, "{} ", prop.data_u32[cur[idx]++])) - return {filename + ": write error"}; + format_values(fs, "{} ", prop.data_u32[cur[idx]++]); break; case ply_type::u64: - if (!format_values(fs, "{} ", prop.data_u64[cur[idx]++])) - return {filename + ": write error"}; + format_values(fs, "{} ", prop.data_u64[cur[idx]++]); break; case ply_type::f32: - if (!format_values(fs, "{} ", prop.data_f32[cur[idx]++])) - return {filename + ": write error"}; + format_values(fs, "{} ", prop.data_f32[cur[idx]++]); break; case ply_type::f64: - if (!format_values(fs, "{} ", prop.data_f64[cur[idx]++])) - return {filename + ": write error"}; + format_values(fs, "{} ", prop.data_f64[cur[idx]++]); break; } } - if (!format_values(fs, "\n")) return {filename + ": write error"}; + format_values(fs, "\n"); } } } @@ -829,51 +848,39 @@ plyio_status save_ply(const string& filename, const ply_model& ply) { for (auto idx = 0; idx < elem.count; idx++) { for (auto pidx = 0; pidx < elem.properties.size(); pidx++) { auto& prop = elem.properties[pidx]; - if (prop.is_list) - if (!write_value(fs, prop.ldata_u8[idx], big_endian)) - return {filename + ": write error"}; + if (prop.is_list) write_value(fs, prop.ldata_u8[idx], big_endian); auto vcount = prop.is_list ? prop.ldata_u8[idx] : 1; for (auto i = 0; i < vcount; i++) { switch (prop.type) { case ply_type::i8: - if (!write_value(fs, prop.data_i8[cur[pidx]++], big_endian)) - return {filename + ": write error"}; + write_value(fs, prop.data_i8[cur[pidx]++], big_endian); break; case ply_type::i16: - if (!write_value(fs, prop.data_i16[cur[pidx]++], big_endian)) - return {filename + ": write error"}; + write_value(fs, prop.data_i16[cur[pidx]++], big_endian); break; case ply_type::i32: - if (!write_value(fs, prop.data_i32[cur[pidx]++], big_endian)) - return {filename + ": write error"}; + write_value(fs, prop.data_i32[cur[pidx]++], big_endian); break; case ply_type::i64: - if (!write_value(fs, prop.data_i64[cur[pidx]++], big_endian)) - return {filename + ": write error"}; + write_value(fs, prop.data_i64[cur[pidx]++], big_endian); break; case ply_type::u8: - if (!write_value(fs, prop.data_u8[cur[pidx]++], big_endian)) - return {filename + ": write error"}; + write_value(fs, prop.data_u8[cur[pidx]++], big_endian); break; case ply_type::u16: - if (!write_value(fs, prop.data_u16[cur[pidx]++], big_endian)) - return {filename + ": write error"}; + write_value(fs, prop.data_u16[cur[pidx]++], big_endian); break; case ply_type::u32: - if (!write_value(fs, prop.data_u32[cur[pidx]++], big_endian)) - return {filename + ": write error"}; + write_value(fs, prop.data_u32[cur[pidx]++], big_endian); break; case ply_type::u64: - if (!write_value(fs, prop.data_u64[cur[pidx]++], big_endian)) - return {filename + ": write error"}; + write_value(fs, prop.data_u64[cur[pidx]++], big_endian); break; case ply_type::f32: - if (!write_value(fs, prop.data_f32[cur[pidx]++], big_endian)) - return {filename + ": write error"}; + write_value(fs, prop.data_f32[cur[pidx]++], big_endian); break; case ply_type::f64: - if (!write_value(fs, prop.data_f64[cur[pidx]++], big_endian)) - return {filename + ": write error"}; + write_value(fs, prop.data_f64[cur[pidx]++], big_endian); break; } } @@ -881,8 +888,6 @@ plyio_status save_ply(const string& filename, const ply_model& ply) { } } } - - return {}; } // Get ply properties @@ -1308,21 +1313,19 @@ void add_ply_points(ply_model& ply, const vector& values) { // get ply value either ascii or binary template -[[nodiscard]] static bool read_ply_prop(FILE* fs, VT& value, bool big_endian) { - auto read_value = [](FILE* fs, auto& value, bool big_endian) -> bool { - auto ok = fread(&value, sizeof(value), 1, fs) == 1; +static void read_ply_prop(FILE* fs, VT& value, bool big_endian) { + auto read_value = [](FILE* fs, auto& value, bool big_endian) -> void { + if (fread(&value, sizeof(value), 1, fs) != 1) + throw std::invalid_argument{"read error"}; if (big_endian) value = swap_endian(value); - return ok; }; auto tvalue = T{}; - auto ok = read_value(fs, tvalue, big_endian); - value = (VT)tvalue; - return ok; + read_value(fs, tvalue, big_endian); + value = (VT)tvalue; } template -[[nodiscard]] static bool read_ply_prop( - FILE* fs, ply_type type, VT& value, bool big_endian) { +static void read_ply_prop(FILE* fs, ply_type type, VT& value, bool big_endian) { switch (type) { case ply_type::i8: return read_ply_prop(fs, value, big_endian); case ply_type::i16: return read_ply_prop(fs, value, big_endian); @@ -1335,18 +1338,16 @@ template case ply_type::f32: return read_ply_prop(fs, value, big_endian); case ply_type::f64: return read_ply_prop(fs, value, big_endian); } - return false; } template -static bool parse_ply_prop(string_view& str, VT& value) { +static void parse_ply_prop(string_view& str, VT& value) { auto tvalue = T{}; - if (!parse_value(str, tvalue)) return false; + parse_value(str, tvalue); value = (VT)tvalue; - return false; } template -static bool parse_ply_prop(string_view& str, ply_type type, VT& value) { +static void parse_ply_prop(string_view& str, ply_type type, VT& value) { switch (type) { case ply_type::i8: return parse_ply_prop(str, value); case ply_type::i16: return parse_ply_prop(str, value); @@ -1359,176 +1360,11 @@ static bool parse_ply_prop(string_view& str, ply_type type, VT& value) { case ply_type::f32: return parse_ply_prop(str, value); case ply_type::f64: return parse_ply_prop(str, value); } - return false; -} - -// Load ply data -plyio_status read_ply_header(const string& filename, FILE* fs, - ply_format& format, vector& elements, - vector& comments) { - // ply type names - static auto type_map = unordered_map{{"char", ply_type::i8}, - {"short", ply_type::i16}, {"int", ply_type::i32}, {"long", ply_type::i64}, - {"uchar", ply_type::u8}, {"ushort", ply_type::u16}, - {"uint", ply_type::u32}, {"ulong", ply_type::u64}, - {"float", ply_type::f32}, {"double", ply_type::f64}, - {"int8", ply_type::i8}, {"int16", ply_type::i16}, - {"int32", ply_type::i32}, {"int64", ply_type::i64}, - {"uint8", ply_type::u8}, {"uint16", ply_type::u16}, - {"uint32", ply_type::u32}, {"uint64", ply_type::u64}, - {"float32", ply_type::f32}, {"float64", ply_type::f64}}; - - // parsing checks - auto first_line = true; - auto end_header = false; - - // prepare elements - elements.clear(); - - // read the file header str by str - char buffer[4096]; - while (fgets(buffer, sizeof(buffer), fs)) { - // str - auto str = string_view{buffer}; - remove_ply_comment(str); - skip_whitespace(str); - if (str.empty()) continue; - - // get command - auto cmd = ""s; - if (!parse_value(str, cmd)) return {filename + ": parse error"}; - if (cmd == "") continue; - - // check magic number - if (first_line) { - if (cmd != "ply") return {filename + ": format not ply"}; - first_line = false; - continue; - } - - // possible token values - if (cmd == "ply") { - if (!first_line) return {filename + ": corrupt header"}; - } else if (cmd == "format") { - auto fmt = string_view{}; - if (!parse_value(str, fmt)) return {filename + ": parse error"}; - if (fmt == "ascii") { - format = ply_format::ascii; - } else if (fmt == "binary_little_endian") { - format = ply_format::binary_little_endian; - } else if (fmt == "binary_big_endian") { - format = ply_format::binary_big_endian; - } else { - return {filename + ": unknown format"}; - } - } else if (cmd == "comment") { - skip_whitespace(str); - comments.push_back(string{str}); - } else if (cmd == "obj_info") { - skip_whitespace(str); - // comment is the rest of the str - } else if (cmd == "element") { - auto& elem = elements.emplace_back(); - if (!parse_value(str, elem.name)) return {filename + ": parse error"}; - if (!parse_value(str, elem.count)) return {filename + ": parse error"}; - } else if (cmd == "property") { - if (elements.empty()) throw std::runtime_error{"bad ply header"}; - auto& prop = elements.back().properties.emplace_back(); - auto tname = ""s; - if (!parse_value(str, tname)) return {filename + ": parse error"}; - if (tname == "list") { - prop.is_list = true; - if (!parse_value(str, tname)) return {filename + ": parse error"}; - if (type_map.find(tname) == type_map.end()) - return {filename + ": unknown type " + tname}; - auto itype = type_map.at(tname); - if (itype != ply_type::u8) - throw std::runtime_error{"unsupported list size type " + tname}; - if (!parse_value(str, tname)) return {filename + ": parse error"}; - if (type_map.find(tname) == type_map.end()) - return {filename + ": unknown type " + tname}; - prop.type = type_map.at(tname); - } else { - prop.is_list = false; - if (type_map.find(tname) == type_map.end()) - return {filename + ": unknown type " + tname}; - prop.type = type_map.at(tname); - } - if (!parse_value(str, prop.name)) return {filename + ": parse error"}; - } else if (cmd == "end_header") { - end_header = true; - break; - } else { - return {filename + ": unknown command " + cmd}; - } - } - - if (!end_header) return {filename + ": incomplete header"}; - - return {}; -} - -template -static plyio_status read_ply_value_generic(const string& filename, FILE* fs, - ply_format format, const ply_element& element, vector& values, - vector>& lists) { - // prepare properties - if (values.size() != element.properties.size()) { - values.resize(element.properties.size()); - } - if (lists.size() != element.properties.size()) { - lists.resize(element.properties.size()); - } - for (auto& list : lists) list.clear(); - - // read property values - if (format == ply_format::ascii) { - char buffer[4096]; - if (!fgets(buffer, sizeof(buffer), fs)) return {filename + ": read error"}; - auto str = string_view{buffer}; - for (auto pidx = 0; pidx < element.properties.size(); pidx++) { - auto& prop = element.properties[pidx]; - auto& value = values[pidx]; - auto& list = lists[pidx]; - if (!prop.is_list) { - if (!parse_ply_prop(str, prop.type, value)) - return {filename + ": parse error"}; - } else { - if (!parse_ply_prop(str, ply_type::u8, value)) - return {filename + ": parse error"}; - list.resize((int)value); - for (auto i = 0; i < (int)value; i++) - if (!parse_ply_prop(str, prop.type, list[i])) - return {filename + ": parse error"}; - } - } - } else { - for (auto pidx = 0; pidx < element.properties.size(); pidx++) { - auto& prop = element.properties[pidx]; - auto& value = values[pidx]; - auto& list = lists[pidx]; - if (!prop.is_list) { - if (!read_ply_prop( - fs, prop.type, value, format == ply_format::binary_big_endian)) - return {filename + ": read error"}; - } else { - if (!read_ply_prop(fs, ply_type::u8, value, - format == ply_format::binary_big_endian)) - return {filename + ": read error"}; - list.resize((int)value); - for (auto i = 0; i < (int)value; i++) - if (!read_ply_prop(fs, prop.type, list[i], - format == ply_format::binary_big_endian)) - return {filename + ": read error"}; - } - } - } - - return {}; + throw std::invalid_argument{"unknown type"}; } template -static bool format_ply_prop(FILE* fs, ply_type type, VT value) { +static void format_ply_prop(FILE* fs, ply_type type, VT value) { switch (type) { case ply_type::i8: return format_value(fs, (int8_t)value); case ply_type::i16: return format_value(fs, (int16_t)value); @@ -1541,187 +1377,7 @@ static bool format_ply_prop(FILE* fs, ply_type type, VT value) { case ply_type::f32: return format_value(fs, (float)value); case ply_type::f64: return format_value(fs, (double)value); } - return false; -} - -template -static bool write_ply_prop(FILE* fs, ply_type type, VT value, bool big_endian) { - auto write_value = [](FILE* fs, auto value_, bool big_endian) -> bool { - auto value = big_endian ? swap_endian(value_) : value_; - return fwrite(&value, sizeof(value), 1, fs) == 1; - }; - - switch (type) { - case ply_type::i8: return write_value(fs, (int8_t)value, big_endian); - case ply_type::i16: return write_value(fs, (int16_t)value, big_endian); - case ply_type::i32: return write_value(fs, (int32_t)value, big_endian); - case ply_type::i64: return write_value(fs, (int64_t)value, big_endian); - case ply_type::u8: return write_value(fs, (uint8_t)value, big_endian); - case ply_type::u16: return write_value(fs, (uint16_t)value, big_endian); - case ply_type::u32: return write_value(fs, (uint32_t)value, big_endian); - case ply_type::u64: return write_value(fs, (uint64_t)value, big_endian); - case ply_type::f32: return write_value(fs, (float)value, big_endian); - case ply_type::f64: return write_value(fs, (double)value, big_endian); - } - return false; -} - -// Write Ply functions -plyio_status write_ply_header(const string& filename, FILE* fs, - ply_format format, const vector& elements, - const vector& comments) { - // ply type names - static auto type_map = unordered_map{{ply_type::i8, "char"}, - {ply_type::i16, "short"}, {ply_type::i32, "int"}, {ply_type::i64, "uint"}, - {ply_type::u8, "uchar"}, {ply_type::u16, "ushort"}, - {ply_type::u32, "uint"}, {ply_type::u64, "ulong"}, - {ply_type::f32, "float"}, {ply_type::f64, "double"}}; - - if (!format_values(fs, "ply\n")) return {filename + ": write error"}; - switch (format) { - case ply_format::ascii: - if (!format_values(fs, "format ascii 1.0\n")) - return {filename + ": write error"}; - break; - case ply_format::binary_little_endian: - if (!format_values(fs, "format binary_little_endian 1.0\n")) - return {filename + ": write error"}; - break; - case ply_format::binary_big_endian: - if (!format_values(fs, "format binary_big_endian 1.0\n")) - return {filename + ": write error"}; - break; - } - for (auto& comment : comments) - if (!format_values(fs, "comment " + comment + "\n")) - return {filename + ": write error"}; - for (auto& elem : elements) { - if (!format_values(fs, - "element " + elem.name + " " + std::to_string(elem.count) + "\n")) - return {filename + ": write error"}; - for (auto& prop : elem.properties) { - if (prop.is_list) { - if (!format_values(fs, "property list uchar " + type_map[prop.type] + - " " + prop.name + "\n")) - return {filename + ": write error"}; - } else { - if (!format_values( - fs, "property " + type_map[prop.type] + " " + prop.name + "\n")) - return {filename + ": write error"}; - } - } - } - if (!format_values(fs, "end_header\n")) return {filename + ": write error"}; - - return {}; -} - -template -static plyio_status write_ply_value_generic(const string& filename, FILE* fs, - ply_format format, const ply_element& element, vector& values, - vector>& lists) { - if (format == ply_format::ascii) { - for (auto pidx = 0; pidx < element.properties.size(); pidx++) { - auto& prop = element.properties[pidx]; - if (pidx) - if (!format_value(fs, " ")) return {filename + ": write error"}; - if (!prop.is_list) { - if (!format_ply_prop(fs, prop.type, values[pidx])) - return {filename + ": write error"}; - } else { - if (!format_ply_prop(fs, ply_type::u8, values[pidx])) - return {filename + ": write error"}; - for (auto i = 0; i < (int)lists[pidx].size(); i++) { - if (i) - if (!format_value(fs, " ")) return {filename + ": write error"}; - if (!format_ply_prop(fs, prop.type, lists[pidx][i])) - return {filename + ": write error"}; - } - } - if (!format_value(fs, "\n")) return {filename + ": write error"}; - } - } else { - for (auto pidx = 0; pidx < element.properties.size(); pidx++) { - auto& prop = element.properties[pidx]; - if (!prop.is_list) { - if (!write_ply_prop(fs, prop.type, values[pidx], - format == ply_format::binary_big_endian)) - return {filename + ": write error"}; - } else { - if (!write_ply_prop(fs, ply_type::u8, values[pidx], - format == ply_format::binary_big_endian)) - return {filename + ": write error"}; - for (auto i = 0; i < (int)lists[pidx].size(); i++) - if (!write_ply_prop(fs, prop.type, lists[pidx][i], - format == ply_format::binary_big_endian)) - return {filename + ": write error"}; - } - } - } - return {}; -} - -plyio_status write_ply_value(const string& filename, FILE* fs, - ply_format format, const ply_element& element, vector& values, - vector>& lists) { - return write_ply_value_generic(filename, fs, format, element, values, lists); -} -plyio_status write_ply_value(const string& filename, FILE* fs, - ply_format format, const ply_element& element, vector& values, - vector>& lists) { - return write_ply_value_generic(filename, fs, format, element, values, lists); -} - -plyio_status read_ply_value(const string& filename, FILE* fs, ply_format format, - const ply_element& element, vector& values, - vector>& lists) { - return read_ply_value_generic(filename, fs, format, element, values, lists); -} -plyio_status read_ply_value(const string& filename, FILE* fs, ply_format format, - const ply_element& element, vector& values, - vector>& lists) { - return read_ply_value_generic(filename, fs, format, element, values, lists); -} - -int find_ply_element(const vector& elements, const string& name) { - for (auto idx = 0; idx < elements.size(); idx++) - if (elements[idx].name == name) return idx; - return -1; -} -int find_ply_property(const ply_element& element, const string& name) { - for (auto idx = 0; idx < element.properties.size(); idx++) - if (element.properties[idx].name == name) return idx; - return -1; -} -vec2i find_ply_property( - const ply_element& element, const string& name1, const string& name2) { - auto ids = vec2i{ - find_ply_property(element, name1), - find_ply_property(element, name2), - }; - if (ids.x < 0 || ids.y < 0) return vec2i{-1}; - return ids; -} -vec3i find_ply_property(const ply_element& element, const string& name1, - const string& name2, const string& name3) { - auto ids = vec3i{ - find_ply_property(element, name1), - find_ply_property(element, name2), - find_ply_property(element, name3), - }; - if (ids.x < 0 || ids.y < 0 || ids.z < 0) return vec3i{-1}; - return ids; -} -vec4i find_ply_property(const ply_element& element, const string& name1, - const string& name2, const string& name3, const string& name4) { - auto ids = vec4i{ - find_ply_property(element, name1), - find_ply_property(element, name2), - find_ply_property(element, name3), - find_ply_property(element, name4), - }; - if (ids.x < 0 || ids.y < 0 || ids.z < 0 || ids.w < 0) return vec4i{-1}; - return ids; + throw std::invalid_argument{"unknown type"}; } } // namespace yocto @@ -1738,27 +1394,26 @@ static void remove_obj_comment(string_view& str, char comment_char = '#') { str.remove_suffix(cpy.size()); } -static bool parse_value(string_view& str, obj_vertex& value) { +static void parse_value(string_view& str, obj_vertex& value) { value = obj_vertex{0, 0, 0}; - if (!parse_value(str, value.position)) return false; + parse_value(str, value.position); if (!str.empty() && str.front() == '/') { str.remove_prefix(1); if (!str.empty() && str.front() == '/') { str.remove_prefix(1); - if (!parse_value(str, value.normal)) return false; + parse_value(str, value.normal); } else { - if (!parse_value(str, value.texcoord)) return false; + parse_value(str, value.texcoord); if (!str.empty() && str.front() == '/') { str.remove_prefix(1); - if (!parse_value(str, value.normal)) return false; + parse_value(str, value.normal); } } } - return true; } // Input for OBJ textures -static bool parse_value(string_view& str, obj_texture_info& info) { +static void parse_value(string_view& str, obj_texture_info& info) { // initialize info = obj_texture_info(); @@ -1767,11 +1422,11 @@ static bool parse_value(string_view& str, obj_texture_info& info) { skip_whitespace(str); while (!str.empty()) { auto token = ""s; - if (!parse_value(str, token)) return false; + parse_value(str, token); tokens.push_back(token); skip_whitespace(str); } - if (tokens.empty()) return false; + if (tokens.empty()) throw std::invalid_argument{"empty name"}; // texture name info.path = normalize_path(tokens.back()); @@ -1782,24 +1437,20 @@ static bool parse_value(string_view& str, obj_texture_info& info) { if (tokens[i] == "-bm") info.scale = atof(tokens[i + 1].c_str()); if (tokens[i] == "-clamp") info.clamp = true; } - - return true; } // Read obj -static objio_status load_mtl( +static void load_mtl( const string& filename, obj_model& obj, bool fliptr = true) { // open file - auto fs = fopen(filename.c_str(), "rt"); - if (!fs) return {filename + ": file not found"}; - auto fs_guard = std::unique_ptr{fs, fclose}; + auto fs = open_file(filename, "rt"); // init parsing obj.materials.emplace_back(); // read the file str by str char buffer[4096]; - while (fgets(buffer, sizeof(buffer), fs)) { + while (read_line(fs, buffer, sizeof(buffer))) { // str auto str = string_view{buffer}; remove_obj_comment(str); @@ -1808,36 +1459,28 @@ static objio_status load_mtl( // get command auto cmd = ""s; - if (!parse_value(str, cmd)) return {filename + ": parse error"}; + parse_value(fs, str, cmd); if (cmd == "") continue; // possible token values if (cmd == "newmtl") { obj.materials.emplace_back(); - if (!parse_value(str, obj.materials.back().name)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().name); } else if (cmd == "illum") { - if (!parse_value(str, obj.materials.back().illum)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().illum); } else if (cmd == "Ke") { - if (!parse_value(str, obj.materials.back().emission)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().emission); } else if (cmd == "Ka") { - if (!parse_value(str, obj.materials.back().ambient)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().ambient); } else if (cmd == "Kd") { - if (!parse_value(str, obj.materials.back().diffuse)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().diffuse); } else if (cmd == "Ks") { - if (!parse_value(str, obj.materials.back().specular)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().specular); } else if (cmd == "Kt") { - if (!parse_value(str, obj.materials.back().transmission)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().transmission); } else if (cmd == "Tf") { obj.materials.back().transmission = vec3f{-1}; - if (!parse_value(str, obj.materials.back().transmission)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().transmission); if (obj.materials.back().transmission.y < 0) obj.materials.back().transmission = vec3f{ obj.materials.back().transmission.x}; @@ -1845,114 +1488,78 @@ static objio_status load_mtl( obj.materials.back().transmission = 1 - obj.materials.back().transmission; } else if (cmd == "Tr") { - if (!parse_value(str, obj.materials.back().opacity)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().opacity); if (fliptr) obj.materials.back().opacity = 1 - obj.materials.back().opacity; } else if (cmd == "Ns") { - if (!parse_value(str, obj.materials.back().exponent)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().exponent); } else if (cmd == "d") { - if (!parse_value(str, obj.materials.back().opacity)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().opacity); } else if (cmd == "map_Ke") { - if (!parse_value(str, obj.materials.back().emission_map)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().emission_map); } else if (cmd == "map_Ka") { - if (!parse_value(str, obj.materials.back().ambient_map)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().ambient_map); } else if (cmd == "map_Kd") { - if (!parse_value(str, obj.materials.back().diffuse_map)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().diffuse_map); } else if (cmd == "map_Ks") { - if (!parse_value(str, obj.materials.back().specular_map)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().specular_map); } else if (cmd == "map_Tr") { - if (!parse_value(str, obj.materials.back().transmission_map)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().transmission_map); } else if (cmd == "map_d" || cmd == "map_Tr") { - if (!parse_value(str, obj.materials.back().opacity_map)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().opacity_map); } else if (cmd == "map_bump" || cmd == "bump") { - if (!parse_value(str, obj.materials.back().bump_map)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().bump_map); } else if (cmd == "map_disp" || cmd == "disp") { - if (!parse_value(str, obj.materials.back().displacement_map)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().displacement_map); } else if (cmd == "map_norm" || cmd == "norm") { - if (!parse_value(str, obj.materials.back().normal_map)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().normal_map); } else if (cmd == "Pm") { - if (!parse_value(str, obj.materials.back().pbr_metallic)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().pbr_metallic); } else if (cmd == "Pr") { - if (!parse_value(str, obj.materials.back().pbr_roughness)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().pbr_roughness); } else if (cmd == "Ps") { - if (!parse_value(str, obj.materials.back().pbr_sheen)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().pbr_sheen); } else if (cmd == "Pc") { - if (!parse_value(str, obj.materials.back().pbr_clearcoat)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().pbr_clearcoat); } else if (cmd == "Pcr") { - if (!parse_value(str, obj.materials.back().pbr_coatroughness)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().pbr_coatroughness); } else if (cmd == "map_Pm") { - if (!parse_value(str, obj.materials.back().pbr_metallic_map)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().pbr_metallic_map); } else if (cmd == "map_Pr") { - if (!parse_value(str, obj.materials.back().pbr_roughness_map)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().pbr_roughness_map); } else if (cmd == "map_Ps") { - if (!parse_value(str, obj.materials.back().pbr_sheen_map)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().pbr_sheen_map); } else if (cmd == "map_Pc") { - if (!parse_value(str, obj.materials.back().pbr_clearcoat_map)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().pbr_clearcoat_map); } else if (cmd == "map_Pcr") { - if (!parse_value(str, obj.materials.back().pbr_coatroughness_map)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().pbr_coatroughness_map); } else if (cmd == "Vt") { - if (!parse_value(str, obj.materials.back().vol_transmission)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().vol_transmission); } else if (cmd == "Vp") { - if (!parse_value(str, obj.materials.back().vol_meanfreepath)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().vol_meanfreepath); } else if (cmd == "Ve") { - if (!parse_value(str, obj.materials.back().vol_emission)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().vol_emission); } else if (cmd == "Vs") { - if (!parse_value(str, obj.materials.back().vol_scattering)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().vol_scattering); } else if (cmd == "Vg") { - if (!parse_value(str, obj.materials.back().vol_anisotropy)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().vol_anisotropy); } else if (cmd == "Vr") { - if (!parse_value(str, obj.materials.back().vol_scale)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().vol_scale); } else if (cmd == "map_Vs") { - if (!parse_value(str, obj.materials.back().vol_scattering_map)) - return {filename + ": parse error"}; + parse_value(fs, str, obj.materials.back().vol_scattering_map); } else { continue; } } - // check error - if (ferror(fs)) return {filename + ": read error"}; - // remove placeholder material obj.materials.erase(obj.materials.begin()); - - return {}; } // Read obj -static objio_status load_objx(const string& filename, obj_model& obj) { +static void load_objx(const string& filename, obj_model& obj) { // open file - auto fs = fopen(filename.c_str(), "rt"); - if (!fs) return {filename + ": file not found"}; - auto fs_guard = std::unique_ptr{fs, fclose}; + auto fs = open_file(filename, "rt"); // shape map for instances auto shape_map = unordered_map>{}; @@ -1962,7 +1569,7 @@ static objio_status load_objx(const string& filename, obj_model& obj) { // read the file str by str char buffer[4096]; - while (fgets(buffer, sizeof(buffer), fs)) { + while (read_line(fs, buffer, sizeof(buffer))) { // str auto str = string_view{buffer}; remove_obj_comment(str); @@ -1971,40 +1578,36 @@ static objio_status load_objx(const string& filename, obj_model& obj) { // get command auto cmd = ""s; - if (!parse_value(str, cmd)) return {filename + ": parse error"}; + parse_value(fs, str, cmd); if (cmd == "") continue; // read values if (cmd == "c") { auto& camera = obj.cameras.emplace_back(); - if (!parse_value(str, camera.name)) return {filename + ": parse error"}; - if (!parse_value(str, camera.ortho)) return {filename + ": parse error"}; - if (!parse_value(str, camera.width)) return {filename + ": parse error"}; - if (!parse_value(str, camera.height)) return {filename + ": parse error"}; - if (!parse_value(str, camera.lens)) return {filename + ": parse error"}; - if (!parse_value(str, camera.focus)) return {filename + ": parse error"}; - if (!parse_value(str, camera.aperture)) - return {filename + ": parse error"}; - if (!parse_value(str, camera.frame)) return {filename + ": parse error"}; + parse_value(fs, str, camera.name); + parse_value(fs, str, camera.ortho); + parse_value(fs, str, camera.width); + parse_value(fs, str, camera.height); + parse_value(fs, str, camera.lens); + parse_value(fs, str, camera.focus); + parse_value(fs, str, camera.aperture); + parse_value(fs, str, camera.frame); } else if (cmd == "e") { auto& environment = obj.environments.emplace_back(); - if (!parse_value(str, environment.name)) - return {filename + ": parse error"}; - if (!parse_value(str, environment.emission)) - return {filename + ": parse error"}; + parse_value(fs, str, environment.name); + parse_value(fs, str, environment.emission); auto emission_path = ""s; - if (!parse_value(str, emission_path)) return {filename + ": parse error"}; + parse_value(fs, str, emission_path); if (emission_path == "\"\"") emission_path = ""; environment.emission_map.path = emission_path; - if (!parse_value(str, environment.frame)) - return {filename + ": parse error"}; + parse_value(fs, str, environment.frame); } else if (cmd == "i") { auto object = ""s; auto frame = identity3x4f; - if (!parse_value(str, object)) return {filename + ": parse error"}; - if (!parse_value(str, frame)) return {filename + ": parse error"}; + parse_value(fs, str, object); + parse_value(fs, str, frame); if (shape_map.find(object) == shape_map.end()) { - return {filename + ": missing object " + object}; + throw_parse_error(fs, "unknown object " + object); } for (auto idx : shape_map.at(object)) { obj.shapes[idx].instances.push_back(frame); @@ -2013,20 +1616,13 @@ static objio_status load_objx(const string& filename, obj_model& obj) { // unused } } - - // check error - if (ferror(fs)) return {filename + "read error"}; - - return {}; } // Read obj -objio_status load_obj(const string& filename, obj_model& obj, bool geom_only, +void load_obj(const string& filename, obj_model& obj, bool geom_only, bool split_elements, bool split_materials) { // open file - auto fs = fopen(filename.c_str(), "rt"); - if (!fs) return {filename + ": file not found"}; - auto fs_guard = std::unique_ptr{fs, fclose}; + auto fs = open_file(filename, "rt"); // parsing state auto opositions = vector{}; @@ -2044,7 +1640,7 @@ objio_status load_obj(const string& filename, obj_model& obj, bool geom_only, // read the file str by str char buffer[4096]; - while (fgets(buffer, sizeof(buffer), fs)) { + while (read_line(fs, buffer, sizeof(buffer))) { // str auto str = string_view{buffer}; remove_obj_comment(str); @@ -2053,21 +1649,18 @@ objio_status load_obj(const string& filename, obj_model& obj, bool geom_only, // get command auto cmd = ""s; - if (!parse_value(str, cmd)) return {filename + ": parse error"}; + parse_value(fs, str, cmd); if (cmd == "") continue; // possible token values if (cmd == "v") { - if (!parse_value(str, opositions.emplace_back())) - return {filename + ": parse error"}; + parse_value(fs, str, opositions.emplace_back()); vert_size.position += 1; } else if (cmd == "vn") { - if (!parse_value(str, onormals.emplace_back())) - return {filename + ": parse error"}; + parse_value(fs, str, onormals.emplace_back()); vert_size.normal += 1; } else if (cmd == "vt") { - if (!parse_value(str, otexcoords.emplace_back())) - return {filename + ": parse error"}; + parse_value(fs, str, otexcoords.emplace_back()); vert_size.texcoord += 1; } else if (cmd == "f" || cmd == "l" || cmd == "p") { // split if split_elements and different primitives @@ -2110,7 +1703,7 @@ objio_status load_obj(const string& filename, obj_model& obj, bool geom_only, skip_whitespace(str); while (!str.empty()) { auto vert = obj_vertex{}; - if (!parse_value(str, vert)) return {filename + ": parse error"}; + parse_value(fs, str, vert); if (!vert.position) break; if (vert.position < 0) vert.position = vert_size.position + vert.position + 1; @@ -2123,8 +1716,7 @@ objio_status load_obj(const string& filename, obj_model& obj, bool geom_only, } } else if (cmd == "o" || cmd == "g") { if (geom_only) continue; - if (!parse_value_or_empty(str, cmd == "o" ? oname : gname)) - return {filename + ": parse error"}; + parse_value_or_empty(str, cmd == "o" ? oname : gname); if (!obj.shapes.back().vertices.empty()) { obj.shapes.emplace_back(); obj.shapes.back().name = oname + gname; @@ -2133,14 +1725,13 @@ objio_status load_obj(const string& filename, obj_model& obj, bool geom_only, } } else if (cmd == "usemtl") { if (geom_only) continue; - if (!parse_value_or_empty(str, mname)) - return {filename + ": parse error"}; + parse_value_or_empty(str, mname); } else if (cmd == "s") { if (geom_only) continue; } else if (cmd == "mtllib") { if (geom_only) continue; auto mtllib = ""s; - if (!parse_value(str, mtllib)) return {filename + ": parse error"}; + parse_value(fs, str, mtllib); if (std::find(mtllibs.begin(), mtllibs.end(), mtllib) == mtllibs.end()) { mtllibs.push_back(mtllib); } @@ -2149,9 +1740,6 @@ objio_status load_obj(const string& filename, obj_model& obj, bool geom_only, } } - // check error - if (ferror(fs)) return {filename + ": read error"}; - // convert vertex data auto ipositions = vector{}; auto inormals = vector{}; @@ -2180,23 +1768,27 @@ objio_status load_obj(const string& filename, obj_model& obj, bool geom_only, } // exit if done - if (geom_only) return {}; + if (geom_only) return; // load materials auto dirname = get_dirname(filename); for (auto& mtllib : mtllibs) { - if (auto ret = load_mtl(dirname + mtllib, obj); !ret) - return {filename + ": mtl error (" + ret.error + ")"}; + try { + load_mtl(dirname + mtllib, obj); + } catch (std::exception& e) { + throw_dependent_error(fs, e.what()); + } } // load extensions auto extfilename = replace_extension(filename, ".objx"); if (exists_file(extfilename)) { - if (auto ret = load_objx(extfilename, obj); !ret) - return {filename + ": objx error (" + ret.error + ")"}; + try { + load_objx(extfilename, obj); + } catch (std::exception& e) { + throw_dependent_error(fs, e.what()); + } } - - return {}; } // Format values @@ -2219,157 +1811,106 @@ static void format_value(string& str, const obj_vertex& value) { } // Save obj -static objio_status save_mtl(const string& filename, const obj_model& obj) { +static void save_mtl(const string& filename, const obj_model& obj) { // open file - auto fs = fopen(filename.c_str(), "wt"); - if (!fs) return {filename + ": file not found"}; - auto fs_guard = std::unique_ptr{fs, fclose}; + auto fs = open_file(filename, "wt"); // save comments - if (!format_values(fs, "#\n")) return {filename + ": write error"}; - if (!format_values(fs, "# Written by Yocto/GL\n")) - return {filename + ": write error"}; - if (!format_values(fs, "# https://github.com/xelatihy/yocto-gl\n")) - return {filename + ": write error"}; - if (!format_values(fs, "#\n\n")) return {filename + ": write error"}; + format_values(fs, "#\n"); + format_values(fs, "# Written by Yocto/GL\n"); + format_values(fs, "# https://github.com/xelatihy/yocto-gl\n"); + format_values(fs, "#\n\n"); for (auto& comment : obj.comments) { - if (!format_values(fs, "# {}\n", comment)) - return {filename + ": write error"}; + format_values(fs, "# {}\n", comment); } - if (!format_values(fs, "\n")) return {filename + ": write error"}; + format_values(fs, "\n"); // write material for (auto& material : obj.materials) { - if (!format_values(fs, "newmtl {}\n", material.name)) - return {filename + ": write error"}; - if (!format_values(fs, "illum {}\n", material.illum)) - return {filename + ": write error"}; + format_values(fs, "newmtl {}\n", material.name); + format_values(fs, "illum {}\n", material.illum); if (material.emission != zero3f) - if (!format_values(fs, "Ke {}\n", material.emission)) - return {filename + ": write error"}; + format_values(fs, "Ke {}\n", material.emission); if (material.ambient != zero3f) - if (!format_values(fs, "Ka {}\n", material.ambient)) - return {filename + ": write error"}; - if (!format_values(fs, "Kd {}\n", material.diffuse)) - return {filename + ": write error"}; - if (!format_values(fs, "Ks {}\n", material.specular)) - return {filename + ": write error"}; + format_values(fs, "Ka {}\n", material.ambient); + format_values(fs, "Kd {}\n", material.diffuse); + format_values(fs, "Ks {}\n", material.specular); if (material.reflection != zero3f) - if (!format_values(fs, "Kr {}\n", material.reflection)) - return {filename + ": write error"}; + format_values(fs, "Kr {}\n", material.reflection); if (material.transmission != zero3f) - if (!format_values(fs, "Kt {}\n", material.transmission)) - return {filename + ": write error"}; + format_values(fs, "Kt {}\n", material.transmission); format_values(fs, "Ns {}\n", (int)material.exponent); - if (material.opacity != 1) - if (!format_values(fs, "d {}\n", material.opacity)) - return {filename + ": write error"}; + if (material.opacity != 1) format_values(fs, "d {}\n", material.opacity); if (!material.emission_map.path.empty()) - if (!format_values(fs, "map_Ke {}\n", material.emission_map)) - return {filename + ": write error"}; + format_values(fs, "map_Ke {}\n", material.emission_map); if (!material.diffuse_map.path.empty()) - if (!format_values(fs, "map_Kd {}\n", material.diffuse_map)) - return {filename + ": write error"}; + format_values(fs, "map_Kd {}\n", material.diffuse_map); if (!material.specular_map.path.empty()) - if (!format_values(fs, "map_Ks {}\n", material.specular_map)) - return {filename + ": write error"}; + format_values(fs, "map_Ks {}\n", material.specular_map); if (!material.transmission_map.path.empty()) - if (!format_values(fs, "map_Kt {}\n", material.transmission_map)) - return {filename + ": write error"}; + format_values(fs, "map_Kt {}\n", material.transmission_map); if (!material.reflection_map.path.empty()) - if (!format_values(fs, "map_Kr {}\n", material.reflection_map)) - return {filename + ": write error"}; + format_values(fs, "map_Kr {}\n", material.reflection_map); if (!material.exponent_map.path.empty()) - if (!format_values(fs, "map_Ns {}\n", material.exponent_map)) - return {filename + ": write error"}; + format_values(fs, "map_Ns {}\n", material.exponent_map); if (!material.opacity_map.path.empty()) - if (!format_values(fs, "map_d {}\n", material.opacity_map)) - return {filename + ": write error"}; + format_values(fs, "map_d {}\n", material.opacity_map); if (!material.bump_map.path.empty()) - if (!format_values(fs, "map_bump {}\n", material.bump_map)) - return {filename + ": write error"}; + format_values(fs, "map_bump {}\n", material.bump_map); if (!material.displacement_map.path.empty()) - if (!format_values(fs, "map_disp {}\n", material.displacement_map)) - return {filename + ": write error"}; + format_values(fs, "map_disp {}\n", material.displacement_map); if (!material.normal_map.path.empty()) - if (!format_values(fs, "map_norm {}\n", material.normal_map)) - return {filename + ": write error"}; + format_values(fs, "map_norm {}\n", material.normal_map); if (material.pbr_roughness) - if (!format_values(fs, "Pr {}\n", material.pbr_roughness)) - return {filename + ": write error"}; + format_values(fs, "Pr {}\n", material.pbr_roughness); if (material.pbr_metallic) - if (!format_values(fs, "Pm {}\n", material.pbr_metallic)) - return {filename + ": write error"}; - if (material.pbr_sheen) - if (!format_values(fs, "Ps {}\n", material.pbr_sheen)) - return {filename + ": write error"}; + format_values(fs, "Pm {}\n", material.pbr_metallic); + if (material.pbr_sheen) format_values(fs, "Ps {}\n", material.pbr_sheen); if (material.pbr_clearcoat) - if (!format_values(fs, "Pc {}\n", material.pbr_clearcoat)) - return {filename + ": write error"}; + format_values(fs, "Pc {}\n", material.pbr_clearcoat); if (material.pbr_coatroughness) - if (!format_values(fs, "Pcr {}\n", material.pbr_coatroughness)) - return {filename + ": write error"}; + format_values(fs, "Pcr {}\n", material.pbr_coatroughness); if (!material.pbr_roughness_map.path.empty()) - if (!format_values(fs, "map_Pr {}\n", material.pbr_roughness_map)) - return {filename + ": write error"}; + format_values(fs, "map_Pr {}\n", material.pbr_roughness_map); if (!material.pbr_metallic_map.path.empty()) - if (!format_values(fs, "map_Pm {}\n", material.pbr_metallic_map)) - return {filename + ": write error"}; + format_values(fs, "map_Pm {}\n", material.pbr_metallic_map); if (!material.pbr_sheen_map.path.empty()) - if (!format_values(fs, "map_Ps {}\n", material.pbr_sheen_map)) - return {filename + ": write error"}; + format_values(fs, "map_Ps {}\n", material.pbr_sheen_map); if (!material.pbr_clearcoat_map.path.empty()) - if (!format_values(fs, "map_Pc {}\n", material.pbr_clearcoat_map)) - return {filename + ": write error"}; + format_values(fs, "map_Pc {}\n", material.pbr_clearcoat_map); if (!material.pbr_coatroughness_map.path.empty()) - if (!format_values(fs, "map_Pcr {}\n", material.pbr_coatroughness_map)) - return {filename + ": write error"}; + format_values(fs, "map_Pcr {}\n", material.pbr_coatroughness_map); if (material.vol_transmission != zero3f) - if (!format_values(fs, "Vt {}\n", material.vol_transmission)) - return {filename + ": write error"}; + format_values(fs, "Vt {}\n", material.vol_transmission); if (material.vol_meanfreepath != zero3f) - if (!format_values(fs, "Vp {}\n", material.vol_meanfreepath)) - return {filename + ": write error"}; + format_values(fs, "Vp {}\n", material.vol_meanfreepath); if (material.vol_emission != zero3f) - if (!format_values(fs, "Ve {}\n", material.vol_emission)) - return {filename + ": write error"}; + format_values(fs, "Ve {}\n", material.vol_emission); if (material.vol_scattering != zero3f) - if (!format_values(fs, "Vs {}\n", material.vol_scattering)) - return {filename + ": write error"}; + format_values(fs, "Vs {}\n", material.vol_scattering); if (material.vol_anisotropy) - if (!format_values(fs, "Vg {}\n", material.vol_anisotropy)) - return {filename + ": write error"}; - if (material.vol_scale) - if (!format_values(fs, "Vr {}\n", material.vol_scale)) - return {filename + ": write error"}; + format_values(fs, "Vg {}\n", material.vol_anisotropy); + if (material.vol_scale) format_values(fs, "Vr {}\n", material.vol_scale); if (!material.vol_scattering_map.path.empty()) - if (!format_values(fs, "map_Vs {}\n", material.vol_scattering_map)) - return {filename + ": write error"}; - if (!format_values(fs, "\n")) return {filename + ": write error"}; + format_values(fs, "map_Vs {}\n", material.vol_scattering_map); + format_values(fs, "\n"); } - - return {}; } // Save obj -static objio_status save_objx(const string& filename, const obj_model& obj) { +static void save_objx(const string& filename, const obj_model& obj) { // open file - auto fs = fopen(filename.c_str(), "wt"); - if (!fs) return {filename + ": file not found"}; - auto fs_guard = std::unique_ptr{fs, fclose}; + auto fs = open_file(filename, "wt"); // save comments - if (!format_values(fs, "#\n")) return {filename + ": write error"}; - if (!format_values(fs, "# Written by Yocto/GL\n")) - return {filename + ": write error"}; - if (!format_values(fs, "# https://github.com/xelatihy/yocto-gl\n")) - return {filename + ": write error"}; - if (!format_values(fs, "#\n\n")) return {filename + ": write error"}; + format_values(fs, "#\n"); + format_values(fs, "# Written by Yocto/GL\n"); + format_values(fs, "# https://github.com/xelatihy/yocto-gl\n"); + format_values(fs, "#\n\n"); for (auto& comment : obj.comments) { - if (!format_values(fs, "# {}\n", comment)) - return {filename + ": write error"}; + format_values(fs, "# {}\n", comment); } - if (!format_values(fs, "\n")) return {filename + ": write error"}; + format_values(fs, "\n"); // cameras for (auto& camera : obj.cameras) { @@ -2392,29 +1933,22 @@ static objio_status save_objx(const string& filename, const obj_model& obj) { format_values(fs, "i {} {}\n", shape.name, frame); } } - - return {}; } // Save obj -objio_status save_obj(const string& filename, const obj_model& obj) { +void save_obj(const string& filename, const obj_model& obj) { // open file - auto fs = fopen(filename.c_str(), "wt"); - if (!fs) return {filename + ": file not found"}; - auto fs_guard = std::unique_ptr{fs, fclose}; + auto fs = open_file(filename, "wt"); // save comments - if (!format_values(fs, "#\n")) return {filename + ": write error"}; - if (!format_values(fs, "# Written by Yocto/GL\n")) - return {filename + ": write error"}; - if (!format_values(fs, "# https://github.com/xelatihy/yocto-gl\n")) - return {filename + ": write error"}; - if (!format_values(fs, "#\n\n")) return {filename + ": write error"}; + format_values(fs, "#\n"); + format_values(fs, "# Written by Yocto/GL\n"); + format_values(fs, "# https://github.com/xelatihy/yocto-gl\n"); + format_values(fs, "#\n\n"); for (auto& comment : obj.comments) { - if (!format_values(fs, "# {}\n", comment)) - return {filename + ": write error"}; + format_values(fs, "# {}\n", comment); } - if (!format_values(fs, "\n")) return {filename + ": write error"}; + format_values(fs, "\n"); // save material library if (!obj.materials.empty()) { @@ -2425,14 +1959,10 @@ objio_status save_obj(const string& filename, const obj_model& obj) { // save objects auto vert_size = obj_vertex{0, 0, 0}; for (auto& shape : obj.shapes) { - if (!format_values(fs, "o {}\n", shape.name)) - return {filename + ": write error"}; - for (auto& p : shape.positions) - if (!format_values(fs, "v {}\n", p)) return {filename + ": write error"}; - for (auto& n : shape.normals) - if (!format_values(fs, "vn {}\n", n)) return {filename + ": write error"}; - for (auto& t : shape.texcoords) - if (!format_values(fs, "vt {}\n", t)) return {filename + ": write error"}; + format_values(fs, "o {}\n", shape.name); + for (auto& p : shape.positions) format_values(fs, "v {}\n", p); + for (auto& n : shape.normals) format_values(fs, "vn {}\n", n); + for (auto& t : shape.texcoords) format_values(fs, "vt {}\n", t); auto element_labels = vector{"f", "l", "p"}; auto element_groups = vector*>{ &shape.faces, &shape.lines, &shape.points}; @@ -2445,20 +1975,18 @@ objio_status save_obj(const string& filename, const obj_model& obj) { format_values(fs, "usemtl {}\n", shape.materials[element.material]); cur_material = element.material; } - if (!format_values(fs, "{}", label)) - return {filename + ": write error"}; + format_values(fs, "{}", label); for (auto c = 0; c < element.size; c++) { auto vert = shape.vertices[cur_vertex++]; if (vert.position) vert.position += vert_size.position; if (vert.normal) vert.normal += vert_size.normal; if (vert.texcoord) vert.texcoord += vert_size.texcoord; - if (!format_values(fs, " {}", vert)) - return {filename + ": write error"}; + format_values(fs, " {}", vert); } - if (!format_values(fs, "\n")) return {filename + ": write error"}; + format_values(fs, "\n"); } } - if (!format_values(fs, "\n")) return {filename + ": write error"}; + format_values(fs, "\n"); vert_size.position += (int)shape.positions.size(); vert_size.normal += (int)shape.normals.size(); vert_size.texcoord += (int)shape.texcoords.size(); @@ -2466,19 +1994,15 @@ objio_status save_obj(const string& filename, const obj_model& obj) { // save mtl if (!obj.materials.empty()) { - if (auto ret = save_mtl(replace_extension(filename, ".mtl"), obj); !ret) - return ret; + save_mtl(replace_extension(filename, ".mtl"), obj); } // save objx if (!obj.cameras.empty() || !obj.environments.empty() || std::any_of(obj.shapes.begin(), obj.shapes.end(), [](auto& shape) { return !shape.instances.empty(); })) { - if (auto ret = save_objx(replace_extension(filename, ".objx"), obj); !ret) - return ret; + save_objx(replace_extension(filename, ".objx"), obj); } - - return {}; } // convert between roughness and exponent @@ -2800,539 +2324,6 @@ void add_obj_fvquads(obj_model& obj, const string& name, shape.faces.push_back({(uint8_t)nv, ematerials.empty() ? (uint8_t)0 : (uint8_t)ematerials[idx]}); } -} // namespace yocto - -// Read obj -objio_status read_obj_command(const string& filename, FILE* fs, - obj_command& command, string& name, vec3f& value, - vector& vertices, obj_vertex& vert_size) { - // read the file str by str - char buffer[4096]; - while (fgets(buffer, sizeof(buffer), fs)) { - // str - auto str = string_view{buffer}; - remove_obj_comment(str); - skip_whitespace(str); - if (str.empty()) continue; - - // get command - auto cmd = ""s; - if (!parse_value(str, cmd)) return {filename + ": parse error"}; - if (cmd == "") continue; - - // possible token values - if (cmd == "v") { - command = obj_command::vertex; - if (!parse_value(str, value)) return {filename + ": parse error"}; - vert_size.position += 1; - return {}; - } else if (cmd == "vn") { - command = obj_command::normal; - if (!parse_value(str, value)) return {filename + ": parse error"}; - vert_size.normal += 1; - return {}; - } else if (cmd == "vt") { - command = obj_command::texcoord; - if (!parse_value(str, (vec2f&)value)) return {filename + ": parse error"}; - value.z = 0; - vert_size.texcoord += 1; - return {}; - } else if (cmd == "f" || cmd == "l" || cmd == "p") { - vertices.clear(); - skip_whitespace(str); - while (!str.empty()) { - auto vert = obj_vertex{}; - if (!parse_value(str, vert)) return {filename + ": parse error"}; - if (!vert.position) break; - if (vert.position < 0) - vert.position = vert_size.position + vert.position + 1; - if (vert.texcoord < 0) - vert.texcoord = vert_size.texcoord + vert.texcoord + 1; - if (vert.normal < 0) vert.normal = vert_size.normal + vert.normal + 1; - vertices.push_back(vert); - skip_whitespace(str); - } - if (cmd == "f") command = obj_command::face; - if (cmd == "l") command = obj_command::str; - if (cmd == "p") command = obj_command::point; - return {}; - } else if (cmd == "o") { - command = obj_command::object; - if (!parse_value_or_empty(str, name)) return {filename + ": parse error"}; - return {}; - } else if (cmd == "usemtl") { - command = obj_command::usemtl; - if (!parse_value_or_empty(str, name)) return {filename + ": parse error"}; - return {}; - } else if (cmd == "g") { - command = obj_command::group; - if (!parse_value_or_empty(str, name)) return {filename + ": parse error"}; - return {}; - } else if (cmd == "s") { - command = obj_command::smoothing; - if (!parse_value_or_empty(str, name)) return {filename + ": parse error"}; - return {}; - } else if (cmd == "mtllib") { - command = obj_command::mtllib; - if (!parse_value(str, name)) return {filename + ": parse error"}; - return {}; - } else { - // unused - } - } - - // check error - if (ferror(fs)) return {filename + ": read error"}; - - return {"eof"}; -} - -// Read mtl -objio_status read_mtl_command(const string& filename, FILE* fs, - mtl_command& command, obj_material& material, bool fliptr) { - material = {}; - - // read the file str by str - auto pos = ftell(fs); - auto found = false; - char buffer[4096]; - while (fgets(buffer, sizeof(buffer), fs)) { - // str - auto str = string_view{buffer}; - remove_obj_comment(str); - skip_whitespace(str); - if (str.empty()) continue; - - // get command - auto cmd = ""s; - if (!parse_value(str, cmd)) return {filename + ": parse error"}; - if (cmd == "") continue; - - // possible token values - if (cmd == "newmtl") { - if (found) { - command = mtl_command::material; - fseek(fs, pos, SEEK_SET); - return {}; - } else { - found = true; - } - if (!parse_value(str, material.name)) return {filename + ": parse error"}; - } else if (cmd == "illum") { - if (!parse_value(str, material.illum)) - return {filename + ": parse error"}; - } else if (cmd == "Ke") { - if (!parse_value(str, material.emission)) - return {filename + ": parse error"}; - } else if (cmd == "Ka") { - if (!parse_value(str, material.ambient)) - return {filename + ": parse error"}; - } else if (cmd == "Kd") { - if (!parse_value(str, material.diffuse)) - return {filename + ": parse error"}; - } else if (cmd == "Ks") { - if (!parse_value(str, material.specular)) - return {filename + ": parse error"}; - } else if (cmd == "Kt") { - if (!parse_value(str, material.transmission)) - return {filename + ": parse error"}; - } else if (cmd == "Tf") { - material.transmission = vec3f{-1}; - if (!parse_value(str, material.transmission)) - return {filename + ": parse error"}; - if (material.transmission.y < 0) - material.transmission = vec3f{material.transmission.x}; - if (fliptr) material.transmission = 1 - material.transmission; - } else if (cmd == "Tr") { - if (!parse_value(str, material.opacity)) - return {filename + ": parse error"}; - if (fliptr) material.opacity = 1 - material.opacity; - } else if (cmd == "Ns") { - if (!parse_value(str, material.exponent)) - return {filename + ": parse error"}; - } else if (cmd == "d") { - if (!parse_value(str, material.opacity)) - return {filename + ": parse error"}; - } else if (cmd == "map_Ke") { - if (!parse_value(str, material.emission_map)) - return {filename + ": parse error"}; - } else if (cmd == "map_Ka") { - if (!parse_value(str, material.ambient_map)) - return {filename + ": parse error"}; - } else if (cmd == "map_Kd") { - if (!parse_value(str, material.diffuse_map)) - return {filename + ": parse error"}; - } else if (cmd == "map_Ks") { - if (!parse_value(str, material.specular_map)) - return {filename + ": parse error"}; - } else if (cmd == "map_Tr") { - if (!parse_value(str, material.transmission_map)) - return {filename + ": parse error"}; - } else if (cmd == "map_d" || cmd == "map_Tr") { - if (!parse_value(str, material.opacity_map)) - return {filename + ": parse error"}; - } else if (cmd == "map_bump" || cmd == "bump") { - if (!parse_value(str, material.bump_map)) - return {filename + ": parse error"}; - } else if (cmd == "map_disp" || cmd == "disp") { - if (!parse_value(str, material.displacement_map)) - return {filename + ": parse error"}; - } else if (cmd == "map_norm" || cmd == "norm") { - if (!parse_value(str, material.normal_map)) - return {filename + ": parse error"}; - } else if (cmd == "Pm") { - if (!parse_value(str, material.pbr_metallic)) - return {filename + ": parse error"}; - } else if (cmd == "Pr") { - if (!parse_value(str, material.pbr_roughness)) - return {filename + ": parse error"}; - } else if (cmd == "Ps") { - if (!parse_value(str, material.pbr_sheen)) - return {filename + ": parse error"}; - } else if (cmd == "Pc") { - if (!parse_value(str, material.pbr_clearcoat)) - return {filename + ": parse error"}; - } else if (cmd == "Pcr") { - if (!parse_value(str, material.pbr_coatroughness)) - return {filename + ": parse error"}; - } else if (cmd == "map_Pm") { - if (!parse_value(str, material.pbr_metallic_map)) - return {filename + ": parse error"}; - } else if (cmd == "map_Pr") { - if (!parse_value(str, material.pbr_roughness_map)) - return {filename + ": parse error"}; - } else if (cmd == "map_Ps") { - if (!parse_value(str, material.pbr_sheen_map)) - return {filename + ": parse error"}; - } else if (cmd == "map_Pc") { - if (!parse_value(str, material.pbr_clearcoat_map)) - return {filename + ": parse error"}; - } else if (cmd == "map_Pcr") { - if (!parse_value(str, material.pbr_coatroughness_map)) - return {filename + ": parse error"}; - } else if (cmd == "Vt") { - if (!parse_value(str, material.vol_transmission)) - return {filename + ": parse error"}; - } else if (cmd == "Vp") { - if (!parse_value(str, material.vol_meanfreepath)) - return {filename + ": parse error"}; - } else if (cmd == "Ve") { - if (!parse_value(str, material.vol_emission)) - return {filename + ": parse error"}; - } else if (cmd == "Vs") { - if (!parse_value(str, material.vol_scattering)) - return {filename + ": parse error"}; - } else if (cmd == "Vg") { - if (!parse_value(str, material.vol_anisotropy)) - return {filename + ": parse error"}; - } else if (cmd == "Vr") { - if (!parse_value(str, material.vol_scale)) - return {filename + ": parse error"}; - } else if (cmd == "map_Vs") { - if (!parse_value(str, material.vol_scattering_map)) - return {filename + ": parse error"}; - } else { - continue; - } - pos = ftell(fs); - } - - if (found) { - command = mtl_command::material; - return {}; - } - - // check error - if (ferror(fs)) return {filename + "read error"}; - - return {"eof"}; -} - -// Read objx -objio_status read_objx_command(const string& filename, FILE* fs, - objx_command& command, obj_camera& camera, obj_environment& environment, - obj_instance& instance) { - // read the file str by str - char buffer[4096]; - auto found = false; - while (fgets(buffer, sizeof(buffer), fs)) { - // str - auto str = string_view{buffer}; - remove_obj_comment(str); - skip_whitespace(str); - if (str.empty()) continue; - - // get command - auto cmd = ""s; - if (!parse_value(str, cmd)) return {filename + ": parse error"}; - if (cmd == "") continue; - - // read values - if (cmd == "c") { - command = objx_command::camera; - if (!parse_value(str, camera.name)) return {filename + ": parse error"}; - if (!parse_value(str, camera.ortho)) return {filename + ": parse error"}; - if (!parse_value(str, camera.width)) return {filename + ": parse error"}; - if (!parse_value(str, camera.height)) return {filename + ": parse error"}; - if (!parse_value(str, camera.lens)) return {filename + ": parse error"}; - if (!parse_value(str, camera.focus)) return {filename + ": parse error"}; - if (!parse_value(str, camera.aperture)) - return {filename + ": parse error"}; - if (!parse_value(str, camera.frame)) return {filename + ": parse error"}; - return {}; - } else if (cmd == "e") { - command = objx_command::environment; - if (!parse_value(str, environment.name)) - return {filename + ": parse error"}; - if (!parse_value(str, environment.emission)) - return {filename + ": parse error"}; - if (!parse_value(str, environment.emission_map)) - return {filename + ": parse error"}; - if (!parse_value(str, environment.frame)) - return {filename + ": parse error"}; - return {}; - } else if (cmd == "i") { - command = objx_command::instance; - if (!parse_value(str, instance.object)) - return {filename + ": parse error"}; - if (!parse_value(str, instance.frame)) - return {filename + ": parse error"}; - return {}; - } - } - - if (found) return {}; - - // check error - if (ferror(fs)) return {filename + ": read error"}; - - return {"eof"}; -} - -static vector split_obj_string(const string& str, const string& delim) { - auto tokens = vector{}; - auto last = (size_t)0, next = (size_t)0; - while ((next = str.find(delim, last)) != string::npos) { - tokens.push_back(str.substr(last, next - last)); - last = next + delim.size(); - } - if (last < str.size()) tokens.push_back(str.substr(last)); - return tokens; -} - -// Write obj elements -objio_status write_obj_comment( - const string& filename, FILE* fs, const string& comment) { - auto lines = split_obj_string(comment, "\n"); - for (auto& str : lines) { - if (!format_values(fs, "# {}\n", str)) return {filename + ": write error"}; - } - if (!format_values(fs, "\n")) return {filename + ": write error"}; - return {}; -} - -objio_status write_obj_command(const string& filename, FILE* fs, - obj_command command, const string& name, const vec3f& value, - const vector& vertices) { - switch (command) { - case obj_command::vertex: - if (!format_values(fs, "v {}\n", value)) - return {filename + ": write error"}; - break; - case obj_command::normal: - if (!format_values(fs, "vn {}\n", value)) - return {filename + ": write error"}; - break; - case obj_command::texcoord: - if (!format_values(fs, "vt {}\n", value)) - return {filename + ": write error"}; - break; - case obj_command::face: - case obj_command::str: - case obj_command::point: - if (command == obj_command::face) - if (!format_values(fs, "f ")) return {filename + ": write error"}; - if (command == obj_command::str) - if (!format_values(fs, "l ")) return {filename + ": write error"}; - if (command == obj_command::point) - if (!format_values(fs, "p ")) return {filename + ": write error"}; - for (auto& vert : vertices) - if (!format_values(fs, " {}", vert)) - return {filename + ": write error"}; - if (!format_values(fs, "\n")) return {filename + ": write error"}; - break; - case obj_command::object: - if (!format_values(fs, "o {}\n", name)) - return {filename + ": write error"}; - break; - case obj_command::group: - if (!format_values(fs, "g {}\n", name)) - return {filename + ": write error"}; - break; - case obj_command::usemtl: - if (!format_values(fs, "usemtl {}\n", name)) - return {filename + ": write error"}; - break; - case obj_command::smoothing: - if (!format_values(fs, "s {}\n", name)) - return {filename + ": write error"}; - break; - case obj_command::mtllib: - if (!format_values(fs, "mtllib {}\n", name)) - return {filename + ": write error"}; - break; - case obj_command::objxlib: break; - case obj_command::error: break; - } - - return {}; -} - -objio_status write_mtl_command(const string& filename, FILE* fs, - mtl_command command, const obj_material& material) { - // write material - switch (command) { - case mtl_command::material: - if (!format_values(fs, "newmtl {}\n", material.name)) - return {filename + ": write error"}; - if (!format_values(fs, "illum {}\n", material.illum)) - return {filename + ": write error"}; - if (material.emission != zero3f) - if (!format_values(fs, "Ke {}\n", material.emission)) - return {filename + ": write error"}; - if (material.ambient != zero3f) - if (!format_values(fs, "Ka {}\n", material.ambient)) - return {filename + ": write error"}; - if (!format_values(fs, "Kd {}\n", material.diffuse)) - return {filename + ": write error"}; - if (!format_values(fs, "Ks {}\n", material.specular)) - return {filename + ": write error"}; - if (material.reflection != zero3f) - if (!format_values(fs, "Kr {}\n", material.reflection)) - return {filename + ": write error"}; - if (material.transmission != zero3f) - if (!format_values(fs, "Kt {}\n", material.transmission)) - return {filename + ": write error"}; - format_values(fs, "Ns {}\n", (int)material.exponent); - if (material.opacity != 1) - if (!format_values(fs, "d {}\n", material.opacity)) - return {filename + ": write error"}; - if (!material.emission_map.path.empty()) - if (!format_values(fs, "map_Ke {}\n", material.emission_map)) - return {filename + ": write error"}; - if (!material.diffuse_map.path.empty()) - if (!format_values(fs, "map_Kd {}\n", material.diffuse_map)) - return {filename + ": write error"}; - if (!material.specular_map.path.empty()) - if (!format_values(fs, "map_Ks {}\n", material.specular_map)) - return {filename + ": write error"}; - if (!material.transmission_map.path.empty()) - if (!format_values(fs, "map_Kt {}\n", material.transmission_map)) - return {filename + ": write error"}; - if (!material.reflection_map.path.empty()) - if (!format_values(fs, "map_Kr {}\n", material.reflection_map)) - return {filename + ": write error"}; - if (!material.exponent_map.path.empty()) - if (!format_values(fs, "map_Ns {}\n", material.exponent_map)) - return {filename + ": write error"}; - if (!material.opacity_map.path.empty()) - if (!format_values(fs, "map_d {}\n", material.opacity_map)) - return {filename + ": write error"}; - if (!material.bump_map.path.empty()) - if (!format_values(fs, "map_bump {}\n", material.bump_map)) - return {filename + ": write error"}; - if (!material.displacement_map.path.empty()) - if (!format_values(fs, "map_disp {}\n", material.displacement_map)) - return {filename + ": write error"}; - if (!material.normal_map.path.empty()) - if (!format_values(fs, "map_norm {}\n", material.normal_map)) - return {filename + ": write error"}; - if (material.pbr_roughness) - if (!format_values(fs, "Pr {}\n", material.pbr_roughness)) - return {filename + ": write error"}; - if (material.pbr_metallic) - if (!format_values(fs, "Pm {}\n", material.pbr_metallic)) - return {filename + ": write error"}; - if (material.pbr_sheen) - if (!format_values(fs, "Ps {}\n", material.pbr_sheen)) - return {filename + ": write error"}; - if (material.pbr_clearcoat) - if (!format_values(fs, "Pc {}\n", material.pbr_clearcoat)) - return {filename + ": write error"}; - if (material.pbr_coatroughness) - if (!format_values(fs, "Pcr {}\n", material.pbr_coatroughness)) - return {filename + ": write error"}; - if (!material.pbr_roughness_map.path.empty()) - if (!format_values(fs, "map_Pr {}\n", material.pbr_roughness_map)) - return {filename + ": write error"}; - if (!material.pbr_metallic_map.path.empty()) - if (!format_values(fs, "map_Pm {}\n", material.pbr_metallic_map)) - return {filename + ": write error"}; - if (!material.pbr_sheen_map.path.empty()) - if (!format_values(fs, "map_Ps {}\n", material.pbr_sheen_map)) - return {filename + ": write error"}; - if (!material.pbr_clearcoat_map.path.empty()) - if (!format_values(fs, "map_Pc {}\n", material.pbr_clearcoat_map)) - return {filename + ": write error"}; - if (!material.pbr_coatroughness_map.path.empty()) - if (!format_values(fs, "map_Pcr {}\n", material.pbr_coatroughness_map)) - return {filename + ": write error"}; - if (material.vol_transmission != zero3f) - if (!format_values(fs, "Vt {}\n", material.vol_transmission)) - return {filename + ": write error"}; - if (material.vol_meanfreepath != zero3f) - if (!format_values(fs, "Vp {}\n", material.vol_meanfreepath)) - return {filename + ": write error"}; - if (material.vol_emission != zero3f) - if (!format_values(fs, "Ve {}\n", material.vol_emission)) - return {filename + ": write error"}; - if (material.vol_scattering != zero3f) - if (!format_values(fs, "Vs {}\n", material.vol_scattering)) - return {filename + ": write error"}; - if (material.vol_anisotropy) - if (!format_values(fs, "Vg {}\n", material.vol_anisotropy)) - return {filename + ": write error"}; - if (material.vol_scale) - if (!format_values(fs, "Vr {}\n", material.vol_scale)) - return {filename + ": write error"}; - if (!material.vol_scattering_map.path.empty()) - if (!format_values(fs, "map_Vs {}\n", material.vol_scattering_map)) - return {filename + ": write error"}; - if (!format_values(fs, "\n")) return {filename + ": write error"}; - break; - case mtl_command::error: break; - } - - return {}; -} - -objio_status write_objx_command(const string& filename, FILE* fs, - objx_command command, const obj_camera& camera, - const obj_environment& environment, const obj_instance& instance) { - switch (command) { - case objx_command::camera: { - if (!format_values(fs, "c {} {} {} {} {} {} {} {}\n", camera.name, - camera.ortho, camera.width, camera.height, camera.lens, - camera.focus, camera.aperture, camera.frame)) - return {filename + ": write error"}; - } break; - case objx_command::environment: { - if (!format_values(fs, "e {} {} {} {}\n", environment.name, - environment.emission, - environment.emission_map.path.empty() - ? "\"\""s - : environment.emission_map.path, - environment.frame)) - return {filename + ": write error"}; - } break; - case objx_command::instance: { - if (!format_values(fs, "i {} {}\n", instance.object, instance.frame)) - return {filename + ": write error"}; - } break; - case objx_command::error: break; - } - - return {}; } } // namespace yocto @@ -3357,14 +2348,13 @@ static void remove_pbrt_comment(string_view& str, char comment_char = '#') { } // Read a pbrt command from file -static bool read_pbrt_cmdline(FILE* fs, string& cmd, int& line_num) { +static bool read_pbrt_cmdline(file_wrapper& fs, string& cmd) { char buffer[4096]; cmd.clear(); auto found = false; - auto pos = ftell(fs); - while (fgets(buffer, sizeof(buffer), fs)) { + auto pos = ftell(fs.fs); + while (read_line(fs, buffer, sizeof(buffer))) { // line - line_num += 1; auto line = string_view{buffer}; remove_pbrt_comment(line); skip_whitespace(line); @@ -3374,8 +2364,8 @@ static bool read_pbrt_cmdline(FILE* fs, string& cmd, int& line_num) { auto is_cmd = line[0] >= 'A' && line[0] <= 'Z'; if (is_cmd) { if (found) { - fseek(fs, pos, SEEK_SET); - line_num -= 1; + fseek(fs.fs, pos, SEEK_SET); + // line_num -= 1; return true; } else { found = true; @@ -3385,15 +2375,16 @@ static bool read_pbrt_cmdline(FILE* fs, string& cmd, int& line_num) { } cmd += line; cmd += " "; - pos = ftell(fs); + pos = ftell(fs.fs); } return found; } // parse a quoted string -static bool parse_pbrt_command(string_view& str, string& value) { +static void parse_pbrt_command(string_view& str, string& value) { skip_whitespace(str); - if (!isalpha((int)str.front())) return {}; + if (!isalpha((int)str.front())) + throw std::invalid_argument{"expected command"}; auto pos = str.find_first_not_of( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); if (pos == string_view::npos) { @@ -3403,41 +2394,57 @@ static bool parse_pbrt_command(string_view& str, string& value) { value.assign(str.substr(0, pos)); str.remove_prefix(pos + 1); } - return true; +} + +static void parse_pbrt_command( + file_wrapper& fs, string_view& str, string& value) { + try { + parse_pbrt_command(str, value); + } catch (std::exception& e) { + throw std::runtime_error{fs.filename + ": parse error"}; + } } // parse pbrt value with optional parens template -static bool parse_pbrt_param(string_view& str, T& value) { +static void parse_pbrt_param(string_view& str, T& value) { skip_whitespace(str); auto parens = !str.empty() && str.front() == '['; if (parens) str.remove_prefix(1); - if (!parse_value(str, value)) return false; - if (!str.data()) return {}; + parse_value(str, value); + if (!str.data()) throw std::invalid_argument{"invalid param"}; if (parens) { skip_whitespace(str); - if (!str.empty() && str.front() == '[') return {}; + if (!str.empty() && str.front() == '[') + throw std::invalid_argument{"invalid param"}; str.remove_prefix(1); } - return true; +} + +// parse pbrt value with optional parens +template +static void parse_pbrt_param(file_wrapper& fs, string_view& str, T& value) { + try { + parse_pbrt_param(str, value); + } catch (std::exception& e) { + throw std::runtime_error{fs.filename + ": parse error"}; + } } // parse a quoted string -[[nodiscard]] static bool parse_pbrt_nametype( - string_view& str_, string& name, string& type) { +static void parse_pbrt_nametype(string_view& str_, string& name, string& type) { auto value = ""s; - if (!parse_value(str_, value)) return false; - if (!str_.data()) return {}; + parse_value(str_, value); + if (!str_.data()) throw std::invalid_argument{"string expected"}; auto str = string_view{value}; auto pos1 = str.find(' '); - if (pos1 == string_view::npos) return {}; + if (pos1 == string_view::npos) throw std::invalid_argument{"string expected"}; type = string(str.substr(0, pos1)); str.remove_prefix(pos1); auto pos2 = str.find_first_not_of(' '); - if (pos2 == string_view::npos) return {}; + if (pos2 == string_view::npos) throw std::invalid_argument{"string expected"}; str.remove_prefix(pos2); name = string(str); - return true; } static pair get_pbrt_etak(const string& name) { @@ -3618,32 +2625,30 @@ pair get_pbrt_subsurface(const string& name) { return params.at(name); } -[[nodiscard]] static bool parse_pbrt_params( - string_view& str, vector& values) { +static void parse_pbrt_params(string_view& str, vector& values) { auto parse_pbrt_pvalues = [](string_view& str, auto& value, - auto& values) -> bool { + auto& values) -> void { values.clear(); skip_whitespace(str); - if (str.empty()) throw std::runtime_error("bad pbrt value"); + if (str.empty()) throw std::invalid_argument("bad pbrt value"); if (str.front() == '[') { str.remove_prefix(1); skip_whitespace(str); - if (str.empty()) throw std::runtime_error("bad pbrt value"); + if (str.empty()) throw std::invalid_argument{"bad pbrt value"}; while (!str.empty()) { auto& val = values.empty() ? value : values.emplace_back(); - if (!parse_value(str, val)) return false; - if (!str.data()) return {}; + parse_value(str, val); + if (!str.data()) throw std::invalid_argument{"bad pbrt value"}; skip_whitespace(str); if (str.empty()) break; if (str.front() == ']') break; if (values.empty()) values.push_back(value); } - if (str.empty()) return false; - if (str.front() != ']') return false; + if (str.empty()) throw std::invalid_argument{"bad pbrt value"}; + if (str.front() != ']') throw std::invalid_argument{"bad pbrt value"}; str.remove_prefix(1); - return true; } else { - return parse_value(str, value); + parse_value(str, value); } }; @@ -3652,62 +2657,61 @@ pair get_pbrt_subsurface(const string& name) { while (!str.empty()) { auto& value = values.emplace_back(); auto type = ""s; - if (!parse_pbrt_nametype(str, value.name, type)) return false; + parse_pbrt_nametype(str, value.name, type); skip_whitespace(str); - if (str.empty()) throw std::runtime_error("expected value"); + if (str.empty()) throw std::invalid_argument{"bad pbrt value"}; if (type == "float") { value.type = pbrt_value_type::real; - if (!parse_pbrt_pvalues(str, value.value1f, value.vector1f)) return false; + parse_pbrt_pvalues(str, value.value1f, value.vector1f); } else if (type == "integer") { value.type = pbrt_value_type::integer; - if (!parse_pbrt_pvalues(str, value.value1i, value.vector1i)) return false; + parse_pbrt_pvalues(str, value.value1i, value.vector1i); } else if (type == "string") { auto vector1s = vector{}; value.type = pbrt_value_type::string; - if (!parse_pbrt_pvalues(str, value.value1s, vector1s)) return false; - if (!vector1s.empty()) return false; + parse_pbrt_pvalues(str, value.value1s, vector1s); + if (!vector1s.empty()) throw std::invalid_argument{"bad pbrt value"}; } else if (type == "bool") { auto value1s = ""s; auto vector1s = vector{}; value.type = pbrt_value_type::boolean; - if (!parse_pbrt_pvalues(str, value1s, vector1s)) return false; - if (!vector1s.empty()) return false; + parse_pbrt_pvalues(str, value1s, vector1s); + if (!vector1s.empty()) throw std::invalid_argument{"bad pbrt value"}; value.value1b = value1s == "true"; } else if (type == "texture") { auto vector1s = vector{}; value.type = pbrt_value_type::texture; - if (!parse_pbrt_pvalues(str, value.value1s, vector1s)) return false; - if (!vector1s.empty()) return false; + parse_pbrt_pvalues(str, value.value1s, vector1s); + if (!vector1s.empty()) throw std::invalid_argument{"bad pbrt value"}; } else if (type == "point" || type == "point3") { value.type = pbrt_value_type::point; - if (!parse_pbrt_pvalues(str, value.value3f, value.vector3f)) return false; + parse_pbrt_pvalues(str, value.value3f, value.vector3f); } else if (type == "normal" || type == "normal3") { value.type = pbrt_value_type::normal; - if (!parse_pbrt_pvalues(str, value.value3f, value.vector3f)) return false; + parse_pbrt_pvalues(str, value.value3f, value.vector3f); } else if (type == "vector" || type == "vector3") { value.type = pbrt_value_type::vector; - if (!parse_pbrt_pvalues(str, value.value3f, value.vector3f)) return false; + parse_pbrt_pvalues(str, value.value3f, value.vector3f); } else if (type == "point2") { value.type = pbrt_value_type::point2; - if (!parse_pbrt_pvalues(str, value.value2f, value.vector2f)) return false; + parse_pbrt_pvalues(str, value.value2f, value.vector2f); } else if (type == "vector2") { value.type = pbrt_value_type::vector2; - if (!parse_pbrt_pvalues(str, value.value2f, value.vector2f)) return false; + parse_pbrt_pvalues(str, value.value2f, value.vector2f); } else if (type == "blackbody") { value.type = pbrt_value_type::color; auto blackbody = zero2f; auto vector2f = vector{}; - if (!parse_pbrt_pvalues(str, blackbody, vector2f)) return false; - if (!vector2f.empty()) return false; + parse_pbrt_pvalues(str, blackbody, vector2f); + if (!vector2f.empty()) throw std::invalid_argument{"bad pbrt value"}; value.value3f = blackbody_to_rgb(blackbody.x) * blackbody.y; } else if (type == "color" || type == "rgb") { value.type = pbrt_value_type::color; - if (!parse_pbrt_pvalues(str, value.value3f, value.vector3f)) return false; + parse_pbrt_pvalues(str, value.value3f, value.vector3f); } else if (type == "xyz") { value.type = pbrt_value_type::color; - if (!parse_pbrt_pvalues(str, value.value3f, value.vector3f)) return false; - throw std::runtime_error("xyz conversion"); - return false; + parse_pbrt_pvalues(str, value.value3f, value.vector3f); + throw std::invalid_argument("xyz conversion"); } else if (type == "spectrum") { auto is_string = false; auto str1 = str; @@ -3723,8 +2727,8 @@ pair get_pbrt_subsurface(const string& name) { value.type = pbrt_value_type::color; auto filename = ""s; auto filenames = vector{}; - if (!parse_value(str, filename)) return false; - if (!str.data()) return {}; + parse_value(str, filename); + if (!str.data()) throw std::invalid_argument{"bad pbrt value"}; auto filenamep = get_filename(filename); if (get_extension(filenamep) == ".spd") { filenamep = replace_extension(filenamep, ""); @@ -3737,47 +2741,54 @@ pair get_pbrt_subsurface(const string& name) { auto k = get_pbrt_etak(replace_extension(filenamep, "")).second; value.value3f = {k.x, k.y, k.z}; } else { - return false; + throw std::invalid_argument{"bad pbrt value"}; } } else { - return false; + throw std::invalid_argument{"bad pbrt value"}; } } else { value.type = pbrt_value_type::spectrum; - if (!parse_pbrt_pvalues(str, value.value1f, value.vector1f)) - return false; + parse_pbrt_pvalues(str, value.value1f, value.vector1f); } } else { - return false; + throw std::invalid_argument{"bad pbrt value"}; } skip_whitespace(str); } - return true; +} + +static void parse_pbrt_params( + file_wrapper& fs, string_view& str, vector& values) { + try { + parse_pbrt_params(str, values); + } catch (std::exception& e) { + throw std::runtime_error{fs.filename + ": parse error"}; + } } // convert pbrt films -static pbrtio_status convert_pbrt_films( +static void convert_pbrt_films( const string& filename, vector& films, bool verbose = false) { for (auto& film : films) { - auto& values = film.values; - if (film.type == "image") { - film.resolution = {512, 512}; - if (!get_pbrt_value(values, "xresolution", film.resolution.x)) - return {filename + ": type error"}; - if (!get_pbrt_value(values, "yresolution", film.resolution.y)) - return {filename + ": type error"}; - film.filename = "out.png"s; - if (!get_pbrt_value(values, "filename", film.filename)) - return {filename + ": type error"}; - } else { - return {filename + ": unsupported film type " + film.type}; + try { + auto& values = film.values; + if (film.type == "image") { + film.resolution = {512, 512}; + get_pbrt_value(values, "xresolution", film.resolution.x); + get_pbrt_value(values, "yresolution", film.resolution.y); + film.filename = "out.png"s; + get_pbrt_value(values, "filename", film.filename); + } else { + throw std::invalid_argument{"unknown film " + film.type}; + } + } catch (std::invalid_argument& e) { + throw std::runtime_error{filename + ": conversion error"}; } } - return {}; } // convert pbrt elements -static pbrtio_status convert_pbrt_cameras(const string& filename, +static void convert_pbrt_cameras(const string& filename, vector& cameras, const vector& films, bool verbose = false) { auto film_aspect = 1.0f; @@ -3785,50 +2796,47 @@ static pbrtio_status convert_pbrt_cameras(const string& filename, film_aspect = (float)film.resolution.x / (float)film.resolution.y; } for (auto& camera : cameras) { - auto& values = camera.values; - camera.frame = inverse((frame3f)camera.frame); - camera.frame.z = -camera.frame.z; - if (camera.type == "perspective") { - auto fov = 90.0f; - if (!get_pbrt_value(values, "fov", fov)) - return {filename + ": type error"}; - // auto lensradius = get_pbrt_value(values, "lensradius", 0.0f); - camera.aspect = film_aspect; - if (camera.aspect >= 1) { - camera.lens = (0.036 / camera.aspect) / (2 * tan(radians(fov) / 2)); + try { + auto& values = camera.values; + camera.frame = inverse((frame3f)camera.frame); + camera.frame.z = -camera.frame.z; + if (camera.type == "perspective") { + auto fov = 90.0f; + get_pbrt_value(values, "fov", fov); + // auto lensradius = get_pbrt_value(values, "lensradius", 0.0f); + camera.aspect = film_aspect; + if (camera.aspect >= 1) { + camera.lens = (0.036 / camera.aspect) / (2 * tan(radians(fov) / 2)); + } else { + camera.lens = (0.036 * camera.aspect) / (2 * tan(radians(fov) / 2)); + } + get_pbrt_value(values, "frameaspectratio", camera.aspect); + camera.focus = 10.0f; + get_pbrt_value(values, "focaldistance", camera.focus); + } else if (camera.type == "realistic") { + auto lensfile = ""s; + get_pbrt_value(values, "lensfile", lensfile); + lensfile = lensfile.substr(0, lensfile.size() - 4); + lensfile = lensfile.substr(lensfile.find('.') + 1); + lensfile = lensfile.substr(0, lensfile.size() - 2); + auto lens = max(std::atof(lensfile.c_str()), 35.0f) * 0.001f; + camera.lens = 2 * atan(0.036f / (2 * lens)); + camera.aperture = 0.0f; + get_pbrt_value(values, "aperturediameter", camera.aperture); + camera.focus = 10.0f; + get_pbrt_value(values, "focusdistance", camera.focus); + camera.aspect = film_aspect; } else { - camera.lens = (0.036 * camera.aspect) / (2 * tan(radians(fov) / 2)); + throw std::invalid_argument{"unknown camera " + camera.type}; } - if (!get_pbrt_value(values, "frameaspectratio", camera.aspect)) - return {filename + ": type error"}; - camera.focus = 10.0f; - if (!get_pbrt_value(values, "focaldistance", camera.focus)) - return {filename + ": type error"}; - } else if (camera.type == "realistic") { - auto lensfile = ""s; - if (!get_pbrt_value(values, "lensfile", lensfile)) - return {filename + ": type error"}; - lensfile = lensfile.substr(0, lensfile.size() - 4); - lensfile = lensfile.substr(lensfile.find('.') + 1); - lensfile = lensfile.substr(0, lensfile.size() - 2); - auto lens = max(std::atof(lensfile.c_str()), 35.0f) * 0.001f; - camera.lens = 2 * atan(0.036f / (2 * lens)); - camera.aperture = 0.0f; - if (!get_pbrt_value(values, "aperturediameter", camera.aperture)) - return {filename + ": type error"}; - camera.focus = 10.0f; - if (!get_pbrt_value(values, "focusdistance", camera.focus)) - return {filename + ": type error"}; - camera.aspect = film_aspect; - } else { - return {filename + ": unsupported camera type " + camera.type}; + } catch (std::invalid_argument& e) { + throw std::runtime_error{filename + ": conversion error"}; } } - return {}; } // convert pbrt textures -static pbrtio_status convert_pbrt_textures(const string& filename, +static void convert_pbrt_textures(const string& filename, vector& textures, bool verbose = false) { auto texture_map = unordered_map{}; for (auto& texture : textures) { @@ -3841,83 +2849,78 @@ static pbrtio_status convert_pbrt_textures(const string& filename, if (pos == texture_map.end()) return ""s; return textures[pos->second].filename; }; - auto make_placeholder = [verbose](pbrt_texture& texture, - const vec3f& color = {1, 0, 0}) { + auto make_placeholder = [](pbrt_texture& texture, + const vec3f& color = {1, 0, 0}) { texture.constant = color; - if (verbose) - printf("texture %s not supported well\n", texture.type.c_str()); }; for (auto& texture : textures) { - auto& values = texture.values; - if (texture.type == "imagemap") { - texture.filename = ""; - if (!get_pbrt_value(values, "filename", texture.filename)) - return {filename + ": type error"}; - } else if (texture.type == "constant") { - texture.constant = vec3f{1}; - if (!get_pbrt_value(values, "value", texture.constant)) - return {filename + ": type error"}; - } else if (texture.type == "bilerp") { - make_placeholder(texture, {1, 0, 0}); - } else if (texture.type == "checkerboard") { - // auto tex1 = get_pbrt_value(values, "tex1", pair{vec3f{1}, ""s}); - // auto tex2 = get_pbrt_value(values, "tex2", pair{vec3f{0}, ""s}); - // auto rgb1 = tex1.second == "" ? tex1.first : vec3f{0.4f, 0.4f, - // 0.4f}; auto rgb2 = tex1.second == "" ? tex2.first : vec3f{0.6f, - // 0.6f, 0.6f}; auto params = proc_image_params{}; params.type = - // proc_image_params::type_t::checker; params.color0 = {rgb1.x, rgb1.y, - // rgb1.z, 1}; params.color1 = {rgb2.x, rgb2.y, rgb2.z, 1}; params.scale - // = 2; make_proc_image(texture.hdr, params); float_to_byte(texture.ldr, - // texture.hdr); texture.hdr = {}; - make_placeholder(texture, vec3f{0.5}); - } else if (texture.type == "dots") { - make_placeholder(texture, vec3f{0.5}); - } else if (texture.type == "fbm") { - make_placeholder(texture, vec3f{0.5}); - } else if (texture.type == "marble") { - make_placeholder(texture, vec3f{0.5}); - } else if (texture.type == "mix") { - auto tex1 = pair{vec3f{0}, ""s}, tex2 = pair{vec3f{1}, ""s}; - if (!get_pbrt_value(values, "tex1", tex1)) - return {filename + ": type error"}; - if (!get_pbrt_value(values, "tex2", tex2)) - return {filename + ": type error"}; - if (!get_filename(tex1.second).empty()) { - texture.filename = get_filename(tex1.second); - } else if (!get_filename(tex2.second).empty()) { - texture.filename = get_filename(tex2.second); - } else { + try { + auto& values = texture.values; + if (texture.type == "imagemap") { + texture.filename = ""; + get_pbrt_value(values, "filename", texture.filename); + } else if (texture.type == "constant") { + texture.constant = vec3f{1}; + get_pbrt_value(values, "value", texture.constant); + } else if (texture.type == "bilerp") { + make_placeholder(texture, {1, 0, 0}); + } else if (texture.type == "checkerboard") { + // auto tex1 = get_pbrt_value(values, "tex1", pair{vec3f{1}, ""s}); + // auto tex2 = get_pbrt_value(values, "tex2", pair{vec3f{0}, ""s}); + // auto rgb1 = tex1.second == "" ? tex1.first : vec3f{0.4f, 0.4f, + // 0.4f}; auto rgb2 = tex1.second == "" ? tex2.first : vec3f{0.6f, + // 0.6f, 0.6f}; auto params = proc_image_params{}; params.type = + // proc_image_params::type_t::checker; params.color0 = {rgb1.x, rgb1.y, + // rgb1.z, 1}; params.color1 = {rgb2.x, rgb2.y, rgb2.z, 1}; params.scale + // = 2; make_proc_image(texture.hdr, params); float_to_byte(texture.ldr, + // texture.hdr); texture.hdr = {}; + make_placeholder(texture, vec3f{0.5}); + } else if (texture.type == "dots") { + make_placeholder(texture, vec3f{0.5}); + } else if (texture.type == "fbm") { + make_placeholder(texture, vec3f{0.5}); + } else if (texture.type == "marble") { + make_placeholder(texture, vec3f{0.5}); + } else if (texture.type == "mix") { + auto tex1 = pair{vec3f{0}, ""s}, tex2 = pair{vec3f{1}, ""s}; + get_pbrt_value(values, "tex1", tex1); + get_pbrt_value(values, "tex2", tex2); + if (!get_filename(tex1.second).empty()) { + texture.filename = get_filename(tex1.second); + } else if (!get_filename(tex2.second).empty()) { + texture.filename = get_filename(tex2.second); + } else { + make_placeholder(texture); + } + } else if (texture.type == "scale") { + auto tex1 = pair{vec3f{1}, ""s}, tex2 = pair{vec3f{1}, ""s}; + get_pbrt_value(values, "tex1", tex2); + get_pbrt_value(values, "tex2", tex1); + if (!get_filename(tex1.second).empty()) { + texture.filename = get_filename(tex1.second); + } else if (!get_filename(tex2.second).empty()) { + texture.filename = get_filename(tex2.second); + } else { + make_placeholder(texture); + } + } else if (texture.type == "uv") { make_placeholder(texture); - } - } else if (texture.type == "scale") { - auto tex1 = pair{vec3f{1}, ""s}, tex2 = pair{vec3f{1}, ""s}; - if (!get_pbrt_value(values, "tex1", tex2)) - return {filename + ": type error"}; - if (!get_pbrt_value(values, "tex2", tex1)) - return {filename + ": type error"}; - if (!get_filename(tex1.second).empty()) { - texture.filename = get_filename(tex1.second); - } else if (!get_filename(tex2.second).empty()) { - texture.filename = get_filename(tex2.second); - } else { + } else if (texture.type == "windy") { + make_placeholder(texture); + } else if (texture.type == "wrinkled") { make_placeholder(texture); + } else { + throw std::invalid_argument{"unknown texture " + texture.type}; } - } else if (texture.type == "uv") { - make_placeholder(texture); - } else if (texture.type == "windy") { - make_placeholder(texture); - } else if (texture.type == "wrinkled") { - make_placeholder(texture); - } else { - return {filename + ": unsupported texture type " + texture.type}; + } catch (std::invalid_argument& e) { + throw std::runtime_error{filename + ": conversion error"}; } } - return {}; } // convert pbrt materials -static pbrtio_status convert_pbrt_materials(const string& filename, +static void convert_pbrt_materials(const string& filename, vector& materials, const vector& textures, bool verbose = false) { // add constant textures @@ -3930,36 +2933,33 @@ static pbrtio_status convert_pbrt_materials(const string& filename, // helpers auto get_scaled_texture = [&](const vector& values, const string& name, vec3f& color, - string& texture, const vec3f& def) -> bool { + string& texture, const vec3f& def) -> void { auto textured = pair{def, ""s}; - if (!get_pbrt_value(values, name, textured)) return false; + get_pbrt_value(values, name, textured); if (textured.second == "") { color = textured.first; texture = ""; - return true; } else if (constants.find(textured.second) != constants.end()) { color = constants.at(textured.second); texture = ""; - return true; } else { color = {1, 1, 1}; texture = textured.second; - return true; } }; auto get_pbrt_roughness = [&](const vector& values, - vec2f& roughness, float def = 0.1) -> bool { + vec2f& roughness, float def = 0.1) -> void { auto roughness_ = pair{vec3f{def}, ""s}; - if (!get_pbrt_value(values, "roughness", roughness_)) return false; + get_pbrt_value(values, "roughness", roughness_); auto uroughness = roughness_, vroughness = roughness_; auto remaproughness = true; - if (!get_pbrt_value(values, "uroughness", uroughness)) return false; - if (!get_pbrt_value(values, "vroughness", vroughness)) return false; - if (!get_pbrt_value(values, "remaproughness", remaproughness)) return false; + get_pbrt_value(values, "uroughness", uroughness); + get_pbrt_value(values, "vroughness", vroughness); + get_pbrt_value(values, "remaproughness", remaproughness); roughness = zero2f; - if (uroughness.first == zero3f || vroughness.first == zero3f) return true; + if (uroughness.first == zero3f || vroughness.first == zero3f) return; roughness = vec2f{mean(uroughness.first), mean(vroughness.first)}; // from pbrt code if (remaproughness) { @@ -3969,7 +2969,6 @@ static pbrtio_status convert_pbrt_materials(const string& filename, 0.0171201f * x * x * x + 0.000640711f * x * x * x * x; } roughness = sqrt(roughness); - return true; }; auto eta_to_reflectivity = [](const vec3f& eta, @@ -3978,253 +2977,212 @@ static pbrtio_status convert_pbrt_materials(const string& filename, ((eta + 1) * (eta + 1) + etak * etak); }; - // convert materials - for (auto& material : materials) { - auto& values = material.values; - if (material.type == "uber") { - if (!get_scaled_texture(values, "Kd", material.diffuse, - material.diffuse_map, vec3f{0.25})) - return {filename + ": type error"}; - if (!get_scaled_texture(values, "Ks", material.specular, - material.specular_map, vec3f{0.25})) - return {filename + ": type error"}; - if (!get_scaled_texture(values, "Kt", material.transmission, - material.transmission_map, vec3f{0})) - return {filename + ": type error"}; - if (!get_scaled_texture(values, "opacity", material.opacity, - material.opacity_map, vec3f{1})) - return {filename + ": type error"}; - if (!get_scaled_texture( - values, "eta", material.eta, material.eta_map, vec3f{1.5})) - return {filename + ": type error"}; - if (!get_pbrt_roughness(values, material.roughness, 0.1f)) - return {filename + ": type error"}; - material.sspecular = material.specular * - eta_to_reflectivity(material.eta); - } else if (material.type == "plastic") { - if (!get_scaled_texture(values, "Kd", material.diffuse, - material.diffuse_map, vec3f{0.25})) - return {filename + ": type error"}; - if (!get_scaled_texture(values, "Ks", material.specular, - material.specular_map, vec3f{0.25})) - return {filename + ": type error"}; - if (!get_scaled_texture( - values, "eta", material.eta, material.eta_map, vec3f{1.5})) - return {filename + ": type error"}; - material.roughness = vec2f{0.1f}; - if (!get_pbrt_roughness(values, material.roughness, 0.1)) - return {filename + ": type error"}; - material.sspecular = material.specular * - eta_to_reflectivity(material.eta); - } else if (material.type == "translucent") { - if (!get_scaled_texture(values, "Kd", material.diffuse, - material.diffuse_map, vec3f{0.25})) - return {filename + ": type error"}; - if (!get_scaled_texture(values, "Ks", material.specular, - material.specular_map, vec3f{0.25})) - return {filename + ": type error"}; - if (!get_scaled_texture( - values, "eta", material.eta, material.eta_map, vec3f{1.5})) - return {filename + ": type error"}; - if (!get_pbrt_roughness(values, material.roughness, 0.1)) - return {filename + ": type error"}; - material.sspecular = material.specular * - eta_to_reflectivity(material.eta); - } else if (material.type == "matte") { - if (!get_scaled_texture( - values, "Kd", material.diffuse, material.diffuse_map, vec3f{0.5})) - return {filename + ": type error"}; - material.roughness = vec2f{1}; - } else if (material.type == "mirror") { - if (!get_scaled_texture(values, "Kr", material.specular, - material.specular_map, vec3f{0.9})) - return {filename + ": type error"}; - material.eta = zero3f; - material.etak = zero3f; - material.roughness = zero2f; - material.sspecular = material.specular; - } else if (material.type == "metal") { - if (!get_scaled_texture( - values, "Kr", material.specular, material.specular_map, vec3f{1})) - return {filename + ": type error"}; - if (!get_scaled_texture(values, "eta", material.eta, material.eta_map, - vec3f{0.2004376970f, 0.9240334304f, 1.1022119527f})) - return {filename + ": type error"}; - if (!get_scaled_texture(values, "k", material.etak, material.etak_map, - vec3f{3.9129485033f, 2.4528477015f, 2.1421879552f})) - return {filename + ": type error"}; - material.roughness = vec2f{0.01f}; - if (!get_pbrt_roughness(values, material.roughness, 0.01)) - return {filename + ": type error"}; - material.sspecular = material.specular * - eta_to_reflectivity(material.eta, material.etak); - } else if (material.type == "substrate") { - if (!get_scaled_texture( - values, "Kd", material.diffuse, material.diffuse_map, vec3f{0.5})) - return {filename + ": type error"}; - if (!get_scaled_texture(values, "Ks", material.specular, - material.specular_map, vec3f{0.5})) - return {filename + ": type error"}; - if (!get_scaled_texture( - values, "eta", material.eta, material.eta_map, vec3f{1.5})) - return {filename + ": type error"}; - material.roughness = vec2f{0.1f}; - if (!get_pbrt_roughness(values, material.roughness, 0.1)) - return {filename + ": type error"}; - material.sspecular = material.specular * - eta_to_reflectivity(material.eta); - } else if (material.type == "glass") { - if (!get_scaled_texture( - values, "Kr", material.specular, material.specular_map, vec3f{1})) - return {filename + ": type error"}; - if (!get_scaled_texture(values, "Kt", material.transmission, - material.transmission_map, vec3f{1})) - return {filename + ": type error"}; - if (!get_scaled_texture( - values, "eta", material.eta, material.eta_map, vec3f{1.5})) - return {filename + ": type error"}; - material.roughness = vec2f{0}; - if (!get_pbrt_roughness(values, material.roughness, 0)) - return {filename + ": type error"}; - material.sspecular = material.specular * - eta_to_reflectivity(material.eta); - material.refract = true; - } else if (material.type == "hair") { - if (!get_scaled_texture(values, "color", material.diffuse, - material.diffuse_map, vec3f{0})) - return {filename + ": type error"}; - material.roughness = {1, 1}; - if (verbose) printf("hair material not properly supported\n"); - } else if (material.type == "disney") { - if (!get_scaled_texture(values, "color", material.diffuse, - material.diffuse_map, vec3f{0.5})) - return {filename + ": type error"}; - material.roughness = {1, 1}; - if (verbose) printf("disney material not properly supported\n"); - } else if (material.type == "kdsubsurface") { - if (!get_scaled_texture( - values, "Kd", material.diffuse, material.diffuse_map, vec3f{0.5})) - return {filename + ": type error"}; - if (!get_scaled_texture( - values, "Kr", material.specular, material.specular_map, vec3f{1})) - return {filename + ": type error"}; - if (!get_scaled_texture( - values, "eta", material.eta, material.eta_map, vec3f{1.5})) - return {filename + ": type error"}; - material.roughness = vec2f{0}; - if (!get_pbrt_roughness(values, material.roughness, 0)) - return {filename + ": type error"}; - material.sspecular = material.specular * - eta_to_reflectivity(material.eta); - if (verbose) printf("kdsubsurface material not properly supported\n"); - } else if (material.type == "subsurface") { - if (!get_scaled_texture( - values, "Kr", material.specular, material.specular_map, vec3f{1})) - return {filename + ": type error"}; - if (!get_scaled_texture(values, "Kt", material.transmission, - material.transmission_map, vec3f{1})) - return {filename + ": type error"}; - if (!get_scaled_texture( - values, "eta", material.eta, material.eta_map, vec3f{1.5})) - return {filename + ": type error"}; - material.roughness = vec2f{0}; - if (!get_pbrt_roughness(values, material.roughness, 0)) - return {filename + ": type error"}; - material.sspecular = material.specular * - eta_to_reflectivity(material.eta); - auto scale = 1.0f; - if (!get_pbrt_value(values, "scale", scale)) - return {filename + ": type error"}; - material.volscale = 1 / scale; - auto sigma_a = zero3f, sigma_s = zero3f; - auto sigma_a_tex = ""s, sigma_s_tex = ""s; - if (!get_scaled_texture(values, "sigma_a", sigma_a, sigma_a_tex, - vec3f{0011, .0024, .014})) - return {filename + ": type error"}; - if (!get_scaled_texture(values, "sigma_prime_s", sigma_s, sigma_s_tex, - vec3f{2.55, 3.12, 3.77})) - return {filename + ": type error"}; - material.volmeanfreepath = 1 / (sigma_a + sigma_s); - material.volscatter = sigma_s / (sigma_a + sigma_s); - if (verbose) printf("subsurface material not properly supported\n"); - } else if (material.type == "mix") { - auto namedmaterial1 = ""s, namedmaterial2 = ""s; - if (!get_pbrt_value(values, "namedmaterial1", namedmaterial1)) - return {filename + ": type error"}; - if (!get_pbrt_value(values, "namedmaterial2", namedmaterial2)) - return {filename + ": type error"}; - auto matname = (!namedmaterial1.empty()) ? namedmaterial1 - : namedmaterial2; - auto matit = std::find_if(materials.begin(), materials.end(), - [&matname](auto& material) { return material.name == matname; }); - if (matit == materials.end()) - throw std::runtime_error("cannot find material " + matname); - auto saved_name = material.name; - material = *matit; - material.name = saved_name; - if (verbose) printf("mix material not properly supported\n"); - } else if (material.type == "fourier") { - auto bsdffile = ""s; - if (!get_pbrt_value(values, "bsdffile", bsdffile)) - return {filename + ": type error"}; - if (bsdffile.rfind("/") != string::npos) - bsdffile = bsdffile.substr(bsdffile.rfind("/") + 1); - if (bsdffile == "paint.bsdf") { - material.diffuse = {0.6f, 0.6f, 0.6f}; - material.specular = {1, 1, 1}; - material.eta = vec3f{1.5}; - material.roughness = vec2f{0.2}; - // material.roughness = get_pbrt_roughnessf(0.2f, true); + try { + // convert materials + for (auto& material : materials) { + auto& values = material.values; + if (material.type == "uber") { + get_scaled_texture( + values, "Kd", material.diffuse, material.diffuse_map, vec3f{0.25}); + get_scaled_texture(values, "Ks", material.specular, + material.specular_map, vec3f{0.25}); + get_scaled_texture(values, "Kt", material.transmission, + material.transmission_map, vec3f{0}); + get_scaled_texture(values, "opacity", material.opacity, + material.opacity_map, vec3f{1}); + get_scaled_texture( + values, "eta", material.eta, material.eta_map, vec3f{1.5}); + get_pbrt_roughness(values, material.roughness, 0.1f); material.sspecular = material.specular * eta_to_reflectivity(material.eta); - } else if (bsdffile == "ceramic.bsdf") { - material.diffuse = {0.6f, 0.6f, 0.6f}; - material.specular = {1, 1, 1}; - material.eta = vec3f{1.5}; - material.roughness = vec2f{0.25}; - // material.roughness = get_pbrt_roughnessf(0.25, true); + } else if (material.type == "plastic") { + get_scaled_texture( + values, "Kd", material.diffuse, material.diffuse_map, vec3f{0.25}); + get_scaled_texture(values, "Ks", material.specular, + material.specular_map, vec3f{0.25}); + get_scaled_texture( + values, "eta", material.eta, material.eta_map, vec3f{1.5}); + material.roughness = vec2f{0.1f}; + get_pbrt_roughness(values, material.roughness, 0.1); material.sspecular = material.specular * eta_to_reflectivity(material.eta); - } else if (bsdffile == "leather.bsdf") { - material.diffuse = {0.6f, 0.57f, 0.48f}; - material.specular = {1, 1, 1}; - material.eta = vec3f{1.5}; - material.roughness = vec2f{0.3}; - // material.roughness = get_pbrt_roughnessf(0.3, true); + } else if (material.type == "translucent") { + get_scaled_texture( + values, "Kd", material.diffuse, material.diffuse_map, vec3f{0.25}); + get_scaled_texture(values, "Ks", material.specular, + material.specular_map, vec3f{0.25}); + get_scaled_texture( + values, "eta", material.eta, material.eta_map, vec3f{1.5}); + get_pbrt_roughness(values, material.roughness, 0.1); material.sspecular = material.specular * eta_to_reflectivity(material.eta); - } else if (bsdffile == "coated_copper.bsdf") { - material.specular = vec3f{1}; - material.eta = vec3f{0.2004376970f, 0.9240334304f, 1.1022119527f}; - material.etak = vec3f{3.9129485033f, 2.4528477015f, 2.1421879552f}; - material.roughness = vec2f{0.01}; - // material.roughness = get_pbrt_roughnessf(0.01, true); + } else if (material.type == "matte") { + get_scaled_texture( + values, "Kd", material.diffuse, material.diffuse_map, vec3f{0.5}); + material.roughness = vec2f{1}; + } else if (material.type == "mirror") { + get_scaled_texture( + values, "Kr", material.specular, material.specular_map, vec3f{0.9}); + material.eta = zero3f; + material.etak = zero3f; + material.roughness = zero2f; + material.sspecular = material.specular; + } else if (material.type == "metal") { + get_scaled_texture( + values, "Kr", material.specular, material.specular_map, vec3f{1}); + get_scaled_texture(values, "eta", material.eta, material.eta_map, + vec3f{0.2004376970f, 0.9240334304f, 1.1022119527f}); + get_scaled_texture(values, "k", material.etak, material.etak_map, + vec3f{3.9129485033f, 2.4528477015f, 2.1421879552f}); + material.roughness = vec2f{0.01f}; + get_pbrt_roughness(values, material.roughness, 0.01); material.sspecular = material.specular * eta_to_reflectivity(material.eta, material.etak); - } else if (bsdffile == "roughglass_alpha_0.2.bsdf") { - material.specular = {1, 1, 1}; - material.eta = vec3f{1.5}; - material.transmission = {1, 1, 1}; - material.roughness = vec2f{0.2}; - // material.roughness = get_pbrt_roughness(0.2, true); + } else if (material.type == "substrate") { + get_scaled_texture( + values, "Kd", material.diffuse, material.diffuse_map, vec3f{0.5}); + get_scaled_texture( + values, "Ks", material.specular, material.specular_map, vec3f{0.5}); + get_scaled_texture( + values, "eta", material.eta, material.eta_map, vec3f{1.5}); + material.roughness = vec2f{0.1f}; + get_pbrt_roughness(values, material.roughness, 0.1); material.sspecular = material.specular * eta_to_reflectivity(material.eta); - } else if (bsdffile == "roughgold_alpha_0.2.bsdf") { - material.specular = vec3f{1, 1, 1}; - material.eta = vec3f{0.1431189557f, 0.3749570432f, 1.4424785571f}; - material.etak = vec3f{3.9831604247f, 2.3857207478f, 1.6032152899f}; - material.roughness = vec2f{0.2}; - // material.roughness = get_pbrt_roughness(0.2, true); + } else if (material.type == "glass") { + get_scaled_texture( + values, "Kr", material.specular, material.specular_map, vec3f{1}); + get_scaled_texture(values, "Kt", material.transmission, + material.transmission_map, vec3f{1}); + get_scaled_texture( + values, "eta", material.eta, material.eta_map, vec3f{1.5}); + material.roughness = vec2f{0}; + get_pbrt_roughness(values, material.roughness, 0); material.sspecular = material.specular * - eta_to_reflectivity(material.eta, material.etak); + eta_to_reflectivity(material.eta); + material.refract = true; + } else if (material.type == "hair") { + get_scaled_texture( + values, "color", material.diffuse, material.diffuse_map, vec3f{0}); + material.roughness = {1, 1}; + if (verbose) printf("hair material not properly supported\n"); + } else if (material.type == "disney") { + get_scaled_texture(values, "color", material.diffuse, + material.diffuse_map, vec3f{0.5}); + material.roughness = {1, 1}; + if (verbose) printf("disney material not properly supported\n"); + } else if (material.type == "kdsubsurface") { + get_scaled_texture( + values, "Kd", material.diffuse, material.diffuse_map, vec3f{0.5}); + get_scaled_texture( + values, "Kr", material.specular, material.specular_map, vec3f{1}); + get_scaled_texture( + values, "eta", material.eta, material.eta_map, vec3f{1.5}); + material.roughness = vec2f{0}; + get_pbrt_roughness(values, material.roughness, 0); + material.sspecular = material.specular * + eta_to_reflectivity(material.eta); + if (verbose) printf("kdsubsurface material not properly supported\n"); + } else if (material.type == "subsurface") { + get_scaled_texture( + values, "Kr", material.specular, material.specular_map, vec3f{1}); + get_scaled_texture(values, "Kt", material.transmission, + material.transmission_map, vec3f{1}); + get_scaled_texture( + values, "eta", material.eta, material.eta_map, vec3f{1.5}); + material.roughness = vec2f{0}; + get_pbrt_roughness(values, material.roughness, 0); + material.sspecular = material.specular * + eta_to_reflectivity(material.eta); + auto scale = 1.0f; + get_pbrt_value(values, "scale", scale); + material.volscale = 1 / scale; + auto sigma_a = zero3f, sigma_s = zero3f; + auto sigma_a_tex = ""s, sigma_s_tex = ""s; + get_scaled_texture( + values, "sigma_a", sigma_a, sigma_a_tex, vec3f{0011, .0024, .014}); + get_scaled_texture(values, "sigma_prime_s", sigma_s, sigma_s_tex, + vec3f{2.55, 3.12, 3.77}); + material.volmeanfreepath = 1 / (sigma_a + sigma_s); + material.volscatter = sigma_s / (sigma_a + sigma_s); + if (verbose) printf("subsurface material not properly supported\n"); + } else if (material.type == "mix") { + auto namedmaterial1 = ""s, namedmaterial2 = ""s; + get_pbrt_value(values, "namedmaterial1", namedmaterial1); + get_pbrt_value(values, "namedmaterial2", namedmaterial2); + auto matname = (!namedmaterial1.empty()) ? namedmaterial1 + : namedmaterial2; + auto matit = std::find_if(materials.begin(), materials.end(), + [&matname](auto& material) { return material.name == matname; }); + if (matit == materials.end()) + throw std::invalid_argument("cannot find material " + matname); + auto saved_name = material.name; + material = *matit; + material.name = saved_name; + if (verbose) printf("mix material not properly supported\n"); + } else if (material.type == "fourier") { + auto bsdffile = ""s; + get_pbrt_value(values, "bsdffile", bsdffile); + if (bsdffile.rfind("/") != string::npos) + bsdffile = bsdffile.substr(bsdffile.rfind("/") + 1); + if (bsdffile == "paint.bsdf") { + material.diffuse = {0.6f, 0.6f, 0.6f}; + material.specular = {1, 1, 1}; + material.eta = vec3f{1.5}; + material.roughness = vec2f{0.2}; + // material.roughness = get_pbrt_roughnessf(0.2f, true); + material.sspecular = material.specular * + eta_to_reflectivity(material.eta); + } else if (bsdffile == "ceramic.bsdf") { + material.diffuse = {0.6f, 0.6f, 0.6f}; + material.specular = {1, 1, 1}; + material.eta = vec3f{1.5}; + material.roughness = vec2f{0.25}; + // material.roughness = get_pbrt_roughnessf(0.25, true); + material.sspecular = material.specular * + eta_to_reflectivity(material.eta); + } else if (bsdffile == "leather.bsdf") { + material.diffuse = {0.6f, 0.57f, 0.48f}; + material.specular = {1, 1, 1}; + material.eta = vec3f{1.5}; + material.roughness = vec2f{0.3}; + // material.roughness = get_pbrt_roughnessf(0.3, true); + material.sspecular = material.specular * + eta_to_reflectivity(material.eta); + } else if (bsdffile == "coated_copper.bsdf") { + material.specular = vec3f{1}; + material.eta = vec3f{0.2004376970f, 0.9240334304f, 1.1022119527f}; + material.etak = vec3f{3.9129485033f, 2.4528477015f, 2.1421879552f}; + material.roughness = vec2f{0.01}; + // material.roughness = get_pbrt_roughnessf(0.01, true); + material.sspecular = material.specular * + eta_to_reflectivity(material.eta, material.etak); + } else if (bsdffile == "roughglass_alpha_0.2.bsdf") { + material.specular = {1, 1, 1}; + material.eta = vec3f{1.5}; + material.transmission = {1, 1, 1}; + material.roughness = vec2f{0.2}; + // material.roughness = get_pbrt_roughness(0.2, true); + material.sspecular = material.specular * + eta_to_reflectivity(material.eta); + } else if (bsdffile == "roughgold_alpha_0.2.bsdf") { + material.specular = vec3f{1, 1, 1}; + material.eta = vec3f{0.1431189557f, 0.3749570432f, 1.4424785571f}; + material.etak = vec3f{3.9831604247f, 2.3857207478f, 1.6032152899f}; + material.roughness = vec2f{0.2}; + // material.roughness = get_pbrt_roughness(0.2, true); + material.sspecular = material.specular * + eta_to_reflectivity(material.eta, material.etak); + } else { + throw std::invalid_argument{"unknown bsdffile " + bsdffile}; + } } else { - return {filename + ": unsupported bsdffile " + bsdffile}; + throw std::invalid_argument{"unknown material type " + material.type}; } - } else { - return {filename + ": unsupported material type" + material.type}; } + } catch (std::invalid_argument& e) { + throw std::runtime_error{filename + ": conversion error"}; } - return {}; } // Make a triangle shape from a quad grid @@ -4297,156 +3255,151 @@ static void make_pbrt_quad(vector& triangles, vector& positions, } // Convert pbrt shapes -static pbrtio_status convert_pbrt_shapes( +static void convert_pbrt_shapes( const string& filename, vector& shapes, bool verbose = false) { for (auto& shape : shapes) { - auto& values = shape.values; - if (shape.type == "trianglemesh") { - shape.positions = {}; - shape.normals = {}; - shape.texcoords = {}; - shape.triangles = {}; - if (!get_pbrt_value(values, "P", shape.positions)) - return {filename + ": type error"}; - if (!get_pbrt_value(values, "N", shape.normals)) - return {filename + ": type error"}; - if (!get_pbrt_value(values, "uv", shape.texcoords)) - return {filename + ": type error"}; - for (auto& uv : shape.texcoords) uv.y = (1 - uv.y); - if (!get_pbrt_value(values, "indices", shape.triangles)) - return {filename + ": type error"}; - } else if (shape.type == "loopsubdiv") { - shape.positions = {}; - shape.triangles = {}; - if (!get_pbrt_value(values, "P", shape.positions)) - return {filename + ": type error"}; - if (!get_pbrt_value(values, "indices", shape.triangles)) - return {filename + ": type error"}; - shape.normals.resize(shape.positions.size()); - // compute_normals(shape.normals, shape.triangles, shape.positions); - } else if (shape.type == "plymesh") { - shape.filename = ""s; - if (!get_pbrt_value(values, "filename", shape.filename)) - return {filename + ": type error"}; - } else if (shape.type == "sphere") { - auto radius = 1.0f; - if (!get_pbrt_value(values, "radius", radius)) - return {filename + ": type error"}; - make_pbrt_sphere(shape.triangles, shape.positions, shape.normals, - shape.texcoords, {32, 16}, radius); - } else if (shape.type == "disk") { - auto radius = 1.0f; - if (!get_pbrt_value(values, "radius", radius)) - return {filename + ": type error"}; - make_pbrt_disk(shape.triangles, shape.positions, shape.normals, - shape.texcoords, {32, 1}, radius); - } else { - return {filename + ": unsupported shape type " + shape.type}; + try { + auto& values = shape.values; + if (shape.type == "trianglemesh") { + shape.positions = {}; + shape.normals = {}; + shape.texcoords = {}; + shape.triangles = {}; + get_pbrt_value(values, "P", shape.positions); + get_pbrt_value(values, "N", shape.normals); + get_pbrt_value(values, "uv", shape.texcoords); + for (auto& uv : shape.texcoords) uv.y = (1 - uv.y); + get_pbrt_value(values, "indices", shape.triangles); + } else if (shape.type == "loopsubdiv") { + shape.positions = {}; + shape.triangles = {}; + get_pbrt_value(values, "P", shape.positions); + get_pbrt_value(values, "indices", shape.triangles); + shape.normals.resize(shape.positions.size()); + // compute_normals(shape.normals, shape.triangles, shape.positions); + } else if (shape.type == "plymesh") { + shape.filename = ""s; + get_pbrt_value(values, "filename", shape.filename); + } else if (shape.type == "sphere") { + auto radius = 1.0f; + get_pbrt_value(values, "radius", radius); + make_pbrt_sphere(shape.triangles, shape.positions, shape.normals, + shape.texcoords, {32, 16}, radius); + } else if (shape.type == "disk") { + auto radius = 1.0f; + get_pbrt_value(values, "radius", radius); + make_pbrt_disk(shape.triangles, shape.positions, shape.normals, + shape.texcoords, {32, 1}, radius); + } else { + throw std::invalid_argument{"unknown shape " + shape.type}; + } + } catch (std::invalid_argument& e) { + throw std::runtime_error{filename + ": conversion error"}; } } - return {}; } // Convert pbrt arealights -static pbrtio_status convert_pbrt_arealights(const string& filename, +static void convert_pbrt_arealights(const string& filename, vector& lights, bool verbose = false) { for (auto& light : lights) { - auto& values = light.values; - if (light.type == "diffuse") { - auto l = vec3f{1}, scale = vec3f{1}; - if (!get_pbrt_value(values, "L", l)) return {filename + ": type error"}; - if (!get_pbrt_value(values, "scale", scale)) - return {filename + ": type error"}; - light.emission = l * scale; - } else { - return {filename + ": unsupported arealight type " + light.type}; + try { + auto& values = light.values; + if (light.type == "diffuse") { + auto l = vec3f{1}, scale = vec3f{1}; + get_pbrt_value(values, "L", l); + get_pbrt_value(values, "scale", scale); + light.emission = l * scale; + } else { + throw std::invalid_argument{"unknown arealight " + light.type}; + } + } catch (std::invalid_argument& e) { + throw std::runtime_error{filename + ": conversion error"}; } } - return {}; } // Convert pbrt lights -static pbrtio_status convert_pbrt_lights( +static void convert_pbrt_lights( const string& filename, vector& lights, bool verbose = false) { for (auto& light : lights) { - auto& values = light.values; - if (light.type == "distant") { - auto l = vec3f{1}, scale = vec3f{1}; - if (!get_pbrt_value(values, "L", l)) return {filename + ": type error"}; - if (!get_pbrt_value(values, "scale", scale)) - return {filename + ": type error"}; - light.emission = l * scale; - light.from = zero3f; - light.to = vec3f{0, 0, 1}; - if (!get_pbrt_value(values, "from", light.from)) - return {filename + ": type error"}; - if (!get_pbrt_value(values, "to", light.to)) - return {filename + ": type error"}; - light.distant = true; - auto distant_dist = 100; - auto size = distant_dist * sin(5 * pif / 180); - light.area_emission = light.emission * (distant_dist * distant_dist) / - (size * size); - light.area_frame = - light.frame * - lookat_frame(normalize(light.from - light.to) * distant_dist, zero3f, - {0, 1, 0}, true); - light.area_frend = - light.frend * - lookat_frame(normalize(light.from - light.to) * distant_dist, zero3f, - {0, 1, 0}, true); - auto texcoords = vector{}; - make_pbrt_quad(light.area_triangles, light.area_positions, - light.area_normals, texcoords, {4, 2}, size); - } else if (light.type == "point" || light.type == "goniometric" || - light.type == "spot") { - auto i = vec3f{1}, scale = vec3f{1}; - if (!get_pbrt_value(values, "I", i)) return {filename + ": type error"}; - if (!get_pbrt_value(values, "scale", scale)) - return {filename + ": type error"}; - light.emission = i * scale; - light.from = zero3f; - if (!get_pbrt_value(values, "from", light.from)) - return {filename + ": type error"}; - light.area_emission = light.emission; - light.area_frame = light.frame * translation_frame(light.from); - light.area_frend = light.frend * translation_frame(light.from); - auto texcoords = vector{}; - make_pbrt_sphere(light.area_triangles, light.area_positions, - light.area_normals, texcoords, {4, 2}, 0.0025f); - } else { - return {filename + ": unsupported light type " + light.type}; + try { + auto& values = light.values; + if (light.type == "distant") { + auto l = vec3f{1}, scale = vec3f{1}; + get_pbrt_value(values, "L", l); + get_pbrt_value(values, "scale", scale); + light.emission = l * scale; + light.from = zero3f; + light.to = vec3f{0, 0, 1}; + get_pbrt_value(values, "from", light.from); + get_pbrt_value(values, "to", light.to); + light.distant = true; + auto distant_dist = 100; + auto size = distant_dist * sin(5 * pif / 180); + light.area_emission = light.emission * (distant_dist * distant_dist) / + (size * size); + light.area_frame = light.frame * + lookat_frame( + normalize(light.from - light.to) * distant_dist, + zero3f, {0, 1, 0}, true); + light.area_frend = light.frend * + lookat_frame( + normalize(light.from - light.to) * distant_dist, + zero3f, {0, 1, 0}, true); + auto texcoords = vector{}; + make_pbrt_quad(light.area_triangles, light.area_positions, + light.area_normals, texcoords, {4, 2}, size); + } else if (light.type == "point" || light.type == "goniometric" || + light.type == "spot") { + auto i = vec3f{1}, scale = vec3f{1}; + get_pbrt_value(values, "I", i); + get_pbrt_value(values, "scale", scale); + light.emission = i * scale; + light.from = zero3f; + get_pbrt_value(values, "from", light.from); + light.area_emission = light.emission; + light.area_frame = light.frame * translation_frame(light.from); + light.area_frend = light.frend * translation_frame(light.from); + auto texcoords = vector{}; + make_pbrt_sphere(light.area_triangles, light.area_positions, + light.area_normals, texcoords, {4, 2}, 0.0025f); + } else { + throw std::invalid_argument{"unknown light " + light.type}; + } + } catch (std::invalid_argument& e) { + throw std::runtime_error{filename + ": conversion error"}; } } - return {}; } -static pbrtio_status convert_pbrt_environments(const string& filename, +static void convert_pbrt_environments(const string& filename, vector& environments, vector& textures, bool verbose = false) { for (auto& light : environments) { - auto& values = light.values; - if (light.type == "infinite") { - auto l = vec3f{1}, scale = vec3f{1}; - if (!get_pbrt_value(values, "L", l)) return {filename + ": type error"}; - if (!get_pbrt_value(values, "scale", scale)) - return {filename + ": type error"}; - light.emission = scale * l; - light.filename = ""s; - if (!get_pbrt_value(values, "mapname", light.filename)) - return {filename + ": type error"}; - // environment.frame = - // frame3f{{1,0,0},{0,0,-1},{0,-1,0},{0,0,0}} - // * stack.back().frame; - light.frame = light.frame * - frame3f{{1, 0, 0}, {0, 0, 1}, {0, 1, 0}, {0, 0, 0}}; - light.frend = light.frend * - frame3f{{1, 0, 0}, {0, 0, 1}, {0, 1, 0}, {0, 0, 0}}; - } else { - return {filename + ": unsupported environment type " + light.type}; + try { + auto& values = light.values; + if (light.type == "infinite") { + auto l = vec3f{1}, scale = vec3f{1}; + get_pbrt_value(values, "L", l); + get_pbrt_value(values, "scale", scale); + light.emission = scale * l; + light.filename = ""s; + get_pbrt_value(values, "mapname", light.filename); + // environment.frame = + // frame3f{{1,0,0},{0,0,-1},{0,-1,0},{0,0,0}} + // * stack.back().frame; + light.frame = light.frame * + frame3f{{1, 0, 0}, {0, 0, 1}, {0, 1, 0}, {0, 0, 0}}; + light.frend = light.frend * + frame3f{{1, 0, 0}, {0, 0, 1}, {0, 1, 0}, {0, 0, 0}}; + } else { + throw std::invalid_argument{"unknown light " + light.type}; + } + } catch (std::invalid_argument& e) { + throw std::runtime_error{filename + ": conversion error"}; } } - return {}; } // pbrt stack ctm @@ -4471,11 +3424,8 @@ struct pbrt_context { }; // load pbrt -pbrtio_status load_pbrt( - const string& filename, pbrt_model& pbrt, pbrt_context& ctx) { - auto fs = fopen(filename.c_str(), "rt"); - if (!fs) return {filename + ": file not found"}; - auto fs_guard = std::unique_ptr{fs, fclose}; +void load_pbrt(const string& filename, pbrt_model& pbrt, pbrt_context& ctx) { + auto fs = open_file(filename, "rt"); // parser state auto& stack = ctx.stack; @@ -4497,44 +3447,40 @@ pbrtio_status load_pbrt( if (stack.empty()) stack.emplace_back(); // parse command by command - auto line = ""s; - auto line_num = 0; - auto parse_error = [&line_num, &filename]() -> pbrtio_status { - return {filename + ": parse error at line " + std::to_string(line_num)}; - }; - while (read_pbrt_cmdline(fs, line, line_num)) { + auto line = ""s; + while (read_pbrt_cmdline(fs, line)) { auto str = string_view{line}; // get command auto cmd = ""s; - if (!parse_pbrt_command(str, cmd)) return parse_error(); + parse_pbrt_command(fs, str, cmd); if (cmd == "WorldBegin") { stack.push_back({}); } else if (cmd == "WorldEnd") { - if (stack.empty()) return {filename + ": bad stack"}; + if (stack.empty()) throw_parse_error(fs, "bad stack"); stack.pop_back(); - if (stack.size() != 1) return {filename + ": bad stack"}; + if (stack.size() != 1) throw_parse_error(fs, "bad stack"); } else if (cmd == "AttributeBegin") { stack.push_back(stack.back()); } else if (cmd == "AttributeEnd") { - if (stack.empty()) return {filename + ": bad stack"}; + if (stack.empty()) throw_parse_error(fs, "bad stack"); stack.pop_back(); } else if (cmd == "TransformBegin") { stack.push_back(stack.back()); } else if (cmd == "TransformEnd") { - if (stack.empty()) return {filename + ": bad stack"}; + if (stack.empty()) throw_parse_error(fs, "bad stack"); stack.pop_back(); } else if (cmd == "ObjectBegin") { stack.push_back(stack.back()); - if (!parse_pbrt_param(str, cur_object)) return parse_error(); + parse_pbrt_param(fs, str, cur_object); objects[cur_object] = {}; } else if (cmd == "ObjectEnd") { stack.pop_back(); cur_object = ""; } else if (cmd == "ObjectInstance") { auto object = ""s; - if (!parse_pbrt_param(str, object)) return parse_error(); + parse_pbrt_param(fs, str, object); if (objects.find(object) == objects.end()) - return {filename + ": unknown object " + object}; + throw_parse_error(fs, "unknown object " + object); for (auto shape_id : objects.at(object)) { auto& shape = pbrt.shapes[shape_id]; shape.instance_frames.push_back(stack.back().transform_start); @@ -4542,7 +3488,7 @@ pbrtio_status load_pbrt( } } else if (cmd == "ActiveTransform") { auto name = ""s; - if (!parse_pbrt_command(str, name)) return parse_error(); + parse_pbrt_command(str, name); if (name == "StartTime") { stack.back().active_transform_start = true; stack.back().active_transform_end = false; @@ -4553,89 +3499,89 @@ pbrtio_status load_pbrt( stack.back().active_transform_start = true; stack.back().active_transform_end = true; } else { - return {filename + ": bad active transform"}; + throw_parse_error(fs, "bad coordsys"); } } else if (cmd == "Transform") { auto xf = identity4x4f; - if (!parse_pbrt_param(str, xf)) return parse_error(); + parse_pbrt_param(fs, str, xf); set_transform(stack.back(), frame3f{xf}); } else if (cmd == "ConcatTransform") { auto xf = identity4x4f; - if (!parse_pbrt_param(str, xf)) return parse_error(); + parse_pbrt_param(fs, str, xf); concat_transform(stack.back(), frame3f{xf}); } else if (cmd == "Scale") { auto v = zero3f; - if (!parse_pbrt_param(str, v)) return parse_error(); + parse_pbrt_param(fs, str, v); concat_transform(stack.back(), scaling_frame(v)); } else if (cmd == "Translate") { auto v = zero3f; - if (!parse_pbrt_param(str, v)) return parse_error(); + parse_pbrt_param(fs, str, v); concat_transform(stack.back(), translation_frame(v)); } else if (cmd == "Rotate") { auto v = zero4f; - if (!parse_pbrt_param(str, v)) return parse_error(); + parse_pbrt_param(fs, str, v); concat_transform( stack.back(), rotation_frame(vec3f{v.y, v.z, v.w}, radians(v.x))); } else if (cmd == "LookAt") { auto from = zero3f, to = zero3f, up = zero3f; - if (!parse_pbrt_param(str, from)) return parse_error(); - if (!parse_pbrt_param(str, to)) return parse_error(); - if (!parse_pbrt_param(str, up)) return parse_error(); + parse_pbrt_param(fs, str, from); + parse_pbrt_param(fs, str, to); + parse_pbrt_param(fs, str, up); auto frame = lookat_frame(from, to, up, true); concat_transform(stack.back(), inverse(frame)); } else if (cmd == "ReverseOrientation") { stack.back().reverse = !stack.back().reverse; } else if (cmd == "CoordinateSystem") { auto name = ""s; - if (!parse_pbrt_param(str, name)) return parse_error(); + parse_pbrt_param(fs, str, name); coordsys[name].transform_start = stack.back().transform_start; coordsys[name].transform_end = stack.back().transform_end; } else if (cmd == "CoordSysTransform") { auto name = ""s; - if (!parse_pbrt_param(str, name)) return parse_error(); + parse_pbrt_param(fs, str, name); if (coordsys.find(name) != coordsys.end()) { stack.back().transform_start = coordsys.at(name).transform_start; stack.back().transform_end = coordsys.at(name).transform_end; } } else if (cmd == "Integrator") { auto& integrator = pbrt.integrators.emplace_back(); - if (!parse_pbrt_param(str, integrator.type)) return parse_error(); - if (!parse_pbrt_params(str, integrator.values)) return parse_error(); + parse_pbrt_param(fs, str, integrator.type); + parse_pbrt_params(fs, str, integrator.values); } else if (cmd == "Sampler") { auto& sampler = pbrt.samplers.emplace_back(); - if (!parse_pbrt_param(str, sampler.type)) return parse_error(); - if (!parse_pbrt_params(str, sampler.values)) return parse_error(); + parse_pbrt_param(fs, str, sampler.type); + parse_pbrt_params(fs, str, sampler.values); } else if (cmd == "PixelFilter") { auto& filter = pbrt.filters.emplace_back(); - if (!parse_pbrt_param(str, filter.type)) return parse_error(); - if (!parse_pbrt_params(str, filter.values)) return parse_error(); + parse_pbrt_param(fs, str, filter.type); + parse_pbrt_params(fs, str, filter.values); } else if (cmd == "Film") { auto& film = pbrt.films.emplace_back(); - if (!parse_pbrt_param(str, film.type)) return parse_error(); - if (!parse_pbrt_params(str, film.values)) return parse_error(); + parse_pbrt_param(fs, str, film.type); + parse_pbrt_params(fs, str, film.values); } else if (cmd == "Accelerator") { auto& accelerator = pbrt.accelerators.emplace_back(); - if (!parse_pbrt_param(str, accelerator.type)) return parse_error(); - if (!parse_pbrt_params(str, accelerator.values)) return parse_error(); + parse_pbrt_param(fs, str, accelerator.type); + parse_pbrt_params(fs, str, accelerator.values); } else if (cmd == "Camera") { auto& camera = pbrt.cameras.emplace_back(); - if (!parse_pbrt_param(str, camera.type)) return parse_error(); - if (!parse_pbrt_params(str, camera.values)) return parse_error(); + parse_pbrt_param(fs, str, camera.type); + parse_pbrt_params(fs, str, camera.values); camera.frame = stack.back().transform_start; camera.frend = stack.back().transform_end; } else if (cmd == "Texture") { auto& texture = pbrt.textures.emplace_back(); auto comptype = ""s; - if (!parse_pbrt_param(str, texture.name)) return parse_error(); - if (!parse_pbrt_param(str, comptype)) return parse_error(); - if (!parse_pbrt_param(str, texture.type)) return parse_error(); - if (!parse_pbrt_params(str, texture.values)) return parse_error(); + parse_pbrt_param(fs, str, texture.name); + parse_pbrt_param(fs, str, comptype); + parse_pbrt_param(fs, str, texture.type); + parse_pbrt_params(fs, str, texture.values); } else if (cmd == "Material") { static auto material_id = 0; auto& material = pbrt.materials.emplace_back(); material.name = "material_" + std::to_string(material_id++); - if (!parse_pbrt_param(str, material.type)) return parse_error(); - if (!parse_pbrt_params(str, material.values)) return parse_error(); + parse_pbrt_param(fs, str, material.type); + parse_pbrt_params(fs, str, material.values); if (material.type == "") { stack.back().material = ""; pbrt.materials.pop_back(); @@ -4644,17 +3590,17 @@ pbrtio_status load_pbrt( } } else if (cmd == "MakeNamedMaterial") { auto& material = pbrt.materials.emplace_back(); - if (!parse_pbrt_param(str, material.name)) return parse_error(); - if (!parse_pbrt_params(str, material.values)) return parse_error(); + parse_pbrt_param(fs, str, material.name); + parse_pbrt_params(fs, str, material.values); material.type = ""; for (auto& value : material.values) if (value.name == "type") material.type = value.value1s; } else if (cmd == "NamedMaterial") { - if (!parse_pbrt_param(str, stack.back().material)) return parse_error(); + parse_pbrt_param(fs, str, stack.back().material); } else if (cmd == "Shape") { auto& shape = pbrt.shapes.emplace_back(); - if (!parse_pbrt_param(str, shape.type)) return parse_error(); - if (!parse_pbrt_params(str, shape.values)) return parse_error(); + parse_pbrt_param(fs, str, shape.type); + parse_pbrt_params(fs, str, shape.values); shape.frame = stack.back().transform_start; shape.frend = stack.back().transform_end; shape.material = stack.back().material; @@ -4672,15 +3618,15 @@ pbrtio_status load_pbrt( static auto arealight_id = 0; auto& arealight = pbrt.arealights.emplace_back(); arealight.name = "arealight_" + std::to_string(arealight_id++); - if (!parse_pbrt_param(str, arealight.type)) return parse_error(); - if (!parse_pbrt_params(str, arealight.values)) return parse_error(); + parse_pbrt_param(fs, str, arealight.type); + parse_pbrt_params(fs, str, arealight.values); arealight.frame = stack.back().transform_start; arealight.frend = stack.back().transform_end; stack.back().arealight = arealight.name; } else if (cmd == "LightSource") { auto& light = pbrt.lights.emplace_back(); - if (!parse_pbrt_param(str, light.type)) return parse_error(); - if (!parse_pbrt_params(str, light.values)) return parse_error(); + parse_pbrt_param(fs, str, light.type); + parse_pbrt_params(fs, str, light.values); light.frame = stack.back().transform_start; light.frend = stack.back().transform_end; if (light.type == "infinite") { @@ -4693,55 +3639,42 @@ pbrtio_status load_pbrt( } } else if (cmd == "MakeNamedMedium") { auto& medium = pbrt.mediums.emplace_back(); - if (!parse_pbrt_param(str, medium.name)) return parse_error(); - if (!parse_pbrt_params(str, medium.values)) return parse_error(); + parse_pbrt_param(fs, str, medium.name); + parse_pbrt_params(fs, str, medium.values); medium.type = ""; for (auto& value : medium.values) if (value.name == "type") medium.type = value.value1s; } else if (cmd == "MediumInterface") { - if (!parse_pbrt_param(str, stack.back().medium_interior)) - return parse_error(); - if (!parse_pbrt_param(str, stack.back().medium_exterior)) - return parse_error(); + parse_pbrt_param(fs, str, stack.back().medium_interior); + parse_pbrt_param(fs, str, stack.back().medium_exterior); } else if (cmd == "Include") { auto includename = ""s; - if (!parse_pbrt_param(str, includename)) return parse_error(); - if (auto ret = load_pbrt(get_dirname(filename) + includename, pbrt, ctx); - !ret) - return ret; + parse_pbrt_param(fs, str, includename); + try { + load_pbrt(get_dirname(filename) + includename, pbrt, ctx); + } catch (std::exception& e) { + throw_dependent_error(fs, e.what()); + } } else { - return {filename + ": unknown command " + cmd}; + throw_parse_error(fs, "unknown command " + cmd); } } - - return {}; } // load pbrt -pbrtio_status load_pbrt(const string& filename, pbrt_model& pbrt) { +void load_pbrt(const string& filename, pbrt_model& pbrt) { auto ctx = pbrt_context{}; - if (auto ret = load_pbrt(filename, pbrt, ctx); !ret) return ret; + load_pbrt(filename, pbrt, ctx); // convert objects - if (auto ret = convert_pbrt_films(filename, pbrt.films); !ret) return ret; - if (auto ret = convert_pbrt_cameras(filename, pbrt.cameras, pbrt.films); !ret) - return ret; - if (auto ret = convert_pbrt_textures(filename, pbrt.textures); !ret) - return ret; - if (auto ret = convert_pbrt_materials( - filename, pbrt.materials, pbrt.textures); - !ret) - return ret; - if (auto ret = convert_pbrt_shapes(filename, pbrt.shapes); !ret) return ret; - if (auto ret = convert_pbrt_lights(filename, pbrt.lights); !ret) return ret; - if (auto ret = convert_pbrt_arealights(filename, pbrt.arealights); !ret) - return ret; - if (auto ret = convert_pbrt_environments( - filename, pbrt.environments, pbrt.textures); - !ret) - return ret; - - return {}; + convert_pbrt_films(filename, pbrt.films); + convert_pbrt_cameras(filename, pbrt.cameras, pbrt.films); + convert_pbrt_textures(filename, pbrt.textures); + convert_pbrt_materials(filename, pbrt.materials, pbrt.textures); + convert_pbrt_shapes(filename, pbrt.shapes); + convert_pbrt_lights(filename, pbrt.lights); + convert_pbrt_arealights(filename, pbrt.arealights); + convert_pbrt_environments(filename, pbrt.environments, pbrt.textures); } static void format_value(string& str, const pbrt_value& value) { @@ -4821,23 +3754,18 @@ static void format_value(string& str, const vector& values) { } } -pbrtio_status save_pbrt(const string& filename, const pbrt_model& pbrt) { - auto fs = fopen(filename.c_str(), "wt"); - if (!fs) return {filename + ": file not found"}; - auto fs_guard = std::unique_ptr{fs, fclose}; +void save_pbrt(const string& filename, const pbrt_model& pbrt) { + auto fs = open_file(filename, "wt"); // save comments - if (!format_values(fs, "#\n")) return {filename + ": write error"}; - if (!format_values(fs, "# Written by Yocto/GL\n")) - return {filename + ": write error"}; - if (!format_values(fs, "# https://github.com/xelatihy/yocto-gl\n")) - return {filename + ": write error"}; - if (!format_values(fs, "#\n\n")) return {filename + ": write error"}; + format_values(fs, "#\n"); + format_values(fs, "# Written by Yocto/GL\n"); + format_values(fs, "# https://github.com/xelatihy/yocto-gl\n"); + format_values(fs, "#\n\n"); for (auto& comment : pbrt.comments) { - if (!format_values(fs, "# {}\n", comment)) - return {filename + ": write error"}; + format_values(fs, "# {}\n", comment); } - if (!format_values(fs, "\n")) return {filename + ": write error"}; + format_values(fs, "\n"); for (auto& camera_ : pbrt.cameras) { auto camera = camera_; @@ -4846,11 +3774,9 @@ pbrtio_status save_pbrt(const string& filename, const pbrt_model& pbrt) { camera.values.push_back(make_pbrt_value( "fov", 2 * tan(0.036f / (2 * camera.lens)) * 180 / pif)); } - if (!format_values(fs, "LookAt {} {} {}\n", camera.frame.o, - camera.frame.o - camera.frame.z, camera.frame.y)) - return {filename + ": write error"}; - if (!format_values(fs, "Camera \"{}\" {}\n", camera.type, camera.values)) - return {filename + ": write error"}; + format_values(fs, "LookAt {} {} {}\n", camera.frame.o, + camera.frame.o - camera.frame.z, camera.frame.y); + format_values(fs, "Camera \"{}\" {}\n", camera.type, camera.values); } for (auto& film_ : pbrt.films) { @@ -4861,39 +3787,32 @@ pbrtio_status save_pbrt(const string& filename, const pbrt_model& pbrt) { film.values.push_back(make_pbrt_value("yresolution", film.resolution.y)); film.values.push_back(make_pbrt_value("filename", film.filename)); } - if (!format_values(fs, "Film \"{}\" {}\n", film.type, film.values)) - return {filename + ": write error"}; + format_values(fs, "Film \"{}\" {}\n", film.type, film.values); } for (auto& integrator_ : pbrt.integrators) { auto integrator = integrator_; - if (!format_values( - fs, "Integrator \"{}\" {}\n", integrator.type, integrator.values)) - return {filename + ": write error"}; + format_values( + fs, "Integrator \"{}\" {}\n", integrator.type, integrator.values); } for (auto& sampler_ : pbrt.samplers) { auto sampler = sampler_; - if (!format_values(fs, "Sampler \"{}\" {}\n", sampler.type, sampler.values)) - return {filename + ": write error"}; + format_values(fs, "Sampler \"{}\" {}\n", sampler.type, sampler.values); } for (auto& filter_ : pbrt.filters) { auto filter = filter_; - if (!format_values( - fs, "PixelFilter \"{}\" {}\n", filter.type, filter.values)) - return {filename + ": write error"}; + format_values(fs, "PixelFilter \"{}\" {}\n", filter.type, filter.values); } for (auto& accelerator_ : pbrt.accelerators) { auto accelerator = accelerator_; - if (!format_values(fs, "Accelerator \"{}\" {}\n", accelerator.type, - accelerator.values)) - return {filename + ": write error"}; + format_values( + fs, "Accelerator \"{}\" {}\n", accelerator.type, accelerator.values); } - if (!format_values(fs, "\nWorldBegin\n\n")) - return {filename + ": write error"}; + format_values(fs, "\nWorldBegin\n\n"); for (auto& texture_ : pbrt.textures) { auto texture = texture_; @@ -4906,9 +3825,8 @@ pbrtio_status save_pbrt(const string& filename, const pbrt_model& pbrt) { texture.values.push_back(make_pbrt_value("filename", texture.filename)); } } - if (!format_values(fs, "Texture \"{}\" \"color\" \"{}\" {}\n", texture.name, - texture.type, texture.values)) - return {filename + ": write error"}; + format_values(fs, "Texture \"{}\" \"color\" \"{}\" {}\n", texture.name, + texture.type, texture.values); } auto reflectivity_to_eta = [](const vec3f& reflectivity) { @@ -4964,17 +3882,14 @@ pbrtio_status save_pbrt(const string& filename, const pbrt_model& pbrt) { } } } - if (!format_values(fs, - "MakeNamedMaterial \"{}\" \"string type\" \"{}\" {}\n", - material.name, material.type, material.values)) - return {filename + ": write error"}; + format_values(fs, "MakeNamedMaterial \"{}\" \"string type\" \"{}\" {}\n", + material.name, material.type, material.values); } for (auto& medium_ : pbrt.mediums) { auto medium = medium_; - if (!format_values(fs, "MakeNamedMedium \"{}\" \"string type\" \"{}\" {}\n", - medium.name, medium.type, medium.values)) - return {filename + ": write error"}; + format_values(fs, "MakeNamedMedium \"{}\" \"string type\" \"{}\" {}\n", + medium.name, medium.type, medium.values); } for (auto& light_ : pbrt.lights) { @@ -4988,14 +3903,10 @@ pbrtio_status save_pbrt(const string& filename, const pbrt_model& pbrt) { light.values.push_back(make_pbrt_value("I", light.emission)); } } - if (!format_values(fs, "AttributeBegin\n")) - return {filename + ": write error"}; - if (!format_values(fs, "Transform {}\n", (mat4f)light.frame)) - return {filename + ": write error"}; - if (!format_values(fs, "LightSource \"{}\" {}\n", light.type, light.values)) - return {filename + ": write error"}; - if (!format_values(fs, "AttributeEnd\n")) - return {filename + ": write error"}; + format_values(fs, "AttributeBegin\n"); + format_values(fs, "Transform {}\n", (mat4f)light.frame); + format_values(fs, "LightSource \"{}\" {}\n", light.type, light.values); + format_values(fs, "AttributeEnd\n"); } for (auto& environment_ : pbrt.environments) { @@ -5006,15 +3917,11 @@ pbrtio_status save_pbrt(const string& filename, const pbrt_model& pbrt) { environment.values.push_back( make_pbrt_value("mapname", environment.filename)); } - if (!format_values(fs, "AttributeBegin\n")) - return {filename + ": write error"}; - if (!format_values(fs, "Transform {}\n", (mat4f)environment.frame)) - return {filename + ": write error"}; - if (!format_values(fs, "LightSource \"{}\" {}\n", environment.type, - environment.values)) - return {filename + ": write error"}; - if (!format_values(fs, "AttributeEnd\n")) - return {filename + ": write error"}; + format_values(fs, "AttributeBegin\n"); + format_values(fs, "Transform {}\n", (mat4f)environment.frame); + format_values( + fs, "LightSource \"{}\" {}\n", environment.type, environment.values); + format_values(fs, "AttributeEnd\n"); } auto arealights_map = unordered_map{}; @@ -5048,591 +3955,88 @@ pbrtio_status save_pbrt(const string& filename, const pbrt_model& pbrt) { } } auto object = "object" + std::to_string(object_id++); - if (shape.is_instanced) - if (!format_values(fs, "ObjectBegin \"{}\"\n", object)) - return {filename + ": write error"}; - if (!format_values(fs, "AttributeBegin\n")) - return {filename + ": write error"}; - if (!format_values(fs, "Transform {}\n", (mat4f)shape.frame)) - return {filename + ": write error"}; - if (!format_values(fs, "NamedMaterial \"{}\"\n", shape.material)) - return {filename + ": write error"}; + if (shape.is_instanced) format_values(fs, "ObjectBegin \"{}\"\n", object); + format_values(fs, "AttributeBegin\n"); + format_values(fs, "Transform {}\n", (mat4f)shape.frame); + format_values(fs, "NamedMaterial \"{}\"\n", shape.material); if (shape.arealight != "") - if (!format_values(fs, arealights_map.at(shape.arealight))) - return {filename + ": write error"}; - if (!format_values(fs, "Shape \"{}\" {}\n", shape.type, shape.values)) - return {filename + ": write error"}; - if (!format_values(fs, "AttributeEnd\n")) - return {filename + ": write error"}; - if (shape.is_instanced) - if (!format_values(fs, "ObjectEnd\n")) - return {filename + ": write error"}; + format_values(fs, arealights_map.at(shape.arealight)); + format_values(fs, "Shape \"{}\" {}\n", shape.type, shape.values); + format_values(fs, "AttributeEnd\n"); + if (shape.is_instanced) format_values(fs, "ObjectEnd\n"); for (auto& iframe : shape.instance_frames) { - if (!format_values(fs, "AttributeBegin\n")) - return {filename + ": write error"}; - if (!format_values(fs, "Transform {}\n", (mat4f)iframe)) - return {filename + ": write error"}; - if (!format_values(fs, "ObjectInstance \"{}\"\n", object)) - return {filename + ": write error"}; - if (!format_values(fs, "AttributeEnd\n")) - return {filename + ": write error"}; - } - } - - if (!format_values(fs, "\nWorldEnd\n\n")) return {filename + ": write error"}; - - return {}; -} - -// Read pbrt commands -pbrtio_status read_pbrt_command(const string& filename, FILE* fs, - pbrt_command& command, string& name, string& type, frame3f& xform, - vector& values, string& line) { - // parse command by command - auto line_num = 0; - auto parse_error = [&line_num, &filename]() -> pbrtio_status { - return {filename + ": parse error at line " + std::to_string(line_num)}; - }; - while (read_pbrt_cmdline(fs, line, line_num)) { - auto str = string_view{line}; - // get command - auto cmd = ""s; - if (!parse_pbrt_command(str, cmd)) return parse_error(); - if (cmd == "WorldBegin") { - command = pbrt_command::world_begin; - return {}; - } else if (cmd == "WorldEnd") { - command = pbrt_command::world_end; - return {}; - } else if (cmd == "AttributeBegin") { - command = pbrt_command::attribute_begin; - return {}; - } else if (cmd == "AttributeEnd") { - command = pbrt_command::attribute_end; - return {}; - } else if (cmd == "TransformBegin") { - command = pbrt_command::transform_begin; - return {}; - } else if (cmd == "TransformEnd") { - command = pbrt_command::transform_end; - return {}; - } else if (cmd == "ObjectBegin") { - if (!parse_pbrt_param(str, name)) return parse_error(); - command = pbrt_command::object_begin; - return {}; - } else if (cmd == "ObjectEnd") { - command = pbrt_command::object_end; - return {}; - } else if (cmd == "ObjectInstance") { - if (!parse_pbrt_param(str, name)) return parse_error(); - command = pbrt_command::object_instance; - return {}; - } else if (cmd == "ActiveTransform") { - if (!parse_pbrt_command(str, name)) return parse_error(); - command = pbrt_command::active_transform; - return {}; - } else if (cmd == "Transform") { - auto xf = identity4x4f; - if (!parse_pbrt_param(str, xf)) return parse_error(); - xform = frame3f{xf}; - command = pbrt_command::set_transform; - return {}; - } else if (cmd == "ConcatTransform") { - auto xf = identity4x4f; - if (!parse_pbrt_param(str, xf)) return parse_error(); - xform = frame3f{xf}; - command = pbrt_command::concat_transform; - return {}; - } else if (cmd == "Scale") { - auto v = zero3f; - if (!parse_pbrt_param(str, v)) return parse_error(); - xform = scaling_frame(v); - command = pbrt_command::concat_transform; - return {}; - } else if (cmd == "Translate") { - auto v = zero3f; - if (!parse_pbrt_param(str, v)) return parse_error(); - xform = translation_frame(v); - command = pbrt_command::concat_transform; - return {}; - } else if (cmd == "Rotate") { - auto v = zero4f; - if (!parse_pbrt_param(str, v)) return parse_error(); - xform = rotation_frame(vec3f{v.y, v.z, v.w}, radians(v.x)); - command = pbrt_command::concat_transform; - return {}; - } else if (cmd == "LookAt") { - auto from = zero3f, to = zero3f, up = zero3f; - if (!parse_pbrt_param(str, from)) return parse_error(); - if (!parse_pbrt_param(str, to)) return parse_error(); - if (!parse_pbrt_param(str, up)) return parse_error(); - xform = {from, to, up, zero3f}; - command = pbrt_command::lookat_transform; - return {}; - } else if (cmd == "ReverseOrientation") { - command = pbrt_command::reverse_orientation; - return {}; - } else if (cmd == "CoordinateSystem") { - if (!parse_pbrt_param(str, name)) return parse_error(); - command = pbrt_command::coordinate_system_set; - return {}; - } else if (cmd == "CoordSysTransform") { - if (!parse_pbrt_param(str, name)) return parse_error(); - command = pbrt_command::coordinate_system_transform; - return {}; - } else if (cmd == "Integrator") { - if (!parse_pbrt_param(str, type)) return parse_error(); - if (!parse_pbrt_params(str, values)) return parse_error(); - command = pbrt_command::integrator; - return {}; - } else if (cmd == "Sampler") { - if (!parse_pbrt_param(str, type)) return parse_error(); - if (!parse_pbrt_params(str, values)) return parse_error(); - command = pbrt_command::sampler; - return {}; - } else if (cmd == "PixelFilter") { - if (!parse_pbrt_param(str, type)) return parse_error(); - if (!parse_pbrt_params(str, values)) return parse_error(); - command = pbrt_command::filter; - return {}; - } else if (cmd == "Film") { - if (!parse_pbrt_param(str, type)) return parse_error(); - if (!parse_pbrt_params(str, values)) return parse_error(); - command = pbrt_command::film; - return {}; - } else if (cmd == "Accelerator") { - if (!parse_pbrt_param(str, type)) return parse_error(); - if (!parse_pbrt_params(str, values)) return parse_error(); - command = pbrt_command::accelerator; - return {}; - } else if (cmd == "Camera") { - if (!parse_pbrt_param(str, type)) return parse_error(); - if (!parse_pbrt_params(str, values)) return parse_error(); - command = pbrt_command::camera; - return {}; - } else if (cmd == "Texture") { - auto comptype = ""s; - if (!parse_pbrt_param(str, name)) return parse_error(); - if (!parse_pbrt_param(str, comptype)) return parse_error(); - if (!parse_pbrt_param(str, type)) return parse_error(); - if (!parse_pbrt_params(str, values)) return parse_error(); - command = pbrt_command::named_texture; - return {}; - } else if (cmd == "Material") { - if (!parse_pbrt_param(str, type)) return parse_error(); - if (!parse_pbrt_params(str, values)) return parse_error(); - command = pbrt_command::material; - return {}; - } else if (cmd == "MakeNamedMaterial") { - if (!parse_pbrt_param(str, name)) return parse_error(); - if (!parse_pbrt_params(str, values)) return parse_error(); - type = ""; - for (auto& value : values) - if (value.name == "type") type = value.value1s; - command = pbrt_command::named_material; - return {}; - } else if (cmd == "NamedMaterial") { - if (!parse_pbrt_param(str, name)) return parse_error(); - command = pbrt_command::use_material; - return {}; - } else if (cmd == "Shape") { - if (!parse_pbrt_param(str, type)) return parse_error(); - if (!parse_pbrt_params(str, values)) return parse_error(); - command = pbrt_command::shape; - return {}; - } else if (cmd == "AreaLightSource") { - if (!parse_pbrt_param(str, type)) return parse_error(); - if (!parse_pbrt_params(str, values)) return parse_error(); - command = pbrt_command::arealight; - return {}; - } else if (cmd == "LightSource") { - if (!parse_pbrt_param(str, type)) return parse_error(); - if (!parse_pbrt_params(str, values)) return parse_error(); - command = pbrt_command::light; - return {}; - } else if (cmd == "MakeNamedMedium") { - if (!parse_pbrt_param(str, name)) return parse_error(); - if (!parse_pbrt_params(str, values)) return parse_error(); - type = ""; - for (auto& value : values) - if (value.name == "type") type = value.value1s; - command = pbrt_command::named_medium; - return {}; - } else if (cmd == "MediumInterface") { - auto interior = ""s, exterior = ""s; - if (!parse_pbrt_param(str, interior)) return parse_error(); - if (!parse_pbrt_param(str, exterior)) return parse_error(); - name = interior + "####" + exterior; - command = pbrt_command::medium_interface; - return {}; - } else if (cmd == "Include") { - if (!parse_pbrt_param(str, name)) return parse_error(); - command = pbrt_command::include; - return {}; - } else { - return {filename + ": unknown command " + cmd}; - } - } - - if (ferror(fs)) return {filename + ": read error"}; - - return {"eof"}; -} -pbrtio_status read_pbrt_command(const string& filename, FILE* fs, - pbrt_command& command, string& name, string& type, frame3f& xform, - vector& values) { - auto command_buffer = ""s; - return read_pbrt_command( - filename, fs, command, name, type, xform, values, command_buffer); -} - -vector split_pbrt_string(const string& str, const string& delim) { - auto tokens = vector{}; - auto last = (size_t)0, next = (size_t)0; - while ((next = str.find(delim, last)) != string::npos) { - tokens.push_back(str.substr(last, next - last)); - last = next + delim.size(); - } - if (last < str.size()) tokens.push_back(str.substr(last)); - return tokens; -} - -// Write obj elements -pbrtio_status write_pbrt_comment( - const string& filename, FILE* fs, const string& comment) { - auto lines = split_pbrt_string(comment, "\n"); - for (auto& line : lines) { - if (!format_values(fs, "# {}\n", line)) return {filename + ": write error"}; - } - if (!format_values(fs, "\n")) return {filename + ": write error"}; - return {}; -} - -bool write_pbrt_values( - const string& filename, FILE* fs, const vector& values) { - static auto type_labels = unordered_map{ - {pbrt_value_type::real, "float"}, - {pbrt_value_type::integer, "integer"}, - {pbrt_value_type::boolean, "bool"}, - {pbrt_value_type::string, "string"}, - {pbrt_value_type::point, "point"}, - {pbrt_value_type::normal, "normal"}, - {pbrt_value_type::vector, "vector"}, - {pbrt_value_type::texture, "texture"}, - {pbrt_value_type::color, "rgb"}, - {pbrt_value_type::point2, "point2"}, - {pbrt_value_type::vector2, "vector2"}, - {pbrt_value_type::spectrum, "spectrum"}, - }; - - auto write_error = [](FILE* fs) { - if (!ferror(fs)) return false; - return true; - }; - - for (auto& value : values) { - format_values(fs, " \"{} {}\" ", type_labels.at(value.type), value.name); - switch (value.type) { - case pbrt_value_type::real: - if (!value.vector1f.empty()) { - format_values(fs, "[ "); - for (auto& v : value.vector1f) format_values(fs, " {}", v); - format_values(fs, " ]"); - } else { - format_values(fs, "{}", value.value1f); - } - break; - case pbrt_value_type::integer: - if (!value.vector1f.empty()) { - format_values(fs, "[ "); - for (auto& v : value.vector1i) format_values(fs, " {}", v); - format_values(fs, " ]"); - } else { - format_values(fs, "{}", value.value1i); - } - break; - case pbrt_value_type::boolean: - format_values(fs, "\"{}\"", value.value1b ? "true" : "false"); - break; - case pbrt_value_type::string: - format_values(fs, "\"{}\"", value.value1s); - break; - case pbrt_value_type::point: - case pbrt_value_type::vector: - case pbrt_value_type::normal: - case pbrt_value_type::color: - if (!value.vector3f.empty()) { - format_values(fs, "[ "); - for (auto& v : value.vector3f) format_values(fs, " {}", v); - format_values(fs, " ]"); - } else { - format_values(fs, "[ {} ]", value.value3f); - } - break; - case pbrt_value_type::spectrum: - format_values(fs, "[ "); - for (auto& v : value.vector1f) format_values(fs, " {}", v); - format_values(fs, " ]"); - break; - case pbrt_value_type::texture: - format_values(fs, "\"{}\"", value.value1s); - break; - case pbrt_value_type::point2: - case pbrt_value_type::vector2: - if (!value.vector2f.empty()) { - format_values(fs, "[ "); - for (auto& v : value.vector2f) format_values(fs, " {}", v); - format_values(fs, " ]"); - } else { - format_values(fs, "[ {} ]", value.value2f); - } - break; + format_values(fs, "AttributeBegin\n"); + format_values(fs, "Transform {}\n", (mat4f)iframe); + format_values(fs, "ObjectInstance \"{}\"\n", object); + format_values(fs, "AttributeEnd\n"); } } - format_values(fs, "\n"); - - if (write_error(fs)) return false; - - return true; -} - -pbrtio_status write_pbrt_command(const string& filename, FILE* fs, - pbrt_command command, const string& name, const string& type, - const frame3f& xform, const vector& values, - bool texture_float) { - switch (command) { - case pbrt_command::world_begin: - if (!format_values(fs, "WorldBegin\n")) - return {filename + ": write error"}; - break; - case pbrt_command::world_end: - if (!format_values(fs, "WorldEnd\n")) return {filename + ": write error"}; - break; - case pbrt_command::attribute_begin: - if (!format_values(fs, "AttributeBegin\n")) - return {filename + ": write error"}; - break; - case pbrt_command::attribute_end: - if (!format_values(fs, "AttributeEnd\n")) - return {filename + ": write error"}; - break; - case pbrt_command::transform_begin: - if (!format_values(fs, "TransformBegin\n")) - return {filename + ": write error"}; - break; - case pbrt_command::transform_end: - if (!format_values(fs, "TransformEnd\n")) - return {filename + ": write error"}; - break; - case pbrt_command::object_begin: - if (!format_values(fs, "ObjectBegin \"{}\"\n", name)) - return {filename + ": write error"}; - break; - case pbrt_command::object_end: - if (!format_values(fs, "ObjectEnd\n")) - return {filename + ": write error"}; - break; - case pbrt_command::object_instance: - if (!format_values(fs, "ObjectInstance \"{}\"\n", name)) - return {filename + ": write error"}; - break; - case pbrt_command::sampler: - if (!format_values(fs, "Sampler \"{}\" {}\n", type, values)) - return {filename + ": write error"}; - break; - case pbrt_command::integrator: - if (!format_values(fs, "Integrator \"{}\" {}\n", type, values)) - return {filename + ": write error"}; - break; - case pbrt_command::accelerator: - if (!format_values(fs, "Accelerator \"{}\" {}\n", type, values)) - return {filename + ": write error"}; - break; - case pbrt_command::film: - if (!format_values(fs, "Film \"{}\" {}\n", type, values)) - return {filename + ": write error"}; - break; - case pbrt_command::filter: - if (!format_values(fs, "Filter \"{}\" {}\n", type, values)) - return {filename + ": write error"}; - break; - case pbrt_command::camera: - if (!format_values(fs, "Camera \"{}\" {}\n", type, values)) - return {filename + ": write error"}; - break; - case pbrt_command::shape: - if (!format_values(fs, "Shape \"{}\" {}\n", type, values)) - return {filename + ": write error"}; - break; - case pbrt_command::light: - if (!format_values(fs, "LightSource \"{}\" {}\n", type, values)) - return {filename + ": write error"}; - break; - case pbrt_command::material: - if (!format_values(fs, "Material \"{}\" {}\n", type, values)) - return {filename + ": write error"}; - break; - case pbrt_command::arealight: - if (!format_values(fs, "AreaLightSource \"{}\" {}\n", type, values)) - return {filename + ": write error"}; - break; - case pbrt_command::named_texture: - if (!format_values(fs, "Texture \"{}\" \"{}\" \"{}\" {}\n", name, - texture_float ? "float" : "rgb", type, values)) - return {filename + ": write error"}; - break; - case pbrt_command::named_medium: - if (!format_values(fs, - "MakeNamedMedium \"{}\" \"string type\" \"{}\" {}\n", name, type, - values)) - return {filename + ": write error"}; - break; - case pbrt_command::named_material: - if (!format_values(fs, - "MakeNamedMaterial \"{}\" \"string type\" \"{}\" {}\n", name, - type, values)) - return {filename + ": write error"}; - break; - case pbrt_command::include: - if (!format_values(fs, "Include \"{}\"\n", name)) - return {filename + ": write error"}; - break; - case pbrt_command::reverse_orientation: - if (!format_values(fs, "ReverseOrientation\n")) - return {filename + ": write error"}; - break; - case pbrt_command::set_transform: - if (!format_values(fs, "Transform {}\n", (mat4f)xform)) - return {filename + ": write error"}; - break; - case pbrt_command::concat_transform: - if (!format_values(fs, "ConcatTransform {}\n", (mat4f)xform)) - return {filename + ": write error"}; - break; - case pbrt_command::lookat_transform: - if (!format_values(fs, "LookAt {} {} {}\n", xform.x, xform.y, xform.z)) - return {filename + ": write error"}; - break; - case pbrt_command::use_material: - if (!format_values(fs, "NamedMaterial \"{}\"\n", name)) - return {filename + ": write error"}; - break; - case pbrt_command::medium_interface: { - auto interior = ""s, exterior = ""s; - auto found = false; - for (auto c : name) { - if (c == '#') { - found = true; - continue; - } - if (found) - exterior.push_back(c); - else - interior.push_back(c); - } - if (!format_values( - fs, "MediumInterface \"{}\" \"{}\"\n", interior, exterior)) - return {filename + ": write error"}; - } break; - case pbrt_command::active_transform: - if (!format_values(fs, "ActiveTransform \"{}\"\n", name)) - return {filename + ": write error"}; - break; - case pbrt_command::coordinate_system_set: - if (!format_values(fs, "CoordinateSystem \"{}\"\n", name)) - return {filename + ": write error"}; - break; - case pbrt_command::coordinate_system_transform: - if (!format_values(fs, "CoordinateSysTransform \"{}\"\n", name)) - return {filename + ": write error"}; - break; - case pbrt_command::error: break; - } - return {}; -} - -pbrtio_status write_pbrt_command(const string& filename, FILE* fs, - pbrt_command command, const string& name, const frame3f& xform) { - return write_pbrt_command(filename, fs, command, name, "", xform, {}); -} -pbrtio_status write_pbrt_command(const string& filename, FILE* fs, - pbrt_command command, const string& name, const string& type, - const vector& values, bool texture_as_float) { - return write_pbrt_command(filename, fs, command, name, type, identity3x4f, - values, texture_as_float); + format_values(fs, "\nWorldEnd\n\n"); } // get pbrt value -bool get_pbrt_value(const pbrt_value& pbrt, string& value) { +void get_pbrt_value(const pbrt_value& pbrt, string& value) { if (pbrt.type == pbrt_value_type::string || pbrt.type == pbrt_value_type::texture) { value = pbrt.value1s; - return true; } else { - return false; + throw std::invalid_argument{"expected string"}; } } -bool get_pbrt_value(const pbrt_value& pbrt, bool& value) { +void get_pbrt_value(const pbrt_value& pbrt, bool& value) { if (pbrt.type == pbrt_value_type::boolean) { value = pbrt.value1b; - return true; } else { - return false; + throw std::invalid_argument{"expected bool"}; } } -bool get_pbrt_value(const pbrt_value& pbrt, int& value) { +void get_pbrt_value(const pbrt_value& pbrt, int& value) { if (pbrt.type == pbrt_value_type::integer) { value = pbrt.value1i; - return true; } else { - return false; + throw std::invalid_argument{"expected int"}; } } -bool get_pbrt_value(const pbrt_value& pbrt, float& value) { +void get_pbrt_value(const pbrt_value& pbrt, float& value) { if (pbrt.type == pbrt_value_type::real) { value = pbrt.value1f; - return true; } else { - return false; + throw std::invalid_argument{"expected float"}; } } -bool get_pbrt_value(const pbrt_value& pbrt, vec2f& value) { +void get_pbrt_value(const pbrt_value& pbrt, vec2f& value) { if (pbrt.type == pbrt_value_type::point2 || pbrt.type == pbrt_value_type::vector2) { value = pbrt.value2f; - return true; } else { - return false; + throw std::invalid_argument{"expected float2"}; } } -bool get_pbrt_value(const pbrt_value& pbrt, vec3f& value) { +void get_pbrt_value(const pbrt_value& pbrt, vec3f& value) { if (pbrt.type == pbrt_value_type::point || pbrt.type == pbrt_value_type::vector || pbrt.type == pbrt_value_type::normal || pbrt.type == pbrt_value_type::color) { value = pbrt.value3f; - return true; } else if (pbrt.type == pbrt_value_type::real) { value = vec3f{pbrt.value1f}; - return true; } else { - return false; + throw std::invalid_argument{"expected float3"}; } } -bool get_pbrt_value(const pbrt_value& pbrt, vector& value) { +void get_pbrt_value(const pbrt_value& pbrt, vector& value) { if (pbrt.type == pbrt_value_type::real) { if (!pbrt.vector1f.empty()) { value = pbrt.vector1f; } else { value = {pbrt.value1f}; } - return true; } else { - return false; + throw std::invalid_argument{"expected float array"}; } } -bool get_pbrt_value(const pbrt_value& pbrt, vector& value) { +void get_pbrt_value(const pbrt_value& pbrt, vector& value) { if (pbrt.type == pbrt_value_type::point2 || pbrt.type == pbrt_value_type::vector2) { if (!pbrt.vector2f.empty()) { @@ -5640,19 +4044,17 @@ bool get_pbrt_value(const pbrt_value& pbrt, vector& value) { } else { value = {pbrt.value2f}; } - return true; } else if (pbrt.type == pbrt_value_type::real) { if (pbrt.vector1f.empty() || pbrt.vector1f.size() % 2) throw std::runtime_error("bad pbrt type"); value.resize(pbrt.vector1f.size() / 2); for (auto i = 0; i < value.size(); i++) value[i] = {pbrt.vector1f[i * 2 + 0], pbrt.vector1f[i * 2 + 1]}; - return true; } else { - return false; + throw std::invalid_argument{"expected float2 array"}; } } -bool get_pbrt_value(const pbrt_value& pbrt, vector& value) { +void get_pbrt_value(const pbrt_value& pbrt, vector& value) { if (pbrt.type == pbrt_value_type::point || pbrt.type == pbrt_value_type::vector || pbrt.type == pbrt_value_type::normal || @@ -5662,45 +4064,42 @@ bool get_pbrt_value(const pbrt_value& pbrt, vector& value) { } else { value = {pbrt.value3f}; } - return true; } else if (pbrt.type == pbrt_value_type::real) { if (pbrt.vector1f.empty() || pbrt.vector1f.size() % 3) - throw std::runtime_error("bad pbrt type"); + throw std::invalid_argument{"expected float3 array"}; value.resize(pbrt.vector1f.size() / 3); for (auto i = 0; i < value.size(); i++) value[i] = {pbrt.vector1f[i * 3 + 0], pbrt.vector1f[i * 3 + 1], pbrt.vector1f[i * 3 + 2]}; - return true; } else { - return false; + throw std::invalid_argument{"expected float3 array"}; } } -bool get_pbrt_value(const pbrt_value& pbrt, vector& value) { +void get_pbrt_value(const pbrt_value& pbrt, vector& value) { if (pbrt.type == pbrt_value_type::integer) { if (!pbrt.vector1i.empty()) { value = pbrt.vector1i; } else { value = {pbrt.vector1i}; } - return true; } else { - return false; + throw std::invalid_argument{"expected int array"}; } } -bool get_pbrt_value(const pbrt_value& pbrt, vector& value) { +void get_pbrt_value(const pbrt_value& pbrt, vector& value) { if (pbrt.type == pbrt_value_type::integer) { - if (pbrt.vector1i.empty() || pbrt.vector1i.size() % 3) return false; + if (pbrt.vector1i.empty() || pbrt.vector1i.size() % 3) + throw std::invalid_argument{"expected int3 array"}; value.resize(pbrt.vector1i.size() / 3); for (auto i = 0; i < value.size(); i++) value[i] = {pbrt.vector1i[i * 3 + 0], pbrt.vector1i[i * 3 + 1], pbrt.vector1i[i * 3 + 2]}; - return true; } else { - return false; + throw std::invalid_argument{"expected int3 array"}; } } -bool get_pbrt_value(const pbrt_value& pbrt, pair& value) { +void get_pbrt_value(const pbrt_value& pbrt, pair& value) { if (pbrt.type == pbrt_value_type::string) { value.first = 0; return get_pbrt_value(pbrt, value.second); @@ -5709,7 +4108,7 @@ bool get_pbrt_value(const pbrt_value& pbrt, pair& value) { return get_pbrt_value(pbrt, value.first); } } -bool get_pbrt_value(const pbrt_value& pbrt, pair& value) { +void get_pbrt_value(const pbrt_value& pbrt, pair& value) { if (pbrt.type == pbrt_value_type::string || pbrt.type == pbrt_value_type::texture) { value.first = zero3f; @@ -5810,14 +4209,14 @@ void update_transforms( } // convert gltf to scene -gltfio_status load_gltf(const string& filename, gltf_model& scene) { +void load_gltf(const string& filename, gltf_model& scene) { // load gltf auto params = cgltf_options{}; memset(¶ms, 0, sizeof(params)); auto data = (cgltf_data*)nullptr; auto result = cgltf_parse_file(¶ms, filename.c_str(), &data); if (result != cgltf_result_success) { - throw std::runtime_error("could not load " + filename); + throw std::runtime_error{filename + ": read error"}; } auto gltf = std::unique_ptr{ data, cgltf_free}; @@ -5825,7 +4224,7 @@ gltfio_status load_gltf(const string& filename, gltf_model& scene) { if (dirname != "") dirname += "/"; if (cgltf_load_buffers(¶ms, data, dirname.c_str()) != cgltf_result_success) { - throw std::runtime_error("could not load gltf buffers " + filename); + throw std::runtime_error(filename + ": error reading buffers"); } // convert textures @@ -6244,8 +4643,6 @@ gltfio_status load_gltf(const string& filename, gltf_model& scene) { } } #endif - - return {}; } } // namespace yocto @@ -6262,10 +4659,10 @@ static void remove_yaml_comment(string_view& str, char comment_char = '#') { str.remove_suffix(cpy.size()); } -static bool parse_yaml_varname(string_view& str, string_view& value) { +static void parse_yaml_varname(string_view& str, string_view& value) { skip_whitespace(str); - if (str.empty()) return false; - if (!is_alpha(str.front())) return false; + if (str.empty()) throw std::invalid_argument{"string expected"}; + if (!is_alpha(str.front())) throw std::invalid_argument{"string expected"}; auto pos = 0; while (is_alpha(str[pos]) || str[pos] == '_' || is_digit(str[pos])) { pos += 1; @@ -6273,55 +4670,62 @@ static bool parse_yaml_varname(string_view& str, string_view& value) { } value = str.substr(0, pos); str.remove_prefix(pos); - return true; } -static bool parse_yaml_varname(string_view& str, string& value) { +static void parse_yaml_varname(string_view& str, string& value) { auto view = string_view{}; - if (!parse_yaml_varname(str, view)) return false; + parse_yaml_varname(str, view); value = string{view}; - return true; +} + +static void parse_yaml_varname( + file_wrapper& fs, string_view& str, string& value) { + try { + parse_yaml_varname(str, value); + } catch (std::invalid_argument& e) { + throw std::runtime_error{fs.filename + ": parse error [" + e.what() + "]"}; + } } // parse yaml value -bool get_yaml_value(const yaml_value& yaml, string& value) { - if (yaml.type != yaml_value_type::string) return false; +void get_yaml_value(const yaml_value& yaml, string& value) { + if (yaml.type != yaml_value_type::string) + throw std::invalid_argument{"string expected"}; value = yaml.string_; - return true; } -bool get_yaml_value(const yaml_value& yaml, bool& value) { - if (yaml.type != yaml_value_type::boolean) return false; +void get_yaml_value(const yaml_value& yaml, bool& value) { + if (yaml.type != yaml_value_type::boolean) + throw std::invalid_argument{"bool expected"}; value = yaml.boolean; - return true; } -bool get_yaml_value(const yaml_value& yaml, int& value) { - if (yaml.type != yaml_value_type::number) return false; +void get_yaml_value(const yaml_value& yaml, int& value) { + if (yaml.type != yaml_value_type::number) + throw std::invalid_argument{"int expected"}; value = (int)yaml.number; - return true; } -bool get_yaml_value(const yaml_value& yaml, float& value) { - if (yaml.type != yaml_value_type::number) return false; +void get_yaml_value(const yaml_value& yaml, float& value) { + if (yaml.type != yaml_value_type::number) + throw std::invalid_argument{"float expected"}; value = (float)yaml.number; - return true; } -bool get_yaml_value(const yaml_value& yaml, vec2f& value) { - if (yaml.type != yaml_value_type::array || yaml.number != 2) return false; +void get_yaml_value(const yaml_value& yaml, vec2f& value) { + if (yaml.type != yaml_value_type::array || yaml.number != 2) + throw std::invalid_argument{"float2 expected"}; value = {(float)yaml.array_[0], (float)yaml.array_[1]}; - return true; } -bool get_yaml_value(const yaml_value& yaml, vec3f& value) { - if (yaml.type != yaml_value_type::array || yaml.number != 3) return false; +void get_yaml_value(const yaml_value& yaml, vec3f& value) { + if (yaml.type != yaml_value_type::array || yaml.number != 3) + throw std::invalid_argument{"float3 expected"}; value = {(float)yaml.array_[0], (float)yaml.array_[1], (float)yaml.array_[2]}; - return true; } -bool get_yaml_value(const yaml_value& yaml, mat3f& value) { - if (yaml.type != yaml_value_type::array || yaml.number != 9) return false; +void get_yaml_value(const yaml_value& yaml, mat3f& value) { + if (yaml.type != yaml_value_type::array || yaml.number != 9) + throw std::invalid_argument{"float9 expected"}; for (auto i = 0; i < 9; i++) (&value.x.x)[i] = (float)yaml.array_[i]; - return true; } -bool get_yaml_value(const yaml_value& yaml, frame3f& value) { - if (yaml.type != yaml_value_type::array || yaml.number != 12) return false; +void get_yaml_value(const yaml_value& yaml, frame3f& value) { + if (yaml.type != yaml_value_type::array || yaml.number != 12) + throw std::invalid_argument{"float12 expected"}; for (auto i = 0; i < 12; i++) (&value.x.x)[i] = (float)yaml.array_[i]; - return true; } bool has_yaml_value(const yaml_element& element, const string& name) { for (auto& [key, _] : element.key_values) { @@ -6362,21 +4766,21 @@ yaml_value make_yaml_value(const frame3f& value) { return yaml; } -static bool parse_value(string_view& str, yaml_value& value) { +static void parse_value(string_view& str, yaml_value& value) { trim_whitespace(str); - if (str.empty()) return false; + if (str.empty()) throw std::invalid_argument{"bad format"}; if (str.front() == '[') { str.remove_prefix(1); value.type = yaml_value_type::array; value.number = 0; while (!str.empty()) { skip_whitespace(str); - if (str.empty()) return false; + if (str.empty()) throw std::invalid_argument{"bad format"}; if (str.front() == ']') { str.remove_prefix(1); break; } - if (value.number >= 16) return false; + if (value.number >= 16) throw std::invalid_argument{"bad format"}; parse_value(str, value.array_[(int)value.number]); value.number += 1; skip_whitespace(str); @@ -6387,7 +4791,7 @@ static bool parse_value(string_view& str, yaml_value& value) { str.remove_prefix(1); break; } else { - return false; + throw std::invalid_argument{"bad format"}; } } } else if (is_digit(str.front()) || str.front() == '-' || @@ -6403,22 +4807,20 @@ static bool parse_value(string_view& str, yaml_value& value) { } } skip_whitespace(str); - if (!str.empty() && !is_whitespace(str)) return false; - return true; + if (!str.empty() && !is_whitespace(str)) + throw std::invalid_argument{"bad format"}; } // Load/save yaml -yamlio_status load_yaml(const string& filename, yaml_model& yaml) { - auto fs = fopen(filename.c_str(), "rt"); - if (!fs) return {filename + ": file not found"}; - auto fs_guard = std::unique_ptr{fs, fclose}; +void load_yaml(const string& filename, yaml_model& yaml) { + auto fs = open_file(filename, "rt"); // read the file line by line auto group = ""s; auto key = ""s; auto value = yaml_value{}; char buffer[4096]; - while (fgets(buffer, sizeof(buffer), fs)) { + while (read_line(fs, buffer, sizeof(buffer))) { // line auto line = string_view{buffer}; remove_yaml_comment(line); @@ -6428,9 +4830,9 @@ yamlio_status load_yaml(const string& filename, yaml_model& yaml) { // peek commands if (is_space(line.front())) { // indented property - if (group == "") return {filename + " parse_error"}; + if (group == "") throw_parse_error(fs, "bad format"); skip_whitespace(line); - if (line.empty()) return {filename + " parse_error"}; + if (line.empty()) throw_parse_error(fs, "bad format"); if (line.front() == '-') { auto& element = yaml.elements.emplace_back(); element.name = group; @@ -6440,19 +4842,19 @@ yamlio_status load_yaml(const string& filename, yaml_model& yaml) { auto& element = yaml.elements.emplace_back(); element.name = group; } - if (!parse_yaml_varname(line, key)) return {filename + " parse_error"}; + parse_yaml_varname(line, key); skip_whitespace(line); if (line.empty() || line.front() != ':') - return {filename + " parse_error"}; + throw_parse_error(fs, "bad format"); line.remove_prefix(1); - if (!parse_value(line, value)) return {filename + " parse_error"}; + parse_value(fs, line, value); yaml.elements.back().key_values.push_back({key, value}); } else if (is_alpha(line.front())) { // new group - if (!parse_yaml_varname(line, key)) return {filename + " parse_error"}; + parse_yaml_varname(fs, line, key); skip_whitespace(line); if (line.empty() || line.front() != ':') - return {filename + " parse_error"}; + throw_parse_error(fs, "bad format"); line.remove_prefix(1); if (!line.empty() && !is_whitespace(line)) { group = ""; @@ -6460,17 +4862,16 @@ yamlio_status load_yaml(const string& filename, yaml_model& yaml) { auto& element = yaml.elements.emplace_back(); element.name = group; } - if (!parse_value(line, value)) return {filename + " parse_error"}; + parse_value(fs, line, value); yaml.elements.back().key_values.push_back({key, value}); } else { group = key; key = ""; } } else { - return {filename + " parse_error"}; + throw_parse_error(fs, "bad format"); } } - return {}; } static void format_value(string& str, const yaml_value& value) { @@ -6497,157 +4898,39 @@ static void format_value(string& str, const yaml_value& value) { } } -yamlio_status save_yaml(const string& filename, const yaml_model& yaml) { - auto fs = fopen(filename.c_str(), "wt"); - if (!fs) throw std::runtime_error("cannot open " + filename); - auto fs_guard = std::unique_ptr{fs, fclose}; +void save_yaml(const string& filename, const yaml_model& yaml) { + auto fs = open_file(filename, "wt"); // save comments - if (!format_values(fs, "#\n")) return {filename + ": write error"}; - if (!format_values(fs, "# Written by Yocto/GL\n")) - return {filename + ": write error"}; - if (!format_values(fs, "# https://github.com/xelatihy/yocto-gl\n")) - return {filename + ": write error"}; - if (!format_values(fs, "#\n\n")) return {filename + ": write error"}; + format_values(fs, "#\n"); + format_values(fs, "# Written by Yocto/GL\n"); + format_values(fs, "# https://github.com/xelatihy/yocto-gl\n"); + format_values(fs, "#\n\n"); for (auto& comment : yaml.comments) { - if (!format_values(fs, "# {}\n", comment)) - return {filename + ": write error"}; + format_values(fs, "# {}\n", comment); } - if (!format_values(fs, "\n")) return {filename + ": write error"}; + format_values(fs, "\n"); auto group = ""s; for (auto& element : yaml.elements) { if (group != element.name) { group = element.name; if (group != "") { - if (!format_values(fs, "\n{}:\n", group)) - return {filename + ": write error"}; + format_values(fs, "\n{}:\n", group); } else { - if (!format_values(fs, "\n")) return {filename + ": write error"}; + format_values(fs, "\n"); } } auto first = true; for (auto& [key, value] : element.key_values) { if (group != "") { - if (!format_values(fs, " {} {}: {}\n", first ? "-" : " ", key, value)) - return {filename + ": write error"}; + format_values(fs, " {} {}: {}\n", first ? "-" : " ", key, value); first = false; } else { - if (!format_values(fs, "{}: {}\n", key, value)) - return {filename + ": write error"}; + format_values(fs, "{}: {}\n", key, value); } } } - - return {}; -} - -yamlio_status read_yaml_property(const string& filename, FILE* fs, - string& group, string& key, bool& newobj, bool& done, yaml_value& value) { - // read the file line by line - char buffer[4096]; - while (fgets(buffer, sizeof(buffer), fs)) { - // str - auto str = string_view{buffer}; - remove_yaml_comment(str); - if (str.empty()) continue; - if (is_whitespace(str)) continue; - - // peek commands - if (is_space(str.front())) { - // indented property - if (group == "") return {filename + ": parse error"}; - skip_whitespace(str); - if (str.empty()) return {filename + ": parse error"}; - if (str.front() == '-') { - newobj = true; - str.remove_prefix(1); - skip_whitespace(str); - } else { - newobj = false; - } - if (!parse_yaml_varname(str, key)) return {filename + ": parse error"}; - skip_whitespace(str); - if (str.empty() || str.front() != ':') - return {filename + ": parse error"}; - str.remove_prefix(1); - if (!parse_value(str, value)) return {filename + ": parse error"}; - return {}; - } else if (is_alpha(str.front())) { - // new group - if (!parse_yaml_varname(str, key)) return {filename + ": parse error"}; - skip_whitespace(str); - if (str.empty() || str.front() != ':') - return {filename + ": parse error"}; - str.remove_prefix(1); - if (!str.empty() && !is_whitespace(str)) { - group = ""; - if (!parse_value(str, value)) return {filename + ": parse error"}; - return {}; - } else { - group = key; - key = ""; - return {}; - } - } else { - str = {}; - } - } - - if (ferror(fs)) return {filename + ": read error"}; - - done = true; - return {}; -} - -static vector split_yaml_string( - const string& str, const string& delim) { - auto tokens = vector{}; - auto last = (size_t)0, next = (size_t)0; - while ((next = str.find(delim, last)) != string::npos) { - tokens.push_back(str.substr(last, next - last)); - last = next + delim.size(); - } - if (last < str.size()) tokens.push_back(str.substr(last)); - return tokens; -} - -yamlio_status write_yaml_comment( - const string& filename, FILE* fs, const string& comment) { - auto lines = split_yaml_string(comment, "\n"); - for (auto& line : lines) { - if (!format_values(fs, "# {}\n", line)) return {filename + ": write error"}; - } - if (!format_values(fs, "\n")) return {filename + ": write error"}; - - return {}; -} - -// Save yaml property -yamlio_status write_yaml_property(const string& filename, FILE* fs, - const string& object, const string& key, bool newobj, - const yaml_value& value) { - if (key.empty()) { - if (!format_values(fs, "\n{}:\n", object)) - return {filename + ": write error"}; - } else { - if (!object.empty()) { - if (!format_values(fs, " {} {}: {}\n", newobj ? "-" : " ", key, value)) - return {filename + ": write error"}; - } else { - if (!format_values(fs, "{}: {}\n", key, value)) - return {filename + ": write error"}; - } - } - - return {}; -} - -yamlio_status write_yaml_object( - const string& filename, FILE* fs, const string& object) { - if (!format_values(fs, "\n{}:\n", object)) - return {filename + ": write error"}; - return {}; } } // namespace yocto diff --git a/yocto/yocto_modelio.h b/yocto/yocto_modelio.h index e3b5ecdfd..1337ced7a 100644 --- a/yocto/yocto_modelio.h +++ b/yocto/yocto_modelio.h @@ -1,60 +1,10 @@ // // # Yocto/ModelIO: Tiny library for Ply/Obj/Pbrt/Yaml/glTF parsing and writing // -// Yocto/Ply is a tiny library for loading and saving Ply/Obj/Pbrt/Yaml/glTF. -// Yocto/ModelIO supports two interfaces: a simple interface where all model -// data is loaded and saved at once and a low-level interface where single -// commands values are read and written one at a time. -// -// -// ## Low-Level Ply Loading -// -// Load a PLY by first opening the file and reading its header. Then, for each -// element, read the values of its lists and non-lists properties. Example: -// -// auto ply = fopen(filename, "rb"); // open for reading -// auto format = ply_format{}; // initialize format -// auto elemnts = vector{}; // initialize elements -// auto comments = vector{}; // initialize comments -// read_ply_header(ply, fromat elements, comments); // read ply header -// for(auto& element : elements) { // iterate elements -// // initialize the element's property values and lists -// // using either doubles or vector and vector> -// auto values = vector(element.properties.size()); -// auto lists - vector>(element.properties.size()); -// for(auto i = 0; i < element.count; i ++) { // iterate values -// read_ply_value(ply, format, element, values, lists); // read props -// // values contains values for non-list properties -// // lists contains the values for list properties -// } -// -// For convenience during parsing, you can use `find_ply_property()` to -// determine the index of the property you may be interested in. -// -// -// ## Load-Level PLY Saving -// -// Write a PLY by first opening the file for writing and deciding whether to -// use ASCII or binary (we recommend tha letter). Then fill in the elements -// and comments and write its header. Finally, write its values one by one. -// Example: -// -// auto fs = fopen(filename, "rb"); // open for writing -// auto format = ply_format::binary_little_endian; // initialize format -// auto elemnts = vector{}; // initialize elements -// auto comments = vector{}; // initialize comments -// // add eleements and comments to the previous lists -// write_ply_header(ply, format, elements, comments); // read ply header -// for(auto& element : elements) { // iterate elements -// // initialize the element's property values and lists -// // using either doubles or vector and vector> -// auto values = vector(element.properties.size()); -// auto lists - vector>(element.properties.size()); -// for(auto i = 0; i < element.count; i ++) { // iterate values -// values = {...}; lists = {...}; // set values/lists -// write_ply_value(ply, foramt, element, values, lists); // write props -// } -// +// Yocto/ModelIO is a tiny library for loading and saving +// Ply/Obj/Pbrt/Yaml/glTF. In Yocto/ModelIO, all model data is loaded and saved +// at once. Each format is parsed in a manner that is as close as possible to +// the original. Data can be accessed directly or via converters. // // @@ -141,15 +91,9 @@ struct ply_model { vector elements = {}; }; -// Result of io operations -struct plyio_status { - string error = {}; - explicit operator bool() const { return error.empty(); } -}; - // Load and save ply -plyio_status load_ply(const string& filename, ply_model& ply); -plyio_status save_ply(const string& filename, const ply_model& ply); +void load_ply(const string& filename, ply_model& ply); +void save_ply(const string& filename, const ply_model& ply); // Get ply properties bool has_ply_property( @@ -238,45 +182,6 @@ void add_ply_points(ply_model& ply, const vector& values); } // namespace yocto -// ----------------------------------------------------------------------------- -// LOW_LEVEL PLY LOADING AND SAVING -// ----------------------------------------------------------------------------- -namespace yocto { - -// Read Ply functions -plyio_status read_ply_header(const string& filename, FILE* fs, - ply_format& format, vector& elements, - vector& comments); -plyio_status read_ply_value(const string& filename, FILE* fs, ply_format format, - const ply_element& element, vector& values, - vector>& lists); -plyio_status read_ply_value(const string& filename, FILE* fs, ply_format format, - const ply_element& element, vector& values, - vector>& lists); - -// Write Ply functions -plyio_status write_ply_header(const string& filename, FILE* fs, - ply_format format, const vector& elements, - const vector& comments); -plyio_status write_ply_value(const string& filename, FILE* fs, - ply_format format, const ply_element& element, vector& values, - vector>& lists); -plyio_status write_ply_value(const string& filename, FILE* fs, - ply_format format, const ply_element& element, vector& values, - vector>& lists); - -// Helpers to get element and property indices -int find_ply_element(const vector& elements, const string& name); -int find_ply_property(const ply_element& element, const string& name); -vec2i find_ply_property( - const ply_element& element, const string& name1, const string& name2); -vec3i find_ply_property(const ply_element& element, const string& name1, - const string& name2, const string& name3); -vec4i find_ply_property(const ply_element& element, const string& name1, - const string& name2, const string& name3, const string& name4); - -} // namespace yocto - // ----------------------------------------------------------------------------- // SIMPLE OBJ LOADER AND WRITER // ----------------------------------------------------------------------------- @@ -413,17 +318,10 @@ struct obj_model { vector environments = {}; }; -// Result of io operations -struct objio_status { - string error = {}; - explicit operator bool() const { return error.empty(); } -}; - // Load and save obj -objio_status load_obj(const string& filename, obj_model& obj, - bool geom_only = false, bool split_elements = true, - bool split_materials = false); -objio_status save_obj(const string& filename, const obj_model& obj); +void load_obj(const string& filename, obj_model& obj, bool geom_only = false, + bool split_elements = true, bool split_materials = false); +void save_obj(const string& filename, const obj_model& obj); // convert between roughness and exponent float obj_exponent_to_roughness(float exponent); @@ -486,55 +384,6 @@ void add_obj_fvquads(obj_model& obj, const string& name, } // namespace yocto -// ----------------------------------------------------------------------------- -// LOW-LEVEL INTERFACE -// ----------------------------------------------------------------------------- -namespace yocto { - -// Obj/Mtl/Objx command -enum struct obj_command { - // clang-format off - vertex, normal, texcoord, // data in value - face, str, point, // data in vertices - object, group, usemtl, smoothing, // data in name - mtllib, objxlib, // data in name - error // no data - // clang-format on -}; -enum struct mtl_command { material, error }; -enum struct objx_command { camera, environment, instance, error }; - -// Obj instance -struct obj_instance { - string object = ""; - frame3f frame = identity3x4f; -}; - -// Read obj/mtl/objx elements -objio_status read_obj_command(const string& filename, FILE* fs, - obj_command& command, string& name, vec3f& value, - vector& vertices, obj_vertex& vert_size); -objio_status read_mtl_command(const string& filename, FILE* fs, - mtl_command& command, obj_material& material, bool fliptr = true); -objio_status read_objx_command(const string& filename, FILE* fs, - objx_command& command, obj_camera& camera, obj_environment& environment, - obj_instance& instance); - -// Write obj/mtl/objx elements -objio_status write_obj_comment( - const string& filename, FILE* fs, const string& comment); -objio_status write_obj_command(const string& filename, FILE* fs, - obj_command command, const string& name, const vec3f& value, - const vector& vertices = {}); -objio_status write_mtl_command(const string& filename, FILE* fs, - mtl_command command, obj_material& material, - const obj_texture_info& texture = {}); -objio_status write_objx_command(const string& filename, FILE* fs, - objx_command command, const obj_camera& camera, - const obj_environment& environment, const obj_instance& instance); - -} // namespace yocto - // ----------------------------------------------------------------------------- // HELPER FOR DICTIONARIES // ----------------------------------------------------------------------------- @@ -586,40 +435,21 @@ struct yaml_model { vector elements = {}; }; -// // Result of io operations -struct yamlio_status { - string error = {}; - explicit operator bool() const { return error.empty(); } -}; - // Load/save yaml -yamlio_status load_yaml(const string& filename, yaml_model& yaml); -yamlio_status save_yaml(const string& filename, const yaml_model& yaml); - -// Load Yaml properties -yamlio_status read_yaml_property(const string& filename, FILE* fs, - string& group, string& key, bool& newobj, bool& done, yaml_value& value); - -// Write Yaml properties -yamlio_status write_yaml_comment( - const string& filename, FILE* fs, const string& comment); -yamlio_status write_yaml_property(const string& filename, FILE* fs, - const string& object, const string& key, bool newobj, - const yaml_value& value); -yamlio_status write_yaml_object( - const string& filename, FILE* fs, const string& object); +void load_yaml(const string& filename, yaml_model& yaml); +void save_yaml(const string& filename, const yaml_model& yaml); // type-cheked yaml value access -bool get_yaml_value(const yaml_value& yaml, string& value); -bool get_yaml_value(const yaml_value& yaml, bool& value); -bool get_yaml_value(const yaml_value& yaml, int& value); -bool get_yaml_value(const yaml_value& yaml, float& value); -bool get_yaml_value(const yaml_value& yaml, vec2f& value); -bool get_yaml_value(const yaml_value& yaml, vec3f& value); -bool get_yaml_value(const yaml_value& yaml, mat3f& value); -bool get_yaml_value(const yaml_value& yaml, frame3f& value); +void get_yaml_value(const yaml_value& yaml, string& value); +void get_yaml_value(const yaml_value& yaml, bool& value); +void get_yaml_value(const yaml_value& yaml, int& value); +void get_yaml_value(const yaml_value& yaml, float& value); +void get_yaml_value(const yaml_value& yaml, vec2f& value); +void get_yaml_value(const yaml_value& yaml, vec3f& value); +void get_yaml_value(const yaml_value& yaml, mat3f& value); +void get_yaml_value(const yaml_value& yaml, frame3f& value); template -inline bool get_yaml_value( +inline void get_yaml_value( const yaml_element& element, const string& name, const T& value); bool has_yaml_value(const yaml_element& element, const string& name); @@ -633,7 +463,7 @@ yaml_value make_yaml_value(const vec3f& value); yaml_value make_yaml_value(const mat3f& value); yaml_value make_yaml_value(const frame3f& value); template -inline bool add_yaml_value( +inline void add_yaml_value( yaml_element& element, const string& name, const T& value); } // namespace yocto @@ -842,76 +672,26 @@ struct pbrt_model { vector accelerators = {}; }; -// Result of io operations -struct pbrtio_status { - string error = {}; - explicit operator bool() const { return error.empty(); } -}; - // Load/save pbrt -pbrtio_status load_pbrt(const string& filename, pbrt_model& pbrt); -pbrtio_status save_pbrt(const string& filename, const pbrt_model& pbrt); - -} // namespace yocto - -// ----------------------------------------------------------------------------- -// LOW-LEVEL INTERFACE -// ----------------------------------------------------------------------------- -namespace yocto { - -// Pbrt command -enum struct pbrt_command { - // clang-format off - world_begin, world_end, attribute_begin, attribute_end, - transform_begin, transform_end, reverse_orientation, - set_transform, concat_transform, lookat_transform, - object_instance, object_begin, object_end, include, - sampler, integrator, accelerator, film, filter, camera, shape, light, - material, arealight, named_texture, named_medium, named_material, - use_material, medium_interface, active_transform, - coordinate_system_set, coordinate_system_transform, - error - // clang-format on -}; - -// Read pbrt commands -pbrtio_status read_pbrt_command(const string& filename, FILE* fs, - pbrt_command& command, string& name, string& type, frame3f& xform, - vector& values); -pbrtio_status read_pbrt_command(const string& filename, FILE* fs, - pbrt_command& command, string& name, string& type, frame3f& xform, - vector& values, string& buffer); - -// Write pbrt commands -pbrtio_status write_pbrt_comment( - const string& filename, FILE* fs, const string& comment); -pbrtio_status write_pbrt_command(const string& filename, FILE* fs, - pbrt_command command, const string& name, const string& type, - const frame3f& xform, const vector& values, - bool texture_as_float = false); -pbrtio_status write_pbrt_command(const string& filename, FILE* fs, - pbrt_command command, const string& name = "", - const frame3f& xform = identity3x4f); -pbrtio_status write_pbrt_command(const string& filename, FILE* fs, - pbrt_command command, const string& name, const string& type, - const vector& values, bool texture_as_float = false); +void load_pbrt(const string& filename, pbrt_model& pbrt); +void save_pbrt(const string& filename, const pbrt_model& pbrt); // type-cheked pbrt value access -bool get_pbrt_value(const pbrt_value& pbrt, string& value); -bool get_pbrt_value(const pbrt_value& pbrt, bool& value); -bool get_pbrt_value(const pbrt_value& pbrt, int& value); -bool get_pbrt_value(const pbrt_value& pbrt, float& value); -bool get_pbrt_value(const pbrt_value& pbrt, vec2f& value); -bool get_pbrt_value(const pbrt_value& pbrt, vec3f& value); -bool get_pbrt_value(const pbrt_value& pbrt, vector& value); -bool get_pbrt_value(const pbrt_value& pbrt, vector& value); -bool get_pbrt_value(const pbrt_value& pbrt, vector& value); -bool get_pbrt_value(const pbrt_value& pbrt, vector& value); -bool get_pbrt_value(const pbrt_value& pbrt, vector& value); -bool get_pbrt_value(const pbrt_value& pbrt, pair& value); -bool get_pbrt_value(const pbrt_value& pbrt, pair& value); +void get_pbrt_value(const pbrt_value& pbrt, string& value); +void get_pbrt_value(const pbrt_value& pbrt, bool& value); +void get_pbrt_value(const pbrt_value& pbrt, int& value); +void get_pbrt_value(const pbrt_value& pbrt, float& value); +void get_pbrt_value(const pbrt_value& pbrt, vec2f& value); +void get_pbrt_value(const pbrt_value& pbrt, vec3f& value); +void get_pbrt_value(const pbrt_value& pbrt, vector& value); +void get_pbrt_value(const pbrt_value& pbrt, vector& value); +void get_pbrt_value(const pbrt_value& pbrt, vector& value); +void get_pbrt_value(const pbrt_value& pbrt, vector& value); +void get_pbrt_value(const pbrt_value& pbrt, vector& value); +void get_pbrt_value(const pbrt_value& pbrt, pair& value); +void get_pbrt_value(const pbrt_value& pbrt, pair& value); template -inline bool get_pbrt_value( +inline void get_pbrt_value( const vector& pbrt, const string& name, T& value); // pbrt value construction @@ -1010,13 +790,8 @@ struct gltf_model { vector scenes = {}; }; -// Result of io operations -struct gltfio_status { - string error = {}; - explicit operator bool() const { return error.empty(); } -}; - -gltfio_status load_gltf(const string& filename, gltf_model& gltf); +// Load gltf file. +void load_gltf(const string& filename, gltf_model& gltf); } // namespace yocto @@ -1026,32 +801,29 @@ gltfio_status load_gltf(const string& filename, gltf_model& gltf); namespace yocto { template -inline bool get_yaml_value( +inline void get_yaml_value( const yaml_element& element, const string& name, T& value) { for (auto& [key, value_] : element.key_values) { if (key == name) return get_yaml_value(value_, value); } - return true; } template -inline bool add_yaml_value( +inline void add_yaml_value( yaml_element& element, const string& name, const T& value) { for (auto& [key, value] : element.key_values) - if (key == name) return false; + if (key == name) throw std::invalid_argument{"value exists"}; element.key_values.push_back({name, make_yaml_value(value)}); - return true; } template -inline bool get_pbrt_value( +inline void get_pbrt_value( const vector& pbrt, const string& name, T& value) { for (auto& p : pbrt) { if (p.name == name) { return get_pbrt_value(p, value); } } - return true; } } // namespace yocto diff --git a/yocto/yocto_sceneio.cpp b/yocto/yocto_sceneio.cpp index 181957b5a..f9ff19e6c 100644 --- a/yocto/yocto_sceneio.cpp +++ b/yocto/yocto_sceneio.cpp @@ -752,39 +752,53 @@ void update_transforms( // ----------------------------------------------------------------------------- namespace yocto { +// Helpers for throwing +static void throw_format_error(const string& filename) { + throw std::runtime_error{filename + ": unknown format"}; +} +static void throw_dependent_error(const string& filename, const string& err) { + throw std::runtime_error{filename + ": error in resource (" + err + ")"}; +} +static void throw_emptyshape_error(const string& filename, const string& name) { + throw std::runtime_error{filename + ": empty shape " + name}; +} +static void throw_missing_reference_error( + const string& filename, const string& type, const string& name) { + throw std::runtime_error{filename + ": missing " + type + " " + name}; +} + // Load/save a scene in the builtin YAML format. -static sceneio_status load_yaml_scene( +static void load_yaml_scene( const string& filename, sceneio_model& scene, bool noparallel); -static sceneio_status save_yaml_scene( +static void save_yaml_scene( const string& filename, const sceneio_model& scene, bool noparallel); // Load/save a scene from/to OBJ. -static sceneio_status load_obj_scene( +static void load_obj_scene( const string& filename, sceneio_model& scene, bool noparallel); -static sceneio_status save_obj_scene(const string& filename, - const sceneio_model& scene, bool instances, bool noparallel); +static void save_obj_scene(const string& filename, const sceneio_model& scene, + bool instances, bool noparallel); // Load/save a scene from/to PLY. Loads/saves only one mesh with no other data. -static sceneio_status load_ply_scene( +static void load_ply_scene( const string& filename, sceneio_model& scene, bool noparallel); -static sceneio_status save_ply_scene( +static void save_ply_scene( const string& filename, const sceneio_model& scene, bool noparallel); // Load/save a scene from/to glTF. -static sceneio_status load_gltf_scene( +static void load_gltf_scene( const string& filename, sceneio_model& scene, bool noparallel); // Load/save a scene from/to pbrt. This is not robust at all and only // works on scene that have been previously adapted since the two renderers // are too different to match. -static sceneio_status load_pbrt_scene( +static void load_pbrt_scene( const string& filename, sceneio_model& scene, bool noparallel); -static sceneio_status save_pbrt_scene( +static void save_pbrt_scene( const string& filename, const sceneio_model& scene, bool noparallel); // Load a scene -sceneio_status load_scene( - const string& filename, sceneio_model& scene, bool noparallel) { +void load_scene(const string& filename, sceneio_model& scene, bool noparallel) { auto ext = get_extension(filename); if (ext == ".yaml" || ext == ".YAML") { return load_yaml_scene(filename, scene, noparallel); @@ -798,12 +812,12 @@ sceneio_status load_scene( return load_ply_scene(filename, scene, noparallel); } else { scene = {}; - return {filename + ": unsupported format"}; + throw_format_error(filename); } } // Save a scene -sceneio_status save_scene( +void save_scene( const string& filename, const sceneio_model& scene, bool noparallel) { auto ext = get_extension(filename); if (ext == ".yaml" || ext == ".YAML") { @@ -815,268 +829,179 @@ sceneio_status save_scene( } else if (ext == ".ply" || ext == ".PLY") { return save_ply_scene(filename, scene, noparallel); } else { - return {filename + ": unsupported format"}; + throw_format_error(filename); } } -sceneio_status load_texture(const string& filename, sceneio_texture& texture) { - if (is_hdr_filename(texture.filename)) { - if (auto ret = load_image( - get_dirname(filename) + texture.filename, texture.hdr); - !ret) { - return {filename + ": missing texture (" + ret.error + ")"}; +void load_texture(const string& filename, sceneio_texture& texture) { + try { + if (is_hdr_filename(texture.filename)) { + load_image(get_dirname(filename) + texture.filename, texture.hdr); } else { - return {}; - } - } else { - if (auto ret = load_imageb( - get_dirname(filename) + texture.filename, texture.ldr); - !ret) { - return {filename + ": missing texture (" + ret.error + ")"}; - } else { - return {}; + load_imageb(get_dirname(filename) + texture.filename, texture.ldr); } + } catch (std::exception& e) { + throw_dependent_error(filename, e.what()); } } -sceneio_status save_texture( - const string& filename, const sceneio_texture& texture) { - if (!texture.hdr.empty()) { - if (auto ret = save_image( - get_dirname(filename) + texture.filename, texture.hdr); - !ret) { - return {filename + ": missing texture (" + ret.error + ")"}; - } else { - return {}; - } - } else { - if (auto ret = save_imageb( - get_dirname(filename) + texture.filename, texture.ldr); - !ret) { - return {filename + ": missing texture (" + ret.error + ")"}; +void save_texture(const string& filename, const sceneio_texture& texture) { + try { + if (!texture.hdr.empty()) { + save_image(get_dirname(filename) + texture.filename, texture.hdr); } else { - return {}; + save_imageb(get_dirname(filename) + texture.filename, texture.ldr); } + } catch (std::exception& e) { + throw_dependent_error(filename, e.what()); } } -sceneio_status load_textures( +void load_textures( const string& filename, sceneio_model& scene, bool noparallel) { // load images if (noparallel) { for (auto& texture : scene.textures) { if (!texture.hdr.empty() || !texture.ldr.empty()) continue; - if (auto ret = load_texture(filename, texture); !ret) return ret; + load_texture(filename, texture); } - return {}; } else { - auto mutex = std::mutex{}; - auto status = sceneio_status{}; - parallel_foreach( - scene.textures, [&filename, &mutex, &status](sceneio_texture& texture) { - if (!status) return; - if (!texture.hdr.empty() || !texture.ldr.empty()) return; - if (auto ret = load_texture(filename, texture); !ret) { - auto lock = std::lock_guard{mutex}; - status = ret; - } - }); - return status; + parallel_foreach(scene.textures, [filename](sceneio_texture& texture) { + if (!texture.hdr.empty() || !texture.ldr.empty()) return; + load_texture(filename, texture); + }); } } // helper to save textures -sceneio_status save_textures( +void save_textures( const string& filename, const sceneio_model& scene, bool noparallel) { // save images if (noparallel) { for (auto& texture : scene.textures) { - if (auto ret = save_texture(filename, texture); !ret) return ret; + save_texture(filename, texture); } - return {}; } else { - auto mutex = std::mutex{}; - auto status = sceneio_status{}; - parallel_foreach(scene.textures, - [&filename, &mutex, &status](const sceneio_texture& texture) { - if (!status) return; - if (auto ret = save_texture(filename, texture); !ret) { - auto lock = std::lock_guard{mutex}; - status = ret; - } + parallel_foreach( + scene.textures, [filename](const sceneio_texture& texture) { + save_texture(filename, texture); }); - return status; } } -sceneio_status load_shape(const string& filename, sceneio_shape& shape) { - if (auto ret = load_shape(get_dirname(filename) + shape.filename, - shape.points, shape.lines, shape.triangles, shape.quads, - shape.positions, shape.normals, shape.texcoords, shape.colors, - shape.radius); - !ret) { - return {filename + ": missing shape (" + ret.error + ")"}; - } else { - return {}; +void load_shape(const string& filename, sceneio_shape& shape) { + try { + load_shape(get_dirname(filename) + shape.filename, shape.points, + shape.lines, shape.triangles, shape.quads, shape.positions, + shape.normals, shape.texcoords, shape.colors, shape.radius); + } catch (std::exception& e) { + throw_dependent_error(filename, e.what()); } } -sceneio_status save_shape(const string& filename, const sceneio_shape& shape) { - if (auto ret = save_shape(get_dirname(filename) + shape.filename, - shape.points, shape.lines, shape.triangles, shape.quads, - shape.positions, shape.normals, shape.texcoords, shape.colors, - shape.radius); - !ret) { - return {filename + ": missing shape (" + ret.error + ")"}; - } else { - return {}; +void save_shape(const string& filename, const sceneio_shape& shape) { + try { + save_shape(get_dirname(filename) + shape.filename, shape.points, + shape.lines, shape.triangles, shape.quads, shape.positions, + shape.normals, shape.texcoords, shape.colors, shape.radius); + } catch (std::exception& e) { + throw_dependent_error(filename, e.what()); } } // Load json meshes -sceneio_status load_shapes( +void load_shapes( const string& filename, sceneio_model& scene, bool noparallel) { // load shapes if (noparallel) { for (auto& shape : scene.shapes) { if (!shape.positions.empty()) continue; - if (auto ret = load_shape(filename, shape); !ret) return ret; + load_shape(filename, shape); } - return {}; } else { - auto mutex = std::mutex{}; - auto status = sceneio_status{}; - parallel_foreach( - scene.shapes, [&filename, &status, &mutex](sceneio_shape& shape) { - if (!status) return; - if (!shape.positions.empty()) return; - if (auto ret = load_shape(filename, shape); !ret) { - auto lock = std::lock_guard{mutex}; - status = ret; - } - }); - return status; + parallel_foreach(scene.shapes, [filename](sceneio_shape& shape) { + if (!shape.positions.empty()) return; + load_shape(filename, shape); + }); } } // Save json meshes -sceneio_status save_shapes( +void save_shapes( const string& filename, const sceneio_model& scene, bool noparallel) { // save shapes if (noparallel) { for (auto& shape : scene.shapes) { - if (auto ret = save_shape(filename, shape); !ret) return ret; + save_shape(filename, shape); } - return {}; } else { - auto mutex = std::mutex{}; - auto status = sceneio_status{}; - parallel_foreach( - scene.shapes, [&filename, &status, &mutex](const sceneio_shape& shape) { - if (!status) return; - if (auto ret = save_shape(filename, shape); !ret) { - auto lock = std::lock_guard{mutex}; - status = ret; - } - }); - return {}; + parallel_foreach(scene.shapes, [&filename](const sceneio_shape& shape) { + save_shape(filename, shape); + }); } } -sceneio_status load_subdiv(const string& filename, sceneio_subdiv& subdiv) { - if (!subdiv.facevarying) { - if (auto ret = load_shape(get_dirname(filename) + subdiv.filename, - subdiv.points, subdiv.lines, subdiv.triangles, subdiv.quads, - subdiv.positions, subdiv.normals, subdiv.texcoords, subdiv.colors, - subdiv.radius); - !ret) { - return {filename + ": missing subdivs (" + ret.error + ")"}; +void load_subdiv(const string& filename, sceneio_subdiv& subdiv) { + try { + if (!subdiv.facevarying) { + load_shape(get_dirname(filename) + subdiv.filename, subdiv.points, + subdiv.lines, subdiv.triangles, subdiv.quads, subdiv.positions, + subdiv.normals, subdiv.texcoords, subdiv.colors, subdiv.radius); } else { - return {}; - } - } else { - if (auto ret = load_fvshape(get_dirname(filename) + subdiv.filename, - subdiv.quadspos, subdiv.quadsnorm, subdiv.quadstexcoord, - subdiv.positions, subdiv.normals, subdiv.texcoords); - !ret) { - return {filename + ": missing subdivs (" + ret.error + ")"}; - } else { - return {}; + load_fvshape(get_dirname(filename) + subdiv.filename, subdiv.quadspos, + subdiv.quadsnorm, subdiv.quadstexcoord, subdiv.positions, + subdiv.normals, subdiv.texcoords); } + } catch (std::exception& e) { + throw_dependent_error(filename, e.what()); } } -sceneio_status save_subdiv( - const string& filename, const sceneio_subdiv& subdiv) { - if (subdiv.quadspos.empty()) { - if (auto ret = save_shape(get_dirname(filename) + subdiv.filename, - subdiv.points, subdiv.lines, subdiv.triangles, subdiv.quads, - subdiv.positions, subdiv.normals, subdiv.texcoords, subdiv.colors, - subdiv.radius); - !ret) { - return {filename + ": missing subdiv (" + ret.error + ")"}; - } else { - return {}; - } - } else { - if (auto ret = save_fvshape(get_dirname(filename) + subdiv.filename, - subdiv.quadspos, subdiv.quadsnorm, subdiv.quadstexcoord, - subdiv.positions, subdiv.normals, subdiv.texcoords); - !ret) { - return {filename + ": missing subdiv (" + ret.error + ")"}; +void save_subdiv(const string& filename, const sceneio_subdiv& subdiv) { + try { + if (subdiv.quadspos.empty()) { + save_shape(get_dirname(filename) + subdiv.filename, subdiv.points, + subdiv.lines, subdiv.triangles, subdiv.quads, subdiv.positions, + subdiv.normals, subdiv.texcoords, subdiv.colors, subdiv.radius); } else { - return {}; + save_fvshape(get_dirname(filename) + subdiv.filename, subdiv.quadspos, + subdiv.quadsnorm, subdiv.quadstexcoord, subdiv.positions, + subdiv.normals, subdiv.texcoords); } + } catch (std::exception& e) { + throw_dependent_error(filename, e.what()); } } // Load json meshes -sceneio_status load_subdivs( +void load_subdivs( const string& filename, sceneio_model& scene, bool noparallel) { // load shapes if (noparallel) { for (auto& subdiv : scene.subdivs) { if (!subdiv.positions.empty()) continue; - if (auto ret = load_subdiv(filename, subdiv); !ret) return ret; + load_subdiv(filename, subdiv); } - return {}; } else { - auto mutex = std::mutex{}; - auto status = sceneio_status{}; - parallel_foreach( - scene.subdivs, [&filename, &status, &mutex](sceneio_subdiv& subdiv) { - if (!status) return; - if (!subdiv.positions.empty()) return; - if (auto ret = load_subdiv(filename, subdiv); !ret) { - auto lock = std::lock_guard{mutex}; - status = ret; - } - }); - return status; + parallel_foreach(scene.subdivs, [filename](sceneio_subdiv& subdiv) { + if (!subdiv.positions.empty()) return; + load_subdiv(filename, subdiv); + }); } } // Save json meshes -sceneio_status save_subdivs( +void save_subdivs( const string& filename, const sceneio_model& scene, bool noparallel) { // save shapes if (noparallel) { for (auto& subdiv : scene.subdivs) { - if (auto ret = save_subdiv(filename, subdiv); !ret) return ret; + save_subdiv(filename, subdiv); } - return {}; } else { - auto mutex = std::mutex{}; - auto status = sceneio_status{}; - parallel_foreach(scene.subdivs, - [&filename, &status, &mutex](const sceneio_subdiv& subdiv) { - if (!status) return; - if (auto ret = save_subdiv(filename, subdiv); !ret) { - auto lock = std::lock_guard{mutex}; - status = ret; - } - }); - return {}; + parallel_foreach(scene.subdivs, [&filename](const sceneio_subdiv& subdiv) { + save_subdiv(filename, subdiv); + }); } } @@ -1133,11 +1058,10 @@ static bool make_image_preset( #if 1 -sceneio_status load_yaml( - const string& filename, sceneio_model& scene, bool noparallel) { +void load_yaml(const string& filename, sceneio_model& scene, bool noparallel) { // open file auto yaml = yaml_model{}; - if (auto ret = load_yaml(filename, yaml); !ret) return {ret.error}; + load_yaml(filename, yaml); auto tmap = unordered_map{{"", -1}}; auto vmap = unordered_map{{"", -1}}; @@ -1146,17 +1070,15 @@ sceneio_status load_yaml( // parse yaml reference auto get_yaml_ref = [](const yaml_element& yelment, const string& name, - int& value, - const unordered_map& refs) -> bool { + int& value, const unordered_map& refs) { auto ref = ""s; - if (!get_yaml_value(yelment, name, ref)) return false; + get_yaml_value(yelment, name, ref); if (ref == "") { value = -1; - return true; } else { - if (refs.find(ref) == refs.end()) return false; + if (refs.find(ref) == refs.end()) + throw std::invalid_argument{"missing reference to " + ref}; value = refs.at(ref); - return true; } }; @@ -1168,248 +1090,178 @@ sceneio_status load_yaml( auto groups = vector{}; auto igroups = vector{}; - // cameras - for (auto& yelement : yaml.elements) { - if (yelement.name == "cameras") { - auto& camera = scene.cameras.emplace_back(); - if (!get_yaml_value(yelement, "name", camera.name)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "uri", camera.name)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "frame", camera.frame)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "orthographic", camera.orthographic)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "lens", camera.lens)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "aspect", camera.aspect)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "film", camera.film)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "focus", camera.focus)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "aperture", camera.aperture)) - return {filename + ": parse error"}; - if (has_yaml_value(yelement, "uri")) { - auto uri = ""s; - if (!get_yaml_value(yelement, "uri", uri)) - return {filename + ": parse error"}; - camera.name = get_basename(uri); - } - if (has_yaml_value(yelement, "lookat")) { - auto lookat = identity3x3f; - if (!get_yaml_value(yelement, "lookat", lookat)) - return {filename + ": parse error"}; - camera.frame = lookat_frame(lookat.x, lookat.y, lookat.z); - camera.focus = length(lookat.x - lookat.y); - } - } else if (yelement.name == "textures") { - auto& texture = scene.textures.emplace_back(); - if (!get_yaml_value(yelement, "name", texture.name)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "filename", texture.filename)) - return {filename + ": parse error"}; - if (has_yaml_value(yelement, "preset")) { - auto preset = ""s; - if (!get_yaml_value(yelement, "preset", preset)) - return {filename + ": parse error"}; - make_image_preset(texture.hdr, texture.ldr, preset); - if (texture.filename.empty()) { - texture.filename = "textures/ypreset-" + preset + - (texture.hdr.empty() ? ".png" : ".hdr"); + // check for conversion errors + try { + // cameras + for (auto& yelement : yaml.elements) { + if (yelement.name == "cameras") { + auto& camera = scene.cameras.emplace_back(); + get_yaml_value(yelement, "name", camera.name); + get_yaml_value(yelement, "uri", camera.name); + get_yaml_value(yelement, "frame", camera.frame); + get_yaml_value(yelement, "orthographic", camera.orthographic); + get_yaml_value(yelement, "lens", camera.lens); + get_yaml_value(yelement, "aspect", camera.aspect); + get_yaml_value(yelement, "film", camera.film); + get_yaml_value(yelement, "focus", camera.focus); + get_yaml_value(yelement, "aperture", camera.aperture); + if (has_yaml_value(yelement, "uri")) { + auto uri = ""s; + get_yaml_value(yelement, "uri", uri); + camera.name = get_basename(uri); + } + if (has_yaml_value(yelement, "lookat")) { + auto lookat = identity3x3f; + get_yaml_value(yelement, "lookat", lookat); + camera.frame = lookat_frame(lookat.x, lookat.y, lookat.z); + camera.focus = length(lookat.x - lookat.y); + } + } else if (yelement.name == "textures") { + auto& texture = scene.textures.emplace_back(); + get_yaml_value(yelement, "name", texture.name); + get_yaml_value(yelement, "filename", texture.filename); + if (has_yaml_value(yelement, "preset")) { + auto preset = ""s; + get_yaml_value(yelement, "preset", preset); + make_image_preset(texture.hdr, texture.ldr, preset); + if (texture.filename.empty()) { + texture.filename = "textures/ypreset-" + preset + + (texture.hdr.empty() ? ".png" : ".hdr"); + } + } + if (has_yaml_value(yelement, "uri")) { + get_yaml_value(yelement, "uri", texture.filename); + texture.name = get_basename(texture.filename); + tmap[texture.filename] = (int)scene.textures.size() - 1; + } + tmap[texture.name] = (int)scene.textures.size() - 1; + } else if (yelement.name == "materials") { + auto& material = scene.materials.emplace_back(); + get_yaml_value(yelement, "name", material.name); + get_yaml_value(yelement, "emission", material.emission); + get_yaml_value(yelement, "diffuse", material.diffuse); + get_yaml_value(yelement, "metallic", material.metallic); + get_yaml_value(yelement, "specular", material.specular); + get_yaml_value(yelement, "roughness", material.roughness); + get_yaml_value(yelement, "coat", material.coat); + get_yaml_value(yelement, "transmission", material.transmission); + get_yaml_value(yelement, "refract", material.refract); + get_yaml_value(yelement, "voltransmission", material.voltransmission); + get_yaml_value(yelement, "volmeanfreepath", material.volmeanfreepath); + get_yaml_value(yelement, "volscatter", material.volscatter); + get_yaml_value(yelement, "volemission", material.volemission); + get_yaml_value(yelement, "volanisotropy", material.volanisotropy); + get_yaml_value(yelement, "volscale", material.volscale); + get_yaml_value(yelement, "opacity", material.opacity); + get_yaml_value(yelement, "coat", material.coat); + get_yaml_ref(yelement, "emission_tex", material.emission_tex, tmap); + get_yaml_ref(yelement, "diffuse_tex", material.diffuse_tex, tmap); + get_yaml_ref(yelement, "metallic_tex", material.metallic_tex, tmap); + get_yaml_ref(yelement, "specular_tex", material.specular_tex, tmap); + get_yaml_ref( + yelement, "transmission_tex", material.transmission_tex, tmap); + get_yaml_ref(yelement, "roughness_tex", material.roughness_tex, tmap); + get_yaml_ref(yelement, "subsurface_tex", material.subsurface_tex, tmap); + get_yaml_ref(yelement, "normal_tex", material.normal_tex, tmap); + get_yaml_ref(yelement, "normal_tex", material.normal_tex, tmap); + get_yaml_value(yelement, "gltf_textures", material.gltf_textures); + if (has_yaml_value(yelement, "uri")) { + get_yaml_value(yelement, "uri", material.name); + mmap[material.name] = (int)scene.materials.size() - 1; + material.name = get_basename(material.name); } - } - if (has_yaml_value(yelement, "uri")) { - if (!get_yaml_value(yelement, "uri", texture.filename)) - return {filename + ": parse error"}; - texture.name = get_basename(texture.filename); - tmap[texture.filename] = (int)scene.textures.size() - 1; - } - tmap[texture.name] = (int)scene.textures.size() - 1; - } else if (yelement.name == "materials") { - auto& material = scene.materials.emplace_back(); - if (!get_yaml_value(yelement, "name", material.name)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "emission", material.emission)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "diffuse", material.diffuse)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "metallic", material.metallic)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "specular", material.specular)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "roughness", material.roughness)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "coat", material.coat)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "transmission", material.transmission)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "refract", material.refract)) - return {filename + ": parse error"}; - if (!get_yaml_value( - yelement, "voltransmission", material.voltransmission)) - return {filename + ": parse error"}; - if (!get_yaml_value( - yelement, "volmeanfreepath", material.volmeanfreepath)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "volscatter", material.volscatter)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "volemission", material.volemission)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "volanisotropy", material.volanisotropy)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "volscale", material.volscale)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "opacity", material.opacity)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "coat", material.coat)) - return {filename + ": parse error"}; - if (!get_yaml_ref(yelement, "emission_tex", material.emission_tex, tmap)) - return {filename + ": parse error"}; - if (!get_yaml_ref(yelement, "diffuse_tex", material.diffuse_tex, tmap)) - return {filename + ": parse error"}; - if (!get_yaml_ref(yelement, "metallic_tex", material.metallic_tex, tmap)) - return {filename + ": parse error"}; - if (!get_yaml_ref(yelement, "specular_tex", material.specular_tex, tmap)) - return {filename + ": parse error"}; - if (!get_yaml_ref( - yelement, "transmission_tex", material.transmission_tex, tmap)) - return {filename + ": parse error"}; - if (!get_yaml_ref( - yelement, "roughness_tex", material.roughness_tex, tmap)) - return {filename + ": parse error"}; - if (!get_yaml_ref( - yelement, "subsurface_tex", material.subsurface_tex, tmap)) - return {filename + ": parse error"}; - if (!get_yaml_ref(yelement, "normal_tex", material.normal_tex, tmap)) - return {filename + ": parse error"}; - if (!get_yaml_ref(yelement, "normal_tex", material.normal_tex, tmap)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "gltf_textures", material.gltf_textures)) - return {filename + ": parse error"}; - if (has_yaml_value(yelement, "uri")) { - if (!get_yaml_value(yelement, "uri", material.name)) - return {filename + ": parse error"}; mmap[material.name] = (int)scene.materials.size() - 1; - material.name = get_basename(material.name); - } - mmap[material.name] = (int)scene.materials.size() - 1; - } else if (yelement.name == "shapes") { - auto& shape = scene.shapes.emplace_back(); - if (!get_yaml_value(yelement, "name", shape.name)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "filename", shape.filename)) - return {filename + ": parse error"}; - if (has_yaml_value(yelement, "uri")) { - if (!get_yaml_value(yelement, "uri", shape.filename)) - return {filename + ": parse error"}; - shape.name = get_basename(shape.filename); - smap[shape.filename] = (int)scene.shapes.size() - 1; - } - if (has_yaml_value(yelement, "preset")) { - auto preset = ""s; - if (!get_yaml_value(yelement, "preset", preset)) - return {filename + ": parse error"}; - make_shape_preset(shape.points, shape.lines, shape.triangles, - shape.quads, shape.positions, shape.normals, shape.texcoords, - shape.colors, shape.radius, preset); - if (shape.filename.empty()) { - shape.filename = "shapes/ypreset-" + preset + ".yvol"; + } else if (yelement.name == "shapes") { + auto& shape = scene.shapes.emplace_back(); + get_yaml_value(yelement, "name", shape.name); + get_yaml_value(yelement, "filename", shape.filename); + if (has_yaml_value(yelement, "uri")) { + get_yaml_value(yelement, "uri", shape.filename); + shape.name = get_basename(shape.filename); + smap[shape.filename] = (int)scene.shapes.size() - 1; } - } - smap[shape.name] = (int)scene.shapes.size() - 1; - } else if (yelement.name == "subdivs") { - auto& subdiv = scene.subdivs.emplace_back(); - if (!get_yaml_value(yelement, "name", subdiv.name)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "filename", subdiv.filename)) - return {filename + ": parse error"}; - if (!get_yaml_ref(yelement, "shape", subdiv.shape, smap)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "subdivisions", subdiv.subdivisions)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "catmullclark", subdiv.catmullclark)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "smooth", subdiv.smooth)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "facevarying", subdiv.facevarying)) - return {filename + ": parse error"}; - if (!get_yaml_ref( - yelement, "displacement_tex", subdiv.displacement_tex, tmap)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "displacement", subdiv.displacement)) - return {filename + ": parse error"}; - if (has_yaml_value(yelement, "uri")) { - if (!get_yaml_value(yelement, "uri", subdiv.filename)) - return {filename + ": parse error"}; - subdiv.name = get_basename(subdiv.filename); - } - if (has_yaml_value(yelement, "preset")) { - auto preset = ""s; - if (!get_yaml_value(yelement, "preset", preset)) - return {filename + ": parse error"}; - make_shape_preset(subdiv.points, subdiv.lines, subdiv.triangles, - subdiv.quads, subdiv.quadspos, subdiv.quadsnorm, - subdiv.quadstexcoord, subdiv.positions, subdiv.normals, - subdiv.texcoords, subdiv.colors, subdiv.radius, preset); - if (subdiv.filename.empty()) { - subdiv.filename = "shapes/ypreset-" + preset + ".yvol"; + if (has_yaml_value(yelement, "preset")) { + auto preset = ""s; + get_yaml_value(yelement, "preset", preset); + make_shape_preset(shape.points, shape.lines, shape.triangles, + shape.quads, shape.positions, shape.normals, shape.texcoords, + shape.colors, shape.radius, preset); + if (shape.filename.empty()) { + shape.filename = "shapes/ypreset-" + preset + ".yvol"; + } + } + smap[shape.name] = (int)scene.shapes.size() - 1; + } else if (yelement.name == "subdivs") { + auto& subdiv = scene.subdivs.emplace_back(); + get_yaml_value(yelement, "name", subdiv.name); + get_yaml_value(yelement, "filename", subdiv.filename); + get_yaml_ref(yelement, "shape", subdiv.shape, smap); + get_yaml_value(yelement, "subdivisions", subdiv.subdivisions); + get_yaml_value(yelement, "catmullclark", subdiv.catmullclark); + get_yaml_value(yelement, "smooth", subdiv.smooth); + get_yaml_value(yelement, "facevarying", subdiv.facevarying); + get_yaml_ref( + yelement, "displacement_tex", subdiv.displacement_tex, tmap); + get_yaml_value(yelement, "displacement", subdiv.displacement); + if (has_yaml_value(yelement, "uri")) { + get_yaml_value(yelement, "uri", subdiv.filename); + subdiv.name = get_basename(subdiv.filename); + } + if (has_yaml_value(yelement, "preset")) { + auto preset = ""s; + get_yaml_value(yelement, "preset", preset); + make_shape_preset(subdiv.points, subdiv.lines, subdiv.triangles, + subdiv.quads, subdiv.quadspos, subdiv.quadsnorm, + subdiv.quadstexcoord, subdiv.positions, subdiv.normals, + subdiv.texcoords, subdiv.colors, subdiv.radius, preset); + if (subdiv.filename.empty()) { + subdiv.filename = "shapes/ypreset-" + preset + ".yvol"; + } + } + } else if (yelement.name == "instances") { + auto& instance = scene.instances.emplace_back(); + get_yaml_value(yelement, "name", instance.name); + get_yaml_value(yelement, "frame", instance.frame); + get_yaml_ref(yelement, "shape", instance.shape, smap); + get_yaml_ref(yelement, "material", instance.material, mmap); + if (has_yaml_value(yelement, "uri")) { + auto uri = ""s; + get_yaml_value(yelement, "uri", uri); + instance.name = get_basename(uri); + } + if (has_yaml_value(yelement, "lookat")) { + auto lookat = identity3x3f; + get_yaml_value(yelement, "lookat", lookat); + instance.frame = lookat_frame(lookat.x, lookat.y, lookat.z, true); + } + if (has_yaml_value(yelement, "instances")) { + auto& group = groups.emplace_back(); + get_yaml_value(yelement, "instances", group.filename); + while (igroups.size() < scene.instances.size()) + igroups.emplace_back() = -1; + igroups.back() = (int)groups.size() - 1; + } + } else if (yelement.name == "environments") { + auto& environment = scene.environments.emplace_back(); + get_yaml_value(yelement, "name", environment.name); + get_yaml_value(yelement, "frame", environment.frame); + get_yaml_value(yelement, "emission", environment.emission); + get_yaml_ref(yelement, "emission_tex", environment.emission_tex, tmap); + if (has_yaml_value(yelement, "uri")) { + auto uri = ""s; + get_yaml_value(yelement, "uri", uri); + environment.name = get_basename(uri); + } + if (has_yaml_value(yelement, "lookat")) { + auto lookat = identity3x3f; + get_yaml_value(yelement, "lookat", lookat); + environment.frame = lookat_frame(lookat.x, lookat.y, lookat.z, true); } - } - } else if (yelement.name == "instances") { - auto& instance = scene.instances.emplace_back(); - if (!get_yaml_value(yelement, "name", instance.name)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "frame", instance.frame)) - return {filename + ": parse error"}; - if (!get_yaml_ref(yelement, "shape", instance.shape, smap)) - return {filename + ": parse error"}; - if (!get_yaml_ref(yelement, "material", instance.material, mmap)) - return {filename + ": parse error"}; - if (has_yaml_value(yelement, "uri")) { - auto uri = ""s; - if (!get_yaml_value(yelement, "uri", uri)) - return {filename + ": parse error"}; - instance.name = get_basename(uri); - } - if (has_yaml_value(yelement, "lookat")) { - auto lookat = identity3x3f; - if (!get_yaml_value(yelement, "lookat", lookat)) - return {filename + ": parse error"}; - instance.frame = lookat_frame(lookat.x, lookat.y, lookat.z, true); - } - if (has_yaml_value(yelement, "instances")) { - auto& group = groups.emplace_back(); - if (!get_yaml_value(yelement, "instances", group.filename)) - return {filename + ": parse error"}; - while (igroups.size() < scene.instances.size()) - igroups.emplace_back() = -1; - igroups.back() = (int)groups.size() - 1; - } - } else if (yelement.name == "environments") { - auto& environment = scene.environments.emplace_back(); - if (!get_yaml_value(yelement, "name", environment.name)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "frame", environment.frame)) - return {filename + ": parse error"}; - if (!get_yaml_value(yelement, "emission", environment.emission)) - return {filename + ": parse error"}; - if (!get_yaml_ref( - yelement, "emission_tex", environment.emission_tex, tmap)) - return {filename + ": parse error"}; - if (has_yaml_value(yelement, "uri")) { - auto uri = ""s; - if (!get_yaml_value(yelement, "uri", uri)) - return {filename + ": parse error"}; - environment.name = get_basename(uri); - } - if (has_yaml_value(yelement, "lookat")) { - auto lookat = identity3x3f; - if (!get_yaml_value(yelement, "lookat", lookat)) - return {filename + ": parse error"}; - environment.frame = lookat_frame(lookat.x, lookat.y, lookat.z, true); } } + + } catch (std::invalid_argument& e) { + throw std::runtime_error{filename + ": parse error [" + e.what() + "]"}; } // instance groups @@ -1418,31 +1270,27 @@ sceneio_status load_yaml( if (noparallel) { for (auto& group : groups) { auto ply = ply_model{}; - if (auto ret = load_ply(get_dirname(filename) + group.filename, ply); - !ret) - return {filename + ": missing instances (" + ret.error + ")"}; + try { + load_ply(get_dirname(filename) + group.filename, ply); + } catch (std::exception& e) { + throw_dependent_error(filename, e.what()); + } group.frames = get_ply_values(ply, "frame", array{"xx", "xy", "xz", "yx", "yy", "yz", "zx", "zy", "zz", "ox", "oy", "oz"}); } } else { - auto mutex = std::mutex{}; - auto status = sceneio_status{}; - parallel_foreach(groups, [&filename, &status, &mutex]( - sceneio_group& group) { - if (!status) return; + parallel_foreach(groups, [filename](sceneio_group& group) { auto ply = ply_model{}; - if (auto ret = load_ply(get_dirname(filename) + group.filename, ply); - !ret) { - auto lock = std::lock_guard{mutex}; - status = {filename + ": missing instances (" + ret.error + ")"}; - } else { - group.frames = get_ply_values(ply, "frame", - array{"xx", "xy", "xz", "yx", "yy", "yz", "zx", "zy", - "zz", "ox", "oy", "oz"}); + try { + load_ply(get_dirname(filename) + group.filename, ply); + } catch (std::exception& e) { + throw_dependent_error(filename, e.what()); } + group.frames = get_ply_values(ply, "frame", + array{"xx", "xy", "xz", "yx", "yy", "yz", "zx", "zy", + "zz", "ox", "oy", "oz"}); }); - if (!status) return status; } auto instances = scene.instances; scene.instances.clear(); @@ -1459,8 +1307,6 @@ sceneio_status load_yaml( } } } - - return {}; } #else @@ -1481,7 +1327,7 @@ sceneio_status load_yaml(const string& filename, sceneio_model& scene) { int& value, const unordered_map& refs) -> bool { auto ref = ""s; - if (!get_yaml_value(yelment, name, ref)) return false; + get_yaml_value(yelment, name, ref)) return false; if (ref == "") { value = -1; return true; @@ -1494,7 +1340,7 @@ sceneio_status load_yaml(const string& filename, sceneio_model& scene) { auto get_yaml_ref = [](const yaml_value& yvalue, int& value, const unordered_map& refs) -> bool { auto ref = ""s; - if (!get_yaml_value(yvalue, ref)) return false; + get_yaml_value(yvalue, ref)) return false; if (ref == "") { value = -1; return true; @@ -1521,40 +1367,40 @@ sceneio_status load_yaml(const string& filename, sceneio_model& scene) { if (newobj) scene.cameras.emplace_back(); auto& camera = scene.cameras.back(); if (key == "name") - if (!get_yaml_value(value, camera.name)) + get_yaml_value(value, camera.name)) return {filename + ": parse error"}; if (key == "uri") - if (!get_yaml_value(value, camera.name)) + get_yaml_value(value, camera.name)) return {filename + ": parse error"}; if (key == "frame") - if (!get_yaml_value(value, camera.frame)) + get_yaml_value(value, camera.frame)) return {filename + ": parse error"}; if (key == "orthographic") - if (!get_yaml_value(value, camera.orthographic)) + get_yaml_value(value, camera.orthographic)) return {filename + ": parse error"}; if (key == "lens") - if (!get_yaml_value(value, camera.lens)) + get_yaml_value(value, camera.lens)) return {filename + ": parse error"}; if (key == "aspect") - if (!get_yaml_value(value, camera.aspect)) + get_yaml_value(value, camera.aspect)) return {filename + ": parse error"}; if (key == "film") - if (!get_yaml_value(value, camera.film)) + get_yaml_value(value, camera.film)) return {filename + ": parse error"}; if (key == "focus") - if (!get_yaml_value(value, camera.focus)) + get_yaml_value(value, camera.focus)) return {filename + ": parse error"}; if (key == "aperture") - if (!get_yaml_value(value, camera.aperture)) + get_yaml_value(value, camera.aperture)) return {filename + ": parse error"}; if (key == "uri") { auto uri = ""s; - if (!get_yaml_value(value, uri)) return {filename + ": parse error"}; + get_yaml_value(value, uri)) return {filename + ": parse error"}; camera.name = get_basename(uri); } if (key == "lookat") { auto lookat = identity3x3f; - if (!get_yaml_value(value, lookat)) return {filename + ": parse error"}; + get_yaml_value(value, lookat)) return {filename + ": parse error"}; camera.frame = lookat_frame(lookat.x, lookat.y, lookat.z); camera.focus = length(lookat.x - lookat.y); } @@ -1562,14 +1408,14 @@ sceneio_status load_yaml(const string& filename, sceneio_model& scene) { if (newobj) scene.textures.emplace_back(); auto& texture = scene.textures.back(); if (key == "name") - if (!get_yaml_value(value, texture.name)) + get_yaml_value(value, texture.name)) return {filename + ": parse error"}; if (key == "filename") - if (!get_yaml_value(value, texture.filename)) + get_yaml_value(value, texture.filename)) return {filename + ": parse error"}; if (key == "preset") { auto preset = ""s; - if (!get_yaml_value(value, preset)) return {filename + ": parse error"}; + get_yaml_value(value, preset)) return {filename + ": parse error"}; make_image_preset(texture.hdr, texture.ldr, preset); if (texture.filename.empty()) { texture.filename = "textures/ypreset-" + preset + @@ -1577,7 +1423,7 @@ sceneio_status load_yaml(const string& filename, sceneio_model& scene) { } } if (key == "uri") { - if (!get_yaml_value(value, texture.filename)) + get_yaml_value(value, texture.filename)) return {filename + ": parse error"}; texture.name = get_basename(texture.filename); tmap[texture.filename] = (int)scene.textures.size() - 1; @@ -1587,31 +1433,31 @@ sceneio_status load_yaml(const string& filename, sceneio_model& scene) { if (newobj) scene.materials.emplace_back(); auto& material = scene.materials.back(); if (key == "name") - if (!get_yaml_value(value, material.name)) + get_yaml_value(value, material.name)) return {filename + ": parse error"}; if (key == "emission") - if (!get_yaml_value(value, material.emission)) + get_yaml_value(value, material.emission)) return {filename + ": parse error"}; if (key == "diffuse") - if (!get_yaml_value(value, material.diffuse)) + get_yaml_value(value, material.diffuse)) return {filename + ": parse error"}; if (key == "metallic") - if (!get_yaml_value(value, material.metallic)) + get_yaml_value(value, material.metallic)) return {filename + ": parse error"}; if (key == "specular") - if (!get_yaml_value(value, material.specular)) + get_yaml_value(value, material.specular)) return {filename + ": parse error"}; if (key == "roughness") - if (!get_yaml_value(value, material.roughness)) + get_yaml_value(value, material.roughness)) return {filename + ": parse error"}; if (key == "coat") - if (!get_yaml_value(value, material.coat)) + get_yaml_value(value, material.coat)) return {filename + ": parse error"}; if (key == "transmission") - if (!get_yaml_value(value, material.transmission)) + get_yaml_value(value, material.transmission)) return {filename + ": parse error"}; if (key == "refract") - if (!get_yaml_value(value, material.refract)) + get_yaml_value(value, material.refract)) return {filename + ": parse error"}; if (key == "voltransmission") if (get_yaml_value(value, material.voltransmission)) @@ -1620,52 +1466,52 @@ sceneio_status load_yaml(const string& filename, sceneio_model& scene) { if (get_yaml_value(value, material.volmeanfreepath)) return {filename + ": parse error"}; if (key == "volscatter") - if (!get_yaml_value(value, material.volscatter)) + get_yaml_value(value, material.volscatter)) return {filename + ": parse error"}; if (key == "volemission") - if (!get_yaml_value(value, material.volemission)) + get_yaml_value(value, material.volemission)) return {filename + ": parse error"}; if (key == "volanisotropy") - if (!get_yaml_value(value, material.volanisotropy)) + get_yaml_value(value, material.volanisotropy)) return {filename + ": parse error"}; if (key == "volscale") - if (!get_yaml_value(value, material.volscale)) + get_yaml_value(value, material.volscale)) return {filename + ": parse error"}; if (key == "opacity") - if (!get_yaml_value(value, material.opacity)) + get_yaml_value(value, material.opacity)) return {filename + ": parse error"}; if (key == "coat") - if (!get_yaml_value(value, material.coat)) + get_yaml_value(value, material.coat)) return {filename + ": parse error"}; if (key == "emission_tex") - if (!get_yaml_ref(value, material.emission_tex, tmap)) + get_yaml_ref(value, material.emission_tex, tmap)) return {filename + ": parse error"}; if (key == "diffuse_tex") - if (!get_yaml_ref(value, material.diffuse_tex, tmap)) + get_yaml_ref(value, material.diffuse_tex, tmap)) return {filename + ": parse error"}; if (key == "metallic_tex") - if (!get_yaml_ref(value, material.metallic_tex, tmap)) + get_yaml_ref(value, material.metallic_tex, tmap)) return {filename + ": parse error"}; if (key == "specular_tex") - if (!get_yaml_ref(value, material.specular_tex, tmap)) + get_yaml_ref(value, material.specular_tex, tmap)) return {filename + ": parse error"}; if (key == "transmission_tex") - if (!get_yaml_ref(value, material.transmission_tex, tmap)) + get_yaml_ref(value, material.transmission_tex, tmap)) return {filename + ": parse error"}; if (key == "roughness_tex") - if (!get_yaml_ref(value, material.roughness_tex, tmap)) + get_yaml_ref(value, material.roughness_tex, tmap)) return {filename + ": parse error"}; if (key == "subsurface_tex") - if (!get_yaml_ref(value, material.subsurface_tex, tmap)) + get_yaml_ref(value, material.subsurface_tex, tmap)) return {filename + ": parse error"}; if (key == "normal_tex") - if (!get_yaml_ref(value, material.normal_tex, tmap)) + get_yaml_ref(value, material.normal_tex, tmap)) return {filename + ": parse error"}; if (key == "gltf_textures") - if (!get_yaml_value(value, material.gltf_textures)) + get_yaml_value(value, material.gltf_textures)) return {filename + ": parse error"}; if (key == "uri") { - if (!get_yaml_value(value, material.name)) + get_yaml_value(value, material.name)) return {filename + ": parse error"}; mmap[material.name] = (int)scene.materials.size() - 1; material.name = get_basename(material.name); @@ -1675,38 +1521,38 @@ sceneio_status load_yaml(const string& filename, sceneio_model& scene) { if (newobj) scene.shapes.emplace_back(); auto& shape = scene.shapes.back(); if (key == "name") - if (!get_yaml_value(value, shape.name)) + get_yaml_value(value, shape.name)) return {filename + ": parse error"}; if (key == "filename") - if (!get_yaml_value(value, shape.filename)) + get_yaml_value(value, shape.filename)) return {filename + ": parse error"}; if (key == "subdivisions") - if (!get_yaml_value(value, shape.subdivisions)) + get_yaml_value(value, shape.subdivisions)) return {filename + ": parse error"}; if (key == "catmullclark") - if (!get_yaml_value(value, shape.catmullclark)) + get_yaml_value(value, shape.catmullclark)) return {filename + ": parse error"}; if (key == "smooth") - if (!get_yaml_value(value, shape.smooth)) + get_yaml_value(value, shape.smooth)) return {filename + ": parse error"}; if (key == "facevarying") - if (!get_yaml_value(value, shape.facevarying)) + get_yaml_value(value, shape.facevarying)) return {filename + ": parse error"}; if (key == "displacement_tex") - if (!get_yaml_ref(value, shape.displacement_tex, tmap)) + get_yaml_ref(value, shape.displacement_tex, tmap)) return {filename + ": parse error"}; if (key == "displacement") - if (!get_yaml_value(value, shape.displacement)) + get_yaml_value(value, shape.displacement)) return {filename + ": parse error"}; if (key == "uri") { - if (!get_yaml_value(value, shape.filename)) + get_yaml_value(value, shape.filename)) return {filename + ": parse error"}; shape.name = get_basename(shape.filename); smap[shape.filename] = (int)scene.shapes.size() - 1; } if (key == "preset") { auto preset = ""s; - if (!get_yaml_value(value, preset)) return {filename + ": parse error"}; + get_yaml_value(value, preset)) return {filename + ": parse error"}; make_shape_preset(shape.points, shape.lines, shape.triangles, shape.quads, shape.quadspos, shape.quadsnorm, shape.quadstexcoord, shape.positions, shape.normals, shape.texcoords, shape.colors, @@ -1720,50 +1566,50 @@ sceneio_status load_yaml(const string& filename, sceneio_model& scene) { if (newobj) scene.instances.emplace_back(); auto& instance = scene.instances.back(); if (key == "name") - if (!get_yaml_value(value, instance.name)) + get_yaml_value(value, instance.name)) return {filename + ": parse error"}; if (key == "frame") - if (!get_yaml_value(value, instance.frame)) + get_yaml_value(value, instance.frame)) return {filename + ": parse error"}; if (key == "shape") - if (!get_yaml_ref(value, instance.shape, smap)) + get_yaml_ref(value, instance.shape, smap)) return {filename + ": parse error"}; if (key == "material") - if (!get_yaml_ref(value, instance.material, mmap)) + get_yaml_ref(value, instance.material, mmap)) return {filename + ": parse error"}; if (key == "uri") { auto uri = ""s; - if (!get_yaml_value(value, uri)) return {filename + ": parse error"}; + get_yaml_value(value, uri)) return {filename + ": parse error"}; instance.name = get_basename(uri); } if (key == "lookat") { auto lookat = identity3x3f; - if (!get_yaml_value(value, lookat)) return {filename + ": parse error"}; + get_yaml_value(value, lookat)) return {filename + ": parse error"}; instance.frame = lookat_frame(lookat.x, lookat.y, lookat.z, true); } } else if (group == "environments") { if (newobj) scene.environments.emplace_back(); auto& environment = scene.environments.back(); if (key == "name") - if (!get_yaml_value(value, environment.name)) + get_yaml_value(value, environment.name)) return {filename + ": parse error"}; if (key == "frame") - if (!get_yaml_value(value, environment.frame)) + get_yaml_value(value, environment.frame)) return {filename + ": parse error"}; if (key == "emission") - if (!get_yaml_value(value, environment.emission)) + get_yaml_value(value, environment.emission)) return {filename + ": parse error"}; if (key == "emission_tex") - if (!get_yaml_ref(value, environment.emission_tex, tmap)) + get_yaml_ref(value, environment.emission_tex, tmap)) return {filename + ": parse error"}; if (key == "uri") { auto uri = ""s; - if (!get_yaml_value(value, uri)) return {filename + ": parse error"}; + get_yaml_value(value, uri)) return {filename + ": parse error"}; environment.name = get_basename(uri); } if (key == "lookat") { auto lookat = identity3x3f; - if (!get_yaml_value(value, lookat)) return {filename + ": parse error"}; + get_yaml_value(value, lookat)) return {filename + ": parse error"}; environment.frame = lookat_frame(lookat.x, lookat.y, lookat.z, true); } } @@ -1775,17 +1621,17 @@ sceneio_status load_yaml(const string& filename, sceneio_model& scene) { #endif // Save a scene in the builtin YAML format. -static sceneio_status load_yaml_scene( +static void load_yaml_scene( const string& filename, sceneio_model& scene, bool noparallel) { scene = {}; // Parse yaml - if (auto ret = load_yaml(filename, scene, noparallel); !ret) return ret; + load_yaml(filename, scene, noparallel); // load shape and textures - if (auto ret = load_shapes(filename, scene, noparallel); !ret) return ret; - if (auto ret = load_subdivs(filename, scene, noparallel); !ret) return ret; - if (auto ret = load_textures(filename, scene, noparallel); !ret) return ret; + load_shapes(filename, scene, noparallel); + load_subdivs(filename, scene, noparallel); + load_textures(filename, scene, noparallel); // fix scene scene.name = get_basename(filename); @@ -1793,14 +1639,11 @@ static sceneio_status load_yaml_scene( add_materials(scene); add_radius(scene); trim_memory(scene); - - return {}; } // Save yaml -static sceneio_status save_yaml(const string& filename, - const sceneio_model& scene, bool ply_instances = false, - const string& instances_name = "") { +static void save_yaml(const string& filename, const sceneio_model& scene, + bool ply_instances = false, const string& instances_name = "") { auto yaml = yaml_model{}; for (auto stat : scene_stats(scene)) yaml.comments.push_back(stat); @@ -1939,21 +1782,17 @@ static sceneio_status save_yaml(const string& filename, scene.textures[environment.emission_tex].name); } - if (auto ret = save_yaml(filename, yaml); !ret) return {ret.error}; - - return {}; + save_yaml(filename, yaml); } // Save a scene in the builtin YAML format. -static sceneio_status save_yaml_scene( +static void save_yaml_scene( const string& filename, const sceneio_model& scene, bool noparallel) { // save yaml file - if (auto ret = save_yaml(filename, scene); !ret) return ret; - if (auto ret = save_shapes(filename, scene, noparallel); !ret) return ret; - if (auto ret = save_subdivs(filename, scene, noparallel); !ret) return ret; - if (auto ret = save_textures(filename, scene, noparallel); !ret) return ret; - - return {}; + save_yaml(filename, scene); + save_shapes(filename, scene, noparallel); + save_subdivs(filename, scene, noparallel); + save_textures(filename, scene, noparallel); } } // namespace yocto @@ -1963,11 +1802,10 @@ static sceneio_status save_yaml_scene( // ----------------------------------------------------------------------------- namespace yocto { -static sceneio_status load_obj(const string& filename, sceneio_model& scene) { +static void load_obj(const string& filename, sceneio_model& scene) { // load obj auto obj = obj_model{}; - if (auto ret = load_obj(filename, obj, false, true, true); !ret) - return {ret.error}; + load_obj(filename, obj, false, true, true); // convert cameras for (auto& ocam : obj.cameras) { @@ -2055,14 +1893,15 @@ static sceneio_status load_obj(const string& filename, sceneio_model& scene) { get_obj_points(obj, oshape, shape.points, shape.positions, shape.normals, shape.texcoords, materials, ematerials, true); } else { - return {filename + ": empty shape"}; + throw_emptyshape_error(filename, oshape.name); } // get material if (oshape.materials.size() != 1) { - return {filename + ": missing material for " + oshape.name}; + throw_missing_reference_error(filename, "material for", oshape.name); } if (material_map.find(oshape.materials.at(0)) == material_map.end()) { - return {filename + ": missing material " + oshape.materials.at(0)}; + throw_missing_reference_error( + filename, "material", oshape.materials.at(0)); } auto material = material_map.at(oshape.materials.at(0)); // make instances @@ -2091,29 +1930,23 @@ static sceneio_status load_obj(const string& filename, sceneio_model& scene) { environment.emission = oenvironment.emission; environment.emission_tex = get_texture(oenvironment.emission_map); } - - return {}; } // Loads an OBJ -static sceneio_status load_obj_scene( +static void load_obj_scene( const string& filename, sceneio_model& scene, bool noparallel) { - scene = {}; - // Parse obj - if (auto ret = load_obj(filename, scene); !ret) return ret; - if (auto ret = load_textures(filename, scene, noparallel); !ret) return ret; + load_obj(filename, scene); + load_textures(filename, scene, noparallel); // fix scene scene.name = get_basename(filename); add_cameras(scene); add_materials(scene); add_radius(scene); - - return {}; } -static sceneio_status save_obj( +static void save_obj( const string& filename, const sceneio_model& scene, bool instances) { auto obj = obj_model{}; @@ -2189,7 +2022,7 @@ static sceneio_status save_obj( add_obj_points(obj, shape.name, shape.points, shape.positions, shape.normals, shape.texcoords, {}, {}, true); } else { - throw std::runtime_error("do not support empty shapes"); + throw_emptyshape_error(filename, shape.name); } } for (auto& instance : scene.instances) { @@ -2215,7 +2048,7 @@ static sceneio_status save_obj( add_obj_points(obj, instance.name, shape.points, positions, normals, shape.texcoords, materials, {}, true); } else { - return {filename + ": do not support empty shapes"}; + throw_emptyshape_error(filename, shape.name); } } } @@ -2230,16 +2063,13 @@ static sceneio_status save_obj( } // save obj - if (auto ret = save_obj(filename, obj); !ret) return {ret.error}; - - return {}; + save_obj(filename, obj); } -static sceneio_status save_obj_scene(const string& filename, - const sceneio_model& scene, bool instances, bool noparallel) { - if (auto ret = save_obj(filename, scene, instances); !ret) return ret; - if (auto ret = save_textures(filename, scene, noparallel)) return ret; - return {}; +static void save_obj_scene(const string& filename, const sceneio_model& scene, + bool instances, bool noparallel) { + save_obj(filename, scene, instances); + save_textures(filename, scene, noparallel); } void print_obj_camera(const sceneio_camera& camera) { @@ -2258,7 +2088,7 @@ void print_obj_camera(const sceneio_camera& camera) { // ----------------------------------------------------------------------------- namespace yocto { -static sceneio_status load_ply_scene( +static void load_ply_scene( const string& filename, sceneio_model& scene, bool noparallel) { scene = {}; @@ -2267,11 +2097,12 @@ static sceneio_status load_ply_scene( auto& shape = scene.shapes.back(); shape.name = "shape"; shape.filename = get_filename(filename); - if (auto ret = load_shape(filename, shape.points, shape.lines, - shape.triangles, shape.quads, shape.positions, shape.normals, - shape.texcoords, shape.colors, shape.radius); - !ret) { - return {ret.error}; + try { + load_shape(filename, shape.points, shape.lines, shape.triangles, + shape.quads, shape.positions, shape.normals, shape.texcoords, + shape.colors, shape.radius); + } catch (std::exception& e) { + throw_dependent_error(filename, e.what()); } // add instance @@ -2285,18 +2116,15 @@ static sceneio_status load_ply_scene( add_cameras(scene); add_materials(scene); add_radius(scene); - - return {}; } -static sceneio_status save_ply_scene( +static void save_ply_scene( const string& filename, const sceneio_model& scene, bool noparallel) { - if (scene.shapes.empty()) return {filename + ": empty scene"}; + if (scene.shapes.empty()) throw_emptyshape_error(filename, ""); auto& shape = scene.shapes.front(); save_shape(filename, shape.points, shape.lines, shape.triangles, shape.quads, shape.positions, shape.normals, shape.texcoords, shape.colors, shape.radius); - return {}; } } // namespace yocto @@ -2307,9 +2135,9 @@ static sceneio_status save_ply_scene( namespace yocto { // convert gltf to scene -static sceneio_status load_gltf(const string& filename, sceneio_model& scene) { +static void load_gltf(const string& filename, sceneio_model& scene) { auto gltf = gltf_model{}; - if (auto ret = load_gltf(filename, gltf); !ret) return {ret.error}; + load_gltf(filename, gltf); // convert textures for (auto& gtexture : gltf.textures) { @@ -2406,19 +2234,14 @@ static sceneio_status load_gltf(const string& filename, sceneio_model& scene) { } } } - - return {}; } // Load a scene -static sceneio_status load_gltf_scene( +static void load_gltf_scene( const string& filename, sceneio_model& scene, bool noparallel) { - // initialization - scene = {}; - // load gltf - if (auto ret = load_gltf(filename, scene); !ret) return ret; - if (auto ret = load_textures(filename, scene, noparallel)) return ret; + load_gltf(filename, scene); + load_textures(filename, scene, noparallel); // fix scene scene.name = get_basename(filename); @@ -2433,8 +2256,6 @@ static sceneio_status load_gltf_scene( auto distance = dot(-camera.frame.z, center - camera.frame.o); if (distance > 0) camera.focus = distance; } - - return {}; } } // namespace yocto @@ -2444,11 +2265,11 @@ static sceneio_status load_gltf_scene( // ----------------------------------------------------------------------------- namespace yocto { -static sceneio_status load_pbrt( +static void load_pbrt( const string& filename, sceneio_model& scene, bool noparallel) { // load pbrt auto pbrt = pbrt_model{}; - if (auto ret = load_pbrt(filename, pbrt); !ret) return {ret.error}; + load_pbrt(filename, pbrt); // convert cameras for (auto& pcamera : pbrt.cameras) { @@ -2580,32 +2401,25 @@ static sceneio_status load_pbrt( instance.shape = (int)scene.shapes.size() - 1; instance.material = (int)scene.materials.size() - 1; } - - return {}; } // load pbrt scenes -static sceneio_status load_pbrt_scene( +static void load_pbrt_scene( const string& filename, sceneio_model& scene, bool noparallel) { - scene = sceneio_model{}; - // Parse pbrt - if (auto ret = load_pbrt(filename, scene, noparallel); !ret) return ret; - if (auto ret = load_shapes(filename, scene, noparallel); !ret) return ret; - if (auto ret = load_textures(filename, scene, noparallel); !ret) return ret; + load_pbrt(filename, scene, noparallel); + load_shapes(filename, scene, noparallel); + load_textures(filename, scene, noparallel); // fix scene scene.name = get_basename(filename); add_cameras(scene); add_materials(scene); add_radius(scene); - - return {}; } // Convert a scene to pbrt format -static sceneio_status save_pbrt( - const string& filename, const sceneio_model& scene) { +static void save_pbrt(const string& filename, const sceneio_model& scene) { auto pbrt = pbrt_model{}; // embed data @@ -2664,33 +2478,26 @@ static sceneio_status save_pbrt( } } - if (auto ret = save_pbrt(filename, pbrt); !ret) return {ret.error}; - - return {}; + save_pbrt(filename, pbrt); } // Save a pbrt scene -sceneio_status save_pbrt_scene( +void save_pbrt_scene( const string& filename, const sceneio_model& scene, bool noparallel) { // save pbrt - if (auto ret = save_pbrt(filename, scene); !ret) return ret; + save_pbrt(filename, scene); // save meshes auto dirname = get_dirname(filename); for (auto& shape : scene.shapes) { - if (auto ret = save_shape( - replace_extension(dirname + shape.filename, ".ply"), shape.points, - shape.lines, shape.triangles, shape.quads, shape.positions, - shape.normals, shape.texcoords, shape.colors, shape.radius); - !ret) { - return {filename + ": missing shape (" + ret.error + ")"}; - } + save_shape(replace_extension(dirname + shape.filename, ".ply"), + shape.points, shape.lines, shape.triangles, shape.quads, + shape.positions, shape.normals, shape.texcoords, shape.colors, + shape.radius); } // save textures - if (auto ret = save_textures(filename, scene, noparallel); !ret) return ret; - - return {}; + save_textures(filename, scene, noparallel); } } // namespace yocto diff --git a/yocto/yocto_sceneio.h b/yocto/yocto_sceneio.h index 9297232d9..d9bece25c 100644 --- a/yocto/yocto_sceneio.h +++ b/yocto/yocto_sceneio.h @@ -265,31 +265,26 @@ struct sceneio_model { namespace yocto { -// Scene io status -struct sceneio_status { - string error = {}; - explicit operator bool() const { return error.empty(); } -}; - -// Load/save a scene in the supported formats. -sceneio_status load_scene( +// Load/save a scene in the supported formats. Throws on error. +void load_scene( const string& filename, sceneio_model& scene, bool noparallel = false); -sceneio_status save_scene(const string& filename, const sceneio_model& scene, +void save_scene(const string& filename, const sceneio_model& scene, bool noparallel = false); // Load/save a shape in the supported formats. Filename is the scene filename. -sceneio_status load_shape(const string& filename, sceneio_shape& shape); -sceneio_status save_shape(const string& filename, const sceneio_shape& shape); +// Throws on error. +void load_shape(const string& filename, sceneio_shape& shape); +void save_shape(const string& filename, const sceneio_shape& shape); // Load/save a subdiv in the supported formats. Filename is the scene filename. -sceneio_status load_subdiv(const string& filename, sceneio_subdiv& subdiv); -sceneio_status save_subdiv( - const string& filename, const sceneio_subdiv& subdiv); +// Throws on error. +void load_subdiv(const string& filename, sceneio_subdiv& subdiv); +void save_subdiv(const string& filename, const sceneio_subdiv& subdiv); // Load/save a texture in the supported formats. Filename is the scene filename. -sceneio_status load_texture(const string& filename, sceneio_texture& texture); -sceneio_status save_texture( - const string& filename, const sceneio_texture& texture); +// Throws on error. +void load_texture(const string& filename, sceneio_texture& texture); +void save_texture(const string& filename, const sceneio_texture& texture); } // namespace yocto diff --git a/yocto/yocto_shape.cpp b/yocto/yocto_shape.cpp index 09c3a4fb8..003ddd125 100644 --- a/yocto/yocto_shape.cpp +++ b/yocto/yocto_shape.cpp @@ -3498,6 +3498,14 @@ void make_shape_preset(vector& points, vector& lines, // ----------------------------------------------------------------------------- namespace yocto { +// Helpers for throwing +static void throw_format_error(const string& filename) { + throw std::runtime_error{filename + ": unknown format"}; +} +static void throw_emptyshape_error(const string& filename) { + throw std::runtime_error{filename + ": empty shape"}; +} + // Utility to normalize a path static inline string normalize_path(const string& filename_) { auto filename = filename_; @@ -3528,15 +3536,10 @@ static inline string get_extension(const string& filename_) { } // Load ply mesh -shapeio_status load_shape(const string& filename, vector& points, +void load_shape(const string& filename, vector& points, vector& lines, vector& triangles, vector& quads, vector& positions, vector& normals, vector& texcoords, vector& colors, vector& radius, bool flip_texcoord) { - auto ok = []() { return shapeio_status{}; }; - auto error = [&filename](const string& err) { - return shapeio_status{filename + ": " + err}; - }; - points = {}; lines = {}; triangles = {}; @@ -3551,7 +3554,7 @@ shapeio_status load_shape(const string& filename, vector& points, if (ext == ".ply" || ext == ".PLY") { // open ply auto ply = ply_model{}; - if (auto ret = load_ply(filename, ply); !ret) return error(ret.error); + load_ply(filename, ply); // gets vertex positions = get_ply_positions(ply); @@ -3569,19 +3572,18 @@ shapeio_status load_shape(const string& filename, vector& points, lines = get_ply_lines(ply); points = get_ply_points(ply); - if (positions.empty()) return error("empty shape"); - return ok(); + if (positions.empty()) throw_emptyshape_error(filename); } else if (ext == ".obj" || ext == ".OBJ") { // load obj auto obj = obj_model(); - if (auto ret = load_obj(filename, obj, true); !ret) return error(ret.error); + load_obj(filename, obj, true); // get shape - if (obj.shapes.empty()) return error("empty shape"); - if (obj.shapes.size() > 1) return error("diffent element types"); + if (obj.shapes.empty()) throw_emptyshape_error(filename); + if (obj.shapes.size() > 1) throw_emptyshape_error(filename); auto& shape = obj.shapes.front(); if (shape.points.empty() && shape.lines.empty() && shape.faces.empty()) - return ok(); + return; // decide what to do and get properties auto materials = vector{}; @@ -3600,28 +3602,22 @@ shapeio_status load_shape(const string& filename, vector& points, get_obj_points(obj, shape, points, positions, normals, texcoords, materials, ematerials, flip_texcoord); } else { - return error("empty shape"); + throw_emptyshape_error(filename); } - if (positions.empty()) return error("empty shape"); - return ok(); + if (positions.empty()) throw_emptyshape_error(filename); } else { - return error("unsupported format"); + throw_format_error(filename); } } // Save ply mesh -shapeio_status save_shape(const string& filename, const vector& points, +void save_shape(const string& filename, const vector& points, const vector& lines, const vector& triangles, const vector& quads, const vector& positions, const vector& normals, const vector& texcoords, const vector& colors, const vector& radius, bool ascii, bool flip_texcoord) { - auto ok = []() { return shapeio_status{}; }; - auto error = [&filename](const string& err) { - return shapeio_status{filename + ": " + err}; - }; - auto ext = get_extension(filename); if (ext == ".ply" || ext == ".PLY") { // create ply @@ -3634,8 +3630,7 @@ shapeio_status save_shape(const string& filename, const vector& points, add_ply_faces(ply, triangles, quads); add_ply_lines(ply, lines); add_ply_points(ply, points); - if (auto ret = save_ply(filename, ply); !ret) return error(ret.error); - return ok(); + save_ply(filename, ply); } else if (ext == ".obj" || ext == ".OBJ") { auto obj = obj_model{}; if (!triangles.empty()) { @@ -3651,27 +3646,20 @@ shapeio_status save_shape(const string& filename, const vector& points, add_obj_points(obj, "", points, positions, normals, texcoords, {}, {}, flip_texcoord); } else { - return error("empty shape"); + throw_emptyshape_error(filename); } auto err = ""s; - if (!save_obj(filename, obj)) return error(err); - return ok(); + save_obj(filename, obj); } else { - return error("unsupported format"); - ; + throw_format_error(filename); } } // Load ply mesh -shapeio_status load_fvshape(const string& filename, vector& quadspos, +void load_fvshape(const string& filename, vector& quadspos, vector& quadsnorm, vector& quadstexcoord, vector& positions, vector& normals, vector& texcoords, bool flip_texcoord) { - auto ok = []() { return shapeio_status{}; }; - auto error = [&filename](const string& err) { - return shapeio_status{filename + ": " + err}; - }; - quadspos = {}; quadsnorm = {}; quadstexcoord = {}; @@ -3682,46 +3670,38 @@ shapeio_status load_fvshape(const string& filename, vector& quadspos, auto ext = get_extension(filename); if (ext == ".ply" || ext == ".PLY") { auto ply = ply_model{}; - if (auto ret = load_ply(filename, ply); !ret) return error(ret.error); + load_ply(filename, ply); positions = get_ply_positions(ply); normals = get_ply_normals(ply); texcoords = get_ply_texcoords(ply, flip_texcoord); quadspos = get_ply_quads(ply); if (!normals.empty()) quadsnorm = quadspos; if (!texcoords.empty()) quadstexcoord = quadspos; - if (positions.empty()) return error("empty shape"); - return ok(); + if (positions.empty()) throw_emptyshape_error(filename); } else if (ext == ".obj" || ext == ".OBJ") { auto obj = obj_model(); auto err = ""s; - if (!load_obj(filename, obj, true)) return error(err); - if (obj.shapes.empty()) return error("empty shape"); - if (obj.shapes.size() > 1) return error("diffent element types"); + load_obj(filename, obj, true); + if (obj.shapes.empty()) throw_emptyshape_error(filename); + if (obj.shapes.size() > 1) throw_emptyshape_error(filename); auto& shape = obj.shapes.front(); - if (shape.faces.empty()) return error("empty shape"); + if (shape.faces.empty()) throw_emptyshape_error(filename); auto materials = vector{}; auto ematerials = vector{}; get_obj_fvquads(obj, shape, quadspos, quadsnorm, quadstexcoord, positions, normals, texcoords, materials, ematerials, flip_texcoord); - if (positions.empty()) return error("empty shape"); - return ok(); + throw_emptyshape_error(filename); } else { - return error("unsupported format"); + throw_format_error(filename); ; } } // Save ply mesh -shapeio_status save_fvshape(const string& filename, - const vector& quadspos, const vector& quadsnorm, - const vector& quadstexcoord, const vector& positions, - const vector& normals, const vector& texcoords, bool ascii, - bool flip_texcoord) { - auto ok = []() { return shapeio_status{}; }; - auto error = [&filename](const string& err) { - return shapeio_status{filename + ": " + err}; - }; - +void save_fvshape(const string& filename, const vector& quadspos, + const vector& quadsnorm, const vector& quadstexcoord, + const vector& positions, const vector& normals, + const vector& texcoords, bool ascii, bool flip_texcoord) { auto ext = get_extension(filename); if (ext == ".ply" || ext == ".PLY") { auto [split_quads, split_positions, split_normals, split_texturecoords] = @@ -3738,10 +3718,9 @@ shapeio_status save_fvshape(const string& filename, normals, texcoords, {}, {}, flip_texcoord); // Save - if (auto ret = save_obj(filename, obj); !ret) return error(ret.error); - return ok(); + save_obj(filename, obj); } else { - return error("unsupported format"); + throw_format_error(filename); } } diff --git a/yocto/yocto_shape.h b/yocto/yocto_shape.h index 63a6e0990..719e012bc 100644 --- a/yocto/yocto_shape.h +++ b/yocto/yocto_shape.h @@ -479,18 +479,12 @@ vec3f compute_gradient(const vec3i& triangle, const vector& positions, // ----------------------------------------------------------------------------- namespace yocto { -// Result of io operations -struct shapeio_status { - string error = {}; - explicit operator bool() const { return error.empty(); } -}; - // Load/save a shape as indexed meshes -shapeio_status load_shape(const string& filename, vector& points, +void load_shape(const string& filename, vector& points, vector& lines, vector& triangles, vector& quads, vector& positions, vector& normals, vector& texcoords, vector& colors, vector& radius, bool flip_texcoords = true); -shapeio_status save_shape(const string& filename, const vector& points, +void save_shape(const string& filename, const vector& points, const vector& lines, const vector& triangles, const vector& quads, const vector& positions, const vector& normals, const vector& texcoords, @@ -498,15 +492,15 @@ shapeio_status save_shape(const string& filename, const vector& points, bool ascii = false, bool flip_texcoords = true); // Load/save a facevarying shape -shapeio_status load_fvshape(const string& filename, vector& quadspos, +void load_fvshape(const string& filename, vector& quadspos, vector& quadsnorm, vector& quadstexcoord, vector& positions, vector& normals, vector& texcoords, bool flip_texcoords = true); -shapeio_status save_fvshape(const string& filename, - const vector& quadspos, const vector& quadsnorm, - const vector& quadstexcoord, const vector& positions, - const vector& normals, const vector& texcoords, - bool ascii = false, bool flip_texcoords = true); +void save_fvshape(const string& filename, const vector& quadspos, + const vector& quadsnorm, const vector& quadstexcoord, + const vector& positions, const vector& normals, + const vector& texcoords, bool ascii = false, + bool flip_texcoords = true); } // namespace yocto diff --git a/yocto/yocto_trace.cpp b/yocto/yocto_trace.cpp index af1eaf8fa..8d1d89442 100644 --- a/yocto/yocto_trace.cpp +++ b/yocto/yocto_trace.cpp @@ -1336,7 +1336,7 @@ static RTCDevice trace_embree_device() { } // Initialize Embree BVH -static void init_embree_bvh(trace_shape* shape, const trace_params& params) { +static void init_embree_bvh(trace_shape& shape, const trace_params& params) { auto edevice = trace_embree_device(); auto escene = rtcNewScene(edevice); if (params.bvh == trace_bvh_type::embree_compact) @@ -1458,9 +1458,9 @@ static void init_embree_bvh(trace_scene& scene, const trace_params& params) { rtcSetSceneBuildQuality(escene, RTC_BUILD_QUALITY_HIGH); for (auto instance_id = 0; instance_id < scene.instances.size(); instance_id++) { - auto instance = &scene.instances[instance_id]; - auto shape = &scene.shapes[instance.shape]; - auto egeometry = rtcNewGeometry(edevice, RTC_GEOMETRY_TYPE_INSTANCE); + auto& instance = scene.instances[instance_id]; + auto& shape = scene.shapes[instance.shape]; + auto egeometry = rtcNewGeometry(edevice, RTC_GEOMETRY_TYPE_INSTANCE); rtcSetGeometryInstancedScene(egeometry, (RTCScene)shape.embree_bvh.get()); rtcSetGeometryTransform( egeometry, 0, RTC_FORMAT_FLOAT3X4_COLUMN_MAJOR, &instance.frame); @@ -1477,9 +1477,9 @@ static void update_embree_bvh( // scene bvh auto escene = (RTCScene)scene.embree_bvh.get(); for (auto instance_id : updated_instances) { - auto instance = &scene.instances[instance_id]; - auto shape = &scene.shapes[instance.shape]; - auto egeometry = rtcGetGeometry(escene, instance_id); + auto& instance = scene.instances[instance_id]; + auto& shape = scene.shapes[instance.shape]; + auto egeometry = rtcGetGeometry(escene, instance_id); rtcSetGeometryInstancedScene(egeometry, (RTCScene)shape.embree_bvh.get()); rtcSetGeometryTransform( egeometry, 0, RTC_FORMAT_FLOAT3X4_COLUMN_MAJOR, &instance.frame);