Compare commits

..

58 Commits

Author SHA1 Message Date
b3fbc7f191 [Fast import/export] Fixed segfault on some files 2019-07-21 19:46:58 +01:00
e42a7de767 Merge remote-tracking branch 'origin/master' into soc-2019-fast-io 2019-07-21 01:37:07 +01:00
309bc3c653 [Fast import/export] Added OBJ Import with support for only one object and using only vertices and faces 2019-07-21 01:36:08 +01:00
e2b0647272 Industry Compat keymap: Fix inconsistencies and conflicts with color swatches and other over-widget hotkeys
Sample hotkey is set to the I key now, similar to other apps.

Reported on Devtalk
2019-07-20 14:45:45 +02:00
4b980e2f57 Fix T67196: bevel bad at a 3-edge bevel.
The test for whether or not this was a "pipe" neglected to use
fabsf() around a dot result.
2019-07-20 08:38:41 -04:00
c2e8e249ac Fix assert in some cases when using DataTransfer modifier for custom normals.
Reported by @sybren on IRC, thanks.

Should be safe for 2.80.
2019-07-19 16:17:09 +02:00
65aac86d5a GPencil: Fix wrong material when apply Opacity, Hue or Tint modifier
When apply the modifier a new material is created, but it was assigned wrongly. The problem was the index was base 0 already, so subtract 1, got a wrong value.
2019-07-19 15:22:42 +02:00
23ea4b56ad GPencil: Fix showing wrong material in brush panel with pinning
The options of the material are from object material not pinned material.

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

8
.gitignore vendored
View File

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

View File

@@ -0,0 +1,136 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import os
import bpy
from mathutils import Matrix, Vector, Color
from bpy_extras import io_utils, node_shader_utils
def write_mtl(filepath, materials, path_mode):
C = bpy.context
scene = C.scene
world = scene.world
world_amb = Color((0.8, 0.8, 0.8))
source_dir = os.path.dirname(bpy.data.filepath)
dest_dir = os.path.dirname(filepath)
# Set of images topotentially copy
copy_set = set()
with open(filepath, "w", encoding="utf8", newline="\n") as f:
fw = f.write
fw('# Blender MTL File: %r\n' % (os.path.basename(bpy.data.filepath) or "None"))
fw('# Material Count: %i\n' % len(materials))
for material in materials:
fw('newmtl %s\n' % material)
mat = bpy.data.materials[material]
mat_wrap = node_shader_utils.PrincipledBSDFWrapper(mat) if mat else None
if mat_wrap:
use_mirror = mat_wrap.metallic != 0.0
use_transparency = mat_wrap.alpha != 1.0
# XXX Totally empirical conversion, trying to adapt it
# (from 1.0 - 0.0 Principled BSDF range to 0.0 - 900.0 OBJ specular exponent range)...
spec = (1.0 - mat_wrap.roughness) * 30
spec *= spec
fw('Ns %.6f\n' % spec)
# Ambient
if use_mirror:
fw('Ka %.6f %.6f %.6f\n' % (mat_wrap.metallic, mat_wrap.metallic, mat_wrap.metallic))
else:
fw('Ka %.6f %.6f %.6f\n' % (1.0, 1.0, 1.0))
fw('Kd %.6f %.6f %.6f\n' % mat_wrap.base_color[:3]) # Diffuse
# XXX TODO Find a way to handle tint and diffuse color, in a consistent way with import...
fw('Ks %.6f %.6f %.6f\n' % (mat_wrap.specular, mat_wrap.specular, mat_wrap.specular)) # Specular
# Emission, not in original MTL standard but seems pretty common, see T45766.
fw('Ke 0.0 0.0 0.0\n') # XXX Not supported by current Principled-based shader.
fw('Ni %.6f\n' % mat_wrap.ior) # Refraction index
fw('d %.6f\n' % mat_wrap.alpha) # Alpha (obj uses 'd' for dissolve)
# See http://en.wikipedia.org/wiki/Wavefront_.obj_file for whole list of values...
# Note that mapping is rather fuzzy sometimes, trying to do our best here.
if mat_wrap.specular == 0:
fw('illum 1\n') # no specular.
elif use_mirror:
if use_transparency:
fw('illum 6\n') # Reflection, Transparency, Ray trace
else:
fw('illum 3\n') # Reflection and Ray trace
elif use_transparency:
fw('illum 9\n') # 'Glass' transparency and no Ray trace reflection... fuzzy matching, but...
else:
fw('illum 2\n') # light normally
#### And now, the image textures...
image_map = {
"map_Kd": "base_color_texture",
"map_Ka": None, # ambient...
"map_Ks": "specular_texture",
"map_Ns": "roughness_texture",
"map_d": "alpha_texture",
"map_Tr": None, # transmission roughness?
"map_Bump": "normalmap_texture",
"disp": None, # displacement...
"refl": "metallic_texture",
"map_Ke": None # emission...
}
for key, mat_wrap_key in sorted(image_map.items()):
if mat_wrap_key is None:
continue
tex_wrap = getattr(mat_wrap, mat_wrap_key, None)
if tex_wrap is None:
continue
image = tex_wrap.image
if image is None:
continue
filepath = io_utils.path_reference(image.filepath, source_dir, dest_dir,
path_mode, "", copy_set, image.library)
options = []
if key == "map_Bump":
if mat_wrap.normalmap_strength != 1.0:
options.append('-bm %.6f' % mat_wrap.normalmap_strength)
if tex_wrap.translation != Vector((0.0, 0.0, 0.0)):
options.append('-o %.6f %.6f %.6f' % tex_wrap.translation[:])
if tex_wrap.scale != Vector((1.0, 1.0, 1.0)):
options.append('-s %.6f %.6f %.6f' % tex_wrap.scale[:])
if options:
fw('%s %s %s\n' % (key, " ".join(options), repr(filepath)[1:-1]))
else:
fw('%s %s\n' % (key, repr(filepath)[1:-1]))
else:
# Write a dummy material here?
fw('Ns 500\n')
fw('Ka 0.8 0.8 0.8\n') # Ambient
fw('Kd 0.8 0.8 0.8\n') # Diffuse
fw('Ks 0.8 0.8 0.8\n') # Specular
fw('d 1\n') # No alpha
fw('illum 2\n') # light normally
# After closing file
io_utils.path_reference_copy(copy_set)

View File

@@ -385,21 +385,21 @@ def km_user_interface(params):
items.extend([
# Eyedroppers all have the same event, and pass it through until
# a suitable eyedropper handles it.
("ui.eyedropper_color", {"type": 'E', "value": 'PRESS'}, None),
("ui.eyedropper_colorramp", {"type": 'E', "value": 'PRESS'}, None),
("ui.eyedropper_colorramp_point", {"type": 'E', "value": 'PRESS', "alt": True}, None),
("ui.eyedropper_id", {"type": 'E', "value": 'PRESS'}, None),
("ui.eyedropper_depth", {"type": 'E', "value": 'PRESS'}, None),
("ui.eyedropper_color", {"type": 'I', "value": 'PRESS'}, None),
("ui.eyedropper_colorramp", {"type": 'I', "value": 'PRESS'}, None),
("ui.eyedropper_colorramp_point", {"type": 'I', "value": 'PRESS', "alt": True}, None),
("ui.eyedropper_id", {"type": 'I', "value": 'PRESS'}, None),
("ui.eyedropper_depth", {"type": 'I', "value": 'PRESS'}, None),
# Copy data path
("ui.copy_data_path_button", {"type": 'C', "value": 'PRESS', "shift": True, "ctrl": True}, None),
("ui.copy_data_path_button", {"type": 'C', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True},
("ui.copy_data_path_button", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
("ui.copy_data_path_button", {"type": 'C', "value": 'PRESS', "ctrl": True, "alt": True},
{"properties": [("full_path", True)]}),
# rames and drivers
("anim.keyframe_insert_button", {"type": 'S', "value": 'PRESS'}, None),
("anim.keyframe_delete_button", {"type": 'S', "value": 'PRESS', "alt": True}, None),
("anim.keyframe_clear_button", {"type": 'S', "value": 'PRESS', "shift": True, "alt": True}, None),
("anim.driver_button_add", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
("anim.driver_button_remove", {"type": 'D', "value": 'PRESS', "ctrl": True, "alt": True}, None),
("anim.driver_button_add", {"type": 'D', "value": 'PRESS'}, None),
("anim.driver_button_remove", {"type": 'D', "value": 'PRESS', "alt": True}, None),
("anim.keyingset_button_add", {"type": 'K', "value": 'PRESS'}, None),
("anim.keyingset_button_remove", {"type": 'K', "value": 'PRESS', "alt": True}, None),
])
@@ -979,6 +979,7 @@ def km_image(params):
op_tool("builtin.transform", {"type": 'R', "value": 'PRESS'}),
op_tool("builtin.cursor", {"type": 'C', "value": 'PRESS'}),
op_tool("builtin.annotate", {"type": 'D', "value": 'PRESS'}),
op_tool("builtin.sample", {"type": 'I', "value": 'PRESS'}),
])
@@ -2827,6 +2828,7 @@ def km_image_paint(params):
{"properties": [("mode", 'NORMAL')]}),
("paint.image_paint", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
{"properties": [("mode", 'INVERT')]}),
("paint.sample_color", {"type": 'I', "value": 'PRESS'}, None),
("paint.brush_colors_flip", {"type": 'X', "value": 'PRESS'}, None),
("paint.grab_clone", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None),
("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS'},
@@ -2851,8 +2853,6 @@ def km_image_paint(params):
("wm.context_toggle", {"type": 'S', "value": 'PRESS', "shift": True},
{"properties": [("data_path", 'tool_settings.image_paint.brush.use_smooth_stroke')]}),
op_menu("VIEW3D_MT_angle_control", {"type": 'R', "value": 'PRESS'}),
("wm.context_menu_enum", {"type": 'E', "value": 'PRESS'},
{"properties": [("data_path", 'tool_settings.image_paint.brush.stroke_method')]}),
op_panel("VIEW3D_PT_paint_texture_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
])
@@ -2895,8 +2895,6 @@ def km_vertex_paint(params):
("wm.context_toggle", {"type": 'S', "value": 'PRESS', "shift": True},
{"properties": [("data_path", 'tool_settings.vertex_paint.brush.use_smooth_stroke')]}),
op_menu("VIEW3D_MT_angle_control", {"type": 'R', "value": 'PRESS'}),
("wm.context_menu_enum", {"type": 'E', "value": 'PRESS'},
{"properties": [("data_path", 'tool_settings.vertex_paint.brush.stroke_method')]}),
op_panel("VIEW3D_PT_paint_vertex_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
])
@@ -2913,15 +2911,13 @@ def km_weight_paint(params):
items.extend([
("paint.weight_paint", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
("paint.weight_sample", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True}, None),
("paint.weight_sample", {"type": 'I', "value": 'PRESS'}, None),
("paint.weight_sample_group", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, None),
("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS'},
{"properties": [("scalar", 0.9)]}),
("brush.scale_size", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
{"properties": [("scalar", 1.0 / 0.9)]}),
*_template_paint_radial_control("weight_paint"),
("wm.context_menu_enum", {"type": 'E', "value": 'PRESS'},
{"properties": [("data_path", 'tool_settings.vertex_paint.brush.stroke_method')]}),
("wm.context_toggle", {"type": 'M', "value": 'PRESS'},
{"properties": [("data_path", 'weight_paint_object.data.use_paint_mask')]}),
("wm.context_toggle", {"type": 'V', "value": 'PRESS'},

View File

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

View File

@@ -1781,7 +1781,7 @@ class VIEW3D_PT_tools_grease_pencil_brush_option(View3DPanel, Panel):
col.prop(gp_settings, "angle_factor", text="Factor", slider=True)
ob = context.object
if ob:
if ob and brush.gpencil_settings.use_material_pin is False:
ma = ob.active_material
elif brush.gpencil_settings.material:
ma = brush.gpencil_settings.material

View File

@@ -480,7 +480,6 @@ void BKE_mesh_calc_poly_center(const struct MPoly *mpoly,
float BKE_mesh_calc_poly_area(const struct MPoly *mpoly,
const struct MLoop *loopstart,
const struct MVert *mvarray);
float BKE_mesh_calc_poly_uv_area(const struct MPoly *mpoly, const struct MLoopUV *uv_array);
void BKE_mesh_calc_poly_angles(const struct MPoly *mpoly,
const struct MLoop *loopstart,
const struct MVert *mvarray,

View File

@@ -1,3 +1,4 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -85,6 +86,7 @@ void BKE_object_data_transfer_dttypes_to_cdmask(const int dtdata_types,
}
else if (cddata_type == CD_FAKE_LNOR) {
r_data_masks->vmask |= CD_MASK_NORMAL;
r_data_masks->pmask |= CD_MASK_NORMAL;
r_data_masks->lmask |= CD_MASK_NORMAL | CD_MASK_CUSTOMLOOPNORMAL;
}
}

View File

@@ -2378,24 +2378,6 @@ float BKE_mesh_calc_poly_area(const MPoly *mpoly, const MLoop *loopstart, const
}
}
float BKE_mesh_calc_poly_uv_area(const MPoly *mpoly, const MLoopUV *uv_array)
{
int i, l_iter = mpoly->loopstart;
float area;
float(*vertexcos)[2] = BLI_array_alloca(vertexcos, (size_t)mpoly->totloop);
/* pack vertex cos into an array for area_poly_v2 */
for (i = 0; i < mpoly->totloop; i++, l_iter++) {
copy_v2_v2(vertexcos[i], uv_array[l_iter].uv);
}
/* finally calculate the area */
area = area_poly_v2((const float(*)[2])vertexcos, (unsigned int)mpoly->totloop);
return area;
}
/**
* Calculate the volume and volume-weighted centroid of the volume
* formed by the polygon and the origin.

View File

@@ -3201,7 +3201,7 @@ static BoundVert *pipe_test(BevVert *bv)
/* check face planes: all should have normals perpendicular to epipe */
for (e = &bv->edges[0]; e != &bv->edges[bv->edgecount]; e++) {
if (e->fnext) {
if (dot_v3v3(dir1, e->fnext->no) > BEVEL_EPSILON_BIG) {
if (fabsf(dot_v3v3(dir1, e->fnext->no)) > BEVEL_EPSILON_BIG) {
return NULL;
}
}

View File

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

View File

@@ -52,7 +52,6 @@ set(SRC
intern/draw_anim_viz.c
intern/draw_armature.c
intern/draw_cache.c
intern/draw_cache_extract_mesh.c
intern/draw_cache_impl_curve.c
intern/draw_cache_impl_displist.c
intern/draw_cache_impl_lattice.c
@@ -131,7 +130,6 @@ set(SRC
DRW_engine.h
intern/DRW_render.h
intern/draw_cache.h
intern/draw_cache_extract.h
intern/draw_cache_impl.h
intern/draw_cache_inline.h
intern/draw_common.h

View File

@@ -4033,7 +4033,7 @@ void drw_batch_cache_validate(Object *ob)
void drw_batch_cache_generate_requested(Object *ob)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene = draw_ctx->scene;
const ToolSettings *ts = draw_ctx->scene->toolsettings;
const enum eContextObjectMode mode = CTX_data_mode_enum_ex(
draw_ctx->object_edit, draw_ctx->obact, draw_ctx->object_mode);
const bool is_paint_mode = ELEM(
@@ -4047,13 +4047,13 @@ void drw_batch_cache_generate_requested(Object *ob)
struct Mesh *mesh_eval = ob->runtime.mesh_eval;
switch (ob->type) {
case OB_MESH:
DRW_mesh_batch_cache_create_requested(ob, (Mesh *)ob->data, scene, is_paint_mode, use_hide);
DRW_mesh_batch_cache_create_requested(ob, (Mesh *)ob->data, ts, is_paint_mode, use_hide);
break;
case OB_CURVE:
case OB_FONT:
case OB_SURF:
if (mesh_eval) {
DRW_mesh_batch_cache_create_requested(ob, mesh_eval, scene, is_paint_mode, use_hide);
DRW_mesh_batch_cache_create_requested(ob, mesh_eval, ts, is_paint_mode, use_hide);
}
DRW_curve_batch_cache_create_requested(ob);
break;

View File

@@ -1,244 +0,0 @@
/*
* 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.
*
* Copyright 2019, Blender Foundation.
*/
/** \file
* \ingroup draw
*/
#ifndef __DRAW_CACHE_EXTRACT_MESH_H__
#define __DRAW_CACHE_EXTRACT_MESH_H__
/* Vertex Group Selection and display options */
typedef struct DRW_MeshWeightState {
int defgroup_active;
int defgroup_len;
short flags;
char alert_mode;
/* Set of all selected bones for Multipaint. */
bool *defgroup_sel; /* [defgroup_len] */
int defgroup_sel_count;
} DRW_MeshWeightState;
/* DRW_MeshWeightState.flags */
enum {
DRW_MESH_WEIGHT_STATE_MULTIPAINT = (1 << 0),
DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE = (1 << 1),
};
typedef struct DRW_MeshCDMask {
uint32_t uv : 8;
uint32_t tan : 8;
uint32_t vcol : 8;
uint32_t orco : 1;
uint32_t tan_orco : 1;
} DRW_MeshCDMask;
typedef enum eMRIterType {
MR_ITER_LOOPTRI = 1 << 0,
MR_ITER_LOOP = 1 << 1,
MR_ITER_LEDGE = 1 << 2,
MR_ITER_LVERT = 1 << 3,
} eMRIterType;
typedef enum eMRDataType {
MR_DATA_POLY_NOR = 1 << 1,
MR_DATA_LOOP_NOR = 1 << 2,
MR_DATA_LOOPTRI = 1 << 3,
} eMRDataType;
typedef enum eMRExtractType {
MR_EXTRACT_BMESH,
MR_EXTRACT_MAPPED,
MR_EXTRACT_MESH,
} eMRExtractType;
BLI_INLINE int mesh_render_mat_len_get(Mesh *me)
{
return MAX2(1, me->totcol);
}
typedef struct MeshBufferCache {
/* Every VBO below contains at least enough
* data for every loops in the mesh (except fdots).
* For some VBOs, it extends to (in this exact order) :
* loops + loose_edges*2 + loose_verts */
struct {
GPUVertBuf *pos_nor; /* extend */
GPUVertBuf *lnor; /* extend */
GPUVertBuf *edge_fac; /* extend */
GPUVertBuf *weights; /* extend */
GPUVertBuf *uv_tan;
GPUVertBuf *vcol;
GPUVertBuf *orco;
/* Only for edit mode. */
GPUVertBuf *edit_data; /* extend */
GPUVertBuf *edituv_data;
GPUVertBuf *stretch_area;
GPUVertBuf *stretch_angle;
GPUVertBuf *mesh_analysis;
GPUVertBuf *fdots_pos;
GPUVertBuf *fdots_nor;
GPUVertBuf *fdots_uv;
// GPUVertBuf *fdots_edit_data; /* inside fdots_nor for now. */
GPUVertBuf *fdots_edituv_data;
/* Selection */
GPUVertBuf *vert_idx; /* extend */
GPUVertBuf *edge_idx; /* extend */
GPUVertBuf *poly_idx;
GPUVertBuf *fdot_idx;
} vbo;
/* Index Buffers:
* Only need to be updated when topology changes. */
struct {
/* Indices to vloops. */
GPUIndexBuf *tris; /* Ordered per material. */
GPUIndexBuf *lines; /* Loose edges last. */
GPUIndexBuf *points;
GPUIndexBuf *fdots;
/* 3D overlays. */
GPUIndexBuf *lines_paint_mask; /* no loose edges. */
GPUIndexBuf *lines_adjacency;
/* Uv overlays. (visibility can differ from 3D view) */
GPUIndexBuf *edituv_tris;
GPUIndexBuf *edituv_lines;
GPUIndexBuf *edituv_points;
GPUIndexBuf *edituv_fdots;
} ibo;
} MeshBufferCache;
typedef enum DRWBatchFlag {
MBC_SURFACE = (1 << 0),
MBC_SURFACE_WEIGHTS = (1 << 1),
MBC_EDIT_TRIANGLES = (1 << 2),
MBC_EDIT_VERTICES = (1 << 3),
MBC_EDIT_EDGES = (1 << 4),
MBC_EDIT_VNOR = (1 << 5),
MBC_EDIT_LNOR = (1 << 6),
MBC_EDIT_FACEDOTS = (1 << 7),
MBC_EDIT_MESH_ANALYSIS = (1 << 8),
MBC_EDITUV_FACES_STRECH_AREA = (1 << 9),
MBC_EDITUV_FACES_STRECH_ANGLE = (1 << 10),
MBC_EDITUV_FACES = (1 << 11),
MBC_EDITUV_EDGES = (1 << 12),
MBC_EDITUV_VERTS = (1 << 13),
MBC_EDITUV_FACEDOTS = (1 << 14),
MBC_EDIT_SELECTION_VERTS = (1 << 15),
MBC_EDIT_SELECTION_EDGES = (1 << 16),
MBC_EDIT_SELECTION_FACES = (1 << 17),
MBC_EDIT_SELECTION_FACEDOTS = (1 << 18),
MBC_ALL_VERTS = (1 << 19),
MBC_ALL_EDGES = (1 << 20),
MBC_LOOSE_EDGES = (1 << 21),
MBC_EDGE_DETECTION = (1 << 22),
MBC_WIRE_EDGES = (1 << 23),
MBC_WIRE_LOOPS = (1 << 24),
MBC_WIRE_LOOPS_UVS = (1 << 25),
MBC_SURF_PER_MAT = (1 << 26),
} DRWBatchFlag;
#define MBC_EDITUV \
(MBC_EDITUV_FACES_STRECH_AREA | MBC_EDITUV_FACES_STRECH_ANGLE | MBC_EDITUV_FACES | \
MBC_EDITUV_EDGES | MBC_EDITUV_VERTS | MBC_EDITUV_FACEDOTS | MBC_WIRE_LOOPS_UVS)
#define FOREACH_MESH_BUFFER_CACHE(batch_cache, mbc) \
for (MeshBufferCache *mbc = &batch_cache->final; \
mbc == &batch_cache->final || mbc == &batch_cache->cage; \
mbc = (mbc == &batch_cache->final) ? &batch_cache->cage : NULL)
typedef struct MeshBatchCache {
MeshBufferCache final, cage;
struct {
/* Surfaces / Render */
GPUBatch *surface;
GPUBatch *surface_weights;
/* Edit mode */
GPUBatch *edit_triangles;
GPUBatch *edit_vertices;
GPUBatch *edit_edges;
GPUBatch *edit_vnor;
GPUBatch *edit_lnor;
GPUBatch *edit_fdots;
GPUBatch *edit_mesh_analysis;
/* Edit UVs */
GPUBatch *edituv_faces_strech_area;
GPUBatch *edituv_faces_strech_angle;
GPUBatch *edituv_faces;
GPUBatch *edituv_edges;
GPUBatch *edituv_verts;
GPUBatch *edituv_fdots;
/* Edit selection */
GPUBatch *edit_selection_verts;
GPUBatch *edit_selection_edges;
GPUBatch *edit_selection_faces;
GPUBatch *edit_selection_fdots;
/* Common display / Other */
GPUBatch *all_verts;
GPUBatch *all_edges;
GPUBatch *loose_edges;
GPUBatch *edge_detection;
GPUBatch *wire_edges; /* Individual edges with face normals. */
GPUBatch *wire_loops; /* Loops around faces. no edges between selected faces */
GPUBatch *wire_loops_uvs; /* Same as wire_loops but only has uvs. */
} batch;
GPUBatch **surface_per_mat;
/* arrays of bool uniform names (and value) that will be use to
* set srgb conversion for auto attributes.*/
char *auto_layer_names;
int *auto_layer_is_srgb;
int auto_layer_len;
DRWBatchFlag batch_requested;
DRWBatchFlag batch_ready;
/* settings to determine if cache is invalid */
int edge_len;
int tri_len;
int poly_len;
int vert_len;
int mat_len;
bool is_dirty; /* Instantly invalidates cache, skipping mesh check */
bool is_editmode;
bool is_uvsyncsel;
struct DRW_MeshWeightState weight_state;
DRW_MeshCDMask cd_used, cd_needed, cd_used_over_time;
int lastmatch;
/* Valid only if edge_detection is up to date. */
bool is_manifold;
bool no_loose_wire;
} MeshBatchCache;
void mesh_buffer_cache_create_requested(MeshBatchCache *cache,
MeshBufferCache mbc,
Mesh *me,
const bool do_final,
const bool use_subsurf_fdots,
const DRW_MeshCDMask *cd_layer_used,
const ToolSettings *ts,
const bool use_hide);
#endif /* __DRAW_CACHE_EXTRACT_MESH_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -119,7 +119,7 @@ struct GPUBatch *DRW_lattice_batch_cache_get_edit_verts(struct Lattice *lt);
/* Mesh */
void DRW_mesh_batch_cache_create_requested(struct Object *ob,
struct Mesh *me,
const struct Scene *scene,
const struct ToolSettings *ts,
const bool is_paint_mode,
const bool use_hide);
@@ -143,7 +143,6 @@ struct GPUBatch *DRW_mesh_batch_cache_get_surface_weights(struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_edit_triangles(struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_edit_vertices(struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_edit_edges(struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_edit_vnors(struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_edit_lnors(struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_edit_facedots(struct Mesh *me);
/* edit-mesh selection */

File diff suppressed because it is too large Load Diff

View File

@@ -2772,7 +2772,7 @@ void DRW_draw_select_id_object(Scene *scene,
if (use_faceselect && draw_facedot) {
geom_facedots = DRW_mesh_batch_cache_get_facedots_with_select_id(me);
}
DRW_mesh_batch_cache_create_requested(ob, me, scene, false, true);
DRW_mesh_batch_cache_create_requested(ob, me, NULL, false, true);
draw_mesh_face(geom_faces, initial_offset, use_faceselect, world_clip_planes);
@@ -2824,7 +2824,7 @@ void DRW_draw_select_id_object(Scene *scene,
((ob->mode & OB_MODE_WEIGHT_PAINT) || (ob->mode & OB_MODE_VERTEX_PAINT))) {
GPUBatch *geom_verts = DRW_mesh_batch_cache_get_verts_with_select_id(me_eval);
DRW_mesh_batch_cache_create_requested(ob, me_eval, scene, false, true);
DRW_mesh_batch_cache_create_requested(ob, me_eval, NULL, false, true);
/* Only draw faces to mask out verts, we don't want their selection ID's. */
draw_mesh_face(geom_faces, 0, false, world_clip_planes);
@@ -2835,7 +2835,7 @@ void DRW_draw_select_id_object(Scene *scene,
}
else {
const bool use_hide = (me_orig->editflag & ME_EDIT_PAINT_FACE_SEL);
DRW_mesh_batch_cache_create_requested(ob, me_eval, scene, false, use_hide);
DRW_mesh_batch_cache_create_requested(ob, me_eval, NULL, false, use_hide);
draw_mesh_face(geom_faces, initial_offset, true, world_clip_planes);

View File

@@ -727,7 +727,7 @@ static void EDIT_MESH_cache_populate(void *vedata, Object *ob)
}
if (vnormals_do) {
geom = DRW_mesh_batch_cache_get_edit_vnors(ob->data);
geom = DRW_mesh_batch_cache_get_edit_vertices(ob->data);
DRW_shgroup_call_no_cull(g_data->vnormals_shgrp, geom, ob);
}
if (lnormals_do) {

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,42 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Hugo Sales
*
* ***** END GPL LICENSE BLOCK *****
*/
/* #ifndef __IO_OBJ_H__ */
/* #define __IO_OBJ_H__ */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include "../io_common.h"
struct bContext;
bool OBJ_export(bContext *C, ExportSettings *const settings);
bool OBJ_import(bContext *C, ImportSettings *const settings);
#ifdef __cplusplus
}
#endif /* __cplusplus */
/* #endif /\* __IO_OBJ_H__ *\/ */

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,40 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Hugo Sales
*
* ***** END GPL LICENSE BLOCK *****
*/
/* #ifndef __IO_STL_H__ */
/* #define __IO_STL_H__ */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include "../io_common.h"
struct bContext;
bool STL_export(bContext *C, ExportSettings *const settings);
#ifdef __cplusplus
}
#endif /* __cplusplus */
/* #endif /\* __IO_STL_H__ *\/ */

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,78 @@
/* * ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2016 Blender Foundation.
* All rights reserved.
*
* ***** END GPL LICENSE BLOCK *****
*
*/
#ifndef __IO_OBJ_H__
#define __IO_OBJ_H__
struct wmOperatorType;
/* #include "WM_types.h" */
#include "io_common.h"
#ifdef __cplusplus
extern "C" {
#endif
enum path_reference_mode { AUTO, ABSOLUTE, RELATIVE, MATCH, STRIP, COPY };
enum split_mode { SPLIT_ON, SPLIT_OFF };
extern const EnumPropertyItem path_reference_mode[];
extern const EnumPropertyItem split_mode[];
typedef struct OBJExportSettings {
float start_frame;
float end_frame;
enum path_reference_mode path_mode;
bool export_animations;
bool export_objects_as_objects;
bool export_objects_as_groups;
bool export_face_sets;
bool dedup_normals;
bool dedup_uvs;
float dedup_normals_threshold;
float dedup_uvs_threshold;
bool export_smooth_groups;
bool smooth_groups_bitflags;
} OBJExportSettings;
typedef struct OBJImportSettings {
bool import_smooth_groups;
bool import_edges;
enum split_mode split_mode;
bool split_by_object;
bool split_by_groups;
bool import_vertex_groups;
bool use_image_search;
} OBJImportSettings;
void WM_OT_obj_export(struct wmOperatorType *ot);
void WM_OT_obj_import(struct wmOperatorType *ot);
#ifdef __cplusplus
}
#endif
#endif /* __IO_OBJ_H__ */

View File

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

View File

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

View File

@@ -0,0 +1,49 @@
/* * ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2016 Blender Foundation.
* All rights reserved.
*
* ***** END GPL LICENSE BLOCK *****
*
*/
#ifndef __IO_STL_H__
#define __IO_STL_H__
struct wmOperatorType;
/* #include "WM_types.h" */
#include "io_common.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct STLExportSettings {
bool use_ascii;
} STLExportSettings;
void WM_OT_stl_export(struct wmOperatorType *ot);
void WM_OT_stl_import(struct wmOperatorType *ot);
#ifdef __cplusplus
}
#endif
#endif /* __IO_STL_H__ */

View File

@@ -488,7 +488,6 @@ static bool uv_edge_compare(const void *a, const void *b)
static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wmEvent *event)
{
Depsgraph *depsgraph = CTX_data_depsgraph(C);
Scene *scene = CTX_data_scene(C);
Object *obedit = CTX_data_edit_object(C);
ToolSettings *ts = scene->toolsettings;
@@ -555,7 +554,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
UvElement *element;
UvNearestHit hit = UV_NEAREST_HIT_INIT;
Image *ima = CTX_data_edit_image(C);
uv_find_nearest_vert(depsgraph, scene, ima, obedit, co, 0.0f, &hit);
uv_find_nearest_vert(scene, ima, obedit, co, 0.0f, &hit);
element = BM_uv_element_get(data->elementMap, hit.efa, hit.l);
island_index = element->island;

View File

@@ -163,13 +163,13 @@ void ED_image_draw_cursor(ARegion *ar, const float cursor[2])
static void uvedit_get_batches(Object *ob,
SpaceImage *sima,
const Scene *scene,
const ToolSettings *ts,
GPUBatch **faces,
GPUBatch **edges,
GPUBatch **verts,
GPUBatch **facedots)
{
int drawfaces = draw_uvs_face_check(scene->toolsettings);
int drawfaces = draw_uvs_face_check(ts);
const bool draw_stretch = (sima->flag & SI_DRAW_STRETCH) != 0;
const bool draw_faces = (sima->flag & SI_NO_DRAWFACES) == 0;
@@ -197,7 +197,7 @@ static void uvedit_get_batches(Object *ob,
*faces = NULL;
}
DRW_mesh_batch_cache_create_requested(ob, ob->data, scene, false, false);
DRW_mesh_batch_cache_create_requested(ob, ob->data, ts, false, false);
}
static void draw_uvs_shadow(SpaceImage *UNUSED(sima),
@@ -212,7 +212,7 @@ static void draw_uvs_shadow(SpaceImage *UNUSED(sima),
DRW_mesh_batch_cache_validate(me);
GPUBatch *edges = DRW_mesh_batch_cache_get_uv_edges(me);
DRW_mesh_batch_cache_create_requested(eval_ob, me, scene, false, false);
DRW_mesh_batch_cache_create_requested(eval_ob, me, scene->toolsettings, false, false);
if (edges) {
GPU_batch_program_set_builtin(edges, GPU_SHADER_2D_UV_UNIFORM_COLOR);
@@ -235,7 +235,7 @@ static void draw_uvs_texpaint(Scene *scene, Object *ob, Depsgraph *depsgraph)
DRW_mesh_batch_cache_validate(me);
GPUBatch *geom = DRW_mesh_batch_cache_get_uv_edges(me);
DRW_mesh_batch_cache_create_requested(eval_ob, me, scene, false, false);
DRW_mesh_batch_cache_create_requested(eval_ob, me, scene->toolsettings, false, false);
GPU_batch_program_set_builtin(geom, GPU_SHADER_2D_UV_UNIFORM_COLOR);
GPU_batch_uniform_4fv(geom, "color", col);
@@ -300,7 +300,7 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit, Depsgraph *
}
}
uvedit_get_batches(eval_ob, sima, scene, &faces, &edges, &verts, &facedots);
uvedit_get_batches(eval_ob, sima, ts, &faces, &edges, &verts, &facedots);
bool interpedges;
bool draw_stretch = (sima->flag & SI_DRAW_STRETCH) != 0;

View File

@@ -57,15 +57,13 @@ typedef struct UvNearestHit {
.dist_sq = FLT_MAX, \
}
bool uv_find_nearest_vert(struct Depsgraph *depsgraph,
struct Scene *scene,
bool uv_find_nearest_vert(struct Scene *scene,
struct Image *ima,
struct Object *obedit,
const float co[2],
const float penalty_dist,
struct UvNearestHit *hit_final);
bool uv_find_nearest_vert_multi(struct Depsgraph *depsgraph,
struct Scene *scene,
bool uv_find_nearest_vert_multi(struct Scene *scene,
struct Image *ima,
struct Object **objects,
const uint objects_len,
@@ -73,28 +71,24 @@ bool uv_find_nearest_vert_multi(struct Depsgraph *depsgraph,
const float penalty_dist,
struct UvNearestHit *hit_final);
bool uv_find_nearest_edge(struct Depsgraph *depsgraph,
struct Scene *scene,
bool uv_find_nearest_edge(struct Scene *scene,
struct Image *ima,
struct Object *obedit,
const float co[2],
struct UvNearestHit *hit_final);
bool uv_find_nearest_edge_multi(struct Depsgraph *depsgraph,
struct Scene *scene,
bool uv_find_nearest_edge_multi(struct Scene *scene,
struct Image *ima,
struct Object **objects,
const uint objects_len,
const float co[2],
struct UvNearestHit *hit_final);
bool uv_find_nearest_face(struct Depsgraph *depsgraph,
struct Scene *scene,
bool uv_find_nearest_face(struct Scene *scene,
struct Image *ima,
struct Object *obedit,
const float co[2],
struct UvNearestHit *hit_final);
bool uv_find_nearest_face_multi(struct Depsgraph *depsgraph,
struct Scene *scene,
bool uv_find_nearest_face_multi(struct Scene *scene,
struct Image *ima,
struct Object **objects,
const uint objects_len,

View File

@@ -56,7 +56,6 @@
#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_modifier.h"
#include "BKE_node.h"
#include "BKE_report.h"
#include "BKE_scene.h"
@@ -607,18 +606,6 @@ void uv_poly_center(BMFace *f, float r_cent[2], const int cd_loop_uv_offset)
mul_v2_fl(r_cent, 1.0f / (float)f->len);
}
static void uv_mpoly_center(const MPoly *mpoly, const MLoopUV *mloopuv, float r_cent[2])
{
zero_v2(r_cent);
for (int i = 0; i < mpoly->totloop; i++) {
const MLoopUV *luv = &mloopuv[mpoly->loopstart + i];
add_v2_v2(r_cent, luv->uv);
}
mul_v2_fl(r_cent, 1.0f / (float)mpoly->totloop);
}
void uv_poly_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float aspy, int len)
{
int i;
@@ -766,12 +753,8 @@ bool ED_uvedit_center(Scene *scene, Image *ima, Object *obedit, float cent[2], c
/** \name Find Nearest Elements
* \{ */
bool uv_find_nearest_edge(Depsgraph *depsgraph,
Scene *scene,
Image *ima,
Object *obedit,
const float co[2],
UvNearestHit *hit)
bool uv_find_nearest_edge(
Scene *scene, Image *ima, Object *obedit, const float co[2], UvNearestHit *hit)
{
BMEditMesh *em = BKE_editmesh_from_object(obedit);
BMFace *efa;
@@ -783,85 +766,35 @@ bool uv_find_nearest_edge(Depsgraph *depsgraph,
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
/* (fclem) Is it garanteed that obedit_eval will be updated? */
Object *obedit_eval = DEG_get_evaluated_object(depsgraph, obedit);
BMEditMesh *em_eval = BKE_editmesh_from_object(obedit_eval);
BM_mesh_elem_index_ensure(em->bm, BM_VERT);
if (!em_eval->mesh_eval_cage || em_eval->mesh_eval_cage->runtime.is_original) {
BM_mesh_elem_index_ensure(em->bm, BM_VERT);
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (!uvedit_face_visible_test(scene, obedit, ima, efa)) {
continue;
}
BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
const float dist_test_sq = dist_squared_to_line_segment_v2(co, luv->uv, luv_next->uv);
if (dist_test_sq < hit->dist_sq) {
hit->efa = efa;
hit->l = l;
hit->luv = luv;
hit->luv_next = luv_next;
hit->lindex = i;
hit->dist_sq = dist_test_sq;
found = true;
}
}
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (!uvedit_face_visible_test(scene, obedit, ima, efa)) {
continue;
}
}
else {
Mesh *me = em_eval->mesh_eval_cage;
BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
BM_mesh_elem_table_ensure(em->bm, BM_FACE | BM_EDGE);
const float dist_test_sq = dist_squared_to_line_segment_v2(co, luv->uv, luv_next->uv);
int *p_origindex = CustomData_get_layer(&me->pdata, CD_ORIGINDEX);
int *e_origindex = CustomData_get_layer(&me->edata, CD_ORIGINDEX);
if (dist_test_sq < hit->dist_sq) {
hit->efa = efa;
MLoopUV *mluv = CustomData_get_layer(&me->ldata, CD_MLOOPUV);
hit->l = l;
hit->luv = luv;
hit->luv_next = luv_next;
hit->lindex = i;
MPoly *mpoly = me->mpoly;
for (int p = 0; p < me->totpoly; p++, mpoly++) {
if (p_origindex[p] == ORIGINDEX_NONE) {
continue;
}
efa = BM_face_at_index(em->bm, p_origindex[p]);
if (!uvedit_face_visible_test(scene, obedit, ima, efa)) {
continue;
}
for (i = 0; i < mpoly->totloop; i++) {
MLoop *mloop = &me->mloop[mpoly->loopstart + i];
if (e_origindex[mloop->e] == ORIGINDEX_NONE) {
continue;
}
luv = &mluv[mpoly->loopstart + i];
luv_next = &mluv[mpoly->loopstart + ((i + 1) % mpoly->totloop)];
const float dist_test_sq = dist_squared_to_line_segment_v2(co, luv->uv, luv_next->uv);
if (dist_test_sq < hit->dist_sq) {
BMEdge *eed = BM_edge_at_index(em->bm, e_origindex[mloop->e]);
hit->efa = efa;
hit->l = BM_face_edge_share_loop(efa, eed);
hit->luv = luv;
hit->luv_next = luv_next;
hit->lindex = i;
hit->dist_sq = dist_test_sq;
found = true;
}
hit->dist_sq = dist_test_sq;
found = true;
}
}
}
return found;
}
bool uv_find_nearest_edge_multi(Depsgraph *depsgraph,
Scene *scene,
bool uv_find_nearest_edge_multi(Scene *scene,
Image *ima,
Object **objects,
const uint objects_len,
@@ -871,7 +804,7 @@ bool uv_find_nearest_edge_multi(Depsgraph *depsgraph,
bool found = false;
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
if (uv_find_nearest_edge(depsgraph, scene, ima, obedit, co, hit_final)) {
if (uv_find_nearest_edge(scene, ima, obedit, co, hit_final)) {
hit_final->ob = obedit;
found = true;
}
@@ -879,12 +812,8 @@ bool uv_find_nearest_edge_multi(Depsgraph *depsgraph,
return found;
}
bool uv_find_nearest_face(Depsgraph *depsgraph,
Scene *scene,
Image *ima,
Object *obedit,
const float co[2],
UvNearestHit *hit_final)
bool uv_find_nearest_face(
Scene *scene, Image *ima, Object *obedit, const float co[2], UvNearestHit *hit_final)
{
BMEditMesh *em = BKE_editmesh_from_object(obedit);
bool found = false;
@@ -894,94 +823,28 @@ bool uv_find_nearest_face(Depsgraph *depsgraph,
/* this will fill in hit.vert1 and hit.vert2 */
float dist_sq_init = hit_final->dist_sq;
UvNearestHit hit = *hit_final;
if (uv_find_nearest_edge(depsgraph, scene, ima, obedit, co, &hit)) {
if (uv_find_nearest_edge(scene, ima, obedit, co, &hit)) {
hit.dist_sq = dist_sq_init;
hit.l = NULL;
hit.luv = hit.luv_next = NULL;
/* (fclem) Is it garanteed that obedit_eval will be updated? */
Object *obedit_eval = DEG_get_evaluated_object(depsgraph, obedit);
BMEditMesh *em_eval = BKE_editmesh_from_object(obedit_eval);
BMIter iter;
BMFace *efa;
if (!em_eval->mesh_eval_cage || em_eval->mesh_eval_cage->runtime.is_original) {
BMIter iter;
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (!uvedit_face_visible_test(scene, obedit, ima, efa)) {
continue;
}
float cent[2];
uv_poly_center(efa, cent, cd_loop_uv_offset);
const float dist_test_sq = len_squared_v2v2(co, cent);
if (dist_test_sq < hit.dist_sq) {
hit.efa = efa;
hit.dist_sq = dist_test_sq;
found = true;
}
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (!uvedit_face_visible_test(scene, obedit, ima, efa)) {
continue;
}
}
else {
Mesh *me = em_eval->mesh_eval_cage;
BM_mesh_elem_table_ensure(em->bm, BM_FACE);
float cent[2];
uv_poly_center(efa, cent, cd_loop_uv_offset);
int *p_origindex = CustomData_get_layer(&me->pdata, CD_ORIGINDEX);
const float dist_test_sq = len_squared_v2v2(co, cent);
MLoopUV *mluv = CustomData_get_layer(&me->ldata, CD_MLOOPUV);
if (modifiers_usesSubsurfFacedots(scene, obedit)) {
const MPoly *mpoly = me->mpoly;
for (int p = 0; p < me->totpoly; p++, mpoly++) {
if (p_origindex[p] == ORIGINDEX_NONE) {
continue;
}
efa = BM_face_at_index(em->bm, p_origindex[p]);
if (!uvedit_face_visible_test(scene, obedit, ima, efa)) {
continue;
}
for (int i = 0; i < mpoly->totloop; i++) {
MLoop *mloop = &me->mloop[mpoly->loopstart + i];
MVert *mvert = &me->mvert[mloop->v];
if ((mvert->flag & ME_VERT_FACEDOT) == 0) {
continue;
}
const float dist_test_sq = len_squared_v2v2(co, mluv[mpoly->loopstart + i].uv);
if (dist_test_sq < hit.dist_sq) {
hit.efa = efa;
hit.dist_sq = dist_test_sq;
found = true;
}
}
}
}
else {
const MPoly *mpoly = me->mpoly;
for (int p = 0; p < me->totpoly; p++, mpoly++) {
if (p_origindex[p] == ORIGINDEX_NONE) {
continue;
}
efa = BM_face_at_index(em->bm, p_origindex[p]);
if (!uvedit_face_visible_test(scene, obedit, ima, efa)) {
continue;
}
float cent[2];
uv_mpoly_center(mpoly, mluv, cent);
const float dist_test_sq = len_squared_v2v2(co, cent);
if (dist_test_sq < hit.dist_sq) {
hit.efa = efa;
hit.dist_sq = dist_test_sq;
found = true;
}
}
if (dist_test_sq < hit.dist_sq) {
hit.efa = efa;
hit.dist_sq = dist_test_sq;
found = true;
}
}
}
@@ -991,8 +854,7 @@ bool uv_find_nearest_face(Depsgraph *depsgraph,
return found;
}
bool uv_find_nearest_face_multi(Depsgraph *depsgraph,
Scene *scene,
bool uv_find_nearest_face_multi(Scene *scene,
Image *ima,
Object **objects,
const uint objects_len,
@@ -1002,7 +864,7 @@ bool uv_find_nearest_face_multi(Depsgraph *depsgraph,
bool found = false;
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
if (uv_find_nearest_face(depsgraph, scene, ima, obedit, co, hit_final)) {
if (uv_find_nearest_face(scene, ima, obedit, co, hit_final)) {
hit_final->ob = obedit;
found = true;
}
@@ -1020,8 +882,7 @@ static bool uv_nearest_between(const BMLoop *l, const float co[2], const int cd_
(line_point_side_v2(uv_next, uv_curr, co) <= 0.0f));
}
bool uv_find_nearest_vert(Depsgraph *depsgraph,
Scene *scene,
bool uv_find_nearest_vert(Scene *scene,
Image *ima,
Object *obedit,
float const co[2],
@@ -1033,7 +894,7 @@ bool uv_find_nearest_vert(Depsgraph *depsgraph,
/* this will fill in hit.vert1 and hit.vert2 */
float dist_sq_init = hit_final->dist_sq;
UvNearestHit hit = *hit_final;
if (uv_find_nearest_edge(depsgraph, scene, ima, obedit, co, &hit)) {
if (uv_find_nearest_edge(scene, ima, obedit, co, &hit)) {
hit.dist_sq = dist_sq_init;
hit.l = NULL;
@@ -1041,112 +902,46 @@ bool uv_find_nearest_vert(Depsgraph *depsgraph,
BMEditMesh *em = BKE_editmesh_from_object(obedit);
BMFace *efa;
BMIter iter;
/* (fclem) Is it garanteed that obedit_eval will be updated? */
Object *obedit_eval = DEG_get_evaluated_object(depsgraph, obedit);
BMEditMesh *em_eval = BKE_editmesh_from_object(obedit_eval);
BM_mesh_elem_index_ensure(em->bm, BM_VERT);
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
if (!em_eval->mesh_eval_cage || em_eval->mesh_eval_cage->runtime.is_original) {
BMIter iter;
BM_mesh_elem_index_ensure(em->bm, BM_VERT);
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (!uvedit_face_visible_test(scene, obedit, ima, efa)) {
continue;
}
BMIter liter;
BMLoop *l;
int i;
BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
float dist_test_sq;
MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
if (penalty_dist != 0.0f && uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
dist_test_sq = len_v2v2(co, luv->uv) + penalty_dist;
dist_test_sq = SQUARE(dist_test_sq);
}
else {
dist_test_sq = len_squared_v2v2(co, luv->uv);
}
if (dist_test_sq <= hit.dist_sq) {
if (dist_test_sq == hit.dist_sq) {
if (!uv_nearest_between(l, co, cd_loop_uv_offset)) {
continue;
}
}
hit.dist_sq = dist_test_sq;
hit.l = l;
hit.luv = luv;
hit.luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
hit.efa = efa;
hit.lindex = i;
found = true;
}
}
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (!uvedit_face_visible_test(scene, obedit, ima, efa)) {
continue;
}
}
else {
Mesh *me = em_eval->mesh_eval_cage;
BM_mesh_elem_table_ensure(em->bm, BM_FACE | BM_EDGE);
int *p_origindex = CustomData_get_layer(&me->pdata, CD_ORIGINDEX);
int *e_origindex = CustomData_get_layer(&me->edata, CD_ORIGINDEX);
MLoopUV *mluv = CustomData_get_layer(&me->ldata, CD_MLOOPUV);
MPoly *mpoly = me->mpoly;
for (int p = 0; p < me->totpoly; p++, mpoly++) {
if (p_origindex[p] == ORIGINDEX_NONE) {
continue;
BMIter liter;
BMLoop *l;
int i;
BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
float dist_test_sq;
MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
if (penalty_dist != 0.0f && uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
dist_test_sq = len_v2v2(co, luv->uv) + penalty_dist;
dist_test_sq = SQUARE(dist_test_sq);
}
efa = BM_face_at_index(em->bm, p_origindex[p]);
if (!uvedit_face_visible_test(scene, obedit, ima, efa)) {
continue;
else {
dist_test_sq = len_squared_v2v2(co, luv->uv);
}
for (int i = 0; i < mpoly->totloop; i++) {
MLoop *mloop = &me->mloop[mpoly->loopstart + i];
if (e_origindex[mloop->e] == ORIGINDEX_NONE) {
continue;
}
MLoopUV *luv = &mluv[mpoly->loopstart + i];
MLoopUV *luv_next = &mluv[mpoly->loopstart + ((i + 1) % mpoly->totloop)];
BMEdge *eed = BM_edge_at_index(em->bm, e_origindex[mloop->e]);
BMLoop *l = BM_face_edge_share_loop(efa, eed);
float dist_test_sq;
if (penalty_dist != 0.0f && uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
dist_test_sq = len_v2v2(co, luv->uv) + penalty_dist;
dist_test_sq = SQUARE(dist_test_sq);
}
else {
dist_test_sq = len_squared_v2v2(co, luv->uv);
}
if (dist_test_sq <= hit.dist_sq) {
/* FIXME this is testing against original coord, not deformed cage. */
if (dist_test_sq == hit.dist_sq) {
if (!uv_nearest_between(l, co, cd_loop_uv_offset)) {
continue;
}
if (dist_test_sq <= hit.dist_sq) {
if (dist_test_sq == hit.dist_sq) {
if (!uv_nearest_between(l, co, cd_loop_uv_offset)) {
continue;
}
hit.dist_sq = dist_test_sq;
hit.l = l;
hit.luv = luv;
hit.luv_next = luv_next;
hit.efa = efa;
hit.lindex = i;
found = true;
}
hit.dist_sq = dist_test_sq;
hit.l = l;
hit.luv = luv;
hit.luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
hit.efa = efa;
hit.lindex = i;
found = true;
}
}
}
@@ -1159,8 +954,7 @@ bool uv_find_nearest_vert(Depsgraph *depsgraph,
return found;
}
bool uv_find_nearest_vert_multi(Depsgraph *depsgraph,
Scene *scene,
bool uv_find_nearest_vert_multi(Scene *scene,
Image *ima,
Object **objects,
const uint objects_len,
@@ -1171,7 +965,7 @@ bool uv_find_nearest_vert_multi(Depsgraph *depsgraph,
bool found = false;
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
if (uv_find_nearest_vert(depsgraph, scene, ima, obedit, co, penalty_dist, hit_final)) {
if (uv_find_nearest_vert(scene, ima, obedit, co, penalty_dist, hit_final)) {
hit_final->ob = obedit;
found = true;
}
@@ -1480,9 +1274,9 @@ static void uv_select_linked_multi(Scene *scene,
BM_mesh_elem_table_ensure(em->bm, BM_FACE); /* we can use this too */
/* Note, we had 'use winding' so we don't consider overlapping islands as connected, see
* T44320 this made *every* projection split the island into front/back islands. Keep
* 'use_winding' to false, see: T50970.
/* Note, we had 'use winding' so we don't consider overlapping islands as connected, see T44320
* this made *every* projection split the island into front/back islands.
* Keep 'use_winding' to false, see: T50970.
*
* Better solve this by having a delimit option for select-linked operator,
* keeping island-select working as is. */
@@ -2719,12 +2513,12 @@ static int uv_mouse_select_multi(bContext *C,
/* find nearest element */
if (loop) {
/* find edge */
found_item = uv_find_nearest_edge_multi(depsgraph, scene, ima, objects, objects_len, co, &hit);
found_item = uv_find_nearest_edge_multi(scene, ima, objects, objects_len, co, &hit);
}
else if (selectmode == UV_SELECT_VERTEX) {
/* find vertex */
found_item = uv_find_nearest_vert_multi(
depsgraph, scene, ima, objects, objects_len, co, penalty_dist, &hit);
scene, ima, objects, objects_len, co, penalty_dist, &hit);
found_item = found_item && (!deselect_all || hit.dist_sq < penalty_dist);
if (found_item) {
@@ -2741,7 +2535,7 @@ static int uv_mouse_select_multi(bContext *C,
}
else if (selectmode == UV_SELECT_EDGE) {
/* find edge */
found_item = uv_find_nearest_edge_multi(depsgraph, scene, ima, objects, objects_len, co, &hit);
found_item = uv_find_nearest_edge_multi(scene, ima, objects, objects_len, co, &hit);
found_item = found_item && (!deselect_all || hit.dist_sq < penalty_dist);
if (found_item) {
@@ -2760,7 +2554,7 @@ static int uv_mouse_select_multi(bContext *C,
}
else if (selectmode == UV_SELECT_FACE) {
/* find face */
found_item = uv_find_nearest_face_multi(depsgraph, scene, ima, objects, objects_len, co, &hit);
found_item = uv_find_nearest_face_multi(scene, ima, objects, objects_len, co, &hit);
found_item = found_item && (!deselect_all || hit.dist_sq < penalty_dist);
if (found_item) {
@@ -2784,7 +2578,7 @@ static int uv_mouse_select_multi(bContext *C,
}
}
else if (selectmode == UV_SELECT_ISLAND) {
found_item = uv_find_nearest_edge_multi(depsgraph, scene, ima, objects, objects_len, co, &hit);
found_item = uv_find_nearest_edge_multi(scene, ima, objects, objects_len, co, &hit);
found_item = found_item && (!deselect_all || hit.dist_sq < penalty_dist);
}
@@ -3098,7 +2892,6 @@ static void UV_OT_select_loop(wmOperatorType *ot)
static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent *event, bool pick)
{
Depsgraph *depsgraph = CTX_data_depsgraph(C);
SpaceImage *sima = CTX_wm_space_image(C);
Scene *scene = CTX_data_scene(C);
ToolSettings *ts = scene->toolsettings;
@@ -3143,7 +2936,7 @@ static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent
RNA_float_get_array(op->ptr, "location", co);
}
if (!uv_find_nearest_edge_multi(depsgraph, scene, ima, objects, objects_len, co, &hit)) {
if (!uv_find_nearest_edge_multi(scene, ima, objects, objects_len, co, &hit)) {
MEM_freeN(objects);
return OPERATOR_CANCELLED;
}

View File

@@ -2550,13 +2550,11 @@ static StitchState *stitch_select(bContext *C,
UvNearestHit hit = UV_NEAREST_HIT_INIT;
ARegion *ar = CTX_wm_region(C);
Image *ima = CTX_data_edit_image(C);
Depsgraph *depsgraph = CTX_data_depsgraph(C);
UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
if (ssc->mode == STITCH_VERT) {
if (uv_find_nearest_vert_multi(
depsgraph, scene, ima, ssc->objects, ssc->objects_len, co, 0.0f, &hit)) {
if (uv_find_nearest_vert_multi(scene, ima, ssc->objects, ssc->objects_len, co, 0.0f, &hit)) {
/* Add vertex to selection, deselect all common uv's of vert other than selected and
* update the preview. This behavior was decided so that you can do stuff like deselect
* the opposite stitchable vertex and the initial still gets deselected */
@@ -2577,8 +2575,7 @@ static StitchState *stitch_select(bContext *C,
return state;
}
}
else if (uv_find_nearest_edge_multi(
depsgraph, scene, ima, ssc->objects, ssc->objects_len, co, &hit)) {
else if (uv_find_nearest_edge_multi(scene, ima, ssc->objects, ssc->objects_len, co, &hit)) {
/* find StitchState from hit->ob */
StitchState *state = NULL;
for (uint ob_index = 0; ob_index < ssc->objects_len; ob_index++) {

View File

@@ -192,9 +192,8 @@ void gpencil_apply_modifier_material(
BLI_ghash_insert(gh_color, mat->id.name, newmat);
DEG_id_tag_update(&newmat->id, ID_RECALC_COPY_ON_WRITE);
}
/* Reaasign color index. */
int idx = BKE_gpencil_object_material_get_index(ob, newmat);
gps->mat_nr = idx - 1;
/* Reassign color index. */
gps->mat_nr = BKE_gpencil_object_material_get_index(ob, newmat);
}
else {
/* reuse existing color (but update only first time) */

View File

@@ -40,7 +40,7 @@ typedef enum {
GPU_BATCH_READY_TO_DRAW,
} GPUBatchPhase;
#define GPU_BATCH_VBO_MAX_LEN 5
#define GPU_BATCH_VBO_MAX_LEN 4
#define GPU_BATCH_VAO_STATIC_LEN 3
#define GPU_BATCH_VAO_DYN_ALLOC_COUNT 16
@@ -114,7 +114,6 @@ void GPU_batch_vao_cache_clear(GPUBatch *);
void GPU_batch_callback_free_set(GPUBatch *, void (*callback)(GPUBatch *, void *), void *);
void GPU_batch_instbuf_set(GPUBatch *, GPUVertBuf *, bool own_vbo); /* Instancing */
void GPU_batch_elembuf_set(GPUBatch *batch, GPUIndexBuf *elem, bool own_ibo);
int GPU_batch_vertbuf_add_ex(GPUBatch *, GPUVertBuf *, bool own_vbo);

View File

@@ -36,19 +36,14 @@ typedef enum {
} GPUIndexBufType;
typedef struct GPUIndexBuf {
uint index_start;
uint index_len;
bool is_subrange;
#if GPU_TRACK_INDEX_RANGE
GPUIndexBufType index_type;
uint32_t gl_index_type;
uint base_index;
#endif
uint32_t ibo_id; /* 0 indicates not yet sent to VRAM */
union {
void *data; /* non-NULL indicates not yet sent to VRAM */
struct GPUIndexBuf *src; /* if is_subrange is true, this is the source buffer. */
};
void *data; /* non-NULL indicates not yet sent to VRAM */
} GPUIndexBuf;
void GPU_indexbuf_use(GPUIndexBuf *);
@@ -76,21 +71,9 @@ void GPU_indexbuf_add_line_verts(GPUIndexBufBuilder *, uint v1, uint v2);
void GPU_indexbuf_add_tri_verts(GPUIndexBufBuilder *, uint v1, uint v2, uint v3);
void GPU_indexbuf_add_line_adj_verts(GPUIndexBufBuilder *, uint v1, uint v2, uint v3, uint v4);
void GPU_indexbuf_set_point_vert(GPUIndexBufBuilder *builder, uint elem, uint v1);
void GPU_indexbuf_set_line_verts(GPUIndexBufBuilder *builder, uint elem, uint v1, uint v2);
void GPU_indexbuf_set_tri_verts(GPUIndexBufBuilder *builder, uint elem, uint v1, uint v2, uint v3);
/* Skip primitive rendering at the given index. */
void GPU_indexbuf_set_point_restart(GPUIndexBufBuilder *builder, uint elem);
void GPU_indexbuf_set_line_restart(GPUIndexBufBuilder *builder, uint elem);
void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem);
GPUIndexBuf *GPU_indexbuf_build(GPUIndexBufBuilder *);
void GPU_indexbuf_build_in_place(GPUIndexBufBuilder *, GPUIndexBuf *);
/* Create a subrange of an existing indexbuffer. */
GPUIndexBuf *GPU_indexbuf_create_subrange(GPUIndexBuf *ibo, uint start, uint length);
void GPU_indexbuf_discard(GPUIndexBuf *);
int GPU_indexbuf_primitive_len(GPUPrimType prim_type);

View File

@@ -31,7 +31,7 @@
#include "BLI_assert.h"
#define GPU_VERT_ATTR_MAX_LEN 16
#define GPU_VERT_ATTR_MAX_NAMES 6
#define GPU_VERT_ATTR_MAX_NAMES 5
#define GPU_VERT_ATTR_NAME_AVERAGE_LEN 11
#define GPU_VERT_ATTR_NAMES_BUF_LEN ((GPU_VERT_ATTR_NAME_AVERAGE_LEN + 1) * GPU_VERT_ATTR_MAX_LEN)
@@ -88,8 +88,6 @@ typedef struct GPUVertFormat {
uint packed : 1;
/** Current offset in names[]. */
uint name_offset : 8;
/** Store each attrib in one contiguous buffer region. */
uint deinterleaved : 1;
GPUVertAttr attrs[GPU_VERT_ATTR_MAX_LEN];
char names[GPU_VERT_ATTR_NAMES_BUF_LEN];
@@ -106,8 +104,6 @@ uint GPU_vertformat_attr_add(
GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode);
void GPU_vertformat_alias_add(GPUVertFormat *, const char *alias);
void GPU_vertformat_deinterleave(GPUVertFormat *format);
int GPU_vertformat_attr_id_get(const GPUVertFormat *, const char *name);
BLI_INLINE const char *GPU_vertformat_attr_name_get(const GPUVertFormat *format,
@@ -126,59 +122,7 @@ typedef struct GPUPackedNormal {
int w : 2; /* 0 by default, can manually set to { -2, -1, 0, 1 } */
} GPUPackedNormal;
/* OpenGL ES packs in a different order as desktop GL but component conversion is the same.
* Of the code here, only struct GPUPackedNormal needs to change. */
#define SIGNED_INT_10_MAX 511
#define SIGNED_INT_10_MIN -512
BLI_INLINE int clampi(int x, int min_allowed, int max_allowed)
{
#if TRUST_NO_ONE
assert(min_allowed <= max_allowed);
#endif
if (x < min_allowed) {
return min_allowed;
}
else if (x > max_allowed) {
return max_allowed;
}
else {
return x;
}
}
BLI_INLINE int quantize(float x)
{
int qx = x * 511.0f;
return clampi(qx, SIGNED_INT_10_MIN, SIGNED_INT_10_MAX);
}
BLI_INLINE int convert_i16(short x)
{
/* 16-bit signed --> 10-bit signed */
/* TODO: round? */
return x >> 6;
}
BLI_INLINE GPUPackedNormal GPU_normal_convert_i10_v3(const float data[3])
{
GPUPackedNormal n = {
.x = quantize(data[0]),
.y = quantize(data[1]),
.z = quantize(data[2]),
};
return n;
}
BLI_INLINE GPUPackedNormal GPU_normal_convert_i10_s3(const short data[3])
{
GPUPackedNormal n = {
.x = convert_i16(data[0]),
.y = convert_i16(data[1]),
.z = convert_i16(data[2]),
};
return n;
}
GPUPackedNormal GPU_normal_convert_i10_v3(const float data[3]);
GPUPackedNormal GPU_normal_convert_i10_s3(const short data[3]);
#endif /* __GPU_VERTEX_FORMAT_H__ */

View File

@@ -181,25 +181,6 @@ void GPU_batch_instbuf_set(GPUBatch *batch, GPUVertBuf *inst, bool own_vbo)
}
}
void GPU_batch_elembuf_set(GPUBatch *batch, GPUIndexBuf *elem, bool own_ibo)
{
BLI_assert(elem != NULL);
/* redo the bindings */
GPU_batch_vao_cache_clear(batch);
if (batch->elem != NULL && (batch->owns_flag & GPU_BATCH_OWNS_INDEX)) {
GPU_indexbuf_discard(batch->elem);
}
batch->elem = elem;
if (own_ibo) {
batch->owns_flag |= GPU_BATCH_OWNS_INDEX;
}
else {
batch->owns_flag &= ~GPU_BATCH_OWNS_INDEX;
}
}
/* Returns the index of verts in the batch. */
int GPU_batch_vertbuf_add_ex(GPUBatch *batch, GPUVertBuf *verts, bool own_vbo)
{
@@ -380,23 +361,13 @@ static void create_bindings(GPUVertBuf *verts,
const GPUVertFormat *format = &verts->format;
const uint attr_len = format->attr_len;
uint stride = format->stride;
uint offset = 0;
const uint stride = format->stride;
GPU_vertbuf_use(verts);
for (uint a_idx = 0; a_idx < attr_len; ++a_idx) {
const GPUVertAttr *a = &format->attrs[a_idx];
if (format->deinterleaved) {
offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].sz) * verts->vertex_len;
stride = a->sz;
}
else {
offset = a->offset;
}
const GLvoid *pointer = (const GLubyte *)0 + offset + v_first * stride;
const GLvoid *pointer = (const GLubyte *)0 + a->offset + v_first * stride;
for (uint n_idx = 0; n_idx < a->name_len; ++n_idx) {
const char *name = GPU_vertformat_attr_name_get(format, a, n_idx);
@@ -447,11 +418,8 @@ static void create_bindings(GPUVertBuf *verts,
static void batch_update_program_bindings(GPUBatch *batch, uint v_first)
{
/* Reverse order so first vbos have more prevalence (in term of attrib override). */
for (int v = GPU_BATCH_VBO_MAX_LEN - 1; v > -1; --v) {
if (batch->verts[v] != NULL) {
create_bindings(batch->verts[v], batch->interface, (batch->inst) ? 0 : v_first, false);
}
for (int v = 0; v < GPU_BATCH_VBO_MAX_LEN && batch->verts[v] != NULL; ++v) {
create_bindings(batch->verts[v], batch->interface, (batch->inst) ? 0 : v_first, false);
}
if (batch->inst) {
create_bindings(batch->inst, batch->interface, v_first, true);
@@ -581,10 +549,10 @@ static void *elem_offset(const GPUIndexBuf *el, int v_first)
{
#if GPU_TRACK_INDEX_RANGE
if (el->index_type == GPU_INDEX_U16) {
return (GLushort *)0 + v_first + el->index_start;
return (GLushort *)0 + v_first;
}
#endif
return (GLuint *)0 + v_first + el->index_start;
return (GLuint *)0 + v_first;
}
/* Use when drawing with GPU_batch_draw_advanced */

View File

@@ -162,100 +162,6 @@ void GPU_indexbuf_add_line_adj_verts(
GPU_indexbuf_add_generic_vert(builder, v4);
}
void GPU_indexbuf_set_point_vert(GPUIndexBufBuilder *builder, uint elem, uint v1)
{
BLI_assert(builder->prim_type == GPU_PRIM_POINTS);
BLI_assert(elem < builder->max_index_len);
builder->data[elem++] = v1;
if (builder->index_len < elem) {
builder->index_len = elem;
}
}
void GPU_indexbuf_set_line_verts(GPUIndexBufBuilder *builder, uint elem, uint v1, uint v2)
{
BLI_assert(builder->prim_type == GPU_PRIM_LINES);
BLI_assert(v1 != v2);
BLI_assert(v1 <= builder->max_allowed_index);
BLI_assert(v2 <= builder->max_allowed_index);
BLI_assert((elem + 1) * 2 <= builder->max_index_len);
uint idx = elem * 2;
builder->data[idx++] = v1;
builder->data[idx++] = v2;
if (builder->index_len < idx) {
builder->index_len = idx;
}
}
void GPU_indexbuf_set_tri_verts(GPUIndexBufBuilder *builder, uint elem, uint v1, uint v2, uint v3)
{
BLI_assert(builder->prim_type == GPU_PRIM_TRIS);
BLI_assert(v1 != v2 && v2 != v3 && v3 != v1);
BLI_assert(v1 <= builder->max_allowed_index);
BLI_assert(v2 <= builder->max_allowed_index);
BLI_assert(v3 <= builder->max_allowed_index);
BLI_assert((elem + 1) * 3 <= builder->max_index_len);
uint idx = elem * 3;
builder->data[idx++] = v1;
builder->data[idx++] = v2;
builder->data[idx++] = v3;
if (builder->index_len < idx) {
builder->index_len = idx;
}
}
void GPU_indexbuf_set_point_restart(GPUIndexBufBuilder *builder, uint elem)
{
BLI_assert(builder->prim_type == GPU_PRIM_POINTS);
BLI_assert(elem < builder->max_index_len);
builder->data[elem++] = RESTART_INDEX;
if (builder->index_len < elem) {
builder->index_len = elem;
}
}
void GPU_indexbuf_set_line_restart(GPUIndexBufBuilder *builder, uint elem)
{
BLI_assert(builder->prim_type == GPU_PRIM_LINES);
BLI_assert((elem + 1) * 2 <= builder->max_index_len);
uint idx = elem * 2;
builder->data[idx++] = RESTART_INDEX;
builder->data[idx++] = RESTART_INDEX;
if (builder->index_len < idx) {
builder->index_len = idx;
}
}
void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem)
{
BLI_assert(builder->prim_type == GPU_PRIM_TRIS);
BLI_assert((elem + 1) * 3 <= builder->max_index_len);
uint idx = elem * 3;
builder->data[idx++] = RESTART_INDEX;
builder->data[idx++] = RESTART_INDEX;
builder->data[idx++] = RESTART_INDEX;
if (builder->index_len < idx) {
builder->index_len = idx;
}
}
GPUIndexBuf *GPU_indexbuf_create_subrange(GPUIndexBuf *elem_src, uint start, uint length)
{
GPUIndexBuf *elem = MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf");
BLI_assert(elem_src && !elem_src->is_subrange);
BLI_assert(start + length <= elem_src->index_len);
#if GPU_TRACK_INDEX_RANGE
elem->index_type = elem_src->index_type;
elem->gl_index_type = elem_src->gl_index_type;
elem->base_index = elem_src->base_index;
#endif
elem->is_subrange = true;
elem->src = elem_src;
elem->index_start = start;
elem->index_len = length;
return elem;
}
#if GPU_TRACK_INDEX_RANGE
/* Everything remains 32 bit while building to keep things simple.
* Find min/max after, then convert to smallest index type possible. */
@@ -365,10 +271,6 @@ static void indexbuf_upload_data(GPUIndexBuf *elem)
void GPU_indexbuf_use(GPUIndexBuf *elem)
{
if (elem->is_subrange) {
GPU_indexbuf_use(elem->src);
return;
}
if (elem->ibo_id == 0) {
elem->ibo_id = GPU_buf_alloc();
}
@@ -383,7 +285,7 @@ void GPU_indexbuf_discard(GPUIndexBuf *elem)
if (elem->ibo_id) {
GPU_buf_free(elem->ibo_id);
}
if (!elem->is_subrange && elem->data) {
if (elem->data) {
MEM_freeN(elem->data);
}
MEM_freeN(elem);

View File

@@ -216,29 +216,6 @@ int GPU_vertformat_attr_id_get(const GPUVertFormat *format, const char *name)
return -1;
}
/* Make attribute layout non-interleaved.
* Warning! This does not change data layout!
* Use direct buffer access to fill the data.
* This is for advanced usage.
*
* Deinterleaved data means all attrib data for each attrib
* is stored continuously like this :
* 000011112222
* instead of :
* 012012012012
*
* Note this is per attrib deinterleaving, NOT per component.
* */
void GPU_vertformat_deinterleave(GPUVertFormat *format)
{
/* Ideally we should change the stride and offset here. This would allow
* us to use GPU_vertbuf_attr_set / GPU_vertbuf_attr_fill. But since
* we use only 11 bits for attr->offset this limits the size of the
* buffer considerably. So instead we do the conversion when creating
* bindings in create_bindings(). */
format->deinterleaved = true;
}
uint padding(uint offset, uint alignment)
{
const uint mod = offset % alignment;
@@ -412,3 +389,58 @@ void GPU_vertformat_from_interface(GPUVertFormat *format, const GPUShaderInterfa
}
}
}
/* OpenGL ES packs in a different order as desktop GL but component conversion is the same.
* Of the code here, only struct GPUPackedNormal needs to change. */
#define SIGNED_INT_10_MAX 511
#define SIGNED_INT_10_MIN -512
static int clampi(int x, int min_allowed, int max_allowed)
{
#if TRUST_NO_ONE
assert(min_allowed <= max_allowed);
#endif
if (x < min_allowed) {
return min_allowed;
}
else if (x > max_allowed) {
return max_allowed;
}
else {
return x;
}
}
static int quantize(float x)
{
int qx = x * 511.0f;
return clampi(qx, SIGNED_INT_10_MIN, SIGNED_INT_10_MAX);
}
static int convert_i16(short x)
{
/* 16-bit signed --> 10-bit signed */
/* TODO: round? */
return x >> 6;
}
GPUPackedNormal GPU_normal_convert_i10_v3(const float data[3])
{
GPUPackedNormal n = {
.x = quantize(data[0]),
.y = quantize(data[1]),
.z = quantize(data[2]),
};
return n;
}
GPUPackedNormal GPU_normal_convert_i10_s3(const short data[3])
{
GPUPackedNormal n = {
.x = convert_i16(data[0]),
.y = convert_i16(data[1]),
.z = convert_i16(data[2]),
};
return n;
}

View File

@@ -27,7 +27,6 @@
#define __GPU_VERTEX_FORMAT_PRIVATE_H__
void VertexFormat_pack(GPUVertFormat *format);
void VertexFormat_deinterleave(GPUVertFormat *format, uint vertex_len);
uint padding(uint offset, uint alignment);
uint vertex_buffer_size(const GPUVertFormat *format, uint vertex_len);

View File

@@ -8,7 +8,7 @@ in vec2 pos;
in float stretch;
#else
in vec2 uv_angles;
in vec4 uv_adj;
in float angle;
#endif
@@ -52,11 +52,6 @@ vec3 weight_to_rgb(float weight)
#define M_PI 3.1415926535897932
vec2 angle_to_v2(float angle)
{
return vec2(cos(angle), sin(angle));
}
/* Adapted from BLI_math_vector.h */
float angle_normalized_v2v2(vec2 v1, vec2 v2)
{
@@ -74,9 +69,7 @@ void main()
gl_Position = ModelViewProjectionMatrix * vec4(pos, 0.0, 1.0);
#ifdef STRETCH_ANGLE
vec2 v1 = angle_to_v2(uv_angles.x * M_PI);
vec2 v2 = angle_to_v2(uv_angles.y * M_PI);
float uv_angle = angle_normalized_v2v2(v1, v2) / M_PI;
float uv_angle = angle_normalized_v2v2(uv_adj.xy, uv_adj.zw) / M_PI;
float stretch = 1.0 - abs(uv_angle - angle);
stretch = stretch;
stretch = 1.0 - stretch * stretch;

View File

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