1
1

Compare commits

...

53 Commits

Author SHA1 Message Date
b3fbc7f191 [Fast import/export] Fixed segfault on some files 2019-07-21 19:46:58 +01:00
e42a7de767 Merge remote-tracking branch 'origin/master' into soc-2019-fast-io 2019-07-21 01:37:07 +01:00
309bc3c653 [Fast import/export] Added OBJ Import with support for only one object and using only vertices and faces 2019-07-21 01:36:08 +01:00
014b53304e [Fast import/export] Added boiler plate for OBJ importing 2019-07-18 01:22:46 +01:00
af9ff0f0ee [Fast import/export] UI for OBJ import 2019-07-15 21:19:33 +01:00
e77c87a2a9 Merge remote-tracking branch 'origin/master' into soc-2019-fast-io 2019-07-15 16:32:41 +01:00
d187b683e6 [Fast import/export] Added proper material support 2019-07-12 16:01:04 +01:00
be6e1ed92a [Fast import/export] Fixed index of normals 2019-07-12 16:01:04 +01:00
d1409d2420 [Fast import/export] Added support for smooth groups 2019-07-11 10:43:05 +01:00
7d99e457cd [Fast import/export] Fixed indexes of normals, using poly vertex instead of insertion order 2019-07-11 10:41:06 +01:00
b6a1af07b4 [Fast import/export] Small refactoring to consistently use size_t isntead of ulong 2019-07-11 10:31:35 +01:00
7b26bc3779 Merge remote-tracking branch 'origin/master' into soc-2019-fast-io 2019-07-10 10:31:26 +01:00
55f6cf2fbb [Fast import/export] Fixed bug where address of temporary pointer is taken, leading to UB 2019-07-06 03:36:52 +01:00
52c6143fb4 [Fast import/export] Fixed OBJ indexing, after previous commit 2019-07-06 03:03:28 +01:00
c9d5f50057 [Fast import/export] Reorder OBJ export so vertices, UVs and Normals for each mesh are respectively grouped. Only write mtllib if exporting materials 2019-07-06 01:20:27 +01:00
c49b65a27e [Fast import/export] Fixed Axis Conversion 2019-07-05 00:19:41 +01:00
a4383d39d9 Merge remote-tracking branch 'origin/master' into soc-2019-fast-io 2019-07-03 16:23:09 +01:00
8cad95d10b [Fast import/export] Fixed crash related to normal_iter and incorrect default aixs remapping behaviour 2019-07-03 16:18:49 +01:00
922e549c65 [Fast import/export] Fixed small memory leak and apply object transform to mesh 2019-06-28 18:03:40 +01:00
4ab735a46e [Fast import/export] Fixed two off by one bugs and writing UVs indexes even though none are available 2019-06-28 17:45:08 +01:00
cfb6f02870 [Fast import/export] Removed clang-format overrides 2019-06-28 14:50:19 +01:00
aff190c14a [Fast import/export] Refactoring, using DEG_OBJECT_ITER, fixed crash when UVs don't exist for object, fixed applying scale and axis remap 2019-06-28 14:45:27 +01:00
ad93a383f7 Merge remote-tracking branch 'origin/master' into soc-2019-fast-io 2019-06-27 10:45:51 +01:00
2a1127c199 [Fast import/export] Fixed compilation errors 2019-06-26 13:34:14 +01:00
10da053977 [Fast import/export] Added unit scaling 2019-06-24 11:03:06 +01:00
32b4a7aa87 [Fast import/export] Refactoring to separate settings specific to each exporter 2019-06-24 10:40:43 +01:00
72712e9985 [Fast import/export] Added NURBS export. Previous commit added path mode 2019-06-23 23:20:41 +01:00
eaacdda4f1 [Fast import/export] Bug fixing, added and fixed UI 2019-06-23 15:43:44 +01:00
1cd052f469 [Fast import/export] OBJ exports materials and animations 2019-06-23 14:57:44 +01:00
d08fbfacb2 [Fast import/export] Removed the boost::iterator_facade dependency for iterators 2019-06-19 17:19:36 +01:00
b9e10ba1b6 [Fast import/export] Changed to use C style output for OBJ 2019-06-17 18:37:26 +01:00
3a5273458c [Fast import/export] Refactored iterators and added code to test copying data 2019-06-14 13:42:31 +01:00
661c98ca14 Merge remote-tracking branch 'origin/master' into soc-2019-fast-io 2019-06-12 15:04:52 +01:00
0c02c06306 [Fast import/export] Ran clang-format 2019-06-11 16:17:19 +01:00
0822ec6fbf [Fast import/export] Really fixed build issues on windows 2019-06-09 10:50:13 +01:00
ad89f5a6db [Fast import/export] Fixed build issues on windows 2019-06-09 10:27:29 +01:00
23eff48c9a [Fast import/export] Added binary STL 2019-06-08 17:21:56 +01:00
59d301befc [Fast import/export] Fixed STL export UI 2019-06-08 09:49:02 +01:00
b8a84a0402 [Fast import/export] Refactoring, fixed ASCII STL export and other bugs 2019-06-06 21:39:15 +01:00
acf50b21f2 [Fast import/export] Review and documentation, and bug fixes 2019-06-06 14:25:09 +01:00
c25527edfb [Fast import/export] Fixed normal iterator and some other small bugs 2019-06-06 14:24:23 +01:00
d2e83dc4b2 [Fast import/export] Fixed UV and deduplicated iterator, and other bug fixes 2019-06-05 17:37:07 +01:00
6e3e0f8c7f [Fast import/export] Fixed loose_edge_iterator 2019-06-03 16:44:59 +01:00
33d50425ea [Fast import/export] Fixed iterator bugs 2019-06-03 16:09:55 +01:00
2eb8ba7005 [Fast import/export] Initial iterator implementation and STL export 2019-06-03 09:51:58 +01:00
1ac9a06a58 [Fast import/export] Added option to disable deduplicating UVs and normals and small refactoring 2019-05-23 17:42:06 +01:00
0757c7fa08 [Fast import/export] Fixed compile error
Including BKE_mesh.h before BLI_sys_types.h creates a compile error, where uint isn't defined.
This is strange and I'm not sure where it comes from, but I think it's a consequence of some change in master.
2019-05-22 12:22:03 +01:00
39cddacb85 [Fast import/export] Bug fixing and adding support for orientation changes 2019-05-22 12:22:03 +01:00
8cf1e0c865 [Fast import/export] Initial support for materials and STL 2019-05-22 12:22:03 +01:00
Hugo Sales
2ca1634636 [Fast import/export] Refactoring to move common code to common.hpp 2019-05-22 12:22:03 +01:00
566eafd50b [Fast import/export] Working MVP for obj exporter 2019-05-22 12:22:03 +01:00
688444698b [Fast import/export] Expanded the common library to include support for applying modifiers and triangulating
Includes some minor refactoring
2019-05-22 12:22:03 +01:00
7258cc7943 [Fast import/export] Initial skeleton 2019-05-22 12:21:38 +01:00
25 changed files with 3552 additions and 5 deletions

8
.gitignore vendored
View File

@@ -26,6 +26,7 @@ Desktop.ini
# commonly used paths in blender
/blender.bin
/BUILD_NOTES.txt
build_files/build_environment/BUILD_NOTES.txt
# local patches
/*.patch
@@ -40,3 +41,10 @@ Desktop.ini
# in-source lib downloads
/build_files/build_environment/downloads
# in-source GTAGS databases
GPATH
GRTAGS
GTAGS
CMakeFiles/

View File

@@ -0,0 +1,136 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# 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.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import os
import bpy
from mathutils import Matrix, Vector, Color
from bpy_extras import io_utils, node_shader_utils
def write_mtl(filepath, materials, path_mode):
C = bpy.context
scene = C.scene
world = scene.world
world_amb = Color((0.8, 0.8, 0.8))
source_dir = os.path.dirname(bpy.data.filepath)
dest_dir = os.path.dirname(filepath)
# Set of images topotentially copy
copy_set = set()
with open(filepath, "w", encoding="utf8", newline="\n") as f:
fw = f.write
fw('# Blender MTL File: %r\n' % (os.path.basename(bpy.data.filepath) or "None"))
fw('# Material Count: %i\n' % len(materials))
for material in materials:
fw('newmtl %s\n' % material)
mat = bpy.data.materials[material]
mat_wrap = node_shader_utils.PrincipledBSDFWrapper(mat) if mat else None
if mat_wrap:
use_mirror = mat_wrap.metallic != 0.0
use_transparency = mat_wrap.alpha != 1.0
# XXX Totally empirical conversion, trying to adapt it
# (from 1.0 - 0.0 Principled BSDF range to 0.0 - 900.0 OBJ specular exponent range)...
spec = (1.0 - mat_wrap.roughness) * 30
spec *= spec
fw('Ns %.6f\n' % spec)
# Ambient
if use_mirror:
fw('Ka %.6f %.6f %.6f\n' % (mat_wrap.metallic, mat_wrap.metallic, mat_wrap.metallic))
else:
fw('Ka %.6f %.6f %.6f\n' % (1.0, 1.0, 1.0))
fw('Kd %.6f %.6f %.6f\n' % mat_wrap.base_color[:3]) # Diffuse
# XXX TODO Find a way to handle tint and diffuse color, in a consistent way with import...
fw('Ks %.6f %.6f %.6f\n' % (mat_wrap.specular, mat_wrap.specular, mat_wrap.specular)) # Specular
# Emission, not in original MTL standard but seems pretty common, see T45766.
fw('Ke 0.0 0.0 0.0\n') # XXX Not supported by current Principled-based shader.
fw('Ni %.6f\n' % mat_wrap.ior) # Refraction index
fw('d %.6f\n' % mat_wrap.alpha) # Alpha (obj uses 'd' for dissolve)
# See http://en.wikipedia.org/wiki/Wavefront_.obj_file for whole list of values...
# Note that mapping is rather fuzzy sometimes, trying to do our best here.
if mat_wrap.specular == 0:
fw('illum 1\n') # no specular.
elif use_mirror:
if use_transparency:
fw('illum 6\n') # Reflection, Transparency, Ray trace
else:
fw('illum 3\n') # Reflection and Ray trace
elif use_transparency:
fw('illum 9\n') # 'Glass' transparency and no Ray trace reflection... fuzzy matching, but...
else:
fw('illum 2\n') # light normally
#### And now, the image textures...
image_map = {
"map_Kd": "base_color_texture",
"map_Ka": None, # ambient...
"map_Ks": "specular_texture",
"map_Ns": "roughness_texture",
"map_d": "alpha_texture",
"map_Tr": None, # transmission roughness?
"map_Bump": "normalmap_texture",
"disp": None, # displacement...
"refl": "metallic_texture",
"map_Ke": None # emission...
}
for key, mat_wrap_key in sorted(image_map.items()):
if mat_wrap_key is None:
continue
tex_wrap = getattr(mat_wrap, mat_wrap_key, None)
if tex_wrap is None:
continue
image = tex_wrap.image
if image is None:
continue
filepath = io_utils.path_reference(image.filepath, source_dir, dest_dir,
path_mode, "", copy_set, image.library)
options = []
if key == "map_Bump":
if mat_wrap.normalmap_strength != 1.0:
options.append('-bm %.6f' % mat_wrap.normalmap_strength)
if tex_wrap.translation != Vector((0.0, 0.0, 0.0)):
options.append('-o %.6f %.6f %.6f' % tex_wrap.translation[:])
if tex_wrap.scale != Vector((1.0, 1.0, 1.0)):
options.append('-s %.6f %.6f %.6f' % tex_wrap.scale[:])
if options:
fw('%s %s %s\n' % (key, " ".join(options), repr(filepath)[1:-1]))
else:
fw('%s %s\n' % (key, repr(filepath)[1:-1]))
else:
# Write a dummy material here?
fw('Ns 500\n')
fw('Ka 0.8 0.8 0.8\n') # Ambient
fw('Kd 0.8 0.8 0.8\n') # Diffuse
fw('Ks 0.8 0.8 0.8\n') # Specular
fw('d 1\n') # No alpha
fw('illum 2\n') # light normally
# After closing file
io_utils.path_reference_copy(copy_set)

View File

@@ -395,6 +395,9 @@ class TOPBAR_MT_file_import(Menu):
if bpy.app.build_options.alembic:
self.layout.operator("wm.alembic_import", text="Alembic (.abc)")
self.layout.operator("wm.obj_import_c", text="Wavefront (.obj)")
self.layout.operator("wm.stl_import_c", text="STL (.stl)")
class TOPBAR_MT_file_export(Menu):
bl_idname = "TOPBAR_MT_file_export"
@@ -406,6 +409,8 @@ class TOPBAR_MT_file_export(Menu):
if bpy.app.build_options.alembic:
self.layout.operator("wm.alembic_export", text="Alembic (.abc)")
self.layout.operator("wm.obj_export_c", text="Wavefront (.obj)")
self.layout.operator("wm.stl_export_c", text="STL (.stl)")
class TOPBAR_MT_file_external_data(Menu):
bl_label = "External Data"

View File

@@ -26,6 +26,8 @@
#ifndef __DEG_DEPSGRAPH_QUERY_H__
#define __DEG_DEPSGRAPH_QUERY_H__
#include "BLI_iterator.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"

View File

@@ -28,10 +28,11 @@ set(INC
../../makesrna
../../windowmanager
../../../../intern/guardedalloc
intern
)
set(INC_SYS
${BOOST_INCLUDE_DIR}
)
set(SRC
@@ -39,11 +40,26 @@ set(SRC
io_cache.c
io_collada.c
io_ops.c
io_common.c
io_obj.c
io_stl.c
io_alembic.h
io_cache.h
io_collada.h
io_ops.h
io_common.h
io_obj.h
io_stl.h
intern/obj_export.cpp
intern/obj_import.cpp
intern/obj.h
intern/stl.cpp
intern/stl.h
intern/common.cpp
intern/common.hpp
intern/iterators.hpp
)
set(LIB
@@ -73,4 +89,13 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
if(WITH_PYTHON)
list(APPEND INC ../../python)
add_definitions(-DWITH_PYTHON)
endif()
if(CMAKE_COMPILER_IS_GNUCC)
add_definitions(-Wfatal-errors) # Temporary, for development
endif()
blender_add_lib(bf_editor_io "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")

View File

@@ -0,0 +1,359 @@
extern "C" {
#include "BLI_listbase.h"
#include "BLI_math_matrix.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_layer_types.h"
#include "DNA_object_types.h"
#include "DNA_modifier_types.h"
#include "BKE_modifier.h"
#include "BKE_modifier.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "RNA_access.h"
#include "bmesh.h"
#include "bmesh_tools.h"
#include "../io_common.h"
#include <stdio.h>
}
#include <iostream>
#include <chrono>
#include "common.hpp"
#include "iterators.hpp"
// Anonymous namespace for internal functions
namespace {
inline bool axis_enum_to_index(int &axis)
{
switch (axis) {
case AXIS_X: // Fallthrough
case AXIS_Y: // Fallthrough
case AXIS_Z:
return false; // Just swap
case AXIS_NEG_X: // Fallthrough
case AXIS_NEG_Y: // Fallthrough
case AXIS_NEG_Z:
axis -= AXIS_NEG_X; // Transform the enum into an index
return true; // Swap and negate
default:
return false;
}
}
} // namespace
namespace common {
void name_compat(std::string &ob_name, const std::string &mesh_name)
{
if (ob_name.compare(mesh_name) != 0) {
ob_name += "_" + mesh_name;
}
size_t start_pos;
while ((start_pos = ob_name.find(" ")) != std::string::npos)
ob_name.replace(start_pos, 1, "_");
}
bool object_is_smoke_sim(const Object *const ob)
{
ModifierData *md = modifiers_findByType((Object *)ob, eModifierType_Smoke);
if (md) {
SmokeModifierData *smd = (SmokeModifierData *)md;
return (smd->type == MOD_SMOKE_TYPE_DOMAIN);
}
return false;
}
bool object_type_is_exportable(const Object *const ob)
{
switch (ob->type) {
case OB_MESH:
return !object_is_smoke_sim(ob);
case OB_CURVE:
case OB_SURF:
return true;
case OB_LAMP:
case OB_EMPTY:
case OB_CAMERA:
return false;
default:
// TODO someone Print in debug only
fprintf(stderr, "Export for this object type is not yet defined %s\n", ob->id.name);
return false;
}
}
// Whether the object should be exported
bool should_export_object(const ExportSettings *const settings, const Object *const ob)
{
if (!object_type_is_exportable(ob))
return false;
// If the object is a dupli, it's export satus depends on the parent
if (!(ob->flag & BASE_FROM_DUPLI)) {
/* These tests only make sense when the object isn't being instanced
* into the scene. When it is, its exportability is determined by
* its dupli-object and the DupliObject::no_draw property. */
return (settings->selected_only ? ((ob->base_flag & BASE_SELECTED) != 0) : true) &&
(settings->visible_only ? ((ob->base_flag & BASE_VISIBLE) != 0) : true) &&
(settings->renderable_only ? ((ob->base_flag & BASE_ENABLED_RENDER) != 0) : true);
}
else if (!(ob->parent != NULL && ob->parent->transflag & OB_DUPLI))
return should_export_object(settings, ob->parent);
else {
BLI_assert(!"should_export_object");
return false;
}
}
/**
* Fills in mat with the matrix which converts the Y axis to `forward` and the Z axis to `up`,
* scaled by `scale`, and where the remaining axis is the cross product of `up` and `forward`
*/
bool get_axis_remap_and_scale_matrix(float (&mat)[4][4], int forward, int up, float scale)
{
bool negate[4];
bool temp = axis_enum_to_index(up); // Modifies up
negate[up] = temp;
temp = axis_enum_to_index(forward);
negate[forward] = temp;
int other_axis = (3 - forward - up);
negate[other_axis] = false;
negate[3] = false;
// Can't have two axis be the same
if (forward == up) {
return false;
}
int axis_from = 0;
for (int axis_to : {other_axis, forward, up, 3}) {
for (int i = 0; i < 4; ++i) {
mat[axis_from][i] = 0;
}
mat[axis_from][axis_to] = (negate[axis_to] ? -1 : 1) * scale;
++axis_from;
}
// A bit hacky, but it's a pointer anyway, it just modifies the first three elements
mat[other_axis][3] = 0;
cross_v3_v3v3(mat[other_axis], mat[up], mat[forward]);
return true;
}
float get_unit_scale(const Scene *const scene)
{
// From collada_internal.cpp
PointerRNA scene_ptr, unit_settings;
PropertyRNA *system_ptr, *scale_ptr;
RNA_id_pointer_create((ID *)&scene->id, &scene_ptr);
unit_settings = RNA_pointer_get(&scene_ptr, "unit_settings");
system_ptr = RNA_struct_find_property(&unit_settings, "system");
scale_ptr = RNA_struct_find_property(&unit_settings, "scale_length");
int type = RNA_property_enum_get(&unit_settings, system_ptr);
float scale = 1.0;
switch (type) {
case USER_UNIT_NONE:
scale = 1.0; // map 1 Blender unit to 1 Meter
break;
case USER_UNIT_METRIC:
scale = RNA_property_float_get(&unit_settings, scale_ptr);
break;
case USER_UNIT_IMPERIAL:
scale = RNA_property_float_get(&unit_settings, scale_ptr);
// it looks like the conversion to Imperial is done implicitly.
// So nothing to do here.
break;
default:
BLI_assert("New unit system added but not handled");
}
return scale;
}
bool get_final_mesh(const ExportSettings *const settings,
const Scene *const escene,
const Object *eob,
Mesh **mesh /* out */,
float (*mat)[4][4] /* out */)
{
bool r = get_axis_remap_and_scale_matrix(*mat,
settings->axis_forward,
settings->axis_up,
settings->global_scale * get_unit_scale(escene));
// Failed, up == forward
if (!r) {
// Ignore remapping
scale_m4_fl(*mat, settings->global_scale * get_unit_scale(escene));
}
mul_m4_m4m4(*mat, *mat, eob->obmat);
*mesh = eob->runtime.mesh_eval;
if (settings->triangulate) {
struct BMeshCreateParams bmcp = {false};
struct BMeshFromMeshParams bmfmp = {true, false, false, 0};
BMesh *bm = BKE_mesh_to_bmesh_ex(*mesh, &bmcp, &bmfmp);
BM_mesh_triangulate(bm,
settings->quad_method,
settings->ngon_method,
4,
false /* tag_only */,
NULL,
NULL,
NULL);
Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, 0);
BM_mesh_free(bm);
*mesh = result;
/* Needs to free? */
return true;
}
/* Needs to free? */
return false;
}
void free_mesh(Mesh *mesh, bool needs_free)
{
if (needs_free)
BKE_id_free(NULL, mesh); // TODO someoene null? (alembic)
}
std::string get_version_string()
{
return ""; // TODO someone implement
}
void export_start(bContext *UNUSED(C), ExportSettings *const settings)
{
/* From alembic_capi.cc
* XXX annoying hack: needed to prevent data corruption when changing
* scene frame in separate threads
*/
G.is_rendering = true; // TODO someone Should use BKE_main_lock(bmain);?
BKE_spacedata_draw_locks(true);
// If render_modifiers use render depsgraph, to get render modifiers
settings->depsgraph = DEG_graph_new(settings->scene,
settings->view_layer,
settings->render_modifiers ? DAG_EVAL_RENDER :
DAG_EVAL_VIEWPORT);
DEG_graph_build_from_view_layer(
settings->depsgraph, settings->main, settings->scene, settings->view_layer);
BKE_scene_graph_update_tagged(settings->depsgraph, settings->main);
}
bool export_end(bContext *UNUSED(C), ExportSettings *const settings)
{
G.is_rendering = false;
BKE_spacedata_draw_locks(false);
DEG_graph_free(settings->depsgraph);
if (settings->format_specific)
MEM_freeN(settings->format_specific);
MEM_freeN(settings);
return true;
}
void import_start(bContext *UNUSED(C), ImportSettings *const settings)
{
G.is_rendering = true;
BKE_spacedata_draw_locks(true);
}
bool import_end(bContext *UNUSED(C), ImportSettings *const settings)
{
G.is_rendering = false;
BKE_spacedata_draw_locks(false);
DEG_graph_free(settings->depsgraph);
if (settings->format_specific)
MEM_freeN(settings->format_specific);
MEM_freeN(settings);
return true;
}
const float3 calculate_normal(const Mesh *const mesh, const MPoly &mp)
{
float no[3];
BKE_mesh_calc_poly_normal(&mp, mesh->mloop + mp.loopstart, mesh->mvert, no);
return float3{no[0], no[1], no[2]};
}
std::vector<float3> get_normals(const Mesh *const mesh)
{
std::vector<float3> normals{};
normals.reserve(mesh->totvert);
const float(*loop_no)[3] = static_cast<float(*)[3]>(
CustomData_get_layer(&mesh->ldata, CD_NORMAL));
unsigned loop_index = 0;
MVert *verts = mesh->mvert;
MPoly *mp = mesh->mpoly;
MLoop *mloop = mesh->mloop;
MLoop *ml = mloop;
// TODO someone Should -0 be converted to 0?
if (loop_no) {
for (int i = 0, e = mesh->totpoly; i < e; ++i, ++mp) {
ml = mesh->mloop + mp->loopstart + (mp->totloop - 1);
for (int j = 0; j < mp->totloop; --ml, ++j, ++loop_index) {
const float(&no)[3] = loop_no[ml->v];
normals.push_back(float3{no[0], no[1], no[2]});
}
}
}
else {
float no[3];
for (int i = 0, e = mesh->totpoly; i < e; ++i, ++mp) {
ml = mloop + mp->loopstart + (mp->totloop - 1);
/* Flat shaded, use common normal for all verts. */
if ((mp->flag & ME_SMOOTH) == 0) {
BKE_mesh_calc_poly_normal(mp, ml - (mp->totloop - 1), verts, no);
normals.push_back(float3{no[0], no[1], no[2]});
ml -= mp->totloop;
loop_index += mp->totloop;
}
else {
/* Smooth shaded, use individual vert normals. */
for (int j = 0; j < mp->totloop; --ml, ++j, ++loop_index) {
normal_short_to_float_v3(no, verts[ml->v].no);
normals.push_back(float3{no[0], no[1], no[2]});
}
}
}
}
normals.shrink_to_fit();
return normals;
}
std::vector<float2> get_uv(const Mesh *const mesh)
{
std::vector<float2> uvs{};
uvs.reserve(mesh->totloop);
for (int i = 0, e = mesh->totloop; i < e; ++i) {
const float(&uv)[2] = mesh->mloopuv[i].uv;
uvs.push_back(float2{uv[0], uv[1]});
}
return uvs;
}
std::vector<float3> get_vertices(const Mesh *const mesh)
{
std::vector<float3> vxs{};
vxs.reserve(mesh->totvert);
for (int i = 0, e = mesh->totvert; i < e; ++i) {
const MVert &v = mesh->mvert[i];
vxs.push_back(float3{v.co[0], v.co[1], v.co[2]});
}
return vxs;
}
} // namespace common

View File

@@ -0,0 +1,161 @@
#ifndef __io_intern_common__
# define __io_intern_common__
/* #include "BLI_listbase.h" */
/* #include "DNA_mesh_types.h" */
/* #include "DNA_object_types.h" */
extern "C" {
// Clang-format is adding these spaces, but it's not allowed by the standard
# include "BKE_global.h"
# include "BKE_mesh.h"
# include "BKE_mesh_runtime.h"
# include "BKE_modifier.h"
# include "BKE_library.h"
# include "BKE_customdata.h"
# include "BKE_scene.h"
# include "MEM_guardedalloc.h"
/* SpaceType struct has a member called 'new' which obviously conflicts with C++
* so temporarily redefining the new keyword to make it compile. */
# define new extern_new
# include "BKE_screen.h"
# undef new
# include "BLI_listbase.h"
# include "BLI_math_matrix.h"
# include "BLI_math_vector.h"
# include "BLI_utildefines.h"
# include "bmesh.h"
# include "bmesh_tools.h"
# include "DNA_layer_types.h"
# include "DNA_meshdata_types.h"
# include "DNA_mesh_types.h"
# include "DNA_modifier_types.h"
# include "DNA_object_types.h"
# include "DEG_depsgraph_build.h"
# include "DEG_depsgraph_query.h"
# include "../io_common.h"
}
# include <utility>
# include <string>
# include <vector>
# include <set>
# include <array>
# include <typeinfo>
# include <iterator>
namespace common {
using float3 = std::array<float, 3>;
using float2 = std::array<float, 2>;
// --- PROTOTYPES ---
bool object_is_smoke_sim(const Object *const ob);
bool object_type_is_exportable(const Object *const ob);
bool should_export_object(const ExportSettings *const settings, const Object *const ob);
bool get_axis_remap_and_scale_matrix(float (&mat)[4][4], int forward, int up, float scale);
bool get_final_mesh(const ExportSettings *const settings,
const Scene *const escene,
const Object *eob,
Mesh **mesh /* out */,
float (*mat)[4][4] /* out */);
void free_mesh(Mesh *mesh, bool needs_free);
void name_compat(std::string &ob_name, const std::string &mesh_name);
std::string get_version_string();
float get_unit_scale(const Scene *const scene);
void export_start(bContext *C, ExportSettings *const settings);
bool export_end(bContext *C, ExportSettings *const settings);
void import_start(bContext *C, ImportSettings *const settings);
bool import_end(bContext *C, ImportSettings *const settings);
// Execute `start` and `end` and time it. Those functions should be
// specific to each exportter, but have the same signature as the two above
bool time_export(bContext *C,
ExportSettings *const settings,
void (*start)(bContext *C, ExportSettings *const settings),
bool (*end)(bContext *C, ExportSettings *const settings));
const float3 calculate_normal(const Mesh *const mesh, const MPoly &mp);
std::vector<float3> get_normals(const Mesh *const mesh);
std::vector<float2> get_uv(const Mesh *const mesh);
std::vector<float3> get_vertices(const Mesh *const mesh);
// --- TEMPLATES ---
// T should be something with an ID, like Mesh or Curve
template<typename T> std::string get_object_name(const Object *const ob, const T *const data)
{
std::string name{ob->id.name + 2};
std::string data_name{data->id.name + 2};
name_compat(name /* modifies */, data_name);
return name;
}
template<typename Start, typename End, typename Settings>
bool time(bContext *C, Settings *const settings, Start start, End end)
{
auto f = std::chrono::steady_clock::now();
start(C, settings);
auto ret = end(C, settings);
std::cout << "Took "
<< std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - f)
.count()
<< "ms\n";
return ret;
}
// --- Deduplication ---
// TODO someone How to properly compare similarity of vectors?
struct threshold_comparator {
threshold_comparator(const float th) : th(th)
{
}
template<typename key_t> bool operator()(const key_t &lhs, const key_t &rhs) const
{
return // too_similar(lhs.first, rhs.first, th) &&
lhs.first < rhs.first;
}
const float th;
};
// The set used to deduplicate UVs and normals
template<typename key_t> using set_t = std::set<key_t, threshold_comparator>;
// Provides the mapping from the deduplicated to the original index
template<typename key_t> using set_mapping_t = std::vector<typename set_t<key_t>::iterator>;
// A pair of the two above, to tie them together
template<typename key_t> using dedup_pair_t = std::pair<set_t<key_t>, set_mapping_t<key_t>>;
// The set key for UV and normal. size_t is the original index
using uv_key_t = std::pair<float2, size_t>;
using no_key_t = std::pair<float3, size_t>;
// Construct a new deduplicated set for either normals or UVs, with the given similarity threshold
// C++14/17 would be useful here...
template<typename key_t> dedup_pair_t<key_t> make_deduplicate_set(const float threshold)
{
return {set_t<key_t>{threshold_comparator(threshold)} /* set */, {} /* set_mapping */};
}
} // namespace common
#endif // __io_intern_common_

View File

@@ -0,0 +1,686 @@
#ifndef __io_intern_iterators_h__
#define __io_intern_iterators_h__
extern "C" {
#include "BKE_customdata.h"
#include "BKE_global.h"
#include "BKE_library.h"
#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_modifier.h"
#include "BKE_scene.h"
#include "DNA_curve_types.h"
#include "DNA_layer_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "../io_common.h"
}
#include "common.hpp"
#include <boost/iterator/iterator_facade.hpp>
#include <boost/iterator/iterator_adaptor.hpp>
namespace common {
// Adapt a pointer-size pair as a random access iterator
// This makes use of `boost::iterator_facade` and makes it possible to use
// for each style loops, as well as cleanly hiding how the underlying Blender
// data structures are accessed
template<typename T, typename Tag = std::random_access_iterator_tag> struct pointer_iterator_base {
using difference_type = ptrdiff_t;
using value_type = T;
using pointer = T *;
using reference = T &;
using iterator_category = Tag;
pointer_iterator_base(pointer p, size_t size) : first(p), curr(p), size(size)
{
}
pointer_iterator_base(pointer p, size_t size, pointer first) : first(first), curr(p), size(size)
{
}
pointer_iterator_base(const pointer_iterator_base &pib)
: first(pib.first), curr(pib.curr), size(pib.size)
{
}
// Conversion to base pointer
operator pointer() const
{
return curr;
}
pointer_iterator_base &operator=(const pointer_iterator_base &p)
{
// Placement new: construct a new object in the position of `this`
// Doesn't actually allocate memory
new (this) pointer_iterator_base(p);
return *this;
}
pointer_iterator_base begin() const
{
return {this->first, this->size, this->first};
}
pointer_iterator_base end() const
{
return {this->first + this->size, this->size, this->first};
}
pointer_iterator_base &operator++()
{
++curr;
return *this;
}
pointer_iterator_base &operator--()
{
--curr;
return *this;
}
pointer_iterator_base &operator+(difference_type n)
{
curr += n;
return *this;
}
difference_type operator-(const pointer_iterator_base &other) const
{
return other.curr - curr;
}
bool operator==(const pointer_iterator_base &other) const
{
return curr == other.curr;
}
pointer first;
pointer curr;
size_t size;
};
template<typename T>
struct pointer_iterator : pointer_iterator_base<T, std::random_access_iterator_tag> {
using pointer_iterator_base<T, std::random_access_iterator_tag>::pointer_iterator_base;
pointer_iterator(const pointer_iterator_base<T, std::random_access_iterator_tag> &pi)
: pointer_iterator_base<T, std::random_access_iterator_tag>(pi)
{
}
inline const T &operator*() const
{
return *this->curr;
}
};
// An iterator that iterates over doubly linked lists
template<typename SourceT,
typename ResT = SourceT &,
typename Tag = std::bidirectional_iterator_tag>
struct list_iterator : pointer_iterator_base<SourceT, Tag> {
list_iterator(SourceT *p) : pointer_iterator_base<SourceT, Tag>(p, 0)
{
}
list_iterator begin() const
{
return {this->first};
}
list_iterator end() const
{
return {nullptr};
}
list_iterator &operator++()
{
this->curr = this->curr->next;
return *this;
}
list_iterator &operator--()
{
this->curr = this->curr->prev;
return *this;
}
inline const ResT operator*() const
{
return *this->curr;
}
};
// Represents an offset into an array (base for iterators like material_iter)
template<typename T>
struct offset_iterator : pointer_iterator_base<T, std::random_access_iterator_tag> {
offset_iterator(size_t size) : pointer_iterator_base<T, std::random_access_iterator_tag>(0, size)
{
}
size_t offset() const
{
return ((size_t)this->curr) / sizeof(T);
}
};
// Iterator over the polygons of a mesh
struct poly_iter : pointer_iterator<MPoly> {
poly_iter(const Mesh *const m) : pointer_iterator(m->mpoly, m->totpoly)
{
}
poly_iter(const pointer_iterator_base<MPoly> &pi) : pointer_iterator(pi)
{
}
poly_iter begin() const
{
return {{this->first, this->size, this->first}};
}
poly_iter end() const
{
return {{this->first + this->size, this->size, this->first}};
}
};
// Iterator over the vertices of a mesh
struct vert_iter : pointer_iterator<MVert> {
vert_iter(const Mesh *const m) : pointer_iterator(m->mvert, m->totvert)
{
}
};
struct transformed_vertex_iter : pointer_iterator_base<MVert> {
using Mat = const float (*)[4]; // Must actually be float[4][4]
transformed_vertex_iter(const Mesh *const m, Mat mat)
: pointer_iterator_base(m->mvert, m->totvert), mat(mat)
{
}
transformed_vertex_iter(const pointer_iterator_base<MVert> &pi, Mat mat)
: pointer_iterator_base(pi), mat(mat)
{
}
transformed_vertex_iter begin() const
{
return {{this->first, this->size, this->first}, mat};
}
transformed_vertex_iter end() const
{
return {{this->first + this->size, this->size, this->first}, mat};
}
const std::array<float, 3> operator*() const
{
float co[3];
mul_v3_m4v3(co, mat, this->curr->co);
return {co[0], co[1], co[2]};
}
Mat mat;
};
// Iterator over the vertices of a polygon
struct vert_of_poly_iter : pointer_iterator_base<MLoop, std::random_access_iterator_tag> {
// TODO someone What order are the vertices stored in? Clockwise?
vert_of_poly_iter(const Mesh *const mesh, const MPoly &mp)
: pointer_iterator_base(mesh->mloop + mp.loopstart, mp.totloop), mvert(mesh->mvert)
{
}
vert_of_poly_iter(const MVert *const mvert,
const pointer_iterator_base<MLoop, std::random_access_iterator_tag> &pi)
: pointer_iterator_base(pi), mvert(mvert)
{
}
vert_of_poly_iter begin() const
{
return {mvert, {this->first, this->size, this->first}};
}
vert_of_poly_iter end() const
{
return {mvert, {this->first + this->size, this->size, this->first}};
}
const MVert &operator*() const
{
return mvert[this->curr->v];
}
const MVert *const mvert;
};
// Iterator over all the edges of a mesh
struct edge_iter : pointer_iterator<MEdge> {
edge_iter(const Mesh *const m) : pointer_iterator(m->medge, m->totedge)
{
}
edge_iter(const pointer_iterator<MEdge> &pi) : pointer_iterator(pi)
{
}
};
// Iterator over the edges of a mesh which are marked as loose
struct loose_edge_iter : edge_iter {
loose_edge_iter(const Mesh *const m) : edge_iter(m)
{
}
loose_edge_iter(const pointer_iterator<MEdge> &pi) : edge_iter(pi)
{
}
loose_edge_iter begin() const
{
return {{this->first, this->size, this->first}};
}
loose_edge_iter end() const
{
return {{this->first + this->size, this->size, this->first}};
}
loose_edge_iter &operator++()
{
do {
++this->curr;
} while (!(this->curr->flag & ME_LOOSEEDGE));
return *this;
}
loose_edge_iter &operator--()
{
do {
--this->curr;
} while (!(this->curr->flag & ME_LOOSEEDGE));
return *this;
}
};
// Iterator over all the objects in a `ViewLayer`
// TODO someone G.is_break
struct object_iter : list_iterator<Base, Object *> {
object_iter(Base *const b) : list_iterator(b)
{
}
object_iter(const ViewLayer *const vl) : list_iterator((Base *)vl->object_bases.first)
{
}
const Object *operator*()
{
return this->curr->object;
}
};
struct exportable_object_iter : object_iter {
exportable_object_iter(const ViewLayer *const vl, const ExportSettings *const settings)
: object_iter(vl), settings(settings)
{
}
exportable_object_iter(Base *base, const ExportSettings *const settings)
: object_iter(base), settings(settings)
{
}
exportable_object_iter begin() const
{
return {this->first, settings};
}
exportable_object_iter end() const
{
return {(Base *)nullptr, settings};
}
exportable_object_iter &operator++()
{
do {
this->curr = this->curr->next;
} while (this->curr != nullptr && !common::should_export_object(settings, this->curr->object));
return *this;
}
const ExportSettings *const settings;
};
// Iterator over the modifiers of an `Object`
struct modifier_iter : list_iterator<ModifierData> {
modifier_iter(const Object *const ob) : list_iterator((ModifierData *)ob->modifiers.first)
{
}
};
// Iterator over the `MLoop` of a `MPoly` of a mesh
struct loop_of_poly_iter : pointer_iterator<MLoop> {
loop_of_poly_iter(const Mesh *const mesh, const poly_iter &poly)
: pointer_iterator(mesh->mloop + (*poly).loopstart, (*poly).totloop)
{
}
loop_of_poly_iter(const Mesh *const mesh, const MPoly &poly)
: pointer_iterator(mesh->mloop + poly.loopstart, poly.totloop)
{
}
loop_of_poly_iter(const pointer_iterator_base<MLoop> &pi) : pointer_iterator(pi)
{
}
loop_of_poly_iter begin() const
{
return loop_of_poly_iter{{this->first, this->size, this->first}};
}
loop_of_poly_iter end() const
{
return loop_of_poly_iter{{this->first + this->size, this->size, this->first}};
}
};
struct material_iter : offset_iterator<Material *> {
material_iter(const Object *const ob)
: offset_iterator(ob->totcol), ob(ob), mdata(*give_matarar((Object *)ob))
{
}
material_iter begin() const
{
return material_iter(ob);
}
material_iter end() const
{
material_iter mi(ob);
mi.curr = mi.first + mi.size;
return mi;
}
const Material *operator*()
{
const size_t off = offset();
if (ob->matbits && ob->matbits[off]) {
// In Object
return ob->mat[off];
}
else {
// In Data
return mdata[off];
}
}
const Object *const ob;
const Material *const *const mdata;
};
struct nurbs_of_curve_iter : list_iterator<Nurb> {
nurbs_of_curve_iter(const Curve *const curve) : list_iterator((Nurb *)curve->nurb.first)
{
}
};
struct points_of_nurbs_iter : pointer_iterator_base<BPoint> {
points_of_nurbs_iter(const Nurb *const nu)
: pointer_iterator_base(nu->bp, (nu->pntsv > 0 ? nu->pntsu * nu->pntsv : nu->pntsu))
{
}
points_of_nurbs_iter(const pointer_iterator_base<BPoint> &pi) : pointer_iterator_base(pi)
{
}
points_of_nurbs_iter begin() const
{
return {{this->first, this->size, this->first}};
}
points_of_nurbs_iter end() const
{
return {{this->first + this->size, this->size, this->first}};
}
inline const std::array<float, 3> operator*() const
{
return {curr->vec[0], curr->vec[1], curr->vec[2]};
}
};
// Iterator over the UVs of a mesh (as `const std::array<float, 2>`)
struct uv_iter : pointer_iterator_base<MLoopUV> {
uv_iter(const Mesh *const m) : pointer_iterator_base(m->mloopuv, m->mloopuv ? m->totloop : 0)
{
}
uv_iter(const pointer_iterator_base<MLoopUV> &pi) : pointer_iterator_base(pi)
{
}
uv_iter begin() const
{
return {{this->first, this->size, this->first}};
}
uv_iter end() const
{
return {{this->first + this->size, this->size, this->first}};
}
inline const std::array<float, 2> operator*()
{
return {this->curr->uv[0], this->curr->uv[1]};
}
};
// Iterator over the normals of mesh
// This is a more complex one, because there are three possible sources for the normals of a mesh:
// - Custom normals
// - Face normals, when the face is not smooth shaded
// - Vertex normals, when the face is smooth shaded
// This is a completely separate iterator because it needs to override pretty much all behaviours
// It's only a bidirectional iterator, because it is not continuous
struct normal_iter {
using ResT = const std::array<float, 3>;
using difference_type = ptrdiff_t;
using value_type = ResT;
using pointer = ResT *;
using reference = ResT &;
using iterator_category = std::forward_iterator_tag;
normal_iter(const Mesh *const mesh) : mesh(mesh), mp(mesh->mpoly), ml(mesh->mloop)
{
custom_no = static_cast<float(*)[3]>(CustomData_get_layer(&mesh->ldata, CD_NORMAL));
}
normal_iter(const Mesh *const mesh, const MPoly *poly, const MLoop *loop)
: mesh(mesh), mp(poly), ml(loop)
{
}
normal_iter begin() const
{
return {mesh};
}
normal_iter end() const
{
const MPoly *const last_poly = mesh->mpoly + (mesh->totpoly);
return {mesh, last_poly, mesh->mloop + last_poly->loopstart + (last_poly->totloop - 1)};
}
normal_iter &operator++()
{
// If not past the last element of the current loop, go to the next one
if (ml < (mesh->mloop + mp->loopstart + mp->totloop)) {
++ml;
}
// If past the last (eg after the previous increment)
if (ml >= (mesh->mloop + mp->loopstart + mp->totloop)) {
// If not past the last poly
if (mp < (mesh->mpoly + mesh->totpoly)) {
// Increment the poly
++mp;
// Get the loop of the current poly
ml = mesh->mloop + mp->loopstart;
// If now past the last poly
if (mp >= (mesh->mpoly + mesh->totpoly)) {
// Make sure the loop is at it's end, so we compare true with `end()`
ml += mp->totloop - 1;
}
}
}
return *this;
}
bool operator==(const normal_iter &other) const
{
return mp == other.mp && ml == other.ml;
}
bool operator!=(const normal_iter &other) const
{
return !(*this == other);
}
ResT operator*() const
{
// TODO someone Should -0 be converted to 0?
if (custom_no) {
const float(&no)[3] = custom_no[ml->v];
return {no[0], no[1], no[2]};
}
else {
float no[3];
/* Flat shaded, use common normal for all verts. */
if ((mp->flag & ME_SMOOTH) == 0) {
BKE_mesh_calc_poly_normal(mp, mesh->mloop + mp->loopstart, mesh->mvert, no);
return {no[0], no[1], no[2]};
}
else {
/* Smooth shaded, use individual vert normals. */
normal_short_to_float_v3(no, mesh->mvert[ml->v].no);
return {no[0], no[1], no[2]};
}
}
}
const Mesh *const mesh;
const MPoly *mp;
const MLoop *ml;
const float (*custom_no)[3];
};
struct transformed_normal_iter {
using difference_type = ptrdiff_t;
using value_type = std::array<float, 3>;
using pointer = value_type *;
using reference = value_type &;
using iterator_category = std::forward_iterator_tag;
using Mat = const float (*)[4]; // Must actually be float[4][4]
transformed_normal_iter() = delete;
transformed_normal_iter(const Mesh *const mesh, Mat mat) : ni(mesh), mat(mat)
{
}
transformed_normal_iter(const normal_iter &ni, Mat mat) : ni(ni), mat(mat)
{
}
transformed_normal_iter &operator++()
{
++ni;
return *this;
}
transformed_normal_iter begin() const
{
return {ni.begin(), mat};
}
transformed_normal_iter end() const
{
return {ni.end(), mat};
}
bool operator==(const transformed_normal_iter &other) const
{
return ni == other.ni;
}
bool operator!=(const transformed_normal_iter &other) const
{
return ni != other.ni;
}
std::array<float, 3> operator*() const
{
std::array<float, 3> no = *ni;
mul_v3_m4v3(no.data(), mat, no.data());
return no;
}
normal_iter ni;
Mat mat;
};
// --- Deduplication ---
// Iterator which performs a deduplication of the values coming out of the SourceIter
template<typename KeyT,
typename SourceIter,
typename ResT = const typename KeyT::first_type,
typename Tag = std::forward_iterator_tag>
struct deduplicated_iterator {
using difference_type = ptrdiff_t;
using value_type = ResT;
using pointer = ResT *;
using reference = ResT &;
using iterator_category = Tag;
deduplicated_iterator(const Mesh *const mesh,
dedup_pair_t<KeyT> &dp,
size_t &total,
SourceIter it)
: it(it), mesh(mesh), dedup_pair(dp), total(total)
{
}
deduplicated_iterator(
const Mesh *const mesh, dedup_pair_t<KeyT> &dp, size_t &total, size_t reserve, SourceIter it)
: deduplicated_iterator(mesh, dp, total, it)
{
if (this->it != this->it.end()) {
// Reserve space so we don't constantly allocate
dedup_pair.second.reserve(reserve);
auto p = dedup_pair.first.insert(std::make_pair(*this->it, total));
// Need to insert the first element, because we need to dereference before incrementing
// dedup_pair.second.insert(dedup_pair.second.end(), reserve - dedup_pair.second.size(), {});
dedup_pair.second.push_back(p.first);
if (p.second) {
++total;
}
// ++this->it;
}
}
deduplicated_iterator begin() const
{
return deduplicated_iterator(mesh, dedup_pair, total, it.begin());
}
deduplicated_iterator end() const
{
return deduplicated_iterator(mesh, dedup_pair, total, it.end());
}
deduplicated_iterator &operator++()
{
// Handle everything until the next different element, or the end, by...
while (this->it != this->it.end()) {
// going to the next element of the `SourceIter`
++this->it;
// if at the end, we're done
if (this->it == this->it.end())
return *this;
// try to insert it into the set
auto p = dedup_pair.first.insert(std::make_pair(*this->it, total));
// push the set::iterator onto the back of the mapping vector
dedup_pair.second.push_back(p.first);
// If we actually inserted in the set
if (p.second) {
// There's a new element, so increment the total and stop
++this->total;
return *this;
}
}
// Should be unreachable
return *this;
}
bool operator==(const deduplicated_iterator &other)
{
return it == other.it;
}
bool operator!=(const deduplicated_iterator &other)
{
return !(it == other.it);
}
ResT operator*() const
{
return this->dedup_pair.second.back()->first;
}
SourceIter it;
const Mesh *const mesh;
dedup_pair_t<KeyT> &dedup_pair;
size_t &total;
};
// Iterator to deduplicated normals (returns `const std::array<float, 3>`)
struct deduplicated_normal_iter : deduplicated_iterator<no_key_t, transformed_normal_iter> {
deduplicated_normal_iter(const Mesh *const mesh,
size_t &total,
dedup_pair_t<no_key_t> &dp,
const float (*mat)[4])
: deduplicated_iterator<no_key_t, transformed_normal_iter>(
mesh, dp, total, total + mesh->totloop, transformed_normal_iter(mesh, mat))
{
}
// The last element in the mapping vector. Requires we insert the first element
// in the constructor, otherwise this vector would be empty
const std::array<float, 3> operator*() const
{
return this->dedup_pair.second.back()->first;
}
};
// Iterator to deduplicated UVs (returns `const std::array<float, 2>`)
struct deduplicated_uv_iter : deduplicated_iterator<uv_key_t, uv_iter> {
deduplicated_uv_iter(const Mesh *const mesh, size_t &total, dedup_pair_t<uv_key_t> &dp)
: deduplicated_iterator<uv_key_t, uv_iter>(
mesh, dp, total, total + mesh->totloop, uv_iter{mesh})
{
}
// The last element in the mapping vector. Requires we insert the first element
// in the constructor, otherwise this vector would be empty
const std::array<float, 2> operator*() const
{
return this->dedup_pair.second.back()->first;
}
};
} // namespace common
#endif // __io_intern_iterators_h__

View File

@@ -0,0 +1,42 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* 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.
*
* Contributor(s): Hugo Sales
*
* ***** END GPL LICENSE BLOCK *****
*/
/* #ifndef __IO_OBJ_H__ */
/* #define __IO_OBJ_H__ */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include "../io_common.h"
struct bContext;
bool OBJ_export(bContext *C, ExportSettings *const settings);
bool OBJ_import(bContext *C, ImportSettings *const settings);
#ifdef __cplusplus
}
#endif /* __cplusplus */
/* #endif /\* __IO_OBJ_H__ *\/ */

View File

@@ -0,0 +1,518 @@
extern "C" {
#include "BLI_sys_types.h"
#include "BLI_math.h"
#include "BLT_translation.h"
#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_global.h"
#include "BKE_library.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_mesh_runtime.h"
#include "BKE_scene.h"
#include "DNA_curve_types.h"
#include "DNA_ID.h"
#include "DNA_layer_types.h"
#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_mesh_types.h"
#include "DNA_object_types.h"
#include "DNA_space_types.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
#include "WM_api.h"
#include "WM_types.h"
#ifdef WITH_PYTHON
# include "BPY_extern.h"
#endif
#include "ED_object.h"
#include "bmesh.h"
#include "bmesh_tools.h"
#include "obj.h"
#include "../io_common.h"
#include "../io_obj.h"
}
#include <array>
#include <algorithm>
#include <chrono>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <set>
#include <sstream>
#include <unordered_map>
#include <vector>
#include "common.hpp"
#include "iterators.hpp"
/*
TODO someone: () not done, -- done, # maybe add, ? unsure
--selection only
--animation
--apply modifiers
--render modifiers -- mesh_create_derived_{view,render}, deg_get_mode
--edges
--normals
--uvs
--materials
-- path mode
-- triangulate
-- nurbs
--obj objects
--obj groups
--scale
--units
--removing duplicates with a threshold and as an option
presets
axis remap -- doesn't work. Does it need to update, somehow?
DEG_id_tag_update(&mesh->id, 0); obedit->id.recalc & ID_RECALC_ALL
smooth groups
bitflag smooth groups?
material groups
polygroups?
-?vertex order
*/
namespace {
struct Mesh_export {
Object *object;
Mesh *mesh;
float mat[4][4];
bool needs_free;
// Index offsets
size_t vx_offset;
size_t uv_offset;
size_t no_offset;
};
using namespace common;
std::string get_path(const char *const original_path,
const char *const ext,
const bool export_animations = false,
const int frame = 0)
{
std::string path = original_path;
size_t start_pos = path.rfind(".obj");
if (start_pos == std::string::npos) {
std::cerr << "Invalid file path: " << original_path << '\n';
return "";
}
path.erase(path.begin() + start_pos, path.end());
if (export_animations) {
char fr[8];
std::snprintf(fr, 8, "_%06d", frame);
path += fr;
}
path += ext;
return path;
}
std::FILE *get_file(const char *const original_path,
const char *const ext,
const bool export_animations = false,
const size_t frame = 0)
{
std::string path = get_path(original_path, ext, export_animations, frame);
if (path == "")
return nullptr;
std::FILE *file = std::fopen(path.c_str(), "w");
if (file == nullptr)
std::cerr << "Couldn't open file: " << path << '\n';
return file;
}
bool OBJ_export_materials(bContext *C,
ExportSettings *settings,
std::string mtl_path,
std::set<const Material *> materials)
{
#ifdef WITH_PYTHON
const char *imports[] = {"obj_export", NULL};
std::stringstream ss;
ss << "obj_export.write_mtl(\"" << mtl_path << "\", [";
bool first = true;
for (const Material *const mat : materials) {
if (first) {
ss << '"';
first = false;
}
else
ss << ", \"";
ss << (mat->id.name + 2) << '"';
}
ss << "], '"
<< path_reference_mode[((OBJExportSettings *)settings->format_specific)->path_mode].identifier
<< "')";
std::cerr << "DEBUG: Running '" << ss.str() << "'\n";
return BPY_execute_string(C, imports, ss.str().c_str());
#else
return false;
#endif
}
bool OBJ_export_curve(bContext *UNUSED(C),
ExportSettings *UNUSED(settings),
std::FILE *file,
const Object *eob,
const Curve *cu)
{
size_t totvert = 0;
for (const Nurb &nurb : nurbs_of_curve_iter(cu)) {
size_t deg_order_u = 0;
if (nurb.type == CU_POLY) {
deg_order_u = 1;
}
else {
// Original OBJ exporter says this is tested to be correct...
deg_order_u = nurb.orderu - 1;
}
if (nurb.type == CU_BEZIER) {
std::cerr << "Warning: BEZIER curve '" << eob->id.name
<< "' skipped. Only Poly and NURBS curves supported\n";
return true; // Don't abort all exports, just skip
}
if (nurb.pntsv > 1) {
std::cerr << "Warning: Surface curve '" << eob->id.name
<< "' skipped. Only Poly and NURBS curves supported\n";
return true; // Don't abort all exports, just skip
}
// If the length of the nurb.bp array is smaller than the degree, we'remissing points
if ((nurb.pntsv > 0 ? nurb.pntsu * nurb.pntsv : nurb.pntsu) <= deg_order_u) {
std::cerr << "Warning: Number of points in object '" << eob->id.name
<< "' is lower than it's degree. Skipped. \n";
return true; // Don't abort all exports, just skip
}
const bool closed = nurb.flagu & CU_NURB_CYCLIC;
const bool endpoints = !closed && (nurb.flagu & CU_NURB_ENDPOINT);
size_t num_points = 0;
// TODO someone Transform, scale and such
for (const std::array<float, 3> &point : points_of_nurbs_iter(&nurb)) {
fprintf(file, "v %.6g %.6g %.6g\n", point[0], point[1], point[2]);
++num_points;
}
totvert += num_points;
fprintf(file, "g %s\n", common::get_object_name(eob, cu).c_str());
fprintf(file, "cstype bspline\ndeg %zu\n", deg_order_u);
size_t real_num_points = num_points;
if (closed) {
num_points += deg_order_u;
}
fputs("curv 0.0 1.0", file);
for (int i = 0; i < num_points; ++i) {
// Relative indexes, reuse vertices if closed
fprintf(file, " %ld", (long)(i < real_num_points ? -(i + 1) : -(i - real_num_points + 1)));
}
fputc('\n', file);
fputs("parm u", file);
size_t tot_parm = deg_order_u + 1 + num_points;
float tot_norm_div = 1.0f / (tot_parm - 1);
for (int i = 0; i < tot_parm; ++i) {
float parm = i * tot_norm_div;
if (endpoints) {
if (i <= deg_order_u)
parm = 0;
else if (i >= num_points)
parm = 1;
}
fprintf(file, " %g", parm);
}
fputs("\nend\n", file);
}
return true;
}
bool OBJ_export_meshes(bContext *UNUSED(C),
ExportSettings *settings,
const Scene *const UNUSED(escene),
std::FILE *file,
std::vector<Mesh_export> &meshes)
{
if (meshes.size() == 0) {
return true;
}
const OBJExportSettings *format_specific = (OBJExportSettings *)settings->format_specific;
size_t vx_total = 0, uv_total = 0, no_total = 0;
auto uv_mapping_pair = common::make_deduplicate_set<uv_key_t>(
format_specific->dedup_uvs_threshold);
auto no_mapping_pair = common::make_deduplicate_set<no_key_t>(
format_specific->dedup_normals_threshold);
auto &uv_mapping = uv_mapping_pair.second;
auto &no_mapping = no_mapping_pair.second;
for (Mesh_export &me : meshes) {
me.vx_offset = vx_total;
vx_total += me.mesh->totvert;
for (const std::array<float, 3> &v : common::transformed_vertex_iter(me.mesh, me.mat)) {
fprintf(file, "v %.6g %.6g %.6g\n", v[0], v[1], v[2]);
}
}
if (settings->export_uvs) {
for (Mesh_export &me : meshes) {
if (me.mesh->mloopuv != nullptr) {
me.uv_offset = uv_mapping.size();
// TODO someone Is T47010 still relevant?
if (format_specific->dedup_uvs) {
for (const std::array<float, 2> &uv :
common::deduplicated_uv_iter(me.mesh, uv_total, uv_mapping_pair)) {
fprintf(file, "vt %.6g %.6g\n", uv[0], uv[1]);
}
}
else {
uv_total += me.mesh->totloop;
for (const std::array<float, 2> &uv : common::uv_iter{me.mesh}) {
fprintf(file, "vt %.6g %.6g\n", uv[0], uv[1]);
}
}
}
}
}
if (settings->export_normals) {
for (Mesh_export &me : meshes) {
// Remove location, because it shouldn't affect normals
for (size_t i = 0; i < 3; ++i) {
me.mat[3][i] = 0.0;
}
me.mat[3][3] = 1.0f;
normalize_m4(me.mat);
me.no_offset = no_mapping.size();
if (format_specific->dedup_normals) {
for (const std::array<float, 3> &no :
common::deduplicated_normal_iter(me.mesh, no_total, no_mapping_pair, me.mat)) {
fprintf(file, "vn %.4g %.4g %.4g\n", no[0], no[1], no[2]);
}
}
else {
for (const std::array<float, 3> &no : common::transformed_normal_iter(me.mesh, me.mat)) {
fprintf(file, "vn %.4g %.4g %.4g\n", no[0], no[1], no[2]);
}
}
}
}
std::cerr << "Totals: " << uv_total << " " << no_total << "\nSizes: " << uv_mapping.size() << " "
<< no_mapping.size() << '\n';
const char *state_mat = nullptr;
for (const Mesh_export &me : meshes) {
if (format_specific->export_objects_as_objects || format_specific->export_objects_as_groups) {
const std::string name = common::get_object_name(me.object, me.mesh);
if (format_specific->export_objects_as_objects)
fprintf(file, "o %s\n", name.c_str());
else
fprintf(file, "g %s\n", name.c_str());
}
int group_total = 0, *smooth_groups = nullptr;
if (format_specific->export_smooth_groups) {
smooth_groups = BKE_mesh_calc_smoothgroups(me.mesh->medge,
me.mesh->totedge,
me.mesh->mpoly,
me.mesh->totpoly,
me.mesh->mloop,
me.mesh->totloop,
&group_total,
format_specific->smooth_groups_bitflags);
}
size_t poly_index = 0;
int state_smooth = -1;
for (auto pi = common::poly_iter(me.mesh); pi != pi.end(); ++pi, ++poly_index) {
const MPoly &p = *pi;
if (settings->export_materials && me.mesh->mat &&
(state_mat == nullptr ||
std::strncmp(me.mesh->mat[p.mat_nr]->id.name, state_mat, MAX_ID_NAME) != 0)) {
fprintf(file, "usemtl %s\n", me.mesh->mat[p.mat_nr]->id.name + 2);
state_mat = me.mesh->mat[p.mat_nr]->id.name;
}
// Smooth indices start at 1, so 0 is not a valid index
int smooth = (p.flag & ME_SMOOTH) ? 1 : 0;
if (smooth && smooth_groups) {
smooth = smooth_groups[poly_index % group_total];
}
if (smooth != state_smooth) {
state_smooth = smooth;
if (smooth && smooth_groups) {
fprintf(file, "s %d\n", smooth_groups[poly_index]);
}
else if (smooth) {
fputs("s 1\n", file);
}
else {
fputs("s off\n", file);
}
}
fputc('f', file);
// Loop index
int li = p.loopstart;
for (const MLoop &l : common::loop_of_poly_iter(me.mesh, p)) {
size_t vx = me.vx_offset + l.v + 1, uv, no;
if (settings->export_uvs && me.mesh->mloopuv != nullptr) {
if (format_specific->dedup_uvs)
uv = uv_mapping[me.uv_offset + li]->second + 1;
else
uv = me.uv_offset + li + 1;
}
if (settings->export_normals) {
if (format_specific->dedup_normals)
no = no_mapping[me.no_offset + li]->second + 1;
else
no = me.no_offset + l.v + 1;
}
if (settings->export_uvs && settings->export_normals && me.mesh->mloopuv != nullptr)
fprintf(file, " %zu/%zu/%zu", vx, uv, no);
else if (settings->export_uvs && me.mesh->mloopuv != nullptr)
fprintf(file, " %zu/%zu", vx, uv);
else if (settings->export_normals)
fprintf(file, " %zu//%zu", vx, no);
else
fprintf(file, " %zu", vx);
++li;
}
fputc('\n', file);
}
if (settings->export_edges) {
for (const MEdge &e : common::loose_edge_iter{me.mesh})
fprintf(file, "l %u %u\n", me.mesh->totvert + e.v1, me.mesh->totvert + e.v2);
}
}
return true;
}
bool OBJ_export_object(bContext *C,
ExportSettings *const settings,
Scene *scene,
Object *ob,
std::FILE *file,
std::vector<Mesh_export> &meshes)
{
switch (ob->type) {
case OB_MESH: {
meshes.emplace_back();
Mesh_export &me = meshes.back();
me.object = ob;
me.needs_free = common::get_final_mesh(
settings, scene, ob, &me.mesh /* OUT */, &me.mat /* OUT */);
return me.mesh != nullptr;
}
case OB_CURVE:
case OB_SURF:
if (settings->export_curves)
return OBJ_export_curve(C, settings, file, ob, (const Curve *)ob->data);
else
return true; // Continue
default:
// TODO someone Probably abort, it shouldn't be possible to get here (once finished)
std::cerr << "OBJ Export for the object \"" << ob->id.name << "\" is not implemented\n";
// BLI_assert(false);
return true;
}
}
void OBJ_export_start(bContext *C, ExportSettings *const settings)
{
common::export_start(C, settings);
const OBJExportSettings *format_specific = (OBJExportSettings *)settings->format_specific;
// If not exporting animations, the start and end are the same
for (int frame = format_specific->start_frame; frame <= format_specific->end_frame; ++frame) {
std::FILE *obj_file = get_file(
settings->filepath, ".obj", format_specific->export_animations, frame);
if (obj_file == nullptr)
return;
fprintf(obj_file, "# %s\n# www.blender.org\n", common::get_version_string().c_str());
BKE_scene_frame_set(settings->scene, frame);
BKE_scene_graph_update_for_newframe(settings->depsgraph, settings->main);
Scene *escene = DEG_get_evaluated_scene(settings->depsgraph);
std::string mtl_path = get_path(
settings->filepath, ".mtl", format_specific->export_animations, frame);
std::set<const Material *> materials;
if (settings->export_materials) {
fprintf(obj_file, "mtllib %s\n", (mtl_path.c_str() + mtl_path.find_last_of("/\\") + 1));
}
std::vector<Mesh_export> meshes;
DEG_OBJECT_ITER_BEGIN (settings->depsgraph,
ob,
DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY |
DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET | DEG_ITER_OBJECT_FLAG_DUPLI) {
if (common::should_export_object(settings, ob)) {
if (!OBJ_export_object(C, settings, escene, ob, obj_file, meshes /* OUT */)) {
return;
}
if (settings->export_materials) {
auto mi = material_iter(ob);
materials.insert(mi.begin(), mi.end());
}
}
}
DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END;
OBJ_export_meshes(C, settings, escene, obj_file, meshes /* IN */);
if (settings->export_materials) {
if (!OBJ_export_materials(C, settings, mtl_path, materials)) {
std::cerr << "Couldn't export materials\n";
}
}
fclose(obj_file);
}
}
bool OBJ_export_end(bContext *C, ExportSettings *const settings)
{
return common::export_end(C, settings);
}
} // namespace
extern "C" {
bool OBJ_export(bContext *C, ExportSettings *const settings)
{
return common::time(C, settings, &OBJ_export_start, &OBJ_export_end);
}
} // extern

View File

@@ -0,0 +1,252 @@
extern "C" {
#include "BLI_sys_types.h"
#include "BLI_math.h"
#include "BLT_translation.h"
#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_global.h"
#include "BKE_library.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_mesh_runtime.h"
#include "BKE_object.h"
#include "BKE_scene.h"
#include "DNA_curve_types.h"
#include "DNA_ID.h"
#include "DNA_layer_types.h"
#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_mesh_types.h"
#include "DNA_object_types.h"
#include "DNA_space_types.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
#include "WM_api.h"
#include "WM_types.h"
#ifdef WITH_PYTHON
# include "BPY_extern.h"
#endif
#include "ED_object.h"
#include "bmesh.h"
#include "bmesh_tools.h"
#include "obj.h"
#include "../io_common.h"
#include "../io_obj.h"
}
#include <array>
#include <algorithm>
#include <chrono>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <set>
#include <sstream>
#include <unordered_map>
#include <vector>
#include <tuple>
#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/iostreams/device/mapped_file.hpp>
#include <boost/phoenix/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/define_struct.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include "common.hpp"
#include "iterators.hpp"
BOOST_FUSION_DEFINE_STRUCT((), obj_face, (int, vx)(int, uv)(int, no))
BOOST_FUSION_DEFINE_STRUCT((), float3, (float, x)(float, y)(float, z))
BOOST_FUSION_DEFINE_STRUCT((), float2, (float, u)(float, v))
namespace {
namespace ph = boost::phoenix;
namespace qi = boost::spirit::qi;
template<typename Result> using rule = qi::rule<const char *, Result>;
template<typename Inner> using vec = std::vector<Inner>;
using str = std::string;
struct OBJImport {
ImportSettings *settings;
vec<float3> vertices;
vec<float2> uvs;
vec<float3> normals;
vec<vec<obj_face>> faces;
// Object *current_object;
Mesh *current_mesh;
vec<Mesh *> meshes;
OBJImport(ImportSettings *settings) : settings(settings)
{
}
void v_line(float3 &v)
{
this->vertices.push_back(v);
}
void vn_line(float3 &no)
{
this->normals.push_back(no);
}
void vt_line(float2 &uv)
{
this->uvs.push_back(uv);
}
void f_line(vec<obj_face> &face)
{
this->faces.push_back(face);
this->current_mesh->totloop += face.size();
++this->current_mesh->totpoly;
}
void g_line(vec<str> &s)
{
}
void o_line(str &o)
{
Object *ob = BKE_object_add(
settings->main, settings->scene, settings->view_layer, OB_MESH, o.c_str());
this->current_mesh = (Mesh *)ob->data;
this->meshes.push_back(current_mesh);
}
void s_line(int &s)
{
}
void mtllib_line(vec<str> &mtllibs)
{
}
void usemtl_line(str &s)
{
}
};
void OBJ_import_start(bContext *C, ImportSettings *const settings)
{
common::import_start(C, settings);
OBJImport import{settings};
boost::iostreams::mapped_file mmap(settings->filepath, boost::iostreams::mapped_file::readonly);
const char *const begin = mmap.const_data();
const char *const end = begin + mmap.size() - 1;
const char *curr = begin;
/* clang-format off */
using ign = qi::unused_type;
// Components
rule<ign()> rest = *qi::omit[qi::char_ - qi::eol];
rule<ign()> comment = '#' >> rest;
rule<str()> token = qi::lexeme[+(qi::graph)];
rule<float()> rulef = qi::float_ | qi::as<float>()[qi::int_];
rule<float2()> rule2f = rulef >> rulef;
rule<float3()> rule3f = qi::float_ >> qi::float_ >> qi::float_; //rulef >> rulef >> rulef;
rule<obj_face> rule_obj_face = qi::int_ >> -('/' >> -qi::int_) >> -('/' >> qi::int_);
rule<int()> on_off = qi::lit("off")[qi::_val = 0] |
qi::lit("on")[qi::_val = 1] |
qi::int_[qi::_val = qi::_1];
// Lines
rule<float3()> v_line = 'v' >> rule3f >> rest;
rule<float3()> vn_line = "vn" >> rule3f >> rest;
rule<float2()> vt_line = "vt" >> rule2f >> rest;
rule<vec<obj_face>()> f_line = 'f' >> qi::repeat(3, qi::inf)[rule_obj_face] >> rest;
rule<vec<str>()> g_line = 'g' >> *token >> rest;
rule<str()> o_line = 'o' >> token >> qi::eol;
rule<int()> s_line = 's' >> on_off >> rest;
rule<vec<str>()> mtllib_line = "mtllib" >> +token >> rest;
rule<str()> usemtl_line = "usemtl" >> token >> rest;
// When rule x matches, run function of same name on the import
// object (instance of OBJImport) with the match
#define BIND(x) x[ph::bind(&OBJImport::x, &import, qi::_1)]
bool result = qi::phrase_parse(first /* modifies */, last,
*(BIND(v_line) |
BIND(vn_line) |
BIND(vt_line) |
BIND(f_line) |
BIND(g_line) |
BIND(o_line) |
BIND(s_line) |
BIND(mtllib_line) |
BIND(usemtl_line)),
qi::blank | qi::char_(0) | comment);
#undef BIND
/* clang-format on */
if (!result || curr != end) {
std::cerr << "Couldn't parse near: " << curr;
return;
}
if (import.meshes.size() >= 2) {
std::cerr << "CAN'T IMPORT MORE THAN ONE OBJECT YET\n";
return;
}
Mesh *mesh = import.current_mesh;
mesh->totvert = import.vertices.size();
mesh->mvert = (struct MVert *)CustomData_add_layer(
&mesh->vdata, CD_MVERT, CD_CALLOC, NULL, mesh->totvert);
for (int i = 0; i < mesh->totvert; ++i) {
mesh->mvert->co[0] = import.vertices[i].x;
mesh->mvert->co[1] = import.vertices[i].y;
mesh->mvert->co[2] = import.vertices[i].z;
}
// mesh->totpoly = import.faces;
mesh->mpoly = (MPoly *)CustomData_add_layer(
&mesh->pdata, CD_MPOLY, CD_CALLOC, NULL, mesh->totpoly);
mesh->mloop = (MLoop *)CustomData_add_layer(
&mesh->ldata, CD_MLOOP, CD_CALLOC, NULL, mesh->totloop);
size_t ls = 0;
for (size_t i = 0; i < import.faces.size(); ++i) {
mesh->mpoly[i].loopstart = ls;
mesh->mpoly[i].totloop = import.faces[i].size();
for (size_t j = mesh->mpoly[i].loopstart; j < ls + mesh->mpoly[i].totloop; ++ls) {
mesh->mloop[j].v = import.faces[i][j].vx;
}
ls += import.faces[i].size() - 1;
}
// Will add the edges. Temporary
BKE_mesh_calc_edges(mesh, false, false);
// BKE_mesh_validate(mesh, false, false);
DEG_id_tag_update(&settings->scene->id, ID_RECALC_COPY_ON_WRITE);
DEG_relations_tag_update(settings->main);
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
}
bool OBJ_import_end(bContext *C, ImportSettings *const settings)
{
return common::import_end(C, settings);
}
} // namespace
extern "C" {
bool OBJ_import(bContext *C, ImportSettings *const settings)
{
return common::time(C, settings, &OBJ_import_start, &OBJ_import_end);
}
}

View File

@@ -0,0 +1,153 @@
extern "C" {
#include "BLI_math.h"
#include "BLI_sys_types.h"
#include "BLT_translation.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_global.h"
#include "BKE_context.h"
#include "BKE_scene.h"
#include "BKE_library.h"
#include "BKE_customdata.h"
/* SpaceType struct has a member called 'new' which obviously conflicts with C++
* so temporarily redefining the new keyword to make it compile. */
#define new extern_new
#include "BKE_screen.h"
#undef new
#include "DNA_space_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_ID.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
#include "DEG_depsgraph_query.h"
#include "MEM_guardedalloc.h"
#include "WM_api.h"
#include "WM_types.h"
#include "ED_object.h"
#include "bmesh.h"
#include "bmesh_tools.h"
#include "stl.h"
#include "../io_common.h"
#include "../io_stl.h"
}
#include <chrono>
#include <iostream>
#include <ios>
#include <fstream>
#include <cstdint>
#include "common.hpp"
#include "iterators.hpp"
namespace {
bool STL_export_mesh_ascii(bContext *UNUSED(C),
ExportSettings *const UNUSED(settings),
std::fstream &fs,
const Object *const ob,
const Mesh *const mesh)
{
// TODO someone Is it ok to add the version info after a # in STL?
const std::string name = common::get_object_name(ob, mesh) + " # " +
common::get_version_string();
fs << std::scientific;
fs << "solid " << name;
for (const MPoly &mp : common::poly_iter{mesh}) {
const std::array<float, 3> no = common::calculate_normal(mesh, mp);
fs << "facet normal " << no[0] << ' ' << no[1] << ' ' << no[2] << "\nouter loop";
for (const MVert &v : common::vert_of_poly_iter{mesh, mp})
fs << "\nvertex " << v.co[0] << ' ' << v.co[1] << ' ' << v.co[2];
fs << "\nendloop\nendfacet\n";
}
fs << "endsolid " << name;
return true;
}
const int __one__ = 1;
// CPU endianness
const bool little_endian = 1 == *(char *)(&__one__);
template<typename T, size_t size = sizeof(T)>
std::fstream &operator<<(std::fstream &fs, const T &v)
{
if (little_endian) {
std::cerr << std::scientific << " ";
std::cerr << v;
fs.write((char *)&v, size);
}
else {
char bytes[size], *pv = (char *)&v;
for (int i = 0; i < size; ++i)
bytes[i] = pv[size - 1 - i];
fs.write(bytes, size);
}
return fs;
}
bool STL_export_mesh_bin(bContext *UNUSED(C),
ExportSettings *const UNUSED(settings),
std::fstream &fs,
const Object *const UNUSED(ob),
const Mesh *const mesh)
{
char header[80] = {'\0'};
std::uint32_t tri = mesh->totpoly;
std::uint16_t attribute = 0;
fs << header << tri;
for (const MPoly &mp : common::poly_iter{mesh}) {
const std::array<float, 3> no = common::calculate_normal(mesh, mp);
fs << no[0] << no[1] << no[2];
for (const MVert &v : common::vert_of_poly_iter{mesh, mp})
fs << v.co[0] << v.co[1] << v.co[2];
fs << attribute;
}
return true;
}
void STL_export_start(bContext *C, ExportSettings *const settings)
{
common::export_start(C, settings);
std::fstream fs;
fs.open(settings->filepath, std::ios::out | std::ios::trunc);
Scene *escene = DEG_get_evaluated_scene(settings->depsgraph);
for (const Object *const ob : common::exportable_object_iter{settings->view_layer, settings}) {
Mesh *mesh;
settings->triangulate = true; // STL only really works with triangles
float mat[4][4];
bool needs_free = common::get_final_mesh(settings, escene, ob, &mesh, &mat);
if (((STLExportSettings *)settings->format_specific)->use_ascii) {
if (!STL_export_mesh_ascii(C, settings, fs, ob, mesh))
return;
}
else {
if (!STL_export_mesh_bin(C, settings, fs, ob, mesh))
return;
}
common::free_mesh(mesh, needs_free);
}
}
bool STL_export_end(bContext *C, ExportSettings *const settings)
{
return common::export_end(C, settings);
}
} // namespace
extern "C" {
bool STL_export(bContext *C, ExportSettings *const settings)
{
return common::time(C, settings, &STL_export_start, &STL_export_end);
}
} // extern

View File

@@ -0,0 +1,40 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* 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.
*
* Contributor(s): Hugo Sales
*
* ***** END GPL LICENSE BLOCK *****
*/
/* #ifndef __IO_STL_H__ */
/* #define __IO_STL_H__ */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include "../io_common.h"
struct bContext;
bool STL_export(bContext *C, ExportSettings *const settings);
#ifdef __cplusplus
}
#endif /* __cplusplus */
/* #endif /\* __IO_STL_H__ *\/ */

View File

@@ -0,0 +1,287 @@
#include "io_common.h"
#include "DNA_modifier_types.h"
#include "BLI_string.h"
#include "BLI_path_util.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
#include "ED_object.h"
#include "MEM_guardedalloc.h"
#include "UI_interface.h"
#include "UI_resources.h"
const EnumPropertyItem axis_remap[] = {{AXIS_X, "AXIS_X", ICON_NONE, "X axis", ""},
{AXIS_Y, "AXIS_Y", ICON_NONE, "Y axis", ""},
{AXIS_Z, "AXIS_Z", ICON_NONE, "Z axis", ""},
{AXIS_NEG_X, "AXIS_NEG_X", ICON_NONE, "-X axis", ""},
{AXIS_NEG_Y, "AXIS_NEG_Y", ICON_NONE, "-Y axis", ""},
{AXIS_NEG_Z, "AXIS_NEG_Z", ICON_NONE, "-Z axis", ""},
{0, NULL, 0, NULL, NULL}};
void io_common_default_declare_export(struct wmOperatorType *ot, eFileSel_File_Types file_type)
{
// Defines "filepath"
WM_operator_properties_filesel(ot,
FILE_TYPE_FOLDER | file_type,
FILE_BLENDER,
FILE_SAVE,
WM_FILESEL_FILEPATH,
FILE_DEFAULTDISPLAY,
FILE_SORT_ALPHA);
RNA_def_enum(ot->srna,
"axis_forward",
axis_remap,
AXIS_NEG_Z, // From orientation helper, not sure why
"Forward",
"The axis to remap the forward axis to");
RNA_def_enum(ot->srna,
"axis_up",
axis_remap,
AXIS_Y, // From orientation helper, not sure why
"Up",
"The axis to remap the up axis to");
RNA_def_boolean(
ot->srna, "selected_only", 0, "Selected Objects Only", "Export only selected objects");
RNA_def_boolean(ot->srna,
"visible_only",
0,
"Visible Objects Only",
"Export only objects marked as visible");
RNA_def_boolean(ot->srna,
"renderable_only",
1,
"Renderable Objects Only",
"Export only objects marked renderable in the outliner");
RNA_def_boolean(
ot->srna, "apply_modifiers", 0, "Apply Modifiers", "Apply modifiers before exporting");
RNA_def_boolean(ot->srna,
"render_modifiers",
0,
"Use Render Modifiers",
"Whether to use Render or View modifier stettings");
RNA_def_boolean(ot->srna,
"triangulate",
false,
"Triangulate",
"Export Polygons (Quads & NGons) as Triangles");
RNA_def_enum(ot->srna,
"quad_method",
rna_enum_modifier_triangulate_quad_method_items,
MOD_TRIANGULATE_QUAD_SHORTEDGE,
"Quad Method",
"Method for splitting the quads into triangles");
RNA_def_enum(ot->srna,
"ngon_method",
rna_enum_modifier_triangulate_quad_method_items,
MOD_TRIANGULATE_NGON_BEAUTY,
"Polygon Method",
"Method for splitting the polygons into triangles");
RNA_def_float(ot->srna,
"global_scale",
1.0f,
0.0001f,
1000.0f,
"Scale",
"Value by which to enlarge or shrink the objects with"
"respect to the world's origin",
0.0001f,
1000.0f);
}
void io_common_default_declare_import(struct wmOperatorType *ot, eFileSel_File_Types file_type)
{
// Defines "filepath"
WM_operator_properties_filesel(ot,
FILE_TYPE_FOLDER | file_type,
FILE_BLENDER,
FILE_SAVE,
WM_FILESEL_FILEPATH,
FILE_DEFAULTDISPLAY,
FILE_SORT_ALPHA);
RNA_def_enum(ot->srna,
"axis_forward",
axis_remap,
AXIS_NEG_Z, // From orientation helper, not sure why
"Forward",
"The axis to remap the forward axis to");
RNA_def_enum(ot->srna,
"axis_up",
axis_remap,
AXIS_Y, // From orientation helper, not sure why
"Up",
"The axis to remap the up axis to");
RNA_def_float(ot->srna,
"global_scale",
1.0f,
0.0001f,
1000.0f,
"Scale",
"Value by which to enlarge or shrink the objects with"
"respect to the world's origin",
0.0001f,
1000.0f);
}
ExportSettings *io_common_construct_default_export_settings(struct bContext *C,
struct wmOperator *op)
{
if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
BKE_report(op->reports, RPT_ERROR, "No file given");
return NULL;
}
ExportSettings *settings = MEM_mallocN(sizeof(ExportSettings), "ExportSettings");
settings->scene = CTX_data_scene(C);
settings->view_layer = CTX_data_view_layer(C);
settings->main = CTX_data_main(C);
settings->depsgraph = NULL; // Defer building
RNA_string_get(op->ptr, "filepath", settings->filepath);
settings->axis_forward = RNA_enum_get(op->ptr, "axis_forward");
settings->axis_up = RNA_enum_get(op->ptr, "axis_up");
settings->selected_only = RNA_boolean_get(op->ptr, "selected_only");
settings->visible_only = RNA_boolean_get(op->ptr, "visible_only");
settings->renderable_only = RNA_boolean_get(op->ptr, "renderable_only");
settings->apply_modifiers = RNA_boolean_get(op->ptr, "apply_modifiers");
settings->render_modifiers = RNA_boolean_get(op->ptr, "render_modifiers");
settings->triangulate = RNA_boolean_get(op->ptr, "triangulate");
settings->quad_method = RNA_enum_get(op->ptr, "quad_method");
settings->ngon_method = RNA_enum_get(op->ptr, "ngon_method");
settings->global_scale = RNA_float_get(op->ptr, "global_scale");
return settings;
}
ImportSettings *io_common_construct_default_import_settings(struct bContext *C,
struct wmOperator *op)
{
if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
BKE_report(op->reports, RPT_ERROR, "No file given");
return NULL;
}
ImportSettings *settings = MEM_mallocN(sizeof(ImportSettings), "ImportSettings");
settings->scene = CTX_data_scene(C);
settings->view_layer = CTX_data_view_layer(C);
settings->main = CTX_data_main(C);
settings->depsgraph = NULL; // Defer building
RNA_string_get(op->ptr, "filepath", settings->filepath);
settings->axis_forward = RNA_enum_get(op->ptr, "axis_forward");
settings->axis_up = RNA_enum_get(op->ptr, "axis_up");
settings->global_scale = RNA_float_get(op->ptr, "global_scale");
return settings;
}
bool io_common_export_check(bContext *UNUSED(C), wmOperator *op, const char *ext)
{
char filepath[FILE_MAX];
RNA_string_get(op->ptr, "filepath", filepath);
if (!BLI_path_extension_check(filepath, ext)) {
BLI_path_extension_ensure(filepath, FILE_MAX, ext);
RNA_string_set(op->ptr, "filepath", filepath);
return true;
}
return false;
}
int io_common_export_invoke(bContext *C,
wmOperator *op,
const wmEvent *UNUSED(event),
const char *ext)
{
RNA_boolean_set(op->ptr, "init_scene_frame_range", true);
if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
Main *bmain = CTX_data_main(C);
char filepath[FILE_MAX];
if (BKE_main_blendfile_path(bmain)[0] == '\0') {
BLI_strncpy(filepath, "untitled", sizeof(filepath));
}
else {
BLI_strncpy(filepath, BKE_main_blendfile_path(bmain), sizeof(filepath));
}
BLI_path_extension_replace(filepath, sizeof(filepath), ext);
RNA_string_set(op->ptr, "filepath", filepath);
}
WM_event_add_fileselect(C, op);
return OPERATOR_RUNNING_MODAL;
}
int io_common_import_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
WM_event_add_fileselect(C, op);
return OPERATOR_RUNNING_MODAL;
}
int io_common_export_exec(struct bContext *C,
struct wmOperator *op,
ExportSettings *settings,
bool (*exporter)(struct bContext *C, ExportSettings *settings))
{
Object *obedit = CTX_data_edit_object(C);
if (obedit) {
ED_object_mode_toggle(C, OB_MODE_EDIT);
}
float orig_frame = BKE_scene_frame_get(CTX_data_scene(C));
bool ok = exporter(C, settings);
BKE_scene_frame_set(CTX_data_scene(C), orig_frame);
return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
int io_common_import_exec(struct bContext *C,
struct wmOperator *op,
ImportSettings *settings,
bool (*importer)(struct bContext *C, ImportSettings *settings))
{
float orig_frame = BKE_scene_frame_get(CTX_data_scene(C));
bool ok = importer(C, settings);
BKE_scene_frame_set(CTX_data_scene(C), orig_frame);
return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}

View File

@@ -0,0 +1,108 @@
#include "WM_api.h"
#include "WM_types.h"
#include "BKE_context.h"
#include "DNA_space_types.h"
#ifndef __IO_COMMON_H__
# define __IO_COMMON_H__
struct bContext;
struct Depsgraph;
enum axis_remap { AXIS_X, AXIS_Y, AXIS_Z, AXIS_NEG_X, AXIS_NEG_Y, AXIS_NEG_Z };
extern const EnumPropertyItem axis_remap[];
typedef struct ExportSettings {
/* Mostly From Alembic */
struct ViewLayer *view_layer;
// Scene layer to export; all its objects will be exported, unless selected_only=true
struct Scene *scene;
struct Main *main;
struct Depsgraph *depsgraph;
char filepath[FILE_MAX];
enum axis_remap axis_forward;
enum axis_remap axis_up;
bool selected_only;
bool visible_only;
bool renderable_only;
bool export_normals;
bool export_uvs;
bool export_edges;
bool export_materials;
bool export_vcolors;
bool export_vweights;
bool export_particles;
bool export_hair;
bool export_child_hairs;
bool export_curves;
bool apply_modifiers;
bool render_modifiers;
bool pack_uv;
bool triangulate;
int quad_method;
int ngon_method;
bool use_scene_units;
float global_scale;
void *format_specific; // Pointer to struct with extra settings that vary by exporter
/* bool (*should_export_object)(const ExportSettings * const settings, const Object * const eob);
*/
} ExportSettings;
typedef struct ImportSettings {
struct ViewLayer *view_layer;
struct Scene *scene;
struct Main *main;
struct Depsgraph *depsgraph;
char filepath[FILE_MAX];
enum axis_remap axis_forward;
enum axis_remap axis_up;
float global_scale;
void *format_specific;
} ImportSettings;
void io_common_default_declare_export(struct wmOperatorType *ot, eFileSel_File_Types file_type);
bool io_common_export_check(struct bContext *UNUSED(C), wmOperator *op, const char *ext);
int io_common_export_invoke(struct bContext *C,
wmOperator *op,
const wmEvent *UNUSED(event),
const char *ext);
int io_common_export_exec(struct bContext *C,
struct wmOperator *op,
ExportSettings *settings,
bool (*exporter)(struct bContext *C, ExportSettings *settings));
void io_common_default_declare_import(struct wmOperatorType *ot, eFileSel_File_Types file_type);
int io_common_import_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event));
int io_common_import_exec(struct bContext *C,
struct wmOperator *op,
ImportSettings *settings,
bool (*importer)(struct bContext *C, ImportSettings *settings));
/* void io_common_export_draw(bContext *C, wmOperator *op); */
ExportSettings *io_common_construct_default_export_settings(struct bContext *C,
struct wmOperator *op);
ImportSettings *io_common_construct_default_import_settings(struct bContext *C,
struct wmOperator *op);
#endif /* __IO_COMMON_H__ */

View File

@@ -0,0 +1,477 @@
#include "WM_api.h"
#include "WM_types.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_scene.h"
#include "MEM_guardedalloc.h"
#include "DNA_space_types.h"
#include "io_obj.h"
#include "intern/obj.h"
#include "BLT_translation.h"
#include "UI_interface.h"
#include "UI_resources.h"
const EnumPropertyItem path_reference_mode[] = {
{AUTO, "AUTO", ICON_NONE, "Auto", "Use Relative paths with subdirectories only"},
{ABSOLUTE, "ABSOLUTE", ICON_NONE, "Absolute", "Always write absolute paths"},
{RELATIVE, "RELATIVE", ICON_NONE, "Relative", "Always write relative paths (where possible)"},
{MATCH, "MATCH", ICON_NONE, "Match", "Match Absolute/Relative setting with input path"},
{STRIP, "STRIP", ICON_NONE, "Strip Path", "Filename only"},
{COPY, "COPY", ICON_NONE, "Copy", "Copy the file to the destination path (or subdirectory)"},
{0, NULL, 0, NULL, NULL}};
const EnumPropertyItem split_mode[] = {
{SPLIT_ON, "ON", ICON_NONE, "Split", "Split geometry, omits unused verts"},
{SPLIT_OFF, "OFF", ICON_NONE, "Keep Vert Order", "Keep vertex order from file"},
{0, NULL, 0, NULL, NULL}};
static int wm_obj_export_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
return io_common_export_invoke(C, op, event, ".obj");
}
static int wm_obj_export_exec(bContext *C, wmOperator *op)
{
ExportSettings *settings = io_common_construct_default_export_settings(C, op);
if (!settings) {
return OPERATOR_CANCELLED;
}
settings->export_normals = RNA_boolean_get(op->ptr, "export_normals");
settings->export_uvs = RNA_boolean_get(op->ptr, "export_uvs");
settings->export_edges = RNA_boolean_get(op->ptr, "export_edges");
settings->export_materials = RNA_boolean_get(op->ptr, "export_materials");
settings->export_vcolors = RNA_boolean_get(op->ptr, "export_vcolors");
settings->export_vweights = RNA_boolean_get(op->ptr, "export_vweights");
settings->export_curves = RNA_boolean_get(op->ptr, "export_curves");
settings->pack_uv = RNA_boolean_get(op->ptr, "pack_uv");
settings->format_specific = MEM_mallocN(sizeof(OBJExportSettings), "OBJExportSettings");
OBJExportSettings *format_specific = settings->format_specific;
format_specific->start_frame = RNA_int_get(op->ptr, "start_frame");
format_specific->end_frame = RNA_int_get(op->ptr, "end_frame");
format_specific->export_animations = RNA_boolean_get(op->ptr, "export_animations");
format_specific->dedup_normals = RNA_boolean_get(op->ptr, "dedup_normals");
format_specific->dedup_normals_threshold = RNA_float_get(op->ptr, "dedup_normals_threshold");
format_specific->dedup_uvs = RNA_boolean_get(op->ptr, "dedup_uvs");
format_specific->dedup_uvs_threshold = RNA_float_get(op->ptr, "dedup_uvs_threshold");
format_specific->export_face_sets = RNA_boolean_get(op->ptr, "export_face_sets");
format_specific->path_mode = RNA_enum_get(op->ptr, "path_mode");
format_specific->export_objects_as_objects = RNA_boolean_get(op->ptr,
"export_objects_as_objects");
format_specific->export_objects_as_groups = RNA_boolean_get(op->ptr, "export_objects_as_groups");
format_specific->export_smooth_groups = RNA_boolean_get(op->ptr, "export_smooth_groups");
format_specific->smooth_groups_bitflags = RNA_boolean_get(op->ptr, "smooth_groups_bitflags");
if (!format_specific->export_animations) {
format_specific->start_frame = BKE_scene_frame_get(CTX_data_scene(C));
format_specific->end_frame = format_specific->start_frame;
}
return io_common_export_exec(C, op, settings, &OBJ_export /* export function */);
}
static void wm_obj_export_draw(bContext *C, wmOperator *op)
{
PointerRNA ptr;
RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
/* Conveniently set start and end frame to match the scene's frame range. */
Scene *scene = CTX_data_scene(C); // For SFRA and EFRA macros
/* So we only do this once, use the dummy property */
if (scene != NULL && RNA_boolean_get(&ptr, "init_scene_frame_range")) {
RNA_int_set(&ptr, "start_frame", SFRA);
RNA_int_set(&ptr, "end_frame", EFRA);
RNA_boolean_set(&ptr, "init_scene_frame_range", false);
}
uiLayout *box;
uiLayout *sub_box;
uiLayout *row;
/* Scene Options */
box = uiLayoutBox(op->layout);
row = uiLayoutRow(box, false);
uiItemL(row, IFACE_("Scene Options:"), ICON_SCENE_DATA);
row = uiLayoutRow(box, false);
uiItemR(row, &ptr, "axis_forward", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, &ptr, "axis_up", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
sub_box = uiLayoutBox(row);
uiItemR(sub_box, &ptr, "export_animations", 0, NULL, ICON_NONE);
const bool animations = RNA_boolean_get(&ptr, "export_animations");
row = uiLayoutRow(sub_box, false);
uiLayoutSetEnabled(row, animations);
uiItemR(row, &ptr, "start_frame", 0, NULL, ICON_NONE);
row = uiLayoutRow(sub_box, false);
uiLayoutSetEnabled(row, animations);
uiItemR(row, &ptr, "end_frame", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, &ptr, "selected_only", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, &ptr, "visible_only", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, &ptr, "renderable_only", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, &ptr, "global_scale", 0, NULL, ICON_NONE);
/* Object Data */
box = uiLayoutBox(op->layout);
row = uiLayoutRow(box, false);
uiItemL(row, IFACE_("Object Options:"), ICON_OBJECT_DATA);
row = uiLayoutRow(box, false);
sub_box = uiLayoutBox(row);
uiItemR(sub_box, &ptr, "export_normals", 0, NULL, ICON_NONE);
const bool normals = RNA_boolean_get(&ptr, "export_normals");
row = uiLayoutRow(sub_box, false);
uiLayoutSetEnabled(row, normals);
uiItemR(row, &ptr, "dedup_normals", 0, NULL, ICON_NONE);
const bool dedup_normals = RNA_boolean_get(&ptr, "dedup_normals");
row = uiLayoutRow(sub_box, false);
uiLayoutSetEnabled(row, normals && dedup_normals);
uiItemR(row, &ptr, "dedup_normals_threshold", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
sub_box = uiLayoutBox(row);
uiItemR(sub_box, &ptr, "export_uvs", 0, NULL, ICON_NONE);
const bool uvs = RNA_boolean_get(&ptr, "export_uvs");
row = uiLayoutRow(sub_box, false);
uiLayoutSetEnabled(row, uvs);
uiItemR(row, &ptr, "dedup_uvs", 0, NULL, ICON_NONE);
const bool dedup_uvs = RNA_boolean_get(&ptr, "dedup_uvs");
row = uiLayoutRow(sub_box, false);
uiLayoutSetEnabled(row, uvs && dedup_uvs);
uiItemR(row, &ptr, "dedup_uvs_threshold", 0, NULL, ICON_NONE);
row = uiLayoutRow(sub_box, false);
uiLayoutSetEnabled(row, uvs);
uiItemR(row, &ptr, "pack_uv", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, &ptr, "export_edges", 0, NULL, ICON_NONE);
sub_box = uiLayoutBox(box);
row = uiLayoutRow(sub_box, false);
uiItemR(row, &ptr, "export_materials", 0, NULL, ICON_NONE);
const bool materials = RNA_boolean_get(&ptr, "export_materials");
row = uiLayoutRow(sub_box, false);
uiLayoutSetEnabled(row, materials);
uiItemR(row, &ptr, "path_mode", 0, NULL, ICON_NONE);
sub_box = uiLayoutBox(box);
row = uiLayoutRow(sub_box, false);
uiItemR(row, &ptr, "export_smooth_groups", 0, NULL, ICON_NONE);
const bool smooth_groups = RNA_boolean_get(&ptr, "export_smooth_groups");
row = uiLayoutRow(sub_box, false);
uiLayoutSetEnabled(row, smooth_groups);
uiItemR(row, &ptr, "smooth_groups_bitflags", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, &ptr, "export_vcolors", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, &ptr, "export_face_sets", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
sub_box = uiLayoutBox(row);
uiItemR(sub_box, &ptr, "apply_modifiers", 0, NULL, ICON_NONE);
const bool modifiers = RNA_boolean_get(&ptr, "apply_modifiers");
row = uiLayoutRow(sub_box, false);
uiLayoutSetEnabled(row, modifiers);
uiItemR(row, &ptr, "render_modifiers", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, &ptr, "export_curves", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, &ptr, "triangulate", 0, NULL, ICON_NONE);
const bool triangulate = RNA_boolean_get(&ptr, "triangulate");
row = uiLayoutRow(box, false);
uiLayoutSetEnabled(row, triangulate);
uiItemR(row, &ptr, "quad_method", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiLayoutSetEnabled(row, triangulate);
uiItemR(row, &ptr, "ngon_method", 0, NULL, ICON_NONE);
}
static bool wm_obj_export_check(bContext *C, wmOperator *op)
{
return io_common_export_check(C, op, ".obj");
}
static int wm_obj_import_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
return io_common_import_invoke(C, op, event);
}
static int wm_obj_import_exec(bContext *C, wmOperator *op)
{
ImportSettings *settings = io_common_construct_default_import_settings(C, op);
if (!settings) {
return OPERATOR_CANCELLED;
}
settings->format_specific = MEM_mallocN(sizeof(OBJImportSettings), "OBJImportSettings");
OBJImportSettings *format_specific = settings->format_specific;
format_specific->import_smooth_groups = RNA_boolean_get(op->ptr, "import_smooth_groups");
format_specific->import_edges = RNA_boolean_get(op->ptr, "import_edges");
format_specific->split_mode = RNA_enum_get(op->ptr, "split_mode");
format_specific->split_by_object = RNA_boolean_get(op->ptr, "split_by_object");
format_specific->split_by_groups = RNA_boolean_get(op->ptr, "split_by_groups");
format_specific->import_vertex_groups = RNA_boolean_get(op->ptr, "import_vertex_groups");
format_specific->use_image_search = RNA_boolean_get(op->ptr, "use_image_search");
return io_common_import_exec(C, op, settings, &OBJ_import /* import function */);
}
static void wm_obj_import_draw(bContext *UNUSED(C), wmOperator *op)
{
PointerRNA ptr;
RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
uiLayout *box;
uiLayout *row;
row = uiLayoutRow(op->layout, false);
uiItemR(row, &ptr, "axis_forward", 0, NULL, ICON_NONE);
row = uiLayoutRow(op->layout, false);
uiItemR(row, &ptr, "axis_up", 0, NULL, ICON_NONE);
row = uiLayoutRow(op->layout, false);
uiItemR(row, &ptr, "import_smooth_groups", 0, NULL, ICON_NONE);
row = uiLayoutRow(op->layout, false);
uiItemR(row, &ptr, "import_edges", 0, NULL, ICON_NONE);
box = uiLayoutBox(op->layout);
row = uiLayoutRow(box, false);
uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_EXPAND);
uiItemR(row, &ptr, "split_mode", 0, NULL, ICON_NONE);
/* uiItemEnumR(row, "FOO", ICON_NONE, &ptr, "split_mode", SPLIT_ON); */
/* uiItemFullR( */
/* row, &ptr, RNA_struct_find_property(op->ptr, "split_mode"), 0, SPLIT_ON, 0, "",
* ICON_NONE); */
row = uiLayoutRow(box, false);
const enum split_mode split = RNA_enum_get(&ptr, "split_mode");
if (split == SPLIT_ON) {
uiItemL(row, IFACE_("Split by:"), ICON_NONE);
uiItemR(row, &ptr, "split_by_object", 0, NULL, ICON_NONE);
uiItemR(row, &ptr, "split_by_groups", 0, NULL, ICON_NONE);
}
else {
uiItemR(row, &ptr, "import_vertex_groups", 0, NULL, ICON_NONE);
}
row = uiLayoutRow(op->layout, false);
uiItemR(row, &ptr, "global_scale", 0, NULL, ICON_NONE);
}
static bool wm_obj_import_check(bContext *UNUSED(C), wmOperator *UNUSED(op))
{
return false;
} /* TODO someone */
void WM_OT_obj_export(struct wmOperatorType *ot)
{
ot->name = "Export Wavefront (.obj)";
ot->description = "Export current scene in an .obj archive";
ot->idname = "WM_OT_obj_export_c";
ot->invoke = wm_obj_export_invoke;
ot->exec = wm_obj_export_exec;
ot->poll = WM_operator_winactive;
ot->ui = wm_obj_export_draw;
ot->check = wm_obj_export_check;
io_common_default_declare_export(ot, FILE_TYPE_OBJ);
RNA_def_int(ot->srna,
"start_frame",
INT_MIN,
INT_MIN,
INT_MAX,
"Start Frame",
"Start frame of the export, use the default value to "
"take the start frame of the current scene",
INT_MIN,
INT_MAX);
RNA_def_int(ot->srna,
"end_frame",
INT_MIN,
INT_MIN,
INT_MAX,
"End Frame",
"End frame of the export, use the default value to "
"take the end frame of the current scene",
INT_MIN,
INT_MAX);
RNA_def_boolean(ot->srna, "export_animations", 0, "Animations", "Export animations");
RNA_def_boolean(ot->srna, "export_normals", 1, "Normals", "Export normals");
RNA_def_boolean(ot->srna, "export_uvs", 1, "UVs", "Export UVs");
RNA_def_boolean(ot->srna, "dedup_normals", 1, "Deduplicate Normals", "Remove duplicate normals");
// The UI seems to make it so the minimum softlimit can't be smaller than 0.001,
// but normals are only printed with four decimal places, so it doesn't matter too much
RNA_def_float(ot->srna,
"dedup_normals_threshold",
0.001,
FLT_MIN,
FLT_MAX,
"Threshold for deduplication of Normals",
"The minimum difference so two Normals are considered different",
0.001,
10.0);
RNA_def_boolean(ot->srna, "dedup_uvs", 1, "Deduplicate UVs", "Remove duplicate UVs");
RNA_def_float(ot->srna,
"dedup_uvs_threshold",
0.001,
FLT_MIN,
FLT_MAX,
"Threshold for deduplication of UVs",
"The minimum difference so two UVs are considered different",
0.001,
10.0);
RNA_def_boolean(ot->srna, "export_edges", 0, "Edges", "Export Edges");
RNA_def_boolean(ot->srna, "export_materials", 0, "Materials", "Export Materials");
RNA_def_boolean(
ot->srna, "export_face_sets", 0, "Face Sets", "Export per face shading group assignments");
RNA_def_boolean(ot->srna, "export_vcolors", 0, "Vertex Colors", "Export vertex colors");
RNA_def_boolean(ot->srna, "export_vweights", 0, "Vertex Weights", "Exports vertex weights");
RNA_def_boolean(ot->srna,
"export_objects_as_objects",
1,
"Export as Objects named like Blender Objects",
"Export Blender Object as named objects");
RNA_def_boolean(ot->srna,
"export_objects_as_groups",
0,
"Export as Groups named like Blender Objects",
"Export Blender Objects as named groups");
RNA_def_boolean(
ot->srna, "export_curves", false, "Export curves", "Export curves and NURBS surfaces");
RNA_def_boolean(ot->srna, "pack_uv", false, "Pack UV Islands", "Export UVs with packed island");
RNA_def_enum(ot->srna,
"path_mode",
path_reference_mode,
AUTO,
"Path mode",
"How external files (such as images) are treated");
RNA_def_boolean(ot->srna,
"export_smooth_groups",
false,
"Smooth Groups",
"Write sharp edges as smooth groups");
RNA_def_boolean(ot->srna,
"smooth_groups_bitflags",
false,
"Bitflags",
"Use bitflags for smooth groups (produces at most 32 groups)");
/* This dummy prop is used to check whether we need to init the start and
* end frame values to that of the scene's, otherwise they are reset at
* every change, draw update. */
RNA_def_boolean(ot->srna, "init_scene_frame_range", true, "", "");
}
void WM_OT_obj_import(struct wmOperatorType *ot)
{
ot->name = "Import Wavefront (.obj)";
ot->description = "Import an .obj archive";
ot->idname = "WM_OT_obj_import_c";
ot->invoke = wm_obj_import_invoke;
ot->exec = wm_obj_import_exec;
ot->poll = WM_operator_winactive;
ot->ui = wm_obj_import_draw;
ot->check = wm_obj_import_check;
io_common_default_declare_import(ot, FILE_TYPE_OBJ);
RNA_def_boolean(ot->srna,
"import_smooth_groups",
true,
"Smooth Groups",
"Surround smooth groups by sharp edges");
RNA_def_boolean(
ot->srna, "import_edges", true, "Lines", "Import lines and faces with 2 verts as edge");
RNA_def_enum(ot->srna,
"split_mode",
split_mode,
SPLIT_ON,
"Split Mode",
"Whether to split the vertices into objects");
RNA_def_boolean(
ot->srna, "split_by_object", true, "Object", "Import OBJ Objects into Blender Objects");
RNA_def_boolean(
ot->srna, "split_by_groups", false, "Groups", "Import OBJ Groups into Blender Objects");
RNA_def_boolean(ot->srna,
"import_vertex_groups",
false,
"Vertex Groups",
"Import OBJ groups as vertex groups");
RNA_def_boolean(ot->srna,
"use_image_search",
false,
"Image Search",
"Search subdirs for any associated images (Warning: may be slow)");
}

View File

@@ -0,0 +1,78 @@
/* * ***** BEGIN GPL LICENSE BLOCK *****
*
* 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.
*
* The Original Code is Copyright (C) 2016 Blender Foundation.
* All rights reserved.
*
* ***** END GPL LICENSE BLOCK *****
*
*/
#ifndef __IO_OBJ_H__
#define __IO_OBJ_H__
struct wmOperatorType;
/* #include "WM_types.h" */
#include "io_common.h"
#ifdef __cplusplus
extern "C" {
#endif
enum path_reference_mode { AUTO, ABSOLUTE, RELATIVE, MATCH, STRIP, COPY };
enum split_mode { SPLIT_ON, SPLIT_OFF };
extern const EnumPropertyItem path_reference_mode[];
extern const EnumPropertyItem split_mode[];
typedef struct OBJExportSettings {
float start_frame;
float end_frame;
enum path_reference_mode path_mode;
bool export_animations;
bool export_objects_as_objects;
bool export_objects_as_groups;
bool export_face_sets;
bool dedup_normals;
bool dedup_uvs;
float dedup_normals_threshold;
float dedup_uvs_threshold;
bool export_smooth_groups;
bool smooth_groups_bitflags;
} OBJExportSettings;
typedef struct OBJImportSettings {
bool import_smooth_groups;
bool import_edges;
enum split_mode split_mode;
bool split_by_object;
bool split_by_groups;
bool import_vertex_groups;
bool use_image_search;
} OBJImportSettings;
void WM_OT_obj_export(struct wmOperatorType *ot);
void WM_OT_obj_import(struct wmOperatorType *ot);
#ifdef __cplusplus
}
#endif
#endif /* __IO_OBJ_H__ */

View File

@@ -33,6 +33,10 @@
# include "io_alembic.h"
#endif
// TODO someone make optional?
#include "io_obj.h"
#include "io_stl.h"
#include "io_cache.h"
void ED_operatortypes_io(void)
@@ -47,6 +51,12 @@ void ED_operatortypes_io(void)
WM_operatortype_append(WM_OT_alembic_export);
#endif
WM_operatortype_append(WM_OT_obj_import);
WM_operatortype_append(WM_OT_obj_export);
WM_operatortype_append(WM_OT_stl_import);
WM_operatortype_append(WM_OT_stl_export);
WM_operatortype_append(CACHEFILE_OT_open);
WM_operatortype_append(CACHEFILE_OT_reload);
}

View File

@@ -0,0 +1,150 @@
#include "WM_api.h"
#include "WM_types.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_main.h"
#include "MEM_guardedalloc.h"
#include "DNA_space_types.h"
#include "io_stl.h"
#include "intern/stl.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
#include "UI_interface.h"
#include "UI_resources.h"
/* bool STL_export(bContext *C, ExportSettings * const settings); */
static int wm_stl_export_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
return io_common_export_invoke(C, op, event, ".stl");
}
static int wm_stl_export_exec(bContext *C, wmOperator *op)
{
ExportSettings *settings = io_common_construct_default_export_settings(C, op);
settings->use_scene_units = RNA_boolean_get(op->ptr, "use_scene_units");
settings->format_specific = MEM_mallocN(sizeof(STLExportSettings), "STLExportSettings");
STLExportSettings *format_specific = settings->format_specific;
format_specific->use_ascii = RNA_boolean_get(op->ptr, "use_ascii");
return io_common_export_exec(C, op, settings, &STL_export /* export function */);
}
static void wm_stl_export_draw(bContext *UNUSED(C), wmOperator *op)
{
PointerRNA ptr;
RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
/* uiLayout *box; */
/* uiLayout *sub_box; */
/* box = uiLayoutBox(op->layout); */
uiLayout *row;
row = uiLayoutRow(op->layout, false);
uiItemR(row, &ptr, "axis_forward", 0, NULL, ICON_NONE);
row = uiLayoutRow(op->layout, false);
uiItemR(row, &ptr, "axis_up", 0, NULL, ICON_NONE);
row = uiLayoutRow(op->layout, false);
uiItemR(row, &ptr, "selected_only", 0, NULL, ICON_NONE);
row = uiLayoutRow(op->layout, false);
uiItemR(row, &ptr, "visible_only", 0, NULL, ICON_NONE);
row = uiLayoutRow(op->layout, false);
uiItemR(row, &ptr, "renderable_only", 0, NULL, ICON_NONE);
row = uiLayoutRow(op->layout, false);
uiItemR(row, &ptr, "use_ascii", 0, NULL, ICON_NONE);
row = uiLayoutRow(op->layout, false);
uiItemR(row, &ptr, "global_scale", 0, NULL, ICON_NONE);
row = uiLayoutRow(op->layout, false);
uiItemR(row, &ptr, "use_scene_units", 0, NULL, ICON_NONE);
row = uiLayoutRow(op->layout, false);
uiItemR(row, &ptr, "apply_modifiers", 0, NULL, ICON_NONE);
const bool modifiers = RNA_boolean_get(&ptr, "apply_modifiers");
row = uiLayoutRow(op->layout, false);
uiLayoutSetEnabled(row, modifiers);
uiItemR(row, &ptr, "render_modifiers", 0, NULL, ICON_NONE);
}
static bool wm_stl_export_check(bContext *C, wmOperator *op)
{
return io_common_export_check(C, op, ".stl");
}
static int wm_stl_import_invoke(bContext *UNUSED(C),
wmOperator *UNUSED(op),
const wmEvent *UNUSED(event))
{
return OPERATOR_CANCELLED;
}
static int wm_stl_import_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
{
return OPERATOR_CANCELLED;
}
static void wm_stl_import_draw(bContext *UNUSED(C), wmOperator *UNUSED(op))
{
}
static bool wm_stl_import_check(bContext *UNUSED(C), wmOperator *UNUSED(op))
{
return false;
}
void WM_OT_stl_export(struct wmOperatorType *ot)
{
ot->name = "Export STL (.stl)";
ot->description = "Export current scene in a .stl archive";
ot->idname = "WM_OT_stl_export_c"; // TODO someone
ot->invoke = wm_stl_export_invoke;
ot->exec = wm_stl_export_exec;
ot->poll = WM_operator_winactive;
ot->ui = wm_stl_export_draw;
ot->check = wm_stl_export_check;
// TODO someone Does there need to be a new file type?
io_common_default_declare_export(ot, FILE_TYPE_OBJ);
RNA_def_boolean(ot->srna,
"use_ascii",
0,
"Use ASCII format",
"Whether to use the ASCII or the binary variant");
RNA_def_boolean(ot->srna,
"use_scene_units",
0,
"Use scene units",
"Whether to use the scene's units as a scaling factor");
}
void WM_OT_stl_import(struct wmOperatorType *ot)
{
ot->name = "Import STL (.stl)";
ot->description = "Import a .stl archive";
ot->idname = "WM_OT_stl_import_c";
ot->invoke = wm_stl_import_invoke;
ot->exec = wm_stl_import_exec;
ot->poll = WM_operator_winactive;
ot->ui = wm_stl_import_draw;
ot->check = wm_stl_import_check;
} /* TODO someone Importer */

View File

@@ -0,0 +1,49 @@
/* * ***** BEGIN GPL LICENSE BLOCK *****
*
* 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.
*
* The Original Code is Copyright (C) 2016 Blender Foundation.
* All rights reserved.
*
* ***** END GPL LICENSE BLOCK *****
*
*/
#ifndef __IO_STL_H__
#define __IO_STL_H__
struct wmOperatorType;
/* #include "WM_types.h" */
#include "io_common.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct STLExportSettings {
bool use_ascii;
} STLExportSettings;
void WM_OT_stl_export(struct wmOperatorType *ot);
void WM_OT_stl_import(struct wmOperatorType *ot);
#ifdef __cplusplus
}
#endif
#endif /* __IO_STL_H__ */

View File

@@ -817,6 +817,7 @@ typedef enum eFileSel_File_Types {
FILE_TYPE_OPERATOR = (1 << 14),
FILE_TYPE_APPLICATIONBUNDLE = (1 << 15),
FILE_TYPE_ALEMBIC = (1 << 16),
FILE_TYPE_OBJ = (1 << 17),
/** An FS directory (i.e. S_ISDIR on its path is true). */
FILE_TYPE_DIR = (1 << 30),