1
1

Compare commits

...

1272 Commits

Author SHA1 Message Date
912f61f9f5 Sculpt: updated Mask and Face Set menu operators
Changed the menu operators:

    Expand Mask by Topology (hotkey Shift A)
    Expand Mask by Normals (hotkey Shift Alt A)
    Expand Face Set by Topology (hotkey Shift W)
    Expand Active Face Set (hotkey Shift Alt W)

so that their hotkeys would appear in their menu entries.

Resolves #104023

Co-authored-by: DisquietingFridge <30654622+DisquietingFridge@users.noreply.github.com>
Pull Request: blender/blender#104568
2023-04-16 23:58:45 +02:00
e9151dc96a Merge remote-tracking branch 'origin' into sculpt-dev 2023-02-07 17:45:51 -08:00
4e85e0b98e sculpt-dev: Experiment in pre-emptive merge of c->c++ conversion 2023-02-06 03:17:14 -08:00
4079136bf4 sculpt-dev: Fix new BMLog for new id system
Everything seems to be working, I can now
start reverting the old ID system (which
was not suitable for master).
2023-02-06 02:52:36 -08:00
155fa374eb sculpt-dev: Add face corner color attribute support to dyntopo undo 2023-02-06 02:40:12 -08:00
bd5f5597c8 sculpt-dev: BMLog rewrite part 2
Finished implementing old behavior.  I still
need to do more testing, but the code is now
much (much) cleaner and less confusing.
2023-02-06 02:20:06 -08:00
e4851c22b0 sculpt-dev: Rewrite BMLog
Part one of BMLog C++ rewrite.
2023-02-04 04:29:35 -08:00
9fc73526a0 sculpt-dev: remove optimization flags 2023-02-02 12:14:55 -08:00
a391a01509 sculpt-dev: Cleanup and fix bugs in dyntopo subdivide
* Removed unused code.
* Fixed nasty double increment bug.
* Fixed subdivision patterns.
2023-01-30 15:54:58 -08:00
b4cc9b67fc sculpt-dev: Dyntopo collapse now works with new id system
. . .but subdivide is now broken.
2023-01-28 00:34:05 -08:00
f68b5cd7e1 sculpt-dev: Cleanup collapse code 2023-01-27 23:35:48 -08:00
4e76cc86b0 sculpt-dev: Cleanup dyntopo.cc
* Split into dyntopo.cc, dyntopo_collapse.cc and dyntopo_intern.hh
* Removed various unused functions.
2023-01-27 17:27:44 -08:00
390b6492e7 sculpt-dev: Fix bugs from merge:
* MSculptVert->valence was not initialized for PBVH_FACES
* PBVH_BMESH did not draw color attributes.
* Crashes related to active mesh attributes.
2023-01-26 21:23:58 -08:00
6c0c339da9 sculpt-dev: fix error from merge 2023-01-26 20:31:25 -08:00
71bce6d8e8 Merge branch 'master' into sculpt-dev 2023-01-26 20:13:46 -08:00
abc1b0ce41 sculpt-dev: Update submodule refs ahead of merge 2023-01-13 20:37:39 -08:00
a5bddb4364 Sculpt-dev: Cleanup of edge collapse
Still a work in progress.
2022-12-20 02:54:34 -08:00
bb764e418a sculpt-dev: DynTopo cleanup
* Moved dyntopo.c to c++.
* The new element id system now mostly
  works. Doesn't yet work with collapse
  though, which is a tricky case.  Element
  IDs have to be manually freed now, doing that
  with something as convoluted as a non-manifold
  BREP's edge collapse is not easy.
2022-12-11 00:38:14 -08:00
e53a8f9c51 Merge branch 'master' into sculpt-dev 2022-12-03 03:26:49 -08:00
aeefe65171 sculpt-dev: fix compile errors 2022-12-02 12:35:34 -08:00
e19d6d2f18 sculpt-dev: part one of new id system for dyntopo 2022-11-22 12:39:49 -08:00
8a4d2eb670 sculpt-dev: fix crash in boundary brush 2022-11-20 10:02:13 -08:00
11643ab8f7 Merge branch 'master' into sculpt-dev 2022-11-14 09:47:58 -08:00
a2bc81e5f0 sculpt-dev: pre-emptively rename prototype C++ code in sculpt.cc to
sculpt_new.cc before merge
2022-11-10 09:54:21 -08:00
adc007d4e7 sculpt-dev: Rename BLI_arc_spline.hh to BLI_even_spline.hh
Contracting "arc length parameterized" to "arc" is ambigous
(could mean a circle spline).  Instead use "even", short for
"evenly spaced."
2022-11-01 12:15:48 -07:00
c6232390d3 sculpt-dev: fix memory corruption 2022-10-22 16:56:56 -07:00
5b8b99cf8a sculpt-dev: Fix stroke id error in smooth code
Also dialed up quality of roll mapping distance to
curve function to fix a few artifacts.
2022-10-22 16:26:54 -07:00
46839d1f43 sculpt-dev: Roll mapping
* Fixed errors in second derivative of stroke spline
* Fix error on surfaces at an oblique angle to the viewport
* Fix roll mapping not working for sculpt texture paint
2022-10-20 15:19:54 -07:00
9b7561f16a sculpt-dev: Implement arc-length derivatives for BLI_arc_spline.hh 2022-10-19 01:57:10 -07:00
82a3696234 sculpt-dev: Improve roll texture mapping mode 2022-10-19 01:07:33 -07:00
eaef66c661 sculpt-dev: Add beginnings of c++-afification of BLI_heap_minmax. 2022-10-18 16:36:34 -07:00
8786b5c0c0 sculpt-dev: roll brush update
* Wrote a little C++ library for working with arc length
  parameterized cubic splines.
* Stroke samples are queued if a roll texture exists.
  That way we can build a stroke curve of sufficient
  length to cover all the verts in the brush radius.
2022-10-18 16:33:05 -07:00
cb22eae31b sculpt-dev: fix hide bugs and a compiler error 2022-10-17 00:48:36 -07:00
4dad98a6ae sculpt-dev: Fix windows compile error 2022-10-17 00:18:47 -07:00
106edd7caf sculpt-dev: fix crash in extract face set
Also fixed a few compile errors
2022-10-16 18:58:08 -07:00
8c6a054e8e sculpt-dev: fix broken face set boundary automask mode 2022-10-16 18:39:52 -07:00
92bc610c36 Merge remote-tracking branch 'origin' into sculpt-dev 2022-10-12 13:19:59 -07:00
87863770bb sculpt-dev: Fix .hide_poly attribute crash
Further fixes will have to wait for next merge with master
2022-10-10 16:44:44 -07:00
57d5fc7352 sculpt-dev: Split boundary flags into its own attribute
Boundary and corner flags are now stored in their own
attribute instead of in MSculptVert->flag.  Note that
the other parts of MSculptVert will eventually become
their own indiviudual attibute (including the ->flag
which is still used by dyntopo).
2022-10-10 16:06:56 -07:00
a5f53346a6 sculpt-dev: another windows compiler error 2022-10-07 17:10:17 -07:00
6c2d0354e7 sculpt-dev: Compile error 2022-10-07 12:30:17 -07:00
c3c67cf0c2 sculpt-dev: Another compile error 2022-10-07 12:13:07 -07:00
24324e5ff4 sculpt-dev: Fix another compile error 2022-10-07 09:50:11 -07:00
29638fb918 sculpt-dev: fix linux compile error 2022-10-07 09:48:45 -07:00
f420c1f3d4 sculpt-dev: Fix excessively pedantic compiler error 2022-10-07 02:22:01 -07:00
8dcca698e0 sculpt-dev: add missing file 2022-10-07 02:13:26 -07:00
e18be332b3 sculpt-dev: Experimental: Flush sculpt data to mesh on autosave 2022-10-07 01:39:11 -07:00
5e2b234855 sculpt-dev: fix draw face sets brush 2022-10-06 02:18:21 -07:00
42948e2389 sculpt-dev: Fix draw bug 2022-10-06 01:47:05 -07:00
0598a4b360 sculpt-dev: Fix code being in wrong order 2022-10-06 01:21:20 -07:00
059fb4098f sculpt-dev: Various fixes
* Fix memory corruption in sculpt attribute code.
* Fix a few compile errors for particularly pedantic compilers.
* Consolidate a bit of ASAN code in customdata.cc.
2022-10-06 01:08:36 -07:00
e5fcaa293a sculpt-dev: fix merge errors 2022-10-05 20:01:48 -07:00
2aa347b64e Merge remote-tracking branch 'origin' into sculpt-dev 2022-10-05 13:06:06 -07:00
9ae40de20b sculpt-dev: Fix errors from merge 2022-09-30 18:45:03 -07:00
9c619fd381 Merge remote-tracking branch 'origin' into sculpt-dev 2022-09-30 17:35:24 -07:00
dc027bd8a3 Sculpt-dev: Finish fixing merge errors 2022-09-25 09:43:31 -07:00
7934336d80 Sculpt-dev: fix various memory corruption errors 2022-09-20 14:15:49 -07:00
426c7d7101 sculpt-dev: begin fixing merge errors 2022-09-20 10:52:54 -07:00
84342ed7b2 Merge remote-tracking branch 'origin' into sculpt-dev 2022-09-17 17:58:13 -07:00
404a0d5840 Sculpt-dev: Pre-merge preperations 2022-09-16 23:58:26 -07:00
5fa8dbeacd Merge remote-tracking branch 'origin' into sculpt-dev 2022-07-30 00:18:24 -07:00
1ed1fc0f57 sculpt-dev: Rename SculptVertRef -> PBVHVertRef
Should make merging easier.
2022-07-29 19:31:57 -07:00
ed8ffec61f Sculpt-dev: Cleanup sculpt temp attribute API
* The sculpt code now handles lifetime ownership
  of SculptCustomLayer structs.  This removes the
  need to create temp attributes and get their
  SculptCustomLayer reference structs in seperate
  steps, as the code can internally update e.g. bmesh
  block offsets for all SculptCustomLayer instances.
* Removed ss->custom_layers.  The SCULPT_SCL_XXX enums
  are no longer used to reference standard attributes
  (though they are used, at the moment, to provide names
   for them).  Instead a new accessor struct, ss->scl,
   has pointers to standard attributes (e.g. ss->scl.automasking_factor,
   ss->scl.fairing_mask, etc).

This is the final version of the API that will be ported to master
(possibly minus the SCULPT_xxx alias functions that simply call
BKE_sculptsession_XXX equivalents).
2022-07-15 20:18:44 -07:00
93b96bab9a Merge branch 'master' into sculpt-dev 2022-07-03 23:39:50 -07:00
c135ddb9b4 sculpt-dev: Cleanup warnings 2022-06-14 10:52:12 -07:00
443bc36759 sculpt-dev: remove named struct parameter initilization 2022-06-14 10:50:39 -07:00
c73fc97d45 sculpt-dev: Fix undo bug 2022-06-14 10:24:53 -07:00
47a14ef4ad sculpt-dev: Fix draw cache bug
Fix the pbvh draw optimization that only sends
active attributes to the GPU. The solution here
is the original sculpt-dev one (somehow this
ended up being removed but not replaced in the
pbvh-eevee patch).

It works by checking every viewport in every
window and if it finds eevee or workbench
in material mode it sets a flag in the pbvh
to forcibly upload all attributes.

Another approach is to simply rewrite pbvh
draw properly inside the draw manager to
begin with.
2022-06-14 09:52:34 -07:00
e108318af8 Merge remote-tracking branch 'origin' into sculpt-dev 2022-06-14 02:01:58 -07:00
1cd88104f0 Sculpt-dev: fix bmesh python bug
Save and restore python pointers when
adding or removing customdata layers, this is needed
since tool flags are a customdata layer in sculpt-dev.
Reallocating tool flags shouldn't clear python pointers.
2022-06-13 20:13:02 -07:00
d54416506a Merge remote-tracking branch 'origin' into sculpt-dev 2022-06-02 23:44:50 -07:00
3afff59ca5 sculpt-dev: Preemptively apply 'e' enum prefix change before merge 2022-06-02 17:30:21 -07:00
b60f298608 sculpt-dev: fix crash with missing pmap 2022-06-01 20:21:32 -07:00
c12e3ff183 sculpt-dev: fix pmap ref count bug 2022-06-01 19:45:59 -07:00
d7d6ad76d7 sculpt-dev: Fix tip_roundness versioning bug 2022-06-01 19:34:22 -07:00
4c3d11c233 Merge remote-tracking branch 'origin' into sculpt-dev 2022-05-26 13:45:19 -07:00
445af8da7a sculpt-dev: Update chat link
Also fixed a nullptr crash.
2022-05-25 09:58:43 -07:00
4b41114f67 Sculpt-dev: fix brush test api bug
* Fixed two missing break statements.
  Amazing what a cascade of bugs this
  produced.
* Fixed small bug in BKE_brush_curve_strength_ex.
* The radius scale fix for original normal mode
  now applies to all sculpt brushes, not just
  the draw brush.
2022-05-22 02:47:11 -07:00
3210924485 Sculpt-dev: Properly fix BKE_brush_curve_strength_ex & revert prior hack
* BKE_brush_curve_strength_ex now takes an argment on
  whether to invert the input.
* BKE_brush_curve is now deprecated, it applies prior
  behavior of disabling inversion for custom curves
2022-05-21 23:10:16 -07:00
7217d1f355 Sculpt-dev: Clean up brush test code
* The standard sculpt brush spot test
  API now supports square brushes.
* Supports tip scale x from paint brush.
* TODO: rewrite clay strips and paint
  brushes to use standard api.
* Roll texture mapping semi-works.
2022-05-21 14:29:18 -07:00
d6d1ddbf0b Sculpt-dev: Roll brush tests.
Wrote test code for a "roll" texture mapping
mode.  This is purely a development test, there
is no user-visible functionality here.
2022-05-17 00:12:44 -07:00
7e969000a2 sculpt-dev: fix crash in detail flood fill 2022-05-14 14:32:16 -07:00
d2d5d382c5 sculpt-dev: Fix bmesh toolflags crash 2022-05-13 23:48:26 -07:00
394bd7fe05 Sculpt-dev: Fix crash in bmesh conversion code 2022-05-13 23:34:28 -07:00
da4349c147 Sculpt-dev: fix shapekey undo bug with PBVH_FACES
* Also fixed bug in bmesh conversion code.
2022-05-13 16:53:47 -07:00
cd316eb6c6 Merge remote-tracking branch 'origin/master' into sculpt-dev
Also fixed a bug with eevee and color attributes.
2022-05-12 01:10:47 -07:00
09b2261669 Sculpt-dev: fix crash in rna color_attributes iterator 2022-05-12 00:23:11 -07:00
7677b2428a Sculpt-dev: fix merge errors in attribute code 2022-05-11 23:30:09 -07:00
e5a111c637 sculpt-dev: Device mapping curves reversed
* Turns out BKE_brush_curve_strength_ex requires inverted input,
  except for BRUSH_CURVE_CUSTOM.
2022-05-11 22:38:09 -07:00
7a21cc4f70 Sculpt-dev: Fix brush curves being allocated in error. 2022-05-11 19:53:45 -07:00
cfc46e43b2 Sculpt-dev: Use brush curve presets for device curves
* Brush mapping (device) curves now use the BrushCurve
  API.  This prevents having to save a CurveMapping
  instance for every single device mapping (of which there
  is ~5 or so) in every single channel (>40) for
  every single brush (~40-70).
* Fixed regression where temporary attributes weren't being
  stripped on file save in all cases.
2022-05-11 19:18:33 -07:00
ca8f30d54e Merge branch 'master' into sculpt-dev 2022-05-11 16:05:35 -07:00
67fdff9d69 Sculpt-dev: Fix merge bug and broken origmask
* Remove error check for PBVH_BMESH that's
  not valid in sculpt-dev.
* Fix bug in original mask api.
2022-05-04 20:33:43 -07:00
eb8b7e3292 Sculpt-dev: finish up merge 2022-04-30 01:09:57 -07:00
2cb7a0066d Merge remote-tracking branch 'origin/master' into sculpt-dev 2022-04-29 22:37:03 -07:00
2eb8f66de8 Sculpt-Dev:
* Change CD_MLOOPCOL to CD_PROP_BYTE_COLOR preemptively before merge
* Fix color attribute bug with dyntopo
2022-04-29 21:02:46 -07:00
d2dee8f482 Sculpt-dev: fix crash in paint brush 2022-04-20 13:28:55 -07:00
afc86042b7 Sculpt-dev: fix remesh crash and a compile error. 2022-04-19 19:16:31 -07:00
c4f48e240e Sculpt-dev: Fix incorrect static function forward prototype. 2022-04-18 12:52:35 -07:00
55740b7bee Sculpt-dev: make mask slice work in dyntopo mode. 2022-04-18 12:50:31 -07:00
25ae5ab2d9 Sculpt-dev: fix crash in mask slice 2022-04-18 12:38:01 -07:00
185ca2912f Sculpt-dev: fix cloth crash
* Fixed ss->totedges not being set correctly
  in all cases.  Fixes crash in cloth brush.
* Fix compile errors in alembic and fluid
  code.
* Remove a couple of debug ATTR_NO_OPTs.
2022-04-17 11:01:47 -07:00
ae555c3219 Sculpt-dev: fix compile errors from merge. 2022-04-15 20:03:50 -07:00
ecbf681e31 Merge remote-tracking branch 'origin/master' into sculpt-dev 2022-04-14 22:25:34 -07:00
953ee087b4 Sculpt-dev: commit a few temporary bugfixes
prior to merge.

If fixes are still valid after merge they will
submitted for master.
2022-04-14 22:17:32 -07:00
f0c2994b02 Sculpt-dev: Fix more crashes from recent merge
* Fixed crashes in color attribute system
* Fixed mask related crash.
* Removed dead code.
2022-04-11 21:59:38 -07:00
ed550e1da9 Sculpt-dev: fix bugs in recent merge 2022-04-09 02:13:21 -07:00
a38889b2bd Sculpt-dev: Fix merge errors
* Cleaned up a few old bmesh-only pbvh
  functions into generic ones.
* Sculpt colors from master is now merged in.
* Implemenation for querying original verts is now
  inside pbvh instead of sculpt.
2022-04-08 21:36:50 -07:00
d75932cbef Merge branch 'master' into sculpt-dev 2022-04-08 17:28:02 -07:00
6f5535e56c Merge branch 'master' into sculpt-dev 2022-04-08 17:27:29 -07:00
5761ccbf22 sculpt-dev: commit prior to merge
Removed a bunch of debug stuff.
2022-04-08 13:21:55 -07:00
8a58f1f3bb Merge remote-tracking branch 'origin' into sculpt-dev 2022-03-31 19:11:32 -07:00
273040f84c Sculpt-dev: fix another pbvh cache crash 2022-03-25 02:47:14 -07:00
711bcef7f4 Sculpt-dev: Yet another pbvh cache crash 2022-03-24 10:46:11 -07:00
34d8df4aba sculpt-dev: fix another pbvh cache crash 2022-03-24 10:38:25 -07:00
43af973cd9 sculpt-dev: fix another bug in pbvh cache 2022-03-24 10:32:00 -07:00
b53339edfa Sculpt-dev: fix crash in pbvh cache 2022-03-23 23:59:29 -07:00
307f13aee5 Sculpt-dev: new PBVH caching system
* Still experimental
* PBVHs are cached in a global map keyed by
  object name (though at the moment only one pbvh
  is cached at a time).
* ss->pmap is now freed managed by pbvh.
* ss->bm is freed by the pbvh cache in some
  circumstances but is stilled owned by SculptSession.
2022-03-18 21:12:43 -07:00
371a6799c4 Sculpt-dev: Fix local collpase mode
* Also disabled "cleanup" mode, need to rewrite
  the vertex dissolve func it uses.
2022-03-14 14:19:23 -07:00
891b8797ed Sculpt-dev: Fix invalid prototype and remove
a bunch of ATTR_NO_OPTs
2022-03-14 10:50:20 -07:00
389724c1ac Sculpt-dev: Change the negative scale warning from RPT_WARN
to RPT_ERROR.
2022-03-14 10:47:11 -07:00
194ec7460c sculpt-dev: Fix broken keymap entry 2022-03-14 10:34:32 -07:00
b71693b5f0 Sculpt-dev: fix exploding vertices bug
Added code to detect and fix unnormalized normals,
still need to track down where exactly they are
coming from.
2022-03-13 22:10:58 -07:00
8b719fee19 Sculpt-dev: Dyntopo Tweaks
* Increased substep count.
* Snake hook now runs dyntopo twice by
  inserting it into its commandlist twice,
  instead of running over the same nodes and
  re-executing it.
2022-03-12 12:21:17 -08:00
54a3c7525b Merge branch 'master' into sculpt-dev 2022-03-12 00:15:29 -08:00
ca9f462a59 sculpt-dev: Fix anchored stroke mode
* Anchored stroke mode now works with
  new brush command system
* Fixed original normal calc for anchored
  brushes.
* Added yet more code in my as-yet vain
  attempt to enforce seperation of
  graphic drivers and ASAN.
2022-03-07 12:54:05 -08:00
47f54d332e sculpt-dev: Fix a couple of memory leaks. 2022-02-24 02:17:58 -08:00
a7c944e50d Merge branch 'master' into sculpt-dev 2022-02-24 02:05:52 -08:00
c9c9b80593 Sculpt-dev: fixed edge collapse
* Code needs more cleanup
* Added even more debugging tools
  to bmesh_log.c.
2022-02-23 20:56:32 -08:00
1591c9a978 Merge branch 'master' into sculpt-dev 2022-02-14 09:41:42 -08:00
a5cc113c7b Sculpt-dev: Code cleanup 2022-02-08 20:09:25 -08:00
5f631ca7c8 sculpt-dev: started work on new customdata
backend for BMesh for profiling
	    purposes

* Purpose is to test the performance of
  block vs page vs array CustomData allocation.
* Code is as simple as possible.
* Patches existing BMesh CustomData API.
* Not intended for master.
2022-02-01 20:14:27 -08:00
9d09011f91 Sculpt-dev: part 2 of dyntopo cleanup
Code is now much cleaner, at least
relative to before.  Still need to test
local mode.
2022-01-31 19:03:47 -08:00
a393db3c23 Sculpt-dev: commit 1 of dyntopo cleanup
In an attempt to fix dyntopo artifacts with
snake hook I wrote a new heap implementation
for dyntopo, a min-max heap.

Turns out the seperation of collapse and
subdivision queues was a fundamental design
flaw in DynTopo.  Unifiying them in
a single heap is much faster (as much as
2x faster depending on the setup), and
will allow for much cleaner code.
2022-01-31 16:42:57 -08:00
778db43b8f Sculpt-dev: another merge error 2022-01-31 09:39:21 -08:00
5854892d95 Sculpt-dev: fix merge errors 2022-01-31 09:32:23 -08:00
73accfecca Merge 2022-01-31 09:23:44 -08:00
7705166438 Merge branch 'master' into sculpt-dev 2022-01-29 14:07:16 -08:00
20a8d55bf3 Sculpt-dev: fix another merge error 2022-01-25 16:51:22 -08:00
7eb1dd08ba Sculpt-dev: fix improper forward declaration 2022-01-25 16:27:09 -08:00
0e7e759a33 Sculpt-dev: fix a few merge errors 2022-01-25 15:14:06 -08:00
3165ec4dac Sculpt-dev: Add some more debug printfs for wintab 2022-01-25 14:21:58 -08:00
cc5c1e7837 Sculpt-dev: Print error message to console
when wintab initilization fails.
2022-01-25 14:12:04 -08:00
a34611b453 Sculpt-dev: Roughed out brush channel
C++ API.

Basic idea is to wrap the C DNA structs
in a C++ wrapper class.  Instances of
this class are then passed around by
value.

Rewrote BKE_brush_channelset_get_final_float
and BKE_brush_channelset_get_final_vector to
use these classes.  Seems to work, will test
more.

Also, added a bunch of files that git failed
to add in the last merge.
2022-01-23 12:53:42 -08:00
ccbde00a83 Merge sculpt-dev -> master 2022-01-23 12:27:34 -08:00
1acb608f52 Sculpt-dev: Start moving brush engine API to C++
Meed to do a merge so the code is in an
incomplete state.  Anyway, I'm going to
move the brush engine code to C++, the
existing C code would never pass code
review.

I figure I can do this incrementally over
the next month or two and then submit a patch.
2022-01-22 10:59:35 -08:00
3e081fba86 Sculpt-dev: update doc comments for sculpt_intern.h
Also fixed a few warnings
2022-01-19 16:18:18 -08:00
95919953af Sculpt-dev: fix compile error from last merge 2022-01-18 21:41:13 -08:00
533009b095 Merge branch 'master' into sculpt-dev 2022-01-16 21:36:29 -08:00
959dfc9d88 Sculpt-dev: redo last commit to be
a bit better
2022-01-16 21:23:57 -08:00
eedb3d0685 Sculpt-dev: fix crash in sculpt attribute API 2022-01-16 21:12:52 -08:00
4d54182159 Sculpt-dev: fix a few broken panels and a crash 2022-01-15 06:58:10 -08:00
bf812abfcb Sculpt-dev: fix dyntopo not working for scrape brush 2022-01-14 21:59:18 -08:00
59d1995311 Merge branch 'master' into sculpt-dev 2022-01-14 02:10:10 -08:00
55fac22463 Merge branch 'master' into sculpt-dev 2022-01-14 02:09:31 -08:00
0de6f16a25 Commit prior to merge 2022-01-13 16:51:52 -08:00
fab69104da Sculpt-dev: fix a few bugs in last commit
and update some icons

* Fixed crash in automasking when ss->cache
  is not present.
* Updated color filter icon.
* Use standard brush icon for paint.
2022-01-12 02:33:52 -08:00
5e27ed5f0e Sculpt-dev: clean up sculpt custom attribute API
* Cleaned up naming.
  - SCULPT_temp_customlayer_ensure is now
    SCULPT_attr_ensure_layer.
  - SCULPT_temp_cdata_get is now
    SCULPT_attr_vertex_data.
  - SCULPT_temp_cdata_get_f is now
    SCULPT_attr_face_data
  - SCULPT_temp_customlayer_get is now
    SCULPT_attr_get_layer.
* Replaced with ss->limit_surface with an
  entry in ss->custom_layers.
2022-01-12 01:13:15 -08:00
15cb3130c3 Sculpt-dev: fix crash related to curve
cache

* Fixed a few bugs in the curvemapping
  cache code.
* Fixed bug in how BKE_channelset_compat_load
  was copying brush.curve to/from the falloff_curve
  brush channel.
2022-01-11 23:26:56 -08:00
bfd1dc3a75 Sculpt-dev: increase pbvh max depth
Fixes performance regression.
2022-01-10 03:58:42 -08:00
1f43ea041e Sculpt-dev: fix broken brush falloff
curve defaults

* brush_engine_presets.c now sets falloff
  curves properly when resetting brushes.
* Fixed visual bug in one-column toolbar.
2022-01-10 03:32:11 -08:00
ac4f8292d0 Sculpt-dev: fix a few warnings 2022-01-08 13:01:37 -08:00
118edbef0e Sculpt-dev: bke_pbvh_update_vert_boundary is
now atomic

* bke_pbvh_update_vert_boundary now uses atomics
* This allows dyntopo surface smoothing
  (surface_smooth_v_safe) to update vertex boundary
  flags inside of a thread.  This lessens degenerate
  geometry created by hard edged brushes like
  clay strips.
2022-01-08 12:57:29 -08:00
8b3fc04d4e Sculpt-dev: Fix typo in python script 2022-01-08 11:54:51 -08:00
0584c813b5 Sculpt-dev: fix gcc compile error 2022-01-08 02:09:53 -08:00
c8aedd75d7 Sculpt-dev: New normal automasking modes
Two new automasking modes: "Brush Normal"
and "View Normal."

Brush Normal compares vertex normals to
the initial sculpt normal, while View
Normal of course compares with the
view vector.

Each of these modes have an angular limit
and a falloff.  There's also an "original
normal" option, which needs a better name;
"original normal" is actually already taken,
but "automasking original normal" is a lot of
characters.  Not sure what to do here.
2022-01-07 12:03:40 -08:00
7e1c56d909 Sculpt-dev: fix sculpt colors for new opensubdiv
system.
2022-01-03 23:29:51 -08:00
cbe010ca00 Merge branch 'master' into sculpt-dev 2022-01-03 05:20:10 -08:00
c5dbd4bc87 Sculpt-dev: add range checking for vertex ids 2022-01-02 22:15:21 -08:00
8f18ee27e7 Sculpt-dev: New displacement heal brush
This brush goes through all
the grids inside each PBVH node under
the brush, converts coordinates to
tangent space, filters out extreme displacements
and then converts back.

Simple, but very effective.

TODO: make this into a mesh filter too.
2021-12-21 11:31:59 -05:00
084a967ab4 Merge branch 'master' into sculpt-dev 2021-12-20 14:54:25 -05:00
134eccf5bf Merge branch 'master' into sculpt-dev 2021-12-20 14:37:48 -05:00
e0039cb302 Merge with master 2021-12-20 14:31:59 -05:00
af0350393e Sculpt-dev: Rearrange a few functions 2021-12-20 14:07:54 -05:00
0837926740 Sculpt-dev: fix weight paint panel showing
up in sculpt mode
2021-12-14 14:09:42 -08:00
302dba95a3 Sculpt-dev: fix BKE_brush_add
* BKE_brush_add now calls BKE_brush_sculpt_reset if
  ob_mode has OB_MODE_SCULPT set.  This should hopefully
  eliminate a whole class of bugs I've been chasing.
2021-12-14 13:45:47 -08:00
0917431ca4 Merge branch 'master' into sculpt-dev 2021-12-14 13:27:44 -08:00
33586f16fc Sculpt-dev: split automasking into its own panel
Also replaced texture dropdown in sculpt header
with automasking.
2021-12-14 11:23:27 -08:00
9f7f437af7 Sculpt-dev: add option to ignore UVs
* Added option to ignore UVs for sculpt
  mode.
* Tweaked sculpt header a bit.
2021-12-14 11:16:10 -08:00
4a9495ef7d Sculpt-dev: fix pbvh_bmesh_normals_update 2021-12-14 10:19:21 -08:00
268e18e9a3 Sculpt-dev: More improvements to detail enhance
* Detail enhance is now it's own dedicated toolslot.
  This makes it possible to use autosmooth with
  detail enhance.

  Note the behavior of the smooth brush (which
  invokes enhance when pressing control)
  is unchanged.

* Fixed a bug with temporary attribute layers being constantly
  cleared and reallocated with multires.

* Cleaned up velocity smooth code a bit.
* Pruned various dead #if blocks.
* generate_from_enum_ex in space_toolsystem.py
  now takes a special argument to combine enum
  items.  This is used to make enhance a subtool
  of smooth.
2021-12-12 05:03:24 -08:00
0b9e40cc0c Sculpt-dev: another gcc fix 2021-12-11 14:01:46 -08:00
03cb9aee26 Sculpt-dev: more linux errors
Need to figure out -Wshadow equivalent
for msvc.
2021-12-11 13:28:19 -08:00
f9a2a9ae45 Sculpt-dev: fix extraneous calls to
SCULPT_vertex_random_access_ensure

Fixes performance regression in simplify brush.
2021-12-11 13:24:31 -08:00
792b39e193 Sculpt-dev: cleanup SCULPT_neighbor_coords_average_interior
* The "boundary smooth" (psuedo-bevel) functionality of
  SCULPT_neighbor_coords_average_interior is now its
  own function.
* Fixed a few more pedantic gcc errors.
2021-12-11 13:10:44 -08:00
cde09a4b24 Sculpt-dev: attempt to fix warnings/error on linux 2021-12-11 12:26:19 -08:00
6ccfeae9b1 Merge branch 'master' into sculpt-dev 2021-12-11 01:43:14 -08:00
4a1d90ade7 commit_log.txt 2021-12-11 00:20:33 -08:00
744361a1af commit_log.txt 2021-12-11 00:07:34 -08:00
9a77a920f0 Merge branch 'master' into sculpt-dev 2021-12-04 20:07:32 -08:00
3788fd2c29 Sculpt-dev: expose a few cloth settings for cloth
deform target in bounday/pose brushes.
2021-12-04 19:45:31 -08:00
fbc020c752 Sculpt: fix twist brush mirroring 2021-12-03 05:22:49 -08:00
a5c3d3476c Sculpt-dev: fix memory corruption
* Forgot to change the face area
  customdata layer to CD_FLOAT2 everywhere,
  leading to nasty memory corruption.
2021-12-03 00:51:42 -08:00
e4b6180c4e Sculpt-dev: fix elastic deform brush
* Volume preservation had wrong bounds in
  elastic deform brush leading to wrong
  behavior.
* Fixed unprojected_radius channel not
  being shown in header when scene radius
  unit is on.
* Draw face sets now only fully rebuild draw buffers
  in indexed draw modes that require it.
2021-12-02 16:37:31 -08:00
61e2d15bfe Sculpt-dev: fix memory corruption 2021-11-28 18:50:42 -08:00
62f68639e0 Sculpt-dev: Fix color filter not auto
creating a color layer.

* This is probably something I broke.
  There was a SCULPT_has_colors() call that
  wasn't needed.
2021-11-28 18:36:03 -08:00
777168cc00 Sculpt-dev: fix memory corruption 2021-11-28 02:37:01 -08:00
7ad9afd08a Sculpt-dev: fix broken color filter, alpha
was being set to garbage value.
2021-11-28 02:30:28 -08:00
628925a5c6 Sculpt-dev: fix thread contention
in weighted smooth
* Cached face areas are now updated
  in a double buffered fashion;
  all threads read from one side of
  the buffer while the other is written
  to by the threads that own a given
  face; the buffers are swapped on
  each iteration of a tool that uses
  face areas.
* Fixes smooth flickering.
2021-11-28 02:23:06 -08:00
e21c21bbf9 Sculpt-dev: support area weighted smooth for PBVH_FACES
* This was actually kind of annoying; the
  vertex->face-area-list code is in pbvh and
  relies on edge ordering
  around verts, but that order is non-trivial for
  PBVH_FACES (relying as it does on a vertex->poly
  map).  This ordering was calculated entirely in
  editors/sculpt_paint/sculpt.c, not callable from
  pbvh.
* The solution was to add a helper function to pbvh
  for building vertex->edge lists from vertex->poly
  maps.  This is then used by both sculpt.c and the
  vertex->face-area-list code.

* Also improved boundary bevel smooth a bit.  I'm
  thinking of extracting it from SCULPT_neighbor_coords_average_interior
  into its own function and possibly its own brush.
2021-11-28 01:19:23 -08:00
e5804dc607 Sculpt-dev: fix corruption in dyntopo
fast pbvh leaf builder callback.
2021-11-27 09:17:53 -08:00
66d362b464 Sculpt-dev: don't show input mapping
settings in sculpt context menu
2021-11-27 09:02:49 -08:00
1e4b49d3c1 Sculpt-dev: Add brush_eval field to Paint
* Paint now has a brush_eval field which
  is used in leu of ->brush if non-null.
* This lets us get rid of all the annoying:
  `brush = ss->cache ? ss->cache->brush : BKE_paint_brush`
  code.  Now it's just BKE_paint_brush.
* Used by SCULPT_run_command.
* Also fixed nasty scene spacing bug.
2021-11-26 21:10:14 -08:00
a662f81822 Sculpt-dev: fix multires crash in last commit 2021-11-26 16:27:19 -08:00
8de1899c33 Sculpt-dev: bugfixes
* Fixed PBVH_FACES vcol draw bug.
* Fixed boundary smooth being turned on
  if its temp customdata layer exists.
* Fixed unnesting of brush panels from
  last commit improperly showing up
  in the workspace tab for sculpt
  instead of the brush tab.
2021-11-26 15:39:44 -08:00
f13bedd649 Sculpt-dev: sculpt_init_tool_override_channels
related fixes

Various fixes so sculpt_init_tool_override_channels
for shift-smooth can replicate the prior behavior:

* Brush spacing will now look up brush channel
  spacing directly for sculpt, instead of relying
  on copying the channel data into Brush.
* Brush spacing code will now use brush channel
  pressure for sculpt.  Fixes broken shift-smooth
  pen pressure.
* The falloff_curve channel is now automatically
  added (before it was only used internally by
  command lists, the code was defaulting to
  the Brush field otherwise).
* BrushCurve now has an option for custom curve
  presets to have negative slopes.
* The Falloff panel now puts the type dropbox
  inside the panel header.
* Falloff panel also now uses brush channel data in
  sculpt mode.
* falloff_shape is now a brush channel

In a somewhat unrelated change, I also unnested the
Brush Settings subpanels.  It's been driving me
insane for a very, very long time.  Much more
usable this way.
2021-11-25 11:34:24 -08:00
9f45edd430 Sculpt-dev: fix smooth_strength_factor bug
* Fixed bug where BRUSH_MAPPING_INHERIT was
  being or'd to BrushMapping->flag instead
  of assigned to ->inherit_mode.
* Fixed smooth_strength_factor
  and smooth_strength_projection.
* Also added yet more asan poisoning to mempool
* Added a function to build final inherited brush
  channel, BKE_brush_channel_copy_final_data.  Takes
  BrushMapping->inherit_mode into account.
2021-11-23 19:46:18 -08:00
7f933459a8 Sculpt-dev: undo debug disabling of threads for
color filter.
2021-11-20 08:52:40 -08:00
2255892f5b Sculpt-dev: add random_[hue/sat/value]
color filter options from
	    sculpt-mode-features

* Follows comment in mesh filter's randomize and
  uses BLI_hash_int_3d.
* Added a seed parameter.
* Also renamed SCULPT_get_mdyntopo to SCULPT_get_sculptvert.
2021-11-20 08:49:14 -08:00
7753fda1b5 Sculpt-dev: fix broken vcol attr
auto-creation

* Fixed BKE_sculpt_color_layer_create_if_needed
* Also fixed BKE_id_attribute_active_color_set,
  accidentally pasted the getter's precondition.
2021-11-20 07:49:40 -08:00
39292be4cf Sculpt-dev: fix another crash 2021-11-19 07:08:03 -08:00
605a06975c Sculpt-dev: fix gcc compile error 2021-11-19 07:01:17 -08:00
bcf91617b3 Sculpt-dev: fix crash
* Tool slots weren't initializes sculpt
  brushes properly.
2021-11-19 06:58:54 -08:00
26a82fd5cc Sculpt-dev: improve uv smooth brush a bit 2021-11-19 04:50:20 -08:00
9c9be5599b Sculpt-dev: SCULPT_reproject_cdata fixes
* SCULPT_reproject_cdata now forcibly re-snaps
  for non-boundary loops.
* Also fixed a wrong radial loop iterator.
2021-11-19 03:28:58 -08:00
e5394e962a Sculpt-dev: dyntopo now detects uv island
boundaries, independently of
	    edge seams.
2021-11-17 16:42:56 -08:00
7d870c75fb sculpt-dev: remove debug ATTR_NO_OPTS 2021-11-17 15:32:44 -08:00
5ce01b8ce8 * Sculpt-dev: pbvh draw cleanup
* PBVH drawing for eevee is now used for
  PBVH_FACES as well as PBVH_BMESH.
* PBVH_FACES now uses pbvh draw for eevee rendering
* Refactored gpu_pbvh_gpu_make_vcol_offs into a
  more general gpu_pbvh_gpu_make_attr_offs.
  This should be a usable alternative to using a generic
  attribute gpu system (whether the one that's #ifdef'd out
  in gpu_buffers.c, or the new one that hit master
  recently).
* Textured workbench draw mode now works for pbvh drawing.
* Fixed nasty stack overflow in dyntopo edge collapse.
2021-11-17 15:27:47 -08:00
51f1a359a1 Sculpt-dev: run clang-format on a few files 2021-11-17 13:16:09 -08:00
a4ce7b6741 Sculpt-dev: fix pbvh draw getting gpu format
out of sync.

GPU_pbvh_update_attribute_names now returns a bool
if the format has changed.
2021-11-17 13:14:10 -08:00
4144366bc1 Sculpt-dev: fix bug in last commit 2021-11-17 04:18:09 -08:00
c4781c7243 Sculpt-dev: cross-module ref cleanup
* Removed a bunch of nasty editors->blenkernel
  dependencies (2 still remain).
* Fixed a few bugs in last commit.
2021-11-17 04:08:41 -08:00
e724479ca6 Sculpt-dev: sculpt colors stuff
* Mask by color now works for dyntopo.
* Attribute and vertex color lists in the UI
  now hide temporary CD layers.
* Added a specific op for removing vertex color
  attributes.
* Fixed various bugs.
2021-11-17 03:09:37 -08:00
7a55066b18 Sculpt-dev: Add support for attribute triplets
It simply wasn't maintainable to store active/render
vertex color layer as integer indices into the
global attribute list.  Making that work would've
required a great deal of boilerplate that would
have to be inserted in lots of places.  There
is simply no justification for doing that.

Instead, I've coded an AttributeRef struct that
simply stores a (type, name, domain) triplet to
uniquely reference an attribute layer.

This will be submitted as a patch for master
too.
2021-11-16 21:58:50 -08:00
b2c64e2147 Sculpt-dev: fix compile error 2021-11-16 19:17:13 -08:00
ddd1af5901 Sculpt-dev: fix crash 2021-11-16 16:23:42 -08:00
581dc18c08 Remove debug ATTR_NO_OPT 2021-11-16 13:32:42 -08:00
cddcc54f32 Sculpt-dev: fix brush input mapping inheritance
* Brush input mappings now have three
  inheritance mode: "always", "never" and
  "channel" (use channel's inheritance mode).
* Note that the UI exposes a single inherit icon,
  which just toggles between "always" and
  "never," to lessen user confusion.
* Brush mappings default to "never" inheritance
  mode.
* Fixed wrong node update flag in bke_pbvh_insert_face_finalize
  leading to disappearing parts of the mesh.
2021-11-16 13:26:39 -08:00
b43119ad84 Sculpt-dev: add icons for twist and multires
displacement smear.
2021-11-14 03:34:26 -08:00
e68c7df94e Sculpt-dev: use standard UI idioms
Thanks to @TonatiuhdeSanJulián on chat
for helping me with this.
2021-11-14 02:37:51 -08:00
e1cf0657d8 Merge branch 'master' into sculpt-dev 2021-11-14 02:35:23 -08:00
9f7ebd9c39 Sculpt-dev: update pbvh_print_mem_size 2021-11-14 01:59:08 -08:00
daf03b6f56 Sculpt-dev: fix another freelist id bug 2021-11-14 01:28:18 -08:00
b633032711 Sculpt-dev: add corruption check
Stopgap measure to deal with rare
PBVH corruption until I have the
time to properly track it down.
2021-11-14 01:10:02 -08:00
2e782109fc Sculpt-dev: Replace usage of smallhash with ghash
SmallHash doesn't deal with repeated removals
and insertions very well.
2021-11-13 18:57:39 -08:00
c88701178b Sculpt-dev: fix missing NULL assignment 2021-11-13 16:17:16 -08:00
a72721a2ae Sculpt-dev: Performance improvements
* PBVH_BMESH construction is now partially
  threaded.
* Fixed n**2 behavior in freelist-based
  bmesh id implementation.  I'd really
  rather get range-tree faster, but
  this works as a stopgap.
* Removed a bunch of debug ATTR_NO_OPTs.
2021-11-13 01:13:47 -08:00
35133f9a06 Sculpt-dev: fix compile error
Why can't we have -werror settings
the same on all compilers and platforms?
2021-11-09 10:16:45 -08:00
66523d2447 Sculpt-dev: fix test 2021-11-09 01:49:39 -08:00
0569618578 Sculpt-dev: get vcol render layer working
* Mesh now has a render_color_index member.
  + We really need an attribute ref system
    based on (domain, type, name) triplets.
* RNA uses render_color_index in all three
  cases (loop colors, vert colors, and
         the generic attribute types).
* PBVH draw uses render_color_index too.
2021-11-09 00:11:03 -08:00
f0d0369506 Sculpt-dev: all vertex colors now live in single
UI list.

* Added an index to Mesh for active color attribute.
  This seems janky to me, shouldn't this (along
  with the active attribute index) be a
  domain, name, cdtype triplet?
* Added a little api to preserve the active attribute
  and color indices when changing mesh customdata
  layouts.  See above comment.
* The vertex color panel is now completely unified.
* TODO: allow setting render color layer (not sure how
  to do this).
2021-11-08 21:09:17 -08:00
78d62d9be7 Merge remote-tracking branch 'origin/temp-sculpt-colors' into sculpt-dev
This was necassary because sculpt colors is currently broken in master,
and temp-sculpt-colors hasn't been approved for merging to master
yet.
2021-11-08 17:55:19 -08:00
6c0da7cc81 Merge branch 'master' into temp-sculpt-colors 2021-11-08 13:17:16 -08:00
a3f4e93497 Merge branch 'master' into sculpt-dev 2021-11-08 13:16:18 -08:00
58d7be5642 Sculpt-dev: add tool slot override to sculpt.brush_stroke
* Sculpt.brush_stroke now has a property to override
  the tool slot.
* Note that this is somewhat different from how this
  was typically done in the past.  Instead of physically
  switching slots we instead load the overriding slot's
  brush's channels into ss->cache->tool_override_channels.

Basic idea is based off of {D6515}
2021-11-03 18:34:33 -07:00
86fe67d6ed Fix python error 2021-11-03 16:42:04 -07:00
923547add3 temp-sculpt-colors: fix out of bounds error 2021-11-03 16:19:06 -07:00
65ef507b8b tempt-sculpt-colors: editmode fix 2021-11-03 16:01:06 -07:00
a8fa8bf364 temp-sculpt-colors: implement patch review items 2021-11-03 15:06:27 -07:00
3efad90211 Merge branch 'master' into temp-sculpt-colors 2021-11-03 14:39:07 -07:00
6a84d61c8b Update icons 2021-11-03 14:37:49 -07:00
d42acadba5 temp-sculpt-colors: vcol extractor rewrite
Draw cache system now properly uploads all three
vcol types to the gpu.
2021-11-03 14:30:40 -07:00
1a5ea3c8b3 Merge branch 'master' into temp-sculpt-colors 2021-11-03 13:03:43 -07:00
1dcc741f43 temp-sculpt-colors: commit patch 2021-11-03 12:15:13 -07:00
04d1bdbf36 Sculpt: memory fixes
* Removed a pointer from a sculpt cloth struct.
  Due to padding this doubled the size of the
  struct.  Hopefully this will be nicer on the L3 cache.
* Fixed a nasty memory leak in the smoothing code with multires.
2021-11-02 21:55:18 -07:00
87c6d95603 Sculpt: fix off-by-half error in
paint_stroke_apply_subspacing
2021-10-29 14:39:53 -07:00
736d1cf5f5 Sculpt: remove extraneous call to
SCULPT_vertex_random_access_ensure
2021-10-29 14:32:23 -07:00
9549dbb994 Sculpt: UI update
* Sculpt viewport header is now auto-generated in
  much the same way as the workspace buttons
  and right click menu.
* Added an API in the python code to insert
  bits of UI code after a brush channel, currently
  used to add flip colors operator.
* Added icon to add brush channels to header in
  brush edit mode.
* Updated input mappings UI.
2021-10-29 13:20:28 -07:00
ec3dd16ebb Sculpt: Remove a few debug ATTR_NO_OPT's 2021-10-28 10:57:53 -07:00
b701cd63ad Sculpt: various bugfixes
* BMLog now saves face material indices
* Fixed id corruption in join mesh edge case.
* The mesh->bmesh conversion function now checks
  if IDs are corrupted in a bit smarter way:
  + Any id that's greater then 5 times the sum of
    total elements with ids is assumed to be corrupt.
    Avoids very large allocations for the fast element->id
    map, which is a simple lookup table.
  + Alternative is to automatically switch to the slower
    GHash id->element map.
  + Sculpt code would have to detect this case and regenerate
    IDs?
* The slide relax brush is now internally split into two
  seperate commands.
* Basic smoothing now uses fewer iterations, velocity smooth
  is used to speed up convergence.
2021-10-28 10:50:38 -07:00
6aa14c3433 Sculpt: fix compile error
Missing #ifdefs for instant meshes
2021-10-26 02:35:26 -07:00
a7947a233d Sculpt: New "auto-fset" setting
Added a new "auto face set" setting for brushes.
It basically invokes the draw face set tool,
with a few differences:

* The painted face set is fixed by user setting.
* More then one face set can be created,
  these are assigned based on distance to
  the stroke line.
* TODO: write a proper API for wrangling stroke
        curves (should interpolate at least G2!).

The point of this is to enable better hard surface
brushes.  Since the settings are extremely finicky
I've added an example of one (based on clay strips)
to startup.blend.

The necassary steps to make a hard brush out of this
are as follows:

1. Autosmooth: ~0.4;
2. Autosmooth radius scale: ~1.5.
3. Auto fset: on.
4. Hard edge mode: on (but turn off inherit).
2021-10-26 01:55:13 -07:00
3bcfe9b5ac Sculpt-dev: fix compile errors for quadriflow
. . .when compiled with threading on (for quadriflow).
Still off however.
2021-10-25 22:23:28 -07:00
779aeb72ab Sculpt: integrate instant meshes
Still not particularly usable, but
much faster then Quadriflow.
2021-10-25 10:05:12 -07:00
95b84b5e13 Fork instant-meshes quadrangulator into intern/ 2021-10-24 00:17:54 -07:00
d294084432 Merge branch 'master' into sculpt-dev 2021-10-23 23:15:36 -07:00
d21737189e Sculpt: fix gcc compiler error 2021-10-22 10:48:34 -07:00
d765680f56 Sculpt: fix slide deform brush's deform
type setting.
2021-10-22 09:00:17 -07:00
2e3e10cc95 Sculpt: add option to fade face sets with a
Moire pattern instead of transparently.
2021-10-22 05:01:08 -07:00
243e9be588 Sculpt: disable slow code
How silly of me.  I had left on
some debug code I wrote for edge collapse.
This particular debug code generated a .obj
format string of the local mesh around an edge
before collapsing edges.

What amazes me is that the performance regression
was only about as bad as the original edge collapse
implementation in DynTopo.
2021-10-20 17:27:34 -07:00
c563169e32 Sculpt: revert past several commits
Note that I never pushed any of these to remote.

Decided to work on this in a local branch. I am
keeping the lock-free mempool code though.
2021-10-20 17:22:40 -07:00
6e74907f4f Sculpt: Continue individual edge locks experiemnt 2021-10-20 17:16:12 -07:00
5de8134abc Sculpt: experiment with lock-free mempools
and fine grained locks.
2021-10-20 14:14:12 -07:00
6ddd95f15a Sculpt: fix crash in pose face set fk 2021-10-20 09:26:13 -07:00
1d80f1a2b5 Merge branch 'master' into sculpt-dev
Also fixed bug with face set lasso
and face set from mask.
2021-10-20 09:20:19 -07:00
656a922a65 Sculpt: bugfixes
* Fixed smooth bug with seam corners.
* Fixed crash.
2021-10-20 08:22:27 -07:00
5cc582fec7 Sculpt: Implement accumulate for paint and
other fixes

* The paint brush now supports accumulate.
* Fixed bug with PBVH_FACES not setting
  MSculptVert update flags correctly in
  face set draw brush.
* Topology rake now has a mode where it
  simply propegates directions from
  boundaries.
2021-10-18 03:44:32 -07:00
a55b58476e Sculpt: Fix topology rake performance regression
* Don't use quite so many iterations
* Defer normals update
2021-10-17 16:27:55 -07:00
934abc9280 Sculpt: split operators from sculpt.c to sculpt_ops.c
Except for the main brush operator, that's still in sculpt.c.
2021-10-17 15:55:53 -07:00
08960bcda6 Sculpt: Split the brushes from sculpt.c into sculpt_brushes.c.
* Also killed the warnings in sculpt_boundary.c
2021-10-17 15:45:05 -07:00
f7bf7bec99 Sculpt: move dyntopo command to top of command list.
* Dyntopo is now run before other commands in the
  brush command lists.
* Fixed nasty command subspacing bug.
* Added a missing RNA library override flag.
2021-10-17 15:02:49 -07:00
c14fa10d56 Sculpt: Fix gcc compile error 2021-10-17 04:25:55 -07:00
1e7cae84e6 Sculpt: brush UI and debug changes
* You can now edit brush input mappings
  inside the main workspace brush panels.
* PBVH_CHECK_NAN now limits how many reports
  it prints from a given source file.
* Fixed various problems with shift-smooth strength
  setting.
* Fixed a NaN in the smoothing code.
2021-10-17 04:19:38 -07:00
f500ef58e2 Sculpt: fix memory corruption
* Fixed a nasty bit of memory corruption
* Along the way, added ASAN support to
  bmesh customdata blocks.
* Each CD layer is padded by 32 bytes
  inside the bmesh data block.
* Also fixed a few minor errors in
  mempool's asan support.
* Tried and failed to fix numerical
  stability issues with lasso/box
  trim brushes.
2021-10-17 01:57:33 -07:00
6d07e148b1 Sculpt: fix bug with building paths for
mapping curve inside the ui
2021-10-16 16:14:35 -07:00
556084f45a Sculpt: fix versioning error and add cutoff option
for square and cutoff mapping functions.
2021-10-16 15:52:05 -07:00
c65e1076a7 Sculpt: forgot yet again to increment the subversion 2021-10-16 15:13:09 -07:00
6777176691 Sculpt: brush input mapping improvements
* Input mappings now take a premultiply factor
  to scale the input data prior to evaluation;
* Mapping data can also now be fed through a
  (wave) function prior to evaluation.
* The UI now has seperate inputs and outputs
  sections for input mapping to avoid confusion.
* Added a distance mapping and implemented the speed
  mapping.
* Also fixed original data bug in color filter.
2021-10-16 15:06:36 -07:00
1e194722e5 Sculpt: fix missing NULL check for wire edges 2021-10-16 02:26:48 -07:00
f838fbde06 Sculpt: fix crash in PBVH_FACES 2021-10-16 01:48:14 -07:00
a5d816eb6d Sculpt: forget to increment subversion in
last commit
2021-10-16 01:20:22 -07:00
07be162dd5 Sculpt: brush input mappings improvements
Cleaned up brush channel input mappings:

* BrushMapping now stores .min/.max
* BrushMappingDef .min/max now sets BrushMapping min/max
  instead of changing the curve preset bounds.
* Fixed how BKE_brush_channel_eval_mappings evaluates
  the mappings stack.  Mappings now blend directly
  with channel value instead of accumulating a multiplier
  that's applied at the end.
* Consequently, BrushMapping->blendmode now defaults to MA_BLEND_MULT.
* Exposed BrushMapping->blendmode in RNA and UI.
  Note that it doesn't support every MA_BLEND_ type,
  it provides its own EnumPropertyItem list of
  supported blendmodes.
* Added a random input method, BRUSH_MAPPING_RANDOM.
* Fixed BRUSH_MAPPING_ANGLE being given data in the wrong
  range (all channels should be 0..1, not -pi..pi).

Other changes:
* Improved the uv smooth brush.  It's still hidden behind an
  experimental pref.
* Added a SCULPT_temp_customlayer_has function to check if a temporary
  customdata attribute layer exists.
* Fixed a bunch of broken sliders in the paint_toolsystem_common.py.
2021-10-16 01:16:21 -07:00
673e1fbac5 Sculpt: smothing ops now slide UVs
* Wrote a new function, SCULPT_reproject_cdata,
  to reproject loop customdata after smoothing.
* SCULPT_reproject_cdata is only called if UV
  layers exist.
* All of the smoothing tools (hopefully all)
  use it.
* This change is necassary to properly support vector
  displacement maps in the future; otherwise DynTopo
  will introduce lots of noise into the uv tangent
  space.
2021-10-15 13:40:02 -07:00
7d25a5ab3f Sculpt: fix adding shapekeys in sculpt mode
zeroing verts.
2021-10-14 22:52:40 -07:00
164ab68397 Fix missing comment slashes that somehow
compiled on msvc
2021-10-14 20:28:37 -07:00
77f3d2b423 Sculpt: Add various checks to detect NaNs
Most of this commit made it in one or two commits ago.
Added a little macro to detect mysterious NaNs reported
by users and that appear to be related to threading.

It's seeded in various places to hopefully catch
where this is happening.
2021-10-14 20:11:36 -07:00
151df9b3fc Sculpt: get rid of debug ATTR_NO_OPT's 2021-10-14 20:05:57 -07:00
da4138e1de Sculpt: sculpt colors fixes
* Paint brush now uses its own temp attribute
  layers instead of hijacking MSculptVert->origcolor.
* The various SCULPT_UNDO_XXX enums are now bit flags.
* Fixed anchored/drag drop mode for the paint brushes.
* Color hardening brush now works with dyntopo.
* Added a CD_FLAG_ELEM_NOINTERP flag to the customdata
  API.  If set it will either copy the first element
  in the sources list if CD_FLAG_ELEM_NOCOPY is unset,
  or nothing at all if it is.

  This necassary to properly support the design
  pattern whereby helper custom attributes
  reset themselves in each brush stroke by comparing
  a per-vertex stroke id with ss->stroke_id, thus obviating
  the need to walk the entire mesh at every stroke start.
2021-10-14 19:57:02 -07:00
38c47e8c03 Merge branch 'master' into sculpt-dev 2021-10-14 15:20:13 -07:00
8c28a9906e Sculpt: try to fix NaN bug 2021-10-13 22:05:11 -07:00
38d3054cae Sculpt: fix missing extern keyword 2021-10-13 18:59:01 -07:00
ba8d2c266a Sculpt: fix gcc compile errors 2021-10-13 18:59:01 -07:00
95e0bea7bf Sculpt: More BMLog fixes and cleanups
This commit rewrites much of the core BMLog
serialization logic.

BMLog originally did not support mesh
elements changing their internal topology,
which created ID conflicts in the log. Thus
it was impossible to use much of the BMesh API.
Instead DynTopo had its own implementations of
BM_edge_collapse and triangle edge splitting
that worked by recreating local mesh topology
from scratch.

I got rid of these functions a while ago and
tried to add support for elements changing
topology to BMLog. Unfortunatey I struggled to work
out all of  the edge cases, and it turned out easier to
refactor the core serializer methods wholesale.
2021-10-13 18:30:56 -07:00
b6442c3317 Sculpt: fix shape keys being destroyed by
SCULPT_UNDO_GEOMETRY/SYMMETRIZE pushes.

* Also cleaned up bmesh conversion parameters in
  a few other places.
2021-10-13 03:25:30 -07:00
4050acf830 Sculpt: add buttons in sculpt header to toggle
vertex color drawing.

* Added two buttons to toggle between vertex
  and material color modes in workbench drawing
  mode.
* They are labeled "Colors" and "Normal" to
  avoid confusion; "Vertex" is ambiguous, while
  "Material" might be taken to impley "EEVEE."
* For now they only show up if a sculpt paint
  brush is active.
* Updated startup.blend so the sculpt layout
  has vertex drawing mode on by default.
2021-10-13 02:45:33 -07:00
b8fb46ab20 Sculpt: BMLog stuff
* BMLog now has a more fine-grained logging
  facility to track the call chains
  that produce specific log elements.
* Wrote a new function in bmesh_core.c to
  dump local geometry around an element to
  stdout in .obj format.
* Edge collapse now properly handles the
  fact that bmesh handles sharp flags
  inversely, i.e. edges *not* marked
  with BM_ELEM_SMOOTH are sharp.

* Wrote a new BMLog API that handles elements
  undergoing topological changes a bit better.
  - BM_log_[edge/face]_[pre/post]
  - Idea is to call the _pre before calling
    collapse or split edge, then _post afterwards.
  - No longer need to assign new IDs
    in certain cases to avoid confusing BMLog.
  - Other parts of BMLog may now be redundant;
    need to check.

* Deleted some #if 0'd code
* Fixed a bug in BLI_smallhash_ensure_p, it didn't properly
  set up state for when smallhash is used as a very simple
  set.
2021-10-13 02:12:30 -07:00
ffa62ecb60 Sculpt: shapekey fixes
* Fixed the completely broken shapekey conversion
  in BM_mesh_bm_from_me; this bug is most likely
  in master as well.
* Note that, while shapekeys now work in dyntopo
  it does not represent a complete sculpt layers
  implementation; the layers are still stored in
  world space.
* The motivation for fixing this was to keep
  dyntopo from destroying shapekey data, which
  was the only form of custom attributes it didn't
  support.
* That was because shapekeys were never being converted
  to custom attributes for bmesh to begin with, the
  conversion code was quite broken.

In addition to all this, this commit also has
a fix dyntopo collapse fixes.
2021-10-12 15:18:59 -07:00
fe03543e8d Sculpt: fix missing NULL check 2021-10-12 01:05:10 -07:00
73eb2b426a Sculpt: fix collapse for non-manifold edges
* BM_edge_collapse now has an option to use
  a new collapse implementation that can
  handle non-manifold geometry properly.
* The aforementioned implementation is a replacement
  for bmesh_kernel_join_vert_kill_edge.  Note that
  the old code still exists as
  bmesh_kernel_join_vert_kill_edge_fast and is used
  by default.
2021-10-12 00:34:17 -07:00
e2755640c1 Sculpt: Allow non-manifold collapse
* This was always buggy, which is why
  I have code that detects and snips off
  non-manifold fins.
* Turns out it is more robust to simply allow
  non-manifold collapses and clean up geometry
  afterwards.  IIRC this is how DynTopo (and the
  paper it's based off of) originall worked.
2021-10-11 13:37:18 -07:00
cd2646adbf Sculpt: bump subversion 2021-10-11 13:29:23 -07:00
7293c2b5e5 Sculpt: enable dyntopo on paint brushes
* Paint brushes will now support
  dyntopo if "dyntopo disabled"
  (the one in their brush settings,
   not the global setting in the
   "Dynamic Topology" panel) is unchecked.
* Fixed dyntopo undo not properly handling
  pushing more then one undo type.
2021-10-11 13:27:47 -07:00
cbaa9f723b Sculpt: fix gcc compiler error
This one is an actual error.  I
went through the trouble of compiling
in a linux VM and still somehow managed
to miss it.

Also fix wrong range for normal_radius_factor.
2021-10-11 08:36:00 -07:00
318bc8ea42 Sculpt: fix symmetry bug and mesh filter
smooth improvements

* Fixed symmetry bug
* Exposed a few hard edge options to the mesh filter tool
* Updated default brushes inside of startup.blend.
2021-10-11 04:25:47 -07:00
6c16801001 Sculpt: add new bending constraint to cloth filter 2021-10-10 15:38:14 -07:00
3083e3fd26 Sculpt: experimental bending constraints for cloth
brush.

See "bending" checkmark in the cloth settings.
2021-10-10 14:37:26 -07:00
3b167c257d Sculpt: don't set strength to inherit from
scene defaults by default.
2021-10-08 16:22:48 -07:00
cf003b5e30 Sculpt: improve accuracy of tri in brush
test for dyntopo
2021-10-08 16:19:33 -07:00
0aac26fd2e Sculpt: fix sharp brush, new experimental
mode was being enabled when it
	shouldn't be
2021-10-08 00:28:32 -07:00
f91161a707 Sculpt: fix broken jitter and smooth
stabilize brush settings.
2021-10-07 19:44:38 -07:00
a1202d3ce1 Sculpt: Added a little brush icon tool slot
interface to mask expand in "selection"
	mode.

Leftclick runs normal expand.
Shfit-leftclick subtracts from mask.
Pressing CTRL inverts adding/subtracting
2021-10-07 06:14:37 -07:00
ba584f6819 Fix mask expand 2021-10-07 02:19:42 -07:00
e11ba956d2 Sculpt: various uv-related fixed
* The dyntopo collapse function now
  properly snaps UVs, including at
  island boundaries.
* PBVHTriBufs are now split by UVs
  (and face sets); this turned out
  to be surprising easy.
* Also fixed a few bugs relating to
  hiding/revealing stuff.
2021-10-06 21:54:10 -07:00
c2f2a8260c Merge branch 'master' into sculpt-dev 2021-10-06 18:33:08 -07:00
b970edb97e Sculpt: Add plane mode for draw sharp brush 2021-10-06 18:26:19 -07:00
313c6811c3 Sculpt: fix nasty radius symmetry bug
* Brush radius wasn't being calculated correctly if symmetry was on.
* Unnessed SCULPT_run_commandlist, it now calls
  do_symmetrical_brush_actions instead of the reverse.
* Renamed MDynTopoVert to MSculptVert.  Old name didn't
  make sense given that all three PBVH types now use it.
  Mercifully it's never saved in files, and even if it
  somehow was saved the CD file loading code checks for
  that.
2021-10-06 13:35:22 -07:00
88efbf7d1e Sculpt: fix multi-res crash 2021-10-06 03:10:57 -07:00
f6a8d745c2 Sculpt: fix broken ray casting of original data in pbvh
Not sure if this ever worked.
2021-10-06 01:46:25 -07:00
c209e0902d Sculpt: Fixed a few sculpt color issues
* Fixed broken smear brush
* Tweaked ui defaults a bit
2021-10-05 21:55:53 -07:00
6264a6327f remove ATTR_NO_OPTs 2021-10-05 20:09:45 -07:00
a0dbcd890d Sculpt: more nasty customdata bugs
* Fixed mesh_sculpt_vertex_color_add_exec,
  it's post-exec undo push was overwriting
  the mesh cd layout.
2021-10-05 20:07:25 -07:00
66c8a8ac24 Fix typo in last commit 2021-10-05 18:52:23 -07:00
86ee4dc571 Sculpt: Fix customdata_regen_active_refs 2021-10-05 18:38:29 -07:00
12ff7b4297 Sculpt: fix problem with smooth brush defaults
Also wrapped some mv->flag OR's in a
macro, that can be switched with a macro
for debugging.
2021-10-05 17:27:51 -07:00
3d4e14b7e1 Sculpt: various fixes
* Fixed very nasty customdata bug where
  if the first layer of a given type was
  CD_TEMPORARY, then on being stripped out
  of the customdata set the active references.

  I think I tracked down where this happened, but
  just to be safe I also wrote code to detect it and
  regenerate the active refs too.

* Fixed rotate brush.
* Fixed mesh filter crashing with automasking on.
2021-10-05 16:50:37 -07:00
0b5b8d0beb Sculpt: disable freelist mesh id backend,
trying to keep it disabled in builds
	until I've tested it a bit more.
2021-10-05 14:28:33 -07:00
727f5efa7b Add comment for last one-liner commit. 2021-10-05 14:27:31 -07:00
8082b19c10 Sculpt: Fix transform sculpt tools 2021-10-05 14:25:27 -07:00
47cbb9b1ac Sculpt: fix rotate brush 2021-10-05 14:19:53 -07:00
8dda36655e Sculpt: array brush fixes 2021-10-05 03:34:15 -07:00
22bf9507f1 Sculpt: add call to fix ui settings
to subversion 32 check.
2021-10-05 02:57:21 -07:00
91e70def7e Tweak last commit 2021-10-05 02:45:29 -07:00
73bd469fe0 * Sculpt: fix clay defaults again 2021-10-05 02:43:46 -07:00
19e4c235ba Sculpt: fix crash 2021-10-05 01:06:07 -07:00
7dd11f2edc Sculpt: fix dyntopo mode dropdown menu
Menu is now more intelligent about handling
brushes that use the "combine with scene defaults"
inheritance mode.
2021-10-05 00:28:19 -07:00
e557b2096a Sculpt: Fix topology rake updating original coordinates 2021-10-05 00:17:15 -07:00
4d8648a9f3 Sculpt: fix grab brush
Turns out brushes with large radii
can't have their proxy offsets immediately
applied, have to wait until all symmetry
passes have finished.
2021-10-04 17:04:10 -07:00
e46e95bb1f Remove dangling include 2021-10-04 12:20:32 -07:00
8435b4334d Fix gcc compile errors 2021-10-04 12:06:52 -07:00
f8df47977c Sculpt: Fix face set boundary flag settings in PBVH_FACES 2021-10-04 11:42:49 -07:00
ea19c3a4b8 Sculpt: fix set limit surface crashing
Also tweaked smallhash hash function.
2021-10-04 10:24:48 -07:00
23db569df7 Sculpt: added parameter to options panel
to control smooth projection
	(volume preservation) for shift-smooth
2021-10-04 02:19:12 -07:00
2d541b2e39 Sculpt: fix shift-smooth strength option
Finally fixed the last of what turned out
to be many bugs that were causing it
to be ignored.
2021-10-04 02:09:08 -07:00
4e2a5c5562 Remove debugging ATTR_NO_OPTS 2021-10-04 00:10:18 -07:00
e32bcbd7e8 Sculpt: face set extrude now kinda works
Face set extrude now mostly works, thought it's
still buggy and unstable in DynTopo mode.

Interfacing PBVH_BMESH with the BMOp API
turned out to have a few nasty gotchas; the last commit
fixed a lot of things but some rethinking of the basic
design still needs to happen.
2021-10-04 00:03:33 -07:00
1969510974 Sculpt: Minor bmesh refactor, made tool flags a CD layer
The way toolflags reallocated the entire mesh
just to add or remove one pointer from BMEdge/Vert/Face
was highly broken.  Now a CD layer is used instead.
2021-10-03 20:46:44 -07:00
99b9c1bcad Commit working code. 2021-10-03 14:25:38 -07:00
7327e15630 Merge branch 'master' into sculpt-dev 2021-10-02 03:22:22 -07:00
e01dd8140e Sculpt: experimental brush palette ui
Pure python, experimental.
2021-10-02 00:15:48 -07:00
f52ed67289 Sculpt: fixed dyntopo speed issue
* PBVH_UpdateTopology is now properly cleared in all cases.
* BKE_curvemapping_cache.h no longer provides a global curve
  cache, the brush system now allocates its own on startup.
2021-09-30 21:30:15 -07:00
4da8f71c14 Fix compile error 2021-09-30 19:52:37 -07:00
23da07bf45 Sculpt: Fixed tooltips for brush channes in RNA
* BrushChannel now uses its refine callback to
  generate new structs for individual BrushChannelType's.
  - It generates a .value member that's a copy of
    one of the exisitng float_ bool_ enum_ etc_value members.
  - Haven't figured out how to delete the XXX_value members
    yet though.
2021-09-30 19:11:14 -07:00
d02b557f10 Sculpt: Drag dot fixes
* Drag dot now uses anchored's method
  of calculating brush rake angle, this
  is much more usable and less numerically
  unstable.

* There is now an option ("Smooth Raking" to
  smooth rake angles during brushes. This was
  a failed effort at fixing drag dot raking
  that turned out to be useful for other things.

* Drag dot no longer requests subspacing events
  from the brush system.  This made a huge
  difference in performance and is now on par
  with anchored mode.
2021-09-30 13:07:22 -07:00
189d32823a Sculpt: fix compile error
GCC is giving errors on assigning
integers to char arrays.  Naturally
this makes sense, and of course MSVC
doesn't even have a warning to detect
this (I tried enabling them all).

I may go back to clang.
2021-09-30 11:32:19 -07:00
02f1982e7d Remove ATTR_NO_OPTs 2021-09-30 11:03:19 -07:00
be004d8e7e Sculpt: Fix fairing brush for dyntopo 2021-09-30 11:02:17 -07:00
5e3d84eed7 Sculpt: fix multires crashing 2021-09-30 10:54:11 -07:00
966c4eedc4 fix compiler error 2021-09-30 02:01:36 -07:00
9c30155c47 Sculpt: fix drag-dot/anchored for face sets
* Added an API for original face sets
  based on the SculptCustomLayer API.
2021-09-30 01:36:08 -07:00
f6da6961e0 Sculpt: fix anchored/drag dot brush modes 2021-09-30 00:37:12 -07:00
afcb34428c Sculpt: fix broken view3d.view_selected in
sculpt mode
2021-09-29 22:16:47 -07:00
07049bd217 Sculpt: more clay fixes
For some reason I have to
take the sqrt of pressure
to match previous behavior,
but for the life of me I can't
find the offending double
multiplication.
2021-09-29 22:03:39 -07:00
150ad9cf9c Sculpt: More clay brush fixes 2021-09-29 21:06:39 -07:00
daaf6d6660 Run autopep8 on a few files 2021-09-29 19:44:40 -07:00
abbba7371f Sculpt: UI organization
* Brush channels can now be grouped in
  categories.
* TODO: let user edit and create the
  categories.
2021-09-29 19:31:44 -07:00
82bc438cfb Sculpt: fixed right click menus
They are now configurable in the
brush tab.
2021-09-29 17:06:47 -07:00
32d390c678 fix a char* that should have been a const char *. 2021-09-29 03:33:55 -07:00
7a3756f8b0 fix linux compile error 2021-09-29 03:32:16 -07:00
7b19b47cbf One more clay fix 2021-09-29 03:29:21 -07:00
159211da8f Sculpt: fix various issues with clay brushes
* No longer applies size pressure mapping twice
* Fixed hardness setting not being applied.
2021-09-29 02:54:32 -07:00
a38654594d Sculpt: fix change in accumulate setting now
applying until next stroke.
2021-09-29 01:19:50 -07:00
1820ca6e34 Sculpt: fix mesh filter brush 2021-09-29 01:14:36 -07:00
c0b0e4cd16 Sculpt: Clay brush speedups
* Made clay brush use a spacing
  of 7 for autosmooth instead of
  the brush default (which is 3).

* Also fixed a few small UI issues
2021-09-29 00:15:21 -07:00
06e8cc0256 Sculpt-dev: Improve autosmooth performance
* PBVH_FACES now uses MDynTopoVert (have got
  to rename it) to store boundary/corner/visibility
  flags the same way PBVH_BMESH does.
* Fixed brush add/sub buttons in header not
  working
* Fixed inverted brushes feeding negative strength
  to sub commands (like autosmooth, which flips it
  to sharpen mode).
2021-09-28 23:14:27 -07:00
486627215c Fix macro problem with clang/gcc 2021-09-28 11:35:51 -07:00
8a99bc0829 Sculpt: couple of small ui tweaks 2021-09-28 10:26:20 -07:00
d1b0677817 Sculpt: UI updates and fixed hard edge mode setting
* Brush editor tab now defaults to old
  interface, with two options:
  - Advanced, which shows more options
  - Edit Mode, which shows workspace visibility buttons
* Hard edge mode (which forcibly sets face set slide to 0
  and enables preserve faceset boundarys) now works again.
* Smooth_strength_factor is now a brush channel with
  inheritance set to true.
* Alt-smooth now restores the hard edge mode and
  smooth_strength_factor from the active brush context.
  While both are default inherited from toolsetting defaults,
  it can be quite confusing if either have inheritance unset
  by the user in the Smooth brush.
2021-09-28 09:46:32 -07:00
b825fb4625 Fix last commit (why did that ever work?) 2021-09-27 02:55:25 -07:00
0d8fb1464e Sculpt Dyntopo: Fixed memory leak
* Fixed a particularly nasty memory leak
  where the entire process of entering sculpt
  mode was being done twice.

* Discovered that range tree is extremely slow.
  Got the alternative freelist version up and running,
  and replace a usage of GSet with a bitmap. However
  the new code is disabled pending further testing.
  Literally an order of magnutude improvement.
2021-09-27 02:39:05 -07:00
ef88b4f98b Sculpt: fix crash in multires layer brush 2021-09-26 15:50:24 -07:00
71c9af7eb5 Sculpt: Cleaned up custom customdata api
* Cleaned up the SculptCustomLayer API that is
  used for custom data.
* Various SculptCustomLayer references are now maintained
  in ss->custom_layers and are updated automatically when
  the CD layout changes.
* PBVH_GRIDS now forcibly allocate custom layers in simple_array
  mode (i.e. they don't allocated real customdata layers at all).
* SculptCustomLayers can optionally be preserved in the final mesh.
* Fixed handling of CD_TEMPORARY which was broken.
* The layer brush can now split the pbvh during use.
* Persistent base data is now saved as permanent CD layers (except
  for PBVH_GRIDS).  This is necessary for undo, if we want we can
  add special code to remove them on exising sculpt mode.
* The layer brush now uses the SculptCustomLayer API instead of
  having seperate bmesh and faces/grids implementations.

* In unrelated changes, fixed curve presets for clay brushes.
  - Still need to implement stabilized input mappings, which the
    clay thumb brush needs.
2021-09-26 15:11:42 -07:00
45f45d9e07 Sculpt: more brush engine stuff
* Setting sculpt paint color from
  the palette works again.
* Fixed various defaults.
2021-09-26 02:47:01 -07:00
fa8195ba55 Fix color palette panel 2021-09-25 14:20:57 -07:00
c1b8049adf Fix a few defaults for sculpt paint brush 2021-09-25 01:49:52 -07:00
49e64d4e26 Fix dyntopo undo crash 2021-09-25 01:36:23 -07:00
63044a696b Sculpt: fix paint brush defaults 2021-09-25 00:38:54 -07:00
028a0770f1 fix small but significant typo 2021-09-25 00:21:24 -07:00
6b382e7fb0 Fix a few regressions in other paint modes. 2021-09-25 00:19:31 -07:00
1b6beea2df Sculpt: More brush channels stuff 2021-09-24 21:04:49 -07:00
430e7efd09 Sculpt: Unnest channels.channels in RNA
* BrushChannelSet is now a pure collection. This
  eliminated the annoying brush.channels.channels
  paths, and also makes library overriding work.
* Now sure I've done this correctly. Struct extended
  collection properties are a bit confusing.
2021-09-24 18:47:24 -07:00
23f952d7d7 fix python script error 2021-09-24 02:44:13 -07:00
b7c1d58f9c remove debug line from last commit 2021-09-24 02:41:39 -07:00
eb9a5e8f8b Sculpt: more brush stuff
* Move more dyntopo settings to brush channels
* Implemented the unprojected radius hack in
  the new brush system.  I'm not really happy
  with it, but doing it properly is going to
  take some thought.
2021-09-24 02:38:20 -07:00
33cd635a82 Sculpt: fix clay brush
* BRUSH_MAPPING_INHERIT is now respected
  when unset.
* Also added inherit icon to
  input mapping curves ui.
2021-09-23 21:39:20 -07:00
aca54ad3e6 Fix bug in last commit 2021-09-23 16:15:50 -07:00
ccb3ca41cd Sculpt: Add a curve brush channel type
* Added a new curve brush channel type
* Added a BKE_brush_curve_strength_ex method
  that just takes preset and curve as arguments,
  instead of pulling them from Brush.
* Autosmooth and topology rake now have their
  own falloff curves.
2021-09-23 16:11:29 -07:00
99c0ee0558 Sculpt: cloth brush constraint solver is now multi-threaded
* Used a simple method of assigning constraints
  to threads.
* Constraints that couldn't be assigned are solved
  in the main thread.
2021-09-23 03:40:11 -07:00
c6a51c1259 Sculpt: move more brush settings to brush channels 2021-09-23 02:13:00 -07:00
7e220dc3f0 Fix brush defaults bug 2021-09-22 19:05:27 -07:00
36d5b6e959 Sculpt: Brush settings panel editor
* Moved brush settings (in sculpt mode) to
  (for now) a new properties editor tab.
* Brush settings can now be individually configured
  to show up in the workspace buttons.
* Brush settings can also be reordered.
* The new brush tab has a "preview" subpanel
  to preview the workspace settings layout.
  This is where settings are reordered.
2021-09-22 18:26:25 -07:00
0e2ec88e6b Tweak more brush channel settings 2021-09-22 03:31:34 -07:00
2a5b98c78f Fix backwards preset in last commit 2021-09-22 02:50:30 -07:00
668a819bec Sculpt: Added compile-type name checking for
brush channels.
2021-09-22 02:47:42 -07:00
a67ff7552c Tune up a few hot loops revealed by profiling. 2021-09-21 02:39:33 -07:00
c8004ff653 Just what version of C++ does the linux buildbot use, anyway. 2021-09-21 00:44:36 -07:00
fdffbf87ac Fix last commit 2021-09-21 00:13:43 -07:00
bee00909bc forgot a line 2021-09-21 00:04:10 -07:00
562723b080 More linux fixes 2021-09-21 00:03:32 -07:00
2195a5eb2c Try to fix another linux compile error 2021-09-20 23:28:03 -07:00
4307c0eb68 try to fix weird gcc compile error 2021-09-20 22:56:02 -07:00
2d473ff4a8 Sculpt: more brush stuff 2021-09-20 22:52:17 -07:00
2b2b569229 Disable a static assert on gcc. 2021-09-20 20:26:53 -07:00
015dae3dc5 add missing files 2021-09-20 19:42:31 -07:00
3fc687ad75 Sculpt: Finish color support for brush channels
* BRUSH_CHANNEL_VEC3/4 are now implemented
* New flag BRUSH_CHANNEL_COLOR
* Also tried to sculpt's usage of wm.radial_control in keymaps
2021-09-20 19:41:00 -07:00
76beed9068 Sculpt: More brush engine stuff, got automasking to work with it
* Sculpt now has an API to get brush channel settings.
  If a sculpt cache exists it will use the channels there
  (ss->cache->channels_final), otherwise it pulls them
  from a brush and Sculpt toolsettings. Exampes:

  float f = SCULPT_get_float(ss, "setting", sd, brush);
  itn i = SCULPT_get_int(ss, "setting", sd, brush);

* Improved the UI a bit
2021-09-20 14:10:35 -07:00
645aee0835 Fix small crash in last commit and a few typos 2021-09-20 03:34:13 -07:00
83bfa950b1 Sculpt: add missing files 2021-09-20 03:14:39 -07:00
898625547f Fix problem in last commit 2021-09-20 03:14:14 -07:00
73150981c8 Sculpt: More brush channel stuff
* The input device curves for brush channels
  now use a copy on write mechanism.
  + It's based on a global cache of curves.
    The alternative is to reference count
    BrushChannels, which I also implemented
    then abandoned.
  + Profiling showed that copying CurveMapping
    instances was actually a problem.

* Lots of small fixes to the old<-> new brush setting
  conversion code;

* Brush commands can now, sortof, have individual
  spacing.  The default brush spacing still acts
  as a minimum though.

* Added a BLI_ghash_lookup_p_ex method that
  returns the key ptr inside the ghash (it
  returns the actual key, not a pointer to
  Entry.key).
* Added a general 1d CurveMapping preset operator
  that uses an RNA path to get the curve.
2021-09-20 03:01:30 -07:00
c9f1e104da Sculpt: Brush channels names are now lower-case instead
of upper case.
2021-09-19 16:17:01 -07:00
336b263b56 Sculpt: brush engine stuff
BrushChannels are now stored in linked lists
instead of simple arrays.  This helps to
avoid memory corruption.

I had originally wanted to be able to pass
BrushChannels by value, but that doesn't really
work since they heap allocd data (the input
mapping curves).
2021-09-19 15:35:10 -07:00
b3ed969b86 commit prior to small refactor 2021-09-19 13:30:43 -07:00
87feba04dd Sculpt: more brush engine stuff; do not test.
Pushing this commit early due to computer
weirdness
2021-09-19 01:11:35 -07:00
1ca57bc5f4 Sculpt: flushed out brush channel RNA and made basic UI 2021-09-18 12:10:14 -07:00
7749b89d74 Sculpt: Added some icons and commit icon_geom.blend into the branch
Help with icons would be appreciated.  The
scene project icon kind of came out looking
like a band-aid.
2021-09-18 02:43:03 -07:00
bae92a0ce5 Disable threading for sculpt project brush 2021-09-18 01:39:40 -07:00
5223f73a1d Fix compile error 2021-09-18 01:26:12 -07:00
fe47584316 Sculpt: Flesh out RNA wrapping of BrushChannels 2021-09-18 00:35:56 -07:00
2d3d6eb7b2 Merge remote-tracking branch 'origin/temp_bmesh_multires' into sculpt-dev 2021-09-17 23:32:08 -07:00
f0c35d16f3 Merge branch 'master' into temp_bmesh_multires 2021-09-17 20:13:39 -07:00
85d274a60c Merge branch 'master' into sculpt-dev 2021-09-17 16:22:10 -07:00
d3bba94bf2 Commit current working copy; having
weird file system issues
2021-09-17 14:43:00 -07:00
04c3690299 Sculpt dyntopo: Roughed out skeleton of new brush engine API
Command Lists

* The new system will be based on command lists
  generated by (eventually) a node editor.
* For now, the lists will be hardcoded.
* Idea is to make a minimal viable
  brush engine that won't cause file breakage
  when the upgrade to node-based brushes happen.

Brush Channels

* Wrote new structures and API to wrange
  brush parameters: BrushChannel.
* Supports, floats, ints, enums, bitmasks,
  with plans for vec3 and vec4.
* This will replace UnifiedPaintStruct,
  most of the members of Brush and the
  DynTopoSettings struct.
* Brush channels can
  be mapped to various input device
  channels (e.g. pen pressure); each
  mapping has its own associated curve
  (CurveMapping instance) and bounds.

Brush channel inheritence chaining

* Brush channels can form inheritence chains
* Channel sets are stored in three places:
  in the scene toolsettings, in Brush, and in
  individual brush commands.
* Node groups will also have a channel set.
* Channels in each set can be flagged to
  inherit from the parent set.
* Inheritence happens in seperate merged
  channel sets at runtime.  The final
  Brush channels live in Brush->channels_final,
  while the final command channels live in
  BrushCommand->params_final.
2021-09-16 20:29:33 -07:00
627edd1efa Merge branch 'master' into temp_bmesh_multires 2021-09-16 13:44:21 -07:00
445889676b commit prior to merge 2021-09-16 10:42:30 -07:00
350e783668 Add skeletal beginnings of C++ sculpt refactor
design study (note that it's #ifdef'd out).

Basic idea is to put all the sculpt brush code
in a single large template.  This template
takes a PBVH adaptor class (of which there
would be three, one per PBVH_XXX type)
as a template argument.

Basically we're using the compiler to generate
three complete copies of every brush implementation.
C++20 concepts are used to validate the pbvh classes.

An example brush implementation:

    pbvh->forVertsInRange(
        {
          .brush = ss->cache->brush,
          .radius = radius,
          .use_threads = true,
          .use_original = false
        },

        [&offset](auto viter, int node_i, void *userdata) {
          //add offset to vertex coordinates

          madd_v3_v3fl(viter.co, offset, viter.fade);
        },

        [](PBVHNode *node, int node_i, void *userdata) {
          BKE_pbvh_node_mark_update(node);
        });
2021-09-15 14:56:19 -07:00
0b2aee5841 Sculpt dyntopo: Fix a few issues from last commit
* Fixed crashing on entering sculpt mode
* Fixed transitioning between sculpt and
  another undo system type sometimes
  corrupting stack.
2021-09-15 02:50:32 -07:00
ecdd6a302e Sculpt dyntopo: fix missing bit from last commit
* BM_mesh_bm_to_me now saves id layers by default.
2021-09-15 02:16:03 -07:00
173f5f94ff Sculpt dyntopo:
Seperate enabling PBVH_BMESH from enabling DynTopo:

* Created a new option to globally disabled
  DynTopo.
* The DynTopo panel header now reads "Dynamic Mode",
  to hopefully signal that turning on PBVH_BMESH is
  a seperate step from enabling or disabling DynTopo
  itself.
* The first checkbox in the panel is "DynTopo" so it
  should be clear enough (it's on by default, with multiple
  layers of file versioning checks).

PBVH_BMesh's undo system:

* CD_MESH_ID layers are now permanently saved once
  they are created (by default they are not).  This
  fixed a *lot* of bugs:

  Before this the undo system had to save maps between
  mesh indices and mesh IDs on transitioning
  between sculpt and global undo steps.  This was
  extremely error prone, and it simply wasn't possible
  to cover all of the corner cases

* Note that there is still an odd bug where the first
  global undo push after a sculpt step gets ignored,
  I dunno what's up with this.

* Dyntopo undo should be nearly (hopefully completely)
  bug-free after this commit.

C++20

* Made a few small changes to get blender to compile
  with c++20.  std::result_of was removed, had to
  replace a couple of usages of it with std::invoke_result.

* I'm planning to do some design studies on rewriting
  sculpt into C++.

* I strongly suspect we are going to need C++20'a new
  concepts feature if we move sculpt into C++.
  I'm planning to do some design studies on how
  that might work.
2021-09-15 01:41:03 -07:00
0eeaeb3fc2 Sculpt dyntopo: fix nasty node splitting bug 2021-09-13 20:14:46 -07:00
3df335d330 Sculpt dyntopo:
* Fixed noise on using autosmooth with tools that use original
  coorinates.  While this was most prominent with DynTopo,
  it did happen with other tools.
* The solution is to smooth the original coordinates as well
  as the explicit coordinates if the active tool requires
  original data.

* I decided to replace the original coordinates system for
  PBVH_FACES and PBVH_GRIDS with the same MDynTopoVert structure
  DynTopo uses.  The alternative would have been extremely messy
  code.

* Todo: Rename MDynTopoVert to. . .SculptInfoVert?
* Todo: Cache boundary flag and corner info in MDynTopoVert->flag
        for PBVH_FACES/GRIDS similar to PBVH_BMESH.
2021-09-13 19:24:21 -07:00
0676928408 Sculpt dyntopo: more collapse fixes
The edge cases just never end.
2021-09-13 02:31:33 -07:00
e68667a835 Sculpt dyntopo:
* A few more collapse fixes
* Fixed boudary handling in surface_smooth_v_safe
2021-09-12 23:39:49 -07:00
86972d294f Dyntopo sculpt: fix bug with boundary flags 2021-09-11 13:38:42 -07:00
f52a03dd71 Sculpt dyntopo: Added a 'hard edge mode' option
to forcibly set autosmooth_fset_slide to zero
(i.e. treat face set boundaries as hard edges
and not project them on the surface).
2021-09-09 10:06:24 -08:00
a52f89a446 Add more int casts for linux 2021-09-08 23:21:57 -08:00
9f3bafc4ab Sculpt dyntopo
* Non-manifold "fins" are now detected and automatically
  deleted.
* Fixed compile error on linux.
2021-09-08 23:18:07 -08:00
bb1096f475 Sculpt dyntopo:
* Collapse now uses code from decimate to detect
  degenerate cases.
* Remaining, unknown (and rare) degenerate cases
  are now detected (via presence of duplicate verts
  in faces) and fixed.
* DynTopo fills in undo size properly now,
  so undo memory limiting works.
2021-09-08 15:51:56 -08:00
3e6edf5278 Sculpt dyntopo:
* BLI_table_gset now internally uses a SmallHash instead of
  a GHash.  Profiling revealed this to be quite a bit
  faster.
* This is something of a stopgap until C++-afication of
  pbvh, when we'll have our pick of a bunch of
  really nice C++ hashmap libs.
* pbvh_collapse_edge bites the dust; dyntopo now uses
  BM_collapse_edge.  Of the three topology operations
  (subdivide edge, collapse edge, dissolve 3/4 valence
  vertex) only dissolve still has a dyntopo-specific
  implementation.
* Fixed a bunch of annoying memory corruption bugs.
* Non-manifold triangles are now detected in more
  places.

SmallHash changes:

* Enabled removal
* Fixed infinite loop bug caused by
  improperly counting free*d* cells
  versus free cells.
* Added a BLI_smallhash_ensure_p method
  that works just like the GHash version.

Sculpt replay system

* Roughed out a simple system to record and
  play back sculpt strokes using a simple
  text format.
* This is exclusively for
  performance profiling and unit tests.
* For each brush stroke the system saves a copy
  of the active StrokeCache and PaintStroke
  and parts of Sculpt.

This should make profiling DRAM thrashing a lot
easier.
2021-09-07 23:49:54 -08:00
37bce7b701 commit working code 2021-09-02 23:19:11 -07:00
6febbd7a55 Linux's gcc's flags are evil 2021-09-02 11:42:06 -07:00
416c7a32d8 Fix warnings 2021-09-02 11:32:46 -07:00
d4badd4b22 Fix compile error 2021-09-02 11:26:28 -07:00
4e43e09cca Sculpt dyntopo: increment subversion. 2021-09-02 11:08:28 -07:00
21b6d78cd2 Sculpt dyntopo: change brush defaults and fix bug
* Changed brush defaults a bit.  New defaults
  are for organic modeling.
* autosmooth_fset_slide now defaults to 1, so
  face set boundaries are smoothed but stick to mesh
  surface (if 0 they would function as hard edges).
* Weight by area smooth mode is on by default for all
  brushes.
* Cleaned up versioning code and made it
  kick in at 3.00:21, with some simple checks to
  try and detect existing data from beta testers.

* Also fixed a small crash bug.
2021-09-02 11:02:44 -07:00
2b20931707 Merge branch 'master' into temp_bmesh_multires 2021-09-02 00:02:20 -07:00
416686707f commit before merge 2021-09-01 21:32:43 -07:00
8bfbbc467a Sculpt dyntopo
* Wrote a simple fix for drawing face sets
  in inverse (ctrl) mode with face set automasking
  on.

* Various fixes related to hard edges and smoothing.

* Started writing some code to defragment bmesh mempools.
  Need to figure out how to avoid triggering excessive
  PBVH node rebuilds.
2021-09-01 11:47:03 -07:00
baa24243a5 Sculpt dyntopo: Dynamic field-propegated topology rake
I might write a paper on this.  Topology rake now locally
updates a vector field, which it uses to smooth the input
and constrain to mesh (including face set) boundaries.
This can make an enormous difference for things like
smoothing.

Note that this is different from the existing 'curvature rake'
mode, which also builds a field and which is fed into the input
of this new one.

The only oddity is that the field is stored in a CD_PROP_COLOR
since we don't have a CD_PROP_FLOAT4, and this shows up in the UI
(not sure if I'm messing up the CD_TEMPORARY flags or if the UI
doesn't check for them).
2021-08-30 15:04:43 -07:00
73529fb1bb Sculpy dyntopo: fixed various topology bugs
* Fixed crash in dyntopo collapse.  The
  loops around vertex iterator dyntopo uses
  doesn't actually work on non-manifold meshes,
  or meshes with invalid normals, this was not
  being checked in pbvh_bmesh_collapse_edge.
* Rotate tool now works with dyntopo.
2021-08-29 16:05:26 -07:00
381ef09073 Sculpt dyntopo: another bugfix from today's earlier commits 2021-08-28 15:33:41 -07:00
56ba339cd8 Fix a few bugs in lat commit 2021-08-28 14:59:54 -07:00
055fa3fa5e fix missing curly braces 2021-08-28 13:59:00 -07:00
7d5c3beb06 Sculpt dyntopo: Fix bug with edge collapse 2021-08-28 13:30:46 -07:00
39b0e9df81 Sculpt dyntopo: Add edge API
* Added a minimal edge API to query edge
  boundary states.
* This is necassary because the previous approximation,
  testing if two adjacent verts are boundaries, breaks
  for triangles.
2021-08-28 12:14:59 -07:00
6648f82ef7 Merge branch 'master' into sculpt-dev 2021-08-28 19:58:13 +02:00
320ff87948 Sculpt: Quantize mask filter 2021-08-28 19:57:32 +02:00
b21595cdf9 Tweak last commit to not affect base face set. 2021-08-27 21:54:15 -07:00
e4b36fb6bc Sculpt dyntopo: split face set boundaries on mirror boundary
Added an option to split face set boundaries on mirror
boundaries; currently only DynTopo supports this.

Very useful for making hard edges along mirror lines.
2021-08-27 20:02:20 -07:00
966e4ba9ae Sculpt dyntopo: improved boundary smoothing
It's kind of hackish mathematically.
2021-08-27 18:13:18 -07:00
268682527f Sculpt dyntopo: added a smoothing factor for sharp boundaries
Works by projecting non-boundary verts onto boundary vert
normals and weighting by boundary_smooth_Factor.
2021-08-27 14:18:50 -07:00
cdb52aee9f Missed an ATTR_NONNULL fix 2021-08-27 01:59:34 -07:00
8b02ab86f1 change 'error' to 'warning' in a printf 2021-08-27 01:57:13 -07:00
019700583b Sculpt dyntopo: undo bugfixes
* Fixed nasty undo bug related
  to now rewinding BMLogEntry subchains properly.
* Fixed bug in dyntopo collapse
2021-08-27 01:51:56 -07:00
31d2b04411 Sculpt dyntopo: Who knew gcc's nonnull attribute is 1-based 2021-08-26 20:23:17 -07:00
3508c699fb Sculpt dyntopo: Cleanup past few commits
* Removed some ATTR_NO_OPTs
* Made pbvh_split_edges disallowed
  4-valence splits if cleanup topology
  mode is on
2021-08-26 20:10:54 -07:00
8ea7c93a37 Sculpt dyntopo: support sharp edge flags
* Sharp edge flags are now supported and are
  treated much the same as face set boundaries:
 + Dyntopo preserves them
 + Interior smoothing treats them as boundaries
 + Corners are detected and pinned in smoothing
 + TODO: add a brush flag to ignore sharp boundaries
   for smoothing.
* Seams are also preserved, but don't affect smoothing.
* BMLog now saves edges.
* The new edge split function is enabled.
* Dyntopo now pushes new combined BMLog entries in
  its top-level function, to avoid scary id reuse
  edge cases.
* SCULPT_vertex_is_boundary/corner now take a bitmask
  of which types of boundaries you wish to query instead
  of check_face_sets.
2021-08-26 18:00:29 -07:00
8b8733e76c Sculpt Vertex Colors: New sample color operator 2021-08-27 01:07:07 +02:00
4dda04eaa9 Merge branch 'master' into sculpt-dev 2021-08-26 23:19:21 +02:00
30691560fa Fix C++20 designated initializer build issue. 2021-08-26 20:56:13 +05:30
12f87d02c6 commit prior to small cleanup 2021-08-25 03:01:56 -07:00
9680edf77c * Implemented bounday/face set corner handling.
* New function SCULPT_vertex_is_corner, similar to
  SCULPT_vertex_is_boundary it takes argument to
  check face sets.  PBVH_FACES/GRIDS version is
  incomplete.  It returns a bitmask of whether
  the vert is a boundary corner and/or a face
  set one.
* PBVH_BMESH uses a somewhat more expensive
  calculation to detect corners of face set islands by
  edge angle. This is currently not done for boundary
  corners.

Corner pinning now happens in:

* The internal smoother dyntopo uses for stability reasons.
* SCULPT_vertex_neighbor_average_interior.
* Topology rake.
* Dyntopo collapse.

Note that DynTopo always pins face set corners
but everything else only does so if preserve face
sets is on.
2021-08-24 13:18:36 -07:00
a00bfa8976 Sculpt dyntopo: more smooth stuff
* All of the smooth brushes now use the SculptCustomLayer
  system for temporary data, so all work with dyntopo now.
* You can now use a flat array in SculptCustomLayer with
  PBVH_BMESH (though you have to build the structure manually).
  The mesh filter code uses this.
* Smooth (and autosmooth) now have an option to preserve face
  set boundaries.  Corners are currently not handled.
* Simplify brush has preserve face set boundaries autosmooth
  flag set by default.
* SCULPT_vertex_is_boundary now takes an addition argument
  for whether to check for face set boundaries.
2021-08-23 22:55:17 -07:00
bde54e127e Sculpt dyntopo: Smooth improvements and bug fixes
* Added an option to weight smooth by face areas
* Dyntopo now caches face areas in a CD_PROP_FLOAT layer
* Dyntopo also caches number of edges around verts inside of
  MDynTopoVert.  To avoid increasing the struct size flag was
  made a short.
* Cleanup mode (dissolves 3/4-valence verts) now piggybacks on
  subdivide code to build list of verts; this is much faster.
2021-08-23 21:06:10 -07:00
9b8c82e2ed Sculpt dyntopo: fix mem cache test function, how did this
ever work
2021-08-23 02:28:19 -07:00
5866a4680d Dyntopo Sculpt: Wrote new edge split code, currently disabled 2021-08-23 01:19:55 -07:00
d795144f54 I hate default int 2021-08-22 13:31:05 -07:00
de6258c618 Silly me, don't use alloca in this situation 2021-08-22 13:16:10 -07:00
d912ceeb40 Fix compile error on linux 2021-08-22 13:15:08 -07:00
9a197a1185 Sculpt dyntopo: Temp fix for nasty GCC compiler bug on linux.
I'll file a bug report with GCC tomorrow.
2021-08-22 12:51:53 -07:00
36785c83f0 Sculpt dyntopo: Fix a dumb mistake I made in space_toolsystem_toolbar.py 2021-08-22 10:29:36 -07:00
8eb7a04150 fix signed/unsigned warning 2021-08-21 20:46:08 -07:00
97c3e5944c Sculpt dyntopo: Finished bmesh cache coherency tester
To run, in the python console enter:

bpy.msgbus.pbvh_bmesh_do_cache_test()

The output will be in the regular console.  The test
build a half-million vert cube and smooths it. It runs
several passes (all of which perform the same smoothing
operation):

1; Randomized order pass
2. Ordered pass (by vertex clustering)
3. Same as 2 but with a purely data-oriented version
   of the bmesh structs.
4. Same as 2, but using a version of the bmesh structs
   with all pointers replaced by integer indices.

At least on my laptop #3 and #2 are about a third faster
then #1, and #2 tends to be around 15%.
2021-08-21 20:40:22 -07:00
6f523ffacb Sculpt dyntopo: Don't allow dyntopo when multires modfier exists
Note that thoeretically we could support multires in dyntopo,
but numerical instability would probably make the grid data
explode.
2021-08-21 15:19:15 -07:00
04562794df Sculpt dyntopo: disable mem cache test executable
from compiling altogether
2021-08-20 20:52:22 -07:00
93004c7b19 Dyntopo: Disable mem cache test executable for now as it
doens't link on mac
2021-08-20 20:49:29 -07:00
0d542db1e1 Sculpt dyntopo:
* Fixed multires apply base feeding bad original coordinates to
  deform modifiers.
* Roughed out some code for cache performance testing.
* Wrote skeleton code for a PBVH texel API; hasn't been tested
  yet and may be removed.
2021-08-20 20:36:04 -07:00
de1f2c41fa * BM_mesh_remap can now reorder loops
* Wrote yet another BKE_pbvh_reorder_bmesh function
2021-08-18 21:43:59 -07:00
106f542ac4 Sculpt dyntopo: yet another undo bug
BKE_pbvh_bmesh_add_face had a branch where it ignored
the log_face argument, leading to corruption in
BMLog.
2021-08-17 14:58:37 -07:00
83f94ebb6f Sculpt dyntopo: remove debug ATTR_NO_OPT's 2021-08-17 00:31:04 -07:00
954aa88ba4 Sculpt dyntopo: more undo fixes
Yay for massively overdetermined bugs.
2021-08-17 00:09:43 -07:00
9503751c83 Sculpt dyntopo
* Added a limited "fast draw" mode to pbvh drawing
  that tries to limit data sent to the GPU.
 - Facesets and mask data are disabled.
 - Indexed mode is forced.
 - Does not work (at all) for multires.

* Finally fixed some outstanding bmesh sculpt undo bugs:
 - Forgot to mark pbvh nodes to update their bounds, which
   produced a bug I had thought was caused by something else.
 - Hopefully fixed a long-standing occasional memory corruption
   bug.
2021-08-16 20:50:02 -07:00
dcaba4c5e3 Sculpt dyntopo: fix various faceset init operator bugs
that crept in.
2021-08-16 16:34:35 -07:00
fb463a13cd Sculpt dyntopo: Fix memory corruption caused by improperly disabled
code.
2021-08-16 02:12:30 -07:00
b5100e73c8 Sculpt dyntopo: Fix boundary brush for multires
This commit fixes boundary brush for multires which
broke two commits ago.

This required implementing the geodesic api for PBVH_GRIDS,
which I did by building topology maps in a rather. . .
haphazard fashion.

Basically I built a vert->edge map and then used it to
derive a pseudo edge to quads mapping (it maps edges
to all the verts in the two surrounding quads except
the edge's own verts).

Just for fun I enabled geodesic mode in mask expand;
it seems to work.
2021-08-16 01:44:31 -07:00
ab632243e6 Merge branch 'master' into temp_bmesh_multires 2021-08-15 18:14:41 -07:00
8510c77b9c Sculpt dyntopo: improve boundary brush for irregular topology
(still a wip)

The boundary brush now builds a geodesic distance
  field (from the boundary) from which it derives a tangent
  field:

* These now define the rotation plane for bend mode.
* Rotation origins snap to these planes.

There is also typedef'd code for visualization tangents
in a temporary object (note the sculpt object), to enable
define VISBM in sculpt_boundary.c.  This will be removed
lated.

Additional changes:

* Added a function to get the number of edges around verts,
  SCULPT_vertex_valence_get.
* Added an API to calculate cotangent weights for vert fans,
  SCULPT_cotangents_begin (call in main thread first) and
  SCULPT_get_cotangents.
* Sculpt neighbors for PBVH_FACES now uses ss->vemap if it exists.
* The Mesh topology mapping code now takes extra parameters for
  sorting vert/edge cycles geometrically.
* Similarly, there is now a function to sort BMesh edge cycles,
  BM_sort_disk_cycle.
* MDynTopoVert->flag now has a bitflag for when the disk cycle
  sorting needs to be redone, DYNVERT_NEED_DISK_SORT.
* The sculpt geodesic code now supports passing in custom vertex
  coordinates.
* The geodesic API can also build an optional map of which vertex
  in the initial vertex list is closest to any other vertex.
2021-08-15 18:02:16 -07:00
40aa321dc7 Sculpt dyntopo: fix bug with dyntopo geometry undo push 2021-08-11 22:51:09 -07:00
a80c381ec5 Sculpt Dyntopo: PBVH draw fixes
* The PBVH draw subsystem is now told whether any viewports
  have drawtype >= OB_MATERIAL before anything in any windows
  are drawn.  There are no alternatives given the design
  constraints of sculpting, where simply uploading data to the GPU
  quickly becomes a bottleneck.

* Fixed flat vcol shading mode.
2021-08-07 22:28:00 -07:00
1e9a7383ef * Don't rely on BMLog's stored normals, instead flag pbvh nodes to
update normals on undo/redo.
2021-08-07 19:25:36 -07:00
dc738de90c Sculpt dyntopo: Fix bug in cleanup_valence_3_4 2021-08-06 18:39:54 -07:00
8ed4c5fc61 Merge branch 'master' into temp_bmesh_multires
Also fixed a pbvh corruption bug
2021-08-06 12:51:18 -07:00
76bd253be7 Dyntopo sculpt: add dev operator to debug memory cache coherency 2021-08-06 09:58:46 -07:00
c78b0874ad Asset Browser: Support dropping materials into Face Sets 2021-08-06 17:33:18 +02:00
b991a92aab Merge branch 'master' into sculpt-dev 2021-08-05 20:10:57 +02:00
01965a275c Cursor: Change face set color and wireframe preview 2021-08-04 22:17:48 +02:00
b2dc8bd92e Merge branch 'master' into sculpt-dev 2021-08-02 21:35:31 +02:00
d3faf09082 Merge branch 'master' into sculpt-dev 2021-07-31 17:16:02 +02:00
b243eb2646 Merge branch 'master' into temp_bmesh_multires
Also:

* added BMLog function to save mesh IDs.
  - Used by SCULPT_UNDO_DYNTOPO_BEGIN/END instead of
    saving the whole mesh, which was the previous behavior.
* SCULPT_UNDO_DYNTOPO_BEGIN no longer pushes a non-dyntopo
  geomtry undo node, as this is no longer necassary.

This greatly speeds up going into/out of sculpt mode
with dyntopo enabled, as before it was copying
the mesh twice.
2021-07-29 16:08:14 -07:00
fcfb47b983 Merge branch 'master' into sculpt-dev 2021-07-27 22:00:24 +02:00
c1f5ac7cfe Array Brush: Support array editing with the voxel remesher 2021-07-25 20:36:56 +02:00
89897140cf Merge branch 'master' into sculpt-dev 2021-07-25 18:20:27 +02:00
af71984942 sculpt dyntopo: fix memory corruption and associated memory leak 2021-07-25 05:28:54 -07:00
ec4b9dff19 Add 'projection' option for volume-preserving smoothing to
smooth corrective modifier
2021-07-23 15:46:01 -07:00
d71b6d37fe Array Brush: Initial radial and linear array editing 2021-07-23 21:29:07 +02:00
2689124320 Array Brush: Path edit mode working 2021-07-21 23:27:46 +02:00
70a4956020 Sculpt dyntopo: Removed triangle limit for PBVH_BMESH
* PBVH_BMESH now supports faces other then triangles;

* Dyntopo triangulates faces as it finds them.
  - I looked into methods of preserving quads and failed to
    find anything that worked well in practice; it actually
    worked better to use topology rake to align triangles
    into quads and then mark diagonal edges for later dissolving
    then to try to preserve quads explicitly (I've not
    implementated that here, that was research code).
  - To avoid excessive cache-destroying loops over vertex-faces,
    DynTopo flags which verts have non-triangle faces.

* PBVHTriBuf now builds edge buffers so we can avoid drawing
  tesselation phantom edges.

* BMLog also now supports arbitrary faces.  It still does not
  support edges though.

TODO:

* Fix vcol cell shading mode
* Make sure indexed drawing works
2021-07-20 19:46:00 -07:00
93305b97df Merge branch 'master' into sculpt-dev 2021-07-20 18:28:53 +02:00
7cd74015f8 Sculpt dyntopo: Another fix for faceset boundary handling
* Turned out pbvh->cd_faceset_offset wasn't being updated
  correctly.
2021-07-20 05:47:08 -07:00
7bd521a5c4 Sculpt dyntopo: Another small fix 2021-07-20 04:56:29 -07:00
7dda5ac5ba A few small fixes for last commit 2021-07-20 04:29:23 -07:00
1694e2aca4 Sculpt dyntopo: Face set boundaries are now presered with dyntopo
* Face set boundaries are now preserved on dyntopo remeshing.
* MDynTopoVert->flag now has a DYNVERT_FSET_BOUNDARY flag
  in addition to DYNVERT_BOUNDARY.
* Instrumented uiBut with ASAN poison regions to hopefully
  find the super evil memory corruption bug that's been driving
  me insane.  It's frustratingly intermittent.  There are five
  poison regions.
2021-07-20 04:14:59 -07:00
2fddbebf93 Sculpt dyntopo: Dyntopo now handles mesh boundaries in a more
intelligent way.
2021-07-19 15:32:41 -07:00
4247a56cde Add back hackish fix for clang bug. I should probably
file a bug report with the clang people.
2021-07-18 13:15:43 -07:00
e3b58b6451 Sculpt dyntopo: Split off dyntopo stuff from pbvh_bmesh.c into a new file, dyntopo.c. 2021-07-18 13:12:08 -07:00
5b10d08db3 Sculpt dyntopo:
* Prototyped a threaded bmesh->Mesh converter function.  It's about
  20-30% faster.  Currently disabled.
* Tried to fix more of the bugs when stepping between sculpt and
  other undo step types in the global undo stack.
2021-07-17 04:10:28 -07:00
7c4eb4f8db Merge branch 'master' into temp_bmesh_multires 2021-07-17 00:44:01 -07:00
792292e3de Sculpt dyntopo: BLI_mempool now adds redzones when compiled with ASAN 2021-07-16 23:29:28 -07:00
95a969e7d5 Array Brush: Disable debug drawing 2021-07-15 21:20:48 +02:00
f9c0c9e70c Merge branch 'master' into sculpt-dev 2021-07-15 21:16:12 +02:00
aousdfh
3600e9057b Array Brush: expose fill holes and orientation lock 2021-07-15 21:00:14 +02:00
aousdfh
a78ecfe0f3 Array Brush: Fix crash 2021-07-14 02:57:07 +02:00
aousdfh
65127f9875 Array Brush: Refactor array indices access 2021-07-14 02:02:26 +02:00
aousdfh
37c2bbe5ea Array Brush: Fix EEVEE and modifier evaluation 2021-07-13 19:22:00 +02:00
aousdfh
ceac1d91e2 Array Brush: Fix scale, symmetry and rotation jittering 2021-07-13 02:16:05 +02:00
aousdfh
dfab5a0c4a Array Brush: Array rotation orientations working 2021-07-13 00:37:10 +02:00
aousdfh
85379482d5 Array Brush: Orientation detection working 2021-07-12 23:49:22 +02:00
4ca249018d More msvc errors 2021-07-12 16:03:22 -04:00
d529e13d65 Sculpt dyntopo: fix msvc compiler error 2021-07-12 16:01:17 -04:00
4e797dbc69 Definitely going to modify BLI_strict_types.h 2021-07-12 12:30:23 -04:00
aousdfh
41a8e94d4d Array Brush: First attempt to copy orientation (disabled) 2021-07-12 17:48:42 +02:00
1b9a835893 Make gcc happy 2021-07-11 23:57:14 -04:00
320a1b6f35 Fix bug in previous commit; customdata_bmesh_set_default now ignores
CD_MESH_ID layers
2021-07-11 23:28:19 -04:00
f07f56aa37 BM_mesh_create will now add mesh_id customdata layers when asked,
instead of leaving that to the client.

Also semi-fixed uninitialized memory bug in bmesh unit test (dunno
how best to memset a C struct in C++ won't won't run afoul of some
random compiler somewhere).
2021-07-11 22:02:52 -04:00
4e91e72d53 Let's try that again 2021-07-11 21:21:10 -04:00
58b4688c34 Weird linux compile error 2021-07-11 21:20:37 -04:00
2f862bc7b9 Fix more dyntopo sculpt undo memory corruption 2021-07-11 21:14:49 -04:00
4674795591 Sculpt dyntopo: another attempt to fix undo memory corruption bug 2021-07-11 13:14:24 -04:00
be9017c349 Fix implicit int error 2021-07-11 06:01:39 -04:00
4c2f5b80b7 Remove extraneous code 2021-07-11 05:25:41 -04:00
0502b0103f Merge branch 'master' into temp_bmesh_multires 2021-07-11 05:23:24 -04:00
e8b717ee42 Sculpt dyntopo: Exposed UV smooth bruth in experimental features prefs.
* Also got undo working for it
2021-07-11 04:12:20 -04:00
4b4ac93acd Sculpt dyntopo: Wrote a little UV smoother sculpt brush.
It uses a simple LSCM-like solver; hold ctrl to do simple
laplacian relaxation.

There are some interesting artistic possibilities to using
DynTopo with UV maps, the point of this tool is to help
explore them.

Note that I'm not planning to merge this into master with
the rest of this branch.  When the time comes I plan to
move it into a new branch for sculpt research stuff.
2021-07-11 03:43:47 -04:00
aousdfh
f283c527d2 Array Brush: Scale and stroke strength working 2021-07-09 00:01:56 +02:00
aousdfh
820d086719 Array Brush: Count and deform mode brush properties 2021-07-07 03:26:52 +02:00
aousdfh
69668d62ba Array Brush: Working code for path and radial arrays 2021-07-07 02:20:14 +02:00
aousdfh
351744f2b0 Sculpt Array: Debug path drawing 2021-07-06 01:21:14 +02:00
aousdfh
5ac471a021 Sculpt Array: Initial code for path and path points 2021-07-06 01:06:15 +02:00
aousdfh
db932ccde6 Array Brush: Implement symmetry datalayers 2021-07-06 00:44:49 +02:00
57286eed8d Sculpt dyntopo: Add support for multiple materials to dyntopo
pbvh drawing.

* Dyntopo now stores a list of PBVHTriBufs in leaf nodes, one per material
  used by the node.
* Actual drawing buffers live in a new mat_draw_buffers PBVHNode member.
2021-07-02 13:14:00 -07:00
9dbf3b9fab Array Brush: Implement array instances datalayer 2021-07-02 22:10:21 +02:00
4f8c63e77b Array Brush: BMesh duplication working 2021-07-02 19:52:58 +02:00
47c226cbe3 Array Brush: Add template code and support geometry modifications 2021-07-02 16:38:37 +02:00
d0759840a0 Merge branch 'master' into temp_bmesh_multires 2021-06-30 21:38:18 -07:00
5b19b2cb56 remove debug printf 2021-06-30 21:25:46 -07:00
dcb00bf529 * Enable mask/fset extract for dyntopo]
* Fix nasty memory corruption bug
2021-06-30 21:13:24 -07:00
e8e062f866 Merge branch 'master' into sculpt-dev 2021-06-30 18:12:50 +02:00
9838e9e485 Merge branch 'master' into sculpt-dev 2021-06-30 01:31:21 +02:00
d66d5b431e Clean up a few warnings 2021-06-29 05:50:40 -07:00
43a0fe378f Merge branch 'master' into temp_bmesh_multires 2021-06-29 04:35:53 -07:00
199f846072 Merge branch 'master' into sculpt-dev 2021-06-28 19:44:07 +02:00
fb6636c812 Sculpt dyntopo: fix bug with automasking being needlessly initialized 2021-06-26 23:46:47 -07:00
adc8980dec Sculpt dyntopo: fix symmetrix undo bug from last commit 2021-06-26 22:52:35 -07:00
48c41b189a fix type of code line being after the parenthesis
instead of before
2021-06-26 21:40:07 -07:00
7562ad5777 Sculpt dyntopo: BMLog now uses the new bmesh unique id system
A few notes:

* MESH_ID layers are not saved on conversion to Mesh unless
  you ask for it in BM_mesh_bm_to_me's params.
* Still need to test the box/lasso trim tools.
* Need to find some way to test loop/edge ids, which aren't
  used by dyntopo.
2021-06-26 21:26:33 -07:00
27986e9b56 Sculpt dyntopo: Added a function to add multiple customdata
layers to a bmesh at once.  Helpful since bmesh customdata
layers are allocated in single blocks, thus adding
layers individually can lead to lots of memory
copying.
2021-06-26 19:12:07 -07:00
dba4f30328 Sculpt dyntopo: Added (optional) support for unique mesh id tracking in
bmesh

* System is per element type.  So you can have unique ids for verts and
  faces, but not edges and loops.
* Supports an optional id to element lookup table.
* Uses single id space for all elements
* Added a new CD_FLAG_ELEM_NOCOPY flag to tell
  customdata_bmesh_copy_data to ignore that layer.
* IDs are stored as a temporary customdata layer with
  CD_FLAG_ELEM_NOCOPY set.
2021-06-26 18:24:00 -07:00
83c491f044 Merge branch 'master' into temp_bmesh_multires 2021-06-26 13:00:26 -07:00
7ef7843ada Sculpt Dyntopo: Fix memory corruption in dyntopo undo
ss->active_XXX_index wasn't being handled properly.
2021-06-25 17:10:12 -07:00
5a7b9adef1 Dyntopo sculpt: add support for asan to mempool 2021-06-25 15:04:09 -07:00
d293de425f Sculpt Dyntopo: fix bug in dyntopo brush spacing. 2021-06-25 11:49:28 -07:00
17d4c7abb1 Sculpt dyntopo:
* Got automasking to work with dyntopo properly.
  - AutomaskingCache->factor
    has been replaced with the new temp layer API (which works for all
    PBVH modes).
  - AutomaskingCache->factor is, however, only initialized for
    topology and face set boundary modes (if DynTopo is enabled)
    since it's probably better to calculate the rest dynamically
    in that case.
* Fixed stats bug
2021-06-24 23:50:49 -07:00
2f81449a70 Twist brush: use relax instead of smooth 2021-06-22 01:20:36 +02:00
ca4ac36c59 Merge branch 'master' into temp_bmesh_multires 2021-06-20 15:58:24 -07:00
5c328ab8b0 Twist Brush: Add separate smooth step 2021-06-20 18:32:19 +02:00
7a67cb2aeb Twist Brush: Fix bug in rotation 2021-06-20 17:53:35 +02:00
178c3a4064 Twist Brush: Scale brush tip space per vertex 2021-06-20 17:34:20 +02:00
15884510e8 Twist Brush: Deform rotation space 2021-06-20 17:21:53 +02:00
22e2e4f062 Twist Brush: Rotate in brush tip space 2021-06-20 17:12:05 +02:00
3ccc160682 Merge branch 'master' into sculpt-dev 2021-06-20 16:46:21 +02:00
00fd823bcd Sculpt dyntopo: Don't calculate curvature info in topology rake if curvature
mode is not enabled.
2021-06-19 23:54:51 -07:00
d9badbf25c Merge branch 'master' into sculpt-dev 2021-06-19 22:12:13 +02:00
f16e35ce0a Twist Brush: Initial working version 2021-06-18 22:01:26 +02:00
425e5c8d76 Twitst Brush: initial commit 2021-06-18 21:44:29 +02:00
1ab412f13d Merge branch 'master' into sculpt-dev 2021-06-18 21:24:36 +02:00
ee1c0c682b Fix build in edit mode mesh fairing 2021-06-16 21:06:27 +02:00
59f0811ac9 Merge branch 'master' into sculpt-dev 2021-06-16 21:01:43 +02:00
45f522888b Merge branch 'master' into sculpt-dev 2021-06-14 22:53:41 +02:00
d15262ded1 Merge branch 'master' into sculpt-dev 2021-06-11 21:58:12 +02:00
ab5b138805 Merge branch 'master' into sculpt-dev 2021-06-08 22:56:21 +02:00
8ca52a7757 Merge branch 'master' into temp_bmesh_multires 2021-06-07 16:57:33 -07:00
d4af8a8d26 Sculpt: Make subdivision surface modifier use the On Cage option 2021-06-04 16:20:38 +02:00
b1e59f1a20 Merge branch 'master' into sculpt-dev 2021-06-03 22:46:38 +02:00
bd34d77a90 Merge branch 'master' into sculpt-dev 2021-06-03 20:24:11 +02:00
3ac83dcc73 Merge branch 'master' into sculpt-dev 2021-06-01 14:26:49 +02:00
900178d438 Fix build after merge 2021-06-01 14:22:49 +02:00
414b8b7628 Merge branch 'master' into sculpt-dev 2021-06-01 12:52:07 +02:00
4adc0a7798 Dyntopo: do a little bit of code cleanup 2021-05-31 10:53:22 -07:00
88620e8cd9 Merge branch 'master' into sculpt-dev 2021-05-31 11:14:07 +02:00
552e44bd25 Fix implicit int error 2021-05-27 14:30:45 -07:00
1f19a86150 DynTopo:
* Fixed failed patch reversion from a while ago
* Also tweaked a few comments.
2021-05-27 12:15:49 -07:00
702376e935 Merge branch 'master' into sculpt-dev 2021-05-27 11:24:32 +02:00
3ca3098ed7 DynTopo: Enable BLI_linklist_stack type checking for clang-cl
and fix a few errors.
2021-05-24 11:40:05 -07:00
995702da48 Let's try that again. 2021-05-23 21:39:37 -07:00
33066542ca MIssed a flag in bli_strict_types.h 2021-05-23 21:34:20 -07:00
5a26ea0961 DynTopo: Attempt to fix compiling on macos 2021-05-23 21:02:29 -07:00
1e3e79fe7b Fix one more strict flags violation 2021-05-23 20:52:25 -07:00
d4292bbd28 * Added clang-cl support to BLI_strict_flags.h 2021-05-23 20:39:52 -07:00
a97c5d7daa DynTopo: started refactoring pbvh drawing to handle customdata layers in
a more generic (and maintainable) way.
2021-05-23 16:11:16 -07:00
6b009b8890 Merge branch 'master' into temp_bmesh_multires 2021-05-23 14:30:32 -07:00
3d16099a77 Dyntopo:
Fix multires not setting pbvh->depth_limit
2021-05-20 17:59:09 -07:00
4352980b0f Dyntopo
* Got threaded mesh->bmesh conversion working (it's disabled
  pending further testing however).

Note that SCULPT_dynamic_topology_enable_ex calls BKE_scene_graph_update_tagged,
which in tests was adding ~1 second to conversion time for larger
meshes.  Do we need this call?
2021-05-19 14:36:00 -07:00
3d6ac0bd7b Dyntopo: code cleanup 2021-05-19 12:21:46 -07:00
71959181ad * Fix bug with symmetrize creating non-manifold geometry.
* Fix bug in pbvh face create
2021-05-18 22:26:49 -07:00
daa4a33383 Merge branch 'master' into temp_bmesh_multires 2021-05-17 19:48:16 -07:00
d3695eb12c * Fix corner case in last commit 2021-05-17 14:23:02 -07:00
c1f236dcd4 BMLogFace now stores the face normal. 2021-05-17 14:02:31 -07:00
3276c3b4a1 Merge branch 'master' into sculpt-dev 2021-05-17 22:08:28 +02:00
a90533e9b8 Cleanup failed patch reversion 2021-05-17 00:45:07 -07:00
9bea7259e1 Merge branch 'master' into temp_bmesh_multires 2021-05-16 23:32:35 -07:00
7fca310f25 Dyntopo now updates the existing pbvh on undo instead of building
a new one from scratch, an operation that can be slow despite being
  threaded.

PBVH building is a memory bound operation (not just on the CPU side
either, remember the draw buffers have to be fully regenerated too).
Incrementally updating it this way is enormously faster (about as fast
as non-dyntopo undo).

The downside is we don't have the convienience of users regularly
building the pbvh from scratch anymore.  Dyntopo does try to
join empty PBVH nodes (which happens after every stroke), but
that's not a complete substitute for a decent tree balancer.
That's on the todo list.
2021-05-15 21:19:20 -07:00
27f4f761e7 Tried to make pbvh bmesh normals calc a bit more efficient. 2021-05-15 11:51:14 -07:00
17fafe2f63 fix memory leak 2021-05-14 21:21:01 -07:00
89d56ea225 fix missing null pointer check. 2021-05-14 21:12:23 -07:00
cfd6d48aab * Prototyped a faster smooth algo, currently disabled.
* Wrote a new API for wrangling temporary customdata layers across pbvh types:
  - SCULPT_temp_customlayer_ensure: makes sure a (named) customdata
    layer exists.  Works for multires; since these are temporary
    layers we can safely allocate them in a temporary CustomData
    structure (in ss->temp_vdata).
  - SCULPT_temp_customlayer_get: initializes a special structure,
    SculptCustomLayer, that is used to get per elem customdata.
  - SCULPT_temp_cdata_get: Uses a SculptCustomLayer ref along with
    a SculptVertexRef to look up the data.
2021-05-14 18:26:41 -07:00
6be2c079c1 Did some profiling with VTune.
* Sculpt code seems to be memory bandwidth bound.
  * Some key topology loops will have to be written manually
    instead of using BM_ITER.

I wrote a function to re-allocate a bmesh with elements ordered by
PBVH leaf nodes, SCULPT_reorder_bmesh.  It's currently disabled.

This is going to take more profiling, but my original proxy refactor
idea might be worth revisiting.  Might be more cache efficient.

The good news is that the worst case is the smooth code, which I can speed
up significantly by keeping a bit of state around.
2021-05-14 15:56:04 -07:00
ae5d971e8f Merge branch 'master' into sculpt-dev 2021-05-14 19:09:06 +02:00
13bdc4a7e6 Merge branch 'master' into sculpt-dev 2021-05-14 19:08:19 +02:00
72aad83780 Merge branch 'master' into sculpt-dev 2021-05-13 17:43:21 +02:00
32ceaa7919 Update ATTR_NO_OPT macro 2021-05-12 22:27:35 -07:00
ec4786d00b Dyntopo branch
* Sculpt expand now works with dyntopo in more cases
* Fixed various dyntopo face set bugs

Stuff from several commits ago:

* There is now an API to get face sets using SculptFaceRef's.
  + SCULPT_face_set_get
  + SCULPT_face_set_set

* Prototyped a faster geodesic propagation function, but it currently
  doesn't work.

* Dyntopo triangulation now preserves face flags (took some work as BM_triangulate_face explicitly prevents selection flags from being preserved).
* Also face smooth flags are no longer being overriden.
* Most of the faceset create operators work (I'm not sure I've tested
  all of them though).
* SCULPT_face_set.c now has helper functions that checks if a pbvh
  is *not* PBVH_BMESH, in which case it builds a temporary bmesh,
  otherwise ss->bm is used (sculpt_faceset_bm_begin and sculpt_faceset_bm_end).
  + Note that some functions were able to use SCULPT_face_set_XXX
    instead and avoid bmesh entirely.
2021-05-12 16:22:39 -07:00
582c30d32f Fix nasty edge case for BMLog. 2021-05-11 22:57:06 -07:00
b047b333b0 Remove yet more debugging crap . . . 2021-05-11 21:48:14 -07:00
f2c9706781 Get rid of various ATTR_NO_OPT debug attributes left from two
commits ago.
2021-05-11 21:46:44 -07:00
fa06238aa7 Fix bug in last commit. 2021-05-11 21:45:43 -07:00
b8a8e4f9b1 Today I ran outside screaming and an hour later came back in and made BMLog
reference counted.  This fixes various undo bugs caused by dyntopo
needing to execute an undo push but not being able too (e.g. during
file load, and I think it also happens sometimes with global undo).

A much better way of fixing this would be to add unique IDs to mesh
verts and faces, perhaps as a customdata layer.

The root problem is that BMLog assigns unique IDs to mesh elements,
which it does via a series of GHash's.  I imagine this is a fairly
serious violation of the undo system's design rules, since it means
BMLogs are tied to specific instances of the bmesh in SculptSession->bm.

There were some hacks to try and get around this, but they were buggy
and needed to push the unstack stack to work, which wasn't possible in
all cases (e.g. if G_MAIN->wm.first->undo_stack is NULL, as it is during
file loading and apparently global undo).

Anyway, long story short each chain of SculptUndoNodes needs some way
to reconstruct their ID GHash's when exiting/entering the chain. The
only way I could think to do this was to make BMLog reference counted,
with BMLogEntry the users.

Like I said, having a proper system to assign unique IDs to mesh
elements would be *much* better.
2021-05-11 21:14:45 -07:00
dbe767f5d9 Merge branch 'master' into temp_bmesh_multires 2021-05-11 14:18:48 -07:00
fad42fc6c7 Merge branch 'master' into sculpt-dev 2021-05-10 19:01:57 +02:00
7cf3b1f472 Get face set create operator working for dyntopo 2021-05-07 07:04:19 -07:00
6c3b29a14c Merge branch 'master' into temp_bmesh_multires 2021-05-05 08:52:14 -07:00
febfaecd97 * Added new paint API method paint_stroke_apply_subspacing, for various
things that need custom spacing (that is coaser than the brush radius),
  and refactored the existing dyntopo spacing code to use it.

* Added option to topology rake to ignore brush falloff settings
  (it forcibly uses Smooth falloff).

* Smooth and topology rake support custom spacing now.
  + This is especially important for the clay brush, which works
    better at smaller spacings and with a bit of autosmoothing.
    Now you can tell it to override the smooth spacing to be coarser,
    leading to much better performance.

* Topology rake now has a projection setting similar to autosmooth
  which defaults to 1.0 (to preserve current behavior).

The main motivation for this commit was to make topology rake work
better for normal brushes.  This is now possible, however it tends to
make the brush slower and also the settings are a bit fiddly.
We might want to make dedicated brush presets for this.

Btw, the UI for brush settings are becoming a real mess. Help!
2021-05-02 14:29:30 -07:00
2d98802905 * Added a "projection" option to smooth/autosmooth. It works by
projection neighboring vertices onto the current smooth vert's normal
  plane, multiplied by a "projection" factor.  This is extremely similar
  to what the surface laplacian produces, but is much simpler, uses
  no temporary state and thus can be used in more places.
2021-05-02 10:54:12 -07:00
f70a8c1581 Merge branch 'master' into temp_bmesh_multires
Also fixed a pose brush bug that may or may not have been
the result of the merge nixing code, need to check master.
2021-04-29 10:17:22 -07:00
8eeacca9cb Fix a few cast warnings 2021-04-29 08:01:32 -07:00
82847af9f9 Fix invalid parameter type in bmlog. 2021-04-29 06:28:37 -07:00
ee19a80041 * Eeek, left in an ATTR_NO_OPT attribute 2021-04-28 19:31:43 -07:00
013eee7ec6 * The dyntopo remesher is now a bit smarter about avoiding
certain degenerate cases that produce lots of bad geometry (and
  also bad PBVHs that slows down the whole mesh).
 + A very small amount of surface smoothing is now applied by dyntopo.
 + The recursive functions that expand the region considered by dyntopo
   now ignore "front face only" for depths less then 5.
 + The number of edges dyntopo can split or collapse in one run has been cut in
   half to 4k.
   - This limit is also now dynamically adjusted by a measure
     of how skinny the edges are.
2021-04-28 19:11:23 -07:00
ce0b4e6681 Merge branch 'master' into sculpt-dev 2021-04-28 21:55:09 +02:00
d1cf59e547 Sculpt: Add operator to store a limit surface manually 2021-04-27 23:54:04 +02:00
d0407b0ed5 Merge branch 'master' into sculpt-dev 2021-04-27 22:42:21 +02:00
83a855a8a1 fix compile error 2021-04-27 13:38:14 -07:00
9e46eebcf1 Fix last commit flipping the order of booleans. 2021-04-27 13:20:36 -07:00
786781304c * Got box trim tool working for dyntopo
- This required implementing SCULPT_UNDO_GEOMETRY for dyntopo.
    That turned out to be more work then I expected.  Basically
    it writes an entire Mesh to BMLogEntry, which can be swapped
    with current bmesh.  Tricky part was patching bm log ids.
2021-04-27 13:04:36 -07:00
2e888bfe6c Fix possible crash with dyntopo and clay strips normal filter 2021-04-27 21:31:25 +02:00
901d6e68d7 Merge branch 'master' into sculpt-dev 2021-04-27 21:31:00 +02:00
3028d53865 Edit face set brush now works for pbvh bmesh 2021-04-27 09:51:49 -07:00
fb5f6e762e Merge branch 'master' into sculpt-dev 2021-04-27 18:31:19 +02:00
02b4df9827 Enable color filter brush for pbvh bmesh 2021-04-27 09:12:35 -07:00
815d77192f One more null ptr check 2021-04-27 09:07:00 -07:00
9fab16f0f3 * Fix null ptr bug in automasking code 2021-04-27 09:01:32 -07:00
82f5e0200c * Fix bug in last commit where boundary flags weren't properly
updated after symmetrize.
2021-04-26 17:44:38 -07:00
032a35fb50 Fix memory corruption in sculpt neighbors code 2021-04-25 15:12:11 -07:00
8a700673db * Boundary info is no longer initialized with
SCULPT_boundary_info_ensure, instead PBVH_BMESH keeps
  track of boundary vert flags internally.  This prevents
  nasty first-click lag on the smooth brush as well as anything
  with autosmooth.
2021-04-25 14:51:31 -07:00
0386350de6 * Fixed SCULPT_dynamic_topology_sync_layers to properly sync
active/render/clone/mask layers indices.
* Fixed sculpt color bugs in pbvh drawing for bmesh.
2021-04-25 14:02:39 -07:00
d912d4735a Merge branch 'master' into sculpt-dev 2021-04-22 21:23:44 +02:00
97fc606c65 Fix nasty crash in dyntopo with non-manifold geometry. 2021-04-21 10:00:02 -07:00
14be52f0fc * Fixed vcol drawing in indexed smooth shading mode 2021-04-20 15:40:03 -07:00
739bb8f1d2 Merge branch 'master' into sculpt-dev 2021-04-20 22:43:50 +02:00
7107753f73 Merge branch 'master' into sculpt-dev 2021-04-20 17:56:22 +02:00
73c590e1eb Added detail_size to local dyntopo brush settings 2021-04-20 06:34:35 -07:00
44fd07c1c8 Merge branch 'master' into sculpt-dev 2021-04-19 23:24:55 +02:00
931a8da26c Merge branch 'master' into sculpt-dev 2021-04-19 20:14:24 +02:00
895fef5ccb Sculpt: Add area normal test to clay strips for better front faces detection 2021-04-18 20:10:12 +02:00
b82a2fd9ab Fix wrong visibility flush with Face Sets 2021-04-18 19:36:44 +02:00
18a86af416 Fix neighbor connectivy with hidden edges 2021-04-18 19:00:02 +02:00
a0b5fd9cb6 Fix Scene project brush after merge 2021-04-18 18:14:45 +02:00
553e8ffd1a Merge branch 'master' into sculpt-dev 2021-04-18 18:04:00 +02:00
fdc6fb2c53 SCULPT_calc_principle_curvatures now has option to use
slower but more accurate BLI_eigen_solve_selfadjoint_m3 solver.
2021-04-17 20:25:51 -07:00
8bc9286c4c Merge branch 'master' into sculpt-dev 2021-04-16 19:52:23 +02:00
4f8693652b Sculpt Symmetrize: Fix missing updates with modifiers 2021-04-16 01:47:27 +02:00
d29378c2f2 Sculpt Symmetrize: Free the symmetrize map on changes 2021-04-16 01:31:44 +02:00
0e6c07c063 Sculpt Symmetrize: Prevent crash with dyntopo and multires 2021-04-16 01:30:02 +02:00
cb3e0ff344 Sculpt Symmetrize: Add brush falloff 2021-04-16 01:29:12 +02:00
11d1fdeb34 Sculpt Symmetrize: Fix bug in main brush loop 2021-04-16 01:25:22 +02:00
430641d481 Sculpt Symmetrize: Initial working version 2021-04-16 01:24:49 +02:00
4745f29971 Sculpt Symmetrize: Add base symmetrize map code 2021-04-16 00:56:39 +02:00
8512b1b5dd Sculpt Symmetrize: sculpt_symmetrize file and main loop 2021-04-16 00:47:08 +02:00
674bc2e4b4 Sculpt Symmetrize: Initial commit 2021-04-16 00:38:47 +02:00
b240973412 Object: Make flash animation slower 2021-04-15 20:28:04 +02:00
39994c3208 Object: Use theme select color for animation 2021-04-15 20:26:45 +02:00
8189f2c806 Merge branch 'master' into sculpt-dev 2021-04-15 20:14:30 +02:00
237ac342c9 Merge branch 'master' into temp_bmesh_multires 2021-04-14 22:07:27 -07:00
465e804193 commit prior to merge 2021-04-14 22:04:36 -07:00
4ab8614cd1 Last attempt at fixing boundary brush for now. Unassigned boundary
verts now get bend data from their neighbors.  Still isn't quite
satisfactory but better then before.
2021-04-14 22:02:32 -07:00
dabff9af7d * Fix crash in boundary sculpt tool
* For some reason boundary tool sometimes fails to match verts to
  boundary verts.  It now simply freezes them in place if that happens
2021-04-14 18:41:23 -07:00
384a0930d1 Commit current code state 2021-04-14 15:55:09 -07:00
a5fa9ec310 Fix another crash 2021-04-13 01:44:29 -07:00
ed15d0c1b9 Fix infinite loop bug 2021-04-13 01:08:05 -07:00
e07a95b86d Fix extremely nastly memory corruption bug in pbvh 2021-04-12 04:19:49 -07:00
b52e04f794 * Fix bug in last commit, broke draw brush 2021-04-11 23:39:25 -07:00
e74c298cf9 Fix dyntopo undo bug 2021-04-11 22:03:16 -07:00
cd485a7c6f fix last commit 2021-04-11 21:44:00 -07:00
06341c19c4 Fix crash 2021-04-11 21:42:47 -07:00
a527bd5f48 Get rid of various __attribute__((optnone)) I had scattered throughout
the code
2021-04-11 19:29:06 -07:00
7f14d519c0 * Dyntopo cleanup mode is now on by default.
* Fixed undo bug
2021-04-11 18:19:13 -07:00
64337d087d * Merge branch 'master' into temp_bmesh_multires
* Implemented DynTopo option to dissolve 3 and 4-valence
  vertices.
* Fixed bug in dyntopo collapse customdata snapping.
2021-04-11 17:59:16 -07:00
e1fae3cbee Fix do_versions check 2021-04-11 12:34:15 -07:00
7dbb4c9b2d Fix issues with last commit. Also, simplify brush now
uses dyntopo local overrides to always enable collapse instead
of being hardcoded.
2021-04-11 12:27:02 -07:00
fe67ca56d6 * Fixed wireframe drawing in vcol cell drawing mode
* Added UI for editing local brush dyntopo settings.
2021-04-11 11:38:27 -07:00
68e4e1bb41 Merge branch 'master' into sculpt-dev 2021-04-11 17:43:40 +02:00
881df30a46 Cavity masking now has a checkbox in the UI, instead of simply turning
itself on if the concave mask factor is > 0.
2021-04-07 22:49:39 -07:00
6f91eaad39 * Fixed an annoying number of undo bugs
* Original data for bmesh pbvh is no longer handled by the undo code.
  This should eliminate a whole class of subtle and hard to track down
  bugs.
2021-04-07 20:39:16 -07:00
55045ed85a SCULPT_dyntopo_ensure_templayer no longer returns a customdata offset.
Making multiply layers sequentially can lead to corrupted offsets.
Instead, ensure all layers exist with SCULPT_dyntopo_ensure_templayer
first,

then get all the offsets at once.
2021-04-07 02:03:32 -07:00
2582090824 Fix bug in previous commit, code was being subject to inside
brush test that should not ahve been.
2021-04-07 01:46:10 -07:00
55415cd62a * Layer brush now supports dyntopo.
- To do this I made a little API to make scratch
    customdata layers: SCULPT_dyntopo_[ensure/get]_templayer.
    Takes a customdata type and a layer name (e.g.
    '__dyntopo_bleh") and returns a customdata offset.
  - Note that I also did this for the persistent base code.

* Added a macro to check if a tool supports splitting the PBVH
  during a stroke, DYNTOPO_HAS_DYNAMIC_SPLIT.  It is in sculpt_intern.h
  (for now) to avoid the enormous amount of recompiling that is
  triggered if DNA_brush_enum.h is modified.

* TODO: Right now the undo code resets original vertex coordinates for
  bmesh PBVH.  This means tools that rely on original data (sharp and
  layer) can't split the pbvh during the stroke, since that will
  allocate new undo nodes and reset original coords.

  The solution is to move the original data API entirely out of the undo
  code.
2021-04-07 01:20:21 -07:00
d29bdda34c Fix build error with include 2021-04-06 19:56:17 +02:00
e3ee04dec5 Object: Flash objects on mode transfer 2021-04-06 19:49:18 +02:00
347c8255ed Merge branch 'master' into sculpt-dev 2021-04-06 17:49:13 +02:00
8aac19cab5 Merge branch 'master' into temp_bmesh_multires 2021-04-05 21:26:04 -07:00
b4536d274c Forgot to increase array size for data transfer modifier struct. 2021-04-05 21:24:38 -07:00
d3ffaa8e52 Merge branch 'master' into sculpt-dev 2021-04-05 18:22:38 +02:00
f5588dfb70 Merge branch 'master' into temp_bmesh_multires 2021-04-03 19:29:12 -07:00
4b308888d8 Add support for sculpt colors to the data transfer modifier 2021-04-03 19:23:15 -07:00
fdfa2045ec Add a paranoia check 2021-04-02 13:55:48 -07:00
33af94dc46 Fixed an out of bounds read error. 2021-04-02 13:42:29 -07:00
8ea624aeaf fix gcc build errors 2021-04-01 15:58:56 -07:00
e29cd298fe Fix last commit to handle symmetrical brushing properly 2021-04-01 15:09:50 -07:00
7bba304c57 * Dyntopo now has its own brush spacing. This is a huge performance
boost for brushes with fine spacing, e.g. the clay brush.
2021-04-01 14:51:10 -07:00
0dc09668ce Finished curvature rake. 2021-04-01 13:07:10 -07:00
cfa6753518 wrote a fast and inaccurate distance to triangle function
for DynTopo.  Profiling consistently showed it to be a bottleneck.
I've now written scalar versions of this function four times.

For now I'm committing this inaccuration version.  I'm also committing
code for a vectorized version I found in a paper.  Still need to rejigger
the dyntopo code to use it though.
2021-03-30 19:00:08 -07:00
2cf8c35578 Merge branch 'master' into temp_bmesh_multires 2021-03-30 00:57:43 -07:00
aa116ba5ba Added per-brush DynTopo settings which are stored in a new DynTopoSettings struct.
This system is designed to inherit settings from scene dyntopo defaults
in a highly flexible way; settings can be individually overridden via the
.inherit bitmask.

At stroke time the scene settings and brush->dyntopo are merged
and stored in brush->cached_dyntopo.

Note that brush->flag has a bit flag, DYNTOPO_DISABLED, with a few
subtlies.  It does not switch the PBVH back to PBVH_FACES mode, it
simply disbles dyntopo topology update.  It also doesn't inherit from
any default settings.  And it's the only setting
that's currently exposed in the UI.
2021-03-30 00:08:09 -07:00
d2c1357fc5 Sculpt: Initial implementation of uniform smoothing 2021-03-27 21:52:24 +01:00
0bf2a1f8fe Merge branch 'master' into sculpt-dev 2021-03-27 21:27:41 +01:00
7be027075f Add this file 2021-03-26 14:23:31 -07:00
215c346017 Only send all vcol layers to gpu (for pbvh drawing) in a material
draw mode.
2021-03-25 17:38:27 -07:00
7c9235d0e0 * Sculpt color layers are now properly handle by pbvh bmesh draw code.
Before only the active layer was uploaded to the GPU, now all of them
  (except the autogenerated original color layer) are.
2021-03-25 17:17:42 -07:00
e254cc23b2 Code cleanup 2021-03-25 11:32:20 -07:00
f5a122309d Sculpt Poly Loop: Fix symmetry 2021-03-25 02:46:00 +01:00
dee286fc7e clean up code a bit 2021-03-24 17:01:53 -07:00
ab9b89ac5d Fixed two remaining performance bugs with dyntopo:
* Normals are now updated in threads.
* The sculpt neighbor code was using the slower BM_LOOPS_OF_VERT
  instead of BM_EDGES_OF_VERT, fixed.
2021-03-24 14:09:02 -07:00
ce9e0b53b1 Sculpt Expand: Default Face Set boundary modes to topology 2021-03-24 21:20:09 +01:00
ce6d9112aa Sculpt Poly Loop: Symmetry support 2021-03-24 20:55:31 +01:00
20f4fe138e Merge branch 'master' into sculpt-dev 2021-03-24 20:55:05 +01:00
ecbf642ddd Cleanup: Clang format 2021-03-24 18:48:39 +01:00
caf5f5767f Cloth Filter: Add pinch origin modes 2021-03-24 18:47:01 +01:00
e0cc387124 Cloth Filter: Add symmetry support to pinch deformation mode 2021-03-24 18:12:55 +01:00
ebeb144701 Merge branch 'master' into sculpt-dev 2021-03-23 17:54:08 +01:00
924b13b03a * Added a new CustomData type for dyntopo vertex data: MDynTopoVert.
It stores:
   - Original coordiates, colors and mask (which were previously four
     seperate layers).
   - A bitmask with (currently) one bitflag, whether or not a vertex is
     on a boundary.

I needed to cache calculating vertex boundary state (which involves
iterating over the edges surrounding a vertex) and got fed up with
having so many CD layers for dyntopo.  This struct consolidates them
and saves having yet another layer just to store a flag.
2021-03-21 16:26:38 -07:00
75a1116b51 Merge from master 2021-03-19 18:01:12 -07:00
73a4b885ec Merge branch 'master' into sculpt-dev 2021-03-19 20:08:30 +01:00
f6cda6bf88 Merge branch 'master' into temp_bmesh_multires 2021-03-19 10:57:32 -07:00
9ce0a2815d Merge branch 'master' into sculpt-dev 2021-03-19 18:44:23 +01:00
7d7dcce859 Merge branch 'master' into sculpt-dev 2021-03-18 16:42:53 +01:00
f429a7dc13 Sculpt Expand: Split snap event into two separate events 2021-03-17 16:58:56 +01:00
257562ad6d Sculpt Expand: Add Poly Loop Falloff mode 2021-03-17 16:49:08 +01:00
8883c76b9a Merge branch 'master' into sculpt-dev 2021-03-17 16:20:44 +01:00
dd77b22626 Face Sets Topology: Improve keymap and creation delay 2021-03-17 03:03:02 +01:00
3d70805a68 Sculpt Poly Loop: Improve loop direction detection 2021-03-17 01:39:08 +01:00
b6f0176c10 Face Sets Topology: Add repeat last and default keymap 2021-03-17 01:20:50 +01:00
6835ea72f2 Sculpt Poly Loop: Move poly loop code to its own file 2021-03-17 00:07:41 +01:00
6408c4ebf6 Sculpt Poly Loop: refactor poly loop code for reusability 2021-03-16 23:46:46 +01:00
e557e4d03c Face Sets Topology: Initial working version 2021-03-16 23:17:08 +01:00
02d482e8f2 Face Sets Topology: Initial loop iteration implementation (one side only) 2021-03-16 22:48:31 +01:00
525badbd7d Face Sets Topology: Initial edge loop detection 2021-03-16 22:28:39 +01:00
49fa3dd5ff Face Sets Topology: Initial commit 2021-03-16 22:03:22 +01:00
8b0bc7e3e8 Cleanup: Clang format 2021-03-16 21:25:56 +01:00
ef4abf1212 Sculpt IPMask: Working exec callback for scripts 2021-03-16 21:24:08 +01:00
6ce37c59ec Sculpt IPMask: Refactor filter initialization 2021-03-16 21:05:10 +01:00
6824d73b75 Sculpt IPMask: Add direction property to the filter 2021-03-16 20:45:58 +01:00
06762eefcf Sculpt IPMask: Start implementing exec callback 2021-03-16 20:42:12 +01:00
f791fc031e Sculpt IPMask: Fix memory leak in step compute 2021-03-16 20:26:55 +01:00
57b7eba4b6 Merge branch 'master' into sculpt-dev 2021-03-16 20:00:37 +01:00
abda36c076 Sculpt: Apply latest clang-format changes to the branch 2021-03-14 21:15:47 +01:00
476b333a3e Merge branch 'master' into sculpt-dev 2021-03-14 21:14:58 +01:00
4ad1a7ab31 Cleanup: Clang format 2021-03-14 21:14:39 +01:00
5f5aa43ab1 Sculpt: Fair all by tangency mode for face set edit 2021-03-14 21:14:19 +01:00
4293db60b3 Merge branch 'master' into sculpt-dev 2021-03-12 21:57:37 +01:00
647c065b9c Merge branch 'master' into sculpt-dev 2021-03-12 16:51:18 +01:00
497e532e23 Merge branch 'master' into sculpt-dev 2021-03-12 01:46:40 +01:00
e1f338b9f4 Merge branch 'master' into sculpt-dev 2021-03-11 17:53:47 +01:00
15ef2d42f3 Merge branch 'master' into sculpt-dev 2021-03-10 18:55:12 +01:00
a9083cc0ee Sculpt: Move Mask Init to its own file 2021-03-09 22:11:23 +01:00
c8a03bc716 Sculpt IPMask: Increase sensitivity of contrast mask filter 2021-03-09 21:53:19 +01:00
3efb094fd3 Merge branch 'master' into sculpt-dev 2021-03-09 21:50:31 +01:00
251440f538 Merge branch 'master' into sculpt-dev 2021-03-09 18:44:15 +01:00
cb0f159155 Merge branch 'master' into temp_bmesh_multires
Merge not finished, but need to commit to move to different computer;
laptop being sent in for repairs
2021-03-08 13:57:21 -08:00
409a525fa9 Merge branch 'master' into sculpt-dev 2021-03-08 21:18:53 +01:00
966af5a075 Cleanup: Clang format 2021-03-08 17:37:57 +01:00
5234e23c2c Voxel Remesher: Option to preserver material slots 2021-03-08 17:37:47 +01:00
11613f8c7a Merge branch 'master' into sculpt-dev 2021-03-07 18:06:27 +01:00
8a98189bfb commit before merge 2021-03-07 04:00:14 -08:00
3678b87272 Merge branch 'master' into sculpt-dev 2021-03-06 18:51:48 +01:00
1f2b48eb45 Add a little todo list 2021-03-05 16:54:19 -08:00
dd1c4c84c9 Merge branch 'master' into sculpt-dev 2021-03-04 20:06:27 +01:00
de2625518b Merge branch 'master' into sculpt-dev 2021-03-04 17:32:05 +01:00
75709e4d57 Fix crash in Boundary brush expand mode after refactor 2021-03-03 22:13:19 +01:00
6a74211f99 Merge branch 'master' into sculpt-dev 2021-03-03 20:25:18 +01:00
d3742022c2 Sculpt: Make global smooth factor control the strengh directly 2021-03-03 18:52:16 +01:00
75794afdcd Cleaup: Clang format 2021-03-03 18:51:35 +01:00
b1b49f6130 Sculpt IPMask: Tweak default filter sensitivity 2021-03-03 17:29:46 +01:00
ca60d520d9 Sculp IPMask: Property to enable/disable step interpolation 2021-03-03 17:29:24 +01:00
2fd22fd663 Cleanup: Clang format 2021-03-03 17:12:38 +01:00
04c85de559 Sculpt IPMask: Implement restoring and cancelling the filter 2021-03-03 17:10:09 +01:00
407dd37573 Merge branch 'master' into sculpt-dev 2021-03-03 17:08:59 +01:00
0acaa40c08 Fix after merge 2021-03-03 16:43:41 +01:00
f2024f7d99 Merge branch 'master' into sculpt-dev 2021-03-02 17:05:00 +01:00
a7b5bbf13f Merge branch 'master' into sculpt-dev 2021-03-01 22:28:41 +01:00
e696280b88 Sculpt IPMask: Fix crash in original data updates 2021-03-01 22:14:22 +01:00
5e9eb1930f Sculpt IPMask: Implement filters that use original data 2021-03-01 22:03:22 +01:00
9a7cbff535 Sculpt IPMask: Some experiments for a better sharpen filter 2021-03-01 20:21:23 +01:00
8dec2ca7ba Sculpt IPMask: Tweak default sensitivity of each step 2021-03-01 19:24:58 +01:00
098d0d782a Sculp IPMask: First working version of substep interpolation 2021-03-01 19:19:41 +01:00
779a268c6e Sculpt IPMask: Initial refactor to support step interpolation 2021-03-01 18:52:27 +01:00
13b7b69e7f Merge branch 'master' into sculpt-dev 2021-03-01 18:01:17 +01:00
148b39caec Added some code to calculate principle curvature direction for
uniform triangle tesselations (dyntopo).  This will be used for
a version of topological rake that aligns edge flows to lines of curvature
automatically.
2021-03-01 01:09:27 -08:00
e9f9217f75 Merge from master 2021-02-28 17:05:35 -08:00
dd45a4bc6e Merge branch 'master' into temp_bmesh_multires 2021-02-28 04:59:03 -08:00
1ccd92d0bb commit prior to merge 2021-02-28 04:23:45 -08:00
9f78a24eca Merge branch 'master' into sculpt-dev 2021-02-27 18:32:35 +01:00
930a9edc1e Sculpt IPMask: First version of contrast filter 2021-02-26 22:03:48 +01:00
5cfe7bf72c Merge branch 'master' into sculpt-dev 2021-02-26 20:34:38 +01:00
422b1c64cd Merge branch 'master' into sculpt-dev 2021-02-26 19:05:58 +01:00
1edad56ffc Sculpt IPMask: update pie menu 2021-02-26 17:17:00 +01:00
ef3d68f17b Sculpt IPMask: Use the new mask filter in the pie menu 2021-02-26 14:46:02 +01:00
094ffcf669 Sculpt IPMask: Implement some other filterts (hard/soft) 2021-02-26 02:47:49 +01:00
56d33186a4 Sculpt IPMask: Move main data update to its own function 2021-02-26 01:41:23 +01:00
d570feeffc Cleanup: Clang format 2021-02-26 01:36:13 +01:00
158397a007 Sculpt IPMask: Ignore inbetween mouse events 2021-02-26 01:35:30 +01:00
4e8c430c6e Sculpt IPMask: Free ghash when finishing 2021-02-26 01:31:49 +01:00
d8a4ebb068 Sculpt IPMask: Multithreaded and fast mesh updates 2021-02-26 01:23:45 +01:00
6d46a3ca4e Sculpt IPMask: Enable iteration count 2021-02-26 01:12:14 +01:00
1751b3c7c1 Sculpt IPMask: Refactor, prepare for iteration counts 2021-02-26 01:08:21 +01:00
8ae0573c0f Sculpt IPMask: Unify function to apply delta steps 2021-02-26 00:50:25 +01:00
be0194ef3e Sculpt IPMask: Remove debug prints 2021-02-26 00:47:45 +01:00
0a2ca5860f Sculpt IPMask: Implement automasking for the filter 2021-02-26 00:46:52 +01:00
151cf0b9d9 Sculpt IPMask: Implement filter selection 2021-02-26 00:43:46 +01:00
12cb22d5f6 Sculpt IPMask: Multithreaded compute step 2021-02-26 00:25:06 +01:00
5c2bb6f93a Sculpt IPMask: Implement undo 2021-02-26 00:14:11 +01:00
2e99348da7 Sculpt IPMask: Generalize compute function 2021-02-26 00:08:25 +01:00
817ccc3a99 Sculpt IPMask: Implement smooth and sharpen callbacks 2021-02-26 00:03:44 +01:00
a429f73d2b Sculpt IPMask: Fix bugs in stack index, start refactor 2021-02-25 23:52:41 +01:00
5898cc25c9 Sculpt IPMask: First working version 2021-02-25 23:09:56 +01:00
c05c2c3fb7 Merge branch 'master' into sculpt-dev 2021-02-25 22:24:03 +01:00
f55c39a974 Sculpt IPMask: Functions to step back/forward in the stack 2021-02-25 18:24:09 +01:00
ebf94179b5 Sculpt IPMask: Dummy function to compute a step 2021-02-24 20:48:12 +01:00
9e5a219f3f Sculpt IPMask: delta steps struct and packing 2021-02-24 20:10:34 +01:00
76de0eeb95 Sculpt: Add mask filter tool and target step count 2021-02-24 20:00:02 +01:00
773c5c7464 Sculpt: Initial commit for Mask Filter tool with interactive preview
Hopefully this will become a mask filter that can grow/shrink and smooth/
sharpen any mask shape interactively by dragging the pen, like the rest
of the filters.
2021-02-24 16:50:24 +01:00
7abdfb5c2a Merge branch 'master' into sculpt-dev 2021-02-24 16:23:38 +01:00
ad743c056f Fix crash in versioning code 2021-02-23 23:27:51 +01:00
05bb102a30 Sculpt: Add global smooth strength factor for alt-smooth 2021-02-23 22:52:40 +01:00
b37fd7a14b Sculpt: Add extra options to Face Set pie menu 2021-02-23 22:36:43 +01:00
4154943eef Merge branch 'master' into sculpt-dev 2021-02-23 18:58:53 +01:00
5d7f7b7065 Sculpt: Initial support for bend deformations in Pose 2021-02-23 17:33:21 +01:00
09e394583e Sculpt Expand: Silence warning 2021-02-21 19:33:53 +01:00
7baa3c7df3 Sculpt Expand: Fix artifacts in sculpt geodesic distances 2021-02-21 19:33:32 +01:00
06a8b84792 Merge branch 'master' into sculpt-dev 2021-02-21 17:57:56 +01:00
343950f0c0 Merge branch 'master' into sculpt-dev 2021-02-21 17:28:13 +01:00
da0ef1daac Merge branch 'master' into sculpt-dev 2021-02-19 22:24:49 +01:00
2eb35c7cc0 Merge branch 'master' into sculpt-dev 2021-02-17 19:21:38 +01:00
13952ecec1 Merge branch 'master' into sculpt-dev 2021-02-17 18:02:01 +01:00
214e802f5f Sculpt Expand: More comment on ExpandCache 2021-02-17 17:50:18 +01:00
697e6e53b8 Sculpt Expand: Improve and fix Face Set snapping whit Face Sets 2021-02-17 17:44:00 +01:00
64c0f1fc3f Sculpt Expand: ExpandCache comments 2021-02-17 17:40:14 +01:00
ca5ccb52e5 Sculpt Expand: Expose normals expand in the keymap 2021-02-17 17:39:50 +01:00
51e90a0ada Sculpt Expand: Fix normals falloff 2021-02-17 16:50:41 +01:00
822bbb19f1 Merge branch 'master' into sculpt-dev 2021-02-17 16:35:48 +01:00
a35da15228 Merge branch 'master' into sculpt-dev 2021-02-17 16:35:31 +01:00
53874b413e Sculpt Expand: More comments 2021-02-17 00:17:49 +01:00
1b029b790b Sculpt Expand: Refactor, more comments 2021-02-16 21:03:34 +01:00
acd99f12e7 Sculpt Expand: Cleanup, comments 2021-02-16 20:38:06 +01:00
1a11ac5e34 Sculpt Expand: rename texture options to distortion 2021-02-15 19:41:23 +01:00
416afcb1c3 Sculpt: Move geodesic distances code to its own file 2021-02-15 19:11:49 +01:00
3539cbc497 Sculpt Expand: Fix face set snapping with Multires 2021-02-15 19:07:59 +01:00
4288b2a1d1 Sculpt Expand: Fix crash when editing an existing face set with Multires 2021-02-15 18:56:58 +01:00
d4548fe55b install_deps: Bump OpenEXR to 2.5.5 version. 2021-02-15 18:29:47 +01:00
7b360abb18 Cleanup: Remove code duplication (merge error) 2021-02-15 18:29:47 +01:00
b30294ba41 Cleanup: clang tidy 2021-02-15 18:29:47 +01:00
50f1bd6f09 Cleanup: Spelling in function name 2021-02-15 18:29:47 +01:00
Peter Fog
83d530688b FFmpeg: Improve multi-threading settings for VSE proxies
Following code from D8627 this patch corrects multi threaded processing
of proxies, where a 60 sec proxy generation drops to 35 sec.

Differential Revision: https://developer.blender.org/D8659
2021-02-15 18:29:47 +01:00
b790b57fe1 FFmpeg: Improve multi-threading settings
Allow use all system threads for frame encoding/decoding. This is very
straightforward: the value of zero basically disables threading.

Change threading policy to slice when decoding frames. The reason for
this is because decoding happens frame-by-frame, so inter-frame threading
policy will not bring any speedup.

The change for threading policy to slice is less obvious and is based on
benchmark of the demo files from T78986. This gives best performance so
far.

Rendering the following file went down from 190sec down to 160sec.

  https://storage.googleapis.com/institute-storage/vse_simplified_example.zip

This change makes both reading and writing faster. The animation render
is just easiest to get actual time metrics.

Differential Revision: https://developer.blender.org/D8627
2021-02-15 18:29:47 +01:00
14fe422b48 CMake/Deps: upgrade USD 20.05 → 21.02
USD version 21.02 includes two of the changes Blender used to patch in,
which have now been removed from `usd.diff`. Unfortunately 21.02
introduces another issue where LZ4 symbols are accidentally exported,
causing linker errors. Fortunately these symbols are only used for
resting, so I added a patch hunk that simply removes their `extern "C"`
declaration.

The LZ4 linker issue has been reported upstream at
https://github.com/PixarAnimationStudios/USD/issues/1447.

Reviewed By: sebbas, mont29

Differential Revision: https://developer.blender.org/D10367
2021-02-15 18:29:47 +01:00
0af8ddab2d CMake/Deps: OpenImageDenoise 1.2.3 → 1.3.0 2021-02-15 18:29:47 +01:00
Ray molenkamp
79ecc59f0b CMake/Deps: XR_OpenXR v1.0.14
Straight up version bump

Things of note:

They started using API calls only available in windows 8, however given
the Python 3.9 update will forcibly bump us to 8.1+ anyhow this is not
an issue.

Will require some minor tweaks to platform_win32.cmake after adding the
libs to svn which are not included in this diff so this diff can land
without having to have the libs in place yet.

Reviewed By: sebbas, sybren

Differential Revision: https://developer.blender.org/D10349
2021-02-15 18:29:47 +01:00
Ray molenkamp
0ac4824d07 CMake/Deps: OpenVDB 8.0.1
Straight forward version bump, some of the variables to detect a static
OpenEXR changed and the folder structure seemingly changed a little
requiring updates to the diff

Reviewed By: sebbas, sybren

Differential Revision: https://developer.blender.org/D10340
2021-02-15 18:29:47 +01:00
5f8d444a76 CMake/Deps: Alembic 1.7.16
Version bump + no longer using Boost.

Building Alembic with Boost gave compiler errors, and having one less
inter-dependency is good as well.

Reviewed By: sebbas, sybren

Differential Revision: https://developer.blender.org/D10329
2021-02-15 18:29:47 +01:00
Ray molenkamp
42c5fdeff4 CMake/Deps: TBB 2020U2
Straight forward version bump.

2020U2 is significantly louder in the deprecated header usage warning
department, we should probably see if we need to act on this: P1949

Differential Revision: https://developer.blender.org/D10359
2021-02-15 18:29:47 +01:00
Ray molenkamp
54801f695c CMake/Deps: Boost 1.73
Just a simple version bump.

Maniphest Tasks: T85365

Differential Revision: https://developer.blender.org/D10314
2021-02-15 18:29:47 +01:00
39ac38c25f Attributes: return refined Attribute from attributes.new method
Previously `mesh.attributes.new(...)` would return a generic attribute that
one could not do much with. Now it returns refined attributes like `FloatAttribute`.
2021-02-15 18:29:47 +01:00
834d3e15ce Fix T71960: Malformed .bmp files lead to crash
Add a boundary check, avoiding access past actual data.

Ideally would need to report error to the user somehow,
but it doesn't seem to be easy to do.

This is a minimal safe patch. The proper complete fix is
being worked on by Jesse.

Differential Revision: https://developer.blender.org/D10357
2021-02-15 18:29:47 +01:00
b3c00f97be Cleanup: Use enum class for CryptomatteLayerState. 2021-02-15 18:29:47 +01:00
6324e9bae8 Cleanup: Grammar in comments 2021-02-15 18:29:47 +01:00
bbdfd0cabe Fix: incorrect versioning for Attribute Randomize node 2021-02-15 18:29:47 +01:00
6ef557fdf1 Cleanup: unused variable 2021-02-15 18:29:47 +01:00
30897a56e7 Fix T85633: Misspelling of "neighborhood" in description 2021-02-15 18:29:47 +01:00
eb18eb709d Merge branch 'master' into sculpt-dev 2021-02-14 21:20:30 +01:00
fc7c57f4a1 Sculpt Expand: Improve support for Multires and dyntopo 2021-02-14 20:02:24 +01:00
e3fdfcc981 Sculpt Expand: Fix propagation from grids to faces in Multires 2021-02-14 19:53:30 +01:00
e9b6848fe7 Sculpt Expand: Limited support for Multires 2021-02-14 19:50:59 +01:00
38e61c04ea Sculpt Expand: Fix texture strength when switching falloffs 2021-02-14 19:12:48 +01:00
5e56eb8ca2 Sculpt Expand: Make texture strength consistent across falloff modes 2021-02-14 19:09:07 +01:00
ccfc8aacf8 Sculpt: Add option to show the sculpt pivot as part of the cursor 2021-02-14 18:17:29 +01:00
edece9e741 Sculpt Expand: Update Keymap descriptions 2021-02-14 18:07:27 +01:00
6ee1db0d67 Sculpt Expand: Fix wrong expandcache ref when finishing expand 2021-02-14 18:06:43 +01:00
30434e3904 Sculpt Expand: Render the origin of expand as part of the cursor 2021-02-14 17:45:30 +01:00
e45986291d Merge branch 'master' into sculpt-dev 2021-02-14 17:42:08 +01:00
eb6e96006e Cleanup: clang format 2021-02-12 18:47:23 +01:00
9047bf89fa Sculpt Expand: Fix max falloff with texture distorsion 2021-02-12 18:42:14 +01:00
63ef98d056 Sculpt Expand: Support for texture distorsion for mask and colors 2021-02-12 18:34:18 +01:00
bf53622eef Merge branch 'master' into sculpt-dev 2021-02-12 17:57:50 +01:00
f419d4625a Sculpt Expand: Fix and improve behaviour of pivot reposition 2021-02-11 20:32:25 +01:00
ae19d5303a Sculpt Expand: Fix Face Set ID constantly updated when moving 2021-02-11 20:05:29 +01:00
35f883b886 Cleanup: clang format 2021-02-11 20:02:15 +01:00
7d52658d84 Sculpt Expand: Use symmetry and component filtering for pivot reposition 2021-02-11 20:01:58 +01:00
974b50d16b Scupt Expand: Remove unused Face Sets update code 2021-02-11 18:55:49 +01:00
55382bcdd9 Merge branch 'master' into sculpt-dev 2021-02-11 18:55:15 +01:00
51c931ebb5 Fix depth PBVH raycast with grids 2021-02-11 18:48:14 +01:00
0b9aaaf97f Sculpt Expand: Force all enabled when cursor not over the mesh 2021-02-11 18:47:48 +01:00
faccaee6c3 Fix cloth filter not working after merge 2021-02-11 17:54:54 +01:00
a56fb568ed Cleanup: Clang format 2021-02-10 21:09:41 +01:00
c365e0c6b6 Merge branch 'master' into sculpt-dev 2021-02-10 21:08:52 +01:00
0d6deb7e65 Merge branch 'master' into sculpt-dev 2021-02-10 18:04:45 +01:00
50eed6e54f Merge branch 'master' into sculpt-dev 2021-02-10 01:45:53 +01:00
147cddb398 Merge branch 'master' into sculpt-dev 2021-02-10 00:35:07 +01:00
88c4684466 Sculpt expand: Fix crash when missing connected components 2021-02-09 20:18:02 +01:00
77a30a2886 Sculpt: fix pose brush flood fill with symmetry 2021-02-09 00:30:14 +01:00
370e2b5d82 Sculpt Expand: Force expand to work only on active connected components 2021-02-08 20:27:09 +01:00
44dc741e89 Fix crash in versioning code 2021-02-08 19:02:23 +01:00
7a8cf94170 Sculpt Expand: Fix keymap entry for snapping 2021-02-08 19:01:55 +01:00
fd28c19d09 Scupt: Fix failed scene projection with ortographic views 2021-02-08 18:59:26 +01:00
2f5eabf859 Sculpt: Fix wrong vertex normals in Scene Project 2021-02-08 18:42:43 +01:00
1a17c578a8 Merge branch 'master' into temp_bmesh_multires 2021-02-07 23:17:09 -08:00
56e1ae1afa * Yet another attempt at writing a faster distance to tri function.
I think I will end up writing a less accurate version and be done
  with it.  This is a consistent hotspot in profiling.
* Fixed a few more undo bugs
2021-02-07 23:01:09 -08:00
eea78bd112 PBVH depth: Improve detection with normal orientation in trim 2021-02-07 19:33:35 +01:00
d927f54da6 Merge branch 'arcpatch-D9622' into sculpt-dev 2021-02-07 19:18:46 +01:00
Pablo Dobarro
ec8d1b4eae Sculpt: Location option for lasso trim and depth info
This adds a location option to the trim lasso tool to position the shape
in the middle of the volume of the object instead of in the surface
under the cursor.

{F9349724}

In order to make this work, the SCULPT_cursor_geometry_info_update can
now also get the depth of the geometry from the raycast. The depth is
calculated as the second further intersecting triangle in the raycast
over the entire mesh. This information will also be used to improve and
create new tools.

Differential Revision: https://developer.blender.org/D9622
2021-02-07 19:12:37 +01:00
4e4c1f40e0 Sculpt Expand: Use visibility state for enabled values 2021-02-07 18:54:13 +01:00
35ac8cf4dc Sculpt Expand: Fix gradient when using invert 2021-02-07 17:59:15 +01:00
2f2f14ffed Sculpt Expand: Enable gradient when enabling brush gradient 2021-02-07 17:53:38 +01:00
03778b787e Merge branch 'master' into sculpt-dev 2021-02-07 17:49:48 +01:00
f7e062ef3e Sculpt: fix pose and expand floodfill initial vertex 2021-02-07 17:45:28 +01:00
23cf808585 Sculpt: Boundary circle deformation mode 2021-02-07 17:42:51 +01:00
8e8845786f Cleanup: clang format 2021-02-05 21:59:21 +01:00
3ed1a99dd2 Fix elastic deform surface falloff crashing or breaking geometry 2021-02-05 20:44:45 +01:00
0a9bc6944c Rename random per component to random per loose part 2021-02-05 20:33:38 +01:00
ec4e2f32c8 Fix const warning 2021-02-05 20:31:56 +01:00
2ea03ff860 Remove unused variable 2021-02-05 20:31:18 +01:00
9a3f32f80f Fix crash in versioning code 2021-02-05 20:30:54 +01:00
bcf2f30878 Merge branch 'master' into sculpt-dev 2021-02-05 20:23:18 +01:00
23312a6994 Merge branch 'master' into sculpt-dev 2021-02-04 19:06:15 +01:00
cf5a96aa8d Merge branch 'master' into sculpt-dev 2021-02-04 16:46:45 +01:00
7ed64d9430 Merge branch 'master' into sculpt-dev 2021-02-03 21:08:28 +01:00
1cc0f8cf75 Sculpt Expand: Enable repeat events for loop count increase/decrease 2021-02-03 19:26:18 +01:00
b4e90b6e61 Sculpt Expand: fix topology recursion and flood fill 2021-02-03 18:49:03 +01:00
112c67f688 Merge branch 'master' into sculpt-dev 2021-02-03 17:03:50 +01:00
19fa6fd9f4 Cleanup: Clang format 2021-02-03 01:26:24 +01:00
88921150ff Sculpt: Init Face Sets by Face Sets boundary 2021-02-03 01:25:47 +01:00
e973c94036 Sculpt Expand: Add Expand to the keymap by default 2021-02-03 01:12:11 +01:00
b40f1f4c79 Sculpt Expand: Fix multiple crashes when using modifiers 2021-02-03 00:36:54 +01:00
c191344efb Sculpt Expand: Fix modal keymap descriptions 2021-02-03 00:10:28 +01:00
debf4fc7c9 Sculpt Expand: Refactor and pivot reposition 2021-02-02 23:48:06 +01:00
a60aca00bb Sculpt Expand: Implement topology diagonals falloff 2021-02-02 23:06:18 +01:00
763f96526e Sculpt Expand: implement restore, undo and cancel 2021-02-02 22:44:44 +01:00
abfd14817f Merge branch 'master' into sculpt-dev 2021-02-02 22:05:49 +01:00
529cffd804 Merge branch 'master' into sculpt-dev 2021-02-02 16:08:54 +01:00
09bb5e3aa0 Merge branch 'master' into sculpt-dev 2021-02-01 19:34:38 +01:00
5fb15545c1 Merge branch 'master' into sculpt-dev 2021-01-30 18:15:24 +01:00
a2df63e996 Merge branch 'master' into sculpt-dev 2021-01-29 22:44:44 +01:00
ccaba71fdf Merge branch 'master' into sculpt-dev 2021-01-28 01:16:42 +01:00
710c72807f Merge branch 'master' into sculpt-dev 2021-01-27 20:58:23 +01:00
e779eab729 Sculpt: Implement elastic surface falloff 2021-01-26 20:51:53 +01:00
a72cf784a7 Merge branch 'master' into sculpt-dev 2021-01-26 20:30:39 +01:00
78dffe2f16 Fix Face Set init not showing in the menus 2021-01-25 21:06:36 +01:00
9d31943d88 Merge branch 'master' into sculpt-dev 2021-01-25 20:05:56 +01:00
0a2b19e96d Merge branch 'master' into sculpt-dev 2021-01-24 20:23:54 +01:00
46582b3743 Merge branch 'master' into sculpt-dev 2021-01-24 03:04:27 +01:00
c10b19e453 Merge branch 'master' into sculpt-dev 2021-01-20 18:56:37 +01:00
5631bd4f94 Cleanup: Clang format 2021-01-20 18:51:49 +01:00
30b6d7826d Sculpt: Reset all brushes operator 2021-01-20 18:48:34 +01:00
d1b2c55322 Sculpt Expand: Brush Gradients 2021-01-20 18:17:19 +01:00
87ae49c78e Sculpt Expand: Expose geodesic and topology recursions 2021-01-19 00:14:10 +01:00
2765fb6c76 Sculpt Expand: Loops and recursion types 2021-01-18 23:28:49 +01:00
65cea9523e Merge branch 'master' into sculpt-dev 2021-01-18 22:26:46 +01:00
3d311fdaf1 Sculpt Expand: Expand from active 2021-01-16 21:21:16 +01:00
7a0d5e0c9e Sculpt Expand: Expand from face set boundary 2021-01-16 21:05:16 +01:00
e2fb2315fd Cleanup: Clang Format 2021-01-16 20:41:33 +01:00
fc1fc95b79 Sculpt Expand: Modify Active Face Set 2021-01-16 20:41:10 +01:00
05703d630e Sculpt Expand: face set selection snapping 2021-01-16 20:11:37 +01:00
65c358851d Sculpt Expand: Recursivity, move and falloff switching 2021-01-16 18:38:05 +01:00
b997fc5ea1 Sculpt Expand: Initial implementation of face falloff factors 2021-01-16 01:54:36 +01:00
869747e527 Sculpt Expand: Initial face sets expand target 2021-01-16 01:19:56 +01:00
df7857a364 Sculpt Expand: vertex colors target 2021-01-16 00:42:26 +01:00
cd0cf0a5b7 Expand: Support for invert, preserve and mask gradients 2021-01-15 23:37:42 +01:00
abf7518cbf Sculpt expand modal keymap 2021-01-15 22:24:12 +01:00
58406eddb5 Merge branch 'master' into sculpt-dev 2021-01-15 19:47:28 +01:00
97ab225428 Merge branch 'master' into sculpt-dev 2021-01-14 17:56:31 +01:00
24bbe05913 Add automatic falloff type switching to expand 2021-01-14 02:03:49 +01:00
81ac9f61b6 Implement boundary expand 2021-01-14 01:40:32 +01:00
a9f6ab7357 Basic mask expand operation working 2021-01-14 01:05:11 +01:00
df9a5825e1 Initial commit for new generic sculpt expand operator 2021-01-13 22:59:59 +01:00
0c8b9cd7d1 Cleanup: Clang format 2021-01-13 20:28:09 +01:00
142aba9485 Sculpt: Grab brush surface falloff 2021-01-13 20:27:51 +01:00
bf632990e0 Merge branch 'master' into sculpt-dev 2021-01-13 18:53:20 +01:00
3b4a9b2319 Merge branch 'master' into sculpt-dev 2021-01-12 19:43:30 +01:00
38dce1d29d Merge branch 'master' into sculpt-dev 2021-01-09 17:56:44 +01:00
deeb0b3aac Add experimental ripple effects option to the cloth solver 2021-01-06 20:26:38 +01:00
237b1f9d75 Merge branch 'master' into sculpt-dev 2021-01-06 17:34:16 +01:00
11cd86a8d2 Fix crash in fairing and memory optimization 2021-01-06 17:34:01 +01:00
27713d65d1 Merge branch 'master' into sculpt-dev 2021-01-05 23:49:10 +01:00
d37d96437e Fix crash with scene project and dyntopo 2021-01-05 23:32:29 +01:00
940fb3c044 Sculpt Scene Project: Add option to use vertex normals and brush normal 2021-01-05 22:47:22 +01:00
f91a2a0831 Sculpt: add option for use the center of mass to the sphere mesh filter 2021-01-05 21:59:15 +01:00
6d6ab057e4 Add missing random mask init menu 2021-01-05 21:27:59 +01:00
ea12ba5441 Merge branch 'master' into sculpt-dev 2021-01-05 21:03:21 +01:00
601d896fc6 Fix crash with custom input curves versioning 2021-01-05 18:12:32 +01:00
c237a48104 Fix warnings and windows build 2021-01-05 17:53:50 +01:00
29078186f8 Merge branch 'master' into sculpt-dev 2021-01-05 17:52:46 +01:00
7754c4f10f Fix windows build 2021-01-04 21:22:45 +01:00
90931d6de9 Sculpt: Update clay strips presets with custom input curves 2021-01-01 21:47:18 +01:00
4785f80d51 Fix multiple crashes with custom input curves 2021-01-01 20:12:46 +01:00
5136cc701f Merge branch 'master' into sculpt-dev 2021-01-01 17:18:40 +01:00
ef9eb626a8 Sculpt: Initial support for custom pressure input curves 2020-12-31 19:48:25 +01:00
0663576d19 Tweak draw sharp preset to include size-pressure 2020-12-31 18:36:39 +01:00
e1b8577e41 Merge branch 'master' into sculpt-dev 2020-12-31 18:30:27 +01:00
e09d9c86b6 Merge branch 'master' into sculpt-dev 2020-12-30 18:17:24 +01:00
52e99c5639 Merge branch 'master' into sculpt-dev 2020-12-29 16:13:12 +01:00
193806f590 Merge branch 'master' into sculpt-dev 2020-12-28 22:41:46 +01:00
a3e4f9d9a6 Merge branch 'master' into sculpt-dev 2020-12-28 14:21:15 +01:00
482f46a35a Change default cloth grab deform constraints strength 2020-12-27 00:41:45 +01:00
975a17c816 Sculpt: Auto detect the sphere radius in the sphere mesh filter 2020-12-27 00:14:57 +01:00
985c6464e2 Sculpt: Edit face set extrude operation 2020-12-26 20:11:19 +01:00
bca07d9039 Merge branch 'master' into sculpt-dev 2020-12-26 17:52:31 +01:00
35092510ba Merge branch 'master' into temp_bmesh_multires 2020-12-26 04:24:54 -08:00
85842851d5 Fix build 2020-12-25 21:54:14 +01:00
61ec4159c8 Implement radial gradient 2020-12-25 21:51:21 +01:00
c4ebefc1c8 Cleanup: Clang format 2020-12-25 21:36:58 +01:00
eb4f2f1548 Sculpt: Base code for gradient tools 2020-12-25 21:36:31 +01:00
47b4b45011 Optimize fairing weights calculation 2020-12-25 17:25:00 +01:00
7547e28ba4 Merge branch 'master' into sculpt-dev 2020-12-25 17:09:01 +01:00
0b2e4fa52e Cleanup: Clang format 2020-12-24 19:58:09 +01:00
f4900b3d02 Sculpt: Smooth Directional deform mode 2020-12-24 19:57:58 +01:00
1d53fbfd06 Merge branch 'master' into sculpt-dev 2020-12-24 19:30:30 +01:00
c203de25db Merge branch 'master' into sculpt-dev 2020-12-24 16:30:33 +01:00
ac8c589b7c Preparte curvature fairing and Fairing edit mode operator 2020-12-24 16:30:12 +01:00
f32d3f7b06 Merge branch 'master' into sculpt-dev 2020-12-23 19:23:03 +01:00
5385455e6a Fixed undo bugs caused by SculptVertRef refactor. I need to figure out
the right way to make assigning a pointer to a SculptVertRef to an int*
spit out a compiler error.
2020-12-22 22:47:50 -08:00
1bf726f1ad Merge branch 'master' into sculpt-dev 2020-12-22 23:59:48 +01:00
f031000b28 Sculpt: Mask Init operator and random init modes 2020-12-22 23:59:25 +01:00
2c54c641a3 Added a new cavity automasking mode for sculpt colors painting.
In theory it should also work with other sculpt tools (the
automasking code is fairly general) though it doesn't seem
to do much.
2020-12-22 14:11:11 -08:00
554be8d96c Merge branch 'master' into sculpt-dev 2020-12-22 16:30:14 +01:00
f969b3d440 Merge branch 'master' into sculpt-dev 2020-12-21 23:44:17 +01:00
6034a5bd5b Merge branch 'master' into sculpt-dev 2020-12-21 21:17:51 +01:00
eea756e513 Sculpt: Scene Project Brush 2020-12-21 21:16:16 +01:00
ecbf3e2d0f Sculpt: Face Set Edit fill component mode 2020-12-21 20:00:56 +01:00
6fa6a8851f Merge branch 'master' into sculpt-dev 2020-12-21 16:19:03 +01:00
b3fff9b07a Merge remote-tracking branch 'origin/master' into temp_bmesh_multires
Also redid vcol boundary tool icon, and made a little icon for sculpt
color paint.
2020-12-21 01:08:24 -08:00
671ef8f0e9 Merge branch 'master' into sculpt-dev 2020-12-20 16:10:56 +01:00
373b9b4481 Merge branch 'master' into sculpt-dev 2020-12-19 21:00:53 +01:00
0ada9c1638 Sculpt: Improve boundary artifacts in fairing mask with line gestures 2020-12-19 17:46:23 +01:00
31158ef0c9 Merge branch 'master' into sculpt-dev 2020-12-19 17:00:44 +01:00
5fbd436bb1 Merge branch 'master' into sculpt-dev 2020-12-18 20:10:58 +01:00
c0cb475689 Merge branch 'master' into sculpt-dev 2020-12-17 22:28:12 +01:00
b8a0455983 Fix crash in combine proxies 2020-12-17 19:24:45 +01:00
6145a6bc6c Merge branch 'master' into sculpt-dev 2020-12-17 17:48:12 +01:00
5c03d0c30c Fix plane falloff with elastic cloth 2020-12-16 21:09:45 +01:00
6b42781319 Merge branch 'master' into sculpt-dev 2020-12-16 21:02:04 +01:00
6c4a9b83ff Fix elastic drag cloth brush deformation 2020-12-16 20:59:20 +01:00
9453b45e20 Sculpt: Change default damping for cloth deform target 2020-12-16 20:52:52 +01:00
c0f19d6082 Sculpt: Cloth Brush elastic drag deform type 2020-12-16 20:49:02 +01:00
06078a6f92 Merge branch 'master' into sculpt-dev 2020-12-16 20:27:13 +01:00
1199b9e678 Merge branch 'master' into sculpt-dev 2020-12-16 19:10:48 +01:00
92bc9477d1 Merge branch 'master' into sculpt-dev 2020-12-16 16:43:09 +01:00
7bc1657d05 Fix cloth sim artifacts on planes 2020-12-15 22:13:11 +01:00
e582a4dc77 Fix debug build 2020-12-15 22:07:47 +01:00
243e329ec3 Sculpt: Cloth Deform target for transform 2020-12-15 22:07:08 +01:00
5775efba2f Sculpt: Elastic Transform 2020-12-15 21:38:07 +01:00
cab8626cf2 Sculpt: lasso and box project gestures 2020-12-15 21:33:49 +01:00
19cb01aa21 Cleanup: Clang format 2020-12-15 21:15:05 +01:00
6e6828e09d Sculpt: Project gesture fairing deformation mode 2020-12-15 21:14:26 +01:00
72ff64590d Sculpt: Fairing Brush 2020-12-15 20:39:53 +01:00
553724529e Change PBVH leaf limit size 2020-12-15 20:35:29 +01:00
681e2b6134 Fixes from merge 2020-12-08 13:37:00 -08:00
49bd2228cb Merge branch 'master' into temp_bmesh_multires 2020-12-07 13:04:19 -08:00
0a140ec66e Commit before merge 2020-12-07 11:48:32 -08:00
f30225725d Some multires stuff 2020-11-18 18:34:08 -08:00
a1ac104f02 add NULL pointer check 2020-11-11 05:19:35 -08:00
d823f6e2cf Added an "Exponent" slider to Color Boundary brush. 2020-11-09 22:25:03 -08:00
3042f6e608 Tweaked value for smoothing in vcol boundary tool (a slight amount of
smoothing is applied to prevent normal discontiuties from perfectly
colinear triangles).

Also reverted some CLANG compiling stuff.
2020-11-09 18:30:24 -08:00
6115091103 View3D: take clipping into account for Frame All
Clamp the min/max used for Frame All/Selected
by the clipping region if it's set.

Resolve T81050
2020-11-07 02:54:23 -08:00
78ef2d0d84 Cleanup: move plane array intersection into a function
Also add check to ensure a point isn't occluded by it's own plane,
which could happen if a small epsilon values are passed in.
2020-11-07 02:54:23 -08:00
9d92a97562 Cleanup: Rename render texture files to texture_* 2020-11-07 02:54:23 -08:00
192670e8ce Cleanup: Remove unused variable 2020-11-07 02:54:23 -08:00
7ae1cc23fc Cleanup: remove unused includes in readfile.c and writefile.c 2020-11-07 02:54:23 -08:00
ebb6648bda Fix missing include warning
Caused by rB580ff2cb937daf43699908afe1190baea8d117aa
2020-11-07 02:54:23 -08:00
384f2956e5 Cleanup: fix naming and remove unnecessary code 2020-11-07 02:54:23 -08:00
2ba90cf717 Refactor: move Screen .blend data read to blenkernel
Ref T76372.
2020-11-07 02:54:23 -08:00
e5af8597e5 Refactor: move Ipo .blend I/O to IDTypeInfo callbacks 2020-11-07 02:54:23 -08:00
1f6a7785f6 Refactor: move Object .blend I/O to IDTypeInfo callbacks 2020-11-07 02:54:23 -08:00
0ee053fb12 Cleanup: Clang-Tidy modernize-use-nullptr
Replace `NULL` with `nullptr` in C++ code.

No functional changes.
2020-11-07 02:54:20 -08:00
8cd4776764 Clang-Tidy: error out when executable not found
Stop with an error when the Clang-Tidy executable cannot be found.

Without this check, CMake will happily report "Found Clang-Tidy" but with
the fallback version (0, 0, 0), when `CLANG_TIDY_EXECUTABLE` points to a
non-existing executable.
2020-11-07 02:53:25 -08:00
f32bde6bd9 Refactor: move Pose .blend I/O to blenkernel
Ref T76372.
2020-11-07 02:53:25 -08:00
Yevgeny Makarov
8b808cefae UI: Improved macOS Application Icon Progress Bar
Nicer appearance for the progress bar that is drawn over the application icon during long processes on macOS.

Differential Revision: https://developer.blender.org/D9398

Reviewed by Brecht Van Lommel
2020-11-07 02:53:24 -08:00
f8466b6284 Refactor: move Constraint .blend I/O to blenkernel
Ref T76372.
2020-11-07 02:53:24 -08:00
4a2d7a2f89 Refactor: move MotionPath .blend I/O to blenkernel
Ref T76372.
2020-11-07 02:53:24 -08:00
67defe866b Fix T81997: Subsurf Optimal Display sticks after object conversion
When using Optimal Display, some edges are not flagged `ME_EDGEDRAW` |
`ME_EDGERENDER`.
When the modifier is applied through the UI in the modifier stack this is
not an issue because the `modifyMesh` callback is run with
`MOD_APPLY_TO_BASE_MESH` (this will effectively turn of Optimal
Display).
When converting to mesh though, this will just get an evaluated mesh
(where the edge flags are still the same as with the subdivision
modifier).
Now ensure every edge is flagged to draw after conversion.

Maniphest Tasks: T81997

Differential Revision: https://developer.blender.org/D9331
2020-11-07 02:53:24 -08:00
b6575fed92 Refactor: move gpencil modifier .blend I/O to blenkernel
Ref T76372.
2020-11-07 02:53:24 -08:00
d6735bfae0 Refactor: move modifier .blend I/O to blenkernel
Ref T76372.
2020-11-07 02:53:24 -08:00
e4c45d0c5e Fix T82251: Outliner Material Drag&Drop missing tree update
Caused by rBb077de086e14.

Not entirely sure why this was rebuilding the tree prior to above
commit, but sending an ND_OB_SHADING notifier is appropriate (and also
what the Outliners listener listens to).

Maniphest Tasks: T82251

Differential Revision: https://developer.blender.org/D9396
2020-11-07 02:53:24 -08:00
c4668b750c Fix T82220 Missing viewport update after manual "HDRI Preview Size" input
This is caused by the TAA being reset after the init phase, leading to
1 sample being kept as valid when it is clearly not.

To fix this, we run the lookdev validation before TAA init.

Reviewed By: Jeroen Bakker

Differential Revision: https://developer.blender.org/D9452
2020-11-07 02:53:24 -08:00
81a85233a7 Cleanup: Clang-Tidy, modernize-make-unique 2020-11-07 02:53:24 -08:00
89f62e5dc3 Cleanup: Sort includes after recent render module cleanup 2020-11-07 02:53:23 -08:00
408e13783d Cleanup: remove unused functions 2020-11-07 02:53:23 -08:00
3900734063 Refactor: move ShaderFx .blend I/O to blenkernel
Ref T76372.
2020-11-07 02:53:23 -08:00
fa688ac593 Cleanup: Render Module: combine intern/ source & include 2020-11-07 02:53:23 -08:00
787f952ef4 Cleanup: whitespace 2020-11-07 02:53:23 -08:00
63241d25b9 Cleanup: Render Module: move header files to main directory
Move headers files from `render/extern/` to `render/`

Part of T73586
2020-11-07 02:53:23 -08:00
Paul Melis
98166b5c98 Add background rectangle option to video sequencer Text strip
This adds a Box option to the Text strip's style properties, plus related Box Margin value:

{F9208309}

When enabled the text is placed on top of a solid-filled rectangle of a chosen color, as shown below:

{F9208324}

When the box option is disabled the text strip works the same as it does now. When the box option is enabled the meaning of the Shadow option changes to provide a drop-shadow on the rectangle (and not on the text itself). The latter made more sense to me.

The box margin is specified as a fraction of the image width. The offset of the drop-down box shadow is fixed to a specific fraction of the image width as well.

I tested this feature on a movie of a couple of minutes containing dozens of text strips (all with box background), edge cases like multi-line strings and text overlapping the image edges.

Reviewed By: ISS

Differential Revision: https://developer.blender.org/D9468
2020-11-07 02:53:23 -08:00
229849af0f Refactor: move ParticleSystem .blend I/O to blenkernel
Ref T76372.
2020-11-07 02:53:23 -08:00
0a1fc855bc Sequencer: Make naming consistent in header and implementation files 2020-11-07 02:53:23 -08:00
46ccaff9a3 VSE: Don't store proxy images in cache
Proxies are expected to be fast to read. Storing them in cache has
little to no effect on performance.

This change also allows to omit invalidation of cache when user switch
between proxies and original media.

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D9473
2020-11-07 02:53:23 -08:00
9c8b0a20c1 Refactor: move Scene .blend expand to IDTypeInfo callback 2020-11-07 02:53:22 -08:00
a42cf75ecb Refactor: move Scene .blend lib reading to IDTypeInfo callback 2020-11-07 02:53:22 -08:00
d77e884bd7 Cleanup: rename time related variables
Variables renaned:
 - cfra -> timeline_frame
 - nr -> frame index
 - cfra_over -> overlap_frame

Function seq_give_stripelem_index was renamed to seq_give_frame_index.
2020-11-07 02:53:22 -08:00
e2111233f0 Blenloader: access report list via api 2020-11-07 02:53:22 -08:00
e4efd04063 Blenloader: expose BLO_reportf_wrap in api
This function is used by a couple of functions that are moved out of blenloader.
2020-11-07 02:53:22 -08:00
8edd20274e Refactor: move Scene .blend data reading to IDTypeInfo callback 2020-11-07 02:53:22 -08:00
9be60839cd Refactor: move Scene .blend writing to IDTypeInfo callback 2020-11-07 02:53:22 -08:00
af934d751d CMake: Fix wrong library used for dependency
Was causing compilation failure on fresh builds.
2020-11-07 02:53:22 -08:00
5ac07c31f0 Refactor: move PointCache .blend I/O to blenkernel
Ref T76372.
2020-11-07 02:53:22 -08:00
f3a7d9375d Cleanup: Fluid engine API return types
Use bool return type where possible instead of int (the return values from fluid object are already boolean instead of int).

Also removed several if guards in API functions. If one of the arguments is in fact invalid / nullptr (should not happen though), it better to catch them directly where they failed and not silently escape them.
2020-11-07 02:53:22 -08:00
a033a0eaaf Cleanup: Clang-Tidy, modernize-use-bool-literals 2020-11-07 02:53:22 -08:00
c137b53569 Refactor: move sequencer modifier .blend I/O to sequencer module
Ref T76372.
2020-11-07 02:53:21 -08:00
f0a9986ccf Cleanup: Alembic, simplify expression
Change `1 + current_mat++` to `++current_mat`.

No functional changes.
2020-11-07 02:53:21 -08:00
f639ffe795 Cleanup: Alembic, simplify material assignment code
Refactor material assignment code such that:
- `build_mat_map()` just returns the built map (instead of relying on
  modifying a map passed as parameter),
- `LISTBASE_FOREACH` is used to loop over a `ListBase` (instead of a
  hand-crafted for-loop),
- just `return` when not enough material slots can be created (instead
  of setting a boolean to false, then doing some useless work, then
  checking the boolean),
- reorder some code for clarity, and
- rename `mat_map` to `matname_to_material` so that the semantics are
  clearer.

No functional changes.
2020-11-07 02:53:21 -08:00
326381bfe1 Refactor: move Paint lib linking to blenkernel
Ref T76372.
2020-11-07 02:53:21 -08:00
174e3c6b65 Refactor: move color settings .blend I/O to blenkernel
Ref T76372.
2020-11-07 02:53:21 -08:00
5c899dcc92 Cleanup: Clang-Tidy, modernize-use-emplace 2020-11-07 02:53:21 -08:00
24ee80f077 Fix compilation error of bf_draw
Similar to previous commit, missing build dependency.
2020-11-07 02:53:21 -08:00
22b69af796 Fix compilation error when building from scratch
Make sure the DNA offset files is ready at a time bf_windowmanager
need it.
2020-11-07 02:53:21 -08:00
0758c006ec Cleanup: Clang-Tidy, modernize-redundant-void-arg 2020-11-07 02:53:21 -08:00
c2a329ec07 Refactor: move LightCache .blend I/O to eevee_lightcache.c
Ref T76372.
2020-11-07 02:53:21 -08:00
bdaee543f3 Refactor: move remaining ViewLayer .blend I/O to blenkernel
Ref T76372.
2020-11-07 02:53:21 -08:00
44d358c384 Cleanup: remove unnecessary function 2020-11-07 02:53:21 -08:00
f87123a6eb Refactor: move Paint .blend I/O to blenkernel
Ref T76372.
2020-11-07 02:53:21 -08:00
f2fc2cd521 Refactor: move wmWindowManager .blend I/O to IDTypeInfo callbacks 2020-11-07 02:53:20 -08:00
af92a39e37 Fix T82364: Widget anim state not updated when deleting channel in Graph Editor
So a keyframed e.g. location slider would stay yellow/green even if its
corresponding channel was removed.

Needs a appropriate notifier so the listeners (e.g.
buttons_area_listener, view3d_buttons_region_listener) would cause a
redraw.

Maniphest Tasks: T82364

Differential Revision: https://developer.blender.org/D9438
2020-11-07 02:53:20 -08:00
49cbb23855 Fix T82457: Python error when clicking on a tool in the viewport
Ths variable was initialized for false, while it was expected to be
true.
2020-11-07 02:53:20 -08:00
9cb84de509 Clang Tidy: Expand modernize category
Gives an idea of which warnings are affecting Blender code base.
2020-11-07 02:53:20 -08:00
53f6991de8 Cleanup: Use nullptr everywhere in fluid code
Switched from NULL to nullptr.
2020-11-07 02:53:20 -08:00
5b696c2bd8 Fix T82407: Negative number input gives syntax error for velocities and
accelerations

Caused by rB45dbc38a8b15.

Above commit would place parentheses surrounding a block until the next
operator was found.
For velocities and accelerations though, the '/' in 'm/s' or 'ft/s'
should not be considered an operator.

Maniphest Tasks: T82407

Differential Revision: https://developer.blender.org/D9467
2020-11-07 02:53:20 -08:00
b95a5c2e68 Fix T82387: Crash loading file saved with recent master in old versions
This fix makes sure new files save `wmWindow.global_areas` under a different
name, so old Blender versions don't recognize and 0-initialize it.

Since enabling global area writing (ef4aa42ea4), loading a file in old
Blender versions would cause `wmWindow.global_areas` to be read, because there
was already reading code for it and `ScrAreaMap` was in SDNA.
However the `ScrArea.global` of the global areas would be NULL, because it was
*not* in SDNA (`ScrGlobalAreaData` was excluded).
Now, issue is that the code assumes that areas in the global area-map have a
valid ScrArea.global pointer.

Think this was a mistake in rB5f6c45498c92. We should have cleared all this data
on reading, until the global area writing was enabled.

Differential Revision: https://developer.blender.org/D9442

Reviewed by: Brecht Van Lommel
2020-11-07 02:53:20 -08:00
080e78d205 Cleanup: Clang-Tidy, readability-redundant-member-init 2020-11-07 02:53:20 -08:00
a55a69f81a Fix Outliner editbone selection with 'Sync Selection'
When editbones were selected from the Outliner (and they were connected
to a parent) with the 'Sync Selection' option turned ON, they could not
get duplicated.

For duplication to work, the (connected) parent bone's tip also has to
be selected [which was not the case when selection is done from the
Outliner under above circumstances]. The reason being that
armature_duplicate_selected_exec ->
ED_armature_edit_sync_selection clears the BONE_ROOTSEL flag if the
parent bone's BONE_TIPSEL is not set.

Caused by rB71eb65328078 btw.

The correct "parent-tip-selection" would actually happen in activation
- `tree_element_active_ebone`
-- `tree_element_active_ebone__sel`
but for 'Sync Selection' this happens [also] in
- `outliner_sync_selection_from_outliner`
-- `outliner_select_sync_to_edit_bone`
which did not do the "flushing" to the parent bone's tip

Now use existing dedicated function for this.

ref. T82347

Reviewers: Zachman

Differential Revision: https://developer.blender.org/D9470
2020-11-07 02:53:20 -08:00
596fea8afd Cleanup: Clang-Tidy warnings 2020-11-07 02:53:19 -08:00
47e4adde5a Fix compilation error in GHOST
ELEM macro isn't available in GHOST library.
2020-11-07 02:53:19 -08:00
a349943cd7 Cleanup: Fix the order of info_cfg_option. 2020-11-07 02:53:19 -08:00
360faf5c3c Cleanup: add missing doxygen group 2020-11-07 02:53:19 -08:00
9cf97857d1 Cleanup: Fix wrong doxygen groups
Was missed in rB9b6088cb9da4df1a893361997fc1a22986bf6f2e
2020-11-07 02:53:19 -08:00
44bdf515ba CMake: configue_file() to pass strings for build-info
Using configue_file(..) would have avoided the breakage from
1daa3c3f0a, caused by buildinfo not properly escaping quotes.

Rely on CMake to escaping strings instead using configure_file().
2020-11-07 02:53:19 -08:00
45ccdebad3 Fix uninitialized value
Own mistake in rB74188e65028d268af887ab2140e4253087410c1e
2020-11-07 02:53:19 -08:00
d242d33571 Cleanup: use 'BKE_' prefix for initialization functions 2020-11-07 02:53:19 -08:00
731eaa68d3 Cleanup: use doxy sections for node_group.c 2020-11-07 02:53:19 -08:00
344963bf23 Cleanup: use if/else check for property poll 2020-11-07 02:53:19 -08:00
aaca5731fb Cleanup: replace STREQLEN with STRPREFIX for constant strings 2020-11-07 02:53:19 -08:00
aa7147f665 Cleanup: use STR_ELEM macro 2020-11-07 02:53:19 -08:00
0fb50afdc5 Cleanup: Use LISTBASE_FOREACH macro 2020-11-07 02:53:18 -08:00
f3cc74d216 Cleanup: Use descriptive variable names 2020-11-07 02:53:18 -08:00
0946a8da3b Cleanup: de-duplicate code for instancing objects when linking
Ref D8843
2020-11-07 02:53:18 -08:00
a8e5e63f45 Cleanup: doxygen comments 2020-11-07 02:53:18 -08:00
e52f8ac7ce Cleanup: doxygen comments in ghost
Use colon after parameters, use hash to reference symbols.
2020-11-07 02:53:18 -08:00
71a088582a Cleanup: use ELEM macro (>2 args) 2020-11-07 02:53:18 -08:00
3aab8dd731 Cleanup: clang-format
Missed this last commit.
2020-11-07 02:53:18 -08:00
56d57b2e9d Cleanup: use ELEM macro 2020-11-07 02:53:18 -08:00
b16eb9d7ad Cleanup: follow our code style for float literals 2020-11-07 02:53:17 -08:00
3fcd3d41c4 Cleanup: use bool argument in BLI_noise 2020-11-07 02:53:17 -08:00
0b0bfa0628 Cleanup: BLI_noise
Use common prefix as this collided with existing API's (eg BLI_voronoi).

Also expand some non-obvious abbreviations:

- 'g'  -> 'generic'
- 'vl' -> 'variable_lacunarity'
- 'V'  -> 'v3'
2020-11-07 02:53:17 -08:00
ff9ce5fd98 Cleanup: remove unused BLI_turbulence1
A slightly modified version of BLI_turbulence1, unused for years.
2020-11-07 02:53:17 -08:00
2d99e118b4 Cleanup: use snake case for BLI_args API 2020-11-07 02:53:17 -08:00
a93add61b0 Cleanup: remove unused BLI_argsArgv 2020-11-07 02:53:17 -08:00
7f28a99dd5 Cleanup: use string APPEND/PREPEND
Replace 'set' with 'string(APPEND/PREPEND ...)'.
This avoids duplicating the variable name.
2020-11-07 02:53:10 -08:00
9aaf9857a0 Cleanup: transform.h comments, use doxy sections 2020-11-07 02:52:23 -08:00
e2595de761 Cleanup: sort structs, files 2020-11-07 02:52:23 -08:00
40c4942901 Cleanup: clang-format 2020-11-07 02:52:23 -08:00
2e188dc504 Cleanup: unused variable 2020-11-07 02:52:23 -08:00
Yevgeny Makarov
dfce29b809 UI: Tweaks to the Warning Icon
Warning Sign Alert Icon given a more rounded border.

Differential Revision: https://developer.blender.org/D9443

Reviewed by Pablo Vazquez
2020-11-07 02:52:23 -08:00
decf7adcf6 Fix T81915: Draw Face Sets not working with deformed sculpt mesh
The draw face sets brush uses the poly center when used in meshes to increase
its precision when working in low poly geometry. For this to work with deformed
meshes, the deformed coordinates from the PBVH should be used instead.

Reviewed By: sergey

Maniphest Tasks: T81915

Differential Revision: https://developer.blender.org/D9424
2020-11-07 02:52:23 -08:00
81531d5e76 Fix memory leaks in sculpt mode trimming tools
BKE_mesh_free() seems to not free the meshes correctly, so using BKE_id_free() instead.
The looptri array was also not freed.

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D9426
2020-11-07 02:52:23 -08:00
15bc0b7d5d Fix T82400: Dyntopo detail size edit operator visual glitch
Just a missing immUnbindProgram

Reviewed By: sergey

Maniphest Tasks: T82400

Differential Revision: https://developer.blender.org/D9459
2020-11-07 02:52:23 -08:00
17dec471c5 Fix Dyntopo detail size preview orientation not matching the cursor
Used the sampled cursor normal when available instead of the raycast face normal.
This makes the preview match the previous orientation of the cursor.

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D9460
2020-11-07 02:52:23 -08:00
81f05aca29 Move "Camera Parent Lock" from preferences to Object Relations
"Camera Parent Lock" can be useful when rigging cameras, but it is not
intuitive, and has also generated a lot of confusion (bug reports).
This is because it breaks the fundamental parent <-> child relationship
conventions in Blender, and there is no indication that it's intended
without diving into the preferences.

This commit moves the setting to the object level, and exposes it in
the relations panel in the property editor. It is exposed for every
object type because any object type can be "View Locked" in the 3D view.
The property description is also updated to reflect this change and be
more specific without getting too long.

In the future this could become a more general feature of the transform
system, but for now it is limited to "Lock Camera to View".

Differential Revision: https://developer.blender.org/D9239
2020-11-07 02:52:22 -08:00
d1270925a5 Fix T82423: Add modifier key back into keymap
Commit rBf5080c82dd915db6c7b9dd68a52aaaccf2600137
accidentally remove the Shift modifier key from
the `AUTOCONSTRAINPLANE` shortcut.

Differential Revision: https://developer.blender.org/D9480
2020-11-07 02:52:22 -08:00
9e09ae7aff Fix T82164: Knife tool draws huge vertices after using bgl.glPointSize
Since it is possible to have multiple draw callbacks, (some of which
use bgl and others gpu), check and force the reset of the drawing status
at the end of each callback.

Differential Revision: https://developer.blender.org/D9476
2020-11-07 02:52:22 -08:00
Robert Guetzkow
c51a5e1066 Fix T82423: Replace duplicate name in keymap
The two entries `TFM_MODAL_AUTOCONSTRAINT` and
`TFM_MODAL_AUTOCONSTRAINTPLANE` had the same name
displayed in the UI. The latter is now includes
"plane" in it's name.

Reviewed By: mano-wii

Differential Revision: https://developer.blender.org/D9474
2020-11-07 02:52:22 -08:00
758452ffba Fix T80043: missing Cycles displacement update when relinking output sockets 2020-11-07 02:52:22 -08:00
c7cd82c356 Cleanup: Reduce variable scope
Differential Revision: https://developer.blender.org/D9475
2020-11-07 02:52:22 -08:00
cc554eea75 Fix T82428: Cycles crashes when building volume meshes
The Volume Node did not have all of the sockets from its Mesh base class
which are now required due to the recent socket API change.
2020-11-07 02:52:21 -08:00
0d770432d6 pre-merge commit 2020-11-07 02:48:34 -08:00
b2cb9d4b1b Fixed handling of edge seam flags in dyntopo, for some reason did the
code was not brought over from trimesh branch.
2020-11-06 16:23:12 -08:00
08e19c6bc0 Added debug option in dyntopo to draw sculpt colors as solid cells
instead of interpolating (it's next to "draw smooth" in the dyntopo
settings panel).
2020-11-06 03:17:45 -08:00
b928f58849 Made vcol boundary tool avoid perfectly degenerate triangles. 2020-11-06 01:35:52 -08:00
228c1e5345 Replace call to BMLog with origco CD layer.
Also fixed SCULPT_HIDE_FACE_SETS.  It was 1<<16, which overlaps
SCULPT_DYNTOPO_DETAIL_MANUAL.  Changed it to 1<<17.  Someone should do
this in master too.
2020-11-05 16:35:42 -08:00
488af21ae0 Merge remote-tracking branch 'origin' into temp_bmesh_multires 2020-11-05 09:05:02 -08:00
b54d7bfad8 Fix redraw error for dyntopo smooth brush 2020-11-05 09:03:56 -08:00
2babf80ae6 Fixed sculpt colors undo for mesh pbvh. 2020-11-04 21:27:21 -08:00
022dcb8e6c Added autosmooth-like option to run vcol boundary tool in paint tool 2020-11-04 20:36:43 -08:00
6d66e81667 Fixed color boundary tool for faces pbvh 2020-11-04 03:05:38 -08:00
91a5e1aef8 Added an icon for new sculpt colors boundary smooth tool 2020-11-04 02:34:18 -08:00
6d704d57ad Merge remote-tracking branch 'origin' into temp_bmesh_multires 2020-11-03 22:41:57 -08:00
a1b1c840a0 Adjust weighting for color boundary tool 2020-11-03 19:41:18 -08:00
901654dcbf * Added a "color boundary" tool that tries to smooth out vcol boundaries 2020-11-03 19:08:37 -08:00
37a657a45f Added face set support to dyntopo. Still need to finish "edit face
sets" tool and do more testing.
2020-11-03 16:05:38 -08:00
e07bb3955e Enabled dyntopo for sharp brush. Also fixed paint brush strength being
too strong with dyntopo enabled.
2020-11-03 00:47:19 -08:00
7834b59598 Merge remote-tracking branch 'origin' into temp_bmesh_multires 2020-11-02 14:33:52 -08:00
0c8d40d2de Fixed bug with multires editmode code messing up loop indices for draw
code.

Also fixed a bug in dyntopo ray tracing, where r_active_vertex_index
wasn't always being set.
2020-11-02 14:29:45 -08:00
1014b6f455 More fixes for sculpt vertex color undo. Moved one of the undo pushes
outside a thread, since dyntopo undo is not thread safe.
2020-10-31 11:06:15 -07:00
093e29f3c2 Tried to fix sculpt vcol paint undo. It's better, but I still need to
go through and thorougly analyze just what the undo stack is doing.
2020-10-30 20:06:05 -07:00
0f0d1f8e2a Paint brush no long SCULPT_undo_push_node per dab (except for first
stroke) for dyntopo.  Instead it updates the original vertex color
customdata layer.

Calling into the undo system destroys threading with dyntopo, as its
undo code is single-threaded.
2020-10-30 19:37:22 -07:00
43ccbe353f Try to make SCULPT_orig_vert_data_init avoid allocating undo nodes,
which calls into BMLog which, while now threadsafe (ish?) causes
threads to get bogged down in lock contention.
2020-10-30 19:17:03 -07:00
2d861122e1 Remove debugging clang keyword. It's not #ifdef'd code, but still. 2020-10-29 22:39:06 -07:00
66f8852e30 * Edge queue creation for dyntopo edge split/collapse is now
multithreaded.
* Worked on ProxyVert system, but it still needs more work.  Issue is keeping
  topological layers (reasonably) up to date without it being slow.
* Fixed memory leak in bmlog.
2020-10-29 22:33:57 -07:00
54ddb01299 Fixed a variety of memory corruption bugs 2020-10-29 04:28:03 -07:00
3d47323162 Fixed bug with original coordinates/normals not being set when
dyntopo pbvh is first built
2020-10-27 01:38:59 -07:00
5c77439264 * Fix bug in BKE_pbvh_node_color_buffer_get. 2020-10-27 00:20:07 -07:00
e2c92c1341 More fixes for dyntopo undo and vertices (face data still isn't
implemented).
2020-10-26 18:46:12 -07:00
faf8402c19 Added initial support for customdata to dyntopo undo (BMLog).
TODO:
 - Handle face (loop) data
 - Figure out what to do about edge data (warn user? actually handle
   it?)
 - Handle sculpt color undo push nodes properly (shouldn't be hard).
2020-10-26 02:45:56 -07:00
4c0bcc3d13 PBVH drawing now properly names attributes.
NOTE: I've added a new function, DRW_make_cdlayer_attr_aliases, for
this.  It's patterned after extract_uvs.  The appropriate devs from the
draw engine team should take a look.
2020-10-26 00:19:14 -07:00
4fc4a7e1f4 Fixed SCULPT_dynamic_topology_sync_layers from last commit. 2020-10-25 22:55:46 -07:00
f9859a3b2a Got sculpt colors to work, needs more testing 2020-10-25 22:35:02 -07:00
6da9fa1bf2 pre-valgrind commit 2020-10-25 20:00:42 -07:00
5c6407c268 Fixed broken customdata interpolation in dyntopo collapse.
Also added support for uvs to dyntopo gpu buffer building code.
2020-10-25 04:31:06 -07:00
3214b1114f Added support for customdata interpolation to dyntopo.
It seems fast enough for simple cases, I make no promises that it
will be fast in crazy cases lots if there's lots of vgroup layers.

Note I still need to interface properly with the sculpt colors code.
2020-10-25 01:37:52 -07:00
bfbe9a0d55 Fix bugs in last commit. 2020-10-25 01:07:05 -07:00
f482afadab Refactored sculpt code to use a new type, SculptVertRef, that replaces
much of the usage of integer indices.  Meshes and grids simply store the
index here, but bmesh stores a pointer to a BMVert.  This greatly speeds
up DynTopo by reducing the need to maintain flat pointer arrays in bmesh.

To prevent the accidental casting of ScuptVertexRef to indices and vice
versa SculptVertRef is defined as a struct:

typedef struct {intptr_t i} SculptVertRef;

There are also two functions to convert flat index indices to
SculptVertRefs and back:

ScultpVertRef BKE_pbvh_table_index_to_vertex(PBVH *pbvh, int index);
int BKE_pbvh_vertex_index_to_table(PBVH *pbvh, SculptVertRef *ref);

Note that these functions require the aforementioned maintanance of
flat pointer arrays in bmesh, so make sure to call
SCULPT_ensure_vertex_random_access().
2020-10-25 00:32:59 -07:00
661dcd813c Merge performance improvements from trimesh branch into this one. 2020-10-24 19:41:54 -07:00
5541de9a3a Submodule update 2020-10-24 16:03:05 -07:00
3791afa29c Merge remote-tracking branch 'origin' into temp_bmesh_multires 2020-10-24 16:01:45 -07:00
3ae8229843 commit before merge 2020-10-24 16:01:08 -07:00
f61d4b2e3a * Improved multires projection some more in bmesh_interp.c 2020-10-18 18:10:16 -07:00
49f57d8de8 COde cleanup 2020-10-18 16:12:02 -07:00
64c7bad391 New branch to fix multires topology editing. WIP
Changes:
* Brought back bmesh_mdisps_space_set, written from scratch using
  subdiv api, not ccg.
* Wrote a function to smooth multigres grids from within bmesh.  I might
  not need it; unless anyone thinks of a use for it I'll go ahead and
  delete it.

Todo:
* Purge code of all usages of CCG for multires.

This commit:
* Wrote a utility function to dump multires displacements into a (new)
  scene object.
* Consequently, I now know that the CCG_based multires code is not
  compatible with the OpenSubdiv based code.  The former produces gaps
  between grids when converting displacements to object space.
2020-10-17 15:11:02 -07:00
4659855b0f commit multires patch 2020-10-17 01:24:34 -07:00
500 changed files with 104290 additions and 9377 deletions

View File

@@ -266,6 +266,7 @@ ForEachMacros:
- SET_SLOT_PROBING_BEGIN
- MAP_SLOT_PROBING_BEGIN
- VECTOR_SET_SLOT_PROBING_BEGIN
- TGSET_ITER
- WL_ARRAY_FOR_EACH
- FOREACH_SPECTRUM_CHANNEL

View File

@@ -707,6 +707,12 @@ if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\LLVM\\LLVM;]/lib/clang/7.0.0/lib/windows
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\LLVM\\LLVM;]/lib/clang/6.0.0/lib/windows
)
find_library(
COMPILER_ASAN_LIBRARY_THUNK NAMES clang_rt.asan_dll_thunk-x86_64
PATHS
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\LLVM\\LLVM;]/lib/clang/7.0.0/lib/windows
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\LLVM\\LLVM;]/lib/clang/6.0.0/lib/windows
)
elseif(APPLE)
execute_process(COMMAND ${CMAKE_CXX_COMPILER}
-print-file-name=lib
@@ -727,6 +733,7 @@ if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
)
endif()
mark_as_advanced(COMPILER_ASAN_LIBRARY_THUNK)
mark_as_advanced(COMPILER_ASAN_LIBRARY)
endif()
endif()
@@ -784,6 +791,8 @@ if("${CMAKE_GENERATOR}" MATCHES "Ninja")
mark_as_advanced(WITH_NINJA_POOL_JOBS)
endif()
option(WITH_INSTANT_MESHES, "Instant Meshes Quadrangulator" ON)
# Installation process.
set(POSTINSTALL_SCRIPT "" CACHE FILEPATH "Run given CMake script after installation process")
mark_as_advanced(POSTINSTALL_SCRIPT)
@@ -1054,9 +1063,9 @@ if(NOT CMAKE_BUILD_TYPE MATCHES "Release")
unset(_list_COMPILER_ASAN_CFLAGS)
unset(_is_CONFIG_DEBUG)
elseif(COMPILER_ASAN_LIBRARY)
set(PLATFORM_LINKLIBS "${PLATFORM_LINKLIBS};${COMPILER_ASAN_LIBRARY}")
set(PLATFORM_LINKFLAGS "${COMPILER_ASAN_LIBRARY} ${COMPILER_ASAN_LINKER_FLAGS}")
set(PLATFORM_LINKFLAGS_DEBUG "${COMPILER_ASAN_LIBRARY} ${COMPILER_ASAN_LINKER_FLAGS}")
set(PLATFORM_LINKLIBS "${PLATFORM_LINKLIBS};\"${COMPILER_ASAN_LIBRARY}\" \"${COMPILER_ASAN_LIBRARY_THUNK}\"")
set(PLATFORM_LINKFLAGS "\"${COMPILER_ASAN_LIBRARY}\" \"${COMPILER_ASAN_LIBRARY_THUNK}\" ${COMPILER_ASAN_LINKER_FLAGS}")
set(PLATFORM_LINKFLAGS_DEBUG "\"${COMPILER_ASAN_LIBRARY}\" \"${COMPILER_ASAN_LIBRARY_THUNK}\" ${COMPILER_ASAN_LINKER_FLAGS}")
endif()
endif()
endif()

View File

@@ -389,7 +389,7 @@
// Does the compiler support result_of?
#ifndef EIGEN_HAS_STD_RESULT_OF
#if EIGEN_MAX_CPP_VER>=11 && ((__has_feature(cxx_lambdas) || (defined(__cplusplus) && __cplusplus >= 201103L)))
#if __cplusplus < 201703L && EIGEN_MAX_CPP_VER>=11 && ((__has_feature(cxx_lambdas) || (defined(__cplusplus) && __cplusplus >= 201103L && __cplusplus)))
#define EIGEN_HAS_STD_RESULT_OF 1
#else
#define EIGEN_HAS_STD_RESULT_OF 0

View File

@@ -221,7 +221,7 @@ AUD_API void AUD_Device_setListenerVelocity(AUD_Device* device, const float valu
AUD_API double AUD_Device_getRate(AUD_Device* device)
{
auto dev = device ? *device : DeviceManager::getDevice();
return dev->getSpecs().rate;
return dev ? dev->getSpecs().rate : 0.0;
}
AUD_API float AUD_Device_getSpeedOfSound(AUD_Device* device)

View File

@@ -87,11 +87,17 @@ public:
* \param args The arguments of the task.
* \return A future of the same type as the return type of the task.
*/
#if __cplusplus > 201703L
template<class T, class... Args>
std::future<typename std::invoke_result<T, Args...>::type> enqueue(T&& t, Args&&... args)
{
using pkgdTask = std::packaged_task<typename std::invoke_result<T, Args...>::type()>;
#else
template<class T, class... Args>
std::future<typename std::result_of<T(Args...)>::type> enqueue(T&& t, Args&&... args)
{
using pkgdTask = std::packaged_task<typename std::result_of<T(Args...)>::type()>;
#endif
std::shared_ptr<pkgdTask> task = std::make_shared<pkgdTask>(std::bind(std::forward<T>(t), std::forward<Args>(args)...));
auto result = task->get_future();

View File

@@ -67,7 +67,7 @@ SET(LEMON_ENABLE_ILOG YES CACHE STRING "Enable ILOG (CPLEX) solver backend.")
SET(LEMON_ENABLE_COIN YES CACHE STRING "Enable COIN solver backend.")
SET(LEMON_ENABLE_SOPLEX YES CACHE STRING "Enable SoPlex solver backend.")
IF(LEMON_ENABLE_GLPK)
IF(LEMON_ENABLE_GLPK)
FIND_PACKAGE(GLPK 4.33)
ENDIF(LEMON_ENABLE_GLPK)
IF(LEMON_ENABLE_ILOG)

View File

@@ -4,7 +4,7 @@ FIND_PATH(ILOG_ROOT_DIR
PATHS /opt/ibm/ILOG /usr/local/ibm/ILOG /usr/local/ILOG /usr/local/ilog
PATHS "$ENV{HOME}/ILOG" "$ENV{HOME}/.local/ILOG"
PATHS "$ENV{HOME}/ibm/ILOG" "$ENV{HOME}/.local/ibm/ILOG"
PATHS "C:/Program Files/IBM/ILOG"
PATHS "C:/Program Files/IBM/ILOG"
PATH_SUFFIXES "CPLEX_Studio126" "CPLEX_Studio125"
"CPLEX_Studio124" "CPLEX_Studio123" "CPLEX_Studio122"
NO_DEFAULT_PATH

View File

@@ -16,3 +16,4 @@ LINK_DIRECTORIES(
# ADD_EXECUTABLE(myprog myprog-main.cc)
# TARGET_LINK_LIBRARIES(myprog lemon)

View File

@@ -88,3 +88,4 @@ INSTALL(
FILES ${CMAKE_CURRENT_BINARY_DIR}/lemon.pc
DESTINATION lib/pkgconfig
)

View File

@@ -50,6 +50,18 @@ set(INC_SYS
${EIGEN3_INCLUDE_DIRS}
)
#if(WITH_TBB)
# add_definitions(-DWITH_TBB)
#
# list(APPEND INC_SYS
# ${TBB_INCLUDE_DIRS}
# )
#
# list(APPEND LIB
# ${TBB_LIBRARIES}
# )
#endif()
set(SRC
src/adjacent-matrix.cpp
src/adjacent-matrix.hpp

View File

@@ -10,6 +10,7 @@
#include <fstream>
#include <unordered_map>
#include <functional>
namespace qflow {
@@ -69,7 +70,7 @@ void load(const char* filename, MatrixXd& V, MatrixXi& F)
};
/// Hash function for obj_vertex
struct obj_vertexHash {
struct obj_vertexHash : std::function<size_t(obj_vertex)> {
std::size_t operator()(const obj_vertex &v) const {
size_t hash = std::hash<uint32_t>()(v.p);
hash = hash * 37 + std::hash<uint32_t>()(v.uv);

View File

@@ -13,6 +13,13 @@
#include "flow.hpp"
#include "parametrizer.hpp"
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
namespace qflow {
#ifdef WITH_CUDA

View File

@@ -6,6 +6,16 @@
#include <random>
#include "optimizer.hpp"
#include <algorithm>
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
namespace qflow {

View File

@@ -8,6 +8,16 @@
#include "dedge.hpp"
#include <queue>
#include <algorithm>
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
namespace qflow {
void Parametrizer::NormalizeMesh() {

View File

@@ -2,6 +2,14 @@
#include "field-math.hpp"
#include "parametrizer.hpp"
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
namespace qflow {
void Parametrizer::ComputeOrientationSingularities() {

View File

@@ -19,6 +19,13 @@
#include "post-solver.hpp"
#include "serialize.hpp"
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
namespace qflow {
using namespace Eigen;

View File

@@ -8,6 +8,14 @@
#include "field-math.hpp"
#include "parametrizer.hpp"
#include <algorithm>
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
namespace qflow {
void subdivide(MatrixXi &F, MatrixXd &V, VectorXd& rho, VectorXi &V2E, VectorXi &E2E, VectorXi &boundary,

BIN
icon_geom.blend Normal file

Binary file not shown.

View File

@@ -67,6 +67,10 @@ if(UNIX AND NOT APPLE)
add_subdirectory(libc_compat)
endif()
if(WITH_INSTANT_MESHES)
add_subdirectory(instant-meshes)
endif()
if(UNIX AND NOT APPLE)
# Important this comes after "ghost" as it uses includes defined by GHOST's CMake.
if(WITH_GHOST_WAYLAND AND WITH_GHOST_WAYLAND_DYNLOAD)

View File

@@ -63,17 +63,17 @@
/* Unsigned */
ATOMIC_INLINE uint64_t atomic_add_and_fetch_uint64(uint64_t *p, uint64_t x)
{
return InterlockedExchangeAdd64((int64_t *)p, (int64_t)x) + x;
return (uint64_t)(InterlockedExchangeAdd64((int64_t *)p, (int64_t)x) + (int64_t)x);
}
ATOMIC_INLINE uint64_t atomic_sub_and_fetch_uint64(uint64_t *p, uint64_t x)
{
return InterlockedExchangeAdd64((int64_t *)p, -((int64_t)x)) - x;
return (uint64_t)(InterlockedExchangeAdd64((int64_t *)p, -((int64_t)x)) - (int64_t)x);
}
ATOMIC_INLINE uint64_t atomic_cas_uint64(uint64_t *v, uint64_t old, uint64_t _new)
{
return InterlockedCompareExchange64((int64_t *)v, _new, old);
return (uint64_t)(InterlockedCompareExchange64((int64_t *)v, _new, old));
}
ATOMIC_INLINE uint64_t atomic_load_uint64(const uint64_t *v)
@@ -88,12 +88,12 @@ ATOMIC_INLINE void atomic_store_uint64(uint64_t *p, uint64_t v)
ATOMIC_INLINE uint64_t atomic_fetch_and_add_uint64(uint64_t *p, uint64_t x)
{
return InterlockedExchangeAdd64((int64_t *)p, (int64_t)x);
return (uint64_t)InterlockedExchangeAdd64((int64_t *)p, (int64_t)x);
}
ATOMIC_INLINE uint64_t atomic_fetch_and_sub_uint64(uint64_t *p, uint64_t x)
{
return InterlockedExchangeAdd64((int64_t *)p, -((int64_t)x));
return (uint64_t)InterlockedExchangeAdd64((int64_t *)p, -((int64_t)x));
}
/* Signed */
@@ -137,17 +137,17 @@ ATOMIC_INLINE int64_t atomic_fetch_and_sub_int64(int64_t *p, int64_t x)
/* Unsigned */
ATOMIC_INLINE uint32_t atomic_add_and_fetch_uint32(uint32_t *p, uint32_t x)
{
return InterlockedExchangeAdd(p, x) + x;
return (uint32_t)InterlockedExchangeAdd(p, x) + x;
}
ATOMIC_INLINE uint32_t atomic_sub_and_fetch_uint32(uint32_t *p, uint32_t x)
{
return InterlockedExchangeAdd(p, -((int32_t)x)) - x;
return (uint32_t)InterlockedExchangeAdd(p, -((int32_t)x)) - x;
}
ATOMIC_INLINE uint32_t atomic_cas_uint32(uint32_t *v, uint32_t old, uint32_t _new)
{
return InterlockedCompareExchange((long *)v, _new, old);
return (uint32_t)InterlockedCompareExchange((long *)v, _new, old);
}
ATOMIC_INLINE uint32_t atomic_load_uint32(const uint32_t *v)
@@ -162,17 +162,17 @@ ATOMIC_INLINE void atomic_store_uint32(uint32_t *p, uint32_t v)
ATOMIC_INLINE uint32_t atomic_fetch_and_add_uint32(uint32_t *p, uint32_t x)
{
return InterlockedExchangeAdd(p, x);
return (uint32_t)InterlockedExchangeAdd(p, x);
}
ATOMIC_INLINE uint32_t atomic_fetch_and_or_uint32(uint32_t *p, uint32_t x)
{
return InterlockedOr((long *)p, x);
return (uint32_t)InterlockedOr((long *)p, x);
}
ATOMIC_INLINE uint32_t atomic_fetch_and_and_uint32(uint32_t *p, uint32_t x)
{
return InterlockedAnd((long *)p, x);
return (uint32_t)InterlockedAnd((long *)p, x);
}
/* Signed */
@@ -259,9 +259,9 @@ ATOMIC_INLINE uint8_t atomic_fetch_and_or_uint8(uint8_t *p, uint8_t b)
ATOMIC_INLINE int8_t atomic_fetch_and_and_int8(int8_t *p, int8_t b)
{
#if (LG_SIZEOF_PTR == 8 || LG_SIZEOF_INT == 8)
return InterlockedAnd8((char *)p, (char)b);
return (int8_t)InterlockedAnd8((char *)p, (char)b);
#else
return _InterlockedAnd8((char *)p, (char)b);
return (int8_t)_InterlockedAnd8((char *)p, (char)b);
#endif
}
@@ -269,9 +269,9 @@ ATOMIC_INLINE int8_t atomic_fetch_and_and_int8(int8_t *p, int8_t b)
ATOMIC_INLINE int8_t atomic_fetch_and_or_int8(int8_t *p, int8_t b)
{
#if (LG_SIZEOF_PTR == 8 || LG_SIZEOF_INT == 8)
return InterlockedOr8((char *)p, (char)b);
return (int8_t)InterlockedOr8((char *)p, (char)b);
#else
return _InterlockedOr8((char *)p, (char)b);
return (int8_t)_InterlockedOr8((char *)p, (char)b);
#endif
}

View File

@@ -409,6 +409,7 @@ if(WITH_CYCLES_CUDA_BINARIES)
string(REGEX REPLACE ".*release ([0-9]+)\\.([0-9]+).*" "\\2" CUDA_VERSION_MINOR "${NVCC_OUT}")
set(CUDA_VERSION "${CUDA_VERSION_MAJOR}${CUDA_VERSION_MINOR}")
# warn for other versions
if((CUDA_VERSION STREQUAL "101") OR
(CUDA_VERSION STREQUAL "102") OR
@@ -451,6 +452,7 @@ if(WITH_CYCLES_CUDA_BINARIES)
-D CCL_NAMESPACE_BEGIN=
-D CCL_NAMESPACE_END=
-D NVCC
-allow-unsupported-compiler
-m ${CUDA_BITS}
-I ${CMAKE_CURRENT_SOURCE_DIR}/..
-I ${CMAKE_CURRENT_SOURCE_DIR}/device/cuda

View File

@@ -596,4 +596,7 @@ if(WITH_XR_OPENXR)
unset(XR_PLATFORM_DEFINES)
endif()
remove_cc_flag("-fsanitize=address")
remove_cc_flag("/fsanitize=address")
blender_add_lib(bf_intern_ghost "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")

View File

@@ -9,6 +9,18 @@
#include <stdint.h>
#ifndef ATTR_NO_OPT
# ifdef __clang__
# define ATTR_NO_OPT __attribute__((optnone))
# elif defined(_MSC_VER)
# define ATTR_NO_OPT __pragma(optimize("", off))
# elif defined(__GNUC__)
# define ATTR_NO_OPT __attribute__((optimize("O0")))
# else
# define ATTR_NO_OPT
# endif
#endif
#ifdef WITH_CXX_GUARDEDALLOC
# include "MEM_guardedalloc.h"
#else

View File

@@ -15,6 +15,14 @@
#include <cstdlib> // for NULL
#if defined(__clang__) || defined(__GCC__)
# define ATTR_NO_ASAN __attribute__((no_sanitize("address")))
#elif _MSC_VER
# define ATTR_NO_ASAN __declspec(no_sanitize_address)
#else
# define ATTR_NO_ASAN
#endif
class GHOST_Context : public GHOST_IContext {
public:
/**

View File

@@ -127,7 +127,8 @@ GHOST_TSuccess GHOST_ContextWGL::releaseDrawingContext()
/* Ron Fosner's code for weighting pixel formats and forcing software.
* See http://www.opengl.org/resources/faq/technical/weight.cpp
*/
static int weight_pixel_format(PIXELFORMATDESCRIPTOR &pfd, PIXELFORMATDESCRIPTOR &preferredPFD)
ATTR_NO_ASAN static int weight_pixel_format(PIXELFORMATDESCRIPTOR &pfd,
PIXELFORMATDESCRIPTOR &preferredPFD)
{
int weight = 0;
@@ -162,7 +163,7 @@ static int weight_pixel_format(PIXELFORMATDESCRIPTOR &pfd, PIXELFORMATDESCRIPTOR
* A modification of Ron Fosner's replacement for ChoosePixelFormat
* returns 0 on error, else returns the pixel format number to be used
*/
static int choose_pixel_format_legacy(HDC hDC, PIXELFORMATDESCRIPTOR &preferredPFD)
ATTR_NO_ASAN static int choose_pixel_format_legacy(HDC hDC, PIXELFORMATDESCRIPTOR &preferredPFD)
{
int iPixelFormat = 0;
int weight = 0;
@@ -215,7 +216,7 @@ static int choose_pixel_format_legacy(HDC hDC, PIXELFORMATDESCRIPTOR &preferredP
* There is no generic way to clone the lpParam parameter,
* so the caller is responsible for cloning it themselves.
*/
static HWND clone_window(HWND hWnd, LPVOID lpParam)
ATTR_NO_ASAN static HWND clone_window(HWND hWnd, LPVOID lpParam)
{
int count;
@@ -478,7 +479,7 @@ int GHOST_ContextWGL::_choose_pixel_format_arb_1(bool stereoVisual, bool needAlp
return iPixelFormat;
}
int GHOST_ContextWGL::choose_pixel_format_arb(bool stereoVisual, bool needAlpha)
ATTR_NO_ASAN int GHOST_ContextWGL::choose_pixel_format_arb(bool stereoVisual, bool needAlpha)
{
int iPixelFormat;
@@ -505,7 +506,7 @@ static void reportContextString(const char *name, const char *dummy, const char
}
#endif
GHOST_TSuccess GHOST_ContextWGL::initializeDrawingContext()
ATTR_NO_ASAN GHOST_TSuccess GHOST_ContextWGL::initializeDrawingContext()
{
SetLastError(NO_ERROR);

View File

@@ -39,19 +39,19 @@ extern "C" {
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
}
GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
const char *title,
int32_t left,
int32_t top,
uint32_t width,
uint32_t height,
GHOST_TWindowState state,
GHOST_TDrawingContextType type,
bool wantStereoVisual,
bool alphaBackground,
GHOST_WindowWin32 *parentwindow,
bool is_debug,
bool dialog)
ATTR_NO_ASAN GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
const char *title,
int32_t left,
int32_t top,
uint32_t width,
uint32_t height,
GHOST_TWindowState state,
GHOST_TDrawingContextType type,
bool wantStereoVisual,
bool alphaBackground,
GHOST_WindowWin32 *parentwindow,
bool is_debug,
bool dialog)
: GHOST_Window(width, height, state, wantStereoVisual, false),
m_mousePresent(false),
m_inLiveResize(false),
@@ -579,7 +579,7 @@ GHOST_TSuccess GHOST_WindowWin32::invalidate()
return success;
}
GHOST_Context *GHOST_WindowWin32::newDrawingContext(GHOST_TDrawingContextType type)
ATTR_NO_ASAN GHOST_Context *GHOST_WindowWin32::newDrawingContext(GHOST_TDrawingContextType type)
{
if (type == GHOST_kDrawingContextTypeOpenGL) {
GHOST_Context *context;

View File

@@ -7,12 +7,20 @@
#define _USE_MATH_DEFINES
#include "GHOST_Wintab.h"
#include <cstdio>
static void wintab_load_error(const char *func)
{
fprintf(stderr, "corrupted wintab32.dll; missing %s\n", func);
}
GHOST_Wintab *GHOST_Wintab::loadWintab(HWND hwnd)
{
/* Load Wintab library if available. */
auto handle = unique_hmodule(::LoadLibrary("Wintab32.dll"), &::FreeLibrary);
if (!handle) {
fprintf(stderr, "Could not find wintab32.dll\n");
return nullptr;
}
@@ -20,51 +28,61 @@ GHOST_Wintab *GHOST_Wintab::loadWintab(HWND hwnd)
auto info = (GHOST_WIN32_WTInfo)::GetProcAddress(handle.get(), "WTInfoA");
if (!info) {
wintab_load_error("WTInfoA");
return nullptr;
}
auto open = (GHOST_WIN32_WTOpen)::GetProcAddress(handle.get(), "WTOpenA");
if (!open) {
wintab_load_error("WTOpenA");
return nullptr;
}
auto get = (GHOST_WIN32_WTGet)::GetProcAddress(handle.get(), "WTGetA");
if (!get) {
wintab_load_error("WTGetA");
return nullptr;
}
auto set = (GHOST_WIN32_WTSet)::GetProcAddress(handle.get(), "WTSetA");
if (!set) {
wintab_load_error("WTSetA");
return nullptr;
}
auto close = (GHOST_WIN32_WTClose)::GetProcAddress(handle.get(), "WTClose");
if (!close) {
wintab_load_error("WTClose");
return nullptr;
}
auto packetsGet = (GHOST_WIN32_WTPacketsGet)::GetProcAddress(handle.get(), "WTPacketsGet");
if (!packetsGet) {
wintab_load_error("WTPacketGet");
return nullptr;
}
auto queueSizeGet = (GHOST_WIN32_WTQueueSizeGet)::GetProcAddress(handle.get(), "WTQueueSizeGet");
if (!queueSizeGet) {
wintab_load_error("WTQueueSizeGet");
return nullptr;
}
auto queueSizeSet = (GHOST_WIN32_WTQueueSizeSet)::GetProcAddress(handle.get(), "WTQueueSizeSet");
if (!queueSizeSet) {
wintab_load_error("WTQueueSizeSet");
return nullptr;
}
auto enable = (GHOST_WIN32_WTEnable)::GetProcAddress(handle.get(), "WTEnable");
if (!enable) {
wintab_load_error("WTEnable");
return nullptr;
}
auto overlap = (GHOST_WIN32_WTOverlap)::GetProcAddress(handle.get(), "WTOverlap");
if (!overlap) {
wintab_load_error("WTOverlap");
return nullptr;
}
@@ -72,6 +90,8 @@ GHOST_Wintab *GHOST_Wintab::loadWintab(HWND hwnd)
LOGCONTEXT lc = {0};
if (!info(WTI_DEFSYSCTX, 0, &lc)) {
fprintf(stderr, "Failed to initialize Wintab driver\n");
return nullptr;
}
@@ -82,6 +102,8 @@ GHOST_Wintab *GHOST_Wintab::loadWintab(HWND hwnd)
/* The Wintab spec says we must open the context disabled if we are using cursor masks. */
auto hctx = unique_hctx(open(hwnd, &lc, FALSE), close);
if (!hctx) {
fprintf(stderr, "Failed to open Wintab driver\n");
return nullptr;
}
@@ -188,7 +210,7 @@ GHOST_Wintab::GHOST_Wintab(unique_hmodule handle,
m_context{std::move(hctx)},
m_tabletCoord{tablet},
m_systemCoord{system},
m_pkts{queueSize}
m_pkts{(size_t)queueSize}
{
m_fpInfo(WTI_INTERFACE, IFC_NDEVICES, &m_numDevices);
WINTAB_PRINTF("Wintab Devices: %d\n", m_numDevices);

View File

@@ -9,6 +9,7 @@ set(INC
.
..
../atomic
../../source/blender/blenlib
)
set(INC_SYS

View File

@@ -66,6 +66,12 @@ extern short (*MEM_testN)(void *vmemh);
* NULL-safe; will return NULL when receiving a NULL pointer. */
extern void *(*MEM_dupallocN)(const void *vmemh) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT;
/**
* Duplicates a block of memory, and returns a pointer to the
* newly allocated block. */
extern void *(*MEM_dupallocN_id)(const void *vmemh,
const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT;
/**
* Reallocates a block of memory, and returns pointer to the newly
* allocated block, the old one is freed. this is not as optimized
@@ -247,6 +253,8 @@ void MEM_use_lockfree_allocator(void);
* NOTE: The switch between allocator types can only happen before any allocation did happen. */
void MEM_use_guarded_allocator(void);
#define MEM_dupallocN(vmemh) MEM_dupallocN_id(vmemh, __func__)
#ifdef __cplusplus
}
#endif /* __cplusplus */

View File

@@ -26,6 +26,7 @@ const char *malloc_conf = "background_thread:true,dirty_decay_ms:4000";
size_t (*MEM_allocN_len)(const void *vmemh) = MEM_lockfree_allocN_len;
void (*MEM_freeN)(void *vmemh) = MEM_lockfree_freeN;
void *(*MEM_dupallocN)(const void *vmemh) = MEM_lockfree_dupallocN;
void *(*MEM_dupallocN_id)(const void *vmemh, const char *str) = MEM_lockfree_dupallocN_id;
void *(*MEM_reallocN_id)(void *vmemh, size_t len, const char *str) = MEM_lockfree_reallocN_id;
void *(*MEM_recallocN_id)(void *vmemh, size_t len, const char *str) = MEM_lockfree_recallocN_id;
void *(*MEM_callocN)(size_t len, const char *str) = MEM_lockfree_callocN;
@@ -108,6 +109,7 @@ void MEM_use_lockfree_allocator(void)
MEM_allocN_len = MEM_lockfree_allocN_len;
MEM_freeN = MEM_lockfree_freeN;
MEM_dupallocN = MEM_lockfree_dupallocN;
MEM_dupallocN_id = MEM_lockfree_dupallocN_id;
MEM_reallocN_id = MEM_lockfree_reallocN_id;
MEM_recallocN_id = MEM_lockfree_recallocN_id;
MEM_callocN = MEM_lockfree_callocN;
@@ -140,6 +142,7 @@ void MEM_use_guarded_allocator(void)
MEM_allocN_len = MEM_guarded_allocN_len;
MEM_freeN = MEM_guarded_freeN;
MEM_dupallocN = MEM_guarded_dupallocN;
MEM_dupallocN_id = MEM_guarded_dupallocN_id;
MEM_reallocN_id = MEM_guarded_reallocN_id;
MEM_recallocN_id = MEM_guarded_recallocN_id;
MEM_callocN = MEM_guarded_callocN;

View File

@@ -287,6 +287,31 @@ void *MEM_guarded_dupallocN(const void *vmemh)
return newp;
}
void *MEM_guarded_dupallocN_id(const void *vmemh, const char *str)
{
void *newp = NULL;
if (vmemh) {
const MemHead *memh = vmemh;
memh--;
if (LIKELY(memh->alignment == 0)) {
newp = MEM_guarded_mallocN(memh->len, str);
}
else {
newp = MEM_guarded_mallocN_aligned(memh->len, (size_t)memh->alignment, str);
}
if (newp == NULL) {
return NULL;
}
memcpy(newp, vmemh, memh->len);
}
return newp;
}
void *MEM_guarded_reallocN_id(void *vmemh, size_t len, const char *str)
{
void *newp = NULL;
@@ -1012,7 +1037,7 @@ static void MemorY_ErroR(const char *block, const char *error)
print_error("Memoryblock %s: %s\n", block, error);
#ifdef WITH_ASSERT_ABORT
abort();
abort();
#endif
}

View File

@@ -101,6 +101,8 @@ void memory_usage_peak_reset(void);
size_t MEM_lockfree_allocN_len(const void *vmemh) ATTR_WARN_UNUSED_RESULT;
void MEM_lockfree_freeN(void *vmemh);
void *MEM_lockfree_dupallocN(const void *vmemh) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
void *MEM_lockfree_dupallocN_id(const void *vmemh,
const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
void *MEM_lockfree_reallocN_id(void *vmemh,
size_t len,
const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
@@ -145,6 +147,8 @@ void MEM_lockfree_name_ptr_set(void *vmemh, const char *str);
size_t MEM_guarded_allocN_len(const void *vmemh) ATTR_WARN_UNUSED_RESULT;
void MEM_guarded_freeN(void *vmemh);
void *MEM_guarded_dupallocN(const void *vmemh) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
void *MEM_guarded_dupallocN_id(const void *vmemh,
const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
void *MEM_guarded_reallocN_id(void *vmemh,
size_t len,
const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT

View File

@@ -15,6 +15,7 @@
#include "MEM_guardedalloc.h"
/* to ensure strict conversions */
#include "../../source/blender/blenlib/BLI_asan.h"
#include "../../source/blender/blenlib/BLI_strict_flags.h"
#include "atomic_ops.h"
@@ -44,6 +45,21 @@ enum {
#define MEMHEAD_IS_ALIGNED(memhead) ((memhead)->len & (size_t)MEMHEAD_ALIGN_FLAG)
#define MEMHEAD_LEN(memhead) ((memhead)->len & ~((size_t)(MEMHEAD_ALIGN_FLAG)))
#define MEM_POISON_MEMHEAD(vmemh) BLI_asan_poison(MEMHEAD_FROM_PTR(vmemh), sizeof(MemHead))
#define MEM_UNPOISON_MEMHEAD(vmemh) BLI_asan_unpoison(MEMHEAD_FROM_PTR(vmemh), sizeof(MemHead))
/* Uncomment this to have proper peak counter. */
#define USE_ATOMIC_MAX
MEM_INLINE void update_maximum(size_t *maximum_value, size_t value)
{
#ifdef USE_ATOMIC_MAX
atomic_fetch_and_update_max_z(maximum_value, value);
#else
*maximum_value = value > *maximum_value ? value : *maximum_value;
#endif
}
#ifdef __GNUC__
__attribute__((format(printf, 1, 2)))
#endif
@@ -66,7 +82,13 @@ print_error(const char *str, ...)
size_t MEM_lockfree_allocN_len(const void *vmemh)
{
if (LIKELY(vmemh)) {
return MEMHEAD_LEN(MEMHEAD_FROM_PTR(vmemh));
size_t ret;
MEM_UNPOISON_MEMHEAD(vmemh);
ret = MEMHEAD_FROM_PTR(vmemh)->len & ~((size_t)(MEMHEAD_ALIGN_FLAG));
MEM_POISON_MEMHEAD(vmemh);
return ret;
}
return 0;
@@ -87,6 +109,8 @@ void MEM_lockfree_freeN(void *vmemh)
}
MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
MEM_UNPOISON_MEMHEAD(vmemh);
size_t len = MEMHEAD_LEN(memh);
memory_usage_block_free(len);
@@ -109,6 +133,9 @@ void *MEM_lockfree_dupallocN(const void *vmemh)
if (vmemh) {
MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
const size_t prev_size = MEM_lockfree_allocN_len(vmemh);
MEM_UNPOISON_MEMHEAD(vmemh);
if (UNLIKELY(MEMHEAD_IS_ALIGNED(memh))) {
MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
newp = MEM_lockfree_mallocN_aligned(
@@ -117,6 +144,31 @@ void *MEM_lockfree_dupallocN(const void *vmemh)
else {
newp = MEM_lockfree_mallocN(prev_size, "dupli_malloc");
}
MEM_POISON_MEMHEAD(vmemh);
memcpy(newp, vmemh, prev_size);
}
return newp;
}
void *MEM_lockfree_dupallocN_id(const void *vmemh, const char *str)
{
void *newp = NULL;
if (vmemh) {
MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
const size_t prev_size = MEM_lockfree_allocN_len(vmemh);
MEM_UNPOISON_MEMHEAD(vmemh);
if (UNLIKELY(MEMHEAD_IS_ALIGNED(memh))) {
MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
newp = MEM_lockfree_mallocN_aligned(prev_size, (size_t)memh_aligned->alignment, str);
}
else {
newp = MEM_lockfree_mallocN(prev_size, str);
}
MEM_POISON_MEMHEAD(vmemh);
memcpy(newp, vmemh, prev_size);
}
return newp;
@@ -130,6 +182,8 @@ void *MEM_lockfree_reallocN_id(void *vmemh, size_t len, const char *str)
MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
size_t old_len = MEM_lockfree_allocN_len(vmemh);
MEM_UNPOISON_MEMHEAD(vmemh);
if (LIKELY(!MEMHEAD_IS_ALIGNED(memh))) {
newp = MEM_lockfree_mallocN(len, "realloc");
}
@@ -138,6 +192,8 @@ void *MEM_lockfree_reallocN_id(void *vmemh, size_t len, const char *str)
newp = MEM_lockfree_mallocN_aligned(len, (size_t)memh_aligned->alignment, "realloc");
}
MEM_POISON_MEMHEAD(vmemh);
if (newp) {
if (len < old_len) {
/* shrink */
@@ -166,6 +222,8 @@ void *MEM_lockfree_recallocN_id(void *vmemh, size_t len, const char *str)
MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
size_t old_len = MEM_lockfree_allocN_len(vmemh);
MEM_UNPOISON_MEMHEAD(vmemh);
if (LIKELY(!MEMHEAD_IS_ALIGNED(memh))) {
newp = MEM_lockfree_mallocN(len, "recalloc");
}
@@ -173,6 +231,7 @@ void *MEM_lockfree_recallocN_id(void *vmemh, size_t len, const char *str)
MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
newp = MEM_lockfree_mallocN_aligned(len, (size_t)memh_aligned->alignment, "recalloc");
}
MEM_POISON_MEMHEAD(vmemh);
if (newp) {
if (len < old_len) {
@@ -211,6 +270,7 @@ void *MEM_lockfree_callocN(size_t len, const char *str)
memh->len = len;
memory_usage_block_alloc(len);
MEM_POISON_MEMHEAD(PTR_FROM_MEMHEAD(memh));
return PTR_FROM_MEMHEAD(memh);
}
print_error("Calloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
@@ -254,6 +314,8 @@ void *MEM_lockfree_mallocN(size_t len, const char *str)
memh->len = len;
memory_usage_block_alloc(len);
MEM_POISON_MEMHEAD(PTR_FROM_MEMHEAD(memh));
return PTR_FROM_MEMHEAD(memh);
}
print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
@@ -323,6 +385,8 @@ void *MEM_lockfree_mallocN_aligned(size_t len, size_t alignment, const char *str
memh->alignment = (short)alignment;
memory_usage_block_alloc(len);
MEM_POISON_MEMHEAD(PTR_FROM_MEMHEAD(memh));
return PTR_FROM_MEMHEAD(memh);
}
print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",

View File

@@ -0,0 +1,98 @@
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# The Original Code is Copyright (C) 2006, Blender Foundation
# All rights reserved.
# ***** END GPL LICENSE BLOCK *****
set(INC
.
../atomic
../eigen
../guardedalloc
../../extern/Eigen3
ext/half
ext/dset
ext/pss
ext/pcg32
../
)
set(INC_SYS
${GMP_INCLUDE_DIRS}
)
set(SRC
src/aabb.h
src/adjacency.cpp
src/adjacency.h
src/batch.cpp
src/batch.h
src/bvh.cpp
src/bvh.h
src/cleanup.cpp
src/cleanup.h
src/common.h
src/dedge.cpp
src/dedge.h
src/diff.cpp
src/extract.cpp
src/extract.h
src/field.cpp
src/field.h
src/meshstats.cpp
src/meshstats.h
src/normal.cpp
src/normal.h
src/hierarchy.cpp
src/hierarchy.h
src/reorder.cpp
src/reorder.h
src/subdivide.cpp
src/subdivide.h
src/smoothcurve.cpp
src/smoothcurve.h
src/c_api.cpp
instant_meshes_c_api.h
)
set(LIB
)
if(WITH_TBB)
add_definitions(-DWITH_TBB)
list(APPEND INC_SYS
${TBB_INCLUDE_DIRS}
)
list(APPEND LIB
${TBB_LIBRARIES}
)
endif()
if(WIN32 AND NOT UNIX)
list(APPEND INC_SYS
${PTHREADS_INC}
)
list(APPEND LIB
${PTHREADS_LIBRARIES}
)
endif()
blender_add_lib(bf_intern_instant_meshes "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")

View File

@@ -0,0 +1,37 @@
Copyright (c) 2015 Wenzel Jakob, Daniele Panozzo, Marco Tarini,
and Olga Sorkine-Hornung. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
You are under no obligation whatsoever to provide any bug fixes, patches, or
upgrades to the features, functionality or performance of the source code
("Enhancements") to anyone; however, if you choose to make your Enhancements
available either publicly, or directly to the authors of this software, without
imposing a separate written license agreement for such Enhancements, then you
hereby grant the following license: a non-exclusive, royalty-free perpetual
license to install, use, modify, prepare derivative works, incorporate into
other computer software, distribute, and sublicense such enhancements or
derivative works thereof, in binary and source code form.

View File

@@ -0,0 +1,72 @@
# Instant Meshes
[![Build Status](https://travis-ci.org/wjakob/instant-meshes.svg?branch=master)](https://travis-ci.org/wjakob/instant-meshes)
[![Build status](https://ci.appveyor.com/api/projects/status/dm4kqxhin5uxiey0/branch/master?svg=true)](https://ci.appveyor.com/project/wjakob/instant-meshes/branch/master)
<img width="170" height="166" src="https://github.com/wjakob/instant-meshes/raw/master/resources/icon.png">
This repository contains the interactive meshing software developed as part of the publication
> **Instant Field-Aligned Meshes**<br/>
> Wenzel Jakob, Marco Tarini, Daniele Panozzo, Olga Sorkine-Hornung<br/>
> In *ACM Transactions on Graphics (Proceedings of SIGGRAPH Asia 2015)*<br/>
> [PDF](http://igl.ethz.ch/projects/instant-meshes/instant-meshes-SA-2015-jakob-et-al.pdf),
> [Video](https://www.youtube.com/watch?v=U6wtw6W4x3I),
> [Project page](http://igl.ethz.ch/projects/instant-meshes/)
##### In commercial software
Since version 10.2, Modo uses the Instant Meshes algorithm to implement its
automatic retopology feature. An interview discussing this technique and more
recent projects is available [here](https://www.foundry.com/trends/design-visualisation/mitsuba-renderer-instant-meshes).
## Screenshot
![Instant Meshes logo](https://github.com/wjakob/instant-meshes/raw/master/resources/screenshot.jpg)
## Pre-compiled binaries
The following binaries (Intel, 64 bit) are automatically generated from the latest GitHub revision.
> [Microsoft Windows](https://instant-meshes.s3.eu-central-1.amazonaws.com/Release/instant-meshes-windows.zip)<br/>
> [Mac OS X](https://instant-meshes.s3.eu-central-1.amazonaws.com/instant-meshes-macos.zip)<br/>
> [Linux](https://instant-meshes.s3.eu-central-1.amazonaws.com/instant-meshes-linux.zip)
Please also fetch the following dataset ZIP file and extract it so that the
``datasets`` folder is in the same directory as ``Instant Meshes``, ``Instant Meshes.app``,
or ``Instant Meshes.exe``.
> [Datasets](https://instant-meshes.s3.eu-central-1.amazonaws.com/instant-meshes-datasets.zip)
Note: On Linux, Instant Meshes relies on the program ``zenity``, which must be installed.
## Compiling
Compiling from scratch requires CMake and a recent version of XCode on Mac,
Visual Studio 2015 on Windows, and GCC on Linux.
On MacOS, compiling should be as simple as
git clone --recursive https://github.com/wjakob/instant-meshes
cd instant-meshes
cmake .
make -j 4
To build on Linux, please install the prerequisites ``libxrandr-dev``,
``libxinerama-dev``, ``libxcursor-dev``, and ``libxi-dev`` and then use the
same sequence of commands shown above for MacOS.
On Windows, open the generated file ``InstantMeshes.sln`` after step 3 and proceed building as usual from within Visual Studio.
## Usage
To get started, launch the binary and select a dataset using the "Open mesh" button on the top left (the application must be located in the same directory as the 'datasets' folder, otherwise the panel will be empty).
The standard workflow is to solve for an orientation field (first blue button) and a position field (second blue button) in sequence, after which the 'Export mesh' button becomes active. Many user interface elements display a descriptive message when hovering the mouse cursor above for a second.
A range of additional information about the input mesh, the computed fields,
and the output mesh can be visualized using the check boxes accessible via the
'Advanced' panel.
Clicking the left mouse button and dragging rotates the object; right-dragging
(or shift+left-dragging) translates, and the mouse wheel zooms. The fields can also be manipulated using brush tools that are accessible by clicking the first icon in each 'Tool' row.

View File

@@ -0,0 +1 @@
This is a fork of instant-meshes, an alternative to QuadriFlow.

View File

@@ -0,0 +1,17 @@
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

View File

@@ -0,0 +1,11 @@
# Lock-free parallel disjoint set data structure
This is a small self-contained C++11 implementation of the UNION-FIND data
structure with path compression and union by rank and a few extras It supports
concurrent `find()`, `same()` and `unite()` calls as described in the paper
*Wait-free Parallel Algorithms for the Union-Find Problem*
by Richard J. Anderson and Heather Woll
In addition, this class supports optimistic locking (`try_lock()`/`unlock()`)
of disjoint sets and a *combined* unite+unlock operation for pairs of sets.

View File

@@ -0,0 +1,159 @@
#if !defined(__UNIONFIND_H)
#define __UNIONFIND_H
#include <vector>
#include <atomic>
#include <iostream>
/**
* Lock-free parallel disjoint set data structure (aka UNION-FIND)
* with path compression and union by rank
*
* Supports concurrent find(), same() and unite() calls as described
* in the paper
*
* "Wait-free Parallel Algorithms for the Union-Find Problem"
* by Richard J. Anderson and Heather Woll
*
* In addition, this class supports optimistic locking (try_lock/unlock)
* of disjoint sets and a combined unite+unlock operation.
*
* \author Wenzel Jakob
*/
class DisjointSets {
public:
DisjointSets(uint32_t size) : mData(size) {
for (uint32_t i=0; i<size; ++i)
mData[i] = (uint32_t) i;
}
uint32_t find(uint32_t id) const {
while (id != parent(id)) {
uint64_t value = mData[id];
uint32_t new_parent = parent((uint32_t) value);
uint64_t new_value =
(value & 0xFFFFFFFF00000000ULL) | new_parent;
/* Try to update parent (may fail, that's ok) */
if (value != new_value)
mData[id].compare_exchange_weak(value, new_value);
id = new_parent;
}
return id;
}
bool same(uint32_t id1, uint32_t id2) const {
for (;;) {
id1 = find(id1);
id2 = find(id2);
if (id1 == id2)
return true;
if (parent(id1) == id1)
return false;
}
}
uint32_t unite(uint32_t id1, uint32_t id2) {
for (;;) {
id1 = find(id1);
id2 = find(id2);
if (id1 == id2)
return id1;
uint32_t r1 = rank(id1), r2 = rank(id2);
if (r1 > r2 || (r1 == r2 && id1 < id2)) {
std::swap(r1, r2);
std::swap(id1, id2);
}
uint64_t oldEntry = ((uint64_t) r1 << 32) | id1;
uint64_t newEntry = ((uint64_t) r1 << 32) | id2;
if (!mData[id1].compare_exchange_strong(oldEntry, newEntry))
continue;
if (r1 == r2) {
oldEntry = ((uint64_t) r2 << 32) | id2;
newEntry = ((uint64_t) (r2+1) << 32) | id2;
/* Try to update the rank (may fail, that's ok) */
mData[id2].compare_exchange_weak(oldEntry, newEntry);
}
break;
}
return id2;
}
/**
* Try to lock the a disjoint union identified by one
* of its elements (this can occasionally fail when there
* are concurrent operations). The parameter 'id' will be
* updated to store the current representative ID of the
* union
*/
bool try_lock(uint32_t &id) {
const uint64_t lock_flag = 1ULL << 63;
id = find(id);
uint64_t value = mData[id];
if ((value & lock_flag) || (uint32_t) value != id)
return false;
// On IA32/x64, a PAUSE instruction is recommended for CAS busy loops
#if defined(__i386__) || defined(__amd64__)
__asm__ __volatile__ ("pause\n");
#endif
return mData[id].compare_exchange_strong(value, value | lock_flag);
}
void unlock(uint32_t id) {
const uint64_t lock_flag = 1ULL << 63;
mData[id] &= ~lock_flag;
}
/**
* Return the representative index of the set that results from merging
* locked disjoint sets 'id1' and 'id2'
*/
uint32_t unite_index_locked(uint32_t id1, uint32_t id2) const {
uint32_t r1 = rank(id1), r2 = rank(id2);
return (r1 > r2 || (r1 == r2 && id1 < id2)) ? id1 : id2;
}
/**
* Atomically unite two locked disjoint sets and unlock them. Assumes
* that here are no other concurrent unite() involving the same sets
*/
uint32_t unite_unlock(uint32_t id1, uint32_t id2) {
uint32_t r1 = rank(id1), r2 = rank(id2);
if (r1 > r2 || (r1 == r2 && id1 < id2)) {
std::swap(r1, r2);
std::swap(id1, id2);
}
mData[id1] = ((uint64_t) r1 << 32) | id2;
mData[id2] = ((uint64_t) (r2 + ((r1 == r2) ? 1 : 0)) << 32) | id2;
return id2;
}
uint32_t size() const { return (uint32_t) mData.size(); }
uint32_t rank(uint32_t id) const {
return ((uint32_t) (mData[id] >> 32)) & 0x7FFFFFFFu;
}
uint32_t parent(uint32_t id) const {
return (uint32_t) mData[id];
}
friend std::ostream &operator<<(std::ostream &os, const DisjointSets &f) {
for (size_t i=0; i<f.mData.size(); ++i)
os << i << ": parent=" << f.parent(i) << ", rank=" << f.rank(i) << std::endl;
return os;
}
mutable std::vector<std::atomic<uint64_t>> mData;
};
#endif /* __UNIONFIND_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
# pcg32
This is a tiny self-contained C++ implementation of the PCG32 random number
based on code by Melissa O'Neill available at http://www.pcg-random.org.
I decided to make put together my own version because the official small
implementation lacks a C++ interface and various important features (e.g.
rewind/difference support, shuffling, floating point sample generation), and
the big C++ version is extremely large and uses very recent language features
that are not yet supported by all compilers.

View File

@@ -0,0 +1,109 @@
/*
* PCG Random Number Generation for C.
*
* Copyright 2014 Melissa O'Neill <oneill@pcg-random.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For additional information about the PCG random number generation scheme,
* including its license and other licensing options, visit
*
* http://www.pcg-random.org
*/
/*
* This is the original demo application from the PCG library ported to the new API
*/
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include <string.h>
#include "pcg32.h"
int main(int argc, char** argv) {
// Read command-line options
int rounds = 5;
if (argc > 1)
rounds = atoi(argv[1]);
pcg32 rng;
// You should *always* seed the RNG. The usual time to do it is the
// point in time when you create RNG (typically at the beginning of the
// program).
//
// pcg32::seed takes two 64-bit constants (the initial state, and the
// rng sequence selector; rngs with different sequence selectors will
// *never* have random sequences that coincide, at all)
rng.seed(42u, 54u);
printf("pcg32_random_r:\n"
" - result: 32-bit unsigned int (uint32_t)\n"
" - period: 2^64 (* 2^63 streams)\n"
" - state type: pcg32_random_t (%zu bytes)\n"
" - output func: XSH-RR\n"
"\n",
sizeof(pcg32));
for (int round = 1; round <= rounds; ++round) {
printf("Round %d:\n", round);
/* Make some 32-bit numbers */
printf(" 32bit:");
for (int i = 0; i < 6; ++i)
printf(" 0x%08x", rng.nextUInt());
printf("\n");
/* Toss some coins */
printf(" Coins: ");
for (int i = 0; i < 65; ++i)
printf("%c", rng.nextUInt(2) ? 'H' : 'T');
printf("\n");
/* Roll some dice */
printf(" Rolls:");
for (int i = 0; i < 33; ++i) {
printf(" %d", (int)rng.nextUInt(6) + 1);
}
printf("\n");
/* Deal some cards */
enum { SUITS = 4, NUMBERS = 13, CARDS = 52 };
char cards[CARDS];
for (int i = 0; i < CARDS; ++i)
cards[i] = i;
rng.shuffle(cards, cards + CARDS);
printf(" Cards:");
static const char number[] = {'A', '2', '3', '4', '5', '6', '7',
'8', '9', 'T', 'J', 'Q', 'K'};
static const char suit[] = {'h', 'c', 'd', 's'};
for (int i = 0; i < CARDS; ++i) {
printf(" %c%c", number[cards[i] / SUITS], suit[cards[i] % SUITS]);
if ((i + 1) % 22 == 0)
printf("\n\t");
}
printf("\n");
printf("\n");
}
return 0;
}

View File

@@ -0,0 +1,46 @@
pcg32_random_r:
- result: 32-bit unsigned int (uint32_t)
- period: 2^64 (* 2^63 streams)
- state type: pcg32_random_t (16 bytes)
- output func: XSH-RR
Round 1:
32bit: 0xa15c02b7 0x7b47f409 0xba1d3330 0x83d2f293 0xbfa4784b 0xcbed606e
Coins: HHTTTHTHHHTHTTTHHHHHTTTHHHTHTHTHTTHTTTHHHHHHTTTTHHTTTTTHTTTTTTTHT
Rolls: 3 4 1 1 2 2 3 2 4 3 2 4 3 3 5 2 3 1 3 1 5 1 4 1 5 6 4 6 6 2 6 3 3
Cards: Qd Ks 6d 3s 3d 4c 3h Td Kc 5c Jh Kd Jd As 4s 4h Ad Th Ac Jc 7s Qs
2s 7h Kh 2d 6c Ah 4d Qh 9h 6s 5s 2c 9c Ts 8d 9s 3c 8c Js 5d 2h 6h
7d 8s 9d 5h 8h Qc 7c Tc
Round 2:
32bit: 0x74ab93ad 0x1c1da000 0x494ff896 0x34462f2f 0xd308a3e5 0x0fa83bab
Coins: HHHHHHHHHHTHHHTHTHTHTHTTTTHHTTTHHTHHTHTTHHTTTHHHHHHTHTTHTHTTTTTTT
Rolls: 5 1 1 3 3 2 4 5 3 2 2 6 4 3 2 4 2 4 3 2 3 6 3 2 3 4 2 4 1 1 5 4 4
Cards: 7d 2s 7h Td 8s 3c 3d Js 2d Tc 4h Qs 5c 9c Th 2c Jc Qd 9d Qc 7s 3s
5s 6h 4d Jh 4c Ac 4s 5h 5d Kc 8h 8d Jd 9s Ad 6s 6c Kd 2h 3h Kh Ts
Qh 9h 6d As 7c Ks Ah 8c
Round 3:
32bit: 0x39af5f9f 0x04196b18 0xc3c3eb28 0xc076c60c 0xc693e135 0xf8f63932
Coins: HTTHHTTTTTHTTHHHTHTTHHTTHTHHTHTHTTTTHHTTTHHTHHTTHTTHHHTHHHTHTTTHT
Rolls: 5 1 5 3 2 2 4 5 3 3 1 3 4 6 3 2 3 4 2 2 3 1 5 2 4 6 6 4 2 4 3 3 6
Cards: Kd Jh Kc Qh 4d Qc 4h 9d 3c Kh Qs 8h 5c Jd 7d 8d 3h 7c 8s 3s 2h Ks
9c 9h 2c 8c Ad 7s 4s 2s 5h 6s 4c Ah 7h 5s Ac 3d 5d Qd As Tc 6h 9s
2d 6c 6d Td Jc Ts Th Js
Round 4:
32bit: 0x55ce6851 0x97a7726d 0x17e10815 0x58007d43 0x962fb148 0xb9bb55bd
Coins: HHTHHTTTTHTHHHHHTTHHHTTTHHTHTHTHTHHTTHTHHHHHHTHHTHHTHHTTTTHHTHHTT
Rolls: 6 6 3 2 3 4 2 6 4 2 6 3 2 3 5 5 3 4 4 6 6 2 6 5 4 4 6 1 6 1 3 6 5
Cards: Qd 8h 5d 8s 8d Ts 7h Th Qs Js 7s Kc 6h 5s 4d Ac Jd 7d 7c Td 2c 6s
5h 6d 3s Kd 9s Jh Kh As Ah 9h 3c Qh 9c 2d Tc 9d 2s 3d Ks 4h Qc Ad
Jc 8c 2h 3h 4s 4c 5c 6c
Round 5:
32bit: 0xfcef7cd6 0x1b488b5a 0xd0daf7ea 0x1d9a70f7 0x241a37cf 0x9a3857b7
Coins: HHHHTHHTTHTTHHHTTTHHTHTHTTTTHTTHTHTTTHHHTHTHTTHTTHTHHTHTHHHTHTHTT
Rolls: 5 4 1 2 6 1 3 1 5 6 3 6 2 1 4 4 5 2 1 5 6 5 6 4 4 4 5 2 6 4 3 5 6
Cards: 4d 9s Qc 9h As Qs 7s 4c Kd 6h 6s 2c 8c 5d 7h 5h Jc 3s 7c Jh Js Ks
Tc Jd Kc Th 3h Ts Qh Ad Td 3c Ah 2d 3d 5c Ac 8s 5s 9c 2h 6c 6d Kh
Qd 8d 7d 2s 8h 4h 9d 4s

View File

@@ -0,0 +1,208 @@
/*
* Tiny self-contained version of the PCG Random Number Generation for C++
* put together from pieces of the much larger C/C++ codebase.
* Wenzel Jakob, February 2015
*
* The PCG random number generator was developed by Melissa O'Neill <oneill@pcg-random.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For additional information about the PCG random number generation scheme,
* including its license and other licensing options, visit
*
* http://www.pcg-random.org
*/
#ifndef __PCG32_H
#define __PCG32_H 1
#define PCG32_DEFAULT_STATE 0x853c49e6748fea9bULL
#define PCG32_DEFAULT_STREAM 0xda3e39cb94b95bdbULL
#define PCG32_MULT 0x5851f42d4c957f2dULL
#include <inttypes.h>
#include <cmath>
#include <cassert>
#include <algorithm>
/// PCG32 Pseudorandom number generator
struct pcg32 {
/// Initialize the pseudorandom number generator with default seed
pcg32() : state(PCG32_DEFAULT_STATE), inc(PCG32_DEFAULT_STREAM) {}
/// Initialize the pseudorandom number generator with the \ref seed() function
pcg32(uint64_t initstate, uint64_t initseq = 1u) { seed(initstate, initseq); }
/**
* \brief Seed the pseudorandom number generator
*
* Specified in two parts: a state initializer and a sequence selection
* constant (a.k.a. stream id)
*/
void seed(uint64_t initstate, uint64_t initseq = 1) {
state = 0U;
inc = (initseq << 1u) | 1u;
nextUInt();
state += initstate;
nextUInt();
}
/// Generate a uniformly distributed unsigned 32-bit random number
uint32_t nextUInt() {
uint64_t oldstate = state;
state = oldstate * PCG32_MULT + inc;
uint32_t xorshifted = (uint32_t) (((oldstate >> 18u) ^ oldstate) >> 27u);
uint32_t rot = (uint32_t) (oldstate >> 59u);
return (xorshifted >> rot) | (xorshifted << ((~rot + 1u) & 31));
}
/// Generate a uniformly distributed number, r, where 0 <= r < bound
uint32_t nextUInt(uint32_t bound) {
// To avoid bias, we need to make the range of the RNG a multiple of
// bound, which we do by dropping output less than a threshold.
// A naive scheme to calculate the threshold would be to do
//
// uint32_t threshold = 0x100000000ull % bound;
//
// but 64-bit div/mod is slower than 32-bit div/mod (especially on
// 32-bit platforms). In essence, we do
//
// uint32_t threshold = (0x100000000ull-bound) % bound;
//
// because this version will calculate the same modulus, but the LHS
// value is less than 2^32.
uint32_t threshold = (~bound+1u) % bound;
// Uniformity guarantees that this loop will terminate. In practice, it
// should usually terminate quickly; on average (assuming all bounds are
// equally likely), 82.25% of the time, we can expect it to require just
// one iteration. In the worst case, someone passes a bound of 2^31 + 1
// (i.e., 2147483649), which invalidates almost 50% of the range. In
// practice, bounds are typically small and only a tiny amount of the range
// is eliminated.
for (;;) {
uint32_t r = nextUInt();
if (r >= threshold)
return r % bound;
}
}
/// Generate a single precision floating point value on the interval [0, 1)
float nextFloat() {
/* Trick from MTGP: generate an uniformly distributed
single precision number in [1,2) and subtract 1. */
union {
uint32_t u;
float f;
} x;
x.u = (nextUInt() >> 9) | 0x3f800000UL;
return x.f - 1.0f;
}
/**
* \brief Generate a double precision floating point value on the interval [0, 1)
*
* \remark Since the underlying random number generator produces 32 bit output,
* only the first 32 mantissa bits will be filled (however, the resolution is still
* finer than in \ref nextFloat(), which only uses 23 mantissa bits)
*/
double nextDouble() {
/* Trick from MTGP: generate an uniformly distributed
double precision number in [1,2) and subtract 1. */
union {
uint64_t u;
double d;
} x;
x.u = ((uint64_t) nextUInt() << 20) | 0x3ff0000000000000ULL;
return x.d - 1.0;
}
/**
* \brief Multi-step advance function (jump-ahead, jump-back)
*
* The method used here is based on Brown, "Random Number Generation
* with Arbitrary Stride", Transactions of the American Nuclear
* Society (Nov. 1994). The algorithm is very similar to fast
* exponentiation.
*/
void advance(int64_t delta_) {
uint64_t
cur_mult = PCG32_MULT,
cur_plus = inc,
acc_mult = 1u,
acc_plus = 0u;
/* Even though delta is an unsigned integer, we can pass a signed
integer to go backwards, it just goes "the long way round". */
uint64_t delta = (uint64_t) delta_;
while (delta > 0) {
if (delta & 1) {
acc_mult *= cur_mult;
acc_plus = acc_plus * cur_mult + cur_plus;
}
cur_plus = (cur_mult + 1) * cur_plus;
cur_mult *= cur_mult;
delta /= 2;
}
state = acc_mult * state + acc_plus;
}
/**
* \brief Draw uniformly distributed permutation and permute the
* given STL container
*
* From: Knuth, TAoCP Vol. 2 (3rd 3d), Section 3.4.2
*/
template <typename Iterator> void shuffle(Iterator begin, Iterator end) {
for (Iterator it = end - 1; it > begin; --it)
std::iter_swap(it, begin + nextUInt((uint32_t) (it - begin + 1)));
}
/// Compute the distance between two PCG32 pseudorandom number generators
int64_t operator-(const pcg32 &other) const {
assert(inc == other.inc);
uint64_t
cur_mult = PCG32_MULT,
cur_plus = inc,
cur_state = other.state,
the_bit = 1u,
distance = 0u;
while (state != cur_state) {
if ((state & the_bit) != (cur_state & the_bit)) {
cur_state = cur_state * cur_mult + cur_plus;
distance |= the_bit;
}
assert((state & the_bit) == (cur_state & the_bit));
the_bit <<= 1;
cur_plus = (cur_mult + 1ULL) * cur_plus;
cur_mult *= cur_mult;
}
return (int64_t) distance;
}
/// Equality operator
bool operator==(const pcg32 &other) const { return state == other.state && inc == other.inc; }
/// Inequality operator
bool operator!=(const pcg32 &other) const { return state != other.state || inc != other.inc; }
uint64_t state; // RNG state. All values are possible.
uint64_t inc; // Controls which RNG sequence (stream) is selected. Must *always* be odd.
};
#endif // __PCG32_H

View File

@@ -0,0 +1,29 @@
Copyright (C) 2014 Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Intel Corporation nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,9 @@
# Parallel Stable Sort
This repository contains a parallel stable sort implementation using Thread
Building Blocks. It contains the low-level TBB version (with minor
modifications to remove warnings on various platforms) presented in Arch
D. Robison's article [A Parallel Stable Sort Using C++11 for TBB, Cilk Plus,
and OpenMP][1].
[1] https://software.intel.com/en-us/articles/a-parallel-stable-sort-using-c11-for-tbb-cilk-plus-and-openmp

View File

@@ -0,0 +1,140 @@
/*
Copyright (C) 2014 Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Intel Corporation nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <iterator>
#include <algorithm>
#include <tbb/task.h>
#include "pss_common.h"
namespace pss {
namespace internal {
template<typename RandomAccessIterator1, typename RandomAccessIterator2, typename RandomAccessIterator3, typename Compare>
class merge_task: public tbb::task {
tbb::task* execute();
RandomAccessIterator1 xs, xe;
RandomAccessIterator2 ys, ye;
RandomAccessIterator3 zs;
Compare comp;
bool destroy;
public:
merge_task( RandomAccessIterator1 xs_, RandomAccessIterator1 xe_, RandomAccessIterator2 ys_, RandomAccessIterator2 ye_, RandomAccessIterator3 zs_, bool destroy_, Compare comp_ ) :
xs(xs_), xe(xe_), ys(ys_), ye(ye_), zs(zs_), comp(comp_), destroy(destroy_)
{}
};
template<typename RandomAccessIterator1, typename RandomAccessIterator2, typename RandomAccessIterator3, typename Compare>
tbb::task* merge_task<RandomAccessIterator1,RandomAccessIterator2,RandomAccessIterator3,Compare>::execute() {
const size_t MERGE_CUT_OFF = 2000;
auto n = (xe-xs) + (ye-ys);
if( (size_t) n <= MERGE_CUT_OFF ) {
serial_move_merge( xs, xe, ys, ye, zs, comp );
if( destroy ) {
serial_destroy(xs,xe);
serial_destroy(ys,ye);
}
return NULL;
} else {
RandomAccessIterator1 xm;
RandomAccessIterator2 ym;
if( xe-xs < ye-ys ) {
ym = ys+(ye-ys)/2;
xm = std::upper_bound(xs,xe,*ym,comp);
} else {
xm = xs+(xe-xs)/2;
ym = std::lower_bound(ys,ye,*xm,comp);
}
RandomAccessIterator3 zm = zs + ((xm-xs) + (ym-ys));
tbb::task* right = new( allocate_additional_child_of(*parent()) ) merge_task( xm, xe, ym, ye, zm, destroy, comp );
spawn(*right);
recycle_as_continuation();
xe = xm;
ye = ym;
return this;
}
}
template<typename RandomAccessIterator1, typename RandomAccessIterator2, typename Compare>
class stable_sort_task: public tbb::task {
tbb::task* execute();
RandomAccessIterator1 xs, xe;
RandomAccessIterator2 zs;
Compare comp;
signed char inplace;
public:
stable_sort_task(RandomAccessIterator1 xs_, RandomAccessIterator1 xe_, RandomAccessIterator2 zs_, int inplace_, Compare comp_ ) :
xs(xs_), xe(xe_), zs(zs_), comp(comp_), inplace(inplace_)
{}
};
template<typename RandomAccessIterator1, typename RandomAccessIterator2, typename Compare>
tbb::task* stable_sort_task<RandomAccessIterator1, RandomAccessIterator2, Compare>::execute() {
const size_t SORT_CUT_OFF = 500;
if ((size_t) (xe - xs) <= SORT_CUT_OFF) {
stable_sort_base_case(xs, xe, zs, inplace, comp);
return NULL;
} else {
RandomAccessIterator1 xm = xs + (xe - xs) / 2;
RandomAccessIterator2 zm = zs + (xm - xs);
RandomAccessIterator2 ze = zs + (xe - xs);
task* m;
if (inplace)
m = new (allocate_continuation()) merge_task<RandomAccessIterator2,RandomAccessIterator2,RandomAccessIterator1,Compare>(zs, zm, zm, ze, xs, inplace==2, comp);
else
m = new (allocate_continuation()) merge_task<RandomAccessIterator1,RandomAccessIterator1,RandomAccessIterator2,Compare>(xs, xm, xm, xe, zs, false, comp);
m->set_ref_count(2);
task* right = new(m->allocate_child()) stable_sort_task(xm,xe,zm,!inplace, comp);
spawn(*right);
recycle_as_child_of(*m);
xe=xm;
inplace=!inplace;
return this;
}
}
} // namespace internal
template<typename RandomAccessIterator, typename Compare>
void parallel_stable_sort( RandomAccessIterator xs, RandomAccessIterator xe, Compare comp ) {
typedef typename std::iterator_traits<RandomAccessIterator>::value_type T;
if( internal::raw_buffer z = internal::raw_buffer( sizeof(T)*(xe-xs) ) ) {
using tbb::task;
typedef typename std::iterator_traits<RandomAccessIterator>::value_type T;
internal::raw_buffer buf( sizeof(T)*(xe-xs) );
task::spawn_root_and_wait(*new( task::allocate_root() ) internal::stable_sort_task<RandomAccessIterator,T*,Compare>( xs, xe, (T*)buf.get(), 2, comp ));
} else
// Not enough memory available - fall back on serial sort
std::stable_sort( xs, xe, comp );
}
} // namespace pss

View File

@@ -0,0 +1,106 @@
/*
Copyright (C) 2014 Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Intel Corporation nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <utility>
#include <iterator>
namespace pss {
namespace internal {
//! Destroy sequence [xs,xe)
template<class RandomAccessIterator>
void serial_destroy( RandomAccessIterator zs, RandomAccessIterator ze ) {
typedef typename std::iterator_traits<RandomAccessIterator>::value_type T;
while( zs!=ze ) {
--ze;
(*ze).~T();
}
}
//! Merge sequences [xs,xe) and [ys,ye) to output sequence [zs,(xe-xs)+(ye-ys)), using std::move
template<class RandomAccessIterator1, class RandomAccessIterator2, class RandomAccessIterator3, class Compare>
void serial_move_merge( RandomAccessIterator1 xs, RandomAccessIterator1 xe, RandomAccessIterator2 ys, RandomAccessIterator2 ye, RandomAccessIterator3 zs, Compare comp ) {
if( xs!=xe ) {
if( ys!=ye ) {
for(;;) {
if( comp(*ys,*xs) ) {
*zs = std::move(*ys);
++zs;
if( ++ys==ye ) { break; }
} else {
*zs = std::move(*xs);
++zs;
if( ++xs==xe ) { goto movey; }
}
}
}
ys = xs;
ye = xe;
}
movey:
std::move( ys, ye, zs );
}
template<typename RandomAccessIterator1, typename RandomAccessIterator2, typename Compare>
void stable_sort_base_case( RandomAccessIterator1 xs, RandomAccessIterator1 xe, RandomAccessIterator2 zs, int inplace, Compare comp) {
std::stable_sort( xs, xe, comp );
if( inplace!=2 ) {
RandomAccessIterator2 ze = zs + (xe-xs);
typedef typename std::iterator_traits<RandomAccessIterator2>::value_type T;
if( inplace )
// Initialize the temporary buffer
for( ; zs<ze; ++zs )
new(&*zs) T;
else
// Initialize the temporary buffer and move keys to it.
for( ; zs<ze; ++xs, ++zs )
new(&*zs) T(std::move(*xs));
}
}
//! Raw memory buffer with automatic cleanup.
class raw_buffer {
void* ptr;
public:
//! Try to obtain buffer of given size.
raw_buffer( size_t bytes ) : ptr( operator new(bytes,std::nothrow) ) {}
//! True if buffer was successfully obtained, zero otherwise.
operator bool() const {return ptr;}
//! Return pointer to buffer, or NULL if buffer could not be obtained.
void* get() const {return ptr;}
//! Destroy buffer
~raw_buffer() {operator delete(ptr);}
};
} // namespace internal
} // namespace pss

View File

@@ -0,0 +1,20 @@
RPly 1.1.3 license
Copyright <20> 2003-2013 Diego Nehab.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,376 @@
#ifndef RPLY_H
#define RPLY_H
/* ----------------------------------------------------------------------
* RPly library, read/write PLY files
* Diego Nehab, IMPA
* http://www.impa.br/~diego/software/rply
*
* This library is distributed under the MIT License. See notice
* at the end of this file.
* ---------------------------------------------------------------------- */
#ifdef __cplusplus
extern "C" {
#endif
#define RPLY_VERSION "RPly 1.1.3"
#define RPLY_COPYRIGHT "Copyright (C) 2003-2013 Diego Nehab"
#define RPLY_AUTHORS "Diego Nehab"
/* ----------------------------------------------------------------------
* Types
* ---------------------------------------------------------------------- */
/* structures are opaque */
typedef struct t_ply_ *p_ply;
typedef struct t_ply_element_ *p_ply_element;
typedef struct t_ply_property_ *p_ply_property;
typedef struct t_ply_argument_ *p_ply_argument;
/* ply format mode type */
typedef enum e_ply_storage_mode_ {
PLY_BIG_ENDIAN,
PLY_LITTLE_ENDIAN,
PLY_ASCII,
PLY_DEFAULT /* has to be the last in enum */
} e_ply_storage_mode; /* order matches ply_storage_mode_list */
/* ply data type */
typedef enum e_ply_type {
PLY_INT8, PLY_UINT8, PLY_INT16, PLY_UINT16,
PLY_INT32, PLY_UINT32, PLY_FLOAT32, PLY_FLOAT64,
PLY_CHAR, PLY_UCHAR, PLY_SHORT, PLY_USHORT,
PLY_INT, PLY_UINT, PLY_FLOAT, PLY_DOUBLE,
PLY_LIST /* has to be the last in enum */
} e_ply_type; /* order matches ply_type_list */
/* ----------------------------------------------------------------------
* Error callback prototype
*
* message: error message
* ply: handle returned by ply_open or ply_create
* ---------------------------------------------------------------------- */
typedef void (*p_ply_error_cb)(p_ply ply, const char *message);
/* ----------------------------------------------------------------------
* Gets user data from within an error callback
*
* ply: handle returned by ply_open or ply_create
* idata,pdata: contextual information set in ply_open or ply_create
* ---------------------------------------------------------------------- */
int ply_get_ply_user_data(p_ply ply, void **pdata, long *idata);
/* ----------------------------------------------------------------------
* Opens a PLY file for reading (fails if file is not a PLY file)
*
* name: file name
* error_cb: error callback function
* idata,pdata: contextual information available to users
*
* Returns 1 if successful, 0 otherwise
* ---------------------------------------------------------------------- */
p_ply ply_open(const char *name, p_ply_error_cb error_cb, long idata,
void *pdata);
/* ----------------------------------------------------------------------
* Reads and parses the header of a PLY file returned by ply_open
*
* ply: handle returned by ply_open
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_read_header(p_ply ply);
/* ----------------------------------------------------------------------
* Property reading callback prototype
*
* argument: parameters for property being processed when callback is called
*
* Returns 1 if should continue processing file, 0 if should abort.
* ---------------------------------------------------------------------- */
typedef int (*p_ply_read_cb)(p_ply_argument argument);
/* ----------------------------------------------------------------------
* Sets up callbacks for property reading after header was parsed
*
* ply: handle returned by ply_open
* element_name: element where property is
* property_name: property to associate element with
* read_cb: function to be called for each property value
* pdata/idata: user data that will be passed to callback
*
* Returns 0 if no element or no property in element, returns the
* number of element instances otherwise.
* ---------------------------------------------------------------------- */
long ply_set_read_cb(p_ply ply, const char *element_name,
const char *property_name, p_ply_read_cb read_cb,
void *pdata, long idata);
/* ----------------------------------------------------------------------
* Returns information about the element originating a callback
*
* argument: handle to argument
* element: receives a the element handle (if non-null)
* instance_index: receives the index of the current element instance
* (if non-null)
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_get_argument_element(p_ply_argument argument,
p_ply_element *element, long *instance_index);
/* ----------------------------------------------------------------------
* Returns information about the property originating a callback
*
* argument: handle to argument
* property: receives the property handle (if non-null)
* length: receives the number of values in this property (if non-null)
* value_index: receives the index of current property value (if non-null)
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_get_argument_property(p_ply_argument argument,
p_ply_property *property, long *length, long *value_index);
/* ----------------------------------------------------------------------
* Returns user data associated with callback
*
* pdata: receives a copy of user custom data pointer (if non-null)
* idata: receives a copy of user custom data integer (if non-null)
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_get_argument_user_data(p_ply_argument argument, void **pdata,
long *idata);
/* ----------------------------------------------------------------------
* Returns the value associated with a callback
*
* argument: handle to argument
*
* Returns the current data item
* ---------------------------------------------------------------------- */
double ply_get_argument_value(p_ply_argument argument);
/* ----------------------------------------------------------------------
* Reads all elements and properties calling the callbacks defined with
* calls to ply_set_read_cb
*
* ply: handle returned by ply_open
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_read(p_ply ply);
/* ----------------------------------------------------------------------
* Iterates over all elements by returning the next element.
* Call with NULL to return handle to first element.
*
* ply: handle returned by ply_open
* last: handle of last element returned (NULL for first element)
*
* Returns element if successfull or NULL if no more elements
* ---------------------------------------------------------------------- */
p_ply_element ply_get_next_element(p_ply ply, p_ply_element last);
/* ----------------------------------------------------------------------
* Iterates over all comments by returning the next comment.
* Call with NULL to return pointer to first comment.
*
* ply: handle returned by ply_open
* last: pointer to last comment returned (NULL for first comment)
*
* Returns comment if successfull or NULL if no more comments
* ---------------------------------------------------------------------- */
const char *ply_get_next_comment(p_ply ply, const char *last);
/* ----------------------------------------------------------------------
* Iterates over all obj_infos by returning the next obj_info.
* Call with NULL to return pointer to first obj_info.
*
* ply: handle returned by ply_open
* last: pointer to last obj_info returned (NULL for first obj_info)
*
* Returns obj_info if successfull or NULL if no more obj_infos
* ---------------------------------------------------------------------- */
const char *ply_get_next_obj_info(p_ply ply, const char *last);
/* ----------------------------------------------------------------------
* Returns information about an element
*
* element: element of interest
* name: receives a pointer to internal copy of element name (if non-null)
* ninstances: receives the number of instances of this element (if non-null)
*
* Returns 1 if successfull or 0 otherwise
* ---------------------------------------------------------------------- */
int ply_get_element_info(p_ply_element element, const char** name,
long *ninstances);
/* ----------------------------------------------------------------------
* Iterates over all properties by returning the next property.
* Call with NULL to return handle to first property.
*
* element: handle of element with the properties of interest
* last: handle of last property returned (NULL for first property)
*
* Returns element if successfull or NULL if no more properties
* ---------------------------------------------------------------------- */
p_ply_property ply_get_next_property(p_ply_element element,
p_ply_property last);
/* ----------------------------------------------------------------------
* Returns information about a property
*
* property: handle to property of interest
* name: receives a pointer to internal copy of property name (if non-null)
* type: receives the property type (if non-null)
* length_type: for list properties, receives the scalar type of
* the length field (if non-null)
* value_type: for list properties, receives the scalar type of the value
* fields (if non-null)
*
* Returns 1 if successfull or 0 otherwise
* ---------------------------------------------------------------------- */
int ply_get_property_info(p_ply_property property, const char** name,
e_ply_type *type, e_ply_type *length_type, e_ply_type *value_type);
/* ----------------------------------------------------------------------
* Creates new PLY file
*
* name: file name
* storage_mode: file format mode
*
* Returns handle to PLY file if successfull, NULL otherwise
* ---------------------------------------------------------------------- */
p_ply ply_create(const char *name, e_ply_storage_mode storage_mode,
p_ply_error_cb error_cb, long idata, void *pdata);
/* ----------------------------------------------------------------------
* Adds a new element to the PLY file created by ply_create
*
* ply: handle returned by ply_create
* name: name of new element
* ninstances: number of element of this time in file
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_add_element(p_ply ply, const char *name, long ninstances);
/* ----------------------------------------------------------------------
* Adds a new property to the last element added by ply_add_element
*
* ply: handle returned by ply_create
* name: name of new property
* type: property type
* length_type: scalar type of length field of a list property
* value_type: scalar type of value fields of a list property
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_add_property(p_ply ply, const char *name, e_ply_type type,
e_ply_type length_type, e_ply_type value_type);
/* ----------------------------------------------------------------------
* Adds a new list property to the last element added by ply_add_element
*
* ply: handle returned by ply_create
* name: name of new property
* length_type: scalar type of length field of a list property
* value_type: scalar type of value fields of a list property
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_add_list_property(p_ply ply, const char *name,
e_ply_type length_type, e_ply_type value_type);
/* ----------------------------------------------------------------------
* Adds a new property to the last element added by ply_add_element
*
* ply: handle returned by ply_create
* name: name of new property
* type: property type
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_add_scalar_property(p_ply ply, const char *name, e_ply_type type);
/* ----------------------------------------------------------------------
* Adds a new comment item
*
* ply: handle returned by ply_create
* comment: pointer to string with comment text
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_add_comment(p_ply ply, const char *comment);
/* ----------------------------------------------------------------------
* Adds a new obj_info item
*
* ply: handle returned by ply_create
* comment: pointer to string with obj_info data
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_add_obj_info(p_ply ply, const char *obj_info);
/* ----------------------------------------------------------------------
* Writes the PLY file header after all element and properties have been
* defined by calls to ply_add_element and ply_add_property
*
* ply: handle returned by ply_create
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_write_header(p_ply ply);
/* ----------------------------------------------------------------------
* Writes one property value, in the order they should be written to the
* file. For each element type, write all elements of that type in order.
* For each element, write all its properties in order. For scalar
* properties, just write the value. For list properties, write the length
* and then each of the values.
*
* ply: handle returned by ply_create
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_write(p_ply ply, double value);
/* ----------------------------------------------------------------------
* Closes a PLY file handle. Releases all memory used by handle
*
* ply: handle to be closed.
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_close(p_ply ply);
#ifdef __cplusplus
}
#endif
#endif /* RPLY_H */
/* ----------------------------------------------------------------------
* Copyright (C) 2003-2011 Diego Nehab. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
* ---------------------------------------------------------------------- */

View File

@@ -0,0 +1,63 @@
#pragma once
//#define INSTANT_MESHES_VIS_COLOR
/* clang-format off */
//remeshedge->flag
enum {
REMESH_EDGE_BOUNDARY = (1<<0),
REMESH_EDGE_USE_DIR = (1<<1)
};
/* clang-format on */
typedef struct RemeshVertex {
float co[3], no[3];
#ifdef INSTANT_MESHES_VIS_COLOR
float viscolor[3];
#endif
} RemeshVertex;
// edge constraint
typedef struct RemeshEdge {
int v1, v2, flag;
float dir[3];
} RemeshEdge;
typedef struct RemeshTri {
int v1, v2, v3;
int eflags[3];
} RemeshTri;
typedef struct RemeshOutFace {
int *verts;
int totvert;
} RemeshOutFace;
typedef struct RemeshMesh {
RemeshTri *tris;
RemeshVertex *verts;
RemeshEdge *edges; // list of constrained edges, need not be all edges in the mesh
int tottri, totvert, totedge;
RemeshOutFace *outfaces;
RemeshVertex *outverts;
int *out_scracth;
int totoutface;
int totoutvert;
int goal_faces, iterations;
} RemeshMesh;
#ifdef __cplusplus
extern "C" {
#endif
void instant_meshes_run(RemeshMesh *mesh);
void instant_meshes_finish(RemeshMesh *mesh);
void instant_meshes_set_number_of_threads(int n);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,124 @@
/*
aabb.h -- basic axis-aligned bounding box & ray intersection code
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#pragma once
#include "common.h"
struct Ray {
Vector3f o, d;
Float mint, maxt;
Ray(const Vector3f &o, const Vector3f &d) :
o(o), d(d), mint(0), maxt(std::numeric_limits<Float>::infinity()) { }
Ray(const Vector3f &o, const Vector3f &d, Float mint, Float maxt) :
o(o), d(d), mint(mint), maxt(maxt) { }
Vector3f operator()(Float t) const { return o + t*d; }
};
struct AABB {
Vector3f min, max;
AABB() { clear(); }
AABB(const Vector3f &min, const Vector3f &max) : min(min), max(max) {}
void clear() {
const Float inf = std::numeric_limits<Float>::infinity();
min.setConstant(inf);
max.setConstant(-inf);
}
void expandBy(const Vector3f &p) {
min = min.cwiseMin(p);
max = max.cwiseMax(p);
}
void expandBy(const AABB &aabb) {
min = min.cwiseMin(aabb.min);
max = max.cwiseMax(aabb.max);
}
bool contains(const Vector3f &p) {
return (p.array() >= min.array()).all() &&
(p.array() <= max.array()).all();
}
bool rayIntersect(const Ray &ray) const {
Float nearT = -std::numeric_limits<Float>::infinity();
Float farT = std::numeric_limits<Float>::infinity();
for (int i=0; i<3; i++) {
Float origin = ray.o[i];
Float minVal = min[i], maxVal = max[i];
if (ray.d[i] == 0) {
if (origin < minVal || origin > maxVal)
return false;
} else {
Float t1 = (minVal - origin) / ray.d[i];
Float t2 = (maxVal - origin) / ray.d[i];
if (t1 > t2)
std::swap(t1, t2);
nearT = std::max(t1, nearT);
farT = std::min(t2, farT);
if (!(nearT <= farT))
return false;
}
}
return ray.mint <= farT && nearT <= ray.maxt;
}
Float squaredDistanceTo(const Vector3f &p) const {
Float result = 0;
for (int i=0; i<3; ++i) {
Float value = 0;
if (p[i] < min[i])
value = min[i] - p[i];
else if (p[i] > max[i])
value = p[i] - max[i];
result += value*value;
}
return result;
}
int largestAxis() const {
Vector3f extents = max-min;
if (extents[0] >= extents[1] && extents[0] >= extents[2])
return 0;
else if (extents[1] >= extents[0] && extents[1] >= extents[2])
return 1;
else
return 2;
}
Float surfaceArea() const {
Vector3f d = max - min;
return 2.0f * (d[0]*d[1] + d[0]*d[2] + d[1]*d[2]);
}
Vector3f center() const {
return 0.5f * (min + max);
}
static AABB merge(const AABB &aabb1, const AABB &aabb2) {
return AABB(aabb1.min.cwiseMin(aabb2.min), aabb1.max.cwiseMax(aabb2.max));
}
};

View File

@@ -0,0 +1,339 @@
/*
aabb.cpp -- functionality for creating adjacency matrices together with
uniform or cotangent weights. Also contains data structures
used to store integer variables.
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#include "adjacency.h"
#include "dedge.h"
#include "bvh.h"
#include "meshstats.h"
#include "dset.h"
#include <set>
#include <map>
AdjacencyMatrix generate_adjacency_matrix_uniform(
const MatrixXu &F, const VectorXu &V2E, const VectorXu &E2E,
const VectorXb &nonManifold, const ProgressCallback &progress) {
VectorXu neighborhoodSize(V2E.size() + 1);
cout << "Generating adjacency matrix .. ";
cout.flush();
Timer<> timer;
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t) V2E.size(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t i = range.begin(); i != range.end(); ++i) {
uint32_t edge = V2E[i], stop = edge;
if (nonManifold[i] || edge == INVALID) {
neighborhoodSize[i+1] = 0;
continue;
}
uint32_t nNeighbors = 0;
do {
uint32_t opp = E2E[edge];
if (opp == INVALID) {
nNeighbors += 2;
break;
}
edge = dedge_next_3(opp);
nNeighbors++;
} while (edge != stop);
neighborhoodSize[i+1] = nNeighbors;
}
SHOW_PROGRESS_RANGE(range, V2E.size(), "Generating adjacency matrix (1/2)");
}
);
neighborhoodSize[0] = 0;
for (uint32_t i=0; i<neighborhoodSize.size()-1; ++i)
neighborhoodSize[i+1] += neighborhoodSize[i];
AdjacencyMatrix adj = new Link*[V2E.size() + 1];
uint32_t nLinks = neighborhoodSize[neighborhoodSize.size()-1];
Link *links = new Link[nLinks];
for (uint32_t i=0; i<neighborhoodSize.size(); ++i)
adj[i] = links + neighborhoodSize[i];
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t) V2E.size(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t i = range.begin(); i != range.end(); ++i) {
uint32_t edge = V2E[i], stop = edge;
if (nonManifold[i] || edge == INVALID)
continue;
Link *ptr = adj[i];
int it = 0;
do {
uint32_t base = edge % 3, f = edge / 3;
uint32_t opp = E2E[edge], next = dedge_next_3(opp);
if (it == 0)
*ptr++ = Link(F((base + 2)%3, f));
if (opp == INVALID || next != stop) {
*ptr++ = Link(F((base + 1)%3, f));
if (opp == INVALID)
break;
}
edge = next;
++it;
} while (edge != stop);
}
SHOW_PROGRESS_RANGE(range, V2E.size(), "Generating adjacency matrix (2/2)");
}
);
cout << "done. (took " << timeString(timer.value()) << ")" << endl;
return adj;
}
AdjacencyMatrix
generate_adjacency_matrix_cotan(const MatrixXu &F, const MatrixXf &V,
const VectorXu &V2E, const VectorXu &E2E,
const VectorXb &nonManifold,
const ProgressCallback &progress) {
VectorXu neighborhoodSize(V2E.size() + 1);
cout << "Computing cotangent Laplacian .. ";
cout.flush();
Timer<> timer;
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t) V2E.size(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t i = range.begin(); i != range.end(); ++i) {
uint32_t edge = V2E[i], stop = edge;
if (nonManifold[i] || edge == INVALID) {
neighborhoodSize[i+1] = 0;
continue;
}
uint32_t nNeighbors = 0;
do {
uint32_t opp = E2E[edge];
if (opp == INVALID) {
nNeighbors += 2;
break;
}
edge = dedge_next_3(opp);
nNeighbors++;
} while (edge != stop);
neighborhoodSize[i+1] = nNeighbors;
}
SHOW_PROGRESS_RANGE(range, V2E.size(), "Computing cotangent Laplacian (1/2)");
}
);
neighborhoodSize[0] = 0;
for (uint32_t i=0; i<neighborhoodSize.size()-1; ++i)
neighborhoodSize[i+1] += neighborhoodSize[i];
AdjacencyMatrix adj = new Link*[V2E.size() + 1];
uint32_t nLinks = neighborhoodSize[neighborhoodSize.size()-1];
Link *links = new Link[nLinks];
for (uint32_t i=0; i<neighborhoodSize.size(); ++i)
adj[i] = links + neighborhoodSize[i];
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t)V.cols(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t i = range.begin(); i != range.end(); ++i) {
uint32_t edge = V2E[i], stop = edge;
if (nonManifold[i] || edge == INVALID)
continue;
Link *ptr = adj[i];
int it = 0;
do {
uint32_t f = edge / 3, curr_idx = edge % 3,
next_idx = (curr_idx + 1) % 3,
prev_idx = (curr_idx + 2) % 3;
if (it == 0) {
Vector3f p = V.col(F(next_idx, f)),
d0 = V.col(F(prev_idx, f)) - p,
d1 = V.col(F(curr_idx, f)) - p;
Float cot_weight = 0.0f,
sin_alpha = d0.cross(d1).norm();
if (sin_alpha > RCPOVERFLOW)
cot_weight = d0.dot(d1) / sin_alpha;
uint32_t opp = E2E[dedge_prev_3(edge)];
if (opp != INVALID) {
uint32_t o_f = opp / 3, o_curr_idx = opp % 3,
o_next_idx = (o_curr_idx + 1) % 3,
o_prev_idx = (o_curr_idx + 2) % 3;
p = V.col(F(o_prev_idx, o_f));
d0 = V.col(F(o_curr_idx, o_f)) - p;
d1 = V.col(F(o_next_idx, o_f)) - p;
sin_alpha = d0.cross(d1).norm();
if (sin_alpha > RCPOVERFLOW)
cot_weight += d0.dot(d1) / sin_alpha;
}
*ptr++ = Link(F(prev_idx, f), (float) cot_weight * 0.5f);
}
uint32_t opp = E2E[edge], next = dedge_next_3(opp);
if (opp == INVALID || next != stop) {
Vector3f p = V.col(F(prev_idx, f)),
d0 = V.col(F(curr_idx, f)) - p,
d1 = V.col(F(next_idx, f)) - p;
Float cot_weight = 0.0f,
sin_alpha = d0.cross(d1).norm();
if (sin_alpha > RCPOVERFLOW)
cot_weight = d0.dot(d1) / sin_alpha;
if (opp != INVALID) {
uint32_t o_f = opp / 3, o_curr_idx = opp % 3,
o_next_idx = (o_curr_idx + 1) % 3,
o_prev_idx = (o_curr_idx + 2) % 3;
p = V.col(F(o_prev_idx, o_f));
d0 = V.col(F(o_curr_idx, o_f)) - p;
d1 = V.col(F(o_next_idx, o_f)) - p;
sin_alpha = d0.cross(d1).norm();
if (sin_alpha > RCPOVERFLOW)
cot_weight += d0.dot(d1) / sin_alpha;
}
*ptr++ = Link(F(next_idx, f), (float) cot_weight * 0.5f);
if (opp == INVALID)
break;
}
edge = next;
++it;
} while (edge != stop);
}
SHOW_PROGRESS_RANGE(range, V.cols(), "Computing cotangent Laplacian (2/2)");
}
);
cout << "done. (took " << timeString(timer.value()) << ")" << endl;
return adj;
}
AdjacencyMatrix generate_adjacency_matrix_pointcloud(
MatrixXf &V, MatrixXf &N, const BVH *bvh, MeshStats &stats, uint32_t knn_points,
bool deterministic, const ProgressCallback &progress) {
Timer<> timer;
cout << "Generating adjacency matrix .. ";
cout.flush();
stats.mAverageEdgeLength = bvh->diskRadius();
const Float maxQueryRadius = bvh->diskRadius() * 3;
uint32_t *adj_sets = new uint32_t[V.cols() * (size_t) knn_points];
DisjointSets dset(V.cols());
VectorXu adj_size(V.cols());
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t) V.cols(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
std::vector<std::pair<Float, uint32_t>> result;
for (uint32_t i = range.begin(); i < range.end(); ++i) {
uint32_t *adj_set = adj_sets + (size_t) i * (size_t) knn_points;
memset(adj_set, 0xFF, sizeof(uint32_t) * knn_points);
Float radius = maxQueryRadius;
bvh->findKNearest(V.col(i), N.col(i), knn_points, radius, result);
uint32_t ctr = 0;
for (auto k : result) {
if (k.second == i)
continue;
adj_set[ctr++] = k.second;
dset.unite(k.second, i);
}
adj_size[i] = ctr;
}
SHOW_PROGRESS_RANGE(range, V.cols(), "Generating adjacency matrix");
}
);
std::map<uint32_t, uint32_t> dset_size;
for (uint32_t i=0; i<V.cols(); ++i) {
dset_size[dset.find(i)]++;
uint32_t *adj_set_i = adj_sets + (size_t) i * (size_t) knn_points;
for (uint32_t j=0; j<knn_points; ++j) {
uint32_t k = adj_set_i[j];
if (k == INVALID)
break;
uint32_t *adj_set_k = adj_sets + (size_t) k * (size_t) knn_points;
bool found = false;
for (uint32_t l=0; l<knn_points; ++l) {
uint32_t value = adj_set_k[l];
if (value == i) { found = true; break; }
if (value == INVALID) break;
}
if (!found)
adj_size[k]++;
}
}
size_t nLinks = 0;
for (uint32_t i=0; i<V.cols(); ++i) {
uint32_t dsetSize = dset_size[dset.find(i)];
if (dsetSize < V.cols() * 0.01f) {
adj_size[i] = INVALID;
V.col(i) = Vector3f::Constant(1e6);
continue;
}
nLinks += adj_size[i];
}
cout << "allocating " << memString(sizeof(Link) * nLinks) << " .. ";
cout.flush();
AdjacencyMatrix adj = new Link*[V.size() + 1];
adj[0] = new Link[nLinks];
for (uint32_t i=1; i<=V.cols(); ++i) {
uint32_t size = adj_size[i-1];
if (size == INVALID)
size = 0;
adj[i] = adj[i-1] + size;
}
VectorXu adj_offset(V.cols());
adj_offset.setZero();
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t) V.cols(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t i = range.begin(); i < range.end(); ++i) {
uint32_t *adj_set_i = adj_sets + (size_t) i * (size_t) knn_points;
if (adj_size[i] == INVALID)
continue;
for (uint32_t j=0; j<knn_points; ++j) {
uint32_t k = adj_set_i[j];
if (k == INVALID)
break;
adj[i][atomicAdd(&adj_offset.coeffRef(i), 1)-1] = Link(k);
uint32_t *adj_set_k = adj_sets + (size_t) k * (size_t) knn_points;
bool found = false;
for (uint32_t l=0; l<knn_points; ++l) {
uint32_t value = adj_set_k[l];
if (value == i) { found = true; break; }
if (value == INVALID) break;
}
if (!found)
adj[k][atomicAdd(&adj_offset.coeffRef(k), 1)-1] = Link(i);
}
}
}
);
/* Use a heuristic to estimate some useful quantities for point clouds (this
is a biased estimate due to the kNN queries, but it's convenient and
reasonably accurate) */
stats.mSurfaceArea = M_PI * stats.mAverageEdgeLength*stats.mAverageEdgeLength * 0.5f * V.cols();
cout << "done. (took " << timeString(timer.value()) << ")" << endl;
return adj;
}

View File

@@ -0,0 +1,77 @@
/*
aabb.h -- functionality for creating adjacency matrices together with
uniform or cotangent weights. Also contains data structures
used to store integer variables.
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#pragma once
#include "common.h"
/* Stores integer jumps between nodes of the adjacency matrix */
struct IntegerVariable {
unsigned short rot : 2;
signed short translate_u : 7;
signed short translate_v : 7;
Vector2i shift() const {
return Vector2i(translate_u, translate_v);
}
void setShift(Vector2i &v) {
translate_u = v.x();
translate_v = v.y();
}
};
/* Stores a weighted adjacency matrix entry together with integer variables */
struct Link {
uint32_t id;
float weight;
union {
IntegerVariable ivar[2];
uint32_t ivar_uint32;
};
inline Link() { }
inline Link(uint32_t id) : id(id), weight(1.0f), ivar_uint32(0u) { }
inline Link(uint32_t id, float weight) : id(id), weight(weight), ivar_uint32(0u) { }
inline bool operator<(const Link &link) const { return id < link.id; }
} ;
typedef Link** AdjacencyMatrix;
extern AdjacencyMatrix generate_adjacency_matrix_uniform(
const MatrixXu &F, const VectorXu &V2E,
const VectorXu &E2E, const VectorXb &nonManifold,
const ProgressCallback &progress = ProgressCallback());
extern AdjacencyMatrix generate_adjacency_matrix_cotan(
const MatrixXu &F, const MatrixXf &V, const VectorXu &V2E,
const VectorXu &E2E, const VectorXb &nonManifold,
const ProgressCallback &progress = ProgressCallback());
inline Link &search_adjacency(AdjacencyMatrix &adj, uint32_t i, uint32_t j) {
for (Link* l = adj[i]; l != adj[i+1]; ++l)
if (l->id == j)
return *l;
throw std::runtime_error("search_adjacency: failure!");
}
class BVH;
struct MeshStats;
extern AdjacencyMatrix generate_adjacency_matrix_pointcloud(
MatrixXf &V, MatrixXf &N, const BVH *bvh, MeshStats &stats,
uint32_t knn_points, bool deterministic = false,
const ProgressCallback &progress = ProgressCallback());

View File

@@ -0,0 +1,214 @@
/*
batch.cpp -- command line interface to Instant Meshes
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#include "batch.h"
#include "meshio.h"
#include "dedge.h"
#include "subdivide.h"
#include "meshstats.h"
#include "hierarchy.h"
#include "field.h"
#include "normal.h"
#include "extract.h"
#include "bvh.h"
void batch_process(const std::string &input, const std::string &output,
int rosy, int posy, Float scale, int face_count,
int vertex_count, Float creaseAngle, bool extrinsic,
bool align_to_boundaries, int smooth_iter, int knn_points,
bool pure_quad, bool deterministic) {
cout << endl;
cout << "Running in batch mode:" << endl;
cout << " Input file = " << input << endl;
cout << " Output file = " << output << endl;
cout << " Rotation symmetry type = " << rosy << endl;
cout << " Position symmetry type = " << (posy==3?6:posy) << endl;
cout << " Crease angle threshold = ";
if (creaseAngle > 0)
cout << creaseAngle << endl;
else
cout << "disabled" << endl;
cout << " Extrinsic mode = " << (extrinsic ? "enabled" : "disabled") << endl;
cout << " Align to boundaries = " << (align_to_boundaries ? "yes" : "no") << endl;
cout << " kNN points = " << knn_points << " (only applies to point clouds)"<< endl;
cout << " Fully deterministic = " << (deterministic ? "yes" : "no") << endl;
if (posy == 4)
cout << " Output mode = " << (pure_quad ? "pure quad mesh" : "quad-dominant mesh") << endl;
cout << endl;
MatrixXu F;
MatrixXf V, N;
VectorXf A;
std::set<uint32_t> crease_in, crease_out;
BVH *bvh = nullptr;
AdjacencyMatrix adj = nullptr;
/* Load the input mesh */
load_mesh_or_pointcloud(input, F, V, N);
bool pointcloud = F.size() == 0;
Timer<> timer;
MeshStats stats = compute_mesh_stats(F, V, deterministic);
if (pointcloud) {
bvh = new BVH(&F, &V, &N, stats.mAABB);
bvh->build();
adj = generate_adjacency_matrix_pointcloud(V, N, bvh, stats, knn_points, deterministic);
A.resize(V.cols());
A.setConstant(1.0f);
}
if (scale < 0 && vertex_count < 0 && face_count < 0) {
cout << "No target vertex count/face count/scale argument provided. "
"Setting to the default of 1/16 * input vertex count." << endl;
vertex_count = V.cols() / 16;
}
if (scale > 0) {
Float face_area = posy == 4 ? (scale*scale) : (std::sqrt(3.f)/4.f*scale*scale);
face_count = stats.mSurfaceArea / face_area;
vertex_count = posy == 4 ? face_count : (face_count / 2);
} else if (face_count > 0) {
Float face_area = stats.mSurfaceArea / face_count;
vertex_count = posy == 4 ? face_count : (face_count / 2);
scale = posy == 4 ? std::sqrt(face_area) : (2*std::sqrt(face_area * std::sqrt(1.f/3.f)));
} else if (vertex_count > 0) {
face_count = posy == 4 ? vertex_count : (vertex_count * 2);
Float face_area = stats.mSurfaceArea / face_count;
scale = posy == 4 ? std::sqrt(face_area) : (2*std::sqrt(face_area * std::sqrt(1.f/3.f)));
}
cout << "Output mesh goals (approximate)" << endl;
cout << " Vertex count = " << vertex_count << endl;
cout << " Face count = " << face_count << endl;
cout << " Edge length = " << scale << endl;
MultiResolutionHierarchy mRes;
if (!pointcloud) {
/* Subdivide the mesh if necessary */
VectorXu V2E, E2E;
VectorXb boundary, nonManifold;
if (stats.mMaximumEdgeLength*2 > scale || stats.mMaximumEdgeLength > stats.mAverageEdgeLength * 2) {
cout << "Input mesh is too coarse for the desired output edge length "
"(max input mesh edge length=" << stats.mMaximumEdgeLength
<< "), subdividing .." << endl;
build_dedge(F, V, V2E, E2E, boundary, nonManifold);
subdivide(F, V, V2E, E2E, boundary, nonManifold, std::min(scale/2, (Float) stats.mAverageEdgeLength*2), deterministic);
}
/* Compute a directed edge data structure */
build_dedge(F, V, V2E, E2E, boundary, nonManifold);
/* Compute adjacency matrix */
adj = generate_adjacency_matrix_uniform(F, V2E, E2E, nonManifold);
/* Compute vertex/crease normals */
if (creaseAngle >= 0)
generate_crease_normals(F, V, V2E, E2E, boundary, nonManifold, creaseAngle, N, crease_in);
else
generate_smooth_normals(F, V, V2E, E2E, nonManifold, N);
/* Compute dual vertex areas */
compute_dual_vertex_areas(F, V, V2E, E2E, nonManifold, A);
mRes.setE2E(std::move(E2E));
}
/* Build multi-resolution hierarrchy */
mRes.setAdj(std::move(adj));
mRes.setF(std::move(F));
mRes.setV(std::move(V));
mRes.setA(std::move(A));
mRes.setN(std::move(N));
mRes.setScale(scale);
mRes.build(deterministic);
mRes.resetSolution();
if (align_to_boundaries && !pointcloud) {
mRes.clearConstraints();
for (uint32_t i=0; i<3*mRes.F().cols(); ++i) {
if (mRes.E2E()[i] == INVALID) {
uint32_t i0 = mRes.F()(i%3, i/3);
uint32_t i1 = mRes.F()((i+1)%3, i/3);
Vector3f p0 = mRes.V().col(i0), p1 = mRes.V().col(i1);
Vector3f edge = p1-p0;
if (edge.squaredNorm() > 0) {
edge.normalize();
mRes.CO().col(i0) = p0;
mRes.CO().col(i1) = p1;
mRes.CQ().col(i0) = mRes.CQ().col(i1) = edge;
mRes.CQw()[i0] = mRes.CQw()[i1] = mRes.COw()[i0] =
mRes.COw()[i1] = 1.0f;
}
}
}
mRes.propagateConstraints(rosy, posy);
}
if (bvh) {
bvh->setData(&mRes.F(), &mRes.V(), &mRes.N());
} else if (smooth_iter > 0) {
bvh = new BVH(&mRes.F(), &mRes.V(), &mRes.N(), stats.mAABB);
bvh->build();
}
cout << "Preprocessing is done. (total time excluding file I/O: "
<< timeString(timer.reset()) << ")" << endl;
Optimizer optimizer(mRes, false);
optimizer.setRoSy(rosy);
optimizer.setPoSy(posy);
optimizer.setExtrinsic(extrinsic);
cout << "Optimizing orientation field .. ";
cout.flush();
optimizer.optimizeOrientations(-1);
optimizer.notify();
optimizer.wait();
cout << "done. (took " << timeString(timer.reset()) << ")" << endl;
std::map<uint32_t, uint32_t> sing;
compute_orientation_singularities(mRes, sing, extrinsic, rosy);
cout << "Orientation field has " << sing.size() << " singularities." << endl;
timer.reset();
cout << "Optimizing position field .. ";
cout.flush();
optimizer.optimizePositions(-1);
optimizer.notify();
optimizer.wait();
cout << "done. (took " << timeString(timer.reset()) << ")" << endl;
//std::map<uint32_t, Vector2i> pos_sing;
//compute_position_singularities(mRes, sing, pos_sing, extrinsic, rosy, posy);
//cout << "Position field has " << pos_sing.size() << " singularities." << endl;
//timer.reset();
optimizer.shutdown();
MatrixXf O_extr, N_extr, Nf_extr;
std::vector<std::vector<TaggedLink>> adj_extr;
extract_graph(mRes, extrinsic, rosy, posy, adj_extr, O_extr, N_extr,
crease_in, crease_out, deterministic);
MatrixXu F_extr;
extract_faces(adj_extr, O_extr, N_extr, Nf_extr, F_extr, posy,
mRes.scale(), crease_out, true, pure_quad, bvh, smooth_iter);
cout << "Extraction is done. (total time: " << timeString(timer.reset()) << ")" << endl;
write_mesh(output, F_extr, O_extr, MatrixXf(), Nf_extr);
if (bvh)
delete bvh;
}

View File

@@ -0,0 +1,22 @@
/*
batch.h -- command line interface to Instant Meshes
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#pragma once
#include "common.h"
extern void batch_process(const std::string &input, const std::string &output,
int rosy, int posy, Float scale, int face_count,
int vertex_count, Float creaseAngle, bool extrinsic,
bool align_to_boundaries, int smooth_iter,
int knn_points, bool dominant, bool deterministic);

View File

@@ -0,0 +1,898 @@
/*
bvh.cpp -- bounding volume hierarchy for fast ray-intersection queries
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#include "bvh.h"
struct Bins {
static const int BIN_COUNT = 8;
Bins() { memset(counts, 0, sizeof(uint32_t) * BIN_COUNT); }
uint32_t counts[BIN_COUNT];
AABB bounds[BIN_COUNT];
};
struct BVHBuildTask : public tbb::task {
enum { SERIAL_THRESHOLD = 32 };
BVH &bvh;
uint32_t node_idx;
uint32_t *start, *end, *temp;
BVHBuildTask(BVH &bvh, uint32_t node_idx, uint32_t *start, uint32_t *end, uint32_t *temp)
: bvh(bvh), node_idx(node_idx), start(start), end(end), temp(temp) { }
task *execute() {
const MatrixXu &F = *bvh.mF;
const MatrixXf &V = *bvh.mV;
bool pointcloud = F.size() == 0;
uint32_t size = end-start, total_size = pointcloud ? V.cols() : F.cols();
BVHNode &node = bvh.mNodes[node_idx];
if (size < SERIAL_THRESHOLD) {
tbb::blocked_range<uint32_t> range(start-bvh.mIndices, end-bvh.mIndices);
const ProgressCallback &progress = bvh.mProgress;
SHOW_PROGRESS_RANGE(range, total_size, "Constructing Bounding Volume Hierarchy");
execute_serially(bvh, node_idx, start, end, temp);
return nullptr;
}
int axis = node.aabb.largestAxis();
Float min = node.aabb.min[axis], max = node.aabb.max[axis],
inv_bin_size = Bins::BIN_COUNT / (max-min);
Bins bins = tbb::parallel_reduce(
tbb::blocked_range<uint32_t>(0u, size, GRAIN_SIZE),
Bins(),
[&](const tbb::blocked_range<uint32_t> &range, Bins result) {
for (uint32_t i = range.begin(); i != range.end(); ++i) {
uint32_t f = start[i];
Float centroid = pointcloud ? V(axis, f)
: ((1.0f / 3.0f) * (V(axis, F(0, f)) +
V(axis, F(1, f)) +
V(axis, F(2, f))));
int index = std::min(std::max(
(int) ((centroid - min) * inv_bin_size), 0),
(Bins::BIN_COUNT - 1));
result.counts[index]++;
AABB &bin_bounds = result.bounds[index];
if (!pointcloud) {
bin_bounds.expandBy(V.col(F(0, f)));
bin_bounds.expandBy(V.col(F(1, f)));
bin_bounds.expandBy(V.col(F(2, f)));
} else {
bin_bounds.expandBy(V.col(f));
}
}
return result;
},
[](const Bins &b1, const Bins &b2) {
Bins result;
for (int i=0; i < Bins::BIN_COUNT; ++i) {
result.counts[i] = b1.counts[i] + b2.counts[i];
result.bounds[i] = AABB::merge(b1.bounds[i], b2.bounds[i]);
}
return result;
}
);
AABB bounds_left[Bins::BIN_COUNT];
bounds_left[0] = bins.bounds[0];
for (int i=1; i<Bins::BIN_COUNT; ++i) {
bins.counts[i] += bins.counts[i-1];
bounds_left[i] = AABB::merge(bounds_left[i-1], bins.bounds[i]);
}
AABB bounds_right = bins.bounds[Bins::BIN_COUNT-1];
int64_t best_index = -1;
Float best_cost = BVH::T_tri * size;
Float tri_factor = BVH::T_tri / node.aabb.surfaceArea();
AABB best_bounds_right;
for (int i=Bins::BIN_COUNT - 2; i >= 0; --i) {
uint32_t prims_left = bins.counts[i], prims_right = (end - start) - bins.counts[i];
Float sah_cost = 2.0f * BVH::T_aabb +
tri_factor * (prims_left * bounds_left[i].surfaceArea() +
prims_right * bounds_right.surfaceArea());
if (sah_cost < best_cost) {
best_cost = sah_cost;
best_index = i;
best_bounds_right = bounds_right;
}
bounds_right = AABB::merge(bounds_right, bins.bounds[i]);
}
if (best_index == -1) {
/* Could not find a good split plane -- retry with
more careful serial code just to be sure.. */
execute_serially(bvh, node_idx, start, end, temp);
return nullptr;
}
uint32_t left_count = bins.counts[best_index];
int node_idx_left = node_idx+1;
int node_idx_right = node_idx+2*left_count;
bvh.mNodes[node_idx_left ].aabb = bounds_left[best_index];
bvh.mNodes[node_idx_right].aabb = best_bounds_right;
node.inner.rightChild = node_idx_right;
node.inner.unused = 0;
std::atomic<uint32_t> offset_left(0), offset_right(bins.counts[best_index]);
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, size, GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
uint32_t count_left = 0, count_right = 0;
for (uint32_t i = range.begin(); i != range.end(); ++i) {
uint32_t f = start[i];
Float centroid = pointcloud ? V(axis, f)
: ((1.0f / 3.0f) * (V(axis, F(0, f)) +
V(axis, F(1, f)) +
V(axis, F(2, f))));
int index = (int) ((centroid - min) * inv_bin_size);
(index <= best_index ? count_left : count_right)++;
}
uint32_t idx_l = offset_left.fetch_add(count_left);
uint32_t idx_r = offset_right.fetch_add(count_right);
for (uint32_t i = range.begin(); i != range.end(); ++i) {
uint32_t f = start[i];
Float centroid = pointcloud ? V(axis, f)
: ((1.0f / 3.0f) * (V(axis, F(0, f)) +
V(axis, F(1, f)) +
V(axis, F(2, f))));
int index = (int) ((centroid - min) * inv_bin_size);
if (index <= best_index)
temp[idx_l++] = f;
else
temp[idx_r++] = f;
}
}
);
memcpy(start, temp, size * sizeof(uint32_t));
assert(offset_left == left_count && offset_right == size);
/* Create an empty parent task */
tbb::task& c = *new (allocate_continuation()) tbb::empty_task;
c.set_ref_count(2);
/* Post right subtree to scheduler */
BVHBuildTask &b = *new (c.allocate_child())
BVHBuildTask(bvh, node_idx_right, start + left_count,
end, temp + left_count);
spawn(b);
/* Directly start working on left subtree */
recycle_as_child_of(c);
node_idx = node_idx_left;
end = start + left_count;
return this;
}
static void execute_serially(BVH &bvh, uint32_t node_idx, uint32_t *start, uint32_t *end, uint32_t *temp) {
uint32_t size = end-start;
BVHNode &node = bvh.mNodes[node_idx];
const MatrixXu &F = *bvh.mF;
const MatrixXf &V = *bvh.mV;
Float best_cost = BVH::T_tri * size;
int64_t best_index = -1, best_axis = -1;
float *left_areas = (float *) temp;
bool pointcloud = F.size() == 0;
for (int axis=0; axis<3; ++axis) {
if (pointcloud) {
std::sort(start, end, [&](uint32_t f1, uint32_t f2) {
return V(axis, f1) < V(axis, f2);
});
} else {
std::sort(start, end, [&](uint32_t f1, uint32_t f2) {
return
(V(axis, F(0, f1)) + V(axis, F(1, f1)) + V(axis, F(2, f1))) <
(V(axis, F(0, f2)) + V(axis, F(1, f2)) + V(axis, F(2, f2)));
});
}
AABB aabb;
for (uint32_t i = 0; i<size; ++i) {
uint32_t f = *(start + i);
if (pointcloud) {
aabb.expandBy(V.col(f));
} else {
aabb.expandBy(V.col(F(0, f)));
aabb.expandBy(V.col(F(1, f)));
aabb.expandBy(V.col(F(2, f)));
}
left_areas[i] = (float) aabb.surfaceArea();
}
if (axis == 0)
node.aabb = aabb;
aabb.clear();
Float tri_factor = BVH::T_tri / node.aabb.surfaceArea();
for (uint32_t i = size-1; i>=1; --i) {
uint32_t f = *(start + i);
if (pointcloud) {
aabb.expandBy(V.col(f));
} else {
aabb.expandBy(V.col(F(0, f)));
aabb.expandBy(V.col(F(1, f)));
aabb.expandBy(V.col(F(2, f)));
}
float left_area = left_areas[i-1];
float right_area = aabb.surfaceArea();
uint32_t prims_left = i;
uint32_t prims_right = size-i;
Float sah_cost = 2.0f * BVH::T_aabb +
tri_factor * (prims_left * left_area +
prims_right * right_area);
if (sah_cost < best_cost) {
best_cost = sah_cost;
best_index = i;
best_axis = axis;
}
}
}
if (best_index == -1) {
/* Splitting does not reduce the cost, make a leaf */
node.leaf.flag = 1;
node.leaf.start = start - bvh.mIndices;
node.leaf.size = size;
return;
}
if (pointcloud) {
std::sort(start, end, [&](uint32_t f1, uint32_t f2) {
return V(best_axis, f1) < V(best_axis, f2);
});
} else {
std::sort(start, end, [&](uint32_t f1, uint32_t f2) {
return
(V(best_axis, F(0, f1)) + V(best_axis, F(1, f1)) + V(best_axis, F(2, f1))) <
(V(best_axis, F(0, f2)) + V(best_axis, F(1, f2)) + V(best_axis, F(2, f2)));
});
}
uint32_t left_count = best_index;
int node_idx_left = node_idx+1;
int node_idx_right = node_idx+2*left_count;
node.inner.rightChild = node_idx_right;
node.inner.unused = 0;
execute_serially(bvh, node_idx_left, start, start + left_count, temp);
execute_serially(bvh, node_idx_right, start+left_count, end, temp + left_count);
}
};
BVH::BVH(const MatrixXu *F, const MatrixXf *V, const MatrixXf *N, const AABB &aabb)
: mIndices(nullptr), mF(F), mV(V), mN(N), mDiskRadius(0.f) {
if (mF->size() > 0) {
mNodes.resize(2*mF->cols());
memset(mNodes.data(), 0, sizeof(BVHNode) * mNodes.size());
mNodes[0].aabb = aabb;
mIndices = new uint32_t[mF->cols()];
} else if (mV->size() > 0) {
mNodes.resize(2*mV->cols());
memset(mNodes.data(), 0, sizeof(BVHNode) * mNodes.size());
mNodes[0].aabb = aabb;
mIndices = new uint32_t[mV->cols()];
}
}
void BVH::build(const ProgressCallback &progress) {
if (mF->cols() == 0 && mV->cols() == 0)
return;
mProgress = progress;
#if defined(SINGLE_PRECISION)
if (sizeof(BVHNode) != 32)
throw std::runtime_error("BVH Node is not packed! Investigate compiler settings.");
#endif
cout << "Constructing Bounding Volume Hierarchy .. ";
cout.flush();
bool pointcloud = mF->size() == 0;
uint32_t total_size = pointcloud ? mV->cols() : mF->cols();
for (uint32_t i = 0; i < total_size; ++i)
mIndices[i] = i;
Timer<> timer;
uint32_t *temp = new uint32_t[total_size];
BVHBuildTask& task = *new(tbb::task::allocate_root())
BVHBuildTask(*this, 0u, mIndices, mIndices + total_size, temp);
tbb::task::spawn_root_and_wait(task);
delete[] temp;
std::pair<Float, uint32_t> stats = statistics();
cout << "done. ("
<< "SAH cost = " << stats.first << ", "
<< "nodes = " << stats.second << ", "
<< "took " << timeString(timer.reset())
<< ")" << endl;
cout.precision(4);
cout << "Compressing BVH node storage to "
<< 100 * stats.second / (float) mNodes.size() << "% of its original size .. ";
cout.flush();
std::vector<BVHNode> compressed(stats.second);
std::vector<uint32_t> skipped_accum(mNodes.size());
for (int64_t i = stats.second-1, j = mNodes.size(), skipped = 0; i >= 0; --i) {
while (mNodes[--j].isUnused())
skipped++;
BVHNode &new_node = compressed[i];
new_node = mNodes[j];
skipped_accum[j] = skipped;
if (new_node.isInner()) {
new_node.inner.rightChild =
i + new_node.inner.rightChild - j -
(skipped - skipped_accum[new_node.inner.rightChild]);
}
}
mNodes = std::move(compressed);
cout << "done. (took " << timeString(timer.value()) << ")" << endl;
if (pointcloud) {
cout << "Assigning disk radius .. ";
cout.flush();
auto map = [&](const tbb::blocked_range<uint32_t> &range, double radius_sum) -> double {
std::vector<std::pair<Float, uint32_t>> result;
for (uint32_t i = range.begin(); i < range.end(); ++i) {
Float radius = std::numeric_limits<double>::infinity();
if (findNearest(mV->col(i), radius) != (uint32_t) -1)
radius_sum += radius;
}
SHOW_PROGRESS_RANGE(range, mV->cols(), "Assigning disk radius");
return radius_sum;
};
auto reduce = [](double radius_sum1, double radius_sum2) -> double {
return radius_sum1 + radius_sum2;
};
tbb::blocked_range<uint32_t> range(0u, (uint32_t) mV->cols(), GRAIN_SIZE);
mDiskRadius = tbb::parallel_deterministic_reduce(range, 0, map, reduce) / (double) range.size();
mDiskRadius *= 3;
refitBoundingBoxes();
cout << "done. (took " << timeString(timer.value()) << ")" << endl;
}
mProgress = nullptr;
}
bool BVH::rayIntersect(Ray ray, uint32_t &idx, Float &t, Vector2f *uv) const {
if (mNodes.empty())
return false;
uint32_t node_idx = 0, stack[64];
uint32_t stack_idx = 0;
bool hit = false;
t = std::numeric_limits<Float>::infinity();
if (mF->size() > 0) {
while (true) {
const BVHNode &node = mNodes[node_idx];
if (!node.aabb.rayIntersect(ray)) {
if (stack_idx == 0)
break;
node_idx = stack[--stack_idx];
continue;
}
if (node.isInner()) {
stack[stack_idx++] = node.inner.rightChild;
node_idx++;
assert(stack_idx<64);
} else {
Float _t;
Vector2f _uv;
for (uint32_t i = node.start(), end = node.end(); i < end; ++i) {
if (rayIntersectTri(ray, mIndices[i], _t, _uv)) {
idx = mIndices[i];
t = ray.maxt = _t;
hit = true;
if (uv)
*uv = _uv;
}
}
if (stack_idx == 0)
break;
node_idx = stack[--stack_idx];
continue;
}
}
} else {
if (uv)
*uv = Vector2f::Zero();
while (true) {
const BVHNode &node = mNodes[node_idx];
if (!node.aabb.rayIntersect(ray)) {
if (stack_idx == 0)
break;
node_idx = stack[--stack_idx];
continue;
}
if (node.isInner()) {
stack[stack_idx++] = node.inner.rightChild;
node_idx++;
assert(stack_idx<64);
} else {
Float _t;
for (uint32_t i = node.start(), end = node.end(); i < end; ++i) {
if (rayIntersectDisk(ray, mIndices[i], _t)) {
idx = mIndices[i];
t = ray.maxt = _t;
hit = true;
}
}
if (stack_idx == 0)
break;
node_idx = stack[--stack_idx];
continue;
}
}
}
return hit;
}
bool BVH::rayIntersect(Ray ray) const {
if (mNodes.empty())
return false;
uint32_t node_idx = 0, stack[64];
uint32_t stack_idx = 0;
if (mF->size() > 0) {
while (true) {
const BVHNode &node = mNodes[node_idx];
if (!node.aabb.rayIntersect(ray)) {
if (stack_idx == 0)
break;
node_idx = stack[--stack_idx];
continue;
}
if (node.isInner()) {
stack[stack_idx++] = node.inner.rightChild;
node_idx++;
assert(stack_idx<64);
} else {
Float t;
Vector2f uv;
for (uint32_t i = node.start(), end = node.end(); i < end; ++i)
if (rayIntersectTri(ray, mIndices[i], t, uv))
return true;
if (stack_idx == 0)
break;
node_idx = stack[--stack_idx];
continue;
}
}
} else {
while (true) {
const BVHNode &node = mNodes[node_idx];
if (!node.aabb.rayIntersect(ray)) {
if (stack_idx == 0)
break;
node_idx = stack[--stack_idx];
continue;
}
if (node.isInner()) {
stack[stack_idx++] = node.inner.rightChild;
node_idx++;
assert(stack_idx<64);
} else {
Float t;
for (uint32_t i = node.start(), end = node.end(); i < end; ++i)
if (rayIntersectDisk(ray, mIndices[i], t))
return true;
if (stack_idx == 0)
break;
node_idx = stack[--stack_idx];
continue;
}
}
}
return false;
}
void BVH::findNearestWithRadius(const Vector3f &p, Float radius,
std::vector<uint32_t> &result,
bool includeSelf) const {
result.clear();
uint32_t node_idx = 0, stack[64];
uint32_t stack_idx = 0;
Float radius2 = radius*radius;
while (true) {
const BVHNode &node = mNodes[node_idx];
if (node.aabb.squaredDistanceTo(p) > radius2) {
if (stack_idx == 0)
break;
node_idx = stack[--stack_idx];
continue;
}
if (node.isInner()) {
stack[stack_idx++] = node.inner.rightChild;
node_idx++;
assert(stack_idx<64);
} else {
uint32_t start = node.leaf.start, end = start + node.leaf.size;
for (uint32_t i = start; i < end; ++i) {
uint32_t f = mIndices[i];
Vector3f pointPos = Vector3f::Zero();
if (mF->size() > 0) {
for (int j=0; j<3; ++j)
pointPos += mV->col((*mF)(j, f));
pointPos *= 1.0f / 3.0f;
} else {
pointPos = mV->col(f);
}
Float pointDist2 = (pointPos-p).squaredNorm();
if (pointDist2 < radius2 && (pointDist2 != 0 || includeSelf))
result.push_back(f);
}
if (stack_idx == 0)
break;
node_idx = stack[--stack_idx];
continue;
}
}
}
uint32_t BVH::findNearest(const Vector3f &p, Float &radius, bool includeSelf) const {
uint32_t node_idx = 0, stack[64];
uint32_t stack_idx = 0;
Float radius2 = radius*radius;
uint32_t result = (uint32_t) -1;
while (true) {
const BVHNode &node = mNodes[node_idx];
if (node.aabb.squaredDistanceTo(p) > radius2) {
if (stack_idx == 0)
break;
node_idx = stack[--stack_idx];
continue;
}
if (node.isInner()) {
uint32_t left = node_idx + 1, right = node.inner.rightChild;
Float distLeft = mNodes[left].aabb.squaredDistanceTo(p);
Float distRight = mNodes[right].aabb.squaredDistanceTo(p);
if (distLeft < distRight) {
node_idx = left;
if (distRight < radius2)
stack[stack_idx++] = right;
} else {
node_idx = right;
if (distLeft < radius2)
stack[stack_idx++] = left;
}
assert(stack_idx<64);
} else {
uint32_t start = node.leaf.start, end = start + node.leaf.size;
for (uint32_t i = start; i < end; ++i) {
uint32_t f = mIndices[i];
Vector3f pointPos = Vector3f::Zero();
if (mF->size() > 0) {
for (int j=0; j<3; ++j)
pointPos += mV->col((*mF)(j, f));
pointPos *= 1.0f / 3.0f;
} else {
pointPos = mV->col(f);
}
Float pointDist2 = (pointPos-p).squaredNorm();
if (pointDist2 < radius2 && (pointDist2 != 0 || includeSelf)) {
radius2 = pointDist2;
result = f;
}
}
if (stack_idx == 0)
break;
node_idx = stack[--stack_idx];
continue;
}
}
radius = std::sqrt(radius2);
return result;
}
void BVH::findKNearest(const Vector3f &p, uint32_t k, Float &radius,
std::vector<std::pair<Float, uint32_t>> &result,
bool includeSelf) const {
result.clear();
uint32_t node_idx = 0, stack[64];
uint32_t stack_idx = 0;
Float radius2 = radius*radius;
bool isHeap = false;
auto comp = [](const std::pair<Float, uint32_t> &v1, const std::pair<Float, uint32_t> &v2) {
return v1.first < v2.first;
};
while (true) {
const BVHNode &node = mNodes[node_idx];
if (node.aabb.squaredDistanceTo(p) > radius2) {
if (stack_idx == 0)
break;
node_idx = stack[--stack_idx];
continue;
}
if (node.isInner()) {
uint32_t left = node_idx + 1, right = node.inner.rightChild;
Float distLeft = mNodes[left].aabb.squaredDistanceTo(p);
Float distRight = mNodes[right].aabb.squaredDistanceTo(p);
if (distLeft < distRight) {
node_idx = left;
if (distRight < radius2)
stack[stack_idx++] = right;
} else {
node_idx = right;
if (distLeft < radius2)
stack[stack_idx++] = left;
}
assert(stack_idx<64);
} else {
uint32_t start = node.leaf.start, end = start + node.leaf.size;
for (uint32_t i = start; i < end; ++i) {
uint32_t f = mIndices[i];
Vector3f pointPos = Vector3f::Zero();
if (mF->size() > 0) {
for (int j=0; j<3; ++j)
pointPos += mV->col((*mF)(j, f));
pointPos *= 1.0f / 3.0f;
} else {
pointPos = mV->col(f);
}
Float pointDist2 = (pointPos-p).squaredNorm();
if (pointDist2 < radius2 && (pointDist2 != 0 || includeSelf)) {
if (result.size() < k) {
result.push_back(std::make_pair(pointDist2, f));
} else {
if (!isHeap) {
/* Establish the max-heap property */
std::make_heap(result.begin(), result.end(), comp);
isHeap = true;
}
result.push_back(std::make_pair(pointDist2, f));
std::push_heap(result.begin(), result.end(), comp);
std::pop_heap(result.begin(), result.end(), comp);
result.pop_back();
/* Reduce the search radius accordingly */
radius2 = result[0].first;
}
}
}
if (stack_idx == 0)
break;
node_idx = stack[--stack_idx];
continue;
}
}
radius = std::sqrt(radius2);
}
void BVH::findKNearest(const Vector3f &p, const Vector3f &n, uint32_t k,
Float &radius,
std::vector<std::pair<Float, uint32_t> > &result,
Float angleThresh, bool includeSelf) const {
result.clear();
uint32_t node_idx = 0, stack[64];
uint32_t stack_idx = 0;
Float radius2 = radius*radius;
bool isHeap = false;
angleThresh = std::cos(angleThresh * M_PI/180);
auto comp = [](const std::pair<Float, uint32_t> &v1, const std::pair<Float, uint32_t> &v2) {
return v1.first < v2.first;
};
while (true) {
const BVHNode &node = mNodes[node_idx];
if (node.aabb.squaredDistanceTo(p) > radius2) {
if (stack_idx == 0)
break;
node_idx = stack[--stack_idx];
continue;
}
if (node.isInner()) {
uint32_t left = node_idx + 1, right = node.inner.rightChild;
Float distLeft = mNodes[left].aabb.squaredDistanceTo(p);
Float distRight = mNodes[right].aabb.squaredDistanceTo(p);
if (distLeft < distRight) {
node_idx = left;
if (distRight < radius2)
stack[stack_idx++] = right;
} else {
node_idx = right;
if (distLeft < radius2)
stack[stack_idx++] = left;
}
assert(stack_idx<64);
} else {
uint32_t start = node.leaf.start, end = start + node.leaf.size;
for (uint32_t i = start; i < end; ++i) {
uint32_t f = mIndices[i];
Vector3f pointPos = Vector3f::Zero();
if (mF->size() > 0) {
for (int j=0; j<3; ++j)
pointPos += mV->col((*mF)(j, f));
pointPos *= 1.0f / 3.0f;
} else {
pointPos = mV->col(f);
}
Vector3f pointNormal = Vector3f::Zero();
if (mF->size() > 0) {
for (int j=0; j<3; ++j)
pointNormal += mN->col((*mF)(j, f));
} else {
pointNormal = mN->col(f);
}
Float pointDist2 = (pointPos-p).squaredNorm();
if (pointDist2 < radius2 && (pointDist2 != 0 || includeSelf) && pointNormal.dot(n) > angleThresh) {
if (result.size() < k) {
result.push_back(std::make_pair(pointDist2, f));
} else {
if (!isHeap) {
/* Establish the max-heap property */
std::make_heap(result.begin(), result.end(), comp);
isHeap = true;
}
result.push_back(std::make_pair(pointDist2, f));
std::push_heap(result.begin(), result.end(), comp);
std::pop_heap(result.begin(), result.end(), comp);
result.pop_back();
/* Reduce the search radius accordingly */
radius2 = result[0].first;
}
}
}
if (stack_idx == 0)
break;
node_idx = stack[--stack_idx];
continue;
}
}
radius = std::sqrt(radius2);
}
bool BVH::rayIntersectTri(const Ray &ray, uint32_t i, Float &t, Vector2f &uv) const {
const Vector3f &p0 = mV->col((*mF)(0, i)),
&p1 = mV->col((*mF)(1, i)),
&p2 = mV->col((*mF)(2, i));
Vector3f edge1 = p1 - p0, edge2 = p2 - p0;
Vector3f pvec = ray.d.cross(edge2);
Float det = edge1.dot(pvec);
if (det == 0.0f)
return false;
Float inv_det = 1.0f / det;
Vector3f tvec = ray.o - p0;
Float u = tvec.dot(pvec) * inv_det;
if (u < 0.0f || u > 1.0f)
return false;
Vector3f qvec = tvec.cross(edge1);
Float v = ray.d.dot(qvec) * inv_det;
if (v < 0.0f || u + v > 1.0f)
return false;
Float tempT = edge2.dot(qvec) * inv_det;
if (tempT < ray.mint || tempT > ray.maxt)
return false;
t = tempT;
uv << u, v;
return true;
}
bool BVH::rayIntersectDisk(const Ray &ray, uint32_t i, Float &t) const {
Vector3f v = mV->col(i), n = mN->col(i);
Float dp = ray.d.dot(n);
if (std::abs(dp) < RCPOVERFLOW)
return false;
t = (n.dot(v) - n.dot(ray.o)) / dp;
return (ray(t)-v).squaredNorm() < mDiskRadius*mDiskRadius;
}
void BVH::printStatistics() const {
cout << endl;
cout << "Bounding Volume Hierarchy statistics:" << endl;
cout << " Tree nodes : " << memString(sizeof(BVHNode) * mNodes.size()) << endl;
cout << " Index buffer : " << memString(sizeof(uint32_t) * mF->size()) << endl;
cout << " Total : "
<< memString(sizeof(BVHNode) * mNodes.size() + sizeof(uint32_t) * mF->size()) << endl;
}
std::pair<Float, uint32_t> BVH::statistics(uint32_t node_idx) const {
const BVHNode &node = mNodes[node_idx];
if (node.isLeaf()) {
return std::make_pair(T_tri * node.leaf.size, 1u);
} else {
std::pair<Float, uint32_t> stats_left = statistics(node_idx + 1u);
std::pair<Float, uint32_t> stats_right = statistics(node.inner.rightChild);
Float saLeft = mNodes[node_idx + 1u].aabb.surfaceArea();
Float saRight = mNodes[node.inner.rightChild].aabb.surfaceArea();
Float saCur = node.aabb.surfaceArea();
Float sahCost = 2 * BVH::T_aabb + (saLeft * stats_left.first +
saRight * stats_right.first) / saCur;
return std::make_pair(
sahCost,
stats_left.second + stats_right.second + 1u
);
}
}
BVH::~BVH() {
delete[] mIndices;
}
void BVH::refitBoundingBoxes(uint32_t node_idx) {
BVHNode &node = mNodes[node_idx];
if (node.isLeaf()) {
for (uint32_t i=node.start(); i<node.end(); ++i) {
uint32_t j = mIndices[i];
const Vector3f &p = mV->col(j), &n = mN->col(j);
Vector3f s, t;
coordinate_system(n, s, t);
AABB aabb;
for (int k=0; k<4; ++k)
aabb.expandBy(p + mDiskRadius * (
((k&1)*2 - 1) * s +
((k&2) - 1) * t));
node.aabb = aabb;
}
} else {
uint32_t left = node_idx + 1u, right = node.inner.rightChild;
refitBoundingBoxes(left);
refitBoundingBoxes(right);
node.aabb = AABB::merge(mNodes[left].aabb, mNodes[right].aabb);
}
}

View File

@@ -0,0 +1,108 @@
/*
bvh.h -- bounding volume hierarchy for fast ray-intersection queries
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#pragma once
#include "aabb.h"
/* BVH node in 32 bytes */
struct BVHNode {
union {
struct {
unsigned flag : 1;
uint32_t size : 31;
uint32_t start;
} leaf;
struct {
uint32_t unused;
uint32_t rightChild;
} inner;
};
AABB aabb;
inline bool isLeaf() const {
return leaf.flag == 1;
}
inline bool isInner() const {
return leaf.flag == 0;
}
inline bool isUnused() const {
return inner.unused == 0 && inner.rightChild == 0;
}
inline uint32_t start() const {
return leaf.start;
}
inline uint32_t end() const {
return leaf.start + leaf.size;
}
};
class BVH {
friend struct BVHBuildTask;
/* Cost values for BVH surface area heuristic */
enum { T_aabb = 1, T_tri = 1 };
public:
BVH(const MatrixXu *F, const MatrixXf *V, const MatrixXf *N, const AABB &aabb);
~BVH();
void setData(const MatrixXu *F, const MatrixXf *V, const MatrixXf *N) { mF = F; mV = V; mN = N; }
const MatrixXu *F() const { return mF; }
const MatrixXf *V() const { return mV; }
const MatrixXf *N() const { return mN; }
Float diskRadius() const { return mDiskRadius; }
void build(const ProgressCallback &progress = ProgressCallback());
void printStatistics() const;
bool rayIntersect(Ray ray) const;
bool rayIntersect(Ray ray, uint32_t &idx, Float &t, Vector2f *uv = nullptr) const;
void findNearestWithRadius(const Vector3f &p, Float radius,
std::vector<uint32_t> &result,
bool includeSelf = false) const;
uint32_t findNearest(const Vector3f &p, Float &radius, bool includeSelf = false) const;
void findKNearest(const Vector3f &p, uint32_t k, Float &radius,
std::vector<std::pair<Float, uint32_t> > &result,
bool includeSelf = false) const;
void findKNearest(const Vector3f &p, const Vector3f &N, uint32_t k,
Float &radius,
std::vector<std::pair<Float, uint32_t> > &result,
Float angleThresh = 30,
bool includeSelf = false) const;
protected:
bool rayIntersectTri(const Ray &ray, uint32_t i, Float &t, Vector2f &uv) const;
bool rayIntersectDisk(const Ray &ray, uint32_t i, Float &t) const;
void refitBoundingBoxes(uint32_t node_idx = 0);
std::pair<Float, uint32_t> statistics(uint32_t node_idx = 0) const;
protected:
std::vector<BVHNode> mNodes;
uint32_t *mIndices;
const MatrixXu *mF;
const MatrixXf *mV, *mN;
ProgressCallback mProgress;
Float mDiskRadius;
};

View File

@@ -0,0 +1,427 @@
#include "MEM_guardedalloc.h"
#include "adjacency.h"
#include "cleanup.h"
#include "common.h"
#include "dedge.h"
#include "extract.h"
#include "field.h"
#include "hierarchy.h"
#include "normal.h"
#include "smoothcurve.h"
#include "subdivide.h"
#include "meshstats.h"
#include "../instant_meshes_c_api.h"
#include <set>
int instant_meshes_nprocs = 1;
static const float len_v3v3(const float *a, const float *b)
{
float dx = a[0] - b[0];
float dy = a[1] - b[1];
float dz = a[2] - b[2];
float len = dx * dx + dy * dy + dz * dz;
return len > 0.0f ? sqrtf(len) : 0.0f;
}
static void cross_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
{
float n1[3], n2[3];
n1[0] = v1[0] - v2[0];
n2[0] = v2[0] - v3[0];
n1[1] = v1[1] - v2[1];
n2[1] = v2[1] - v3[1];
n1[2] = v1[2] - v2[2];
n2[2] = v2[2] - v3[2];
n[0] = n1[1] * n2[2] - n1[2] * n2[1];
n[1] = n1[2] * n2[0] - n1[0] * n2[2];
n[2] = n1[0] * n2[1] - n1[1] * n2[0];
}
/* Triangles */
static float area_tri_v3(const float v1[3], const float v2[3], const float v3[3])
{
float n[3];
cross_tri_v3(n, v1, v2, v3);
return sqrtf(n[0] * n[0] + n[1] * n[1] + n[2] * n[2]) * 0.5f;
}
static float fract(float f)
{
return f - floorf(f);
}
extern "C" {
void instant_meshes_run(RemeshMesh *mesh)
{
MatrixXu F;
MatrixXf V;
MatrixXf N;
float elen = 0.0f;
int totlen = 0;
float scale = 0.1f;
std::set<uint32_t> creaseSet;
F.resize(3, mesh->tottri);
V.resize(3, mesh->totvert);
N.resize(3, mesh->totvert);
float area = 0.0f;
for (int i = 0; i < mesh->tottri; i++) {
RemeshTri *tri = mesh->tris + i;
float *co1 = mesh->verts[tri->v1].co;
float *co2 = mesh->verts[tri->v2].co;
float *co3 = mesh->verts[tri->v3].co;
elen += len_v3v3(co1, co2);
elen += len_v3v3(co2, co3);
elen += len_v3v3(co3, co1);
totlen += 3;
area += area_tri_v3(co1, co2, co3);
F(0, i) = tri->v1;
F(1, i) = tri->v2;
F(2, i) = tri->v3;
}
for (int i = 0; i < mesh->totvert; i++) {
RemeshVertex *v = mesh->verts + i;
for (int j = 0; j < 3; j++) {
V(j, i) = v->co[j];
N(j, i) = v->no[j];
}
}
if (totlen > 0) {
elen /= totlen;
}
scale = sqrt(area / (float)mesh->goal_faces);
if (scale == 0.0f) {
return; // error
}
const bool extrinsic = true;
const bool deterministic = false;
const int rosy = 2, posy = 4;
printf("totarea: %.4f, scale: %.5f\n", area, scale);
VectorXu V2E, E2E;
VectorXb boundary;
VectorXb nonManifold;
nonManifold.resize(V.cols());
build_dedge(F, V, V2E, E2E, boundary, nonManifold);
// AdjacencyMatrix adj = generate_adjacency_matrix_cotan(F, V, V2E, E2E, nonManifold);
AdjacencyMatrix adj = generate_adjacency_matrix_uniform(F, V2E, E2E, nonManifold);
VectorXf A;
generate_smooth_normals(F, V, V2E, E2E, nonManifold, N);
compute_dual_vertex_areas(F, V, V2E, E2E, nonManifold, A);
MultiResolutionHierarchy mRes;
mRes.setF(std::move(F));
mRes.setV(std::move(V));
mRes.setN(std::move(N));
mRes.setE2E(std::move(E2E));
mRes.setAdj(std::move(adj));
mRes.setA(std::move(A));
mRes.setScale(scale);
printf("building multiresolution hierarchy\n");
mRes.build(deterministic);
mRes.resetSolution();
/* set up edge constraints */
mRes.clearConstraints();
for (int i = 0; i < mesh->totedge; i++) {
RemeshEdge *e = mesh->edges + i;
const MatrixXu &F = mRes.F();
const MatrixXf &N = mRes.N(), &V = mRes.V();
const VectorXu &E2E = mRes.E2E();
uint32_t i0 = (uint32_t)e->v1;
uint32_t i1 = (uint32_t)e->v2;
Vector3f p0 = V.col(i0), p1 = V.col(i1);
if (0 && (e->flag & REMESH_EDGE_USE_DIR)) {
Vector3f dir(e->dir);
if (dir.squaredNorm() > 0) {
mRes.CO().col(i0) = p0;
mRes.CO().col(i1) = p1;
mRes.CQ().col(i0) = mRes.CQ().col(i1) = dir;
mRes.CQw()[i0] = mRes.CQw()[i1] = mRes.COw()[i0] = mRes.COw()[i1] = 0.05f;
}
}
else if (e->flag & REMESH_EDGE_BOUNDARY) {
Vector3f edge = p1 - p0;
creaseSet.insert((uint32_t)e->v1);
creaseSet.insert((uint32_t)e->v2);
if (edge.squaredNorm() > 0) {
edge.normalize();
mRes.CO().col(i0) = p0;
mRes.CO().col(i1) = p1;
mRes.CQ().col(i0) = mRes.CQ().col(i1) = edge;
mRes.CQw()[i0] = mRes.CQw()[i1] = mRes.COw()[i0] = mRes.COw()[i1] = 1.0f;
}
}
}
mRes.propagateConstraints(rosy, posy);
Optimizer opt(mRes, false);
opt.setExtrinsic(extrinsic);
opt.setRoSy(rosy);
opt.setPoSy(posy);
const int iterations = mesh->iterations;
printf("optimizing orientation field\n");
for (int step = 0; step < iterations; step++) {
opt.optimizeOrientations(-1);
opt.notify();
opt.wait();
}
std::map<uint32_t, uint32_t> sing;
compute_orientation_singularities(mRes, sing, extrinsic, rosy);
cout << "Orientation field has " << sing.size() << " singularities." << endl;
printf("optimizing position field\n");
for (int step = 0; step < iterations; step++) {
opt.optimizePositions(-1);
opt.notify();
opt.wait();
}
std::map<uint32_t, Vector2i> pos_sing;
compute_position_singularities(mRes, sing, pos_sing, extrinsic, rosy, posy);
cout << "Position field has " << pos_sing.size() << " singularities." << endl;
#ifdef INSTANT_MESHES_VIS_COLOR
{
mesh->totoutface = mesh->tottri;
mesh->totoutvert = mesh->totvert;
mesh->outfaces = (RemeshOutFace *)MEM_malloc_arrayN(
mesh->tottri, sizeof(RemeshOutFace), "RemeshOutFace");
mesh->outverts = (RemeshVertex *)MEM_malloc_arrayN(
mesh->totvert, sizeof(RemeshVertex), "RemeshVertex");
memcpy(mesh->outverts, mesh->verts, sizeof(*mesh->outverts) * mesh->totvert);
mesh->out_scracth = (int *)MEM_malloc_arrayN(mesh->tottri * 3, sizeof(int), "out_scratch");
int li = 0;
RemeshOutFace *f = mesh->outfaces;
RemeshTri *tri = mesh->tris;
RemeshVertex *v = mesh->outverts;
auto O = mRes.O(0);
printf("O size: [%d][%d]\n", (int)O.cols(), (int)O.rows());
for (int i = 0; i < mesh->totvert; i++, v++) {
v->viscolor[0] = O(0, i);
v->viscolor[1] = O(1, i);
// v->viscolor[2] = O(2, i);
}
for (int i = 0; i < mesh->tottri; i++, f++, tri++) {
f->verts = mesh->out_scracth + li;
li += 3;
f->verts[0] = tri->v1;
f->verts[1] = tri->v2;
f->verts[2] = tri->v3;
f->totvert = 3;
}
}
#endif
opt.shutdown();
#ifdef INSTANT_MESHES_VIS_COLOR
return;
#endif
std::vector<std::vector<TaggedLink>> adj_extracted;
MatrixXu F_extracted;
MatrixXf V_extracted;
MatrixXf N_extracted;
MatrixXf Nf_extracted;
std::set<uint32_t> creaseOut;
printf("extracting mesh\n");
extract_graph(mRes,
extrinsic,
rosy,
posy,
adj_extracted,
V_extracted,
N_extracted,
creaseSet,
creaseOut,
deterministic,
true,
true,
true);
extract_faces(adj_extracted,
V_extracted,
N_extracted,
Nf_extracted,
F_extracted,
posy,
mRes.scale(),
creaseOut,
true,
true,
nullptr,
0);
// F_extracted = mRes.F();
// V_extracted = mRes.V();
mesh->totoutface = F_extracted.cols();
mesh->totoutvert = V_extracted.cols();
int sides = F_extracted.rows();
mesh->outfaces = (RemeshOutFace *)MEM_malloc_arrayN(
mesh->totoutface, sizeof(RemeshOutFace), "RemeshOutFaces");
mesh->outverts = (RemeshVertex *)MEM_malloc_arrayN(
mesh->totoutvert, sizeof(RemeshVertex), "RemeshOutVerts");
mesh->out_scracth = (int *)MEM_malloc_arrayN(
mesh->totoutface * sides, sizeof(int), "out_scratch");
printf(
"sides:%d\ntotal faces: %d\ntotal verts: %d\n", sides, mesh->totoutface, mesh->totoutvert);
RemeshOutFace *f = mesh->outfaces;
int totface = mesh->totoutface;
int i = 0, fi = 0, li = 0;
/* Check for irregular faces */
std::map<uint32_t, std::pair<uint32_t, std::map<uint32_t, uint32_t>>> irregular;
size_t nIrregular = 0;
for (; fi < totface; fi++) {
if (F_extracted(2, fi) == F_extracted(3, fi)) {
nIrregular++;
auto &value = irregular[F_extracted(2, fi)];
value.first = fi;
value.second[F_extracted(0, fi)] = F_extracted(1, fi);
continue;
}
f->verts = mesh->out_scracth + li;
li += sides;
int j = 0;
for (j = 0; j < sides; j++) {
f->verts[j] = F_extracted(j, fi);
}
// if (j != sides) {
// i--;
//}
f->totvert = sides;
i++;
f++;
}
for (auto item : irregular) {
auto face = item.second;
uint32_t v = face.second.begin()->first, first = v, k = 0;
int j = 0;
f->verts = mesh->out_scracth + li;
while (true) {
v = face.second[v];
f->verts[j] = (int)v;
li++;
j++;
if (v == first || ++k == face.second.size())
break;
}
f->totvert = j;
f++;
i++;
}
printf("final totface: %d, alloc totface: %d\n", i, mesh->totoutface);
printf("final totloop: %d, alloc totloop: %d\n", li, mesh->totoutface * sides);
mesh->totoutface = i;
RemeshVertex *v = mesh->outverts;
for (int i = 0; i < mesh->totoutvert; i++, v++) {
for (int j = 0; j < 3; j++) {
v->co[j] = V_extracted(j, i);
v->no[j] = N_extracted(j, i);
}
}
}
void instant_meshes_finish(RemeshMesh *mesh)
{
/*
if (mesh->verts) {
MEM_freeN((void *)mesh->verts);
}
if (mesh->edges) {
MEM_freeN((void *)mesh->edges);
}
if (mesh->tris) {
MEM_freeN((void *)mesh->tris);
}*/
if (mesh->outverts) {
MEM_freeN((void *)mesh->outverts);
}
if (mesh->outfaces) {
MEM_freeN((void *)mesh->outfaces);
}
if (mesh->out_scracth) {
MEM_freeN((void *)mesh->out_scracth);
}
}
void instant_meshes_set_number_of_threads(int n)
{
instant_meshes_nprocs = n;
}
};

View File

@@ -0,0 +1,189 @@
/*
cleanup.cpp -- functionality to greedily drop non-manifold elements from a mesh
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#include "dedge.h"
#include "field.h"
#include <set>
void remove_nonmanifold(MatrixXu &F, MatrixXf &V, MatrixXf &Nf) {
typedef std::pair<uint32_t, uint32_t> Edge;
std::map<uint32_t, std::map<uint32_t, std::pair<uint32_t, uint32_t>>> irregular;
std::vector<std::set<uint32_t>> E(V.cols());
std::vector<std::set<uint32_t>> VF(V.cols());
auto kill_face_single = [&](uint32_t f) {
if (F(0, f) == INVALID)
return;
for (int i=0; i<F.rows(); ++i)
E[F(i, f)].erase(F((i+1)%F.rows(), f));
F.col(f).setConstant(INVALID);
};
auto kill_face = [&](uint32_t f) {
if (F.rows() == 4 && F(2, f) == F(3, f)) {
auto it = irregular.find(F(2, f));
if (it != irregular.end()) {
for (auto &item : it->second) {
kill_face_single(item.second.second);
}
}
}
kill_face_single(f);
};
uint32_t nm_edge = 0, nm_vert = 0;
for (uint32_t f=0; f < (uint32_t) F.cols(); ++f) {
if (F(0, f) == INVALID)
continue;
if (F.rows() == 4 && F(2, f) == F(3, f)) {
/* Special handling of irregular faces */
irregular[F(2, f)][F(0, f)] = std::make_pair(F(1, f), f);
continue;
}
bool nonmanifold = false;
for (uint32_t e=0; e<F.rows(); ++e) {
uint32_t v0 = F(e, f), v1 = F((e + 1) % F.rows(), f), v2 = F((e + 2) % F.rows(), f);
if (E[v0].find(v1) != E[v0].end() ||
(F.rows() == 4 && E[v0].find(v2) != E[v0].end()))
nonmanifold = true;
}
if (nonmanifold) {
nm_edge++;
F.col(f).setConstant(INVALID);
continue;
}
for (uint32_t e=0; e<F.rows(); ++e) {
uint32_t v0 = F(e, f), v1 = F((e + 1) % F.rows(), f), v2 = F((e + 2) % F.rows(), f);
E[v0].insert(v1);
if (F.rows() == 4)
E[v0].insert(v2);
VF[v0].insert(f);
}
}
std::vector<Edge> edges;
for (auto item : irregular) {
bool nonmanifold = false;
auto face = item.second;
edges.clear();
uint32_t cur = face.begin()->first, stop = cur;
while (true) {
uint32_t pred = cur;
cur = face[cur].first;
uint32_t next = face[cur].first, it = 0;
while (true) {
++it;
if (next == pred)
break;
if (E[cur].find(next) != E[cur].end() && it == 1)
nonmanifold = true;
edges.push_back(Edge(cur, next));
next = face[next].first;
}
if (cur == stop)
break;
}
if (nonmanifold) {
nm_edge++;
for (auto &i : item.second)
F.col(i.second.second).setConstant(INVALID);
continue;
} else {
for (auto e : edges) {
E[e.first].insert(e.second);
for (auto e2 : face)
VF[e.first].insert(e2.second.second);
}
}
}
/* Check vertices */
std::set<uint32_t> v_marked, v_unmarked, f_adjacent;
std::function<void(uint32_t)> dfs = [&](uint32_t i) {
v_marked.insert(i);
v_unmarked.erase(i);
for (uint32_t f : VF[i]) {
if (f_adjacent.find(f) == f_adjacent.end()) /* if not part of adjacent face */
continue;
for (uint32_t j = 0; j<F.rows(); ++j) {
uint32_t k = F(j, f);
if (v_unmarked.find(k) == v_unmarked.end() || /* if not unmarked OR */
v_marked.find(k) != v_marked.end()) /* if already marked */
continue;
dfs(k);
}
}
};
for (uint32_t i = 0; i < (uint32_t) V.cols(); ++i) {
v_marked.clear();
v_unmarked.clear();
f_adjacent.clear();
for (uint32_t f : VF[i]) {
if (F(0, f) == INVALID)
continue;
for (uint32_t k=0; k<F.rows(); ++k)
v_unmarked.insert(F(k, f));
f_adjacent.insert(f);
}
if (v_unmarked.empty())
continue;
v_marked.insert(i);
v_unmarked.erase(i);
dfs(*v_unmarked.begin());
if (v_unmarked.size() > 0) {
nm_vert++;
for (uint32_t f : f_adjacent)
kill_face(f);
}
}
if (nm_vert > 0 || nm_edge > 0)
cout << "Non-manifold elements: vertices=" << nm_vert << ", edges=" << nm_edge << endl;
uint32_t nFaces = 0, nFacesOrig = F.cols();
for (uint32_t f = 0; f < (uint32_t) F.cols(); ++f) {
if (F(0, f) == INVALID)
continue;
if (nFaces != f) {
F.col(nFaces) = F.col(f);
Nf.col(nFaces) = Nf.col(f);
}
++nFaces;
}
if (nFacesOrig != nFaces) {
F.conservativeResize(F.rows(), nFaces);
Nf.conservativeResize(Nf.rows(), nFaces);
cout << "Faces reduced from " << nFacesOrig << " -> " << nFaces << endl;
}
}

View File

@@ -0,0 +1,18 @@
/*
cleanup.h -- functionality to greedily drop non-manifold elements from a mesh
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#pragma once
#include "common.h"
extern void remove_nonmanifold(MatrixXu &F, MatrixXf &V, MatrixXf &Nf);

View File

@@ -0,0 +1,440 @@
/*
common.h -- commonly used definitions, header files and inline functions
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#pragma once
#if defined(_WIN32)
# define NOMINMAX
# pragma warning(disable : 4244 4018 4100 4610 4510 4127 4512 4146 4267 4503 4800 4706)
#endif
/* clang-format off */
#define _USE_MATH_DEFINES
#include <cmath>
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <algorithm>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <tbb/tbb.h>
#include <thread>
#include <vector>
/* clang-format on */
#define PARALLELIZE
#define SINGLE_PRECISION
#define GRAIN_SIZE 1024
/* Application precision -- can be set to single or double precision */
#if defined(SINGLE_PRECISION)
typedef float Float;
#else
typedef double Float;
#endif
/* Useful Eigen typedefs based on the current precision */
typedef Eigen::Matrix<int32_t, 2, 1> Vector2i;
typedef Eigen::Matrix<int32_t, 3, 1> Vector3i;
typedef Eigen::Matrix<int32_t, 4, 1> Vector4i;
typedef Eigen::Matrix<uint32_t, 2, 1> Vector2u;
typedef Eigen::Matrix<uint32_t, 3, 1> Vector3u;
typedef Eigen::Matrix<uint32_t, 4, 1> Vector4u;
typedef Eigen::Matrix<uint8_t, 4, 1> Vector4u8;
typedef Eigen::Matrix<Float, 2, 1> Vector2f;
typedef Eigen::Matrix<Float, 3, 1> Vector3f;
typedef Eigen::Matrix<Float, 4, 1> Vector4f;
typedef Eigen::Matrix<int32_t, Eigen::Dynamic, 1> VectorXi;
typedef Eigen::Matrix<uint32_t, Eigen::Dynamic, 1> VectorXu;
typedef Eigen::Matrix<uint8_t, Eigen::Dynamic, 1> VectorXu8;
typedef Eigen::Matrix<bool, Eigen::Dynamic, 1> VectorXb;
typedef Eigen::Matrix<Float, Eigen::Dynamic, 1> VectorXf;
typedef Eigen::Matrix<int32_t, Eigen::Dynamic, Eigen::Dynamic> MatrixXi;
typedef Eigen::Matrix<uint32_t, Eigen::Dynamic, Eigen::Dynamic> MatrixXu;
typedef Eigen::Matrix<uint8_t, Eigen::Dynamic, Eigen::Dynamic> MatrixXu8;
typedef Eigen::Matrix<Float, Eigen::Dynamic, Eigen::Dynamic> MatrixXf;
typedef Eigen::Matrix<Float, 2, 2> Matrix2f;
typedef Eigen::Matrix<Float, 3, 3> Matrix3f;
typedef Eigen::Matrix<Float, 4, 4> Matrix4f;
using std::cerr;
using std::cout;
using std::endl;
using namespace std::placeholders;
/* A callback to inform the GUI about progress of an operation */
typedef std::function<void(const std::string &, Float)> ProgressCallback;
#define PROGRESS_BLKSIZE (1 << 18)
#define SHOW_PROGRESS(i, maxval, text) \
if (progress && (i % PROGRESS_BLKSIZE) == 0) \
progress(text, -PROGRESS_BLKSIZE / (Float)maxval)
#define PROGRESS_SHIFT 18u
#define SHOW_PROGRESS_RANGE(range, maxval, text) \
if (progress && range.begin() > 0) { \
uint32_t nUpdates = (range.end() >> PROGRESS_SHIFT) - \
((range.begin() - 1) >> PROGRESS_SHIFT); \
if (nUpdates > 0) { \
const uint32_t nUpdatesTotal = (uint32_t)(maxval) / (1 << PROGRESS_SHIFT); \
progress(text, -(float)nUpdates / (float)nUpdatesTotal); \
} \
}
#if defined(_WIN32)
# define RCPOVERFLOW_FLT 2.93873587705571876e-39f
# define RCPOVERFLOW_DBL 5.56268464626800345e-309
#else
# define RCPOVERFLOW_FLT 0x1p-128f
# define RCPOVERFLOW_DBL 0x1p-1024
#endif
#if defined(SINGLE_PRECISION)
# define RCPOVERFLOW RCPOVERFLOW_FLT
#else
# define RCPOVERFLOW RCPOVERFLOW_DBL
#endif
template<typename TimeT = std::chrono::milliseconds> class Timer {
public:
Timer()
{
start = std::chrono::system_clock::now();
}
size_t value() const
{
auto now = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<TimeT>(now - start);
return (size_t)duration.count();
}
size_t reset()
{
auto now = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<TimeT>(now - start);
start = now;
return (size_t)duration.count();
}
private:
std::chrono::system_clock::time_point start;
};
inline std::string timeString(double time, bool precise = false)
{
if (std::isnan(time) || std::isinf(time))
return "inf";
std::string suffix = "ms";
if (time > 1000) {
time /= 1000;
suffix = "s";
if (time > 60) {
time /= 60;
suffix = "m";
if (time > 60) {
time /= 60;
suffix = "h";
if (time > 12) {
time /= 12;
suffix = "d";
}
}
}
}
std::ostringstream os;
os << std::setprecision(precise ? 4 : 1) << std::fixed << time << suffix;
return os.str();
}
inline std::string memString(size_t size, bool precise = false)
{
double value = (double)size;
const char *suffixes[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB"};
int suffix = 0;
while (suffix < 5 && value > 1024.0f) {
value /= 1024.0f;
++suffix;
}
std::ostringstream os;
os << std::setprecision(suffix == 0 ? 0 : (precise ? 4 : 1)) << std::fixed << value << " "
<< suffixes[suffix];
return os.str();
}
template<typename Matrix> inline size_t sizeInBytes(const Matrix &matrix)
{
return matrix.size() * sizeof(typename Matrix::Scalar);
}
inline bool atomicCompareAndExchange(volatile uint32_t *v, uint32_t newValue, uint32_t oldValue)
{
#if defined(_WIN32)
return _InterlockedCompareExchange(reinterpret_cast<volatile long *>(v),
(long)newValue,
(long)oldValue) == (long)oldValue;
#else
return __sync_bool_compare_and_swap(v, oldValue, newValue);
#endif
}
inline uint32_t atomicAdd(volatile uint32_t *dst, uint32_t delta)
{
#if defined(_MSC_VER)
return _InterlockedExchangeAdd(reinterpret_cast<volatile long *>(dst), delta) + delta;
#else
return __sync_add_and_fetch(dst, delta);
#endif
}
inline float atomicAdd(volatile float *dst, float delta)
{
union bits {
float f;
uint32_t i;
};
bits oldVal, newVal;
do {
#if defined(__i386__) || defined(__amd64__)
__asm__ __volatile__("pause\n");
#endif
oldVal.f = *dst;
newVal.f = oldVal.f + delta;
} while (!atomicCompareAndExchange((volatile uint32_t *)dst, newVal.i, oldVal.i));
return newVal.f;
}
/// Always-positive modulo function, Float precision version (assumes b > 0)
inline Float modulo(Float a, Float b)
{
Float r = std::fmod(a, b);
return (r < 0.0) ? r + b : r;
}
/// Always-positive modulo function (assumes b > 0)
inline int32_t modulo(int32_t a, int32_t b)
{
int32_t r = a % b;
return (r < 0) ? r + b : r;
}
inline float fast_acos(float x)
{
float negate = float(x < 0.0f);
x = std::abs(x);
float ret = -0.0187293f;
ret *= x;
ret = ret + 0.0742610f;
ret *= x;
ret = ret - 0.2121144f;
ret *= x;
ret = ret + 1.5707288f;
ret = ret * std::sqrt(1.0f - x);
ret = ret - 2.0f * negate * ret;
return negate * (float)M_PI + ret;
}
template<typename T, typename U> inline T union_cast(const U &val)
{
union {
U u;
T t;
} tmp = {val};
return tmp.t;
}
inline Float signum(Float value)
{
return std::copysign((Float)1, value);
}
inline void coordinate_system(const Vector3f &a, Vector3f &b, Vector3f &c)
{
if (std::abs(a.x()) > std::abs(a.y())) {
Float invLen = 1.0f / std::sqrt(a.x() * a.x() + a.z() * a.z());
c = Vector3f(a.z() * invLen, 0.0f, -a.x() * invLen);
}
else {
Float invLen = 1.0f / std::sqrt(a.y() * a.y() + a.z() * a.z());
c = Vector3f(0.0f, a.z() * invLen, -a.y() * invLen);
}
b = c.cross(a);
}
class ordered_lock {
public:
ordered_lock() : next_ticket(0), counter(0)
{
}
void lock()
{
std::unique_lock<std::mutex> acquire(cvar_lock);
unsigned int ticket = next_ticket++;
while (ticket != counter)
cvar.wait(acquire);
}
void unlock()
{
std::unique_lock<std::mutex> acquire(cvar_lock);
counter++;
cvar.notify_all();
}
protected:
std::condition_variable cvar;
std::mutex cvar_lock;
unsigned int next_ticket, counter;
};
inline std::string str_tolower(std::string str)
{
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
return str;
}
inline uint32_t str_to_uint32_t(const std::string &str)
{
char *end_ptr = nullptr;
uint32_t result = (uint32_t)strtoul(str.c_str(), &end_ptr, 10);
if (*end_ptr != '\0')
throw std::runtime_error("Could not parse unsigned integer \"" + str + "\"");
return result;
}
inline uint32_t str_to_int32_t(const std::string &str)
{
char *end_ptr = nullptr;
int32_t result = (int32_t)strtol(str.c_str(), &end_ptr, 10);
if (*end_ptr != '\0')
throw std::runtime_error("Could not parse signed integer \"" + str + "\"");
return result;
}
inline Float str_to_float(const std::string &str)
{
char *end_ptr = nullptr;
Float result = (Float)strtod(str.c_str(), &end_ptr);
if (*end_ptr != '\0')
throw std::runtime_error("Could not parse floating point value \"" + str + "\"");
return result;
}
inline std::vector<std::string> &str_tokenize(const std::string &s,
char delim,
std::vector<std::string> &elems,
bool include_empty = false)
{
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim))
if (!item.empty() || include_empty)
elems.push_back(item);
return elems;
}
inline std::vector<std::string> str_tokenize(const std::string &s, char delim, bool include_empty)
{
std::vector<std::string> elems;
str_tokenize(s, delim, elems, include_empty);
return elems;
}
inline void jet(float x, float &r, float &g, float &b)
{
const Float rone = 0.8f, gone = 1.0f, bone = 1.0f;
x = std::max(std::min(x, 1.f), 0.f);
if (x < 1.f / 8.f) {
r = 0;
g = 0;
b = bone * (.5f + x / (1.f / 8.f) * 0.5f);
}
else if (x < 3.f / 8.f) {
r = 0;
g = gone * (x - 1.f / 8.f) / (3.f / 8.f - 1.f / 8.f);
b = bone;
}
else if (x < 5.f / 8.f) {
r = rone * (x - 3.f / 8.f) / (5.f / 8.f - 3.f / 8.f);
g = gone;
b = (bone - (x - 3.f / 8.f) / (5.f / 8.f - 3.f / 8.f));
}
else if (x < 7.f / 8.f) {
r = rone;
g = (gone - (x - 5.f / 8.f) / (7.f / 8.f - 5.f / 8.f));
b = 0;
}
else {
r = (rone - (x - 7.f / 8.f) / (1.f - 7.f / 8.f) * .5f);
g = 0;
b = 0;
}
}
inline void jet(VectorXf &X, MatrixXu8 &C, float min, float max)
{
for (int i = 0; i < X.size(); ++i) {
float r, g, b;
jet((-min + X[i]) / (max - min), r, g, b);
C.col(i) << (uint8_t)(r * 255.f), (uint8_t)(g * 255.f), (uint8_t)(b * 255.f), (uint8_t)255;
}
}
inline void jet(VectorXf &X, MatrixXu8 &C)
{
jet(X, C, X.minCoeff(), X.maxCoeff());
}
inline Vector3f hsv_to_rgb(Float h, Float s, Float v)
{
if (s == 0.f) { // achromatic (grey)
return Vector3f::Constant(v);
}
h *= 6;
int i = std::floor(h);
Float f = h - i; // fractional part of h
Float p = v * (1 - s);
Float q = v * (1 - s * f);
Float t = v * (1 - s * (1 - f));
switch (i) {
case 0:
return Vector3f(v, t, p);
break;
case 1:
return Vector3f(q, v, p);
break;
case 2:
return Vector3f(p, v, t);
break;
case 3:
return Vector3f(p, q, v);
break;
case 4:
return Vector3f(t, p, v);
break;
default:
return Vector3f(v, p, q);
break;
}
}

View File

@@ -0,0 +1,156 @@
/*
dedge.cpp: Parallel directed edge data structure builder
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#include "dedge.h"
void build_dedge(const MatrixXu &F, const MatrixXf &V, VectorXu &V2E,
VectorXu &E2E, VectorXb &boundary, VectorXb &nonManifold,
const ProgressCallback &progress, bool quiet) {
if (!quiet) {
cout << "Building a directed edge data structure .. ";
cout.flush();
}
Timer<> timer;
if (progress && !quiet)
progress("Building directed edge data structure", 0.0f);
V2E.resize(V.cols());
V2E.setConstant(INVALID);
uint32_t deg = F.rows();
std::vector<std::pair<uint32_t, uint32_t>> tmp(F.size());
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t) F.cols(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t f = range.begin(); f != range.end(); ++f) {
for (uint32_t i = 0; i < deg; ++i) {
uint32_t idx_cur = F(i, f),
idx_next = F((i+1)%deg, f),
edge_id = deg * f + i;
if (idx_cur >= V.cols() || idx_next >= V.cols())
throw std::runtime_error("Mesh data contains an out-of-bounds vertex reference!");
if (idx_cur == idx_next)
continue;
tmp[edge_id] = std::make_pair(idx_next, INVALID);
if (!atomicCompareAndExchange(&V2E[idx_cur], edge_id, INVALID)) {
uint32_t idx = V2E[idx_cur];
while (!atomicCompareAndExchange(&tmp[idx].second, edge_id, INVALID))
idx = tmp[idx].second;
}
}
}
if (!quiet)
SHOW_PROGRESS_RANGE(range, F.cols(), "Building directed edge data structure (1/3)");
}
);
nonManifold.resize(V.cols());
nonManifold.setConstant(false);
E2E.resize(F.cols() * deg);
E2E.setConstant(INVALID);
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t) F.cols(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t f = range.begin(); f != range.end(); ++f) {
for (uint32_t i = 0; i < deg; ++i) {
uint32_t idx_cur = F(i, f),
idx_next = F((i+1)%deg, f),
edge_id_cur = deg * f + i;
if (idx_cur == idx_next)
continue;
uint32_t it = V2E[idx_next], edge_id_opp = INVALID;
while (it != INVALID) {
if (tmp[it].first == idx_cur) {
if (edge_id_opp == INVALID) {
edge_id_opp = it;
} else {
nonManifold[idx_cur] = true;
nonManifold[idx_next] = true;
edge_id_opp = INVALID;
break;
}
}
it = tmp[it].second;
}
if (edge_id_opp != INVALID && edge_id_cur < edge_id_opp) {
E2E[edge_id_cur] = edge_id_opp;
E2E[edge_id_opp] = edge_id_cur;
}
}
}
if (!quiet)
SHOW_PROGRESS_RANGE(range, F.cols(), "Building directed edge data structure (2/3)");
}
);
std::atomic<uint32_t> nonManifoldCounter(0), boundaryCounter(0), isolatedCounter(0);
boundary.resize(V.cols());
boundary.setConstant(false);
/* Detect boundary regions of the mesh and adjust vertex->edge pointers*/
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t) V.cols(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t i = range.begin(); i != range.end(); ++i) {
uint32_t edge = V2E[i];
if (edge == INVALID) {
isolatedCounter++;
continue;
}
if (nonManifold[i]) {
nonManifoldCounter++;
V2E[i] = INVALID;
continue;
}
/* Walk backwards to the first boundary edge (if any) */
uint32_t start = edge, v2e = INVALID;
do {
v2e = std::min(v2e, edge);
uint32_t prevEdge = E2E[dedge_prev(edge, deg)];
if (prevEdge == INVALID) {
/* Reached boundary -- update the vertex->edge link */
v2e = edge;
boundary[i] = true;
boundaryCounter++;
break;
}
edge = prevEdge;
} while (edge != start);
V2E[i] = v2e;
}
if (!quiet)
SHOW_PROGRESS_RANGE(range, V.cols(), "Building directed edge data structure (3/3)");
}
);
if (!quiet) {
cout << "done. (";
if (nonManifoldCounter)
cout << nonManifoldCounter << " non-manifold vertices, ";
if (boundaryCounter)
cout << boundaryCounter << " boundary vertices, ";
if (isolatedCounter)
cout << isolatedCounter << " isolated vertices, ";
cout << "took " << timeString(timer.value()) << ")" << endl;
}
}

View File

@@ -0,0 +1,31 @@
/*
dedge.h: Parallel directed edge data structure builder
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#pragma once
#include "common.h"
static const uint32_t INVALID = (uint32_t) -1;
inline uint32_t dedge_prev_3(uint32_t e) { return (e % 3 == 0) ? e + 2 : e - 1; }
inline uint32_t dedge_next_3(uint32_t e) { return (e % 3 == 2) ? e - 2 : e + 1; }
inline uint32_t dedge_prev_4(uint32_t e) { return (e % 4 == 0) ? e + 3 : e - 1; }
inline uint32_t dedge_next_4(uint32_t e) { return (e % 4 == 3) ? e - 3 : e + 1; }
inline uint32_t dedge_prev(uint32_t e, uint32_t deg) { return (e % deg == 0u) ? e + (deg - 1) : e - 1; }
inline uint32_t dedge_next(uint32_t e, uint32_t deg) { return (e % deg == deg - 1) ? e - (deg - 1) : e + 1; }
extern void build_dedge(const MatrixXu &F, const MatrixXf &V, VectorXu &V2E,
VectorXu &E2E, VectorXb &boundary, VectorXb &nonManifold,
const ProgressCallback &progress = ProgressCallback(),
bool quiet = false);

View File

@@ -0,0 +1,18 @@
#include "serializer.h"
int main(int argc, char **argv) {
if (argc < 3) {
cout << "Syntax: diff [application state 1.ply] [application state 2.ply]" << endl;
return -1;
}
Serializer s1(argv[1]);
cout << s1 << endl;
Serializer s2(argv[2]);
cout << s2 << endl;
bool diff = s1.diff(s2);
if (!diff)
cout << "No differences found." << endl;
return diff ? 1 : 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,43 @@
/*
extract.h: Mesh extraction from existing orientation/position fields
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#pragma once
#include "hierarchy.h"
#include <set>
struct TaggedLink {
uint32_t id;
uint8_t flag;
TaggedLink(uint32_t id) : id(id), flag(0) { }
bool used() const { return flag & 1; }
void markUsed() { flag |= 1; }
};
class BVH;
extern void
extract_graph(const MultiResolutionHierarchy &mRes, bool extrinsic, int rosy, int posy,
std::vector<std::vector<TaggedLink>> &adj_new,
MatrixXf &O_new, MatrixXf &N_new,
const std::set<uint32_t> &crease_in,
std::set<uint32_t> &crease_out,
bool deterministic, bool remove_spurious_vertices = true,
bool remove_unnecessary_triangles = true,
bool snap_vertices = true);
extern void extract_faces(std::vector<std::vector<TaggedLink> > &adj,
MatrixXf &O, MatrixXf &N, MatrixXf &Nf, MatrixXu &F,
int posy, Float scale, std::set<uint32_t> &crease,
bool fill_holes = true, bool pure_quad = true,
BVH *bvh = nullptr, int smooth_iterations = 2);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,296 @@
/*
field.h: Routines for averaging orientations and directions subject
to various symmetry conditions. Also contains the Optimizer class which
uses these routines to smooth fields hierarchically.
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#pragma once
#include "hierarchy.h"
#include <map>
/* Rotation helper functions */
extern Vector3f rotate60(const Vector3f &d, const Vector3f &n);
extern Vector3f rotate90(const Vector3f &d, const Vector3f &n);
extern Vector3f rotate180(const Vector3f &d, const Vector3f &n);
extern Vector3f rotate60_by(const Vector3f &d, const Vector3f &n, int amount);
extern Vector3f rotate90_by(const Vector3f &d, const Vector3f &n, int amount);
extern Vector3f rotate180_by(const Vector3f &d, const Vector3f &n, int amount);
extern Vector2i rshift60(Vector2i shift, int amount);
extern Vector2i rshift90(Vector2i shift, int amount);
extern Vector2i rshift180(Vector2i shift, int amount);
extern Vector3f rotate_vector_into_plane(Vector3f q,
const Vector3f &source_normal,
const Vector3f &target_normal);
/* Extrinsic & intrinsic orientation symmetry functors */
extern std::pair<Vector3f, Vector3f> compat_orientation_intrinsic_2(const Vector3f &q0,
const Vector3f &n0,
const Vector3f &q1,
const Vector3f &n1);
extern std::pair<Vector3f, Vector3f> compat_orientation_intrinsic_4(const Vector3f &q0,
const Vector3f &n0,
const Vector3f &q1,
const Vector3f &n1);
extern std::pair<Vector3f, Vector3f> compat_orientation_intrinsic_4_knoeppel(const Vector3f &q0,
const Vector3f &n0,
const Vector3f &q1,
const Vector3f &n1);
extern std::pair<Vector3f, Vector3f> compat_orientation_intrinsic_6(const Vector3f &q0,
const Vector3f &n0,
const Vector3f &q1,
const Vector3f &n1);
extern std::pair<Vector3f, Vector3f> compat_orientation_extrinsic_2(const Vector3f &q0,
const Vector3f &n0,
const Vector3f &q1,
const Vector3f &n1);
extern std::pair<Vector3f, Vector3f> compat_orientation_extrinsic_4(const Vector3f &q0,
const Vector3f &n0,
const Vector3f &q1,
const Vector3f &n1);
extern std::pair<Vector3f, Vector3f> compat_orientation_extrinsic_6(const Vector3f &q0,
const Vector3f &n0,
const Vector3f &q1,
const Vector3f &n1);
extern std::pair<int, int> compat_orientation_extrinsic_index_2(const Vector3f &q0,
const Vector3f &n0,
const Vector3f &q1,
const Vector3f &n1);
extern std::pair<int, int> compat_orientation_extrinsic_index_4(const Vector3f &q0,
const Vector3f &n0,
const Vector3f &q1,
const Vector3f &n1);
extern std::pair<int, int> compat_orientation_extrinsic_index_6(const Vector3f &q0,
const Vector3f &n0,
const Vector3f &q1,
const Vector3f &n1);
extern std::pair<int, int> compat_orientation_intrinsic_index_2(const Vector3f &q0,
const Vector3f &n0,
const Vector3f &q1,
const Vector3f &n1);
extern std::pair<int, int> compat_orientation_intrinsic_index_4(const Vector3f &q0,
const Vector3f &n0,
const Vector3f &q1,
const Vector3f &n1);
extern std::pair<int, int> compat_orientation_intrinsic_index_6(const Vector3f &q0,
const Vector3f &n0,
const Vector3f &q1,
const Vector3f &n1);
/* Extrinsic & intrinsic position symmetry functors */
extern std::pair<Vector3f, Vector3f> compat_position_extrinsic_3(const Vector3f &p0,
const Vector3f &n0,
const Vector3f &q0,
const Vector3f &o0,
const Vector3f &p1,
const Vector3f &n1,
const Vector3f &q1,
const Vector3f &o1,
Float scale,
Float inv_scale);
extern std::pair<Vector3f, Vector3f> compat_position_extrinsic_4(const Vector3f &p0,
const Vector3f &n0,
const Vector3f &q0,
const Vector3f &o0,
const Vector3f &p1,
const Vector3f &n1,
const Vector3f &q1,
const Vector3f &o1,
Float scale,
Float inv_scale);
extern std::pair<Vector2i, Vector2i> compat_position_extrinsic_index_3(const Vector3f &p0,
const Vector3f &n0,
const Vector3f &q0,
const Vector3f &o0,
const Vector3f &p1,
const Vector3f &n1,
const Vector3f &q1,
const Vector3f &o1,
Float scale,
Float inv_scale,
Float *error = nullptr);
extern std::pair<Vector2i, Vector2i> compat_position_extrinsic_index_4(const Vector3f &p0,
const Vector3f &n0,
const Vector3f &q0,
const Vector3f &o0,
const Vector3f &p1,
const Vector3f &n1,
const Vector3f &q1,
const Vector3f &o1,
Float scale,
Float inv_scale,
Float *error = nullptr);
extern std::pair<Vector3f, Vector3f> compat_position_intrinsic_3(const Vector3f &p0,
const Vector3f &n0,
const Vector3f &q0,
const Vector3f &o0,
const Vector3f &p1,
const Vector3f &n1,
const Vector3f &q1,
const Vector3f &o1,
Float scale,
Float inv_scale);
extern std::pair<Vector3f, Vector3f> compat_position_intrinsic_4(const Vector3f &p0,
const Vector3f &n0,
const Vector3f &q0,
const Vector3f &o0,
const Vector3f &p1,
const Vector3f &n1,
const Vector3f &q1,
const Vector3f &o1,
Float scale,
Float inv_scale);
extern std::pair<Vector2i, Vector2i> compat_position_intrinsic_index_3(const Vector3f &p0,
const Vector3f &n0,
const Vector3f &q0,
const Vector3f &o0,
const Vector3f &p1,
const Vector3f &n1,
const Vector3f &q1,
const Vector3f &o1,
Float scale,
Float inv_scale,
Float *error = nullptr);
extern std::pair<Vector2i, Vector2i> compat_position_intrinsic_index_4(const Vector3f &p0,
const Vector3f &n0,
const Vector3f &q0,
const Vector3f &o0,
const Vector3f &p1,
const Vector3f &n1,
const Vector3f &q1,
const Vector3f &o1,
Float scale,
Float inv_scale,
Float *error = nullptr);
/* Optimization kernels */
extern Float optimize_orientations(MultiResolutionHierarchy &mRes,
int level,
bool extrinsic,
int rosy,
const std::function<void(uint32_t)> &progress);
extern Float optimize_positions(MultiResolutionHierarchy &mRes,
int level,
bool extrinsic,
int posy,
const std::function<void(uint32_t)> &progress);
/* Singularity computation */
extern void compute_orientation_singularities(const MultiResolutionHierarchy &mRes,
std::map<uint32_t, uint32_t> &sing,
bool extrinsic,
int rosy);
extern void compute_position_singularities(const MultiResolutionHierarchy &mRes,
const std::map<uint32_t, uint32_t> &orient_sing,
std::map<uint32_t, Vector2i> &pos_sing,
bool extrinsic,
int rosy,
int posy);
/* Field optimizer (invokes optimization kernels in a separate thread) */
class Serializer;
class Optimizer {
public:
/* clang-format off */
Optimizer(MultiResolutionHierarchy &mRes, bool interactive);
void save(Serializer &state);
void load(const Serializer &state);
void stop() {
if (mOptimizeOrientations)
mRes.propagateSolution(mRoSy);
mOptimizePositions = mOptimizeOrientations = false;
notify();
}
void shutdown() { mRunning = false; notify(); mThread.join(); }
bool active() { return mOptimizePositions | mOptimizeOrientations; }
inline void notify() { mCond.notify_all(); }
void optimizeOrientations(int level);
void optimizePositions(int level);
void wait();
void setExtrinsic(bool extrinsic) { mExtrinsic = extrinsic; }
bool extrinsic() const { return mExtrinsic; }
void setRoSy(int rosy) { mRoSy = rosy; }
int rosy() const { return mRoSy; }
void setPoSy(int posy) { mPoSy = posy; }
int posy() const { return mPoSy; }
void setLevel(int level) { mLevel = level; }
int level() const { return mLevel; }
Float progress() const { return mProgress; }
#ifdef VISUALIZE_ERROR
const VectorXf &error() { return mError; }
#endif
/* clang-format on */
void moveSingularity(const std::vector<uint32_t> &path, bool orientations)
{
std::lock_guard<ordered_lock> lock(mRes.mutex());
mAttractorStrokes.push_back(std::make_pair(orientations, path));
setLevel(0);
}
void run();
protected:
MultiResolutionHierarchy &mRes;
std::vector<std::pair<bool, std::vector<uint32_t>>> mAttractorStrokes;
bool mRunning;
bool mOptimizeOrientations;
bool mOptimizePositions;
std::thread mThread;
std::condition_variable_any mCond;
int mLevel, mLevelIterations;
bool mHierarchical;
int mRoSy, mPoSy;
bool mExtrinsic;
bool mInteractive;
double mLastUpdate;
Float mProgress;
#ifdef VISUALIZE_ERROR
VectorXf mError;
#endif
Timer<> mTimer;
};

View File

@@ -0,0 +1,88 @@
/*
glutil.cpp: Represents the state of an OpenGL shader together with attached
buffers. The entire state can be serialized and unserialized from a file,
which is useful for debugging.
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#include "glutil.h"
void SerializableGLShader::load(const Serializer &serializer) {
serializer.pushPrefix(mName);
std::vector<std::string> key_list = serializer.getKeys();
std::set<std::string> keys;
for (auto key : key_list) {
if (key.find(".") != std::string::npos)
keys.insert(key.substr(0, key.find(".")));
}
bind();
for (auto key : keys) {
if (mBufferObjects.find(key) == mBufferObjects.end()) {
GLuint bufferID;
glGenBuffers(1, &bufferID);
mBufferObjects[key].id = bufferID;
}
Buffer &buf = mBufferObjects[key];
MatrixXu8 data;
serializer.pushPrefix(key);
serializer.get("glType", buf.glType);
serializer.get("compSize", buf.compSize);
serializer.get("dim", buf.dim);
serializer.get("size", buf.size);
serializer.get("version", buf.version);
serializer.get("data", data);
serializer.popPrefix();
size_t totalSize = (size_t) buf.size * (size_t) buf.compSize;
if (key == "indices") {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf.id);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, totalSize,
(void *) data.data(), GL_DYNAMIC_DRAW);
} else {
int attribID = attrib(key);
glEnableVertexAttribArray(attribID);
glBindBuffer(GL_ARRAY_BUFFER, buf.id);
glBufferData(GL_ARRAY_BUFFER, totalSize, (void *) data.data(),
GL_DYNAMIC_DRAW);
glVertexAttribPointer(attribID, buf.dim, buf.glType,
buf.compSize == 1 ? GL_TRUE : GL_FALSE, 0, 0);
}
}
serializer.popPrefix();
}
void SerializableGLShader::save(Serializer &serializer) const {
serializer.pushPrefix(mName);
for (auto &item : mBufferObjects) {
const Buffer &buf = item.second;
size_t totalSize = (size_t) buf.size * (size_t) buf.compSize;
serializer.pushPrefix(item.first);
serializer.set("glType", buf.glType);
serializer.set("compSize", buf.compSize);
serializer.set("dim", buf.dim);
serializer.set("size", buf.size);
serializer.set("version", buf.version);
MatrixXu8 temp(1, totalSize);
if (item.first == "indices") {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf.id);
glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, totalSize,
temp.data());
} else {
glBindBuffer(GL_ARRAY_BUFFER, buf.id);
glGetBufferSubData(GL_ARRAY_BUFFER, 0, totalSize, temp.data());
}
serializer.set("data", temp);
serializer.popPrefix();
}
serializer.popPrefix();
}

View File

@@ -0,0 +1,34 @@
/*
glutil.h: Represents the state of an OpenGL shader together with attached
buffers. The entire state can be serialized and unserialized from a file,
which is useful for debugging.
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#pragma once
#include "serializer.h"
#include <nanogui/glutil.h>
#include <half.hpp>
class SerializableGLShader : public nanogui::GLShader {
public:
SerializableGLShader() : nanogui::GLShader() { }
void load(const Serializer &serializer);
void save(Serializer &serializer) const;
// Upload an Eigen matrix and convert to half precision
template <typename Matrix> void uploadAttrib_half(const std::string &name, const Matrix &_M, int version = -1) {
Eigen::Matrix<half_float::half, Eigen::Dynamic, Eigen::Dynamic> M = _M.template cast<half_float::half>();
uploadAttrib(name, M, version);
}
};

View File

@@ -0,0 +1,263 @@
/*
gui_serializer.h: Utility class for serializing the entire user interface
state to a file. which is useful for debugging.
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#pragma once
#include "widgets.h"
#include "serializer.h"
class GUISerializer : public Serializer {
public:
GUISerializer() : Serializer() { }
GUISerializer(const std::string &filename, bool compat) : Serializer(filename, compat) { }
GUISerializer(const std::string &filename, bool compatibilityMode = false,
const ProgressCallback &progress = ProgressCallback()) : Serializer(filename, compatibilityMode, progress) { }
using Serializer::get;
using Serializer::set;
bool get(const std::string &key, Widget *widget) const {
pushPrefix(key);
bool result = getRecursive(widget);
popPrefix();
return result;
}
void set(const std::string &key, const Widget *widget) {
pushPrefix(key);
setRecursive(widget);
popPrefix();
}
protected:
void setRecursive(const Widget *widget) {
bool serialize = widget->id().length() > 0;
if (serialize) {
pushPrefix(widget->id());
if (dynamic_cast<const TextBox *>(widget))
setTextBox((const TextBox *) widget);
else if (dynamic_cast<const Slider *>(widget))
setSlider((const Slider *) widget);
else if (dynamic_cast<const ComboBox *>(widget))
setComboBox((const ComboBox *) widget);
else if (dynamic_cast<const CheckBox *>(widget))
setCheckBox((const CheckBox *) widget);
else if (dynamic_cast<const ProgressButton *>(widget))
setProgressButton((const ProgressButton *) widget);
else if (dynamic_cast<const Button *>(widget))
setButton((const Button *) widget);
else if (dynamic_cast<const Label *>(widget))
setLabel((const Label *) widget);
else
setWidget(widget);
}
for (const Widget *child : widget->children())
setRecursive(child);
if (serialize)
popPrefix();
}
bool getRecursive(Widget *widget) const {
bool serialize = widget->id().length() > 0;
bool success = true;
if (serialize) {
pushPrefix(widget->id());
if (dynamic_cast<TextBox *>(widget))
success &= getTextBox((TextBox *) widget);
else if (dynamic_cast<Slider *>(widget))
success &= getSlider((Slider *) widget);
else if (dynamic_cast<ComboBox *>(widget))
success &= getComboBox((ComboBox *) widget);
else if (dynamic_cast<CheckBox *>(widget))
success &= getCheckBox((CheckBox *) widget);
else if (dynamic_cast<ProgressButton *>(widget))
success &= getProgressButton((ProgressButton *) widget);
else if (dynamic_cast<Button *>(widget))
success &= getButton((Button *) widget);
else if (dynamic_cast<Label *>(widget))
success &= getLabel((Label *) widget);
else
success &= getWidget(widget);
}
for (Widget *child : widget->children())
success &= getRecursive(child);
if (serialize)
popPrefix();
return success;
}
void setWidget(const Widget *widget) {
set("position", widget->position());
set("size", widget->size());
set("fixedSize", widget->fixedSize());
set("visible", widget->visible());
set("enabled", widget->enabled());
set("focused", widget->focused());
set("tooltip", widget->tooltip());
set("fontSize", widget->hasFontSize() ? widget->fontSize() : -1);
}
#define try_get(name, ref) \
if (!get(name, ref)) \
return false;
bool getWidget(Widget *widget) const {
Vector2i v2i_val;
bool bool_val;
std::string str_val;
int int_val;
try_get("position", v2i_val); widget->setPosition(v2i_val);
try_get("size", v2i_val); widget->setSize(v2i_val);
try_get("fixedSize", v2i_val); widget->setFixedSize(v2i_val);
try_get("visible", bool_val); widget->setVisible(bool_val);
try_get("enabled", bool_val); widget->setEnabled(bool_val);
try_get("focused", bool_val); widget->setFocused(bool_val);
try_get("tooltip", str_val); widget->setTooltip(str_val);
try_get("fontSize", int_val); widget->setFontSize(int_val);
return true;
}
void setLabel(const Label *widget) {
setWidget(widget);
set("caption", widget->caption());
set("font", widget->font());
set("fontSize", widget->fontSize());
}
bool getLabel(Label *widget) const {
getWidget(widget);
std::string str_val;
try_get("caption", str_val); widget->setCaption(str_val);
try_get("font", str_val); widget->setFont(str_val);
return true;
}
void setButton(const Button *widget) {
setWidget(widget);
set("caption", widget->caption());
set("icon", widget->icon());
set("iconPosition", (int) widget->iconPosition());
set("pushed", widget->pushed());
set("buttonFlags", widget->flags());
set("backgroundColor", widget->backgroundColor());
set("textColor", widget->textColor());
}
bool getButton(Button *widget) const {
getWidget(widget);
Color col_val;
int int_val;
bool bool_val;
std::string str_val;
try_get("caption", str_val); widget->setCaption(str_val);
try_get("icon", int_val); widget->setIcon(int_val);
try_get("iconPosition", int_val); widget->setIconPosition((Button::IconPosition) int_val);
try_get("pushed", bool_val); widget->setPushed(bool_val);
try_get("buttonFlags", int_val); widget->setFlags(int_val);
try_get("backgroundColor", col_val); widget->setBackgroundColor(col_val);
try_get("textColor", col_val); widget->setTextColor(col_val);
try_get("fontSize", int_val); widget->setFontSize(int_val);
return true;
}
void setProgressButton(const ProgressButton *widget) {
setButton(widget);
set("progress", widget->progress());
}
bool getProgressButton(ProgressButton *widget) const {
getButton(widget);
Float float_val;
try_get("progress", float_val); widget->setProgress(float_val);
return true;
}
void setCheckBox(const CheckBox *widget) {
setWidget(widget);
set("caption", widget->caption());
set("pushed", widget->pushed());
set("checked", widget->checked());
}
bool getCheckBox(CheckBox *widget) const {
getWidget(widget);
bool bool_val;
std::string str_val;
try_get("caption", str_val); widget->setCaption(str_val);
try_get("pushed", bool_val); widget->setPushed(bool_val);
try_get("checked", bool_val); widget->setChecked(bool_val);
return true;
}
void setComboBox(const ComboBox *widget) {
setButton(widget);
set("selectedIndex", widget->selectedIndex());
}
bool getComboBox(ComboBox *widget) const {
getButton(widget);
int int_val;
try_get("selectedIndex", int_val); widget->setSelectedIndex(int_val);
return true;
}
void setSlider(const Slider *widget) {
setWidget(widget);
set("value", widget->value());
set("highlightedRange.min", widget->highlightedRange().first);
set("highlightedRange.max", widget->highlightedRange().second);
}
bool getSlider(Slider *widget) const {
getWidget(widget);
float float_val, float_val_2;
try_get("value", float_val); widget->setValue(float_val);
try_get("highlightedRange.min", float_val);
try_get("highlightedRange.max", float_val_2);
widget->setHighlightedRange(std::make_pair(float_val, float_val_2));
return true;
}
void setTextBox(const TextBox *widget) {
setWidget(widget);
set("value", widget->value());
set("units", widget->units());
set("unitsImage", widget->unitsImage());
}
bool getTextBox(TextBox *widget) const {
getWidget(widget);
std::string str_value;
int int_value;
try_get("value", str_value); widget->setValue(str_value);
try_get("units", str_value); widget->setUnits(str_value);
try_get("unitsImage", int_value); widget->setUnitsImage(int_value);
return true;
}
#undef try_get
};

View File

@@ -0,0 +1,873 @@
/*
hierarchy.cpp: Code to generate unstructured multi-resolution hierarchies
from meshes or point clouds
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#include "hierarchy.h"
#include "serializer.h"
#include "dedge.h"
#include "field.h"
#include <parallel_stable_sort.h>
#include <pcg32.h>
AdjacencyMatrix downsample_graph(const AdjacencyMatrix adj, const MatrixXf &V,
const MatrixXf &N, const VectorXf &A,
MatrixXf &V_p, MatrixXf &N_p, VectorXf &A_p,
MatrixXu &to_upper, VectorXu &to_lower,
bool deterministic,
const ProgressCallback &progress) {
struct Entry {
uint32_t i, j;
float order;
inline Entry() { };
inline Entry(uint32_t i, uint32_t j, float order) : i(i), j(j), order(order) { }
inline bool operator<(const Entry &e) const { return order > e.order; }
};
uint32_t nLinks = adj[V.cols()] - adj[0];
Entry *entries = new Entry[nLinks];
Timer<> timer;
cout << " Collapsing .. ";
cout.flush();
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t) V.cols(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t i = range.begin(); i != range.end(); ++i) {
uint32_t nNeighbors = adj[i + 1] - adj[i];
uint32_t base = adj[i] - adj[0];
for (uint32_t j = 0; j < nNeighbors; ++j) {
uint32_t k = adj[i][j].id;
Float dp = N.col(i).dot(N.col(k));
Float ratio = A[i]>A[k] ? (A[i]/A[k]) : (A[k]/A[i]);
entries[base + j] = Entry(i, k, dp * ratio);
}
}
SHOW_PROGRESS_RANGE(range, V.cols(), "Downsampling graph (1/6)");
}
);
if (progress)
progress("Downsampling graph (2/6)", 0.0f);
if (deterministic)
pss::parallel_stable_sort(entries, entries + nLinks, std::less<Entry>());
else
tbb::parallel_sort(entries, entries + nLinks, std::less<Entry>());
std::vector<bool> mergeFlag(V.cols(), false);
uint32_t nCollapsed = 0;
for (uint32_t i=0; i<nLinks; ++i) {
const Entry &e = entries[i];
if (mergeFlag[e.i] || mergeFlag[e.j])
continue;
mergeFlag[e.i] = mergeFlag[e.j] = true;
entries[nCollapsed++] = entries[i];
}
uint32_t vertexCount = V.cols() - nCollapsed;
/* Allocate memory for coarsened graph */
V_p.resize(3, vertexCount);
N_p.resize(3, vertexCount);
A_p.resize(vertexCount);
to_upper.resize(2, vertexCount);
to_lower.resize(V.cols());
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t) nCollapsed, GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t i = range.begin(); i != range.end(); ++i) {
const Entry &e = entries[i];
const Float area1 = A[e.i], area2 = A[e.j], surfaceArea = area1+area2;
if (surfaceArea > RCPOVERFLOW)
V_p.col(i) = (V.col(e.i) * area1 + V.col(e.j) * area2) / surfaceArea;
else
V_p.col(i) = (V.col(e.i) + V.col(e.j)) * 0.5f;
Vector3f normal = N.col(e.i) * area1 + N.col(e.j) * area2;
Float norm = normal.norm();
N_p.col(i) = norm > RCPOVERFLOW ? Vector3f(normal / norm)
: Vector3f::UnitX();
A_p[i] = surfaceArea;
to_upper.col(i) << e.i, e.j;
to_lower[e.i] = i; to_lower[e.j] = i;
}
SHOW_PROGRESS_RANGE(range, nCollapsed, "Downsampling graph (3/6)");
}
);
delete[] entries;
std::atomic<int> offset(nCollapsed);
tbb::blocked_range<uint32_t> range(0u, (uint32_t) V.cols(), GRAIN_SIZE);
auto copy_uncollapsed = [&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t i = range.begin(); i != range.end(); ++i) {
if (!mergeFlag[i]) {
uint32_t idx = offset++;
V_p.col(idx) = V.col(i);
N_p.col(idx) = N.col(i);
A_p[idx] = A[i];
to_upper.col(idx) << i, INVALID;
to_lower[i] = idx;
}
}
SHOW_PROGRESS_RANGE(range, V.cols(), "Downsampling graph (4/6)");
};
if (deterministic)
copy_uncollapsed(range);
else
tbb::parallel_for(range, copy_uncollapsed);
VectorXu neighborhoodSize(V_p.cols() + 1);
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t) V_p.cols(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
std::vector<Link> scratch;
for (uint32_t i = range.begin(); i != range.end(); ++i) {
scratch.clear();
for (int j=0; j<2; ++j) {
uint32_t upper = to_upper(j, i);
if (upper == INVALID)
continue;
for (Link *link = adj[upper]; link != adj[upper+1]; ++link)
scratch.push_back(Link(to_lower[link->id], link->weight));
}
std::sort(scratch.begin(), scratch.end());
uint32_t id = INVALID, size = 0;
for (const auto &link : scratch) {
if (id != link.id && link.id != i) {
id = link.id;
++size;
}
}
neighborhoodSize[i+1] = size;
}
SHOW_PROGRESS_RANGE(range, V_p.cols(), "Downsampling graph (5/6)");
}
);
neighborhoodSize[0] = 0;
for (uint32_t i=0; i<neighborhoodSize.size()-1; ++i)
neighborhoodSize[i+1] += neighborhoodSize[i];
uint32_t nLinks_p = neighborhoodSize[neighborhoodSize.size()-1];
AdjacencyMatrix adj_p = new Link*[V_p.size() + 1];
Link *links = new Link[nLinks_p];
for (uint32_t i=0; i<neighborhoodSize.size(); ++i)
adj_p[i] = links + neighborhoodSize[i];
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t) V_p.cols(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
std::vector<Link> scratch;
for (uint32_t i = range.begin(); i != range.end(); ++i) {
scratch.clear();
for (int j=0; j<2; ++j) {
uint32_t upper = to_upper(j, i);
if (upper == INVALID)
continue;
for (Link *link = adj[upper]; link != adj[upper+1]; ++link)
scratch.push_back(Link(to_lower[link->id], link->weight));
}
std::sort(scratch.begin(), scratch.end());
Link *dest = adj_p[i];
uint32_t id = INVALID;
for (const auto &link : scratch) {
if (link.id != i) {
if (id != link.id) {
*dest++ = link;
id = link.id;
} else {
dest[-1].weight += link.weight;
}
}
}
}
SHOW_PROGRESS_RANGE(range, V_p.cols(), "Downsampling graph (6/6)");
}
);
cout << "done. (" << V.cols() << " -> " << V_p.cols() << " vertices, took "
<< timeString(timer.value()) << ")" << endl;
return adj_p;
}
void generate_graph_coloring_deterministic(const AdjacencyMatrix &adj, uint32_t size,
std::vector<std::vector<uint32_t> > &phases,
const ProgressCallback &progress) {
if (progress)
progress("Graph coloring", 0.0f);
phases.clear();
cout << " Coloring .. ";
cout.flush();
Timer<> timer;
std::vector<uint32_t> perm(size);
for (uint32_t i=0; i<size; ++i)
perm[i] = i;
pcg32 rng;
rng.shuffle(perm.begin(), perm.end());
std::vector<int> color(size, -1);
std::vector<uint8_t> possible_colors;
std::vector<int> size_per_color;
int ncolors = 0;
for (uint32_t i=0; i<size; ++i) {
uint32_t ip = perm[i];
SHOW_PROGRESS(i, size, "Graph coloring");
std::fill(possible_colors.begin(), possible_colors.end(), 1);
for (const Link *link = adj[ip]; link != adj[ip+1]; ++link) {
int c = color[link->id];
if (c >= 0)
possible_colors[c] = 0;
}
int chosen_color = -1;
for (uint32_t j=0; j<possible_colors.size(); ++j) {
if (possible_colors[j]) {
chosen_color = j;
break;
}
}
if (chosen_color < 0) {
chosen_color = ncolors++;
possible_colors.resize(ncolors);
size_per_color.push_back(0);
}
color[ip] = chosen_color;
size_per_color[chosen_color]++;
}
phases.resize(ncolors);
for (int i=0; i<ncolors; ++i)
phases[i].reserve(size_per_color[i]);
for (uint32_t i=0; i<size; ++i)
phases[color[i]].push_back(i);
cout << "done. (" << phases.size() << " colors, took "
<< timeString(timer.value()) << ")" << endl;
}
void generate_graph_coloring(const AdjacencyMatrix &adj, uint32_t size,
std::vector<std::vector<uint32_t> > &phases,
const ProgressCallback &progress) {
struct ColorData {
uint8_t nColors;
uint32_t nNodes[256];
ColorData() : nColors(0) { }
};
const uint8_t INVALID_COLOR = 0xFF;
if (progress)
progress("Graph coloring", 0.0f);
phases.clear();
cout << " Coloring .. ";
cout.flush();
Timer<> timer;
/* Generate a permutation */
std::vector<uint32_t> perm(size);
std::vector<tbb::spin_mutex> mutex(size);
for (uint32_t i=0; i<size; ++i)
perm[i] = i;
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, size, GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
pcg32 rng;
rng.advance(range.begin());
for (uint32_t i = range.begin(); i != range.end(); ++i) {
uint32_t j = i, k =
rng.nextUInt(size - i) + i;
if (j == k)
continue;
if (j > k)
std::swap(j, k);
tbb::spin_mutex::scoped_lock l0(mutex[j]);
tbb::spin_mutex::scoped_lock l1(mutex[k]);
std::swap(perm[j], perm[k]);
}
}
);
std::vector<uint8_t> color(size, INVALID_COLOR);
ColorData colorData = tbb::parallel_reduce(
tbb::blocked_range<uint32_t>(0u, size, GRAIN_SIZE),
ColorData(),
[&](const tbb::blocked_range<uint32_t> &range, ColorData colorData) -> ColorData {
std::vector<uint32_t> neighborhood;
bool possible_colors[256];
for (uint32_t pidx = range.begin(); pidx != range.end(); ++pidx) {
uint32_t i = perm[pidx];
neighborhood.clear();
neighborhood.push_back(i);
for (const Link *link = adj[i]; link != adj[i+1]; ++link)
neighborhood.push_back(link->id);
std::sort(neighborhood.begin(), neighborhood.end());
for (uint32_t j: neighborhood)
mutex[j].lock();
std::fill(possible_colors, possible_colors + colorData.nColors, true);
for (const Link *link = adj[i]; link != adj[i+1]; ++link) {
uint8_t c = color[link->id];
if (c != INVALID_COLOR) {
while (c >= colorData.nColors) {
possible_colors[colorData.nColors] = true;
colorData.nNodes[colorData.nColors] = 0;
colorData.nColors++;
}
possible_colors[c] = false;
}
}
uint8_t chosen_color = INVALID_COLOR;
for (uint8_t j=0; j<colorData.nColors; ++j) {
if (possible_colors[j]) {
chosen_color = j;
break;
}
}
if (chosen_color == INVALID_COLOR) {
if (colorData.nColors == INVALID_COLOR-1)
throw std::runtime_error("Ran out of colors during graph coloring! "
"The input mesh is very likely corrupt.");
colorData.nNodes[colorData.nColors] = 1;
color[i] = colorData.nColors++;
} else {
colorData.nNodes[chosen_color]++;
color[i] = chosen_color;
}
for (uint32_t j: neighborhood)
mutex[j].unlock();
}
SHOW_PROGRESS_RANGE(range, size, "Graph coloring");
return colorData;
},
[](ColorData c1, ColorData c2) -> ColorData {
ColorData result;
result.nColors = std::max(c1.nColors, c2.nColors);
memset(result.nNodes, 0, sizeof(uint32_t) * result.nColors);
for (uint8_t i=0; i<c1.nColors; ++i)
result.nNodes[i] += c1.nNodes[i];
for (uint8_t i=0; i<c2.nColors; ++i)
result.nNodes[i] += c2.nNodes[i];
return result;
}
);
phases.resize(colorData.nColors);
for (int i=0; i<colorData.nColors; ++i)
phases[i].reserve(colorData.nNodes[i]);
for (uint32_t i=0; i<size; ++i)
phases[color[i]].push_back(i);
cout << "done. (" << phases.size() << " colors, took "
<< timeString(timer.value()) << ")" << endl;
}
MultiResolutionHierarchy::MultiResolutionHierarchy() {
if (sizeof(Link) != 12)
throw std::runtime_error("Adjacency matrix entries are not packed! Investigate compiler settings.");
mA.reserve(MAX_DEPTH+1);
mV.reserve(MAX_DEPTH+1);
mN.reserve(MAX_DEPTH+1);
mQ.reserve(MAX_DEPTH+1);
mO.reserve(MAX_DEPTH+1);
mCQ.reserve(MAX_DEPTH+1);
mCQw.reserve(MAX_DEPTH+1);
mCO.reserve(MAX_DEPTH+1);
mCOw.reserve(MAX_DEPTH+1);
mAdj.reserve(MAX_DEPTH+1);
mToUpper.reserve(MAX_DEPTH);
mToLower.reserve(MAX_DEPTH);
mIterationsQ = mIterationsO = -1;
mScale = 0;
mTotalSize = 0;
mFrozenO = mFrozenQ = false;
}
void MultiResolutionHierarchy::build(bool deterministic, const ProgressCallback &progress) {
std::vector<std::vector<uint32_t>> phases;
cout << "Processing level 0 .." << endl;
if (deterministic)
generate_graph_coloring_deterministic(mAdj[0], mV[0].cols(), phases, progress);
else
generate_graph_coloring(mAdj[0], mV[0].cols(), phases, progress);
mPhases.push_back(phases);
mTotalSize = mV[0].cols();
mCO.push_back(MatrixXf());
mCOw.push_back(VectorXf());
mCQ.push_back(MatrixXf());
mCQw.push_back(VectorXf());
cout << "Building multiresolution hierarchy .." << endl;
Timer<> timer;
for (int i=0; i<MAX_DEPTH; ++i) {
std::vector<std::vector<uint32_t>> phases_p;
MatrixXf N_p, V_p;
VectorXf A_p;
MatrixXu toUpper;
VectorXu toLower;
AdjacencyMatrix adj_p =
downsample_graph(mAdj[i], mV[i], mN[i], mA[i], V_p, N_p, A_p,
toUpper, toLower, deterministic, progress);
if (deterministic)
generate_graph_coloring_deterministic(adj_p, V_p.cols(), phases_p, progress);
else
generate_graph_coloring(adj_p, V_p.cols(), phases_p, progress);
mTotalSize += V_p.cols();
mPhases.push_back(std::move(phases_p));
mAdj.push_back(std::move(adj_p));
mV.push_back(std::move(V_p));
mN.push_back(std::move(N_p));
mA.push_back(std::move(A_p));
mToUpper.push_back(std::move(toUpper));
mToLower.push_back(std::move(toLower));
mCO.push_back(MatrixXf());
mCOw.push_back(VectorXf());
mCQ.push_back(MatrixXf());
mCQw.push_back(VectorXf());
if (mV[mV.size()-1].cols() == 1)
break;
}
mIterationsQ = mIterationsO = -1;
mFrozenO = mFrozenQ = false;
cout << "Hierarchy construction took " << timeString(timer.value()) << "." << endl;
}
void init_random_tangent(const MatrixXf &N, MatrixXf &Q) {
Q.resize(N.rows(), N.cols());
tbb::parallel_for(tbb::blocked_range<uint32_t>(0u, (uint32_t) N.cols()),
[&](const tbb::blocked_range<uint32_t> &range) {
pcg32 rng;
rng.advance(range.begin());
for (uint32_t i = range.begin(); i != range.end(); ++i) {
Vector3f s, t;
coordinate_system(N.col(i), s, t);
float angle = rng.nextFloat() * 2 * M_PI;
Q.col(i) = s * std::cos(angle) + t * std::sin(angle);
}
}
);
}
void init_random_position(const MatrixXf &P, const MatrixXf &N, MatrixXf &O, Float scale) {
O.resize(N.rows(), N.cols());
tbb::parallel_for(tbb::blocked_range<uint32_t>(0u, (uint32_t) N.cols()),
[&](const tbb::blocked_range<uint32_t> &range) {
pcg32 rng;
rng.advance(2*range.begin());
for (uint32_t i = range.begin(); i != range.end(); ++i) {
Vector3f s, t;
coordinate_system(N.col(i), s, t);
float x = rng.nextFloat() * 2.f - 1.f,
y = rng.nextFloat() * 2.f - 1.f;
O.col(i) = P.col(i) + (s*x + t*y)*scale;
}
}
);
}
void MultiResolutionHierarchy::resetSolution() {
cout << "Setting to random solution .. ";
cout.flush();
Timer<> timer;
if (mQ.size() != mV.size()) {
mQ.resize(mV.size());
mO.resize(mV.size());
}
for (size_t i=0; i<mV.size(); ++i) {
init_random_tangent(mN[i], mQ[i]);
init_random_position(mV[i], mN[i], mO[i], mScale);
}
mFrozenO = mFrozenQ = false;
cout << "done. (took " << timeString(timer.value()) << ")" << endl;
}
void MultiResolutionHierarchy::free() {
for (size_t i=0; i<mAdj.size(); ++i) {
delete[] mAdj[i][0];
delete[] mAdj[i];
}
mAdj.clear(); mV.clear(); mQ.clear();
mO.clear(); mN.clear(); mA.clear();
mCQ.clear(); mCO.clear();
mCQw.clear(); mCOw.clear();
mToUpper.clear(); mToLower.clear();
mPhases.clear();
mF.resize(0, 0);
mE2E.resize(0);
mTotalSize = 0;
}
void MultiResolutionHierarchy::save(Serializer &serializer) {
serializer.set("levels", (uint32_t) mV.size());
serializer.set("iterations_O", mIterationsO);
serializer.set("iterations_Q", mIterationsQ);
serializer.set("totalSize", mTotalSize);
serializer.set("scale", mScale);
serializer.set("F", mF);
serializer.set("E2E", mE2E);
serializer.set("frozenO", mFrozenO);
serializer.set("frozenQ", mFrozenQ);
for (uint32_t i=0; i<mV.size(); ++i) {
serializer.pushPrefix(std::to_string(i));
serializer.set("V", mV[i]);
serializer.set("N", mN[i]);
serializer.set("A", mA[i]);
serializer.set("O", mO[i]);
serializer.set("Q", mQ[i]);
if (i < mV.size() - 1) {
serializer.set("toLower", mToLower[i]);
serializer.set("toUpper", mToUpper[i]);
}
serializer.set("CQ", mCQ[i]);
serializer.set("CQw", mCQw[i]);
serializer.set("CO", mCO[i]);
serializer.set("COw", mCOw[i]);
const AdjacencyMatrix &adj = mAdj[i];
std::vector<std::vector<uint32_t>> link_id(size(i));
std::vector<std::vector<uint32_t>> link_ivar(size(i));
std::vector<std::vector<Float>> link_weight(size(i));
for (uint32_t j=0; j<size(i); ++j) {
size_t size = adj[j+1] - adj[j];
link_id[j].reserve(size);
link_weight[j].reserve(size);
link_ivar[j].reserve(size);
for (const Link *link = adj[j]; link != adj[j+1]; ++link) {
link_id[j].push_back(link->id);
link_weight[j].push_back(link->weight);
link_ivar[j].push_back(link->ivar_uint32);
}
}
serializer.set("phases", mPhases[i]);
serializer.set("adj_id", link_id);
serializer.set("adj_ivar", link_ivar);
serializer.set("adj_weight", link_weight);
serializer.popPrefix();
}
}
void MultiResolutionHierarchy::load(const Serializer &serializer) {
free();
uint32_t levels;
serializer.get("levels", levels);
serializer.get("iterations_O", mIterationsO);
serializer.get("iterations_Q", mIterationsQ);
serializer.get("totalSize", mTotalSize);
serializer.get("scale", mScale);
serializer.get("F", mF);
serializer.get("E2E", mE2E);
serializer.get("frozenO", mFrozenO);
serializer.get("frozenQ", mFrozenQ);
for (uint32_t i=0; i<levels; ++i) {
serializer.pushPrefix(std::to_string(i));
MatrixXf V, N, O, Q, CQ, CO;
VectorXf A, CQw, COw;
std::vector<std::vector<uint32_t>> phases;
serializer.get("V", V);
serializer.get("N", N);
serializer.get("A", A);
serializer.get("O", O);
serializer.get("Q", Q);
serializer.get("phases", phases);
serializer.get("CQ", CQ);
serializer.get("CQw", CQw);
serializer.get("CO", CO);
serializer.get("COw", COw);
mV.push_back(std::move(V));
mN.push_back(std::move(N));
mA.push_back(std::move(A));
mO.push_back(std::move(O));
mQ.push_back(std::move(Q));
mCQ.push_back(std::move(CQ));
mCO.push_back(std::move(CO));
mCQw.push_back(std::move(CQw));
mCOw.push_back(std::move(COw));
mPhases.push_back(std::move(phases));
if (i < levels - 1) {
MatrixXu toUpper;
VectorXu toLower;
serializer.get("toLower", toLower);
serializer.get("toUpper", toUpper);
mToLower.push_back(std::move(toLower));
mToUpper.push_back(std::move(toUpper));
}
std::vector<std::vector<uint32_t>> adj_id;
std::vector<std::vector<uint32_t>> adj_ivar;
std::vector<std::vector<Float>> adj_weight;
serializer.get("adj_id", adj_id);
serializer.get("adj_ivar", adj_ivar);
serializer.get("adj_weight", adj_weight);
if (adj_id.size() != adj_ivar.size() || adj_ivar.size() != adj_weight.size())
throw std::runtime_error("Could not unserialize data");
uint32_t linkCount = 0;
for (uint32_t j=0; j<adj_id.size(); ++j) {
if (adj_id[j].size() != adj_ivar[j].size() || adj_ivar[j].size() != adj_weight[j].size())
throw std::runtime_error("Could not unserialize data");
linkCount += adj_id[j].size();
}
AdjacencyMatrix adj = new Link*[adj_id.size() + 1];
adj[0] = new Link[linkCount];
for (uint32_t j=0; j<adj_id.size(); ++j)
adj[j+1] = adj[j] + adj_id[j].size();
for (uint32_t j=0; j<adj_id.size(); ++j) {
for (uint32_t k=0; k<adj_id[j].size(); ++k) {
adj[j][k].id = adj_id[j][k];
adj[j][k].ivar_uint32 = adj_ivar[j][k];
adj[j][k].weight = adj_weight[j][k];
}
}
mAdj.push_back(adj);
serializer.popPrefix();
}
}
void MultiResolutionHierarchy::clearConstraints() {
if (levels() == 0)
return;
if (mCQ[0].size() == 0)
cout << "Allocating memory for constraints .." << endl;
for (int i=0; i<levels(); ++i) {
mCQ[i].resize(3, size(i));
mCO[i].resize(3, size(i));
mCQw[i].resize(size(i));
mCOw[i].resize(size(i));
mCQw[i].setZero();
mCOw[i].setZero();
}
}
void MultiResolutionHierarchy::propagateSolution(int rosy) {
auto compat_orient = compat_orientation_extrinsic_2;
if (rosy == 2)
;
else if (rosy == 4)
compat_orient = compat_orientation_extrinsic_4;
else if (rosy == 6)
compat_orient = compat_orientation_extrinsic_6;
else
throw std::runtime_error("Unsupported symmetry!");
cout << "Propagating updated solution.. ";
cout.flush();
Timer<> timer;
for (int l=0; l<levels()-1; ++l) {
const MatrixXf &N = mN[l];
const MatrixXf &N_next = mN[l+1];
const MatrixXf &Q = mQ[l];
MatrixXf &Q_next = mQ[l+1];
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, size(l+1), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t i=range.begin(); i != range.end(); ++i) {
Vector2u upper = toUpper(l).col(i);
Vector3f q0 = Q.col(upper[0]);
Vector3f n0 = N.col(upper[0]);
Vector3f q;
if (upper[1] != INVALID) {
Vector3f q1 = Q.col(upper[1]);
Vector3f n1 = N.col(upper[1]);
auto result = compat_orient(q0, n0, q1, n1);
q = result.first + result.second;
} else {
q = q0;
}
Vector3f n = N_next.col(i);
q -= n.dot(q) * n;
if (q.squaredNorm() > RCPOVERFLOW)
q.normalize();
Q_next.col(i) = q;
}
}
);
}
cout << "done. (took " << timeString(timer.value()) << ")" << endl;
}
void MultiResolutionHierarchy::propagateConstraints(int rosy, int posy) {
if (levels() == 0)
return;
cout << "Propagating constraints .. ";
cout.flush();
Timer<> timer;
auto compat_orient = compat_orientation_extrinsic_2;
if (rosy == 2)
;
else if (rosy == 4)
compat_orient = compat_orientation_extrinsic_4;
else if (rosy == 6)
compat_orient = compat_orientation_extrinsic_6;
else
throw std::runtime_error("Unsupported symmetry!");
auto compat_pos = compat_position_extrinsic_4;
if (posy == 4)
;
else if (posy == 3)
compat_pos = compat_position_extrinsic_3;
else
throw std::runtime_error("Unsupported symmetry!");
Float scale = mScale, inv_scale = 1/mScale;
for (int l=0; l<levels()-1; ++l) {
const MatrixXf &N = mN[l];
const MatrixXf &N_next = mN[l+1];
const MatrixXf &V = mV[l];
const MatrixXf &V_next = mV[l+1];
const MatrixXf &CQ = mCQ[l];
MatrixXf &CQ_next = mCQ[l+1];
const VectorXf &CQw = mCQw[l];
VectorXf &CQw_next = mCQw[l+1];
const MatrixXf &CO = mCO[l];
MatrixXf &CO_next = mCO[l+1];
const VectorXf &COw = mCOw[l];
VectorXf &COw_next = mCOw[l+1];
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, size(l+1), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t i=range.begin(); i != range.end(); ++i) {
Vector2u upper = toUpper(l).col(i);
Vector3f cq = Vector3f::Zero(), co = Vector3f::Zero();
Float cqw = 0.0f, cow = 0.0f;
bool has_cq0 = CQw[upper[0]] != 0;
bool has_cq1 = upper[1] != INVALID && CQw[upper[1]] != 0;
bool has_co0 = COw[upper[0]] != 0;
bool has_co1 = upper[1] != INVALID && COw[upper[1]] != 0;
if (has_cq0 && !has_cq1) {
cq = CQ.col(upper[0]);
cqw = CQw[upper[0]];
} else if (has_cq1 && !has_cq0) {
cq = CQ.col(upper[1]);
cqw = CQw[upper[1]];
} else if (has_cq1 && has_cq0) {
auto result = compat_orient(CQ.col(upper[0]), N.col(upper[0]), CQ.col(upper[1]), N.col(upper[1]));
cq = result.first * CQw[upper[0]] + result.second * CQw[upper[1]];
cqw = (CQw[upper[0]] + CQw[upper[1]]);
}
if (cq != Vector3f::Zero()) {
Vector3f n = N_next.col(i);
cq -= n.dot(cq) * n;
if (cq.squaredNorm() > RCPOVERFLOW)
cq.normalize();
}
if (has_co0 && !has_co1) {
co = CO.col(upper[0]);
cow = COw[upper[0]];
} else if (has_co1 && !has_co0) {
co = CO.col(upper[1]);
cow = COw[upper[1]];
} else if (has_co1 && has_co0) {
auto result = compat_pos(
V.col(upper[0]), N.col(upper[0]), CQ.col(upper[0]), CO.col(upper[0]),
V.col(upper[1]), N.col(upper[1]), CQ.col(upper[1]), CO.col(upper[1]),
scale, inv_scale
);
cow = COw[upper[0]] + COw[upper[1]];
co = (result.first * COw[upper[0]] + result.second * COw[upper[1]]) / cow;
}
if (co != Vector3f::Zero()) {
Vector3f n = N_next.col(i), v = V_next.col(i);
co -= n.dot(cq - v) * n;
}
#if 0
cqw *= 0.5f;
cow *= 0.5f;
#else
if (cqw > 0)
cqw = 1;
if (cow > 0)
cow = 1;
#endif
CQw_next[i] = cqw;
COw_next[i] = cow;
CQ_next.col(i) = cq;
CO_next.col(i) = co;
}
}
);
}
cout << "done. (took " << timeString(timer.value()) << ")" << endl;
}
void MultiResolutionHierarchy::printStatistics() const {
if (levels() == 0)
return;
std::ostringstream oss;
size_t field_s = 0, V_s = 0, N_s = 0, A_s = 0, adj_s = 0, tree_s = 0,
phases_s = 0, cedge_s = 0, cvertex_s = 0;
for (int i=0; i<levels(); ++i) {
field_s += sizeInBytes(mQ[i]) + sizeInBytes(mO[i]);
V_s += sizeInBytes(mV[i]);
N_s += sizeInBytes(mN[i]);
A_s += sizeInBytes(mA[i]);
adj_s += (mAdj[i][mV[i].cols()] - mAdj[i][0]) * sizeof(Link) + mV[i].cols() * sizeof(Link *);
phases_s += mPhases[i].size() * sizeof(std::vector<uint32_t>) + mV[i].cols() * sizeof(uint32_t);
}
for (int i=0; i<levels()-1; ++i) {
tree_s += sizeInBytes(mToUpper[i]);
tree_s += sizeInBytes(mToLower[i]);
}
cvertex_s = sizeInBytes(mF);
cedge_s = sizeInBytes(mE2E);
cout << endl;
cout << "Multiresolution hierarchy statistics:" << endl;
cout << " Field data : " << memString(field_s) << endl;
cout << " Vertex data : " << memString(V_s + N_s + A_s) << " (level 0: "
<< memString(sizeInBytes(mV[0]) + sizeInBytes(mN[0]) + sizeInBytes(mA[0])) << ")" << endl;
cout << " Adjacency matrices : " << memString(adj_s) << " (level 0: "
<< memString((mAdj[0][mV[0].cols()] - mAdj[0][0]) * sizeof(Link)) << ")" << endl;
cout << " Tree connectivity : " << memString(tree_s) << endl;
cout << " Vertex indices : " << memString(cvertex_s) << endl;
cout << " Edge connectivity : " << memString(cedge_s) << endl;
cout << " Parallel phases : " << memString(phases_s) << endl;
cout << " Total : "
<< memString(field_s + V_s + N_s + A_s + adj_s + tree_s + cedge_s + cvertex_s + phases_s) << endl;
}

View File

@@ -0,0 +1,267 @@
/*
hierarchy.h: Code to generate unstructured multi-resolution hierarchies
from meshes or point clouds
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#pragma once
#include "adjacency.h"
class Serializer;
extern AdjacencyMatrix downsample_graph(const AdjacencyMatrix adj,
const MatrixXf &V,
const MatrixXf &N,
const VectorXf &areas,
MatrixXf &V_p,
MatrixXf &V_n,
VectorXf &areas_p,
MatrixXu &to_upper,
VectorXu &to_lower,
bool deterministic = false,
const ProgressCallback &progress = ProgressCallback());
struct MultiResolutionHierarchy {
enum { MAX_DEPTH = 25 };
public:
MultiResolutionHierarchy();
void free();
void save(Serializer &state);
void load(const Serializer &state);
int levels() const
{
return (int)mV.size();
}
void build(bool deterministic = false, const ProgressCallback &progress = ProgressCallback());
void printStatistics() const;
void resetSolution();
inline ordered_lock &mutex()
{
return mMutex;
}
inline const std::vector<std::vector<uint32_t>> &phases(int level) const
{
return mPhases[level];
}
inline const AdjacencyMatrix &adj(int level = 0) const
{
return mAdj[level];
}
inline AdjacencyMatrix &adj(int level = 0)
{
return mAdj[level];
}
inline const MatrixXf &V(int level = 0) const
{
return mV[level];
}
inline const MatrixXf &N(int level = 0) const
{
return mN[level];
}
inline const VectorXf &A(int level = 0) const
{
return mA[level];
}
inline const MatrixXu &toUpper(int level) const
{
return mToUpper[level];
}
inline const VectorXu &toLower(int level) const
{
return mToLower[level];
}
inline const MatrixXf &Q(int level = 0) const
{
return mQ[level];
}
inline const MatrixXf &O(int level = 0) const
{
return mO[level];
}
inline const MatrixXf &CQ(int level = 0) const
{
return mCQ[level];
}
inline const MatrixXf &CO(int level = 0) const
{
return mCO[level];
}
inline const VectorXf &CQw(int level = 0) const
{
return mCQw[level];
}
inline const VectorXf &COw(int level = 0) const
{
return mCOw[level];
}
inline const MatrixXu &F() const
{
return mF;
}
inline const VectorXu &E2E() const
{
return mE2E;
}
inline MatrixXf &Q(int level = 0)
{
return mQ[level];
}
inline MatrixXf &O(int level = 0)
{
return mO[level];
}
inline MatrixXf &CQ(int level = 0)
{
return mCQ[level];
}
inline MatrixXf &CO(int level = 0)
{
return mCO[level];
}
inline VectorXf &CQw(int level = 0)
{
return mCQw[level];
}
inline VectorXf &COw(int level = 0)
{
return mCOw[level];
}
inline void setF(MatrixXu &&F)
{
mF = std::move(F);
}
inline void setE2E(VectorXu &&E2E)
{
mE2E = std::move(E2E);
}
inline void setV(MatrixXf &&V)
{
mV.clear();
mV.push_back(std::move(V));
}
inline void setN(MatrixXf &&N)
{
mN.clear();
mN.push_back(std::move(N));
}
inline void setA(MatrixXf &&A)
{
mA.clear();
mA.push_back(std::move(A));
}
inline void setAdj(AdjacencyMatrix &&adj)
{
mAdj.clear();
mAdj.push_back(std::move(adj));
}
inline uint32_t size(int level = 0) const
{
return mV[level].cols();
}
inline Float scale() const
{
return mScale;
}
inline void setScale(Float scale)
{
mScale = scale;
}
inline int iterationsQ() const
{
return mIterationsQ;
}
inline void setIterationsQ(int iterationsQ)
{
mIterationsQ = iterationsQ;
}
inline int iterationsO() const
{
return mIterationsO;
}
inline void setIterationsO(int iterationsO)
{
mIterationsO = iterationsO;
}
inline size_t totalSize() const
{
return mTotalSize;
}
void clearConstraints();
void propagateConstraints(int rosy, int posy);
void propagateSolution(int rosy);
inline Vector3f faceCenter(uint32_t idx) const
{
Vector3f pos = Vector3f::Zero();
for (int i = 0; i < 3; ++i)
pos += mV[0].col(mF(i, idx));
return pos * (1.0f / 3.0f);
}
inline Vector3f faceNormal(uint32_t idx) const
{
Vector3f p0 = mV[0].col(mF(0, idx)), p1 = mV[0].col(mF(1, idx)), p2 = mV[0].col(mF(2, idx));
return (p1 - p0).cross(p2 - p0).normalized();
}
/* Flags which indicate whether the integer variables are froen */
bool frozenQ() const
{
return mFrozenQ;
}
bool frozenO() const
{
return mFrozenO;
}
void setFrozenQ(bool frozen)
{
mFrozenQ = frozen;
}
void setFrozenO(bool frozen)
{
mFrozenO = frozen;
}
public:
MatrixXu mF;
VectorXu mE2E;
std::vector<std::vector<std::vector<uint32_t>>> mPhases;
std::vector<AdjacencyMatrix> mAdj;
std::vector<MatrixXf> mV;
std::vector<MatrixXf> mN;
std::vector<VectorXf> mA;
std::vector<VectorXu> mToLower;
std::vector<MatrixXu> mToUpper;
std::vector<MatrixXf> mO;
std::vector<MatrixXf> mQ;
std::vector<MatrixXf> mCQ;
std::vector<MatrixXf> mCO;
std::vector<VectorXf> mCQw;
std::vector<VectorXf> mCOw;
bool mFrozenQ, mFrozenO;
ordered_lock mMutex;
Float mScale;
int mIterationsQ;
int mIterationsO;
uint32_t mTotalSize;
};

View File

@@ -0,0 +1,226 @@
/*
main.cpp -- Instant Meshes application entry point
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#include "batch.h"
#include "viewer.h"
#include "serializer.h"
#include <thread>
#include <cstdlib>
/* Force usage of discrete GPU on laptops */
NANOGUI_FORCE_DISCRETE_GPU();
int nprocs = -1;
int main(int argc, char **argv) {
std::vector<std::string> args;
bool extrinsic = true, dominant = false, align_to_boundaries = false;
bool fullscreen = false, help = false, deterministic = false, compat = false;
int rosy = 4, posy = 4, face_count = -1, vertex_count = -1;
uint32_t knn_points = 10, smooth_iter = 2;
Float crease_angle = -1, scale = -1;
std::string batchOutput;
#if defined(__APPLE__)
bool launched_from_finder = false;
#endif
try {
for (int i=1; i<argc; ++i) {
if (strcmp("--fullscreen", argv[i]) == 0 || strcmp("-F", argv[i]) == 0) {
fullscreen = true;
} else if (strcmp("--help", argv[i]) == 0 || strcmp("-h", argv[i]) == 0) {
help = true;
} else if (strcmp("--deterministic", argv[i]) == 0 || strcmp("-d", argv[i]) == 0) {
deterministic = true;
} else if (strcmp("--intrinsic", argv[i]) == 0 || strcmp("-i", argv[i]) == 0) {
extrinsic = false;
} else if (strcmp("--boundaries", argv[i]) == 0 || strcmp("-b", argv[i]) == 0) {
align_to_boundaries = true;
} else if (strcmp("--threads", argv[i]) == 0 || strcmp("-t", argv[i]) == 0) {
if (++i >= argc) {
cerr << "Missing thread count!" << endl;
return -1;
}
nprocs = str_to_uint32_t(argv[i]);
} else if (strcmp("--smooth", argv[i]) == 0 || strcmp("-S", argv[i]) == 0) {
if (++i >= argc) {
cerr << "Missing smoothing iteration count argument!" << endl;
return -1;
}
smooth_iter = str_to_uint32_t(argv[i]);
} else if (strcmp("--knn", argv[i]) == 0 || strcmp("-k", argv[i]) == 0) {
if (++i >= argc) {
cerr << "Missing knn point count argument!" << endl;
return -1;
}
knn_points = str_to_uint32_t(argv[i]);
} else if (strcmp("--crease", argv[i]) == 0 || strcmp("-c", argv[i]) == 0) {
if (++i >= argc) {
cerr << "Missing crease angle argument!" << endl;
return -1;
}
crease_angle = str_to_float(argv[i]);
} else if (strcmp("--rosy", argv[i]) == 0 || strcmp("-r", argv[i]) == 0) {
if (++i >= argc) {
cerr << "Missing rotation symmetry type!" << endl;
return -1;
}
rosy = str_to_int32_t(argv[i]);
} else if (strcmp("--posy", argv[i]) == 0 || strcmp("-p", argv[i]) == 0) {
if (++i >= argc) {
cerr << "Missing position symmetry type!" << endl;
return -1;
}
posy = str_to_int32_t(argv[i]);
if (posy == 6)
posy = 3;
} else if (strcmp("--scale", argv[i]) == 0 || strcmp("-s", argv[i]) == 0) {
if (++i >= argc) {
cerr << "Missing scale argument!" << endl;
return -1;
}
scale = str_to_float(argv[i]);
} else if (strcmp("--faces", argv[i]) == 0 || strcmp("-f", argv[i]) == 0) {
if (++i >= argc) {
cerr << "Missing face count argument!" << endl;
return -1;
}
face_count = str_to_int32_t(argv[i]);
} else if (strcmp("--vertices", argv[i]) == 0 || strcmp("-v", argv[i]) == 0) {
if (++i >= argc) {
cerr << "Missing vertex count argument!" << endl;
return -1;
}
vertex_count = str_to_int32_t(argv[i]);
} else if (strcmp("--output", argv[i]) == 0 || strcmp("-o", argv[i]) == 0) {
if (++i >= argc) {
cerr << "Missing batch mode output file argument!" << endl;
return -1;
}
batchOutput = argv[i];
} else if (strcmp("--dominant", argv[i]) == 0 || strcmp("-D", argv[i]) == 0) {
dominant = true;
} else if (strcmp("--compat", argv[i]) == 0 || strcmp("-C", argv[i]) == 0) {
compat = true;
#if defined(__APPLE__)
} else if (strncmp("-psn", argv[i], 4) == 0) {
launched_from_finder = true;
#endif
} else {
if (strncmp(argv[i], "-", 1) == 0) {
cerr << "Invalid argument: \"" << argv[i] << "\"!" << endl;
help = true;
}
args.push_back(argv[i]);
}
}
} catch (const std::exception &e) {
cout << "Error: " << e.what() << endl;
help = true;
}
if ((posy != 3 && posy != 4) || (rosy != 2 && rosy != 4 && rosy != 6)) {
cerr << "Error: Invalid symmetry type!" << endl;
help = true;
}
int nConstraints = 0;
nConstraints += scale > 0 ? 1 : 0;
nConstraints += face_count > 0 ? 1 : 0;
nConstraints += vertex_count > 0 ? 1 : 0;
if (nConstraints > 1) {
cerr << "Error: Only one of the --scale, --face and --vertices parameters can be used at once!" << endl;
help = true;
}
if (args.size() > 1 || help || (!batchOutput.empty() && args.size() == 0)) {
cout << "Syntax: " << argv[0] << " [options] <input mesh / point cloud / application state snapshot>" << endl;
cout << "Options:" << endl;
cout << " -o, --output <output> Writes to the specified PLY/OBJ output file in batch mode" << endl;
cout << " -t, --threads <count> Number of threads used for parallel computations" << endl;
cout << " -d, --deterministic Prefer (slower) deterministic algorithms" << endl;
cout << " -c, --crease <degrees> Dihedral angle threshold for creases" << endl;
cout << " -S, --smooth <iter> Number of smoothing & ray tracing reprojection steps (default: 2)" << endl;
cout << " -D, --dominant Generate a tri/quad dominant mesh instead of a pure tri/quad mesh" << endl;
cout << " -i, --intrinsic Intrinsic mode (extrinsic is the default)" << endl;
cout << " -b, --boundaries Align to boundaries (only applies when the mesh is not closed)" << endl;
cout << " -r, --rosy <number> Specifies the orientation symmetry type (2, 4, or 6)" << endl;
cout << " -p, --posy <number> Specifies the position symmetry type (4 or 6)" << endl;
cout << " -s, --scale <scale> Desired world space length of edges in the output" << endl;
cout << " -f, --faces <count> Desired face count of the output mesh" << endl;
cout << " -v, --vertices <count> Desired vertex count of the output mesh" << endl;
cout << " -C, --compat Compatibility mode to load snapshots from old software versions" << endl;
cout << " -k, --knn <count> Point cloud mode: number of adjacent points to consider" << endl;
cout << " -F, --fullscreen Open a full-screen window" << endl;
cout << " -h, --help Display this message" << endl;
return -1;
}
if (args.size() == 0)
cout << "Running in GUI mode, start with -h for instructions on batch mode." << endl;
tbb::task_scheduler_init init(nprocs == -1 ? tbb::task_scheduler_init::automatic : nprocs);
if (!batchOutput.empty() && args.size() == 1) {
try {
batch_process(args[0], batchOutput, rosy, posy, scale, face_count,
vertex_count, crease_angle, extrinsic,
align_to_boundaries, smooth_iter, knn_points,
!dominant, deterministic);
return 0;
} catch (const std::exception &e) {
cerr << "Caught runtime error : " << e.what() << endl;
return -1;
}
}
try {
nanogui::init();
#if defined(__APPLE__)
if (launched_from_finder)
nanogui::chdir_to_bundle_parent();
#endif
{
nanogui::ref<Viewer> viewer = new Viewer(fullscreen, deterministic);
viewer->setVisible(true);
if (args.size() == 1) {
if (Serializer::isSerializedFile(args[0])) {
viewer->loadState(args[0], compat);
} else {
viewer->loadInput(args[0], crease_angle,
scale, face_count, vertex_count,
rosy, posy, knn_points);
viewer->setExtrinsic(extrinsic);
}
}
nanogui::mainloop();
}
nanogui::shutdown();
} catch (const std::runtime_error &e) {
std::string error_msg = std::string("Caught a fatal error: ") + std::string(e.what());
#if defined(_WIN32)
MessageBoxA(nullptr, error_msg.c_str(), NULL, MB_ICONERROR | MB_OK);
#else
std::cerr << error_msg << endl;
#endif
return -1;
}
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,618 @@
/*
meshio.cpp: Mesh file input/output routines
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#include "meshio.h"
#include "normal.h"
#include <unordered_map>
#include <fstream>
#if !defined(_WIN32)
#include <libgen.h>
#endif
extern "C" {
#include "rply.h"
}
void load_mesh_or_pointcloud(const std::string &filename, MatrixXu &F, MatrixXf &V, MatrixXf &N,
const ProgressCallback &progress) {
std::string extension;
if (filename.size() > 4)
extension = str_tolower(filename.substr(filename.size()-4));
if (extension == ".ply")
load_ply(filename, F, V, N, false, progress);
else if (extension == ".obj")
load_obj(filename, F, V, progress);
else if (extension == ".aln")
load_pointcloud(filename, V, N, progress);
else
throw std::runtime_error("load_mesh_or_pointcloud: Unknown file extension \"" + extension + "\" (.ply/.obj/.aln are supported)");
}
void write_mesh(const std::string &filename, const MatrixXu &F,
const MatrixXf &V, const MatrixXf &N, const MatrixXf &Nf,
const MatrixXf &UV, const MatrixXf &C,
const ProgressCallback &progress) {
std::string extension;
if (filename.size() > 4)
extension = str_tolower(filename.substr(filename.size()-4));
if (extension == ".ply")
write_ply(filename, F, V, N, Nf, UV, C, progress);
else if (extension == ".obj")
write_obj(filename, F, V, N, Nf, UV, C, progress);
else
throw std::runtime_error("write_mesh: Unknown file extension \"" + extension + "\" (.ply/.obj are supported)");
}
void load_ply(const std::string &filename, MatrixXu &F, MatrixXf &V,
MatrixXf &N, bool pointcloud, const ProgressCallback &progress) {
auto message_cb = [](p_ply ply, const char *msg) { cerr << "rply: " << msg << endl; };
Timer<> timer;
cout << "Loading \"" << filename << "\" .. ";
cout.flush();
p_ply ply = ply_open(filename.c_str(), message_cb, 0, nullptr);
if (!ply)
throw std::runtime_error("Unable to open PLY file \"" + filename + "\"!");
if (!ply_read_header(ply)) {
ply_close(ply);
throw std::runtime_error("Unable to open PLY header of \"" + filename + "\"!");
}
p_ply_element element = nullptr;
uint32_t vertexCount = 0, faceCount = 0;
/* Inspect the structure of the PLY file, load number of faces if avaliable */
while ((element = ply_get_next_element(ply, element)) != nullptr) {
const char *name;
long nInstances;
ply_get_element_info(element, &name, &nInstances);
if (!strcmp(name, "vertex"))
vertexCount = (uint32_t) nInstances;
else if (!strcmp(name, "face"))
faceCount = (uint32_t) nInstances;
}
if (vertexCount == 0 && faceCount == 0)
throw std::runtime_error("PLY file \"" + filename + "\" is invalid! No face/vertex/elements found!");
else if (!pointcloud && faceCount == 0)
throw std::runtime_error("PLY file \"" + filename + "\" is invalid! No faces found!");
F.resize(3, faceCount);
V.resize(3, vertexCount);
struct VertexCallbackData {
MatrixXf &V;
const ProgressCallback &progress;
VertexCallbackData(MatrixXf &V, const ProgressCallback &progress)
: V(V), progress(progress) { }
};
struct FaceCallbackData {
MatrixXu &F;
const ProgressCallback &progress;
FaceCallbackData(MatrixXu &F, const ProgressCallback &progress)
: F(F), progress(progress) { }
};
struct VertexNormalCallbackData {
MatrixXf &N;
const ProgressCallback &progress;
VertexNormalCallbackData(MatrixXf &_N, const ProgressCallback &progress)
: N(_N), progress(progress) { }
};
auto rply_vertex_cb = [](p_ply_argument argument) -> int {
VertexCallbackData *data; long index, coord;
ply_get_argument_user_data(argument, (void **) &data, &coord);
ply_get_argument_element(argument, nullptr, &index);
data->V(coord, index) = (Float) ply_get_argument_value(argument);
if (data->progress && coord == 0 && index % 500000 == 0)
data->progress("Loading vertex data", index / (Float) data->V.cols());
return 1;
};
auto rply_vertex_normal_cb = [](p_ply_argument argument) -> int {
VertexNormalCallbackData *data; long index, coord;
ply_get_argument_user_data(argument, (void **) &data, &coord);
ply_get_argument_element(argument, nullptr, &index);
data->N(coord, index) = (Float) ply_get_argument_value(argument);
if (data->progress && coord == 0 && index % 500000 == 0)
data->progress("Loading vertex normal data", index / (Float)data->N.cols());
return 1;
};
auto rply_index_cb = [](p_ply_argument argument) -> int {
FaceCallbackData *data;
long length, value_index, index;
ply_get_argument_property(argument, nullptr, &length, &value_index);
if (length != 3)
throw std::runtime_error("Only triangle faces are supported!");
ply_get_argument_user_data(argument, (void **) &data, nullptr);
ply_get_argument_element(argument, nullptr, &index);
if (value_index >= 0)
data->F(value_index, index) = (uint32_t) ply_get_argument_value(argument);
if (data->progress && value_index == 0 && index % 500000 == 0)
data->progress("Loading face data", index / (Float) data->F.cols());
return 1;
};
VertexCallbackData vcbData(V, progress);
FaceCallbackData fcbData(F, progress);
VertexNormalCallbackData vncbData(N, progress);
if (!ply_set_read_cb(ply, "vertex", "x", rply_vertex_cb, &vcbData, 0) ||
!ply_set_read_cb(ply, "vertex", "y", rply_vertex_cb, &vcbData, 1) ||
!ply_set_read_cb(ply, "vertex", "z", rply_vertex_cb, &vcbData, 2)) {
ply_close(ply);
throw std::runtime_error("PLY file \"" + filename + "\" does not contain vertex position data!");
}
if (pointcloud && faceCount == 0) {
N.resize(3, vertexCount);
if (!ply_set_read_cb(ply, "vertex", "nx", rply_vertex_normal_cb, &vncbData, 0) ||
!ply_set_read_cb(ply, "vertex", "ny", rply_vertex_normal_cb, &vncbData, 1) ||
!ply_set_read_cb(ply, "vertex", "nz", rply_vertex_normal_cb, &vncbData, 2)) {
ply_close(ply);
throw std::runtime_error("PLY file \"" + filename + "\" does not contain vertex normal or face data!");
}
} else {
if (!ply_set_read_cb(ply, "face", "vertex_indices", rply_index_cb, &fcbData, 0)) {
ply_close(ply);
throw std::runtime_error("PLY file \"" + filename + "\" does not contain vertex indices!");
}
}
if (!ply_read(ply)) {
ply_close(ply);
throw std::runtime_error("Error while loading PLY data from \"" + filename + "\"!");
}
ply_close(ply);
cout << "done. (V=" << vertexCount;
if (faceCount > 0)
cout << ", F=" << faceCount;
cout << ", took " << timeString(timer.value()) << ")" << endl;
}
void write_ply(const std::string &filename, const MatrixXu &F,
const MatrixXf &V, const MatrixXf &N, const MatrixXf &Nf, const MatrixXf &UV,
const MatrixXf &C, const ProgressCallback &progress) {
auto message_cb = [](p_ply ply, const char *msg) { cerr << "rply: " << msg << endl; };
Timer<> timer;
cout << "Writing \"" << filename << "\" (V=" << V.cols()
<< ", F=" << F.cols() << ") .. ";
cout.flush();
if (N.size() > 0 && Nf.size() > 0)
throw std::runtime_error("Please specify either face or vertex normals but not both!");
p_ply ply = ply_create(filename.c_str(), PLY_DEFAULT, message_cb, 0, nullptr);
if (!ply)
throw std::runtime_error("Unable to write PLY file!");
ply_add_comment(ply, "Generated by Instant Meshes");
ply_add_element(ply, "vertex", V.cols());
ply_add_scalar_property(ply, "x", PLY_FLOAT);
ply_add_scalar_property(ply, "y", PLY_FLOAT);
ply_add_scalar_property(ply, "z", PLY_FLOAT);
if (N.size() > 0) {
ply_add_scalar_property(ply, "nx", PLY_FLOAT);
ply_add_scalar_property(ply, "ny", PLY_FLOAT);
ply_add_scalar_property(ply, "nz", PLY_FLOAT);
if (N.cols() != V.cols() || N.rows() != 3)
throw std::runtime_error("Vertex normal matrix has incorrect size");
}
if (UV.size() > 0) {
ply_add_scalar_property(ply, "u", PLY_FLOAT);
ply_add_scalar_property(ply, "v", PLY_FLOAT);
if (UV.cols() != V.cols() || UV.rows() != 2)
throw std::runtime_error("Texture coordinate matrix has incorrect size");
}
if (C.size() > 0) {
ply_add_scalar_property(ply, "red", PLY_FLOAT);
ply_add_scalar_property(ply, "green", PLY_FLOAT);
ply_add_scalar_property(ply, "blue", PLY_FLOAT);
if (C.cols() != V.cols() || (C.rows() != 3 && C.rows() != 4))
throw std::runtime_error("Color matrix has incorrect size");
}
/* Check for irregular faces */
std::map<uint32_t, std::pair<uint32_t, std::map<uint32_t, uint32_t>>> irregular;
size_t nIrregular = 0;
if (F.rows() == 4) {
for (uint32_t f=0; f<F.cols(); ++f) {
if (F(2, f) == F(3, f)) {
nIrregular++;
auto &value = irregular[F(2, f)];
value.first = f;
value.second[F(0, f)] = F(1, f);
}
}
}
ply_add_element(ply, "face", F.cols() - nIrregular + irregular.size());
ply_add_list_property(ply, "vertex_indices", PLY_UINT8, PLY_INT);
if (Nf.size() > 0) {
ply_add_scalar_property(ply, "nx", PLY_FLOAT);
ply_add_scalar_property(ply, "ny", PLY_FLOAT);
ply_add_scalar_property(ply, "nz", PLY_FLOAT);
if (Nf.cols() != F.cols() || Nf.rows() != 3)
throw std::runtime_error("Face normal matrix has incorrect size");
}
ply_write_header(ply);
for (uint32_t j=0; j<V.cols(); ++j) {
for (uint32_t i=0; i<V.rows(); ++i)
ply_write(ply, V(i, j));
if (N.size() > 0) {
for (uint32_t i=0; i<N.rows(); ++i)
ply_write(ply, N(i, j));
}
if (UV.size() > 0) {
for (uint32_t i=0; i<UV.rows(); ++i)
ply_write(ply, UV(i, j));
}
if (C.size() > 0) {
for (uint32_t i=0; i<std::min(3u, (uint32_t) C.rows()); ++i)
ply_write(ply, C(i, j));
}
if (progress && j % 500000 == 0)
progress("Writing vertex data", j / (Float) V.cols());
}
for (uint32_t f=0; f<F.cols(); ++f) {
if (F.rows() == 4 && F(2, f) == F(3, f))
continue;
ply_write(ply, F.rows());
for (uint32_t i=0; i<F.rows(); ++i)
ply_write(ply, F(i, f));
if (Nf.size() > 0) {
for (uint32_t i=0; i<Nf.rows(); ++i)
ply_write(ply, Nf(i, f));
}
if (progress && f % 500000 == 0)
progress("Writing face data", f / (Float) F.cols());
}
for (auto item : irregular) {
auto face = item.second;
uint32_t v = face.second.begin()->first, first = v, i = 0;
ply_write(ply, face.second.size());
while (true) {
ply_write(ply, v);
v = face.second[v];
++i;
if (v == first || i == face.second.size())
break;
}
while (i != face.second.size()) {
ply_write(ply, v);
++i;
}
if (Nf.size() > 0) {
for (uint32_t i=0; i<Nf.rows(); ++i)
ply_write(ply, Nf(i, face.first));
}
}
ply_close(ply);
cout << "done. (";
if (irregular.size() > 0)
cout << irregular.size() << " irregular faces, ";
cout << "took " << timeString(timer.value()) << ")" << endl;
}
void load_obj(const std::string &filename, MatrixXu &F, MatrixXf &V,
const ProgressCallback &progress) {
/// Vertex indices used by the OBJ format
struct obj_vertex {
uint32_t p = (uint32_t) -1;
uint32_t n = (uint32_t) -1;
uint32_t uv = (uint32_t) -1;
inline obj_vertex() { }
inline obj_vertex(const std::string &string) {
std::vector<std::string> tokens = str_tokenize(string, '/', true);
if (tokens.size() < 1 || tokens.size() > 3)
throw std::runtime_error("Invalid vertex data: \"" + string + "\"");
p = str_to_uint32_t(tokens[0]);
#if 0
if (tokens.size() >= 2 && !tokens[1].empty())
uv = str_to_uint32_t(tokens[1]);
if (tokens.size() >= 3 && !tokens[2].empty())
n = str_to_uint32_t(tokens[2]);
#endif
}
inline bool operator==(const obj_vertex &v) const {
return v.p == p && v.n == n && v.uv == uv;
}
};
/// Hash function for obj_vertex
struct obj_vertexHash : std::unary_function<obj_vertex, size_t> {
std::size_t operator()(const obj_vertex &v) const {
size_t hash = std::hash<uint32_t>()(v.p);
hash = hash * 37 + std::hash<uint32_t>()(v.uv);
hash = hash * 37 + std::hash<uint32_t>()(v.n);
return hash;
}
};
typedef std::unordered_map<obj_vertex, uint32_t, obj_vertexHash> VertexMap;
std::ifstream is(filename);
if (is.fail())
throw std::runtime_error("Unable to open OBJ file \"" + filename + "\"!");
cout << "Loading \"" << filename << "\" .. ";
cout.flush();
Timer<> timer;
std::vector<Vector3f> positions;
//std::vector<Vector2f> texcoords;
//std::vector<Vector3f> normals;
std::vector<uint32_t> indices;
std::vector<obj_vertex> vertices;
VertexMap vertexMap;
std::string line_str;
while (std::getline(is, line_str)) {
std::istringstream line(line_str);
std::string prefix;
line >> prefix;
if (prefix == "v") {
Vector3f p;
line >> p.x() >> p.y() >> p.z();
positions.push_back(p);
} else if (prefix == "vt") {
/*
Vector2f tc;
line >> tc.x() >> tc.y();
texcoords.push_back(tc);
*/
} else if (prefix == "vn") {
/*
Vector3f n;
line >> n.x() >> n.y() >> n.z();
normals.push_back(n);
*/
} else if (prefix == "f") {
std::string v1, v2, v3, v4;
line >> v1 >> v2 >> v3 >> v4;
obj_vertex tri[6];
int nVertices = 3;
tri[0] = obj_vertex(v1);
tri[1] = obj_vertex(v2);
tri[2] = obj_vertex(v3);
if (!v4.empty()) {
/* This is a quad, split into two triangles */
tri[3] = obj_vertex(v4);
tri[4] = tri[0];
tri[5] = tri[2];
nVertices = 6;
}
/* Convert to an indexed vertex list */
for (int i=0; i<nVertices; ++i) {
const obj_vertex &v = tri[i];
VertexMap::const_iterator it = vertexMap.find(v);
if (it == vertexMap.end()) {
vertexMap[v] = (uint32_t) vertices.size();
indices.push_back((uint32_t) vertices.size());
vertices.push_back(v);
} else {
indices.push_back(it->second);
}
}
}
}
F.resize(3, indices.size()/3);
memcpy(F.data(), indices.data(), sizeof(uint32_t)*indices.size());
V.resize(3, vertices.size());
for (uint32_t i=0; i<vertices.size(); ++i)
V.col(i) = positions.at(vertices[i].p-1);
cout << "done. (V=" << V.cols() << ", F=" << F.cols() << ", took "
<< timeString(timer.value()) << ")" << endl;
}
void load_pointcloud(const std::string &filename, MatrixXf &V, MatrixXf &N,
const ProgressCallback &progress) {
std::ifstream is(filename);
if (is.fail())
throw std::runtime_error("Unable to open ALN file \"" + filename + "\"!");
cout.flush();
Timer<> timer;
std::istringstream line;
auto fetch_line = [&]() {
std::string line_str;
do {
std::getline(is, line_str);
if (is.eof())
throw std::runtime_error("Parser error while processing ALN file!");
} while (line_str.empty() || line_str[0] == '#');
line.clear();
line.str(std::move(line_str));
};
auto fetch_string = [&](std::string &value) {
while (!(line >> value)) {
if (line.eof())
fetch_line();
else
throw std::runtime_error("Parser error while processing ALN file!");
}
};
auto fetch_uint = [&](uint32_t &value) {
while (!(line >> value)) {
if (line.eof())
fetch_line();
else
throw std::runtime_error("Parser error while processing ALN file!");
}
};
auto fetch_float = [&](Float &value) {
while (!(line >> value)) {
if (line.eof())
fetch_line();
else
throw std::runtime_error("Parser error while processing ALN file!");
}
};
uint32_t nFiles;
fetch_uint(nFiles);
#if defined(_WIN32)
char path_drive[_MAX_DRIVE];
char path_dir[_MAX_DIR];
char path_fname[_MAX_FNAME];
char path_ext[_MAX_EXT];
_splitpath(filename.c_str(), path_drive, path_dir, path_fname, path_ext);
#else
char *path_dir = dirname((char *) filename.c_str());
#endif
for (uint32_t i=0; i<nFiles; ++i) {
std::string filename_sub;
fetch_string(filename_sub);
MatrixXu F_sub;
MatrixXf V_sub, N_sub;
load_ply(std::string(path_dir) + "/" + filename_sub, F_sub, V_sub, N_sub, true);
Eigen::Matrix<Float, 4, 4> M;
for (uint32_t k=0; k<16; ++k)
fetch_float(M.data()[k]);
M.transposeInPlace();
for (uint32_t k=0; k<V_sub.cols(); ++k) {
Vector4f p;
p << V_sub.col(k), 1.0f;
p = (M*p).eval();
p /= p.w();
V_sub.col(k) = p.head<3>();
}
if (N_sub.cols() == 0)
generate_smooth_normals(F_sub, V_sub, N_sub, true);
uint32_t base = (uint32_t) V.cols();
V.conservativeResize(3, base + V_sub.cols());
V.block(0, base, 3, V_sub.cols()) = V_sub;
N.conservativeResize(3, base + N_sub.cols());
N.block(0, base, 3, N_sub.cols()) = N_sub;
if (progress)
progress("Loading point cloud", i / (Float) (nFiles-1));
}
cout << "Point cloud loading finished. (V=" << V.cols() << ", took "
<< timeString(timer.value()) << ")" << endl;
}
void write_obj(const std::string &filename, const MatrixXu &F,
const MatrixXf &V, const MatrixXf &N, const MatrixXf &Nf,
const MatrixXf &UV, const MatrixXf &C,
const ProgressCallback &progress) {
Timer<> timer;
cout << "Writing \"" << filename << "\" (V=" << V.cols()
<< ", F=" << F.cols() << ") .. ";
cout.flush();
std::ofstream os(filename);
if (os.fail())
throw std::runtime_error("Unable to open OBJ file \"" + filename + "\"!");
if (N.size() > 0 && Nf.size() > 0)
throw std::runtime_error("Please specify either face or vertex normals but not both!");
for (uint32_t i=0; i<V.cols(); ++i)
os << "v " << V(0, i) << " " << V(1, i) << " " << V(2, i) << endl;
for (uint32_t i=0; i<N.cols(); ++i)
os << "vn " << N(0, i) << " " << N(1, i) << " " << N(2, i) << endl;
for (uint32_t i=0; i<Nf.cols(); ++i)
os << "vn " << Nf(0, i) << " " << Nf(1, i) << " " << Nf(2, i) << endl;
for (uint32_t i=0; i<UV.cols(); ++i)
os << "vt " << UV(0, i) << " " << UV(1, i) << endl;
/* Check for irregular faces */
std::map<uint32_t, std::pair<uint32_t, std::map<uint32_t, uint32_t>>> irregular;
size_t nIrregular = 0;
for (uint32_t f=0; f<F.cols(); ++f) {
if (F.rows() == 4) {
if (F(2, f) == F(3, f)) {
nIrregular++;
auto &value = irregular[F(2, f)];
value.first = f;
value.second[F(0, f)] = F(1, f);
continue;
}
}
os << "f ";
for (uint32_t j=0; j<F.rows(); ++j) {
uint32_t idx = F(j, f);
idx += 1;
os << idx;
if (Nf.size() > 0)
idx = f + 1;
os << "//" << idx << " ";
}
os << endl;
}
for (auto item : irregular) {
auto face = item.second;
uint32_t v = face.second.begin()->first, first = v, i = 0;
os << "f ";
while (true) {
uint32_t idx = v + 1;
os << idx;
if (Nf.size() > 0)
idx = face.first + 1;
os << "//" << idx << " ";
v = face.second[v];
if (v == first || ++i == face.second.size())
break;
}
os << endl;
}
cout << "done. (";
if (irregular.size() > 0)
cout << irregular.size() << " irregular faces, ";
cout << "took " << timeString(timer.value()) << ")" << endl;
}

View File

@@ -0,0 +1,56 @@
/*
meshio.h: Mesh file input/output routines
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#pragma once
#include "common.h"
extern void
load_mesh_or_pointcloud(const std::string &filename, MatrixXu &F,
MatrixXf &V, MatrixXf &N,
const ProgressCallback &progress = ProgressCallback());
extern void load_obj(const std::string &filename, MatrixXu &F, MatrixXf &V,
const ProgressCallback &progress = ProgressCallback());
extern void load_ply(const std::string &filename, MatrixXu &F, MatrixXf &V,
MatrixXf &N, bool pointcloud = false,
const ProgressCallback &progress = ProgressCallback());
extern void
load_pointcloud(const std::string &filename, MatrixXf &V, MatrixXf &N,
const ProgressCallback &progress = ProgressCallback());
extern void write_mesh(const std::string &filename, const MatrixXu &F,
const MatrixXf &V,
const MatrixXf &N = MatrixXf(),
const MatrixXf &Nf = MatrixXf(),
const MatrixXf &UV = MatrixXf(),
const MatrixXf &C = MatrixXf(),
const ProgressCallback &progress = ProgressCallback());
extern void write_obj(const std::string &filename, const MatrixXu &F,
const MatrixXf &V,
const MatrixXf &N = MatrixXf(),
const MatrixXf &Nf = MatrixXf(),
const MatrixXf &UV = MatrixXf(),
const MatrixXf &C = MatrixXf(),
const ProgressCallback &progress = ProgressCallback());
extern void write_ply(const std::string &filename, const MatrixXu &F,
const MatrixXf &V,
const MatrixXf &N = MatrixXf(),
const MatrixXf &Nf = MatrixXf(),
const MatrixXf &UV = MatrixXf(),
const MatrixXf &C = MatrixXf(),
const ProgressCallback &progress = ProgressCallback());

View File

@@ -0,0 +1,151 @@
/*
meshstats.cpp: Routines to efficiently compute various mesh statistics such
as the bounding box, surface area, etc.
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#include "meshstats.h"
#include "dedge.h"
MeshStats compute_mesh_stats(const MatrixXu &F, const MatrixXf &V,
bool deterministic,
const ProgressCallback &progress) {
Timer<> timer;
MeshStats stats;
if (F.size() != 0) {
cout << "Computing mesh statistics .. ";
cout.flush();
auto map = [&](const tbb::blocked_range<uint32_t> &range, MeshStats stats) -> MeshStats {
for (uint32_t f = range.begin(); f != range.end(); ++f) {
Vector3f v[3] = { V.col(F(0, f)), V.col(F(1, f)), V.col(F(2, f)) };
Vector3f face_center = Vector3f::Zero();
for (int i=0; i<3; ++i) {
Float edge_length = (v[i] - v[i == 2 ? 0 : (i+1)]).norm();
stats.mAverageEdgeLength += edge_length;
stats.mMaximumEdgeLength = std::max(stats.mMaximumEdgeLength, (double) edge_length);
stats.mAABB.expandBy(v[i]);
face_center += v[i];
}
face_center *= 1.0f / 3.0f;
Float face_area = 0.5f * (v[1]-v[0]).cross(v[2]-v[0]).norm();
stats.mSurfaceArea += face_area;
stats.mWeightedCenter += face_area * face_center;
}
SHOW_PROGRESS_RANGE(range, F.cols(), "Computing mesh statistics");
return stats;
};
auto reduce = [](MeshStats s0, MeshStats s1) -> MeshStats {
MeshStats result;
result.mSurfaceArea = s0.mSurfaceArea + s1.mSurfaceArea;
result.mWeightedCenter = s0.mWeightedCenter + s1.mWeightedCenter;
result.mAverageEdgeLength =
s0.mAverageEdgeLength + s1.mAverageEdgeLength;
result.mMaximumEdgeLength =
std::max(s0.mMaximumEdgeLength, s1.mMaximumEdgeLength);
result.mAABB = AABB::merge(s0.mAABB, s1.mAABB);
return result;
};
tbb::blocked_range<uint32_t> range(0u, (uint32_t) F.cols(), GRAIN_SIZE);
if (deterministic)
stats = tbb::parallel_deterministic_reduce(range, MeshStats(), map, reduce);
else
stats = tbb::parallel_reduce(range, MeshStats(), map, reduce);
stats.mAverageEdgeLength /= F.cols() * 3;
stats.mWeightedCenter /= stats.mSurfaceArea;
} else {
cout << "Computing point cloud statistics .. ";
cout.flush();
auto map = [&](const tbb::blocked_range<uint32_t> &range, MeshStats stats) -> MeshStats {
for (uint32_t i = range.begin(); i != range.end(); ++i) {
const Vector3f &v = V.col(i);
stats.mAABB.expandBy(v);
stats.mWeightedCenter += v;
}
SHOW_PROGRESS_RANGE(range, V.cols(), "Computing point cloud statistics");
return stats;
};
auto reduce = [](MeshStats s0, MeshStats s1) -> MeshStats {
MeshStats result;
result.mWeightedCenter = s0.mWeightedCenter + s1.mWeightedCenter;
result.mAABB = AABB::merge(s0.mAABB, s1.mAABB);
return result;
};
tbb::blocked_range<uint32_t> range(0u, (uint32_t) V.cols(), GRAIN_SIZE);
if (deterministic)
stats = tbb::parallel_deterministic_reduce(range, MeshStats(), map, reduce);
else
stats = tbb::parallel_reduce(range, MeshStats(), map, reduce);
stats.mSurfaceArea = stats.mAverageEdgeLength = stats.mMaximumEdgeLength = 0;
stats.mWeightedCenter /= V.cols();
}
cout << "done. (took " << timeString(timer.value()) << ")" << endl;
return stats;
}
void compute_dual_vertex_areas(const MatrixXu &F, const MatrixXf &V,
const VectorXu &V2E, const VectorXu &E2E,
const VectorXb &nonManifold, VectorXf &A,
const ProgressCallback &progress) {
A.resize(V.cols());
A.setZero();
cout << "Computing dual vertex areas .. ";
cout.flush();
Timer<> timer;
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t) V.cols(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t i = range.begin(); i != range.end(); ++i) {
uint32_t edge = V2E[i], stop = edge;
if (nonManifold[i] || edge == INVALID)
continue;
Float vertex_area = 0;
do {
uint32_t ep = dedge_prev_3(edge), en = dedge_next_3(edge);
Vector3f v = V.col(F(edge%3, edge/3));
Vector3f vn = V.col(F(en%3, en/3));
Vector3f vp = V.col(F(ep%3, ep/3));
Vector3f face_center = (v + vp + vn) * (1.0f / 3.0f);
Vector3f prev = (v+vp) * 0.5f;
Vector3f next = (v+vn) * 0.5f;
vertex_area +=
0.5f * ((v - prev).cross(v - face_center).norm() +
(v - next).cross(v - face_center).norm());
uint32_t opp = E2E[edge];
if (opp == INVALID)
break;
edge = dedge_next_3(opp);
} while (edge != stop);
A[i] = vertex_area;
}
SHOW_PROGRESS_RANGE(range, V.cols(), "Computing dual vertex areas");
}
);
cout << "done. (took " << timeString(timer.value()) << ")" << endl;
}

View File

@@ -0,0 +1,41 @@
/*
meshstats.h: Routines to efficiently compute various mesh statistics such
as the bounding box, surface area, etc.
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#pragma once
#include "aabb.h"
struct MeshStats {
AABB mAABB;
Vector3f mWeightedCenter;
double mAverageEdgeLength;
double mMaximumEdgeLength;
double mSurfaceArea;
MeshStats() :
mWeightedCenter(Vector3f::Zero()),
mAverageEdgeLength(0.0f),
mMaximumEdgeLength(0.0f),
mSurfaceArea(0.0f) { }
};
extern MeshStats
compute_mesh_stats(const MatrixXu &F, const MatrixXf &V,
bool deterministic = false,
const ProgressCallback &progress = ProgressCallback());
void compute_dual_vertex_areas(
const MatrixXu &F, const MatrixXf &V, const VectorXu &V2E,
const VectorXu &E2E, const VectorXb &nonManifold, VectorXf &A,
const ProgressCallback &progress = ProgressCallback());

View File

@@ -0,0 +1,443 @@
/*
normal.cpp: Helper routines for computing vertex normals
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#include "normal.h"
#include "dedge.h"
extern void
generate_smooth_normals(const MatrixXu &F, const MatrixXf &V, MatrixXf &N,
bool deterministic,
const ProgressCallback &progress) {
cout << "Computing vertex normals .. ";
cout.flush();
std::atomic<uint32_t> badFaces(0);
Timer<> timer;
N.resize(V.rows(), V.cols());
N.setZero();
auto map = [&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t f = range.begin(); f != range.end(); ++f) {
Vector3f fn = Vector3f::Zero();
for (int i=0; i<3; ++i) {
Vector3f v0 = V.col(F(i, f)),
v1 = V.col(F((i+1)%3, f)),
v2 = V.col(F((i+2)%3, f)),
d0 = v1-v0,
d1 = v2-v0;
if (i == 0) {
fn = d0.cross(d1);
Float norm = fn.norm();
if (norm < RCPOVERFLOW) {
badFaces++; /* degenerate */
break;
}
fn /= norm;
}
/* "Computing Vertex Normals from Polygonal Facets"
by Grit Thuermer and Charles A. Wuethrich, JGT 1998, Vol 3 */
Float angle = fast_acos(d0.dot(d1) / std::sqrt(d0.squaredNorm() * d1.squaredNorm()));
for (uint32_t k=0; k<3; ++k)
atomicAdd(&N.coeffRef(k, F(i, f)), fn[k]*angle);
}
}
SHOW_PROGRESS_RANGE(range, F.cols(), "Computing vertex normals (1/2)");
};
tbb::blocked_range<uint32_t> range(0u, (uint32_t) F.cols(), GRAIN_SIZE);
if (!deterministic)
tbb::parallel_for(range, map);
else
map(range);
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t) V.cols(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t i = range.begin(); i != range.end(); ++i) {
Float norm = N.col(i).norm();
if (norm < RCPOVERFLOW) {
N.col(i) = Vector3f::UnitX();
} else {
N.col(i) /= norm;
}
}
SHOW_PROGRESS_RANGE(range, V.cols(), "Computing vertex normals (2/2)");
}
);
cout << "done. (";
if (badFaces > 0)
cout << badFaces << " degenerate faces, ";
cout << "took " << timeString(timer.value()) << ")" << endl;
}
void generate_smooth_normals(const MatrixXu &F, const MatrixXf &V, const VectorXu &V2E,
const VectorXu &E2E, const VectorXb &nonManifold,
MatrixXf &N, const ProgressCallback &progress) {
cout << "Computing vertex normals .. ";
cout.flush();
std::atomic<uint32_t> badFaces(0);
Timer<> timer;
/* Compute face normals */
MatrixXf Nf(3, F.cols());
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t) F.cols(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t f = range.begin(); f != range.end(); ++f) {
Vector3f v0 = V.col(F(0, f)),
v1 = V.col(F(1, f)),
v2 = V.col(F(2, f)),
n = (v1-v0).cross(v2-v0);
Float norm = n.norm();
if (norm < RCPOVERFLOW) {
badFaces++; /* degenerate */
n = Vector3f::UnitX();
} else {
n /= norm;
}
Nf.col(f) = n;
}
SHOW_PROGRESS_RANGE(range, F.cols(), "Computing vertex normals (1/2)");
}
);
N.resize(3, V.cols());
/* Finally, compute the normals */
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t) V.cols(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t i = range.begin(); i != range.end(); ++i) {
uint32_t edge = V2E[i];
if (nonManifold[i] || edge == INVALID) {
N.col(i) = Vector3f::UnitX();
continue;
}
uint32_t stop = edge;
Vector3f normal = Vector3f::Zero();
do {
uint32_t idx = edge % 3;
Vector3f d0 = V.col(F((idx+1)%3, edge/3)) - V.col(i);
Vector3f d1 = V.col(F((idx+2)%3, edge/3)) - V.col(i);
Float angle = fast_acos(d0.dot(d1) / std::sqrt(d0.squaredNorm() * d1.squaredNorm()));
/* "Computing Vertex Normals from Polygonal Facets"
by Grit Thuermer and Charles A. Wuethrich, JGT 1998, Vol 3 */
if (std::isfinite(angle))
normal += Nf.col(edge/3) * angle;
uint32_t opp = E2E[edge];
if (opp == INVALID)
break;
edge = dedge_next_3(opp);
} while (edge != stop);
Float norm = normal.norm();
N.col(i) = norm > RCPOVERFLOW ? Vector3f(normal / norm)
: Vector3f::UnitX();
}
SHOW_PROGRESS_RANGE(range, V.cols(), "Computing vertex normals (2/2)");
}
);
cout << "done. (";
if (badFaces > 0)
cout << badFaces << " degenerate faces, ";
cout << "took " << timeString(timer.value()) << ")" << endl;
}
void generate_crease_normals(MatrixXu &F, MatrixXf &V, const VectorXu &_V2E,
const VectorXu &E2E, const VectorXb boundary,
const VectorXb &nonManifold, Float angleThreshold,
MatrixXf &N, std::map<uint32_t, uint32_t> &creases,
const ProgressCallback &progress) {
const Float dpThreshold = std::cos(angleThreshold * M_PI / 180);
cout << "Computing vertex & crease normals .. ";
cout.flush();
creases.clear();
std::atomic<uint32_t> badFaces(0), creaseVert(0), offset(V.cols());
Timer<> timer;
VectorXu V2E(_V2E);
/* Compute face normals */
MatrixXf Nf(3, F.cols());
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t) F.cols(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t f = range.begin(); f != range.end(); ++f) {
Vector3f v0 = V.col(F(0, f)),
v1 = V.col(F(1, f)),
v2 = V.col(F(2, f)),
n = (v1-v0).cross(v2-v0);
Float norm = n.norm();
if (norm < RCPOVERFLOW) {
badFaces++; /* degenerate */
n = Vector3f::UnitX();
} else {
n /= norm;
}
Nf.col(f) = n;
}
SHOW_PROGRESS_RANGE(range, F.cols(), "Computing vertex & crease normals (1/3)");
}
);
/* Determine how many extra vertices are needed, and adjust
the vertex->edge pointers so that they are located just after
the first crease edge */
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t) V.cols(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t i = range.begin(); i != range.end(); ++i) {
uint32_t edge = V2E[i], stop = edge;
if (nonManifold[i] || edge == INVALID)
continue;
uint32_t creaseEdge = INVALID, nCreaseEdges = 0;
bool is_boundary = boundary[i];
do {
uint32_t opp = E2E[edge];
if (opp == INVALID)
break;
uint32_t nextEdge = dedge_next_3(opp);
if (Nf.col(edge / 3).dot(Nf.col(nextEdge / 3)) < dpThreshold) {
nCreaseEdges++;
if (creaseEdge == INVALID || creaseEdge < nextEdge)
creaseEdge = nextEdge;
}
edge = nextEdge;
} while (edge != stop);
if (creaseEdge != INVALID) {
if (!is_boundary)
V2E[i] = creaseEdge;
creaseVert += nCreaseEdges - (is_boundary ? 0 : 1);
}
}
SHOW_PROGRESS_RANGE(range, V.cols(), "Computing vertex & crease normals (2/3)");
}
);
uint32_t oldSize = V.cols();
V.conservativeResize(3, V.cols() + creaseVert);
N.resize(3, V.cols());
tbb::spin_mutex mutex;
/* Finally, compute the normals */
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, oldSize, GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t i = range.begin(); i != range.end(); ++i) {
uint32_t edge = V2E[i];
if (nonManifold[i] || edge == INVALID) {
N.col(i) = Vector3f::UnitX();
continue;
}
uint32_t stop = edge, vertexID = i;
Vector3f normal = Vector3f::Zero();
do {
uint32_t idx = edge % 3;
if (vertexID != i)
F(idx, edge/3) = vertexID;
Vector3f d0 = V.col(F((idx+1)%3, edge/3)) - V.col(i);
Vector3f d1 = V.col(F((idx+2)%3, edge/3)) - V.col(i);
Float angle = fast_acos(d0.dot(d1) / std::sqrt(d0.squaredNorm() * d1.squaredNorm()));
/* "Computing Vertex Normals from Polygonal Facets"
by Grit Thuermer and Charles A. Wuethrich, JGT 1998, Vol 3 */
if (std::isfinite(angle))
normal += Nf.col(edge/3) * angle;
uint32_t opp = E2E[edge];
if (opp == INVALID) {
Float norm = normal.norm();
N.col(vertexID) = norm > RCPOVERFLOW ? Vector3f(normal / norm)
: Vector3f::UnitX();
break;
}
uint32_t nextEdge = dedge_next_3(opp);
if (Nf.col(edge / 3).dot(Nf.col(nextEdge / 3)) < dpThreshold ||
nextEdge == stop) {
Float norm = normal.norm();
N.col(vertexID) = norm > RCPOVERFLOW ? Vector3f(normal / norm)
: Vector3f::UnitX();
normal = Vector3f::Zero();
if (nextEdge != stop) {
vertexID = offset++;
V.col(vertexID) = V.col(i);
mutex.lock();
creases[vertexID] = i;
mutex.unlock();
}
}
edge = nextEdge;
} while (edge != stop);
}
SHOW_PROGRESS_RANGE(range, oldSize, "Computing vertex & crease normals (3/3)");
}
);
if (offset != (uint32_t) V.cols())
throw std::runtime_error("Internal error (incorrect final vertex count)!");
cout << "done. (";
if (badFaces > 0)
cout << badFaces << " degenerate faces, ";
if (creaseVert > 0)
cout << creaseVert << " crease vertices, ";
cout << "took " << timeString(timer.value()) << ")" << endl;
}
void generate_crease_normals(const MatrixXu &F, const MatrixXf &V,
const VectorXu &_V2E, const VectorXu &E2E,
const VectorXb boundary,
const VectorXb &nonManifold, Float angleThreshold,
MatrixXf &N, std::set<uint32_t> &creases,
const ProgressCallback &progress) {
const Float dpThreshold = std::cos(angleThreshold * M_PI / 180);
cout << "Computing vertex & crease normals .. ";
cout.flush();
creases.clear();
Timer<> timer;
VectorXu V2E(_V2E);
/* Compute face normals */
MatrixXf Nf(3, F.cols());
std::atomic<uint32_t> badFaces(0);
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t) F.cols(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t f = range.begin(); f != range.end(); ++f) {
Vector3f v0 = V.col(F(0, f)),
v1 = V.col(F(1, f)),
v2 = V.col(F(2, f)),
n = (v1-v0).cross(v2-v0);
Float norm = n.norm();
if (norm < RCPOVERFLOW) {
badFaces++; /* degenerate */
n = Vector3f::UnitX();
} else {
n /= norm;
}
Nf.col(f) = n;
}
SHOW_PROGRESS_RANGE(range, F.cols(), "Computing vertex & crease normals (1/3)");
}
);
/* Determine how many extra vertices are needed, and adjust
the vertex->edge pointers so that they are located just after
the first crease edge */
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t) V.cols(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t i = range.begin(); i != range.end(); ++i) {
uint32_t edge = V2E[i], stop = edge;
if (nonManifold[i] || edge == INVALID)
continue;
uint32_t creaseEdge = INVALID;
do {
uint32_t opp = E2E[edge];
if (opp == INVALID)
break;
uint32_t nextEdge = dedge_next_3(opp);
if (Nf.col(edge / 3).dot(Nf.col(nextEdge / 3)) < dpThreshold) {
if (creaseEdge == INVALID || creaseEdge < nextEdge)
creaseEdge = nextEdge;
}
edge = nextEdge;
} while (edge != stop);
if (creaseEdge != INVALID) {
if (!boundary[i])
V2E[i] = creaseEdge;
}
}
SHOW_PROGRESS_RANGE(range, V.cols(), "Computing vertex & crease normals (2/3)");
}
);
N.resize(3, V.cols());
/* Finally, compute the normals */
tbb::spin_mutex mutex;
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, V.cols(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t i = range.begin(); i != range.end(); ++i) {
uint32_t edge = V2E[i];
if (nonManifold[i] || edge == INVALID) {
N.col(i) = Vector3f::UnitX();
continue;
}
uint32_t stop = edge;
Vector3f normal = Vector3f::Zero();
do {
uint32_t idx = edge % 3, face = edge/3;
Vector3f d0 = V.col(F((idx+1)%3, face)) - V.col(i);
Vector3f d1 = V.col(F((idx+2)%3, face)) - V.col(i);
Float angle = fast_acos(d0.dot(d1) / std::sqrt(d0.squaredNorm() * d1.squaredNorm()));
/* "Computing Vertex Normals from Polygonal Facets"
by Grit Thuermer and Charles A. Wuethrich, JGT 1998, Vol 3 */
if (std::isfinite(angle))
normal += Nf.col(edge/3) * angle;
uint32_t opp = E2E[edge];
if (opp == INVALID)
break;
uint32_t nextEdge = dedge_next_3(opp);
if (Nf.col(edge / 3).dot(Nf.col(nextEdge / 3)) < dpThreshold) {
mutex.lock();
creases.insert(i);
mutex.unlock();
break;
}
edge = nextEdge;
} while (edge != stop);
Float norm = normal.norm();
N.col(i) = norm > RCPOVERFLOW ? Vector3f(normal / norm)
: Vector3f::UnitX();
}
SHOW_PROGRESS_RANGE(range, V.cols(), "Computing vertex & crease normals (3/3)");
}
);
cout << "done. (";
if (badFaces > 0)
cout << badFaces << " degenerate faces, ";
if (!creases.empty())
cout << creases.size() << " crease vertices, ";
cout << "took " << timeString(timer.value()) << ")" << endl;
}

View File

@@ -0,0 +1,43 @@
/*
normal.h: Helper routines for computing vertex normals
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#pragma once
#include "common.h"
#include <map>
#include <set>
extern void
generate_smooth_normals(const MatrixXu &F, const MatrixXf &V, MatrixXf &N,
bool deterministic,
const ProgressCallback &progress = ProgressCallback());
extern void
generate_smooth_normals(const MatrixXu &F, const MatrixXf &V,
const VectorXu &V2E, const VectorXu &E2E,
const VectorXb &nonManifold, MatrixXf &N,
const ProgressCallback &progress = ProgressCallback());
extern void
generate_crease_normals(MatrixXu &F, MatrixXf &V, const VectorXu &V2E,
const VectorXu &E2E, const VectorXb boundary,
const VectorXb &nonManifold, Float angleThreshold,
MatrixXf &N, std::map<uint32_t, uint32_t> &creases,
const ProgressCallback &progress = ProgressCallback());
extern void
generate_crease_normals(
const MatrixXu &F, const MatrixXf &V, const VectorXu &V2E, const VectorXu &E2E,
const VectorXb boundary, const VectorXb &nonManifold, Float angleThreshold,
MatrixXf &N, std::set<uint32_t> &creases,
const ProgressCallback &progress = ProgressCallback());

View File

@@ -0,0 +1,123 @@
/*
reorder.cpp: Reorder mesh face/vertex indices to improve coherence in various
applications
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#include "reorder.h"
#include "dedge.h"
#include <unordered_set>
#include <unordered_map>
#include <queue>
void reorder_mesh(MatrixXu &F, std::vector<MatrixXf> &V_vec, std::vector<MatrixXf> &F_vec,
const ProgressCallback &progress) {
/* Build a directed edge data structure */
VectorXu V2E, E2E;
VectorXb boundary, nonManifold;
build_dedge(F, V_vec[0], V2E, E2E, boundary, nonManifold, progress, true);
std::unordered_set<uint32_t> unprocessed(F.cols());
for (uint32_t i=0; i<F.cols(); ++i)
unprocessed.insert(i);
std::queue<uint32_t> queue;
std::unordered_map<uint32_t, uint32_t> v_map(V_vec[0].cols());
uint32_t nF = 0, nV = 0;
MatrixXu Fp(F.rows(), F.cols());
std::vector<MatrixXf> V_new(V_vec.size()), F_new(F_vec.size());
for (uint32_t i=0; i<V_vec.size(); ++i) {
if (V_vec[i].cols() != V_vec[0].cols())
throw std::runtime_error("reorder_mesh: inconsistent input!");
V_new[i].resize(V_vec[i].rows(), V_vec[i].cols());
}
for (uint32_t i=0; i<F_vec.size(); ++i) {
if (F_vec[i].cols() != F.cols())
throw std::runtime_error("reorder_mesh: inconsistent input!");
F_new[i].resize(F_vec[i].rows(), F_vec[i].cols());
}
while (!unprocessed.empty()) {
if (queue.empty()) {
auto it = unprocessed.begin();
queue.push(*it);
unprocessed.erase(it);
}
while (!queue.empty()) {
uint32_t f_old = queue.front();
uint32_t f_new = nF++;
queue.pop();
for (uint32_t j=0; j<F_vec.size(); ++j)
F_new[j].col(f_new) = F_vec[j].col(f_old);
for (uint32_t i=0; i<F.rows(); ++i) {
uint32_t v_old = F(i, f_old);
uint32_t v_new;
auto it_v = v_map.find(v_old);
if (it_v != v_map.end()) {
v_new = it_v->second;
} else {
v_new = v_map[v_old] = nV++;
for (uint32_t j=0; j<V_vec.size(); ++j)
V_new[j].col(v_new) = V_vec[j].col(v_old);
}
Fp(i, f_new) = v_new;
uint32_t edge_other = E2E[f_old * F.rows() + i];
if (edge_other != INVALID) {
uint32_t f_neighbor = edge_other / F.rows();
auto it_f = unprocessed.find(f_neighbor);
if (it_f != unprocessed.end()) {
queue.push(f_neighbor);
unprocessed.erase(f_neighbor);
}
}
}
if (progress && unprocessed.size() % 10000 == 0)
progress("Reordering mesh indices", 1-unprocessed.size() / (Float) F.cols());
}
}
F = std::move(Fp);
for (uint32_t i=0; i<V_vec.size(); ++i) {
V_vec[i] = std::move(V_new[i]);
V_vec[i].conservativeResize(V_vec[i].rows(), nV);
}
for (uint32_t i=0; i<F_vec.size(); ++i) {
F_vec[i] = std::move(F_new[i]);
F_vec[i].conservativeResize(F_vec[i].rows(), nF);
}
}
void replicate_vertices(MatrixXu &F, std::vector<MatrixXf> &V) {
std::vector<MatrixXf> R(V.size());
for (uint32_t i=0; i<V.size(); ++i)
R[i].resize(V[i].rows(), F.size());
for (uint32_t i=0; i<F.size(); ++i) {
uint32_t &idx = F.data()[i];
for (uint32_t j=0; j<V.size(); ++j)
R[j].col(i) = V[j].col(idx);
idx = i;
}
V = std::move(R);
}

View File

@@ -0,0 +1,22 @@
/*
reorder.h: Reorder mesh face/vertex indices to improve coherence in various
applications
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#pragma once
#include "common.h"
extern void reorder_mesh(MatrixXu &F, std::vector<MatrixXf> &V_vec, std::vector<MatrixXf> &F_vec,
const ProgressCallback &progress = ProgressCallback());
extern void replicate_vertices(MatrixXu &F, std::vector<MatrixXf> &V);

View File

@@ -0,0 +1,466 @@
/*
serializer.cpp: Helper class to serialize the application state to a .PLY file
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#include "serializer.h"
#include <set>
extern "C" {
#include "rply.h"
}
Serializer::Serializer() : mCompatibilityMode(false) { mPrefixStack.push(""); }
bool Serializer::isSerializedFile(const std::string &filename) {
auto message_cb = [](p_ply ply, const char *msg) { /* ignore */};
p_ply ply = ply_open(filename.c_str(), message_cb, 0, nullptr);
if (!ply)
return false;
if (!ply_read_header(ply)) {
ply_close(ply);
return false;
}
bool is_serialized = false;
const char *comment = nullptr;
while ((comment = ply_get_next_comment(ply, comment))) {
if (strcmp(comment, "Instant Meshes Application State") == 0)
is_serialized = true;
}
ply_close(ply);
return is_serialized;
}
std::vector<std::string> Serializer::getKeys() const {
const std::string &prefix = mPrefixStack.top();
std::vector<std::string> result;
for (auto const &kv : mData) {
if (kv.first.substr(0, prefix.length()) == prefix)
result.push_back(kv.first.substr(prefix.length()));
}
return result;
}
struct CallbackState {
const ProgressCallback &progress;
void *data;
std::string name;
size_t offset, total = 0;
bool visited = false;
CallbackState(const ProgressCallback &progress, void *data,
const std::string &name, size_t offset)
: progress(progress), data(data), name(name), offset(offset) {}
};
Serializer::Serializer(const std::string &filename, bool compatibilityMode, const ProgressCallback &progress)
: mCompatibilityMode(compatibilityMode) {
auto message_cb = [](p_ply ply, const char *msg) { cerr << "rply: " << msg << endl; };
mPrefixStack.push("");
Timer<> timer;
cout << "Unserializing application state from \"" << filename << "\" .. ";
cout.flush();
p_ply ply = ply_open(filename.c_str(), message_cb, 0, nullptr);
if (!ply)
throw std::runtime_error("Serializer: unable to open PLY file \"" + filename + "\"!");
if (!ply_read_header(ply)) {
ply_close(ply);
throw std::runtime_error("Serializer: unable to open PLY header of \"" + filename + "\"!");
}
p_ply_element element = nullptr;
#define IMPLEMENT(type) \
case Variant::Matrix_##type: \
const_cast<Eigen::Matrix<type, Eigen::Dynamic, Eigen::Dynamic>&>(*data->matrix_##type)(coord, index) = (type) ply_get_argument_value(argument); \
break; \
\
case Variant::List_##type: {\
ply_get_argument_property(argument, nullptr, &length, &value_index); \
std::vector<std::vector<type>> &vec = \
const_cast<std::vector<std::vector<type>>&>(*data->list_##type); \
if (value_index >= 0) \
vec[index][value_index] = (type) ply_get_argument_value(argument); \
else \
vec[index].resize(length); \
} \
break;
auto rply_element_cb = [](p_ply_argument argument) -> int {
CallbackState *state; long index, coord, length, value_index;
ply_get_argument_user_data(argument, (void **) &state, &coord);
Variant *data = (Variant *) state->data;
ply_get_argument_element(argument, nullptr, &index);
switch (data->type_id) {
IMPLEMENT(uint8_t);
IMPLEMENT(uint16_t);
IMPLEMENT(uint32_t);
IMPLEMENT(float);
IMPLEMENT(double);
default:
throw std::runtime_error("Unexpected data type while unserializing! (1)");
}
if (state->progress && !state->visited) {
state->progress("Loading field \"" + state->name + "\"", state->offset / (float) state->total);
state->visited = true;
}
return 1;
};
#undef IMPLEMENT
std::vector<CallbackState *> callbackStates;
try {
/* Inspect the structure of the PLY file */
size_t totalSize = 0;
while ((element = ply_get_next_element(ply, element)) != nullptr) {
const char *element_name;
long cols = 0, rows = 0;
ply_get_element_info(element, &element_name, &cols);
e_ply_type type = PLY_UINT8;
bool fail = false, list = false;
p_ply_property property = nullptr;
while ((property = ply_get_next_property(element, property)) != nullptr) {
e_ply_type property_type, length_type, list_type;
const char *property_name;
ply_get_property_info(property, &property_name, &property_type,
&length_type, &list_type);
if (property_type == PLY_LIST) {
property_type = list_type;
list = true;
}
if (rows == 0)
type = property_type;
else if (type != property_type)
fail = true;
if (cols == 0)
continue;
Variant &variant = mData[element_name];
CallbackState *state = new CallbackState(progress, &variant, element_name, totalSize);
callbackStates.push_back(state);
if (!ply_set_read_cb(ply, element_name, property_name, rply_element_cb, state, rows++)) {
ply_close(ply);
throw std::runtime_error(
"Serializer: could not register read callback for " + std::string(element_name) +
"." + std::string(property_name) + " in PLY file \"" + filename + "\"!");
}
}
if (rows == 0 && cols == 0)
rows = 1;
if (fail)
throw std::runtime_error("Serializer: unsupported data format in \"" + filename + "\"!");
Variant &variant = mData[std::string(element_name)];
totalSize += cols;
#define IMPLEMENT(ply_type, type) \
case ply_type: \
if (list) { \
auto l = new std::vector<std::vector<type>>(); \
variant.type_id = Variant::Type::List_##type; \
variant.list_##type = l; \
l->resize(cols); \
} else { \
auto mat = new Eigen::Matrix<type, Eigen::Dynamic, Eigen::Dynamic>(rows, cols); \
variant.type_id = Variant::Type::Matrix_##type; \
variant.matrix_##type = mat; \
} \
break;
switch (type) {
IMPLEMENT(PLY_UINT8, uint8_t)
IMPLEMENT(PLY_UINT16, uint16_t)
IMPLEMENT(PLY_UINT32, uint32_t)
IMPLEMENT(PLY_FLOAT32, float)
IMPLEMENT(PLY_FLOAT64, double)
default:
throw std::runtime_error("Unexpected data type while unserializing! (2)");
}
#undef IMPLEMENT
}
for (auto c : callbackStates)
c->total = totalSize;
if (!ply_read(ply)) {
ply_close(ply);
throw std::runtime_error("Error while loading application state from \"" + filename + "\"!");
}
} catch (...) {
for (auto c : callbackStates)
delete c;
throw;
}
ply_close(ply);
for (auto c : callbackStates)
delete c;
cout << "done. (took " << timeString(timer.value()) << ")" << endl;
}
Serializer::~Serializer() {
#define IMPLEMENT(type) \
case Variant::Type::List_##type: delete kv.second.list_##type; break; \
case Variant::Type::Matrix_##type: delete kv.second.matrix_##type; break;
for (auto const &kv : mData) {
switch (kv.second.type_id) {
IMPLEMENT(uint8_t);
IMPLEMENT(uint16_t);
IMPLEMENT(uint32_t);
IMPLEMENT(float);
IMPLEMENT(double);
default: break;
}
}
#undef IMPLEMENT
}
void Serializer::write(const std::string &filename, const ProgressCallback &progress) {
auto message_cb = [](p_ply ply, const char *msg) { cerr << "rply: " << msg << endl; };
Timer<> timer;
cout << "Writing application state to \"" << filename << "\" .. ";
cout.flush();
p_ply ply = ply_create(filename.c_str(), PLY_DEFAULT, message_cb, 0, nullptr);
if (!ply)
throw std::runtime_error("Unable to write PLY file!");
ply_add_comment(ply, "Instant Meshes Application State");
for (auto const &kv : mData) {
#define IMPLEMENT(ply_type, type) \
case Variant::Type::Matrix_##type: \
ply_add_element(ply, kv.first.c_str(), kv.second.matrix_##type->cols()); \
if (kv.second.matrix_##type->rows() <= 1) \
ply_add_scalar_property(ply, "value", ply_type); \
else \
for (uint32_t i=0; i<kv.second.matrix_##type->rows(); ++i) \
ply_add_scalar_property(ply, ("value_" + std::to_string(i)).c_str(), ply_type); \
break; \
case Variant::Type::List_##type: { \
ply_add_element(ply, kv.first.c_str(), kv.second.list_##type->size()); \
size_t max_size = 0; \
for (size_t i=0; i<kv.second.list_##type->size(); ++i) \
max_size = std::max(max_size, kv.second.list_##type->operator[](i).size()); \
ply_add_list_property(ply, "value", max_size < 256 ? PLY_UINT8 : (max_size < 65536 ? PLY_UINT16 : PLY_UINT32), ply_type); \
} \
break;
switch (kv.second.type_id) {
IMPLEMENT(PLY_UINT8, uint8_t)
IMPLEMENT(PLY_UINT16, uint16_t)
IMPLEMENT(PLY_UINT32, uint32_t)
IMPLEMENT(PLY_FLOAT32, float)
IMPLEMENT(PLY_FLOAT64, double)
default:
throw std::runtime_error("Unexpected data type while serializing! (1)");
break;
}
#undef IMPLEMENT
}
ply_write_header(ply);
#define IMPLEMENT(type) \
case Variant::Type::Matrix_##type: \
for (uint32_t i=0; i<kv.second.matrix_##type->size(); ++i) \
ply_write(ply, kv.second.matrix_##type->data()[i]); \
written += kv.second.matrix_##type->size() * sizeof(type); \
break; \
case Variant::Type::List_##type: { \
const std::vector<std::vector<type>> &list = *kv.second.list_##type; \
for (uint32_t i=0; i<list.size(); ++i) { \
ply_write(ply, list[i].size()); \
for (uint32_t j=0; j<list[i].size(); ++j) \
ply_write(ply, list[i][j]); \
written += list[i].size() * sizeof(type) + sizeof(uint32_t); \
} \
} \
break;
size_t size = totalSize(), written = 0;
for (auto const &kv : mData) {
switch (kv.second.type_id) {
IMPLEMENT(uint8_t);
IMPLEMENT(uint16_t);
IMPLEMENT(uint32_t);
IMPLEMENT(float);
IMPLEMENT(double);
default:
throw std::runtime_error("Unexpected data type while serializing! (2)");
break;
}
if (progress)
progress("Writing \"" + kv.first + "\"", written / (Float) size);
}
#undef IMPLEMENT
ply_close(ply);
cout << "done. (took " << timeString(timer.value()) << ")" << endl;
}
size_t Serializer::totalSize() const {
size_t result = 0;
#define IMPLEMENT(type) \
case Variant::Type::Matrix_##type: \
result += kv.second.matrix_##type->size() * sizeof(type); \
break; \
case Variant::Type::List_##type: {\
const std::vector<std::vector<type>> &list = *kv.second.list_##type; \
for (uint32_t i=0; i<list.size(); ++i) \
result += list[i].size() * sizeof(type) + sizeof(uint32_t); \
} \
break;
for (auto const &kv : mData) {
switch (kv.second.type_id) {
IMPLEMENT(uint8_t);
IMPLEMENT(uint16_t);
IMPLEMENT(uint32_t);
IMPLEMENT(float);
IMPLEMENT(double);
}
}
#undef IMPLEMENT
return result;
}
bool Serializer::diff(const Serializer &other) const {
std::set<std::string> keys;
for (auto const &kv : mData)
keys.insert(kv.first);
for (auto const &kv : other.mData)
keys.insert(kv.first);
bool diff = false;
for (const std::string &key : keys) {
auto it1 = mData.find(key);
if (it1 == mData.end()) {
cout << "Element " << key << " does not exist in serializer 1." << endl;
diff = true;
continue;
}
auto it2 = other.mData.find(key);
if (it2 == other.mData.end()) {
cout << "Element " << key << " does not exist in serializer 2." << endl;
diff = true;
continue;
}
const Variant &v1 = it1->second;
const Variant &v2 = it2->second;
if (v1.type_id != v2.type_id) {
cout << "Element " << key << " have different types." << endl;
diff = true;
continue;
}
#define IMPLEMENT(type) \
case Variant::Type::Matrix_##type: \
if (v1.matrix_##type->cols() != v2.matrix_##type->cols() || \
v1.matrix_##type->rows() != v2.matrix_##type->rows()) \
result = true; \
else \
result = *(v1.matrix_##type) != *(v2.matrix_##type); \
break; \
case Variant::Type::List_##type: \
result = *(v1.list_##type) != *(v2.list_##type); \
break;
bool result = false;
switch (v1.type_id) {
IMPLEMENT(uint8_t);
IMPLEMENT(uint16_t);
IMPLEMENT(uint32_t);
IMPLEMENT(float);
IMPLEMENT(double);
default:
throw std::runtime_error("Unexpected data type while diffing!");
break;
}
#undef IMPLEMENT
if (result) {
cout << "Element " << key << " differs." << endl;
diff = true;
}
}
return diff;
}
std::ostream &operator<<(std::ostream &os, const Serializer &state) {
os << "Serializer[";
bool first = true;
for (auto const &kv : state.mData) {
const Serializer::Variant &v = kv.second;
if (!first)
cout << ",";
first = false;
cout << endl;
std::string tname, value;
#define IMPLEMENT(type) \
case Serializer::Variant::Type::Matrix_##type: { \
const Eigen::Matrix<type, Eigen::Dynamic, Eigen::Dynamic> &mat = *(v.matrix_##type); \
if (mat.size() == 1) { \
tname = #type; \
value = std::to_string(mat(0, 0)); \
} else if (mat.cols() == 1 && mat.rows() <= 4) { \
tname = "vec<" #type ">"; \
value = "["; \
for (int i=0; i<mat.rows(); ++i) { \
value += std::to_string(mat(i, 0)); \
if (i+1<mat.rows()) \
value += ", "; \
} \
value += "]"; \
} else { \
tname = std::string(mat.cols() == 1 ? "vec<" : "mat<") + #type + std::string(">"); \
value = "data[" + std::to_string(mat.rows()) + "x" + \
std::to_string(mat.cols()) + "]"; \
} \
} \
break; \
case Serializer::Variant::Type::List_##type: \
tname = #type "**"; \
value = "data[" + std::to_string(v.list_##type->size()) + "][]"; \
break; \
switch (v.type_id) {
IMPLEMENT(uint8_t);
IMPLEMENT(uint16_t);
IMPLEMENT(uint32_t);
IMPLEMENT(float);
IMPLEMENT(double);
default:
throw std::runtime_error("Unexpected type in stream operator");
}
os << "\t" << tname << " " << kv.first << " = " << value;
}
os << endl << "]";
return os;
}

View File

@@ -0,0 +1,324 @@
/*
serializer.h: Helper class to serialize the application state to a .PLY file
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#pragma once
#include "common.h"
#include <map>
#include <set>
#include <stack>
class Serializer {
public:
Serializer();
Serializer(const std::string &filename, bool compatibilityMode = false,
const ProgressCallback &progress = ProgressCallback());
~Serializer();
void write(const std::string &filename,
const ProgressCallback &progress = ProgressCallback());
static bool isSerializedFile(const std::string &filename);
void pushPrefix(const std::string &prefix) const { mPrefixStack.push(mPrefixStack.top() + prefix + "."); }
void popPrefix() const { mPrefixStack.pop(); }
bool diff(const Serializer &serializer) const;
inline size_t totalSize() const;
std::vector<std::string> getKeys() const;
#define MISSING_KEY(key) \
if (mCompatibilityMode) {\
cerr << "Warning: Serializer: could not find element \"" + mPrefixStack.top() + key + "\"!" << endl; \
return false; \
} else { \
throw std::runtime_error("Serializer: could not find element \"" + mPrefixStack.top() + key + "\"!"); \
}
#define IMPLEMENT(type, btype) \
void set(const std::string &key, type value) { \
Eigen::Matrix<btype, Eigen::Dynamic, Eigen::Dynamic> *mat = new Eigen::Matrix<btype, Eigen::Dynamic, Eigen::Dynamic>(1, 1); \
(*mat)(0, 0) = (btype) value; \
mData.emplace(mPrefixStack.top() + key, mat); \
} \
void set(const std::string &key, const Eigen::Matrix<type, 2, 1> &v) { \
mData.emplace(mPrefixStack.top() + key, \
new Eigen::Matrix<btype, Eigen::Dynamic, Eigen::Dynamic>(v.cast<btype>())); \
} \
\
void set(const std::string &key, const Eigen::Matrix<type, 3, 1> &v) { \
mData.emplace(mPrefixStack.top() + key, \
new Eigen::Matrix<btype, Eigen::Dynamic, Eigen::Dynamic>(v.cast<btype>())); \
} \
\
void set(const std::string &key, const Eigen::Matrix<type, 4, 1> &v) { \
mData.emplace(mPrefixStack.top() + key, \
new Eigen::Matrix<btype, Eigen::Dynamic, Eigen::Dynamic>(v.cast<btype>())); \
} \
\
void set(const std::string &key, const Eigen::Matrix<type, 1, Eigen::Dynamic> &v) { \
mData.emplace(mPrefixStack.top() + key, \
new Eigen::Matrix<btype, Eigen::Dynamic, Eigen::Dynamic>(v.cast<btype>())); \
} \
\
void set(const std::string &key, const Eigen::Matrix<type, Eigen::Dynamic, 1> &v) { \
mData.emplace(mPrefixStack.top() + key, \
new Eigen::Matrix<btype, Eigen::Dynamic, Eigen::Dynamic>(v.cast<btype>().transpose())); \
} \
\
void set(const std::string &key, const Eigen::Matrix<type, Eigen::Dynamic, Eigen::Dynamic> &m) { \
mData.emplace(mPrefixStack.top() + key, \
new Eigen::Matrix<btype, Eigen::Dynamic, Eigen::Dynamic>(m.cast<btype>())); \
} \
\
void set(const std::string &key, const std::vector<std::vector<type>> &v) { \
mData.emplace(mPrefixStack.top() + key, new std::vector<std::vector<btype>>( \
reinterpret_cast<const std::vector<std::vector<btype>>&>(v))); \
} \
\
bool get(const std::string &key, type &value) const { \
auto it = mData.find(mPrefixStack.top() + key); \
if (it == mData.end()) { MISSING_KEY(key); } \
if (it->second.type_id != Variant::Type::Matrix_##btype) \
throw std::runtime_error("Serializer: element \"" + mPrefixStack.top() + key + "\": type mismatch!"); \
if (it->second.matrix_##btype->size() != 1) \
throw std::runtime_error("Serializer: element \"" + mPrefixStack.top() + key + "\": size mismatch!"); \
value = (type) (*(it->second.matrix_##btype))(0, 0); \
return true; \
} \
\
bool get(const std::string &key, Eigen::Matrix<type, 2, 1> &v) const { \
auto it = mData.find(mPrefixStack.top() + key); \
if (it == mData.end()) { MISSING_KEY(key); } \
if (it->second.type_id != Variant::Type::Matrix_##btype) \
throw std::runtime_error("Serializer: element \"" + mPrefixStack.top() + key + "\": type mismatch!"); \
if (it->second.matrix_##btype->cols() != 1 && it->second.matrix_##btype->rows() != 2) \
throw std::runtime_error("Serializer: element \"" + mPrefixStack.top() + key + "\": size mismatch!"); \
v = it->second.matrix_##btype->col(0).cast<type>(); \
return true; \
} \
\
bool get(const std::string &key, Eigen::Matrix<type, 3, 1> &v) const { \
auto it = mData.find(mPrefixStack.top() + key); \
if (it == mData.end()) { MISSING_KEY(key); } \
if (it->second.type_id != Variant::Type::Matrix_##btype) \
throw std::runtime_error("Serializer: element \"" + mPrefixStack.top() + key + "\": type mismatch!"); \
if (it->second.matrix_##btype->cols() != 1 && it->second.matrix_##btype->rows() != 3) \
throw std::runtime_error("Serializer: element \"" + mPrefixStack.top() + key + "\": size mismatch!"); \
v = it->second.matrix_##btype->col(0).cast<type>(); \
return true; \
} \
\
bool get(const std::string &key, Eigen::Matrix<type, 4, 1> &v) const { \
auto it = mData.find(mPrefixStack.top() + key); \
if (it == mData.end()) { MISSING_KEY(key); } \
if (it->second.type_id != Variant::Type::Matrix_##btype) \
throw std::runtime_error("Serializer: element \"" + mPrefixStack.top() + key + "\": type mismatch!"); \
if (it->second.matrix_##btype->cols() != 1 && it->second.matrix_##btype->rows() != 4) \
throw std::runtime_error("Serializer: element \"" + mPrefixStack.top() + key + "\": size mismatch!"); \
v = it->second.matrix_##btype->col(0).cast<type>(); \
return true; \
} \
\
bool get(const std::string &key, Eigen::Matrix<type, Eigen::Dynamic, 1> &v) const { \
auto it = mData.find(mPrefixStack.top() + key); \
if (it == mData.end()) { MISSING_KEY(key); } \
if (it->second.type_id != Variant::Type::Matrix_##btype) \
throw std::runtime_error("Serializer: element \"" + mPrefixStack.top() + key + "\": type mismatch!"); \
if (it->second.matrix_##btype->rows() != 1) \
throw std::runtime_error("Serializer: element \"" + mPrefixStack.top() + key + "\": size mismatch!"); \
v = it->second.matrix_##btype->row(0).cast<type>().transpose(); \
return true; \
} \
\
bool get(const std::string &key, Eigen::Matrix<type, 1, Eigen::Dynamic> &v) const { \
auto it = mData.find(mPrefixStack.top() + key); \
if (it == mData.end()) { MISSING_KEY(key); } \
if (it->second.type_id != Variant::Type::Matrix_##btype) \
throw std::runtime_error("Serializer: element \"" + mPrefixStack.top() + key + "\": type mismatch!"); \
if (it->second.matrix_##btype->rows() != 1) \
throw std::runtime_error("Serializer: element \"" + mPrefixStack.top() + key + "\": size mismatch!"); \
v = it->second.matrix_##btype->row(0).cast<type>(); \
return true; \
} \
\
bool get(const std::string &key, Eigen::Matrix<type, Eigen::Dynamic, Eigen::Dynamic> &v) const { \
auto it = mData.find(mPrefixStack.top() + key); \
if (it == mData.end()) { MISSING_KEY(key); } \
if (it->second.type_id != Variant::Type::Matrix_##btype) \
throw std::runtime_error("Serializer: element \"" + mPrefixStack.top() + key + "\": type mismatch!"); \
v = it->second.matrix_##btype->cast<type>(); \
return true; \
} \
\
bool get(const std::string &key, std::vector<std::vector<type>> &v) const { \
auto it = mData.find(mPrefixStack.top() + key); \
if (it == mData.end()) { MISSING_KEY(key); } \
if (it->second.type_id != Variant::Type::List_##btype) \
throw std::runtime_error("Serializer: element \"" + mPrefixStack.top() + key + "\": type mismatch!"); \
v = reinterpret_cast<const std::vector<std::vector<type>>&>(*(it->second.list_##btype)); \
return true; \
}
IMPLEMENT(bool, uint8_t)
IMPLEMENT(uint8_t, uint8_t)
IMPLEMENT(uint16_t, uint16_t)
IMPLEMENT(uint32_t, uint32_t)
IMPLEMENT(int32_t, uint32_t)
IMPLEMENT(float, float)
IMPLEMENT(double, double)
#undef IMPLEMENT
#undef MISSING_KEY
template <typename Scalar, int N> inline void set(const std::string &key, const Eigen::Matrix<Eigen::Matrix<Scalar, N, 1>, 1, Eigen::Dynamic> &m) {
Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> temp(N, m.size());
for (uint32_t i=0; i<m.size(); ++i)
temp.col(i) = m[i];
set(key, temp);
}
template <typename Scalar, int N> inline bool get(const std::string &key, Eigen::Matrix<Eigen::Matrix<Scalar, N, 1>, 1, Eigen::Dynamic> &m) const {
Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> temp;
if (!get(key, temp))
return false;
if (temp.rows() != N && temp.cols() != 0)
throw std::runtime_error("Serializer: element \"" + mPrefixStack.top() + key + "\": size mismatch!");
m.resize(temp.cols());
for (uint32_t i=0; i<temp.cols(); ++i)
m[i] = temp.col(i);
return true;
}
template <typename Key, typename Value> inline void set(const std::string &key, const std::map<Key, Value> &map) {
Eigen::Matrix<Key, 1, Eigen::Dynamic> keys(map.size());
Eigen::Matrix<Value, 1, Eigen::Dynamic> values(map.size());
uint32_t ctr = 0;
for (auto const &kv : map) {
keys[ctr] = kv.first;
values[ctr++] = kv.second;
}
set(key + ".keys", keys);
set(key + ".values", values);
}
template <typename Key, typename Value> inline bool get(const std::string &key, std::map<Key, Value> &map) const {
Eigen::Matrix<Key, 1, Eigen::Dynamic> keys;
Eigen::Matrix<Value, 1, Eigen::Dynamic> values;
if (!get(key + ".keys", keys) || !get(key + ".values", values))
return false;
if (keys.size() != values.size())
throw std::runtime_error("Serializer: element \"" + mPrefixStack.top() + key + "\": type mismatch!");
map.clear();
for (uint32_t i=0; i<values.size(); ++i)
map[keys[i]] = values[i];
return true;
}
template <typename Key> inline void set(const std::string &key, const std::set<Key> &value) {
Eigen::Matrix<Key, 1, Eigen::Dynamic> keys(value.size());
uint32_t ctr = 0;
for (auto const &k : value)
keys[ctr++] = k;
set(key + ".keys", keys);
}
template <typename Key> inline bool get(const std::string &key, std::set<Key> &value) const {
Eigen::Matrix<Key, 1, Eigen::Dynamic> keys;
if (!get(key + ".keys", keys))
return false;
value.clear();
for (uint32_t i=0; i<keys.size(); ++i)
value.insert(keys[i]);
return true;
}
template <typename Scalar> inline bool get(const std::string &key, Eigen::Quaternion<Scalar> &quat) const {
Eigen::Matrix<Scalar, 4, 1> coeffs;
if (!get(key, coeffs))
return false;
quat.coeffs() << coeffs;
return true;
}
template <typename Scalar> inline void set(const std::string &key, const Eigen::Quaternion<Scalar> &quat) {
set(key, Eigen::Matrix<Scalar, 4, 1>(quat.coeffs()));
}
inline void set(const std::string &key, const std::string &str) {
Eigen::Matrix<uint8_t, 1, Eigen::Dynamic> v(str.length());
memcpy(v.data(), str.c_str(), str.length());
set(key, v);
}
inline bool get(const std::string &key, std::string &str) const {
Eigen::Matrix<uint8_t, 1, Eigen::Dynamic> v;
if (!get(key, v))
return false;
v.conservativeResize(v.size() + 1);
v[v.size()-1] = '\0';
str = (const char *) v.data();
return true;
}
friend std::ostream &operator<<(std::ostream &os, const Serializer &state);
protected:
struct Variant {
#define VARIANT_ENUM(type) \
List_##type, Matrix_##type
#define VARIANT_UNION(type) \
const Eigen::Matrix<type, Eigen::Dynamic, Eigen::Dynamic> *matrix_##type; \
const std::vector<std::vector<type>> *list_##type
#define VARIANT_CONSTR(type) \
inline Variant(const Eigen::Matrix<type, Eigen::Dynamic, Eigen::Dynamic> *value) \
: type_id(Type::Matrix_##type), matrix_##type(value) { } \
inline Variant(const std::vector<std::vector<type>> *value) \
: type_id(Type::List_##type), list_##type(value) { }
enum Type {
VARIANT_ENUM(uint8_t),
VARIANT_ENUM(uint16_t),
VARIANT_ENUM(uint32_t),
VARIANT_ENUM(float),
VARIANT_ENUM(double)
} type_id;
union {
VARIANT_UNION(uint8_t);
VARIANT_UNION(uint16_t);
VARIANT_UNION(uint32_t);
VARIANT_UNION(float);
VARIANT_UNION(double);
};
inline Variant() { }
VARIANT_CONSTR(uint8_t);
VARIANT_CONSTR(uint16_t);
VARIANT_CONSTR(uint32_t);
VARIANT_CONSTR(float);
VARIANT_CONSTR(double);
#undef VARIANT_CONSTR
#undef VARIANT_UNION
};
std::map<std::string, Variant> mData;
mutable std::stack<std::string> mPrefixStack;
bool mCompatibilityMode;
};

View File

@@ -0,0 +1,224 @@
/*
smoothcurve.cpp: Helper routines to compute smooth curves on meshes to enable
intuitive stroke annotations
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#include "smoothcurve.h"
#include "dedge.h"
#include "bvh.h"
#include <queue>
#include <map>
#include <set>
bool smooth_curve(const BVH *bvh, const VectorXu &E2E, std::vector<CurvePoint> &curve, bool watertight) {
const MatrixXu &F = *bvh->F();
const MatrixXf &V = *bvh->V(), &N = *bvh->N();
cout << endl;
std::vector<CurvePoint> curve_new;
std::vector<Float> weight;
std::vector<uint32_t> path;
cout << "Input: " << curve.size() << " vertices" << endl;
for (int it=0;; ++it) {
if (curve.size() < 2)
return false;
for (uint32_t it2=0; it2<curve.size(); ++it2) {
curve_new.clear();
curve_new.push_back(curve[0]);
for (uint32_t i=1; i<curve.size()-1; ++i) {
Vector3f p_new = 0.5f * (curve[i-1].p + curve[i+1].p);
Vector3f n_new = (curve[i-1].n + curve[i+1].n).normalized();
Float maxlength = (curve[i-1].p - curve[i+1].p).norm()*2;
Ray ray1(p_new, n_new, 0, maxlength);
Ray ray2(p_new, -n_new, 0, maxlength);
uint32_t idx1 = 0, idx2 = 0;
Float t1 = 0, t2 = 0;
Vector2f uv1, uv2;
bool hit1 = bvh->rayIntersect(ray1, idx1, t1, &uv1);
bool hit2 = bvh->rayIntersect(ray2, idx2, t2, &uv2);
if (!hit1 && !hit2)
continue;
CurvePoint pt;
if (t1 < t2) {
pt.p = ray1(t1);
pt.f = idx1;
pt.n = ((1 - uv1.sum()) * N.col(F(0, idx1)) + uv1.x() * N.col(F(1, idx1)) + uv1.y() * N.col(F(2, idx1))).normalized();
} else {
pt.p = ray2(t2);
pt.f = idx2;
pt.n = ((1 - uv2.sum()) * N.col(F(0, idx2)) + uv2.x() * N.col(F(1, idx2)) + uv2.y() * N.col(F(2, idx2))).normalized();
}
curve_new.push_back(pt);
}
curve_new.push_back(curve[curve.size()-1]);
curve.swap(curve_new);
}
if (!watertight && it == 1)
break;
curve_new.clear();
curve_new.push_back(curve[0]);
for (uint32_t i=1; i<curve.size(); ++i) {
if (!astar(F, E2E, V, curve[i-1].f, curve[i].f, path))
return false;
auto closest = [](const Vector3f &p0, const Vector3f &p1, const Vector3f &target) -> Vector3f {
Vector3f d = (p1-p0).normalized();
return p0 + d * std::min(std::max((target-p0).dot(d), 0.0f), (p0-p1).norm());
};
if (path.size() > 2) {
uint32_t base = curve_new.size() - 1;
for (uint32_t j=1; j<path.size()-1; ++j) {
uint32_t f = path[j];
Vector3f p0 = V.col(F(0, f)), p1 = V.col(F(1, f)), p2 = V.col(F(2, f));
CurvePoint pt2;
pt2.f = f;
pt2.n = (p1-p0).cross(p2-p0).normalized();
pt2.p = (p0+p1+p2) * (1.0f / 3.0f);
curve_new.push_back(pt2);
}
curve_new.push_back(curve[i]);
for (uint32_t q=1; q<path.size()-1; ++q) {
for (uint32_t j=1; j<path.size()-1; ++j) {
Float bestDist1 = std::numeric_limits<Float>::infinity();
Float bestDist2 = std::numeric_limits<Float>::infinity();
Vector3f bestPt1 = Vector3f::Zero(), bestPt2 = Vector3f::Zero();
uint32_t f = path[j];
for (uint32_t k=0; k<3; ++k) {
Vector3f closest1 = closest(V.col(F(k, f)), V.col(F((k + 1) % 3, f)), curve_new[base+j-1].p);
Vector3f closest2 = closest(V.col(F(k, f)), V.col(F((k + 1) % 3, f)), curve_new[base+j+1].p);
Float dist1 = (closest1 - curve_new[base+j-1].p).norm();
Float dist2 = (closest2 - curve_new[base+j+1].p).norm();
if (dist1 < bestDist1) { bestDist1 = dist1; bestPt1 = closest1; }
if (dist2 < bestDist2) { bestDist2 = dist2; bestPt2 = closest2; }
}
curve_new[base+j].p = (bestPt1 + bestPt2) * 0.5f;
}
}
} else {
curve_new.push_back(curve[i]);
}
}
curve.swap(curve_new);
curve_new.clear();
curve_new.push_back(curve[0]);
weight.clear();
weight.push_back(1.0f);
for (uint32_t i=0; i<curve.size(); ++i) {
auto &cur = curve_new[curve_new.size()-1];
auto &cur_weight = weight[weight.size()-1];
if (cur.f == curve[i].f) {
cur.p += curve[i].p;
cur.n += curve[i].n;
cur_weight += 1;
} else {
curve_new.push_back(curve[i]);
weight.push_back(1.f);
}
}
for (uint32_t i=0; i<curve_new.size(); ++i) {
curve_new[i].p /= weight[i];
curve_new[i].n.normalize();
}
curve_new[0] = curve[0];
curve_new[curve_new.size()-1] = curve[curve.size()-1];
if (curve_new.size() < 2 || curve_new[0].f == curve_new[curve_new.size()-1].f)
return false;
curve_new.swap(curve);
if (it > 2)
break;
}
cout << "Smoothed curve: " << curve.size() << " vertices" << endl;
return true;
}
inline bool astar(const MatrixXu &F, const VectorXu &E2E, const MatrixXf &V, uint32_t start, uint32_t end, std::vector<uint32_t> &path) {
typedef std::pair<uint32_t, Float> Entry;
struct comp {
bool operator() (const Entry & lhs, const Entry & rhs) const { return lhs.second > rhs.second; }
};
auto eucldist = [&](uint32_t i, uint32_t j) -> Float {
Vector3f diff = Vector3f::Zero();
for (uint32_t k=0; k<3; ++k)
diff += V.col(F(k, i)) - V.col(F(k, j));
return diff.norm() * (1.0f / 3.0f);
};
std::map<uint32_t, Float> g_score;
std::map<uint32_t, uint32_t> came_from;
std::set<uint32_t> closed;
std::priority_queue<Entry, std::vector<Entry>, comp> pq;
path.clear();
g_score[start] = 0;
pq.push(Entry(start, g_score[start] + eucldist(start, end)));
uint32_t iter = 0;
while (true) {
if (pq.empty()) {
/* Internal error - graph disconnected? */
return false;
}
iter++;
uint32_t current = pq.top().first;
Float currentDist = pq.top().second;
if (current == end)
break;
pq.pop();
if (currentDist != g_score[current] + eucldist(current, end))
continue;
closed.insert(current);
for (uint32_t i=0; i<3; ++i) {
uint32_t neighbor = E2E[current*3+i];
if (neighbor == INVALID)
continue;
neighbor /= 3;
if (closed.find(neighbor) != closed.end())
continue;
Float tentative_g_score = g_score[current] + eucldist(current, neighbor);
bool is_open = g_score.find(neighbor) != g_score.end();
bool closer = is_open && tentative_g_score < g_score[neighbor];
if (!is_open || closer) {
g_score[neighbor] = tentative_g_score;
came_from[neighbor] = current;
pq.push(Entry(neighbor, g_score[neighbor] + eucldist(neighbor, end)));
}
}
}
uint32_t current = end;
path.push_back(current);
while (came_from.find(path[path.size()-1]) != came_from.end()) {
current = came_from[current];
path.push_back(current);
}
std::reverse(path.begin(), path.end());
return true;
}

View File

@@ -0,0 +1,32 @@
/*
smoothcurve.h: Helper routines to compute smooth curves on meshes to enable
intuitive stroke annotations
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#pragma once
#include "common.h"
struct CurvePoint {
Vector3f p;
Vector3f n;
uint32_t f;
};
class BVH;
extern bool smooth_curve(const BVH *bvh, const VectorXu &E2E,
std::vector<CurvePoint> &curve, bool watertight = false);
extern bool astar(const MatrixXu &F, const VectorXu &E2E, const MatrixXf &V,
uint32_t start, uint32_t end, std::vector<uint32_t> &path);

View File

@@ -0,0 +1,181 @@
/*
subdivide.cpp: Subdivides edges in a triangle mesh until all edges
are below a specified maximum length
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#include "subdivide.h"
#include "dedge.h"
void subdivide(MatrixXu &F, MatrixXf &V, VectorXu &V2E, VectorXu &E2E,
VectorXb &boundary, VectorXb &nonmanifold, Float maxLength,
bool deterministic,
const ProgressCallback &progress) {
typedef std::pair<uint32_t, Float> Edge;
struct EdgeComp {
bool operator()(const Edge& u, const Edge& v) const {
return u.second < v.second;
}
};
tbb::concurrent_priority_queue<Edge, EdgeComp> queue;
maxLength *= maxLength;
cout << "Subdividing mesh .. ";
cout.flush();
Timer<> timer;
if (progress)
progress("Subdividing mesh", 0.0f);
tbb::blocked_range<uint32_t> range(0u, (uint32_t) E2E.size(), GRAIN_SIZE);
auto subdiv = [&](const tbb::blocked_range<uint32_t> &range) {
for (uint32_t i = range.begin(); i<range.end(); ++i) {
uint32_t v0 = F(i%3, i/3), v1 = F((i+1)%3, i/3);
if (nonmanifold[v0] || nonmanifold[v1])
continue;
Float length = (V.col(v0) - V.col(v1)).squaredNorm();
if (length > maxLength) {
uint32_t other = E2E[i];
if (other == INVALID || other > i)
queue.push(Edge(i, length));
}
}
SHOW_PROGRESS_RANGE(range, E2E.size(), "Subdividing mesh (1/2)");
};
if (!deterministic)
tbb::parallel_for(range, subdiv);
else
subdiv(range);
uint32_t nV = V.cols(), nF = F.cols(), nSplit = 0;
/*
/ v0 \
v1p 1 | 0 v0p
\ v1 /
/ v0 \
/ 1 | 0 \
v1p - vn - v0p
\ 2 | 3 /
\ v1 /
f0: vn, v0p, v0
f1: vn, v0, v1p
f2: vn, v1p, v1
f3: vn, v1, v0p
*/
while (!queue.empty()) {
Edge edge;
if (!queue.try_pop(edge))
return;
uint32_t e0 = edge.first, e1 = E2E[e0];
bool is_boundary = e1 == INVALID;
uint32_t f0 = e0/3, f1 = is_boundary ? INVALID : (e1 / 3);
uint32_t v0 = F(e0%3, f0), v0p = F((e0+2)%3, f0), v1 = F((e0+1)%3, f0);
if ((V.col(v0) - V.col(v1)).squaredNorm() != edge.second)
continue;
uint32_t v1p = is_boundary ? INVALID : F((e1+2)%3, f1);
uint32_t vn = nV++;
nSplit++;
/* Update V */
if (nV > V.cols()) {
V.conservativeResize(V.rows(), V.cols() * 2);
V2E.conservativeResize(V.cols());
boundary.conservativeResize(V.cols());
nonmanifold.conservativeResize(V.cols());
}
/* Update V */
V.col(vn) = (V.col(v0) + V.col(v1)) * 0.5f;
nonmanifold[vn] = false;
boundary[vn] = is_boundary;
/* Update F and E2E */
uint32_t f2 = is_boundary ? INVALID : (nF++);
uint32_t f3 = nF++;
if (nF > F.cols()) {
F.conservativeResize(F.rows(), std::max(nF, (uint32_t) F.cols() * 2));
E2E.conservativeResize(F.cols()*3);
}
/* Update F */
F.col(f0) << vn, v0p, v0;
if (!is_boundary) {
F.col(f1) << vn, v0, v1p;
F.col(f2) << vn, v1p, v1;
}
F.col(f3) << vn, v1, v0p;
/* Update E2E */
const uint32_t e0p = E2E[dedge_prev_3(e0)],
e0n = E2E[dedge_next_3(e0)];
#define sE2E(a, b) E2E[a] = b; if (b != INVALID) E2E[b] = a;
sE2E(3*f0+0, 3*f3+2);
sE2E(3*f0+1, e0p);
sE2E(3*f3+1, e0n);
if (is_boundary) {
sE2E(3*f0+2, INVALID);
sE2E(3*f3+0, INVALID);
} else {
const uint32_t e1p = E2E[dedge_prev_3(e1)],
e1n = E2E[dedge_next_3(e1)];
sE2E(3*f0+2, 3*f1+0);
sE2E(3*f1+1, e1n);
sE2E(3*f1+2, 3*f2+0);
sE2E(3*f2+1, e1p);
sE2E(3*f2+2, 3*f3+0);
}
#undef sE2E
/* Update V2E */
V2E[v0] = 3*f0 + 2;
V2E[vn] = 3*f0 + 0;
V2E[v1] = 3*f3 + 1;
V2E[v0p] = 3*f0 + 1;
if (!is_boundary)
V2E[v1p] = 3*f1 + 2;
auto schedule = [&](uint32_t f) {
for (int i=0; i<3; ++i) {
Float length = (V.col(F(i, f))-V.col(F((i+1)%3, f))).squaredNorm();
if (length > maxLength)
queue.push(Edge(f*3+i, length));
}
};
schedule(f0);
if (!is_boundary) {
schedule(f2);
schedule(f1);
};
schedule(f3);
}
F.conservativeResize(F.rows(), nF);
V.conservativeResize(V.rows(), nV);
V2E.conservativeResize(nV);
boundary.conservativeResize(nV);
nonmanifold.conservativeResize(nV);
E2E.conservativeResize(nF*3);
cout << "done. (split " << nSplit << " edges, took "
<< timeString(timer.value()) << ", new V=" << V.cols()
<< ", F=" << F.cols() << ", took " << timeString(timer.value()) << ")" << endl;
}

View File

@@ -0,0 +1,22 @@
/*
subdivide.h: Subdivides edges in a triangle mesh until all edges
are below a specified maximum length
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#pragma once
#include "common.h"
extern void subdivide(MatrixXu &F, MatrixXf &V, VectorXu &V2E, VectorXu &E2E,
VectorXb &boundary, VectorXb &nonmanifold,
Float maxLength, bool deterministic = false,
const ProgressCallback &progress = ProgressCallback());

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,239 @@
/*
viewer.h: Contains the graphical user interface of Instant Meshes
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#pragma once
#include "glutil.h"
#include "widgets.h"
#include "hierarchy.h"
#include "field.h"
#include "bvh.h"
#include "meshstats.h"
#include <set>
using nanogui::Alignment;
using nanogui::Arcball;
using nanogui::BoxLayout;
using nanogui::Button;
using nanogui::CheckBox;
using nanogui::Color;
using nanogui::ComboBox;
using nanogui::GLFramebuffer;
using nanogui::GroupLayout;
using nanogui::ImagePanel;
using nanogui::Label;
using nanogui::MessageDialog;
using nanogui::Orientation;
using nanogui::Popup;
using nanogui::PopupButton;
using nanogui::ProgressBar;
using nanogui::Screen;
using nanogui::Slider;
using nanogui::TextBox;
using nanogui::ToolButton;
using nanogui::VScrollPanel;
using nanogui::Widget;
using nanogui::Window;
using nanogui::frustum;
using nanogui::lookAt;
using nanogui::project;
using nanogui::scale;
using nanogui::translate;
using nanogui::unproject;
using nanogui::utf8;
struct CurvePoint;
class Viewer : public Screen {
public:
Viewer(bool fullscreen, bool deterministic);
virtual ~Viewer();
bool mouseMotionEvent(const Vector2i &p, const Vector2i &rel,
int button, int modifiers);
bool mouseButtonEvent(const Vector2i &p, int button, bool down,
int modifiers);
bool keyboardEvent(int key, int scancode, int action, int modifiers);
bool scrollEvent(const Vector2i &p, const Vector2f &rel);
void loadInput(std::string filename,
Float creaseAngle = std::numeric_limits<Float>::infinity(),
Float scale = -1, int face_count = -1, int vertex_count = -1,
int rosy = 4, int posy = 4, int knn_points = 10);
void setSymmetry(int rosy, int posy);
void setExtrinsic(bool extrinsic);
void resetState();
void loadState(std::string filename, bool compat = false);
void saveState(std::string filename);
void renderMitsuba();
void setFloorPosition();
void draw(NVGcontext *ctx);
protected:
void extractMesh();
void extractConsensusGraph();
void drawContents();
void drawOverlay();
bool resizeEvent(const Vector2i &size);
void refreshColors();
void traceFlowLines();
void refreshStrokes();
void showProgress(const std::string &caption, Float value);
void computeCameraMatrices(Eigen::Matrix4f &model,
Eigen::Matrix4f &view,
Eigen::Matrix4f &proj);
void setLevel(int level);
void setTargetScale(Float scale);
void setTargetVertexCount(uint32_t v);
void setTargetVertexCountPrompt(uint32_t v);
bool createSmoothPath(const std::vector<Vector2i> &curve);
void repaint();
void setCreaseAnglePrompt(bool enabled, Float creaseAngle);
void shareGLBuffers();
bool refreshPositionSingularities();
bool refreshOrientationSingularities();
std::pair<Vector3f, Vector3f> singularityPositionAndNormal(uint32_t v) const;
bool toolActive() const;
protected:
struct CameraParameters {
Arcball arcball;
float zoom = 1.0f, viewAngle = 45.0f;
float dnear = 0.05f, dfar = 100.0f;
Eigen::Vector3f eye = Eigen::Vector3f(0.0f, 0.0f, 5.0f);
Eigen::Vector3f center = Eigen::Vector3f(0.0f, 0.0f, 0.0f);
Eigen::Vector3f up = Eigen::Vector3f(0.0f, 1.0f, 5.0f);
Eigen::Vector3f modelTranslation = Eigen::Vector3f::Zero();
Eigen::Vector3f modelTranslation_start = Eigen::Vector3f::Zero();
float modelZoom = 1.0f;
};
std::vector<std::pair<int, std::string>> mExampleImages;
std::string mFilename;
bool mDeterministic;
bool mUseHalfFloats;
/* Data being processed */
std::map<uint32_t, uint32_t> mCreaseMap;
std::set<uint32_t> mCreaseSet;
VectorXb mNonmanifoldVertices;
VectorXb mBoundaryVertices;
MultiResolutionHierarchy mRes;
Optimizer mOptimizer;
BVH *mBVH;
MeshStats mMeshStats;
int mSelectedLevel;
Float mCreaseAngle;
Matrix4f mFloor;
VectorXu mE2E;
/* Painting tools */
std::vector<Vector2i> mScreenCurve;
std::vector<std::pair<uint32_t, std::vector<CurvePoint>>> mStrokes;
/* Extraction result */
MatrixXu mF_extracted;
MatrixXf mV_extracted;
MatrixXf mN_extracted, mNf_extracted;
/* Camera / navigation / misc */
CameraParameters mCamera;
CameraParameters mCameraSnapshots[12];
Vector2i mTranslateStart;
bool mTranslate, mDrag;
std::map<uint32_t, uint32_t> mOrientationSingularities;
std::map<uint32_t, Vector2i> mPositionSingularities;
bool mContinueWithPositions;
/* Colors */
Vector3f mSpecularColor, mBaseColor;
Vector3f mInteriorFactor, mEdgeFactor0;
Vector3f mEdgeFactor1, mEdgeFactor2;
/* OpenGL objects */
GLFramebuffer mFBO;
SerializableGLShader mPointShader63, mPointShader24, mPointShader44;
SerializableGLShader mMeshShader63, mMeshShader24, mMeshShader44;
SerializableGLShader mOrientationFieldShader;
SerializableGLShader mPositionFieldShader;
SerializableGLShader mPositionSingularityShader;
SerializableGLShader mOrientationSingularityShader;
SerializableGLShader mFlowLineShader, mStrokeShader;
SerializableGLShader mOutputMeshShader;
SerializableGLShader mOutputMeshWireframeShader;
bool mNeedsRepaint;
uint32_t mDrawIndex;
/* GUI-related */
enum Layers {
InputMesh,
InputMeshWireframe,
FaceLabels,
VertexLabels,
FlowLines,
OrientationField,
OrientationFieldSingularities,
PositionField,
PositionFieldSingularities,
BrushStrokes,
OutputMesh,
OutputMeshWireframe,
LayerCount
};
CheckBox *mLayers[LayerCount];
ComboBox *mVisualizeBox, *mSymmetryBox;
CheckBox *mExtrinsicBox, *mAlignToBoundariesBox;
CheckBox *mCreaseBox, *mPureQuadBox;
ProgressButton *mSolveOrientationBtn, *mSolvePositionBtn;
Button *mHierarchyMinusButton, *mHierarchyPlusButton;
Button *mSaveBtn, *mSwitchBtn;
PopupButton *mExportBtn;
ToolButton *mOrientationComb, *mOrientationAttractor, *mOrientationScareBrush;
ToolButton *mEdgeBrush, *mPositionAttractor, *mPositionScareBrush;
TextBox *mHierarchyLevelBox, *mScaleBox, *mCreaseAngleBox;
TextBox *mOrientationSingularityBox, *mPositionSingularityBox, *mSmoothBox;
Slider *mScaleSlider, *mCreaseAngleSlider, *mSmoothSlider;
Slider *mOrientationFieldSizeSlider, *mOrientationFieldSingSizeSlider;
Slider *mPositionFieldSingSizeSlider, *mFlowLineSlider;
#ifdef VISUALIZE_ERROR
Graph *mGraph;
#endif
/* Progress display */
std::function<void(const std::string &, Float)> mProgress;
Window *mProgressWindow;
ProgressBar *mProgressBar;
Label *mProgressLabel;
tbb::spin_mutex mProgressMutex;
double mLastProgressMessage;
double mOperationStart;
uint32_t mOutputMeshFaces, mOutputMeshLines;
uint32_t mFlowLineFaces, mStrokeFaces;
};

View File

@@ -0,0 +1,145 @@
/*
widgets.cpp: Additional widgets that are not part of NanoGUI
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#include "widgets.h"
#include "common.h"
#include <nanogui/opengl.h>
using nanogui::Button;
using nanogui::utf8;
using nanogui::nvgIsFontIcon;
using nanogui::Color;
ProgressButton::ProgressButton(Widget *parent, const std::string &caption, int icon)
: Button(parent, caption, icon), mProgress(0.f) { }
void ProgressButton::draw(NVGcontext *ctx) {
Widget::draw(ctx);
NVGcolor gradTop = mTheme->mButtonGradientTopUnfocused;
NVGcolor gradBot = mTheme->mButtonGradientBotUnfocused;
if (mPushed && mProgress == 1) {
gradTop = mTheme->mButtonGradientTopPushed;
gradBot = mTheme->mButtonGradientBotPushed;
} else if (mMouseFocus && mEnabled) {
gradTop = mTheme->mButtonGradientTopFocused;
gradBot = mTheme->mButtonGradientBotFocused;
}
if (mProgress != 1) {
nvgSave(ctx);
nvgScissor(ctx, mPos.x(), mPos.y(), (int) (mSize.x() * mProgress), mSize.y());
}
nvgBeginPath(ctx);
nvgRoundedRect(ctx, mPos.x() + 1, mPos.y() + 1.0f, mSize.x() - 2,
mSize.y() - 2, mTheme->mButtonCornerRadius - 1);
if (mBackgroundColor.w() != 0) {
nvgFillColor(ctx, Color(mBackgroundColor.head<3>(), 1.f));
nvgFill(ctx);
if (mPushed && mProgress == 1) {
gradTop.a = gradBot.a = 0.8f;
} else {
double v = 1 - mBackgroundColor.w();
gradTop.a = gradBot.a = mEnabled ? v : v * .5f + .5f;
}
}
NVGpaint bg = nvgLinearGradient(ctx, mPos.x(), mPos.y(), mPos.x(),
mPos.y() + mSize.y(), gradTop, gradBot);
nvgFillPaint(ctx, bg);
nvgFill(ctx);
if (mProgress != 1) {
nvgRestore(ctx);
}
nvgBeginPath(ctx);
nvgRoundedRect(ctx, mPos.x() + 0.5f, mPos.y() + (mPushed ? 0.5f : 1.5f), mSize.x() - 1,
mSize.y() - 1 - (mPushed ? 0.0f : 1.0f), mTheme->mButtonCornerRadius);
nvgStrokeColor(ctx, mTheme->mBorderLight);
nvgStroke(ctx);
nvgBeginPath(ctx);
nvgRoundedRect(ctx, mPos.x() + 0.5f, mPos.y() + 0.5f, mSize.x() - 1,
mSize.y() - 2, mTheme->mButtonCornerRadius);
nvgStrokeColor(ctx, mTheme->mBorderDark);
nvgStroke(ctx);
nvgFontSize(ctx, mFontSize == -1 ? mTheme->mButtonFontSize : mFontSize);
nvgFontFace(ctx, "sans-bold");
float tw = nvgTextBounds(ctx, 0,0, mCaption.c_str(), nullptr, nullptr);
Vector2f center = mPos.cast<float>() + mSize.cast<float>() * 0.5f;
Vector2f textPos(center.x() - tw * 0.5f, center.y() - 1);
NVGcolor textColor =
mTextColor.w() == 0 ? mTheme->mTextColor : mTextColor;
if (!mEnabled)
textColor = mTheme->mDisabledTextColor;
if (mIcon) {
auto icon = utf8(mIcon);
float iw, ih = mFontSize == -1 ? mTheme->mButtonFontSize : mFontSize;
if (nvgIsFontIcon(mIcon)) {
ih *= 1.5f;
nvgFontSize(ctx, ih);
nvgFontFace(ctx, "icons");
iw = nvgTextBounds(ctx, 0, 0, icon.data(), nullptr, nullptr);
} else {
int w, h;
ih *= 0.9f;
nvgImageSize(ctx, mIcon, &w, &h);
iw = w * ih / h;
}
if (mCaption != "")
iw += mSize.y() * 0.15f;
nvgFillColor(ctx, textColor);
nvgTextAlign(ctx, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
Vector2f iconPos = center;
iconPos.y() -= 1;
if (mIconPosition == IconPosition::LeftCentered) {
iconPos.x() -= (tw + iw) * 0.5f;
textPos.x() += iw * 0.5f;
} else if (mIconPosition == IconPosition::RightCentered) {
textPos.x() -= iw * 0.5f;
iconPos.x() += tw * 0.5f;
} else if (mIconPosition == IconPosition::Left) {
iconPos.x() = mPos.x() + 8;
} else if (mIconPosition == IconPosition::Right) {
iconPos.x() = mPos.x() + mSize.x() - iw - 8;
}
if (nvgIsFontIcon(mIcon)) {
nvgText(ctx, iconPos.x(), iconPos.y()+1, icon.data(), nullptr);
} else {
NVGpaint imgPaint = nvgImagePattern(ctx,
iconPos.x(), iconPos.y() - ih/2, iw, ih, 0, mIcon, mEnabled ? 0.5f : 0.25f);
nvgFillPaint(ctx, imgPaint);
nvgFill(ctx);
}
}
nvgFontSize(ctx, mFontSize == -1 ? mTheme->mButtonFontSize : mFontSize);
nvgFontFace(ctx, "sans-bold");
nvgTextAlign(ctx, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
nvgFillColor(ctx, mTheme->mTextColorShadow);
nvgText(ctx, textPos.x(), textPos.y(), mCaption.c_str(), nullptr);
nvgFillColor(ctx, textColor);
nvgText(ctx, textPos.x(), textPos.y() + 1, mCaption.c_str(), nullptr);
}

View File

@@ -0,0 +1,28 @@
/*
widgets.h: Additional widgets that are not part of NanoGUI
This file is part of the implementation of
Instant Field-Aligned Meshes
Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#pragma once
#include <nanogui/nanogui.h>
class ProgressButton : public nanogui::Button {
public:
ProgressButton(Widget *parent, const std::string &caption = "Untitled", int icon = 0);
float progress() const { return mProgress; }
void setProgress(float value) { mProgress = value; }
void draw(NVGcontext *ctx);
private:
float mProgress;
};

View File

@@ -65,6 +65,12 @@ void QFLOW_quadriflow_remesh(QuadriflowRemeshData *qrd,
/* Get remeshing parameters. */
int faces = qrd->target_faces;
field.flag_adaptive_scale = 1;
field.flag_minimum_cost_flow = 1;
field.flag_preserve_boundary = 1;
field.flag_preserve_sharp = 1;
// field.flag_aggresive_sat = 1;
if (qrd->preserve_sharp) {
field.flag_preserve_sharp = 1;
}
@@ -91,6 +97,7 @@ void QFLOW_quadriflow_remesh(QuadriflowRemeshData *qrd,
/* Copy mesh to quadriflow data structures. */
std::vector<Vector3d> positions;
std::vector<uint32_t> indices;
std::vector<uint32_t> eflags;
std::vector<ObjVertex> vertices;
for (int i = 0; i < qrd->totverts; i++) {
@@ -99,16 +106,18 @@ void QFLOW_quadriflow_remesh(QuadriflowRemeshData *qrd,
}
for (int q = 0; q < qrd->totfaces; q++) {
Vector3i f(qrd->faces[q * 3], qrd->faces[q * 3 + 1], qrd->faces[q * 3 + 2]);
Vector3i f(qrd->faces[q].v[0], qrd->faces[q].v[1], qrd->faces[q].v[2]);
ObjVertex tri[6];
int nVertices = 3;
const int nVertices = 3;
tri[0] = ObjVertex(f[0]);
tri[1] = ObjVertex(f[1]);
tri[2] = ObjVertex(f[2]);
for (int i = 0; i < nVertices; ++i) {
eflags.push_back(qrd->faces[q].eflag[i]);
const ObjVertex &v = tri[i];
VertexMap::const_iterator it = vertexMap.find(v);
if (it == vertexMap.end()) {
@@ -123,7 +132,10 @@ void QFLOW_quadriflow_remesh(QuadriflowRemeshData *qrd,
}
field.F.resize(3, indices.size() / 3);
// field.FF.resize(3, indices.size() / 3);
memcpy(field.F.data(), indices.data(), sizeof(uint32_t) * indices.size());
// memcpy(field.FF.data(), eflags.data(), sizeof(uint32_t) * eflags.size());
field.V.resize(3, vertices.size());
for (uint32_t i = 0; i < vertices.size(); ++i) {
@@ -142,12 +154,17 @@ void QFLOW_quadriflow_remesh(QuadriflowRemeshData *qrd,
return;
}
const int steps = 2;
/* Setup mesh boundary constraints if needed */
if (field.flag_preserve_boundary) {
#if 1
if (true) { // field.flag_preserve_boundary) {
Hierarchy &mRes = field.hierarchy;
mRes.clearConstraints();
for (uint32_t i = 0; i < 3 * mRes.mF.cols(); ++i) {
if (mRes.mE2E[i] == -1) {
if (qrd->faces[i / 3].eflag[i % 3] & QFLOW_CONSTRAINED) {
// if (mRes.mE2E[i] == -1) {
uint32_t i0 = mRes.mF(i % 3, i / 3);
uint32_t i1 = mRes.mF((i + 1) % 3, i / 3);
Vector3d p0 = mRes.mV[0].col(i0), p1 = mRes.mV[0].col(i1);
@@ -157,15 +174,21 @@ void QFLOW_quadriflow_remesh(QuadriflowRemeshData *qrd,
mRes.mCO[0].col(i0) = p0;
mRes.mCO[0].col(i1) = p1;
mRes.mCQ[0].col(i0) = mRes.mCQ[0].col(i1) = edge;
mRes.mCQw[0][i0] = mRes.mCQw[0][i1] = mRes.mCOw[0][i0] = mRes.mCOw[0][i1] = 1.0;
mRes.mCQw[0][i0] = mRes.mCQw[0][i1] = mRes.mCOw[0][i0] = mRes.mCOw[0][i1] = 0.1;
}
}
}
mRes.propagateConstraints();
for (int j = 0; j < 2; j++) {
mRes.propagateConstraints();
}
}
#endif
/* Optimize the mesh field orientations (tangental field etc) */
Optimizer::optimize_orientations(field.hierarchy);
for (int i = 0; i < steps; i++) {
Optimizer::optimize_orientations(field.hierarchy);
}
field.ComputeOrientationSingularities();
if (check_if_canceled(0.3f, update_cb, update_cb_data)) {
@@ -180,11 +203,13 @@ void QFLOW_quadriflow_remesh(QuadriflowRemeshData *qrd,
return;
}
Optimizer::optimize_scale(field.hierarchy, field.rho, field.flag_adaptive_scale);
field.flag_adaptive_scale = 1;
Optimizer::optimize_positions(field.hierarchy, field.flag_adaptive_scale);
for (int i = 0; i < steps; i++) {
Optimizer::optimize_scale(field.hierarchy, field.rho, field.flag_adaptive_scale);
}
for (int i = 0; i < steps; i++) {
Optimizer::optimize_positions(field.hierarchy, field.flag_adaptive_scale);
}
field.ComputePositionSingularities();
if (check_if_canceled(0.5f, update_cb, update_cb_data)) {

View File

@@ -8,9 +8,16 @@
extern "C" {
#endif
enum { QFLOW_CONSTRAINED = 1 };
typedef struct QuadriflowFace {
int v[3];
char eflag[3];
} QuadriflowFace;
typedef struct QuadriflowRemeshData {
float *verts;
int *faces;
QuadriflowFace *faces;
int totfaces;
int totverts;

View File

@@ -118,7 +118,14 @@ def object_child_map(objects):
ob_all.sort(key=lambda ob: ob.name)
return objects_children
import sys
def get_active_vcol(me):
vcol = me.vertex_colors.active
if not vcol and len(me.vertex_colors) > 0:
vcol = me.vertex_colors[0]
return vcol
def mesh_data_lists_from_mesh(me, material_colors):
me_loops = me.loops[:]
me_verts = me.vertices[:]
@@ -130,7 +137,10 @@ def mesh_data_lists_from_mesh(me, material_colors):
me_loops_color = None
tris_data = []
class white:
color = [1, 1, 1, 1]
for p in me_polys:
# Note, all faces are handled, backfacing/zero area is checked just before writing.
material_index = p.material_index

Some files were not shown because too many files have changed in this diff Show More