Compare commits

...

325 Commits

Author SHA1 Message Date
9dd50b39a4 Merge remote-tracking branch 'origin/blender-v2.90-release' into soc-2020-production-ready-light-tree-2 2020-08-20 11:40:02 -05:00
35624cdfe5 Cycles: added comments to light tree code 2020-08-20 09:43:18 -05:00
c74b4caa72 Fix incorrect mask use with curves
Ref D8652
2020-08-20 21:51:35 +10:00
0fae682d61 Fix T79935: Inonsistent simplify behavior when linking collection with objects in sub-collection.
RNA update function would only update objects from 'main' instantiated
collection, not those from sub-collections.

This should be comitted to 2.90 (and backported to 2.83 too).

Maniphest Tasks: T79935

Differential Revision: https://developer.blender.org/D8654
2020-08-20 11:52:12 +02:00
4429c39264 Cycles: Cleaning up light tree code.
* removed unused update light picking code
* switched all light tree function parameters to be in the same P, V, t order
* renamed N_pick to V_pick since it is not always a normal
2020-08-19 13:58:30 -05:00
83c78529b9 Fix T79816: Restore scene.statistics() BPY function
This RNA/BPY function was removed in c08d847488. For understandable
reasons really-- getting scene statistics from a string displayed in the
status bar is not exactly the best design. But we have committed to not
changing the RNA API too much for the 2.90 release, so we would like to
keep this functionality.

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

Reviewd by: Julian Eisel
2020-08-19 12:14:03 +02:00
97dc370c50 Fix compiler error in MSVSC
Introduced in: rB20a8edaa725ddbae16179d2f7cea88c097c61615

It broke building on windows, initializing a static may not be done with a function in C.
2020-08-19 08:58:29 +02:00
Hans Goudey
04ca28a6e0 Fix T77300: Some scientific notation evaluation has incorrect results
Ref D7922
2020-08-19 14:44:42 +10:00
762e4cf221 BLI_listbase: add utility macro for looping over lists with an index
Add to the 2.90 branch to avoid problems if fixes from master use it.
2020-08-19 13:43:51 +10:00
d5b5b228e4 UI: Fix protential stray GPU state preventing correct drawing
Fix potential issue with nodetree introduced in rB47c6f41b8926.
2020-08-18 21:29:47 +02:00
2452031268 Cycles: never hard code -1 for t_max when calling light_sample 2020-08-18 09:39:11 -05:00
47c6f41b89 Fix T77564: VSE (and compositor background) lost stereoscopy preview
Issue introduced on fe045b2b77.

Since the stereoscopy compositing (anaglyph, ...) is only done for
viewports the VSE preview and compositor need to use viewports.

Reviewed by: dfelinto

Differential Revision: https://developer.blender.org/D8472
2020-08-18 15:50:49 +02:00
83e3d25bca Remove (ifdef) RNA for simulation, point cloud and particles
For 2.90 release this should not be exposed in the RNA API.

In master this needs to be ON by default, that's all.

Differential Revision: https://developer.blender.org/D8589
2020-08-18 14:41:16 +02:00
20a8edaa72 No experimental feature (but debug ones) to work for blender beta/release
Final releases (including beta) should strictly show features that are
finalized to prevent loss of data, old API clanging around, and the
overall quality of the product (Blender) presented.

Note that rendering should never be affected by user preferences, so
this is only changing things in the UI level.

Development note: This is reset experimental UI on file load.
Also note: to hide RNA (needed for hair and particles) will be done as a
separate patch.

Differential Revision: https://developer.blender.org/D8606
2020-08-18 14:02:47 +02:00
aba46371a1 GPUTexture: Extend CUBE_MAP_ARRAY_ARB proxy workaround to all Apple gpus
Related to T79716
2020-08-18 14:01:14 +02:00
6df4b00f5f Fix IDProps definition still being editable in liboverrides.
One should be able to edit overridable IDProps values, but never their
settings/definitions.

Note that being able to add new IDProps to overrides is still a TODO.

Reported by Josephbburg (@Josephbburg) over blenderchat, thanks.
2020-08-18 12:43:42 +02:00
0b49fdd0ee Fix/cleanup Constraint poll function in liboverride cases.
Some constraint-specific operators, like set/clear inverse matrix of
childof constraint, are also valid on original, linked/overridden
constraints.

Similar change to what was done to modifiers poll function a few days
ago.

Reported by Josephbburg (@Josephbburg) over IRC, thanks.
2020-08-18 11:36:34 +02:00
567e333ea4 Fix T79580: Control Mesh Only Partially Drawn After Filling a Surface From a Cage of Surface Curves
Differential Revision: https://developer.blender.org/D8600
2020-08-18 09:51:36 +02:00
f94e322ab7 Fix T79770: Crash opening 2.83 .blend with linked collection containing greasepencil object in 2.91
The scene was null and could not be patched.
2020-08-18 08:36:26 +02:00
d1057890c4 Fix incorrect pixelsize use where DPI scale was intended
Changing line-width shouldn't scale cursor motion. Related to T79787.

Use dpi_fac for scaling curve error threshold & number button drag
threshold calculation.
2020-08-18 12:09:30 +10:00
6978635622 Fix T79787: orbit/zoom sensitivity depends on line-width
Use 'dpi_fac' instead of 'pixelsize' to scale input sensitivity
based on the interface scale.
Also use dpi_fac for view zoom operator.

Thanks to @ISS for investigating.
2020-08-18 11:44:16 +10:00
9085fb8073 Cleanup: expand UserDef pixel-size & DPI documentation
Avoid misunderstandings with UI scaling.
2020-08-18 11:17:15 +10:00
e157573fab Fix T77683: Cycles baking crash with motion blur enabled and no camera
specified

Maniphest Tasks: T77683

Differential Revision: https://developer.blender.org/D8593
2020-08-17 21:04:55 +02:00
Stefan Werner
1c892e6814 Cycles: Fix local intersections in Embree for non-instancd geometry.
Embree's occlusion filter was checking against the wrong object ID
and not exiting correctly in case of a mismatch.

Fixes T79723
2020-08-17 20:41:34 +02:00
9c6ef48315 Cycles: Removed some unused light tree code 2020-08-17 13:38:50 -05:00
4b69e55da3 Fix T79757: Crash on prefetch when renaming strips
Original sequence lookup failed, becase name changed in another thread.
Fix is same as 0471349c90 - stop prefetching before changing content
of seqbase.

I have covered more cases, and added assert so it is more obvious that
issue is in lookup, and it shouldn't fail.

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D8592
2020-08-17 20:19:11 +02:00
2b896fc481 Fix T79052: Cycles new sky texture fails with sun size zero
Clamp to a minimum angle to avoid precision issues.
2020-08-17 17:57:29 +02:00
c7a7a38b65 Fix T79633: ovrride apply code broken with RNA-defined runtime IDProps in some cases.
When copying between a set RNA runtime property and an unset one, code
would not behave properly.

We have to also consider NULL 'override apply' callback pointer as a
valid 'use default override apply function' case.
2020-08-17 16:44:36 +02:00
8010cbe620 Fix T79823: Typo in cloth filter type description 2020-08-16 08:54:07 -04:00
59bc71d69b Fix popups opening in the wrong direction over headers
Checks for header alignment didn't account for tool-header & header
having different alignment.

There is no reason to use a lookup function on the area
(ED_area_header_alignment) as we already have region.

Check the regions alignment directly, remove access functions.
2020-08-16 21:14:26 +10:00
73fb445b8d Fix T78426: Header flips when changing editors
The existing header flip detection didn't account for mixed
tool-header and header flipping & visibility between space-types.

Now alignment syncing handles any combination of header,
tool-header & footer flipped state, in a way that can be extended
to other region types in the future.
2020-08-16 14:18:40 +10:00
c300fc56c3 UI: Fix type in multires modifier subpanel
Thanks to @zanqdo for reporting "Subdivions"

Also remove the empty space I left for the "Delete Lower" operator that
never made it into 2.90.
2020-08-14 16:34:48 -04:00
114a5a8ce8 Cycles: Fixed small light tree volume bug 2020-08-14 15:22:04 -05:00
9ab3b8f7f4 UI: Remove cutoff text in popovers for 2.90 release
Many of these popovers could use a design pass in 2.91, but for 2.90 we
don't want to change any UI strings at this point, so the best way to
solve the cutoff text is to widen the popovers. Sadly this won't affect
popovers when other languages besides English have longer strings, but
solving that is a much larger task.

Another benefit is that tweaking sculpt / paint brush options feels much
less cramped with slightly wider popovers.

I only know of one string that is still slightly cutoff by default with
this patch, the "Max Element Distance" property of the pose brush in the
sculpt mode brush settings popover. But I didn't think it was worth
widening that popover more to deal with that one case.

Differential Revision: https://developer.blender.org/D8575
2020-08-14 12:58:24 -04:00
04f703fca6 Fix warning when compiling on Linux with WITH_XR_OPENXR enabled 2020-08-14 17:57:24 +02:00
b3c08a3a0a Fix constant lighting change in VR view when rotating head
We have to explicitly enable fixed world space lighting. This was in
fact already done, but overridden by the code to sync the 3D View
shading settings to the VR view.
2020-08-14 17:03:10 +02:00
c074943dfd Fix undefined behavior with --debug-xr
Mistake in cb578ca104. Before that, the extension vector was static,
to make sure the extension name strings wouldn't get destructed when
leaving the function. I didn't think that was an issue and couldn't
recreate one, because until the previous commit we wouldn't actually
add any extensions to the vector on Windows (the system I tested
with).

Use C++17's `std::string_view` now, which avoids the string copies
`std::string` creates for itself and thus its destruction when leaving
the local scope.
2020-08-14 17:03:10 +02:00
77e4905b17 Fix --debug-xr not outputting OpenXR debug prints on Windows
The OpenXR debug extension was disabled on Windows as a workaround. This
was an old leftover from when there was only the Windows Mixed Reality
runtime on Windows. The debug extension didn't work for it and we didn't
have a way to disable it just for Windows Mixed Reality.
Now it seems to work though, so we remove the workaround. If specific
runtimes still have trouble with the extension, we can disable it
specifically for these runtimes now.
2020-08-14 17:03:10 +02:00
d958ab62e7 Fix NULL pointer access in render engine reported by address sanitizer
This may not have caused an actual bug.
2020-08-14 16:49:22 +02:00
690d76c624 Fix T79769, T79768: crash tweaking volume settings with Cycles viewport render
Refitting the BVH does not work in this case, it needs to be fully rebuilt.
2020-08-14 16:49:08 +02:00
cb578ca104 Fix/workaround graphics issues breaking SteamVR use with Blender
Windows only workaround. I'll have to investigate Linux separately.

Steam's OpenGL compatibility is still new and doesn't work for us yet
(neither does it for standard OpenXR examples from what I've heard and
seen myself). We can work around that by falling back to our DirectX
compatibility layer.
Note that this DirectX compatibility still doesn't work for some
systems, see T76082.

Implementation note: Since the graphics binding extensions have to be
enabled before we can find out which runtime is in use (e.g. SteamVR vs.
Oculus, etc), we can now enable multiple graphics binding extensions but
settle for a single one to use later.

Once the SteamVR OpenGL backend works, we can remove this workaround
again.

Fixes T78267.
2020-08-14 16:00:03 +02:00
ab3a651515 Fix offset applied on top of VR landmark with no positional tracking
On VR session start with positional tracking disabled, the pose would
have an offset applied but it was supposed to start exactly at the
landmark position.

Issue is that we applied the offset to cancel out the position offset
reported by the OpenXR runtime incorrectly. We only want to do that if
positional tracking is enabled, because if not we don't even apply the
runtime's position offset. So we'd cancel something out that wasn't
there.
2020-08-14 13:27:09 +02:00
4c625df759 Fix: Increase Viewport Sculpt Options panel width
The "Delay Viewport Update" label was showing only:
"Delay Viewport U..."
2020-08-14 11:36:40 +02:00
a5cf71fa5d Fix (unreported) sculpt vertex color panel visible for all object types
Since DATA_PT_sculpt_vertex_colors has its own poll() we need to call
the poll() of MeshButtonsPanel as well

Differential Revision: https://developer.blender.org/D8563
2020-08-14 10:35:30 +02:00
3b49ca465f Finished bringing light tree code up to date 2020-08-13 15:17:04 -05:00
c5519d4b6f Fix T78065: OSL shader compilation fails on macOS 2020-08-13 17:53:48 +02:00
9813778a2d Fix T79591: Liboverride: do not update overrides on missing linked data.
It makes no sense to generate/update overrides from missing (broken
linked) reference data, just keep existing ones unchanged then.
2020-08-13 17:41:33 +02:00
7c38d008de Merge branch 'blender-v2.90-release' into soc-2020-production-ready-light-tree-2 2020-08-13 09:58:33 -05:00
4e103101f7 UI: Add sculpt cloth filter tool icon
This icon mimics the details of the cloth brush icon while using the
frame style extablished for the other "filter" tools.

Differential Revision: https://developer.blender.org/D8467
2020-08-13 10:04:48 -04:00
4f209fab2c Fix T79082: Softbody self-collision does not work on lattices
Reviewers: zeddb

Differential Revision: https://developer.blender.org/D8562
2020-08-13 14:41:44 +02:00
ada98869df Preferences: correct property description for keyitem restore
Spotted while checking T79657.

Reviewers: brecht

Differential Revision: https://developer.blender.org/D8517
2020-08-13 11:44:57 +02:00
0843d5b024 Fix T79743: baking still uses cage object after removal 2020-08-13 11:30:56 +02:00
28c1300115 Fix T79653: Change soft min frame start of cache from 1 to 0
It was always possible to set it to zero by typing in the value.
This new soft limit is more consistent with the fluid cache
and the Scene.frame_start property.
2020-08-13 11:03:05 +02:00
29c28ac022 Cleanup: incorrect comment wrapping
Missed this comment when updating fix for T77409.
2020-08-13 16:38:01 +10:00
ad05e1100f Fix T77409: Crash showing vertex/face duplicators in edit-mode
This was a regression in deaff945d0 which skips copying a mesh.
Dupli-verts/faces were not updated to account for this.

This supports iterating over edit-mesh vertices & faces,
since falling back to a full copy (as we do in some places)
will be slow while transforming geometry.

This commit looks as if it would change behavior with orcos,
since any edit-mesh deformation causes them to be assigned.
However in practice there is no functional change, details in comments.
2020-08-13 15:41:57 +10:00
9bf8b6799d Cleanup: remove two sided face check
Two sided faces aren't supported and would cause many issues elsewhere.
2020-08-13 15:41:57 +10:00
ee909783de Cleanup: use 'inst_ob' variable name for consistency
Dupli verts/faces named these arguments differently.
2020-08-13 15:41:57 +10:00
494560f19d Cleanup: remove unused struct members from dupli-face/vert
Replace the evaluated mesh with VertexDupliData.mvert since only
vertices are used. This makes dupli-vert similar to how dupli-face
was already working.
2020-08-13 15:41:57 +10:00
a45f2fd1fb Cleanup: move mesh access for dupli vert/face into shared function
De-duplicate mesh access & comments.
2020-08-13 15:41:57 +10:00
12c24ecf6e Cleanup: pass normal as a float to dupli-vert function
Needed for supporting edit-mode dupli-verts.

Currently the un-scaled short values are used to avoid
changing behavior (noted in comments).
2020-08-13 15:41:57 +10:00
51a461dcab Cleanup: spelling, use full sentences for object_dupli.c 2020-08-13 15:41:52 +10:00
f0285e347e Cleanup: use const for dupli vert/face, 'r_' prefix for return value
Make it obvious which values are used read-only, which are written to.
2020-08-13 14:03:46 +10:00
pembem22
bb2908472c UI: Fix curve widget fill artifacts
Disable antialiasing which caused artifacts.

Differential Revision: https://developer.blender.org/D8497
2020-08-12 21:02:05 -04:00
cc86e03fd5 brought light tree code up to date 2020-08-12 15:00:25 -05:00
ba2d1c8898 Fix T79738: Double Click does not opening folders in File Browser
If the File Browser was used in regular editor mode (e.g. not through an
operation like File > Open), the operator that usually opens files and
directories wouldn't execute. We need to keep two operators for
double-click in the keymap so selecting and opening works in all cases.

Caused by c606044157.
2020-08-12 19:26:04 +02:00
b3165fb8b5 Fix (unreported) glitch when making liboverride of object from IDtemplate.
We need to ensure new override is instantiated in the scene...

Reported by @Severin, thanks.
2020-08-12 19:03:03 +02:00
ac0bab2d43 Merge branch 'blender-v2.90-release' into soc-2020-production-ready-light-tree-2 2020-08-12 11:59:35 -05:00
54c2c14921 CMake: clarify that the modules are licensed BSD 3-Clause
Many of these are derived from similar modules in the CMake project, which
have this license.

Fixes T79715
2020-08-12 18:49:16 +02:00
58a457da3d Fix T79680, T79680: confusing viewport denoise start sample behavior 2020-08-12 18:10:50 +02:00
6f502136c7 Fix T79718: Eevee OpenVDB render error when frames miss part of the grids 2020-08-12 18:10:50 +02:00
bff91b32aa Fix T79706: Delta Transform Animation not working
The object "delta_" rna variables were not added to the depsgraph search
and thus it would not trigger updates of the object during animation
playback.
2020-08-12 18:09:00 +02:00
2218b61e8e Fix T79637 Motion blur gives artifacts when changing the camera
DRW_render_set_time is calling RE_engine_frame_set will in turn calls
BKE_scene_camera_switch_update.

To workaround this, we get the original camera object at render init and
get the evaluated version from it after each time change.
2020-08-12 18:06:36 +02:00
879ed5a165 EEVEE: Motion Blur: Fix issue with batch overflowing with VBOs 2020-08-12 18:06:36 +02:00
bea79e0c7b EEVEE: Fix dupli recursion constant 2020-08-12 18:06:36 +02:00
1eab858dbc EEVEE: Rework deformation motion blur
This change how motion data are indexed inside the ghash.
We follow cycles closely now and use evaluated ID pointers.

By removing the hack, it fixes T78561 (No Motion Blur on linked objects)
2020-08-12 18:06:36 +02:00
2b042d885a EEVEE: Motion Blur: Use evaluated object as key to motion data
This fix issues with instanced geometry and modifiers. Since the
depsgraph will duplicate the objects when they have different modifiers,
the evaluated object are garanteed to be unique.
2020-08-12 18:06:36 +02:00
cfbea0e09d LibOverride and modifiers: Add copying of linked modifiers.
It also enables copying of linked modifiers (generating new local ones).
2020-08-12 16:16:37 +02:00
21118fb610 Fix T79622: Mesh Filter on a locked Shape crashes
For a locked shapekey, a SculptSession's orig_cos / deform_cos /
deform_imats are not initialized (they only are when
deform_modifiers_active is true -- this in turn is only true for
shapekeys if they are //not// locked [and for deforming modifiers of
course])

We can just update that keyblock with sculpt_update_keyblock() in case
of a locked shapekey

Maniphest Tasks: T79622

Differential Revision: https://developer.blender.org/D8499
2020-08-12 15:53:22 +02:00
08286ef8ba Fix T79712: Color Changes do not record in Auto-Key Mode
Caused by rBfffba2b6530.

In above commit, the buttons rnaindex [-1 for entire arrays like colors]
was not used anymore for the entire function body of
`ED_autokeyframe_property` (previously in `ui_but_anim_autokey`).
Replacing the index with 0 (in the array case) is indeed necessary for
`BKE_fcurve_find_by_rna_context_ui`, prior to rBfffba2b6530 this was
taken care of by using `ui_but_get_fcurve` instead [which did this
internally].

But using an index of 0 (instead of -1) for the entire function body of
`ED_autokeyframe_property` fails for the array part later, namely
`insert_keyframe` needs -1 here.
Now just replace the index for //finding the FCurve//, but use the
original for //inserting the keyframe//.

Could be backported to 2.83 LTS.

Reviewers: campbellbarton, sybren

Subscribers:
2020-08-12 15:28:12 +02:00
Kévin Dietrich
9280fb19e4 Fixes T77882: artifacts rendering OpenVDB volumes with multiple grids in Cycles
The previous algorithm was not using all of the requested grids to build a mesh
around the volume due to limitations regarding the use of a dense buffer to
gather information about the volume's topology. This resulted in artefacts during
rendering.

The mesh generation is now done by merging all of the input grids and using the
resulting grid's topology to create the mesh. The generation of the mesh
is still done in index space as before, and the vertices are converted to object
space by using the merged topology grid indexToWorld transform.

To be able to merge the grids together we have to make sure that their transformation
matrices and their index spaces match, thus, if they do not match we simply resample
the grids. This behaviour should tackle one other limitation of the current algorithm,
which is that only one transformation matrix was used to generate the final mesh.

If we do not have an OpenVDB grid for the requested volume data, we generate
a temporary OpenVDB grid for it.

Differential Revision: https://developer.blender.org/D8401
2020-08-12 11:52:12 +02:00
da95fa8344 Fix UV unwrap not working with only one pinned vertex
Now the one vertex defines the position of the UV chart, while rotation and
shape is still determined automatically.

Initial patch by Willis (wlssirius).

Differential Revision: https://developer.blender.org/D8484
2020-08-12 11:52:09 +02:00
cbb1bb90fd Bump the minimum required version for 2.90
This is particular important because 2.90 will coexist with 2.83 LTS, so
we should warn the users of potential loss of data when going from 2.90
to 2.83 and back.

Differential Revision: https://developer.blender.org/D8488
2020-08-12 11:43:29 +02:00
d070d33aa3 Cleanup: Use proper bool type and literals for operators poll functions. 2020-08-12 11:11:48 +02:00
4e31b5b173 Fix (unreported) broken Poll function of GPencile mods and ShaderFX operators in liboverride case.
Those where assuming we always get a valid modifier data from context,
which is not always true...

Also  fix similar issue with shortcuts as reported in T79635.
2020-08-12 11:11:48 +02:00
b3702f5918 Fix part of T79635: Disable by default operation on unknown mod from liboverride object.
It is unfortunate that we cannot get active modifier from context when
operator is called from a shortcut, but we'd need an event for this to
work... So for now forbid any modifier operation of liboverride objects
in that case.
2020-08-12 11:11:48 +02:00
9b58a41a92 Fix T79708: ShapeKey value greyed out
Mistake in rB0e9999a93edd.

Maniphest Tasks: T79708

Differential Revision: https://developer.blender.org/D8539
2020-08-12 10:35:06 +02:00
dbf5bb7af2 Fix T79640: "Assign Shortcut" doesn't work for "View 2D Zoom"
'VIEW2D_OT' operators were not respected in WM_keymap_guess_opname().
This was seemingly done on purpose (see comment "Op types purposely
skipped  for now"), but dont really see the reason for doing so.

Since the "View2D" keymap is not bound to a specific spacetype, we can
still find it using WM_keymap_find_all() [and passing 0 as spacetype].

Reviewers: Severin

Subscribers:
2020-08-11 22:06:15 +02:00
2fc2a0a56b Fix T79700: skin modifier: prevent error for vert-only mesh
Vert-only mesh is valid input for the skin modifier (displays isolated
cubes), prevent error message about missing root vertex in that case.

Maniphest Tasks: T79700

Differential Revision: https://developer.blender.org/D8533
2020-08-11 21:41:52 +02:00
aec0cfaf08 Fix T79676: Video Sequencer image sequence strip source path breaks when
saving with 'Remap Relative' option

Caused by rBf7386b97571e.

Logic in BKE_bpath_traverse_main calls the callback multiple times [as
often as there are images in the strip].

Prior to above commit, the callback was
'bpath_relative_convert_visit_cb' [this one did not have this problem -
since it returned early if the path was already made relative once]

After rBf7386b97571e though, the 'bpath_relative_rebase_visit_cb' is
used [this one should not be entered multiple times, it would modifiy the
directy again and again].

Luckily, we have a flag (BKE_BPATH_TRAVERSE_SKIP_MULTIFILE) that can be
used to prevent this (this will take care of only calling the callback
once in BKE_bpath_traverse_main for the VSE case)

Could be backported to 2.83 I think.

Maniphest Tasks: T79676

Differential Revision: https://developer.blender.org/D8536
2020-08-11 21:21:40 +02:00
74556a5a17 Fix T79703 EEVEE: Crash on Macos due to lightcache baking 2020-08-11 20:07:03 +02:00
7219abc5bd Fix T79672 EEVEE: Motion blur steps value broken after recent change
Was just an issue of `taa_render_sample` being reset to 1 when it shouldn't.
2020-08-11 20:07:03 +02:00
d2c150772a Fix T79692: Full copy of scene makes Blender freeze if there is content directly in the master collection.
Old and new collections are the same data in Master collection case
here, so we cannot consider the `gobject` listbase of `collection_old`
as always immutable.
2020-08-11 18:46:47 +02:00
eca062b9cb Fix T79563: Compositor's Stabilize 2D in invert mode does not work correctly
Is not only the values of translation/scale/rotation which are to be inverted,
but also the order of operations.

Differential Revision: https://developer.blender.org/D8518
2020-08-11 17:00:30 +02:00
e84e6e12ed Fix T79616: Sort by column in filebrowser is broken
After changes in rBc606044157a3, mouse press events would be blocked by
the selection operator. This only worked by chance before.
2020-08-11 16:55:35 +02:00
1c294fb1fd Revert "Fix T77409: Crash showing vertex/face duplicators in edit-mode"
This reverts commit 9adedb2605. It changes
how duplis work, and by that altered how Alembic and USD files are
written. This was signalled by a failing Alembic unit test.
2020-08-11 16:12:44 +02:00
18c9f7ef72 Fix T79517: Data Transfer modifier fails in edit-mode 2020-08-11 21:48:02 +10:00
6b573d9877 Fix mesh data-transfer tracking if a change was made 2020-08-11 21:48:02 +10:00
e11aa3eddd Python: don't remove existing context overrides when calling an operator
Reviewers: campbellbarton, brecht

Differential Revision: https://developer.blender.org/D8532
2020-08-11 13:32:07 +02:00
6d888133da Timers: set first window as context in timer
This avoids some crashes when running Python code in timers.

Reviewers: brecht

Differential Revision: https://developer.blender.org/D8531
2020-08-11 13:24:52 +02:00
bb5cc3bccf Fix memory leak setting error text in the data-transfer modifier 2020-08-11 17:10:36 +10:00
9adedb2605 Fix T77409: Crash showing vertex/face duplicators in edit-mode
Support duplicators in edit-mode without creating a mesh copy.
2020-08-11 16:21:19 +10:00
23a6b5d91e BMesh: add UV calculate center call
Move uv_poly_center to BM_face_uv_calc_center_median as
it was only defined in uvedit_intern.h
2020-08-11 15:11:31 +10:00
fc5ff99770 Cleanup: use doxy sections for object_dupli.c 2020-08-11 14:57:20 +10:00
bc5d144855 Fix T77298: Can't bake texture with multiple objects
The problem here is that the baking code uses tiles to exchange pixel data with
the renderer since a recent-ish refactor, but the code that sent data to the
renderer did not initialize the bake result pixels.

Therefore, when the baking process for the second object started, Cycles
received empty tiles and sent them back as-is if the second object did not
cover them.

By initializing the tiles with the result of the previous bakes, we avoid this
problem.
2020-08-10 22:54:01 +02:00
8ef05d3180 Fix T79636: Inserting special characters with Ctrl+Alt broken on Windows
We can't exactly follow what we do for macOS here. On Windows special
characters can be inserted with Ctrl+Alt. So make sure we expect UTF-8
characters when Alt is held.

Mistake in 87062d4d67.
2020-08-10 18:29:02 +02:00
3b08cb3236 Cleanup: follow code style regarding braces 2020-08-10 18:18:59 +02:00
9c093a5d9a Fix T79324: Crash when changing View Layer while VR session runs
Proper handling of View Layers for the VR session was never implemented.
Now the View Layer of the VR session follows the window the session was
started in.
Note that if this window is closed, we fallback to another window. This
is done to avoid the overhead it would take to maintain a separate
depsgraph for the VR view. Instead we always share some already visible
View Layer (and hence the depsgraph).
2020-08-10 17:39:36 +02:00
c0340ec893 Fix T78113: Random explosions of cloth with self collision
The problem is caused by a lack of prediction in the `isect_line_segment_tri_v3`
that incorrectly confirms some intersections of coplanar segments to the triangle.

The solution is to use another algorithm to detect intersections.

This also resulted in a slight improvement in the performance:
- 1min 17sec to 1min 6sec in my test file

Differential Revision: https://developer.blender.org/D8500
2020-08-10 12:05:37 -03:00
ab2dbafd8b Fix T77847: "Add plane > align" causes crash when certain rigs are in the scene (2.83, fixed in 2.90).
Root of the issue was not fixed in 2.90, only hidden by the fact that we
now re-read much less data during undo's that we used to, when some new
datablock gets added or removed.

This is not an ideal solution (as usual when dealing with data pointers
shared across data-blocks), but it's decent enough. thanks a lot to
@brecht for it!

To be backported to 2.83 too.
2020-08-10 14:34:55 +02:00
2e5c877056 Fix pose offset on VR session start for some OpenXR runtimes
We want the session to start exactly at the landmark position, with
no additional offset. Some runtimes (e.g. Windows Mixed Reality) may
give an initial non-[0,0,0] position at session start though.

Also add a comment explaining the purpose of the eye offset variable.
2020-08-10 13:52:13 +02:00
f1cb3dfbaa Fix broken behavior on active VR Landmark change
There would always be an unintended offset applied. Per design there
should not be any offset when changing VR Landmarks, the view should
just jump exactly to the Landmark.

Due to the recent changes, we don't have to add, but substract the eye
offset we apply to get the wanted behavior.

Mistake in 607d745a79.
2020-08-10 13:51:43 +02:00
342a6b5f93 Fix T77685: object transforms from rigid body simulation are ignored by modifiers
This does not fix all the cases in the bug report, because there are multiple
different issues. Only the first two are fixed. The third is probably a known
issue for now.

Before this patch, the rigid body simulation was always done after modifiers
are evaluated, because to perform the simulation, the final geometry of the
object was required. However, the geometry is not required in all cases,
depending on the selected collisions shape.

This patch changes it so that when the simulation does not need the
evaluated geometry, the simulation will be done before the modifiers
are evaluated. This gives the modifiers access to the simulated positions.
When the rigid body simulation does depend on the evaluated geometry,
it will still be performed after modifiers are evaluated.

The simulation will be performed after modifiers are evaluated, iff
the collision shape is "Convex Hull" or "Mesh" and the source is set
to "Deform" or "Final".

Reviewers: sergey

Differential Revision: https://developer.blender.org/D8487
2020-08-10 10:54:28 +02:00
f15d33d585 Fix T79619: Sequencer window not updating whilst playing animation
Reverted Playhead optimizations for VSE. Needs more investigation to
detect which settings in the VSE would require a redraw of the area.
2020-08-10 09:28:18 +02:00
6640ed92c0 Fix T79346: VSE custom proxy file is broken
In last set of refactoring patches, code implementing this feature
has been accidentally removed.

Reviewed By: brecht

Differential Revision: https://developer.blender.org/D8449
2020-08-10 08:11:09 +02:00
00162e6b7b Fix T79246 GPUShader: compile error on AWS Elastic Graphics 2020-08-09 14:07:00 +02:00
a93be410c9 Fix previous commit, we first need to be sure we actually get a mesh... 2020-08-07 17:11:21 +02:00
8fa42f0bd4 Fix T79604: Switching to edit mode on boolean object runs out of memory.
Note that this is a dummy safe fix for now, far from optimal.
2020-08-07 17:07:30 +02:00
c2691c93d5 Fix T79201: Mantaflow: Fluid guides don't affect simulation.
This broke during the OpenVDB update for 2.90. Just making sure that guiding velocity files are being read correctly.
2020-08-07 15:33:51 +02:00
1b1129f82a Code Style: use "#pragma once" in intern/ghost
More information can be found in D8466.
2020-08-07 10:18:01 +02:00
91694b9b58 Code Style: use "#pragma once" in source directory
This replaces header include guards with `#pragma once`.
A couple of include guards are not removed yet (e.g. `__RNA_TYPES_H__`),
because they are used in other places.

This patch has been generated by P1561 followed by `make format`.

Differential Revision: https://developer.blender.org/D8466
2020-08-07 09:50:34 +02:00
58909abc68 EEVEE: Render: Fix regression caused by previous Motion blur fix
Caused by rB4f59e4bddcb0c06e441adf68a5f252a4e5b4b260
2020-08-07 00:59:14 +02:00
4f59e4bddc Fix T78452 EEVEE: Motion Blur: Crash when using camera switching
This was caused by the ViewLayer being freed with all its
engine data.
2020-08-06 23:06:18 +02:00
3dcaca93a0 Fix T78160 EEVEE: Motion Blur: Bug with Follow Path animation
Follow path seems to not be catched by `BKE_object_moves_in_time`.
For this reason, we cache all transforms for all object and check
ourselves if an animation occurs. This is almost what cycles does.

We also fix the rigid body case if the rigid body use deformation.
2020-08-06 23:06:18 +02:00
3d35012a05 GPencil: Fix unreported wrong Polyline bottom tooltip
It was mising the Wheelmouse option and the name of the tool was wrong.
2020-08-06 19:52:30 +02:00
8123b12006 Fix T79586: "rendering paused" not shown when viewport render starts paused 2020-08-06 19:19:22 +02:00
Vincent Blankfield
8fbfc150a0 Fix T77885: crash rendering grease pencil from compositor with multiple scenes 2020-08-06 19:19:22 +02:00
cc3cb52b23 Fix Pose Brush FK mode detecting wrong rotation origin
The Pose FK mode assings the rotation origin to the boundary of the last
visited face set in the floodfill operation. In some cases, the topology
of the model may make the flood fill operation to visit a face set as the
first one (assinging it to target) and visit it again as the last one
(assinging it to origin). This will make the pose brush to default the
origin and target to the brush location and not to the face sets as it
considers that there is only one possible boundary.
This adds a GSet to ensure that a particular face set is not visited
twice in the flood fill, fixing these cases.

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D7984
2020-08-06 19:14:39 +02:00
96e460ed9b GPencil: Patch old files after the change in how the first frame is used
This patching duplicates the first frame of the layer if the first frame number is not equals to the scene first frame number.

Related to T79567

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

Some minor cleanup in the patch.
2020-08-06 17:28:59 +02:00
bc8168f4a2 Cleanup: Remove bad level calls from space image
Groundwork for upcoming fix (D8472)
2020-08-06 16:44:03 +02:00
45e6ca3661 Cleanup: Stop accessing gpu_batch_presets_reset()
The current code is accessing this from outside the gpu "namespace". As
such it should be accessing GPU_ functions, not gpu_ functions.

This is also a place to centralize the XXX message that will be
addressed upon refactor. So we can reuse this call in other places that
need the same temporary workaround.

Groundwork for upcoming fix (D8472)
2020-08-06 16:44:03 +02:00
5e6119ddca Cycles: load OpenVDB file earlier in Blender export
In an upcoming bugfix we'll use OpenVDB data structures directly to build mesh
for sparse OpenVDB volumes, loading them OpenVDB grids earlier and removing any
references to Blender data structures makes that easier.

This also makes changes to Blender volumes to support this, so Cycles can take
ownership of a grid without Blender having to keep its own reference to it.
This should also be useful in a future Python API.

Ref D8401
2020-08-06 15:13:05 +02:00
77d71cc113 Move CDData debug print helper from DM to CustomData 'namespace'/files. 2020-08-06 15:03:49 +02:00
e4f400f0d6 Cleanup: undeclared warnings 2020-08-06 22:50:38 +10:00
ba20da7214 Cleanup: avoid debug-only includes for BLI_assert.h
Having includes in debug builds makes it possible to accidentally
break release builds.

Avoid this by moving calls to other modules out of BLI_assert.h
into BLI_assert.c
2020-08-06 22:49:28 +10:00
73a43c9d8a Fix buffer-overflow when drawing Curve Guide objects
Was passing an array of length 3 to `where_on_path()` that expected
length 4.
2020-08-06 14:14:55 +02:00
574bd866c8 Fix T78520 EEVEE: No viewport update when changing material nodetree
This was comming from rBd82c3d86155ea3c7831c7b5ef5d07bc8e2d99394
2020-08-06 13:37:41 +02:00
Red Mser
b313710c10 Fix padding when multi-editing aligned widgets
Similar to T58668, labels were not aligned when multi-editing widgets
that are not center-aligned.

Reviewed by: Hans Goudey, Julian Eisel

Differential Revision: https://developer.blender.org/D8441
2020-08-06 13:34:40 +02:00
769ec7ffe6 Fix T79408: ungroup operation update animation data incorrectly
Reviewers: sybren, sergey

Differential Revision: https://developer.blender.org/D8464
2020-08-06 12:50:08 +02:00
82150f5641 Workaround release builds failing
Issue caused by e9c4325515.
2020-08-06 19:19:25 +10:00
c872e87bd4 Fix T79309: Safe Areas are not visible 2020-08-06 18:59:20 +10:00
d4804f00fb Fix T79575: Crash loading nested set-scenes 2020-08-06 17:15:20 +10:00
Daniel Bailey
e9c4325515 Python: include Python stack trace in the crash log
This helps Python developers troubleshoot errors when
Python causes a crash.
2020-08-06 15:44:00 +10:00
c5b6b3d82f Fix T78698: Move cursor stuck after removing modifier
The panels are rebuilt when a modifier is removed so the button handlers need
to properly finish. By adding a context argument to the panel_delete function
this will happen properly.
2020-08-05 17:36:16 -04:00
59861db763 Fix T77517 EEVEE: Collection Holdout doesn't work in 2.90
The default material was missing its init code.
2020-08-05 22:29:21 +02:00
29ef7142dd EEVEE: Fix previous commit
Small mistake in rB5249a813f22f

Now for fix it real!
2020-08-05 22:18:26 +02:00
5249a813f2 Fix T78954 EEVEE: Motion Blur: Bug with hair particles on linked objects
The cache key for particle system was the original Object data. But this
is incorrect for particle systems as modifiers are not shared.
2020-08-05 22:12:53 +02:00
315ae005c3 Fix file name sometimes not visible immediately after renaming
Steps to reproduce were:
* Open File Browser
* Create a new directory
* Cancel renaming with Esc

File selection flags were modified during drawing when the rename button
got removed, but the file name label drawing wasn't checking the
modified state.
2020-08-05 21:57:51 +02:00
c606044157 Fix double-click not opening directories on some touch-pads
The file.execute() operator is the one that actually opened directories
and files, not file.select() with the "open" option, as it was assumed
when changing the keymap to double-click for opening. It only acts on
the current selection though, so we have to ensure the selection is set
on the first click.
Now, some touch-pads have a delay until they register a click event, so
the double-click would be registered instead, before the selection is
set. Always select on mouse-down now and remove the unnecessary select
operator call on double-click.
2020-08-05 21:34:00 +02:00
Deep Majumder
c323f3e90a Fix T77548: Crash when using Add Object Tool with Normal Orientation and zero objects in scene
The crash is caused by the fact that a NULL Object pointer is passed to
calculate the transform orientation, which has been set to normal.

A check has been include to detect the same.

Differential Revision: https://developer.blender.org/D7951
2020-08-05 15:45:46 -03:00
f3e724b93d Fix T79370 EEVEE: Texture paint does not update during stroke
Was caused by rBd82c3d86155e
2020-08-05 20:33:24 +02:00
1b593edf1d Fix T78907: Renaming file doesn't work while mouse is over file icon
The icons are label buttons. Usually these are not editable and can not
become active. These are draggable ones though (so dragging files can be
dragged by dragging the icon) which creates an exception to this rule.
So hovering the icon would activate its label and when executing the
rename operator via shortcut it wouldn't get exited properly. This broke
the invariant of only allowing a single active button at a time.
Added an assert to check that invariant now.

Letting the code to activate the text button ensure any currently active
button is exited seems sensible.
2020-08-05 19:40:40 +02:00
38e9a349de Workbench: Fix broken id pass 2020-08-05 19:37:41 +02:00
cf3431e0e8 Fix T79509 Workbench: Object color mode broken if more than 4096 objects
This was due to the new DRWShadingGroup not being saved and reused for
the next objects.
2020-08-05 19:37:41 +02:00
a316d3b6c6 Eevee: do not rely on the SOCK_HIDE_VALUE flag for node group sockets.
When disconnecting links for defaulted node group inputs, recurse
into the nested node group nodes, instead of checking the socket
flag. Otherwise the behavior is confusing and differs from Cycles.

Differential Revision: https://developer.blender.org/D8455
2020-08-05 20:20:48 +03:00
d84dce85f3 Fix T72297: disabled buttons toggling on drag
Disabled buttons would incorrectly toggle state when a drag toggle
passed over them. This adds a check to prevent a drag toggle on disabled
buttons.

Differential Revision: https://developer.blender.org/D8476
2020-08-05 10:42:43 -06:00
531a3f6c4e Sculpt: Use vertices instead of faces to limit the grids in each PBVH node
This uses the vertices per grid instead of quads to set the limit of
grids per PBVH Node. This should create more leaf nodes in lower
subdivisions levels where the duplicates count is high, producing more
uniform performance across different levels.

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D8454
2020-08-05 17:50:28 +02:00
adfde60831 Fix T79524: Button alignment broken in some cases
ad4928a171 disabled alignment for too many cases. Still try to avoid
aligning many items, to avoid thousands of redundant alignment
calculations. But now we're much more picky adding an sub-row with
alignment.
2020-08-05 16:56:24 +02:00
87062d4d67 Fix T78412: Ctrl+Spacebar does not maximize Python console on Windows
On windows, spacebar would be passed as UTF-8 text input, despite the
control key being pressed. On macOS, there already was an explicit
exception for this (command key in this case), on Linux XInput already
handled this case for us.
Note that Alt should still allow text input, for special character
sequences.

Issue also happened in the Text Editor if a text data-block was set.
2020-08-05 15:50:58 +02:00
ee351cb74d Fix T78869: denoising performance regression on Windows
Optimization was disabled in this function to work around a bug in MSVC, use
a different solution that does not come with such a big performance regression.
2020-08-05 15:46:44 +02:00
Jeroen Bakker
f7d38e2e64 Fix T77346: GPU Workaround Always Render Using Main Context
In Blender 2.90 EEVEE materials were refactored that introduced crashes on Intel
GPUs on Windows. The crash happened in the `local_context_workaround` that temporary
stored compiled materials in a binary form to reload it in the main GL context.

It has been tested that the workaround isn't needed anymore for HD6xx GPUs, but it
is still needed for HD4000.

After several unsuccesfull fixes we came to the conclusion that we could not support
the local context workaround and needed to come with a different workaround. The idea
of this patch is that in these cases there is only a single context that is used for
rendering. Threads that uses these contextes are guarded by a mutex and will block.

Impact on User Level:
* Due to main mutex lock the UI freezes when rendering or baking or feel less snappy

Reviewed By: Clément Foucault, Brecht van Lommel

Differential Revision: https://developer.blender.org/D8410
2020-08-05 15:45:42 +02:00
56d7e39b92 Fix T79520: Data Transfer modifier: crash/assert going into editmode on a source object. 2020-08-05 15:36:24 +02:00
c961bf8975 GPUTexture: Fix missing break 2020-08-05 14:17:34 +02:00
229fe01a15 PyDoc: use glClearColor before glClear in gpu docs
ref T79491

Maniphest Tasks: T79491

Differential Revision: https://developer.blender.org/D8471
2020-08-05 14:09:00 +02:00
f9aba4f6e1 Fix T79374: Render audio produces random clipping
Port of the bugfix from audaspace upstream.
2020-08-05 14:02:59 +02:00
396abbbfe7 Audaspace: port documentation bugfix from upstream. 2020-08-05 14:02:59 +02:00
f96afde3bd Fix T79544: No sound in video sequencer preview.
Directly caused by rB2bb73787791a, but actual issue was a pre-exiting
typo that never caused problems so far apparently...
2020-08-05 13:05:51 +02:00
02ccc37144 Correct recent fix for Cycles motion blur test
Need to only compare directory name, not the whole path.
2020-08-05 12:59:28 +02:00
c3113724ed Fix T78630: Custom icons not grayed out in inactive layouts
For regular icons this worked because they used the text color, which
was already grayed out by the caller.
2020-08-05 12:19:14 +02:00
de53178b26 Fix T78777: Cycles motion blur test differences between AVX/AVX2
This appears to be slight precision differences in the Embree implementation,
simply increase the diff threshold a little for these motion blur tests.
2020-08-05 11:45:31 +02:00
4a289081a5 Keymap: add back Shift-K to cut-through selected faces
Resolves T78540
2020-08-05 16:24:26 +10:00
fce71a255c EEVEE: LightCache: Add warning if the cache cannot be saved 2020-08-05 02:26:44 +02:00
6390b530d0 Fix T78529: Blend file corrupted during save caused by high Cubemap Size
This just avoid the corruption. A better fix still need to be finished.

See P1564
2020-08-05 02:26:44 +02:00
6be8b6af40 EEVEE: LightCache: Prevent crash when using a lightcache too big
Some implementation have different maximum texture size.
This patch avoid crash when texture allocation fails when:
- trying to bake a lightcache too big for the OpenGL imeplementaion.
- loading a cache from file that is too big for the OpenGL imeplementation.
2020-08-05 02:26:44 +02:00
d1b3da697d GPUTexture: Check PROXY textures for cubemap types
It can happen than some textures are not supported on some implementation
even if they fix the `GPU_max_texture_size` and `GPU_max_texture_layers`.
2020-08-05 02:26:44 +02:00
de947c5c50 GPUTexture: Improve debug print 2020-08-05 02:26:44 +02:00
3a522f7a7f Fix T79213 EEVEE: rendering with motion blur can change current frame
It seems to be expected that the render engine reset to the right CFRA
if it modifies it.
2020-08-05 02:26:44 +02:00
3ebe97c06b Fix T78665: Face Set visibility reverted when chaning Multires Levels
Face Sets where only set and updated on the PBVH after starting a sculpt
tool. In order to preserve the visibility they store when changing
levels, they need to be updated and sync also on PBVH creation

Reviewed By: sergey

Maniphest Tasks: T78665

Differential Revision: https://developer.blender.org/D8225
2020-08-04 23:33:51 +02:00
50e6d56e4e Fix missing duplicates in the subdiv_ccg neighbors function
Duplicates of a grid corner adjacent to an edge which are on the
adjacent grid of the same face were not added when requested.

Needed for D8356 to work, it may also fix some other bug in Multires.

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D8385
2020-08-04 23:21:12 +02:00
97be726f93 Fix T74796: Outliner child objects hidden when collections are filtered
Filtering Collections when also filtering object children would only
display the parent object. Add a check for the NO_CHILDREN filter before
creating the object-parent hierarchy.
2020-08-04 12:05:05 -06:00
e4370eccdf Fix T71488: Flipping Custom Split Normal Data leads to artifacts
The winding order of the faces changes when flipping the faces.
This lead to the loop indices changing as well.

Now we take this into account when restoring and flipping the custom
normals. Before the normals would be swapped.
2020-08-04 19:43:34 +02:00
e818f1274b Fix T78932: fix linking reroute nodes of different type
Reviewers: mano-wii

Differential Revision: https://developer.blender.org/D8460
2020-08-04 17:36:50 +02:00
a4a814a180 Fix buildbot macOS packaging error after recent changes 2020-08-04 17:13:09 +02:00
9538e38704 Fix buildbot packaging error after recent changes 2020-08-04 16:54:18 +02:00
16c7b6a7fe Buildbot: make code signing of packages optional with --codesign parameter
This is in preparation of doing builds per commit that will not be code signed.

Ref D8438

Differential Revision: https://developer.blender.org/D8451
2020-08-04 15:44:41 +02:00
e8483f9bd7 Fix T78758 Alembic export: crash when file is in use by another application
In cases when the output file cannot be opened, an exception is thrown that
was not caught.
2020-08-04 15:23:43 +02:00
e1a10b5e53 Fix T79234: Material preview does not update
When changing the material while the properties editor temporarily isn't
visible (e.g. because another editor is in full-screen or a different
workspace is active), the preview wouldn't be updated on changes.

Always trigger a material preview update on screen layout or editor type
changes.
2020-08-04 15:05:24 +02:00
701a9d3917 Cleanup: typos & co in UI messages (and some other places). 2020-08-04 13:26:58 +02:00
164f40c50a Fix T79503: Selecting edit-bones while holding Ctrl crashes 2020-08-04 21:23:53 +10:00
f921ae4665 Fix T79453: Motion Tracking: marker does not remember 'enabled' state
Caused by rB63ee3db96107.

While above commit corrected the clip offset, it also removed logic to
ensure a marker on a particular frame. This is needed though, otherwise
changes on a particular frame are applied to the marker being returned
by 'BKE_tracking_marker_get' which can be a completely different marker
if none exist for that frame yet.

This patch partly reverts rB63ee3db96107 and reintroduces the framenr
for the MarkerUpdateCb and uses that to ensure a marker on that frame.

Candidate for backporting to 2.83 LTS?

Reviewers: sergey, jacqueslucke

Subscribers:
2020-08-04 12:52:40 +02:00
a9e0aeaf65 Fix crash switching render slots when there is only one slot 2020-08-04 20:11:57 +10:00
8d3b8bc835 Depsgraph: Use UUID to identify pose channels
Fixes possible fiasco caused by re-allocation re-using pointers between
pose channels.

Differential Revision: https://developer.blender.org/D8453
2020-08-04 11:40:09 +02:00
52c2f296bc Pose channel: Add session UUID
Allows to identify pose channels more reliably than by the pointer.
2020-08-04 11:40:09 +02:00
51af20b856 Cycles: Fix nan in decomposed transform for degenerated input
The decomposed transform would have consists of nan values if the input
transform had zero scale.

Now the decomposition will check for zero scale, and if it is detected
then the result will be ensured to be finite. Additionally, rotation
value will be copied from previous/next time step to help avoiding
obscure interpolation.

The latter step can become more comprehensive than the current simple
implementation.

Differential Revision: https://developer.blender.org/D8450
2020-08-04 11:40:09 +02:00
7f6530e5bd Cycles: Add finite checks for float4 and transforms
Currently unused, preparing for an upcoming development.
2020-08-04 11:40:09 +02:00
9306037ed3 Node Groups: expose the SOCK_HIDE_VALUE flag for node group inputs.
This flag specifies that even when the socket is not connected,
the node should not display the input field for the constant input
value. This is useful for inputs like Normal, which have special
handling for the missing input case and don't use a constant value.
Currently there is no way to change this flag from Python, and
through UI it can only be done by re-creating the socket.

This patch exposes the flag through RNA and UI, makes sure it
is properly updated when changed, and adds special handling to
ensure that it is correctly set when creating a node group from
a node set that includes reroute nodes.

Differential Revision: https://developer.blender.org/D8395
2020-08-04 11:29:16 +03:00
b016e7f258 Node Groups: fix placement of the Output node when creating a group.
Instead of assuming the node width, compute the actual right bound.
Otherwise BSDF nodes tend to be too wide, and intersect Output.

Differential Revision: https://developer.blender.org/D8395
2020-08-04 11:29:01 +03:00
c1386795a9 Modifier: bypass mesh conversion for hooks in edit-mode
Avoid the conversion for the hook modifier as riggers
may need to enable this modifier in edit-mode.

The speedup of the modifier alone is significant since the hook
operation is simple compared to conversion that took over 99.9%
of the time in my tests, however the overall speedup was around to 1.6x.
2020-08-04 15:34:04 +10:00
9de5adc6a1 Fix: Remove debug print added mistakenly
This print whenever units are evaluated in number input was added in
45dbc38a8b mistakenly.
2020-08-03 18:28:39 -04:00
fbc3c1b24d Fix (unreported): Bevel tool custom profile uses wrong segments number
The bevel code initialized the CurveProfile with the "higher power of 2"
segments after the normal number of segments, leaving the widget in an
incorrect state after the calculation. A simple fix is to re-order the
initializations, doing the input number second.
2020-08-03 18:12:24 -04:00
e9bcf09819 Cleanup: Typos in comments. 2020-08-03 17:42:45 +02:00
473d9507fa Fix T78536: Crash calling object.modifier_apply on an empty
Caused by rB6add0cc88a0d.

Maniphest Tasks: T78536

Differential Revision: https://developer.blender.org/D8440
2020-08-03 16:20:46 +02:00
2bb7378779 Fix T79222: Assert due to multiple building of same scene sequencer in depsgraph.
Just check and skip building if it has already been done before.

Thanks to @sergey for the help.
2020-08-03 15:11:05 +02:00
3f94f47113 Fix T79482: Triangulate quads with 'Beauty' can make zero area faces 2020-08-03 21:12:46 +10:00
14b77b37cb Fix T78428: Checkbox labels don't highlight on mouse-over in popovers
The text colors set by the general widget state function
(`widget_state()`) would always be overriden by the menu-back text
colors to avoid contrast issues. This would only respect the selected
state, not other states.

Address this now by changing the input theme colors to use the menu-back
ones, rather than overriding after the fact (calling `widget_state()`).
2020-08-03 13:00:53 +02:00
33e6562a8a Fix T78266: Mantaflow: changing flow type (fire -> fire + smoke) resets Surface Emission
Changing the surface distance through the flow type is inappropriate here. It had been added to ensure that liquids / smoke use a different emission value.

Now the value will only be changed when changing from a gas to a liquid emitter or, vice-versa, when changing from a liquid to a gas emitter.
2020-08-03 12:38:07 +02:00
nutti
b5e3451540 Fix parameters in bpy.props.StringProperty Python API docs
There is a parameter typo in Python API document about bpy.props.StringProperty.
This patch fixes this.

Also this patch should apply to 2.90 branch as well.

Reviewed By: Grische, mont29

Differential Revision: https://developer.blender.org/D8430
2020-08-03 12:35:54 +02:00
057f3f2f3d Fix T78575: Assert when using UILayout.prop_tabs_enum
Code required the tabs to be placed in an aligned region. Code should
work fine even for unaligned regions though, so I don't see a reason to
forbid this.
2020-08-03 11:57:20 +02:00
fe70605a2a Building: Add ceres to Ninja's heavy jobs pool.
Some files in this library require more than 1.5Gb to build, so they
also belong to the 'heavy' pool.
2020-08-03 10:30:33 +02:00
8f22feefbc Fix T79440: Deform modifiers fail in edit-mode when not first
Lattice, armature & curve only worked when an edit-mesh was passed in,
the mesh argument was being ignored.

Regression in 9f5833798c
2020-08-03 18:01:34 +10:00
d406edf1ee Mesh: correct negative material indices when validating
Fixes corrupt mesh from T79451.
2020-08-03 14:48:16 +10:00
0264f53e30 Fix T67181: Only the first added movie strip will have audio below
Find free slot first for sound strips then for movie strips.
This patch also fixes issue where all strips were added to channel 2 by default.

Reviewed By: brecht

Differential Revision: https://developer.blender.org/D8339
2020-08-02 20:54:37 +02:00
189eb2550d Fix T66805: No cache invalidation with remove gaps operator
Reviewed By: brecht

Differential Revision: https://developer.blender.org/D8379
2020-08-02 20:53:55 +02:00
nutti
cc0d8ab036 Docs: add translation keyword argument default for msgctxt 2020-08-02 18:12:31 +10:00
nutti
c442ff0017 Fix RST syntax for freestyle documentation 2020-08-02 18:12:22 +10:00
f349a53c08 Fix T78691: Fly/Walk navigation ignores object channel locks 2020-08-02 18:10:07 +10:00
70d7805fa9 Cleanup: pass const matrices
Also order return matrices last.
2020-08-02 18:02:20 +10:00
ba6ed01058 Fix UV face dot refresh when toggling sync-select 2020-08-02 16:11:07 +10:00
72b422c1e1 UV: support select linked with sync-select in vert/edge modes 2020-08-01 12:15:05 +10:00
732434cb1f Fix T46568: UV select-linked failure to de-select
Shift-L was de-selecting all instead of the linked UV's.
2020-08-01 12:14:42 +10:00
71683be4bb Fix T78884: GPencil weight paint crash when painting over modifier generated points
The weight must be added only to the real points, not to the autogenerated points by modifiers. This affects, not only to subdivide, but to any modifier that generate points.
2020-07-31 18:20:39 +02:00
cab9673bed Fix T79111: Cycles: Image Sequence not rendering
Caused by c7aa0f9d74.

Since above commit, BKE_image_user_frame_calc requires an image (not
just the iuser) to get the framenumber.

Cycles used to call this with NULL image (in `image_user_file_path` and
`image_user_frame_number`), now pass the image as well.

Maniphest Tasks: T79111

Differential Revision: https://developer.blender.org/D8439
2020-07-31 16:39:22 +02:00
1ff1a2be9c Fix T78600: Crash when transforming doubles with mirroring on
`EDBM_verts_mirror_cache_begin_ex` could do a better work avoiding these
duplicates, but this is a separate problem.
2020-07-31 11:24:34 -03:00
feaed44ef6 GPencil: Fix unreported missing last point in NURBS conversion
The last point of the curve was missing.
2020-07-31 13:47:12 +02:00
c565f16afb Fix T79397: blurry icons at some UI scales, after recent refactor
Solution found by Yevgeny Makarov.
2020-07-31 12:39:58 +02:00
627b294317 Fix build error with clang 2020-07-31 12:03:11 +02:00
de68880e79 Cleanup: Remove unused original pointer in Sequence 2020-07-31 11:45:40 +02:00
1e7afea2bb Fix T78835: Ghosting audio after using undo
The root of the issue comes to the fact that sub-data pointers were
used to match strips before/after copy-on-write. The undo system might
re-use sub-data pointers after re-allocating them, making it so that,
for example, pointer used by sound strip is later re-used by video
strip.

This fix takes an advantage of recently introduced per-sequence UUID
and uses it to match sequences before/after copy-on-write.
2020-07-31 11:45:40 +02:00
9ea6228b07 Sequencer: Ensure UUIDs are updated when needed
Document cases where it seems that they need to be updated, but where
the proper behavior is to not update the UUID.
2020-07-31 11:45:40 +02:00
eb8cbb6d7c Depsgraph: Add command line argument to debug UUIDs
Will trigger code paths which makes sure UUIDs are generated and
are unique.

Enabled with --debug-depsgraph-uuid (which is also implied by
--debug-depsgraph).
2020-07-31 11:45:00 +02:00
cc63897c22 Sequencer: Add session UUID check function
Is aimed for use during development and debug, to help
verifying that operations do not leave sequences with
invalid UUIDs.
2020-07-31 11:44:59 +02:00
3305a94fa1 Sequencer: Add session UUID management to Sequence
This is the first step for having sequences covered with session UUID
with the goal to remove code which uses original sequence pointer to
match sequences.

Currently this UUID is maintained on file load, allocation and leaf
duplication function.There are more cases to cover and ensure UUID
is re-generated or re-used when needed. It will be done as follow-up
development.
2020-07-31 11:44:59 +02:00
44b32e0e4a Sequencer: Add session UUID field to the Sequence DNA 2020-07-31 11:29:55 +02:00
de21ddf821 BLI: Initial implementation of generic session UUID API
Covers basics like generation of new UUID.

Also contains code needed to use the SessionUUID as a key in the Map.
2020-07-31 11:24:03 +02:00
cd579c4996 Add generic session UUID structures to DNA
Allows to use pre-defined structure for session UUIDs in all data
structures which needs it: pose channels, sequencer strips, modifiers.

The goal of all this is to have a reliable way of matching original
and copy-on-written versions of data, so that it's possible to
preserve runtime caches.
2020-07-31 11:22:05 +02:00
298d5eb669 UI: fix buttons ranges not properly updated when tabbing to next/previous
button

This was reported for the FCurve modifier restrict ranges, but might fail
elsewhere, too. Reason is that the post_but has its range (hardmin/
hardmax etc) set before the updates to the active button take place, so
changes here dont end up on the post_but (even though the RNA range
function is properly called for a new defined button - new one is not the
same as the post_but though).

Now update the ranges on the post_but when that gets active.

Fixes T78763

Maniphest Tasks: T78763

Differential Revision: https://developer.blender.org/D8265
2020-07-31 10:47:52 +02:00
8c375113b5 UI: Label Fixes
These were added in rB146473f08335e8cb774ccaf1baad82a1d308fbe1 however 
there were a few errors with the labels:

1. Underscore in label
2. Abbreviation when not needed
2020-07-30 22:05:25 -04:00
df8bbe98c4 Merge commit 'e12c08e8d170b7ca40f204a5b0423c23a9fbc2c1' into gsoc-2018-many-light-sampling 2020-06-02 09:46:53 -06:00
76870830e2 Merge commit 'e12c08e8d170b7ca40f204a5b0423c23a9fbc2c1^1' into gsoc-2018-many-light-sampling 2020-06-02 09:30:44 -06:00
Erik Englesson
8d399eef67 Merge branch 'master' into gsoc-2018-many-light-sampling 2018-08-12 12:30:42 +02:00
Erik Englesson
4608e5ac26 Cycles: light_tree_pdf() now accounts for splitting
For the MIS calculations we need to be able to calculate the
probability to sample a light using the light tree. This
did not account for splitting so if splitting was used the
probability would be wrong. This has now been fixed.

Also, if we are in PATH mode then the splitting threshold is
set to zero.
2018-08-12 11:59:33 +02:00
Erik Englesson
2d839a08e4 Cycles: Removed unused code
The simplified GGX code is no longer needed
with the new splitting heuristic and has
now been removed.
2018-08-12 11:57:16 +02:00
Erik Englesson
0216daeb80 Cycles: Removed energy term in SAOH denominator
This should not affect the result since it is just a
scale factor to all terms in the minimization. This
term was not in eq. 2 of the paper.
2018-08-10 17:34:23 +02:00
Erik Englesson
8e36d94be2 Cycles: Added more comments and renamed variables
More code comments have been added to all code related to
the light tree. I also renamed all uses of "light BVH" to
use light tree instead to keep everything consistent.
Functions and variable names that used the camel case
naming convention has been changed to follow Blender's
code style. Also, unneeded includes were removed.
2018-08-10 17:31:33 +02:00
Erik Englesson
94af4326e3 Cycles: Light tree: volume and MIS fixes
Now using a more reliable way of knowing if a shading point
is inside or on the boundary of a volume.

Fixed a bug in light_background_sample() that used an index
into the lights array as an index into the distribution array.
2018-08-03 17:24:32 +02:00
Erik Englesson
f6305047f4 Cycles: Light tree split heuristic fix
Doubles are no longer needed in the split heuristic
calculations.
2018-08-03 17:24:32 +02:00
Erik Englesson
e8e0669785 Cycles: Picking position and normal fix for MIS
The picking position and normal has been changed to
use the position and normal from the last
non-transparent bounce.
2018-08-03 17:22:23 +02:00
Erik Englesson
5d344f4360 Cycles: Light tree: Area light fix
The bounding boxes for area lights was not calculated
properly but has now been fixed.
2018-08-03 17:22:23 +02:00
Erik Englesson
108594d8c8 Cycles: Light tree: energy and light picking fixes
When calculating the energy for a light source I previously returned
zero energy if is_constant_emission() returned false. This has now
been changed so it uses an emission of (1,1,1) instead.

The normal that is used for light picking for the BSDF approximation
in the importance calculations now takes into consideration if the
point is on glass, a reflective or transmissive surface. The position
and normal used for light picking is now stored in the ShaderData struct.
2018-08-03 17:22:23 +02:00
Erik Englesson
84fec21522 Cycles: Light tree instancing and energy fixes
Instancing for mesh lights now works with the light tree.
The conversion from emission to luminance is now using
linear_rgb_to_gray() instead.
2018-08-03 17:21:40 +02:00
b1837d5f69 Fix CUDA build for many light sampling, still fails when running. 2018-07-30 12:03:17 +02:00
b3c375e37e Fix missing viewport update when toggling light BVH option. 2018-07-30 12:03:17 +02:00
Erik Englesson
1fe1e742a6 Cycles: WIP: Volumes are not using the light tree.
If a shading point is inside or on the boundary of a volume
then it will use the old sampling method until we have
properly implemented the volume parts of the paper.

Also, the PDF picking probability for volumes have now been
updated properly since the PDF restructure.

Tried to provide the correct shading point and normal to
the MIS calculations. This part needs more work.
2018-07-27 18:19:09 +02:00
Erik Englesson
6045fc40f4 Cycles: Light tree optimization
The importance metric calculations now uses the
fast_cos() and fast_acos() functions. This gave
a 1.4x speedup on one of the test scenes.
2018-07-27 18:18:59 +02:00
Erik Englesson
00b90ede1b Cycles: Light tree related bug fixes
- Fixed bug in triangle_light_pdf_area() from PDF refactor
- Early exit if picking prob is zero in tree traversal
- The background index is now an index into the lights
  array instead of the distribution array.
2018-07-27 18:16:21 +02:00
Erik Englesson
beef4874d3 Cycles: Several emitters per leaf in light tree
This commits makes it possible for leaf nodes in the
light tree to have more than one emitter. This reduces
the maximum depth of the light tree which makes the
traversal of the tree faster but the sampling code
is slower if a leaf with several emitters is found.

Technical details:
-- If cost of splitting in build is larger than the
   energy of the node then we create a leaf instead
   of splitting.
-- Maximum emitters per leaf is now set to 64
-- During traversal if a leaf with several emitters
   is found then we sample one of these based on
   the importance of each emitter. This is calculated
   on the fly and therefore a CDF generation without
   dynamic allocation was implemented.
-  Moved the distribution id to node id lookup from
   light_distribution_pdf() to light_bvh_pdf().
2018-07-20 14:29:20 +02:00
Erik Englesson
ff4833a6b3 Cycles: Bug fixes
- Stopping recursive tree traversal if
  negative PDFs are encountered.

- accum_light_tree_contribution() now
  takes a scale factor as input which
  is passed through to accum_light_contribution

- light_bvh_sample now changes randu. This
  is similar to the other *_sample functions.
  This fixed a bug where recursive traversal
  with no splitting gave a different result
  compared to just using light_sample.
2018-07-13 13:47:18 +02:00
Erik Englesson
0db119ad11 Cycles: Updated SAOH with regularization factor
This factor penalizes thin bounding boxes in
the tree construction.
2018-07-13 13:44:25 +02:00
Erik Englesson
8a0ff32abd Cycles: Updated cone aggregation and measure
The paper provided a way to merge two bounding
cones and an updated version of the cone
measure.
2018-07-13 13:41:00 +02:00
Erik Englesson
260b2e9e20 Cycles: Updated the importance metric
The new paper provides an updated importance
metric which is now implemented. Also, a bug
in the index used for background lights in
the MIS calculations have been fixed.

Technical details:
- The new importance metric depends on the
  normal at the shading point. The MIS
  calculations have been changed
  accordingly.

- The uncertainty angle was described in
  the paper and is now implemented.

- In the implementation details section
  of the paper they mention that they
  limit the distance if splitting is
  disabled. This is implemented too.
2018-07-13 13:33:01 +02:00
Erik Englesson
2d6e1ddb94 Cycles: Updated rescaling of random numbers
The new paper describes how they rescale the
random numbers in the tree traversal. We do
the same now.
2018-07-13 13:30:09 +02:00
Erik Englesson
c6bd7a974b Cycles: Updated the split heuristic
The split heuristic is now based on the
new paper instead of the abstract/slides
from 2017.
2018-07-13 13:21:32 +02:00
Erik Englesson
36cfc9e9fd Cycles: First iteration on split traversal
This makes it possible to sample and evaluate several
lights in a single tree traversal. Should sample highly
specular lights better too. Can only be used in branched
path tracing.

This commit contains the following:
* GUI for setting the splitting threshold
* Recursive split traversal
  - Split method based on solid angle and BSDF peak
  - At leafs the path radiance is accumulated to L
  - Have created a simplified GGX eval that is not
    currently being used.
* Refactor of common code

This is in development.
2018-07-06 09:20:41 +02:00
Erik Englesson
5c10bd4f19 Fix: Merge conflict with background resolution 2018-06-29 16:25:28 +02:00
Erik Englesson
136991be1a Merge branch 'master' into gsoc-2018-many-light-sampling 2018-06-29 11:02:18 +02:00
Erik Englesson
2cdb08f4ff Cycles: Background lights works with light tree
Added support for background lights when using the
light tree, fixed minor bugs related to disabled lights
and trying to build the light tree without any lights.

Now samples either the light tree, distant lights or
background lights based on their energy.
2018-06-29 10:34:15 +02:00
Erik Englesson
8b24cf8c83 Cycles: Support for distant lights
Distant lights are not put in the light
BVH and are treated as a special case.
Either we sample a light from the BVH
or choose one of the distant lights.
2018-06-22 08:07:00 +02:00
Erik Englesson
118731d7d4 Cycles: New design for PDF computations
Now there are functions to calculate the picking
probability for a given lamp/triangle. Fixed
a bug that lamps was before triangles in the
distribution array.
2018-06-22 08:05:52 +02:00
Erik Englesson
2f85295be6 Cleanup: Replaced space tabs with real tabs 2018-06-21 11:33:01 +02:00
Erik Englesson
6f2d885f7e Cleanup: Refactored code
Merged the device_update_tree_distribution()
and device_update_distribution() functions.
2018-06-15 12:27:32 +02:00
Erik Englesson
df7f4d1e26 Cycles: Area lights support and better energy estimation
The light BVH now supports area lights. Also, the total emitted
energy of each light is now calculated by integrating luminance
over the sphere of directions and area.
2018-06-15 12:21:38 +02:00
Erik Englesson
0028f7cf0f Merge branch 'master' into gsoc-2018-many-light-sampling 2018-06-08 15:21:09 +02:00
Erik Englesson
a3826bf41c Cycles: Added a checkbox in Blender's GUI to turn
on/off the new light sampling method.

The checkbox can be found at the bottom of the sampling
section in the Render tab. The method is not used by
default.
2018-06-08 13:51:49 +02:00
Erik Englesson
4f55918a5e Cycles: Implemented the SAOH splitting method
Previously the light bvh construction code used a simple method
to split a node into two child nodes based only on the bounding
box of the node. Now, it splits a node into two by looking at
the energy and direction of the lights inside the nodes and the
area of their bounding boxes.

Technical changes:
- Implemented orientation BVH build
- Added energy to each node based on emission
- Updated PDF calculations for lights when using the light BVH
2018-06-08 13:51:43 +02:00
Erik Englesson
bcb89f0b96 Fix: Minimal compilation error fix 2018-06-04 11:10:08 +02:00
Erik Englesson
2e17c3f3a1 Added simple construction and sampling code for light BVH
The light BVH is created on the host out of all emissive
triangles and all lights in the scene. The nodes of the
light BVH is then copied to the device where it is used
when sampling lights. When a light is asked for it
traverses the light BVH randomly to find a single light.

This is still very much in development.
2018-06-01 16:12:17 +02:00
Erik Englesson
e558b1afef My own build path modifications 2018-06-01 16:04:28 +02:00
Hristo Gueorguiev
458104ee0b Fix OpenCL compilation error - BPT without SSS. 2018-06-01 15:41:19 +02:00
3342114d31 make.bat: fix support for building with just the build tools installed. 2018-06-01 15:41:19 +02:00
ed1b92cf8e Fix missing movieclip ID type in allowed ones for Outliner... 2018-06-01 15:41:19 +02:00
59e40721dd Edit Mode: add success return value
Also remove fix for T6614, since BKE_object_obdata_is_libdata
no longer checks proxy.
2018-06-01 15:41:19 +02:00
8e462b0f48 DNA: add OB_DATA_SUPPORT_EDITMODE macro 2018-06-01 15:41:19 +02:00
68b0cf6da0 Cleanup: Get rid of G.main in BKE_material.
Note that in some cases, this only moves the G.main case to somne other
places - in particular, RNA getters/setters are becoming annoying here...
2018-06-01 15:41:19 +02:00
55e96e1955 make.bat : fix release build not getting the right settings. 2018-06-01 15:41:19 +02:00
c5803441f3 make.bat : improve error message while detecting the msvc runtime when using clang 2018-06-01 15:41:19 +02:00
3e3a44dcfa make.bat : add support for building with ninja and clang together. 2018-06-01 15:41:19 +02:00
ac4eec8cb9 Windows: Add support for building with clang.
This commit contains the minimum to make clang build/work with blender, asan and ninja build support is forthcoming

Things to note:

1) Builds and runs, and is able to pass all tests (except for the freestyle_stroke_material.blend test which was broken at that time for all platforms by the looks of it)

2) It's slightly faster than msvc when using cycles. (time in seconds, on an i7-3370)

victor_cpu
	msvc:3099.51
	clang:2796.43

pavillon_barcelona_cpu
	msvc:1872.05
	clang:1827.72

koro_cpu
	msvc:1097.58
	clang:1006.51

fishy_cat_cpu
	msvc:815.37
	clang:722.2

classroom_cpu
	msvc:1705.39
	clang:1575.43

bmw27_cpu
	msvc:552.38
	clang:561.53

barbershop_interior_cpu
	msvc:2134.93
	clang:1922.33

3) clang on windows uses a drop in replacement for the Microsoft cl.exe (takes some of the Microsoft parameters, but not all, and takes some of the clang parameters but not all) and uses ms headers + libraries + linker, so you still need visual studio installed and will use our existing vc14 svn libs.

4) X64 only currently, X86 builds but crashes on startup.

5) Tested with llvm/clang 6.0.0

6) Requires visual studio integration, available at https://github.com/LazyDodo/llvm-vs2017-integration

7) The Microsoft compiler spawns a few copies of cl in parallel to get faster build times, clang doesn't, so the build time is 3-4x slower than with msvc.

8) No openmp support yet. Have not looked at this much, the binary distribution of clang doesn't seem to include it on windows.

9) No ASAN support yet, some of the sanitizers can be made to work, but it was decided to leave support out of this commit.

Reviewers: campbellbarton

Differential Revision: https://developer.blender.org/D3304
2018-06-01 15:41:19 +02:00
dac0b7c0eb Fix Cycles + OSL build error, pass main to node editing functions. 2018-06-01 15:41:19 +02:00
8b5f8c0131 UI: center align number buttons w/o text
This makes supporting split properties and text possible, see T54951
2018-06-01 15:41:18 +02:00
81406e39f8 Cleanup: Nuke most of remaining evil G.main from RNA.
The few ones in getters/setters we cannot remove as easily, for now we
can live with those I think...
2018-06-01 15:41:18 +02:00
88fa8610ef Cycles: Fix problems in the IES loader when rendering with no file selected 2018-06-01 15:41:18 +02:00
6c68384c76 UI: replace BLI_strncpy w/ memcpy
Size is already checked.
2018-06-01 15:41:18 +02:00
5522d28d54 Recently added IES conflicts w/ EEVEE 2018-06-01 15:41:18 +02:00
cbeb9f2c35 3D View: minor change to NDOF view orbit
This change is needed for 2.8, where the NULL check isn't a reliable way
of testing if dynamic offset is needed.
2018-06-01 15:41:18 +02:00
975194b237 Fix restrict error in BLI_str_format_byte_unit
Don't use sprintf to append a string to it's self.

Also correct BLI_str_rstrip_float_zero's return value.
2018-06-01 15:41:18 +02:00
8519814b89 make.bat : Fix builtime.txt being written in the wrong folder. 2018-06-01 15:41:18 +02:00
0347abee2d Cycles: Add Support for IES files as textures for light strength
This patch adds support for IES files, a file format that is commonly used to store the directional intensity distribution of light sources.
The new IES node is supposed to be plugged into the Strength input of the Emission node of the lamp.

Since people generating IES files do not really seem to care about the standard, the parser is flexible enough to accept all test files I have tried.
Some common weirdnesses are distributing values over multiple lines that should go into one line, using commas instead of spaces as delimiters and adding various useless stuff at the end of the file.

The user interface of the node is similar to the script node, the user can either select an internal Text or load a file.
Internally, IES files are handled similar to Image textures: They are stored in slots by the LightManager and each unique IES is assigned to one slot.

The local coordinate system of the lamp is used, so that the direction of the light can be changed. For UI reasons, it's usually best to add an area light,
rotate it and then change its type, since especially the point light does not immediately show its local coordinate system in the viewport.

Reviewers: #cycles, dingto, sergey, brecht

Reviewed By: #cycles, dingto, brecht

Subscribers: OgDEV, crazyrobinhood, secundar, cardboard, pisuke, intrah, swerner, micah_denn, harvester, gottfried, disnel, campbellbarton, duarteframos, Lapineige, brecht, juicyfruit, dingto, marek, rickyblender, bliblubli, lockal, sergey

Differential Revision: https://developer.blender.org/D1543
2018-06-01 15:41:18 +02:00
2b21ce73e9 Threads: add spinlock hit for hyperthreading processors on Windows.
Suggested by Percy Ross Tiglao.
2018-06-01 15:41:18 +02:00
c0e1765c3a make.bat: Add support for building with ninja.
ninja is an alternative to msbuild designed for fast rebuilds. However there is no IDE support, builds only from the command line.

Comparison between msbuild and ninja for a full build, build time in seconds.

Full Clean Build
msbuild     867.5
Ninja       801.2
Difference  -66.3 (-7.6%)

Minor Change
msbuild      43.0
Ninja        14.9
Difference  -28.1 (-64.4%)

No Changes
msbuild      23.0
Ninja         6.1
Difference  -16.9 (-73.5%)
2018-06-01 15:41:18 +02:00
68fce6c4db make.bat/cleanup: removed stray echo in make.bat 2018-06-01 15:41:17 +02:00
a554255c13 make.bat: refactor make.bat
make.bat was starting to become hard to maintain, this refactors it into separate batch files for each stage of the process.

-Improved detection of msvc2013/2015
-Improved failure handling.
-Added check for working msbuild and C++ compiler
-Added verbose switch to ease trouble shooting.
-Added Check if svn/cmake/git are in the path before using them
-Display the build configuration before asking to download the libraries
-Offer an option to recover an interrupted checkout of the libraries.
-Automatically check out sub-modules in-case they are missing.
2018-06-01 15:41:17 +02:00
ea1322c64d Add number and memory size formatting throughout the UI
This commit adds number formatting (thousands separator) to the baking panel. It also adds a new function to format memory sizes (KB/GB/etc) and applies it to the baking panel and scene stats. The new function is unit tested.

Reviewers: Severin
Tags: #user_interface
Differential Revision: https://developer.blender.org/D1248
2018-06-01 15:41:17 +02:00
f383c3ce78 [windows/make.bat] use a more reliable way of locating visual studio 2017.
The registry hack we were using wasn't very reliable, the recommended way to locating visual studio is using vswhere (15.2 and up), using it also allows to switch between the regular and pre-release versions.
2018-06-01 15:41:17 +02:00
1d5ecd2a6b WM: check modal handlers for keymap lookups
Keep in sync with 2.8x
2018-06-01 15:41:17 +02:00
f60520306c UI: fix assert
Replace hard-coded button size check with UI_UNIT_X.

Caused icon-only buttons to have strings assigned based on UI-scale.
2018-06-01 15:41:17 +02:00
efb19da3e8 3D View: add pixelsize function w/o UI scale 2018-06-01 15:41:17 +02:00
49496a460a Cycles: Cleanup: Remove duplicated atan2f definition for OpenCL
Turns out that atan2f was already defined for OpenCL.
2018-06-01 15:41:17 +02:00
2f12b83dbd Cycles Denoising: Don't use atomics in the accumulation kernel on CPUs
The GPU kernel needs to use atomics for accumulation since all offsets are processed in
parallel, but on CPUs that's not the case, so we can disable them there for a considerable speedup.
2018-06-01 15:41:17 +02:00
fef9647db4 Cycles/Compositor: Add arctan2 operation to the Math node
The Math node currently has the normal atan() function, but for
actual angles this is fairly useless without additional nodes to handle the signs.

Since the node has two inputs anyways, it only makes sense to add an arctan2 option.

Reviewers: sergey, brecht

Differential Revision: https://developer.blender.org/D3430
2018-06-01 15:41:17 +02:00
f914311949 3D View: remove poll 3D view for copy/paste
These operators only need selected objects.
2018-06-01 15:41:17 +02:00
d9b2b82532 Fix T55034: Setting duplication group for multiple selected items only
affects one item

UI editing multiple selected items missed the case of PROP_POINTER
properties

Reviewed By: campbellbarton

Differential Revision: https://developer.blender.org/D3373
2018-06-01 15:41:17 +02:00
584cb4a9b0 Fix Extend property of Lasso select tool in Mask editor not working
Reviewed By: campbellbarton

Differential Revision: https://developer.blender.org/D3361
2018-06-01 15:41:16 +02:00
9bccf1425d Fix T54336: Extend property of Lasso select tool in Node editor does not
work

Reviewed By: campbellbarton

Differential Revision: https://developer.blender.org/D3360
2018-06-01 15:41:16 +02:00
4e7eef02bc Fix T55137: Compilation failing on non-x86-64 architectures
Some conversion helper functions were (most likely by accident) contained
inside an ifdef for SSE2 support, so on e.g. ARM they would be undefined
and therefore cause compilation to fail.
2018-06-01 15:41:16 +02:00
3633221c9b Cleanup: strip trailing space from interface files 2018-06-01 15:41:16 +02:00
db14acaf22 Fix incorrect size in aligned lockfree realloc
Thanks to @alikendarfen for finding.
2018-06-01 15:41:16 +02:00
95d786f598 blenderplayer: add stubs 2018-06-01 15:41:16 +02:00
cd502b8287 Fix T55093: Bisect + fill crash 2018-06-01 15:41:16 +02:00
72d9ca0db6 Memory allocator: use lockfree calls internally
Was already used in some areas.
2018-06-01 15:41:16 +02:00
deb5c1dae9 CMake: only include licences for enabled libs 2018-06-01 15:41:16 +02:00
ed657c9407 Cleanup: use const for transform internal API 2018-06-01 15:41:16 +02:00
72ba23c7fc RNA: support for PARM_OUTPUT & PARM_RNAPTR 2018-06-01 15:41:16 +02:00
96b252d90e Fix too much memory usage for Cycles attribute map.
Thanks to Thomas Krebs for identifying the problem and solution.
2018-06-01 15:41:15 +02:00
922b77f142 Fix/workaround RNA build error in C++ API.
It seems output parameter needs to be the last one.
2018-06-01 15:41:15 +02:00
f024ae11e3 Cleanup: correct variable names 2018-06-01 15:41:15 +02:00
d01207e4fb WM: utility to find a keymap item from an operator
Also RNA access to WM_keyconfig_update,
needed when generating dynamic keymaps used in menus immediately after.
2018-06-01 15:41:15 +02:00
1832 changed files with 10553 additions and 9019 deletions

View File

@@ -236,6 +236,7 @@ ForEachMacros:
- LISTBASE_CIRCULAR_FORWARD_BEGIN
- LISTBASE_FOREACH
- LISTBASE_FOREACH_BACKWARD
- LISTBASE_FOREACH_INDEX
- LISTBASE_FOREACH_MUTABLE
- LISTBASE_FOREACH_BACKWARD_MUTABLE
- MAN_ITER_AXES_BEGIN

View File

@@ -428,6 +428,10 @@ if(WIN32)
option(WITH_TBB_MALLOC_PROXY "Enable the TBB malloc replacement" ON)
endif()
# This should be turned off when Blender enter beta/rc/release
option(WITH_EXPERIMENTAL_FEATURES "Enable experimental features (still need to enable them in the user preferences)" OFF)
mark_as_advanced(WITH_EXPERIMENTAL_FEATURES)
# Unit testsing
option(WITH_GTESTS "Enable GTest unit testing" OFF)
option(WITH_OPENGL_RENDER_TESTS "Enable OpenGL render related unit testing (Experimental)" OFF)

View File

@@ -72,7 +72,7 @@ COMMANDLINE=$@
DISTRO=""
RPM=""
SRC="$HOME/src/blender-deps"
SRC="$HOME/Programming/blender-git/src/blender-deps"
INST="/opt/lib"
TMP="/tmp"
CWD=$PWD

View File

@@ -33,10 +33,11 @@ def is_tool(name):
return which(name) is not None
class Builder:
def __init__(self, name, branch):
def __init__(self, name, branch, codesign):
self.name = name
self.branch = branch
self.is_release_branch = re.match("^blender-v(.*)-release$", branch) is not None
self.codesign = codesign
# Buildbot runs from build/ directory
self.blender_dir = os.path.abspath(os.path.join('..', 'blender.git'))
@@ -67,8 +68,9 @@ def create_builder_from_arguments():
parser = argparse.ArgumentParser()
parser.add_argument('builder_name')
parser.add_argument('branch', default='master', nargs='?')
parser.add_argument("--codesign", action="store_true")
args = parser.parse_args()
return Builder(args.builder_name, args.branch)
return Builder(args.builder_name, args.branch, args.codesign)
class VersionInfo:

View File

@@ -82,6 +82,10 @@ def create_argument_parser():
type=Path,
help="Optional path to applescript to set up folder looks of DMG."
"If not provided default Blender's one is used.")
parser.add_argument(
'--codesign',
action="store_true",
help="Code sign and notarize DMG contents.")
return parser
@@ -395,7 +399,8 @@ def create_final_dmg(app_bundles: List[Path],
dmg_filepath: Path,
background_image_filepath: Path,
volume_name: str,
applescript: Path) -> None:
applescript: Path,
codesign: bool) -> None:
"""
Create DMG with all app bundles
@@ -421,7 +426,8 @@ def create_final_dmg(app_bundles: List[Path],
#
# This allows to recurs into the content of bundles without worrying about
# possible interfereice of Application symlink.
codesign_app_bundles_in_dmg(mount_directory)
if codesign:
codesign_app_bundles_in_dmg(mount_directory)
copy_background_if_needed(background_image_filepath, mount_directory)
create_applications_link(mount_directory)
@@ -434,7 +440,8 @@ def create_final_dmg(app_bundles: List[Path],
compress_dmg(writable_dmg_filepath, dmg_filepath)
writable_dmg_filepath.unlink()
codesign_and_notarize_dmg(dmg_filepath)
if codesign:
codesign_and_notarize_dmg(dmg_filepath)
def ensure_dmg_extension(filepath: Path) -> Path:
@@ -521,6 +528,7 @@ def main():
source_dir = args.source_dir.absolute()
background_image_filepath = get_background_image(args.background_image)
applescript = get_applescript(args.applescript)
codesign = args.codesign
app_bundles = collect_and_log_app_bundles(source_dir)
if not app_bundles:
@@ -535,7 +543,8 @@ def main():
dmg_filepath,
background_image_filepath,
volume_name,
applescript)
applescript,
codesign)
if __name__ == "__main__":

View File

@@ -24,7 +24,7 @@ import shutil
import buildbot_utils
def get_cmake_options(builder):
post_install_script = os.path.join(
codesign_script = os.path.join(
builder.blender_dir, 'build_files', 'buildbot', 'worker_codesign.cmake')
config_file = "build_files/cmake/config/blender_release.cmake"
@@ -36,7 +36,8 @@ def get_cmake_options(builder):
options.append('-DCMAKE_OSX_DEPLOYMENT_TARGET=10.9')
elif builder.platform == 'win':
options.extend(['-G', 'Visual Studio 16 2019', '-A', 'x64'])
options.extend(['-DPOSTINSTALL_SCRIPT:PATH=' + post_install_script])
if builder.codesign:
options.extend(['-DPOSTINSTALL_SCRIPT:PATH=' + codesign_script])
elif builder.platform == 'linux':
config_file = "build_files/buildbot/config/blender_linux.cmake"

View File

@@ -117,6 +117,8 @@ def pack_mac(builder):
if info.is_development_build:
background_image = os.path.join(release_dir, 'buildbot', 'background.tif')
command += ['--background-image', background_image]
if builder.codesign:
command += ['--codesign']
command += [builder.install_dir]
buildbot_utils.call(command)
@@ -150,7 +152,8 @@ def pack_win(builder):
package_filename = package_name + '.msi'
package_filepath = os.path.join(builder.build_dir, package_filename)
sign_file_or_directory(package_filepath)
if builder.codesign:
sign_file_or_directory(package_filepath)
package_files += [(package_filepath, package_filename)]

View File

@@ -12,12 +12,8 @@
#=============================================================================
# Copyright 2016 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If ALEMBIC_ROOT_DIR was defined in the environment, use it.

View File

@@ -11,6 +11,13 @@
# AUDASPACE_PY_INCLUDE_DIRS - the audaspace's python binding include directories
# AUDASPACE_PY_LIBRARIES - link these to use audaspace's python binding
#=============================================================================
# Copyright 2014 Blender Foundation.
#
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
IF(NOT AUDASPACE_ROOT_DIR AND NOT $ENV{AUDASPACE_ROOT_DIR} STREQUAL "")
SET(AUDASPACE_ROOT_DIR $ENV{AUDASPACE_ROOT_DIR})
ENDIF()

View File

@@ -14,12 +14,8 @@
#=============================================================================
# Copyright 2018 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If BLOSC_ROOT_DIR was defined in the environment, use it.

View File

@@ -17,12 +17,8 @@
#=============================================================================
# Copyright 2020 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If CLANG_TIDY_ROOT_DIR was defined in the environment, use it.

View File

@@ -10,12 +10,8 @@
#=============================================================================
# Copyright 2015 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If EIGEN3_ROOT_DIR was defined in the environment, use it.

View File

@@ -14,12 +14,8 @@
#=============================================================================
# Copyright 2018 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If EMBREE_ROOT_DIR was defined in the environment, use it.

View File

@@ -14,12 +14,8 @@
#=============================================================================
# Copyright 2011 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If FFTW3_ROOT_DIR was defined in the environment, use it.

View File

@@ -13,12 +13,8 @@
#=============================================================================
# Copyright 2014 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If GLEW_ROOT_DIR was defined in the environment, use it.

View File

@@ -12,12 +12,8 @@
#=============================================================================
# Copyright 2012 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If ICU_ROOT_DIR was defined in the environment, use it.

View File

@@ -14,12 +14,8 @@
#=============================================================================
# Copyright 2011 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If JACK_ROOT_DIR was defined in the environment, use it.

View File

@@ -14,12 +14,8 @@
#=============================================================================
# Copyright 2011 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If JEMALLOC_ROOT_DIR was defined in the environment, use it.

View File

@@ -13,12 +13,8 @@
#=============================================================================
# Copyright 2015 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
if(LLVM_ROOT_DIR)

View File

@@ -14,12 +14,8 @@
#=============================================================================
# Copyright 2015 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If LZO_ROOT_DIR was defined in the environment, use it.

View File

@@ -14,12 +14,8 @@
#=============================================================================
# Copyright 2011 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# note about include paths, there are 2 ways includes are set

View File

@@ -14,12 +14,8 @@
#=============================================================================
# Copyright 2012 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If OPENCOLORIO_ROOT_DIR was defined in the environment, use it.

View File

@@ -21,12 +21,8 @@
#=============================================================================
# Copyright 2011 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If OPENEXR_ROOT_DIR was defined in the environment, use it.

View File

@@ -10,6 +10,13 @@
# OPENGLES_LIBRARIES - all libraries needed for OpenGLES
# OPENGLES_INCLUDES - all includes needed for OpenGLES
#=============================================================================
# Copyright 2014 Blender Foundation.
#
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If OPENGLES_ROOT_DIR was defined in the environment, use it.
IF(NOT OPENGLES_ROOT_DIR AND NOT $ENV{OPENGLES_ROOT_DIR} STREQUAL "")
SET(OPENGLES_ROOT_DIR $ENV{OPENGLES_ROOT_DIR})

View File

@@ -14,12 +14,8 @@
#=============================================================================
# Copyright 2019 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If OPENIMAGEDENOISE_ROOT_DIR was defined in the environment, use it.

View File

@@ -16,12 +16,8 @@
#=============================================================================
# Copyright 2011 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If OPENIMAGEIO_ROOT_DIR was defined in the environment, use it.

View File

@@ -14,12 +14,8 @@
#=============================================================================
# Copyright 2011 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If OPENJPEG_ROOT_DIR was defined in the environment, use it.

View File

@@ -14,12 +14,8 @@
#=============================================================================
# Copyright 2014 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If OSL_ROOT_DIR was defined in the environment, use it.

View File

@@ -14,12 +14,8 @@
#=============================================================================
# Copyright 2013 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If OPENSUBDIV_ROOT_DIR was defined in the environment, use it.

View File

@@ -14,12 +14,8 @@
#=============================================================================
# Copyright 2015 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If OPENVDB_ROOT_DIR was defined in the environment, use it.

View File

@@ -10,12 +10,8 @@
#=============================================================================
# Copyright 2019 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If OPTIX_ROOT_DIR was defined in the environment, use it.

View File

@@ -14,12 +14,8 @@
#=============================================================================
# Copyright 2011 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If PCRE_ROOT_DIR was defined in the environment, use it.

View File

@@ -14,12 +14,8 @@
#=============================================================================
# Copyright 2014 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If PUGIXML_ROOT_DIR was defined in the environment, use it.

View File

@@ -25,12 +25,8 @@
#=============================================================================
# Copyright 2011 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If PYTHON_ROOT_DIR was defined in the environment, use it.

View File

@@ -13,12 +13,8 @@
#=============================================================================
# Copyright 2015 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If SDL2_ROOT_DIR was defined in the environment, use it.

View File

@@ -14,12 +14,8 @@
#=============================================================================
# Copyright 2011 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If LIBSNDFILE_ROOT_DIR was defined in the environment, use it.

View File

@@ -14,12 +14,8 @@
#=============================================================================
# Copyright 2011 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If SPACENAV_ROOT_DIR was defined in the environment, use it.

View File

@@ -14,12 +14,8 @@
#=============================================================================
# Copyright 2016 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If TBB_ROOT_DIR was defined in the environment, use it.

View File

@@ -12,12 +12,8 @@
#=============================================================================
# Copyright 2019 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If USD_ROOT_DIR was defined in the environment, use it.

View File

@@ -14,12 +14,8 @@
#=============================================================================
# Copyright 2011 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If XML2_ROOT_DIR was defined in the environment, use it.

View File

@@ -20,12 +20,8 @@
# XR_OPENXR_SDK_LOADER_LIBRARY, where to find the OpenXR-SDK loader library.
#=============================================================================
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#=============================================================================
# If XR_OPENXR_SDK_ROOT_DIR was defined in the environment, use it.

View File

@@ -1,12 +1,8 @@
#=============================================================================
# Copyright 2014 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
# Distributed under the OSI-approved BSD 3-Clause License,
# see accompanying file BSD-3-Clause-license.txt for details.
#
# Inspired on the Testing.cmake from Libmv
#

View File

@@ -397,7 +397,7 @@ function(setup_heavy_lib_pool)
list(APPEND _HEAVY_LIBS "cycles_device" "cycles_kernel")
endif()
if(WITH_LIBMV)
list(APPEND _HEAVY_LIBS "bf_intern_libmv")
list(APPEND _HEAVY_LIBS "extern_ceres" "bf_intern_libmv")
endif()
if(WITH_OPENVDB)
list(APPEND _HEAVY_LIBS "bf_intern_openvdb")

View File

@@ -373,8 +373,9 @@ if(WITH_CYCLES_OSL)
list(APPEND OSL_LIBRARIES ${OSL_LIB_COMP} -force_load ${OSL_LIB_EXEC} ${OSL_LIB_QUERY})
find_path(OSL_INCLUDE_DIR OSL/oslclosure.h PATHS ${CYCLES_OSL}/include)
find_program(OSL_COMPILER NAMES oslc PATHS ${CYCLES_OSL}/bin)
find_path(OSL_SHADER_DIR NAMES stdosl.h PATHS ${CYCLES_OSL}/shaders)
if(OSL_INCLUDE_DIR AND OSL_LIBRARIES AND OSL_COMPILER)
if(OSL_INCLUDE_DIR AND OSL_LIBRARIES AND OSL_COMPILER AND OSL_SHADER_DIR)
set(OSL_FOUND TRUE)
else()
message(STATUS "OSL not found")

View File

@@ -77,4 +77,4 @@ echo msbuild ^
/verbosity:minimal ^
/p:platform=%MSBUILD_PLATFORM% ^
/flp:Summary;Verbosity=minimal;LogFile=%BUILD_DIR%\Build.log >> %BUILD_DIR%\rebuild.cmd
echo echo %%TIME%% ^>^> buildtime.txt >> %BUILD_DIR%\rebuild.cmd
echo echo %%TIME%% ^>^> buildtime.txt >> %BUILD_DIR%\rebuild.cmd

View File

@@ -93,4 +93,4 @@ echo call "%VCVARS%" %BUILD_ARCH% >> %BUILD_DIR%\rebuild.cmd
echo ^) >> %BUILD_DIR%\rebuild.cmd
echo echo %%TIME%% ^> buildtime.txt >> %BUILD_DIR%\rebuild.cmd
echo ninja install >> %BUILD_DIR%\rebuild.cmd
echo echo %%TIME%% ^>^> buildtime.txt >> %BUILD_DIR%\rebuild.cmd
echo echo %%TIME%% ^>^> buildtime.txt >> %BUILD_DIR%\rebuild.cmd

View File

@@ -0,0 +1,26 @@
BSD 3-Clause License
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.

View File

@@ -20,6 +20,7 @@ from gpu_extras.presets import draw_circle_2d
offscreen = gpu.types.GPUOffScreen(512, 512)
with offscreen.bind():
bgl.glClearColor(0.0, 0.0, 0.0, 0.0)
bgl.glClear(bgl.GL_COLOR_BUFFER_BIT)
with gpu.matrix.push_pop():
# reset matrices -> use normalized device coordinates [-1, 1]

View File

@@ -25,6 +25,7 @@ RING_AMOUNT = 10
offscreen = gpu.types.GPUOffScreen(WIDTH, HEIGHT)
with offscreen.bind():
bgl.glClearColor(0.0, 0.0, 0.0, 0.0)
bgl.glClear(bgl.GL_COLOR_BUFFER_BIT)
with gpu.matrix.push_pop():
# reset matrices -> use normalized device coordinates [-1, 1]

View File

@@ -1395,9 +1395,9 @@ PyDoc_STRVAR(M_aud_Sound_threshold_doc,
" all between to 0.\n\n"
" :arg threshold: Threshold value over which an amplitude counts\n"
" non-zero.\n\n"
":type threshold: float\n"
":return: The created :class:`Sound` object.\n"
":rtype: :class:`Sound`");
" :type threshold: float\n"
" :return: The created :class:`Sound` object.\n"
" :rtype: :class:`Sound`");
static PyObject *
Sound_threshold(Sound* self, PyObject* args)

View File

@@ -119,8 +119,8 @@ void JOSResampleReader::updateBuffer(int size, double factor, int samplesize)
P = int_to_fp(m_L) - P;\
\
end = std::floor((m_len - 1) / double(m_L) + m_P) - 1;\
if(m_cache_valid - m_n - 2 < end)\
end = m_cache_valid - m_n - 2;\
if(m_cache_valid - int(m_n) - 2 < end)\
end = m_cache_valid - int(m_n) - 2;\
\
data = buf + (m_n + 2 + end) * m_channels - 1;\
l = fp_to_int(P);\
@@ -166,8 +166,8 @@ void JOSResampleReader::updateBuffer(int size, double factor, int samplesize)
P = 0 - P;\
\
end = (int_to_fp(m_len) - P) / P_increment - 1;\
if(m_cache_valid - m_n - 2 < end)\
end = m_cache_valid - m_n - 2;\
if(m_cache_valid - int(m_n) - 2 < end)\
end = m_cache_valid - int(m_n) - 2;\
\
P += P_increment * end;\
data = buf + (m_n + 2 + end) * m_channels - 1;\

View File

@@ -1,3 +1,3 @@
#define MANTA_GIT_VERSION "commit 841bfd09c068dfb95637c0ec14fa78305286a433"
#define MANTA_GIT_VERSION "commit e2f6e59e3679f88e5100ae2145410cca4971b9df"

View File

@@ -355,6 +355,38 @@ class GridBase : public PbClass {
return isInBounds(Vec3i(i, j, k), bnd);
}
#ifdef BLENDER
//! expose name field to Python for Blender
void setName(const std::string &name)
{
mName = name;
}
static PyObject *_W_9(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
GridBase *pbo = dynamic_cast<GridBase *>(Pb::objFromPy(_self));
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
pbPreparePlugin(pbo->getParent(), "GridBase::setName", !noTiming);
PyObject *_retval = 0;
{
ArgLocker _lock;
const std::string &name = _args.get<std::string>("name", 0, &_lock);
pbo->_args.copy(_args);
_retval = getPyNone();
pbo->setName(name);
pbo->_args.check();
}
pbFinalizePlugin(pbo->getParent(), "GridBase::setName", !noTiming);
return _retval;
}
catch (std::exception &e) {
pbSetError("GridBase::setName", e.what());
return 0;
}
}
#endif
protected:
GridType mType;
Vec3i mSize;
@@ -373,7 +405,7 @@ template<class T> class Grid : public GridBase {
public:
//! init new grid, values are set to zero
Grid(FluidSolver *parent, bool show = true);
static int _W_9(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static int _W_10(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
PbClass *obj = Pb::objFromPy(_self);
if (obj)
@@ -410,7 +442,7 @@ template<class T> class Grid : public GridBase {
typedef GridBase BASETYPE_GRID;
int save(std::string name);
static PyObject *_W_10(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_11(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -435,7 +467,7 @@ template<class T> class Grid : public GridBase {
}
int load(std::string name);
static PyObject *_W_11(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_12(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -461,7 +493,7 @@ template<class T> class Grid : public GridBase {
//! set all cells to zero
void clear();
static PyObject *_W_12(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_13(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -588,7 +620,7 @@ template<class T> class Grid : public GridBase {
// Grid<T>& operator=(const Grid<T>& a);
//! copy content from other grid (use this one instead of operator= !)
Grid<T> &copyFrom(const Grid<T> &a, bool copyType = true);
static PyObject *_W_13(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_14(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -618,7 +650,7 @@ template<class T> class Grid : public GridBase {
//! get grid type
int getGridType();
static PyObject *_W_14(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_15(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -643,7 +675,7 @@ template<class T> class Grid : public GridBase {
//! add/subtract other grid
void add(const Grid<T> &a);
static PyObject *_W_15(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_16(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -669,7 +701,7 @@ template<class T> class Grid : public GridBase {
}
void sub(const Grid<T> &a);
static PyObject *_W_16(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_17(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -696,7 +728,7 @@ template<class T> class Grid : public GridBase {
//! set all cells to constant value
void setConst(T s);
static PyObject *_W_17(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_18(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -723,7 +755,7 @@ template<class T> class Grid : public GridBase {
//! add constant to all grid cells
void addConst(T s);
static PyObject *_W_18(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_19(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -750,7 +782,7 @@ template<class T> class Grid : public GridBase {
//! add scaled other grid to current one (note, only "Real" factor, "T" type not supported here!)
void addScaled(const Grid<T> &a, const T &factor);
static PyObject *_W_19(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_20(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -778,7 +810,7 @@ template<class T> class Grid : public GridBase {
//! multiply contents of grid
void mult(const Grid<T> &a);
static PyObject *_W_20(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_21(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -805,7 +837,7 @@ template<class T> class Grid : public GridBase {
//! multiply each cell by a constant scalar value
void multConst(T s);
static PyObject *_W_21(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_22(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -832,7 +864,7 @@ template<class T> class Grid : public GridBase {
//! safely divide contents of grid (with zero check)
Grid<T> &safeDivide(const Grid<T> &a);
static PyObject *_W_22(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_23(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -858,7 +890,7 @@ template<class T> class Grid : public GridBase {
//! clamp content to range (for vec3, clamps each component separately)
void clamp(Real min, Real max);
static PyObject *_W_23(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_24(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -886,7 +918,7 @@ template<class T> class Grid : public GridBase {
//! reduce small values to zero
void stomp(const T &threshold);
static PyObject *_W_24(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_25(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -913,7 +945,7 @@ template<class T> class Grid : public GridBase {
//! permute grid axes, e.g. switch y with z (0,2,1)
void permuteAxes(int axis0, int axis1, int axis2);
static PyObject *_W_25(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_26(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -942,7 +974,7 @@ template<class T> class Grid : public GridBase {
//! permute grid axes, e.g. switch y with z (0,2,1)
void permuteAxesCopyToGrid(int axis0, int axis1, int axis2, Grid<T> &out);
static PyObject *_W_26(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_27(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -972,7 +1004,7 @@ template<class T> class Grid : public GridBase {
//! join other grid by either keeping min or max value at cell
void join(const Grid<T> &a, bool keepMax = true);
static PyObject *_W_27(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_28(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1001,7 +1033,7 @@ template<class T> class Grid : public GridBase {
// common compound operators
//! get absolute max value in grid
Real getMaxAbs() const;
static PyObject *_W_28(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_29(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1026,7 +1058,7 @@ template<class T> class Grid : public GridBase {
//! get max value in grid
Real getMax() const;
static PyObject *_W_29(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_30(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1051,7 +1083,7 @@ template<class T> class Grid : public GridBase {
//! get min value in grid
Real getMin() const;
static PyObject *_W_30(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_31(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1076,7 +1108,7 @@ template<class T> class Grid : public GridBase {
//! calculate L1 norm of grid content
Real getL1(int bnd = 0);
static PyObject *_W_31(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_32(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1102,7 +1134,7 @@ template<class T> class Grid : public GridBase {
//! calculate L2 norm of grid content
Real getL2(int bnd = 0);
static PyObject *_W_32(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_33(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1128,7 +1160,7 @@ template<class T> class Grid : public GridBase {
//! set all boundary cells to constant value (Dirichlet)
void setBound(T value, int boundaryWidth = 1);
static PyObject *_W_33(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_34(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1156,7 +1188,7 @@ template<class T> class Grid : public GridBase {
//! set all boundary cells to last inner value (Neumann)
void setBoundNeumann(int boundaryWidth = 1);
static PyObject *_W_34(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_35(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1183,7 +1215,7 @@ template<class T> class Grid : public GridBase {
//! get data pointer of grid
std::string getDataPointer();
static PyObject *_W_35(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_36(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1208,7 +1240,7 @@ template<class T> class Grid : public GridBase {
//! debugging helper, print grid from python. skip boundary of width bnd
void printGrid(int zSlice = -1, bool printIndex = false, int bnd = 1);
static PyObject *_W_36(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_37(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1276,7 +1308,7 @@ class MACGrid : public Grid<Vec3> {
{
mType = (GridType)(TypeMAC | TypeVec3);
}
static int _W_37(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static int _W_38(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
PbClass *obj = Pb::objFromPy(_self);
if (obj)
@@ -1358,7 +1390,7 @@ class MACGrid : public Grid<Vec3> {
//! set all boundary cells of a MAC grid to certain value (Dirchlet). Respects staggered grid
//! locations optionally, only set normal components
void setBoundMAC(Vec3 value, int boundaryWidth, bool normalOnly = false);
static PyObject *_W_38(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_39(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1399,7 +1431,7 @@ class FlagGrid : public Grid<int> {
{
mType = (GridType)(TypeFlags | TypeInt);
}
static int _W_39(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static int _W_40(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
PbClass *obj = Pb::objFromPy(_self);
if (obj)
@@ -1579,7 +1611,7 @@ class FlagGrid : public Grid<int> {
const std::string &inflow = " ",
const std::string &outflow = " ",
Grid<Real> *phiWalls = 0x00);
static PyObject *_W_40(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_41(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1613,7 +1645,7 @@ class FlagGrid : public Grid<int> {
//! set fluid flags inside levelset (liquids)
void updateFromLevelset(LevelsetGrid &levelset);
static PyObject *_W_41(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_42(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1640,7 +1672,7 @@ class FlagGrid : public Grid<int> {
//! set all cells (except obs/in/outflow) to type (fluid by default)
void fillGrid(int type = TypeFluid);
static PyObject *_W_42(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_43(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1669,7 +1701,7 @@ class FlagGrid : public Grid<int> {
//! warning for large grids! only regular int returned (due to python interface)
//! optionally creates mask in RealGrid (1 where flag matches, 0 otherwise)
int countCells(int flag, int bnd = 0, Grid<Real> *mask = NULL);
static PyObject *_W_43(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
static PyObject *_W_44(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);

View File

@@ -8,103 +8,103 @@ namespace Manta {
#ifdef _C_FlagGrid
static const Pb::Register _R_26("FlagGrid", "FlagGrid", "Grid<int>");
template<> const char *Namify<FlagGrid>::S = "FlagGrid";
static const Pb::Register _R_27("FlagGrid", "FlagGrid", FlagGrid::_W_39);
static const Pb::Register _R_28("FlagGrid", "initDomain", FlagGrid::_W_40);
static const Pb::Register _R_29("FlagGrid", "updateFromLevelset", FlagGrid::_W_41);
static const Pb::Register _R_30("FlagGrid", "fillGrid", FlagGrid::_W_42);
static const Pb::Register _R_31("FlagGrid", "countCells", FlagGrid::_W_43);
static const Pb::Register _R_27("FlagGrid", "FlagGrid", FlagGrid::_W_40);
static const Pb::Register _R_28("FlagGrid", "initDomain", FlagGrid::_W_41);
static const Pb::Register _R_29("FlagGrid", "updateFromLevelset", FlagGrid::_W_42);
static const Pb::Register _R_30("FlagGrid", "fillGrid", FlagGrid::_W_43);
static const Pb::Register _R_31("FlagGrid", "countCells", FlagGrid::_W_44);
#endif
#ifdef _C_Grid
static const Pb::Register _R_32("Grid<int>", "Grid<int>", "GridBase");
template<> const char *Namify<Grid<int>>::S = "Grid<int>";
static const Pb::Register _R_33("Grid<int>", "Grid", Grid<int>::_W_9);
static const Pb::Register _R_34("Grid<int>", "save", Grid<int>::_W_10);
static const Pb::Register _R_35("Grid<int>", "load", Grid<int>::_W_11);
static const Pb::Register _R_36("Grid<int>", "clear", Grid<int>::_W_12);
static const Pb::Register _R_37("Grid<int>", "copyFrom", Grid<int>::_W_13);
static const Pb::Register _R_38("Grid<int>", "getGridType", Grid<int>::_W_14);
static const Pb::Register _R_39("Grid<int>", "add", Grid<int>::_W_15);
static const Pb::Register _R_40("Grid<int>", "sub", Grid<int>::_W_16);
static const Pb::Register _R_41("Grid<int>", "setConst", Grid<int>::_W_17);
static const Pb::Register _R_42("Grid<int>", "addConst", Grid<int>::_W_18);
static const Pb::Register _R_43("Grid<int>", "addScaled", Grid<int>::_W_19);
static const Pb::Register _R_44("Grid<int>", "mult", Grid<int>::_W_20);
static const Pb::Register _R_45("Grid<int>", "multConst", Grid<int>::_W_21);
static const Pb::Register _R_46("Grid<int>", "safeDivide", Grid<int>::_W_22);
static const Pb::Register _R_47("Grid<int>", "clamp", Grid<int>::_W_23);
static const Pb::Register _R_48("Grid<int>", "stomp", Grid<int>::_W_24);
static const Pb::Register _R_49("Grid<int>", "permuteAxes", Grid<int>::_W_25);
static const Pb::Register _R_50("Grid<int>", "permuteAxesCopyToGrid", Grid<int>::_W_26);
static const Pb::Register _R_51("Grid<int>", "join", Grid<int>::_W_27);
static const Pb::Register _R_52("Grid<int>", "getMaxAbs", Grid<int>::_W_28);
static const Pb::Register _R_53("Grid<int>", "getMax", Grid<int>::_W_29);
static const Pb::Register _R_54("Grid<int>", "getMin", Grid<int>::_W_30);
static const Pb::Register _R_55("Grid<int>", "getL1", Grid<int>::_W_31);
static const Pb::Register _R_56("Grid<int>", "getL2", Grid<int>::_W_32);
static const Pb::Register _R_57("Grid<int>", "setBound", Grid<int>::_W_33);
static const Pb::Register _R_58("Grid<int>", "setBoundNeumann", Grid<int>::_W_34);
static const Pb::Register _R_59("Grid<int>", "getDataPointer", Grid<int>::_W_35);
static const Pb::Register _R_60("Grid<int>", "printGrid", Grid<int>::_W_36);
static const Pb::Register _R_33("Grid<int>", "Grid", Grid<int>::_W_10);
static const Pb::Register _R_34("Grid<int>", "save", Grid<int>::_W_11);
static const Pb::Register _R_35("Grid<int>", "load", Grid<int>::_W_12);
static const Pb::Register _R_36("Grid<int>", "clear", Grid<int>::_W_13);
static const Pb::Register _R_37("Grid<int>", "copyFrom", Grid<int>::_W_14);
static const Pb::Register _R_38("Grid<int>", "getGridType", Grid<int>::_W_15);
static const Pb::Register _R_39("Grid<int>", "add", Grid<int>::_W_16);
static const Pb::Register _R_40("Grid<int>", "sub", Grid<int>::_W_17);
static const Pb::Register _R_41("Grid<int>", "setConst", Grid<int>::_W_18);
static const Pb::Register _R_42("Grid<int>", "addConst", Grid<int>::_W_19);
static const Pb::Register _R_43("Grid<int>", "addScaled", Grid<int>::_W_20);
static const Pb::Register _R_44("Grid<int>", "mult", Grid<int>::_W_21);
static const Pb::Register _R_45("Grid<int>", "multConst", Grid<int>::_W_22);
static const Pb::Register _R_46("Grid<int>", "safeDivide", Grid<int>::_W_23);
static const Pb::Register _R_47("Grid<int>", "clamp", Grid<int>::_W_24);
static const Pb::Register _R_48("Grid<int>", "stomp", Grid<int>::_W_25);
static const Pb::Register _R_49("Grid<int>", "permuteAxes", Grid<int>::_W_26);
static const Pb::Register _R_50("Grid<int>", "permuteAxesCopyToGrid", Grid<int>::_W_27);
static const Pb::Register _R_51("Grid<int>", "join", Grid<int>::_W_28);
static const Pb::Register _R_52("Grid<int>", "getMaxAbs", Grid<int>::_W_29);
static const Pb::Register _R_53("Grid<int>", "getMax", Grid<int>::_W_30);
static const Pb::Register _R_54("Grid<int>", "getMin", Grid<int>::_W_31);
static const Pb::Register _R_55("Grid<int>", "getL1", Grid<int>::_W_32);
static const Pb::Register _R_56("Grid<int>", "getL2", Grid<int>::_W_33);
static const Pb::Register _R_57("Grid<int>", "setBound", Grid<int>::_W_34);
static const Pb::Register _R_58("Grid<int>", "setBoundNeumann", Grid<int>::_W_35);
static const Pb::Register _R_59("Grid<int>", "getDataPointer", Grid<int>::_W_36);
static const Pb::Register _R_60("Grid<int>", "printGrid", Grid<int>::_W_37);
static const Pb::Register _R_61("Grid<Real>", "Grid<Real>", "GridBase");
template<> const char *Namify<Grid<Real>>::S = "Grid<Real>";
static const Pb::Register _R_62("Grid<Real>", "Grid", Grid<Real>::_W_9);
static const Pb::Register _R_63("Grid<Real>", "save", Grid<Real>::_W_10);
static const Pb::Register _R_64("Grid<Real>", "load", Grid<Real>::_W_11);
static const Pb::Register _R_65("Grid<Real>", "clear", Grid<Real>::_W_12);
static const Pb::Register _R_66("Grid<Real>", "copyFrom", Grid<Real>::_W_13);
static const Pb::Register _R_67("Grid<Real>", "getGridType", Grid<Real>::_W_14);
static const Pb::Register _R_68("Grid<Real>", "add", Grid<Real>::_W_15);
static const Pb::Register _R_69("Grid<Real>", "sub", Grid<Real>::_W_16);
static const Pb::Register _R_70("Grid<Real>", "setConst", Grid<Real>::_W_17);
static const Pb::Register _R_71("Grid<Real>", "addConst", Grid<Real>::_W_18);
static const Pb::Register _R_72("Grid<Real>", "addScaled", Grid<Real>::_W_19);
static const Pb::Register _R_73("Grid<Real>", "mult", Grid<Real>::_W_20);
static const Pb::Register _R_74("Grid<Real>", "multConst", Grid<Real>::_W_21);
static const Pb::Register _R_75("Grid<Real>", "safeDivide", Grid<Real>::_W_22);
static const Pb::Register _R_76("Grid<Real>", "clamp", Grid<Real>::_W_23);
static const Pb::Register _R_77("Grid<Real>", "stomp", Grid<Real>::_W_24);
static const Pb::Register _R_78("Grid<Real>", "permuteAxes", Grid<Real>::_W_25);
static const Pb::Register _R_79("Grid<Real>", "permuteAxesCopyToGrid", Grid<Real>::_W_26);
static const Pb::Register _R_80("Grid<Real>", "join", Grid<Real>::_W_27);
static const Pb::Register _R_81("Grid<Real>", "getMaxAbs", Grid<Real>::_W_28);
static const Pb::Register _R_82("Grid<Real>", "getMax", Grid<Real>::_W_29);
static const Pb::Register _R_83("Grid<Real>", "getMin", Grid<Real>::_W_30);
static const Pb::Register _R_84("Grid<Real>", "getL1", Grid<Real>::_W_31);
static const Pb::Register _R_85("Grid<Real>", "getL2", Grid<Real>::_W_32);
static const Pb::Register _R_86("Grid<Real>", "setBound", Grid<Real>::_W_33);
static const Pb::Register _R_87("Grid<Real>", "setBoundNeumann", Grid<Real>::_W_34);
static const Pb::Register _R_88("Grid<Real>", "getDataPointer", Grid<Real>::_W_35);
static const Pb::Register _R_89("Grid<Real>", "printGrid", Grid<Real>::_W_36);
static const Pb::Register _R_62("Grid<Real>", "Grid", Grid<Real>::_W_10);
static const Pb::Register _R_63("Grid<Real>", "save", Grid<Real>::_W_11);
static const Pb::Register _R_64("Grid<Real>", "load", Grid<Real>::_W_12);
static const Pb::Register _R_65("Grid<Real>", "clear", Grid<Real>::_W_13);
static const Pb::Register _R_66("Grid<Real>", "copyFrom", Grid<Real>::_W_14);
static const Pb::Register _R_67("Grid<Real>", "getGridType", Grid<Real>::_W_15);
static const Pb::Register _R_68("Grid<Real>", "add", Grid<Real>::_W_16);
static const Pb::Register _R_69("Grid<Real>", "sub", Grid<Real>::_W_17);
static const Pb::Register _R_70("Grid<Real>", "setConst", Grid<Real>::_W_18);
static const Pb::Register _R_71("Grid<Real>", "addConst", Grid<Real>::_W_19);
static const Pb::Register _R_72("Grid<Real>", "addScaled", Grid<Real>::_W_20);
static const Pb::Register _R_73("Grid<Real>", "mult", Grid<Real>::_W_21);
static const Pb::Register _R_74("Grid<Real>", "multConst", Grid<Real>::_W_22);
static const Pb::Register _R_75("Grid<Real>", "safeDivide", Grid<Real>::_W_23);
static const Pb::Register _R_76("Grid<Real>", "clamp", Grid<Real>::_W_24);
static const Pb::Register _R_77("Grid<Real>", "stomp", Grid<Real>::_W_25);
static const Pb::Register _R_78("Grid<Real>", "permuteAxes", Grid<Real>::_W_26);
static const Pb::Register _R_79("Grid<Real>", "permuteAxesCopyToGrid", Grid<Real>::_W_27);
static const Pb::Register _R_80("Grid<Real>", "join", Grid<Real>::_W_28);
static const Pb::Register _R_81("Grid<Real>", "getMaxAbs", Grid<Real>::_W_29);
static const Pb::Register _R_82("Grid<Real>", "getMax", Grid<Real>::_W_30);
static const Pb::Register _R_83("Grid<Real>", "getMin", Grid<Real>::_W_31);
static const Pb::Register _R_84("Grid<Real>", "getL1", Grid<Real>::_W_32);
static const Pb::Register _R_85("Grid<Real>", "getL2", Grid<Real>::_W_33);
static const Pb::Register _R_86("Grid<Real>", "setBound", Grid<Real>::_W_34);
static const Pb::Register _R_87("Grid<Real>", "setBoundNeumann", Grid<Real>::_W_35);
static const Pb::Register _R_88("Grid<Real>", "getDataPointer", Grid<Real>::_W_36);
static const Pb::Register _R_89("Grid<Real>", "printGrid", Grid<Real>::_W_37);
static const Pb::Register _R_90("Grid<Vec3>", "Grid<Vec3>", "GridBase");
template<> const char *Namify<Grid<Vec3>>::S = "Grid<Vec3>";
static const Pb::Register _R_91("Grid<Vec3>", "Grid", Grid<Vec3>::_W_9);
static const Pb::Register _R_92("Grid<Vec3>", "save", Grid<Vec3>::_W_10);
static const Pb::Register _R_93("Grid<Vec3>", "load", Grid<Vec3>::_W_11);
static const Pb::Register _R_94("Grid<Vec3>", "clear", Grid<Vec3>::_W_12);
static const Pb::Register _R_95("Grid<Vec3>", "copyFrom", Grid<Vec3>::_W_13);
static const Pb::Register _R_96("Grid<Vec3>", "getGridType", Grid<Vec3>::_W_14);
static const Pb::Register _R_97("Grid<Vec3>", "add", Grid<Vec3>::_W_15);
static const Pb::Register _R_98("Grid<Vec3>", "sub", Grid<Vec3>::_W_16);
static const Pb::Register _R_99("Grid<Vec3>", "setConst", Grid<Vec3>::_W_17);
static const Pb::Register _R_100("Grid<Vec3>", "addConst", Grid<Vec3>::_W_18);
static const Pb::Register _R_101("Grid<Vec3>", "addScaled", Grid<Vec3>::_W_19);
static const Pb::Register _R_102("Grid<Vec3>", "mult", Grid<Vec3>::_W_20);
static const Pb::Register _R_103("Grid<Vec3>", "multConst", Grid<Vec3>::_W_21);
static const Pb::Register _R_104("Grid<Vec3>", "safeDivide", Grid<Vec3>::_W_22);
static const Pb::Register _R_105("Grid<Vec3>", "clamp", Grid<Vec3>::_W_23);
static const Pb::Register _R_106("Grid<Vec3>", "stomp", Grid<Vec3>::_W_24);
static const Pb::Register _R_107("Grid<Vec3>", "permuteAxes", Grid<Vec3>::_W_25);
static const Pb::Register _R_108("Grid<Vec3>", "permuteAxesCopyToGrid", Grid<Vec3>::_W_26);
static const Pb::Register _R_109("Grid<Vec3>", "join", Grid<Vec3>::_W_27);
static const Pb::Register _R_110("Grid<Vec3>", "getMaxAbs", Grid<Vec3>::_W_28);
static const Pb::Register _R_111("Grid<Vec3>", "getMax", Grid<Vec3>::_W_29);
static const Pb::Register _R_112("Grid<Vec3>", "getMin", Grid<Vec3>::_W_30);
static const Pb::Register _R_113("Grid<Vec3>", "getL1", Grid<Vec3>::_W_31);
static const Pb::Register _R_114("Grid<Vec3>", "getL2", Grid<Vec3>::_W_32);
static const Pb::Register _R_115("Grid<Vec3>", "setBound", Grid<Vec3>::_W_33);
static const Pb::Register _R_116("Grid<Vec3>", "setBoundNeumann", Grid<Vec3>::_W_34);
static const Pb::Register _R_117("Grid<Vec3>", "getDataPointer", Grid<Vec3>::_W_35);
static const Pb::Register _R_118("Grid<Vec3>", "printGrid", Grid<Vec3>::_W_36);
static const Pb::Register _R_91("Grid<Vec3>", "Grid", Grid<Vec3>::_W_10);
static const Pb::Register _R_92("Grid<Vec3>", "save", Grid<Vec3>::_W_11);
static const Pb::Register _R_93("Grid<Vec3>", "load", Grid<Vec3>::_W_12);
static const Pb::Register _R_94("Grid<Vec3>", "clear", Grid<Vec3>::_W_13);
static const Pb::Register _R_95("Grid<Vec3>", "copyFrom", Grid<Vec3>::_W_14);
static const Pb::Register _R_96("Grid<Vec3>", "getGridType", Grid<Vec3>::_W_15);
static const Pb::Register _R_97("Grid<Vec3>", "add", Grid<Vec3>::_W_16);
static const Pb::Register _R_98("Grid<Vec3>", "sub", Grid<Vec3>::_W_17);
static const Pb::Register _R_99("Grid<Vec3>", "setConst", Grid<Vec3>::_W_18);
static const Pb::Register _R_100("Grid<Vec3>", "addConst", Grid<Vec3>::_W_19);
static const Pb::Register _R_101("Grid<Vec3>", "addScaled", Grid<Vec3>::_W_20);
static const Pb::Register _R_102("Grid<Vec3>", "mult", Grid<Vec3>::_W_21);
static const Pb::Register _R_103("Grid<Vec3>", "multConst", Grid<Vec3>::_W_22);
static const Pb::Register _R_104("Grid<Vec3>", "safeDivide", Grid<Vec3>::_W_23);
static const Pb::Register _R_105("Grid<Vec3>", "clamp", Grid<Vec3>::_W_24);
static const Pb::Register _R_106("Grid<Vec3>", "stomp", Grid<Vec3>::_W_25);
static const Pb::Register _R_107("Grid<Vec3>", "permuteAxes", Grid<Vec3>::_W_26);
static const Pb::Register _R_108("Grid<Vec3>", "permuteAxesCopyToGrid", Grid<Vec3>::_W_27);
static const Pb::Register _R_109("Grid<Vec3>", "join", Grid<Vec3>::_W_28);
static const Pb::Register _R_110("Grid<Vec3>", "getMaxAbs", Grid<Vec3>::_W_29);
static const Pb::Register _R_111("Grid<Vec3>", "getMax", Grid<Vec3>::_W_30);
static const Pb::Register _R_112("Grid<Vec3>", "getMin", Grid<Vec3>::_W_31);
static const Pb::Register _R_113("Grid<Vec3>", "getL1", Grid<Vec3>::_W_32);
static const Pb::Register _R_114("Grid<Vec3>", "getL2", Grid<Vec3>::_W_33);
static const Pb::Register _R_115("Grid<Vec3>", "setBound", Grid<Vec3>::_W_34);
static const Pb::Register _R_116("Grid<Vec3>", "setBoundNeumann", Grid<Vec3>::_W_35);
static const Pb::Register _R_117("Grid<Vec3>", "getDataPointer", Grid<Vec3>::_W_36);
static const Pb::Register _R_118("Grid<Vec3>", "printGrid", Grid<Vec3>::_W_37);
#endif
#ifdef _C_GridBase
static const Pb::Register _R_119("GridBase", "GridBase", "PbClass");
@@ -118,12 +118,13 @@ static const Pb::Register _R_125("GridBase", "is3D", GridBase::_W_5);
static const Pb::Register _R_126("GridBase", "is4D", GridBase::_W_6);
static const Pb::Register _R_127("GridBase", "getSizeT", GridBase::_W_7);
static const Pb::Register _R_128("GridBase", "getStrideT", GridBase::_W_8);
static const Pb::Register _R_129("GridBase", "setName", GridBase::_W_9);
#endif
#ifdef _C_MACGrid
static const Pb::Register _R_129("MACGrid", "MACGrid", "Grid<Vec3>");
static const Pb::Register _R_130("MACGrid", "MACGrid", "Grid<Vec3>");
template<> const char *Namify<MACGrid>::S = "MACGrid";
static const Pb::Register _R_130("MACGrid", "MACGrid", MACGrid::_W_37);
static const Pb::Register _R_131("MACGrid", "setBoundMAC", MACGrid::_W_38);
static const Pb::Register _R_131("MACGrid", "MACGrid", MACGrid::_W_38);
static const Pb::Register _R_132("MACGrid", "setBoundMAC", MACGrid::_W_39);
#endif
static const Pb::Register _R_7("GridType_TypeNone", 0);
static const Pb::Register _R_8("GridType_TypeReal", 1);
@@ -253,6 +254,7 @@ void PbRegister_file_7()
KEEP_UNUSED(_R_129);
KEEP_UNUSED(_R_130);
KEEP_UNUSED(_R_131);
KEEP_UNUSED(_R_132);
}
}
} // namespace Manta

View File

@@ -109,6 +109,10 @@ if(WITH_OPENIMAGEDENOISE)
)
endif()
if(WITH_EXPERIMENTAL_FEATURES)
add_definitions(-DWITH_HAIR_NODES)
endif()
blender_add_lib(bf_intern_cycles "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
# avoid link failure with clang 3.4 debug

View File

@@ -68,6 +68,8 @@ class AddPresetSampling(AddPresetBase, Operator):
"cycles.subsurface_samples",
"cycles.volume_samples",
"cycles.use_square_samples",
"cycles.use_light_tree",
"cycles.splitting_threshold",
"cycles.progressive",
"cycles.seed",
"cycles.sample_clamp_direct",

View File

@@ -291,6 +291,19 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
default=False,
)
use_light_tree: BoolProperty(
name="Light Tree",
description="Samples many lights more efficiently",
default=False,
)
splitting_threshold: FloatProperty(
name="Splitting",
description="Amount of lights to sample at a time, from one light at 0.0, to adaptively more lights as needed, to all lights at 1.0",
min=0.0, max=1.0,
default=0.0,
)
samples: IntProperty(
name="Samples",
description="Number of samples to render for each pixel",

View File

@@ -326,6 +326,35 @@ class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel):
layout.row().prop(cscene, "use_layer_samples")
break
class CYCLES_RENDER_PT_sampling_light_tree(CyclesButtonsPanel, Panel):
bl_label = "Light Tree"
bl_parent_id = "CYCLES_RENDER_PT_sampling"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
return (context.scene.cycles.feature_set == 'EXPERIMENTAL')
def draw_header(self, context):
layout = self.layout
scene = context.scene
cscene = scene.cycles
layout.prop(cscene, "use_light_tree", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
scene = context.scene
cscene = scene.cycles
layout.active = cscene.use_light_tree
row = layout.row(align=True)
row.label(text="") # create empty column
row.prop(cscene, "splitting_threshold", text="Splitting")
class CYCLES_RENDER_PT_sampling_total(CyclesButtonsPanel, Panel):
bl_label = "Total Samples"
@@ -2272,6 +2301,7 @@ classes = (
CYCLES_RENDER_PT_sampling_adaptive,
CYCLES_RENDER_PT_sampling_denoising,
CYCLES_RENDER_PT_sampling_advanced,
CYCLES_RENDER_PT_sampling_light_tree,
CYCLES_RENDER_PT_light_paths,
CYCLES_RENDER_PT_light_paths_max_bounces,
CYCLES_RENDER_PT_light_paths_clamping,

View File

@@ -628,6 +628,7 @@ void BlenderSync::sync_particle_hair(
}
}
#ifdef WITH_HAIR_NODES
static float4 hair_point_as_float4(BL::HairPoint b_point)
{
float4 mP = float3_to_float4(get_float3(b_point.co()));
@@ -806,6 +807,15 @@ void BlenderSync::sync_hair(Hair *hair, BL::Object &b_ob, bool motion, int motio
export_hair_curves(scene, hair, b_hair);
}
}
#else
void BlenderSync::sync_hair(Hair *hair, BL::Object &b_ob, bool motion, int motion_step)
{
(void)hair;
(void)b_ob;
(void)motion;
(void)motion_step;
}
#endif
void BlenderSync::sync_hair(BL::Depsgraph b_depsgraph,
BL::Object b_ob,

View File

@@ -458,15 +458,19 @@ void BlenderSync::sync_motion(BL::RenderSettings &b_render,
python_thread_state_restore(python_thread_state);
b_engine.frame_set(frame, subframe);
python_thread_state_save(python_thread_state);
sync_camera_motion(b_render, b_cam, width, height, 0.0f);
if (b_cam) {
sync_camera_motion(b_render, b_cam, width, height, 0.0f);
}
sync_objects(b_depsgraph, b_v3d, 0.0f);
}
/* Insert motion times from camera. Motion times from other objects
* have already been added in a sync_objects call. */
uint camera_motion_steps = object_motion_steps(b_cam, b_cam);
for (size_t step = 0; step < camera_motion_steps; step++) {
motion_times.insert(scene->camera->motion_time(step));
if (b_cam) {
uint camera_motion_steps = object_motion_steps(b_cam, b_cam);
for (size_t step = 0; step < camera_motion_steps; step++) {
motion_times.insert(scene->camera->motion_time(step));
}
}
/* note iteration over motion_times set happens in sorted order */

View File

@@ -59,6 +59,7 @@ BlenderSession::BlenderSession(BL::RenderEngine &b_engine,
BL::BlendData &b_data,
bool preview_osl)
: session(NULL),
scene(NULL),
sync(NULL),
b_engine(b_engine),
b_userpref(b_userpref),
@@ -88,6 +89,7 @@ BlenderSession::BlenderSession(BL::RenderEngine &b_engine,
int width,
int height)
: session(NULL),
scene(NULL),
sync(NULL),
b_engine(b_engine),
b_userpref(b_userpref),
@@ -970,7 +972,8 @@ void BlenderSession::update_status_progress()
remaining_time = (1.0 - (double)progress) * (render_time / (double)progress);
if (background) {
scene_status += " | " + scene->name;
if (scene)
scene_status += " | " + scene->name;
if (b_rlay_name != "")
scene_status += ", " + b_rlay_name;

View File

@@ -678,7 +678,7 @@ static ShaderNode *add_node(Scene *scene,
* builtin names for packed images and movies
*/
int scene_frame = b_scene.frame_current();
int image_frame = image_user_frame_number(b_image_user, scene_frame);
int image_frame = image_user_frame_number(b_image_user, b_image, scene_frame);
image->handle = scene->image_manager->add_image(
new BlenderImageLoader(b_image, image_frame), image->image_params());
}
@@ -713,7 +713,7 @@ static ShaderNode *add_node(Scene *scene,
if (is_builtin) {
int scene_frame = b_scene.frame_current();
int image_frame = image_user_frame_number(b_image_user, scene_frame);
int image_frame = image_user_frame_number(b_image_user, b_image, scene_frame);
env->handle = scene->image_manager->add_image(new BlenderImageLoader(b_image, image_frame),
env->image_params());
}

View File

@@ -304,6 +304,8 @@ void BlenderSync::sync_integrator()
integrator->method = (Integrator::Method)get_enum(
cscene, "progressive", Integrator::NUM_METHODS, Integrator::PATH);
integrator->use_light_tree = get_boolean(cscene, "use_light_tree");
integrator->splitting_threshold = get_float(cscene, "splitting_threshold");
integrator->sample_all_lights_direct = get_boolean(cscene, "sample_all_lights_direct");
integrator->sample_all_lights_indirect = get_boolean(cscene, "sample_all_lights_indirect");
integrator->light_sampling_threshold = get_float(cscene, "light_sampling_threshold");
@@ -359,8 +361,13 @@ void BlenderSync::sync_integrator()
integrator->ao_bounces = 0;
}
if (integrator->modified(previntegrator))
if (integrator->modified(previntegrator)) {
integrator->tag_update(scene);
}
if (integrator->use_light_tree != previntegrator.use_light_tree ||
integrator->splitting_threshold != previntegrator.splitting_threshold) {
scene->light_manager->tag_update(scene);
}
}
/* Film */

View File

@@ -238,7 +238,7 @@ static inline string image_user_file_path(BL::ImageUser &iuser,
{
char filepath[1024];
iuser.tile(0);
BKE_image_user_frame_calc(NULL, iuser.ptr.data, cfra);
BKE_image_user_frame_calc(ima.ptr.data, iuser.ptr.data, cfra);
BKE_image_user_file_path(iuser.ptr.data, ima.ptr.data, filepath);
string filepath_str = string(filepath);
@@ -248,9 +248,9 @@ static inline string image_user_file_path(BL::ImageUser &iuser,
return filepath_str;
}
static inline int image_user_frame_number(BL::ImageUser &iuser, int cfra)
static inline int image_user_frame_number(BL::ImageUser &iuser, BL::Image &ima, int cfra)
{
BKE_image_user_frame_calc(NULL, iuser.ptr.data, cfra);
BKE_image_user_frame_calc(ima.ptr.data, iuser.ptr.data, cfra);
return iuser.frame_current();
}

View File

@@ -217,43 +217,29 @@ static void sync_smoke_volume(Scene *scene, BL::Object &b_ob, Mesh *mesh, float
class BlenderVolumeLoader : public VDBImageLoader {
public:
BlenderVolumeLoader(BL::BlendData &b_data, BL::Volume &b_volume, const string &grid_name)
: VDBImageLoader(grid_name), b_data(b_data), b_volume(b_volume), unload(false)
{
}
bool load_metadata(ImageMetaData &metadata) override
: VDBImageLoader(grid_name), b_volume(b_volume)
{
b_volume.grids.load(b_data.ptr.data);
BL::VolumeGrid b_volume_grid = find_grid();
if (!b_volume_grid) {
return false;
}
unload = !b_volume_grid.is_loaded();
#ifdef WITH_OPENVDB
Volume *volume = (Volume *)b_volume.ptr.data;
VolumeGrid *volume_grid = (VolumeGrid *)b_volume_grid.ptr.data;
grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
#endif
BL::Volume::grids_iterator b_grid_iter;
for (b_volume.grids.begin(b_grid_iter); b_grid_iter != b_volume.grids.end(); ++b_grid_iter) {
BL::VolumeGrid b_volume_grid(*b_grid_iter);
if (b_volume_grid.name() == grid_name) {
const bool unload = !b_volume_grid.is_loaded();
return VDBImageLoader::load_metadata(metadata);
}
Volume *volume = (Volume *)b_volume.ptr.data;
VolumeGrid *volume_grid = (VolumeGrid *)b_volume_grid.ptr.data;
grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
bool load_pixels(const ImageMetaData &metadata,
void *pixels,
const size_t pixel_size,
const bool associate_alpha) override
{
b_volume.grids.load(b_data.ptr.data);
BL::VolumeGrid b_volume_grid = find_grid();
if (unload) {
b_volume_grid.unload();
}
if (!b_volume_grid) {
return false;
break;
}
}
return VDBImageLoader::load_pixels(metadata, pixels, pixel_size, associate_alpha);
#endif
}
bool equals(const ImageLoader &other) const override
@@ -263,36 +249,7 @@ class BlenderVolumeLoader : public VDBImageLoader {
return b_volume == other_loader.b_volume && grid_name == other_loader.grid_name;
}
void cleanup() override
{
VDBImageLoader::cleanup();
BL::VolumeGrid b_volume_grid = find_grid();
if (b_volume_grid && unload) {
b_volume_grid.unload();
}
}
/* Find grid with matching name. Grid point not stored in the class since
* grids may be unloaded before we load the pixels, for example for motion
* blur where we move between frames. */
BL::VolumeGrid find_grid()
{
#ifdef WITH_OPENVDB
BL::Volume::grids_iterator b_grid_iter;
for (b_volume.grids.begin(b_grid_iter); b_grid_iter != b_volume.grids.end(); ++b_grid_iter) {
if (b_grid_iter->name() == grid_name) {
return *b_grid_iter;
}
}
#endif
return BL::VolumeGrid(PointerRNA_NULL);
}
BL::BlendData b_data;
BL::Volume b_volume;
bool unload;
};
static void sync_volume_object(BL::BlendData &b_data, BL::Object &b_ob, Scene *scene, Mesh *mesh)
@@ -342,7 +299,7 @@ static void sync_volume_object(BL::BlendData &b_data, BL::Object &b_ob, Scene *s
ImageParams params;
params.frame = b_volume.grids.frame();
attr->data_voxel() = scene->image_manager->add_image(loader, params);
attr->data_voxel() = scene->image_manager->add_image(loader, params, false);
}
}
}

View File

@@ -126,9 +126,13 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args)
}
else {
kernel_embree_convert_hit(kg, ray, hit, &current_isect);
if (ctx->local_object_id != current_isect.object) {
int object = (current_isect.object == OBJECT_NONE) ?
kernel_tex_fetch(__prim_object, current_isect.prim) :
current_isect.object;
if (ctx->local_object_id != object) {
/* This tells Embree to continue tracing. */
*args->valid = 0;
break;
}
}

View File

@@ -81,6 +81,9 @@ ccl_device_noinline void compute_light_pass(
kg, sd, emission_sd, L, &state, &ray, &throughput, &ss_indirect)) {
while (ss_indirect.num_rays) {
kernel_path_subsurface_setup_indirect(kg, &ss_indirect, &state, &ray, L, &throughput);
indirect_sd.P_pick = sd->P_pick;
indirect_sd.V_pick = sd->V_pick;
indirect_sd.t_pick = sd->t_pick;
kernel_path_indirect(kg, &indirect_sd, emission_sd, &ray, throughput, &state, L);
}
is_sss_sample = true;
@@ -97,6 +100,9 @@ ccl_device_noinline void compute_light_pass(
state.ray_t = 0.0f;
# endif
/* compute indirect light */
indirect_sd.P_pick = sd->P_pick;
indirect_sd.V_pick = sd->V_pick;
indirect_sd.t_pick = sd->t_pick;
kernel_path_indirect(kg, &indirect_sd, emission_sd, &ray, throughput, &state, L);
/* sum and reset indirect light pass variables for the next samples */

View File

@@ -222,6 +222,7 @@ ccl_device_noinline_cpu float3 indirect_primitive_emission(
/* multiple importance sampling, get triangle light pdf,
* and compute weight with respect to BSDF pdf */
float pdf = triangle_light_pdf(kg, sd, t);
pdf *= light_distribution_pdf(kg, sd->P_pick, sd->V_pick, sd->t_pick, sd->prim, sd->object);
float mis_weight = power_heuristic(bsdf_pdf, pdf);
return L * mis_weight;
@@ -270,11 +271,16 @@ ccl_device_noinline_cpu void indirect_lamp_emission(KernelGlobals *kg,
kernel_volume_shadow(kg, emission_sd, state, &volume_ray, &volume_tp);
lamp_L *= volume_tp;
}
#endif
if (!(state->flag & PATH_RAY_MIS_SKIP)) {
/* multiple importance sampling, get regular light pdf,
* and compute weight with respect to BSDF pdf */
/* multiply with light picking probablity to pdf */
ls.pdf *= light_distribution_pdf(
kg, emission_sd->P_pick, emission_sd->V_pick, emission_sd->t_pick, ~ls.lamp, -1);
float mis_weight = power_heuristic(state->ray_pdf, ls.pdf);
lamp_L *= mis_weight;
}
@@ -326,10 +332,14 @@ ccl_device_noinline_cpu float3 indirect_background(KernelGlobals *kg,
/* Background MIS weights. */
# ifdef __BACKGROUND_MIS__
/* Check if background light exists or if we should skip pdf. */
/* consider shading point at previous non-transparent bounce */
float3 P_pick = ray->P - state->ray_t * ray->D;
if (!(state->flag & PATH_RAY_MIS_SKIP) && kernel_data.background.use_mis) {
/* multiple importance sampling, get background light pdf for ray
* direction, and compute weight with respect to BSDF pdf */
float pdf = background_light_pdf(kg, ray->P, ray->D);
float pdf = background_light_pdf(kg, P_pick, ray->D);
float mis_weight = power_heuristic(state->ray_pdf, pdf);
return L * mis_weight;

View File

@@ -35,8 +35,31 @@ typedef struct LightSample {
LightType type; /* type of light */
} LightSample;
/* Updates the position and normal used to pick a light for direct lighting.
*
* The importance calculation for the light tree is different for scattering events on a surface
* and in a volume. For volume events we need to know the point where the ray started and the point
* it scattered. To do this we keep track of the ray direction and length. For surface events we
* need the normal at the surface. In this case we set the ray length to -1 to mark that surface
* importance sampling should be used.
*/
ccl_device void kernel_update_light_picking(ShaderData *sd, Ray *ray)
{
if (ray) {
sd->P_pick = ray->P;
sd->V_pick = ray->D;
sd->t_pick = ray->t;
}
else {
sd->P_pick = sd->P;
sd->V_pick = sd->N;
sd->t_pick = -1.0f;
}
}
/* Regular Light */
/* returns the PDF of sampling a point on this lamp */
ccl_device_inline bool lamp_light_sample(
KernelGlobals *kg, int lamp, float randu, float randv, float3 P, LightSample *ls)
{
@@ -153,8 +176,6 @@ ccl_device_inline bool lamp_light_sample(
}
}
ls->pdf *= kernel_data.integrator.pdf_lights;
return (ls->pdf > 0.0f);
}
@@ -291,8 +312,6 @@ ccl_device bool lamp_light_eval(
return false;
}
ls->pdf *= kernel_data.integrator.pdf_lights;
return true;
}
@@ -328,18 +347,15 @@ ccl_device_inline bool triangle_world_space_vertices(
return has_motion;
}
ccl_device_inline float triangle_light_pdf_area(KernelGlobals *kg,
const float3 Ng,
const float3 I,
float t)
ccl_device_inline float triangle_light_pdf_area(
KernelGlobals *kg, const float3 Ng, const float3 I, float t, float triangle_area)
{
float pdf = kernel_data.integrator.pdf_triangles;
float cos_pi = fabsf(dot(Ng, I));
if (cos_pi == 0.0f)
return 0.0f;
return t * t * pdf / cos_pi;
return t * t / (cos_pi * triangle_area);
}
ccl_device_forceinline float triangle_light_pdf(KernelGlobals *kg, ShaderData *sd, float t)
@@ -349,7 +365,7 @@ ccl_device_forceinline float triangle_light_pdf(KernelGlobals *kg, ShaderData *s
* to the length of the edges of the triangle. */
float3 V[3];
bool has_motion = triangle_world_space_vertices(kg, sd->object, sd->prim, sd->time, V);
triangle_world_space_vertices(kg, sd->object, sd->prim, sd->time, V);
const float3 e0 = V[1] - V[0];
const float3 e1 = V[2] - V[0];
@@ -380,34 +396,12 @@ ccl_device_forceinline float triangle_light_pdf(KernelGlobals *kg, ShaderData *s
return 0.0f;
}
else {
float area = 1.0f;
if (has_motion) {
/* get the center frame vertices, this is what the PDF was calculated from */
triangle_world_space_vertices(kg, sd->object, sd->prim, -1.0f, V);
area = triangle_area(V[0], V[1], V[2]);
}
else {
area = 0.5f * len(N);
}
const float pdf = area * kernel_data.integrator.pdf_triangles;
return pdf / solid_angle;
return 1.0f / solid_angle;
}
}
else {
float pdf = triangle_light_pdf_area(kg, sd->Ng, sd->I, t);
if (has_motion) {
const float area = 0.5f * len(N);
if (UNLIKELY(area == 0.0f)) {
return 0.0f;
}
/* scale the PDF.
* area = the area the sample was taken from
* area_pre = the are from which pdf_triangles was calculated from */
triangle_world_space_vertices(kg, sd->object, sd->prim, -1.0f, V);
const float area_pre = triangle_area(V[0], V[1], V[2]);
pdf = pdf * area_pre / area;
}
return pdf;
const float area = 0.5f * len(N);
return triangle_light_pdf_area(kg, sd->Ng, sd->I, t, area);
}
}
@@ -425,7 +419,7 @@ ccl_device_forceinline void triangle_light_sample(KernelGlobals *kg,
* to the length of the edges of the triangle. */
float3 V[3];
bool has_motion = triangle_world_space_vertices(kg, object, prim, time, V);
triangle_world_space_vertices(kg, object, prim, time, V);
const float3 e0 = V[1] - V[0];
const float3 e1 = V[2] - V[0];
@@ -434,7 +428,6 @@ ccl_device_forceinline void triangle_light_sample(KernelGlobals *kg,
const float3 N0 = cross(e0, e1);
float Nl = 0.0f;
ls->Ng = safe_normalize_len(N0, &Nl);
float area = 0.5f * Nl;
/* flip normal if necessary */
const int object_flag = kernel_tex_fetch(__object_flag, object);
@@ -537,13 +530,7 @@ ccl_device_forceinline void triangle_light_sample(KernelGlobals *kg,
return;
}
else {
if (has_motion) {
/* get the center frame vertices, this is what the PDF was calculated from */
triangle_world_space_vertices(kg, object, prim, -1.0f, V);
area = triangle_area(V[0], V[1], V[2]);
}
const float pdf = area * kernel_data.integrator.pdf_triangles;
ls->pdf = pdf / solid_angle;
ls->pdf = 1.0f / solid_angle;
}
}
else {
@@ -564,20 +551,53 @@ ccl_device_forceinline void triangle_light_sample(KernelGlobals *kg,
ls->P = u * V[0] + v * V[1] + t * V[2];
/* compute incoming direction, distance and pdf */
ls->D = normalize_len(ls->P - P, &ls->t);
ls->pdf = triangle_light_pdf_area(kg, ls->Ng, -ls->D, ls->t);
if (has_motion && area != 0.0f) {
/* scale the PDF.
* area = the area the sample was taken from
* area_pre = the are from which pdf_triangles was calculated from */
triangle_world_space_vertices(kg, object, prim, -1.0f, V);
const float area_pre = triangle_area(V[0], V[1], V[2]);
ls->pdf = ls->pdf * area_pre / area;
}
float area = 0.5f * Nl;
ls->pdf = triangle_light_pdf_area(kg, ls->Ng, -ls->D, ls->t, area);
ls->u = u;
ls->v = v;
}
}
/* chooses either to sample the light tree, distant or background lights by
* sampling a CDF based on energy */
ccl_device int light_group_distribution_sample(KernelGlobals *kg, float *randu)
{
/* This is basically std::upper_bound as used by pbrt, to find a point light or
* triangle to emit from, proportional to area. a good improvement would be to
* also sample proportional to power, though it's not so well defined with
* arbitrary shaders. */
const int num_groups = LIGHTGROUP_NUM;
int first = 0;
int len = num_groups + 1;
float r = *randu;
// todo: refactor this into its own function. It is used in several places
while (len > 0) {
int half_len = len >> 1;
int middle = first + half_len;
if (r < kernel_tex_fetch(__light_group_sample_cdf, middle)) {
len = half_len;
}
else {
first = middle + 1;
len = len - half_len - 1;
}
}
/* Clamping should not be needed but float rounding errors seem to
* make this fail on rare occasions. */
int index = clamp(first - 1, 0, num_groups - 1);
/* Rescale to reuse random number. this helps the 2D samples within
* each area light be stratified as well. */
float distr_min = kernel_tex_fetch(__light_group_sample_cdf, index);
float distr_max = kernel_tex_fetch(__light_group_sample_cdf, index + 1);
*randu = (r - distr_min) / (distr_max - distr_min);
return index;
}
/* Light Distribution */
ccl_device int light_distribution_sample(KernelGlobals *kg, float *randu)
@@ -623,22 +643,691 @@ ccl_device_inline bool light_select_reached_max_bounces(KernelGlobals *kg, int i
return (bounce > kernel_tex_fetch(__lights, index).max_bounces);
}
ccl_device_noinline bool light_sample(KernelGlobals *kg,
int lamp,
float randu,
float randv,
float time,
float3 P,
int bounce,
LightSample *ls)
/*
* Finds the solid angle of the smallest cone with vertex P that contains the bounding box. If P is
* inside the bounding box light can be in any direction so use the entire sphere.
*/
ccl_device float calc_bbox_solid_angle(float3 P,
float3 centroid_to_P_dir,
float3 bboxMin,
float3 bboxMax)
{
if (P.x < bboxMax.x && P.y < bboxMax.y && P.z < bboxMax.z && P.x > bboxMin.x &&
P.y > bboxMin.y && P.z > bboxMin.z) {
/* P is inside bounding box */
return M_PI_F;
}
else {
/* Find the smallest cone that contains the bounding box by checking which bbox vertex is
* farthest out. If we use a bounding sphere we get a too big cone. For example consider a long
* skinny bbox oriented with P next to one of the small sides. */
float theta_u = 0;
float3 corners[8];
corners[0] = bboxMin;
corners[1] = make_float3(bboxMin.x, bboxMin.y, bboxMax.z);
corners[2] = make_float3(bboxMin.x, bboxMax.y, bboxMin.z);
corners[3] = make_float3(bboxMin.x, bboxMax.y, bboxMax.z);
corners[4] = make_float3(bboxMax.x, bboxMin.y, bboxMin.z);
corners[5] = make_float3(bboxMax.x, bboxMin.y, bboxMax.z);
corners[6] = make_float3(bboxMax.x, bboxMax.y, bboxMin.z);
corners[7] = bboxMax;
for (int i = 0; i < 8; ++i) {
float3 P_to_corner = normalize(P - corners[i]);
const float cos_theta_u = dot(-centroid_to_P_dir, P_to_corner);
theta_u = fmaxf(fast_acosf(cos_theta_u), theta_u);
}
return theta_u;
}
}
/* calculates the importance metric for the given node and shading point P
* If t_max is negative assume that V is the normal vector and use surface importance calculation.
* Otherwise assume that V is the ray direction and use volume importance.
*/
ccl_device float calc_importance(KernelGlobals *kg,
float3 P,
float3 V,
float t_max,
float3 bboxMax,
float3 bboxMin,
float theta_o,
float theta_e,
float3 axis,
float energy,
float3 centroid)
{
if (t_max < 0.0f) {
/* eq. 3 */
const float3 centroid_to_P = P - centroid;
const float3 centroid_to_P_dir = normalize(centroid_to_P);
const float r2 = len_squared(bboxMax - centroid);
float d2 = len_squared(centroid_to_P);
/* based on comment in the implementation details of the paper */
const bool splitting = kernel_data.integrator.splitting_threshold != 0.0f;
if (!splitting) {
d2 = fmaxf(d2, r2 * 0.25f);
}
/* "theta_u captures the solid angle of the entire box" */
float theta_u = calc_bbox_solid_angle(P, centroid_to_P_dir, bboxMin, bboxMax);
/* cos(theta') */
float cos_theta = dot(axis, centroid_to_P_dir);
const float theta = fast_acosf(cos_theta);
const float theta_prime = fmaxf(theta - theta_o - theta_u, 0.0f);
if (theta_prime >= theta_e) {
return 0.0f;
}
const float cos_theta_prime = fast_cosf(theta_prime);
/* f_a|cos(theta'_i)| -- diffuse approximation */
const float cos_theta_i = dot(V, -centroid_to_P_dir);
const float theta_i = fast_acosf(cos_theta_i);
const float theta_i_prime = fmaxf(theta_i - theta_u, 0.0f);
const float cos_theta_i_prime = fast_cosf(theta_i_prime);
const float abs_cos_theta_i_prime = fabsf(cos_theta_i_prime);
/* doing something similar to bsdf_diffuse_eval_reflect() */
/* TODO: Use theta_i or theta_i_prime here? */
const float f_a = fmaxf(cos_theta_i_prime, 0.0f) * M_1_PI_F;
return f_a * abs_cos_theta_i_prime * energy * cos_theta_prime / d2;
}
else {
const float3 p_to_c = centroid - P;
/* find closest point to centroid */
const float t = fminf(t_max, fmaxf(0.0f, dot(V, p_to_c)));
const float3 geometry_point = P + V * t;
const float d_min = len(centroid - geometry_point);
const float3 V0 = normalize(P - centroid);
const float3 V1 = normalize(P + V * fminf(t_max, 1e12f) - centroid);
const float3 O0 = V0;
float3 O1, O2;
make_orthonormals_tangent(O0, V1, &O1, &O2);
const float b_max = fmaxf(dot(V0, axis), dot(V1, axis));
const float O0_dot_a = dot(O0, axis);
const float O1_dot_a = dot(O1, axis);
const float cos_phi_o = O0_dot_a / sqrtf(O1_dot_a * O1_dot_a + O0_dot_a * O0_dot_a); /* Eq 5 */
const float sin_phi_o = sqrtf(1 - cos_phi_o * cos_phi_o);
const float3 v = O0 * cos_phi_o + O1 * sin_phi_o;
/* Eq 6 */
float cos_theta_min;
if (O1_dot_a < 0 || dot(V0, V1) > cos_phi_o) {
cos_theta_min = b_max;
}
else {
cos_theta_min = dot(axis, v);
}
float theta_min = fast_acosf(cos_theta_min);
float theta_u = calc_bbox_solid_angle(
geometry_point, normalize(geometry_point - centroid), bboxMin, bboxMax);
float theta_prime = fmaxf(theta_min - theta_o - theta_u, 0);
if (theta_prime >= theta_e) {
return 0;
}
return energy * fast_cosf(theta_prime) / d_min; /* Eq 7 */
}
}
/* the energy, spatial and orientation bounds for the light are loaded and decoded
* and then this information is used to calculate the importance for this light.
* This function is used to calculate the importance for a light in a node
* containing several lights. */
ccl_device float calc_light_importance(
KernelGlobals *kg, float3 P, float3 V, float t_max, int node_offset, int light_offset)
{
/* find offset into light_tree_leaf_emitters array */
int first_emitter = kernel_tex_fetch(__leaf_to_first_emitter, node_offset / 4);
kernel_assert(first_emitter != -1);
int offset = first_emitter + light_offset * 3;
/* get relevant information to be able to calculate the importance */
const float4 data0 = kernel_tex_fetch(__light_tree_leaf_emitters, offset + 0);
const float4 data1 = kernel_tex_fetch(__light_tree_leaf_emitters, offset + 1);
const float4 data2 = kernel_tex_fetch(__light_tree_leaf_emitters, offset + 2);
/* decode data for this light */
const float3 bbox_min = make_float3(data0.x, data0.y, data0.z);
const float3 bbox_max = make_float3(data0.w, data1.x, data1.y);
const float theta_o = data1.z;
const float theta_e = data1.w;
const float3 axis = make_float3(data2.x, data2.y, data2.z);
const float energy = data2.w;
const float3 centroid = 0.5f * (bbox_max + bbox_min);
return calc_importance(
kg, P, V, t_max, bbox_max, bbox_min, theta_o, theta_e, axis, energy, centroid);
}
/* the combined energy, spatial and orientation bounds for all the lights for the
* given node are loaded and decoded and then this information is used to
* calculate the importance for this node. */
ccl_device float calc_node_importance(
KernelGlobals *kg, float3 P, float3 V, float t_max, int node_offset)
{
/* load the data for this node */
const float4 node0 = kernel_tex_fetch(__light_tree_nodes, node_offset + 0);
const float4 node1 = kernel_tex_fetch(__light_tree_nodes, node_offset + 1);
const float4 node2 = kernel_tex_fetch(__light_tree_nodes, node_offset + 2);
const float4 node3 = kernel_tex_fetch(__light_tree_nodes, node_offset + 3);
/* decode the data so it can be used to calculate the importance */
const float energy = node0.x;
const float3 bbox_min = make_float3(node1.x, node1.y, node1.z);
const float3 bbox_max = make_float3(node1.w, node2.x, node2.y);
const float theta_o = node2.z;
const float theta_e = node2.w;
const float3 axis = make_float3(node3.x, node3.y, node3.z);
const float3 centroid = 0.5f * (bbox_max + bbox_min);
return calc_importance(
kg, P, V, t_max, bbox_max, bbox_min, theta_o, theta_e, axis, energy, centroid);
}
/* given a node offset, this function loads and decodes the minimum amount of
* data needed for a the given node to be able to only either identify if it is
* a leaf node or how to find its two children
*
* child_o ffset is an offset into the nodes array to this nodes right child. the
* left child has index node_offset+4.
* distribution_id corresponds to an offset into the distribution array for the
* first light contained in this node. num_emitters is how many lights there are
* in this node. */
ccl_device void update_node(
KernelGlobals *kg, int node_offset, int *child_offset, int *distribution_id, int *num_emitters)
{
float4 node = kernel_tex_fetch(__light_tree_nodes, node_offset);
(*child_offset) = __float_as_int(node.y);
(*distribution_id) = __float_as_int(node.z);
(*num_emitters) = __float_as_int(node.w);
}
/* picks one of the distant lights and computes the probability of picking it */
ccl_device void light_distant_sample(
KernelGlobals *kg, float3 P, float *randu, int *index, float *pdf)
{
light_distribution_sample(kg, randu); // rescale random number
/* choose one of the distant lights randomly */
int num_distant = kernel_data.integrator.num_distant_lights;
int light = min((int)(*randu * (float)num_distant), num_distant - 1);
/* This assumes the distant lights are next to each other in the
* distribution array starting at distant_lights_offset. */
int distant_lights_offset = kernel_data.integrator.distant_lights_offset;
*index = light + distant_lights_offset;
*pdf = kernel_data.integrator.inv_num_distant_lights;
}
/* picks the background light and sets the probability of picking it */
ccl_device void light_background_sample(
KernelGlobals *kg, float3 P, float *randu, int *index, float *pdf)
{
*index = kernel_tex_fetch(__lamp_to_distribution, kernel_data.integrator.background_light_index);
*pdf = 1.0f;
}
/* picks a light from the light tree and returns its index and the probability of
* picking this light. */
ccl_device void light_tree_sample(KernelGlobals *kg,
float3 P,
float3 V,
float t_max,
float *randu,
int *index,
float *pdf_factor)
{
int sampled_index = -1;
*pdf_factor = 1.0f;
int offset = 0;
int right_child_offset, distribution_id, num_emitters;
do {
/* read in first part of node of light tree */
update_node(kg, offset, &right_child_offset, &distribution_id, &num_emitters);
/* Found a leaf - Choose which light to use */
if (right_child_offset == -1) { // Found a leaf
if (num_emitters == 1) {
sampled_index = distribution_id;
}
else {
/* At a leaf node containing several lights. Pick one of these
* by creating and sampling a CDF based on the importance metric.
*
* The number of lights in this leaf node is not known at compile
* time and dynamic allocation is not allowed on the GPU, so
* some more computations have to be done instead.
* (TODO: Could we allocate a fixed array of the same size as
* the maximum allowed number of lights per node in the
* construction algorithm? i.e. max_lights_in_node.)
*
* First, the total importance of all the lights are calculated.
* Then, a linear loop over the lights are done where the
* current CDF value is calculated. This loop can stop as soon
* as the random value used to sample the CDF is less than the
* current CDF value. The sampled light has index i-1 if i is
* the iteration counter of the loop over the lights. This is
* similar to for example light the old light_distribution_sample()
* except not having an array to store the CDF in. */
float sum = 0.0f;
for (int i = 0; i < num_emitters; ++i) {
sum += calc_light_importance(kg, P, V, t_max, offset, i);
}
if (sum == 0.0f) {
*pdf_factor = 0.0f;
return;
}
float sum_inv = 1.0f / sum;
float cdf_L = 0.0f;
float cdf_R = 0.0f;
float prob = 0.0f;
int light = 0;
for (int i = 1; i < num_emitters + 1; ++i) {
prob = calc_light_importance(kg, P, V, t_max, offset, i - 1) * sum_inv;
cdf_R = cdf_L + prob;
if (*randu < cdf_R) {
light = i - 1;
break;
}
cdf_L = cdf_R;
}
sampled_index = distribution_id + light;
*pdf_factor *= prob;
/* rescale random number */
*randu = (*randu - cdf_L) / (cdf_R - cdf_L);
}
break;
}
else { // Interior node, pick left or right randomly
/* calculate probability of going down left node */
int child_offsetL = offset + 4;
int child_offsetR = 4 * right_child_offset;
float I_L = calc_node_importance(kg, P, V, t_max, child_offsetL);
float I_R = calc_node_importance(kg, P, V, t_max, child_offsetR);
if ((I_L == 0.0f) && (I_R == 0.0f)) {
*pdf_factor = 0.0f;
break;
}
float P_L = I_L / (I_L + I_R);
/* choose which node to go down */
if (*randu <= P_L) { // Going down left node
/* rescale random number */
*randu = *randu / P_L;
offset = child_offsetL;
*pdf_factor *= P_L;
}
else { // Going down right node
/* rescale random number */
*randu = (*randu * (I_L + I_R) - I_L) / I_R;
offset = child_offsetR;
*pdf_factor *= 1.0f - P_L;
}
}
} while (true);
*index = sampled_index;
}
/* converts from an emissive triangle index to the corresponding
* light distribution index. */
ccl_device int triangle_to_distribution(KernelGlobals *kg, int triangle_id, int object_id)
{
/* binary search to find triangle_id which then gives distribution_id */
/* equivalent to implementation of std::lower_bound */
/* todo: of complexity log(N) now. could be made constant with a hash table? */
/* __triangle_to_distribution is an array of uints of the format below:
* [triangle_id0, object_id0, distribution_id0, triangle_id1,... ]
* where e.g. [triangle_id0,object_id0] corresponds to distribution id
* distribution_id0
*/
int first = 0;
int last = kernel_data.integrator.num_triangle_lights;
int count = last - first;
int middle, step;
while (count > 0) {
step = count / 2;
middle = first + step;
int triangle = kernel_tex_fetch(__triangle_to_distribution, middle * 3);
if (triangle < triangle_id) {
first = middle + 1;
count -= step + 1;
}
else
count = step;
}
/* If instancing then we can have several triangles with the same triangle_id
* so loop over object_id too. */
/* todo: do a binary search here too if many instances */
while (true) {
int object = kernel_tex_fetch(__triangle_to_distribution, first * 3 + 1);
if (object == object_id)
break;
++first;
}
kernel_assert(kernel_tex_fetch(__triangle_to_distribution, first * 3) == triangle_id);
return kernel_tex_fetch(__triangle_to_distribution, first * 3 + 2);
}
/* Decides whether to go down both childen or only one in the tree traversal.
* The split heuristic is based on the variance of the lighting within the node.
* There are two types of variances that are considered: variance in energy and
* in the distance term 1/d^2. The variance in energy is pre-computed on the
* host but the distance term is calculated here. These variances are then
* combined and normalized to get the final splitting heuristic. High variance
* leads to a lower splitting heuristic which leads to more splits during the
* traversal. */
ccl_device bool split(KernelGlobals *kg, float3 P, int node_offset)
{
/* early exists if never/always splitting */
const float threshold = kernel_data.integrator.splitting_threshold;
if (threshold == 0.0f) {
return false;
}
else if (threshold == 1.0f) {
return true;
}
/* extract bounding box of cluster */
const float4 node1 = kernel_tex_fetch(__light_tree_nodes, node_offset + 1);
const float4 node2 = kernel_tex_fetch(__light_tree_nodes, node_offset + 2);
const float3 bboxMin = make_float3(node1.x, node1.y, node1.z);
const float3 bboxMax = make_float3(node1.w, node2.x, node2.y);
/* if P is inside bounding sphere then split */
const float3 centroid = 0.5f * (bboxMax + bboxMin);
const float radius_squared = len_squared(bboxMax - centroid);
const float dist_squared = len_squared(centroid - P);
if (dist_squared <= radius_squared) {
return true;
}
/* eq. 8 & 9 */
/* the integral in eq. 8 requires us to know the interval the distance can
* be in: [a,b]. This is found by considering a bounding sphere around the
* bounding box of the node and "a" then becomes the smallest distance to
* this sphere and "b" becomes the largest. */
const float radius = sqrt(radius_squared);
const float dist = sqrt(dist_squared);
const float a = dist - radius;
const float b = dist + radius;
const float g_mean = 1.0f / (a * b);
const float g_mean_squared = g_mean * g_mean;
const float a3 = a * a * a;
const float b3 = b * b * b;
const float g_variance = (b3 - a3) / (3.0f * (b - a) * a3 * b3) - g_mean_squared;
/* eq. 10 */
const float4 node0 = kernel_tex_fetch(__light_tree_nodes, node_offset);
const float4 node3 = kernel_tex_fetch(__light_tree_nodes, node_offset + 3);
const float energy = node0.x;
const float e_variance = node3.w;
const float num_emitters = (float)__float_as_int(node0.w);
const float num_emitters_squared = num_emitters * num_emitters;
const float e_mean = energy / num_emitters;
const float e_mean_squared = e_mean * e_mean;
const float variance = (e_variance * (g_variance + g_mean_squared) +
e_mean_squared * g_variance) *
num_emitters_squared;
/* normalize the variance heuristic to be within [0,1]. Note that high
* variance corresponds to a low normalized variance. To give an idea of
* how this normalization function looks like:
* variance: 0 1 10 100 1000 10000 100000
* normalized variance: 1 0.8 0.7 0.5 0.4 0.3 0.2 */
const float variance_normalized = sqrt(sqrt(1.0f / (1.0f + sqrt(variance))));
return variance_normalized < threshold;
}
/* given a light in the form of a distribution id, this function computes the
* the probability of picking it using the light tree. this mimics the
* probability calculations in accum_light_tree_contribution()
*
* the nodes array contains all the nodes of the tree and each interior node
* has its left child directly after it in the nodes array and the right child
* is also after it at the second_child_offset.
*
* Given the structure of the nodes array we can find the path from the root
* to the leaf node the given light belongs to as follows:
*
* 1. Find the offset of the leaf node the given light belongs to.
* 2. Traverse the tree in a top-down manner where the decision to go down the
* left or right child is determined as follows:
* If the node we are looking for has a lower offset than the right child
* then it belongs to a node between the current node and the right child.
* That is, we should go down the left node. Otherwise, the right node.
* This is done recursively until the leaf node is found.
* 3. Before going down the left or right child:
* a) If we are splitting then the probability is not affected.
* b) If we are not splitting then the probability is multiplied by the
* probability of choosing this particular child node.
*/
ccl_device float light_tree_pdf(KernelGlobals *kg,
float3 P,
float3 V,
float t_max,
int distribution_id,
int offset,
float pdf,
bool can_split)
{
/* find mapping from distribution_id to node_id */
int node_id = kernel_tex_fetch(__light_distribution_to_node, distribution_id);
/* read in first part of node of light tree */
int right_child_offset, first_distribution_id, num_emitters;
update_node(kg, offset, &right_child_offset, &first_distribution_id, &num_emitters);
while (right_child_offset != -1) {
int child_offsetL = offset + 4;
int child_offsetR = 4 * right_child_offset;
/* choose whether to go down both(split) or only one of the children */
if (can_split && split(kg, P, offset)) {
/* go down to the child node that is an ancestor of this node_id
* without changing the probability since we split here */
if (node_id < child_offsetR) {
offset = child_offsetL;
}
else {
offset = child_offsetR;
}
return light_tree_pdf(kg, P, V, t_max, distribution_id, offset, pdf, true);
}
else {
/* go down one of the child nodes */
/* evaluate the importance of each of the child nodes */
float I_L = calc_node_importance(kg, P, V, t_max, child_offsetL);
float I_R = calc_node_importance(kg, P, V, t_max, child_offsetR);
if ((I_L == 0.0f) && (I_R == 0.0f)) {
return 0.0f;
}
/* calculate the probability of going down the left node */
float P_L = I_L / (I_L + I_R);
/* choose which node to go down */
if (node_id < child_offsetR) {
offset = child_offsetL;
pdf *= P_L;
}
else {
offset = child_offsetR;
pdf *= 1.0f - P_L;
}
update_node(kg, offset, &right_child_offset, &first_distribution_id, &num_emitters);
}
}
/* if there are several emitters in this leaf then pick one of them */
if (num_emitters > 1) {
/* the case of being a light inside a leaf node with several lights.
* during sampling, a CDF is created based on importance, so here
* the probability of sampling this light using the CDF has to be
* computed. This is done by dividing the importance of this light
* by the total sum of the importance of all lights in the leaf. */
float sum = 0.0f;
for (int i = 0; i < num_emitters; ++i) {
sum += calc_light_importance(kg, P, V, t_max, offset, i);
}
if (sum == 0.0f) {
return 0.0f;
}
pdf *= calc_light_importance(
kg, P, V, t_max, offset, distribution_id - first_distribution_id) /
sum;
}
return pdf;
}
/* computes the the probability of picking the given light out of all lights.
* this mimics the probability calculations in light_distribution_sample() */
ccl_device float light_distribution_pdf(
KernelGlobals *kg, float3 P, float3 V, float t_max, int prim_id, int object_id)
{
/* convert from triangle/lamp to light distribution */
int distribution_id;
if (prim_id >= 0) { // Triangle_id = prim_id
distribution_id = triangle_to_distribution(kg, prim_id, object_id);
}
else { // Lamp
int lamp_id = -prim_id - 1;
distribution_id = kernel_tex_fetch(__lamp_to_distribution, lamp_id);
}
kernel_assert((distribution_id >= 0) &&
(distribution_id < kernel_data.integrator.num_distribution));
/* compute picking pdf for this light */
if (kernel_data.integrator.use_light_tree) {
/* find out which group of lights to sample */
int group;
if (prim_id >= 0) {
group = LIGHTGROUP_TREE;
}
else {
int lamp = -prim_id - 1;
int light_type = kernel_tex_fetch(__lights, lamp).type;
if (light_type == LIGHT_DISTANT) {
group = LIGHTGROUP_DISTANT;
}
else if (light_type == LIGHT_BACKGROUND) {
group = LIGHTGROUP_BACKGROUND;
}
else {
group = LIGHTGROUP_TREE;
}
}
/* get probabilty to sample this group of lights */
float group_prob = kernel_tex_fetch(__light_group_sample_prob, group);
float pdf = group_prob;
if (group == LIGHTGROUP_TREE) {
pdf *= light_tree_pdf(kg, P, V, t_max, distribution_id, 0, 1.0f, true);
}
else if (group == LIGHTGROUP_DISTANT) {
pdf *= kernel_data.integrator.inv_num_distant_lights;
}
else if (group == LIGHTGROUP_BACKGROUND) {
/* there is only one background light so nothing to do here */
}
else {
kernel_assert(false);
}
return pdf;
}
else {
const ccl_global KernelLightDistribution *kdistribution = &kernel_tex_fetch(
__light_distribution, distribution_id);
return kdistribution->area * kernel_data.integrator.pdf_inv_totarea;
}
}
/* picks a light and returns its index and the probability of picking it */
ccl_device void light_distribution_sample(
KernelGlobals *kg, float3 P, float3 V, float t_max, float *randu, int *index, float *pdf)
{
if (kernel_data.integrator.use_light_tree) {
/* sample light type distribution */
int group = light_group_distribution_sample(kg, randu);
float group_prob = kernel_tex_fetch(__light_group_sample_prob, group);
if (group == LIGHTGROUP_TREE) {
light_tree_sample(kg, P, V, t_max, randu, index, pdf);
}
else if (group == LIGHTGROUP_DISTANT) {
light_distant_sample(kg, P, randu, index, pdf);
}
else if (group == LIGHTGROUP_BACKGROUND) {
light_background_sample(kg, P, randu, index, pdf);
}
else {
kernel_assert(false);
}
*pdf *= group_prob;
}
else { // Sample light distribution CDF
*index = light_distribution_sample(kg, randu);
const ccl_global KernelLightDistribution *kdistribution = &kernel_tex_fetch(
__light_distribution, *index);
*pdf = kdistribution->area * kernel_data.integrator.pdf_inv_totarea;
}
}
/* picks a point on a given light and computes the probability of picking this point*/
ccl_device void light_point_sample(KernelGlobals *kg,
int lamp,
float randu,
float randv,
float time,
float3 P,
int bounce,
int distribution_id,
LightSample *ls)
{
if (lamp < 0) {
/* sample index */
int index = light_distribution_sample(kg, &randu);
/* fetch light data */
/* fetch light data and compute rest of light pdf */
const ccl_global KernelLightDistribution *kdistribution = &kernel_tex_fetch(
__light_distribution, index);
__light_distribution, distribution_id);
int prim = kdistribution->prim;
if (prim >= 0) {
@@ -647,17 +1336,54 @@ ccl_device_noinline bool light_sample(KernelGlobals *kg,
triangle_light_sample(kg, prim, object, randu, randv, time, ls, P);
ls->shader |= shader_flag;
return (ls->pdf > 0.0f);
return;
}
lamp = -prim - 1;
}
if (UNLIKELY(light_select_reached_max_bounces(kg, lamp, bounce))) {
ls->pdf = 0.0f;
return;
}
if (!lamp_light_sample(kg, lamp, randu, randv, P, ls)) {
ls->pdf = 0.0f;
return;
}
}
/* picks a light and then picks a point on the light and computes the
* probability of doing so. V and t_max are only used when using light tree for sampling. If t_max
* is negative V should be the normal vector at P. Otherwise V should be the direction of the light
* ray starting at P.
*/
ccl_device_noinline bool light_sample(KernelGlobals *kg,
int lamp,
float randu,
float randv,
float time,
float3 P,
float3 V,
float t_max,
int bounce,
LightSample *ls)
{
/* pick a light and compute the probability of picking this light */
float pdf_factor = 0.0f;
int index = -1;
light_distribution_sample(kg, P, V, t_max, &randu, &index, &pdf_factor);
if (pdf_factor == 0.0f) {
return false;
}
return lamp_light_sample(kg, lamp, randu, randv, P, ls);
/* pick a point on the light and the probability of picking this point */
light_point_sample(kg, lamp, randu, randv, time, P, bounce, index, ls);
/* combine pdfs */
ls->pdf *= pdf_factor;
return (ls->pdf > 0.0f);
}
ccl_device_inline int light_select_num_samples(KernelGlobals *kg, int index)

View File

@@ -99,6 +99,7 @@ ccl_device_forceinline void kernel_path_lamp_emission(KernelGlobals *kg,
Ray light_ray ccl_optional_struct_init;
light_ray.P = ray->P - state->ray_t * ray->D;
state->ray_t += isect->t;
light_ray.D = ray->D;
light_ray.t = state->ray_t;
@@ -185,6 +186,8 @@ ccl_device_forceinline VolumeIntegrateResult kernel_path_volume(KernelGlobals *k
shader_setup_from_volume(kg, sd, &volume_ray);
kernel_volume_decoupled_record(kg, state, &volume_ray, sd, &volume_segment, step_size);
kernel_update_light_picking(sd, &volume_ray);
volume_segment.sampling_method = sampling_method;
/* emission */
@@ -231,6 +234,8 @@ ccl_device_forceinline VolumeIntegrateResult kernel_path_volume(KernelGlobals *k
VolumeIntegrateResult result = kernel_volume_integrate(
kg, state, sd, &volume_ray, L, throughput, step_size);
kernel_update_light_picking(sd, NULL);
# ifdef __VOLUME_SCATTER__
if (result == VOLUME_PATH_SCATTERED) {
/* direct lighting */
@@ -390,6 +395,7 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
/* path iteration */
for (;;) {
/* Find intersection with objects in scene. */
Intersection isect;
bool hit = kernel_path_scene_intersect(kg, state, ray, &isect, L);
@@ -408,6 +414,7 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
else if (result == VOLUME_PATH_MISSED) {
break;
}
# endif /* __VOLUME__*/
/* Shade background. */
@@ -453,6 +460,8 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
throughput /= probability;
}
kernel_update_light_picking(sd, NULL);
# ifdef __DENOISING_FEATURES__
kernel_update_denoising_features(kg, sd, state, L);
# endif
@@ -518,7 +527,6 @@ ccl_device_forceinline void kernel_path_integrate(KernelGlobals *kg,
/* Shader data memory used for both volumes and surfaces, saves stack space. */
ShaderData sd;
# ifdef __SUBSURFACE__
SubsurfaceIndirectRays ss_indirect;
kernel_path_subsurface_init_indirect(&ss_indirect);
@@ -528,6 +536,7 @@ ccl_device_forceinline void kernel_path_integrate(KernelGlobals *kg,
/* path iteration */
for (;;) {
/* Find intersection with objects in scene. */
Intersection isect;
bool hit = kernel_path_scene_intersect(kg, state, ray, &isect, L);
@@ -590,6 +599,8 @@ ccl_device_forceinline void kernel_path_integrate(KernelGlobals *kg,
throughput /= probability;
}
kernel_update_light_picking(&sd, NULL);
# ifdef __DENOISING_FEATURES__
kernel_update_denoising_features(kg, &sd, state, L);
# endif

View File

@@ -102,6 +102,8 @@ ccl_device_forceinline void kernel_branched_path_volume(KernelGlobals *kg,
shader_setup_from_volume(kg, sd, &volume_ray);
kernel_volume_decoupled_record(kg, state, &volume_ray, sd, &volume_segment, step_size);
kernel_update_light_picking(sd, &volume_ray);
/* direct light sampling */
if (volume_segment.closure_flag & SD_SCATTER) {
volume_segment.sampling_method = volume_stack_sampling_method(kg, state->volume_stack);
@@ -134,6 +136,9 @@ ccl_device_forceinline void kernel_branched_path_volume(KernelGlobals *kg,
if (result == VOLUME_PATH_SCATTERED &&
kernel_path_volume_bounce(kg, sd, &tp, &ps, &L->state, &pray)) {
indirect_sd->P_pick = sd->P_pick;
indirect_sd->V_pick = sd->V_pick;
indirect_sd->t_pick = sd->t_pick;
kernel_path_indirect(kg, indirect_sd, emission_sd, &pray, tp * num_samples_inv, &ps, L);
/* for render passes, sum and reset indirect light pass variables
@@ -173,6 +178,8 @@ ccl_device_forceinline void kernel_branched_path_volume(KernelGlobals *kg,
VolumeIntegrateResult result = kernel_volume_integrate(
kg, &ps, sd, &volume_ray, L, &tp, step_size);
kernel_update_light_picking(sd, &volume_ray);
# ifdef __VOLUME_SCATTER__
if (result == VOLUME_PATH_SCATTERED) {
/* todo: support equiangular, MIS and all light sampling.
@@ -180,6 +187,9 @@ ccl_device_forceinline void kernel_branched_path_volume(KernelGlobals *kg,
kernel_path_volume_connect_light(kg, sd, emission_sd, tp, state, L);
if (kernel_path_volume_bounce(kg, sd, &tp, &ps, &L->state, &pray)) {
indirect_sd->P_pick = sd->P_pick;
indirect_sd->V_pick = sd->V_pick;
indirect_sd->t_pick = sd->t_pick;
kernel_path_indirect(kg, indirect_sd, emission_sd, &pray, tp, &ps, L);
/* for render passes, sum and reset indirect light pass variables
@@ -265,7 +275,9 @@ ccl_device_noinline_cpu void kernel_branched_path_surface_indirect_light(KernelG
}
ps.rng_hash = state->rng_hash;
indirect_sd->P_pick = sd->P_pick;
indirect_sd->V_pick = sd->V_pick;
indirect_sd->t_pick = sd->t_pick;
kernel_path_indirect(kg, indirect_sd, emission_sd, &bsdf_ray, tp * num_samples_inv, &ps, L);
/* for render passes, sum and reset indirect light pass variables
@@ -393,6 +405,7 @@ ccl_device void kernel_branched_path_integrate(KernelGlobals *kg,
* Indirect bounces are handled in kernel_branched_path_surface_indirect_light().
*/
for (;;) {
/* Find intersection with objects in scene. */
Intersection isect;
bool hit = kernel_path_scene_intersect(kg, &state, &ray, &isect, L);
@@ -445,6 +458,8 @@ ccl_device void kernel_branched_path_integrate(KernelGlobals *kg,
}
}
kernel_update_light_picking(&sd, NULL);
# ifdef __DENOISING_FEATURES__
kernel_update_denoising_features(kg, &sd, &state, L);
# endif

View File

@@ -68,6 +68,7 @@ ccl_device_inline
* integration loop stops when this function returns true.
*/
subsurface_scatter_multi_setup(kg, &ss_isect, hit, sd, state, bssrdf_type, bssrdf_roughness);
kernel_update_light_picking(sd, NULL);
kernel_path_surface_connect_light(kg, sd, emission_sd, *throughput, state, L);

View File

@@ -16,6 +16,220 @@
CCL_NAMESPACE_BEGIN
/* connect the given light sample with the shading point and calculate its
* contribution and accumulate it to L */
ccl_device void accum_light_contribution(KernelGlobals *kg,
ShaderData *sd,
ShaderData *emission_sd,
LightSample *ls,
ccl_addr_space PathState *state,
Ray *light_ray,
BsdfEval *L_light,
PathRadiance *L,
bool *is_lamp,
float terminate,
float3 throughput,
float scale)
{
if (direct_emission(kg, sd, emission_sd, ls, state, light_ray, L_light, is_lamp, terminate)) {
/* trace shadow ray */
float3 shadow;
if (!shadow_blocked(kg, sd, emission_sd, state, light_ray, &shadow)) {
/* accumulate */
path_radiance_accum_light(
kg, L, state, throughput * scale, L_light, shadow, scale, *is_lamp);
}
else {
path_radiance_accum_total_light(L, state, throughput * scale, L_light);
}
}
}
/* The accum_light_tree_contribution() function does the following:
* 1. Recursive tree traversal using splitting. This picks one or more lights.
* 2. For each picked light, a position on the light is also chosen.
* 3. The total contribution of all these light samples are evaluated and
* accumulated to L. */
ccl_device void accum_light_tree_contribution(KernelGlobals *kg,
float randu,
float randv,
int offset,
float pdf_factor,
bool can_split,
float3 throughput,
float scale_factor,
PathRadiance *L,
ccl_addr_space PathState *state,
ShaderData *sd,
ShaderData *emission_sd)
{
float3 P = sd->P_pick;
float3 V = sd->V_pick;
float t = sd->t_pick;
float time = sd->time;
int bounce = state->bounce;
float randu_stack[64];
float randv_stack[64];
int offset_stack[64];
float pdf_stack[64];
randu_stack[0] = randu;
randv_stack[0] = randv;
offset_stack[0] = offset;
pdf_stack[0] = pdf_factor;
int stack_idx = 0;
while (stack_idx > -1) {
randu = randu_stack[stack_idx];
randv = randv_stack[stack_idx];
offset = offset_stack[stack_idx];
pdf_factor = pdf_stack[stack_idx];
/* read in first part of node of light tree */
int right_child_offset, distribution_id, num_emitters;
update_node(kg, offset, &right_child_offset, &distribution_id, &num_emitters);
/* found a leaf */
if (right_child_offset == -1) {
/* if there are several emitters in this leaf then pick one of them */
if (num_emitters > 1) {
/* create and sample CDF without dynamic allocation.
* see comment in light_tree_sample() for this piece of code */
float sum = 0.0f;
for (int i = 0; i < num_emitters; ++i) {
sum += calc_light_importance(kg, P, V, t, offset, i);
}
if (sum == 0.0f) {
--stack_idx;
continue;
}
float sum_inv = 1.0f / sum;
float cdf_L = 0.0f;
float cdf_R = 0.0f;
float prob = 0.0f;
int light = num_emitters - 1;
for (int i = 1; i < num_emitters + 1; ++i) {
prob = calc_light_importance(kg, P, V, t, offset, i - 1) * sum_inv;
cdf_R = cdf_L + prob;
if (randu < cdf_R) {
light = i - 1;
break;
}
cdf_L = cdf_R;
}
distribution_id += light;
pdf_factor *= prob;
/* rescale random number */
randu = (randu - cdf_L) / (cdf_R - cdf_L);
}
/* pick a point on the chosen light(distribution_id) and calculate the
* probability of picking this point */
LightSample ls;
light_point_sample(kg, -1, randu, randv, time, P, bounce, distribution_id, &ls);
/* combine pdfs */
ls.pdf *= pdf_factor;
if (ls.pdf <= 0.0f) {
--stack_idx;
continue;
}
/* compute and accumulate the total contribution of this light */
Ray light_ray;
light_ray.t = 0.0f;
#ifdef __OBJECT_MOTION__
light_ray.time = sd->time;
#endif
BsdfEval L_light;
bool is_lamp;
float terminate = path_state_rng_light_termination(kg, state);
accum_light_contribution(kg,
sd,
emission_sd,
&ls,
state,
&light_ray,
&L_light,
L,
&is_lamp,
terminate,
throughput,
scale_factor);
--stack_idx;
can_split = true;
continue;
}
else { // Interior node, choose which child(ren) to go down
int child_offsetL = offset + 4;
int child_offsetR = 4 * right_child_offset;
/* choose whether to go down both(split) or only one of the children */
if (can_split && split(kg, P, offset)) {
/* go down both child nodes */
randu_stack[stack_idx] = randu;
randv_stack[stack_idx] = randv;
offset_stack[stack_idx] = child_offsetL;
pdf_stack[stack_idx] = pdf_factor;
++stack_idx;
randu_stack[stack_idx] = randu;
randv_stack[stack_idx] = randv;
offset_stack[stack_idx] = child_offsetR;
pdf_stack[stack_idx] = pdf_factor;
}
else {
/* go down one of the child nodes */
/* evaluate the importance of each of the child nodes */
float I_L = calc_node_importance(kg, P, V, t, child_offsetL);
float I_R = calc_node_importance(kg, P, V, t, child_offsetR);
if ((I_L == 0.0f) && (I_R == 0.0f)) {
return;
}
/* calculate the probability of going down the left node */
float P_L = I_L / (I_L + I_R);
/* choose which node to go down */
if (randu <= P_L) { // Going down left node
/* rescale random number */
randu = randu / P_L;
offset = child_offsetL;
pdf_factor *= P_L;
}
else { // Going down right node
/* rescale random number */
randu = (randu * (I_L + I_R) - I_L) / I_R;
offset = child_offsetR;
pdf_factor *= 1.0f - P_L;
}
can_split = false;
randu_stack[stack_idx] = randu;
randv_stack[stack_idx] = randv;
offset_stack[stack_idx] = offset;
pdf_stack[stack_idx] = pdf_factor;
}
}
}
}
#if defined(__BRANCHED_PATH__) || defined(__SUBSURFACE__) || defined(__SHADOW_TRICKS__) || \
defined(__BAKING__)
/* branched path tracing: connect path directly to position on one or more lights and add it to L
@@ -34,104 +248,174 @@ ccl_device_noinline_cpu void kernel_branched_path_surface_connect_light(
/* sample illumination from lights to find path contribution */
BsdfEval L_light ccl_optional_struct_init;
int num_lights = 0;
if (kernel_data.integrator.use_direct_light) {
if (sample_all_lights) {
num_lights = kernel_data.integrator.num_all_lights;
if (kernel_data.integrator.pdf_triangles != 0.0f) {
num_lights += 1;
}
bool use_light_tree = kernel_data.integrator.use_light_tree;
if (use_light_tree) {
Ray light_ray;
bool is_lamp;
light_ray.t = 0.0f;
# ifdef __OBJECT_MOTION__
light_ray.time = sd->time;
# endif
int index;
float randu, randv;
path_state_rng_2D(kg, state, PRNG_LIGHT_U, &randu, &randv);
/* sample light group distribution */
int group = light_group_distribution_sample(kg, &randu);
float group_prob = kernel_tex_fetch(__light_group_sample_prob, group);
float pdf = 1.0f;
if (group == LIGHTGROUP_TREE) {
/* accumulate contribution to L from potentially several lights */
accum_light_tree_contribution(kg,
randu,
randv,
0,
group_prob,
true,
throughput,
num_samples_adjust,
L, // todo: is num_samples_adjust correct here?
state,
sd,
emission_sd);
/* have accumulated all the contributions so return */
return;
}
else if (group == LIGHTGROUP_DISTANT) {
/* pick a single distant light */
light_distant_sample(kg, sd->P, &randu, &index, &pdf);
}
else if (group == LIGHTGROUP_BACKGROUND) {
/* pick a single background light */
light_background_sample(kg, sd->P, &randu, &index, &pdf);
}
else {
num_lights = 1;
kernel_assert(false);
}
/* sample a point on the given distant/background light */
LightSample ls;
light_point_sample(kg, -1, randu, randv, sd->time, sd->P, state->bounce, index, &ls);
/* combine pdfs */
ls.pdf *= group_prob;
if (ls.pdf <= 0.0f)
return;
/* accumulate the contribution of this distant/background light to L */
float terminate = path_state_rng_light_termination(kg, state);
accum_light_contribution(kg,
sd,
emission_sd,
&ls,
state,
&light_ray,
&L_light,
L,
&is_lamp,
terminate,
throughput,
num_samples_adjust);
}
for (int i = 0; i < num_lights; i++) {
/* sample one light at random */
int num_samples = 1;
int num_all_lights = 1;
uint lamp_rng_hash = state->rng_hash;
bool double_pdf = false;
bool is_mesh_light = false;
bool is_lamp = false;
if (sample_all_lights) {
/* lamp sampling */
is_lamp = i < kernel_data.integrator.num_all_lights;
if (is_lamp) {
if (UNLIKELY(light_select_reached_max_bounces(kg, i, state->bounce))) {
continue;
else {
int num_lights = 0;
if (kernel_data.integrator.use_direct_light) {
if (sample_all_lights) {
num_lights = kernel_data.integrator.num_all_lights;
if (kernel_data.integrator.pdf_triangles != 0.0f) {
num_lights += 1;
}
num_samples = ceil_to_int(num_samples_adjust * light_select_num_samples(kg, i));
num_all_lights = kernel_data.integrator.num_all_lights;
lamp_rng_hash = cmj_hash(state->rng_hash, i);
double_pdf = kernel_data.integrator.pdf_triangles != 0.0f;
}
/* mesh light sampling */
else {
num_samples = ceil_to_int(num_samples_adjust * kernel_data.integrator.mesh_light_samples);
double_pdf = kernel_data.integrator.num_all_lights != 0;
is_mesh_light = true;
num_lights = 1;
}
}
float num_samples_inv = num_samples_adjust / (num_samples * num_all_lights);
for (int i = 0; i < num_lights; i++) {
/* sample one light at random */
int num_samples = 1;
uint lamp_rng_hash = state->rng_hash;
bool double_pdf = false;
bool is_mesh_light = false;
bool is_lamp = false;
for (int j = 0; j < num_samples; j++) {
Ray light_ray ccl_optional_struct_init;
light_ray.t = 0.0f; /* reset ray */
# ifdef __OBJECT_MOTION__
light_ray.time = sd->time;
# endif
bool has_emission = false;
if (kernel_data.integrator.use_direct_light && (sd->flag & SD_BSDF_HAS_EVAL)) {
float light_u, light_v;
path_branched_rng_2D(
kg, lamp_rng_hash, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
float terminate = path_branched_rng_light_termination(
kg, lamp_rng_hash, state, j, num_samples);
/* only sample triangle lights */
if (is_mesh_light && double_pdf) {
light_u = 0.5f * light_u;
if (sample_all_lights) {
/* lamp sampling */
is_lamp = i < kernel_data.integrator.num_all_lights;
if (is_lamp) {
if (UNLIKELY(light_select_reached_max_bounces(kg, i, state->bounce))) {
continue;
}
num_samples = ceil_to_int(num_samples_adjust * light_select_num_samples(kg, i));
lamp_rng_hash = cmj_hash(state->rng_hash, i);
double_pdf = kernel_data.integrator.pdf_triangles != 0.0f;
}
/* mesh light sampling */
else {
num_samples = ceil_to_int(num_samples_adjust *
kernel_data.integrator.mesh_light_samples);
double_pdf = kernel_data.integrator.num_all_lights != 0;
is_mesh_light = true;
}
}
LightSample ls ccl_optional_struct_init;
const int lamp = is_lamp ? i : -1;
if (light_sample(kg, lamp, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) {
/* The sampling probability returned by lamp_light_sample assumes that all lights were
* sampled. However, this code only samples lamps, so if the scene also had mesh lights,
* the real probability is twice as high. */
if (double_pdf) {
ls.pdf *= 2.0f;
float num_samples_inv = num_samples_adjust / num_samples;
for (int j = 0; j < num_samples; j++) {
Ray light_ray ccl_optional_struct_init;
light_ray.t = 0.0f; /* reset ray */
# ifdef __OBJECT_MOTION__
light_ray.time = sd->time;
# endif
if (kernel_data.integrator.use_direct_light && (sd->flag & SD_BSDF_HAS_EVAL)) {
float light_u, light_v;
path_branched_rng_2D(
kg, lamp_rng_hash, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
float terminate = path_branched_rng_light_termination(
kg, lamp_rng_hash, state, j, num_samples);
/* only sample triangle lights */
if (is_mesh_light && double_pdf) {
light_u = 0.5f * light_u;
}
has_emission = direct_emission(
kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate);
}
}
/* trace shadow ray */
float3 shadow;
const bool blocked = shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow);
if (has_emission) {
if (!blocked) {
/* accumulate */
path_radiance_accum_light(kg,
L,
state,
throughput * num_samples_inv,
&L_light,
shadow,
num_samples_inv,
is_lamp);
}
else {
path_radiance_accum_total_light(L, state, throughput * num_samples_inv, &L_light);
LightSample ls ccl_optional_struct_init;
const int lamp = is_lamp ? i : -1;
if (light_sample(kg,
lamp,
light_u,
light_v,
sd->time,
sd->P_pick,
sd->V_pick,
sd->t_pick,
state->bounce,
&ls)) {
/* The sampling probability returned by lamp_light_sample assumes that all lights were
* sampled. However, this code only samples lamps, so if the scene also had mesh
* lights, the real probability is twice as high. */
if (double_pdf) {
ls.pdf *= 2.0f;
}
accum_light_contribution(kg,
sd,
emission_sd,
&ls,
state,
&light_ray,
&L_light,
L,
&is_lamp,
terminate,
throughput,
num_samples_inv);
}
}
}
}
@@ -225,11 +509,11 @@ ccl_device_inline void kernel_path_surface_connect_light(KernelGlobals *kg,
int all = (state->flag & PATH_RAY_SHADOW_CATCHER);
kernel_branched_path_surface_connect_light(kg, sd, emission_sd, state, throughput, 1.0f, L, all);
# else
/* sample illumination from lights to find path contribution */
Ray light_ray ccl_optional_struct_init;
BsdfEval L_light ccl_optional_struct_init;
bool is_lamp = false;
bool has_emission = false;
light_ray.t = 0.0f;
# ifdef __OBJECT_MOTION__
@@ -241,27 +525,32 @@ ccl_device_inline void kernel_path_surface_connect_light(KernelGlobals *kg,
path_state_rng_2D(kg, state, PRNG_LIGHT_U, &light_u, &light_v);
LightSample ls ccl_optional_struct_init;
if (light_sample(kg, -1, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) {
if (light_sample(kg,
-1,
light_u,
light_v,
sd->time,
sd->P_pick,
sd->V_pick,
sd->t_pick,
state->bounce,
&ls)) {
float terminate = path_state_rng_light_termination(kg, state);
has_emission = direct_emission(
kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate);
accum_light_contribution(kg,
sd,
emission_sd,
&ls,
state,
&light_ray,
&L_light,
L,
&is_lamp,
terminate,
throughput,
1.0f);
}
}
/* trace shadow ray */
float3 shadow;
const bool blocked = shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow);
if (has_emission) {
if (!blocked) {
/* accumulate */
path_radiance_accum_light(kg, L, state, throughput, &L_light, shadow, 1.0f, is_lamp);
}
else {
path_radiance_accum_total_light(L, state, throughput, &L_light);
}
}
# endif
#endif
}
@@ -310,6 +599,7 @@ ccl_device bool kernel_path_surface_bounce(KernelGlobals *kg,
/* setup ray */
ray->P = ray_offset(sd->P, (label & LABEL_TRANSMIT) ? -sd->Ng : sd->Ng);
kernel_update_light_picking(sd, NULL);
ray->D = normalize(bsdf_omega_in);
if (state->bounce == 0)
@@ -342,6 +632,8 @@ ccl_device bool kernel_path_surface_bounce(KernelGlobals *kg,
/* setup ray position, direction stays unchanged */
ray->P = ray_offset(sd->P, -sd->Ng);
kernel_update_light_picking(sd, NULL);
# ifdef __RAY_DIFFERENTIALS__
ray->dP = sd->dP;
# endif

View File

@@ -43,7 +43,16 @@ ccl_device_inline void kernel_path_volume_connect_light(KernelGlobals *kg,
path_state_rng_2D(kg, state, PRNG_LIGHT_U, &light_u, &light_v);
LightSample ls ccl_optional_struct_init;
if (light_sample(kg, -1, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) {
if (light_sample(kg,
-1,
light_u,
light_v,
sd->time,
sd->P_pick,
sd->V_pick,
sd->t_pick,
state->bounce,
&ls)) {
float terminate = path_state_rng_light_termination(kg, state);
has_emission = direct_emission(
kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate);
@@ -128,6 +137,210 @@ ccl_device_noinline_cpu bool kernel_path_volume_bounce(KernelGlobals *kg,
}
# if !defined(__SPLIT_KERNEL__) && (defined(__BRANCHED_PATH__) || defined(__VOLUME_DECOUPLED__))
ccl_device void accum_light_tree_contribution_volume(KernelGlobals *kg,
float randu,
float randv,
int offset,
float pdf_factor,
bool can_split,
float3 throughput,
float scale_factor,
PathRadiance *L,
ccl_addr_space PathState *state,
ShaderData *sd,
ShaderData *emission_sd,
Ray *ray,
const VolumeSegment *segment)
{
float3 P = sd->P_pick;
float3 V = sd->V_pick;
float t = sd->t_pick;
float time = sd->time;
int bounce = state->bounce;
float randu_stack[64];
float randv_stack[64];
int offset_stack[64];
float pdf_stack[64];
randu_stack[0] = randu;
randv_stack[0] = randv;
offset_stack[0] = offset;
pdf_stack[0] = pdf_factor;
int stack_idx = 0;
while (stack_idx > -1) {
randu = randu_stack[stack_idx];
randv = randv_stack[stack_idx];
offset = offset_stack[stack_idx];
pdf_factor = pdf_stack[stack_idx];
/* read in first part of node of light tree */
int right_child_offset, distribution_id, num_emitters;
update_node(kg, offset, &right_child_offset, &distribution_id, &num_emitters);
/* found a leaf */
if (right_child_offset == -1) {
/* if there are several emitters in this leaf then pick one of them */
if (num_emitters > 1) {
/* create and sample CDF without dynamic allocation.
* see comment in light_tree_sample() for this piece of code */
float sum = 0.0f;
for (int i = 0; i < num_emitters; ++i) {
sum += calc_light_importance(kg, P, V, t, offset, i);
}
if (sum == 0.0f) {
--stack_idx;
continue;
}
float sum_inv = 1.0f / sum;
float cdf_L = 0.0f;
float cdf_R = 0.0f;
float prob = 0.0f;
int light = num_emitters - 1;
for (int i = 1; i < num_emitters + 1; ++i) {
prob = calc_light_importance(kg, P, V, t, offset, i - 1) * sum_inv;
cdf_R = cdf_L + prob;
if (randu < cdf_R) {
light = i - 1;
break;
}
cdf_L = cdf_R;
}
distribution_id += light;
pdf_factor *= prob;
/* rescale random number */
randu = (randu - cdf_L) / (cdf_R - cdf_L);
}
/* pick a point on the chosen light(distribution_id) and calculate the
* probability of picking this point */
LightSample ls;
light_point_sample(kg, -1, randu, randv, time, P, bounce, distribution_id, &ls);
/* combine pdfs */
ls.pdf *= pdf_factor;
/* compute and accumulate the total contribution of this light */
Ray light_ray;
light_ray.t = 0.0f;
# ifdef __OBJECT_MOTION__
light_ray.time = sd->time;
# endif
float3 tp = throughput;
bool has_emission = false;
bool is_lamp = false;
BsdfEval L_light ccl_optional_struct_init;
/* sample position on volume segment */
float rphase = path_branched_rng_1D(
kg, state->rng_hash, state, distribution_id, 1.0f, PRNG_PHASE_CHANNEL);
float rscatter = path_branched_rng_1D(
kg, state->rng_hash, state, distribution_id, 1.0f, PRNG_SCATTER_DISTANCE);
VolumeIntegrateResult result = kernel_volume_decoupled_scatter(kg,
state,
ray,
sd,
&tp,
rphase,
rscatter,
segment,
(ls.t != FLT_MAX) ? &ls.P :
NULL,
false);
if (result == VOLUME_PATH_SCATTERED) {
light_point_sample(kg, -1, randu, randv, time, sd->P_pick, bounce, distribution_id, &ls);
if (ls.pdf <= 0.0f) {
--stack_idx;
continue;
}
/* sample random light */
float terminate = path_branched_rng_light_termination(
kg, state->rng_hash, state, distribution_id, 1.0f);
has_emission = direct_emission(
kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate);
}
/* trace shadow ray */
float3 shadow;
const bool blocked = shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow);
if (has_emission && !blocked) {
/* accumulate */
path_radiance_accum_light(kg, L, state, tp, &L_light, shadow, 1.0f, is_lamp);
}
--stack_idx;
can_split = true;
continue;
}
else { // Interior node, choose which child(ren) to go down
int child_offsetL = offset + 4;
int child_offsetR = 4 * right_child_offset;
/* choose whether to go down both(split) or only one of the children */
if (can_split && split(kg, P, offset)) {
/* go down both child nodes */
randu_stack[stack_idx] = randu;
randv_stack[stack_idx] = randv;
offset_stack[stack_idx] = child_offsetL;
pdf_stack[stack_idx] = pdf_factor;
++stack_idx;
randu_stack[stack_idx] = randu;
randv_stack[stack_idx] = randv;
offset_stack[stack_idx] = child_offsetR;
pdf_stack[stack_idx] = pdf_factor;
}
else {
/* go down one of the child nodes */
/* evaluate the importance of each of the child nodes */
float I_L = calc_node_importance(kg, P, V, t, child_offsetL);
float I_R = calc_node_importance(kg, P, V, t, child_offsetR);
if ((I_L == 0.0f) && (I_R == 0.0f)) {
return;
}
/* calculate the probability of going down the left node */
float P_L = I_L / (I_L + I_R);
/* choose which node to go down */
if (randu <= P_L) { // Going down left node
/* rescale random number */
randu = randu / P_L;
offset = child_offsetL;
pdf_factor *= P_L;
}
else { // Going down right node
/* rescale random number */
randu = (randu * (I_L + I_R) - I_L) / I_R;
offset = child_offsetR;
pdf_factor *= 1.0f - P_L;
}
can_split = false;
randu_stack[stack_idx] = randu;
randv_stack[stack_idx] = randv;
offset_stack[stack_idx] = offset;
pdf_stack[stack_idx] = pdf_factor;
}
}
}
}
ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg,
ShaderData *sd,
ShaderData *emission_sd,
@@ -141,113 +354,228 @@ ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg,
# ifdef __EMISSION__
BsdfEval L_light ccl_optional_struct_init;
int num_lights = 1;
if (sample_all_lights) {
num_lights = kernel_data.integrator.num_all_lights;
if (kernel_data.integrator.pdf_triangles != 0.0f) {
num_lights += 1;
bool use_light_tree = kernel_data.integrator.use_light_tree;
if (use_light_tree) {
Ray light_ray;
bool is_lamp;
light_ray.t = 0.0f;
# ifdef __OBJECT_MOTION__
light_ray.time = sd->time;
# endif
int index;
float randu, randv;
path_state_rng_2D(kg, state, PRNG_LIGHT_U, &randu, &randv);
/* sample light group distribution */
int group = light_group_distribution_sample(kg, &randu);
float group_prob = kernel_tex_fetch(__light_group_sample_prob, group);
float pdf = 1.0f;
if (group == LIGHTGROUP_TREE) {
/* accumulate contribution to L from potentially several lights */
accum_light_tree_contribution_volume(kg,
randu,
randv,
0,
group_prob,
true,
throughput,
1.0f,
L, // todo: is num_samples_adjust correct here?
state,
sd,
emission_sd,
ray,
segment);
/* have accumulated all the contributions so return */
return;
}
else if (group == LIGHTGROUP_DISTANT) {
/* pick a single distant light */
light_distant_sample(kg, sd->P, &randu, &index, &pdf);
}
else if (group == LIGHTGROUP_BACKGROUND) {
/* pick a single background light */
light_background_sample(kg, sd->P, &randu, &index, &pdf);
}
else {
kernel_assert(false);
}
/* sample a point on the given distant/background light */
LightSample ls;
light_point_sample(kg, -1, randu, randv, sd->time, sd->P, state->bounce, index, &ls);
/* combine pdfs */
ls.pdf *= group_prob;
if (ls.pdf <= 0.0f)
return;
float3 tp = throughput;
bool has_emission = false;
/* sample position on volume segment */
float rphase = path_branched_rng_1D(
kg, state->rng_hash, state, index, 1.0f, PRNG_PHASE_CHANNEL);
float rscatter = path_branched_rng_1D(
kg, state->rng_hash, state, index, 1.0f, PRNG_SCATTER_DISTANCE);
VolumeIntegrateResult result = kernel_volume_decoupled_scatter(kg,
state,
ray,
sd,
&tp,
rphase,
rscatter,
segment,
(ls.t != FLT_MAX) ? &ls.P :
NULL,
false);
if (result == VOLUME_PATH_SCATTERED) {
light_point_sample(kg, -1, randu, randv, sd->time, sd->P_pick, state->bounce, index, &ls);
/* sample random light */
float terminate = path_branched_rng_light_termination(
kg, state->rng_hash, state, index, 1.0f);
has_emission = direct_emission(
kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate);
}
/* trace shadow ray */
float3 shadow;
const bool blocked = shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow);
if (has_emission && !blocked) {
/* accumulate */
path_radiance_accum_light(kg, L, state, tp, &L_light, shadow, 1.0f, is_lamp);
}
}
for (int i = 0; i < num_lights; ++i) {
/* sample one light at random */
int num_samples = 1;
int num_all_lights = 1;
uint lamp_rng_hash = state->rng_hash;
bool double_pdf = false;
bool is_mesh_light = false;
bool is_lamp = false;
else {
int num_lights = 1;
if (sample_all_lights) {
/* lamp sampling */
is_lamp = i < kernel_data.integrator.num_all_lights;
if (is_lamp) {
if (UNLIKELY(light_select_reached_max_bounces(kg, i, state->bounce))) {
continue;
}
num_samples = light_select_num_samples(kg, i);
num_all_lights = kernel_data.integrator.num_all_lights;
lamp_rng_hash = cmj_hash(state->rng_hash, i);
double_pdf = kernel_data.integrator.pdf_triangles != 0.0f;
}
/* mesh light sampling */
else {
num_samples = kernel_data.integrator.mesh_light_samples;
double_pdf = kernel_data.integrator.num_all_lights != 0;
is_mesh_light = true;
num_lights = kernel_data.integrator.num_all_lights;
if (kernel_data.integrator.pdf_triangles != 0.0f) {
num_lights += 1;
}
}
for (int i = 0; i < num_lights; ++i) {
/* sample one light at random */
int num_samples = 1;
uint lamp_rng_hash = state->rng_hash;
bool double_pdf = false;
bool is_mesh_light = false;
bool is_lamp = false;
float num_samples_inv = 1.0f / (num_samples * num_all_lights);
for (int j = 0; j < num_samples; j++) {
Ray light_ray ccl_optional_struct_init;
light_ray.t = 0.0f; /* reset ray */
# ifdef __OBJECT_MOTION__
light_ray.time = sd->time;
# endif
bool has_emission = false;
float3 tp = throughput;
if (kernel_data.integrator.use_direct_light) {
/* sample random position on random light/triangle */
float light_u, light_v;
path_branched_rng_2D(
kg, lamp_rng_hash, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
/* only sample triangle lights */
if (is_mesh_light && double_pdf) {
light_u = 0.5f * light_u;
}
LightSample ls ccl_optional_struct_init;
const int lamp = is_lamp ? i : -1;
light_sample(kg, lamp, light_u, light_v, sd->time, ray->P, state->bounce, &ls);
/* sample position on volume segment */
float rphase = path_branched_rng_1D(
kg, state->rng_hash, state, j, num_samples, PRNG_PHASE_CHANNEL);
float rscatter = path_branched_rng_1D(
kg, state->rng_hash, state, j, num_samples, PRNG_SCATTER_DISTANCE);
VolumeIntegrateResult result = kernel_volume_decoupled_scatter(kg,
state,
ray,
sd,
&tp,
rphase,
rscatter,
segment,
(ls.t != FLT_MAX) ? &ls.P :
NULL,
false);
if (result == VOLUME_PATH_SCATTERED) {
/* todo: split up light_sample so we don't have to call it again with new position */
if (light_sample(kg, lamp, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) {
if (double_pdf) {
ls.pdf *= 2.0f;
}
/* sample random light */
float terminate = path_branched_rng_light_termination(
kg, state->rng_hash, state, j, num_samples);
has_emission = direct_emission(
kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate);
if (sample_all_lights) {
/* lamp sampling */
is_lamp = i < kernel_data.integrator.num_all_lights;
if (is_lamp) {
if (UNLIKELY(light_select_reached_max_bounces(kg, i, state->bounce))) {
continue;
}
lamp_rng_hash = cmj_hash(state->rng_hash, i);
double_pdf = kernel_data.integrator.pdf_triangles != 0.0f;
}
/* mesh light sampling */
else {
num_samples = kernel_data.integrator.mesh_light_samples;
double_pdf = kernel_data.integrator.num_all_lights != 0;
is_mesh_light = true;
}
}
/* trace shadow ray */
float3 shadow;
float num_samples_inv = 1.0f / num_samples;
const bool blocked = shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow);
for (int j = 0; j < num_samples; j++) {
Ray light_ray ccl_optional_struct_init;
light_ray.t = 0.0f; /* reset ray */
# ifdef __OBJECT_MOTION__
light_ray.time = sd->time;
# endif
bool has_emission = false;
if (has_emission && !blocked) {
/* accumulate */
path_radiance_accum_light(
kg, L, state, tp * num_samples_inv, &L_light, shadow, num_samples_inv, is_lamp);
float3 tp = throughput;
if (kernel_data.integrator.use_direct_light) {
/* sample random position on random light/triangle */
float light_u, light_v;
path_branched_rng_2D(
kg, lamp_rng_hash, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
/* only sample triangle lights */
if (is_mesh_light && double_pdf) {
light_u = 0.5f * light_u;
}
LightSample ls ccl_optional_struct_init;
const int lamp = is_lamp ? i : -1;
light_sample(kg,
lamp,
light_u,
light_v,
sd->time,
sd->P_pick,
sd->V_pick,
sd->t_pick,
state->bounce,
&ls);
/* sample position on volume segment */
float rphase = path_branched_rng_1D(
kg, state->rng_hash, state, j, num_samples, PRNG_PHASE_CHANNEL);
float rscatter = path_branched_rng_1D(
kg, state->rng_hash, state, j, num_samples, PRNG_SCATTER_DISTANCE);
VolumeIntegrateResult result = kernel_volume_decoupled_scatter(
kg,
state,
ray,
sd,
&tp,
rphase,
rscatter,
segment,
(ls.t != FLT_MAX) ? &ls.P : NULL,
false);
if (result == VOLUME_PATH_SCATTERED) {
/* todo: split up light_sample so we don't have to call it again with new position */
if (light_sample(kg,
lamp,
light_u,
light_v,
sd->time,
sd->P_pick,
sd->V_pick,
sd->t_pick,
state->bounce,
&ls)) {
if (double_pdf) {
ls.pdf *= 2.0f;
}
/* sample random light */
float terminate = path_branched_rng_light_termination(
kg, state->rng_hash, state, j, num_samples);
has_emission = direct_emission(
kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate);
}
}
}
/* trace shadow ray */
float3 shadow;
const bool blocked = shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow);
if (has_emission && !blocked) {
/* accumulate */
path_radiance_accum_light(
kg, L, state, tp * num_samples_inv, &L_light, shadow, num_samples_inv, is_lamp);
}
}
}
}

View File

@@ -66,6 +66,14 @@ KERNEL_TEX(KernelLightDistribution, __light_distribution)
KERNEL_TEX(KernelLight, __lights)
KERNEL_TEX(float2, __light_background_marginal_cdf)
KERNEL_TEX(float2, __light_background_conditional_cdf)
KERNEL_TEX(float4, __light_tree_nodes)
KERNEL_TEX(uint, __light_distribution_to_node)
KERNEL_TEX(uint, __lamp_to_distribution)
KERNEL_TEX(uint, __triangle_to_distribution)
KERNEL_TEX(float, __light_group_sample_cdf)
KERNEL_TEX(float, __light_group_sample_prob)
KERNEL_TEX(float4, __light_tree_leaf_emitters)
KERNEL_TEX(int, __leaf_to_first_emitter)
/* particles */
KERNEL_TEX(KernelParticle, __particles)

View File

@@ -626,6 +626,16 @@ enum PanoramaType {
PANORAMA_NUM_TYPES,
};
/* Light Sampling Group */
enum LightGroup {
LIGHTGROUP_TREE,
LIGHTGROUP_DISTANT,
LIGHTGROUP_BACKGROUND,
LIGHTGROUP_NUM,
};
/* Differential */
typedef struct differential3 {
@@ -939,6 +949,12 @@ typedef ccl_addr_space struct ccl_align(16) ShaderData
float3 N;
/* true geometric normal */
float3 Ng;
/* position used in light picking */
float3 P_pick;
/* normal or ray direction used in light picking */
float3 V_pick;
/* ray dist used for light picking */
float t_pick;
/* view/incoming direction */
float3 I;
/* shader id */
@@ -1320,13 +1336,22 @@ static_assert_align(KernelBackground, 16);
typedef struct KernelIntegrator {
/* emission */
int use_light_tree;
float splitting_threshold;
int use_direct_light;
int use_ambient_occlusion;
int num_distribution;
int num_all_lights;
int num_light_nodes;
int num_triangle_lights;
int num_distant_lights;
float inv_num_distant_lights;
float pdf_triangles;
float pdf_lights;
float pdf_inv_totarea;
float light_inv_rr_threshold;
int distant_lights_offset;
int background_light_index;
/* bounces */
int min_bounce;
@@ -1389,7 +1414,7 @@ typedef struct KernelIntegrator {
int max_closures;
int pad1, pad2;
int pad1;
} KernelIntegrator;
static_assert_align(KernelIntegrator, 16);
@@ -1533,8 +1558,10 @@ typedef struct KernelLight {
static_assert_align(KernelLight, 16);
typedef struct KernelLightDistribution {
float area;
float totarea;
int prim;
float pad1, pad2, pad3;
union {
struct {
int shader_flag;

View File

@@ -1125,6 +1125,11 @@ ccl_device VolumeIntegrateResult kernel_volume_decoupled_scatter(KernelGlobals *
/* move to new position */
sd->P = ray->P + sample_t * ray->D;
kernel_update_light_picking(sd, NULL);
sd->V_pick = ray->D;
sd->t_pick = ray->t;
return VOLUME_PATH_SCATTERED;
}
# endif /* __SPLIT_KERNEL */

View File

@@ -86,7 +86,16 @@ ccl_device void kernel_direct_lighting(KernelGlobals *kg,
float terminate = path_state_rng_light_termination(kg, state);
LightSample ls;
if (light_sample(kg, -1, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) {
if (light_sample(kg,
-1,
light_u,
light_v,
sd->time,
sd->P_pick,
sd->V_pick,
sd->t_pick,
state->bounce,
&ls)) {
Ray light_ray;
light_ray.time = sd->time;

View File

@@ -63,6 +63,8 @@ ccl_device_noinline bool kernel_split_branched_path_volume_indirect_light_iter(K
VolumeIntegrateResult result = kernel_volume_integrate(
kg, ps, sd, &volume_ray, L, tp, step_size);
kernel_update_light_picking(sd, NULL);
# ifdef __VOLUME_SCATTER__
if (result == VOLUME_PATH_SCATTERED) {
/* direct lighting */

View File

@@ -59,7 +59,6 @@ ccl_device void kernel_lamp_emission(KernelGlobals *kg)
Ray ray = kernel_split_state.ray[ray_index];
ccl_global Intersection *isect = &kernel_split_state.isect[ray_index];
ShaderData *sd = kernel_split_sd(sd, ray_index);
kernel_path_lamp_emission(kg, state, &ray, throughput, isect, sd, L);
}
}

View File

@@ -137,7 +137,7 @@ ccl_device float3 sky_radiance_nishita(KernelGlobals *kg,
float sun_rotation = nishita_data[7];
float angular_diameter = nishita_data[8];
float sun_intensity = nishita_data[9];
bool sun_disc = (angular_diameter > 0.0f);
bool sun_disc = (angular_diameter >= 0.0f);
float3 xyz;
/* convert dir to spherical coordinates */
float2 direction = direction_to_spherical(dir);

View File

@@ -30,6 +30,7 @@ set(SRC
integrator.cpp
jitter.cpp
light.cpp
light_tree.cpp
merge.cpp
mesh.cpp
mesh_displace.cpp

View File

@@ -1201,9 +1201,13 @@ void GeometryManager::device_update_volume_images(Device *device, Scene *scene,
}
ImageHandle &handle = attr.data_voxel();
const int slot = handle.svm_slot();
if (slot != -1) {
volume_images.insert(slot);
/* We can build directly from OpenVDB data structures, no need to
* load such images early. */
if (!handle.vdb_loader()) {
const int slot = handle.svm_slot();
if (slot != -1) {
volume_images.insert(slot);
}
}
}
}

View File

@@ -18,6 +18,7 @@
#include "device/device.h"
#include "render/colorspace.h"
#include "render/image_oiio.h"
#include "render/image_vdb.h"
#include "render/scene.h"
#include "render/stats.h"
@@ -172,6 +173,31 @@ device_texture *ImageHandle::image_memory(const int tile_index) const
return img ? img->mem : NULL;
}
VDBImageLoader *ImageHandle::vdb_loader(const int tile_index) const
{
if (tile_index >= tile_slots.size()) {
return NULL;
}
ImageManager::Image *img = manager->images[tile_slots[tile_index]];
if (img == NULL) {
return NULL;
}
ImageLoader *loader = img->loader;
if (loader == NULL) {
return NULL;
}
if (loader->is_vdb_loader()) {
return dynamic_cast<VDBImageLoader *>(loader);
}
return NULL;
}
bool ImageHandle::operator==(const ImageHandle &other) const
{
return manager == other.manager && tile_slots == other.tile_slots;
@@ -258,6 +284,11 @@ bool ImageLoader::equals(const ImageLoader *a, const ImageLoader *b)
}
}
bool ImageLoader::is_vdb_loader() const
{
return false;
}
/* Image Manager */
ImageManager::ImageManager(const DeviceInfo &info)
@@ -362,9 +393,11 @@ ImageHandle ImageManager::add_image(const string &filename,
return handle;
}
ImageHandle ImageManager::add_image(ImageLoader *loader, const ImageParams &params)
ImageHandle ImageManager::add_image(ImageLoader *loader,
const ImageParams &params,
const bool builtin)
{
const int slot = add_image_slot(loader, params, true);
const int slot = add_image_slot(loader, params, builtin);
ImageHandle handle;
handle.tile_slots.push_back(slot);

View File

@@ -39,6 +39,7 @@ class Progress;
class RenderStats;
class Scene;
class ColorSpaceProcessor;
class VDBImageLoader;
/* Image Parameters */
class ImageParams {
@@ -124,6 +125,8 @@ class ImageLoader {
virtual bool equals(const ImageLoader &other) const = 0;
static bool equals(const ImageLoader *a, const ImageLoader *b);
virtual bool is_vdb_loader() const;
/* Work around for no RTTI. */
};
@@ -149,6 +152,8 @@ class ImageHandle {
int svm_slot(const int tile_index = 0) const;
device_texture *image_memory(const int tile_index = 0) const;
VDBImageLoader *vdb_loader(const int tile_index = 0) const;
protected:
vector<int> tile_slots;
ImageManager *manager;
@@ -169,7 +174,7 @@ class ImageManager {
ImageHandle add_image(const string &filename,
const ImageParams &params,
const vector<int> &tiles);
ImageHandle add_image(ImageLoader *loader, const ImageParams &params);
ImageHandle add_image(ImageLoader *loader, const ImageParams &params, const bool builtin = true);
void device_update(Device *device, Scene *scene, Progress &progress);
void device_update_slot(Device *device, Scene *scene, int slot, Progress *progress);

View File

@@ -185,4 +185,16 @@ void VDBImageLoader::cleanup()
#endif
}
bool VDBImageLoader::is_vdb_loader() const
{
return true;
}
#ifdef WITH_OPENVDB
openvdb::GridBase::ConstPtr VDBImageLoader::get_grid()
{
return grid;
}
#endif
CCL_NAMESPACE_END

View File

@@ -43,6 +43,12 @@ class VDBImageLoader : public ImageLoader {
virtual void cleanup() override;
virtual bool is_vdb_loader() const override;
#ifdef WITH_OPENVDB
openvdb::GridBase::ConstPtr get_grid();
#endif
protected:
string grid_name;
#ifdef WITH_OPENVDB

View File

@@ -77,6 +77,11 @@ NODE_DEFINE(Integrator)
SOCKET_BOOLEAN(sample_all_lights_direct, "Sample All Lights Direct", true);
SOCKET_BOOLEAN(sample_all_lights_indirect, "Sample All Lights Indirect", true);
SOCKET_FLOAT(light_sampling_threshold, "Light Sampling Threshold", 0.05f);
SOCKET_BOOLEAN(use_light_tree, "Use light tree to optimize many light sampling", false);
SOCKET_FLOAT(splitting_threshold,
"Amount of lights to sample at a time, from one light at 0.0, to adaptively more "
"lights as needed, to all lights at 1.0",
0.0f);
static NodeEnum method_enum;
method_enum.insert("path", PATH);

View File

@@ -74,6 +74,8 @@ class Integrator : public Node {
bool sample_all_lights_direct;
bool sample_all_lights_indirect;
float light_sampling_threshold;
bool use_light_tree;
float splitting_threshold;
int adaptive_min_samples;
float adaptive_threshold;

View File

@@ -20,6 +20,7 @@
#include "render/film.h"
#include "render/graph.h"
#include "render/integrator.h"
#include "render/light_tree.h"
#include "render/mesh.h"
#include "render/nodes.h"
#include "render/object.h"
@@ -274,7 +275,89 @@ bool LightManager::object_usable_as_light(Object *object)
return false;
}
void LightManager::device_update_distribution(Device *,
float LightManager::distant_lights_energy(const Scene *scene, const vector<Primitive> &prims)
{
float luminance = 0.0f;
float3 emission;
foreach (Primitive prim, prims) {
if (prim.prim_id >= 0)
continue;
const Light *lamp = scene->lights[prim.lamp_id];
if (lamp->type != LIGHT_DISTANT)
continue;
/* get emission from shader */
bool is_constant_emission = lamp->shader->is_constant_emission(&emission);
if (!is_constant_emission)
continue; // TODO: Properly handle this case
luminance += scene->shader_manager->linear_rgb_to_gray(emission);
}
/* TODO: could project each bbox onto a disk outside the scene and sum up
* all the projected areas instead if this results in too high sampling */
/* get radius of bounding sphere of scene */
BoundBox scene_bbox = BoundBox::empty;
foreach (Object *object, scene->objects) {
// TODO: What about transforms?
scene_bbox.grow(object->bounds);
}
float radius_squared = len_squared(scene_bbox.max - scene_bbox.center());
return M_PI_F * radius_squared * luminance;
}
float LightManager::background_light_energy(Device *device,
DeviceScene *dscene,
Scene *scene,
Progress &progress,
const vector<Primitive> &prims)
{
/* compute energy for all background lights */
float average_luminance = 0.0f;
size_t num_pixels = 0;
/* find background lights */
foreach (Primitive prim, prims) {
if (prim.prim_id >= 0)
continue;
const Light *lamp = scene->lights[prim.lamp_id];
if (lamp->type != LIGHT_BACKGROUND)
continue;
vector<float3> pixels;
int2 res = get_background_map_resolution(lamp, scene);
shade_background_pixels(device, dscene, res.x, res.y, pixels, progress);
num_pixels += pixels.size();
for (int i = 0; i < pixels.size(); ++i) {
average_luminance += scene->shader_manager->linear_rgb_to_gray(pixels[i]);
}
break;
}
if (num_pixels == 0)
return 0.0f;
average_luminance /= (float)num_pixels;
/* get radius of bounding sphere of scene */
BoundBox scene_bbox = BoundBox::empty;
foreach (Object *object, scene->objects) {
// TODO: What about transforms?
scene_bbox.grow(object->bounds);
}
float radius_squared = len_squared(scene_bbox.max - scene_bbox.center());
return M_PI_F * radius_squared * average_luminance;
}
void LightManager::device_update_distribution(Device *device,
DeviceScene *dscene,
Scene *scene,
Progress &progress)
@@ -285,28 +368,35 @@ void LightManager::device_update_distribution(Device *,
size_t num_lights = 0;
size_t num_portals = 0;
size_t num_background_lights = 0;
size_t num_distant_lights = 0;
size_t num_triangles = 0;
bool background_mis = false;
foreach (Light *light, scene->lights) {
if (light->is_enabled) {
num_lights++;
}
if (light->is_portal) {
num_portals++;
}
}
/* The emissive_prims vector contains all emissive primitives in the scene,
* i.e., all mesh light triangles and all lamps. The order of the primitives
* in the vector is important since it has the same order as the
* light_distribution array.
*
* If using the light tree then the order is important since the light tree
* reordered the lights so lights in the same node are next to each other
* in memory.
*
* If NOT using the light tree then the order is important since during
* sampling we assume all triangles are first in the array. */
vector<Primitive> emissive_prims;
emissive_prims.reserve(scene->lights.size());
int object_id = 0;
foreach (Object *object, scene->objects) {
if (progress.get_cancel())
return;
if (!object_usable_as_light(object)) {
object_id++;
continue;
}
/* Count triangles. */
/* Count emissive triangles. */
Mesh *mesh = static_cast<Mesh *>(object->geometry);
size_t mesh_num_triangles = mesh->num_triangles();
for (size_t i = 0; i < mesh_num_triangles; i++) {
@@ -316,35 +406,272 @@ void LightManager::device_update_distribution(Device *,
scene->default_surface;
if (shader->use_mis && shader->has_surface_emission) {
emissive_prims.push_back(Primitive(i + mesh->prim_offset, object_id));
num_triangles++;
}
}
object_id++;
}
/* light index is the index of this lamp in the device lights array*/
int light_index = 0;
/* light_id is the index of this lamp in the scene lights array */
int light_id = 0;
/* the light index of the background light */
int background_index = -1;
foreach (Light *light, scene->lights) {
if (light->is_enabled) {
emissive_prims.push_back(Primitive(~light_index, light_id));
num_lights++;
if (light->type == LIGHT_BACKGROUND) {
background_index = light_index;
}
light_index++;
}
if (light->is_portal) {
num_portals++;
}
light_id++;
}
size_t num_distribution = num_triangles + num_lights;
VLOG(1) << "Total " << num_distribution << " of light distribution primitives.";
if (scene->integrator->use_light_tree) {
/* create light tree */
double time_start = time_dt();
LightTree light_tree(emissive_prims, scene, 64);
VLOG(1) << "Light tree build time: " << time_dt() - time_start;
/* the light tree reorders the primitives so update emissive_prims */
const vector<Primitive> &ordered_prims = light_tree.get_primitives();
emissive_prims = ordered_prims;
if (progress.get_cancel())
return;
/* create the nodes to be used on the device */
const vector<CompactNode> &nodes = light_tree.get_nodes();
float4 *dnodes = dscene->light_tree_nodes.alloc(nodes.size() * LIGHT_TREE_NODE_SIZE);
/* convert each compact node into 4xfloat4
* 4 for energy, right_child_offset, prim_id, num_emitters
* 4 for bbox.min + bbox.max[0]
* 4 for bbox.max[1-2], theta_o, theta_e
* 4 for axis + energy variance */
size_t offset = 0;
size_t num_leaf_lights = 0;
foreach (CompactNode node, nodes) {
dnodes[offset].x = node.energy;
dnodes[offset].y = __int_as_float(node.right_child_offset);
dnodes[offset].z = __int_as_float(node.first_prim_offset);
dnodes[offset].w = __int_as_float(node.num_lights);
dnodes[offset + 1].x = node.bounds_s.min[0];
dnodes[offset + 1].y = node.bounds_s.min[1];
dnodes[offset + 1].z = node.bounds_s.min[2];
dnodes[offset + 1].w = node.bounds_s.max[0];
dnodes[offset + 2].x = node.bounds_s.max[1];
dnodes[offset + 2].y = node.bounds_s.max[2];
dnodes[offset + 2].z = node.bounds_o.theta_o;
dnodes[offset + 2].w = node.bounds_o.theta_e;
dnodes[offset + 3].x = node.bounds_o.axis[0];
dnodes[offset + 3].y = node.bounds_o.axis[1];
dnodes[offset + 3].z = node.bounds_o.axis[2];
dnodes[offset + 3].w = node.energy_variance;
offset += 4;
if ((node.right_child_offset == -1) && (node.num_lights > 1)) {
num_leaf_lights += node.num_lights;
}
}
/* store information needed for importance computations for each emitter
* in leaf nodes containing several emitters.
*
* each leaf node with several emitters stores relevant information about
* its emitters in the light_tree_leaf_emitters array. each such node
* also stores an offset into the light_tree_leaf_emitters array to where
* its first light is. this offset is stored in leaf_to_first_emitter.
*/
float4 *leaf_emitters = dscene->light_tree_leaf_emitters.alloc(num_leaf_lights * 3);
int *leaf_to_first_emitter = dscene->leaf_to_first_emitter.alloc(nodes.size());
offset = 0;
for (int i = 0; i < nodes.size(); ++i) {
const CompactNode &node = nodes[i];
/* only store this information for leaf nodes with several emitters */
if (!((node.right_child_offset == -1) && (node.num_lights > 1))) {
leaf_to_first_emitter[i] = -1;
continue;
}
leaf_to_first_emitter[i] = offset;
int start = node.first_prim_offset; // distribution id
int end = start + node.num_lights;
for (int j = start; j < end; ++j) {
/* todo: is there a better way than recalcing this? */
/* have getters for the light tree that just accesses build_data? */
BoundBox bbox = light_tree.compute_bbox(emissive_prims[j]);
Orientation bcone = light_tree.compute_bcone(emissive_prims[j]);
float energy = light_tree.compute_energy(emissive_prims[j]);
leaf_emitters[offset].x = bbox.min[0];
leaf_emitters[offset].y = bbox.min[1];
leaf_emitters[offset].z = bbox.min[2];
leaf_emitters[offset].w = bbox.max[0];
leaf_emitters[offset + 1].x = bbox.max[1];
leaf_emitters[offset + 1].y = bbox.max[2];
leaf_emitters[offset + 1].z = bcone.theta_o;
leaf_emitters[offset + 1].w = bcone.theta_e;
leaf_emitters[offset + 2].x = bcone.axis[0];
leaf_emitters[offset + 2].y = bcone.axis[1];
leaf_emitters[offset + 2].z = bcone.axis[2];
leaf_emitters[offset + 2].w = energy;
offset += 3;
}
}
if (progress.get_cancel())
return;
/* create CDF for distant lights, background lights and light tree */
float tree_energy = (nodes.size() > 0) ? nodes[0].energy : 0.0f;
float distant_energy = distant_lights_energy(scene, emissive_prims);
float background_energy = background_light_energy(
device, dscene, scene, progress, emissive_prims);
/* stores the function that the CDF will be generated from */
float3 func = make_float3(tree_energy, distant_energy, background_energy);
/* probs stores the probability of sampling each of the light groups.
* probs[0] corresponds to the probability to sample the tree, etc. */
float3 probs;
float4 cdf = make_float4(0.0f);
const int num_func_values = LIGHTGROUP_NUM;
const int num_cdf_values = num_func_values + 1;
for (int i = 1; i < num_cdf_values; ++i) {
cdf[i] = cdf[i - 1] + func[i - 1];
}
float func_integral = cdf[num_func_values];
if (func_integral == 0.0f) { // Sample uniformly if no energy
for (int i = 1; i < num_cdf_values; ++i) {
cdf[i] = (float)i / num_func_values;
}
probs = make_float3(1.0f / (float)num_func_values);
}
else {
for (int i = 0; i < num_cdf_values; ++i) {
cdf[i] /= func_integral;
}
probs = func / func_integral;
}
/* create and fill device arrays for the light group probabilities and CDF */
float *type_cdf = dscene->light_group_sample_cdf.alloc(num_cdf_values);
for (int i = 0; i < num_cdf_values; ++i) {
type_cdf[i] = cdf[i];
}
float *type_prob = dscene->light_group_sample_prob.alloc(num_func_values);
for (int i = 0; i < num_func_values; ++i) {
type_prob[i] = probs[i];
}
if (progress.get_cancel())
return;
/* find mapping between distribution_id to node_id, used for MIS */
uint *distribution_to_node = dscene->light_distribution_to_node.alloc(num_distribution);
for (int i = 0; i < nodes.size(); ++i) {
const CompactNode &node = nodes[i];
if (node.right_child_offset != -1)
continue; // Skip interior nodes
int start = node.first_prim_offset; // distribution id
int end = start + node.num_lights;
for (int j = start; j < end; ++j) {
distribution_to_node[j] = 4 * i;
}
}
}
if (progress.get_cancel())
return;
/* find mapping between lamp_id to distribution_id, used for MIS */
uint *lamp_to_distribution = dscene->lamp_to_distribution.alloc(num_lights);
for (int i = 0; i < emissive_prims.size(); ++i) {
const Primitive &prim = emissive_prims[i];
if (prim.prim_id >= 0)
continue; // Skip triangles
int lamp_id = -prim.prim_id - 1; // This should not use prim.lamp_id
lamp_to_distribution[lamp_id] = i;
}
/* find mapping between [triangle_id, object_id] to distribution_id, used for MIS */
/* tri_to_distr has the following format:
* [triangle_id0, object_id0, distrib_id0, triangle_id1,..]
* where [triangle_idX, object_idX] is mapped to distrib_idX. */
vector<std::tuple<uint, uint, uint>> tri_to_distr;
tri_to_distr.reserve(num_triangles);
for (int i = 0; i < emissive_prims.size(); ++i) {
const Primitive &prim = emissive_prims[i];
if (prim.prim_id < 0)
continue; // Skip lamps
tri_to_distr.push_back(std::make_tuple(prim.prim_id, prim.object_id, i));
}
std::sort(tri_to_distr.begin(), tri_to_distr.end());
assert(num_triangles == tri_to_distr.size());
uint *triangle_to_distribution = dscene->triangle_to_distribution.alloc(num_triangles * 3);
for (int i = 0; i < tri_to_distr.size(); ++i) {
triangle_to_distribution[3 * i] = std::get<0>(tri_to_distr[i]);
triangle_to_distribution[3 * i + 1] = std::get<1>(tri_to_distr[i]);
triangle_to_distribution[3 * i + 2] = std::get<2>(tri_to_distr[i]);
}
if (progress.get_cancel())
return;
/* create light distribution in same order as the emissive_prims */
/* emission area */
KernelLightDistribution *distribution = dscene->light_distribution.alloc(num_distribution + 1);
float totarea = 0.0f;
/* triangles */
size_t offset = 0;
int j = 0;
foreach (Object *object, scene->objects) {
assert(emissive_prims.size() == num_distribution);
/* create distributions for mesh lights */
foreach (Primitive prim, emissive_prims) {
if (progress.get_cancel())
return;
if (!object_usable_as_light(object)) {
j++;
if (prim.prim_id < 0) { // Early exit for lights
offset++;
continue;
}
const Object *object = scene->objects[prim.object_id];
/* Sum area. */
Mesh *mesh = static_cast<Mesh *>(object->geometry);
bool transform_applied = mesh->transform_applied;
Transform tfm = object->tfm;
int object_id = j;
int shader_flag = 0;
if (!(object->visibility & PATH_RAY_DIFFUSE)) {
@@ -364,39 +691,16 @@ void LightManager::device_update_distribution(Device *,
use_light_visibility = true;
}
size_t mesh_num_triangles = mesh->num_triangles();
for (size_t i = 0; i < mesh_num_triangles; i++) {
int shader_index = mesh->shader[i];
Shader *shader = (shader_index < mesh->used_shaders.size()) ?
mesh->used_shaders[shader_index] :
scene->default_surface;
int triangle_id = prim.prim_id - mesh->prim_offset;
const float area = mesh->compute_triangle_area(triangle_id, object->tfm);
distribution[offset].area = area;
distribution[offset].totarea = totarea;
distribution[offset].prim = prim.prim_id;
distribution[offset].mesh_light.shader_flag = shader_flag;
distribution[offset].mesh_light.object_id = prim.object_id;
offset++;
if (shader->use_mis && shader->has_surface_emission) {
distribution[offset].totarea = totarea;
distribution[offset].prim = i + mesh->prim_offset;
distribution[offset].mesh_light.shader_flag = shader_flag;
distribution[offset].mesh_light.object_id = object_id;
offset++;
Mesh::Triangle t = mesh->get_triangle(i);
if (!t.valid(&mesh->verts[0])) {
continue;
}
float3 p1 = mesh->verts[t.v[0]];
float3 p2 = mesh->verts[t.v[1]];
float3 p3 = mesh->verts[t.v[2]];
if (!transform_applied) {
p1 = transform_point(&tfm, p1);
p2 = transform_point(&tfm, p2);
p3 = transform_point(&tfm, p3);
}
totarea += triangle_area(p1, p2, p3);
}
}
j++;
totarea += area;
}
float trianglearea = totarea;
@@ -404,19 +708,29 @@ void LightManager::device_update_distribution(Device *,
/* point lights */
float lightarea = (totarea > 0.0f) ? totarea / num_lights : 1.0f;
bool use_lamp_mis = false;
offset = 0;
int light_index = 0;
foreach (Light *light, scene->lights) {
if (!light->is_enabled)
/* create distributions for lights */
foreach (Primitive prim, emissive_prims) {
if (progress.get_cancel())
return;
if (prim.prim_id >= 0) { // Early exit for mesh lights
offset++;
continue;
}
const Light *light = scene->lights[prim.lamp_id];
distribution[offset].area = lightarea;
distribution[offset].totarea = totarea;
distribution[offset].prim = ~light_index;
distribution[offset].prim = prim.prim_id;
distribution[offset].lamp.pad = 1.0f;
distribution[offset].lamp.size = light->size;
totarea += lightarea;
if (light->type == LIGHT_DISTANT) {
num_distant_lights++;
use_lamp_mis |= (light->angle > 0.0f && light->use_mis);
}
else if (light->type == LIGHT_POINT || light->type == LIGHT_SPOT) {
@@ -430,11 +744,11 @@ void LightManager::device_update_distribution(Device *,
background_mis |= light->use_mis;
}
light_index++;
offset++;
}
/* normalize cumulative distribution functions */
distribution[num_distribution].area = 0.0f;
distribution[num_distribution].totarea = totarea;
distribution[num_distribution].prim = 0.0f;
distribution[num_distribution].lamp.pad = 0.0f;
@@ -456,12 +770,34 @@ void LightManager::device_update_distribution(Device *,
kintegrator->use_direct_light = (totarea > 0.0f);
if (kintegrator->use_direct_light) {
/* update light tree */
kintegrator->use_light_tree = scene->integrator->use_light_tree;
kintegrator->splitting_threshold = scene->integrator->splitting_threshold;
dscene->light_tree_nodes.copy_to_device();
dscene->light_distribution_to_node.copy_to_device();
dscene->lamp_to_distribution.copy_to_device();
dscene->triangle_to_distribution.copy_to_device();
dscene->light_group_sample_cdf.copy_to_device();
dscene->light_group_sample_prob.copy_to_device();
dscene->leaf_to_first_emitter.copy_to_device();
dscene->light_tree_leaf_emitters.copy_to_device();
kintegrator->num_light_nodes = dscene->light_tree_nodes.size() / LIGHT_TREE_NODE_SIZE;
// TODO: Currently this is only the correct offset when using light tree
kintegrator->distant_lights_offset = num_distribution - num_distant_lights;
kintegrator->background_light_index = background_index;
/* number of emissives */
kintegrator->num_distribution = num_distribution;
kintegrator->num_triangle_lights = num_triangles;
kintegrator->num_distant_lights = num_distant_lights;
kintegrator->inv_num_distant_lights = 1.0f / (float)num_distant_lights;
/* precompute pdfs */
kintegrator->pdf_triangles = 0.0f;
kintegrator->pdf_lights = 0.0f;
kintegrator->pdf_inv_totarea = 1.0f / totarea;
/* sample one, with 0.5 probability of light or triangle */
kintegrator->num_all_lights = num_lights;
@@ -509,10 +845,24 @@ void LightManager::device_update_distribution(Device *,
kbackground->map_weight = background_mis ? 1.0f : 0.0f;
}
else {
dscene->light_group_sample_cdf.free();
dscene->light_group_sample_prob.free();
dscene->leaf_to_first_emitter.free();
dscene->light_tree_leaf_emitters.free();
dscene->light_distribution.free();
dscene->light_tree_nodes.free();
dscene->light_distribution_to_node.free();
dscene->lamp_to_distribution.free();
dscene->triangle_to_distribution.free();
kintegrator->pdf_inv_totarea = 0.0f;
kintegrator->num_light_nodes = 0;
kintegrator->num_triangle_lights = 0;
kintegrator->use_light_tree = false;
kintegrator->num_distribution = 0;
kintegrator->num_all_lights = 0;
kintegrator->num_distant_lights = 0;
kintegrator->inv_num_distant_lights = 0.0f;
kintegrator->pdf_triangles = 0.0f;
kintegrator->pdf_lights = 0.0f;
kintegrator->use_lamp_mis = false;
@@ -521,8 +871,9 @@ void LightManager::device_update_distribution(Device *,
kbackground->portal_offset = 0;
kbackground->portal_weight = 0.0f;
kbackground->sun_weight = 0.0f;
kintegrator->distant_lights_offset = 0;
kintegrator->background_light_index = 0;
kbackground->map_weight = 0.0f;
kfilm->pass_shadow_scale = 1.0f;
}
}
@@ -634,7 +985,7 @@ void LightManager::device_update_background(Device *device,
sun_direction = transform_direction(&sky_transform, sun_direction);
/* Pack sun direction and size. */
float half_angle = sky->sun_size * 0.5f;
float half_angle = sky->get_sun_size() * 0.5f;
kbackground->sun = make_float4(
sun_direction.x, sun_direction.y, sun_direction.z, half_angle);
@@ -662,14 +1013,6 @@ void LightManager::device_update_background(Device *device,
/* If the resolution isn't set manually, try to find an environment texture. */
if (res.x == 0) {
res = environment_res;
if (res.x > 0 && res.y > 0) {
VLOG(2) << "Automatically set World MIS resolution to " << res.x << " by " << res.y << "\n";
}
}
/* If it's still unknown, just use the default. */
if (res.x == 0 || res.y == 0) {
res = make_int2(1024, 512);
VLOG(2) << "Setting World MIS resolution to default\n";
}
kbackground->map_res_x = res.x;
kbackground->map_res_y = res.y;
@@ -1000,11 +1343,19 @@ void LightManager::device_update(Device *device,
void LightManager::device_free(Device *, DeviceScene *dscene, const bool free_background)
{
dscene->light_distribution.free();
dscene->light_tree_nodes.free();
dscene->light_distribution_to_node.free();
dscene->lamp_to_distribution.free();
dscene->triangle_to_distribution.free();
dscene->lights.free();
if (free_background) {
dscene->light_background_marginal_cdf.free();
dscene->light_background_conditional_cdf.free();
}
dscene->light_group_sample_prob.free();
dscene->light_group_sample_cdf.free();
dscene->leaf_to_first_emitter.free();
dscene->light_tree_leaf_emitters.free();
dscene->ies_lights.free();
}
@@ -1131,4 +1482,32 @@ void LightManager::device_update_ies(DeviceScene *dscene)
}
}
int2 LightManager::get_background_map_resolution(const Light *background_light, const Scene *scene)
{
int2 res = make_int2(background_light->map_resolution, background_light->map_resolution / 2);
/* If the resolution isn't set manually, try to find an environment texture. */
if (res.x == 0) {
Shader *shader = (scene->background->shader) ? scene->background->shader :
scene->default_background;
foreach (ShaderNode *node, shader->graph->nodes) {
if (node->type == EnvironmentTextureNode::node_type) {
EnvironmentTextureNode *env = (EnvironmentTextureNode *)node;
ImageMetaData metadata = env->handle.metadata();
res.x = max(res.x, metadata.width);
res.y = max(res.y, metadata.height);
}
}
if (res.x > 0 && res.y > 0) {
VLOG(2) << "Automatically set World MIS resolution to " << res.x << " by " << res.y << "\n";
}
}
/* If it's still unknown, just use the default. */
if (res.x == 0 || res.y == 0) {
res = make_int2(1024, 512);
VLOG(2) << "Setting World MIS resolution to default\n";
}
return res;
}
CCL_NAMESPACE_END

View File

@@ -31,6 +31,7 @@ CCL_NAMESPACE_BEGIN
class Device;
class DeviceScene;
class Object;
class Primitive;
class Progress;
class Scene;
class Shader;
@@ -128,6 +129,18 @@ class LightManager {
/* Check whether light manager can use the object as a light-emissive. */
bool object_usable_as_light(Object *object);
/* compute energy of background lights */
float background_light_energy(Device *device,
DeviceScene *dscene,
Scene *scene,
Progress &progress,
const vector<Primitive> &prims);
/* compute energy of distant lights */
float distant_lights_energy(const Scene *scene, const vector<Primitive> &prims);
int2 get_background_map_resolution(const Light *background_light, const Scene *scene);
struct IESSlot {
IESFile ies;
uint hash;

View File

@@ -0,0 +1,601 @@
/*
* Copyright 2011-2018 Blender Foundation
*
* 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.
*/
#include "render/light_tree.h"
#include "render/light.h"
#include "render/mesh.h"
#include "render/object.h"
#include "util/util_foreach.h"
#include "util/util_logging.h"
CCL_NAMESPACE_BEGIN
LightTree::LightTree(const vector<Primitive> &prims_,
Scene *scene_,
const uint max_lights_in_node_)
: max_lights_in_node(max_lights_in_node_), scene(scene_)
{
if (prims_.empty())
return;
/* background and distant lights are not added to the light tree and are
* considered seperately. so here all primitives except background and
* distant lights are moved into a local primitives array */
primitives.reserve(prims_.size());
vector<Primitive> distant_lights;
vector<Primitive> background_lights;
foreach (Primitive prim, prims_) {
/* put background and distant lights into their own arrays */
if (prim.prim_id < 0) {
const Light *lamp = scene->lights[prim.lamp_id];
if (lamp->type == LIGHT_DISTANT) {
distant_lights.push_back(prim);
continue;
}
else if (lamp->type == LIGHT_BACKGROUND) {
background_lights.push_back(prim);
continue;
}
}
primitives.push_back(prim);
}
/* initialize build_data array that stores the energy and spatial and
* orientation bounds for each light. */
vector<BVHPrimitiveInfo> build_data;
build_data.reserve(primitives.size());
for (int i = 0; i < primitives.size(); ++i) {
BoundBox bbox = compute_bbox(primitives[i]);
Orientation bcone = compute_bcone(primitives[i]);
float energy = compute_energy(primitives[i]);
build_data.push_back(BVHPrimitiveInfo(i, bbox, bcone, energy));
}
/* recursively build BVH tree */
uint total_nodes = 0;
vector<Primitive> ordered_prims;
ordered_prims.reserve(primitives.size());
BVHBuildNode *root = recursive_build(
0, primitives.size(), build_data, total_nodes, ordered_prims);
/* order the primitives array so lights belonging to the same node are
* next to each other */
primitives.swap(ordered_prims);
ordered_prims.clear();
build_data.clear();
/* add background lights to the primitives array */
for (int i = 0; i < background_lights.size(); ++i) {
primitives.push_back(background_lights[i]);
}
/* add distant lights to the end of primitives array */
for (int i = 0; i < distant_lights.size(); ++i) {
primitives.push_back(distant_lights[i]);
}
VLOG(1) << "Total BVH nodes: " << total_nodes;
if (!root)
return;
/* convert to linear representation of the tree */
nodes.resize(total_nodes);
int offset = 0;
flattenBVHTree(*root, offset);
assert(offset == total_nodes);
}
int LightTree::flattenBVHTree(const BVHBuildNode &node, int &offset)
{
CompactNode &compact_node = nodes[offset];
compact_node.bounds_s = node.bbox;
compact_node.bounds_o = node.bcone;
int my_offset = offset++;
if (node.is_leaf) {
/* create leaf node */
assert(!node.children[0] && !node.children[1]);
compact_node.energy = node.energy;
compact_node.energy_variance = node.energy_variance;
compact_node.first_prim_offset = node.first_prim_offset;
compact_node.num_lights = node.num_lights;
}
else {
/* create interior compact node */
compact_node.num_lights = node.num_lights;
compact_node.energy = node.energy;
compact_node.energy_variance = node.energy_variance;
assert(node.children[0] && node.children[1]);
flattenBVHTree(*node.children[0], offset);
compact_node.right_child_offset = flattenBVHTree(*node.children[1], offset);
compact_node.energy = node.energy;
}
return my_offset;
}
BoundBox LightTree::compute_bbox(const Primitive &prim)
{
BoundBox bbox = BoundBox::empty;
if (prim.prim_id >= 0) {
/* extract bounding box from emissive triangle */
const Object *object = scene->objects[prim.object_id];
const Mesh *mesh = (Mesh *)object->geometry;
const int triangle_id = prim.prim_id - mesh->prim_offset;
const Mesh::Triangle triangle = mesh->get_triangle(triangle_id);
float3 p0 = mesh->verts[triangle.v[0]];
float3 p1 = mesh->verts[triangle.v[1]];
float3 p2 = mesh->verts[triangle.v[2]];
/* instanced mesh lights have not applied their transform at this point.
* in this case, these points have to be transformed to get the proper
* spatial bound. */
if (!mesh->transform_applied) {
const Transform &tfm = object->tfm;
p0 = transform_point(&tfm, p0);
p1 = transform_point(&tfm, p1);
p2 = transform_point(&tfm, p2);
}
bbox.grow(p0);
bbox.grow(p1);
bbox.grow(p2);
}
else {
/* extract bounding box from lamp based on light type */
Light *lamp = scene->lights[prim.lamp_id];
if (lamp->type == LIGHT_POINT || lamp->type == LIGHT_SPOT) {
float radius = lamp->size;
bbox.grow(lamp->co + make_float3(radius));
bbox.grow(lamp->co - make_float3(radius));
}
else if (lamp->type == LIGHT_AREA) {
const float3 center = lamp->co;
const float3 half_axisu = 0.5f * lamp->axisu * (lamp->sizeu * lamp->size);
const float3 half_axisv = 0.5f * lamp->axisv * (lamp->sizev * lamp->size);
const float3 p0 = center - half_axisu - half_axisv;
const float3 p1 = center - half_axisu + half_axisv;
const float3 p2 = center + half_axisu - half_axisv;
const float3 p3 = center + half_axisu + half_axisv;
bbox.grow(p0);
bbox.grow(p1);
bbox.grow(p2);
bbox.grow(p3);
}
else {
/* LIGHT_DISTANT and LIGHT_BACKGROUND are handled separately */
assert(false);
}
}
return bbox;
}
Orientation LightTree::compute_bcone(const Primitive &prim)
{
Orientation bcone;
if (prim.prim_id >= 0) {
/* extract bounding cone from emissive triangle */
const Object *object = scene->objects[prim.object_id];
const Mesh *mesh = (Mesh *)object->geometry;
const int triangle_id = prim.prim_id - mesh->prim_offset;
const Mesh::Triangle triangle = mesh->get_triangle(triangle_id);
float3 p0 = mesh->verts[triangle.v[0]];
float3 p1 = mesh->verts[triangle.v[1]];
float3 p2 = mesh->verts[triangle.v[2]];
if (!mesh->transform_applied) {
const Transform &tfm = object->tfm;
p0 = transform_point(&tfm, p0);
p1 = transform_point(&tfm, p1);
p2 = transform_point(&tfm, p2);
}
float3 normal = make_float3(1.0f, 0.0f, 0.0f);
const float3 norm = cross(p1 - p0, p2 - p0);
const float normlen = len(norm);
if (normlen != 0.0f) {
normal = norm / normlen;
}
bcone.axis = normal;
bcone.theta_o = 0.0f;
bcone.theta_e = M_PI_2_F;
}
else {
Light *lamp = scene->lights[prim.lamp_id];
bcone.axis = lamp->dir / len(lamp->dir);
if (lamp->type == LIGHT_POINT) {
bcone.theta_o = M_PI_F;
bcone.theta_e = M_PI_2_F;
}
else if (lamp->type == LIGHT_SPOT) {
bcone.theta_o = 0;
bcone.theta_e = lamp->spot_angle * 0.5f;
}
else if (lamp->type == LIGHT_AREA) {
bcone.theta_o = 0;
bcone.theta_e = M_PI_2_F;
}
}
return bcone;
}
float LightTree::compute_energy(const Primitive &prim)
{
float3 emission = make_float3(0.0f);
Shader *shader = NULL;
if (prim.prim_id >= 0) {
/* extract shader from emissive triangle */
const Object *object = scene->objects[prim.object_id];
const Mesh *mesh = (Mesh *)object->geometry;
const int triangle_id = prim.prim_id - mesh->prim_offset;
int shader_index = mesh->shader[triangle_id];
shader = mesh->used_shaders.at(shader_index);
/* get emission from shader */
bool is_constant_emission = shader->is_constant_emission(&emission);
if (!is_constant_emission) {
emission = make_float3(1.0f);
}
const Transform &tfm = scene->objects[prim.object_id]->tfm;
float area = mesh->compute_triangle_area(triangle_id, tfm);
emission *= area * 4;
}
else {
const Light *light = scene->lights[prim.lamp_id];
emission = light->strength;
/* calculate the max emission in a single direction. */
if (light->type == LIGHT_POINT) {
emission /= M_PI_F;
}
else if (light->type == LIGHT_SPOT) {
emission /= M_PI_F;
}
else if (light->type == LIGHT_AREA) {
}
else {
/* LIGHT_DISTANT and LIGHT_BACKGROUND are handled separately */
assert(false);
}
}
return scene->shader_manager->linear_rgb_to_gray(emission);
}
Orientation LightTree::combine_bounding_cones(const vector<Orientation> &bcones)
{
if (bcones.size() == 0) {
return Orientation();
}
else if (bcones.size() == 1) {
return bcones[0];
}
Orientation cone = bcones[0];
for (int i = 1; i < bcones.size(); ++i) {
cone = cone_union(cone, bcones[i]);
}
return cone;
}
/* Algorithm 1 */
Orientation LightTree::cone_union(const Orientation &cone1, const Orientation &cone2)
{
const Orientation *a = &cone1;
const Orientation *b = &cone2;
if (b->theta_o > a->theta_o) {
a = &cone2;
b = &cone1;
}
float theta_d = safe_acosf(dot(a->axis, b->axis));
float theta_e = fmaxf(a->theta_e, b->theta_e);
if (fminf(theta_d + b->theta_o, M_PI_F) <= a->theta_o) {
return Orientation(a->axis, a->theta_o, theta_e);
}
float theta_o = (a->theta_o + theta_d + b->theta_o) * 0.5f;
if (M_PI_F <= theta_o) {
return Orientation(a->axis, M_PI_F, theta_e);
}
float theta_r = theta_o - a->theta_o;
float3 axis = rotate_around_axis(a->axis, cross(a->axis, b->axis), theta_r);
axis = normalize(axis);
return Orientation(axis, theta_o, theta_e);
}
float LightTree::calculate_cone_measure(const Orientation &bcone)
{
/* eq. 1 */
float theta_w = fminf(bcone.theta_o + bcone.theta_e, M_PI_F);
return M_2PI_F *
(1.0f - cosf(bcone.theta_o) + 0.5f * (theta_w - bcone.theta_o) * sinf(bcone.theta_o) +
0.25f * cosf(bcone.theta_o) - 0.25f * cosf(bcone.theta_o - 2.0f * theta_w));
}
void LightTree::split_saoh(const BoundBox &centroid_bbox,
const vector<BVHPrimitiveInfo> &build_data,
const int start,
const int end,
const int num_buckets,
const float node_M_Omega,
const BoundBox &node_bbox,
float &min_cost,
int &min_dim,
int &min_bucket)
{
struct BucketInfo {
BucketInfo() : count(0), energy(0.0f)
{
bounds = BoundBox::empty;
}
int count;
float energy; // total energy
BoundBox bounds; // bounds of all primitives
Orientation bcone;
};
min_cost = std::numeric_limits<float>::max();
min_bucket = -1;
const float extent_max = max3(centroid_bbox.size());
for (int dim = 0; dim < 3; ++dim) {
vector<BucketInfo> buckets(num_buckets);
vector<vector<Orientation>> bucket_bcones(num_buckets);
/* calculate total energy in each bucket and a bbox of it */
const float extent = centroid_bbox.max[dim] - centroid_bbox.min[dim];
if (extent == 0.0f) { // All dims cannot be zero
continue;
}
const float extent_inv = 1.0f / extent;
for (unsigned int i = start; i < end; ++i) {
int bucket_id = (int)((float)num_buckets *
(build_data[i].centroid[dim] - centroid_bbox.min[dim]) * extent_inv);
if (bucket_id == num_buckets)
bucket_id = num_buckets - 1;
buckets[bucket_id].count++;
buckets[bucket_id].energy += build_data[i].energy;
buckets[bucket_id].bounds.grow(build_data[i].bbox);
bucket_bcones[bucket_id].push_back(build_data[i].bcone);
}
for (unsigned int i = 0; i < num_buckets; ++i) {
if (buckets[i].count != 0) {
buckets[i].bcone = combine_bounding_cones(bucket_bcones[i]);
}
}
/* compute costs for splitting at bucket boundaries */
vector<float> cost(num_buckets - 1);
BoundBox bbox_L, bbox_R;
float energy_L, energy_R;
vector<Orientation> bcones_L, bcones_R;
for (int i = 0; i < num_buckets - 1; ++i) {
bbox_L = BoundBox::empty;
bbox_R = BoundBox::empty;
energy_L = 0;
energy_R = 0;
bcones_L.clear();
bcones_R.clear();
/* L corresponds to all buckets up to and including i */
for (int j = 0; j <= i; ++j) {
if (buckets[j].count != 0) {
energy_L += buckets[j].energy;
bbox_L.grow(buckets[j].bounds);
bcones_L.push_back(buckets[j].bcone);
}
}
/* R corresponds to bucket i+1 and all after */
for (int j = i + 1; j < num_buckets; ++j) {
if (buckets[j].count != 0) {
energy_R += buckets[j].energy;
bbox_R.grow(buckets[j].bounds);
bcones_R.push_back(buckets[j].bcone);
}
}
/* eq. 2 */
const Orientation bcone_L = combine_bounding_cones(bcones_L);
const Orientation bcone_R = combine_bounding_cones(bcones_R);
const float M_Omega_L = calculate_cone_measure(bcone_L);
const float M_Omega_R = calculate_cone_measure(bcone_R);
const float K = extent_max * extent_inv;
cost[i] = K * (energy_L * M_Omega_L * bbox_L.area() + energy_R * M_Omega_R * bbox_R.area()) /
(node_M_Omega * node_bbox.area());
}
/* update minimum cost, dim and bucket */
for (int i = 0; i < num_buckets - 1; ++i) {
if (cost[i] < min_cost) {
min_cost = cost[i];
min_dim = dim;
min_bucket = i;
}
}
}
}
BVHBuildNode *LightTree::recursive_build(const uint start,
const uint end,
vector<BVHPrimitiveInfo> &build_data,
uint &total_nodes,
vector<Primitive> &ordered_prims)
{
if (build_data.size() == 0)
return NULL;
total_nodes++;
BVHBuildNode *node = new BVHBuildNode();
/* compute bounds and energy for all emissive primitives in node */
BoundBox node_bbox = BoundBox::empty;
vector<Orientation> bcones;
bcones.reserve(end - start);
double node_energy = 0.0;
double node_energy_sum_squared = 0.0;
uint num_lights = end - start;
for (unsigned int i = start; i < end; ++i) {
const BVHPrimitiveInfo &light = build_data.at(i);
node_bbox.grow(light.bbox);
bcones.push_back(light.bcone);
double energy = (double)light.energy;
node_energy += energy;
node_energy_sum_squared += energy * energy;
}
/* pre-calculate energy variance for the splitting heuristic */
double node_energy_mean = node_energy / (double)num_lights;
double node_energy_variance = node_energy_sum_squared / (double)num_lights - // E[e^2]
node_energy_mean * node_energy_mean; // E[e]^2
node_energy_variance = max(node_energy_variance, 0.0);
Orientation node_bcone = combine_bounding_cones(bcones);
bcones.clear();
const float node_M_Omega = calculate_cone_measure(node_bcone);
if (num_lights == 1) {
/* create leaf */
int first_prim_offset = ordered_prims.size();
int prim = build_data.at(start).primitive_offset;
ordered_prims.push_back(primitives.at(prim));
node->init_leaf(
first_prim_offset, num_lights, node_bbox, node_bcone, node_energy, node_energy_variance);
return node;
}
else {
/* compute spatial bound for primitive centroids */
BoundBox centroid_bbox = BoundBox::empty;
for (unsigned int i = start; i < end; ++i) {
centroid_bbox.grow(build_data.at(i).centroid);
}
/* find dimension of bounding box with maximum extent */
float3 diag = centroid_bbox.size();
int max_dim;
if (diag[0] > diag[1] && diag[0] > diag[2]) {
max_dim = 0;
}
else if (diag[1] > diag[2]) {
max_dim = 1;
}
else {
max_dim = 2;
}
/* checks special case if all lights are in the same place */
if (centroid_bbox.max[max_dim] == centroid_bbox.min[max_dim]) {
/* create leaf */
int first_prim_offset = ordered_prims.size();
for (int i = start; i < end; ++i) {
int prim = build_data.at(i).primitive_offset;
ordered_prims.push_back(primitives.at(prim));
}
node->init_leaf(
first_prim_offset, num_lights, node_bbox, node_bcone, node_energy, node_energy_variance);
return node;
}
else {
/* find dimension and bucket with smallest SAOH cost */
const int num_buckets = 12;
float min_cost;
int min_dim, min_bucket;
split_saoh(centroid_bbox,
build_data,
start,
end,
num_buckets,
node_M_Omega,
node_bbox,
min_cost,
min_dim,
min_bucket);
assert(min_dim != -1);
int mid = 0;
if (num_lights > max_lights_in_node || min_cost < (float)node_energy) {
/* partition primitives */
BVHPrimitiveInfo *mid_ptr = std::partition(
&build_data[start],
&build_data[end - 1] + 1,
CompareToBucket(min_bucket, num_buckets, min_dim, centroid_bbox));
mid = mid_ptr - &build_data[0];
}
else {
/* create leaf */
int first_prim_offset = ordered_prims.size();
for (int i = start; i < end; ++i) {
int prim = build_data.at(i).primitive_offset;
ordered_prims.push_back(primitives.at(prim));
}
node->init_leaf(first_prim_offset,
num_lights,
node_bbox,
node_bcone,
node_energy,
node_energy_variance);
return node;
}
/* build children */
BVHBuildNode *left = recursive_build(start, mid, build_data, total_nodes, ordered_prims);
BVHBuildNode *right = recursive_build(mid, end, build_data, total_nodes, ordered_prims);
node->init_interior(left, right, node_bcone, num_lights, node_energy, node_energy_variance);
}
}
return node;
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,356 @@
/*
* Copyright 2011-2018 Blender Foundation
*
* 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.
*/
#ifndef __LIGHT_TREE_H__
#define __LIGHT_TREE_H__
#include "util/util_boundbox.h"
CCL_NAMESPACE_BEGIN
class Light;
class Object;
class Scene;
#define LIGHT_TREE_NODE_SIZE 4
/* Data structure to represent orientation bounds. It consists of two bounding
* cones represented by a direction(axis) and two angles out from this axis.
* This can be thought of as two cones.
*/
struct Orientation {
Orientation()
{
axis = make_float3(0.0f, 0.0f, 0.0f);
theta_o = 0;
theta_e = 0;
}
Orientation(const float3 &a, float o, float e) : axis(a), theta_o(o), theta_e(e)
{
}
/* orientation/direction of the cones */
float3 axis;
/* angle bounding light orientations */
float theta_o;
/* angle bounding the directions light can be emitted in */
float theta_e;
};
/* Temporary data structure for nodes during construction.
* After the construction is complete a final step converts the tree consisting
* of these nodes into a tree consisting of CompactNode:s. */
struct BVHBuildNode {
BVHBuildNode()
{
children[0] = children[1] = NULL;
bbox = BoundBox::empty;
}
/* initializes this node as a leaf node */
void init_leaf(
uint first, uint n, const BoundBox &b, const Orientation &c, double e, double e_var)
{
first_prim_offset = first;
num_lights = n;
bbox = b;
bcone = c;
energy = (float)e;
energy_variance = (float)e_var;
is_leaf = true;
}
/* initializes this node as an interior node */
void init_interior(
BVHBuildNode *c0, BVHBuildNode *c1, const Orientation &c, uint n, double e, double e_var)
{
bbox = merge(c0->bbox, c1->bbox);
bcone = c;
children[0] = c0;
children[1] = c1;
num_lights = n;
energy = (float)e;
energy_variance = (float)e_var;
is_leaf = false;
}
/* spatial and orientation bounds */
BoundBox bbox;
Orientation bcone;
/* total energy and energy variance for the lights in the node */
float energy, energy_variance;
/* pointers to the two children */
BVHBuildNode *children[2];
/* each leaf node contains one or more lights. lights that are contained in
* the same node are stored next to each other in the ordered primitives
* array. this offset points to the first of these lights. num_lights below
* can be used to find the last light for this node */
uint first_prim_offset;
/* total number of lights contained in this node */
uint num_lights;
/* if this node is a leaf or not */
bool is_leaf;
};
// TODO: Have this struct in kernel_types.h instead?
/* A more memory efficient representation of BVHBuildNode above. This is the
* structure of the nodes on the device. */
struct CompactNode {
CompactNode()
: right_child_offset(-1),
first_prim_offset(-1),
num_lights(-1),
bounds_s(BoundBox::empty),
energy(0.0f),
energy_variance(0.0f)
{
bounds_o.axis = make_float3(0.0f);
bounds_o.theta_o = 0.0f;
bounds_o.theta_e = 0.0f;
}
/* All compact nodes are stored in a single array. interior nodes can find
* their two child nodes as follows:
* - the left child node is directly after its parent in the nodes array
* - the right child node is at the offset below in the nodes array.
*
* This offset is default initialized to -1 and will only change if this is
* and interior node. this therefore used to see if a node is a leaf/interior
* node as well. */
int right_child_offset;
/* see comment in BVHBuildNode for the variable with the same name */
int first_prim_offset;
/* total number of lights contained in this node */
int num_lights;
/* spatial and orientation bounds */
BoundBox bounds_s;
Orientation bounds_o;
/* total energy and energy variance for the lights in the node */
float energy, energy_variance;
};
/* Helper struct that is only used during the construction of the tree */
struct BVHPrimitiveInfo {
BVHPrimitiveInfo()
{
bbox = BoundBox::empty;
}
BVHPrimitiveInfo(uint offset, const BoundBox &bounds, const Orientation &oBounds, float e)
: primitive_offset(offset), bbox(bounds), centroid(bounds.center()), energy(e)
{
bcone.axis = oBounds.axis;
bcone.theta_o = oBounds.theta_o;
bcone.theta_e = oBounds.theta_e;
}
/* this primitives offset into the unordered primtives array */
uint primitive_offset;
/* spatial and orientation bounds */
BoundBox bbox;
float3 centroid;
Orientation bcone;
/* total energy of this emissive primitive */
float energy;
};
/* A custom pointer struct that points to an emissive triangle or a lamp. */
class Primitive {
public:
/* If prim_id >= 0 then the primitive is a triangle and prim_id is a global
* triangle index.
* If prim_id < 0 then the primitive is a lamp and -prim_id-1 is an index
* into the lights array on the device. */
int prim_id;
union {
/* which object the triangle belongs to */
int object_id;
/* index for this lamp in the scene->lights array */
int lamp_id;
};
Primitive(int prim, int object) : prim_id(prim), object_id(object)
{
}
};
/* Compare operator that returns true if the given light is in a lower
* bucket than a given split_bucket. This is used to partition lights into lights
* for the left and right child during tree construction. */
struct CompareToBucket {
CompareToBucket(int split, int num, int d, const BoundBox &b) : centroid_bbox(b)
{
split_bucket = split;
num_buckets = num;
dim = d;
inv_extent = 1.0f / (centroid_bbox.max[dim] - centroid_bbox.min[dim]);
}
bool operator()(const BVHPrimitiveInfo &p) const
{
int bucket_id = (int)((float)num_buckets * (p.centroid[dim] - centroid_bbox.min[dim]) *
inv_extent);
if (bucket_id == num_buckets) {
bucket_id = num_buckets - 1;
}
return bucket_id <= split_bucket;
}
/* everything lower or equal to the split_bucket is considered to be in one
* child and everything above will be considered to belong to the other. */
int split_bucket;
/* the total number of buckets that are considered for this dimension(dim) */
int num_buckets;
/* the construction creates candidate splits along the three dimensions.
* this variable stores which dimension is currently being split along.*/
int dim;
/* storing the inverse extend of the bounding box along the current
* dimension to only have to do the division once instead of everytime the
* operator() is called. */
float inv_extent;
/* bound for the centroids of all lights of the current node being split */
const BoundBox &centroid_bbox;
};
/* This class takes a set of lights as input and organizes them into a light
* hierarchy. This hierarchy is represented as a Bounding Volume Hierarchy(BVH).
* This is the process to acheive this:
* 1. For each given light, important information is gathered
* - Bounding box of the light
* - Bounding cones of the light
* - The energy of the light
* This first calculated and then stored as BVHPrimitiveInfo for each light.
* 2. A top-down recursive build algorithm creates a BVH consisting of
* BVHBuildNode:s which each are allocated randomly on the heap with new.
* This step also reorders the given array of lights such that lights
* belonging to the same node are next to each other in the primitives array.
* 3. A final step converts this BVH into a more memory efficient layout where
* each BVHBuildNode is converted to a CompactNode and all of these nodes
* are placed next to each other in memory in a single nodes array.
*
* This structure is based on PBRTs geometry BVH implementation.
**/
class LightTree {
public:
LightTree(const vector<Primitive> &prims_, Scene *scene_, const uint max_lights_in_node_);
/* returns the ordered emissive primitives */
const vector<Primitive> &get_primitives() const
{
return primitives;
}
/* returns the array of nodes */
const vector<CompactNode> &get_nodes() const
{
return nodes;
}
/* computes the bounding box for the given light */
BoundBox compute_bbox(const Primitive &prim);
/* computes the orientation bounds for the given light. */
Orientation compute_bcone(const Primitive &prim);
/* computes the emitted energy for the given light. this is done by
* integrating the constant emission over the angles of the sphere it emits
* light in. */
float compute_energy(const Primitive &prim);
private:
/* the top-down recursive build algorithm mentioned in step 2 above */
BVHBuildNode *recursive_build(const uint start,
const uint end,
vector<BVHPrimitiveInfo> &build_data,
uint &total_nodes,
vector<Primitive> &ordered_prims);
/* returns the union of two orientation bounds and returns the result */
Orientation cone_union(const Orientation &a, const Orientation &b);
/* returns the union of several orientation bounds */
Orientation combine_bounding_cones(const vector<Orientation> &bcones);
/* calculates the cone measure in the surface area orientation heuristic */
float calculate_cone_measure(const Orientation &bcone);
/* takes a node and the lights contained in it as input and returns a way to
* split the node into two child nodes. This is done as follows:
* 1. A bounding box of all lights centroid is constructed
* 2. A set of candidate splits(proposed left and right child nodes) are
* created.
* - This is done by partitioning the bounding box into two regions.
* All lights in the same region belongs to the same child node. This
* is done for several partions of the bounding box.
* 3. Each such candidate is evaluated using the Surface Area Orientation
* Heuristic(SAOH).
* 4. The candidate split with the minimum cost(heuristic) is returned */
void split_saoh(const BoundBox &centroid_bbox,
const vector<BVHPrimitiveInfo> &build_data,
const int start,
const int end,
const int num_buckets,
const float node_M_Omega,
const BoundBox &node_bbox,
float &min_cost,
int &min_dim,
int &min_bucket);
/* this method performs step 3 above. */
int flattenBVHTree(const BVHBuildNode &node, int &offset);
/* contains all the lights in the scene. when the constructor has finished,
* these will be ordered such that lights belonging to the same node will be
* next to each other in this array. */
vector<Primitive> primitives;
/* the maximum number of allowed lights in each leaf node */
uint max_lights_in_node;
/* pointer to the scene. this is used to access the objects, the lights and
* the shader manager of the scene.*/
Scene *scene;
/* the nodes of the light hierarchy */
vector<CompactNode> nodes;
};
CCL_NAMESPACE_END
#endif // __LIGHT_TREE_H__

View File

@@ -86,6 +86,25 @@ class Mesh : public Geometry {
return tri;
}
float compute_triangle_area(size_t i, const Transform &tfm) const
{
Mesh::Triangle t = get_triangle(i);
if (!t.valid(&verts[0])) {
return 0.0f;
}
float3 p1 = verts[t.v[0]];
float3 p2 = verts[t.v[1]];
float3 p3 = verts[t.v[2]];
if (!transform_applied) {
p1 = transform_point(&tfm, p1);
p2 = transform_point(&tfm, p2);
p3 = transform_point(&tfm, p3);
}
return triangle_area(p1, p2, p3);
}
size_t num_triangles() const
{
return triangles.size() / 3;

View File

@@ -15,34 +15,25 @@
*/
#include "render/attribute.h"
#include "render/image_vdb.h"
#include "render/mesh.h"
#include "render/scene.h"
#ifdef WITH_OPENVDB
# include <openvdb/tools/Dense.h>
# include <openvdb/tools/GridTransformer.h>
# include <openvdb/tools/Morphology.h>
#endif
#include "util/util_foreach.h"
#include "util/util_hash.h"
#include "util/util_logging.h"
#include "util/util_openvdb.h"
#include "util/util_progress.h"
#include "util/util_types.h"
CCL_NAMESPACE_BEGIN
const int64_t VOXEL_INDEX_NONE = -1;
static int64_t compute_voxel_index(const int3 &resolution, int64_t x, int64_t y, int64_t z)
{
if (x < 0 || x >= resolution.x) {
return VOXEL_INDEX_NONE;
}
else if (y < 0 || y >= resolution.y) {
return VOXEL_INDEX_NONE;
}
else if (z < 0 || z >= resolution.z) {
return VOXEL_INDEX_NONE;
}
return x + y * resolution.x + z * resolution.x * resolution.y;
}
struct QuadData {
int v0, v1, v2, v3;
@@ -123,122 +114,148 @@ static void create_quad(int3 corners[8],
quads.push_back(quad);
}
struct VolumeParams {
int3 resolution;
float3 cell_size;
float3 start_point;
int pad_size;
};
static const int CUBE_SIZE = 8;
/* Create a mesh from a volume.
*
* The way the algorithm works is as follows:
*
* - The coordinates of active voxels from a dense volume (or 3d image) are
* gathered inside an auxiliary volume.
* - Each set of coordinates of an CUBE_SIZE cube are mapped to the same
* coordinate of the auxiliary volume.
* - Quads are created between active and non-active voxels in the auxiliary
* volume to generate a tight mesh around the volume.
* - The topologies of input OpenVDB grids are merged into a temporary grid.
* - Voxels of the temporary grid are dilated to account for the padding necessary for volume
* sampling.
* - Quads are created on the boundary between active and inactive leaf nodes of the temporary
* grid.
*/
class VolumeMeshBuilder {
/* Auxiliary volume that is used to check if a node already added. */
vector<char> grid;
/* The resolution of the auxiliary volume, set to be equal to 1/CUBE_SIZE
* of the original volume on each axis. */
int3 res;
size_t number_of_nodes;
/* Offset due to padding in the original grid. Padding will transform the
* coordinates of the original grid from 0...res to -padding...res+padding,
* so some coordinates are negative, and we need to properly account for
* them. */
int3 pad_offset;
VolumeParams *params;
public:
VolumeMeshBuilder(VolumeParams *volume_params);
#ifdef WITH_OPENVDB
/* use a MaskGrid to store the topology to save memory */
openvdb::MaskGrid::Ptr topology_grid;
openvdb::CoordBBox bbox;
#endif
bool first_grid;
void add_node(int x, int y, int z);
VolumeMeshBuilder();
void add_node_with_padding(int x, int y, int z);
#ifdef WITH_OPENVDB
void add_grid(openvdb::GridBase::ConstPtr grid, bool do_clipping, float volume_clipping);
#endif
void create_mesh(vector<float3> &vertices, vector<int> &indices, vector<float3> &face_normals);
void add_padding(int pad_size);
void create_mesh(vector<float3> &vertices,
vector<int> &indices,
vector<float3> &face_normals,
const float face_overlap_avoidance);
private:
void generate_vertices_and_quads(vector<int3> &vertices_is, vector<QuadData> &quads);
void convert_object_space(const vector<int3> &vertices, vector<float3> &out_vertices);
void convert_object_space(const vector<int3> &vertices,
vector<float3> &out_vertices,
const float face_overlap_avoidance);
void convert_quads_to_tris(const vector<QuadData> &quads,
vector<int> &tris,
vector<float3> &face_normals);
bool empty_grid() const;
#ifdef WITH_OPENVDB
template<typename GridType>
void merge_grid(openvdb::GridBase::ConstPtr grid, bool do_clipping, float volume_clipping)
{
typename GridType::ConstPtr typed_grid = openvdb::gridConstPtrCast<GridType>(grid);
if (do_clipping) {
using ValueType = typename GridType::ValueType;
typename GridType::Ptr copy = typed_grid->deepCopy();
typename GridType::ValueOnIter iter = copy->beginValueOn();
for (; iter; ++iter) {
if (iter.getValue() < ValueType(volume_clipping)) {
iter.setValueOff();
}
}
typed_grid = copy;
}
topology_grid->topologyUnion(*typed_grid);
}
#endif
};
VolumeMeshBuilder::VolumeMeshBuilder(VolumeParams *volume_params)
VolumeMeshBuilder::VolumeMeshBuilder()
{
params = volume_params;
number_of_nodes = 0;
const int64_t x = divide_up(params->resolution.x, CUBE_SIZE);
const int64_t y = divide_up(params->resolution.y, CUBE_SIZE);
const int64_t z = divide_up(params->resolution.z, CUBE_SIZE);
/* Adding 2*pad_size since we pad in both positive and negative directions
* along the axis. */
const int64_t px = divide_up(params->resolution.x + 2 * params->pad_size, CUBE_SIZE);
const int64_t py = divide_up(params->resolution.y + 2 * params->pad_size, CUBE_SIZE);
const int64_t pz = divide_up(params->resolution.z + 2 * params->pad_size, CUBE_SIZE);
res = make_int3(px, py, pz);
pad_offset = make_int3(px - x, py - y, pz - z);
grid.resize(px * py * pz, 0);
first_grid = true;
}
void VolumeMeshBuilder::add_node(int x, int y, int z)
#ifdef WITH_OPENVDB
void VolumeMeshBuilder::add_grid(openvdb::GridBase::ConstPtr grid,
bool do_clipping,
float volume_clipping)
{
/* Map coordinates to index space. */
const int index_x = (x / CUBE_SIZE) + pad_offset.x;
const int index_y = (y / CUBE_SIZE) + pad_offset.y;
const int index_z = (z / CUBE_SIZE) + pad_offset.z;
assert((index_x >= 0) && (index_y >= 0) && (index_z >= 0));
const int64_t index = compute_voxel_index(res, index_x, index_y, index_z);
if (index == VOXEL_INDEX_NONE) {
return;
/* set the transform of our grid from the first one */
if (first_grid) {
topology_grid = openvdb::MaskGrid::create();
topology_grid->setTransform(grid->transform().copy());
first_grid = false;
}
/* if the transforms do not match, we need to resample one of the grids so that
* its index space registers with that of the other, here we resample our mask
* grid so memory usage is kept low */
else if (topology_grid->transform() != grid->transform()) {
openvdb::MaskGrid::Ptr temp_grid = topology_grid->copyWithNewTree();
temp_grid->setTransform(grid->transform().copy());
openvdb::tools::resampleToMatch<openvdb::tools::BoxSampler>(*topology_grid, *temp_grid);
topology_grid = temp_grid;
topology_grid->setTransform(grid->transform().copy());
}
/* We already have a node here. */
if (grid[index] == 1) {
return;
if (grid->isType<openvdb::FloatGrid>()) {
merge_grid<openvdb::FloatGrid>(grid, do_clipping, volume_clipping);
}
else if (grid->isType<openvdb::Vec3fGrid>()) {
merge_grid<openvdb::Vec3fGrid>(grid, do_clipping, volume_clipping);
}
else if (grid->isType<openvdb::Vec4fGrid>()) {
merge_grid<openvdb::Vec4fGrid>(grid, do_clipping, volume_clipping);
}
else if (grid->isType<openvdb::BoolGrid>()) {
merge_grid<openvdb::BoolGrid>(grid, do_clipping, volume_clipping);
}
else if (grid->isType<openvdb::DoubleGrid>()) {
merge_grid<openvdb::DoubleGrid>(grid, do_clipping, volume_clipping);
}
else if (grid->isType<openvdb::Int32Grid>()) {
merge_grid<openvdb::Int32Grid>(grid, do_clipping, volume_clipping);
}
else if (grid->isType<openvdb::Int64Grid>()) {
merge_grid<openvdb::Int64Grid>(grid, do_clipping, volume_clipping);
}
else if (grid->isType<openvdb::Vec3IGrid>()) {
merge_grid<openvdb::Vec3IGrid>(grid, do_clipping, volume_clipping);
}
else if (grid->isType<openvdb::Vec3dGrid>()) {
merge_grid<openvdb::Vec3dGrid>(grid, do_clipping, volume_clipping);
}
else if (grid->isType<openvdb::MaskGrid>()) {
topology_grid->topologyUnion(*openvdb::gridConstPtrCast<openvdb::MaskGrid>(grid));
}
++number_of_nodes;
grid[index] = 1;
}
#endif
void VolumeMeshBuilder::add_node_with_padding(int x, int y, int z)
void VolumeMeshBuilder::add_padding(int pad_size)
{
for (int px = x - params->pad_size; px < x + params->pad_size; ++px) {
for (int py = y - params->pad_size; py < y + params->pad_size; ++py) {
for (int pz = z - params->pad_size; pz < z + params->pad_size; ++pz) {
add_node(px, py, pz);
}
}
}
#ifdef WITH_OPENVDB
openvdb::tools::dilateVoxels(topology_grid->tree(), pad_size);
#else
(void)pad_size;
#endif
}
void VolumeMeshBuilder::create_mesh(vector<float3> &vertices,
vector<int> &indices,
vector<float3> &face_normals)
vector<float3> &face_normals,
const float face_overlap_avoidance)
{
/* We create vertices in index space (is), and only convert them to object
* space when done. */
@@ -247,7 +264,7 @@ void VolumeMeshBuilder::create_mesh(vector<float3> &vertices,
generate_vertices_and_quads(vertices_is, quads);
convert_object_space(vertices_is, vertices);
convert_object_space(vertices_is, vertices, face_overlap_avoidance);
convert_quads_to_tris(quads, indices, face_normals);
}
@@ -255,85 +272,97 @@ void VolumeMeshBuilder::create_mesh(vector<float3> &vertices,
void VolumeMeshBuilder::generate_vertices_and_quads(vector<ccl::int3> &vertices_is,
vector<QuadData> &quads)
{
#ifdef WITH_OPENVDB
const openvdb::MaskGrid::TreeType &tree = topology_grid->tree();
tree.evalLeafBoundingBox(bbox);
const int3 resolution = make_int3(bbox.dim().x(), bbox.dim().y(), bbox.dim().z());
unordered_map<size_t, int> used_verts;
for (int z = 0; z < res.z; ++z) {
for (int y = 0; y < res.y; ++y) {
for (int x = 0; x < res.x; ++x) {
int64_t voxel_index = compute_voxel_index(res, x, y, z);
if (grid[voxel_index] == 0) {
continue;
}
for (auto iter = tree.cbeginLeaf(); iter; ++iter) {
openvdb::CoordBBox leaf_bbox = iter->getNodeBoundingBox();
/* +1 to convert from exclusive to include bounds. */
leaf_bbox.max() = leaf_bbox.max().offsetBy(1);
/* Compute min and max coords of the node in index space. */
int3 min = make_int3((x - pad_offset.x) * CUBE_SIZE,
(y - pad_offset.y) * CUBE_SIZE,
(z - pad_offset.z) * CUBE_SIZE);
int3 min = make_int3(leaf_bbox.min().x(), leaf_bbox.min().y(), leaf_bbox.min().z());
int3 max = make_int3(leaf_bbox.max().x(), leaf_bbox.max().y(), leaf_bbox.max().z());
/* Maximum is just CUBE_SIZE voxels away from minimum on each axis. */
int3 max = make_int3(min.x + CUBE_SIZE, min.y + CUBE_SIZE, min.z + CUBE_SIZE);
int3 corners[8] = {
make_int3(min[0], min[1], min[2]),
make_int3(max[0], min[1], min[2]),
make_int3(max[0], max[1], min[2]),
make_int3(min[0], max[1], min[2]),
make_int3(min[0], min[1], max[2]),
make_int3(max[0], min[1], max[2]),
make_int3(max[0], max[1], max[2]),
make_int3(min[0], max[1], max[2]),
};
int3 corners[8] = {
make_int3(min[0], min[1], min[2]),
make_int3(max[0], min[1], min[2]),
make_int3(max[0], max[1], min[2]),
make_int3(min[0], max[1], min[2]),
make_int3(min[0], min[1], max[2]),
make_int3(max[0], min[1], max[2]),
make_int3(max[0], max[1], max[2]),
make_int3(min[0], max[1], max[2]),
};
/* Only create a quad if on the border between an active and an inactive leaf.
*
* We verify that a leaf exists by probing a coordinate that is at its center,
* to do so we compute the center of the current leaf and offset this coordinate
* by the size of a leaf in each direction.
*/
static const int LEAF_DIM = openvdb::MaskGrid::TreeType::LeafNodeType::DIM;
auto center = leaf_bbox.min() + openvdb::Coord(LEAF_DIM / 2);
/* Only create a quad if on the border between an active and
* an inactive node.
*/
if (!tree.probeLeaf(openvdb::Coord(center.x() - LEAF_DIM, center.y(), center.z()))) {
create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_X_MIN);
}
voxel_index = compute_voxel_index(res, x - 1, y, z);
if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) {
create_quad(corners, vertices_is, quads, res, used_verts, QUAD_X_MIN);
}
if (!tree.probeLeaf(openvdb::Coord(center.x() + LEAF_DIM, center.y(), center.z()))) {
create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_X_MAX);
}
voxel_index = compute_voxel_index(res, x + 1, y, z);
if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) {
create_quad(corners, vertices_is, quads, res, used_verts, QUAD_X_MAX);
}
if (!tree.probeLeaf(openvdb::Coord(center.x(), center.y() - LEAF_DIM, center.z()))) {
create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_Y_MIN);
}
voxel_index = compute_voxel_index(res, x, y - 1, z);
if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) {
create_quad(corners, vertices_is, quads, res, used_verts, QUAD_Y_MIN);
}
if (!tree.probeLeaf(openvdb::Coord(center.x(), center.y() + LEAF_DIM, center.z()))) {
create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_Y_MAX);
}
voxel_index = compute_voxel_index(res, x, y + 1, z);
if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) {
create_quad(corners, vertices_is, quads, res, used_verts, QUAD_Y_MAX);
}
if (!tree.probeLeaf(openvdb::Coord(center.x(), center.y(), center.z() - LEAF_DIM))) {
create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_Z_MIN);
}
voxel_index = compute_voxel_index(res, x, y, z - 1);
if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) {
create_quad(corners, vertices_is, quads, res, used_verts, QUAD_Z_MIN);
}
voxel_index = compute_voxel_index(res, x, y, z + 1);
if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) {
create_quad(corners, vertices_is, quads, res, used_verts, QUAD_Z_MAX);
}
}
if (!tree.probeLeaf(openvdb::Coord(center.x(), center.y(), center.z() + LEAF_DIM))) {
create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_Z_MAX);
}
}
#else
(void)vertices_is;
(void)quads;
#endif
}
void VolumeMeshBuilder::convert_object_space(const vector<int3> &vertices,
vector<float3> &out_vertices)
vector<float3> &out_vertices,
const float face_overlap_avoidance)
{
#ifdef WITH_OPENVDB
/* compute the offset for the face overlap avoidance */
bbox = topology_grid->evalActiveVoxelBoundingBox();
openvdb::Coord dim = bbox.dim();
float3 cell_size = make_float3(1.0f / dim.x(), 1.0f / dim.y(), 1.0f / dim.z());
float3 point_offset = cell_size * face_overlap_avoidance;
out_vertices.reserve(vertices.size());
for (size_t i = 0; i < vertices.size(); ++i) {
float3 vertex = make_float3(vertices[i].x, vertices[i].y, vertices[i].z);
vertex *= params->cell_size;
vertex += params->start_point;
out_vertices.push_back(vertex);
openvdb::math::Vec3d p = topology_grid->indexToWorld(
openvdb::math::Vec3d(vertices[i].x, vertices[i].y, vertices[i].z));
float3 vertex = make_float3((float)p.x(), (float)p.y(), (float)p.z());
out_vertices.push_back(vertex + point_offset);
}
#else
(void)vertices;
(void)out_vertices;
(void)face_overlap_avoidance;
#endif
}
void VolumeMeshBuilder::convert_quads_to_tris(const vector<QuadData> &quads,
@@ -359,57 +388,128 @@ void VolumeMeshBuilder::convert_quads_to_tris(const vector<QuadData> &quads,
}
}
/* ************************************************************************** */
bool VolumeMeshBuilder::empty_grid() const
{
#ifdef WITH_OPENVDB
return !topology_grid || topology_grid->tree().leafCount() == 0;
#else
return true;
#endif
}
struct VoxelAttributeGrid {
float *data;
int channels;
};
#ifdef WITH_OPENVDB
template<typename GridType>
static openvdb::GridBase::ConstPtr openvdb_grid_from_device_texture(device_texture *image_memory,
float volume_clipping,
Transform transform_3d)
{
using ValueType = typename GridType::ValueType;
openvdb::CoordBBox dense_bbox(0,
0,
0,
image_memory->data_width - 1,
image_memory->data_height - 1,
image_memory->data_depth - 1);
openvdb::tools::Dense<ValueType, openvdb::tools::MemoryLayout::LayoutXYZ> dense(
dense_bbox, static_cast<ValueType *>(image_memory->host_pointer));
typename GridType::Ptr sparse = GridType::create(ValueType(0.0f));
openvdb::tools::copyFromDense(dense, *sparse, ValueType(volume_clipping));
/* copyFromDense will remove any leaf node that contains constant data and replace it with a
* tile, however, we need to preserve the leaves in order to generate the mesh, so revoxelize the
* leaves that were pruned. This should not affect areas that were skipped due to the
* volume_clipping parameter. */
sparse->tree().voxelizeActiveTiles();
/* Compute index to world matrix. */
float3 voxel_size = make_float3(1.0f / image_memory->data_width,
1.0f / image_memory->data_height,
1.0f / image_memory->data_depth);
transform_3d = transform_inverse(transform_3d);
openvdb::Mat4R index_to_world_mat((double)(voxel_size.x * transform_3d[0][0]),
0.0,
0.0,
0.0,
0.0,
(double)(voxel_size.y * transform_3d[1][1]),
0.0,
0.0,
0.0,
0.0,
(double)(voxel_size.z * transform_3d[2][2]),
0.0,
(double)transform_3d[0][3],
(double)transform_3d[1][3],
(double)transform_3d[2][3],
1.0);
openvdb::math::Transform::Ptr index_to_world_tfm =
openvdb::math::Transform::createLinearTransform(index_to_world_mat);
sparse->setTransform(index_to_world_tfm);
return sparse;
}
#endif
/* ************************************************************************** */
void GeometryManager::create_volume_mesh(Mesh *mesh, Progress &progress)
{
string msg = string_printf("Computing Volume Mesh %s", mesh->name.c_str());
progress.set_status("Updating Mesh", msg);
vector<VoxelAttributeGrid> voxel_grids;
/* Compute volume parameters. */
VolumeParams volume_params;
volume_params.resolution = make_int3(0, 0, 0);
Transform transform = transform_identity();
VolumeMeshBuilder builder;
#ifdef WITH_OPENVDB
foreach (Attribute &attr, mesh->attributes.attributes) {
if (attr.element != ATTR_ELEMENT_VOXEL) {
continue;
}
bool do_clipping = false;
ImageHandle &handle = attr.data_voxel();
device_texture *image_memory = handle.image_memory();
int3 resolution = make_int3(
image_memory->data_width, image_memory->data_height, image_memory->data_depth);
if (volume_params.resolution == make_int3(0, 0, 0)) {
volume_params.resolution = resolution;
}
else if (volume_params.resolution != resolution) {
/* TODO: support this as it's common for OpenVDB. */
VLOG(1) << "Can't create accurate volume mesh, all voxel grid resolutions must be equal\n";
continue;
/* Try building from OpenVDB grid directly. */
VDBImageLoader *vdb_loader = handle.vdb_loader();
openvdb::GridBase::ConstPtr grid;
if (vdb_loader) {
grid = vdb_loader->get_grid();
/* If building from an OpenVDB grid, we need to manually clip the values. */
do_clipping = true;
}
VoxelAttributeGrid voxel_grid;
voxel_grid.data = static_cast<float *>(image_memory->host_pointer);
voxel_grid.channels = image_memory->data_elements;
voxel_grids.push_back(voxel_grid);
/* Else fall back to creating an OpenVDB grid from the dense volume data. */
if (!grid) {
device_texture *image_memory = handle.image_memory();
/* TODO: support multiple transforms. */
if (image_memory->info.use_transform_3d) {
transform = image_memory->info.transform_3d;
if (image_memory->data_elements == 1) {
grid = openvdb_grid_from_device_texture<openvdb::FloatGrid>(
image_memory, mesh->volume_clipping, handle.metadata().transform_3d);
}
else if (image_memory->data_elements == 3) {
grid = openvdb_grid_from_device_texture<openvdb::Vec3fGrid>(
image_memory, mesh->volume_clipping, handle.metadata().transform_3d);
}
else if (image_memory->data_elements == 4) {
grid = openvdb_grid_from_device_texture<openvdb::Vec4fGrid>(
image_memory, mesh->volume_clipping, handle.metadata().transform_3d);
}
}
if (grid) {
builder.add_grid(grid, do_clipping, mesh->volume_clipping);
}
}
#endif
if (voxel_grids.empty()) {
if (builder.empty_grid()) {
return;
}
@@ -438,60 +538,24 @@ void GeometryManager::create_volume_mesh(Mesh *mesh, Progress &progress)
return;
}
/* Compute start point and cell size from transform. */
const int3 resolution = volume_params.resolution;
float3 start_point = make_float3(0.0f, 0.0f, 0.0f);
float3 cell_size = make_float3(1.0f / resolution.x, 1.0f / resolution.y, 1.0f / resolution.z);
/* TODO: support arbitrary transforms, not just scale + translate. */
const Transform itfm = transform_inverse(transform);
start_point = transform_point(&itfm, start_point);
cell_size = transform_direction(&itfm, cell_size);
builder.add_padding(pad_size);
/* Slightly offset vertex coordinates to avoid overlapping faces with other
* volumes or meshes. The proper solution would be to improve intersection in
* the kernel to support robust handling of multiple overlapping faces or use
* an all-hit intersection similar to shadows. */
const float3 face_overlap_avoidance = cell_size * 0.1f *
hash_uint_to_float(hash_string(mesh->name.c_str()));
volume_params.start_point = start_point + face_overlap_avoidance;
volume_params.cell_size = cell_size;
volume_params.pad_size = pad_size;
/* Build bounding mesh around non-empty volume cells. */
VolumeMeshBuilder builder(&volume_params);
const float clipping = mesh->volume_clipping;
for (int z = 0; z < resolution.z; ++z) {
for (int y = 0; y < resolution.y; ++y) {
for (int x = 0; x < resolution.x; ++x) {
int64_t voxel_index = compute_voxel_index(resolution, x, y, z);
for (size_t i = 0; i < voxel_grids.size(); ++i) {
const VoxelAttributeGrid &voxel_grid = voxel_grids[i];
const int channels = voxel_grid.channels;
for (int c = 0; c < channels; c++) {
if (voxel_grid.data[voxel_index * channels + c] >= clipping) {
builder.add_node_with_padding(x, y, z);
break;
}
}
}
}
}
}
const float face_overlap_avoidance = 0.1f * hash_uint_to_float(hash_string(mesh->name.c_str()));
/* Create mesh. */
vector<float3> vertices;
vector<int> indices;
vector<float3> face_normals;
builder.create_mesh(vertices, indices, face_normals);
builder.create_mesh(vertices, indices, face_normals, face_overlap_avoidance);
mesh->clear(true);
mesh->reserve_mesh(vertices.size(), indices.size() / 3);
mesh->used_shaders.push_back(volume_shader);
mesh->need_update_rebuild = true;
for (size_t i = 0; i < vertices.size(); ++i) {
mesh->add_vertex(vertices[i]);
@@ -514,10 +578,6 @@ void GeometryManager::create_volume_mesh(Mesh *mesh, Progress &progress)
indices.size() * sizeof(int)) /
(1024.0 * 1024.0)
<< "Mb.";
VLOG(1) << "Memory usage volume grid: "
<< (resolution.x * resolution.y * resolution.z * sizeof(float)) / (1024.0 * 1024.0)
<< "Mb.";
}
CCL_NAMESPACE_END

View File

@@ -776,7 +776,7 @@ static void sky_texture_precompute_nishita(SunSky *sunsky,
sunsky->nishita_data[5] = pixel_top[2];
sunsky->nishita_data[6] = sun_elevation;
sunsky->nishita_data[7] = sun_rotation;
sunsky->nishita_data[8] = sun_disc ? sun_size : 0.0f;
sunsky->nishita_data[8] = sun_disc ? sun_size : -1.0f;
sunsky->nishita_data[9] = sun_intensity;
}
@@ -834,7 +834,7 @@ void SkyTextureNode::compile(SVMCompiler &compiler)
sky_texture_precompute_nishita(&sunsky,
sun_disc,
sun_size,
get_sun_size(),
sun_intensity,
sun_elevation,
sun_rotation,
@@ -930,7 +930,7 @@ void SkyTextureNode::compile(OSLCompiler &compiler)
sky_texture_precompute_nishita(&sunsky,
sun_disc,
sun_size,
get_sun_size(),
sun_intensity,
sun_elevation,
sun_rotation,

View File

@@ -179,6 +179,12 @@ class SkyTextureNode : public TextureNode {
float ozone_density;
float3 vector;
ImageHandle handle;
float get_sun_size()
{
/* Clamping for numerical precision. */
return fmaxf(sun_size, 0.0005f);
}
};
class OutputNode : public ShaderNode {

View File

@@ -79,7 +79,15 @@ DeviceScene::DeviceScene(Device *device)
shaders(device, "__shaders", MEM_GLOBAL),
lookup_table(device, "__lookup_table", MEM_GLOBAL),
sample_pattern_lut(device, "__sample_pattern_lut", MEM_GLOBAL),
ies_lights(device, "__ies", MEM_GLOBAL)
ies_lights(device, "__ies", MEM_GLOBAL),
light_tree_nodes(device, "__light_tree_nodes", MEM_GLOBAL),
light_distribution_to_node(device, "__light_distribution_to_node", MEM_GLOBAL),
lamp_to_distribution(device, "__lamp_to_distribution", MEM_GLOBAL),
triangle_to_distribution(device, "__triangle_to_distribution", MEM_GLOBAL),
light_group_sample_cdf(device, "__light_group_sample_cdf", MEM_GLOBAL),
light_group_sample_prob(device, "__light_group_sample_prob", MEM_GLOBAL),
leaf_to_first_emitter(device, "__leaf_to_first_emitter", MEM_GLOBAL),
light_tree_leaf_emitters(device, "__light_tree_leaf_emitters", MEM_GLOBAL)
{
memset((void *)&data, 0, sizeof(data));
}

View File

@@ -108,6 +108,14 @@ class DeviceScene {
device_vector<KernelLight> lights;
device_vector<float2> light_background_marginal_cdf;
device_vector<float2> light_background_conditional_cdf;
device_vector<float4> light_tree_nodes;
device_vector<uint> light_distribution_to_node;
device_vector<uint> lamp_to_distribution;
device_vector<uint> triangle_to_distribution;
device_vector<float> light_group_sample_cdf;
device_vector<float> light_group_sample_prob;
device_vector<int> leaf_to_first_emitter;
device_vector<float4> light_tree_leaf_emitters;
/* particles */
device_vector<KernelParticle> particles;

View File

@@ -945,8 +945,14 @@ void Session::set_pause(bool pause_)
}
}
if (notify)
pause_cond.notify_all();
if (session_thread) {
if (notify) {
pause_cond.notify_all();
}
}
else if (pause_) {
update_status_time(pause_);
}
}
void Session::set_denoising(const DenoiseParams &denoising)
@@ -1150,8 +1156,15 @@ bool Session::render_need_denoise(bool &delayed)
return false;
}
/* Immediately denoise when we reach the start sample or last sample. */
const int num_samples_finished = tile_manager.state.sample + 1;
if (num_samples_finished == params.denoising.start_sample ||
num_samples_finished == params.samples) {
return true;
}
/* Do not denoise until the sample at which denoising should start is reached. */
if (tile_manager.state.sample < min(params.denoising.start_sample, params.samples - 1)) {
if (num_samples_finished < params.denoising.start_sample) {
return false;
}

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