1
1

Compare commits

...

214 Commits

Author SHA1 Message Date
2da3949e26 Fix second issue introduced in refactoring/cleanup of weighted normals.
'degenerated too short normals' check was probably a bit too aggressive,
moved it back to more common 1e-6 value.
2018-04-12 10:30:19 +02:00
3b01edd52e Fix weighted normals modifier sometimes affecting actual mesh.
Same issue & same fix as with edit normals modifier some times ago...
2018-04-11 17:27:23 +02:00
8e44c30d71 Merge branch 'master' into soc-2017-normal-tools 2018-04-11 17:08:25 +02:00
b4c057f42f Create README.md 2018-04-05 12:46:41 +05:30
4ae2d8daf9 Changed rotation calculation in rotate_normal 2018-04-05 12:31:46 +05:30
ec52b7dbb6 Merge branch 'master' into soc-2017-normal-tools 2018-03-31 19:43:39 +05:30
51e196f716 Moar cleanup. 2018-03-13 10:25:30 +01:00
6e57d8475b Cleanup some leftover after recent refactor. 2018-03-13 10:00:13 +01:00
15e299a8b2 Merge branch 'master' into soc-2017-normal-tools 2018-03-12 17:38:13 +01:00
d6940e4e89 Heavy refactor of weighted normals code, especially 'keep sharp' part.
Before, 'keep sharp' was bsically:
* Overwriting everything just computed by 'single nor per vertex' code,
leading to a nice share of computations being tossed to nowhere.
* Re-implementing most of core clnor computaion code, only skipping all
'sharp edge' cases but the one defined by shapr edge tags. This was not
only bad for code maintenance and (reasonable) simplicity, it was also
introducing inconsistencies in how we define sharp edges, since
everwhere else in code we take into account sharp edge tag, flat face
tag, and 'smooth threshold' value (aka split angle) of mesh.

At first I though 'keep sharp' would need its own totally separated code
path, but thanks to some data structure tricks, it was possible to merge
most of both cases into single code. So this commit:
* Heavily factorizes and simplifies code, especially the 'keep sharp' case.
* Makes use of clnor spaces as generated by core BKE code to define
'smooth fans' that should have the same weighted normals, just like
anywhere else in Blender.

Note that since code is now using same functions in all cases, it also
naturally brings back vgroup support in keep_sharp case.

Also, this has been a fairly involved change, basic testing seems to be
OK, but more would be welcomed here. :)
2018-03-12 17:22:21 +01:00
ec58cbbfa6 Add clnor_spaces counter and opaque userdata pointer to clnor spaces.
Those will help with weighted normals modifier incomming refactor.
2018-03-12 17:21:13 +01:00
cd7d305d1a Merge branch 'master' into soc-2017-normal-tools 2018-03-11 11:39:31 +01:00
4be1a79a14 Partially revert "Fixed vertex groups not respected with keep sharp in modifier."
This reverts some changes from commit a7a4beecb2.
2018-03-11 11:38:51 +01:00
a7a4beecb2 Fixed vertex groups not respected with keep sharp in modifier.
Also fixed some other trivial issues.
2018-03-11 14:53:19 +05:30
e294a1154c Change 'face strength' values.
WARNING! this will break files saved with this branch and using face
strength feature (no crash or data corruption, but face strength values
will become invalid and will produce weird results)!

Rationals for this change are:
* Having strength values in natural order simplifies existing code
already, not to mention any future potential addition to strengthes.
* Now, code will work as-is, even if we decide to add more stength
values in the future.
2018-03-09 18:45:13 +01:00
bcfc16fcca Cleanup: use MEM_[cm]alloc_arrayN as needed, instead of MEM_[cm]allocN.
New memory allocator was added a few months ago as an attempt to make
our memory management more secure, still have to go over rest of this
patch's code for same thing. ;)
2018-03-09 18:32:17 +01:00
ec82e6ffcd Serious refactor, adding common data struct, some factorizing...
We now pass most common parameters to processing functions using a
single struct, this is much cleaner.

Also, factorized code aggregating poly nors into vertex nors in new
aggregate_vertex_normal function, and optimized expensive powf calls
(exponential inverse weight...) by computing once and caching most
common values (since weight is static all-over processing, and nearly
all vertices will only have a few faces or loops using them...).

Note that am still very unhapy with 'keep sharp edges' case, that one
seems to copy a lot of code we already have in BKE_mesh_evaluate, and
(if I followed code correctly) recompute everything, essentially
ditching the whole first computations of weighted normals!
2018-03-09 18:21:23 +01:00
970d16b669 Fix broken 'is vertex in vgroup' check.
Also, better to avoid binary XOR on booleans, you never know how those
are actually stored internally (or how they were generated), and may get
surprising results depending on compiler etc., though with modern
compilers I’d expect this to be safe now?
2018-03-09 15:22:25 +01:00
93ef006e3c More cleanup and optimizations.
Style and naming, arg positons (all loops data together, etc.),
const values and args, and so on...

Also, avoid allocating unused memory, avoid extra loops when possible, etc.
2018-03-09 15:03:47 +01:00
bd74ae9ca5 Minor cleanup and silplification/optimizations... 2018-03-09 12:54:22 +01:00
c389ba20d3 Merge branch 'master' into soc-2017-normal-tools 2018-03-07 15:27:35 +01:00
cd06e503a5 Minor cleanup & fixes in seightednormals modifier code.
Did not check code itself in deep yet...
2018-03-06 16:14:02 +01:00
4fa55703dc Fix critical issue with usage of unamed CD_PROP_INT layer.
Those types are generic, specific usages should always use named layers
with a specific 'id name'.

Also, make weightednormals modifier to show edited normals in 3DView in
edit mode.
2018-03-06 15:58:22 +01:00
76817f1745 More cleanup & co, also make invalid normals checks more coherent.
Checks for invalid normals was rather inconsistent, sometimes even
missing, trying to fix that.
2018-03-06 15:29:55 +01:00
b19b250d55 Try to fix naming of new editnormals ops, and some optimizations.
Some operators were using just 'normals', others 'lopp normals', others
'custom normals'... For now, decided to tick to just 'normals', should
we decide this is too confusing with vnors, I'd rather go to 'custom
normals'.

Also did some cleanup/simplification/optimization in split/merge normals code.
2018-03-06 12:20:29 +01:00
481dc77389 Heavy refactor of 'Point To' editing normals tools.
Core code remains the same, but UI/UX was quiet deeply changed:
* Some fixes in core code (like missing normalization of new normal in
spherize case), as well as some optimizations (reducing amount of vector
operations per affected normals) and cleanup/simplifications.
* Moving modal part of the op closer to moder 'state of the art' Blender
code, mainly:
** Use a modal keymap instead of hard-coded shortcuts.
** Use of proper header message generator extracting shortcuts from
modal keymap.
** Get rid of odd 'callback' function stuff for 'point to mouse', this
can be handled much more simply directly in main modal function.
** Adding some more keymap shortcuts (to temporarily reset normals, to
toggle invert, aligned and spherize options...).
** Do not immediately finish when selecting 'point to pivot/object/etc.',
keep consistent behavior requiring an explicit validation. This also
makes it possible to cancel after seeing effects of changes, or to try
another option, go back and forth between original and changed versions,
etc.
* Fixed memleak in case one would close Blender while modal op was
running (we need to define cancel operator callback for that case).

Note that we could (should?) go further still, adding ways to edit
spherize strength, even with some numinput. We could also add an option
to handle target value as global (current case) or local coordinates.
And so on. For now that will do though.
2018-03-05 22:01:57 +01:00
04e61143c5 Fix broken UI code after recent refactor. 2018-03-05 22:01:14 +01:00
c63e9a32e8 Merge branch 'master' into soc-2017-normal-tools 2018-03-05 12:16:53 +01:00
d1596dc170 Cleanup: renaming mostly, plus some const qualifiers etc. 2018-03-02 13:52:10 +01:00
acb4a99c42 Merge branch 'master' into soc-2017-normal-tools 2018-03-02 12:31:38 +01:00
d92bd5746d Merge branch 'master' into soc-2017-normal-tools
Conflicts:
	source/blender/blenkernel/BKE_mesh.h
2018-03-01 17:02:01 +01:00
87d167e303 Merge branch 'master' into soc-2017-normal-tools 2018-03-01 16:35:43 +01:00
7b512dde2d More minor cleanup/style editing. 2018-03-01 12:38:25 +01:00
d7a8cb0a35 Cleanup: make static utility functions, try to find some better names.
Naming is always complicated, trying to get something a bit more
coherent, also with already existing clnor struct...
2018-02-28 20:33:06 +01:00
2e26844ef6 Simplify/deduplicate code to 'select' loops from faces/verts selection. 2018-02-28 18:19:54 +01:00
9a568dbce0 Merge branch 'master' into soc-2017-normal-tools 2018-02-28 17:43:12 +01:00
5bc2f90d6f Cleanup, remove dead code, do not allocate when not needed... 2018-02-28 16:44:19 +01:00
f9b2e4a16b Merge branch 'master' into soc-2017-normal-tools 2018-02-27 12:17:23 +01:00
fdd7b788ac Cleanup: comments. 2018-02-23 20:36:33 +01:00
604b6369da Cleanup: use anonymous enum instead of defines for flags. 2018-02-23 20:36:06 +01:00
9d3ad0eb5a Merge branch 'master' into soc-2017-normal-tools 2018-02-23 14:30:34 +01:00
0d60656039 Simplify & optimize code in BM_lnorspace_invalidate().
Quiet sure that one is correct, and it does not have to invalidate all
loops (smoothfans) of all faces of a given affected vertex, only
neighbor ones should be enough.

Also, no need to loop into smoothfans here, since we already check on
all loops of all vertices.
2018-02-22 16:30:53 +01:00
5fbc799d37 Fix broken initialization of normals when first editing them.
rBdd6642bb80ac fixed one case but broke a lot others (including when
custom normals data were already there before strating editing the mesh...).

Now simply using same (new) code from master when creating empty clnors
data, i.e. defining sharp edges as needed to mimic results from
autosmooth shading.
2018-02-22 16:25:37 +01:00
11adf6e998 Merge branch 'master' into soc-2017-normal-tools
Conflicts:
	source/blender/blenkernel/intern/mesh_evaluate.c
	source/blender/bmesh/intern/bmesh_mesh.c
2018-02-22 16:03:58 +01:00
e8bce2b56d Merge branch 'master' into soc-2017-normal-tools 2018-02-21 14:54:29 +01:00
399cbf9f4b Cleanup: move new BMesh clnors editing data to more logical place in struct. 2018-02-21 14:20:17 +01:00
b41bce3905 Cleanup: remove 'lnorspace_dirty' flag from BMHeader.hflag.
Makes no sense to use last free slot here for a temp, internal data only
relevant for loops.

For now, re-used 'make' in internal api flags, should be safe enough
hopefully.
2018-02-20 16:06:13 +01:00
648cdd7984 Cleanup: comment. 2018-02-20 15:22:02 +01:00
b9573ac780 Merge branch 'master' into soc-2017-normal-tools 2018-02-20 15:07:57 +01:00
a08e5d8695 Cleanup: do not use 'split_angle' name for a var which is a cosine.
This is confusing (and has to be fixed in master code as well).
2018-02-20 12:57:52 +01:00
4bb4749791 Cleanup: remove unused codelines. 2018-02-20 12:51:26 +01:00
8c4295f3c3 Cleanup: MLoopNorSpaceArray new 'flags' are not flags at all, they are mutually exclusive data type information.
Makes code easier to follow, and a tad safer too.
2018-02-20 12:49:10 +01:00
5a6be7ba72 Fix after last merge. 2018-02-20 12:21:05 +01:00
ee23226f5d Merge branch 'master' into soc-2017-normal-tools 2018-02-20 12:12:27 +01:00
d17d562ad0 Merge branch 'master' into soc-2017-normal-tools 2018-02-14 14:03:55 +05:30
dd6642bb80 Added custom loop normals to work correctly with autosmooth 2018-02-14 14:03:04 +05:30
40407509d4 Merge branch 'master' into soc-2017-normal-tools
Conflicts:
	release/datafiles/locale
	release/scripts/addons
	release/scripts/addons_contrib
	source/blender/makesdna/DNA_scene_types.h
	source/tools
2018-02-02 15:14:03 +01:00
78b555b940 Resolved issues with copy 2017-12-29 23:15:22 +05:30
4220891236 Removed mem alloc for align in point_normals 2017-12-29 22:02:21 +05:30
ccd25d93a4 Merge branch 'master' into soc-2017-normal-tools 2017-12-22 14:27:58 +05:30
70bc7fbe37 Merge branch 'master' into soc-2017-normal-tools 2017-11-21 15:25:11 +01:00
203af5af9b Merge branch 'master' into soc-2017-normal-tools 2017-11-09 22:53:39 +01:00
f0e23f9321 More rampaging cleanup & refactor in new clnor editmesh/transform code.
Also added loop_index to TransDataLoopNormal mapping, which allows us to
avoid another nasty loop-in-loop case.
2017-11-09 22:34:51 +01:00
5ee8eac59e Fix bug with transform_ui helper. 2017-11-09 17:35:10 +01:00
f956156e32 Fix/cleanup BM_lnorspace_invalidate().
This func can be made much nicer now that we store BMLoop pointers.
2017-11-09 17:26:23 +01:00
0a01cc04d6 LNorSpaceArray: Ease access to loop in BMesh context.
Unlike 'regular' mesh, BMesh makes it really hard to get a BMLoop from
its index - it's designed to work in reverse, with passing BMLoop
pointers around, not their indices.

Henceforce, changed MLoopNorSpaceArray to support two kind of loop
storage, either indices (when used with regular Mesh), or BMLoop
pointers themselves (when used with BMesh).

Changes are luckily fairly simple and straightforward, so would not
expect too much issues here (quick tests seem to be all OK), and this
makes handling of clnors in BMesh context much eaiser (avoiding some
mapping or double-looping to get loops from their indices...).

For same reason, also added BMLoop pointer itself in TransDataLoopNormal struct.

There's still some (new) code to adjust/simplify, will be done in next
commits.
2017-11-09 17:03:36 +01:00
1f649dc265 More cleanup... 2017-11-08 18:23:28 +01:00
95bbe124a1 Move WeightedNormal modifier together with NormalEdit one in menu.
Though this breaks alphabetical order, it makes much more sense to have
those two together...
2017-11-08 17:53:55 +01:00
c8d84df4aa More cleanup... 2017-11-08 16:51:52 +01:00
7c8d616cbc More style cleanup, renaming some now-public functions. 2017-11-08 13:24:32 +01:00
14fc221614 Tweaks to UI. 2017-11-08 13:05:28 +01:00
d35869ace8 Fix missing break in switch block. 2017-11-08 12:08:34 +01:00
670aea5029 Merge branch 'master' into soc-2017-normal-tools
Conflicts:
	source/blender/makesrna/intern/rna_scene.c
2017-11-08 12:07:46 +01:00
a578ea34f7 Cleanup: better names for new clnor edit operators enums.
Even if those remain local to the file, better have not to use too much
generic names, this can easily backfire in future.
2017-10-13 18:54:18 +02:00
eff73afd72 Fix bad UI-related code in action callbacks of operators.
UI/drawing related code in action callbacks is really not nice,
especially since we do have a more consistent way to dynamically
show/hide some properties.

Note that transform_ops one remains rather hackish here (since we have
to use context for this case)... Not sure how to do it better so far.
2017-10-13 18:45:54 +02:00
4b4114acc4 Fix missing new weighted normals modifier case in Outliners' drawing code. 2017-10-12 18:19:27 +02:00
50e32a7744 Fix some build warnings in new weighted normals modifier code. 2017-10-12 18:17:54 +02:00
e7b93f06e1 Cleanup (no functional change expected).
Mostly styling, and some minor/harmless errors.
2017-10-12 18:14:53 +02:00
f9d7ff4d2d Merge branch 'master' into soc-2017-normal-tools
Conflicts:
	intern/cycles/device/device.cpp
2017-10-12 16:56:58 +02:00
e45dbcfb6b Fix T53040: Vertex paint, texure & symmetry fail
Removed old logic for brush texture which was being applied twice.
2017-10-10 19:49:02 +05:30
cabec0af38 Vertex Paint: add back missing VertProjHandle free
Also avoid passing large struct as value.
2017-10-10 19:49:02 +05:30
9f10bfc1e4 Correct logic for vertex paint smear
Swapping gave values from the state before the last.
Thanks to @angavrilov for spotting.
2017-10-10 19:49:02 +05:30
920294b248 Fix Sculpt 2D falloff, missing brush caused crash 2017-10-10 19:49:01 +05:30
3fa30dfa40 Cycles: Fix compilation of sm_20 and sm_21 kernels
Was broken since the bicubic commit for GPU support.
2017-10-10 19:49:01 +05:30
60b2603634 Recent check for navigation missed NULL check 2017-10-10 19:49:01 +05:30
5d11e6a009 UI: VSE strip menu reorganization
Add Inputs and Transform submenus.
2017-10-10 19:49:01 +05:30
502ebd058a Cycles: schedule more work for non-display and compute preemption CUDA cards.
This change affects CUDA GPUs not connected to a display or connected to a
display but supporting compute preemption so that the display does not
freeze. I couldn't find an official list, but compute preemption seems to be
only supported with GTX 1070+ and Linux (not GTX 1060- or Windows).

This helps improve small tile rendering performance further if there are
sufficient samples x number of pixels in a single tile to keep the GPU busy.
2017-10-10 19:49:01 +05:30
Mathieu Menuet
5d8e35d9d9 Fix T53017: Cycles not detecting AMD GPU when there is an NVidia GPU too.
Best guess is that cuInit() somehow interferes with the AMD graphics driver
on Windows, and switching the initialization order to do OpenCL first seems
to solve the issue.
2017-10-10 19:49:01 +05:30
8524793a39 Fix potential string buffer overruns.
Note that our library path handling is still rather dodgy on this
regards, shall take some time at some point to seriously sanitize it...
2017-10-10 19:49:01 +05:30
28e2ee839e Code refactor: use DeviceInfo to enable QBVH and decoupled volume shading. 2017-10-10 19:49:01 +05:30
a31c8d1fe3 Cleanup: --help text
Sync with manual
2017-10-10 19:49:01 +05:30
6ee95ebc75 CMake: Re-order PYTHON_VERSION check
Missing paths would error first.
2017-10-10 19:49:01 +05:30
ff15d06f53 Cycles: OpenCL bicubic and tricubic texture interpolation support. 2017-10-10 19:49:00 +05:30
8eb7f3a3d4 Fix incorrect MIS with principled BSDF and specular roughness 0. 2017-10-10 19:49:00 +05:30
aac0c9b910 [cmake] Add minimum python version check to cmake to prevent later build errors. 2017-10-10 19:49:00 +05:30
460b8600d5 Code cleanup: simplify cmake kernel install. 2017-10-10 19:49:00 +05:30
41eaabfb38 Cycles: CUDA bicubic and tricubic texture interpolation support.
While cubic interpolation is quite expensive on the CPU compared to linear
interpolation, the difference on the GPU is quite small.
2017-10-10 19:49:00 +05:30
a63cb77c56 Code refactor: make texture code more consistent between devices.
* Use common TextureInfo struct for all devices, except CUDA fermi.
* Move image sampling code to kernels/*/kernel_*_image.h files.
* Use arrays for data textures on Fermi too, so device_vector<Struct> works.
2017-10-10 19:49:00 +05:30
40a034e522 Actually force accumulate mode for the Smear brush in weight/vertex paint.
Just removing the checkbox from UI isn't enough for proper behavior.
2017-10-10 19:49:00 +05:30
39c1f7318a Make weight paint blur thread safe, and fix smear ignoring mirror.
Instead of trying to be clever with swaps and lazy updating the weight
data, simply recalculate one single array. To improve performance, use
threading for that.
2017-10-10 19:49:00 +05:30
cb3d7e28bb Cycles: Fix possible race condition when generating Beckmann table
Two issues here:

- Checking table size to be non-zero is not a proper way to go here. This is
  because we first resize the table and then fill it in. So it was possible that
  non-initialized table was used.

  Trickery with using temporary memory and then doing table.swap() might work,
  but we can not guarantee that table size will be set after the data pointer.

- Mutex guard was useless, because every thread was using own mutex. Need to
  make mutex guard static so all threads are using same mutex.
2017-10-10 19:49:00 +05:30
e0b4c8b250 Cycles: Cleanup, indendation 2017-10-10 19:49:00 +05:30
d96556021d Fix T53012: Shadow catcher creates artifacts on contact area
The issue was caused by light sample being evaluated to nan at some point.
This is root of the cause which is to be fixed, but is very hard to trace down
especially via ssh (the issue only happens on AVX2 release build). Will give it
a closer look when back to my AVX2 machine.

For until then this is a good check to have anyway, it corresponds to what's
happening in regular radiance sum.
2017-10-10 19:49:00 +05:30
22616a67c9 Cycles: Cleanup, indentation and wrapping 2017-10-10 19:49:00 +05:30
98ed543352 Cleanup: Math lib naming (use v3 suffix) 2017-10-10 19:48:59 +05:30
ecf1ef7420 Sequencer: Cleanup, use more BLI utilities where possible 2017-10-10 19:48:59 +05:30
9d0d881fa5 Sequencer: Use BLI math functions where possible 2017-10-10 19:48:59 +05:30
f03533fda1 Sequencer: Avoid re-calculation index offset multiple times 2017-10-10 19:48:59 +05:30
9dcc9e3bc4 Sequencer: Use funciton instead of macr oto avoid argument re-evaluation 2017-10-10 19:48:59 +05:30
61a5e1bd54 Fix T52940: VSE Glow Effect Strip on transparent images has no blur 2017-10-10 19:48:59 +05:30
c67a6a8d1b Error in falloff_angle range in last commit 2017-10-10 19:48:59 +05:30
ab6df05037 Vertex Paint: move normal falloff into the brush
All related settings are already in the brush,
so it's inconvenient to switch panels to change this one option.
2017-10-10 19:48:59 +05:30
cb10147f78 Vertex Paint: use view normal w/ 2D falloff
When projecting to the view, cull faces pointing
away from the view normal.
2017-10-10 19:48:59 +05:30
598198f8c0 Vertex Paint: use brush front-face setting
Follow sculpt mode more closely by using the brush front-face option.
2017-10-10 19:48:59 +05:30
3b2865e88c Sculpt: use tube falloff when calculating normals
Also apply 2D clamping for other tools when the option is set.
2017-10-10 19:48:59 +05:30
5255dc86c7 Sculpt: clamp normal to plane w/ projected falloff
Allows for editing outlines w/o pushing geometry towards/away from
the view.
2017-10-10 19:48:59 +05:30
724cdee37b Fix ruler access from search pop-up
D2831 by @1xundoredo
2017-10-10 19:48:58 +05:30
2eedacfee2 Cleanup: style 2017-10-10 19:48:58 +05:30
d23e682cc6 Fix T52514: don't clear filename when dropping directory path in file browser. 2017-10-10 19:48:58 +05:30
c280d8c8e9 Fix T52998: disabled menu entries responding to key shortcuts. 2017-10-10 19:48:58 +05:30
3e8c42a36c Fix T53002: Batch-Generate Previews generate empty or none image for large objects.
Camera clipping was left to default values, which won't work well for
very large (or small) objects. Now recompute valid clipping start/end
based on boundingbox of rendered data, and final location of camera.
2017-10-10 19:48:58 +05:30
20303c5fe6 Fix T53001: more workarounds for crash in AMD compiler with recent drivers. 2017-10-10 19:48:58 +05:30
100fe0ad1c Code refactor: split displace/background into separate kernels, remove luma. 2017-10-10 19:48:58 +05:30
eb0fb1eb13 Sculpt Mode: 2D falloff option
This makes brush influence into a tube instead of a sphere.
It can be used along the outline of a mesh to adjust it's silhouette.

Note that all this takes advantage of changes from vertex paint,
from testing this seems useful so exposing from the brush options.
2017-10-10 19:48:58 +05:30
65e3d546f1 Vertex Paint: apply when cursor isn't over faces
This behavior makes more sense for sculpt, less so for painting.
Restores non PBVH behavior, adding `BKE_pbvh_find_nearest_to_ray` -
similar to ray-cast except it finds the closest point on the surface.
2017-10-10 19:48:58 +05:30
7858428075 Cleanup: rename dist -> depth
Prepare to add code that stores distance to the ray, avoid confusion.
2017-10-10 19:48:58 +05:30
4993d3f3a4 Cleanup: remove unused struct member
Merged with soc-2016-pbvh-painting, no longer needed.
2017-10-10 19:48:58 +05:30
db480d0e9f Disable cursor drawing while navigating
Was performing ray-casts in sculpt mode on every update.
2017-10-10 19:48:58 +05:30
b6ff16f426 Fix sculpt secondary color missing some brushes
D2841 by @uvwxyz w/ edits
2017-10-10 19:48:58 +05:30
bfce6fa630 Cleanup: use bool for brush checks 2017-10-10 19:48:57 +05:30
009129c0c9 Fix brush reset (missing notifier)
D2843 by @uvwxyz
2017-10-10 19:48:57 +05:30
de1d13f03a CMake: use restrict w/ gcc, not clang 2017-10-10 19:48:57 +05:30
81cb9381a7 PyAPI: fast keyword parsing for bpy modules
No functional changes.
2017-10-10 19:48:57 +05:30
dbd3445d78 CMake: add -Wrestrict for GCC 2017-10-10 19:48:57 +05:30
27ffbb5d99 Fix passing the same argument twice to BLI_strncpy 2017-10-10 19:48:57 +05:30
0e73b3e5bd Fix setting the operator name in Py operator API 2017-10-10 19:48:57 +05:30
b4ca473704 Fix incorrect CUDA remaining time estimate after previous commit. 2017-10-10 19:48:57 +05:30
f4588ec10f Cycles: CUDA faster rendering of small tiles, using multiple samples like OpenCL.
The work size is still very conservative, and this doesn't help for progressive
refine. For that we will need to render multiple tiles at the same time. But this
should already help for denoising renders that require too much memory with big
tiles, and just generally soften the performance dropoff with small tiles.

Differential Revision: https://developer.blender.org/D2856
2017-10-10 19:48:57 +05:30
08bda283f7 Fix use of uninitialized memory in Cycles normal baking. 2017-10-10 19:48:57 +05:30
dc64328df1 Code refactor: zero render buffers outside of kernel.
This was originally done with the first sample in the kernel for better
performance, but it doesn't work anymore with atomics. Any benefit was
very minor anyway, too small to measure it seems.
2017-10-10 19:48:56 +05:30
1139f19db2 Code refactor: use split variance calculation for mega kernels too.
There is no significant difference in denoised benchmark scenes and
denoising ctests, so might as well make it all consistent.
2017-10-10 19:48:56 +05:30
cfe33f3d58 Code refactor: remove rng_state buffer and compute hash on the fly.
A little faster on some benchmark scenes, a little slower on others, seems
about performance neutral on average and saves a little memory.
2017-10-10 19:48:56 +05:30
b7a753cc6f Code refactor: add WorkTile struct for passing work to kernel.
This makes sharing some code between mega/split in following commits a bit
easier, and also paves the way for rendering multiple tiles later.
2017-10-10 19:48:56 +05:30
f43a48ac7e Fix T52645, T52645: AMD OpenCL compiler crash with recent drivers.
Work around the bug by reshuffling code.
2017-10-10 19:48:55 +05:30
5fe32381a3 installdeps: Fix building numpy with newer py system...
Many thanks to Jens Verwiebe for investigating and finding this fix.
2017-10-10 19:48:55 +05:30
0b0af5baa6 Fix T42489 and T52936: Loading blend with minimized window results in crash or empty screen on windows.
Reviewed By: @brecht , @sergey

Differential Revision: http://developer.blender.org/D2866
2017-10-10 19:48:55 +05:30
ce9fa1fb75 Fix error copying smoke modifier uv layer 2017-10-10 19:48:55 +05:30
f85cff9fd4 Tssst, we use USA english, not Oxford one... :P 2017-10-10 19:48:55 +05:30
6b1d91f20c Fix i18n messages extraction script, and a few more UI messages... 2017-10-10 19:48:55 +05:30
4d3ed78510 Fix T52982: Join operator with context override crashes Blender 2.79 2017-10-10 19:48:55 +05:30
4f7bc6da81 Fix T52923: Circle diameter is in fact radius 2017-10-10 19:48:55 +05:30
243dcf5e87 Cleanup: redundant casts 2017-10-10 19:48:54 +05:30
b4ee0225fb Docs: remove outdated PyAPI guide 2017-10-10 19:48:54 +05:30
1c6f59def7 PyAPI: fast keyword parsing for __import__
No functional changes.
2017-10-10 19:48:54 +05:30
3cc2dcdb0b PyAPI: fast keyword parsing for bpy.props
No functional changes.
2017-10-10 19:48:54 +05:30
39e60c3067 Cleanup/fixes compilation warnings.
Mainly, shadowing variables and unused parameters.
2017-10-03 22:20:22 +02:00
ea631a1e7a Merge branch 'master' into soc-2017-normal-tools 2017-10-03 21:33:47 +02:00
4a438cfa58 Fixed error in mod with many loops sharing edge.
Also removed redundant function and replaced its definition to BKE_mesh.h
2017-10-03 22:46:26 +05:30
1f5857b395 Merge branch 'master' into soc-2017-normal-tools 2017-09-24 13:28:30 +05:30
3776754641 Merge branch 'master' into soc-2017-normal-tools 2017-09-13 19:52:50 +05:30
dca08a4440 Merge branch 'master' into soc-2017-normal-tools 2017-09-05 16:25:51 +05:30
986b1f6f32 Added 3 level weighting to modifier. Also did minor cleanup of code 2017-09-02 12:13:47 +05:30
18df657631 Changed T_CLNOR_INVALIDATE flag as it conflicted with prop edit. Fixed mistake with weighting in modifier making it work differently from average. 2017-08-29 19:39:17 +05:30
8c942d9708 Made changes to split tool 2017-08-21 22:59:04 +05:30
1b92a80703 Made UI changes. Removed binary weighting in average. Changed weight calculation. 2017-08-20 21:41:40 +05:30
96e76ea4a3 Made workflow changes to copy, paste. Added add and multiply.
Did a cleanup of point_normals and split. Added ability to spherize in point_normals. Added reset. Added normal smoothing based on adjacent vertices. Did a cleanup of UI.
The set of normal tools now share the normal vector in UI. The copy/paste operator now contains all these methods and has been renamed.
2017-08-18 20:44:15 +05:30
342793ef75 Fixed bugs in copy and made UI changes 2017-08-15 22:39:39 +05:30
2b5f825ce4 Fixed issues with set normals from faces with smooth faces. All loop normals of smooth fan weren't updated 2017-08-13 22:40:32 +05:30
a917e14afa Fixed memalloc bug in lnorspacearr and removed BMO_OPTYPE_FLAG_INVALIDATE_CLNOR_ALL flag 2017-08-13 19:26:25 +05:30
395119d5ce Changed merge, split and average to make them more coherent.
Now merge and split are only used to join/split split normals. All other functionality is transferred over to average.
2017-08-12 21:35:27 +05:30
07c700365b Added weighting, sharp edges to average.
Made small UI changes.
2017-08-12 21:35:27 +05:30
28afe63fb7 changed weight calculation in modifier 2017-08-12 21:35:27 +05:30
29e5f37bdd Fix memleak in Weighted Normals modifier when autosmooth is disabled. 2017-08-07 10:31:55 +02:00
3df612c61f Fix error in build by including limits.h 2017-08-07 07:17:21 +05:30
7eb51ebaae Rewrote the code for sharp edges on weighted modifier. 2017-08-06 23:31:06 +05:30
16289abe50 Merge branch 'master' into soc-2017-normal-tools 2017-08-06 18:42:20 +02:00
c7e8f0d6da Merge branch 'master' into soc-2017-normal-tools 2017-08-04 22:13:59 +05:30
c174f1c888 Fixed potential memory leak in modifier 2017-08-04 21:42:59 +05:30
5aab60e60f Fixed issues with modifier: Adding vertgroups now does not overwrite data. Also added proper weights to sharp edges 2017-08-04 21:35:34 +05:30
fd3960a4eb Fixed vgroups editing all loop data in modifier 2017-08-04 17:39:56 +05:30
c1af648c33 Fixed the following for weighted modifier:
1) Now correctly splits loop normal according to sharp edges
2) Modifier now only acts on active vertex group
3) Added a check to use smooth/sharp flags as boolean weights
2017-08-03 19:21:35 +05:30
d0c25f8296 Added the following to weighted modifier:
1) Modifier now respects sharp edges, requires bool value to be checked.
2) Can now use vertex groups to apply modifier to only selected vgroup
3) Added boolean (0-1) weights in the form of Smooth/Sharp flags. Smooth flag means the face will hold 0 weight.
2017-07-30 21:38:33 +05:30
8b4d448ec7 Fixed issues with set_normal_from_faces 2017-07-28 23:42:01 +05:30
6a06b247d8 Added Average and weighting modes to merge. 2017-07-28 19:54:06 +05:30
7c8ecd9226 Merge branch 'master' into soc-2017-normal-tools 2017-07-27 22:37:53 +05:30
33a95c40a3 Merge branch 'master' into soc-2017-normal-tools 2017-07-26 14:50:24 +05:30
d586e5557b Changed set_normals_from_faces to respect sharp edges. 2017-07-26 14:45:15 +05:30
86e6350747 Fixed issues with Copy Normals 2017-07-25 23:18:41 +05:30
3858f956db Added weighted normal modifier.
Has a weight int which specifies the weights with which face area/corner angle will be exponentially divided. Threshold provides the limit in values which will be weighted equally.
2017-07-22 17:37:02 +05:30
604bd8799e Added snapping to rotate normal.
Also added a ui function to point_normals which allows easier editing by creating a sphere box. Shown when align is enabled.
2017-07-18 18:40:50 +05:30
94007a1769 Merge branch 'master' into soc-2017-normal-tools 2017-07-16 18:50:00 +05:30
51d49a0f7c Added ability to copy clnors.
Also contains commented code for averaging, I'll finish it after weighting modes. No hotkeys for copy (yet). You can copy either face normals or single loop normals.
Here single loop normal means: selecting a vert then a face of the vert will copy loop normal of the face associated with this vert. So, standard vert ->click-> copy wont work.

click on vert then copy will work if a) Loops of vert are merged b) Loops of vert have same normal value.
Pasting works with the same standard selection rules
2017-07-13 20:50:39 +05:30
9773709cf1 Optimised split to faces and fixed preserve_clnor not keeping clnors 2017-07-12 21:34:15 +05:30
d438bbfa43 Merge branch 'master' into soc-2017-normal-tools 2017-07-10 17:05:25 +05:30
4a7653005b Now invalidates all loops in an lnor space 2017-07-10 17:00:22 +05:30
626c9888d7 cleanup of point_normals and added poll 2017-07-04 23:53:06 +05:30
55237cfe97 Merge branch 'master' into soc-2017-normal-tools 2017-07-04 11:47:40 +05:30
7376444b84 Fixed all clnors in lnorspace not updating 2017-07-04 11:31:24 +05:30
28061ac85e Fixed bugs and cleanup 2017-07-03 13:02:00 +05:30
448c61b861 Added Split/Merge normals.
Doesn't work on the basis of any weighting mode. Will have to implement that first. I'll add more methods as I progress in other areas.
2017-07-01 12:05:49 +05:30
41b118c9d9 Merge branch 'master' into soc-2017-normal-tools
Conflicts:
	source/blender/python/intern/bpy_props.c
2017-06-27 15:08:56 +02:00
5c8a38a858 Added ability to edit individual normal.
Works by using multiple selection modes, so if yo
works by using multiple selection modes, so if you select vert then face. Th
Works by using multiple selection modes.
So, if you select vert then face, only clnor linked with
both vert and face will be edited.

Has some issues though, as vert + edge selection can
be associated to either of 2 clnors. Need to fix.
2017-06-23 11:35:45 +05:30
a2ad1b4e88 Added invalidate for transform ops.
Can now keep clnors when transform ops are applied. As now invalidation for each function is harder to keep track of, Added a function which rebuilds all the lnor spaces in the mesh when BM_lnorspace_rebuild is called to detect if any unmarked spaces are rebuilt.
2017-06-20 23:03:32 +05:30
137bcc528a Merge branch 'master' into soc-2017-normal-tools 2017-06-19 11:11:04 +05:30
66199cf540 Added normals to follow mouse location.
Now M key can be used after ALT - L to have clnors point to mouse location.
2017-06-19 11:00:48 +05:30
1516d8d1d1 Added point to origin and LMB click 2017-06-15 20:27:01 +05:30
19ff231ff5 Fixed issues with Point Normals.
Added header, fixed bugs and logic errors in point normals to target. Target location is now editable from UI.
Also renamed 'bmspacearr' to 'lnor_spacearr'
2017-06-13 11:34:03 +05:30
201689daeb Remove support of lnorspacearray in editmesh undo/redo, fix memleak.
Supporting lnorspacearray in undo/redo would require a deep copy of lnor
spaces, not only a shallow copy of top struct. And it would eat a fair
amount of memory, think we'd rather recompute those on undo for now!

Also, fixed memory leaked by not correctly freeing lnorspace array on
BMesh deletion.
2017-06-12 09:32:15 +02:00
15279c04d7 Do not include BKE_mesh.h in bmesh_class.h
We absolutely avoid such include unless totally mandatory, here we can
simply keep lnorspaces array a pointer in BMesh struct, and allocate it
on demand. Also, was breaking bmesh tests building.

Note: this totally breaks undo/redo, but previous code was utterly
wrong here as well (shallow copy of lnorspaces, ending up sharing whole
internal memory -> crash garanteed ;) ). Think we can skip that struct
for undo/redo for now at least, we can rebuild it in those cases imho...
2017-06-12 09:22:19 +02:00
cd9939fa50 Merge branch 'master' into soc-2017-normal-tools 2017-06-12 08:52:23 +02:00
9673aa8c15 Added point normals to target.
Moved common loop functions and made changes to structure
2017-06-11 23:21:42 +05:30
bf892c10fc Added ability to rotate custom normal 2017-06-06 11:00:47 +05:30
5bc15b3f75 Added clnor invalidate for all BMOps 2017-06-02 11:46:19 +05:30
cf811e7828 Cached lnorspace array and added dirty Mechanic 2017-06-01 11:00:56 +05:30
40 changed files with 3072 additions and 99 deletions

30
README.md Normal file
View File

@@ -0,0 +1,30 @@
# soc-2017-normal-tools
Clone of my GSoC branch hosted at Blender's git servers [soc-2017-normal-tools](https://git.blender.org/gitweb/gitweb.cgi/blender.git/shortlog/refs/heads/soc-2017-normal-tools)
### Phabricator tracker
https://developer.blender.org/diffusion/B/browse/soc-2017-normal-tools/
### Proposal
https://wiki.blender.org/index.php/User:RohanRathi/GSoC_2017/Proposal
### Weekly Reports
https://wiki.blender.org/index.php/User:RohanRathi/GSoC_2017/Reports
### Blender Artists thread
https://blenderartists.org/forum/showthread.php?427746-GSoC-2017-Normal-Editing-Tools
### User Documentation
https://wiki.blender.org/index.php/User:RohanRathi/GSoC_2017/User_Documentation
Do note that this branch does not include any other work not associated with Normal editing.

View File

@@ -56,6 +56,7 @@ KM_HIERARCHY = [
('Particle', 'EMPTY', 'WINDOW', []),
('Knife Tool Modal Map', 'EMPTY', 'WINDOW', []),
('Custom Normals Modal Map', 'EMPTY', 'WINDOW', []),
('Paint Stroke Modal', 'EMPTY', 'WINDOW', []),
('Paint Curve', 'EMPTY', 'WINDOW', []),

View File

@@ -203,57 +203,7 @@ class MeshSelectPrev(Operator):
return {'FINISHED'}
# XXX This is hackish (going forth and back from Object mode...), to be redone once we have proper support of
# custom normals in BMesh/edit mode.
class MehsSetNormalsFromFaces(Operator):
"""Set the custom vertex normals from the selected faces ones"""
bl_idname = "mesh.set_normals_from_faces"
bl_label = "Set Normals From Faces"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return (context.mode == 'EDIT_MESH' and context.edit_object.data.polygons)
def execute(self, context):
import mathutils
bpy.ops.object.mode_set(mode='OBJECT')
obj = context.active_object
me = obj.data
v2nors = {}
for p in me.polygons:
if not p.select:
continue
for lidx, vidx in zip(p.loop_indices, p.vertices):
assert(me.loops[lidx].vertex_index == vidx)
v2nors.setdefault(vidx, []).append(p.normal)
for nors in v2nors.values():
nors[:] = [sum(nors, mathutils.Vector((0, 0, 0))).normalized()]
if not me.has_custom_normals:
me.create_normals_split()
me.calc_normals_split()
normals = []
for l in me.loops:
nor = v2nors.get(l.vertex_index, [None])[0]
if nor is None:
nor = l.normal
normals.append(nor.to_tuple())
me.normals_split_custom_set(normals)
me.free_normals_split()
bpy.ops.object.mode_set(mode='EDIT')
return {'FINISHED'}
classes = (
MehsSetNormalsFromFaces,
MeshMirrorUV,
MeshSelectNext,
MeshSelectPrev,

View File

@@ -1549,6 +1549,22 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
if md.rest_source == 'BIND':
layout.operator("object.correctivesmooth_bind", text="Unbind" if is_bind else "Bind")
def WEIGHTED_NORMAL(self, layout, ob, md):
layout.label("Weighting Mode:")
split = layout.split(align=True)
col = split.column(align=True)
col.prop(md, "mode", text="")
col.prop(md, "weight", text="Weight")
col.prop(md, "keep_sharp")
col = split.column(align=True)
row = col.row(align=True)
row.prop_search(md, "vertex_group", ob, "vertex_groups", text="")
row.active = bool(md.vertex_group)
row.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT')
col.prop(md, "thresh", text="Threshold")
col.prop(md, "face_influence")
classes = (
DATA_PT_modifiers,

View File

@@ -441,11 +441,59 @@ class VIEW3D_PT_tools_shading(View3DPanel, Panel):
props.clear = True
row.operator("mesh.mark_sharp", text="Sharp").use_verts = True
class VIEW3D_PT_tools_normal(View3DPanel, Panel):
bl_category = "Shading / UVs"
bl_context = "mesh_edit"
bl_label = "Normal Tools"
def draw(self, context):
layout = self.layout
toolsettings = context.tool_settings
col = layout.column(align=True)
col.label(text="Normals:")
col.label(text="Vertex Normals:")
col.operator("mesh.normals_make_consistent", text="Recalculate")
col.operator("mesh.flip_normals", text="Flip Direction")
layout.separator()
layout.label(text="Custom Normals:")
col = layout.column(align=True)
col.operator("mesh.set_normals_from_faces", text="Set From Faces")
col.operator("transform.rotate_normal", text="Rotate")
col.operator("mesh.point_normals", text="Point To...")
row = layout.row(align=True)
row.operator("mesh.merge_normals", text="Merge")
row.operator("mesh.split_normals", text="Split")
col = layout.column(align=True)
col.operator_menu_enum("mesh.average_normals", "average_type")
col = layout.column(align=True)
col.label(text="Normal Vector:")
col.prop(toolsettings, "normal_vector", text="")
row = col.row(align=True)
row.operator("mesh.normals_tools", text="Copy").mode = 'COPY'
row.operator("mesh.normals_tools", text="Paste").mode = 'PASTE'
row = col.row(align=True)
row.operator("mesh.normals_tools", text="Multiply").mode = 'MULTIPLY'
row.operator("mesh.normals_tools", text="Add").mode = 'ADD'
col.operator("mesh.normals_tools", text="Reset").mode = 'RESET'
col = layout.column(align=True)
col.operator("mesh.smoothen_normals", text="Smoothen")
col = layout.column(align=True)
col.label(text="Face Strength:")
row = col.row(align=True)
row.prop(toolsettings, "face_strength", text="")
row.operator("mesh.mod_weighted_strength", text="", icon = "FACESEL").set = False
row.operator("mesh.mod_weighted_strength", text="", icon = "ZOOMIN").set = True
class VIEW3D_PT_tools_uvs(View3DPanel, Panel):
@@ -2073,6 +2121,7 @@ classes = (
VIEW3D_PT_tools_meshweight,
VIEW3D_PT_tools_add_mesh_edit,
VIEW3D_PT_tools_shading,
VIEW3D_PT_tools_normal,
VIEW3D_PT_tools_uvs,
VIEW3D_PT_tools_meshedit_options,
VIEW3D_PT_tools_transform_curve,

View File

@@ -93,6 +93,7 @@ void BKE_editmesh_update_linked_customdata(BMEditMesh *em);
void BKE_editmesh_color_free(BMEditMesh *em);
void BKE_editmesh_color_ensure(BMEditMesh *em, const char htype);
float (*BKE_editmesh_vertexCos_get_orco(BMEditMesh *em, int *r_numVerts))[3];
void BKE_editmesh_lnorspace_update(BMEditMesh *em);
/* editderivedmesh.c */
/* should really be defined in editmesh.c, but they use 'EditDerivedBMesh' */

View File

@@ -222,6 +222,8 @@ typedef struct MLoopNorSpace {
* - BMLoop pointers. */
struct LinkNode *loops;
char flags;
void *user_data; /* To be used for extended processing related to loop normal spaces (aka smooth fans). */
} MLoopNorSpace;
/**
* MLoopNorSpace.flags
@@ -237,6 +239,7 @@ typedef struct MLoopNorSpaceArray {
MLoopNorSpace **lspacearr; /* MLoop aligned array */
struct LinkNode *loops_pool; /* Allocated once, avoids to call BLI_linklist_prepend_arena() for each loop! */
char data_type; /* Whether we store loop indices, or pointers to BMLoop. */
int num_spaces; /* Number of clnors spaces defined in this array. */
struct MemArena *mem;
} MLoopNorSpaceArray;
/**

View File

@@ -214,7 +214,7 @@ static void emDM_calcLoopNormalsSpaceArray(
cd_loop_clnors_offset = clnors_data ? -1 : CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
BM_loops_calc_normal_vcos(bm, vertexCos, vertexNos, polyNos, use_split_normals, split_angle, loopNos,
r_lnors_spacearr, clnors_data, cd_loop_clnors_offset);
r_lnors_spacearr, clnors_data, cd_loop_clnors_offset, false);
#ifdef DEBUG_CLNORS
if (r_lnors_spacearr) {
int i;

View File

@@ -264,3 +264,26 @@ float (*BKE_editmesh_vertexCos_get_orco(BMEditMesh *em, int *r_numVerts))[3]
return orco;
}
void BKE_editmesh_lnorspace_update(BMEditMesh *em)
{
BMesh *bm = em->bm;
/* We need to create clnors data is none exist yet, otherwise there is no way to edit them. */
/* Similar code to MESH_OT_customdata_custom_splitnormals_add operator, we want to keep same shading
* in case we were using autosmooth so far... */
/* Note: there is a problem here, which is that is someone starts a normal editing operation on previously
* autosmooth-ed mesh, and cancel that operation, generated clnors data remain, with related sharp edges
* (and hence autosmooth is 'lost').
* Not sure how critical this is, and how to fix that issue? */
if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) {
Mesh *me = em->ob->data;
if (me->flag & ME_AUTOSMOOTH) {
BM_edges_sharp_from_angle_set(bm, me->smoothresh);
me->drawflag |= ME_DRAWSHARP;
}
}
BM_lnorspace_update(bm);
}

View File

@@ -455,6 +455,8 @@ void BKE_lnor_spacearr_init(MLoopNorSpaceArray *lnors_spacearr, const int numLoo
mem = lnors_spacearr->mem;
lnors_spacearr->lspacearr = BLI_memarena_calloc(mem, sizeof(MLoopNorSpace *) * (size_t)numLoops);
lnors_spacearr->loops_pool = BLI_memarena_alloc(mem, sizeof(LinkNode) * (size_t)numLoops);
lnors_spacearr->num_spaces = 0;
}
BLI_assert(ELEM(data_type, MLNOR_SPACEARR_BMLOOP_PTR, MLNOR_SPACEARR_LOOP_INDEX));
lnors_spacearr->data_type = data_type;
@@ -462,21 +464,24 @@ void BKE_lnor_spacearr_init(MLoopNorSpaceArray *lnors_spacearr, const int numLoo
void BKE_lnor_spacearr_clear(MLoopNorSpaceArray *lnors_spacearr)
{
BLI_memarena_clear(lnors_spacearr->mem);
lnors_spacearr->num_spaces = 0;
lnors_spacearr->lspacearr = NULL;
lnors_spacearr->loops_pool = NULL;
BLI_memarena_clear(lnors_spacearr->mem);
}
void BKE_lnor_spacearr_free(MLoopNorSpaceArray *lnors_spacearr)
{
BLI_memarena_free(lnors_spacearr->mem);
lnors_spacearr->num_spaces = 0;
lnors_spacearr->lspacearr = NULL;
lnors_spacearr->loops_pool = NULL;
BLI_memarena_free(lnors_spacearr->mem);
lnors_spacearr->mem = NULL;
}
MLoopNorSpace *BKE_lnor_space_create(MLoopNorSpaceArray *lnors_spacearr)
{
lnors_spacearr->num_spaces++;
return BLI_memarena_calloc(lnors_spacearr->mem, sizeof(MLoopNorSpace));
}

View File

@@ -38,6 +38,8 @@ struct BMEdge;
struct BMLoop;
struct BMFace;
struct MLoopNorSpaceArray;
struct BLI_mempool;
/* note: it is very important for BMHeader to start with two
@@ -236,6 +238,9 @@ typedef struct BMesh {
struct BLI_mempool *looplistpool;
#endif
struct MLoopNorSpaceArray *lnor_spacearr; /* Stores MLoopNorSpaceArray for this BMesh */
char spacearr_dirty;
/* should be copy of scene select mode */
/* stored in BMEditMesh too, this is a bit confusing,
* make sure they're in sync!
@@ -263,9 +268,33 @@ enum {
BM_FACE = 8
};
typedef struct BMLoopNorEditData {
int loop_index;
BMLoop *loop;
float niloc[3];
float nloc[3];
float *loc;
short *clnors_data;
} BMLoopNorEditData;
typedef struct BMLoopNorEditDataArray {
BMLoopNorEditData *lnor_editdata;
/* This one has full amount of loops, used to map loop index to actual BMLoopNorEditData struct. */
BMLoopNorEditData **lidx_to_lnor_editdata;
int cd_custom_normal_offset;
int totloop;
} BMLoopNorEditDataArray;
#define BM_ALL (BM_VERT | BM_EDGE | BM_LOOP | BM_FACE)
#define BM_ALL_NOLOOP (BM_VERT | BM_EDGE | BM_FACE)
enum {
BM_SPACEARR_DIRTY = 1 << 0,
BM_SPACEARR_DIRTY_ALL = 1 << 1,
BM_SPACEARR_BMO_SET = 1 << 2,
};
/* args for _Generic */
#define _BM_GENERIC_TYPE_ELEM_NONCONST \
void *, BMVert *, BMEdge *, BMLoop *, BMFace *, \

View File

@@ -30,6 +30,7 @@
#include "DNA_listBase.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BLI_linklist_stack.h"
#include "BLI_listbase.h"
@@ -260,6 +261,11 @@ void BM_mesh_data_free(BMesh *bm)
BLI_freelistN(&bm->selected);
if (bm->lnor_spacearr) {
BKE_lnor_spacearr_free(bm->lnor_spacearr);
MEM_freeN(bm->lnor_spacearr);
}
BMO_error_clear(bm);
}
@@ -313,6 +319,10 @@ void BM_mesh_free(BMesh *bm)
* Helpers for #BM_mesh_normals_update and #BM_verts_calc_normal_vcos
*/
/* We use that existing internal API flag, assuming no other tool using it would run concurrently to clnors editing. */
/* XXX Should we rather add a new internal flag? */
#define BM_LNORSPACE_UPDATE _FLAG_MF
typedef struct BMEdgesCalcVectorsData {
/* Read-only data. */
const float (*vcos)[3];
@@ -638,7 +648,8 @@ bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr)
* Will use first clnors_data array, and fallback to cd_loop_clnors_offset (use NULL and -1 to not use clnors). */
static void bm_mesh_loops_calc_normals(
BMesh *bm, const float (*vcos)[3], const float (*fnos)[3], float (*r_lnos)[3],
MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], const int cd_loop_clnors_offset)
MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2],
const int cd_loop_clnors_offset, const bool do_rebuild)
{
BMIter fiter;
BMFace *f_curr;
@@ -694,6 +705,11 @@ static void bm_mesh_loops_calc_normals(
l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr);
do {
if (do_rebuild && !BM_ELEM_API_FLAG_TEST(l_curr, BM_LNORSPACE_UPDATE) &&
!(bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL))
{
continue;
}
/* A smooth edge, we have to check for cyclic smooth fan case.
* If we find a new, never-processed cyclic smooth fan, we can do it now using that loop/edge as
* 'entry point', otherwise we can skip it. */
@@ -894,7 +910,10 @@ static void bm_mesh_loops_calc_normals(
clnors_avg[0] /= clnors_nbr;
clnors_avg[1] /= clnors_nbr;
/* Fix/update all clnors of this fan with computed average value. */
printf("Invalid clnors in this fan!\n");
/* Prints continuously when merge custom normals, so commenting. */
/* printf("Invalid clnors in this fan!\n"); */
while ((clnor = BLI_SMALLSTACK_POP(clnors))) {
//print_v2("org clnor", clnor);
clnor[0] = (short)clnors_avg[0];
@@ -1009,7 +1028,8 @@ void BM_mesh_loop_normals_update(
void BM_loops_calc_normal_vcos(
BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*fnos)[3],
const bool use_split_normals, const float split_angle, float (*r_lnos)[3],
MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], const int cd_loop_clnors_offset)
MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2],
const int cd_loop_clnors_offset, const bool do_rebuild)
{
const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
@@ -1019,7 +1039,8 @@ void BM_loops_calc_normal_vcos(
bm_mesh_edges_sharp_tag(bm, vnos, fnos, r_lnos, has_clnors ? (float)M_PI : split_angle, false);
/* Finish computing lnos by accumulating face normals in each fan of faces defined by sharp edges. */
bm_mesh_loops_calc_normals(bm, vcos, fnos, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset);
bm_mesh_loops_calc_normals(
bm, vcos, fnos, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset, do_rebuild);
}
else {
BLI_assert(!r_lnors_spacearr);
@@ -1041,6 +1062,380 @@ void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle)
bm_mesh_edges_sharp_tag(bm, NULL, NULL, NULL, split_angle, true);
}
void BM_lnorspacearr_store(BMesh *bm, float (*r_lnors)[3])
{
BLI_assert(bm->lnor_spacearr != NULL);
if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) {
BM_data_layer_add(bm, &bm->ldata, CD_CUSTOMLOOPNORMAL);
}
int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
BM_loops_calc_normal_vcos(
bm, NULL, NULL, NULL, true, M_PI, r_lnors, bm->lnor_spacearr, NULL, cd_loop_clnors_offset, false);
bm->spacearr_dirty &= ~(BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL);
}
/* will change later */
#define CLEAR_SPACEARRAY_THRESHOLD(x) ((x) / 2)
void BM_lnorspace_invalidate(BMesh *bm, const bool do_invalidate_all)
{
if (bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
return;
}
if (do_invalidate_all || bm->totvertsel > CLEAR_SPACEARRAY_THRESHOLD(bm->totvert)) {
bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
return;
}
if (bm->lnor_spacearr == NULL) {
bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
return;
}
BMVert *v;
BMLoop *l;
BMIter viter, liter;
/* Note: we could use temp tag of BMItem for that, but probably better not use it in such a low-level func?
* --mont29 */
BLI_bitmap *done_verts = BLI_BITMAP_NEW(bm->totvert, __func__);
BM_mesh_elem_index_ensure(bm, BM_VERT);
/* When we affect a given vertex, we may affect following smooth fans:
* - all smooth fans of said vertex;
* - all smooth fans of all immediate loop-neighbors vertices;
* This can be simplified as 'all loops of selected vertices and their immediate neighbors'
* need to be tagged for update.
*/
BM_ITER_MESH(v, &viter, bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
BM_ITER_ELEM(l, &liter, v, BM_LOOPS_OF_VERT) {
BM_ELEM_API_FLAG_ENABLE(l, BM_LNORSPACE_UPDATE);
/* Note that we only handle unselected neighbor vertices here, main loop will take care of
* selected ones. */
if (!BM_elem_flag_test(l->prev->v, BM_ELEM_SELECT) &&
!BLI_BITMAP_TEST(done_verts, BM_elem_index_get(l->prev->v)))
{
BMLoop *l_prev;
BMIter liter_prev;
BM_ITER_ELEM(l_prev, &liter_prev, l->prev->v, BM_LOOPS_OF_VERT) {
BM_ELEM_API_FLAG_ENABLE(l_prev, BM_LNORSPACE_UPDATE);
}
BLI_BITMAP_ENABLE(done_verts, BM_elem_index_get(l_prev->v));
}
if (!BM_elem_flag_test(l->next->v, BM_ELEM_SELECT) &&
!BLI_BITMAP_TEST(done_verts, BM_elem_index_get(l->next->v)))
{
BMLoop *l_next;
BMIter liter_next;
BM_ITER_ELEM(l_next, &liter_next, l->next->v, BM_LOOPS_OF_VERT) {
BM_ELEM_API_FLAG_ENABLE(l_next, BM_LNORSPACE_UPDATE);
}
BLI_BITMAP_ENABLE(done_verts, BM_elem_index_get(l_next->v));
}
}
BLI_BITMAP_ENABLE(done_verts, BM_elem_index_get(v));
}
}
MEM_freeN(done_verts);
bm->spacearr_dirty |= BM_SPACEARR_DIRTY;
}
void BM_lnorspace_rebuild(BMesh *bm, bool preserve_clnor)
{
BLI_assert(bm->lnor_spacearr != NULL);
if (!(bm->spacearr_dirty & (BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL))) {
return;
}
BMFace *f;
BMLoop *l;
BMIter fiter, liter;
float (*r_lnors)[3] = MEM_callocN(sizeof(*r_lnors) * bm->totloop, __func__);
float (*oldnors)[3] = preserve_clnor ? MEM_mallocN(sizeof(*oldnors) * bm->totloop, __func__) : NULL;
int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
BM_mesh_elem_index_ensure(bm, BM_LOOP);
if (preserve_clnor) {
BLI_assert(bm->lnor_spacearr->lspacearr != NULL);
BM_ITER_MESH(f, &fiter, bm, BM_FACES_OF_MESH) {
BM_ITER_ELEM(l, &liter, f, BM_LOOPS_OF_FACE) {
if (BM_ELEM_API_FLAG_TEST(l, BM_LNORSPACE_UPDATE) || bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
short(*clnor)[2] = BM_ELEM_CD_GET_VOID_P(l, cd_loop_clnors_offset);
int l_index = BM_elem_index_get(l);
BKE_lnor_space_custom_data_to_normal(bm->lnor_spacearr->lspacearr[l_index], *clnor, oldnors[l_index]);
}
}
}
}
if (bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
BKE_lnor_spacearr_clear(bm->lnor_spacearr);
}
BM_loops_calc_normal_vcos(
bm, NULL, NULL, NULL, true, M_PI, r_lnors, bm->lnor_spacearr, NULL, cd_loop_clnors_offset, true);
MEM_freeN(r_lnors);
BM_ITER_MESH(f, &fiter, bm, BM_FACES_OF_MESH) {
BM_ITER_ELEM(l, &liter, f, BM_LOOPS_OF_FACE) {
if (BM_ELEM_API_FLAG_TEST(l, BM_LNORSPACE_UPDATE) || bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
if (preserve_clnor) {
short(*clnor)[2] = BM_ELEM_CD_GET_VOID_P(l, cd_loop_clnors_offset);
int l_index = BM_elem_index_get(l);
BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[l_index], oldnors[l_index], *clnor);
}
BM_ELEM_API_FLAG_DISABLE(l, BM_LNORSPACE_UPDATE);
}
}
}
MEM_SAFE_FREE(oldnors);
bm->spacearr_dirty &= ~(BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL);
#ifndef NDEBUG
BM_lnorspace_err(bm);
#endif
}
void BM_lnorspace_update(BMesh *bm)
{
if (bm->lnor_spacearr == NULL) {
bm->lnor_spacearr = MEM_callocN(sizeof(*bm->lnor_spacearr), __func__);
}
if (bm->lnor_spacearr->lspacearr == NULL) {
float (*lnors)[3] = MEM_callocN(sizeof(*lnors) * bm->totloop, __func__);
BM_lnorspacearr_store(bm, lnors);
MEM_freeN(lnors);
}
else if(bm->spacearr_dirty & (BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL)){
BM_lnorspace_rebuild(bm, false);
}
}
/**
* Auxillary function only used by rebuild to detect if any spaces were not marked as invalid.
* Reports error if any of the lnor spaces change after rebuilding, meaning that all the possible
* lnor spaces to be rebuilt were not correctly marked.
*/
#ifndef NDEBUG
void BM_lnorspace_err(BMesh *bm)
{
bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
bool clear = true;
MLoopNorSpaceArray *temp = MEM_callocN(sizeof(*temp), __func__);
temp->lspacearr = NULL;
BKE_lnor_spacearr_init(temp, bm->totloop, MLNOR_SPACEARR_BMLOOP_PTR);
int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
float (*lnors)[3] = MEM_callocN(sizeof(*lnors) * bm->totloop, __func__);
BM_loops_calc_normal_vcos(bm, NULL, NULL, NULL, true, M_PI, lnors, temp, NULL, cd_loop_clnors_offset, true);
for (int i = 0; i < bm->totloop; i++) {
int j = 0;
j += compare_ff(temp->lspacearr[i]->ref_alpha, bm->lnor_spacearr->lspacearr[i]->ref_alpha, 1e-4f);
j += compare_ff(temp->lspacearr[i]->ref_beta, bm->lnor_spacearr->lspacearr[i]->ref_beta, 1e-4f);
j += compare_v3v3(temp->lspacearr[i]->vec_lnor, bm->lnor_spacearr->lspacearr[i]->vec_lnor, 1e-4f);
j += compare_v3v3(temp->lspacearr[i]->vec_ortho, bm->lnor_spacearr->lspacearr[i]->vec_ortho, 1e-4f);
j += compare_v3v3(temp->lspacearr[i]->vec_ref, bm->lnor_spacearr->lspacearr[i]->vec_ref, 1e-4f);
if (j != 5) {
clear = false;
break;
}
}
BKE_lnor_spacearr_free(temp);
MEM_freeN(temp);
MEM_freeN(lnors);
BLI_assert(clear);
bm->spacearr_dirty &= ~BM_SPACEARR_DIRTY_ALL;
}
#endif
static void bm_loop_normal_mark_indiv_do_loop(
BMLoop *l, BLI_bitmap *loops, MLoopNorSpaceArray *lnor_spacearr, int *totloopsel)
{
if (l != NULL) {
const int l_idx = BM_elem_index_get(l);
if (!BLI_BITMAP_TEST(loops, BM_elem_index_get(l))) {
/* If vert and face selected share a loop, mark it for editing. */
BLI_BITMAP_ENABLE(loops, l_idx);
(*totloopsel)++;
/* Mark all loops in same loop normal space (aka smooth fan). */
if ((lnor_spacearr->lspacearr[l_idx]->flags & MLNOR_SPACE_IS_SINGLE) == 0) {
for (LinkNode *node = lnor_spacearr->lspacearr[l_idx]->loops; node; node = node->next) {
const int lfan_idx = BM_elem_index_get((BMLoop *)node->link);
if (!BLI_BITMAP_TEST(loops, lfan_idx)) {
BLI_BITMAP_ENABLE(loops, lfan_idx);
(*totloopsel)++;
}
}
}
}
}
}
/* Mark the individual clnors to be edited, if multiple selection methods are used. */
static int bm_loop_normal_mark_indiv(BMesh *bm, BLI_bitmap *loops)
{
BMEditSelection *ese, *ese_prev;
int totloopsel = 0;
BM_mesh_elem_index_ensure(bm, BM_LOOP);
BLI_assert(bm->lnor_spacearr != NULL);
BLI_assert(bm->lnor_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR);
/* Goes from last selected to the first selected element. */
for (ese = bm->selected.last; ese; ese = ese->prev) {
if (ese->htype == BM_FACE) {
ese_prev = ese;
/* If current face is selected, then any verts to be edited must have been selected before it. */
while ((ese_prev = ese_prev->prev)) {
if (ese_prev->htype == BM_VERT) {
bm_loop_normal_mark_indiv_do_loop(
BM_face_vert_share_loop((BMFace *)ese->ele, (BMVert *)ese_prev->ele),
loops, bm->lnor_spacearr, &totloopsel);
}
else if (ese_prev->htype == BM_EDGE) {
bm_loop_normal_mark_indiv_do_loop(
BM_face_vert_share_loop((BMFace *)ese->ele, ((BMEdge *)ese_prev->ele)->v1),
loops, bm->lnor_spacearr, &totloopsel);
bm_loop_normal_mark_indiv_do_loop(
BM_face_vert_share_loop((BMFace *)ese->ele, ((BMEdge *)ese_prev->ele)->v2),
loops, bm->lnor_spacearr, &totloopsel);
}
}
}
}
return totloopsel;
}
static void loop_normal_editdata_init(BMesh *bm, BMLoopNorEditData *lnor_ed, BMVert *v, BMLoop *l, const int offset)
{
BLI_assert(bm->lnor_spacearr != NULL);
BLI_assert(bm->lnor_spacearr->lspacearr != NULL);
const int l_index = BM_elem_index_get(l);
short *clnors_data = BM_ELEM_CD_GET_VOID_P(l, offset);
lnor_ed->loop_index = l_index;
lnor_ed->loop = l;
float custom_normal[3];
BKE_lnor_space_custom_data_to_normal(bm->lnor_spacearr->lspacearr[l_index], clnors_data, custom_normal);
lnor_ed->clnors_data = clnors_data;
copy_v3_v3(lnor_ed->nloc, custom_normal);
copy_v3_v3(lnor_ed->niloc, custom_normal);
lnor_ed->loc = v->co;
}
BMLoopNorEditDataArray *BM_loop_normal_editdata_array_init(BMesh *bm)
{
BMLoop *l;
BMVert *v;
BMIter liter, viter;
bool verts = (bm->selectmode & SCE_SELECT_VERTEX) != 0;
bool edges = (bm->selectmode & SCE_SELECT_EDGE) != 0;
bool faces = (bm->selectmode & SCE_SELECT_FACE) != 0;
int totloopsel = 0;
BLI_assert(bm->spacearr_dirty == 0);
BMLoopNorEditDataArray *lnors_ed_arr = MEM_mallocN(sizeof(*lnors_ed_arr), __func__);
lnors_ed_arr->lidx_to_lnor_editdata = MEM_callocN(sizeof(*lnors_ed_arr->lidx_to_lnor_editdata) * bm->totloop, __func__);
if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) {
BM_data_layer_add(bm, &bm->ldata, CD_CUSTOMLOOPNORMAL);
}
const int cd_custom_normal_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
BM_mesh_elem_index_ensure(bm, BM_LOOP);
BLI_bitmap *loops = BLI_BITMAP_NEW(bm->totloop, __func__);
if (faces && (verts || edges)) {
/* More than one selection mode, check for individual normals to edit. */
totloopsel = bm_loop_normal_mark_indiv(bm, loops);
}
if (totloopsel) {
BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata = MEM_mallocN(sizeof(*lnor_ed) * totloopsel, __func__);
BM_ITER_MESH(v, &viter, bm, BM_VERTS_OF_MESH) {
BM_ITER_ELEM(l, &liter, v, BM_LOOPS_OF_VERT) {
if (BLI_BITMAP_TEST(loops, BM_elem_index_get(l))) {
loop_normal_editdata_init(bm, lnor_ed, v, l, cd_custom_normal_offset);
lnors_ed_arr->lidx_to_lnor_editdata[BM_elem_index_get(l)] = lnor_ed;
lnor_ed++;
}
}
}
lnors_ed_arr->totloop = totloopsel;
}
else { /* If multiple selection modes are inactive OR no such loop is found, fall back to editing all loops. */
totloopsel = BM_total_loop_select(bm);
BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata = MEM_mallocN(sizeof(*lnor_ed) * totloopsel, __func__);
BM_ITER_MESH(v, &viter, bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
BM_ITER_ELEM(l, &liter, v, BM_LOOPS_OF_VERT) {
loop_normal_editdata_init(bm, lnor_ed, v, l, cd_custom_normal_offset);
lnors_ed_arr->lidx_to_lnor_editdata[BM_elem_index_get(l)] = lnor_ed;
lnor_ed++;
}
}
}
lnors_ed_arr->totloop = totloopsel;
}
MEM_freeN(loops);
lnors_ed_arr->cd_custom_normal_offset = cd_custom_normal_offset;
return lnors_ed_arr;
}
void BM_loop_normal_editdata_array_free(BMLoopNorEditDataArray *lnors_ed_arr)
{
MEM_SAFE_FREE(lnors_ed_arr->lnor_editdata);
MEM_SAFE_FREE(lnors_ed_arr->lidx_to_lnor_editdata);
MEM_freeN(lnors_ed_arr);
}
int BM_total_loop_select(BMesh *bm)
{
int r_sel = 0;
BMVert *v;
BMIter viter;
BM_ITER_MESH(v, &viter, bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
r_sel += BM_vert_face_count(v);
}
}
return r_sel;
}
static void UNUSED_FUNCTION(bm_mdisps_space_set)(Object *ob, BMesh *bm, int from, int to)
{
/* switch multires data out of tangent space */
@@ -1141,6 +1536,7 @@ void bmesh_edit_end(BMesh *bm, BMOpTypeFlag type_flag)
/* compute normals, clear temp flags and flush selections */
if (type_flag & BMO_OPTYPE_FLAG_NORMALS_CALC) {
bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
BM_mesh_normals_update(bm);
}
@@ -1157,6 +1553,9 @@ void bmesh_edit_end(BMesh *bm, BMOpTypeFlag type_flag)
if ((type_flag & BMO_OPTYPE_FLAG_SELECT_VALIDATE) == 0) {
bm->selected = select_history;
}
if (type_flag & BMO_OPTYPE_FLAG_INVALIDATE_CLNOR_ALL) {
bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
}
}
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)

View File

@@ -29,6 +29,7 @@
struct BMAllocTemplate;
struct MLoopNorSpaceArray;
struct BMLoopNorEditDataArray;
void BM_mesh_elem_toolflags_ensure(BMesh *bm);
void BM_mesh_elem_toolflags_clear(BMesh *bm);
@@ -50,9 +51,22 @@ void BM_verts_calc_normal_vcos(BMesh *bm, const float (*fnos)[3], const float (*
void BM_loops_calc_normal_vcos(
BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*pnos)[3],
const bool use_split_normals, const float split_angle, float (*r_lnos)[3],
struct MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], const int cd_loop_clnors_offset);
struct MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2],
const int cd_loop_clnors_offset, const bool do_rebuild);
bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr);
void BM_lnorspacearr_store(BMesh *bm, float (*r_lnors)[3]);
void BM_lnorspace_invalidate(BMesh *bm, const bool do_invalidate_all);
void BM_lnorspace_rebuild(BMesh *bm, bool preserve_clnor);
void BM_lnorspace_update(BMesh *bm);
#ifndef NDEBUG
void BM_lnorspace_err(BMesh *bm);
#endif
/* Loop Generics */
struct BMLoopNorEditDataArray *BM_loop_normal_editdata_array_init(BMesh *bm);
void BM_loop_normal_editdata_array_free(struct BMLoopNorEditDataArray *lnors_ed_arr);
int BM_total_loop_select(BMesh *bm);
void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle);

View File

@@ -244,6 +244,7 @@ typedef enum {
BMO_OPTYPE_FLAG_NORMALS_CALC = (1 << 1),
BMO_OPTYPE_FLAG_SELECT_FLUSH = (1 << 2),
BMO_OPTYPE_FLAG_SELECT_VALIDATE = (1 << 3),
BMO_OPTYPE_FLAG_INVALIDATE_CLNOR_ALL = (1 << 4),
} BMOpTypeFlag;
typedef struct BMOperator {

View File

@@ -171,6 +171,7 @@ int ED_operator_object_active_editable_font(struct bContext *C);
int ED_operator_editmesh(struct bContext *C);
int ED_operator_editmesh_view3d(struct bContext *C);
int ED_operator_editmesh_region_view3d(struct bContext *C);
int ED_operator_editmesh_auto_smooth(struct bContext *C);
int ED_operator_editarmature(struct bContext *C);
int ED_operator_editcurve(struct bContext *C);
int ED_operator_editcurve_3d(struct bContext *C);

View File

@@ -88,6 +88,7 @@ enum TfmMode {
TFM_VERT_SLIDE,
TFM_SEQ_SLIDE,
TFM_BONE_ENVELOPE_DIST,
TFM_NORMAL_ROTATION,
};
/* TRANSFORM CONTEXTS */
@@ -150,6 +151,7 @@ int BIF_countTransformOrientation(const struct bContext *C);
#define P_NO_TEXSPACE (1 << 11)
#define P_CENTER (1 << 12)
#define P_GPENCIL_EDIT (1 << 13)
#define P_CLNOR_INVALIDATE (1 << 14)
void Transform_Properties(struct wmOperatorType *ot, int flags);

File diff suppressed because it is too large Load Diff

View File

@@ -589,6 +589,8 @@ static void undomesh_to_editmesh(UndoMesh *um, BMEditMesh *em, Mesh *obmesh)
bm->selectmode = um->selectmode;
em->ob = ob;
bm->spacearr_dirty = BM_SPACEARR_DIRTY_ALL;
/* T35170: Restore the active key on the RealMesh. Otherwise 'fake' offset propagation happens
* if the active is a basis for any other. */
if (key && (key->type == KEY_RELATIVE)) {

View File

@@ -1349,7 +1349,10 @@ void EDBM_update_generic(BMEditMesh *em, const bool do_tessface, const bool is_d
/* in debug mode double check we didn't need to recalculate */
BLI_assert(BM_mesh_elem_table_check(em->bm) == true);
}
if (em->bm->spacearr_dirty & BM_SPACEARR_BMO_SET) {
BM_lnorspace_invalidate(em->bm, false);
em->bm->spacearr_dirty &= ~BM_SPACEARR_BMO_SET;
}
/* don't keep stale derivedMesh data around, see: [#38872] */
BKE_editmesh_free_derivedmesh(em);

View File

@@ -229,6 +229,16 @@ void MESH_OT_duplicate(struct wmOperatorType *ot);
void MESH_OT_merge(struct wmOperatorType *ot);
void MESH_OT_remove_doubles(struct wmOperatorType *ot);
void MESH_OT_poke(struct wmOperatorType *ot);
void MESH_OT_point_normals(struct wmOperatorType *ot);
void MESH_OT_merge_normals(struct wmOperatorType *ot);
void MESH_OT_split_normals(struct wmOperatorType *ot);
void MESH_OT_normals_tools(struct wmOperatorType *ot);
void MESH_OT_set_normals_from_faces(struct wmOperatorType *ot);
void MESH_OT_average_normals(struct wmOperatorType *ot);
void MESH_OT_smoothen_normals(struct wmOperatorType *ot);
void MESH_OT_mod_weighted_strength(struct wmOperatorType *ot);
struct wmKeyMap *point_normals_modal_keymap(wmKeyConfig *keyconf);
#ifdef WITH_FREESTYLE
void MESH_OT_mark_freestyle_edge(struct wmOperatorType *ot);

View File

@@ -192,6 +192,15 @@ void ED_operatortypes_mesh(void)
WM_operatortype_append(MESH_OT_symmetrize);
WM_operatortype_append(MESH_OT_symmetry_snap);
WM_operatortype_append(MESH_OT_point_normals);
WM_operatortype_append(MESH_OT_merge_normals);
WM_operatortype_append(MESH_OT_split_normals);
WM_operatortype_append(MESH_OT_normals_tools);
WM_operatortype_append(MESH_OT_set_normals_from_faces);
WM_operatortype_append(MESH_OT_average_normals);
WM_operatortype_append(MESH_OT_smoothen_normals);
WM_operatortype_append(MESH_OT_mod_weighted_strength);
#ifdef WITH_GAMEENGINE
WM_operatortype_append(MESH_OT_navmesh_make);
WM_operatortype_append(MESH_OT_navmesh_face_copy);
@@ -423,6 +432,8 @@ void ED_keymap_mesh(wmKeyConfig *keyconf)
WM_keymap_add_item(keymap, "MESH_OT_split", YKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "MESH_OT_vert_connect_path", JKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "MESH_OT_point_normals", LKEY, KM_PRESS, KM_ALT, 0);
/* Vertex Slide */
WM_keymap_add_item(keymap, "TRANSFORM_OT_vert_slide", VKEY, KM_PRESS, KM_SHIFT, 0);
/* use KM_CLICK because same key is used for tweaks */
@@ -466,5 +477,6 @@ void ED_keymap_mesh(wmKeyConfig *keyconf)
ED_keymap_proportional_editmode(keyconf, keymap, true);
knifetool_modal_keymap(keyconf);
point_normals_modal_keymap(keyconf);
}

View File

@@ -46,6 +46,7 @@
#include "DNA_gpencil_types.h"
#include "DNA_scene_types.h"
#include "DNA_meta_types.h"
#include "DNA_mesh_types.h"
#include "DNA_mask_types.h"
#include "DNA_node_types.h"
#include "DNA_userdef_types.h"
@@ -353,6 +354,15 @@ int ED_operator_editmesh_region_view3d(bContext *C)
return 0;
}
int ED_operator_editmesh_auto_smooth(bContext *C)
{
Object *obedit = CTX_data_edit_object(C);
if (obedit && obedit->type == OB_MESH && (((Mesh *)(obedit->data))->flag & ME_AUTOSMOOTH)) {
return NULL != BKE_editmesh_from_object(obedit);
}
return 0;
}
int ED_operator_editarmature(bContext *C)
{
Object *obedit = CTX_data_edit_object(C);

View File

@@ -1181,6 +1181,7 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto
case eModifierType_DataTransfer:
UI_icon_draw(x, y, ICON_MOD_DATA_TRANSFER); break;
case eModifierType_NormalEdit:
case eModifierType_WeightedNormal:
UI_icon_draw(x, y, ICON_MOD_NORMALEDIT); break;
/* Default */
case eModifierType_None:

View File

@@ -41,6 +41,7 @@
#include "DNA_armature_types.h"
#include "DNA_constraint_types.h"
#include "DNA_mask_types.h"
#include "DNA_mesh_types.h"
#include "DNA_movieclip_types.h"
#include "DNA_scene_types.h" /* PET modes */
@@ -62,6 +63,7 @@
#include "BKE_particle.h"
#include "BKE_unit.h"
#include "BKE_mask.h"
#include "BKE_mesh.h"
#include "BKE_report.h"
#include "BIF_gl.h"
@@ -86,6 +88,7 @@
#include "UI_resources.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "BLF_api.h"
#include "BLT_translation.h"
@@ -106,6 +109,7 @@ static void postInputRotation(TransInfo *t, float values[3]);
static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], const short around);
static void initSnapSpatial(TransInfo *t, float r_snap[3]);
static void storeCustomLNorValue(TransInfo *t, BMesh *bm);
/* Transform Callbacks */
static void initBend(TransInfo *t);
@@ -131,6 +135,9 @@ static void applyToSphere(TransInfo *t, const int mval[2]);
static void initRotation(TransInfo *t);
static void applyRotation(TransInfo *t, const int mval[2]);
static void initNormalRotation(TransInfo *t);
static void applyNormalRotation(TransInfo *t, const int mval[2]);
static void initShrinkFatten(TransInfo *t);
static void applyShrinkFatten(TransInfo *t, const int mval[2]);
@@ -1465,6 +1472,20 @@ int transformEvent(TransInfo *t, const wmEvent *event)
handled = true;
}
break;
case NKEY:
if (ELEM(t->mode, TFM_ROTATION)) {
if (t->obedit && t->obedit->type == OB_MESH) {
if (((Mesh *)(t->obedit->data))->flag & ME_AUTOSMOOTH) {
restoreTransObjects(t);
resetTransModal(t);
resetTransRestrictions(t);
initNormalRotation(t);
t->redraw = TREDRAW_HARD;
handled = true;
}
}
}
break;
default:
break;
}
@@ -2303,6 +2324,9 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
case TFM_SEQ_SLIDE:
initSeqSlide(t);
break;
case TFM_NORMAL_ROTATION:
initNormalRotation(t);
break;
}
if (t->state == TRANS_CANCEL) {
@@ -2359,6 +2383,34 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
t->flag |= T_AUTOVALUES;
}
if ((prop = RNA_struct_find_property(op->ptr, "preserve_clnor"))) {
if (t->obedit && t->obedit->type == OB_MESH && (((Mesh *)(t->obedit->data))->flag & ME_AUTOSMOOTH)) {
BMEditMesh *em = BKE_editmesh_from_object(t->obedit);
bool do_skip = false;
/* Currently only used for two of three most frequent transform ops, can include more ops.
* Note that scaling cannot be included here, non-uniform scaling will affect normals. */
if (ELEM(t->mode, TFM_TRANSLATION, TFM_ROTATION)) {
if (em->bm->totvertsel == em->bm->totvert) {
/* No need to invalidate if whole mesh is selected. */
do_skip = true;
}
}
if (t->flag & T_MODAL) {
RNA_property_boolean_set(op->ptr, prop, false);
}
else if (!do_skip) {
const bool preserve_clnor = RNA_property_boolean_get(op->ptr, prop);
if (preserve_clnor) {
BKE_editmesh_lnorspace_update(em);
t->flag |= T_CLNOR_REBUILD;
}
BM_lnorspace_invalidate(em->bm, true);
}
}
}
t->context = NULL;
return 1;
@@ -2419,6 +2471,10 @@ int transformEnd(bContext *C, TransInfo *t)
restoreTransObjects(t); // calls recalcData()
}
else {
if (t->flag & T_CLNOR_REBUILD) {
BMEditMesh *em = BKE_editmesh_from_object(t->obedit);
BM_lnorspace_rebuild(em->bm, true);
}
exit_code = OPERATOR_FINISHED;
}
@@ -3714,6 +3770,28 @@ static void initRotation(TransInfo *t)
copy_v3_v3(t->axis_orig, t->axis);
}
/* Used by Transform Rotation and Transform Normal Rotation */
static void headerRotation(TransInfo *t, char str[UI_MAX_DRAW_STR], float final)
{
size_t ofs = 0;
if (hasNumInput(&t->num)) {
char c[NUM_STR_REP_LEN];
outputNumInput(&(t->num), c, &t->scene->unit);
ofs += BLI_snprintf(str + ofs, UI_MAX_DRAW_STR - ofs, IFACE_("Rot: %s %s %s"), &c[0], t->con.text, t->proptext);
}
else {
ofs += BLI_snprintf(str + ofs, UI_MAX_DRAW_STR - ofs, IFACE_("Rot: %.2f%s %s"),
RAD2DEGF(final), t->con.text, t->proptext);
}
if (t->flag & T_PROP_EDIT_ALL) {
ofs += BLI_snprintf(str + ofs, UI_MAX_DRAW_STR - ofs, IFACE_(" Proportional size: %.2f"), t->prop_size);
}
}
/**
* Applies values of rotation to `td->loc` and `td->ext->quat`
* based on a rotation matrix (mat) and a pivot (center).
@@ -3979,7 +4057,6 @@ static void applyRotationValue(TransInfo *t, float angle, float axis[3])
static void applyRotation(TransInfo *t, const int UNUSED(mval[2]))
{
char str[UI_MAX_DRAW_STR];
size_t ofs = 0;
float final;
@@ -4002,21 +4079,7 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2]))
t->values[0] = final;
if (hasNumInput(&t->num)) {
char c[NUM_STR_REP_LEN];
outputNumInput(&(t->num), c, &t->scene->unit);
ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, IFACE_("Rot: %s %s %s"), &c[0], t->con.text, t->proptext);
}
else {
ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, IFACE_("Rot: %.2f%s %s"),
RAD2DEGF(final), t->con.text, t->proptext);
}
if (t->flag & T_PROP_EDIT_ALL) {
ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, IFACE_(" Proportional size: %.2f"), t->prop_size);
}
headerRotation(t, str, final);
applyRotationValue(t, final, t->axis);
@@ -4142,6 +4205,122 @@ static void applyTrackball(TransInfo *t, const int UNUSED(mval[2]))
/** \} */
/* -------------------------------------------------------------------- */
/* Transform (Normal Rotation) */
/** \name Transform Normal Rotation
* \{ */
static void storeCustomLNorValue(TransInfo *t, BMesh *bm)
{
BMLoopNorEditDataArray *lnors_ed_arr = BM_loop_normal_editdata_array_init(bm);
BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
t->custom.mode.data = lnors_ed_arr;
t->custom.mode.free_cb = freeCustomNormalArray;
}
void freeCustomNormalArray(TransInfo *t, TransCustomData *custom_data)
{
BMLoopNorEditDataArray *lnors_ed_arr = custom_data->data;
if (t->state == TRANS_CANCEL) {
BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
BMEditMesh *em = BKE_editmesh_from_object(t->obedit);
BMesh *bm = em->bm;
for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { /* Restore custom loop normal on cancel */
BKE_lnor_space_custom_normal_to_data(
bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->niloc, lnor_ed->clnors_data);
}
}
BM_loop_normal_editdata_array_free(lnors_ed_arr);
t->custom.mode.data = NULL;
t->custom.mode.free_cb = NULL;
}
static void initNormalRotation(TransInfo *t)
{
t->mode = TFM_NORMAL_ROTATION;
t->transform = applyNormalRotation;
setInputPostFct(&t->mouse, postInputRotation);
initMouseInputMode(t, &t->mouse, INPUT_ANGLE);
t->idx_max = 0;
t->num.idx_max = 0;
t->snap[0] = 0.0f;
t->snap[1] = DEG2RAD(5.0);
t->snap[2] = DEG2RAD(1.0);
copy_v3_fl(t->num.val_inc, t->snap[2]);
t->num.unit_sys = t->scene->unit.system;
t->num.unit_use_radians = (t->scene->unit.system_rotation == USER_UNIT_ROT_RADIANS);
t->num.unit_type[0] = B_UNIT_ROTATION;
BMEditMesh *em = BKE_editmesh_from_object(t->obedit);
BMesh *bm = em->bm;
BKE_editmesh_lnorspace_update(em);
storeCustomLNorValue(t, bm);
negate_v3_v3(t->axis, t->viewinv[2]);
normalize_v3(t->axis);
copy_v3_v3(t->axis_orig, t->axis);
}
/* Works by getting custom normal from clnor_data, transform, then store */
static void applyNormalRotation(TransInfo *t, const int UNUSED(mval[2]))
{
BMEditMesh *em = BKE_editmesh_from_object(t->obedit);
BMesh *bm = em->bm;
char str[UI_MAX_DRAW_STR];
if ((t->con.mode & CON_APPLY) && t->con.applyRot) {
t->con.applyRot(t, NULL, t->axis, NULL);
}
else {
/* reset axis if constraint is not set */
copy_v3_v3(t->axis, t->axis_orig);
}
BMLoopNorEditDataArray *lnors_ed_arr = t->custom.mode.data;
BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
float axis[3];
float mat[3][3];
float angle = t->values[0];
copy_v3_v3(axis, t->axis);
snapGridIncrement(t, &angle);
applySnapping(t, &angle);
applyNumInput(&t->num, &angle);
headerRotation(t, str, angle);
axis_angle_normalized_to_mat3(mat, axis, angle);
for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
mul_v3_m3v3(lnor_ed->nloc, mat, lnor_ed->niloc);
BKE_lnor_space_custom_normal_to_data(
bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->nloc, lnor_ed->clnors_data);
}
recalcData(t);
ED_area_headerprint(t->sa, str);
}
/** \} */
/* -------------------------------------------------------------------- */
/* Transform (Translation) */

View File

@@ -536,6 +536,8 @@ typedef struct TransInfo {
/** #TransInfo.center has been set, don't change it. */
#define T_OVERRIDE_CENTER (1 << 25)
#define T_CLNOR_REBUILD (1 << 26)
/* TransInfo->modifiers */
#define MOD_CONSTRAINT_SELECT 0x01
#define MOD_PRECISION 0x02
@@ -798,6 +800,8 @@ bool applyTransformOrientation(const struct bContext *C, float mat[3][3], char r
int getTransformOrientation_ex(const struct bContext *C, float normal[3], float plane[3], const short around);
int getTransformOrientation(const struct bContext *C, float normal[3], float plane[3]);
void freeCustomNormalArray(TransInfo *t, TransCustomData *custom_data);
void freeEdgeSlideTempFaces(EdgeSlideData *sld);
void freeEdgeSlideVerts(TransInfo *t, TransCustomData *custom_data);
void projectEdgeSlideData(TransInfo *t, bool is_final);

View File

@@ -1072,6 +1072,9 @@ void resetTransModal(TransInfo *t)
else if (t->mode == TFM_VERT_SLIDE) {
freeVertSlideVerts(t, &t->custom.mode);
}
else if (t->mode == TFM_NORMAL_ROTATION) {
freeCustomNormalArray(t, &t->custom.mode);
}
}
void resetTransRestrictions(TransInfo *t)

View File

@@ -81,6 +81,7 @@ static const char OP_VERT_SLIDE[] = "TRANSFORM_OT_vert_slide";
static const char OP_EDGE_CREASE[] = "TRANSFORM_OT_edge_crease";
static const char OP_EDGE_BWEIGHT[] = "TRANSFORM_OT_edge_bevelweight";
static const char OP_SEQ_SLIDE[] = "TRANSFORM_OT_seq_slide";
static const char OP_NORMAL_ROTATION[] = "TRANSFORM_OT_rotate_normal";
static void TRANSFORM_OT_translate(struct wmOperatorType *ot);
static void TRANSFORM_OT_rotate(struct wmOperatorType *ot);
@@ -99,6 +100,7 @@ static void TRANSFORM_OT_vert_slide(struct wmOperatorType *ot);
static void TRANSFORM_OT_edge_crease(struct wmOperatorType *ot);
static void TRANSFORM_OT_edge_bevelweight(struct wmOperatorType *ot);
static void TRANSFORM_OT_seq_slide(struct wmOperatorType *ot);
static void TRANSFORM_OT_rotate_normal(struct wmOperatorType *ot);
static TransformModeItem transform_modes[] =
{
@@ -119,6 +121,7 @@ static TransformModeItem transform_modes[] =
{OP_EDGE_CREASE, TFM_CREASE, TRANSFORM_OT_edge_crease},
{OP_EDGE_BWEIGHT, TFM_BWEIGHT, TRANSFORM_OT_edge_bevelweight},
{OP_SEQ_SLIDE, TFM_SEQ_SLIDE, TRANSFORM_OT_seq_slide},
{OP_NORMAL_ROTATION, TFM_NORMAL_ROTATION, TRANSFORM_OT_rotate_normal},
{NULL, 0}
};
@@ -495,6 +498,39 @@ static int transform_invoke(bContext *C, wmOperator *op, const wmEvent *event)
}
}
static bool transform_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop)
{
const char *prop_id = RNA_property_identifier(prop);
/* Only show preserve_clnor option if requested (kinda hackish, we need to take that decision based on context... */
if (STREQ(prop_id, "preserve_clnor")) {
return RNA_boolean_get(ptr, "show_preserve_clnor");;
}
/* Else, show it! */
return true;
}
static void transform_ui(bContext *C, wmOperator *op)
{
uiLayout *layout = op->layout;
wmWindowManager *wm = CTX_wm_manager(C);
PointerRNA ptr;
Object *obedit = CTX_data_edit_object(C);
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "show_preserve_clnor");
if (prop) {
RNA_property_boolean_set(
op->ptr, prop,
(obedit && obedit->type == OB_MESH && (((Mesh *)(obedit->data))->flag & ME_AUTOSMOOTH)));
}
RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
/* Main auto-draw call */
uiDefAutoButsRNA(layout, &ptr, transform_draw_check_prop, '\0');
}
void Transform_Properties(struct wmOperatorType *ot, int flags)
{
PropertyRNA *prop;
@@ -580,6 +616,12 @@ void Transform_Properties(struct wmOperatorType *ot, int flags)
prop = RNA_def_boolean(ot->srna, "use_accurate", 0, "Accurate", "Use accurate transformation");
RNA_def_property_flag(prop, PROP_HIDDEN);
}
if (flags & P_CLNOR_INVALIDATE) {
RNA_def_boolean(ot->srna, "preserve_clnor", false, "Preserve Normals", "Keep custom normals during transform");
prop = RNA_def_boolean(ot->srna, "show_preserve_clnor", false, "Show Preserve Normals", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
}
static void TRANSFORM_OT_translate(struct wmOperatorType *ot)
@@ -596,10 +638,11 @@ static void TRANSFORM_OT_translate(struct wmOperatorType *ot)
ot->modal = transform_modal;
ot->cancel = transform_cancel;
ot->poll = ED_operator_screenactive;
ot->ui = transform_ui;
RNA_def_float_vector_xyz(ot->srna, "value", 3, NULL, -FLT_MAX, FLT_MAX, "Vector", "", -FLT_MAX, FLT_MAX);
Transform_Properties(ot, P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_ALIGN_SNAP | P_OPTIONS | P_GPENCIL_EDIT);
Transform_Properties(ot, P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_ALIGN_SNAP | P_OPTIONS | P_GPENCIL_EDIT | P_CLNOR_INVALIDATE);
}
static void TRANSFORM_OT_resize(struct wmOperatorType *ot)
@@ -616,12 +659,12 @@ static void TRANSFORM_OT_resize(struct wmOperatorType *ot)
ot->modal = transform_modal;
ot->cancel = transform_cancel;
ot->poll = ED_operator_screenactive;
ot->ui = transform_ui;
RNA_def_float_vector(ot->srna, "value", 3, VecOne, -FLT_MAX, FLT_MAX, "Vector", "", -FLT_MAX, FLT_MAX);
Transform_Properties(
ot, P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_GEO_SNAP | P_OPTIONS | P_GPENCIL_EDIT | P_CENTER);
}
ot, P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_GEO_SNAP | P_OPTIONS | P_GPENCIL_EDIT | P_CENTER | P_CLNOR_INVALIDATE);}
static int skin_resize_poll(bContext *C)
{
@@ -647,6 +690,7 @@ static void TRANSFORM_OT_skin_resize(struct wmOperatorType *ot)
ot->modal = transform_modal;
ot->cancel = transform_cancel;
ot->poll = skin_resize_poll;
ot->ui = transform_ui;
RNA_def_float_vector(ot->srna, "value", 3, VecOne, -FLT_MAX, FLT_MAX, "Vector", "", -FLT_MAX, FLT_MAX);
@@ -667,12 +711,12 @@ static void TRANSFORM_OT_trackball(struct wmOperatorType *ot)
ot->modal = transform_modal;
ot->cancel = transform_cancel;
ot->poll = ED_operator_screenactive;
ot->ui = transform_ui;
/* Maybe we could use float_vector_xyz here too? */
RNA_def_float_rotation(ot->srna, "value", 2, NULL, -FLT_MAX, FLT_MAX, "Angle", "", -FLT_MAX, FLT_MAX);
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT | P_CENTER);
}
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT | P_CENTER | P_CLNOR_INVALIDATE);}
static void TRANSFORM_OT_rotate(struct wmOperatorType *ot)
{
@@ -688,12 +732,12 @@ static void TRANSFORM_OT_rotate(struct wmOperatorType *ot)
ot->modal = transform_modal;
ot->cancel = transform_cancel;
ot->poll = ED_operator_screenactive;
ot->ui = transform_ui;
RNA_def_float_rotation(ot->srna, "value", 0, NULL, -FLT_MAX, FLT_MAX, "Angle", "", -M_PI * 2, M_PI * 2);
Transform_Properties(
ot, P_AXIS | P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_GEO_SNAP | P_GPENCIL_EDIT | P_CENTER);
}
ot, P_AXIS | P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_GEO_SNAP | P_GPENCIL_EDIT | P_CENTER | P_CLNOR_INVALIDATE);}
static void TRANSFORM_OT_tilt(struct wmOperatorType *ot)
{
@@ -712,6 +756,7 @@ static void TRANSFORM_OT_tilt(struct wmOperatorType *ot)
ot->modal = transform_modal;
ot->cancel = transform_cancel;
ot->poll = ED_operator_editcurve_3d;
ot->ui = transform_ui;
RNA_def_float_rotation(ot->srna, "value", 0, NULL, -FLT_MAX, FLT_MAX, "Angle", "", -M_PI * 2, M_PI * 2);
@@ -732,11 +777,11 @@ static void TRANSFORM_OT_bend(struct wmOperatorType *ot)
ot->modal = transform_modal;
ot->cancel = transform_cancel;
ot->poll = ED_operator_region_view3d_active;
ot->ui = transform_ui;
RNA_def_float_rotation(ot->srna, "value", 1, NULL, -FLT_MAX, FLT_MAX, "Angle", "", -M_PI * 2, M_PI * 2);
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT | P_CENTER);
}
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT | P_CENTER | P_CLNOR_INVALIDATE);}
static void TRANSFORM_OT_shear(struct wmOperatorType *ot)
{
@@ -752,10 +797,11 @@ static void TRANSFORM_OT_shear(struct wmOperatorType *ot)
ot->modal = transform_modal;
ot->cancel = transform_cancel;
ot->poll = ED_operator_screenactive;
ot->ui = transform_ui;
RNA_def_float(ot->srna, "value", 0, -FLT_MAX, FLT_MAX, "Offset", "", -FLT_MAX, FLT_MAX);
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT);
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT | P_CLNOR_INVALIDATE);
// XXX Shear axis?
}
@@ -773,11 +819,11 @@ static void TRANSFORM_OT_push_pull(struct wmOperatorType *ot)
ot->modal = transform_modal;
ot->cancel = transform_cancel;
ot->poll = ED_operator_screenactive;
ot->ui = transform_ui;
RNA_def_float(ot->srna, "value", 0, -FLT_MAX, FLT_MAX, "Distance", "", -FLT_MAX, FLT_MAX);
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_CENTER);
}
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_CENTER | P_CLNOR_INVALIDATE);}
static void TRANSFORM_OT_shrink_fatten(struct wmOperatorType *ot)
{
@@ -793,12 +839,13 @@ static void TRANSFORM_OT_shrink_fatten(struct wmOperatorType *ot)
ot->modal = transform_modal;
ot->cancel = transform_cancel;
ot->poll = ED_operator_editmesh;
ot->ui = transform_ui;
RNA_def_float(ot->srna, "value", 0, -FLT_MAX, FLT_MAX, "Offset", "", -FLT_MAX, FLT_MAX);
RNA_def_boolean(ot->srna, "use_even_offset", true, "Offset Even", "Scale the offset to give more even thickness");
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP);
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_CLNOR_INVALIDATE);
}
static void TRANSFORM_OT_tosphere(struct wmOperatorType *ot)
@@ -816,11 +863,11 @@ static void TRANSFORM_OT_tosphere(struct wmOperatorType *ot)
ot->modal = transform_modal;
ot->cancel = transform_cancel;
ot->poll = ED_operator_screenactive;
ot->ui = transform_ui;
RNA_def_float_factor(ot->srna, "value", 0, 0, 1, "Factor", "", 0, 1);
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT | P_CENTER);
}
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT | P_CENTER | P_CLNOR_INVALIDATE);}
static void TRANSFORM_OT_mirror(struct wmOperatorType *ot)
{
@@ -836,9 +883,9 @@ static void TRANSFORM_OT_mirror(struct wmOperatorType *ot)
ot->modal = transform_modal;
ot->cancel = transform_cancel;
ot->poll = ED_operator_screenactive;
ot->ui = transform_ui;
Transform_Properties(ot, P_CONSTRAINT | P_PROPORTIONAL | P_GPENCIL_EDIT | P_CENTER);
}
Transform_Properties(ot, P_CONSTRAINT | P_PROPORTIONAL | P_GPENCIL_EDIT | P_CENTER | P_CLNOR_INVALIDATE);}
static void TRANSFORM_OT_edge_slide(struct wmOperatorType *ot)
{
@@ -856,6 +903,7 @@ static void TRANSFORM_OT_edge_slide(struct wmOperatorType *ot)
ot->modal = transform_modal;
ot->cancel = transform_cancel;
ot->poll = ED_operator_editmesh_region_view3d;
ot->ui = transform_ui;
RNA_def_float_factor(ot->srna, "value", 0, -10.0f, 10.0f, "Factor", "", -1.0f, 1.0f);
@@ -868,7 +916,7 @@ static void TRANSFORM_OT_edge_slide(struct wmOperatorType *ot)
RNA_def_boolean(ot->srna, "use_clamp", true, "Clamp",
"Clamp within the edge extents");
Transform_Properties(ot, P_MIRROR | P_SNAP | P_CORRECT_UV);
Transform_Properties(ot, P_MIRROR | P_SNAP | P_CORRECT_UV | P_CLNOR_INVALIDATE);
}
static void TRANSFORM_OT_vert_slide(struct wmOperatorType *ot)
@@ -885,6 +933,7 @@ static void TRANSFORM_OT_vert_slide(struct wmOperatorType *ot)
ot->modal = transform_modal;
ot->cancel = transform_cancel;
ot->poll = ED_operator_editmesh_region_view3d;
ot->ui = transform_ui;
RNA_def_float_factor(ot->srna, "value", 0, -10.0f, 10.0f, "Factor", "", -1.0f, 1.0f);
RNA_def_boolean(ot->srna, "use_even", false, "Even",
@@ -894,7 +943,7 @@ static void TRANSFORM_OT_vert_slide(struct wmOperatorType *ot)
RNA_def_boolean(ot->srna, "use_clamp", true, "Clamp",
"Clamp within the edge extents");
Transform_Properties(ot, P_MIRROR | P_SNAP | P_CORRECT_UV);
Transform_Properties(ot, P_MIRROR | P_SNAP | P_CORRECT_UV | P_CLNOR_INVALIDATE);
}
static void TRANSFORM_OT_edge_crease(struct wmOperatorType *ot)
@@ -911,6 +960,7 @@ static void TRANSFORM_OT_edge_crease(struct wmOperatorType *ot)
ot->modal = transform_modal;
ot->cancel = transform_cancel;
ot->poll = ED_operator_editmesh;
ot->ui = transform_ui;
RNA_def_float_factor(ot->srna, "value", 0, -1.0f, 1.0f, "Factor", "", -1.0f, 1.0f);
@@ -952,6 +1002,7 @@ static void TRANSFORM_OT_edge_bevelweight(struct wmOperatorType *ot)
ot->modal = transform_modal;
ot->cancel = transform_cancel;
ot->poll = ED_operator_editmesh;
ot->ui = transform_ui;
RNA_def_float_factor(ot->srna, "value", 0, -1.0f, 1.0f, "Factor", "", -1.0f, 1.0f);
@@ -972,12 +1023,34 @@ static void TRANSFORM_OT_seq_slide(struct wmOperatorType *ot)
ot->modal = transform_modal;
ot->cancel = transform_cancel;
ot->poll = ED_operator_sequencer_active;
ot->ui = transform_ui;
RNA_def_float_vector_xyz(ot->srna, "value", 2, NULL, -FLT_MAX, FLT_MAX, "Vector", "", -FLT_MAX, FLT_MAX);
Transform_Properties(ot, P_SNAP);
}
static void TRANSFORM_OT_rotate_normal(struct wmOperatorType *ot)
{
/* identifiers */
ot->name = "Normal Rotate";
ot->description = "Rotate split normal of selected items";
ot->idname = OP_NORMAL_ROTATION;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
/* api callbacks */
ot->invoke = transform_invoke;
ot->exec = transform_exec;
ot->modal = transform_modal;
ot->cancel = transform_cancel;
ot->poll = ED_operator_editmesh_auto_smooth;
ot->ui = transform_ui;
RNA_def_float_rotation(ot->srna, "value", 0, NULL, -FLT_MAX, FLT_MAX, "Angle", "", -M_PI * 2, M_PI * 2);
Transform_Properties(ot, P_AXIS | P_CONSTRAINT | P_MIRROR);
}
static void TRANSFORM_OT_transform(struct wmOperatorType *ot)
{
PropertyRNA *prop;
@@ -994,6 +1067,7 @@ static void TRANSFORM_OT_transform(struct wmOperatorType *ot)
ot->modal = transform_modal;
ot->cancel = transform_cancel;
ot->poll = ED_operator_screenactive;
ot->ui = transform_ui;
prop = RNA_def_enum(ot->srna, "mode", rna_enum_transform_mode_types, TFM_TRANSLATION, "Mode", "");
RNA_def_property_flag(prop, PROP_HIDDEN);

View File

@@ -87,6 +87,7 @@ typedef enum ModifierType {
eModifierType_CorrectiveSmooth = 51,
eModifierType_MeshSequenceCache = 52,
eModifierType_SurfaceDeform = 53,
eModifierType_WeightedNormal = 54,
NUM_MODIFIER_TYPES
} ModifierType;
@@ -1619,6 +1620,32 @@ enum {
MOD_SDEF_MODE_CENTROID = 2,
};
typedef struct WeightedNormalModifierData {
ModifierData modifier;
char defgrp_name[64]; /* MAX_VGROUP_NAME */
char mode, flag;
short weight;
float thresh;
} WeightedNormalModifierData;
/* Name/id of the generic PROP_INT cdlayer storing face weights. */
#define MOD_WEIGHTEDNORMALS_FACEWEIGHT_CDLAYER_ID "__mod_weightednormals_faceweight"
/* WeightedNormalModifierData.mode */
enum {
MOD_WEIGHTEDNORMAL_MODE_FACE = 0,
MOD_WEIGHTEDNORMAL_MODE_ANGLE = 1,
MOD_WEIGHTEDNORMAL_MODE_FACE_ANGLE = 2,
};
/* WeightedNormalModifierData.flag */
enum {
MOD_WEIGHTEDNORMAL_KEEP_SHARP = (1 << 0),
MOD_WEIGHTEDNORMAL_INVERT_VGROUP = (1 << 1),
MOD_WEIGHTEDNORMAL_FACE_INFLUENCE = (1 << 2),
};
#define MOD_MESHSEQ_READ_ALL \
(MOD_MESHSEQ_READ_VERT | MOD_MESHSEQ_READ_POLY | MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)

View File

@@ -1579,6 +1579,10 @@ typedef struct ToolSettings {
struct CurvePaintSettings curve_paint_settings;
struct MeshStatVis statvis;
/* Normal Editing */
float normal_vector[3];
int face_strength;
} ToolSettings;
/* *************************************************************** */
@@ -2042,6 +2046,13 @@ enum {
OB_DRAW_GROUPUSER_ALL = 2
};
/* toolsettings->face_strength */
enum {
FACE_STRENGTH_WEAK = -16384,
FACE_STRENGTH_MEDIUM = 0,
FACE_STRENGTH_STRONG = 16384,
};
/* object_vgroup.c */
/* ToolSettings.vgroupsubset */
typedef enum eVGroupSelect {

View File

@@ -697,6 +697,7 @@ extern StructRNA RNA_WaveModifier;
extern StructRNA RNA_VertexWeightEditModifier;
extern StructRNA RNA_VertexWeightMixModifier;
extern StructRNA RNA_VertexWeightProximityModifier;
extern StructRNA RNA_WeightedNormalModifier;
extern StructRNA RNA_Window;
extern StructRNA RNA_WindowManager;
extern StructRNA RNA_WipeSequence;

View File

@@ -68,6 +68,7 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
{eModifierType_MeshCache, "MESH_CACHE", ICON_MOD_MESHDEFORM, "Mesh Cache", ""},
{eModifierType_MeshSequenceCache, "MESH_SEQUENCE_CACHE", ICON_MOD_MESHDEFORM, "Mesh Sequence Cache", ""},
{eModifierType_NormalEdit, "NORMAL_EDIT", ICON_MOD_NORMALEDIT, "Normal Edit", ""},
{eModifierType_WeightedNormal, "WEIGHTED_NORMAL", ICON_MOD_NORMALEDIT, "Weighted Normal", ""},
{eModifierType_UVProject, "UV_PROJECT", ICON_MOD_UVPROJECT, "UV Project", ""},
{eModifierType_UVWarp, "UV_WARP", ICON_MOD_UVPROJECT, "UV Warp", ""},
{eModifierType_WeightVGEdit, "VERTEX_WEIGHT_EDIT", ICON_MOD_VERTEX_WEIGHT, "Vertex Weight Edit", ""},
@@ -411,6 +412,8 @@ static StructRNA *rna_Modifier_refine(struct PointerRNA *ptr)
return &RNA_MeshSequenceCacheModifier;
case eModifierType_SurfaceDeform:
return &RNA_SurfaceDeformModifier;
case eModifierType_WeightedNormal:
return &RNA_WeightedNormalModifier;
/* Default */
case eModifierType_None:
case eModifierType_ShapeKey:
@@ -501,6 +504,7 @@ RNA_MOD_VGROUP_NAME_SET(WeightVGMix, defgrp_name_b);
RNA_MOD_VGROUP_NAME_SET(WeightVGMix, mask_defgrp_name);
RNA_MOD_VGROUP_NAME_SET(WeightVGProximity, defgrp_name);
RNA_MOD_VGROUP_NAME_SET(WeightVGProximity, mask_defgrp_name);
RNA_MOD_VGROUP_NAME_SET(WeightedNormal, defgrp_name);
RNA_MOD_VGROUP_NAME_SET(Wireframe, defgrp_name);
static void rna_ExplodeModifier_vgroup_get(PointerRNA *ptr, char *value)
@@ -4809,6 +4813,68 @@ static void rna_def_modifier_surfacedeform(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
}
static void rna_def_modifier_weightednormal(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
static EnumPropertyItem prop_weighting_mode_items[] = {
{MOD_WEIGHTEDNORMAL_MODE_FACE, "FACE_AREA", 0, "Face Area", "Generate face area weighted normals"},
{MOD_WEIGHTEDNORMAL_MODE_ANGLE, "CORNER_ANGLE", 0, "Corner Angle", "Generate corner angle weighted normals"},
{MOD_WEIGHTEDNORMAL_MODE_FACE_ANGLE, "FACE_AREA_WITH_ANGLE", 0, "Face Area And Angle",
"Generated normals weighted by both face area and angle"},
{0, NULL, 0, NULL, NULL}
};
srna = RNA_def_struct(brna, "WeightedNormalModifier", "Modifier");
RNA_def_struct_ui_text(srna, "WeightedNormal Modifier", "");
RNA_def_struct_sdna(srna, "WeightedNormalModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_NORMALEDIT);
prop = RNA_def_property(srna, "weight", PROP_INT, PROP_NONE);
RNA_def_property_range(prop, 1, 100);
RNA_def_property_ui_range(prop, 1, 100, 1, -1);
RNA_def_property_ui_text(prop, "Weight",
"Corrective factor applied to faces' weights, 50 is neutral, "
"lower values increase weight of weak faces, "
"higher values increase weight of strong faces");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, prop_weighting_mode_items);
RNA_def_property_ui_text(prop, "Weighting Mode", "Weighted vertex normal mode to use");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "thresh", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0, 10);
RNA_def_property_ui_range(prop, 0, 10, 1, 2);
RNA_def_property_ui_text(prop, "Threshold", "Threshold value for different weights to be considered equal");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "keep_sharp", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_WEIGHTEDNORMAL_KEEP_SHARP);
RNA_def_property_ui_text(prop, "Keep Sharp",
"Keep sharp edges as computed for default split normals, "
"instead of setting a single weighted normal for each vertex.");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "defgrp_name");
RNA_def_property_ui_text(prop, "Vertex Group", "Vertex group name for modifying the selected areas");
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_WeightedNormalModifier_defgrp_name_set");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "invert_vertex_group", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_WEIGHTEDNORMAL_INVERT_VGROUP);
RNA_def_property_ui_text(prop, "Invert", "Invert vertex group influence");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "face_influence", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_WEIGHTEDNORMAL_FACE_INFLUENCE);
RNA_def_property_ui_text(prop, "Face Influence", "Use influence of face for weighting");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
}
void RNA_def_modifier(BlenderRNA *brna)
{
StructRNA *srna;
@@ -4927,6 +4993,7 @@ void RNA_def_modifier(BlenderRNA *brna)
rna_def_modifier_normaledit(brna);
rna_def_modifier_meshseqcache(brna);
rna_def_modifier_surfacedeform(brna);
rna_def_modifier_weightednormal(brna);
}
#endif

View File

@@ -2502,6 +2502,13 @@ static void rna_def_tool_settings(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL}
};
static EnumPropertyItem mod_weighted_strength[] = {
{FACE_STRENGTH_WEAK, "Weak", 0, "Weak", ""},
{FACE_STRENGTH_MEDIUM, "Medium", 0, "Medium", ""},
{FACE_STRENGTH_STRONG, "Strong", 0, "Strong", ""},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem draw_groupuser_items[] = {
{OB_DRAW_GROUPUSER_NONE, "NONE", 0, "None", ""},
{OB_DRAW_GROUPUSER_ACTIVE, "ACTIVE", 0, "Active", "Show vertices with no weights in the active group"},
@@ -2890,6 +2897,14 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "edge_mode_live_unwrap", 1);
RNA_def_property_ui_text(prop, "Live Unwrap", "Changing edges seam re-calculates UV unwrap");
prop = RNA_def_property(srna, "normal_vector", PROP_FLOAT, PROP_XYZ);
RNA_def_property_ui_text(prop, "Normal Vector", "Normal Vector used to copy, add or multiply");
RNA_def_property_ui_range(prop, -10000.0, 10000.0, 1, 3);
prop = RNA_def_property(srna, "face_strength", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, mod_weighted_strength);
RNA_def_property_ui_text(prop, "Face Strength", "Set strength of face to specified value");
/* etch-a-ton */
prop = RNA_def_property(srna, "use_bone_sketching", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "bone_sketching", BONE_SKETCHING);

View File

@@ -100,6 +100,7 @@ set(SRC
intern/MOD_uvproject.c
intern/MOD_warp.c
intern/MOD_wave.c
intern/MOD_weighted_normal.c
intern/MOD_weightvg_util.c
intern/MOD_weightvgedit.c
intern/MOD_weightvgmix.c

View File

@@ -86,6 +86,7 @@ extern ModifierTypeInfo modifierType_NormalEdit;
extern ModifierTypeInfo modifierType_CorrectiveSmooth;
extern ModifierTypeInfo modifierType_MeshSequenceCache;
extern ModifierTypeInfo modifierType_SurfaceDeform;
extern ModifierTypeInfo modifierType_WeightedNormal;
/* MOD_util.c */
void modifier_type_init(ModifierTypeInfo *types[]);

View File

@@ -288,5 +288,6 @@ void modifier_type_init(ModifierTypeInfo *types[])
INIT_TYPE(CorrectiveSmooth);
INIT_TYPE(MeshSequenceCache);
INIT_TYPE(SurfaceDeform);
INIT_TYPE(WeightedNormal);
#undef INIT_TYPE
}

View File

@@ -0,0 +1,657 @@
/*
* ***** 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 *****
*
*/
/** \file blender/modifiers/intern/MOD_weighted_normal.c
* \ingroup modifiers
*/
#include "MEM_guardedalloc.h"
#include "DNA_mesh_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BKE_cdderivedmesh.h"
#include "BKE_deform.h"
#include "BKE_mesh.h"
#include "BLI_math.h"
#include "BLI_linklist.h"
#include "MOD_modifiertypes.h"
#include "MOD_util.h"
#define CLNORS_VALID_VEC_LEN (1e-6f)
typedef struct ModePair {
float val; /* Contains mode based value (face area / corner angle). */
int index; /* Index value per poly or per loop. */
} ModePair;
/* Sorting function used in modifier, sorts in decreasing order. */
static int modepair_cmp_by_val_inverse(const void *p1, const void *p2)
{
ModePair *r1 = (ModePair *)p1;
ModePair *r2 = (ModePair *)p2;
return (r1->val < r2->val) ? 1 : ((r1->val > r2->val) ? -1 : 0);
}
/* There will be one of those per vertex (simple case, computing one normal per vertex), or per smooth fan. */
typedef struct WeightedNormalDataAggregateItem {
float normal[3];
int num_loops; /* Count number of loops using this item so far. */
float curr_val; /* Current max val for this item. */
int curr_strength; /* Current max strength encountered for this item. */
} WeightedNormalDataAggregateItem;
#define NUM_CACHED_INVERSE_POWERS_OF_WEIGHT 128
typedef struct WeightedNormalData {
const int numVerts;
const int numEdges;
const int numLoops;
const int numPolys;
MVert *mvert;
MEdge *medge;
MLoop *mloop;
short (*clnors)[2];
const bool has_clnors; /* True if clnors already existed, false if we had to create them. */
const float split_angle;
MPoly *mpoly;
float (*polynors)[3];
int *poly_strength;
MDeformVert *dvert;
const int defgrp_index;
const bool use_invert_vgroup;
const float weight;
const short mode;
/* Lower-level, internal processing data. */
float cached_inverse_powers_of_weight[NUM_CACHED_INVERSE_POWERS_OF_WEIGHT];
WeightedNormalDataAggregateItem *items_data;
ModePair *mode_pair;
int *loop_to_poly;
} WeightedNormalData;
/* Check strength of given poly compared to those found so far for that given item (vertex or smooth fan),
* and reset matching item_data in case we get a stronger new strength. */
static bool check_item_poly_strength(
WeightedNormalData *wn_data, WeightedNormalDataAggregateItem *item_data, const int mp_index)
{
BLI_assert (wn_data->poly_strength != NULL);
const int mp_strength = wn_data->poly_strength[mp_index];
if (mp_strength > item_data->curr_strength) {
item_data->curr_strength = mp_strength;
item_data->curr_val = 0.0f;
item_data->num_loops = 0;
zero_v3(item_data->normal);
}
return mp_strength == item_data->curr_strength;
}
static void aggregate_item_normal(
WeightedNormalModifierData *wnmd, WeightedNormalData *wn_data,
WeightedNormalDataAggregateItem *item_data,
const int mv_index, const int mp_index,
const float curr_val, const bool use_face_influence)
{
float (*polynors)[3] = wn_data->polynors;
MDeformVert *dvert = wn_data->dvert;
const int defgrp_index = wn_data->defgrp_index;
const bool use_invert_vgroup = wn_data->use_invert_vgroup;
const float weight = wn_data->weight;
float *cached_inverse_powers_of_weight = wn_data->cached_inverse_powers_of_weight;
const bool has_vgroup = dvert != NULL;
const bool vert_of_group = has_vgroup && defvert_find_index(&dvert[mv_index], defgrp_index) != NULL;
if (has_vgroup && ((vert_of_group && use_invert_vgroup) || (!vert_of_group && !use_invert_vgroup))) {
return;
}
if (use_face_influence && !check_item_poly_strength(wn_data, item_data, mp_index)) {
return;
}
/* If item's curr_val is 0 init it to present value. */
if (item_data->curr_val == 0.0f) {
item_data->curr_val = curr_val;
}
if (!compare_ff(item_data->curr_val, curr_val, wnmd->thresh)) {
/* item's curr_val and present value differ more than threshold, update. */
item_data->num_loops++;
item_data->curr_val = curr_val;
}
/* Exponentially divided weight for each normal (since a few values will be used by most cases, we cache those). */
const int num_loops = item_data->num_loops;
if (num_loops < NUM_CACHED_INVERSE_POWERS_OF_WEIGHT && cached_inverse_powers_of_weight[num_loops] == 0.0f) {
cached_inverse_powers_of_weight[num_loops] = 1.0f / powf(weight, num_loops);
}
const float inverted_n_weight = num_loops < NUM_CACHED_INVERSE_POWERS_OF_WEIGHT ?
cached_inverse_powers_of_weight[num_loops] : 1.0f / powf(weight, num_loops);
madd_v3_v3fl(item_data->normal, polynors[mp_index], curr_val * inverted_n_weight);
}
static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd, WeightedNormalData *wn_data)
{
const int numVerts = wn_data->numVerts;
const int numEdges = wn_data->numEdges;
const int numLoops = wn_data->numLoops;
const int numPolys = wn_data->numPolys;
MVert *mvert = wn_data->mvert;
MEdge *medge = wn_data->medge;
MLoop *mloop = wn_data->mloop;
short (*clnors)[2] = wn_data->clnors;
int *loop_to_poly = wn_data->loop_to_poly;
MPoly *mpoly = wn_data->mpoly;
float (*polynors)[3] = wn_data->polynors;
int *poly_strength = wn_data->poly_strength;
MDeformVert *dvert = wn_data->dvert;
const short mode = wn_data->mode;
ModePair *mode_pair = wn_data->mode_pair;
const bool has_clnors = wn_data->has_clnors;
const float split_angle = wn_data->split_angle;
MLoopNorSpaceArray lnors_spacearr = {NULL};
const bool keep_sharp = (wnmd->flag & MOD_WEIGHTEDNORMAL_KEEP_SHARP) != 0;
const bool use_face_influence = (wnmd->flag & MOD_WEIGHTEDNORMAL_FACE_INFLUENCE) != 0 && poly_strength != NULL;
const bool has_vgroup = dvert != NULL;
float (*loop_normals)[3] = NULL;
WeightedNormalDataAggregateItem *items_data = NULL;
int num_items = 0;
if (keep_sharp) {
BLI_bitmap *done_loops = BLI_BITMAP_NEW(numLoops, __func__);
/* This will give us loop normal spaces, we do not actually care about computed loop_normals for now... */
loop_normals = MEM_calloc_arrayN((size_t)numLoops, sizeof(*loop_normals), __func__);
BKE_mesh_normals_loop_split(mvert, numVerts, medge, numEdges,
mloop, loop_normals, numLoops, mpoly, polynors, numPolys,
true, split_angle, &lnors_spacearr, has_clnors ? clnors : NULL, loop_to_poly);
num_items = lnors_spacearr.num_spaces;
items_data = MEM_calloc_arrayN((size_t)num_items, sizeof(*items_data), __func__);
/* In this first loop, we assign each WeightedNormalDataAggregateItem to its smooth fan of loops (aka lnor space). */
MPoly *mp;
int mp_index;
int item_index;
for (mp = mpoly, mp_index = 0, item_index = 0; mp_index < numPolys; mp++, mp_index++) {
int ml_index = mp->loopstart;
const int ml_end_index = ml_index + mp->totloop;
for (; ml_index < ml_end_index; ml_index++) {
if (BLI_BITMAP_TEST(done_loops, ml_index)) {
/* Smooth fan of this loop has already been processed, skip it. */
continue;
}
BLI_assert(item_index < num_items);
WeightedNormalDataAggregateItem *itdt = &items_data[item_index];
itdt->curr_strength = FACE_STRENGTH_WEAK;
MLoopNorSpace *lnor_space = lnors_spacearr.lspacearr[ml_index];
lnor_space->user_data = itdt;
if (!(lnor_space->flags & MLNOR_SPACE_IS_SINGLE)) {
for (LinkNode *lnode = lnor_space->loops; lnode; lnode = lnode->next) {
const int ml_fan_index = GET_INT_FROM_POINTER(lnode->link);
BLI_BITMAP_ENABLE(done_loops, ml_fan_index);
}
}
else {
BLI_BITMAP_ENABLE(done_loops, ml_index);
}
item_index++;
}
}
MEM_freeN(done_loops);
}
else {
num_items = numVerts;
items_data = MEM_calloc_arrayN((size_t)num_items, sizeof(*items_data), __func__);
if (use_face_influence) {
for (int item_index = 0; item_index < num_items; item_index++) {
items_data[item_index].curr_strength = FACE_STRENGTH_WEAK;
}
}
}
wn_data->items_data = items_data;
switch (mode) {
case MOD_WEIGHTEDNORMAL_MODE_FACE:
for (int i = 0; i < numPolys; i++) {
const int mp_index = mode_pair[i].index;
const float mp_val = mode_pair[i].val;
int ml_index = mpoly[mp_index].loopstart;
const int ml_index_end = ml_index + mpoly[mp_index].totloop;
for (; ml_index < ml_index_end; ml_index++) {
const int mv_index = mloop[ml_index].v;
WeightedNormalDataAggregateItem *item_data = keep_sharp ?
lnors_spacearr.lspacearr[ml_index]->user_data:
&items_data[mv_index];
aggregate_item_normal(wnmd, wn_data, item_data, mv_index, mp_index, mp_val, use_face_influence);
}
}
break;
case MOD_WEIGHTEDNORMAL_MODE_ANGLE:
case MOD_WEIGHTEDNORMAL_MODE_FACE_ANGLE:
BLI_assert(loop_to_poly != NULL);
for (int i = 0; i < numLoops; i++) {
const int ml_index = mode_pair[i].index;
const float ml_val = mode_pair[i].val;
const int mp_index = loop_to_poly[ml_index];
const int mv_index = mloop[ml_index].v;
WeightedNormalDataAggregateItem *item_data = keep_sharp ?
lnors_spacearr.lspacearr[ml_index]->user_data:
&items_data[mv_index];
aggregate_item_normal(wnmd, wn_data, item_data, mv_index, mp_index, ml_val, use_face_influence);
}
break;
default:
BLI_assert(0);
}
/* Validate computed weighted normals. */
for (int item_index = 0; item_index < num_items; item_index++) {
if (normalize_v3(items_data[item_index].normal) < CLNORS_VALID_VEC_LEN) {
zero_v3(items_data[item_index].normal);
}
}
if (keep_sharp) {
/* Set loop normals for normal computed for each lnor space (smooth fan).
* Note that loop_normals is already populated with clnors (before this modifier is applied, at start of
* this function), so no need to recompute them here. */
for (int ml_index = 0; ml_index < numLoops; ml_index++) {
WeightedNormalDataAggregateItem *item_data = lnors_spacearr.lspacearr[ml_index]->user_data;
if (!is_zero_v3(item_data->normal)) {
copy_v3_v3(loop_normals[ml_index], item_data->normal);
}
}
BKE_mesh_normals_loop_custom_set(mvert, numVerts, medge, numEdges,
mloop, loop_normals, numLoops, mpoly, polynors, numPolys, clnors);
}
else {
/* TODO: Ideally, we could add an option to BKE_mesh_normals_loop_custom_[from_vertices_]set() to keep current
* clnors instead of resetting them to default autocomputed ones, when given new custom normal is zero-vec.
* But this is not exactly trivial change, better to keep this optimization for later...
*/
if (!has_vgroup) {
/* Note: in theory, we could avoid this extra allocation & copying... But think we can live with it for now,
* and it makes code simpler & cleaner. */
float (*vert_normals)[3] = MEM_calloc_arrayN((size_t)numVerts, sizeof(*loop_normals), __func__);
for (int ml_index = 0; ml_index < numLoops; ml_index++) {
const int mv_index = mloop[ml_index].v;
copy_v3_v3(vert_normals[mv_index], items_data[mv_index].normal);
}
BKE_mesh_normals_loop_custom_from_vertices_set(mvert, vert_normals, numVerts, medge, numEdges,
mloop, numLoops, mpoly, polynors, numPolys, clnors);
MEM_freeN(vert_normals);
}
else {
loop_normals = MEM_calloc_arrayN((size_t)numLoops, sizeof(*loop_normals), __func__);
BKE_mesh_normals_loop_split(mvert, numVerts, medge, numEdges,
mloop, loop_normals, numLoops, mpoly, polynors, numPolys,
true, split_angle, NULL, has_clnors ? clnors : NULL, loop_to_poly);
for (int ml_index = 0; ml_index < numLoops; ml_index++) {
const int item_index = mloop[ml_index].v;
if (!is_zero_v3(items_data[item_index].normal)) {
copy_v3_v3(loop_normals[ml_index], items_data[item_index].normal);
}
}
BKE_mesh_normals_loop_custom_set(mvert, numVerts, medge, numEdges,
mloop, loop_normals, numLoops, mpoly, polynors, numPolys, clnors);
}
}
if (keep_sharp) {
BKE_lnor_spacearr_free(&lnors_spacearr);
}
MEM_SAFE_FREE(loop_normals);
}
static void wn_face_area(WeightedNormalModifierData *wnmd, WeightedNormalData *wn_data)
{
const int numPolys = wn_data->numPolys;
MVert *mvert = wn_data->mvert;
MLoop *mloop = wn_data->mloop;
MPoly *mpoly = wn_data->mpoly;
MPoly *mp;
int mp_index;
ModePair *face_area = MEM_malloc_arrayN((size_t)numPolys, sizeof(*face_area), __func__);
ModePair *f_area = face_area;
for (mp_index = 0, mp = mpoly; mp_index < numPolys; mp_index++, mp++, f_area++) {
f_area->val = BKE_mesh_calc_poly_area(mp, &mloop[mp->loopstart], mvert);
f_area->index = mp_index;
}
qsort(face_area, numPolys, sizeof(*face_area), modepair_cmp_by_val_inverse);
wn_data->mode_pair = face_area;
apply_weights_vertex_normal(wnmd, wn_data);
}
static void wn_corner_angle(WeightedNormalModifierData *wnmd, WeightedNormalData *wn_data)
{
const int numLoops = wn_data->numLoops;
const int numPolys = wn_data->numPolys;
MVert *mvert = wn_data->mvert;
MLoop *mloop = wn_data->mloop;
MPoly *mpoly = wn_data->mpoly;
MPoly *mp;
int mp_index;
int *loop_to_poly = MEM_malloc_arrayN((size_t)numLoops, sizeof(*loop_to_poly), __func__);
ModePair *corner_angle = MEM_malloc_arrayN((size_t)numLoops, sizeof(*corner_angle), __func__);
for (mp_index = 0, mp = mpoly; mp_index < numPolys; mp_index++, mp++) {
MLoop *ml_start = &mloop[mp->loopstart];
float *index_angle = MEM_malloc_arrayN((size_t)mp->totloop, sizeof(*index_angle), __func__);
BKE_mesh_calc_poly_angles(mp, ml_start, mvert, index_angle);
ModePair *c_angl = &corner_angle[mp->loopstart];
float *angl = index_angle;
for (int ml_index = mp->loopstart; ml_index < mp->loopstart + mp->totloop; ml_index++, c_angl++, angl++) {
c_angl->val = (float)M_PI - *angl;
c_angl->index = ml_index;
loop_to_poly[ml_index] = mp_index;
}
MEM_freeN(index_angle);
}
qsort(corner_angle, numLoops, sizeof(*corner_angle), modepair_cmp_by_val_inverse);
wn_data->loop_to_poly = loop_to_poly;
wn_data->mode_pair = corner_angle;
apply_weights_vertex_normal(wnmd, wn_data);
}
static void wn_face_with_angle(WeightedNormalModifierData *wnmd, WeightedNormalData *wn_data)
{
const int numLoops = wn_data->numLoops;
const int numPolys = wn_data->numPolys;
MVert *mvert = wn_data->mvert;
MLoop *mloop = wn_data->mloop;
MPoly *mpoly = wn_data->mpoly;
MPoly *mp;
int mp_index;
int *loop_to_poly = MEM_malloc_arrayN((size_t)numLoops, sizeof(*loop_to_poly), __func__);
ModePair *combined = MEM_malloc_arrayN((size_t)numLoops, sizeof(*combined), __func__);
for (mp_index = 0, mp = mpoly; mp_index < numPolys; mp_index++, mp++) {
MLoop *ml_start = &mloop[mp->loopstart];
float face_area = BKE_mesh_calc_poly_area(mp, ml_start, mvert);
float *index_angle = MEM_malloc_arrayN((size_t)mp->totloop, sizeof(*index_angle), __func__);
BKE_mesh_calc_poly_angles(mp, ml_start, mvert, index_angle);
ModePair *cmbnd = &combined[mp->loopstart];
float *angl = index_angle;
for (int ml_index = mp->loopstart; ml_index < mp->loopstart + mp->totloop; ml_index++, cmbnd++, angl++) {
/* In this case val is product of corner angle and face area. */
cmbnd->val = ((float)M_PI - *angl) * face_area;
cmbnd->index = ml_index;
loop_to_poly[ml_index] = mp_index;
}
MEM_freeN(index_angle);
}
qsort(combined, numLoops, sizeof(*combined), modepair_cmp_by_val_inverse);
wn_data->loop_to_poly = loop_to_poly;
wn_data->mode_pair = combined;
apply_weights_vertex_normal(wnmd, wn_data);
}
static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *dm, ModifierApplyFlag UNUSED(flag))
{
WeightedNormalModifierData *wnmd = (WeightedNormalModifierData *)md;
Mesh *me = ob->data;
if (!(me->flag & ME_AUTOSMOOTH)) {
modifier_setError((ModifierData *)wnmd, "Enable 'Auto Smooth' option in mesh settings");
return dm;
}
const int numVerts = dm->getNumVerts(dm);
const int numEdges = dm->getNumEdges(dm);
const int numLoops = dm->getNumLoops(dm);
const int numPolys = dm->getNumPolys(dm);
MEdge *medge = dm->getEdgeArray(dm);
if (me->medge == medge) {
/* We need to duplicate data here, otherwise setting custom normals (which may also affect sharp edges) could
* modify org mesh. */
dm = CDDM_copy(dm);
medge = dm->getEdgeArray(dm);
}
MPoly *mpoly = dm->getPolyArray(dm);
MVert *mvert = dm->getVertArray(dm);
MLoop *mloop = dm->getLoopArray(dm);
bool free_polynors = false;
/* Right now:
* If weight = 50 then all faces are given equal weight.
* If weight > 50 then more weight given to faces with larger vals (face area / corner angle).
* If weight < 50 then more weight given to faces with lesser vals. However current calculation
* does not converge to min/max.
*/
float weight = ((float)wnmd->weight) / 50.0f;
if (wnmd->weight == 100) {
weight = (float)SHRT_MAX;
}
else if (wnmd->weight == 1) {
weight = 1 / (float)SHRT_MAX;
}
else if ((weight - 1) * 25 > 1) {
weight = (weight - 1) * 25;
}
float (*polynors)[3] = dm->getPolyDataArray(dm, CD_NORMAL);
if (!polynors) {
polynors = MEM_malloc_arrayN((size_t)numPolys, sizeof(*polynors), __func__);
BKE_mesh_calc_normals_poly(mvert, NULL, numVerts, mloop, mpoly, numLoops, numPolys, polynors, false);
free_polynors = true;
}
const float split_angle = me->smoothresh;
short (*clnors)[2];
clnors = CustomData_duplicate_referenced_layer(&dm->loopData, CD_CUSTOMLOOPNORMAL, numLoops);
/* Keep info whether we had clnors, it helps when generating clnor spaces and default normals. */
const bool has_clnors = clnors != NULL;
if (!clnors) {
DM_add_loop_layer(dm, CD_CUSTOMLOOPNORMAL, CD_CALLOC, NULL);
clnors = dm->getLoopDataArray(dm, CD_CUSTOMLOOPNORMAL);
}
MDeformVert *dvert;
int defgrp_index;
modifier_get_vgroup(ob, dm, wnmd->defgrp_name, &dvert, &defgrp_index);
WeightedNormalData wn_data = {
.numVerts = numVerts,
.numEdges = numEdges,
.numLoops = numLoops,
.numPolys = numPolys,
.mvert = mvert,
.medge = medge,
.mloop = mloop,
.clnors = clnors,
.has_clnors = has_clnors,
.split_angle = split_angle,
.mpoly = mpoly,
.polynors = polynors,
.poly_strength = CustomData_get_layer_named(&dm->polyData, CD_PROP_INT, MOD_WEIGHTEDNORMALS_FACEWEIGHT_CDLAYER_ID),
.dvert = dvert,
.defgrp_index = defgrp_index,
.use_invert_vgroup = (wnmd->flag & MOD_WEIGHTEDNORMAL_INVERT_VGROUP) != 0,
.weight = weight,
.mode = wnmd->mode,
};
switch (wnmd->mode) {
case MOD_WEIGHTEDNORMAL_MODE_FACE:
wn_face_area(wnmd, &wn_data);
break;
case MOD_WEIGHTEDNORMAL_MODE_ANGLE:
wn_corner_angle(wnmd, &wn_data);
break;
case MOD_WEIGHTEDNORMAL_MODE_FACE_ANGLE:
wn_face_with_angle(wnmd, &wn_data);
break;
}
if (free_polynors) {
MEM_freeN(polynors);
}
MEM_SAFE_FREE(wn_data.loop_to_poly);
MEM_SAFE_FREE(wn_data.mode_pair);
MEM_SAFE_FREE(wn_data.items_data);
return dm;
}
static void copyData(ModifierData *md, ModifierData *target)
{
modifier_copyData_generic(md, target);
}
static void initData(ModifierData *md)
{
WeightedNormalModifierData *wnmd = (WeightedNormalModifierData *)md;
wnmd->mode = MOD_WEIGHTEDNORMAL_MODE_FACE;
wnmd->weight = 50;
wnmd->thresh = 1e-2f;
wnmd->flag = 0;
}
static CustomDataMask requiredDataMask(Object *UNUSED(ob), ModifierData *md)
{
WeightedNormalModifierData *wnmd = (WeightedNormalModifierData *)md;
CustomDataMask dataMask = CD_CUSTOMLOOPNORMAL;
if (wnmd->defgrp_name[0]) {
dataMask |= CD_MASK_MDEFORMVERT;
}
if (wnmd->flag & MOD_WEIGHTEDNORMAL_FACE_INFLUENCE) {
dataMask |= CD_MASK_PROP_INT;
}
return dataMask;
}
static bool dependsOnNormals(ModifierData *UNUSED(md))
{
return true;
}
ModifierTypeInfo modifierType_WeightedNormal = {
/* name */ "Weighted Normal",
/* structName */ "WeightedNormalModifierData",
/* structSize */ sizeof(WeightedNormalModifierData),
/* type */ eModifierTypeType_Constructive,
/* flags */ eModifierTypeFlag_AcceptsMesh |
eModifierTypeFlag_SupportsMapping |
eModifierTypeFlag_SupportsEditmode |
eModifierTypeFlag_EnableInEditmode,
/* copyData */ copyData,
/* deformVerts */ NULL,
/* deformMatrices */ NULL,
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* applyModifier */ applyModifier,
/* applyModifierEM */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
/* freeData */ NULL,
/* isDisabled */ NULL,
/* updateDepgraph */ NULL,
/* updateDepsgraph */ NULL,
/* dependsOnTime */ NULL,
/* dependsOnNormals */ dependsOnNormals,
/* foreachObjectLink */ NULL,
/* foreachIDLink */ NULL,
/* foreachTexLink */ NULL,
};