diff options
198 files changed, 4630 insertions, 2286 deletions
diff --git a/build_files/build_environment/cmake/harvest.cmake b/build_files/build_environment/cmake/harvest.cmake index 5b5c254b150..5fb62e330af 100644 --- a/build_files/build_environment/cmake/harvest.cmake +++ b/build_files/build_environment/cmake/harvest.cmake @@ -62,14 +62,8 @@ if(BUILD_MODE STREQUAL Debug) # OpenImageIO COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/openimageio/lib/OpenImageIO.lib ${HARVEST_TARGET}/openimageio/lib/OpenImageIO_d.lib && ${CMAKE_COMMAND} -E copy ${LIBDIR}/openimageio/lib/OpenImageIO_Util.lib ${HARVEST_TARGET}/openimageio/lib/OpenImageIO_Util_d.lib && - # python - ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/python/ ${HARVEST_TARGET}/python/ && # hdf5 ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/hdf5/lib ${HARVEST_TARGET}/hdf5/lib && - # numpy - ${CMAKE_COMMAND} -E copy ${LIBDIR}/python${PYTHON_SHORT_VERSION_NO_DOTS}_numpy_${NUMPY_SHORT_VERSION}d.tar.gz ${HARVEST_TARGET}/Release/python${PYTHON_SHORT_VERSION_NO_DOTS}_numpy_${NUMPY_SHORT_VERSION}d.tar.gz && - # python - ${CMAKE_COMMAND} -E copy ${LIBDIR}/python${PYTHON_SHORT_VERSION_NO_DOTS}_d.tar.gz ${HARVEST_TARGET}/Release/python${PYTHON_SHORT_VERSION_NO_DOTS}_d.tar.gz DEPENDS Package_Python ) endif() diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index dd15fb0d2ed..bc9ee802810 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -19,7 +19,32 @@ # A shell script installing/building all needed dependencies to build Blender, for some Linux distributions. -##### Args and Help Handling ##### +# ---------------------------------------------------------------------------- +# Debugging Helpers +# +# Use for developing this script (keep first). + +# Useful for debugging this script: +USE_DEBUG_TRAP=${USE_DEBUG_TRAP:-0} +USE_DEBUG_LOG=${USE_DEBUG_LOG:-0} + +# Print the line that exits. +if [ $USE_DEBUG_TRAP -ne 0 ]; then + err_report() { + echo "Error on line $1" + exit 1 + } + trap 'err_report $LINENO' ERR +fi + +# Noisy, show every line that runs with it's line number. +if [ $USE_DEBUG_LOG -ne 0 ]; then + PS4='\e[0;33m$(printf %4d ${LINENO}):\e\033[0m ' + set -x +fi + +# ---------------------------------------------------------------------------- +# Args and Help Handling # Parse command line! ARGS=$( \ @@ -305,7 +330,8 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS: --skip-ffmpeg Unconditionally skip FFMpeg installation/building.\"" -##### Main Vars ##### +# ---------------------------------------------------------------------------- +# Main Vars DO_SHOW_DEPS=false @@ -447,7 +473,8 @@ LANG_BACK=$LANG LANG="" export LANG -##### Generic Helpers ##### +# ---------------------------------------------------------------------------- +# Generic Helpers BLACK=$(tput setaf 0) RED=$(tput setaf 1) @@ -489,7 +516,8 @@ PRINT() { _echo "$@" } -##### Args Handling ##### +# ---------------------------------------------------------------------------- +# Args Handling # Finish parsing the commandline args. eval set -- "$ARGS" @@ -892,7 +920,8 @@ CXXFLAGS_BACK=$CXXFLAGS CXXFLAGS="$CXXFLAGS -std=c++11" export CXXFLAGS -#### Show Dependencies #### +# ---------------------------------------------------------------------------- +# Show Dependencies # Need those to be after we defined versions... DEPS_COMMON_INFO="\"COMMON DEPENDENCIES: @@ -940,9 +969,8 @@ if [ "$DO_SHOW_DEPS" = true ]; then exit 0 fi - - -##### Generic Helpers ##### +# ---------------------------------------------------------------------------- +# Generic Helpers # Check return code of wget for success... download() { @@ -965,15 +993,25 @@ download() { fi } +version_sanitize() { + # Remove suffix such as '1.3_RC2', keeping only '1.3'. + # Needed for numeric comparisons. + local val=$(sed -r 's/^([^_]+).*/\1/' <<< "$1") + # Remove trailing punctuation such as '1.0.', keeping only '1.0'. + val=$(sed -r 's/[[:punct:]]*$//g' <<< "$val") + echo $val +} + # Return 0 if $1 = $2 (i.e. 1.01.0 = 1.1, but 1.1.1 != 1.1), else 1. # $1 and $2 should be version numbers made of numbers only. version_eq() { - backIFS=$IFS - IFS='.' + local VER_1=$(version_sanitize "$1") + local VER_2=$(version_sanitize "$2") + local IFS='.' # Split both version numbers into their numeric elements. - arr1=( $1 ) - arr2=( $2 ) + arr1=( $VER_1 ) + arr2=( $VER_2 ) ret=1 @@ -983,8 +1021,8 @@ version_eq() { _t=$count1 count1=$count2 count2=$_t - arr1=( $2 ) - arr2=( $1 ) + arr1=( $VER_2 ) + arr2=( $VER_1 ) fi ret=0 @@ -1004,7 +1042,6 @@ version_eq() { fi done - IFS=$backIFS return $ret } @@ -1035,12 +1072,13 @@ version_ge_lt() { # $1 and $2 should be version numbers made of numbers only. # $1 should be at least as long as $2! version_match() { - backIFS=$IFS - IFS='.' + local VER_1=$(version_sanitize "$1") + local VER_2=$(version_sanitize "$2") + local IFS='.' # Split both version numbers into their numeric elements. - arr1=( $1 ) - arr2=( $2 ) + arr1=( $VER_1 ) + arr2=( $VER_2 ) ret=1 @@ -1057,11 +1095,12 @@ version_match() { done fi - IFS=$backIFS return $ret } -##### Generic compile helpers ##### +# ---------------------------------------------------------------------------- +# Generic compile helpers + prepare_opt() { INFO "Ensuring $INST exists and is writable by us" if [ ! $SUDO ]; then @@ -1123,7 +1162,9 @@ run_ldconfig() { PRINT "" } -#### Build Python #### +# ---------------------------------------------------------------------------- +# Build Python + _init_python() { _src=$SRC/Python-$PYTHON_VERSION _git=false @@ -1192,7 +1233,9 @@ compile_Python() { fi } -##### Build Numpy ##### +# ---------------------------------------------------------------------------- +# Build Numpy + _init_numpy() { _src=$SRC/numpy-$NUMPY_VERSION _git=false @@ -1259,7 +1302,9 @@ compile_Numpy() { fi } -#### Build Boost #### +# ---------------------------------------------------------------------------- +# Build Boost + _init_boost() { _src=$SRC/boost-$BOOST_VERSION _git=false @@ -1337,7 +1382,9 @@ compile_Boost() { run_ldconfig "boost" } -#### Build OCIO #### +# ---------------------------------------------------------------------------- +# Build OCIO + _init_ocio() { _src=$SRC/OpenColorIO-$OCIO_VERSION if [ "$OCIO_USE_REPO" = true ]; then @@ -1452,7 +1499,9 @@ compile_OCIO() { run_ldconfig "ocio" } -#### Build ILMBase #### +# ---------------------------------------------------------------------------- +# Build ILMBase + _init_ilmbase() { _src=$SRC/ILMBase-$ILMBASE_VERSION _git=false @@ -1543,7 +1592,9 @@ compile_ILMBASE() { magic_compile_set ilmbase-$ILMBASE_VERSION $ilmbase_magic } -#### Build OpenEXR #### +# ---------------------------------------------------------------------------- +# Build OpenEXR + _init_openexr() { _src=$SRC/OpenEXR-$OPENEXR_VERSION _git=true @@ -1663,7 +1714,9 @@ compile_OPENEXR() { run_ldconfig "openexr" } -#### Build OIIO #### +# ---------------------------------------------------------------------------- +# Build OIIO + _init_oiio() { _src=$SRC/OpenImageIO-$OIIO_VERSION _git=true @@ -1804,7 +1857,9 @@ compile_OIIO() { run_ldconfig "oiio" } -#### Build LLVM #### +# ---------------------------------------------------------------------------- +# Build LLVM + _init_llvm() { _src=$SRC/LLVM-$LLVM_VERSION _src_clang=$SRC/CLANG-$LLVM_VERSION @@ -1904,7 +1959,9 @@ compile_LLVM() { fi } -#### Build OSL #### +# ---------------------------------------------------------------------------- +# Build OSL + _init_osl() { _src=$SRC/OpenShadingLanguage-$OSL_VERSION _git=true @@ -2034,7 +2091,9 @@ compile_OSL() { run_ldconfig "osl" } -#### Build OSD #### +# ---------------------------------------------------------------------------- +# Build OSD + _init_osd() { _src=$SRC/OpenSubdiv-$OSD_VERSION _git=true @@ -2131,7 +2190,9 @@ compile_OSD() { run_ldconfig "osd" } -#### Build Blosc #### +# ---------------------------------------------------------------------------- +# Build Blosc + _init_blosc() { _src=$SRC/c-blosc-$OPENVDB_BLOSC_VERSION _git=false @@ -2218,7 +2279,9 @@ compile_BLOSC() { run_ldconfig "blosc" } -#### Build OpenVDB #### +# ---------------------------------------------------------------------------- +# Build OpenVDB + _init_openvdb() { _src=$SRC/openvdb-$OPENVDB_VERSION _git=false @@ -2319,7 +2382,9 @@ compile_OPENVDB() { run_ldconfig "openvdb" } -#### Build Alembic #### +# ---------------------------------------------------------------------------- +# Build Alembic + _init_alembic() { _src=$SRC/alembic-$ALEMBIC_VERSION _git=false @@ -2412,7 +2477,9 @@ compile_ALEMBIC() { run_ldconfig "alembic" } -#### Build OpenCOLLADA #### +# ---------------------------------------------------------------------------- +# Build OpenCOLLADA + _init_opencollada() { _src=$SRC/OpenCOLLADA-$OPENCOLLADA_VERSION _git=true @@ -2504,7 +2571,9 @@ compile_OpenCOLLADA() { fi } -#### Build Embree #### +# ---------------------------------------------------------------------------- +# Build Embree + _init_embree() { _src=$SRC/embree-$EMBREE_VERSION _git=true @@ -2599,7 +2668,9 @@ compile_Embree() { fi } -#### Build OpenImageDenoise #### +# ---------------------------------------------------------------------------- +# Build OpenImageDenoise + _init_oidn() { _src=$SRC/oidn-$OIDN_VERSION _git=true @@ -2691,7 +2762,9 @@ compile_OIDN() { run_ldconfig "oidn" } -#### Build FFMPEG #### +# ---------------------------------------------------------------------------- +# Build FFMPEG + _init_ffmpeg() { _src=$SRC/ffmpeg-$FFMPEG_VERSION _inst=$INST/ffmpeg-$FFMPEG_VERSION @@ -2806,7 +2879,9 @@ compile_FFmpeg() { } -#### Install on DEB-like #### +# ---------------------------------------------------------------------------- +# Install on DEB-like + get_package_version_DEB() { dpkg-query -W -f '${Version}' $1 | sed -r 's/([0-9]+:)?(([0-9]+\.?)+([0-9]+)).*/\2/' } @@ -3341,7 +3416,9 @@ install_DEB() { } -#### Install on RPM-like #### +# ---------------------------------------------------------------------------- +# Install on RPM-like + rpm_flavour() { if [ -f /etc/redhat-release ]; then if [ "`grep '[6-7]\.' /etc/redhat-release`" ]; then @@ -3936,7 +4013,9 @@ install_RPM() { } -#### Install on ARCH-like #### +# ---------------------------------------------------------------------------- +# Install on ARCH-like + get_package_version_ARCH() { pacman -Si $1 | grep Version | tail -n 1 | sed -r 's/.*:\s+?(([0-9]+\.?)+).*/\1/' } @@ -4056,7 +4135,7 @@ install_ARCH() { fi if [ "$WITH_JACK" = true ]; then - _packages="$_packages jack" + _packages="$_packages jack2" fi PRINT "" @@ -4426,7 +4505,8 @@ install_ARCH() { } -#### Install on other distro (very limited!) #### +# ---------------------------------------------------------------------------- +# Install on other distro (very limited!) install_OTHER() { PRINT "" @@ -4621,7 +4701,8 @@ install_OTHER() { fi } -#### Printing User Info #### +# ---------------------------------------------------------------------------- +# Printing User Info print_info_ffmpeglink_DEB() { dpkg -L $_packages | grep -e ".*\/lib[^\/]\+\.so" | gawk '{ printf(nlines ? "'"$_ffmpeg_list_sep"'%s" : "%s", gensub(/.*lib([^\/]+)\.so/, "\\1", "g", $0)); nlines++ }' @@ -4713,7 +4794,7 @@ print_info() { _1="-D PYTHON_VERSION=$PYTHON_VERSION_MIN" PRINT " $_1" _buildargs="$_buildargs $_1" - if [ -d $INST/python-$PYTHON_VERSION_MIN ]; then + if [ -d "$INST/python-$PYTHON_VERSION_MIN" ]; then _1="-D PYTHON_ROOT_DIR=$INST/python-$PYTHON_VERSION_MIN" PRINT " $_1" _buildargs="$_buildargs $_1" @@ -4889,7 +4970,9 @@ print_info() { PRINT " cmake $_buildargs ." } -#### "Main" #### +# ---------------------------------------------------------------------------- +# "Main" + # Detect distribution type used on this machine if [ -f /etc/debian_version ]; then DISTRO="DEB" diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile index c07a80bf0d5..ecd60957f2b 100644 --- a/doc/doxygen/Doxyfile +++ b/doc/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = Blender # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = "V2.82" +PROJECT_NUMBER = "V2.83" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/intern/cycles/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp index 8a8eef0e059..c1106b367ca 100644 --- a/intern/cycles/device/device_optix.cpp +++ b/intern/cycles/device/device_optix.cpp @@ -1188,8 +1188,8 @@ class OptiXDevice : public Device { out_data, sizes.outputSizeInBytes, &out_handle, - &compacted_size_prop, - 1)); + background ? &compacted_size_prop : NULL, + background ? 1 : 0)); // Wait for all operations to finish check_result_cuda_ret(cuStreamSynchronize(NULL)); diff --git a/intern/cycles/kernel/shaders/node_ies_light.osl b/intern/cycles/kernel/shaders/node_ies_light.osl index 6e9181cde40..4d881eb3b65 100644 --- a/intern/cycles/kernel/shaders/node_ies_light.osl +++ b/intern/cycles/kernel/shaders/node_ies_light.osl @@ -31,7 +31,7 @@ shader node_ies_light(int use_mapping = 0, p = transform(mapping, p); } - p = normalize(p); + p = normalize((vector)p); float v_angle = acos(-p[2]); float h_angle = atan2(p[0], p[1]) + M_PI; diff --git a/intern/cycles/kernel/shaders/node_mapping.osl b/intern/cycles/kernel/shaders/node_mapping.osl index 8eed0ae9c48..e8a9d940eda 100644 --- a/intern/cycles/kernel/shaders/node_mapping.osl +++ b/intern/cycles/kernel/shaders/node_mapping.osl @@ -65,7 +65,7 @@ shader node_mapping(string type = "point", VectorOut = transform(euler_to_mat(Rotation), (VectorIn * Scale)); } else if (type == "normal") { - VectorOut = normalize(transform(euler_to_mat(Rotation), safe_divide(VectorIn, Scale))); + VectorOut = normalize((vector)transform(euler_to_mat(Rotation), safe_divide(VectorIn, Scale))); } else { warning("%s", "Unknown Mapping vector type!"); diff --git a/intern/cycles/kernel/shaders/node_voronoi_texture.osl b/intern/cycles/kernel/shaders/node_voronoi_texture.osl index 5de4aeef943..10a9f7a6329 100644 --- a/intern/cycles/kernel/shaders/node_voronoi_texture.osl +++ b/intern/cycles/kernel/shaders/node_voronoi_texture.osl @@ -603,7 +603,7 @@ void voronoi_distance_to_edge_3d(vector3 coord, float randomness, output float o vector3 perpendicularToEdge = vectorToPoint - vectorToClosest; if (dot(perpendicularToEdge, perpendicularToEdge) > 0.0001) { float distanceToEdge = dot((vectorToClosest + vectorToPoint) / 2.0, - normalize(perpendicularToEdge)); + normalize((vector)perpendicularToEdge)); minDistance = min(minDistance, distanceToEdge); } } diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp index da04ef63295..661208c6463 100644 --- a/intern/cycles/render/shader.cpp +++ b/intern/cycles/render/shader.cpp @@ -317,7 +317,8 @@ void Shader::tag_update(Scene *scene) * has use_mis set to false. We are quite close to release now, so * better to be safe. */ - if (this == scene->background->get_shader(scene) && scene->light_manager->has_background_light(scene)) { + if (this == scene->background->get_shader(scene) && + scene->light_manager->has_background_light(scene)) { scene->light_manager->need_update = true; } diff --git a/intern/ghost/GHOST_IWindow.h b/intern/ghost/GHOST_IWindow.h index c19d4bdf6bd..07133d86ce4 100644 --- a/intern/ghost/GHOST_IWindow.h +++ b/intern/ghost/GHOST_IWindow.h @@ -247,7 +247,11 @@ class GHOST_IWindow { * Returns the tablet data (pressure etc). * \return The tablet data (pressure etc). */ - virtual const GHOST_TabletData *GetTabletData() = 0; + virtual const GHOST_TabletData &GetTabletData() + { + /* Default state when no tablet is used, for systems without tablet support. */ + return GHOST_TABLET_DATA_DEFAULT; + } /*************************************************************************************** * Progress bar functionality diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index fab315e5f13..c7c9f91a361 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -101,6 +101,12 @@ typedef struct GHOST_TabletData { float Ytilt; /* as above */ } GHOST_TabletData; +static const GHOST_TabletData GHOST_TABLET_DATA_DEFAULT = { + GHOST_kTabletModeNone, /* No tablet connected. */ + 1.0f, /* Pressure */ + 0.0f, /* Xtilt */ + 0.0f}; /* Ytilt */ + typedef enum { GHOST_kNotVisible = 0, GHOST_kPartiallyVisible, @@ -409,11 +415,15 @@ typedef struct { GHOST_TInt32 x; /** The y-coordinate of the cursor position. */ GHOST_TInt32 y; + /** Associated tablet data. */ + GHOST_TabletData tablet; } GHOST_TEventCursorData; typedef struct { /** The mask of the mouse button. */ GHOST_TButtonMask button; + /** Associated tablet data. */ + GHOST_TabletData tablet; } GHOST_TEventButtonData; typedef struct { diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp index 1bed7afdfc4..3c3860bd2c0 100644 --- a/intern/ghost/intern/GHOST_C-api.cpp +++ b/intern/ghost/intern/GHOST_C-api.cpp @@ -708,7 +708,7 @@ void GHOST_SetTabletAPI(GHOST_SystemHandle systemhandle, GHOST_TTabletAPI api) const GHOST_TabletData *GHOST_GetTabletData(GHOST_WindowHandle windowhandle) { - return ((GHOST_IWindow *)windowhandle)->GetTabletData(); + return &((GHOST_IWindow *)windowhandle)->GetTabletData(); } GHOST_TInt32 GHOST_GetWidthRectangle(GHOST_RectangleHandle rectanglehandle) diff --git a/intern/ghost/intern/GHOST_EventButton.h b/intern/ghost/intern/GHOST_EventButton.h index da1dc929f4f..0f9d3b7e6bc 100644 --- a/intern/ghost/intern/GHOST_EventButton.h +++ b/intern/ghost/intern/GHOST_EventButton.h @@ -46,6 +46,7 @@ class GHOST_EventButton : public GHOST_Event { : GHOST_Event(time, type, window) { m_buttonEventData.button = button; + m_buttonEventData.tablet = window->GetTabletData(); m_data = &m_buttonEventData; } diff --git a/intern/ghost/intern/GHOST_EventCursor.h b/intern/ghost/intern/GHOST_EventCursor.h index ac40f78569d..41597db216a 100644 --- a/intern/ghost/intern/GHOST_EventCursor.h +++ b/intern/ghost/intern/GHOST_EventCursor.h @@ -48,6 +48,7 @@ class GHOST_EventCursor : public GHOST_Event { { m_cursorEventData.x = x; m_cursorEventData.y = y; + m_cursorEventData.tablet = window->GetTabletData(); m_data = &m_cursorEventData; } diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index 2f597aee476..e50e478b9fc 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -1451,11 +1451,10 @@ GHOST_TSuccess GHOST_SystemCocoa::handleTabletEvent(void *eventPtr, short eventT break; case NSEventTypeTabletProximity: - ct.Pressure = 0; - ct.Xtilt = 0; - ct.Ytilt = 0; + /* Reset tablet data when device enters proximity or leaves. */ + ct = GHOST_TABLET_DATA_DEFAULT; if ([event isEnteringProximity]) { - // pointer is entering tablet area proximity + /* Pointer is entering tablet area proximity. */ switch ([event pointingDeviceType]) { case NSPointingDeviceTypePen: ct.Active = GHOST_kTabletModeStylus; @@ -1466,14 +1465,9 @@ GHOST_TSuccess GHOST_SystemCocoa::handleTabletEvent(void *eventPtr, short eventT case NSPointingDeviceTypeCursor: case NSPointingDeviceTypeUnknown: default: - ct.Active = GHOST_kTabletModeNone; break; } } - else { - // pointer is leaving - return to mouse - ct.Active = GHOST_kTabletModeNone; - } break; default: @@ -1524,46 +1518,45 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr) switch ([event type]) { case NSEventTypeLeftMouseDown: + handleTabletEvent(event); // Update window tablet state to be included in event. pushEvent(new GHOST_EventButton( [event timestamp] * 1000, GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft)); - handleTabletEvent(event); // Handle tablet events combined with mouse events break; case NSEventTypeRightMouseDown: + handleTabletEvent(event); // Update window tablet state to be included in event. pushEvent(new GHOST_EventButton( [event timestamp] * 1000, GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight)); - handleTabletEvent(event); // Handle tablet events combined with mouse events break; case NSEventTypeOtherMouseDown: + handleTabletEvent(event); // Handle tablet events combined with mouse events pushEvent(new GHOST_EventButton([event timestamp] * 1000, GHOST_kEventButtonDown, window, convertButton([event buttonNumber]))); - handleTabletEvent(event); // Handle tablet events combined with mouse events break; case NSEventTypeLeftMouseUp: + handleTabletEvent(event); // Update window tablet state to be included in event. pushEvent(new GHOST_EventButton( [event timestamp] * 1000, GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft)); - handleTabletEvent(event); // Handle tablet events combined with mouse events break; case NSEventTypeRightMouseUp: + handleTabletEvent(event); // Update window tablet state to be included in event. pushEvent(new GHOST_EventButton( [event timestamp] * 1000, GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight)); - handleTabletEvent(event); // Handle tablet events combined with mouse events break; case NSEventTypeOtherMouseUp: + handleTabletEvent(event); // Update window tablet state to be included in event. pushEvent(new GHOST_EventButton([event timestamp] * 1000, GHOST_kEventButtonUp, window, convertButton([event buttonNumber]))); - handleTabletEvent(event); // Handle tablet events combined with mouse events break; case NSEventTypeLeftMouseDragged: case NSEventTypeRightMouseDragged: case NSEventTypeOtherMouseDragged: - // Handle tablet events combined with mouse events - handleTabletEvent(event); + handleTabletEvent(event); // Update window tablet state to be included in event. case NSEventTypeMouseMoved: { GHOST_TGrabCursorMode grab_mode = window->getCursorGrabMode(); diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index fa2fc9fff37..31a6ea96bc3 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -894,6 +894,7 @@ GHOST_Event *GHOST_SystemWin32::processPointerEvent(GHOST_TEventType type, switch (type) { case GHOST_kEventButtonDown: + /* Update window tablet data to be included in event. */ window->setTabletData(&pointerInfo.tabletData); eventHandled = true; return new GHOST_EventButton( @@ -903,6 +904,7 @@ GHOST_Event *GHOST_SystemWin32::processPointerEvent(GHOST_TEventType type, return new GHOST_EventButton( system->getMilliSeconds(), GHOST_kEventButtonUp, window, pointerInfo.buttonMask); case GHOST_kEventCursorMove: + /* Update window tablet data to be included in event. */ window->setTabletData(&pointerInfo.tabletData); eventHandled = true; return new GHOST_EventCursor(system->getMilliSeconds(), diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index c50ff8e7426..f15641352e0 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -823,7 +823,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) * in the future we could have a ghost call window->CheckTabletProximity() * but for now enough parts of the code are checking 'Active' * - campbell */ - if (window->GetTabletData()->Active != GHOST_kTabletModeNone) { + if (window->GetTabletData().Active != GHOST_kTabletModeNone) { bool any_proximity = false; for (GHOST_TabletX11 &xtablet : m_xtablets) { @@ -834,7 +834,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) if (!any_proximity) { // printf("proximity disable\n"); - window->GetTabletData()->Active = GHOST_kTabletModeNone; + window->GetTabletData().Active = GHOST_kTabletModeNone; } } #endif /* WITH_X11_XINPUT */ @@ -855,7 +855,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) XMotionEvent &xme = xe->xmotion; #ifdef WITH_X11_XINPUT - bool is_tablet = window->GetTabletData()->Active != GHOST_kTabletModeNone; + bool is_tablet = window->GetTabletData().Active != GHOST_kTabletModeNone; #else bool is_tablet = false; #endif @@ -1395,7 +1395,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) /* stroke might begin without leading ProxyIn event, * this happens when window is opened when stylus is already hovering * around tablet surface */ - window->GetTabletData()->Active = xtablet.mode; + window->GetTabletData().Active = xtablet.mode; /* Note: This event might be generated with incomplete dataset * (don't exactly know why, looks like in some cases, if the value does not change, @@ -1408,7 +1408,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) ((void)(val = data->axis_data[axis - axis_first]), true)) if (AXIS_VALUE_GET(2, axis_value)) { - window->GetTabletData()->Pressure = axis_value / ((float)xtablet.PressureLevels); + window->GetTabletData().Pressure = axis_value / ((float)xtablet.PressureLevels); } /* the (short) cast and the & 0xffff is bizarre and unexplained anywhere, @@ -1420,12 +1420,12 @@ void GHOST_SystemX11::processEvent(XEvent *xe) * check this. --mont29 */ if (AXIS_VALUE_GET(3, axis_value)) { - window->GetTabletData()->Xtilt = (short)(axis_value & 0xffff) / - ((float)xtablet.XtiltLevels); + window->GetTabletData().Xtilt = (short)(axis_value & 0xffff) / + ((float)xtablet.XtiltLevels); } if (AXIS_VALUE_GET(4, axis_value)) { - window->GetTabletData()->Ytilt = (short)(axis_value & 0xffff) / - ((float)xtablet.YtiltLevels); + window->GetTabletData().Ytilt = (short)(axis_value & 0xffff) / + ((float)xtablet.YtiltLevels); } # undef AXIS_VALUE_GET @@ -1436,10 +1436,10 @@ void GHOST_SystemX11::processEvent(XEvent *xe) continue; } - window->GetTabletData()->Active = xtablet.mode; + window->GetTabletData().Active = xtablet.mode; } else if (xe->type == xtablet.ProxOutEvent) { - window->GetTabletData()->Active = GHOST_kTabletModeNone; + window->GetTabletData().Active = GHOST_kTabletModeNone; } } #endif // WITH_X11_XINPUT diff --git a/intern/ghost/intern/GHOST_WindowCocoa.h b/intern/ghost/intern/GHOST_WindowCocoa.h index d260d0eacbc..a49949c2c8d 100644 --- a/intern/ghost/intern/GHOST_WindowCocoa.h +++ b/intern/ghost/intern/GHOST_WindowCocoa.h @@ -222,9 +222,9 @@ class GHOST_WindowCocoa : public GHOST_Window { bool isDialog() const; - const GHOST_TabletData *GetTabletData() + const GHOST_TabletData &GetTabletData() { - return &m_tablet; + return m_tablet; } GHOST_TabletData &GetCocoaTabletData() diff --git a/intern/ghost/intern/GHOST_WindowCocoa.mm b/intern/ghost/intern/GHOST_WindowCocoa.mm index 1d89da90a32..340beef9609 100644 --- a/intern/ghost/intern/GHOST_WindowCocoa.mm +++ b/intern/ghost/intern/GHOST_WindowCocoa.mm @@ -387,7 +387,7 @@ GHOST_WindowCocoa::GHOST_WindowCocoa(GHOST_SystemCocoa *systemCocoa, setTitle(title); - m_tablet.Active = GHOST_kTabletModeNone; + m_tablet = GHOST_TABLET_DATA_DEFAULT; CocoaWindowDelegate *windowDelegate = [[CocoaWindowDelegate alloc] init]; [windowDelegate setSystemAndWindowCocoa:systemCocoa windowCocoa:this]; diff --git a/intern/ghost/intern/GHOST_WindowNULL.h b/intern/ghost/intern/GHOST_WindowNULL.h index 29f3eee7cce..db40075e6ca 100644 --- a/intern/ghost/intern/GHOST_WindowNULL.h +++ b/intern/ghost/intern/GHOST_WindowNULL.h @@ -31,11 +31,6 @@ class GHOST_SystemNULL; class GHOST_WindowNULL : public GHOST_Window { public: - const GHOST_TabletData *GetTabletData() - { - return NULL; - } - GHOST_TSuccess hasCursorShape(GHOST_TStandardCursor) { return GHOST_kSuccess; diff --git a/intern/ghost/intern/GHOST_WindowSDL.h b/intern/ghost/intern/GHOST_WindowSDL.h index d9342de4d69..6332ce584d2 100644 --- a/intern/ghost/intern/GHOST_WindowSDL.h +++ b/intern/ghost/intern/GHOST_WindowSDL.h @@ -48,11 +48,6 @@ class GHOST_WindowSDL : public GHOST_Window { SDL_Cursor *m_sdl_custom_cursor; public: - const GHOST_TabletData *GetTabletData() - { - return NULL; - } - GHOST_WindowSDL(GHOST_SystemSDL *system, const STR_String &title, GHOST_TInt32 left, diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index fd9e0240b1b..e91dba5cef8 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -89,8 +89,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, { // Initialize tablet variables memset(&m_wintab, 0, sizeof(m_wintab)); - memset(&m_tabletData, 0, sizeof(m_tabletData)); - m_tabletData.Active = GHOST_kTabletModeNone; + m_tabletData = GHOST_TABLET_DATA_DEFAULT; // Create window if (state != GHOST_kWindowStateFullScreen) { @@ -1085,10 +1084,7 @@ void GHOST_WindowWin32::setTabletData(GHOST_TabletData *pTabletData) m_tabletData = *pTabletData; } else { - m_tabletData.Active = GHOST_kTabletModeNone; - m_tabletData.Pressure = 1.0f; - m_tabletData.Xtilt = 0.0f; - m_tabletData.Ytilt = 0.0f; + m_tabletData = GHOST_TABLET_DATA_DEFAULT; } } @@ -1150,8 +1146,6 @@ void GHOST_WindowWin32::processWin32TabletInitEvent() m_wintab.maxAzimuth = m_wintab.maxAltitude = 0; } } - - m_tabletData.Active = GHOST_kTabletModeNone; } m_tabletData.Active = GHOST_kTabletModeNone; diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h index f72f03855fd..4795539e0f9 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.h +++ b/intern/ghost/intern/GHOST_WindowWin32.h @@ -392,9 +392,9 @@ class GHOST_WindowWin32 : public GHOST_Window { HCURSOR getStandardCursor(GHOST_TStandardCursor shape) const; void loadCursor(bool visible, GHOST_TStandardCursor cursorShape) const; - const GHOST_TabletData *GetTabletData() + const GHOST_TabletData &GetTabletData() { - return &m_tabletData; + return m_tabletData; } void setTabletData(GHOST_TabletData *tabletData); diff --git a/intern/ghost/intern/GHOST_WindowX11.cpp b/intern/ghost/intern/GHOST_WindowX11.cpp index ae8d705fe4a..6fbdacf98d6 100644 --- a/intern/ghost/intern/GHOST_WindowX11.cpp +++ b/intern/ghost/intern/GHOST_WindowX11.cpp @@ -478,7 +478,7 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system, #ifdef WITH_X11_XINPUT refreshXInputDevices(); - m_tabletData.Active = GHOST_kTabletModeNone; + m_tabletData = GHOST_TABLET_DATA_DEFAULT; #endif /* now set up the rendering context. */ diff --git a/intern/ghost/intern/GHOST_WindowX11.h b/intern/ghost/intern/GHOST_WindowX11.h index faf3acba234..a9914d88340 100644 --- a/intern/ghost/intern/GHOST_WindowX11.h +++ b/intern/ghost/intern/GHOST_WindowX11.h @@ -145,14 +145,9 @@ class GHOST_WindowX11 : public GHOST_Window { */ Window getXWindow(); #ifdef WITH_X11_XINPUT - GHOST_TabletData *GetTabletData() + GHOST_TabletData &GetTabletData() { - return &m_tabletData; - } -#else // WITH_X11_XINPUT - const GHOST_TabletData *GetTabletData() - { - return NULL; + return m_tabletData; } #endif // WITH_X11_XINPUT diff --git a/intern/guardedalloc/intern/mallocn.c b/intern/guardedalloc/intern/mallocn.c index fa2d0d1e334..d24437c85f2 100644 --- a/intern/guardedalloc/intern/mallocn.c +++ b/intern/guardedalloc/intern/mallocn.c @@ -70,6 +70,9 @@ const char *(*MEM_name_ptr)(void *vmemh) = MEM_lockfree_name_ptr; void *aligned_malloc(size_t size, size_t alignment) { + /* posix_memalign requires alignment to be a multiple of sizeof(void *). */ + assert(alignment >= ALIGNED_MALLOC_MINIMUM_ALIGNMENT); + #ifdef _WIN32 return _aligned_malloc(size, alignment); #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) diff --git a/intern/guardedalloc/intern/mallocn_intern.h b/intern/guardedalloc/intern/mallocn_intern.h index e6e090703d4..876607fdb77 100644 --- a/intern/guardedalloc/intern/mallocn_intern.h +++ b/intern/guardedalloc/intern/mallocn_intern.h @@ -107,6 +107,8 @@ size_t malloc_usable_size(void *ptr); #include "mallocn_inline.h" +#define ALIGNED_MALLOC_MINIMUM_ALIGNMENT sizeof(void *) + void *aligned_malloc(size_t size, size_t alignment); void aligned_free(void *ptr); diff --git a/intern/guardedalloc/intern/mallocn_lockfree_impl.c b/intern/guardedalloc/intern/mallocn_lockfree_impl.c index e8fd8de738b..87091bb9862 100644 --- a/intern/guardedalloc/intern/mallocn_lockfree_impl.c +++ b/intern/guardedalloc/intern/mallocn_lockfree_impl.c @@ -346,7 +346,17 @@ void *MEM_lockfree_malloc_arrayN(size_t len, size_t size, const char *str) void *MEM_lockfree_mallocN_aligned(size_t len, size_t alignment, const char *str) { - MemHeadAligned *memh; + /* Huge alignment values doesn't make sense and they wouldn't fit into 'short' used in the + * MemHead. */ + assert(alignment < 1024); + + /* We only support alignments that are a power of two. */ + assert(IS_POW2(alignment)); + + /* Some OS specific aligned allocators require a certain minimal alignment. */ + if (alignment < ALIGNED_MALLOC_MINIMUM_ALIGNMENT) { + alignment = ALIGNED_MALLOC_MINIMUM_ALIGNMENT; + } /* It's possible that MemHead's size is not properly aligned, * do extra padding to deal with this. @@ -356,17 +366,10 @@ void *MEM_lockfree_mallocN_aligned(size_t len, size_t alignment, const char *str */ size_t extra_padding = MEMHEAD_ALIGN_PADDING(alignment); - /* Huge alignment values doesn't make sense and they - * wouldn't fit into 'short' used in the MemHead. - */ - assert(alignment < 1024); - - /* We only support alignment to a power of two. */ - assert(IS_POW2(alignment)); - len = SIZET_ALIGN_4(len); - memh = (MemHeadAligned *)aligned_malloc(len + extra_padding + sizeof(MemHeadAligned), alignment); + MemHeadAligned *memh = (MemHeadAligned *)aligned_malloc( + len + extra_padding + sizeof(MemHeadAligned), alignment); if (LIKELY(memh)) { /* We keep padding in the beginning of MemHead, diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject 8a05b618f031582c006c6f62b9e60619ab3eef8 +Subproject 19f3349d204303ae90e31939d5bf3f0c4fe9df4 diff --git a/release/datafiles/userdef/userdef_default_theme.c b/release/datafiles/userdef/userdef_default_theme.c index 147b55f60ef..03df24d2057 100644 --- a/release/datafiles/userdef/userdef_default_theme.c +++ b/release/datafiles/userdef/userdef_default_theme.c @@ -339,6 +339,7 @@ const bTheme U_theme_default = { .bone_solid = RGBA(0xb2b2b2ff), .bone_pose = RGBA(0x50c8ff50), .bone_pose_active = RGBA(0x8cffff50), + .bone_locked_weight = RGBA(0xff000080), .cframe = RGBA(0x60c040ff), .time_keyframe = RGBA(0xddd700ff), .time_gp_keyframe = RGBA(0xb5e61dff), @@ -453,11 +454,11 @@ const bTheme U_theme_default = { .anim_preview_range = RGBA(0xa14d0066), }, .space_info = { - .back = RGBA(0x42424200), - .title = RGBA(0xeeeeeeff), - .text = RGBA(0xe6e6e6ff), + .back = RGBA(0x28282800), + .title = RGBA(0xffffffff), + .text = RGBA(0xc3c3c3ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x424242ff), + .header = RGBA(0x454545ff), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), .tab_active = RGBA(0x4b4b4bff), @@ -476,7 +477,7 @@ const bTheme U_theme_default = { .vertex_size = 3, .outline_width = 1, .facedot_size = 4, - .info_selected = RGBA(0x6080ffff), + .info_selected = RGBA(0x3b5689ff), .info_selected_text = RGBA(0xffffffff), .info_error = RGBA(0x990000ff), .info_error_text = RGBA(0xffffffff), diff --git a/release/scripts/addons b/release/scripts/addons -Subproject 1470f353c65034db91131d21ab9c782d029a2ee +Subproject 1e165b809b66fbf19778dbb6f1a3f4e64efef03 diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib -Subproject ffbaca558a27bab4716bcd51ca7ea1df8e4f4b1 +Subproject 70b649775eeeebedb02c1c7b7aa996a7f629417 diff --git a/release/scripts/modules/bpy_extras/io_utils.py b/release/scripts/modules/bpy_extras/io_utils.py index 19697b25f70..9e8febb47cd 100644 --- a/release/scripts/modules/bpy_extras/io_utils.py +++ b/release/scripts/modules/bpy_extras/io_utils.py @@ -116,6 +116,13 @@ class ImportHelper: subtype='FILE_PATH', ) + # Hide opertator properties, rest of this is managed in C. See WM_operator_properties_filesel(). + hide_props_region: BoolProperty( + name="Hide Operator Properties", + description="Collapse the region displaying the operator settings", + default=True, + ) + def invoke(self, context, _event): context.window_manager.fileselect_add(self) return {'RUNNING_MODAL'} diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py index 202fd865723..8dda8c90f85 100644 --- a/release/scripts/modules/rna_prop_ui.py +++ b/release/scripts/modules/rna_prop_ui.py @@ -277,14 +277,15 @@ def draw(layout, context, context_member, property_type, use_edit=True): else: val_draw = val - row = flow.row(align=True) + row = layout.row(align=True) box = row.box() if use_edit: split = box.split(factor=0.75) row = split.row(align=True) else: - row = box.row(align=True) + split = box.split(factor=1.00) + row = split.row(align=True) row.alignment = 'RIGHT' diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 8f8b1cc39d9..81d59f9f751 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -290,12 +290,14 @@ def _template_items_proportional_editing(*, connected=False): # Tool System Templates -def _template_items_tool_select(params, operator, cursor_operator): +def _template_items_tool_select(params, operator, cursor_operator, *, extend): if params.select_mouse == 'LEFTMOUSE': # Immediate select without quick delay. return [ (operator, {"type": 'LEFTMOUSE', "value": 'PRESS'}, {"properties": [("deselect_all", True)]}), + (operator, {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, + {"properties": [(extend, True)]}), ] else: # For right mouse, set the cursor. @@ -2333,6 +2335,7 @@ def km_sequencercommon(_params): items.extend([ *_template_space_region_type_toggle( + toolbar_key={"type": 'T', "value": 'PRESS'}, sidebar_key={"type": 'N', "value": 'PRESS'}, ), ("wm.context_toggle", {"type": 'O', "value": 'PRESS', "shift": True}, @@ -2340,6 +2343,13 @@ def km_sequencercommon(_params): ("sequencer.view_toggle", {"type": 'TAB', "value": 'PRESS', "ctrl": True}, None), ]) + if _params.select_mouse == 'LEFTMOUSE' and not _params.legacy: + # Quick switch to select tool, since left select can't easily + # select with any tool active. + items.extend([ + op_tool_cycle("builtin.select_box", {"type": 'W', "value": 'PRESS'}), + ]) + return keymap @@ -5055,7 +5065,7 @@ def km_image_editor_tool_uv_select(params): return ( "Image Editor Tool: Uv, Tweak", {"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select(params, "uv.select", "uv.cursor_set")}, + {"items": _template_items_tool_select(params, "uv.select", "uv.cursor_set", extend="extend")}, ) @@ -5206,7 +5216,7 @@ def km_3d_view_tool_select(params): return ( "3D View Tool: Tweak", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select(params, "view3d.select", "view3d.cursor3d")}, + {"items": _template_items_tool_select(params, "view3d.select", "view3d.cursor3d", extend="toggle")}, ) @@ -5913,7 +5923,7 @@ def km_3d_view_tool_edit_gpencil_select(params): return ( "3D View Tool: Edit Gpencil, Tweak", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select(params, "gpencil.select", "view3d.cursor3d")}, + {"items": _template_items_tool_select(params, "gpencil.select", "view3d.cursor3d", extend="toggle")}, ) @@ -6018,7 +6028,7 @@ def km_3d_view_tool_sculpt_gpencil_select(params): return ( "3D View Tool: Sculpt Gpencil, Tweak", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select(params, "gpencil.select", "view3d.cursor3d")}, + {"items": _template_items_tool_select(params, "gpencil.select", "view3d.cursor3d", extend="toggle")}, ) @@ -6049,6 +6059,39 @@ def km_3d_view_tool_sculpt_gpencil_select_lasso(params): ) +def km_sequencer_editor_tool_select(params): + return ( + "Sequencer Tool: Select", + {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, + {"items": [ + ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS'}, + {"properties": [("extend", False), ("deselect_all", not params.legacy)]}), + ]}, + ) + + +def km_sequencer_editor_tool_select_box(params): + return ( + "Sequencer Tool: Select Box", + {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, + {"items": _template_items_tool_select_actions_simple( + "sequencer.select_box", type=params.tool_tweak, value='ANY', + properties=[("tweak", True)], + )}, + ) + + +def km_sequencer_editor_tool_cut(params): + return ( + "Sequencer Tool: Cut", + {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, + {"items":[ + ("sequencer.cut", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": [("type", 'SOFT'), ("side", 'NO_CHANGE'), ("use_cursor_position", True), ("ignore_selection", True)]}), + ]}, + ) + + # ------------------------------------------------------------------------------ # Full Configuration @@ -6262,6 +6305,9 @@ def generate_keymaps(params=None): km_3d_view_tool_sculpt_gpencil_select_box(params), km_3d_view_tool_sculpt_gpencil_select_circle(params), km_3d_view_tool_sculpt_gpencil_select_lasso(params), + km_sequencer_editor_tool_select(params), + km_sequencer_editor_tool_select_box(params), + km_sequencer_editor_tool_cut(params), ] # ------------------------------------------------------------------------------ diff --git a/release/scripts/startup/bl_operators/vertexpaint_dirt.py b/release/scripts/startup/bl_operators/vertexpaint_dirt.py index 39d792bd557..a249599b5d7 100644 --- a/release/scripts/startup/bl_operators/vertexpaint_dirt.py +++ b/release/scripts/startup/bl_operators/vertexpaint_dirt.py @@ -37,6 +37,20 @@ def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, from math import acos import array + # We simulate the accumulation of dirt in the creases of geometric surfaces + # by comparing the vertex normal to the average direction of all vertices + # connected to that vertex. We can also simulate surfaces being buffed or + # worn by testing protruding surfaces. + # + # So if the angle between the normal and geometric direction is: + # < 90 - dirt has accumulated in the crease + # > 90 - surface has been worn or buffed + # ~ 90 - surface is flat and is generally unworn and clean + # + # This method is limited by the complexity or lack there of in the geometry. + # + # Original code and method by Keith "Wahooney" Boshoff. + vert_tone = array.array("f", [0.0]) * len(me.vertices) # create lookup table for each vertex's connected vertices (via edges) diff --git a/release/scripts/startup/bl_ui/properties_data_light.py b/release/scripts/startup/bl_ui/properties_data_light.py index 6f730cf3307..cf894b48e1e 100644 --- a/release/scripts/startup/bl_ui/properties_data_light.py +++ b/release/scripts/startup/bl_ui/properties_data_light.py @@ -134,17 +134,15 @@ class DATA_PT_EEVEE_light_distance(DataButtonsPanel, Panel): light = context.light layout = self.layout - layout.active = light.use_shadow layout.prop(light, "use_custom_distance", text="") def draw(self, context): layout = self.layout light = context.light + layout.active = light.use_custom_distance layout.use_property_split = True - col = layout.column() - - col.prop(light, "cutoff_distance", text="Distance") + layout.prop(light, "cutoff_distance", text="Distance") class DATA_PT_EEVEE_shadow(DataButtonsPanel, Panel): @@ -311,7 +309,8 @@ class DATA_PT_falloff_curve(DataButtonsPanel, Panel): def draw(self, context): light = context.light - self.layout.template_curve_mapping(light, "falloff_curve", use_negative_slope=True) + self.layout.template_curve_mapping( + light, "falloff_curve", use_negative_slope=True) class DATA_PT_custom_props_light(DataButtonsPanel, PropertyPanel, Panel): diff --git a/release/scripts/startup/bl_ui/space_info.py b/release/scripts/startup/bl_ui/space_info.py index 82ed701aa4c..cd65980fc0d 100644 --- a/release/scripts/startup/bl_ui/space_info.py +++ b/release/scripts/startup/bl_ui/space_info.py @@ -92,7 +92,7 @@ class INFO_MT_area(Menu): layout.separator() - layout.operator("screen.area_dupli", icon='DUPLICATE') + layout.operator("screen.area_dupli", icon='WINDOW') layout.separator() diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 097564444d0..af0c23e7892 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -30,6 +30,9 @@ from bpy.app.translations import ( from bl_ui.properties_grease_pencil_common import ( AnnotationDataPanel, ) +from bl_ui.space_toolsystem_common import ( + ToolActivePanelHelper, +) from rna_prop_ui import PropertyPanel @@ -89,6 +92,35 @@ def draw_color_balance(layout, color_balance): split.template_color_picker(color_balance, "gain", value_slider=True, lock_luminosity=True, cubic=True) +class SEQUENCER_PT_active_tool(ToolActivePanelHelper, Panel): + bl_space_type = 'SEQUENCE_EDITOR' + bl_region_type = 'UI' + bl_category = "Tool" + + +class SEQUENCER_HT_tool_header(Header): + bl_space_type = 'SEQUENCE_EDITOR' + bl_region_type = 'TOOL_HEADER' + + def draw(self, context): + layout = self.layout + + layout.template_header() + + self.draw_tool_settings(context) + + # TODO: options popover. + + def draw_tool_settings(self, context): + layout = self.layout + + # Active Tool + # ----------- + from bl_ui.space_toolsystem_common import ToolSelectPanelHelper + tool = ToolSelectPanelHelper.draw_active_tool_header(context, layout) + tool_mode = context.mode if tool is None else tool.mode + + class SEQUENCER_HT_header(Header): bl_space_type = 'SEQUENCE_EDITOR' @@ -97,7 +129,10 @@ class SEQUENCER_HT_header(Header): st = context.space_data - layout.template_header() + show_region_tool_header = st.show_region_tool_header + + if not show_region_tool_header: + layout.template_header() layout.prop(st, "view_type", text="") @@ -226,8 +261,12 @@ class SEQUENCER_MT_view(Menu): # wm_keymap_item_find_props() (see #32595). layout.operator_context = 'INVOKE_REGION_PREVIEW' layout.prop(st, "show_region_ui") + layout.prop(st, "show_region_toolbar") layout.operator_context = 'INVOKE_DEFAULT' + if is_sequencer_view: + layout.prop(st, "show_region_hud") + if st.view_type == 'SEQUENCER': layout.prop(st, "show_backdrop", text="Preview as Backdrop") @@ -268,6 +307,7 @@ class SEQUENCER_MT_view(Menu): layout.operator_context = 'INVOKE_DEFAULT' layout.prop(st, "show_seconds") + layout.prop(st, "show_locked_time") layout.prop(st, "show_strip_offset") layout.separator() layout.prop(st, "show_markers") @@ -2128,6 +2168,7 @@ class SEQUENCER_PT_custom_props(SequencerButtonsPanel, PropertyPanel, Panel): classes = ( SEQUENCER_MT_change, + SEQUENCER_HT_tool_header, SEQUENCER_HT_header, SEQUENCER_MT_editor_menus, SEQUENCER_MT_range, @@ -2153,7 +2194,7 @@ classes = ( SEQUENCER_MT_strip_input, SEQUENCER_MT_strip_lock_mute, SEQUENCER_MT_context_menu, - + SEQUENCER_PT_active_tool, SEQUENCER_PT_strip, SEQUENCER_PT_effect, diff --git a/release/scripts/startup/bl_ui/space_text.py b/release/scripts/startup/bl_ui/space_text.py index 81ccc9216a1..b7c5dcd5437 100644 --- a/release/scripts/startup/bl_ui/space_text.py +++ b/release/scripts/startup/bl_ui/space_text.py @@ -30,7 +30,7 @@ class TEXT_HT_header(Header): st = context.space_data text = st.text - + is_syntax_highlight_supported = st.is_syntax_highlight_supported() layout.template_header() TEXT_MT_editor_menus.draw_collapsible(context, layout) @@ -43,7 +43,18 @@ class TEXT_HT_header(Header): layout.separator_spacer() row = layout.row(align=True) - row.template_ID(st, "text", new="text.new", unlink="text.unlink", open="text.open") + row.template_ID(st, "text", new="text.new", + unlink="text.unlink", open="text.open") + + if text: + is_osl = text.name.endswith((".osl", ".osl")) + if is_osl: + row.operator("node.shader_script_update", + text="", icon='FILE_REFRESH') + else: + row = layout.row() + row.active = is_syntax_highlight_supported + row.operator("text.run_script", text="", icon='PLAY') layout.separator_spacer() @@ -51,28 +62,10 @@ class TEXT_HT_header(Header): row.prop(st, "show_line_numbers", text="") row.prop(st, "show_word_wrap", text="") - is_syntax_highlight_supported = st.is_syntax_highlight_supported() syntax = row.row(align=True) syntax.active = is_syntax_highlight_supported syntax.prop(st, "show_syntax_highlight", text="") - if text: - text_name = text.name - is_osl = text_name.endswith((".osl", ".oso")) - - row = layout.row() - if is_osl: - row = layout.row() - row.operator("node.shader_script_update") - else: - row = layout.row() - row.active = text_name.endswith(".py") - row.prop(text, "use_module") - - row = layout.row() - row.active = is_syntax_highlight_supported - row.operator("text.run_script") - class TEXT_HT_footer(Header): bl_space_type = 'TEXT_EDITOR' diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py index 05785b85dfc..4dc724299f0 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_common.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py @@ -390,6 +390,14 @@ class ToolSelectPanelHelper: if tool is not None: tool.refresh_from_context() return tool + elif space_type == 'SEQUENCE_EDITOR': + space_data = context.space_data + if mode is None: + mode = space_data.view_type + tool = context.workspace.tools.from_space_sequencer(mode, create=create) + if tool is not None: + tool.refresh_from_context() + return tool return None @staticmethod @@ -656,6 +664,8 @@ class ToolSelectPanelHelper: return space_type, space_data.mode elif space_type == 'NODE_EDITOR': return space_type, None + elif space_type == 'SEQUENCE_EDITOR': + return space_type, context.space_data.view_type else: return None, None diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 170b635f7e1..c03d681f2b2 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1707,6 +1707,51 @@ class _defs_node_edit: keymap="Node Tool: Links Cut", ) +class _defs_sequencer_generic: + + @ToolDef.from_fn + def cut(): + def draw_settings(_context, layout, tool): + props = tool.operator_properties("sequencer.cut") + row = layout.row() + row.use_property_split = False + row.prop(props, "type", expand=True) + return dict( + idname="builtin.cut", + label="Cut", + icon="ops.mesh.knife_tool", + widget=None, + keymap="Sequencer Tool: Cut", + draw_settings=draw_settings, + ) + +class _defs_sequencer_select: + @ToolDef.from_fn + def select(): + return dict( + idname="builtin.select", + label="Select", + icon="ops.generic.select", + widget=None, + keymap="Sequencer Tool: Select", + ) + @ToolDef.from_fn + def box(): + def draw_settings(_context, layout, tool): + props = tool.operator_properties("sequencer.select_box") + row = layout.row() + row.use_property_split = False + row.prop(props, "mode", text="", expand=True, icon_only=True) + pass + return dict( + idname="builtin.select_box", + label="Select Box", + icon="ops.generic.select_box", + widget=None, + keymap="Sequencer Tool: Select Box", + draw_settings=draw_settings, + ) + class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel): bl_space_type = 'IMAGE_EDITOR' @@ -2150,12 +2195,71 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): *_tools_annotate, ], } +class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): + bl_space_type = 'SEQUENCE_EDITOR' + bl_region_type = 'TOOLS' + bl_label = "Tools" # not visible + bl_options = {'HIDE_HEADER'} + + # Satisfy the 'ToolSelectPanelHelper' API. + keymap_prefix = "Sequence Editor Tool:" + + # Default group to use as a fallback. + tool_fallback_id = "builtin.select" + + @classmethod + def tools_from_context(cls, context, mode=None): + if mode is None: + if context.space_data: + mode = context.space_data.view_type + for tools in (cls._tools[None], cls._tools.get(mode, ())): + for item in tools: + if not (type(item) is ToolDef) and callable(item): + yield from item(context) + else: + yield item + + @classmethod + def tools_all(cls): + yield from cls._tools.items() + _tools_select = ( + ( + _defs_sequencer_select.select, + _defs_sequencer_select.box, + ), + ) + _tools_annotate = ( + ( + _defs_annotate.scribble, + _defs_annotate.line, + _defs_annotate.poly, + _defs_annotate.eraser, + ), + ) + + _tools = { + None: [ + ], + 'PREVIEW': [ + *_tools_annotate, + ], + 'SEQUENCER': [ + *_tools_select, + _defs_sequencer_generic.cut, + ], + 'SEQUENCER_PREVIEW': [ + *_tools_select, + *_tools_annotate, + _defs_sequencer_generic.cut, + ], + } classes = ( IMAGE_PT_tools_active, NODE_PT_tools_active, VIEW3D_PT_tools_active, + SEQUENCER_PT_tools_active, ) if __name__ == "__main__": # only for live edit. diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 4f35bcc29df..ad5e7b5442c 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -997,6 +997,7 @@ class PreferenceThemeSpacePanel: "freestyle_face_mark", "split_normal", "bone_solid", + "bone_locked_weight", "paint_curve_pivot", }, 'GRAPH_EDITOR': { diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 011c2a8b39a..19d5e3da309 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -560,12 +560,23 @@ class VIEW3D_PT_slots_projectpaint(View3DPanel, Panel): layout.operator("image.save_all_modified", text="Save All Images", icon='FILE_TICK') +class VIEW3D_PT_mask(View3DPanel, Panel): + bl_category = "Tool" + bl_context = ".imagepaint" # dot on purpose (access from topbar) + bl_label = "Masking" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + pass + + # TODO, move to space_view3d.py class VIEW3D_PT_stencil_projectpaint(View3DPanel, Panel): bl_category = "Tool" bl_context = ".imagepaint" # dot on purpose (access from topbar) - bl_label = "Mask" + bl_label = "Stencil Mask" bl_options = {'DEFAULT_CLOSED'} + bl_parent_id = "VIEW3D_PT_mask" bl_ui_units_x = 14 @classmethod @@ -1192,6 +1203,7 @@ class VIEW3D_PT_tools_imagepaint_options_cavity(View3DPaintPanel, Panel): bl_context = ".imagepaint" # dot on purpose (access from topbar) bl_label = "Cavity Mask" bl_parent_id = "VIEW3D_PT_tools_imagepaint_options" + bl_parent_id = "VIEW3D_PT_mask" bl_options = {'DEFAULT_CLOSED'} def draw_header(self, context): @@ -1876,7 +1888,7 @@ classes = ( VIEW3D_PT_tools_curveedit_options_stroke, VIEW3D_PT_tools_armatureedit_options, VIEW3D_PT_tools_posemode_options, - + VIEW3D_PT_slots_projectpaint, VIEW3D_PT_tools_brush_select, VIEW3D_PT_tools_brush_settings, @@ -1886,7 +1898,6 @@ classes = ( VIEW3D_PT_tools_brush_clone, TEXTURE_UL_texpaintslots, VIEW3D_MT_tools_projectpaint_uvlayer, - VIEW3D_PT_stencil_projectpaint, VIEW3D_PT_tools_brush_texture, VIEW3D_PT_tools_mask_texture, VIEW3D_PT_tools_brush_stroke, @@ -1912,9 +1923,13 @@ classes = ( VIEW3D_PT_tools_vertexpaint_symmetry_for_topbar, VIEW3D_PT_tools_vertexpaint_options, + VIEW3D_PT_mask, + VIEW3D_PT_stencil_projectpaint, + VIEW3D_PT_tools_imagepaint_options_cavity, + VIEW3D_PT_tools_imagepaint_symmetry, VIEW3D_PT_tools_imagepaint_options, - VIEW3D_PT_tools_imagepaint_options_cavity, + VIEW3D_PT_tools_imagepaint_options_external, VIEW3D_MT_tools_projectpaint_stencil, diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c index 10bb1bd3c9c..2b592c9e550 100644 --- a/source/blender/blenfont/intern/blf.c +++ b/source/blender/blenfont/intern/blf.c @@ -935,10 +935,7 @@ void blf_draw_buffer__start(FontBLF *font) { FontBufInfoBLF *buf_info = &font->buf_info; - buf_info->col_char[0] = buf_info->col_init[0] * 255; - buf_info->col_char[1] = buf_info->col_init[1] * 255; - buf_info->col_char[2] = buf_info->col_init[2] * 255; - buf_info->col_char[3] = buf_info->col_init[3] * 255; + rgba_float_to_uchar(buf_info->col_char, buf_info->col_init); if (buf_info->display) { copy_v4_v4(buf_info->col_float, buf_info->col_init); diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h index 963e3158d46..9da17d777cd 100644 --- a/source/blender/blenkernel/BKE_animsys.h +++ b/source/blender/blenkernel/BKE_animsys.h @@ -249,6 +249,10 @@ typedef enum eAnimData_Recalc { ADT_RECALC_ALL = (ADT_RECALC_DRIVERS | ADT_RECALC_ANIM), } eAnimData_Recalc; +bool BKE_animsys_store_rna_setting(struct PointerRNA *ptr, + const char *rna_path, + const int array_index, + struct PathResolvedRNA *r_result); bool BKE_animsys_read_rna_setting(struct PathResolvedRNA *anim_rna, float *r_value); bool BKE_animsys_write_rna_setting(struct PathResolvedRNA *anim_rna, const float value); diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 75e14b7efca..4fcb10b29f3 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -26,8 +26,8 @@ * * \note Use #STRINGIFY() rather than defining with quotes. */ -#define BLENDER_VERSION 282 -#define BLENDER_SUBVERSION 6 +#define BLENDER_VERSION 283 +#define BLENDER_SUBVERSION 1 /** Several breakages with 280, e.g. collections vs layers. */ #define BLENDER_MINVERSION 280 #define BLENDER_MINSUBVERSION 0 @@ -36,7 +36,7 @@ /** Can be left blank, otherwise a,b,c... etc with no quotes. */ #define BLENDER_VERSION_CHAR /** alpha/beta/rc/release, docs use this. */ -#define BLENDER_VERSION_CYCLE beta +#define BLENDER_VERSION_CYCLE alpha /** Optionally set to 1,2,... for example to get alpha1 or rc2. */ #define BLENDER_VERSION_CYCLE_NUMBER diff --git a/source/blender/blenkernel/BKE_cloth.h b/source/blender/blenkernel/BKE_cloth.h index 17de53be42a..2862dda8ead 100644 --- a/source/blender/blenkernel/BKE_cloth.h +++ b/source/blender/blenkernel/BKE_cloth.h @@ -74,11 +74,11 @@ typedef struct ClothSolverResult { * own connectivity of the mesh based on the actual edges in the mesh. */ typedef struct Cloth { - struct ClothVertex *verts; /* The vertices that represent this cloth. */ - struct LinkNode *springs; /* The springs connecting the mesh. */ - unsigned int numsprings; /* The count of springs. */ - unsigned int mvert_num; /* The number of verts == m * n. */ - unsigned int tri_num; + struct ClothVertex *verts; /* The vertices that represent this cloth. */ + struct LinkNode *springs; /* The springs connecting the mesh. */ + unsigned int numsprings; /* The count of springs. */ + unsigned int mvert_num; /* The number of verts == m * n. */ + unsigned int primitive_num; /* Number of triangles for cloth and edges for hair. */ unsigned char old_solver_type; /* unused, only 1 solver here */ unsigned char pad2; short pad3; @@ -89,6 +89,7 @@ typedef struct Cloth { struct EdgeSet *edgeset; /* used for selfcollisions */ int last_frame; float initial_mesh_volume; /* Initial volume of the mesh. Used for pressure */ + struct MEdge *edges; /* Used for hair collisions. */ } Cloth; /** @@ -265,15 +266,6 @@ int cloth_bvh_collision(struct Depsgraph *depsgraph, float step, float dt); -void cloth_find_point_contacts(struct Depsgraph *depsgraph, - struct Object *ob, - struct ClothModifierData *clmd, - float step, - float dt, - ColliderContacts **r_collider_contacts, - int *r_totcolliders); -void cloth_free_contacts(ColliderContacts *collider_contacts, int totcolliders); - //////////////////////////////////////////////// ///////////////////////////////////////////////// diff --git a/source/blender/blenkernel/BKE_lightprobe.h b/source/blender/blenkernel/BKE_lightprobe.h index bd442c97000..153ad9bb915 100644 --- a/source/blender/blenkernel/BKE_lightprobe.h +++ b/source/blender/blenkernel/BKE_lightprobe.h @@ -29,6 +29,7 @@ struct LightProbe; struct Main; void BKE_lightprobe_init(struct LightProbe *probe); +void BKE_lightprobe_type_set(struct LightProbe *probe, const short lightprobe_type); void *BKE_lightprobe_add(struct Main *bmain, const char *name); void BKE_lightprobe_copy_data(struct Main *bmain, struct LightProbe *probe_dst, diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index 33bcd3bc91d..aba3a5d5b8d 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -504,6 +504,7 @@ enum { SEQ_SIDE_LEFT, SEQ_SIDE_RIGHT, SEQ_SIDE_BOTH, + SEQ_SIDE_NO_CHANGE, }; int BKE_sequencer_find_next_prev_edit(struct Scene *scene, int cfra, diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 32420e2e894..be6622e5d42 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -1665,11 +1665,11 @@ void BKE_keyingsets_free(ListBase *list) /* ***************************************** */ /* Evaluation Data-Setting Backend */ -static bool animsys_store_rna_setting(PointerRNA *ptr, - /* typically 'fcu->rna_path', 'fcu->array_index' */ - const char *rna_path, - const int array_index, - PathResolvedRNA *r_result) +bool BKE_animsys_store_rna_setting(PointerRNA *ptr, + /* typically 'fcu->rna_path', 'fcu->array_index' */ + const char *rna_path, + const int array_index, + PathResolvedRNA *r_result) { bool success = false; const char *path = rna_path; @@ -1880,7 +1880,7 @@ static void animsys_write_orig_anim_rna(PointerRNA *ptr, } PathResolvedRNA orig_anim_rna; /* TODO(sergey): Should be possible to cache resolved path in dependency graph somehow. */ - if (animsys_store_rna_setting(&ptr_orig, rna_path, array_index, &orig_anim_rna)) { + if (BKE_animsys_store_rna_setting(&ptr_orig, rna_path, array_index, &orig_anim_rna)) { BKE_animsys_write_rna_setting(&orig_anim_rna, value); } } @@ -1910,7 +1910,7 @@ static void animsys_evaluate_fcurves(PointerRNA *ptr, continue; } PathResolvedRNA anim_rna; - if (animsys_store_rna_setting(ptr, fcu->rna_path, fcu->array_index, &anim_rna)) { + if (BKE_animsys_store_rna_setting(ptr, fcu->rna_path, fcu->array_index, &anim_rna)) { const float curval = calculate_fcurve(&anim_rna, fcu, ctime); BKE_animsys_write_rna_setting(&anim_rna, curval); if (flush_to_original) { @@ -1944,7 +1944,7 @@ static void animsys_evaluate_drivers(PointerRNA *ptr, AnimData *adt, float ctime * NOTE: for 'layering' option later on, we should check if we should remove old value * before adding new to only be done when drivers only changed. */ PathResolvedRNA anim_rna; - if (animsys_store_rna_setting(ptr, fcu->rna_path, fcu->array_index, &anim_rna)) { + if (BKE_animsys_store_rna_setting(ptr, fcu->rna_path, fcu->array_index, &anim_rna)) { const float curval = calculate_fcurve(&anim_rna, fcu, ctime); ok = BKE_animsys_write_rna_setting(&anim_rna, curval); } @@ -2023,7 +2023,7 @@ void animsys_evaluate_action_group(PointerRNA *ptr, bAction *act, bActionGroup * /* check if this curve should be skipped */ if ((fcu->flag & (FCURVE_MUTED | FCURVE_DISABLED)) == 0 && !BKE_fcurve_is_empty(fcu)) { PathResolvedRNA anim_rna; - if (animsys_store_rna_setting(ptr, fcu->rna_path, fcu->array_index, &anim_rna)) { + if (BKE_animsys_store_rna_setting(ptr, fcu->rna_path, fcu->array_index, &anim_rna)) { const float curval = calculate_fcurve(&anim_rna, fcu, ctime); BKE_animsys_write_rna_setting(&anim_rna, curval); } @@ -3803,7 +3803,7 @@ static void animsys_evaluate_overrides(PointerRNA *ptr, AnimData *adt) /* for each override, simply execute... */ for (aor = adt->overrides.first; aor; aor = aor->next) { PathResolvedRNA anim_rna; - if (animsys_store_rna_setting(ptr, aor->rna_path, aor->array_index, &anim_rna)) { + if (BKE_animsys_store_rna_setting(ptr, aor->rna_path, aor->array_index, &anim_rna)) { BKE_animsys_write_rna_setting(&anim_rna, aor->value); } } @@ -4125,7 +4125,7 @@ void BKE_animsys_eval_driver(Depsgraph *depsgraph, ID *id, int driver_index, FCu // printf("\told val = %f\n", fcu->curval); PathResolvedRNA anim_rna; - if (animsys_store_rna_setting(&id_ptr, fcu->rna_path, fcu->array_index, &anim_rna)) { + if (BKE_animsys_store_rna_setting(&id_ptr, fcu->rna_path, fcu->array_index, &anim_rna)) { /* Evaluate driver, and write results to COW-domain destination */ const float ctime = DEG_get_ctime(depsgraph); const float curval = calculate_fcurve(&anim_rna, fcu, ctime); diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index c26800aefba..7332c3e0d43 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -190,22 +190,36 @@ static BVHTree *bvhtree_build_from_cloth(ClothModifierData *clmd, float epsilon) vt = cloth->tri; /* in the moment, return zero if no faces there */ - if (!cloth->tri_num) { + if (!cloth->primitive_num) { return NULL; } /* create quadtree with k=26 */ - bvhtree = BLI_bvhtree_new(cloth->tri_num, epsilon, 4, 26); + bvhtree = BLI_bvhtree_new(cloth->primitive_num, epsilon, 4, 26); /* fill tree */ - for (i = 0; i < cloth->tri_num; i++, vt++) { - float co[3][3]; + if (clmd->hairdata == NULL) { + for (i = 0; i < cloth->primitive_num; i++, vt++) { + float co[3][3]; - copy_v3_v3(co[0], verts[vt->tri[0]].xold); - copy_v3_v3(co[1], verts[vt->tri[1]].xold); - copy_v3_v3(co[2], verts[vt->tri[2]].xold); + copy_v3_v3(co[0], verts[vt->tri[0]].xold); + copy_v3_v3(co[1], verts[vt->tri[1]].xold); + copy_v3_v3(co[2], verts[vt->tri[2]].xold); - BLI_bvhtree_insert(bvhtree, i, co[0], 3); + BLI_bvhtree_insert(bvhtree, i, co[0], 3); + } + } + else { + MEdge *edges = cloth->edges; + + for (i = 0; i < cloth->primitive_num; i++) { + float co[2][3]; + + copy_v3_v3(co[0], verts[edges[i].v1].xold); + copy_v3_v3(co[1], verts[edges[i].v2].xold); + + BLI_bvhtree_insert(bvhtree, i, co[0], 2); + } } /* balance tree */ @@ -222,6 +236,8 @@ void bvhtree_update_from_cloth(ClothModifierData *clmd, bool moving, bool self) ClothVertex *verts = cloth->verts; const MVertTri *vt; + BLI_assert(!(clmd->hairdata != NULL && self)); + if (self) { bvhtree = cloth->bvhselftree; } @@ -236,39 +252,59 @@ void bvhtree_update_from_cloth(ClothModifierData *clmd, bool moving, bool self) vt = cloth->tri; /* update vertex position in bvh tree */ - if (verts && vt) { - for (i = 0; i < cloth->tri_num; i++, vt++) { - float co[3][3], co_moving[3][3]; - bool ret; - - /* copy new locations into array */ - if (moving) { - copy_v3_v3(co[0], verts[vt->tri[0]].txold); - copy_v3_v3(co[1], verts[vt->tri[1]].txold); - copy_v3_v3(co[2], verts[vt->tri[2]].txold); - - /* update moving positions */ - copy_v3_v3(co_moving[0], verts[vt->tri[0]].tx); - copy_v3_v3(co_moving[1], verts[vt->tri[1]].tx); - copy_v3_v3(co_moving[2], verts[vt->tri[2]].tx); - - ret = BLI_bvhtree_update_node(bvhtree, i, co[0], co_moving[0], 3); - } - else { - copy_v3_v3(co[0], verts[vt->tri[0]].tx); - copy_v3_v3(co[1], verts[vt->tri[1]].tx); - copy_v3_v3(co[2], verts[vt->tri[2]].tx); + if (clmd->hairdata == NULL) { + if (verts && vt) { + for (i = 0; i < cloth->primitive_num; i++, vt++) { + float co[3][3], co_moving[3][3]; + bool ret; + + /* copy new locations into array */ + if (moving) { + copy_v3_v3(co[0], verts[vt->tri[0]].txold); + copy_v3_v3(co[1], verts[vt->tri[1]].txold); + copy_v3_v3(co[2], verts[vt->tri[2]].txold); + + /* update moving positions */ + copy_v3_v3(co_moving[0], verts[vt->tri[0]].tx); + copy_v3_v3(co_moving[1], verts[vt->tri[1]].tx); + copy_v3_v3(co_moving[2], verts[vt->tri[2]].tx); + + ret = BLI_bvhtree_update_node(bvhtree, i, co[0], co_moving[0], 3); + } + else { + copy_v3_v3(co[0], verts[vt->tri[0]].tx); + copy_v3_v3(co[1], verts[vt->tri[1]].tx); + copy_v3_v3(co[2], verts[vt->tri[2]].tx); - ret = BLI_bvhtree_update_node(bvhtree, i, co[0], NULL, 3); - } + ret = BLI_bvhtree_update_node(bvhtree, i, co[0], NULL, 3); + } - /* check if tree is already full */ - if (ret == false) { - break; + /* check if tree is already full */ + if (ret == false) { + break; + } } + + BLI_bvhtree_update_tree(bvhtree); } + } + else { + if (verts) { + MEdge *edges = cloth->edges; + + for (i = 0; i < cloth->primitive_num; i++) { + float co[2][3]; - BLI_bvhtree_update_tree(bvhtree); + copy_v3_v3(co[0], verts[edges[i].v1].tx); + copy_v3_v3(co[1], verts[edges[i].v2].tx); + + if (!BLI_bvhtree_update_node(bvhtree, i, co[0], NULL, 2)) { + break; + } + } + + BLI_bvhtree_update_tree(bvhtree); + } } } @@ -900,7 +936,13 @@ static void cloth_from_mesh(ClothModifierData *clmd, Mesh *mesh) } /* save face information */ - clmd->clothObject->tri_num = looptri_num; + if (clmd->hairdata == NULL) { + clmd->clothObject->primitive_num = looptri_num; + } + else { + clmd->clothObject->primitive_num = mesh->totedge; + } + clmd->clothObject->tri = MEM_mallocN(sizeof(MVertTri) * looptri_num, "clothLoopTris"); if (clmd->clothObject->tri == NULL) { cloth_free_modifier(clmd); @@ -910,6 +952,8 @@ static void cloth_from_mesh(ClothModifierData *clmd, Mesh *mesh) } BKE_mesh_runtime_verttri_from_looptri(clmd->clothObject->tri, mloop, looptri, looptri_num); + clmd->clothObject->edges = mesh->medge; + /* Free the springs since they can't be correct if the vertices * changed. */ diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c index 220b9417a6c..5db42618a9e 100644 --- a/source/blender/blenkernel/intern/collision.c +++ b/source/blender/blenkernel/intern/collision.c @@ -33,6 +33,7 @@ #include "BLI_utildefines.h" #include "BLI_blenlib.h" +#include "BLI_linklist.h" #include "BLI_math.h" #include "BLI_task.h" #include "BLI_threads.h" @@ -193,17 +194,17 @@ BLI_INLINE int next_ind(int i) return (++i < 3) ? i : 0; } -static float compute_collision_point(float a1[3], - const float a2[3], - const float a3[3], - const float b1[3], - const float b2[3], - const float b3[3], - bool culling, - bool use_normal, - float r_a[3], - float r_b[3], - float r_vec[3]) +static float compute_collision_point_tri_tri(const float a1[3], + const float a2[3], + const float a3[3], + const float b1[3], + const float b2[3], + const float b3[3], + bool culling, + bool use_normal, + float r_a[3], + float r_b[3], + float r_vec[3]) { float a[3][3]; float b[3][3]; @@ -423,6 +424,179 @@ static float compute_collision_point(float a1[3], return dist; } +static float compute_collision_point_edge_tri(const float a1[3], + const float a2[3], + const float b1[3], + const float b2[3], + const float b3[3], + bool culling, + bool use_normal, + float r_a[3], + float r_b[3], + float r_vec[3]) +{ + float a[2][3]; + float b[3][3]; + float dist = FLT_MAX; + float tmp_co1[3], tmp_co2[3]; + float isect_a[3]; + bool isect = false; + float tmp, tmp_vec[3]; + float normal[3], cent[3]; + bool backside = false; + + copy_v3_v3(a[0], a1); + copy_v3_v3(a[1], a2); + + copy_v3_v3(b[0], b1); + copy_v3_v3(b[1], b2); + copy_v3_v3(b[2], b3); + + normal_tri_v3(normal, b[0], b[1], b[2]); + + /* Find intersection. */ + if (isect_line_segment_tri_v3(a[0], a[1], b[0], b[1], b[2], &tmp, NULL)) { + interp_v3_v3v3(isect_a, a[0], a[1], tmp); + isect = true; + } + + /* Determine collision side. */ + if (culling) { + if (isect) { + backside = true; + } + else { + mid_v3_v3v3v3(cent, b[0], b[1], b[2]); + + for (int i = 0; i < 2; i++) { + sub_v3_v3v3(tmp_vec, a[i], cent); + if (dot_v3v3(tmp_vec, normal) < 0.0f) { + backside = true; + break; + } + } + } + } + + if (isect) { + /* Edge intersection. */ + copy_v3_v3(r_a, isect_a); + copy_v3_v3(r_b, isect_a); + + copy_v3_v3(r_vec, normal); + + return 0.0f; + } + + if (backside) { + float maxdist = 0.0f; + bool found = false; + + /* Point projections. */ + for (int i = 0; i < 2; i++) { + if (isect_ray_tri_v3(a[i], normal, b[0], b[1], b[2], &tmp, NULL)) { + if (tmp > maxdist) { + maxdist = tmp; + copy_v3_v3(r_a, a[i]); + madd_v3_v3v3fl(r_b, a[i], normal, tmp); + found = true; + } + } + } + + /* Edge projections. */ + for (int i = 0; i < 3; i++) { + float dir[3]; + + sub_v3_v3v3(tmp_vec, b[next_ind(i)], b[i]); + cross_v3_v3v3(dir, tmp_vec, normal); + + if (isect_line_plane_v3(tmp_co1, a[0], a[1], b[i], dir) && + point_in_slice_seg(tmp_co1, a[0], a[1]) && + point_in_slice_seg(tmp_co1, b[i], b[next_ind(i)])) { + closest_to_line_v3(tmp_co2, tmp_co1, b[i], b[next_ind(i)]); + sub_v3_v3v3(tmp_vec, tmp_co1, tmp_co2); + tmp = len_v3(tmp_vec); + + if ((tmp > maxdist) && (dot_v3v3(tmp_vec, normal) < 0.0f)) { + maxdist = tmp; + copy_v3_v3(r_a, tmp_co1); + copy_v3_v3(r_b, tmp_co2); + found = true; + } + } + } + + /* If no point is found, will fallback onto regular proximity test below. */ + if (found) { + sub_v3_v3v3(r_vec, r_b, r_a); + + if (use_normal) { + if (dot_v3v3(normal, r_vec) >= 0.0f) { + copy_v3_v3(r_vec, normal); + } + else { + negate_v3_v3(r_vec, normal); + } + } + + return 0.0f; + } + } + + /* Closest point. */ + for (int i = 0; i < 2; i++) { + closest_on_tri_to_point_v3(tmp_co1, a[i], b[0], b[1], b[2]); + tmp = len_squared_v3v3(tmp_co1, a[i]); + + if (tmp < dist) { + dist = tmp; + copy_v3_v3(r_a, a[i]); + copy_v3_v3(r_b, tmp_co1); + } + } + + /* Closest edge. */ + if (!isect) { + for (int j = 0; j < 3; j++) { + isect_seg_seg_v3(a[0], a[1], b[j], b[next_ind(j)], tmp_co1, tmp_co2); + tmp = len_squared_v3v3(tmp_co1, tmp_co2); + + if (tmp < dist) { + dist = tmp; + copy_v3_v3(r_a, tmp_co1); + copy_v3_v3(r_b, tmp_co2); + } + } + } + + if (isect) { + sub_v3_v3v3(r_vec, r_b, r_a); + dist = 0.0f; + } + else { + sub_v3_v3v3(r_vec, r_a, r_b); + dist = sqrtf(dist); + } + + if (culling && use_normal) { + copy_v3_v3(r_vec, normal); + } + else if (use_normal) { + if (dot_v3v3(normal, r_vec) >= 0.0f) { + copy_v3_v3(r_vec, normal); + } + else { + negate_v3_v3(r_vec, normal); + } + } + else if (culling && (dot_v3v3(r_vec, normal) < 0.0f)) { + return FLT_MAX; + } + + return dist; +} + // w3 is not perfect static void collision_compute_barycentric( const float pv[3], float p1[3], float p2[3], float p3[3], float *w1, float *w2, float *w3) @@ -494,6 +668,7 @@ static int cloth_collision_response_static(ClothModifierData *clmd, float v1[3], v2[3], relativeVelocity[3]; float magrelVel; float epsilon2 = BLI_bvhtree_get_epsilon(collmd->bvhtree); + const bool is_hair = (clmd->hairdata != NULL); cloth1 = clmd->clothObject; @@ -509,14 +684,32 @@ static int cloth_collision_response_static(ClothModifierData *clmd, continue; } - /* Compute barycentric coordinates for both collision points. */ - collision_compute_barycentric(collpair->pa, - cloth1->verts[collpair->ap1].tx, - cloth1->verts[collpair->ap2].tx, - cloth1->verts[collpair->ap3].tx, - &w1, - &w2, - &w3); + /* Compute barycentric coordinates and relative "velocity" for both collision points. */ + if (is_hair) { + w2 = line_point_factor_v3( + collpair->pa, cloth1->verts[collpair->ap1].tx, cloth1->verts[collpair->ap2].tx); + + w1 = 1.0f - w2; + + interp_v3_v3v3(v1, cloth1->verts[collpair->ap1].tv, cloth1->verts[collpair->ap2].tv, w2); + } + else { + collision_compute_barycentric(collpair->pa, + cloth1->verts[collpair->ap1].tx, + cloth1->verts[collpair->ap2].tx, + cloth1->verts[collpair->ap3].tx, + &w1, + &w2, + &w3); + + collision_interpolateOnTriangle(v1, + cloth1->verts[collpair->ap1].tv, + cloth1->verts[collpair->ap2].tv, + cloth1->verts[collpair->ap3].tv, + w1, + w2, + w3); + } collision_compute_barycentric(collpair->pb, collmd->current_xnew[collpair->bp1].co, @@ -526,15 +719,6 @@ static int cloth_collision_response_static(ClothModifierData *clmd, &u2, &u3); - /* Calculate relative "velocity". */ - collision_interpolateOnTriangle(v1, - cloth1->verts[collpair->ap1].tv, - cloth1->verts[collpair->ap2].tv, - cloth1->verts[collpair->ap3].tv, - w1, - w2, - w3); - collision_interpolateOnTriangle(v2, collmd->current_v[collpair->bp1].co, collmd->current_v[collpair->bp2].co, @@ -576,7 +760,10 @@ static int cloth_collision_response_static(ClothModifierData *clmd, VECADDMUL(i1, vrel_t_pre, w1 * impulse); VECADDMUL(i2, vrel_t_pre, w2 * impulse); - VECADDMUL(i3, vrel_t_pre, w3 * impulse); + + if (!is_hair) { + VECADDMUL(i3, vrel_t_pre, w3 * impulse); + } } /* Apply velocity stopping impulse. */ @@ -588,8 +775,10 @@ static int cloth_collision_response_static(ClothModifierData *clmd, VECADDMUL(i2, collpair->normal, w2 * impulse); cloth1->verts[collpair->ap2].impulse_count++; - VECADDMUL(i3, collpair->normal, w3 * impulse); - cloth1->verts[collpair->ap3].impulse_count++; + if (!is_hair) { + VECADDMUL(i3, collpair->normal, w3 * impulse); + cloth1->verts[collpair->ap3].impulse_count++; + } time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale); @@ -609,7 +798,10 @@ static int cloth_collision_response_static(ClothModifierData *clmd, VECADDMUL(i1, collpair->normal, impulse); VECADDMUL(i2, collpair->normal, impulse); - VECADDMUL(i3, collpair->normal, impulse); + + if (!is_hair) { + VECADDMUL(i3, collpair->normal, impulse); + } } result = 1; @@ -627,11 +819,17 @@ static int cloth_collision_response_static(ClothModifierData *clmd, VECADDMUL(i1, collpair->normal, w1 * impulse); VECADDMUL(i2, collpair->normal, w2 * impulse); - VECADDMUL(i3, collpair->normal, w3 * impulse); + + if (!is_hair) { + VECADDMUL(i3, collpair->normal, w3 * impulse); + } cloth1->verts[collpair->ap1].impulse_count++; cloth1->verts[collpair->ap2].impulse_count++; - cloth1->verts[collpair->ap3].impulse_count++; + + if (!is_hair) { + cloth1->verts[collpair->ap3].impulse_count++; + } result = 1; } @@ -656,9 +854,11 @@ static int cloth_collision_response_static(ClothModifierData *clmd, cloth1->verts[collpair->ap2].impulse[j] = i2[j]; } - if (cloth1->verts[collpair->ap3].impulse_count > 0 && - ABS(cloth1->verts[collpair->ap3].impulse[j]) < ABS(i3[j])) { - cloth1->verts[collpair->ap3].impulse[j] = i3[j]; + if (!is_hair) { + if (cloth1->verts[collpair->ap3].impulse_count > 0 && + ABS(cloth1->verts[collpair->ap3].impulse[j]) < ABS(i3[j])) { + cloth1->verts[collpair->ap3].impulse[j] = i3[j]; + } } } } @@ -875,17 +1075,17 @@ static void cloth_collision(void *__restrict userdata, tri_b = &collmd->tri[data->overlap[index].indexB]; /* Compute distance and normal. */ - distance = compute_collision_point(verts1[tri_a->tri[0]].tx, - verts1[tri_a->tri[1]].tx, - verts1[tri_a->tri[2]].tx, - collmd->current_xnew[tri_b->tri[0]].co, - collmd->current_xnew[tri_b->tri[1]].co, - collmd->current_xnew[tri_b->tri[2]].co, - data->culling, - data->use_normal, - pa, - pb, - vect); + distance = compute_collision_point_tri_tri(verts1[tri_a->tri[0]].tx, + verts1[tri_a->tri[1]].tx, + verts1[tri_a->tri[2]].tx, + collmd->current_xnew[tri_b->tri[0]].co, + collmd->current_xnew[tri_b->tri[1]].co, + collmd->current_xnew[tri_b->tri[2]].co, + data->culling, + data->use_normal, + pa, + pb, + vect); if ((distance <= (epsilon1 + epsilon2 + ALMOST_ZERO)) && (len_squared_v3(vect) > ALMOST_ZERO)) { collpair[index].ap1 = tri_a->tri[0]; @@ -946,17 +1146,17 @@ static void cloth_selfcollision(void *__restrict userdata, } /* Compute distance and normal. */ - distance = compute_collision_point(verts1[tri_a->tri[0]].tx, - verts1[tri_a->tri[1]].tx, - verts1[tri_a->tri[2]].tx, - verts1[tri_b->tri[0]].tx, - verts1[tri_b->tri[1]].tx, - verts1[tri_b->tri[2]].tx, - false, - false, - pa, - pb, - vect); + distance = compute_collision_point_tri_tri(verts1[tri_a->tri[0]].tx, + verts1[tri_a->tri[1]].tx, + verts1[tri_a->tri[2]].tx, + verts1[tri_b->tri[0]].tx, + verts1[tri_b->tri[1]].tx, + verts1[tri_b->tri[2]].tx, + false, + false, + pa, + pb, + vect); if ((distance <= (epsilon * 2.0f + ALMOST_ZERO)) && (len_squared_v3(vect) > ALMOST_ZERO)) { collpair[index].ap1 = tri_a->tri[0]; @@ -983,6 +1183,64 @@ static void cloth_selfcollision(void *__restrict userdata, } } +static void hair_collision(void *__restrict userdata, + const int index, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + ColDetectData *data = (ColDetectData *)userdata; + + ClothModifierData *clmd = data->clmd; + CollisionModifierData *collmd = data->collmd; + CollPair *collpair = data->collisions; + const MVertTri *tri_coll; + const MEdge *edge_coll; + ClothVertex *verts1 = clmd->clothObject->verts; + float distance = 0.0f; + float epsilon1 = clmd->coll_parms->epsilon; + float epsilon2 = BLI_bvhtree_get_epsilon(collmd->bvhtree); + float pa[3], pb[3], vect[3]; + + /* TODO: This is not efficient. Might be wise to instead build an array before iterating, to + * avoid walking the list every time. */ + edge_coll = &clmd->clothObject->edges[data->overlap[index].indexA]; + tri_coll = &collmd->tri[data->overlap[index].indexB]; + + /* Compute distance and normal. */ + distance = compute_collision_point_edge_tri(verts1[edge_coll->v1].tx, + verts1[edge_coll->v2].tx, + collmd->current_x[tri_coll->tri[0]].co, + collmd->current_x[tri_coll->tri[1]].co, + collmd->current_x[tri_coll->tri[2]].co, + data->culling, + data->use_normal, + pa, + pb, + vect); + + if ((distance <= (epsilon1 + epsilon2 + ALMOST_ZERO)) && (len_squared_v3(vect) > ALMOST_ZERO)) { + collpair[index].ap1 = edge_coll->v1; + collpair[index].ap2 = edge_coll->v2; + + collpair[index].bp1 = tri_coll->tri[0]; + collpair[index].bp2 = tri_coll->tri[1]; + collpair[index].bp3 = tri_coll->tri[2]; + + copy_v3_v3(collpair[index].pa, pa); + copy_v3_v3(collpair[index].pb, pb); + copy_v3_v3(collpair[index].vector, vect); + + normalize_v3_v3(collpair[index].normal, collpair[index].vector); + + collpair[index].distance = distance; + collpair[index].flag = 0; + + data->collided = true; + } + else { + collpair[index].flag = COLLISION_INACTIVE; + } +} + static void add_collision_object(ListBase *relations, Object *ob, int level, @@ -1148,6 +1406,7 @@ static bool cloth_bvh_objcollisions_nearcheck(ClothModifierData *clmd, bool culling, bool use_normal) { + const bool is_hair = (clmd->hairdata != NULL); *collisions = (CollPair *)MEM_mallocN(sizeof(CollPair) * numresult, "collision array"); ColDetectData data = { @@ -1163,7 +1422,8 @@ static bool cloth_bvh_objcollisions_nearcheck(ClothModifierData *clmd, TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); settings.use_threading = true; - BLI_task_parallel_range(0, numresult, &data, cloth_collision, &settings); + BLI_task_parallel_range( + 0, numresult, &data, is_hair ? hair_collision : cloth_collision, &settings); return data.collided; } @@ -1308,8 +1568,14 @@ int cloth_bvh_collision( if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) { bvhtree_update_from_cloth(clmd, false, false); - collobjs = BKE_collision_objects_create( - depsgraph, ob, clmd->coll_parms->group, &numcollobj, eModifierType_Collision); + /* Enable self collision if this is a hair sim */ + const bool is_hair = (clmd->hairdata != NULL); + + collobjs = BKE_collision_objects_create(depsgraph, + is_hair ? NULL : ob, + clmd->coll_parms->group, + &numcollobj, + eModifierType_Collision); if (collobjs) { coll_counts_obj = MEM_callocN(sizeof(uint) * numcollobj, "CollCounts"); @@ -1474,286 +1740,3 @@ void collision_get_collider_velocity(float vel_old[3], /* XXX assume constant velocity of the collider for now */ copy_v3_v3(vel_old, vel_new); } - -BLI_INLINE bool cloth_point_face_collision_params(const float p1[3], - const float p2[3], - const float v0[3], - const float v1[3], - const float v2[3], - float r_nor[3], - float *r_lambda, - float r_w[3]) -{ - float edge1[3], edge2[3], p2face[3], p1p2[3], v0p2[3]; - float nor_v0p2, nor_p1p2; - - sub_v3_v3v3(edge1, v1, v0); - sub_v3_v3v3(edge2, v2, v0); - cross_v3_v3v3(r_nor, edge1, edge2); - normalize_v3(r_nor); - - sub_v3_v3v3(v0p2, p2, v0); - nor_v0p2 = dot_v3v3(v0p2, r_nor); - madd_v3_v3v3fl(p2face, p2, r_nor, -nor_v0p2); - interp_weights_tri_v3(r_w, v0, v1, v2, p2face); - - sub_v3_v3v3(p1p2, p2, p1); - nor_p1p2 = dot_v3v3(p1p2, r_nor); - *r_lambda = (nor_p1p2 != 0.0f ? nor_v0p2 / nor_p1p2 : 0.0f); - - return r_w[1] >= 0.0f && r_w[2] >= 0.0f && r_w[1] + r_w[2] <= 1.0f; -} - -static CollPair *cloth_point_collpair(float p1[3], - const float p2[3], - const MVert *mverts, - int bp1, - int bp2, - int bp3, - int index_cloth, - int index_coll, - float epsilon, - CollPair *collpair) -{ - const float *co1 = mverts[bp1].co, *co2 = mverts[bp2].co, *co3 = mverts[bp3].co; - float lambda /*, distance1 */, distance2; - float facenor[3], v1p1[3], v1p2[3]; - float w[3]; - - if (!cloth_point_face_collision_params(p1, p2, co1, co2, co3, facenor, &lambda, w)) { - return collpair; - } - - sub_v3_v3v3(v1p1, p1, co1); - // distance1 = dot_v3v3(v1p1, facenor); - sub_v3_v3v3(v1p2, p2, co1); - distance2 = dot_v3v3(v1p2, facenor); - // if (distance2 > epsilon || (distance1 < 0.0f && distance2 < 0.0f)) - if (distance2 > epsilon) { - return collpair; - } - - collpair->face1 = index_cloth; /* XXX actually not a face, but equivalent index for point */ - collpair->face2 = index_coll; - collpair->ap1 = index_cloth; - collpair->ap2 = collpair->ap3 = -1; /* unused */ - collpair->bp1 = bp1; - collpair->bp2 = bp2; - collpair->bp3 = bp3; - - /* note: using the second point here, which is - * the current updated position that needs to be corrected - */ - copy_v3_v3(collpair->pa, p2); - collpair->distance = distance2; - mul_v3_v3fl(collpair->vector, facenor, -distance2); - - interp_v3_v3v3v3(collpair->pb, co1, co2, co3, w); - - copy_v3_v3(collpair->normal, facenor); - collpair->time = lambda; - collpair->flag = 0; - - collpair++; - return collpair; -} - -/* Determines collisions on overlap, - * collisions are written to collpair[i] and collision+number_collision_found is returned. */ -static CollPair *cloth_point_collision(ModifierData *md1, - ModifierData *md2, - BVHTreeOverlap *overlap, - float epsilon, - CollPair *collpair, - float UNUSED(dt)) -{ - ClothModifierData *clmd = (ClothModifierData *)md1; - CollisionModifierData *collmd = (CollisionModifierData *)md2; - /* Cloth *cloth = clmd->clothObject; */ /* UNUSED */ - ClothVertex *vert = NULL; - const MVertTri *vt; - const MVert *mverts = collmd->current_x; - - vert = &clmd->clothObject->verts[overlap->indexA]; - vt = &collmd->tri[overlap->indexB]; - - collpair = cloth_point_collpair(vert->tx, - vert->x, - mverts, - vt->tri[0], - vt->tri[1], - vt->tri[2], - overlap->indexA, - overlap->indexB, - epsilon, - collpair); - - return collpair; -} - -static void cloth_points_objcollisions_nearcheck(ClothModifierData *clmd, - CollisionModifierData *collmd, - CollPair **collisions, - CollPair **collisions_index, - int numresult, - BVHTreeOverlap *overlap, - float epsilon, - double dt) -{ - int i; - - /* can return 2 collisions in total */ - *collisions = (CollPair *)MEM_mallocN(sizeof(CollPair) * numresult * 2, "collision array"); - *collisions_index = *collisions; - - for (i = 0; i < numresult; i++) { - *collisions_index = cloth_point_collision( - (ModifierData *)clmd, (ModifierData *)collmd, overlap + i, epsilon, *collisions_index, dt); - } -} - -void cloth_find_point_contacts(Depsgraph *depsgraph, - Object *ob, - ClothModifierData *clmd, - float step, - float dt, - ColliderContacts **r_collider_contacts, - int *r_totcolliders) -{ - Cloth *cloth = clmd->clothObject; - BVHTree *cloth_bvh; - unsigned int i = 0, mvert_num = 0; - ClothVertex *verts = NULL; - - ColliderContacts *collider_contacts; - - Object **collobjs = NULL; - unsigned int numcollobj = 0; - - verts = cloth->verts; - mvert_num = cloth->mvert_num; - - //////////////////////////////////////////////////////////// - // static collisions - //////////////////////////////////////////////////////////// - - /* Check we do have collision objects to test against, before doing anything else. */ - collobjs = BKE_collision_objects_create( - depsgraph, ob, clmd->coll_parms->group, &numcollobj, eModifierType_Collision); - if (!collobjs) { - *r_collider_contacts = NULL; - *r_totcolliders = 0; - return; - } - - // create temporary cloth points bvh - cloth_bvh = BLI_bvhtree_new(mvert_num, clmd->coll_parms->epsilon, 4, 6); - /* fill tree */ - for (i = 0; i < mvert_num; i++) { - float co[6]; - - copy_v3_v3(&co[0 * 3], verts[i].x); - copy_v3_v3(&co[1 * 3], verts[i].tx); - - BLI_bvhtree_insert(cloth_bvh, i, co, 2); - } - /* balance tree */ - BLI_bvhtree_balance(cloth_bvh); - - /* move object to position (step) in time */ - for (i = 0; i < numcollobj; i++) { - Object *collob = collobjs[i]; - CollisionModifierData *collmd = (CollisionModifierData *)modifiers_findByType( - collob, eModifierType_Collision); - if (!collmd->bvhtree) { - continue; - } - - /* move object to position (step) in time */ - collision_move_object(collmd, step + dt, step, true); - } - - collider_contacts = MEM_callocN(sizeof(ColliderContacts) * numcollobj, "CollPair"); - - // check all collision objects - for (i = 0; i < numcollobj; i++) { - ColliderContacts *ct = collider_contacts + i; - Object *collob = collobjs[i]; - CollisionModifierData *collmd = (CollisionModifierData *)modifiers_findByType( - collob, eModifierType_Collision); - BVHTreeOverlap *overlap; - unsigned int result = 0; - float epsilon; - - ct->ob = collob; - ct->collmd = collmd; - ct->collisions = NULL; - ct->totcollisions = 0; - - if (!collmd->bvhtree) { - continue; - } - - /* search for overlapping collision pairs */ - overlap = BLI_bvhtree_overlap(cloth_bvh, collmd->bvhtree, &result, NULL, NULL); - epsilon = BLI_bvhtree_get_epsilon(collmd->bvhtree); - - // go to next object if no overlap is there - if (result && overlap) { - CollPair *collisions_index; - - /* check if collisions really happen (costly near check) */ - cloth_points_objcollisions_nearcheck( - clmd, collmd, &ct->collisions, &collisions_index, result, overlap, epsilon, dt); - ct->totcollisions = (int)(collisions_index - ct->collisions); - - /* Resolve nearby collisions. */ -#if 0 - ret += cloth_points_objcollisions_resolve( - clmd, collmd, collob->pd, collisions[i], collisions_index[i], dt); -#endif - } - - if (overlap) { - MEM_freeN(overlap); - } - } - - BKE_collision_objects_free(collobjs); - - BLI_bvhtree_free(cloth_bvh); - - //////////////////////////////////////////////////////////// - // update positions - // this is needed for bvh_calc_DOP_hull_moving() [kdop.c] - //////////////////////////////////////////////////////////// - - // verts come from clmd - for (i = 0; i < mvert_num; i++) { - if (clmd->sim_parms->vgroup_mass > 0) { - if (verts[i].flags & CLOTH_VERT_FLAG_PINNED) { - continue; - } - } - - add_v3_v3v3(verts[i].tx, verts[i].txold, verts[i].tv); - } - //////////////////////////////////////////////////////////// - - *r_collider_contacts = collider_contacts; - *r_totcolliders = numcollobj; -} - -void cloth_free_contacts(ColliderContacts *collider_contacts, int totcolliders) -{ - if (collider_contacts) { - int i; - for (i = 0; i < totcolliders; i++) { - ColliderContacts *ct = collider_contacts + i; - if (ct->collisions) { - MEM_freeN(ct->collisions); - } - } - MEM_freeN(collider_contacts); - } -} diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 12bb7b573bd..4f0ff8bdcd3 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -1805,91 +1805,88 @@ void BKE_curve_bevel_make(Object *ob, ListBase *disp) } } else { - short dnr; - - /* bevel now in three parts, for proper vertex normals */ - /* part 1, back */ - - if ((cu->flag & CU_BACK) || !(cu->flag & CU_FRONT)) { - dnr = nr = 2 + cu->bevresol; - if ((cu->flag & (CU_FRONT | CU_BACK)) == 0) { - nr = 3 + 2 * cu->bevresol; - } - dl = MEM_callocN(sizeof(DispList), "makebevelcurve p1"); - dl->verts = MEM_malloc_arrayN(nr, sizeof(float[3]), "makebevelcurve p1"); - BLI_addtail(disp, dl); - dl->type = DL_SEGM; - dl->parts = 1; - dl->flag = DL_BACK_CURVE; - dl->nr = nr; - - /* half a circle */ - fp = dl->verts; - dangle = ((float)M_PI_2 / (dnr - 1)); - angle = -(nr - 1) * dangle; - - for (a = 0; a < nr; a++) { + /* The general case for nonzero extrusion or an incomplete loop. */ + dl = MEM_callocN(sizeof(DispList), "makebevelcurve"); + if ((cu->flag & (CU_FRONT | CU_BACK)) == 0) { + /* The full loop. */ + nr = 4 * cu->bevresol + 6; + dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE; + } + else if ((cu->flag & CU_FRONT) && (cu->flag & CU_BACK)) { + /* Half the loop. */ + nr = 2 * (cu->bevresol + 1) + ((cu->ext1 == 0.0f) ? 1 : 2); + dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE; + } + else { + /* One quarter of the loop (just front or back). */ + nr = (cu->ext1 == 0.0f) ? cu->bevresol + 2 : cu->bevresol + 3; + dl->flag = (cu->flag & CU_FRONT) ? DL_FRONT_CURVE : DL_BACK_CURVE; + } + + dl->verts = MEM_malloc_arrayN(nr, sizeof(float[3]), "makebevelcurve"); + BLI_addtail(disp, dl); + /* Use a different type depending on whether the loop is complete or not. */ + dl->type = ((cu->flag & (CU_FRONT | CU_BACK)) == 0) ? DL_POLY : DL_SEGM; + dl->parts = 1; + dl->nr = nr; + + fp = dl->verts; + dangle = (float)M_PI_2 / (cu->bevresol + 1); + angle = 0.0; + + /* Build the back section. */ + if (cu->flag & CU_BACK || !(cu->flag & CU_FRONT)) { + angle = (float)M_PI_2 * 3.0f; + for (a = 0; a < cu->bevresol + 2; a++) { fp[0] = 0.0; fp[1] = (float)(cosf(angle) * (cu->ext2)); fp[2] = (float)(sinf(angle) * (cu->ext2)) - cu->ext1; angle += dangle; fp += 3; } + if ((cu->ext1 != 0.0f) && !(cu->flag & CU_FRONT) && (cu->flag & CU_BACK)) { + /* Add the extrusion if we're only building the back. */ + fp[0] = 0.0; + fp[1] = cu->ext2; + fp[2] = cu->ext1; + } } - /* part 2, sidefaces */ - if (cu->ext1 != 0.0f) { - nr = 2; - - dl = MEM_callocN(sizeof(DispList), "makebevelcurve p2"); - dl->verts = MEM_malloc_arrayN(nr, sizeof(float[3]), "makebevelcurve p2"); - BLI_addtail(disp, dl); - dl->type = DL_SEGM; - dl->parts = 1; - dl->nr = nr; - - fp = dl->verts; - fp[1] = cu->ext2; - fp[2] = -cu->ext1; - fp[4] = cu->ext2; - fp[5] = cu->ext1; - - if ((cu->flag & (CU_FRONT | CU_BACK)) == 0) { - dl = MEM_dupallocN(dl); - dl->verts = MEM_dupallocN(dl->verts); - BLI_addtail(disp, dl); - - fp = dl->verts; - fp[1] = -fp[1]; - fp[2] = -fp[2]; - fp[4] = -fp[4]; - fp[5] = -fp[5]; + /* Build the front section. */ + if (cu->flag & CU_FRONT || !(cu->flag & CU_BACK)) { + if ((cu->ext1 != 0.0f) && !(cu->flag & CU_BACK) && (cu->flag & CU_FRONT)) { + /* Add the extrusion if we're only building the back. */ + fp[0] = 0.0; + fp[1] = cu->ext2; + fp[2] = -cu->ext1; + fp += 3; + } + /* Don't duplicate the last back vertex. */ + angle = (cu->ext1 == 0.0f && (cu->flag & CU_BACK)) ? dangle : 0; + for (a = 0; a < cu->bevresol + 2; a++) { + fp[0] = 0.0; + fp[1] = (float)(cosf(angle) * (cu->ext2)); + fp[2] = (float)(sinf(angle) * (cu->ext2)) + cu->ext1; + angle += dangle; + fp += 3; } } - /* part 3, front */ - if ((cu->flag & CU_FRONT) || !(cu->flag & CU_BACK)) { - dnr = nr = 2 + cu->bevresol; - if ((cu->flag & (CU_FRONT | CU_BACK)) == 0) { - nr = 3 + 2 * cu->bevresol; + /* Build the other half only if we're building the full loop. */ + if (!(cu->flag & (CU_FRONT | CU_BACK))) { + for (a = 0; a < cu->bevresol + 1; a++) { + fp[0] = 0.0; + fp[1] = (float)(cosf(angle) * (cu->ext2)); + fp[2] = (float)(sinf(angle) * (cu->ext2)) + cu->ext1; + angle += dangle; + fp += 3; } - dl = MEM_callocN(sizeof(DispList), "makebevelcurve p3"); - dl->verts = MEM_malloc_arrayN(nr, sizeof(float[3]), "makebevelcurve p3"); - BLI_addtail(disp, dl); - dl->type = DL_SEGM; - dl->flag = DL_FRONT_CURVE; - dl->parts = 1; - dl->nr = nr; - - /* half a circle */ - fp = dl->verts; - angle = 0.0; - dangle = ((float)M_PI_2 / (dnr - 1)); - for (a = 0; a < nr; a++) { + angle = (float)M_PI; + for (a = 0; a < cu->bevresol + 1; a++) { fp[0] = 0.0; fp[1] = (float)(cosf(angle) * (cu->ext2)); - fp[2] = (float)(sinf(angle) * (cu->ext2)) + cu->ext1; + fp[2] = (float)(sinf(angle) * (cu->ext2)) - cu->ext1; angle += dangle; fp += 3; } diff --git a/source/blender/blenkernel/intern/lightprobe.c b/source/blender/blenkernel/intern/lightprobe.c index 06f1ee5050b..3cba3aa9611 100644 --- a/source/blender/blenkernel/intern/lightprobe.c +++ b/source/blender/blenkernel/intern/lightprobe.c @@ -41,6 +41,30 @@ void BKE_lightprobe_init(LightProbe *probe) MEMCPY_STRUCT_AFTER(probe, DNA_struct_default_get(LightProbe), id); } +void BKE_lightprobe_type_set(LightProbe *probe, const short lightprobe_type) +{ + probe->type = lightprobe_type; + + switch (probe->type) { + case LIGHTPROBE_TYPE_GRID: + probe->distinf = 0.3f; + probe->falloff = 1.0f; + probe->clipsta = 0.01f; + break; + case LIGHTPROBE_TYPE_PLANAR: + probe->distinf = 0.1f; + probe->falloff = 0.5f; + probe->clipsta = 0.001f; + break; + case LIGHTPROBE_TYPE_CUBE: + probe->attenuation_type = LIGHTPROBE_SHAPE_ELIPSOID; + break; + default: + BLI_assert(!"LightProbe type not configured."); + break; + } +} + void *BKE_lightprobe_add(Main *bmain, const char *name) { LightProbe *probe; diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index da3986d33df..d0bbbe997cb 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -804,6 +804,8 @@ static const char *get_obdata_defname(int type) return DATA_("Empty"); case OB_GPENCIL: return DATA_("GPencil"); + case OB_LIGHTPROBE: + return DATA_("LightProbe"); default: CLOG_ERROR(&LOG, "Internal error, bad type: %d", type); return DATA_("Empty"); diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c index 3e449fa6b25..f58c20a7d72 100644 --- a/source/blender/blenkernel/intern/workspace.c +++ b/source/blender/blenkernel/intern/workspace.c @@ -247,8 +247,12 @@ WorkSpaceLayout *BKE_workspace_layout_add(Main *bmain, void BKE_workspace_layout_remove(Main *bmain, WorkSpace *workspace, WorkSpaceLayout *layout) { - id_us_min(&layout->screen->id); - BKE_id_free(bmain, layout->screen); + /* Screen should usually be set, but we call this from file reading to get rid of invalid + * layouts. */ + if (layout->screen) { + id_us_min(&layout->screen->id); + BKE_id_free(bmain, layout->screen); + } BLI_freelinkN(&workspace->layouts, layout); } diff --git a/source/blender/blenlib/BLI_rect.h b/source/blender/blenlib/BLI_rect.h index e3cd70f7413..fdb4fe30f1c 100644 --- a/source/blender/blenlib/BLI_rect.h +++ b/source/blender/blenlib/BLI_rect.h @@ -39,6 +39,10 @@ bool BLI_rcti_is_empty(const struct rcti *rect); bool BLI_rctf_is_empty(const struct rctf *rect); void BLI_rctf_init(struct rctf *rect, float xmin, float xmax, float ymin, float ymax); void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax); +bool BLI_rctf_is_valid(const struct rctf *rect); +bool BLI_rcti_is_valid(const struct rcti *rect); +void BLI_rctf_sanitize(struct rctf *rect); +void BLI_rcti_sanitize(struct rcti *rect); void BLI_rctf_init_pt_radius(struct rctf *rect, const float xy[2], float size); void BLI_rcti_init_pt_radius(struct rcti *rect, const int xy[2], int size); void BLI_rcti_init_minmax(struct rcti *rect); diff --git a/source/blender/blenlib/intern/rct.c b/source/blender/blenlib/intern/rct.c index ecff2ebffef..8fab4ed8e6a 100644 --- a/source/blender/blenlib/intern/rct.c +++ b/source/blender/blenlib/intern/rct.c @@ -439,42 +439,66 @@ void BLI_rcti_union(rcti *rct1, const rcti *rct2) void BLI_rctf_init(rctf *rect, float xmin, float xmax, float ymin, float ymax) { - if (xmin <= xmax) { - rect->xmin = xmin; - rect->xmax = xmax; - } - else { - rect->xmax = xmin; - rect->xmin = xmax; - } - if (ymin <= ymax) { - rect->ymin = ymin; - rect->ymax = ymax; - } - else { - rect->ymax = ymin; - rect->ymin = ymax; - } + rect->xmin = xmin; + rect->xmax = xmax; + rect->ymin = ymin; + rect->ymax = ymax; + + BLI_rctf_sanitize(rect); } void BLI_rcti_init(rcti *rect, int xmin, int xmax, int ymin, int ymax) { - if (xmin <= xmax) { - rect->xmin = xmin; - rect->xmax = xmax; + rect->xmin = xmin; + rect->xmax = xmax; + rect->ymin = ymin; + rect->ymax = ymax; + + BLI_rcti_sanitize(rect); +} + +/** + * Check if X-min and Y-min are less than or equal to X-max and Y-max, respectively. + * If this returns false, #BLI_rctf_sanitize() can be called to address this. + * + * This is not a hard constraint or invariant for rectangles, in some cases it may be useful to + * have max < min. Usually this is what you'd want though. + */ +bool BLI_rctf_is_valid(const rctf *rect) +{ + return (rect->xmin <= rect->xmax) && (rect->ymin <= rect->ymax); +} + +bool BLI_rcti_is_valid(const rcti *rect) +{ + return (rect->xmin <= rect->xmax) && (rect->ymin <= rect->ymax); +} + +/** + * Ensure X-min and Y-min are less than or equal to X-max and Y-max, respectively. + */ +void BLI_rctf_sanitize(rctf *rect) +{ + if (rect->xmin > rect->xmax) { + SWAP(float, rect->xmin, rect->xmax); } - else { - rect->xmax = xmin; - rect->xmin = xmax; + if (rect->ymin > rect->ymax) { + SWAP(float, rect->ymin, rect->ymax); } - if (ymin <= ymax) { - rect->ymin = ymin; - rect->ymax = ymax; + + BLI_assert(BLI_rctf_is_valid(rect)); +} + +void BLI_rcti_sanitize(rcti *rect) +{ + if (rect->xmin > rect->xmax) { + SWAP(int, rect->xmin, rect->xmax); } - else { - rect->ymax = ymin; - rect->ymin = ymax; + if (rect->ymin > rect->ymax) { + SWAP(int, rect->ymin, rect->ymax); } + + BLI_assert(BLI_rcti_is_valid(rect)); } void BLI_rctf_init_pt_radius(rctf *rect, const float xy[2], float size) diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 0ebff916cf9..e1424da2207 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -3371,6 +3371,11 @@ static void lib_link_workspaces(FileData *fd, Main *bmain) } } } + else { + /* If we're reading a layout without screen stored, it's useless and we shouldn't keep it + * around. */ + BKE_workspace_layout_remove(bmain, workspace, layout); + } } id->tag &= ~LIB_TAG_NEED_LINK; @@ -3869,7 +3874,7 @@ static void direct_link_bones(FileData *fd, Bone *bone) bone->bbone_next = newdataadr(fd, bone->bbone_next); bone->bbone_prev = newdataadr(fd, bone->bbone_prev); - bone->flag &= ~BONE_DRAW_ACTIVE; + bone->flag &= ~(BONE_DRAW_ACTIVE | BONE_DRAW_LOCKED_WEIGHT); link_list(fd, &bone->childbase); diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 6d4dd98729a..7377f6a62a1 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -636,6 +636,29 @@ static ARegion *do_versions_add_region(int regiontype, const char *name) return ar; } +static void do_versions_area_ensure_tool_region(Main *bmain, + const short space_type, + const short region_flag) +{ + for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == space_type) { + ListBase *regionbase = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase; + ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_TOOLS); + if (!ar) { + ARegion *header = BKE_area_find_region_type(sa, RGN_TYPE_HEADER); + ar = do_versions_add_region(RGN_TYPE_TOOLS, "tools region"); + BLI_insertlinkafter(regionbase, header, ar); + ar->alignment = RGN_ALIGN_LEFT; + ar->flag = region_flag; + } + } + } + } + } +} + static void do_version_bones_split_bbone_scale(ListBase *lb) { for (Bone *bone = lb->first; bone; bone = bone->next) { @@ -2828,6 +2851,35 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + /* Files stored pre 2.5 (possibly re-saved with newer versions) may have non-visible + * spaces without a header (visible/active ones are properly versioned). + * Multiple version patches below assume there's always a header though. So inserting this + * patch in-between older ones to add a header when needed. + * + * From here on it should be fine to assume there always is a header. + */ + if (!MAIN_VERSION_ATLEAST(bmain, 283, 1)) { + for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { + ListBase *regionbase = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase; + ARegion *ar_header = do_versions_find_region_or_null(regionbase, RGN_TYPE_HEADER); + + if (!ar_header) { + /* Headers should always be first in the region list, except if there's also a + * tool-header. These were only introduced in later versions though, so should be + * fine to always insert headers first. */ + BLI_assert(!do_versions_find_region_or_null(regionbase, RGN_TYPE_TOOL_HEADER)); + + ARegion *ar = do_versions_add_region(RGN_TYPE_HEADER, "header 2.83.1 versioning"); + ar->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; + BLI_addhead(regionbase, ar); + } + } + } + } + } + for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { @@ -3709,7 +3761,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) ListBase *regionbase = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase; /* All spaces that use tools must be eventually added. */ ARegion *ar = NULL; - if (ELEM(sl->spacetype, SPACE_VIEW3D, SPACE_IMAGE) && + if (ELEM(sl->spacetype, SPACE_VIEW3D, SPACE_IMAGE, SPACE_SEQ) && ((ar = do_versions_find_region_or_null(regionbase, RGN_TYPE_TOOL_HEADER)) == NULL)) { /* Add tool header. */ ar = do_versions_add_region(RGN_TYPE_TOOL_HEADER, "tool header"); @@ -4309,6 +4361,9 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) { /* Keep this block, even when empty. */ + /* Sequencer Tool region */ + do_versions_area_ensure_tool_region(bmain, SPACE_SEQ, RGN_FLAG_HIDDEN); + /* Cloth internal springs */ for (Object *ob = bmain->objects.first; ob; ob = ob->id.next) { for (ModifierData *md = ob->modifiers.first; md; md = md->next) { diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index bc43d9605e2..8c7a0c4f7b2 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -207,13 +207,20 @@ static void blo_update_defaults_screen(bScreen *screen, } } - /* Show top-bar by default. */ + /* Show tool-header by default (for most cases at least, hide for others). */ + const bool hide_image_tool_header = STREQ(workspace_name, "Rendering"); for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { ListBase *regionbase = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase; + for (ARegion *ar = regionbase->first; ar; ar = ar->next) { if (ar->regiontype == RGN_TYPE_TOOL_HEADER) { - ar->flag &= ~(RGN_FLAG_HIDDEN | RGN_FLAG_HIDDEN_BY_USER); + if ((sl->spacetype == SPACE_IMAGE) && hide_image_tool_header) { + ar->flag |= RGN_FLAG_HIDDEN; + } + else { + ar->flag &= ~(RGN_FLAG_HIDDEN | RGN_FLAG_HIDDEN_BY_USER); + } } } } diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index b1f70848bdc..e1ea4e3bb24 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -164,6 +164,10 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) FROM_DEFAULT_V4_UCHAR(space_view3d.face_front); } + if (!USER_VERSION_ATLEAST(283, 1)) { + FROM_DEFAULT_V4_UCHAR(space_view3d.bone_locked_weight); + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/collada/EffectExporter.cpp b/source/blender/collada/EffectExporter.cpp index 80f84738f6e..2d69fae035f 100644 --- a/source/blender/collada/EffectExporter.cpp +++ b/source/blender/collada/EffectExporter.cpp @@ -112,26 +112,22 @@ void EffectsExporter::set_transparency(COLLADASW::EffectProfile &ep, Material *m void EffectsExporter::set_diffuse_color(COLLADASW::EffectProfile &ep, Material *ma) { - // get diffuse color COLLADASW::ColorOrTexture cot = bc_get_base_color(ma); ep.setDiffuse(cot, false, "diffuse"); } void EffectsExporter::set_ambient(COLLADASW::EffectProfile &ep, Material *ma) { - // get diffuse color COLLADASW::ColorOrTexture cot = bc_get_ambient(ma); ep.setAmbient(cot, false, "ambient"); } void EffectsExporter::set_specular(COLLADASW::EffectProfile &ep, Material *ma) { - // get diffuse color COLLADASW::ColorOrTexture cot = bc_get_specular(ma); ep.setSpecular(cot, false, "specular"); } void EffectsExporter::set_reflective(COLLADASW::EffectProfile &ep, Material *ma) { - // get diffuse color COLLADASW::ColorOrTexture cot = bc_get_reflective(ma); ep.setReflective(cot, false, "reflective"); } diff --git a/source/blender/collada/Materials.cpp b/source/blender/collada/Materials.cpp index 3b2c68ef95e..06f54884668 100644 --- a/source/blender/collada/Materials.cpp +++ b/source/blender/collada/Materials.cpp @@ -202,6 +202,7 @@ void MaterialNode::set_alpha(COLLADAFW::EffectCommon::OpaqueMode mode, bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Alpha"); ((bNodeSocketValueFloat *)socket->default_value)->value = alpha; + material->a = alpha; } else if (cot.isTexture()) { int locy = -300 * (node_map.size() - 2); diff --git a/source/blender/collada/collada_utils.cpp b/source/blender/collada/collada_utils.cpp index b688840cb09..d5011581204 100644 --- a/source/blender/collada/collada_utils.cpp +++ b/source/blender/collada/collada_utils.cpp @@ -1321,10 +1321,10 @@ void bc_add_default_shader(bContext *C, Material *ma) COLLADASW::ColorOrTexture bc_get_base_color(Material *ma) { - Color default_color = {0.8, 0.8, 0.8, 1.0}; + Color default_color = {ma->r, ma->g, ma->b, 1.0}; // for alpha see bc_get_alpha() bNode *shader = bc_get_master_shader(ma); if (ma->use_nodes && shader) { - return bc_get_cot_from_shader(shader, "Base Color", default_color); + return bc_get_cot_from_shader(shader, "Base Color", default_color, false); } else { return bc_get_cot(default_color); @@ -1414,16 +1414,17 @@ double bc_get_float_from_shader(bNode *shader, double &val, std::string nodeid) COLLADASW::ColorOrTexture bc_get_cot_from_shader(bNode *shader, std::string nodeid, - Color &default_color) + Color &default_color, + bool with_alpha) { bNodeSocket *socket = nodeFindSocket(shader, SOCK_IN, nodeid.c_str()); if (socket) { bNodeSocketValueRGBA *dcol = (bNodeSocketValueRGBA *)socket->default_value; float *col = dcol->value; - return bc_get_cot(col); + return bc_get_cot(col, with_alpha); } else { - return bc_get_cot(default_color); /* default black */ + return bc_get_cot(default_color, with_alpha); } } @@ -1447,9 +1448,9 @@ COLLADASW::ColorOrTexture bc_get_cot(float r, float g, float b, float a) return cot; } -COLLADASW::ColorOrTexture bc_get_cot(Color col) +COLLADASW::ColorOrTexture bc_get_cot(Color col, bool with_alpha) { - COLLADASW::Color color(col[0], col[1], col[2], col[3]); + COLLADASW::Color color(col[0], col[1], col[2], (with_alpha) ? col[3] : 1.0); COLLADASW::ColorOrTexture cot(color); return cot; } diff --git a/source/blender/collada/collada_utils.h b/source/blender/collada/collada_utils.h index c0425e59d1a..b313fcd6e66 100644 --- a/source/blender/collada/collada_utils.h +++ b/source/blender/collada/collada_utils.h @@ -397,9 +397,10 @@ double bc_get_shininess(Material *ma); double bc_get_float_from_shader(bNode *shader, double &ior, std::string nodeid); COLLADASW::ColorOrTexture bc_get_cot_from_shader(bNode *shader, std::string nodeid, - Color &default_color); + Color &default_color, + bool with_alpha = true); COLLADASW::ColorOrTexture bc_get_cot(float r, float g, float b, float a); -COLLADASW::ColorOrTexture bc_get_cot(Color col); +COLLADASW::ColorOrTexture bc_get_cot(Color col, bool with_alpha = true); #endif diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt index 4abeec19645..fad8bc22e08 100644 --- a/source/blender/depsgraph/CMakeLists.txt +++ b/source/blender/depsgraph/CMakeLists.txt @@ -59,6 +59,7 @@ set(SRC intern/eval/deg_eval_copy_on_write.cc intern/eval/deg_eval_flush.cc intern/eval/deg_eval_runtime_backup.cc + intern/eval/deg_eval_runtime_backup_animation.cc intern/eval/deg_eval_runtime_backup_modifier.cc intern/eval/deg_eval_runtime_backup_movieclip.cc intern/eval/deg_eval_runtime_backup_object.cc @@ -82,6 +83,7 @@ set(SRC intern/depsgraph_query.cc intern/depsgraph_query_foreach.cc intern/depsgraph_query_iter.cc + intern/depsgraph_relation.cc intern/depsgraph_registry.cc intern/depsgraph_tag.cc intern/depsgraph_type.cc @@ -104,10 +106,12 @@ set(SRC intern/builder/deg_builder_rna.h intern/builder/deg_builder_transitive.h intern/debug/deg_debug.h + intern/debug/deg_time_average.h intern/eval/deg_eval.h intern/eval/deg_eval_copy_on_write.h intern/eval/deg_eval_flush.h intern/eval/deg_eval_runtime_backup.h + intern/eval/deg_eval_runtime_backup_animation.h intern/eval/deg_eval_runtime_backup_modifier.h intern/eval/deg_eval_runtime_backup_movieclip.h intern/eval/deg_eval_runtime_backup_object.h @@ -127,6 +131,7 @@ set(SRC intern/depsgraph.h intern/depsgraph_physics.h intern/depsgraph_registry.h + intern/depsgraph_relation.h intern/depsgraph_tag.h intern/depsgraph_type.h intern/depsgraph_update.h diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc index fb7104e7556..440d9dd07f6 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder.cc @@ -42,6 +42,7 @@ extern "C" { } #include "intern/depsgraph.h" +#include "intern/depsgraph_relation.h" #include "intern/depsgraph_tag.h" #include "intern/depsgraph_type.h" #include "intern/builder/deg_builder_cache.h" diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc b/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc index bea59eceea2..f18583beb9c 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc @@ -35,6 +35,7 @@ #include "intern/node/deg_node_operation.h" #include "intern/depsgraph.h" +#include "intern/depsgraph_relation.h" namespace DEG { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 7d7a183ee75..bc441676c86 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -114,6 +114,7 @@ extern "C" { #include "intern/node/deg_node_operation.h" #include "intern/node/deg_node_time.h" +#include "intern/depsgraph_relation.h" #include "intern/depsgraph_type.h" namespace DEG { @@ -2690,6 +2691,26 @@ void DepsgraphRelationBuilder::build_copy_on_write_relations(IDNode *id_node) BLI_assert(object->type == OB_EMPTY); } } + +#if 0 + /* NOTE: Relation is disabled since AnimationBackup() is disabled. + * See comment in AnimationBackup:init_from_id(). */ + + /* Copy-on-write of write will iterate over f-curves to store current values corresponding + * to their RNA path. This means that action must be copied prior to the ID's copy-on-write, + * otherwise depsgraph might try to access freed data. */ + AnimData *animation_data = BKE_animdata_from_id(id_orig); + if (animation_data != NULL) { + if (animation_data->action != NULL) { + OperationKey action_copy_on_write_key( + &animation_data->action->id, NodeType::COPY_ON_WRITE, OperationCode::COPY_ON_WRITE); + add_relation(action_copy_on_write_key, + copy_on_write_key, + "Eval Order", + RELATION_FLAG_GODMODE | RELATION_FLAG_NO_FLUSH); + } + } +#endif } /* **** ID traversal callbacks functions **** */ diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc index e254f8df0d2..484a4d828e3 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc @@ -58,6 +58,7 @@ extern "C" { #include "intern/node/deg_node_operation.h" #include "intern/depsgraph_type.h" +#include "intern/depsgraph_relation.h" namespace DEG { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc b/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc index 13cf8e63832..fc7c546e294 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc @@ -30,6 +30,7 @@ #include "intern/node/deg_node_operation.h" #include "intern/depsgraph.h" +#include "intern/depsgraph_relation.h" #include "intern/debug/deg_debug.h" namespace DEG { diff --git a/source/blender/depsgraph/intern/debug/deg_debug.cc b/source/blender/depsgraph/intern/debug/deg_debug.cc index b811f11f721..00ff73b8f63 100644 --- a/source/blender/depsgraph/intern/debug/deg_debug.cc +++ b/source/blender/depsgraph/intern/debug/deg_debug.cc @@ -28,10 +28,50 @@ #include "BLI_hash.h" #include "BLI_string.h" +#include "PIL_time_utildefines.h" + #include "BKE_global.h" namespace DEG { +DepsgraphDebug::DepsgraphDebug() + : flags(G.debug), is_ever_evaluated(false), graph_evaluation_start_time_(0) +{ +} + +bool DepsgraphDebug::do_time_debug() const +{ + return ((G.debug & G_DEBUG_DEPSGRAPH_TIME) != 0); +} + +void DepsgraphDebug::begin_graph_evaluation() +{ + if (!do_time_debug()) { + return; + } + + const double current_time = PIL_check_seconds_timer(); + + if (is_ever_evaluated) { + fps_samples_.add_sample(current_time - graph_evaluation_start_time_); + } + + graph_evaluation_start_time_ = current_time; +} + +void DepsgraphDebug::end_graph_evaluation() +{ + if (!do_time_debug()) { + return; + } + + const double graph_eval_end_time = PIL_check_seconds_timer(); + printf("Depsgraph updated in %f seconds.\n", graph_eval_end_time - graph_evaluation_start_time_); + printf("Depsgraph evaluation FPS: %f\n", fps_samples_.get_averaged()); + + is_ever_evaluated = true; +} + bool terminal_do_color(void) { return (G.debug & G_DEBUG_DEPSGRAPH_PRETTY) != 0; diff --git a/source/blender/depsgraph/intern/debug/deg_debug.h b/source/blender/depsgraph/intern/debug/deg_debug.h index 3e4da644641..21a802828dc 100644 --- a/source/blender/depsgraph/intern/debug/deg_debug.h +++ b/source/blender/depsgraph/intern/debug/deg_debug.h @@ -24,6 +24,7 @@ #pragma once #include "intern/depsgraph_type.h" +#include "intern/debug/deg_time_average.h" #include "BKE_global.h" @@ -31,6 +32,38 @@ namespace DEG { +class DepsgraphDebug { + public: + DepsgraphDebug(); + + bool do_time_debug() const; + + void begin_graph_evaluation(); + void end_graph_evaluation(); + + /* NOTE: Corresponds to G_DEBUG_DEPSGRAPH_* flags. */ + int flags; + + /* Name of this dependency graph (is used for debug prints, helping to distinguish graphs + * created for different view layer). */ + string name; + + /* Is true when dependency graph was evaluated at least once. + * This is NOT an indication that depsgraph is at its evaluated state. */ + bool is_ever_evaluated; + + protected: + /* Maximum number of counters used to calculate frame rate of depsgraph update. */ + static const constexpr int MAX_FPS_COUNTERS = 64; + + /* Point in time when last graph evaluation began. + * Is initialized from begin_graph_evaluation() when time debug is enabled. + */ + double graph_evaluation_start_time_; + + AveragedTimeSampler<MAX_FPS_COUNTERS> fps_samples_; +}; + #define DEG_DEBUG_PRINTF(depsgraph, type, ...) \ do { \ if (DEG_debug_flags_get(depsgraph) & G_DEBUG_DEPSGRAPH_##type) { \ diff --git a/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc b/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc index ee3959a0861..45d15c230d7 100644 --- a/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc +++ b/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc @@ -36,6 +36,8 @@ extern "C" { #include "DEG_depsgraph_debug.h" #include "intern/depsgraph.h" +#include "intern/depsgraph_relation.h" + #include "intern/node/deg_node_component.h" #include "intern/node/deg_node_id.h" #include "intern/node/deg_node_operation.h" diff --git a/source/blender/depsgraph/intern/debug/deg_time_average.h b/source/blender/depsgraph/intern/debug/deg_time_average.h new file mode 100644 index 00000000000..9794e9a88c3 --- /dev/null +++ b/source/blender/depsgraph/intern/debug/deg_time_average.h @@ -0,0 +1,71 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2013 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup depsgraph + */ + +#pragma once + +namespace DEG { + +// Utility class which takes care of calculating average of time series, such as +// FPS counters. +template<int MaxSamples> class AveragedTimeSampler { + public: + AveragedTimeSampler() : num_samples_(0), next_sample_index_(0) + { + } + + void add_sample(double value) + { + samples_[next_sample_index_] = value; + + // Move to the next index, keeping wrapping at the end of array into account. + ++next_sample_index_; + if (next_sample_index_ == MaxSamples) { + next_sample_index_ = 0; + } + + // Update number of stored samples. + if (num_samples_ != MaxSamples) { + ++num_samples_; + } + } + + double get_averaged() const + { + double sum = 0.0; + for (int i = 0; i < num_samples_; ++i) { + sum += samples_[i]; + } + return sum / num_samples_; + } + + protected: + double samples_[MaxSamples]; + + // Number of samples which are actually stored in the array. + int num_samples_; + + // Index in the samples_ array under which next sample will be stored. + int next_sample_index_; +}; + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc index 292c3b361d8..7e26857e440 100644 --- a/source/blender/depsgraph/intern/depsgraph.cc +++ b/source/blender/depsgraph/intern/depsgraph.cc @@ -46,6 +46,7 @@ extern "C" { #include "intern/depsgraph_update.h" #include "intern/depsgraph_physics.h" +#include "intern/depsgraph_relation.h" #include "intern/depsgraph_registry.h" #include "intern/eval/deg_eval_copy_on_write.h" @@ -82,7 +83,6 @@ Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluati BLI_spin_init(&lock); id_hash = BLI_ghash_ptr_new("Depsgraph id hash"); entry_tags = BLI_gset_ptr_new("Depsgraph entry_tags"); - debug_flags = G.debug; memset(id_type_updated, 0, sizeof(id_type_updated)); memset(id_type_exist, 0, sizeof(id_type_exist)); memset(physics_relations, 0, sizeof(physics_relations)); @@ -220,43 +220,6 @@ Relation *Depsgraph::check_nodes_connected(const Node *from, return NULL; } -/* ************************ */ -/* Relationships Management */ - -Relation::Relation(Node *from, Node *to, const char *description) - : from(from), to(to), name(description), flag(0) -{ - /* Hook it up to the nodes which use it. - * - * NOTE: We register relation in the nodes which this link connects to here - * in constructor but we don't unregister it in the destructor. - * - * Reasoning: - * - * - Destructor is currently used on global graph destruction, so there's no - * real need in avoiding dangling pointers, all the memory is to be freed - * anyway. - * - * - Unregistering relation is not a cheap operation, so better to have it - * as an explicit call if we need this. */ - from->outlinks.push_back(this); - to->inlinks.push_back(this); -} - -Relation::~Relation() -{ - /* Sanity check. */ - BLI_assert(from != NULL && to != NULL); -} - -void Relation::unlink() -{ - /* Sanity check. */ - BLI_assert(from != NULL && to != NULL); - remove_from_vector(&from->outlinks, this); - remove_from_vector(&to->inlinks, this); -} - /* Low level tagging -------------------------------------- */ /* Tag a specific node as needing updates. */ diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h index 43829f4e045..a7ee5612af4 100644 --- a/source/blender/depsgraph/intern/depsgraph.h +++ b/source/blender/depsgraph/intern/depsgraph.h @@ -40,6 +40,7 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_physics.h" +#include "intern/debug/deg_debug.h" #include "intern/depsgraph_type.h" struct GHash; @@ -53,47 +54,9 @@ namespace DEG { struct IDNode; struct Node; struct OperationNode; +struct Relation; struct TimeSourceNode; -/* *************************** */ -/* Relationships Between Nodes */ - -/* Settings/Tags on Relationship. - * NOTE: Is a bitmask, allowing accumulation. */ -enum RelationFlag { - /* "cyclic" link - when detecting cycles, this relationship was the one - * which triggers a cyclic relationship to exist in the graph. */ - RELATION_FLAG_CYCLIC = (1 << 0), - /* Update flush will not go through this relation. */ - RELATION_FLAG_NO_FLUSH = (1 << 1), - /* Only flush along the relation is update comes from a node which was - * affected by user input. */ - RELATION_FLAG_FLUSH_USER_EDIT_ONLY = (1 << 2), - /* The relation can not be killed by the cyclic dependencies solver. */ - RELATION_FLAG_GODMODE = (1 << 4), - /* Relation will check existence before being added. */ - RELATION_CHECK_BEFORE_ADD = (1 << 5), -}; - -/* B depends on A (A -> B) */ -struct Relation { - Relation(Node *from, Node *to, const char *description); - ~Relation(); - - void unlink(); - - /* the nodes in the relationship (since this is shared between the nodes) */ - Node *from; /* A */ - Node *to; /* B */ - - /* relationship attributes */ - const char *name; /* label for debugging */ - int flag; /* Bitmask of RelationFlag) */ -}; - -/* ********* */ -/* Depsgraph */ - /* Dependency Graph object */ struct Depsgraph { // TODO(sergey): Go away from C++ container and use some native BLI. @@ -194,9 +157,7 @@ struct Depsgraph { * to read stuff from. */ bool is_active; - /* NOTE: Corresponds to G_DEBUG_DEPSGRAPH_* flags. */ - int debug_flags; - string debug_name; + DepsgraphDebug debug; bool is_evaluating; diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index 0acf777e2f0..4ecb8b8068c 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -59,6 +59,7 @@ extern "C" { #include "intern/node/deg_node_id.h" #include "intern/node/deg_node_operation.h" +#include "intern/depsgraph_relation.h" #include "intern/depsgraph_registry.h" #include "intern/depsgraph_type.h" diff --git a/source/blender/depsgraph/intern/depsgraph_debug.cc b/source/blender/depsgraph/intern/depsgraph_debug.cc index bb60db5209c..4cf3a150eac 100644 --- a/source/blender/depsgraph/intern/depsgraph_debug.cc +++ b/source/blender/depsgraph/intern/depsgraph_debug.cc @@ -38,6 +38,7 @@ extern "C" { #include "DEG_depsgraph_query.h" #include "intern/depsgraph.h" +#include "intern/depsgraph_relation.h" #include "intern/depsgraph_type.h" #include "intern/debug/deg_debug.h" #include "intern/node/deg_node_component.h" @@ -47,25 +48,25 @@ extern "C" { void DEG_debug_flags_set(Depsgraph *depsgraph, int flags) { DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(depsgraph); - deg_graph->debug_flags = flags; + deg_graph->debug.flags = flags; } int DEG_debug_flags_get(const Depsgraph *depsgraph) { const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(depsgraph); - return deg_graph->debug_flags; + return deg_graph->debug.flags; } void DEG_debug_name_set(struct Depsgraph *depsgraph, const char *name) { DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(depsgraph); - deg_graph->debug_name = name; + deg_graph->debug.name = name; } const char *DEG_debug_name_get(struct Depsgraph *depsgraph) { const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(depsgraph); - return deg_graph->debug_name.c_str(); + return deg_graph->debug.name.c_str(); } bool DEG_debug_compare(const struct Depsgraph *graph1, const struct Depsgraph *graph2) diff --git a/source/blender/depsgraph/intern/depsgraph_query_foreach.cc b/source/blender/depsgraph/intern/depsgraph_query_foreach.cc index b7a40fb69bd..cd5e8f8a8d9 100644 --- a/source/blender/depsgraph/intern/depsgraph_query_foreach.cc +++ b/source/blender/depsgraph/intern/depsgraph_query_foreach.cc @@ -40,6 +40,7 @@ extern "C" { #include "DEG_depsgraph_query.h" #include "intern/depsgraph.h" +#include "intern/depsgraph_relation.h" #include "intern/node/deg_node.h" #include "intern/node/deg_node_component.h" #include "intern/node/deg_node_id.h" diff --git a/source/blender/depsgraph/intern/depsgraph_relation.cc b/source/blender/depsgraph/intern/depsgraph_relation.cc new file mode 100644 index 00000000000..387582ca4e0 --- /dev/null +++ b/source/blender/depsgraph/intern/depsgraph_relation.cc @@ -0,0 +1,73 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup depsgraph + */ + +#include "intern/depsgraph_relation.h" /* own include */ + +#include "BLI_utildefines.h" + +#include "intern/depsgraph_type.h" +#include "intern/node/deg_node.h" + +namespace DEG { + +/* TODO(sergey): Find a better place for this. */ +template<typename T> static void remove_from_vector(vector<T> *vector, const T &value) +{ + vector->erase(std::remove(vector->begin(), vector->end(), value), vector->end()); +} + +Relation::Relation(Node *from, Node *to, const char *description) + : from(from), to(to), name(description), flag(0) +{ + /* Hook it up to the nodes which use it. + * + * NOTE: We register relation in the nodes which this link connects to here + * in constructor but we don't unregister it in the destructor. + * + * Reasoning: + * + * - Destructor is currently used on global graph destruction, so there's no + * real need in avoiding dangling pointers, all the memory is to be freed + * anyway. + * + * - Unregistering relation is not a cheap operation, so better to have it + * as an explicit call if we need this. */ + from->outlinks.push_back(this); + to->inlinks.push_back(this); +} + +Relation::~Relation() +{ + /* Sanity check. */ + BLI_assert(from != NULL && to != NULL); +} + +void Relation::unlink() +{ + /* Sanity check. */ + BLI_assert(from != NULL && to != NULL); + remove_from_vector(&from->outlinks, this); + remove_from_vector(&to->inlinks, this); +} + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_relation.h b/source/blender/depsgraph/intern/depsgraph_relation.h new file mode 100644 index 00000000000..9195e9abfbd --- /dev/null +++ b/source/blender/depsgraph/intern/depsgraph_relation.h @@ -0,0 +1,63 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup depsgraph + */ + +#pragma once + +namespace DEG { + +class Node; + +/* Settings/Tags on Relationship. + * NOTE: Is a bitmask, allowing accumulation. */ +enum RelationFlag { + /* "cyclic" link - when detecting cycles, this relationship was the one + * which triggers a cyclic relationship to exist in the graph. */ + RELATION_FLAG_CYCLIC = (1 << 0), + /* Update flush will not go through this relation. */ + RELATION_FLAG_NO_FLUSH = (1 << 1), + /* Only flush along the relation is update comes from a node which was + * affected by user input. */ + RELATION_FLAG_FLUSH_USER_EDIT_ONLY = (1 << 2), + /* The relation can not be killed by the cyclic dependencies solver. */ + RELATION_FLAG_GODMODE = (1 << 4), + /* Relation will check existence before being added. */ + RELATION_CHECK_BEFORE_ADD = (1 << 5), +}; + +/* B depends on A (A -> B) */ +struct Relation { + Relation(Node *from, Node *to, const char *description); + ~Relation(); + + void unlink(); + + /* the nodes in the relationship (since this is shared between the nodes) */ + Node *from; /* A */ + Node *to; /* B */ + + /* relationship attributes */ + const char *name; /* label for debugging */ + int flag; /* Bitmask of RelationFlag) */ +}; + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index ff30bf7571a..9a10177a462 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -52,6 +52,7 @@ #include "intern/node/deg_node_operation.h" #include "intern/node/deg_node_time.h" #include "intern/depsgraph.h" +#include "intern/depsgraph_relation.h" namespace DEG { @@ -364,14 +365,15 @@ void deg_evaluate_on_refresh(Depsgraph *graph) if (BLI_gset_len(graph->entry_tags) == 0) { return; } - const bool do_time_debug = ((G.debug & G_DEBUG_DEPSGRAPH_TIME) != 0); - const double start_time = do_time_debug ? PIL_check_seconds_timer() : 0; + + graph->debug.begin_graph_evaluation(); + graph->is_evaluating = true; depsgraph_ensure_view_layer(graph); /* Set up evaluation state. */ DepsgraphEvalState state; state.graph = graph; - state.do_stats = do_time_debug; + state.do_stats = graph->debug.do_time_debug(); state.need_single_thread_pass = false; /* Set up task scheduler and pull for threaded evaluation. */ TaskScheduler *task_scheduler; @@ -418,9 +420,8 @@ void deg_evaluate_on_refresh(Depsgraph *graph) BLI_task_scheduler_free(task_scheduler); } graph->is_evaluating = false; - if (do_time_debug) { - printf("Depsgraph updated in %f seconds.\n", PIL_check_seconds_timer() - start_time); - } + + graph->debug.end_graph_evaluation(); } } // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc index 96e2974a7ab..cf9b1259e4e 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc @@ -49,6 +49,7 @@ extern "C" { #include "intern/debug/deg_debug.h" #include "intern/depsgraph.h" +#include "intern/depsgraph_relation.h" #include "intern/depsgraph_update.h" #include "intern/node/deg_node.h" #include "intern/node/deg_node_component.h" diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc index 88390ab412f..4da5ca77fb8 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc @@ -32,7 +32,8 @@ namespace DEG { RuntimeBackup::RuntimeBackup(const Depsgraph *depsgraph) - : scene_backup(depsgraph), + : animation_backup(depsgraph), + scene_backup(depsgraph), sound_backup(depsgraph), object_backup(depsgraph), drawdata_ptr(NULL), @@ -47,6 +48,8 @@ void RuntimeBackup::init_from_id(ID *id) return; } + animation_backup.init_from_id(id); + const ID_Type id_type = GS(id->name); switch (id_type) { case ID_OB: @@ -76,6 +79,8 @@ void RuntimeBackup::init_from_id(ID *id) void RuntimeBackup::restore_to_id(ID *id) { + animation_backup.restore_to_id(id); + const ID_Type id_type = GS(id->name); switch (id_type) { case ID_OB: diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h index 31ae3164e37..892cc88002f 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h @@ -25,6 +25,7 @@ #include "DNA_ID.h" +#include "intern/eval/deg_eval_runtime_backup_animation.h" #include "intern/eval/deg_eval_runtime_backup_movieclip.h" #include "intern/eval/deg_eval_runtime_backup_object.h" #include "intern/eval/deg_eval_runtime_backup_scene.h" @@ -44,6 +45,7 @@ class RuntimeBackup { /* Restore fields to the given ID. */ void restore_to_id(ID *id); + AnimationBackup animation_backup; SceneBackup scene_backup; SoundBackup sound_backup; ObjectRuntimeBackup object_backup; diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.cc new file mode 100644 index 00000000000..cc4935431d1 --- /dev/null +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.cc @@ -0,0 +1,144 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup depsgraph + */ + +#include "intern/eval/deg_eval_runtime_backup_animation.h" + +#include "DNA_anim_types.h" + +#include "BKE_animsys.h" + +#include "RNA_access.h" +#include "RNA_types.h" + +#include "intern/depsgraph.h" + +namespace DEG { + +namespace { + +struct AnimatedPropertyStoreCalbackData { + AnimationBackup *backup; + + /* ID which needs to be stored. + * Is used to check possibly nested IDs which f-curves are pointing to. */ + ID *id; + + PointerRNA id_pointer_rna; +}; + +void animated_property_store_cb(ID *id, FCurve *fcurve, void *data_v) +{ + AnimatedPropertyStoreCalbackData *data = reinterpret_cast<AnimatedPropertyStoreCalbackData *>( + data_v); + if (fcurve->rna_path == NULL || fcurve->rna_path[0] == '\0') { + return; + } + if (id != data->id) { + return; + } + + /* Resolve path to the property. */ + PathResolvedRNA resolved_rna; + if (!BKE_animsys_store_rna_setting( + &data->id_pointer_rna, fcurve->rna_path, fcurve->array_index, &resolved_rna)) { + return; + } + + /* Read property value. */ + float value; + if (!BKE_animsys_read_rna_setting(&resolved_rna, &value)) { + return; + } + + data->backup->values_backup.emplace_back(fcurve->rna_path, fcurve->array_index, value); +} + +} // namespace + +AnimationValueBackup::AnimationValueBackup() +{ +} + +AnimationValueBackup::AnimationValueBackup(const string &rna_path, int array_index, float value) + : rna_path(rna_path), array_index(array_index), value(value) +{ +} + +AnimationValueBackup::~AnimationValueBackup() +{ +} + +AnimationBackup::AnimationBackup(const Depsgraph *depsgraph) +{ + meed_value_backup = !depsgraph->is_active; + reset(); +} + +void AnimationBackup::reset() +{ +} + +void AnimationBackup::init_from_id(ID *id) +{ + /* NOTE: This animation backup nicely preserves values which are animated and + * are not touched by frame/depsgraph post_update handler. + * + * But it makes it impossible to have user edits to animated properties: for + * example, translation of object with animated location will not work with + * the current version of backup. */ + return; + + AnimatedPropertyStoreCalbackData data; + data.backup = this; + data.id = id; + RNA_id_pointer_create(id, &data.id_pointer_rna); + BKE_fcurves_id_cb(id, animated_property_store_cb, &data); +} + +void AnimationBackup::restore_to_id(ID *id) +{ + return; + + PointerRNA id_pointer_rna; + RNA_id_pointer_create(id, &id_pointer_rna); + for (const AnimationValueBackup &value_backup : values_backup) { + /* Resolve path to the property. + * + * NOTE: Do it again (after storing), since the sub-data pointers might be + * changed after copy-on-write. */ + PathResolvedRNA resolved_rna; + if (!BKE_animsys_store_rna_setting(&id_pointer_rna, + value_backup.rna_path.c_str(), + value_backup.array_index, + &resolved_rna)) { + return; + } + + /* Write property value. */ + if (!BKE_animsys_write_rna_setting(&resolved_rna, value_backup.value)) { + return; + } + } +} + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h new file mode 100644 index 00000000000..d97ee2b0556 --- /dev/null +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h @@ -0,0 +1,65 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup depsgraph + */ + +#pragma once + +#include "BKE_modifier.h" + +#include "intern/depsgraph_type.h" + +namespace DEG { + +struct Depsgraph; + +class AnimationValueBackup { + public: + AnimationValueBackup(); + AnimationValueBackup(const string &rna_path, int array_index, float value); + ~AnimationValueBackup(); + + AnimationValueBackup(const AnimationValueBackup &other) = default; + AnimationValueBackup(AnimationValueBackup &&other) noexcept = default; + + AnimationValueBackup &operator=(const AnimationValueBackup &other) = default; + AnimationValueBackup &operator=(AnimationValueBackup &&other) = default; + + string rna_path; + int array_index; + float value; +}; + +/* Backup of animated properties values. */ +class AnimationBackup { + public: + AnimationBackup(const Depsgraph *depsgraph); + + void reset(); + + void init_from_id(ID *id); + void restore_to_id(ID *id); + + bool meed_value_backup; + vector<AnimationValueBackup> values_backup; +}; + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/node/deg_node.cc b/source/blender/depsgraph/intern/node/deg_node.cc index 16c934e72fe..5002f2890ae 100644 --- a/source/blender/depsgraph/intern/node/deg_node.cc +++ b/source/blender/depsgraph/intern/node/deg_node.cc @@ -28,6 +28,7 @@ #include "BLI_utildefines.h" #include "intern/depsgraph.h" +#include "intern/depsgraph_relation.h" #include "intern/eval/deg_eval_copy_on_write.h" #include "intern/node/deg_node_component.h" #include "intern/node/deg_node_factory.h" diff --git a/source/blender/depsgraph/intern/node/deg_node_time.cc b/source/blender/depsgraph/intern/node/deg_node_time.cc index cae98ef56c0..ff3e950bb44 100644 --- a/source/blender/depsgraph/intern/node/deg_node_time.cc +++ b/source/blender/depsgraph/intern/node/deg_node_time.cc @@ -26,6 +26,7 @@ #include "DNA_scene_types.h" #include "intern/depsgraph.h" +#include "intern/depsgraph_relation.h" namespace DEG { diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index d0fe7c6637e..97d492d5dd5 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -1529,18 +1529,10 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, if ((ob->dt >= OB_SOLID) || DRW_state_is_image_render()) { /* Get per-material split surface */ - char *auto_layer_names; - int *auto_layer_is_srgb; - int auto_layer_count; struct GPUBatch **mat_geom = NULL; if (!use_sculpt_pbvh) { - mat_geom = DRW_cache_object_surface_material_get(ob, - gpumat_array, - materials_len, - &auto_layer_names, - &auto_layer_is_srgb, - &auto_layer_count); + mat_geom = DRW_cache_object_surface_material_get(ob, gpumat_array, materials_len); } if (use_sculpt_pbvh) { @@ -1577,28 +1569,6 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, ADD_SHGROUP_CALL_SAFE(shgrp_depth_array[i], ob, mat_geom[i], oedata); ADD_SHGROUP_CALL_SAFE(shgrp_depth_clip_array[i], ob, mat_geom[i], oedata); - char *name = auto_layer_names; - for (int j = 0; j < auto_layer_count; j++) { - /* TODO don't add these uniform when not needed (default pass shaders). */ - /* FIXME: This is broken, as it overrides any autolayers srgb bool of the previous mesh - * that shares the same material. */ - if (shgrp_array[i]) { - DRW_shgroup_uniform_bool_copy(shgrp_array[i], name, auto_layer_is_srgb[j]); - } - if (shgrp_depth_array[i]) { - DRW_shgroup_uniform_bool_copy(shgrp_depth_array[i], name, auto_layer_is_srgb[j]); - } - if (shgrp_depth_clip_array[i]) { - DRW_shgroup_uniform_bool_copy( - shgrp_depth_clip_array[i], name, auto_layer_is_srgb[j]); - } - /* Go to next layer name. */ - while (*name != '\0') { - name++; - } - name += 1; - } - /* Shadow Pass */ struct GPUMaterial *gpumat; const bool use_gpumat = (ma_array[i]->use_nodes && ma_array[i]->nodetree); diff --git a/source/blender/draw/engines/overlay/overlay_armature.c b/source/blender/draw/engines/overlay/overlay_armature.c index 0b77fcad265..416283e321b 100644 --- a/source/blender/draw/engines/overlay/overlay_armature.c +++ b/source/blender/draw/engines/overlay/overlay_armature.c @@ -35,6 +35,7 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BKE_action.h" #include "BKE_armature.h" #include "BKE_modifier.h" @@ -974,6 +975,15 @@ static bool set_pchan_color(const ArmatureDrawContext *ctx, /** \name Drawing Color Helpers * \{ */ +static void bone_locked_color_shade(float color[4]) +{ + float locked_color[4]; + + UI_GetThemeColor4fv(TH_BONE_LOCKED_WEIGHT, locked_color); + + interp_v3_v3v3(color, color, locked_color, locked_color[3]); +} + static const float *get_bone_solid_color(const ArmatureDrawContext *ctx, const EditBone *UNUSED(eBone), const bPoseChannel *pchan, @@ -989,6 +999,11 @@ static const float *get_bone_solid_color(const ArmatureDrawContext *ctx, static float disp_color[4]; copy_v4_v4(disp_color, pchan->draw_data->solid_color); set_pchan_color(ctx, PCHAN_COLOR_SOLID, boneflag, constflag, disp_color); + + if (boneflag & BONE_DRAW_LOCKED_WEIGHT) { + bone_locked_color_shade(disp_color); + } + return disp_color; } @@ -1009,7 +1024,7 @@ static const float *get_bone_solid_with_consts_color(const ArmatureDrawContext * const float *col = get_bone_solid_color(ctx, eBone, pchan, arm, boneflag, constflag); static float consts_color[4]; - if ((arm->flag & ARM_POSEMODE) && + if ((arm->flag & ARM_POSEMODE) && !(boneflag & BONE_DRAW_LOCKED_WEIGHT) && set_pchan_color(ctx, PCHAN_COLOR_CONSTS, boneflag, constflag, consts_color)) { interp_v3_v3v3(consts_color, col, consts_color, 0.5f); } @@ -1065,6 +1080,10 @@ static const float *get_bone_wire_color(const ArmatureDrawContext *ctx, else if (arm->flag & ARM_POSEMODE) { copy_v4_v4(disp_color, pchan->draw_data->wire_color); set_pchan_color(ctx, PCHAN_COLOR_NORMAL, boneflag, constflag, disp_color); + + if (boneflag & BONE_DRAW_LOCKED_WEIGHT) { + bone_locked_color_shade(disp_color); + } } else { copy_v3_v3(disp_color, ctx->color.vertex); @@ -1518,7 +1537,7 @@ static void draw_bone_custom_shape(ArmatureDrawContext *ctx, drw_shgroup_bone_custom_empty(ctx, disp_mat, col_wire, pchan->custom); } } - if ((boneflag & BONE_DRAWWIRE) == 0) { + if ((boneflag & BONE_DRAWWIRE) == 0 && (boneflag & BONE_DRAW_LOCKED_WEIGHT) == 0) { drw_shgroup_bone_custom_solid(ctx, disp_mat, col_solid, col_hint, col_wire, pchan->custom); } else { @@ -2010,6 +2029,8 @@ static void draw_armature_edit(ArmatureDrawContext *ctx) boneflag |= BONE_DRAW_ACTIVE; } + boneflag &= ~BONE_DRAW_LOCKED_WEIGHT; + draw_bone_relations(ctx, eBone, NULL, arm, boneflag, constflag); if (arm->drawtype == ARM_ENVELOPE) { @@ -2054,6 +2075,7 @@ static void draw_armature_pose(ArmatureDrawContext *ctx) bPoseChannel *pchan; int index = -1; const bool show_text = DRW_state_show_text(); + bool draw_locked_weights = false; /* We can't safely draw non-updated pose, might contain NULL bone pointers... */ if (ob->pose->flag & POSE_RECALC) { @@ -2089,6 +2111,28 @@ static void draw_armature_pose(ArmatureDrawContext *ctx) } } + /* In weight paint mode retrieve the vertex group lock status. */ + if ((draw_ctx->object_mode == OB_MODE_WEIGHT_PAINT) && (draw_ctx->object_pose == ob) && + (draw_ctx->obact != NULL)) { + draw_locked_weights = true; + + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + pchan->bone->flag &= ~BONE_DRAW_LOCKED_WEIGHT; + } + + const Object *obact_orig = DEG_get_original_object(draw_ctx->obact); + + LISTBASE_FOREACH (bDeformGroup *, dg, &obact_orig->defbase) { + if (dg->flag & DG_LOCK_WEIGHT) { + pchan = BKE_pose_channel_find_name(ob->pose, dg->name); + + if (pchan) { + pchan->bone->flag |= BONE_DRAW_LOCKED_WEIGHT; + } + } + } + } + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next, index += 0x10000) { Bone *bone = pchan->bone; const bool bone_visible = (bone->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG)) == 0; @@ -2120,6 +2164,10 @@ static void draw_armature_pose(ArmatureDrawContext *ctx) boneflag |= BONE_DRAW_ACTIVE; } + if (!draw_locked_weights) { + boneflag &= ~BONE_DRAW_LOCKED_WEIGHT; + } + draw_bone_relations(ctx, NULL, pchan, arm, boneflag, constflag); if ((pchan->custom) && !(arm->flag & ARM_NO_CUSTOM)) { diff --git a/source/blender/draw/engines/workbench/shaders/workbench_prepass_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_prepass_vert.glsl index 04dd9ab85bb..0a3252f0b9b 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_prepass_vert.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_prepass_vert.glsl @@ -4,13 +4,18 @@ in vec3 pos; in vec3 nor; in vec2 au; /* active texture layer */ # ifdef V3D_SHADING_VERTEX_COLOR -in vec3 ac; /* active color */ +in vec4 ac; /* active color */ # endif # define uv au #else /* HAIR_SHADER */ + # ifdef V3D_SHADING_TEXTURE_COLOR uniform samplerBuffer au; /* active texture layer */ # endif +# ifdef V3D_SHADING_VERTEX_COLOR +uniform samplerBuffer ac; /* active color layer */ +# endif + flat out float hair_rand; #endif /* HAIR_SHADER */ @@ -37,16 +42,6 @@ float integer_noise(int n) return (float(nn) / 1073741824.0); } -#ifdef V3D_SHADING_VERTEX_COLOR -vec3 srgb_to_linear_attr(vec3 c) -{ - c = max(c, vec3(0.0)); - vec3 c1 = c * (1.0 / 12.92); - vec3 c2 = pow((c + 0.055) * (1.0 / 1.055), vec3(2.4)); - return mix(c1, c2, step(vec3(0.04045), c)); -} -#endif - vec3 workbench_hair_hair_normal(vec3 tan, vec3 binor, float rand) { /* To "simulate" anisotropic shading, randomize hair normal per strand. */ @@ -90,7 +85,9 @@ void main() #ifdef V3D_SHADING_VERTEX_COLOR # ifndef HAIR_SHADER - vertexColor = srgb_to_linear_attr(ac); + vertexColor = ac.rgb; +# else + vertexColor = hair_get_customdata_vec4(ac).rgb; # endif #endif diff --git a/source/blender/draw/engines/workbench/workbench_deferred.c b/source/blender/draw/engines/workbench/workbench_deferred.c index 1af9357e015..86ed1f0b4c3 100644 --- a/source/blender/draw/engines/workbench/workbench_deferred.c +++ b/source/blender/draw/engines/workbench/workbench_deferred.c @@ -1138,8 +1138,7 @@ void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob) struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len); memset(gpumat_array, 0, sizeof(*gpumat_array) * materials_len); - geoms = DRW_cache_object_surface_material_get( - ob, gpumat_array, materials_len, NULL, NULL, NULL); + geoms = DRW_cache_object_surface_material_get(ob, gpumat_array, materials_len); for (int i = 0; i < materials_len; i++) { if (geoms != NULL && geoms[i] != NULL) { Material *mat = give_current_material(ob, i + 1); diff --git a/source/blender/draw/engines/workbench/workbench_forward.c b/source/blender/draw/engines/workbench/workbench_forward.c index ae001f8d10c..3b588868a94 100644 --- a/source/blender/draw/engines/workbench/workbench_forward.c +++ b/source/blender/draw/engines/workbench/workbench_forward.c @@ -756,7 +756,7 @@ void workbench_forward_cache_populate(WORKBENCH_Data *vedata, Object *ob) memset(gpumat_array, 0, sizeof(*gpumat_array) * materials_len); struct GPUBatch **mat_geom = DRW_cache_object_surface_material_get( - ob, gpumat_array, materials_len, NULL, NULL, NULL); + ob, gpumat_array, materials_len); if (mat_geom) { for (int i = 0; i < materials_len; i++) { if (mat_geom[i] == NULL) { diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index f2e43e032a9..bfb74a9576f 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -826,25 +826,11 @@ GPUBatch *DRW_cache_object_surface_get(Object *ob) GPUBatch **DRW_cache_object_surface_material_get(struct Object *ob, struct GPUMaterial **gpumat_array, - uint gpumat_array_len, - char **auto_layer_names, - int **auto_layer_is_srgb, - int *auto_layer_count) + uint gpumat_array_len) { - if (auto_layer_names != NULL) { - *auto_layer_names = NULL; - *auto_layer_is_srgb = NULL; - *auto_layer_count = 0; - } - switch (ob->type) { case OB_MESH: - return DRW_cache_mesh_surface_shaded_get(ob, - gpumat_array, - gpumat_array_len, - auto_layer_names, - auto_layer_is_srgb, - auto_layer_count); + return DRW_cache_mesh_surface_shaded_get(ob, gpumat_array, gpumat_array_len); case OB_CURVE: return DRW_cache_curve_surface_shaded_get(ob, gpumat_array, gpumat_array_len); case OB_SURF: @@ -2737,18 +2723,10 @@ GPUBatch *DRW_cache_mesh_surface_edges_get(Object *ob) /* Return list of batches with length equal to max(1, totcol). */ GPUBatch **DRW_cache_mesh_surface_shaded_get(Object *ob, struct GPUMaterial **gpumat_array, - uint gpumat_array_len, - char **auto_layer_names, - int **auto_layer_is_srgb, - int *auto_layer_count) + uint gpumat_array_len) { BLI_assert(ob->type == OB_MESH); - return DRW_mesh_batch_cache_get_surface_shaded(ob->data, - gpumat_array, - gpumat_array_len, - auto_layer_names, - auto_layer_is_srgb, - auto_layer_count); + return DRW_mesh_batch_cache_get_surface_shaded(ob->data, gpumat_array, gpumat_array_len); } /* Return list of batches with length equal to max(1, totcol). */ @@ -2899,8 +2877,7 @@ GPUBatch **DRW_cache_curve_surface_shaded_get(Object *ob, struct Curve *cu = ob->data; struct Mesh *mesh_eval = ob->runtime.mesh_eval; if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_surface_shaded( - mesh_eval, gpumat_array, gpumat_array_len, NULL, NULL, NULL); + return DRW_mesh_batch_cache_get_surface_shaded(mesh_eval, gpumat_array, gpumat_array_len); } else { return DRW_curve_batch_cache_get_surface_shaded(cu, gpumat_array, gpumat_array_len); @@ -3040,8 +3017,7 @@ GPUBatch **DRW_cache_text_surface_shaded_get(Object *ob, return NULL; } if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_surface_shaded( - mesh_eval, gpumat_array, gpumat_array_len, NULL, NULL, NULL); + return DRW_mesh_batch_cache_get_surface_shaded(mesh_eval, gpumat_array, gpumat_array_len); } else { return DRW_curve_batch_cache_get_surface_shaded(cu, gpumat_array, gpumat_array_len); @@ -3135,8 +3111,7 @@ GPUBatch **DRW_cache_surf_surface_shaded_get(Object *ob, struct Curve *cu = ob->data; struct Mesh *mesh_eval = ob->runtime.mesh_eval; if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_surface_shaded( - mesh_eval, gpumat_array, gpumat_array_len, NULL, NULL, NULL); + return DRW_mesh_batch_cache_get_surface_shaded(mesh_eval, gpumat_array, gpumat_array_len); } else { return DRW_curve_batch_cache_get_surface_shaded(cu, gpumat_array, gpumat_array_len); diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h index 11564464546..3759654931a 100644 --- a/source/blender/draw/intern/draw_cache.h +++ b/source/blender/draw/intern/draw_cache.h @@ -54,10 +54,7 @@ struct GPUBatch *DRW_cache_object_surface_get(struct Object *ob); struct GPUBatch *DRW_cache_object_loose_edges_get(struct Object *ob); struct GPUBatch **DRW_cache_object_surface_material_get(struct Object *ob, struct GPUMaterial **gpumat_array, - uint gpumat_array_len, - char **auto_layer_names, - int **auto_layer_is_srgb, - int *auto_layer_count); + uint gpumat_array_len); struct GPUBatch *DRW_cache_object_face_wireframe_get(struct Object *ob); /* Empties */ @@ -127,10 +124,7 @@ struct GPUBatch *DRW_cache_mesh_surface_get(struct Object *ob); struct GPUBatch *DRW_cache_mesh_surface_edges_get(struct Object *ob); struct GPUBatch **DRW_cache_mesh_surface_shaded_get(struct Object *ob, struct GPUMaterial **gpumat_array, - uint gpumat_array_len, - char **auto_layer_names, - int **auto_layer_is_srgb, - int *auto_layer_count); + uint gpumat_array_len); struct GPUBatch **DRW_cache_mesh_surface_texpaint_get(struct Object *ob); struct GPUBatch *DRW_cache_mesh_surface_texpaint_single_get(struct Object *ob); struct GPUBatch *DRW_cache_mesh_surface_vertpaint_get(struct Object *ob); diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index 2d10199782b..9228147af44 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -213,12 +213,6 @@ typedef struct MeshBatchCache { GPUBatch **surface_per_mat; - /* arrays of bool uniform names (and value) that will be use to - * set srgb conversion for auto attributes.*/ - char *auto_layer_names; - int *auto_layer_is_srgb; - int auto_layer_len; - DRWBatchFlag batch_requested; DRWBatchFlag batch_ready; diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c index c98494ebdd9..ee0597c6b21 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh.c @@ -1928,7 +1928,7 @@ static void *extract_vcol_init(const MeshRenderData *mr, void *buf) GPU_vertformat_safe_attrib_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTRIB_NAME); BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name); - GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL)) { GPU_vertformat_alias_add(&format, "c"); @@ -1948,12 +1948,20 @@ static void *extract_vcol_init(const MeshRenderData *mr, void *buf) GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); - MLoopCol *vcol_data = (MLoopCol *)vbo->data; + typedef struct gpuMeshVcol { + ushort r, g, b, a; + } gpuMeshVcol; + + gpuMeshVcol *vcol_data = (gpuMeshVcol *)vbo->data; for (int i = 0; i < 8; i++) { if (vcol_layers & (1 << i)) { - void *layer_data = CustomData_get_layer_n(cd_ldata, CD_MLOOPCOL, i); - memcpy(vcol_data, layer_data, sizeof(*vcol_data) * mr->loop_len); - vcol_data += mr->loop_len; + MLoopCol *mcol = (MLoopCol *)CustomData_get_layer_n(cd_ldata, CD_MLOOPCOL, i); + for (int l = 0; l < mr->loop_len; l++, mcol++, vcol_data++) { + vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->r]); + vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->g]); + vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->b]); + vcol_data->a = unit_float_to_ushort_clamp(mcol->a * (1.0f / 255.0f)); + } } } return NULL; diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h index 8cb318bd0bb..bf056d7444d 100644 --- a/source/blender/draw/intern/draw_cache_impl.h +++ b/source/blender/draw/intern/draw_cache_impl.h @@ -127,10 +127,7 @@ struct GPUBatch *DRW_mesh_batch_cache_get_surface(struct Mesh *me); struct GPUBatch *DRW_mesh_batch_cache_get_surface_edges(struct Mesh *me); struct GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(struct Mesh *me, struct GPUMaterial **gpumat_array, - uint gpumat_array_len, - char **auto_layer_names, - int **auto_layer_is_srgb, - int *auto_layer_count); + uint gpumat_array_len); struct GPUBatch **DRW_mesh_batch_cache_get_surface_texpaint(struct Mesh *me); struct GPUBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(struct Mesh *me); struct GPUBatch *DRW_mesh_batch_cache_get_surface_vertpaint(struct Mesh *me); diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index aadcc2a939a..458029c2580 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -230,68 +230,6 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, return cd_used; } -static void mesh_cd_extract_auto_layers_names_and_srgb(Mesh *me, - DRW_MeshCDMask cd_used, - char **r_auto_layers_names, - int **r_auto_layers_srgb, - int *r_auto_layers_len) -{ - const Mesh *me_final = (me->edit_mesh) ? me->edit_mesh->mesh_eval_final : me; - const CustomData *cd_ldata = &me_final->ldata; - - int uv_len_used = count_bits_i(cd_used.uv); - int vcol_len_used = count_bits_i(cd_used.vcol); - int uv_len = CustomData_number_of_layers(cd_ldata, CD_MLOOPUV); - int vcol_len = CustomData_number_of_layers(cd_ldata, CD_MLOOPCOL); - - uint auto_names_len = 32 * (uv_len_used + vcol_len_used); - uint auto_ofs = 0; - /* Allocate max, resize later. */ - char *auto_names = MEM_callocN(sizeof(char) * auto_names_len, __func__); - int *auto_is_srgb = MEM_callocN(sizeof(int) * (uv_len_used + vcol_len_used), __func__); - - for (int i = 0; i < uv_len; i++) { - if ((cd_used.uv & (1 << i)) != 0) { - const char *name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i); - char safe_name[GPU_MAX_SAFE_ATTRIB_NAME]; - GPU_vertformat_safe_attrib_name(name, safe_name, GPU_MAX_SAFE_ATTRIB_NAME); - auto_ofs += BLI_snprintf_rlen( - auto_names + auto_ofs, auto_names_len - auto_ofs, "ba%s", safe_name); - /* +1 to include '\0' terminator. */ - auto_ofs += 1; - } - } - - uint auto_is_srgb_ofs = uv_len_used; - for (int i = 0; i < vcol_len; i++) { - if ((cd_used.vcol & (1 << i)) != 0) { - const char *name = CustomData_get_layer_name(cd_ldata, CD_MLOOPCOL, i); - /* We only do vcols that are not overridden by a uv layer with same name. */ - if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, name) == -1) { - char safe_name[GPU_MAX_SAFE_ATTRIB_NAME]; - GPU_vertformat_safe_attrib_name(name, safe_name, GPU_MAX_SAFE_ATTRIB_NAME); - auto_ofs += BLI_snprintf_rlen( - auto_names + auto_ofs, auto_names_len - auto_ofs, "ba%s", safe_name); - /* +1 to include '\0' terminator. */ - auto_ofs += 1; - auto_is_srgb[auto_is_srgb_ofs] = true; - auto_is_srgb_ofs++; - } - } - } - - auto_names = MEM_reallocN(auto_names, sizeof(char) * auto_ofs); - auto_is_srgb = MEM_reallocN(auto_is_srgb, sizeof(int) * auto_is_srgb_ofs); - - /* WATCH: May have been referenced somewhere before freeing. */ - MEM_SAFE_FREE(*r_auto_layers_names); - MEM_SAFE_FREE(*r_auto_layers_srgb); - - *r_auto_layers_names = auto_names; - *r_auto_layers_srgb = auto_is_srgb; - *r_auto_layers_len = auto_is_srgb_ofs; -} - /** \} */ /* ---------------------------------------------------------------------- */ @@ -492,8 +430,6 @@ static void mesh_batch_cache_discard_shaded_tri(MeshBatchCache *cache) mesh_cd_layers_type_clear(&cache->cd_used); MEM_SAFE_FREE(cache->surface_per_mat); - MEM_SAFE_FREE(cache->auto_layer_names); - MEM_SAFE_FREE(cache->auto_layer_is_srgb); cache->mat_len = 0; } @@ -771,10 +707,7 @@ GPUBatch *DRW_mesh_batch_cache_get_edit_mesh_analysis(Mesh *me) GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Mesh *me, struct GPUMaterial **gpumat_array, - uint gpumat_array_len, - char **auto_layer_names, - int **auto_layer_is_srgb, - int *auto_layer_count) + uint gpumat_array_len) { MeshBatchCache *cache = mesh_batch_cache_get(me); DRW_MeshCDMask cd_needed = mesh_cd_calc_used_gpu_layers(me, gpumat_array, gpumat_array_len); @@ -783,21 +716,8 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Mesh *me, mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed); - if (!mesh_cd_layers_type_overlap(cache->cd_used, cd_needed)) { - mesh_cd_extract_auto_layers_names_and_srgb(me, - cache->cd_needed, - &cache->auto_layer_names, - &cache->auto_layer_is_srgb, - &cache->auto_layer_len); - } - mesh_batch_cache_add_request(cache, MBC_SURF_PER_MAT); - if (auto_layer_names) { - *auto_layer_names = cache->auto_layer_names; - *auto_layer_is_srgb = cache->auto_layer_is_srgb; - *auto_layer_count = cache->auto_layer_len; - } for (int i = 0; i < cache->mat_len; i++) { DRW_batch_request(&cache->surface_per_mat[i]); } diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c index 7de6ee2b3b1..f4057927d55 100644 --- a/source/blender/draw/intern/draw_cache_impl_particles.c +++ b/source/blender/draw/intern/draw_cache_impl_particles.c @@ -1199,7 +1199,7 @@ static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit, GPU_vertformat_safe_attrib_name(name, attr_safe_name, GPU_MAX_SAFE_ATTRIB_NAME); BLI_snprintf(uuid, sizeof(uuid), "c%s", attr_safe_name); - col_id[i] = GPU_vertformat_attr_add(&format, uuid, GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + col_id[i] = GPU_vertformat_attr_add(&format, uuid, GPU_COMP_U16, 4, GPU_FETCH_FLOAT); if (i == active_col) { GPU_vertformat_alias_add(&format, "c"); diff --git a/source/blender/editors/animation/time_scrub_ui.c b/source/blender/editors/animation/time_scrub_ui.c index 70a9b7ba1fa..ae489fb5233 100644 --- a/source/blender/editors/animation/time_scrub_ui.c +++ b/source/blender/editors/animation/time_scrub_ui.c @@ -179,9 +179,9 @@ void ED_time_scrub_channel_search_draw(const bContext *C, ARegion *ar, bDopeShee rcti rect; rect.xmin = 0; - rect.xmax = ceilf(ar->sizex * UI_DPI_FAC); - rect.ymin = ar->sizey * UI_DPI_FAC - UI_TIME_SCRUB_MARGIN_Y; - rect.ymax = ceilf(ar->sizey * UI_DPI_FAC); + rect.xmax = ar->winx; + rect.ymin = ar->winy - UI_TIME_SCRUB_MARGIN_Y; + rect.ymax = ar->winy; uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c index 4c4bac6a249..5486d60d5d7 100644 --- a/source/blender/editors/curve/editcurve_paint.c +++ b/source/blender/editors/curve/editcurve_paint.c @@ -460,14 +460,8 @@ static void curve_draw_event_add(wmOperator *op, const wmEvent *event) ARRAY_SET_ITEMS(selem->mval, event->mval[0], event->mval[1]); - /* handle pressure sensitivity (which is supplied by tablets) */ - if (event->tablet_data) { - const wmTabletData *wmtab = event->tablet_data; - selem->pressure = wmtab->Pressure; - } - else { - selem->pressure = 1.0f; - } + /* handle pressure sensitivity (which is supplied by tablets or otherwise 1.0) */ + selem->pressure = event->tablet.pressure; bool is_depth_found = stroke_elem_project_fallback_elem( cdd, cdd->prev.location_world_valid, selem); diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 04b5d8f6d40..5eaf14e361b 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -1461,12 +1461,7 @@ static void gpencil_draw_toggle_eraser_cursor(bContext *C, tGPsdata *p, short en /* Check if tablet eraser is being used (when processing events) */ static bool gpencil_is_tablet_eraser_active(const wmEvent *event) { - if (event->tablet_data) { - const wmTabletData *wmtab = event->tablet_data; - return (wmtab->Active == EVT_TABLET_ERASER); - } - - return false; + return (event->tablet.active == EVT_TABLET_ERASER); } /* ------------------------------- */ @@ -1686,7 +1681,6 @@ static void annotation_draw_apply_event( tGPsdata *p = op->customdata; PointerRNA itemptr; float mousef[2]; - int tablet = 0; /* convert from window-space to area-space mouse coordinates * add any x,y override position for fake events @@ -1720,29 +1714,20 @@ static void annotation_draw_apply_event( p->curtime = PIL_check_seconds_timer(); - /* handle pressure sensitivity (which is supplied by tablets) */ - if (event->tablet_data) { - const wmTabletData *wmtab = event->tablet_data; + /* handle pressure sensitivity (which is supplied by tablets or otherwise 1.0) */ + p->pressure = event->tablet.pressure; - tablet = (wmtab->Active != EVT_TABLET_NONE); - p->pressure = wmtab->Pressure; - - /* Hack for pressure sensitive eraser on D+RMB when using a tablet: - * The pen has to float over the tablet surface, resulting in - * zero pressure (T47101). Ignore pressure values if floating - * (i.e. "effectively zero" pressure), and only when the "active" - * end is the stylus (i.e. the default when not eraser) - */ - if (p->paintmode == GP_PAINTMODE_ERASER) { - if ((wmtab->Active != EVT_TABLET_ERASER) && (p->pressure < 0.001f)) { - p->pressure = 1.0f; - } + /* Hack for pressure sensitive eraser on D+RMB when using a tablet: + * The pen has to float over the tablet surface, resulting in + * zero pressure (T47101). Ignore pressure values if floating + * (i.e. "effectively zero" pressure), and only when the "active" + * end is the stylus (i.e. the default when not eraser) + */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + if ((event->tablet.active != EVT_TABLET_ERASER) && (p->pressure < 0.001f)) { + p->pressure = 1.0f; } } - else { - /* No tablet data -> No pressure info is available */ - p->pressure = 1.0f; - } /* special exception for start of strokes (i.e. maybe for just a dot) */ if (p->flags & GP_PAINTFLAG_FIRSTRUN) { @@ -1758,7 +1743,7 @@ static void annotation_draw_apply_event( /* special exception here for too high pressure values on first touch in * windows for some tablets, then we just skip first touch... */ - if (tablet && (p->pressure >= 0.99f)) { + if ((event->tablet.active != EVT_TABLET_NONE) && (p->pressure >= 0.99f)) { return; } } diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c index 91e70e0e089..c2f1e9f091a 100644 --- a/source/blender/editors/gpencil/gpencil_brush.c +++ b/source/blender/editors/gpencil/gpencil_brush.c @@ -1977,7 +1977,6 @@ static void gpsculpt_brush_apply_event(bContext *C, wmOperator *op, const wmEven GP_Sculpt_Settings *gset = &ts->gp_sculpt; PointerRNA itemptr; float mouse[2]; - int tablet = 0; mouse[0] = event->mval[0] + 1; mouse[1] = event->mval[1] + 1; @@ -1989,24 +1988,14 @@ static void gpsculpt_brush_apply_event(bContext *C, wmOperator *op, const wmEven RNA_boolean_set(&itemptr, "pen_flip", event->ctrl != false); RNA_boolean_set(&itemptr, "is_start", gso->first); - /* handle pressure sensitivity (which is supplied by tablets) */ - if (event->tablet_data) { - const wmTabletData *wmtab = event->tablet_data; - float pressure = wmtab->Pressure; - - tablet = (wmtab->Active != EVT_TABLET_NONE); - - /* special exception here for too high pressure values on first touch in - * windows for some tablets: clamp the values to be sane - */ - if (tablet && (pressure >= 0.99f)) { - pressure = 1.0f; - } - RNA_float_set(&itemptr, "pressure", pressure); - } - else { - RNA_float_set(&itemptr, "pressure", 1.0f); + /* handle pressure sensitivity (which is supplied by tablets and otherwise 1.0) */ + float pressure = event->tablet.pressure; + /* special exception here for too high pressure values on first touch in + * windows for some tablets: clamp the values to be sane */ + if (pressure >= 0.99f) { + pressure = 1.0f; } + RNA_float_set(&itemptr, "pressure", pressure); if (!gso->is_weight_mode) { if (event->shift) { diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 6a91417e7f3..09ff24f05fc 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -2553,12 +2553,7 @@ static void gpencil_draw_toggle_eraser_cursor(bContext *C, tGPsdata *p, short en /* Check if tablet eraser is being used (when processing events) */ static bool gpencil_is_tablet_eraser_active(const wmEvent *event) { - if (event->tablet_data) { - const wmTabletData *wmtab = event->tablet_data; - return (wmtab->Active == EVT_TABLET_ERASER); - } - - return false; + return (event->tablet.active == EVT_TABLET_ERASER); } /* ------------------------------- */ @@ -3020,7 +3015,6 @@ static void gpencil_draw_apply_event(bContext *C, GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; PointerRNA itemptr; float mousef[2]; - int tablet = 0; bool is_speed_guide = ((guide->use_guide) && (p->brush && (p->brush->gpencil_tool == GPAINT_TOOL_DRAW))); @@ -3055,29 +3049,20 @@ static void gpencil_draw_apply_event(bContext *C, p->curtime = PIL_check_seconds_timer(); - /* handle pressure sensitivity (which is supplied by tablets) */ - if (event->tablet_data) { - const wmTabletData *wmtab = event->tablet_data; + /* handle pressure sensitivity (which is supplied by tablets or otherwise 1.0) */ + p->pressure = event->tablet.pressure; - tablet = (wmtab->Active != EVT_TABLET_NONE); - p->pressure = wmtab->Pressure; - - /* Hack for pressure sensitive eraser on D+RMB when using a tablet: - * The pen has to float over the tablet surface, resulting in - * zero pressure (T47101). Ignore pressure values if floating - * (i.e. "effectively zero" pressure), and only when the "active" - * end is the stylus (i.e. the default when not eraser) - */ - if (p->paintmode == GP_PAINTMODE_ERASER) { - if ((wmtab->Active != EVT_TABLET_ERASER) && (p->pressure < 0.001f)) { - p->pressure = 1.0f; - } + /* Hack for pressure sensitive eraser on D+RMB when using a tablet: + * The pen has to float over the tablet surface, resulting in + * zero pressure (T47101). Ignore pressure values if floating + * (i.e. "effectively zero" pressure), and only when the "active" + * end is the stylus (i.e. the default when not eraser) + */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + if ((event->tablet.active != EVT_TABLET_ERASER) && (p->pressure < 0.001f)) { + p->pressure = 1.0f; } } - else { - /* No tablet data -> No pressure info is available */ - p->pressure = 1.0f; - } /* special eraser modes */ if (p->paintmode == GP_PAINTMODE_ERASER) { @@ -3101,7 +3086,7 @@ static void gpencil_draw_apply_event(bContext *C, /* special exception here for too high pressure values on first touch in * windows for some tablets, then we just skip first touch... */ - if (tablet && (p->pressure >= 0.99f)) { + if ((event->tablet.active != EVT_TABLET_NONE) && (p->pressure >= 0.99f)) { return; } diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 6a801fc9928..9e0272d1402 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -69,7 +69,7 @@ void ED_region_exit(struct bContext *C, struct ARegion *ar); void ED_region_remove(struct bContext *C, struct ScrArea *sa, struct ARegion *ar); void ED_region_pixelspace(struct ARegion *ar); void ED_region_update_rect(struct ARegion *ar); -void ED_region_init(struct ARegion *ar); +void ED_region_floating_initialize(struct ARegion *ar); void ED_region_tag_redraw(struct ARegion *ar); void ED_region_tag_redraw_partial(struct ARegion *ar, const struct rcti *rct, bool rebuild); void ED_region_tag_redraw_overlay(struct ARegion *ar); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index b81fa4ae483..3089d980f06 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -1640,7 +1640,12 @@ struct Panel *UI_panel_begin(struct ScrArea *sa, struct PanelType *pt, struct Panel *pa, bool *r_open); -void UI_panel_end(uiBlock *block, int width, int height, bool open); +void UI_panel_end(const struct ScrArea *sa, + const struct ARegion *ar, + uiBlock *block, + int width, + int height, + bool open); void UI_panels_scale(struct ARegion *ar, float new_width); void UI_panel_label_offset(struct uiBlock *block, int *r_x, int *r_y); int UI_panel_size_y(const struct Panel *pa); diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index bd8eed4e4aa..66662c8c27d 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -143,6 +143,7 @@ typedef enum ThemeColorID { TH_BONE_SOLID, TH_BONE_POSE, TH_BONE_POSE_ACTIVE, + TH_BONE_LOCKED_WEIGHT, TH_STRIP, TH_STRIP_SELECT, diff --git a/source/blender/editors/interface/interface_align.c b/source/blender/editors/interface/interface_align.c index cc68e303e4a..c058fefb4fa 100644 --- a/source/blender/editors/interface/interface_align.c +++ b/source/blender/editors/interface/interface_align.c @@ -124,7 +124,7 @@ bool ui_but_can_align(const uiBut *but) int ui_but_align_opposite_to_area_align_get(const ARegion *ar) { - switch (ar->alignment) { + switch (RGN_ALIGN_ENUM_FROM_MASK(ar->alignment)) { case RGN_ALIGN_TOP: return UI_BUT_ALIGN_DOWN; case RGN_ALIGN_BOTTOM: diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 05d5f03a363..16996681695 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -1886,11 +1886,13 @@ static bool ui_but_drag_init(bContext *C, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER, RGN_TYPE_FOOTER)) { + const int ar_alignment = RGN_ALIGN_ENUM_FROM_MASK(data->region->alignment); int lock_axis = -1; - if (ELEM(data->region->alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) { + + if (ELEM(ar_alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) { lock_axis = 0; } - else if (ELEM(data->region->alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) { + else if (ELEM(ar_alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) { lock_axis = 1; } if (lock_axis != -1) { diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 52696475c20..179fe84264f 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -1241,17 +1241,19 @@ static void ui_item_menu_hold(struct bContext *C, ARegion *butregion, uiBut *but char direction = UI_DIR_DOWN; if (!but->drawstr[0]) { - if (butregion->alignment == RGN_ALIGN_LEFT) { - direction = UI_DIR_RIGHT; - } - else if (butregion->alignment == RGN_ALIGN_RIGHT) { - direction = UI_DIR_LEFT; - } - else if (butregion->alignment == RGN_ALIGN_BOTTOM) { - direction = UI_DIR_UP; - } - else { - direction = UI_DIR_DOWN; + switch (RGN_ALIGN_ENUM_FROM_MASK(butregion->alignment)) { + case RGN_ALIGN_LEFT: + direction = UI_DIR_RIGHT; + break; + case RGN_ALIGN_RIGHT: + direction = UI_DIR_LEFT; + break; + case RGN_ALIGN_BOTTOM: + direction = UI_DIR_UP; + break; + default: + direction = UI_DIR_DOWN; + break; } } UI_block_direction_set(block, direction); @@ -2436,6 +2438,10 @@ void uiItemEnumR_string_prop(uiLayout *layout, } for (a = 0; item[a].identifier; a++) { + if (item[a].identifier[0] == '\0') { + /* Skip enum item separators. */ + continue; + } if (item[a].value == ivalue) { const char *item_name = name ? name : diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 8adb82a22c8..cc1b7187e45 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -131,7 +131,7 @@ typedef enum eSpaceButtons_Align { BUT_AUTO = 2, } eSpaceButtons_Align; -static int panel_aligned(ScrArea *sa, ARegion *ar) +static int panel_aligned(const ScrArea *sa, const ARegion *ar) { if (sa->spacetype == SPACE_PROPERTIES && ar->regiontype == RGN_TYPE_WINDOW) { return BUT_VERTICAL; @@ -367,7 +367,19 @@ Panel *UI_panel_begin( return pa; } -void UI_panel_end(uiBlock *block, int width, int height, bool open) +static float panel_region_offset_x_get(const ARegion *ar, int align) +{ + if (UI_panel_category_is_visible(ar)) { + if (align == BUT_VERTICAL && (RGN_ALIGN_ENUM_FROM_MASK(ar->alignment) != RGN_ALIGN_RIGHT)) { + return UI_PANEL_CATEGORY_MARGIN_WIDTH; + } + } + + return 0; +} + +void UI_panel_end( + const ScrArea *sa, const ARegion *ar, uiBlock *block, int width, int height, bool open) { Panel *pa = block->panel; @@ -391,6 +403,7 @@ void UI_panel_end(uiBlock *block, int width, int height, bool open) } else { int old_sizex = pa->sizex, old_sizey = pa->sizey; + int old_region_ofsx = pa->runtime.region_ofsx; /* update width/height if non-zero */ if (width != 0) { @@ -405,6 +418,11 @@ void UI_panel_end(uiBlock *block, int width, int height, bool open) pa->runtime_flag |= PNL_ANIM_ALIGN; pa->ofsy += old_sizey - pa->sizey; } + + int align = panel_aligned(sa, ar); + if (old_region_ofsx != panel_region_offset_x_get(ar, align)) { + pa->runtime_flag |= PNL_ANIM_ALIGN; + } } } @@ -1004,7 +1022,6 @@ static bool uiAlignPanelStep(ScrArea *sa, ARegion *ar, const float fac, const bo int a, tot = 0; bool done; int align = panel_aligned(sa, ar); - bool has_category_tabs = UI_panel_category_is_visible(ar); /* count active, not tabbed panels */ for (pa = ar->panels.first; pa; pa = pa->next) { @@ -1061,14 +1078,10 @@ static bool uiAlignPanelStep(ScrArea *sa, ARegion *ar, const float fac, const bo /* no smart other default start loc! this keeps switching f5/f6/etc compatible */ ps = panelsort; + ps->pa->runtime.region_ofsx = panel_region_offset_x_get(ar, align); ps->pa->ofsx = 0; ps->pa->ofsy = -get_panel_size_y(ps->pa); - - if (has_category_tabs) { - if (align == BUT_VERTICAL && (ar->alignment != RGN_ALIGN_RIGHT)) { - ps->pa->ofsx += UI_PANEL_CATEGORY_MARGIN_WIDTH; - } - } + ps->pa->ofsx += ps->pa->runtime.region_ofsx; for (a = 0; a < tot - 1; a++, ps++) { psnext = ps + 1; @@ -1913,7 +1926,7 @@ void UI_panel_category_draw_all(ARegion *ar, const char *category_id_active) { /* no tab outlines for */ // #define USE_FLAT_INACTIVE - const bool is_left = (ar->alignment != RGN_ALIGN_RIGHT); + const bool is_left = RGN_ALIGN_ENUM_FROM_MASK(ar->alignment != RGN_ALIGN_RIGHT); View2D *v2d = &ar->v2d; uiStyle *style = UI_style_get(); const uiFontStyle *fstyle = &style->widget; @@ -2201,7 +2214,7 @@ static int ui_handle_panel_category_cycling(const wmEvent *event, { const bool is_mousewheel = ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE); const bool inside_tabregion = - ((ar->alignment != RGN_ALIGN_RIGHT) ? + ((RGN_ALIGN_ENUM_FROM_MASK(ar->alignment) != RGN_ALIGN_RIGHT) ? (event->mval[0] < ((PanelCategoryDyn *)ar->panels_category.first)->rect.xmax) : (event->mval[0] > ((PanelCategoryDyn *)ar->panels_category.first)->rect.xmin)); diff --git a/source/blender/editors/interface/interface_region_hud.c b/source/blender/editors/interface/interface_region_hud.c index fa471441cc9..d32cd5c17e2 100644 --- a/source/blender/editors/interface/interface_region_hud.c +++ b/source/blender/editors/interface/interface_region_hud.c @@ -364,7 +364,7 @@ void ED_area_type_hud_ensure(bContext *C, ScrArea *sa) ED_area_update_region_sizes(wm, win, sa); } - ED_region_init(ar); + ED_region_floating_initialize(ar); ED_region_tag_redraw(ar); /* Reset zoom level (not well supported). */ diff --git a/source/blender/editors/interface/interface_region_menu_popup.c b/source/blender/editors/interface/interface_region_menu_popup.c index fed3c0b3d11..560c6146afe 100644 --- a/source/blender/editors/interface/interface_region_menu_popup.c +++ b/source/blender/editors/interface/interface_region_menu_popup.c @@ -280,13 +280,13 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi ARegion *ar = CTX_wm_region(C); if (sa && ar) { if (ELEM(ar->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) { - if (ED_area_header_alignment(sa) == RGN_ALIGN_BOTTOM) { + if (RGN_ALIGN_ENUM_FROM_MASK(ED_area_header_alignment(sa)) == RGN_ALIGN_BOTTOM) { UI_block_direction_set(block, UI_DIR_UP); UI_block_order_flip(block); } } if (ar->regiontype == RGN_TYPE_FOOTER) { - if (ED_area_footer_alignment(sa) == RGN_ALIGN_BOTTOM) { + if (RGN_ALIGN_ENUM_FROM_MASK(ED_area_footer_alignment(sa)) == RGN_ALIGN_BOTTOM) { UI_block_direction_set(block, UI_DIR_UP); UI_block_order_flip(block); } diff --git a/source/blender/editors/interface/interface_region_popover.c b/source/blender/editors/interface/interface_region_popover.c index cd0421dde09..2042c15ed96 100644 --- a/source/blender/editors/interface/interface_region_popover.c +++ b/source/blender/editors/interface/interface_region_popover.c @@ -189,12 +189,12 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v /* Prefer popover from header to be positioned into the editor. */ else if (sa && ar) { if (ELEM(ar->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) { - if (ED_area_header_alignment(sa) == RGN_ALIGN_BOTTOM) { + if (RGN_ALIGN_ENUM_FROM_MASK(ED_area_header_alignment(sa)) == RGN_ALIGN_BOTTOM) { UI_block_direction_set(block, UI_DIR_UP | UI_DIR_CENTER_X); } } if (ar->regiontype == RGN_TYPE_FOOTER) { - if (ED_area_footer_alignment(sa) == RGN_ALIGN_BOTTOM) { + if (RGN_ALIGN_ENUM_FROM_MASK(ED_area_footer_alignment(sa)) == RGN_ALIGN_BOTTOM) { UI_block_direction_set(block, UI_DIR_UP | UI_DIR_CENTER_X); } } diff --git a/source/blender/editors/interface/interface_region_popup.c b/source/blender/editors/interface/interface_region_popup.c index 63dee77e90e..59d955e0278 100644 --- a/source/blender/editors/interface/interface_region_popup.c +++ b/source/blender/editors/interface/interface_region_popup.c @@ -728,7 +728,7 @@ uiBlock *ui_popup_block_refresh(bContext *C, ui_popup_block_scrolltest(block); /* adds subwindow */ - ED_region_init(ar); + ED_region_floating_initialize(ar); /* get winmat now that we actually have the subwindow */ wmGetProjectionMatrix(block->winmat, &ar->winrct); diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c index 251bc86a3f5..94bcd6ab37d 100644 --- a/source/blender/editors/interface/interface_region_search.c +++ b/source/blender/editors/interface/interface_region_search.c @@ -639,7 +639,7 @@ ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiBut *but } /* adds subwindow */ - ED_region_init(ar); + ED_region_floating_initialize(ar); /* notify change and redraw */ ED_region_tag_redraw(ar); diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index 38c17f3c395..7a0c04be356 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -1391,7 +1391,7 @@ static ARegion *ui_tooltip_create_with_data(bContext *C, } /* adds subwindow */ - ED_region_init(ar); + ED_region_floating_initialize(ar); /* notify change and redraw */ ED_region_tag_redraw(ar); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 280ce4c0efa..c43b7385a36 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -6865,7 +6865,6 @@ static char *progress_tooltip_func(bContext *UNUSED(C), void *argN, const char * void uiTemplateRunningJobs(uiLayout *layout, bContext *C) { - bScreen *screen = CTX_wm_screen(C); wmWindowManager *wm = CTX_wm_manager(C); ScrArea *sa = CTX_wm_area(C); uiBlock *block; @@ -7047,7 +7046,7 @@ void uiTemplateRunningJobs(uiLayout *layout, bContext *C) } } - if (screen->animtimer) { + if (ED_screen_animation_no_scrub(wm)) { uiDefIconTextBut(block, UI_BTYPE_BUT, B_STOPANIM, diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index d313310bfa1..fd2f652d40e 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -2662,10 +2662,14 @@ static void widget_state(uiWidgetType *wt, int state, int drawflag) if (color_blend != NULL) { color_blend_v3_v3(wt->wcol.inner, color_blend, wcol_state->blend); } - } - if (state & UI_ACTIVE) { - widget_active_color(&wt->wcol); + /* Add "hover" highlight. Ideally this could apply in all cases, + * even if UI_SELECT. But currently this causes some flickering + * as buttons can be created and updated without respect to mouse + * position and so can draw without UI_ACTIVE set. See D6503. */ + if (state & UI_ACTIVE) { + widget_active_color(&wt->wcol); + } } if (state & UI_BUT_REDALERT) { diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index f8b4d85a212..37921b48401 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -443,6 +443,9 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) case TH_BONE_POSE_ACTIVE: cp = ts->bone_pose_active; break; + case TH_BONE_LOCKED_WEIGHT: + cp = ts->bone_locked_weight; + break; case TH_STRIP: cp = ts->strip; break; diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index a3f84e7bdd0..a23bd7dad35 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -354,11 +354,6 @@ void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy) /* note, scroll is being flipped in ED_region_panels() drawing */ v2d->scroll |= (V2D_SCROLL_HORIZONTAL_HIDE | V2D_SCROLL_VERTICAL_HIDE); - /* initialize without scroll bars (interferes with zoom level see: T47047) */ - if (do_init) { - v2d->scroll |= (V2D_SCROLL_VERTICAL_FULLR | V2D_SCROLL_HORIZONTAL_FULLR); - } - if (do_init) { float panelzoom = (style) ? style->panelzoom : 1.0f; diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c index 7582ef3f41d..9f9e523b57b 100644 --- a/source/blender/editors/io/io_alembic.c +++ b/source/blender/editors/io/io_alembic.c @@ -316,7 +316,7 @@ void WM_OT_alembic_export(wmOperatorType *ot) FILE_TYPE_FOLDER | FILE_TYPE_ALEMBIC, FILE_BLENDER, FILE_SAVE, - WM_FILESEL_FILEPATH, + WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); @@ -694,7 +694,7 @@ void WM_OT_alembic_import(wmOperatorType *ot) FILE_TYPE_FOLDER | FILE_TYPE_ALEMBIC, FILE_BLENDER, FILE_SAVE, - WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, + WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH | WM_FILESEL_SHOW_PROPS, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c index dad84c87fd7..3f51504d6ac 100644 --- a/source/blender/editors/io/io_collada.c +++ b/source/blender/editors/io/io_collada.c @@ -541,7 +541,7 @@ void WM_OT_collada_export(wmOperatorType *ot) FILE_TYPE_FOLDER | FILE_TYPE_COLLADA, FILE_BLENDER, FILE_SAVE, - WM_FILESEL_FILEPATH, + WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); @@ -870,7 +870,7 @@ void WM_OT_collada_import(wmOperatorType *ot) FILE_TYPE_FOLDER | FILE_TYPE_COLLADA, FILE_BLENDER, FILE_OPENFILE, - WM_FILESEL_FILEPATH, + WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); diff --git a/source/blender/editors/mesh/editmesh_extrude_spin.c b/source/blender/editors/mesh/editmesh_extrude_spin.c index f19a988809a..69274f77049 100644 --- a/source/blender/editors/mesh/editmesh_extrude_spin.c +++ b/source/blender/editors/mesh/editmesh_extrude_spin.c @@ -199,7 +199,7 @@ void MESH_OT_spin(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* props */ - RNA_def_int(ot->srna, "steps", 9, 0, 1000000, "Steps", "Steps", 0, 1000); + RNA_def_int(ot->srna, "steps", 12, 0, 1000000, "Steps", "Steps", 0, 1000); prop = RNA_def_boolean(ot->srna, "dupli", 0, "Use Duplicates", ""); RNA_def_property_flag(prop, PROP_SKIP_SAVE); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 452b349cfbf..5c1c6d27ec6 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -603,27 +603,8 @@ static int lightprobe_add_exec(bContext *C, wmOperator *op) copy_v3_fl(ob->scale, radius); probe = (LightProbe *)ob->data; - probe->type = type; - switch (type) { - case LIGHTPROBE_TYPE_GRID: - probe->distinf = 0.3f; - probe->falloff = 1.0f; - probe->clipsta = 0.01f; - break; - case LIGHTPROBE_TYPE_PLANAR: - probe->distinf = 0.1f; - probe->falloff = 0.5f; - probe->clipsta = 0.001f; - ob->empty_drawsize = 0.5f; - break; - case LIGHTPROBE_TYPE_CUBE: - probe->attenuation_type = LIGHTPROBE_SHAPE_ELIPSOID; - break; - default: - BLI_assert(!"LightProbe type not configured."); - break; - } + BKE_lightprobe_type_set(probe, type); DEG_relations_tag_update(CTX_data_main(C)); diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 8012565ba2e..34e1b3b2b4b 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -218,7 +218,7 @@ static int object_hide_view_set_exec(bContext *C, wmOperator *op) /* Hide selected or unselected objects. */ for (Base *base = view_layer->object_bases.first; base; base = base->next) { - if (!(base->flag & BASE_VISIBLE_DEPSGRAPH)) { + if (!(base->flag & BASE_VISIBLE_VIEWLAYER)) { continue; } diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index e8e0569f15e..05fa78aab1c 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -3179,6 +3179,8 @@ static int vertex_group_lock_exec(bContext *C, wmOperator *op) vgroup_lock_all(ob, action); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + return OPERATOR_FINISHED; } diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 29626fb6a8f..cb0be6f56ac 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1141,6 +1141,9 @@ static void region_overlap_fix(ScrArea *sa, ARegion *ar) } } + /* Guard against flags slipping through that would have to be masked out in usages below. */ + BLI_assert(align1 == RGN_ALIGN_ENUM_FROM_MASK(align1)); + /* translate or close */ if (ar1) { if (align1 == RGN_ALIGN_LEFT) { @@ -1244,6 +1247,20 @@ static void region_rect_recursive( alignment = RGN_ALIGN_NONE; } + /* If both the ARegion.sizex/y and the prefsize are 0, the region is tagged as too small, even + * before the layout for dynamic regions is created. #wm_draw_window_offscreen() allows the + * layout to be created despite the RGN_FLAG_TOO_SMALL flag being set. But there may still be + * regions that don't have a separate ARegionType.layout callback. For those, set a default + * prefsize so they can become visible. */ + if ((ar->flag & RGN_FLAG_DYNAMIC_SIZE) && !(ar->type->layout)) { + if ((ar->sizex == 0) && (ar->type->prefsizex == 0)) { + ar->type->prefsizex = AREAMINX; + } + if ((ar->sizey == 0) && (ar->type->prefsizey == 0)) { + ar->type->prefsizey = HEADERY; + } + } + /* prefsize, taking into account DPI */ int prefsizex = UI_DPI_FAC * ((ar->sizex > 1) ? ar->sizex + 0.5f : ar->type->prefsizex); int prefsizey; @@ -1323,7 +1340,7 @@ static void region_rect_recursive( else if (alignment == RGN_ALIGN_TOP || alignment == RGN_ALIGN_BOTTOM) { rcti *winrct = (ar->overlap) ? overlap_remainder : remainder; - if (rct_fits(winrct, 'v', prefsizey) < 0) { + if ((prefsizey == 0) || (rct_fits(winrct, 'v', prefsizey) < 0)) { ar->flag |= RGN_FLAG_TOO_SMALL; } else { @@ -1348,7 +1365,7 @@ static void region_rect_recursive( else if (ELEM(alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) { rcti *winrct = (ar->overlap) ? overlap_remainder : remainder; - if (rct_fits(winrct, 'h', prefsizex) < 0) { + if ((prefsizex == 0) || (rct_fits(winrct, 'h', prefsizex) < 0)) { ar->flag |= RGN_FLAG_TOO_SMALL; } else { @@ -1438,9 +1455,8 @@ static void region_rect_recursive( } /* Fix any negative dimensions. This can happen when a quad split 3d view gets to small. (see - * T72200). BLI_rcti_init() sanitizes, making sure min values are <= max values. */ - BLI_rcti_init( - &ar->winrct, ar->winrct.xmin, ar->winrct.xmax, ar->winrct.ymin, ar->winrct.ymax); + * T72200). */ + BLI_rcti_sanitize(&ar->winrct); quad++; } @@ -1479,11 +1495,16 @@ static void region_rect_recursive( ar->winrct.xmin = ar->winrct.xmax; break; case RGN_ALIGN_LEFT: + ar->winrct.xmax = ar->winrct.xmin; + break; default: /* prevent winrct to be valid */ ar->winrct.xmax = ar->winrct.xmin; break; } + + /* Size on one axis is now 0, the other axis may still be invalid (negative) though. */ + BLI_rcti_sanitize(&ar->winrct); } /* restore prev-split exception */ @@ -1501,6 +1522,8 @@ static void region_rect_recursive( *overlap_remainder = *remainder; } + BLI_assert(BLI_rcti_is_valid(&ar->winrct)); + region_rect_recursive(sa, ar->next, remainder, overlap_remainder, quad); /* Tag for redraw if size changes. */ @@ -1818,8 +1841,10 @@ void ED_region_update_rect(ARegion *ar) } /* externally called for floating regions like menus */ -void ED_region_init(ARegion *ar) +void ED_region_floating_initialize(ARegion *ar) { + BLI_assert(ar->alignment == RGN_ALIGN_FLOAT); + /* refresh can be called before window opened */ region_subwindow(ar); @@ -2321,7 +2346,7 @@ static void ed_panel_draw(const bContext *C, } } - UI_panel_end(block, w, h, open); + UI_panel_end(sa, ar, block, w, h, open); } /** @@ -2565,7 +2590,7 @@ void ED_region_panels_draw(const bContext *C, ARegion *ar) /* scrollers */ const rcti *mask = NULL; rcti mask_buf; - if (ar->runtime.category && (ar->alignment == RGN_ALIGN_RIGHT)) { + if (ar->runtime.category && (RGN_ALIGN_ENUM_FROM_MASK(ar->alignment) == RGN_ALIGN_RIGHT)) { UI_view2d_mask_from_win(v2d, &mask_buf); mask_buf.xmax -= UI_PANEL_CATEGORY_MARGIN_WIDTH; mask = &mask_buf; @@ -2902,7 +2927,7 @@ void ED_region_info_draw(ARegion *ar, float fill_color[4], const bool full_redraw) { - ED_region_info_draw_multiline(ar, (const char * [2]){text, NULL}, fill_color, full_redraw); + ED_region_info_draw_multiline(ar, (const char *[2]){text, NULL}, fill_color, full_redraw); } #define MAX_METADATA_STR 1024 @@ -3327,7 +3352,9 @@ static void region_visible_rect_calc(ARegion *ar, rcti *rect) for (; arn; arn = arn->next) { if (ar != arn && arn->overlap) { if (BLI_rcti_isect(rect, &arn->winrct, NULL)) { - if (ELEM(arn->alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) { + int alignment = RGN_ALIGN_ENUM_FROM_MASK(arn->alignment); + + if (ELEM(alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) { /* Overlap left, also check 1 pixel offset (2 regions on one side). */ if (ABS(rect->xmin - arn->winrct.xmin) < 2) { rect->xmin = arn->winrct.xmax; @@ -3338,7 +3365,7 @@ static void region_visible_rect_calc(ARegion *ar, rcti *rect) rect->xmax = arn->winrct.xmin; } } - else if (ELEM(arn->alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) { + else if (ELEM(alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) { /* Same logic as above for vertical regions. */ if (ABS(rect->ymin - arn->winrct.ymin) < 2) { rect->ymin = arn->winrct.ymax; @@ -3347,7 +3374,7 @@ static void region_visible_rect_calc(ARegion *ar, rcti *rect) rect->ymax = arn->winrct.ymin; } } - else if (arn->alignment == RGN_ALIGN_FLOAT) { + else if (alignment == RGN_ALIGN_FLOAT) { /* Skip floating. */ } else { diff --git a/source/blender/editors/screen/area_query.c b/source/blender/editors/screen/area_query.c index 46559efc614..942050aaffd 100644 --- a/source/blender/editors/screen/area_query.c +++ b/source/blender/editors/screen/area_query.c @@ -67,10 +67,12 @@ bool ED_region_panel_category_gutter_calc_rect(const ARegion *ar, rcti *r_ar_gut if (UI_panel_category_is_visible(ar)) { const int category_tabs_width = round_fl_to_int(UI_view2d_scale_get_x(&ar->v2d) * UI_PANEL_CATEGORY_MARGIN_WIDTH); - if (ar->alignment == RGN_ALIGN_LEFT) { + const int alignment = RGN_ALIGN_ENUM_FROM_MASK(ar->alignment); + + if (alignment == RGN_ALIGN_LEFT) { r_ar_gutter->xmax = r_ar_gutter->xmin + category_tabs_width; } - else if (ar->alignment == RGN_ALIGN_RIGHT) { + else if (alignment == RGN_ALIGN_RIGHT) { r_ar_gutter->xmin = r_ar_gutter->xmax - category_tabs_width; } else { @@ -141,14 +143,16 @@ bool ED_region_contains_xy(const ARegion *ar, const int event_xy[2]) else { /* Side-bar & any other kind of overlapping region. */ + const int alignment = RGN_ALIGN_ENUM_FROM_MASK(ar->alignment); + /* Check alignment to avoid region tabs being clipped out * by only clipping a single axis for aligned regions. */ - if (ELEM(ar->alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) { + if (ELEM(alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) { if (!ED_region_overlap_isect_x_with_margin(ar, event_xy[0], overlap_margin)) { return false; } } - else if (ELEM(ar->alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) { + else if (ELEM(alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) { if (ED_region_panel_category_gutter_isect_xy(ar, event_xy)) { /* pass */ } diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index dd09def2df6..0e1ae03fd20 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -4114,8 +4114,9 @@ void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UN { ScrArea *sa = CTX_wm_area(C); ARegion *ar = CTX_wm_region(C); - const char *but_flip_str = (ar->alignment == RGN_ALIGN_TOP) ? IFACE_("Flip to Bottom") : - IFACE_("Flip to Top"); + const char *but_flip_str = (RGN_ALIGN_ENUM_FROM_MASK(ar->alignment) == RGN_ALIGN_TOP) ? + IFACE_("Flip to Bottom") : + IFACE_("Flip to Top"); { PointerRNA ptr; RNA_pointer_create((ID *)CTX_wm_screen(C), &RNA_Space, sa->spacedata.first, &ptr); @@ -4160,8 +4161,9 @@ void ED_screens_footer_tools_menu_create(bContext *C, uiLayout *layout, void *UN { ScrArea *sa = CTX_wm_area(C); ARegion *ar = CTX_wm_region(C); - const char *but_flip_str = (ar->alignment == RGN_ALIGN_TOP) ? IFACE_("Flip to Bottom") : - IFACE_("Flip to Top"); + const char *but_flip_str = (RGN_ALIGN_ENUM_FROM_MASK(ar->alignment) == RGN_ALIGN_TOP) ? + IFACE_("Flip to Bottom") : + IFACE_("Flip to Top"); { PointerRNA ptr; RNA_pointer_create((ID *)CTX_wm_screen(C), &RNA_Space, sa->spacedata.first, &ptr); @@ -4186,8 +4188,9 @@ void ED_screens_footer_tools_menu_create(bContext *C, uiLayout *layout, void *UN void ED_screens_navigation_bar_tools_menu_create(bContext *C, uiLayout *layout, void *UNUSED(arg)) { const ARegion *ar = CTX_wm_region(C); - const char *but_flip_str = (ar->alignment == RGN_ALIGN_LEFT) ? IFACE_("Flip to Right") : - IFACE_("Flip to Left"); + const char *but_flip_str = (RGN_ALIGN_ENUM_FROM_MASK(ar->alignment) == RGN_ALIGN_LEFT) ? + IFACE_("Flip to Right") : + IFACE_("Flip to Left"); /* default is WM_OP_INVOKE_REGION_WIN, which we don't want here. */ uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT); @@ -4840,7 +4843,10 @@ static int userpref_show_invoke(bContext *C, wmOperator *op, const wmEvent *even * So hiding in the temp window makes sense. */ ScrArea *area = CTX_wm_area(C); ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_HEADER); + region->flag |= RGN_FLAG_HIDDEN; + ED_region_visibility_change_update(C, area, region); + return OPERATOR_FINISHED; } else { diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c index 5ceaefd6309..5fdabea62c1 100644 --- a/source/blender/editors/space_action/action_data.c +++ b/source/blender/editors/space_action/action_data.c @@ -211,12 +211,13 @@ static int action_new_exec(bContext *C, wmOperator *UNUSED(op)) PointerRNA ptr, idptr; PropertyRNA *prop; + bAction *oldact = NULL; + AnimData *adt = NULL; /* hook into UI */ UI_context_active_but_prop_get_templateID(C, &ptr, &prop); if (prop) { - bAction *action = NULL, *oldact = NULL; - AnimData *adt = NULL; + /* The operator was called from a button. */ PointerRNA oldptr; oldptr = RNA_property_pointer_get(&ptr, prop); @@ -229,6 +230,13 @@ static int action_new_exec(bContext *C, wmOperator *UNUSED(op)) else if (ptr.type == &RNA_SpaceDopeSheetEditor) { adt = ED_actedit_animdata_from_context(C); } + } + else { + adt = ED_actedit_animdata_from_context(C); + oldact = adt->action; + } + { + bAction *action = NULL; /* Perform stashing operation - But only if there is an action */ if (adt && oldact) { @@ -257,12 +265,14 @@ static int action_new_exec(bContext *C, wmOperator *UNUSED(op)) /* create action */ action = action_create_new(C, oldact); - /* set this new action - * NOTE: we can't use actedit_change_action, as this function is also called from the NLA - */ - RNA_id_pointer_create(&action->id, &idptr); - RNA_property_pointer_set(&ptr, prop, idptr, NULL); - RNA_property_update(C, &ptr, prop); + if (prop) { + /* set this new action + * NOTE: we can't use actedit_change_action, as this function is also called from the NLA + */ + RNA_id_pointer_create(&action->id, &idptr); + RNA_property_pointer_set(&ptr, prop, idptr, NULL); + RNA_property_update(C, &ptr, prop); + } } /* set notifier that keyframes have changed */ diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 5eac041b217..6e44992ea6e 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -488,9 +488,9 @@ static int actkeys_view_frame_exec(bContext *C, wmOperator *op) void ACTION_OT_view_frame(wmOperatorType *ot) { /* identifiers */ - ot->name = "View Frame"; + ot->name = "Go to Current Frame"; ot->idname = "ACTION_OT_view_frame"; - ot->description = "Reset viewable area to show range around current frame"; + ot->description = "Move the view to the playhead"; /* api callbacks */ ot->exec = actkeys_view_frame_exec; diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index 16305a9b17b..04c939ec41b 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -1083,11 +1083,7 @@ static void graph_region_draw(const bContext *C, ARegion *ar) /* scale indicators */ { rcti rect; - BLI_rcti_init(&rect, - 0, - 15 * UI_DPI_FAC, - 15 * UI_DPI_FAC, - UI_DPI_FAC * ar->sizey - UI_TIME_SCRUB_MARGIN_Y); + BLI_rcti_init(&rect, 0, 15 * UI_DPI_FAC, 15 * UI_DPI_FAC, ar->winy - UI_TIME_SCRUB_MARGIN_Y); UI_view2d_draw_scale_y__values(ar, v2d, &rect, TH_TEXT); } } diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index 6c95e74e834..0a3663372be 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -156,7 +156,7 @@ static void graph_panel_properties(const bContext *C, Panel *pa) FCurve *fcu; PointerRNA fcu_ptr; uiLayout *layout = pa->layout; - uiLayout *col, *row, *sub; + uiLayout *col; char name[256]; int icon = 0; @@ -193,27 +193,26 @@ static void graph_panel_properties(const bContext *C, Panel *pa) } uiItemL(col, name, icon); + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + /* RNA-Path Editing - only really should be enabled when things aren't working */ - col = uiLayoutColumn(layout, true); + col = uiLayoutColumn(layout, false); uiLayoutSetEnabled(col, (fcu->flag & FCURVE_DISABLED) != 0); uiItemR(col, &fcu_ptr, "data_path", 0, "", ICON_RNA); uiItemR(col, &fcu_ptr, "array_index", 0, NULL, ICON_NONE); /* color settings */ col = uiLayoutColumn(layout, true); - uiItemL(col, IFACE_("Display Color:"), ICON_NONE); + uiItemR(col, &fcu_ptr, "color_mode", 0, "Display Color", ICON_NONE); - row = uiLayoutRow(col, true); - uiItemR(row, &fcu_ptr, "color_mode", 0, "", ICON_NONE); - - sub = uiLayoutRow(row, true); - uiLayoutSetEnabled(sub, (fcu->color_mode == FCURVE_COLOR_CUSTOM)); - uiItemR(sub, &fcu_ptr, "color", 0, "", ICON_NONE); + if (fcu->color_mode == FCURVE_COLOR_CUSTOM) { + uiItemR(col, &fcu_ptr, "color", 0, "Color", ICON_NONE); + } /* smoothing setting */ col = uiLayoutColumn(layout, true); - uiItemL(col, IFACE_("Auto Handle Smoothing:"), ICON_NONE); - uiItemR(col, &fcu_ptr, "auto_smoothing", 0, "", ICON_NONE); + uiItemR(col, &fcu_ptr, "auto_smoothing", 0, "Handle Smoothing", ICON_NONE); MEM_freeN(ale); } @@ -339,6 +338,9 @@ static void graph_panel_key_properties(const bContext *C, Panel *pa) BezTriple *bezt, *prevbezt; uiLayout *layout = pa->layout; + const ARegion *ar = CTX_wm_region(C); + /* Just a width big enough so buttons use entire layout width (will be clamped by it then). */ + const int but_max_width = ar->winx; uiLayout *col; uiBlock *block; @@ -348,6 +350,8 @@ static void graph_panel_key_properties(const bContext *C, Panel *pa) block = uiLayoutGetBlock(layout); /* UI_block_func_handle_set(block, do_graph_region_buttons, NULL); */ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); /* only show this info if there are keyframes to edit */ if (get_active_fcurve_keyframe_edit(fcu, &bezt, &prevbezt)) { @@ -404,57 +408,60 @@ static void graph_panel_key_properties(const bContext *C, Panel *pa) col = uiLayoutColumn(layout, true); /* keyframe itself */ { - uiItemL(col, IFACE_("Key:"), ICON_NONE); + uiItemL_respect_property_split(col, IFACE_("Key Value"), ICON_NONE); but = uiDefButR(block, UI_BTYPE_NUM, B_REDR, - IFACE_("Frame:"), + "", 0, 0, - UI_UNIT_X, + but_max_width, UI_UNIT_Y, &bezt_ptr, "co", - 0, + 1, 0, 0, -1, -1, NULL); UI_but_func_set(but, graphedit_activekey_update_cb, fcu, bezt); + UI_but_unit_type_set(but, unit); + uiItemL_respect_property_split(col, IFACE_("Frame"), ICON_NONE); but = uiDefButR(block, UI_BTYPE_NUM, B_REDR, - IFACE_("Value:"), + "", 0, 0, - UI_UNIT_X, + but_max_width, UI_UNIT_Y, &bezt_ptr, "co", - 1, + 0, 0, 0, -1, -1, NULL); UI_but_func_set(but, graphedit_activekey_update_cb, fcu, bezt); - UI_but_unit_type_set(but, unit); } /* previous handle - only if previous was Bezier interpolation */ if ((prevbezt) && (prevbezt->ipo == BEZT_IPO_BEZ)) { - uiItemL(col, IFACE_("Left Handle:"), ICON_NONE); + col = uiLayoutColumn(layout, true); + + uiItemL_respect_property_split(col, IFACE_("Left Handle X"), ICON_NONE); but = uiDefButR(block, UI_BTYPE_NUM, B_REDR, - "X:", + "", 0, 0, - UI_UNIT_X, + but_max_width, UI_UNIT_Y, &bezt_ptr, "handle_left", @@ -466,13 +473,14 @@ static void graph_panel_key_properties(const bContext *C, Panel *pa) NULL); UI_but_func_set(but, graphedit_activekey_left_handle_coord_cb, fcu, bezt); + uiItemL_respect_property_split(col, IFACE_("Y"), ICON_NONE); but = uiDefButR(block, UI_BTYPE_NUM, B_REDR, - "Y:", + "", 0, 0, - UI_UNIT_X, + but_max_width, UI_UNIT_Y, &bezt_ptr, "handle_left", @@ -485,14 +493,14 @@ static void graph_panel_key_properties(const bContext *C, Panel *pa) UI_but_func_set(but, graphedit_activekey_left_handle_coord_cb, fcu, bezt); UI_but_unit_type_set(but, unit); - /* XXX: with label? */ + uiItemL_respect_property_split(col, IFACE_("Type"), ICON_NONE); but = uiDefButR(block, UI_BTYPE_MENU, B_REDR, NULL, 0, 0, - UI_UNIT_X, + but_max_width, UI_UNIT_Y, &bezt_ptr, "handle_left_type", @@ -508,15 +516,16 @@ static void graph_panel_key_properties(const bContext *C, Panel *pa) /* next handle - only if current is Bezier interpolation */ if (bezt->ipo == BEZT_IPO_BEZ) { /* NOTE: special update callbacks are needed on the coords here due to T39911 */ - uiItemL(col, IFACE_("Right Handle:"), ICON_NONE); + col = uiLayoutColumn(layout, true); + uiItemL_respect_property_split(col, IFACE_("Right Handle X"), ICON_NONE); but = uiDefButR(block, UI_BTYPE_NUM, B_REDR, - "X:", + "", 0, 0, - UI_UNIT_X, + but_max_width, UI_UNIT_Y, &bezt_ptr, "handle_right", @@ -528,13 +537,14 @@ static void graph_panel_key_properties(const bContext *C, Panel *pa) NULL); UI_but_func_set(but, graphedit_activekey_right_handle_coord_cb, fcu, bezt); + uiItemL_respect_property_split(col, IFACE_("Y"), ICON_NONE); but = uiDefButR(block, UI_BTYPE_NUM, B_REDR, - "Y:", + "", 0, 0, - UI_UNIT_X, + but_max_width, UI_UNIT_Y, &bezt_ptr, "handle_right", @@ -547,14 +557,14 @@ static void graph_panel_key_properties(const bContext *C, Panel *pa) UI_but_func_set(but, graphedit_activekey_right_handle_coord_cb, fcu, bezt); UI_but_unit_type_set(but, unit); - /* XXX: with label? */ + uiItemL_respect_property_split(col, IFACE_("Type"), ICON_NONE); but = uiDefButR(block, UI_BTYPE_MENU, B_REDR, NULL, 0, 0, - UI_UNIT_X, + but_max_width, UI_UNIT_Y, &bezt_ptr, "handle_right_type", diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 03df93e4c8a..59a1102f7d6 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -292,13 +292,12 @@ static int graphkeys_viewall(bContext *C, /* Give some more space at the borders. */ BLI_rctf_scale(&cur_new, 1.1f); - /* Take regions into account, that could block the view. */ + /* Take regions into account, that could block the view. Marker region is supposed to be larger + * than the scrollbar, so priorize it.*/ float pad_top = UI_TIME_SCRUB_MARGIN_Y; - float pad_bottom = 0; - if (!BLI_listbase_is_empty(ED_context_get_markers(C))) { - pad_bottom = UI_MARKER_MARGIN_Y; - } - BLI_rctf_pad_y(&cur_new, ac.ar->sizey * UI_DPI_FAC, pad_bottom, pad_top); + float pad_bottom = BLI_listbase_is_empty(ED_context_get_markers(C)) ? V2D_SCROLL_HANDLE_HEIGHT : + UI_MARKER_MARGIN_Y; + BLI_rctf_pad_y(&cur_new, ac.ar->winy, pad_bottom, pad_top); UI_view2d_smooth_view(C, ac.ar, &cur_new, smooth_viewtx); return OPERATOR_FINISHED; @@ -384,9 +383,9 @@ static int graphkeys_view_frame_exec(bContext *C, wmOperator *op) void GRAPH_OT_view_frame(wmOperatorType *ot) { /* identifiers */ - ot->name = "View Frame"; + ot->name = "Go to Current Frame"; ot->idname = "GRAPH_OT_view_frame"; - ot->description = "Reset viewable area to show range around current frame"; + ot->description = "Move the view to the playhead"; /* api callbacks */ ot->exec = graphkeys_view_frame_exec; diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 7bc907bb3db..d01e4112fd0 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -326,11 +326,7 @@ static void graph_main_region_draw(const bContext *C, ARegion *ar) /* scale numbers */ { rcti rect; - BLI_rcti_init(&rect, - 0, - 15 * UI_DPI_FAC, - 15 * UI_DPI_FAC, - UI_DPI_FAC * ar->sizey - UI_TIME_SCRUB_MARGIN_Y); + BLI_rcti_init(&rect, 0, 15 * UI_DPI_FAC, 15 * UI_DPI_FAC, ar->winy - UI_TIME_SCRUB_MARGIN_Y); UI_view2d_draw_scale_y__values(ar, v2d, &rect, TH_SCROLL_TEXT); } } diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index 04a20efe887..0f170b1f530 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -565,9 +565,9 @@ static int nlaedit_viewframe_exec(bContext *C, wmOperator *op) void NLA_OT_view_frame(wmOperatorType *ot) { /* identifiers */ - ot->name = "View Frame"; + ot->name = "Go to Current Frame"; ot->idname = "NLA_OT_view_frame"; - ot->description = "Reset viewable area to show range around current frame"; + ot->description = "Move the view to the playhead"; /* api callbacks */ ot->exec = nlaedit_viewframe_exec; diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 589375530f5..9e8fa6475a0 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -96,8 +96,8 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag) INT_MAX, "Start Frame", "Start frame of the sequence strip", - INT_MIN, - INT_MAX); + -MAXFRAME, + MAXFRAME); } if (flag & SEQPROP_ENDFRAME) { @@ -109,8 +109,8 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag) INT_MAX, "End Frame", "End frame for the color strip", - INT_MIN, - INT_MAX); + -MAXFRAME, + MAXFRAME); } RNA_def_int( @@ -312,6 +312,26 @@ static void sequencer_add_apply_replace_sel(bContext *C, wmOperator *op, Sequenc } } +static bool seq_effect_add_properties_poll(const bContext *UNUSED(C), + wmOperator *op, + const PropertyRNA *prop) +{ + const char *prop_id = RNA_property_identifier(prop); + int type = RNA_enum_get(op->ptr, "type"); + + /* Hide start/end frames for effect strips that are locked to their parents' location. */ + if (BKE_sequence_effect_get_num_inputs(type) != 0) { + if ((STREQ(prop_id, "frame_start")) || (STREQ(prop_id, "frame_end"))) { + return false; + } + } + if ((type != SEQ_TYPE_COLOR) && (STREQ(prop_id, "color"))) { + return false; + } + + return true; +} + /* add scene operator */ static int sequencer_add_scene_strip_exec(bContext *C, wmOperator *op) { @@ -1066,8 +1086,8 @@ static int sequencer_add_effect_strip_exec(bContext *C, wmOperator *op) /* If seq1 is NULL and no error was raised it means the seq is standalone * (like color strips) and we need to check its start and end frames are valid */ if (seq1 == NULL && end_frame <= start_frame) { - BKE_report(op->reports, RPT_ERROR, "Start and end frame are not set"); - return OPERATOR_CANCELLED; + end_frame = start_frame + 1; + RNA_int_set(op->ptr, "frame_end", end_frame); } seq = BKE_sequence_alloc(ed->seqbasep, start_frame, channel, type); @@ -1161,6 +1181,8 @@ static int sequencer_add_effect_strip_invoke(bContext *C, void SEQUENCER_OT_effect_strip_add(struct wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Add Effect Strip"; ot->idname = "SEQUENCER_OT_effect_strip_add"; @@ -1171,25 +1193,27 @@ void SEQUENCER_OT_effect_strip_add(struct wmOperatorType *ot) ot->exec = sequencer_add_effect_strip_exec; ot->poll = ED_operator_sequencer_active_editable; + ot->poll_property = seq_effect_add_properties_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_ENDFRAME); RNA_def_enum(ot->srna, "type", sequencer_prop_effect_types, SEQ_TYPE_CROSS, "Type", "Sequencer effect type"); - RNA_def_float_vector(ot->srna, - "color", - 3, - NULL, - 0.0f, - 1.0f, - "Color", - "Initialize the strip with this color (only used when type='COLOR')", - 0.0f, - 1.0f); + sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_ENDFRAME); + prop = RNA_def_float_color(ot->srna, + "color", + 3, + NULL, + 0.0f, + 1.0f, + "Color", + "Initialize the strip with this color (only used when type='COLOR')", + 0.0f, + 1.0f); + RNA_def_property_subtype(prop, PROP_COLOR_GAMMA); } diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 20fba7aa919..bef4a7cdd22 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -2113,11 +2113,7 @@ void draw_timeline_seq(const bContext *C, ARegion *ar) /* channel numbers */ { rcti rect; - BLI_rcti_init(&rect, - 0, - 15 * UI_DPI_FAC, - 15 * UI_DPI_FAC, - UI_DPI_FAC * ar->sizey - UI_TIME_SCRUB_MARGIN_Y); + BLI_rcti_init(&rect, 0, 15 * UI_DPI_FAC, 15 * UI_DPI_FAC, ar->winy - UI_TIME_SCRUB_MARGIN_Y); UI_view2d_draw_scale_y__block(ar, v2d, &rect, TH_SCROLL_TEXT); } } diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 57a0d63c35e..a1177454acd 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -103,6 +103,7 @@ EnumPropertyItem prop_side_types[] = { {SEQ_SIDE_LEFT, "LEFT", 0, "Left", ""}, {SEQ_SIDE_RIGHT, "RIGHT", 0, "Right", ""}, {SEQ_SIDE_BOTH, "BOTH", 0, "Both", ""}, + {SEQ_SIDE_NO_CHANGE, "NO_CHANGE", 0, "No change", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -958,6 +959,8 @@ static bool cut_seq_list(Main *bmain, Scene *scene, ListBase *slist, int cutframe, + int channel, + bool use_cursor_position, Sequence *(*cut_seq)(Main *bmain, Scene *, Sequence *, ListBase *, int)) { Sequence *seq, *seq_next_iter; @@ -968,8 +971,8 @@ static bool cut_seq_list(Main *bmain, while (seq && seq != seq_first_new) { seq_next_iter = seq->next; /* we need this because we may remove seq */ seq->tmp = NULL; - if (seq->flag & SELECT) { - if (cutframe > seq->startdisp && cutframe < seq->enddisp) { + if (use_cursor_position) { + if (seq->machine == channel && seq->startdisp < cutframe && seq->enddisp > cutframe) { Sequence *seqn = cut_seq(bmain, scene, seq, slist, cutframe); if (seqn) { if (seq_first_new == NULL) { @@ -977,16 +980,28 @@ static bool cut_seq_list(Main *bmain, } } } - else if (seq->enddisp <= cutframe) { - /* do nothing */ - } - else if (seq->startdisp >= cutframe) { - /* move to tail */ - BLI_remlink(slist, seq); - BLI_addtail(slist, seq); + } + else { + if (seq->flag & SELECT) { + if (cutframe > seq->startdisp && cutframe < seq->enddisp) { + Sequence *seqn = cut_seq(bmain, scene, seq, slist, cutframe); + if (seqn) { + if (seq_first_new == NULL) { + seq_first_new = seqn; + } + } + } + else if (seq->enddisp <= cutframe) { + /* do nothing */ + } + else if (seq->startdisp >= cutframe) { + /* move to tail */ + BLI_remlink(slist, seq); + BLI_addtail(slist, seq); - if (seq_first_new == NULL) { - seq_first_new = seq; + if (seq_first_new == NULL) { + seq_first_new = seq; + } } } } @@ -2154,40 +2169,64 @@ static int sequencer_cut_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); Editing *ed = BKE_sequencer_editing_get(scene, false); - int cut_side, cut_hard, cut_frame; - - bool changed; + int cut_side, cut_hard, cut_frame, cut_channel; + bool changed, use_cursor_position, ignore_selection; + bool seq_selected = false; cut_frame = RNA_int_get(op->ptr, "frame"); + cut_channel = RNA_int_get(op->ptr, "channel"); + use_cursor_position = RNA_boolean_get(op->ptr, "use_cursor_position"); cut_hard = RNA_enum_get(op->ptr, "type"); cut_side = RNA_enum_get(op->ptr, "side"); + ignore_selection = RNA_boolean_get(op->ptr, "ignore_selection"); if (cut_hard == SEQ_CUT_HARD) { - changed = cut_seq_list(bmain, scene, ed->seqbasep, cut_frame, cut_seq_hard); + changed = cut_seq_list( + bmain, scene, ed->seqbasep, cut_frame, cut_channel, use_cursor_position, cut_seq_hard); } else { - changed = cut_seq_list(bmain, scene, ed->seqbasep, cut_frame, cut_seq_soft); + changed = cut_seq_list( + bmain, scene, ed->seqbasep, cut_frame, cut_channel, use_cursor_position, cut_seq_soft); } if (changed) { /* got new strips ? */ Sequence *seq; - if (cut_side != SEQ_SIDE_BOTH) { - SEQP_BEGIN (ed, seq) { - if (cut_side == SEQ_SIDE_LEFT) { - if (seq->startdisp >= cut_frame) { - seq->flag &= ~SEQ_ALLSEL; + if (ignore_selection) { + if (use_cursor_position) { + SEQP_BEGIN (ed, seq) { + if (seq->enddisp == cut_frame && seq->machine == cut_channel) { + seq_selected = seq->flag & SEQ_ALLSEL; } } - else { - if (seq->enddisp <= cut_frame) { - seq->flag &= ~SEQ_ALLSEL; + SEQ_END; + if (!seq_selected) { + SEQP_BEGIN (ed, seq) { + if (seq->startdisp == cut_frame && seq->machine == cut_channel) { + seq->flag &= ~SEQ_ALLSEL; + } } + SEQ_END; } } - SEQ_END; } - + else { + if (cut_side != SEQ_SIDE_BOTH) { + SEQP_BEGIN (ed, seq) { + if (cut_side == SEQ_SIDE_LEFT) { + if (seq->startdisp >= cut_frame) { + seq->flag &= ~SEQ_ALLSEL; + } + } + else { + if (seq->enddisp <= cut_frame) { + seq->flag &= ~SEQ_ALLSEL; + } + } + } + SEQ_END; + } + } SEQP_BEGIN (ed, seq) { if (seq->seq1 || seq->seq2 || seq->seq3) { BKE_sequence_calc(scene, seq); @@ -2204,7 +2243,8 @@ static int sequencer_cut_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } else { - return OPERATOR_CANCELLED; + /* Passthrough to selection if used as tool. */ + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } } @@ -2224,7 +2264,17 @@ static int sequencer_cut_invoke(bContext *C, wmOperator *op, const wmEvent *even cut_side = SEQ_SIDE_BOTH; } } - RNA_int_set(op->ptr, "frame", cut_frame); + + float mouseloc[2]; + UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &mouseloc[0], &mouseloc[1]); + + if (RNA_boolean_get(op->ptr, "use_cursor_position")) { + RNA_int_set(op->ptr, "frame", mouseloc[0]); + } + else { + RNA_int_set(op->ptr, "frame", cut_frame); + } + RNA_int_set(op->ptr, "channel", mouseloc[1]); RNA_enum_set(op->ptr, "side", cut_side); /*RNA_enum_set(op->ptr, "type", cut_hard); */ /*This type is set from the key shortcut */ return sequencer_cut_exec(C, op); @@ -2255,19 +2305,43 @@ void SEQUENCER_OT_cut(struct wmOperatorType *ot) "Frame where selected strips will be cut", INT_MIN, INT_MAX); + RNA_def_int(ot->srna, + "channel", + 0, + INT_MIN, + INT_MAX, + "Channel", + "Channel in which strip will be cut", + INT_MIN, + INT_MAX); RNA_def_enum(ot->srna, "type", prop_cut_types, SEQ_CUT_SOFT, "Type", "The type of cut operation to perform on strips"); + RNA_def_boolean(ot->srna, + "use_cursor_position", + 0, + "Use Cursor Position", + "Cut at position of the cursor instead of playhead"); prop = RNA_def_enum(ot->srna, "side", prop_side_types, SEQ_SIDE_MOUSE, "Side", "The side that remains selected after cutting"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + prop = RNA_def_boolean( + ot->srna, + "ignore_selection", + false, + "Ignore Selection", + "Make cut event if strip is not selected preserving selection state after cut"); + + RNA_def_property_flag(prop, PROP_HIDDEN); } #undef SEQ_SIDE_MOUSE @@ -2326,9 +2400,6 @@ void SEQUENCER_OT_duplicate(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* to give to transform */ - RNA_def_enum(ot->srna, "mode", rna_enum_transform_mode_types, TFM_TRANSLATION, "Mode", ""); } /* delete operator */ @@ -3153,7 +3224,7 @@ void SEQUENCER_OT_strip_jump(wmOperatorType *ot) ot->poll = sequencer_strip_jump_poll; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_UNDO; /* properties */ RNA_def_boolean(ot->srna, "next", true, "Next Strip", ""); @@ -4246,5 +4317,5 @@ void SEQUENCER_OT_set_range_to_strips(struct wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; prop = RNA_def_boolean(ot->srna, "preview", false, "Preview", "Set the preview range instead"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); } diff --git a/source/blender/editors/space_sequencer/sequencer_modifier.c b/source/blender/editors/space_sequencer/sequencer_modifier.c index f262c6518aa..b90dc5e10ff 100644 --- a/source/blender/editors/space_sequencer/sequencer_modifier.c +++ b/source/blender/editors/space_sequencer/sequencer_modifier.c @@ -123,6 +123,8 @@ static int strip_modifier_remove_exec(bContext *C, wmOperator *op) void SEQUENCER_OT_strip_modifier_remove(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Remove Strip Modifier"; ot->idname = "SEQUENCER_OT_strip_modifier_remove"; @@ -136,7 +138,8 @@ void SEQUENCER_OT_strip_modifier_remove(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_string(ot->srna, "name", "Name", MAX_NAME, "Name", "Name of modifier to remove"); + prop = RNA_def_string(ot->srna, "name", "Name", MAX_NAME, "Name", "Name of modifier to remove"); + RNA_def_property_flag(prop, PROP_HIDDEN); } /*********************** Move operator *************************/ @@ -183,6 +186,8 @@ static int strip_modifier_move_exec(bContext *C, wmOperator *op) void SEQUENCER_OT_strip_modifier_move(wmOperatorType *ot) { + PropertyRNA *prop; + static const EnumPropertyItem direction_items[] = { {SEQ_MODIFIER_MOVE_UP, "UP", 0, "Up", "Move modifier up in the stack"}, {SEQ_MODIFIER_MOVE_DOWN, "DOWN", 0, "Down", "Move modifier down in the stack"}, @@ -202,8 +207,10 @@ void SEQUENCER_OT_strip_modifier_move(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_string(ot->srna, "name", "Name", MAX_NAME, "Name", "Name of modifier to remove"); - RNA_def_enum(ot->srna, "direction", direction_items, SEQ_MODIFIER_MOVE_UP, "Type", ""); + prop = RNA_def_string(ot->srna, "name", "Name", MAX_NAME, "Name", "Name of modifier to remove"); + RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_enum(ot->srna, "direction", direction_items, SEQ_MODIFIER_MOVE_UP, "Type", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); } /*********************** Copy to selected operator *************************/ diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index ea93ef2e040..a5bb66ca65f 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -312,7 +312,7 @@ void SEQUENCER_OT_select_all(struct wmOperatorType *ot) ot->poll = sequencer_edit_poll; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_UNDO; WM_operator_properties_select_all(ot); } @@ -353,7 +353,7 @@ void SEQUENCER_OT_select_inverse(struct wmOperatorType *ot) ot->poll = sequencer_edit_poll; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_UNDO; } static int sequencer_select_exec(bContext *C, wmOperator *op) @@ -647,7 +647,7 @@ void SEQUENCER_OT_select(wmOperatorType *ot) ot->poll = ED_operator_sequencer_active; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_UNDO; /* properties */ WM_operator_properties_generic_select(ot); @@ -1086,7 +1086,7 @@ void SEQUENCER_OT_select_box(wmOperatorType *ot) ot->poll = ED_operator_sequencer_active; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_UNDO; /* properties */ WM_operator_properties_gesture_box(ot); diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 6e1b9d62f0e..81783f9105c 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -110,6 +110,14 @@ static SpaceLink *sequencer_new(const ScrArea *UNUSED(sa), const Scene *scene) ar->alignment = RGN_ALIGN_RIGHT; ar->flag = RGN_FLAG_HIDDEN; + /* toolbar */ + ar = MEM_callocN(sizeof(ARegion), "tools for sequencer"); + + BLI_addtail(&sseq->regionbase, ar); + ar->regiontype = RGN_TYPE_TOOLS; + ar->alignment = RGN_ALIGN_LEFT; + ar->flag = RGN_FLAG_HIDDEN; + /* preview region */ /* NOTE: if you change values here, also change them in sequencer_init_preview_region */ ar = MEM_callocN(sizeof(ARegion), "preview region for sequencer"); @@ -618,6 +626,23 @@ static void sequencer_header_region_draw(const bContext *C, ARegion *ar) ED_region_header(C, ar); } +/* *********************** toolbar region ************************ */ +/* add handlers, stuff you only do once or on area/region changes */ +static void sequencer_tools_region_init(wmWindowManager *wm, ARegion *ar) +{ + wmKeyMap *keymap; + + ar->v2d.scroll = V2D_SCROLL_RIGHT | V2D_SCROLL_VERTICAL_HIDE; + ED_region_panels_init(wm, ar); + + keymap = WM_keymap_ensure(wm->defaultconf, "SequencerCommon", SPACE_SEQ, 0); + WM_event_add_keymap_handler_v2d_mask(&ar->handlers, keymap); +} + +static void sequencer_tools_region_draw(const bContext *C, ARegion *ar) +{ + ED_region_panels(C, ar); +} /* *********************** preview region ************************ */ static void sequencer_preview_region_init(wmWindowManager *wm, ARegion *ar) { @@ -832,7 +857,7 @@ void ED_spacetype_sequencer(void) art->draw = sequencer_main_region_draw; art->listener = sequencer_main_region_listener; art->message_subscribe = sequencer_main_region_message_subscribe; - art->keymapflag = ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_ANIMATION; + art->keymapflag = ED_KEYMAP_TOOL | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_ANIMATION; BLI_addhead(&st->regiontypes, art); @@ -842,7 +867,8 @@ void ED_spacetype_sequencer(void) art->init = sequencer_preview_region_init; art->draw = sequencer_preview_region_draw; art->listener = sequencer_preview_region_listener; - art->keymapflag = ED_KEYMAP_GIZMO | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_GPENCIL; + art->keymapflag = ED_KEYMAP_TOOL | ED_KEYMAP_GIZMO | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | + ED_KEYMAP_GPENCIL; BLI_addhead(&st->regiontypes, art); /* regions: listview/buttons */ @@ -850,12 +876,35 @@ void ED_spacetype_sequencer(void) art->regionid = RGN_TYPE_UI; art->prefsizex = UI_SIDEBAR_PANEL_WIDTH * 1.3f; art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_FRAMES; + art->message_subscribe = ED_area_do_mgs_subscribe_for_tool_ui; art->listener = sequencer_buttons_region_listener; art->init = sequencer_buttons_region_init; art->draw = sequencer_buttons_region_draw; BLI_addhead(&st->regiontypes, art); sequencer_buttons_register(art); + /* regions: tool(bar) */ + art = MEM_callocN(sizeof(ARegionType), "spacetype sequencer tools region"); + art->regionid = RGN_TYPE_TOOLS; + art->prefsizex = 58; /* XXX */ + art->prefsizey = 50; /* XXX */ + art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_FRAMES; + art->message_subscribe = ED_region_generic_tools_region_message_subscribe; + art->snap_size = ED_region_generic_tools_region_snap_size; + art->init = sequencer_tools_region_init; + art->draw = sequencer_tools_region_draw; + BLI_addhead(&st->regiontypes, art); + + /* regions: tool header */ + art = MEM_callocN(sizeof(ARegionType), "spacetype sequencer tool header region"); + art->regionid = RGN_TYPE_TOOL_HEADER; + art->prefsizey = HEADERY; + art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER; + art->listener = sequencer_main_region_listener; + art->init = sequencer_header_region_init; + art->draw = sequencer_header_region_draw; + art->message_subscribe = ED_area_do_mgs_subscribe_for_tool_header; + BLI_addhead(&st->regiontypes, art); /* regions: header */ art = MEM_callocN(sizeof(ARegionType), "spacetype sequencer region"); @@ -869,6 +918,10 @@ void ED_spacetype_sequencer(void) BLI_addhead(&st->regiontypes, art); + /* regions: hud */ + art = ED_area_type_hud(st->spaceid); + BLI_addhead(&st->regiontypes, art); + BKE_spacetype_register(st); /* set the sequencer callback when not in background mode */ diff --git a/source/blender/editors/space_statusbar/space_statusbar.c b/source/blender/editors/space_statusbar/space_statusbar.c index 63f27b4d74a..69060daa171 100644 --- a/source/blender/editors/space_statusbar/space_statusbar.c +++ b/source/blender/editors/space_statusbar/space_statusbar.c @@ -81,7 +81,7 @@ static SpaceLink *statusbar_duplicate(SpaceLink *sl) /* add handlers, stuff you only do once or on area/region changes */ static void statusbar_header_region_init(wmWindowManager *UNUSED(wm), ARegion *region) { - if (ELEM(region->alignment, RGN_ALIGN_RIGHT)) { + if (ELEM(RGN_ALIGN_ENUM_FROM_MASK(region->alignment), RGN_ALIGN_RIGHT)) { region->flag |= RGN_FLAG_DYNAMIC_SIZE; } ED_region_header_init(region); diff --git a/source/blender/editors/space_topbar/space_topbar.c b/source/blender/editors/space_topbar/space_topbar.c index 725a49e417e..d62fcf45d68 100644 --- a/source/blender/editors/space_topbar/space_topbar.c +++ b/source/blender/editors/space_topbar/space_topbar.c @@ -103,7 +103,7 @@ static void topbar_main_region_init(wmWindowManager *wm, ARegion *region) wmKeyMap *keymap; /* force delayed UI_view2d_region_reinit call */ - if (ELEM(region->alignment, RGN_ALIGN_RIGHT)) { + if (ELEM(RGN_ALIGN_ENUM_FROM_MASK(region->alignment), RGN_ALIGN_RIGHT)) { region->flag |= RGN_FLAG_DYNAMIC_SIZE; } UI_view2d_region_reinit(®ion->v2d, V2D_COMMONVIEW_HEADER, region->winx, region->winy); @@ -123,7 +123,7 @@ static void topbar_keymap(struct wmKeyConfig *UNUSED(keyconf)) /* add handlers, stuff you only do once or on area/region changes */ static void topbar_header_region_init(wmWindowManager *UNUSED(wm), ARegion *ar) { - if ((ar->alignment & ~RGN_SPLIT_PREV) == RGN_ALIGN_RIGHT) { + if (RGN_ALIGN_ENUM_FROM_MASK(ar->alignment) == RGN_ALIGN_RIGHT) { ar->flag |= RGN_FLAG_DYNAMIC_SIZE; } ED_region_header_init(ar); diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index a4714249da2..83fb87264e3 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -158,6 +158,12 @@ typedef struct ViewOpsData { float trackvec[3]; /** Dolly only. */ float mousevec[3]; + + /** + * #RegionView3D.persp set after auto-perspective is applied. + * If we want the value before running the operator, add a separate member. + */ + char persp; } init; /** Previous state (previous modal event handled). */ @@ -413,6 +419,7 @@ static void viewops_data_create(bContext *C, * we may want to make this optional but for now its needed always */ ED_view3d_camera_lock_init(depsgraph, vod->v3d, vod->rv3d); + vod->init.persp = rv3d->persp; vod->init.dist = rv3d->dist; vod->init.camzoom = rv3d->camzoom; copy_qt_qt(vod->init.quat, rv3d->viewquat); @@ -613,6 +620,7 @@ static void viewrotate_apply_snap(ViewOpsData *vod) float zaxis_best[3]; int x, y, z; bool found = false; + bool is_axis_aligned = false; invert_qt_qt_normalized(viewquat_inv, vod->curr.viewquat); @@ -630,6 +638,10 @@ static void viewrotate_apply_snap(ViewOpsData *vod) if (angle_normalized_v3v3(zaxis_test, zaxis) < axis_limit) { copy_v3_v3(zaxis_best, zaxis_test); found = true; + + if (abs(x) + abs(y) + abs(z) == 1) { + is_axis_aligned = true; + } } } } @@ -700,6 +712,17 @@ static void viewrotate_apply_snap(ViewOpsData *vod) copy_qt_qt(rv3d->viewquat, quat_best); viewrotate_apply_dyn_ofs(vod, rv3d->viewquat); + + if (U.uiflag & USER_AUTOPERSP) { + if (is_axis_aligned) { + if (rv3d->persp == RV3D_PERSP) { + rv3d->persp = RV3D_ORTHO; + } + } + } + } + else if (U.uiflag & USER_AUTOPERSP) { + rv3d->persp = vod->init.persp; } } @@ -859,6 +882,7 @@ static int viewrotate_modal(bContext *C, wmOperator *op, const wmEvent *event) event_code = VIEW_APPLY; break; case VIEWROT_MODAL_AXIS_SNAP_DISABLE: + vod->rv3d->persp = vod->init.persp; vod->axis_snap = false; event_code = VIEW_APPLY; break; diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c index 494e7529975..34470896fb9 100644 --- a/source/blender/editors/space_view3d/view3d_walk.c +++ b/source/blender/editors/space_view3d/view3d_walk.c @@ -676,7 +676,7 @@ static void walkEvent(bContext *C, WalkInfo *walk, const wmEvent *event) return; } - if ((walk->is_cursor_absolute == false) && event->is_motion_absolute) { + if ((walk->is_cursor_absolute == false) && event->tablet.is_motion_absolute) { walk->is_cursor_absolute = true; copy_v2_v2_int(walk->prev_mval, event->mval); copy_v2_v2_int(walk->center_mval, event->mval); diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 9a98a2b8a00..62a61a3a05a 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -856,7 +856,7 @@ static bool transform_modal_item_poll(const wmOperator *op, int value) if (t->spacetype != SPACE_VIEW3D) { return false; } - else if (t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) { + else if ((t->tsnap.mode & ~(SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) == 0) { return false; } else if (!validSnap(t)) { diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index b2d8671fbce..09992e8be0e 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -720,7 +720,7 @@ static void TRANSFORM_OT_translate(struct wmOperatorType *ot) ot->poll = ED_operator_screenactive; ot->poll_property = transform_poll_property; - RNA_def_float_vector_xyz( + RNA_def_float_translation( ot->srna, "value", 3, NULL, -FLT_MAX, FLT_MAX, "Move", "", -FLT_MAX, FLT_MAX); WM_operatortype_props_advanced_begin(ot); diff --git a/source/blender/freestyle/intern/application/AppConfig.cpp b/source/blender/freestyle/intern/application/AppConfig.cpp index 3e3a939a8fe..a2f0a3395af 100644 --- a/source/blender/freestyle/intern/application/AppConfig.cpp +++ b/source/blender/freestyle/intern/application/AppConfig.cpp @@ -53,10 +53,6 @@ void Path::setRootDir(const string &iRootDir) string(DIR_SEP.c_str()); _BrushesPath = _ProjectDir + string(DIR_SEP.c_str()) + "data" + string(DIR_SEP.c_str()) + "textures" + string(DIR_SEP.c_str()) + "brushes" + string(DIR_SEP.c_str()); - _PythonPath = _ProjectDir + string(DIR_SEP.c_str()) + "modules" + string(DIR_SEP.c_str()); - if (getenv("PYTHONPATH")) { - _PythonPath += string(PATH_SEP.c_str()) + string(getenv("PYTHONPATH")); - } _EnvMapDir = _ProjectDir + string(DIR_SEP.c_str()) + "data" + string(DIR_SEP.c_str()) + "env_map" + string(DIR_SEP.c_str()); _MapsDir = _ProjectDir + string(DIR_SEP.c_str()) + "data" + string(DIR_SEP.c_str()) + "maps" + diff --git a/source/blender/freestyle/intern/application/AppConfig.h b/source/blender/freestyle/intern/application/AppConfig.h index cc2a3962ecd..9369ed81d50 100644 --- a/source/blender/freestyle/intern/application/AppConfig.h +++ b/source/blender/freestyle/intern/application/AppConfig.h @@ -43,7 +43,6 @@ class Path { string _ModelsPath; string _PatternsPath; string _BrushesPath; - string _PythonPath; string _EnvMapDir; string _MapsDir; string _HomeDir; @@ -72,10 +71,6 @@ class Path { { return _BrushesPath; } - const string &getPythonPath() const - { - return _PythonPath; - } const string &getEnvMapDir() const { return _EnvMapDir; diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index a8e8ca72023..19c7386ad6d 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -188,7 +188,6 @@ GPUTexture *GPU_texture_create_buffer(eGPUTextureFormat data_type, const uint bu GPUTexture *GPU_texture_from_bindcode(int textarget, int bindcode); GPUTexture *GPU_texture_from_blender(struct Image *ima, struct ImageUser *iuser, int textarget); -GPUTexture *GPU_texture_from_preview(struct PreviewImage *prv, int mipmap); /* movie clip drawing */ GPUTexture *GPU_texture_from_movieclip(struct MovieClip *clip, diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c index 2c74afd2d8e..bf7b1908321 100644 --- a/source/blender/gpu/intern/gpu_buffers.c +++ b/source/blender/gpu/intern/gpu_buffers.c @@ -116,7 +116,7 @@ void gpu_pbvh_init() g_vbo_id.msk = GPU_vertformat_attr_add( &g_vbo_id.format, "msk", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); g_vbo_id.col = GPU_vertformat_attr_add( - &g_vbo_id.format, "ac", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + &g_vbo_id.format, "ac", GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); } } @@ -240,8 +240,13 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers, for (int j = 0; j < 3; j++) { const int loop_index = lt->tri[j]; const int vidx = face_vert_indices[i][j]; - const uchar *elem = &vcol[loop_index].r; - GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vidx, elem); + const MLoopCol *mcol = &vcol[loop_index]; + ushort scol[4]; + scol[0] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->r]); + scol[1] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->g]); + scol[2] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->b]); + scol[3] = unit_float_to_ushort_clamp(mcol->a * (1.0f / 255.0f)); + GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vidx, scol); } } } @@ -289,8 +294,13 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers, if (show_vcol) { const uint loop_index = lt->tri[j]; - const uchar *elem = &vcol[loop_index].r; - memcpy(GPU_vertbuf_raw_step(&col_step), elem, sizeof(uchar) * 4); + const MLoopCol *mcol = &vcol[loop_index]; + ushort scol[4]; + scol[0] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->r]); + scol[1] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->g]); + scol[2] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->b]); + scol[3] = unit_float_to_ushort_clamp(mcol->a * (1.0f / 255.0f)); + memcpy(GPU_vertbuf_raw_step(&col_step), scol, sizeof(scol)); } } } @@ -654,7 +664,7 @@ void GPU_pbvh_grid_buffers_update(GPU_PBVH_Buffers *buffers, } if (show_vcol) { - char vcol[4] = {255, 255, 255, 255}; + ushort vcol[4] = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX}; GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index, &vcol); } @@ -705,7 +715,7 @@ void GPU_pbvh_grid_buffers_update(GPU_PBVH_Buffers *buffers, empty_mask = empty_mask && (fmask == 0.0f); } - char vcol[4] = {255, 255, 255, 255}; + ushort vcol[4] = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX}; GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index + 0, &vcol); GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index + 1, &vcol); GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index + 2, &vcol); @@ -781,7 +791,7 @@ static void gpu_bmesh_vert_to_buffer_copy(BMVert *v, } if (show_vcol) { - static char vcol[4] = {255, 255, 255, 255}; + ushort vcol[4] = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX}; GPU_vertbuf_attr_set(vert_buf, g_vbo_id.col, v_index, &vcol); } } diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index 5a5c3ace552..a7dd34a5f96 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -1042,12 +1042,6 @@ static char *code_generate_vertex(ListBase *nodes, const char *vert_code, bool u input->attr_id, attr_prefix_get(input->attr_type), attr_safe_name); - /* Auto attribute can be vertex color byte buffer. - * We need to know and convert them to linear space in VS. */ - if (input->attr_type == CD_AUTO_FROM_NAME) { - BLI_dynstr_appendf(ds, "uniform bool ba%s;\n", attr_safe_name); - BLI_dynstr_appendf(ds, "#define att%d_is_srgb ba%s\n", input->attr_id, attr_safe_name); - } } BLI_dynstr_appendf(ds, "out %s var%d%s;\n", @@ -1101,24 +1095,6 @@ static char *code_generate_vertex(ListBase *nodes, const char *vert_code, bool u BLI_dynstr_append(ds, use_geom ? "RESOURCE_ID_VARYING_GEOM\n" : "RESOURCE_ID_VARYING\n"); - BLI_dynstr_append(ds, - "#define USE_ATTR\n" - "vec3 srgb_to_linear_attr(vec3 c) {\n" - "\tc = max(c, vec3(0.0));\n" - "\tvec3 c1 = c * (1.0 / 12.92);\n" - "\tvec3 c2 = pow((c + 0.055) * (1.0 / 1.055), vec3(2.4));\n" - "\treturn mix(c1, c2, step(vec3(0.04045), c));\n" - "}\n\n"); - - BLI_dynstr_append(ds, - "vec4 srgba_to_linear_attr(vec4 c) {\n" - "\tc = max(c, vec4(0.0));\n" - "\tvec4 c1 = c * (1.0 / 12.92);\n" - "\tvec4 c2 = pow((c + 0.055) * (1.0 / 1.055), vec4(2.4));\n" - "\tvec4 final = mix(c1, c2, step(vec4(0.04045), c));" - "\treturn vec4(final.xyz, c.a);\n" - "}\n\n"); - /* Prototype because defined later. */ BLI_dynstr_append(ds, "vec2 hair_get_customdata_vec2(const samplerBuffer);\n" @@ -1224,22 +1200,6 @@ static char *code_generate_vertex(ListBase *nodes, const char *vert_code, bool u input->attr_id, use_geom ? "g" : ""); } - else if (input->attr_type == CD_MCOL) { - BLI_dynstr_appendf(ds, - "\tvar%d%s = srgba_to_linear_attr(att%d);\n", - input->attr_id, - use_geom ? "g" : "", - input->attr_id); - } - else if (input->attr_type == CD_AUTO_FROM_NAME) { - BLI_dynstr_appendf(ds, - "\tvar%d%s = (att%d_is_srgb) ? srgb_to_linear_attr(att%d) : att%d;\n", - input->attr_id, - use_geom ? "g" : "", - input->attr_id, - input->attr_id, - input->attr_id); - } else { BLI_dynstr_appendf( ds, "\tvar%d%s = att%d;\n", input->attr_id, use_geom ? "g" : "", input->attr_id); diff --git a/source/blender/gpu/intern/gpu_texture.c b/source/blender/gpu/intern/gpu_texture.c index 201194232db..84328b8dfd4 100644 --- a/source/blender/gpu/intern/gpu_texture.c +++ b/source/blender/gpu/intern/gpu_texture.c @@ -1064,56 +1064,6 @@ GPUTexture *GPU_texture_from_bindcode(int textarget, int bindcode) return tex; } -GPUTexture *GPU_texture_from_preview(PreviewImage *prv, int mipmap) -{ - GPUTexture *tex = prv->gputexture[0]; - GLuint bindcode = 0; - - if (tex) { - bindcode = tex->bindcode; - } - - /* this binds a texture, so that's why we restore it to 0 */ - if (bindcode == 0) { - GPU_create_gl_tex( - &bindcode, prv->rect[0], NULL, prv->w[0], prv->h[0], GL_TEXTURE_2D, mipmap, false, NULL); - } - if (tex) { - tex->bindcode = bindcode; - glBindTexture(GL_TEXTURE_2D, 0); - return tex; - } - - tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); - tex->bindcode = bindcode; - tex->number = -1; - tex->refcount = 1; - tex->target = GL_TEXTURE_2D; - tex->target_base = GL_TEXTURE_2D; - tex->format = -1; - tex->components = -1; - - prv->gputexture[0] = tex; - - if (!glIsTexture(tex->bindcode)) { - GPU_print_error_debug("Blender Texture Not Loaded"); - } - else { - GLint w, h; - - glBindTexture(GL_TEXTURE_2D, tex->bindcode); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); - - tex->w = w; - tex->h = h; - } - - glBindTexture(GL_TEXTURE_2D, 0); - - return tex; -} - GPUTexture *GPU_texture_create_1d(int w, eGPUTextureFormat tex_format, const float *pixels, diff --git a/source/blender/makesdna/DNA_armature_types.h b/source/blender/makesdna/DNA_armature_types.h index 354344328d3..7192b1295aa 100644 --- a/source/blender/makesdna/DNA_armature_types.h +++ b/source/blender/makesdna/DNA_armature_types.h @@ -252,7 +252,8 @@ typedef enum eBone_Flag { BONE_ADD_PARENT_END_ROLL = (1 << 24), /** this bone was transformed by the mirror function */ BONE_TRANSFORM_MIRROR = (1 << 25), - + /** this bone is associated with a locked vertex group, ONLY USE FOR DRAWING */ + BONE_DRAW_LOCKED_WEIGHT = (1 << 26), } eBone_Flag; /* bone->inherit_scale_mode */ diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index d3c5a707b44..a52767834a4 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -129,6 +129,12 @@ typedef struct ScrAreaMap { ListBase areabase; } ScrAreaMap; +typedef struct Panel_Runtime { + /* Applied to Panel.ofsx, but saved separately so we can track changes between redraws. */ + int region_ofsx; + char _pad[4]; +} Panel_Runtime; + /** The part from uiBlock that needs saved in file. */ typedef struct Panel { struct Panel *next, *prev; @@ -159,6 +165,8 @@ typedef struct Panel { void *activedata; /** Sub panels. */ ListBase children; + + Panel_Runtime runtime; } Panel; /** @@ -409,7 +417,9 @@ typedef struct ARegion { short flag; /** Current split size in unscaled pixels (if zero it uses regiontype). - * To convert to pixels use: `UI_DPI_FAC * ar->sizex + 0.5f`. */ + * To convert to pixels use: `UI_DPI_FAC * ar->sizex + 0.5f`. + * However to get the current region size, you should usually use winx/winy from above, not this! + */ short sizex, sizey; /** Private, cached notifier events. */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index f0a852a7a1a..d81d8db3bd3 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -283,13 +283,12 @@ typedef struct ThemeSpace { unsigned char normal[4]; unsigned char vertex_normal[4]; unsigned char loop_normal[4]; - unsigned char bone_solid[4], bone_pose[4], bone_pose_active[4]; + unsigned char bone_solid[4], bone_pose[4], bone_pose_active[4], bone_locked_weight[4]; unsigned char strip[4], strip_select[4]; unsigned char cframe[4]; unsigned char time_keyframe[4], time_gp_keyframe[4]; unsigned char freestyle_edge_mark[4], freestyle_face_mark[4]; unsigned char time_scrub_background[4]; - char _pad5[4]; unsigned char nurb_uline[4], nurb_vline[4]; unsigned char act_spline[4], nurb_sel_uline[4], nurb_sel_vline[4], lastsel_point[4]; diff --git a/source/blender/makesrna/RNA_define.h b/source/blender/makesrna/RNA_define.h index 0beb14614ec..349b30fa64e 100644 --- a/source/blender/makesrna/RNA_define.h +++ b/source/blender/makesrna/RNA_define.h @@ -233,6 +233,16 @@ PropertyRNA *RNA_def_float_matrix(StructOrFunctionRNA *cont, const char *ui_description, float softmin, float softmax); +PropertyRNA *RNA_def_float_translation(StructOrFunctionRNA *cont, + const char *identifier, + int len, + const float *default_value, + float hardmin, + float hardmax, + const char *ui_name, + const char *ui_description, + float softmin, + float softmax); PropertyRNA *RNA_def_float_rotation(StructOrFunctionRNA *cont, const char *identifier, int len, diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index d7f6ec1fb5a..c10436ae08e 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -52,6 +52,7 @@ extern const EnumPropertyItem rna_enum_mesh_select_mode_items[]; extern const EnumPropertyItem rna_enum_mesh_select_mode_uv_items[]; extern const EnumPropertyItem rna_enum_mesh_delimit_mode_items[]; extern const EnumPropertyItem rna_enum_space_graph_mode_items[]; +extern const EnumPropertyItem rna_enum_space_sequencer_view_type_items[]; extern const EnumPropertyItem rna_enum_space_type_items[]; extern const EnumPropertyItem rna_enum_space_image_mode_items[]; extern const EnumPropertyItem rna_enum_space_image_mode_all_items[]; @@ -141,6 +142,8 @@ extern const EnumPropertyItem rna_enum_texture_type_items[]; extern const EnumPropertyItem rna_enum_light_type_items[]; +extern const EnumPropertyItem rna_enum_lightprobes_type_items[]; + extern const EnumPropertyItem rna_enum_unpack_method_items[]; extern const EnumPropertyItem rna_enum_object_type_items[]; diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c index 55aa529a30e..73a59cbba11 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -3888,6 +3888,36 @@ PropertyRNA *RNA_def_float_matrix(StructOrFunctionRNA *cont_, return prop; } +PropertyRNA *RNA_def_float_translation(StructOrFunctionRNA *cont_, + const char *identifier, + int len, + const float *default_value, + float hardmin, + float hardmax, + const char *ui_name, + const char *ui_description, + float softmin, + float softmax) +{ + PropertyRNA *prop; + + prop = RNA_def_float_vector(cont_, + identifier, + len, + default_value, + hardmin, + hardmax, + ui_name, + ui_description, + softmin, + softmax); + prop->subtype = PROP_TRANSLATION; + + RNA_def_property_ui_range(prop, softmin, softmax, 1, RNA_TRANSLATION_PREC_DEFAULT); + + return prop; +} + PropertyRNA *RNA_def_float_rotation(StructOrFunctionRNA *cont_, const char *identifier, int len, diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index d85c5c5f249..9df21a16e90 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -250,6 +250,9 @@ static Object *rna_Main_objects_new(Main *bmain, ReportList *reports, const char case ID_AR: type = OB_ARMATURE; break; + case ID_LP: + type = OB_LIGHTPROBE; + break; default: { const char *idname; if (RNA_enum_id_from_value(rna_enum_id_type_items, GS(data->name), &idname) == 0) { @@ -665,12 +668,15 @@ static FreestyleLineStyle *rna_Main_linestyles_new(Main *bmain, const char *name return linestyle; } -static LightProbe *rna_Main_lightprobe_new(Main *bmain, const char *name) +static LightProbe *rna_Main_lightprobe_new(Main *bmain, const char *name, int type) { char safe_name[MAX_ID_NAME - 2]; rna_idname_validate(name, safe_name); LightProbe *probe = BKE_lightprobe_add(bmain, safe_name); + + BKE_lightprobe_type_set(probe, type); + id_us_min(&probe->id); return probe; } @@ -2079,6 +2085,9 @@ void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_function_ui_description(func, "Add a new probe to the main database"); parm = RNA_def_string(func, "name", "Probe", 0, "", "New name for the data-block"); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_enum( + func, "type", rna_enum_lightprobes_type_items, 0, "Type", "The type of lightprobe to add"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); /* return type */ parm = RNA_def_pointer(func, "lightprobe", "LightProbe", "", "New light probe data-block"); RNA_def_function_return(func, parm); diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 40622894145..182e80900bd 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -25,6 +25,7 @@ #include "DNA_brush_types.h" #include "DNA_collection_types.h" #include "DNA_customdata_types.h" +#include "DNA_lightprobe_types.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_object_force_types.h" @@ -196,6 +197,13 @@ const EnumPropertyItem rna_enum_metaelem_type_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_lightprobes_type_items[] = { + {LIGHTPROBE_TYPE_CUBE, "CUBE", ICON_LIGHTPROBE_CUBEMAP, "Cube", ""}, + {LIGHTPROBE_TYPE_PLANAR, "PLANAR", ICON_LIGHTPROBE_PLANAR, "Planar", ""}, + {LIGHTPROBE_TYPE_GRID, "GRID", ICON_LIGHTPROBE_GRID, "Grid", ""}, + {0, NULL, 0, NULL, NULL}, +}; + /* used for 2 enums */ #define OBTYPE_CU_CURVE \ { \ diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index 536691b074d..d914dc3b8dd 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -867,7 +867,7 @@ static void rna_PartSettings_start_set(struct PointerRNA *ptr, float value) /* check for clipping */ if (value > settings->end) { - value = settings->end; + settings->end = value; } /*if (settings->type==PART_REACTOR && value < 1.0) */ @@ -886,7 +886,7 @@ static void rna_PartSettings_end_set(struct PointerRNA *ptr, float value) /* check for clipping */ if (value < settings->sta) { - value = settings->sta; + settings->sta = value; } settings->end = value; diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index ea1fb1eb489..7d35a1c5e2a 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -941,8 +941,8 @@ static void rna_Scene_start_frame_set(PointerRNA *ptr, int value) CLAMP(value, MINFRAME, MAXFRAME); data->r.sfra = value; - if (data->r.sfra >= data->r.efra) { - data->r.efra = MIN2(data->r.sfra, MAXFRAME); + if (value > data->r.efra) { + data->r.efra = MIN2(value, MAXFRAME); } } @@ -952,8 +952,8 @@ static void rna_Scene_end_frame_set(PointerRNA *ptr, int value) CLAMP(value, MINFRAME, MAXFRAME); data->r.efra = value; - if (data->r.sfra >= data->r.efra) { - data->r.sfra = MAX2(data->r.efra, MINFRAME); + if (data->r.sfra > value) { + data->r.sfra = MAX2(value, MINFRAME); } } @@ -985,10 +985,12 @@ static void rna_Scene_preview_range_start_frame_set(PointerRNA *ptr, int value) /* TODO: or just refuse to set instead? */ data->r.pefra = data->r.efra; } - - /* now set normally */ - CLAMP(value, MINAFRAME, data->r.pefra); + CLAMP(value, MINAFRAME, MAXFRAME); data->r.psfra = value; + + if (value > data->r.pefra) { + data->r.pefra = MIN2(value, MAXFRAME); + } } static void rna_Scene_preview_range_end_frame_set(PointerRNA *ptr, int value) @@ -1001,10 +1003,12 @@ static void rna_Scene_preview_range_end_frame_set(PointerRNA *ptr, int value) /* TODO: or just refuse to set instead? */ data->r.psfra = data->r.sfra; } - - /* now set normally */ - CLAMP(value, data->r.psfra, MAXFRAME); + CLAMP(value, MINAFRAME, MAXFRAME); data->r.pefra = value; + + if (data->r.psfra > value) { + data->r.psfra = MAX2(value, MINAFRAME); + } } static void rna_Scene_show_subframe_update(Main *UNUSED(bmain), diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 67b43cb13df..5a671ab146c 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -161,6 +161,13 @@ const EnumPropertyItem rna_enum_space_graph_mode_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_space_sequencer_view_type_items[] = { + {SEQ_VIEW_SEQUENCE, "SEQUENCER", ICON_SEQ_SEQUENCER, "Sequencer", ""}, + {SEQ_VIEW_PREVIEW, "PREVIEW", ICON_SEQ_PREVIEW, "Preview", ""}, + {SEQ_VIEW_SEQUENCE_PREVIEW, "SEQUENCER_PREVIEW", ICON_SEQ_SPLITVIEW, "Sequencer/Preview", ""}, + {0, NULL, 0, NULL, NULL}, +}; + #define SACT_ITEM_DOPESHEET \ { \ SACTCONT_DOPESHEET, "DOPESHEET", ICON_ACTION, "Dope Sheet", "Edit all keyframes in scene" \ @@ -2640,7 +2647,9 @@ static void rna_def_space(BlenderRNA *brna) /* access to V2D_VIEWSYNC_SCREEN_TIME */ prop = RNA_def_property(srna, "show_locked_time", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_funcs(prop, "rna_Space_view2d_sync_get", "rna_Space_view2d_sync_set"); - RNA_def_property_ui_text(prop, "Lock Time to Other Windows", ""); + RNA_def_property_ui_text(prop, + "Sync Visible Range", + "Syncronize the visible timeline range with other time-based editors"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_TIME, "rna_Space_view2d_sync_update"); rna_def_space_generic_show_region_toggles(srna, (1 << RGN_TYPE_HEADER)); @@ -4458,17 +4467,6 @@ static void rna_def_space_sequencer(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; - static const EnumPropertyItem view_type_items[] = { - {SEQ_VIEW_SEQUENCE, "SEQUENCER", ICON_SEQ_SEQUENCER, "Sequencer", ""}, - {SEQ_VIEW_PREVIEW, "PREVIEW", ICON_SEQ_PREVIEW, "Preview", ""}, - {SEQ_VIEW_SEQUENCE_PREVIEW, - "SEQUENCER_PREVIEW", - ICON_SEQ_SPLITVIEW, - "Sequencer/Preview", - ""}, - {0, NULL, 0, NULL, NULL}, - }; - static const EnumPropertyItem display_mode_items[] = { {SEQ_DRAW_IMG_IMBUF, "IMAGE", ICON_SEQ_PREVIEW, "Image Preview", ""}, {SEQ_DRAW_IMG_WAVEFORM, "WAVEFORM", ICON_SEQ_LUMA_WAVEFORM, "Luma Waveform", ""}, @@ -4528,12 +4526,13 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_struct_sdna(srna, "SpaceSeq"); RNA_def_struct_ui_text(srna, "Space Sequence Editor", "Sequence editor space data"); - rna_def_space_generic_show_region_toggles(srna, (1 << RGN_TYPE_UI)); + rna_def_space_generic_show_region_toggles( + srna, (1 << RGN_TYPE_TOOL_HEADER) | (1 << RGN_TYPE_UI) | (1 << RGN_TYPE_TOOLS)); /* view type, fairly important */ prop = RNA_def_property(srna, "view_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "view"); - RNA_def_property_enum_items(prop, view_type_items); + RNA_def_property_enum_items(prop, rna_enum_space_sequencer_view_type_items); RNA_def_property_ui_text( prop, "View Type", "Type of the Sequencer view (sequencer, preview or both)"); RNA_def_property_update(prop, 0, "rna_Sequencer_view_type_update"); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 56f19d313fd..fc2b074d6d1 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -2231,6 +2231,14 @@ static void rna_def_userdef_theme_space_view3d(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Bone Solid", ""); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + prop = RNA_def_property(srna, "bone_locked_weight", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_array(prop, 4); + RNA_def_property_ui_text( + prop, + "Bone Locked Weight", + "Shade for bones corresponding to a locked weight group during painting"); + RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + /* misc */ prop = RNA_def_property(srna, "bundle_solid", PROP_FLOAT, PROP_COLOR_GAMMA); diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 249581d22bf..532992baa5c 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -2185,7 +2185,7 @@ static void rna_def_event(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Is Tablet", "The event has tablet data"); prop = RNA_def_property(srna, "is_mouse_absolute", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "is_motion_absolute", 1); + RNA_def_property_boolean_sdna(prop, NULL, "tablet.is_motion_absolute", 1); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Absolute Motion", "The last motion event was an absolute input"); diff --git a/source/blender/makesrna/intern/rna_workspace.c b/source/blender/makesrna/intern/rna_workspace.c index 4d4ab64abb9..32e348ea72f 100644 --- a/source/blender/makesrna/intern/rna_workspace.c +++ b/source/blender/makesrna/intern/rna_workspace.c @@ -152,7 +152,17 @@ static bToolRef *rna_WorkSpace_tools_from_space_node(WorkSpace *workspace, bool }, create); } - +static bToolRef *rna_WorkSpace_tools_from_space_sequencer(WorkSpace *workspace, + int mode, + bool create) +{ + return rna_WorkSpace_tools_from_tkey(workspace, + &(bToolKey){ + .space_type = SPACE_SEQ, + .mode = mode, + }, + create); +} const EnumPropertyItem *rna_WorkSpace_tools_mode_itemf(bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), @@ -164,6 +174,8 @@ const EnumPropertyItem *rna_WorkSpace_tools_mode_itemf(bContext *UNUSED(C), return rna_enum_context_mode_items; case SPACE_IMAGE: return rna_enum_space_image_mode_all_items; + case SPACE_SEQ: + return rna_enum_space_sequencer_view_type_items; } return DummyRNA_DEFAULT_items; } @@ -335,6 +347,16 @@ static void rna_def_workspace_tools(BlenderRNA *brna, PropertyRNA *cprop) /* return type */ parm = RNA_def_pointer(func, "result", "WorkSpaceTool", "", ""); RNA_def_function_return(func, parm); + + func = RNA_def_function( + srna, "from_space_sequencer", "rna_WorkSpace_tools_from_space_sequencer"); + RNA_def_function_ui_description(func, ""); + parm = RNA_def_enum(func, "mode", rna_enum_space_sequencer_view_type_items, 0, "", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + RNA_def_boolean(func, "create", false, "Create", ""); + /* return type */ + parm = RNA_def_pointer(func, "result", "WorkSpaceTool", "", ""); + RNA_def_function_return(func, parm); } static void rna_def_workspace(BlenderRNA *brna) diff --git a/source/blender/physics/intern/BPH_mass_spring.cpp b/source/blender/physics/intern/BPH_mass_spring.cpp index 999cefde104..94eaffd1f91 100644 --- a/source/blender/physics/intern/BPH_mass_spring.cpp +++ b/source/blender/physics/intern/BPH_mass_spring.cpp @@ -83,7 +83,7 @@ static float cloth_calc_volume(ClothModifierData *clmd) float vol = 0; if (clmd->sim_parms->vgroup_pressure > 0) { - for (unsigned int i = 0; i < cloth->tri_num; i++) { + for (unsigned int i = 0; i < cloth->primitive_num; i++) { bool skip_face = false; /* We have custom vertex weights for pressure. */ const MVertTri *vt = &tri[i]; @@ -103,7 +103,7 @@ static float cloth_calc_volume(ClothModifierData *clmd) } } else { - for (unsigned int i = 0; i < cloth->tri_num; i++) { + for (unsigned int i = 0; i < cloth->primitive_num; i++) { const MVertTri *vt = &tri[i]; vol += BPH_tri_tetra_volume_signed_6x(data, vt->tri[0], vt->tri[1], vt->tri[2]); } @@ -174,96 +174,17 @@ void BKE_cloth_solver_set_volume(ClothModifierData *clmd) cloth->initial_mesh_volume = cloth_calc_volume(clmd); } -static bool collision_response(ClothModifierData *clmd, - CollisionModifierData *collmd, - CollPair *collpair, - float dt, - float restitution, - float r_impulse[3]) -{ - Cloth *cloth = clmd->clothObject; - int index = collpair->ap1; - bool result = false; - - float v1[3], v2_old[3], v2_new[3], v_rel_old[3], v_rel_new[3]; - float epsilon2 = BLI_bvhtree_get_epsilon(collmd->bvhtree); - - float margin_distance = (float)collpair->distance - epsilon2; - float mag_v_rel; - - zero_v3(r_impulse); - - if (margin_distance > 0.0f) { - return false; /* XXX tested before already? */ - } - - /* only handle static collisions here */ - if (collpair->flag & COLLISION_IN_FUTURE) { - return false; - } - - /* velocity */ - copy_v3_v3(v1, cloth->verts[index].v); - collision_get_collider_velocity(v2_old, v2_new, collmd, collpair); - /* relative velocity = velocity of the cloth point relative to the collider */ - sub_v3_v3v3(v_rel_old, v1, v2_old); - sub_v3_v3v3(v_rel_new, v1, v2_new); - /* normal component of the relative velocity */ - mag_v_rel = dot_v3v3(v_rel_old, collpair->normal); - - /* only valid when moving toward the collider */ - if (mag_v_rel < -ALMOST_ZERO) { - float v_nor_old, v_nor_new; - float v_tan_old[3], v_tan_new[3]; - float bounce, repulse; - - /* Collision response based on - * "Simulating Complex Hair with Robust Collision Handling" (Choe, Choi, Ko, ACM SIGGRAPH 2005) - * http://graphics.snu.ac.kr/publications/2005-choe-HairSim/Choe_2005_SCA.pdf - */ - - v_nor_old = mag_v_rel; - v_nor_new = dot_v3v3(v_rel_new, collpair->normal); - - madd_v3_v3v3fl(v_tan_old, v_rel_old, collpair->normal, -v_nor_old); - madd_v3_v3v3fl(v_tan_new, v_rel_new, collpair->normal, -v_nor_new); - - bounce = -v_nor_old * restitution; - - repulse = -margin_distance / dt; /* base repulsion velocity in normal direction */ - /* XXX this clamping factor is quite arbitrary ... - * not sure if there is a more scientific approach, but seems to give good results - */ - CLAMP(repulse, 0.0f, 4.0f * bounce); - - if (margin_distance < -epsilon2) { - mul_v3_v3fl(r_impulse, collpair->normal, max_ff(repulse, bounce) - v_nor_new); - } - else { - bounce = 0.0f; - mul_v3_v3fl(r_impulse, collpair->normal, repulse - v_nor_new); - } - - result = true; - } - - return result; -} - /* Init constraint matrix * This is part of the modified CG method suggested by Baraff/Witkin in * "Large Steps in Cloth Simulation" (Siggraph 1998) */ -static void cloth_setup_constraints(ClothModifierData *clmd, - ColliderContacts *contacts, - int totcolliders, - float dt) +static void cloth_setup_constraints(ClothModifierData *clmd) { Cloth *cloth = clmd->clothObject; Implicit_Data *data = cloth->implicit; ClothVertex *verts = cloth->verts; int mvert_num = cloth->mvert_num; - int i, j, v; + int v; const float ZERO[3] = {0.0f, 0.0f, 0.0f}; @@ -277,37 +198,6 @@ static void cloth_setup_constraints(ClothModifierData *clmd, verts[v].impulse_count = 0; } - - for (i = 0; i < totcolliders; i++) { - ColliderContacts *ct = &contacts[i]; - for (j = 0; j < ct->totcollisions; j++) { - CollPair *collpair = &ct->collisions[j]; - // float restitution = (1.0f - clmd->coll_parms->damping) * (1.0f - ct->ob->pd->pdef_sbdamp); - float restitution = 0.0f; - int v = collpair->face1; - float impulse[3]; - - /* pinned verts handled separately */ - if (verts[v].flags & CLOTH_VERT_FLAG_PINNED) { - continue; - } - - /* XXX cheap way of avoiding instability from multiple collisions in the same step - * this should eventually be supported ... - */ - if (verts[v].impulse_count > 0) { - continue; - } - - /* calculate collision response */ - if (!collision_response(clmd, ct->collmd, collpair, dt, restitution, impulse)) { - continue; - } - - BPH_mass_spring_add_constraint_ndof2(data, v, collpair->normal, impulse); - ++verts[v].impulse_count; - } - } } /* computes where the cloth would be if it were subject to perfectly stiff edges @@ -691,7 +581,7 @@ static void cloth_calc_force( pressure_difference *= clmd->sim_parms->pressure_factor; - for (i = 0; i < cloth->tri_num; i++) { + for (i = 0; i < cloth->primitive_num; i++) { const MVertTri *vt = &tri[i]; if (fabs(pressure_difference) > 1E-6f) { if (clmd->sim_parms->vgroup_pressure > 0) { @@ -744,13 +634,13 @@ static void cloth_calc_force( effectors, NULL, clmd->sim_parms->effector_weights, &epoint, winvec[i], NULL); } - for (i = 0; i < cloth->tri_num; i++) { + for (i = 0; i < cloth->primitive_num; i++) { const MVertTri *vt = &tri[i]; BPH_mass_spring_force_face_wind(data, vt->tri[0], vt->tri[1], vt->tri[2], winvec); } /* Hair has only edges */ - if (cloth->tri_num == 0) { + if (cloth->primitive_num == 0) { #if 0 ClothHairData *hairdata = clmd->hairdata; ClothHairData *hair_ij, *hair_kl; @@ -1241,8 +1131,6 @@ int BPH_cloth_solve( unsigned int mvert_num = cloth->mvert_num; float dt = clmd->sim_parms->dt * clmd->sim_parms->timescale; Implicit_Data *id = cloth->implicit; - ColliderContacts *contacts = NULL; - int totcolliders = 0; BKE_sim_debug_data_clear_category("collision"); @@ -1269,25 +1157,8 @@ int BPH_cloth_solve( while (step < tf) { ImplicitSolverResult result; - if (is_hair) { - /* copy velocities for collision */ - for (i = 0; i < mvert_num; i++) { - BPH_mass_spring_get_motion_state(id, i, NULL, verts[i].tv); - copy_v3_v3(verts[i].v, verts[i].tv); - } - - /* determine contact points */ - if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) { - cloth_find_point_contacts(depsgraph, ob, clmd, 0.0f, tf, &contacts, &totcolliders); - } - - /* setup vertex constraints for pinned vertices and contacts */ - cloth_setup_constraints(clmd, contacts, totcolliders, dt); - } - else { - /* setup vertex constraints for pinned vertices */ - cloth_setup_constraints(clmd, NULL, 0, dt); - } + /* setup vertex constraints for pinned vertices */ + cloth_setup_constraints(clmd); /* initialize forces to zero */ BPH_mass_spring_clear_forces(id); @@ -1300,9 +1171,7 @@ int BPH_cloth_solve( cloth_record_result(clmd, &result, dt); /* Calculate collision impulses. */ - if (!is_hair) { - cloth_solve_collisions(depsgraph, ob, clmd, step, dt); - } + cloth_solve_collisions(depsgraph, ob, clmd, step, dt); if (is_hair) { cloth_continuum_step(clmd, dt); @@ -1327,11 +1196,6 @@ int BPH_cloth_solve( BPH_mass_spring_get_motion_state(id, i, verts[i].txold, NULL); } - /* free contact points */ - if (contacts) { - cloth_free_contacts(contacts, totcolliders); - } - step += dt; } diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 73fbde4148e..e7a4ca9a005 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -54,6 +54,7 @@ set(SRC intern/wm_cursors.c intern/wm_dragdrop.c intern/wm_draw.c + intern/wm_event_query.c intern/wm_event_system.c intern/wm_files.c intern/wm_files_link.c diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index d24157a22a6..cfdb15026fd 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -205,11 +205,6 @@ void WM_paint_cursor_tag_redraw(struct wmWindow *win, struct ARegion *ar); void WM_cursor_warp(struct wmWindow *win, int x, int y); void WM_cursor_compatible_xy(wmWindow *win, int *x, int *y); -float WM_cursor_pressure(const struct wmWindow *win); - -/* event map */ -int WM_userdef_event_map(int kmitype); -int WM_userdef_event_type_from_keymap_type(int kmitype); /* handlers */ @@ -250,6 +245,11 @@ wmKeyMapItem *WM_event_match_keymap_item(struct bContext *C, wmKeyMap *keymap, const struct wmEvent *event); +wmKeyMapItem *WM_event_match_keymap_item_from_handlers(struct bContext *C, + struct wmWindowManager *wm, + struct ListBase *handlers, + const struct wmEvent *event); + typedef int (*wmUIHandlerFunc)(struct bContext *C, const struct wmEvent *event, void *userdata); typedef void (*wmUIHandlerRemoveFunc)(struct bContext *C, void *userdata); @@ -294,8 +294,6 @@ struct wmEventHandler_Dropbox *WM_event_add_dropbox_handler(ListBase *handlers, /* mouse */ void WM_event_add_mousemove(const struct bContext *C); -bool WM_event_is_modal_tweak_exit(const struct wmEvent *event, int tweak_event); -bool WM_event_is_last_mousemove(const struct wmEvent *event); #ifdef WITH_INPUT_NDOF /* 3D mouse */ @@ -631,15 +629,9 @@ bool WM_gesture_is_modal_first(const struct wmGesture *gesture); /* fileselecting support */ void WM_event_add_fileselect(struct bContext *C, struct wmOperator *op); void WM_event_fileselect_event(struct wmWindowManager *wm, void *ophandle, int eventval); -int WM_event_modifier_flag(const struct wmEvent *event); -void WM_event_print(const struct wmEvent *event); void WM_operator_region_active_win_set(struct bContext *C); -int WM_event_drag_threshold(const struct wmEvent *event); -bool WM_event_drag_test(const struct wmEvent *event, const int prev_xy[2]); -bool WM_event_drag_test_with_delta(const struct wmEvent *event, const int delta[2]); - /* drag and drop */ struct wmDrag *WM_event_start_drag( struct bContext *C, int icon, int type, void *poin, double value, unsigned int flags); @@ -783,6 +775,36 @@ bool write_crash_blend(void); /* Lock the interface for any communication */ void WM_set_locked_interface(struct wmWindowManager *wm, bool lock); +/* For testing only 'G_FLAG_EVENT_SIMULATE' */ +struct wmEvent *WM_event_add_simulate(struct wmWindow *win, const struct wmEvent *event_to_add); + +const char *WM_window_cursor_keymap_status_get(const struct wmWindow *win, + int button_index, + int type_index); +void WM_window_cursor_keymap_status_refresh(struct bContext *C, struct wmWindow *win); + +void WM_window_status_area_tag_redraw(struct wmWindow *win); +struct ScrArea *WM_window_status_area_find(struct wmWindow *win, struct bScreen *sc); +bool WM_window_modal_keymap_status_draw(struct bContext *C, + struct wmWindow *win, + struct uiLayout *layout); + +/* wm_event_query.c */ +void WM_event_print(const struct wmEvent *event); + +int WM_event_modifier_flag(const struct wmEvent *event); + +bool WM_event_is_modal_tweak_exit(const struct wmEvent *event, int tweak_event); +bool WM_event_is_last_mousemove(const struct wmEvent *event); + +int WM_event_drag_threshold(const struct wmEvent *event); +bool WM_event_drag_test(const struct wmEvent *event, const int prev_xy[2]); +bool WM_event_drag_test_with_delta(const struct wmEvent *event, const int delta[2]); + +/* event map */ +int WM_userdef_event_map(int kmitype); +int WM_userdef_event_type_from_keymap_type(int kmitype); + #ifdef WITH_INPUT_NDOF void WM_event_ndof_pan_get(const struct wmNDOFMotionData *ndof, float r_pan[3], @@ -800,20 +822,6 @@ bool WM_event_is_tablet(const struct wmEvent *event); bool WM_event_is_ime_switch(const struct wmEvent *event); #endif -/* For testing only 'G_FLAG_EVENT_SIMULATE' */ -struct wmEvent *WM_event_add_simulate(struct wmWindow *win, const struct wmEvent *event_to_add); - -const char *WM_window_cursor_keymap_status_get(const struct wmWindow *win, - int button_index, - int type_index); -void WM_window_cursor_keymap_status_refresh(struct bContext *C, struct wmWindow *win); - -void WM_window_status_area_tag_redraw(struct wmWindow *win); -struct ScrArea *WM_window_status_area_find(struct wmWindow *win, struct bScreen *sc); -bool WM_window_modal_keymap_status_draw(struct bContext *C, - struct wmWindow *win, - struct uiLayout *layout); - /* wm_tooltip.c */ typedef struct ARegion *(*wmTooltipInitFn)(struct bContext *C, struct ARegion *ar, diff --git a/source/blender/windowmanager/WM_toolsystem.h b/source/blender/windowmanager/WM_toolsystem.h index 620150ba14f..36cb5be7547 100644 --- a/source/blender/windowmanager/WM_toolsystem.h +++ b/source/blender/windowmanager/WM_toolsystem.h @@ -41,8 +41,8 @@ struct wmOperatorType; /* wm_toolsystem.c */ -#define WM_TOOLSYSTEM_SPACE_MASK ((1 << SPACE_IMAGE) | (1 << SPACE_NODE) | (1 << SPACE_VIEW3D)) - +#define WM_TOOLSYSTEM_SPACE_MASK \ + ((1 << SPACE_IMAGE) | (1 << SPACE_NODE) | (1 << SPACE_VIEW3D) | (1 << SPACE_SEQ)) /* Values that define a categoey of active tool. */ typedef struct bToolKey { int space_type; diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 0c3a5f92113..5870802d02b 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -505,6 +505,19 @@ typedef struct wmGesture { /* ************** wmEvent ************************ */ +typedef struct wmTabletData { + /** 0=EVT_TABLET_NONE, 1=EVT_TABLET_STYLUS, 2=EVT_TABLET_ERASER. */ + int active; + /** range 0.0 (not touching) to 1.0 (full pressure). */ + float pressure; + /** range 0.0 (upright) to 1.0 (tilted fully against the tablet surface). */ + float x_tilt; + /** as above. */ + float y_tilt; + /** Interpret mouse motion as absolute as typical for tablets. */ + char is_motion_absolute; +} wmTabletData; + /** * Each event should have full modifier state. * event comes from event manager and from keymap. @@ -546,13 +559,9 @@ typedef struct wmEvent { /** Set in case a #KM_PRESS went by unhandled. */ char check_click; char check_drag; - char is_motion_absolute; - /** Keymap item, set by handler (weak?). */ - const char *keymap_idname; - - /** Tablet info, only use when the tablet is active. */ - const struct wmTabletData *tablet_data; + /** Tablet info, available for mouse move and button events. */ + wmTabletData tablet; /* custom data */ /** Custom data type, stylus, 6dof, see wm_event_types.h */ @@ -580,18 +589,6 @@ bool WM_event_cursor_click_drag_threshold_met(const wmEvent *event); */ #define WM_EVENT_CURSOR_MOTION_THRESHOLD ((float)U.move_threshold * U.dpi_fac) -/* ************** custom wmEvent data ************** */ -typedef struct wmTabletData { - /** 0=EVT_TABLET_NONE, 1=EVT_TABLET_STYLUS, 2=EVT_TABLET_ERASER. */ - int Active; - /** range 0.0 (not touching) to 1.0 (full pressure). */ - float Pressure; - /** range 0.0 (upright) to 1.0 (tilted fully against the tablet surface). */ - float Xtilt; - /** as above. */ - float Ytilt; -} wmTabletData; - /** Motion progress, for modal handlers. */ typedef enum { P_NOT_STARTED, diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 59975080f49..383ca806d35 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -1088,7 +1088,7 @@ void wm_gizmomap_modal_set( gz->state |= WM_GIZMO_STATE_MODAL; gzmap->gzmap_context.modal = gz; - if ((gz->flag & WM_GIZMO_MOVE_CURSOR) && (event->is_motion_absolute == false)) { + if ((gz->flag & WM_GIZMO_MOVE_CURSOR) && (event->tablet.is_motion_absolute == false)) { WM_cursor_grab_enable(win, WM_CURSOR_WRAP_XY, true, NULL); copy_v2_v2_int(gzmap->gzmap_context.event_xy, &event->x); gzmap->gzmap_context.event_grabcursor = win->grabcursor; diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c index 8e796a7981a..b82865a727d 100644 --- a/source/blender/windowmanager/intern/wm_cursors.c +++ b/source/blender/windowmanager/intern/wm_cursors.c @@ -304,8 +304,7 @@ void WM_cursor_grab_enable(wmWindow *win, int wrap, bool hide, int bounds[4]) if ((G.debug & G_DEBUG) == 0) { if (win->ghostwin) { - /* Note: There is no tabletdata on Windows if no tablet device is connected. */ - if (win->eventstate->is_motion_absolute == false) { + if (win->eventstate->tablet.is_motion_absolute == false) { GHOST_SetCursorGrab(win->ghostwin, mode, mode_axis, bounds, NULL); } diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 09b7d89fc2b..a26a728461d 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -514,12 +514,12 @@ void wm_draw_region_blend(ARegion *ar, int view, bool blend) /* Slide vertical panels */ float ofs_x = BLI_rcti_size_x(&ar->winrct) * (1.0f - alpha_easing); - if (ar->alignment == RGN_ALIGN_RIGHT) { + if (RGN_ALIGN_ENUM_FROM_MASK(ar->alignment) == RGN_ALIGN_RIGHT) { rect_geo.xmin += ofs_x; rect_tex.xmax *= alpha_easing; alpha = 1.0f; } - else if (ar->alignment == RGN_ALIGN_LEFT) { + else if (RGN_ALIGN_ENUM_FROM_MASK(ar->alignment) == RGN_ALIGN_LEFT) { rect_geo.xmax -= ofs_x; rect_tex.xmin += 1.0f - alpha_easing; alpha = 1.0f; @@ -581,7 +581,14 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo) /* Compute UI layouts for dynamically size regions. */ for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) { - if (ar->visible && ar->do_draw && ar->type && ar->type->layout) { + /* Dynamic region may have been flagged as too small because their size on init is 0. + * ARegion.visible is false then, as expected. The layout should still be created then, so + * the region size can be updated (it may turn out to be not too small then). */ + const bool ignore_visibility = (ar->flag & RGN_FLAG_DYNAMIC_SIZE) && + (ar->flag & RGN_FLAG_TOO_SMALL) && + !(ar->flag & RGN_FLAG_HIDDEN); + + if ((ar->visible || ignore_visibility) && ar->do_draw && ar->type && ar->type->layout) { CTX_wm_region_set(C, ar); ED_region_do_layout(C, ar); CTX_wm_region_set(C, NULL); diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c new file mode 100644 index 00000000000..3cec185fd36 --- /dev/null +++ b/source/blender/windowmanager/intern/wm_event_query.c @@ -0,0 +1,427 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2007 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup wm + * + * Read-only queries utility functions for the event system. + */ + +#include <stdlib.h> +#include <string.h> + +#include "DNA_listBase.h" +#include "DNA_screen_types.h" +#include "DNA_scene_types.h" +#include "DNA_windowmanager_types.h" +#include "DNA_userdef_types.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_math.h" + +#include "BKE_context.h" + +#include "RNA_access.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "wm_event_system.h" +#include "wm_event_types.h" + +#include "RNA_enum_types.h" + +#include "DEG_depsgraph.h" + +/* -------------------------------------------------------------------- */ +/** \name Event Printing + * \{ */ + +/* for debugging only, getting inspecting events manually is tedious */ +void WM_event_print(const wmEvent *event) +{ + if (event) { + const char *unknown = "UNKNOWN"; + const char *type_id = unknown; + const char *val_id = unknown; + + RNA_enum_identifier(rna_enum_event_type_items, event->type, &type_id); + RNA_enum_identifier(rna_enum_event_value_items, event->val, &val_id); + + printf( + "wmEvent type:%d / %s, val:%d / %s,\n" + " shift:%d, ctrl:%d, alt:%d, oskey:%d, keymodifier:%d,\n" + " mouse:(%d,%d), ascii:'%c', utf8:'%.*s', pointer:%p\n", + event->type, + type_id, + event->val, + val_id, + event->shift, + event->ctrl, + event->alt, + event->oskey, + event->keymodifier, + event->x, + event->y, + event->ascii, + BLI_str_utf8_size(event->utf8_buf), + event->utf8_buf, + (const void *)event); + +#ifdef WITH_INPUT_NDOF + if (ISNDOF(event->type)) { + const wmNDOFMotionData *ndof = event->customdata; + if (event->type == NDOF_MOTION) { + printf(" ndof: rot: (%.4f %.4f %.4f), tx: (%.4f %.4f %.4f), dt: %.4f, progress: %u\n", + UNPACK3(ndof->rvec), + UNPACK3(ndof->tvec), + ndof->dt, + ndof->progress); + } + else { + /* ndof buttons printed already */ + } + } +#endif /* WITH_INPUT_NDOF */ + + if (event->tablet.active != EVT_TABLET_NONE) { + const wmTabletData *wmtab = &event->tablet; + printf(" tablet: active: %d, pressure %.4f, tilt: (%.4f %.4f)\n", + wmtab->active, + wmtab->pressure, + wmtab->x_tilt, + wmtab->y_tilt); + } + } + else { + printf("wmEvent - NULL\n"); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Event Modifier/Type Queries + * \{ */ + +int WM_event_modifier_flag(const wmEvent *event) +{ + int flag = 0; + if (event->ctrl) { + flag |= KM_CTRL; + } + if (event->alt) { + flag |= KM_ALT; + } + if (event->shift) { + flag |= KM_SHIFT; + } + if (event->oskey) { + flag |= KM_OSKEY; + } + return flag; +} + +bool WM_event_type_mask_test(const int event_type, const enum eEventType_Mask mask) +{ + /* Keyboard. */ + if (mask & EVT_TYPE_MASK_KEYBOARD) { + if (ISKEYBOARD(event_type)) { + return true; + } + } + else if (mask & EVT_TYPE_MASK_KEYBOARD_MODIFIER) { + if (ISKEYMODIFIER(event_type)) { + return true; + } + } + + /* Mouse. */ + if (mask & EVT_TYPE_MASK_MOUSE) { + if (ISMOUSE(event_type)) { + return true; + } + } + else if (mask & EVT_TYPE_MASK_MOUSE_WHEEL) { + if (ISMOUSE_WHEEL(event_type)) { + return true; + } + } + else if (mask & EVT_TYPE_MASK_MOUSE_GESTURE) { + if (ISMOUSE_GESTURE(event_type)) { + return true; + } + } + + /* Tweak. */ + if (mask & EVT_TYPE_MASK_TWEAK) { + if (ISTWEAK(event_type)) { + return true; + } + } + + /* Action Zone. */ + if (mask & EVT_TYPE_MASK_ACTIONZONE) { + if (IS_EVENT_ACTIONZONE(event_type)) { + return true; + } + } + + return false; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Event Motion Queries + * \{ */ + +/* for modal callbacks, check configuration for how to interpret exit with tweaks */ +bool WM_event_is_modal_tweak_exit(const wmEvent *event, int tweak_event) +{ + /* if the release-confirm userpref setting is enabled, + * tweak events can be canceled when mouse is released + */ + if (U.flag & USER_RELEASECONFIRM) { + /* option on, so can exit with km-release */ + if (event->val == KM_RELEASE) { + switch (tweak_event) { + case EVT_TWEAK_L: + case EVT_TWEAK_M: + case EVT_TWEAK_R: + return 1; + } + } + else { + /* if the initial event wasn't a tweak event then + * ignore USER_RELEASECONFIRM setting: see [#26756] */ + if (ELEM(tweak_event, EVT_TWEAK_L, EVT_TWEAK_M, EVT_TWEAK_R) == 0) { + return 1; + } + } + } + else { + /* this is fine as long as not doing km-release, otherwise + * some items (i.e. markers) being tweaked may end up getting + * dropped all over + */ + if (event->val != KM_RELEASE) { + return 1; + } + } + + return 0; +} + +bool WM_event_is_last_mousemove(const wmEvent *event) +{ + while ((event = event->next)) { + if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { + return false; + } + } + return true; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Event Click/Drag Checks + * + * Values under this limit are detected as clicks. + * + * \{ */ + +int WM_event_drag_threshold(const struct wmEvent *event) +{ + int drag_threshold; + if (WM_event_is_tablet(event)) { + drag_threshold = U.drag_threshold_tablet; + } + else if (ISMOUSE(event->prevtype)) { + drag_threshold = U.drag_threshold_mouse; + } + else { + /* Typically keyboard, could be NDOF button or other less common types. */ + drag_threshold = U.drag_threshold; + } + return drag_threshold * U.dpi_fac; +} + +bool WM_event_drag_test_with_delta(const wmEvent *event, const int drag_delta[2]) +{ + const int drag_threshold = WM_event_drag_threshold(event); + return abs(drag_delta[0]) > drag_threshold || abs(drag_delta[1]) > drag_threshold; +} + +bool WM_event_drag_test(const wmEvent *event, const int prev_xy[2]) +{ + const int drag_delta[2] = { + prev_xy[0] - event->x, + prev_xy[1] - event->y, + }; + return WM_event_drag_test_with_delta(event, drag_delta); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Event Preference Mapping + * \{ */ + +int WM_userdef_event_map(int kmitype) +{ + switch (kmitype) { + case WHEELOUTMOUSE: + return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELUPMOUSE : WHEELDOWNMOUSE; + case WHEELINMOUSE: + return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELDOWNMOUSE : WHEELUPMOUSE; + } + + return kmitype; +} + +/** + * Use so we can check if 'wmEvent.type' is released in modal operators. + * + * An alternative would be to add a 'wmEvent.type_nokeymap'... or similar. + */ +int WM_userdef_event_type_from_keymap_type(int kmitype) +{ + switch (kmitype) { + case EVT_TWEAK_L: + return LEFTMOUSE; + case EVT_TWEAK_M: + return MIDDLEMOUSE; + case EVT_TWEAK_R: + return RIGHTMOUSE; + case WHEELOUTMOUSE: + return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELUPMOUSE : WHEELDOWNMOUSE; + case WHEELINMOUSE: + return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELDOWNMOUSE : WHEELUPMOUSE; + } + + return kmitype; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Event NDOF Input Access + * \{ */ + +#ifdef WITH_INPUT_NDOF + +void WM_event_ndof_pan_get(const wmNDOFMotionData *ndof, float r_pan[3], const bool use_zoom) +{ + int z_flag = use_zoom ? NDOF_ZOOM_INVERT : NDOF_PANZ_INVERT_AXIS; + r_pan[0] = ndof->tvec[0] * ((U.ndof_flag & NDOF_PANX_INVERT_AXIS) ? -1.0f : 1.0f); + r_pan[1] = ndof->tvec[1] * ((U.ndof_flag & NDOF_PANY_INVERT_AXIS) ? -1.0f : 1.0f); + r_pan[2] = ndof->tvec[2] * ((U.ndof_flag & z_flag) ? -1.0f : 1.0f); +} + +void WM_event_ndof_rotate_get(const wmNDOFMotionData *ndof, float r_rot[3]) +{ + r_rot[0] = ndof->rvec[0] * ((U.ndof_flag & NDOF_ROTX_INVERT_AXIS) ? -1.0f : 1.0f); + r_rot[1] = ndof->rvec[1] * ((U.ndof_flag & NDOF_ROTY_INVERT_AXIS) ? -1.0f : 1.0f); + r_rot[2] = ndof->rvec[2] * ((U.ndof_flag & NDOF_ROTZ_INVERT_AXIS) ? -1.0f : 1.0f); +} + +float WM_event_ndof_to_axis_angle(const struct wmNDOFMotionData *ndof, float axis[3]) +{ + float angle; + angle = normalize_v3_v3(axis, ndof->rvec); + + axis[0] = axis[0] * ((U.ndof_flag & NDOF_ROTX_INVERT_AXIS) ? -1.0f : 1.0f); + axis[1] = axis[1] * ((U.ndof_flag & NDOF_ROTY_INVERT_AXIS) ? -1.0f : 1.0f); + axis[2] = axis[2] * ((U.ndof_flag & NDOF_ROTZ_INVERT_AXIS) ? -1.0f : 1.0f); + + return ndof->dt * angle; +} + +void WM_event_ndof_to_quat(const struct wmNDOFMotionData *ndof, float q[4]) +{ + float axis[3]; + float angle; + + angle = WM_event_ndof_to_axis_angle(ndof, axis); + axis_angle_to_quat(q, axis, angle); +} +#endif /* WITH_INPUT_NDOF */ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Event Tablet Input Access + * \{ */ + +/* applies the global tablet pressure correction curve */ +float wm_pressure_curve(float pressure) +{ + if (U.pressure_threshold_max != 0.0f) { + pressure /= U.pressure_threshold_max; + } + + CLAMP(pressure, 0.0f, 1.0f); + + if (U.pressure_softness != 0.0f) { + pressure = powf(pressure, powf(4.0f, -U.pressure_softness)); + } + + return pressure; +} + +/* if this is a tablet event, return tablet pressure and set *pen_flip + * to 1 if the eraser tool is being used, 0 otherwise */ +float WM_event_tablet_data(const wmEvent *event, int *pen_flip, float tilt[2]) +{ + if (tilt) { + tilt[0] = event->tablet.x_tilt; + tilt[1] = event->tablet.y_tilt; + } + + if (pen_flip) { + (*pen_flip) = (event->tablet.active == EVT_TABLET_ERASER); + } + + return event->tablet.pressure; +} + +bool WM_event_is_tablet(const struct wmEvent *event) +{ + return (event->tablet.active != EVT_TABLET_NONE); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Event IME Input Access + * \{ */ + +#ifdef WITH_INPUT_IME +/* most os using ctrl/oskey + space to switch ime, avoid added space */ +bool WM_event_is_ime_switch(const struct wmEvent *event) +{ + return event->val == KM_PRESS && event->type == SPACEKEY && + (event->ctrl || event->oskey || event->shift || event->alt); +} +#endif + +/** \} */ diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index f5970e8fb61..ba1f34478ed 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -70,7 +70,6 @@ #include "RNA_access.h" #include "UI_interface.h" -#include "UI_view2d.h" #include "PIL_time.h" @@ -84,8 +83,6 @@ #include "wm_event_system.h" #include "wm_event_types.h" -#include "RNA_enum_types.h" - #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -104,7 +101,6 @@ #define USE_GIZMO_MOUSE_PRIORITY_HACK static void wm_notifier_clear(wmNotifier *note); -static void update_tablet_data(wmWindow *win, wmEvent *event); static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, @@ -114,6 +110,8 @@ static int wm_operator_call_internal(bContext *C, const bool poll_only, wmEvent *event); +static bool wm_operator_check_locked_interface(bContext *C, wmOperatorType *ot); + /* -------------------------------------------------------------------- */ /** \name Event Management * \{ */ @@ -126,14 +124,6 @@ wmEvent *wm_event_add_ex(wmWindow *win, *event = *event_to_add; - update_tablet_data(win, event); - - if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { - /* We could have a preference to support relative tablet motion (we can't detect that). */ - event->is_motion_absolute = ((event->tablet_data != NULL) && - (event->tablet_data->Active != GHOST_kTabletModeNone)); - } - if (event_to_add_after == NULL) { BLI_addtail(&win->queue, event); } @@ -177,10 +167,6 @@ void wm_event_free(wmEvent *event) } } - if (event->tablet_data) { - MEM_freeN((void *)event->tablet_data); - } - MEM_freeN(event); } @@ -195,9 +181,6 @@ void wm_event_free_all(wmWindow *win) void wm_event_init_from_window(wmWindow *win, wmEvent *event) { - /* make sure we don't copy any owned pointers */ - BLI_assert(win->eventstate->tablet_data == NULL); - *event = *(win->eventstate); } @@ -682,6 +665,82 @@ static void wm_handler_ui_cancel(bContext *C) /** \} */ /* -------------------------------------------------------------------- */ +/** \name WM Reports + * + * Access to #wmWindowManager.reports + * \{ */ + +/** + * Show the report in the info header. + */ +void WM_report_banner_show(void) +{ + wmWindowManager *wm = G_MAIN->wm.first; + ReportList *wm_reports = &wm->reports; + ReportTimerInfo *rti; + + /* After adding reports to the global list, reset the report timer. */ + WM_event_remove_timer(wm, NULL, wm_reports->reporttimer); + + /* Records time since last report was added */ + wm_reports->reporttimer = WM_event_add_timer(wm, wm->winactive, TIMERREPORT, 0.05); + + rti = MEM_callocN(sizeof(ReportTimerInfo), "ReportTimerInfo"); + wm_reports->reporttimer->customdata = rti; +} + +#ifdef WITH_INPUT_NDOF +void WM_ndof_deadzone_set(float deadzone) +{ + GHOST_setNDOFDeadZone(deadzone); +} +#endif + +static void wm_add_reports(ReportList *reports) +{ + /* if the caller owns them, handle this */ + if (reports->list.first && (reports->flag & RPT_OP_HOLD) == 0) { + wmWindowManager *wm = G_MAIN->wm.first; + + /* add reports to the global list, otherwise they are not seen */ + BLI_movelisttolist(&wm->reports.list, &reports->list); + + WM_report_banner_show(); + } +} + +void WM_report(ReportType type, const char *message) +{ + ReportList reports; + + BKE_reports_init(&reports, RPT_STORE); + BKE_report(&reports, type, message); + + wm_add_reports(&reports); + + BKE_reports_clear(&reports); +} + +void WM_reportf(ReportType type, const char *format, ...) +{ + DynStr *ds; + va_list args; + + ds = BLI_dynstr_new(); + va_start(args, format); + BLI_dynstr_vappendf(ds, format, args); + va_end(args); + + char *str = BLI_dynstr_get_cstring(ds); + WM_report(type, str); + MEM_freeN(str); + + BLI_dynstr_free(ds); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Operator Logic * \{ */ @@ -762,164 +821,6 @@ void WM_operator_region_active_win_set(bContext *C) } } -int WM_event_modifier_flag(const wmEvent *event) -{ - int flag = 0; - if (event->ctrl) { - flag |= KM_CTRL; - } - if (event->alt) { - flag |= KM_ALT; - } - if (event->shift) { - flag |= KM_SHIFT; - } - if (event->oskey) { - flag |= KM_OSKEY; - } - return flag; -} - -/* for debugging only, getting inspecting events manually is tedious */ -void WM_event_print(const wmEvent *event) -{ - if (event) { - const char *unknown = "UNKNOWN"; - const char *type_id = unknown; - const char *val_id = unknown; - - RNA_enum_identifier(rna_enum_event_type_items, event->type, &type_id); - RNA_enum_identifier(rna_enum_event_value_items, event->val, &val_id); - - printf( - "wmEvent type:%d / %s, val:%d / %s,\n" - " shift:%d, ctrl:%d, alt:%d, oskey:%d, keymodifier:%d,\n" - " mouse:(%d,%d), ascii:'%c', utf8:'%.*s', keymap_idname:%s, pointer:%p\n", - event->type, - type_id, - event->val, - val_id, - event->shift, - event->ctrl, - event->alt, - event->oskey, - event->keymodifier, - event->x, - event->y, - event->ascii, - BLI_str_utf8_size(event->utf8_buf), - event->utf8_buf, - event->keymap_idname, - (const void *)event); - -#ifdef WITH_INPUT_NDOF - if (ISNDOF(event->type)) { - const wmNDOFMotionData *ndof = event->customdata; - if (event->type == NDOF_MOTION) { - printf(" ndof: rot: (%.4f %.4f %.4f), tx: (%.4f %.4f %.4f), dt: %.4f, progress: %u\n", - UNPACK3(ndof->rvec), - UNPACK3(ndof->tvec), - ndof->dt, - ndof->progress); - } - else { - /* ndof buttons printed already */ - } - } -#endif /* WITH_INPUT_NDOF */ - - if (event->tablet_data) { - const wmTabletData *wmtab = event->tablet_data; - printf(" tablet: active: %d, pressure %.4f, tilt: (%.4f %.4f)\n", - wmtab->Active, - wmtab->Pressure, - wmtab->Xtilt, - wmtab->Ytilt); - } - } - else { - printf("wmEvent - NULL\n"); - } -} - -/** - * Show the report in the info header. - */ -void WM_report_banner_show(void) -{ - wmWindowManager *wm = G_MAIN->wm.first; - ReportList *wm_reports = &wm->reports; - ReportTimerInfo *rti; - - /* After adding reports to the global list, reset the report timer. */ - WM_event_remove_timer(wm, NULL, wm_reports->reporttimer); - - /* Records time since last report was added */ - wm_reports->reporttimer = WM_event_add_timer(wm, wm->winactive, TIMERREPORT, 0.05); - - rti = MEM_callocN(sizeof(ReportTimerInfo), "ReportTimerInfo"); - wm_reports->reporttimer->customdata = rti; -} - -bool WM_event_is_last_mousemove(const wmEvent *event) -{ - while ((event = event->next)) { - if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { - return false; - } - } - return true; -} - -#ifdef WITH_INPUT_NDOF -void WM_ndof_deadzone_set(float deadzone) -{ - GHOST_setNDOFDeadZone(deadzone); -} -#endif - -static void wm_add_reports(ReportList *reports) -{ - /* if the caller owns them, handle this */ - if (reports->list.first && (reports->flag & RPT_OP_HOLD) == 0) { - wmWindowManager *wm = G_MAIN->wm.first; - - /* add reports to the global list, otherwise they are not seen */ - BLI_movelisttolist(&wm->reports.list, &reports->list); - - WM_report_banner_show(); - } -} - -void WM_report(ReportType type, const char *message) -{ - ReportList reports; - - BKE_reports_init(&reports, RPT_STORE); - BKE_report(&reports, type, message); - - wm_add_reports(&reports); - - BKE_reports_clear(&reports); -} - -void WM_reportf(ReportType type, const char *format, ...) -{ - DynStr *ds; - va_list args; - - ds = BLI_dynstr_new(); - va_start(args, format); - BLI_dynstr_vappendf(ds, format, args); - va_end(args); - - char *str = BLI_dynstr_get_cstring(ds); - WM_report(type, str); - MEM_freeN(str); - - BLI_dynstr_free(ds); -} - /* (caller_owns_reports == true) when called from python */ static void wm_operator_reports(bContext *C, wmOperator *op, int retval, bool caller_owns_reports) { @@ -1301,105 +1202,6 @@ static void wm_region_mouse_co(bContext *C, wmEvent *event) } } -#if 1 /* may want to disable operator remembering previous state for testing */ - -static bool operator_last_properties_init_impl(wmOperator *op, IDProperty *last_properties) -{ - bool changed = false; - IDPropertyTemplate val = {0}; - IDProperty *replaceprops = IDP_New(IDP_GROUP, &val, "wmOperatorProperties"); - PropertyRNA *iterprop; - - CLOG_INFO(WM_LOG_OPERATORS, 1, "loading previous properties for '%s'", op->type->idname); - - iterprop = RNA_struct_iterator_property(op->type->srna); - - RNA_PROP_BEGIN (op->ptr, itemptr, iterprop) { - PropertyRNA *prop = itemptr.data; - if ((RNA_property_flag(prop) & PROP_SKIP_SAVE) == 0) { - if (!RNA_property_is_set(op->ptr, prop)) { /* don't override a setting already set */ - const char *identifier = RNA_property_identifier(prop); - IDProperty *idp_src = IDP_GetPropertyFromGroup(last_properties, identifier); - if (idp_src) { - IDProperty *idp_dst = IDP_CopyProperty(idp_src); - - /* note - in the future this may need to be done recursively, - * but for now RNA doesn't access nested operators */ - idp_dst->flag |= IDP_FLAG_GHOST; - - /* add to temporary group instead of immediate replace, - * because we are iterating over this group */ - IDP_AddToGroup(replaceprops, idp_dst); - changed = true; - } - } - } - } - RNA_PROP_END; - - IDP_MergeGroup(op->properties, replaceprops, true); - IDP_FreeProperty(replaceprops); - return changed; -} - -bool WM_operator_last_properties_init(wmOperator *op) -{ - bool changed = false; - if (op->type->last_properties) { - changed |= operator_last_properties_init_impl(op, op->type->last_properties); - for (wmOperator *opm = op->macro.first; opm; opm = opm->next) { - IDProperty *idp_src = IDP_GetPropertyFromGroup(op->type->last_properties, opm->idname); - if (idp_src) { - changed |= operator_last_properties_init_impl(opm, idp_src); - } - } - } - return changed; -} - -bool WM_operator_last_properties_store(wmOperator *op) -{ - if (op->type->last_properties) { - IDP_FreeProperty(op->type->last_properties); - op->type->last_properties = NULL; - } - - if (op->properties) { - CLOG_INFO(WM_LOG_OPERATORS, 1, "storing properties for '%s'", op->type->idname); - op->type->last_properties = IDP_CopyProperty(op->properties); - } - - if (op->macro.first != NULL) { - for (wmOperator *opm = op->macro.first; opm; opm = opm->next) { - if (opm->properties) { - if (op->type->last_properties == NULL) { - op->type->last_properties = IDP_New( - IDP_GROUP, &(IDPropertyTemplate){0}, "wmOperatorProperties"); - } - IDProperty *idp_macro = IDP_CopyProperty(opm->properties); - STRNCPY(idp_macro->name, opm->type->idname); - IDP_ReplaceInGroup(op->type->last_properties, idp_macro); - } - } - } - - return (op->type->last_properties != NULL); -} - -#else - -bool WM_operator_last_properties_init(wmOperator *UNUSED(op)) -{ - return false; -} - -bool WM_operator_last_properties_store(wmOperator *UNUSED(op)) -{ - return false; -} - -#endif - /** * Also used for exec when 'event' is NULL. */ @@ -1954,42 +1756,6 @@ void WM_event_remove_handlers(bContext *C, ListBase *handlers) } } -/* do userdef mappings */ -int WM_userdef_event_map(int kmitype) -{ - switch (kmitype) { - case WHEELOUTMOUSE: - return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELUPMOUSE : WHEELDOWNMOUSE; - case WHEELINMOUSE: - return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELDOWNMOUSE : WHEELUPMOUSE; - } - - return kmitype; -} - -/** - * Use so we can check if 'wmEvent.type' is released in modal operators. - * - * An alternative would be to add a 'wmEvent.type_nokeymap'... or similar. - */ -int WM_userdef_event_type_from_keymap_type(int kmitype) -{ - switch (kmitype) { - case EVT_TWEAK_L: - return LEFTMOUSE; - case EVT_TWEAK_M: - return MIDDLEMOUSE; - case EVT_TWEAK_R: - return RIGHTMOUSE; - case WHEELOUTMOUSE: - return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELUPMOUSE : WHEELDOWNMOUSE; - case WHEELINMOUSE: - return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELDOWNMOUSE : WHEELUPMOUSE; - } - - return kmitype; -} - static bool wm_eventmatch(const wmEvent *winevent, const wmKeyMapItem *kmi) { if (kmi->flag & KMI_INACTIVE) { @@ -2011,19 +1777,16 @@ static bool wm_eventmatch(const wmEvent *winevent, const wmKeyMapItem *kmi) if (kmitype != KM_ANY) { if (ELEM(kmitype, TABLET_STYLUS, TABLET_ERASER)) { - const wmTabletData *wmtab = winevent->tablet_data; + const wmTabletData *wmtab = &winevent->tablet; - if (wmtab == NULL) { - return false; - } - else if (winevent->type != LEFTMOUSE) { + if (winevent->type != LEFTMOUSE) { /* tablet events can occur on hover + keypress */ return false; } - else if ((kmitype == TABLET_STYLUS) && (wmtab->Active != EVT_TABLET_STYLUS)) { + else if ((kmitype == TABLET_STYLUS) && (wmtab->active != EVT_TABLET_STYLUS)) { return false; } - else if ((kmitype == TABLET_ERASER) && (wmtab->Active != EVT_TABLET_ERASER)) { + else if ((kmitype == TABLET_ERASER) && (wmtab->active != EVT_TABLET_ERASER)) { return false; } } @@ -2103,10 +1866,10 @@ static wmKeyMapItem *wm_eventmatch_modal_keymap_items(const wmKeyMap *keymap, * This is done since we only want to use double click events to match key-map items, * allowing modal functions to check for press/release events without having to interpret them. */ -static void wm_event_modalkeymap(const bContext *C, - wmOperator *op, - wmEvent *event, - bool *dbl_click_disabled) +static void wm_event_modalkeymap_begin(const bContext *C, + wmOperator *op, + wmEvent *event, + bool *dbl_click_disabled) { BLI_assert(event->type != EVT_MODAL_MAP); @@ -2159,25 +1922,13 @@ static void wm_event_modalkeymap(const bContext *C, } /** - * Check whether operator is allowed to run in case interface is locked, - * If interface is unlocked, will always return truth. + * Restore changes from #wm_event_modalkeymap_begin + * + * \warning bad hacking event system... + * better restore event type for checking of #KM_CLICK for example. + * Modal maps could use different method (ton). */ -static bool wm_operator_check_locked_interface(bContext *C, wmOperatorType *ot) -{ - wmWindowManager *wm = CTX_wm_manager(C); - - if (wm->is_interface_locked) { - if ((ot->flag & OPTYPE_LOCK_BYPASS) == 0) { - return false; - } - } - - return true; -} - -/* bad hacking event system... better restore event type for checking of KM_CLICK for example */ -/* XXX modal maps could use different method (ton) */ -static void wm_event_modalmap_end(wmEvent *event, bool dbl_click_disabled) +static void wm_event_modalkeymap_end(wmEvent *event, bool dbl_click_disabled) { if (event->type == EVT_MODAL_MAP) { event->type = event->prevtype; @@ -2196,7 +1947,8 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHandler *handler_base, wmEvent *event, - PointerRNA *properties) + PointerRNA *properties, + const char *kmi_idname) { int retval = OPERATOR_PASS_THROUGH; @@ -2221,7 +1973,7 @@ static int wm_handler_operator_call(bContext *C, wm_handler_op_context(C, handler, event); wm_region_mouse_co(C, event); - wm_event_modalkeymap(C, op, event, &dbl_click_disabled); + wm_event_modalkeymap_begin(C, op, event, &dbl_click_disabled); if (ot->flag & OPTYPE_UNDO) { wm->op_undo_depth++; @@ -2236,7 +1988,7 @@ static int wm_handler_operator_call(bContext *C, * the event, operator etc have all been freed. - campbell */ if (CTX_wm_manager(C) == wm) { - wm_event_modalmap_end(event, dbl_click_disabled); + wm_event_modalkeymap_end(event, dbl_click_disabled); if (ot->flag & OPTYPE_UNDO) { wm->op_undo_depth--; @@ -2298,7 +2050,7 @@ static int wm_handler_operator_call(bContext *C, } } else { - wmOperatorType *ot = WM_operatortype_find(event->keymap_idname, 0); + wmOperatorType *ot = WM_operatortype_find(kmi_idname, 0); if (ot && wm_operator_check_locked_interface(C, ot)) { bool use_last_properties = true; @@ -2636,10 +2388,8 @@ static int wm_handlers_do_keymap_with_keymap_handler( PRINT("%s: item matched '%s'\n", __func__, kmi->idname); - /* weak, but allows interactive callback to not use rawkey */ - event->keymap_idname = kmi->idname; - - action |= wm_handler_operator_call(C, handlers, &handler->head, event, kmi->ptr); + action |= wm_handler_operator_call( + C, handlers, &handler->head, event, kmi->ptr, kmi->idname); if (action & WM_HANDLER_BREAK) { /* not always_pass here, it denotes removed handler_base */ @@ -2693,13 +2443,11 @@ static int wm_handlers_do_keymap_with_gizmo_handler( if (wm_eventmatch(event, kmi)) { PRINT("%s: item matched '%s'\n", __func__, kmi->idname); - /* weak, but allows interactive callback to not use rawkey */ - event->keymap_idname = kmi->idname; - CTX_wm_gizmo_group_set(C, gzgroup); /* handler->op is called later, we want keymap op to be triggered here */ - action |= wm_handler_operator_call(C, handlers, &handler->head, event, kmi->ptr); + action |= wm_handler_operator_call( + C, handlers, &handler->head, event, kmi->ptr, kmi->idname); CTX_wm_gizmo_group_set(C, NULL); @@ -2990,7 +2738,7 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers } } else { - action |= wm_handler_operator_call(C, handlers, handler_base, event, NULL); + action |= wm_handler_operator_call(C, handlers, handler_base, event, NULL, NULL); } } else { @@ -4099,91 +3847,6 @@ void WM_event_add_mousemove(const bContext *C) window->addmousemove = 1; } -/* for modal callbacks, check configuration for how to interpret exit with tweaks */ -bool WM_event_is_modal_tweak_exit(const wmEvent *event, int tweak_event) -{ - /* if the release-confirm userpref setting is enabled, - * tweak events can be canceled when mouse is released - */ - if (U.flag & USER_RELEASECONFIRM) { - /* option on, so can exit with km-release */ - if (event->val == KM_RELEASE) { - switch (tweak_event) { - case EVT_TWEAK_L: - case EVT_TWEAK_M: - case EVT_TWEAK_R: - return 1; - } - } - else { - /* if the initial event wasn't a tweak event then - * ignore USER_RELEASECONFIRM setting: see [#26756] */ - if (ELEM(tweak_event, EVT_TWEAK_L, EVT_TWEAK_M, EVT_TWEAK_R) == 0) { - return 1; - } - } - } - else { - /* this is fine as long as not doing km-release, otherwise - * some items (i.e. markers) being tweaked may end up getting - * dropped all over - */ - if (event->val != KM_RELEASE) { - return 1; - } - } - - return 0; -} - -bool WM_event_type_mask_test(const int event_type, const enum eEventType_Mask mask) -{ - /* Keyboard. */ - if (mask & EVT_TYPE_MASK_KEYBOARD) { - if (ISKEYBOARD(event_type)) { - return true; - } - } - else if (mask & EVT_TYPE_MASK_KEYBOARD_MODIFIER) { - if (ISKEYMODIFIER(event_type)) { - return true; - } - } - - /* Mouse. */ - if (mask & EVT_TYPE_MASK_MOUSE) { - if (ISMOUSE(event_type)) { - return true; - } - } - else if (mask & EVT_TYPE_MASK_MOUSE_WHEEL) { - if (ISMOUSE_WHEEL(event_type)) { - return true; - } - } - else if (mask & EVT_TYPE_MASK_MOUSE_GESTURE) { - if (ISMOUSE_GESTURE(event_type)) { - return true; - } - } - - /* Tweak. */ - if (mask & EVT_TYPE_MASK_TWEAK) { - if (ISTWEAK(event_type)) { - return true; - } - } - - /* Action Zone. */ - if (mask & EVT_TYPE_MASK_ACTIONZONE) { - if (IS_EVENT_ACTIONZONE(event_type)) { - return true; - } - } - - return false; -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -4421,41 +4084,23 @@ static void wm_eventemulation(wmEvent *event, bool test_only) } } -/* applies the global tablet pressure correction curve */ -float wm_pressure_curve(float pressure) +void wm_tablet_data_from_ghost(const GHOST_TabletData *tablet_data, wmTabletData *wmtab) { - if (U.pressure_threshold_max != 0.0f) { - pressure /= U.pressure_threshold_max; - } - - CLAMP(pressure, 0.0f, 1.0f); - - if (U.pressure_softness != 0.0f) { - pressure = powf(pressure, powf(4.0f, -U.pressure_softness)); - } - - return pressure; -} - -/* adds customdata to event */ -static void update_tablet_data(wmWindow *win, wmEvent *event) -{ - const GHOST_TabletData *td = GHOST_GetTabletData(win->ghostwin); - - /* if there's tablet data from an active tablet device then add it */ - if ((td != NULL) && td->Active != GHOST_kTabletModeNone) { - struct wmTabletData *wmtab = MEM_mallocN(sizeof(wmTabletData), "customdata tablet"); - - wmtab->Active = (int)td->Active; - wmtab->Pressure = wm_pressure_curve(td->Pressure); - wmtab->Xtilt = td->Xtilt; - wmtab->Ytilt = td->Ytilt; - - event->tablet_data = wmtab; - // printf("%s: using tablet %.5f\n", __func__, wmtab->Pressure); + if ((tablet_data != NULL) && tablet_data->Active != GHOST_kTabletModeNone) { + wmtab->active = (int)tablet_data->Active; + wmtab->pressure = wm_pressure_curve(tablet_data->Pressure); + wmtab->x_tilt = tablet_data->Xtilt; + wmtab->y_tilt = tablet_data->Ytilt; + /* We could have a preference to support relative tablet motion (we can't detect that). */ + wmtab->is_motion_absolute = true; + // printf("%s: using tablet %.5f\n", __func__, wmtab->pressure); } else { - event->tablet_data = NULL; + wmtab->active = EVT_TABLET_NONE; + wmtab->pressure = 1.0f; + wmtab->x_tilt = 0.0f; + wmtab->y_tilt = 0.0f; + wmtab->is_motion_absolute = false; // printf("%s: not using tablet\n", __func__); } } @@ -4607,6 +4252,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void copy_v2_v2_int(&event.x, &cd->x); wm_stereo3d_mouse_offset_apply(win, &event.x); + wm_tablet_data_from_ghost(&cd->tablet, &event.tablet); event.prevtype = event.type; event.prevval = event.val; @@ -4614,7 +4260,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void { wmEvent *event_new = wm_event_add_mousemove(win, &event); copy_v2_v2_int(&evt->x, &event_new->x); - evt->is_motion_absolute = event_new->is_motion_absolute; + evt->tablet.is_motion_absolute = event_new->tablet.is_motion_absolute; } /* also add to other window if event is there, this makes overdraws disappear nicely */ @@ -4632,7 +4278,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void { wmEvent *event_new = wm_event_add_mousemove(owin, &oevent); copy_v2_v2_int(&oevt->x, &event_new->x); - oevt->is_motion_absolute = event_new->is_motion_absolute; + oevt->tablet.is_motion_absolute = event_new->tablet.is_motion_absolute; } } @@ -4696,6 +4342,9 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void event.type = MIDDLEMOUSE; } + /* Get tablet data. */ + wm_tablet_data_from_ghost(&bd->tablet, &event.tablet); + wm_eventemulation(&event, false); /* copy previous state to prev event state (two old!) */ @@ -4706,17 +4355,6 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void evt->val = event.val; evt->type = event.type; - if (win->active == 0) { - int cx, cy; - - /* Entering window, update mouse pos. - * (ghost sends win-activate *after* the mouseclick in window!) */ - wm_get_cursor_position(win, &cx, &cy); - - event.x = evt->x = cx; - event.y = evt->y = cy; - } - /* double click test */ if (wm_event_is_double_click(&event, evt)) { CLOG_INFO(WM_LOG_HANDLERS, 1, "Send double click"); @@ -4737,6 +4375,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void oevent.y = event.y; oevent.type = event.type; oevent.val = event.val; + oevent.tablet = event.tablet; wm_event_add(owin, &oevent); } @@ -5003,6 +4642,29 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void #endif } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name WM Interface Locking + * \{ */ + +/** + * Check whether operator is allowed to run in case interface is locked, + * If interface is unlocked, will always return truth. + */ +static bool wm_operator_check_locked_interface(bContext *C, wmOperatorType *ot) +{ + wmWindowManager *wm = CTX_wm_manager(C); + + if (wm->is_interface_locked) { + if ((ot->flag & OPTYPE_LOCK_BYPASS) == 0) { + return false; + } + } + + return true; +} + void WM_set_locked_interface(wmWindowManager *wm, bool lock) { /* This will prevent events from being handled while interface is locked @@ -5024,96 +4686,12 @@ void WM_set_locked_interface(wmWindowManager *wm, bool lock) BKE_spacedata_draw_locks(lock); } -#ifdef WITH_INPUT_NDOF +/** \} */ /* -------------------------------------------------------------------- */ -/** \name NDOF Utility Functions +/** \name Event / Keymap Matching API * \{ */ -void WM_event_ndof_pan_get(const wmNDOFMotionData *ndof, float r_pan[3], const bool use_zoom) -{ - int z_flag = use_zoom ? NDOF_ZOOM_INVERT : NDOF_PANZ_INVERT_AXIS; - r_pan[0] = ndof->tvec[0] * ((U.ndof_flag & NDOF_PANX_INVERT_AXIS) ? -1.0f : 1.0f); - r_pan[1] = ndof->tvec[1] * ((U.ndof_flag & NDOF_PANY_INVERT_AXIS) ? -1.0f : 1.0f); - r_pan[2] = ndof->tvec[2] * ((U.ndof_flag & z_flag) ? -1.0f : 1.0f); -} - -void WM_event_ndof_rotate_get(const wmNDOFMotionData *ndof, float r_rot[3]) -{ - r_rot[0] = ndof->rvec[0] * ((U.ndof_flag & NDOF_ROTX_INVERT_AXIS) ? -1.0f : 1.0f); - r_rot[1] = ndof->rvec[1] * ((U.ndof_flag & NDOF_ROTY_INVERT_AXIS) ? -1.0f : 1.0f); - r_rot[2] = ndof->rvec[2] * ((U.ndof_flag & NDOF_ROTZ_INVERT_AXIS) ? -1.0f : 1.0f); -} - -float WM_event_ndof_to_axis_angle(const struct wmNDOFMotionData *ndof, float axis[3]) -{ - float angle; - angle = normalize_v3_v3(axis, ndof->rvec); - - axis[0] = axis[0] * ((U.ndof_flag & NDOF_ROTX_INVERT_AXIS) ? -1.0f : 1.0f); - axis[1] = axis[1] * ((U.ndof_flag & NDOF_ROTY_INVERT_AXIS) ? -1.0f : 1.0f); - axis[2] = axis[2] * ((U.ndof_flag & NDOF_ROTZ_INVERT_AXIS) ? -1.0f : 1.0f); - - return ndof->dt * angle; -} - -void WM_event_ndof_to_quat(const struct wmNDOFMotionData *ndof, float q[4]) -{ - float axis[3]; - float angle; - - angle = WM_event_ndof_to_axis_angle(ndof, axis); - axis_angle_to_quat(q, axis, angle); -} -#endif /* WITH_INPUT_NDOF */ - -/* if this is a tablet event, return tablet pressure and set *pen_flip - * to 1 if the eraser tool is being used, 0 otherwise */ -float WM_event_tablet_data(const wmEvent *event, int *pen_flip, float tilt[2]) -{ - int erasor = 0; - float pressure = 1; - - if (tilt) { - zero_v2(tilt); - } - - if (event->tablet_data) { - const wmTabletData *wmtab = event->tablet_data; - - erasor = (wmtab->Active == EVT_TABLET_ERASER); - if (wmtab->Active != EVT_TABLET_NONE) { - pressure = wmtab->Pressure; - if (tilt) { - tilt[0] = wmtab->Xtilt; - tilt[1] = wmtab->Ytilt; - } - } - } - - if (pen_flip) { - (*pen_flip) = erasor; - } - - return pressure; -} - -bool WM_event_is_tablet(const struct wmEvent *event) -{ - return (event->tablet_data) ? true : false; -} - -#ifdef WITH_INPUT_IME -/* most os using ctrl/oskey + space to switch ime, avoid added space */ -bool WM_event_is_ime_switch(const struct wmEvent *event) -{ - return event->val == KM_PRESS && event->type == SPACEKEY && - (event->ctrl || event->oskey || event->shift || event->alt); -} -#endif - -/** \} */ - wmKeyMap *WM_event_get_keymap_from_handler(wmWindowManager *wm, wmEventHandler_Keymap *handler) { wmKeyMap *keymap; @@ -5141,10 +4719,10 @@ wmKeyMapItem *WM_event_match_keymap_item(bContext *C, wmKeyMap *keymap, const wm return NULL; } -static wmKeyMapItem *wm_kmi_from_event(bContext *C, - wmWindowManager *wm, - ListBase *handlers, - const wmEvent *event) +wmKeyMapItem *WM_event_match_keymap_item_from_handlers(bContext *C, + wmWindowManager *wm, + ListBase *handlers, + const wmEvent *event) { LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) { /* during this loop, ui handlers for nested menus can tag multiple handlers free */ @@ -5167,6 +4745,8 @@ static wmKeyMapItem *wm_kmi_from_event(bContext *C, return NULL; } +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Cursor Keymap Status * @@ -5379,7 +4959,7 @@ void WM_window_cursor_keymap_status_refresh(bContext *C, wmWindow *win) wm_eventemulation(&test_event, true); wmKeyMapItem *kmi = NULL; for (int handler_index = 0; handler_index < ARRAY_SIZE(handlers); handler_index++) { - kmi = wm_kmi_from_event(C, wm, handlers[handler_index], &test_event); + kmi = WM_event_match_keymap_item_from_handlers(C, wm, handlers[handler_index], &test_event); if (kmi) { break; } @@ -5475,43 +5055,3 @@ bool WM_window_modal_keymap_status_draw(bContext *UNUSED(C), wmWindow *win, uiLa } /** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Event Click/Drag Checks - * - * Values under this limit are detected as clicks. - * - * \{ */ - -int WM_event_drag_threshold(const struct wmEvent *event) -{ - int drag_threshold; - if (WM_event_is_tablet(event)) { - drag_threshold = U.drag_threshold_tablet; - } - else if (ISMOUSE(event->prevtype)) { - drag_threshold = U.drag_threshold_mouse; - } - else { - /* Typically keyboard, could be NDOF button or other less common types. */ - drag_threshold = U.drag_threshold; - } - return drag_threshold * U.dpi_fac; -} - -bool WM_event_drag_test_with_delta(const wmEvent *event, const int drag_delta[2]) -{ - const int drag_threshold = WM_event_drag_threshold(event); - return abs(drag_delta[0]) > drag_threshold || abs(drag_delta[1]) > drag_threshold; -} - -bool WM_event_drag_test(const wmEvent *event, const int prev_xy[2]) -{ - const int drag_delta[2] = { - prev_xy[0] - event->x, - prev_xy[1] - event->y, - }; - return WM_event_drag_test_with_delta(event, drag_delta); -} - -/** \} */ diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 678b7d9dcee..5837e8e952c 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -720,6 +720,111 @@ void WM_operator_properties_free(PointerRNA *ptr) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Operator Last Properties API + * \{ */ + +#if 1 /* may want to disable operator remembering previous state for testing */ + +static bool operator_last_properties_init_impl(wmOperator *op, IDProperty *last_properties) +{ + bool changed = false; + IDPropertyTemplate val = {0}; + IDProperty *replaceprops = IDP_New(IDP_GROUP, &val, "wmOperatorProperties"); + PropertyRNA *iterprop; + + CLOG_INFO(WM_LOG_OPERATORS, 1, "loading previous properties for '%s'", op->type->idname); + + iterprop = RNA_struct_iterator_property(op->type->srna); + + RNA_PROP_BEGIN (op->ptr, itemptr, iterprop) { + PropertyRNA *prop = itemptr.data; + if ((RNA_property_flag(prop) & PROP_SKIP_SAVE) == 0) { + if (!RNA_property_is_set(op->ptr, prop)) { /* don't override a setting already set */ + const char *identifier = RNA_property_identifier(prop); + IDProperty *idp_src = IDP_GetPropertyFromGroup(last_properties, identifier); + if (idp_src) { + IDProperty *idp_dst = IDP_CopyProperty(idp_src); + + /* note - in the future this may need to be done recursively, + * but for now RNA doesn't access nested operators */ + idp_dst->flag |= IDP_FLAG_GHOST; + + /* add to temporary group instead of immediate replace, + * because we are iterating over this group */ + IDP_AddToGroup(replaceprops, idp_dst); + changed = true; + } + } + } + } + RNA_PROP_END; + + IDP_MergeGroup(op->properties, replaceprops, true); + IDP_FreeProperty(replaceprops); + return changed; +} + +bool WM_operator_last_properties_init(wmOperator *op) +{ + bool changed = false; + if (op->type->last_properties) { + changed |= operator_last_properties_init_impl(op, op->type->last_properties); + for (wmOperator *opm = op->macro.first; opm; opm = opm->next) { + IDProperty *idp_src = IDP_GetPropertyFromGroup(op->type->last_properties, opm->idname); + if (idp_src) { + changed |= operator_last_properties_init_impl(opm, idp_src); + } + } + } + return changed; +} + +bool WM_operator_last_properties_store(wmOperator *op) +{ + if (op->type->last_properties) { + IDP_FreeProperty(op->type->last_properties); + op->type->last_properties = NULL; + } + + if (op->properties) { + CLOG_INFO(WM_LOG_OPERATORS, 1, "storing properties for '%s'", op->type->idname); + op->type->last_properties = IDP_CopyProperty(op->properties); + } + + if (op->macro.first != NULL) { + for (wmOperator *opm = op->macro.first; opm; opm = opm->next) { + if (opm->properties) { + if (op->type->last_properties == NULL) { + op->type->last_properties = IDP_New( + IDP_GROUP, &(IDPropertyTemplate){0}, "wmOperatorProperties"); + } + IDProperty *idp_macro = IDP_CopyProperty(opm->properties); + STRNCPY(idp_macro->name, opm->type->idname); + IDP_ReplaceInGroup(op->type->last_properties, idp_macro); + } + } + } + + return (op->type->last_properties != NULL); +} + +#else + +bool WM_operator_last_properties_init(wmOperator *UNUSED(op)) +{ + return false; +} + +bool WM_operator_last_properties_store(wmOperator *UNUSED(op)) +{ + return false; +} + +#endif + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Default Operator Callbacks * \{ */ diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c index 3218f8c45e4..8e49e47c492 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.c +++ b/source/blender/windowmanager/intern/wm_toolsystem.c @@ -487,6 +487,8 @@ static bool toolsystem_key_ensure_check(const bToolKey *tkey) break; case SPACE_NODE: return true; + case SPACE_SEQ: + return true; } return false; } @@ -516,6 +518,11 @@ int WM_toolsystem_mode_from_spacetype(ViewLayer *view_layer, ScrArea *sa, int sp mode = 0; break; } + case SPACE_SEQ: { + SpaceSeq *sseq = sa->spacedata.first; + mode = sseq->view; + break; + } } return mode; } @@ -736,6 +743,17 @@ static const char *toolsystem_default_tool(const bToolKey *tkey) case SPACE_NODE: { return "builtin.select_box"; } + case SPACE_SEQ: { + switch (tkey->mode) { + case SEQ_VIEW_SEQUENCE: + return "builtin.select"; + case SEQ_VIEW_PREVIEW: + return "builtin.annotate"; + case SEQ_VIEW_SEQUENCE_PREVIEW: + return "builtin.select"; + } + return "builtin.select_box"; + } } return "builtin.select_box"; diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index e68d4902c66..850eb12b12a 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -547,6 +547,12 @@ void WM_window_set_dpi(const wmWindow *win) BLF_default_dpi(U.pixelsize * U.dpi); } +static void wm_window_update_eventstate(wmWindow *win) +{ + /* Update mouse position when a window is activated. */ + wm_get_cursor_position(win, &win->eventstate->x, &win->eventstate->y); +} + static void wm_window_ensure_eventstate(wmWindow *win) { if (win->eventstate) { @@ -554,7 +560,7 @@ static void wm_window_ensure_eventstate(wmWindow *win) } win->eventstate = MEM_callocN(sizeof(wmEvent), "window event state"); - wm_get_cursor_position(win, &win->eventstate->x, &win->eventstate->y); + wm_window_update_eventstate(win); } /* belongs to below */ @@ -702,6 +708,8 @@ static void wm_window_ghostwindow_ensure(wmWindowManager *wm, wmWindow *win, boo /* happens after fileread */ wm_window_ensure_eventstate(win); + + WM_window_set_dpi(win); } /* add keymap handlers (1 handler for all keys in map!) */ @@ -1206,7 +1214,6 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr case GHOST_kEventWindowActivate: { GHOST_TEventKeyData kdata; wmEvent event; - int wx, wy; const int keymodifier = ((query_qual(SHIFT) ? KM_SHIFT : 0) | (query_qual(CONTROL) ? KM_CTRL : 0) | (query_qual(ALT) ? KM_ALT : 0) | (query_qual(OS) ? KM_OSKEY : 0)); @@ -1291,10 +1298,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr win->eventstate->keymodifier = 0; /* entering window, update mouse pos. but no event */ - wm_get_cursor_position(win, &wx, &wy); - - win->eventstate->x = wx; - win->eventstate->y = wy; + wm_window_update_eventstate(win); win->addmousemove = 1; /* enables highlighted buttons */ @@ -1455,12 +1459,9 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr case GHOST_kEventDraggingDropDone: { wmEvent event; GHOST_TEventDragnDropData *ddd = GHOST_GetEventData(evt); - int wx, wy; /* entering window, update mouse pos */ - wm_get_cursor_position(win, &wx, &wy); - win->eventstate->x = wx; - win->eventstate->y = wy; + wm_window_update_eventstate(win); wm_event_init_from_window(win, &event); /* copy last state, like mouse coords */ @@ -1542,9 +1543,21 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr wm_event_add_ghostevent(wm, win, type, data); break; } - default: + case GHOST_kEventButtonDown: + case GHOST_kEventButtonUp: { + if (win->active == 0) { + /* Entering window, update cursor and tablet state. + * (ghost sends win-activate *after* the mouseclick in window!) */ + wm_window_update_eventstate(win); + } + + wm_event_add_ghostevent(wm, win, type, data); + break; + } + default: { wm_event_add_ghostevent(wm, win, type, data); break; + } } } return 1; @@ -2098,21 +2111,6 @@ void WM_cursor_compatible_xy(wmWindow *win, int *x, int *y) } } -/** - * Get the cursor pressure, in most cases you'll want to use wmTabletData from the event - */ -float WM_cursor_pressure(const struct wmWindow *win) -{ - const GHOST_TabletData *td = GHOST_GetTabletData(win->ghostwin); - /* if there's tablet data from an active tablet device then add it */ - if ((td != NULL) && td->Active != GHOST_kTabletModeNone) { - return wm_pressure_curve(td->Pressure); - } - else { - return -1.0f; - } -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -2189,8 +2187,8 @@ void WM_window_screen_rect_calc(const wmWindow *win, rcti *r_rect) } } - BLI_assert(screen_rect.xmin < screen_rect.xmax); - BLI_assert(screen_rect.ymin < screen_rect.ymax); + BLI_assert(BLI_rcti_is_valid(&screen_rect)); + *r_rect = screen_rect; } diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h index c53ccda170a..97c5980e3e7 100644 --- a/source/blender/windowmanager/wm_event_system.h +++ b/source/blender/windowmanager/wm_event_system.h @@ -31,6 +31,7 @@ #define WM_HANDLER_MODAL 4 /* MODAL|BREAK means unhandled */ struct ARegion; +struct GHOST_TabletData; struct ScrArea; /* wmKeyMap is in DNA_windowmanager.h, it's saveable */ @@ -148,7 +149,9 @@ void wm_event_do_depsgraph(bContext *C, bool is_after_open_file); void wm_event_do_refresh_wm_and_depsgraph(bContext *C); void wm_event_do_notifiers(bContext *C); +/* wm_event_query.c */ float wm_pressure_curve(float raw_pressure); +void wm_tablet_data_from_ghost(const struct GHOST_TabletData *tablet_data, wmTabletData *wmtab); /* wm_keymap.c */ diff --git a/source/creator/creator_signals.c b/source/creator/creator_signals.c index e8c6e9251bc..446f8f51875 100644 --- a/source/creator/creator_signals.c +++ b/source/creator/creator_signals.c @@ -102,15 +102,18 @@ static void sig_handle_crash_backtrace(FILE *fp) static void sig_handle_crash(int signum) { - wmWindowManager *wm = G_MAIN->wm.first; + /* Might be called after WM/Main exit, so needs to be careful about NULL-checking before + * dereferencing. */ + + wmWindowManager *wm = G_MAIN ? G_MAIN->wm.first : NULL; # ifdef USE_WRITE_CRASH_BLEND - if (wm->undo_stack) { + if (wm && wm->undo_stack) { struct MemFile *memfile = BKE_undosys_stack_memfile_get_active(wm->undo_stack); if (memfile) { char fname[FILE_MAX]; - if (!G_MAIN->name[0]) { + if (!(G_MAIN && G_MAIN->name[0])) { BLI_make_file_string("/", fname, BKE_tempdir_base(), "crash.blend"); } else { @@ -131,7 +134,7 @@ static void sig_handle_crash(int signum) char fname[FILE_MAX]; - if (!G_MAIN->name[0]) { + if (!(G_MAIN && G_MAIN->name[0])) { BLI_join_dirfile(fname, sizeof(fname), BKE_tempdir_base(), "blender.crash.txt"); } else { diff --git a/tests/gtests/CMakeLists.txt b/tests/gtests/CMakeLists.txt index 7da65bcc8b9..bcf77fb6de7 100644 --- a/tests/gtests/CMakeLists.txt +++ b/tests/gtests/CMakeLists.txt @@ -16,6 +16,9 @@ if(WITH_GTESTS) add_subdirectory(blenloader) add_subdirectory(guardedalloc) add_subdirectory(bmesh) + if(WITH_CODEC_FFMPEG) + add_subdirectory(ffmpeg) + endif() if(WITH_ALEMBIC) add_subdirectory(alembic) endif() diff --git a/tests/gtests/ffmpeg/CMakeLists.txt b/tests/gtests/ffmpeg/CMakeLists.txt new file mode 100644 index 00000000000..dbd4f9f1fed --- /dev/null +++ b/tests/gtests/ffmpeg/CMakeLists.txt @@ -0,0 +1,44 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2014, Blender Foundation +# All rights reserved. +# ***** END GPL LICENSE BLOCK ***** + +set(INC + . + .. + + ${FFMPEG_INCLUDE_DIRS} + ${PNG_INCLUDE_DIRS} + ${ZLIB_INCLUDE_DIRS} +) + +set(LIB + ${PNG_LIBRARIES} + ${FFMPEG_LIBRARIES} + ${ZLIB_LIBRARIES} +) + +if(WITH_IMAGE_OPENJPEG) + set(LIB ${LIB} ${OPENJPEG_LIBRARIES}) +endif() + +setup_platform_linker_flags() +link_directories(${FFMPEG_LIBPATH} ${PNG_LIBPATH} ${ZLIB_LIBPATH}) +include_directories(${INC}) + +BLENDER_SRC_GTEST(ffmpeg "ffmpeg_codecs.cc" "${LIB}") diff --git a/tests/gtests/ffmpeg/ffmpeg_codecs.cc b/tests/gtests/ffmpeg/ffmpeg_codecs.cc new file mode 100644 index 00000000000..bbf2b3a4111 --- /dev/null +++ b/tests/gtests/ffmpeg/ffmpeg_codecs.cc @@ -0,0 +1,147 @@ +#include "testing/testing.h" + +extern "C" { +#include <libavcodec/avcodec.h> +#include <libavutil/log.h> +} + +bool test_vcodec(AVCodec *codec, AVPixelFormat pixelformat) +{ + av_log_set_level(AV_LOG_QUIET); + bool result = false; + if (codec) { + AVCodecContext *ctx = avcodec_alloc_context3(codec); + if (ctx) { + ctx->time_base.num = 1; + ctx->time_base.den = 25; + ctx->pix_fmt = pixelformat; + ctx->width = 720; + ctx->height = 576; + int open = avcodec_open2(ctx, codec, NULL); + if (open >= 0) { + avcodec_free_context(&ctx); + result = true; + } + } + } + return result; +} +bool test_acodec(AVCodec *codec, AVSampleFormat fmt) +{ + av_log_set_level(AV_LOG_QUIET); + bool result = false; + if (codec) { + AVCodecContext *ctx = avcodec_alloc_context3(codec); + if (ctx) { + ctx->sample_fmt = fmt; + ctx->sample_rate = 48000; + ctx->channel_layout = AV_CH_LAYOUT_MONO; + ctx->bit_rate = 128000; + int open = avcodec_open2(ctx, codec, NULL); + if (open >= 0) { + avcodec_free_context(&ctx); + result = true; + } + } + } + return result; +} + +bool test_codec_video_by_codecid(AVCodecID codec_id, AVPixelFormat pixelformat) +{ + bool result = false; + AVCodec *codec = avcodec_find_encoder(codec_id); + if (codec) + result = test_vcodec(codec, pixelformat); + return result; +} + +bool test_codec_video_by_name(const char *codecname, AVPixelFormat pixelformat) +{ + bool result = false; + AVCodec *codec = avcodec_find_encoder_by_name(codecname); + if (codec) + result = test_vcodec(codec, pixelformat); + return result; +} + +bool test_codec_audio_by_codecid(AVCodecID codec_id, AVSampleFormat fmt) +{ + bool result = false; + AVCodec *codec = avcodec_find_encoder(codec_id); + if (codec) + result = test_acodec(codec, fmt); + return result; +} + +bool test_codec_audio_by_name(const char *codecname, AVSampleFormat fmt) +{ + bool result = false; + AVCodec *codec = avcodec_find_encoder_by_name(codecname); + if (codec) + result = test_acodec(codec, fmt); + return result; +} + +#define str(s) #s +#define FFMPEG_TEST_VCODEC_ID(codec, fmt) \ + TEST(CheckCodec, codec##_##fmt) \ + { \ + EXPECT_TRUE(test_codec_video_by_codecid(codec, fmt)); \ + } + +#define FFMPEG_TEST_VCODEC_NAME(codec, fmt) \ + TEST(CheckCodec, codec##_##fmt) \ + { \ + EXPECT_TRUE(test_codec_video_by_name(str(codec), fmt)); \ + } + +#define FFMPEG_TEST_ACODEC_ID(codec, fmt) \ + TEST(CheckCodec, codec##_##fmt) \ + { \ + EXPECT_TRUE(test_codec_audio_by_codecid(codec, fmt)); \ + } + +#define FFMPEG_TEST_ACODEC_NAME(codec, fmt) \ + TEST(CheckCodec, codec) \ + { \ + EXPECT_TRUE(test_codec_audio_by_name(str(codec), fmt)); \ + } + +/* generic codec ID's used in blender */ + +FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_HUFFYUV, AV_PIX_FMT_BGRA) +FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_HUFFYUV, AV_PIX_FMT_RGB32) +FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_FFV1, AV_PIX_FMT_RGB32) +FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_QTRLE, AV_PIX_FMT_ARGB) +FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_VP9, AV_PIX_FMT_YUVA420P) +FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_PNG, AV_PIX_FMT_RGBA) +FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_H264, AV_PIX_FMT_YUV420P) +FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_MPEG4, AV_PIX_FMT_YUV420P) +FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_THEORA, AV_PIX_FMT_YUV420P) +FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_DVVIDEO, AV_PIX_FMT_YUV420P) +FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_MPEG1VIDEO, AV_PIX_FMT_YUV420P) +FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_MPEG2VIDEO, AV_PIX_FMT_YUV420P) +FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_FLV1, AV_PIX_FMT_YUV420P) + +/* Audio codecs */ + +FFMPEG_TEST_ACODEC_ID(AV_CODEC_ID_AAC, AV_SAMPLE_FMT_FLTP) +FFMPEG_TEST_ACODEC_ID(AV_CODEC_ID_AC3, AV_SAMPLE_FMT_FLTP) +FFMPEG_TEST_ACODEC_ID(AV_CODEC_ID_FLAC, AV_SAMPLE_FMT_S16) +FFMPEG_TEST_ACODEC_ID(AV_CODEC_ID_MP2, AV_SAMPLE_FMT_S16) +FFMPEG_TEST_ACODEC_ID(AV_CODEC_ID_MP3, AV_SAMPLE_FMT_FLTP) +FFMPEG_TEST_ACODEC_ID(AV_CODEC_ID_OPUS, AV_SAMPLE_FMT_FLT) +FFMPEG_TEST_ACODEC_ID(AV_CODEC_ID_PCM_S16LE, AV_SAMPLE_FMT_S16) +FFMPEG_TEST_ACODEC_ID(AV_CODEC_ID_VORBIS, AV_SAMPLE_FMT_FLTP) + +/* Libraries we count on ffmpeg being linked against */ + +FFMPEG_TEST_VCODEC_NAME(libtheora, AV_PIX_FMT_YUV420P) +FFMPEG_TEST_VCODEC_NAME(libx264, AV_PIX_FMT_YUV420P) +FFMPEG_TEST_VCODEC_NAME(libvpx, AV_PIX_FMT_YUV420P) +FFMPEG_TEST_VCODEC_NAME(libopenjpeg, AV_PIX_FMT_YUV420P) +FFMPEG_TEST_VCODEC_NAME(libxvid, AV_PIX_FMT_YUV420P) +FFMPEG_TEST_ACODEC_NAME(libvorbis, AV_SAMPLE_FMT_FLTP) +FFMPEG_TEST_ACODEC_NAME(libopus, AV_SAMPLE_FMT_FLT) +FFMPEG_TEST_ACODEC_NAME(libmp3lame, AV_SAMPLE_FMT_FLTP) diff --git a/tests/gtests/guardedalloc/guardedalloc_alignment_test.cc b/tests/gtests/guardedalloc/guardedalloc_alignment_test.cc index efb29a6088d..4866ac44e3c 100644 --- a/tests/gtests/guardedalloc/guardedalloc_alignment_test.cc +++ b/tests/gtests/guardedalloc/guardedalloc_alignment_test.cc @@ -34,6 +34,50 @@ void DoBasicAlignmentChecks(const int alignment) } // namespace +TEST(guardedalloc, LockfreeAlignedAlloc1) +{ + DoBasicAlignmentChecks(1); +} + +TEST(guardedalloc, GuardedAlignedAlloc1) +{ + MEM_use_guarded_allocator(); + DoBasicAlignmentChecks(1); +} + +TEST(guardedalloc, LockfreeAlignedAlloc2) +{ + DoBasicAlignmentChecks(2); +} + +TEST(guardedalloc, GuardedAlignedAlloc2) +{ + MEM_use_guarded_allocator(); + DoBasicAlignmentChecks(2); +} + +TEST(guardedalloc, LockfreeAlignedAlloc4) +{ + DoBasicAlignmentChecks(4); +} + +TEST(guardedalloc, GuardedAlignedAlloc4) +{ + MEM_use_guarded_allocator(); + DoBasicAlignmentChecks(4); +} + +TEST(guardedalloc, LockfreeAlignedAlloc8) +{ + DoBasicAlignmentChecks(8); +} + +TEST(guardedalloc, GuardedAlignedAlloc8) +{ + MEM_use_guarded_allocator(); + DoBasicAlignmentChecks(8); +} + TEST(guardedalloc, LockfreeAlignedAlloc16) { DoBasicAlignmentChecks(16); @@ -45,13 +89,35 @@ TEST(guardedalloc, GuardedAlignedAlloc16) DoBasicAlignmentChecks(16); } -// On Apple we currently support 16 bit alignment only. -// Harmless for Blender, but would be nice to support -// eventually. -#ifndef __APPLE__ +TEST(guardedalloc, LockfreeAlignedAlloc32) +{ + DoBasicAlignmentChecks(32); +} + TEST(guardedalloc, GuardedAlignedAlloc32) { MEM_use_guarded_allocator(); DoBasicAlignmentChecks(32); } -#endif + +TEST(guardedalloc, LockfreeAlignedAlloc256) +{ + DoBasicAlignmentChecks(256); +} + +TEST(guardedalloc, GuardedAlignedAlloc256) +{ + MEM_use_guarded_allocator(); + DoBasicAlignmentChecks(256); +} + +TEST(guardedalloc, LockfreeAlignedAlloc512) +{ + DoBasicAlignmentChecks(512); +} + +TEST(guardedalloc, GuardedAlignedAlloc512) +{ + MEM_use_guarded_allocator(); + DoBasicAlignmentChecks(512); +} diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 7241c26dfec..b5af3e14237 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -22,6 +22,7 @@ set(USE_EXPERIMENTAL_TESTS FALSE) set(TEST_SRC_DIR ${CMAKE_SOURCE_DIR}/../lib/tests) +set(TEST_PYTHON_DIR ${CMAKE_SOURCE_DIR}/tests/python) set(TEST_OUT_DIR ${CMAKE_BINARY_DIR}/tests) # ugh, any better way to do this on testing only? @@ -126,13 +127,17 @@ add_blender_test( add_blender_test( bmesh_bevel ${TEST_SRC_DIR}/modeling/bevel_regression.blend - --python-text run_tests + --python ${TEST_PYTHON_DIR}/bevel_operator.py + -- + --run-all-tests ) add_blender_test( bmesh_boolean ${TEST_SRC_DIR}/modeling/bool_regression.blend - --python-text run_tests + --python ${TEST_PYTHON_DIR}/boolean_operator.py + -- + --run-all-tests ) add_blender_test( @@ -149,6 +154,24 @@ add_blender_test( --python-text run_tests.py ) +add_blender_test( + modifiers + ${TEST_SRC_DIR}/modeling/modifiers.blend + --python ${TEST_PYTHON_DIR}/modifiers.py + -- + --run-all-tests +) + +# ------------------------------------------------------------------------------ +# OPERATORS TESTS +add_blender_test( + operators + ${TEST_SRC_DIR}/modeling/operators.blend + --python ${TEST_PYTHON_DIR}/operators.py + -- + --run-all-tests +) + # ------------------------------------------------------------------------------ # IO TESTS diff --git a/tests/python/bevel_operator.py b/tests/python/bevel_operator.py new file mode 100644 index 00000000000..3cdbeb9300b --- /dev/null +++ b/tests/python/bevel_operator.py @@ -0,0 +1,184 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# To run all tests, use +# BLENDER_VERBOSE=1 blender path/to/bevel_regression.blend --python path/to/bevel_operator.py -- --run-all-tests +# To run one test, use +# BLENDER_VERBOSE=1 blender path/to/bevel_regression.blend --python path/to/bevel_operator.py -- --run-test <index> +# where <index> is the index of the test specified in the list tests. + +import bpy +import os +import sys + +sys.path.append(os.path.dirname(os.path.realpath(__file__))) +from modules.mesh_test import OperatorTest + + +def main(): + tests = [ + # 0 + ['EDGE', {10}, 'Cube_test', 'Cube_result_1', 'bevel', {'offset': 0.2}], + ['EDGE', {10, 7}, 'Cube_test', 'Cube_result_2', 'bevel', {'offset': 0.2, 'offset_type': 'WIDTH'}], + ['EDGE', {8, 10, 7}, 'Cube_test', 'Cube_result_3', 'bevel', {'offset': 0.2, 'offset_type': 'DEPTH'}], + ['EDGE', {10}, 'Cube_test', 'Cube_result_4', 'bevel', {'offset': 0.4, 'segments': 2}], + ['EDGE', {10, 7}, 'Cube_test', 'Cube_result_5', 'bevel', {'offset': 0.4, 'segments': 3}], + # 5 + ['EDGE', {8, 10, 7}, 'Cube_test', 'Cube_result_6', 'bevel', {'offset': 0.4, 'segments': 4}], + ['EDGE', {0, 10, 4, 7}, 'Cube_test', 'Cube_result_7', 'bevel', {'offset': 0.4, 'segments': 5, 'profile': 0.2}], + ['EDGE', {8, 10, 7}, 'Cube_test', 'Cube_result_8', 'bevel', {'offset': 0.4, 'segments': 5, 'profile': 0.25}], + ['EDGE', {8, 10, 7}, 'Cube_test', 'Cube_result_9', 'bevel', {'offset': 0.4, 'segments': 6, 'profile': 0.9}], + ['EDGE', {10, 7}, 'Cube_test', 'Cube_result_10', 'bevel', {'offset': 0.4, 'segments': 4, 'profile': 1.0}], + # 10 + ['EDGE', {8, 10, 7}, 'Cube_test', 'Cube_result_11', 'bevel', {'offset': 0.4, 'segments': 5, 'profile': 1.0}], + ['EDGE', {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 'Cube_test', 'Cube_result_12', 'bevel', + {'offset': 0.4, 'segments': 8}], + ['EDGE', {5}, 'Pyr4_test', 'Pyr4_result_1', 'bevel', {'offset': 0.2}], + ['EDGE', {2, 5}, 'Pyr4_test', 'Pyr4_result_2', 'bevel', {'offset': 0.2}], + ['EDGE', {2, 3, 5}, 'Pyr4_test', 'Pyr4_result_3', 'bevel', {'offset': 0.2}], + # 15 + ['EDGE', {1, 2, 3, 5}, 'Pyr4_test', 'Pyr4_result_4', 'bevel', {'offset': 0.2}], + ['EDGE', {1, 2, 3, 5}, 'Pyr4_test', 'Pyr4_result_5', 'bevel', {'offset': 0.2, 'segments': 3}], + ['EDGE', {2, 3}, 'Pyr4_test', 'Pyr4_result_6', 'bevel', {'offset': 0.2, 'segments': 2}], + ['EDGE', {1, 2, 3, 5}, 'Pyr4_test', 'Pyr4_result_7', 'bevel', {'offset': 0.2, 'segments': 4, 'profile': 0.15}], + ['VERT', {1}, 'Pyr4_test', 'Pyr4_result_8', 'bevel', {'offset': 0.75, 'segments': 4, 'vertex_only': True}], + # 20 + ['VERT', {1}, 'Pyr4_test', 'Pyr4_result_9', 'bevel', + {'offset': 0.75, 'segments': 3, 'vertex_only': True, 'profile': 0.25}], + ['EDGE', {2, 3}, 'Pyr6_test', 'Pyr6_result_1', 'bevel', {'offset': 0.2}], + ['EDGE', {8, 2, 3}, 'Pyr6_test', 'Pyr6_result_2', 'bevel', {'offset': 0.2, 'segments': 2}], + ['EDGE', {0, 2, 3, 4, 6, 7, 9, 10, 11}, 'Pyr6_test', 'Pyr6_result_3', 'bevel', + {'offset': 0.2, 'segments': 4, 'profile': 0.8}], + ['EDGE', {8, 9, 3, 11}, 'Sept_test', 'Sept_result_1', 'bevel', {'offset': 0.1}], + # 25 + ['EDGE', {8, 9, 11}, 'Sept_test', 'Sept_result_2', 'bevel', {'offset': 0.1, 'offset_type': 'WIDTH'}], + ['EDGE', {2, 8, 9, 12, 13, 14}, 'Saddle_test', 'Saddle_result_1', 'bevel', {'offset': 0.3, 'segments': 5}], + ['VERT', {4}, 'Saddle_test', 'Saddle_result_2', 'bevel', {'offset': 0.6, 'segments': 6, 'vertex_only': True}], + ['EDGE', {2, 5, 8, 11, 14, 18, 21, 24, 27, 30, 34, 37, 40, 43, 46, 50, 53, 56, 59, 62, 112, 113, 114, 115}, + 'Bent_test', 'Bent_result_1', 'bevel', {'offset': 0.2, 'segments': 3}], + ['EDGE', {1, 8, 9, 10, 11}, 'Bentlines_test', 'Bentlines_result_1', 'bevel', {'offset': 0.2, 'segments': 3}], + # 30 + ['EDGE', {26, 12, 20}, 'Flaretop_test', 'Flaretop_result_1', 'bevel', {'offset': 0.4, 'segments': 2}], + ['EDGE', {26, 12, 20}, 'Flaretop_test', 'Flaretop_result_2', 'bevel', + {'offset': 0.4, 'segments': 2, 'profile': 1.0}], + ['FACE', {1, 6, 7, 8, 9, 10, 11, 12}, 'Flaretop_test', 'Flaretop_result_3', 'bevel', + {'offset': 0.4, 'segments': 4}], + ['EDGE', {4, 8, 10, 18, 24}, 'BentL_test', 'BentL_result_1', 'bevel', {'offset': 0.2}], + ['EDGE', {0, 1, 2, 10}, 'Wires_test', 'Wires_test_result_1', 'bevel', {'offset': 0.3}], + # 35 + ['VERT', {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 'Wires_test', 'Wires_test_result_2', 'bevel', + {'offset': 0.3, 'vertex_only': True}], + ['EDGE', {3, 4, 5}, 'tri', 'tri_result_1', 'bevel', {'offset': 0.2}], + ['EDGE', {3, 4, 5}, 'tri', 'tri_result_2', 'bevel', {'offset': 0.2, 'segments': 2}], + ['EDGE', {3, 4, 5}, 'tri', 'tri_result_3', 'bevel', {'offset': 0.2, 'segments': 3}], + ['EDGE', {3, 4}, 'tri', 'tri_result_4', 'bevel', {'offset': 0.2}], + # 40 + ['EDGE', {3, 4}, 'tri', 'tri_result_5', 'bevel', {'offset': 0.2, 'segments': 2}], + ['VERT', {3}, 'tri', 'tri_result_6', 'bevel', {'offset': 0.2, 'vertex_only': True}], + ['VERT', {3}, 'tri', 'tri_result_7', 'bevel', {'offset': 0.2, 'segments': 2, 'vertex_only': True}], + ['VERT', {3}, 'tri', 'tri_result_8', 'bevel', {'offset': 0.2, 'segments': 3, 'vertex_only': True}], + ['VERT', {1}, 'tri', 'tri_result_9', 'bevel', {'offset': 0.2, 'vertex_only': True}], + # 45 + ['EDGE', {3, 4, 5}, 'tri1gap', 'tri1gap_result_1', 'bevel', {'offset': 0.2}], + ['EDGE', {3, 4, 5}, 'tri1gap', 'tri1gap_result_2', 'bevel', {'offset': 0.2, 'segments': 2}], + ['EDGE', {3, 4, 5}, 'tri1gap', 'tri1gap_result_3', 'bevel', {'offset': 0.2, 'segments': 3}], + ['EDGE', {3, 4}, 'tri1gap', 'tri1gap_result_4', 'bevel', {'offset': 0.2}], + ['EDGE', {3, 4}, 'tri1gap', 'tri1gap_result_5', 'bevel', {'offset': 0.2, 'segments': 2}], + # 50 + ['EDGE', {3, 4}, 'tri1gap', 'tri1gap_result_6', 'bevel', {'offset': 0.2, 'segments': 3}], + ['EDGE', {3, 5}, 'tri1gap', 'tri1gap_result_7', 'bevel', {'offset': 0.2}], + ['EDGE', {3, 5}, 'tri1gap', 'tri1gap_result_8', 'bevel', {'offset': 0.2, 'segments': 2}], + ['EDGE', {3, 5}, 'tri1gap', 'tri1gap_result_9', 'bevel', {'offset': 0.2, 'segments': 3}], + ['VERT', {3}, 'tri1gap', 'tri1gap_result_10', 'bevel', {'offset': 0.2, 'vertex_only': True}], + # 55 + ['EDGE', {3, 4, 5}, 'tri2gaps', 'tri2gaps_result_1', 'bevel', {'offset': 0.2}], + ['EDGE', {3, 4, 5}, 'tri2gaps', 'tri2gaps_result_2', 'bevel', {'offset': 0.2, 'segments': 2}], + ['EDGE', {3, 4, 5}, 'tri2gaps', 'tri2gaps_result_3', 'bevel', {'offset': 0.2, 'segments': 3}], + ['EDGE', {3, 4}, 'tri2gaps', 'tri2gaps_result_4', 'bevel', {'offset': 0.2}], + ['EDGE', {3, 4}, 'tri2gaps', 'tri2gaps_result_5', 'bevel', {'offset': 0.2, 'segments': 2}], + # 60 + ['EDGE', {3, 4}, 'tri2gaps', 'tri2gaps_result_6', 'bevel', {'offset': 0.2, 'segments': 3}], + ['EDGE', {3, 4, 5}, 'tri3gaps', 'tri3gaps_result_1', 'bevel', {'offset': 0.2}], + ['EDGE', {3, 4, 5}, 'tri3gaps', 'tri3gaps_result_2', 'bevel', {'offset': 0.2, 'segments': 2}], + ['EDGE', {3, 4, 5}, 'tri3gaps', 'tri3gaps_result_3', 'bevel', {'offset': 0.2, 'segments': 3}], + ['EDGE', {32, 33, 34, 35, 24, 25, 26, 27, 28, 29, 30, 31}, 'cube3', 'cube3_result_1', 'bevel', {'offset': 0.2}], + # 65 + ['EDGE', {32, 33, 34, 35, 24, 25, 26, 27, 28, 29, 30, 31}, 'cube3', 'cube3_result_2', 'bevel', + {'offset': 0.2, 'segments': 2}], + ['EDGE', {32, 35}, 'cube3', 'cube3_result_3', 'bevel', {'offset': 0.2}], + ['EDGE', {24, 35}, 'cube3', 'cube3_result_4', 'bevel', {'offset': 0.2}], + ['EDGE', {24, 32, 35}, 'cube3', 'cube3_result_5', 'bevel', {'offset': 0.2, 'segments': 2}], + ['EDGE', {24, 32, 35}, 'cube3', 'cube3_result_6', 'bevel', {'offset': 0.2, 'segments': 3}], + # 70 + ['EDGE', {0, 1, 6, 7, 12, 14, 16, 17}, 'Tray', 'Tray_result_1', 'bevel', {'offset': 0.01, 'segments': 2}], + ['EDGE', {33, 4, 38, 8, 41, 10, 42, 12, 14, 17, 24, 31}, 'Bumptop', 'Bumptop_result_1', 'bevel', + {'offset': 0.1, 'segments': 4}], + ['EDGE', {16, 14, 15}, 'Multisegment_test', 'Multisegment_result_1', 'bevel', {'offset': 0.2}], + ['EDGE', {16, 14, 15}, 'Multisegment_test', 'Multisegment_result_1', 'bevel', {'offset': 0.2}], + ['EDGE', {19, 20, 23, 15}, 'Window_test', 'Window_result_1', 'bevel', {'offset': 0.05, 'segments': 2}], + # 75 + ['EDGE', {8}, 'Cube_hn_test', 'Cube_hn_result_1', 'bevel', {'offset': 0.2, 'harden_normals': True}], + ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_1', 'bevel', + {'offset': 0.2, 'miter_outer': 'PATCH'}], + ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_2', 'bevel', + {'offset': 0.2, 'segments': 2, 'miter_outer': 'PATCH'}], + ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_3', 'bevel', + {'offset': 0.2, 'segments': 3, 'miter_outer': 'PATCH'}], + ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_4', 'bevel', + {'offset': 0.2, 'miter_outer': 'ARC'}], + # 80 + ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_5', 'bevel', + {'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}], + ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_6', 'bevel', + {'offset': 0.2, 'segments': 3, 'miter_outer': 'ARC'}], + ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_7', 'bevel', + {'offset': 0.2, 'miter_outer': 'PATCH', 'miter_inner': 'ARC'}], + ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_8', 'bevel', + {'offset': 0.2, 'segments': 2, 'miter_outer': 'PATCH', 'miter_inner': 'ARC'}], + ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps2_test', 'Blocksteps2_result_9', 'bevel', + {'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}], + # 85 + ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps3_test', 'Blocksteps3_result_10', 'bevel', + {'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}], + ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps4_test', 'Blocksteps4_result_11', 'bevel', + {'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}], + ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps4_test', 'Blocksteps4_result_12', 'bevel', + {'offset': 0.2, 'segments': 3, 'miter_outer': 'ARC'}], + ['EDGE', {1, 7}, 'Spike_test', 'Spike_result_1', 'bevel', {'offset': 0.2, 'segments': 3}] + ] + + operator_test = OperatorTest(tests) + + command = list(sys.argv) + for i, cmd in enumerate(command): + if cmd == "--run-all-tests": + operator_test.run_all_tests() + break + elif cmd == "--run-test": + index = int(command[i + 1]) + operator_test.run_test(index) + break + + +if __name__ == "__main__": + try: + main() + except: + import traceback + traceback.print_exc() + sys.exit(1) diff --git a/tests/python/boolean_operator.py b/tests/python/boolean_operator.py new file mode 100644 index 00000000000..b05e60eea6c --- /dev/null +++ b/tests/python/boolean_operator.py @@ -0,0 +1,68 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +# To run all tests, use +# BLENDER_VERBOSE=1 blender path/to/bool_regression.blend --python path/to/boolean_operator.py -- --run-all-tests +# To run one test, use +# BLENDER_VERBOSE=1 blender path/to/bool_regression.blend --python path/to/boolean_operator.py -- --run-test <index> +# where <index> is the index of the test specified in the list tests. + +import bpy +import os +import sys + +sys.path.append(os.path.dirname(os.path.realpath(__file__))) +from modules.mesh_test import OperatorTest + + +def main(): + tests = [ + ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_1', 'intersect_boolean', {'operation': 'UNION'}], + ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_2', 'intersect_boolean', {'operation': 'INTERSECT'}], + ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_3', 'intersect_boolean', {'operation': 'DIFFERENCE'}], + ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_4', 'intersect', {'separate_mode': 'CUT'}], + ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_5', 'intersect', {'separate_mode': 'ALL'}], + ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_6', 'intersect', {'separate_mode': 'NONE'}], + ['FACE', {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 'Cubecube', 'Cubecube_result_7', 'intersect', + {'mode': 'SELECT', 'separate_mode': 'NONE'}], + ['FACE', {6, 7, 8, 9, 10}, 'Cubecone', 'Cubecone_result_1', 'intersect_boolean', {'operation': 'UNION'}], + ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecones', 'Cubecones_result_1', 'intersect_boolean', {'operation': 'UNION'}], + ] + + operator_test = OperatorTest(tests) + + command = list(sys.argv) + for i, cmd in enumerate(command): + if cmd == "--run-all-tests": + operator_test.run_all_tests() + break + elif cmd == "--run-test": + index = int(command[i + 1]) + operator_test.run_test(index) + break + + +if __name__ == "__main__": + try: + main() + except: + import traceback + traceback.print_exc() + sys.exit(1) diff --git a/tests/python/modifiers.py b/tests/python/modifiers.py new file mode 100644 index 00000000000..22ddfd163b1 --- /dev/null +++ b/tests/python/modifiers.py @@ -0,0 +1,156 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import bpy +import os +import sys +from random import shuffle, seed +seed(0) + +sys.path.append(os.path.dirname(os.path.realpath(__file__))) +from modules.mesh_test import ModifierTest, ModifierSpec + + +def get_generate_modifiers_list(test_object_name, randomize=False): + """ + Construct a list of 'Generate' modifiers with default parameters. + :param test_object_name: str - name of test object. Some modifiers like boolean need an extra parameter beside + the default one. E.g. boolean needs object, mask needs vertex group etc... + The extra parameter name will be <test_object_name>_<modifier_type> + :param randomize: bool - if True shuffle the list of modifiers. + :return: list of 'Generate' modifiers with default parameters. + """ + + boolean_test_object = bpy.data.objects[test_object_name + "_boolean"] + + generate_modifiers = [ + ModifierSpec('array', 'ARRAY', {}), + ModifierSpec('bevel', 'BEVEL', {'width': 0.1}), + ModifierSpec('boolean', 'BOOLEAN', {'object': boolean_test_object}), + ModifierSpec('build', 'BUILD', {'frame_start': 0, 'frame_duration': 1}), + ModifierSpec('decimate', 'DECIMATE', {}), + ModifierSpec('edge split', 'EDGE_SPLIT', {}), + + # mask can effectively delete the mesh since the vertex group need to be updated after each + # applied modifier. Needs to be tested separately. + # ModifierSpec('mask', 'MASK', {'vertex_group': mask_vertex_group}, False), + + ModifierSpec('mirror', 'MIRROR', {}), + ModifierSpec('multires', 'MULTIRES', {}), + + # remesh can also generate an empty mesh. Skip. + # ModifierSpec('remesh', 'REMESH', {}), + + # ModifierSpec('screw', 'SCREW', {}), # screw can make the test very slow. Skipping for now. + # ModifierSpec('skin', 'SKIN', {}), # skin is not reproducible . + + ModifierSpec('solidify', 'SOLIDIFY', {}), + ModifierSpec('subsurf', 'SUBSURF', {}), + ModifierSpec('triangulate', 'TRIANGULATE', {}), + ModifierSpec('wireframe', 'WIREFRAME', {}) + + ] + + if randomize: + shuffle(generate_modifiers) + + return generate_modifiers + + +def main(): + + mask_first_list = get_generate_modifiers_list("testCubeMaskFirst", randomize=True) + mask_vertex_group = "testCubeMaskFirst" + "_mask" + mask_first_list.insert(0, ModifierSpec('mask', 'MASK', {'vertex_group': mask_vertex_group})) + + tests = [ + ############################### + # List of 'Generate' modifiers on a cube + ############################### + # 0 + # ["testCube", "expectedCube", get_generate_modifiers_list("testCube")], + ["testCubeRandom", "expectedCubeRandom", get_generate_modifiers_list("testCubeRandom", randomize=True)], + ["testCubeMaskFirst", "expectedCubeMaskFirst", mask_first_list], + + ############################################ + # One 'Generate' modifier on primitive meshes + ############################################# + # 4 + ["testCubeArray", "expectedCubeArray", [ModifierSpec('array', 'ARRAY', {})]], + ["testCylinderBuild", "expectedCylinderBuild", [ModifierSpec('build', 'BUILD', {'frame_start': 0, 'frame_duration': 1})]], + + # 6 + ["testConeDecimate", "expectedConeDecimate", [ModifierSpec('decimate', 'DECIMATE', {'ratio': 0.5})]], + ["testCubeEdgeSplit", "expectedCubeEdgeSplit", [ModifierSpec('edge split', 'EDGE_SPLIT', {})]], + ["testSphereMirror", "expectedSphereMirror", [ModifierSpec('mirror', 'MIRROR', {})]], + ["testCylinderMask", "expectedCylinderMask", [ModifierSpec('mask', 'MASK', {'vertex_group': "mask_vertex_group"})]], + ["testConeMultiRes", "expectedConeMultiRes", [ModifierSpec('multires', 'MULTIRES', {})]], + + # 11 + ["testCubeScrew", "expectedCubeScrew", [ModifierSpec('screw', 'SCREW', {})]], + ["testCubeSolidify", "expectedCubeSolidify", [ModifierSpec('solidify', 'SOLIDIFY', {})]], + ["testMonkeySubsurf", "expectedMonkeySubsurf", [ModifierSpec('subsurf', 'SUBSURF', {})]], + ["testSphereTriangulate", "expectedSphereTriangulate", [ModifierSpec('triangulate', 'TRIANGULATE', {})]], + ["testMonkeyWireframe", "expectedMonkeyWireframe", [ModifierSpec('wireframe', 'WIREFRAME', {})]], + #ModifierSpec('skin', 'SKIN', {}), # skin is not reproducible . + + ############################################# + # One 'Deform' modifier on primitive meshes + ############################################# + # 16 + ["testMonkeyArmature", "expectedMonkeyArmature", + [ModifierSpec('armature', 'ARMATURE', {'object': bpy.data.objects['testArmature'], 'use_vertex_groups': True})]], + ["testTorusCast", "expectedTorusCast", [ModifierSpec('cast', 'CAST', {'factor': 2.64})]], + ["testCubeCurve", "expectedCubeCurve", + [ModifierSpec('curve', 'CURVE', {'object': bpy.data.objects['testBezierCurve']})]], + ["testMonkeyDisplace", "expectedMonkeyDisplace", [ModifierSpec('displace', "DISPLACE", {})]], + + # Hook modifier requires moving the hook object to get a mesh change, so can't test it with the current framework + # ["testMonkeyHook", "expectedMonkeyHook", + # [ModifierSpec('hook', 'HOOK', {'object': bpy.data.objects["EmptyHook"], 'vertex_group': "HookVertexGroup"})]], + + # 20 + #ModifierSpec('laplacian_deform', 'LAPLACIANDEFORM', {}) Laplacian requires a more complex mesh + ["testCubeLattice", "expectedCubeLattice", + [ModifierSpec('lattice', 'LATTICE', {'object': bpy.data.objects["testLattice"]})]], + ] + + modifiers_test = ModifierTest(tests) + + command = list(sys.argv) + for i, cmd in enumerate(command): + if cmd == "--run-all-tests": + modifiers_test.apply_modifiers = True + modifiers_test.run_all_tests() + break + elif cmd == "--run-test": + modifiers_test.apply_modifiers = False + index = int(command[i + 1]) + modifiers_test.run_test(index) + break + + +if __name__ == "__main__": + try: + main() + except: + import traceback + traceback.print_exc() + sys.exit(1) diff --git a/tests/python/modules/mesh_test.py b/tests/python/modules/mesh_test.py new file mode 100644 index 00000000000..9fb487bcef9 --- /dev/null +++ b/tests/python/modules/mesh_test.py @@ -0,0 +1,495 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +# A framework to run regression tests on mesh modifiers and operators based on howardt's mesh_ops_test.py +# +# General idea: +# A test is: +# Object mode +# Select <test_object> +# Duplicate the object +# Select the object +# Apply operation for each operation in <operations_stack> with given parameters +# (an operation is either a modifier or an operator) +# test_mesh = <test_object>.data +# run test_mesh.unit_test_compare(<expected object>.data) +# delete the duplicate object +# +# The words in angle brackets are parameters of the test, and are specified in +# the main class MeshTest. +# +# If the environment variable BLENDER_TEST_UPDATE is set to 1, the <expected_object> +# is updated with the new test result. +# Tests are verbose when the environment variable BLENDER_VERBOSE is set. + + +import bpy +import os +import inspect + + +class ModifierSpec: + """ + Holds one modifier and its parameters. + """ + + def __init__(self, modifier_name: str, modifier_type: str, modifier_parameters: dict): + """ + Constructs a modifier spec. + :param modifier_name: str - name of object modifier, e.g. "myFirstSubsurfModif" + :param modifier_type: str - type of object modifier, e.g. "SUBSURF" + :param modifier_parameters: dict - {name : val} dictionary giving modifier parameters, e.g. {"quality" : 4} + """ + self.modifier_name = modifier_name + self.modifier_type = modifier_type + self.modifier_parameters = modifier_parameters + + def __str__(self): + return "Modifier: " + self.modifier_name + " of type " + self.modifier_type + \ + " with parameters: " + str(self.modifier_parameters) + + +class OperatorSpec: + """ + Holds one operator and its parameters. + """ + + def __init__(self, operator_name: str, operator_parameters: dict, select_mode: str, selection: set): + """ + Constructs an operatorSpec. Raises ValueError if selec_mode is invalid. + :param operator_name: str - name of mesh operator from bpy.ops.mesh, e.g. "bevel" or "fill" + :param operator_parameters: dict - {name : val} dictionary containing operator parameters. + :param select_mode: str - mesh selection mode, must be either 'VERT', 'EDGE' or 'FACE' + :param selection: set - set of vertices/edges/faces indices to select, e.g. [0, 9, 10]. + """ + self.operator_name = operator_name + self.operator_parameters = operator_parameters + if select_mode not in ['VERT', 'EDGE', 'FACE']: + raise ValueError("select_mode must be either {}, {} or {}".format('VERT', 'EDGE', 'FACE')) + self.select_mode = select_mode + self.selection = selection + + def __str__(self): + return "Operator: " + self.operator_name + " with parameters: " + str(self.operator_parameters) + \ + " in selection mode: " + self.select_mode + ", selecting " + str(self.selection) + + +class MeshTest: + """ + A mesh testing class targeted at testing modifiers and operators on a single object. + It holds a stack of mesh operations, i.e. modifiers or operators. The test is executed using + the public method run_test(). + """ + + def __init__(self, test_object_name: str, expected_object_name: str, operations_stack=None, apply_modifiers=False): + """ + Constructs a MeshTest object. Raises a KeyError if objects with names expected_object_name + or test_object_name don't exist. + :param test_object: str - Name of object of mesh type to run the operations on. + :param expected_object: str - Name of object of mesh type that has the expected + geometry after running the operations. + :param operations_stack: list - stack holding operations to perform on the test_object. + :param apply_modifier: bool - True if we want to apply the modifiers right after adding them to the object. + This affects operations of type ModifierSpec only. + """ + if operations_stack is None: + operations_stack = [] + for operation in operations_stack: + if not (isinstance(operation, ModifierSpec) or isinstance(operation, OperatorSpec)): + raise ValueError("Expected operation of type {} or {}. Got {}". + format(type(ModifierSpec), type(OperatorSpec), + type(operation))) + self.operations_stack = operations_stack + self.apply_modifier = apply_modifiers + + self.verbose = os.environ.get("BLENDER_VERBOSE") is not None + self.update = os.getenv('BLENDER_TEST_UPDATE') is not None + + # Initialize test objects. + objects = bpy.data.objects + self.test_object = objects[test_object_name] + self.expected_object = objects[expected_object_name] + if self.verbose: + print("Found test object {}".format(test_object_name)) + print("Found test object {}".format(expected_object_name)) + + # Private flag to indicate whether the blend file was updated after the test. + self._test_updated = False + + def set_test_object(self, test_object_name): + """ + Set test object for the test. Raises a KeyError if object with given name does not exist. + :param test_object_name: name of test object to run operations on. + """ + objects = bpy.data.objects + self.test_object = objects[test_object_name] + + def set_expected_object(self, expected_object_name): + """ + Set expected object for the test. Raises a KeyError if object with given name does not exist + :param expected_object_name: Name of expected object. + """ + objects = bpy.data.objects + self.expected_object = objects[expected_object_name] + + def add_modifier(self, modifier_spec: ModifierSpec): + """ + Add a modifier to the operations stack. + :param modifier_spec: modifier to add to the operations stack + """ + self.operations_stack.append(modifier_spec) + if self.verbose: + print("Added modififier {}".format(modifier_spec)) + + def add_operator(self, operator_spec: OperatorSpec): + """ + Adds an operator to the operations stack. + :param operator_spec: OperatorSpec - operator to add to the operations stack. + """ + self.operations_stack.append(operator_spec) + + def _on_failed_test(self, compare, evaluated_test_object): + if self.update: + if self.verbose: + print("Test failed expectantly. Updating expected mesh...") + + # Replace expected object with object we ran operations on, i.e. evaluated_test_object. + evaluated_test_object.location = self.expected_object.location + expected_object_name = self.expected_object.name + + bpy.data.objects.remove(self.expected_object, do_unlink=True) + evaluated_test_object.name = expected_object_name + + # Save file + blend_file = bpy.data.filepath + bpy.ops.wm.save_as_mainfile(filepath=blend_file) + + self._test_updated = True + + # Set new expected object. + self.expected_object = evaluated_test_object + return True + + else: + blender_file = bpy.data.filepath + print("Test failed with error: {}. Resulting object mesh '{}' did not match expected object '{}' " + "from file blender file {}". + format(compare, evaluated_test_object.name, self.expected_object.name, blender_file)) + + return False + + def is_test_updated(self): + """ + Check whether running the test with BLENDER_TEST_UPDATE actually modified the .blend test file. + :return: Bool - True if blend file has been updated. False otherwise. + """ + return self._test_updated + + def _apply_modifier(self, test_object, modifier_spec: ModifierSpec): + """ + Add modifier to object and apply (if modifier_spec.apply_modifier is True) + :param test_object: bpy.types.Object - Blender object to apply modifier on. + :param modifier_spec: ModifierSpec - ModifierSpec object with parameters + """ + modifier = test_object.modifiers.new(modifier_spec.modifier_name, + modifier_spec.modifier_type) + if self.verbose: + print("Created modifier '{}' of type '{}'.". + format(modifier_spec.modifier_name, modifier_spec.modifier_type)) + + for param_name in modifier_spec.modifier_parameters: + try: + setattr(modifier, param_name, modifier_spec.modifier_parameters[param_name]) + if self.verbose: + print("\t set parameter '{}' with value '{}'". + format(param_name, modifier_spec.modifier_parameters[param_name])) + except AttributeError: + # Clean up first + bpy.ops.object.delete() + raise AttributeError("Modifier '{}' has no parameter named '{}'". + format(modifier_spec.modifier_type, param_name)) + + if self.apply_modifier: + bpy.ops.object.modifier_apply(modifier=modifier_spec.modifier_name) + + def _apply_operator(self, test_object, operator: OperatorSpec): + """ + Apply operator on test object. + :param test_object: bpy.types.Object - Blender object to apply operator on. + :param operator: OperatorSpec - OperatorSpec object with parameters. + """ + mesh = test_object.data + bpy.ops.object.mode_set(mode='EDIT') + bpy.ops.mesh.select_all(action='DESELECT') + bpy.ops.object.mode_set(mode='OBJECT') + + # Do selection. + bpy.context.tool_settings.mesh_select_mode = (operator.select_mode == 'VERT', + operator.select_mode == 'EDGE', + operator.select_mode == 'FACE') + for index in operator.selection: + if operator.select_mode == 'VERT': + mesh.vertices[index].select = True + elif operator.select_mode == 'EDGE': + mesh.edges[index].select = True + elif operator.select_mode == 'FACE': + mesh.polygons[index].select = True + else: + raise ValueError("Invalid selection mode") + + # Apply operator in edit mode. + bpy.ops.object.mode_set(mode='EDIT') + bpy.ops.mesh.select_mode(type=operator.select_mode) + mesh_operator = getattr(bpy.ops.mesh, operator.operator_name) + if not mesh_operator: + raise AttributeError("No mesh operator {}".format(operator.operator_name)) + retval = mesh_operator(**operator.operator_parameters) + if retval != {'FINISHED'}: + raise RuntimeError("Unexpected operator return value: {}".format(retval)) + if self.verbose: + print("Applied operator {}".format(operator)) + + bpy.ops.object.mode_set(mode='OBJECT') + + def run_test(self): + """ + Apply operations in self.operations_stack on self.test_object and compare the + resulting mesh with self.expected_object.data + :return: bool - True if the test passed, False otherwise. + """ + self._test_updated = False + bpy.context.view_layer.objects.active = self.test_object + + # Duplicate test object. + bpy.ops.object.mode_set(mode="OBJECT") + bpy.ops.object.select_all(action="DESELECT") + bpy.context.view_layer.objects.active = self.test_object + + self.test_object.select_set(True) + bpy.ops.object.duplicate() + evaluated_test_object = bpy.context.active_object + evaluated_test_object.name = "evaluated_object" + if self.verbose: + print(evaluated_test_object.name, "is set to active") + + # Add modifiers and operators. + for operation in self.operations_stack: + if isinstance(operation, ModifierSpec): + self._apply_modifier(evaluated_test_object, operation) + + elif isinstance(operation, OperatorSpec): + self._apply_operator(evaluated_test_object, operation) + else: + raise ValueError("Expected operation of type {} or {}. Got {}". + format(type(ModifierSpec), type(OperatorSpec), + type(operation))) + + # Compare resulting mesh with expected one. + if self.verbose: + print("Comparing expected mesh with resulting mesh...") + evaluated_test_mesh = evaluated_test_object.data + expected_mesh = self.expected_object.data + compare = evaluated_test_mesh.unit_test_compare(mesh=expected_mesh) + success = (compare == 'Same') + + if success: + if self.verbose: + print("Success!") + + # Clean up. + if self.verbose: + print("Cleaning up...") + # Delete evaluated_test_object. + bpy.ops.object.delete() + return True + + else: + return self._on_failed_test(compare, evaluated_test_object) + + +class OperatorTest: + """ + Helper class that stores and executes operator tests. + + Example usage: + + >>> tests = [ + >>> ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_1', 'intersect_boolean', {'operation': 'UNION'}], + >>> ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_2', 'intersect_boolean', {'operation': 'INTERSECT'}], + >>> ] + >>> operator_test = OperatorTest(tests) + >>> operator_test.run_all_tests() + """ + + def __init__(self, operator_tests): + """ + Constructs an operator test. + :param operator_tests: list - list of operator test cases. Each element in the list must contain the following + in the correct order: + 1) select_mode: str - mesh selection mode, must be either 'VERT', 'EDGE' or 'FACE' + 2) selection: set - set of vertices/edges/faces indices to select, e.g. [0, 9, 10]. + 3) test_object_name: bpy.Types.Object - test object + 4) expected_object_name: bpy.Types.Object - expected object + 5) operator_name: str - name of mesh operator from bpy.ops.mesh, e.g. "bevel" or "fill" + 6) operator_parameters: dict - {name : val} dictionary containing operator parameters. + """ + self.operator_tests = operator_tests + self.verbose = os.environ.get("BLENDER_VERBOSE") is not None + self._failed_tests_list = [] + + def run_test(self, index: int): + """ + Run a single test from operator_tests list + :param index: int - index of test + :return: bool - True if test is successful. False otherwise. + """ + case = self.operator_tests[index] + if len(case) != 6: + raise ValueError("Expected exactly 6 parameters for each test case, got {}".format(len(case))) + select_mode = case[0] + selection = case[1] + test_object_name = case[2] + expected_object_name = case[3] + operator_name = case[4] + operator_parameters = case[5] + + operator_spec = OperatorSpec(operator_name, operator_parameters, select_mode, selection) + + test = MeshTest(test_object_name, expected_object_name) + test.add_operator(operator_spec) + + success = test.run_test() + if test.is_test_updated(): + # Run the test again if the blend file has been updated. + success = test.run_test() + return success + + def run_all_tests(self): + for index, _ in enumerate(self.operator_tests): + if self.verbose: + print() + print("Running test {}...".format(index)) + success = self.run_test(index) + + if not success: + self._failed_tests_list.append(index) + + if len(self._failed_tests_list) != 0: + print("Following tests failed: {}".format(self._failed_tests_list)) + + blender_path = bpy.app.binary_path + blend_path = bpy.data.filepath + frame = inspect.stack()[1] + module = inspect.getmodule(frame[0]) + python_path = module.__file__ + + print("Run following command to open Blender and run the failing test:") + print("{} {} --python {} -- {} {}" + .format(blender_path, blend_path, python_path, "--run-test", "<test_index>")) + + raise Exception("Tests {} failed".format(self._failed_tests_list)) + + +class ModifierTest: + """ + Helper class that stores and executes modifier tests. + + Example usage: + + >>> modifier_list = [ + >>> ModifierSpec("firstSUBSURF", "SUBSURF", {"quality": 5}), + >>> ModifierSpec("firstSOLIDIFY", "SOLIDIFY", {"thickness_clamp": 0.9, "thickness": 1}) + >>> ] + >>> tests = [ + >>> ["testCube", "expectedCube", modifier_list], + >>> ["testCube_2", "expectedCube_2", modifier_list] + >>> ] + >>> modifiers_test = ModifierTest(tests) + >>> modifiers_test.run_all_tests() + """ + + def __init__(self, modifier_tests: list, apply_modifiers=False): + """ + Construct a modifier test. + :param modifier_tests: list - list of modifier test cases. Each element in the list must contain the following + in the correct order: + 1) test_object_name: bpy.Types.Object - test object + 2) expected_object_name: bpy.Types.Object - expected object + 3) modifiers: list - list of mesh_test.ModifierSpec objects. + """ + self.modifier_tests = modifier_tests + self.apply_modifiers = apply_modifiers + self.verbose = os.environ.get("BLENDER_VERBOSE") is not None + self._failed_tests_list = [] + + def run_test(self, index: int): + """ + Run a single test from self.modifier_tests list + :param index: int - index of test + :return: bool - True if test passed, False otherwise. + """ + case = self.modifier_tests[index] + if len(case) != 3: + raise ValueError("Expected exactly 3 parameters for each test case, got {}".format(len(case))) + test_object_name = case[0] + expected_object_name = case[1] + spec_list = case[2] + + test = MeshTest(test_object_name, expected_object_name) + if self.apply_modifiers: + test.apply_modifier = True + + for modifier_spec in spec_list: + test.add_modifier(modifier_spec) + + success = test.run_test() + if test.is_test_updated(): + # Run the test again if the blend file has been updated. + success = test.run_test() + + return success + + def run_all_tests(self): + """ + Run all tests in self.modifiers_tests list. Raises an exception if one the tests fails. + """ + for index, _ in enumerate(self.modifier_tests): + if self.verbose: + print() + print("Running test {}...\n".format(index)) + success = self.run_test(index) + + if not success: + self._failed_tests_list.append(index) + + if len(self._failed_tests_list) != 0: + print("Following tests failed: {}".format(self._failed_tests_list)) + + blender_path = bpy.app.binary_path + blend_path = bpy.data.filepath + frame = inspect.stack()[1] + module = inspect.getmodule(frame[0]) + python_path = module.__file__ + + print("Run following command to open Blender and run the failing test:") + print("{} {} --python {} -- {} {}" + .format(blender_path, blend_path, python_path, "--run-test", "<test_index>")) + + raise Exception("Tests {} failed".format(self._failed_tests_list)) diff --git a/tests/python/operators.py b/tests/python/operators.py new file mode 100644 index 00000000000..c5b3ac745c6 --- /dev/null +++ b/tests/python/operators.py @@ -0,0 +1,172 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import bpy +import os +import sys +from random import shuffle, seed + +seed(0) + +sys.path.append(os.path.dirname(os.path.realpath(__file__))) +from modules.mesh_test import OperatorTest, OperatorSpec + +# Central vertical loop of Suzanne +MONKEY_LOOP_VERT = {68, 69, 71, 73, 74, 75, 76, 77, 90, 129, 136, 175, 188, 189, 198, 207, + 216, 223, 230, 301, 302, 303, 304, 305, 306, 307, 308} +MONKEY_LOOP_EDGE = {131, 278, 299, 305, 307, 334, 337, 359, 384, 396, 399, 412, 415, 560, + 567, 572, 577, 615, 622, 627, 632, 643, 648, 655, 660, 707} + + +def main(): + tests = [ + #### 0 + # bisect + ['FACE', {0, 1, 2, 3, 4, 5}, "testCubeBisect", "expectedCubeBisect", "bisect", + {"plane_co": (0, 0, 0), "plane_no": (0, 1, 1), "clear_inner": True, "use_fill": True}], + + # blend from shape + ['FACE', {0, 1, 2, 3, 4, 5}, "testCubeBlendFromShape", "expectedCubeBlendFromShape", "blend_from_shape", + {"shape": "Key 1"}], + + # bridge edge loops + ["FACE", {0, 1}, "testCubeBrigeEdgeLoop", "expectedCubeBridgeEdgeLoop", "bridge_edge_loops", {}], + + # decimate + ["FACE", {i for i in range(500)}, "testMonkeyDecimate", "expectedMonkeyDecimate", "decimate", {"ratio": 0.1}], + + ### 4 + # delete + ["VERT", {3}, "testCubeDeleteVertices", "expectedCubeDeleteVertices", "delete", {}], + ["FACE", {0}, "testCubeDeleteFaces", "expectedCubeDeleteFaces", "delete", {}], + ["EDGE", {0, 1, 2, 3}, "testCubeDeleteEdges", "expectedCubeDeleteEdges", "delete", {}], + + # delete edge loop + ["VERT", MONKEY_LOOP_VERT, "testMokneyDeleteEdgeLoopVertices", "expectedMonkeyDeleteEdgeLoopVertices", + "delete_edgeloop", {}], + ["EDGE", MONKEY_LOOP_EDGE, "testMokneyDeleteEdgeLoopEdges", "expectedMonkeyDeleteEdgeLoopEdges", + "delete_edgeloop", {}], + + ### 9 + # delete loose + ["VERT", {i for i in range(12)}, "testCubeDeleteLooseVertices", "expectedCubeDeleteLooseVertices", + "delete_loose", {"use_verts": True, "use_edges": False, "use_faces": False}], + ["EDGE", {i for i in range(14)}, "testCubeDeleteLooseEdges", "expectedCubeDeleteLooseEdges", + "delete_loose", {"use_verts": False, "use_edges": True, "use_faces": False}], + ["FACE", {i for i in range(7)}, "testCubeDeleteLooseFaces", "expectedCubeDeleteLooseFaces", + "delete_loose", {"use_verts": False, "use_edges": False, "use_faces": True}], + + # dissolve degenerate + ["VERT", {i for i in range(8)}, "testCubeDissolveDegenerate", "expectedCubeDissolveDegenerate", + "dissolve_degenerate", {}], + + ### 13 + # dissolve edges + ["EDGE", {0, 5, 6, 9}, "testCylinderDissolveEdges", "expectedCylinderDissolveEdges", + "dissolve_edges", {}], + + # dissolve faces + ["VERT", {5, 34, 47, 49, 83, 91, 95}, "testCubeDissolveFaces", "expectedCubeDissolveFaces", "dissolve_faces", + {}], + + ### 15 + # dissolve verts + ["VERT", {16, 20, 22, 23, 25}, "testCubeDissolveVerts", "expectedCubeDissolveVerts", "dissolve_verts", {}], + + # duplicate + ["VERT", {i for i in range(33)} - {23}, "testConeDuplicateVertices", "expectedConeDuplicateVertices", + "duplicate", {}], + ["VERT", {23}, "testConeDuplicateOneVertex", "expectedConeDuplicateOneVertex", "duplicate", {}], + ["FACE", {6, 9}, "testConeDuplicateFaces", "expectedConeDuplicateFaces", "duplicate", {}], + ["EDGE", {i for i in range(64)}, "testConeDuplicateEdges", "expectedConeDuplicateEdges", "duplicate", {}], + + ### 20 + # edge collapse + ["EDGE", {1, 9, 4}, "testCylinderEdgeCollapse", "expectedCylinderEdgeCollapse", "edge_collapse", {}], + + # edge face add + ["VERT", {1, 3, 4, 5, 7}, "testCubeEdgeFaceAddFace", "expectedCubeEdgeFaceAddFace", "edge_face_add", {}], + ["VERT", {4, 5}, "testCubeEdgeFaceAddEdge", "expectedCubeEdgeFaceAddEdge", "edge_face_add", {}], + + # edge rotate + ["EDGE", {1}, "testCubeEdgeRotate", "expectedCubeEdgeRotate", "edge_rotate", {}], + + # edge split + ["EDGE", {2, 5, 8, 11, 14, 17, 20, 23}, "testCubeEdgeSplit", "expectedCubeEdgeSplit", "edge_split", {}], + + ### 25 + # face make planar + ["FACE", {i for i in range(500)}, "testMonkeyFaceMakePlanar", "expectedMonkeyFaceMakePlanar", + "face_make_planar", {}], + + # face split by edges + ["VERT", {i for i in range(6)}, "testPlaneFaceSplitByEdges", "expectedPlaneFaceSplitByEdges", + "face_split_by_edges", {}], + + # fill + ["EDGE", {20, 21, 22, 23, 24, 45, 46, 47, 48, 49}, "testIcosphereFill", "expectedIcosphereFill", + "fill", {}], + ["EDGE", {20, 21, 22, 23, 24, 45, 46, 47, 48, 49}, "testIcosphereFillUseBeautyFalse", + "expectedIcosphereFillUseBeautyFalse", "fill", {"use_beauty": False}], + + # fill grid + ["EDGE", {1, 2, 3, 4, 5, 7, 9, 10, 11, 12, 13, 15}, "testPlaneFillGrid", "expectedPlaneFillGrid", + "fill_grid", {}], + ["EDGE", {1, 2, 3, 4, 5, 7, 9, 10, 11, 12, 13, 15}, "testPlaneFillGridSimpleBlending", + "expectedPlaneFillGridSimpleBlending", "fill_grid", {"use_interp_simple": True}], + + ### 31 + # fill holes + ["VERT", {i for i in range(481)}, "testSphereFillHoles", "expectedSphereFillHoles", "fill_holes", {"sides": 9}], + + # inset faces + ["VERT", {5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50, 52, 59, 61, 62, 65, 83, 91, 95}, "testCubeInset", + "expectedCubeInset", "inset", {"thickness": 0.2}], + ["VERT", {5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50, 52, 59, 61, 62, 65, 83, 91, 95}, + "testCubeInsetEvenOffsetFalse", "expectedCubeInsetEvenOffsetFalse", + "inset", {"thickness": 0.2, "use_even_offset": False}], + ["VERT", {5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50, 52, 59, 61, 62, 65, 83, 91, 95}, "testCubeInsetDepth", + "expectedCubeInsetDepth", "inset", {"thickness": 0.2, "depth": 0.2}], + ["FACE", {35, 36, 37, 45, 46, 47, 55, 56, 57}, "testGridInsetRelativeOffset", "expectedGridInsetRelativeOffset", + "inset", {"thickness": 0.4, "use_relative_offset": True}], + ] + + operators_test = OperatorTest(tests) + + command = list(sys.argv) + for i, cmd in enumerate(command): + if cmd == "--run-all-tests": + operators_test.run_all_tests() + break + elif cmd == "--run-test": + operators_test.apply_modifiers = False + index = int(command[i + 1]) + operators_test.run_test(index) + break + + +if __name__ == "__main__": + try: + main() + except: + import traceback + + traceback.print_exc() + sys.exit(1) |