Animation: Add in Parent space alignment option to the Transform Orientation gizmo #104724
|
@ -10,7 +10,7 @@ ExternalProject_Add(external_epoxy
|
|||
URL_HASH ${EPOXY_HASH_TYPE}=${EPOXY_HASH}
|
||||
PREFIX ${BUILD_DIR}/epoxy
|
||||
PATCH_COMMAND ${PATCH_CMD} -p 1 -N -d ${BUILD_DIR}/epoxy/src/external_epoxy/ < ${PATCH_DIR}/epoxy.diff
|
||||
CONFIGURE_COMMAND ${CONFIGURE_ENV} && ${MESON} setup --prefix ${LIBDIR}/epoxy --default-library ${EPOXY_LIB_TYPE} --libdir lib ${BUILD_DIR}/epoxy/src/external_epoxy-build ${BUILD_DIR}/epoxy/src/external_epoxy -Dtests=false
|
||||
CONFIGURE_COMMAND ${CONFIGURE_ENV} && ${MESON} setup --prefix ${LIBDIR}/epoxy --default-library ${EPOXY_LIB_TYPE} --libdir lib ${BUILD_DIR}/epoxy/src/external_epoxy-build ${BUILD_DIR}/epoxy/src/external_epoxy -Dtests=false ${MESON_BUILD_TYPE}
|
||||
BUILD_COMMAND ninja
|
||||
INSTALL_COMMAND ninja install
|
||||
)
|
||||
|
|
|
@ -9,7 +9,7 @@ ExternalProject_Add(external_fribidi
|
|||
URL_HASH ${FRIBIDI_HASH_TYPE}=${FRIBIDI_HASH}
|
||||
DOWNLOAD_DIR ${DOWNLOAD_DIR}
|
||||
PREFIX ${BUILD_DIR}/fribidi
|
||||
CONFIGURE_COMMAND ${MESON} setup --prefix ${LIBDIR}/fribidi -Ddocs=false --default-library static --libdir lib ${BUILD_DIR}/fribidi/src/external_fribidi-build ${BUILD_DIR}/fribidi/src/external_fribidi
|
||||
CONFIGURE_COMMAND ${MESON} setup --prefix ${LIBDIR}/fribidi ${MESON_BUILD_TYPE} -Ddocs=false --default-library static --libdir lib ${BUILD_DIR}/fribidi/src/external_fribidi-build ${BUILD_DIR}/fribidi/src/external_fribidi
|
||||
BUILD_COMMAND ninja
|
||||
INSTALL_COMMAND ninja install
|
||||
INSTALL_DIR ${LIBDIR}/fribidi
|
||||
|
|
|
@ -21,6 +21,7 @@ set(HARFBUZZ_EXTRA_OPTIONS
|
|||
# Only used for command line utilities,
|
||||
# disable as this would add an addition & unnecessary build-dependency.
|
||||
-Dcairo=disabled
|
||||
${MESON_BUILD_TYPE}
|
||||
)
|
||||
|
||||
ExternalProject_Add(external_harfbuzz
|
||||
|
@ -59,3 +60,10 @@ if(BUILD_MODE STREQUAL Release AND WIN32)
|
|||
DEPENDEES install
|
||||
)
|
||||
endif()
|
||||
|
||||
if(BUILD_MODE STREQUAL Debug AND WIN32)
|
||||
ExternalProject_Add_Step(external_harfbuzz after_install
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/harfbuzz/lib/libharfbuzz.a ${HARVEST_TARGET}/harfbuzz/lib/libharfbuzz_d.lib
|
||||
DEPENDEES install
|
||||
)
|
||||
endif()
|
||||
|
|
|
@ -15,7 +15,7 @@ llvm-config = '${LIBDIR}/llvm/bin/llvm-config'"
|
|||
)
|
||||
|
||||
set(MESA_EXTRA_FLAGS
|
||||
-Dbuildtype=release
|
||||
${MESON_BUILD_TYPE}
|
||||
-Dc_args=${MESA_CFLAGS}
|
||||
-Dcpp_args=${MESA_CXXFLAGS}
|
||||
-Dc_link_args=${MESA_LDFLAGS}
|
||||
|
|
|
@ -16,8 +16,10 @@ message("BuildMode = ${BUILD_MODE}")
|
|||
|
||||
if(BUILD_MODE STREQUAL "Debug")
|
||||
set(LIBDIR ${CMAKE_CURRENT_BINARY_DIR}/Debug)
|
||||
set(MESON_BUILD_TYPE -Dbuildtype=debug)
|
||||
else()
|
||||
set(LIBDIR ${CMAKE_CURRENT_BINARY_DIR}/Release)
|
||||
set(MESON_BUILD_TYPE -Dbuildtype=release)
|
||||
endif()
|
||||
|
||||
set(DOWNLOAD_DIR "${CMAKE_CURRENT_BINARY_DIR}/downloads" CACHE STRING "Path for downloaded files")
|
||||
|
|
|
@ -13,7 +13,7 @@ ExternalProject_Add(external_wayland
|
|||
# NOTE: `-lm` is needed for `libxml2` which is a static library that uses `libm.so`,
|
||||
# without this, math symbols such as `floor` aren't found.
|
||||
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env PKG_CONFIG_PATH=${LIBDIR}/expat/lib/pkgconfig:${LIBDIR}/xml2/lib/pkgconfig:${LIBDIR}/ffi/lib/pkgconfig:$PKG_CONFIG_PATH
|
||||
${MESON} --prefix ${LIBDIR}/wayland -Ddocumentation=false -Dtests=false -D "c_link_args=-L${LIBDIR}/ffi/lib -lm" . ../external_wayland
|
||||
${MESON} --prefix ${LIBDIR}/wayland ${MESON_BUILD_TYPE} -Ddocumentation=false -Dtests=false -D "c_link_args=-L${LIBDIR}/ffi/lib -lm" . ../external_wayland
|
||||
BUILD_COMMAND ninja
|
||||
INSTALL_COMMAND ninja install
|
||||
)
|
||||
|
|
|
@ -7,7 +7,7 @@ ExternalProject_Add(external_wayland_protocols
|
|||
PREFIX ${BUILD_DIR}/wayland-protocols
|
||||
# Use `-E` so the `PKG_CONFIG_PATH` can be defined to link against our own WAYLAND.
|
||||
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env PKG_CONFIG_PATH=${LIBDIR}/wayland/lib64/pkgconfig:$PKG_CONFIG_PATH
|
||||
${MESON} --prefix ${LIBDIR}/wayland-protocols . ../external_wayland_protocols -Dtests=false
|
||||
${MESON} --prefix ${LIBDIR}/wayland-protocols ${MESON_BUILD_TYPE} . ../external_wayland_protocols -Dtests=false
|
||||
BUILD_COMMAND ninja
|
||||
INSTALL_COMMAND ninja install
|
||||
)
|
||||
|
|
|
@ -17,11 +17,13 @@ ExternalProject_Add(external_xvidcore
|
|||
INSTALL_DIR ${LIBDIR}/xvidcore
|
||||
)
|
||||
|
||||
ExternalProject_Add_Step(external_xvidcore after_install
|
||||
COMMAND ${CMAKE_COMMAND} -E rename ${LIBDIR}/xvidcore/lib/xvidcore.a ${LIBDIR}/xvidcore/lib/libxvidcore.a || true
|
||||
COMMAND ${CMAKE_COMMAND} -E remove ${LIBDIR}/xvidcore/lib/xvidcore.dll.a
|
||||
DEPENDEES install
|
||||
)
|
||||
if(WIN32)
|
||||
ExternalProject_Add_Step(external_xvidcore after_install
|
||||
COMMAND ${CMAKE_COMMAND} -E rename ${LIBDIR}/xvidcore/lib/xvidcore.a ${LIBDIR}/xvidcore/lib/libxvidcore.a || true
|
||||
COMMAND ${CMAKE_COMMAND} -E remove ${LIBDIR}/xvidcore/lib/xvidcore.dll.a
|
||||
DEPENDEES install
|
||||
)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
set_target_properties(external_xvidcore PROPERTIES FOLDER Mingw)
|
||||
|
|
|
@ -142,7 +142,7 @@ def cmake_advanced_info() -> Union[Tuple[List[str], List[Tuple[str, str]]], Tupl
|
|||
|
||||
make_exe = cmake_cache_var("CMAKE_MAKE_PROGRAM")
|
||||
if make_exe is None:
|
||||
print("Make command not found in: %r not found" % project_path)
|
||||
print("Make command not found: CMAKE_MAKE_PROGRAM")
|
||||
return None, None
|
||||
|
||||
make_exe_basename = os.path.basename(make_exe)
|
||||
|
|
|
@ -42,6 +42,7 @@ def parse_arguments() -> argparse.Namespace:
|
|||
parser.add_argument("--svn-branch", default=None)
|
||||
parser.add_argument("--git-command", default="git")
|
||||
parser.add_argument("--use-linux-libraries", action="store_true")
|
||||
parser.add_argument("--architecture", type=str, choices=("x86_64", "amd64", "arm64",))
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
|
@ -51,6 +52,19 @@ def get_blender_git_root() -> str:
|
|||
# Setup for precompiled libraries and tests from svn.
|
||||
|
||||
|
||||
def get_effective_architecture(args: argparse.Namespace) -> str:
|
||||
architecture = args.architecture
|
||||
if architecture:
|
||||
assert isinstance(architecture, str)
|
||||
return architecture
|
||||
|
||||
# Check platform.version to detect arm64 with x86_64 python binary.
|
||||
if "ARM64" in platform.version():
|
||||
return "arm64"
|
||||
|
||||
return platform.machine().lower()
|
||||
|
||||
|
||||
def svn_update(args: argparse.Namespace, release_version: Optional[str]) -> None:
|
||||
svn_non_interactive = [args.svn_command, '--non-interactive']
|
||||
|
||||
|
@ -58,11 +72,11 @@ def svn_update(args: argparse.Namespace, release_version: Optional[str]) -> None
|
|||
svn_url = make_utils.svn_libraries_base_url(release_version, args.svn_branch)
|
||||
|
||||
# Checkout precompiled libraries
|
||||
architecture = get_effective_architecture(args)
|
||||
if sys.platform == 'darwin':
|
||||
# Check platform.version to detect arm64 with x86_64 python binary.
|
||||
if platform.machine() == 'arm64' or ('ARM64' in platform.version()):
|
||||
if architecture == 'arm64':
|
||||
lib_platform = "darwin_arm64"
|
||||
elif platform.machine() == 'x86_64':
|
||||
elif architecture == 'x86_64':
|
||||
lib_platform = "darwin"
|
||||
else:
|
||||
lib_platform = None
|
||||
|
@ -256,14 +270,15 @@ if __name__ == "__main__":
|
|||
blender_skip_msg = ""
|
||||
submodules_skip_msg = ""
|
||||
|
||||
# Test if we are building a specific release version.
|
||||
branch = make_utils.git_branch(args.git_command)
|
||||
if branch == 'HEAD':
|
||||
sys.stderr.write('Blender git repository is in detached HEAD state, must be in a branch\n')
|
||||
sys.exit(1)
|
||||
|
||||
tag = make_utils.git_tag(args.git_command)
|
||||
release_version = make_utils.git_branch_release_version(branch, tag)
|
||||
blender_version = make_utils. parse_blender_version()
|
||||
if blender_version.cycle != 'alpha':
|
||||
major = blender_version.version // 100
|
||||
minor = blender_version.version % 100
|
||||
branch = f"blender-v{major}.{minor}-release"
|
||||
release_version: Optional[str] = f"{major}.{minor}"
|
||||
else:
|
||||
branch = 'main'
|
||||
release_version = None
|
||||
|
||||
if not args.no_libraries:
|
||||
svn_update(args, release_version)
|
||||
|
|
|
@ -122,7 +122,7 @@ KERNEL_STRUCT_MEMBER(guiding, bool, use_surface_guiding, KERNEL_FEATURE_PATH_GUI
|
|||
KERNEL_STRUCT_MEMBER(guiding, float, sample_surface_guiding_rand, KERNEL_FEATURE_PATH_GUIDING)
|
||||
/* The probability to use surface guiding (i.e., diffuse sampling prob * guiding prob)*/
|
||||
KERNEL_STRUCT_MEMBER(guiding, float, surface_guiding_sampling_prob, KERNEL_FEATURE_PATH_GUIDING)
|
||||
/* Probability of sampling a BSSRDF closure instead of a BSDF closure*/
|
||||
/* Probability of sampling a BSSRDF closure instead of a BSDF closure. */
|
||||
KERNEL_STRUCT_MEMBER(guiding, float, bssrdf_sampling_prob, KERNEL_FEATURE_PATH_GUIDING)
|
||||
/* If volume guiding is enabled */
|
||||
KERNEL_STRUCT_MEMBER(guiding, bool, use_volume_guiding, KERNEL_FEATURE_PATH_GUIDING)
|
||||
|
|
|
@ -1177,7 +1177,7 @@ void LightManager::device_update(Device *device,
|
|||
|
||||
void LightManager::device_free(Device *, DeviceScene *dscene, const bool free_background)
|
||||
{
|
||||
/* to-do: check if the light tree member variables need to be wrapped in a conditional too*/
|
||||
/* TODO: check if the light tree member variables need to be wrapped in a conditional too. */
|
||||
dscene->light_tree_nodes.free();
|
||||
dscene->light_tree_emitters.free();
|
||||
dscene->light_to_tree.free();
|
||||
|
|
|
@ -310,6 +310,9 @@ void GHOST_WindowWin32::adjustWindowRectForDesktop(LPRECT win_rect, DWORD dwStyl
|
|||
monitor.cbSize = sizeof(MONITORINFOEX);
|
||||
monitor.dwFlags = 0;
|
||||
|
||||
/* We'll need this value before it is altered for checking later. */
|
||||
LONG requested_top = win_rect->top;
|
||||
|
||||
/* Note that with MonitorFromPoint using MONITOR_DEFAULTTONEAREST, it will return
|
||||
* the exact monitor if there is one at the location or the nearest monitor if not. */
|
||||
|
||||
|
@ -355,6 +358,9 @@ void GHOST_WindowWin32::adjustWindowRectForDesktop(LPRECT win_rect, DWORD dwStyl
|
|||
/* Adjust to allow for caption, borders, shadows, scaling, etc. Resulting values can be
|
||||
* correctly outside of monitor bounds. NOTE: You cannot specify #WS_OVERLAPPED when calling. */
|
||||
if (fpAdjustWindowRectExForDpi) {
|
||||
/* Use the DPI of the monitor that is at the middle of the rect. */
|
||||
hmonitor = MonitorFromRect(win_rect, MONITOR_DEFAULTTONEAREST);
|
||||
GetMonitorInfo(hmonitor, &monitor);
|
||||
UINT dpiX, dpiY;
|
||||
GetDpiForMonitor(hmonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
|
||||
fpAdjustWindowRectExForDpi(win_rect, dwStyle & ~WS_OVERLAPPED, FALSE, dwExStyle, dpiX);
|
||||
|
@ -362,6 +368,14 @@ void GHOST_WindowWin32::adjustWindowRectForDesktop(LPRECT win_rect, DWORD dwStyl
|
|||
else {
|
||||
AdjustWindowRectEx(win_rect, dwStyle & ~WS_OVERLAPPED, FALSE, dwExStyle);
|
||||
}
|
||||
|
||||
/* Don't hide the title bar. Check the working area of the monitor at the top-left corner, using
|
||||
* the original top since the justWindowRects might have altered it to different monitor. */
|
||||
pt.x = win_rect->left;
|
||||
pt.y = requested_top;
|
||||
hmonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
|
||||
GetMonitorInfo(hmonitor, &monitor);
|
||||
win_rect->top = max(monitor.rcWork.top, win_rect->top);
|
||||
}
|
||||
|
||||
bool GHOST_WindowWin32::getValid() const
|
||||
|
|
|
@ -7726,22 +7726,6 @@
|
|||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccccccccc" />
|
||||
</g>
|
||||
<g
|
||||
id="g3791"
|
||||
inkscape:label="CA-24">
|
||||
<path
|
||||
sodipodi:nodetypes="csssscccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path28911-6"
|
||||
d="m 489,604 v 8 c 0,0.54532 0.45468,1 1,1 h 12 c 0.54532,0 1,-0.45468 1,-1 v -8 h -1 v 8 h -12 v -8 z"
|
||||
style="opacity:0.6;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke" />
|
||||
<path
|
||||
sodipodi:nodetypes="ssccccsssccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
style="vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke"
|
||||
d="m 490,599 c -0.54532,0 -1,0.45468 -1,1 v 3 h 1 12 1 v -3 c 0,-0.54532 -0.45468,-1 -1,-1 z m 0,1 h 2 v 2 h -2 z"
|
||||
id="path28913-4" />
|
||||
</g>
|
||||
<g
|
||||
style="display:inline;enable-background:new"
|
||||
id="g28909-7"
|
||||
|
@ -7762,6 +7746,22 @@
|
|||
transform="matrix(1,0,0,-1,520,1334)"
|
||||
id="path28907-4" />
|
||||
</g>
|
||||
<g
|
||||
id="g3791"
|
||||
inkscape:label="CA-24">
|
||||
<path
|
||||
sodipodi:nodetypes="csssscccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path28911-6"
|
||||
d="m 489,604 v 8 c 0,0.54532 0.45468,1 1,1 h 12 c 0.54532,0 1,-0.45468 1,-1 v -8 h -1 v 8 h -12 v -8 z"
|
||||
style="opacity:0.6;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke" />
|
||||
<path
|
||||
sodipodi:nodetypes="ssccccsssccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
style="vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke"
|
||||
d="m 490,599 c -0.54532,0 -1,0.45468 -1,1 v 3 h 1 12 1 v -3 c 0,-0.54532 -0.45468,-1 -1,-1 z m 0,1 h 2 v 2 h -2 z"
|
||||
id="path28913-4" />
|
||||
</g>
|
||||
<g
|
||||
id="g9239"
|
||||
inkscape:label="CA-23"
|
||||
|
@ -13113,6 +13113,40 @@
|
|||
d="m 221.03217,109.33958 c 0.67621,0.01 0.67621,-1.00961 0,-1 h -2.79493 c 1.0479,-1.11729 1.7641,-1.66802 2.82812,-2.73204 0.62065,-0.56444 -0.28321,-1.46832 -0.84765,-0.84766 -1.06063,1.10128 -1.59202,1.7772 -2.68554,2.87072 v -2.79102 c 0.01,-0.67616 -1.00956,-0.67616 -1,0 v 4 c 3e-5,0.27613 0.22387,0.49998 0.5,0.5 z"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
</g>
|
||||
<g
|
||||
id="g24638"
|
||||
inkscape:label="O-18"
|
||||
style="display:inline;enable-background:new">
|
||||
<path
|
||||
style="color:#000000;opacity:1;fill:none;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;-inkscape-stroke:none"
|
||||
d="m 374.12695,305.31445 c -1.10721,0 -1.93616,0.24199 -2.97851,0.63086 -1.04236,0.38888 -2.18224,0.95849 -3.30078,1.77735 -2.2371,1.63771 -4.46485,4.54533 -4.46485,8.40625 a 3.0581999,3.0581999 0 0 0 3.0586,3.05859 3.0581999,3.0581999 0 0 0 3.05859,-3.05859 c 0,-1.63187 0.77567,-2.603 1.96094,-3.47071 0.59263,-0.43385 1.26841,-0.77311 1.82422,-0.98047 0.5558,-0.20735 1.10962,-0.24804 0.84179,-0.24804 a 3.0581999,3.0581999 0 0 0 3.05664,-3.0586 3.0581999,3.0581999 0 0 0 -3.05664,-3.05664 z"
|
||||
id="path6454-6" />
|
||||
<path
|
||||
style="display:inline;opacity:0.5;fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1;enable-background:new"
|
||||
d="m 366.4419,316.12916 c 0,-5.49278 6.00534,-7.75723 7.68412,-7.75723"
|
||||
id="path15424"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
<g
|
||||
id="g24643"
|
||||
inkscape:label="O-17"
|
||||
style="display:inline;enable-background:new">
|
||||
<path
|
||||
style="opacity:0.5;fill:#ffffff;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 343.45275,317.52104 4.51631,-3.99333"
|
||||
id="path4694"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 342.44508,318.83525 c 14.125,0.14581 -0.42233,-12.9179 13.5429,-12.85486"
|
||||
id="path6454"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="opacity:0.5;fill:#ffffff;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 350.84964,311.18794 4.21454,-3.72502"
|
||||
id="path19640"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
<g
|
||||
style="display:inline;enable-background:new"
|
||||
id="g28812"
|
||||
|
@ -18997,36 +19031,6 @@
|
|||
id="path4817-4"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
id="g24638">
|
||||
<path
|
||||
style="color:#000000;fill:none;stroke-linecap:round;-inkscape-stroke:none;opacity:1;stroke:#ffffff;stroke-opacity:1;stroke-linejoin:round"
|
||||
d="m 374.12695,305.31445 c -1.10721,0 -1.93616,0.24199 -2.97851,0.63086 -1.04236,0.38888 -2.18224,0.95849 -3.30078,1.77735 -2.2371,1.63771 -4.46485,4.54533 -4.46485,8.40625 a 3.0581999,3.0581999 0 0 0 3.0586,3.05859 3.0581999,3.0581999 0 0 0 3.05859,-3.05859 c 0,-1.63187 0.77567,-2.603 1.96094,-3.47071 0.59263,-0.43385 1.26841,-0.77311 1.82422,-0.98047 0.5558,-0.20735 1.10962,-0.24804 0.84179,-0.24804 a 3.0581999,3.0581999 0 0 0 3.05664,-3.0586 3.0581999,3.0581999 0 0 0 -3.05664,-3.05664 z"
|
||||
id="path6454-6" />
|
||||
<path
|
||||
style="display:inline;opacity:0.5;fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1;enable-background:new"
|
||||
d="m 366.4419,316.12916 c 0,-5.49278 6.00534,-7.75723 7.68412,-7.75723"
|
||||
id="path15424"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
<g
|
||||
id="g24643">
|
||||
<path
|
||||
style="opacity:0.5;fill:#ffffff;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 343.45275,317.52104 4.51631,-3.99333"
|
||||
id="path4694"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 342.44508,318.83525 c 14.125,0.14581 -0.42233,-12.9179 13.5429,-12.85486"
|
||||
id="path6454"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="opacity:0.5;fill:#ffffff;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 350.84964,311.18794 4.21454,-3.72502"
|
||||
id="path19640"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
|
|
Before Width: | Height: | Size: 2.5 MiB After Width: | Height: | Size: 2.5 MiB |
|
@ -101,7 +101,11 @@ const UserDef U_default = {
|
|||
.gp_euclideandist = 2,
|
||||
.gp_eraser = 25,
|
||||
.gp_settings = 0,
|
||||
#ifdef __APPLE__
|
||||
.gpu_backend = GPU_BACKEND_METAL,
|
||||
#else
|
||||
.gpu_backend = GPU_BACKEND_OPENGL,
|
||||
#endif
|
||||
|
||||
/** Initialized by: #BKE_studiolight_default. */
|
||||
.light_param = {{0}},
|
||||
|
|
|
@ -3513,6 +3513,9 @@ def km_animation_channels(params):
|
|||
("anim.channels_ungroup", {"type": 'G', "value": 'PRESS', "ctrl": True, "alt": True}, None),
|
||||
# Menus.
|
||||
*_template_items_context_menu("DOPESHEET_MT_channel_context_menu", params.context_menu_event),
|
||||
# View
|
||||
("anim.channel_view_pick", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "alt": True}, None),
|
||||
("anim.channels_view_selected", {"type": 'NUMPAD_PERIOD', "value": 'PRESS'}, None),
|
||||
])
|
||||
|
||||
return keymap
|
||||
|
|
|
@ -25,7 +25,7 @@ def build_default_empty_geometry_node_group(name):
|
|||
|
||||
def geometry_node_group_empty_new():
|
||||
group = build_default_empty_geometry_node_group(data_("Geometry Nodes"))
|
||||
group.links.new(group.nodes["Group Input"].outputs[0], group.nodes["Group Output"].inputs[0])
|
||||
group.links.new(group.nodes[data_("Group Input")].outputs[0], group.nodes[data_("Group Output")].inputs[0])
|
||||
return group
|
||||
|
||||
|
||||
|
@ -121,8 +121,8 @@ class MoveModifierToNodes(Operator):
|
|||
group_node.node_tree = old_group
|
||||
group_node.update()
|
||||
|
||||
group_input_node = group.nodes["Group Input"]
|
||||
group_output_node = group.nodes["Group Output"]
|
||||
group_input_node = group.nodes[data_("Group Input")]
|
||||
group_output_node = group.nodes[data_("Group Output")]
|
||||
|
||||
# Copy default values for inputs and create named attribute input nodes.
|
||||
input_nodes = []
|
||||
|
@ -173,6 +173,7 @@ class MoveModifierToNodes(Operator):
|
|||
first_geometry_output = group_node_output
|
||||
|
||||
# Adjust locations of store named attribute nodes and move group output.
|
||||
# Note that the node group has its sockets names translated, while the built-in nodes don't.
|
||||
if store_nodes:
|
||||
for i, node in enumerate(store_nodes):
|
||||
node.location.x = (i + 1) * 175
|
||||
|
@ -182,9 +183,10 @@ class MoveModifierToNodes(Operator):
|
|||
group.links.new(first_geometry_output, store_nodes[0].inputs["Geometry"])
|
||||
for i in range(len(store_nodes) - 1):
|
||||
group.links.new(store_nodes[i].outputs["Geometry"], store_nodes[i + 1].inputs["Geometry"])
|
||||
group.links.new(store_nodes[-1].outputs["Geometry"], group_output_node.inputs["Geometry"])
|
||||
|
||||
group.links.new(store_nodes[-1].outputs["Geometry"], group_output_node.inputs[data_("Geometry")])
|
||||
else:
|
||||
group.links.new(first_geometry_output, group_output_node.inputs["Geometry"])
|
||||
group.links.new(first_geometry_output, group_output_node.inputs[data_("Geometry")])
|
||||
|
||||
modifier.node_group = group
|
||||
|
||||
|
|
|
@ -556,7 +556,7 @@ class WM_OT_context_toggle_enum(Operator):
|
|||
|
||||
class WM_OT_context_cycle_int(Operator):
|
||||
"""Set a context value (useful for cycling active material, """ \
|
||||
"""vertex keys, groups, etc.)"""
|
||||
"""shape keys, groups, etc.)"""
|
||||
bl_idname = "wm.context_cycle_int"
|
||||
bl_label = "Context Int Cycle"
|
||||
bl_options = {'UNDO', 'INTERNAL'}
|
||||
|
|
|
@ -397,7 +397,7 @@ class ConstraintButtonsPanel:
|
|||
sub.prop(con, "invert_z", text="Z", toggle=True)
|
||||
row.label(icon='BLANK1')
|
||||
|
||||
layout.prop(con, "mix_mode", text="Mix")
|
||||
layout.prop(con, "mix_mode", text="Mix", text_ctxt=i18n_contexts.constraint)
|
||||
|
||||
self.space_template(layout, con)
|
||||
|
||||
|
@ -488,7 +488,7 @@ class ConstraintButtonsPanel:
|
|||
self.target_template(layout, con)
|
||||
|
||||
layout.prop(con, "remove_target_shear")
|
||||
layout.prop(con, "mix_mode", text="Mix")
|
||||
layout.prop(con, "mix_mode", text="Mix", text_ctxt=i18n_contexts.constraint)
|
||||
|
||||
self.space_template(layout, con)
|
||||
|
||||
|
@ -513,7 +513,7 @@ class ConstraintButtonsPanel:
|
|||
subsub.prop(con, "eval_time", text="")
|
||||
row.prop_decorator(con, "eval_time")
|
||||
|
||||
layout.prop(con, "mix_mode", text="Mix")
|
||||
layout.prop(con, "mix_mode", text="Mix", text_ctxt=i18n_contexts.constraint)
|
||||
|
||||
self.draw_influence(layout, con)
|
||||
|
||||
|
@ -1024,7 +1024,7 @@ class ConstraintButtonsSubPanel:
|
|||
col.prop(con, "to_min_z" + ext, text="Min")
|
||||
col.prop(con, "to_max_z" + ext, text="Max")
|
||||
|
||||
layout.prop(con, "mix_mode" + ext, text="Mix")
|
||||
layout.prop(con, "mix_mode" + ext, text="Mix", text_ctxt=i18n_contexts.constraint)
|
||||
|
||||
def draw_armature_bones(self, context):
|
||||
layout = self.layout
|
||||
|
|
|
@ -78,7 +78,7 @@ class PARTICLE_MT_context_menu(Menu):
|
|||
props.use_active = False
|
||||
props.remove_target_particles = True
|
||||
|
||||
if psys.settings.type == 'HAIR':
|
||||
if psys is not None and psys.settings.type == 'HAIR':
|
||||
layout.operator(
|
||||
"curves.convert_from_particle_system",
|
||||
text="Convert to Curves")
|
||||
|
|
|
@ -474,6 +474,9 @@ class DOPESHEET_MT_channel(Menu):
|
|||
layout.separator()
|
||||
layout.operator("anim.channels_fcurves_enable")
|
||||
|
||||
layout.separator()
|
||||
layout.operator("anim.channels_view_selected")
|
||||
|
||||
|
||||
class DOPESHEET_MT_key(Menu):
|
||||
bl_label = "Key"
|
||||
|
@ -601,6 +604,9 @@ class DOPESHEET_MT_gpencil_channel(Menu):
|
|||
layout.separator()
|
||||
layout.operator_menu_enum("anim.channels_move", "direction", text="Move...")
|
||||
|
||||
layout.separator()
|
||||
layout.operator("anim.channels_view_selected")
|
||||
|
||||
|
||||
class DOPESHEET_MT_gpencil_key(Menu):
|
||||
bl_label = "Key"
|
||||
|
@ -689,6 +695,9 @@ class DOPESHEET_MT_channel_context_menu(Menu):
|
|||
# This menu is used from the graph editor too.
|
||||
is_graph_editor = context.area.type == 'GRAPH_EDITOR'
|
||||
|
||||
layout.separator()
|
||||
layout.operator("anim.channels_view_selected")
|
||||
|
||||
layout.operator("anim.channels_setting_enable", text="Mute Channels").type = 'MUTE'
|
||||
layout.operator("anim.channels_setting_disable", text="Unmute Channels").type = 'MUTE'
|
||||
layout.separator()
|
||||
|
|
|
@ -239,6 +239,9 @@ class GRAPH_MT_channel(Menu):
|
|||
layout.separator()
|
||||
layout.operator("anim.channels_fcurves_enable")
|
||||
|
||||
layout.separator()
|
||||
layout.operator("anim.channels_view_selected")
|
||||
|
||||
|
||||
class GRAPH_MT_key(Menu):
|
||||
bl_label = "Key"
|
||||
|
|
|
@ -574,6 +574,11 @@ class NODE_MT_context_menu(Menu):
|
|||
layout.menu("NODE_MT_context_menu_select_menu")
|
||||
layout.menu("NODE_MT_context_menu_show_hide_menu")
|
||||
|
||||
if active_node:
|
||||
layout.separator()
|
||||
props = layout.operator("wm.doc_view_manual", text="Online Manual", icon='URL')
|
||||
props.doc_id = active_node.bl_idname
|
||||
|
||||
|
||||
class NODE_PT_active_node_generic(Panel):
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
|
|
|
@ -23,8 +23,6 @@ extern "C" {
|
|||
/** Blender release cycle stage: alpha/beta/rc/release. */
|
||||
#define BLENDER_VERSION_CYCLE alpha
|
||||
|
||||
/* TODO proper version bump. */
|
||||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 0
|
||||
|
|
|
@ -108,7 +108,7 @@ BVHTree *bvhtree_from_editmesh_verts(
|
|||
*/
|
||||
BVHTree *bvhtree_from_editmesh_verts_ex(BVHTreeFromEditMesh *data,
|
||||
struct BMEditMesh *em,
|
||||
const blender::BitVector<> &mask,
|
||||
blender::BitSpan mask,
|
||||
int verts_num_active,
|
||||
float epsilon,
|
||||
int tree_type,
|
||||
|
@ -124,7 +124,7 @@ BVHTree *bvhtree_from_editmesh_verts_ex(BVHTreeFromEditMesh *data,
|
|||
BVHTree *bvhtree_from_mesh_verts_ex(struct BVHTreeFromMesh *data,
|
||||
const float (*vert_positions)[3],
|
||||
int verts_num,
|
||||
const blender::BitVector<> &verts_mask,
|
||||
blender::BitSpan verts_mask,
|
||||
int verts_num_active,
|
||||
float epsilon,
|
||||
int tree_type,
|
||||
|
@ -138,7 +138,7 @@ BVHTree *bvhtree_from_editmesh_edges(
|
|||
*/
|
||||
BVHTree *bvhtree_from_editmesh_edges_ex(BVHTreeFromEditMesh *data,
|
||||
struct BMEditMesh *em,
|
||||
const blender::BitVector<> &edges_mask,
|
||||
blender::BitSpan edges_mask,
|
||||
int edges_num_active,
|
||||
float epsilon,
|
||||
int tree_type,
|
||||
|
@ -156,7 +156,7 @@ BVHTree *bvhtree_from_mesh_edges_ex(struct BVHTreeFromMesh *data,
|
|||
const float (*vert_positions)[3],
|
||||
const struct MEdge *edge,
|
||||
int edges_num,
|
||||
const blender::BitVector<> &edges_mask,
|
||||
blender::BitSpan edges_mask,
|
||||
int edges_num_active,
|
||||
float epsilon,
|
||||
int tree_type,
|
||||
|
@ -170,7 +170,7 @@ BVHTree *bvhtree_from_editmesh_looptri(
|
|||
*/
|
||||
BVHTree *bvhtree_from_editmesh_looptri_ex(BVHTreeFromEditMesh *data,
|
||||
struct BMEditMesh *em,
|
||||
const blender::BitVector<> &mask,
|
||||
blender::BitSpan mask,
|
||||
int looptri_num_active,
|
||||
float epsilon,
|
||||
int tree_type,
|
||||
|
@ -184,7 +184,7 @@ BVHTree *bvhtree_from_mesh_looptri_ex(struct BVHTreeFromMesh *data,
|
|||
const struct MLoop *mloop,
|
||||
const struct MLoopTri *looptri,
|
||||
int looptri_num,
|
||||
const blender::BitVector<> &mask,
|
||||
blender::BitSpan mask,
|
||||
int looptri_num_active,
|
||||
float epsilon,
|
||||
int tree_type,
|
||||
|
|
|
@ -373,6 +373,8 @@ bool BKE_fcurve_calc_range(
|
|||
|
||||
/**
|
||||
* Calculate the extents of F-Curve's data.
|
||||
* \param range Only calculate the bounds of the FCurve in the given range.
|
||||
* Does the full range if NULL.
|
||||
*/
|
||||
bool BKE_fcurve_calc_bounds(const struct FCurve *fcu,
|
||||
float *xmin,
|
||||
|
@ -380,7 +382,8 @@ bool BKE_fcurve_calc_bounds(const struct FCurve *fcu,
|
|||
float *ymin,
|
||||
float *ymax,
|
||||
bool do_sel_only,
|
||||
bool include_handles);
|
||||
bool include_handles,
|
||||
const float range[2]);
|
||||
|
||||
/**
|
||||
* Return an array of keyed frames, rounded to `interval`.
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#include "BKE_particle.h"
|
||||
#include "BLI_kdopbvh.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "BKE_modifier.h"
|
||||
|
||||
#include "RNA_enum_types.h"
|
||||
|
@ -1607,7 +1609,7 @@ BoidRule *boid_new_rule(int type)
|
|||
|
||||
rule->type = type;
|
||||
rule->flag |= BOIDRULE_IN_AIR | BOIDRULE_ON_LAND;
|
||||
BLI_strncpy(rule->name, rna_enum_boidrule_type_items[type - 1].name, sizeof(rule->name));
|
||||
BLI_strncpy(rule->name, DATA_(rna_enum_boidrule_type_items[type - 1].name), sizeof(rule->name));
|
||||
|
||||
return rule;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
using blender::BitSpan;
|
||||
using blender::BitVector;
|
||||
using blender::float3;
|
||||
using blender::IndexRange;
|
||||
|
@ -672,7 +673,7 @@ static BVHTree *bvhtree_from_editmesh_verts_create_tree(float epsilon,
|
|||
int tree_type,
|
||||
int axis,
|
||||
BMEditMesh *em,
|
||||
const BitVector<> &verts_mask,
|
||||
const BitSpan verts_mask,
|
||||
int verts_num_active)
|
||||
{
|
||||
BM_mesh_elem_table_ensure(em->bm, BM_VERT);
|
||||
|
@ -706,7 +707,7 @@ static BVHTree *bvhtree_from_mesh_verts_create_tree(float epsilon,
|
|||
int axis,
|
||||
const float (*positions)[3],
|
||||
const int verts_num,
|
||||
const BitVector<> &verts_mask,
|
||||
const BitSpan verts_mask,
|
||||
int verts_num_active)
|
||||
{
|
||||
if (!verts_mask.is_empty()) {
|
||||
|
@ -737,7 +738,7 @@ static BVHTree *bvhtree_from_mesh_verts_create_tree(float epsilon,
|
|||
|
||||
BVHTree *bvhtree_from_editmesh_verts_ex(BVHTreeFromEditMesh *data,
|
||||
BMEditMesh *em,
|
||||
const BitVector<> &verts_mask,
|
||||
const BitSpan verts_mask,
|
||||
int verts_num_active,
|
||||
float epsilon,
|
||||
int tree_type,
|
||||
|
@ -764,7 +765,7 @@ BVHTree *bvhtree_from_editmesh_verts(
|
|||
BVHTree *bvhtree_from_mesh_verts_ex(BVHTreeFromMesh *data,
|
||||
const float (*vert_positions)[3],
|
||||
const int verts_num,
|
||||
const BitVector<> &verts_mask,
|
||||
const BitSpan verts_mask,
|
||||
int verts_num_active,
|
||||
float epsilon,
|
||||
int tree_type,
|
||||
|
@ -794,7 +795,7 @@ static BVHTree *bvhtree_from_editmesh_edges_create_tree(float epsilon,
|
|||
int tree_type,
|
||||
int axis,
|
||||
BMEditMesh *em,
|
||||
const BitVector<> &edges_mask,
|
||||
const BitSpan edges_mask,
|
||||
int edges_num_active)
|
||||
{
|
||||
BM_mesh_elem_table_ensure(em->bm, BM_EDGE);
|
||||
|
@ -833,7 +834,7 @@ static BVHTree *bvhtree_from_editmesh_edges_create_tree(float epsilon,
|
|||
static BVHTree *bvhtree_from_mesh_edges_create_tree(const float (*positions)[3],
|
||||
const MEdge *edge,
|
||||
const int edge_num,
|
||||
const BitVector<> &edges_mask,
|
||||
const BitSpan edges_mask,
|
||||
int edges_num_active,
|
||||
float epsilon,
|
||||
int tree_type,
|
||||
|
@ -871,7 +872,7 @@ static BVHTree *bvhtree_from_mesh_edges_create_tree(const float (*positions)[3],
|
|||
|
||||
BVHTree *bvhtree_from_editmesh_edges_ex(BVHTreeFromEditMesh *data,
|
||||
BMEditMesh *em,
|
||||
const BitVector<> &edges_mask,
|
||||
const BitSpan edges_mask,
|
||||
int edges_num_active,
|
||||
float epsilon,
|
||||
int tree_type,
|
||||
|
@ -899,7 +900,7 @@ BVHTree *bvhtree_from_mesh_edges_ex(BVHTreeFromMesh *data,
|
|||
const float (*vert_positions)[3],
|
||||
const MEdge *edge,
|
||||
const int edges_num,
|
||||
const BitVector<> &edges_mask,
|
||||
const BitSpan edges_mask,
|
||||
int edges_num_active,
|
||||
float epsilon,
|
||||
int tree_type,
|
||||
|
@ -931,7 +932,7 @@ static BVHTree *bvhtree_from_mesh_faces_create_tree(float epsilon,
|
|||
const float (*positions)[3],
|
||||
const MFace *face,
|
||||
const int faces_num,
|
||||
const BitVector<> &faces_mask,
|
||||
const BitSpan faces_mask,
|
||||
int faces_num_active)
|
||||
{
|
||||
if (faces_num == 0) {
|
||||
|
@ -984,7 +985,7 @@ static BVHTree *bvhtree_from_editmesh_looptri_create_tree(float epsilon,
|
|||
int tree_type,
|
||||
int axis,
|
||||
BMEditMesh *em,
|
||||
const BitVector<> &looptri_mask,
|
||||
const BitSpan looptri_mask,
|
||||
int looptri_num_active)
|
||||
{
|
||||
const int looptri_num = em->tottri;
|
||||
|
@ -1038,7 +1039,7 @@ static BVHTree *bvhtree_from_mesh_looptri_create_tree(float epsilon,
|
|||
const MLoop *mloop,
|
||||
const MLoopTri *looptri,
|
||||
const int looptri_num,
|
||||
const BitVector<> &looptri_mask,
|
||||
const BitSpan looptri_mask,
|
||||
int looptri_num_active)
|
||||
{
|
||||
if (!looptri_mask.is_empty()) {
|
||||
|
@ -1079,7 +1080,7 @@ static BVHTree *bvhtree_from_mesh_looptri_create_tree(float epsilon,
|
|||
|
||||
BVHTree *bvhtree_from_editmesh_looptri_ex(BVHTreeFromEditMesh *data,
|
||||
BMEditMesh *em,
|
||||
const BitVector<> &looptri_mask,
|
||||
const BitSpan looptri_mask,
|
||||
int looptri_num_active,
|
||||
float epsilon,
|
||||
int tree_type,
|
||||
|
@ -1109,7 +1110,7 @@ BVHTree *bvhtree_from_mesh_looptri_ex(BVHTreeFromMesh *data,
|
|||
const struct MLoop *mloop,
|
||||
const struct MLoopTri *looptri,
|
||||
const int looptri_num,
|
||||
const BitVector<> &looptri_mask,
|
||||
const BitSpan looptri_mask,
|
||||
int looptri_num_active,
|
||||
float epsilon,
|
||||
int tree_type,
|
||||
|
|
|
@ -159,12 +159,8 @@ static void camera_blend_read_expand(BlendExpander *expander, ID *id)
|
|||
BLO_expand(expander, ca->ipo); // XXX deprecated - old animation system
|
||||
|
||||
LISTBASE_FOREACH (CameraBGImage *, bgpic, &ca->bg_images) {
|
||||
if (bgpic->source == CAM_BGIMG_SOURCE_IMAGE) {
|
||||
BLO_expand(expander, bgpic->ima);
|
||||
}
|
||||
else if (bgpic->source == CAM_BGIMG_SOURCE_MOVIE) {
|
||||
BLO_expand(expander, bgpic->ima);
|
||||
}
|
||||
BLO_expand(expander, bgpic->ima);
|
||||
BLO_expand(expander, bgpic->clip);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -557,10 +557,11 @@ int BKE_fcurve_bezt_binarysearch_index(const BezTriple array[],
|
|||
/* ...................................... */
|
||||
|
||||
/* Helper for calc_fcurve_* functions -> find first and last BezTriple to be used. */
|
||||
static short get_fcurve_end_keyframes(const FCurve *fcu,
|
||||
BezTriple **first,
|
||||
BezTriple **last,
|
||||
const bool do_sel_only)
|
||||
static bool get_fcurve_end_keyframes(const FCurve *fcu,
|
||||
BezTriple **first,
|
||||
BezTriple **last,
|
||||
const bool do_sel_only,
|
||||
const float range[2])
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
|
@ -573,11 +574,31 @@ static short get_fcurve_end_keyframes(const FCurve *fcu,
|
|||
return found;
|
||||
}
|
||||
|
||||
int first_index = 0;
|
||||
int last_index = fcu->totvert - 1;
|
||||
|
||||
if (range != NULL) {
|
||||
/* If a range is passed in find the first and last keyframe within that range. */
|
||||
bool replace = false;
|
||||
first_index = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, range[0], fcu->totvert, &replace);
|
||||
last_index = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, range[1], fcu->totvert, &replace);
|
||||
|
||||
/* If first and last index are the same, no keyframes were found in the range. */
|
||||
if (first_index == last_index) {
|
||||
return found;
|
||||
}
|
||||
|
||||
/* The binary search returns an index where a keyframe would be inserted,
|
||||
so it needs to be clamped to ensure it is in range of the array. */
|
||||
first_index = clamp_i(first_index, 0, fcu->totvert - 1);
|
||||
last_index = clamp_i(last_index - 1, 0, fcu->totvert - 1);
|
||||
}
|
||||
|
||||
/* Only include selected items? */
|
||||
if (do_sel_only) {
|
||||
/* Find first selected. */
|
||||
BezTriple *bezt = fcu->bezt;
|
||||
for (int i = 0; i < fcu->totvert; bezt++, i++) {
|
||||
for (int i = first_index; i <= last_index; i++) {
|
||||
BezTriple *bezt = &fcu->bezt[i];
|
||||
if (BEZT_ISSEL_ANY(bezt)) {
|
||||
*first = bezt;
|
||||
found = true;
|
||||
|
@ -586,8 +607,8 @@ static short get_fcurve_end_keyframes(const FCurve *fcu,
|
|||
}
|
||||
|
||||
/* Find last selected. */
|
||||
bezt = ARRAY_LAST_ITEM(fcu->bezt, BezTriple, fcu->totvert);
|
||||
for (int i = 0; i < fcu->totvert; bezt--, i++) {
|
||||
for (int i = last_index; i >= first_index; i--) {
|
||||
BezTriple *bezt = &fcu->bezt[i];
|
||||
if (BEZT_ISSEL_ANY(bezt)) {
|
||||
*last = bezt;
|
||||
found = true;
|
||||
|
@ -596,9 +617,8 @@ static short get_fcurve_end_keyframes(const FCurve *fcu,
|
|||
}
|
||||
}
|
||||
else {
|
||||
/* Use the whole array. */
|
||||
*first = fcu->bezt;
|
||||
*last = ARRAY_LAST_ITEM(fcu->bezt, BezTriple, fcu->totvert);
|
||||
*first = &fcu->bezt[first_index];
|
||||
*last = &fcu->bezt[last_index];
|
||||
found = true;
|
||||
}
|
||||
|
||||
|
@ -611,23 +631,25 @@ bool BKE_fcurve_calc_bounds(const FCurve *fcu,
|
|||
float *ymin,
|
||||
float *ymax,
|
||||
const bool do_sel_only,
|
||||
const bool include_handles)
|
||||
const bool include_handles,
|
||||
const float range[2])
|
||||
{
|
||||
float xminv = 999999999.0f, xmaxv = -999999999.0f;
|
||||
float yminv = 999999999.0f, ymaxv = -999999999.0f;
|
||||
bool foundvert = false;
|
||||
|
||||
const bool use_range = range != NULL;
|
||||
|
||||
if (fcu->totvert) {
|
||||
if (fcu->bezt) {
|
||||
BezTriple *bezt_first = NULL, *bezt_last = NULL;
|
||||
|
||||
if (xmin || xmax) {
|
||||
/* Get endpoint keyframes. */
|
||||
foundvert = get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only);
|
||||
BezTriple *bezt_first = NULL, *bezt_last = NULL;
|
||||
foundvert = get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only, range);
|
||||
|
||||
if (bezt_first) {
|
||||
BLI_assert(bezt_last != NULL);
|
||||
|
||||
foundvert = true;
|
||||
if (include_handles) {
|
||||
xminv = min_fff(xminv, bezt_first->vec[0][0], bezt_first->vec[1][0]);
|
||||
xmaxv = max_fff(xmaxv, bezt_last->vec[1][0], bezt_last->vec[2][0]);
|
||||
|
@ -645,6 +667,9 @@ bool BKE_fcurve_calc_bounds(const FCurve *fcu,
|
|||
|
||||
int i;
|
||||
for (bezt = fcu->bezt, i = 0; i < fcu->totvert; prevbezt = bezt, bezt++, i++) {
|
||||
if (use_range && (bezt->vec[1][0] < range[0] || bezt->vec[1][0] > range[1])) {
|
||||
continue;
|
||||
}
|
||||
if ((do_sel_only == false) || BEZT_ISSEL_ANY(bezt)) {
|
||||
/* Keyframe itself. */
|
||||
yminv = min_ff(yminv, bezt->vec[1][1]);
|
||||
|
@ -717,10 +742,10 @@ bool BKE_fcurve_calc_bounds(const FCurve *fcu,
|
|||
}
|
||||
|
||||
if (xmin) {
|
||||
*xmin = 0.0f;
|
||||
*xmin = use_range ? range[0] : 0.0f;
|
||||
}
|
||||
if (xmax) {
|
||||
*xmax = 1.0f;
|
||||
*xmax = use_range ? range[1] : 1.0f;
|
||||
}
|
||||
|
||||
if (ymin) {
|
||||
|
@ -745,7 +770,7 @@ bool BKE_fcurve_calc_range(
|
|||
BezTriple *bezt_first = NULL, *bezt_last = NULL;
|
||||
|
||||
/* Get endpoint keyframes. */
|
||||
get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only);
|
||||
get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only, NULL);
|
||||
|
||||
if (bezt_first) {
|
||||
BLI_assert(bezt_last != NULL);
|
||||
|
|
|
@ -190,6 +190,9 @@ static int stroke_march_next_point(const bGPDstroke *gps,
|
|||
float *pressure,
|
||||
float *strength,
|
||||
float *vert_color,
|
||||
float *uv_fac,
|
||||
float *uv_fill,
|
||||
float *uv_rot,
|
||||
float *ratio_result,
|
||||
int *index_from,
|
||||
int *index_to)
|
||||
|
@ -271,6 +274,10 @@ static int stroke_march_next_point(const bGPDstroke *gps,
|
|||
gps->points[next_point_index].pressure, gps->points[*index_from].pressure, vratio);
|
||||
*strength = interpf(
|
||||
gps->points[next_point_index].strength, gps->points[*index_from].strength, vratio);
|
||||
*uv_fac = interpf(gps->points[next_point_index].uv_fac, gps->points[*index_from].uv_fac, vratio);
|
||||
*uv_rot = interpf(gps->points[next_point_index].uv_rot, gps->points[*index_from].uv_rot, vratio);
|
||||
interp_v2_v2v2(
|
||||
uv_fill, gps->points[*index_from].uv_fill, gps->points[next_point_index].uv_fill, vratio);
|
||||
interp_v4_v4v4(vert_color,
|
||||
gps->points[*index_from].vert_color,
|
||||
gps->points[next_point_index].vert_color,
|
||||
|
@ -474,6 +481,7 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd,
|
|||
int next_point_index = 1;
|
||||
int i = 0;
|
||||
float pressure, strength, ratio_result;
|
||||
float uv_fac, uv_rot, uv_fill[2];
|
||||
float vert_color[4];
|
||||
int index_from, index_to;
|
||||
float last_coord[3];
|
||||
|
@ -504,6 +512,9 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd,
|
|||
&pressure,
|
||||
&strength,
|
||||
vert_color,
|
||||
&uv_fac,
|
||||
uv_fill,
|
||||
&uv_rot,
|
||||
&ratio_result,
|
||||
&index_from,
|
||||
&index_to)) > -1) {
|
||||
|
@ -514,6 +525,10 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd,
|
|||
copy_v3_v3(&pt2->x, last_coord);
|
||||
new_pt[i].pressure = pressure;
|
||||
new_pt[i].strength = strength;
|
||||
new_pt[i].uv_fac = uv_fac;
|
||||
new_pt[i].uv_rot = uv_rot;
|
||||
copy_v2_v2(new_pt[i].uv_fill, uv_fill);
|
||||
|
||||
memcpy(new_pt[i].vert_color, vert_color, sizeof(float[4]));
|
||||
if (select) {
|
||||
new_pt[i].flag |= GP_SPOINT_SELECT;
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include "DNA_meshdata_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "BLI_bit_vector.hh"
|
||||
#include "BLI_bounds.hh"
|
||||
#include "BLI_edgehash.h"
|
||||
#include "BLI_endian_switch.h"
|
||||
|
@ -66,7 +65,6 @@
|
|||
|
||||
#include "BLO_read_write.h"
|
||||
|
||||
using blender::BitVector;
|
||||
using blender::float3;
|
||||
using blender::MutableSpan;
|
||||
using blender::Span;
|
||||
|
|
|
@ -316,7 +316,7 @@ void BKE_mesh_foreach_mapped_subdiv_face_center(
|
|||
BKE_mesh_vertex_normals_ensure(mesh) :
|
||||
nullptr;
|
||||
const int *index = static_cast<const int *>(CustomData_get_layer(&mesh->pdata, CD_ORIGINDEX));
|
||||
const blender::BitVector<> &facedot_tags = mesh->runtime->subsurf_face_dot_tags;
|
||||
const blender::BitSpan facedot_tags = mesh->runtime->subsurf_face_dot_tags;
|
||||
|
||||
if (index) {
|
||||
for (int i = 0; i < mesh->totpoly; i++, mp++) {
|
||||
|
|
|
@ -1558,7 +1558,6 @@ void BKE_mesh_legacy_convert_uvs_to_struct(
|
|||
{
|
||||
using namespace blender;
|
||||
using namespace blender::bke;
|
||||
const AttributeAccessor attributes = mesh->attributes();
|
||||
Vector<CustomDataLayer, 16> new_layer_to_write;
|
||||
|
||||
/* Don't write the boolean UV map sublayers which will be written in the legacy #MLoopUV type. */
|
||||
|
@ -1591,24 +1590,30 @@ void BKE_mesh_legacy_convert_uvs_to_struct(
|
|||
mloopuv_layer.data = mloopuv.data();
|
||||
|
||||
char buffer[MAX_CUSTOMDATA_LAYER_NAME];
|
||||
const VArray<bool> vert_selection = attributes.lookup_or_default<bool>(
|
||||
BKE_uv_map_vert_select_name_get(layer.name, buffer), ATTR_DOMAIN_CORNER, false);
|
||||
const VArray<bool> edge_selection = attributes.lookup_or_default<bool>(
|
||||
BKE_uv_map_edge_select_name_get(layer.name, buffer), ATTR_DOMAIN_CORNER, false);
|
||||
const VArray<bool> pin = attributes.lookup_or_default<bool>(
|
||||
BKE_uv_map_pin_name_get(layer.name, buffer), ATTR_DOMAIN_CORNER, false);
|
||||
const bool *vert_selection = static_cast<const bool *>(CustomData_get_layer_named(
|
||||
&mesh->ldata, CD_PROP_BOOL, BKE_uv_map_vert_select_name_get(layer.name, buffer)));
|
||||
const bool *edge_selection = static_cast<const bool *>(CustomData_get_layer_named(
|
||||
&mesh->ldata, CD_PROP_BOOL, BKE_uv_map_edge_select_name_get(layer.name, buffer)));
|
||||
const bool *pin = static_cast<const bool *>(CustomData_get_layer_named(
|
||||
&mesh->ldata, CD_PROP_BOOL, BKE_uv_map_pin_name_get(layer.name, buffer)));
|
||||
|
||||
threading::parallel_for(mloopuv.index_range(), 2048, [&](IndexRange range) {
|
||||
for (const int i : range) {
|
||||
copy_v2_v2(mloopuv[i].uv, coords[i]);
|
||||
SET_FLAG_FROM_TEST(mloopuv[i].flag, vert_selection[i], MLOOPUV_VERTSEL);
|
||||
SET_FLAG_FROM_TEST(mloopuv[i].flag, edge_selection[i], MLOOPUV_EDGESEL);
|
||||
SET_FLAG_FROM_TEST(mloopuv[i].flag, pin[i], MLOOPUV_PINNED);
|
||||
SET_FLAG_FROM_TEST(mloopuv[i].flag, vert_selection && vert_selection[i], MLOOPUV_VERTSEL);
|
||||
SET_FLAG_FROM_TEST(mloopuv[i].flag, edge_selection && edge_selection[i], MLOOPUV_EDGESEL);
|
||||
SET_FLAG_FROM_TEST(mloopuv[i].flag, pin && pin[i], MLOOPUV_PINNED);
|
||||
}
|
||||
});
|
||||
new_layer_to_write.append(mloopuv_layer);
|
||||
}
|
||||
|
||||
/* #CustomData expects the layers to be sorted in increasing order based on type. */
|
||||
std::stable_sort(
|
||||
new_layer_to_write.begin(),
|
||||
new_layer_to_write.end(),
|
||||
[](const CustomDataLayer &a, const CustomDataLayer &b) { return a.type < b.type; });
|
||||
|
||||
loop_layers_to_write = new_layer_to_write;
|
||||
mesh->ldata.totlayer = new_layer_to_write.size();
|
||||
mesh->ldata.maxlayer = mesh->ldata.totlayer;
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
using blender::BitVector;
|
||||
using blender::float3;
|
||||
using blender::int2;
|
||||
using blender::MutableBitSpan;
|
||||
using blender::MutableSpan;
|
||||
using blender::short2;
|
||||
using blender::Span;
|
||||
|
@ -1238,7 +1239,7 @@ static bool loop_split_generator_check_cyclic_smooth_fan(const Span<MLoop> mloop
|
|||
const Span<int2> edge_to_loops,
|
||||
const Span<int> loop_to_poly,
|
||||
const int *e2l_prev,
|
||||
BitVector<> &skip_loops,
|
||||
MutableBitSpan skip_loops,
|
||||
const int ml_curr_index,
|
||||
const int ml_prev_index,
|
||||
const int mp_curr_index)
|
||||
|
|
|
@ -77,40 +77,40 @@ TEST(nla_track, BKE_nlatrack_remove_strip)
|
|||
strip2.start = 11;
|
||||
strip2.end = 20;
|
||||
|
||||
// Add NLA strips to the NLATrack.
|
||||
/* Add NLA strips to the NLATrack. */
|
||||
BKE_nlastrips_add_strip(&strips, &strip1);
|
||||
BKE_nlastrips_add_strip(&strips, &strip2);
|
||||
track.strips = strips;
|
||||
|
||||
// ensure we have 2 strips in the track.
|
||||
/* Ensure we have 2 strips in the track. */
|
||||
EXPECT_EQ(2, BLI_listbase_count(&track.strips));
|
||||
|
||||
BKE_nlatrack_remove_strip(&track, &strip2);
|
||||
EXPECT_EQ(1, BLI_listbase_count(&track.strips));
|
||||
// ensure the correct strip was removed.
|
||||
/* Ensure the correct strip was removed. */
|
||||
EXPECT_EQ(-1, BLI_findindex(&track.strips, &strip2));
|
||||
}
|
||||
|
||||
TEST(nla_track, BKE_nlatrack_remove_and_free)
|
||||
{
|
||||
AnimData adt{};
|
||||
AnimData adt{};
|
||||
NlaTrack *track1;
|
||||
NlaTrack *track2;
|
||||
|
||||
// Add NLA tracks to the Animation Data.
|
||||
/* Add NLA tracks to the Animation Data. */
|
||||
track1 = BKE_nlatrack_add(&adt, NULL, false);
|
||||
track2 = BKE_nlatrack_add(&adt, track1, false);
|
||||
|
||||
// ensure we have 2 tracks in the track.
|
||||
/* Ensure we have 2 tracks in the track. */
|
||||
EXPECT_EQ(2, BLI_listbase_count(&adt.nla_tracks));
|
||||
|
||||
BKE_nlatrack_remove_and_free(&adt.nla_tracks, track2, false);
|
||||
EXPECT_EQ(1, BLI_listbase_count(&adt.nla_tracks));
|
||||
|
||||
// ensure the correct track was removed.
|
||||
/* Ensure the correct track was removed. */
|
||||
EXPECT_EQ(-1, BLI_findindex(&adt.nla_tracks, track2));
|
||||
|
||||
// free the rest of the tracks, and ensure they are removed.
|
||||
/* Free the rest of the tracks, and ensure they are removed. */
|
||||
BKE_nlatrack_remove_and_free(&adt.nla_tracks, track1, false);
|
||||
EXPECT_EQ(0, BLI_listbase_count(&adt.nla_tracks));
|
||||
EXPECT_EQ(-1, BLI_findindex(&adt.nla_tracks, track1));
|
||||
|
|
|
@ -2479,6 +2479,8 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node)
|
|||
link.tosock->link = &link;
|
||||
}
|
||||
|
||||
Vector<bNodeLink *> duplicate_links_to_remove;
|
||||
|
||||
/* redirect downstream links */
|
||||
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
|
||||
/* do we have internal link? */
|
||||
|
@ -2495,7 +2497,7 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node)
|
|||
link_to_compare->tosock == link->tosock) {
|
||||
adjust_multi_input_indices_after_removed_link(
|
||||
ntree, link_to_compare->tosock, link_to_compare->multi_input_socket_index);
|
||||
nodeRemLink(ntree, link_to_compare);
|
||||
duplicate_links_to_remove.append_non_duplicates(link_to_compare);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2533,6 +2535,10 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node)
|
|||
}
|
||||
}
|
||||
|
||||
for (bNodeLink *link : duplicate_links_to_remove) {
|
||||
nodeRemLink(ntree, link);
|
||||
}
|
||||
|
||||
/* remove remaining upstream links */
|
||||
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
|
||||
if (link->tonode == node) {
|
||||
|
|
|
@ -508,6 +508,7 @@ static void prepare_inferencing_interfaces(
|
|||
|
||||
bool update_field_inferencing(const bNodeTree &tree)
|
||||
{
|
||||
BLI_assert(tree.type == NTREE_GEOMETRY);
|
||||
tree.ensure_topology_cache();
|
||||
|
||||
const Span<const bNode *> nodes = tree.all_nodes();
|
||||
|
|
|
@ -2266,6 +2266,8 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob)
|
|||
return pbvh;
|
||||
}
|
||||
|
||||
ob->sculpt->islands_valid = false;
|
||||
|
||||
if (ob->sculpt->bm != nullptr) {
|
||||
/* Sculpting on a BMesh (dynamic-topology) gets a special PBVH. */
|
||||
pbvh = build_pbvh_for_dynamic_topology(ob);
|
||||
|
|
|
@ -956,7 +956,7 @@ ScrArea *BKE_screen_area_map_find_area_xy(const ScrAreaMap *areamap,
|
|||
const int xy[2])
|
||||
{
|
||||
LISTBASE_FOREACH (ScrArea *, area, &areamap->areabase) {
|
||||
/* Test area's outer screen verts, not inner totrct. */
|
||||
/* Test area's outer screen verts, not inner `area->totrct`. */
|
||||
if (xy[0] >= area->v1->vec.x && xy[0] <= area->v4->vec.x && xy[1] >= area->v1->vec.y &&
|
||||
xy[1] <= area->v2->vec.y) {
|
||||
if (ELEM(spacetype, SPACE_TYPE_ANY, area->spacetype)) {
|
||||
|
|
|
@ -0,0 +1,234 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* This file provides the basis for processing "indexed bits" (i.e. every bit has an index).
|
||||
* The main purpose of this file is to define how bits are indexed within a memory buffer.
|
||||
* For example, one has to define whether the first bit is the least or most significant bit and
|
||||
* how endianness affect the bit order.
|
||||
*
|
||||
* The order is defined as follows:
|
||||
* - Every indexed bit is part of an #BitInt. These ints are ordered by their address as usual.
|
||||
* - Within each #BitInt, the bits are ordered from least to most significant.
|
||||
*/
|
||||
|
||||
#include "BLI_index_range.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include <ostream>
|
||||
|
||||
namespace blender::bits {
|
||||
|
||||
/** Using a large integer type is better because then it's easier to process many bits at once. */
|
||||
using BitInt = uint64_t;
|
||||
/** Number of bits that fit into #BitInt. */
|
||||
static constexpr int64_t BitsPerInt = int64_t(sizeof(BitInt) * 8);
|
||||
/** Shift amount to get from a bit index to an int index. Equivalent to `log(BitsPerInt, 2)`. */
|
||||
static constexpr int64_t BitToIntIndexShift = 3 + (sizeof(BitInt) >= 2) + (sizeof(BitInt) >= 4) +
|
||||
(sizeof(BitInt) >= 8);
|
||||
/** Bit mask containing a 1 for the last few bits that index a bit inside of an #BitInt. */
|
||||
static constexpr BitInt BitIndexMask = (BitInt(1) << BitToIntIndexShift) - 1;
|
||||
|
||||
inline BitInt mask_first_n_bits(const int64_t n)
|
||||
{
|
||||
BLI_assert(n >= 0);
|
||||
BLI_assert(n <= BitsPerInt);
|
||||
if (n == BitsPerInt) {
|
||||
return BitInt(-1);
|
||||
}
|
||||
return (BitInt(1) << n) - 1;
|
||||
}
|
||||
|
||||
inline BitInt mask_last_n_bits(const int64_t n)
|
||||
{
|
||||
return ~mask_first_n_bits(BitsPerInt - n);
|
||||
}
|
||||
|
||||
inline BitInt mask_range_bits(const int64_t start, const int64_t size)
|
||||
{
|
||||
BLI_assert(start >= 0);
|
||||
BLI_assert(size >= 0);
|
||||
const int64_t end = start + size;
|
||||
BLI_assert(end <= BitsPerInt);
|
||||
if (end == BitsPerInt) {
|
||||
return mask_last_n_bits(size);
|
||||
}
|
||||
return ((BitInt(1) << end) - 1) & ~((BitInt(1) << start) - 1);
|
||||
}
|
||||
|
||||
inline BitInt mask_single_bit(const int64_t bit_index)
|
||||
{
|
||||
BLI_assert(bit_index >= 0);
|
||||
BLI_assert(bit_index < BitsPerInt);
|
||||
return BitInt(1) << bit_index;
|
||||
}
|
||||
|
||||
inline BitInt *int_containing_bit(BitInt *data, const int64_t bit_index)
|
||||
{
|
||||
return data + (bit_index >> BitToIntIndexShift);
|
||||
}
|
||||
|
||||
inline const BitInt *int_containing_bit(const BitInt *data, const int64_t bit_index)
|
||||
{
|
||||
return data + (bit_index >> BitToIntIndexShift);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a read-only pointer to a specific bit. The value of the bit can be retrieved, but
|
||||
* not changed.
|
||||
*/
|
||||
class BitRef {
|
||||
private:
|
||||
/** Points to the exact integer that the bit is in. */
|
||||
const BitInt *int_;
|
||||
/** All zeros except for a single one at the bit that is referenced. */
|
||||
BitInt mask_;
|
||||
|
||||
friend class MutableBitRef;
|
||||
|
||||
public:
|
||||
BitRef() = default;
|
||||
|
||||
/**
|
||||
* Reference a specific bit in an array. Note that #data does *not* have to point to the
|
||||
* exact integer the bit is in.
|
||||
*/
|
||||
BitRef(const BitInt *data, const int64_t bit_index)
|
||||
{
|
||||
int_ = int_containing_bit(data, bit_index);
|
||||
mask_ = mask_single_bit(bit_index & BitIndexMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true when the bit is currently 1 and false otherwise.
|
||||
*/
|
||||
bool test() const
|
||||
{
|
||||
const BitInt value = *int_;
|
||||
const BitInt masked_value = value & mask_;
|
||||
return masked_value != 0;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return this->test();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Similar to #BitRef, but also allows changing the referenced bit.
|
||||
*/
|
||||
class MutableBitRef {
|
||||
private:
|
||||
/** Points to the integer that the bit is in. */
|
||||
BitInt *int_;
|
||||
/** All zeros except for a single one at the bit that is referenced. */
|
||||
BitInt mask_;
|
||||
|
||||
public:
|
||||
MutableBitRef() = default;
|
||||
|
||||
/**
|
||||
* Reference a specific bit in an array. Note that #data does *not* have to point to the
|
||||
* exact int the bit is in.
|
||||
*/
|
||||
MutableBitRef(BitInt *data, const int64_t bit_index)
|
||||
{
|
||||
int_ = int_containing_bit(data, bit_index);
|
||||
mask_ = mask_single_bit(bit_index & BitIndexMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Support implicitly casting to a read-only #BitRef.
|
||||
*/
|
||||
operator BitRef() const
|
||||
{
|
||||
BitRef bit_ref;
|
||||
bit_ref.int_ = int_;
|
||||
bit_ref.mask_ = mask_;
|
||||
return bit_ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true when the bit is currently 1 and false otherwise.
|
||||
*/
|
||||
bool test() const
|
||||
{
|
||||
const BitInt value = *int_;
|
||||
const BitInt masked_value = value & mask_;
|
||||
return masked_value != 0;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return this->test();
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the bit to a 1.
|
||||
*/
|
||||
void set()
|
||||
{
|
||||
*int_ |= mask_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the bit to a 0.
|
||||
*/
|
||||
void reset()
|
||||
{
|
||||
*int_ &= ~mask_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the bit to a 1 if #value is true and 0 otherwise. If the value is highly unpredictable
|
||||
* by the CPU branch predictor, it can be faster to use #set_branchless instead.
|
||||
*/
|
||||
void set(const bool value)
|
||||
{
|
||||
if (value) {
|
||||
this->set();
|
||||
}
|
||||
else {
|
||||
this->reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the same as #set, but does not use a branch. This is faster when the input value is
|
||||
* unpredictable for the CPU branch predictor (best case for this function is a uniform random
|
||||
* distribution with 50% probability for true and false). If the value is predictable, this is
|
||||
* likely slower than #set.
|
||||
*/
|
||||
void set_branchless(const bool value)
|
||||
{
|
||||
const BitInt value_int = BitInt(value);
|
||||
BLI_assert(ELEM(value_int, 0, 1));
|
||||
const BitInt old = *int_;
|
||||
*int_ =
|
||||
/* Unset bit. */
|
||||
(~mask_ & old)
|
||||
/* Optionally set it again. The -1 turns a 1 into `0x00...` and a 0 into `0xff...`. */
|
||||
| (mask_ & ~(value_int - 1));
|
||||
}
|
||||
};
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &stream, const BitRef &bit)
|
||||
{
|
||||
return stream << (bit ? "1" : "0");
|
||||
}
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &stream, const MutableBitRef &bit)
|
||||
{
|
||||
return stream << BitRef(bit);
|
||||
}
|
||||
|
||||
} // namespace blender::bits
|
||||
|
||||
namespace blender {
|
||||
using bits::BitRef;
|
||||
using bits::MutableBitRef;
|
||||
} // namespace blender
|
|
@ -0,0 +1,290 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_bit_ref.hh"
|
||||
#include "BLI_index_range.hh"
|
||||
#include "BLI_memory_utils.hh"
|
||||
|
||||
namespace blender::bits {
|
||||
|
||||
/** Base class for a const and non-const bit-iterator. */
|
||||
class BitIteratorBase {
|
||||
protected:
|
||||
const BitInt *data_;
|
||||
int64_t bit_index_;
|
||||
|
||||
public:
|
||||
BitIteratorBase(const BitInt *data, const int64_t bit_index) : data_(data), bit_index_(bit_index)
|
||||
{
|
||||
}
|
||||
|
||||
BitIteratorBase &operator++()
|
||||
{
|
||||
bit_index_++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend bool operator!=(const BitIteratorBase &a, const BitIteratorBase &b)
|
||||
{
|
||||
BLI_assert(a.data_ == b.data_);
|
||||
return a.bit_index_ != b.bit_index_;
|
||||
}
|
||||
};
|
||||
|
||||
/** Allows iterating over the bits in a memory buffer. */
|
||||
class BitIterator : public BitIteratorBase {
|
||||
public:
|
||||
BitIterator(const BitInt *data, const int64_t bit_index) : BitIteratorBase(data, bit_index)
|
||||
{
|
||||
}
|
||||
|
||||
BitRef operator*() const
|
||||
{
|
||||
return BitRef(data_, bit_index_);
|
||||
}
|
||||
};
|
||||
|
||||
/** Allows iterating over the bits in a memory buffer. */
|
||||
class MutableBitIterator : public BitIteratorBase {
|
||||
public:
|
||||
MutableBitIterator(BitInt *data, const int64_t bit_index) : BitIteratorBase(data, bit_index)
|
||||
{
|
||||
}
|
||||
|
||||
MutableBitRef operator*() const
|
||||
{
|
||||
return MutableBitRef(const_cast<BitInt *>(data_), bit_index_);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Similar to #Span, but references a range of bits instead of normal C++ types (which must be at
|
||||
* least one byte large). Use #MutableBitSpan if the values are supposed to be modified.
|
||||
*
|
||||
* The beginning and end of a #BitSpan does *not* have to be at byte/int boundaries. It can start
|
||||
* and end at any bit.
|
||||
*/
|
||||
class BitSpan {
|
||||
private:
|
||||
/** Base pointer to the integers containing the bits. The actual bit span might start at a much
|
||||
* higher address when `bit_range_.start()` is large. */
|
||||
const BitInt *data_ = nullptr;
|
||||
/** The range of referenced bits. */
|
||||
IndexRange bit_range_ = {0, 0};
|
||||
|
||||
public:
|
||||
/** Construct an empty span. */
|
||||
BitSpan() = default;
|
||||
|
||||
BitSpan(const BitInt *data, const int64_t size_in_bits) : data_(data), bit_range_(size_in_bits)
|
||||
{
|
||||
}
|
||||
|
||||
BitSpan(const BitInt *data, const IndexRange bit_range) : data_(data), bit_range_(bit_range)
|
||||
{
|
||||
}
|
||||
|
||||
/** Number of bits referenced by the span. */
|
||||
int64_t size() const
|
||||
{
|
||||
return bit_range_.size();
|
||||
}
|
||||
|
||||
bool is_empty() const
|
||||
{
|
||||
return bit_range_.is_empty();
|
||||
}
|
||||
|
||||
IndexRange index_range() const
|
||||
{
|
||||
return IndexRange(bit_range_.size());
|
||||
}
|
||||
|
||||
BitRef operator[](const int64_t index) const
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < bit_range_.size());
|
||||
return {data_, bit_range_.start() + index};
|
||||
}
|
||||
|
||||
BitSpan slice(const IndexRange range) const
|
||||
{
|
||||
return {data_, bit_range_.slice(range)};
|
||||
}
|
||||
|
||||
const BitInt *data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
const IndexRange &bit_range() const
|
||||
{
|
||||
return bit_range_;
|
||||
}
|
||||
|
||||
BitIterator begin() const
|
||||
{
|
||||
return {data_, bit_range_.start()};
|
||||
}
|
||||
|
||||
BitIterator end() const
|
||||
{
|
||||
return {data_, bit_range_.one_after_last()};
|
||||
}
|
||||
};
|
||||
|
||||
/** Same as #BitSpan, but also allows modifying the referenced bits. */
|
||||
class MutableBitSpan {
|
||||
private:
|
||||
BitInt *data_ = nullptr;
|
||||
IndexRange bit_range_ = {0, 0};
|
||||
|
||||
public:
|
||||
MutableBitSpan() = default;
|
||||
|
||||
MutableBitSpan(BitInt *data, const int64_t size) : data_(data), bit_range_(size)
|
||||
{
|
||||
}
|
||||
|
||||
MutableBitSpan(BitInt *data, const IndexRange bit_range) : data_(data), bit_range_(bit_range)
|
||||
{
|
||||
}
|
||||
|
||||
int64_t size() const
|
||||
{
|
||||
return bit_range_.size();
|
||||
}
|
||||
|
||||
bool is_empty() const
|
||||
{
|
||||
return bit_range_.is_empty();
|
||||
}
|
||||
|
||||
IndexRange index_range() const
|
||||
{
|
||||
return IndexRange(bit_range_.size());
|
||||
}
|
||||
|
||||
MutableBitRef operator[](const int64_t index) const
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < bit_range_.size());
|
||||
return {data_, bit_range_.start() + index};
|
||||
}
|
||||
|
||||
MutableBitSpan slice(const IndexRange range) const
|
||||
{
|
||||
return {data_, bit_range_.slice(range)};
|
||||
}
|
||||
|
||||
BitInt *data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
const IndexRange &bit_range() const
|
||||
{
|
||||
return bit_range_;
|
||||
}
|
||||
|
||||
MutableBitIterator begin() const
|
||||
{
|
||||
return {data_, bit_range_.start()};
|
||||
}
|
||||
|
||||
MutableBitIterator end() const
|
||||
{
|
||||
return {data_, bit_range_.one_after_last()};
|
||||
}
|
||||
|
||||
operator BitSpan() const
|
||||
{
|
||||
return {data_, bit_range_};
|
||||
}
|
||||
|
||||
/** Sets all referenced bits to 1. */
|
||||
void set_all()
|
||||
{
|
||||
const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt);
|
||||
{
|
||||
BitInt &first_int = *int_containing_bit(data_, bit_range_.start());
|
||||
const BitInt first_int_mask = mask_range_bits(ranges.prefix.start() & BitIndexMask,
|
||||
ranges.prefix.size());
|
||||
first_int |= first_int_mask;
|
||||
}
|
||||
{
|
||||
BitInt *start = int_containing_bit(data_, ranges.aligned.start());
|
||||
const int64_t ints_to_fill = ranges.aligned.size() / BitsPerInt;
|
||||
constexpr BitInt fill_value = BitInt(-1);
|
||||
initialized_fill_n(start, ints_to_fill, fill_value);
|
||||
}
|
||||
{
|
||||
BitInt &last_int = *int_containing_bit(data_, bit_range_.one_after_last() - 1);
|
||||
const BitInt last_int_mask = mask_first_n_bits(ranges.suffix.size());
|
||||
last_int |= last_int_mask;
|
||||
}
|
||||
}
|
||||
|
||||
/** Sets all referenced bits to 0. */
|
||||
void reset_all()
|
||||
{
|
||||
const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt);
|
||||
{
|
||||
BitInt &first_int = *int_containing_bit(data_, bit_range_.start());
|
||||
const BitInt first_int_mask = mask_range_bits(ranges.prefix.start() & BitIndexMask,
|
||||
ranges.prefix.size());
|
||||
first_int &= ~first_int_mask;
|
||||
}
|
||||
{
|
||||
BitInt *start = int_containing_bit(data_, ranges.aligned.start());
|
||||
const int64_t ints_to_fill = ranges.aligned.size() / BitsPerInt;
|
||||
constexpr BitInt fill_value = 0;
|
||||
initialized_fill_n(start, ints_to_fill, fill_value);
|
||||
}
|
||||
{
|
||||
BitInt &last_int = *int_containing_bit(data_, bit_range_.one_after_last() - 1);
|
||||
const BitInt last_int_mask = mask_first_n_bits(ranges.suffix.size());
|
||||
last_int &= ~last_int_mask;
|
||||
}
|
||||
}
|
||||
|
||||
/** Sets all referenced bits to either 0 or 1. */
|
||||
void set_all(const bool value)
|
||||
{
|
||||
if (value) {
|
||||
this->set_all();
|
||||
}
|
||||
else {
|
||||
this->reset_all();
|
||||
}
|
||||
}
|
||||
|
||||
/** Same as #set_all to mirror #MutableSpan. */
|
||||
void fill(const bool value)
|
||||
{
|
||||
this->set_all(value);
|
||||
}
|
||||
};
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &stream, const BitSpan &span)
|
||||
{
|
||||
stream << "(Size: " << span.size() << ", ";
|
||||
for (const BitRef bit : span) {
|
||||
stream << bit;
|
||||
}
|
||||
stream << ")";
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &stream, const MutableBitSpan &span)
|
||||
{
|
||||
return stream << BitSpan(span);
|
||||
}
|
||||
|
||||
} // namespace blender::bits
|
||||
|
||||
namespace blender {
|
||||
using bits::BitSpan;
|
||||
using bits::MutableBitSpan;
|
||||
} // namespace blender
|
|
@ -38,142 +38,11 @@
|
|||
#include <cstring>
|
||||
|
||||
#include "BLI_allocator.hh"
|
||||
#include "BLI_index_range.hh"
|
||||
#include "BLI_memory_utils.hh"
|
||||
#include "BLI_bit_span.hh"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
namespace blender::bits {
|
||||
|
||||
/**
|
||||
* Using a large integer type is better because then it's easier to process many bits at once.
|
||||
*/
|
||||
using IntType = uint64_t;
|
||||
static constexpr int64_t BitsPerInt = int64_t(sizeof(IntType) * 8);
|
||||
static constexpr int64_t BitToIntIndexShift = 3 + (sizeof(IntType) >= 2) + (sizeof(IntType) >= 4) +
|
||||
(sizeof(IntType) >= 8);
|
||||
static constexpr IntType BitIndexMask = (IntType(1) << BitToIntIndexShift) - 1;
|
||||
|
||||
/**
|
||||
* This is a read-only pointer to a specific bit. The value of the bit can be retrieved, but
|
||||
* not changed.
|
||||
*/
|
||||
class BitRef {
|
||||
private:
|
||||
/** Points to the integer that the bit is in. */
|
||||
const IntType *ptr_;
|
||||
/** All zeros except for a single one at the bit that is referenced. */
|
||||
IntType mask_;
|
||||
|
||||
friend class MutableBitRef;
|
||||
|
||||
public:
|
||||
BitRef() = default;
|
||||
|
||||
/**
|
||||
* Reference a specific bit in an array. Note that #ptr does *not* have to point to the
|
||||
* exact integer the bit is in.
|
||||
*/
|
||||
BitRef(const IntType *ptr, const int64_t bit_index)
|
||||
{
|
||||
ptr_ = ptr + (bit_index >> BitToIntIndexShift);
|
||||
mask_ = IntType(1) << (bit_index & BitIndexMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true when the bit is currently 1 and false otherwise.
|
||||
*/
|
||||
bool test() const
|
||||
{
|
||||
const IntType value = *ptr_;
|
||||
const IntType masked_value = value & mask_;
|
||||
return masked_value != 0;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return this->test();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Similar to #BitRef, but also allows changing the referenced bit.
|
||||
*/
|
||||
class MutableBitRef {
|
||||
private:
|
||||
/** Points to the integer that the bit is in. */
|
||||
IntType *ptr_;
|
||||
/** All zeros except for a single one at the bit that is referenced. */
|
||||
IntType mask_;
|
||||
|
||||
public:
|
||||
MutableBitRef() = default;
|
||||
|
||||
/**
|
||||
* Reference a specific bit in an array. Note that #ptr does *not* have to point to the
|
||||
* exact int the bit is in.
|
||||
*/
|
||||
MutableBitRef(IntType *ptr, const int64_t bit_index)
|
||||
{
|
||||
ptr_ = ptr + (bit_index >> BitToIntIndexShift);
|
||||
mask_ = IntType(1) << IntType(bit_index & BitIndexMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Support implicitly casting to a read-only #BitRef.
|
||||
*/
|
||||
operator BitRef() const
|
||||
{
|
||||
BitRef bit_ref;
|
||||
bit_ref.ptr_ = ptr_;
|
||||
bit_ref.mask_ = mask_;
|
||||
return bit_ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true when the bit is currently 1 and false otherwise.
|
||||
*/
|
||||
bool test() const
|
||||
{
|
||||
const IntType value = *ptr_;
|
||||
const IntType masked_value = value & mask_;
|
||||
return masked_value != 0;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return this->test();
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the bit to a 1.
|
||||
*/
|
||||
void set()
|
||||
{
|
||||
*ptr_ |= mask_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the bit to a 0.
|
||||
*/
|
||||
void reset()
|
||||
{
|
||||
*ptr_ &= ~mask_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the bit to a 1 if #value is true and 0 otherwise.
|
||||
*/
|
||||
void set(const bool value)
|
||||
{
|
||||
if (value) {
|
||||
this->set();
|
||||
}
|
||||
else {
|
||||
this->reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<
|
||||
/**
|
||||
* Number of bits that can be stored in the vector without doing an allocation.
|
||||
|
@ -193,13 +62,13 @@ class BitVector {
|
|||
|
||||
static constexpr int64_t IntsInInlineBuffer = required_ints_for_bits(InlineBufferCapacity);
|
||||
static constexpr int64_t BitsInInlineBuffer = IntsInInlineBuffer * BitsPerInt;
|
||||
static constexpr int64_t AllocationAlignment = alignof(IntType);
|
||||
static constexpr int64_t AllocationAlignment = alignof(BitInt);
|
||||
|
||||
/**
|
||||
* Points to the first integer used by the vector. It might point to the memory in the inline
|
||||
* buffer.
|
||||
*/
|
||||
IntType *data_;
|
||||
BitInt *data_;
|
||||
|
||||
/** Current size of the vector in bits. */
|
||||
int64_t size_in_bits_;
|
||||
|
@ -211,7 +80,7 @@ class BitVector {
|
|||
BLI_NO_UNIQUE_ADDRESS Allocator allocator_;
|
||||
|
||||
/** Contains the bits as long as the vector is small enough. */
|
||||
BLI_NO_UNIQUE_ADDRESS TypedBuffer<IntType, IntsInInlineBuffer> inline_buffer_;
|
||||
BLI_NO_UNIQUE_ADDRESS TypedBuffer<BitInt, IntsInInlineBuffer> inline_buffer_;
|
||||
|
||||
public:
|
||||
BitVector(Allocator allocator = {}) noexcept : allocator_(allocator)
|
||||
|
@ -219,7 +88,7 @@ class BitVector {
|
|||
data_ = inline_buffer_;
|
||||
size_in_bits_ = 0;
|
||||
capacity_in_bits_ = BitsInInlineBuffer;
|
||||
uninitialized_fill_n(data_, IntsInInlineBuffer, IntType(0));
|
||||
uninitialized_fill_n(data_, IntsInInlineBuffer, BitInt(0));
|
||||
}
|
||||
|
||||
BitVector(NoExceptConstructor, Allocator allocator = {}) noexcept : BitVector(allocator)
|
||||
|
@ -236,8 +105,8 @@ class BitVector {
|
|||
}
|
||||
else {
|
||||
/* Allocate a new array because the inline buffer is too small. */
|
||||
data_ = static_cast<IntType *>(
|
||||
allocator_.allocate(ints_to_copy * sizeof(IntType), AllocationAlignment, __func__));
|
||||
data_ = static_cast<BitInt *>(
|
||||
allocator_.allocate(ints_to_copy * sizeof(BitInt), AllocationAlignment, __func__));
|
||||
capacity_in_bits_ = ints_to_copy * BitsPerInt;
|
||||
}
|
||||
size_in_bits_ = other.size_in_bits_;
|
||||
|
@ -303,6 +172,16 @@ class BitVector {
|
|||
return move_assign_container(*this, std::move(other));
|
||||
}
|
||||
|
||||
operator BitSpan() const
|
||||
{
|
||||
return {data_, IndexRange(size_in_bits_)};
|
||||
}
|
||||
|
||||
operator MutableBitSpan()
|
||||
{
|
||||
return {data_, IndexRange(size_in_bits_)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of bits in the bit vector.
|
||||
*/
|
||||
|
@ -352,80 +231,24 @@ class BitVector {
|
|||
size_in_bits_++;
|
||||
}
|
||||
|
||||
class Iterator {
|
||||
private:
|
||||
const BitVector *vector_;
|
||||
int64_t index_;
|
||||
|
||||
public:
|
||||
Iterator(const BitVector &vector, const int64_t index) : vector_(&vector), index_(index)
|
||||
{
|
||||
}
|
||||
|
||||
Iterator &operator++()
|
||||
{
|
||||
index_++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend bool operator!=(const Iterator &a, const Iterator &b)
|
||||
{
|
||||
BLI_assert(a.vector_ == b.vector_);
|
||||
return a.index_ != b.index_;
|
||||
}
|
||||
|
||||
BitRef operator*() const
|
||||
{
|
||||
return (*vector_)[index_];
|
||||
}
|
||||
};
|
||||
|
||||
class MutableIterator {
|
||||
private:
|
||||
BitVector *vector_;
|
||||
int64_t index_;
|
||||
|
||||
public:
|
||||
MutableIterator(BitVector &vector, const int64_t index) : vector_(&vector), index_(index)
|
||||
{
|
||||
}
|
||||
|
||||
MutableIterator &operator++()
|
||||
{
|
||||
index_++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend bool operator!=(const MutableIterator &a, const MutableIterator &b)
|
||||
{
|
||||
BLI_assert(a.vector_ == b.vector_);
|
||||
return a.index_ != b.index_;
|
||||
}
|
||||
|
||||
MutableBitRef operator*() const
|
||||
{
|
||||
return (*vector_)[index_];
|
||||
}
|
||||
};
|
||||
|
||||
Iterator begin() const
|
||||
BitIterator begin() const
|
||||
{
|
||||
return {*this, 0};
|
||||
return {data_, 0};
|
||||
}
|
||||
|
||||
Iterator end() const
|
||||
BitIterator end() const
|
||||
{
|
||||
return {*this, size_in_bits_};
|
||||
return {data_, size_in_bits_};
|
||||
}
|
||||
|
||||
MutableIterator begin()
|
||||
MutableBitIterator begin()
|
||||
{
|
||||
return {*this, 0};
|
||||
return {data_, 0};
|
||||
}
|
||||
|
||||
MutableIterator end()
|
||||
MutableBitIterator end()
|
||||
{
|
||||
return {*this, size_in_bits_};
|
||||
return {data_, size_in_bits_};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -441,31 +264,8 @@ class BitVector {
|
|||
}
|
||||
size_in_bits_ = new_size_in_bits;
|
||||
if (old_size_in_bits < new_size_in_bits) {
|
||||
this->fill_range(IndexRange(old_size_in_bits, new_size_in_bits - old_size_in_bits), value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set #value for every element in #range.
|
||||
*/
|
||||
void fill_range(const IndexRange range, const bool value)
|
||||
{
|
||||
const AlignedIndexRanges aligned_ranges = split_index_range_by_alignment(range, BitsPerInt);
|
||||
|
||||
/* Fill first few bits. */
|
||||
for (const int64_t i : aligned_ranges.prefix) {
|
||||
(*this)[i].set(value);
|
||||
}
|
||||
|
||||
/* Fill entire ints at once. */
|
||||
const int64_t start_fill_int_index = aligned_ranges.aligned.start() / BitsPerInt;
|
||||
const int64_t ints_to_fill = aligned_ranges.aligned.size() / BitsPerInt;
|
||||
const IntType fill_value = value ? IntType(-1) : IntType(0);
|
||||
initialized_fill_n(data_ + start_fill_int_index, ints_to_fill, fill_value);
|
||||
|
||||
/* Fill bits in the end that don't cover a full int. */
|
||||
for (const int64_t i : aligned_ranges.suffix) {
|
||||
(*this)[i].set(value);
|
||||
MutableBitSpan(data_, IndexRange(old_size_in_bits, new_size_in_bits - old_size_in_bits))
|
||||
.set_all(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -474,7 +274,7 @@ class BitVector {
|
|||
*/
|
||||
void fill(const bool value)
|
||||
{
|
||||
this->fill_range(IndexRange(0, size_in_bits_), value);
|
||||
MutableBitSpan(data_, size_in_bits_).set_all(value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -517,7 +317,7 @@ class BitVector {
|
|||
}
|
||||
|
||||
BLI_NOINLINE void realloc_to_at_least(const int64_t min_capacity_in_bits,
|
||||
const IntType initial_value_for_new_ints = 0x00)
|
||||
const BitInt initial_value_for_new_ints = 0)
|
||||
{
|
||||
if (capacity_in_bits_ >= min_capacity_in_bits) {
|
||||
return;
|
||||
|
@ -531,8 +331,8 @@ class BitVector {
|
|||
const int64_t new_capacity_in_ints = std::max(min_capacity_in_ints, min_new_capacity_in_ints);
|
||||
const int64_t ints_to_copy = this->used_ints_amount();
|
||||
|
||||
IntType *new_data = static_cast<IntType *>(allocator_.allocate(
|
||||
new_capacity_in_ints * sizeof(IntType), AllocationAlignment, __func__));
|
||||
BitInt *new_data = static_cast<BitInt *>(
|
||||
allocator_.allocate(new_capacity_in_ints * sizeof(BitInt), AllocationAlignment, __func__));
|
||||
uninitialized_copy_n(data_, ints_to_copy, new_data);
|
||||
/* Always initialize new capacity even if it isn't used yet. That's necessary to avoid warnings
|
||||
* caused by using uninitialized memory. This happens when e.g. setting a clearing a bit in an
|
||||
|
@ -562,7 +362,5 @@ class BitVector {
|
|||
} // namespace blender::bits
|
||||
|
||||
namespace blender {
|
||||
using bits::BitRef;
|
||||
using bits::BitVector;
|
||||
using bits::MutableBitRef;
|
||||
} // namespace blender
|
||||
|
|
|
@ -142,6 +142,18 @@ double BLI_dir_free_space(const char *dir) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(
|
|||
*/
|
||||
char *BLI_current_working_dir(char *dir, size_t maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
eFileAttributes BLI_file_attributes(const char *path);
|
||||
/**
|
||||
* Changes the current working directory to the provided path.
|
||||
*
|
||||
* Usage of this function is strongly discouraged as it is not thread safe. It will likely cause
|
||||
* issues if there is an operation on another thread that does not expect the current working
|
||||
* directory to change. This has been added to support USDZ export, which has a problematic
|
||||
* "feature" described in this issue https://projects.blender.org/blender/blender/issues/99807. It
|
||||
* will be removed if it is possible to resolve that issue upstream in the USD library.
|
||||
*
|
||||
* \return true on success, false otherwise.
|
||||
*/
|
||||
bool BLI_change_working_dir(const char *dir);
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -304,6 +304,13 @@ void rotate_eul(float beul[3], char axis, float angle);
|
|||
|
||||
/* Order independent. */
|
||||
|
||||
/**
|
||||
* Manipulate `eul` so it's close to `oldrot` while representing the same rotation
|
||||
* with the aim of having the minimum difference between all axes.
|
||||
*
|
||||
* This is typically done so interpolating the values between two euler rotations
|
||||
* doesn't add undesired rotation (even rotating multiple times around one axis).
|
||||
*/
|
||||
void compatible_eul(float eul[3], const float oldrot[3]);
|
||||
|
||||
void add_eul_euleul(float r_eul[3], float a[3], float b[3], short order);
|
||||
|
|
|
@ -457,6 +457,8 @@ if(WITH_GTESTS)
|
|||
tests/BLI_array_store_test.cc
|
||||
tests/BLI_array_test.cc
|
||||
tests/BLI_array_utils_test.cc
|
||||
tests/BLI_bit_ref_test.cc
|
||||
tests/BLI_bit_span_test.cc
|
||||
tests/BLI_bit_vector_test.cc
|
||||
tests/BLI_bitmap_test.cc
|
||||
tests/BLI_bounds_test.cc
|
||||
|
|
|
@ -1490,15 +1490,18 @@ void rotate_eul(float beul[3], const char axis, const float angle)
|
|||
|
||||
void compatible_eul(float eul[3], const float oldrot[3])
|
||||
{
|
||||
/* we could use M_PI as pi_thresh: which is correct but 5.1 gives better results.
|
||||
* Checked with baking actions to fcurves - campbell */
|
||||
const float pi_thresh = (5.1f);
|
||||
/* When the rotation exceeds 180 degrees, it can be wrapped by 360 degrees
|
||||
* to produce a closer match.
|
||||
* NOTE: Values between `pi` & `2 * pi` work, where `pi` has the lowest number of
|
||||
* discontinuities and values approaching `2 * pi` center the resulting rotation around zero,
|
||||
* at the expense of the result being less compatible, see !104856. */
|
||||
const float pi_thresh = (float)M_PI;
|
||||
const float pi_x2 = (2.0f * (float)M_PI);
|
||||
|
||||
float deul[3];
|
||||
uint i;
|
||||
|
||||
/* correct differences of about 360 degrees first */
|
||||
/* Correct differences around 360 degrees first. */
|
||||
for (i = 0; i < 3; i++) {
|
||||
deul[i] = eul[i] - oldrot[i];
|
||||
if (deul[i] > pi_thresh) {
|
||||
|
@ -1511,29 +1514,17 @@ void compatible_eul(float eul[3], const float oldrot[3])
|
|||
}
|
||||
}
|
||||
|
||||
/* is 1 of the axis rotations larger than 180 degrees and the other small? NO ELSE IF!! */
|
||||
if (fabsf(deul[0]) > 3.2f && fabsf(deul[1]) < 1.6f && fabsf(deul[2]) < 1.6f) {
|
||||
if (deul[0] > 0.0f) {
|
||||
eul[0] -= pi_x2;
|
||||
}
|
||||
else {
|
||||
eul[0] += pi_x2;
|
||||
}
|
||||
}
|
||||
if (fabsf(deul[1]) > 3.2f && fabsf(deul[2]) < 1.6f && fabsf(deul[0]) < 1.6f) {
|
||||
if (deul[1] > 0.0f) {
|
||||
eul[1] -= pi_x2;
|
||||
}
|
||||
else {
|
||||
eul[1] += pi_x2;
|
||||
}
|
||||
}
|
||||
if (fabsf(deul[2]) > 3.2f && fabsf(deul[0]) < 1.6f && fabsf(deul[1]) < 1.6f) {
|
||||
if (deul[2] > 0.0f) {
|
||||
eul[2] -= pi_x2;
|
||||
}
|
||||
else {
|
||||
eul[2] += pi_x2;
|
||||
uint j = 1, k = 2;
|
||||
for (i = 0; i < 3; j = k, k = i++) {
|
||||
/* Check if this axis of rotations larger than 180 degrees and
|
||||
* the others are smaller than 90 degrees. */
|
||||
if (fabsf(deul[i]) > M_PI && fabsf(deul[j]) < M_PI_2 && fabsf(deul[k]) < M_PI_2) {
|
||||
if (deul[i] > 0.0f) {
|
||||
eul[i] -= pi_x2;
|
||||
}
|
||||
else {
|
||||
eul[i] += pi_x2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,11 +54,36 @@
|
|||
#include "BLI_linklist.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_threads.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
/* The implementation for Apple lives in storage_apple.mm.*/
|
||||
bool BLI_change_working_dir(const char *dir)
|
||||
{
|
||||
BLI_assert(BLI_thread_is_main());
|
||||
|
||||
if (!BLI_is_dir(dir)) {
|
||||
return false;
|
||||
}
|
||||
# if defined(WIN32)
|
||||
wchar_t wdir[FILE_MAX];
|
||||
if (conv_utf_8_to_16(dir, wdir, ARRAY_SIZE(wdir)) != 0) {
|
||||
return false;
|
||||
}
|
||||
return _wchdir(wdir) == 0;
|
||||
# else
|
||||
int result = chdir(dir);
|
||||
if (result == 0) {
|
||||
BLI_setenv("PWD", dir);
|
||||
}
|
||||
return result == 0;
|
||||
# endif
|
||||
}
|
||||
|
||||
char *BLI_current_working_dir(char *dir, const size_t maxncpy)
|
||||
{
|
||||
#if defined(WIN32)
|
||||
# if defined(WIN32)
|
||||
wchar_t path[MAX_PATH];
|
||||
if (_wgetcwd(path, MAX_PATH)) {
|
||||
if (BLI_strncpy_wchar_as_utf8(dir, path, maxncpy) != maxncpy) {
|
||||
|
@ -66,7 +91,7 @@ char *BLI_current_working_dir(char *dir, const size_t maxncpy)
|
|||
}
|
||||
}
|
||||
return NULL;
|
||||
#else
|
||||
# else
|
||||
const char *pwd = BLI_getenv("PWD");
|
||||
if (pwd) {
|
||||
size_t srclen = BLI_strnlen(pwd, maxncpy);
|
||||
|
@ -77,8 +102,9 @@ char *BLI_current_working_dir(char *dir, const size_t maxncpy)
|
|||
return NULL;
|
||||
}
|
||||
return getcwd(dir, maxncpy);
|
||||
#endif
|
||||
# endif
|
||||
}
|
||||
#endif /* !defined (__APPLE__) */
|
||||
|
||||
double BLI_dir_free_space(const char *dir)
|
||||
{
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
#include "BLI_fileops.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
|
||||
/* Extended file attribute used by OneDrive to mark placeholder files. */
|
||||
static const char *ONEDRIVE_RECALLONOPEN_ATTRIBUTE = "com.microsoft.OneDrive.RecallOnOpen";
|
||||
|
@ -185,3 +187,27 @@ const char *BLI_expand_tilde(const char *path_with_tilde)
|
|||
}
|
||||
return path_expanded;
|
||||
}
|
||||
|
||||
char *BLI_current_working_dir(char *dir, const size_t maxncpy) {
|
||||
/* Can't just copy to the *dir pointer, as [path getCString gets grumpy.*/
|
||||
static char path_expanded[PATH_MAX];
|
||||
@autoreleasepool {
|
||||
NSString *path = [[NSFileManager defaultManager] currentDirectoryPath];
|
||||
const size_t length = maxncpy > PATH_MAX ? PATH_MAX : maxncpy;
|
||||
[path getCString:path_expanded maxLength:length encoding:NSUTF8StringEncoding];
|
||||
BLI_strncpy(dir, path_expanded, maxncpy);
|
||||
return path_expanded;
|
||||
}
|
||||
}
|
||||
|
||||
bool BLI_change_working_dir(const char* dir) {
|
||||
@autoreleasepool {
|
||||
NSString* path = [[NSString alloc] initWithUTF8String: dir];
|
||||
if ([[NSFileManager defaultManager] changeCurrentDirectoryPath: path] == YES) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "BLI_bit_ref.hh"
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
namespace blender::bits::tests {
|
||||
|
||||
TEST(bit_ref, MaskFirstNBits)
|
||||
{
|
||||
EXPECT_EQ(mask_first_n_bits(0), 0);
|
||||
EXPECT_EQ(mask_first_n_bits(1),
|
||||
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001);
|
||||
EXPECT_EQ(mask_first_n_bits(5),
|
||||
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001'1111);
|
||||
EXPECT_EQ(mask_first_n_bits(63),
|
||||
0b0111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111);
|
||||
EXPECT_EQ(mask_first_n_bits(64),
|
||||
0b1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111);
|
||||
}
|
||||
|
||||
TEST(bit_ref, MaskLastNBits)
|
||||
{
|
||||
EXPECT_EQ(mask_last_n_bits(0),
|
||||
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
|
||||
EXPECT_EQ(mask_last_n_bits(1),
|
||||
0b1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
|
||||
EXPECT_EQ(mask_last_n_bits(5),
|
||||
0b1111'1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
|
||||
EXPECT_EQ(mask_last_n_bits(63),
|
||||
0b1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1110);
|
||||
EXPECT_EQ(mask_last_n_bits(64),
|
||||
0b1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111);
|
||||
}
|
||||
|
||||
TEST(bit_ref, MaskSingleBit)
|
||||
{
|
||||
EXPECT_EQ(mask_single_bit(0), 1);
|
||||
EXPECT_EQ(mask_single_bit(1),
|
||||
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0010);
|
||||
EXPECT_EQ(mask_single_bit(5),
|
||||
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0010'0000);
|
||||
EXPECT_EQ(mask_single_bit(63),
|
||||
0b1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
|
||||
}
|
||||
|
||||
TEST(bit_ref, IntContainingBit)
|
||||
{
|
||||
std::array<uint64_t, 5> array;
|
||||
uint64_t *data = array.data();
|
||||
EXPECT_EQ(int_containing_bit(data, 0), data);
|
||||
EXPECT_EQ(int_containing_bit(data, 1), data);
|
||||
EXPECT_EQ(int_containing_bit(data, 63), data);
|
||||
EXPECT_EQ(int_containing_bit(data, 64), data + 1);
|
||||
EXPECT_EQ(int_containing_bit(data, 65), data + 1);
|
||||
EXPECT_EQ(int_containing_bit(data, 100), data + 1);
|
||||
EXPECT_EQ(int_containing_bit(data, 127), data + 1);
|
||||
EXPECT_EQ(int_containing_bit(data, 128), data + 2);
|
||||
const uint64_t *data_const = data;
|
||||
EXPECT_EQ(int_containing_bit(data_const, 0), data_const);
|
||||
EXPECT_EQ(int_containing_bit(data_const, 1), data_const);
|
||||
EXPECT_EQ(int_containing_bit(data_const, 63), data_const);
|
||||
EXPECT_EQ(int_containing_bit(data_const, 64), data_const + 1);
|
||||
EXPECT_EQ(int_containing_bit(data_const, 65), data_const + 1);
|
||||
EXPECT_EQ(int_containing_bit(data_const, 100), data_const + 1);
|
||||
EXPECT_EQ(int_containing_bit(data_const, 127), data_const + 1);
|
||||
EXPECT_EQ(int_containing_bit(data_const, 128), data_const + 2);
|
||||
}
|
||||
|
||||
TEST(bit_ref, Test)
|
||||
{
|
||||
uint64_t data = (1 << 3) | (1 << 7);
|
||||
EXPECT_FALSE(BitRef(&data, 0).test());
|
||||
EXPECT_FALSE(BitRef(&data, 1).test());
|
||||
EXPECT_FALSE(BitRef(&data, 2).test());
|
||||
EXPECT_TRUE(BitRef(&data, 3).test());
|
||||
EXPECT_FALSE(BitRef(&data, 4));
|
||||
EXPECT_FALSE(BitRef(&data, 5));
|
||||
EXPECT_FALSE(BitRef(&data, 6));
|
||||
EXPECT_TRUE(BitRef(&data, 7));
|
||||
|
||||
EXPECT_FALSE(MutableBitRef(&data, 0).test());
|
||||
EXPECT_FALSE(MutableBitRef(&data, 1).test());
|
||||
EXPECT_FALSE(MutableBitRef(&data, 2).test());
|
||||
EXPECT_TRUE(MutableBitRef(&data, 3).test());
|
||||
EXPECT_FALSE(MutableBitRef(&data, 4));
|
||||
EXPECT_FALSE(MutableBitRef(&data, 5));
|
||||
EXPECT_FALSE(MutableBitRef(&data, 6));
|
||||
EXPECT_TRUE(MutableBitRef(&data, 7));
|
||||
}
|
||||
|
||||
TEST(bit_ref, Set)
|
||||
{
|
||||
uint64_t data = 0;
|
||||
MutableBitRef(&data, 0).set();
|
||||
MutableBitRef(&data, 1).set();
|
||||
MutableBitRef(&data, 1).set();
|
||||
MutableBitRef(&data, 4).set();
|
||||
EXPECT_EQ(data, (1 << 0) | (1 << 1) | (1 << 4));
|
||||
MutableBitRef(&data, 5).set(true);
|
||||
MutableBitRef(&data, 1).set(false);
|
||||
EXPECT_EQ(data, (1 << 0) | (1 << 4) | (1 << 5));
|
||||
}
|
||||
|
||||
TEST(bit_ref, Reset)
|
||||
{
|
||||
uint64_t data = -1;
|
||||
MutableBitRef(&data, 0).reset();
|
||||
MutableBitRef(&data, 2).reset();
|
||||
EXPECT_EQ(data, uint64_t(-1) & ~(1 << 0) & ~(1 << 2));
|
||||
}
|
||||
|
||||
TEST(bit_ref, SetBranchless)
|
||||
{
|
||||
uint64_t data = 0;
|
||||
MutableBitRef(&data, 0).set_branchless(true);
|
||||
EXPECT_EQ(data, 1);
|
||||
MutableBitRef(&data, 0).set_branchless(false);
|
||||
EXPECT_EQ(data, 0);
|
||||
MutableBitRef(&data, 3).set_branchless(false);
|
||||
MutableBitRef(&data, 4).set_branchless(true);
|
||||
EXPECT_EQ(data, 16);
|
||||
MutableBitRef(&data, 3).set_branchless(true);
|
||||
MutableBitRef(&data, 4).set_branchless(true);
|
||||
EXPECT_EQ(data, 24);
|
||||
}
|
||||
|
||||
TEST(bit_ref, Cast)
|
||||
{
|
||||
uint64_t data = 0;
|
||||
MutableBitRef mutable_ref(&data, 3);
|
||||
BitRef ref = mutable_ref;
|
||||
EXPECT_FALSE(ref);
|
||||
mutable_ref.set();
|
||||
EXPECT_TRUE(ref);
|
||||
}
|
||||
|
||||
TEST(bit_ref, MaskRangeBits)
|
||||
{
|
||||
EXPECT_EQ(mask_range_bits(0, 0),
|
||||
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
|
||||
EXPECT_EQ(mask_range_bits(0, 1),
|
||||
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001);
|
||||
EXPECT_EQ(mask_range_bits(0, 5),
|
||||
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001'1111);
|
||||
EXPECT_EQ(mask_range_bits(64, 0),
|
||||
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
|
||||
EXPECT_EQ(mask_range_bits(63, 1),
|
||||
0b1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
|
||||
EXPECT_EQ(mask_range_bits(59, 5),
|
||||
0b1111'1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
|
||||
EXPECT_EQ(mask_range_bits(8, 3),
|
||||
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0111'0000'0000);
|
||||
EXPECT_EQ(mask_range_bits(0, 64),
|
||||
0b1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111);
|
||||
}
|
||||
|
||||
} // namespace blender::bits::tests
|
|
@ -0,0 +1,139 @@
|
|||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "BLI_bit_span.hh"
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
namespace blender::bits::tests {
|
||||
|
||||
TEST(bit_span, DefaultConstructor)
|
||||
{
|
||||
{
|
||||
char buffer[sizeof(BitSpan)];
|
||||
memset(buffer, 0xff, sizeof(BitSpan));
|
||||
BitSpan &span = *new (buffer) BitSpan();
|
||||
EXPECT_TRUE(span.is_empty());
|
||||
EXPECT_EQ(span.size(), 0);
|
||||
}
|
||||
{
|
||||
char buffer[sizeof(MutableBitSpan)];
|
||||
memset(buffer, 0xff, sizeof(MutableBitSpan));
|
||||
MutableBitSpan &span = *new (buffer) MutableBitSpan();
|
||||
EXPECT_TRUE(span.is_empty());
|
||||
EXPECT_EQ(span.size(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(bit_span, Iteration)
|
||||
{
|
||||
uint64_t data = (1 << 2) | (1 << 3);
|
||||
const BitSpan span(&data, 30);
|
||||
EXPECT_EQ(span.size(), 30);
|
||||
int index = 0;
|
||||
for (const BitRef bit : span) {
|
||||
EXPECT_EQ(bit.test(), ELEM(index, 2, 3));
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(bit_span, MutableIteration)
|
||||
{
|
||||
uint64_t data = 0;
|
||||
MutableBitSpan span(&data, 40);
|
||||
EXPECT_EQ(span.size(), 40);
|
||||
int index = 0;
|
||||
for (MutableBitRef bit : span) {
|
||||
bit.set(index % 4 == 0);
|
||||
index++;
|
||||
}
|
||||
EXPECT_EQ(data,
|
||||
0b0000'0000'0000'0000'0000'0000'0001'0001'0001'0001'0001'0001'0001'0001'0001'0001);
|
||||
}
|
||||
|
||||
TEST(bit_span, SubscriptOperator)
|
||||
{
|
||||
uint64_t data[2] = {0, 0};
|
||||
MutableBitSpan mutable_span(data, 128);
|
||||
BitSpan span = mutable_span;
|
||||
|
||||
EXPECT_EQ(mutable_span.data(), data);
|
||||
EXPECT_EQ(mutable_span.bit_range(), IndexRange(128));
|
||||
EXPECT_EQ(span.data(), data);
|
||||
EXPECT_EQ(span.bit_range(), IndexRange(128));
|
||||
|
||||
EXPECT_FALSE(mutable_span[5].test());
|
||||
EXPECT_FALSE(span[5].test());
|
||||
mutable_span[5].set(5);
|
||||
EXPECT_TRUE(mutable_span[5].test());
|
||||
EXPECT_TRUE(span[5].test());
|
||||
|
||||
EXPECT_FALSE(mutable_span[120].test());
|
||||
EXPECT_FALSE(span[120].test());
|
||||
mutable_span[120].set(120);
|
||||
EXPECT_TRUE(mutable_span[120].test());
|
||||
EXPECT_TRUE(span[120].test());
|
||||
|
||||
EXPECT_EQ(data[0],
|
||||
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0010'0000);
|
||||
EXPECT_EQ(data[1],
|
||||
0b0000'0001'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
|
||||
}
|
||||
|
||||
TEST(bit_span, RangeConstructor)
|
||||
{
|
||||
uint64_t data = 0;
|
||||
MutableBitSpan mutable_span(&data, IndexRange(4, 3));
|
||||
BitSpan span = mutable_span;
|
||||
|
||||
EXPECT_FALSE(mutable_span[1].test());
|
||||
EXPECT_FALSE(span[1].test());
|
||||
mutable_span[0].set(true);
|
||||
mutable_span[1].set(true);
|
||||
mutable_span[2].set(true);
|
||||
mutable_span[0].set(false);
|
||||
mutable_span[2].set(false);
|
||||
EXPECT_TRUE(mutable_span[1].test());
|
||||
EXPECT_TRUE(span[1].test());
|
||||
|
||||
EXPECT_EQ(data,
|
||||
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0010'0000);
|
||||
}
|
||||
|
||||
TEST(bit_span, Set)
|
||||
{
|
||||
uint64_t data = 0;
|
||||
MutableBitSpan(&data, 64).set_all(true);
|
||||
EXPECT_EQ(data, uint64_t(-1));
|
||||
MutableBitSpan(&data, 64).set_all(false);
|
||||
EXPECT_EQ(data, uint64_t(0));
|
||||
|
||||
MutableBitSpan(&data, IndexRange(4, 8)).set_all(true);
|
||||
EXPECT_EQ(data,
|
||||
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'1111'1111'0000);
|
||||
MutableBitSpan(&data, IndexRange(8, 30)).set_all(false);
|
||||
|
||||
EXPECT_EQ(data,
|
||||
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'1111'0000);
|
||||
}
|
||||
|
||||
TEST(bit_span, SetSliced)
|
||||
{
|
||||
std::array<uint64_t, 10> data;
|
||||
memset(data.data(), 0, sizeof(data));
|
||||
MutableBitSpan span{data.data(), 640};
|
||||
span.slice(IndexRange(5, 500)).set_all(true);
|
||||
|
||||
for (const int64_t i : IndexRange(640)) {
|
||||
EXPECT_EQ(span[i], i >= 5 && i < 505);
|
||||
}
|
||||
|
||||
span.slice(IndexRange(10, 190)).set_all(false);
|
||||
|
||||
for (const int64_t i : IndexRange(640)) {
|
||||
EXPECT_EQ(span[i], (i >= 5 && i < 10) || (i >= 200 && i < 505));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::bits::tests
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#include "testing/testing.h"
|
||||
|
||||
namespace blender::tests {
|
||||
namespace blender::bits::tests {
|
||||
|
||||
TEST(bit_vector, DefaultConstructor)
|
||||
{
|
||||
|
@ -183,4 +183,4 @@ TEST(bit_vector, AppendMany)
|
|||
EXPECT_TRUE(vec[5]);
|
||||
}
|
||||
|
||||
} // namespace blender::tests
|
||||
} // namespace blender::bits::tests
|
||||
|
|
|
@ -1,11 +1,27 @@
|
|||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "BLI_fileops.hh"
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "BLI_fileops.hh"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_threads.h"
|
||||
|
||||
namespace blender::tests {
|
||||
|
||||
class ChangeWorkingDirectoryTest : public testing::Test {
|
||||
public:
|
||||
std::string test_temp_dir;
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
if (!test_temp_dir.empty()) {
|
||||
BLI_delete(test_temp_dir.c_str(), true, false);
|
||||
}
|
||||
|
||||
BLI_threadapi_exit();
|
||||
}
|
||||
};
|
||||
|
||||
TEST(fileops, fstream_open_string_filename)
|
||||
{
|
||||
const std::string test_files_dir = blender::tests::flags_test_asset_dir();
|
||||
|
@ -37,4 +53,43 @@ TEST(fileops, fstream_open_charptr_filename)
|
|||
/* Reading the file not tested here. That's deferred to `std::fstream` anyway. */
|
||||
}
|
||||
|
||||
TEST_F(ChangeWorkingDirectoryTest, change_working_directory)
|
||||
{
|
||||
/* Must use because BLI_change_working_dir() checks that we are on the main thread. */
|
||||
BLI_threadapi_init();
|
||||
|
||||
char original_wd[FILE_MAX];
|
||||
if (!BLI_current_working_dir(original_wd, FILE_MAX)) {
|
||||
FAIL() << "unable to get the current working directory";
|
||||
}
|
||||
|
||||
const std::string temp_file_name(std::tmpnam(nullptr));
|
||||
test_temp_dir = temp_file_name + "_новый";
|
||||
|
||||
if (BLI_exists(test_temp_dir.c_str())) {
|
||||
BLI_delete(test_temp_dir.c_str(), true, false);
|
||||
}
|
||||
|
||||
ASSERT_FALSE(BLI_change_working_dir(test_temp_dir.c_str()))
|
||||
<< "changing directory to a non-existent directory is expected to fail.";
|
||||
|
||||
ASSERT_TRUE(BLI_dir_create_recursive(test_temp_dir.c_str()))
|
||||
<< "temporary directory should have been created successfully.";
|
||||
|
||||
ASSERT_TRUE(BLI_change_working_dir(test_temp_dir.c_str()))
|
||||
<< "temporary directory should succeed changing directory.";
|
||||
|
||||
char cwd[FILE_MAX];
|
||||
if (!BLI_current_working_dir(cwd, FILE_MAX)) {
|
||||
FAIL() << "unable to get the current working directory";
|
||||
}
|
||||
|
||||
ASSERT_EQ(BLI_path_cmp_normalized(cwd, test_temp_dir.c_str()), 0)
|
||||
<< "the path of the current working directory should equal the path of the temporary "
|
||||
"directory that was created.";
|
||||
|
||||
ASSERT_TRUE(BLI_change_working_dir(original_wd))
|
||||
<< "changing directory back to the original working directory should succeed.";
|
||||
}
|
||||
|
||||
} // namespace blender::tests
|
||||
|
|
|
@ -290,6 +290,24 @@ TEST(index_range, SplitByAlignment)
|
|||
EXPECT_EQ(ranges.aligned, IndexRange());
|
||||
EXPECT_EQ(ranges.suffix, IndexRange());
|
||||
}
|
||||
{
|
||||
AlignedIndexRanges ranges = split_index_range_by_alignment(IndexRange(64), 64);
|
||||
EXPECT_EQ(ranges.prefix, IndexRange());
|
||||
EXPECT_EQ(ranges.aligned, IndexRange(64));
|
||||
EXPECT_EQ(ranges.suffix, IndexRange());
|
||||
}
|
||||
{
|
||||
AlignedIndexRanges ranges = split_index_range_by_alignment(IndexRange(64, 64), 64);
|
||||
EXPECT_EQ(ranges.prefix, IndexRange());
|
||||
EXPECT_EQ(ranges.aligned, IndexRange(64, 64));
|
||||
EXPECT_EQ(ranges.suffix, IndexRange());
|
||||
}
|
||||
{
|
||||
AlignedIndexRanges ranges = split_index_range_by_alignment(IndexRange(4, 8), 64);
|
||||
EXPECT_EQ(ranges.prefix, IndexRange(4, 8));
|
||||
EXPECT_EQ(ranges.aligned, IndexRange());
|
||||
EXPECT_EQ(ranges.suffix, IndexRange());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::tests
|
||||
|
|
|
@ -770,7 +770,11 @@ void blo_do_versions_userdef(UserDef *userdef)
|
|||
|
||||
/* Set GPU backend to OpenGL. */
|
||||
if (!USER_VERSION_ATLEAST(305, 5)) {
|
||||
#ifdef __APPLE__
|
||||
userdef->gpu_backend = GPU_BACKEND_METAL;
|
||||
#else
|
||||
userdef->gpu_backend = GPU_BACKEND_OPENGL;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!USER_VERSION_ATLEAST(305, 10)) {
|
||||
|
|
|
@ -126,6 +126,10 @@ const char *BLT_translate_do_new_dataname(const char *msgctxt, const char *msgid
|
|||
#define BLT_I18NCONTEXT_EDITOR_VIEW3D "View3D"
|
||||
#define BLT_I18NCONTEXT_EDITOR_FILEBROWSER "File browser"
|
||||
|
||||
/* Generic contexts. */
|
||||
#define BLT_I18NCONTEXT_VIRTUAL_REALITY "Virtual reality"
|
||||
#define BLT_I18NCONTEXT_CONSTRAINT "Constraint"
|
||||
|
||||
/* Helper for bpy.app.i18n object... */
|
||||
typedef struct {
|
||||
const char *c_id;
|
||||
|
@ -190,6 +194,8 @@ typedef struct {
|
|||
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "id_windowmanager"), \
|
||||
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_EDITOR_VIEW3D, "editor_view3d"), \
|
||||
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_EDITOR_FILEBROWSER, "editor_filebrowser"), \
|
||||
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_VIRTUAL_REALITY, "virtual_reality"), \
|
||||
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_CONSTRAINT, "constraint"), \
|
||||
{ \
|
||||
NULL, NULL, NULL \
|
||||
} \
|
||||
|
|
|
@ -211,7 +211,7 @@ void *BMO_iter_as_arrayN(BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
|
|||
|
||||
int BM_iter_mesh_bitmap_from_filter(const char itype,
|
||||
BMesh *bm,
|
||||
blender::BitVector<> &bitmap,
|
||||
blender::MutableBitSpan bitmap,
|
||||
bool (*test_fn)(BMElem *, void *user_data),
|
||||
void *user_data)
|
||||
{
|
||||
|
@ -234,7 +234,7 @@ int BM_iter_mesh_bitmap_from_filter(const char itype,
|
|||
}
|
||||
|
||||
int BM_iter_mesh_bitmap_from_filter_tessface(BMesh *bm,
|
||||
blender::BitVector<> &bitmap,
|
||||
blender::MutableBitSpan bitmap,
|
||||
bool (*test_fn)(BMFace *, void *user_data),
|
||||
void *user_data)
|
||||
{
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include "BLI_mempool.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
# include "BLI_bit_vector.hh"
|
||||
# include "BLI_bit_span.hh"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -228,14 +228,14 @@ void *BMO_iter_as_arrayN(BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
|
|||
|
||||
int BM_iter_mesh_bitmap_from_filter(char itype,
|
||||
BMesh *bm,
|
||||
blender::BitVector<> &bitmap,
|
||||
blender::MutableBitSpan bitmap,
|
||||
bool (*test_fn)(BMElem *, void *user_data),
|
||||
void *user_data);
|
||||
/**
|
||||
* Needed when we want to check faces, but return a loop aligned array.
|
||||
*/
|
||||
int BM_iter_mesh_bitmap_from_filter_tessface(BMesh *bm,
|
||||
blender::BitVector<> &bitmap,
|
||||
blender::MutableBitSpan bitmap,
|
||||
bool (*test_fn)(BMFace *, void *user_data),
|
||||
void *user_data);
|
||||
|
||||
|
|
|
@ -318,11 +318,6 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
|
|||
CustomData_bmesh_merge(&mesh_ldata, &bm->ldata, mask.lmask, CD_SET_DEFAULT, bm, BM_LOOP);
|
||||
}
|
||||
|
||||
const Vector<MeshToBMeshLayerInfo> vert_info = mesh_to_bm_copy_info_calc(mesh_vdata, bm->vdata);
|
||||
const Vector<MeshToBMeshLayerInfo> edge_info = mesh_to_bm_copy_info_calc(mesh_edata, bm->edata);
|
||||
const Vector<MeshToBMeshLayerInfo> poly_info = mesh_to_bm_copy_info_calc(mesh_pdata, bm->pdata);
|
||||
const Vector<MeshToBMeshLayerInfo> loop_info = mesh_to_bm_copy_info_calc(mesh_ldata, bm->ldata);
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Shape Key */
|
||||
int tot_shape_keys = 0;
|
||||
|
@ -407,6 +402,10 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
|
|||
}
|
||||
}
|
||||
|
||||
const Vector<MeshToBMeshLayerInfo> vert_info = mesh_to_bm_copy_info_calc(mesh_vdata, bm->vdata);
|
||||
const Vector<MeshToBMeshLayerInfo> edge_info = mesh_to_bm_copy_info_calc(mesh_edata, bm->edata);
|
||||
const Vector<MeshToBMeshLayerInfo> poly_info = mesh_to_bm_copy_info_calc(mesh_pdata, bm->pdata);
|
||||
const Vector<MeshToBMeshLayerInfo> loop_info = mesh_to_bm_copy_info_calc(mesh_ldata, bm->ldata);
|
||||
if (is_new) {
|
||||
CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT);
|
||||
CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE);
|
||||
|
|
|
@ -338,16 +338,12 @@ class ShadowPunctual : public NonCopyable, NonMovable {
|
|||
float size_x_, size_y_;
|
||||
/** Shape type. */
|
||||
eLightType light_type_;
|
||||
/** Random position on the light. In world space. */
|
||||
float3 random_offset_;
|
||||
/** Light position. */
|
||||
float3 position_;
|
||||
/** Near and far clip distances. */
|
||||
float far_, near_;
|
||||
/** Number of tile-maps needed to cover the light angular extents. */
|
||||
int tilemaps_needed_;
|
||||
/** Visibility cone angle from the light source. */
|
||||
int cone_aperture_;
|
||||
|
||||
public:
|
||||
ShadowPunctual(ShadowModule &module) : shadows_(module){};
|
||||
|
|
|
@ -59,9 +59,9 @@ void main()
|
|||
* IF PrimType == LineList: base_vertex_id = quad_id*2
|
||||
* IF PrimType == LineStrip: base_vertex_id = quad_id
|
||||
*
|
||||
* Note: This is currently used as LineList.
|
||||
* NOTE: This is currently used as LineList.
|
||||
*
|
||||
* Note: Primitive Restart Will not work with this setup as-is. We should avoid using
|
||||
* NOTE: Primitive Restart Will not work with this setup as-is. We should avoid using
|
||||
* input primitive types which use restart indices. */
|
||||
int base_vertex_id = quad_id * 2;
|
||||
|
||||
|
|
|
@ -557,7 +557,7 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr,
|
|||
const bool mp_select = (efa) ? BM_elem_flag_test_bool(efa, BM_ELEM_SELECT) : false;
|
||||
|
||||
if (mr->use_subsurf_fdots) {
|
||||
const BitVector<> &facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
|
||||
const BitSpan facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
|
||||
|
||||
const MLoop *mloop = mr->mloop;
|
||||
const int ml_index_end = mp->loopstart + mp->totloop;
|
||||
|
|
|
@ -46,7 +46,7 @@ static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr,
|
|||
|
||||
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
|
||||
if (mr->use_subsurf_fdots) {
|
||||
const BitVector<> &facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
|
||||
const BitSpan facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
|
||||
|
||||
const MLoop *mloop = mr->mloop;
|
||||
const int ml_index_end = mp->loopstart + mp->totloop;
|
||||
|
|
|
@ -102,7 +102,7 @@ static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr,
|
|||
void *_data)
|
||||
{
|
||||
MeshExtract_EdgeFac_Data *data = static_cast<MeshExtract_EdgeFac_Data *>(_data);
|
||||
const BitVector<> &optimal_display_edges = mr->me->runtime->subsurf_optimal_display_edges;
|
||||
const BitSpan optimal_display_edges = mr->me->runtime->subsurf_optimal_display_edges;
|
||||
|
||||
const MLoop *mloop = mr->mloop;
|
||||
const int ml_index_end = mp->loopstart + mp->totloop;
|
||||
|
|
|
@ -76,7 +76,7 @@ static void extract_fdots_pos_iter_poly_mesh(const MeshRenderData *mr,
|
|||
zero_v3(co);
|
||||
|
||||
const MLoop *mloop = mr->mloop;
|
||||
const BitVector<> &facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
|
||||
const BitSpan facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
|
||||
|
||||
const int ml_index_end = mp->loopstart + mp->totloop;
|
||||
for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
|
||||
|
|
|
@ -74,7 +74,7 @@ static void extract_fdots_uv_iter_poly_mesh(const MeshRenderData *mr,
|
|||
void *_data)
|
||||
{
|
||||
MeshExtract_FdotUV_Data *data = static_cast<MeshExtract_FdotUV_Data *>(_data);
|
||||
const BitVector<> &facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
|
||||
const BitSpan facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
|
||||
|
||||
const MLoop *mloop = mr->mloop;
|
||||
const int ml_index_end = mp->loopstart + mp->totloop;
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "ED_anim_api.h"
|
||||
#include "ED_armature.h"
|
||||
#include "ED_keyframes_edit.h" /* XXX move the select modes out of there! */
|
||||
#include "ED_markers.h"
|
||||
#include "ED_object.h"
|
||||
#include "ED_screen.h"
|
||||
#include "ED_select_utils.h"
|
||||
|
@ -3638,6 +3639,283 @@ static void ANIM_OT_channel_select_keys(wmOperatorType *ot)
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name View Channel Operator
|
||||
* \{ */
|
||||
|
||||
static void get_normalized_fcurve_bounds(FCurve *fcu,
|
||||
bAnimContext *ac,
|
||||
const bAnimListElem *ale,
|
||||
const bool include_handles,
|
||||
const float range[2],
|
||||
rctf *r_bounds)
|
||||
{
|
||||
const bool fcu_selection_only = false;
|
||||
BKE_fcurve_calc_bounds(fcu,
|
||||
&r_bounds->xmin,
|
||||
&r_bounds->xmax,
|
||||
&r_bounds->ymin,
|
||||
&r_bounds->ymax,
|
||||
fcu_selection_only,
|
||||
include_handles,
|
||||
range);
|
||||
const short mapping_flag = ANIM_get_normalization_flags(ac);
|
||||
|
||||
const float min_height = 0.01f;
|
||||
const float height = BLI_rctf_size_y(r_bounds);
|
||||
if (height < min_height) {
|
||||
r_bounds->ymin -= (min_height - height) / 2;
|
||||
r_bounds->ymax += (min_height - height) / 2;
|
||||
}
|
||||
|
||||
float offset;
|
||||
const float unit_fac = ANIM_unit_mapping_get_factor(
|
||||
ac->scene, ale->id, fcu, mapping_flag, &offset);
|
||||
|
||||
r_bounds->ymin = (r_bounds->ymin + offset) * unit_fac;
|
||||
r_bounds->ymax = (r_bounds->ymax + offset) * unit_fac;
|
||||
}
|
||||
|
||||
static void get_gpencil_bounds(bGPDlayer *gpl, const float range[2], rctf *r_bounds)
|
||||
{
|
||||
bool found_start = false;
|
||||
int start_frame = 0;
|
||||
int end_frame = 1;
|
||||
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
|
||||
if (gpf->framenum < range[0]) {
|
||||
continue;
|
||||
}
|
||||
if (gpf->framenum > range[1]) {
|
||||
break;
|
||||
}
|
||||
if (!found_start) {
|
||||
start_frame = gpf->framenum;
|
||||
found_start = true;
|
||||
}
|
||||
end_frame = gpf->framenum;
|
||||
}
|
||||
r_bounds->xmin = start_frame;
|
||||
r_bounds->xmax = end_frame;
|
||||
r_bounds->ymin = 0;
|
||||
r_bounds->ymax = 1;
|
||||
}
|
||||
|
||||
static bool get_channel_bounds(bAnimContext *ac,
|
||||
bAnimListElem *ale,
|
||||
const float range[2],
|
||||
const bool include_handles,
|
||||
rctf *r_bounds)
|
||||
{
|
||||
bool found_bounds = false;
|
||||
switch (ale->datatype) {
|
||||
case ALE_GPFRAME: {
|
||||
bGPDlayer *gpl = (bGPDlayer *)ale->data;
|
||||
get_gpencil_bounds(gpl, range, r_bounds);
|
||||
found_bounds = true;
|
||||
break;
|
||||
}
|
||||
case ALE_FCURVE: {
|
||||
FCurve *fcu = (FCurve *)ale->key_data;
|
||||
get_normalized_fcurve_bounds(fcu, ac, ale, include_handles, range, r_bounds);
|
||||
found_bounds = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return found_bounds;
|
||||
}
|
||||
|
||||
static void get_view_range(Scene *scene, const bool use_preview_range, float r_range[2])
|
||||
{
|
||||
if (use_preview_range && scene->r.flag & SCER_PRV_RANGE) {
|
||||
r_range[0] = scene->r.psfra;
|
||||
r_range[1] = scene->r.pefra;
|
||||
}
|
||||
else {
|
||||
r_range[0] = -FLT_MAX;
|
||||
r_range[1] = FLT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pad the given rctf with regions that could block the view.
|
||||
* For example Markers and Time Scrubbing. */
|
||||
static void add_region_padding(bContext *C, bAnimContext *ac, rctf *bounds)
|
||||
{
|
||||
BLI_rctf_scale(bounds, 1.1f);
|
||||
|
||||
const float pad_top = UI_TIME_SCRUB_MARGIN_Y;
|
||||
const float pad_bottom = BLI_listbase_is_empty(ED_context_get_markers(C)) ?
|
||||
V2D_SCROLL_HANDLE_HEIGHT :
|
||||
UI_MARKER_MARGIN_Y;
|
||||
BLI_rctf_pad_y(bounds, ac->region->winy, pad_bottom, pad_top);
|
||||
}
|
||||
|
||||
/* Find the window region in the bAnimContext area and move it to bounds. */
|
||||
static void move_graph_view(bContext *C, bAnimContext *ac, rctf *bounds, const int smooth_viewtx)
|
||||
{
|
||||
LISTBASE_FOREACH (ARegion *, region, &ac->area->regionbase) {
|
||||
if (region->regiontype == RGN_TYPE_WINDOW) {
|
||||
UI_view2d_smooth_view(C, region, bounds, smooth_viewtx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int graphkeys_view_selected_channels_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
bAnimContext ac;
|
||||
|
||||
/* Get editor data. */
|
||||
if (ANIM_animdata_get_context(C, &ac) == 0) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
ListBase anim_data = {NULL, NULL};
|
||||
const int filter = (ANIMFILTER_SEL | ANIMFILTER_NODUPLIS | ANIMFILTER_DATA_VISIBLE |
|
||||
ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
|
||||
size_t anim_data_length = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
|
||||
|
||||
if (anim_data_length == 0) {
|
||||
WM_report(RPT_WARNING, "No channels to operate on");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
float range[2];
|
||||
const bool use_preview_range = RNA_boolean_get(op->ptr, "use_preview_range");
|
||||
get_view_range(ac.scene, use_preview_range, range);
|
||||
|
||||
rctf bounds = {.xmin = FLT_MAX, .xmax = -FLT_MAX, .ymin = FLT_MAX, .ymax = -FLT_MAX};
|
||||
|
||||
bAnimListElem *ale;
|
||||
const bool include_handles = RNA_boolean_get(op->ptr, "include_handles");
|
||||
|
||||
bool valid_bounds = false;
|
||||
for (ale = anim_data.first; ale; ale = ale->next) {
|
||||
rctf channel_bounds;
|
||||
const bool found_bounds = get_channel_bounds(
|
||||
&ac, ale, range, include_handles, &channel_bounds);
|
||||
if (found_bounds) {
|
||||
BLI_rctf_union(&bounds, &channel_bounds);
|
||||
valid_bounds = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid_bounds) {
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
add_region_padding(C, &ac, &bounds);
|
||||
|
||||
const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
|
||||
move_graph_view(C, &ac, &bounds, smooth_viewtx);
|
||||
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static bool channel_view_poll(bContext *C)
|
||||
{
|
||||
return ED_operator_action_active(C) || ED_operator_graphedit_active(C);
|
||||
}
|
||||
|
||||
static void ANIM_OT_channels_view_selected(wmOperatorType *ot)
|
||||
{
|
||||
/* Identifiers */
|
||||
ot->name = "Frame Selected Channels";
|
||||
ot->idname = "ANIM_OT_channels_view_selected";
|
||||
ot->description = "Reset viewable area to show the selected channels";
|
||||
|
||||
/* API callbacks */
|
||||
ot->exec = graphkeys_view_selected_channels_exec;
|
||||
ot->poll = channel_view_poll;
|
||||
|
||||
ot->flag = 0;
|
||||
|
||||
ot->prop = RNA_def_boolean(ot->srna,
|
||||
"include_handles",
|
||||
true,
|
||||
"Include Handles",
|
||||
"Include handles of keyframes when calculating extents");
|
||||
|
||||
ot->prop = RNA_def_boolean(ot->srna,
|
||||
"use_preview_range",
|
||||
true,
|
||||
"Use Preview Range",
|
||||
"Ignore frames outside of the preview range");
|
||||
}
|
||||
|
||||
static int graphkeys_channel_view_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
bAnimContext ac;
|
||||
|
||||
if (ANIM_animdata_get_context(C, &ac) == 0) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
ListBase anim_data = {NULL, NULL};
|
||||
const int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS |
|
||||
ANIMFILTER_LIST_CHANNELS);
|
||||
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
|
||||
|
||||
bAnimListElem *ale;
|
||||
const int channel_index = animchannels_channel_get(&ac, event->mval);
|
||||
ale = BLI_findlink(&anim_data, channel_index);
|
||||
if (ale == NULL) {
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
float range[2];
|
||||
const bool use_preview_range = RNA_boolean_get(op->ptr, "use_preview_range");
|
||||
get_view_range(ac.scene, use_preview_range, range);
|
||||
|
||||
rctf bounds;
|
||||
const bool include_handles = RNA_boolean_get(op->ptr, "include_handles");
|
||||
const bool found_bounds = get_channel_bounds(&ac, ale, range, include_handles, &bounds);
|
||||
|
||||
if (!found_bounds) {
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
add_region_padding(C, &ac, &bounds);
|
||||
|
||||
const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
|
||||
move_graph_view(C, &ac, &bounds, smooth_viewtx);
|
||||
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static void ANIM_OT_channel_view_pick(wmOperatorType *ot)
|
||||
{
|
||||
/* Identifiers */
|
||||
ot->name = "Frame Channel Under Cursor";
|
||||
ot->idname = "ANIM_OT_channel_view_pick";
|
||||
ot->description = "Reset viewable area to show the channel under the cursor";
|
||||
|
||||
/* API callbacks */
|
||||
ot->invoke = graphkeys_channel_view_pick_invoke;
|
||||
ot->poll = channel_view_poll;
|
||||
|
||||
ot->flag = 0;
|
||||
|
||||
ot->prop = RNA_def_boolean(ot->srna,
|
||||
"include_handles",
|
||||
true,
|
||||
"Include Handles",
|
||||
"Include handles of keyframes when calculating extents");
|
||||
|
||||
ot->prop = RNA_def_boolean(ot->srna,
|
||||
"use_preview_range",
|
||||
true,
|
||||
"Use Preview Range",
|
||||
"Ignore frames outside of the preview range");
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Operator Registration
|
||||
* \{ */
|
||||
|
@ -3657,6 +3935,9 @@ void ED_operatortypes_animchannels(void)
|
|||
WM_operatortype_append(ANIM_OT_channels_setting_disable);
|
||||
WM_operatortype_append(ANIM_OT_channels_setting_toggle);
|
||||
|
||||
WM_operatortype_append(ANIM_OT_channel_view_pick);
|
||||
WM_operatortype_append(ANIM_OT_channels_view_selected);
|
||||
|
||||
WM_operatortype_append(ANIM_OT_channels_delete);
|
||||
|
||||
/* XXX does this need to be a separate operator? */
|
||||
|
|
|
@ -1429,7 +1429,7 @@ static int separate_exec(bContext *C, wmOperator *op)
|
|||
|
||||
/* All curves failed due to the same error. */
|
||||
if (status.error_vertex_keys) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Cannot separate curves with vertex keys");
|
||||
BKE_report(op->reports, RPT_ERROR, "Cannot separate curves with shape keys");
|
||||
}
|
||||
else {
|
||||
BLI_assert(status.error_generic);
|
||||
|
|
|
@ -285,7 +285,7 @@ static int paint_mask_extract_exec(bContext *C, wmOperator *op)
|
|||
params.add_solidify = RNA_boolean_get(op->ptr, "add_solidify");
|
||||
|
||||
/* Push an undo step prior to extraction.
|
||||
* Note: A second push happens after the operator due to
|
||||
* NOTE: A second push happens after the operator due to
|
||||
* the OPTYPE_UNDO flag; having an initial undo step here
|
||||
* is just needed to preserve the active object pointer.
|
||||
*
|
||||
|
|
|
@ -1534,8 +1534,13 @@ void ED_mesh_split_faces(Mesh *mesh)
|
|||
const Span<MPoly> polys = mesh->polys();
|
||||
const Span<MLoop> loops = mesh->loops();
|
||||
const float split_angle = (mesh->flag & ME_AUTOSMOOTH) != 0 ? mesh->smoothresh : float(M_PI);
|
||||
const bke::AttributeAccessor attributes = mesh->attributes();
|
||||
const VArray<bool> mesh_sharp_edges = attributes.lookup_or_default<bool>(
|
||||
"sharp_edge", ATTR_DOMAIN_EDGE, false);
|
||||
|
||||
Array<bool> sharp_edges(mesh->totedge);
|
||||
mesh_sharp_edges.materialize(sharp_edges);
|
||||
|
||||
Array<bool> sharp_edges(mesh->totedge, false);
|
||||
BKE_edges_sharp_from_angle_set(mesh->totedge,
|
||||
loops.data(),
|
||||
loops.size(),
|
||||
|
|
|
@ -3446,55 +3446,63 @@ static char *vertex_group_lock_description(bContext * /*C*/,
|
|||
int action = RNA_enum_get(params, "action");
|
||||
int mask = RNA_enum_get(params, "mask");
|
||||
|
||||
const char *action_str, *target_str;
|
||||
|
||||
/* NOTE: constructing the following string literals can be done in a less verbose way,
|
||||
* however the resulting strings can't be usefully translated, (via `TIP_`). */
|
||||
switch (action) {
|
||||
case VGROUP_LOCK:
|
||||
action_str = TIP_("Lock");
|
||||
switch (mask) {
|
||||
case VGROUP_MASK_ALL:
|
||||
return BLI_strdup(TIP_("Lock all vertex groups of the active object"));
|
||||
case VGROUP_MASK_SELECTED:
|
||||
return BLI_strdup(TIP_("Lock selected vertex groups of the active object"));
|
||||
case VGROUP_MASK_UNSELECTED:
|
||||
return BLI_strdup(TIP_("Lock unselected vertex groups of the active object"));
|
||||
case VGROUP_MASK_INVERT_UNSELECTED:
|
||||
return BLI_strdup(
|
||||
TIP_("Lock selected and unlock unselected vertex groups of the active object"));
|
||||
}
|
||||
break;
|
||||
case VGROUP_UNLOCK:
|
||||
action_str = TIP_("Unlock");
|
||||
switch (mask) {
|
||||
case VGROUP_MASK_ALL:
|
||||
return BLI_strdup(TIP_("Unlock all vertex groups of the active object"));
|
||||
case VGROUP_MASK_SELECTED:
|
||||
return BLI_strdup(TIP_("Unlock selected vertex groups of the active object"));
|
||||
case VGROUP_MASK_UNSELECTED:
|
||||
return BLI_strdup(TIP_("Unlock unselected vertex groups of the active object"));
|
||||
case VGROUP_MASK_INVERT_UNSELECTED:
|
||||
return BLI_strdup(
|
||||
TIP_("Unlock selected and lock unselected vertex groups of the active object"));
|
||||
}
|
||||
break;
|
||||
case VGROUP_TOGGLE:
|
||||
action_str = TIP_("Toggle locks of");
|
||||
switch (mask) {
|
||||
case VGROUP_MASK_ALL:
|
||||
return BLI_strdup(TIP_("Toggle locks of all vertex groups of the active object"));
|
||||
case VGROUP_MASK_SELECTED:
|
||||
return BLI_strdup(TIP_("Toggle locks of selected vertex groups of the active object"));
|
||||
case VGROUP_MASK_UNSELECTED:
|
||||
return BLI_strdup(TIP_("Toggle locks of unselected vertex groups of the active object"));
|
||||
case VGROUP_MASK_INVERT_UNSELECTED:
|
||||
return BLI_strdup(TIP_(
|
||||
"Toggle locks of all and invert unselected vertex groups of the active object"));
|
||||
}
|
||||
break;
|
||||
case VGROUP_INVERT:
|
||||
action_str = TIP_("Invert locks of");
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
switch (mask) {
|
||||
case VGROUP_MASK_ALL:
|
||||
target_str = TIP_("all");
|
||||
break;
|
||||
case VGROUP_MASK_SELECTED:
|
||||
target_str = TIP_("selected");
|
||||
break;
|
||||
case VGROUP_MASK_UNSELECTED:
|
||||
target_str = TIP_("unselected");
|
||||
break;
|
||||
case VGROUP_MASK_INVERT_UNSELECTED:
|
||||
switch (action) {
|
||||
case VGROUP_INVERT:
|
||||
target_str = TIP_("selected");
|
||||
break;
|
||||
case VGROUP_LOCK:
|
||||
target_str = TIP_("selected and unlock unselected");
|
||||
break;
|
||||
case VGROUP_UNLOCK:
|
||||
target_str = TIP_("selected and lock unselected");
|
||||
break;
|
||||
default:
|
||||
target_str = TIP_("all and invert unselected");
|
||||
switch (mask) {
|
||||
case VGROUP_MASK_ALL:
|
||||
return BLI_strdup(TIP_("Invert locks of all vertex groups of the active object"));
|
||||
case VGROUP_MASK_SELECTED:
|
||||
case VGROUP_MASK_INVERT_UNSELECTED:
|
||||
return BLI_strdup(TIP_("Invert locks of selected vertex groups of the active object"));
|
||||
case VGROUP_MASK_UNSELECTED:
|
||||
return BLI_strdup(TIP_("Invert locks of unselected vertex groups of the active object"));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return BLI_sprintfN(TIP_("%s %s vertex groups of the active object"), action_str, target_str);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void OBJECT_OT_vertex_group_lock(wmOperatorType *ot)
|
||||
|
|
|
@ -83,7 +83,7 @@ bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd,
|
|||
|
||||
bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, const Brush *br)
|
||||
{
|
||||
if (br && SCULPT_stroke_is_dynamic_topology(ss, br)) {
|
||||
if (ss && br && SCULPT_stroke_is_dynamic_topology(ss, br)) {
|
||||
return false;
|
||||
}
|
||||
if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_TOPOLOGY)) {
|
||||
|
|
|
@ -1571,8 +1571,13 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent
|
|||
SCULPT_stroke_id_next(ob);
|
||||
|
||||
SCULPT_undo_push_begin(ob, op);
|
||||
SCULPT_filter_cache_init(
|
||||
C, ob, sd, SCULPT_UNDO_COORDS, event->mval, RNA_float_get(op->ptr, "area_normal_radius"));
|
||||
SCULPT_filter_cache_init(C,
|
||||
ob,
|
||||
sd,
|
||||
SCULPT_UNDO_COORDS,
|
||||
event->mval,
|
||||
RNA_float_get(op->ptr, "area_normal_radius"),
|
||||
RNA_float_get(op->ptr, "strength"));
|
||||
|
||||
ss->filter_cache->automasking = SCULPT_automasking_cache_init(sd, nullptr, ob);
|
||||
|
||||
|
|
|
@ -347,8 +347,13 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent
|
|||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
SCULPT_filter_cache_init(
|
||||
C, ob, sd, SCULPT_UNDO_COLOR, event->mval, RNA_float_get(op->ptr, "area_normal_radius"));
|
||||
SCULPT_filter_cache_init(C,
|
||||
ob,
|
||||
sd,
|
||||
SCULPT_UNDO_COLOR,
|
||||
event->mval,
|
||||
RNA_float_get(op->ptr, "area_normal_radius"),
|
||||
RNA_float_get(op->ptr, "strength"));
|
||||
FilterCache *filter_cache = ss->filter_cache;
|
||||
filter_cache->active_face_set = SCULPT_FACE_SET_NONE;
|
||||
filter_cache->automasking = SCULPT_automasking_cache_init(sd, nullptr, ob);
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_hash.h"
|
||||
#include "BLI_index_range.hh"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_task.h"
|
||||
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
@ -23,6 +25,7 @@
|
|||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "ED_screen.h"
|
||||
#include "ED_view3d.h"
|
||||
|
||||
#include "paint_intern.h"
|
||||
|
@ -30,14 +33,20 @@
|
|||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
|
||||
using blender::float2;
|
||||
using blender::float3;
|
||||
using blender::IndexRange;
|
||||
|
||||
void SCULPT_filter_to_orientation_space(float r_v[3], FilterCache *filter_cache)
|
||||
{
|
||||
switch (filter_cache->orientation) {
|
||||
|
@ -96,13 +105,14 @@ void SCULPT_filter_cache_init(bContext *C,
|
|||
Sculpt *sd,
|
||||
const int undo_type,
|
||||
const int mval[2],
|
||||
float area_normal_radius)
|
||||
float area_normal_radius,
|
||||
float start_strength)
|
||||
{
|
||||
SculptSession *ss = ob->sculpt;
|
||||
PBVH *pbvh = ob->sculpt->pbvh;
|
||||
|
||||
ss->filter_cache = MEM_cnew<FilterCache>(__func__);
|
||||
|
||||
ss->filter_cache->start_filter_strength = start_strength;
|
||||
ss->filter_cache->random_seed = rand();
|
||||
|
||||
if (undo_type == SCULPT_UNDO_COLOR) {
|
||||
|
@ -338,6 +348,15 @@ static bool sculpt_mesh_filter_needs_pmap(eSculptMeshFilterType filter_type)
|
|||
MESH_FILTER_SHARPEN);
|
||||
}
|
||||
|
||||
static bool sculpt_mesh_filter_is_continuous(eSculptMeshFilterType type)
|
||||
{
|
||||
return (ELEM(type,
|
||||
MESH_FILTER_SHARPEN,
|
||||
MESH_FILTER_SMOOTH,
|
||||
MESH_FILTER_RELAX,
|
||||
MESH_FILTER_RELAX_FACE_SETS));
|
||||
}
|
||||
|
||||
static void mesh_filter_task_cb(void *__restrict userdata,
|
||||
const int i,
|
||||
const TaskParallelTLS *__restrict /*tls*/)
|
||||
|
@ -379,7 +398,8 @@ static void mesh_filter_task_cb(void *__restrict userdata,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (ELEM(filter_type, MESH_FILTER_RELAX, MESH_FILTER_RELAX_FACE_SETS)) {
|
||||
if (ELEM(filter_type, MESH_FILTER_RELAX, MESH_FILTER_RELAX_FACE_SETS) ||
|
||||
ss->filter_cache->no_orig_co) {
|
||||
copy_v3_v3(orig_co, vd.co);
|
||||
}
|
||||
else {
|
||||
|
@ -681,34 +701,16 @@ static void mesh_filter_surface_smooth_displace_task_cb(void *__restrict userdat
|
|||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
|
||||
static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
static void sculpt_mesh_filter_apply(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
||||
SculptSession *ss = ob->sculpt;
|
||||
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
|
||||
eSculptMeshFilterType filter_type = eSculptMeshFilterType(RNA_enum_get(op->ptr, "type"));
|
||||
float filter_strength = RNA_float_get(op->ptr, "strength");
|
||||
|
||||
if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
|
||||
SCULPT_filter_cache_free(ss);
|
||||
SCULPT_undo_push_end(ob);
|
||||
SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
if (event->type != MOUSEMOVE) {
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
const float len = event->prev_press_xy[0] - event->xy[0];
|
||||
filter_strength = filter_strength * -len * 0.001f * UI_DPI_FAC;
|
||||
|
||||
SCULPT_vertex_random_access_ensure(ss);
|
||||
|
||||
bool needs_pmap = sculpt_mesh_filter_needs_pmap(filter_type);
|
||||
BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false, false);
|
||||
|
||||
SculptThreadedTaskData data{};
|
||||
data.sd = sd;
|
||||
data.ob = ob;
|
||||
|
@ -740,22 +742,136 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *
|
|||
}
|
||||
|
||||
SCULPT_flush_update_step(C, SCULPT_UPDATE_COORDS);
|
||||
}
|
||||
|
||||
static void sculpt_mesh_update_strength(wmOperator *op,
|
||||
SculptSession *ss,
|
||||
float2 prev_press_mouse,
|
||||
float2 mouse)
|
||||
{
|
||||
const float len = prev_press_mouse[0] - mouse[0];
|
||||
|
||||
float filter_strength = ss->filter_cache->start_filter_strength * -len * 0.001f * UI_DPI_FAC;
|
||||
RNA_float_set(op->ptr, "strength", filter_strength);
|
||||
}
|
||||
static void sculpt_mesh_filter_apply_with_history(bContext *C, wmOperator *op)
|
||||
{
|
||||
/* Event history is only stored for smooth and relax filters. */
|
||||
if (!RNA_collection_length(op->ptr, "event_history")) {
|
||||
sculpt_mesh_filter_apply(C, op);
|
||||
return;
|
||||
}
|
||||
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
SculptSession *ss = ob->sculpt;
|
||||
float2 start_mouse;
|
||||
bool first = true;
|
||||
float initial_strength = ss->filter_cache->start_filter_strength;
|
||||
|
||||
RNA_BEGIN (op->ptr, item, "event_history") {
|
||||
float2 mouse;
|
||||
RNA_float_get_array(&item, "mouse_event", mouse);
|
||||
|
||||
if (first) {
|
||||
first = false;
|
||||
start_mouse = mouse;
|
||||
continue;
|
||||
}
|
||||
|
||||
sculpt_mesh_update_strength(op, ss, start_mouse, mouse);
|
||||
sculpt_mesh_filter_apply(C, op);
|
||||
}
|
||||
RNA_END;
|
||||
|
||||
RNA_float_set(op->ptr, "strength", initial_strength);
|
||||
}
|
||||
|
||||
static void sculpt_mesh_filter_end(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
SculptSession *ss = ob->sculpt;
|
||||
|
||||
SCULPT_filter_cache_free(ss);
|
||||
SCULPT_undo_push_end(ob);
|
||||
SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS);
|
||||
}
|
||||
|
||||
static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
||||
SculptSession *ss = ob->sculpt;
|
||||
const eSculptMeshFilterType filter_type = eSculptMeshFilterType(RNA_enum_get(op->ptr, "type"));
|
||||
|
||||
if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
|
||||
float initial_strength = ss->filter_cache->start_filter_strength;
|
||||
sculpt_mesh_filter_end(C, op);
|
||||
|
||||
/* Don't update strength property if we're storing an event history. */
|
||||
if (sculpt_mesh_filter_is_continuous(filter_type)) {
|
||||
RNA_float_set(op->ptr, "strength", initial_strength);
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
if (event->type != MOUSEMOVE) {
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
/* Note: some filter types are continuous, for these we store an
|
||||
* event history in RNA for continuous.
|
||||
* This way the user can tweak the last operator properties
|
||||
* or repeat the op and get expected results. */
|
||||
if (sculpt_mesh_filter_is_continuous(filter_type)) {
|
||||
if (RNA_collection_length(op->ptr, "event_history") == 0) {
|
||||
/* First entry is the start mouse position, event->prev_press_xy. */
|
||||
PointerRNA startptr;
|
||||
RNA_collection_add(op->ptr, "event_history", &startptr);
|
||||
|
||||
float2 mouse_start(float(event->prev_press_xy[0]), float(event->prev_press_xy[1]));
|
||||
RNA_float_set_array(&startptr, "mouse_event", mouse_start);
|
||||
}
|
||||
|
||||
PointerRNA itemptr;
|
||||
RNA_collection_add(op->ptr, "event_history", &itemptr);
|
||||
|
||||
float2 mouse(float(event->xy[0]), float(event->xy[1]));
|
||||
RNA_float_set_array(&itemptr, "mouse_event", mouse);
|
||||
RNA_float_set(&itemptr, "pressure", WM_event_tablet_data(event, nullptr, nullptr));
|
||||
}
|
||||
|
||||
float2 prev_mval(float(event->prev_press_xy[0]), float(event->prev_press_xy[1]));
|
||||
float2 mval(float(event->xy[0]), float(event->xy[1]));
|
||||
|
||||
sculpt_mesh_update_strength(op, ss, prev_mval, mval);
|
||||
|
||||
bool needs_pmap = sculpt_mesh_filter_needs_pmap(filter_type);
|
||||
BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false, false);
|
||||
|
||||
sculpt_mesh_filter_apply(C, op);
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
/* Returns OPERATOR_PASS_THROUGH on success. */
|
||||
static int sculpt_mesh_filter_start(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
||||
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
|
||||
int mval[2];
|
||||
RNA_int_get_array(op->ptr, "start_mouse", mval);
|
||||
|
||||
const eSculptMeshFilterType filter_type = eSculptMeshFilterType(RNA_enum_get(op->ptr, "type"));
|
||||
const bool use_automasking = SCULPT_is_automasking_enabled(sd, nullptr, nullptr);
|
||||
const bool needs_topology_info = sculpt_mesh_filter_needs_pmap(filter_type) || use_automasking;
|
||||
|
||||
BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_topology_info, false, false);
|
||||
SculptSession *ss = ob->sculpt;
|
||||
|
||||
const eMeshFilterDeformAxis deform_axis = eMeshFilterDeformAxis(
|
||||
RNA_enum_get(op->ptr, "deform_axis"));
|
||||
const eSculptMeshFilterType filter_type = eSculptMeshFilterType(RNA_enum_get(op->ptr, "type"));
|
||||
const bool use_automasking = SCULPT_is_automasking_enabled(sd, ss, nullptr);
|
||||
const bool needs_topology_info = sculpt_mesh_filter_needs_pmap(filter_type) || use_automasking;
|
||||
|
||||
if (deform_axis == 0) {
|
||||
/* All axis are disabled, so the filter is not going to produce any deformation. */
|
||||
|
@ -768,21 +884,25 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
|
|||
|
||||
/* Update the active face set manually as the paint cursor is not enabled when using the Mesh
|
||||
* Filter Tool. */
|
||||
float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
|
||||
float mval_fl[2] = {float(mval[0]), float(mval[1])};
|
||||
SculptCursorGeometryInfo sgi;
|
||||
SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);
|
||||
}
|
||||
|
||||
SCULPT_vertex_random_access_ensure(ss);
|
||||
BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_topology_info, false, false);
|
||||
if (needs_topology_info) {
|
||||
SCULPT_boundary_info_ensure(ob);
|
||||
}
|
||||
|
||||
SCULPT_undo_push_begin(ob, op);
|
||||
|
||||
SCULPT_filter_cache_init(
|
||||
C, ob, sd, SCULPT_UNDO_COORDS, event->mval, RNA_float_get(op->ptr, "area_normal_radius"));
|
||||
SCULPT_filter_cache_init(C,
|
||||
ob,
|
||||
sd,
|
||||
SCULPT_UNDO_COORDS,
|
||||
mval,
|
||||
RNA_float_get(op->ptr, "area_normal_radius"),
|
||||
RNA_float_get(op->ptr, "strength"));
|
||||
|
||||
FilterCache *filter_cache = ss->filter_cache;
|
||||
filter_cache->active_face_set = SCULPT_FACE_SET_NONE;
|
||||
|
@ -826,12 +946,56 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
|
|||
RNA_enum_get(op->ptr, "orientation"));
|
||||
ss->filter_cache->orientation = orientation;
|
||||
|
||||
WM_event_add_modal_handler(C, op);
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
|
||||
static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
RNA_int_set_array(op->ptr, "start_mouse", event->mval);
|
||||
int ret = sculpt_mesh_filter_start(C, op);
|
||||
|
||||
if (ret == OPERATOR_PASS_THROUGH) {
|
||||
WM_event_add_modal_handler(C, op);
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sculpt_mesh_filter_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
int ret = sculpt_mesh_filter_start(C, op);
|
||||
|
||||
if (ret == OPERATOR_PASS_THROUGH) {
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
SculptSession *ss = ob->sculpt;
|
||||
|
||||
int iterations = RNA_int_get(op->ptr, "iteration_count");
|
||||
bool has_history = RNA_collection_length(op->ptr, "event_history") > 0;
|
||||
|
||||
if (!has_history) {
|
||||
ss->filter_cache->no_orig_co = true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
sculpt_mesh_filter_apply_with_history(C, op);
|
||||
|
||||
ss->filter_cache->no_orig_co = true;
|
||||
}
|
||||
|
||||
sculpt_mesh_filter_end(C, op);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SCULPT_mesh_filter_properties(wmOperatorType *ot)
|
||||
{
|
||||
RNA_def_int_array(
|
||||
ot->srna, "start_mouse", 2, nullptr, 0, 1 << 14, "Starting Mouse", "", 0, 1 << 14);
|
||||
|
||||
RNA_def_float(
|
||||
ot->srna,
|
||||
"area_normal_radius",
|
||||
|
@ -844,6 +1008,31 @@ void SCULPT_mesh_filter_properties(wmOperatorType *ot)
|
|||
1.0);
|
||||
RNA_def_float(
|
||||
ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f);
|
||||
RNA_def_int(ot->srna,
|
||||
"iteration_count",
|
||||
1,
|
||||
1,
|
||||
10000,
|
||||
"Repeat",
|
||||
"How many times to repeat the filter",
|
||||
1,
|
||||
100);
|
||||
|
||||
/* Smooth filter requires entire event history. */
|
||||
PropertyRNA *prop = RNA_def_collection_runtime(
|
||||
ot->srna, "event_history", &RNA_OperatorStrokeElement, "", "");
|
||||
RNA_def_property_flag(prop, PropertyFlag(int(PROP_HIDDEN) | int(PROP_SKIP_SAVE)));
|
||||
}
|
||||
|
||||
static void sculpt_mesh_ui_exec(bContext * /*C*/, wmOperator *op)
|
||||
{
|
||||
uiLayout *layout = op->layout;
|
||||
|
||||
uiItemR(layout, op->ptr, "strength", 0, nullptr, ICON_NONE);
|
||||
uiItemR(layout, op->ptr, "iteration_count", 0, nullptr, ICON_NONE);
|
||||
uiItemR(layout, op->ptr, "orientation", 0, nullptr, ICON_NONE);
|
||||
layout = uiLayoutRow(layout, true);
|
||||
uiItemR(layout, op->ptr, "deform_axis", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
|
||||
}
|
||||
|
||||
void SCULPT_OT_mesh_filter(wmOperatorType *ot)
|
||||
|
@ -857,6 +1046,8 @@ void SCULPT_OT_mesh_filter(wmOperatorType *ot)
|
|||
ot->invoke = sculpt_mesh_filter_invoke;
|
||||
ot->modal = sculpt_mesh_filter_modal;
|
||||
ot->poll = SCULPT_mode_poll;
|
||||
ot->exec = sculpt_mesh_filter_exec;
|
||||
ot->ui = sculpt_mesh_ui_exec;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
|
|
|
@ -494,6 +494,8 @@ struct FilterCache {
|
|||
float (*pre_smoothed_color)[4];
|
||||
|
||||
ViewContext vc;
|
||||
float start_filter_strength;
|
||||
bool no_orig_co;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1427,7 +1429,8 @@ void SCULPT_filter_cache_init(bContext *C,
|
|||
Sculpt *sd,
|
||||
int undo_type,
|
||||
const int mval[2],
|
||||
float area_normal_radius);
|
||||
float area_normal_radius,
|
||||
float start_strength);
|
||||
void SCULPT_filter_cache_free(SculptSession *ss);
|
||||
void SCULPT_mesh_filter_properties(wmOperatorType *ot);
|
||||
|
||||
|
|
|
@ -229,6 +229,8 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op)
|
|||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
SCULPT_topology_islands_invalidate(ss);
|
||||
|
||||
/* Redraw. */
|
||||
SCULPT_pbvh_clear(ob);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
|
||||
|
|
|
@ -58,7 +58,7 @@ void ED_sculpt_init_transform(bContext *C, Object *ob, const int mval[2], const
|
|||
|
||||
SCULPT_vertex_random_access_ensure(ss);
|
||||
|
||||
SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS, mval, 5.0);
|
||||
SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS, mval, 5.0, 1.0f);
|
||||
|
||||
if (sd->transform_mode == SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC) {
|
||||
ss->filter_cache->transform_displacement_mode = SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL;
|
||||
|
|
|
@ -400,6 +400,21 @@ static void saction_channel_region_message_subscribe(const wmRegionMessageSubscr
|
|||
}
|
||||
}
|
||||
|
||||
static void action_clamp_scroll(ARegion *region)
|
||||
{
|
||||
View2D *v2d = ®ion->v2d;
|
||||
const float cur_height_y = BLI_rctf_size_y(&v2d->cur);
|
||||
|
||||
if (BLI_rctf_size_y(&v2d->cur) > BLI_rctf_size_y(&v2d->tot)) {
|
||||
v2d->cur.ymin = -cur_height_y;
|
||||
v2d->cur.ymax = 0;
|
||||
}
|
||||
else if (v2d->cur.ymin < v2d->tot.ymin) {
|
||||
v2d->cur.ymin = v2d->tot.ymin;
|
||||
v2d->cur.ymax = v2d->cur.ymin + cur_height_y;
|
||||
}
|
||||
}
|
||||
|
||||
static void action_main_region_listener(const wmRegionListenerParams *params)
|
||||
{
|
||||
ARegion *region = params->region;
|
||||
|
@ -860,6 +875,13 @@ static void action_blend_write(BlendWriter *writer, SpaceLink *sl)
|
|||
BLO_write_struct(writer, SpaceAction, sl);
|
||||
}
|
||||
|
||||
static void action_main_region_view2d_changed(const bContext *UNUSED(C), ARegion *region)
|
||||
{
|
||||
/* V2D_KEEPTOT_STRICT cannot be used to clamp scrolling
|
||||
* because it also clamps the x-axis to 0. */
|
||||
action_clamp_scroll(region);
|
||||
}
|
||||
|
||||
void ED_spacetype_action(void)
|
||||
{
|
||||
SpaceType *st = MEM_callocN(sizeof(SpaceType), "spacetype action");
|
||||
|
@ -892,6 +914,7 @@ void ED_spacetype_action(void)
|
|||
art->draw_overlay = action_main_region_draw_overlay;
|
||||
art->listener = action_main_region_listener;
|
||||
art->message_subscribe = saction_main_region_message_subscribe;
|
||||
art->on_view2d_changed = action_main_region_view2d_changed;
|
||||
art->keymapflag = ED_KEYMAP_GIZMO | ED_KEYMAP_VIEW2D | ED_KEYMAP_ANIMATION | ED_KEYMAP_FRAMES;
|
||||
|
||||
BLI_addhead(&st->regiontypes, art);
|
||||
|
|
|
@ -785,7 +785,12 @@ void uiTemplateMovieclipInformation(uiLayout *layout,
|
|||
uiLayout *col = uiLayoutColumn(layout, false);
|
||||
uiLayoutSetAlignment(col, UI_LAYOUT_ALIGN_RIGHT);
|
||||
|
||||
ImBuf *ibuf = BKE_movieclip_get_ibuf_flag(clip, user, clip->flag, MOVIECLIP_CACHE_SKIP);
|
||||
/* NOTE: Put the frame to cache. If the panel is drawn, the display will also be shown, as well
|
||||
* as metadata panel. So if the cache is skipped here it is not really a memory saver, but
|
||||
* skipping the cache could lead to a performance impact depending on the order in which panels
|
||||
* and the main area is drawn. Basically, if it is this template drawn first and then the main
|
||||
* area it will lead to frame read and processing happening twice. */
|
||||
ImBuf *ibuf = BKE_movieclip_get_ibuf_flag(clip, user, clip->flag, 0);
|
||||
|
||||
int width, height;
|
||||
/* Display frame dimensions, channels number and buffer type. */
|
||||
|
|
|
@ -1880,6 +1880,11 @@ static bool euler_filter_single_channel(FCurve *fcu)
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Skip baked FCurves. */
|
||||
if (fcu->bezt == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* `prev` follows bezt, bezt = "current" point to be fixed. */
|
||||
/* Our method depends on determining a "difference" from the previous vert. */
|
||||
bool is_modified = false;
|
||||
|
|
|
@ -91,7 +91,7 @@ void get_graph_keyframe_extents(bAnimContext *ac,
|
|||
|
||||
/* Get range. */
|
||||
if (BKE_fcurve_calc_bounds(
|
||||
fcu, &txmin, &txmax, &tymin, &tymax, do_sel_only, include_handles)) {
|
||||
fcu, &txmin, &txmax, &tymin, &tymax, do_sel_only, include_handles, NULL)) {
|
||||
short mapping_flag = ANIM_get_normalization_flags(ac);
|
||||
|
||||
/* Apply NLA scaling. */
|
||||
|
|
|
@ -993,7 +993,9 @@ static void node_group_make_insert_selected(const bContext &C,
|
|||
}
|
||||
}
|
||||
|
||||
bke::node_field_inferencing::update_field_inferencing(group);
|
||||
if (group.type == NTREE_GEOMETRY) {
|
||||
bke::node_field_inferencing::update_field_inferencing(group);
|
||||
}
|
||||
nodes::update_node_declaration_and_sockets(ntree, *gnode);
|
||||
|
||||
/* Add new links to inputs outside of the group. */
|
||||
|
|
|
@ -2680,7 +2680,6 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
|
|||
Editing *ed = SEQ_editing_get(scene);
|
||||
SpaceSeq *sseq = CTX_wm_space_seq(C);
|
||||
View2D *v2d = ®ion->v2d;
|
||||
float col[3];
|
||||
|
||||
seq_prefetch_wm_notify(C, scene);
|
||||
|
||||
|
@ -2689,8 +2688,7 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
|
|||
GPU_framebuffer_bind_no_srgb(framebuffer_overlay);
|
||||
GPU_depth_test(GPU_DEPTH_NONE);
|
||||
|
||||
UI_GetThemeColor3fv(TH_BACK, col);
|
||||
GPU_clear_color(col[0], col[1], col[2], 0.0f);
|
||||
UI_ThemeClearColor(TH_BACK);
|
||||
|
||||
UI_view2d_view_ortho(v2d);
|
||||
draw_seq_timeline_channels(v2d);
|
||||
|
|
|
@ -68,6 +68,122 @@ std::unique_ptr<ColumnValues> ExtraColumns::get_column_values(
|
|||
return std::make_unique<ColumnValues>(column_id.name, GVArray::ForSpan(*values));
|
||||
}
|
||||
|
||||
static void add_mesh_debug_column_names(
|
||||
const Mesh &mesh,
|
||||
const eAttrDomain domain,
|
||||
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn)
|
||||
{
|
||||
switch (domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
if (CustomData_has_layer(&mesh.vdata, CD_ORIGINDEX)) {
|
||||
fn({(char *)"Original Index"}, false);
|
||||
}
|
||||
break;
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
if (CustomData_has_layer(&mesh.edata, CD_ORIGINDEX)) {
|
||||
fn({(char *)"Original Index"}, false);
|
||||
}
|
||||
fn({(char *)"Vertex 1"}, false);
|
||||
fn({(char *)"Vertex 2"}, false);
|
||||
break;
|
||||
case ATTR_DOMAIN_FACE:
|
||||
if (CustomData_has_layer(&mesh.pdata, CD_ORIGINDEX)) {
|
||||
fn({(char *)"Original Index"}, false);
|
||||
}
|
||||
fn({(char *)"Corner Start"}, false);
|
||||
fn({(char *)"Corner Size"}, false);
|
||||
break;
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
fn({(char *)"Vertex"}, false);
|
||||
fn({(char *)"Edge"}, false);
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static std::unique_ptr<ColumnValues> build_mesh_debug_columns(const Mesh &mesh,
|
||||
const eAttrDomain domain,
|
||||
const StringRef name)
|
||||
{
|
||||
switch (domain) {
|
||||
case ATTR_DOMAIN_POINT: {
|
||||
if (name == "Original Index") {
|
||||
const int *data = static_cast<const int *>(
|
||||
CustomData_get_layer(&mesh.vdata, CD_ORIGINDEX));
|
||||
if (data) {
|
||||
return std::make_unique<ColumnValues>(name, VArray<int>::ForSpan({data, mesh.totvert}));
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
case ATTR_DOMAIN_EDGE: {
|
||||
const Span<MEdge> edges = mesh.edges();
|
||||
if (name == "Original Index") {
|
||||
const int *data = static_cast<const int *>(
|
||||
CustomData_get_layer(&mesh.edata, CD_ORIGINDEX));
|
||||
if (data) {
|
||||
return std::make_unique<ColumnValues>(name, VArray<int>::ForSpan({data, mesh.totedge}));
|
||||
}
|
||||
}
|
||||
if (name == "Vertex 1") {
|
||||
return std::make_unique<ColumnValues>(
|
||||
name, VArray<int>::ForFunc(edges.size(), [edges](int64_t index) {
|
||||
return edges[index].v1;
|
||||
}));
|
||||
}
|
||||
if (name == "Vertex 2") {
|
||||
return std::make_unique<ColumnValues>(
|
||||
name, VArray<int>::ForFunc(edges.size(), [edges](int64_t index) {
|
||||
return edges[index].v2;
|
||||
}));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
case ATTR_DOMAIN_FACE: {
|
||||
const Span<MPoly> polys = mesh.polys();
|
||||
if (name == "Original Index") {
|
||||
const int *data = static_cast<const int *>(
|
||||
CustomData_get_layer(&mesh.pdata, CD_ORIGINDEX));
|
||||
if (data) {
|
||||
return std::make_unique<ColumnValues>(name, VArray<int>::ForSpan({data, mesh.totpoly}));
|
||||
}
|
||||
}
|
||||
if (name == "Corner Start") {
|
||||
return std::make_unique<ColumnValues>(
|
||||
name, VArray<int>::ForFunc(polys.size(), [polys](int64_t index) {
|
||||
return polys[index].loopstart;
|
||||
}));
|
||||
}
|
||||
if (name == "Corner Size") {
|
||||
return std::make_unique<ColumnValues>(
|
||||
name, VArray<int>::ForFunc(polys.size(), [polys](int64_t index) {
|
||||
return polys[index].totloop;
|
||||
}));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
case ATTR_DOMAIN_CORNER: {
|
||||
const Span<MLoop> loops = mesh.loops();
|
||||
if (name == "Vertex") {
|
||||
return std::make_unique<ColumnValues>(
|
||||
name,
|
||||
VArray<int>::ForFunc(loops.size(), [loops](int64_t index) { return loops[index].v; }));
|
||||
}
|
||||
if (name == "Edge") {
|
||||
return std::make_unique<ColumnValues>(
|
||||
name,
|
||||
VArray<int>::ForFunc(loops.size(), [loops](int64_t index) { return loops[index].e; }));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryDataSource::foreach_default_column_ids(
|
||||
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const
|
||||
{
|
||||
|
@ -109,17 +225,9 @@ void GeometryDataSource::foreach_default_column_ids(
|
|||
fn({(char *)"Scale"}, false);
|
||||
}
|
||||
else if (G.debug_value == 4001 && component_->type() == GEO_COMPONENT_TYPE_MESH) {
|
||||
if (domain_ == ATTR_DOMAIN_EDGE) {
|
||||
fn({(char *)"Vertex 1"}, false);
|
||||
fn({(char *)"Vertex 2"}, false);
|
||||
}
|
||||
else if (domain_ == ATTR_DOMAIN_FACE) {
|
||||
fn({(char *)"Corner Start"}, false);
|
||||
fn({(char *)"Corner Size"}, false);
|
||||
}
|
||||
else if (domain_ == ATTR_DOMAIN_CORNER) {
|
||||
fn({(char *)"Vertex"}, false);
|
||||
fn({(char *)"Edge"}, false);
|
||||
const MeshComponent &component = static_cast<const MeshComponent &>(*component_);
|
||||
if (const Mesh *mesh = component.get_for_read()) {
|
||||
add_mesh_debug_column_names(*mesh, domain_, fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -174,51 +282,9 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
|
|||
else if (G.debug_value == 4001 && component_->type() == GEO_COMPONENT_TYPE_MESH) {
|
||||
const MeshComponent &component = static_cast<const MeshComponent &>(*component_);
|
||||
if (const Mesh *mesh = component.get_for_read()) {
|
||||
const Span<MEdge> edges = mesh->edges();
|
||||
const Span<MPoly> polys = mesh->polys();
|
||||
const Span<MLoop> loops = mesh->loops();
|
||||
|
||||
if (domain_ == ATTR_DOMAIN_EDGE) {
|
||||
if (STREQ(column_id.name, "Vertex 1")) {
|
||||
return std::make_unique<ColumnValues>(
|
||||
column_id.name, VArray<int>::ForFunc(edges.size(), [edges](int64_t index) {
|
||||
return edges[index].v1;
|
||||
}));
|
||||
}
|
||||
if (STREQ(column_id.name, "Vertex 2")) {
|
||||
return std::make_unique<ColumnValues>(
|
||||
column_id.name, VArray<int>::ForFunc(edges.size(), [edges](int64_t index) {
|
||||
return edges[index].v2;
|
||||
}));
|
||||
}
|
||||
}
|
||||
else if (domain_ == ATTR_DOMAIN_FACE) {
|
||||
if (STREQ(column_id.name, "Corner Start")) {
|
||||
return std::make_unique<ColumnValues>(
|
||||
column_id.name, VArray<int>::ForFunc(polys.size(), [polys](int64_t index) {
|
||||
return polys[index].loopstart;
|
||||
}));
|
||||
}
|
||||
if (STREQ(column_id.name, "Corner Size")) {
|
||||
return std::make_unique<ColumnValues>(
|
||||
column_id.name, VArray<int>::ForFunc(polys.size(), [polys](int64_t index) {
|
||||
return polys[index].totloop;
|
||||
}));
|
||||
}
|
||||
}
|
||||
else if (domain_ == ATTR_DOMAIN_CORNER) {
|
||||
if (STREQ(column_id.name, "Vertex")) {
|
||||
return std::make_unique<ColumnValues>(
|
||||
column_id.name, VArray<int>::ForFunc(loops.size(), [loops](int64_t index) {
|
||||
return loops[index].v;
|
||||
}));
|
||||
}
|
||||
if (STREQ(column_id.name, "Edge")) {
|
||||
return std::make_unique<ColumnValues>(
|
||||
column_id.name, VArray<int>::ForFunc(loops.size(), [loops](int64_t index) {
|
||||
return loops[index].e;
|
||||
}));
|
||||
}
|
||||
if (std::unique_ptr<ColumnValues> values = build_mesh_debug_columns(
|
||||
*mesh, domain_, column_id.name)) {
|
||||
return values;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1363,8 +1363,84 @@ static void rotation_set_fn(const wmGizmo * /*gz*/, wmGizmoProperty *gz_prop, co
|
|||
ggd->rotation = *(const float *)value;
|
||||
}
|
||||
|
||||
static void gizmo_3d_setup_default_matrix(wmGizmo *axis, const int axis_idx)
|
||||
{
|
||||
float matrix[3][3];
|
||||
|
||||
switch (axis_idx) {
|
||||
/* Arrow. */
|
||||
case MAN_AXIS_TRANS_X:
|
||||
case MAN_AXIS_SCALE_X:
|
||||
case MAN_AXIS_ROT_X:
|
||||
copy_v3_fl3(matrix[0], 0.0f, -1.0f, 0.0f);
|
||||
copy_v3_fl3(matrix[1], 0.0f, 0.0f, -1.0f);
|
||||
copy_v3_fl3(matrix[2], 1.0f, 0.0f, 0.0f);
|
||||
break;
|
||||
case MAN_AXIS_TRANS_Y:
|
||||
case MAN_AXIS_SCALE_Y:
|
||||
case MAN_AXIS_ROT_Y:
|
||||
copy_v3_fl3(matrix[0], 1.0f, 0.0f, 0.0f);
|
||||
copy_v3_fl3(matrix[1], 0.0f, 0.0f, -1.0f);
|
||||
copy_v3_fl3(matrix[2], 0.0f, 1.0f, 0.0f);
|
||||
break;
|
||||
case MAN_AXIS_TRANS_Z:
|
||||
case MAN_AXIS_SCALE_Z:
|
||||
case MAN_AXIS_ROT_Z:
|
||||
copy_v3_fl3(matrix[0], 1.0f, 0.0f, 0.0f);
|
||||
copy_v3_fl3(matrix[1], 0.0f, 1.0f, 0.0f);
|
||||
copy_v3_fl3(matrix[2], 0.0f, 0.0f, 1.0f);
|
||||
break;
|
||||
|
||||
case MAN_AXIS_TRANS_XY:
|
||||
case MAN_AXIS_SCALE_XY:
|
||||
copy_v3_fl3(matrix[0], MAN_AXIS_SCALE_PLANE_SCALE, 0.0f, 0.0f);
|
||||
copy_v3_fl3(matrix[1], 0.0f, MAN_AXIS_SCALE_PLANE_SCALE, 0.0f);
|
||||
copy_v3_fl3(matrix[2], 0.0f, 0.0f, MAN_AXIS_SCALE_PLANE_SCALE);
|
||||
break;
|
||||
case MAN_AXIS_TRANS_YZ:
|
||||
case MAN_AXIS_SCALE_YZ:
|
||||
copy_v3_fl3(matrix[0], 0.0f, 0.0f, MAN_AXIS_SCALE_PLANE_SCALE);
|
||||
copy_v3_fl3(matrix[1], 0.0f, MAN_AXIS_SCALE_PLANE_SCALE, 0.0f);
|
||||
copy_v3_fl3(matrix[2], -MAN_AXIS_SCALE_PLANE_SCALE, 0.0f, 0.0f);
|
||||
break;
|
||||
case MAN_AXIS_TRANS_ZX:
|
||||
case MAN_AXIS_SCALE_ZX:
|
||||
copy_v3_fl3(matrix[0], MAN_AXIS_SCALE_PLANE_SCALE, 0.0f, 0.0f);
|
||||
copy_v3_fl3(matrix[1], 0.0f, 0.0f, MAN_AXIS_SCALE_PLANE_SCALE);
|
||||
copy_v3_fl3(matrix[2], 0.0f, -MAN_AXIS_SCALE_PLANE_SCALE, 0.0f);
|
||||
break;
|
||||
|
||||
case MAN_AXIS_TRANS_C:
|
||||
case MAN_AXIS_SCALE_C:
|
||||
case MAN_AXIS_ROT_C:
|
||||
case MAN_AXIS_ROT_T:
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
copy_m4_m3(axis->matrix_offset, matrix);
|
||||
|
||||
switch (axis_idx) {
|
||||
case MAN_AXIS_TRANS_XY:
|
||||
case MAN_AXIS_TRANS_YZ:
|
||||
case MAN_AXIS_TRANS_ZX:
|
||||
case MAN_AXIS_SCALE_XY:
|
||||
case MAN_AXIS_SCALE_YZ:
|
||||
case MAN_AXIS_SCALE_ZX: {
|
||||
float offs[3];
|
||||
add_v3_v3v3(offs, axis->matrix_offset[0], axis->matrix_offset[1]);
|
||||
mul_v3_fl(offs, MAN_AXIS_SCALE_PLANE_OFFSET);
|
||||
WM_gizmo_set_matrix_offset_location(axis, offs);
|
||||
} break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void gizmo_3d_setup_draw_default(wmGizmo *axis, const int axis_idx)
|
||||
{
|
||||
gizmo_3d_setup_default_matrix(axis, axis_idx);
|
||||
|
||||
switch (axis_idx) {
|
||||
/* Arrow. */
|
||||
case MAN_AXIS_TRANS_X:
|
||||
|
@ -1385,15 +1461,10 @@ static void gizmo_3d_setup_draw_default(wmGizmo *axis, const int axis_idx)
|
|||
case MAN_AXIS_TRANS_ZX:
|
||||
case MAN_AXIS_SCALE_XY:
|
||||
case MAN_AXIS_SCALE_YZ:
|
||||
case MAN_AXIS_SCALE_ZX: {
|
||||
case MAN_AXIS_SCALE_ZX:
|
||||
RNA_enum_set(axis->ptr, "draw_style", ED_GIZMO_PRIMITIVE_STYLE_PLANE);
|
||||
|
||||
const float ofs[3] = {MAN_AXIS_SCALE_PLANE_OFFSET, MAN_AXIS_SCALE_PLANE_OFFSET, 0.0f};
|
||||
WM_gizmo_set_scale(axis, MAN_AXIS_SCALE_PLANE_SCALE);
|
||||
WM_gizmo_set_matrix_offset_location(axis, ofs);
|
||||
WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_OFFSET_SCALE, true);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Dial. */
|
||||
case MAN_AXIS_TRANS_C:
|
||||
|
@ -1489,12 +1560,12 @@ static void gizmo_3d_setup_draw_from_twtype(wmGizmo *axis, const int axis_idx, c
|
|||
case MAN_AXIS_SCALE_X:
|
||||
case MAN_AXIS_SCALE_Y:
|
||||
case MAN_AXIS_SCALE_Z: {
|
||||
float start_co[3] = {0.0f, 0.0f, 0.0f};
|
||||
float start;
|
||||
float end;
|
||||
gizmo_line_range(twtype, axis_type, &start_co[2], &end);
|
||||
gizmo_line_range(twtype, axis_type, &start, &end);
|
||||
madd_v3_v3fl(axis->matrix_offset[3], axis->matrix_offset[2], start);
|
||||
|
||||
WM_gizmo_set_matrix_offset_location(axis, start_co);
|
||||
RNA_float_set(axis->ptr, "length", end - start_co[2]);
|
||||
RNA_float_set(axis->ptr, "length", end - start);
|
||||
WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_OFFSET_SCALE, true);
|
||||
break;
|
||||
}
|
||||
|
@ -1803,61 +1874,49 @@ static void gizmo_refresh_from_matrix(wmGizmo *axis,
|
|||
const short axis_type = gizmo_get_axis_type(axis_idx);
|
||||
const int aidx_norm = gizmo_orientation_axis(axis_idx, nullptr);
|
||||
|
||||
WM_gizmo_set_matrix_location(axis, twmat[3]);
|
||||
switch (axis_idx) {
|
||||
case MAN_AXIS_TRANS_X:
|
||||
case MAN_AXIS_TRANS_Y:
|
||||
case MAN_AXIS_TRANS_Z:
|
||||
case MAN_AXIS_SCALE_X:
|
||||
case MAN_AXIS_SCALE_Y:
|
||||
case MAN_AXIS_SCALE_Z: {
|
||||
const float *z_axis = twmat[aidx_norm];
|
||||
if (axis_type == MAN_AXES_SCALE) {
|
||||
/* Scale handles are cubes that don't look right when not aligned with other axes.
|
||||
* This is noticeable when the axis is rotated to something besides the global-axis. */
|
||||
const int aidx_norm_y = (aidx_norm + 2) % 3;
|
||||
const float *y_axis = twmat[aidx_norm_y];
|
||||
WM_gizmo_set_matrix_rotation_from_yz_axis(axis, y_axis, z_axis);
|
||||
|
||||
if (scale) {
|
||||
float start, end;
|
||||
gizmo_line_range(twtype, axis_type, &start, &end);
|
||||
RNA_float_set(axis->ptr, "length", (end * scale[aidx_norm]) - start);
|
||||
}
|
||||
}
|
||||
else {
|
||||
WM_gizmo_set_matrix_rotation_from_z_axis(axis, z_axis);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case MAN_AXIS_ROT_X:
|
||||
case MAN_AXIS_ROT_Y:
|
||||
case MAN_AXIS_ROT_Z:
|
||||
WM_gizmo_set_matrix_rotation_from_z_axis(axis, twmat[aidx_norm]);
|
||||
break;
|
||||
case MAN_AXIS_TRANS_XY:
|
||||
case MAN_AXIS_TRANS_YZ:
|
||||
case MAN_AXIS_TRANS_ZX:
|
||||
case MAN_AXIS_SCALE_XY:
|
||||
case MAN_AXIS_SCALE_YZ:
|
||||
case MAN_AXIS_SCALE_ZX: {
|
||||
const int aidx_norm_x = (aidx_norm + 1) % 3;
|
||||
const int aidx_norm_y = (aidx_norm + 2) % 3;
|
||||
const float *y_axis = twmat[aidx_norm_y];
|
||||
const float *z_axis = twmat[aidx_norm];
|
||||
WM_gizmo_set_matrix_rotation_from_yz_axis(axis, y_axis, z_axis);
|
||||
|
||||
if (axis_type == MAN_AXES_SCALE) {
|
||||
float ofs[3] = {MAN_AXIS_SCALE_PLANE_OFFSET, MAN_AXIS_SCALE_PLANE_OFFSET, 0.0f};
|
||||
if (scale) {
|
||||
ofs[0] *= scale[aidx_norm_x];
|
||||
ofs[1] *= scale[aidx_norm_y];
|
||||
}
|
||||
WM_gizmo_set_matrix_offset_location(axis, ofs);
|
||||
case MAN_AXIS_SCALE_ZX:
|
||||
copy_m4_m4(axis->matrix_basis, twmat);
|
||||
if (scale) {
|
||||
float offs[3];
|
||||
add_v3_v3v3(offs, axis->matrix_offset[0], axis->matrix_offset[1]);
|
||||
mul_v3_fl(offs, MAN_AXIS_SCALE_PLANE_OFFSET);
|
||||
mul_v3_v3(offs, scale);
|
||||
WM_gizmo_set_matrix_offset_location(axis, offs);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MAN_AXIS_SCALE_X:
|
||||
case MAN_AXIS_SCALE_Y:
|
||||
case MAN_AXIS_SCALE_Z:
|
||||
copy_m4_m4(axis->matrix_basis, twmat);
|
||||
if (scale) {
|
||||
float start, end;
|
||||
gizmo_line_range(twtype, axis_type, &start, &end);
|
||||
RNA_float_set(axis->ptr, "length", (end - start) * scale[aidx_norm]);
|
||||
}
|
||||
break;
|
||||
case MAN_AXIS_TRANS_X:
|
||||
case MAN_AXIS_TRANS_Y:
|
||||
case MAN_AXIS_TRANS_Z:
|
||||
copy_m4_m4(axis->matrix_basis, twmat);
|
||||
break;
|
||||
case MAN_AXIS_ROT_X:
|
||||
case MAN_AXIS_ROT_Y:
|
||||
case MAN_AXIS_ROT_Z:
|
||||
copy_m4_m4(axis->matrix_basis, twmat);
|
||||
orthogonalize_m4(axis->matrix_basis, aidx_norm);
|
||||
break;
|
||||
case MAN_AXIS_SCALE_C:
|
||||
case MAN_AXIS_ROT_C:
|
||||
case MAN_AXIS_ROT_T:
|
||||
default:
|
||||
WM_gizmo_set_matrix_location(axis, twmat[3]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2076,12 +2135,16 @@ static void gizmo_3d_draw_invoke(wmGizmoGroup *gzgroup,
|
|||
}
|
||||
|
||||
if (axis == axis_active) {
|
||||
if (axis_active_type == MAN_AXES_ROTATE) {
|
||||
gizmo_3d_dial_matrixbasis_calc(region,
|
||||
axis_active->matrix_basis[2],
|
||||
axis_active->matrix_basis[3],
|
||||
mval,
|
||||
axis_active->matrix_basis);
|
||||
if (axis_active_type == MAN_AXES_ROTATE && axis_idx_active != MAN_AXIS_ROT_T) {
|
||||
float mat[3][3];
|
||||
mul_m3_m4m4(mat, axis_active->matrix_basis, axis_active->matrix_offset);
|
||||
gizmo_3d_dial_matrixbasis_calc(
|
||||
region, mat[2], axis_active->matrix_basis[3], mval, axis_active->matrix_offset);
|
||||
|
||||
copy_m3_m4(mat, axis_active->matrix_basis);
|
||||
invert_m3(mat);
|
||||
mul_m4_m3m4(axis_active->matrix_offset, mat, axis_active->matrix_offset);
|
||||
zero_v3(axis_active->matrix_offset[3]);
|
||||
}
|
||||
|
||||
gizmo_3d_setup_draw_modal(axis_active, axis_idx);
|
||||
|
|
|
@ -1499,7 +1499,7 @@ static Mesh *create_merged_mesh(const Mesh &mesh,
|
|||
const int totedge = mesh.totedge;
|
||||
|
||||
/* Reuse the same buffer as #vert_dest_map.
|
||||
* Note: the caller must be made aware of it changes. */
|
||||
* NOTE: the caller must be made aware of it changes. */
|
||||
MutableSpan<int> vert_group_map = vert_dest_map;
|
||||
|
||||
WeldMesh weld_mesh;
|
||||
|
|
|
@ -1895,7 +1895,7 @@ static void lineart_sort_adjacent_items(LineartAdjacentEdge *ai, int length)
|
|||
ai, ai + length, [](const LineartAdjacentEdge &p1, const LineartAdjacentEdge &p2) {
|
||||
int a = p1.v1 - p2.v1;
|
||||
int b = p1.v2 - p2.v2;
|
||||
/* parallel_sort() requires cmp() to return true when the first element needs to appear
|
||||
/* `parallel_sort()` requires `cmp()` to return true when the first element needs to appear
|
||||
* before the second element in the sorted array, false otherwise (strict weak ordering),
|
||||
* see https://en.cppreference.com/w/cpp/named_req/Compare. */
|
||||
if (a < 0) {
|
||||
|
|
|
@ -346,7 +346,7 @@ void gpu_shader_create_info_init()
|
|||
overlay_motion_path_line_clipped = overlay_motion_path_line_clipped_no_geom;
|
||||
|
||||
/* Workbench shadows.
|
||||
* Note: Updates additional-info used by workbench shadow permutations.
|
||||
* NOTE: Updates additional-info used by workbench shadow permutations.
|
||||
* Must be prepared prior to permutation preparation. */
|
||||
workbench_shadow_manifold = workbench_shadow_manifold_no_geom;
|
||||
workbench_shadow_no_manifold = workbench_shadow_no_manifold_no_geom;
|
||||
|
|
|
@ -1754,9 +1754,8 @@ void MTLFrameBuffer::blit(uint read_slot,
|
|||
uint height,
|
||||
eGPUFrameBufferBits blit_buffers)
|
||||
{
|
||||
BLI_assert(this);
|
||||
BLI_assert(metal_fb_write);
|
||||
if (!(this && metal_fb_write)) {
|
||||
if (!metal_fb_write) {
|
||||
return;
|
||||
}
|
||||
MTLContext *mtl_context = reinterpret_cast<MTLContext *>(GPU_context_active_get());
|
||||
|
@ -1899,4 +1898,4 @@ int MTLFrameBuffer::get_height()
|
|||
return height_;
|
||||
}
|
||||
|
||||
} // blender::gpu
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -339,7 +339,7 @@ void MTLImmediate::end()
|
|||
} break;
|
||||
case GPU_PRIM_LINE_LOOP: {
|
||||
/* Patch final vertex of line loop to close. Rendered using LineStrip.
|
||||
* Note: vertex_len represents original length, however, allocated Metal
|
||||
* NOTE: vertex_len represents original length, however, allocated Metal
|
||||
* buffer contains space for one extra vertex when LineLoop is used. */
|
||||
uchar *buffer_data = reinterpret_cast<uchar *>(current_allocation_.data);
|
||||
memcpy(buffer_data + (vertex_len)*vertex_format.stride,
|
||||
|
|
|
@ -41,7 +41,18 @@ void GLIndexBuf::bind()
|
|||
|
||||
void GLIndexBuf::bind_as_ssbo(uint binding)
|
||||
{
|
||||
bind();
|
||||
if (ibo_id_ == 0 || data_ != nullptr) {
|
||||
/* Calling `glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_id_)` changes the index buffer
|
||||
* of the currently bound VAO.
|
||||
*
|
||||
* In the OpenGL backend, the VAO state persists even after `GLVertArray::update_bindings`
|
||||
* is called.
|
||||
*
|
||||
* NOTE: For safety, we could call `glBindVertexArray(0)` right after drawing a `GPUBatch`.
|
||||
* However, for performance reasons, we have chosen not to do so. */
|
||||
glBindVertexArray(0);
|
||||
bind();
|
||||
}
|
||||
BLI_assert(ibo_id_ != 0);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, ibo_id_);
|
||||
}
|
||||
|
|
|
@ -640,6 +640,10 @@ void VKShader::compute_shader_from_glsl(MutableSpan<const char *> sources)
|
|||
build_shader_module(sources, shaderc_compute_shader, &compute_module_);
|
||||
}
|
||||
|
||||
void VKShader::warm_cache(int /*limit*/)
|
||||
{
|
||||
}
|
||||
|
||||
bool VKShader::finalize(const shader::ShaderCreateInfo *info)
|
||||
{
|
||||
if (compilation_failed_) {
|
||||
|
|
|
@ -35,7 +35,7 @@ class VKShader : public Shader {
|
|||
void fragment_shader_from_glsl(MutableSpan<const char *> sources) override;
|
||||
void compute_shader_from_glsl(MutableSpan<const char *> sources) override;
|
||||
bool finalize(const shader::ShaderCreateInfo *info = nullptr) override;
|
||||
void warm_cache(int limit) override{};
|
||||
void warm_cache(int limit) override;
|
||||
|
||||
void transform_feedback_names_set(Span<const char *> name_list,
|
||||
eGPUShaderTFBType geom_type) override;
|
||||
|
|
|
@ -85,7 +85,7 @@
|
|||
.type = TEX_IMAGE, \
|
||||
.ima = NULL, \
|
||||
.stype = 0, \
|
||||
.flag = TEX_CHECKER_ODD, \
|
||||
.flag = TEX_CHECKER_ODD | TEX_NO_CLAMP, \
|
||||
.imaflag = TEX_INTERPOL | TEX_MIPMAP | TEX_USEALPHA, \
|
||||
.extend = TEX_REPEAT, \
|
||||
.cropxmin = 0.0, \
|
||||
|
|
|
@ -1190,11 +1190,11 @@ static void rna_ParticleTarget_name_get(PointerRNA *ptr, char *str)
|
|||
}
|
||||
}
|
||||
else {
|
||||
strcpy(str, "Invalid target!");
|
||||
strcpy(str, TIP_("Invalid target!"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
strcpy(str, "Invalid target!");
|
||||
strcpy(str, TIP_("Invalid target!"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -379,6 +379,10 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
|
|||
|
||||
result = BKE_mesh_new_nomain_from_template(
|
||||
mesh, int(maxVerts), int(maxEdges), 0, int(maxPolys) * 4, int(maxPolys));
|
||||
/* The modifier doesn't support original index mapping on the edge or face domains. Remove
|
||||
* original index layers, since otherwise edges aren't displayed at all in wireframe view. */
|
||||
CustomData_free_layers(&result->edata, CD_ORIGINDEX, result->totedge);
|
||||
CustomData_free_layers(&result->pdata, CD_ORIGINDEX, result->totedge);
|
||||
|
||||
const float(*vert_positions_orig)[3] = BKE_mesh_vert_positions(mesh);
|
||||
const MEdge *medge_orig = BKE_mesh_edges(mesh);
|
||||
|
|
|
@ -93,7 +93,7 @@ static void node_init(bNodeTree * /*tree*/, bNode *node)
|
|||
|
||||
class SocketSearchOp {
|
||||
public:
|
||||
std::string socket_name;
|
||||
const StringRef socket_name;
|
||||
eNodeSocketDatatype data_type;
|
||||
NodeCompareOperation operation;
|
||||
NodeCompareMode mode = NODE_COMPARE_MODE_ELEMENT;
|
||||
|
@ -107,40 +107,62 @@ class SocketSearchOp {
|
|||
}
|
||||
};
|
||||
|
||||
static std::optional<eNodeSocketDatatype> get_compare_type_for_operation(
|
||||
const eNodeSocketDatatype type, const NodeCompareOperation operation)
|
||||
{
|
||||
switch (type) {
|
||||
case SOCK_BOOLEAN:
|
||||
if (ELEM(operation, NODE_COMPARE_COLOR_BRIGHTER, NODE_COMPARE_COLOR_DARKER)) {
|
||||
return SOCK_RGBA;
|
||||
}
|
||||
return SOCK_INT;
|
||||
case SOCK_INT:
|
||||
case SOCK_FLOAT:
|
||||
case SOCK_VECTOR:
|
||||
if (ELEM(operation, NODE_COMPARE_COLOR_BRIGHTER, NODE_COMPARE_COLOR_DARKER)) {
|
||||
return SOCK_RGBA;
|
||||
}
|
||||
return type;
|
||||
case SOCK_RGBA:
|
||||
if (!ELEM(operation,
|
||||
NODE_COMPARE_COLOR_BRIGHTER,
|
||||
NODE_COMPARE_COLOR_DARKER,
|
||||
NODE_COMPARE_EQUAL,
|
||||
NODE_COMPARE_NOT_EQUAL)) {
|
||||
return SOCK_VECTOR;
|
||||
}
|
||||
return type;
|
||||
case SOCK_STRING:
|
||||
if (!ELEM(operation, NODE_COMPARE_EQUAL, NODE_COMPARE_NOT_EQUAL)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return type;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms)
|
||||
{
|
||||
const eNodeSocketDatatype type = static_cast<eNodeSocketDatatype>(params.other_socket().type);
|
||||
if (!ELEM(type, SOCK_BOOLEAN, SOCK_FLOAT, SOCK_RGBA, SOCK_VECTOR, SOCK_INT, SOCK_STRING)) {
|
||||
const eNodeSocketDatatype type = eNodeSocketDatatype(params.other_socket().type);
|
||||
if (!ELEM(type, SOCK_INT, SOCK_BOOLEAN, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_STRING)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const eNodeSocketDatatype mode_type = (type == SOCK_BOOLEAN) ? SOCK_INT : type;
|
||||
const bool string_type = (type == SOCK_STRING);
|
||||
|
||||
const std::string socket_name = params.in_out() == SOCK_IN ? "A" : "Result";
|
||||
|
||||
const StringRef socket_name = params.in_out() == SOCK_IN ? "A" : "Result";
|
||||
for (const EnumPropertyItem *item = rna_enum_node_compare_operation_items;
|
||||
item->identifier != nullptr;
|
||||
item++) {
|
||||
if (item->name != nullptr && item->identifier[0] != '\0') {
|
||||
if (!string_type &&
|
||||
ELEM(item->value, NODE_COMPARE_COLOR_BRIGHTER, NODE_COMPARE_COLOR_DARKER)) {
|
||||
params.add_item(IFACE_(item->name),
|
||||
SocketSearchOp{socket_name,
|
||||
SOCK_RGBA,
|
||||
static_cast<NodeCompareOperation>(item->value)});
|
||||
}
|
||||
else if ((!string_type) ||
|
||||
(string_type && ELEM(item->value, NODE_COMPARE_EQUAL, NODE_COMPARE_NOT_EQUAL))) {
|
||||
params.add_item(IFACE_(item->name),
|
||||
SocketSearchOp{socket_name,
|
||||
mode_type,
|
||||
static_cast<NodeCompareOperation>(item->value)});
|
||||
const NodeCompareOperation operation = NodeCompareOperation(item->value);
|
||||
if (const std::optional<eNodeSocketDatatype> fixed_type = get_compare_type_for_operation(
|
||||
type, operation)) {
|
||||
params.add_item(IFACE_(item->name), SocketSearchOp{socket_name, *fixed_type, operation});
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Add Angle socket. */
|
||||
if (!string_type && params.in_out() == SOCK_IN) {
|
||||
|
||||
if (params.in_out() != SOCK_IN && type != SOCK_STRING) {
|
||||
params.add_item(
|
||||
IFACE_("Angle"),
|
||||
SocketSearchOp{
|
||||
|
|
|
@ -227,6 +227,72 @@ static PyObject *pygpu_state_viewport_get(PyObject *UNUSED(self), PyObject *UNUS
|
|||
return ret;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(pygpu_state_scissor_set_doc,
|
||||
".. function:: scissor_set(x, y, xsize, ysize)\n"
|
||||
"\n"
|
||||
" Specifies the scissor area of the active framebuffer.\n"
|
||||
" Note: The scissor state is not saved upon framebuffer rebind.\n"
|
||||
"\n"
|
||||
" :arg x, y: lower left corner of the scissor rectangle, in pixels.\n"
|
||||
" :type x, y: int\n"
|
||||
" :arg xsize, ysize: width and height of the scissor rectangle.\n"
|
||||
" :type xsize, ysize: int\n");
|
||||
static PyObject *pygpu_state_scissor_set(PyObject *UNUSED(self), PyObject *args)
|
||||
{
|
||||
int x, y, xsize, ysize;
|
||||
if (!PyArg_ParseTuple(args, "iiii:scissor_set", &x, &y, &xsize, &ysize)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GPU_scissor(x, y, xsize, ysize);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(pygpu_state_scissor_get_doc,
|
||||
".. function:: scissor_get()\n"
|
||||
"\n"
|
||||
" Retrieve the scissors of the active framebuffer.\n"
|
||||
" Note: Only valid between 'scissor_set' and a framebuffer rebind.\n"
|
||||
"\n"
|
||||
" :return: The scissor of the active framebuffer as a tuple\n"
|
||||
" (x, y, xsize, ysize).\n"
|
||||
" x, y: lower left corner of the scissor rectangle, in pixels.\n"
|
||||
" xsize, ysize: width and height of the scissor rectangle.\n"
|
||||
" :rtype: tuple(int, int, int, int)\n");
|
||||
static PyObject *pygpu_state_scissor_get(PyObject *UNUSED(self), PyObject *UNUSED(args))
|
||||
{
|
||||
int scissor[4];
|
||||
GPU_scissor_get(scissor);
|
||||
|
||||
PyObject *ret = PyTuple_New(4);
|
||||
PyTuple_SET_ITEMS(ret,
|
||||
PyLong_FromLong(scissor[0]),
|
||||
PyLong_FromLong(scissor[1]),
|
||||
PyLong_FromLong(scissor[2]),
|
||||
PyLong_FromLong(scissor[3]));
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(pygpu_state_scissor_test_set_doc,
|
||||
".. function:: scissor_test_set(enable)\n"
|
||||
"\n"
|
||||
" Enable/disable scissor testing on the active framebuffer.\n"
|
||||
"\n"
|
||||
" :arg enable:\n"
|
||||
" True - enable scissor testing.\n"
|
||||
" False - disable scissor testing.\n"
|
||||
" :type enable: bool\n");
|
||||
static PyObject *pygpu_state_scissor_test_set(PyObject *UNUSED(self), PyObject *value)
|
||||
{
|
||||
bool enabled;
|
||||
if (!PyC_ParseBool(value, &enabled)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GPU_scissor_test(enabled);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(pygpu_state_line_width_set_doc,
|
||||
".. function:: line_width_set(width)\n"
|
||||
"\n"
|
||||
|
@ -394,6 +460,18 @@ static struct PyMethodDef pygpu_state__tp_methods[] = {
|
|||
(PyCFunction)pygpu_state_viewport_get,
|
||||
METH_NOARGS,
|
||||
pygpu_state_viewport_get_doc},
|
||||
{"scissor_set",
|
||||
(PyCFunction)pygpu_state_scissor_set,
|
||||
METH_VARARGS,
|
||||
pygpu_state_scissor_set_doc},
|
||||
{"scissor_get",
|
||||
(PyCFunction)pygpu_state_scissor_get,
|
||||
METH_NOARGS,
|
||||
pygpu_state_scissor_get_doc},
|
||||
{"scissor_test_set",
|
||||
(PyCFunction)pygpu_state_scissor_test_set,
|
||||
METH_VARARGS,
|
||||
pygpu_state_scissor_test_set_doc},
|
||||
{"line_width_set",
|
||||
(PyCFunction)pygpu_state_line_width_set,
|
||||
METH_O,
|
||||
|
|
|
@ -394,7 +394,7 @@ PyTypeObject PyKDTree_Type = {
|
|||
/*tp_setattro*/ NULL,
|
||||
/*tp_as_buffer*/ NULL,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*Documentation string*/ py_KDtree_doc,
|
||||
/*tp_doc*/ py_KDtree_doc,
|
||||
/*tp_traverse*/ NULL,
|
||||
/*tp_clear*/ NULL,
|
||||
/*tp_richcompare*/ NULL,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue