Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/io/wavefront_obj/exporter/obj_exporter.cc')
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_exporter.cc302
1 files changed, 302 insertions, 0 deletions
diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc
new file mode 100644
index 00000000000..d15d053adc9
--- /dev/null
+++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc
@@ -0,0 +1,302 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup obj
+ */
+
+#include <cstdio>
+#include <exception>
+#include <memory>
+
+#include "BKE_scene.h"
+
+#include "BLI_path_util.h"
+#include "BLI_vector.hh"
+
+#include "DEG_depsgraph_query.h"
+
+#include "DNA_scene_types.h"
+
+#include "ED_object.h"
+
+#include "obj_export_mesh.hh"
+#include "obj_export_nurbs.hh"
+#include "obj_exporter.hh"
+
+#include "obj_export_file_writer.hh"
+
+namespace blender::io::obj {
+
+OBJDepsgraph::OBJDepsgraph(const bContext *C, const eEvaluationMode eval_mode)
+{
+ Scene *scene = CTX_data_scene(C);
+ Main *bmain = CTX_data_main(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ if (eval_mode == DAG_EVAL_RENDER) {
+ depsgraph_ = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER);
+ needs_free_ = true;
+ DEG_graph_build_for_all_objects(depsgraph_);
+ BKE_scene_graph_evaluated_ensure(depsgraph_, bmain);
+ }
+ else {
+ depsgraph_ = CTX_data_ensure_evaluated_depsgraph(C);
+ needs_free_ = false;
+ }
+}
+
+OBJDepsgraph::~OBJDepsgraph()
+{
+ if (needs_free_) {
+ DEG_graph_free(depsgraph_);
+ }
+}
+
+Depsgraph *OBJDepsgraph::get()
+{
+ return depsgraph_;
+}
+
+void OBJDepsgraph::update_for_newframe()
+{
+ BKE_scene_graph_update_for_newframe(depsgraph_);
+}
+
+static void print_exception_error(const std::system_error &ex)
+{
+ std::cerr << ex.code().category().name() << ": " << ex.what() << ": " << ex.code().message()
+ << std::endl;
+}
+
+/**
+ * Filter supported objects from the Scene.
+ *
+ * \note Curves are also stored with Meshes if export settings specify so.
+ */
+std::pair<Vector<std::unique_ptr<OBJMesh>>, Vector<std::unique_ptr<OBJCurve>>>
+filter_supported_objects(Depsgraph *depsgraph, const OBJExportParams &export_params)
+{
+ Vector<std::unique_ptr<OBJMesh>> r_exportable_meshes;
+ Vector<std::unique_ptr<OBJCurve>> r_exportable_nurbs;
+ const ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
+ LISTBASE_FOREACH (const Base *, base, &view_layer->object_bases) {
+ Object *object_in_layer = base->object;
+ if (export_params.export_selected_objects && !(object_in_layer->base_flag & BASE_SELECTED)) {
+ continue;
+ }
+ switch (object_in_layer->type) {
+ case OB_SURF:
+ /* Export in mesh form: vertices and polygons. */
+ ATTR_FALLTHROUGH;
+ case OB_MESH:
+ r_exportable_meshes.append(
+ std::make_unique<OBJMesh>(depsgraph, export_params, object_in_layer));
+ break;
+ case OB_CURVE: {
+ Curve *curve = static_cast<Curve *>(object_in_layer->data);
+ Nurb *nurb{static_cast<Nurb *>(curve->nurb.first)};
+ if (!nurb) {
+ /* An empty curve. Not yet supported to export these as meshes. */
+ if (export_params.export_curves_as_nurbs) {
+ r_exportable_nurbs.append(
+ std::make_unique<OBJCurve>(depsgraph, export_params, object_in_layer));
+ }
+ break;
+ }
+ switch (nurb->type) {
+ case CU_NURBS:
+ if (export_params.export_curves_as_nurbs) {
+ /* Export in parameter form: control points. */
+ r_exportable_nurbs.append(
+ std::make_unique<OBJCurve>(depsgraph, export_params, object_in_layer));
+ }
+ else {
+ /* Export in mesh form: edges and vertices. */
+ r_exportable_meshes.append(
+ std::make_unique<OBJMesh>(depsgraph, export_params, object_in_layer));
+ }
+ break;
+ case CU_BEZIER:
+ /* Always export in mesh form: edges and vertices. */
+ r_exportable_meshes.append(
+ std::make_unique<OBJMesh>(depsgraph, export_params, object_in_layer));
+ break;
+ default:
+ /* Other curve types are not supported. */
+ break;
+ }
+ break;
+ }
+ default:
+ /* Other object types are not supported. */
+ break;
+ }
+ }
+ return {std::move(r_exportable_meshes), std::move(r_exportable_nurbs)};
+}
+
+static void write_mesh_objects(Vector<std::unique_ptr<OBJMesh>> exportable_as_mesh,
+ OBJWriter &obj_writer,
+ MTLWriter *mtl_writer,
+ const OBJExportParams &export_params)
+{
+ if (mtl_writer) {
+ obj_writer.write_mtllib_name(mtl_writer->mtl_file_path());
+ }
+
+ /* Smooth groups and UV vertex indices may make huge memory allocations, so they should be freed
+ * right after they're written, instead of waiting for #blender::Vector to clean them up after
+ * all the objects are exported. */
+ for (auto &obj_mesh : exportable_as_mesh) {
+ obj_writer.write_object_name(*obj_mesh);
+ obj_writer.write_vertex_coords(*obj_mesh);
+ Vector<int> obj_mtlindices;
+
+ if (obj_mesh->tot_polygons() > 0) {
+ if (export_params.export_smooth_groups) {
+ obj_mesh->calc_smooth_groups(export_params.smooth_groups_bitflags);
+ }
+ if (export_params.export_normals) {
+ obj_writer.write_poly_normals(*obj_mesh);
+ }
+ if (export_params.export_uv) {
+ obj_writer.write_uv_coords(*obj_mesh);
+ }
+ if (mtl_writer) {
+ obj_mtlindices = mtl_writer->add_materials(*obj_mesh);
+ }
+ /* This function takes a 0-indexed slot index for the obj_mesh object and
+ * returns the material name that we are using in the .obj file for it. */
+ std::function<const char *(int)> matname_fn = [&](int s) -> const char * {
+ if (!mtl_writer || s < 0 || s >= obj_mtlindices.size()) {
+ return nullptr;
+ }
+ return mtl_writer->mtlmaterial_name(obj_mtlindices[s]);
+ };
+ obj_writer.write_poly_elements(*obj_mesh, matname_fn);
+ }
+ obj_writer.write_edges_indices(*obj_mesh);
+
+ obj_writer.update_index_offsets(*obj_mesh);
+ }
+}
+
+/**
+ * Export NURBS Curves in parameter form, not as vertices and edges.
+ */
+static void write_nurbs_curve_objects(const Vector<std::unique_ptr<OBJCurve>> &exportable_as_nurbs,
+ const OBJWriter &obj_writer)
+{
+ /* #OBJCurve doesn't have any dynamically allocated memory, so it's fine
+ * to wait for #blender::Vector to clean the objects up. */
+ for (const std::unique_ptr<OBJCurve> &obj_curve : exportable_as_nurbs) {
+ obj_writer.write_nurbs_curve(*obj_curve);
+ }
+}
+
+/**
+ * Export a single frame to a .OBJ file.
+ *
+ * Conditionally write a .MTL file also.
+ */
+void export_frame(Depsgraph *depsgraph, const OBJExportParams &export_params, const char *filepath)
+{
+ std::unique_ptr<OBJWriter> frame_writer = nullptr;
+ try {
+ frame_writer = std::make_unique<OBJWriter>(filepath, export_params);
+ }
+ catch (const std::system_error &ex) {
+ print_exception_error(ex);
+ return;
+ }
+ if (!frame_writer) {
+ BLI_assert(!"File should be writable by now.");
+ return;
+ }
+ std::unique_ptr<MTLWriter> mtl_writer = nullptr;
+ if (export_params.export_materials) {
+ try {
+ mtl_writer = std::make_unique<MTLWriter>(export_params.filepath);
+ }
+ catch (const std::system_error &ex) {
+ print_exception_error(ex);
+ }
+ }
+
+ frame_writer->write_header();
+
+ auto [exportable_as_mesh, exportable_as_nurbs] = filter_supported_objects(depsgraph,
+ export_params);
+
+ write_mesh_objects(
+ std::move(exportable_as_mesh), *frame_writer, mtl_writer.get(), export_params);
+ if (mtl_writer) {
+ mtl_writer->write_header(export_params.blen_filepath);
+ mtl_writer->write_materials();
+ }
+ write_nurbs_curve_objects(std::move(exportable_as_nurbs), *frame_writer);
+}
+
+/**
+ * Append the current frame number in the .OBJ file name.
+ *
+ * \return Whether the filepath is in #FILE_MAX limits.
+ */
+bool append_frame_to_filename(const char *filepath, const int frame, char *r_filepath_with_frames)
+{
+ BLI_strncpy(r_filepath_with_frames, filepath, FILE_MAX);
+ BLI_path_extension_replace(r_filepath_with_frames, FILE_MAX, "");
+ const int digits = frame == 0 ? 1 : integer_digits_i(abs(frame));
+ BLI_path_frame(r_filepath_with_frames, frame, digits);
+ return BLI_path_extension_replace(r_filepath_with_frames, FILE_MAX, ".obj");
+}
+
+/**
+ * Central internal function to call Scene update & writer functions.
+ */
+void exporter_main(bContext *C, const OBJExportParams &export_params)
+{
+ ED_object_mode_set(C, OB_MODE_OBJECT);
+ OBJDepsgraph obj_depsgraph(C, export_params.export_eval_mode);
+ Scene *scene = DEG_get_input_scene(obj_depsgraph.get());
+ const char *filepath = export_params.filepath;
+
+ /* Single frame export, i.e. no animation. */
+ if (!export_params.export_animation) {
+ fprintf(stderr, "Writing to %s\n", filepath);
+ export_frame(obj_depsgraph.get(), export_params, filepath);
+ return;
+ }
+
+ char filepath_with_frames[FILE_MAX];
+ /* Used to reset the Scene to its original state. */
+ const int original_frame = CFRA;
+
+ for (int frame = export_params.start_frame; frame <= export_params.end_frame; frame++) {
+ const bool filepath_ok = append_frame_to_filename(filepath, frame, filepath_with_frames);
+ if (!filepath_ok) {
+ fprintf(stderr, "Error: File Path too long.\n%s\n", filepath_with_frames);
+ return;
+ }
+
+ CFRA = frame;
+ obj_depsgraph.update_for_newframe();
+ fprintf(stderr, "Writing to %s\n", filepath_with_frames);
+ export_frame(obj_depsgraph.get(), export_params, filepath_with_frames);
+ }
+ CFRA = original_frame;
+}
+} // namespace blender::io::obj