Compare commits
53 Commits
tmp-gpu-po
...
soc-2019-f
Author | SHA1 | Date | |
---|---|---|---|
b3fbc7f191 | |||
e42a7de767 | |||
309bc3c653 | |||
014b53304e | |||
af9ff0f0ee | |||
e77c87a2a9 | |||
d187b683e6 | |||
be6e1ed92a | |||
d1409d2420 | |||
7d99e457cd | |||
b6a1af07b4 | |||
7b26bc3779 | |||
55f6cf2fbb | |||
52c6143fb4 | |||
c9d5f50057 | |||
c49b65a27e | |||
a4383d39d9 | |||
8cad95d10b | |||
922e549c65 | |||
4ab735a46e | |||
cfb6f02870 | |||
aff190c14a | |||
ad93a383f7 | |||
2a1127c199 | |||
10da053977 | |||
32b4a7aa87 | |||
72712e9985 | |||
eaacdda4f1 | |||
1cd052f469 | |||
d08fbfacb2 | |||
b9e10ba1b6 | |||
3a5273458c | |||
661c98ca14 | |||
0c02c06306 | |||
0822ec6fbf | |||
ad89f5a6db | |||
23eff48c9a | |||
59d301befc | |||
b8a84a0402 | |||
acf50b21f2 | |||
c25527edfb | |||
d2e83dc4b2 | |||
6e3e0f8c7f | |||
33d50425ea | |||
2eb8ba7005 | |||
1ac9a06a58 | |||
0757c7fa08 | |||
39cddacb85 | |||
8cf1e0c865 | |||
![]() |
2ca1634636 | ||
566eafd50b | |||
688444698b | |||
7258cc7943 |
8
.gitignore
vendored
8
.gitignore
vendored
@@ -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/
|
Submodule release/datafiles/locale updated: 0f771b0f38...6a6b84fd50
Submodule release/scripts/addons updated: aa3366b780...ccf80c0e04
Submodule release/scripts/addons_contrib updated: 0aa23a4d61...f00d4fbe84
136
release/scripts/modules/obj_export.py
Normal file
136
release/scripts/modules/obj_export.py
Normal 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)
|
@@ -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"
|
||||
|
@@ -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"
|
||||
|
||||
|
@@ -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}")
|
||||
|
359
source/blender/editors/io/intern/common.cpp
Normal file
359
source/blender/editors/io/intern/common.cpp
Normal 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
|
161
source/blender/editors/io/intern/common.hpp
Normal file
161
source/blender/editors/io/intern/common.hpp
Normal 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_
|
686
source/blender/editors/io/intern/iterators.hpp
Normal file
686
source/blender/editors/io/intern/iterators.hpp
Normal 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__
|
42
source/blender/editors/io/intern/obj.h
Normal file
42
source/blender/editors/io/intern/obj.h
Normal 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__ *\/ */
|
518
source/blender/editors/io/intern/obj_export.cpp
Normal file
518
source/blender/editors/io/intern/obj_export.cpp
Normal 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
|
252
source/blender/editors/io/intern/obj_import.cpp
Normal file
252
source/blender/editors/io/intern/obj_import.cpp
Normal 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);
|
||||
}
|
||||
}
|
153
source/blender/editors/io/intern/stl.cpp
Normal file
153
source/blender/editors/io/intern/stl.cpp
Normal 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
|
40
source/blender/editors/io/intern/stl.h
Normal file
40
source/blender/editors/io/intern/stl.h
Normal 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__ *\/ */
|
287
source/blender/editors/io/io_common.c
Normal file
287
source/blender/editors/io/io_common.c
Normal 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;
|
||||
}
|
108
source/blender/editors/io/io_common.h
Normal file
108
source/blender/editors/io/io_common.h
Normal 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__ */
|
477
source/blender/editors/io/io_obj.c
Normal file
477
source/blender/editors/io/io_obj.c
Normal 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)");
|
||||
}
|
78
source/blender/editors/io/io_obj.h
Normal file
78
source/blender/editors/io/io_obj.h
Normal 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__ */
|
@@ -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);
|
||||
}
|
||||
|
150
source/blender/editors/io/io_stl.c
Normal file
150
source/blender/editors/io/io_stl.c
Normal 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 */
|
49
source/blender/editors/io/io_stl.h
Normal file
49
source/blender/editors/io/io_stl.h
Normal 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__ */
|
@@ -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),
|
||||
|
Submodule source/tools updated: aa9cc18913...8598818108
Reference in New Issue
Block a user