diff options
295 files changed, 7106 insertions, 3684 deletions
diff --git a/build_files/build_environment/windows/build_deps.cmd b/build_files/build_environment/windows/build_deps.cmd index d836a6a3a50..d3879f3d6ae 100644 --- a/build_files/build_environment/windows/build_deps.cmd +++ b/build_files/build_environment/windows/build_deps.cmd @@ -48,10 +48,13 @@ if "%4" == "nobuild" set dobuild=0 REM If Python is be available certain deps may try to REM to use this over the version we build, to prevent that -REM make sure python is NOT in the path -for %%X in (python.exe) do (set PYTHON=%%~$PATH:X) -if EXIST "%PYTHON%" ( - echo PYTHON found at %PYTHON% dependencies cannot be build with python available in the path +REM make sure pythonw is NOT in the path. We look for pythonw.exe +REM since windows apparently ships a python.exe that just opens up +REM the windows store but does not ship any actual python files that +REM could cause issues. +for %%X in (pythonw.exe) do (set PYTHONW=%%~$PATH:X) +if EXIST "%PYTHONW%" ( + echo PYTHON found at %PYTHONW% dependencies cannot be build with python available in the path goto exit ) diff --git a/intern/cycles/device/metal/queue.h b/intern/cycles/device/metal/queue.h index de20514de0b..b0bd487c86d 100644 --- a/intern/cycles/device/metal/queue.h +++ b/intern/cycles/device/metal/queue.h @@ -38,45 +38,50 @@ class MetalDeviceQueue : public DeviceQueue { virtual void copy_from_device(device_memory &mem) override; protected: + void setup_capture(); + void update_capture(DeviceKernel kernel); + void begin_capture(); + void end_capture(); void prepare_resources(DeviceKernel kernel); id<MTLComputeCommandEncoder> get_compute_encoder(DeviceKernel kernel); id<MTLBlitCommandEncoder> get_blit_encoder(); - MetalDevice *metal_device; - MetalBufferPool temp_buffer_pool; + MetalDevice *metal_device_; + MetalBufferPool temp_buffer_pool_; API_AVAILABLE(macos(11.0), ios(14.0)) - MTLCommandBufferDescriptor *command_buffer_desc = nullptr; - id<MTLDevice> mtlDevice = nil; - id<MTLCommandQueue> mtlCommandQueue = nil; - id<MTLCommandBuffer> mtlCommandBuffer = nil; - id<MTLComputeCommandEncoder> mtlComputeEncoder = nil; - id<MTLBlitCommandEncoder> mtlBlitEncoder = nil; + MTLCommandBufferDescriptor *command_buffer_desc_ = nullptr; + id<MTLDevice> mtlDevice_ = nil; + id<MTLCommandQueue> mtlCommandQueue_ = nil; + id<MTLCommandBuffer> mtlCommandBuffer_ = nil; + id<MTLComputeCommandEncoder> mtlComputeEncoder_ = nil; + id<MTLBlitCommandEncoder> mtlBlitEncoder_ = nil; API_AVAILABLE(macos(10.14), ios(14.0)) - id<MTLSharedEvent> shared_event = nil; + id<MTLSharedEvent> shared_event_ = nil; API_AVAILABLE(macos(10.14), ios(14.0)) - MTLSharedEventListener *shared_event_listener = nil; + MTLSharedEventListener *shared_event_listener_ = nil; - dispatch_queue_t event_queue; - dispatch_semaphore_t wait_semaphore; + dispatch_queue_t event_queue_; + dispatch_semaphore_t wait_semaphore_; struct CopyBack { void *host_pointer; void *gpu_mem; uint64_t size; }; - std::vector<CopyBack> copy_back_mem; + std::vector<CopyBack> copy_back_mem_; - uint64_t shared_event_id; - uint64_t command_buffers_submitted = 0; - uint64_t command_buffers_completed = 0; - Stats &stats; + uint64_t shared_event_id_; + uint64_t command_buffers_submitted_ = 0; + uint64_t command_buffers_completed_ = 0; + Stats &stats_; void close_compute_encoder(); void close_blit_encoder(); - bool verbose_tracing = false; + bool verbose_tracing_ = false; + bool label_command_encoders_ = false; /* Per-kernel profiling (see CYCLES_METAL_PROFILING). */ @@ -85,28 +90,30 @@ class MetalDeviceQueue : public DeviceQueue { int work_size; uint64_t timing_id; }; - std::vector<TimingData> command_encoder_labels; - id<MTLSharedEvent> timing_shared_event = nil; - uint64_t timing_shared_event_id; - uint64_t command_buffer_start_timing_id; + std::vector<TimingData> command_encoder_labels_; + API_AVAILABLE(macos(10.14), ios(14.0)) + id<MTLSharedEvent> timing_shared_event_ = nil; + uint64_t timing_shared_event_id_; + uint64_t command_buffer_start_timing_id_; struct TimingStats { double total_time = 0.0; uint64_t total_work_size = 0; uint64_t num_dispatches = 0; }; - TimingStats timing_stats[DEVICE_KERNEL_NUM]; - double last_completion_time = 0.0; + TimingStats timing_stats_[DEVICE_KERNEL_NUM]; + double last_completion_time_ = 0.0; /* .gputrace capture (see CYCLES_DEBUG_METAL_CAPTURE_...). */ - id<MTLCaptureScope> mtlCaptureScope = nil; - DeviceKernel capture_kernel; - int capture_dispatch = 0; - int capture_dispatch_counter = 0; - bool is_capturing = false; - bool is_capturing_to_disk = false; - bool has_captured_to_disk = false; + id<MTLCaptureScope> mtlCaptureScope_ = nil; + DeviceKernel capture_kernel_; + int capture_dispatch_counter_ = 0; + bool capture_samples_ = false; + int capture_reset_counter_ = 0; + bool is_capturing_ = false; + bool is_capturing_to_disk_ = false; + bool has_captured_to_disk_ = false; }; CCL_NAMESPACE_END diff --git a/intern/cycles/device/metal/queue.mm b/intern/cycles/device/metal/queue.mm index 9d8625e1455..0e260886abb 100644 --- a/intern/cycles/device/metal/queue.mm +++ b/intern/cycles/device/metal/queue.mm @@ -17,79 +17,180 @@ CCL_NAMESPACE_BEGIN /* MetalDeviceQueue */ MetalDeviceQueue::MetalDeviceQueue(MetalDevice *device) - : DeviceQueue(device), metal_device(device), stats(device->stats) + : DeviceQueue(device), metal_device_(device), stats_(device->stats) { if (@available(macos 11.0, *)) { - command_buffer_desc = [[MTLCommandBufferDescriptor alloc] init]; - command_buffer_desc.errorOptions = MTLCommandBufferErrorOptionEncoderExecutionStatus; + command_buffer_desc_ = [[MTLCommandBufferDescriptor alloc] init]; + command_buffer_desc_.errorOptions = MTLCommandBufferErrorOptionEncoderExecutionStatus; } - mtlDevice = device->mtlDevice; - mtlCommandQueue = [mtlDevice newCommandQueue]; + mtlDevice_ = device->mtlDevice; + mtlCommandQueue_ = [mtlDevice_ newCommandQueue]; if (@available(macos 10.14, *)) { - shared_event = [mtlDevice newSharedEvent]; - shared_event_id = 1; + shared_event_ = [mtlDevice_ newSharedEvent]; + shared_event_id_ = 1; /* Shareable event listener */ - event_queue = dispatch_queue_create("com.cycles.metal.event_queue", NULL); - shared_event_listener = [[MTLSharedEventListener alloc] initWithDispatchQueue:event_queue]; + event_queue_ = dispatch_queue_create("com.cycles.metal.event_queue", NULL); + shared_event_listener_ = [[MTLSharedEventListener alloc] initWithDispatchQueue:event_queue_]; } - wait_semaphore = dispatch_semaphore_create(0); + wait_semaphore_ = dispatch_semaphore_create(0); if (@available(macos 10.14, *)) { if (getenv("CYCLES_METAL_PROFILING")) { /* Enable per-kernel timing breakdown (shown at end of render). */ - timing_shared_event = [mtlDevice newSharedEvent]; + timing_shared_event_ = [mtlDevice_ newSharedEvent]; + label_command_encoders_ = true; } if (getenv("CYCLES_METAL_DEBUG")) { /* Enable very verbose tracing (shows every dispatch). */ - verbose_tracing = true; + verbose_tracing_ = true; + label_command_encoders_ = true; } - timing_shared_event_id = 1; + timing_shared_event_id_ = 1; } - capture_kernel = DeviceKernel(-1); + setup_capture(); +} + +void MetalDeviceQueue::setup_capture() +{ + capture_kernel_ = DeviceKernel(-1); + if (auto capture_kernel_str = getenv("CYCLES_DEBUG_METAL_CAPTURE_KERNEL")) { - /* Enable .gputrace capture for the specified DeviceKernel. */ - MTLCaptureManager *captureManager = [MTLCaptureManager sharedCaptureManager]; - mtlCaptureScope = [captureManager newCaptureScopeWithDevice:mtlDevice]; - mtlCaptureScope.label = [NSString stringWithFormat:@"Cycles kernel dispatch"]; - [captureManager setDefaultCaptureScope:mtlCaptureScope]; + /* CYCLES_DEBUG_METAL_CAPTURE_KERNEL captures a single dispatch of the specified kernel. */ + capture_kernel_ = DeviceKernel(atoi(capture_kernel_str)); + printf("Capture kernel: %d = %s\n", capture_kernel_, device_kernel_as_string(capture_kernel_)); - capture_dispatch = -1; + capture_dispatch_counter_ = 0; if (auto capture_dispatch_str = getenv("CYCLES_DEBUG_METAL_CAPTURE_DISPATCH")) { - capture_dispatch = atoi(capture_dispatch_str); - capture_dispatch_counter = 0; + capture_dispatch_counter_ = atoi(capture_dispatch_str); + + printf("Capture dispatch number %d\n", capture_dispatch_counter_); + } + } + else if (auto capture_samples_str = getenv("CYCLES_DEBUG_METAL_CAPTURE_SAMPLES")) { + /* CYCLES_DEBUG_METAL_CAPTURE_SAMPLES captures a block of dispatches from reset#(N) to + * reset#(N+1). */ + capture_samples_ = true; + capture_reset_counter_ = atoi(capture_samples_str); + + capture_dispatch_counter_ = INT_MAX; + if (auto capture_limit_str = getenv("CYCLES_DEBUG_METAL_CAPTURE_LIMIT")) { + /* CYCLES_DEBUG_METAL_CAPTURE_LIMIT sets the maximum number of dispatches to capture. */ + capture_dispatch_counter_ = atoi(capture_limit_str); } - capture_kernel = DeviceKernel(atoi(capture_kernel_str)); - printf("Capture kernel: %d = %s\n", capture_kernel, device_kernel_as_string(capture_kernel)); + printf("Capturing sample block %d (dispatch limit: %d)\n", + capture_reset_counter_, + capture_dispatch_counter_); + } + else { + /* No capturing requested. */ + return; + } - if (auto capture_url = getenv("CYCLES_DEBUG_METAL_CAPTURE_URL")) { - if (@available(macos 10.15, *)) { - if ([captureManager supportsDestination:MTLCaptureDestinationGPUTraceDocument]) { + /* Enable .gputrace capture for the specified DeviceKernel. */ + MTLCaptureManager *captureManager = [MTLCaptureManager sharedCaptureManager]; + mtlCaptureScope_ = [captureManager newCaptureScopeWithDevice:mtlDevice_]; + mtlCaptureScope_.label = [NSString stringWithFormat:@"Cycles kernel dispatch"]; + [captureManager setDefaultCaptureScope:mtlCaptureScope_]; - MTLCaptureDescriptor *captureDescriptor = [[MTLCaptureDescriptor alloc] init]; - captureDescriptor.captureObject = mtlCaptureScope; - captureDescriptor.destination = MTLCaptureDestinationGPUTraceDocument; - captureDescriptor.outputURL = [NSURL fileURLWithPath:@(capture_url)]; + label_command_encoders_ = true; - NSError *error; - if (![captureManager startCaptureWithDescriptor:captureDescriptor error:&error]) { - NSString *err = [error localizedDescription]; - printf("Start capture failed: %s\n", [err UTF8String]); - } - else { - printf("Capture started (URL: %s)\n", capture_url); - is_capturing_to_disk = true; - } + if (auto capture_url = getenv("CYCLES_DEBUG_METAL_CAPTURE_URL")) { + if (@available(macos 10.15, *)) { + if ([captureManager supportsDestination:MTLCaptureDestinationGPUTraceDocument]) { + + MTLCaptureDescriptor *captureDescriptor = [[MTLCaptureDescriptor alloc] init]; + captureDescriptor.captureObject = mtlCaptureScope_; + captureDescriptor.destination = MTLCaptureDestinationGPUTraceDocument; + captureDescriptor.outputURL = [NSURL fileURLWithPath:@(capture_url)]; + + NSError *error; + if (![captureManager startCaptureWithDescriptor:captureDescriptor error:&error]) { + NSString *err = [error localizedDescription]; + printf("Start capture failed: %s\n", [err UTF8String]); } else { - printf("Capture to file is not supported\n"); + printf("Capture started (URL: %s)\n", capture_url); + is_capturing_to_disk_ = true; } } + else { + printf("Capture to file is not supported\n"); + } + } + } +} + +void MetalDeviceQueue::update_capture(DeviceKernel kernel) +{ + /* Handle capture end triggers. */ + if (is_capturing_) { + capture_dispatch_counter_ -= 1; + if (capture_dispatch_counter_ <= 0 || kernel == DEVICE_KERNEL_INTEGRATOR_RESET) { + /* End capture if we've hit the dispatch limit or we hit a "reset". */ + end_capture(); + } + return; + } + + if (capture_dispatch_counter_ < 0) { + /* We finished capturing. */ + return; + } + + /* Handle single-capture start trigger. */ + if (kernel == capture_kernel_) { + /* Start capturing when the we hit the Nth dispatch of the specified kernel. */ + if (capture_dispatch_counter_ == 0) { + begin_capture(); + } + capture_dispatch_counter_ -= 1; + return; + } + + /* Handle multi-capture start trigger. */ + if (capture_samples_) { + /* Start capturing when the reset countdown is at 0. */ + if (capture_reset_counter_ == 0) { + begin_capture(); + } + + if (kernel == DEVICE_KERNEL_INTEGRATOR_RESET) { + capture_reset_counter_ -= 1; + } + return; + } +} + +void MetalDeviceQueue::begin_capture() +{ + /* Start gputrace capture. */ + if (mtlCommandBuffer_) { + synchronize(); + } + [mtlCaptureScope_ beginScope]; + printf("[mtlCaptureScope_ beginScope]\n"); + is_capturing_ = true; +} + +void MetalDeviceQueue::end_capture() +{ + [mtlCaptureScope_ endScope]; + is_capturing_ = false; + printf("[mtlCaptureScope_ endScope]\n"); + + if (is_capturing_to_disk_) { + if (@available(macos 10.15, *)) { + [[MTLCaptureManager sharedCaptureManager] stopCapture]; + has_captured_to_disk_ = true; + is_capturing_to_disk_ = false; + is_capturing_ = false; + printf("Capture stopped\n"); } } } @@ -98,31 +199,31 @@ MetalDeviceQueue::~MetalDeviceQueue() { /* Tidying up here isn't really practical - we should expect and require the work * queue to be empty here. */ - assert(mtlCommandBuffer == nil); - assert(command_buffers_submitted == command_buffers_completed); + assert(mtlCommandBuffer_ == nil); + assert(command_buffers_submitted_ == command_buffers_completed_); if (@available(macos 10.14, *)) { - [shared_event_listener release]; - [shared_event release]; + [shared_event_listener_ release]; + [shared_event_ release]; } if (@available(macos 11.0, *)) { - [command_buffer_desc release]; + [command_buffer_desc_ release]; } - if (mtlCommandQueue) { - [mtlCommandQueue release]; - mtlCommandQueue = nil; + if (mtlCommandQueue_) { + [mtlCommandQueue_ release]; + mtlCommandQueue_ = nil; } - if (mtlCaptureScope) { - [mtlCaptureScope release]; + if (mtlCaptureScope_) { + [mtlCaptureScope_ release]; } double total_time = 0.0; /* Show per-kernel timings, if gathered (see CYCLES_METAL_PROFILING). */ int64_t num_dispatches = 0; - for (auto &stat : timing_stats) { + for (auto &stat : timing_stats_) { total_time += stat.total_time; num_dispatches += stat.num_dispatches; } @@ -140,7 +241,7 @@ MetalDeviceQueue::~MetalDeviceQueue() printf("%s\n%s\n%s\n", divider.c_str(), header.c_str(), divider.c_str()); for (size_t i = 0; i < DEVICE_KERNEL_NUM; i++) { - auto &stat = timing_stats[i]; + auto &stat = timing_stats_[i]; if (stat.num_dispatches > 0) { printf("%-40s %16s %12s %12s %6.2fs %6.2f%%\n", device_kernel_as_string(DeviceKernel(i)), @@ -169,10 +270,10 @@ int MetalDeviceQueue::num_concurrent_states(const size_t /*state_size*/) const /* TODO: compute automatically. */ /* TODO: must have at least num_threads_per_block. */ int result = 1048576; - if (metal_device->device_vendor == METAL_GPU_AMD) { + if (metal_device_->device_vendor == METAL_GPU_AMD) { result *= 2; } - else if (metal_device->device_vendor == METAL_GPU_APPLE) { + else if (metal_device_->device_vendor == METAL_GPU_APPLE) { result *= 4; } return result; @@ -183,10 +284,10 @@ int MetalDeviceQueue::num_concurrent_busy_states() const /* METAL_WIP */ /* TODO: compute automatically. */ int result = 65536; - if (metal_device->device_vendor == METAL_GPU_AMD) { + if (metal_device_->device_vendor == METAL_GPU_AMD) { result *= 2; } - else if (metal_device->device_vendor == METAL_GPU_APPLE) { + else if (metal_device_->device_vendor == METAL_GPU_APPLE) { result *= 4; } return result; @@ -195,7 +296,7 @@ int MetalDeviceQueue::num_concurrent_busy_states() const void MetalDeviceQueue::init_execution() { /* Synchronize all textures and memory copies before executing task. */ - metal_device->load_texture_info(); + metal_device_->load_texture_info(); synchronize(); } @@ -204,20 +305,9 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, const int work_size, DeviceKernelArguments const &args) { - if (kernel == capture_kernel) { - if (capture_dispatch < 0 || capture_dispatch == capture_dispatch_counter) { - /* Start gputrace capture. */ - if (mtlCommandBuffer) { - synchronize(); - } - [mtlCaptureScope beginScope]; - printf("[mtlCaptureScope beginScope]\n"); - is_capturing = true; - } - capture_dispatch_counter += 1; - } + update_capture(kernel); - if (metal_device->have_error()) { + if (metal_device_->have_error()) { return false; } @@ -226,8 +316,10 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, id<MTLComputeCommandEncoder> mtlComputeCommandEncoder = get_compute_encoder(kernel); - if (timing_shared_event) { - command_encoder_labels.push_back({kernel, work_size, timing_shared_event_id}); + if (@available(macos 10.14, *)) { + if (timing_shared_event_) { + command_encoder_labels_.push_back({kernel, work_size, timing_shared_event_id_}); + } } /* Determine size requirement for argument buffer. */ @@ -246,8 +338,8 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, /* Metal ancillary bindless pointers. */ size_t metal_offsets = arg_buffer_length; - arg_buffer_length += metal_device->mtlAncillaryArgEncoder.encodedLength; - arg_buffer_length = round_up(arg_buffer_length, metal_device->mtlAncillaryArgEncoder.alignment); + arg_buffer_length += metal_device_->mtlAncillaryArgEncoder.encodedLength; + arg_buffer_length = round_up(arg_buffer_length, metal_device_->mtlAncillaryArgEncoder.alignment); /* Temporary buffer used to prepare arg_buffer */ uint8_t *init_arg_buffer = (uint8_t *)alloca(arg_buffer_length); @@ -270,19 +362,23 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, sizeof(IntegratorStateGPU); size_t plain_old_launch_data_size = sizeof(KernelParamsMetal) - plain_old_launch_data_offset; memcpy(init_arg_buffer + globals_offsets + plain_old_launch_data_offset, - (uint8_t *)&metal_device->launch_params + plain_old_launch_data_offset, + (uint8_t *)&metal_device_->launch_params + plain_old_launch_data_offset, plain_old_launch_data_size); /* Allocate an argument buffer. */ MTLResourceOptions arg_buffer_options = MTLResourceStorageModeManaged; if (@available(macOS 11.0, *)) { - if ([mtlDevice hasUnifiedMemory]) { + if ([mtlDevice_ hasUnifiedMemory]) { arg_buffer_options = MTLResourceStorageModeShared; } } - id<MTLBuffer> arg_buffer = temp_buffer_pool.get_buffer( - mtlDevice, mtlCommandBuffer, arg_buffer_length, arg_buffer_options, init_arg_buffer, stats); + id<MTLBuffer> arg_buffer = temp_buffer_pool_.get_buffer(mtlDevice_, + mtlCommandBuffer_, + arg_buffer_length, + arg_buffer_options, + init_arg_buffer, + stats_); /* Encode the pointer "enqueue" arguments */ bytes_written = 0; @@ -290,16 +386,16 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, size_t size_in_bytes = args.sizes[i]; bytes_written = round_up(bytes_written, size_in_bytes); if (args.types[i] == DeviceKernelArguments::POINTER) { - [metal_device->mtlBufferKernelParamsEncoder setArgumentBuffer:arg_buffer - offset:bytes_written]; + [metal_device_->mtlBufferKernelParamsEncoder setArgumentBuffer:arg_buffer + offset:bytes_written]; if (MetalDevice::MetalMem *mmem = *(MetalDevice::MetalMem **)args.values[i]) { [mtlComputeCommandEncoder useResource:mmem->mtlBuffer usage:MTLResourceUsageRead | MTLResourceUsageWrite]; - [metal_device->mtlBufferKernelParamsEncoder setBuffer:mmem->mtlBuffer offset:0 atIndex:0]; + [metal_device_->mtlBufferKernelParamsEncoder setBuffer:mmem->mtlBuffer offset:0 atIndex:0]; } else { if (@available(macos 12.0, *)) { - [metal_device->mtlBufferKernelParamsEncoder setBuffer:nil offset:0 atIndex:0]; + [metal_device_->mtlBufferKernelParamsEncoder setBuffer:nil offset:0 atIndex:0]; } } } @@ -307,9 +403,10 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, } /* Encode KernelParamsMetal buffers */ - [metal_device->mtlBufferKernelParamsEncoder setArgumentBuffer:arg_buffer offset:globals_offsets]; + [metal_device_->mtlBufferKernelParamsEncoder setArgumentBuffer:arg_buffer + offset:globals_offsets]; - if (verbose_tracing || timing_shared_event || is_capturing) { + if (label_command_encoders_) { /* Add human-readable labels if we're doing any form of debugging / profiling. */ mtlComputeCommandEncoder.label = [[NSString alloc] initWithFormat:@"Metal queue launch %s, work_size %d", @@ -321,43 +418,43 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, const size_t pointer_block_end = offsetof(KernelParamsMetal, __integrator_state) + sizeof(IntegratorStateGPU); for (size_t offset = 0; offset < pointer_block_end; offset += sizeof(device_ptr)) { - int pointer_index = offset / sizeof(device_ptr); + int pointer_index = int(offset / sizeof(device_ptr)); MetalDevice::MetalMem *mmem = *( - MetalDevice::MetalMem **)((uint8_t *)&metal_device->launch_params + offset); + MetalDevice::MetalMem **)((uint8_t *)&metal_device_->launch_params + offset); if (mmem && mmem->mem && (mmem->mtlBuffer || mmem->mtlTexture)) { - [metal_device->mtlBufferKernelParamsEncoder setBuffer:mmem->mtlBuffer - offset:0 - atIndex:pointer_index]; + [metal_device_->mtlBufferKernelParamsEncoder setBuffer:mmem->mtlBuffer + offset:0 + atIndex:pointer_index]; } else { if (@available(macos 12.0, *)) { - [metal_device->mtlBufferKernelParamsEncoder setBuffer:nil offset:0 atIndex:pointer_index]; + [metal_device_->mtlBufferKernelParamsEncoder setBuffer:nil offset:0 atIndex:pointer_index]; } } } bytes_written = globals_offsets + sizeof(KernelParamsMetal); - const MetalKernelPipeline *metal_kernel_pso = MetalDeviceKernels::get_best_pipeline(metal_device, - kernel); + const MetalKernelPipeline *metal_kernel_pso = MetalDeviceKernels::get_best_pipeline( + metal_device_, kernel); if (!metal_kernel_pso) { - metal_device->set_error( + metal_device_->set_error( string_printf("No MetalKernelPipeline for %s\n", device_kernel_as_string(kernel))); return false; } /* Encode ancillaries */ - [metal_device->mtlAncillaryArgEncoder setArgumentBuffer:arg_buffer offset:metal_offsets]; - [metal_device->mtlAncillaryArgEncoder setBuffer:metal_device->texture_bindings_2d - offset:0 - atIndex:0]; - [metal_device->mtlAncillaryArgEncoder setBuffer:metal_device->texture_bindings_3d - offset:0 - atIndex:1]; + [metal_device_->mtlAncillaryArgEncoder setArgumentBuffer:arg_buffer offset:metal_offsets]; + [metal_device_->mtlAncillaryArgEncoder setBuffer:metal_device_->texture_bindings_2d + offset:0 + atIndex:0]; + [metal_device_->mtlAncillaryArgEncoder setBuffer:metal_device_->texture_bindings_3d + offset:0 + atIndex:1]; if (@available(macos 12.0, *)) { - if (metal_device->use_metalrt) { - if (metal_device->bvhMetalRT) { - id<MTLAccelerationStructure> accel_struct = metal_device->bvhMetalRT->accel_struct; - [metal_device->mtlAncillaryArgEncoder setAccelerationStructure:accel_struct atIndex:2]; + if (metal_device_->use_metalrt) { + if (metal_device_->bvhMetalRT) { + id<MTLAccelerationStructure> accel_struct = metal_device_->bvhMetalRT->accel_struct; + [metal_device_->mtlAncillaryArgEncoder setAccelerationStructure:accel_struct atIndex:2]; } for (int table = 0; table < METALRT_TABLE_NUM; table++) { @@ -365,19 +462,19 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, [metal_kernel_pso->intersection_func_table[table] setBuffer:arg_buffer offset:globals_offsets atIndex:1]; - [metal_device->mtlAncillaryArgEncoder + [metal_device_->mtlAncillaryArgEncoder setIntersectionFunctionTable:metal_kernel_pso->intersection_func_table[table] atIndex:3 + table]; [mtlComputeCommandEncoder useResource:metal_kernel_pso->intersection_func_table[table] usage:MTLResourceUsageRead]; } else { - [metal_device->mtlAncillaryArgEncoder setIntersectionFunctionTable:nil - atIndex:3 + table]; + [metal_device_->mtlAncillaryArgEncoder setIntersectionFunctionTable:nil + atIndex:3 + table]; } } } - bytes_written = metal_offsets + metal_device->mtlAncillaryArgEncoder.encodedLength; + bytes_written = metal_offsets + metal_device_->mtlAncillaryArgEncoder.encodedLength; } if (arg_buffer.storageMode == MTLStorageModeManaged) { @@ -388,10 +485,10 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, [mtlComputeCommandEncoder setBuffer:arg_buffer offset:globals_offsets atIndex:1]; [mtlComputeCommandEncoder setBuffer:arg_buffer offset:metal_offsets atIndex:2]; - if (metal_device->use_metalrt) { + if (metal_device_->use_metalrt) { if (@available(macos 12.0, *)) { - auto bvhMetalRT = metal_device->bvhMetalRT; + auto bvhMetalRT = metal_device_->bvhMetalRT; switch (kernel) { case DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST: case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW: @@ -433,7 +530,7 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, case DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_PATHS_ARRAY: /* See parallel_active_index.h for why this amount of shared memory is needed. * Rounded up to 16 bytes for Metal */ - shared_mem_bytes = round_up((num_threads_per_block + 1) * sizeof(int), 16); + shared_mem_bytes = (int)round_up((num_threads_per_block + 1) * sizeof(int), 16); [mtlComputeCommandEncoder setThreadgroupMemoryLength:shared_mem_bytes atIndex:0]; break; @@ -447,7 +544,7 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, [mtlComputeCommandEncoder dispatchThreadgroups:size_threadgroups_per_dispatch threadsPerThreadgroup:size_threads_per_threadgroup]; - [mtlCommandBuffer addCompletedHandler:^(id<MTLCommandBuffer> command_buffer) { + [mtlCommandBuffer_ addCompletedHandler:^(id<MTLCommandBuffer> command_buffer) { NSString *kernel_name = metal_kernel_pso->function.label; /* Enhanced command buffer errors are only available in 11.0+ */ @@ -472,12 +569,12 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, } }]; - if (verbose_tracing || is_capturing) { + if (verbose_tracing_ || is_capturing_) { /* Force a sync we've enabled step-by-step verbose tracing or if we're capturing. */ synchronize(); /* Show queue counters and dispatch timing. */ - if (verbose_tracing) { + if (verbose_tracing_) { if (kernel == DEVICE_KERNEL_INTEGRATOR_RESET) { printf( "_____________________________________.____________________.______________.___________" @@ -487,9 +584,9 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, printf("%-40s| %7d threads |%5.2fms | buckets [", device_kernel_as_string(kernel), work_size, - last_completion_time * 1000.0); - std::lock_guard<std::recursive_mutex> lock(metal_device->metal_mem_map_mutex); - for (auto &it : metal_device->metal_mem_map) { + last_completion_time_ * 1000.0); + std::lock_guard<std::recursive_mutex> lock(metal_device_->metal_mem_map_mutex); + for (auto &it : metal_device_->metal_mem_map) { const string c_integrator_queue_counter = "integrator_queue_counter"; if (it.first->name == c_integrator_queue_counter) { /* Workaround "device_copy_from" being protected. */ @@ -513,89 +610,76 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, } } - return !(metal_device->have_error()); + return !(metal_device_->have_error()); } bool MetalDeviceQueue::synchronize() { - if (has_captured_to_disk || metal_device->have_error()) { + if (has_captured_to_disk_ || metal_device_->have_error()) { return false; } - if (mtlComputeEncoder) { + if (mtlComputeEncoder_) { close_compute_encoder(); } close_blit_encoder(); - if (mtlCommandBuffer) { + if (mtlCommandBuffer_) { scoped_timer timer; - if (timing_shared_event) { - /* For per-kernel timing, add event handlers to measure & accumulate dispatch times. */ - __block double completion_time = 0; - for (uint64_t i = command_buffer_start_timing_id; i < timing_shared_event_id; i++) { - [timing_shared_event notifyListener:shared_event_listener - atValue:i - block:^(id<MTLSharedEvent> sharedEvent, uint64_t value) { - completion_time = timer.get_time() - completion_time; - last_completion_time = completion_time; - for (auto label : command_encoder_labels) { - if (label.timing_id == value) { - TimingStats &stat = timing_stats[label.kernel]; - stat.num_dispatches++; - stat.total_time += completion_time; - stat.total_work_size += label.work_size; - } - } - }]; + + if (@available(macos 10.14, *)) { + if (timing_shared_event_) { + /* For per-kernel timing, add event handlers to measure & accumulate dispatch times. */ + __block double completion_time = 0; + for (uint64_t i = command_buffer_start_timing_id_; i < timing_shared_event_id_; i++) { + [timing_shared_event_ notifyListener:shared_event_listener_ + atValue:i + block:^(id<MTLSharedEvent> sharedEvent, uint64_t value) { + completion_time = timer.get_time() - completion_time; + last_completion_time_ = completion_time; + for (auto label : command_encoder_labels_) { + if (label.timing_id == value) { + TimingStats &stat = timing_stats_[label.kernel]; + stat.num_dispatches++; + stat.total_time += completion_time; + stat.total_work_size += label.work_size; + } + } + }]; + } } } - uint64_t shared_event_id = this->shared_event_id++; + uint64_t shared_event_id_ = this->shared_event_id_++; if (@available(macos 10.14, *)) { - __block dispatch_semaphore_t block_sema = wait_semaphore; - [shared_event notifyListener:shared_event_listener - atValue:shared_event_id - block:^(id<MTLSharedEvent> sharedEvent, uint64_t value) { - dispatch_semaphore_signal(block_sema); - }]; - - [mtlCommandBuffer encodeSignalEvent:shared_event value:shared_event_id]; - [mtlCommandBuffer commit]; - dispatch_semaphore_wait(wait_semaphore, DISPATCH_TIME_FOREVER); - } - - if (is_capturing) { - [mtlCaptureScope endScope]; - is_capturing = false; - printf("[mtlCaptureScope endScope]\n"); - - if (is_capturing_to_disk) { - if (@available(macos 10.15, *)) { - [[MTLCaptureManager sharedCaptureManager] stopCapture]; - has_captured_to_disk = true; - is_capturing_to_disk = false; - is_capturing = false; - printf("Capture stopped\n"); - } - } + __block dispatch_semaphore_t block_sema = wait_semaphore_; + [shared_event_ notifyListener:shared_event_listener_ + atValue:shared_event_id_ + block:^(id<MTLSharedEvent> sharedEvent, uint64_t value) { + dispatch_semaphore_signal(block_sema); + }]; + + [mtlCommandBuffer_ encodeSignalEvent:shared_event_ value:shared_event_id_]; + [mtlCommandBuffer_ commit]; + dispatch_semaphore_wait(wait_semaphore_, DISPATCH_TIME_FOREVER); } - [mtlCommandBuffer release]; + [mtlCommandBuffer_ release]; - for (const CopyBack &mmem : copy_back_mem) { + for (const CopyBack &mmem : copy_back_mem_) { memcpy((uchar *)mmem.host_pointer, (uchar *)mmem.gpu_mem, mmem.size); } - copy_back_mem.clear(); + copy_back_mem_.clear(); - temp_buffer_pool.process_command_buffer_completion(mtlCommandBuffer); - metal_device->flush_delayed_free_list(); + temp_buffer_pool_.process_command_buffer_completion(mtlCommandBuffer_); + metal_device_->flush_delayed_free_list(); - mtlCommandBuffer = nil; - command_encoder_labels.clear(); + mtlCommandBuffer_ = nil; + command_encoder_labels_.clear(); } - return !(metal_device->have_error()); + return !(metal_device_->have_error()); } void MetalDeviceQueue::zero_to_device(device_memory &mem) @@ -608,20 +692,20 @@ void MetalDeviceQueue::zero_to_device(device_memory &mem) /* Allocate on demand. */ if (mem.device_pointer == 0) { - metal_device->mem_alloc(mem); + metal_device_->mem_alloc(mem); } /* Zero memory on device. */ assert(mem.device_pointer != 0); - std::lock_guard<std::recursive_mutex> lock(metal_device->metal_mem_map_mutex); - MetalDevice::MetalMem &mmem = *metal_device->metal_mem_map.at(&mem); + std::lock_guard<std::recursive_mutex> lock(metal_device_->metal_mem_map_mutex); + MetalDevice::MetalMem &mmem = *metal_device_->metal_mem_map.at(&mem); if (mmem.mtlBuffer) { id<MTLBlitCommandEncoder> blitEncoder = get_blit_encoder(); [blitEncoder fillBuffer:mmem.mtlBuffer range:NSMakeRange(mmem.offset, mmem.size) value:0]; } else { - metal_device->mem_zero(mem); + metal_device_->mem_zero(mem); } } @@ -633,15 +717,15 @@ void MetalDeviceQueue::copy_to_device(device_memory &mem) /* Allocate on demand. */ if (mem.device_pointer == 0) { - metal_device->mem_alloc(mem); + metal_device_->mem_alloc(mem); } assert(mem.device_pointer != 0); assert(mem.host_pointer != nullptr); - std::lock_guard<std::recursive_mutex> lock(metal_device->metal_mem_map_mutex); - auto result = metal_device->metal_mem_map.find(&mem); - if (result != metal_device->metal_mem_map.end()) { + std::lock_guard<std::recursive_mutex> lock(metal_device_->metal_mem_map_mutex); + auto result = metal_device_->metal_mem_map.find(&mem); + if (result != metal_device_->metal_mem_map.end()) { if (mem.host_pointer == mem.shared_pointer) { return; } @@ -649,12 +733,12 @@ void MetalDeviceQueue::copy_to_device(device_memory &mem) MetalDevice::MetalMem &mmem = *result->second; id<MTLBlitCommandEncoder> blitEncoder = get_blit_encoder(); - id<MTLBuffer> buffer = temp_buffer_pool.get_buffer(mtlDevice, - mtlCommandBuffer, - mmem.size, - MTLResourceStorageModeShared, - mem.host_pointer, - stats); + id<MTLBuffer> buffer = temp_buffer_pool_.get_buffer(mtlDevice_, + mtlCommandBuffer_, + mmem.size, + MTLResourceStorageModeShared, + mem.host_pointer, + stats_); [blitEncoder copyFromBuffer:buffer sourceOffset:0 @@ -663,7 +747,7 @@ void MetalDeviceQueue::copy_to_device(device_memory &mem) size:mmem.size]; } else { - metal_device->mem_copy_to(mem); + metal_device_->mem_copy_to(mem); } } @@ -678,8 +762,8 @@ void MetalDeviceQueue::copy_from_device(device_memory &mem) assert(mem.device_pointer != 0); assert(mem.host_pointer != nullptr); - std::lock_guard<std::recursive_mutex> lock(metal_device->metal_mem_map_mutex); - MetalDevice::MetalMem &mmem = *metal_device->metal_mem_map.at(&mem); + std::lock_guard<std::recursive_mutex> lock(metal_device_->metal_mem_map_mutex); + MetalDevice::MetalMem &mmem = *metal_device_->metal_mem_map.at(&mem); if (mmem.mtlBuffer) { const size_t size = mem.memory_size(); @@ -689,8 +773,8 @@ void MetalDeviceQueue::copy_from_device(device_memory &mem) [blitEncoder synchronizeResource:mmem.mtlBuffer]; } if (mem.host_pointer != mmem.hostPtr) { - if (mtlCommandBuffer) { - copy_back_mem.push_back({mem.host_pointer, mmem.hostPtr, size}); + if (mtlCommandBuffer_) { + copy_back_mem_.push_back({mem.host_pointer, mmem.hostPtr, size}); } else { memcpy((uchar *)mem.host_pointer, (uchar *)mmem.hostPtr, size); @@ -702,16 +786,16 @@ void MetalDeviceQueue::copy_from_device(device_memory &mem) } } else { - metal_device->mem_copy_from(mem); + metal_device_->mem_copy_from(mem); } } void MetalDeviceQueue::prepare_resources(DeviceKernel kernel) { - std::lock_guard<std::recursive_mutex> lock(metal_device->metal_mem_map_mutex); + std::lock_guard<std::recursive_mutex> lock(metal_device_->metal_mem_map_mutex); /* declare resource usage */ - for (auto &it : metal_device->metal_mem_map) { + for (auto &it : metal_device_->metal_mem_map) { device_memory *mem = it.first; MTLResourceUsage usage = MTLResourceUsageRead; @@ -721,97 +805,99 @@ void MetalDeviceQueue::prepare_resources(DeviceKernel kernel) if (it.second->mtlBuffer) { /* METAL_WIP - use array version (i.e. useResources) */ - [mtlComputeEncoder useResource:it.second->mtlBuffer usage:usage]; + [mtlComputeEncoder_ useResource:it.second->mtlBuffer usage:usage]; } else if (it.second->mtlTexture) { /* METAL_WIP - use array version (i.e. useResources) */ - [mtlComputeEncoder useResource:it.second->mtlTexture usage:usage | MTLResourceUsageSample]; + [mtlComputeEncoder_ useResource:it.second->mtlTexture usage:usage | MTLResourceUsageSample]; } } /* ancillaries */ - [mtlComputeEncoder useResource:metal_device->texture_bindings_2d usage:MTLResourceUsageRead]; - [mtlComputeEncoder useResource:metal_device->texture_bindings_3d usage:MTLResourceUsageRead]; + [mtlComputeEncoder_ useResource:metal_device_->texture_bindings_2d usage:MTLResourceUsageRead]; + [mtlComputeEncoder_ useResource:metal_device_->texture_bindings_3d usage:MTLResourceUsageRead]; } id<MTLComputeCommandEncoder> MetalDeviceQueue::get_compute_encoder(DeviceKernel kernel) { bool concurrent = (kernel < DEVICE_KERNEL_INTEGRATOR_NUM); - if (timing_shared_event) { - /* Close the current encoder to ensure we're able to capture per-encoder timing data. */ - if (mtlComputeEncoder) { - close_compute_encoder(); + if (@available(macos 10.14, *)) { + if (timing_shared_event_) { + /* Close the current encoder to ensure we're able to capture per-encoder timing data. */ + if (mtlComputeEncoder_) { + close_compute_encoder(); + } } - } - if (@available(macos 10.14, *)) { - if (mtlComputeEncoder) { - if (mtlComputeEncoder.dispatchType == concurrent ? MTLDispatchTypeConcurrent : - MTLDispatchTypeSerial) { + if (mtlComputeEncoder_) { + if (mtlComputeEncoder_.dispatchType == concurrent ? MTLDispatchTypeConcurrent : + MTLDispatchTypeSerial) { /* declare usage of MTLBuffers etc */ prepare_resources(kernel); - return mtlComputeEncoder; + return mtlComputeEncoder_; } close_compute_encoder(); } close_blit_encoder(); - if (!mtlCommandBuffer) { - mtlCommandBuffer = [mtlCommandQueue commandBuffer]; - [mtlCommandBuffer retain]; + if (!mtlCommandBuffer_) { + mtlCommandBuffer_ = [mtlCommandQueue_ commandBuffer]; + [mtlCommandBuffer_ retain]; } - mtlComputeEncoder = [mtlCommandBuffer + mtlComputeEncoder_ = [mtlCommandBuffer_ computeCommandEncoderWithDispatchType:concurrent ? MTLDispatchTypeConcurrent : MTLDispatchTypeSerial]; - [mtlComputeEncoder setLabel:@(device_kernel_as_string(kernel))]; + [mtlComputeEncoder_ setLabel:@(device_kernel_as_string(kernel))]; /* declare usage of MTLBuffers etc */ prepare_resources(kernel); } - return mtlComputeEncoder; + return mtlComputeEncoder_; } id<MTLBlitCommandEncoder> MetalDeviceQueue::get_blit_encoder() { - if (mtlBlitEncoder) { - return mtlBlitEncoder; + if (mtlBlitEncoder_) { + return mtlBlitEncoder_; } - if (mtlComputeEncoder) { + if (mtlComputeEncoder_) { close_compute_encoder(); } - if (!mtlCommandBuffer) { - mtlCommandBuffer = [mtlCommandQueue commandBuffer]; - [mtlCommandBuffer retain]; - command_buffer_start_timing_id = timing_shared_event_id; + if (!mtlCommandBuffer_) { + mtlCommandBuffer_ = [mtlCommandQueue_ commandBuffer]; + [mtlCommandBuffer_ retain]; + command_buffer_start_timing_id_ = timing_shared_event_id_; } - mtlBlitEncoder = [mtlCommandBuffer blitCommandEncoder]; - return mtlBlitEncoder; + mtlBlitEncoder_ = [mtlCommandBuffer_ blitCommandEncoder]; + return mtlBlitEncoder_; } void MetalDeviceQueue::close_compute_encoder() { - [mtlComputeEncoder endEncoding]; - mtlComputeEncoder = nil; + [mtlComputeEncoder_ endEncoding]; + mtlComputeEncoder_ = nil; - if (timing_shared_event) { - [mtlCommandBuffer encodeSignalEvent:timing_shared_event value:timing_shared_event_id++]; + if (@available(macos 10.14, *)) { + if (timing_shared_event_) { + [mtlCommandBuffer_ encodeSignalEvent:timing_shared_event_ value:timing_shared_event_id_++]; + } } } void MetalDeviceQueue::close_blit_encoder() { - if (mtlBlitEncoder) { - [mtlBlitEncoder endEncoding]; - mtlBlitEncoder = nil; + if (mtlBlitEncoder_) { + [mtlBlitEncoder_ endEncoding]; + mtlBlitEncoder_ = nil; } } diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index dceb9ced803..5b06b5d98e6 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -324,16 +324,21 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND) ${CMAKE_CURRENT_BINARY_DIR} ) - # xdg-shell. + # `xdg-shell`. generate_protocol_bindings( xdg-shell "${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml" ) - # xdg-decoration. + # `xdg-decoration`. generate_protocol_bindings( xdg-decoration "${WAYLAND_PROTOCOLS_DIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" ) + # `xdg-output`. + generate_protocol_bindings( + xdg-output + "${WAYLAND_PROTOCOLS_DIR}/unstable/xdg-output/xdg-output-unstable-v1.xml" + ) # Pointer-constraints. generate_protocol_bindings( pointer-constraints @@ -344,6 +349,11 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND) relative-pointer "${WAYLAND_PROTOCOLS_DIR}/unstable/relative-pointer/relative-pointer-unstable-v1.xml" ) + # Tablet. + generate_protocol_bindings( + tablet + "${WAYLAND_PROTOCOLS_DIR}/unstable/tablet/tablet-unstable-v2.xml" + ) add_definitions(-DWITH_GHOST_WAYLAND) endif() diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index ad19efadba5..d27be40af0c 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -20,7 +20,7 @@ extern "C" { * \param event: The event received. * \param userdata: The callback's user data, supplied to #GHOST_CreateSystem. */ -typedef int (*GHOST_EventCallbackProcPtr)(GHOST_EventHandle event, GHOST_TUserDataPtr userdata); +typedef bool (*GHOST_EventCallbackProcPtr)(GHOST_EventHandle event, GHOST_TUserDataPtr userdata); /** * Creates the one and only system. @@ -206,7 +206,7 @@ extern GHOST_TUserDataPtr GHOST_GetWindowUserData(GHOST_WindowHandle windowhandl */ extern void GHOST_SetWindowUserData(GHOST_WindowHandle windowhandle, GHOST_TUserDataPtr userdata); -extern int GHOST_IsDialogWindow(GHOST_WindowHandle windowhandle); +extern bool GHOST_IsDialogWindow(GHOST_WindowHandle windowhandle); /** * Dispose a window. @@ -223,7 +223,7 @@ extern GHOST_TSuccess GHOST_DisposeWindow(GHOST_SystemHandle systemhandle, * \param windowhandle: Handle to the window to be checked. * \return Indication of validity. */ -extern int GHOST_ValidWindow(GHOST_SystemHandle systemhandle, GHOST_WindowHandle windowhandle); +extern bool GHOST_ValidWindow(GHOST_SystemHandle systemhandle, GHOST_WindowHandle windowhandle); /** * Begins full screen mode. @@ -235,7 +235,7 @@ extern int GHOST_ValidWindow(GHOST_SystemHandle systemhandle, GHOST_WindowHandle */ extern GHOST_WindowHandle GHOST_BeginFullScreen(GHOST_SystemHandle systemhandle, GHOST_DisplaySetting *setting, - const int stereoVisual); + const bool stereoVisual); /** * Ends full screen mode. @@ -249,7 +249,7 @@ extern GHOST_TSuccess GHOST_EndFullScreen(GHOST_SystemHandle systemhandle); * \param systemhandle: The handle to the system. * \return The current status. */ -extern int GHOST_GetFullScreen(GHOST_SystemHandle systemhandle); +extern bool GHOST_GetFullScreen(GHOST_SystemHandle systemhandle); /** * Get the Window under the cursor. @@ -369,7 +369,7 @@ extern GHOST_TSuccess GHOST_SetCustomCursorShape(GHOST_WindowHandle windowhandle * \param windowhandle: The handle to the window. * \return The visibility state of the cursor. */ -extern int GHOST_GetCursorVisibility(GHOST_WindowHandle windowhandle); +extern bool GHOST_GetCursorVisibility(GHOST_WindowHandle windowhandle); /** * Shows or hides the cursor. @@ -377,7 +377,7 @@ extern int GHOST_GetCursorVisibility(GHOST_WindowHandle windowhandle); * \param visible: The new visibility state of the cursor. * \return Indication of success. */ -extern GHOST_TSuccess GHOST_SetCursorVisibility(GHOST_WindowHandle windowhandle, int visible); +extern GHOST_TSuccess GHOST_SetCursorVisibility(GHOST_WindowHandle windowhandle, bool visible); /** * Returns the current location of the cursor (location in screen coordinates) @@ -436,7 +436,7 @@ extern GHOST_TSuccess GHOST_SetCursorGrab(GHOST_WindowHandle windowhandle, */ extern GHOST_TSuccess GHOST_GetModifierKeyState(GHOST_SystemHandle systemhandle, GHOST_TModifierKeyMask mask, - int *isDown); + bool *r_is_down); /** * Returns the state of a mouse button (outside the message queue). @@ -447,7 +447,7 @@ extern GHOST_TSuccess GHOST_GetModifierKeyState(GHOST_SystemHandle systemhandle, */ extern GHOST_TSuccess GHOST_GetButtonState(GHOST_SystemHandle systemhandle, GHOST_TButtonMask mask, - int *isDown); + bool *r_is_down); #ifdef WITH_INPUT_NDOF /*************************************************************************************** @@ -468,7 +468,7 @@ extern void GHOST_setNDOFDeadZone(float deadzone); /** * Tells if the ongoing drag'n'drop object can be accepted upon mouse drop */ -extern void GHOST_setAcceptDragOperation(GHOST_WindowHandle windowhandle, bool canAccept); +extern void GHOST_setAcceptDragOperation(GHOST_WindowHandle windowhandle, bool can_accept); /** * Returns the event type. @@ -534,7 +534,7 @@ extern void GHOST_SetTimerTaskUserData(GHOST_TimerTaskHandle timertaskhandle, * \param windowhandle: The handle to the window. * \return The validity of the window. */ -extern int GHOST_GetValid(GHOST_WindowHandle windowhandle); +extern bool GHOST_GetValid(GHOST_WindowHandle windowhandle); /** * Returns the type of drawing context used in this window. @@ -894,22 +894,32 @@ extern void GHOST_putClipboard(const char *buffer, bool selection); * \param action: console state * \return current status (1 -visible, 0 - hidden) */ -extern int setConsoleWindowState(GHOST_TConsoleWindowState action); +extern bool GHOST_setConsoleWindowState(GHOST_TConsoleWindowState action); /** * Use native pixel size (MacBook pro 'retina'), if supported. */ -extern int GHOST_UseNativePixels(void); +extern bool GHOST_UseNativePixels(void); /** * Warp the cursor, if supported. */ -extern int GHOST_SupportsCursorWarp(void); +extern bool GHOST_SupportsCursorWarp(void); + +/** + * Support positioning windows (when false `wmWindow.x,y` are meaningless). + */ +extern bool GHOST_SupportsWindowPosition(void); + +/** + * Assign the callback which generates a back-trace (may be NULL). + */ +extern void GHOST_SetBacktraceHandler(GHOST_TBacktraceFn backtrace_fn); /** * Focus window after opening, or put them in the background. */ -extern void GHOST_UseWindowFocus(int use_focus); +extern void GHOST_UseWindowFocus(bool use_focus); /** * If window was opened using native pixel size, it returns scaling factor. diff --git a/intern/ghost/GHOST_ISystem.h b/intern/ghost/GHOST_ISystem.h index 8a4412403f7..d946d2d882a 100644 --- a/intern/ghost/GHOST_ISystem.h +++ b/intern/ghost/GHOST_ISystem.h @@ -133,6 +133,9 @@ class GHOST_ISystem { */ static GHOST_ISystem *getSystem(); + static GHOST_TBacktraceFn getBacktraceFn(); + static void setBacktraceFn(GHOST_TBacktraceFn backtrace_fn); + protected: /** * Constructor. @@ -310,6 +313,11 @@ class GHOST_ISystem { virtual bool supportsCursorWarp() = 0; /** + * Return true getting/setting the window position is supported. + */ + virtual bool supportsWindowPosition() = 0; + + /** * Focus window after opening, or put them in the background. */ virtual void useWindowFocus(const bool use_focus) = 0; @@ -482,6 +490,9 @@ class GHOST_ISystem { /** The one and only system */ static GHOST_ISystem *m_system; + /** Function to call that sets the back-trace. */ + static GHOST_TBacktraceFn m_backtrace_fn; + #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_ISystem") #endif diff --git a/intern/ghost/GHOST_Rect.h b/intern/ghost/GHOST_Rect.h index 0a5561b7d68..a8082bf015a 100644 --- a/intern/ghost/GHOST_Rect.h +++ b/intern/ghost/GHOST_Rect.h @@ -190,26 +190,34 @@ inline bool GHOST_Rect::isValid() const inline void GHOST_Rect::unionRect(const GHOST_Rect &r) { - if (r.m_l < m_l) + if (r.m_l < m_l) { m_l = r.m_l; - if (r.m_r > m_r) + } + if (r.m_r > m_r) { m_r = r.m_r; - if (r.m_t < m_t) + } + if (r.m_t < m_t) { m_t = r.m_t; - if (r.m_b > m_b) + } + if (r.m_b > m_b) { m_b = r.m_b; + } } inline void GHOST_Rect::unionPoint(int32_t x, int32_t y) { - if (x < m_l) + if (x < m_l) { m_l = x; - if (x > m_r) + } + if (x > m_r) { m_r = x; - if (y < m_t) + } + if (y < m_t) { m_t = y; - if (y > m_b) + } + if (y > m_b) { m_b = y; + } } inline void GHOST_Rect::wrapPoint(int32_t &x, int32_t &y, int32_t ofs, GHOST_TAxisFlag axis) @@ -223,16 +231,20 @@ inline void GHOST_Rect::wrapPoint(int32_t &x, int32_t &y, int32_t ofs, GHOST_TAx } if (axis & GHOST_kAxisX) { - while (x - ofs < m_l) + while (x - ofs < m_l) { x += w - (ofs * 2); - while (x + ofs > m_r) + } + while (x + ofs > m_r) { x -= w - (ofs * 2); + } } if (axis & GHOST_kGrabAxisY) { - while (y - ofs < m_t) + while (y - ofs < m_t) { y += h - (ofs * 2); - while (y + ofs > m_b) + } + while (y + ofs > m_b) { y -= h - (ofs * 2); + } } } diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index a7dc619f573..01a0a7652aa 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -42,6 +42,8 @@ GHOST_DECLARE_HANDLE(GHOST_EventConsumerHandle); GHOST_DECLARE_HANDLE(GHOST_ContextHandle); GHOST_DECLARE_HANDLE(GHOST_XrContextHandle); +typedef void (*GHOST_TBacktraceFn)(void *file_handle); + typedef struct { int flags; } GHOST_GLSettings; diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp index 8dd31a88ad3..c8127f59941 100644 --- a/intern/ghost/intern/GHOST_C-api.cpp +++ b/intern/ghost/intern/GHOST_C-api.cpp @@ -177,11 +177,11 @@ void GHOST_SetWindowUserData(GHOST_WindowHandle windowhandle, GHOST_TUserDataPtr window->setUserData(userdata); } -int GHOST_IsDialogWindow(GHOST_WindowHandle windowhandle) +bool GHOST_IsDialogWindow(GHOST_WindowHandle windowhandle) { GHOST_IWindow *window = (GHOST_IWindow *)windowhandle; - return (int)window->isDialog(); + return window->isDialog(); } GHOST_TSuccess GHOST_DisposeWindow(GHOST_SystemHandle systemhandle, @@ -193,17 +193,17 @@ GHOST_TSuccess GHOST_DisposeWindow(GHOST_SystemHandle systemhandle, return system->disposeWindow(window); } -int GHOST_ValidWindow(GHOST_SystemHandle systemhandle, GHOST_WindowHandle windowhandle) +bool GHOST_ValidWindow(GHOST_SystemHandle systemhandle, GHOST_WindowHandle windowhandle) { GHOST_ISystem *system = (GHOST_ISystem *)systemhandle; GHOST_IWindow *window = (GHOST_IWindow *)windowhandle; - return (int)system->validWindow(window); + return system->validWindow(window); } GHOST_WindowHandle GHOST_BeginFullScreen(GHOST_SystemHandle systemhandle, GHOST_DisplaySetting *setting, - const int stereoVisual) + const bool stereoVisual) { GHOST_ISystem *system = (GHOST_ISystem *)systemhandle; GHOST_IWindow *window = nullptr; @@ -228,11 +228,11 @@ GHOST_TSuccess GHOST_EndFullScreen(GHOST_SystemHandle systemhandle) return system->endFullScreen(); } -int GHOST_GetFullScreen(GHOST_SystemHandle systemhandle) +bool GHOST_GetFullScreen(GHOST_SystemHandle systemhandle) { GHOST_ISystem *system = (GHOST_ISystem *)systemhandle; - return (int)system->getFullScreen(); + return system->getFullScreen(); } GHOST_WindowHandle GHOST_GetWindowUnderCursor(GHOST_SystemHandle systemhandle, @@ -326,18 +326,18 @@ GHOST_TSuccess GHOST_SetCustomCursorShape(GHOST_WindowHandle windowhandle, return window->setCustomCursorShape(bitmap, mask, sizex, sizey, hotX, hotY, canInvertColor); } -int GHOST_GetCursorVisibility(GHOST_WindowHandle windowhandle) +bool GHOST_GetCursorVisibility(GHOST_WindowHandle windowhandle) { GHOST_IWindow *window = (GHOST_IWindow *)windowhandle; - return (int)window->getCursorVisibility(); + return window->getCursorVisibility(); } -GHOST_TSuccess GHOST_SetCursorVisibility(GHOST_WindowHandle windowhandle, int visible) +GHOST_TSuccess GHOST_SetCursorVisibility(GHOST_WindowHandle windowhandle, bool visible) { GHOST_IWindow *window = (GHOST_IWindow *)windowhandle; - return window->setCursorVisibility(visible ? true : false); + return window->setCursorVisibility(visible); } GHOST_TSuccess GHOST_GetCursorPosition(GHOST_SystemHandle systemhandle, int32_t *x, int32_t *y) @@ -392,28 +392,28 @@ void GHOST_GetCursorGrabState(GHOST_WindowHandle windowhandle, GHOST_TSuccess GHOST_GetModifierKeyState(GHOST_SystemHandle systemhandle, GHOST_TModifierKeyMask mask, - int *isDown) + bool *r_is_down) { GHOST_ISystem *system = (GHOST_ISystem *)systemhandle; GHOST_TSuccess result; - bool isdown = false; + bool is_down = false; - result = system->getModifierKeyState(mask, isdown); - *isDown = (int)isdown; + result = system->getModifierKeyState(mask, is_down); + *r_is_down = is_down; return result; } GHOST_TSuccess GHOST_GetButtonState(GHOST_SystemHandle systemhandle, GHOST_TButtonMask mask, - int *isDown) + bool *r_is_down) { GHOST_ISystem *system = (GHOST_ISystem *)systemhandle; GHOST_TSuccess result; - bool isdown = false; + bool is_down = false; - result = system->getButtonState(mask, isdown); - *isDown = (int)isdown; + result = system->getButtonState(mask, is_down); + *r_is_down = is_down; return result; } @@ -426,11 +426,11 @@ void GHOST_setNDOFDeadZone(float deadzone) } #endif -void GHOST_setAcceptDragOperation(GHOST_WindowHandle windowhandle, bool canAccept) +void GHOST_setAcceptDragOperation(GHOST_WindowHandle windowhandle, bool can_accept) { GHOST_IWindow *window = (GHOST_IWindow *)windowhandle; - window->setAcceptDragOperation(canAccept); + window->setAcceptDragOperation(can_accept); } GHOST_TEventType GHOST_GetEventType(GHOST_EventHandle eventhandle) @@ -489,11 +489,11 @@ void GHOST_SetTimerTaskUserData(GHOST_TimerTaskHandle timertaskhandle, GHOST_TUs timertask->setUserData(userdata); } -int GHOST_GetValid(GHOST_WindowHandle windowhandle) +bool GHOST_GetValid(GHOST_WindowHandle windowhandle) { GHOST_IWindow *window = (GHOST_IWindow *)windowhandle; - return (int)window->getValid(); + return window->getValid(); } GHOST_TDrawingContextType GHOST_GetDrawingContextType(GHOST_WindowHandle windowhandle) @@ -817,25 +817,37 @@ void GHOST_putClipboard(const char *buffer, bool selection) system->putClipboard(buffer, selection); } -int setConsoleWindowState(GHOST_TConsoleWindowState action) +bool GHOST_setConsoleWindowState(GHOST_TConsoleWindowState action) { GHOST_ISystem *system = GHOST_ISystem::getSystem(); - return system->setConsoleWindowState(action); + /* FIXME: use `bool` instead of int for this value. */ + return (bool)system->setConsoleWindowState(action); } -int GHOST_UseNativePixels(void) +bool GHOST_UseNativePixels(void) { GHOST_ISystem *system = GHOST_ISystem::getSystem(); return system->useNativePixel(); } -int GHOST_SupportsCursorWarp(void) +bool GHOST_SupportsCursorWarp(void) { GHOST_ISystem *system = GHOST_ISystem::getSystem(); return system->supportsCursorWarp(); } -void GHOST_UseWindowFocus(int use_focus) +bool GHOST_SupportsWindowPosition(void) +{ + GHOST_ISystem *system = GHOST_ISystem::getSystem(); + return system->supportsWindowPosition(); +} + +void GHOST_SetBacktraceHandler(GHOST_TBacktraceFn backtrace_fn) +{ + GHOST_ISystem::setBacktraceFn(backtrace_fn); +} + +void GHOST_UseWindowFocus(bool use_focus) { GHOST_ISystem *system = GHOST_ISystem::getSystem(); return system->useWindowFocus(use_focus); diff --git a/intern/ghost/intern/GHOST_CallbackEventConsumer.cpp b/intern/ghost/intern/GHOST_CallbackEventConsumer.cpp index 72aeebdc876..9f14d56cd9a 100644 --- a/intern/ghost/intern/GHOST_CallbackEventConsumer.cpp +++ b/intern/ghost/intern/GHOST_CallbackEventConsumer.cpp @@ -22,5 +22,5 @@ GHOST_CallbackEventConsumer::GHOST_CallbackEventConsumer(GHOST_EventCallbackProc bool GHOST_CallbackEventConsumer::processEvent(GHOST_IEvent *event) { - return m_eventCallback((GHOST_EventHandle)event, m_userData) != 0; + return m_eventCallback((GHOST_EventHandle)event, m_userData); } diff --git a/intern/ghost/intern/GHOST_ContextGLX.cpp b/intern/ghost/intern/GHOST_ContextGLX.cpp index baabdc6c521..b4a076e4598 100644 --- a/intern/ghost/intern/GHOST_ContextGLX.cpp +++ b/intern/ghost/intern/GHOST_ContextGLX.cpp @@ -105,7 +105,7 @@ GHOST_TSuccess GHOST_ContextGLX::initializeDrawingContext() GHOST_X11_ERROR_HANDLERS_OVERRIDE(handler_store); /* -------------------------------------------------------------------- */ - /* Begin Inline Glew */ + /* Begin Inline GLEW. */ #ifdef USE_GLXEW_INIT_WORKAROUND const GLubyte *extStart = (GLubyte *)""; @@ -142,11 +142,11 @@ GHOST_TSuccess GHOST_ContextGLX::initializeDrawingContext() "GLX_EXT_create_context_es2_profile", extStart, extEnd); # endif /* WITH_GLEW_ES */ - /* End Inline Glew */ + /* End Inline GLEW. */ /* -------------------------------------------------------------------- */ #else - /* important to initialize only glxew (_not_ glew), - * since this breaks w/ Mesa's `swrast`, see: T46431 */ + /* Important to initialize only glxew (_not_ GLEW), + * since this breaks w/ Mesa's `swrast`, see: T46431. */ glxewInit(); #endif /* USE_GLXEW_INIT_WORKAROUND */ @@ -395,7 +395,7 @@ int GHOST_X11_GL_GetAttributes( return i; } -/* excuse inlining part of glew */ +/* Excuse inlining part of GLEW. */ #ifdef USE_GLXEW_INIT_WORKAROUND static GLuint _glewStrLen(const GLubyte *s) { diff --git a/intern/ghost/intern/GHOST_DropTargetX11.cpp b/intern/ghost/intern/GHOST_DropTargetX11.cpp index 70c2eb8c29e..900e46c3732 100644 --- a/intern/ghost/intern/GHOST_DropTargetX11.cpp +++ b/intern/ghost/intern/GHOST_DropTargetX11.cpp @@ -31,7 +31,7 @@ int GHOST_DropTargetX11::m_refCounter = 0; #define dndTypePlainText m_dndTypes[dndTypePlainTextID] #define dndTypeOctetStream m_dndTypes[dndTypeOctetStreamID] -void GHOST_DropTargetX11::Initialize(void) +void GHOST_DropTargetX11::Initialize() { Display *display = m_system->getXDisplay(); int dndTypesCount = sizeof(m_dndMimeTypes) / sizeof(char *); @@ -60,7 +60,7 @@ void GHOST_DropTargetX11::Initialize(void) m_dndActions[counter++] = 0; } -void GHOST_DropTargetX11::Uninitialize(void) +void GHOST_DropTargetX11::Uninitialize() { xdnd_shut(&m_dndClass); @@ -98,12 +98,12 @@ GHOST_DropTargetX11::~GHOST_DropTargetX11() /* Based on: https://stackoverflow.com/a/2766963/432509 */ -typedef enum DecodeState_e { +using DecodeState_e = enum DecodeState_e { /** Searching for an ampersand to convert. */ STATE_SEARCH = 0, /** Convert the two proceeding characters from hex. */ STATE_CONVERTING -} DecodeState_e; +}; void GHOST_DropTargetX11::UrlDecode(char *decodedOut, int bufferSize, const char *encodedIn) { @@ -122,7 +122,7 @@ void GHOST_DropTargetX11::UrlDecode(char *decodedOut, int bufferSize, const char case STATE_SEARCH: if (encodedIn[i] != '%') { strncat(decodedOut, &encodedIn[i], 1); - assert(strlen(decodedOut) < bufferSize); + assert((int)strlen(decodedOut) < bufferSize); break; } @@ -145,18 +145,19 @@ void GHOST_DropTargetX11::UrlDecode(char *decodedOut, int bufferSize, const char /* Ensure both characters are hexadecimal */ for (j = 0; j < 2; ++j) { - if (!isxdigit(tempNumBuf[j])) + if (!isxdigit(tempNumBuf[j])) { bothDigits = false; + } } - if (!bothDigits) + if (!bothDigits) { break; - + } /* Convert two hexadecimal characters into one character */ sscanf(tempNumBuf, "%x", &asciiCharacter); /* Ensure we aren't going to overflow */ - assert(strlen(decodedOut) < bufferSize); + assert((int)strlen(decodedOut) < bufferSize); /* Concatenate this character onto the output */ strncat(decodedOut, (char *)&asciiCharacter, 1); diff --git a/intern/ghost/intern/GHOST_EventPrinter.cpp b/intern/ghost/intern/GHOST_EventPrinter.cpp index 758938e879e..2620bcc075d 100644 --- a/intern/ghost/intern/GHOST_EventPrinter.cpp +++ b/intern/ghost/intern/GHOST_EventPrinter.cpp @@ -12,7 +12,7 @@ #include "GHOST_EventKey.h" #include <iostream> -#include <stdio.h> +#include <cstdio> bool GHOST_EventPrinter::processEvent(GHOST_IEvent *event) { @@ -20,9 +20,9 @@ bool GHOST_EventPrinter::processEvent(GHOST_IEvent *event) GHOST_ASSERT(event, "event==0"); - if (event->getType() == GHOST_kEventWindowUpdate) + if (event->getType() == GHOST_kEventWindowUpdate) { return false; - + } std::cout << "GHOST_EventPrinter::processEvent, time: " << (int32_t)event->getTime() << ", type: "; switch (event->getType()) { @@ -106,8 +106,9 @@ bool GHOST_EventPrinter::processEvent(GHOST_IEvent *event) std::cout << " type : GHOST_kDragnDropTypeFilenames,"; std::cout << "\n Received " << strArray->count << " filename" << (strArray->count > 1 ? "s:" : ":"); - for (i = 0; i < strArray->count; i++) + for (i = 0; i < strArray->count; i++) { std::cout << "\n File[" << i << "] : " << strArray->strings[i]; + } } break; default: break; @@ -117,10 +118,12 @@ bool GHOST_EventPrinter::processEvent(GHOST_IEvent *event) case GHOST_kEventOpenMainFile: { GHOST_TEventDataPtr eventData = ((GHOST_IEvent *)event)->getData(); - if (eventData) + if (eventData) { std::cout << "GHOST_kEventOpenMainFile for path : " << (char *)eventData; - else + } + else { std::cout << "GHOST_kEventOpenMainFile with no path specified!!"; + } } break; case GHOST_kEventQuitRequest: @@ -167,7 +170,7 @@ void GHOST_EventPrinter::getKeyString(GHOST_TKey key, char str[32]) const sprintf(str, "F%d", key - GHOST_kKeyF1 + 1); } else { - const char *tstr = NULL; + const char *tstr = nullptr; switch (key) { case GHOST_kKeyBackSpace: tstr = "BackSpace"; diff --git a/intern/ghost/intern/GHOST_ISystem.cpp b/intern/ghost/intern/GHOST_ISystem.cpp index 87111306ec7..745d5faeed4 100644 --- a/intern/ghost/intern/GHOST_ISystem.cpp +++ b/intern/ghost/intern/GHOST_ISystem.cpp @@ -31,6 +31,8 @@ GHOST_ISystem *GHOST_ISystem::m_system = nullptr; +GHOST_TBacktraceFn GHOST_ISystem::m_backtrace_fn = nullptr; + GHOST_TSuccess GHOST_ISystem::createSystem() { GHOST_TSuccess success; @@ -89,3 +91,13 @@ GHOST_ISystem *GHOST_ISystem::getSystem() { return m_system; } + +GHOST_TBacktraceFn GHOST_ISystem::getBacktraceFn() +{ + return GHOST_ISystem::m_backtrace_fn; +} + +void GHOST_ISystem::setBacktraceFn(GHOST_TBacktraceFn backtrace_fn) +{ + GHOST_ISystem::m_backtrace_fn = backtrace_fn; +} diff --git a/intern/ghost/intern/GHOST_NDOFManager.cpp b/intern/ghost/intern/GHOST_NDOFManager.cpp index 7f6b5f53316..2298ba86521 100644 --- a/intern/ghost/intern/GHOST_NDOFManager.cpp +++ b/intern/ghost/intern/GHOST_NDOFManager.cpp @@ -6,10 +6,10 @@ #include "GHOST_EventNDOF.h" #include "GHOST_WindowManager.h" -#include <limits.h> -#include <math.h> -#include <stdio.h> /* For error/info reporting. */ -#include <string.h> /* For memory functions. */ +#include <climits> +#include <cmath> +#include <cstdio> /* For error/info reporting. */ +#include <cstring> /* For memory functions. */ #ifdef DEBUG_NDOF_MOTION /* Printable version of each GHOST_TProgress value. */ @@ -255,8 +255,9 @@ bool GHOST_NDOFManager::setDevice(unsigned short vendor_id, unsigned short produ printf("ndof: unknown device %04hx:%04hx\n", vendor_id, product_id); } - if (m_buttonMask == 0) + if (m_buttonMask == 0) { m_buttonMask = (int)~(UINT_MAX << m_buttonCount); + } #ifdef DEBUG_NDOF_BUTTONS printf("ndof: %d buttons -> hex:%X\n", m_buttonCount, m_buttonMask); diff --git a/intern/ghost/intern/GHOST_NDOFManagerUnix.cpp b/intern/ghost/intern/GHOST_NDOFManagerUnix.cpp index 7e53ce45f70..7770f5f39ce 100644 --- a/intern/ghost/intern/GHOST_NDOFManagerUnix.cpp +++ b/intern/ghost/intern/GHOST_NDOFManagerUnix.cpp @@ -32,10 +32,11 @@ GHOST_NDOFManagerUnix::GHOST_NDOFManagerUnix(GHOST_System &sys) char line[MAX_LINE_LENGTH] = {0}; while (fgets(line, MAX_LINE_LENGTH, command_output)) { unsigned short vendor_id = 0, product_id = 0; - if (sscanf(line, "Bus %*d Device %*d: ID %hx:%hx", &vendor_id, &product_id) == 2) + if (sscanf(line, "Bus %*d Device %*d: ID %hx:%hx", &vendor_id, &product_id) == 2) { if (setDevice(vendor_id, product_id)) { break; /* stop looking once the first 3D mouse is found */ } + } } pclose(command_output); } @@ -44,8 +45,9 @@ GHOST_NDOFManagerUnix::GHOST_NDOFManagerUnix(GHOST_System &sys) GHOST_NDOFManagerUnix::~GHOST_NDOFManagerUnix() { - if (m_available) + if (m_available) { spnav_close(); + } } bool GHOST_NDOFManagerUnix::available() diff --git a/intern/ghost/intern/GHOST_System.cpp b/intern/ghost/intern/GHOST_System.cpp index 2aad4f2ea29..cc8d0915c5a 100644 --- a/intern/ghost/intern/GHOST_System.cpp +++ b/intern/ghost/intern/GHOST_System.cpp @@ -158,7 +158,7 @@ GHOST_TSuccess GHOST_System::updateFullScreen(const GHOST_DisplaySetting &settin return success; } -GHOST_TSuccess GHOST_System::endFullScreen(void) +GHOST_TSuccess GHOST_System::endFullScreen() { GHOST_TSuccess success = GHOST_kFailure; GHOST_ASSERT(m_windowManager, "GHOST_System::endFullScreen(): invalid window manager"); @@ -177,7 +177,7 @@ GHOST_TSuccess GHOST_System::endFullScreen(void) return success; } -bool GHOST_System::getFullScreen(void) +bool GHOST_System::getFullScreen() { bool fullScreen; if (m_windowManager) { @@ -289,7 +289,7 @@ void GHOST_System::setTabletAPI(GHOST_TTabletAPI api) m_tabletAPI = api; } -GHOST_TTabletAPI GHOST_System::getTabletAPI(void) +GHOST_TTabletAPI GHOST_System::getTabletAPI() { return m_tabletAPI; } @@ -319,9 +319,7 @@ GHOST_TSuccess GHOST_System::init() if (m_timerManager && m_windowManager && m_eventManager) { return GHOST_kSuccess; } - else { - return GHOST_kFailure; - } + return GHOST_kFailure; } GHOST_TSuccess GHOST_System::exit() @@ -357,10 +355,12 @@ GHOST_TSuccess GHOST_System::createFullScreenWindow(GHOST_Window **window, { GHOST_GLSettings glSettings = {0}; - if (stereoVisual) + if (stereoVisual) { glSettings.flags |= GHOST_glStereoVisual; - if (alphaBackground) + } + if (alphaBackground) { glSettings.flags |= GHOST_glAlphaBackground; + } /* NOTE: don't use #getCurrentDisplaySetting() because on X11 we may * be zoomed in and the desktop may be bigger than the viewport. */ @@ -395,6 +395,11 @@ bool GHOST_System::supportsCursorWarp() return true; } +bool GHOST_System::supportsWindowPosition() +{ + return true; +} + void GHOST_System::initDebug(GHOST_Debug debug) { m_is_debug_enabled = debug.flags & GHOST_kDebugDefault; diff --git a/intern/ghost/intern/GHOST_System.h b/intern/ghost/intern/GHOST_System.h index 9f2fba1a2c6..b60ce09f743 100644 --- a/intern/ghost/intern/GHOST_System.h +++ b/intern/ghost/intern/GHOST_System.h @@ -152,6 +152,7 @@ class GHOST_System : public GHOST_ISystem { bool m_nativePixel; bool supportsCursorWarp(void); + bool supportsWindowPosition(void); /** * Focus window after opening, or put them in the background. diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index a5789a2526f..2b0abd2cc41 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -29,7 +29,10 @@ #include "GHOST_WaylandCursorSettings.h" #include <pointer-constraints-client-protocol.h> #include <relative-pointer-client-protocol.h> +#include <tablet-client-protocol.h> #include <wayland-cursor.h> +#include <xdg-output-client-protocol.h> + #include <xkbcommon/xkbcommon.h> #include <fcntl.h> @@ -37,6 +40,9 @@ #include <unistd.h> #include <cstring> +#include <mutex> + +static GHOST_WindowWayland *window_from_surface(struct wl_surface *surface); /* -------------------------------------------------------------------- */ /** \name Private Types & Defines @@ -57,105 +63,155 @@ #define BTN_BACK 0x116 // #define BTN_TASK 0x117 /* UNUSED. */ +/** + * Tablet events, also from `linux/input-event-codes.h`. + */ +#define BTN_STYLUS 0x14b /* Use as right-mouse. */ +#define BTN_STYLUS2 0x14c /* Use as middle-mouse. */ +/* NOTE(@campbellbarton): Map to an additional button (not sure which hardware uses this). */ +#define BTN_STYLUS3 0x149 + struct buffer_t { - void *data; - size_t size; + void *data = nullptr; + size_t size = 0; }; struct cursor_t { - bool visible; - struct wl_surface *surface = nullptr; - struct wl_buffer *buffer; - struct wl_cursor_image image; + bool visible = false; + struct wl_surface *wl_surface = nullptr; + struct wl_buffer *wl_buffer = nullptr; + struct wl_cursor_image wl_image = {0}; + struct wl_cursor_theme *wl_theme = nullptr; struct buffer_t *file_buffer = nullptr; - struct wl_cursor_theme *theme = nullptr; - int size; + int size = 0; std::string theme_name; /** Outputs on which the cursor is visible. */ std::unordered_set<const output_t *> outputs; int scale = 1; }; +/** + * A single tablet can have multiple tools (pen, eraser, brush... etc). + * WAYLAND exposes tools via #zwp_tablet_tool_v2. + * Since are no API's to access properties of the tool, store the values here. + */ +struct tablet_tool_input_t { + struct input_t *input = nullptr; + struct wl_surface *cursor_surface = nullptr; + + GHOST_TabletData data = GHOST_TABLET_DATA_NONE; +}; + struct data_offer_t { std::unordered_set<std::string> types; - uint32_t source_actions; - uint32_t dnd_action; - struct wl_data_offer *id; - std::atomic<bool> in_use; + uint32_t source_actions = 0; + uint32_t dnd_action = 0; + struct wl_data_offer *id = nullptr; + std::atomic<bool> in_use = false; struct { - int x, y; + /** Compatible with #input_t.xy coordinates. */ + wl_fixed_t xy[2] = {0, 0}; } dnd; }; struct data_source_t { - struct wl_data_source *data_source; - /** Last device that was active. */ - uint32_t source_serial; - char *buffer_out; + struct wl_data_source *data_source = nullptr; + char *buffer_out = nullptr; }; struct key_repeat_payload_t { - GHOST_SystemWayland *system; - GHOST_IWindow *window; - GHOST_TKey key; - GHOST_TEventKeyData key_data; + GHOST_SystemWayland *system = nullptr; + GHOST_IWindow *window = nullptr; + GHOST_TEventKeyData key_data = {GHOST_kKeyUnknown}; }; struct input_t { - GHOST_SystemWayland *system; + GHOST_SystemWayland *system = nullptr; std::string name; - struct wl_seat *seat; - struct wl_pointer *pointer = nullptr; - struct wl_keyboard *keyboard = nullptr; - - uint32_t pointer_serial; - int x, y; - GHOST_Buttons buttons; + struct wl_seat *wl_seat = nullptr; + struct wl_pointer *wl_pointer = nullptr; + struct wl_keyboard *wl_keyboard = nullptr; + struct zwp_tablet_seat_v2 *tablet_seat = nullptr; + + /** All currently active tablet tools (needed for changing the cursor). */ + std::unordered_set<zwp_tablet_tool_v2 *> tablet_tools; + + uint32_t pointer_serial = 0; + uint32_t tablet_serial = 0; + + /** Use to check if the last cursor input was tablet or pointer. */ + uint32_t cursor_serial = 0; + + /** + * High precision mouse coordinates (pointer or tablet). + * + * The following example converts these values to screen coordinates. + * \code{.cc} + * const wl_fixed_t scale = win->scale(); + * const int event_xy[2] = { + * wl_fixed_to_int(scale * input->xy[0]), + * wl_fixed_to_int(scale * input->xy[1]), + * }; + * \endcode + */ + wl_fixed_t xy[2] = {0, 0}; + GHOST_Buttons buttons = GHOST_Buttons(); struct cursor_t cursor; - struct zwp_relative_pointer_v1 *relative_pointer; - struct zwp_locked_pointer_v1 *locked_pointer; - struct zwp_confined_pointer_v1 *confined_pointer; + struct zwp_relative_pointer_v1 *relative_pointer = nullptr; + struct zwp_locked_pointer_v1 *locked_pointer = nullptr; + struct zwp_confined_pointer_v1 *confined_pointer = nullptr; - struct xkb_context *xkb_context; - struct xkb_state *xkb_state; + struct xkb_context *xkb_context = nullptr; + struct xkb_state *xkb_state = nullptr; struct { /** Key repetition in character per second. */ - int32_t rate; + int32_t rate = 0; /** Time (milliseconds) after which to start repeating keys. */ - int32_t delay; + int32_t delay = 0; /** Timer for key repeats. */ GHOST_ITimerTask *timer = nullptr; } key_repeat; + struct wl_surface *focus_tablet = nullptr; struct wl_surface *focus_pointer = nullptr; struct wl_surface *focus_keyboard = nullptr; + struct wl_surface *focus_dnd = nullptr; struct wl_data_device *data_device = nullptr; /** Drag & Drop. */ - struct data_offer_t *data_offer_dnd; + struct data_offer_t *data_offer_dnd = nullptr; + std::mutex data_offer_dnd_mutex; + /** Copy & Paste. */ - struct data_offer_t *data_offer_copy_paste; + struct data_offer_t *data_offer_copy_paste = nullptr; + std::mutex data_offer_copy_paste_mutex; - struct data_source_t *data_source; + struct data_source_t *data_source = nullptr; + std::mutex data_source_mutex; + + /** Last device that was active. */ + uint32_t data_source_serial = 0; }; struct display_t { - GHOST_SystemWayland *system; + GHOST_SystemWayland *system = nullptr; - struct wl_display *display; + struct wl_display *display = nullptr; struct wl_compositor *compositor = nullptr; struct xdg_wm_base *xdg_shell = nullptr; struct zxdg_decoration_manager_v1 *xdg_decoration_manager = nullptr; + struct zxdg_output_manager_v1 *xdg_output_manager = nullptr; struct wl_shm *shm = nullptr; std::vector<output_t *> outputs; std::vector<input_t *> inputs; struct { std::string theme; - int size; + int size = 0; } cursor; struct wl_data_device_manager *data_device_manager = nullptr; + struct zwp_tablet_manager_v2 *tablet_manager = nullptr; struct zwp_relative_pointer_manager_v1 *relative_pointer_manager = nullptr; struct zwp_pointer_constraints_v1 *pointer_constraints = nullptr; @@ -171,54 +227,97 @@ struct display_t { static GHOST_WindowManager *window_manager = nullptr; +/** Check this lock before accessing `GHOST_SystemWayland::selection` from a thread. */ +static std::mutex system_selection_mutex; + +/** + * Callback for WAYLAND to run when there is an error. + * + * \note It's useful to set a break-point on this function as some errors are fatal + * (for all intents and purposes) but don't crash the process. + */ +static void ghost_wayland_log_handler(const char *msg, va_list arg) +{ + fprintf(stderr, "GHOST/Wayland: "); + vfprintf(stderr, msg, arg); /* Includes newline. */ + + GHOST_TBacktraceFn backtrace_fn = GHOST_ISystem::getBacktraceFn(); + if (backtrace_fn) { + backtrace_fn(stderr); /* Includes newline. */ + } +} + static void display_destroy(display_t *d) { if (d->data_device_manager) { wl_data_device_manager_destroy(d->data_device_manager); } + if (d->tablet_manager) { + zwp_tablet_manager_v2_destroy(d->tablet_manager); + } + for (output_t *output : d->outputs) { - wl_output_destroy(output->output); + wl_output_destroy(output->wl_output); delete output; } for (input_t *input : d->inputs) { - if (input->data_source) { - free(input->data_source->buffer_out); - if (input->data_source->data_source) { - wl_data_source_destroy(input->data_source->data_source); + + /* First handle members that require locking. + * While highly unlikely, it's possible they are being used while this function runs. */ + { + std::lock_guard lock{input->data_source_mutex}; + if (input->data_source) { + free(input->data_source->buffer_out); + if (input->data_source->data_source) { + wl_data_source_destroy(input->data_source->data_source); + } + delete input->data_source; } - delete input->data_source; } - if (input->data_offer_copy_paste) { - wl_data_offer_destroy(input->data_offer_copy_paste->id); - delete input->data_offer_copy_paste; + + { + std::lock_guard lock{input->data_offer_dnd_mutex}; + if (input->data_offer_dnd) { + wl_data_offer_destroy(input->data_offer_dnd->id); + delete input->data_offer_dnd; + } + } + + { + std::lock_guard lock{input->data_offer_copy_paste_mutex}; + if (input->data_offer_copy_paste) { + wl_data_offer_destroy(input->data_offer_copy_paste->id); + delete input->data_offer_copy_paste; + } } + if (input->data_device) { wl_data_device_release(input->data_device); } - if (input->pointer) { + if (input->wl_pointer) { if (input->cursor.file_buffer) { munmap(input->cursor.file_buffer->data, input->cursor.file_buffer->size); delete input->cursor.file_buffer; } - if (input->cursor.surface) { - wl_surface_destroy(input->cursor.surface); + if (input->cursor.wl_surface) { + wl_surface_destroy(input->cursor.wl_surface); } - if (input->cursor.theme) { - wl_cursor_theme_destroy(input->cursor.theme); + if (input->cursor.wl_theme) { + wl_cursor_theme_destroy(input->cursor.wl_theme); } - if (input->pointer) { - wl_pointer_destroy(input->pointer); + if (input->wl_pointer) { + wl_pointer_destroy(input->wl_pointer); } } - if (input->keyboard) { + if (input->wl_keyboard) { if (input->key_repeat.timer) { delete static_cast<key_repeat_payload_t *>(input->key_repeat.timer->getUserData()); input->system->removeTimer(input->key_repeat.timer); input->key_repeat.timer = nullptr; } - wl_keyboard_destroy(input->keyboard); + wl_keyboard_destroy(input->wl_keyboard); } if (input->xkb_state) { xkb_state_unref(input->xkb_state); @@ -226,7 +325,7 @@ static void display_destroy(display_t *d) if (input->xkb_context) { xkb_context_unref(input->xkb_context); } - wl_seat_destroy(input->seat); + wl_seat_destroy(input->wl_seat); delete input; } @@ -374,6 +473,27 @@ static GHOST_TKey xkb_map_gkey(const xkb_keysym_t &sym) return gkey; } +static GHOST_TTabletMode tablet_tool_map_type(enum zwp_tablet_tool_v2_type wl_tablet_tool_type) +{ + switch (wl_tablet_tool_type) { + case ZWP_TABLET_TOOL_V2_TYPE_ERASER: { + return GHOST_kTabletModeEraser; + } + case ZWP_TABLET_TOOL_V2_TYPE_PEN: + case ZWP_TABLET_TOOL_V2_TYPE_BRUSH: + case ZWP_TABLET_TOOL_V2_TYPE_PENCIL: + case ZWP_TABLET_TOOL_V2_TYPE_AIRBRUSH: + case ZWP_TABLET_TOOL_V2_TYPE_FINGER: + case ZWP_TABLET_TOOL_V2_TYPE_MOUSE: + case ZWP_TABLET_TOOL_V2_TYPE_LENS: { + return GHOST_kTabletModeStylus; + } + } + + GHOST_PRINT("unknown tablet tool: " << wl_tablet_tool_type << std::endl); + return GHOST_kTabletModeStylus; +} + static const int default_cursor_size = 24; static const std::unordered_map<GHOST_TStandardCursor, std::string> cursors = { @@ -451,7 +571,7 @@ static const std::vector<std::string> mime_send = { * an event is received from the compositor. * \{ */ -static void relative_pointer_relative_motion( +static void relative_pointer_handle_relative_motion( void *data, struct zwp_relative_pointer_v1 * /*zwp_relative_pointer_v1*/, uint32_t /*utime_hi*/, @@ -462,23 +582,24 @@ static void relative_pointer_relative_motion( wl_fixed_t /*dy_unaccel*/) { input_t *input = static_cast<input_t *>(data); - - input->x += wl_fixed_to_int(dx); - input->y += wl_fixed_to_int(dy); - - GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>( - wl_surface_get_user_data(input->focus_pointer)); + GHOST_WindowWayland *win = window_from_surface(input->focus_pointer); + if (!win) { + return; + } + const wl_fixed_t scale = win->scale(); + input->xy[0] += dx / scale; + input->xy[1] += dy / scale; input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(), GHOST_kEventCursorMove, win, - input->x, - input->y, + wl_fixed_to_int(scale * input->xy[0]), + wl_fixed_to_int(scale * input->xy[1]), GHOST_TABLET_DATA_NONE)); } static const zwp_relative_pointer_v1_listener relative_pointer_listener = { - relative_pointer_relative_motion, + relative_pointer_handle_relative_motion, }; /** \} */ @@ -489,21 +610,28 @@ static const zwp_relative_pointer_v1_listener relative_pointer_listener = { static void dnd_events(const input_t *const input, const GHOST_TEventType event) { + /* NOTE: `input->data_offer_dnd_mutex` must already be locked. */ const uint64_t time = input->system->getMilliSeconds(); - GHOST_IWindow *const window = static_cast<GHOST_WindowWayland *>( - wl_surface_get_user_data(input->focus_pointer)); + GHOST_WindowWayland *const win = static_cast<GHOST_WindowWayland *>( + wl_surface_get_user_data(input->focus_dnd)); + if (!win) { + return; + } + const wl_fixed_t scale = win->scale(); + const int event_xy[2] = { + wl_fixed_to_int(scale * input->data_offer_dnd->dnd.xy[0]), + wl_fixed_to_int(scale * input->data_offer_dnd->dnd.xy[1]), + }; + for (const std::string &type : mime_preference_order) { - input->system->pushEvent(new GHOST_EventDragnDrop(time, - event, - mime_dnd.at(type), - window, - input->data_offer_dnd->dnd.x, - input->data_offer_dnd->dnd.y, - nullptr)); + input->system->pushEvent(new GHOST_EventDragnDrop( + time, event, mime_dnd.at(type), win, event_xy[0], event_xy[1], nullptr)); } } -static std::string read_pipe(data_offer_t *data_offer, const std::string mime_receive) +static std::string read_pipe(data_offer_t *data_offer, + const std::string mime_receive, + std::mutex *mutex) { int pipefd[2]; if (pipe(pipefd) != 0) { @@ -512,6 +640,13 @@ static std::string read_pipe(data_offer_t *data_offer, const std::string mime_re wl_data_offer_receive(data_offer->id, mime_receive.c_str(), pipefd[1]); close(pipefd[1]); + data_offer->in_use.store(false); + + if (mutex) { + mutex->unlock(); + } + /* WARNING: `data_offer` may be freed from now on. */ + std::string data; ssize_t len; char buffer[4096]; @@ -519,7 +654,6 @@ static std::string read_pipe(data_offer_t *data_offer, const std::string mime_re data.insert(data.end(), buffer, buffer + len); } close(pipefd[0]); - data_offer->in_use.store(false); return data; } @@ -530,26 +664,29 @@ static std::string read_pipe(data_offer_t *data_offer, const std::string mime_re * Sent when a target accepts pointer_focus or motion events. If * a target does not accept any of the offered types, type is nullptr. */ -static void data_source_target(void * /*data*/, - struct wl_data_source * /*wl_data_source*/, - const char * /*mime_type*/) +static void data_source_handle_target(void * /*data*/, + struct wl_data_source * /*wl_data_source*/, + const char * /*mime_type*/) { /* pass */ } -static void data_source_send(void *data, - struct wl_data_source * /*wl_data_source*/, - const char * /*mime_type*/, - int32_t fd) +static void data_source_handle_send(void *data, + struct wl_data_source * /*wl_data_source*/, + const char * /*mime_type*/, + int32_t fd) { - const char *const buffer = static_cast<char *>(data); + input_t *input = static_cast<input_t *>(data); + std::lock_guard lock{input->data_source_mutex}; + + const char *const buffer = input->data_source->buffer_out; if (write(fd, buffer, strlen(buffer)) < 0) { GHOST_PRINT("error writing to clipboard: " << std::strerror(errno) << std::endl); } close(fd); } -static void data_source_cancelled(void * /*data*/, struct wl_data_source *wl_data_source) +static void data_source_handle_cancelled(void * /*data*/, struct wl_data_source *wl_data_source) { wl_data_source_destroy(wl_data_source); } @@ -561,8 +698,8 @@ static void data_source_cancelled(void * /*data*/, struct wl_data_source *wl_dat * indicate acceptance, #wl_data_source.cancelled may still be * emitted afterwards if the drop destination does not accept any mime type. */ -static void data_source_dnd_drop_performed(void * /*data*/, - struct wl_data_source * /*wl_data_source*/) +static void data_source_handle_dnd_drop_performed(void * /*data*/, + struct wl_data_source * /*wl_data_source*/) { /* pass */ } @@ -574,7 +711,8 @@ static void data_source_dnd_drop_performed(void * /*data*/, * source, so the client is now free to destroy this data source * and free all associated data. */ -static void data_source_dnd_finished(void * /*data*/, struct wl_data_source * /*wl_data_source*/) +static void data_source_handle_dnd_finished(void * /*data*/, + struct wl_data_source * /*wl_data_source*/) { /* pass */ } @@ -586,20 +724,20 @@ static void data_source_dnd_finished(void * /*data*/, struct wl_data_source * /* * after matching the source/destination side actions. Only one * action (or none) will be offered here. */ -static void data_source_action(void * /*data*/, - struct wl_data_source * /*wl_data_source*/, - uint32_t /*dnd_action*/) +static void data_source_handle_action(void * /*data*/, + struct wl_data_source * /*wl_data_source*/, + uint32_t /*dnd_action*/) { /* pass */ } static const struct wl_data_source_listener data_source_listener = { - data_source_target, - data_source_send, - data_source_cancelled, - data_source_dnd_drop_performed, - data_source_dnd_finished, - data_source_action, + data_source_handle_target, + data_source_handle_send, + data_source_handle_cancelled, + data_source_handle_dnd_drop_performed, + data_source_handle_dnd_finished, + data_source_handle_action, }; /** \} */ @@ -608,31 +746,31 @@ static const struct wl_data_source_listener data_source_listener = { /** \name Listener (Data Offer), #wl_data_offer_listener * \{ */ -static void data_offer_offer(void *data, - struct wl_data_offer * /*wl_data_offer*/, - const char *mime_type) +static void data_offer_handle_offer(void *data, + struct wl_data_offer * /*wl_data_offer*/, + const char *mime_type) { static_cast<data_offer_t *>(data)->types.insert(mime_type); } -static void data_offer_source_actions(void *data, - struct wl_data_offer * /*wl_data_offer*/, - uint32_t source_actions) +static void data_offer_handle_source_actions(void *data, + struct wl_data_offer * /*wl_data_offer*/, + uint32_t source_actions) { static_cast<data_offer_t *>(data)->source_actions = source_actions; } -static void data_offer_action(void *data, - struct wl_data_offer * /*wl_data_offer*/, - uint32_t dnd_action) +static void data_offer_handle_action(void *data, + struct wl_data_offer * /*wl_data_offer*/, + uint32_t dnd_action) { static_cast<data_offer_t *>(data)->dnd_action = dnd_action; } static const struct wl_data_offer_listener data_offer_listener = { - data_offer_offer, - data_offer_source_actions, - data_offer_action, + data_offer_handle_offer, + data_offer_handle_source_actions, + data_offer_handle_action, }; /** \} */ @@ -641,30 +779,32 @@ static const struct wl_data_offer_listener data_offer_listener = { /** \name Listener (Data Device), #wl_data_device_listener * \{ */ -static void data_device_data_offer(void * /*data*/, - struct wl_data_device * /*wl_data_device*/, - struct wl_data_offer *id) +static void data_device_handle_data_offer(void * /*data*/, + struct wl_data_device * /*wl_data_device*/, + struct wl_data_offer *id) { data_offer_t *data_offer = new data_offer_t; data_offer->id = id; wl_data_offer_add_listener(id, &data_offer_listener, data_offer); } -static void data_device_enter(void *data, - struct wl_data_device * /*wl_data_device*/, - uint32_t serial, - struct wl_surface * /*surface*/, - wl_fixed_t x, - wl_fixed_t y, - struct wl_data_offer *id) +static void data_device_handle_enter(void *data, + struct wl_data_device * /*wl_data_device*/, + uint32_t serial, + struct wl_surface *surface, + wl_fixed_t x, + wl_fixed_t y, + struct wl_data_offer *id) { input_t *input = static_cast<input_t *>(data); + std::lock_guard lock{input->data_offer_dnd_mutex}; + input->data_offer_dnd = static_cast<data_offer_t *>(wl_data_offer_get_user_data(id)); data_offer_t *data_offer = input->data_offer_dnd; data_offer->in_use.store(true); - data_offer->dnd.x = wl_fixed_to_int(x); - data_offer->dnd.y = wl_fixed_to_int(y); + data_offer->dnd.xy[0] = x; + data_offer->dnd.xy[1] = y; wl_data_offer_set_actions(id, WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | @@ -675,14 +815,17 @@ static void data_device_enter(void *data, wl_data_offer_accept(id, serial, type.c_str()); } + input->focus_dnd = surface; dnd_events(input, GHOST_kEventDraggingEntered); } -static void data_device_leave(void *data, struct wl_data_device * /*wl_data_device*/) +static void data_device_handle_leave(void *data, struct wl_data_device * /*wl_data_device*/) { input_t *input = static_cast<input_t *>(data); + std::lock_guard lock{input->data_offer_dnd_mutex}; dnd_events(input, GHOST_kEventDraggingExited); + input->focus_dnd = nullptr; if (input->data_offer_dnd && !input->data_offer_dnd->in_use.load()) { wl_data_offer_destroy(input->data_offer_dnd->id); @@ -691,21 +834,26 @@ static void data_device_leave(void *data, struct wl_data_device * /*wl_data_devi } } -static void data_device_motion(void *data, - struct wl_data_device * /*wl_data_device*/, - uint32_t /*time*/, - wl_fixed_t x, - wl_fixed_t y) +static void data_device_handle_motion(void *data, + struct wl_data_device * /*wl_data_device*/, + uint32_t /*time*/, + wl_fixed_t x, + wl_fixed_t y) { input_t *input = static_cast<input_t *>(data); - input->data_offer_dnd->dnd.x = wl_fixed_to_int(x); - input->data_offer_dnd->dnd.y = wl_fixed_to_int(y); + std::lock_guard lock{input->data_offer_dnd_mutex}; + + input->data_offer_dnd->dnd.xy[0] = x; + input->data_offer_dnd->dnd.xy[1] = y; + dnd_events(input, GHOST_kEventDraggingUpdated); } -static void data_device_drop(void *data, struct wl_data_device * /*wl_data_device*/) +static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_data_device*/) { input_t *input = static_cast<input_t *>(data); + std::lock_guard lock{input->data_offer_dnd_mutex}; + data_offer_t *data_offer = input->data_offer_dnd; const std::string mime_receive = *std::find_first_of(mime_preference_order.begin(), @@ -713,13 +861,13 @@ static void data_device_drop(void *data, struct wl_data_device * /*wl_data_devic data_offer->types.begin(), data_offer->types.end()); - auto read_uris = [](input_t *const input, - data_offer_t *data_offer, - const std::string mime_receive) { - const int x = data_offer->dnd.x; - const int y = data_offer->dnd.y; + auto read_uris_fn = [](input_t *const input, + data_offer_t *data_offer, + wl_surface *surface, + const std::string mime_receive) { + const wl_fixed_t xy[2] = {data_offer->dnd.xy[0], data_offer->dnd.xy[1]}; - const std::string data = read_pipe(data_offer, mime_receive); + const std::string data = read_pipe(data_offer, mime_receive, nullptr); wl_data_offer_finish(data_offer->id); wl_data_offer_destroy(data_offer->id); @@ -733,6 +881,9 @@ static void data_device_drop(void *data, struct wl_data_device * /*wl_data_devic static constexpr const char *file_proto = "file://"; static constexpr const char *crlf = "\r\n"; + GHOST_WindowWayland *win = window_from_surface(surface); + GHOST_ASSERT(win != nullptr, "Unable to find window for drop event from surface"); + std::vector<std::string> uris; size_t pos = 0; @@ -756,14 +907,14 @@ static void data_device_drop(void *data, struct wl_data_device * /*wl_data_devic flist->strings[i] = static_cast<uint8_t *>(malloc((uris[i].size() + 1) * sizeof(uint8_t))); memcpy(flist->strings[i], uris[i].data(), uris[i].size() + 1); } - GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>( - wl_surface_get_user_data(input->focus_pointer)); + + const wl_fixed_t scale = win->scale(); system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(), GHOST_kEventDraggingDropDone, GHOST_kDragnDropTypeFilenames, win, - x, - y, + wl_fixed_to_int(scale * xy[0]), + wl_fixed_to_int(scale * xy[1]), flist)); } else if (mime_receive == mime_text_plain || mime_receive == mime_text_utf8) { @@ -773,15 +924,20 @@ static void data_device_drop(void *data, struct wl_data_device * /*wl_data_devic wl_display_roundtrip(system->display()); }; - std::thread read_thread(read_uris, input, data_offer, mime_receive); + /* Pass in `input->focus_dnd` instead of accessing it from `input` since the leave callback + * (#data_device_leave) will clear the value once this function starts. */ + std::thread read_thread(read_uris_fn, input, data_offer, input->focus_dnd, mime_receive); read_thread.detach(); } -static void data_device_selection(void *data, - struct wl_data_device * /*wl_data_device*/, - struct wl_data_offer *id) +static void data_device_handle_selection(void *data, + struct wl_data_device * /*wl_data_device*/, + struct wl_data_offer *id) { input_t *input = static_cast<input_t *>(data); + + std::lock_guard lock{input->data_offer_copy_paste_mutex}; + data_offer_t *data_offer = input->data_offer_copy_paste; /* Delete old data offer. */ @@ -799,65 +955,76 @@ static void data_device_selection(void *data, data_offer = static_cast<data_offer_t *>(wl_data_offer_get_user_data(id)); input->data_offer_copy_paste = data_offer; - std::string mime_receive; - for (const std::string type : {mime_text_utf8, mime_text_plain}) { - if (data_offer->types.count(type)) { - mime_receive = type; - break; + auto read_selection_fn = [](input_t *input) { + GHOST_SystemWayland *const system = input->system; + input->data_offer_copy_paste_mutex.lock(); + + data_offer_t *data_offer = input->data_offer_copy_paste; + std::string mime_receive; + for (const std::string type : {mime_text_utf8, mime_text_plain}) { + if (data_offer->types.count(type)) { + mime_receive = type; + break; + } } - } + const std::string data = read_pipe( + data_offer, mime_receive, &input->data_offer_copy_paste_mutex); - auto read_selection = [](GHOST_SystemWayland *const system, - data_offer_t *data_offer, - const std::string mime_receive) { - const std::string data = read_pipe(data_offer, mime_receive); - system->setSelection(data); + { + std::lock_guard lock{system_selection_mutex}; + system->setSelection(data); + } }; - std::thread read_thread(read_selection, input->system, data_offer, mime_receive); + std::thread read_thread(read_selection_fn, input); read_thread.detach(); } static const struct wl_data_device_listener data_device_listener = { - data_device_data_offer, - data_device_enter, - data_device_leave, - data_device_motion, - data_device_drop, - data_device_selection, + data_device_handle_data_offer, + data_device_handle_enter, + data_device_handle_leave, + data_device_handle_motion, + data_device_handle_drop, + data_device_handle_selection, }; /** \} */ /* -------------------------------------------------------------------- */ -/** \name Listener (Surface), #wl_surface_listener +/** \name Listener (Buffer), #wl_buffer_listener * \{ */ -static void cursor_buffer_release(void *data, struct wl_buffer *wl_buffer) +static void cursor_buffer_handle_release(void *data, struct wl_buffer *wl_buffer) { cursor_t *cursor = static_cast<cursor_t *>(data); wl_buffer_destroy(wl_buffer); - if (wl_buffer == cursor->buffer) { + if (wl_buffer == cursor->wl_buffer) { /* the mapped buffer was from a custom cursor */ - cursor->buffer = nullptr; + cursor->wl_buffer = nullptr; } } const struct wl_buffer_listener cursor_buffer_listener = { - cursor_buffer_release, + cursor_buffer_handle_release, }; -static GHOST_IWindow *get_window(struct wl_surface *surface) -{ - if (!surface) { - return nullptr; - } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Listener (Surface), #wl_surface_listener + * \{ */ - for (GHOST_IWindow *win : window_manager->getWindows()) { - if (surface == static_cast<const GHOST_WindowWayland *>(win)->surface()) { - return win; +static GHOST_WindowWayland *window_from_surface(struct wl_surface *surface) +{ + if (surface) { + for (GHOST_IWindow *iwin : window_manager->getWindows()) { + GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(iwin); + if (surface == win->surface()) { + return win; + } } } return nullptr; @@ -874,34 +1041,34 @@ static bool update_cursor_scale(cursor_t &cursor, wl_shm *shm) if (scale > 0 && cursor.scale != scale) { cursor.scale = scale; - wl_surface_set_buffer_scale(cursor.surface, scale); - wl_cursor_theme_destroy(cursor.theme); - cursor.theme = wl_cursor_theme_load(cursor.theme_name.c_str(), scale * cursor.size, shm); + wl_surface_set_buffer_scale(cursor.wl_surface, scale); + wl_cursor_theme_destroy(cursor.wl_theme); + cursor.wl_theme = wl_cursor_theme_load(cursor.theme_name.c_str(), scale * cursor.size, shm); return true; } return false; } -static void cursor_surface_enter(void *data, - struct wl_surface * /*wl_surface*/, - struct wl_output *output) +static void cursor_surface_handle_enter(void *data, + struct wl_surface * /*wl_surface*/, + struct wl_output *output) { input_t *input = static_cast<input_t *>(data); for (const output_t *reg_output : input->system->outputs()) { - if (reg_output->output == output) { + if (reg_output->wl_output == output) { input->cursor.outputs.insert(reg_output); } } update_cursor_scale(input->cursor, input->system->shm()); } -static void cursor_surface_leave(void *data, - struct wl_surface * /*wl_surface*/, - struct wl_output *output) +static void cursor_surface_handle_leave(void *data, + struct wl_surface * /*wl_surface*/, + struct wl_output *output) { input_t *input = static_cast<input_t *>(data); for (const output_t *reg_output : input->system->outputs()) { - if (reg_output->output == output) { + if (reg_output->wl_output == output) { input->cursor.outputs.erase(reg_output); } } @@ -909,8 +1076,8 @@ static void cursor_surface_leave(void *data, } struct wl_surface_listener cursor_surface_listener = { - cursor_surface_enter, - cursor_surface_leave, + cursor_surface_handle_enter, + cursor_surface_handle_leave, }; /** \} */ @@ -919,15 +1086,14 @@ struct wl_surface_listener cursor_surface_listener = { /** \name Listener (Pointer), #wl_pointer_listener * \{ */ -static void pointer_enter(void *data, - struct wl_pointer * /*wl_pointer*/, - uint32_t serial, - struct wl_surface *surface, - wl_fixed_t surface_x, - wl_fixed_t surface_y) +static void pointer_handle_enter(void *data, + struct wl_pointer * /*wl_pointer*/, + uint32_t serial, + struct wl_surface *surface, + wl_fixed_t surface_x, + wl_fixed_t surface_y) { - GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(surface)); - + GHOST_WindowWayland *win = window_from_surface(surface); if (!win) { return; } @@ -936,27 +1102,28 @@ static void pointer_enter(void *data, input_t *input = static_cast<input_t *>(data); input->pointer_serial = serial; - input->x = win->scale() * wl_fixed_to_int(surface_x); - input->y = win->scale() * wl_fixed_to_int(surface_y); + input->cursor_serial = serial; + input->xy[0] = surface_x; + input->xy[1] = surface_y; input->focus_pointer = surface; win->setCursorShape(win->getCursorShape()); + const wl_fixed_t scale = win->scale(); input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(), GHOST_kEventCursorMove, static_cast<GHOST_WindowWayland *>(win), - input->x, - input->y, + wl_fixed_to_int(scale * input->xy[0]), + wl_fixed_to_int(scale * input->xy[1]), GHOST_TABLET_DATA_NONE)); } -static void pointer_leave(void *data, - struct wl_pointer * /*wl_pointer*/, - uint32_t /*serial*/, - struct wl_surface *surface) +static void pointer_handle_leave(void *data, + struct wl_pointer * /*wl_pointer*/, + uint32_t /*serial*/, + struct wl_surface *surface) { - GHOST_IWindow *win = get_window(surface); - + GHOST_IWindow *win = window_from_surface(surface); if (!win) { return; } @@ -965,42 +1132,39 @@ static void pointer_leave(void *data, static_cast<GHOST_WindowWayland *>(win)->deactivate(); } -static void pointer_motion(void *data, - struct wl_pointer * /*wl_pointer*/, - uint32_t /*time*/, - wl_fixed_t surface_x, - wl_fixed_t surface_y) +static void pointer_handle_motion(void *data, + struct wl_pointer * /*wl_pointer*/, + uint32_t /*time*/, + wl_fixed_t surface_x, + wl_fixed_t surface_y) { input_t *input = static_cast<input_t *>(data); - - GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(input->focus_pointer)); - + GHOST_WindowWayland *win = window_from_surface(input->focus_pointer); if (!win) { return; } - input->x = win->scale() * wl_fixed_to_int(surface_x); - input->y = win->scale() * wl_fixed_to_int(surface_y); + input->xy[0] = surface_x; + input->xy[1] = surface_y; + const wl_fixed_t scale = win->scale(); input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(), GHOST_kEventCursorMove, win, - input->x, - input->y, + wl_fixed_to_int(scale * input->xy[0]), + wl_fixed_to_int(scale * input->xy[1]), GHOST_TABLET_DATA_NONE)); } -static void pointer_button(void *data, - struct wl_pointer * /*wl_pointer*/, - uint32_t serial, - uint32_t /*time*/, - uint32_t button, - uint32_t state) +static void pointer_handle_button(void *data, + struct wl_pointer * /*wl_pointer*/, + uint32_t serial, + uint32_t /*time*/, + uint32_t button, + uint32_t state) { input_t *input = static_cast<input_t *>(data); - - GHOST_IWindow *win = get_window(input->focus_pointer); - + GHOST_IWindow *win = window_from_surface(input->focus_pointer); if (!win) { return; } @@ -1040,22 +1204,20 @@ static void pointer_button(void *data, break; } - input->data_source->source_serial = serial; + input->data_source_serial = serial; input->buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED); input->system->pushEvent(new GHOST_EventButton( input->system->getMilliSeconds(), etype, win, ebutton, GHOST_TABLET_DATA_NONE)); } -static void pointer_axis(void *data, - struct wl_pointer * /*wl_pointer*/, - uint32_t /*time*/, - uint32_t axis, - wl_fixed_t value) +static void pointer_handle_axis(void *data, + struct wl_pointer * /*wl_pointer*/, + uint32_t /*time*/, + uint32_t axis, + wl_fixed_t value) { input_t *input = static_cast<input_t *>(data); - - GHOST_IWindow *win = get_window(input->focus_pointer); - + GHOST_IWindow *win = window_from_surface(input->focus_pointer); if (!win) { return; } @@ -1069,11 +1231,349 @@ static void pointer_axis(void *data, } static const struct wl_pointer_listener pointer_listener = { - pointer_enter, - pointer_leave, - pointer_motion, - pointer_button, - pointer_axis, + pointer_handle_enter, + pointer_handle_leave, + pointer_handle_motion, + pointer_handle_button, + pointer_handle_axis, +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Listener (Tablet Tool), #zwp_tablet_tool_v2_listener + * \{ */ + +static void tablet_tool_handle_type(void *data, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + uint32_t tool_type) +{ + tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); + + tool_input->data.Active = tablet_tool_map_type((enum zwp_tablet_tool_v2_type)tool_type); +} + +static void tablet_tool_handle_hardware_serial(void * /*data*/, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + uint32_t /*hardware_serial_hi*/, + uint32_t /*hardware_serial_lo*/) +{ +} + +static void tablet_tool_handle_hardware_id_wacom( + void * /*data*/, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + uint32_t /*hardware_id_hi*/, + uint32_t /*hardware_id_lo*/) +{ +} + +static void tablet_tool_handle_capability(void * /*data*/, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + uint32_t /*capability*/) +{ +} + +static void tablet_tool_handle_done(void * /*data*/, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/) +{ +} +static void tablet_tool_handle_removed(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) +{ + tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); + input_t *input = tool_input->input; + + if (tool_input->cursor_surface) { + wl_surface_destroy(tool_input->cursor_surface); + } + input->tablet_tools.erase(zwp_tablet_tool_v2); + + delete tool_input; +} +static void tablet_tool_handle_proximity_in(void *data, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + uint32_t serial, + struct zwp_tablet_v2 * /*tablet*/, + struct wl_surface *surface) +{ + tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); + input_t *input = tool_input->input; + + input->focus_tablet = surface; + input->tablet_serial = serial; + input->cursor_serial = serial; + + input->data_source_serial = serial; + + /* Update #GHOST_TabletData. */ + GHOST_TabletData &td = tool_input->data; + /* Reset, to avoid using stale tilt/pressure. */ + td.Xtilt = 0.0f; + td.Ytilt = 0.0f; + /* In case pressure isn't supported. */ + td.Pressure = 1.0f; + + GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); + if (!win) { + return; + } + win->activate(); + + win->setCursorShape(win->getCursorShape()); +} +static void tablet_tool_handle_proximity_out(void *data, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/) +{ + tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); + input_t *input = tool_input->input; + input->focus_tablet = nullptr; + + GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); + if (!win) { + return; + } + win->setCursorShape(win->getCursorShape()); +} + +static void tablet_tool_handle_down(void *data, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + uint32_t serial) +{ + tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); + input_t *input = tool_input->input; + GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); + if (!win) { + return; + } + + const GHOST_TEventType etype = GHOST_kEventButtonDown; + const GHOST_TButtonMask ebutton = GHOST_kButtonMaskLeft; + input->data_source_serial = serial; + input->buttons.set(ebutton, true); + input->system->pushEvent(new GHOST_EventButton( + input->system->getMilliSeconds(), etype, win, ebutton, tool_input->data)); +} + +static void tablet_tool_handle_up(void *data, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/) +{ + tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); + input_t *input = tool_input->input; + GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); + if (!win) { + return; + } + + const GHOST_TEventType etype = GHOST_kEventButtonUp; + const GHOST_TButtonMask ebutton = GHOST_kButtonMaskLeft; + input->buttons.set(ebutton, false); + input->system->pushEvent(new GHOST_EventButton( + input->system->getMilliSeconds(), etype, win, ebutton, tool_input->data)); +} + +static void tablet_tool_handle_motion(void *data, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + wl_fixed_t x, + wl_fixed_t y) +{ + tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); + input_t *input = tool_input->input; + GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); + if (!win) { + return; + } + + input->xy[0] = x; + input->xy[1] = y; + + const wl_fixed_t scale = win->scale(); + input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(), + GHOST_kEventCursorMove, + win, + wl_fixed_to_int(scale * input->xy[0]), + wl_fixed_to_int(scale * input->xy[1]), + tool_input->data)); +} + +static void tablet_tool_handle_pressure(void *data, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + uint32_t pressure) +{ + tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); + input_t *input = tool_input->input; + GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); + if (!win) { + return; + } + + GHOST_TabletData &td = tool_input->data; + td.Pressure = (float)pressure / 65535; +} +static void tablet_tool_handle_distance(void * /*data*/, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + uint32_t /*distance*/) +{ +} +static void tablet_tool_handle_tilt(void *data, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + wl_fixed_t tilt_x, + wl_fixed_t tilt_y) +{ + tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); + input_t *input = tool_input->input; + GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); + if (!win) { + return; + } + + GHOST_TabletData &td = tool_input->data; + /* Map degrees to `-1.0..1.0`. */ + td.Xtilt = wl_fixed_to_double(tilt_x) / 90.0f; + td.Ytilt = wl_fixed_to_double(tilt_y) / 90.0f; + td.Xtilt = td.Xtilt < -1.0f ? -1.0f : (td.Xtilt > 1.0f ? 1.0f : td.Xtilt); + td.Ytilt = td.Ytilt < -1.0f ? -1.0f : (td.Ytilt > 1.0f ? 1.0f : td.Ytilt); +} + +static void tablet_tool_handle_rotation(void * /*data*/, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + wl_fixed_t /*degrees*/) +{ + /* Pass. */ +} + +static void tablet_tool_handle_slider(void * /*data*/, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + int32_t /*position*/) +{ + /* Pass. */ +} +static void tablet_tool_handle_wheel(void *data, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + wl_fixed_t /*degrees*/, + int32_t clicks) +{ + if (clicks == 0) { + return; + } + + tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); + input_t *input = tool_input->input; + GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); + if (!win) { + return; + } + + input->system->pushEvent(new GHOST_EventWheel(input->system->getMilliSeconds(), win, clicks)); +} +static void tablet_tool_handle_button(void *data, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + uint32_t serial, + uint32_t button, + uint32_t state) +{ + tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); + input_t *input = tool_input->input; + GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); + if (!win) { + return; + } + + GHOST_TEventType etype = GHOST_kEventUnknown; + switch (state) { + case WL_POINTER_BUTTON_STATE_RELEASED: + etype = GHOST_kEventButtonUp; + break; + case WL_POINTER_BUTTON_STATE_PRESSED: + etype = GHOST_kEventButtonDown; + break; + } + + GHOST_TButtonMask ebutton = GHOST_kButtonMaskLeft; + switch (button) { + case BTN_STYLUS: + ebutton = GHOST_kButtonMaskRight; + break; + case BTN_STYLUS2: + ebutton = GHOST_kButtonMaskMiddle; + break; + case BTN_STYLUS3: + ebutton = GHOST_kButtonMaskButton4; + break; + } + + input->data_source_serial = serial; + input->buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED); + input->system->pushEvent(new GHOST_EventButton( + input->system->getMilliSeconds(), etype, win, ebutton, tool_input->data)); +} +static void tablet_tool_handle_frame(void * /*data*/, + struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, + uint32_t /*time*/) +{ +} + +static const struct zwp_tablet_tool_v2_listener tablet_tool_listner = { + tablet_tool_handle_type, + tablet_tool_handle_hardware_serial, + tablet_tool_handle_hardware_id_wacom, + tablet_tool_handle_capability, + tablet_tool_handle_done, + tablet_tool_handle_removed, + tablet_tool_handle_proximity_in, + tablet_tool_handle_proximity_out, + tablet_tool_handle_down, + tablet_tool_handle_up, + tablet_tool_handle_motion, + tablet_tool_handle_pressure, + tablet_tool_handle_distance, + tablet_tool_handle_tilt, + tablet_tool_handle_rotation, + tablet_tool_handle_slider, + tablet_tool_handle_wheel, + tablet_tool_handle_button, + tablet_tool_handle_frame, +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Listener (Table Seat), #zwp_tablet_seat_v2_listener + * \{ */ + +static void tablet_seat_handle_tablet_added(void * /*data*/, + struct zwp_tablet_seat_v2 * /*zwp_tablet_seat_v2*/, + struct zwp_tablet_v2 * /*id*/) +{ + /* Pass. */ +} + +static void tablet_seat_handle_tool_added(void *data, + struct zwp_tablet_seat_v2 * /*zwp_tablet_seat_v2*/, + struct zwp_tablet_tool_v2 *id) +{ + input_t *input = static_cast<input_t *>(data); + tablet_tool_input_t *tool_input = new tablet_tool_input_t(); + tool_input->input = input; + + /* Every tool has it's own cursor surface. */ + tool_input->cursor_surface = wl_compositor_create_surface(input->system->compositor()); + wl_surface_add_listener(tool_input->cursor_surface, &cursor_surface_listener, (void *)input); + + zwp_tablet_tool_v2_add_listener(id, &tablet_tool_listner, tool_input); + + input->tablet_tools.insert(id); +} + +static void tablet_seat_handle_pad_added(void * /*data*/, + struct zwp_tablet_seat_v2 * /*zwp_tablet_seat_v2*/, + struct zwp_tablet_pad_v2 * /*id*/) +{ + /* Pass. */ +} + +const struct zwp_tablet_seat_v2_listener tablet_seat_listener = { + tablet_seat_handle_tablet_added, + tablet_seat_handle_tool_added, + tablet_seat_handle_pad_added, }; /** \} */ @@ -1082,7 +1582,7 @@ static const struct wl_pointer_listener pointer_listener = { /** \name Listener (Keyboard), #wl_keyboard_listener * \{ */ -static void keyboard_keymap( +static void keyboard_handle_keymap( void *data, struct wl_keyboard * /*wl_keyboard*/, uint32_t format, int32_t fd, uint32_t size) { input_t *input = static_cast<input_t *>(data); @@ -1107,8 +1607,13 @@ static void keyboard_keymap( return; } - input->xkb_state = xkb_state_new(keymap); - + struct xkb_state *xkb_state_next = xkb_state_new(keymap); + if (xkb_state_next) { + if (input->xkb_state) { + xkb_state_unref(input->xkb_state); + } + input->xkb_state = xkb_state_next; + } xkb_keymap_unref(keymap); } @@ -1118,11 +1623,11 @@ static void keyboard_keymap( * Notification that this seat's keyboard focus is on a certain * surface. */ -static void keyboard_enter(void *data, - struct wl_keyboard * /*wl_keyboard*/, - uint32_t /*serial*/, - struct wl_surface *surface, - struct wl_array * /*keys*/) +static void keyboard_handle_enter(void *data, + struct wl_keyboard * /*wl_keyboard*/, + uint32_t /*serial*/, + struct wl_surface *surface, + struct wl_array * /*keys*/) { if (surface != nullptr) { static_cast<input_t *>(data)->focus_keyboard = surface; @@ -1135,10 +1640,10 @@ static void keyboard_enter(void *data, * Notification that this seat's keyboard focus is no longer on a * certain surface. */ -static void keyboard_leave(void *data, - struct wl_keyboard * /*wl_keyboard*/, - uint32_t /*serial*/, - struct wl_surface *surface) +static void keyboard_handle_leave(void *data, + struct wl_keyboard * /*wl_keyboard*/, + uint32_t /*serial*/, + struct wl_surface *surface) { if (surface != nullptr) { static_cast<input_t *>(data)->focus_keyboard = nullptr; @@ -1171,12 +1676,12 @@ static xkb_keysym_t xkb_state_key_get_one_sym_without_modifiers(struct xkb_state return sym; } -static void keyboard_key(void *data, - struct wl_keyboard * /*wl_keyboard*/, - uint32_t serial, - uint32_t /*time*/, - uint32_t key, - uint32_t state) +static void keyboard_handle_key(void *data, + struct wl_keyboard * /*wl_keyboard*/, + uint32_t serial, + uint32_t /*time*/, + uint32_t key, + uint32_t state) { input_t *input = static_cast<input_t *>(data); @@ -1195,7 +1700,6 @@ static void keyboard_key(void *data, if (sym == XKB_KEY_NoSymbol) { return; } - const GHOST_TKey gkey = xkb_map_gkey(sym); /* Delete previous timer. */ if (xkb_keymap_key_repeats(xkb_state_get_keymap(input->xkb_state), key + 8) && @@ -1205,7 +1709,9 @@ static void keyboard_key(void *data, input->key_repeat.timer = nullptr; } - GHOST_TEventKeyData key_data; + GHOST_TEventKeyData key_data = { + .key = xkb_map_gkey(sym), + }; if (etype == GHOST_kEventKeyDown) { xkb_state_key_get_utf8( @@ -1215,12 +1721,12 @@ static void keyboard_key(void *data, key_data.utf8_buf[0] = '\0'; } - input->data_source->source_serial = serial; + input->data_source_serial = serial; GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>( wl_surface_get_user_data(input->focus_keyboard)); input->system->pushEvent(new GHOST_EventKey( - input->system->getMilliSeconds(), etype, win, gkey, '\0', key_data.utf8_buf, false)); + input->system->getMilliSeconds(), etype, win, key_data.key, '\0', key_data.utf8_buf, false)); /* Start timer for repeating key, if applicable. */ if (input->key_repeat.rate > 0 && @@ -1230,33 +1736,32 @@ static void keyboard_key(void *data, key_repeat_payload_t *payload = new key_repeat_payload_t({ .system = input->system, .window = win, - .key = gkey, .key_data = key_data, }); - auto cb = [](GHOST_ITimerTask *task, uint64_t /*time*/) { + auto key_repeat_fn = [](GHOST_ITimerTask *task, uint64_t /*time*/) { struct key_repeat_payload_t *payload = static_cast<key_repeat_payload_t *>( task->getUserData()); payload->system->pushEvent(new GHOST_EventKey(payload->system->getMilliSeconds(), GHOST_kEventKeyDown, payload->window, - payload->key, + payload->key_data.key, '\0', payload->key_data.utf8_buf, true)); }; input->key_repeat.timer = input->system->installTimer( - input->key_repeat.delay, 1000 / input->key_repeat.rate, cb, payload); + input->key_repeat.delay, 1000 / input->key_repeat.rate, key_repeat_fn, payload); } } -static void keyboard_modifiers(void *data, - struct wl_keyboard * /*wl_keyboard*/, - uint32_t /*serial*/, - uint32_t mods_depressed, - uint32_t mods_latched, - uint32_t mods_locked, - uint32_t group) +static void keyboard_handle_modifiers(void *data, + struct wl_keyboard * /*wl_keyboard*/, + uint32_t /*serial*/, + uint32_t mods_depressed, + uint32_t mods_latched, + uint32_t mods_locked, + uint32_t group) { xkb_state_update_mask(static_cast<input_t *>(data)->xkb_state, mods_depressed, @@ -1267,10 +1772,10 @@ static void keyboard_modifiers(void *data, group); } -static void keyboard_repeat_info(void *data, - struct wl_keyboard * /*wl_keyboard*/, - int32_t rate, - int32_t delay) +static void keyboard_repeat_handle_info(void *data, + struct wl_keyboard * /*wl_keyboard*/, + int32_t rate, + int32_t delay) { input_t *input = static_cast<input_t *>(data); @@ -1279,12 +1784,12 @@ static void keyboard_repeat_info(void *data, } static const struct wl_keyboard_listener keyboard_listener = { - keyboard_keymap, - keyboard_enter, - keyboard_leave, - keyboard_key, - keyboard_modifiers, - keyboard_repeat_info, + keyboard_handle_keymap, + keyboard_handle_enter, + keyboard_handle_leave, + keyboard_handle_key, + keyboard_handle_modifiers, + keyboard_repeat_handle_info, }; /** \} */ @@ -1293,40 +1798,111 @@ static const struct wl_keyboard_listener keyboard_listener = { /** \name Listener (Seat), #wl_seat_listener * \{ */ -static void seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) +static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) { input_t *input = static_cast<input_t *>(data); - input->pointer = nullptr; - input->keyboard = nullptr; + input->wl_pointer = nullptr; + input->wl_keyboard = nullptr; if (capabilities & WL_SEAT_CAPABILITY_POINTER) { - input->pointer = wl_seat_get_pointer(wl_seat); - input->cursor.surface = wl_compositor_create_surface(input->system->compositor()); + input->wl_pointer = wl_seat_get_pointer(wl_seat); + input->cursor.wl_surface = wl_compositor_create_surface(input->system->compositor()); input->cursor.visible = true; - input->cursor.buffer = nullptr; + input->cursor.wl_buffer = nullptr; input->cursor.file_buffer = new buffer_t; if (!get_cursor_settings(input->cursor.theme_name, input->cursor.size)) { input->cursor.theme_name = std::string(); input->cursor.size = default_cursor_size; } - wl_pointer_add_listener(input->pointer, &pointer_listener, data); - wl_surface_add_listener(input->cursor.surface, &cursor_surface_listener, data); + wl_pointer_add_listener(input->wl_pointer, &pointer_listener, data); + wl_surface_add_listener(input->cursor.wl_surface, &cursor_surface_listener, data); } if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { - input->keyboard = wl_seat_get_keyboard(wl_seat); - wl_keyboard_add_listener(input->keyboard, &keyboard_listener, data); + input->wl_keyboard = wl_seat_get_keyboard(wl_seat); + wl_keyboard_add_listener(input->wl_keyboard, &keyboard_listener, data); } } -static void seat_name(void *data, struct wl_seat * /*wl_seat*/, const char *name) +static void seat_handle_name(void *data, struct wl_seat * /*wl_seat*/, const char *name) { static_cast<input_t *>(data)->name = std::string(name); } static const struct wl_seat_listener seat_listener = { - seat_capabilities, - seat_name, + seat_handle_capabilities, + seat_handle_name, +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Listener (XDG Output), #zxdg_output_v1_listener + * \{ */ + +static void xdg_output_handle_logical_position(void *data, + struct zxdg_output_v1 * /*xdg_output*/, + int32_t x, + int32_t y) +{ + output_t *output = static_cast<output_t *>(data); + output->position_logical[0] = x; + output->position_logical[1] = y; + output->has_position_logical = true; +} + +static void xdg_output_handle_logical_size(void *data, + struct zxdg_output_v1 * /*xdg_output*/, + int32_t width, + int32_t height) +{ + output_t *output = static_cast<output_t *>(data); + + if (output->size_logical[0] != 0 && output->size_logical[1] != 0) { + /* Original comment from SDL. */ + /* FIXME(@flibit): GNOME has a bug where the logical size does not account for + * scale, resulting in bogus viewport sizes. + * + * Until this is fixed, validate that _some_ kind of scaling is being + * done (we can't match exactly because fractional scaling can't be + * detected otherwise), then override if necessary. */ + if ((output->size_logical[0] == width) && (output->scale_fractional == wl_fixed_from_int(1))) { + GHOST_PRINT("xdg_output scale did not match, overriding with wl_output scale"); + return; + } + } + + output->size_logical[0] = width; + output->size_logical[1] = height; + output->has_size_logical = true; +} + +static void xdg_output_handle_done(void * /*data*/, struct zxdg_output_v1 * /*xdg_output*/) +{ + /* NOTE: `xdg-output.done` events are deprecated and only apply below version 3 of the protocol. + * `wl-output.done` event will be emitted in version 3 or higher. */ +} + +static void xdg_output_handle_name(void * /*data*/, + struct zxdg_output_v1 * /*xdg_output*/, + const char * /*name*/) +{ + /* Pass. */ +} + +static void xdg_output_handle_description(void * /*data*/, + struct zxdg_output_v1 * /*xdg_output*/, + const char * /*description*/) +{ + /* Pass. */ +} + +static const struct zxdg_output_v1_listener xdg_output_listener = { + xdg_output_handle_logical_position, + xdg_output_handle_logical_size, + xdg_output_handle_done, + xdg_output_handle_name, + xdg_output_handle_description, }; /** \} */ @@ -1335,35 +1911,45 @@ static const struct wl_seat_listener seat_listener = { /** \name Listener (Output), #wl_output_listener * \{ */ -static void output_geometry(void *data, - struct wl_output * /*wl_output*/, - int32_t /*x*/, - int32_t /*y*/, - int32_t physical_width, - int32_t physical_height, - int32_t /*subpixel*/, - const char *make, - const char *model, - int32_t transform) +static void output_handle_geometry(void *data, + struct wl_output * /*wl_output*/, + int32_t /*x*/, + int32_t /*y*/, + int32_t physical_width, + int32_t physical_height, + int32_t /*subpixel*/, + const char *make, + const char *model, + int32_t transform) { output_t *output = static_cast<output_t *>(data); output->transform = transform; output->make = std::string(make); output->model = std::string(model); - output->width_mm = physical_width; - output->height_mm = physical_height; + output->size_mm[0] = physical_width; + output->size_mm[1] = physical_height; } -static void output_mode(void *data, - struct wl_output * /*wl_output*/, - uint32_t /*flags*/, - int32_t width, - int32_t height, - int32_t /*refresh*/) +static void output_handle_mode(void *data, + struct wl_output * /*wl_output*/, + uint32_t flags, + int32_t width, + int32_t height, + int32_t /*refresh*/) { output_t *output = static_cast<output_t *>(data); - output->width_pxl = width; - output->height_pxl = height; + + if (flags & WL_OUTPUT_MODE_CURRENT) { + output->size_native[0] = width; + output->size_native[1] = height; + + /* Don't rotate this yet, `wl-output` coordinates are transformed in + * handle_done and `xdg-output` coordinates are pre-transformed. */ + if (!output->has_size_logical) { + output->size_logical[0] = width; + output->size_logical[1] = height; + } + } } /** @@ -1374,20 +1960,43 @@ static void output_mode(void *data, * changes done after that. This allows changes to the output * properties to be seen as atomic, even if they happen via multiple events. */ -static void output_done(void * /*data*/, struct wl_output * /*wl_output*/) +static void output_handle_done(void *data, struct wl_output * /*wl_output*/) { + output_t *output = static_cast<output_t *>(data); + int32_t size_native[2]; + if (output->transform & WL_OUTPUT_TRANSFORM_90) { + size_native[0] = output->size_native[1]; + size_native[1] = output->size_native[1]; + } + else { + size_native[0] = output->size_native[0]; + size_native[1] = output->size_native[1]; + } + + /* If `xdg-output` is present, calculate the true scale of the desktop */ + if (output->has_size_logical) { + + /* NOTE: it's not necessary to divide these values by their greatest-common-denominator + * as even a 64k screen resolution doesn't approach overflowing an `int32_t`. */ + + GHOST_ASSERT(size_native[0] && output->size_logical[0], + "Screen size values were not set when they were expected to be."); + + output->scale_fractional = wl_fixed_from_int(size_native[0]) / output->size_logical[0]; + output->has_scale_fractional = true; + } } -static void output_scale(void *data, struct wl_output * /*wl_output*/, int32_t factor) +static void output_handle_scale(void *data, struct wl_output * /*wl_output*/, int32_t factor) { static_cast<output_t *>(data)->scale = factor; } static const struct wl_output_listener output_listener = { - output_geometry, - output_mode, - output_done, - output_scale, + output_handle_geometry, + output_handle_mode, + output_handle_done, + output_handle_scale, }; /** \} */ @@ -1396,13 +2005,13 @@ static const struct wl_output_listener output_listener = { /** \name Listener (XDG WM Base), #xdg_wm_base_listener * \{ */ -static void shell_ping(void * /*data*/, struct xdg_wm_base *xdg_wm_base, uint32_t serial) +static void shell_handle_ping(void * /*data*/, struct xdg_wm_base *xdg_wm_base, uint32_t serial) { xdg_wm_base_pong(xdg_wm_base, serial); } static const struct xdg_wm_base_listener shell_listener = { - shell_ping, + shell_handle_ping, }; /** \} */ @@ -1411,11 +2020,11 @@ static const struct xdg_wm_base_listener shell_listener = { /** \name Listener (Registry), #wl_registry_listener * \{ */ -static void global_add(void *data, - struct wl_registry *wl_registry, - uint32_t name, - const char *interface, - uint32_t /*version*/) +static void global_handle_add(void *data, + struct wl_registry *wl_registry, + uint32_t name, + const char *interface, + uint32_t /*version*/) { struct display_t *display = static_cast<struct display_t *>(data); if (!strcmp(interface, wl_compositor_interface.name)) { @@ -1431,31 +2040,37 @@ static void global_add(void *data, display->xdg_decoration_manager = static_cast<zxdg_decoration_manager_v1 *>( wl_registry_bind(wl_registry, name, &zxdg_decoration_manager_v1_interface, 1)); } + else if (!strcmp(interface, zxdg_output_manager_v1_interface.name)) { + display->xdg_output_manager = static_cast<zxdg_output_manager_v1 *>( + wl_registry_bind(wl_registry, name, &zxdg_output_manager_v1_interface, 3)); + for (output_t *output : display->outputs) { + output->xdg_output = zxdg_output_manager_v1_get_xdg_output(display->xdg_output_manager, + output->wl_output); + zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output); + } + } else if (!strcmp(interface, wl_output_interface.name)) { output_t *output = new output_t; - output->scale = 1; - output->output = static_cast<wl_output *>( + output->wl_output = static_cast<wl_output *>( wl_registry_bind(wl_registry, name, &wl_output_interface, 2)); display->outputs.push_back(output); - wl_output_add_listener(output->output, &output_listener, output); + wl_output_add_listener(output->wl_output, &output_listener, output); + + if (display->xdg_output_manager) { + output->xdg_output = zxdg_output_manager_v1_get_xdg_output(display->xdg_output_manager, + output->wl_output); + zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output); + } } else if (!strcmp(interface, wl_seat_interface.name)) { input_t *input = new input_t; input->system = display->system; input->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - input->xkb_state = nullptr; - input->data_offer_dnd = nullptr; - input->data_offer_copy_paste = nullptr; input->data_source = new data_source_t; - input->data_source->data_source = nullptr; - input->data_source->buffer_out = nullptr; - input->relative_pointer = nullptr; - input->locked_pointer = nullptr; - input->confined_pointer = nullptr; - input->seat = static_cast<wl_seat *>( + input->wl_seat = static_cast<wl_seat *>( wl_registry_bind(wl_registry, name, &wl_seat_interface, 4)); display->inputs.push_back(input); - wl_seat_add_listener(input->seat, &seat_listener, input); + wl_seat_add_listener(input->wl_seat, &seat_listener, input); } else if (!strcmp(interface, wl_shm_interface.name)) { display->shm = static_cast<wl_shm *>( @@ -1463,7 +2078,11 @@ static void global_add(void *data, } else if (!strcmp(interface, wl_data_device_manager_interface.name)) { display->data_device_manager = static_cast<wl_data_device_manager *>( - wl_registry_bind(wl_registry, name, &wl_data_device_manager_interface, 1)); + wl_registry_bind(wl_registry, name, &wl_data_device_manager_interface, 3)); + } + else if (!strcmp(interface, zwp_tablet_manager_v2_interface.name)) { + display->tablet_manager = static_cast<zwp_tablet_manager_v2 *>( + wl_registry_bind(wl_registry, name, &zwp_tablet_manager_v2_interface, 1)); } else if (!strcmp(interface, zwp_relative_pointer_manager_v1_interface.name)) { display->relative_pointer_manager = static_cast<zwp_relative_pointer_manager_v1 *>( @@ -1484,13 +2103,15 @@ static void global_add(void *data, * name is no longer available. If the client bound to the global * using the bind request, the client should now destroy that object. */ -static void global_remove(void * /*data*/, struct wl_registry * /*wl_registry*/, uint32_t /*name*/) +static void global_handle_remove(void * /*data*/, + struct wl_registry * /*wl_registry*/, + uint32_t /*name*/) { } static const struct wl_registry_listener registry_listener = { - global_add, - global_remove, + global_handle_add, + global_handle_remove, }; /** \} */ @@ -1503,6 +2124,8 @@ static const struct wl_registry_listener registry_listener = { GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), d(new display_t) { + wl_log_set_handler_client(ghost_wayland_log_handler); + d->system = this; /* Connect to the Wayland server. */ d->display = wl_display_connect(nullptr); @@ -1529,10 +2152,18 @@ GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), d(new display_t) if (d->data_device_manager) { for (input_t *input : d->inputs) { input->data_device = wl_data_device_manager_get_data_device(d->data_device_manager, - input->seat); + input->wl_seat); wl_data_device_add_listener(input->data_device, &data_device_listener, input); } } + + if (d->tablet_manager) { + for (input_t *input : d->inputs) { + input->tablet_seat = zwp_tablet_manager_v2_get_tablet_seat(d->tablet_manager, + input->wl_seat); + zwp_tablet_seat_v2_add_listener(input->tablet_seat, &tablet_seat_listener, input); + } + } } GHOST_SystemWayland::~GHOST_SystemWayland() @@ -1561,42 +2192,47 @@ int GHOST_SystemWayland::setConsoleWindowState(GHOST_TConsoleWindowState /*actio GHOST_TSuccess GHOST_SystemWayland::getModifierKeys(GHOST_ModifierKeys &keys) const { - if (!d->inputs.empty()) { - static const xkb_state_component mods_all = xkb_state_component( - XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED | XKB_STATE_MODS_LOCKED | - XKB_STATE_MODS_EFFECTIVE); - - keys.set(GHOST_kModifierKeyLeftShift, - xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_SHIFT, mods_all) == - 1); - keys.set(GHOST_kModifierKeyRightShift, - xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_SHIFT, mods_all) == - 1); - keys.set(GHOST_kModifierKeyLeftAlt, - xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "LAlt", mods_all) == 1); - keys.set(GHOST_kModifierKeyRightAlt, - xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "RAlt", mods_all) == 1); - keys.set(GHOST_kModifierKeyLeftControl, - xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "LControl", mods_all) == 1); - keys.set(GHOST_kModifierKeyRightControl, - xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "RControl", mods_all) == 1); - keys.set(GHOST_kModifierKeyOS, - xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "Super", mods_all) == 1); - keys.set(GHOST_kModifierKeyNumMasks, - xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "NumLock", mods_all) == 1); - - return GHOST_kSuccess; + if (d->inputs.empty()) { + return GHOST_kFailure; } - return GHOST_kFailure; + + static const xkb_state_component mods_all = xkb_state_component( + XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED | XKB_STATE_MODS_LOCKED | + XKB_STATE_MODS_EFFECTIVE); + + bool val; + + /* NOTE: XKB doesn't seem to differentiate between left/right modifiers. */ + + val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_SHIFT, mods_all) == 1; + keys.set(GHOST_kModifierKeyLeftShift, val); + keys.set(GHOST_kModifierKeyRightShift, val); + + val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_ALT, mods_all) == 1; + keys.set(GHOST_kModifierKeyLeftAlt, val); + keys.set(GHOST_kModifierKeyRightAlt, val); + + val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_CTRL, mods_all) == 1; + keys.set(GHOST_kModifierKeyLeftControl, val); + keys.set(GHOST_kModifierKeyRightControl, val); + + val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_LOGO, mods_all) == 1; + keys.set(GHOST_kModifierKeyOS, val); + + val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_NUM, mods_all) == 1; + keys.set(GHOST_kModifierKeyNumMasks, val); + + return GHOST_kSuccess; } GHOST_TSuccess GHOST_SystemWayland::getButtons(GHOST_Buttons &buttons) const { - if (!d->inputs.empty()) { - buttons = d->inputs[0]->buttons; - return GHOST_kSuccess; + if (d->inputs.empty()) { + return GHOST_kFailure; } - return GHOST_kFailure; + + buttons = d->inputs[0]->buttons; + return GHOST_kSuccess; } char *GHOST_SystemWayland::getClipboard(bool /*selection*/) const @@ -1612,7 +2248,11 @@ void GHOST_SystemWayland::putClipboard(const char *buffer, bool /*selection*/) c return; } - data_source_t *data_source = d->inputs[0]->data_source; + input_t *input = d->inputs[0]; + + std::lock_guard lock{input->data_source_mutex}; + + data_source_t *data_source = input->data_source; /* Copy buffer. */ free(data_source->buffer_out); @@ -1622,16 +2262,15 @@ void GHOST_SystemWayland::putClipboard(const char *buffer, bool /*selection*/) c data_source->data_source = wl_data_device_manager_create_data_source(d->data_device_manager); - wl_data_source_add_listener( - data_source->data_source, &data_source_listener, data_source->buffer_out); + wl_data_source_add_listener(data_source->data_source, &data_source_listener, input); for (const std::string &type : mime_send) { wl_data_source_offer(data_source->data_source, type.c_str()); } - if (!d->inputs.empty() && d->inputs[0]->data_device) { + if (input->data_device) { wl_data_device_set_selection( - d->inputs[0]->data_device, data_source->data_source, data_source->source_serial); + input->data_device, data_source->data_source, input->data_source_serial); } } @@ -1642,12 +2281,30 @@ uint8_t GHOST_SystemWayland::getNumDisplays() const GHOST_TSuccess GHOST_SystemWayland::getCursorPosition(int32_t &x, int32_t &y) const { - if (d->inputs.empty() || (d->inputs[0]->focus_pointer == nullptr)) { + if (d->inputs.empty()) { return GHOST_kFailure; } - x = d->inputs[0]->x; - y = d->inputs[0]->y; + input_t *input = d->inputs[0]; + struct wl_surface *surface = nullptr; + if (input->pointer_serial == input->cursor_serial) { + surface = input->focus_pointer; + } + else if (input->tablet_serial == input->cursor_serial) { + surface = input->focus_tablet; + } + if (!surface) { + return GHOST_kFailure; + } + + GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(wl_surface_get_user_data(surface)); + if (!win) { + return GHOST_kFailure; + } + + const wl_fixed_t scale = win->scale(); + x = wl_fixed_to_int(scale * input->xy[0]); + y = wl_fixed_to_int(scale * input->xy[1]); return GHOST_kSuccess; } @@ -1660,8 +2317,8 @@ void GHOST_SystemWayland::getMainDisplayDimensions(uint32_t &width, uint32_t &he { if (getNumDisplays() > 0) { /* We assume first output as main. */ - width = uint32_t(d->outputs[0]->width_pxl) / d->outputs[0]->scale; - height = uint32_t(d->outputs[0]->height_pxl) / d->outputs[0]->scale; + width = uint32_t(d->outputs[0]->size_native[0]) / d->outputs[0]->scale; + height = uint32_t(d->outputs[0]->size_native[1]) / d->outputs[0]->scale; } } @@ -1782,12 +2439,12 @@ wl_compositor *GHOST_SystemWayland::compositor() return d->compositor; } -xdg_wm_base *GHOST_SystemWayland::shell() +xdg_wm_base *GHOST_SystemWayland::xdg_shell() { return d->xdg_shell; } -zxdg_decoration_manager_v1 *GHOST_SystemWayland::decoration_manager() +zxdg_decoration_manager_v1 *GHOST_SystemWayland::xdg_decoration_manager() { return d->xdg_decoration_manager; } @@ -1813,16 +2470,45 @@ static void set_cursor_buffer(input_t *input, wl_buffer *buffer) c->visible = (buffer != nullptr); - wl_surface_attach(c->surface, buffer, 0, 0); + const int32_t image_size_x = int32_t(c->wl_image.width); + const int32_t image_size_y = int32_t(c->wl_image.height); + + const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / c->scale; + const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / c->scale; - wl_surface_damage(c->surface, 0, 0, int32_t(c->image.width), int32_t(c->image.height)); - wl_pointer_set_cursor(input->pointer, + if (buffer) { + wl_surface_set_buffer_scale(c->wl_surface, c->scale); + wl_surface_attach(c->wl_surface, buffer, 0, 0); + wl_surface_damage(c->wl_surface, 0, 0, image_size_x, image_size_y); + wl_surface_commit(c->wl_surface); + } + + wl_pointer_set_cursor(input->wl_pointer, input->pointer_serial, - c->visible ? c->surface : nullptr, - int32_t(c->image.hotspot_x) / c->scale, - int32_t(c->image.hotspot_y) / c->scale); + c->visible ? c->wl_surface : nullptr, + hotspot_x, + hotspot_y); + + /* Set the cursor for all tablet tools as well. */ + for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : input->tablet_tools) { + tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>( + zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2)); + + if (buffer) { + /* FIXME: for some reason cursor scale is applied twice (when the scale isn't 1x), + * this happens both in gnome-shell & KDE. Setting the surface scale here doesn't help. */ + wl_surface_set_buffer_scale(tool_input->cursor_surface, c->scale); + wl_surface_attach(tool_input->cursor_surface, buffer, 0, 0); + wl_surface_damage(tool_input->cursor_surface, 0, 0, image_size_x, image_size_y); + wl_surface_commit(tool_input->cursor_surface); + } - wl_surface_commit(c->surface); + zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2, + input->tablet_serial, + c->visible ? tool_input->cursor_surface : nullptr, + hotspot_x, + hotspot_y); + } } GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape) @@ -1836,12 +2522,13 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape) input_t *input = d->inputs[0]; cursor_t *c = &input->cursor; - if (!c->theme) { + if (!c->wl_theme) { /* The cursor surface hasn't entered an output yet. Initialize theme with scale 1. */ - c->theme = wl_cursor_theme_load(c->theme_name.c_str(), c->size, d->inputs[0]->system->shm()); + c->wl_theme = wl_cursor_theme_load( + c->theme_name.c_str(), c->size, d->inputs[0]->system->shm()); } - wl_cursor *cursor = wl_cursor_theme_get_cursor(c->theme, cursor_name.c_str()); + wl_cursor *cursor = wl_cursor_theme_get_cursor(c->wl_theme, cursor_name.c_str()); if (!cursor) { GHOST_PRINT("cursor '" << cursor_name << "' does not exist" << std::endl); @@ -1854,8 +2541,8 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape) return GHOST_kFailure; } - c->buffer = buffer; - c->image = *image; + c->wl_buffer = buffer; + c->wl_image = *image; set_cursor_buffer(input, buffer); @@ -1961,11 +2648,11 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap, } } - cursor->buffer = buffer; - cursor->image.width = uint32_t(sizex); - cursor->image.height = uint32_t(sizey); - cursor->image.hotspot_x = uint32_t(hotX); - cursor->image.hotspot_y = uint32_t(hotY); + cursor->wl_buffer = buffer; + cursor->wl_image.width = uint32_t(sizex); + cursor->wl_image.height = uint32_t(sizey); + cursor->wl_image.hotspot_x = uint32_t(hotX); + cursor->wl_image.hotspot_y = uint32_t(hotY); set_cursor_buffer(d->inputs[0], buffer); @@ -1983,7 +2670,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorVisibility(bool visible) cursor_t *cursor = &input->cursor; if (visible) { if (!cursor->visible) { - set_cursor_buffer(input, cursor->buffer); + set_cursor_buffer(input, cursor->wl_buffer); } } else { @@ -1997,12 +2684,19 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorVisibility(bool visible) bool GHOST_SystemWayland::supportsCursorWarp() { + /* WAYLAND doesn't support setting the cursor position directly, + * this is an intentional choice, forcing us to use a software cursor in this case. */ + return false; +} + +bool GHOST_SystemWayland::supportsWindowPosition() +{ + /* WAYLAND doesn't support accessing the window position. */ return false; } GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mode, const GHOST_TGrabCursorMode mode_current, - wl_surface *surface) { /* ignore, if the required protocols are not supported */ @@ -2052,6 +2746,49 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mo input->relative_pointer = nullptr; } if (input->locked_pointer) { + /* Request location to restore to. */ + if (mode_current == GHOST_kGrabWrap) { + /* The chance this fails is _very_ low. */ + GHOST_WindowWayland *win = window_from_surface(surface); + if (!win) { + GHOST_PRINT("could not find window from surface when un-grabbing!" << std::endl); + } + else { + GHOST_Rect bounds; + int32_t xy_new[2] = {input->xy[0], input->xy[1]}; + + /* Fallback to window bounds. */ + if (win->getCursorGrabBounds(bounds) == GHOST_kFailure) { + win->getClientBounds(bounds); + } + + const int scale = win->scale(); + + bounds.m_l = wl_fixed_from_int(bounds.m_l) / scale; + bounds.m_t = wl_fixed_from_int(bounds.m_t) / scale; + bounds.m_r = wl_fixed_from_int(bounds.m_r) / scale; + bounds.m_b = wl_fixed_from_int(bounds.m_b) / scale; + + bounds.wrapPoint(xy_new[0], xy_new[1], 0, win->getCursorGrabAxis()); + + /* Push an event so the new location is registered. */ + if ((xy_new[0] != input->xy[0]) || (xy_new[1] != input->xy[1])) { + input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(), + GHOST_kEventCursorMove, + win, + wl_fixed_to_int(scale * xy_new[0]), + wl_fixed_to_int(scale * xy_new[1]), + GHOST_TABLET_DATA_NONE)); + } + input->xy[0] = xy_new[0]; + input->xy[1] = xy_new[1]; + + zwp_locked_pointer_v1_set_cursor_position_hint( + input->locked_pointer, xy_new[0], xy_new[1]); + wl_surface_commit(surface); + } + } + zwp_locked_pointer_v1_destroy(input->locked_pointer); input->locked_pointer = nullptr; } @@ -2072,13 +2809,13 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mo * An alternative could be to draw the cursor in software (and hide the real cursor), * or just accept a locked cursor on WAYLAND. */ input->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer( - d->relative_pointer_manager, input->pointer); + d->relative_pointer_manager, input->wl_pointer); zwp_relative_pointer_v1_add_listener( input->relative_pointer, &relative_pointer_listener, input); input->locked_pointer = zwp_pointer_constraints_v1_lock_pointer( d->pointer_constraints, surface, - input->pointer, + input->wl_pointer, nullptr, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); } @@ -2088,7 +2825,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mo input->confined_pointer = zwp_pointer_constraints_v1_confine_pointer( d->pointer_constraints, surface, - input->pointer, + input->wl_pointer, nullptr, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); } diff --git a/intern/ghost/intern/GHOST_SystemWayland.h b/intern/ghost/intern/GHOST_SystemWayland.h index 5b3e4f8ed75..762ceb80e38 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.h +++ b/intern/ghost/intern/GHOST_SystemWayland.h @@ -22,11 +22,32 @@ class GHOST_WindowWayland; struct display_t; struct output_t { - struct wl_output *output; - int32_t width_pxl, height_pxl; /* Dimensions in pixel. */ - int32_t width_mm, height_mm; /* Dimensions in millimeter. */ - int transform; - int scale; + struct wl_output *wl_output = nullptr; + struct zxdg_output_v1 *xdg_output = nullptr; + /** Dimensions in pixels. */ + int32_t size_native[2] = {0, 0}; + /** Dimensions in millimeter. */ + int32_t size_mm[2] = {0, 0}; + + int32_t size_logical[2] = {0, 0}; + bool has_size_logical = false; + + int32_t position_logical[2] = {0, 0}; + bool has_position_logical = false; + + int transform = 0; + int scale = 1; + /** + * The integer `scale` value should be used in almost all cases, + * as this is what is used for most API calls. + * Only use fractional scaling to calculate the DPI. + * + * \note Internally an #wl_fixed_t is used to store the scale of the display, + * so use the same value here (avoid floating point arithmetic in general). + */ + wl_fixed_t scale_fractional = wl_fixed_from_int(1); + bool has_scale_fractional = false; + std::string make; std::string model; }; @@ -79,9 +100,9 @@ class GHOST_SystemWayland : public GHOST_System { wl_compositor *compositor(); - xdg_wm_base *shell(); + xdg_wm_base *xdg_shell(); - zxdg_decoration_manager_v1 *decoration_manager(); + zxdg_decoration_manager_v1 *xdg_decoration_manager(); const std::vector<output_t *> &outputs() const; @@ -104,6 +125,7 @@ class GHOST_SystemWayland : public GHOST_System { GHOST_TSuccess setCursorVisibility(bool visible); bool supportsCursorWarp(); + bool supportsWindowPosition(); GHOST_TSuccess setCursorGrab(const GHOST_TGrabCursorMode mode, const GHOST_TGrabCursorMode mode_current, diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index e93a56cc8d4..bed9cd6c784 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -506,8 +506,9 @@ static void destroyIMCallback(XIM /*xim*/, XPointer ptr, XPointer /*data*/) { GHOST_PRINT("XIM server died\n"); - if (ptr) + if (ptr) { *(XIM *)ptr = nullptr; + } } bool GHOST_SystemX11::openX11_IM() @@ -519,8 +520,9 @@ bool GHOST_SystemX11::openX11_IM() XSetLocaleModifiers(""); m_xim = XOpenIM(m_display, nullptr, (char *)GHOST_X11_RES_NAME, (char *)GHOST_X11_RES_CLASS); - if (!m_xim) + if (!m_xim) { return false; + } XIMCallback destroy; destroy.callback = (XIMProc)destroyIMCallback; @@ -641,10 +643,11 @@ bool GHOST_SystemX11::processEvents(bool waitForEvent) SleepTillEvent(m_display, -1); } else { - int64_t maxSleep = next - getMilliSeconds(); + const int64_t maxSleep = next - getMilliSeconds(); - if (maxSleep >= 0) + if (maxSleep >= 0) { SleepTillEvent(m_display, next - getMilliSeconds()); + } } } @@ -692,8 +695,9 @@ bool GHOST_SystemX11::processEvents(bool waitForEvent) } else if (xevent.type == KeyPress) { if ((xevent.xkey.keycode == m_last_release_keycode) && - ((xevent.xkey.time <= m_last_release_time))) + ((xevent.xkey.time <= m_last_release_time))) { continue; + } } processEvent(&xevent); @@ -733,7 +737,7 @@ bool GHOST_SystemX11::processEvents(bool waitForEvent) XK_Super_R, }; - for (int i = 0; i < (sizeof(modifiers) / sizeof(*modifiers)); i++) { + for (int i = 0; i < (int)(sizeof(modifiers) / sizeof(*modifiers)); i++) { KeyCode kc = XKeysymToKeycode(m_display, modifiers[i]); if (kc != 0 && ((xevent.xkeymap.key_vector[kc >> 3] >> (kc & 7)) & 1) != 0) { pushEvent(new GHOST_EventKey(getMilliSeconds(), @@ -1233,36 +1237,46 @@ void GHOST_SystemX11::processEvent(XEvent *xe) /* process wheel mouse events and break, only pass on press events */ if (xbe.button == Button4) { - if (xbe.type == ButtonPress) + if (xbe.type == ButtonPress) { g_event = new GHOST_EventWheel(getMilliSeconds(), window, 1); + } break; } - else if (xbe.button == Button5) { - if (xbe.type == ButtonPress) + if (xbe.button == Button5) { + if (xbe.type == ButtonPress) { g_event = new GHOST_EventWheel(getMilliSeconds(), window, -1); + } break; } /* process rest of normal mouse buttons */ - if (xbe.button == Button1) + if (xbe.button == Button1) { gbmask = GHOST_kButtonMaskLeft; - else if (xbe.button == Button2) + } + else if (xbe.button == Button2) { gbmask = GHOST_kButtonMaskMiddle; - else if (xbe.button == Button3) + } + else if (xbe.button == Button3) { gbmask = GHOST_kButtonMaskRight; - /* It seems events 6 and 7 are for horizontal scrolling. - * you can re-order button mapping like this... (swaps 6,7 with 8,9) - * `xmodmap -e "pointer = 1 2 3 4 5 8 9 6 7"` */ - else if (xbe.button == 6) + /* It seems events 6 and 7 are for horizontal scrolling. + * you can re-order button mapping like this... (swaps 6,7 with 8,9) + * `xmodmap -e "pointer = 1 2 3 4 5 8 9 6 7"` */ + } + else if (xbe.button == 6) { gbmask = GHOST_kButtonMaskButton6; - else if (xbe.button == 7) + } + else if (xbe.button == 7) { gbmask = GHOST_kButtonMaskButton7; - else if (xbe.button == 8) + } + else if (xbe.button == 8) { gbmask = GHOST_kButtonMaskButton4; - else if (xbe.button == 9) + } + else if (xbe.button == 9) { gbmask = GHOST_kButtonMaskButton5; - else + } + else { break; + } g_event = new GHOST_EventButton( getMilliSeconds(), type, window, gbmask, window->GetTabletData()); @@ -1326,8 +1340,9 @@ void GHOST_SystemX11::processEvent(XEvent *xe) if (XGetWindowAttributes(m_display, xcme.window, &attr) == True) { if (XGetInputFocus(m_display, &fwin, &revert_to) == True) { if (attr.map_state == IsViewable) { - if (fwin != xcme.window) + if (fwin != xcme.window) { XSetInputFocus(m_display, xcme.window, RevertToParent, xcme.data.l[1]); + } } } } @@ -1376,10 +1391,12 @@ void GHOST_SystemX11::processEvent(XEvent *xe) // printf("X: %s window %d\n", // xce.type == EnterNotify ? "entering" : "leaving", (int) xce.window); - if (xce.type == EnterNotify) + if (xce.type == EnterNotify) { m_windowManager->setActiveWindow(window); - else + } + else { m_windowManager->setWindowInactive(window); + } break; } @@ -1645,10 +1662,10 @@ static GHOST_TSuccess getCursorPosition_impl(Display *display, &mask_return) == False) { return GHOST_kFailure; } - else { - x = rx; - y = ry; - } + + x = rx; + y = ry; + return GHOST_kSuccess; } @@ -1955,18 +1972,19 @@ void GHOST_SystemX11::getClipboard_xcout(const XEvent *evt, return; case XCLIB_XCOUT_SENTCONVSEL: - if (evt->type != SelectionNotify) + if (evt->type != SelectionNotify) { return; + } if (target == m_atom.UTF8_STRING && evt->xselection.property == None) { *context = XCLIB_XCOUT_FALLBACK_UTF8; return; } - else if (target == m_atom.COMPOUND_TEXT && evt->xselection.property == None) { + if (target == m_atom.COMPOUND_TEXT && evt->xselection.property == None) { *context = XCLIB_XCOUT_FALLBACK_COMP; return; } - else if (target == m_atom.TEXT && evt->xselection.property == None) { + if (target == m_atom.TEXT && evt->xselection.property == None) { *context = XCLIB_XCOUT_FALLBACK_TEXT; return; } @@ -2042,12 +2060,14 @@ void GHOST_SystemX11::getClipboard_xcout(const XEvent *evt, * then read it, delete it, etc. */ /* make sure that the event is relevant */ - if (evt->type != PropertyNotify) + if (evt->type != PropertyNotify) { return; + } /* skip unless the property has a new value */ - if (evt->xproperty.state != PropertyNewValue) + if (evt->xproperty.state != PropertyNewValue) { return; + } /* check size and format of the property */ XGetWindowProperty(m_display, @@ -2121,7 +2141,6 @@ void GHOST_SystemX11::getClipboard_xcout(const XEvent *evt, XFlush(m_display); return; } - return; } char *GHOST_SystemX11::getClipboard(bool selection) const @@ -2136,10 +2155,12 @@ char *GHOST_SystemX11::getClipboard(bool selection) const XEvent evt; unsigned int context = XCLIB_XCOUT_NONE; - if (selection == True) + if (selection == True) { sseln = m_atom.PRIMARY; - else + } + else { sseln = m_atom.CLIPBOARD; + } const vector<GHOST_IWindow *> &win_vec = m_windowManager->getWindows(); vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin(); @@ -2154,20 +2175,18 @@ char *GHOST_SystemX11::getClipboard(bool selection) const strcpy(sel_buf, txt_cut_buffer); return sel_buf; } - else { - sel_buf = (char *)malloc(strlen(txt_select_buffer) + 1); - strcpy(sel_buf, txt_select_buffer); - return sel_buf; - } + sel_buf = (char *)malloc(strlen(txt_select_buffer) + 1); + strcpy(sel_buf, txt_select_buffer); + return sel_buf; } - else if (owner == None) { + if (owner == None) { return nullptr; } /* Restore events so copy doesn't swallow other event types (keyboard/mouse). */ vector<XEvent> restore_events; - while (1) { + while (true) { /* only get an event if xcout() is doing something */ bool restore_this_event = false; if (context != XCLIB_XCOUT_NONE) { @@ -2188,26 +2207,27 @@ char *GHOST_SystemX11::getClipboard(bool selection) const target = m_atom.STRING; continue; } - else if (context == XCLIB_XCOUT_FALLBACK_UTF8) { + if (context == XCLIB_XCOUT_FALLBACK_UTF8) { /* utf8 fail, move to compound text. */ context = XCLIB_XCOUT_NONE; target = m_atom.COMPOUND_TEXT; continue; } - else if (context == XCLIB_XCOUT_FALLBACK_COMP) { + if (context == XCLIB_XCOUT_FALLBACK_COMP) { /* Compound text fail, move to text. */ context = XCLIB_XCOUT_NONE; target = m_atom.TEXT; continue; } - else if (context == XCLIB_XCOUT_FALLBACK_TEXT) { + if (context == XCLIB_XCOUT_FALLBACK_TEXT) { /* Text fail, nothing else to try, break. */ context = XCLIB_XCOUT_NONE; } /* Only continue if #xcout() is doing something. */ - if (context == XCLIB_XCOUT_NONE) + if (context == XCLIB_XCOUT_NONE) { break; + } } while (!restore_events.empty()) { @@ -2221,10 +2241,12 @@ char *GHOST_SystemX11::getClipboard(bool selection) const memcpy(tmp_data, (char *)sel_buf, sel_len); tmp_data[sel_len] = '\0'; - if (sseln == m_atom.STRING) + if (sseln == m_atom.STRING) { XFree(sel_buf); - else + } + else { free(sel_buf); + } return tmp_data; } @@ -2244,8 +2266,9 @@ void GHOST_SystemX11::putClipboard(const char *buffer, bool selection) const if (selection == False) { XSetSelectionOwner(m_display, m_atom.CLIPBOARD, m_window, CurrentTime); owner = XGetSelectionOwner(m_display, m_atom.CLIPBOARD); - if (txt_cut_buffer) + if (txt_cut_buffer) { free((void *)txt_cut_buffer); + } txt_cut_buffer = (char *)malloc(strlen(buffer) + 1); strcpy(txt_cut_buffer, buffer); @@ -2253,15 +2276,17 @@ void GHOST_SystemX11::putClipboard(const char *buffer, bool selection) const else { XSetSelectionOwner(m_display, m_atom.PRIMARY, m_window, CurrentTime); owner = XGetSelectionOwner(m_display, m_atom.PRIMARY); - if (txt_select_buffer) + if (txt_select_buffer) { free((void *)txt_select_buffer); + } txt_select_buffer = (char *)malloc(strlen(buffer) + 1); strcpy(txt_select_buffer, buffer); } - if (owner != m_window) + if (owner != m_window) { fprintf(stderr, "failed to own primary\n"); + } } } @@ -2374,7 +2399,7 @@ GHOST_TSuccess GHOST_SystemX11::showMessageBox(const char *title, const char *help_label, const char *continue_label, const char *link, - GHOST_DialogOptions) const + GHOST_DialogOptions /*dialog_options*/) const { char **text_splitted = nullptr; int textLines = 0; @@ -2557,18 +2582,23 @@ static bool match_token(const char *haystack, const char *needle) { const char *h, *n; for (h = haystack; *h;) { - while (*h && is_filler_char(*h)) + while (*h && is_filler_char(*h)) { h++; - if (!*h) + } + if (!*h) { break; + } - for (n = needle; *n && *h && tolower(*h) == tolower(*n); n++) + for (n = needle; *n && *h && tolower(*h) == tolower(*n); n++) { h++; - if (!*n && (is_filler_char(*h) || !*h)) + } + if (!*n && (is_filler_char(*h) || !*h)) { return true; + } - while (*h && !is_filler_char(*h)) + while (*h && !is_filler_char(*h)) { h++; + } } return false; } diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp index f9f168f772d..21e3793d3b1 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.cpp +++ b/intern/ghost/intern/GHOST_WindowWayland.cpp @@ -15,41 +15,117 @@ #include <wayland-egl.h> +#include <algorithm> /* For `std::find`. */ + static constexpr size_t base_dpi = 96; struct window_t { - GHOST_WindowWayland *w; - wl_surface *surface; - /* Outputs on which the window is currently shown on. */ - std::unordered_set<const output_t *> outputs; - uint16_t dpi = 0; - int scale = 1; - struct xdg_surface *xdg_surface; - struct xdg_toplevel *xdg_toplevel; + GHOST_WindowWayland *w = nullptr; + struct wl_surface *wl_surface = nullptr; + /** + * Outputs on which the window is currently shown on. + * + * This is an ordered set (whoever adds to this is responsible for keeping members unique). + * In practice this is rarely manipulated and is limited by the number of physical displays. + */ + std::vector<output_t *> outputs; + + /** The scale value written to #wl_surface_set_buffer_scale. */ + int scale = 0; + /** + * The DPI, either: + * - `scale * base_dpi` + * - `wl_fixed_to_int(scale_fractional * base_dpi)` + * When fractional scaling is available. + */ + uint32_t dpi = 0; + + struct xdg_surface *xdg_surface = nullptr; + struct xdg_toplevel *xdg_toplevel = nullptr; struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration = nullptr; - enum zxdg_toplevel_decoration_v1_mode decoration_mode; - wl_egl_window *egl_window; - int32_t pending_width, pending_height; - bool is_maximised; - bool is_fullscreen; - bool is_active; - bool is_dialog; - int32_t width, height; + enum zxdg_toplevel_decoration_v1_mode decoration_mode = (enum zxdg_toplevel_decoration_v1_mode)0; + wl_egl_window *egl_window = nullptr; + bool is_maximised = false; + bool is_fullscreen = false; + bool is_active = false; + bool is_dialog = false; + + int32_t size[2] = {0, 0}; + int32_t size_pending[2] = {0, 0}; }; /* -------------------------------------------------------------------- */ -/** \name Wayland Interface Callbacks - * - * These callbacks are registered for Wayland interfaces and called when - * an event is received from the compositor. +/** \name Internal Utilities + * \{ */ + +/** + * Return -1 if `output_a` has a scale smaller than `output_b`, 0 when there equal, otherwise 1. + */ +static int output_scale_cmp(const output_t *output_a, const output_t *output_b) +{ + if (output_a->scale < output_b->scale) { + return -1; + } + if (output_a->scale > output_b->scale) { + return 1; + } + if (output_a->has_scale_fractional || output_b->has_scale_fractional) { + const wl_fixed_t scale_fractional_a = output_a->has_scale_fractional ? + output_a->scale_fractional : + wl_fixed_from_int(output_a->scale); + const wl_fixed_t scale_fractional_b = output_b->has_scale_fractional ? + output_b->scale_fractional : + wl_fixed_from_int(output_b->scale); + if (scale_fractional_a < scale_fractional_b) { + return -1; + } + if (scale_fractional_a > scale_fractional_b) { + return 1; + } + } + return 0; +} + +static int outputs_max_scale_or_default(const std::vector<output_t *> &outputs, + const int32_t scale_default, + uint32_t *r_dpi) +{ + const output_t *output_max = nullptr; + for (const output_t *reg_output : outputs) { + if (!output_max || (output_scale_cmp(output_max, reg_output) == -1)) { + output_max = reg_output; + } + } + + if (output_max) { + if (r_dpi) { + *r_dpi = output_max->has_scale_fractional ? + /* Fractional DPI. */ + wl_fixed_to_int(output_max->scale_fractional * base_dpi) : + /* Simple non-fractional DPI. */ + (output_max->scale * base_dpi); + } + return output_max->scale; + } + + if (r_dpi) { + *r_dpi = scale_default * base_dpi; + } + return scale_default; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Listener (XDG Top Level), #xdg_toplevel_listener * \{ */ -static void toplevel_configure( +static void xdg_toplevel_handle_configure( void *data, xdg_toplevel * /*xdg_toplevel*/, int32_t width, int32_t height, wl_array *states) { window_t *win = static_cast<window_t *>(data); - win->pending_width = width; - win->pending_height = height; + win->size_pending[0] = win->scale * width; + win->size_pending[1] = win->scale * height; win->is_maximised = false; win->is_fullscreen = false; @@ -77,17 +153,23 @@ static void toplevel_configure( } } -static void toplevel_close(void *data, xdg_toplevel * /*xdg_toplevel*/) +static void xdg_toplevel_handle_close(void *data, xdg_toplevel * /*xdg_toplevel*/) { static_cast<window_t *>(data)->w->close(); } static const xdg_toplevel_listener toplevel_listener = { - toplevel_configure, - toplevel_close, + xdg_toplevel_handle_configure, + xdg_toplevel_handle_close, }; -static void toplevel_decoration_configure( +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Listener (XDG Decoration Listener), #zxdg_toplevel_decoration_v1_listener + * \{ */ + +static void xdg_toplevel_decoration_handle_configure( void *data, struct zxdg_toplevel_decoration_v1 * /*zxdg_toplevel_decoration_v1*/, uint32_t mode) @@ -96,10 +178,16 @@ static void toplevel_decoration_configure( } static const zxdg_toplevel_decoration_v1_listener toplevel_decoration_v1_listener = { - toplevel_decoration_configure, + xdg_toplevel_decoration_handle_configure, }; -static void surface_configure(void *data, xdg_surface *xdg_surface, uint32_t serial) +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Listener (XDG Surface Handle Configure), #xdg_surface_listener + * \{ */ + +static void xdg_surface_handle_configure(void *data, xdg_surface *xdg_surface, uint32_t serial) { window_t *win = static_cast<window_t *>(data); @@ -107,12 +195,12 @@ static void surface_configure(void *data, xdg_surface *xdg_surface, uint32_t ser return; } - if (win->pending_width != 0 && win->pending_height != 0) { - win->width = win->scale * win->pending_width; - win->height = win->scale * win->pending_height; - wl_egl_window_resize(win->egl_window, win->width, win->height, 0, 0); - win->pending_width = 0; - win->pending_height = 0; + if (win->size_pending[0] != 0 && win->size_pending[1] != 0) { + win->size[0] = win->size_pending[0]; + win->size[1] = win->size_pending[1]; + wl_egl_window_resize(win->egl_window, win->size[0], win->size[1], 0, 0); + win->size_pending[0] = 0; + win->size_pending[1] = 0; win->w->notify_size(); } @@ -126,55 +214,49 @@ static void surface_configure(void *data, xdg_surface *xdg_surface, uint32_t ser xdg_surface_ack_configure(xdg_surface, serial); } -static const xdg_surface_listener surface_listener = { - surface_configure, +static const xdg_surface_listener xdg_surface_listener = { + xdg_surface_handle_configure, }; -static bool update_scale(GHOST_WindowWayland *window) +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Listener (Surface), #wl_surface_listener + * \{ */ + +static void surface_handle_enter(void *data, + struct wl_surface * /*wl_surface*/, + struct wl_output *output) { - int scale = 0; - for (const output_t *output : window->outputs_active()) { - if (output->scale > scale) { - scale = output->scale; - } + GHOST_WindowWayland *w = static_cast<GHOST_WindowWayland *>(data); + output_t *reg_output = w->output_find_by_wl(output); + if (reg_output == nullptr) { + return; } - if (scale > 0 && window->scale() != scale) { - window->scale() = scale; - /* Using the real DPI will cause wrong scaling of the UI - * use a multiplier for the default DPI as workaround. */ - window->dpi() = scale * base_dpi; - wl_surface_set_buffer_scale(window->surface(), scale); - return true; + if (w->outputs_enter(reg_output)) { + w->outputs_changed_update_scale(); } - return false; } -static void surface_enter(void *data, struct wl_surface * /*wl_surface*/, struct wl_output *output) +static void surface_handle_leave(void *data, + struct wl_surface * /*wl_surface*/, + struct wl_output *output) { GHOST_WindowWayland *w = static_cast<GHOST_WindowWayland *>(data); - for (const output_t *reg_output : w->outputs()) { - if (reg_output->output == output) { - w->outputs_active().insert(reg_output); - } + output_t *reg_output = w->output_find_by_wl(output); + if (reg_output == nullptr) { + return; } - update_scale(w); -} -static void surface_leave(void *data, struct wl_surface * /*wl_surface*/, struct wl_output *output) -{ - GHOST_WindowWayland *w = static_cast<GHOST_WindowWayland *>(data); - for (const output_t *reg_output : w->outputs()) { - if (reg_output->output == output) { - w->outputs_active().erase(reg_output); - } + if (w->outputs_leave(reg_output)) { + w->outputs_changed_update_scale(); } - update_scale(w); } struct wl_surface_listener wl_surface_listener = { - surface_enter, - surface_leave, + surface_handle_enter, + surface_handle_leave, }; /** \} */ @@ -208,32 +290,51 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, { w->w = this; - w->width = int32_t(width); - w->height = int32_t(height); + w->size[0] = int32_t(width); + w->size[1] = int32_t(height); w->is_dialog = is_dialog; + /* NOTE(@campbellbarton): The scale set here to avoid flickering on startup. + * When all monitors use the same scale (which is quite common) there aren't any problems. + * + * When monitors have different scales there may still be a visible window resize on startup. + * Ideally it would be possible to know the scale this window will use however that's only + * known once #surface_enter callback runs (which isn't guaranteed to run at all). + * + * Using the maximum scale is best as it results in the window first being smaller, + * avoiding a large window flashing before it's made smaller. */ + w->scale = outputs_max_scale_or_default(this->m_system->outputs(), 1, &w->dpi); + /* Window surfaces. */ - w->surface = wl_compositor_create_surface(m_system->compositor()); - wl_surface_add_listener(w->surface, &wl_surface_listener, this); + w->wl_surface = wl_compositor_create_surface(m_system->compositor()); + wl_surface_set_buffer_scale(this->surface(), w->scale); + + wl_surface_add_listener(w->wl_surface, &wl_surface_listener, this); - w->egl_window = wl_egl_window_create(w->surface, int(width), int(height)); + w->egl_window = wl_egl_window_create(w->wl_surface, int(w->size[0]), int(w->size[1])); - w->xdg_surface = xdg_wm_base_get_xdg_surface(m_system->shell(), w->surface); + w->xdg_surface = xdg_wm_base_get_xdg_surface(m_system->xdg_shell(), w->wl_surface); w->xdg_toplevel = xdg_surface_get_toplevel(w->xdg_surface); - if (m_system->decoration_manager()) { + /* NOTE: The limit is in points (not pixels) so Hi-DPI will limit to larger number of pixels. + * This has the advantage that the size limit is the same when moving the window between monitors + * with different scales set. If it was important to limit in pixels it could be re-calculated + * when the `w->scale` changed. */ + xdg_toplevel_set_min_size(w->xdg_toplevel, 320, 240); + + if (m_system->xdg_decoration_manager()) { w->xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( - m_system->decoration_manager(), w->xdg_toplevel); + m_system->xdg_decoration_manager(), w->xdg_toplevel); zxdg_toplevel_decoration_v1_add_listener( w->xdg_toplevel_decoration, &toplevel_decoration_v1_listener, w); zxdg_toplevel_decoration_v1_set_mode(w->xdg_toplevel_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); } - wl_surface_set_user_data(w->surface, this); + wl_surface_set_user_data(w->wl_surface, this); - xdg_surface_add_listener(w->xdg_surface, &surface_listener, w); + xdg_surface_add_listener(w->xdg_surface, &xdg_surface_listener, w); xdg_toplevel_add_listener(w->xdg_toplevel, &toplevel_listener, w); if (parentWindow && is_dialog) { @@ -242,7 +343,7 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, } /* Call top-level callbacks. */ - wl_surface_commit(w->surface); + wl_surface_commit(w->wl_surface); wl_display_roundtrip(m_system->display()); #ifdef GHOST_OPENGL_ALPHA @@ -296,32 +397,92 @@ GHOST_TSuccess GHOST_WindowWayland::notify_size() wl_surface *GHOST_WindowWayland::surface() const { - return w->surface; + return w->wl_surface; } -const std::vector<output_t *> &GHOST_WindowWayland::outputs() const +const std::vector<output_t *> &GHOST_WindowWayland::outputs() { - return m_system->outputs(); + return w->outputs; } -std::unordered_set<const output_t *> &GHOST_WindowWayland::outputs_active() +output_t *GHOST_WindowWayland::output_find_by_wl(struct wl_output *output) { - return w->outputs; + for (output_t *reg_output : this->m_system->outputs()) { + if (reg_output->wl_output == output) { + return reg_output; + } + } + return nullptr; } -uint16_t &GHOST_WindowWayland::dpi() +bool GHOST_WindowWayland::outputs_changed_update_scale() +{ + uint32_t dpi_next; + const int scale_next = outputs_max_scale_or_default(this->outputs(), 0, &dpi_next); + if (scale_next == 0) { + return false; + } + + window_t *win = this->w; + const uint32_t dpi_curr = win->dpi; + const int scale_curr = win->scale; + bool changed = false; + + if (scale_next != scale_curr) { + /* Unlikely but possible there is a pending size change is set. */ + win->size_pending[0] = (win->size_pending[0] / scale_curr) * scale_next; + win->size_pending[1] = (win->size_pending[1] / scale_curr) * scale_next; + + win->scale = scale_next; + wl_surface_set_buffer_scale(this->surface(), scale_next); + changed = true; + } + + if (dpi_next != dpi_curr) { + /* Using the real DPI will cause wrong scaling of the UI + * use a multiplier for the default DPI as workaround. */ + win->dpi = dpi_next; + changed = true; + } + + return changed; +} + +bool GHOST_WindowWayland::outputs_enter(output_t *reg_output) +{ + std::vector<output_t *> &outputs = w->outputs; + auto it = std::find(outputs.begin(), outputs.end(), reg_output); + if (it != outputs.end()) { + return false; + } + outputs.push_back(reg_output); + return true; +} + +bool GHOST_WindowWayland::outputs_leave(output_t *reg_output) +{ + std::vector<output_t *> &outputs = w->outputs; + auto it = std::find(outputs.begin(), outputs.end(), reg_output); + if (it == outputs.end()) { + return false; + } + outputs.erase(it); + return true; +} + +uint16_t GHOST_WindowWayland::dpi() { return w->dpi; } -int &GHOST_WindowWayland::scale() +int GHOST_WindowWayland::scale() { return w->scale; } GHOST_TSuccess GHOST_WindowWayland::setWindowCursorGrab(GHOST_TGrabCursorMode mode) { - return m_system->setCursorGrab(mode, m_cursorGrab, w->surface); + return m_system->setCursorGrab(mode, m_cursorGrab, w->wl_surface); } GHOST_TSuccess GHOST_WindowWayland::setWindowCursorShape(GHOST_TStandardCursor shape) @@ -356,22 +517,32 @@ void GHOST_WindowWayland::getWindowBounds(GHOST_Rect &bounds) const void GHOST_WindowWayland::getClientBounds(GHOST_Rect &bounds) const { - bounds.set(0, 0, w->width, w->height); + bounds.set(0, 0, w->size[0], w->size[1]); } GHOST_TSuccess GHOST_WindowWayland::setClientWidth(uint32_t width) { - return setClientSize(width, uint32_t(w->height)); + return setClientSize(width, uint32_t(w->size[1])); } GHOST_TSuccess GHOST_WindowWayland::setClientHeight(uint32_t height) { - return setClientSize(uint32_t(w->width), height); + return setClientSize(uint32_t(w->size[0]), height); } GHOST_TSuccess GHOST_WindowWayland::setClientSize(uint32_t width, uint32_t height) { wl_egl_window_resize(w->egl_window, int(width), int(height), 0, 0); + + /* Override any pending size that may be set. */ + w->size_pending[0] = 0; + w->size_pending[1] = 0; + + w->size[0] = width; + w->size[1] = height; + + notify_size(); + return GHOST_kSuccess; } @@ -403,7 +574,7 @@ GHOST_WindowWayland::~GHOST_WindowWayland() } xdg_toplevel_destroy(w->xdg_toplevel); xdg_surface_destroy(w->xdg_surface); - wl_surface_destroy(w->surface); + wl_surface_destroy(w->wl_surface); delete w; } @@ -494,7 +665,7 @@ void GHOST_WindowWayland::setOpaque() const /* Make the window opaque. */ region = wl_compositor_create_region(m_system->compositor()); - wl_region_add(region, 0, 0, w->width, w->height); + wl_region_add(region, 0, 0, w->size[0], w->size[1]); wl_surface_set_opaque_region(w->surface, region); wl_region_destroy(region); } diff --git a/intern/ghost/intern/GHOST_WindowWayland.h b/intern/ghost/intern/GHOST_WindowWayland.h index d5dd123014b..b6d9fa04079 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.h +++ b/intern/ghost/intern/GHOST_WindowWayland.h @@ -17,7 +17,6 @@ class GHOST_SystemWayland; struct output_t; struct window_t; -struct wl_surface; class GHOST_WindowWayland : public GHOST_Window { public: @@ -40,25 +39,8 @@ class GHOST_WindowWayland : public GHOST_Window { uint16_t getDPIHint() override; - GHOST_TSuccess close(); - - GHOST_TSuccess activate(); - - GHOST_TSuccess deactivate(); - - GHOST_TSuccess notify_size(); + /* Ghost API */ - wl_surface *surface() const; - - const std::vector<output_t *> &outputs() const; - - std::unordered_set<const output_t *> &outputs_active(); - - uint16_t &dpi(); - - int &scale(); - - protected: GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode) override; GHOST_TSuccess setWindowCursorShape(GHOST_TStandardCursor shape) override; @@ -109,6 +91,30 @@ class GHOST_WindowWayland : public GHOST_Window { void setOpaque() const; #endif + /* WAYLAND utility functions. */ + + GHOST_TSuccess close(); + + GHOST_TSuccess activate(); + + GHOST_TSuccess deactivate(); + + GHOST_TSuccess notify_size(); + + struct wl_surface *surface() const; + + output_t *output_find_by_wl(struct wl_output *output); + + const std::vector<output_t *> &outputs(); + + bool outputs_enter(output_t *reg_output); + bool outputs_leave(output_t *reg_output); + bool outputs_changed_update_scale(); + + uint16_t dpi(); + + int scale(); + private: GHOST_SystemWayland *m_system; struct window_t *w; diff --git a/intern/ghost/intern/GHOST_XrAction.cpp b/intern/ghost/intern/GHOST_XrAction.cpp index 587b1124848..0e725bf4075 100644 --- a/intern/ghost/intern/GHOST_XrAction.cpp +++ b/intern/ghost/intern/GHOST_XrAction.cpp @@ -332,23 +332,26 @@ void GHOST_XrAction::updateState(XrSession session, break; } case GHOST_kXrActionTypePoseInput: { - XrActionStatePose state{XR_TYPE_ACTION_STATE_POSE}; - CHECK_XR( - xrGetActionStatePose(session, &state_info, &state), - (std::string("Failed to get state for pose action \"") + action_name + "\".").data()); - if (state.isActive) { - XrSpace pose_space = ((subaction != nullptr) && (subaction->space != nullptr)) ? - subaction->space->getSpace() : - XR_NULL_HANDLE; - if (pose_space != XR_NULL_HANDLE) { - XrSpaceLocation space_location{XR_TYPE_SPACE_LOCATION}; - CHECK_XR( - xrLocateSpace( - pose_space, reference_space, predicted_display_time, &space_location), - (std::string("Failed to query pose space for action \"") + action_name + "\".") - .data()); - copy_openxr_pose_to_ghost_pose(space_location.pose, - ((GHOST_XrPose *)m_states)[subaction_idx]); + /* Check for valid display time to avoid an error in #xrLocateSpace(). */ + if (predicted_display_time > 0) { + XrActionStatePose state{XR_TYPE_ACTION_STATE_POSE}; + CHECK_XR(xrGetActionStatePose(session, &state_info, &state), + (std::string("Failed to get state for pose action \"") + action_name + "\".") + .data()); + if (state.isActive) { + XrSpace pose_space = ((subaction != nullptr) && (subaction->space != nullptr)) ? + subaction->space->getSpace() : + XR_NULL_HANDLE; + if (pose_space != XR_NULL_HANDLE) { + XrSpaceLocation space_location{XR_TYPE_SPACE_LOCATION}; + CHECK_XR( + xrLocateSpace( + pose_space, reference_space, predicted_display_time, &space_location), + (std::string("Failed to query pose space for action \"") + action_name + "\".") + .data()); + copy_openxr_pose_to_ghost_pose(space_location.pose, + ((GHOST_XrPose *)m_states)[subaction_idx]); + } } } break; diff --git a/intern/ghost/test/gears/GHOST_C-Test.c b/intern/ghost/test/gears/GHOST_C-Test.c index 71f3a6f43b4..46c8fda0665 100644 --- a/intern/ghost/test/gears/GHOST_C-Test.c +++ b/intern/ghost/test/gears/GHOST_C-Test.c @@ -31,7 +31,7 @@ #endif /* defined(WIN32) || defined(__APPLE__) */ static void gearsTimerProc(GHOST_TimerTaskHandle task, uint64_t time); -int processEvent(GHOST_EventHandle hEvent, GHOST_TUserDataPtr userData); +bool processEvent(GHOST_EventHandle hEvent, GHOST_TUserDataPtr userData); static GLfloat view_rotx = 20.0, view_roty = 30.0, view_rotz = 0.0; static GLfloat fAngle = 0.0; @@ -269,9 +269,9 @@ static void setViewPortGL(GHOST_WindowHandle hWindow) GHOST_DisposeRectangle(hRect); } -int processEvent(GHOST_EventHandle hEvent, GHOST_TUserDataPtr userData) +bool processEvent(GHOST_EventHandle hEvent, GHOST_TUserDataPtr userData) { - int handled = 1; + bool handled = true; int cursor; int visibility; GHOST_TEventKeyData *keyData = NULL; @@ -389,10 +389,10 @@ int processEvent(GHOST_EventHandle hEvent, GHOST_TUserDataPtr userData) } break; case GHOST_kEventWindowActivate: - handled = 0; + handled = false; break; case GHOST_kEventWindowDeactivate: - handled = 0; + handled = false; break; case GHOST_kEventWindowUpdate: { GHOST_WindowHandle window2 = GHOST_GetEventWindow(hEvent); @@ -404,7 +404,7 @@ int processEvent(GHOST_EventHandle hEvent, GHOST_TUserDataPtr userData) } break; default: - handled = 0; + handled = false; break; } return handled; diff --git a/intern/ghost/test/multitest/MultiTest.c b/intern/ghost/test/multitest/MultiTest.c index 35b1de65171..157e4f1b0f2 100644 --- a/intern/ghost/test/multitest/MultiTest.c +++ b/intern/ghost/test/multitest/MultiTest.c @@ -812,7 +812,7 @@ struct _MultiTestApp { int exit; }; -static int multitest_event_handler(GHOST_EventHandle evt, GHOST_TUserDataPtr data) +static bool multitest_event_handler(GHOST_EventHandle evt, GHOST_TUserDataPtr data) { MultiTestApp *app = data; GHOST_WindowHandle win; @@ -820,7 +820,7 @@ static int multitest_event_handler(GHOST_EventHandle evt, GHOST_TUserDataPtr dat win = GHOST_GetEventWindow(evt); if (win && !GHOST_ValidWindow(app->sys, win)) { loggerwindow_log(app->logger, "WARNING: bad event, non-valid window\n"); - return 1; + return true; } if (win) { @@ -845,7 +845,7 @@ static int multitest_event_handler(GHOST_EventHandle evt, GHOST_TUserDataPtr dat } } - return 1; + return true; } /**/ diff --git a/release/datafiles/brushicons/curves_sculpt_add.png b/release/datafiles/brushicons/curves_sculpt_add.png Binary files differnew file mode 100644 index 00000000000..e8b81b73919 --- /dev/null +++ b/release/datafiles/brushicons/curves_sculpt_add.png diff --git a/release/datafiles/brushicons/curves_sculpt_comb.png b/release/datafiles/brushicons/curves_sculpt_comb.png Binary files differnew file mode 100644 index 00000000000..bdf49e413e3 --- /dev/null +++ b/release/datafiles/brushicons/curves_sculpt_comb.png diff --git a/release/datafiles/brushicons/curves_sculpt_cut.png b/release/datafiles/brushicons/curves_sculpt_cut.png Binary files differnew file mode 100644 index 00000000000..7f9e116fcd1 --- /dev/null +++ b/release/datafiles/brushicons/curves_sculpt_cut.png diff --git a/release/datafiles/brushicons/curves_sculpt_delete.png b/release/datafiles/brushicons/curves_sculpt_delete.png Binary files differnew file mode 100644 index 00000000000..361b8e335a5 --- /dev/null +++ b/release/datafiles/brushicons/curves_sculpt_delete.png diff --git a/release/datafiles/brushicons/curves_sculpt_density.png b/release/datafiles/brushicons/curves_sculpt_density.png Binary files differnew file mode 100644 index 00000000000..305fa02298f --- /dev/null +++ b/release/datafiles/brushicons/curves_sculpt_density.png diff --git a/release/datafiles/brushicons/curves_sculpt_grow_shrink.png b/release/datafiles/brushicons/curves_sculpt_grow_shrink.png Binary files differnew file mode 100644 index 00000000000..e4a76c6d6e4 --- /dev/null +++ b/release/datafiles/brushicons/curves_sculpt_grow_shrink.png diff --git a/release/datafiles/brushicons/curves_sculpt_pinch.png b/release/datafiles/brushicons/curves_sculpt_pinch.png Binary files differnew file mode 100644 index 00000000000..cc98203ef57 --- /dev/null +++ b/release/datafiles/brushicons/curves_sculpt_pinch.png diff --git a/release/datafiles/brushicons/curves_sculpt_puff.png b/release/datafiles/brushicons/curves_sculpt_puff.png Binary files differnew file mode 100644 index 00000000000..e5b48b8a96c --- /dev/null +++ b/release/datafiles/brushicons/curves_sculpt_puff.png diff --git a/release/datafiles/brushicons/curves_sculpt_slide.png b/release/datafiles/brushicons/curves_sculpt_slide.png Binary files differnew file mode 100644 index 00000000000..7627cf76791 --- /dev/null +++ b/release/datafiles/brushicons/curves_sculpt_slide.png diff --git a/release/datafiles/brushicons/curves_sculpt_smooth.png b/release/datafiles/brushicons/curves_sculpt_smooth.png Binary files differnew file mode 100644 index 00000000000..7c7fd8fef4a --- /dev/null +++ b/release/datafiles/brushicons/curves_sculpt_smooth.png diff --git a/release/datafiles/brushicons/curves_sculpt_snake_hook.png b/release/datafiles/brushicons/curves_sculpt_snake_hook.png Binary files differnew file mode 100644 index 00000000000..468abe66c08 --- /dev/null +++ b/release/datafiles/brushicons/curves_sculpt_snake_hook.png diff --git a/release/datafiles/brushicons/paint_select.png b/release/datafiles/brushicons/paint_select.png Binary files differnew file mode 100644 index 00000000000..38f9a1250b2 --- /dev/null +++ b/release/datafiles/brushicons/paint_select.png diff --git a/release/datafiles/icons/ops.curves.density.dat b/release/datafiles/icons/ops.curves.sculpt_density.dat Binary files differindex f336de79082..f336de79082 100644 --- a/release/datafiles/icons/ops.curves.density.dat +++ b/release/datafiles/icons/ops.curves.sculpt_density.dat diff --git a/release/datafiles/icons/ops.curves.slide.dat b/release/datafiles/icons/ops.curves.sculpt_slide.dat Binary files differindex 7f143ad92cc..7f143ad92cc 100644 --- a/release/datafiles/icons/ops.curves.slide.dat +++ b/release/datafiles/icons/ops.curves.sculpt_slide.dat diff --git a/release/datafiles/userdef/userdef_default_theme.c b/release/datafiles/userdef/userdef_default_theme.c index 19798e3f165..04ad04d4812 100644 --- a/release/datafiles/userdef/userdef_default_theme.c +++ b/release/datafiles/userdef/userdef_default_theme.c @@ -194,6 +194,15 @@ const bTheme U_theme_default = { .text_sel = RGBA(0xffffffff), .roundness = 0.2f, }, + .wcol_view_item = { + .outline = RGBA(0x2d2d2dff), + .inner = RGBA(0x303030ff), + .inner_sel = RGBA(0x4772b3ff), + .item = RGBA(0x4772b3ff), + .text = RGBA(0xccccccff), + .text_sel = RGBA(0xffffffff), + .roundness = 0.2f, + }, .wcol_pie_menu = { .outline = RGBA(0x242424ff), .inner = RGBA(0x181818ff), diff --git a/release/scripts/freestyle/modules/freestyle/shaders.py b/release/scripts/freestyle/modules/freestyle/shaders.py index d2b10206b9f..f3e4a58d38e 100644 --- a/release/scripts/freestyle/modules/freestyle/shaders.py +++ b/release/scripts/freestyle/modules/freestyle/shaders.py @@ -726,7 +726,7 @@ class pyDiffusion2Shader(StrokeShader): self._curvatureInfo = Curvature2DAngleF0D() def shade(self, stroke): - for i in range(1, self._nbIter): + for _i in range(1, self._nbIter): it = Interface0DIterator(stroke) for svert in it: svert.point += self._normalInfo(it) * self._lambda * self._curvatureInfo(it) @@ -911,7 +911,7 @@ class pyBluePrintCirclesShader(StrokeShader): it = iter(stroke) - for j in range(self.__turns): + for _j in range(self.__turns): prev_radius = radius prev_center = center radius += randint(-R, R) @@ -952,7 +952,7 @@ class pyBluePrintEllipsesShader(StrokeShader): # for description of the line below, see pyBluePrintCirclesShader directions = phase_to_direction(sv_nb) it = iter(stroke) - for j in range(self.__turns): + for _j in range(self.__turns): prev_radius = radius prev_center = center radius = radius + Vector((randint(-R, R), randint(-R, R))) @@ -1030,7 +1030,7 @@ class pyBluePrintSquaresShader(StrokeShader): it = iter(stroke) verticesToRemove = list() - for j in range(self.__turns): + for _j in range(self.__turns): for i, svert in zip(range(num_segments), it): if i < first: svert.point = points[0] + old_vecs[0] * i / (first - 1) @@ -1125,7 +1125,7 @@ class pyBluePrintDirectedSquaresShader(StrokeShader): it = iter(stroke) verticesToRemove = list() - for j in range(self.__turns): + for _j in range(self.__turns): for i, svert in zip(range(num_segments), it): if i < first: svert.point = points[0] + old_vecs[0] * i / (first - 1) diff --git a/release/scripts/freestyle/modules/freestyle/utils.py b/release/scripts/freestyle/modules/freestyle/utils.py index 152dbca6f5e..309497430a8 100644 --- a/release/scripts/freestyle/modules/freestyle/utils.py +++ b/release/scripts/freestyle/modules/freestyle/utils.py @@ -151,7 +151,7 @@ def normal_at_I0D(it: Interface0DIterator) -> Vector: # this case sometimes has a small difference with Normal2DF0D (1e-3 -ish) it.decrement() a = it.object - curr, b = next(it), next(it) + _curr, b = next(it), next(it) # give iterator back in original state it.decrement() return (b.point - a.point).orthogonal().normalized() @@ -229,8 +229,6 @@ def simplifyDouglasPeucker(points, tolerance): first_stack = [] last_stack = [] - new_points = [] - markers[first] = 1 markers[last] = 1 diff --git a/release/scripts/freestyle/modules/parameter_editor.py b/release/scripts/freestyle/modules/parameter_editor.py index 5940f4f5cc8..d07532dfe54 100644 --- a/release/scripts/freestyle/modules/parameter_editor.py +++ b/release/scripts/freestyle/modules/parameter_editor.py @@ -1028,7 +1028,7 @@ class DashedLineShader(StrokeShader): it = stroke.stroke_vertices_begin(sampling) pattern_cycle = cycle(self.pattern) pattern = next(pattern_cycle) - for svert in it: + for _svert in it: pos = it.t # curvilinear abscissa if pos - start + sampling > pattern: diff --git a/release/scripts/modules/bl_console_utils/__init__.py b/release/scripts/modules/bl_console_utils/__init__.py index 78800fa5703..ab95fe1e68a 100644 --- a/release/scripts/modules/bl_console_utils/__init__.py +++ b/release/scripts/modules/bl_console_utils/__init__.py @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-or-later """ -Utilities relating to text mode console interations. +Utilities relating to text mode console interactions. """ diff --git a/release/scripts/modules/blend_render_info.py b/release/scripts/modules/blend_render_info.py index 4ba818a19e5..37c5f6dd3ba 100755 --- a/release/scripts/modules/blend_render_info.py +++ b/release/scripts/modules/blend_render_info.py @@ -85,7 +85,13 @@ def _read_blend_rend_chunk_from_file(blendfile, filepath): sizeof_bhead = 24 if is_64_bit else 20 - while len(bhead_id := blendfile.read(4)) == 4: + # Should always be 4, but a malformed/corrupt file may be less. + while (bhead_id := blendfile.read(4)) != b'ENDB': + + if len(bhead_id) != 4: + sys.stderr.write("Unable to read until ENDB block (corrupt file): %s\n" % filepath) + break + sizeof_data_left = struct.unpack('>i' if is_big_endian else '<i', blendfile.read(4))[0] # 4 from the `head_id`, another 4 for the size of the BHEAD. sizeof_bhead_left = sizeof_bhead - 8 @@ -107,8 +113,12 @@ def _read_blend_rend_chunk_from_file(blendfile, filepath): scenes.append((start_frame, end_frame, scene_name)) - if sizeof_data_left != 0: + if sizeof_data_left > 0: blendfile.seek(sizeof_data_left, SEEK_CUR) + elif sizeof_data_left < 0: + # Very unlikely, but prevent attempting to further parse corrupt data. + sys.stderr.write("Error calculating next block (corrupt file): %s\n" % filepath) + break return scenes diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 77890a9800d..7d0929ef28f 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2223,6 +2223,7 @@ def km_file_browser(params): ("file.smoothscroll", {"type": 'TIMER1', "value": 'ANY', "any": True}, None), ("file.bookmark_add", {"type": 'B', "value": 'PRESS', "ctrl": True}, None), ("file.start_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), + ("file.edit_directory_path", {"type": 'L', "value": 'PRESS', "ctrl": True}, None), ("file.filenum", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "repeat": True}, {"properties": [("increment", 1)]}), ("file.filenum", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "shift": True, "repeat": True}, @@ -3013,7 +3014,7 @@ def km_sequencerpreview(params): return keymap -def km_sequencer_channels(params): +def km_sequencer_channels(_params): items = [] keymap = ( "Sequencer Channels", @@ -5588,6 +5589,17 @@ def km_font(params): return keymap +# Curves edit mode. +def km_curves(params): + items = [] + keymap = ( + "Curves", + {"space_type": 'EMPTY', "region_type": 'WINDOW'}, + {"items": items}, + ) + + return keymap + def km_sculpt_curves(params): items = [] @@ -8013,6 +8025,7 @@ def generate_keymaps(params=None): km_lattice(params), km_particle(params), km_font(params), + km_curves(params), km_sculpt_curves(params), km_object_non_modal(params), diff --git a/release/scripts/startup/bl_operators/node.py b/release/scripts/startup/bl_operators/node.py index a99a81e0c63..bad12ff4621 100644 --- a/release/scripts/startup/bl_operators/node.py +++ b/release/scripts/startup/bl_operators/node.py @@ -130,7 +130,7 @@ class NodeAddOperator: return result @classmethod - def description(cls, context, properties): + def description(cls, _context, properties): nodetype = properties["type"] bl_rna = bpy.types.Node.bl_rna_get_subclass(nodetype) if bl_rna is not None: diff --git a/release/scripts/startup/bl_operators/object.py b/release/scripts/startup/bl_operators/object.py index 60f534b3ab6..6e08d557353 100644 --- a/release/scripts/startup/bl_operators/object.py +++ b/release/scripts/startup/bl_operators/object.py @@ -596,6 +596,8 @@ class MakeDupliFace(Operator): for obj in context.selected_objects: if obj.type == 'MESH': linked[obj.data].append(obj) + elif obj.type == 'EMPTY' and obj.instance_type == 'COLLECTION' and obj.instance_collection: + linked[obj.instance_collection].append(obj) for data, objects in linked.items(): face_verts = [axis for obj in objects @@ -621,7 +623,12 @@ class MakeDupliFace(Operator): ob_new = bpy.data.objects.new(mesh.name, mesh) context.collection.objects.link(ob_new) - ob_inst = bpy.data.objects.new(data.name, data) + if type(data) is bpy.types.Collection: + ob_inst = bpy.data.objects.new(data.name, None) + ob_inst.instance_type = 'COLLECTION' + ob_inst.instance_collection = data + else: + ob_inst = bpy.data.objects.new(data.name, data) context.collection.objects.link(ob_inst) ob_new.instance_type = 'FACES' diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 0eaada7249b..50fc6bad720 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -1352,7 +1352,7 @@ class WM_OT_properties_edit(Operator): items=rna_custom_property_type_items, ) is_overridable_library: BoolProperty( - name="Is Library Overridable", + name="Library Overridable", description="Allow the property to be overridden when the data-block is linked", default=False, ) @@ -1363,7 +1363,7 @@ class WM_OT_properties_edit(Operator): # Shared for integer and string properties. use_soft_limits: BoolProperty( - name="Use Soft Limits", + name="Soft Limits", description=( "Limits the Property Value slider to a range, " "values outside the range must be inputted numerically" diff --git a/release/scripts/startup/bl_ui/properties_data_curves.py b/release/scripts/startup/bl_ui/properties_data_curves.py index ed7f6e3697c..4a11c5edde6 100644 --- a/release/scripts/startup/bl_ui/properties_data_curves.py +++ b/release/scripts/startup/bl_ui/properties_data_curves.py @@ -77,12 +77,12 @@ class CURVES_MT_add_attribute(Menu): class CURVES_UL_attributes(UIList): - def filter_items(self, context, data, property): + def filter_items(self, _context, data, property): attributes = getattr(data, property) flags = [] indices = [i for i in range(len(attributes))] - for index, item in enumerate(attributes): + for item in attributes: flags.append(self.bitflag_filter_item if item.is_internal else 0) return flags, indices diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py index 6feddfe4d3b..0b043905713 100644 --- a/release/scripts/startup/bl_ui/properties_data_mesh.py +++ b/release/scripts/startup/bl_ui/properties_data_mesh.py @@ -70,7 +70,7 @@ class MESH_MT_color_attribute_context_menu(Menu): def draw(self, _context): layout = self.layout - props = layout.operator( + layout.operator( "geometry.color_attribute_duplicate", icon='DUPLICATE', ) @@ -507,12 +507,12 @@ class MESH_UL_attributes(UIList): 'CORNER': "Face Corner", } - def filter_items(self, context, data, property): + def filter_items(self, _context, data, property): attributes = getattr(data, property) flags = [] indices = [i for i in range(len(attributes))] - for index, item in enumerate(attributes): + for item in attributes: flags.append(self.bitflag_filter_item if item.is_internal else 0) return flags, indices @@ -596,7 +596,7 @@ class ColorAttributesListBase(): 'CORNER': "Face Corner", } - def filter_items(self, context, data, property): + def filter_items(self, _context, data, property): attrs = getattr(data, property) ret = [] idxs = [] diff --git a/release/scripts/startup/bl_ui/properties_data_pointcloud.py b/release/scripts/startup/bl_ui/properties_data_pointcloud.py index b492a87c741..8ef6ad63bba 100644 --- a/release/scripts/startup/bl_ui/properties_data_pointcloud.py +++ b/release/scripts/startup/bl_ui/properties_data_pointcloud.py @@ -65,12 +65,12 @@ class POINTCLOUD_MT_add_attribute(Menu): class POINTCLOUD_UL_attributes(UIList): - def filter_items(self, context, data, property): + def filter_items(self, _context, data, property): attributes = getattr(data, property) flags = [] indices = [i for i in range(len(attributes))] - for index, item in enumerate(attributes): + for item in attributes: flags.append(self.bitflag_filter_item if item.is_internal else 0) return flags, indices diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 5a9a8882bd9..f0034a3d710 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -1316,7 +1316,11 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False) def brush_basic_gpencil_sculpt_settings(layout, _context, brush, *, compact=False): + if brush is None: + return gp_settings = brush.gpencil_settings + if gp_settings is None: + return tool = brush.gpencil_sculpt_tool row = layout.row(align=True) diff --git a/release/scripts/startup/bl_ui/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py index d8d387036ff..a806e9e0c33 100644 --- a/release/scripts/startup/bl_ui/space_clip.py +++ b/release/scripts/startup/bl_ui/space_clip.py @@ -199,7 +199,8 @@ class CLIP_HT_header(Header): row = layout.row(align=True) row.prop(dopesheet, "sort_method", text="") row.prop(dopesheet, "use_invert_sort", - text="Invert", toggle=True) + text="", toggle=True, + icon='SORT_DESC' if dopesheet.use_invert_sort else 'SORT_ASC') def _draw_masking(self, context): layout = self.layout diff --git a/release/scripts/startup/bl_ui/space_nla.py b/release/scripts/startup/bl_ui/space_nla.py index d6f26880d20..5157a215f34 100644 --- a/release/scripts/startup/bl_ui/space_nla.py +++ b/release/scripts/startup/bl_ui/space_nla.py @@ -151,7 +151,7 @@ class NLA_MT_marker(Menu): class NLA_MT_marker_select(Menu): bl_label = 'Select' - def draw(self, context): + def draw(self, _context): layout = self.layout layout.operator("marker.select_all", text="All").action = 'SELECT' diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 84725d9e781..2db30ac1450 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -2087,10 +2087,9 @@ class SEQUENCER_PT_adjust_transform(SequencerButtonsPanel, Panel): col = layout.column(align=True) col.prop(strip.transform, "origin") - row = layout.row(heading="Mirror") - sub = row.row(align=True) - sub.prop(strip, "use_flip_x", text="X", toggle=True) - sub.prop(strip, "use_flip_y", text="Y", toggle=True) + col = layout.column(heading="Mirror", align=True) + col.prop(strip, "use_flip_x", text="X", toggle=True) + col.prop(strip, "use_flip_y", text="Y", toggle=True) class SEQUENCER_PT_adjust_video(SequencerButtonsPanel, Panel): diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 191066df89f..5070bd04142 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -1196,6 +1196,8 @@ class ThemeGenericClassGenerator: ("Scroll Bar", "wcol_scroll"), ("Progress Bar", "wcol_progress"), ("List Item", "wcol_list_item"), + # Not used yet, so hide this from the UI. + # ("Data-View Item", "wcol_view_item"), ("Tab", "wcol_tab"), ] diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 80ea33f8a75..5e5e7a79035 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -95,7 +95,7 @@ class VIEW3D_HT_tool_header(Header): elif tool_mode == 'PAINT_GPENCIL': if is_valid_context: brush = context.tool_settings.gpencil_paint.brush - if brush.gpencil_tool != 'ERASE': + if brush and brush.gpencil_tool != 'ERASE': if brush.gpencil_tool != 'TINT': layout.popover("VIEW3D_PT_tools_grease_pencil_brush_advanced") @@ -106,10 +106,11 @@ class VIEW3D_HT_tool_header(Header): elif tool_mode == 'SCULPT_GPENCIL': if is_valid_context: brush = context.tool_settings.gpencil_sculpt_paint.brush - tool = brush.gpencil_sculpt_tool - if tool != 'CLONE': - layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_brush_popover") - layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_appearance") + if brush: + tool = brush.gpencil_sculpt_tool + if tool != 'CLONE': + layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_brush_popover") + layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_appearance") elif tool_mode == 'WEIGHT_GPENCIL': if is_valid_context: layout.popover("VIEW3D_PT_tools_grease_pencil_weight_appearance") diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h index 5755ddb3018..13eefd27bec 100644 --- a/source/blender/blenkernel/BKE_attribute.h +++ b/source/blender/blenkernel/BKE_attribute.h @@ -64,7 +64,7 @@ bool BKE_id_attribute_remove(struct ID *id, const char *name, struct ReportList * Creates a duplicate attribute layer. */ struct CustomDataLayer *BKE_id_attribute_duplicate(struct ID *id, - struct CustomDataLayer *layer, + const char *name, struct ReportList *reports); struct CustomDataLayer *BKE_id_attribute_find(const struct ID *id, @@ -72,7 +72,7 @@ struct CustomDataLayer *BKE_id_attribute_find(const struct ID *id, int type, eAttrDomain domain); -struct CustomDataLayer *BKE_id_attribute_search(const struct ID *id, +struct CustomDataLayer *BKE_id_attribute_search(struct ID *id, const char *name, eCustomDataMask type, eAttrDomainMask domain_mask); diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h index 41d369ae9b2..feb3dc7de80 100644 --- a/source/blender/blenkernel/BKE_collection.h +++ b/source/blender/blenkernel/BKE_collection.h @@ -119,7 +119,7 @@ bool BKE_collection_object_add(struct Main *bmain, /** * Add object to given collection, similar to #BKE_collection_object_add. * - * However, it additionnally ensures that the selected collection is also part of the given + * However, it additionally ensures that the selected collection is also part of the given * `view_layer`, if non-NULL. Otherwise, the object is not added to any collection. */ bool BKE_collection_viewlayer_object_add(struct Main *bmain, diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index 86630a6ab44..2e28b3c00ee 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -70,6 +70,19 @@ bool BKE_lib_override_library_is_user_edited(const struct ID *id); bool BKE_lib_override_library_is_system_defined(const struct Main *bmain, const struct ID *id); /** + * Check if given Override Property for given ID is animated (through a F-Curve in an Action, or + * from a driver). + * + * \param override_rna_prop if not NULL, the RNA property matching the given path in the + * `override_prop`. + * \param rnaprop_index Array in the RNA property, 0 if unknown or irrelevant. + */ +bool BKE_lib_override_library_property_is_animated(const ID *id, + const IDOverrideLibraryProperty *override_prop, + const struct PropertyRNA *override_rna_prop, + const int rnaprop_index); + +/** * Check if given ID is a leaf in its liboverride hierarchy (i.e. if it does not use any other * override ID). * @@ -281,11 +294,14 @@ void BKE_lib_override_library_property_delete(struct IDOverrideLibrary *override * * \param idpoin: Pointer to the override ID. * \param library_prop: The library override property to find the matching RNA property for. + * \param r_index: The RNA array flat index (i.e. flattened index in case of multi-dimensional + * array properties). See #RNA_path_resolve_full family of functions for details. */ bool BKE_lib_override_rna_property_find(struct PointerRNA *idpoin, const struct IDOverrideLibraryProperty *library_prop, struct PointerRNA *r_override_poin, - struct PropertyRNA **r_override_prop); + struct PropertyRNA **r_override_prop, + int *r_index); /** * Find override property operation from given sub-item(s), if it exists. diff --git a/source/blender/blenkernel/BKE_mesh_boolean_convert.hh b/source/blender/blenkernel/BKE_mesh_boolean_convert.hh index abaf2ab0178..441783d46a1 100644 --- a/source/blender/blenkernel/BKE_mesh_boolean_convert.hh +++ b/source/blender/blenkernel/BKE_mesh_boolean_convert.hh @@ -23,6 +23,8 @@ namespace blender::meshintersect { * \param material_remaps: An array of maps from material slot numbers in the corresponding mesh * to the material slot in the first mesh. It is OK for material_remaps or any of its constituent * arrays to be empty. + * \param r_intersecting_edges: Array to store indices of edges on the resulting mesh in. These + * 'new' edges are the result of the intersections. */ Mesh *direct_mesh_boolean(Span<const Mesh *> meshes, Span<const float4x4 *> transforms, @@ -30,6 +32,7 @@ Mesh *direct_mesh_boolean(Span<const Mesh *> meshes, Span<Array<short>> material_remaps, bool use_self, bool hole_tolerant, - int boolean_mode); + int boolean_mode, + Vector<int> *r_intersecting_edges); } // namespace blender::meshintersect diff --git a/source/blender/blenkernel/BKE_subdiv_modifier.h b/source/blender/blenkernel/BKE_subdiv_modifier.h index 4ad17610207..271026d4253 100644 --- a/source/blender/blenkernel/BKE_subdiv_modifier.h +++ b/source/blender/blenkernel/BKE_subdiv_modifier.h @@ -7,6 +7,8 @@ #pragma once +#include "BKE_subdiv.h" + #include "BLI_sys_types.h" #ifdef __cplusplus @@ -24,9 +26,30 @@ struct Subdiv; struct SubdivSettings; struct SubsurfModifierData; -void BKE_subsurf_modifier_subdiv_settings_init(struct SubdivSettings *settings, - const struct SubsurfModifierData *smd, - bool use_render_params); +/* Runtime subsurf modifier data, cached in modifier on evaluated meshes. */ +typedef struct SubsurfRuntimeData { + /* Subdivision settings, exists before descriptor or mesh wrapper is created. */ + SubdivSettings settings; + + /* Cached subdivision surface descriptor, with topology and settings. */ + struct Subdiv *subdiv; + bool set_by_draw_code; + + /* Cached mesh wrapper data, to be used for GPU subdiv or lazy evaluation on CPU. */ + bool has_gpu_subdiv; + int resolution; + bool use_optimal_display; + bool calc_loop_normals; + bool use_loop_normals; + + /* Cached from the draw code for stats display. */ + int stats_totvert; + int stats_totedge; + int stats_totpoly; + int stats_totloop; +} SubsurfRuntimeData; + +bool BKE_subsurf_modifier_runtime_init(struct SubsurfModifierData *smd, bool use_render_params); bool BKE_subsurf_modifier_use_custom_loop_normals(const struct SubsurfModifierData *smd, const struct Mesh *mesh); @@ -57,12 +80,7 @@ extern void (*BKE_subsurf_modifier_free_gpu_cache_cb)(struct Subdiv *subdiv); * which matches settings and topology. */ struct Subdiv *BKE_subsurf_modifier_subdiv_descriptor_ensure( - const struct SubsurfModifierData *smd, - const struct SubdivSettings *subdiv_settings, - const struct Mesh *mesh, - bool for_draw_code); - -struct SubsurfRuntimeData *BKE_subsurf_modifier_ensure_runtime(struct SubsurfModifierData *smd); + struct SubsurfRuntimeData *runtime_data, const struct Mesh *mesh, bool for_draw_code); /** * Return the #ModifierMode required for the evaluation of the subsurf modifier, diff --git a/source/blender/blenkernel/BKE_unit.h b/source/blender/blenkernel/BKE_unit.h index 823d32f83ba..f051335fc41 100644 --- a/source/blender/blenkernel/BKE_unit.h +++ b/source/blender/blenkernel/BKE_unit.h @@ -19,6 +19,10 @@ struct UnitSettings; */ size_t BKE_unit_value_as_string_adaptive( char *str, int len_max, double value, int prec, int system, int type, bool split, bool pad); +/** + * Representation of a value in units. Negative precision is used to disable stripping of zeroes. + * This reduces text jumping when changing values. + */ size_t BKE_unit_value_as_string(char *str, int len_max, double value, diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index 8496692ceb3..b3a9d894944 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -52,6 +52,7 @@ #include "BKE_object.h" #include "BKE_object_deform.h" #include "BKE_paint.h" +#include "BKE_subdiv_modifier.h" #include "BLI_sys_types.h" /* for intptr_t support */ @@ -598,10 +599,10 @@ static bool mesh_has_modifier_final_normals(const Mesh *mesh_input, /* Test if mesh has the required loop normals, in case an additional modifier * evaluation from another instance or from an operator requests it but the * initial normals were not loop normals. */ - const bool do_loop_normals = ((mesh_input->flag & ME_AUTOSMOOTH) != 0 || - (final_datamask->lmask & CD_MASK_NORMAL) != 0); + const bool calc_loop_normals = ((mesh_input->flag & ME_AUTOSMOOTH) != 0 || + (final_datamask->lmask & CD_MASK_NORMAL) != 0); - return (!do_loop_normals || CustomData_has_layer(&mesh_final->ldata, CD_NORMAL)); + return (!calc_loop_normals || CustomData_has_layer(&mesh_final->ldata, CD_NORMAL)); } static void mesh_calc_modifier_final_normals(const Mesh *mesh_input, @@ -610,16 +611,19 @@ static void mesh_calc_modifier_final_normals(const Mesh *mesh_input, Mesh *mesh_final) { /* Compute normals. */ - const bool do_loop_normals = ((mesh_input->flag & ME_AUTOSMOOTH) != 0 || - (final_datamask->lmask & CD_MASK_NORMAL) != 0); + const bool calc_loop_normals = ((mesh_input->flag & ME_AUTOSMOOTH) != 0 || + (final_datamask->lmask & CD_MASK_NORMAL) != 0); /* Needed as `final_datamask` is not preserved outside modifier stack evaluation. */ - mesh_final->runtime.subsurf_do_loop_normals = do_loop_normals; + SubsurfRuntimeData *subsurf_runtime_data = mesh_final->runtime.subsurf_runtime_data; + if (subsurf_runtime_data) { + subsurf_runtime_data->calc_loop_normals = calc_loop_normals; + } - if (do_loop_normals) { + if (calc_loop_normals) { /* Compute loop normals (NOTE: will compute poly and vert normals as well, if needed!). In case * of deferred CPU subdivision, this will be computed when the wrapper is generated. */ - if (mesh_final->runtime.subsurf_resolution == 0) { + if (!subsurf_runtime_data || subsurf_runtime_data->resolution == 0) { BKE_mesh_calc_normals_split(mesh_final); } } @@ -1266,15 +1270,18 @@ static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final, return; } - const bool do_loop_normals = ((mesh_final->flag & ME_AUTOSMOOTH) != 0 || - (final_datamask->lmask & CD_MASK_NORMAL) != 0); + const bool calc_loop_normals = ((mesh_final->flag & ME_AUTOSMOOTH) != 0 || + (final_datamask->lmask & CD_MASK_NORMAL) != 0); - mesh_final->runtime.subsurf_do_loop_normals = do_loop_normals; + SubsurfRuntimeData *subsurf_runtime_data = mesh_final->runtime.subsurf_runtime_data; + if (subsurf_runtime_data) { + subsurf_runtime_data->calc_loop_normals = calc_loop_normals; + } - if (do_loop_normals) { + if (calc_loop_normals) { /* Compute loop normals. In case of deferred CPU subdivision, this will be computed when the * wrapper is generated. */ - if (mesh_final->runtime.subsurf_resolution == 0) { + if (!subsurf_runtime_data || subsurf_runtime_data->resolution == 0) { BKE_mesh_calc_normals_split(mesh_final); } } diff --git a/source/blender/blenkernel/intern/attribute.cc b/source/blender/blenkernel/intern/attribute.cc index b2ea8b833b3..7c09b4a4ce3 100644 --- a/source/blender/blenkernel/intern/attribute.cc +++ b/source/blender/blenkernel/intern/attribute.cc @@ -208,53 +208,36 @@ CustomDataLayer *BKE_id_attribute_new( return (index == -1) ? nullptr : &(customdata->layers[index]); } -CustomDataLayer *BKE_id_attribute_duplicate(ID *id, CustomDataLayer *layer, ReportList *reports) +CustomDataLayer *BKE_id_attribute_duplicate(ID *id, const char *name, ReportList *reports) { - DomainInfo info[ATTR_DOMAIN_NUM]; - get_domains(id, info); - - eCustomDataType type = (eCustomDataType)layer->type; - eAttrDomain domain = BKE_id_attribute_domain(id, layer); - - CustomData *customdata = info[domain].customdata; - if (customdata == nullptr) { - BKE_report(reports, RPT_ERROR, "Attribute domain not supported by this geometry type"); + const CustomDataLayer *src_layer = BKE_id_attribute_search( + id, name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL); + if (src_layer == nullptr) { + BKE_report(reports, RPT_ERROR, "Attribute is not part of this geometry"); return nullptr; } - char name[MAX_CUSTOMDATA_LAYER_NAME]; - char uniquename[MAX_CUSTOMDATA_LAYER_NAME]; + const eCustomDataType type = (eCustomDataType)src_layer->type; + const eAttrDomain domain = BKE_id_attribute_domain(id, src_layer); /* Make a copy of name in case CustomData API reallocates the layers. */ - BLI_strncpy(name, layer->name, MAX_CUSTOMDATA_LAYER_NAME); - BKE_id_attribute_calc_unique_name(id, layer->name, uniquename); + const std::string name_copy = name; - switch (GS(id->name)) { - case ID_ME: { - Mesh *me = (Mesh *)id; - BMEditMesh *em = me->edit_mesh; - if (em != nullptr) { - BM_data_layer_add_named(em->bm, customdata, type, uniquename); - } - else { - CustomData_add_layer_named( - customdata, type, CD_DEFAULT, nullptr, info[domain].length, uniquename); - } - break; - } - default: { - CustomData_add_layer_named( - customdata, type, CD_DEFAULT, nullptr, info[domain].length, uniquename); - break; - } + DomainInfo info[ATTR_DOMAIN_NUM]; + get_domains(id, info); + CustomData *customdata = info[domain].customdata; + + CustomDataLayer *new_layer = BKE_id_attribute_new(id, name_copy.c_str(), type, domain, reports); + if (new_layer == nullptr) { + return nullptr; } - int from_index = CustomData_get_named_layer_index(customdata, type, name); - int to_index = CustomData_get_named_layer_index(customdata, type, uniquename); + const int from_index = CustomData_get_named_layer_index(customdata, type, name_copy.c_str()); + const int to_index = CustomData_get_named_layer_index(customdata, type, new_layer->name); CustomData_copy_data_layer( customdata, customdata, from_index, to_index, 0, 0, info[domain].length); - return (to_index == -1) ? nullptr : &(customdata->layers[to_index]); + return new_layer; } bool BKE_id_attribute_remove(ID *id, const char *name, ReportList *reports) @@ -317,7 +300,7 @@ CustomDataLayer *BKE_id_attribute_find(const ID *id, return nullptr; } -CustomDataLayer *BKE_id_attribute_search(const ID *id, +CustomDataLayer *BKE_id_attribute_search(ID *id, const char *name, const eCustomDataMask type_mask, const eAttrDomainMask domain_mask) diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 48f2d66c1cd..e5bb70f8cda 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -2024,13 +2024,13 @@ static Mesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, Object * settings.use_threading = (sData->total_points > 1000); BLI_task_parallel_range( 0, sData->total_points, &data, dynamic_paint_apply_surface_wave_cb, &settings); - BKE_mesh_normals_tag_dirty(mesh); + BKE_mesh_normals_tag_dirty(result); } /* displace */ if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) { dynamicPaint_applySurfaceDisplace(surface, result); - BKE_mesh_normals_tag_dirty(mesh); + BKE_mesh_normals_tag_dirty(result); } } } diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index d3c3f41779a..2d6e0e05a97 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -32,9 +32,7 @@ static void add_final_mesh_as_geometry_component(const Object &object, GeometryS if (mesh != nullptr) { BKE_mesh_wrapper_ensure_mdata(mesh); - - MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); - mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly); + geometry_set.replace_mesh(mesh, GeometryOwnershipType::ReadOnly); } } diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index faafd1e1040..0bf5418ea8f 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -647,32 +647,38 @@ static bGPdata *gpencil_copy_structure_for_eval(bGPdata *gpd) return gpd_eval; } -static void copy_frame_to_eval_cb(bGPDlayer *UNUSED(gpl), +static void copy_frame_to_eval_ex(bGPDframe *gpf_orig, bGPDframe *gpf_eval) +{ + /* Free any existing eval stroke data. This happens in case we have a single user on the data + * block and the strokes have not been deleted. */ + if (!BLI_listbase_is_empty(&gpf_eval->strokes)) { + BKE_gpencil_free_strokes(gpf_eval); + } + /* Copy strokes to eval frame and update internal orig pointers. */ + BKE_gpencil_frame_copy_strokes(gpf_orig, gpf_eval); + BKE_gpencil_frame_original_pointers_update(gpf_orig, gpf_eval); +} + +static void copy_frame_to_eval_cb(bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *UNUSED(gps), void *UNUSED(thunk)) { - /* Early return when callback is not provided with a frame. */ - if (gpf == NULL) { + /* Early return when callback: + * - Is not provided with a frame. + * - When the frame is the layer's active frame (already handled in + * gpencil_copy_visible_frames_to_eval). + */ + if (gpf == NULL || gpf == gpl->actframe) { return; } - /* Free any existing eval stroke data. This happens in case we have a single user on the data - * block and the strokes have not been deleted. */ - if (!BLI_listbase_is_empty(&gpf->strokes)) { - BKE_gpencil_free_strokes(gpf); - } - - /* Get original frame. */ - bGPDframe *gpf_orig = gpf->runtime.gpf_orig; - /* Copy strokes to eval frame and update internal orig pointers. */ - BKE_gpencil_frame_copy_strokes(gpf_orig, gpf); - BKE_gpencil_frame_original_pointers_update(gpf_orig, gpf); + copy_frame_to_eval_ex(gpf->runtime.gpf_orig, gpf); } static void gpencil_copy_visible_frames_to_eval(Depsgraph *depsgraph, Scene *scene, Object *ob) { - /* Remap layers' active frame with time modifiers applied. */ + /* Remap layers active frame with time modifiers applied. */ bGPdata *gpd_eval = ob->data; LISTBASE_FOREACH (bGPDlayer *, gpl_eval, &gpd_eval->layers) { bGPDframe *gpf_eval = gpl_eval->actframe; @@ -680,9 +686,14 @@ static void gpencil_copy_visible_frames_to_eval(Depsgraph *depsgraph, Scene *sce if (gpf_eval == NULL || gpf_eval->framenum != remap_cfra) { gpl_eval->actframe = BKE_gpencil_layer_frame_get(gpl_eval, remap_cfra, GP_GETFRAME_USE_PREV); } + /* Always copy active frame to eval, because the modifiers always evaluate the active frame, + * even if it's not visible (e.g. the layer is hidden).*/ + if (gpl_eval->actframe != NULL) { + copy_frame_to_eval_ex(gpl_eval->actframe->runtime.gpf_orig, gpl_eval->actframe); + } } - /* Copy only visible frames to evaluated version. */ + /* Copy visible frames that are not the active one to evaluated version. */ BKE_gpencil_visible_stroke_advanced_iter( NULL, ob, copy_frame_to_eval_cb, NULL, NULL, true, scene->r.cfra); } diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index 06a5d877882..0c1f01c3796 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -1730,7 +1730,7 @@ static void stampdata_from_template(StampData *stamp_data, stamp_data->file[0] = '\0'; } if (scene->r.stamp & R_STAMP_NOTE) { - SNPRINTF(stamp_data->note, "%s", stamp_data_template->note); + STRNCPY(stamp_data->note, stamp_data_template->note); } else { stamp_data->note[0] = '\0'; @@ -3895,7 +3895,7 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) } if (BKE_image_is_stereo(ima) && ima->views_format == R_IMF_VIEWS_STEREO_3D) { - IMB_ImBufFromStereo3d(ima->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]); + IMB_ImBufFromStereo3d(ima->stereo3d_format, ibuf_arr[0], ibuf_arr.data(), &ibuf_arr[1]); } for (int i = 0; i < totviews; i++) { @@ -4064,7 +4064,7 @@ static ImBuf *image_load_image_file( /* multi-views/multi-layers OpenEXR files directly populate ima, and return null ibuf... */ if (BKE_image_is_stereo(ima) && ima->views_format == R_IMF_VIEWS_STEREO_3D && ibuf_arr[0] && tot_viewfiles == 1 && totviews >= 2) { - IMB_ImBufFromStereo3d(ima->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]); + IMB_ImBufFromStereo3d(ima->stereo3d_format, ibuf_arr[0], ibuf_arr.data(), &ibuf_arr[1]); } /* return the original requested ImBuf */ diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc index aa43ca94c99..22012662180 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -21,8 +21,10 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" +#include "BKE_anim_data.h" #include "BKE_armature.h" #include "BKE_collection.h" +#include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_idtype.h" #include "BKE_key.h" @@ -312,7 +314,6 @@ bool BKE_lib_override_library_is_user_edited(const ID *id) bool BKE_lib_override_library_is_system_defined(const Main *bmain, const ID *id) { - if (ID_IS_OVERRIDE_LIBRARY(id)) { const ID *override_owner_id; lib_override_get(bmain, id, &override_owner_id); @@ -322,6 +323,34 @@ bool BKE_lib_override_library_is_system_defined(const Main *bmain, const ID *id) return false; } +bool BKE_lib_override_library_property_is_animated(const ID *id, + const IDOverrideLibraryProperty *override_prop, + const PropertyRNA *override_rna_prop, + const int rnaprop_index) +{ + AnimData *anim_data = BKE_animdata_from_id(id); + if (anim_data != nullptr) { + struct FCurve *fcurve; + char *index_token_start = const_cast<char *>( + RNA_path_array_index_token_find(override_prop->rna_path, override_rna_prop)); + if (index_token_start != nullptr) { + const char index_token_start_backup = *index_token_start; + *index_token_start = '\0'; + fcurve = BKE_animadata_fcurve_find_by_rna_path( + anim_data, override_prop->rna_path, rnaprop_index, nullptr, nullptr); + *index_token_start = index_token_start_backup; + } + else { + fcurve = BKE_animadata_fcurve_find_by_rna_path( + anim_data, override_prop->rna_path, 0, nullptr, nullptr); + } + if (fcurve != nullptr) { + return true; + } + } + return false; +} + static int foreachid_is_hierarchy_leaf_fn(LibraryIDLinkCallbackData *cb_data) { ID *id_owner = cb_data->id_owner; @@ -2677,11 +2706,12 @@ IDOverrideLibraryProperty *BKE_lib_override_library_property_get(IDOverrideLibra bool BKE_lib_override_rna_property_find(PointerRNA *idpoin, const IDOverrideLibraryProperty *library_prop, PointerRNA *r_override_poin, - PropertyRNA **r_override_prop) + PropertyRNA **r_override_prop, + int *r_index) { BLI_assert(RNA_struct_is_ID(idpoin->type) && ID_IS_OVERRIDE_LIBRARY(idpoin->data)); - return RNA_path_resolve_property( - idpoin, library_prop->rna_path, r_override_poin, r_override_prop); + return RNA_path_resolve_property_full( + idpoin, library_prop->rna_path, r_override_poin, r_override_prop, r_index); } void lib_override_library_property_copy(IDOverrideLibraryProperty *op_dst, diff --git a/source/blender/blenkernel/intern/mesh_boolean_convert.cc b/source/blender/blenkernel/intern/mesh_boolean_convert.cc index 14bd6aa5b2f..a1ef2d2e6b5 100644 --- a/source/blender/blenkernel/intern/mesh_boolean_convert.cc +++ b/source/blender/blenkernel/intern/mesh_boolean_convert.cc @@ -791,7 +791,8 @@ Mesh *direct_mesh_boolean(Span<const Mesh *> meshes, Span<Array<short>> material_remaps, const bool use_self, const bool hole_tolerant, - const int boolean_mode) + const int boolean_mode, + Vector<int> *r_intersecting_edges) { #ifdef WITH_GMP BLI_assert(meshes.size() == transforms.size()); @@ -828,7 +829,23 @@ Mesh *direct_mesh_boolean(Span<const Mesh *> meshes, write_obj_mesh(m_out, "m_out"); } - return imesh_to_mesh(&m_out, mim); + Mesh *result = imesh_to_mesh(&m_out, mim); + + /* Store intersecting edge indices. */ + if (r_intersecting_edges != nullptr) { + for (int fi : m_out.face_index_range()) { + const Face &face = *m_out.face(fi); + const MPoly &poly = result->mpoly[fi]; + for (int corner_i : face.index_range()) { + if (face.is_intersect[corner_i]) { + int e_index = result->mloop[poly.loopstart + corner_i].e; + r_intersecting_edges->append(e_index); + } + } + } + } + + return result; #else // WITH_GMP UNUSED_VARS(meshes, transforms, @@ -836,7 +853,8 @@ Mesh *direct_mesh_boolean(Span<const Mesh *> meshes, target_transform, use_self, hole_tolerant, - boolean_mode); + boolean_mode, + r_intersecting_edges); return nullptr; #endif // WITH_GMP } diff --git a/source/blender/blenkernel/intern/mesh_calc_edges.cc b/source/blender/blenkernel/intern/mesh_calc_edges.cc index 5895eb7fd71..31e20750cf2 100644 --- a/source/blender/blenkernel/intern/mesh_calc_edges.cc +++ b/source/blender/blenkernel/intern/mesh_calc_edges.cc @@ -81,7 +81,7 @@ static void add_existing_edges_to_hash_maps(Mesh *mesh, { /* Assume existing edges are valid. */ threading::parallel_for_each(edge_maps, [&](EdgeMap &edge_map) { - const int task_index = &edge_map - &edge_maps[0]; + const int task_index = &edge_map - edge_maps.data(); for (const MEdge &edge : Span(mesh->medge, mesh->totedge)) { OrderedEdge ordered_edge{edge.v1, edge.v2}; /* Only add the edge when it belongs into this map. */ @@ -98,7 +98,7 @@ static void add_polygon_edges_to_hash_maps(Mesh *mesh, { const Span<MLoop> loops{mesh->mloop, mesh->totloop}; threading::parallel_for_each(edge_maps, [&](EdgeMap &edge_map) { - const int task_index = &edge_map - &edge_maps[0]; + const int task_index = &edge_map - edge_maps.data(); for (const MPoly &poly : Span(mesh->mpoly, mesh->totpoly)) { Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); const MLoop *prev_loop = &poly_loops.last(); @@ -131,7 +131,7 @@ static void serialize_and_initialize_deduplicated_edges(MutableSpan<EdgeMap> edg } threading::parallel_for_each(edge_maps, [&](EdgeMap &edge_map) { - const int task_index = &edge_map - &edge_maps[0]; + const int task_index = &edge_map - edge_maps.data(); int new_edge_index = edge_index_offsets[task_index]; for (EdgeMap::MutableItem item : edge_map.items()) { diff --git a/source/blender/blenkernel/intern/mesh_wrapper.cc b/source/blender/blenkernel/intern/mesh_wrapper.cc index c505a74724b..fdebf1d6a26 100644 --- a/source/blender/blenkernel/intern/mesh_wrapper.cc +++ b/source/blender/blenkernel/intern/mesh_wrapper.cc @@ -314,28 +314,23 @@ static Mesh *mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me) return me; } + SubsurfRuntimeData *runtime_data = (SubsurfRuntimeData *)smd->modifier.runtime; + if (runtime_data == nullptr || runtime_data->settings.level == 0) { + return me; + } + /* Initialize the settings before ensuring the descriptor as this is checked to decide whether * subdivision is needed at all, and checking the descriptor status might involve checking if the * data is out-of-date, which is a very expensive operation. */ SubdivToMeshSettings mesh_settings; - mesh_settings.resolution = me->runtime.subsurf_resolution; - mesh_settings.use_optimal_display = me->runtime.subsurf_use_optimal_display; + mesh_settings.resolution = runtime_data->resolution; + mesh_settings.use_optimal_display = runtime_data->use_optimal_display; if (mesh_settings.resolution < 3) { return me; } - const bool apply_render = me->runtime.subsurf_apply_render; - - SubdivSettings subdiv_settings; - BKE_subsurf_modifier_subdiv_settings_init(&subdiv_settings, smd, apply_render); - if (subdiv_settings.level == 0) { - return me; - } - - SubsurfRuntimeData *runtime_data = BKE_subsurf_modifier_ensure_runtime(smd); - - Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure(smd, &subdiv_settings, me, false); + Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure(runtime_data, me, false); if (subdiv == nullptr) { /* Happens on bad topology, but also on empty input mesh. */ return me; @@ -358,7 +353,7 @@ static Mesh *mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me) CustomData_set_layer_flag(&me->ldata, CD_NORMAL, CD_FLAG_TEMPORARY); CustomData_set_layer_flag(&subdiv_mesh->ldata, CD_NORMAL, CD_FLAG_TEMPORARY); } - else if (me->runtime.subsurf_do_loop_normals) { + else if (runtime_data->calc_loop_normals) { BKE_mesh_calc_normals_split(subdiv_mesh); } diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index a5f6c453ed4..8f54d71108a 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -244,24 +244,42 @@ void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, const ListBase *src, const } } -static void update_active_strip_from_listbase(AnimData *adt_dest, - NlaTrack *track_dest, - const NlaStrip *active_strip, - const ListBase /* NlaStrip */ *strips_source) +/** + * Find `active_strip` in `strips_source`, then return the strip with the same + * index from `strips_dest`. + */ +static NlaStrip *find_active_strip_from_listbase(const NlaStrip *active_strip, + const ListBase /* NlaStrip */ *strips_source, + const ListBase /* NlaStrip */ *strips_dest) { - NlaStrip *strip_dest = track_dest->strips.first; + NlaStrip *strip_dest = strips_dest->first; LISTBASE_FOREACH (const NlaStrip *, strip_source, strips_source) { - if (strip_source == active_strip) { - adt_dest->actstrip = strip_dest; - return; + if (strip_dest == NULL) { + /* The tracks are assumed to have an equal number of strips, but this is not the case when + * dragging multiple strips. The transform system merges the selected strips into one + * meta-strip, reducing the number of strips in `track_dest`. */ + break; } - - if (strip_source->type == NLASTRIP_TYPE_META) { - update_active_strip_from_listbase(adt_dest, track_dest, active_strip, &strip_source->strips); + if (strip_source == active_strip) { + return strip_dest; + } + + const bool src_is_meta = strip_source->type == NLASTRIP_TYPE_META; + const bool dst_is_meta = strip_dest->type == NLASTRIP_TYPE_META; + BLI_assert_msg(src_is_meta == dst_is_meta, + "Expecting topology of source and destination strips to be equal"); + if (src_is_meta && dst_is_meta) { + NlaStrip *found_in_meta = find_active_strip_from_listbase( + active_strip, &strip_source->strips, &strip_dest->strips); + if (found_in_meta != NULL) { + return found_in_meta; + } } strip_dest = strip_dest->next; } + + return NULL; } /* Set adt_dest->actstrip to the strip with the same index as adt_source->actstrip. */ @@ -272,8 +290,9 @@ static void update_active_strip(AnimData *adt_dest, { BLI_assert(BLI_listbase_count(&track_source->strips) == BLI_listbase_count(&track_dest->strips)); - update_active_strip_from_listbase( - adt_dest, track_dest, adt_source->actstrip, &track_source->strips); + NlaStrip *active_strip = find_active_strip_from_listbase( + adt_source->actstrip, &track_source->strips, &track_dest->strips); + adt_dest->actstrip = active_strip; } /* Set adt_dest->act_track to the track with the same index as adt_source->act_track. */ diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 8c9db339753..3b20bdc826c 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -1391,8 +1391,7 @@ void pbvh_free_draw_buffers(PBVH *pbvh, PBVHNode *node) } } -static void pbvh_update_draw_buffers( - PBVH *pbvh, PBVHNode **nodes, int totnode, int update_flag, bool full_render) +static void pbvh_check_draw_layout(PBVH *pbvh) { const CustomData *vdata; const CustomData *ldata; @@ -1420,10 +1419,14 @@ static void pbvh_update_draw_buffers( break; } - const bool active_attrs_only = !full_render; - - /* rebuild all draw buffers if attribute layout changed */ - if (GPU_pbvh_attribute_names_update(pbvh->type, pbvh->vbo_id, vdata, ldata, active_attrs_only)) { + /* Rebuild all draw buffers if attribute layout changed. + * + * NOTE: The optimization where we only send active attributes + * to the GPU in workbench mode is disabled due to bugs + * (there's no guarantee there isn't another EEVEE viewport which would + * free the draw buffers and corrupt the draw cache). + */ + if (GPU_pbvh_attribute_names_update(pbvh->type, pbvh->vbo_id, vdata, ldata, false)) { /* attribute layout changed; force rebuild */ for (int i = 0; i < pbvh->totnode; i++) { PBVHNode *node = pbvh->nodes + i; @@ -1433,6 +1436,33 @@ static void pbvh_update_draw_buffers( } } } +} + +static void pbvh_update_draw_buffers(PBVH *pbvh, PBVHNode **nodes, int totnode, int update_flag) +{ + const CustomData *vdata; + + if (!pbvh->vbo_id) { + pbvh->vbo_id = GPU_pbvh_make_format(); + } + + switch (pbvh->type) { + case PBVH_BMESH: + if (!pbvh->bm) { + /* BMesh hasn't been created yet */ + return; + } + + vdata = &pbvh->bm->vdata; + break; + case PBVH_FACES: + vdata = pbvh->vdata; + break; + case PBVH_GRIDS: + vdata = NULL; + break; + } + UNUSED_VARS(vdata); if ((update_flag & PBVH_RebuildDrawBuffers) || ELEM(pbvh->type, PBVH_GRIDS, PBVH_BMESH)) { /* Free buffers uses OpenGL, so not in parallel. */ @@ -2837,10 +2867,8 @@ void BKE_pbvh_draw_cb(PBVH *pbvh, PBVHFrustumPlanes *draw_frustum, void (*draw_fn)(void *user_data, GPU_PBVH_Buffers *buffers), void *user_data, - bool full_render) + bool UNUSED(full_render)) { - pbvh->draw_cache_invalid = false; - PBVHNode **nodes; int totnode; int update_flag = 0; @@ -2862,9 +2890,11 @@ void BKE_pbvh_draw_cb(PBVH *pbvh, update_flag = PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers; } + pbvh_check_draw_layout(pbvh); + /* Update draw buffers. */ if (totnode != 0 && (update_flag & (PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers))) { - pbvh_update_draw_buffers(pbvh, nodes, totnode, update_flag, full_render); + pbvh_update_draw_buffers(pbvh, nodes, totnode, update_flag); } MEM_SAFE_FREE(nodes); diff --git a/source/blender/blenkernel/intern/subdiv_mesh.c b/source/blender/blenkernel/intern/subdiv_mesh.c index ca4130d6bc4..433bad34479 100644 --- a/source/blender/blenkernel/intern/subdiv_mesh.c +++ b/source/blender/blenkernel/intern/subdiv_mesh.c @@ -5,9 +5,6 @@ * \ingroup bke */ -#include "BKE_mesh.h" -#include "BKE_subdiv_mesh.h" - #include "atomic_ops.h" #include "DNA_key_types.h" @@ -24,6 +21,7 @@ #include "BKE_subdiv.h" #include "BKE_subdiv_eval.h" #include "BKE_subdiv_foreach.h" +#include "BKE_subdiv_mesh.h" #include "MEM_guardedalloc.h" diff --git a/source/blender/blenkernel/intern/subdiv_modifier.c b/source/blender/blenkernel/intern/subdiv_modifier.c index e43da956ce5..f5423dccc0f 100644 --- a/source/blender/blenkernel/intern/subdiv_modifier.c +++ b/source/blender/blenkernel/intern/subdiv_modifier.c @@ -3,8 +3,6 @@ #include "BKE_subdiv_modifier.h" -#include "BLI_session_uuid.h" - #include "MEM_guardedalloc.h" #include "DNA_mesh_types.h" @@ -21,22 +19,40 @@ #include "opensubdiv_capi.h" -void BKE_subsurf_modifier_subdiv_settings_init(SubdivSettings *settings, - const SubsurfModifierData *smd, - const bool use_render_params) +bool BKE_subsurf_modifier_runtime_init(SubsurfModifierData *smd, const bool use_render_params) { const int requested_levels = (use_render_params) ? smd->renderLevels : smd->levels; - settings->is_simple = (smd->subdivType == SUBSURF_TYPE_SIMPLE); - settings->is_adaptive = !(smd->flags & eSubsurfModifierFlag_UseRecursiveSubdivision); - settings->level = settings->is_simple ? - 1 : - (settings->is_adaptive ? smd->quality : requested_levels); - settings->use_creases = (smd->flags & eSubsurfModifierFlag_UseCrease); - settings->vtx_boundary_interpolation = BKE_subdiv_vtx_boundary_interpolation_from_subsurf( + SubdivSettings settings; + settings.is_simple = (smd->subdivType == SUBSURF_TYPE_SIMPLE); + settings.is_adaptive = !(smd->flags & eSubsurfModifierFlag_UseRecursiveSubdivision); + settings.level = settings.is_simple ? 1 : + (settings.is_adaptive ? smd->quality : requested_levels); + settings.use_creases = (smd->flags & eSubsurfModifierFlag_UseCrease); + settings.vtx_boundary_interpolation = BKE_subdiv_vtx_boundary_interpolation_from_subsurf( smd->boundary_smooth); - settings->fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth( + settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth( smd->uv_smooth); + + SubsurfRuntimeData *runtime_data = (SubsurfRuntimeData *)smd->modifier.runtime; + if (settings.level == 0) { + /* Modifier is effectively disabled, but still update settings if runtime data + * was already allocated. */ + if (runtime_data) { + runtime_data->settings = settings; + } + + return false; + } + else { + /* Allocate runtime data if it did not exist yet. */ + if (runtime_data == NULL) { + runtime_data = MEM_callocN(sizeof(*runtime_data), "subsurf runtime"); + smd->modifier.runtime = runtime_data; + } + runtime_data->settings = settings; + return true; + } } static ModifierData *modifier_get_last_enabled_for_mode(const Scene *scene, @@ -133,37 +149,27 @@ bool BKE_subsurf_modifier_can_do_gpu_subdiv(const Scene *scene, bool BKE_subsurf_modifier_has_gpu_subdiv(const Mesh *mesh) { - return BLI_session_uuid_is_generated(&mesh->runtime.subsurf_session_uuid); + SubsurfRuntimeData *runtime_data = mesh->runtime.subsurf_runtime_data; + return runtime_data && runtime_data->has_gpu_subdiv; } void (*BKE_subsurf_modifier_free_gpu_cache_cb)(Subdiv *subdiv) = NULL; -Subdiv *BKE_subsurf_modifier_subdiv_descriptor_ensure(const SubsurfModifierData *smd, - const SubdivSettings *subdiv_settings, +Subdiv *BKE_subsurf_modifier_subdiv_descriptor_ensure(SubsurfRuntimeData *runtime_data, const Mesh *mesh, const bool for_draw_code) { - SubsurfRuntimeData *runtime_data = (SubsurfRuntimeData *)smd->modifier.runtime; if (runtime_data->subdiv && runtime_data->set_by_draw_code != for_draw_code) { BKE_subdiv_free(runtime_data->subdiv); runtime_data->subdiv = NULL; } - Subdiv *subdiv = BKE_subdiv_update_from_mesh(runtime_data->subdiv, subdiv_settings, mesh); + Subdiv *subdiv = BKE_subdiv_update_from_mesh( + runtime_data->subdiv, &runtime_data->settings, mesh); runtime_data->subdiv = subdiv; runtime_data->set_by_draw_code = for_draw_code; return subdiv; } -SubsurfRuntimeData *BKE_subsurf_modifier_ensure_runtime(SubsurfModifierData *smd) -{ - SubsurfRuntimeData *runtime_data = (SubsurfRuntimeData *)smd->modifier.runtime; - if (runtime_data == NULL) { - runtime_data = MEM_callocN(sizeof(*runtime_data), "subsurf runtime"); - smd->modifier.runtime = runtime_data; - } - return runtime_data; -} - int BKE_subsurf_modifier_eval_required_mode(bool is_final_render, bool is_edit_mode) { if (is_final_render) { diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c index f9d3a44e5cb..8b462cba7ed 100644 --- a/source/blender/blenkernel/intern/tracking.c +++ b/source/blender/blenkernel/intern/tracking.c @@ -336,7 +336,11 @@ void BKE_tracking_settings_init(MovieTracking *tracking) tracking->stabilization.filter = TRACKING_FILTER_BILINEAR; tracking->stabilization.flag |= TRACKING_SHOW_STAB_TRACKS; - BKE_tracking_object_add(tracking, "Camera"); + /* Descending order of average error: tracks with the highest error are on top. */ + tracking->dopesheet.sort_method = TRACKING_DOPE_SORT_AVERAGE_ERROR; + tracking->dopesheet.flag |= TRACKING_DOPE_SORT_INVERSE; + + BKE_tracking_object_add(tracking, DATA_("Camera")); } ListBase *BKE_tracking_get_active_tracks(MovieTracking *tracking) @@ -2904,6 +2908,10 @@ static int channels_average_error_sort(const void *a, const void *b) return 1; } + if (channel_a->track->error == channel_b->track->error) { + return channels_alpha_sort(a, b); + } + return 0; } diff --git a/source/blender/blenkernel/intern/unit.c b/source/blender/blenkernel/intern/unit.c index 557fb2568fe..b31632f0234 100644 --- a/source/blender/blenkernel/intern/unit.c +++ b/source/blender/blenkernel/intern/unit.c @@ -462,11 +462,6 @@ static size_t unit_as_string(char *str, double value_conv = (value / unit->scalar) - unit->bias; bool strip_skip = false; - /* Adjust precision to expected number of significant digits. - * Note that here, we shall not have to worry about very big/small numbers, units are expected - * to replace 'scientific notation' in those cases. */ - prec -= integer_digits_d(value_conv); - /* Negative precision is used to disable stripping of zeroes. * This reduces text jumping when changing values. */ if (prec < 0) { @@ -474,6 +469,11 @@ static size_t unit_as_string(char *str, prec *= -1; } + /* Adjust precision to expected number of significant digits. + * Note that here, we shall not have to worry about very big/small numbers, units are expected + * to replace 'scientific notation' in those cases. */ + prec -= integer_digits_d(value_conv); + CLAMP(prec, 0, 6); /* Convert to a string. */ @@ -491,10 +491,10 @@ static size_t unit_as_string(char *str, while (i > 0 && str[i] == '0') { /* 4.300 -> 4.3 */ str[i--] = pad; } - } - if (i > 0 && str[i] == '.') { /* 10. -> 10 */ - str[i--] = pad; + if (i > 0 && str[i] == '.') { /* 10. -> 10 */ + str[i--] = pad; + } } } diff --git a/source/blender/blenlib/intern/delaunay_2d.cc b/source/blender/blenlib/intern/delaunay_2d.cc index ece22bcf82e..db6cb0824dc 100644 --- a/source/blender/blenlib/intern/delaunay_2d.cc +++ b/source/blender/blenlib/intern/delaunay_2d.cc @@ -2637,6 +2637,7 @@ void prepare_cdt_for_output(CDT_state<T> *cdt_state, const CDT_output_type outpu remove_faces_in_holes(cdt_state); } else if (output_type == CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES) { + remove_outer_edges_until_constraints(cdt_state); remove_non_constraint_edges_leave_valid_bmesh(cdt_state); remove_faces_in_holes(cdt_state); } diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c index a983821f15e..4a213f5fe74 100644 --- a/source/blender/blenlib/intern/math_base_inline.c +++ b/source/blender/blenlib/intern/math_base_inline.c @@ -767,6 +767,20 @@ MALWAYS_INLINE __m128 _bli_math_fastpow24(const __m128 arg) return _mm_mul_ps(x, _mm_mul_ps(x, x)); } +MALWAYS_INLINE __m128 _bli_math_rsqrt(__m128 in) +{ + __m128 r = _mm_rsqrt_ps(in); + /* Only do additional Newton-Raphson iterations when using actual SSE + * code path. When we are emulating SSE on NEON via sse2neon, the + * additional NR iterations are already done inside _mm_rsqrt_ps + * emulation. */ +# if defined(__SSE2__) + r = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(1.5f), r), + _mm_mul_ps(_mm_mul_ps(_mm_mul_ps(in, _mm_set1_ps(-0.5f)), r), _mm_mul_ps(r, r))); +# endif + return r; +} + /* Calculate powf(x, 1.0f / 2.4) */ MALWAYS_INLINE __m128 _bli_math_fastpow512(const __m128 arg) { @@ -776,14 +790,14 @@ MALWAYS_INLINE __m128 _bli_math_fastpow512(const __m128 arg) */ __m128 xf = _bli_math_fastpow(0x3f2aaaab, 0x5eb504f3, arg); __m128 xover = _mm_mul_ps(arg, xf); - __m128 xfm1 = _mm_rsqrt_ps(xf); + __m128 xfm1 = _bli_math_rsqrt(xf); __m128 x2 = _mm_mul_ps(arg, arg); __m128 xunder = _mm_mul_ps(x2, xfm1); /* sqrt2 * over + 2 * sqrt2 * under */ __m128 xavg = _mm_mul_ps(_mm_set1_ps(1.0f / (3.0f * 0.629960524947437f) * 0.999852f), _mm_add_ps(xover, xunder)); - xavg = _mm_mul_ps(xavg, _mm_rsqrt_ps(xavg)); - xavg = _mm_mul_ps(xavg, _mm_rsqrt_ps(xavg)); + xavg = _mm_mul_ps(xavg, _bli_math_rsqrt(xavg)); + xavg = _mm_mul_ps(xavg, _bli_math_rsqrt(xavg)); return xavg; } diff --git a/source/blender/blenlib/tests/BLI_math_color_test.cc b/source/blender/blenlib/tests/BLI_math_color_test.cc index 7f2c0a3f1ca..4d928477870 100644 --- a/source/blender/blenlib/tests/BLI_math_color_test.cc +++ b/source/blender/blenlib/tests/BLI_math_color_test.cc @@ -74,3 +74,71 @@ TEST(math_color, LinearRGBTosRGBRoundtrip) EXPECT_NEAR(orig_linear_color, linear_color, 1e-5); } } + +TEST(math_color, linearrgb_to_srgb_v3_v3) +{ + float srgb_color[3]; + { + const float kTolerance = 1.0e-8f; + const float linear_color[3] = {0.0023f, 0.0024f, 0.0025f}; + linearrgb_to_srgb_v3_v3(srgb_color, linear_color); + EXPECT_NEAR(0.029716f, srgb_color[0], kTolerance); + EXPECT_NEAR(0.031008f, srgb_color[1], kTolerance); + EXPECT_NEAR(0.032300f, srgb_color[2], kTolerance); + } + + { + /* SIMD implementation of linear->srgb for larger inputs + * is less accurate; use larger tolerance. */ + const float kTolerance = 3.6e-5f; + const float linear_color[3] = {0.71f, 0.75f, 0.78f}; + linearrgb_to_srgb_v3_v3(srgb_color, linear_color); + EXPECT_NEAR(0.859696f, srgb_color[0], kTolerance); + EXPECT_NEAR(0.880825f, srgb_color[1], kTolerance); + EXPECT_NEAR(0.896244f, srgb_color[2], kTolerance); + } + + { + /* Not a common, but possible case: values beyond 1.0 range. */ + const float kTolerance = 2.3e-4f; + const float linear_color[3] = {1.5f, 2.8f, 5.6f}; + linearrgb_to_srgb_v3_v3(srgb_color, linear_color); + EXPECT_NEAR(1.19418f, srgb_color[0], kTolerance); + EXPECT_NEAR(1.56520f, srgb_color[1], kTolerance); + EXPECT_NEAR(2.10771f, srgb_color[2], kTolerance); + } +} + +TEST(math_color, srgb_to_linearrgb_v3_v3) +{ + float linear_color[3]; + { + const float kTolerance = 1.0e-8f; + const float srgb_color[3] = {0.0023f, 0.0024f, 0.0025f}; + srgb_to_linearrgb_v3_v3(linear_color, srgb_color); + EXPECT_NEAR(0.000178019f, linear_color[0], kTolerance); + EXPECT_NEAR(0.000185759f, linear_color[1], kTolerance); + EXPECT_NEAR(0.000193498f, linear_color[2], kTolerance); + } + + { + /* SIMD implementation of linear->srgb for larger inputs + * is less accurate; use larger tolerance. */ + const float kTolerance = 1.5e-7f; + const float srgb_color[3] = {0.71f, 0.72f, 0.73f}; + srgb_to_linearrgb_v3_v3(linear_color, srgb_color); + EXPECT_NEAR(0.4623615f, linear_color[0], kTolerance); + EXPECT_NEAR(0.4770000f, linear_color[1], kTolerance); + EXPECT_NEAR(0.4919052f, linear_color[2], kTolerance); + } + + { + /* Not a common, but possible case: values beyond 1.0 range. */ + const float kTolerance = 7.7e-6f; + const float srgb_color[3] = {1.1f, 2.5f, 5.6f}; + srgb_to_linearrgb_v3_v3(linear_color, srgb_color); + EXPECT_NEAR(1.24277f, linear_color[0], kTolerance); + EXPECT_NEAR(8.35473f, linear_color[1], kTolerance); + EXPECT_NEAR(56.23833f, linear_color[2], kTolerance); + } +} diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index eafb27f9758..e542bc46a28 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -4102,7 +4102,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) LISTBASE_FOREACH (Object *, ob, &bmain->objects) { LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { if (md->type == eModifierType_DataTransfer) { - /* Now datatransfer's mix factor is multiplied with weights when any, + /* Now data-transfer's mix factor is multiplied with weights when any, * instead of being ignored, * we need to take care of that to keep 'old' files compatible. */ DataTransferModifierData *dtmd = (DataTransferModifierData *)md; diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 57240b93ab1..97b414d4b4a 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1001,6 +1001,9 @@ static void do_version_subsurface_methods(bNode *node) static void version_geometry_nodes_add_attribute_input_settings(NodesModifierData *nmd) { + if (nmd->settings.properties == NULL) { + return; + } /* Before versioning the properties, make sure it hasn't been done already. */ LISTBASE_FOREACH (const IDProperty *, property, &nmd->settings.properties->data.group) { if (strstr(property->name, "_use_attribute") || strstr(property->name, "_attribute_name")) { diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index b3f3b9cbf7d..40359500d3c 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -347,6 +347,7 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) */ { /* Keep this block, even when empty. */ + btheme->tui.wcol_view_item = U_theme_default.tui.wcol_view_item; } #undef FROM_DEFAULT_V4_UCHAR diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc b/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc index d632c4e2f7e..8ba6c840a59 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc @@ -42,9 +42,7 @@ struct StackEntry { struct CyclesSolverState { CyclesSolverState(Depsgraph *graph) - : graph(graph), - traversal_stack(BLI_stack_new(sizeof(StackEntry), "DEG detect cycles stack")), - num_cycles(0) + : graph(graph), traversal_stack(BLI_stack_new(sizeof(StackEntry), "DEG detect cycles stack")) { /* pass */ } @@ -57,7 +55,7 @@ struct CyclesSolverState { } Depsgraph *graph; BLI_Stack *traversal_stack; - int num_cycles; + int num_cycles = 0; }; inline void set_node_visited_state(Node *node, eCyclicCheckVisitedState state) diff --git a/source/blender/draw/engines/eevee/eevee_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c index 6fd5d97089d..5709621fc05 100644 --- a/source/blender/draw/engines/eevee/eevee_shaders.c +++ b/source/blender/draw/engines/eevee/eevee_shaders.c @@ -718,7 +718,7 @@ GPUShader *EEVEE_shaders_cryptomatte_sh_get(bool is_hair) if (e_data.cryptomatte_sh[index] == NULL) { DynStr *ds = BLI_dynstr_new(); BLI_dynstr_append(ds, SHADER_DEFINES); - BLI_dynstr_append(ds, "#define attrib_load(a) \n"); + BLI_dynstr_append(ds, "#define attrib_load() \n"); if (is_hair) { BLI_dynstr_append(ds, "#define HAIR_SHADER\n"); } diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index 1261c855a82..eb409f076f3 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -126,7 +126,7 @@ BLI_STATIC_ASSERT_ALIGN(VelocityGeometryIndex, 16) * \{ */ enum eClosureBits : uint32_t { - /** NOTE: Theses are used as stencil bits. So we are limited to 8bits. */ + /** NOTE: These are used as stencil bits. So we are limited to 8bits. */ CLOSURE_DIFFUSE = (1u << 0u), CLOSURE_SSS = (1u << 1u), CLOSURE_REFLECTION = (1u << 2u), diff --git a/source/blender/draw/engines/eevee_next/eevee_velocity.hh b/source/blender/draw/engines/eevee_next/eevee_velocity.hh index 1bfd9f8c18f..e2606c061e1 100644 --- a/source/blender/draw/engines/eevee_next/eevee_velocity.hh +++ b/source/blender/draw/engines/eevee_next/eevee_velocity.hh @@ -111,7 +111,7 @@ class VelocityModule { void step_sync(eVelocityStep step, float time); /* Gather motion data. Returns true if the object **can** have motion. */ - bool step_object_sync(Object *ob, ObjectKey &ob_key, int recalc = 0); + bool step_object_sync(Object *ob, ObjectKey &object_key, int recalc = 0); /* Moves next frame data to previous frame data. Nullify next frame data. */ void step_swap(); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl index fafea1e576e..a65bb7decb6 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl @@ -287,7 +287,7 @@ vec3 attr_load_uv(vec3 attr) /** \name Volume Attribute post * * TODO(@fclem): These implementation details should concern the DRWManager and not be a fix on - * the engine side. But as of now, the engines are reponsible for loading the attributes. + * the engine side. But as of now, the engines are responsible for loading the attributes. * * \{ */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl index 277b2e35d8b..0ccf06a9e14 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl @@ -29,7 +29,7 @@ bool closure_select(float weight, inout float total_weight, inout float r) float x = weight / total_weight; bool chosen = (r < x); /* Assuming that if r is in the interval [0,x] or [x,1], it's still uniformly distributed within - * that interval, so you remaping to [0,1] again to explore this space of probability. */ + * that interval, so you remapping to [0,1] again to explore this space of probability. */ r = (chosen) ? (r / x) : ((r - x) / (1.0 - x)); return chosen; } @@ -333,7 +333,7 @@ vec3 coordinate_screen(vec3 P) window.xy = vec2(0.5); } else { - /* TODO(fclem): Actual camera tranform. */ + /* TODO(fclem): Actual camera transform. */ window.xy = project_point(ViewProjectionMatrix, P).xy * 0.5 + 0.5; window.xy = window.xy * CameraTexCoFactors.xy + CameraTexCoFactors.zw; } diff --git a/source/blender/draw/engines/overlay/shaders/overlay_edit_uv_face_dots_vert.glsl b/source/blender/draw/engines/overlay/shaders/overlay_edit_uv_face_dots_vert.glsl index 280b31ea463..c0d4393f2da 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_edit_uv_face_dots_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_edit_uv_face_dots_vert.glsl @@ -5,6 +5,6 @@ void main() vec3 world_pos = point_object_to_world(vec3(au, 0.0)); gl_Position = point_world_to_ndc(world_pos); - finalColor = ((flag & FACE_UV_SELECT) != 0) ? colorVertexSelect : vec4(colorWire.rgb, 1.0); + finalColor = ((flag & FACE_UV_SELECT) != 0) ? colorFaceDot : vec4(colorWire.rgb, 1.0); gl_PointSize = pointSize; } diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc index 82be73f6de8..baea0c7b646 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc @@ -117,7 +117,7 @@ static void mesh_render_data_lverts_bm(const MeshRenderData *mr, MeshBufferCache cache->loose_geom.verts = static_cast<int *>( MEM_mallocN(mr->vert_len * sizeof(*cache->loose_geom.verts), __func__)); BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) { - if (eve->e == NULL) { + if (eve->e == nullptr) { cache->loose_geom.verts[cache->loose_geom.vert_len++] = elem_id; } } @@ -135,7 +135,7 @@ static void mesh_render_data_ledges_bm(const MeshRenderData *mr, MeshBufferCache cache->loose_geom.edges = static_cast<int *>( MEM_mallocN(mr->edge_len * sizeof(*cache->loose_geom.edges), __func__)); BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) { - if (ede->l == NULL) { + if (ede->l == nullptr) { cache->loose_geom.edges[cache->loose_geom.edge_len++] = elem_id; } } @@ -337,7 +337,7 @@ void mesh_render_data_update_looptris(MeshRenderData *mr, * The overall advantage is small (around 1%), so keep this as-is. */ mr->mlooptri = static_cast<MLoopTri *>( MEM_mallocN(sizeof(*mr->mlooptri) * mr->tri_len, "MR_DATATYPE_LOOPTRI")); - if (mr->poly_normals != NULL) { + if (mr->poly_normals != nullptr) { BKE_mesh_recalc_looptri_with_normals(me->mloop, me->mpoly, me->mvert, @@ -356,7 +356,7 @@ void mesh_render_data_update_looptris(MeshRenderData *mr, /* #BMesh */ if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) { /* Edit mode ensures this is valid, no need to calculate. */ - BLI_assert((mr->bm->totloop == 0) || (mr->edit_bmesh->looptris != NULL)); + BLI_assert((mr->bm->totloop == 0) || (mr->edit_bmesh->looptris != nullptr)); } } } @@ -391,9 +391,9 @@ void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_ mr->poly_len, is_auto_smooth, split_angle, - NULL, + nullptr, clnors, - NULL); + nullptr); } } else { @@ -403,9 +403,9 @@ void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_ } if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { - const float(*vert_coords)[3] = NULL; - const float(*vert_normals)[3] = NULL; - const float(*poly_normals)[3] = NULL; + const float(*vert_coords)[3] = nullptr; + const float(*vert_normals)[3] = nullptr; + const float(*poly_normals)[3] = nullptr; if (mr->edit_data && mr->edit_data->vertexCos) { vert_coords = mr->bm_vert_coords; @@ -423,8 +423,8 @@ void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_ is_auto_smooth, split_angle, mr->loop_normals, - NULL, - NULL, + nullptr, + nullptr, clnors_offset, false); } @@ -455,7 +455,7 @@ MeshRenderData *mesh_render_data_create(Object *object, mr->bm = me->edit_mesh->bm; mr->edit_bmesh = me->edit_mesh; mr->me = (do_final) ? editmesh_eval_final : editmesh_eval_cage; - mr->edit_data = is_mode_active ? mr->me->runtime.edit_data : NULL; + mr->edit_data = is_mode_active ? mr->me->runtime.edit_data : nullptr; if (mr->edit_data) { EditMeshData *emd = mr->edit_data; @@ -515,13 +515,13 @@ MeshRenderData *mesh_render_data_create(Object *object, /* Seems like the mesh_eval_final do not have the right origin indices. * Force not mapped in this case. */ if (has_mdata && do_final && editmesh_eval_final != editmesh_eval_cage) { - // mr->edit_bmesh = NULL; + // mr->edit_bmesh = nullptr; mr->extract_type = MR_EXTRACT_MESH; } } else { mr->me = me; - mr->edit_bmesh = NULL; + mr->edit_bmesh = nullptr; bool use_mapped = is_paint_mode && mr->me && !mr->me->runtime.is_original; if (use_mapped) { @@ -575,8 +575,8 @@ void mesh_render_data_free(MeshRenderData *mr) MEM_SAFE_FREE(mr->loop_normals); /* Loose geometry are owned by #MeshBufferCache. */ - mr->ledges = NULL; - mr->lverts = NULL; + mr->ledges = nullptr; + mr->lverts = nullptr; MEM_freeN(mr); } diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h index 0755d5967d5..4fa5813d476 100644 --- a/source/blender/draw/intern/draw_cache_impl.h +++ b/source/blender/draw/intern/draw_cache_impl.h @@ -156,11 +156,15 @@ struct GPUBatch *DRW_lattice_batch_cache_get_edit_verts(struct Lattice *lt); /** \} */ /* -------------------------------------------------------------------- */ -/** \name Hair +/** \name Curves * \{ */ int DRW_curves_material_count_get(struct Curves *curves); +struct GPUBatch *DRW_curves_batch_cache_get_edit_points(struct Curves *curves); + +void DRW_curves_batch_cache_create_requested(struct Object *ob); + /** \} */ /* -------------------------------------------------------------------- */ @@ -352,16 +356,6 @@ struct GPUBatch *DRW_particles_batch_cache_get_edit_tip_points(struct Object *ob /** \} */ -/* -------------------------------------------------------------------- */ -/** \name Curves - * \{ */ - -struct GPUBatch *DRW_curves_batch_cache_get_edit_points(struct Curves *curves); - -void DRW_curves_batch_cache_create_requested(const struct Object *ob); - -/** \} */ - #ifdef __cplusplus } #endif diff --git a/source/blender/draw/intern/draw_cache_impl_curves.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc index 2c07b651c7c..8399f5c9650 100644 --- a/source/blender/draw/intern/draw_cache_impl_curves.cc +++ b/source/blender/draw/intern/draw_cache_impl_curves.cc @@ -163,7 +163,7 @@ void DRW_curves_batch_cache_dirty_tag(Curves *curves, int mode) cache->is_dirty = true; break; default: - BLI_assert(0); + BLI_assert_unreachable(); } } @@ -530,8 +530,6 @@ static bool curves_ensure_attributes(const Curves &curves, } switch (type) { - default: - break; case CD_PROP_BOOL: case CD_PROP_INT8: case CD_PROP_INT32: @@ -548,6 +546,8 @@ static bool curves_ensure_attributes(const Curves &curves, } break; } + default: + break; } } @@ -638,7 +638,7 @@ GPUBatch *DRW_curves_batch_cache_get_edit_points(Curves *curves) return DRW_batch_request(&cache.edit_points); } -void DRW_curves_batch_cache_create_requested(const Object *ob) +void DRW_curves_batch_cache_create_requested(Object *ob) { Curves *curves = static_cast<Curves *>(ob->data); CurvesBatchCache &cache = curves_batch_cache_get(*curves); diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.cc b/source/blender/draw/intern/draw_cache_impl_mesh.cc index 1b45f3f88dd..7c02ee2c033 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.cc +++ b/source/blender/draw/intern/draw_cache_impl_mesh.cc @@ -238,9 +238,9 @@ BLI_INLINE void mesh_cd_layers_type_clear(DRW_MeshCDMask *a) BLI_INLINE const Mesh *editmesh_final_or_this(const Object *object, const Mesh *me) { - if (me->edit_mesh != NULL) { + if (me->edit_mesh != nullptr) { Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(object); - if (editmesh_eval_final != NULL) { + if (editmesh_eval_final != nullptr) { return editmesh_eval_final; } } @@ -351,7 +351,8 @@ static void mesh_cd_calc_active_mloopcol_layer(const Object *object, const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final); const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final); - BKE_id_attribute_copy_domains_temp(ID_ME, cd_vdata, NULL, cd_ldata, NULL, NULL, &me_query.id); + BKE_id_attribute_copy_domains_temp( + ID_ME, cd_vdata, nullptr, cd_ldata, nullptr, nullptr, &me_query.id); const CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me_query.id); int layer_i = BKE_id_attribute_to_index( @@ -367,7 +368,7 @@ static uint mesh_cd_calc_gpu_layers_vcol_used(const Mesh *me_query, const CustomData *cd_ldata, const char name[]) { - const CustomDataLayer *layer = NULL; + const CustomDataLayer *layer = nullptr; eAttrDomain domain; if (name[0]) { @@ -426,7 +427,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, Mesh me_query = blender::dna::shallow_zero_initialize(); BKE_id_attribute_copy_domains_temp( - ID_ME, cd_vdata, cd_edata, cd_ldata, cd_pdata, NULL, &me_query.id); + ID_ME, cd_vdata, cd_edata, cd_ldata, cd_pdata, nullptr, &me_query.id); /* See: DM_vertex_attributes_from_gpu for similar logic */ DRW_MeshCDMask cd_used; @@ -739,7 +740,7 @@ static bool mesh_batch_cache_valid(Object *object, Mesh *me) { MeshBatchCache *cache = static_cast<MeshBatchCache *>(me->runtime.batch_cache); - if (cache == NULL) { + if (cache == nullptr) { return false; } @@ -755,7 +756,7 @@ static bool mesh_batch_cache_valid(Object *object, Mesh *me) } } - if (cache->is_editmode != (me->edit_mesh != NULL)) { + if (cache->is_editmode != (me->edit_mesh != nullptr)) { return false; } @@ -782,7 +783,7 @@ static void mesh_batch_cache_init(Object *object, Mesh *me) memset(cache, 0, sizeof(*cache)); } - cache->is_editmode = me->edit_mesh != NULL; + cache->is_editmode = me->edit_mesh != nullptr; if (object->sculpt && object->sculpt->pbvh) { cache->pbvh_is_drawing = BKE_pbvh_is_drawing(object->sculpt->pbvh); @@ -928,7 +929,7 @@ static void mesh_batch_cache_discard_uvedit_select(MeshBatchCache *cache) void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode) { MeshBatchCache *cache = static_cast<MeshBatchCache *>(me->runtime.batch_cache); - if (cache == NULL) { + if (cache == nullptr) { return; } DRWBatchFlag batch_map; @@ -1009,7 +1010,7 @@ static void mesh_batch_cache_free_subdiv_cache(MeshBatchCache *cache) if (cache->subdiv_cache) { draw_subdiv_cache_free(cache->subdiv_cache); MEM_freeN(cache->subdiv_cache); - cache->subdiv_cache = NULL; + cache->subdiv_cache = nullptr; } } @@ -1088,7 +1089,8 @@ static void sculpt_request_active_vcol(MeshBatchCache *cache, Object *object, Me const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final); Mesh me_query = blender::dna::shallow_zero_initialize(); - BKE_id_attribute_copy_domains_temp(ID_ME, cd_vdata, NULL, cd_ldata, NULL, NULL, &me_query.id); + BKE_id_attribute_copy_domains_temp( + ID_ME, cd_vdata, nullptr, cd_ldata, nullptr, nullptr, &me_query.id); const CustomDataLayer *active = BKE_id_attributes_active_color_get(&me_query.id); const CustomDataLayer *render = BKE_id_attributes_render_color_get(&me_query.id); @@ -1134,7 +1136,7 @@ GPUBatch *DRW_mesh_batch_cache_get_loose_edges(Mesh *me) MeshBatchCache *cache = mesh_batch_cache_get(me); mesh_batch_cache_add_request(cache, MBC_LOOSE_EDGES); if (cache->no_loose_wire) { - return NULL; + return nullptr; } return DRW_batch_request(&cache->batch.loose_edges); @@ -1253,7 +1255,7 @@ GPUVertBuf *DRW_mesh_batch_cache_pos_vertbuf_get(Mesh *me) /* Request surface to trigger the vbo filling. Otherwise it may do nothing. */ mesh_batch_cache_request_surface_batches(cache); - DRW_vbo_request(NULL, &cache->final.buff.vbo.pos_nor); + DRW_vbo_request(nullptr, &cache->final.buff.vbo.pos_nor); return cache->final.buff.vbo.pos_nor; } @@ -1375,10 +1377,10 @@ GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_stretch_area(Object *object, edituv_request_active_uv(cache, object, me); mesh_batch_cache_add_request(cache, MBC_EDITUV_FACES_STRETCH_AREA); - if (tot_area != NULL) { + if (tot_area != nullptr) { *tot_area = &cache->tot_area; } - if (tot_uv_area != NULL) { + if (tot_uv_area != nullptr) { *tot_uv_area = &cache->tot_uv_area; } return DRW_batch_request(&cache->batch.edituv_faces_stretch_area); @@ -1450,7 +1452,7 @@ void DRW_mesh_batch_cache_free_old(Mesh *me, int ctime) { MeshBatchCache *cache = static_cast<MeshBatchCache *>(me->runtime.batch_cache); - if (cache == NULL) { + if (cache == nullptr) { return; } @@ -1521,7 +1523,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, const bool use_hide) { BLI_assert(task_graph); - const ToolSettings *ts = NULL; + const ToolSettings *ts = nullptr; if (scene) { ts = scene->toolsettings; } @@ -1556,12 +1558,12 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, #endif /* Sanity check. */ - if ((me->edit_mesh != NULL) && (ob->mode & OB_MODE_EDIT)) { - BLI_assert(BKE_object_get_editmesh_eval_final(ob) != NULL); + if ((me->edit_mesh != nullptr) && (ob->mode & OB_MODE_EDIT)) { + BLI_assert(BKE_object_get_editmesh_eval_final(ob) != nullptr); } - const bool is_editmode = (me->edit_mesh != NULL) && - (BKE_object_get_editmesh_eval_final(ob) != NULL) && + const bool is_editmode = (me->edit_mesh != nullptr) && + (BKE_object_get_editmesh_eval_final(ob) != nullptr) && DRW_object_is_in_edit_mode(ob); /* This could be set for paint mode too, currently it's only used for edit-mode. */ @@ -1572,7 +1574,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if (batch_requested & MBC_SURFACE_WEIGHTS) { /* Check vertex weights. */ - if ((cache->batch.surface_weights != NULL) && (ts != NULL)) { + if ((cache->batch.surface_weights != nullptr) && (ts != nullptr)) { struct DRW_MeshWeightState wstate; BLI_assert(ob->type == OB_MESH); drw_mesh_weight_state_extract(ob, me, ts, is_paint_mode, &wstate); @@ -1589,7 +1591,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if (cache->cd_needed.orco != 0) { /* Orco is always extracted from final mesh. */ Mesh *me_final = (me->edit_mesh) ? BKE_object_get_editmesh_eval_final(ob) : me; - if (CustomData_get_layer(&me_final->vdata, CD_ORCO) == NULL) { + if (CustomData_get_layer(&me_final->vdata, CD_ORCO) == nullptr) { /* Skip orco calculation */ cache->cd_needed.orco = 0; } @@ -1749,7 +1751,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, } assert_deps_valid(MBC_LOOSE_EDGES, {BUFFER_INDEX(ibo.lines_loose), BUFFER_INDEX(vbo.pos_nor)}); if (DRW_batch_requested(cache->batch.loose_edges, GPU_PRIM_LINES)) { - DRW_ibo_request(NULL, &mbuflist->ibo.lines); + DRW_ibo_request(nullptr, &mbuflist->ibo.lines); DRW_ibo_request(cache->batch.loose_edges, &mbuflist->ibo.lines_loose); DRW_vbo_request(cache->batch.loose_edges, &mbuflist->vbo.pos_nor); } @@ -2066,8 +2068,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, } if (do_subdivision) { - DRW_create_subdivision(scene, - ob, + DRW_create_subdivision(ob, me, cache, &cache->final, diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index e7726ff55ca..b37a420b555 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -1052,14 +1052,10 @@ static void build_vertex_face_adjacency_maps(DRWSubdivCache *cache) static bool draw_subdiv_build_cache(DRWSubdivCache *cache, Subdiv *subdiv, Mesh *mesh_eval, - const Scene *scene, - const SubsurfModifierData *smd, - const bool is_final_render) + const SubsurfRuntimeData *runtime_data) { - const int requested_levels = (is_final_render) ? smd->renderLevels : smd->levels; - const int level = get_render_subsurf_level(&scene->r, requested_levels, is_final_render); SubdivToMeshSettings to_mesh_settings; - to_mesh_settings.resolution = (1 << level) + 1; + to_mesh_settings.resolution = runtime_data->resolution; to_mesh_settings.use_optimal_display = false; if (cache->resolution != to_mesh_settings.resolution) { @@ -1837,6 +1833,7 @@ void draw_subdiv_build_lnor_buffer(const DRWSubdivCache *cache, GPU_vertbuf_bind_as_ssbo(cache->subdiv_polygon_offset_buffer, binding_point++); GPU_vertbuf_bind_as_ssbo(pos_nor, binding_point++); GPU_vertbuf_bind_as_ssbo(cache->extra_coarse_face_data, binding_point++); + GPU_vertbuf_bind_as_ssbo(cache->verts_orig_index, binding_point++); /* Outputs */ GPU_vertbuf_bind_as_ssbo(lnor, binding_point++); @@ -2005,8 +2002,7 @@ static void draw_subdiv_cache_ensure_mat_offsets(DRWSubdivCache *cache, MEM_freeN(per_polygon_mat_offset); } -static bool draw_subdiv_create_requested_buffers(const Scene *scene, - Object *ob, +static bool draw_subdiv_create_requested_buffers(Object *ob, Mesh *mesh, struct MeshBatchCache *batch_cache, MeshBufferCache *mbc, @@ -2020,16 +2016,10 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene, const bool use_hide, OpenSubdiv_EvaluatorCache *evaluator_cache) { - SubsurfModifierData *smd = reinterpret_cast<SubsurfModifierData *>( - BKE_modifiers_findby_session_uuid(ob, &mesh->runtime.subsurf_session_uuid)); - BLI_assert(smd); - - const bool is_final_render = DRW_state_is_scene_render(); + SubsurfRuntimeData *runtime_data = mesh->runtime.subsurf_runtime_data; + BLI_assert(runtime_data && runtime_data->has_gpu_subdiv); - SubdivSettings settings; - BKE_subsurf_modifier_subdiv_settings_init(&settings, smd, is_final_render); - - if (settings.level == 0) { + if (runtime_data->settings.level == 0) { return false; } @@ -2040,9 +2030,7 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene, bm = mesh->edit_mesh->bm; } - BKE_subsurf_modifier_ensure_runtime(smd); - - Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure(smd, &settings, mesh_eval, true); + Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure(runtime_data, mesh_eval, true); if (!subdiv) { return false; } @@ -2063,13 +2051,13 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene, } DRWSubdivCache *draw_cache = mesh_batch_cache_ensure_subdiv_cache(batch_cache); - if (!draw_subdiv_build_cache(draw_cache, subdiv, mesh_eval, scene, smd, is_final_render)) { + if (!draw_subdiv_build_cache(draw_cache, subdiv, mesh_eval, runtime_data)) { return false; } /* Edges which do not come from coarse edges should not be drawn in edit mode, only in object * mode when optimal display in turned off. */ - const bool optimal_display = (smd->flags & eSubsurfModifierFlag_ControlEdges) || is_editmode; + const bool optimal_display = runtime_data->use_optimal_display || is_editmode; draw_cache->bm = bm; draw_cache->mesh = mesh_eval; @@ -2077,14 +2065,13 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene, draw_cache->optimal_display = optimal_display; draw_cache->num_subdiv_triangles = tris_count_from_number_of_loops(draw_cache->num_subdiv_loops); - /* Copy topology information for stats display. Use `mesh` directly, as `mesh_eval` could be the - * edit mesh. */ - mesh->runtime.subsurf_totvert = draw_cache->num_subdiv_verts; - mesh->runtime.subsurf_totedge = draw_cache->num_subdiv_edges; - mesh->runtime.subsurf_totpoly = draw_cache->num_subdiv_quads; - mesh->runtime.subsurf_totloop = draw_cache->num_subdiv_loops; + /* Copy topology information for stats display. */ + runtime_data->stats_totvert = draw_cache->num_subdiv_verts; + runtime_data->stats_totedge = draw_cache->num_subdiv_edges; + runtime_data->stats_totpoly = draw_cache->num_subdiv_quads; + runtime_data->stats_totloop = draw_cache->num_subdiv_loops; - draw_cache->use_custom_loop_normals = (smd->flags & eSubsurfModifierFlag_UseCustomNormals) && + draw_cache->use_custom_loop_normals = (runtime_data->use_loop_normals) && (mesh_eval->flag & ME_AUTOSMOOTH) && CustomData_has_layer(&mesh_eval->ldata, CD_CUSTOMLOOPNORMAL); @@ -2206,8 +2193,7 @@ blender::Span<DRWSubdivLooseVertex> draw_subdiv_cache_get_loose_verts(const DRWS static OpenSubdiv_EvaluatorCache *g_evaluator_cache = nullptr; -void DRW_create_subdivision(const Scene *scene, - Object *ob, +void DRW_create_subdivision(Object *ob, Mesh *mesh, struct MeshBatchCache *batch_cache, MeshBufferCache *mbc, @@ -2230,8 +2216,7 @@ void DRW_create_subdivision(const Scene *scene, const double begin_time = PIL_check_seconds_timer(); #endif - if (!draw_subdiv_create_requested_buffers(scene, - ob, + if (!draw_subdiv_create_requested_buffers(ob, mesh, batch_cache, mbc, diff --git a/source/blender/draw/intern/draw_subdivision.h b/source/blender/draw/intern/draw_subdivision.h index 8920a2dcd51..2d9f4713feb 100644 --- a/source/blender/draw/intern/draw_subdivision.h +++ b/source/blender/draw/intern/draw_subdivision.h @@ -185,8 +185,7 @@ void draw_subdiv_cache_free(DRWSubdivCache *cache); /** \} */ -void DRW_create_subdivision(const struct Scene *scene, - struct Object *ob, +void DRW_create_subdivision(struct Object *ob, struct Mesh *mesh, struct MeshBatchCache *batch_cache, struct MeshBufferCache *mbc, diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh.cc index 7e02fbbf7d0..ec7d3a933b4 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.cc @@ -128,7 +128,7 @@ void mesh_render_data_loop_flag(const MeshRenderData *mr, return; } MLoopUV *luv = (MLoopUV *)BM_ELEM_CD_GET_VOID_P(l, cd_ofs); - if (luv != NULL && (luv->flag & MLOOPUV_PINNED)) { + if (luv != nullptr && (luv->flag & MLOOPUV_PINNED)) { eattr->v_flag |= VFLAG_VERT_UV_PINNED; } if (uvedit_uv_select_test_ex(mr->toolsettings, l, cd_ofs)) { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc index 7d584824045..4e89b34c0a0 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc @@ -183,10 +183,18 @@ static void extract_lines_loose_geom_subdiv(const DRWSubdivCache *subdiv_cache, uint *flags_data = static_cast<uint *>(GPU_vertbuf_get_data(flags)); - const MEdge *medge = mr->medge; - - for (DRWSubdivLooseEdge edge : loose_edges) { - *flags_data++ = (medge[edge.coarse_edge_index].flag & ME_HIDE) != 0; + if (mr->extract_type == MR_EXTRACT_MESH) { + const MEdge *medge = mr->medge; + for (DRWSubdivLooseEdge edge : loose_edges) { + *flags_data++ = (medge[edge.coarse_edge_index].flag & ME_HIDE) != 0; + } + } + else { + BMesh *bm = mr->bm; + for (DRWSubdivLooseEdge edge : loose_edges) { + const BMEdge *bm_edge = BM_edge_at_index(bm, edge.coarse_edge_index); + *flags_data++ = BM_elem_flag_test_bool(bm_edge, BM_ELEM_HIDDEN) != 0; + } } GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buffer); diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc index 5f07f8dfb72..fb6b5e1904b 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc @@ -27,21 +27,16 @@ namespace blender::draw { static CustomData *get_custom_data_for_domain(const MeshRenderData *mr, eAttrDomain domain) { switch (domain) { - default: { - return nullptr; - } - case ATTR_DOMAIN_POINT: { + case ATTR_DOMAIN_POINT: return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; - } - case ATTR_DOMAIN_CORNER: { + case ATTR_DOMAIN_CORNER: return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; - } - case ATTR_DOMAIN_FACE: { + case ATTR_DOMAIN_FACE: return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->pdata : &mr->me->pdata; - } - case ATTR_DOMAIN_EDGE: { + case ATTR_DOMAIN_EDGE: return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->edata : &mr->me->edata; - } + default: + return nullptr; } } @@ -50,7 +45,7 @@ static CustomData *get_custom_data_for_domain(const MeshRenderData *mr, eAttrDom * etc.) directly map to available GPU types. Booleans are still converted as attributes are vec4 * in the shader. */ -template<typename AttributeType, typename VBOType> struct attribute_type_converter { +template<typename AttributeType, typename VBOType> struct AttributeTypeConverter { static VBOType convert_value(AttributeType value) { if constexpr (std::is_same_v<AttributeType, VBOType>) { @@ -67,7 +62,7 @@ struct gpuMeshCol { ushort r, g, b, a; }; -template<> struct attribute_type_converter<MPropCol, gpuMeshCol> { +template<> struct AttributeTypeConverter<MPropCol, gpuMeshCol> { static gpuMeshCol convert_value(MPropCol value) { gpuMeshCol result; @@ -86,53 +81,42 @@ static uint gpu_component_size_for_attribute_type(eCustomDataType type) case CD_PROP_BOOL: case CD_PROP_INT8: case CD_PROP_INT32: - case CD_PROP_FLOAT: { + case CD_PROP_FLOAT: /* TODO(@kevindietrich): should be 1 when scalar attributes conversion is handled by us. See * comment #extract_attr_init. */ return 3; - } - case CD_PROP_FLOAT2: { + case CD_PROP_FLOAT2: return 2; - } - case CD_PROP_FLOAT3: { + case CD_PROP_FLOAT3: return 3; - } - case CD_PROP_COLOR: { + case CD_PROP_COLOR: return 4; - } - default: { + default: return 0; - } } } static GPUVertFetchMode get_fetch_mode_for_type(eCustomDataType type) { switch (type) { - case CD_PROP_INT32: { + case CD_PROP_INT32: return GPU_FETCH_INT_TO_FLOAT; - } - case CD_PROP_COLOR: { + case CD_PROP_COLOR: return GPU_FETCH_INT_TO_FLOAT_UNIT; - } - default: { + default: return GPU_FETCH_FLOAT; - } } } static GPUVertCompType get_comp_type_for_type(eCustomDataType type) { switch (type) { - case CD_PROP_INT32: { + case CD_PROP_INT32: return GPU_COMP_I32; - } - case CD_PROP_COLOR: { + case CD_PROP_COLOR: return GPU_COMP_U16; - } - default: { + default: return GPU_COMP_F32; - } } } @@ -184,41 +168,36 @@ static void fill_vertbuf_with_attribute(const MeshRenderData *mr, const AttributeType *attr_data = static_cast<const AttributeType *>( CustomData_get_layer_n(custom_data, request.cd_type, layer_index)); - using converter = attribute_type_converter<AttributeType, VBOType>; + using Converter = AttributeTypeConverter<AttributeType, VBOType>; switch (request.domain) { - default: { - BLI_assert(false); - break; - } - case ATTR_DOMAIN_POINT: { + case ATTR_DOMAIN_POINT: for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++, mloop++) { - *vbo_data = converter::convert_value(attr_data[mloop->v]); + *vbo_data = Converter::convert_value(attr_data[mloop->v]); } break; - } - case ATTR_DOMAIN_CORNER: { + case ATTR_DOMAIN_CORNER: for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++) { - *vbo_data = converter::convert_value(attr_data[ml_index]); + *vbo_data = Converter::convert_value(attr_data[ml_index]); } break; - } - case ATTR_DOMAIN_EDGE: { + case ATTR_DOMAIN_EDGE: for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++, mloop++) { - *vbo_data = converter::convert_value(attr_data[mloop->e]); + *vbo_data = Converter::convert_value(attr_data[mloop->e]); } break; - } - case ATTR_DOMAIN_FACE: { + case ATTR_DOMAIN_FACE: for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) { const MPoly &poly = mpoly[mp_index]; - const VBOType value = converter::convert_value(attr_data[mp_index]); + const VBOType value = Converter::convert_value(attr_data[mp_index]); for (int l = 0; l < poly.totloop; l++) { *vbo_data++ = value; } } break; - } + default: + BLI_assert_unreachable(); + break; } } @@ -231,9 +210,9 @@ static void fill_vertbuf_with_attribute_bm(const MeshRenderData *mr, BLI_assert(custom_data); const int layer_index = request.layer_index; - int cd_ofs = CustomData_get_n_offset(custom_data, request.cd_type, layer_index); + const int cd_ofs = CustomData_get_n_offset(custom_data, request.cd_type, layer_index); - using converter = attribute_type_converter<AttributeType, VBOType>; + using Converter = AttributeTypeConverter<AttributeType, VBOType>; BMIter f_iter; BMFace *efa; @@ -255,10 +234,10 @@ static void fill_vertbuf_with_attribute_bm(const MeshRenderData *mr, attr_data = static_cast<const AttributeType *>(BM_ELEM_CD_GET_VOID_P(l_iter->e, cd_ofs)); } else { - BLI_assert(false); + BLI_assert_unreachable(); continue; } - *vbo_data = converter::convert_value(*attr_data); + *vbo_data = Converter::convert_value(*attr_data); vbo_data++; } while ((l_iter = l_iter->next) != l_first); } @@ -297,37 +276,29 @@ static void extract_attr_init(const MeshRenderData *mr, * Blender convention, it should be `vec4(s, s, s, 1)`. This could be resolved using a similar * texture as for volume attribute, so we can control the conversion ourselves. */ switch (request.cd_type) { - case CD_PROP_BOOL: { + case CD_PROP_BOOL: extract_attr_generic<bool, float3>(mr, vbo, request); break; - } - case CD_PROP_INT8: { + case CD_PROP_INT8: extract_attr_generic<int8_t, float3>(mr, vbo, request); break; - } - case CD_PROP_INT32: { + case CD_PROP_INT32: extract_attr_generic<int32_t, float3>(mr, vbo, request); break; - } - case CD_PROP_FLOAT: { + case CD_PROP_FLOAT: extract_attr_generic<float, float3>(mr, vbo, request); break; - } - case CD_PROP_FLOAT2: { + case CD_PROP_FLOAT2: extract_attr_generic<float2>(mr, vbo, request); break; - } - case CD_PROP_FLOAT3: { + case CD_PROP_FLOAT3: extract_attr_generic<float3>(mr, vbo, request); break; - } - case CD_PROP_COLOR: { + case CD_PROP_COLOR: extract_attr_generic<MPropCol, gpuMeshCol>(mr, vbo, request); break; - } - default: { - BLI_assert(false); - } + default: + BLI_assert_unreachable(); } } @@ -353,37 +324,29 @@ static void extract_attr_init_subdiv(const DRWSubdivCache *subdiv_cache, GPU_vertbuf_data_alloc(src_data, static_cast<uint32_t>(coarse_mesh->totloop)); switch (request.cd_type) { - case CD_PROP_BOOL: { + case CD_PROP_BOOL: extract_attr_generic<bool, float3>(mr, src_data, request); break; - } - case CD_PROP_INT8: { + case CD_PROP_INT8: extract_attr_generic<int8_t, float3>(mr, src_data, request); break; - } - case CD_PROP_INT32: { + case CD_PROP_INT32: extract_attr_generic<int32_t, float3>(mr, src_data, request); break; - } - case CD_PROP_FLOAT: { + case CD_PROP_FLOAT: extract_attr_generic<float, float3>(mr, src_data, request); break; - } - case CD_PROP_FLOAT2: { + case CD_PROP_FLOAT2: extract_attr_generic<float2>(mr, src_data, request); break; - } - case CD_PROP_FLOAT3: { + case CD_PROP_FLOAT3: extract_attr_generic<float3>(mr, src_data, request); break; - } - case CD_PROP_COLOR: { + case CD_PROP_COLOR: extract_attr_generic<MPropCol, gpuMeshCol>(mr, src_data, request); break; - } - default: { - BLI_assert(false); - } + default: + BLI_assert_unreachable(); } GPUVertBuf *dst_buffer = static_cast<GPUVertBuf *>(buffer); @@ -430,7 +393,7 @@ EXTRACT_INIT_WRAPPER(12) EXTRACT_INIT_WRAPPER(13) EXTRACT_INIT_WRAPPER(14) -template<int index> +template<int Index> constexpr MeshExtract create_extractor_attr(ExtractInitFn fn, ExtractInitSubdivFn subdiv_fn) { MeshExtract extractor = {nullptr}; @@ -439,7 +402,7 @@ constexpr MeshExtract create_extractor_attr(ExtractInitFn fn, ExtractInitSubdivF extractor.data_type = MR_DATA_NONE; extractor.data_size = 0; extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.attr[index]); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.attr[Index]); return extractor; } diff --git a/source/blender/draw/intern/shaders/common_subdiv_lib.glsl b/source/blender/draw/intern/shaders/common_subdiv_lib.glsl index 55e4ac20271..d76a7369f79 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_lib.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_lib.glsl @@ -139,20 +139,20 @@ void set_vertex_pos(inout PosNorLoop vertex_data, vec3 pos) vertex_data.z = pos.z; } -void set_vertex_nor(inout PosNorLoop vertex_data, vec3 nor, uint flag) +/* Set the vertex normal but preserve the existing flag. This is for when we compute manually the + * vertex normals when we cannot use the limit surface, in which case the flag and the normal are + * set by two separate compute pass. */ +void set_vertex_nor(inout PosNorLoop vertex_data, vec3 nor) { vertex_data.nx = nor.x; vertex_data.ny = nor.y; vertex_data.nz = nor.z; - vertex_data.flag = float(flag); } -/* Set the vertex normal but preserve the existing flag. This is for when we compute manually the - * vertex normals when we cannot use the limit surface, in which case the flag and the normal are - * set by two separate compute pass. */ -void set_vertex_nor(inout PosNorLoop vertex_data, vec3 nor) +void set_vertex_nor(inout PosNorLoop vertex_data, vec3 nor, float flag) { - set_vertex_nor(vertex_data, nor, 0); + set_vertex_nor(vertex_data, nor); + vertex_data.flag = flag; } void add_newell_cross_v3_v3v3(inout vec3 n, vec3 v_prev, vec3 v_curr) diff --git a/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl index 0ffb216fc6f..e146ccb343a 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl @@ -459,9 +459,9 @@ void main() vec3 nor = vec3(0.0); int origindex = input_vert_origindex[loop_index]; - uint flag = 0; + float flag = 0.0; if (origindex == -1) { - flag = -1; + flag = -1.0; } PosNorLoop vertex_data; diff --git a/source/blender/draw/intern/shaders/common_subdiv_vbo_lnor_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_vbo_lnor_comp.glsl index b7bcfd2d369..f5c4c7895aa 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_vbo_lnor_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_vbo_lnor_comp.glsl @@ -11,7 +11,12 @@ layout(std430, binding = 2) readonly buffer extraCoarseFaceData uint extra_coarse_face_data[]; }; -layout(std430, binding = 3) writeonly buffer outputLoopNormals +layout(std430, binding = 3) readonly buffer inputVertOrigIndices +{ + int input_vert_origindex[]; +}; + +layout(std430, binding = 4) writeonly buffer outputLoopNormals { LoopNormal output_lnor[]; }; @@ -60,13 +65,15 @@ void main() loop_normal.nx = face_normal.x; loop_normal.ny = face_normal.y; loop_normal.nz = face_normal.z; - loop_normal.flag = 0.0; - - if (is_face_selected(coarse_quad_index)) { - loop_normal.flag = 1.0; - } for (int i = 0; i < 4; i++) { + int origindex = input_vert_origindex[start_loop_index + i]; + float flag = 0.0; + if (origindex == -1) { + flag = -1.0; + } + loop_normal.flag = flag; + output_lnor[start_loop_index + i] = loop_normal; } } diff --git a/source/blender/editors/animation/keyframes_keylist.cc b/source/blender/editors/animation/keyframes_keylist.cc index 3356ef4d47d..8dc598e6e2d 100644 --- a/source/blender/editors/animation/keyframes_keylist.cc +++ b/source/blender/editors/animation/keyframes_keylist.cc @@ -132,7 +132,7 @@ static void ED_keylist_runtime_init_listbase(AnimKeylist *keylist) return; } - keylist->runtime.list_wrapper.first = &keylist->runtime.key_columns[0]; + keylist->runtime.list_wrapper.first = keylist->runtime.key_columns.data(); keylist->runtime.list_wrapper.last = &keylist->runtime.key_columns[keylist->column_len - 1]; } @@ -309,7 +309,7 @@ static void keylist_first_last(const struct AnimKeylist *keylist, const struct ActKeyColumn **last_column) { if (keylist->is_runtime_initialized) { - *first_column = &keylist->runtime.key_columns[0]; + *first_column = keylist->runtime.key_columns.data(); *last_column = &keylist->runtime.key_columns[keylist->column_len - 1]; } else { diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c index 598779c6ace..6946c09e6f1 100644 --- a/source/blender/editors/curve/editcurve_paint.c +++ b/source/blender/editors/curve/editcurve_paint.c @@ -105,7 +105,7 @@ struct CurveDrawData { } radius; struct { - float mouse[2]; + float mval[2]; /* Used in case we can't calculate the depth. */ float location_world[3]; @@ -463,8 +463,8 @@ static void curve_draw_event_add(wmOperator *op, const wmEvent *event) } copy_v3_v3(cdd->prev.location_world, selem->location_world); - float len_sq = len_squared_v2v2(cdd->prev.mouse, selem->mval); - copy_v2_v2(cdd->prev.mouse, selem->mval); + float len_sq = len_squared_v2v2(cdd->prev.mval, selem->mval); + copy_v2_v2(cdd->prev.mval, selem->mval); if (cdd->sample.use_substeps && cdd->prev.selem) { const struct StrokeElem selem_target = *selem; @@ -1165,7 +1165,7 @@ static int curve_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { if (cdd->state == CURVE_DRAW_PAINTING) { const float mval_fl[2] = {UNPACK2(event->mval)}; - if (len_squared_v2v2(mval_fl, cdd->prev.mouse) > square_f(STROKE_SAMPLE_DIST_MIN_PX)) { + if (len_squared_v2v2(mval_fl, cdd->prev.mval) > square_f(STROKE_SAMPLE_DIST_MIN_PX)) { curve_draw_event_add(op, event); } } diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index 34ae7697d2d..9da2c4819a3 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -772,17 +772,17 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.curve.pen ops.curve.radius ops.curve.vertex_random - ops.curves.density ops.curves.sculpt_add ops.curves.sculpt_comb ops.curves.sculpt_cut ops.curves.sculpt_delete + ops.curves.sculpt_density ops.curves.sculpt_grow_shrink ops.curves.sculpt_pinch ops.curves.sculpt_puff + ops.curves.sculpt_slide ops.curves.sculpt_smooth ops.curves.sculpt_snake_hook - ops.curves.slide ops.generic.cursor ops.generic.select ops.generic.select_box @@ -933,6 +933,7 @@ if(WITH_BLENDER) data_to_c_simple(../../../../release/datafiles/brushicons/mask.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/mix.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/nudge.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/paint_select.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/pinch.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/scrape.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/smear.png SRC) @@ -970,6 +971,19 @@ if(WITH_BLENDER) data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_erase_hard.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_erase_stroke.png SRC) + # curve sculpt + data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_add.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_comb.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_cut.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_delete.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_density.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_grow_shrink.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_pinch.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_puff.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_slide.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_smooth.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_snake_hook.png SRC) + endif() data_to_c_simple(../../../../release/datafiles/startup.blend SRC) diff --git a/source/blender/editors/geometry/geometry_attributes.cc b/source/blender/editors/geometry/geometry_attributes.cc index 37ec7a61bcb..c7e782b7b89 100644 --- a/source/blender/editors/geometry/geometry_attributes.cc +++ b/source/blender/editors/geometry/geometry_attributes.cc @@ -518,15 +518,18 @@ static int geometry_color_attribute_duplicate_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); ID *id = static_cast<ID *>(ob->data); - CustomDataLayer *layer = BKE_id_attributes_active_color_get(id); + const CustomDataLayer *layer = BKE_id_attributes_active_color_get(id); if (layer == nullptr) { return OPERATOR_CANCELLED; } - CustomDataLayer *newLayer = BKE_id_attribute_duplicate(id, layer, op->reports); + CustomDataLayer *new_layer = BKE_id_attribute_duplicate(id, layer->name, op->reports); + if (new_layer == nullptr) { + return OPERATOR_CANCELLED; + } - BKE_id_attributes_active_color_set(id, newLayer); + BKE_id_attributes_active_color_set(id, new_layer); DEG_id_tag_update(id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_GEOM | ND_DATA, id); @@ -539,6 +542,10 @@ static bool geometry_color_attributes_duplicate_poll(bContext *C) if (!geometry_attributes_poll(C)) { return false; } + if (CTX_data_edit_object(C) != nullptr) { + CTX_wm_operator_poll_msg_set(C, "Operation is not allowed in edit mode"); + return false; + } Object *ob = ED_object_context(C); ID *data = ob ? static_cast<ID *>(ob->data) : nullptr; diff --git a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c index a0e30c7518a..11309402220 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c @@ -57,8 +57,6 @@ typedef struct ButtonGizmo2D { GPUBatch *shape_batch[2]; } ButtonGizmo2D; -#define CIRCLE_RESOLUTION 32 - /** \} */ /* -------------------------------------------------------------------- */ @@ -68,11 +66,17 @@ typedef struct ButtonGizmo2D { static void button2d_geom_draw_backdrop(const wmGizmo *gz, const float color[4], const float fill_alpha, - const bool select) + const bool select, + const float screen_scale) { float viewport[4]; GPU_viewport_size_get_f(viewport); + const float max_pixel_error = 0.25f; + int nsegments = (int)(ceilf(M_PI / acosf(1.0f - max_pixel_error / screen_scale))); + nsegments = max_ff(nsegments, 8); + nsegments = min_ff(nsegments, 1000); + GPUVertFormat *format = immVertexFormat(); /* NOTE(Metal): Prefer 3D coordinate for 2D rendering when using 3D shader. */ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); @@ -81,14 +85,14 @@ static void button2d_geom_draw_backdrop(const wmGizmo *gz, if (color[3] == 1.0 && fill_alpha == 1.0 && select == false) { immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor4fv(color); - imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION); + imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, nsegments); immUnbindProgram(); immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); immUniform2fv("viewportSize", &viewport[2]); immUniform1f("lineWidth", gz->line_width * U.pixelsize); immUniformColor4fv(color); - imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION); + imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, nsegments); immUnbindProgram(); } else { @@ -97,7 +101,7 @@ static void button2d_geom_draw_backdrop(const wmGizmo *gz, const float fill_color[4] = {UNPACK3(color), fill_alpha * color[3]}; immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor4fv(fill_color); - imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION); + imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, nsegments); immUnbindProgram(); } @@ -107,7 +111,7 @@ static void button2d_geom_draw_backdrop(const wmGizmo *gz, immUniform2fv("viewportSize", &viewport[2]); immUniform1f("lineWidth", gz->line_width * U.pixelsize); immUniformColor4fv(color); - imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION); + imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, nsegments); immUnbindProgram(); } } @@ -184,9 +188,11 @@ static void button2d_draw_intern(const bContext *C, GPU_matrix_mul(matrix_align); } + const float screen_scale = mat4_to_scale(matrix_final); + if (select) { BLI_assert(is_3d); - button2d_geom_draw_backdrop(gz, color, 1.0, select); + button2d_geom_draw_backdrop(gz, color, 1.0, select, screen_scale); } else { @@ -194,7 +200,7 @@ static void button2d_draw_intern(const bContext *C, if (draw_options & ED_GIZMO_BUTTON_SHOW_BACKDROP) { const float fill_alpha = RNA_float_get(gz->ptr, "backdrop_fill_alpha"); - button2d_geom_draw_backdrop(gz, color, fill_alpha, select); + button2d_geom_draw_backdrop(gz, color, fill_alpha, select, screen_scale); } if (button->shape_batch[0] != NULL) { @@ -315,10 +321,11 @@ static int gizmo_button2d_cursor_get(wmGizmo *gz) return WM_CURSOR_DEFAULT; } +#define CIRCLE_RESOLUTION_3D 32 static bool gizmo_button2d_bounds(bContext *C, wmGizmo *gz, rcti *r_bounding_box) { ScrArea *area = CTX_wm_area(C); - float rad = CIRCLE_RESOLUTION * U.dpi_fac / 2.0f; + float rad = CIRCLE_RESOLUTION_3D * U.dpi_fac / 2.0f; const float *co = NULL; float matrix_final[4][4]; float co_proj[3]; @@ -342,6 +349,7 @@ static bool gizmo_button2d_bounds(bContext *C, wmGizmo *gz, rcti *r_bounding_box } } else { + rad = mat4_to_scale(matrix_final); co = matrix_final[3]; } diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 4733da85291..fc5b3838900 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -2962,7 +2962,7 @@ void ED_gpencil_projected_2d_bound_box(const GP_SpaceConversion *gsc, bool ED_gpencil_stroke_check_collision(const GP_SpaceConversion *gsc, bGPDstroke *gps, - const float mouse[2], + const float mval[2], const int radius, const float diff_mat[4][4]) { @@ -2980,7 +2980,7 @@ bool ED_gpencil_stroke_check_collision(const GP_SpaceConversion *gsc, rcti rect_stroke = {boundbox_min[0], boundbox_max[0], boundbox_min[1], boundbox_max[1]}; /* For mouse, add a small offset to avoid false negative in corners. */ - rcti rect_mouse = {mouse[0] - offset, mouse[0] + offset, mouse[1] - offset, mouse[1] + offset}; + rcti rect_mouse = {mval[0] - offset, mval[0] + offset, mval[1] - offset, mval[1] + offset}; /* Check collision between both rectangles. */ return BLI_rcti_isect(&rect_stroke, &rect_mouse, NULL); @@ -2988,7 +2988,7 @@ bool ED_gpencil_stroke_check_collision(const GP_SpaceConversion *gsc, bool ED_gpencil_stroke_point_is_inside(const bGPDstroke *gps, const GP_SpaceConversion *gsc, - const int mouse[2], + const int mval[2], const float diff_mat[4][4]) { bool hit = false; @@ -3014,9 +3014,8 @@ bool ED_gpencil_stroke_point_is_inside(const bGPDstroke *gps, BLI_lasso_boundbox(&rect, mcoords, len); /* Test if point inside stroke. */ - hit = ((!ELEM(V2D_IS_CLIPPED, mouse[0], mouse[1])) && - BLI_rcti_isect_pt(&rect, mouse[0], mouse[1]) && - BLI_lasso_is_point_inside(mcoords, len, mouse[0], mouse[1], INT_MAX)); + hit = ((!ELEM(V2D_IS_CLIPPED, mval[0], mval[1])) && BLI_rcti_isect_pt(&rect, mval[0], mval[1]) && + BLI_lasso_is_point_inside(mcoords, len, mval[0], mval[1], INT_MAX)); /* Free memory. */ MEM_SAFE_FREE(mcoords); diff --git a/source/blender/editors/gpencil/gpencil_vertex_ops.c b/source/blender/editors/gpencil/gpencil_vertex_ops.c index c0888968a2d..865c4e360b5 100644 --- a/source/blender/editors/gpencil/gpencil_vertex_ops.c +++ b/source/blender/editors/gpencil/gpencil_vertex_ops.c @@ -587,7 +587,7 @@ static int gpencil_vertexpaint_set_exec(bContext *C, wmOperator *op) srgb_to_linearrgb_v4(color, color); for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { if ((!any_selected) || (pt->flag & GP_SPOINT_SELECT)) { - copy_v3_v3(pt->vert_color, color); + copy_v4_v4(pt->vert_color, color); } } } diff --git a/source/blender/editors/include/ED_datafiles.h b/source/blender/editors/include/ED_datafiles.h index c3a66e4a7dd..e3b091430e8 100644 --- a/source/blender/editors/include/ED_datafiles.h +++ b/source/blender/editors/include/ED_datafiles.h @@ -103,6 +103,9 @@ extern const char datatoc_multiply_png[]; extern int datatoc_nudge_png_size; extern const char datatoc_nudge_png[]; +extern int datatoc_paint_select_png_size; +extern const char datatoc_paint_select_png[]; + extern int datatoc_pinch_png_size; extern const char datatoc_pinch_png[]; @@ -284,6 +287,41 @@ extern const char datatoc_gp_brush_erase_hard_png[]; extern int datatoc_gp_brush_erase_stroke_png_size; extern const char datatoc_gp_brush_erase_stroke_png[]; +/* curves sculpt brushes files */ + +extern int datatoc_curves_sculpt_add_png_size; +extern const char datatoc_curves_sculpt_add_png[]; + +extern int datatoc_curves_sculpt_comb_png_size; +extern const char datatoc_curves_sculpt_comb_png[]; + +extern int datatoc_curves_sculpt_cut_png_size; +extern const char datatoc_curves_sculpt_cut_png[]; + +extern int datatoc_curves_sculpt_delete_png_size; +extern const char datatoc_curves_sculpt_delete_png[]; + +extern int datatoc_curves_sculpt_density_png_size; +extern const char datatoc_curves_sculpt_density_png[]; + +extern int datatoc_curves_sculpt_grow_shrink_png_size; +extern const char datatoc_curves_sculpt_grow_shrink_png[]; + +extern int datatoc_curves_sculpt_pinch_png_size; +extern const char datatoc_curves_sculpt_pinch_png[]; + +extern int datatoc_curves_sculpt_puff_png_size; +extern const char datatoc_curves_sculpt_puff_png[]; + +extern int datatoc_curves_sculpt_slide_png_size; +extern const char datatoc_curves_sculpt_slide_png[]; + +extern int datatoc_curves_sculpt_smooth_png_size; +extern const char datatoc_curves_sculpt_smooth_png[]; + +extern int datatoc_curves_sculpt_snake_hook_png_size; +extern const char datatoc_curves_sculpt_snake_hook_png[]; + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index 01f60a81288..0943636a452 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -579,7 +579,7 @@ void ED_gpencil_init_random_settings(struct Brush *brush, */ bool ED_gpencil_stroke_check_collision(const struct GP_SpaceConversion *gsc, struct bGPDstroke *gps, - const float mouse[2], + const float mval[2], int radius, const float diff_mat[4][4]); /** @@ -587,13 +587,13 @@ bool ED_gpencil_stroke_check_collision(const struct GP_SpaceConversion *gsc, * * \param gps: Stroke to check. * \param gsc: Space conversion data. - * \param mouse: Mouse position. + * \param mval: Region relative cursor position. * \param diff_mat: View matrix. * \return True if the point is inside. */ bool ED_gpencil_stroke_point_is_inside(const struct bGPDstroke *gps, const struct GP_SpaceConversion *gsc, - const int mouse[2], + const int mval[2], const float diff_mat[4][4]); /** * Get the bigger 2D bound box points. diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h index 54d67c50d5c..550040d2bc6 100644 --- a/source/blender/editors/include/ED_sculpt.h +++ b/source/blender/editors/include/ED_sculpt.h @@ -51,7 +51,7 @@ void ED_sculpt_face_sets_initialize_none_to_id(struct Mesh *mesh, int new_id); int ED_sculpt_face_sets_active_update_and_get(struct bContext *C, struct Object *ob, - const float mval[2]); + const float mval_fl[2]); /* Undo for changes happening on a base mesh for multires sculpting. * if there is no multi-res sculpt active regular undo is used. */ diff --git a/source/blender/editors/include/UI_grid_view.hh b/source/blender/editors/include/UI_grid_view.hh new file mode 100644 index 00000000000..6f553f4fad1 --- /dev/null +++ b/source/blender/editors/include/UI_grid_view.hh @@ -0,0 +1,264 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup editorui + * + * API for simple creation of grid UIs, supporting typically needed features. + * https://wiki.blender.org/wiki/Source/Interface/Views/Grid_Views + */ + +#pragma once + +#include "BLI_function_ref.hh" +#include "BLI_map.hh" +#include "BLI_vector.hh" + +#include "UI_resources.h" + +struct bContext; +struct PreviewImage; +struct uiBlock; +struct uiButGridTile; +struct uiLayout; +struct View2D; +struct wmNotifier; + +namespace blender::ui { + +class AbstractGridView; + +/* ---------------------------------------------------------------------- */ +/** \name Grid-View Item Type + * \{ */ + +class AbstractGridViewItem { + friend class AbstractGridView; + friend class GridViewLayoutBuilder; + + const AbstractGridView *view_; + + bool is_active_ = false; + + protected: + /** Reference to a string that uniquely identifies this item in the view. */ + StringRef identifier_{}; + /** Every visible item gets a button of type #UI_BTYPE_GRID_TILE during the layout building. */ + uiButGridTile *grid_tile_but_ = nullptr; + + public: + virtual ~AbstractGridViewItem() = default; + + virtual void build_grid_tile(uiLayout &layout) const = 0; + + /** + * Compare this item's identifier to \a other to check if they represent the same data. + * Used to recognize an item from a previous redraw, to be able to keep its state (e.g. active, + * renaming, etc.). + */ + bool matches(const AbstractGridViewItem &other) const; + + const AbstractGridView &get_view() const; + + /** + * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise we + * can't be sure about the item state. + */ + bool is_active() const; + + protected: + AbstractGridViewItem(StringRef identifier); + + /** Called when the item's state changes from inactive to active. */ + virtual void on_activate(); + /** + * If the result is not empty, it controls whether the item should be active or not, + * usually depending on the data that the view represents. + */ + virtual std::optional<bool> should_be_active() const; + + /** + * Copy persistent state (e.g. active, selection, etc.) from a matching item of + * the last redraw to this item. If sub-classes introduce more advanced state they should + * override this and make it update their state accordingly. + */ + virtual void update_from_old(const AbstractGridViewItem &old); + + /** + * Activates this item, deactivates other items, and calls the + * #AbstractGridViewItem::on_activate() function. + * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise the + * actual item state is unknown, possibly calling state-change update functions incorrectly. + */ + void activate(); + void deactivate(); + + private: + /** See #AbstractTreeView::change_state_delayed() */ + void change_state_delayed(); + static void grid_tile_click_fn(bContext *, void *but_arg1, void *); + void add_grid_tile_button(uiBlock &block); +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Grid-View Base Class + * \{ */ + +struct GridViewStyle { + GridViewStyle(int width, int height); + int tile_width = 0; + int tile_height = 0; +}; + +class AbstractGridView { + friend class AbstractGridViewItem; + friend class GridViewBuilder; + friend class GridViewLayoutBuilder; + + protected: + Vector<std::unique_ptr<AbstractGridViewItem>> items_; + /** <identifier, item> map to lookup items by identifier, used for efficient lookups in + * #update_from_old(). */ + Map<StringRef, AbstractGridViewItem *> item_map_; + GridViewStyle style_; + bool is_reconstructed_ = false; + + public: + AbstractGridView(); + virtual ~AbstractGridView() = default; + + using ItemIterFn = FunctionRef<void(AbstractGridViewItem &)>; + void foreach_item(ItemIterFn iter_fn) const; + + /** Listen to a notifier, returning true if a redraw is needed. */ + virtual bool listen(const wmNotifier &) const; + + /** + * Convenience wrapper constructing the item by forwarding given arguments to the constructor of + * the type (\a ItemT). + * + * E.g. if your grid-item type has the following constructor: + * \code{.cpp} + * MyGridItem(std::string str, int i); + * \endcode + * You can add an item like this: + * \code + * add_item<MyGridItem>("blabla", 42); + * \endcode + */ + template<class ItemT, typename... Args> inline ItemT &add_item(Args &&...args); + const GridViewStyle &get_style() const; + int get_item_count() const; + + protected: + virtual void build_items() = 0; + + /** + * Check if the view is fully (re-)constructed. That means, both #build_items() and + * #update_from_old() have finished. + */ + bool is_reconstructed() const; + + private: + /** + * Match the grid-view against an earlier version of itself (if any) and copy the old UI state + * (e.g. active, selected, renaming, etc.) to the new one. See + * #AbstractGridViewItem.update_from_old(). + */ + void update_from_old(uiBlock &new_block); + AbstractGridViewItem *find_matching_item(const AbstractGridViewItem &item_to_match, + const AbstractGridView &view_to_search_in) const; + /** + * Items may want to do additional work when state changes. But these state changes can only be + * reliably detected after the view has completed reconstruction (see #is_reconstructed()). So + * the actual state changes are done in a delayed manner through this function. + */ + void change_state_delayed(); + + /** + * Add an already constructed item, moving ownership to the grid-view. + * All items must be added through this, it handles important invariants! + */ + AbstractGridViewItem &add_item(std::unique_ptr<AbstractGridViewItem> item); +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Grid-View Builder + * + * TODO unify this with `TreeViewBuilder` and call view-specific functions via type erased view? + * \{ */ + +class GridViewBuilder { + uiBlock &block_; + + public: + GridViewBuilder(uiBlock &block); + + /** Build \a grid_view into the previously provided block, clipped by \a view_bounds (view space, + * typically `View2D.cur`). */ + void build_grid_view(AbstractGridView &grid_view, const View2D &v2d); +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Predefined Grid-View Item Types + * + * Common, Basic Grid-View Item Types. + * \{ */ + +/** + * A grid item that shows preview image icons at a nicely readable size (multiple of the normal UI + * unit size). + */ +class PreviewGridItem : public AbstractGridViewItem { + public: + using IsActiveFn = std::function<bool()>; + using ActivateFn = std::function<void(PreviewGridItem &new_active)>; + + protected: + /** See #set_on_activate_fn() */ + ActivateFn activate_fn_; + /** See #set_is_active_fn() */ + IsActiveFn is_active_fn_; + + public: + std::string label{}; + int preview_icon_id = ICON_NONE; + + PreviewGridItem(StringRef identifier, StringRef label, int preview_icon_id); + + void build_grid_tile(uiLayout &layout) const override; + + /** + * Set a custom callback to execute when activating this view item. This way users don't have to + * sub-class #PreviewGridItem, just to implement custom activation behavior (a common thing to + * do). + */ + void set_on_activate_fn(ActivateFn fn); + /** + * Set a custom callback to check if this item should be active. + */ + void set_is_active_fn(IsActiveFn fn); + + private: + std::optional<bool> should_be_active() const override; + void on_activate() override; +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ + +template<class ItemT, typename... Args> inline ItemT &AbstractGridView::add_item(Args &&...args) +{ + static_assert(std::is_base_of<AbstractGridViewItem, ItemT>::value, + "Type must derive from and implement the AbstractGridViewItem interface"); + + return dynamic_cast<ItemT &>(add_item(std::make_unique<ItemT>(std::forward<Args>(args)...))); +} + +} // namespace blender::ui diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index d650f4215b9..f1c0acf43f7 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -891,6 +891,7 @@ DEF_ICON_COLOR(BRUSH_LAYER) DEF_ICON_COLOR(BRUSH_MASK) DEF_ICON_COLOR(BRUSH_MIX) DEF_ICON_COLOR(BRUSH_NUDGE) +DEF_ICON_COLOR(BRUSH_PAINT_SELECT) DEF_ICON_COLOR(BRUSH_PINCH) DEF_ICON_COLOR(BRUSH_SCRAPE) DEF_ICON_COLOR(BRUSH_SCULPT_DRAW) @@ -928,6 +929,19 @@ DEF_ICON_COLOR(GPBRUSH_ERASE_SOFT) DEF_ICON_COLOR(GPBRUSH_ERASE_HARD) DEF_ICON_COLOR(GPBRUSH_ERASE_STROKE) +/* Curves sculpt. */ +DEF_ICON_COLOR(BRUSH_CURVES_ADD) +DEF_ICON_COLOR(BRUSH_CURVES_COMB) +DEF_ICON_COLOR(BRUSH_CURVES_CUT) +DEF_ICON_COLOR(BRUSH_CURVES_DELETE) +DEF_ICON_COLOR(BRUSH_CURVES_DENSITY) +DEF_ICON_COLOR(BRUSH_CURVES_GROW_SHRINK) +DEF_ICON_COLOR(BRUSH_CURVES_PINCH) +DEF_ICON_COLOR(BRUSH_CURVES_PUFF) +DEF_ICON_COLOR(BRUSH_CURVES_SLIDE) +DEF_ICON_COLOR(BRUSH_CURVES_SMOOTH) +DEF_ICON_COLOR(BRUSH_CURVES_SNAKE_HOOK) + /* Vector icons. */ DEF_ICON_VECTOR(KEYTYPE_KEYFRAME_VEC) DEF_ICON_VECTOR(KEYTYPE_BREAKDOWN_VEC) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 07232a478dc..984d3409554 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -64,6 +64,7 @@ struct wmKeyMapItem; struct wmMsgBus; struct wmOperator; struct wmOperatorType; +struct wmRegionListenerParams; struct wmWindow; typedef struct uiBlock uiBlock; @@ -75,6 +76,10 @@ typedef struct uiPopupBlockHandle uiPopupBlockHandle; typedef struct uiTreeViewHandle uiTreeViewHandle; /* C handle for C++ #ui::AbstractTreeViewItem type. */ typedef struct uiTreeViewItemHandle uiTreeViewItemHandle; +/* C handle for C++ #ui::AbstractGridView type. */ +typedef struct uiGridViewHandle uiGridViewHandle; +/* C handle for C++ #ui::AbstractGridViewItem type. */ +typedef struct uiGridViewItemHandle uiGridViewItemHandle; /* Defines */ @@ -390,6 +395,8 @@ typedef enum { UI_BTYPE_DECORATOR = 58 << 9, /* An item in a tree view. Parent items may be collapsible. */ UI_BTYPE_TREEROW = 59 << 9, + /* An item in a grid view. */ + UI_BTYPE_GRID_TILE = 60 << 9, } eButType; #define BUTTYPE (63 << 9) @@ -851,33 +858,6 @@ void UI_block_translate(uiBlock *block, int x, int y); int UI_but_return_value_get(uiBut *but); -void UI_but_drag_set_id(uiBut *but, struct ID *id); -/** - * Set an image to display while dragging. This works for any drag type (`WM_DRAG_XXX`). - * Not to be confused with #UI_but_drag_set_image(), which sets up dragging of an image. - */ -void UI_but_drag_attach_image(uiBut *but, struct ImBuf *imb, float scale); -/** - * \param asset: May be passed from a temporary variable, drag data only stores a copy of this. - */ -void UI_but_drag_set_asset(uiBut *but, - const struct AssetHandle *asset, - const char *path, - struct AssetMetaData *metadata, - int import_type, /* eFileAssetImportType */ - int icon, - struct ImBuf *imb, - float scale); -void UI_but_drag_set_rna(uiBut *but, struct PointerRNA *ptr); -void UI_but_drag_set_path(uiBut *but, const char *path, bool use_free); -void UI_but_drag_set_name(uiBut *but, const char *name); -/** - * Value from button itself. - */ -void UI_but_drag_set_value(uiBut *but); -void UI_but_drag_set_image( - uiBut *but, const char *path, int icon, struct ImBuf *imb, float scale, bool use_free); - uiBut *UI_but_active_drop_name_button(const struct bContext *C); /** * Returns true if highlighted button allows drop of names. @@ -1767,6 +1747,14 @@ struct PointerRNA *UI_but_extra_operator_icon_add(uiBut *but, struct wmOperatorType *UI_but_extra_operator_icon_optype_get(struct uiButExtraOpIcon *extra_icon); struct PointerRNA *UI_but_extra_operator_icon_opptr_get(struct uiButExtraOpIcon *extra_icon); +/** + * A decent size for a button (typically #UI_BTYPE_PREVIEW_TILE) to display a nicely readable + * preview with label in. + */ +int UI_preview_tile_size_x(void); +int UI_preview_tile_size_y(void); +int UI_preview_tile_size_y_no_label(void); + /* Autocomplete * * Tab complete helper functions, for use in uiButCompleteFunc callbacks. @@ -1783,6 +1771,38 @@ AutoComplete *UI_autocomplete_begin(const char *startname, size_t maxlen); void UI_autocomplete_update_name(AutoComplete *autocpl, const char *name); int UI_autocomplete_end(AutoComplete *autocpl, char *autoname); +/* Button drag-data (interface_drag.cc). + * + * Functions to set drag data for buttons. This enables dragging support, whereby the drag data is + * "dragged", not the button itself. */ + +void UI_but_drag_set_id(uiBut *but, struct ID *id); +/** + * Set an image to display while dragging. This works for any drag type (`WM_DRAG_XXX`). + * Not to be confused with #UI_but_drag_set_image(), which sets up dragging of an image. + */ +void UI_but_drag_attach_image(uiBut *but, struct ImBuf *imb, float scale); +/** + * \param asset: May be passed from a temporary variable, drag data only stores a copy of this. + */ +void UI_but_drag_set_asset(uiBut *but, + const struct AssetHandle *asset, + const char *path, + struct AssetMetaData *metadata, + int import_type, /* eFileAssetImportType */ + int icon, + struct ImBuf *imb, + float scale); +void UI_but_drag_set_rna(uiBut *but, struct PointerRNA *ptr); +void UI_but_drag_set_path(uiBut *but, const char *path, bool use_free); +void UI_but_drag_set_name(uiBut *but, const char *name); +/** + * Value from button itself. + */ +void UI_but_drag_set_value(uiBut *but); +void UI_but_drag_set_image( + uiBut *but, const char *path, int icon, struct ImBuf *imb, float scale, bool use_free); + /* Panels * * Functions for creating, freeing and drawing panels. The API here @@ -3181,7 +3201,12 @@ void UI_interface_tag_script_reload(void); /* Support click-drag motion which presses the button and closes a popover (like a menu). */ #define USE_UI_POPOVER_ONCE +void UI_block_views_listen(const uiBlock *block, + const struct wmRegionListenerParams *listener_params); + +bool UI_grid_view_item_is_active(const uiGridViewItemHandle *item_handle); bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item); +bool UI_grid_view_item_matches(const uiGridViewItemHandle *a, const uiGridViewItemHandle *b); bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a, const uiTreeViewItemHandle *b); /** * Attempt to start dragging the tree-item \a item_. This will not work if the tree item doesn't @@ -3219,6 +3244,15 @@ uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const struct ARegion *regi const int xy[2]) ATTR_NONNULL(1, 2); uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const struct ARegion *region); +/** + * Listen to \a notifier, returning true if the region should redraw. + */ +bool UI_tree_view_listen_should_redraw(const uiTreeViewHandle *view, const wmNotifier *notifier); +/** + * Listen to \a notifier, returning true if the region should redraw. + */ +bool UI_grid_view_listen_should_redraw(const uiGridViewHandle *view, const wmNotifier *notifier); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/include/UI_interface.hh b/source/blender/editors/include/UI_interface.hh index db43ec54431..3dc56b01993 100644 --- a/source/blender/editors/include/UI_interface.hh +++ b/source/blender/editors/include/UI_interface.hh @@ -23,6 +23,7 @@ struct uiSearchItems; namespace blender::ui { +class AbstractGridView; class AbstractTreeView; /** @@ -55,6 +56,10 @@ void attribute_search_add_items( /** * Override this for all available tree types. */ +blender::ui::AbstractGridView *UI_block_add_view( + uiBlock &block, + blender::StringRef idname, + std::unique_ptr<blender::ui::AbstractGridView> tree_view); blender::ui::AbstractTreeView *UI_block_add_view( uiBlock &block, blender::StringRef idname, diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index 5504e426f34..1aeb13ca5cc 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -28,6 +28,7 @@ struct uiButTreeRow; struct uiLayout; struct wmDrag; struct wmEvent; +struct wmNotifier; namespace blender::ui { @@ -128,6 +129,9 @@ class AbstractTreeView : public TreeViewItemContainer { void foreach_item(ItemIterFn iter_fn, IterOptions options = IterOptions::None) const; + /** Listen to a notifier, returning true if a redraw is needed. */ + virtual bool listen(const wmNotifier &) const; + /** Only one item can be renamed at a time. */ bool is_renaming() const; @@ -185,7 +189,7 @@ class AbstractTreeViewItem : public TreeViewItemContainer { bool is_renaming_ = false; protected: - /** This label is used for identifying an item within its parent. */ + /** This label is used as the default way to identifying an item within its parent. */ std::string label_{}; /** Every visible item gets a button of type #UI_BTYPE_TREEROW during the layout building. */ uiButTreeRow *tree_row_but_ = nullptr; diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 56df79aac77..a140ee27d3c 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -25,12 +25,14 @@ set(INC ) set(SRC + grid_view.cc interface.cc interface_align.c interface_anim.c interface_button_group.c interface_context_menu.c interface_context_path.cc + interface_drag.cc interface_draw.c interface_dropboxes.cc interface_eyedropper.c diff --git a/source/blender/editors/interface/grid_view.cc b/source/blender/editors/interface/grid_view.cc new file mode 100644 index 00000000000..a82cb7798fe --- /dev/null +++ b/source/blender/editors/interface/grid_view.cc @@ -0,0 +1,525 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edinterface + */ + +#include <limits> +#include <stdexcept> + +#include "BLI_index_range.hh" + +#include "WM_types.h" + +#include "UI_interface.h" +#include "interface_intern.h" + +#include "UI_grid_view.hh" + +namespace blender::ui { + +/* ---------------------------------------------------------------------- */ + +AbstractGridView::AbstractGridView() : style_(UI_preview_tile_size_x(), UI_preview_tile_size_y()) +{ +} + +AbstractGridViewItem &AbstractGridView::add_item(std::unique_ptr<AbstractGridViewItem> item) +{ + items_.append(std::move(item)); + + AbstractGridViewItem &added_item = *items_.last(); + added_item.view_ = this; + + item_map_.add(added_item.identifier_, &added_item); + + return added_item; +} + +void AbstractGridView::foreach_item(ItemIterFn iter_fn) const +{ + for (auto &item_ptr : items_) { + iter_fn(*item_ptr); + } +} + +bool AbstractGridView::listen(const wmNotifier &) const +{ + /* Nothing by default. */ + return false; +} + +AbstractGridViewItem *AbstractGridView::find_matching_item( + const AbstractGridViewItem &item_to_match, const AbstractGridView &view_to_search_in) const +{ + AbstractGridViewItem *const *match = view_to_search_in.item_map_.lookup_ptr( + item_to_match.identifier_); + BLI_assert(!match || item_to_match.matches(**match)); + + return match ? *match : nullptr; +} + +void AbstractGridView::change_state_delayed() +{ + BLI_assert_msg( + is_reconstructed(), + "These state changes are supposed to be delayed until reconstruction is completed"); + foreach_item([](AbstractGridViewItem &item) { item.change_state_delayed(); }); +} + +void AbstractGridView::update_from_old(uiBlock &new_block) +{ + uiGridViewHandle *old_view_handle = ui_block_grid_view_find_matching_in_old_block( + &new_block, reinterpret_cast<uiGridViewHandle *>(this)); + if (!old_view_handle) { + /* Initial construction, nothing to update. */ + is_reconstructed_ = true; + return; + } + + AbstractGridView &old_view = reinterpret_cast<AbstractGridView &>(*old_view_handle); + + foreach_item([this, &old_view](AbstractGridViewItem &new_item) { + const AbstractGridViewItem *matching_old_item = find_matching_item(new_item, old_view); + if (!matching_old_item) { + return; + } + + new_item.update_from_old(*matching_old_item); + }); + + /* Finished (re-)constructing the tree. */ + is_reconstructed_ = true; +} + +bool AbstractGridView::is_reconstructed() const +{ + return is_reconstructed_; +} + +const GridViewStyle &AbstractGridView::get_style() const +{ + return style_; +} + +int AbstractGridView::get_item_count() const +{ + return items_.size(); +} + +GridViewStyle::GridViewStyle(int width, int height) : tile_width(width), tile_height(height) +{ +} + +/* ---------------------------------------------------------------------- */ + +AbstractGridViewItem::AbstractGridViewItem(StringRef identifier) : identifier_(identifier) +{ +} + +bool AbstractGridViewItem::matches(const AbstractGridViewItem &other) const +{ + return identifier_ == other.identifier_; +} + +void AbstractGridViewItem::grid_tile_click_fn(struct bContext * /*C*/, + void *but_arg1, + void * /*arg2*/) +{ + uiButGridTile *grid_tile_but = (uiButGridTile *)but_arg1; + AbstractGridViewItem &grid_item = reinterpret_cast<AbstractGridViewItem &>( + *grid_tile_but->view_item); + + grid_item.activate(); +} + +void AbstractGridViewItem::add_grid_tile_button(uiBlock &block) +{ + const GridViewStyle &style = get_view().get_style(); + grid_tile_but_ = (uiButGridTile *)uiDefBut(&block, + UI_BTYPE_GRID_TILE, + 0, + "", + 0, + 0, + style.tile_width, + style.tile_height, + nullptr, + 0, + 0, + 0, + 0, + ""); + + grid_tile_but_->view_item = reinterpret_cast<uiGridViewItemHandle *>(this); + UI_but_func_set(&grid_tile_but_->but, grid_tile_click_fn, grid_tile_but_, nullptr); +} + +bool AbstractGridViewItem::is_active() const +{ + BLI_assert_msg(get_view().is_reconstructed(), + "State can't be queried until reconstruction is completed"); + return is_active_; +} + +void AbstractGridViewItem::on_activate() +{ + /* Do nothing by default. */ +} + +std::optional<bool> AbstractGridViewItem::should_be_active() const +{ + return std::nullopt; +} + +void AbstractGridViewItem::change_state_delayed() +{ + const std::optional<bool> should_be_active = this->should_be_active(); + if (should_be_active.has_value() && *should_be_active) { + activate(); + } +} + +void AbstractGridViewItem::update_from_old(const AbstractGridViewItem &old) +{ + is_active_ = old.is_active_; +} + +void AbstractGridViewItem::activate() +{ + BLI_assert_msg(get_view().is_reconstructed(), + "Item activation can't be done until reconstruction is completed"); + + if (is_active()) { + return; + } + + /* Deactivate other items in the tree. */ + get_view().foreach_item([](auto &item) { item.deactivate(); }); + + on_activate(); + + is_active_ = true; +} + +void AbstractGridViewItem::deactivate() +{ + is_active_ = false; +} + +const AbstractGridView &AbstractGridViewItem::get_view() const +{ + if (UNLIKELY(!view_)) { + throw std::runtime_error( + "Invalid state, item must be added through AbstractGridView::add_item()"); + } + return *view_; +} + +/* ---------------------------------------------------------------------- */ + +/** + * Helper for only adding layout items for grid items that are actually in view. 3 main functions: + * - #is_item_visible(): Query if an item of a given index is visible in the view (others should be + * skipped when building the layout). + * - #fill_layout_before_visible(): Add empty space to the layout before a visible row is drawn, so + * the layout height is the same as if all items were added (important to get the correct scroll + * height). + * - #fill_layout_after_visible(): Same thing, just adds empty space for after the last visible + * row. + * + * Does two assumptions: + * - Top-to-bottom flow (ymax = 0 and ymin < 0). If that's not good enough, View2D should + * probably provide queries for the scroll offset. + * - Only vertical scrolling. For horizontal scrolling, spacers would have to be added on the + * side(s) as well. + */ +class BuildOnlyVisibleButtonsHelper { + const View2D &v2d_; + const AbstractGridView &grid_view_; + const GridViewStyle &style_; + const int cols_per_row_ = 0; + /* Indices of items within the view. Calculated by constructor */ + IndexRange visible_items_range_{}; + + public: + BuildOnlyVisibleButtonsHelper(const View2D &, + const AbstractGridView &grid_view, + int cols_per_row); + + bool is_item_visible(int item_idx) const; + void fill_layout_before_visible(uiBlock &) const; + void fill_layout_after_visible(uiBlock &) const; + + private: + IndexRange get_visible_range() const; + void add_spacer_button(uiBlock &, int row_count) const; +}; + +BuildOnlyVisibleButtonsHelper::BuildOnlyVisibleButtonsHelper(const View2D &v2d, + const AbstractGridView &grid_view, + const int cols_per_row) + : v2d_(v2d), grid_view_(grid_view), style_(grid_view.get_style()), cols_per_row_(cols_per_row) +{ + visible_items_range_ = get_visible_range(); +} + +IndexRange BuildOnlyVisibleButtonsHelper::get_visible_range() const +{ + int first_idx_in_view = 0; + int max_items_in_view = 0; + + const float scroll_ofs_y = abs(v2d_.cur.ymax - v2d_.tot.ymax); + if (!IS_EQF(scroll_ofs_y, 0)) { + const int scrolled_away_rows = (int)scroll_ofs_y / style_.tile_height; + + first_idx_in_view = scrolled_away_rows * cols_per_row_; + } + + const float view_height = BLI_rctf_size_y(&v2d_.cur); + const int count_rows_in_view = std::max(round_fl_to_int(view_height / style_.tile_height), 1); + max_items_in_view = (count_rows_in_view + 1) * cols_per_row_; + + BLI_assert(max_items_in_view > 0); + return IndexRange(first_idx_in_view, max_items_in_view); +} + +bool BuildOnlyVisibleButtonsHelper::is_item_visible(const int item_idx) const +{ + return visible_items_range_.contains(item_idx); +} + +void BuildOnlyVisibleButtonsHelper::fill_layout_before_visible(uiBlock &block) const +{ + const float scroll_ofs_y = abs(v2d_.cur.ymax - v2d_.tot.ymax); + + if (IS_EQF(scroll_ofs_y, 0)) { + return; + } + + const int scrolled_away_rows = (int)scroll_ofs_y / style_.tile_height; + add_spacer_button(block, scrolled_away_rows); +} + +void BuildOnlyVisibleButtonsHelper::fill_layout_after_visible(uiBlock &block) const +{ + const int last_item_idx = grid_view_.get_item_count() - 1; + const int last_visible_idx = visible_items_range_.last(); + + if (last_item_idx > last_visible_idx) { + const int remaining_rows = (cols_per_row_ > 0) ? + (last_item_idx - last_visible_idx) / cols_per_row_ : + 0; + BuildOnlyVisibleButtonsHelper::add_spacer_button(block, remaining_rows); + } +} + +void BuildOnlyVisibleButtonsHelper::add_spacer_button(uiBlock &block, const int row_count) const +{ + /* UI code only supports button dimensions of `signed short` size, the layout height we want to + * fill may be bigger than that. So add multiple labels of the maximum size if necessary. */ + for (int remaining_rows = row_count; remaining_rows > 0;) { + const short row_count_this_iter = std::min( + std::numeric_limits<short>::max() / style_.tile_height, remaining_rows); + + uiDefBut(&block, + UI_BTYPE_LABEL, + 0, + "", + 0, + 0, + UI_UNIT_X, + row_count_this_iter * style_.tile_height, + nullptr, + 0, + 0, + 0, + 0, + ""); + remaining_rows -= row_count_this_iter; + } +} + +/* ---------------------------------------------------------------------- */ + +class GridViewLayoutBuilder { + uiBlock &block_; + + friend class GridViewBuilder; + + public: + GridViewLayoutBuilder(uiBlock &block); + + void build_from_view(const AbstractGridView &grid_view, const View2D &v2d) const; + + private: + void build_grid_tile(uiLayout &grid_layout, AbstractGridViewItem &item) const; + + uiLayout *current_layout() const; +}; + +GridViewLayoutBuilder::GridViewLayoutBuilder(uiBlock &block) : block_(block) +{ +} + +void GridViewLayoutBuilder::build_grid_tile(uiLayout &grid_layout, + AbstractGridViewItem &item) const +{ + uiLayout *overlap = uiLayoutOverlap(&grid_layout); + + item.add_grid_tile_button(block_); + item.build_grid_tile(*uiLayoutRow(overlap, false)); +} + +void GridViewLayoutBuilder::build_from_view(const AbstractGridView &grid_view, + const View2D &v2d) const +{ + uiLayout *prev_layout = current_layout(); + + uiLayout &layout = *uiLayoutColumn(current_layout(), false); + const GridViewStyle &style = grid_view.get_style(); + + const int cols_per_row = std::max(uiLayoutGetWidth(&layout) / style.tile_width, 1); + + BuildOnlyVisibleButtonsHelper build_visible_helper(v2d, grid_view, cols_per_row); + + build_visible_helper.fill_layout_before_visible(block_); + + /* Use `-cols_per_row` because the grid layout uses a multiple of the passed absolute value for + * the number of columns then, rather than distributing the number of items evenly over rows and + * stretching the items to fit (see #uiLayoutItemGridFlow.columns_len). */ + uiLayout *grid_layout = uiLayoutGridFlow(&layout, true, -cols_per_row, true, true, true); + + int item_idx = 0; + grid_view.foreach_item([&](AbstractGridViewItem &item) { + /* Skip if item isn't visible. */ + if (!build_visible_helper.is_item_visible(item_idx)) { + item_idx++; + return; + } + + build_grid_tile(*grid_layout, item); + item_idx++; + }); + + /* If there are not enough items to fill the layout, add padding items so the layout doesn't + * stretch over the entire width. */ + if (grid_view.get_item_count() < cols_per_row) { + for (int padding_item_idx = 0; padding_item_idx < (cols_per_row - grid_view.get_item_count()); + padding_item_idx++) { + uiItemS(grid_layout); + } + } + + UI_block_layout_set_current(&block_, prev_layout); + + build_visible_helper.fill_layout_after_visible(block_); +} + +uiLayout *GridViewLayoutBuilder::current_layout() const +{ + return block_.curlayout; +} + +/* ---------------------------------------------------------------------- */ + +GridViewBuilder::GridViewBuilder(uiBlock &block) : block_(block) +{ +} + +void GridViewBuilder::build_grid_view(AbstractGridView &grid_view, const View2D &v2d) +{ + grid_view.build_items(); + grid_view.update_from_old(block_); + grid_view.change_state_delayed(); + + GridViewLayoutBuilder builder(block_); + builder.build_from_view(grid_view, v2d); +} + +/* ---------------------------------------------------------------------- */ + +PreviewGridItem::PreviewGridItem(StringRef identifier, StringRef label, int preview_icon_id) + : AbstractGridViewItem(identifier), label(label), preview_icon_id(preview_icon_id) +{ +} + +void PreviewGridItem::build_grid_tile(uiLayout &layout) const +{ + const GridViewStyle &style = get_view().get_style(); + uiBlock *block = uiLayoutGetBlock(&layout); + + uiBut *but = uiDefBut(block, + UI_BTYPE_PREVIEW_TILE, + 0, + label.c_str(), + 0, + 0, + style.tile_width, + style.tile_height, + nullptr, + 0, + 0, + 0, + 0, + ""); + ui_def_but_icon(but, + preview_icon_id, + /* NOLINTNEXTLINE: bugprone-suspicious-enum-usage */ + UI_HAS_ICON | UI_BUT_ICON_PREVIEW); +} + +void PreviewGridItem::set_on_activate_fn(ActivateFn fn) +{ + activate_fn_ = fn; +} + +void PreviewGridItem::set_is_active_fn(IsActiveFn fn) +{ + is_active_fn_ = fn; +} + +void PreviewGridItem::on_activate() +{ + if (activate_fn_) { + activate_fn_(*this); + } +} + +std::optional<bool> PreviewGridItem::should_be_active() const +{ + if (is_active_fn_) { + return is_active_fn_(); + } + return std::nullopt; +} + +} // namespace blender::ui + +using namespace blender::ui; + +/* ---------------------------------------------------------------------- */ +/* C-API */ + +using namespace blender::ui; + +bool UI_grid_view_item_is_active(const uiGridViewItemHandle *item_handle) +{ + const AbstractGridViewItem &item = reinterpret_cast<const AbstractGridViewItem &>(*item_handle); + return item.is_active(); +} + +bool UI_grid_view_listen_should_redraw(const uiGridViewHandle *view_handle, + const wmNotifier *notifier) +{ + const AbstractGridView &view = *reinterpret_cast<const AbstractGridView *>(view_handle); + return view.listen(*notifier); +} + +bool UI_grid_view_item_matches(const uiGridViewItemHandle *a_handle, + const uiGridViewItemHandle *b_handle) +{ + const AbstractGridViewItem &a = reinterpret_cast<const AbstractGridViewItem &>(*a_handle); + const AbstractGridViewItem &b = reinterpret_cast<const AbstractGridViewItem &>(*b_handle); + return a.matches(b); +} diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index c12d6957a95..3f623566807 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -778,6 +778,15 @@ static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut) } } + if ((but->type == UI_BTYPE_GRID_TILE) && (oldbut->type == UI_BTYPE_GRID_TILE)) { + uiButGridTile *but_gridtile = (uiButGridTile *)but; + uiButGridTile *oldbut_gridtile = (uiButGridTile *)oldbut; + if (!but_gridtile->view_item || !oldbut_gridtile->view_item || + !UI_grid_view_item_matches(but_gridtile->view_item, oldbut_gridtile->view_item)) { + return false; + } + } + return true; } @@ -904,6 +913,12 @@ static void ui_but_update_old_active_from_new(uiBut *oldbut, uiBut *but) SWAP(uiTreeViewItemHandle *, treerow_newbut->tree_item, treerow_oldbut->tree_item); break; } + case UI_BTYPE_GRID_TILE: { + uiButGridTile *gridtile_oldbut = (uiButGridTile *)oldbut; + uiButGridTile *gridtile_newbut = (uiButGridTile *)but; + SWAP(uiGridViewItemHandle *, gridtile_newbut->view_item, gridtile_oldbut->view_item); + break; + } default: break; } @@ -996,9 +1011,9 @@ static bool ui_but_update_from_old_block(const bContext *C, else { int flag_copy = UI_BUT_DRAG_MULTI; - /* Stupid special case: The active button may be inside (as in, overlapped on top) a tree-row + /* Stupid special case: The active button may be inside (as in, overlapped on top) a view-item * button which we also want to keep highlighted then. */ - if (but->type == UI_BTYPE_TREEROW) { + if (ui_but_is_view_item(but)) { flag_copy |= UI_ACTIVE; } @@ -2239,6 +2254,15 @@ int ui_but_is_pushed_ex(uiBut *but, double *value) } break; } + case UI_BTYPE_GRID_TILE: { + uiButGridTile *grid_tile_but = (uiButGridTile *)but; + + is_push = -1; + if (grid_tile_but->view_item) { + is_push = UI_grid_view_item_is_active(grid_tile_but->view_item); + } + break; + } default: is_push = -1; break; @@ -3445,9 +3469,7 @@ static void ui_but_free(const bContext *C, uiBut *but) IMB_freeImBuf((struct ImBuf *)but->poin); } - if (but->dragpoin && (but->dragflag & UI_BUT_DRAGPOIN_FREE)) { - WM_drag_data_free(but->dragtype, but->dragpoin); - } + ui_but_drag_free(but); ui_but_extra_operator_icons_free(but); BLI_assert(UI_butstore_is_registered(but->block, but) == false); @@ -3997,6 +4019,10 @@ static void ui_but_alloc_info(const eButType type, alloc_size = sizeof(uiButHotkeyEvent); alloc_str = "uiButHotkeyEvent"; break; + case UI_BTYPE_GRID_TILE: + alloc_size = sizeof(uiButGridTile); + alloc_str = "uiButGridTile"; + break; default: alloc_size = sizeof(uiBut); alloc_str = "uiBut"; @@ -4971,6 +4997,33 @@ int UI_autocomplete_end(AutoComplete *autocpl, char *autoname) return match; } +#define PREVIEW_TILE_PAD (0.15f * UI_UNIT_X) + +int UI_preview_tile_size_x(void) +{ + const float pad = PREVIEW_TILE_PAD; + return round_fl_to_int((96.0f / 20.0f) * UI_UNIT_X + 2.0f * pad); +} + +int UI_preview_tile_size_y(void) +{ + const uiStyle *style = UI_style_get(); + const float font_height = style->widget.points * UI_DPI_FAC; + const float pad = PREVIEW_TILE_PAD; + + return round_fl_to_int(UI_preview_tile_size_y_no_label() + font_height + + /* Add some extra padding to make things less tight vertically. */ + pad); +} + +int UI_preview_tile_size_y_no_label(void) +{ + const float pad = PREVIEW_TILE_PAD; + return round_fl_to_int((96.0f / 20.0f) * UI_UNIT_Y + 2.0f * pad); +} + +#undef PREVIEW_TILE_PAD + static void ui_but_update_and_icon_set(uiBut *but, int icon) { if (icon) { @@ -5881,104 +5934,6 @@ int UI_but_return_value_get(uiBut *but) return but->retval; } -void UI_but_drag_set_id(uiBut *but, ID *id) -{ - but->dragtype = WM_DRAG_ID; - if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { - WM_drag_data_free(but->dragtype, but->dragpoin); - but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; - } - but->dragpoin = (void *)id; -} - -void UI_but_drag_attach_image(uiBut *but, struct ImBuf *imb, const float scale) -{ - but->imb = imb; - but->imb_scale = scale; -} - -void UI_but_drag_set_asset(uiBut *but, - const AssetHandle *asset, - const char *path, - struct AssetMetaData *metadata, - int import_type, - int icon, - struct ImBuf *imb, - float scale) -{ - wmDragAsset *asset_drag = WM_drag_create_asset_data(asset, metadata, path, import_type); - - /* FIXME: This is temporary evil solution to get scene/viewlayer/etc in the copy callback of the - * #wmDropBox. - * TODO: Handle link/append in operator called at the end of the drop process, and NOT in its - * copy callback. - * */ - asset_drag->evil_C = static_cast<bContext *>(but->block->evil_C); - - but->dragtype = WM_DRAG_ASSET; - ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */ - if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { - WM_drag_data_free(but->dragtype, but->dragpoin); - } - but->dragpoin = asset_drag; - but->dragflag |= UI_BUT_DRAGPOIN_FREE; - UI_but_drag_attach_image(but, imb, scale); -} - -void UI_but_drag_set_rna(uiBut *but, PointerRNA *ptr) -{ - but->dragtype = WM_DRAG_RNA; - if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { - WM_drag_data_free(but->dragtype, but->dragpoin); - but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; - } - but->dragpoin = (void *)ptr; -} - -void UI_but_drag_set_path(uiBut *but, const char *path, const bool use_free) -{ - but->dragtype = WM_DRAG_PATH; - if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { - WM_drag_data_free(but->dragtype, but->dragpoin); - but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; - } - but->dragpoin = (void *)path; - if (use_free) { - but->dragflag |= UI_BUT_DRAGPOIN_FREE; - } -} - -void UI_but_drag_set_name(uiBut *but, const char *name) -{ - but->dragtype = WM_DRAG_NAME; - if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { - WM_drag_data_free(but->dragtype, but->dragpoin); - but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; - } - but->dragpoin = (void *)name; -} - -void UI_but_drag_set_value(uiBut *but) -{ - but->dragtype = WM_DRAG_VALUE; -} - -void UI_but_drag_set_image( - uiBut *but, const char *path, int icon, struct ImBuf *imb, float scale, const bool use_free) -{ - but->dragtype = WM_DRAG_PATH; - ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */ - if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { - WM_drag_data_free(but->dragtype, but->dragpoin); - but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; - } - but->dragpoin = (void *)path; - if (use_free) { - but->dragflag |= UI_BUT_DRAGPOIN_FREE; - } - UI_but_drag_attach_image(but, imb, scale); -} - PointerRNA *UI_but_operator_ptr_get(uiBut *but) { if (but->optype && !but->opptr) { diff --git a/source/blender/editors/interface/interface_drag.cc b/source/blender/editors/interface/interface_drag.cc new file mode 100644 index 00000000000..4c68870b2c7 --- /dev/null +++ b/source/blender/editors/interface/interface_drag.cc @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edinterface + */ + +#include "UI_interface.h" + +#include "WM_api.h" + +#include "interface_intern.h" + +void UI_but_drag_set_id(uiBut *but, ID *id) +{ + but->dragtype = WM_DRAG_ID; + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { + WM_drag_data_free(but->dragtype, but->dragpoin); + but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; + } + but->dragpoin = (void *)id; +} + +void UI_but_drag_attach_image(uiBut *but, struct ImBuf *imb, const float scale) +{ + but->imb = imb; + but->imb_scale = scale; +} + +void UI_but_drag_set_asset(uiBut *but, + const AssetHandle *asset, + const char *path, + struct AssetMetaData *metadata, + int import_type, + int icon, + struct ImBuf *imb, + float scale) +{ + wmDragAsset *asset_drag = WM_drag_create_asset_data(asset, metadata, path, import_type); + + /* FIXME: This is temporary evil solution to get scene/viewlayer/etc in the copy callback of the + * #wmDropBox. + * TODO: Handle link/append in operator called at the end of the drop process, and NOT in its + * copy callback. + * */ + asset_drag->evil_C = static_cast<bContext *>(but->block->evil_C); + + but->dragtype = WM_DRAG_ASSET; + ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */ + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { + WM_drag_data_free(but->dragtype, but->dragpoin); + } + but->dragpoin = asset_drag; + but->dragflag |= UI_BUT_DRAGPOIN_FREE; + UI_but_drag_attach_image(but, imb, scale); +} + +void UI_but_drag_set_rna(uiBut *but, PointerRNA *ptr) +{ + but->dragtype = WM_DRAG_RNA; + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { + WM_drag_data_free(but->dragtype, but->dragpoin); + but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; + } + but->dragpoin = (void *)ptr; +} + +void UI_but_drag_set_path(uiBut *but, const char *path, const bool use_free) +{ + but->dragtype = WM_DRAG_PATH; + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { + WM_drag_data_free(but->dragtype, but->dragpoin); + but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; + } + but->dragpoin = (void *)path; + if (use_free) { + but->dragflag |= UI_BUT_DRAGPOIN_FREE; + } +} + +void UI_but_drag_set_name(uiBut *but, const char *name) +{ + but->dragtype = WM_DRAG_NAME; + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { + WM_drag_data_free(but->dragtype, but->dragpoin); + but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; + } + but->dragpoin = (void *)name; +} + +void UI_but_drag_set_value(uiBut *but) +{ + but->dragtype = WM_DRAG_VALUE; +} + +void UI_but_drag_set_image( + uiBut *but, const char *path, int icon, struct ImBuf *imb, float scale, const bool use_free) +{ + but->dragtype = WM_DRAG_PATH; + ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */ + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { + WM_drag_data_free(but->dragtype, but->dragpoin); + but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; + } + but->dragpoin = (void *)path; + if (use_free) { + but->dragflag |= UI_BUT_DRAGPOIN_FREE; + } + UI_but_drag_attach_image(but, imb, scale); +} + +void ui_but_drag_free(uiBut *but) +{ + if (but->dragpoin && (but->dragflag & UI_BUT_DRAGPOIN_FREE)) { + WM_drag_data_free(but->dragtype, but->dragpoin); + } +} + +bool ui_but_drag_is_draggable(const uiBut *but) +{ + return but->dragpoin != nullptr; +} + +void ui_but_drag_start(bContext *C, uiBut *but) +{ + wmDrag *drag = WM_event_start_drag(C, + but->icon, + but->dragtype, + but->dragpoin, + ui_but_value_get(but), + (but->dragflag & UI_BUT_DRAGPOIN_FREE) ? WM_DRAG_FREE_DATA : + WM_DRAG_NOP); + /* wmDrag has ownership over dragpoin now, stop messing with it. */ + but->dragpoin = NULL; + + if (but->imb) { + WM_event_drag_image(drag, but->imb, but->imb_scale); + } + + /* Special feature for assets: We add another drag item that supports multiple assets. It + * gets the assets from context. */ + if (ELEM(but->dragtype, WM_DRAG_ASSET, WM_DRAG_ID)) { + WM_event_start_drag(C, ICON_NONE, WM_DRAG_ASSET_LIST, NULL, 0, WM_DRAG_NOP); + } +} diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c index 241776fd761..c015a60de89 100644 --- a/source/blender/editors/interface/interface_eyedropper_color.c +++ b/source/blender/editors/interface/interface_eyedropper_color.c @@ -50,8 +50,6 @@ #include "RE_pipeline.h" -#include "RE_pipeline.h" - #include "interface_eyedropper_intern.h" typedef struct Eyedropper { diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 11d4a440a68..341d5e78872 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -2135,25 +2135,7 @@ static bool ui_but_drag_init(bContext *C, } } else { - wmDrag *drag = WM_event_start_drag( - C, - but->icon, - but->dragtype, - but->dragpoin, - ui_but_value_get(but), - (but->dragflag & UI_BUT_DRAGPOIN_FREE) ? WM_DRAG_FREE_DATA : WM_DRAG_NOP); - /* wmDrag has ownership over dragpoin now, stop messing with it. */ - but->dragpoin = NULL; - - if (but->imb) { - WM_event_drag_image(drag, but->imb, but->imb_scale); - } - - /* Special feature for assets: We add another drag item that supports multiple assets. It - * gets the assets from context. */ - if (ELEM(but->dragtype, WM_DRAG_ASSET, WM_DRAG_ID)) { - WM_event_start_drag(C, ICON_NONE, WM_DRAG_ASSET_LIST, NULL, 0, WM_DRAG_NOP); - } + ui_but_drag_start(C, but); } return true; } @@ -2307,6 +2289,9 @@ static void ui_apply_but( case UI_BTYPE_ROW: ui_apply_but_ROW(C, block, but, data); break; + case UI_BTYPE_GRID_TILE: + ui_apply_but_ROW(C, block, but, data); + break; case UI_BTYPE_TREEROW: ui_apply_but_TREEROW(C, block, but, data); break; @@ -2963,6 +2948,9 @@ void ui_but_text_password_hide(char password_str[UI_MAX_PASSWORD_STR], void ui_but_set_string_interactive(bContext *C, uiBut *but, const char *value) { + /* Caller should check. */ + BLI_assert((but->flag & UI_BUT_DISABLED) == 0); + button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); ui_textedit_string_set(but, but->active, value); @@ -4825,12 +4813,53 @@ static int ui_do_but_TREEROW(bContext *C, return WM_UI_HANDLER_CONTINUE; } +static int ui_do_but_GRIDTILE(bContext *C, + uiBut *but, + uiHandleButtonData *data, + const wmEvent *event) +{ + uiButGridTile *grid_tile_but = (uiButGridTile *)but; + BLI_assert(grid_tile_but->but.type == UI_BTYPE_GRID_TILE); + + if (data->state == BUTTON_STATE_HIGHLIGHT) { + if (event->type == LEFTMOUSE) { + switch (event->val) { + case KM_PRESS: + /* Extra icons have priority, don't mess with them. */ + if (ui_but_extra_operator_icon_mouse_over_get(but, data->region, event)) { + return WM_UI_HANDLER_BREAK; + } + button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; + return WM_UI_HANDLER_CONTINUE; + + case KM_CLICK: + button_activate_state(C, but, BUTTON_STATE_EXIT); + return WM_UI_HANDLER_BREAK; + + case KM_DBL_CLICK: + data->cancel = true; + // UI_tree_view_item_begin_rename(grid_tile_but->tree_item); + ED_region_tag_redraw(CTX_wm_region(C)); + return WM_UI_HANDLER_BREAK; + } + } + } + else if (data->state == BUTTON_STATE_WAIT_DRAG) { + /* Let "default" button handling take care of the drag logic. */ + return ui_do_but_EXIT(C, but, data, event); + } + + return WM_UI_HANDLER_CONTINUE; +} + static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { if (data->state == BUTTON_STATE_HIGHLIGHT) { /* First handle click on icon-drag type button. */ - if ((event->type == LEFTMOUSE) && (event->val == KM_PRESS) && but->dragpoin) { + if ((event->type == LEFTMOUSE) && (event->val == KM_PRESS) && ui_but_drag_is_draggable(but)) { if (ui_but_contains_point_px_icon(but, data->region, event)) { /* tell the button to wait and keep checking further events to @@ -4853,7 +4882,8 @@ static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, con if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->val == KM_PRESS) { int ret = WM_UI_HANDLER_BREAK; /* XXX: (a bit ugly) Special case handling for file-browser drag button. */ - if (but->dragpoin && but->imb && ui_but_contains_point_px_icon(but, data->region, event)) { + if (ui_but_drag_is_draggable(but) && but->imb && + ui_but_contains_point_px_icon(but, data->region, event)) { ret = WM_UI_HANDLER_CONTINUE; } /* Same special case handling for UI lists. Return CONTINUE so that a tweak or CLICK event @@ -4865,6 +4895,10 @@ static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, con ret = WM_UI_HANDLER_CONTINUE; } } + const uiBut *view_but = ui_view_item_find_mouse_over(data->region, event->xy); + if (view_but) { + ret = WM_UI_HANDLER_CONTINUE; + } button_activate_state(C, but, BUTTON_STATE_EXIT); return ret; } @@ -6007,7 +6041,7 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co if (data->state == BUTTON_STATE_HIGHLIGHT) { /* First handle click on icon-drag type button. */ - if (event->type == LEFTMOUSE && but->dragpoin && event->val == KM_PRESS) { + if (event->type == LEFTMOUSE && ui_but_drag_is_draggable(but) && event->val == KM_PRESS) { if (ui_but_contains_point_px_icon(but, data->region, event)) { button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); data->dragstartx = event->xy[0]; @@ -6193,7 +6227,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co if (data->state == BUTTON_STATE_HIGHLIGHT) { /* First handle click on icon-drag type button. */ - if (event->type == LEFTMOUSE && but->dragpoin && event->val == KM_PRESS) { + if (event->type == LEFTMOUSE && ui_but_drag_is_draggable(but) && event->val == KM_PRESS) { ui_palette_set_active(color_but); if (ui_but_contains_point_px_icon(but, data->region, event)) { button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); @@ -8019,6 +8053,9 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * case UI_BTYPE_ROW: retval = ui_do_but_TOG(C, but, data, event); break; + case UI_BTYPE_GRID_TILE: + retval = ui_do_but_GRIDTILE(C, but, data, event); + break; case UI_BTYPE_TREEROW: retval = ui_do_but_TREEROW(C, but, data, event); break; @@ -9476,7 +9513,7 @@ static bool ui_list_is_hovering_draggable_but(bContext *C, } } - return (hovered_but && hovered_but->dragpoin); + return (hovered_but && ui_but_drag_is_draggable(hovered_but)); } static int ui_list_handle_click_drag(bContext *C, @@ -9686,31 +9723,31 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi return retval; } -static int ui_handle_tree_hover(const wmEvent *event, const ARegion *region) +static int ui_handle_view_items_hover(const wmEvent *event, const ARegion *region) { - bool has_treerows = false; + bool has_view_item = false; LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { - /* Avoid unnecessary work: Tree-rows are assumed to be inside tree-views. */ + /* Avoid unnecessary work: view item buttons are assumed to be inside views. */ if (BLI_listbase_is_empty(&block->views)) { continue; } LISTBASE_FOREACH (uiBut *, but, &block->buttons) { - if (but->type == UI_BTYPE_TREEROW) { + if (ui_but_is_view_item(but)) { but->flag &= ~UI_ACTIVE; - has_treerows = true; + has_view_item = true; } } } - if (!has_treerows) { + if (!has_view_item) { /* Avoid unnecessary lookup. */ return WM_UI_HANDLER_CONTINUE; } - /* Always highlight the hovered tree-row, even if the mouse hovers another button inside of it. + /* Always highlight the hovered view item, even if the mouse hovers another button inside of it. */ - uiBut *hovered_row_but = ui_tree_row_find_mouse_over(region, event->xy); + uiBut *hovered_row_but = ui_view_item_find_mouse_over(region, event->xy); if (hovered_row_but) { hovered_row_but->flag |= UI_ACTIVE; } @@ -9718,6 +9755,21 @@ static int ui_handle_tree_hover(const wmEvent *event, const ARegion *region) return WM_UI_HANDLER_CONTINUE; } +static int ui_handle_view_item_event(bContext *C, + const wmEvent *event, + ARegion *region, + uiBut *view_but) +{ + BLI_assert(ui_but_is_view_item(view_but)); + if (event->type == LEFTMOUSE) { + /* Will free active button if there already is one. */ + ui_handle_button_activate(C, region, view_but, BUTTON_ACTIVATE_OVER); + return ui_do_button(C, view_but->block, view_but, event); + } + + return WM_UI_HANDLER_CONTINUE; +} + static void ui_handle_button_return_submenu(bContext *C, const wmEvent *event, uiBut *but) { uiHandleButtonData *data = but->active; @@ -11318,9 +11370,15 @@ static int ui_region_handler(bContext *C, const wmEvent *event, void *UNUSED(use ui_blocks_set_tooltips(region, true); } - /* Always do this, to reliably update tree-row highlighting, even if the mouse hovers a button - * inside the row (it's an overlapping layout). */ - ui_handle_tree_hover(event, region); + /* Always do this, to reliably update view item highlighting, even if the mouse hovers a button + * nested in the item (it's an overlapping layout). */ + ui_handle_view_items_hover(event, region); + if (retval == WM_UI_HANDLER_CONTINUE) { + uiBut *view_item = ui_view_item_find_mouse_over(region, event->xy); + if (view_item) { + retval = ui_handle_view_item_event(C, event, region, view_item); + } + } /* delayed apply callbacks */ ui_apply_but_funcs_after(C); diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 332b9b44b0a..c19e842aad8 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -546,6 +546,7 @@ static void init_brush_icons(void) INIT_BRUSH_ICON(ICON_BRUSH_MASK, mask); INIT_BRUSH_ICON(ICON_BRUSH_MIX, mix); INIT_BRUSH_ICON(ICON_BRUSH_NUDGE, nudge); + INIT_BRUSH_ICON(ICON_BRUSH_PAINT_SELECT, paint_select); INIT_BRUSH_ICON(ICON_BRUSH_PINCH, pinch); INIT_BRUSH_ICON(ICON_BRUSH_SCRAPE, scrape); INIT_BRUSH_ICON(ICON_BRUSH_SMEAR, smear); @@ -584,6 +585,19 @@ static void init_brush_icons(void) INIT_BRUSH_ICON(ICON_GPBRUSH_ERASE_HARD, gp_brush_erase_hard); INIT_BRUSH_ICON(ICON_GPBRUSH_ERASE_STROKE, gp_brush_erase_stroke); + /* Curves sculpt. */ + INIT_BRUSH_ICON(ICON_BRUSH_CURVES_ADD, curves_sculpt_add); + INIT_BRUSH_ICON(ICON_BRUSH_CURVES_COMB, curves_sculpt_comb); + INIT_BRUSH_ICON(ICON_BRUSH_CURVES_CUT, curves_sculpt_cut); + INIT_BRUSH_ICON(ICON_BRUSH_CURVES_DELETE, curves_sculpt_delete); + INIT_BRUSH_ICON(ICON_BRUSH_CURVES_DENSITY, curves_sculpt_density); + INIT_BRUSH_ICON(ICON_BRUSH_CURVES_GROW_SHRINK, curves_sculpt_grow_shrink); + INIT_BRUSH_ICON(ICON_BRUSH_CURVES_PINCH, curves_sculpt_pinch); + INIT_BRUSH_ICON(ICON_BRUSH_CURVES_PUFF, curves_sculpt_puff); + INIT_BRUSH_ICON(ICON_BRUSH_CURVES_SLIDE, curves_sculpt_slide); + INIT_BRUSH_ICON(ICON_BRUSH_CURVES_SMOOTH, curves_sculpt_smooth); + INIT_BRUSH_ICON(ICON_BRUSH_CURVES_SNAKE_HOOK, curves_sculpt_snake_hook); + # undef INIT_BRUSH_ICON } @@ -2034,6 +2048,9 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id) else if (ob->mode & OB_MODE_TEXTURE_PAINT) { paint_mode = PAINT_MODE_TEXTURE_3D; } + else if (ob->mode & OB_MODE_SCULPT_CURVES) { + paint_mode = PAINT_MODE_SCULPT_CURVES; + } } else if (space_type == SPACE_IMAGE) { if (area->spacetype == space_type) { diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 1b245ba9e6f..791e51b81a6 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -351,6 +351,13 @@ typedef struct uiButTreeRow { int indentation; } uiButTreeRow; +/** Derived struct for #UI_BTYPE_GRID_TILE. */ +typedef struct uiButGridTile { + uiBut but; + + uiGridViewItemHandle *view_item; +} uiButGridTile; + /** Derived struct for #UI_BTYPE_HSVCUBE. */ typedef struct uiButHSVCube { uiBut but; @@ -1321,6 +1328,12 @@ void ui_button_group_add_but(uiBlock *block, uiBut *but); void ui_button_group_replace_but_ptr(uiBlock *block, const void *old_but_ptr, uiBut *new_but); void ui_block_free_button_groups(uiBlock *block); +/* interface_drag.cc */ + +void ui_but_drag_free(uiBut *but); +bool ui_but_drag_is_draggable(const uiBut *but); +void ui_but_drag_start(struct bContext *C, uiBut *but); + /* interface_align.c */ bool ui_but_can_align(const uiBut *but) ATTR_WARN_UNUSED_RESULT; @@ -1359,6 +1372,7 @@ void ui_but_anim_decorate_update_from_flag(uiButDecorator *but); bool ui_but_is_editable(const uiBut *but) ATTR_WARN_UNUSED_RESULT; bool ui_but_is_editable_as_text(const uiBut *but) ATTR_WARN_UNUSED_RESULT; bool ui_but_is_toggle(const uiBut *but) ATTR_WARN_UNUSED_RESULT; +bool ui_but_is_view_item(const uiBut *but) ATTR_WARN_UNUSED_RESULT; /** * Can we mouse over the button or is it hidden/disabled/layout. * \note ctrl is kind of a hack currently, @@ -1390,6 +1404,8 @@ uiBut *ui_list_row_find_mouse_over(const struct ARegion *region, const int xy[2] uiBut *ui_list_row_find_from_index(const struct ARegion *region, int index, uiBut *listbox) ATTR_WARN_UNUSED_RESULT; +uiBut *ui_view_item_find_mouse_over(const struct ARegion *region, const int xy[2]) + ATTR_NONNULL(1, 2); uiBut *ui_tree_row_find_mouse_over(const struct ARegion *region, const int xy[2]) ATTR_NONNULL(1, 2); uiBut *ui_tree_row_find_active(const struct ARegion *region); @@ -1527,8 +1543,10 @@ void ui_interface_tag_script_reload_queries(void); /* interface_view.cc */ void ui_block_free_views(struct uiBlock *block); -uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block, - const uiTreeViewHandle *new_view); +uiTreeViewHandle *ui_block_tree_view_find_matching_in_old_block(const uiBlock *new_block, + const uiTreeViewHandle *new_view); +uiGridViewHandle *ui_block_grid_view_find_matching_in_old_block( + const uiBlock *new_block, const uiGridViewHandle *new_view_handle); uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block, const uiTreeViewItemHandle *new_item_handle); diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 60a68e270bf..aafb56119ae 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1942,6 +1942,24 @@ static void UI_OT_drop_color(wmOperatorType *ot) /** \name Drop Name Operator * \{ */ +static bool drop_name_poll(bContext *C) +{ + if (!ED_operator_regionactive(C)) { + return false; + } + + const uiBut *but = UI_but_active_drop_name_button(C); + if (!but) { + return false; + } + + if (but->flag & UI_BUT_DISABLED) { + return false; + } + + return true; +} + static int drop_name_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { uiBut *but = UI_but_active_drop_name_button(C); @@ -1961,7 +1979,7 @@ static void UI_OT_drop_name(wmOperatorType *ot) ot->idname = "UI_OT_drop_name"; ot->description = "Drop name to button"; - ot->poll = ED_operator_regionactive; + ot->poll = drop_name_poll; ot->invoke = drop_name_invoke; ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; diff --git a/source/blender/editors/interface/interface_query.cc b/source/blender/editors/interface/interface_query.cc index ea1fe3923e4..71cf60985df 100644 --- a/source/blender/editors/interface/interface_query.cc +++ b/source/blender/editors/interface/interface_query.cc @@ -58,18 +58,23 @@ bool ui_but_is_toggle(const uiBut *but) UI_BTYPE_TREEROW); } +bool ui_but_is_view_item(const uiBut *but) +{ + return ELEM(but->type, UI_BTYPE_TREEROW, UI_BTYPE_GRID_TILE); +} + bool ui_but_is_interactive_ex(const uiBut *but, const bool labeledit, const bool for_tooltip) { /* NOTE: #UI_BTYPE_LABEL is included for highlights, this allows drags. */ if (but->type == UI_BTYPE_LABEL) { if (for_tooltip) { /* It's important labels are considered interactive for the purpose of showing tooltip. */ - if (but->dragpoin == nullptr && but->tip_func == nullptr) { + if (!ui_but_drag_is_draggable(but) && but->tip_func == nullptr) { return false; } } else { - if (but->dragpoin == nullptr) { + if (!ui_but_drag_is_draggable(but)) { return false; } } @@ -462,6 +467,16 @@ static bool ui_but_is_treerow(const uiBut *but, const void *UNUSED(customdata)) return but->type == UI_BTYPE_TREEROW; } +static bool ui_but_is_view_item_fn(const uiBut *but, const void *UNUSED(customdata)) +{ + return ui_but_is_view_item(but); +} + +uiBut *ui_view_item_find_mouse_over(const ARegion *region, const int xy[2]) +{ + return ui_but_find_mouse_over_ex(region, xy, false, false, ui_but_is_view_item_fn, nullptr); +} + uiBut *ui_tree_row_find_mouse_over(const ARegion *region, const int xy[2]) { return ui_but_find_mouse_over_ex(region, xy, false, false, ui_but_is_treerow, nullptr); diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index c7ebecb178b..82d4405e1b5 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -800,7 +800,7 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, .style = UI_TIP_STYLE_HEADER, .color_id = UI_TIP_LC_NORMAL, }); - field->text = BLI_sprintfN("%s", but_label.strinfo); + field->text = BLI_strdup(but_label.strinfo); } /* Tip */ diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc index 68a699c652a..e0b6bbb34c4 100644 --- a/source/blender/editors/interface/interface_template_list.cc +++ b/source/blender/editors/interface/interface_template_list.cc @@ -945,13 +945,8 @@ static void ui_template_list_layout_draw(bContext *C, const bool show_names = (flags & UI_TEMPLATE_LIST_NO_NAMES) == 0; - /* TODO ED_fileselect_init_layout(). Share somehow? */ - float size_x = (96.0f / 20.0f) * UI_UNIT_X; - float size_y = (96.0f / 20.0f) * UI_UNIT_Y; - - if (!show_names) { - size_y -= UI_UNIT_Y; - } + const int size_x = UI_preview_tile_size_x(); + const int size_y = show_names ? UI_preview_tile_size_y() : UI_preview_tile_size_y_no_label(); const int cols_per_row = MAX2((uiLayoutGetWidth(box) - V2D_SCROLL_WIDTH) / size_x, 1); uiLayout *grid = uiLayoutGridFlow(row, true, cols_per_row, true, true, true); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index f625bf371e2..4b3937dabce 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -1536,8 +1536,8 @@ static void template_ID_tabs(const bContext *C, 0.0f, ""); UI_but_funcN_set(&tab->but, template_ID_set_property_exec_fn, MEM_dupallocN(template), id); + UI_but_drag_set_id(&tab->but, id); tab->but.custom_data = (void *)id; - tab->but.dragpoin = id; tab->menu = mt; UI_but_drawflag_enable(&tab->but, but_align); diff --git a/source/blender/editors/interface/interface_view.cc b/source/blender/editors/interface/interface_view.cc index 85e1dbe73a5..699ac0c2b53 100644 --- a/source/blender/editors/interface/interface_view.cc +++ b/source/blender/editors/interface/interface_view.cc @@ -10,15 +10,22 @@ */ #include <memory> +#include <type_traits> #include <variant> #include "DNA_screen_types.h" +#include "BKE_screen.h" + #include "BLI_listbase.h" +#include "ED_screen.h" + #include "interface_intern.h" #include "UI_interface.hh" + +#include "UI_grid_view.hh" #include "UI_tree_view.hh" using namespace blender; @@ -30,29 +37,51 @@ using namespace blender::ui; */ struct ViewLink : public Link { using TreeViewPtr = std::unique_ptr<AbstractTreeView>; + using GridViewPtr = std::unique_ptr<AbstractGridView>; std::string idname; /* NOTE: Can't use std::get() on this until minimum macOS deployment target is 10.14. */ - std::variant<TreeViewPtr> view; + std::variant<TreeViewPtr, GridViewPtr> view; }; +template<class T> constexpr void check_if_valid_view_type() +{ + static_assert(std::is_same_v<T, AbstractTreeView> || std::is_same_v<T, AbstractGridView>, + "Unsupported view type"); +} + template<class T> T *get_view_from_link(ViewLink &link) { auto *t_uptr = std::get_if<std::unique_ptr<T>>(&link.view); return t_uptr ? t_uptr->get() : nullptr; } -AbstractTreeView *UI_block_add_view(uiBlock &block, - StringRef idname, - std::unique_ptr<AbstractTreeView> tree_view) +template<class T> +static T *ui_block_add_view_impl(uiBlock &block, StringRef idname, std::unique_ptr<T> view) { + check_if_valid_view_type<T>(); + ViewLink *view_link = MEM_new<ViewLink>(__func__); BLI_addtail(&block.views, view_link); - view_link->view = std::move(tree_view); + view_link->view = std::move(view); view_link->idname = idname; - return get_view_from_link<AbstractTreeView>(*view_link); + return get_view_from_link<T>(*view_link); +} + +AbstractGridView *UI_block_add_view(uiBlock &block, + StringRef idname, + std::unique_ptr<AbstractGridView> tree_view) +{ + return ui_block_add_view_impl<AbstractGridView>(block, idname, std::move(tree_view)); +} + +AbstractTreeView *UI_block_add_view(uiBlock &block, + StringRef idname, + std::unique_ptr<AbstractTreeView> tree_view) +{ + return ui_block_add_view_impl<AbstractTreeView>(block, idname, std::move(tree_view)); } void ui_block_free_views(uiBlock *block) @@ -62,6 +91,26 @@ void ui_block_free_views(uiBlock *block) } } +void UI_block_views_listen(const uiBlock *block, const wmRegionListenerParams *listener_params) +{ + ARegion *region = listener_params->region; + + LISTBASE_FOREACH (ViewLink *, view_link, &block->views) { + if (AbstractGridView *grid_view = get_view_from_link<AbstractGridView>(*view_link)) { + if (UI_grid_view_listen_should_redraw(reinterpret_cast<uiGridViewHandle *>(grid_view), + listener_params->notifier)) { + ED_region_tag_redraw(region); + } + } + else if (AbstractTreeView *tree_view = get_view_from_link<AbstractTreeView>(*view_link)) { + if (UI_tree_view_listen_should_redraw(reinterpret_cast<uiTreeViewHandle *>(tree_view), + listener_params->notifier)) { + ED_region_tag_redraw(region); + } + } + } +} + uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const ARegion *region, const int xy[2]) { uiButTreeRow *tree_row_but = (uiButTreeRow *)ui_tree_row_find_mouse_over(region, xy); @@ -82,11 +131,13 @@ uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const ARegion *region) return tree_row_but->tree_item; } -static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractTreeView &view) +template<class T> static StringRef ui_block_view_find_idname(const uiBlock &block, const T &view) { + check_if_valid_view_type<T>(); + /* First get the idname the of the view we're looking for. */ LISTBASE_FOREACH (ViewLink *, view_link, &block.views) { - if (get_view_from_link<AbstractTreeView>(*view_link) == &view) { + if (get_view_from_link<T>(*view_link) == &view) { return view_link->idname; } } @@ -94,9 +145,11 @@ static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractT return {}; } -static AbstractTreeView *ui_block_view_find_matching_in_old_block(const uiBlock &new_block, - const AbstractTreeView &new_view) +template<class T> +static T *ui_block_view_find_matching_in_old_block(const uiBlock &new_block, const T &new_view) { + check_if_valid_view_type<T>(); + uiBlock *old_block = new_block.oldblock; if (!old_block) { return nullptr; @@ -109,15 +162,15 @@ static AbstractTreeView *ui_block_view_find_matching_in_old_block(const uiBlock LISTBASE_FOREACH (ViewLink *, old_view_link, &old_block->views) { if (old_view_link->idname == idname) { - return get_view_from_link<AbstractTreeView>(*old_view_link); + return get_view_from_link<T>(*old_view_link); } } return nullptr; } -uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block, - const uiTreeViewHandle *new_view_handle) +uiTreeViewHandle *ui_block_tree_view_find_matching_in_old_block( + const uiBlock *new_block, const uiTreeViewHandle *new_view_handle) { BLI_assert(new_block && new_view_handle); const AbstractTreeView &new_view = reinterpret_cast<const AbstractTreeView &>(*new_view_handle); @@ -126,6 +179,16 @@ uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_bl return reinterpret_cast<uiTreeViewHandle *>(old_view); } +uiGridViewHandle *ui_block_grid_view_find_matching_in_old_block( + const uiBlock *new_block, const uiGridViewHandle *new_view_handle) +{ + BLI_assert(new_block && new_view_handle); + const AbstractGridView &new_view = reinterpret_cast<const AbstractGridView &>(*new_view_handle); + + AbstractGridView *old_view = ui_block_view_find_matching_in_old_block(*new_block, new_view); + return reinterpret_cast<uiGridViewHandle *>(old_view); +} + uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block, const uiTreeViewItemHandle *new_item_handle) { diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 3777ff31b26..e2df2d77817 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -105,6 +105,7 @@ typedef enum { UI_WTYPE_PROGRESSBAR, UI_WTYPE_NODESOCKET, UI_WTYPE_TREEROW, + UI_WTYPE_GRID_TILE, } uiWidgetTypeEnum; /** @@ -1419,7 +1420,7 @@ static void widget_draw_icon( const bool has_theme = UI_icon_get_theme_color(icon, color); /* to indicate draggable */ - if (but->dragpoin && (but->flag & UI_ACTIVE)) { + if (ui_but_drag_is_draggable(but) && (but->flag & UI_ACTIVE)) { UI_icon_draw_ex(xs, ys, icon, aspect, 1.25f, 0.0f, color, has_theme); } else if ((but->flag & (UI_ACTIVE | UI_SELECT | UI_SELECT_DRAW))) { @@ -3706,6 +3707,16 @@ static void widget_treerow(uiBut *but, widget_treerow_exec(wcol, rect, state, roundboxalign, tree_row->indentation, zoom); } +static void widget_gridtile(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) +{ + /* TODO Reuse tree-row drawing. */ + widget_treerow_exec(wcol, rect, state, roundboxalign, 0, zoom); +} + static void widget_nodesocket(uiBut *but, uiWidgetColors *wcol, rcti *rect, @@ -4598,9 +4609,15 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) break; case UI_WTYPE_TREEROW: + wt.wcol_theme = &btheme->tui.wcol_view_item; wt.custom = widget_treerow; break; + case UI_WTYPE_GRID_TILE: + wt.wcol_theme = &btheme->tui.wcol_view_item; + wt.draw = widget_gridtile; + break; + case UI_WTYPE_NODESOCKET: wt.custom = widget_nodesocket; break; @@ -4937,6 +4954,11 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu fstyle = &style->widgetlabel; break; + case UI_BTYPE_GRID_TILE: + wt = widget_type(UI_WTYPE_GRID_TILE); + fstyle = &style->widgetlabel; + break; + case UI_BTYPE_SCROLL: wt = widget_type(UI_WTYPE_SCROLL); break; diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index bf756fb5838..f86d1c4d8bc 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -68,6 +68,12 @@ void AbstractTreeView::foreach_item(ItemIterFn iter_fn, IterOptions options) con foreach_item_recursive(iter_fn, options); } +bool AbstractTreeView::listen(const wmNotifier &) const +{ + /* Nothing by default. */ + return false; +} + bool AbstractTreeView::is_renaming() const { return rename_buffer_ != nullptr; @@ -82,7 +88,7 @@ void AbstractTreeView::update_from_old(uiBlock &new_block) return; } - uiTreeViewHandle *old_view_handle = ui_block_view_find_matching_in_old_block( + uiTreeViewHandle *old_view_handle = ui_block_tree_view_find_matching_in_old_block( &new_block, reinterpret_cast<uiTreeViewHandle *>(this)); if (old_view_handle == nullptr) { is_reconstructed_ = true; @@ -805,6 +811,13 @@ class TreeViewItemAPIWrapper { using namespace blender::ui; +bool UI_tree_view_listen_should_redraw(const uiTreeViewHandle *view_handle, + const wmNotifier *notifier) +{ + const AbstractTreeView &view = *reinterpret_cast<const AbstractTreeView *>(view_handle); + return view.listen(*notifier); +} + bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item_handle) { const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_handle); diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c index a8eed136df3..4819ae09785 100644 --- a/source/blender/editors/io/io_obj.c +++ b/source/blender/editors/io/io_obj.c @@ -100,6 +100,7 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op) export_params.export_selected_objects = RNA_boolean_get(op->ptr, "export_selected_objects"); export_params.export_uv = RNA_boolean_get(op->ptr, "export_uv"); export_params.export_normals = RNA_boolean_get(op->ptr, "export_normals"); + export_params.export_colors = RNA_boolean_get(op->ptr, "export_colors"); export_params.export_materials = RNA_boolean_get(op->ptr, "export_materials"); export_params.path_mode = RNA_enum_get(op->ptr, "path_mode"); export_params.export_triangulated_mesh = RNA_boolean_get(op->ptr, "export_triangulated_mesh"); @@ -160,6 +161,7 @@ static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr) sub = uiLayoutColumnWithHeading(col, false, IFACE_("Export")); uiItemR(sub, imfptr, "export_uv", 0, IFACE_("UV Coordinates"), ICON_NONE); uiItemR(sub, imfptr, "export_normals", 0, IFACE_("Normals"), ICON_NONE); + uiItemR(sub, imfptr, "export_colors", 0, IFACE_("Colors"), ICON_NONE); uiItemR(sub, imfptr, "export_materials", 0, IFACE_("Materials"), ICON_NONE); uiItemR(sub, imfptr, "export_triangulated_mesh", 0, IFACE_("Triangulated Mesh"), ICON_NONE); uiItemR(sub, imfptr, "export_curves_as_nurbs", 0, IFACE_("Curves as NURBS"), ICON_NONE); @@ -315,6 +317,7 @@ void WM_OT_obj_export(struct wmOperatorType *ot) "Export Normals", "Export per-face normals if the face is flat-shaded, per-face-per-loop " "normals if smooth-shaded"); + RNA_def_boolean(ot->srna, "export_colors", false, "Export Colors", "Export per-vertex colors"); RNA_def_boolean(ot->srna, "export_materials", true, diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c index 45d658d5b25..a59cdf60243 100644 --- a/source/blender/editors/io/io_usd.c +++ b/source/blender/editors/io/io_usd.c @@ -57,6 +57,20 @@ const EnumPropertyItem rna_enum_usd_export_evaluation_mode_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_usd_mtl_name_collision_mode_items[] = { + {USD_MTL_NAME_COLLISION_MAKE_UNIQUE, + "MAKE_UNIQUE", + 0, + "Make Unique", + "Import each USD material as a unique Blender material"}, + {USD_MTL_NAME_COLLISION_REFERENCE_EXISTING, + "REFERENCE_EXISTING", + 0, + "Reference Existing", + "If a material with the same name already exists, reference that instead of importing"}, + {0, NULL, 0, NULL, NULL}, +}; + /* Stored in the wmOperator's customdata field to indicate it should run as a background job. * This is set when the operator is invoked, and not set when it is only executed. */ enum { AS_BACKGROUND_JOB = 1 }; @@ -371,6 +385,9 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op) const float light_intensity_scale = RNA_float_get(op->ptr, "light_intensity_scale"); + const eUSDMtlNameCollisionMode mtl_name_collision_mode = RNA_enum_get(op->ptr, + "mtl_name_collision_mode"); + /* TODO(makowalski): Add support for sequences. */ const bool is_sequence = false; int offset = 0; @@ -409,7 +426,8 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op) .use_instancing = use_instancing, .import_usd_preview = import_usd_preview, .set_material_blend = set_material_blend, - .light_intensity_scale = light_intensity_scale}; + .light_intensity_scale = light_intensity_scale, + .mtl_name_collision_mode = mtl_name_collision_mode}; const bool ok = USD_import(C, filename, ¶ms, as_background_job); @@ -452,6 +470,7 @@ static void wm_usd_import_draw(bContext *UNUSED(C), wmOperator *op) uiItemR(col, ptr, "relative_path", 0, NULL, ICON_NONE); uiItemR(col, ptr, "create_collection", 0, NULL, ICON_NONE); uiItemR(box, ptr, "light_intensity_scale", 0, NULL, ICON_NONE); + uiItemR(box, ptr, "mtl_name_collision_mode", 0, NULL, ICON_NONE); box = uiLayoutBox(layout); col = uiLayoutColumnWithHeading(box, true, IFACE_("Experimental")); @@ -575,6 +594,14 @@ void WM_OT_usd_import(struct wmOperatorType *ot) "Scale for the intensity of imported lights", 0.0001f, 1000.0f); + + RNA_def_enum( + ot->srna, + "mtl_name_collision_mode", + rna_enum_usd_mtl_name_collision_mode_items, + USD_MTL_NAME_COLLISION_MAKE_UNIQUE, + "Material Name Collision", + "Behavior when the name of an imported material conflicts with an existing material"); } #endif /* WITH_USD */ diff --git a/source/blender/editors/mask/mask_query.c b/source/blender/editors/mask/mask_query.c index 89524a7b9e2..02e1524e23e 100644 --- a/source/blender/editors/mask/mask_query.c +++ b/source/blender/editors/mask/mask_query.c @@ -615,7 +615,7 @@ bool ED_mask_selected_minmax(const bContext *C, /* Use evaluated mask to take animation into account. * The animation of splies is not "flushed" back to original, so need to explicitly - * sue evaluated datablock here. */ + * use evaluated datablock here. */ Mask *mask_eval = (Mask *)DEG_get_evaluated_id(depsgraph, &mask->id); INIT_MINMAX2(min, max); diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c index 7c0b6fb0a93..e5fe108b6cd 100644 --- a/source/blender/editors/mask/mask_select.c +++ b/source/blender/editors/mask/mask_select.c @@ -364,12 +364,9 @@ static int select_exec(bContext *C, wmOperator *op) return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; } if (deselect_all) { - /* For clip editor tracks, leave deselect all to clip editor. */ - if (!ED_clip_can_select(C)) { - ED_mask_deselect_all(C); - ED_mask_view_lock_state_restore_no_jump(C, &lock_state); - return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; - } + ED_mask_deselect_all(C); + ED_mask_view_lock_state_restore_no_jump(C, &lock_state); + return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; } return OPERATOR_PASS_THROUGH; diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index 969d5b5912c..e7891450bd6 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -669,7 +669,7 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event) short etype = event->type; short eval = event->val; - /* When activated from toolbar, need to convert leftmouse release to confirm */ + /* When activated from toolbar, need to convert left-mouse release to confirm. */ if (ELEM(etype, LEFTMOUSE, opdata->launch_event) && (eval == KM_RELEASE) && RNA_boolean_get(op->ptr, "release_confirm")) { etype = EVT_MODAL_MAP; diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index c16b82e34bd..5680865ae67 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -1355,13 +1355,19 @@ static void knife_bvh_raycast_cb(void *userdata, #endif if (isect && dist < hit->dist) { + madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist); + + /* Discard clipped points. */ + if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) && + ED_view3d_clipping_test(kcd->vc.rv3d, hit->co, false)) { + return; + } + hit->dist = dist; hit->index = index; copy_v3_v3(hit->no, ltri[0]->f->no); - madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist); - kcd->bvh.looptris = em->looptris; copy_v2_v2(kcd->bvh.uv, uv); kcd->bvh.base_index = b; diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 6cfd322b2e2..f55ffdf0fcd 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -2290,27 +2290,19 @@ static int make_override_library_exec(bContext *C, wmOperator *op) user_overrides_from_selected_objects = false; } else if (!make_override_library_object_overridable_check(bmain, obact)) { - const int i = RNA_property_enum_get(op->ptr, op->type->prop); - const uint collection_session_uuid = *((uint *)&i); + const int i = RNA_property_int_get(op->ptr, op->type->prop); + const uint collection_session_uuid = *((const uint *)&i); if (collection_session_uuid == MAIN_ID_SESSION_UUID_UNSET) { BKE_reportf(op->reports, RPT_ERROR_INVALID_INPUT, - "Active object '%s' is not overridable", + "Could not find an overridable root hierarchy for object '%s'", obact->id.name + 2); return OPERATOR_CANCELLED; } - Collection *collection = BLI_listbase_bytes_find(&bmain->collections, &collection_session_uuid, sizeof(collection_session_uuid), offsetof(ID, session_uuid)); - if (!ID_IS_OVERRIDABLE_LIBRARY(collection)) { - BKE_reportf(op->reports, - RPT_ERROR_INVALID_INPUT, - "Could not find an overridable collection containing object '%s'", - obact->id.name + 2); - return OPERATOR_CANCELLED; - } id_root = &collection->id; user_overrides_from_selected_objects = true; } @@ -2372,10 +2364,36 @@ static int make_override_library_exec(bContext *C, wmOperator *op) BLI_gset_free(user_overrides_objects_uids, NULL); } - /* Remove the instance empty from this scene, the items now have an overridden collection - * instead. */ - if (success && is_override_instancing_object) { - ED_object_base_free_and_unlink(bmain, scene, obact); + if (success) { + if (is_override_instancing_object) { + /* Remove the instance empty from this scene, the items now have an overridden collection + * instead. */ + ED_object_base_free_and_unlink(bmain, scene, obact); + } + else { + /* Remove the found root ID from the view layer. */ + switch (GS(id_root->name)) { + case ID_GR: { + Collection *collection_root = (Collection *)id_root; + LISTBASE_FOREACH_MUTABLE ( + CollectionParent *, collection_parent, &collection_root->parents) { + if (ID_IS_LINKED(collection_parent->collection) || + !BKE_view_layer_has_collection(view_layer, collection_parent->collection)) { + continue; + } + BKE_collection_child_remove(bmain, collection_parent->collection, collection_root); + } + break; + } + case ID_OB: { + /* TODO: Not sure how well we can handle this case, when we don't have the collections as + * reference containers... */ + break; + } + default: + break; + } + } } DEG_id_tag_update(&CTX_data_scene(C)->id, ID_RECALC_BASE_FLAGS | ID_RECALC_COPY_ON_WRITE); @@ -2385,10 +2403,11 @@ static int make_override_library_exec(bContext *C, wmOperator *op) } /* Set the object to override. */ -static int make_override_library_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int make_override_library_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); Object *obact = ED_object_active_context(C); /* Sanity checks. */ @@ -2402,16 +2421,37 @@ static int make_override_library_invoke(bContext *C, wmOperator *op, const wmEve return make_override_library_exec(C, op); } - if (ID_IS_LINKED(obact)) { - /* Show menu with list of directly linked collections containing the active object. */ - WM_enum_search_invoke(C, op, event); + if (!ID_IS_LINKED(obact)) { + BKE_report(op->reports, RPT_ERROR, "Cannot make library override from a local object"); return OPERATOR_CANCELLED; } - /* Error.. cannot continue. */ - BKE_report(op->reports, - RPT_ERROR, - "Can only make library override for a referenced object or collection"); + int potential_root_collections_num = 0; + uint collection_session_uuid = MAIN_ID_SESSION_UUID_UNSET; + LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { + /* Only check for directly linked collections. */ + if (!ID_IS_LINKED(&collection->id) || (collection->id.tag & LIB_TAG_INDIRECT) != 0 || + !BKE_view_layer_has_collection(view_layer, collection)) { + continue; + } + if (BKE_collection_has_object_recursive(collection, obact)) { + if (potential_root_collections_num == 0) { + collection_session_uuid = collection->id.session_uuid; + } + potential_root_collections_num++; + } + } + + if (potential_root_collections_num <= 1) { + RNA_property_int_set(op->ptr, op->type->prop, *((int *)&collection_session_uuid)); + return make_override_library_exec(C, op); + } + + BKE_reportf(op->reports, + RPT_ERROR, + "Too many potential root collections (%d) for the override hierarchy, " + "please use the Outliner instead", + potential_root_collections_num); return OPERATOR_CANCELLED; } @@ -2426,37 +2466,6 @@ static bool make_override_library_poll(bContext *C) !ID_IS_OVERRIDE_LIBRARY(obact)))); } -static const EnumPropertyItem *make_override_collections_of_linked_object_itemf( - bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) -{ - EnumPropertyItem item_tmp = {0}, *item = NULL; - int totitem = 0; - - Object *object = ED_object_active_context(C); - Main *bmain = CTX_data_main(C); - - if (!object || !ID_IS_LINKED(object)) { - return DummyRNA_DEFAULT_items; - } - - LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { - /* Only check for directly linked collections. */ - if (!ID_IS_LINKED(&collection->id) || (collection->id.tag & LIB_TAG_INDIRECT) != 0) { - continue; - } - if (BKE_collection_has_object_recursive(collection, object)) { - item_tmp.identifier = item_tmp.name = collection->id.name + 2; - item_tmp.value = *((int *)&collection->id.session_uuid); - RNA_enum_item_add(&item, &totitem, &item_tmp); - } - } - - RNA_enum_item_end(&item, &totitem); - *r_free = true; - - return item; -} - void OBJECT_OT_make_override_library(wmOperatorType *ot) { /* identifiers */ @@ -2474,15 +2483,17 @@ void OBJECT_OT_make_override_library(wmOperatorType *ot) /* properties */ PropertyRNA *prop; - prop = RNA_def_enum(ot->srna, - "collection", - DummyRNA_DEFAULT_items, - MAIN_ID_SESSION_UUID_UNSET, - "Override Collection", - "Name of directly linked collection containing the selected object, to make " - "an override from"); - RNA_def_enum_funcs(prop, make_override_collections_of_linked_object_itemf); - RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); + prop = RNA_def_int(ot->srna, + "collection", + MAIN_ID_SESSION_UUID_UNSET, + INT_MIN, + INT_MAX, + "Override Collection", + "Session UUID of the directly linked collection containing the selected " + "object, to make an override from", + INT_MIN, + INT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); ot->prop = prop; prop = RNA_def_boolean(ot->srna, diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index ad815f0d998..f58baa0e25c 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -145,6 +145,10 @@ void ED_region_do_listen(wmRegionListenerParams *params) region->type->listener(params); } + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { + UI_block_views_listen(block, params); + } + LISTBASE_FOREACH (uiList *, list, ®ion->ui_lists) { if (list->type && list->type->listener) { list->type->listener(list, params); @@ -3454,9 +3458,9 @@ ScrArea *ED_area_find_under_cursor(const bContext *C, int spacetype, const int x if (!area) { /* Check all windows except the active one. */ int scr_pos[2]; - wmWindow *r_win = WM_window_find_under_cursor(win, xy, scr_pos); - if (r_win && r_win != win) { - win = r_win; + wmWindow *win_other = WM_window_find_under_cursor(win, xy, scr_pos); + if (win_other && win_other != win) { + win = win_other; screen = WM_window_get_active_screen(win); area = BKE_screen_find_area_xy(screen, spacetype, scr_pos); } diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 80b0862bbab..55721e6380d 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -665,8 +665,8 @@ bool ED_operator_mask(bContext *C) if (area && area->spacedata.first) { switch (area->spacetype) { case SPACE_CLIP: { - SpaceClip *screen = area->spacedata.first; - return ED_space_clip_check_show_maskedit(screen); + SpaceClip *space_clip = area->spacedata.first; + return ED_space_clip_check_show_maskedit(space_clip); } case SPACE_SEQ: { SpaceSeq *sseq = area->spacedata.first; diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index fe48485c7d5..c5ebcf870a3 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -1359,7 +1359,7 @@ static void paint_cursor_sculpt_session_update_and_init(PaintCursorContext *pcon ViewContext *vc = &pcontext->vc; SculptCursorGeometryInfo gi; - const float mouse[2] = { + const float mval_fl[2] = { pcontext->x - pcontext->region->winrct.xmin, pcontext->y - pcontext->region->winrct.ymin, }; @@ -1369,7 +1369,7 @@ static void paint_cursor_sculpt_session_update_and_init(PaintCursorContext *pcon pcontext->prev_active_vertex_index = ss->active_vertex_index; if (!ups->stroke_active) { pcontext->is_cursor_over_mesh = SCULPT_cursor_geometry_info_update( - C, &gi, mouse, (pcontext->brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE)); + C, &gi, mval_fl, (pcontext->brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE)); copy_v3_v3(pcontext->location, gi.location); copy_v3_v3(pcontext->normal, gi.normal); } diff --git a/source/blender/editors/sculpt_paint/paint_image.cc b/source/blender/editors/sculpt_paint/paint_image.cc index e326d385593..56cd4751881 100644 --- a/source/blender/editors/sculpt_paint/paint_image.cc +++ b/source/blender/editors/sculpt_paint/paint_image.cc @@ -272,7 +272,7 @@ static bool image_paint_poll_ex(bContext *C, bool check_tool) SpaceImage *sima = CTX_wm_space_image(C); if (sima) { - if (sima->image != NULL && + if (sima->image != nullptr && (ID_IS_LINKED(sima->image) || ID_IS_OVERRIDE_LIBRARY(sima->image))) { return false; } diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 48397922ca8..89bbf2a3c92 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -1584,9 +1584,9 @@ static int sculpt_trim_gesture_box_invoke(bContext *C, wmOperator *op, const wmE SculptSession *ss = ob->sculpt; SculptCursorGeometryInfo sgi; - const float mouse[2] = {event->mval[0], event->mval[1]}; + const float mval_fl[2] = {UNPACK2(event->mval)}; SCULPT_vertex_random_access_ensure(ss); - ss->gesture_initial_hit = SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + ss->gesture_initial_hit = SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); if (ss->gesture_initial_hit) { copy_v3_v3(ss->gesture_initial_location, sgi.location); copy_v3_v3(ss->gesture_initial_normal, sgi.normal); @@ -1625,9 +1625,9 @@ static int sculpt_trim_gesture_lasso_invoke(bContext *C, wmOperator *op, const w SculptSession *ss = ob->sculpt; SculptCursorGeometryInfo sgi; - const float mouse[2] = {event->mval[0], event->mval[1]}; + const float mval_fl[2] = {UNPACK2(event->mval)}; SCULPT_vertex_random_access_ensure(ss); - ss->gesture_initial_hit = SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + ss->gesture_initial_hit = SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); if (ss->gesture_initial_hit) { copy_v3_v3(ss->gesture_initial_location, sgi.location); copy_v3_v3(ss->gesture_initial_normal, sgi.normal); diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index 926a564184a..0f2b02ed3ab 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -82,11 +82,86 @@ static void BRUSH_OT_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +static eGPBrush_Presets gpencil_get_brush_preset_from_tool(bToolRef *tool, + enum eContextObjectMode mode) +{ + switch (mode) { + case CTX_MODE_PAINT_GPENCIL: { + if (STREQ(tool->runtime->data_block, "DRAW")) { + return GP_BRUSH_PRESET_PENCIL; + } + else if (STREQ(tool->runtime->data_block, "FILL")) { + return GP_BRUSH_PRESET_FILL_AREA; + } + else if (STREQ(tool->runtime->data_block, "ERASE")) { + return GP_BRUSH_PRESET_ERASER_SOFT; + } + else if (STREQ(tool->runtime->data_block, "TINT")) { + return GP_BRUSH_PRESET_TINT; + } + break; + } + case CTX_MODE_SCULPT_GPENCIL: { + if (STREQ(tool->runtime->data_block, "SMOOTH")) { + return GP_BRUSH_PRESET_SMOOTH_STROKE; + } + else if (STREQ(tool->runtime->data_block, "STRENGTH")) { + return GP_BRUSH_PRESET_STRENGTH_STROKE; + } + else if (STREQ(tool->runtime->data_block, "THICKNESS")) { + return GP_BRUSH_PRESET_THICKNESS_STROKE; + } + else if (STREQ(tool->runtime->data_block, "GRAB")) { + return GP_BRUSH_PRESET_GRAB_STROKE; + } + else if (STREQ(tool->runtime->data_block, "PUSH")) { + return GP_BRUSH_PRESET_PUSH_STROKE; + } + else if (STREQ(tool->runtime->data_block, "TWIST")) { + return GP_BRUSH_PRESET_TWIST_STROKE; + } + else if (STREQ(tool->runtime->data_block, "PINCH")) { + return GP_BRUSH_PRESET_PINCH_STROKE; + } + else if (STREQ(tool->runtime->data_block, "RANDOMIZE")) { + return GP_BRUSH_PRESET_RANDOMIZE_STROKE; + } + else if (STREQ(tool->runtime->data_block, "CLONE")) { + return GP_BRUSH_PRESET_CLONE_STROKE; + } + break; + } + case CTX_MODE_WEIGHT_GPENCIL: { + return GP_BRUSH_PRESET_DRAW_WEIGHT; + } + case CTX_MODE_VERTEX_GPENCIL: { + if (STREQ(tool->runtime->data_block, "DRAW")) { + return GP_BRUSH_PRESET_VERTEX_DRAW; + } + else if (STREQ(tool->runtime->data_block, "BLUR")) { + return GP_BRUSH_PRESET_VERTEX_BLUR; + } + else if (STREQ(tool->runtime->data_block, "AVERAGE")) { + return GP_BRUSH_PRESET_VERTEX_AVERAGE; + } + else if (STREQ(tool->runtime->data_block, "SMEAR")) { + return GP_BRUSH_PRESET_VERTEX_SMEAR; + } + else if (STREQ(tool->runtime->data_block, "REPLACE")) { + return GP_BRUSH_PRESET_VERTEX_REPLACE; + } + break; + } + default: + return GP_BRUSH_PRESET_UNKNOWN; + break; + } + return GP_BRUSH_PRESET_UNKNOWN; +} + static int brush_add_gpencil_exec(bContext *C, wmOperator *UNUSED(op)) { - // int type = RNA_enum_get(op->ptr, "type"); - ToolSettings *ts = CTX_data_tool_settings(C); - Paint *paint = &ts->gp_paint->paint; + Paint *paint = BKE_paint_get_active_from_context(C); Brush *br = BKE_paint_brush(paint); Main *bmain = CTX_data_main(C); @@ -94,15 +169,70 @@ static int brush_add_gpencil_exec(bContext *C, wmOperator *UNUSED(op)) br = (Brush *)BKE_id_copy(bmain, &br->id); } else { - br = BKE_brush_add(bmain, "Brush", OB_MODE_PAINT_GPENCIL); + /* Get the active tool to determine what type of brush is active. */ + bScreen *screen = CTX_wm_screen(C); + if (screen == NULL) { + return OPERATOR_CANCELLED; + } - /* Init grease pencil specific data. */ - BKE_brush_init_gpencil_settings(br); - } + bToolRef *tool = NULL; + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + if (area->spacetype == SPACE_VIEW3D) { + /* Check the current tool is a brush. */ + bToolRef *tref = area->runtime.tool; + if (tref && tref->runtime && tref->runtime->data_block[0]) { + tool = tref; + break; + } + } + } - id_us_min(&br->id); /* fake user only */ + if (tool == NULL) { + return OPERATOR_CANCELLED; + } - BKE_paint_brush_set(paint, br); + /* Get Brush mode base on context mode. */ + const enum eContextObjectMode mode = CTX_data_mode_enum(C); + eObjectMode obmode = OB_MODE_PAINT_GPENCIL; + switch (mode) { + case CTX_MODE_PAINT_GPENCIL: + obmode = OB_MODE_PAINT_GPENCIL; + break; + case CTX_MODE_SCULPT_GPENCIL: + obmode = OB_MODE_SCULPT_GPENCIL; + break; + case CTX_MODE_WEIGHT_GPENCIL: + obmode = OB_MODE_WEIGHT_GPENCIL; + break; + case CTX_MODE_VERTEX_GPENCIL: + obmode = OB_MODE_VERTEX_GPENCIL; + break; + default: + return OPERATOR_CANCELLED; + break; + } + + /* Get brush preset using the actual tool. */ + eGPBrush_Presets preset = gpencil_get_brush_preset_from_tool(tool, mode); + + /* Capitalize Brush name first letter using the tool name. */ + char name[64]; + BLI_strncpy(name, tool->runtime->data_block, sizeof(name)); + BLI_str_tolower_ascii(name, sizeof(name)); + name[0] = BLI_toupper_ascii(name[0]); + + /* Create the brush and assign default values. */ + br = BKE_brush_add(bmain, name, obmode); + if (br) { + BKE_brush_init_gpencil_settings(br); + BKE_gpencil_brush_preset_set(bmain, br, preset); + } + } + + if (br) { + id_us_min(&br->id); /* fake user only */ + BKE_paint_brush_set(paint, br); + } return OPERATOR_FINISHED; } diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index c3c69bf4e0a..63e6dc7e965 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -506,7 +506,7 @@ static bool paint_stroke_use_jitter(ePaintMode mode, Brush *brush, bool invert) /* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */ static void paint_brush_stroke_add_step( - bContext *C, wmOperator *op, PaintStroke *stroke, const float mouse_in[2], float pressure) + bContext *C, wmOperator *op, PaintStroke *stroke, const float mval[2], float pressure) { Scene *scene = CTX_data_scene(C); Paint *paint = BKE_paint_get_active_from_context(C); @@ -546,7 +546,7 @@ static void paint_brush_stroke_add_step( /* copy last position -before- jittering, or space fill code * will create too many dabs */ - copy_v2_v2(stroke->last_mouse_position, mouse_in); + copy_v2_v2(stroke->last_mouse_position, mval); stroke->last_pressure = pressure; if (paint_stroke_use_scene_spacing(brush, mode)) { @@ -562,24 +562,24 @@ static void paint_brush_stroke_add_step( factor *= pressure; } - BKE_brush_jitter_pos(scene, brush, mouse_in, mouse_out); + BKE_brush_jitter_pos(scene, brush, mval, mouse_out); /* XXX: meh, this is round about because * BKE_brush_jitter_pos isn't written in the best way to * be reused here */ if (factor != 1.0f) { - sub_v2_v2v2(delta, mouse_out, mouse_in); + sub_v2_v2v2(delta, mouse_out, mval); mul_v2_fl(delta, factor); - add_v2_v2v2(mouse_out, mouse_in, delta); + add_v2_v2v2(mouse_out, mval, delta); } } else { - copy_v2_v2(mouse_out, mouse_in); + copy_v2_v2(mouse_out, mval); } bool is_location_is_set; ups->last_hit = paint_brush_update( - C, brush, mode, stroke, mouse_in, mouse_out, pressure, location, &is_location_is_set); + C, brush, mode, stroke, mval, mouse_out, pressure, location, &is_location_is_set); if (is_location_is_set) { copy_v3_v3(ups->last_location, location); } @@ -605,7 +605,7 @@ static void paint_brush_stroke_add_step( /* Mouse coordinates modified by the stroke type options. */ RNA_float_set_array(&itemptr, "mouse", mouse_out); /* Original mouse coordinates. */ - RNA_float_set_array(&itemptr, "mouse_event", mouse_in); + RNA_float_set_array(&itemptr, "mouse_event", mval); RNA_boolean_set(&itemptr, "pen_flip", stroke->pen_flip); RNA_float_set(&itemptr, "pressure", pressure); RNA_float_set(&itemptr, "x_tilt", stroke->x_tilt); diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index 8b548a89502..67b60acc667 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -1608,7 +1608,7 @@ static void smooth_brush_toggle_off(const bContext *C, Paint *paint, StrokeCache /* Initialize the stroke cache invariants from operator properties */ static void vwpaint_update_cache_invariants( - bContext *C, VPaint *vp, SculptSession *ss, wmOperator *op, const float mouse[2]) + bContext *C, VPaint *vp, SculptSession *ss, wmOperator *op, const float mval[2]) { StrokeCache *cache; const Scene *scene = CTX_data_scene(C); @@ -1629,8 +1629,8 @@ static void vwpaint_update_cache_invariants( } /* Initial mouse location */ - if (mouse) { - copy_v2_v2(cache->initial_mouse, mouse); + if (mval) { + copy_v2_v2(cache->initial_mouse, mval); } else { zero_v2(cache->initial_mouse); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 17a0463abd0..9ce80e4a433 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -4174,7 +4174,7 @@ static void smooth_brush_toggle_off(const bContext *C, Paint *paint, StrokeCache /* Initialize the stroke cache invariants from operator properties. */ static void sculpt_update_cache_invariants( - bContext *C, Sculpt *sd, SculptSession *ss, wmOperator *op, const float mouse[2]) + bContext *C, Sculpt *sd, SculptSession *ss, wmOperator *op, const float mval[2]) { StrokeCache *cache = MEM_callocN(sizeof(StrokeCache), "stroke cache"); UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; @@ -4204,8 +4204,8 @@ static void sculpt_update_cache_invariants( sculpt_init_mirror_clipping(ob, ss); /* Initial mouse location. */ - if (mouse) { - copy_v2_v2(cache->initial_mouse, mouse); + if (mval) { + copy_v2_v2(cache->initial_mouse, mval); } else { zero_v2(cache->initial_mouse); @@ -4343,7 +4343,8 @@ static bool sculpt_needs_delta_from_anchored_origin(Brush *brush) SCULPT_TOOL_POSE, SCULPT_TOOL_BOUNDARY, SCULPT_TOOL_THUMB, - SCULPT_TOOL_ELASTIC_DEFORM)) { + SCULPT_TOOL_ELASTIC_DEFORM, + SCULPT_TOOL_SMEAR)) { return true; } if (brush->sculpt_tool == SCULPT_TOOL_CLOTH && @@ -4373,7 +4374,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru { SculptSession *ss = ob->sculpt; StrokeCache *cache = ss->cache; - const float mouse[2] = { + const float mval[2] = { cache->mouse_event[0], cache->mouse_event[1], }; @@ -4392,6 +4393,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_POSE, SCULPT_TOOL_BOUNDARY, + SCULPT_TOOL_SMEAR, SCULPT_TOOL_THUMB) && !sculpt_brush_use_topology_rake(ss, brush)) { return; @@ -4413,9 +4415,9 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru add_v3_v3(cache->true_location, cache->grab_delta); } - /* Compute 3d coordinate at same z from original location + mouse. */ + /* Compute 3d coordinate at same z from original location + mval. */ mul_v3_m4v3(loc, ob->obmat, cache->orig_grab_location); - ED_view3d_win_to_3d(cache->vc->v3d, cache->vc->region, loc, mouse, grab_location); + ED_view3d_win_to_3d(cache->vc->v3d, cache->vc->region, loc, mval, grab_location); /* Compute delta to move verts by. */ if (!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { @@ -4783,7 +4785,7 @@ static void sculpt_find_nearest_to_ray_cb(PBVHNode *node, void *data_v, float *t } float SCULPT_raycast_init(ViewContext *vc, - const float mouse[2], + const float mval[2], float ray_start[3], float ray_end[3], float ray_normal[3], @@ -4797,7 +4799,7 @@ float SCULPT_raycast_init(ViewContext *vc, /* TODO: what if the segment is totally clipped? (return == 0). */ ED_view3d_win_to_segment_clipped( - vc->depsgraph, vc->region, vc->v3d, mouse, ray_start, ray_end, true); + vc->depsgraph, vc->region, vc->v3d, mval, ray_start, ray_end, true); invert_m4_m4(obimat, ob->obmat); mul_m4_v3(obimat, ray_start); @@ -4821,7 +4823,7 @@ float SCULPT_raycast_init(ViewContext *vc, bool SCULPT_cursor_geometry_info_update(bContext *C, SculptCursorGeometryInfo *out, - const float mouse[2], + const float mval[2], bool use_sampled_normal) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); @@ -4850,7 +4852,7 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, } /* PBVH raycast to get active vertex and face normal. */ - depth = SCULPT_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original); + depth = SCULPT_raycast_init(&vc, mval, ray_start, ray_end, ray_normal, original); SCULPT_stroke_modifiers_check(C, ob, brush); SculptRaycastData srd = { @@ -4948,7 +4950,7 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, return true; } -bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mouse[2]) +bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mval[2]) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Object *ob; @@ -4970,7 +4972,7 @@ bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mouse[2]) SCULPT_stroke_modifiers_check(C, ob, brush); - depth = SCULPT_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original); + depth = SCULPT_raycast_init(&vc, mval, ray_start, ray_end, ray_normal, original); if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { BM_mesh_elem_table_ensure(ss->bm, BM_VERT); @@ -5275,14 +5277,10 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up /* Returns whether the mouse/stylus is over the mesh (1) * or over the background (0). */ -static bool over_mesh(bContext *C, struct wmOperator *UNUSED(op), float x, float y) +static bool over_mesh(bContext *C, struct wmOperator *UNUSED(op), const float mval[2]) { - float mouse[2], co[3]; - - mouse[0] = x; - mouse[1] = y; - - return SCULPT_stroke_get_location(C, co, mouse); + float co_dummy[3]; + return SCULPT_stroke_get_location(C, co_dummy, mval); } bool SCULPT_handles_colors_report(SculptSession *ss, ReportList *reports) @@ -5303,14 +5301,13 @@ bool SCULPT_handles_colors_report(SculptSession *ss, ReportList *reports) return false; } -static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const float mouse[2]) +static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const float mval[2]) { - /* Don't start the stroke until mouse goes over the mesh. - * NOTE: mouse will only be null when re-executing the saved stroke. - * We have exception for 'exec' strokes since they may not set 'mouse', + /* Don't start the stroke until `mval` goes over the mesh. + * NOTE: `mval` will only be null when re-executing the saved stroke. + * We have exception for 'exec' strokes since they may not set `mval`, * only 'location', see: T52195. */ - if (((op->flag & OP_IS_INVOKE) == 0) || (mouse == NULL) || - over_mesh(C, op, mouse[0], mouse[1])) { + if (((op->flag & OP_IS_INVOKE) == 0) || (mval == NULL) || over_mesh(C, op, mval)) { Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; @@ -5328,10 +5325,10 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f ED_view3d_init_mats_rv3d(ob, CTX_wm_region_view3d(C)); - sculpt_update_cache_invariants(C, sd, ss, op, mouse); + sculpt_update_cache_invariants(C, sd, ss, op, mval); SculptCursorGeometryInfo sgi; - SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + SCULPT_cursor_geometry_info_update(C, &sgi, mval, false); /* Setup the correct undo system. Image painting and sculpting are mutual exclusive. * Color attributes are part of the sculpting undo system. */ @@ -5523,7 +5520,7 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent /* For tablet rotation. */ ignore_background_click = RNA_boolean_get(op->ptr, "ignore_background_click"); - if (ignore_background_click && !over_mesh(C, op, event->xy[0], event->xy[1])) { + if (ignore_background_click && !over_mesh(C, op, (const float[2]){UNPACK2(event->mval)})) { paint_stroke_free(C, op, op->customdata); return OPERATOR_PASS_THROUGH; } @@ -5783,7 +5780,8 @@ void SCULPT_connected_components_ensure(Object *ob) { SculptSession *ss = ob->sculpt; - /* Topology IDs already initialized. They only need to be recalculated when the PBVH is rebuild. + /* Topology IDs already initialized. They only need to be recalculated when the PBVH is + * rebuild. */ if (ss->vertex_info.connected_component) { return; @@ -5847,7 +5845,8 @@ void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist) SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); - /* Fake neighbors were already initialized with the same distance, so no need to be recalculated. + /* Fake neighbors were already initialized with the same distance, so no need to be + * recalculated. */ if (ss->fake_neighbors.fake_neighbor_index && ss->fake_neighbors.current_max_distance == max_dist) { diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index 94417defe48..bb101717c9b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -44,8 +44,8 @@ #include "bmesh.h" -#include <math.h> -#include <stdlib.h> +#include <cmath> +#include <cstdlib> using blender::IndexRange; @@ -57,7 +57,7 @@ AutomaskingCache *SCULPT_automasking_active_cache_get(SculptSession *ss) if (ss->filter_cache) { return ss->filter_cache->automasking; } - return NULL; + return nullptr; } bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd, @@ -170,13 +170,13 @@ static bool sculpt_automasking_is_constrained_by_radius(Brush *br) return false; } -typedef struct AutomaskFloodFillData { +struct AutomaskFloodFillData { float *automask_factor; float radius; bool use_radius; float location[3]; char symm; -} AutomaskFloodFillData; +}; static bool automask_floodfill_cb( SculptSession *ss, int from_v, int to_v, bool UNUSED(is_duplicate), void *userdata) @@ -197,7 +197,7 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) { BLI_assert_msg(0, "Topology masking: pmap missing"); - return NULL; + return nullptr; } const int totvert = SCULPT_vertex_count_get(ss); @@ -212,7 +212,7 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au const float radius = ss->cache ? ss->cache->radius : FLT_MAX; SCULPT_floodfill_add_active(sd, ob, ss, &flood, radius); - AutomaskFloodFillData fdata = {0}; + AutomaskFloodFillData fdata = {nullptr}; fdata.automask_factor = automask_factor; fdata.radius = radius; @@ -232,12 +232,12 @@ static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *a Brush *brush = BKE_paint_brush(&sd->paint); if (!SCULPT_is_automasking_enabled(sd, ss, brush)) { - return NULL; + return nullptr; } if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) { BLI_assert_msg(0, "Face Sets automasking: pmap missing"); - return NULL; + return nullptr; } int tot_vert = SCULPT_vertex_count_get(ss); @@ -262,7 +262,7 @@ float *SCULPT_boundary_automasking_init(Object *ob, if (!ss->pmap) { BLI_assert_msg(0, "Boundary Edges masking: pmap missing"); - return NULL; + return nullptr; } const int totvert = SCULPT_vertex_count_get(ss); @@ -327,7 +327,7 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object const int totvert = SCULPT_vertex_count_get(ss); if (!SCULPT_is_automasking_enabled(sd, ss, brush)) { - return NULL; + return nullptr; } AutomaskingCache *automasking = (AutomaskingCache *)MEM_callocN(sizeof(AutomaskingCache), diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index dcf90f9e819..9d231f2ccd2 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -1553,11 +1553,9 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent const eSculptClothFilterType filter_type = RNA_enum_get(op->ptr, "type"); /* Update the active vertex */ - float mouse[2]; + float mval_fl[2] = {UNPACK2(event->mval)}; SculptCursorGeometryInfo sgi; - mouse[0] = event->mval[0]; - mouse[1] = event->mval[1]; - SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); SCULPT_vertex_random_access_ensure(ss); diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.c b/source/blender/editors/sculpt_paint/sculpt_detail.c index 5275c6aac8a..00503087e39 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.c +++ b/source/blender/editors/sculpt_paint/sculpt_detail.c @@ -158,7 +158,7 @@ static EnumPropertyItem prop_sculpt_sample_detail_mode_types[] = { {0, NULL, 0, NULL, NULL}, }; -static void sample_detail_voxel(bContext *C, ViewContext *vc, int mx, int my) +static void sample_detail_voxel(bContext *C, ViewContext *vc, const int mval[2]) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Object *ob = vc->obact; @@ -169,8 +169,8 @@ static void sample_detail_voxel(bContext *C, ViewContext *vc, int mx, int my) SCULPT_vertex_random_access_ensure(ss); /* Update the active vertex. */ - const float mouse[2] = {mx, my}; - SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + const float mval_fl[2] = {UNPACK2(mval)}; + SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); /* Average the edge length of the connected edges to the active vertex. */ @@ -201,7 +201,7 @@ static void sculpt_raycast_detail_cb(PBVHNode *node, void *data_v, float *tmin) } } -static void sample_detail_dyntopo(bContext *C, ViewContext *vc, ARegion *region, int mx, int my) +static void sample_detail_dyntopo(bContext *C, ViewContext *vc, const int mval[2]) { Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Object *ob = vc->obact; @@ -209,9 +209,9 @@ static void sample_detail_dyntopo(bContext *C, ViewContext *vc, ARegion *region, SCULPT_stroke_modifiers_check(C, ob, brush); - const float mouse[2] = {mx - region->winrct.xmin, my - region->winrct.ymin}; + const float mval_fl[2] = {UNPACK2(mval)}; float ray_start[3], ray_end[3], ray_normal[3]; - float depth = SCULPT_raycast_init(vc, mouse, ray_start, ray_end, ray_normal, false); + float depth = SCULPT_raycast_init(vc, mval_fl, ray_start, ray_end, ray_normal, false); SculptDetailRaycastData srd; srd.hit = 0; @@ -228,14 +228,12 @@ static void sample_detail_dyntopo(bContext *C, ViewContext *vc, ARegion *region, } } -static int sample_detail(bContext *C, int mx, int my, int mode) +static int sample_detail(bContext *C, const int event_xy[2], int mode) { /* Find 3D view to pick from. */ bScreen *screen = CTX_wm_screen(C); - ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_VIEW3D, (const int[2]){mx, my}); - ARegion *region = (area) ? - BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, (const int[2]){mx, my}) : - NULL; + ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_VIEW3D, event_xy); + ARegion *region = (area) ? BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, event_xy) : NULL; if (region == NULL) { return OPERATOR_CANCELLED; } @@ -260,6 +258,11 @@ static int sample_detail(bContext *C, int mx, int my, int mode) return OPERATOR_CANCELLED; } + const int mval[2] = { + event_xy[0] - region->winrct.xmin, + event_xy[1] - region->winrct.ymin, + }; + /* Pick sample detail. */ switch (mode) { case SAMPLE_DETAIL_DYNTOPO: @@ -268,7 +271,7 @@ static int sample_detail(bContext *C, int mx, int my, int mode) CTX_wm_region_set(C, prev_region); return OPERATOR_CANCELLED; } - sample_detail_dyntopo(C, &vc, region, mx, my); + sample_detail_dyntopo(C, &vc, mval); break; case SAMPLE_DETAIL_VOXEL: if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { @@ -276,7 +279,7 @@ static int sample_detail(bContext *C, int mx, int my, int mode) CTX_wm_region_set(C, prev_region); return OPERATOR_CANCELLED; } - sample_detail_voxel(C, &vc, mx, my); + sample_detail_voxel(C, &vc, mval); break; } @@ -292,7 +295,7 @@ static int sculpt_sample_detail_size_exec(bContext *C, wmOperator *op) int ss_co[2]; RNA_int_get_array(op->ptr, "location", ss_co); int mode = RNA_enum_get(op->ptr, "mode"); - return sample_detail(C, ss_co[0], ss_co[1], mode); + return sample_detail(C, ss_co, mode); } static int sculpt_sample_detail_size_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e)) @@ -308,12 +311,10 @@ static int sculpt_sample_detail_size_modal(bContext *C, wmOperator *op, const wm switch (event->type) { case LEFTMOUSE: if (event->val == KM_PRESS) { - const int ss_co[2] = {event->xy[0], event->xy[1]}; - int mode = RNA_enum_get(op->ptr, "mode"); - sample_detail(C, ss_co[0], ss_co[1], mode); + sample_detail(C, event->xy, mode); - RNA_int_set_array(op->ptr, "location", ss_co); + RNA_int_set_array(op->ptr, "location", event->xy); WM_cursor_modal_restore(CTX_wm_window(C)); ED_workspace_status_text(C, NULL); WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL); diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c index 644f14905ba..fbf988d63e8 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -1450,13 +1450,11 @@ static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int v * Updates the #SculptSession cursor data and gets the active vertex * if the cursor is over the mesh. */ -static int sculpt_expand_target_vertex_update_and_get(bContext *C, - Object *ob, - const float mouse[2]) +static int sculpt_expand_target_vertex_update_and_get(bContext *C, Object *ob, const float mval[2]) { SculptSession *ss = ob->sculpt; SculptCursorGeometryInfo sgi; - if (SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false)) { + if (SCULPT_cursor_geometry_info_update(C, &sgi, mval, false)) { return SCULPT_active_vertex_get(ss); } return SCULPT_EXPAND_VERTEX_NONE; @@ -1588,16 +1586,16 @@ static void sculpt_expand_find_active_connected_components_from_vert(Object *ob, static void sculpt_expand_set_initial_components_for_mouse(bContext *C, Object *ob, ExpandCache *expand_cache, - const float mouse[2]) + const float mval[2]) { SculptSession *ss = ob->sculpt; - int initial_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mouse); + int initial_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mval); if (initial_vertex == SCULPT_EXPAND_VERTEX_NONE) { /* Cursor not over the mesh, for creating valid initial falloffs, fallback to the last active * vertex in the sculpt session. */ initial_vertex = SCULPT_active_vertex_get(ss); } - copy_v2_v2(ss->expand_cache->initial_mouse, mouse); + copy_v2_v2(ss->expand_cache->initial_mouse, mval); expand_cache->initial_active_vertex = initial_vertex; expand_cache->initial_active_face_set = SCULPT_active_face_set_get(ss); @@ -1629,14 +1627,14 @@ static void sculpt_expand_move_propagation_origin(bContext *C, { Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - const float mouse[2] = {event->mval[0], event->mval[1]}; + const float mval_fl[2] = {UNPACK2(event->mval)}; float move_disp[2]; - sub_v2_v2v2(move_disp, mouse, expand_cache->initial_mouse_move); + sub_v2_v2v2(move_disp, mval_fl, expand_cache->initial_mouse_move); - float new_mouse[2]; - add_v2_v2v2(new_mouse, move_disp, expand_cache->original_mouse_move); + float new_mval[2]; + add_v2_v2v2(new_mval, move_disp, expand_cache->original_mouse_move); - sculpt_expand_set_initial_components_for_mouse(C, ob, expand_cache, new_mouse); + sculpt_expand_set_initial_components_for_mouse(C, ob, expand_cache, new_mval); sculpt_expand_falloff_factors_from_vertex_and_symm_create( expand_cache, sd, @@ -1697,8 +1695,8 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event sculpt_expand_ensure_sculptsession_data(ob); /* Update and get the active vertex (and face) from the cursor. */ - const float mouse[2] = {event->mval[0], event->mval[1]}; - const int target_expand_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mouse); + const float mval_fl[2] = {UNPACK2(event->mval)}; + const int target_expand_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mval_fl); /* Handle the modal keymap state changes. */ ExpandCache *expand_cache = ss->expand_cache; @@ -1756,7 +1754,7 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event } expand_cache->move = true; expand_cache->move_original_falloff_type = expand_cache->falloff_type; - copy_v2_v2(expand_cache->initial_mouse_move, mouse); + copy_v2_v2(expand_cache->initial_mouse_move, mval_fl); copy_v2_v2(expand_cache->original_mouse_move, expand_cache->initial_mouse); if (expand_cache->falloff_type == SCULPT_EXPAND_FALLOFF_GEODESIC && SCULPT_vertex_count_get(ss) > expand_cache->max_geodesic_move_preview) { diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index d03c5ecab4d..ce704e619ea 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -949,11 +949,9 @@ static int sculpt_face_sets_change_visibility_invoke(bContext *C, /* Update the active vertex and Face Set using the cursor position to avoid relying on the paint * cursor updates. */ SculptCursorGeometryInfo sgi; - float mouse[2]; - mouse[0] = event->mval[0]; - mouse[1] = event->mval[1]; + const float mval_fl[2] = {UNPACK2(event->mval)}; SCULPT_vertex_random_access_ensure(ss); - SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); return sculpt_face_sets_change_visibility_exec(C, op); } @@ -1401,8 +1399,8 @@ static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEven /* Update the current active Face Set and Vertex as the operator can be used directly from the * tool without brush cursor. */ SculptCursorGeometryInfo sgi; - const float mouse[2] = {event->mval[0], event->mval[1]}; - if (!SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false)) { + const float mval_fl[2] = {UNPACK2(event->mval)}; + if (!SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false)) { /* The cursor is not over the mesh. Cancel to avoid editing the last updated Face Set ID. */ return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index 165e87eb2cd..95c01d24c6d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -339,11 +339,9 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent if (use_automasking) { /* Update the active face set manually as the paint cursor is not enabled when using the Mesh * Filter Tool. */ - float mouse[2]; + float mval_fl[2] = {UNPACK2(event->mval)}; SculptCursorGeometryInfo sgi; - mouse[0] = event->mval[0]; - mouse[1] = event->mval[1]; - SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); } /* Disable for multires and dyntopo for now */ diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c index a32b1c3015b..dbed5624adf 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -675,11 +675,9 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent if (use_automasking) { /* Update the active face set manually as the paint cursor is not enabled when using the Mesh * Filter Tool. */ - float mouse[2]; + float mval_fl[2] = {UNPACK2(event->mval)}; SculptCursorGeometryInfo sgi; - mouse[0] = event->mval[0]; - mouse[1] = event->mval[1]; - SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); } SCULPT_vertex_random_access_ensure(ss); diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c index 201e02b8235..4593c6a8b60 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c @@ -167,10 +167,8 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * if (RNA_boolean_get(op->ptr, "use_cursor")) { SculptCursorGeometryInfo sgi; - float mouse[2]; - mouse[0] = event->mval[0]; - mouse[1] = event->mval[1]; - if (SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false)) { + const float mval_fl[2] = {UNPACK2(event->mval)}; + if (SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false)) { /* The cursor is over the mesh, get the update iteration from the updated active vertex. */ mask_expand_update_it = ss->filter_cache->mask_update_it[(int)SCULPT_active_vertex_get(ss)]; } @@ -340,16 +338,14 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent const bool create_face_set = RNA_boolean_get(op->ptr, "create_face_set"); SculptCursorGeometryInfo sgi; - float mouse[2]; - mouse[0] = event->mval[0]; - mouse[1] = event->mval[1]; + const float mval_fl[2] = {UNPACK2(event->mval)}; SCULPT_vertex_random_access_ensure(ss); op->customdata = MEM_mallocN(sizeof(float[2]), "initial mouse position"); - copy_v2_v2(op->customdata, mouse); + copy_v2_v2(op->customdata, mval_fl); - SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c index 477c7d5ae00..f16763be735 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.c +++ b/source/blender/editors/sculpt_paint/sculpt_ops.c @@ -1061,10 +1061,8 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven /* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse move, * so it needs to be updated here. */ SculptCursorGeometryInfo sgi; - float mouse[2]; - mouse[0] = event->mval[0]; - mouse[1] = event->mval[1]; - SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + const float mval_fl[2] = {UNPACK2(event->mval)}; + SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); SCULPT_undo_push_begin(ob, "Mask by color"); BKE_sculpt_color_layer_create_if_needed(ob); diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c index ac05652b058..fa9f24377da 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c @@ -380,6 +380,15 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + float brush_delta[3]; + + if (brush->flag & BRUSH_ANCHORED) { + copy_v3_v3(brush_delta, ss->cache->grab_delta_symmetry); + } + else { + sub_v3_v3v3(brush_delta, ss->cache->location, ss->cache->last_location); + } + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; @@ -404,7 +413,7 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, switch (brush->smear_deform_type) { case BRUSH_SMEAR_DEFORM_DRAG: - sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location); + copy_v3_v3(current_disp, brush_delta); break; case BRUSH_SMEAR_DEFORM_PINCH: sub_v3_v3v3(current_disp, ss->cache->location, vd.co); @@ -529,12 +538,10 @@ void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode const int totvert = SCULPT_vertex_count_get(ss); - if (SCULPT_stroke_is_first_brush_step(ss->cache)) { - if (!ss->cache->prev_colors) { - ss->cache->prev_colors = MEM_callocN(sizeof(float[4]) * totvert, "prev colors"); - for (int i = 0; i < totvert; i++) { - SCULPT_vertex_color_get(ss, i, ss->cache->prev_colors[i]); - } + if (!ss->cache->prev_colors) { + ss->cache->prev_colors = MEM_callocN(sizeof(float[4]) * totvert, "prev colors"); + for (int i = 0; i < totvert; i++) { + SCULPT_vertex_color_get(ss, i, ss->cache->prev_colors[i]); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c index cb0a3b75094..48033f3407e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -422,10 +422,11 @@ static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op) /* Pivot to ray-cast surface. */ else if (mode == SCULPT_PIVOT_POSITION_CURSOR_SURFACE) { float stroke_location[3]; - float mouse[2]; - mouse[0] = RNA_float_get(op->ptr, "mouse_x"); - mouse[1] = RNA_float_get(op->ptr, "mouse_y"); - if (SCULPT_stroke_get_location(C, stroke_location, mouse)) { + const float mval[2] = { + RNA_float_get(op->ptr, "mouse_x"), + RNA_float_get(op->ptr, "mouse_y"), + }; + if (SCULPT_stroke_get_location(C, stroke_location, mval)) { copy_v3_v3(ss->pivot_pos, stroke_location); } } diff --git a/source/blender/editors/space_clip/tracking_select.c b/source/blender/editors/space_clip/tracking_select.c index bbfbbd2cc58..28e304bfa74 100644 --- a/source/blender/editors/space_clip/tracking_select.c +++ b/source/blender/editors/space_clip/tracking_select.c @@ -394,8 +394,6 @@ static int select_exec(bContext *C, wmOperator *op) else if (deselect_all) { ed_tracking_deselect_all_tracks(tracksbase); ed_tracking_deselect_all_plane_tracks(plane_tracks_base); - /* Mask as well if we are in combined mask / track view. */ - ED_mask_deselect_all(C); } ED_clip_view_lock_state_restore_no_jump(C, &lock_state); diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index e5a4919f548..87595ecdb88 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -407,8 +407,15 @@ std::string AssetCatalogDropController::drop_tooltip_asset_list(const wmDrag &dr std::string basic_tip = is_multiple_assets ? TIP_("Move assets to catalog") : TIP_("Move asset to catalog"); - return basic_tip + ": " + catalog_item_.get_name() + " (" + catalog_item_.catalog_path().str() + - ")"; + basic_tip += ": " + catalog_item_.get_name(); + + /* Display the full catalog path, but only if it's not exactly the same as the already shown name + * (i.e. not a root level catalog with no parent). */ + if (catalog_item_.get_name() != catalog_item_.catalog_path().str()) { + basic_tip += " (" + catalog_item_.catalog_path().str() + ")"; + } + + return basic_tip; } bool AssetCatalogDropController::on_drop(struct bContext *C, const wmDrag &drag) diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index 1ee6445f4ba..655a7983e2b 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -83,6 +83,7 @@ void FILE_OT_rename(struct wmOperatorType *ot); void FILE_OT_smoothscroll(struct wmOperatorType *ot); void FILE_OT_filepath_drop(struct wmOperatorType *ot); void FILE_OT_start_filter(struct wmOperatorType *ot); +void FILE_OT_edit_directory_path(struct wmOperatorType *ot); void FILE_OT_view_selected(struct wmOperatorType *ot); void file_directory_enter_handle(bContext *C, void *arg_unused, void *arg_but); diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 578288ca289..62bdd583bc1 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -2914,9 +2914,9 @@ void FILE_OT_delete(struct wmOperatorType *ot) static int file_start_filter_exec(bContext *C, wmOperator *UNUSED(op)) { - ScrArea *area = CTX_wm_area(C); - SpaceFile *sfile = CTX_wm_space_file(C); - FileSelectParams *params = ED_fileselect_get_active_params(sfile); + const ScrArea *area = CTX_wm_area(C); + const SpaceFile *sfile = CTX_wm_space_file(C); + const FileSelectParams *params = ED_fileselect_get_active_params(sfile); ARegion *region_ctx = CTX_wm_region(C); @@ -2950,6 +2950,46 @@ void FILE_OT_start_filter(struct wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Edit Directory Path Operator + * \{ */ + +static int file_edit_directory_path_exec(bContext *C, wmOperator *UNUSED(op)) +{ + const ScrArea *area = CTX_wm_area(C); + const SpaceFile *sfile = CTX_wm_space_file(C); + const FileSelectParams *params = ED_fileselect_get_active_params(sfile); + + ARegion *region_ctx = CTX_wm_region(C); + + if (area) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { + CTX_wm_region_set(C, region); + if (UI_textbutton_activate_rna(C, region, params, "directory")) { + break; + } + } + } + + CTX_wm_region_set(C, region_ctx); + + return OPERATOR_FINISHED; +} + +void FILE_OT_edit_directory_path(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Edit Directory Path"; + ot->description = "Start editing directory field"; + ot->idname = "FILE_OT_edit_directory_path"; + + /* api callbacks */ + ot->exec = file_edit_directory_path_exec; + ot->poll = ED_operator_file_active; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Macro Operators * \{ */ diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index ce36e3e4e4f..e42e1e98660 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -983,6 +983,7 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *region) if (params->display == FILE_IMGDISPLAY) { const float pad_fac = compact ? 0.15f : 0.3f; + /* Matches UI_preview_tile_size_x()/_y() by default. */ layout->prv_w = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_X; layout->prv_h = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_Y; layout->tile_border_x = pad_fac * UI_UNIT_X; @@ -1009,6 +1010,7 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *region) else if (params->display == FILE_VERTICALDISPLAY) { int rowcount; + /* Matches UI_preview_tile_size_x()/_y() by default. */ layout->prv_w = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_X; layout->prv_h = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_Y; layout->tile_border_x = 0.4f * UI_UNIT_X; @@ -1030,6 +1032,7 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *region) layout->flag = FILE_LAYOUT_VER; } else if (params->display == FILE_HORIZONTALDISPLAY) { + /* Matches UI_preview_tile_size_x()/_y() by default. */ layout->prv_w = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_X; layout->prv_h = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_Y; layout->tile_border_x = 0.4f * UI_UNIT_X; diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 0170361f244..a462476aae0 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -695,6 +695,7 @@ static void file_operatortypes(void) WM_operatortype_append(FILE_OT_smoothscroll); WM_operatortype_append(FILE_OT_filepath_drop); WM_operatortype_append(FILE_OT_start_filter); + WM_operatortype_append(FILE_OT_edit_directory_path); WM_operatortype_append(FILE_OT_view_selected); } diff --git a/source/blender/editors/space_info/info_stats.cc b/source/blender/editors/space_info/info_stats.cc index b817ff887ce..29a7eb150a1 100644 --- a/source/blender/editors/space_info/info_stats.cc +++ b/source/blender/editors/space_info/info_stats.cc @@ -46,6 +46,7 @@ #include "BKE_pbvh.h" #include "BKE_scene.h" #include "BKE_subdiv_ccg.h" +#include "BKE_subdiv_modifier.h" #include "DEG_depsgraph_query.h" @@ -92,15 +93,18 @@ static bool stats_mesheval(const Mesh *me_eval, bool is_selected, SceneStats *st } int totvert, totedge, totface, totloop; - if (me_eval->runtime.subdiv_ccg != nullptr) { - const SubdivCCG *subdiv_ccg = me_eval->runtime.subdiv_ccg; + + const SubdivCCG *subdiv_ccg = me_eval->runtime.subdiv_ccg; + const SubsurfRuntimeData *subsurf_runtime_data = me_eval->runtime.subsurf_runtime_data; + + if (subdiv_ccg != nullptr) { BKE_subdiv_ccg_topology_counters(subdiv_ccg, &totvert, &totedge, &totface, &totloop); } - else if (me_eval->runtime.subsurf_resolution != 0) { - totvert = me_eval->runtime.subsurf_totvert; - totedge = me_eval->runtime.subsurf_totedge; - totface = me_eval->runtime.subsurf_totpoly; - totloop = me_eval->runtime.subsurf_totloop; + else if (subsurf_runtime_data && subsurf_runtime_data->resolution != 0) { + totvert = subsurf_runtime_data->stats_totvert; + totedge = subsurf_runtime_data->stats_totedge; + totface = subsurf_runtime_data->stats_totpoly; + totloop = subsurf_runtime_data->stats_totloop; } else { totvert = me_eval->totvert; diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index 475d02020d0..ec19e8d5e5b 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -222,13 +222,33 @@ static void unlink_action_fn(bContext *C, } static void unlink_material_fn(bContext *UNUSED(C), - ReportList *UNUSED(reports), + ReportList *reports, Scene *UNUSED(scene), TreeElement *te, TreeStoreElem *tsep, - TreeStoreElem *UNUSED(tselem), + TreeStoreElem *tselem, void *UNUSED(user_data)) { + const bool te_is_material = TSE_IS_REAL_ID(tselem) && (GS(tselem->id->name) == ID_MA); + + if (!te_is_material) { + /* Just fail silently. Another element may be selected that is a material, we don't want to + * confuse users with an error in that case. */ + return; + } + + if (!tsep || !TSE_IS_REAL_ID(tsep)) { + /* Valid case, no parent element of the material or it is not an ID (could be a #TSE_ID_BASE + * for example) so there's no data to unlink from. */ + BKE_reportf(reports, + RPT_WARNING, + "Cannot unlink material '%s'. It's not clear which object or object-data it " + "should be unlinked from, there's no object or object-data as parent in the " + "Outliner tree", + tselem->id->name + 2); + return; + } + Material **matar = nullptr; int a, totcol = 0; diff --git a/source/blender/editors/space_outliner/outliner_tree.cc b/source/blender/editors/space_outliner/outliner_tree.cc index e6098631a68..aa739758ecb 100644 --- a/source/blender/editors/space_outliner/outliner_tree.cc +++ b/source/blender/editors/space_outliner/outliner_tree.cc @@ -802,7 +802,8 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, void *idv, TreeElement *parent, short type, - short index) + short index, + const bool expand) { ID *id = reinterpret_cast<ID *>(idv); @@ -894,10 +895,10 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, te->idcode = GS(id->name); } - if (te->abstract_element && te->abstract_element->isExpandValid()) { + if (expand && te->abstract_element && te->abstract_element->isExpandValid()) { tree_element_expand(*te->abstract_element, *space_outliner); } - else if (type == TSE_SOME_ID) { + else if (expand && (type == TSE_SOME_ID)) { /* ID types not (fully) ported to new design yet. */ if (te->abstract_element->expandPoll(*space_outliner)) { outliner_add_id_contents(space_outliner, te, tselem, id); diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc index 38025b58fd2..67798e978ab 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc @@ -34,13 +34,6 @@ TreeDisplayOverrideLibraryHierarchies::TreeDisplayOverrideLibraryHierarchies( { } -/* XXX Remove expanded subtree, we add our own items here. Expanding should probably be - * optional. */ -static void remove_expanded_children(TreeElement &te) -{ - outliner_free_tree(&te.subtree); -} - ListBase TreeDisplayOverrideLibraryHierarchies::buildTree(const TreeSourceData &source_data) { ListBase tree = {nullptr}; @@ -114,8 +107,7 @@ ListBase TreeDisplayOverrideLibraryHierarchies::build_hierarchy_for_lib_or_main( }); TreeElement *new_id_te = outliner_add_element( - &space_outliner_, &new_base_te->subtree, iter_id, new_base_te, TSE_SOME_ID, 0); - remove_expanded_children(*new_id_te); + &space_outliner_, &new_base_te->subtree, iter_id, new_base_te, TSE_SOME_ID, 0, false); build_hierarchy_for_ID(bmain, *iter_id, *tree_element_cast<TreeElementID>(new_id_te)); } @@ -193,8 +185,8 @@ static int build_hierarchy_foreach_ID_cb(LibraryIDLinkCallbackData *cb_data) &id, &build_data.parent_te->getLegacyElement(), TSE_SOME_ID, - 0); - remove_expanded_children(*new_te); + 0, + false); build_data.sibling_ids.add(&id); BuildHierarchyForeachIDCbData child_build_data{build_data.bmain, diff --git a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc index 80b3365766a..c8869d90eca 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc @@ -271,14 +271,14 @@ void ObjectsChildrenBuilder::make_object_parent_hierarchy_collections() if (!found) { /* We add the child in the tree even if it is not in the collection. - * We deliberately clear its sub-tree though, to make it less prominent. */ + * We don't expand its sub-tree though, to make it less prominent. */ TreeElement *child_ob_tree_element = outliner_add_element(&outliner_, &parent_ob_tree_element->subtree, child, parent_ob_tree_element, TSE_SOME_ID, - 0); - outliner_free_tree(&child_ob_tree_element->subtree); + 0, + false); child_ob_tree_element->flag |= TE_CHILD_NOT_IN_COLLECTION; child_ob_tree_elements.append(child_ob_tree_element); } diff --git a/source/blender/editors/space_outliner/tree/tree_element.hh b/source/blender/editors/space_outliner/tree/tree_element.hh index 0dcd75d340d..1098068d628 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.hh +++ b/source/blender/editors/space_outliner/tree/tree_element.hh @@ -94,13 +94,19 @@ class AbstractTreeElement { * \note "ID" is not always a real ID. * \note If child items are only added to the tree if the item is open, * the `TSE_` type _must_ be added to #outliner_element_needs_rebuild_on_open_change(). + * + * \param expand: If true, the element may add its own sub-tree. E.g. objects will list their + * animation data, object data, constraints, modifiers, ... This often adds visual + * noise, and can be expensive to add in big scenes. So prefer setting this to + * false. */ struct TreeElement *outliner_add_element(SpaceOutliner *space_outliner, ListBase *lb, void *idv, struct TreeElement *parent, short type, - short index); + short index, + const bool expand = true); void tree_element_expand(const AbstractTreeElement &tree_element, SpaceOutliner &space_outliner); diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc index 53e7b88c923..871de39b1dd 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc @@ -67,21 +67,38 @@ void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const for (auto *override_prop : ListBaseWrapper<IDOverrideLibraryProperty>(id.override_library->properties)) { + int rnaprop_index = 0; const bool is_rna_path_valid = BKE_lib_override_rna_property_find( - &idpoin, override_prop, &override_rna_ptr, &override_rna_prop); - if (is_rna_path_valid && !show_system_overrides && - ELEM(override_prop->rna_prop_type, PROP_POINTER, PROP_COLLECTION) && - RNA_struct_is_ID(RNA_property_pointer_type(&override_rna_ptr, override_rna_prop))) { - bool do_continue = true; - for (auto *override_prop_op : - ListBaseWrapper<IDOverrideLibraryPropertyOperation>(override_prop->operations)) { - if ((override_prop_op->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) == 0) { - do_continue = false; - break; + &idpoin, override_prop, &override_rna_ptr, &override_rna_prop, &rnaprop_index); + + /* Check for conditions where the liboverride property should be considered as a system + * override, if needed. */ + if (is_rna_path_valid && !show_system_overrides) { + bool do_skip = true; + bool is_system_override = false; + + /* Matching ID pointers are considered as system overrides. */ + if (ELEM(override_prop->rna_prop_type, PROP_POINTER, PROP_COLLECTION) && + RNA_struct_is_ID(RNA_property_pointer_type(&override_rna_ptr, override_rna_prop))) { + for (auto *override_prop_op : + ListBaseWrapper<IDOverrideLibraryPropertyOperation>(override_prop->operations)) { + if ((override_prop_op->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) == 0) { + do_skip = false; + break; + } + else { + is_system_override = true; + } } } - if (do_continue) { + /* Animated/driven properties are considered as system overrides. */ + if (!is_system_override && !BKE_lib_override_library_property_is_animated( + &id, override_prop, override_rna_prop, rnaprop_index)) { + do_skip = false; + } + + if (do_skip) { continue; } } diff --git a/source/blender/editors/space_outliner/tree/tree_iterator.cc b/source/blender/editors/space_outliner/tree/tree_iterator.cc index 85ff9e6437e..8d2b0b21433 100644 --- a/source/blender/editors/space_outliner/tree/tree_iterator.cc +++ b/source/blender/editors/space_outliner/tree/tree_iterator.cc @@ -43,13 +43,14 @@ void all_open(const SpaceOutliner &space_outliner, { LISTBASE_FOREACH_MUTABLE (TreeElement *, element, &subtree) { /* Get needed data out in case element gets freed. */ - const bool is_open = TSELEM_OPEN(element->store_elem, &space_outliner); + const TreeStoreElem *tselem = TREESTORE(element); const ListBase subtree = element->subtree; visitor(element); - /* Don't access element from now on, it may be freed. */ + /* Don't access element from now on, it may be freed. Note that the open/collapsed state may + * also have been changed in the visitor callback. */ - if (is_open) { + if (TSELEM_OPEN(tselem, &space_outliner)) { all_open(space_outliner, subtree, visitor); } } diff --git a/source/blender/editors/space_outliner/tree/tree_iterator.hh b/source/blender/editors/space_outliner/tree/tree_iterator.hh index e3b3c90eaad..de5bcd2c462 100644 --- a/source/blender/editors/space_outliner/tree/tree_iterator.hh +++ b/source/blender/editors/space_outliner/tree/tree_iterator.hh @@ -26,7 +26,7 @@ void all(const ListBase &subtree, VisitorFn visitor); /** * Preorder (meaning depth-first) traversal of all elements not part of a collapsed sub-tree. - * Freeing the currently visited element in \a visitor is fine. + * Freeing the currently visited element in \a visitor is fine (but not its tree-store element). */ void all_open(const SpaceOutliner &, VisitorFn visitor); void all_open(const SpaceOutliner &, const ListBase &subtree, VisitorFn visitor); diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index ea6d3351eaa..647d13a4d56 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -340,7 +340,7 @@ static void seq_load_apply_generic_options(bContext *C, wmOperator *op, Sequence ScrArea *area = CTX_wm_area(C); const bool use_sync_markers = (((SpaceSeq *)area->spacedata.first)->flag & SEQ_MARKER_TRANS) != 0; - SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, use_sync_markers); + SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, NULL, use_sync_markers); SEQ_collection_free(strip_col); } @@ -863,7 +863,7 @@ static void sequencer_add_movie_multiple_strips(bContext *C, ScrArea *area = CTX_wm_area(C); const bool use_sync_markers = (((SpaceSeq *)area->spacedata.first)->flag & SEQ_MARKER_TRANS) != 0; - SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, use_sync_markers); + SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, NULL, use_sync_markers); } SEQ_collection_free(strip_col); @@ -913,7 +913,7 @@ static bool sequencer_add_movie_single_strip(bContext *C, ScrArea *area = CTX_wm_area(C); const bool use_sync_markers = (((SpaceSeq *)area->spacedata.first)->flag & SEQ_MARKER_TRANS) != 0; - SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, use_sync_markers); + SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, NULL, use_sync_markers); } SEQ_collection_free(strip_col); diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 75966d4f070..86c438c616e 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -581,21 +581,13 @@ static int sequencer_slip_invoke(bContext *C, wmOperator *op, const wmEvent *eve static void sequencer_slip_recursively(Scene *scene, SlipData *data, int offset) { - /* Iterate in reverse so meta-strips are iterated after their children. */ for (int i = data->num_seq - 1; i >= 0; i--) { Sequence *seq = data->seq_array[i]; - int endframe; - /* Offset seq start. */ seq->start = data->ts[i].start + offset; - if (data->trim[i]) { - /* Find the end-frame. */ - endframe = seq->start + seq->len; - - /* Compute the sequence offsets. */ - seq->endofs = endframe - SEQ_time_right_handle_frame_get(seq); - seq->startofs = SEQ_time_left_handle_frame_get(seq) - seq->start; + seq->startofs = data->ts[i].startofs - offset; + seq->endofs = data->ts[i].endofs + offset; } } @@ -2397,6 +2389,13 @@ static void sequencer_copy_animation(Scene *scene, Sequence *seq) return; } + /* Add curves for strips inside meta strip. */ + if (seq->type == SEQ_TYPE_META) { + LISTBASE_FOREACH (Sequence *, meta_child, &seq->seqbase) { + sequencer_copy_animation(scene, meta_child); + } + } + GSet *fcurves = SEQ_fcurves_by_strip_get(seq, &scene->adt->action->curves); if (fcurves == NULL) { return; @@ -2406,6 +2405,7 @@ static void sequencer_copy_animation(Scene *scene, Sequence *seq) BLI_addtail(&fcurves_clipboard, BKE_fcurve_copy(fcu)); } GSET_FOREACH_END(); + BLI_gset_free(fcurves, NULL); } diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 422dc4ad29f..01f94bcab55 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -57,8 +57,6 @@ #include "BKE_tracking.h" #include "BKE_workspace.h" -#include "DEG_depsgraph.h" - #include "WM_api.h" #include "WM_toolsystem.h" #include "WM_types.h" diff --git a/source/blender/editors/transform/transform_convert_graph.c b/source/blender/editors/transform/transform_convert_graph.c index 54222fbb117..2039daee386 100644 --- a/source/blender/editors/transform/transform_convert_graph.c +++ b/source/blender/editors/transform/transform_convert_graph.c @@ -24,12 +24,9 @@ #include "UI_view2d.h" #include "transform.h" -#include "transform_snap.h" - #include "transform_convert.h" -#include "transform_snap.h" - #include "transform_mode.h" +#include "transform_snap.h" typedef struct TransDataGraph { float unit_scale; diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c index 68f04aab969..226b0f84f14 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.c +++ b/source/blender/editors/transform/transform_convert_sequencer.c @@ -281,296 +281,21 @@ static void seq_transform_cancel(TransInfo *t, SeqCollection *transformed_strips } } -static bool seq_transform_check_overlap(SeqCollection *transformed_strips) -{ - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, transformed_strips) { - if (seq->flag & SEQ_OVERLAP) { - return true; - } - } - return false; -} - -static SeqCollection *extract_standalone_strips(SeqCollection *transformed_strips) -{ - SeqCollection *collection = SEQ_collection_create(__func__); - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, transformed_strips) { - if ((seq->type & SEQ_TYPE_EFFECT) == 0 || seq->seq1 == NULL) { - SEQ_collection_append_strip(seq, collection); - } - } - return collection; -} - -/* Query strips positioned after left edge of transformed strips bound-box. */ -static SeqCollection *query_right_side_strips(ListBase *seqbase, SeqCollection *transformed_strips) -{ - int minframe = MAXFRAME; - { - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, transformed_strips) { - minframe = min_ii(minframe, SEQ_time_left_handle_frame_get(seq)); - } - } - - SeqCollection *collection = SEQ_collection_create(__func__); - LISTBASE_FOREACH (Sequence *, seq, seqbase) { - if ((seq->flag & SELECT) == 0 && SEQ_time_left_handle_frame_get(seq) >= minframe) { - SEQ_collection_append_strip(seq, collection); - } - } - return collection; -} - static ListBase *seqbase_active_get(const TransInfo *t) { Editing *ed = SEQ_editing_get(t->scene); return SEQ_active_seqbase_get(ed); } -/* Offset all strips positioned after left edge of transformed strips bound-box by amount equal - * to overlap of transformed strips. */ -static void seq_transform_handle_expand_to_fit(Scene *scene, - ListBase *seqbasep, - SeqCollection *transformed_strips, - bool use_sync_markers) -{ - ListBase *markers = &scene->markers; - - SeqCollection *right_side_strips = query_right_side_strips(seqbasep, transformed_strips); - - /* Temporarily move right side strips beyond timeline boundary. */ - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, right_side_strips) { - seq->machine += MAXSEQ * 2; - } - - /* Shuffle transformed standalone strips. This is because transformed strips can overlap with - * strips on left side. */ - SeqCollection *standalone_strips = extract_standalone_strips(transformed_strips); - SEQ_transform_seqbase_shuffle_time( - standalone_strips, seqbasep, scene, markers, use_sync_markers); - SEQ_collection_free(standalone_strips); - - /* Move temporarily moved strips back to their original place and tag for shuffling. */ - SEQ_ITERATOR_FOREACH (seq, right_side_strips) { - seq->machine -= MAXSEQ * 2; - } - /* Shuffle again to displace strips on right side. Final effect shuffling is done in - * SEQ_transform_handle_overlap. */ - SEQ_transform_seqbase_shuffle_time( - right_side_strips, seqbasep, scene, markers, use_sync_markers); - SEQ_collection_free(right_side_strips); -} - -static SeqCollection *query_overwrite_targets(ListBase *seqbasep, - SeqCollection *transformed_strips) -{ - SeqCollection *collection = SEQ_query_unselected_strips(seqbasep); - - Sequence *seq, *seq_transformed; - SEQ_ITERATOR_FOREACH (seq, collection) { - bool does_overlap = false; - - SEQ_ITERATOR_FOREACH (seq_transformed, transformed_strips) { - /* Effects of transformed strips can be unselected. These must not be included. */ - if (seq == seq_transformed) { - SEQ_collection_remove_strip(seq, collection); - } - if (SEQ_transform_test_overlap_seq_seq(seq, seq_transformed)) { - does_overlap = true; - } - } - - if (!does_overlap) { - SEQ_collection_remove_strip(seq, collection); - } - } - - return collection; -} - -typedef enum eOvelapDescrition { - /* No overlap. */ - STRIP_OVERLAP_NONE, - /* Overlapping strip covers overlapped completely. */ - STRIP_OVERLAP_IS_FULL, - /* Overlapping strip is inside overlapped. */ - STRIP_OVERLAP_IS_INSIDE, - /* Partial overlap between 2 strips. */ - STRIP_OVERLAP_LEFT_SIDE, - STRIP_OVERLAP_RIGHT_SIDE, -} eOvelapDescrition; - -static eOvelapDescrition overlap_description_get(const Sequence *transformed, - const Sequence *target) -{ - if (SEQ_time_left_handle_frame_get(transformed) <= SEQ_time_left_handle_frame_get(target) && - SEQ_time_right_handle_frame_get(transformed) >= SEQ_time_right_handle_frame_get(target)) { - return STRIP_OVERLAP_IS_FULL; - } - if (SEQ_time_left_handle_frame_get(transformed) > SEQ_time_left_handle_frame_get(target) && - SEQ_time_right_handle_frame_get(transformed) < SEQ_time_right_handle_frame_get(target)) { - return STRIP_OVERLAP_IS_INSIDE; - } - if (SEQ_time_left_handle_frame_get(transformed) <= SEQ_time_left_handle_frame_get(target) && - SEQ_time_left_handle_frame_get(target) <= SEQ_time_right_handle_frame_get(transformed)) { - return STRIP_OVERLAP_LEFT_SIDE; - } - if (SEQ_time_left_handle_frame_get(transformed) <= SEQ_time_right_handle_frame_get(target) && - SEQ_time_right_handle_frame_get(target) <= SEQ_time_right_handle_frame_get(transformed)) { - return STRIP_OVERLAP_RIGHT_SIDE; - } - return STRIP_OVERLAP_NONE; -} - -/* Split strip in 3 parts, remove middle part and fit transformed inside. */ -static void seq_transform_handle_overwrite_split(Scene *scene, - ListBase *seqbasep, - const Sequence *transformed, - Sequence *target) -{ - /* Because we are doing a soft split, bmain is not used in SEQ_edit_strip_split, so we can pass - * NULL here. */ - Main *bmain = NULL; - - Sequence *split_strip = SEQ_edit_strip_split(bmain, - scene, - seqbasep, - target, - SEQ_time_left_handle_frame_get(transformed), - SEQ_SPLIT_SOFT, - NULL); - SEQ_edit_strip_split(bmain, - scene, - seqbasep, - split_strip, - SEQ_time_right_handle_frame_get(transformed), - SEQ_SPLIT_SOFT, - NULL); - SEQ_edit_flag_for_removal(scene, seqbasep, split_strip); - SEQ_edit_remove_flagged_sequences(scene, seqbasep); -} - -/* Trim strips by adjusting handle position. - * This is bit more complicated in case overlap happens on effect. */ -static void seq_transform_handle_overwrite_trim(Scene *scene, - ListBase *seqbasep, - const Sequence *transformed, - Sequence *target, - const eOvelapDescrition overlap) -{ - SeqCollection *targets = SEQ_query_by_reference(target, seqbasep, SEQ_query_strip_effect_chain); - - /* Expand collection by adding all target's children, effects and their children. */ - if ((target->type & SEQ_TYPE_EFFECT) != 0) { - SEQ_collection_expand(seqbasep, targets, SEQ_query_strip_effect_chain); - } - - /* Trim all non effects, that have influence on effect length which is overlapping. */ - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, targets) { - if ((seq->type & SEQ_TYPE_EFFECT) != 0 && SEQ_effect_get_num_inputs(seq->type) > 0) { - continue; - } - if (overlap == STRIP_OVERLAP_LEFT_SIDE) { - SEQ_time_left_handle_frame_set(scene, seq, SEQ_time_right_handle_frame_get(transformed)); - } - else { - BLI_assert(overlap == STRIP_OVERLAP_RIGHT_SIDE); - SEQ_time_right_handle_frame_set(scene, seq, SEQ_time_left_handle_frame_get(transformed)); - } - } - SEQ_collection_free(targets); -} - -static void seq_transform_handle_overwrite(Scene *scene, - ListBase *seqbasep, - SeqCollection *transformed_strips) -{ - SeqCollection *targets = query_overwrite_targets(seqbasep, transformed_strips); - SeqCollection *strips_to_delete = SEQ_collection_create(__func__); - - Sequence *target; - Sequence *transformed; - SEQ_ITERATOR_FOREACH (target, targets) { - SEQ_ITERATOR_FOREACH (transformed, transformed_strips) { - if (transformed->machine != target->machine) { - continue; - } - - const eOvelapDescrition overlap = overlap_description_get(transformed, target); - - if (overlap == STRIP_OVERLAP_IS_FULL) { - SEQ_collection_append_strip(target, strips_to_delete); - } - else if (overlap == STRIP_OVERLAP_IS_INSIDE) { - seq_transform_handle_overwrite_split(scene, seqbasep, transformed, target); - } - else if (ELEM(overlap, STRIP_OVERLAP_LEFT_SIDE, STRIP_OVERLAP_RIGHT_SIDE)) { - seq_transform_handle_overwrite_trim(scene, seqbasep, transformed, target, overlap); - } - } - } - - SEQ_collection_free(targets); - - /* Remove covered strips. This must be done in separate loop, because `SEQ_edit_strip_split()` - * also uses `SEQ_edit_remove_flagged_sequences()`. See T91096. */ - if (SEQ_collection_len(strips_to_delete) > 0) { - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, strips_to_delete) { - SEQ_edit_flag_for_removal(scene, seqbasep, seq); - } - SEQ_edit_remove_flagged_sequences(scene, seqbasep); - } - SEQ_collection_free(strips_to_delete); -} - -static void seq_transform_handle_overlap_shuffle(Scene *scene, - ListBase *seqbasep, - SeqCollection *transformed_strips, - bool use_sync_markers) -{ - ListBase *markers = &scene->markers; - - /* Shuffle non strips with no effects attached. */ - SeqCollection *standalone_strips = extract_standalone_strips(transformed_strips); - SEQ_transform_seqbase_shuffle_time( - standalone_strips, seqbasep, scene, markers, use_sync_markers); - SEQ_collection_free(standalone_strips); -} - -void SEQ_transform_handle_overlap(Scene *scene, - ListBase *seqbasep, - SeqCollection *transformed_strips, - bool use_sync_markers) +static bool seq_transform_check_overlap(SeqCollection *transformed_strips) { - const eSeqOverlapMode overlap_mode = SEQ_tool_settings_overlap_mode_get(scene); - - switch (overlap_mode) { - case SEQ_OVERLAP_EXPAND: - seq_transform_handle_expand_to_fit(scene, seqbasep, transformed_strips, use_sync_markers); - break; - case SEQ_OVERLAP_OVERWRITE: - seq_transform_handle_overwrite(scene, seqbasep, transformed_strips); - break; - case SEQ_OVERLAP_SHUFFLE: - seq_transform_handle_overlap_shuffle(scene, seqbasep, transformed_strips, use_sync_markers); - break; - } - - /* If any effects still overlap, we need to move them up. - * In some cases other strips can be overlapping still, see T90646. */ Sequence *seq; SEQ_ITERATOR_FOREACH (seq, transformed_strips) { - if (SEQ_transform_test_overlap(seqbasep, seq)) { - SEQ_transform_seqbase_shuffle(seqbasep, seq, scene); + if (seq->flag & SEQ_OVERLAP) { + return true; } - seq->flag &= ~SEQ_OVERLAP; } + return false; } static SeqCollection *seq_transform_collection_from_transdata(TransDataContainer *tc) @@ -607,12 +332,14 @@ static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *c return; } + TransSeq *ts = tc->custom.type.data; ListBase *seqbasep = seqbase_active_get(t); Scene *scene = t->scene; const bool use_sync_markers = (((SpaceSeq *)t->area->spacedata.first)->flag & SEQ_MARKER_TRANS) != 0; if (seq_transform_check_overlap(transformed_strips)) { - SEQ_transform_handle_overlap(scene, seqbasep, transformed_strips, use_sync_markers); + SEQ_transform_handle_overlap( + scene, seqbasep, transformed_strips, ts->time_dependent_strips, use_sync_markers); } SEQ_collection_free(transformed_strips); diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.c b/source/blender/editors/transform/transform_convert_sequencer_image.c index f0dd0a42ddb..741b1e35838 100644 --- a/source/blender/editors/transform/transform_convert_sequencer_image.c +++ b/source/blender/editors/transform/transform_convert_sequencer_image.c @@ -223,6 +223,7 @@ void recalcData_sequencer_image(TransInfo *t) copy_v2_v2(translation, tdseq->orig_origin_position); sub_v2_v2(translation, origin); mul_v2_v2(translation, mirror); + translation[0] *= t->scene->r.yasp / t->scene->r.xasp; transform->xofs = tdseq->orig_translation[0] - translation[0]; transform->yofs = tdseq->orig_translation[1] - translation[1]; diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 62afc6ea1b5..649217092aa 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -79,7 +79,7 @@ static void TargetSnapActive(TransInfo *t); /** \name Implementations * \{ */ -static bool snapNodeTest(View2D *v2d, bNode *node, eSnapTargetSelect snap_select); +static bool snapNodeTest(View2D *v2d, bNode *node, eSnapTargetSelect snap_target_select); static NodeBorder snapNodeBorder(eSnapMode snap_node_mode); #if 0 diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt index 89d80d582f8..5c2a3374aa1 100644 --- a/source/blender/editors/util/CMakeLists.txt +++ b/source/blender/editors/util/CMakeLists.txt @@ -91,6 +91,7 @@ set(SRC ../include/ED_uvedit.h ../include/ED_view3d.h ../include/ED_view3d_offscreen.h + ../include/UI_grid_view.hh ../include/UI_icons.h ../include/UI_interface.h ../include/UI_interface.hh diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index cbc9b2a8d06..3618286ec01 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -298,6 +298,44 @@ void ED_uvedit_get_aspect(Object *ob, float *r_aspx, float *r_aspy) ED_uvedit_get_aspect_from_material(ob, efa->mat_nr, r_aspx, r_aspy); } +static bool uvedit_is_face_affected(const Scene *scene, + BMFace *efa, + const UnwrapOptions *options, + const int cd_loop_uv_offset) +{ + if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + return false; + } + + if (options->only_selected_faces && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + return false; + } + + if (options->topology_from_uvs && options->only_selected_uvs && + !uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) { + return false; + } + + return true; +} + +/* Prepare unique indices for each unique pinned UV, even if it shares a BMVert. + */ +static void uvedit_prepare_pinned_indices(ParamHandle *handle, + BMFace *efa, + const int cd_loop_uv_offset) +{ + BMIter liter; + BMLoop *l; + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (luv->flag & MLOOPUV_PINNED) { + int bmvertindex = BM_elem_index_get(l->v); + GEO_uv_prepare_pin_index(handle, bmvertindex, luv->uv); + } + } +} + static void construct_param_handle_face_add(ParamHandle *handle, const Scene *scene, BMFace *efa, @@ -319,7 +357,7 @@ static void construct_param_handle_face_add(ParamHandle *handle, BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - vkeys[i] = (ParamKey)BM_elem_index_get(l->v); + vkeys[i] = GEO_uv_find_pin_index(handle, BM_elem_index_get(l->v), luv->uv); co[i] = l->v->co; uv[i] = luv->uv; pin[i] = (luv->flag & MLOOPUV_PINNED) != 0; @@ -337,13 +375,10 @@ static ParamHandle *construct_param_handle(const Scene *scene, UnwrapResultInfo *result_info) { BMFace *efa; - BMLoop *l; BMEdge *eed; - BMIter iter, liter; + BMIter iter; int i; - const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); - ParamHandle *handle = GEO_uv_parametrizer_construct_begin(); if (options->correct_aspect) { @@ -359,30 +394,17 @@ static ParamHandle *construct_param_handle(const Scene *scene, /* we need the vert indices */ BM_mesh_elem_index_ensure(bm, BM_VERT); + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { - - if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) || - (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { - continue; + if (uvedit_is_face_affected(scene, efa, options, cd_loop_uv_offset)) { + uvedit_prepare_pinned_indices(handle, efa, cd_loop_uv_offset); } + } - if (options->topology_from_uvs) { - bool is_loopsel = false; - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (options->only_selected_uvs && - (uvedit_uv_select_test(scene, l, cd_loop_uv_offset) == false)) { - continue; - } - is_loopsel = true; - break; - } - if (is_loopsel == false) { - continue; - } + BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { + if (uvedit_is_face_affected(scene, efa, options, cd_loop_uv_offset)) { + construct_param_handle_face_add(handle, scene, efa, i, cd_loop_uv_offset); } - - construct_param_handle_face_add(handle, scene, efa, i, cd_loop_uv_offset); } if (!options->topology_from_uvs || options->topology_from_uvs_use_seams) { @@ -414,9 +436,8 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene, int *count_fail) { BMFace *efa; - BMLoop *l; BMEdge *eed; - BMIter iter, liter; + BMIter iter; int i; ParamHandle *handle = GEO_uv_parametrizer_construct_begin(); @@ -448,29 +469,15 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene, } BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { - - if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) || - (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { - continue; + if (uvedit_is_face_affected(scene, efa, options, cd_loop_uv_offset)) { + uvedit_prepare_pinned_indices(handle, efa, cd_loop_uv_offset); } + } - if (options->topology_from_uvs) { - bool is_loopsel = false; - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (options->only_selected_uvs && - (uvedit_uv_select_test(scene, l, cd_loop_uv_offset) == false)) { - continue; - } - is_loopsel = true; - break; - } - if (is_loopsel == false) { - continue; - } + BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { + if (uvedit_is_face_affected(scene, efa, options, cd_loop_uv_offset)) { + construct_param_handle_face_add(handle, scene, efa, i + offset, cd_loop_uv_offset); } - - construct_param_handle_face_add(handle, scene, efa, i + offset, cd_loop_uv_offset); } if (!options->topology_from_uvs || options->topology_from_uvs_use_seams) { diff --git a/source/blender/geometry/GEO_uv_parametrizer.h b/source/blender/geometry/GEO_uv_parametrizer.h index 7fe60a3a855..2181f95945e 100644 --- a/source/blender/geometry/GEO_uv_parametrizer.h +++ b/source/blender/geometry/GEO_uv_parametrizer.h @@ -14,6 +14,7 @@ extern "C" { typedef struct ParamHandle ParamHandle; /* Handle to an array of charts. */ typedef intptr_t ParamKey; /* Key (hash) for identifying verts and faces. */ +#define PARAM_KEY_MAX INTPTR_MAX /* -------------------------------------------------------------------- */ /** \name Chart Construction: @@ -34,6 +35,10 @@ ParamHandle *GEO_uv_parametrizer_construct_begin(void); void GEO_uv_parametrizer_aspect_ratio(ParamHandle *handle, float aspx, float aspy); +void GEO_uv_prepare_pin_index(ParamHandle *handle, const int bmvertindex, const float uv[2]); + +ParamKey GEO_uv_find_pin_index(ParamHandle *handle, const int bmvertindex, const float uv[2]); + void GEO_uv_parametrizer_face_add(ParamHandle *handle, const ParamKey key, const int nverts, diff --git a/source/blender/geometry/intern/uv_parametrizer.c b/source/blender/geometry/intern/uv_parametrizer.c index af3bcc3bdec..46ebe6cfdce 100644 --- a/source/blender/geometry/intern/uv_parametrizer.c +++ b/source/blender/geometry/intern/uv_parametrizer.c @@ -8,6 +8,7 @@ #include "BLI_boxpack_2d.h" #include "BLI_convexhull_2d.h" +#include "BLI_ghash.h" #include "BLI_heap.h" #include "BLI_math.h" #include "BLI_memarena.h" @@ -190,6 +191,9 @@ typedef struct ParamHandle { PHash *hash_edges; PHash *hash_faces; + struct GHash *pin_hash; + int unique_pin_count; + PChart **charts; int ncharts; @@ -3817,8 +3821,11 @@ void GEO_uv_parametrizer_delete(ParamHandle *phandle) p_chart_delete(phandle->charts[i]); } - if (phandle->charts) { - MEM_freeN(phandle->charts); + MEM_SAFE_FREE(phandle->charts); + + if (phandle->pin_hash) { + BLI_ghash_free(phandle->pin_hash, NULL, NULL); + phandle->pin_hash = NULL; } if (phandle->construction_chart) { @@ -3835,6 +3842,80 @@ void GEO_uv_parametrizer_delete(ParamHandle *phandle) MEM_freeN(phandle); } +typedef struct GeoUVPinIndex { + struct GeoUVPinIndex *next; + float uv[2]; + ParamKey reindex; +} GeoUVPinIndex; + +/* Find a (mostly) unique ParamKey given a BMVert index and UV co-ordinates. + * For each unique pinned UVs, return a unique ParamKey, starting with + * a very large number, and decreasing steadily from there. + * For non-pinned UVs which share a BMVert with a pinned UV, + * return the index corresponding to the closest pinned UV. + * For everything else, just return the BMVert index. + * Note that ParamKeys will eventually be hashed, so they don't need to be contiguous. + */ +ParamKey GEO_uv_find_pin_index(ParamHandle *handle, const int bmvertindex, const float uv[2]) +{ + if (!handle->pin_hash) { + return bmvertindex; /* No verts pinned. */ + } + + GeoUVPinIndex *pinuvlist = BLI_ghash_lookup(handle->pin_hash, POINTER_FROM_INT(bmvertindex)); + if (!pinuvlist) { + return bmvertindex; /* Vert not pinned. */ + } + + /* At least one of the UVs associated with bmvertindex is pinned. Find the best one. */ + float bestdistsquared = len_squared_v2v2(pinuvlist->uv, uv); + ParamKey bestkey = pinuvlist->reindex; + pinuvlist = pinuvlist->next; + while (pinuvlist) { + const float distsquared = len_squared_v2v2(pinuvlist->uv, uv); + if (bestdistsquared > distsquared) { + bestdistsquared = distsquared; + bestkey = pinuvlist->reindex; + } + pinuvlist = pinuvlist->next; + } + return bestkey; +} + +static GeoUVPinIndex *new_geo_uv_pinindex(ParamHandle *handle, const float uv[2]) +{ + GeoUVPinIndex *pinuv = BLI_memarena_alloc(handle->arena, sizeof(*pinuv)); + pinuv->next = NULL; + copy_v2_v2(pinuv->uv, uv); + pinuv->reindex = PARAM_KEY_MAX - (handle->unique_pin_count++); + return pinuv; +} + +void GEO_uv_prepare_pin_index(ParamHandle *handle, const int bmvertindex, const float uv[2]) +{ + if (!handle->pin_hash) { + handle->pin_hash = BLI_ghash_int_new("uv pin reindex"); + } + + GeoUVPinIndex *pinuvlist = BLI_ghash_lookup(handle->pin_hash, POINTER_FROM_INT(bmvertindex)); + if (!pinuvlist) { + BLI_ghash_insert( + handle->pin_hash, POINTER_FROM_INT(bmvertindex), new_geo_uv_pinindex(handle, uv)); + return; + } + + while (true) { + if (equals_v2v2(pinuvlist->uv, uv)) { + return; + } + if (!pinuvlist->next) { + pinuvlist->next = new_geo_uv_pinindex(handle, uv); + return; + } + pinuvlist = pinuvlist->next; + } +} + static void p_add_ngon(ParamHandle *handle, const ParamKey key, const int nverts, diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c index 6cf7f6f11e5..d59d2cb52fc 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c @@ -7,7 +7,7 @@ #include <stdio.h> -#include "BLI_blenlib.h" +#include "BLI_listbase.h" #include "BLI_math_vector.h" #include "BLI_utildefines.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c index f5ecce69129..092253dde79 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c @@ -8,10 +8,9 @@ #include <stdio.h> #include "BLI_listbase.h" +#include "BLI_math_vector.h" #include "BLI_utildefines.h" -#include "BLI_math.h" - #include "BLT_translation.h" #include "DNA_defaults.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c index 38866bcd32a..407055c6165 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c @@ -9,14 +9,11 @@ #include "MEM_guardedalloc.h" -#include "BLI_utildefines.h" - #include "BLI_hash.h" +#include "BLI_listbase.h" +#include "BLI_math_vector.h" #include "BLI_rand.h" - -#include "BLI_blenlib.h" -#include "BLI_math.h" -#include "BLI_rand.h" +#include "BLI_utildefines.h" #include "BLT_translation.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c index 88515d849bc..cf390e8d3a0 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c @@ -7,14 +7,15 @@ #include <stdio.h> #include <stdlib.h> +#include <string.h> #include "MEM_guardedalloc.h" -#include "BLI_utildefines.h" - -#include "BLI_blenlib.h" -#include "BLI_math.h" +#include "BLI_listbase.h" +#include "BLI_math_base.h" +#include "BLI_math_vector.h" #include "BLI_sort.h" +#include "BLI_utildefines.h" #include "BLT_translation.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c index 792def30812..8ada66cfd55 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c @@ -9,7 +9,6 @@ #include "BLI_utildefines.h" -#include "BLI_blenlib.h" #include "BLI_math_color.h" #include "BLI_math_vector.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c index f023b00480c..41015c429fe 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c @@ -9,7 +9,7 @@ #include <string.h> #include "BLI_listbase.h" -#include "BLI_math.h" +#include "BLI_math_vector.h" #include "BLI_string.h" #include "DNA_defaults.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c index e5604005240..0f1652a5620 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c @@ -8,8 +8,8 @@ #include <stdio.h> #include "BLI_listbase.h" -#include "BLI_math.h" #include "BLI_math_geom.h" +#include "BLI_math_vector.h" #include "BLI_utildefines.h" #include "BLT_translation.h" @@ -22,7 +22,6 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" -#include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_deform.h" #include "BKE_gpencil.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c index bc0464cb4f9..652894af017 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c @@ -6,12 +6,14 @@ */ #include <stdio.h> +#include <string.h> #include "BLI_listbase.h" +#include "BLI_math_base.h" +#include "BLI_math_matrix.h" +#include "BLI_math_vector.h" #include "BLI_utildefines.h" -#include "BLI_math.h" - #include "BLT_translation.h" #include "DNA_defaults.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c index c92c062348c..5a358f5b3a1 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c @@ -6,10 +6,11 @@ */ #include <stdio.h> +#include <string.h> #include "BLI_hash.h" #include "BLI_listbase.h" -#include "BLI_math.h" +#include "BLI_math_base.h" #include "BLI_rand.h" #include "BLI_utildefines.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index a07ef2eb195..f179713e8cb 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -7,9 +7,8 @@ #include <stdio.h> -#include "BLI_utildefines.h" - #include "BLI_math_vector.h" +#include "BLI_utildefines.h" #include "BLT_translation.h" @@ -22,9 +21,6 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" -#include "MOD_gpencil_lineart.h" -#include "lineart/MOD_lineart.h" - #include "BKE_collection.h" #include "BKE_context.h" #include "BKE_global.h" @@ -43,8 +39,10 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" +#include "MOD_gpencil_lineart.h" #include "MOD_gpencil_modifiertypes.h" #include "MOD_gpencil_ui_common.h" +#include "lineart/MOD_lineart.h" #include "WM_api.h" #include "WM_types.h" @@ -354,7 +352,7 @@ static void edge_types_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayout *sub = uiLayoutRowWithHeading(col, false, IFACE_("Crease")); uiItemR(sub, ptr, "use_crease", 0, "", ICON_NONE); uiLayout *entry = uiLayoutRow(sub, false); - uiLayoutSetEnabled(entry, RNA_boolean_get(ptr, "use_crease") || is_first); + uiLayoutSetActive(entry, RNA_boolean_get(ptr, "use_crease") || is_first); if (use_cache && !is_first) { uiItemL(entry, IFACE_("Angle Cached"), ICON_INFO); } @@ -526,6 +524,7 @@ static void intersection_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(layout, ptr, "use_intersection_match", 0, IFACE_("Exact Match"), ICON_NONE); } + static void face_mark_panel_draw_header(const bContext *UNUSED(C), Panel *panel) { uiLayout *layout = panel->layout; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c index 6460a69a6cc..df66251159c 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c @@ -10,7 +10,7 @@ #include "BLI_utildefines.h" #include "BLI_listbase.h" -#include "BLI_math.h" +#include "BLI_math_vector.h" #include "BLT_translation.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c index d80e32feb2e..fd4eb5da835 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c @@ -16,8 +16,8 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" -#include "BLI_blenlib.h" -#include "BLI_math.h" +#include "BLI_listbase.h" +#include "BLI_math_vector.h" #include "BLI_utildefines.h" #include "BKE_context.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c index 259e62a249c..253603aea67 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c @@ -7,11 +7,10 @@ #include <stdio.h> -#include "BLI_listbase.h" -#include "BLI_utildefines.h" - #include "BLI_hash.h" +#include "BLI_listbase.h" #include "BLI_math_vector.h" +#include "BLI_utildefines.h" #include "BLT_translation.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c index f201a147082..ba88c83161f 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c @@ -7,15 +7,14 @@ #include <stdio.h> +#include "BLI_hash.h" #include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_rand.h" #include "BLI_utildefines.h" #include "BLT_translation.h" -#include "BLI_hash.h" -#include "BLI_math.h" -#include "BLI_rand.h" - #include "DNA_defaults.h" #include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c index b964b143a0f..0bcbd71f029 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c @@ -6,12 +6,10 @@ */ #include <stdio.h> +#include <string.h> #include "BLI_utildefines.h" -#include "BLI_blenlib.h" -#include "BLI_math_vector.h" - #include "BLT_translation.h" #include "DNA_defaults.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c index 7de1cc89a45..cd2373dcb26 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c @@ -9,7 +9,7 @@ #include <string.h> /* For #MEMCPY_STRUCT_AFTER. */ #include "BLI_listbase.h" -#include "BLI_math.h" +#include "BLI_math_vector.h" #include "BLI_utildefines.h" #include "BLT_translation.h" @@ -18,7 +18,6 @@ #include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" #include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c index e7dfdfee8d2..07350b73ce3 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c @@ -16,7 +16,6 @@ #include "DNA_gpencil_types.h" #include "DNA_object_types.h" #include "DNA_screen_types.h" -#include "DNA_vec_types.h" #include "BKE_context.h" #include "BKE_gpencil_geom.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c index 5c8c65b9f97..6ba7a934bff 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c @@ -8,7 +8,7 @@ #include <stdio.h> #include "BLI_listbase.h" -#include "BLI_math.h" +#include "BLI_math_vector.h" #include "BLI_utildefines.h" #include "BLT_translation.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c index 3ddb508d58a..68fe15df898 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c @@ -8,7 +8,7 @@ #include <stdio.h> #include "BLI_listbase.h" -#include "BLI_math.h" +#include "BLI_math_vector.h" #include "BLI_utildefines.h" #include "DNA_defaults.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c index 8fa14496616..ad8804782e8 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c @@ -10,7 +10,7 @@ #include "BLI_utildefines.h" #include "BLI_listbase.h" -#include "BLI_math.h" +#include "BLI_math_vector.h" #include "DNA_defaults.h" #include "DNA_gpencil_modifier_types.h" @@ -47,7 +47,6 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" -#include "DEG_depsgraph_query.h" static void initData(GpencilModifierData *md) { diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_angle.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_angle.c index e90408b1a94..f40e6ba0728 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_angle.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_angle.c @@ -8,7 +8,7 @@ #include <stdio.h> #include "BLI_listbase.h" -#include "BLI_math.h" +#include "BLI_math_vector.h" #include "BLI_utildefines.h" #include "DNA_defaults.h" @@ -18,7 +18,6 @@ #include "DNA_object_types.h" #include "DNA_screen_types.h" -#include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_deform.h" #include "BKE_gpencil.h" @@ -29,7 +28,6 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" -#include "DEG_depsgraph_query.h" #include "UI_interface.h" #include "UI_resources.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c index d2c31591a98..1a6cadabf51 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c @@ -8,7 +8,7 @@ #include <stdio.h> #include "BLI_listbase.h" -#include "BLI_math.h" +#include "BLI_math_vector.h" #include "BLI_utildefines.h" #include "DNA_defaults.h" @@ -29,7 +29,6 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" -#include "DEG_depsgraph_query.h" #include "UI_interface.h" #include "UI_resources.h" diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 16b9fcbbdd7..a72ce76d591 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -36,10 +36,10 @@ typedef struct LineartTriangle { /* first culled in line list to use adjacent triangle info, then go through triangle list. */ double gn[3]; - unsigned char material_mask_bits; - unsigned char intersection_mask; - unsigned char mat_occlusion; - unsigned char flags; /* #eLineartTriangleFlags */ + uint8_t material_mask_bits; + uint8_t intersection_mask; + uint8_t mat_occlusion; + uint8_t flags; /* #eLineartTriangleFlags */ /** * Only use single link list, because we don't need to go back in order. @@ -84,10 +84,10 @@ typedef struct LineartEdgeSegment { /** at==0: left at==1: right (this is in 2D projected space) */ double at; /** Occlusion level after "at" point */ - unsigned char occlusion; + uint8_t occlusion; /* Used to filter line art occlusion edges */ - unsigned char material_mask_bits; + uint8_t material_mask_bits; } LineartEdgeSegment; typedef struct LineartVert { @@ -102,7 +102,7 @@ typedef struct LineartVert { * size of the struct is extended to include intersection data. * See #eLineArtVertFlags. */ - char flag; + uint8_t flag; } LineartVert; @@ -119,17 +119,14 @@ typedef enum eLineArtVertFlags { } eLineArtVertFlags; typedef struct LineartEdge { - /** We only need link node kind of list here. */ - struct LineartEdge *next; struct LineartVert *v1, *v2; - struct LineartTriangle *t1, *t2; ListBase segments; - char min_occ; + int8_t min_occ; /** Also for line type determination on chaining. */ uint16_t flags; - unsigned char intersection_mask; + uint8_t intersection_mask; /** * Still need this entry because culled lines will not add to object @@ -149,15 +146,15 @@ typedef struct LineartEdgeChain { float length; /** Used when re-connecting and grease-pencil stroke generation. */ - char picked; - char level; + int8_t picked; + int8_t level; /** Chain now only contains one type of segments */ int type; /** Will only connect chains that has the same loop id. */ int loop_id; - unsigned char material_mask_bits; - unsigned char intersection_mask; + uint8_t material_mask_bits; + uint8_t intersection_mask; struct Object *object_ref; } LineartEdgeChain; @@ -170,9 +167,9 @@ typedef struct LineartEdgeChainItem { float gpos[3]; float normal[3]; uint16_t line_type; - char occlusion; - unsigned char material_mask_bits; - unsigned char intersection_mask; + int8_t occlusion; + uint8_t material_mask_bits; + uint8_t intersection_mask; size_t index; } LineartEdgeChainItem; @@ -180,17 +177,17 @@ typedef struct LineartChainRegisterEntry { struct LineartChainRegisterEntry *next, *prev; LineartEdgeChain *ec; LineartEdgeChainItem *eci; - char picked; + int8_t picked; /* left/right mark. * Because we revert list in chaining so we need the flag. */ - char is_left; + int8_t is_left; } LineartChainRegisterEntry; typedef struct LineartAdjacentEdge { - unsigned int v1; - unsigned int v2; - unsigned int e; + uint32_t v1; + uint32_t v2; + uint32_t e; } LineartAdjacentEdge; enum eLineArtTileRecursiveLimit { @@ -210,52 +207,107 @@ typedef struct LineartPendingEdges { int next; } LineartPendingEdges; -typedef struct LineartRenderBuffer { - struct LineartRenderBuffer *prev, *next; +typedef struct LineartData { + int w, h; int thread_count; + int sizeof_triangle; - int w, h; - int tile_size_w, tile_size_h; - int tile_count_x, tile_count_y; - double width_per_tile, height_per_tile; - double view_projection[4][4]; - double view[4][4]; + LineartStaticMemPool render_data_pool; + /* A pointer to LineartCache::chain_data_pool, which acts as a cache for edge chains. */ + LineartStaticMemPool *chain_data_pool; - float overscan; + struct _qtree { - struct LineartBoundingArea *initial_bounding_areas; - unsigned int bounding_area_count; + int count_x, count_y; + double tile_width, tile_height; - /* When splitting bounding areas, if there's an ortho camera placed at a straight angle, there - * will be a lot of triangles aligned in line which can not be separated by continue subdividing - * the tile. So we set a strict limit when using ortho camera. See eLineArtTileRecursiveLimit. */ - int tile_recursive_level; + /* When splitting bounding areas, if there's an ortho camera placed at a straight angle, there + * will be a lot of triangles aligned in line which can not be separated by continue + * subdividing the tile. So we set a strict limit when using ortho camera. See + * eLineArtTileRecursiveLimit. */ + int recursive_level; - ListBase vertex_buffer_pointers; - ListBase line_buffer_pointers; - ListBase triangle_buffer_pointers; + struct LineartBoundingArea *initials; - LineartElementLinkNode *isect_scheduled_up_to; - int isect_scheduled_up_to_index; + uint32_t tile_count; - /** This one's memory is not from main pool and is free()ed after culling stage. */ - ListBase triangle_adjacent_pointers; + } qtree; - ListBase intersecting_vertex_buffer; - /** Use the one comes with Line Art. */ - LineartStaticMemPool render_data_pool; - ListBase wasted_cuts; - SpinLock lock_cuts; + struct _geom { - /* This is just a pointer to LineartCache::chain_data_pool, which acts as a cache for line - * chains. */ - LineartStaticMemPool *chain_data_pool; + ListBase vertex_buffer_pointers; + ListBase line_buffer_pointers; + ListBase triangle_buffer_pointers; + + /** This one's memory is not from main pool and is free()ed after culling stage. */ + ListBase triangle_adjacent_pointers; + + ListBase intersecting_vertex_buffer; + + } geom; + + struct _conf { + + double view_projection[4][4]; + double view[4][4]; + + float overscan; + + int max_occlusion_level; + double crease_angle; + double crease_cos; - /* Render status */ - double view_vector[3]; + int draw_material_preview; + double material_transparency; - int triangle_size; + bool use_contour; + bool use_crease; + bool use_material; + bool use_edge_marks; + bool use_intersections; + bool use_loose; + bool fuzzy_intersections; + bool fuzzy_everything; + bool allow_boundaries; + bool allow_overlapping_edges; + bool allow_duplicated_types; + bool remove_doubles; + bool use_loose_as_contour; + bool use_loose_edge_chain; + bool use_geometry_space_chain; + bool use_image_boundary_trimming; + bool use_back_face_culling; + + bool filter_face_mark; + bool filter_face_mark_invert; + bool filter_face_mark_boundaries; + bool filter_face_mark_keep_contour; + + bool force_crease; + bool sharp_as_crease; + + bool chain_preserve_details; + + /* Keep an copy of these data so when line art is running it's self-contained. */ + bool cam_is_persp; + float cam_obmat[4][4]; + double camera_pos[3]; + double active_camera_pos[3]; /* Stroke offset calculation may use active or selected camera. */ + double near_clip, far_clip; + float shift_x, shift_y; + float crease_threshold; + float chaining_image_threshold; + float angle_splitting_threshold; + + float chain_smooth_tolerance; + + double view_vector[3]; + + } conf; + + LineartElementLinkNode *isect_scheduled_up_to; + int isect_scheduled_up_to_index; /* Note: Data inside #pending_edges are allocated with MEM_xxx call instead of in pool. */ struct LineartPendingEdges pending_edges; @@ -263,79 +315,22 @@ typedef struct LineartRenderBuffer { ListBase chains; - /* For managing calculation tasks for multiple threads. */ + ListBase wasted_cuts; + SpinLock lock_cuts; SpinLock lock_task; - /* settings */ - - int max_occlusion_level; - double crease_angle; - double crease_cos; - - int draw_material_preview; - double material_transparency; - - bool use_contour; - bool use_crease; - bool use_material; - bool use_edge_marks; - bool use_intersections; - bool use_loose; - bool fuzzy_intersections; - bool fuzzy_everything; - bool allow_boundaries; - bool allow_overlapping_edges; - bool allow_duplicated_types; - bool remove_doubles; - bool use_loose_as_contour; - bool use_loose_edge_chain; - bool use_geometry_space_chain; - bool use_image_boundary_trimming; - bool use_back_face_culling; - - bool filter_face_mark; - bool filter_face_mark_invert; - bool filter_face_mark_boundaries; - bool filter_face_mark_keep_contour; - - bool force_crease; - bool sharp_as_crease; - - bool chain_preserve_details; - - /* Keep an copy of these data so when line art is running it's self-contained. */ - bool cam_is_persp; - float cam_obmat[4][4]; - double camera_pos[3]; - double active_camera_pos[3]; /* Stroke offset calculation may use active or selected camera. */ - double near_clip, far_clip; - float shift_x, shift_y; - float crease_threshold; - float chaining_image_threshold; - float angle_splitting_threshold; - - float chain_smooth_tolerance; - - /* FIXME(Yiming): Temporary solution for speeding up calculation by not including lines that - * are not in the selected source. This will not be needed after we have a proper scene-wise - * cache running because multiple modifiers can then select results from that without further - * calculation. */ - int _source_type; - struct Collection *_source_collection; - struct Object *_source_object; - -} LineartRenderBuffer; +} LineartData; typedef struct LineartCache { /** Separate memory pool for chain data, this goes to the cache, so when we free the main pool, * chains will still be available. */ LineartStaticMemPool chain_data_pool; - /** A copy of rb->chains so we have that data available after rb has been destroyed. */ + /** A copy of ld->chains so we have that data available after ld has been destroyed. */ ListBase chains; /** Cache only contains edge types specified in this variable. */ - char rb_edge_types; + int8_t rb_edge_types; } LineartCache; #define DBL_TRIANGLE_LIM 1e-8 @@ -361,13 +356,13 @@ typedef enum eLineartTriangleFlags { #define LRT_THREAD_EDGE_COUNT 1000 typedef struct LineartRenderTaskInfo { - struct LineartRenderBuffer *rb; + struct LineartData *ld; int thread_id; /** * #pending_edges here only stores a reference to a portion in - * LineartRenderbuffer::pending_edges, assigned by the occlusion scheduler. + * LineartData::pending_edges, assigned by the occlusion scheduler. */ struct LineartPendingEdges pending_edges; @@ -393,12 +388,12 @@ typedef struct LineartObjectInfo { } LineartObjectInfo; typedef struct LineartObjectLoadTaskInfo { - struct LineartRenderBuffer *rb; + struct LineartData *ld; int thread_id; /* LinkNode styled list */ LineartObjectInfo *pending; /* Used to spread the load across several threads. This can not overflow. */ - long unsigned int total_faces; + uint64_t total_faces; } LineartObjectLoadTaskInfo; /** @@ -465,9 +460,6 @@ typedef struct LineartBoundingArea { #define LRT_MIN3_INDEX_ABC(x, y, z) (x < y ? (x < z ? a : (y < z ? b : c)) : (y < z ? b : c)) -#define LRT_ABC(index) (index == 0 ? a : (index == 1 ? b : c)) -#define LRT_PABC(index) (index == 0 ? pa : (index == 1 ? pb : pc)) - #define DBL_LOOSER 1e-5 #define LRT_DOUBLE_CLOSE_LOOSER(a, b) (((a) + DBL_LOOSER) >= (b) && ((a)-DBL_LOOSER) <= (b)) #define LRT_DOUBLE_CLOSE_ENOUGH(a, b) (((a) + DBL_EDGE_LIM) >= (b) && ((a)-DBL_EDGE_LIM) <= (b)) @@ -632,30 +624,28 @@ BLI_INLINE int lineart_intersect_seg_seg(const double *a1, struct Depsgraph; struct LineartGpencilModifierData; -struct LineartRenderBuffer; +struct LineartData; struct Scene; void MOD_lineart_destroy_render_data(struct LineartGpencilModifierData *lmd); -void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb); -void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb); +void MOD_lineart_chain_feature_lines(LineartData *ld); +void MOD_lineart_chain_split_for_fixed_occlusion(LineartData *ld); /** * This function only connects two different chains. It will not do any clean up or smart chaining. * So no: removing overlapping chains, removal of short isolated segments, and no loop reduction is * implemented yet. */ -void MOD_lineart_chain_connect(LineartRenderBuffer *rb); -void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, float threshold); -void MOD_lineart_chain_clip_at_border(LineartRenderBuffer *rb); +void MOD_lineart_chain_connect(LineartData *ld); +void MOD_lineart_chain_discard_short(LineartData *ld, float threshold); +void MOD_lineart_chain_clip_at_border(LineartData *ld); /** * This should always be the last stage!, see the end of * #MOD_lineart_chain_split_for_fixed_occlusion(). */ -void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad); -void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance); -void MOD_lineart_chain_offset_towards_camera(LineartRenderBuffer *rb, - float dist, - bool use_custom_camera); +void MOD_lineart_chain_split_angle(LineartData *ld, float angle_threshold_rad); +void MOD_lineart_smooth_chains(LineartData *ld, float tolerance); +void MOD_lineart_chain_offset_towards_camera(LineartData *ld, float dist, bool use_custom_camera); int MOD_lineart_chain_count(const LineartEdgeChain *ec); void MOD_lineart_chain_clear_picked_flag(LineartCache *lc); @@ -675,14 +665,12 @@ struct Scene; /** * This only gets initial "biggest" tile. */ -LineartBoundingArea *MOD_lineart_get_parent_bounding_area(LineartRenderBuffer *rb, - double x, - double y); +LineartBoundingArea *MOD_lineart_get_parent_bounding_area(LineartData *ld, double x, double y); /** * Wrapper for more convenience. */ -LineartBoundingArea *MOD_lineart_get_bounding_area(LineartRenderBuffer *rb, double x, double y); +LineartBoundingArea *MOD_lineart_get_bounding_area(LineartData *ld, double x, double y); struct bGPDframe; struct bGPDlayer; @@ -695,16 +683,16 @@ void MOD_lineart_gpencil_generate(LineartCache *cache, struct Object *ob, struct bGPDlayer *gpl, struct bGPDframe *gpf, - char source_type, + int8_t source_type, void *source_reference, int level_start, int level_end, int mat_nr, - short edge_types, - unsigned char mask_switches, - unsigned char material_mask_bits, - unsigned char intersection_mask, - short thickness, + int16_t edge_types, + uint8_t mask_switches, + uint8_t material_mask_bits, + uint8_t intersection_mask, + int16_t thickness, float opacity, const char *source_vgname, const char *vgname, diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c index 226b8f532b5..1d84bb8e232 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c @@ -23,7 +23,7 @@ static LineartEdge *lineart_line_get_connected(LineartBoundingArea *ba, LineartVert *vt, LineartVert **new_vt, int match_flag, - unsigned char match_isec_mask) + uint8_t match_isec_mask) { for (int i = 0; i < ba->line_count; i++) { LineartEdge *n_e = ba->linked_lines[i]; @@ -60,12 +60,12 @@ static LineartEdge *lineart_line_get_connected(LineartBoundingArea *ba, return NULL; } -static LineartEdgeChain *lineart_chain_create(LineartRenderBuffer *rb) +static LineartEdgeChain *lineart_chain_create(LineartData *ld) { LineartEdgeChain *ec; - ec = lineart_mem_acquire(rb->chain_data_pool, sizeof(LineartEdgeChain)); + ec = lineart_mem_acquire(ld->chain_data_pool, sizeof(LineartEdgeChain)); - BLI_addtail(&rb->chains, ec); + BLI_addtail(&ld->chains, ec); return ec; } @@ -85,14 +85,14 @@ static bool lineart_point_overlapping(LineartEdgeChainItem *eci, return false; } -static LineartEdgeChainItem *lineart_chain_append_point(LineartRenderBuffer *rb, +static LineartEdgeChainItem *lineart_chain_append_point(LineartData *ld, LineartEdgeChain *ec, float *fbcoord, float *gpos, float *normal, - char type, + uint8_t type, int level, - unsigned char material_mask_bits, + uint8_t material_mask_bits, size_t index) { LineartEdgeChainItem *eci; @@ -108,7 +108,7 @@ static LineartEdgeChainItem *lineart_chain_append_point(LineartRenderBuffer *rb, return old_eci; } - eci = lineart_mem_acquire(rb->chain_data_pool, sizeof(LineartEdgeChainItem)); + eci = lineart_mem_acquire(ld->chain_data_pool, sizeof(LineartEdgeChainItem)); copy_v4_v4(eci->pos, fbcoord); copy_v3_v3(eci->gpos, gpos); @@ -122,14 +122,14 @@ static LineartEdgeChainItem *lineart_chain_append_point(LineartRenderBuffer *rb, return eci; } -static LineartEdgeChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb, +static LineartEdgeChainItem *lineart_chain_prepend_point(LineartData *ld, LineartEdgeChain *ec, float *fbcoord, float *gpos, float *normal, - char type, + uint8_t type, int level, - unsigned char material_mask_bits, + uint8_t material_mask_bits, size_t index) { LineartEdgeChainItem *eci; @@ -138,7 +138,7 @@ static LineartEdgeChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb return ec->chain.first; } - eci = lineart_mem_acquire(rb->chain_data_pool, sizeof(LineartEdgeChainItem)); + eci = lineart_mem_acquire(ld->chain_data_pool, sizeof(LineartEdgeChainItem)); copy_v4_v4(eci->pos, fbcoord); copy_v3_v3(eci->gpos, gpos); @@ -152,14 +152,14 @@ static LineartEdgeChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb return eci; } -void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) +void MOD_lineart_chain_feature_lines(LineartData *ld) { LineartEdgeChain *ec; LineartEdgeChainItem *eci; LineartBoundingArea *ba; LineartEdgeSegment *es; int last_occlusion; - unsigned char last_transparency; + uint8_t last_transparency; /* Used when converting from double. */ float use_fbcoord[4]; float use_gpos[3]; @@ -181,7 +181,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED; - ec = lineart_chain_create(rb); + ec = lineart_chain_create(ld); /* One chain can only have one object_ref and intersection_mask, * so we assign them based on the first segment we found. */ @@ -207,11 +207,11 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) } /* Step 1: grow left. */ - ba = MOD_lineart_get_bounding_area(rb, e->v1->fbcoord[0], e->v1->fbcoord[1]); + ba = MOD_lineart_get_bounding_area(ld, e->v1->fbcoord[0], e->v1->fbcoord[1]); new_vt = e->v1; es = e->segments.first; VERT_COORD_TO_FLOAT(new_vt); - lineart_chain_prepend_point(rb, + lineart_chain_prepend_point(ld, ec, use_fbcoord, use_gpos, @@ -248,7 +248,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at); POS_TO_FLOAT(lpos, gpos) - lineart_chain_prepend_point(rb, + lineart_chain_prepend_point(ld, ec, use_fbcoord, use_gpos, @@ -274,7 +274,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at); POS_TO_FLOAT(lpos, gpos) - lineart_chain_prepend_point(rb, + lineart_chain_prepend_point(ld, ec, use_fbcoord, use_gpos, @@ -287,7 +287,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) last_transparency = es->material_mask_bits; } VERT_COORD_TO_FLOAT(new_e->v2); - lineart_chain_prepend_point(rb, + lineart_chain_prepend_point(ld, ec, use_fbcoord, use_gpos, @@ -297,7 +297,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) last_transparency, new_e->v2->index); } - ba = MOD_lineart_get_bounding_area(rb, new_vt->fbcoord[0], new_vt->fbcoord[1]); + ba = MOD_lineart_get_bounding_area(ld, new_vt->fbcoord[0], new_vt->fbcoord[1]); } /* Restore normal value. */ @@ -328,7 +328,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) interp_v3_v3v3_db(gpos, e->v1->gloc, e->v2->gloc, global_at); use_fbcoord[3] = interpf(e->v2->fbcoord[3], e->v1->fbcoord[3], global_at); POS_TO_FLOAT(lpos, gpos) - lineart_chain_append_point(rb, + lineart_chain_append_point(ld, ec, use_fbcoord, use_gpos, @@ -341,7 +341,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) last_transparency = es->material_mask_bits; } VERT_COORD_TO_FLOAT(e->v2) - lineart_chain_append_point(rb, + lineart_chain_append_point(ld, ec, use_fbcoord, use_gpos, @@ -352,7 +352,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) e->v2->index); /* Step 3: grow right. */ - ba = MOD_lineart_get_bounding_area(rb, e->v2->fbcoord[0], e->v2->fbcoord[1]); + ba = MOD_lineart_get_bounding_area(ld, e->v2->fbcoord[0], e->v2->fbcoord[1]); new_vt = e->v2; while (ba && (new_e = lineart_line_get_connected( ba, new_vt, &new_vt, e->flags, e->intersection_mask))) { @@ -394,7 +394,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) last_occlusion = es->prev ? es->prev->occlusion : last_occlusion; last_transparency = es->prev ? es->prev->material_mask_bits : last_transparency; POS_TO_FLOAT(lpos, gpos) - lineart_chain_append_point(rb, + lineart_chain_append_point(ld, ec, use_fbcoord, use_gpos, @@ -420,7 +420,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at); POS_TO_FLOAT(lpos, gpos) - lineart_chain_append_point(rb, + lineart_chain_append_point(ld, ec, use_fbcoord, use_gpos, @@ -433,7 +433,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) last_transparency = es->material_mask_bits; } VERT_COORD_TO_FLOAT(new_e->v2) - lineart_chain_append_point(rb, + lineart_chain_append_point(ld, ec, use_fbcoord, use_gpos, @@ -443,9 +443,9 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) last_transparency, new_e->v2->index); } - ba = MOD_lineart_get_bounding_area(rb, new_vt->fbcoord[0], new_vt->fbcoord[1]); + ba = MOD_lineart_get_bounding_area(ld, new_vt->fbcoord[0], new_vt->fbcoord[1]); } - if (rb->fuzzy_everything) { + if (ld->conf.fuzzy_everything) { ec->type = LRT_EDGE_FLAG_CONTOUR; } else { @@ -455,7 +455,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) LRT_ITER_ALL_LINES_END } -static LineartBoundingArea *lineart_bounding_area_get_eci_recursive(LineartRenderBuffer *rb, +static LineartBoundingArea *lineart_bounding_area_get_eci_recursive(LineartData *ld, LineartBoundingArea *root, LineartEdgeChainItem *eci) { @@ -468,32 +468,32 @@ static LineartBoundingArea *lineart_bounding_area_get_eci_recursive(LineartRende ba.l <= eci->pos[0] && ba.r >= eci->pos[0] && ba.b <= eci->pos[1] && ba.u >= eci->pos[1] if (IN_BOUND(ch[0], eci)) { - return lineart_bounding_area_get_eci_recursive(rb, &ch[0], eci); + return lineart_bounding_area_get_eci_recursive(ld, &ch[0], eci); } if (IN_BOUND(ch[1], eci)) { - return lineart_bounding_area_get_eci_recursive(rb, &ch[1], eci); + return lineart_bounding_area_get_eci_recursive(ld, &ch[1], eci); } if (IN_BOUND(ch[2], eci)) { - return lineart_bounding_area_get_eci_recursive(rb, &ch[2], eci); + return lineart_bounding_area_get_eci_recursive(ld, &ch[2], eci); } if (IN_BOUND(ch[3], eci)) { - return lineart_bounding_area_get_eci_recursive(rb, &ch[3], eci); + return lineart_bounding_area_get_eci_recursive(ld, &ch[3], eci); } #undef IN_BOUND return NULL; } -static LineartBoundingArea *lineart_bounding_area_get_end_point(LineartRenderBuffer *rb, +static LineartBoundingArea *lineart_bounding_area_get_end_point(LineartData *ld, LineartEdgeChainItem *eci) { if (!eci) { return NULL; } - LineartBoundingArea *root = MOD_lineart_get_parent_bounding_area(rb, eci->pos[0], eci->pos[1]); + LineartBoundingArea *root = MOD_lineart_get_parent_bounding_area(ld, eci->pos[0], eci->pos[1]); if (root == NULL) { return NULL; } - return lineart_bounding_area_get_eci_recursive(rb, root, eci); + return lineart_bounding_area_get_eci_recursive(ld, root, eci); } /** @@ -502,14 +502,14 @@ static LineartBoundingArea *lineart_bounding_area_get_end_point(LineartRenderBuf * areas, this happens either when 1) the geometry is way too dense, or 2) the chaining threshold * is too big that it covers multiple small bounding areas. */ -static void lineart_bounding_area_link_point_recursive(LineartRenderBuffer *rb, +static void lineart_bounding_area_link_point_recursive(LineartData *ld, LineartBoundingArea *root, LineartEdgeChain *ec, LineartEdgeChainItem *eci) { if (root->child == NULL) { LineartChainRegisterEntry *cre = lineart_list_append_pointer_pool_sized( - &root->linked_chains, &rb->render_data_pool, ec, sizeof(LineartChainRegisterEntry)); + &root->linked_chains, &ld->render_data_pool, ec, sizeof(LineartChainRegisterEntry)); cre->eci = eci; @@ -524,34 +524,34 @@ static void lineart_bounding_area_link_point_recursive(LineartRenderBuffer *rb, ba.l <= eci->pos[0] && ba.r >= eci->pos[0] && ba.b <= eci->pos[1] && ba.u >= eci->pos[1] if (IN_BOUND(ch[0], eci)) { - lineart_bounding_area_link_point_recursive(rb, &ch[0], ec, eci); + lineart_bounding_area_link_point_recursive(ld, &ch[0], ec, eci); } else if (IN_BOUND(ch[1], eci)) { - lineart_bounding_area_link_point_recursive(rb, &ch[1], ec, eci); + lineart_bounding_area_link_point_recursive(ld, &ch[1], ec, eci); } else if (IN_BOUND(ch[2], eci)) { - lineart_bounding_area_link_point_recursive(rb, &ch[2], ec, eci); + lineart_bounding_area_link_point_recursive(ld, &ch[2], ec, eci); } else if (IN_BOUND(ch[3], eci)) { - lineart_bounding_area_link_point_recursive(rb, &ch[3], ec, eci); + lineart_bounding_area_link_point_recursive(ld, &ch[3], ec, eci); } #undef IN_BOUND } } -static void lineart_bounding_area_link_chain(LineartRenderBuffer *rb, LineartEdgeChain *ec) +static void lineart_bounding_area_link_chain(LineartData *ld, LineartEdgeChain *ec) { LineartEdgeChainItem *pl = ec->chain.first; LineartEdgeChainItem *pr = ec->chain.last; - LineartBoundingArea *ba1 = MOD_lineart_get_parent_bounding_area(rb, pl->pos[0], pl->pos[1]); - LineartBoundingArea *ba2 = MOD_lineart_get_parent_bounding_area(rb, pr->pos[0], pr->pos[1]); + LineartBoundingArea *ba1 = MOD_lineart_get_parent_bounding_area(ld, pl->pos[0], pl->pos[1]); + LineartBoundingArea *ba2 = MOD_lineart_get_parent_bounding_area(ld, pr->pos[0], pr->pos[1]); if (ba1) { - lineart_bounding_area_link_point_recursive(rb, ba1, ec, pl); + lineart_bounding_area_link_point_recursive(ld, ba1, ec, pl); } if (ba2) { - lineart_bounding_area_link_point_recursive(rb, ba2, ec, pr); + lineart_bounding_area_link_point_recursive(ld, ba2, ec, pr); } } @@ -564,7 +564,7 @@ static bool lineart_chain_fix_ambiguous_segments(LineartEdgeChain *ec, float dist_accum = 0; int fixed_occ = last_matching_eci->occlusion; - unsigned char fixed_mask = last_matching_eci->material_mask_bits; + uint8_t fixed_mask = last_matching_eci->material_mask_bits; LineartEdgeChainItem *can_skip_to = NULL; LineartEdgeChainItem *last_eci = last_matching_eci; @@ -606,28 +606,28 @@ static bool lineart_chain_fix_ambiguous_segments(LineartEdgeChain *ec, return false; } -void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb) +void MOD_lineart_chain_split_for_fixed_occlusion(LineartData *ld) { LineartEdgeChain *ec, *new_ec; LineartEdgeChainItem *eci, *next_eci; ListBase swap = {0}; - swap.first = rb->chains.first; - swap.last = rb->chains.last; + swap.first = ld->chains.first; + swap.last = ld->chains.last; - rb->chains.last = rb->chains.first = NULL; + ld->chains.last = ld->chains.first = NULL; int loop_id = 0; while ((ec = BLI_pophead(&swap)) != NULL) { ec->next = ec->prev = NULL; - BLI_addtail(&rb->chains, ec); + BLI_addtail(&ld->chains, ec); ec->loop_id = loop_id; loop_id++; LineartEdgeChainItem *first_eci = (LineartEdgeChainItem *)ec->chain.first; int fixed_occ = first_eci->occlusion; - unsigned char fixed_mask = first_eci->material_mask_bits; + uint8_t fixed_mask = first_eci->material_mask_bits; ec->level = fixed_occ; ec->material_mask_bits = fixed_mask; for (eci = first_eci->next; eci; eci = next_eci) { @@ -639,8 +639,8 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb) } if (lineart_chain_fix_ambiguous_segments(ec, eci->prev, - rb->chaining_image_threshold, - rb->chain_preserve_details, + ld->conf.chaining_image_threshold, + ld->conf.chain_preserve_details, &next_eci)) { continue; } @@ -653,7 +653,7 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb) /* No need to split at the last point anyway. */ break; } - new_ec = lineart_chain_create(rb); + new_ec = lineart_chain_create(ld); new_ec->chain.first = eci; new_ec->chain.last = ec->chain.last; new_ec->loop_id = loop_id; @@ -662,7 +662,7 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb) eci->prev = 0; /* End the previous one. */ - lineart_chain_append_point(rb, + lineart_chain_append_point(ld, ec, eci->pos, eci->gpos, @@ -683,16 +683,16 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb) } } /* Get rid of those very short "zig-zag" lines that jumps around visibility. */ - MOD_lineart_chain_discard_short(rb, DBL_EDGE_LIM); - LISTBASE_FOREACH (LineartEdgeChain *, iec, &rb->chains) { - lineart_bounding_area_link_chain(rb, iec); + MOD_lineart_chain_discard_short(ld, DBL_EDGE_LIM); + LISTBASE_FOREACH (LineartEdgeChain *, iec, &ld->chains) { + lineart_bounding_area_link_chain(ld, iec); } } /** * NOTE: segment type (crease/material/contour...) is ambiguous after this. */ -static void lineart_chain_connect(LineartRenderBuffer *UNUSED(rb), +static void lineart_chain_connect(LineartData *UNUSED(ld), LineartEdgeChain *onto, LineartEdgeChain *sub, int reverse_1, @@ -742,13 +742,13 @@ static void lineart_chain_connect(LineartRenderBuffer *UNUSED(rb), } } -static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuffer *rb, +static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartData *ld, LineartBoundingArea *ba, LineartEdgeChain *ec, LineartEdgeChainItem *eci, int occlusion, - unsigned char material_mask_bits, - unsigned char isec_mask, + uint8_t material_mask_bits, + uint8_t isec_mask, int loop_id, float dist, float *result_new_len, @@ -761,8 +761,8 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf * next one. */ LISTBASE_FOREACH_MUTABLE (LineartChainRegisterEntry *, cre, &ba->linked_chains) { if (cre->ec->object_ref != ec->object_ref) { - if (!rb->fuzzy_everything) { - if (rb->fuzzy_intersections) { + if (!ld->conf.fuzzy_everything) { + if (ld->conf.fuzzy_intersections) { /* If none of those are intersection lines... */ if ((!(cre->ec->type & LRT_EDGE_FLAG_INTERSECTION)) && (!(ec->type & LRT_EDGE_FLAG_INTERSECTION))) { @@ -782,9 +782,9 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf (cre->ec->intersection_mask != isec_mask)) { continue; } - if (!rb->fuzzy_everything) { + if (!ld->conf.fuzzy_everything) { if (cre->ec->type != ec->type) { - if (rb->fuzzy_intersections) { + if (ld->conf.fuzzy_intersections) { if (!(cre->ec->type == LRT_EDGE_FLAG_INTERSECTION || ec->type == LRT_EDGE_FLAG_INTERSECTION)) { continue; /* Fuzzy intersections but no intersection line found. */ @@ -796,8 +796,8 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf } } - float new_len = rb->use_geometry_space_chain ? len_v3v3(cre->eci->gpos, eci->gpos) : - len_v2v2(cre->eci->pos, eci->pos); + float new_len = ld->conf.use_geometry_space_chain ? len_v3v3(cre->eci->gpos, eci->gpos) : + len_v2v2(cre->eci->pos, eci->pos); /* Even if the vertex is not from the same contour loop, we try to chain it still if the * distance is small enough. This way we can better chain smaller loops and smooth them out * later. */ @@ -817,9 +817,9 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf #define LRT_TEST_ADJACENT_AREAS(dist_to, list) \ if (dist_to < dist && dist_to > 0) { \ - LISTBASE_FOREACH (LinkData *, ld, list) { \ - LineartBoundingArea *sba = (LineartBoundingArea *)ld->data; \ - adjacent_closest = lineart_chain_get_closest_cre(rb, \ + LISTBASE_FOREACH (LinkData *, link, list) { \ + LineartBoundingArea *sba = (LineartBoundingArea *)link->data; \ + adjacent_closest = lineart_chain_get_closest_cre(ld, \ sba, \ ec, \ eci, \ @@ -848,36 +848,36 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf return closest_cre; } -void MOD_lineart_chain_connect(LineartRenderBuffer *rb) +void MOD_lineart_chain_connect(LineartData *ld) { LineartEdgeChain *ec; LineartEdgeChainItem *eci_l, *eci_r; LineartBoundingArea *ba_l, *ba_r; LineartChainRegisterEntry *closest_cre_l, *closest_cre_r, *closest_cre; - float dist = rb->chaining_image_threshold; + float dist = ld->conf.chaining_image_threshold; float dist_l, dist_r; int occlusion, reverse_main, loop_id; - unsigned char material_mask_bits, isec_mask; + uint8_t material_mask_bits, isec_mask; ListBase swap = {0}; - if (rb->chaining_image_threshold < 0.0001) { + if (ld->conf.chaining_image_threshold < 0.0001) { return; } - swap.first = rb->chains.first; - swap.last = rb->chains.last; + swap.first = ld->chains.first; + swap.last = ld->chains.last; - rb->chains.last = rb->chains.first = NULL; + ld->chains.last = ld->chains.first = NULL; while ((ec = BLI_pophead(&swap)) != NULL) { ec->next = ec->prev = NULL; if (ec->picked) { continue; } - BLI_addtail(&rb->chains, ec); + BLI_addtail(&ld->chains, ec); loop_id = ec->loop_id; - if (ec->type == LRT_EDGE_FLAG_LOOSE && (!rb->use_loose_edge_chain)) { + if (ec->type == LRT_EDGE_FLAG_LOOSE && (!ld->conf.use_loose_edge_chain)) { continue; } @@ -887,9 +887,9 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb) eci_l = ec->chain.first; eci_r = ec->chain.last; - while ((ba_l = lineart_bounding_area_get_end_point(rb, eci_l)) && - (ba_r = lineart_bounding_area_get_end_point(rb, eci_r))) { - closest_cre_l = lineart_chain_get_closest_cre(rb, + while ((ba_l = lineart_bounding_area_get_end_point(ld, eci_l)) && + (ba_r = lineart_bounding_area_get_end_point(ld, eci_r))) { + closest_cre_l = lineart_chain_get_closest_cre(ld, ba_l, ec, eci_l, @@ -900,7 +900,7 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb) dist, &dist_l, NULL); - closest_cre_r = lineart_chain_get_closest_cre(rb, + closest_cre_r = lineart_chain_get_closest_cre(ld, ba_r, ec, eci_r, @@ -936,10 +936,10 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb) closest_cre->picked = 1; closest_cre->ec->picked = 1; if (closest_cre->is_left) { - lineart_chain_connect(rb, ec, closest_cre->ec, reverse_main, 0); + lineart_chain_connect(ld, ec, closest_cre->ec, reverse_main, 0); } else { - lineart_chain_connect(rb, ec, closest_cre->ec, reverse_main, 1); + lineart_chain_connect(ld, ec, closest_cre->ec, reverse_main, 1); } BLI_remlink(&swap, closest_cre->ec); eci_l = ec->chain.first; @@ -969,13 +969,13 @@ float MOD_lineart_chain_compute_length(LineartEdgeChain *ec) return offset_accum; } -void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold) +void MOD_lineart_chain_discard_short(LineartData *ld, const float threshold) { LineartEdgeChain *ec, *next_ec; - for (ec = rb->chains.first; ec; ec = next_ec) { + for (ec = ld->chains.first; ec; ec = next_ec) { next_ec = ec->next; if (MOD_lineart_chain_compute_length(ec) < threshold) { - BLI_remlink(&rb->chains, ec); + BLI_remlink(&ld->chains, ec); } } } @@ -999,9 +999,9 @@ void MOD_lineart_chain_clear_picked_flag(LineartCache *lc) } } -void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance) +void MOD_lineart_smooth_chains(LineartData *ld, float tolerance) { - LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { + LISTBASE_FOREACH (LineartEdgeChain *, ec, &ld->chains) { /* Go through the chain two times, once from each direction. */ for (int times = 0; times < 2; times++) { for (LineartEdgeChainItem *eci = ec->chain.first, *next_eci = eci->next; eci; @@ -1067,7 +1067,7 @@ void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance) } } -static LineartEdgeChainItem *lineart_chain_create_crossing_point(LineartRenderBuffer *rb, +static LineartEdgeChainItem *lineart_chain_create_crossing_point(LineartData *ld, LineartEdgeChainItem *eci_inside, LineartEdgeChainItem *eci_outside) { @@ -1097,7 +1097,7 @@ static LineartEdgeChainItem *lineart_chain_create_crossing_point(LineartRenderBu ratiof(eci1->pos[1], eci2->pos[1], isec[1]); float gratio = eci1->pos[3] * ratio / (ratio * eci1->pos[3] + (1 - ratio) * eci2->pos[3]); - LineartEdgeChainItem *eci = lineart_mem_acquire(rb->chain_data_pool, + LineartEdgeChainItem *eci = lineart_mem_acquire(ld->chain_data_pool, sizeof(LineartEdgeChainItem)); memcpy(eci, eci1, sizeof(LineartEdgeChainItem)); interp_v3_v3v3(eci->gpos, eci1->gpos, eci2->gpos, gratio); @@ -1111,16 +1111,16 @@ static LineartEdgeChainItem *lineart_chain_create_crossing_point(LineartRenderBu ((eci)->pos[0] >= -1.0f && (eci)->pos[0] <= 1.0f && (eci)->pos[1] >= -1.0f && \ (eci)->pos[1] <= 1.0f) -void MOD_lineart_chain_clip_at_border(LineartRenderBuffer *rb) +void MOD_lineart_chain_clip_at_border(LineartData *ld) { LineartEdgeChain *ec; LineartEdgeChainItem *eci, *next_eci, *prev_eci, *new_eci; bool is_inside, new_inside; ListBase swap = {0}; - swap.first = rb->chains.first; - swap.last = rb->chains.last; + swap.first = ld->chains.first; + swap.last = ld->chains.last; - rb->chains.last = rb->chains.first = NULL; + ld->chains.last = ld->chains.first = NULL; while ((ec = BLI_pophead(&swap)) != NULL) { bool ec_added = false; LineartEdgeChainItem *first_eci = (LineartEdgeChainItem *)ec->chain.first; @@ -1137,9 +1137,9 @@ void MOD_lineart_chain_clip_at_border(LineartRenderBuffer *rb) if ((new_inside = LRT_ECI_INSIDE(eci)) != is_inside) { if (new_inside == false) { /* Stroke goes out. */ - new_eci = lineart_chain_create_crossing_point(rb, prev_eci, eci); + new_eci = lineart_chain_create_crossing_point(ld, prev_eci, eci); - LineartEdgeChain *new_ec = lineart_mem_acquire(rb->chain_data_pool, + LineartEdgeChain *new_ec = lineart_mem_acquire(ld->chain_data_pool, sizeof(LineartEdgeChain)); memcpy(new_ec, ec, sizeof(LineartEdgeChain)); new_ec->chain.first = next_eci; @@ -1147,7 +1147,7 @@ void MOD_lineart_chain_clip_at_border(LineartRenderBuffer *rb) prev_eci->next = NULL; ec->chain.last = prev_eci; BLI_addtail(&ec->chain, new_eci); - BLI_addtail(&rb->chains, ec); + BLI_addtail(&ld->chains, ec); ec_added = true; ec = new_ec; @@ -1156,7 +1156,7 @@ void MOD_lineart_chain_clip_at_border(LineartRenderBuffer *rb) continue; } /* Stroke comes in. */ - new_eci = lineart_chain_create_crossing_point(rb, eci, prev_eci); + new_eci = lineart_chain_create_crossing_point(ld, eci, prev_eci); ec->chain.first = eci; eci->prev = NULL; @@ -1172,25 +1172,25 @@ void MOD_lineart_chain_clip_at_border(LineartRenderBuffer *rb) } if ((!ec_added) && is_inside) { - BLI_addtail(&rb->chains, ec); + BLI_addtail(&ld->chains, ec); } } } -void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad) +void MOD_lineart_chain_split_angle(LineartData *ld, float angle_threshold_rad) { LineartEdgeChain *ec, *new_ec; LineartEdgeChainItem *eci, *next_eci, *prev_eci; ListBase swap = {0}; - swap.first = rb->chains.first; - swap.last = rb->chains.last; + swap.first = ld->chains.first; + swap.last = ld->chains.last; - rb->chains.last = rb->chains.first = NULL; + ld->chains.last = ld->chains.first = NULL; while ((ec = BLI_pophead(&swap)) != NULL) { ec->next = ec->prev = NULL; - BLI_addtail(&rb->chains, ec); + BLI_addtail(&ld->chains, ec); LineartEdgeChainItem *first_eci = (LineartEdgeChainItem *)ec->chain.first; for (eci = first_eci->next; eci; eci = next_eci) { next_eci = eci->next; @@ -1203,7 +1203,7 @@ void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshol break; /* No need to split at the last point anyway. */ } if (angle < angle_threshold_rad) { - new_ec = lineart_chain_create(rb); + new_ec = lineart_chain_create(ld); new_ec->chain.first = eci; new_ec->chain.last = ec->chain.last; ec->chain.last = eci->prev; @@ -1211,7 +1211,7 @@ void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshol eci->prev = 0; /* End the previous one. */ - lineart_chain_append_point(rb, + lineart_chain_append_point(ld, ec, eci->pos, eci->gpos, @@ -1232,40 +1232,37 @@ void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshol } } -void MOD_lineart_chain_offset_towards_camera(LineartRenderBuffer *rb, - float dist, - bool use_custom_camera) +void MOD_lineart_chain_offset_towards_camera(LineartData *ld, float dist, bool use_custom_camera) { float dir[3]; float cam[3]; float view[3]; float view_clamp[3]; - copy_v3fl_v3db(cam, rb->camera_pos); - copy_v3fl_v3db(view, rb->view_vector); if (use_custom_camera) { - copy_v3fl_v3db(cam, rb->camera_pos); + copy_v3fl_v3db(cam, ld->conf.camera_pos); } else { - copy_v3fl_v3db(cam, rb->active_camera_pos); + copy_v3fl_v3db(cam, ld->conf.active_camera_pos); } - if (rb->cam_is_persp) { - LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { + if (ld->conf.cam_is_persp) { + LISTBASE_FOREACH (LineartEdgeChain *, ec, &ld->chains) { LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) { sub_v3_v3v3(dir, cam, eci->gpos); float orig_len = len_v3(dir); normalize_v3(dir); - mul_v3_fl(dir, MIN2(dist, orig_len - rb->near_clip)); + mul_v3_fl(dir, MIN2(dist, orig_len - ld->conf.near_clip)); add_v3_v3(eci->gpos, dir); } } } else { - LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { + copy_v3fl_v3db(view, ld->conf.view_vector); + LISTBASE_FOREACH (LineartEdgeChain *, ec, &ld->chains) { LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) { sub_v3_v3v3(dir, cam, eci->gpos); - float len_lim = dot_v3v3(view, dir) - rb->near_clip; + float len_lim = dot_v3v3(view, dir) - ld->conf.near_clip; normalize_v3_v3(view_clamp, view); mul_v3_fl(view_clamp, MIN2(dist, len_lim)); add_v3_v3(eci->gpos, view_clamp); diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 621531d29e3..c08bf3e0fe9 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -46,10 +46,6 @@ #include "DNA_scene_types.h" #include "MEM_guardedalloc.h" -#include "bmesh.h" -#include "bmesh_class.h" -#include "bmesh_tools.h" - #include "lineart_intern.h" typedef struct LineartIsecSingle { @@ -73,19 +69,18 @@ typedef struct LineartIsecThread { int count_test; /* For individual thread reference.*/ - LineartRenderBuffer *rb; + LineartData *ld; } LineartIsecThread; typedef struct LineartIsecData { - LineartRenderBuffer *rb; + LineartData *ld; LineartIsecThread *threads; int thread_count; } LineartIsecData; -static LineartBoundingArea *lineart_edge_first_bounding_area(LineartRenderBuffer *rb, - LineartEdge *e); +static LineartBoundingArea *lineart_edge_first_bounding_area(LineartData *ld, LineartEdge *e); -static void lineart_bounding_area_link_edge(LineartRenderBuffer *rb, +static void lineart_bounding_area_link_edge(LineartData *ld, LineartBoundingArea *root_ba, LineartEdge *e); @@ -99,12 +94,8 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this double *next_x, double *next_y); -static bool lineart_get_edge_bounding_areas(LineartRenderBuffer *rb, - LineartEdge *e, - int *rowbegin, - int *rowend, - int *colbegin, - int *colend); +static bool lineart_get_edge_bounding_areas( + LineartData *ld, LineartEdge *e, int *rowbegin, int *rowend, int *colbegin, int *colend); static bool lineart_triangle_edge_image_space_occlusion(SpinLock *spl, const LineartTriangle *tri, @@ -121,7 +112,7 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *spl, static void lineart_add_edge_to_array(LineartPendingEdges *pe, LineartEdge *e); -static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb, +static void lineart_bounding_area_link_triangle(LineartData *ld, LineartBoundingArea *root_ba, LineartTriangle *tri, double *LRUB, @@ -132,54 +123,54 @@ static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb, static void lineart_free_bounding_area_memory(LineartBoundingArea *ba, bool recursive); -static void lineart_free_bounding_area_memories(LineartRenderBuffer *rb); +static void lineart_free_bounding_area_memories(LineartData *ld); static LineartCache *lineart_init_cache(void); -static void lineart_discard_segment(LineartRenderBuffer *rb, LineartEdgeSegment *es) +static void lineart_discard_segment(LineartData *ld, LineartEdgeSegment *es) { - BLI_spin_lock(&rb->lock_cuts); + BLI_spin_lock(&ld->lock_cuts); memset(es, 0, sizeof(LineartEdgeSegment)); /* Storing the node for potentially reuse the memory for new segment data. * Line Art data is not freed after all calculations are done. */ - BLI_addtail(&rb->wasted_cuts, es); + BLI_addtail(&ld->wasted_cuts, es); - BLI_spin_unlock(&rb->lock_cuts); + BLI_spin_unlock(&ld->lock_cuts); } -static LineartEdgeSegment *lineart_give_segment(LineartRenderBuffer *rb) +static LineartEdgeSegment *lineart_give_segment(LineartData *ld) { - BLI_spin_lock(&rb->lock_cuts); + BLI_spin_lock(&ld->lock_cuts); /* See if there is any already allocated memory we can reuse. */ - if (rb->wasted_cuts.first) { - LineartEdgeSegment *es = (LineartEdgeSegment *)BLI_pophead(&rb->wasted_cuts); - BLI_spin_unlock(&rb->lock_cuts); + if (ld->wasted_cuts.first) { + LineartEdgeSegment *es = (LineartEdgeSegment *)BLI_pophead(&ld->wasted_cuts); + BLI_spin_unlock(&ld->lock_cuts); memset(es, 0, sizeof(LineartEdgeSegment)); return es; } - BLI_spin_unlock(&rb->lock_cuts); + BLI_spin_unlock(&ld->lock_cuts); /* Otherwise allocate some new memory. */ - return (LineartEdgeSegment *)lineart_mem_acquire_thread(&rb->render_data_pool, + return (LineartEdgeSegment *)lineart_mem_acquire_thread(&ld->render_data_pool, sizeof(LineartEdgeSegment)); } /** * Cuts the edge in image space and mark occlusion level for each segment. */ -static void lineart_edge_cut(LineartRenderBuffer *rb, +static void lineart_edge_cut(LineartData *ld, LineartEdge *e, double start, double end, uchar material_mask_bits, uchar mat_occlusion) { - LineartEdgeSegment *es, *ies, *next_es, *prev_es; + LineartEdgeSegment *seg, *i_seg, *next_seg, *prev_seg; LineartEdgeSegment *cut_start_before = 0, *cut_end_before = 0; - LineartEdgeSegment *ns = 0, *ns2 = 0; + LineartEdgeSegment *new_seg1 = 0, *new_seg2 = 0; int untouched = 0; /* If for some reason the occlusion function may give a result that has zero length, or reversed @@ -206,132 +197,136 @@ static void lineart_edge_cut(LineartRenderBuffer *rb, /* Begin looking for starting position of the segment. */ /* Not using a list iteration macro because of it more clear when using for loops to iterate * through the segments. */ - for (es = e->segments.first; es; es = es->next) { - if (LRT_DOUBLE_CLOSE_ENOUGH(es->at, start)) { - cut_start_before = es; - ns = cut_start_before; + for (seg = e->segments.first; seg; seg = seg->next) { + if (LRT_DOUBLE_CLOSE_ENOUGH(seg->at, start)) { + cut_start_before = seg; + new_seg1 = cut_start_before; break; } - if (es->next == NULL) { + if (seg->next == NULL) { break; } - ies = es->next; - if (ies->at > start + 1e-09 && start > es->at) { - cut_start_before = ies; - ns = lineart_give_segment(rb); + i_seg = seg->next; + if (i_seg->at > start + 1e-09 && start > seg->at) { + cut_start_before = i_seg; + new_seg1 = lineart_give_segment(ld); break; } } if (!cut_start_before && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) { untouched = 1; } - for (es = cut_start_before; es; es = es->next) { + for (seg = cut_start_before; seg; seg = seg->next) { /* We tried to cut at existing cutting point (e.g. where the line's occluded by a triangle * strip). */ - if (LRT_DOUBLE_CLOSE_ENOUGH(es->at, end)) { - cut_end_before = es; - ns2 = cut_end_before; + if (LRT_DOUBLE_CLOSE_ENOUGH(seg->at, end)) { + cut_end_before = seg; + new_seg2 = cut_end_before; break; } /* This check is to prevent `es->at == 1.0` (where we don't need to cut because we are at the * end point). */ - if (!es->next && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) { - cut_end_before = es; - ns2 = cut_end_before; + if (!seg->next && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) { + cut_end_before = seg; + new_seg2 = cut_end_before; untouched = 1; break; } /* When an actual cut is needed in the line. */ - if (es->at > end) { - cut_end_before = es; - ns2 = lineart_give_segment(rb); + if (seg->at > end) { + cut_end_before = seg; + new_seg2 = lineart_give_segment(ld); break; } } /* When we still can't find any existing cut in the line, we allocate new ones. */ - if (ns == NULL) { - ns = lineart_give_segment(rb); + if (new_seg1 == NULL) { + new_seg1 = lineart_give_segment(ld); } - if (ns2 == NULL) { + if (new_seg2 == NULL) { if (untouched) { - ns2 = ns; - cut_end_before = ns2; + new_seg2 = new_seg1; + cut_end_before = new_seg2; } else { - ns2 = lineart_give_segment(rb); + new_seg2 = lineart_give_segment(ld); } } if (cut_start_before) { - if (cut_start_before != ns) { + if (cut_start_before != new_seg1) { /* Insert cutting points for when a new cut is needed. */ - ies = cut_start_before->prev ? cut_start_before->prev : NULL; - ns->occlusion = ies ? ies->occlusion : 0; - ns->material_mask_bits = ies->material_mask_bits; - BLI_insertlinkbefore(&e->segments, cut_start_before, ns); + i_seg = cut_start_before->prev ? cut_start_before->prev : NULL; + if (i_seg) { + new_seg1->occlusion = i_seg->occlusion; + new_seg1->material_mask_bits = i_seg->material_mask_bits; + } + BLI_insertlinkbefore(&e->segments, cut_start_before, new_seg1); } /* Otherwise we already found a existing cutting point, no need to insert a new one. */ } else { /* We have yet to reach a existing cutting point even after we searched the whole line, so we * append the new cut to the end. */ - ies = e->segments.last; - ns->occlusion = ies->occlusion; - ns->material_mask_bits = ies->material_mask_bits; - BLI_addtail(&e->segments, ns); + i_seg = e->segments.last; + new_seg1->occlusion = i_seg->occlusion; + new_seg1->material_mask_bits = i_seg->material_mask_bits; + BLI_addtail(&e->segments, new_seg1); } if (cut_end_before) { /* The same manipulation as on "cut_start_before". */ - if (cut_end_before != ns2) { - ies = cut_end_before->prev ? cut_end_before->prev : NULL; - ns2->occlusion = ies ? ies->occlusion : 0; - ns2->material_mask_bits = ies ? ies->material_mask_bits : 0; - BLI_insertlinkbefore(&e->segments, cut_end_before, ns2); + if (cut_end_before != new_seg2) { + i_seg = cut_end_before->prev ? cut_end_before->prev : NULL; + if (i_seg) { + new_seg2->occlusion = i_seg->occlusion; + new_seg2->material_mask_bits = i_seg->material_mask_bits; + } + BLI_insertlinkbefore(&e->segments, cut_end_before, new_seg2); } } else { - ies = e->segments.last; - ns2->occlusion = ies->occlusion; - ns2->material_mask_bits = ies->material_mask_bits; - BLI_addtail(&e->segments, ns2); + i_seg = e->segments.last; + new_seg2->occlusion = i_seg->occlusion; + new_seg2->material_mask_bits = i_seg->material_mask_bits; + BLI_addtail(&e->segments, new_seg2); } /* If we touched the cut list, we assign the new cut position based on new cut position, * this way we accommodate precision lost due to multiple cut inserts. */ - ns->at = start; + new_seg1->at = start; if (!untouched) { - ns2->at = end; + new_seg2->at = end; } else { /* For the convenience of the loop below. */ - ns2 = ns2->next; + new_seg2 = new_seg2->next; } /* Register 1 level of occlusion for all touched segments. */ - for (es = ns; es && es != ns2; es = es->next) { - es->occlusion += mat_occlusion; - es->material_mask_bits |= material_mask_bits; + for (seg = new_seg1; seg && seg != new_seg2; seg = seg->next) { + seg->occlusion += mat_occlusion; + seg->material_mask_bits |= material_mask_bits; } /* Reduce adjacent cutting points of the same level, which saves memory. */ - char min_occ = 127; - prev_es = NULL; - for (es = e->segments.first; es; es = next_es) { - next_es = es->next; - - if (prev_es && prev_es->occlusion == es->occlusion && - prev_es->material_mask_bits == es->material_mask_bits) { - BLI_remlink(&e->segments, es); + int8_t min_occ = 127; + prev_seg = NULL; + for (seg = e->segments.first; seg; seg = next_seg) { + next_seg = seg->next; + + if (prev_seg && prev_seg->occlusion == seg->occlusion && + prev_seg->material_mask_bits == seg->material_mask_bits) { + BLI_remlink(&e->segments, seg); /* This puts the node back to the render buffer, if more cut happens, these unused nodes get * picked first. */ - lineart_discard_segment(rb, es); + lineart_discard_segment(ld, seg); continue; } - min_occ = MIN2(min_occ, es->occlusion); + min_occ = MIN2(min_occ, seg->occlusion); - prev_es = es; + prev_seg = seg; } e->min_occ = min_occ; } @@ -374,10 +369,10 @@ static void lineart_bounding_area_line_add(LineartBoundingArea *ba, LineartEdge ba->line_count++; } -static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge *e, int thread_id) +static void lineart_occlusion_single_line(LineartData *ld, LineartEdge *e, int thread_id) { double x = e->v1->fbcoord[0], y = e->v1->fbcoord[1]; - LineartBoundingArea *ba = lineart_edge_first_bounding_area(rb, e); + LineartBoundingArea *ba = lineart_edge_first_bounding_area(ld, e); LineartBoundingArea *nba = ba; LineartTriangleThread *tri; @@ -406,20 +401,20 @@ static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge * continue; } tri->testing_e[thread_id] = e; - if (lineart_triangle_edge_image_space_occlusion(&rb->lock_task, + if (lineart_triangle_edge_image_space_occlusion(&ld->lock_task, (const LineartTriangle *)tri, e, - rb->camera_pos, - rb->cam_is_persp, - rb->allow_overlapping_edges, - rb->view_projection, - rb->view_vector, - rb->shift_x, - rb->shift_y, + ld->conf.camera_pos, + ld->conf.cam_is_persp, + ld->conf.allow_overlapping_edges, + ld->conf.view_projection, + ld->conf.view_vector, + ld->conf.shift_x, + ld->conf.shift_y, &l, &r)) { - lineart_edge_cut(rb, e, l, r, tri->base.material_mask_bits, tri->base.mat_occlusion); - if (e->min_occ > rb->max_occlusion_level) { + lineart_edge_cut(ld, e, l, r, tri->base.material_mask_bits, tri->base.mat_occlusion); + if (e->min_occ > ld->conf.max_occlusion_level) { /* No need to calculate any longer on this line because no level more than set value is * going to show up in the rendered result. */ return; @@ -431,24 +426,24 @@ static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge * } } -static int lineart_occlusion_make_task_info(LineartRenderBuffer *rb, LineartRenderTaskInfo *rti) +static int lineart_occlusion_make_task_info(LineartData *ld, LineartRenderTaskInfo *rti) { int res = 0; int starting_index; - BLI_spin_lock(&rb->lock_task); + BLI_spin_lock(&ld->lock_task); - starting_index = rb->scheduled_count; - rb->scheduled_count += LRT_THREAD_EDGE_COUNT; + starting_index = ld->scheduled_count; + ld->scheduled_count += LRT_THREAD_EDGE_COUNT; - BLI_spin_unlock(&rb->lock_task); + BLI_spin_unlock(&ld->lock_task); - if (starting_index >= rb->pending_edges.next) { + if (starting_index >= ld->pending_edges.next) { res = 0; } else { - rti->pending_edges.array = &rb->pending_edges.array[starting_index]; - int remaining = rb->pending_edges.next - starting_index; + rti->pending_edges.array = &ld->pending_edges.array[starting_index]; + int remaining = ld->pending_edges.next - starting_index; rti->pending_edges.max = MIN2(remaining, LRT_THREAD_EDGE_COUNT); res = 1; } @@ -458,13 +453,13 @@ static int lineart_occlusion_make_task_info(LineartRenderBuffer *rb, LineartRend static void lineart_occlusion_worker(TaskPool *__restrict UNUSED(pool), LineartRenderTaskInfo *rti) { - LineartRenderBuffer *rb = rti->rb; + LineartData *ld = rti->ld; LineartEdge *eip; - while (lineart_occlusion_make_task_info(rb, rti)) { + while (lineart_occlusion_make_task_info(ld, rti)) { for (int i = 0; i < rti->pending_edges.max; i++) { eip = rti->pending_edges.array[i]; - lineart_occlusion_single_line(rb, eip, rti->thread_id); + lineart_occlusion_single_line(ld, eip, rti->thread_id); } } } @@ -474,9 +469,9 @@ static void lineart_occlusion_worker(TaskPool *__restrict UNUSED(pool), LineartR * #MOD_lineart_compute_feature_lines function. * This function handles all occlusion calculation. */ -static void lineart_main_occlusion_begin(LineartRenderBuffer *rb) +static void lineart_main_occlusion_begin(LineartData *ld) { - int thread_count = rb->thread_count; + int thread_count = ld->thread_count; LineartRenderTaskInfo *rti = MEM_callocN(sizeof(LineartRenderTaskInfo) * thread_count, "Task Pool"); int i; @@ -485,7 +480,7 @@ static void lineart_main_occlusion_begin(LineartRenderBuffer *rb) for (i = 0; i < thread_count; i++) { rti[i].thread_id = i; - rti[i].rb = rb; + rti[i].ld = ld; BLI_task_pool_push(tp, (TaskRunFunction)lineart_occlusion_worker, &rti[i], 0, NULL); } BLI_task_pool_work_and_wait(tp); @@ -575,6 +570,12 @@ static int lineart_point_on_line_segment(double v[2], double v0[2], double v1[2] return 0; } +enum LineartPointTri { + LRT_OUTSIDE_TRIANGLE = 0, + LRT_ON_TRIANGLE = 1, + LRT_INSIDE_TRIANGLE = 2, +}; + /** * Same algorithm as lineart_point_inside_triangle(), but returns differently: * 0-outside 1-on the edge 2-inside. @@ -585,7 +586,7 @@ static int lineart_point_triangle_relation(double v[2], double v0[2], double v1[ double r; if (lineart_point_on_line_segment(v, v0, v1) || lineart_point_on_line_segment(v, v1, v2) || lineart_point_on_line_segment(v, v2, v0)) { - return 1; + return LRT_ON_TRIANGLE; } cl = (v0[0] - v[0]) * (v1[1] - v[1]) - (v0[1] - v[1]) * (v1[0] - v[0]); @@ -593,28 +594,28 @@ static int lineart_point_triangle_relation(double v[2], double v0[2], double v1[ cl = (v1[0] - v[0]) * (v2[1] - v[1]) - (v1[1] - v[1]) * (v2[0] - v[0]); if ((r = c * cl) < 0) { - return 0; + return LRT_OUTSIDE_TRIANGLE; } c = cl; cl = (v2[0] - v[0]) * (v0[1] - v[1]) - (v2[1] - v[1]) * (v0[0] - v[0]); if ((r = c * cl) < 0) { - return 0; + return LRT_OUTSIDE_TRIANGLE; } c = cl; cl = (v0[0] - v[0]) * (v1[1] - v[1]) - (v0[1] - v[1]) * (v1[0] - v[0]); if ((r = c * cl) < 0) { - return 0; + return LRT_OUTSIDE_TRIANGLE; } if (r == 0) { - return 1; + return LRT_ON_TRIANGLE; } - return 2; + return LRT_INSIDE_TRIANGLE; } /** @@ -662,17 +663,17 @@ static bool lineart_point_inside_triangle3d(double v[3], double v0[3], double v1 * The following `lineart_memory_get_XXX_space` functions are for allocating new memory for some * modified geometries in the culling stage. */ -static LineartElementLinkNode *lineart_memory_get_triangle_space(LineartRenderBuffer *rb) +static LineartElementLinkNode *lineart_memory_get_triangle_space(LineartData *ld) { LineartElementLinkNode *eln; /* We don't need to allocate a whole bunch of triangles because the amount of clipped triangles * are relatively small. */ - LineartTriangle *render_triangles = lineart_mem_acquire(&rb->render_data_pool, - 64 * rb->triangle_size); + LineartTriangle *render_triangles = lineart_mem_acquire(&ld->render_data_pool, + 64 * ld->sizeof_triangle); - eln = lineart_list_append_pointer_pool_sized(&rb->triangle_buffer_pointers, - &rb->render_data_pool, + eln = lineart_list_append_pointer_pool_sized(&ld->geom.triangle_buffer_pointers, + &ld->render_data_pool, render_triangles, sizeof(LineartElementLinkNode)); eln->element_count = 64; @@ -681,15 +682,15 @@ static LineartElementLinkNode *lineart_memory_get_triangle_space(LineartRenderBu return eln; } -static LineartElementLinkNode *lineart_memory_get_vert_space(LineartRenderBuffer *rb) +static LineartElementLinkNode *lineart_memory_get_vert_space(LineartData *ld) { LineartElementLinkNode *eln; - LineartVert *render_vertices = lineart_mem_acquire(&rb->render_data_pool, + LineartVert *render_vertices = lineart_mem_acquire(&ld->render_data_pool, sizeof(LineartVert) * 64); - eln = lineart_list_append_pointer_pool_sized(&rb->vertex_buffer_pointers, - &rb->render_data_pool, + eln = lineart_list_append_pointer_pool_sized(&ld->geom.vertex_buffer_pointers, + &ld->render_data_pool, render_vertices, sizeof(LineartElementLinkNode)); eln->element_count = 64; @@ -698,18 +699,18 @@ static LineartElementLinkNode *lineart_memory_get_vert_space(LineartRenderBuffer return eln; } -static LineartElementLinkNode *lineart_memory_get_edge_space(LineartRenderBuffer *rb) +static LineartElementLinkNode *lineart_memory_get_edge_space(LineartData *ld) { LineartElementLinkNode *eln; - LineartEdge *render_edges = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdge) * 64); + LineartEdge *render_edges = lineart_mem_acquire(&ld->render_data_pool, sizeof(LineartEdge) * 64); - eln = lineart_list_append_pointer_pool_sized(&rb->line_buffer_pointers, - &rb->render_data_pool, + eln = lineart_list_append_pointer_pool_sized(&ld->geom.line_buffer_pointers, + &ld->render_data_pool, render_edges, sizeof(LineartElementLinkNode)); eln->element_count = 64; - eln->crease_threshold = rb->crease_threshold; + eln->crease_threshold = ld->conf.crease_threshold; eln->flags |= LRT_ELEMENT_IS_ADDITIONAL; return eln; @@ -751,7 +752,7 @@ static void lineart_discard_duplicated_edges(LineartEdge *old_e) * Does near-plane cut on 1 triangle only. When cutting with far-plane, the camera vectors gets * reversed by the caller so don't need to implement one in a different direction. */ -static void lineart_triangle_cull_single(LineartRenderBuffer *rb, +static void lineart_triangle_cull_single(LineartData *ld, LineartTriangle *tri, int in0, int in1, @@ -768,7 +769,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, LineartElementLinkNode *e_eln, LineartElementLinkNode *t_eln) { - double vv1[3], vv2[3], dot1, dot2; + double span_v1[3], span_v2[3], dot_v1, dot_v2; double a; int v_count = *r_v_count; int e_count = *r_e_count; @@ -777,7 +778,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, LineartEdge *new_e, *e, *old_e; LineartEdgeSegment *es; - LineartTriangleAdjacent *ta; + LineartTriangleAdjacent *tri_adj; if (tri->flags & (LRT_CULL_USED | LRT_CULL_GENERATED | LRT_CULL_DISCARD)) { return; @@ -785,11 +786,12 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, /* See definition of tri->intersecting_verts and the usage in * lineart_geometry_object_load() for details. */ - ta = (void *)tri->intersecting_verts; + tri_adj = (void *)tri->intersecting_verts; LineartVert *vt = &((LineartVert *)v_eln->pointer)[v_count]; - LineartTriangle *tri1 = (void *)(((uchar *)t_eln->pointer) + rb->triangle_size * t_count); - LineartTriangle *tri2 = (void *)(((uchar *)t_eln->pointer) + rb->triangle_size * (t_count + 1)); + LineartTriangle *tri1 = (void *)(((uchar *)t_eln->pointer) + ld->sizeof_triangle * t_count); + LineartTriangle *tri2 = (void *)(((uchar *)t_eln->pointer) + + ld->sizeof_triangle * (t_count + 1)); new_e = &((LineartEdge *)e_eln->pointer)[e_count]; /* Init `edge` to the last `edge` entry. */ @@ -799,12 +801,12 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, new_e = &((LineartEdge *)e_eln->pointer)[e_count]; \ e_count++; \ e = new_e; \ - es = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeSegment)); \ + es = lineart_mem_acquire(&ld->render_data_pool, sizeof(LineartEdgeSegment)); \ BLI_addtail(&e->segments, es); #define SELECT_EDGE(e_num, v1_link, v2_link, new_tri) \ - if (ta->e[e_num]) { \ - old_e = ta->e[e_num]; \ + if (tri_adj->e[e_num]) { \ + old_e = tri_adj->e[e_num]; \ new_flag = old_e->flags; \ old_e->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ lineart_discard_duplicated_edges(old_e); \ @@ -817,28 +819,28 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, e->object_ref = ob; \ e->t1 = ((old_e->t1 == tri) ? (new_tri) : (old_e->t1)); \ e->t2 = ((old_e->t2 == tri) ? (new_tri) : (old_e->t2)); \ - lineart_add_edge_to_array(&rb->pending_edges, e); \ + lineart_add_edge_to_array(&ld->pending_edges, e); \ } #define RELINK_EDGE(e_num, new_tri) \ - if (ta->e[e_num]) { \ - old_e = ta->e[e_num]; \ + if (tri_adj->e[e_num]) { \ + old_e = tri_adj->e[e_num]; \ old_e->t1 = ((old_e->t1 == tri) ? (new_tri) : (old_e->t1)); \ old_e->t2 = ((old_e->t2 == tri) ? (new_tri) : (old_e->t2)); \ } #define REMOVE_TRIANGLE_EDGE \ - if (ta->e[0]) { \ - ta->e[0]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ - lineart_discard_duplicated_edges(ta->e[0]); \ + if (tri_adj->e[0]) { \ + tri_adj->e[0]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ + lineart_discard_duplicated_edges(tri_adj->e[0]); \ } \ - if (ta->e[1]) { \ - ta->e[1]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ - lineart_discard_duplicated_edges(ta->e[1]); \ + if (tri_adj->e[1]) { \ + tri_adj->e[1]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ + lineart_discard_duplicated_edges(tri_adj->e[1]); \ } \ - if (ta->e[2]) { \ - ta->e[2]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ - lineart_discard_duplicated_edges(ta->e[2]); \ + if (tri_adj->e[2]) { \ + tri_adj->e[2]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ + lineart_discard_duplicated_edges(tri_adj->e[2]); \ } switch (in0 + in1 + in2) { @@ -878,22 +880,22 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, if (!in0) { /* Cut point for line 2---|-----0. */ - sub_v3_v3v3_db(vv1, tri->v[0]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[2]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot1 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[0]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[2]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v1 / (dot_v1 + dot_v2); /* Assign it to a new point. */ interp_v3_v3v3_db(vt[0].gloc, tri->v[0]->gloc, tri->v[2]->gloc, a); mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); vt[0].index = tri->v[2]->index; /* Cut point for line 1---|-----0. */ - sub_v3_v3v3_db(vv1, tri->v[0]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[1]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot1 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[0]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[1]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v1 / (dot_v1 + dot_v2); /* Assign it to another new point. */ interp_v3_v3v3_db(vt[1].gloc, tri->v[0]->gloc, tri->v[1]->gloc, a); mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); @@ -903,7 +905,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_add_edge_to_array(&rb->pending_edges, e); + lineart_add_edge_to_array(&ld->pending_edges, e); } /* NOTE: inverting `e->v1/v2` (left/right point) doesn't matter as long as * `tri->edge` and `tri->v` has the same sequence. and the winding direction @@ -931,20 +933,20 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, t_count += 1; } else if (!in2) { - sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot1 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[2]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[0]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v1 / (dot_v1 + dot_v2); interp_v3_v3v3_db(vt[0].gloc, tri->v[2]->gloc, tri->v[0]->gloc, a); mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); vt[0].index = tri->v[0]->index; - sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[1]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot1 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[2]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[1]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v1 / (dot_v1 + dot_v2); interp_v3_v3v3_db(vt[1].gloc, tri->v[2]->gloc, tri->v[1]->gloc, a); mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); vt[1].index = tri->v[1]->index; @@ -952,7 +954,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_add_edge_to_array(&rb->pending_edges, e); + lineart_add_edge_to_array(&ld->pending_edges, e); } e->v1 = &vt[0]; e->v2 = &vt[1]; @@ -972,20 +974,20 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, t_count += 1; } else if (!in1) { - sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[2]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot1 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[1]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[2]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v1 / (dot_v1 + dot_v2); interp_v3_v3v3_db(vt[0].gloc, tri->v[1]->gloc, tri->v[2]->gloc, a); mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); vt[0].index = tri->v[2]->index; - sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot1 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[1]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[0]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v1 / (dot_v1 + dot_v2); interp_v3_v3v3_db(vt[1].gloc, tri->v[1]->gloc, tri->v[0]->gloc, a); mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); vt[1].index = tri->v[0]->index; @@ -993,7 +995,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_add_edge_to_array(&rb->pending_edges, e); + lineart_add_edge_to_array(&ld->pending_edges, e); } e->v1 = &vt[1]; e->v2 = &vt[0]; @@ -1043,22 +1045,22 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, */ if (in0) { /* Cut point for line 0---|------1. */ - sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot2 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[1]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[0]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v2 / (dot_v1 + dot_v2); /* Assign to a new point. */ interp_v3_v3v3_db(vt[0].gloc, tri->v[0]->gloc, tri->v[1]->gloc, a); mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); vt[0].index = tri->v[0]->index; /* Cut point for line 0---|------2. */ - sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot2 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[2]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[0]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v2 / (dot_v1 + dot_v2); /* Assign to other new point. */ interp_v3_v3v3_db(vt[1].gloc, tri->v[0]->gloc, tri->v[2]->gloc, a); mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); @@ -1068,7 +1070,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_add_edge_to_array(&rb->pending_edges, e); + lineart_add_edge_to_array(&ld->pending_edges, e); } e->v1 = &vt[1]; e->v2 = &vt[0]; @@ -1099,20 +1101,20 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, } else if (in1) { - sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[2]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot1 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[1]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[2]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v1 / (dot_v1 + dot_v2); interp_v3_v3v3_db(vt[0].gloc, tri->v[1]->gloc, tri->v[2]->gloc, a); mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); vt[0].index = tri->v[1]->index; - sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot1 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[1]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[0]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v1 / (dot_v1 + dot_v2); interp_v3_v3v3_db(vt[1].gloc, tri->v[1]->gloc, tri->v[0]->gloc, a); mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); vt[1].index = tri->v[1]->index; @@ -1120,7 +1122,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_add_edge_to_array(&rb->pending_edges, e); + lineart_add_edge_to_array(&ld->pending_edges, e); } e->v1 = &vt[1]; e->v2 = &vt[0]; @@ -1148,20 +1150,20 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, } else if (in2) { - sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot1 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[2]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[0]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v1 / (dot_v1 + dot_v2); interp_v3_v3v3_db(vt[0].gloc, tri->v[2]->gloc, tri->v[0]->gloc, a); mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); vt[0].index = tri->v[2]->index; - sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[1]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot1 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[2]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[1]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v1 / (dot_v1 + dot_v2); interp_v3_v3v3_db(vt[1].gloc, tri->v[2]->gloc, tri->v[1]->gloc, a); mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); vt[1].index = tri->v[2]->index; @@ -1169,7 +1171,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_add_edge_to_array(&rb->pending_edges, e); + lineart_add_edge_to_array(&ld->pending_edges, e); } e->v1 = &vt[1]; e->v2 = &vt[0]; @@ -1213,22 +1215,22 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, * new topology that represents the trimmed triangle. (which then became a triangle or a square * formed by two triangles) */ -static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far) +static void lineart_main_cull_triangles(LineartData *ld, bool clip_far) { LineartTriangle *tri; LineartElementLinkNode *v_eln, *t_eln, *e_eln; - double(*vp)[4] = rb->view_projection; + double(*vp)[4] = ld->conf.view_projection; int i; int v_count = 0, t_count = 0, e_count = 0; Object *ob; - bool allow_boundaries = rb->allow_boundaries; + bool allow_boundaries = ld->conf.allow_boundaries; double cam_pos[3]; - double clip_start = rb->near_clip, clip_end = rb->far_clip; + double clip_start = ld->conf.near_clip, clip_end = ld->conf.far_clip; double view_dir[3], clip_advance[3]; - copy_v3_v3_db(view_dir, rb->view_vector); - copy_v3_v3_db(clip_advance, rb->view_vector); - copy_v3_v3_db(cam_pos, rb->camera_pos); + copy_v3_v3_db(view_dir, ld->conf.view_vector); + copy_v3_v3_db(clip_advance, ld->conf.view_vector); + copy_v3_v3_db(cam_pos, ld->conf.camera_pos); if (clip_far) { /* Move starting point to end plane. */ @@ -1244,25 +1246,25 @@ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far) add_v3_v3_db(cam_pos, clip_advance); } - v_eln = lineart_memory_get_vert_space(rb); - t_eln = lineart_memory_get_triangle_space(rb); - e_eln = lineart_memory_get_edge_space(rb); + v_eln = lineart_memory_get_vert_space(ld); + t_eln = lineart_memory_get_triangle_space(ld); + e_eln = lineart_memory_get_edge_space(ld); /* Additional memory space for storing generated points and triangles. */ #define LRT_CULL_ENSURE_MEMORY \ if (v_count > 60) { \ v_eln->element_count = v_count; \ - v_eln = lineart_memory_get_vert_space(rb); \ + v_eln = lineart_memory_get_vert_space(ld); \ v_count = 0; \ } \ if (t_count > 60) { \ t_eln->element_count = t_count; \ - t_eln = lineart_memory_get_triangle_space(rb); \ + t_eln = lineart_memory_get_triangle_space(ld); \ t_count = 0; \ } \ if (e_count > 60) { \ e_eln->element_count = e_count; \ - e_eln = lineart_memory_get_edge_space(rb); \ + e_eln = lineart_memory_get_edge_space(ld); \ e_count = 0; \ } @@ -1297,21 +1299,21 @@ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far) int use_w = 3; int in0 = 0, in1 = 0, in2 = 0; - if (!rb->cam_is_persp) { + if (!ld->conf.cam_is_persp) { clip_start = -1; clip_end = 1; use_w = 2; } /* Then go through all the other triangles. */ - LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) { + LISTBASE_FOREACH (LineartElementLinkNode *, eln, &ld->geom.triangle_buffer_pointers) { if (eln->flags & LRT_ELEMENT_IS_ADDITIONAL) { continue; } ob = eln->object_ref; for (i = 0; i < eln->element_count; i++) { /* Select the triangle in the array. */ - tri = (void *)(((uchar *)eln->pointer) + rb->triangle_size * i); + tri = (void *)(((uchar *)eln->pointer) + ld->sizeof_triangle * i); if (tri->flags & LRT_CULL_DISCARD) { continue; @@ -1319,7 +1321,7 @@ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far) LRT_CULL_DECIDE_INSIDE LRT_CULL_ENSURE_MEMORY - lineart_triangle_cull_single(rb, + lineart_triangle_cull_single(ld, tri, in0, in1, @@ -1348,33 +1350,33 @@ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far) * Adjacent data is only used during the initial stages of computing. * So we can free it using this function when it is not needed anymore. */ -static void lineart_main_free_adjacent_data(LineartRenderBuffer *rb) +static void lineart_main_free_adjacent_data(LineartData *ld) { - LinkData *ld; - while ((ld = BLI_pophead(&rb->triangle_adjacent_pointers)) != NULL) { - MEM_freeN(ld->data); + LinkData *link; + while ((link = BLI_pophead(&ld->geom.triangle_adjacent_pointers)) != NULL) { + MEM_freeN(link->data); } - LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) { + LISTBASE_FOREACH (LineartElementLinkNode *, eln, &ld->geom.triangle_buffer_pointers) { LineartTriangle *tri = eln->pointer; int i; for (i = 0; i < eln->element_count; i++) { /* See definition of tri->intersecting_verts and the usage in * lineart_geometry_object_load() for detailed. */ tri->intersecting_verts = NULL; - tri = (LineartTriangle *)(((uchar *)tri) + rb->triangle_size); + tri = (LineartTriangle *)(((uchar *)tri) + ld->sizeof_triangle); } } } -static void lineart_main_perspective_division(LineartRenderBuffer *rb) +static void lineart_main_perspective_division(LineartData *ld) { LineartVert *vt; int i; - LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->vertex_buffer_pointers) { + LISTBASE_FOREACH (LineartElementLinkNode *, eln, &ld->geom.vertex_buffer_pointers) { vt = eln->pointer; for (i = 0; i < eln->element_count; i++) { - if (rb->cam_is_persp) { + if (ld->conf.cam_is_persp) { /* Do not divide Z, we use Z to back transform cut points in later chaining process. */ vt[i].fbcoord[0] /= vt[i].fbcoord[3]; vt[i].fbcoord[1] /= vt[i].fbcoord[3]; @@ -1385,13 +1387,13 @@ static void lineart_main_perspective_division(LineartRenderBuffer *rb) // `vt[i].fbcoord[2] = -2 * vt[i].fbcoord[2] / (far - near) - (far + near) / (far - near); } /* Shifting is always needed. */ - vt[i].fbcoord[0] -= rb->shift_x * 2; - vt[i].fbcoord[1] -= rb->shift_y * 2; + vt[i].fbcoord[0] -= ld->conf.shift_x * 2; + vt[i].fbcoord[1] -= ld->conf.shift_y * 2; } } } -static void lineart_main_discard_out_of_frame_edges(LineartRenderBuffer *rb) +static void lineart_main_discard_out_of_frame_edges(LineartData *ld) { LineartEdge *e; int i; @@ -1399,7 +1401,7 @@ static void lineart_main_discard_out_of_frame_edges(LineartRenderBuffer *rb) #define LRT_VERT_OUT_OF_BOUND(v) \ (v && (v->fbcoord[0] < -1 || v->fbcoord[0] > 1 || v->fbcoord[1] < -1 || v->fbcoord[1] > 1)) - LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->line_buffer_pointers) { + LISTBASE_FOREACH (LineartElementLinkNode *, eln, &ld->geom.line_buffer_pointers) { e = (LineartEdge *)eln->pointer; for (i = 0; i < eln->element_count; i++) { if ((LRT_VERT_OUT_OF_BOUND(e[i].v1) && LRT_VERT_OUT_OF_BOUND(e[i].v2))) { @@ -1436,14 +1438,22 @@ static void lineart_mvert_transform_task(void *__restrict userdata, v->index = i; } -#define LRT_EDGE_FLAG_TYPE_MAX_BITS 6 +static const int LRT_MESH_EDGE_TYPES[] = { + LRT_EDGE_FLAG_EDGE_MARK, + LRT_EDGE_FLAG_CONTOUR, + LRT_EDGE_FLAG_CREASE, + LRT_EDGE_FLAG_MATERIAL, + LRT_EDGE_FLAG_LOOSE, +}; + +#define LRT_MESH_EDGE_TYPES_COUNT 5 -static int lineart_edge_type_duplication_count(char eflag) +static int lineart_edge_type_duplication_count(int eflag) { int count = 0; /* See eLineartEdgeFlag for details. */ - for (int i = 0; i < LRT_EDGE_FLAG_TYPE_MAX_BITS; i++) { - if (eflag & (1 << i)) { + for (int i = 0; i < LRT_MESH_EDGE_TYPES_COUNT; i++) { + if (eflag & LRT_MESH_EDGE_TYPES[i]) { count++; } } @@ -1454,17 +1464,17 @@ static int lineart_edge_type_duplication_count(char eflag) * Because we have a variable size for #LineartTriangle, we need an access helper. * See #LineartTriangleThread for more info. */ -static LineartTriangle *lineart_triangle_from_index(LineartRenderBuffer *rb, +static LineartTriangle *lineart_triangle_from_index(LineartData *ld, LineartTriangle *rt_array, int index) { - char *b = (char *)rt_array; - b += (index * rb->triangle_size); + int8_t *b = (int8_t *)rt_array; + b += (index * ld->sizeof_triangle); return (LineartTriangle *)b; } typedef struct EdgeFeatData { - LineartRenderBuffer *rb; + LineartData *ld; Mesh *me; const MLoopTri *mlooptri; LineartTriangle *tri_array; @@ -1510,7 +1520,8 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata, } bool face_mark_filtered = false; - bool enable_face_mark = (e_feat_data->use_freestyle_face && e_feat_data->rb->filter_face_mark); + bool enable_face_mark = (e_feat_data->use_freestyle_face && + e_feat_data->ld->conf.filter_face_mark); bool only_contour = false; if (enable_face_mark) { FreestyleFace *ff1, *ff2; @@ -1527,7 +1538,8 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata, * path is simper when it's assuming both ff1 and ff2 not NULL. */ ff2 = ff1; } - if (e_feat_data->rb->filter_face_mark_boundaries ^ e_feat_data->rb->filter_face_mark_invert) { + if (e_feat_data->ld->conf.filter_face_mark_boundaries ^ + e_feat_data->ld->conf.filter_face_mark_invert) { if ((ff1->flag & FREESTYLE_FACE_MARK) || (ff2->flag & FREESTYLE_FACE_MARK)) { face_mark_filtered = true; } @@ -1537,12 +1549,12 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata, face_mark_filtered = true; } } - if (e_feat_data->rb->filter_face_mark_invert) { + if (e_feat_data->ld->conf.filter_face_mark_invert) { face_mark_filtered = !face_mark_filtered; } if (!face_mark_filtered) { edge_nabr[i].flags = LRT_EDGE_FLAG_INHIBIT; - if (e_feat_data->rb->filter_face_mark_keep_contour) { + if (e_feat_data->ld->conf.filter_face_mark_keep_contour) { only_contour = true; } } @@ -1561,50 +1573,50 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata, LineartTriangle *tri1, *tri2; LineartVert *vert; - LineartRenderBuffer *rb = e_feat_data->rb; + LineartData *ld = e_feat_data->ld; int f1 = i / 3, f2 = edge_nabr[i].e / 3; /* The mesh should already be triangulated now, so we can assume each face is a triangle. */ - tri1 = lineart_triangle_from_index(rb, e_feat_data->tri_array, f1); - tri2 = lineart_triangle_from_index(rb, e_feat_data->tri_array, f2); + tri1 = lineart_triangle_from_index(ld, e_feat_data->tri_array, f1); + tri2 = lineart_triangle_from_index(ld, e_feat_data->tri_array, f2); vert = &e_feat_data->v_array[edge_nabr[i].v1]; double view_vector_persp[3]; double *view_vector = view_vector_persp; - double dot_1 = 0, dot_2 = 0; + double dot_v1 = 0, dot_v2 = 0; double result; bool material_back_face = ((tri1->flags | tri2->flags) & LRT_TRIANGLE_MAT_BACK_FACE_CULLING); - if (rb->use_contour || rb->use_back_face_culling || material_back_face) { - if (rb->cam_is_persp) { - sub_v3_v3v3_db(view_vector, rb->camera_pos, vert->gloc); + if (ld->conf.use_contour || ld->conf.use_back_face_culling || material_back_face) { + if (ld->conf.cam_is_persp) { + sub_v3_v3v3_db(view_vector, ld->conf.camera_pos, vert->gloc); } else { - view_vector = rb->view_vector; + view_vector = ld->conf.view_vector; } - dot_1 = dot_v3v3_db(view_vector, tri1->gn); - dot_2 = dot_v3v3_db(view_vector, tri2->gn); + dot_v1 = dot_v3v3_db(view_vector, tri1->gn); + dot_v2 = dot_v3v3_db(view_vector, tri2->gn); - if ((result = dot_1 * dot_2) <= 0 && (dot_1 + dot_2)) { + if ((result = dot_v1 * dot_v2) <= 0 && (dot_v1 + dot_v2)) { edge_flag_result |= LRT_EDGE_FLAG_CONTOUR; } - if (rb->use_back_face_culling) { - if (dot_1 < 0) { + if (ld->conf.use_back_face_culling) { + if (dot_v1 < 0) { tri1->flags |= LRT_CULL_DISCARD; } - if (dot_2 < 0) { + if (dot_v2 < 0) { tri2->flags |= LRT_CULL_DISCARD; } } if (material_back_face) { - if (tri1->flags & LRT_TRIANGLE_MAT_BACK_FACE_CULLING && dot_1 < 0) { + if (tri1->flags & LRT_TRIANGLE_MAT_BACK_FACE_CULLING && dot_v1 < 0) { tri1->flags |= LRT_CULL_DISCARD; } - if (tri2->flags & LRT_TRIANGLE_MAT_BACK_FACE_CULLING && dot_2 < 0) { + if (tri2->flags & LRT_TRIANGLE_MAT_BACK_FACE_CULLING && dot_v2 < 0) { tri2->flags |= LRT_CULL_DISCARD; } } @@ -1612,9 +1624,9 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata, if (!only_contour) { - if (rb->use_crease) { + if (ld->conf.use_crease) { bool do_crease = true; - if (!rb->force_crease && !e_feat_data->use_auto_smooth && + if (!ld->conf.force_crease && !e_feat_data->use_auto_smooth && (me->mpoly[mlooptri[f1].poly].flag & ME_SMOOTH) && (me->mpoly[mlooptri[f2].poly].flag & ME_SMOOTH)) { do_crease = false; @@ -1627,7 +1639,7 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata, int mat1 = me->mpoly[mlooptri[f1].poly].mat_nr; int mat2 = me->mpoly[mlooptri[f2].poly].mat_nr; - if (rb->use_material && mat1 != mat2) { + if (ld->conf.use_material && mat1 != mat2) { edge_flag_result |= LRT_EDGE_FLAG_MATERIAL; } } @@ -1643,11 +1655,11 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata, if (real_edges[i % 3] >= 0) { MEdge *medge = &me->medge[real_edges[i % 3]]; - if (rb->use_crease && rb->sharp_as_crease && (medge->flag & ME_SHARP)) { + if (ld->conf.use_crease && ld->conf.sharp_as_crease && (medge->flag & ME_SHARP)) { edge_flag_result |= LRT_EDGE_FLAG_CREASE; } - if (rb->use_edge_marks && e_feat_data->use_freestyle_edge) { + if (ld->conf.use_edge_marks && e_feat_data->use_freestyle_edge) { FreestyleEdge *fe; int index = e_feat_data->freestyle_edge_index; fe = &((FreestyleEdge *)me->edata.layers[index].data)[real_edges[i % 3]]; @@ -1663,7 +1675,7 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata, /* Only allocate for feature edge (instead of all edges) to save memory. * If allow duplicated edges, one edge gets added multiple times if it has multiple types. */ - reduce_data->feat_edges += e_feat_data->rb->allow_duplicated_types ? + reduce_data->feat_edges += e_feat_data->ld->conf.allow_duplicated_types ? lineart_edge_type_duplication_count(edge_flag_result) : 1; } @@ -1789,17 +1801,17 @@ static void lineart_finalize_object_edge_array(LineartPendingEdges *pe, LineartO } static void lineart_triangle_adjacent_assign(LineartTriangle *tri, - LineartTriangleAdjacent *ta, + LineartTriangleAdjacent *tri_adj, LineartEdge *e) { if (lineart_edge_match(tri, e, 0, 1)) { - ta->e[0] = e; + tri_adj->e[0] = e; } else if (lineart_edge_match(tri, e, 1, 2)) { - ta->e[1] = e; + tri_adj->e[1] = e; } else if (lineart_edge_match(tri, e, 2, 0)) { - ta->e[2] = e; + tri_adj->e[2] = e; } } @@ -1885,7 +1897,7 @@ static void lineart_edge_neighbor_init_task(void *__restrict userdata, adj_e->v1 = mloop[looptri->tri[i % 3]].v; adj_e->v2 = mloop[looptri->tri[(i + 1) % 3]].v; if (adj_e->v1 > adj_e->v2) { - SWAP(unsigned int, adj_e->v1, adj_e->v2); + SWAP(uint32_t, adj_e->v1, adj_e->v2); } edge_nabr->e = -1; @@ -1931,7 +1943,7 @@ static LineartEdgeNeighbor *lineart_build_edge_neighbor(Mesh *me, int total_edge return edge_nabr; } -static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRenderBuffer *re_buf) +static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartData *la_data) { LineartElementLinkNode *elem_link_node; LineartVert *la_v_arr; @@ -1965,19 +1977,20 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend /* If we allow duplicated edges, one edge should get added multiple times if is has been * classified as more than one edge type. This is so we can create multiple different line type * chains containing the same edge. */ - la_v_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool, + la_v_arr = lineart_mem_acquire_thread(&la_data->render_data_pool, sizeof(LineartVert) * me->totvert); - la_tri_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool, - tot_tri * re_buf->triangle_size); + la_tri_arr = lineart_mem_acquire_thread(&la_data->render_data_pool, + tot_tri * la_data->sizeof_triangle); Object *orig_ob = ob_info->original_ob; - BLI_spin_lock(&re_buf->lock_task); - elem_link_node = lineart_list_append_pointer_pool_sized_thread(&re_buf->vertex_buffer_pointers, - &re_buf->render_data_pool, - la_v_arr, - sizeof(LineartElementLinkNode)); - BLI_spin_unlock(&re_buf->lock_task); + BLI_spin_lock(&la_data->lock_task); + elem_link_node = lineart_list_append_pointer_pool_sized_thread( + &la_data->geom.vertex_buffer_pointers, + &la_data->render_data_pool, + la_v_arr, + sizeof(LineartElementLinkNode)); + BLI_spin_unlock(&la_data->lock_task); elem_link_node->element_count = me->totvert; elem_link_node->object_ref = orig_ob; @@ -1993,7 +2006,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend use_auto_smooth = true; } else { - crease_angle = re_buf->crease_threshold; + crease_angle = la_data->conf.crease_threshold; } /* FIXME(Yiming): Hack for getting clean 3D text, the seam that extruded text object creates @@ -2002,12 +2015,13 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend elem_link_node->flags |= LRT_ELEMENT_BORDER_ONLY; } - BLI_spin_lock(&re_buf->lock_task); - elem_link_node = lineart_list_append_pointer_pool_sized_thread(&re_buf->triangle_buffer_pointers, - &re_buf->render_data_pool, - la_tri_arr, - sizeof(LineartElementLinkNode)); - BLI_spin_unlock(&re_buf->lock_task); + BLI_spin_lock(&la_data->lock_task); + elem_link_node = lineart_list_append_pointer_pool_sized_thread( + &la_data->geom.triangle_buffer_pointers, + &la_data->render_data_pool, + la_tri_arr, + sizeof(LineartElementLinkNode)); + BLI_spin_unlock(&la_data->lock_task); int usage = ob_info->usage; @@ -2019,10 +2033,10 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend LineartTriangleAdjacent *tri_adj = MEM_callocN(sizeof(LineartTriangleAdjacent) * tot_tri, "LineartTriangleAdjacent"); /* Link is minimal so we use pool anyway. */ - BLI_spin_lock(&re_buf->lock_task); + BLI_spin_lock(&la_data->lock_task); lineart_list_append_pointer_pool_thread( - &re_buf->triangle_adjacent_pointers, &re_buf->render_data_pool, tri_adj); - BLI_spin_unlock(&re_buf->lock_task); + &la_data->geom.triangle_adjacent_pointers, &la_data->render_data_pool, tri_adj); + BLI_spin_unlock(&la_data->lock_task); /* Convert all vertices to lineart verts. */ TaskParallelSettings vert_settings; @@ -2051,10 +2065,10 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend tri_data.mlooptri = mlooptri; tri_data.vert_arr = la_v_arr; tri_data.tri_arr = la_tri_arr; - tri_data.lineart_triangle_size = re_buf->triangle_size; + tri_data.lineart_triangle_size = la_data->sizeof_triangle; tri_data.tri_adj = tri_adj; - unsigned int total_edges = tot_tri * 3; + uint32_t total_edges = tot_tri * 3; BLI_task_parallel_range(0, tot_tri, &tri_data, lineart_load_tri_task, &tri_settings); @@ -2072,7 +2086,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend edge_feat_settings.func_reduce = feat_data_sum_reduce; EdgeFeatData edge_feat_data = {0}; - edge_feat_data.rb = re_buf; + edge_feat_data.ld = la_data; edge_feat_data.me = me; edge_feat_data.mlooptri = mlooptri; edge_feat_data.edge_nabr = lineart_build_edge_neighbor(me, total_edges); @@ -2098,7 +2112,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend &edge_feat_settings); LooseEdgeData loose_data = {0}; - if (re_buf->use_loose) { + if (la_data->conf.use_loose) { /* Only identifying floating edges at this point because other edges has been taken care of * inside #lineart_identify_mlooptri_feature_edges function. */ TaskParallelSettings edge_loose_settings; @@ -2114,16 +2128,17 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend int allocate_la_e = edge_reduce.feat_edges + loose_data.loose_count; - la_edge_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool, + la_edge_arr = lineart_mem_acquire_thread(&la_data->render_data_pool, sizeof(LineartEdge) * allocate_la_e); - la_seg_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool, + la_seg_arr = lineart_mem_acquire_thread(&la_data->render_data_pool, sizeof(LineartEdgeSegment) * allocate_la_e); - BLI_spin_lock(&re_buf->lock_task); - elem_link_node = lineart_list_append_pointer_pool_sized_thread(&re_buf->line_buffer_pointers, - &re_buf->render_data_pool, - la_edge_arr, - sizeof(LineartElementLinkNode)); - BLI_spin_unlock(&re_buf->lock_task); + BLI_spin_lock(&la_data->lock_task); + elem_link_node = lineart_list_append_pointer_pool_sized_thread( + &la_data->geom.line_buffer_pointers, + &la_data->render_data_pool, + la_edge_arr, + sizeof(LineartElementLinkNode)); + BLI_spin_unlock(&la_data->lock_task); elem_link_node->element_count = allocate_la_e; elem_link_node->object_ref = orig_ob; @@ -2148,8 +2163,8 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend LineartEdge *edge_added = NULL; /* See eLineartEdgeFlag for details. */ - for (int flag_bit = 0; flag_bit < LRT_EDGE_FLAG_TYPE_MAX_BITS; flag_bit++) { - char use_type = 1 << flag_bit; + for (int flag_bit = 0; flag_bit < LRT_MESH_EDGE_TYPES_COUNT; flag_bit++) { + int use_type = LRT_MESH_EDGE_TYPES[flag_bit]; if (!(use_type & edge_nabr->flags)) { continue; } @@ -2157,13 +2172,13 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend la_edge->v1 = &la_v_arr[edge_nabr->v1]; la_edge->v2 = &la_v_arr[edge_nabr->v2]; int findex = i / 3; - la_edge->t1 = lineart_triangle_from_index(re_buf, la_tri_arr, findex); + la_edge->t1 = lineart_triangle_from_index(la_data, la_tri_arr, findex); if (!edge_added) { lineart_triangle_adjacent_assign(la_edge->t1, &tri_adj[findex], la_edge); } if (edge_nabr->e != -1) { findex = edge_nabr->e / 3; - la_edge->t2 = lineart_triangle_from_index(re_buf, la_tri_arr, findex); + la_edge->t2 = lineart_triangle_from_index(la_data, la_tri_arr, findex); if (!edge_added) { lineart_triangle_adjacent_assign(la_edge->t2, &tri_adj[findex], la_edge); } @@ -2185,7 +2200,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend la_edge++; la_seg++; - if (!re_buf->allow_duplicated_types) { + if (!la_data->conf.allow_duplicated_types) { break; } } @@ -2219,10 +2234,7 @@ static void lineart_object_load_worker(TaskPool *__restrict UNUSED(pool), LineartObjectLoadTaskInfo *olti) { for (LineartObjectInfo *obi = olti->pending; obi; obi = obi->next) { - lineart_geometry_object_load(obi, olti->rb); - if (G.debug_value == 4000) { - printf("thread id: %d processed: %d\n", olti->thread_id, obi->original_me->totpoly); - } + lineart_geometry_object_load(obi, olti->ld); } } @@ -2300,7 +2312,7 @@ static void lineart_geometry_load_assign_thread(LineartObjectLoadTaskInfo *olti_ int this_face_count) { LineartObjectLoadTaskInfo *use_olti = olti_list; - long unsigned int min_face = use_olti->total_faces; + uint64_t min_face = use_olti->total_faces; for (int i = 0; i < thread_count; i++) { if (olti_list[i].total_faces < min_face) { min_face = olti_list[i].total_faces; @@ -2356,7 +2368,7 @@ static bool lineart_geometry_check_visible(double (*model_view_proj)[4], return true; } -static void lineart_object_load_single_instance(LineartRenderBuffer *rb, +static void lineart_object_load_single_instance(LineartData *ld, Depsgraph *depsgraph, Scene *scene, Object *ob, @@ -2366,7 +2378,7 @@ static void lineart_object_load_single_instance(LineartRenderBuffer *rb, LineartObjectLoadTaskInfo *olti, int thread_count) { - LineartObjectInfo *obi = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartObjectInfo)); + LineartObjectInfo *obi = lineart_mem_acquire(&ld->render_data_pool, sizeof(LineartObjectInfo)); obi->usage = lineart_usage_check(scene->master_collection, ob, is_render); obi->override_intersection_mask = lineart_intersection_mask_check(scene->master_collection, ob); Mesh *use_mesh; @@ -2377,8 +2389,8 @@ static void lineart_object_load_single_instance(LineartRenderBuffer *rb, /* Prepare the matrix used for transforming this specific object (instance). This has to be * done before mesh boundbox check because the function needs that. */ - mul_m4db_m4db_m4fl_uniq(obi->model_view_proj, rb->view_projection, use_mat); - mul_m4db_m4db_m4fl_uniq(obi->model_view, rb->view, use_mat); + mul_m4db_m4db_m4fl_uniq(obi->model_view_proj, ld->conf.view_projection, use_mat); + mul_m4db_m4db_m4fl_uniq(obi->model_view, ld->conf.view, use_mat); if (!ELEM(ob->type, OB_MESH, OB_MBALL, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) { return; @@ -2400,7 +2412,8 @@ static void lineart_object_load_single_instance(LineartRenderBuffer *rb, return; } - if (!lineart_geometry_check_visible(obi->model_view_proj, rb->shift_x, rb->shift_y, use_mesh)) { + if (!lineart_geometry_check_visible( + obi->model_view_proj, ld->conf.shift_x, ld->conf.shift_y, use_mesh)) { return; } @@ -2423,15 +2436,15 @@ static void lineart_main_load_geometries( Depsgraph *depsgraph, Scene *scene, Object *camera /* Still use camera arg for convenience. */, - LineartRenderBuffer *rb, + LineartData *ld, bool allow_duplicates) { double proj[4][4], view[4][4], result[4][4]; float inv[4][4]; Camera *cam = camera->data; float sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y); - int fit = BKE_camera_sensor_fit(cam->sensor_fit, rb->w, rb->h); - double asp = ((double)rb->w / (double)rb->h); + int fit = BKE_camera_sensor_fit(cam->sensor_fit, ld->w, ld->h); + double asp = ((double)ld->w / (double)ld->h); int bound_box_discard_count = 0; @@ -2442,7 +2455,7 @@ static void lineart_main_load_geometries( if (fit == CAMERA_SENSOR_FIT_HOR && asp < 1) { sensor /= asp; } - const double fov = focallength_to_fov(cam->lens / (1 + rb->overscan), sensor); + const double fov = focallength_to_fov(cam->lens / (1 + ld->conf.overscan), sensor); lineart_matrix_perspective_44d(proj, fov, asp, cam->clip_start, cam->clip_end); } else if (cam->type == CAM_ORTHO) { @@ -2450,29 +2463,27 @@ static void lineart_main_load_geometries( lineart_matrix_ortho_44d(proj, -w, w, -w / asp, w / asp, cam->clip_start, cam->clip_end); } - double t_start; - - if (G.debug_value == 4000) { - t_start = PIL_check_seconds_timer(); - } - - invert_m4_m4(inv, rb->cam_obmat); + invert_m4_m4(inv, ld->conf.cam_obmat); mul_m4db_m4db_m4fl_uniq(result, proj, inv); copy_m4_m4_db(proj, result); - copy_m4_m4_db(rb->view_projection, proj); + copy_m4_m4_db(ld->conf.view_projection, proj); unit_m4_db(view); - copy_m4_m4_db(rb->view, view); + copy_m4_m4_db(ld->conf.view, view); - BLI_listbase_clear(&rb->triangle_buffer_pointers); - BLI_listbase_clear(&rb->vertex_buffer_pointers); + BLI_listbase_clear(&ld->geom.triangle_buffer_pointers); + BLI_listbase_clear(&ld->geom.vertex_buffer_pointers); + + double t_start; + if (G.debug_value == 4000) { + t_start = PIL_check_seconds_timer(); + } - int thread_count = rb->thread_count; + int thread_count = ld->thread_count; - /* This memory is in render buffer memory pool. so we don't need to free those after loading. - */ + /* This memory is in render buffer memory pool. So we don't need to free those after loading. */ LineartObjectLoadTaskInfo *olti = lineart_mem_acquire( - &rb->render_data_pool, sizeof(LineartObjectLoadTaskInfo) * thread_count); + &ld->render_data_pool, sizeof(LineartObjectLoadTaskInfo) * thread_count); eEvaluationMode eval_mode = DEG_get_mode(depsgraph); bool is_render = eval_mode == DAG_EVAL_RENDER; @@ -2502,7 +2513,7 @@ static void lineart_main_load_geometries( if (BKE_object_visibility(eval_ob, eval_mode) & OB_VISIBLE_SELF) { lineart_object_load_single_instance( - rb, depsgraph, scene, eval_ob, eval_ob, eval_ob->obmat, is_render, olti, thread_count); + ld, depsgraph, scene, eval_ob, eval_ob, eval_ob->obmat, is_render, olti, thread_count); } } DEG_OBJECT_ITER_END; @@ -2513,7 +2524,7 @@ static void lineart_main_load_geometries( printf("thread count: %d\n", thread_count); } for (int i = 0; i < thread_count; i++) { - olti[i].rb = rb; + olti[i].ld = ld; olti[i].thread_id = i; BLI_task_pool_push(tp, (TaskRunFunction)lineart_object_load_worker, &olti[i], 0, NULL); } @@ -2533,7 +2544,7 @@ static void lineart_main_load_geometries( edge_count += obi->pending_edges.next; } } - lineart_finalize_object_edge_array_reserve(&rb->pending_edges, edge_count); + lineart_finalize_object_edge_array_reserve(&ld->pending_edges, edge_count); for (int i = 0; i < thread_count; i++) { for (LineartObjectInfo *obi = olti[i].pending; obi; obi = obi->next) { @@ -2551,7 +2562,7 @@ static void lineart_main_load_geometries( * same numeric index to come close together. */ obi->global_i_offset = global_i; global_i += v_count; - lineart_finalize_object_edge_array(&rb->pending_edges, obi); + lineart_finalize_object_edge_array(&ld->pending_edges, obi); } } @@ -2645,6 +2656,9 @@ static bool lineart_edge_from_triangle(const LineartTriangle *tri, (num > is[order[1]] ? order[1] : (num > is[order[0]] ? order[0] : -1))); \ } +#define LRT_ISEC(index) (index == 0 ? isec_e1 : (index == 1 ? isec_e2 : isec_e3)) +#define LRT_PARALLEL(index) (index == 0 ? para_e1 : (index == 1 ? para_e2 : para_e3)) + /** * This is the main function to calculate * the occlusion status between 1(one) triangle and 1(one) line. @@ -2658,7 +2672,7 @@ static bool lineart_edge_from_triangle(const LineartTriangle *tri, * extruding from one of the triangle's point. To get the information using one math process can * solve this problem. * - * 2) Currently using discrete a/b/c/pa/pb/pc/is[3] values for storing + * 2) Currently using discrete a/b/c/para_e1/para_e2/para_e3/is[3] values for storing * intersection/edge_aligned/intersection_order info, which isn't optimal, needs a better * representation (likely a struct) for readability and clarity of code path. * @@ -2680,18 +2694,20 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), double *from, double *to) { - double is[3] = {0}; - int order[3]; - int LCross = -1, RCross = -1; - int a, b, c; /* Crossing info. */ - bool pa, pb, pc; /* Parallel info. */ - int st_l = 0, st_r = 0; - - double Lv[3]; - double Rv[3]; - double vd4[4]; - double Cv[3]; - double dot_l, dot_r, dot_la, dot_ra; + double cross_ratios[3] = {0}; + int cross_order[3]; + int cross_v1 = -1, cross_v2 = -1; + /* If the edge intersects with the triangle edges (including extensions). */ + int isec_e1, isec_e2, isec_e3; + /* If edge is parallel to one of the edges in the triangle. */ + bool para_e1, para_e2, para_e3; + enum LineartPointTri state_v1 = 0, state_v2 = 0; + + double dir_v1[3]; + double dir_v2[3]; + double view_vector[4]; + double dir_cam[3]; + double dot_v1, dot_v2, dot_v1a, dot_v2a; double dot_f; double gloc[4], trans[4]; double cut = -1; @@ -2714,31 +2730,25 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), } /* Check if the line visually crosses one of the edge in the triangle. */ - a = lineart_intersect_seg_seg(LFBC, RFBC, FBC0, FBC1, &is[0], &pa); - b = lineart_intersect_seg_seg(LFBC, RFBC, FBC1, FBC2, &is[1], &pb); - c = lineart_intersect_seg_seg(LFBC, RFBC, FBC2, FBC0, &is[2], &pc); + isec_e1 = lineart_intersect_seg_seg(LFBC, RFBC, FBC0, FBC1, &cross_ratios[0], ¶_e1); + isec_e2 = lineart_intersect_seg_seg(LFBC, RFBC, FBC1, FBC2, &cross_ratios[1], ¶_e2); + isec_e3 = lineart_intersect_seg_seg(LFBC, RFBC, FBC2, FBC0, &cross_ratios[2], ¶_e3); /* Sort the intersection distance. */ - INTERSECT_SORT_MIN_TO_MAX_3(is[0], is[1], is[2], order); - - sub_v3_v3v3_db(Lv, e->v1->gloc, tri->v[0]->gloc); - sub_v3_v3v3_db(Rv, e->v2->gloc, tri->v[0]->gloc); + INTERSECT_SORT_MIN_TO_MAX_3(cross_ratios[0], cross_ratios[1], cross_ratios[2], cross_order); - copy_v3_v3_db(Cv, camera_dir); + sub_v3_v3v3_db(dir_v1, e->v1->gloc, tri->v[0]->gloc); + sub_v3_v3v3_db(dir_v2, e->v2->gloc, tri->v[0]->gloc); + copy_v3_v3_db(dir_cam, camera_dir); + copy_v3_v3_db(view_vector, override_camera_loc); if (override_cam_is_persp) { - copy_v3_v3_db(vd4, override_camera_loc); - } - else { - copy_v4_v4_db(vd4, override_camera_loc); - } - if (override_cam_is_persp) { - sub_v3_v3v3_db(Cv, vd4, tri->v[0]->gloc); + sub_v3_v3v3_db(dir_cam, view_vector, tri->v[0]->gloc); } - dot_l = dot_v3v3_db(Lv, tri->gn); - dot_r = dot_v3v3_db(Rv, tri->gn); - dot_f = dot_v3v3_db(Cv, tri->gn); + dot_v1 = dot_v3v3_db(dir_v1, tri->gn); + dot_v2 = dot_v3v3_db(dir_v2, tri->gn); + dot_f = dot_v3v3_db(dir_cam, tri->gn); /* NOTE(Yiming): When we don't use `dot_f==0` here, it's theoretically possible that _some_ * faces in perspective mode would get erroneously caught in this condition where they really @@ -2749,40 +2759,39 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), return false; } + /* Whether two end points are inside/on_the_edge/outside of the triangle. */ + state_v1 = lineart_point_triangle_relation(LFBC, FBC0, FBC1, FBC2); + state_v2 = lineart_point_triangle_relation(RFBC, FBC0, FBC1, FBC2); + /* If the edge doesn't visually cross any edge of the triangle... */ - if (!a && !b && !c) { + if (!isec_e1 && !isec_e2 && !isec_e3) { /* And if both end point from the edge is outside of the triangle... */ - if (!(st_l = lineart_point_triangle_relation(LFBC, FBC0, FBC1, FBC2)) && - !(st_r = lineart_point_triangle_relation(RFBC, FBC0, FBC1, FBC2))) { + if ((!state_v1) && (!state_v2)) { return 0; /* We don't have any occlusion. */ } } - /* Whether two end points are inside/on_the_edge/outside of the triangle. */ - st_l = lineart_point_triangle_relation(LFBC, FBC0, FBC1, FBC2); - st_r = lineart_point_triangle_relation(RFBC, FBC0, FBC1, FBC2); - /* Determine the cut position. */ - dot_la = fabs(dot_l); - if (dot_la < DBL_EPSILON) { - dot_la = 0; - dot_l = 0; + dot_v1a = fabs(dot_v1); + if (dot_v1a < DBL_EPSILON) { + dot_v1a = 0; + dot_v1 = 0; } - dot_ra = fabs(dot_r); - if (dot_ra < DBL_EPSILON) { - dot_ra = 0; - dot_r = 0; + dot_v2a = fabs(dot_v2); + if (dot_v2a < DBL_EPSILON) { + dot_v2a = 0; + dot_v2 = 0; } - if (dot_l - dot_r == 0) { + if (dot_v1 - dot_v2 == 0) { cut = 100000; } - else if (dot_l * dot_r <= 0) { - cut = dot_la / fabs(dot_l - dot_r); + else if (dot_v1 * dot_v2 <= 0) { + cut = dot_v1a / fabs(dot_v1 - dot_v2); } else { - cut = fabs(dot_r + dot_l) / fabs(dot_l - dot_r); - cut = dot_ra > dot_la ? 1 - cut : cut; + cut = fabs(dot_v2 + dot_v1) / fabs(dot_v1 - dot_v2); + cut = dot_v2a > dot_v1a ? 1 - cut : cut; } /* Transform the cut from geometry space to image space. */ @@ -2803,7 +2812,7 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), } #define LRT_GUARD_NOT_FOUND \ - if (LCross < 0 || RCross < 0) { \ + if (cross_v1 < 0 || cross_v2 < 0) { \ return false; \ } @@ -2811,95 +2820,97 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), * indicates triangle boundary. DBL_TRIANGLE_LIM is needed to for floating point precision * tolerance. */ - if (st_l == 2) { + if (state_v1 == LRT_INSIDE_TRIANGLE) { /* Left side is in the triangle. */ - if (st_r == 2) { + if (state_v2 == LRT_INSIDE_TRIANGLE) { /* | l---r | */ - INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross); - INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, RCross); + INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1); + INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2); } - else if (st_r == 1) { + else if (state_v2 == LRT_ON_TRIANGLE) { /* | l------r| */ - INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross); - INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, RCross); + INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1); + INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2); } - else if (st_r == 0) { + else if (state_v2 == LRT_OUTSIDE_TRIANGLE) { /* | l-------|------r */ - INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross); - INTERSECT_JUST_GREATER(is, order, 0, RCross); + INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1); + INTERSECT_JUST_GREATER(cross_ratios, cross_order, 0, cross_v2); } } - else if (st_l == 1) { + else if (state_v1 == LRT_ON_TRIANGLE) { /* Left side is on some edge of the triangle. */ - if (st_r == 2) { + if (state_v2 == LRT_INSIDE_TRIANGLE) { /* |l------r | */ - INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross); - INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, RCross); + INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1); + INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2); } - else if (st_r == 1) { + else if (state_v2 == LRT_ON_TRIANGLE) { /* |l---------r| */ - INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross); - INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, RCross); + INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1); + INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2); } - else if (st_r == 0) { + else if (state_v2 == LRT_OUTSIDE_TRIANGLE) { /* |l----------|-------r (crossing the triangle) [OR] * r---------|l | (not crossing the triangle) */ - INTERSECT_JUST_GREATER(is, order, DBL_TRIANGLE_LIM, RCross); - if (RCross >= 0 && LRT_ABC(RCross) && is[RCross] > (DBL_TRIANGLE_LIM)) { - INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross); + INTERSECT_JUST_GREATER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v2); + if (cross_v2 >= 0 && LRT_ISEC(cross_v2) && cross_ratios[cross_v2] > (DBL_TRIANGLE_LIM)) { + INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1); } else { - INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, RCross); - if (RCross > 0) { - INTERSECT_JUST_SMALLER(is, order, is[RCross], LCross); + INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v2); + if (cross_v2 > 0) { + INTERSECT_JUST_SMALLER(cross_ratios, cross_order, cross_ratios[cross_v2], cross_v1); } } LRT_GUARD_NOT_FOUND /* We could have the edge being completely parallel to the triangle where there isn't a * viable occlusion result. */ - if ((LRT_PABC(LCross) && !LRT_ABC(LCross)) || (LRT_PABC(RCross) && !LRT_ABC(RCross))) { + if ((LRT_PARALLEL(cross_v1) && !LRT_ISEC(cross_v1)) || + (LRT_PARALLEL(cross_v2) && !LRT_ISEC(cross_v2))) { return false; } } } - else if (st_l == 0) { + else if (state_v1 == LRT_OUTSIDE_TRIANGLE) { /* Left side is outside of the triangle. */ - if (st_r == 2) { + if (state_v2 == LRT_INSIDE_TRIANGLE) { /* l---|---r | */ - INTERSECT_JUST_SMALLER(is, order, 1 - DBL_TRIANGLE_LIM, LCross); - INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, RCross); + INTERSECT_JUST_SMALLER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v1); + INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2); } - else if (st_r == 1) { + else if (state_v2 == LRT_ON_TRIANGLE) { /* |r----------|-------l (crossing the triangle) [OR] * l---------|r | (not crossing the triangle) */ - INTERSECT_JUST_SMALLER(is, order, 1 - DBL_TRIANGLE_LIM, LCross); - if (LCross >= 0 && LRT_ABC(LCross) && is[LCross] < (1 - DBL_TRIANGLE_LIM)) { - INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, RCross); + INTERSECT_JUST_SMALLER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v1); + if (cross_v1 >= 0 && LRT_ISEC(cross_v1) && cross_ratios[cross_v1] < (1 - DBL_TRIANGLE_LIM)) { + INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2); } else { - INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, LCross); - if (LCross > 0) { - INTERSECT_JUST_GREATER(is, order, is[LCross], RCross); + INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v1); + if (cross_v1 > 0) { + INTERSECT_JUST_GREATER(cross_ratios, cross_order, cross_ratios[cross_v1], cross_v2); } } LRT_GUARD_NOT_FOUND /* The same logic applies as above case. */ - if ((LRT_PABC(LCross) && !LRT_ABC(LCross)) || (LRT_PABC(RCross) && !LRT_ABC(RCross))) { + if ((LRT_PARALLEL(cross_v1) && !LRT_ISEC(cross_v1)) || + (LRT_PARALLEL(cross_v2) && !LRT_ISEC(cross_v2))) { return false; } } - else if (st_r == 0) { + else if (state_v2 == LRT_OUTSIDE_TRIANGLE) { /* l---|----|----r (crossing the triangle) [OR] * l----r | | (not crossing the triangle) */ - INTERSECT_JUST_GREATER(is, order, -DBL_TRIANGLE_LIM, LCross); - if (LCross >= 0 && LRT_ABC(LCross)) { - INTERSECT_JUST_GREATER(is, order, is[LCross], RCross); + INTERSECT_JUST_GREATER(cross_ratios, cross_order, -DBL_TRIANGLE_LIM, cross_v1); + if (cross_v1 >= 0 && LRT_ISEC(cross_v1)) { + INTERSECT_JUST_GREATER(cross_ratios, cross_order, cross_ratios[cross_v1], cross_v2); } else { - if (LCross >= 0) { - INTERSECT_JUST_GREATER(is, order, is[LCross], LCross); - if (LCross >= 0) { - INTERSECT_JUST_GREATER(is, order, is[LCross], RCross); + if (cross_v1 >= 0) { + INTERSECT_JUST_GREATER(cross_ratios, cross_order, cross_ratios[cross_v1], cross_v1); + if (cross_v1 >= 0) { + INTERSECT_JUST_GREATER(cross_ratios, cross_order, cross_ratios[cross_v1], cross_v2); } } } @@ -2908,28 +2919,28 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), LRT_GUARD_NOT_FOUND - double LF = dot_l * dot_f, RF = dot_r * dot_f; + double dot_1f = dot_v1 * dot_f, dot_2f = dot_v2 * dot_f; /* Determine the start and end point of image space cut on a line. */ - if (LF <= 0 && RF <= 0 && (dot_l || dot_r)) { - *from = MAX2(0, is[LCross]); - *to = MIN2(1, is[RCross]); + if (dot_1f <= 0 && dot_2f <= 0 && (dot_v1 || dot_v2)) { + *from = MAX2(0, cross_ratios[cross_v1]); + *to = MIN2(1, cross_ratios[cross_v2]); if (*from >= *to) { return false; } return true; } - if (LF >= 0 && RF <= 0 && (dot_l || dot_r)) { - *from = MAX2(cut, is[LCross]); - *to = MIN2(1, is[RCross]); + if (dot_1f >= 0 && dot_2f <= 0 && (dot_v1 || dot_v2)) { + *from = MAX2(cut, cross_ratios[cross_v1]); + *to = MIN2(1, cross_ratios[cross_v2]); if (*from >= *to) { return false; } return true; } - if (LF <= 0 && RF >= 0 && (dot_l || dot_r)) { - *from = MAX2(0, is[LCross]); - *to = MIN2(cut, is[RCross]); + if (dot_1f <= 0 && dot_2f >= 0 && (dot_v1 || dot_v2)) { + *from = MAX2(0, cross_ratios[cross_v1]); + *to = MIN2(cut, cross_ratios[cross_v2]); if (*from >= *to) { return false; } @@ -2943,6 +2954,8 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), #undef INTERSECT_SORT_MIN_TO_MAX_3 #undef INTERSECT_JUST_GREATER #undef INTERSECT_JUST_SMALLER +#undef LRT_ISEC +#undef LRT_PARALLEL /** * At this stage of the computation we don't have triangle adjacent info anymore, @@ -3025,37 +3038,37 @@ static LineartVert *lineart_triangle_share_point(const LineartTriangle *l, } static bool lineart_triangle_2v_intersection_math( - LineartVert *v1, LineartVert *v2, LineartTriangle *t2, double *last, double *rv) + LineartVert *v1, LineartVert *v2, LineartTriangle *tri, double *last, double *rv) { - double Lv[3]; - double Rv[3]; - double dot_l, dot_r; + /* Direction vectors for the edge verts. We will check if the verts are on the same side of the + * triangle or not. */ + double dir_v1[3], dir_v2[3]; + double dot_v1, dot_v2; double gloc[3]; - LineartVert *l = v1, *r = v2; - sub_v3_v3v3_db(Lv, l->gloc, t2->v[0]->gloc); - sub_v3_v3v3_db(Rv, r->gloc, t2->v[0]->gloc); + sub_v3_v3v3_db(dir_v1, v1->gloc, tri->v[0]->gloc); + sub_v3_v3v3_db(dir_v2, v2->gloc, tri->v[0]->gloc); - dot_l = dot_v3v3_db(Lv, t2->gn); - dot_r = dot_v3v3_db(Rv, t2->gn); + dot_v1 = dot_v3v3_db(dir_v1, tri->gn); + dot_v2 = dot_v3v3_db(dir_v2, tri->gn); - if (dot_l * dot_r > 0 || (!dot_l && !dot_r)) { + if (dot_v1 * dot_v2 > 0 || (!dot_v1 && !dot_v2)) { return false; } - dot_l = fabs(dot_l); - dot_r = fabs(dot_r); + dot_v1 = fabs(dot_v1); + dot_v2 = fabs(dot_v2); - interp_v3_v3v3_db(gloc, l->gloc, r->gloc, dot_l / (dot_l + dot_r)); + interp_v3_v3v3_db(gloc, v1->gloc, v2->gloc, dot_v1 / (dot_v1 + dot_v2)); - /* Due to precision issue, we might end up with the same point as the one we already detected. - */ + /* Due to precision issue, we might end up with the same point as the one we already detected. */ if (last && LRT_DOUBLE_CLOSE_ENOUGH(last[0], gloc[0]) && LRT_DOUBLE_CLOSE_ENOUGH(last[1], gloc[1]) && LRT_DOUBLE_CLOSE_ENOUGH(last[2], gloc[2])) { return false; } - if (!(lineart_point_inside_triangle3d(gloc, t2->v[0]->gloc, t2->v[1]->gloc, t2->v[2]->gloc))) { + if (!(lineart_point_inside_triangle3d( + gloc, tri->v[0]->gloc, tri->v[1]->gloc, tri->v[2]->gloc))) { return false; } @@ -3152,11 +3165,11 @@ static void lineart_add_isec_thread(LineartIsecThread *th, MEM_freeN(th->array); th->array = new_array; } - LineartIsecSingle *is = &th->array[th->current]; - copy_v3fl_v3db(is->v1, v1); - copy_v3fl_v3db(is->v2, v2); - is->tri1 = tri1; - is->tri2 = tri2; + LineartIsecSingle *isec_single = &th->array[th->current]; + copy_v3fl_v3db(isec_single->v1, v1); + copy_v3fl_v3db(isec_single->v2, v2); + isec_single->tri1 = tri1; + isec_single->tri2 = tri2; th->current++; } @@ -3164,38 +3177,38 @@ static void lineart_add_isec_thread(LineartIsecThread *th, static bool lineart_schedule_new_triangle_task(LineartIsecThread *th) { - LineartRenderBuffer *rb = th->rb; + LineartData *ld = th->ld; int remaining = LRT_ISECT_TRIANGLE_PER_THREAD; - BLI_spin_lock(&rb->lock_task); - LineartElementLinkNode *eln = rb->isect_scheduled_up_to; + BLI_spin_lock(&ld->lock_task); + LineartElementLinkNode *eln = ld->isect_scheduled_up_to; if (!eln) { - BLI_spin_unlock(&rb->lock_task); + BLI_spin_unlock(&ld->lock_task); return false; } th->pending_from = eln; - th->index_from = rb->isect_scheduled_up_to_index; + th->index_from = ld->isect_scheduled_up_to_index; while (remaining > 0 && eln) { - int remaining_this_eln = eln->element_count - rb->isect_scheduled_up_to_index; + int remaining_this_eln = eln->element_count - ld->isect_scheduled_up_to_index; int added_count = MIN2(remaining, remaining_this_eln); remaining -= added_count; if (remaining || added_count == remaining_this_eln) { eln = eln->next; - rb->isect_scheduled_up_to = eln; - rb->isect_scheduled_up_to_index = 0; + ld->isect_scheduled_up_to = eln; + ld->isect_scheduled_up_to_index = 0; } else { - rb->isect_scheduled_up_to_index += added_count; + ld->isect_scheduled_up_to_index += added_count; } } - th->pending_to = eln ? eln : rb->triangle_buffer_pointers.last; - th->index_to = rb->isect_scheduled_up_to_index; + th->pending_to = eln ? eln : ld->geom.triangle_buffer_pointers.last; + th->index_to = ld->isect_scheduled_up_to_index; - BLI_spin_unlock(&rb->lock_task); + BLI_spin_unlock(&ld->lock_task); return true; } @@ -3205,14 +3218,14 @@ static bool lineart_schedule_new_triangle_task(LineartIsecThread *th) * 2) Per-thread intersection result array. Does not store actual #LineartEdge, these results will * be finalized by #lineart_create_edges_from_isec_data */ -static void lineart_init_isec_thread(LineartIsecData *d, LineartRenderBuffer *rb, int thread_count) +static void lineart_init_isec_thread(LineartIsecData *d, LineartData *ld, int thread_count) { d->threads = MEM_callocN(sizeof(LineartIsecThread) * thread_count, "LineartIsecThread arr"); - d->rb = rb; + d->ld = ld; d->thread_count = thread_count; - rb->isect_scheduled_up_to = rb->triangle_buffer_pointers.first; - rb->isect_scheduled_up_to_index = 0; + ld->isect_scheduled_up_to = ld->geom.triangle_buffer_pointers.first; + ld->isect_scheduled_up_to_index = 0; for (int i = 0; i < thread_count; i++) { LineartIsecThread *it = &d->threads[i]; @@ -3220,7 +3233,7 @@ static void lineart_init_isec_thread(LineartIsecData *d, LineartRenderBuffer *rb it->max = 100; it->current = 0; it->thread_id = i; - it->rb = rb; + it->ld = ld; } } @@ -3290,14 +3303,14 @@ static void lineart_triangle_intersect_in_bounding_area(LineartTriangle *tri, /** * The calculated view vector will point towards the far-plane from the camera position. */ -static void lineart_main_get_view_vector(LineartRenderBuffer *rb) +static void lineart_main_get_view_vector(LineartData *ld) { float direction[3] = {0, 0, 1}; float trans[3]; float inv[4][4]; float obmat_no_scale[4][4]; - copy_m4_m4(obmat_no_scale, rb->cam_obmat); + copy_m4_m4(obmat_no_scale, ld->conf.cam_obmat); normalize_v3(obmat_no_scale[0]); normalize_v3(obmat_no_scale[1]); @@ -3305,45 +3318,45 @@ static void lineart_main_get_view_vector(LineartRenderBuffer *rb) invert_m4_m4(inv, obmat_no_scale); transpose_m4(inv); mul_v3_mat3_m4v3(trans, inv, direction); - copy_m4_m4(rb->cam_obmat, obmat_no_scale); - copy_v3db_v3fl(rb->view_vector, trans); + copy_m4_m4(ld->conf.cam_obmat, obmat_no_scale); + copy_v3db_v3fl(ld->conf.view_vector, trans); } -static void lineart_destroy_render_data(LineartRenderBuffer *rb) +static void lineart_destroy_render_data(LineartData *ld) { - if (rb == NULL) { + if (ld == NULL) { return; } - BLI_listbase_clear(&rb->chains); - BLI_listbase_clear(&rb->wasted_cuts); + BLI_listbase_clear(&ld->chains); + BLI_listbase_clear(&ld->wasted_cuts); - BLI_listbase_clear(&rb->vertex_buffer_pointers); - BLI_listbase_clear(&rb->line_buffer_pointers); - BLI_listbase_clear(&rb->triangle_buffer_pointers); + BLI_listbase_clear(&ld->geom.vertex_buffer_pointers); + BLI_listbase_clear(&ld->geom.line_buffer_pointers); + BLI_listbase_clear(&ld->geom.triangle_buffer_pointers); - BLI_spin_end(&rb->lock_task); - BLI_spin_end(&rb->lock_cuts); - BLI_spin_end(&rb->render_data_pool.lock_mem); + BLI_spin_end(&ld->lock_task); + BLI_spin_end(&ld->lock_cuts); + BLI_spin_end(&ld->render_data_pool.lock_mem); - if (rb->pending_edges.array) { - MEM_freeN(rb->pending_edges.array); + if (ld->pending_edges.array) { + MEM_freeN(ld->pending_edges.array); } - lineart_free_bounding_area_memories(rb); + lineart_free_bounding_area_memories(ld); - lineart_mem_destroy(&rb->render_data_pool); + lineart_mem_destroy(&ld->render_data_pool); } void MOD_lineart_destroy_render_data(LineartGpencilModifierData *lmd) { - LineartRenderBuffer *rb = lmd->render_buffer_ptr; + LineartData *ld = lmd->la_data_ptr; - lineart_destroy_render_data(rb); + lineart_destroy_render_data(ld); - if (rb) { - MEM_freeN(rb); - lmd->render_buffer_ptr = NULL; + if (ld) { + MEM_freeN(ld); + lmd->la_data_ptr = NULL; } if (G.debug_value == 4000) { @@ -3367,16 +3380,16 @@ void MOD_lineart_clear_cache(struct LineartCache **lc) (*lc) = NULL; } -static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, - LineartGpencilModifierData *lmd, - Object *camera, - Object *active_camera, - LineartCache *lc) +static LineartData *lineart_create_render_buffer(Scene *scene, + LineartGpencilModifierData *lmd, + Object *camera, + Object *active_camera, + LineartCache *lc) { - LineartRenderBuffer *rb = MEM_callocN(sizeof(LineartRenderBuffer), "Line Art render buffer"); + LineartData *ld = MEM_callocN(sizeof(LineartData), "Line Art render buffer"); lmd->cache = lc; - lmd->render_buffer_ptr = rb; + lmd->la_data_ptr = ld; lc->rb_edge_types = lmd->edge_types_override; if (!scene || !camera || !lc) { @@ -3390,97 +3403,98 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, clipping_offset = 0.0001; } - copy_v3db_v3fl(rb->camera_pos, camera->obmat[3]); + copy_v3db_v3fl(ld->conf.camera_pos, camera->obmat[3]); if (active_camera) { - copy_v3db_v3fl(rb->active_camera_pos, active_camera->obmat[3]); + copy_v3db_v3fl(ld->conf.active_camera_pos, active_camera->obmat[3]); } - copy_m4_m4(rb->cam_obmat, camera->obmat); - rb->cam_is_persp = (c->type == CAM_PERSP); - rb->near_clip = c->clip_start + clipping_offset; - rb->far_clip = c->clip_end - clipping_offset; - rb->w = scene->r.xsch; - rb->h = scene->r.ysch; + copy_m4_m4(ld->conf.cam_obmat, camera->obmat); + + ld->conf.cam_is_persp = (c->type == CAM_PERSP); + ld->conf.near_clip = c->clip_start + clipping_offset; + ld->conf.far_clip = c->clip_end - clipping_offset; + ld->w = scene->r.xsch; + ld->h = scene->r.ysch; - if (rb->cam_is_persp) { - rb->tile_recursive_level = LRT_TILE_RECURSIVE_PERSPECTIVE; + if (ld->conf.cam_is_persp) { + ld->qtree.recursive_level = LRT_TILE_RECURSIVE_PERSPECTIVE; } else { - rb->tile_recursive_level = LRT_TILE_RECURSIVE_ORTHO; + ld->qtree.recursive_level = LRT_TILE_RECURSIVE_ORTHO; } - double asp = ((double)rb->w / (double)rb->h); - int fit = BKE_camera_sensor_fit(c->sensor_fit, rb->w, rb->h); - rb->shift_x = fit == CAMERA_SENSOR_FIT_HOR ? c->shiftx : c->shiftx / asp; - rb->shift_y = fit == CAMERA_SENSOR_FIT_VERT ? c->shifty : c->shifty * asp; + double asp = ((double)ld->w / (double)ld->h); + int fit = BKE_camera_sensor_fit(c->sensor_fit, ld->w, ld->h); + ld->conf.shift_x = fit == CAMERA_SENSOR_FIT_HOR ? c->shiftx : c->shiftx / asp; + ld->conf.shift_y = fit == CAMERA_SENSOR_FIT_VERT ? c->shifty : c->shifty * asp; - rb->overscan = lmd->overscan; + ld->conf.overscan = lmd->overscan; - rb->shift_x /= (1 + rb->overscan); - rb->shift_y /= (1 + rb->overscan); + ld->conf.shift_x /= (1 + ld->conf.overscan); + ld->conf.shift_y /= (1 + ld->conf.overscan); - rb->crease_threshold = cos(M_PI - lmd->crease_threshold); - rb->chaining_image_threshold = lmd->chaining_image_threshold; - rb->angle_splitting_threshold = lmd->angle_splitting_threshold; - rb->chain_smooth_tolerance = lmd->chain_smooth_tolerance; + ld->conf.crease_threshold = cos(M_PI - lmd->crease_threshold); + ld->conf.chaining_image_threshold = lmd->chaining_image_threshold; + ld->conf.angle_splitting_threshold = lmd->angle_splitting_threshold; + ld->conf.chain_smooth_tolerance = lmd->chain_smooth_tolerance; - rb->fuzzy_intersections = (lmd->calculation_flags & LRT_INTERSECTION_AS_CONTOUR) != 0; - rb->fuzzy_everything = (lmd->calculation_flags & LRT_EVERYTHING_AS_CONTOUR) != 0; - rb->allow_boundaries = (lmd->calculation_flags & LRT_ALLOW_CLIPPING_BOUNDARIES) != 0; - rb->use_loose_as_contour = (lmd->calculation_flags & LRT_LOOSE_AS_CONTOUR) != 0; - rb->use_loose_edge_chain = (lmd->calculation_flags & LRT_CHAIN_LOOSE_EDGES) != 0; - rb->use_geometry_space_chain = (lmd->calculation_flags & LRT_CHAIN_GEOMETRY_SPACE) != 0; - rb->use_image_boundary_trimming = (lmd->calculation_flags & LRT_USE_IMAGE_BOUNDARY_TRIMMING) != - 0; + ld->conf.fuzzy_intersections = (lmd->calculation_flags & LRT_INTERSECTION_AS_CONTOUR) != 0; + ld->conf.fuzzy_everything = (lmd->calculation_flags & LRT_EVERYTHING_AS_CONTOUR) != 0; + ld->conf.allow_boundaries = (lmd->calculation_flags & LRT_ALLOW_CLIPPING_BOUNDARIES) != 0; + ld->conf.use_loose_as_contour = (lmd->calculation_flags & LRT_LOOSE_AS_CONTOUR) != 0; + ld->conf.use_loose_edge_chain = (lmd->calculation_flags & LRT_CHAIN_LOOSE_EDGES) != 0; + ld->conf.use_geometry_space_chain = (lmd->calculation_flags & LRT_CHAIN_GEOMETRY_SPACE) != 0; + ld->conf.use_image_boundary_trimming = (lmd->calculation_flags & + LRT_USE_IMAGE_BOUNDARY_TRIMMING) != 0; /* See lineart_edge_from_triangle() for how this option may impact performance. */ - rb->allow_overlapping_edges = (lmd->calculation_flags & LRT_ALLOW_OVERLAPPING_EDGES) != 0; + ld->conf.allow_overlapping_edges = (lmd->calculation_flags & LRT_ALLOW_OVERLAPPING_EDGES) != 0; - rb->allow_duplicated_types = (lmd->calculation_flags & LRT_ALLOW_OVERLAP_EDGE_TYPES) != 0; + ld->conf.allow_duplicated_types = (lmd->calculation_flags & LRT_ALLOW_OVERLAP_EDGE_TYPES) != 0; - rb->force_crease = (lmd->calculation_flags & LRT_USE_CREASE_ON_SMOOTH_SURFACES) != 0; - rb->sharp_as_crease = (lmd->calculation_flags & LRT_USE_CREASE_ON_SHARP_EDGES) != 0; + ld->conf.force_crease = (lmd->calculation_flags & LRT_USE_CREASE_ON_SMOOTH_SURFACES) != 0; + ld->conf.sharp_as_crease = (lmd->calculation_flags & LRT_USE_CREASE_ON_SHARP_EDGES) != 0; - rb->chain_preserve_details = (lmd->calculation_flags & LRT_CHAIN_PRESERVE_DETAILS) != 0; + ld->conf.chain_preserve_details = (lmd->calculation_flags & LRT_CHAIN_PRESERVE_DETAILS) != 0; /* This is used to limit calculation to a certain level to save time, lines who have higher * occlusion levels will get ignored. */ - rb->max_occlusion_level = lmd->level_end_override; + ld->conf.max_occlusion_level = lmd->level_end_override; - rb->use_back_face_culling = (lmd->calculation_flags & LRT_USE_BACK_FACE_CULLING) != 0; + ld->conf.use_back_face_culling = (lmd->calculation_flags & LRT_USE_BACK_FACE_CULLING) != 0; int16_t edge_types = lmd->edge_types_override; - rb->use_contour = (edge_types & LRT_EDGE_FLAG_CONTOUR) != 0; - rb->use_crease = (edge_types & LRT_EDGE_FLAG_CREASE) != 0; - rb->use_material = (edge_types & LRT_EDGE_FLAG_MATERIAL) != 0; - rb->use_edge_marks = (edge_types & LRT_EDGE_FLAG_EDGE_MARK) != 0; - rb->use_intersections = (edge_types & LRT_EDGE_FLAG_INTERSECTION) != 0; - rb->use_loose = (edge_types & LRT_EDGE_FLAG_LOOSE) != 0; + ld->conf.use_contour = (edge_types & LRT_EDGE_FLAG_CONTOUR) != 0; + ld->conf.use_crease = (edge_types & LRT_EDGE_FLAG_CREASE) != 0; + ld->conf.use_material = (edge_types & LRT_EDGE_FLAG_MATERIAL) != 0; + ld->conf.use_edge_marks = (edge_types & LRT_EDGE_FLAG_EDGE_MARK) != 0; + ld->conf.use_intersections = (edge_types & LRT_EDGE_FLAG_INTERSECTION) != 0; + ld->conf.use_loose = (edge_types & LRT_EDGE_FLAG_LOOSE) != 0; - rb->filter_face_mark_invert = (lmd->calculation_flags & LRT_FILTER_FACE_MARK_INVERT) != 0; - rb->filter_face_mark = (lmd->calculation_flags & LRT_FILTER_FACE_MARK) != 0; - rb->filter_face_mark_boundaries = (lmd->calculation_flags & LRT_FILTER_FACE_MARK_BOUNDARIES) != - 0; - rb->filter_face_mark_keep_contour = (lmd->calculation_flags & - LRT_FILTER_FACE_MARK_KEEP_CONTOUR) != 0; + ld->conf.filter_face_mark_invert = (lmd->calculation_flags & LRT_FILTER_FACE_MARK_INVERT) != 0; + ld->conf.filter_face_mark = (lmd->calculation_flags & LRT_FILTER_FACE_MARK) != 0; + ld->conf.filter_face_mark_boundaries = (lmd->calculation_flags & + LRT_FILTER_FACE_MARK_BOUNDARIES) != 0; + ld->conf.filter_face_mark_keep_contour = (lmd->calculation_flags & + LRT_FILTER_FACE_MARK_KEEP_CONTOUR) != 0; - rb->chain_data_pool = &lc->chain_data_pool; + ld->chain_data_pool = &lc->chain_data_pool; - BLI_spin_init(&rb->lock_task); - BLI_spin_init(&rb->lock_cuts); - BLI_spin_init(&rb->render_data_pool.lock_mem); + BLI_spin_init(&ld->lock_task); + BLI_spin_init(&ld->lock_cuts); + BLI_spin_init(&ld->render_data_pool.lock_mem); - rb->thread_count = BKE_render_num_threads(&scene->r); + ld->thread_count = BKE_render_num_threads(&scene->r); - return rb; + return ld; } -static int lineart_triangle_size_get(LineartRenderBuffer *rb) +static int lineart_triangle_size_get(LineartData *ld) { - return sizeof(LineartTriangle) + (sizeof(LineartEdge *) * (rb->thread_count)); + return sizeof(LineartTriangle) + (sizeof(LineartEdge *) * (ld->thread_count)); } -static void lineart_main_bounding_area_make_initial(LineartRenderBuffer *rb) +static void lineart_main_bounding_area_make_initial(LineartData *ld) { /* Initial tile split is defined as 4 (subdivided as 4*4), increasing the value allows the * algorithm to build the acceleration structure for bigger scenes a little faster but not as @@ -3491,11 +3505,11 @@ static void lineart_main_bounding_area_make_initial(LineartRenderBuffer *rb) LineartBoundingArea *ba; /* Always make sure the shortest side has at least LRT_BA_ROWS tiles. */ - if (rb->w > rb->h) { - sp_w = sp_h * rb->w / rb->h; + if (ld->w > ld->h) { + sp_w = sp_h * ld->w / ld->h; } else { - sp_h = sp_w * rb->h / rb->w; + sp_h = sp_w * ld->h / ld->w; } /* Because NDC (Normalized Device Coordinates) range is (-1,1), @@ -3503,19 +3517,19 @@ static void lineart_main_bounding_area_make_initial(LineartRenderBuffer *rb) double span_w = (double)1 / sp_w * 2.0; double span_h = (double)1 / sp_h * 2.0; - rb->tile_count_x = sp_w; - rb->tile_count_y = sp_h; - rb->width_per_tile = span_w; - rb->height_per_tile = span_h; + ld->qtree.count_x = sp_w; + ld->qtree.count_y = sp_h; + ld->qtree.tile_width = span_w; + ld->qtree.tile_height = span_h; - rb->bounding_area_count = sp_w * sp_h; - rb->initial_bounding_areas = lineart_mem_acquire( - &rb->render_data_pool, sizeof(LineartBoundingArea) * rb->bounding_area_count); + ld->qtree.tile_count = sp_w * sp_h; + ld->qtree.initials = lineart_mem_acquire(&ld->render_data_pool, + sizeof(LineartBoundingArea) * ld->qtree.tile_count); /* Initialize tiles. */ for (row = 0; row < sp_h; row++) { for (col = 0; col < sp_w; col++) { - ba = &rb->initial_bounding_areas[row * rb->tile_count_x + col]; + ba = &ld->qtree.initials[row * ld->qtree.count_x + col]; /* Set the four direction limits. */ ba->l = span_w * col - 1.0; @@ -3542,11 +3556,11 @@ static void lineart_main_bounding_area_make_initial(LineartRenderBuffer *rb) /** * Re-link adjacent tiles after one gets subdivided. */ -static void lineart_bounding_areas_connect_new(LineartRenderBuffer *rb, LineartBoundingArea *root) +static void lineart_bounding_areas_connect_new(LineartData *ld, LineartBoundingArea *root) { LineartBoundingArea *ba = root->child, *tba; LinkData *lip2, *next_lip; - LineartStaticMemPool *mph = &rb->render_data_pool; + LineartStaticMemPool *mph = &ld->render_data_pool; /* Inter-connection with newly created 4 child bounding areas. */ lineart_list_append_pointer_pool(&ba[1].rp, mph, &ba[0]); @@ -3682,54 +3696,45 @@ static void lineart_bounding_areas_connect_new(LineartRenderBuffer *rb, LineartB BLI_listbase_clear(&root->bp); } -static void lineart_bounding_areas_connect_recursive(LineartRenderBuffer *rb, - LineartBoundingArea *root) +static void lineart_bounding_areas_connect_recursive(LineartData *ld, LineartBoundingArea *root) { if (root->child) { - lineart_bounding_areas_connect_new(rb, root); + lineart_bounding_areas_connect_new(ld, root); for (int i = 0; i < 4; i++) { - lineart_bounding_areas_connect_recursive(rb, &root->child[i]); + lineart_bounding_areas_connect_recursive(ld, &root->child[i]); } } } -static void lineart_main_bounding_areas_connect_post(LineartRenderBuffer *rb) +static void lineart_main_bounding_areas_connect_post(LineartData *ld) { - int total_tile_initial = rb->tile_count_x * rb->tile_count_y; - int tiles_per_row = rb->tile_count_x; + int total_tile_initial = ld->qtree.count_x * ld->qtree.count_y; + int tiles_per_row = ld->qtree.count_x; - for (int row = 0; row < rb->tile_count_y; row++) { - for (int col = 0; col < rb->tile_count_x; col++) { - LineartBoundingArea *ba = &rb->initial_bounding_areas[row * tiles_per_row + col]; + for (int row = 0; row < ld->qtree.count_y; row++) { + for (int col = 0; col < ld->qtree.count_x; col++) { + LineartBoundingArea *ba = &ld->qtree.initials[row * tiles_per_row + col]; /* Link adjacent ones. */ if (row) { lineart_list_append_pointer_pool( - &ba->up, - &rb->render_data_pool, - &rb->initial_bounding_areas[(row - 1) * tiles_per_row + col]); + &ba->up, &ld->render_data_pool, &ld->qtree.initials[(row - 1) * tiles_per_row + col]); } if (col) { lineart_list_append_pointer_pool( - &ba->lp, - &rb->render_data_pool, - &rb->initial_bounding_areas[row * tiles_per_row + col - 1]); + &ba->lp, &ld->render_data_pool, &ld->qtree.initials[row * tiles_per_row + col - 1]); } - if (row != rb->tile_count_y - 1) { + if (row != ld->qtree.count_y - 1) { lineart_list_append_pointer_pool( - &ba->bp, - &rb->render_data_pool, - &rb->initial_bounding_areas[(row + 1) * tiles_per_row + col]); + &ba->bp, &ld->render_data_pool, &ld->qtree.initials[(row + 1) * tiles_per_row + col]); } - if (col != rb->tile_count_x - 1) { + if (col != ld->qtree.count_x - 1) { lineart_list_append_pointer_pool( - &ba->rp, - &rb->render_data_pool, - &rb->initial_bounding_areas[row * tiles_per_row + col + 1]); + &ba->rp, &ld->render_data_pool, &ld->qtree.initials[row * tiles_per_row + col + 1]); } } } for (int i = 0; i < total_tile_initial; i++) { - lineart_bounding_areas_connect_recursive(rb, &rb->initial_bounding_areas[i]); + lineart_bounding_areas_connect_recursive(ld, &ld->qtree.initials[i]); } } @@ -3737,12 +3742,12 @@ static void lineart_main_bounding_areas_connect_post(LineartRenderBuffer *rb) * Subdivide a tile after one tile contains too many triangles, then re-link triangles into all the * child tiles. */ -static void lineart_bounding_area_split(LineartRenderBuffer *rb, +static void lineart_bounding_area_split(LineartData *ld, LineartBoundingArea *root, int recursive_level) { - LineartBoundingArea *ba = lineart_mem_acquire_thread(&rb->render_data_pool, + LineartBoundingArea *ba = lineart_mem_acquire_thread(&ld->render_data_pool, sizeof(LineartBoundingArea) * 4); ba[0].l = root->cx; ba[0].r = root->r; @@ -3795,16 +3800,16 @@ static void lineart_bounding_area_split(LineartRenderBuffer *rb, /* Re-link triangles into child tiles, not doing intersection lines during this because this * batch of triangles are all tested with each other for intersections. */ if (LRT_BOUND_AREA_CROSSES(b, &ba[0].l)) { - lineart_bounding_area_link_triangle(rb, &ba[0], tri, b, 0, recursive_level + 1, false, NULL); + lineart_bounding_area_link_triangle(ld, &ba[0], tri, b, 0, recursive_level + 1, false, NULL); } if (LRT_BOUND_AREA_CROSSES(b, &ba[1].l)) { - lineart_bounding_area_link_triangle(rb, &ba[1], tri, b, 0, recursive_level + 1, false, NULL); + lineart_bounding_area_link_triangle(ld, &ba[1], tri, b, 0, recursive_level + 1, false, NULL); } if (LRT_BOUND_AREA_CROSSES(b, &ba[2].l)) { - lineart_bounding_area_link_triangle(rb, &ba[2], tri, b, 0, recursive_level + 1, false, NULL); + lineart_bounding_area_link_triangle(ld, &ba[2], tri, b, 0, recursive_level + 1, false, NULL); } if (LRT_BOUND_AREA_CROSSES(b, &ba[3].l)) { - lineart_bounding_area_link_triangle(rb, &ba[3], tri, b, 0, recursive_level + 1, false, NULL); + lineart_bounding_area_link_triangle(ld, &ba[3], tri, b, 0, recursive_level + 1, false, NULL); } } @@ -3812,15 +3817,15 @@ static void lineart_bounding_area_split(LineartRenderBuffer *rb, * inserted, so assign root->child for #lineart_bounding_area_link_triangle to use. */ root->child = ba; - rb->bounding_area_count += 3; + ld->qtree.tile_count += 3; } -static bool lineart_bounding_area_edge_intersect(LineartRenderBuffer *UNUSED(fb), +static bool lineart_bounding_area_edge_intersect(LineartData *UNUSED(fb), const double l[2], const double r[2], LineartBoundingArea *ba) { - double vx, vy; + double dx, dy; double converted[4]; double c1, c; @@ -3831,25 +3836,25 @@ static bool lineart_bounding_area_edge_intersect(LineartRenderBuffer *UNUSED(fb) return false; } - vx = l[0] - r[0]; - vy = l[1] - r[1]; + dx = l[0] - r[0]; + dy = l[1] - r[1]; - c1 = vx * (converted[2] - l[1]) - vy * (converted[0] - l[0]); + c1 = dx * (converted[2] - l[1]) - dy * (converted[0] - l[0]); c = c1; - c1 = vx * (converted[2] - l[1]) - vy * (converted[1] - l[0]); + c1 = dx * (converted[2] - l[1]) - dy * (converted[1] - l[0]); if (c1 * c <= 0) { return true; } c = c1; - c1 = vx * (converted[3] - l[1]) - vy * (converted[0] - l[0]); + c1 = dx * (converted[3] - l[1]) - dy * (converted[0] - l[0]); if (c1 * c <= 0) { return true; } c = c1; - c1 = vx * (converted[3] - l[1]) - vy * (converted[1] - l[0]); + c1 = dx * (converted[3] - l[1]) - dy * (converted[1] - l[0]); if (c1 * c <= 0) { return true; } @@ -3858,7 +3863,7 @@ static bool lineart_bounding_area_edge_intersect(LineartRenderBuffer *UNUSED(fb) return false; } -static bool lineart_bounding_area_triangle_intersect(LineartRenderBuffer *fb, +static bool lineart_bounding_area_triangle_intersect(LineartData *fb, LineartTriangle *tri, LineartBoundingArea *ba) { @@ -3895,7 +3900,7 @@ static bool lineart_bounding_area_triangle_intersect(LineartRenderBuffer *fb, /** * This function does two things: * - * 1) Builds a quad-tree under rb->InitialBoundingAreas to achieve good geometry separation for + * 1) Builds a quad-tree under ld->InitialBoundingAreas to achieve good geometry separation for * fast overlapping test between triangles and lines. This acceleration structure makes the * occlusion stage much faster. * @@ -3905,7 +3910,7 @@ static bool lineart_bounding_area_triangle_intersect(LineartRenderBuffer *fb, * duplicated intersection lines. * */ -static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb, +static void lineart_bounding_area_link_triangle(LineartData *ld, LineartBoundingArea *root_ba, LineartTriangle *tri, double *LRUB, @@ -3914,7 +3919,7 @@ static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb, bool do_intersection, struct LineartIsecThread *th) { - if (!lineart_bounding_area_triangle_intersect(rb, tri, root_ba)) { + if (!lineart_bounding_area_triangle_intersect(ld, tri, root_ba)) { return; } @@ -3935,7 +3940,7 @@ static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb, for (int iba = 0; iba < 4; iba++) { if (LRT_BOUND_AREA_CROSSES(B1, &old_ba->child[iba].l)) { lineart_bounding_area_link_triangle( - rb, &old_ba->child[iba], tri, B1, recursive, recursive_level + 1, do_intersection, th); + ld, &old_ba->child[iba], tri, B1, recursive, recursive_level + 1, do_intersection, th); } } return; @@ -3954,7 +3959,7 @@ static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb, old_ba->linked_triangles[old_ba->triangle_count++] = tri; /* Do intersections in place. */ - if (do_intersection && rb->use_intersections) { + if (do_intersection && ld->conf.use_intersections) { lineart_triangle_intersect_in_bounding_area(tri, old_ba, th, old_tri_count); } @@ -3964,10 +3969,10 @@ static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb, } else { /* We need to wait for either splitting or array extension to be done. */ - if (recursive_level < rb->tile_recursive_level) { + if (recursive_level < ld->qtree.recursive_level) { if (!old_ba->child) { /* old_ba->child==NULL, means we are the thread that's doing the splitting. */ - lineart_bounding_area_split(rb, old_ba, recursive_level); + lineart_bounding_area_split(ld, old_ba, recursive_level); } /* Otherwise other thread has completed the splitting process. */ } else { @@ -3984,7 +3989,7 @@ static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb, /* Of course we still have our own triangle needs to be added. */ lineart_bounding_area_link_triangle( - rb, root_ba, tri, LRUB, recursive, recursive_level, do_intersection, th); + ld, root_ba, tri, LRUB, recursive, recursive_level, do_intersection, th); } } @@ -4003,17 +4008,16 @@ static void lineart_free_bounding_area_memory(LineartBoundingArea *ba, bool recu } } } -static void lineart_free_bounding_area_memories(LineartRenderBuffer *rb) +static void lineart_free_bounding_area_memories(LineartData *ld) { - for (int i = 0; i < rb->tile_count_y; i++) { - for (int j = 0; j < rb->tile_count_x; j++) { - lineart_free_bounding_area_memory(&rb->initial_bounding_areas[i * rb->tile_count_x + j], - true); + for (int i = 0; i < ld->qtree.count_y; i++) { + for (int j = 0; j < ld->qtree.count_x; j++) { + lineart_free_bounding_area_memory(&ld->qtree.initials[i * ld->qtree.count_x + j], true); } } } -static void lineart_bounding_area_link_edge(LineartRenderBuffer *rb, +static void lineart_bounding_area_link_edge(LineartData *ld, LineartBoundingArea *root_ba, LineartEdge *e) { @@ -4022,20 +4026,20 @@ static void lineart_bounding_area_link_edge(LineartRenderBuffer *rb, } else { if (lineart_bounding_area_edge_intersect( - rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[0])) { - lineart_bounding_area_link_edge(rb, &root_ba->child[0], e); + ld, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[0])) { + lineart_bounding_area_link_edge(ld, &root_ba->child[0], e); } if (lineart_bounding_area_edge_intersect( - rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[1])) { - lineart_bounding_area_link_edge(rb, &root_ba->child[1], e); + ld, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[1])) { + lineart_bounding_area_link_edge(ld, &root_ba->child[1], e); } if (lineart_bounding_area_edge_intersect( - rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[2])) { - lineart_bounding_area_link_edge(rb, &root_ba->child[2], e); + ld, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[2])) { + lineart_bounding_area_link_edge(ld, &root_ba->child[2], e); } if (lineart_bounding_area_edge_intersect( - rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[3])) { - lineart_bounding_area_link_edge(rb, &root_ba->child[3], e); + ld, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[3])) { + lineart_bounding_area_link_edge(ld, &root_ba->child[3], e); } } } @@ -4043,16 +4047,16 @@ static void lineart_bounding_area_link_edge(LineartRenderBuffer *rb, /** * Link lines to their respective bounding areas. */ -static void lineart_main_link_lines(LineartRenderBuffer *rb) +static void lineart_main_link_lines(LineartData *ld) { LRT_ITER_ALL_LINES_BEGIN { int r1, r2, c1, c2, row, col; - if (lineart_get_edge_bounding_areas(rb, e, &r1, &r2, &c1, &c2)) { + if (lineart_get_edge_bounding_areas(ld, e, &r1, &r2, &c1, &c2)) { for (row = r1; row != r2 + 1; row++) { for (col = c1; col != c2 + 1; col++) { lineart_bounding_area_link_edge( - rb, &rb->initial_bounding_areas[row * rb->tile_count_x + col], e); + ld, &ld->qtree.initials[row * ld->qtree.count_x + col], e); } } } @@ -4060,14 +4064,10 @@ static void lineart_main_link_lines(LineartRenderBuffer *rb) LRT_ITER_ALL_LINES_END } -static bool lineart_get_triangle_bounding_areas(LineartRenderBuffer *rb, - LineartTriangle *tri, - int *rowbegin, - int *rowend, - int *colbegin, - int *colend) +static bool lineart_get_triangle_bounding_areas( + LineartData *ld, LineartTriangle *tri, int *rowbegin, int *rowend, int *colbegin, int *colend) { - double sp_w = rb->width_per_tile, sp_h = rb->height_per_tile; + double sp_w = ld->qtree.tile_width, sp_h = ld->qtree.tile_height; double b[4]; if (!tri->v[0] || !tri->v[1] || !tri->v[2]) { @@ -4085,14 +4085,14 @@ static bool lineart_get_triangle_bounding_areas(LineartRenderBuffer *rb, (*colbegin) = (int)((b[0] + 1.0) / sp_w); (*colend) = (int)((b[1] + 1.0) / sp_w); - (*rowend) = rb->tile_count_y - (int)((b[2] + 1.0) / sp_h) - 1; - (*rowbegin) = rb->tile_count_y - (int)((b[3] + 1.0) / sp_h) - 1; + (*rowend) = ld->qtree.count_y - (int)((b[2] + 1.0) / sp_h) - 1; + (*rowbegin) = ld->qtree.count_y - (int)((b[3] + 1.0) / sp_h) - 1; - if ((*colend) >= rb->tile_count_x) { - (*colend) = rb->tile_count_x - 1; + if ((*colend) >= ld->qtree.count_x) { + (*colend) = ld->qtree.count_x - 1; } - if ((*rowend) >= rb->tile_count_y) { - (*rowend) = rb->tile_count_y - 1; + if ((*rowend) >= ld->qtree.count_y) { + (*rowend) = ld->qtree.count_y - 1; } if ((*colbegin) < 0) { (*colbegin) = 0; @@ -4104,14 +4104,10 @@ static bool lineart_get_triangle_bounding_areas(LineartRenderBuffer *rb, return true; } -static bool lineart_get_edge_bounding_areas(LineartRenderBuffer *rb, - LineartEdge *e, - int *rowbegin, - int *rowend, - int *colbegin, - int *colend) +static bool lineart_get_edge_bounding_areas( + LineartData *ld, LineartEdge *e, int *rowbegin, int *rowend, int *colbegin, int *colend) { - double sp_w = rb->width_per_tile, sp_h = rb->height_per_tile; + double sp_w = ld->qtree.tile_width, sp_h = ld->qtree.tile_height; double b[4]; if (!e->v1 || !e->v2) { @@ -4133,31 +4129,29 @@ static bool lineart_get_edge_bounding_areas(LineartRenderBuffer *rb, (*colbegin) = (int)((b[0] + 1.0) / sp_w); (*colend) = (int)((b[1] + 1.0) / sp_w); - (*rowend) = rb->tile_count_y - (int)((b[2] + 1.0) / sp_h) - 1; - (*rowbegin) = rb->tile_count_y - (int)((b[3] + 1.0) / sp_h) - 1; + (*rowend) = ld->qtree.count_y - (int)((b[2] + 1.0) / sp_h) - 1; + (*rowbegin) = ld->qtree.count_y - (int)((b[3] + 1.0) / sp_h) - 1; /* It's possible that the line stretches too much out to the side, resulting negative value. */ if ((*rowend) < (*rowbegin)) { - (*rowend) = rb->tile_count_y - 1; + (*rowend) = ld->qtree.count_y - 1; } if ((*colend) < (*colbegin)) { - (*colend) = rb->tile_count_x - 1; + (*colend) = ld->qtree.count_x - 1; } - CLAMP((*colbegin), 0, rb->tile_count_x - 1); - CLAMP((*rowbegin), 0, rb->tile_count_y - 1); - CLAMP((*colend), 0, rb->tile_count_x - 1); - CLAMP((*rowend), 0, rb->tile_count_y - 1); + CLAMP((*colbegin), 0, ld->qtree.count_x - 1); + CLAMP((*rowbegin), 0, ld->qtree.count_y - 1); + CLAMP((*colend), 0, ld->qtree.count_x - 1); + CLAMP((*rowend), 0, ld->qtree.count_y - 1); return true; } -LineartBoundingArea *MOD_lineart_get_parent_bounding_area(LineartRenderBuffer *rb, - double x, - double y) +LineartBoundingArea *MOD_lineart_get_parent_bounding_area(LineartData *ld, double x, double y) { - double sp_w = rb->width_per_tile, sp_h = rb->height_per_tile; + double sp_w = ld->qtree.tile_width, sp_h = ld->qtree.tile_height; int col, row; if (x > 1 || x < -1 || y > 1 || y < -1) { @@ -4165,13 +4159,13 @@ LineartBoundingArea *MOD_lineart_get_parent_bounding_area(LineartRenderBuffer *r } col = (int)((x + 1.0) / sp_w); - row = rb->tile_count_y - (int)((y + 1.0) / sp_h) - 1; + row = ld->qtree.count_y - (int)((y + 1.0) / sp_h) - 1; - if (col >= rb->tile_count_x) { - col = rb->tile_count_x - 1; + if (col >= ld->qtree.count_x) { + col = ld->qtree.count_x - 1; } - if (row >= rb->tile_count_y) { - row = rb->tile_count_y - 1; + if (row >= ld->qtree.count_y) { + row = ld->qtree.count_y - 1; } if (col < 0) { col = 0; @@ -4180,29 +4174,29 @@ LineartBoundingArea *MOD_lineart_get_parent_bounding_area(LineartRenderBuffer *r row = 0; } - return &rb->initial_bounding_areas[row * rb->tile_count_x + col]; + return &ld->qtree.initials[row * ld->qtree.count_x + col]; } -static LineartBoundingArea *lineart_get_bounding_area(LineartRenderBuffer *rb, double x, double y) +static LineartBoundingArea *lineart_get_bounding_area(LineartData *ld, double x, double y) { LineartBoundingArea *iba; - double sp_w = rb->width_per_tile, sp_h = rb->height_per_tile; + double sp_w = ld->qtree.tile_width, sp_h = ld->qtree.tile_height; int c = (int)((x + 1.0) / sp_w); - int r = rb->tile_count_y - (int)((y + 1.0) / sp_h) - 1; + int r = ld->qtree.count_y - (int)((y + 1.0) / sp_h) - 1; if (r < 0) { r = 0; } if (c < 0) { c = 0; } - if (r >= rb->tile_count_y) { - r = rb->tile_count_y - 1; + if (r >= ld->qtree.count_y) { + r = ld->qtree.count_y - 1; } - if (c >= rb->tile_count_x) { - c = rb->tile_count_x - 1; + if (c >= ld->qtree.count_x) { + c = ld->qtree.count_x - 1; } - iba = &rb->initial_bounding_areas[r * rb->tile_count_x + c]; + iba = &ld->qtree.initials[r * ld->qtree.count_x + c]; while (iba->child) { if (x > iba->cx) { if (y > iba->cy) { @@ -4224,49 +4218,48 @@ static LineartBoundingArea *lineart_get_bounding_area(LineartRenderBuffer *rb, d return iba; } -LineartBoundingArea *MOD_lineart_get_bounding_area(LineartRenderBuffer *rb, double x, double y) +LineartBoundingArea *MOD_lineart_get_bounding_area(LineartData *ld, double x, double y) { LineartBoundingArea *ba; - if ((ba = MOD_lineart_get_parent_bounding_area(rb, x, y)) != NULL) { - return lineart_get_bounding_area(rb, x, y); + if ((ba = MOD_lineart_get_parent_bounding_area(ld, x, y)) != NULL) { + return lineart_get_bounding_area(ld, x, y); } return NULL; } static void lineart_add_triangles_worker(TaskPool *__restrict UNUSED(pool), LineartIsecThread *th) { - LineartRenderBuffer *rb = th->rb; + LineartData *ld = th->ld; int _dir_control = 0; while (lineart_schedule_new_triangle_task(th)) { for (LineartElementLinkNode *eln = th->pending_from; eln != th->pending_to->next; eln = eln->next) { int index_start = eln == th->pending_from ? th->index_from : 0; int index_end = eln == th->pending_to ? th->index_to : eln->element_count; - LineartTriangle *tri = (void *)(((uchar *)eln->pointer) + rb->triangle_size * index_start); + LineartTriangle *tri = (void *)(((uchar *)eln->pointer) + ld->sizeof_triangle * index_start); for (int ei = index_start; ei < index_end; ei++) { int x1, x2, y1, y2; int r, co; if ((tri->flags & LRT_CULL_USED) || (tri->flags & LRT_CULL_DISCARD)) { - tri = (void *)(((uchar *)tri) + rb->triangle_size); + tri = (void *)(((uchar *)tri) + ld->sizeof_triangle); continue; } - if (lineart_get_triangle_bounding_areas(rb, tri, &y1, &y2, &x1, &x2)) { + if (lineart_get_triangle_bounding_areas(ld, tri, &y1, &y2, &x1, &x2)) { _dir_control++; for (co = x1; co <= x2; co++) { for (r = y1; r <= y2; r++) { - lineart_bounding_area_link_triangle( - rb, - &rb->initial_bounding_areas[r * rb->tile_count_x + co], - tri, - 0, - 1, - 0, - (!(tri->flags & LRT_TRIANGLE_NO_INTERSECTION)), - th); + lineart_bounding_area_link_triangle(ld, + &ld->qtree.initials[r * ld->qtree.count_x + co], + tri, + 0, + 1, + 0, + (!(tri->flags & LRT_TRIANGLE_NO_INTERSECTION)), + th); } } } /* Else throw away. */ - tri = (void *)(((uchar *)tri) + rb->triangle_size); + tri = (void *)(((uchar *)tri) + ld->sizeof_triangle); } } } @@ -4274,9 +4267,9 @@ static void lineart_add_triangles_worker(TaskPool *__restrict UNUSED(pool), Line static void lineart_create_edges_from_isec_data(LineartIsecData *d) { - LineartRenderBuffer *rb = d->rb; - double ZMax = rb->far_clip; - double ZMin = rb->near_clip; + LineartData *ld = d->ld; + double ZMax = ld->conf.far_clip; + double ZMin = ld->conf.near_clip; for (int i = 0; i < d->thread_count; i++) { LineartIsecThread *th = &d->threads[i]; @@ -4289,9 +4282,9 @@ static void lineart_create_edges_from_isec_data(LineartIsecData *d) /* We don't care about removing duplicated vert in this method, chaining can handle that, * and it saves us from using locks and look up tables. */ LineartVertIntersection *v = lineart_mem_acquire( - &rb->render_data_pool, sizeof(LineartVertIntersection) * th->current * 2); - LineartEdge *e = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdge) * th->current); - LineartEdgeSegment *es = lineart_mem_acquire(&rb->render_data_pool, + &ld->render_data_pool, sizeof(LineartVertIntersection) * th->current * 2); + LineartEdge *e = lineart_mem_acquire(&ld->render_data_pool, sizeof(LineartEdge) * th->current); + LineartEdgeSegment *es = lineart_mem_acquire(&ld->render_data_pool, sizeof(LineartEdgeSegment) * th->current); for (int j = 0; j < th->current; j++) { LineartVertIntersection *v1i = v; @@ -4307,15 +4300,15 @@ static void lineart_create_edges_from_isec_data(LineartIsecData *d) copy_v3db_v3fl(v2->gloc, is->v2); /* The intersection line has been generated only in geometry space, so we need to transform * them as well. */ - mul_v4_m4v3_db(v1->fbcoord, rb->view_projection, v1->gloc); - mul_v4_m4v3_db(v2->fbcoord, rb->view_projection, v2->gloc); + mul_v4_m4v3_db(v1->fbcoord, ld->conf.view_projection, v1->gloc); + mul_v4_m4v3_db(v2->fbcoord, ld->conf.view_projection, v2->gloc); mul_v3db_db(v1->fbcoord, (1 / v1->fbcoord[3])); mul_v3db_db(v2->fbcoord, (1 / v2->fbcoord[3])); - v1->fbcoord[0] -= rb->shift_x * 2; - v1->fbcoord[1] -= rb->shift_y * 2; - v2->fbcoord[0] -= rb->shift_x * 2; - v2->fbcoord[1] -= rb->shift_y * 2; + v1->fbcoord[0] -= ld->conf.shift_x * 2; + v1->fbcoord[1] -= ld->conf.shift_y * 2; + v2->fbcoord[0] -= ld->conf.shift_x * 2; + v2->fbcoord[1] -= ld->conf.shift_y * 2; /* This z transformation is not the same as the rest of the part, because the data don't go * through normal perspective division calls in the pipeline, but this way the 3D result and @@ -4331,7 +4324,7 @@ static void lineart_create_edges_from_isec_data(LineartIsecData *d) e->intersection_mask = (is->tri1->intersection_mask | is->tri2->intersection_mask); BLI_addtail(&e->segments, es); - lineart_add_edge_to_array(&rb->pending_edges, e); + lineart_add_edge_to_array(&ld->pending_edges, e); v += 2; e++; @@ -4344,7 +4337,7 @@ static void lineart_create_edges_from_isec_data(LineartIsecData *d) * Sequentially add triangles into render buffer, intersection lines between those triangles will * also be computed at the same time. */ -static void lineart_main_add_triangles(LineartRenderBuffer *rb) +static void lineart_main_add_triangles(LineartData *ld) { double t_start; if (G.debug_value == 4000) { @@ -4354,10 +4347,10 @@ static void lineart_main_add_triangles(LineartRenderBuffer *rb) /* Initialize per-thread data for thread task scheduling information and storing intersection * results. */ LineartIsecData d = {0}; - lineart_init_isec_thread(&d, rb, rb->thread_count); + lineart_init_isec_thread(&d, ld, ld->thread_count); TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH); - for (int i = 0; i < rb->thread_count; i++) { + for (int i = 0; i < ld->thread_count; i++) { BLI_task_pool_push(tp, (TaskRunFunction)lineart_add_triangles_worker, &d.threads[i], 0, NULL); } BLI_task_pool_work_and_wait(tp); @@ -4378,8 +4371,7 @@ static void lineart_main_add_triangles(LineartRenderBuffer *rb) * This function gets the tile for the point `e->v1`, and later use #lineart_bounding_area_next() * to get next along the way. */ -static LineartBoundingArea *lineart_edge_first_bounding_area(LineartRenderBuffer *rb, - LineartEdge *e) +static LineartBoundingArea *lineart_edge_first_bounding_area(LineartData *ld, LineartEdge *e) { double data[2] = {e->v1->fbcoord[0], e->v1->fbcoord[1]}; double LU[2] = {-1, 1}, RU[2] = {1, 1}, LB[2] = {-1, -1}, RB[2] = {1, -1}; @@ -4387,7 +4379,7 @@ static LineartBoundingArea *lineart_edge_first_bounding_area(LineartRenderBuffer bool p_unused; if (data[0] > -1 && data[0] < 1 && data[1] > -1 && data[1] < 1) { - return lineart_get_bounding_area(rb, data[0], data[1]); + return lineart_get_bounding_area(ld, data[0], data[1]); } if (lineart_intersect_seg_seg(e->v1->fbcoord, e->v2->fbcoord, LU, RU, &sr, &p_unused) && @@ -4408,7 +4400,7 @@ static LineartBoundingArea *lineart_edge_first_bounding_area(LineartRenderBuffer } interp_v2_v2v2_db(data, e->v1->fbcoord, e->v2->fbcoord, r); - return lineart_get_bounding_area(rb, data[0], data[1]); + return lineart_get_bounding_area(ld, data[0], data[1]); } /** @@ -4639,7 +4631,7 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartCache **cached_result, bool enable_stroke_depth_offset) { - LineartRenderBuffer *rb; + LineartData *ld; Scene *scene = DEG_get_evaluated_scene(depsgraph); int intersections_only = 0; /* Not used right now, but preserve for future. */ Object *use_camera; @@ -4650,8 +4642,6 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, t_start = PIL_check_seconds_timer(); } - BKE_scene_camera_switch_update(scene); - if (lmd->calculation_flags & LRT_USE_CUSTOM_CAMERA) { if (!lmd->source_camera || (use_camera = DEG_get_evaluated_object(depsgraph, lmd->source_camera))->type != @@ -4660,6 +4650,9 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, } } else { + + BKE_scene_camera_switch_update(scene); + if (!scene->camera) { return false; } @@ -4669,58 +4662,53 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartCache *lc = lineart_init_cache(); *cached_result = lc; - rb = lineart_create_render_buffer(scene, lmd, use_camera, scene->camera, lc); + ld = lineart_create_render_buffer(scene, lmd, use_camera, scene->camera, lc); /* Triangle thread testing data size varies depending on the thread count. * See definition of LineartTriangleThread for details. */ - rb->triangle_size = lineart_triangle_size_get(rb); - - /* FIXME(Yiming): See definition of int #LineartRenderBuffer::_source_type for detailed. */ - rb->_source_type = lmd->source_type; - rb->_source_collection = lmd->source_collection; - rb->_source_object = lmd->source_object; + ld->sizeof_triangle = lineart_triangle_size_get(ld); /* Get view vector before loading geometries, because we detect feature lines there. */ - lineart_main_get_view_vector(rb); + lineart_main_get_view_vector(ld); lineart_main_load_geometries( - depsgraph, scene, use_camera, rb, lmd->calculation_flags & LRT_ALLOW_DUPLI_OBJECTS); + depsgraph, scene, use_camera, ld, lmd->calculation_flags & LRT_ALLOW_DUPLI_OBJECTS); - if (!rb->vertex_buffer_pointers.first) { + if (!ld->geom.vertex_buffer_pointers.first) { /* No geometry loaded, return early. */ return true; } /* Initialize the bounding box acceleration structure, it's a lot like BVH in 3D. */ - lineart_main_bounding_area_make_initial(rb); + lineart_main_bounding_area_make_initial(ld); /* We need to get cut into triangles that are crossing near/far plans, only this way can we get * correct coordinates of those clipped lines. Done in two steps, * setting clip_far==false for near plane. */ - lineart_main_cull_triangles(rb, false); + lineart_main_cull_triangles(ld, false); /* `clip_far == true` for far plane. */ - lineart_main_cull_triangles(rb, true); + lineart_main_cull_triangles(ld, true); /* At this point triangle adjacent info pointers is no longer needed, free them. */ - lineart_main_free_adjacent_data(rb); + lineart_main_free_adjacent_data(ld); /* Do the perspective division after clipping is done. */ - lineart_main_perspective_division(rb); + lineart_main_perspective_division(ld); - lineart_main_discard_out_of_frame_edges(rb); + lineart_main_discard_out_of_frame_edges(ld); /* Triangle intersections are done here during sequential adding of them. Only after this, * triangles and lines are all linked with acceleration structure, and the 2D occlusion stage * can do its job. */ - lineart_main_add_triangles(rb); + lineart_main_add_triangles(ld); /* Re-link bounding areas because they have been subdivided by worker threads and we need * adjacent info. */ - lineart_main_bounding_areas_connect_post(rb); + lineart_main_bounding_areas_connect_post(ld); /* Link lines to acceleration structure, this can only be done after perspective division, if * we do it after triangles being added, the acceleration structure has already been * subdivided, this way we do less list manipulations. */ - lineart_main_link_lines(rb); + lineart_main_link_lines(ld); /* "intersection_only" is preserved for being called in a standalone fashion. * If so the data will already be available at the stage. Otherwise we do the occlusion and @@ -4729,54 +4717,54 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, if (!intersections_only) { /* Occlusion is work-and-wait. This call will not return before work is completed. */ - lineart_main_occlusion_begin(rb); + lineart_main_occlusion_begin(ld); /* Chaining is all single threaded. See lineart_chain.c * In this particular call, only lines that are geometrically connected (share the _exact_ * same end point) will be chained together. */ - MOD_lineart_chain_feature_lines(rb); + MOD_lineart_chain_feature_lines(ld); /* We are unable to take care of occlusion if we only connect end points, so here we do a * spit, where the splitting point could be any cut in e->segments. */ - MOD_lineart_chain_split_for_fixed_occlusion(rb); + MOD_lineart_chain_split_for_fixed_occlusion(ld); /* Then we connect chains based on the _proximity_ of their end points in image space, here's * the place threshold value gets involved. */ - MOD_lineart_chain_connect(rb); + MOD_lineart_chain_connect(ld); float *t_image = &lmd->chaining_image_threshold; /* This configuration ensures there won't be accidental lost of short unchained segments. */ - MOD_lineart_chain_discard_short(rb, MIN2(*t_image, 0.001f) - FLT_EPSILON); + MOD_lineart_chain_discard_short(ld, MIN2(*t_image, 0.001f) - FLT_EPSILON); - if (rb->chain_smooth_tolerance > FLT_EPSILON) { + if (ld->conf.chain_smooth_tolerance > FLT_EPSILON) { /* Keeping UI range of 0-1 for ease of read while scaling down the actual value for best * effective range in image-space (Coordinate only goes from -1 to 1). This value is * somewhat arbitrary, but works best for the moment. */ - MOD_lineart_smooth_chains(rb, rb->chain_smooth_tolerance / 50); + MOD_lineart_smooth_chains(ld, ld->conf.chain_smooth_tolerance / 50); } - if (rb->use_image_boundary_trimming) { - MOD_lineart_chain_clip_at_border(rb); + if (ld->conf.use_image_boundary_trimming) { + MOD_lineart_chain_clip_at_border(ld); } - if (rb->angle_splitting_threshold > FLT_EPSILON) { - MOD_lineart_chain_split_angle(rb, rb->angle_splitting_threshold); + if (ld->conf.angle_splitting_threshold > FLT_EPSILON) { + MOD_lineart_chain_split_angle(ld, ld->conf.angle_splitting_threshold); } if (enable_stroke_depth_offset && lmd->stroke_depth_offset > FLT_EPSILON) { MOD_lineart_chain_offset_towards_camera( - rb, lmd->stroke_depth_offset, lmd->flags & LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA); + ld, lmd->stroke_depth_offset, lmd->flags & LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA); } /* Finally transfer the result list into cache. */ - memcpy(&lc->chains, &rb->chains, sizeof(ListBase)); + memcpy(&lc->chains, &ld->chains, sizeof(ListBase)); /* At last, we need to clear flags so we don't confuse GPencil generation calls. */ MOD_lineart_chain_clear_picked_flag(lc); } if (G.debug_value == 4000) { - lineart_count_and_print_render_buffer_memory(rb); + lineart_count_and_print_render_buffer_memory(ld); double t_elapsed = PIL_check_seconds_timer() - t_start; printf("Line art total time: %lf\n", t_elapsed); @@ -4785,15 +4773,15 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, return true; } -static int UNUSED_FUNCTION(lineart_rb_edge_types)(LineartRenderBuffer *rb) +static int UNUSED_FUNCTION(lineart_rb_edge_types)(LineartData *ld) { int types = 0; - types |= rb->use_contour ? LRT_EDGE_FLAG_CONTOUR : 0; - types |= rb->use_crease ? LRT_EDGE_FLAG_CREASE : 0; - types |= rb->use_material ? LRT_EDGE_FLAG_MATERIAL : 0; - types |= rb->use_edge_marks ? LRT_EDGE_FLAG_EDGE_MARK : 0; - types |= rb->use_intersections ? LRT_EDGE_FLAG_INTERSECTION : 0; - types |= rb->use_loose ? LRT_EDGE_FLAG_LOOSE : 0; + types |= ld->conf.use_contour ? LRT_EDGE_FLAG_CONTOUR : 0; + types |= ld->conf.use_crease ? LRT_EDGE_FLAG_CREASE : 0; + types |= ld->conf.use_material ? LRT_EDGE_FLAG_MATERIAL : 0; + types |= ld->conf.use_edge_marks ? LRT_EDGE_FLAG_EDGE_MARK : 0; + types |= ld->conf.use_intersections ? LRT_EDGE_FLAG_INTERSECTION : 0; + types |= ld->conf.use_loose ? LRT_EDGE_FLAG_LOOSE : 0; return types; } @@ -4812,7 +4800,7 @@ static void lineart_gpencil_generate(LineartCache *cache, uchar mask_switches, uchar material_mask_bits, uchar intersection_mask, - short thickness, + int16_t thickness, float opacity, const char *source_vgname, const char *vgname, @@ -4971,16 +4959,16 @@ void MOD_lineart_gpencil_generate(LineartCache *cache, Object *ob, bGPDlayer *gpl, bGPDframe *gpf, - char source_type, + int8_t source_type, void *source_reference, int level_start, int level_end, int mat_nr, - short edge_types, + int16_t edge_types, uchar mask_switches, uchar material_mask_bits, uchar intersection_mask, - short thickness, + int16_t thickness, float opacity, const char *source_vgname, const char *vgname, @@ -4993,7 +4981,7 @@ void MOD_lineart_gpencil_generate(LineartCache *cache, Object *source_object = NULL; Collection *source_collection = NULL; - short use_types = 0; + int16_t use_types = 0; if (source_type == LRT_SOURCE_OBJECT) { if (!source_reference) { return; diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h index 2114ca1dc91..5e24061bd78 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h @@ -18,7 +18,7 @@ #include <string.h> struct LineartEdge; -struct LineartRenderBuffer; +struct LineartData; struct LineartStaticMemPool; struct LineartStaticMemPoolNode; @@ -49,7 +49,6 @@ void *lineart_mem_acquire(struct LineartStaticMemPool *smp, size_t size); void *lineart_mem_acquire_thread(struct LineartStaticMemPool *smp, size_t size); void lineart_mem_destroy(struct LineartStaticMemPool *smp); -void lineart_prepend_edge_direct(void **list_head, void *node); void lineart_prepend_pool(LinkNode **first, struct LineartStaticMemPool *smp, void *link); void lineart_matrix_ortho_44d(double (*mProjection)[4], @@ -62,14 +61,14 @@ void lineart_matrix_ortho_44d(double (*mProjection)[4], void lineart_matrix_perspective_44d( double (*mProjection)[4], double fFov_rad, double fAspect, double zMin, double zMax); -int lineart_count_intersection_segment_count(struct LineartRenderBuffer *rb); +int lineart_count_intersection_segment_count(struct LineartData *ld); -void lineart_count_and_print_render_buffer_memory(struct LineartRenderBuffer *rb); +void lineart_count_and_print_render_buffer_memory(struct LineartData *ld); #define LRT_ITER_ALL_LINES_BEGIN \ LineartEdge *e; \ - for (int i = 0; i < rb->pending_edges.next; i++) { \ - e = rb->pending_edges.array[i]; + for (int i = 0; i < ld->pending_edges.next; i++) { \ + e = ld->pending_edges.array[i]; #define LRT_ITER_ALL_LINES_NEXT ; /* Doesn't do anything now with new array setup. */ diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c index 2b60fc45800..bbf88985e6a 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c @@ -141,8 +141,8 @@ static bool bake_strokes(Object *ob, if (!is_first) { MOD_lineart_clear_cache(&local_lc); } - /* Restore the original cache pointer so the modifiers below still have access to the - * "global" cache. */ + /* Restore the original cache pointer so the modifiers below still have access to the "global" + * cache. */ lmd->cache = gpd->runtime.lineart_cache; } diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c index 4ea17b25995..95647f2dd75 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c @@ -144,13 +144,6 @@ void lineart_mem_destroy(LineartStaticMemPool *smp) } } -void lineart_prepend_edge_direct(void **list_head, void *node) -{ - LineartEdge *e_n = (LineartEdge *)node; - e_n->next = (*list_head); - (*list_head) = e_n; -} - void lineart_prepend_pool(LinkNode **first, LineartStaticMemPool *smp, void *link) { LinkNode *ln = lineart_mem_acquire_thread(smp, sizeof(LinkNode)); @@ -212,13 +205,13 @@ void lineart_matrix_ortho_44d(double (*mProjection)[4], mProjection[3][3] = 1.0f; } -void lineart_count_and_print_render_buffer_memory(LineartRenderBuffer *rb) +void lineart_count_and_print_render_buffer_memory(LineartData *ld) { size_t total = 0; size_t sum_this = 0; size_t count_this = 0; - LISTBASE_FOREACH (LineartStaticMemPoolNode *, smpn, &rb->render_data_pool.pools) { + LISTBASE_FOREACH (LineartStaticMemPoolNode *, smpn, &ld->render_data_pool.pools) { count_this++; sum_this += LRT_MEMORY_POOL_1MB; } @@ -227,7 +220,7 @@ void lineart_count_and_print_render_buffer_memory(LineartRenderBuffer *rb) sum_this = 0; count_this = 0; - LISTBASE_FOREACH (LineartElementLinkNode *, reln, &rb->line_buffer_pointers) { + LISTBASE_FOREACH (LineartElementLinkNode *, reln, &ld->geom.line_buffer_pointers) { count_this++; sum_this += reln->element_count * sizeof(LineartEdge); } @@ -236,9 +229,9 @@ void lineart_count_and_print_render_buffer_memory(LineartRenderBuffer *rb) sum_this = 0; count_this = 0; - LISTBASE_FOREACH (LineartElementLinkNode *, reln, &rb->triangle_buffer_pointers) { + LISTBASE_FOREACH (LineartElementLinkNode *, reln, &ld->geom.triangle_buffer_pointers) { count_this++; - sum_this += reln->element_count * rb->triangle_size; + sum_this += reln->element_count * ld->sizeof_triangle; } printf(" allocated %zu triangle blocks, total %zu Bytes.\n", count_this, sum_this); total += sum_this; diff --git a/source/blender/gpu/intern/gpu_codegen.cc b/source/blender/gpu/intern/gpu_codegen.cc index f1b46f8bf86..a5ba0949a83 100644 --- a/source/blender/gpu/intern/gpu_codegen.cc +++ b/source/blender/gpu/intern/gpu_codegen.cc @@ -40,8 +40,8 @@ #include "gpu_shader_create_info.hh" #include "gpu_shader_dependency_private.h" -#include <stdarg.h> -#include <string.h> +#include <cstdarg> +#include <cstring> #include <sstream> #include <string> diff --git a/source/blender/gpu/intern/gpu_immediate_util.c b/source/blender/gpu/intern/gpu_immediate_util.c index 67035853594..daefd57a5b3 100644 --- a/source/blender/gpu/intern/gpu_immediate_util.c +++ b/source/blender/gpu/intern/gpu_immediate_util.c @@ -337,13 +337,27 @@ static void imm_draw_circle_3D( /* Note(Metal/AMD): For small primitives, line list more efficient than line strip. */ immBegin(GPU_PRIM_LINES, nsegments * 2); - immVertex3f(pos, x + radius * cosf(0.0f), y + radius * sinf(0.0f), 0.0f); - for (int i = 1; i < nsegments; i++) { - float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments); - immVertex3f(pos, x + radius * cosf(angle), y + radius * sinf(angle), 0.0f); - immVertex3f(pos, x + radius * cosf(angle), y + radius * sinf(angle), 0.0f); + const float angle = (float)(2 * M_PI) / (float)nsegments; + float xprev = cosf(-angle) * radius; + float yprev = sinf(-angle) * radius; + const float alpha = 2.0f * cosf(angle); + + float xr = radius; + float yr = 0; + + for (int i = 0; i < nsegments; i++) { + immVertex3f(pos, x + xr, y + yr, 0.0f); + if (i) { + immVertex3f(pos, x + xr, y + yr, 0.0f); + } + const float xnext = alpha * xr - xprev; + const float ynext = alpha * yr - yprev; + xprev = xr; + yprev = yr; + xr = xnext; + yr = ynext; } - immVertex3f(pos, x + radius * cosf(0.0f), y + radius * sinf(0.0f), 0.0f); + immVertex3f(pos, x + radius, y, 0.0f); immEnd(); } else { diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 808a8ccd955..5d6651c3e3a 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -669,7 +669,7 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene, BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "GPUNodeGraph.used_libraries"); mat->refcount = 1; #ifndef NDEBUG - BLI_snprintf(mat->name, sizeof(mat->name), "%s", name); + STRNCPY(mat->name, name); #else UNUSED_VARS(name); #endif diff --git a/source/blender/gpu/intern/gpu_shader_interface.cc b/source/blender/gpu/intern/gpu_shader_interface.cc index ea138973c74..6f43b379d31 100644 --- a/source/blender/gpu/intern/gpu_shader_interface.cc +++ b/source/blender/gpu/intern/gpu_shader_interface.cc @@ -38,7 +38,7 @@ static void sort_input_list(MutableSpan<ShaderInput> dst) /* Simple sorting by going through the array and selecting the biggest element each time. */ for (uint i = 0; i < dst.size(); i++) { - ShaderInput *input_src = &src[0]; + ShaderInput *input_src = src.data(); for (uint j = 1; j < src.size(); j++) { if (src[j].name_hash > input_src->name_hash) { input_src = &src[j]; diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc index 5a28b8b7318..83b00da0446 100644 --- a/source/blender/gpu/opengl/gl_shader.cc +++ b/source/blender/gpu/opengl/gl_shader.cc @@ -613,7 +613,7 @@ std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) c if (info.early_fragment_test_) { ss << "layout(early_fragment_tests) in;\n"; } - if (GLEW_VERSION_4_2 || GLEW_ARB_conservative_depth) { + if (GLEW_ARB_conservative_depth) { ss << "layout(" << to_string(info.depth_write_) << ") out float gl_FragDepth;\n"; } ss << "\n/* Outputs. */\n"; @@ -836,7 +836,7 @@ static char *glsl_patch_default_get() STR_CONCAT(patch, slen, "#extension GL_ARB_texture_cube_map_array : enable\n"); STR_CONCAT(patch, slen, "#define GPU_ARB_texture_cube_map_array\n"); } - if (!GLEW_VERSION_4_2 && GLEW_ARB_conservative_depth) { + if (GLEW_ARB_conservative_depth) { STR_CONCAT(patch, slen, "#extension GL_ARB_conservative_depth : enable\n"); } if (GPU_shader_image_load_store_support()) { diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index 54ef5438c23..0414fa1268d 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -174,14 +174,14 @@ class IMMapStream : public Imf::IStream { imb_mmap_lock(); _mmap_file = BLI_mmap_open(file); imb_mmap_unlock(); - if (_mmap_file == NULL) { + if (_mmap_file == nullptr) { throw IEX_NAMESPACE::InputExc("BLI_mmap_open failed"); } close(file); _exrbuf = (unsigned char *)BLI_mmap_get_pointer(_mmap_file); } - ~IMMapStream() + ~IMMapStream() override { imb_mmap_lock(); BLI_mmap_free(_mmap_file); @@ -512,7 +512,7 @@ static bool imb_save_openexr_half(ImBuf *ibuf, const char *name, const int flags /* we store first everything in half array */ std::vector<RGBAZ> pixels(height * width); - RGBAZ *to = &pixels[0]; + RGBAZ *to = pixels.data(); int xstride = sizeof(RGBAZ); int ystride = xstride * width; @@ -1010,7 +1010,7 @@ void IMB_exrtile_begin_write( /* manually create ofstream, so we can handle utf-8 filepaths on windows */ try { data->ofile_stream = new OFileStream(filepath); - data->mpofile = new MultiPartOutputFile(*(data->ofile_stream), &headers[0], headers.size()); + data->mpofile = new MultiPartOutputFile(*(data->ofile_stream), headers.data(), headers.size()); } catch (const std::exception &) { delete data->mpofile; @@ -2209,7 +2209,7 @@ struct ImBuf *imb_load_filepath_thumbnail_openexr(const char *filepath, if (file->header().hasPreviewImage()) { const Imf::PreviewImage &preview = file->header().previewImage(); ImBuf *ibuf = IMB_allocFromBuffer( - (unsigned int *)preview.pixels(), NULL, preview.width(), preview.height(), 4); + (unsigned int *)preview.pixels(), nullptr, preview.width(), preview.height(), 4); delete file; delete stream; IMB_flipy(ibuf); diff --git a/source/blender/imbuf/intern/stereoimbuf.c b/source/blender/imbuf/intern/stereoimbuf.c index 52756891f21..2a0baaf6172 100644 --- a/source/blender/imbuf/intern/stereoimbuf.c +++ b/source/blender/imbuf/intern/stereoimbuf.c @@ -702,21 +702,21 @@ int *IMB_stereo3d_from_rect(const ImageFormatData *im_format, int *rect_left, int *rect_right) { - int *r_rect; + int *rect_result; Stereo3DData s3d_data = {{NULL}}; size_t width, height; const bool is_float = im_format->depth > 8; IMB_stereo3d_write_dimensions( im_format->stereo3d_format.display_mode, false, x, y, &width, &height); - r_rect = MEM_mallocN(channels * sizeof(int) * width * height, __func__); + rect_result = MEM_mallocN(channels * sizeof(int) * width * height, __func__); imb_stereo3d_data_init( - &s3d_data, is_float, x, y, channels, rect_left, rect_right, r_rect, NULL, NULL, NULL); + &s3d_data, is_float, x, y, channels, rect_left, rect_right, rect_result, NULL, NULL, NULL); imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format); - imb_stereo3d_squeeze_rect(r_rect, &im_format->stereo3d_format, x, y, channels); + imb_stereo3d_squeeze_rect(rect_result, &im_format->stereo3d_format, x, y, channels); - return r_rect; + return rect_result; } float *IMB_stereo3d_from_rectf(const ImageFormatData *im_format, @@ -726,21 +726,30 @@ float *IMB_stereo3d_from_rectf(const ImageFormatData *im_format, float *rectf_left, float *rectf_right) { - float *r_rectf; + float *rectf_result; Stereo3DData s3d_data = {{NULL}}; size_t width, height; const bool is_float = im_format->depth > 8; IMB_stereo3d_write_dimensions( im_format->stereo3d_format.display_mode, false, x, y, &width, &height); - r_rectf = MEM_mallocN(channels * sizeof(float) * width * height, __func__); + rectf_result = MEM_mallocN(channels * sizeof(float) * width * height, __func__); - imb_stereo3d_data_init( - &s3d_data, is_float, x, y, channels, NULL, NULL, NULL, rectf_left, rectf_right, r_rectf); + imb_stereo3d_data_init(&s3d_data, + is_float, + x, + y, + channels, + NULL, + NULL, + NULL, + rectf_left, + rectf_right, + rectf_result); imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format); - imb_stereo3d_squeeze_rectf(r_rectf, &im_format->stereo3d_format, x, y, channels); + imb_stereo3d_squeeze_rectf(rectf_result, &im_format->stereo3d_format, x, y, channels); - return r_rectf; + return rectf_result; } ImBuf *IMB_stereo3d_ImBuf(const ImageFormatData *im_format, ImBuf *ibuf_left, ImBuf *ibuf_right) diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c index f2c9c82fa66..6f39009d38d 100644 --- a/source/blender/imbuf/intern/thumbs.c +++ b/source/blender/imbuf/intern/thumbs.c @@ -30,7 +30,6 @@ #include "IMB_thumbs.h" #include <ctype.h> -#include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> @@ -514,7 +513,7 @@ void IMB_thumb_delete(const char *filepath, ThumbSize size) } } -ImBuf *IMB_thumb_manage(const char *org_path, ThumbSize size, ThumbSource source) +ImBuf *IMB_thumb_manage(const char *filepath, ThumbSize size, ThumbSource source) { char thumb_path[FILE_MAX]; char thumb_name[40]; @@ -526,7 +525,7 @@ ImBuf *IMB_thumb_manage(const char *org_path, ThumbSize size, ThumbSource source ImBuf *img = NULL; char *blen_group = NULL, *blen_id = NULL; - path = file_path = org_path; + path = file_path = filepath; if (source == THB_SOURCE_BLEND) { if (BLO_library_path_explode(path, path_buff, &blen_group, &blen_id)) { if (blen_group) { diff --git a/source/blender/imbuf/intern/transform.cc b/source/blender/imbuf/intern/transform.cc index edb47c8c7ce..1499c1071e3 100644 --- a/source/blender/imbuf/intern/transform.cc +++ b/source/blender/imbuf/intern/transform.cc @@ -293,30 +293,37 @@ class Sampler { NumChannels == 4) { const float wrapped_u = uv_wrapper.modify_u(source, u); const float wrapped_v = uv_wrapper.modify_v(source, v); - bilinear_interpolation_color_fl(source, nullptr, &r_sample[0], wrapped_u, wrapped_v); + bilinear_interpolation_color_fl(source, nullptr, r_sample.data(), wrapped_u, wrapped_v); } else if constexpr (Filter == IMB_FILTER_NEAREST && std::is_same_v<StorageType, unsigned char> && NumChannels == 4) { const float wrapped_u = uv_wrapper.modify_u(source, u); const float wrapped_v = uv_wrapper.modify_v(source, v); - nearest_interpolation_color_char(source, &r_sample[0], nullptr, wrapped_u, wrapped_v); + nearest_interpolation_color_char(source, r_sample.data(), nullptr, wrapped_u, wrapped_v); } else if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v<StorageType, unsigned char> && NumChannels == 4) { const float wrapped_u = uv_wrapper.modify_u(source, u); const float wrapped_v = uv_wrapper.modify_v(source, v); - bilinear_interpolation_color_char(source, &r_sample[0], nullptr, wrapped_u, wrapped_v); + bilinear_interpolation_color_char(source, r_sample.data(), nullptr, wrapped_u, wrapped_v); } else if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v<StorageType, float>) { if constexpr (std::is_same_v<UVWrapping, WrapRepeatUV>) { - BLI_bilinear_interpolation_wrap_fl( - source->rect_float, &r_sample[0], source->x, source->y, NumChannels, u, v, true, true); + BLI_bilinear_interpolation_wrap_fl(source->rect_float, + r_sample.data(), + source->x, + source->y, + NumChannels, + u, + v, + true, + true); } else { const float wrapped_u = uv_wrapper.modify_u(source, u); const float wrapped_v = uv_wrapper.modify_v(source, v); BLI_bilinear_interpolation_fl(source->rect_float, - &r_sample[0], + r_sample.data(), source->x, source->y, NumChannels, @@ -390,11 +397,11 @@ class ChannelConverter { BLI_STATIC_ASSERT(SourceNumChannels == 4, "Unsigned chars always have 4 channels."); BLI_STATIC_ASSERT(DestinationNumChannels == 4, "Unsigned chars always have 4 channels."); - copy_v4_v4_uchar(pixel_pointer.get_pointer(), &sample[0]); + copy_v4_v4_uchar(pixel_pointer.get_pointer(), sample.data()); } else if constexpr (std::is_same_v<StorageType, float> && SourceNumChannels == 4 && DestinationNumChannels == 4) { - copy_v4_v4(pixel_pointer.get_pointer(), &sample[0]); + copy_v4_v4(pixel_pointer.get_pointer(), sample.data()); } else if constexpr (std::is_same_v<StorageType, float> && SourceNumChannels == 3 && DestinationNumChannels == 4) { diff --git a/source/blender/io/alembic/exporter/abc_custom_props.cc b/source/blender/io/alembic/exporter/abc_custom_props.cc index 23a39ca2ee6..c5cc4631e18 100644 --- a/source/blender/io/alembic/exporter/abc_custom_props.cc +++ b/source/blender/io/alembic/exporter/abc_custom_props.cc @@ -150,7 +150,7 @@ void CustomPropertiesExporter::write_idparray_of_strings(const IDProperty *idp_a } /* Alembic needs a pointer to the first value of the array. */ - const std::string *array_of_strings = &strings[0]; + const std::string *array_of_strings = strings.data(); set_array_property<OStringArrayProperty, std::string>( idp_array->name, array_of_strings, strings.size()); } @@ -204,7 +204,7 @@ void CustomPropertiesExporter::write_idparray_flattened_typed(const IDProperty * } set_array_property<ABCPropertyType, BlenderValueType>( - idp_array->name, &matrix_values[0], matrix_values.size()); + idp_array->name, matrix_values.data(), matrix_values.size()); } template<typename ABCPropertyType, typename BlenderValueType> diff --git a/source/blender/io/alembic/intern/abc_customdata.cc b/source/blender/io/alembic/intern/abc_customdata.cc index 6e4ec6d23fa..2820a128072 100644 --- a/source/blender/io/alembic/intern/abc_customdata.cc +++ b/source/blender/io/alembic/intern/abc_customdata.cc @@ -257,7 +257,7 @@ void write_generated_coordinates(const OCompoundProperty &prop, CDStreamConfig & /* ORCOs are always stored in the normalized 0..1 range in Blender, but Alembic stores them * unnormalized, so we need to unnormalize (invert transform) them. */ BKE_mesh_orco_verts_transform( - mesh, reinterpret_cast<float(*)[3]>(&coords[0]), mesh->totvert, true); + mesh, reinterpret_cast<float(*)[3]>(coords.data()), mesh->totvert, true); if (!config.abc_orco.valid()) { /* Create the Alembic property and keep a reference so future frames can reuse it. */ diff --git a/source/blender/io/collada/BCMath.cpp b/source/blender/io/collada/BCMath.cpp index fc97110611a..d48e46ca115 100644 --- a/source/blender/io/collada/BCMath.cpp +++ b/source/blender/io/collada/BCMath.cpp @@ -49,11 +49,7 @@ BCMatrix::BCMatrix(BC_global_forward_axis global_forward_axis, BC_global_up_axis float mrot[3][3]; float mat[4][4]; mat3_from_axis_conversion( - BC_DEFAULT_FORWARD, BC_DEFAULT_UP, global_forward_axis, global_up_axis, mrot); - - /* TODO: Verify that `mat3_from_axis_conversion()` returns a transposed matrix */ - transpose_m3(mrot); - + global_forward_axis, global_up_axis, BC_DEFAULT_FORWARD, BC_DEFAULT_UP, mrot); copy_m4_m3(mat, mrot); set_transform(mat); } diff --git a/source/blender/io/common/IO_abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h index 3371501db95..a67cfe6a9d6 100644 --- a/source/blender/io/common/IO_abstract_hierarchy_iterator.h +++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h @@ -59,9 +59,8 @@ struct HierarchyContext { * * The export hierarchy is kept as close to the hierarchy in Blender as possible. As such, an * object that serves as a parent for another object, but which should NOT be exported itself, is - * exported only as transform (i.e. as empty). This happens with objects that are part of a - * holdout collection (which prevents them from being exported) but also parent of an exported - * object. */ + * exported only as transform (i.e. as empty). This happens with objects that are invisible when + * exporting with "Visible Only" enabled, for example. */ bool weak_export; /* When true, this object should check its parents for animation data when determining whether diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc index 82bb1c57833..1fbddc45964 100644 --- a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc +++ b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc @@ -267,6 +267,11 @@ void AbstractHierarchyIterator::export_graph_construct() { Scene *scene = DEG_get_evaluated_scene(depsgraph_); + /* Add a "null" root node with no children immediately for the case where the top-most node in + * the scene is not being exported and a root node otherwise wouldn't get added. */ + ExportGraph::key_type root_node_id = ObjectIdentifier::for_real_object(nullptr); + export_graph_[root_node_id] = ExportChildren(); + DEG_OBJECT_ITER_BEGIN (depsgraph_, object, DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | diff --git a/source/blender/io/stl/importer/stl_import_mesh.cc b/source/blender/io/stl/importer/stl_import_mesh.cc index 5f5c2624414..7de8239b233 100644 --- a/source/blender/io/stl/importer/stl_import_mesh.cc +++ b/source/blender/io/stl/importer/stl_import_mesh.cc @@ -21,30 +21,30 @@ namespace blender::io::stl { -STLMeshHelper::STLMeshHelper(int num_tris, bool use_custom_normals) - : m_use_custom_normals(use_custom_normals) +STLMeshHelper::STLMeshHelper(int tris_num, bool use_custom_normals) + : use_custom_normals_(use_custom_normals) { - m_num_degenerate_tris = 0; - m_num_duplicate_tris = 0; - m_tris.reserve(num_tris); + degenerate_tris_num_ = 0; + duplicate_tris_num_ = 0; + tris_.reserve(tris_num); /* Upper bound (all vertices are unique). */ - m_verts.reserve(num_tris * 3); + verts_.reserve(tris_num * 3); if (use_custom_normals) { - m_loop_normals.reserve(num_tris * 3); + loop_normals_.reserve(tris_num * 3); } } bool STLMeshHelper::add_triangle(const float3 &a, const float3 &b, const float3 &c) { - int v1_id = m_verts.index_of_or_add(a); - int v2_id = m_verts.index_of_or_add(b); - int v3_id = m_verts.index_of_or_add(c); + int v1_id = verts_.index_of_or_add(a); + int v2_id = verts_.index_of_or_add(b); + int v3_id = verts_.index_of_or_add(c); if ((v1_id == v2_id) || (v1_id == v3_id) || (v2_id == v3_id)) { - m_num_degenerate_tris++; + degenerate_tris_num_++; return false; } - if (!m_tris.add({v1_id, v2_id, v3_id})) { - m_num_duplicate_tris++; + if (!tris_.add({v1_id, v2_id, v3_id})) { + duplicate_tris_num_++; return false; } return true; @@ -56,18 +56,18 @@ void STLMeshHelper::add_triangle(const float3 &a, const float3 &custom_normal) { if (add_triangle(a, b, c)) { - m_loop_normals.append_n_times(custom_normal, 3); + loop_normals_.append_n_times(custom_normal, 3); } } Mesh *STLMeshHelper::to_mesh(Main *bmain, char *mesh_name) { - if (m_num_degenerate_tris > 0) { - std::cout << "STL Importer: " << m_num_degenerate_tris << "degenerate triangles were removed" + if (degenerate_tris_num_ > 0) { + std::cout << "STL Importer: " << degenerate_tris_num_ << "degenerate triangles were removed" << std::endl; } - if (m_num_duplicate_tris > 0) { - std::cout << "STL Importer: " << m_num_duplicate_tris << "duplicate triangles were removed" + if (duplicate_tris_num_ > 0) { + std::cout << "STL Importer: " << duplicate_tris_num_ << "duplicate triangles were removed" << std::endl; } @@ -75,36 +75,36 @@ Mesh *STLMeshHelper::to_mesh(Main *bmain, char *mesh_name) /* User count is already 1 here, but will be set later in #BKE_mesh_assign_object. */ id_us_min(&mesh->id); - mesh->totvert = m_verts.size(); + mesh->totvert = verts_.size(); mesh->mvert = static_cast<MVert *>( CustomData_add_layer(&mesh->vdata, CD_MVERT, CD_CALLOC, nullptr, mesh->totvert)); for (int i = 0; i < mesh->totvert; i++) { - copy_v3_v3(mesh->mvert[i].co, m_verts[i]); + copy_v3_v3(mesh->mvert[i].co, verts_[i]); } - mesh->totpoly = m_tris.size(); - mesh->totloop = m_tris.size() * 3; + mesh->totpoly = tris_.size(); + mesh->totloop = tris_.size() * 3; mesh->mpoly = static_cast<MPoly *>( CustomData_add_layer(&mesh->pdata, CD_MPOLY, CD_CALLOC, nullptr, mesh->totpoly)); mesh->mloop = static_cast<MLoop *>( CustomData_add_layer(&mesh->ldata, CD_MLOOP, CD_CALLOC, nullptr, mesh->totloop)); - threading::parallel_for(m_tris.index_range(), 2048, [&](IndexRange tris_range) { + threading::parallel_for(tris_.index_range(), 2048, [&](IndexRange tris_range) { for (const int i : tris_range) { mesh->mpoly[i].loopstart = 3 * i; mesh->mpoly[i].totloop = 3; - mesh->mloop[3 * i].v = m_tris[i].v1; - mesh->mloop[3 * i + 1].v = m_tris[i].v2; - mesh->mloop[3 * i + 2].v = m_tris[i].v3; + mesh->mloop[3 * i].v = tris_[i].v1; + mesh->mloop[3 * i + 1].v = tris_[i].v2; + mesh->mloop[3 * i + 2].v = tris_[i].v3; } }); /* NOTE: edges must be calculated first before setting custom normals. */ BKE_mesh_calc_edges(mesh, false, false); - if (m_use_custom_normals && m_loop_normals.size() == mesh->totloop) { - BKE_mesh_set_custom_normals(mesh, reinterpret_cast<float(*)[3]>(m_loop_normals.data())); + if (use_custom_normals_ && loop_normals_.size() == mesh->totloop) { + BKE_mesh_set_custom_normals(mesh, reinterpret_cast<float(*)[3]>(loop_normals_.data())); mesh->flag |= ME_AUTOSMOOTH; } diff --git a/source/blender/io/stl/importer/stl_import_mesh.hh b/source/blender/io/stl/importer/stl_import_mesh.hh index a01e5be09a4..f1c0d2126a9 100644 --- a/source/blender/io/stl/importer/stl_import_mesh.hh +++ b/source/blender/io/stl/importer/stl_import_mesh.hh @@ -47,15 +47,15 @@ class Triangle { class STLMeshHelper { private: - VectorSet<float3> m_verts; - VectorSet<Triangle> m_tris; - Vector<float3> m_loop_normals; - int m_num_degenerate_tris; - int m_num_duplicate_tris; - const bool m_use_custom_normals; + VectorSet<float3> verts_; + VectorSet<Triangle> tris_; + Vector<float3> loop_normals_; + int degenerate_tris_num_; + int duplicate_tris_num_; + const bool use_custom_normals_; public: - STLMeshHelper(int num_tris, bool use_custom_normals); + STLMeshHelper(int tris_num, bool use_custom_normals); /* Creates a new triangle from specified vertex locations, * duplicate vertices and triangles are merged. diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index 2c86bfff4da..368d0e1bab9 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -62,11 +62,56 @@ static void build_mat_map(const Main *bmain, std::map<std::string, Material *> * } } +static pxr::UsdShadeMaterial compute_bound_material(const pxr::UsdPrim &prim) +{ + return pxr::UsdShadeMaterialBindingAPI(prim).ComputeBoundMaterial(); +} + +/* Returns an existing Blender material that corresponds to the USD + * material with with the given path. Returns null if no such material + * exists. */ +static Material *find_existing_material( + const pxr::SdfPath &usd_mat_path, + const USDImportParams ¶ms, + const std::map<std::string, Material *> &mat_map, + const std::map<std::string, std::string> &usd_path_to_mat_name) +{ + if (params.mtl_name_collision_mode == USD_MTL_NAME_COLLISION_MAKE_UNIQUE) { + /* Check if we've already created the Blender material with a modified name. */ + std::map<std::string, std::string>::const_iterator path_to_name_iter = + usd_path_to_mat_name.find(usd_mat_path.GetAsString()); + + if (path_to_name_iter != usd_path_to_mat_name.end()) { + std::string mat_name = path_to_name_iter->second; + std::map<std::string, Material *>::const_iterator mat_iter = mat_map.find(mat_name); + if (mat_iter != mat_map.end()) { + return mat_iter->second; + } + else { + /* We can't find the Blender material which was previously created for this USD + * material, which should never happen. */ + BLI_assert_unreachable(); + } + } + } + else { + std::string mat_name = usd_mat_path.GetName(); + std::map<std::string, Material *>::const_iterator mat_iter = mat_map.find(mat_name); + + if (mat_iter != mat_map.end()) { + return mat_iter->second; + } + } + + return nullptr; +} + static void assign_materials(Main *bmain, Object *ob, const std::map<pxr::SdfPath, int> &mat_index_map, const USDImportParams ¶ms, - pxr::UsdStageRefPtr stage) + pxr::UsdStageRefPtr stage, + std::map<std::string, std::string> &usd_path_to_mat_name) { if (!(stage && bmain && ob)) { return; @@ -94,13 +139,10 @@ static void assign_materials(Main *bmain, blender::io::usd::USDMaterialReader mat_reader(params, bmain); for (it = mat_index_map.begin(); it != mat_index_map.end(); ++it) { - std::string mat_name = it->first.GetName(); - - std::map<std::string, Material *>::iterator mat_iter = mat_map.find(mat_name); - - Material *assigned_mat = nullptr; - if (mat_iter == mat_map.end()) { + Material *assigned_mat = find_existing_material( + it->first, params, mat_map, usd_path_to_mat_name); + if (!assigned_mat) { /* Blender material doesn't exist, so create it now. */ /* Look up the USD material. */ @@ -122,11 +164,14 @@ static void assign_materials(Main *bmain, continue; } + const std::string mat_name = pxr::TfMakeValidIdentifier(assigned_mat->id.name + 2); mat_map[mat_name] = assigned_mat; - } - else { - /* We found an existing Blender material. */ - assigned_mat = mat_iter->second; + + if (params.mtl_name_collision_mode == USD_MTL_NAME_COLLISION_MAKE_UNIQUE) { + /* Record the name of the Blender material we created for the USD material + * with the given path. */ + usd_path_to_mat_name[it->first.GetAsString()] = mat_name; + } } if (assigned_mat) { @@ -134,7 +179,7 @@ static void assign_materials(Main *bmain, } else { /* This shouldn't happen. */ - std::cout << "WARNING: Couldn't assign material " << mat_name << std::endl; + std::cout << "WARNING: Couldn't assign material " << it->first << std::endl; } } } @@ -710,11 +755,8 @@ void USDMeshReader::assign_facesets_to_mpoly(double motionSampleTime, int current_mat = 0; if (!subsets.empty()) { for (const pxr::UsdGeomSubset &subset : subsets) { - pxr::UsdShadeMaterialBindingAPI subset_api = pxr::UsdShadeMaterialBindingAPI( - subset.GetPrim()); - - pxr::UsdShadeMaterial subset_mtl = subset_api.ComputeBoundMaterial(); + pxr::UsdShadeMaterial subset_mtl = utils::compute_bound_material(subset.GetPrim()); if (!subset_mtl) { continue; } @@ -743,10 +785,9 @@ void USDMeshReader::assign_facesets_to_mpoly(double motionSampleTime, } if (r_mat_map->empty()) { - pxr::UsdShadeMaterialBindingAPI api = pxr::UsdShadeMaterialBindingAPI(prim_); - - if (pxr::UsdShadeMaterial mtl = api.ComputeBoundMaterial()) { + pxr::UsdShadeMaterial mtl = utils::compute_bound_material(prim_); + if (mtl) { pxr::SdfPath mtl_path = mtl.GetPath(); if (!mtl_path.IsEmpty()) { @@ -764,7 +805,12 @@ void USDMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const double mot std::map<pxr::SdfPath, int> mat_map; assign_facesets_to_mpoly(motionSampleTime, mesh->mpoly, mesh->totpoly, &mat_map); - utils::assign_materials(bmain, object_, mat_map, this->import_params_, this->prim_.GetStage()); + utils::assign_materials(bmain, + object_, + mat_map, + this->import_params_, + this->prim_.GetStage(), + this->settings_->usd_path_to_mat_name); } Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh, diff --git a/source/blender/io/usd/intern/usd_reader_prim.h b/source/blender/io/usd/intern/usd_reader_prim.h index fdf6247de16..f2df00accf6 100644 --- a/source/blender/io/usd/intern/usd_reader_prim.h +++ b/source/blender/io/usd/intern/usd_reader_prim.h @@ -7,6 +7,9 @@ #include <pxr/usd/usd/prim.h> +#include <map> +#include <string> + struct Main; struct Object; @@ -33,6 +36,13 @@ struct ImportSettings { CacheFile *cache_file; + /* Map a USD material prim path to a Blender material name. + * This map is updated by readers during stage traversal. + * This field is mutable because it is used to keep track + * of what the importer is doing. This is necessary even + * when all the other import settings are to remain const. */ + mutable std::map<std::string, std::string> usd_path_to_mat_name; + ImportSettings() : do_convert_mat(false), from_up(0), diff --git a/source/blender/io/usd/intern/usd_writer_material.cc b/source/blender/io/usd/intern/usd_writer_material.cc index 857896b9330..6862f3835cf 100644 --- a/source/blender/io/usd/intern/usd_writer_material.cc +++ b/source/blender/io/usd/intern/usd_writer_material.cc @@ -590,6 +590,7 @@ static std::string get_tex_image_asset_path(bNode *node, BLI_split_dir_part(stage_path.c_str(), dir_path, FILE_MAX); BLI_path_join(exp_path, FILE_MAX, dir_path, "textures", file_path, nullptr); } + BLI_str_replace_char(exp_path, '\\', '/'); return exp_path; } @@ -608,7 +609,7 @@ static std::string get_tex_image_asset_path(bNode *node, if (!BLI_path_is_rel(rel_path)) { return path; } - + BLI_str_replace_char(rel_path, '\\', '/'); return rel_path + 2; } diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h index 2e4dcb0da94..a07315d8b4e 100644 --- a/source/blender/io/usd/usd.h +++ b/source/blender/io/usd/usd.h @@ -15,6 +15,13 @@ struct CacheReader; struct Object; struct bContext; +/* Behavior when the name of an imported material + * conflicts with an existing material. */ +typedef enum eUSDMtlNameCollisionMode { + USD_MTL_NAME_COLLISION_MAKE_UNIQUE = 0, + USD_MTL_NAME_COLLISION_REFERENCE_EXISTING = 1, +} eUSDMtlNameCollisionMode; + struct USDExportParams { bool export_animation; bool export_hair; @@ -57,6 +64,7 @@ struct USDImportParams { bool import_usd_preview; bool set_material_blend; float light_intensity_scale; + eUSDMtlNameCollisionMode mtl_name_collision_mode; }; /* The USD_export takes a as_background_job parameter, and returns a boolean. diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.h b/source/blender/io/wavefront_obj/IO_wavefront_obj.h index 0a78cdc714d..a719dff2126 100644 --- a/source/blender/io/wavefront_obj/IO_wavefront_obj.h +++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.h @@ -45,6 +45,7 @@ struct OBJExportParams { eEvaluationMode export_eval_mode; bool export_uv; bool export_normals; + bool export_colors; bool export_materials; bool export_triangulated_mesh; bool export_curves_as_nurbs; diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc index 11d1bafdafe..cb95c561547 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc @@ -8,7 +8,9 @@ #include <cstdio> #include "BKE_blender_version.h" +#include "BKE_geometry_set.hh" +#include "BLI_color.hh" #include "BLI_enumerable_thread_specific.hh" #include "BLI_path_util.h" #include "BLI_task.hh" @@ -241,13 +243,38 @@ void obj_parallel_chunked_output(FormatHandler<eFileType::OBJ> &fh, } void OBJWriter::write_vertex_coords(FormatHandler<eFileType::OBJ> &fh, - const OBJMesh &obj_mesh_data) const + const OBJMesh &obj_mesh_data, + bool write_colors) const { const int tot_count = obj_mesh_data.tot_vertices(); - obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) { - float3 vertex = obj_mesh_data.calc_vertex_coords(i, export_params_.scaling_factor); - buf.write<eOBJSyntaxElement::vertex_coords>(vertex[0], vertex[1], vertex[2]); - }); + + Mesh *mesh = obj_mesh_data.get_mesh(); + CustomDataLayer *colors_layer = nullptr; + if (write_colors) { + colors_layer = BKE_id_attributes_active_color_get(&mesh->id); + } + if (write_colors && (colors_layer != nullptr)) { + MeshComponent component; + component.replace(mesh, GeometryOwnershipType::ReadOnly); + VArray<ColorGeometry4f> attribute = component.attribute_get_for_read<ColorGeometry4f>( + colors_layer->name, ATTR_DOMAIN_POINT, {0.0f, 0.0f, 0.0f, 0.0f}); + + BLI_assert(tot_count == attribute.size()); + obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) { + float3 vertex = obj_mesh_data.calc_vertex_coords(i, export_params_.scaling_factor); + ColorGeometry4f linear = attribute.get(i); + float srgb[3]; + linearrgb_to_srgb_v3_v3(srgb, linear); + buf.write<eOBJSyntaxElement::vertex_coords_color>( + vertex[0], vertex[1], vertex[2], srgb[0], srgb[1], srgb[2]); + }); + } + else { + obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) { + float3 vertex = obj_mesh_data.calc_vertex_coords(i, export_params_.scaling_factor); + buf.write<eOBJSyntaxElement::vertex_coords>(vertex[0], vertex[1], vertex[2]); + }); + } } void OBJWriter::write_uv_coords(FormatHandler<eFileType::OBJ> &fh, OBJMesh &r_obj_mesh_data) const diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh index 77da7b44276..97c23484426 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh @@ -72,9 +72,11 @@ class OBJWriter : NonMovable, NonCopyable { */ void write_mtllib_name(const StringRefNull mtl_filepath) const; /** - * Write vertex coordinates for all vertices as "v x y z". + * Write vertex coordinates for all vertices as "v x y z" or "v x y z r g b". */ - void write_vertex_coords(FormatHandler<eFileType::OBJ> &fh, const OBJMesh &obj_mesh_data) const; + void write_vertex_coords(FormatHandler<eFileType::OBJ> &fh, + const OBJMesh &obj_mesh_data, + bool write_colors) const; /** * Write UV vertex coordinates for all vertices as `vt u v`. * \note UV indices are stored here, but written with polygons later. diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh index f0263989bfc..5413c9969e3 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh @@ -30,6 +30,7 @@ enum class eFileType { enum class eOBJSyntaxElement { vertex_coords, + vertex_coords_color, uv_vertex_coords, normal, poly_element_begin, @@ -130,6 +131,9 @@ constexpr FormattingSyntax syntax_elem_to_formatting(const eOBJSyntaxElement key case eOBJSyntaxElement::vertex_coords: { return {"v {:.6f} {:.6f} {:.6f}\n", 3, is_type_float<T...>}; } + case eOBJSyntaxElement::vertex_coords_color: { + return {"v {:.6f} {:.6f} {:.6f} {:.4f} {:.4f} {:.4f}\n", 6, is_type_float<T...>}; + } case eOBJSyntaxElement::uv_vertex_coords: { return {"vt {:.6f} {:.6f}\n", 2, is_type_float<T...>}; } diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc index 8556d7c275c..e2ecda32717 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc @@ -122,9 +122,7 @@ void OBJMesh::set_world_axes_transform(const eIOAxis forward, const eIOAxis up) float axes_transform[3][3]; unit_m3(axes_transform); /* +Y-forward and +Z-up are the default Blender axis settings. */ - mat3_from_axis_conversion(IO_AXIS_Y, IO_AXIS_Z, forward, up, axes_transform); - /* mat3_from_axis_conversion returns a transposed matrix! */ - transpose_m3(axes_transform); + mat3_from_axis_conversion(forward, up, IO_AXIS_Y, IO_AXIS_Z, axes_transform); mul_m4_m3m4(world_and_axes_transform_, axes_transform, export_object_eval_.obmat); /* mul_m4_m3m4 does not transform last row of obmat, i.e. location data. */ mul_v3_m3v3(world_and_axes_transform_[3], axes_transform, export_object_eval_.obmat[3]); diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh index 91213ec8152..ee2e6227700 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh @@ -241,6 +241,11 @@ class OBJMesh : NonCopyable { return i < 0 || i >= poly_order_.size() ? i : poly_order_[i]; } + Mesh *get_mesh() const + { + return export_mesh_eval_; + } + private: /** * Free the mesh if _the exporter_ created it. diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc b/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc index c0c29fcfdf8..172a59e5341 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc @@ -30,9 +30,7 @@ void OBJCurve::set_world_axes_transform(const eIOAxis forward, const eIOAxis up) float axes_transform[3][3]; unit_m3(axes_transform); /* +Y-forward and +Z-up are the Blender's default axis settings. */ - mat3_from_axis_conversion(IO_AXIS_Y, IO_AXIS_Z, forward, up, axes_transform); - /* mat3_from_axis_conversion returns a transposed matrix! */ - transpose_m3(axes_transform); + mat3_from_axis_conversion(forward, up, IO_AXIS_Y, IO_AXIS_Z, axes_transform); mul_m4_m3m4(world_axes_transform_, axes_transform, export_object_eval_->obmat); /* #mul_m4_m3m4 does not transform last row of #Object.obmat, i.e. location data. */ mul_v3_m3v3(world_axes_transform_[3], axes_transform, export_object_eval_->obmat[3]); diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc index b6e636b389d..b0938084efb 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc @@ -195,7 +195,7 @@ static void write_mesh_objects(Vector<std::unique_ptr<OBJMesh>> exportable_as_me auto &fh = buffers[i]; obj_writer.write_object_name(fh, obj); - obj_writer.write_vertex_coords(fh, obj); + obj_writer.write_vertex_coords(fh, obj, export_params.export_colors); if (obj.tot_polygons() > 0) { if (export_params.export_smooth_groups) { diff --git a/source/blender/io/wavefront_obj/importer/importer_mesh_utils.cc b/source/blender/io/wavefront_obj/importer/importer_mesh_utils.cc index 3c0ef646e13..f33753d720d 100644 --- a/source/blender/io/wavefront_obj/importer/importer_mesh_utils.cc +++ b/source/blender/io/wavefront_obj/importer/importer_mesh_utils.cc @@ -100,9 +100,7 @@ void transform_object(Object *object, const OBJImportParams &import_params) unit_m4(obmat); /* +Y-forward and +Z-up are the default Blender axis settings. */ mat3_from_axis_conversion( - import_params.forward_axis, import_params.up_axis, IO_AXIS_Y, IO_AXIS_Z, axes_transform); - /* mat3_from_axis_conversion returns a transposed matrix! */ - transpose_m3(axes_transform); + IO_AXIS_Y, IO_AXIS_Z, import_params.forward_axis, import_params.up_axis, axes_transform); copy_m4_m3(obmat, axes_transform); BKE_object_apply_mat4(object, obmat, true, false); diff --git a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc index a627e7261e3..ee55dd1e45a 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc @@ -5,12 +5,15 @@ */ #include "BLI_map.hh" +#include "BLI_math_color.h" #include "BLI_string_ref.hh" #include "BLI_vector.hh" #include "obj_import_file_reader.hh" #include "obj_import_string_utils.hh" +#include <charconv> + namespace blender::io::obj { using std::string; @@ -34,6 +37,7 @@ static Geometry *create_geometry(Geometry *const prev_geometry, g->geom_type_ = new_type; g->geometry_name_ = name.is_empty() ? "New object" : name; g->vertex_start_ = global_vertices.vertices.size(); + g->vertex_color_start_ = global_vertices.vertex_colors.size(); r_offset.set_index_offset(g->vertex_start_); return g; }; @@ -71,9 +75,51 @@ static void geom_add_vertex(Geometry *geom, GlobalVertices &r_global_vertices) { float3 vert; - parse_floats(p, end, 0.0f, vert, 3); + p = parse_floats(p, end, 0.0f, vert, 3); r_global_vertices.vertices.append(vert); geom->vertex_count_++; + /* OBJ extension: `xyzrgb` vertex colors, when the vertex position + * is followed by 3 more RGB color components. See + * http://paulbourke.net/dataformats/obj/colour.html */ + if (p < end) { + float3 srgb; + p = parse_floats(p, end, -1.0f, srgb, 3); + if (srgb.x >= 0 && srgb.y >= 0 && srgb.z >= 0) { + float3 linear; + srgb_to_linearrgb_v3_v3(linear, srgb); + r_global_vertices.vertex_colors.append(linear); + geom->vertex_color_count_++; + } + } +} + +static void geom_add_mrgb_colors(Geometry *geom, + const char *p, + const char *end, + GlobalVertices &r_global_vertices) +{ + /* MRGB color extension, in the form of + * "#MRGB MMRRGGBBMMRRGGBB ..." + * http://paulbourke.net/dataformats/obj/colour.html */ + p = drop_whitespace(p, end); + const int mrgb_length = 8; + while (p + mrgb_length <= end) { + uint32_t value = 0; + std::from_chars_result res = std::from_chars(p, p + mrgb_length, value, 16); + if (res.ec == std::errc::invalid_argument || res.ec == std::errc::result_out_of_range) { + return; + } + unsigned char srgb[4]; + srgb[0] = (value >> 16) & 0xFF; + srgb[1] = (value >> 8) & 0xFF; + srgb[2] = value & 0xFF; + srgb[3] = 0xFF; + float linear[4]; + srgb_to_linearrgb_uchar4(linear, srgb); + r_global_vertices.vertex_colors.append({linear[0], linear[1], linear[2]}); + geom->vertex_color_count_++; + p += mrgb_length; + } } static void geom_add_vertex_normal(Geometry *geom, @@ -149,7 +195,7 @@ static void geom_add_polygon(Geometry *geom, if (p < end && *p == '/') { ++p; p = parse_int(p, end, INT32_MAX, corner.vertex_normal_index, false); - got_normal = corner.uv_vert_index != INT32_MAX; + got_normal = corner.vertex_normal_index != INT32_MAX; } } /* Always keep stored indices non-negative and zero-based. */ @@ -172,7 +218,10 @@ static void geom_add_polygon(Geometry *geom, face_valid = false; } } - if (got_normal) { + /* Ignore corner normal index, if the geometry does not have any normals. + * Some obj files out there do have face definitions that refer to normal indices, + * without any normals being present (T98782). */ + if (got_normal && geom->has_vertex_normals_) { corner.vertex_normal_index += corner.vertex_normal_index < 0 ? global_vertices.vertex_normals.size() : -1; @@ -482,6 +531,9 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, else if (parse_keyword(p, end, "mtllib")) { add_mtl_library(StringRef(p, end).trim()); } + else if (parse_keyword(p, end, "#MRGB")) { + geom_add_mrgb_colors(curr_geom, p, end, r_global_vertices); + } /* Comments. */ else if (*p == '#') { /* Nothing to do. */ diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index 8d560bd2c8c..acc35ad46e1 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -8,6 +8,7 @@ #include "DNA_mesh_types.h" #include "DNA_scene_types.h" +#include "BKE_attribute.h" #include "BKE_customdata.h" #include "BKE_material.h" #include "BKE_mesh.h" @@ -50,6 +51,7 @@ Object *MeshFromGeometry::create_mesh(Main *bmain, create_edges(mesh); create_uv_verts(mesh); create_normals(mesh); + create_colors(mesh); create_materials(bmain, materials, created_materials, obj); if (import_params.validate_meshes || mesh_geometry_.has_invalid_polys_) { @@ -289,7 +291,7 @@ static Material *get_or_create_material(Main *bmain, /* We have not, will have to create it. Create a new default * MTLMaterial too, in case the OBJ file tries to use a material * that was not in the MTL file. */ - const MTLMaterial &mtl = *materials.lookup_or_add(name, std::make_unique<MTLMaterial>()).get(); + const MTLMaterial &mtl = *materials.lookup_or_add(name, std::make_unique<MTLMaterial>()); Material *mat = BKE_material_add(bmain, name.c_str()); ShaderNodetreeWrap mat_wrap{bmain, mtl, mat}; @@ -345,4 +347,26 @@ void MeshFromGeometry::create_normals(Mesh *mesh) MEM_freeN(loop_normals); } +void MeshFromGeometry::create_colors(Mesh *mesh) +{ + /* Nothing to do if we don't have vertex colors. */ + if (mesh_geometry_.vertex_color_count_ < 1) { + return; + } + if (mesh_geometry_.vertex_color_count_ != mesh_geometry_.vertex_count_) { + std::cerr << "Mismatching number of vertices (" << mesh_geometry_.vertex_count_ + << ") and colors (" << mesh_geometry_.vertex_color_count_ << ") on object '" + << mesh_geometry_.geometry_name_ << "', ignoring colors." << std::endl; + return; + } + + CustomDataLayer *color_layer = BKE_id_attribute_new( + &mesh->id, "Color", CD_PROP_COLOR, ATTR_DOMAIN_POINT, nullptr); + float4 *colors = (float4 *)color_layer->data; + for (int i = 0; i < mesh_geometry_.vertex_color_count_; ++i) { + float3 c = global_vertices_.vertex_colors[mesh_geometry_.vertex_color_start_ + i]; + colors[i] = float4(c.x, c.y, c.z, 1.0f); + } +} + } // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh b/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh index cf4a2aee394..216717f3578 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh @@ -65,6 +65,7 @@ class MeshFromGeometry : NonMovable, NonCopyable { Map<std::string, Material *> &created_materials, Object *obj); void create_normals(Mesh *mesh); + void create_colors(Mesh *mesh); }; } // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh index b67ba46af03..69babc26bb0 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh @@ -26,6 +26,7 @@ struct GlobalVertices { Vector<float3> vertices; Vector<float2> uv_vertices; Vector<float3> vertex_normals; + Vector<float3> vertex_colors; }; /** @@ -102,6 +103,8 @@ struct Geometry { int vertex_start_ = 0; int vertex_count_ = 0; + int vertex_color_start_ = 0; + int vertex_color_count_ = 0; /** Edges written in the file in addition to (or even without polygon) elements. */ Vector<MEdge> edges_; diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc index b384d934c82..6aec848573f 100644 --- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc @@ -436,6 +436,19 @@ TEST_F(obj_exporter_regression_test, cubes_positioned) _export.params); } +TEST_F(obj_exporter_regression_test, cubes_vertex_colors) +{ + OBJExportParamsDefault _export; + _export.params.export_colors = true; + _export.params.export_normals = false; + _export.params.export_uv = false; + _export.params.export_materials = false; + compare_obj_export_to_golden("io_tests/blend_geometry/cubes_vertex_colors.blend", + "io_tests/obj/cubes_vertex_colors.obj", + "", + _export.params); +} + TEST_F(obj_exporter_regression_test, cubes_with_textures_strip) { OBJExportParamsDefault _export; @@ -494,6 +507,7 @@ TEST_F(obj_exporter_regression_test, all_objects) _export.params.forward_axis = IO_AXIS_Y; _export.params.up_axis = IO_AXIS_Z; _export.params.export_smooth_groups = true; + _export.params.export_colors = true; compare_obj_export_to_golden("io_tests/blend_scene/all_objects.blend", "io_tests/obj/all_objects.obj", "io_tests/obj/all_objects.mtl", diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh index 42f7620999d..7d3b41ed527 100644 --- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh +++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh @@ -26,6 +26,7 @@ struct OBJExportParamsDefault { params.export_selected_objects = false; params.export_uv = true; params.export_normals = true; + params.export_colors = false; params.export_materials = true; params.path_mode = PATH_REFERENCE_AUTO; params.export_triangulated_mesh = false; diff --git a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc index 6d09f522028..b67adbc9753 100644 --- a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc @@ -39,6 +39,7 @@ struct Expectation { float3 vert_first, vert_last; float3 normal_first; float2 uv_first; + float4 color_first = {-1, -1, -1, -1}; }; class obj_importer_test : public BlendfileLoadingBaseTest { @@ -98,6 +99,15 @@ class obj_importer_test : public BlendfileLoadingBaseTest { CustomData_get_layer(&mesh->ldata, CD_MLOOPUV)); float2 uv_first = mloopuv ? float2(mloopuv->uv) : float2(0, 0); EXPECT_V2_NEAR(uv_first, exp.uv_first, 0.0001f); + if (exp.color_first.x >= 0) { + const float4 *colors = (const float4 *)(CustomData_get_layer(&mesh->vdata, + CD_PROP_COLOR)); + EXPECT_TRUE(colors != nullptr); + EXPECT_V4_NEAR(colors[0], exp.color_first, 0.0001f); + } + else { + EXPECT_FALSE(CustomData_has_layer(&mesh->vdata, CD_PROP_COLOR)); + } } if (object->type == OB_CURVES_LEGACY) { Curve *curve = static_cast<Curve *>(DEG_get_evaluated_object(depsgraph, object)->data); @@ -434,7 +444,17 @@ TEST_F(obj_importer_test, import_all_objects) float3(16, 1, -1), float3(14, 1, 1), float3(0, 0, 1)}, - {"OBVColCube", OB_MESH, 8, 13, 7, 26, float3(13, 1, -1), float3(11, 1, 1), float3(0, 0, 1)}, + {"OBVColCube", + OB_MESH, + 8, + 13, + 7, + 26, + float3(13, 1, -1), + float3(11, 1, 1), + float3(0, 0, 1), + float2(0, 0), + float4(0.0f, 0.002125f, 1.0f, 1.0f)}, {"OBUVCube", OB_MESH, 8, @@ -490,4 +510,103 @@ TEST_F(obj_importer_test, import_all_objects) import_and_check("all_objects.obj", expect, std::size(expect), 7); } +TEST_F(obj_importer_test, import_cubes_vertex_colors) +{ + Expectation expect[] = { + {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)}, + {"OBCubeVertexByte", + OB_MESH, + 8, + 12, + 6, + 24, + float3(1.0f, 1.0f, -1.0f), + float3(-1.0f, -1.0f, 1.0f), + float3(0, 0, 0), + float2(0, 0), + float4(0.846873f, 0.027321f, 0.982123f, 1.0f)}, + {"OBCubeVertexFloat", + OB_MESH, + 8, + 12, + 6, + 24, + float3(3.392028f, 1.0f, -1.0f), + float3(1.392028f, -1.0f, 1.0f), + float3(0, 0, 0), + float2(0, 0), + float4(49.99467f, 0.027321f, 0.982123f, 1.0f)}, + {"OBCubeCornerByte", + OB_MESH, + 8, + 12, + 6, + 24, + float3(1.0f, 1.0f, -3.812445f), + float3(-1.0f, -1.0f, -1.812445f), + float3(0, 0, 0), + float2(0, 0), + float4(0.89627f, 0.036889f, 0.47932f, 1.0f)}, + {"OBCubeCornerFloat", + OB_MESH, + 8, + 12, + 6, + 24, + float3(3.481967f, 1.0f, -3.812445f), + float3(1.481967f, -1.0f, -1.812445f), + float3(0, 0, 0), + float2(0, 0), + float4(1.564582f, 0.039217f, 0.664309f, 1.0f)}, + {"OBCubeMultiColorAttribs", + OB_MESH, + 8, + 12, + 6, + 24, + float3(-4.725068f, -1.0f, 1.0f), + float3(-2.725068f, 1.0f, -1.0f), + float3(0, 0, 0), + float2(0, 0), + float4(0.270498f, 0.47932f, 0.262251f, 1.0f)}, + {"OBCubeNoColors", + OB_MESH, + 8, + 12, + 6, + 24, + float3(-4.550208f, -1.0f, -1.918042f), + float3(-2.550208f, 1.0f, -3.918042f)}, + }; + import_and_check("cubes_vertex_colors.obj", expect, std::size(expect), 0); +} + +TEST_F(obj_importer_test, import_cubes_vertex_colors_mrgb) +{ + Expectation expect[] = {{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)}, + {"OBCubeXYZRGB", + OB_MESH, + 8, + 12, + 6, + 24, + float3(1, 1, -1), + float3(-1, -1, 1), + float3(0, 0, 0), + float2(0, 0), + float4(0.6038f, 0.3185f, 0.1329f, 1.0f)}, + {"OBCubeMRGB", + OB_MESH, + 8, + 12, + 6, + 24, + float3(4, 1, -1), + float3(2, -1, 1), + float3(0, 0, 0), + float2(0, 0), + float4(0.8714f, 0.6308f, 0.5271f, 1.0f)}}; + import_and_check("cubes_vertex_colors_mrgb.obj", expect, std::size(expect), 0); +} + } // namespace blender::io::obj diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index 6557f35970d..ad046cbe79a 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -247,10 +247,9 @@ typedef struct bArmatureConstraint { typedef struct bTrackToConstraint { struct Object *tar; /** - * I'll be using reserved1 and reserved2 as Track and Up flags, + * NOTE(@theeth): I'll be using reserved1 and reserved2 as Track and Up flags, * not sure if that's what they were intended for anyway. * Not sure either if it would create backward incompatibility if I were to rename them. - * - theeth */ int reserved1; int reserved2; diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index c20fb180fcd..56963bae3e1 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -1098,7 +1098,7 @@ typedef struct LineartGpencilModifierData { struct LineartCache *cache; /* Keep a pointer to the render buffer so we can call destroy from ModifierData. */ - struct LineartRenderBuffer *render_buffer_ptr; + struct LineartData *la_data_ptr; } LineartGpencilModifierData; diff --git a/source/blender/makesdna/DNA_ipo_types.h b/source/blender/makesdna/DNA_ipo_types.h index 70ee7c99d01..ef35b72d2ab 100644 --- a/source/blender/makesdna/DNA_ipo_types.h +++ b/source/blender/makesdna/DNA_ipo_types.h @@ -338,7 +338,7 @@ typedef struct Ipo { #define CAM_STA 2 #define CAM_END 3 -/* yafray aperture & focal distance curves */ +/* YAFRAY aperture & focal distance curves. */ #define CAM_YF_APERT 4 #define CAM_YF_FDIST 5 diff --git a/source/blender/makesdna/DNA_light_types.h b/source/blender/makesdna/DNA_light_types.h index 9202d7c2d51..f1bf0580b94 100644 --- a/source/blender/makesdna/DNA_light_types.h +++ b/source/blender/makesdna/DNA_light_types.h @@ -120,7 +120,7 @@ typedef struct Light { /* #define LA_NO_DIFF (1 << 11) */ /* not used anywhere */ /* #define LA_NO_SPEC (1 << 12) */ /* not used anywhere */ /* #define LA_SHAD_RAY (1 << 13) */ /* not used anywhere - cleaned */ -/* yafray: light shadowbuffer flag, softlight */ +/* YAFRAY: light shadow-buffer flag, soft-light. */ /* Since it is used with LOCAL light, can't use LA_SHAD */ /* #define LA_YF_SOFT (1 << 14) */ /* not used anymore */ /* #define LA_LAYER_SHADOW (1 << 15) */ /* not used anymore */ diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h index df496269644..444a0e6f247 100644 --- a/source/blender/makesdna/DNA_lineart_types.h +++ b/source/blender/makesdna/DNA_lineart_types.h @@ -14,7 +14,7 @@ * Edge flags and usage flags are used by with scene/object/gpencil modifier bits, and those values * needs to stay consistent throughout. */ -/* These flags are used for 1 time calculation, not stroke selection afterwards. */ +/** These flags are used for 1 time calculation, not stroke selection afterwards. */ typedef enum eLineartMainFlags { LRT_INTERSECTION_AS_CONTOUR = (1 << 0), LRT_EVERYTHING_AS_CONTOUR = (1 << 1), @@ -48,9 +48,11 @@ typedef enum eLineartEdgeFlag { LRT_EDGE_FLAG_INTERSECTION = (1 << 4), LRT_EDGE_FLAG_LOOSE = (1 << 5), /* LRT_EDGE_FLAG_FOR_FUTURE = (1 << 7), */ - /* Limited to 8 bits for edge type flag, don't add anymore because `BMEdge->head.eflag` only has - * 8 bits. So unless we changed this into a non-single-bit flag thing, we keep it this way. */ - /** Also used as discarded line mark. */ + /** + * It's a legacy limit of 8 bits for feature lines that come from original mesh edges. It should + * not be needed in current object loading scheme, but might still be relevant if we are to + * implement edit-mesh loading, so don't exceed 8 bits just yet. + */ LRT_EDGE_FLAG_CHAIN_PICKED = (1 << 8), LRT_EDGE_FLAG_CLIPPED = (1 << 9), /** Limited to 16 bits for the entire thing. */ diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index a66badb4a68..2eca84959b8 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -32,6 +32,7 @@ struct MVert; struct Material; struct Mesh; struct SubdivCCG; +struct SubsurfRuntimeData; # # @@ -123,26 +124,18 @@ typedef struct Mesh_Runtime { /** * Settings for lazily evaluating the subdivision on the CPU if needed. These are - * set in the modifier when GPU subdivision can be performed. + * set in the modifier when GPU subdivision can be performed, and owned by the by + * the modifier in the object. */ - SessionUUID subsurf_session_uuid; - char subsurf_resolution; - char subsurf_do_loop_normals; - char subsurf_apply_render; - char subsurf_use_optimal_display; - - /* Cached from the draw code for stats display. */ - int subsurf_totvert; - int subsurf_totedge; - int subsurf_totpoly; - int subsurf_totloop; - char _pad2[2]; + struct SubsurfRuntimeData *subsurf_runtime_data; + void *_pad1; /** * Caches for lazily computed vertex and polygon normals. These are stored here rather than in * #CustomData because they can be calculated on a const mesh, and adding custom data layers on a * const mesh is not thread-safe. */ + char _pad2[6]; char vert_normals_dirty; char poly_normals_dirty; float (*vert_normals)[3]; diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 6e3ce7e98a8..f148116eba8 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -178,13 +178,6 @@ typedef enum { SUBSURF_BOUNDARY_SMOOTH_PRESERVE_CORNERS = 1, } eSubsurfBoundarySmooth; -typedef struct SubsurfRuntimeData { - /* Cached subdivision surface descriptor, with topology and settings. */ - struct Subdiv *subdiv; - char set_by_draw_code; - char _pad[7]; -} SubsurfRuntimeData; - typedef struct SubsurfModifierData { ModifierData modifier; diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index e449605ed81..d00826208be 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -146,6 +146,7 @@ typedef struct ThemeUI { uiWidgetColors wcol_num, wcol_numslider, wcol_tab; uiWidgetColors wcol_menu, wcol_pulldown, wcol_menu_back, wcol_menu_item, wcol_tooltip; uiWidgetColors wcol_box, wcol_scroll, wcol_progress, wcol_list_item, wcol_pie_menu; + uiWidgetColors wcol_view_item; uiWidgetStateColors wcol_state; diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index cfd0c986df9..f7aaa1186db 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -463,14 +463,19 @@ typedef struct wmKeyMap { /** #wmKeyMap.flag */ enum { - KEYMAP_MODAL = (1 << 0), /* modal map, not using operatornames */ - KEYMAP_USER = (1 << 1), /* user keymap */ + /** Modal map, not using operator-names. */ + KEYMAP_MODAL = (1 << 0), + /** User key-map. */ + KEYMAP_USER = (1 << 1), KEYMAP_EXPANDED = (1 << 2), KEYMAP_CHILDREN_EXPANDED = (1 << 3), - KEYMAP_DIFF = (1 << 4), /* diff keymap for user preferences */ - KEYMAP_USER_MODIFIED = (1 << 5), /* keymap has user modifications */ + /** Diff key-map for user preferences. */ + KEYMAP_DIFF = (1 << 4), + /** Key-map has user modifications. */ + KEYMAP_USER_MODIFIED = (1 << 5), KEYMAP_UPDATE = (1 << 6), - KEYMAP_TOOL = (1 << 7), /* keymap for active tool system */ + /** key-map for active tool system. */ + KEYMAP_TOOL = (1 << 7), }; /** diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index e855395482e..67605201a9f 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -490,6 +490,18 @@ char *RNA_path_append( char *RNA_path_back(const char *path); #endif +/** + * Search for the start of the 'rna array index' part of the given `rna_path`. + * + * Given the root RNA pointer and resolved RNA property, and the RNA path, return the first + * character in `rna_path` that is part of the array index for the given property. Return NULL if + * none can be found, e.g. because the property is not an RNA array. + * + * \param array_prop if not NULL, the PropertyRNA assumed to be the last one from the RNA path. + * Only used to ensure it is a valid array property. + */ +const char *RNA_path_array_index_token_find(const char *rna_path, const PropertyRNA *array_prop); + /* RNA_path_resolve() variants only ensure that a valid pointer (and optionally property) exist. */ /** diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index b5cf8abaac6..8d2ee0ab534 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -33,41 +33,41 @@ const EnumPropertyItem rna_enum_id_type_items[] = { {ID_AC, "ACTION", ICON_ACTION, "Action", ""}, {ID_AR, "ARMATURE", ICON_ARMATURE_DATA, "Armature", ""}, {ID_BR, "BRUSH", ICON_BRUSH_DATA, "Brush", ""}, - {ID_CA, "CAMERA", ICON_CAMERA_DATA, "Camera", ""}, {ID_CF, "CACHEFILE", ICON_FILE, "Cache File", ""}, + {ID_CA, "CAMERA", ICON_CAMERA_DATA, "Camera", ""}, + {ID_GR, "COLLECTION", ICON_OUTLINER_COLLECTION, "Collection", ""}, {ID_CU_LEGACY, "CURVE", ICON_CURVE_DATA, "Curve", ""}, + {ID_CV, "CURVES", ICON_CURVES_DATA, "Curves", ""}, {ID_VF, "FONT", ICON_FONT_DATA, "Font", ""}, {ID_GD, "GREASEPENCIL", ICON_GREASEPENCIL, "Grease Pencil", ""}, - {ID_GR, "COLLECTION", ICON_OUTLINER_COLLECTION, "Collection", ""}, {ID_IM, "IMAGE", ICON_IMAGE_DATA, "Image", ""}, {ID_KE, "KEY", ICON_SHAPEKEY_DATA, "Key", ""}, - {ID_LA, "LIGHT", ICON_LIGHT_DATA, "Light", ""}, + {ID_LT, "LATTICE", ICON_LATTICE_DATA, "Lattice", ""}, {ID_LI, "LIBRARY", ICON_LIBRARY_DATA_DIRECT, "Library", ""}, + {ID_LA, "LIGHT", ICON_LIGHT_DATA, "Light", ""}, + {ID_LP, "LIGHT_PROBE", ICON_LIGHTPROBE_CUBEMAP, "Light Probe", ""}, {ID_LS, "LINESTYLE", ICON_LINE_DATA, "Line Style", ""}, - {ID_LT, "LATTICE", ICON_LATTICE_DATA, "Lattice", ""}, {ID_MSK, "MASK", ICON_MOD_MASK, "Mask", ""}, {ID_MA, "MATERIAL", ICON_MATERIAL_DATA, "Material", ""}, - {ID_MB, "META", ICON_META_DATA, "Metaball", ""}, {ID_ME, "MESH", ICON_MESH_DATA, "Mesh", ""}, + {ID_MB, "META", ICON_META_DATA, "Metaball", ""}, {ID_MC, "MOVIECLIP", ICON_TRACKER, "Movie Clip", ""}, {ID_NT, "NODETREE", ICON_NODETREE, "Node Tree", ""}, {ID_OB, "OBJECT", ICON_OBJECT_DATA, "Object", ""}, {ID_PC, "PAINTCURVE", ICON_CURVE_BEZCURVE, "Paint Curve", ""}, {ID_PAL, "PALETTE", ICON_COLOR, "Palette", ""}, {ID_PA, "PARTICLE", ICON_PARTICLE_DATA, "Particle", ""}, - {ID_LP, "LIGHT_PROBE", ICON_LIGHTPROBE_CUBEMAP, "Light Probe", ""}, + {ID_PT, "POINTCLOUD", ICON_POINTCLOUD_DATA, "Point Cloud", ""}, {ID_SCE, "SCENE", ICON_SCENE_DATA, "Scene", ""}, {ID_SIM, "SIMULATION", ICON_PHYSICS, "Simulation", ""}, /* TODO: Use correct icon. */ {ID_SO, "SOUND", ICON_SOUND, "Sound", ""}, {ID_SPK, "SPEAKER", ICON_SPEAKER, "Speaker", ""}, {ID_TXT, "TEXT", ICON_TEXT, "Text", ""}, {ID_TE, "TEXTURE", ICON_TEXTURE_DATA, "Texture", ""}, - {ID_CV, "CURVES", ICON_CURVES_DATA, "Hair Curves", ""}, - {ID_PT, "POINTCLOUD", ICON_POINTCLOUD_DATA, "Point Cloud", ""}, {ID_VO, "VOLUME", ICON_VOLUME_DATA, "Volume", ""}, {ID_WM, "WINDOWMANAGER", ICON_WINDOW, "Window Manager", ""}, - {ID_WO, "WORLD", ICON_WORLD_DATA, "World", ""}, {ID_WS, "WORKSPACE", ICON_WORKSPACE, "Workspace", ""}, + {ID_WO, "WORLD", ICON_WORLD_DATA, "World", ""}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 75579107465..0bc35d86490 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -5482,6 +5482,52 @@ static UNUSED_FUNCTION_WITH_RETURN_TYPE(char *, RNA_path_back)(const char *path) return result; } +const char *RNA_path_array_index_token_find(const char *rna_path, const PropertyRNA *array_prop) +{ + if (array_prop != NULL) { + if (!ELEM(array_prop->type, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) { + BLI_assert(array_prop->arraydimension == 0); + return NULL; + } + if (array_prop->arraydimension == 0) { + return NULL; + } + } + + /* Valid 'array part' of a rna path can only have '[', ']' and digit characters. + * It may have more than one of those (e.g. `[12][1]`) in case of multi-dimensional arrays. */ + off_t rna_path_len = (off_t)strlen(rna_path); + if (rna_path[rna_path_len] != ']') { + return NULL; + } + const char *last_valid_index_token_start = NULL; + for (rna_path_len--; rna_path_len >= 0; rna_path_len--) { + switch (rna_path[rna_path_len]) { + case '[': + if (rna_path_len <= 0 || rna_path[rna_path_len - 1] != ']') { + return &rna_path[rna_path_len]; + } + last_valid_index_token_start = &rna_path[rna_path_len]; + rna_path_len--; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + default: + return last_valid_index_token_start; + } + } + return last_valid_index_token_start; +} + /* generic path search func * if its needed this could also reference the IDProperty direct */ typedef struct IDP_Chain { @@ -6062,7 +6108,7 @@ char *RNA_path_struct_property_py(PointerRNA *ptr, PropertyRNA *prop, int index) } if ((index == -1) || (RNA_property_array_check(prop) == false)) { - ret = BLI_sprintfN("%s", data_path); + ret = BLI_strdup(data_path); } else { ret = BLI_sprintfN("%s[%d]", data_path, index); diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 46b009191ca..423cb084f27 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -243,15 +243,17 @@ const EnumPropertyItem rna_enum_brush_gpencil_weight_types_items[] = { {0, NULL, 0, NULL, NULL}, }; +/* clang-format off */ const EnumPropertyItem rna_enum_brush_curves_sculpt_tool_items[] = { - {CURVES_SCULPT_TOOL_COMB, "COMB", ICON_NONE, "Comb Curves", ""}, - {CURVES_SCULPT_TOOL_DELETE, "DELETE", ICON_NONE, "Delete Curves", ""}, - {CURVES_SCULPT_TOOL_SNAKE_HOOK, "SNAKE_HOOK", ICON_NONE, "Curves Snake Hook", ""}, - {CURVES_SCULPT_TOOL_ADD, "ADD", ICON_NONE, "Add Curves", ""}, - {CURVES_SCULPT_TOOL_GROW_SHRINK, "GROW_SHRINK", ICON_NONE, "Grow / Shrink Curves", ""}, - {CURVES_SCULPT_TOOL_SELECTION_PAINT, "SELECTION_PAINT", ICON_NONE, "Paint Selection", ""}, + {CURVES_SCULPT_TOOL_COMB, "COMB", ICON_BRUSH_CURVES_COMB, "Comb Curves", ""}, + {CURVES_SCULPT_TOOL_DELETE, "DELETE", ICON_BRUSH_CURVES_DELETE, "Delete Curves", ""}, + {CURVES_SCULPT_TOOL_SNAKE_HOOK, "SNAKE_HOOK", ICON_BRUSH_CURVES_SNAKE_HOOK, "Curves Snake Hook", ""}, + {CURVES_SCULPT_TOOL_ADD, "ADD", ICON_BRUSH_CURVES_ADD, "Add Curves", ""}, + {CURVES_SCULPT_TOOL_GROW_SHRINK, "GROW_SHRINK", ICON_BRUSH_CURVES_GROW_SHRINK, "Grow / Shrink Curves", ""}, + {CURVES_SCULPT_TOOL_SELECTION_PAINT, "SELECTION_PAINT", ICON_BRUSH_PAINT_SELECT, "Paint Selection", ""}, {0, NULL, 0, NULL, NULL}, }; +/* clang-format om */ #ifndef RNA_RUNTIME static EnumPropertyItem rna_enum_gpencil_brush_eraser_modes_items[] = { diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index c3d5819caa3..47bed955b54 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -1279,8 +1279,8 @@ static void rna_def_modifier_gpencilsimplify(BlenderRNA *brna) /* Sample */ prop = RNA_def_property(srna, "length", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_float_sdna(prop, NULL, "length"); - RNA_def_property_range(prop, 0, FLT_MAX); - RNA_def_property_ui_range(prop, 0, 1.0, 0.01, 3); + RNA_def_property_range(prop, 0.005, FLT_MAX); + RNA_def_property_ui_range(prop, 0, 1.0, 0.05, 3); RNA_def_property_ui_text(prop, "Length", "Length of each segment"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index d2b8b32eedc..be1e344d73f 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -160,15 +160,14 @@ static void rna_Image_update(Image *image, ReportList *reports) BKE_image_release_ibuf(image, ibuf, NULL); } -static void rna_Image_scale(Image *image, bContext *C, ReportList *reports, int width, int height) +static void rna_Image_scale(Image *image, ReportList *reports, int width, int height) { if (!BKE_image_scale(image, width, height)) { BKE_reportf(reports, RPT_ERROR, "Image '%s' does not have any image data", image->id.name + 2); + return; } - else { - BKE_image_partial_update_mark_full_update(image); - WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, image); - } + BKE_image_partial_update_mark_full_update(image); + WM_main_add_notifier(NC_IMAGE | NA_EDITED, image); } static int rna_Image_gl_load( @@ -285,7 +284,7 @@ void RNA_api_image(StructRNA *srna) func = RNA_def_function(srna, "scale", "rna_Image_scale"); RNA_def_function_ui_description(func, "Scale the buffer of the image, in pixels"); - RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_CONTEXT); + RNA_def_function_flag(func, FUNC_USE_REPORTS); parm = RNA_def_int(func, "width", 1, 1, INT_MAX, "", "Width", 1, INT_MAX); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_int(func, "height", 1, 1, INT_MAX, "", "Height", 1, INT_MAX); diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c index 895d6e8ba70..16a4dfe71cf 100644 --- a/source/blender/makesrna/intern/rna_rna.c +++ b/source/blender/makesrna/intern/rna_rna.c @@ -89,7 +89,7 @@ const EnumPropertyItem rna_enum_property_type_items[] = { {PROP_XYZ, "XYZ", 0, "XYZ", ""}, \ {PROP_XYZ_LENGTH, "XYZ_LENGTH", 0, "XYZ Length", ""}, \ {PROP_COLOR_GAMMA, "COLOR_GAMMA", 0, "Color", ""}, \ - {PROP_COORDS, "COORDS", 0, "Coordinates", ""}, \ + {PROP_COORDS, "COORDINATES", 0, "Coordinates", ""}, \ /* Boolean. */ \ {PROP_LAYER, "LAYER", 0, "Layer", ""}, \ {PROP_LAYER_MEMBER, "LAYER_MEMBER", 0, "Layer Member", ""} diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 3b739561f9a..9e2a1d81bd3 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -2712,6 +2712,23 @@ static char *rna_FFmpegSettings_path(const PointerRNA *UNUSED(ptr)) return BLI_strdup("render.ffmpeg"); } +# ifdef WITH_FFMPEG +/* FFMpeg Codec setting update hook. */ +static void rna_FFmpegSettings_codec_update(Main *UNUSED(bmain), + Scene *UNUSED(scene), + PointerRNA *ptr) +{ + FFMpegCodecData *codec_data = (FFMpegCodecData *)ptr->data; + if (!ELEM(codec_data->codec, AV_CODEC_ID_H264, AV_CODEC_ID_MPEG4, AV_CODEC_ID_VP9)) { + /* Constant Rate Factor (CRF) setting is only available for H264, + * MPEG4 and WEBM/VP9 codecs. So changing encoder quality mode to + * CBR as CRF is not supported. + */ + codec_data->constant_rate_factor = FFM_CRF_NONE; + } +} +# endif + #else /* Grease Pencil Interpolation tool settings */ @@ -5922,6 +5939,7 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna) RNA_def_property_enum_items(prop, ffmpeg_codec_items); RNA_def_property_enum_default(prop, AV_CODEC_ID_H264); RNA_def_property_ui_text(prop, "Video Codec", "FFmpeg codec to use for video output"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_FFmpegSettings_codec_update"); prop = RNA_def_property(srna, "video_bitrate", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "video_bitrate"); diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c index 1d0723851ad..dabb89bcd5e 100644 --- a/source/blender/makesrna/intern/rna_ui.c +++ b/source/blender/makesrna/intern/rna_ui.c @@ -393,7 +393,14 @@ static StructRNA *rna_Panel_register(Main *bmain, if (parent) { pt->parent = parent; - BLI_addtail(&parent->children, BLI_genericNodeN(pt)); + LinkData *pt_child_iter = parent->children.last; + for (; pt_child_iter; pt_child_iter = pt_child_iter->prev) { + PanelType *pt_child = pt_child_iter->data; + if (pt_child->order <= pt->order) { + break; + } + } + BLI_insertlinkafter(&parent->children, pt_child_iter, BLI_genericNodeN(pt)); } { diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 43e8879fc17..40dc1254a7d 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -1531,6 +1531,11 @@ static void rna_def_userdef_theme_ui(BlenderRNA *brna) RNA_def_property_ui_text(prop, "List Item Colors", ""); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + prop = RNA_def_property(srna, "wcol_view_item", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_ui_text(prop, "Data-View Item Colors", ""); + RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + prop = RNA_def_property(srna, "wcol_state", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); RNA_def_property_ui_text(prop, "State Colors", ""); diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index 07504d91fea..5739de1c65c 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -447,7 +447,8 @@ static Mesh *exact_boolean_mesh(BooleanModifierData *bmd, material_remaps, use_self, hole_tolerant, - bmd->operation); + bmd->operation, + nullptr); } #endif diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c index 4201bab0326..23f447f2469 100644 --- a/source/blender/modifiers/intern/MOD_subsurf.c +++ b/source/blender/modifiers/intern/MOD_subsurf.c @@ -196,16 +196,21 @@ static Mesh *subdiv_as_ccg(SubsurfModifierData *smd, /* Cache settings for lazy CPU evaluation. */ -static void subdiv_cache_cpu_evaluation_settings(const ModifierEvalContext *ctx, - Mesh *me, - SubsurfModifierData *smd) +static void subdiv_cache_mesh_wrapper_settings(const ModifierEvalContext *ctx, + Mesh *mesh, + SubsurfModifierData *smd, + SubsurfRuntimeData *runtime_data) { SubdivToMeshSettings mesh_settings; subdiv_mesh_settings_init(&mesh_settings, smd, ctx); - me->runtime.subsurf_apply_render = (ctx->flag & MOD_APPLY_RENDER) != 0; - me->runtime.subsurf_resolution = mesh_settings.resolution; - me->runtime.subsurf_use_optimal_display = mesh_settings.use_optimal_display; - me->runtime.subsurf_session_uuid = smd->modifier.session_uuid; + + runtime_data->has_gpu_subdiv = true; + runtime_data->resolution = mesh_settings.resolution; + runtime_data->use_optimal_display = mesh_settings.use_optimal_display; + runtime_data->calc_loop_normals = false; /* Set at the end of modifier stack evaluation. */ + runtime_data->use_loop_normals = (smd->flags & eSubsurfModifierFlag_UseCustomNormals); + + mesh->runtime.subsurf_runtime_data = runtime_data; } /* Modifier itself. */ @@ -218,13 +223,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * return result; #endif SubsurfModifierData *smd = (SubsurfModifierData *)md; - SubdivSettings subdiv_settings; - BKE_subsurf_modifier_subdiv_settings_init( - &subdiv_settings, smd, (ctx->flag & MOD_APPLY_RENDER) != 0); - if (subdiv_settings.level == 0) { + if (!BKE_subsurf_modifier_runtime_init(smd, (ctx->flag & MOD_APPLY_RENDER) != 0)) { return result; } - SubsurfRuntimeData *runtime_data = BKE_subsurf_modifier_ensure_runtime(smd); + + SubsurfRuntimeData *runtime_data = (SubsurfRuntimeData *)smd->modifier.runtime; /* Delay evaluation to the draw code if possible, provided we do not have to apply the modifier. */ @@ -237,13 +240,12 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * const bool is_editmode = (mesh->edit_mesh != NULL); const int required_mode = BKE_subsurf_modifier_eval_required_mode(is_render_mode, is_editmode); if (BKE_subsurf_modifier_can_do_gpu_subdiv(scene, ctx->object, mesh, smd, required_mode)) { - subdiv_cache_cpu_evaluation_settings(ctx, mesh, smd); + subdiv_cache_mesh_wrapper_settings(ctx, mesh, smd, runtime_data); return result; } } - Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure( - smd, &subdiv_settings, mesh, false); + Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure(runtime_data, mesh, false); if (subdiv == NULL) { /* Happens on bad topology, but also on empty input mesh. */ return result; @@ -294,15 +296,11 @@ static void deformMatrices(ModifierData *md, (void)deform_matrices; SubsurfModifierData *smd = (SubsurfModifierData *)md; - SubdivSettings subdiv_settings; - BKE_subsurf_modifier_subdiv_settings_init( - &subdiv_settings, smd, (ctx->flag & MOD_APPLY_RENDER) != 0); - if (subdiv_settings.level == 0) { + if (!BKE_subsurf_modifier_runtime_init(smd, (ctx->flag & MOD_APPLY_RENDER) != 0)) { return; } - SubsurfRuntimeData *runtime_data = BKE_subsurf_modifier_ensure_runtime(smd); - Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure( - smd, &subdiv_settings, mesh, false); + SubsurfRuntimeData *runtime_data = (SubsurfRuntimeData *)smd->modifier.runtime; + Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure(runtime_data, mesh, false); if (subdiv == NULL) { /* Happens on bad topology, but also on empty input mesh. */ return; diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index e485172d3e1..daeca311e08 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc @@ -20,6 +20,7 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_input<decl::Bool>(N_("Self Intersection")); b.add_input<decl::Bool>(N_("Hole Tolerant")); b.add_output<decl::Geometry>(N_("Mesh")); + b.add_output<decl::Bool>(N_("Intersecting Edges")).field_source(); } static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -27,6 +28,10 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); } +struct AttributeOutputs { + StrongAnonymousAttributeID intersecting_edges_id; +}; + static void node_update(bNodeTree *ntree, bNode *node) { GeometryNodeBooleanOperation operation = (GeometryNodeBooleanOperation)node->custom1; @@ -121,13 +126,21 @@ static void node_geo_exec(GeoNodeExecParams params) } } - Mesh *result = blender::meshintersect::direct_mesh_boolean(meshes, - transforms, - float4x4::identity(), - material_remaps, - use_self, - hole_tolerant, - operation); + AttributeOutputs attribute_outputs; + if (params.output_is_required("Intersecting Edges")) { + attribute_outputs.intersecting_edges_id = StrongAnonymousAttributeID("Intersecting Edges"); + } + + Vector<int> intersecting_edges; + Mesh *result = blender::meshintersect::direct_mesh_boolean( + meshes, + transforms, + float4x4::identity(), + material_remaps, + use_self, + hole_tolerant, + operation, + attribute_outputs.intersecting_edges_id ? &intersecting_edges : nullptr); if (!result) { params.set_default_remaining_outputs(); return; @@ -138,6 +151,26 @@ static void node_geo_exec(GeoNodeExecParams params) result->totcol = materials.size(); MutableSpan(result->mat, result->totcol).copy_from(materials); + /* Store intersecting edges in attribute. */ + if (attribute_outputs.intersecting_edges_id) { + MeshComponent mesh_component; + mesh_component.replace(result, GeometryOwnershipType::Editable); + OutputAttribute_Typed<bool> attribute = mesh_component.attribute_try_get_for_output_only<bool>( + attribute_outputs.intersecting_edges_id.get(), ATTR_DOMAIN_EDGE); + MutableSpan<bool> selection = attribute.as_span(); + selection.fill(false); + for (const int i : intersecting_edges) { + selection[i] = true; + } + + attribute.save(); + + params.set_output( + "Intersecting Edges", + AnonymousAttributeFieldInput::Create<bool>( + std::move(attribute_outputs.intersecting_edges_id), params.attribute_producer_name())); + } + params.set_output("Mesh", GeometrySet::create_with_mesh(result)); #else params.error_message_add(NodeWarningType::Error, diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index 08b107e0152..352411dd8f5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -8,6 +8,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_attribute_math.hh" #include "BKE_curves.hh" #include "BKE_customdata.h" #include "BKE_mesh.h" diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc index c400760f902..532c3dc81e5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc @@ -65,7 +65,7 @@ class PlanarFieldInput final : public GeometryFieldInput { float3 reference_normal = poly_normals[i_poly]; float min = FLT_MAX; - float max = FLT_MIN; + float max = -FLT_MAX; for (const int i_loop : poly_loops.index_range()) { const float3 vert = mesh->mvert[poly_loops[i_loop].v].co; diff --git a/source/blender/python/intern/bpy_traceback.c b/source/blender/python/intern/bpy_traceback.c index 45977ba400c..cb93843a6de 100644 --- a/source/blender/python/intern/bpy_traceback.c +++ b/source/blender/python/intern/bpy_traceback.c @@ -165,7 +165,7 @@ finally: bool python_script_error_jump( const char *filepath, int *r_lineno, int *r_offset, int *r_lineno_end, int *r_offset_end) { - /* WARNING(@campbellbarton): The normalized exception is restored (loosing line number info). + /* WARNING(@campbellbarton): The normalized exception is restored (losing line number info). * Ideally this would leave the exception state as it found it, but that needs to be done * carefully with regards to reference counting, see: T97731. */ diff --git a/source/blender/render/intern/render_result.c b/source/blender/render/intern/render_result.c index 9f4aa642773..9992d1a507f 100644 --- a/source/blender/render/intern/render_result.c +++ b/source/blender/render/intern/render_result.c @@ -825,7 +825,7 @@ void render_result_merge(RenderResult *rr, RenderResult *rrpart) if (rpass->rect == NULL || rpassp->rect == NULL) { continue; } - /* Renderresult have all passes, renderpart only the active view's passes. */ + /* Render-result have all passes, render-part only the active view's passes. */ if (!STREQ(rpassp->fullname, rpass->fullname)) { continue; } diff --git a/source/blender/sequencer/SEQ_transform.h b/source/blender/sequencer/SEQ_transform.h index 93ce6873d3b..31a7399f844 100644 --- a/source/blender/sequencer/SEQ_transform.h +++ b/source/blender/sequencer/SEQ_transform.h @@ -46,6 +46,7 @@ bool SEQ_transform_seqbase_shuffle(struct ListBase *seqbasep, struct Sequence *test, struct Scene *evil_scene); bool SEQ_transform_seqbase_shuffle_time(struct SeqCollection *strips_to_shuffle, + struct SeqCollection *time_dependent_strips, struct ListBase *seqbasep, struct Scene *evil_scene, struct ListBase *markers, @@ -54,6 +55,7 @@ bool SEQ_transform_seqbase_shuffle_time(struct SeqCollection *strips_to_shuffle, void SEQ_transform_handle_overlap(struct Scene *scene, struct ListBase *seqbasep, struct SeqCollection *transformed_strips, + struct SeqCollection *time_dependent_strips, bool use_sync_markers); /** * Check if the selected seq's reference unselected seq's. diff --git a/source/blender/sequencer/intern/image_cache.c b/source/blender/sequencer/intern/image_cache.c index e097de237f2..47306dbbc6c 100644 --- a/source/blender/sequencer/intern/image_cache.c +++ b/source/blender/sequencer/intern/image_cache.c @@ -640,13 +640,8 @@ void seq_cache_cleanup_sequence(Scene *scene, int range_end = SEQ_time_right_handle_frame_get(seq_changed); if (!force_seq_changed_range) { - if (SEQ_time_left_handle_frame_get(seq) > range_start) { - range_start = SEQ_time_left_handle_frame_get(seq); - } - - if (SEQ_time_right_handle_frame_get(seq) < range_end) { - range_end = SEQ_time_right_handle_frame_get(seq); - } + range_start = max_ii(range_start, SEQ_time_left_handle_frame_get(seq)); + range_end = min_ii(range_end, SEQ_time_right_handle_frame_get(seq)); } int invalidate_composite = invalidate_types & SEQ_CACHE_STORE_FINAL_OUT; diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c index 25d4f08be82..4d6efb1639b 100644 --- a/source/blender/sequencer/intern/strip_time.c +++ b/source/blender/sequencer/intern/strip_time.c @@ -173,6 +173,10 @@ void SEQ_time_update_meta_strip_range(const Scene *scene, Sequence *seq_meta) void seq_time_effect_range_set(Sequence *seq) { + if (seq->seq1 == NULL && seq->seq2 == NULL) { + return; + } + if (seq->seq1 && seq->seq2) { /* 2 - input effect. */ seq->startdisp = max_ii(SEQ_time_left_handle_frame_get(seq->seq1), SEQ_time_left_handle_frame_get(seq->seq2)); diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c index ebf317eff05..19caf12ff2a 100644 --- a/source/blender/sequencer/intern/strip_transform.c +++ b/source/blender/sequencer/intern/strip_transform.c @@ -13,11 +13,13 @@ #include "BLI_listbase.h" #include "BLI_math.h" +#include "BKE_main.h" #include "BKE_scene.h" #include "BKE_sound.h" #include "SEQ_animation.h" #include "SEQ_channels.h" +#include "SEQ_edit.h" #include "SEQ_effects.h" #include "SEQ_iterator.h" #include "SEQ_relations.h" @@ -307,6 +309,7 @@ static int shuffle_seq_time_offset_get(SeqCollection *strips_to_shuffle, } bool SEQ_transform_seqbase_shuffle_time(SeqCollection *strips_to_shuffle, + SeqCollection *time_dependent_strips, ListBase *seqbasep, Scene *evil_scene, ListBase *markers, @@ -323,6 +326,12 @@ bool SEQ_transform_seqbase_shuffle_time(SeqCollection *strips_to_shuffle, seq->flag &= ~SEQ_OVERLAP; } + if (time_dependent_strips != NULL) { + SEQ_ITERATOR_FOREACH (seq, time_dependent_strips) { + SEQ_offset_animdata(evil_scene, seq, offset); + } + } + if (use_sync_markers && !(evil_scene->toolsettings->lock_markers) && (markers != NULL)) { TimeMarker *marker; /* affect selected markers - it's unlikely that we will want to affect all in this way? */ @@ -337,6 +346,296 @@ bool SEQ_transform_seqbase_shuffle_time(SeqCollection *strips_to_shuffle, return offset ? false : true; } +static SeqCollection *extract_standalone_strips(SeqCollection *transformed_strips) +{ + SeqCollection *collection = SEQ_collection_create(__func__); + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, transformed_strips) { + if ((seq->type & SEQ_TYPE_EFFECT) == 0 || seq->seq1 == NULL) { + SEQ_collection_append_strip(seq, collection); + } + } + return collection; +} + +/* Query strips positioned after left edge of transformed strips bound-box. */ +static SeqCollection *query_right_side_strips(ListBase *seqbase, + SeqCollection *transformed_strips, + SeqCollection *time_dependent_strips) +{ + int minframe = MAXFRAME; + { + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, transformed_strips) { + minframe = min_ii(minframe, SEQ_time_left_handle_frame_get(seq)); + } + } + + SeqCollection *collection = SEQ_collection_create(__func__); + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (SEQ_collection_has_strip(seq, time_dependent_strips)) { + continue; + } + if (SEQ_collection_has_strip(seq, transformed_strips)) { + continue; + } + + if ((seq->flag & SELECT) == 0 && SEQ_time_left_handle_frame_get(seq) >= minframe) { + SEQ_collection_append_strip(seq, collection); + } + } + return collection; +} + +/* Offset all strips positioned after left edge of transformed strips bound-box by amount equal + * to overlap of transformed strips. */ +static void seq_transform_handle_expand_to_fit(Scene *scene, + ListBase *seqbasep, + SeqCollection *transformed_strips, + SeqCollection *time_dependent_strips, + bool use_sync_markers) +{ + ListBase *markers = &scene->markers; + + SeqCollection *right_side_strips = query_right_side_strips( + seqbasep, transformed_strips, time_dependent_strips); + + /* Temporarily move right side strips beyond timeline boundary. */ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, right_side_strips) { + seq->machine += MAXSEQ * 2; + } + + /* Shuffle transformed standalone strips. This is because transformed strips can overlap with + * strips on left side. */ + SeqCollection *standalone_strips = extract_standalone_strips(transformed_strips); + SEQ_transform_seqbase_shuffle_time( + standalone_strips, time_dependent_strips, seqbasep, scene, markers, use_sync_markers); + SEQ_collection_free(standalone_strips); + + /* Move temporarily moved strips back to their original place and tag for shuffling. */ + SEQ_ITERATOR_FOREACH (seq, right_side_strips) { + seq->machine -= MAXSEQ * 2; + } + /* Shuffle again to displace strips on right side. Final effect shuffling is done in + * SEQ_transform_handle_overlap. */ + SEQ_transform_seqbase_shuffle_time( + right_side_strips, NULL, seqbasep, scene, markers, use_sync_markers); + SEQ_collection_free(right_side_strips); +} + +static SeqCollection *query_overwrite_targets(ListBase *seqbasep, + SeqCollection *transformed_strips) +{ + SeqCollection *collection = SEQ_query_unselected_strips(seqbasep); + + Sequence *seq, *seq_transformed; + SEQ_ITERATOR_FOREACH (seq, collection) { + bool does_overlap = false; + + SEQ_ITERATOR_FOREACH (seq_transformed, transformed_strips) { + /* Effects of transformed strips can be unselected. These must not be included. */ + if (seq == seq_transformed) { + SEQ_collection_remove_strip(seq, collection); + } + if (SEQ_transform_test_overlap_seq_seq(seq, seq_transformed)) { + does_overlap = true; + } + } + + if (!does_overlap) { + SEQ_collection_remove_strip(seq, collection); + } + } + + return collection; +} + +typedef enum eOvelapDescrition { + /* No overlap. */ + STRIP_OVERLAP_NONE, + /* Overlapping strip covers overlapped completely. */ + STRIP_OVERLAP_IS_FULL, + /* Overlapping strip is inside overlapped. */ + STRIP_OVERLAP_IS_INSIDE, + /* Partial overlap between 2 strips. */ + STRIP_OVERLAP_LEFT_SIDE, + STRIP_OVERLAP_RIGHT_SIDE, +} eOvelapDescrition; + +static eOvelapDescrition overlap_description_get(const Sequence *transformed, + const Sequence *target) +{ + if (SEQ_time_left_handle_frame_get(transformed) <= SEQ_time_left_handle_frame_get(target) && + SEQ_time_right_handle_frame_get(transformed) >= SEQ_time_right_handle_frame_get(target)) { + return STRIP_OVERLAP_IS_FULL; + } + if (SEQ_time_left_handle_frame_get(transformed) > SEQ_time_left_handle_frame_get(target) && + SEQ_time_right_handle_frame_get(transformed) < SEQ_time_right_handle_frame_get(target)) { + return STRIP_OVERLAP_IS_INSIDE; + } + if (SEQ_time_left_handle_frame_get(transformed) <= SEQ_time_left_handle_frame_get(target) && + SEQ_time_left_handle_frame_get(target) <= SEQ_time_right_handle_frame_get(transformed)) { + return STRIP_OVERLAP_LEFT_SIDE; + } + if (SEQ_time_left_handle_frame_get(transformed) <= SEQ_time_right_handle_frame_get(target) && + SEQ_time_right_handle_frame_get(target) <= SEQ_time_right_handle_frame_get(transformed)) { + return STRIP_OVERLAP_RIGHT_SIDE; + } + return STRIP_OVERLAP_NONE; +} + +/* Split strip in 3 parts, remove middle part and fit transformed inside. */ +static void seq_transform_handle_overwrite_split(Scene *scene, + ListBase *seqbasep, + const Sequence *transformed, + Sequence *target) +{ + /* Because we are doing a soft split, bmain is not used in SEQ_edit_strip_split, so we can pass + * NULL here. */ + Main *bmain = NULL; + + Sequence *split_strip = SEQ_edit_strip_split(bmain, + scene, + seqbasep, + target, + SEQ_time_left_handle_frame_get(transformed), + SEQ_SPLIT_SOFT, + NULL); + SEQ_edit_strip_split(bmain, + scene, + seqbasep, + split_strip, + SEQ_time_right_handle_frame_get(transformed), + SEQ_SPLIT_SOFT, + NULL); + SEQ_edit_flag_for_removal(scene, seqbasep, split_strip); + SEQ_edit_remove_flagged_sequences(scene, seqbasep); +} + +/* Trim strips by adjusting handle position. + * This is bit more complicated in case overlap happens on effect. */ +static void seq_transform_handle_overwrite_trim(Scene *scene, + ListBase *seqbasep, + const Sequence *transformed, + Sequence *target, + const eOvelapDescrition overlap) +{ + SeqCollection *targets = SEQ_query_by_reference(target, seqbasep, SEQ_query_strip_effect_chain); + + /* Expand collection by adding all target's children, effects and their children. */ + if ((target->type & SEQ_TYPE_EFFECT) != 0) { + SEQ_collection_expand(seqbasep, targets, SEQ_query_strip_effect_chain); + } + + /* Trim all non effects, that have influence on effect length which is overlapping. */ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, targets) { + if ((seq->type & SEQ_TYPE_EFFECT) != 0 && SEQ_effect_get_num_inputs(seq->type) > 0) { + continue; + } + if (overlap == STRIP_OVERLAP_LEFT_SIDE) { + SEQ_time_left_handle_frame_set(scene, seq, SEQ_time_right_handle_frame_get(transformed)); + } + else { + BLI_assert(overlap == STRIP_OVERLAP_RIGHT_SIDE); + SEQ_time_right_handle_frame_set(scene, seq, SEQ_time_left_handle_frame_get(transformed)); + } + } + SEQ_collection_free(targets); +} + +static void seq_transform_handle_overwrite(Scene *scene, + ListBase *seqbasep, + SeqCollection *transformed_strips) +{ + SeqCollection *targets = query_overwrite_targets(seqbasep, transformed_strips); + SeqCollection *strips_to_delete = SEQ_collection_create(__func__); + + Sequence *target; + Sequence *transformed; + SEQ_ITERATOR_FOREACH (target, targets) { + SEQ_ITERATOR_FOREACH (transformed, transformed_strips) { + if (transformed->machine != target->machine) { + continue; + } + + const eOvelapDescrition overlap = overlap_description_get(transformed, target); + + if (overlap == STRIP_OVERLAP_IS_FULL) { + SEQ_collection_append_strip(target, strips_to_delete); + } + else if (overlap == STRIP_OVERLAP_IS_INSIDE) { + seq_transform_handle_overwrite_split(scene, seqbasep, transformed, target); + } + else if (ELEM(overlap, STRIP_OVERLAP_LEFT_SIDE, STRIP_OVERLAP_RIGHT_SIDE)) { + seq_transform_handle_overwrite_trim(scene, seqbasep, transformed, target, overlap); + } + } + } + + SEQ_collection_free(targets); + + /* Remove covered strips. This must be done in separate loop, because `SEQ_edit_strip_split()` + * also uses `SEQ_edit_remove_flagged_sequences()`. See T91096. */ + if (SEQ_collection_len(strips_to_delete) > 0) { + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips_to_delete) { + SEQ_edit_flag_for_removal(scene, seqbasep, seq); + } + SEQ_edit_remove_flagged_sequences(scene, seqbasep); + } + SEQ_collection_free(strips_to_delete); +} + +static void seq_transform_handle_overlap_shuffle(Scene *scene, + ListBase *seqbasep, + SeqCollection *transformed_strips, + SeqCollection *time_dependent_strips, + bool use_sync_markers) +{ + ListBase *markers = &scene->markers; + + /* Shuffle non strips with no effects attached. */ + SeqCollection *standalone_strips = extract_standalone_strips(transformed_strips); + SEQ_transform_seqbase_shuffle_time( + standalone_strips, time_dependent_strips, seqbasep, scene, markers, use_sync_markers); + SEQ_collection_free(standalone_strips); +} + +void SEQ_transform_handle_overlap(Scene *scene, + ListBase *seqbasep, + SeqCollection *transformed_strips, + SeqCollection *time_dependent_strips, + bool use_sync_markers) +{ + const eSeqOverlapMode overlap_mode = SEQ_tool_settings_overlap_mode_get(scene); + + switch (overlap_mode) { + case SEQ_OVERLAP_EXPAND: + seq_transform_handle_expand_to_fit( + scene, seqbasep, transformed_strips, time_dependent_strips, use_sync_markers); + break; + case SEQ_OVERLAP_OVERWRITE: + seq_transform_handle_overwrite(scene, seqbasep, transformed_strips); + break; + case SEQ_OVERLAP_SHUFFLE: + seq_transform_handle_overlap_shuffle( + scene, seqbasep, transformed_strips, time_dependent_strips, use_sync_markers); + break; + } + + /* If any effects still overlap, we need to move them up. + * In some cases other strips can be overlapping still, see T90646. */ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, transformed_strips) { + if (SEQ_transform_test_overlap(seqbasep, seq)) { + SEQ_transform_seqbase_shuffle(seqbasep, seq, scene); + } + seq->flag &= ~SEQ_OVERLAP; + } +} + void SEQ_transform_offset_after_frame(Scene *scene, ListBase *seqbase, const int delta, diff --git a/source/blender/sequencer/intern/utils.c b/source/blender/sequencer/intern/utils.c index 260f82310fb..3cfe63e284f 100644 --- a/source/blender/sequencer/intern/utils.c +++ b/source/blender/sequencer/intern/utils.c @@ -217,7 +217,7 @@ void seq_open_anim_file(Scene *scene, Sequence *seq, bool openfile) const bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && (scene->r.scemode & R_MULTIVIEW) != 0; - if ((seq->anims.first != NULL) && (((StripAnim *)seq->anims.first)->anim != NULL)) { + if ((seq->anims.first != NULL) && (((StripAnim *)seq->anims.first)->anim != NULL) && !openfile) { return; } diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 9942d62be52..e60d812e5b6 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -738,6 +738,7 @@ void WM_operator_last_properties_ensure(struct wmOperatorType *ot, struct Pointe wmOperator *WM_operator_last_redo(const struct bContext *C); /** * Use for drag & drop a path or name with operators invoke() function. + * Returns null if no operator property is set to identify the file or ID to use. */ ID *WM_operator_drop_load_path(struct bContext *C, struct wmOperator *op, short idcode); diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index c2a63c9db7a..63a7fb5ddaa 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -1153,9 +1153,17 @@ static bool wm_draw_update_test_window(Main *bmain, bContext *C, wmWindow *win) if (wm_software_cursor_needed()) { struct GrabState grab_state; - if (wm_software_cursor_needed_for_window(win, &grab_state) && - wm_software_cursor_motion_test(win)) { - return true; + if (wm_software_cursor_needed_for_window(win, &grab_state)) { + if (wm_software_cursor_motion_test(win)) { + return true; + } + } + else { + /* Detect the edge case when the previous draw used the software cursor but this one doesn't, + * it's important to redraw otherwise the software cursor will remain displayed. */ + if (g_software_cursor.winid != -1) { + return true; + } } } diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc index 7420e756b31..d3ae4177e4d 100644 --- a/source/blender/windowmanager/intern/wm_event_system.cc +++ b/source/blender/windowmanager/intern/wm_event_system.cc @@ -873,7 +873,7 @@ bool WM_operator_poll(bContext *C, wmOperatorType *ot) { LISTBASE_FOREACH (wmOperatorTypeMacro *, macro, &ot->macro) { - wmOperatorType *ot_macro = WM_operatortype_find(macro->idname, 0); + wmOperatorType *ot_macro = WM_operatortype_find(macro->idname, false); if (!WM_operator_poll(C, ot_macro)) { return false; @@ -903,7 +903,7 @@ bool WM_operator_check_ui_empty(wmOperatorType *ot) if (ot->macro.first != nullptr) { /* For macros, check all have exec() we can call. */ LISTBASE_FOREACH (wmOperatorTypeMacro *, macro, &ot->macro) { - wmOperatorType *otm = WM_operatortype_find(macro->idname, 0); + wmOperatorType *otm = WM_operatortype_find(macro->idname, false); if (otm && !WM_operator_check_ui_empty(otm)) { return false; } @@ -1180,7 +1180,7 @@ bool WM_operator_repeat_check(const bContext *UNUSED(C), wmOperator *op) if (op->opm) { /* For macros, check all have exec() we can call. */ LISTBASE_FOREACH (wmOperatorTypeMacro *, macro, &op->opm->type->macro) { - wmOperatorType *otm = WM_operatortype_find(macro->idname, 0); + wmOperatorType *otm = WM_operatortype_find(macro->idname, false); if (otm && otm->exec == nullptr) { return false; } @@ -1260,7 +1260,7 @@ static wmOperator *wm_operator_create(wmWindowManager *wm, /* Skip invalid properties. */ if (STREQ(RNA_property_identifier(prop), otmacro->idname)) { - wmOperatorType *otm = WM_operatortype_find(otmacro->idname, 0); + wmOperatorType *otm = WM_operatortype_find(otmacro->idname, false); PointerRNA someptr = RNA_property_pointer_get(properties, prop); wmOperator *opm = wm_operator_create(wm, otm, &someptr, nullptr); @@ -1276,7 +1276,7 @@ static wmOperator *wm_operator_create(wmWindowManager *wm, } else { LISTBASE_FOREACH (wmOperatorTypeMacro *, macro, &ot->macro) { - wmOperatorType *otm = WM_operatortype_find(macro->idname, 0); + wmOperatorType *otm = WM_operatortype_find(macro->idname, false); wmOperator *opm = wm_operator_create(wm, otm, macro->ptr, nullptr); BLI_addtail(&motherop->macro, opm); @@ -1289,7 +1289,7 @@ static wmOperator *wm_operator_create(wmWindowManager *wm, } } - WM_operator_properties_sanitize(op->ptr, 0); + WM_operator_properties_sanitize(op->ptr, false); return op; } @@ -1649,7 +1649,7 @@ int WM_operator_name_call(bContext *C, PointerRNA *properties, const wmEvent *event) { - wmOperatorType *ot = WM_operatortype_find(opstring, 0); + wmOperatorType *ot = WM_operatortype_find(opstring, false); if (ot) { return WM_operator_name_call_ptr(C, ot, context, properties, event); } @@ -1659,7 +1659,7 @@ int WM_operator_name_call(bContext *C, bool WM_operator_name_poll(bContext *C, const char *opstring) { - wmOperatorType *ot = WM_operatortype_find(opstring, 0); + wmOperatorType *ot = WM_operatortype_find(opstring, false); if (!ot) { return false; } @@ -1827,7 +1827,7 @@ void WM_operator_name_call_ptr_with_depends_on_cursor(bContext *C, int flag = ot->flag; LISTBASE_FOREACH (wmOperatorTypeMacro *, macro, &ot->macro) { - wmOperatorType *otm = WM_operatortype_find(macro->idname, 0); + wmOperatorType *otm = WM_operatortype_find(macro->idname, false); if (otm != nullptr) { flag |= otm->flag; } @@ -2396,11 +2396,11 @@ static int wm_handler_operator_call(bContext *C, } } else { - wmOperatorType *ot = WM_operatortype_find(kmi_idname, 0); + wmOperatorType *ot = WM_operatortype_find(kmi_idname, false); if (ot && wm_operator_check_locked_interface(C, ot)) { bool use_last_properties = true; - PointerRNA tool_properties = {0}; + PointerRNA tool_properties = {nullptr}; bToolRef *keymap_tool = nullptr; if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) { @@ -2770,7 +2770,7 @@ static const char *keymap_handler_log_kmi_op_str(bContext *C, /* The key-map item properties can further help distinguish this item from others. */ char *kmi_props = nullptr; if (kmi->properties != nullptr) { - wmOperatorType *ot = WM_operatortype_find(kmi->idname, 0); + wmOperatorType *ot = WM_operatortype_find(kmi->idname, false); if (ot) { kmi_props = RNA_pointer_as_string_keywords(C, kmi->ptr, false, false, true, 512); } @@ -3031,7 +3031,7 @@ static int wm_handlers_do_gizmo_handler(bContext *C, if ((kmi->flag & KMI_INACTIVE) == 0) { if (wm_eventmatch(&event_test_click, kmi) || wm_eventmatch(&event_test_click_drag, kmi)) { - wmOperatorType *ot = WM_operatortype_find(kmi->idname, 0); + wmOperatorType *ot = WM_operatortype_find(kmi->idname, false); if (WM_operator_poll_context(C, ot, WM_OP_INVOKE_DEFAULT)) { is_event_handle_all = true; break; @@ -3179,7 +3179,7 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis /* Pass single matched #wmDrag onto the operator. */ BLI_remlink(lb, drag); - ListBase single_lb = {0}; + ListBase single_lb = {nullptr}; BLI_addtail(&single_lb, drag); event->customdata = &single_lb; @@ -4604,7 +4604,7 @@ wmOperator *WM_operator_find_modal_by_type(wmWindow *win, const wmOperatorType * return handler->op; } } - return NULL; + return nullptr; } #if 0 @@ -4942,7 +4942,11 @@ static void attach_ndof_data(wmEvent *event, const GHOST_TEventNDOFMotionData *g /* Imperfect but probably usable... draw/enable drags to other windows. */ static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *win, wmEvent *event) { - int mval[2] = {event->xy[0], event->xy[1]}; + /* If GHOST doesn't support window positioning, don't use this feature at all. */ + const static int8_t supports_window_position = GHOST_SupportsWindowPosition(); + if (!supports_window_position) { + return nullptr; + } if (wm->windows.first == wm->windows.last) { return nullptr; @@ -4951,8 +4955,9 @@ static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *wi /* In order to use window size and mouse position (pixels), we have to use a WM function. */ /* Check if outside, include top window bar. */ - if (mval[0] < 0 || mval[1] < 0 || mval[0] > WM_window_pixels_x(win) || - mval[1] > WM_window_pixels_y(win) + 30) { + int event_xy[2] = {UNPACK2(event->xy)}; + if (event_xy[0] < 0 || event_xy[1] < 0 || event_xy[0] > WM_window_pixels_x(win) || + event_xy[1] > WM_window_pixels_y(win) + 30) { /* Let's skip windows having modal handlers now. */ /* Potential XXX ugly... I wouldn't have added a `modalhandlers` list * (introduced in rev 23331, ton). */ @@ -4962,9 +4967,9 @@ static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *wi } } - wmWindow *win_other = WM_window_find_under_cursor(win, mval, mval); + wmWindow *win_other = WM_window_find_under_cursor(win, event_xy, event_xy); if (win_other && win_other != win) { - copy_v2_v2_int(event->xy, mval); + copy_v2_v2_int(event->xy, event_xy); return win_other; } } @@ -5632,7 +5637,7 @@ wmKeyMapItem *WM_event_match_keymap_item(bContext *C, wmKeyMap *keymap, const wm { LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) { if (wm_eventmatch(event, kmi)) { - wmOperatorType *ot = WM_operatortype_find(kmi->idname, 0); + wmOperatorType *ot = WM_operatortype_find(kmi->idname, false); if (WM_operator_poll_context(C, ot, WM_OP_INVOKE_DEFAULT)) { return kmi; } @@ -5882,7 +5887,7 @@ void WM_window_cursor_keymap_status_refresh(bContext *C, wmWindow *win) } } if (kmi) { - wmOperatorType *ot = WM_operatortype_find(kmi->idname, 0); + wmOperatorType *ot = WM_operatortype_find(kmi->idname, false); const char *name = (ot) ? WM_operatortype_name(ot, kmi->ptr) : kmi->idname; STRNCPY(cd->text[button_index][type_index], name); } diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 330231f3f18..f77aad24719 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -338,10 +338,10 @@ void WM_init(bContext *C, int argc, const char **argv) if (!G.background) { if (wm_start_with_console) { - setConsoleWindowState(GHOST_kConsoleWindowStateShow); + GHOST_setConsoleWindowState(GHOST_kConsoleWindowStateShow); } else { - setConsoleWindowState(GHOST_kConsoleWindowStateHideForNonConsoleLaunch); + GHOST_setConsoleWindowState(GHOST_kConsoleWindowStateHideForNonConsoleLaunch); } } diff --git a/source/blender/windowmanager/intern/wm_keymap_utils.c b/source/blender/windowmanager/intern/wm_keymap_utils.c index 531da3cf2e8..0817b10f86e 100644 --- a/source/blender/windowmanager/intern/wm_keymap_utils.c +++ b/source/blender/windowmanager/intern/wm_keymap_utils.c @@ -270,7 +270,10 @@ wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname) break; } } - else if (STRPREFIX(opname, "CURVES_SCULPT_OT")) { + else if (STRPREFIX(opname, "CURVES_OT")) { + km = WM_keymap_find_all(wm, "Curves", 0, 0); + } + else if (STRPREFIX(opname, "SCULPT_CURVES_OT")) { km = WM_keymap_find_all(wm, "Sculpt Curves", 0, 0); } else if (STRPREFIX(opname, "MBALL_OT")) { diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 7b768bd8c70..33c69a23558 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -1308,6 +1308,10 @@ ID *WM_operator_drop_load_path(struct bContext *C, wmOperator *op, const short i return id; } + if (!WM_operator_properties_id_lookup_is_set(op->ptr)) { + return NULL; + } + /* Lookup an already existing ID. */ id = WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, idcode); @@ -2068,7 +2072,7 @@ static void WM_OT_quit_blender(wmOperatorType *ot) static int wm_console_toggle_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) { - setConsoleWindowState(GHOST_kConsoleWindowStateToggle); + GHOST_setConsoleWindowState(GHOST_kConsoleWindowStateToggle); return OPERATOR_FINISHED; } diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index 7cd54a7e191..3100e6e4fd3 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -33,6 +33,7 @@ #include "BLI_path_util.h" #include "BLI_rect.h" #include "BLI_string.h" +#include "BLI_system.h" #include "BLI_utildefines.h" #include "IMB_colormanagement.h" @@ -211,7 +212,7 @@ static void playanim_gl_matrix(void) /* implementation */ static void playanim_event_qual_update(void) { - int val; + bool val; /* Shift */ GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyLeftShift, &val); @@ -870,7 +871,7 @@ static void change_frame(PlayState *ps) ps->need_frame_update = false; } -static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void) +static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void) { PlayState *ps = (PlayState *)ps_void; const GHOST_TEventType type = GHOST_GetEventType(evt); @@ -901,7 +902,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void) default: break; } - return 1; + return true; } if (ps->wait2 && ps->stopped == false) { @@ -1334,7 +1335,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void) break; } - return 1; + return true; } static void playanim_window_open(const char *title, int posx, int posy, int sizex, int sizey) @@ -1536,6 +1537,8 @@ static char *wm_main_playanim_intern(int argc, const char **argv) GHOST_EventConsumerHandle consumer = GHOST_CreateEventConsumer(ghost_event_proc, &ps); + GHOST_SetBacktraceHandler((GHOST_TBacktraceFn)BLI_system_backtrace); + g_WS.ghost_system = GHOST_CreateSystem(); GHOST_AddEventConsumer(g_WS.ghost_system, consumer); diff --git a/source/blender/windowmanager/intern/wm_uilist_type.c b/source/blender/windowmanager/intern/wm_uilist_type.c index 88eacf9013b..6dd64b89eb6 100644 --- a/source/blender/windowmanager/intern/wm_uilist_type.c +++ b/source/blender/windowmanager/intern/wm_uilist_type.c @@ -20,7 +20,6 @@ #include "UI_interface.h" #include "BLI_ghash.h" -#include "BLI_listbase.h" #include "BLI_string.h" #include "BLI_utildefines.h" diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 7b5de5b53b8..104eda220cc 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -23,6 +23,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" +#include "BLI_system.h" #include "BLI_utildefines.h" #include "BLT_translation.h" @@ -982,7 +983,7 @@ static int query_qual(modifierKeyType qual) break; } - int val = 0; + bool val = false; GHOST_GetModifierKeyState(g_system, left, &val); if (!val) { GHOST_GetModifierKeyState(g_system, right, &val); @@ -1052,7 +1053,7 @@ void wm_window_reset_drawable(void) * * Mouse coordinate conversion happens here. */ -static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr) +static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr) { bContext *C = C_void_ptr; wmWindowManager *wm = CTX_wm_manager(C); @@ -1090,17 +1091,17 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr * but it should return if WM didn't initialize yet. * Can happen on file read (especially full size window). */ if ((wm->initialized & WM_WINDOW_IS_INIT) == 0) { - return 1; + return true; } if (!ghostwin) { /* XXX: should be checked, why are we getting an event here, and what is it? */ puts("<!> event has no window"); - return 1; + return true; } if (!GHOST_ValidWindow(g_system, ghostwin)) { /* XXX: should be checked, why are we getting an event here, and what is it? */ puts("<!> event has invalid window"); - return 1; + return true; } wmWindow *win = GHOST_GetWindowUserData(ghostwin); @@ -1443,7 +1444,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr } } } - return 1; + return true; } /** @@ -1543,6 +1544,8 @@ void wm_ghost_init(bContext *C) consumer = GHOST_CreateEventConsumer(ghost_event_proc, C); } + GHOST_SetBacktraceHandler((GHOST_TBacktraceFn)BLI_system_backtrace); + g_system = GHOST_CreateSystem(); GHOST_Debug debug = {0}; @@ -1869,11 +1872,10 @@ wmWindow *WM_window_find_under_cursor(wmWindow *win, const int mval[2], int r_mv return NULL; } - wmWindow *r_win = GHOST_GetWindowUserData(ghostwin); - wm_cursor_position_from_ghost(r_win, &tmp[0], &tmp[1]); + wmWindow *win_other = GHOST_GetWindowUserData(ghostwin); + wm_cursor_position_from_ghost(win_other, &tmp[0], &tmp[1]); copy_v2_v2_int(r_mval, tmp); - - return r_win; + return win_other; } void WM_window_pixel_sample_read(const wmWindowManager *wm, diff --git a/tests/python/modules/render_report.py b/tests/python/modules/render_report.py index cae81af3144..15441918800 100755 --- a/tests/python/modules/render_report.py +++ b/tests/python/modules/render_report.py @@ -253,8 +253,11 @@ class Report: failed = len(failed_tests) > 0 if failed: message = """<div class="alert alert-danger" role="alert">""" - message += """Run this command to update reference images for failed tests, or create images for new tests:<br>""" - message += """<tt>BLENDER_TEST_UPDATE=1 ctest -R %s</tt>""" % self.title.lower() + message += """<p>Run this command to regenerate reference (ground truth) images:</p>""" + message += """<p><tt>BLENDER_TEST_UPDATE=1 ctest -R %s</tt></p>""" % self.title.lower() + message += """<p>This then happens for new and failing tests; reference images of """ \ + """passing test cases will not be updated. Be sure to commit the new reference """ \ + """images to the SVN repository afterwards.</p>""" message += """</div>""" else: message = "" @@ -294,6 +297,7 @@ class Report: background-position:0 0, 25px 0, 25px -25px, 0px 25px; }} table td:first-child {{ width: 256px; }} + p {{ margin-bottom: 0.5rem; }} </style> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> </head> |