Compare commits
41 Commits
temp-exper
...
temp-point
Author | SHA1 | Date | |
---|---|---|---|
1339e7c44f | |||
b66fcd83f6 | |||
e583e236f3 | |||
2809419eff | |||
b4c5729726 | |||
719bea0d6d | |||
cb55872cda | |||
3e59cd213e | |||
d0cf25259d | |||
![]() |
6c15b70279 | ||
225039ff92 | |||
e3f14564a4 | |||
bf5d45fd99 | |||
abbc43e4e4 | |||
8d34b73df2 | |||
957e292c58 | |||
c3b68fa7b1 | |||
c96d596f43 | |||
b2ad52a763 | |||
e0a4b392f3 | |||
28b17ef0e6 | |||
3b77bd48f9 | |||
3a254b93fd | |||
58dae919e5 | |||
![]() |
f5c0ef52cf | ||
c56da67716 | |||
26fd55fad1 | |||
1dc9650d20 | |||
89ae4a7a2a | |||
![]() |
401279253e | ||
![]() |
2a23addf52 | ||
ff51afb194 | |||
55059af01d | |||
![]() |
acbda123ad | ||
b9b59aa6de | |||
![]() |
688e5c6d38 | ||
b70f4a265a | |||
![]() |
d5c59913de | ||
ecdbd83a8d | |||
5bd822dc46 | |||
2a8b987e9e |
@@ -42,7 +42,7 @@ def get_cmake_options(builder):
|
||||
elif builder.platform == 'linux':
|
||||
config_file = "build_files/buildbot/config/blender_linux.cmake"
|
||||
|
||||
optix_sdk_dir = os.path.join(builder.blender_dir, '..', '..', 'NVIDIA-Optix-SDK')
|
||||
optix_sdk_dir = os.path.join(builder.blender_dir, '..', '..', 'NVIDIA-Optix-SDK-7.1')
|
||||
options.append('-DOPTIX_ROOT_DIR:PATH=' + optix_sdk_dir)
|
||||
|
||||
# Workaround to build sm_30 kernels with CUDA 10, since CUDA 11 no longer supports that architecture
|
||||
|
@@ -1929,18 +1929,19 @@ void CUDADevice::render(DeviceTask &task, RenderTile &rtile, device_vector<WorkT
|
||||
}
|
||||
|
||||
uint step_samples = divide_up(min_blocks * num_threads_per_block, wtile->w * wtile->h);
|
||||
if (task.adaptive_sampling.use) {
|
||||
step_samples = task.adaptive_sampling.align_static_samples(step_samples);
|
||||
}
|
||||
|
||||
/* Render all samples. */
|
||||
int start_sample = rtile.start_sample;
|
||||
int end_sample = rtile.start_sample + rtile.num_samples;
|
||||
|
||||
for (int sample = start_sample; sample < end_sample; sample += step_samples) {
|
||||
for (int sample = start_sample; sample < end_sample;) {
|
||||
/* Setup and copy work tile to device. */
|
||||
wtile->start_sample = sample;
|
||||
wtile->num_samples = min(step_samples, end_sample - sample);
|
||||
wtile->num_samples = step_samples;
|
||||
if (task.adaptive_sampling.use) {
|
||||
wtile->num_samples = task.adaptive_sampling.align_samples(sample, step_samples);
|
||||
}
|
||||
wtile->num_samples = min(wtile->num_samples, end_sample - sample);
|
||||
work_tiles.copy_to_device();
|
||||
|
||||
CUdeviceptr d_work_tiles = (CUdeviceptr)work_tiles.device_pointer;
|
||||
@@ -1962,7 +1963,8 @@ void CUDADevice::render(DeviceTask &task, RenderTile &rtile, device_vector<WorkT
|
||||
cuda_assert(cuCtxSynchronize());
|
||||
|
||||
/* Update progress. */
|
||||
rtile.sample = sample + wtile->num_samples;
|
||||
sample += wtile->num_samples;
|
||||
rtile.sample = sample;
|
||||
task.update_progress(&rtile, rtile.w * rtile.h * wtile->num_samples);
|
||||
|
||||
if (task.get_cancel()) {
|
||||
|
@@ -920,8 +920,7 @@ class CPUDevice : public Device {
|
||||
ccl_global float *buffer = render_buffer + index * kernel_data.film.pass_stride;
|
||||
if (buffer[kernel_data.film.pass_sample_count] < 0.0f) {
|
||||
buffer[kernel_data.film.pass_sample_count] = -buffer[kernel_data.film.pass_sample_count];
|
||||
float sample_multiplier = tile.sample / max((float)tile.start_sample + 1.0f,
|
||||
buffer[kernel_data.film.pass_sample_count]);
|
||||
float sample_multiplier = tile.sample / buffer[kernel_data.film.pass_sample_count];
|
||||
if (sample_multiplier != 1.0f) {
|
||||
kernel_adaptive_post_adjust(kg, buffer, sample_multiplier);
|
||||
}
|
||||
@@ -997,7 +996,7 @@ class CPUDevice : public Device {
|
||||
coverage.finalize();
|
||||
}
|
||||
|
||||
if (task.adaptive_sampling.use) {
|
||||
if (task.adaptive_sampling.use && (tile.stealing_state != RenderTile::WAS_STOLEN)) {
|
||||
adaptive_sampling_post(tile, kg);
|
||||
}
|
||||
}
|
||||
|
@@ -760,9 +760,6 @@ class OptiXDevice : public CUDADevice {
|
||||
const int end_sample = rtile.start_sample + rtile.num_samples;
|
||||
// Keep this number reasonable to avoid running into TDRs
|
||||
int step_samples = (info.display_device ? 8 : 32);
|
||||
if (task.adaptive_sampling.use) {
|
||||
step_samples = task.adaptive_sampling.align_static_samples(step_samples);
|
||||
}
|
||||
|
||||
// Offset into launch params buffer so that streams use separate data
|
||||
device_ptr launch_params_ptr = launch_params.device_pointer +
|
||||
@@ -770,10 +767,14 @@ class OptiXDevice : public CUDADevice {
|
||||
|
||||
const CUDAContextScope scope(cuContext);
|
||||
|
||||
for (int sample = rtile.start_sample; sample < end_sample; sample += step_samples) {
|
||||
for (int sample = rtile.start_sample; sample < end_sample;) {
|
||||
// Copy work tile information to device
|
||||
wtile.num_samples = min(step_samples, end_sample - sample);
|
||||
wtile.start_sample = sample;
|
||||
wtile.num_samples = step_samples;
|
||||
if (task.adaptive_sampling.use) {
|
||||
wtile.num_samples = task.adaptive_sampling.align_samples(sample, step_samples);
|
||||
}
|
||||
wtile.num_samples = min(wtile.num_samples, end_sample - sample);
|
||||
device_ptr d_wtile_ptr = launch_params_ptr + offsetof(KernelParams, tile);
|
||||
check_result_cuda(
|
||||
cuMemcpyHtoDAsync(d_wtile_ptr, &wtile, sizeof(wtile), cuda_stream[thread_index]));
|
||||
@@ -815,7 +816,8 @@ class OptiXDevice : public CUDADevice {
|
||||
check_result_cuda(cuStreamSynchronize(cuda_stream[thread_index]));
|
||||
|
||||
// Update current sample, so it is displayed correctly
|
||||
rtile.sample = wtile.start_sample + wtile.num_samples;
|
||||
sample += wtile.num_samples;
|
||||
rtile.sample = sample;
|
||||
// Update task progress after the kernel completed rendering
|
||||
task.update_progress(&rtile, wtile.w * wtile.h * wtile.num_samples);
|
||||
|
||||
|
@@ -223,8 +223,8 @@ bool DeviceSplitKernel::path_trace(DeviceTask &task,
|
||||
subtile.num_samples = samples_per_second;
|
||||
|
||||
if (task.adaptive_sampling.use) {
|
||||
subtile.num_samples = task.adaptive_sampling.align_dynamic_samples(subtile.start_sample,
|
||||
subtile.num_samples);
|
||||
subtile.num_samples = task.adaptive_sampling.align_samples(subtile.start_sample,
|
||||
subtile.num_samples);
|
||||
}
|
||||
|
||||
/* Don't go beyond requested number of samples. */
|
||||
|
@@ -144,41 +144,20 @@ AdaptiveSampling::AdaptiveSampling() : use(true), adaptive_step(0), min_samples(
|
||||
}
|
||||
|
||||
/* Render samples in steps that align with the adaptive filtering. */
|
||||
int AdaptiveSampling::align_static_samples(int samples) const
|
||||
int AdaptiveSampling::align_samples(int sample, int num_samples) const
|
||||
{
|
||||
if (samples > adaptive_step) {
|
||||
/* Make multiple of adaptive_step. */
|
||||
while (samples % adaptive_step != 0) {
|
||||
samples--;
|
||||
}
|
||||
int end_sample = sample + num_samples;
|
||||
|
||||
/* Round down end sample to the nearest sample that needs filtering. */
|
||||
end_sample &= ~(adaptive_step - 1);
|
||||
|
||||
if (end_sample <= sample) {
|
||||
/* In order to reach the next sample that needs filtering, we'd need
|
||||
* to increase num_samples. We don't do that in this function, so
|
||||
* just keep it as is and don't filter this time around. */
|
||||
return num_samples;
|
||||
}
|
||||
else if (samples < adaptive_step) {
|
||||
/* Make divisor of adaptive_step. */
|
||||
while (adaptive_step % samples != 0) {
|
||||
samples--;
|
||||
}
|
||||
}
|
||||
|
||||
return max(samples, 1);
|
||||
}
|
||||
|
||||
/* Render samples in steps that align with the adaptive filtering, with the
|
||||
* suggested number of samples dynamically changing. */
|
||||
int AdaptiveSampling::align_dynamic_samples(int offset, int samples) const
|
||||
{
|
||||
/* Round so that we end up on multiples of adaptive_samples. */
|
||||
samples += offset;
|
||||
|
||||
if (samples > adaptive_step) {
|
||||
/* Make multiple of adaptive_step. */
|
||||
while (samples % adaptive_step != 0) {
|
||||
samples--;
|
||||
}
|
||||
}
|
||||
|
||||
samples -= offset;
|
||||
|
||||
return max(samples, 1);
|
||||
return end_sample - sample;
|
||||
}
|
||||
|
||||
bool AdaptiveSampling::need_filter(int sample) const
|
||||
|
@@ -117,8 +117,7 @@ class AdaptiveSampling {
|
||||
public:
|
||||
AdaptiveSampling();
|
||||
|
||||
int align_static_samples(int samples) const;
|
||||
int align_dynamic_samples(int offset, int samples) const;
|
||||
int align_samples(int sample, int num_samples) const;
|
||||
bool need_filter(int sample) const;
|
||||
|
||||
bool use;
|
||||
|
@@ -139,7 +139,7 @@ kernel_cuda_adaptive_scale_samples(WorkTile *tile, int start_sample, int sample,
|
||||
ccl_global float *buffer = tile->buffer + index * kernel_data.film.pass_stride;
|
||||
if(buffer[kernel_data.film.pass_sample_count] < 0.0f) {
|
||||
buffer[kernel_data.film.pass_sample_count] = -buffer[kernel_data.film.pass_sample_count];
|
||||
float sample_multiplier = sample / max((float)start_sample + 1.0f, buffer[kernel_data.film.pass_sample_count]);
|
||||
float sample_multiplier = sample / buffer[kernel_data.film.pass_sample_count];
|
||||
if(sample_multiplier != 1.0f) {
|
||||
kernel_adaptive_post_adjust(&kg, buffer, sample_multiplier);
|
||||
}
|
||||
|
@@ -29,8 +29,7 @@ ccl_device void kernel_adaptive_adjust_samples(KernelGlobals *kg)
|
||||
int sample = kernel_split_params.tile.start_sample + kernel_split_params.tile.num_samples;
|
||||
if (buffer[kernel_data.film.pass_sample_count] < 0.0f) {
|
||||
buffer[kernel_data.film.pass_sample_count] = -buffer[kernel_data.film.pass_sample_count];
|
||||
float sample_multiplier = sample / max((float)kernel_split_params.tile.start_sample + 1.0f,
|
||||
buffer[kernel_data.film.pass_sample_count]);
|
||||
float sample_multiplier = sample / buffer[kernel_data.film.pass_sample_count];
|
||||
if (sample_multiplier != 1.0f) {
|
||||
kernel_adaptive_post_adjust(kg, buffer, sample_multiplier);
|
||||
}
|
||||
|
@@ -459,13 +459,17 @@ bool Session::acquire_tile(RenderTile &rtile, Device *tile_device, uint tile_typ
|
||||
int device_num = device->device_number(tile_device);
|
||||
|
||||
while (!tile_manager.next_tile(tile, device_num, tile_types)) {
|
||||
if (steal_tile(rtile, tile_device, tile_lock)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Wait for denoising tiles to become available */
|
||||
if ((tile_types & RenderTile::DENOISE) && !progress.get_cancel() && tile_manager.has_tiles()) {
|
||||
denoising_cond.wait(tile_lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
return steal_tile(rtile, tile_device, tile_lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* fill render tile */
|
||||
@@ -477,6 +481,7 @@ bool Session::acquire_tile(RenderTile &rtile, Device *tile_device, uint tile_typ
|
||||
rtile.num_samples = tile_manager.state.num_samples;
|
||||
rtile.resolution = tile_manager.state.resolution_divider;
|
||||
rtile.tile_index = tile->index;
|
||||
rtile.stealing_state = RenderTile::NO_STEALING;
|
||||
|
||||
if (tile->state == Tile::DENOISE) {
|
||||
rtile.task = RenderTile::DENOISE;
|
||||
|
@@ -225,6 +225,9 @@ GHOST_SystemWin32::GHOST_SystemWin32()
|
||||
#ifdef WITH_INPUT_NDOF
|
||||
m_ndofManager = new GHOST_NDOFManagerWin32(*this);
|
||||
#endif
|
||||
|
||||
getCursorPosition(m_mousePosX, m_mousePosY);
|
||||
m_mouseTimestamp = ::GetTickCount();
|
||||
}
|
||||
|
||||
GHOST_SystemWin32::~GHOST_SystemWin32()
|
||||
@@ -533,7 +536,20 @@ GHOST_TSuccess GHOST_SystemWin32::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32
|
||||
{
|
||||
if (!::GetActiveWindow())
|
||||
return GHOST_kFailure;
|
||||
return ::SetCursorPos(x, y) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
|
||||
|
||||
INPUT input;
|
||||
input.type = INPUT_MOUSE;
|
||||
input.mi.mouseData = 0;
|
||||
input.mi.time = ::GetTickCount();
|
||||
/* Map from virtual screen to 0-65536. */
|
||||
input.mi.dx = (x - GetSystemMetrics(SM_XVIRTUALSCREEN)) * 65536 /
|
||||
GetSystemMetrics(SM_CXVIRTUALSCREEN);
|
||||
input.mi.dy = (y - GetSystemMetrics(SM_YVIRTUALSCREEN)) * 65536 /
|
||||
GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
||||
input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK;
|
||||
SendInput(1, &input, sizeof(input));
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_SystemWin32::getModifierKeys(GHOST_ModifierKeys &keys) const
|
||||
@@ -942,8 +958,18 @@ GHOST_EventButton *GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type,
|
||||
GHOST_TabletData td = window->m_tabletInRange ? window->getLastTabletData() :
|
||||
GHOST_TABLET_DATA_NONE;
|
||||
|
||||
/* Ensure button click occurs at its intended position. */
|
||||
processCursorEvent(window);
|
||||
/* Move mouse to button event position. */
|
||||
if (!window->m_tabletInRange) {
|
||||
processCursorEvent(window);
|
||||
}
|
||||
else {
|
||||
/* Tablet should be hadling inbetween mouse moves, only move to event position. */
|
||||
DWORD msgPos = ::GetMessagePos();
|
||||
int msgPosX = GET_X_LPARAM(msgPos);
|
||||
int msgPosY = GET_Y_LPARAM(msgPos);
|
||||
system->pushEvent(new GHOST_EventCursor(
|
||||
::GetMessageTime(), GHOST_kEventCursorMove, window, msgPosX, msgPosY, td));
|
||||
}
|
||||
|
||||
window->updateMouseCapture(type == GHOST_kEventButtonDown ? MousePressed : MouseReleased);
|
||||
return new GHOST_EventButton(system->getMilliSeconds(), type, window, mask, td);
|
||||
@@ -1101,18 +1127,79 @@ void GHOST_SystemWin32::processPointerEvent(
|
||||
system->setCursorPosition(pointerInfo[0].pixelLocation.x, pointerInfo[0].pixelLocation.y);
|
||||
}
|
||||
|
||||
GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *window)
|
||||
void GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *window)
|
||||
{
|
||||
/* Cursor moves handled by tablets while active. */
|
||||
if (window->m_tabletInRange) {
|
||||
return;
|
||||
}
|
||||
|
||||
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
|
||||
|
||||
DWORD msgPos = ::GetMessagePos();
|
||||
GHOST_TInt32 x_screen = GET_X_LPARAM(msgPos);
|
||||
GHOST_TInt32 y_screen = GET_Y_LPARAM(msgPos);
|
||||
GHOST_TInt32 x_accum = 0, y_accum = 0;
|
||||
LONG msgTime = ::GetMessageTime();
|
||||
|
||||
if (window->getCursorGrabModeIsWarp() && !window->m_tabletInRange) {
|
||||
GHOST_TInt32 x_new = x_screen;
|
||||
GHOST_TInt32 y_new = y_screen;
|
||||
/* GetMessagePointsEx processes points as 16 bit integers and can fail or return erroneous values
|
||||
* if negative input is not truncated. */
|
||||
int msgPosX = GET_X_LPARAM(msgPos) & 0x0000FFFF;
|
||||
int msgPosY = GET_Y_LPARAM(msgPos) & 0x0000FFFF;
|
||||
|
||||
const int maxPoints = 64;
|
||||
MOUSEMOVEPOINT currentPoint = {msgPosX, msgPosY, (DWORD)msgTime, 0};
|
||||
MOUSEMOVEPOINT points[maxPoints] = {0};
|
||||
/* GetMouseMovePointsEx returns the number of points returned that are less than or equal to the
|
||||
* requested point. If the requested point is the most recent, this returns up to 64 requested
|
||||
* points. */
|
||||
int numPoints = ::GetMouseMovePointsEx(
|
||||
sizeof(MOUSEMOVEPOINT), ¤tPoint, points, maxPoints, GMMP_USE_DISPLAY_POINTS);
|
||||
|
||||
if (numPoints == -1) {
|
||||
/* Points at edge of screen are often not in the queue, use the message's point instead. */
|
||||
numPoints = 1;
|
||||
points[0] = currentPoint;
|
||||
}
|
||||
|
||||
GHOST_TInt32 x_accum = 0, y_accum = 0;
|
||||
window->getCursorGrabAccum(x_accum, y_accum);
|
||||
|
||||
/* Points are in reverse chronological order. Find least recent, unprocessed mouse move. */
|
||||
int i;
|
||||
for (i = 0; i < numPoints; i++) {
|
||||
if (points[i].time < system->m_mouseTimestamp) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* GetMouseMovePointsEx returns 16 bit number as 32 bit. If negative, we need to sign extend.
|
||||
*/
|
||||
points[i].x = points[i].x > 32767 ? points[i].x | 0xFFFF0000 : points[i].x;
|
||||
points[i].y = points[i].y > 32767 ? points[i].y | 0xFFFF0000 : points[i].y;
|
||||
|
||||
if (points[i].time == system->m_mouseTimestamp && points[i].x == system->m_mousePosX &&
|
||||
points[i].y == system->m_mousePosY) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (--i >= 0) {
|
||||
system->pushEvent(new GHOST_EventCursor(system->getMilliSeconds(),
|
||||
GHOST_kEventCursorMove,
|
||||
window,
|
||||
points[i].x + x_accum,
|
||||
points[i].y + y_accum,
|
||||
GHOST_TABLET_DATA_NONE));
|
||||
}
|
||||
|
||||
DWORD lastTimestamp = points[0].time;
|
||||
|
||||
/* Check if we need to wrap the cursor. */
|
||||
if (window->getCursorGrabModeIsWarp()) {
|
||||
/* Wrap based on current cursor position in case Win32 mouse move queue is out of order due to
|
||||
* prior wrap. */
|
||||
POINT point;
|
||||
::GetCursorPos(&point);
|
||||
GHOST_TInt32 x_current = point.x;
|
||||
GHOST_TInt32 y_current = point.y;
|
||||
GHOST_TInt32 x_wrap = point.x;
|
||||
GHOST_TInt32 y_wrap = point.y;
|
||||
GHOST_Rect bounds;
|
||||
|
||||
/* Fallback to window bounds. */
|
||||
@@ -1122,23 +1209,24 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
|
||||
|
||||
/* Could also clamp to screen bounds wrap with a window outside the view will fail atm.
|
||||
* Use offset of 8 in case the window is at screen bounds. */
|
||||
bounds.wrapPoint(x_new, y_new, 2, window->getCursorGrabAxis());
|
||||
bounds.wrapPoint(x_wrap, y_wrap, 2, window->getCursorGrabAxis());
|
||||
|
||||
window->getCursorGrabAccum(x_accum, y_accum);
|
||||
if (x_new != x_screen || y_new != y_screen) {
|
||||
system->setCursorPosition(x_new, y_new); /* wrap */
|
||||
window->setCursorGrabAccum(x_accum + (x_screen - x_new), y_accum + (y_screen - y_new));
|
||||
if (x_wrap != x_current || y_wrap != y_current) {
|
||||
system->setCursorPosition(x_wrap, y_wrap);
|
||||
window->setCursorGrabAccum(x_accum + (x_current - x_wrap), y_accum + (y_current - y_wrap));
|
||||
|
||||
/* First message after SendInput wrap is invalid for unknown reasons, skip events until one
|
||||
* tick after SendInput event time. */
|
||||
lastTimestamp = ::GetTickCount() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_TabletData td = window->m_tabletInRange ? window->getLastTabletData() :
|
||||
GHOST_TABLET_DATA_NONE;
|
||||
return new GHOST_EventCursor(system->getMilliSeconds(),
|
||||
GHOST_kEventCursorMove,
|
||||
window,
|
||||
x_screen + x_accum,
|
||||
y_screen + y_accum,
|
||||
td);
|
||||
system->m_mousePosX = points[0].x;
|
||||
system->m_mousePosY = points[0].y;
|
||||
/* Use latest time, checking for overflow. */
|
||||
if (lastTimestamp > system->m_mouseTimestamp || ::GetTickCount() < system->m_mouseTimestamp) {
|
||||
system->m_mouseTimestamp = lastTimestamp;
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam)
|
||||
@@ -1631,7 +1719,8 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
||||
}
|
||||
|
||||
if (!window->m_tabletInRange) {
|
||||
event = processCursorEvent(window);
|
||||
processCursorEvent(window);
|
||||
eventHandled = true;
|
||||
}
|
||||
break;
|
||||
case WM_MOUSEWHEEL: {
|
||||
@@ -1677,6 +1766,9 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
||||
case WM_MOUSELEAVE:
|
||||
window->m_mousePresent = false;
|
||||
window->setWintabOverlap(false);
|
||||
if (!window->m_tabletInRange) {
|
||||
processCursorEvent(window);
|
||||
}
|
||||
break;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Mouse events, ignored
|
||||
|
@@ -340,9 +340,8 @@ class GHOST_SystemWin32 : public GHOST_System {
|
||||
/**
|
||||
* Creates cursor event.
|
||||
* \param window: The window receiving the event (the active window).
|
||||
* \return The event created.
|
||||
*/
|
||||
static GHOST_EventCursor *processCursorEvent(GHOST_WindowWin32 *window);
|
||||
static void processCursorEvent(GHOST_WindowWin32 *window);
|
||||
|
||||
/**
|
||||
* Handles a mouse wheel event.
|
||||
@@ -463,16 +462,23 @@ class GHOST_SystemWin32 : public GHOST_System {
|
||||
__int64 m_lfstart;
|
||||
/** AltGr on current keyboard layout. */
|
||||
bool m_hasAltGr;
|
||||
/** language identifier. */
|
||||
/** Language identifier. */
|
||||
WORD m_langId;
|
||||
/** stores keyboard layout. */
|
||||
/** Stores keyboard layout. */
|
||||
HKL m_keylayout;
|
||||
|
||||
/** Console status */
|
||||
/** Console status. */
|
||||
int m_consoleStatus;
|
||||
|
||||
/** Wheel delta accumulator */
|
||||
/** Wheel delta accumulator. */
|
||||
int m_wheelDeltaAccum;
|
||||
|
||||
/** Last mouse x position. */
|
||||
int m_mousePosX;
|
||||
/** Last mouse y position. */
|
||||
int m_mousePosY;
|
||||
/** Last mouse timestamp. */
|
||||
DWORD m_mouseTimestamp;
|
||||
};
|
||||
|
||||
inline void GHOST_SystemWin32::retrieveModifierKeys(GHOST_ModifierKeys &keys) const
|
||||
|
BIN
release/datafiles/icons/brush.sculpt.displacement_smear.dat
Normal file
BIN
release/datafiles/icons/brush.sculpt.displacement_smear.dat
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -303,11 +303,11 @@ class CLIP_MT_masking_editor_menus(Menu):
|
||||
|
||||
if clip:
|
||||
layout.menu("MASK_MT_select")
|
||||
layout.menu("CLIP_MT_clip") # XXX - remove?
|
||||
layout.menu("CLIP_MT_clip")
|
||||
layout.menu("MASK_MT_add")
|
||||
layout.menu("MASK_MT_mask")
|
||||
else:
|
||||
layout.menu("CLIP_MT_clip") # XXX - remove?
|
||||
layout.menu("CLIP_MT_clip")
|
||||
|
||||
|
||||
class CLIP_PT_clip_view_panel:
|
||||
@@ -1330,10 +1330,17 @@ class CLIP_MT_clip(Menu):
|
||||
layout.operator("clip.open")
|
||||
|
||||
if clip:
|
||||
layout.operator("clip.set_scene_frames")
|
||||
layout.operator("clip.set_center_principal")
|
||||
layout.operator("clip.prefetch")
|
||||
layout.operator("clip.reload")
|
||||
layout.menu("CLIP_MT_proxy")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("clip.set_viewport_background")
|
||||
layout.operator("clip.setup_tracking_scene")
|
||||
|
||||
|
||||
class CLIP_MT_proxy(Menu):
|
||||
bl_label = "Proxy"
|
||||
@@ -1345,66 +1352,161 @@ class CLIP_MT_proxy(Menu):
|
||||
layout.operator("clip.delete_proxy")
|
||||
|
||||
|
||||
class CLIP_MT_track(Menu):
|
||||
bl_label = "Track"
|
||||
class CLIP_MT_track_transform(Menu):
|
||||
bl_label = "Transform"
|
||||
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("clip.clear_solution")
|
||||
layout.operator("clip.solve_camera")
|
||||
layout.operator("transform.translate")
|
||||
layout.operator("transform.rotate")
|
||||
layout.operator("transform.resize")
|
||||
|
||||
layout.separator()
|
||||
props = layout.operator("clip.clear_track_path", text="Clear After")
|
||||
props.clear_active = False
|
||||
props.action = 'REMAINED'
|
||||
|
||||
props = layout.operator("clip.clear_track_path", text="Clear Before")
|
||||
class CLIP_MT_track_motion(Menu):
|
||||
bl_label = "Track Motion"
|
||||
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
props = layout.operator("clip.track_markers", text="Backwards")
|
||||
props.backwards = True
|
||||
props.sequence = True
|
||||
|
||||
props = layout.operator("clip.track_markers", text="Frame Backwards")
|
||||
props.backwards = True
|
||||
props.sequence = False
|
||||
|
||||
props = layout.operator("clip.track_markers", text="Forwards")
|
||||
props.backwards = False
|
||||
props.sequence = True
|
||||
|
||||
props = layout.operator("clip.track_markers", text="Frame Forwards")
|
||||
props.backwards = False
|
||||
props.sequence = False
|
||||
|
||||
|
||||
class CLIP_MT_track_clear(Menu):
|
||||
bl_label = "Clear"
|
||||
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
props = layout.operator("clip.clear_track_path", text="Before")
|
||||
props.clear_active = False
|
||||
props.action = 'UPTO'
|
||||
|
||||
props = layout.operator("clip.clear_track_path", text="Clear Track Path")
|
||||
props = layout.operator("clip.clear_track_path", text="After")
|
||||
props.clear_active = False
|
||||
props.action = 'REMAINED'
|
||||
|
||||
props = layout.operator("clip.clear_track_path", text="Track Path")
|
||||
props.clear_active = False
|
||||
props.action = 'ALL'
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("clip.clear_solution", text="Solution")
|
||||
|
||||
|
||||
class CLIP_MT_track_refine(Menu):
|
||||
bl_label = "Refine"
|
||||
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
props = layout.operator("clip.refine_markers", text="Backwards")
|
||||
props.backwards = True
|
||||
|
||||
props = layout.operator("clip.refine_markers", text="Fowards")
|
||||
props.backwards = False
|
||||
|
||||
|
||||
class CLIP_MT_track_animation(Menu):
|
||||
bl_label = "Animation"
|
||||
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("clip.keyframe_insert")
|
||||
layout.operator("clip.keyframe_delete")
|
||||
|
||||
|
||||
class CLIP_MT_track_visibility(Menu):
|
||||
bl_label = "Show/Hide"
|
||||
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("clip.hide_tracks_clear")
|
||||
layout.operator("clip.hide_tracks", text="Hide Selected").unselected = False
|
||||
layout.operator("clip.hide_tracks", text="Hide Unselected").unselected = True
|
||||
|
||||
|
||||
class CLIP_MT_track_cleanup(Menu):
|
||||
bl_label = "Clean Up"
|
||||
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("clip.clean_tracks")
|
||||
layout.operator("clip.filter_tracks")
|
||||
|
||||
|
||||
class CLIP_MT_track(Menu):
|
||||
bl_label = "Track"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
clip = context.space_data.clip
|
||||
tracking_object = clip.tracking.objects.active
|
||||
|
||||
layout.menu("CLIP_MT_track_transform")
|
||||
layout.menu("CLIP_MT_track_motion")
|
||||
layout.menu("CLIP_MT_track_clear")
|
||||
layout.menu("CLIP_MT_track_refine")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("clip.add_marker_move", text="Add Marker")
|
||||
layout.operator("clip.detect_features")
|
||||
layout.operator("clip.create_plane_track")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("clip.solve_camera",
|
||||
text="Solve Camera Motion" if tracking_object.is_camera
|
||||
else "Solve Object Motion")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("clip.join_tracks")
|
||||
|
||||
layout.separator()
|
||||
layout.operator("clip.clean_tracks")
|
||||
|
||||
layout.operator("clip.copy_tracks", icon='COPYDOWN')
|
||||
layout.operator("clip.paste_tracks", icon='PASTEDOWN')
|
||||
|
||||
layout.separator()
|
||||
layout.operator("clip.copy_tracks")
|
||||
layout.operator("clip.paste_tracks")
|
||||
|
||||
layout.operator("clip.track_settings_as_default", text="Copy Settings to Defaults")
|
||||
layout.operator("clip.track_settings_to_track", text="Apply Default Settings")
|
||||
|
||||
layout.separator()
|
||||
props = layout.operator("clip.track_markers", text="Track Frame Backwards")
|
||||
props.backwards = True
|
||||
props.sequence = False
|
||||
|
||||
props = layout.operator("clip.track_markers", text="Track Backwards")
|
||||
props.backwards = True
|
||||
props.sequence = True
|
||||
|
||||
props = layout.operator("clip.track_markers", text="Track Forwards")
|
||||
props.backwards = False
|
||||
props.sequence = True
|
||||
|
||||
props = layout.operator("clip.track_markers", text="Track Frame Forwards")
|
||||
props.backwards = False
|
||||
props.sequence = False
|
||||
layout.menu("CLIP_MT_track_animation")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.menu("CLIP_MT_track_visibility")
|
||||
layout.menu("CLIP_MT_track_cleanup")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("clip.delete_track")
|
||||
layout.operator("clip.delete_marker")
|
||||
|
||||
layout.separator()
|
||||
layout.operator("clip.add_marker_move")
|
||||
|
||||
layout.separator()
|
||||
layout.menu("CLIP_MT_track_visibility")
|
||||
layout.menu("CLIP_MT_track_transform")
|
||||
|
||||
|
||||
class CLIP_MT_reconstruction(Menu):
|
||||
bl_label = "Reconstruction"
|
||||
@@ -1420,6 +1522,7 @@ class CLIP_MT_reconstruction(Menu):
|
||||
layout.operator("clip.set_axis", text="Set Y Axis").axis = 'Y'
|
||||
|
||||
layout.operator("clip.set_scale")
|
||||
layout.operator("clip.apply_solution_scale")
|
||||
|
||||
layout.separator()
|
||||
|
||||
@@ -1427,25 +1530,13 @@ class CLIP_MT_reconstruction(Menu):
|
||||
layout.operator("clip.bundles_to_mesh")
|
||||
|
||||
|
||||
class CLIP_MT_track_visibility(Menu):
|
||||
bl_label = "Show/Hide"
|
||||
class CLIP_MT_select_grouped(Menu):
|
||||
bl_label = "Select Grouped"
|
||||
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("clip.hide_tracks_clear")
|
||||
layout.operator("clip.hide_tracks", text="Hide Selected").unselected = False
|
||||
layout.operator("clip.hide_tracks", text="Hide Unselected").unselected = True
|
||||
|
||||
|
||||
class CLIP_MT_track_transform(Menu):
|
||||
bl_label = "Transform"
|
||||
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("transform.translate")
|
||||
layout.operator("transform.resize")
|
||||
layout.operator_enum("clip.select_grouped", "group")
|
||||
|
||||
|
||||
class CLIP_MT_select(Menu):
|
||||
@@ -1459,21 +1550,15 @@ class CLIP_MT_select(Menu):
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("clip.select_all"
|
||||
).action = 'TOGGLE'
|
||||
layout.operator("clip.select_all",
|
||||
text="Inverse").action = 'INVERT'
|
||||
layout.operator("clip.select_all").action = 'TOGGLE'
|
||||
layout.operator("clip.select_all", text="Inverse").action = 'INVERT'
|
||||
|
||||
layout.menu("CLIP_MT_select_grouped")
|
||||
|
||||
layout.separator()
|
||||
|
||||
class CLIP_MT_select_grouped(Menu):
|
||||
bl_label = "Select Grouped"
|
||||
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator_enum("clip.select_grouped", "group")
|
||||
layout.operator("clip.stabilize_2d_select")
|
||||
layout.operator("clip.stabilize_2d_rotation_select")
|
||||
|
||||
|
||||
class CLIP_MT_tracking_context_menu(Menu):
|
||||
@@ -1746,7 +1831,6 @@ classes = (
|
||||
CLIP_PT_display,
|
||||
CLIP_PT_clip_display,
|
||||
CLIP_PT_marker_display,
|
||||
CLIP_MT_track,
|
||||
CLIP_MT_tracking_editor_menus,
|
||||
CLIP_MT_masking_editor_menus,
|
||||
CLIP_PT_track,
|
||||
@@ -1786,8 +1870,14 @@ classes = (
|
||||
CLIP_MT_clip,
|
||||
CLIP_MT_proxy,
|
||||
CLIP_MT_reconstruction,
|
||||
CLIP_MT_track_visibility,
|
||||
CLIP_MT_track,
|
||||
CLIP_MT_track_transform,
|
||||
CLIP_MT_track_motion,
|
||||
CLIP_MT_track_clear,
|
||||
CLIP_MT_track_refine,
|
||||
CLIP_MT_track_animation,
|
||||
CLIP_MT_track_visibility,
|
||||
CLIP_MT_track_cleanup,
|
||||
CLIP_MT_select,
|
||||
CLIP_MT_select_grouped,
|
||||
CLIP_MT_tracking_context_menu,
|
||||
|
@@ -1004,7 +1004,7 @@ def _activate_by_item(context, space_type, item, index, *, as_fallback=False):
|
||||
if item.draw_cursor is not None:
|
||||
def handle_fn(context, item, tool, xy):
|
||||
item.draw_cursor(context, tool, xy)
|
||||
handle = WindowManager.draw_cursor_add(handle_fn, (context, item, tool), space_type)
|
||||
handle = WindowManager.draw_cursor_add(handle_fn, (context, item, tool), space_type, 'WINDOW')
|
||||
handle_map[space_type] = handle
|
||||
|
||||
|
||||
|
@@ -488,6 +488,7 @@ geometry_node_categories = [
|
||||
NodeItem("GeometryNodeAttributeFill"),
|
||||
NodeItem("GeometryNodeAttributeMix"),
|
||||
NodeItem("GeometryNodeAttributeColorRamp"),
|
||||
NodeItem("GeometryNodeAttributeVectorMath"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_COLOR", "Color", items=[
|
||||
NodeItem("ShaderNodeValToRGB"),
|
||||
@@ -515,6 +516,7 @@ geometry_node_categories = [
|
||||
NodeItem("GeometryNodePointInstance"),
|
||||
NodeItem("GeometryNodePointSeparate"),
|
||||
NodeItem("GeometryNodeRotatePoints"),
|
||||
NodeItem("GeometryNodeAlignRotationToVector"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[
|
||||
NodeItem("ShaderNodeMapRange"),
|
||||
|
@@ -169,8 +169,10 @@ class WriteAttribute {
|
||||
}
|
||||
|
||||
/* Get a span that new attribute values can be written into. When all values have been changed,
|
||||
* #apply_span has to be called. The span might not contain the original attribute values. */
|
||||
* #apply_span has to be called. */
|
||||
fn::GMutableSpan get_span();
|
||||
/* The span returned by this method might not contain the current attribute values. */
|
||||
fn::GMutableSpan get_span_for_write_only();
|
||||
/* Write the changes to the span into the actual attribute, if they aren't already. */
|
||||
void apply_span();
|
||||
|
||||
@@ -178,7 +180,7 @@ class WriteAttribute {
|
||||
virtual void get_internal(const int64_t index, void *r_value) const = 0;
|
||||
virtual void set_internal(const int64_t index, const void *value) = 0;
|
||||
|
||||
virtual void initialize_span();
|
||||
virtual void initialize_span(const bool write_only);
|
||||
virtual void apply_span_if_necessary();
|
||||
};
|
||||
|
||||
@@ -250,12 +252,16 @@ template<typename T> class TypedWriteAttribute {
|
||||
}
|
||||
|
||||
/* Get a span that new values can be written into. Once all values have been updated #apply_span
|
||||
* has to be called. The span might *not* contain the initial attribute values, so one should
|
||||
* generally only write to the span. */
|
||||
* has to be called. */
|
||||
MutableSpan<T> get_span()
|
||||
{
|
||||
return attribute_->get_span().typed<T>();
|
||||
}
|
||||
/* The span returned by this method might not contain the current attribute values. */
|
||||
MutableSpan<T> get_span_for_write_only()
|
||||
{
|
||||
return attribute_->get_span_for_write_only().typed<T>();
|
||||
}
|
||||
|
||||
/* Write back all changes to the actual attribute, if necessary. */
|
||||
void apply_span()
|
||||
|
59
source/blender/blenkernel/BKE_cryptomatte.hh
Normal file
59
source/blender/blenkernel/BKE_cryptomatte.hh
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2020 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* Format to a cryptomatte meta data key.
|
||||
*
|
||||
* Cryptomatte stores meta data. The keys are formatted containing a hash that
|
||||
* is generated from its layer name.
|
||||
*
|
||||
* The output of this function is:
|
||||
* 'cryptomatte/{hash of layer_name}/{key_name}'.
|
||||
*/
|
||||
std::string BKE_cryptomatte_meta_data_key(const StringRef layer_name,
|
||||
const StringRefNull key_name);
|
||||
|
||||
/* Extract the cryptomatte layer name from the given `render_pass_name`.
|
||||
*
|
||||
* Cryptomatte passes are formatted with a trailing number for storing multiple samples that belong
|
||||
* to the same cryptomatte layer. This function would remove the trailing numbers to determine the
|
||||
* cryptomatte layer name.
|
||||
*
|
||||
* # Example
|
||||
*
|
||||
* A render_pass_name could be 'View Layer.CryptoMaterial02'. The cryptomatte layer would be 'View
|
||||
* Layer.CryptoMaterial'.
|
||||
*
|
||||
* NOTE: The return type is a substring of `render_pass_name` and therefore cannot outlive the
|
||||
* `render_pass_name` internal data.
|
||||
*/
|
||||
StringRef BKE_cryptomatte_extract_layer_name(const StringRef render_pass_name);
|
||||
|
||||
} // namespace blender
|
@@ -80,6 +80,9 @@ float BKE_defvert_multipaint_collective_weight(const struct MDeformVert *dv,
|
||||
int defbase_tot_sel,
|
||||
bool is_normalized);
|
||||
|
||||
/* This much unlocked weight is considered equivalent to none. */
|
||||
#define VERTEX_WEIGHT_LOCK_EPSILON 1e-6f
|
||||
|
||||
float BKE_defvert_calc_lock_relative_weight(float weight,
|
||||
float locked_weight,
|
||||
float unlocked_weight);
|
||||
|
@@ -1357,6 +1357,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
|
||||
#define GEO_NODE_POINT_SEPARATE 1014
|
||||
#define GEO_NODE_ATTRIBUTE_COMPARE 1015
|
||||
#define GEO_NODE_ROTATE_POINTS 1016
|
||||
#define GEO_NODE_ATTRIBUTE_VECTOR_MATH 1017
|
||||
#define GEO_NODE_ALIGN_ROTATION_TO_VECTOR 1018
|
||||
|
||||
/** \} */
|
||||
|
||||
|
@@ -288,7 +288,6 @@ void BKE_object_eval_uber_data(struct Depsgraph *depsgraph,
|
||||
struct Object *ob);
|
||||
void BKE_object_eval_assign_data(struct Object *object, struct ID *data, bool is_owned);
|
||||
|
||||
void BKE_object_eval_boundbox(struct Depsgraph *depsgraph, struct Object *object);
|
||||
void BKE_object_sync_to_original(struct Depsgraph *depsgraph, struct Object *object);
|
||||
|
||||
void BKE_object_eval_ptcache_reset(struct Depsgraph *depsgraph,
|
||||
|
@@ -731,6 +731,7 @@ add_dependencies(bf_blenkernel bf_dna)
|
||||
if(WITH_GTESTS)
|
||||
set(TEST_SRC
|
||||
intern/armature_test.cc
|
||||
intern/cryptomatte_test.cc
|
||||
intern/fcurve_test.cc
|
||||
intern/lattice_deform_test.cc
|
||||
intern/layer_test.cc
|
||||
|
@@ -830,18 +830,6 @@ void BKE_pose_splineik_evaluate(struct Depsgraph *depsgraph,
|
||||
BKE_splineik_execute_tree(depsgraph, scene, object, rootchan, ctime);
|
||||
}
|
||||
|
||||
/* Common part for both original and proxy armatrues. */
|
||||
static void pose_eval_done_common(struct Depsgraph *depsgraph, Object *object)
|
||||
{
|
||||
const bArmature *armature = (bArmature *)object->data;
|
||||
if (armature->edbo != NULL) {
|
||||
return;
|
||||
}
|
||||
bPose *pose = object->pose;
|
||||
UNUSED_VARS_NDEBUG(pose);
|
||||
BLI_assert(pose != NULL);
|
||||
BKE_object_eval_boundbox(depsgraph, object);
|
||||
}
|
||||
static void pose_eval_cleanup_common(Object *object)
|
||||
{
|
||||
bPose *pose = object->pose;
|
||||
@@ -857,7 +845,6 @@ void BKE_pose_eval_done(struct Depsgraph *depsgraph, Object *object)
|
||||
UNUSED_VARS_NDEBUG(pose);
|
||||
DEG_debug_print_eval(depsgraph, __func__, object->id.name, object);
|
||||
BLI_assert(object->type == OB_ARMATURE);
|
||||
pose_eval_done_common(depsgraph, object);
|
||||
}
|
||||
|
||||
void BKE_pose_eval_cleanup(struct Depsgraph *depsgraph, Scene *scene, Object *object)
|
||||
@@ -885,7 +872,6 @@ void BKE_pose_eval_proxy_done(struct Depsgraph *depsgraph, Object *object)
|
||||
{
|
||||
BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL);
|
||||
DEG_debug_print_eval(depsgraph, __func__, object->id.name, object);
|
||||
pose_eval_done_common(depsgraph, object);
|
||||
}
|
||||
|
||||
void BKE_pose_eval_proxy_cleanup(struct Depsgraph *depsgraph, Object *object)
|
||||
|
@@ -97,26 +97,45 @@ WriteAttribute::~WriteAttribute()
|
||||
|
||||
/**
|
||||
* Get a mutable span that can be modified. When all modifications to the attribute are done,
|
||||
* #apply_span_if_necessary should be called.
|
||||
*/
|
||||
* #apply_span should be called. */
|
||||
fn::GMutableSpan WriteAttribute::get_span()
|
||||
{
|
||||
if (size_ == 0) {
|
||||
return fn::GMutableSpan(cpp_type_);
|
||||
}
|
||||
if (array_buffer_ == nullptr) {
|
||||
this->initialize_span();
|
||||
this->initialize_span(false);
|
||||
}
|
||||
array_should_be_applied_ = true;
|
||||
return fn::GMutableSpan(cpp_type_, array_buffer_, size_);
|
||||
}
|
||||
|
||||
void WriteAttribute::initialize_span()
|
||||
fn::GMutableSpan WriteAttribute::get_span_for_write_only()
|
||||
{
|
||||
array_buffer_ = MEM_mallocN_aligned(cpp_type_.size() * size_, cpp_type_.alignment(), __func__);
|
||||
if (size_ == 0) {
|
||||
return fn::GMutableSpan(cpp_type_);
|
||||
}
|
||||
if (array_buffer_ == nullptr) {
|
||||
this->initialize_span(true);
|
||||
}
|
||||
array_should_be_applied_ = true;
|
||||
return fn::GMutableSpan(cpp_type_, array_buffer_, size_);
|
||||
}
|
||||
|
||||
void WriteAttribute::initialize_span(const bool write_only)
|
||||
{
|
||||
const int element_size = cpp_type_.size();
|
||||
array_buffer_ = MEM_mallocN_aligned(element_size * size_, cpp_type_.alignment(), __func__);
|
||||
array_is_temporary_ = true;
|
||||
/* This does nothing for trivial types, but is necessary for general correctness. */
|
||||
cpp_type_.construct_default_n(array_buffer_, size_);
|
||||
if (write_only) {
|
||||
/* This does nothing for trivial types, but is necessary for general correctness. */
|
||||
cpp_type_.construct_default_n(array_buffer_, size_);
|
||||
}
|
||||
else {
|
||||
for (const int i : IndexRange(size_)) {
|
||||
this->get(i, POINTER_OFFSET(array_buffer_, i * element_size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WriteAttribute::apply_span()
|
||||
@@ -219,7 +238,7 @@ template<typename T> class ArrayWriteAttribute final : public WriteAttribute {
|
||||
data_[index] = *reinterpret_cast<const T *>(value);
|
||||
}
|
||||
|
||||
void initialize_span() override
|
||||
void initialize_span(const bool UNUSED(write_only)) override
|
||||
{
|
||||
array_buffer_ = data_.data();
|
||||
array_is_temporary_ = false;
|
||||
|
@@ -22,6 +22,7 @@
|
||||
*/
|
||||
|
||||
#include "BKE_cryptomatte.h"
|
||||
#include "BKE_cryptomatte.hh"
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_main.h"
|
||||
|
||||
@@ -43,6 +44,7 @@
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
enum CryptomatteLayerState {
|
||||
EMPTY,
|
||||
@@ -299,15 +301,15 @@ static uint32_t cryptomatte_determine_identifier(const std::string name)
|
||||
return BLI_hash_mm3(reinterpret_cast<const unsigned char *>(name.c_str()), name.length(), 0);
|
||||
}
|
||||
|
||||
static std::string cryptomatte_determine_prefix(const std::string name)
|
||||
static void add_render_result_meta_data(RenderResult *render_result,
|
||||
const blender::StringRef layer_name,
|
||||
const blender::StringRefNull key_name,
|
||||
const blender::StringRefNull value)
|
||||
{
|
||||
std::stringstream stream;
|
||||
const uint32_t render_pass_identifier = cryptomatte_determine_identifier(name);
|
||||
stream << "cryptomatte/";
|
||||
stream << std::setfill('0') << std::setw(sizeof(uint32_t) * 2) << std::hex
|
||||
<< render_pass_identifier;
|
||||
stream << "/";
|
||||
return stream.str();
|
||||
BKE_render_result_stamp_data(
|
||||
render_result,
|
||||
blender::BKE_cryptomatte_meta_data_key(layer_name, key_name).c_str(),
|
||||
value.data());
|
||||
}
|
||||
|
||||
void BKE_cryptomatte_store_metadata(struct CryptomatteSession *session,
|
||||
@@ -335,12 +337,47 @@ void BKE_cryptomatte_store_metadata(struct CryptomatteSession *session,
|
||||
|
||||
const std::string manifest = layer->manifest_get_string();
|
||||
const std::string name = cryptomatte_determine_name(view_layer, cryptomatte_layer_name);
|
||||
const std::string prefix = cryptomatte_determine_prefix(name);
|
||||
|
||||
/* Store the meta data into the render result. */
|
||||
BKE_render_result_stamp_data(render_result, (prefix + "name").c_str(), name.c_str());
|
||||
BKE_render_result_stamp_data(render_result, (prefix + "hash").c_str(), "MurmurHash3_32");
|
||||
BKE_render_result_stamp_data(
|
||||
render_result, (prefix + "conversion").c_str(), "uint32_to_float32");
|
||||
BKE_render_result_stamp_data(render_result, (prefix + "manifest").c_str(), manifest.c_str());
|
||||
add_render_result_meta_data(render_result, name, "name", name);
|
||||
add_render_result_meta_data(render_result, name, "hash", "MurmurHash3_32");
|
||||
add_render_result_meta_data(render_result, name, "conversion", "uint32_to_float32");
|
||||
add_render_result_meta_data(render_result, name, "manifest", manifest);
|
||||
}
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* Return the hash of the given cryptomatte layer name.
|
||||
*
|
||||
* The cryptomatte specification limits the hash to 7 characters.
|
||||
* The 7 position limitation solves issues when using cryptomatte together with OpenEXR.
|
||||
* The specification suggests to use the first 7 chars of the hashed layer_name.
|
||||
*/
|
||||
static std::string cryptomatte_layer_name_hash(const StringRef layer_name)
|
||||
{
|
||||
std::stringstream stream;
|
||||
const uint32_t render_pass_identifier = cryptomatte_determine_identifier(layer_name);
|
||||
stream << std::setfill('0') << std::setw(sizeof(uint32_t) * 2) << std::hex
|
||||
<< render_pass_identifier;
|
||||
return stream.str().substr(0, 7);
|
||||
}
|
||||
|
||||
std::string BKE_cryptomatte_meta_data_key(const StringRef layer_name, const StringRefNull key_name)
|
||||
{
|
||||
return "cryptomatte/" + cryptomatte_layer_name_hash(layer_name) + "/" + key_name;
|
||||
}
|
||||
|
||||
/* Extracts the cryptomatte name from a render pass name.
|
||||
*
|
||||
* Example: A render pass could be named `CryptoObject00`. This
|
||||
* function would remove the trailing digits and return `CryptoObject`. */
|
||||
StringRef BKE_cryptomatte_extract_layer_name(const StringRef render_pass_name)
|
||||
{
|
||||
int64_t last_token = render_pass_name.size();
|
||||
while (last_token > 0 && std::isdigit(render_pass_name[last_token - 1])) {
|
||||
last_token -= 1;
|
||||
}
|
||||
return render_pass_name.substr(0, last_token);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
|
44
source/blender/blenkernel/intern/cryptomatte_test.cc
Normal file
44
source/blender/blenkernel/intern/cryptomatte_test.cc
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2021 by Blender Foundation.
|
||||
*/
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "BKE_cryptomatte.hh"
|
||||
|
||||
namespace blender::bke::tests {
|
||||
|
||||
TEST(cryptomatte, meta_data_key)
|
||||
{
|
||||
ASSERT_EQ("cryptomatte/c7dbf5e/key",
|
||||
BKE_cryptomatte_meta_data_key("ViewLayer.CryptoMaterial", "key"));
|
||||
ASSERT_EQ("cryptomatte/b990b65/𝓴𝓮𝔂",
|
||||
BKE_cryptomatte_meta_data_key("𝖚𝖓𝖎𝖈𝖔𝖉𝖊.CryptoMaterial", "𝓴𝓮𝔂"));
|
||||
}
|
||||
|
||||
TEST(cryptomatte, extract_layer_name)
|
||||
{
|
||||
ASSERT_EQ("ViewLayer.CryptoMaterial",
|
||||
BKE_cryptomatte_extract_layer_name("ViewLayer.CryptoMaterial00"));
|
||||
ASSERT_EQ("𝖚𝖓𝖎𝖈𝖔𝖉𝖊", BKE_cryptomatte_extract_layer_name("𝖚𝖓𝖎𝖈𝖔𝖉𝖊13"));
|
||||
ASSERT_EQ("NoTrailingSampleNumber",
|
||||
BKE_cryptomatte_extract_layer_name("NoTrailingSampleNumber"));
|
||||
ASSERT_EQ("W1thM1dd13Numb3rs", BKE_cryptomatte_extract_layer_name("W1thM1dd13Numb3rs09"));
|
||||
ASSERT_EQ("", BKE_cryptomatte_extract_layer_name("0123"));
|
||||
ASSERT_EQ("", BKE_cryptomatte_extract_layer_name(""));
|
||||
}
|
||||
|
||||
} // namespace blender::bke::tests
|
@@ -899,7 +899,7 @@ float BKE_defvert_calc_lock_relative_weight(float weight,
|
||||
}
|
||||
|
||||
/* handle division by zero */
|
||||
if (locked_weight >= 1.0f) {
|
||||
if (locked_weight >= 1.0f - VERTEX_WEIGHT_LOCK_EPSILON) {
|
||||
if (weight != 0.0f) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
@@ -296,11 +296,16 @@ NlaTrack *BKE_nlatrack_add(AnimData *adt, NlaTrack *prev, const bool is_liboverr
|
||||
nlt->flag = NLATRACK_SELECTED | NLATRACK_OVERRIDELIBRARY_LOCAL;
|
||||
nlt->index = BLI_listbase_count(&adt->nla_tracks);
|
||||
|
||||
/* add track to stack, and make it the active one */
|
||||
if (is_liboverride) {
|
||||
for (; prev != NULL && (prev->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0; prev = prev->next) {
|
||||
/* In liboverride case, we only add local tracks after all those comming from the linked data, so
|
||||
* we need to find the first local track. */
|
||||
if (is_liboverride && prev != NULL && (prev->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0) {
|
||||
NlaTrack *first_local = prev->next;
|
||||
for (; first_local != NULL && (first_local->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0;
|
||||
first_local = first_local->next) {
|
||||
}
|
||||
prev = first_local != NULL ? first_local->prev : NULL;
|
||||
}
|
||||
/* Add track to stack, and make it the active one. */
|
||||
if (prev != NULL) {
|
||||
BLI_insertlinkafter(&adt->nla_tracks, prev, nlt);
|
||||
}
|
||||
|
@@ -4730,6 +4730,7 @@ static void registerGeometryNodes(void)
|
||||
|
||||
register_node_type_geo_attribute_compare();
|
||||
register_node_type_geo_attribute_fill();
|
||||
register_node_type_geo_attribute_vector_math();
|
||||
register_node_type_geo_triangulate();
|
||||
register_node_type_geo_edge_split();
|
||||
register_node_type_geo_transform();
|
||||
@@ -4745,6 +4746,7 @@ static void registerGeometryNodes(void)
|
||||
register_node_type_geo_attribute_mix();
|
||||
register_node_type_geo_attribute_color_ramp();
|
||||
register_node_type_geo_rotate_points();
|
||||
register_node_type_geo_align_rotation_to_vector();
|
||||
}
|
||||
|
||||
static void registerFunctionNodes(void)
|
||||
|
@@ -268,25 +268,17 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
|
||||
}
|
||||
}
|
||||
}
|
||||
BKE_object_eval_boundbox(depsgraph, ob);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO(sergey): Ensure that bounding box is already calculated, and move this
|
||||
* into #BKE_object_sync_to_original().
|
||||
*/
|
||||
void BKE_object_eval_boundbox(Depsgraph *depsgraph, Object *object)
|
||||
/** Bounding box from evaluated geometry. */
|
||||
static void object_sync_boundbox_to_original(Object *object_orig, Object *object_eval)
|
||||
{
|
||||
if (!DEG_is_active(depsgraph)) {
|
||||
return;
|
||||
}
|
||||
Object *ob_orig = DEG_get_original_object(object);
|
||||
BoundBox *bb = BKE_object_boundbox_get(object);
|
||||
BoundBox *bb = BKE_object_boundbox_get(object_eval);
|
||||
if (bb != NULL) {
|
||||
if (ob_orig->runtime.bb == NULL) {
|
||||
ob_orig->runtime.bb = MEM_mallocN(sizeof(*ob_orig->runtime.bb), __func__);
|
||||
if (object_orig->runtime.bb == NULL) {
|
||||
object_orig->runtime.bb = MEM_mallocN(sizeof(*object_orig->runtime.bb), __func__);
|
||||
}
|
||||
*ob_orig->runtime.bb = *bb;
|
||||
*object_orig->runtime.bb = *bb;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,6 +307,8 @@ void BKE_object_sync_to_original(Depsgraph *depsgraph, Object *object)
|
||||
md_orig->error = BLI_strdup(md->error);
|
||||
}
|
||||
}
|
||||
|
||||
object_sync_boundbox_to_original(object_orig, object);
|
||||
}
|
||||
|
||||
bool BKE_object_eval_proxy_copy(Depsgraph *depsgraph, Object *object)
|
||||
|
@@ -251,10 +251,42 @@ static void undosys_stack_validate(UndoStack *ustack, bool expect_non_empty)
|
||||
BLI_assert(!BLI_listbase_is_empty(&ustack->steps));
|
||||
}
|
||||
}
|
||||
|
||||
/* Return whether `us_item` is before (-1), after (1) or same as (0) `us_anchor` step. */
|
||||
static int undosys_stack_order(const UndoStack *ustack,
|
||||
const UndoStep *us_anchor,
|
||||
const UndoStep *us_item)
|
||||
{
|
||||
const int index_anchor = BLI_findindex(&ustack->steps, us_anchor);
|
||||
const int index_item = BLI_findindex(&ustack->steps, us_item);
|
||||
BLI_assert(index_anchor >= 0);
|
||||
BLI_assert(index_item >= 0);
|
||||
|
||||
return (index_item == index_anchor) ? 0 : (index_item < index_anchor) ? -1 : 1;
|
||||
}
|
||||
|
||||
# define ASSERT_VALID_UNDO_STEP(_ustack, _us_undo) \
|
||||
{ \
|
||||
const UndoStep *_us_anchor = (_ustack)->step_active; \
|
||||
BLI_assert(_us_anchor == NULL || \
|
||||
(undosys_stack_order((_ustack), _us_anchor, (_us_undo)) <= 0)); \
|
||||
} \
|
||||
(void)0
|
||||
|
||||
# define ASSERT_VALID_REDO_STEP(_ustack, _us_redo) \
|
||||
{ \
|
||||
const UndoStep *_us_anchor = (_ustack)->step_active; \
|
||||
BLI_assert(_us_anchor == NULL || \
|
||||
(undosys_stack_order((_ustack), _us_anchor, (_us_redo)) >= 0)); \
|
||||
} \
|
||||
(void)0
|
||||
|
||||
#else
|
||||
static void undosys_stack_validate(UndoStack *UNUSED(ustack), bool UNUSED(expect_non_empty))
|
||||
{
|
||||
}
|
||||
# define ASSERT_VALID_UNDO_STEP(_ustack, _us_undo)
|
||||
# define ASSERT_VALID_REDO_STEP(_ustack, _us_redo)
|
||||
#endif
|
||||
|
||||
UndoStack *BKE_undosys_stack_create(void)
|
||||
@@ -676,62 +708,91 @@ bool BKE_undosys_step_undo_with_data_ex(UndoStack *ustack,
|
||||
{
|
||||
UNDO_NESTED_ASSERT(false);
|
||||
if (us == NULL) {
|
||||
CLOG_ERROR(&LOG, "called with a NULL step");
|
||||
return false;
|
||||
}
|
||||
undosys_stack_validate(ustack, true);
|
||||
|
||||
UndoStep *us_prev = us ? us->prev : NULL;
|
||||
if (us) {
|
||||
/* The current state is a copy, we need to load the previous state. */
|
||||
us = us_prev;
|
||||
/* We expect to get next-from-actual-target step here (i.e. active step in case we only undo
|
||||
* once)?
|
||||
* FIXME: this is very confusing now that we may have to undo several steps anyway, this function
|
||||
* should just get the target final step, not assume that it is getting the active one by default
|
||||
* (or the step after the target one when undoing more than one step). */
|
||||
UndoStep *us_target = us->prev;
|
||||
if (us_target == NULL) {
|
||||
CLOG_ERROR(&LOG, "could not find a valid target step");
|
||||
return false;
|
||||
}
|
||||
ASSERT_VALID_UNDO_STEP(ustack, us_target);
|
||||
|
||||
/* This will be active once complete. */
|
||||
UndoStep *us_active = us_prev;
|
||||
UndoStep *us_active = us_target;
|
||||
if (use_skip) {
|
||||
while (us_active && us_active->skip) {
|
||||
us_active = us_active->prev;
|
||||
}
|
||||
}
|
||||
|
||||
if ((us != NULL) && (us_active != NULL)) {
|
||||
CLOG_INFO(&LOG, 1, "addr=%p, name='%s', type='%s'", us, us->name, us->type->name);
|
||||
|
||||
/* Handle accumulate steps. */
|
||||
if (ustack->step_active) {
|
||||
UndoStep *us_iter = ustack->step_active;
|
||||
while (us_iter != us) {
|
||||
/* TODO:
|
||||
* - skip successive steps that store the same data, eg: memfile steps.
|
||||
* - or steps that include another steps data, eg: a memfile step includes text undo data.
|
||||
*/
|
||||
undosys_step_decode(C, G_MAIN, ustack, us_iter, -1, false);
|
||||
|
||||
us_iter = us_iter->prev;
|
||||
}
|
||||
/* This will be the active step once the undo process is complete.
|
||||
*
|
||||
* In case we do skip 'skipped' steps, the final active step may be several steps backward from
|
||||
* the one passed as parameter. */
|
||||
UndoStep *us_target_active = us_target;
|
||||
if (use_skip) {
|
||||
while (us_target_active != NULL && us_target_active->skip) {
|
||||
us_target_active = us_target_active->prev;
|
||||
}
|
||||
|
||||
{
|
||||
UndoStep *us_iter = us_prev;
|
||||
do {
|
||||
const bool is_final = (us_iter == us_active);
|
||||
if (is_final == false) {
|
||||
CLOG_INFO(&LOG,
|
||||
2,
|
||||
"undo continue with skip %p '%s', type='%s'",
|
||||
us_iter,
|
||||
us_iter->name,
|
||||
us_iter->type->name);
|
||||
}
|
||||
undosys_step_decode(C, G_MAIN, ustack, us_iter, -1, is_final);
|
||||
ustack->step_active = us_iter;
|
||||
} while ((us_active != us_iter) && (us_iter = us_iter->prev));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
if (us_target_active == NULL) {
|
||||
CLOG_ERROR(&LOG, "could not find a valid final active target step");
|
||||
return false;
|
||||
}
|
||||
|
||||
CLOG_INFO(
|
||||
&LOG, 1, "addr=%p, name='%s', type='%s'", us_target, us_target->name, us_target->type->name);
|
||||
|
||||
/* Undo steps until we reach original given target, if we do have a current active step.
|
||||
*
|
||||
* NOTE: Unlike with redo case, where we can expect current active step to fully reflect current
|
||||
* data status, in undo case we also do reload the active step.
|
||||
* FIXME: this feels weak, and should probably not be actually needed? Or should also be done in
|
||||
* redo case? */
|
||||
if (ustack->step_active != NULL) {
|
||||
for (UndoStep *us_iter = ustack->step_active; us_iter != us_target; us_iter = us_iter->prev) {
|
||||
BLI_assert(us_iter != NULL);
|
||||
undosys_step_decode(C, G_MAIN, ustack, us_iter, -1, false);
|
||||
ustack->step_active = us_iter;
|
||||
}
|
||||
}
|
||||
|
||||
/* Undo target step, and all potential extra ones if some steps have to be 'skipped'. */
|
||||
for (UndoStep *us_iter = us_target; us_iter != NULL; us_iter = us_iter->prev) {
|
||||
const bool is_final = (us_iter == us_target_active);
|
||||
|
||||
if (!is_final) {
|
||||
BLI_assert(us_iter->skip == true);
|
||||
CLOG_INFO(&LOG,
|
||||
2,
|
||||
"undo continue with skip addr=%p, name='%s', type='%s'",
|
||||
us_iter,
|
||||
us_iter->name,
|
||||
us_iter->type->name);
|
||||
}
|
||||
|
||||
undosys_step_decode(C, G_MAIN, ustack, us_iter, -1, is_final);
|
||||
ustack->step_active = us_iter;
|
||||
|
||||
if (is_final) {
|
||||
/* Undo process is finished and successful. */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert(
|
||||
!"This should never be reached, either undo stack is corrupted, or code above is buggy");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BKE_undosys_step_undo_with_data(UndoStack *ustack, bContext *C, UndoStep *us)
|
||||
{
|
||||
return BKE_undosys_step_undo_with_data_ex(ustack, C, us, true);
|
||||
@@ -756,54 +817,79 @@ bool BKE_undosys_step_redo_with_data_ex(UndoStack *ustack,
|
||||
{
|
||||
UNDO_NESTED_ASSERT(false);
|
||||
if (us == NULL) {
|
||||
CLOG_ERROR(&LOG, "called with a NULL step");
|
||||
return false;
|
||||
}
|
||||
undosys_stack_validate(ustack, true);
|
||||
|
||||
UndoStep *us_next = us ? us->next : NULL;
|
||||
/* Unlike undo accumulate, we always use the next. */
|
||||
us = us_next;
|
||||
/* We expect to get previous-from-actual-target step here (i.e. active step in case we only redo
|
||||
* once)?
|
||||
* FIXME: this is very confusing now that we may have to redo several steps anyway, this function
|
||||
* should just get the target final step, not assume that it is getting the active one by default
|
||||
* (or the step before the target one when redoing more than one step). */
|
||||
UndoStep *us_target = us->next;
|
||||
if (us_target == NULL) {
|
||||
CLOG_ERROR(&LOG, "could not find a valid target step");
|
||||
return false;
|
||||
}
|
||||
ASSERT_VALID_REDO_STEP(ustack, us_target);
|
||||
|
||||
/* This will be active once complete. */
|
||||
UndoStep *us_active = us_next;
|
||||
/* This will be the active step once the redo process is complete.
|
||||
*
|
||||
* In case we do skip 'skipped' steps, the final active step may be several steps forward the one
|
||||
* passed as parameter. */
|
||||
UndoStep *us_target_active = us_target;
|
||||
if (use_skip) {
|
||||
while (us_active && us_active->skip) {
|
||||
us_active = us_active->next;
|
||||
while (us_target_active != NULL && us_target_active->skip) {
|
||||
us_target_active = us_target_active->next;
|
||||
}
|
||||
}
|
||||
if (us_target_active == NULL) {
|
||||
CLOG_ERROR(&LOG, "could not find a valid final active target step");
|
||||
return false;
|
||||
}
|
||||
|
||||
CLOG_INFO(
|
||||
&LOG, 1, "addr=%p, name='%s', type='%s'", us_target, us_target->name, us_target->type->name);
|
||||
|
||||
/* Redo steps until we reach original given target, if we do have a current active step. */
|
||||
if (ustack->step_active != NULL) {
|
||||
for (UndoStep *us_iter = ustack->step_active->next; us_iter != us_target;
|
||||
us_iter = us_iter->next) {
|
||||
BLI_assert(us_iter != NULL);
|
||||
undosys_step_decode(C, G_MAIN, ustack, us_iter, 1, false);
|
||||
ustack->step_active = us_iter;
|
||||
}
|
||||
}
|
||||
|
||||
if ((us != NULL) && (us_active != NULL)) {
|
||||
CLOG_INFO(&LOG, 1, "addr=%p, name='%s', type='%s'", us, us->name, us->type->name);
|
||||
/* Redo target step, and all potential extra ones if some steps have to be 'skipped'. */
|
||||
for (UndoStep *us_iter = us_target; us_iter != NULL; us_iter = us_iter->next) {
|
||||
const bool is_final = (us_iter == us_target_active);
|
||||
|
||||
/* Handle accumulate steps. */
|
||||
if (ustack->step_active && ustack->step_active->next) {
|
||||
UndoStep *us_iter = ustack->step_active->next;
|
||||
while (us_iter != us) {
|
||||
undosys_step_decode(C, G_MAIN, ustack, us_iter, 1, false);
|
||||
us_iter = us_iter->next;
|
||||
}
|
||||
if (!is_final) {
|
||||
BLI_assert(us_iter->skip == true);
|
||||
CLOG_INFO(&LOG,
|
||||
2,
|
||||
"redo continue with skip addr=%p, name='%s', type='%s'",
|
||||
us_iter,
|
||||
us_iter->name,
|
||||
us_iter->type->name);
|
||||
}
|
||||
|
||||
{
|
||||
UndoStep *us_iter = us_next;
|
||||
do {
|
||||
const bool is_final = (us_iter == us_active);
|
||||
if (is_final == false) {
|
||||
CLOG_INFO(&LOG,
|
||||
2,
|
||||
"redo continue with skip %p '%s', type='%s'",
|
||||
us_iter,
|
||||
us_iter->name,
|
||||
us_iter->type->name);
|
||||
}
|
||||
undosys_step_decode(C, G_MAIN, ustack, us_iter, 1, is_final);
|
||||
ustack->step_active = us_iter;
|
||||
} while ((us_active != us_iter) && (us_iter = us_iter->next));
|
||||
undosys_step_decode(C, G_MAIN, ustack, us_iter, 1, is_final);
|
||||
ustack->step_active = us_iter;
|
||||
|
||||
if (is_final) {
|
||||
/* Redo process is finished and successful. */
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
BLI_assert(
|
||||
!"This should never be reached, either undo stack is corrupted, or code above is buggy");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BKE_undosys_step_redo_with_data(UndoStack *ustack, bContext *C, UndoStep *us)
|
||||
{
|
||||
return BKE_undosys_step_redo_with_data_ex(ustack, C, us, true);
|
||||
|
@@ -22,7 +22,7 @@
|
||||
|
||||
#include "BLI_double3.hh"
|
||||
|
||||
namespace blender::math {
|
||||
namespace blender {
|
||||
|
||||
struct double2 {
|
||||
double x, y;
|
||||
@@ -50,98 +50,94 @@ struct double2 {
|
||||
{
|
||||
return &x;
|
||||
}
|
||||
|
||||
double length() const
|
||||
{
|
||||
return len_v2_db(*this);
|
||||
}
|
||||
|
||||
friend double2 operator+(const double2 &a, const double2 &b)
|
||||
{
|
||||
return {a.x + b.x, a.y + b.y};
|
||||
}
|
||||
|
||||
friend double2 operator-(const double2 &a, const double2 &b)
|
||||
{
|
||||
return {a.x - b.x, a.y - b.y};
|
||||
}
|
||||
|
||||
friend double2 operator*(const double2 &a, double b)
|
||||
{
|
||||
return {a.x * b, a.y * b};
|
||||
}
|
||||
|
||||
friend double2 operator/(const double2 &a, double b)
|
||||
{
|
||||
BLI_assert(b != 0.0);
|
||||
return {a.x / b, a.y / b};
|
||||
}
|
||||
|
||||
friend double2 operator*(double a, const double2 &b)
|
||||
{
|
||||
return b * a;
|
||||
}
|
||||
|
||||
friend bool operator==(const double2 &a, const double2 &b)
|
||||
{
|
||||
return a.x == b.x && a.y == b.y;
|
||||
}
|
||||
|
||||
friend bool operator!=(const double2 &a, const double2 &b)
|
||||
{
|
||||
return a.x != b.x || a.y != b.y;
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &stream, const double2 &v)
|
||||
{
|
||||
stream << "(" << v.x << ", " << v.y << ")";
|
||||
return stream;
|
||||
}
|
||||
|
||||
static double dot(const double2 &a, const double2 &b)
|
||||
{
|
||||
return a.x * b.x + a.y * b.y;
|
||||
}
|
||||
|
||||
static double2 interpolate(const double2 &a, const double2 &b, double t)
|
||||
{
|
||||
return a * (1 - t) + b * t;
|
||||
}
|
||||
|
||||
static double2 abs(const double2 &a)
|
||||
{
|
||||
return double2(fabs(a.x), fabs(a.y));
|
||||
}
|
||||
|
||||
static double distance(const double2 &a, const double2 &b)
|
||||
{
|
||||
return (a - b).length();
|
||||
}
|
||||
|
||||
static double distance_squared(const double2 &a, const double2 &b)
|
||||
{
|
||||
double2 diff = a - b;
|
||||
return double2::dot(diff, diff);
|
||||
}
|
||||
|
||||
struct isect_result {
|
||||
enum {
|
||||
LINE_LINE_COLINEAR = -1,
|
||||
LINE_LINE_NONE = 0,
|
||||
LINE_LINE_EXACT = 1,
|
||||
LINE_LINE_CROSS = 2,
|
||||
} kind;
|
||||
double lambda;
|
||||
};
|
||||
|
||||
static isect_result isect_seg_seg(const double2 &v1,
|
||||
const double2 &v2,
|
||||
const double2 &v3,
|
||||
const double2 &v4);
|
||||
};
|
||||
|
||||
inline double length(const double2 &a)
|
||||
{
|
||||
return len_v2_db(a);
|
||||
}
|
||||
|
||||
inline double2 operator+(const double2 &a, const double2 &b)
|
||||
{
|
||||
return {a.x + b.x, a.y + b.y};
|
||||
}
|
||||
|
||||
inline double2 operator-(const double2 &a, const double2 &b)
|
||||
{
|
||||
return {a.x - b.x, a.y - b.y};
|
||||
}
|
||||
|
||||
inline double2 operator*(const double2 &a, double b)
|
||||
{
|
||||
return {a.x * b, a.y * b};
|
||||
}
|
||||
|
||||
inline double2 operator/(const double2 &a, double b)
|
||||
{
|
||||
BLI_assert(b != 0.0);
|
||||
return {a.x / b, a.y / b};
|
||||
}
|
||||
|
||||
inline double2 operator*(double a, const double2 &b)
|
||||
{
|
||||
return b * a;
|
||||
}
|
||||
|
||||
inline bool operator==(const double2 &a, const double2 &b)
|
||||
{
|
||||
return a.x == b.x && a.y == b.y;
|
||||
}
|
||||
|
||||
inline bool operator!=(const double2 &a, const double2 &b)
|
||||
{
|
||||
return a.x != b.x || a.y != b.y;
|
||||
}
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &stream, const double2 &v)
|
||||
{
|
||||
stream << "(" << v.x << ", " << v.y << ")";
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline double dot(const double2 &a, const double2 &b)
|
||||
{
|
||||
return a.x * b.x + a.y * b.y;
|
||||
}
|
||||
|
||||
inline double2 lerp(const double2 &a, const double2 &b, double t)
|
||||
{
|
||||
return a * (1 - t) + b * t;
|
||||
}
|
||||
|
||||
inline double2 abs(const double2 &a)
|
||||
{
|
||||
return double2(fabs(a.x), fabs(a.y));
|
||||
}
|
||||
|
||||
inline double distance(const double2 &a, const double2 &b)
|
||||
{
|
||||
return length(a - b);
|
||||
}
|
||||
|
||||
inline double distance_squared(const double2 &a, const double2 &b)
|
||||
{
|
||||
double2 diff = a - b;
|
||||
return dot(diff, diff);
|
||||
}
|
||||
|
||||
struct isect_result_double2 {
|
||||
enum {
|
||||
LINE_LINE_COLINEAR = -1,
|
||||
LINE_LINE_NONE = 0,
|
||||
LINE_LINE_EXACT = 1,
|
||||
LINE_LINE_CROSS = 2,
|
||||
} kind;
|
||||
double lambda;
|
||||
};
|
||||
|
||||
isect_result_double2 isect_seg_seg(const double2 &v1,
|
||||
const double2 &v2,
|
||||
const double2 &v3,
|
||||
const double2 &v4);
|
||||
|
||||
} // namespace blender::math
|
||||
|
||||
namespace blender {
|
||||
using blender::math::double2;
|
||||
}
|
||||
} // namespace blender
|
||||
|
@@ -25,7 +25,7 @@
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
namespace blender::math {
|
||||
namespace blender {
|
||||
|
||||
struct double3 {
|
||||
double x, y, z;
|
||||
@@ -61,190 +61,186 @@ struct double3 {
|
||||
{
|
||||
return &x;
|
||||
}
|
||||
|
||||
double normalize_and_get_length()
|
||||
{
|
||||
return normalize_v3_db(*this);
|
||||
}
|
||||
|
||||
double3 normalized() const
|
||||
{
|
||||
double3 result;
|
||||
normalize_v3_v3_db(result, *this);
|
||||
return result;
|
||||
}
|
||||
|
||||
double length() const
|
||||
{
|
||||
return len_v3_db(*this);
|
||||
}
|
||||
|
||||
double length_squared() const
|
||||
{
|
||||
return len_squared_v3_db(*this);
|
||||
}
|
||||
|
||||
void reflect(const double3 &normal)
|
||||
{
|
||||
*this = this->reflected(normal);
|
||||
}
|
||||
|
||||
double3 reflected(const double3 &normal) const
|
||||
{
|
||||
double3 result;
|
||||
reflect_v3_v3v3_db(result, *this, normal);
|
||||
return result;
|
||||
}
|
||||
|
||||
static double3 safe_divide(const double3 &a, const double3 &b)
|
||||
{
|
||||
double3 result;
|
||||
result.x = (b.x == 0.0) ? 0.0 : a.x / b.x;
|
||||
result.y = (b.y == 0.0) ? 0.0 : a.y / b.y;
|
||||
result.z = (b.z == 0.0) ? 0.0 : a.z / b.z;
|
||||
return result;
|
||||
}
|
||||
|
||||
void invert()
|
||||
{
|
||||
x = -x;
|
||||
y = -y;
|
||||
z = -z;
|
||||
}
|
||||
|
||||
friend double3 operator+(const double3 &a, const double3 &b)
|
||||
{
|
||||
return {a.x + b.x, a.y + b.y, a.z + b.z};
|
||||
}
|
||||
|
||||
void operator+=(const double3 &b)
|
||||
{
|
||||
this->x += b.x;
|
||||
this->y += b.y;
|
||||
this->z += b.z;
|
||||
}
|
||||
|
||||
friend double3 operator-(const double3 &a, const double3 &b)
|
||||
{
|
||||
return {a.x - b.x, a.y - b.y, a.z - b.z};
|
||||
}
|
||||
|
||||
friend double3 operator-(const double3 &a)
|
||||
{
|
||||
return {-a.x, -a.y, -a.z};
|
||||
}
|
||||
|
||||
void operator-=(const double3 &b)
|
||||
{
|
||||
this->x -= b.x;
|
||||
this->y -= b.y;
|
||||
this->z -= b.z;
|
||||
}
|
||||
|
||||
void operator*=(const double &scalar)
|
||||
{
|
||||
this->x *= scalar;
|
||||
this->y *= scalar;
|
||||
this->z *= scalar;
|
||||
}
|
||||
|
||||
void operator*=(const double3 &other)
|
||||
{
|
||||
this->x *= other.x;
|
||||
this->y *= other.y;
|
||||
this->z *= other.z;
|
||||
}
|
||||
|
||||
friend double3 operator*(const double3 &a, const double3 &b)
|
||||
{
|
||||
return {a.x * b.x, a.y * b.y, a.z * b.z};
|
||||
}
|
||||
|
||||
friend double3 operator*(const double3 &a, const double &b)
|
||||
{
|
||||
return {a.x * b, a.y * b, a.z * b};
|
||||
}
|
||||
|
||||
friend double3 operator*(const double &a, const double3 &b)
|
||||
{
|
||||
return b * a;
|
||||
}
|
||||
|
||||
friend double3 operator/(const double3 &a, const double &b)
|
||||
{
|
||||
BLI_assert(b != 0.0);
|
||||
return {a.x / b, a.y / b, a.z / b};
|
||||
}
|
||||
|
||||
friend bool operator==(const double3 &a, const double3 &b)
|
||||
{
|
||||
return a.x == b.x && a.y == b.y && a.z == b.z;
|
||||
}
|
||||
|
||||
friend bool operator!=(const double3 &a, const double3 &b)
|
||||
{
|
||||
return a.x != b.x || a.y != b.y || a.z != b.z;
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &stream, const double3 &v)
|
||||
{
|
||||
stream << "(" << v.x << ", " << v.y << ", " << v.z << ")";
|
||||
return stream;
|
||||
}
|
||||
|
||||
static double dot(const double3 &a, const double3 &b)
|
||||
{
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z;
|
||||
}
|
||||
|
||||
static double3 cross_high_precision(const double3 &a, const double3 &b)
|
||||
{
|
||||
double3 result;
|
||||
cross_v3_v3v3_db(result, a, b);
|
||||
return result;
|
||||
}
|
||||
|
||||
static double3 project(const double3 &a, const double3 &b)
|
||||
{
|
||||
double3 result;
|
||||
project_v3_v3v3_db(result, a, b);
|
||||
return result;
|
||||
}
|
||||
|
||||
static double distance(const double3 &a, const double3 &b)
|
||||
{
|
||||
return (a - b).length();
|
||||
}
|
||||
|
||||
static double distance_squared(const double3 &a, const double3 &b)
|
||||
{
|
||||
double3 diff = a - b;
|
||||
return double3::dot(diff, diff);
|
||||
}
|
||||
|
||||
static double3 interpolate(const double3 &a, const double3 &b, double t)
|
||||
{
|
||||
return a * (1 - t) + b * t;
|
||||
}
|
||||
|
||||
static double3 abs(const double3 &a)
|
||||
{
|
||||
return double3(fabs(a.x), fabs(a.y), fabs(a.z));
|
||||
}
|
||||
|
||||
static int dominant_axis(const double3 &a)
|
||||
{
|
||||
double x = (a.x >= 0) ? a.x : -a.x;
|
||||
double y = (a.y >= 0) ? a.y : -a.y;
|
||||
double z = (a.z >= 0) ? a.z : -a.z;
|
||||
return ((x > y) ? ((x > z) ? 0 : 2) : ((y > z) ? 1 : 2));
|
||||
}
|
||||
|
||||
static double3 cross_poly(Span<double3> poly);
|
||||
};
|
||||
|
||||
inline double normalize_and_get_length(double3 &a)
|
||||
{
|
||||
return normalize_v3_db(a);
|
||||
}
|
||||
|
||||
inline double3 normalized(const double3 &a)
|
||||
{
|
||||
double3 result;
|
||||
normalize_v3_v3_db(result, a);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline double length(const double3 &a)
|
||||
{
|
||||
return len_v3_db(a);
|
||||
}
|
||||
|
||||
inline double length_squared(const double3 &a)
|
||||
{
|
||||
return len_squared_v3_db(a);
|
||||
}
|
||||
|
||||
inline double3 reflected(const double3 &a, const double3 &normal)
|
||||
{
|
||||
double3 result;
|
||||
reflect_v3_v3v3_db(result, a, normal);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void reflect(double3 &a, const double3 &normal)
|
||||
{
|
||||
a = reflected(a, normal);
|
||||
}
|
||||
|
||||
inline double3 safe_divide(const double3 &a, const double3 &b)
|
||||
{
|
||||
double3 result;
|
||||
result.x = (b.x == 0.0) ? 0.0 : a.x / b.x;
|
||||
result.y = (b.y == 0.0) ? 0.0 : a.y / b.y;
|
||||
result.z = (b.z == 0.0) ? 0.0 : a.z / b.z;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void negate(double3 &a)
|
||||
{
|
||||
a.x = -a.x;
|
||||
a.y = -a.y;
|
||||
a.z = -a.z;
|
||||
}
|
||||
|
||||
inline double3 operator+(const double3 &a, const double3 &b)
|
||||
{
|
||||
return {a.x + b.x, a.y + b.y, a.z + b.z};
|
||||
}
|
||||
|
||||
inline void operator+=(double3 &a, const double3 &b)
|
||||
{
|
||||
a.x += b.x;
|
||||
a.y += b.y;
|
||||
a.z += b.z;
|
||||
}
|
||||
|
||||
inline double3 operator-(const double3 &a, const double3 &b)
|
||||
{
|
||||
return {a.x - b.x, a.y - b.y, a.z - b.z};
|
||||
}
|
||||
|
||||
inline double3 operator-(const double3 &a)
|
||||
{
|
||||
return {-a.x, -a.y, -a.z};
|
||||
}
|
||||
|
||||
inline void operator-=(double3 &a, const double3 &b)
|
||||
{
|
||||
a.x -= b.x;
|
||||
a.y -= b.y;
|
||||
a.z -= b.z;
|
||||
}
|
||||
|
||||
inline void operator*=(double3 &a, const double &scalar)
|
||||
{
|
||||
a.x *= scalar;
|
||||
a.y *= scalar;
|
||||
a.z *= scalar;
|
||||
}
|
||||
|
||||
inline void operator*=(double3 &a, const double3 &other)
|
||||
{
|
||||
a.x *= other.x;
|
||||
a.y *= other.y;
|
||||
a.z *= other.z;
|
||||
}
|
||||
|
||||
inline double3 operator*(const double3 &a, const double3 &b)
|
||||
{
|
||||
return {a.x * b.x, a.y * b.y, a.z * b.z};
|
||||
}
|
||||
|
||||
inline double3 operator*(const double3 &a, const double &b)
|
||||
{
|
||||
return {a.x * b, a.y * b, a.z * b};
|
||||
}
|
||||
|
||||
inline double3 operator*(const double &a, const double3 &b)
|
||||
{
|
||||
return b * a;
|
||||
}
|
||||
|
||||
inline double3 operator/(const double3 &a, const double &b)
|
||||
{
|
||||
BLI_assert(b != 0.0);
|
||||
return {a.x / b, a.y / b, a.z / b};
|
||||
}
|
||||
|
||||
inline bool operator==(const double3 &a, const double3 &b)
|
||||
{
|
||||
return a.x == b.x && a.y == b.y && a.z == b.z;
|
||||
}
|
||||
|
||||
inline bool operator!=(const double3 &a, const double3 &b)
|
||||
{
|
||||
return a.x != b.x || a.y != b.y || a.z != b.z;
|
||||
}
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &stream, const double3 &v)
|
||||
{
|
||||
stream << "(" << v.x << ", " << v.y << ", " << v.z << ")";
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline double dot(const double3 &a, const double3 &b)
|
||||
{
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z;
|
||||
}
|
||||
|
||||
inline double3 cross_high_precision(const double3 &a, const double3 &b)
|
||||
{
|
||||
double3 result;
|
||||
cross_v3_v3v3_db(result, a, b);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline double3 project(const double3 &a, const double3 &b)
|
||||
{
|
||||
double3 result;
|
||||
project_v3_v3v3_db(result, a, b);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline double distance(const double3 &a, const double3 &b)
|
||||
{
|
||||
return length(a - b);
|
||||
}
|
||||
|
||||
inline double distance_squared(const double3 &a, const double3 &b)
|
||||
{
|
||||
const double3 diff = a - b;
|
||||
return dot(diff, diff);
|
||||
}
|
||||
|
||||
inline double3 lerp(const double3 &a, const double3 &b, double t)
|
||||
{
|
||||
return a * (1 - t) + b * t;
|
||||
}
|
||||
|
||||
inline double3 abs(const double3 &a)
|
||||
{
|
||||
return double3(fabs(a.x), fabs(a.y), fabs(a.z));
|
||||
}
|
||||
|
||||
inline int dominant_axis(const double3 &a)
|
||||
{
|
||||
double x = (a.x >= 0) ? a.x : -a.x;
|
||||
double y = (a.y >= 0) ? a.y : -a.y;
|
||||
double z = (a.z >= 0) ? a.z : -a.z;
|
||||
return ((x > y) ? ((x > z) ? 0 : 2) : ((y > z) ? 1 : 2));
|
||||
}
|
||||
|
||||
double3 cross_poly(Span<double3> poly);
|
||||
|
||||
} // namespace blender::math
|
||||
|
||||
namespace blender {
|
||||
using math::double3;
|
||||
}
|
||||
} // namespace blender
|
||||
|
@@ -18,7 +18,7 @@
|
||||
|
||||
#include "BLI_float3.hh"
|
||||
|
||||
namespace blender::math {
|
||||
namespace blender {
|
||||
|
||||
struct float2 {
|
||||
float x, y;
|
||||
@@ -47,133 +47,129 @@ struct float2 {
|
||||
return &x;
|
||||
}
|
||||
|
||||
float length() const
|
||||
{
|
||||
return len_v2(*this);
|
||||
}
|
||||
|
||||
float2 &operator+=(const float2 &other)
|
||||
{
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
float2 &operator-=(const float2 &other)
|
||||
{
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
float2 &operator*=(float factor)
|
||||
{
|
||||
x *= factor;
|
||||
y *= factor;
|
||||
return *this;
|
||||
}
|
||||
|
||||
float2 &operator/=(float divisor)
|
||||
{
|
||||
x /= divisor;
|
||||
y /= divisor;
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint64_t hash() const
|
||||
{
|
||||
uint64_t x1 = *reinterpret_cast<const uint32_t *>(&x);
|
||||
uint64_t x2 = *reinterpret_cast<const uint32_t *>(&y);
|
||||
return (x1 * 812519) ^ (x2 * 707951);
|
||||
}
|
||||
|
||||
friend float2 operator+(const float2 &a, const float2 &b)
|
||||
{
|
||||
return {a.x + b.x, a.y + b.y};
|
||||
}
|
||||
|
||||
friend float2 operator-(const float2 &a, const float2 &b)
|
||||
{
|
||||
return {a.x - b.x, a.y - b.y};
|
||||
}
|
||||
|
||||
friend float2 operator*(const float2 &a, float b)
|
||||
{
|
||||
return {a.x * b, a.y * b};
|
||||
}
|
||||
|
||||
friend float2 operator/(const float2 &a, float b)
|
||||
{
|
||||
BLI_assert(b != 0.0f);
|
||||
return {a.x / b, a.y / b};
|
||||
}
|
||||
|
||||
friend float2 operator*(float a, const float2 &b)
|
||||
{
|
||||
return b * a;
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &stream, const float2 &v)
|
||||
{
|
||||
stream << "(" << v.x << ", " << v.y << ")";
|
||||
return stream;
|
||||
}
|
||||
|
||||
static float dot(const float2 &a, const float2 &b)
|
||||
{
|
||||
return a.x * b.x + a.y * b.y;
|
||||
}
|
||||
|
||||
static float2 interpolate(const float2 &a, const float2 &b, float t)
|
||||
{
|
||||
return a * (1 - t) + b * t;
|
||||
}
|
||||
|
||||
static float2 abs(const float2 &a)
|
||||
{
|
||||
return float2(fabsf(a.x), fabsf(a.y));
|
||||
}
|
||||
|
||||
static float distance(const float2 &a, const float2 &b)
|
||||
{
|
||||
return (a - b).length();
|
||||
}
|
||||
|
||||
static float distance_squared(const float2 &a, const float2 &b)
|
||||
{
|
||||
float2 diff = a - b;
|
||||
return float2::dot(diff, diff);
|
||||
}
|
||||
|
||||
struct isect_result {
|
||||
enum {
|
||||
LINE_LINE_COLINEAR = -1,
|
||||
LINE_LINE_NONE = 0,
|
||||
LINE_LINE_EXACT = 1,
|
||||
LINE_LINE_CROSS = 2,
|
||||
} kind;
|
||||
float lambda;
|
||||
float mu;
|
||||
};
|
||||
|
||||
static isect_result isect_seg_seg(const float2 &v1,
|
||||
const float2 &v2,
|
||||
const float2 &v3,
|
||||
const float2 &v4);
|
||||
|
||||
friend bool operator==(const float2 &a, const float2 &b)
|
||||
{
|
||||
return a.x == b.x && a.y == b.y;
|
||||
}
|
||||
|
||||
friend bool operator!=(const float2 &a, const float2 &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator==(const float2 &a, const float2 &b)
|
||||
{
|
||||
return a.x == b.x && a.y == b.y;
|
||||
}
|
||||
|
||||
inline bool operator!=(const float2 &a, const float2 &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
inline float2 &operator+=(float2 &a, const float2 &other)
|
||||
{
|
||||
a.x += other.x;
|
||||
a.y += other.y;
|
||||
return a;
|
||||
}
|
||||
|
||||
inline float2 &operator-=(float2 &a, const float2 &other)
|
||||
{
|
||||
a.x -= other.x;
|
||||
a.y -= other.y;
|
||||
return a;
|
||||
}
|
||||
|
||||
inline float2 &operator*=(float2 &a, float factor)
|
||||
{
|
||||
a.x *= factor;
|
||||
a.y *= factor;
|
||||
return a;
|
||||
}
|
||||
|
||||
inline float2 &operator/=(float2 &a, float divisor)
|
||||
{
|
||||
a.x /= divisor;
|
||||
a.y /= divisor;
|
||||
return a;
|
||||
}
|
||||
|
||||
inline float2 operator+(const float2 &a, const float2 &b)
|
||||
{
|
||||
return {a.x + b.x, a.y + b.y};
|
||||
}
|
||||
|
||||
inline float2 operator-(const float2 &a, const float2 &b)
|
||||
{
|
||||
return {a.x - b.x, a.y - b.y};
|
||||
}
|
||||
|
||||
inline float2 operator*(const float2 &a, float b)
|
||||
{
|
||||
return {a.x * b, a.y * b};
|
||||
}
|
||||
|
||||
inline float2 operator/(const float2 &a, float b)
|
||||
{
|
||||
BLI_assert(b != 0.0f);
|
||||
return {a.x / b, a.y / b};
|
||||
}
|
||||
|
||||
inline float2 operator*(float a, const float2 &b)
|
||||
{
|
||||
return b * a;
|
||||
}
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &stream, const float2 &v)
|
||||
{
|
||||
stream << "(" << v.x << ", " << v.y << ")";
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline float length(const float2 &a)
|
||||
{
|
||||
return len_v2(a);
|
||||
}
|
||||
|
||||
inline float dot(const float2 &a, const float2 &b)
|
||||
{
|
||||
return a.x * b.x + a.y * b.y;
|
||||
}
|
||||
|
||||
inline float2 lerp(const float2 &a, const float2 &b, float t)
|
||||
{
|
||||
return a * (1 - t) + b * t;
|
||||
}
|
||||
|
||||
inline float2 abs(const float2 &a)
|
||||
{
|
||||
return float2(fabsf(a.x), fabsf(a.y));
|
||||
}
|
||||
|
||||
inline float distance(const float2 &a, const float2 &b)
|
||||
{
|
||||
return length(a - b);
|
||||
}
|
||||
|
||||
inline float distance_squared(const float2 &a, const float2 &b)
|
||||
{
|
||||
const float2 diff = a - b;
|
||||
return dot(diff, diff);
|
||||
}
|
||||
|
||||
struct isect_result_float2 {
|
||||
enum {
|
||||
LINE_LINE_COLINEAR = -1,
|
||||
LINE_LINE_NONE = 0,
|
||||
LINE_LINE_EXACT = 1,
|
||||
LINE_LINE_CROSS = 2,
|
||||
} kind;
|
||||
float lambda;
|
||||
float mu;
|
||||
};
|
||||
|
||||
isect_result_float2 isect_seg_seg(const float2 &v1,
|
||||
const float2 &v2,
|
||||
const float2 &v3,
|
||||
const float2 &v4);
|
||||
|
||||
} // namespace blender::math
|
||||
|
||||
namespace blender {
|
||||
using math::float2;
|
||||
}
|
||||
} // namespace blender
|
||||
|
@@ -20,7 +20,7 @@
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
|
||||
namespace blender::math {
|
||||
namespace blender {
|
||||
|
||||
struct float3 {
|
||||
float x, y, z;
|
||||
@@ -57,6 +57,151 @@ struct float3 {
|
||||
return &x;
|
||||
}
|
||||
|
||||
friend float3 operator+(const float3 &a, const float3 &b)
|
||||
{
|
||||
return {a.x + b.x, a.y + b.y, a.z + b.z};
|
||||
}
|
||||
|
||||
float3 &operator+=(const float3 &b)
|
||||
{
|
||||
this->x += b.x;
|
||||
this->y += b.y;
|
||||
this->z += b.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend float3 operator-(const float3 &a, const float3 &b)
|
||||
{
|
||||
return {a.x - b.x, a.y - b.y, a.z - b.z};
|
||||
}
|
||||
|
||||
friend float3 operator-(const float3 &a)
|
||||
{
|
||||
return {-a.x, -a.y, -a.z};
|
||||
}
|
||||
|
||||
float3 &operator-=(const float3 &b)
|
||||
{
|
||||
this->x -= b.x;
|
||||
this->y -= b.y;
|
||||
this->z -= b.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
float3 &operator*=(float scalar)
|
||||
{
|
||||
this->x *= scalar;
|
||||
this->y *= scalar;
|
||||
this->z *= scalar;
|
||||
return *this;
|
||||
}
|
||||
|
||||
float3 &operator*=(const float3 &other)
|
||||
{
|
||||
this->x *= other.x;
|
||||
this->y *= other.y;
|
||||
this->z *= other.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend float3 operator*(const float3 &a, const float3 &b)
|
||||
{
|
||||
return {a.x * b.x, a.y * b.y, a.z * b.z};
|
||||
}
|
||||
|
||||
friend float3 operator*(const float3 &a, float b)
|
||||
{
|
||||
return {a.x * b, a.y * b, a.z * b};
|
||||
}
|
||||
|
||||
friend float3 operator*(float a, const float3 &b)
|
||||
{
|
||||
return b * a;
|
||||
}
|
||||
|
||||
friend float3 operator/(const float3 &a, float b)
|
||||
{
|
||||
BLI_assert(b != 0.0f);
|
||||
return {a.x / b, a.y / b, a.z / b};
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &stream, const float3 &v)
|
||||
{
|
||||
stream << "(" << v.x << ", " << v.y << ", " << v.z << ")";
|
||||
return stream;
|
||||
}
|
||||
|
||||
friend bool operator==(const float3 &a, const float3 &b)
|
||||
{
|
||||
return a.x == b.x && a.y == b.y && a.z == b.z;
|
||||
}
|
||||
|
||||
friend bool operator!=(const float3 &a, const float3 &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
float normalize_and_get_length()
|
||||
{
|
||||
return normalize_v3(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the vector in place.
|
||||
*/
|
||||
void normalize()
|
||||
{
|
||||
normalize_v3(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a normalized vector. The original vector is not changed.
|
||||
*/
|
||||
float3 normalized() const
|
||||
{
|
||||
float3 result;
|
||||
normalize_v3_v3(result, *this);
|
||||
return result;
|
||||
}
|
||||
|
||||
float length() const
|
||||
{
|
||||
return len_v3(*this);
|
||||
}
|
||||
|
||||
float length_squared() const
|
||||
{
|
||||
return len_squared_v3(*this);
|
||||
}
|
||||
|
||||
void reflect(const float3 &normal)
|
||||
{
|
||||
*this = this->reflected(normal);
|
||||
}
|
||||
|
||||
float3 reflected(const float3 &normal) const
|
||||
{
|
||||
float3 result;
|
||||
reflect_v3_v3v3(result, *this, normal);
|
||||
return result;
|
||||
}
|
||||
|
||||
static float3 safe_divide(const float3 &a, const float3 &b)
|
||||
{
|
||||
float3 result;
|
||||
result.x = (b.x == 0.0f) ? 0.0f : a.x / b.x;
|
||||
result.y = (b.y == 0.0f) ? 0.0f : a.y / b.y;
|
||||
result.z = (b.z == 0.0f) ? 0.0f : a.z / b.z;
|
||||
return result;
|
||||
}
|
||||
|
||||
void invert()
|
||||
{
|
||||
x = -x;
|
||||
y = -y;
|
||||
z = -z;
|
||||
}
|
||||
|
||||
uint64_t hash() const
|
||||
{
|
||||
uint64_t x1 = *reinterpret_cast<const uint32_t *>(&x);
|
||||
@@ -64,189 +209,46 @@ struct float3 {
|
||||
uint64_t x3 = *reinterpret_cast<const uint32_t *>(&z);
|
||||
return (x1 * 435109) ^ (x2 * 380867) ^ (x3 * 1059217);
|
||||
}
|
||||
|
||||
static float dot(const float3 &a, const float3 &b)
|
||||
{
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z;
|
||||
}
|
||||
|
||||
static float3 cross_high_precision(const float3 &a, const float3 &b)
|
||||
{
|
||||
float3 result;
|
||||
cross_v3_v3v3_hi_prec(result, a, b);
|
||||
return result;
|
||||
}
|
||||
|
||||
static float3 project(const float3 &a, const float3 &b)
|
||||
{
|
||||
float3 result;
|
||||
project_v3_v3v3(result, a, b);
|
||||
return result;
|
||||
}
|
||||
|
||||
static float distance(const float3 &a, const float3 &b)
|
||||
{
|
||||
return (a - b).length();
|
||||
}
|
||||
|
||||
static float distance_squared(const float3 &a, const float3 &b)
|
||||
{
|
||||
float3 diff = a - b;
|
||||
return float3::dot(diff, diff);
|
||||
}
|
||||
|
||||
static float3 interpolate(const float3 &a, const float3 &b, float t)
|
||||
{
|
||||
return a * (1 - t) + b * t;
|
||||
}
|
||||
|
||||
static float3 abs(const float3 &a)
|
||||
{
|
||||
return float3(fabsf(a.x), fabsf(a.y), fabsf(a.z));
|
||||
}
|
||||
};
|
||||
|
||||
inline float3 operator+(const float3 &a, const float3 &b)
|
||||
{
|
||||
return {a.x + b.x, a.y + b.y, a.z + b.z};
|
||||
}
|
||||
|
||||
inline float3 &operator+=(float3 &a, const float3 &b)
|
||||
{
|
||||
a.x += b.x;
|
||||
a.y += b.y;
|
||||
a.z += b.z;
|
||||
return a;
|
||||
}
|
||||
|
||||
inline float3 operator-(const float3 &a, const float3 &b)
|
||||
{
|
||||
return {a.x - b.x, a.y - b.y, a.z - b.z};
|
||||
}
|
||||
|
||||
inline float3 &operator-=(float3 &a, const float3 &b)
|
||||
{
|
||||
a.x -= b.x;
|
||||
a.y -= b.y;
|
||||
a.z -= b.z;
|
||||
return a;
|
||||
}
|
||||
|
||||
inline float3 &operator*=(float3 &a, float scalar)
|
||||
{
|
||||
a.x *= scalar;
|
||||
a.y *= scalar;
|
||||
a.z *= scalar;
|
||||
return a;
|
||||
}
|
||||
|
||||
inline float3 &operator*=(float3 &a, const float3 &other)
|
||||
{
|
||||
a.x *= other.x;
|
||||
a.y *= other.y;
|
||||
a.z *= other.z;
|
||||
return a;
|
||||
}
|
||||
|
||||
inline float3 operator*(const float3 &a, const float3 &b)
|
||||
{
|
||||
return {a.x * b.x, a.y * b.y, a.z * b.z};
|
||||
}
|
||||
|
||||
inline float3 operator/(const float3 &a, float b)
|
||||
{
|
||||
BLI_assert(b != 0.0f);
|
||||
return {a.x / b, a.y / b, a.z / b};
|
||||
}
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &stream, const float3 &v)
|
||||
{
|
||||
stream << "(" << v.x << ", " << v.y << ", " << v.z << ")";
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline float3 operator-(const float3 &a)
|
||||
{
|
||||
return {-a.x, -a.y, -a.z};
|
||||
}
|
||||
|
||||
inline bool operator==(const float3 &a, const float3 &b)
|
||||
{
|
||||
return a.x == b.x && a.y == b.y && a.z == b.z;
|
||||
}
|
||||
|
||||
inline bool operator!=(const float3 &a, const float3 &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
inline float3 operator*(const float3 &a, float b)
|
||||
{
|
||||
return {a.x * b, a.y * b, a.z * b};
|
||||
}
|
||||
|
||||
inline float3 operator*(float a, const float3 &b)
|
||||
{
|
||||
return b * a;
|
||||
}
|
||||
|
||||
inline float normalize_and_get_length(float3 &a)
|
||||
{
|
||||
return normalize_v3(a);
|
||||
}
|
||||
|
||||
inline void normalize(float3 &a)
|
||||
{
|
||||
normalize_v3(a);
|
||||
}
|
||||
|
||||
inline float3 normalized(const float3 &a)
|
||||
{
|
||||
float3 result;
|
||||
normalize_v3_v3(result, a);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline float length(const float3 &a)
|
||||
{
|
||||
return len_v3(a);
|
||||
}
|
||||
|
||||
inline float length_squared(const float3 &a)
|
||||
{
|
||||
return len_squared_v3(a);
|
||||
}
|
||||
|
||||
inline float3 reflected(const float3 &a, const float3 &normal)
|
||||
{
|
||||
float3 result;
|
||||
reflect_v3_v3v3(result, a, normal);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void reflect(float3 &a, const float3 &normal)
|
||||
{
|
||||
a = reflected(a, normal);
|
||||
}
|
||||
|
||||
inline float3 safe_divide(const float3 &a, const float3 &b)
|
||||
{
|
||||
float3 result;
|
||||
result.x = (b.x == 0.0f) ? 0.0f : a.x / b.x;
|
||||
result.y = (b.y == 0.0f) ? 0.0f : a.y / b.y;
|
||||
result.z = (b.z == 0.0f) ? 0.0f : a.z / b.z;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void negate(float3 &a)
|
||||
{
|
||||
a.x = -a.x;
|
||||
a.y = -a.y;
|
||||
a.z = -a.z;
|
||||
}
|
||||
|
||||
inline float dot(const float3 &a, const float3 &b)
|
||||
{
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z;
|
||||
}
|
||||
|
||||
inline float3 cross_high_precision(const float3 &a, const float3 &b)
|
||||
{
|
||||
float3 result;
|
||||
cross_v3_v3v3_hi_prec(result, a, b);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline float3 project(const float3 &a, const float3 &b)
|
||||
{
|
||||
float3 result;
|
||||
project_v3_v3v3(result, a, b);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline float distance(const float3 &a, const float3 &b)
|
||||
{
|
||||
return length(a - b);
|
||||
}
|
||||
|
||||
inline float distance_squared(const float3 &a, const float3 &b)
|
||||
{
|
||||
const float3 diff = a - b;
|
||||
return dot(diff, diff);
|
||||
}
|
||||
|
||||
inline float3 lerp(const float3 &a, const float3 &b, float t)
|
||||
{
|
||||
return a * (1 - t) + b * t;
|
||||
}
|
||||
|
||||
inline float3 abs(const float3 &a)
|
||||
{
|
||||
return float3(fabsf(a.x), fabsf(a.y), fabsf(a.z));
|
||||
}
|
||||
|
||||
} // namespace blender::math
|
||||
|
||||
namespace blender {
|
||||
using math::float3;
|
||||
}
|
||||
} // namespace blender
|
||||
|
@@ -25,7 +25,7 @@
|
||||
# include "BLI_math_mpq.hh"
|
||||
# include "BLI_mpq3.hh"
|
||||
|
||||
namespace blender::math {
|
||||
namespace blender {
|
||||
|
||||
struct mpq2 {
|
||||
mpq_class x, y;
|
||||
@@ -80,106 +80,105 @@ struct mpq2 {
|
||||
return &x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cannot do this exactly in rational arithmetic!
|
||||
* Approximate by going in and out of doubles.
|
||||
*/
|
||||
mpq_class length() const
|
||||
{
|
||||
mpq_class lsquared = dot(*this, *this);
|
||||
return mpq_class(sqrt(lsquared.get_d()));
|
||||
}
|
||||
|
||||
friend mpq2 operator+(const mpq2 &a, const mpq2 &b)
|
||||
{
|
||||
return {a.x + b.x, a.y + b.y};
|
||||
}
|
||||
|
||||
friend mpq2 operator-(const mpq2 &a, const mpq2 &b)
|
||||
{
|
||||
return {a.x - b.x, a.y - b.y};
|
||||
}
|
||||
|
||||
friend mpq2 operator*(const mpq2 &a, mpq_class b)
|
||||
{
|
||||
return {a.x * b, a.y * b};
|
||||
}
|
||||
|
||||
friend mpq2 operator/(const mpq2 &a, mpq_class b)
|
||||
{
|
||||
BLI_assert(b != 0);
|
||||
return {a.x / b, a.y / b};
|
||||
}
|
||||
|
||||
friend mpq2 operator*(mpq_class a, const mpq2 &b)
|
||||
{
|
||||
return b * a;
|
||||
}
|
||||
|
||||
friend bool operator==(const mpq2 &a, const mpq2 &b)
|
||||
{
|
||||
return a.x == b.x && a.y == b.y;
|
||||
}
|
||||
|
||||
friend bool operator!=(const mpq2 &a, const mpq2 &b)
|
||||
{
|
||||
return a.x != b.x || a.y != b.y;
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &stream, const mpq2 &v)
|
||||
{
|
||||
stream << "(" << v.x << ", " << v.y << ")";
|
||||
return stream;
|
||||
}
|
||||
|
||||
static mpq_class dot(const mpq2 &a, const mpq2 &b)
|
||||
{
|
||||
return a.x * b.x + a.y * b.y;
|
||||
}
|
||||
|
||||
static mpq2 interpolate(const mpq2 &a, const mpq2 &b, mpq_class t)
|
||||
{
|
||||
return a * (1 - t) + b * t;
|
||||
}
|
||||
|
||||
static mpq2 abs(const mpq2 &a)
|
||||
{
|
||||
mpq_class abs_x = (a.x >= 0) ? a.x : -a.x;
|
||||
mpq_class abs_y = (a.y >= 0) ? a.y : -a.y;
|
||||
return mpq2(abs_x, abs_y);
|
||||
}
|
||||
|
||||
static mpq_class distance(const mpq2 &a, const mpq2 &b)
|
||||
{
|
||||
return (a - b).length();
|
||||
}
|
||||
|
||||
static mpq_class distance_squared(const mpq2 &a, const mpq2 &b)
|
||||
{
|
||||
mpq2 diff = a - b;
|
||||
return dot(diff, diff);
|
||||
}
|
||||
|
||||
struct isect_result {
|
||||
enum {
|
||||
LINE_LINE_COLINEAR = -1,
|
||||
LINE_LINE_NONE = 0,
|
||||
LINE_LINE_EXACT = 1,
|
||||
LINE_LINE_CROSS = 2,
|
||||
} kind;
|
||||
mpq_class lambda;
|
||||
};
|
||||
|
||||
static isect_result isect_seg_seg(const mpq2 &v1,
|
||||
const mpq2 &v2,
|
||||
const mpq2 &v3,
|
||||
const mpq2 &v4);
|
||||
|
||||
/** There is a sensible use for hashing on exact arithmetic types. */
|
||||
uint64_t hash() const;
|
||||
};
|
||||
|
||||
inline mpq2 operator+(const mpq2 &a, const mpq2 &b)
|
||||
{
|
||||
return {a.x + b.x, a.y + b.y};
|
||||
}
|
||||
|
||||
inline mpq2 operator-(const mpq2 &a, const mpq2 &b)
|
||||
{
|
||||
return {a.x - b.x, a.y - b.y};
|
||||
}
|
||||
|
||||
inline mpq2 operator*(const mpq2 &a, mpq_class b)
|
||||
{
|
||||
return {a.x * b, a.y * b};
|
||||
}
|
||||
|
||||
inline mpq2 operator/(const mpq2 &a, mpq_class b)
|
||||
{
|
||||
BLI_assert(b != 0);
|
||||
return {a.x / b, a.y / b};
|
||||
}
|
||||
|
||||
inline mpq2 operator*(mpq_class a, const mpq2 &b)
|
||||
{
|
||||
return b * a;
|
||||
}
|
||||
|
||||
inline bool operator==(const mpq2 &a, const mpq2 &b)
|
||||
{
|
||||
return a.x == b.x && a.y == b.y;
|
||||
}
|
||||
|
||||
inline bool operator!=(const mpq2 &a, const mpq2 &b)
|
||||
{
|
||||
return a.x != b.x || a.y != b.y;
|
||||
}
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &stream, const mpq2 &v)
|
||||
{
|
||||
stream << "(" << v.x << ", " << v.y << ")";
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline mpq_class dot(const mpq2 &a, const mpq2 &b)
|
||||
{
|
||||
return a.x * b.x + a.y * b.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cannot do this exactly in rational arithmetic!
|
||||
* Approximate by going in and out of doubles.
|
||||
*/
|
||||
inline mpq_class length(const mpq2 &a)
|
||||
{
|
||||
mpq_class lsquared = dot(a, a);
|
||||
return mpq_class(sqrt(lsquared.get_d()));
|
||||
}
|
||||
|
||||
inline mpq2 lerp(const mpq2 &a, const mpq2 &b, mpq_class t)
|
||||
{
|
||||
return a * (1 - t) + b * t;
|
||||
}
|
||||
|
||||
inline mpq2 abs(const mpq2 &a)
|
||||
{
|
||||
mpq_class abs_x = (a.x >= 0) ? a.x : -a.x;
|
||||
mpq_class abs_y = (a.y >= 0) ? a.y : -a.y;
|
||||
return mpq2(abs_x, abs_y);
|
||||
}
|
||||
|
||||
inline mpq_class distance(const mpq2 &a, const mpq2 &b)
|
||||
{
|
||||
return length(a - b);
|
||||
}
|
||||
|
||||
inline mpq_class distance_squared(const mpq2 &a, const mpq2 &b)
|
||||
{
|
||||
const mpq2 diff = a - b;
|
||||
return dot(diff, diff);
|
||||
}
|
||||
|
||||
struct isect_result_mpq2 {
|
||||
enum {
|
||||
LINE_LINE_COLINEAR = -1,
|
||||
LINE_LINE_NONE = 0,
|
||||
LINE_LINE_EXACT = 1,
|
||||
LINE_LINE_CROSS = 2,
|
||||
} kind;
|
||||
mpq_class lambda;
|
||||
};
|
||||
|
||||
isect_result_mpq2 isect_seg_seg(const mpq2 &v1, const mpq2 &v2, const mpq2 &v3, const mpq2 &v4);
|
||||
|
||||
} // namespace blender::math
|
||||
|
||||
namespace blender {
|
||||
using math::mpq2;
|
||||
}
|
||||
} // namespace blender
|
||||
|
||||
#endif /* WITH_GMP */
|
||||
|
@@ -28,7 +28,7 @@
|
||||
# include "BLI_math_mpq.hh"
|
||||
# include "BLI_span.hh"
|
||||
|
||||
namespace blender::math {
|
||||
namespace blender {
|
||||
|
||||
struct mpq3 {
|
||||
mpq_class x, y, z;
|
||||
@@ -65,219 +65,217 @@ struct mpq3 {
|
||||
return &x;
|
||||
}
|
||||
|
||||
/* Cannot do this exactly in rational arithmetic!
|
||||
* Approximate by going in and out of doubles.
|
||||
*/
|
||||
mpq_class normalize_and_get_length()
|
||||
{
|
||||
double dv[3] = {x.get_d(), y.get_d(), z.get_d()};
|
||||
double len = normalize_v3_db(dv);
|
||||
this->x = mpq_class(dv[0]);
|
||||
this->y = mpq_class(dv[1]);
|
||||
this->z = mpq_class(dv[2]);
|
||||
return len;
|
||||
}
|
||||
|
||||
mpq3 normalized() const
|
||||
{
|
||||
double dv[3] = {x.get_d(), y.get_d(), z.get_d()};
|
||||
double dr[3];
|
||||
normalize_v3_v3_db(dr, dv);
|
||||
return mpq3(mpq_class(dr[0]), mpq_class(dr[1]), mpq_class(dr[2]));
|
||||
}
|
||||
|
||||
/* Cannot do this exactly in rational arithmetic!
|
||||
* Approximate by going in and out of double.
|
||||
*/
|
||||
mpq_class length() const
|
||||
{
|
||||
mpq_class lsquared = this->length_squared();
|
||||
double dsquared = lsquared.get_d();
|
||||
double d = sqrt(dsquared);
|
||||
return mpq_class(d);
|
||||
}
|
||||
|
||||
mpq_class length_squared() const
|
||||
{
|
||||
return x * x + y * y + z * z;
|
||||
}
|
||||
|
||||
void reflect(const mpq3 &normal)
|
||||
{
|
||||
*this = this->reflected(normal);
|
||||
}
|
||||
|
||||
mpq3 reflected(const mpq3 &normal) const
|
||||
{
|
||||
mpq3 result;
|
||||
const mpq_class dot2 = 2 * dot(*this, normal);
|
||||
result.x = this->x - (dot2 * normal.x);
|
||||
result.y = this->y - (dot2 * normal.y);
|
||||
result.z = this->z - (dot2 * normal.z);
|
||||
return result;
|
||||
}
|
||||
|
||||
static mpq3 safe_divide(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
mpq3 result;
|
||||
result.x = (b.x == 0) ? mpq_class(0) : a.x / b.x;
|
||||
result.y = (b.y == 0) ? mpq_class(0) : a.y / b.y;
|
||||
result.z = (b.z == 0) ? mpq_class(0) : a.z / b.z;
|
||||
return result;
|
||||
}
|
||||
|
||||
void invert()
|
||||
{
|
||||
x = -x;
|
||||
y = -y;
|
||||
z = -z;
|
||||
}
|
||||
|
||||
friend mpq3 operator+(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
return mpq3(a.x + b.x, a.y + b.y, a.z + b.z);
|
||||
}
|
||||
|
||||
void operator+=(const mpq3 &b)
|
||||
{
|
||||
this->x += b.x;
|
||||
this->y += b.y;
|
||||
this->z += b.z;
|
||||
}
|
||||
|
||||
friend mpq3 operator-(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
return mpq3(a.x - b.x, a.y - b.y, a.z - b.z);
|
||||
}
|
||||
|
||||
friend mpq3 operator-(const mpq3 &a)
|
||||
{
|
||||
return mpq3(-a.x, -a.y, -a.z);
|
||||
}
|
||||
|
||||
void operator-=(const mpq3 &b)
|
||||
{
|
||||
this->x -= b.x;
|
||||
this->y -= b.y;
|
||||
this->z -= b.z;
|
||||
}
|
||||
|
||||
void operator*=(mpq_class scalar)
|
||||
{
|
||||
this->x *= scalar;
|
||||
this->y *= scalar;
|
||||
this->z *= scalar;
|
||||
}
|
||||
|
||||
void operator*=(const mpq3 &other)
|
||||
{
|
||||
this->x *= other.x;
|
||||
this->y *= other.y;
|
||||
this->z *= other.z;
|
||||
}
|
||||
|
||||
friend mpq3 operator*(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
return {a.x * b.x, a.y * b.y, a.z * b.z};
|
||||
}
|
||||
|
||||
friend mpq3 operator*(const mpq3 &a, const mpq_class &b)
|
||||
{
|
||||
return mpq3(a.x * b, a.y * b, a.z * b);
|
||||
}
|
||||
|
||||
friend mpq3 operator*(const mpq_class &a, const mpq3 &b)
|
||||
{
|
||||
return mpq3(a * b.x, a * b.y, a * b.z);
|
||||
}
|
||||
|
||||
friend mpq3 operator/(const mpq3 &a, const mpq_class &b)
|
||||
{
|
||||
BLI_assert(b != 0);
|
||||
return mpq3(a.x / b, a.y / b, a.z / b);
|
||||
}
|
||||
|
||||
friend bool operator==(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
return a.x == b.x && a.y == b.y && a.z == b.z;
|
||||
}
|
||||
|
||||
friend bool operator!=(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
return a.x != b.x || a.y != b.y || a.z != b.z;
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &stream, const mpq3 &v)
|
||||
{
|
||||
stream << "(" << v.x << ", " << v.y << ", " << v.z << ")";
|
||||
return stream;
|
||||
}
|
||||
|
||||
static mpq_class dot(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z;
|
||||
}
|
||||
|
||||
static mpq3 cross(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
return mpq3(a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]);
|
||||
}
|
||||
|
||||
static mpq3 cross_high_precision(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
return cross(a, b);
|
||||
}
|
||||
|
||||
static mpq3 project(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
const mpq_class mul = mpq3::dot(a, b) / mpq3::dot(b, b);
|
||||
return mpq3(mul * b[0], mul * b[1], mul * b[2]);
|
||||
}
|
||||
|
||||
static mpq_class distance(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
mpq3 diff(a.x - b.x, a.y - b.y, a.z - b.z);
|
||||
return diff.length();
|
||||
}
|
||||
|
||||
static mpq_class distance_squared(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
mpq3 diff(a.x - b.x, a.y - b.y, a.z - b.z);
|
||||
return mpq3::dot(diff, diff);
|
||||
}
|
||||
|
||||
static mpq3 interpolate(const mpq3 &a, const mpq3 &b, mpq_class t)
|
||||
{
|
||||
mpq_class s = 1 - t;
|
||||
return mpq3(a.x * s + b.x * t, a.y * s + b.y * t, a.z * s + b.z * t);
|
||||
}
|
||||
|
||||
static mpq3 abs(const mpq3 &a)
|
||||
{
|
||||
mpq_class abs_x = (a.x >= 0) ? a.x : -a.x;
|
||||
mpq_class abs_y = (a.y >= 0) ? a.y : -a.y;
|
||||
mpq_class abs_z = (a.z >= 0) ? a.z : -a.z;
|
||||
return mpq3(abs_x, abs_y, abs_z);
|
||||
}
|
||||
|
||||
static int dominant_axis(const mpq3 &a)
|
||||
{
|
||||
mpq_class x = (a.x >= 0) ? a.x : -a.x;
|
||||
mpq_class y = (a.y >= 0) ? a.y : -a.y;
|
||||
mpq_class z = (a.z >= 0) ? a.z : -a.z;
|
||||
return ((x > y) ? ((x > z) ? 0 : 2) : ((y > z) ? 1 : 2));
|
||||
}
|
||||
|
||||
static mpq3 cross_poly(Span<mpq3> poly);
|
||||
|
||||
/** There is a sensible use for hashing on exact arithmetic types. */
|
||||
uint64_t hash() const;
|
||||
};
|
||||
|
||||
/* Cannot do this exactly in rational arithmetic!
|
||||
* Approximate by going in and out of doubles.
|
||||
*/
|
||||
inline mpq_class normalize_and_get_length(mpq3 &a)
|
||||
{
|
||||
double dv[3] = {a.x.get_d(), a.y.get_d(), a.z.get_d()};
|
||||
double len = normalize_v3_db(dv);
|
||||
a.x = mpq_class(dv[0]);
|
||||
a.y = mpq_class(dv[1]);
|
||||
a.z = mpq_class(dv[2]);
|
||||
return len;
|
||||
}
|
||||
|
||||
inline mpq3 normalized(const mpq3 &a)
|
||||
{
|
||||
double dv[3] = {a.x.get_d(), a.y.get_d(), a.z.get_d()};
|
||||
double dr[3];
|
||||
normalize_v3_v3_db(dr, dv);
|
||||
return mpq3(mpq_class(dr[0]), mpq_class(dr[1]), mpq_class(dr[2]));
|
||||
}
|
||||
|
||||
inline mpq_class length_squared(const mpq3 &a)
|
||||
{
|
||||
return a.x * a.x + a.y * a.y + a.z * a.z;
|
||||
}
|
||||
|
||||
/* Cannot do this exactly in rational arithmetic!
|
||||
* Approximate by going in and out of double.
|
||||
*/
|
||||
inline mpq_class length(const mpq3 &a)
|
||||
{
|
||||
mpq_class lsquared = length_squared(a);
|
||||
double dsquared = lsquared.get_d();
|
||||
double d = sqrt(dsquared);
|
||||
return mpq_class(d);
|
||||
}
|
||||
|
||||
inline mpq_class dot(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z;
|
||||
}
|
||||
|
||||
inline mpq3 reflected(const mpq3 &a, const mpq3 &normal)
|
||||
{
|
||||
mpq3 result;
|
||||
const mpq_class dot2 = 2 * dot(a, normal);
|
||||
result.x = a.x - (dot2 * normal.x);
|
||||
result.y = a.y - (dot2 * normal.y);
|
||||
result.z = a.z - (dot2 * normal.z);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void reflect(mpq3 &a, const mpq3 &normal)
|
||||
{
|
||||
a = reflected(a, normal);
|
||||
}
|
||||
|
||||
inline mpq3 safe_divide(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
mpq3 result;
|
||||
result.x = (b.x == 0) ? mpq_class(0) : a.x / b.x;
|
||||
result.y = (b.y == 0) ? mpq_class(0) : a.y / b.y;
|
||||
result.z = (b.z == 0) ? mpq_class(0) : a.z / b.z;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void negate(mpq3 &a)
|
||||
{
|
||||
a.x = -a.x;
|
||||
a.y = -a.y;
|
||||
a.z = -a.z;
|
||||
}
|
||||
|
||||
inline mpq3 operator+(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
return mpq3(a.x + b.x, a.y + b.y, a.z + b.z);
|
||||
}
|
||||
|
||||
inline void operator+=(mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
a.x += b.x;
|
||||
a.y += b.y;
|
||||
a.z += b.z;
|
||||
}
|
||||
|
||||
inline mpq3 operator-(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
return mpq3(a.x - b.x, a.y - b.y, a.z - b.z);
|
||||
}
|
||||
|
||||
inline mpq3 operator-(const mpq3 &a)
|
||||
{
|
||||
return mpq3(-a.x, -a.y, -a.z);
|
||||
}
|
||||
|
||||
inline void operator-=(mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
a.x -= b.x;
|
||||
a.y -= b.y;
|
||||
a.z -= b.z;
|
||||
}
|
||||
|
||||
inline void operator*=(mpq3 &a, mpq_class scalar)
|
||||
{
|
||||
a.x *= scalar;
|
||||
a.y *= scalar;
|
||||
a.z *= scalar;
|
||||
}
|
||||
|
||||
inline void operator*=(mpq3 &a, const mpq3 &other)
|
||||
{
|
||||
a.x *= other.x;
|
||||
a.y *= other.y;
|
||||
a.z *= other.z;
|
||||
}
|
||||
|
||||
inline mpq3 operator*(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
return {a.x * b.x, a.y * b.y, a.z * b.z};
|
||||
}
|
||||
|
||||
inline mpq3 operator*(const mpq3 &a, const mpq_class &b)
|
||||
{
|
||||
return mpq3(a.x * b, a.y * b, a.z * b);
|
||||
}
|
||||
|
||||
inline mpq3 operator*(const mpq_class &a, const mpq3 &b)
|
||||
{
|
||||
return mpq3(a * b.x, a * b.y, a * b.z);
|
||||
}
|
||||
|
||||
inline mpq3 operator/(const mpq3 &a, const mpq_class &b)
|
||||
{
|
||||
BLI_assert(b != 0);
|
||||
return mpq3(a.x / b, a.y / b, a.z / b);
|
||||
}
|
||||
|
||||
inline bool operator==(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
return a.x == b.x && a.y == b.y && a.z == b.z;
|
||||
}
|
||||
|
||||
inline bool operator!=(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
return a.x != b.x || a.y != b.y || a.z != b.z;
|
||||
}
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &stream, const mpq3 &v)
|
||||
{
|
||||
stream << "(" << v.x << ", " << v.y << ", " << v.z << ")";
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline mpq3 cross(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
return mpq3(a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]);
|
||||
}
|
||||
|
||||
inline mpq3 cross_high_precision(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
return cross(a, b);
|
||||
}
|
||||
|
||||
inline mpq3 project(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
const mpq_class mul = dot(a, b) / dot(b, b);
|
||||
return mpq3(mul * b[0], mul * b[1], mul * b[2]);
|
||||
}
|
||||
|
||||
inline mpq_class distance(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
return length(a - b);
|
||||
}
|
||||
|
||||
inline mpq_class distance_squared(const mpq3 &a, const mpq3 &b)
|
||||
{
|
||||
mpq3 diff(a.x - b.x, a.y - b.y, a.z - b.z);
|
||||
return dot(diff, diff);
|
||||
}
|
||||
|
||||
inline mpq3 lerp(const mpq3 &a, const mpq3 &b, mpq_class t)
|
||||
{
|
||||
mpq_class s = 1 - t;
|
||||
return mpq3(a.x * s + b.x * t, a.y * s + b.y * t, a.z * s + b.z * t);
|
||||
}
|
||||
|
||||
inline mpq3 abs(const mpq3 &a)
|
||||
{
|
||||
mpq_class abs_x = (a.x >= 0) ? a.x : -a.x;
|
||||
mpq_class abs_y = (a.y >= 0) ? a.y : -a.y;
|
||||
mpq_class abs_z = (a.z >= 0) ? a.z : -a.z;
|
||||
return mpq3(abs_x, abs_y, abs_z);
|
||||
}
|
||||
|
||||
inline int dominant_axis(const mpq3 &a)
|
||||
{
|
||||
mpq_class x = (a.x >= 0) ? a.x : -a.x;
|
||||
mpq_class y = (a.y >= 0) ? a.y : -a.y;
|
||||
mpq_class z = (a.z >= 0) ? a.z : -a.z;
|
||||
return ((x > y) ? ((x > z) ? 0 : 2) : ((y > z) ? 1 : 2));
|
||||
}
|
||||
|
||||
mpq3 cross_poly(Span<mpq3> poly);
|
||||
uint64_t hash_mpq_class(const mpq_class &value);
|
||||
|
||||
} // namespace blender::math
|
||||
|
||||
namespace blender {
|
||||
using math::mpq3;
|
||||
}
|
||||
} // namespace blender
|
||||
|
||||
#endif /* WITH_GMP */
|
||||
|
@@ -730,11 +730,11 @@ bool in_line<mpq_class>(const FatCo<mpq_class> &a,
|
||||
}
|
||||
vec2<mpq_class> exact_ab = b.exact - a.exact;
|
||||
vec2<mpq_class> exact_ac = c.exact - a.exact;
|
||||
if (dot(exact_ab, exact_ac) < 0) {
|
||||
if (vec2<mpq_class>::dot(exact_ab, exact_ac) < 0) {
|
||||
return false;
|
||||
}
|
||||
vec2<mpq_class> exact_bc = c.exact - b.exact;
|
||||
return dot(exact_bc, exact_ac) >= 0;
|
||||
return vec2<mpq_class>::dot(exact_bc, exact_ac) >= 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -743,11 +743,11 @@ bool in_line<double>(const FatCo<double> &a, const FatCo<double> &b, const FatCo
|
||||
{
|
||||
vec2<double> ab = b.approx - a.approx;
|
||||
vec2<double> ac = c.approx - a.approx;
|
||||
if (dot(ab, ac) < 0) {
|
||||
if (vec2<double>::dot(ab, ac) < 0) {
|
||||
return false;
|
||||
}
|
||||
vec2<double> bc = c.approx - b.approx;
|
||||
return dot(bc, ac) >= 0;
|
||||
return vec2<double>::dot(bc, ac) >= 0;
|
||||
}
|
||||
|
||||
template<> CDTVert<double>::CDTVert(const vec2<double> &pt)
|
||||
@@ -1041,7 +1041,7 @@ template<typename T> CDTEdge<T> *CDTArrangement<T>::split_edge(SymEdge<T> *se, T
|
||||
SymEdge<T> *sesymprev = prev(sesym);
|
||||
SymEdge<T> *sesymprevsym = sym(sesymprev);
|
||||
SymEdge<T> *senext = se->next;
|
||||
CDTVert<T> *v = this->add_vert(lerp(*a, *b, lambda));
|
||||
CDTVert<T> *v = this->add_vert(vec2<T>::interpolate(*a, *b, lambda));
|
||||
CDTEdge<T> *e = this->add_edge(v, se->next->vert, se->face, sesym->face);
|
||||
sesym->vert = v;
|
||||
SymEdge<T> *newse = &e->symedges[0];
|
||||
@@ -1664,17 +1664,16 @@ void fill_crossdata_for_intersect(const FatCo<T> &curco,
|
||||
BLI_assert(se_vcva->vert == vc && se_vcva->next->vert == va);
|
||||
BLI_assert(se_vcvb->vert == vc && se_vcvb->next->vert == vb);
|
||||
UNUSED_VARS_NDEBUG(vc);
|
||||
auto isect = isect_seg_seg(va->co.exact, vb->co.exact, curco.exact, v2->co.exact);
|
||||
using isect_result = decltype(isect);
|
||||
auto isect = vec2<T>::isect_seg_seg(va->co.exact, vb->co.exact, curco.exact, v2->co.exact);
|
||||
T &lambda = isect.lambda;
|
||||
switch (isect.kind) {
|
||||
case isect_result::LINE_LINE_CROSS: {
|
||||
case vec2<T>::isect_result::LINE_LINE_CROSS: {
|
||||
#ifdef WITH_GMP
|
||||
if (!std::is_same<T, mpq_class>::value) {
|
||||
#else
|
||||
if (true) {
|
||||
#endif
|
||||
double len_ab = distance(va->co.approx, vb->co.approx);
|
||||
double len_ab = vec2<double>::distance(va->co.approx, vb->co.approx);
|
||||
if (lambda * len_ab <= epsilon) {
|
||||
fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next);
|
||||
}
|
||||
@@ -1696,7 +1695,7 @@ void fill_crossdata_for_intersect(const FatCo<T> &curco,
|
||||
}
|
||||
break;
|
||||
}
|
||||
case isect_result::LINE_LINE_EXACT: {
|
||||
case vec2<T>::isect_result::LINE_LINE_EXACT: {
|
||||
if (lambda == 0) {
|
||||
fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next);
|
||||
}
|
||||
@@ -1711,7 +1710,7 @@ void fill_crossdata_for_intersect(const FatCo<T> &curco,
|
||||
}
|
||||
break;
|
||||
}
|
||||
case isect_result::LINE_LINE_NONE: {
|
||||
case vec2<T>::isect_result::LINE_LINE_NONE: {
|
||||
#ifdef WITH_GMP
|
||||
if (std::is_same<T, mpq_class>::value) {
|
||||
BLI_assert(false);
|
||||
@@ -1727,9 +1726,9 @@ void fill_crossdata_for_intersect(const FatCo<T> &curco,
|
||||
}
|
||||
break;
|
||||
}
|
||||
case isect_result::LINE_LINE_COLINEAR: {
|
||||
if (distance_squared(va->co.approx, v2->co.approx) <=
|
||||
distance_squared(vb->co.approx, v2->co.approx)) {
|
||||
case vec2<T>::isect_result::LINE_LINE_COLINEAR: {
|
||||
if (vec2<double>::distance_squared(va->co.approx, v2->co.approx) <=
|
||||
vec2<double>::distance_squared(vb->co.approx, v2->co.approx)) {
|
||||
fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next);
|
||||
}
|
||||
else {
|
||||
@@ -1806,7 +1805,7 @@ void get_next_crossing_from_edge(CrossData<T> *cd,
|
||||
{
|
||||
CDTVert<T> *va = cd->in->vert;
|
||||
CDTVert<T> *vb = cd->in->next->vert;
|
||||
vec2<T> curco = lerp(va->co.exact, vb->co.exact, cd->lambda);
|
||||
vec2<T> curco = vec2<T>::interpolate(va->co.exact, vb->co.exact, cd->lambda);
|
||||
FatCo<T> fat_curco(curco);
|
||||
SymEdge<T> *se_ac = sym(cd->in)->next;
|
||||
CDTVert<T> *vc = se_ac->next->vert;
|
||||
@@ -2333,7 +2332,7 @@ template<typename T> void remove_non_constraint_edges_leave_valid_bmesh(CDT_stat
|
||||
dissolvable_edges[i].e = e;
|
||||
const vec2<double> &co1 = e->symedges[0].vert->co.approx;
|
||||
const vec2<double> &co2 = e->symedges[1].vert->co.approx;
|
||||
dissolvable_edges[i].len_squared = distance_squared(co1, co2);
|
||||
dissolvable_edges[i].len_squared = vec2<double>::distance_squared(co1, co2);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
@@ -29,75 +29,78 @@
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
namespace blender::math {
|
||||
namespace blender {
|
||||
|
||||
isect_result_float2 isect_seg_seg(const float2 &v1,
|
||||
const float2 &v2,
|
||||
const float2 &v3,
|
||||
const float2 &v4)
|
||||
float2::isect_result float2::isect_seg_seg(const float2 &v1,
|
||||
const float2 &v2,
|
||||
const float2 &v3,
|
||||
const float2 &v4)
|
||||
{
|
||||
isect_result_float2 ans;
|
||||
float2::isect_result ans;
|
||||
float div = (v2[0] - v1[0]) * (v4[1] - v3[1]) - (v2[1] - v1[1]) * (v4[0] - v3[0]);
|
||||
if (div == 0.0f) {
|
||||
ans.lambda = 0.0f;
|
||||
ans.mu = 0.0f;
|
||||
ans.kind = isect_result_float2::LINE_LINE_COLINEAR;
|
||||
ans.kind = float2::isect_result::LINE_LINE_COLINEAR;
|
||||
}
|
||||
else {
|
||||
ans.lambda = ((v1[1] - v3[1]) * (v4[0] - v3[0]) - (v1[0] - v3[0]) * (v4[1] - v3[1])) / div;
|
||||
ans.mu = ((v1[1] - v3[1]) * (v2[0] - v1[0]) - (v1[0] - v3[0]) * (v2[1] - v1[1])) / div;
|
||||
if (ans.lambda >= 0.0f && ans.lambda <= 1.0f && ans.mu >= 0.0f && ans.mu <= 1.0f) {
|
||||
if (ans.lambda == 0.0f || ans.lambda == 1.0f || ans.mu == 0.0f || ans.mu == 1.0f) {
|
||||
ans.kind = isect_result_float2::LINE_LINE_EXACT;
|
||||
ans.kind = float2::isect_result::LINE_LINE_EXACT;
|
||||
}
|
||||
else {
|
||||
ans.kind = isect_result_float2::LINE_LINE_CROSS;
|
||||
ans.kind = float2::isect_result::LINE_LINE_CROSS;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ans.kind = isect_result_float2::LINE_LINE_NONE;
|
||||
ans.kind = float2::isect_result::LINE_LINE_NONE;
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
isect_result_double2 isect_seg_seg(const double2 &v1,
|
||||
const double2 &v2,
|
||||
const double2 &v3,
|
||||
const double2 &v4)
|
||||
double2::isect_result double2::isect_seg_seg(const double2 &v1,
|
||||
const double2 &v2,
|
||||
const double2 &v3,
|
||||
const double2 &v4)
|
||||
{
|
||||
isect_result_double2 ans;
|
||||
double2::isect_result ans;
|
||||
double div = (v2[0] - v1[0]) * (v4[1] - v3[1]) - (v2[1] - v1[1]) * (v4[0] - v3[0]);
|
||||
if (div == 0.0) {
|
||||
ans.lambda = 0.0;
|
||||
ans.kind = isect_result_double2::LINE_LINE_COLINEAR;
|
||||
ans.kind = double2::isect_result::LINE_LINE_COLINEAR;
|
||||
}
|
||||
else {
|
||||
ans.lambda = ((v1[1] - v3[1]) * (v4[0] - v3[0]) - (v1[0] - v3[0]) * (v4[1] - v3[1])) / div;
|
||||
double mu = ((v1[1] - v3[1]) * (v2[0] - v1[0]) - (v1[0] - v3[0]) * (v2[1] - v1[1])) / div;
|
||||
if (ans.lambda >= 0.0 && ans.lambda <= 1.0 && mu >= 0.0 && mu <= 1.0) {
|
||||
if (ans.lambda == 0.0 || ans.lambda == 1.0 || mu == 0.0 || mu == 1.0) {
|
||||
ans.kind = isect_result_double2::LINE_LINE_EXACT;
|
||||
ans.kind = double2::isect_result::LINE_LINE_EXACT;
|
||||
}
|
||||
else {
|
||||
ans.kind = isect_result_double2::LINE_LINE_CROSS;
|
||||
ans.kind = double2::isect_result::LINE_LINE_CROSS;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ans.kind = isect_result_double2::LINE_LINE_NONE;
|
||||
ans.kind = double2::isect_result::LINE_LINE_NONE;
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
#ifdef WITH_GMP
|
||||
isect_result_mpq2 isect_seg_seg(const mpq2 &v1, const mpq2 &v2, const mpq2 &v3, const mpq2 &v4)
|
||||
mpq2::isect_result mpq2::isect_seg_seg(const mpq2 &v1,
|
||||
const mpq2 &v2,
|
||||
const mpq2 &v3,
|
||||
const mpq2 &v4)
|
||||
{
|
||||
isect_result_mpq2 ans;
|
||||
mpq2::isect_result ans;
|
||||
mpq_class div = (v2[0] - v1[0]) * (v4[1] - v3[1]) - (v2[1] - v1[1]) * (v4[0] - v3[0]);
|
||||
if (div == 0.0) {
|
||||
ans.lambda = 0.0;
|
||||
ans.kind = isect_result_mpq2::LINE_LINE_COLINEAR;
|
||||
ans.kind = mpq2::isect_result::LINE_LINE_COLINEAR;
|
||||
}
|
||||
else {
|
||||
ans.lambda = ((v1[1] - v3[1]) * (v4[0] - v3[0]) - (v1[0] - v3[0]) * (v4[1] - v3[1])) / div;
|
||||
@@ -106,21 +109,21 @@ isect_result_mpq2 isect_seg_seg(const mpq2 &v1, const mpq2 &v2, const mpq2 &v3,
|
||||
if (ans.lambda >= 0 && ans.lambda <= 1 &&
|
||||
((div > 0 && mudiv >= 0 && mudiv <= div) || (div < 0 && mudiv <= 0 && mudiv >= div))) {
|
||||
if (ans.lambda == 0 || ans.lambda == 1 || mudiv == 0 || mudiv == div) {
|
||||
ans.kind = isect_result_mpq2::LINE_LINE_EXACT;
|
||||
ans.kind = mpq2::isect_result::LINE_LINE_EXACT;
|
||||
}
|
||||
else {
|
||||
ans.kind = isect_result_mpq2::LINE_LINE_CROSS;
|
||||
ans.kind = mpq2::isect_result::LINE_LINE_CROSS;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ans.kind = isect_result_mpq2::LINE_LINE_NONE;
|
||||
ans.kind = mpq2::isect_result::LINE_LINE_NONE;
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
} // namespace math
|
||||
}
|
||||
#endif
|
||||
|
||||
double3 cross_poly(Span<double3> poly)
|
||||
double3 double3::cross_poly(Span<double3> poly)
|
||||
{
|
||||
/* Newell's Method. */
|
||||
int nv = static_cast<int>(poly.size());
|
||||
@@ -144,7 +147,7 @@ double3 cross_poly(Span<double3> poly)
|
||||
}
|
||||
|
||||
#ifdef WITH_GMP
|
||||
mpq3 cross_poly(Span<mpq3> poly)
|
||||
mpq3 mpq3::cross_poly(Span<mpq3> poly)
|
||||
{
|
||||
/* Newell's Method. */
|
||||
int nv = static_cast<int>(poly.size());
|
||||
@@ -189,4 +192,4 @@ uint64_t mpq3::hash() const
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace blender::math
|
||||
} // namespace blender
|
||||
|
@@ -1561,13 +1561,13 @@ static Edge find_good_sorting_edge(const Vert *testp,
|
||||
ordinate[axis_next] = -abscissa[axis];
|
||||
ordinate[axis_next_next] = 0;
|
||||
/* By construction, dot(abscissa, ordinate) == 0, so they are perpendicular. */
|
||||
mpq3 normal = cross(abscissa, ordinate);
|
||||
mpq3 normal = mpq3::cross(abscissa, ordinate);
|
||||
if (dbg_level > 0) {
|
||||
std::cout << "abscissa = " << abscissa << "\n";
|
||||
std::cout << "ordinate = " << ordinate << "\n";
|
||||
std::cout << "normal = " << normal << "\n";
|
||||
}
|
||||
mpq_class nlen2 = length_squared(normal);
|
||||
mpq_class nlen2 = normal.length_squared();
|
||||
mpq_class max_abs_slope = -1;
|
||||
Edge esort;
|
||||
const Vector<Edge> &edges = tmtopo.vert_edges(closestp);
|
||||
@@ -1576,12 +1576,12 @@ static Edge find_good_sorting_edge(const Vert *testp,
|
||||
const mpq3 &co_other = v_other->co_exact;
|
||||
mpq3 evec = co_other - co_closest;
|
||||
/* Get projection of evec onto plane of abscissa and ordinate. */
|
||||
mpq3 proj_evec = evec - (dot(evec, normal) / nlen2) * normal;
|
||||
mpq3 proj_evec = evec - (mpq3::dot(evec, normal) / nlen2) * normal;
|
||||
/* The projection calculations along the abscissa and ordinate should
|
||||
* be scaled by 1/abscissa and 1/ordinate respectively,
|
||||
* but we can skip: it won't affect which `evec` has the maximum slope. */
|
||||
mpq_class evec_a = dot(proj_evec, abscissa);
|
||||
mpq_class evec_o = dot(proj_evec, ordinate);
|
||||
mpq_class evec_a = mpq3::dot(proj_evec, abscissa);
|
||||
mpq_class evec_o = mpq3::dot(proj_evec, ordinate);
|
||||
if (dbg_level > 0) {
|
||||
std::cout << "e = " << e << "\n";
|
||||
std::cout << "v_other = " << v_other << "\n";
|
||||
@@ -1700,8 +1700,8 @@ static mpq_class closest_on_tri_to_point(
|
||||
mpq3 ab = b - a;
|
||||
mpq3 ac = c - a;
|
||||
mpq3 ap = p - a;
|
||||
mpq_class d1 = dot(ab, ap);
|
||||
mpq_class d2 = dot(ac, ap);
|
||||
mpq_class d1 = mpq3::dot(ab, ap);
|
||||
mpq_class d2 = mpq3::dot(ac, ap);
|
||||
if (d1 <= 0 && d2 <= 0) {
|
||||
/* Barycentric coordinates (1,0,0). */
|
||||
*r_edge = -1;
|
||||
@@ -1709,12 +1709,12 @@ static mpq_class closest_on_tri_to_point(
|
||||
if (dbg_level > 0) {
|
||||
std::cout << " answer = a\n";
|
||||
}
|
||||
return distance_squared(p, a);
|
||||
return mpq3::distance_squared(p, a);
|
||||
}
|
||||
/* Check if p in vertex region outside b. */
|
||||
mpq3 bp = p - b;
|
||||
mpq_class d3 = dot(ab, bp);
|
||||
mpq_class d4 = dot(ac, bp);
|
||||
mpq_class d3 = mpq3::dot(ab, bp);
|
||||
mpq_class d4 = mpq3::dot(ac, bp);
|
||||
if (d3 >= 0 && d4 <= d3) {
|
||||
/* Barycentric coordinates (0,1,0). */
|
||||
*r_edge = -1;
|
||||
@@ -1722,7 +1722,7 @@ static mpq_class closest_on_tri_to_point(
|
||||
if (dbg_level > 0) {
|
||||
std::cout << " answer = b\n";
|
||||
}
|
||||
return distance_squared(p, b);
|
||||
return mpq3::distance_squared(p, b);
|
||||
}
|
||||
/* Check if p in region of ab. */
|
||||
mpq_class vc = d1 * d4 - d3 * d2;
|
||||
@@ -1735,12 +1735,12 @@ static mpq_class closest_on_tri_to_point(
|
||||
if (dbg_level > 0) {
|
||||
std::cout << " answer = on ab at " << r << "\n";
|
||||
}
|
||||
return distance_squared(p, r);
|
||||
return mpq3::distance_squared(p, r);
|
||||
}
|
||||
/* Check if p in vertex region outside c. */
|
||||
mpq3 cp = p - c;
|
||||
mpq_class d5 = dot(ab, cp);
|
||||
mpq_class d6 = dot(ac, cp);
|
||||
mpq_class d5 = mpq3::dot(ab, cp);
|
||||
mpq_class d6 = mpq3::dot(ac, cp);
|
||||
if (d6 >= 0 && d5 <= d6) {
|
||||
/* Barycentric coordinates (0,0,1). */
|
||||
*r_edge = -1;
|
||||
@@ -1748,7 +1748,7 @@ static mpq_class closest_on_tri_to_point(
|
||||
if (dbg_level > 0) {
|
||||
std::cout << " answer = c\n";
|
||||
}
|
||||
return distance_squared(p, c);
|
||||
return mpq3::distance_squared(p, c);
|
||||
}
|
||||
/* Check if p in edge region of ac. */
|
||||
mpq_class vb = d5 * d2 - d1 * d6;
|
||||
@@ -1761,7 +1761,7 @@ static mpq_class closest_on_tri_to_point(
|
||||
if (dbg_level > 0) {
|
||||
std::cout << " answer = on ac at " << r << "\n";
|
||||
}
|
||||
return distance_squared(p, r);
|
||||
return mpq3::distance_squared(p, r);
|
||||
}
|
||||
/* Check if p in edge region of bc. */
|
||||
mpq_class va = d3 * d6 - d5 * d4;
|
||||
@@ -1776,7 +1776,7 @@ static mpq_class closest_on_tri_to_point(
|
||||
if (dbg_level > 0) {
|
||||
std::cout << " answer = on bc at " << r << "\n";
|
||||
}
|
||||
return distance_squared(p, r);
|
||||
return mpq3::distance_squared(p, r);
|
||||
}
|
||||
/* p inside face region. Compute barycentric coordinates (u,v,w). */
|
||||
mpq_class denom = 1 / (va + vb + vc);
|
||||
@@ -1790,7 +1790,7 @@ static mpq_class closest_on_tri_to_point(
|
||||
if (dbg_level > 0) {
|
||||
std::cout << " answer = inside at " << r << "\n";
|
||||
}
|
||||
return distance_squared(p, r);
|
||||
return mpq3::distance_squared(p, r);
|
||||
}
|
||||
|
||||
struct ComponentContainer {
|
||||
@@ -2375,12 +2375,13 @@ static double generalized_winding_number(const IMesh &tm,
|
||||
/* Calculate the solid angle of abc relative to origin.
|
||||
* See "The Solid Angle of a Plane Triangle" by Oosterom and Strackee
|
||||
* for the derivation of the formula. */
|
||||
double alen = length(a);
|
||||
double blen = length(b);
|
||||
double clen = length(c);
|
||||
double3 bxc = cross_high_precision(b, c);
|
||||
double num = dot(a, bxc);
|
||||
double denom = alen * blen * clen + dot(a, b) * clen + dot(a, c) * blen + dot(b, c) * alen;
|
||||
double alen = a.length();
|
||||
double blen = b.length();
|
||||
double clen = c.length();
|
||||
double3 bxc = double3::cross_high_precision(b, c);
|
||||
double num = double3::dot(a, bxc);
|
||||
double denom = alen * blen * clen + double3::dot(a, b) * clen + double3::dot(a, c) * blen +
|
||||
double3::dot(b, c) * alen;
|
||||
if (denom == 0.0) {
|
||||
if (dbg_level > 0) {
|
||||
std::cout << "denom == 0, skipping this tri\n";
|
||||
@@ -2568,7 +2569,7 @@ static Array<Face *> triangulate_poly(Face *f, IMeshArena *arena)
|
||||
f->populate_plane(false);
|
||||
}
|
||||
const double3 &poly_normal = f->plane->norm;
|
||||
int axis = dominant_axis(poly_normal);
|
||||
int axis = double3::dominant_axis(poly_normal);
|
||||
/* If project down y axis as opposed to x or z, the orientation
|
||||
* of the polygon will be reversed.
|
||||
* Yet another reversal happens if the poly normal in the dominant
|
||||
@@ -2782,7 +2783,7 @@ static void init_face_merge_state(FaceMergeState *fms,
|
||||
std::cout << "process tri = " << &tri << "\n";
|
||||
}
|
||||
BLI_assert(tri.plane_populated());
|
||||
if (dot(norm, tri.plane->norm) <= 0.0) {
|
||||
if (double3::dot(norm, tri.plane->norm) <= 0.0) {
|
||||
if (dbg_level > 0) {
|
||||
std::cout << "triangle has wrong orientation, skipping\n";
|
||||
}
|
||||
@@ -2807,7 +2808,7 @@ static void init_face_merge_state(FaceMergeState *fms,
|
||||
}
|
||||
if (me_index == -1) {
|
||||
double3 vec = new_me.v2->co - new_me.v1->co;
|
||||
new_me.len_squared = length_squared(vec);
|
||||
new_me.len_squared = vec.length_squared();
|
||||
new_me.orig = tri.edge_orig[i];
|
||||
new_me.is_intersect = tri.is_intersect[i];
|
||||
new_me.dissolvable = (new_me.orig == NO_INDEX && !new_me.is_intersect);
|
||||
@@ -3047,7 +3048,7 @@ static Vector<Face *> merge_tris_for_face(Vector<int> tris,
|
||||
bool done = false;
|
||||
double3 first_tri_normal = tm.face(tris[0])->plane->norm;
|
||||
double3 second_tri_normal = tm.face(tris[1])->plane->norm;
|
||||
if (tris.size() == 2 && dot(first_tri_normal, second_tri_normal) > 0.0) {
|
||||
if (tris.size() == 2 && double3::dot(first_tri_normal, second_tri_normal) > 0.0) {
|
||||
/* Is this a case where quad with one diagonal remained unchanged?
|
||||
* Worth special handling because this case will be very common. */
|
||||
Face &tri1 = *tm.face(tris[0]);
|
||||
|
@@ -193,14 +193,14 @@ void Face::populate_plane(bool need_exact)
|
||||
for (int i : index_range()) {
|
||||
co[i] = vert[i]->co_exact;
|
||||
}
|
||||
normal_exact = cross_poly(co);
|
||||
normal_exact = mpq3::cross_poly(co);
|
||||
}
|
||||
else {
|
||||
mpq3 tr02 = vert[0]->co_exact - vert[2]->co_exact;
|
||||
mpq3 tr12 = vert[1]->co_exact - vert[2]->co_exact;
|
||||
normal_exact = cross(tr02, tr12);
|
||||
normal_exact = mpq3::cross(tr02, tr12);
|
||||
}
|
||||
mpq_class d_exact = -dot(normal_exact, vert[0]->co_exact);
|
||||
mpq_class d_exact = -mpq3::dot(normal_exact, vert[0]->co_exact);
|
||||
plane = new Plane(normal_exact, d_exact);
|
||||
}
|
||||
else {
|
||||
@@ -210,14 +210,14 @@ void Face::populate_plane(bool need_exact)
|
||||
for (int i : index_range()) {
|
||||
co[i] = vert[i]->co;
|
||||
}
|
||||
normal = cross_poly(co);
|
||||
normal = double3::cross_poly(co);
|
||||
}
|
||||
else {
|
||||
double3 tr02 = vert[0]->co - vert[2]->co;
|
||||
double3 tr12 = vert[1]->co - vert[2]->co;
|
||||
normal = cross_high_precision(tr02, tr12);
|
||||
normal = double3::cross_high_precision(tr02, tr12);
|
||||
}
|
||||
double d = -dot(normal, vert[0]->co);
|
||||
double d = -double3::dot(normal, vert[0]->co);
|
||||
plane = new Plane(normal, d);
|
||||
}
|
||||
}
|
||||
@@ -1174,15 +1174,15 @@ static mpq2 project_3d_to_2d(const mpq3 &p3d, int proj_axis)
|
||||
*/
|
||||
static double supremum_dot_cross(const double3 &a, const double3 &b)
|
||||
{
|
||||
double3 abs_a = abs(a);
|
||||
double3 abs_b = abs(b);
|
||||
double3 abs_a = double3::abs(a);
|
||||
double3 abs_b = double3::abs(b);
|
||||
double3 c;
|
||||
/* This is dot(cross(a, b), cross(a,b)) but using absolute values for a and b
|
||||
* and always using + when operation is + or -. */
|
||||
c[0] = abs_a[1] * abs_b[2] + abs_a[2] * abs_b[1];
|
||||
c[1] = abs_a[2] * abs_b[0] + abs_a[0] * abs_b[2];
|
||||
c[2] = abs_a[0] * abs_b[1] + abs_a[1] * abs_b[0];
|
||||
return dot(c, c);
|
||||
return double3::dot(c, c);
|
||||
}
|
||||
|
||||
/* The index of dot when inputs are plane_coords with index 1 is much higher.
|
||||
@@ -1219,11 +1219,11 @@ static int filter_plane_side(const double3 &p,
|
||||
const double3 &abs_plane_p,
|
||||
const double3 &abs_plane_no)
|
||||
{
|
||||
double d = dot(p - plane_p, plane_no);
|
||||
double d = double3::dot(p - plane_p, plane_no);
|
||||
if (d == 0.0) {
|
||||
return 0;
|
||||
}
|
||||
double supremum = dot(abs_p + abs_plane_p, abs_plane_no);
|
||||
double supremum = double3::dot(abs_p + abs_plane_p, abs_plane_no);
|
||||
double err_bound = supremum * index_plane_side * DBL_EPSILON;
|
||||
if (fabs(d) > err_bound) {
|
||||
return d > 0 ? 1 : -1;
|
||||
@@ -1248,9 +1248,9 @@ static int filter_plane_side(const double3 &p,
|
||||
static inline mpq3 tti_interp(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &n)
|
||||
{
|
||||
mpq3 ab = a - b;
|
||||
mpq_class den = dot(ab, n);
|
||||
mpq_class den = mpq3::dot(ab, n);
|
||||
BLI_assert(den != 0);
|
||||
mpq_class alpha = dot(a - c, n) / den;
|
||||
mpq_class alpha = mpq3::dot(a - c, n) / den;
|
||||
return a - alpha * ab;
|
||||
}
|
||||
|
||||
@@ -1261,8 +1261,8 @@ static inline mpq3 tti_interp(const mpq3 &a, const mpq3 &b, const mpq3 &c, const
|
||||
*/
|
||||
static inline int tti_above(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &ad)
|
||||
{
|
||||
mpq3 n = cross(b - a, c - a);
|
||||
return sgn(dot(ad, n));
|
||||
mpq3 n = mpq3::cross(b - a, c - a);
|
||||
return sgn(mpq3::dot(ad, n));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1480,11 +1480,11 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2)
|
||||
const double3 &d_r2 = vr2->co;
|
||||
const double3 &d_n2 = tri2.plane->norm;
|
||||
|
||||
const double3 &abs_d_p1 = abs(d_p1);
|
||||
const double3 &abs_d_q1 = abs(d_q1);
|
||||
const double3 &abs_d_r1 = abs(d_r1);
|
||||
const double3 &abs_d_r2 = abs(d_r2);
|
||||
const double3 &abs_d_n2 = abs(d_n2);
|
||||
const double3 &abs_d_p1 = double3::abs(d_p1);
|
||||
const double3 &abs_d_q1 = double3::abs(d_q1);
|
||||
const double3 &abs_d_r1 = double3::abs(d_r1);
|
||||
const double3 &abs_d_r2 = double3::abs(d_r2);
|
||||
const double3 &abs_d_n2 = double3::abs(d_n2);
|
||||
|
||||
int sp1 = filter_plane_side(d_p1, d_r2, d_n2, abs_d_p1, abs_d_r2, abs_d_n2);
|
||||
int sq1 = filter_plane_side(d_q1, d_r2, d_n2, abs_d_q1, abs_d_r2, abs_d_n2);
|
||||
@@ -1500,9 +1500,9 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2)
|
||||
}
|
||||
|
||||
const double3 &d_n1 = tri1.plane->norm;
|
||||
const double3 &abs_d_p2 = abs(d_p2);
|
||||
const double3 &abs_d_q2 = abs(d_q2);
|
||||
const double3 &abs_d_n1 = abs(d_n1);
|
||||
const double3 &abs_d_p2 = double3::abs(d_p2);
|
||||
const double3 &abs_d_q2 = double3::abs(d_q2);
|
||||
const double3 &abs_d_n1 = double3::abs(d_n1);
|
||||
|
||||
int sp2 = filter_plane_side(d_p2, d_r1, d_n1, abs_d_p2, abs_d_r1, abs_d_n1);
|
||||
int sq2 = filter_plane_side(d_q2, d_r1, d_n1, abs_d_q2, abs_d_r1, abs_d_n1);
|
||||
@@ -1526,13 +1526,13 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2)
|
||||
|
||||
const mpq3 &n2 = tri2.plane->norm_exact;
|
||||
if (sp1 == 0) {
|
||||
sp1 = sgn(dot(p1 - r2, n2));
|
||||
sp1 = sgn(mpq3::dot(p1 - r2, n2));
|
||||
}
|
||||
if (sq1 == 0) {
|
||||
sq1 = sgn(dot(q1 - r2, n2));
|
||||
sq1 = sgn(mpq3::dot(q1 - r2, n2));
|
||||
}
|
||||
if (sr1 == 0) {
|
||||
sr1 = sgn(dot(r1 - r2, n2));
|
||||
sr1 = sgn(mpq3::dot(r1 - r2, n2));
|
||||
}
|
||||
|
||||
if (dbg_level > 1) {
|
||||
@@ -1552,13 +1552,13 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2)
|
||||
/* Repeat for signs of t2's vertices with respect to plane of t1. */
|
||||
const mpq3 &n1 = tri1.plane->norm_exact;
|
||||
if (sp2 == 0) {
|
||||
sp2 = sgn(dot(p2 - r1, n1));
|
||||
sp2 = sgn(mpq3::dot(p2 - r1, n1));
|
||||
}
|
||||
if (sq2 == 0) {
|
||||
sq2 = sgn(dot(q2 - r1, n1));
|
||||
sq2 = sgn(mpq3::dot(q2 - r1, n1));
|
||||
}
|
||||
if (sr2 == 0) {
|
||||
sr2 = sgn(dot(r2 - r1, n1));
|
||||
sr2 = sgn(mpq3::dot(r2 - r1, n1));
|
||||
}
|
||||
|
||||
if (dbg_level > 1) {
|
||||
@@ -1757,7 +1757,7 @@ static CDT_data prepare_cdt_input(const IMesh &tm, int t, const Vector<ITT_value
|
||||
BLI_assert(tm.face(t)->plane_populated());
|
||||
ans.t_plane = tm.face(t)->plane;
|
||||
BLI_assert(ans.t_plane->exact_populated());
|
||||
ans.proj_axis = dominant_axis(ans.t_plane->norm_exact);
|
||||
ans.proj_axis = mpq3::dominant_axis(ans.t_plane->norm_exact);
|
||||
prepare_need_tri(ans, tm, t);
|
||||
for (const ITT_value &itt : itts) {
|
||||
switch (itt.kind) {
|
||||
@@ -1793,7 +1793,7 @@ static CDT_data prepare_cdt_input_for_cluster(const IMesh &tm,
|
||||
BLI_assert(tm.face(t0)->plane_populated());
|
||||
ans.t_plane = tm.face(t0)->plane;
|
||||
BLI_assert(ans.t_plane->exact_populated());
|
||||
ans.proj_axis = dominant_axis(ans.t_plane->norm_exact);
|
||||
ans.proj_axis = mpq3::dominant_axis(ans.t_plane->norm_exact);
|
||||
for (const int t : cl) {
|
||||
prepare_need_tri(ans, tm, t);
|
||||
}
|
||||
@@ -2535,15 +2535,15 @@ static bool face_is_degenerate(const Face *f)
|
||||
}
|
||||
double3 da = v2->co - v0->co;
|
||||
double3 db = v2->co - v1->co;
|
||||
double3 dab = cross_high_precision(da, db);
|
||||
double dab_length_squared = length_squared(dab);
|
||||
double3 dab = double3::cross_high_precision(da, db);
|
||||
double dab_length_squared = dab.length_squared();
|
||||
double err_bound = supremum_dot_cross(dab, dab) * index_dot_cross * DBL_EPSILON;
|
||||
if (dab_length_squared > err_bound) {
|
||||
return false;
|
||||
}
|
||||
mpq3 a = v2->co_exact - v0->co_exact;
|
||||
mpq3 b = v2->co_exact - v1->co_exact;
|
||||
mpq3 ab = cross(a, b);
|
||||
mpq3 ab = mpq3::cross(a, b);
|
||||
if (ab.x == 0 && ab.y == 0 && ab.z == 0) {
|
||||
return true;
|
||||
}
|
||||
|
@@ -71,6 +71,8 @@ set(SRC
|
||||
intern/COM_MemoryBuffer.h
|
||||
intern/COM_MemoryProxy.cpp
|
||||
intern/COM_MemoryProxy.h
|
||||
intern/COM_MetaData.cpp
|
||||
intern/COM_MetaData.h
|
||||
intern/COM_Node.cpp
|
||||
intern/COM_Node.h
|
||||
intern/COM_NodeConverter.cpp
|
||||
|
71
source/blender/compositor/intern/COM_MetaData.cpp
Normal file
71
source/blender/compositor/intern/COM_MetaData.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
#include "COM_MetaData.h"
|
||||
|
||||
#include "BKE_cryptomatte.hh"
|
||||
#include "BKE_image.h"
|
||||
|
||||
#include "RE_pipeline.h"
|
||||
|
||||
#include <string_view>
|
||||
|
||||
void MetaData::add(const blender::StringRef key, const blender::StringRef value)
|
||||
{
|
||||
entries_.add(key, value);
|
||||
}
|
||||
|
||||
void MetaData::addCryptomatteEntry(const blender::StringRef layer_name,
|
||||
const blender::StringRefNull key,
|
||||
const blender::StringRef value)
|
||||
{
|
||||
add(blender::BKE_cryptomatte_meta_data_key(layer_name, key), value);
|
||||
}
|
||||
|
||||
/* Replace the hash neutral cryptomatte keys with hashed versions.
|
||||
*
|
||||
* When a conversion happens it will also add the cryptomatte name key with the given
|
||||
* `layer_name`.*/
|
||||
void MetaData::replaceHashNeutralCryptomatteKeys(const blender::StringRef layer_name)
|
||||
{
|
||||
std::string cryptomatte_hash = entries_.pop_default(META_DATA_KEY_CRYPTOMATTE_HASH, "");
|
||||
std::string cryptomatte_conversion = entries_.pop_default(META_DATA_KEY_CRYPTOMATTE_CONVERSION,
|
||||
"");
|
||||
std::string cryptomatte_manifest = entries_.pop_default(META_DATA_KEY_CRYPTOMATTE_MANIFEST, "");
|
||||
|
||||
if (cryptomatte_hash.length() || cryptomatte_conversion.length() ||
|
||||
cryptomatte_manifest.length()) {
|
||||
addCryptomatteEntry(layer_name, "name", layer_name);
|
||||
}
|
||||
if (cryptomatte_hash.length()) {
|
||||
addCryptomatteEntry(layer_name, "hash", cryptomatte_hash);
|
||||
}
|
||||
if (cryptomatte_conversion.length()) {
|
||||
addCryptomatteEntry(layer_name, "conversion", cryptomatte_conversion);
|
||||
}
|
||||
if (cryptomatte_manifest.length()) {
|
||||
addCryptomatteEntry(layer_name, "manifest", cryptomatte_manifest);
|
||||
}
|
||||
}
|
||||
|
||||
void MetaData::addToRenderResult(RenderResult *render_result) const
|
||||
{
|
||||
for (blender::Map<std::string, std::string>::Item entry : entries_.items()) {
|
||||
BKE_render_result_stamp_data(render_result, entry.key.c_str(), entry.value.c_str());
|
||||
}
|
||||
}
|
56
source/blender/compositor/intern/COM_MetaData.h
Normal file
56
source/blender/compositor/intern/COM_MetaData.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "BLI_map.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
/* Forward declarations. */
|
||||
struct StampData;
|
||||
struct RenderResult;
|
||||
|
||||
/* Cryptomatte includes hash in its meta data keys. The hash is generated from the render
|
||||
* layer/pass name. Compositing happens without the knowledge of the original layer and pass. The
|
||||
* next keys are used to transfer the cryptomatte meta data in a neutral way. The file output node
|
||||
* will generate a hash based on the layer name configured by the user.
|
||||
*
|
||||
* The `{hash}` has no special meaning except to make sure that the meta data stays unique. */
|
||||
constexpr blender::StringRef META_DATA_KEY_CRYPTOMATTE_HASH("cryptomatte/{hash}/hash");
|
||||
constexpr blender::StringRef META_DATA_KEY_CRYPTOMATTE_CONVERSION("cryptomatte/{hash}/conversion");
|
||||
constexpr blender::StringRef META_DATA_KEY_CRYPTOMATTE_MANIFEST("cryptomatte/{hash}/manifest");
|
||||
constexpr blender::StringRef META_DATA_KEY_CRYPTOMATTE_NAME("cryptomatte/{hash}/name");
|
||||
|
||||
class MetaData {
|
||||
private:
|
||||
blender::Map<std::string, std::string> entries_;
|
||||
void addCryptomatteEntry(const blender::StringRef layer_name,
|
||||
const blender::StringRefNull key,
|
||||
const blender::StringRef value);
|
||||
|
||||
public:
|
||||
void add(const blender::StringRef key, const blender::StringRef value);
|
||||
void replaceHashNeutralCryptomatteKeys(const blender::StringRef layer_name);
|
||||
void addToRenderResult(RenderResult *render_result) const;
|
||||
#ifdef WITH_CXX_GUARDEDALLOC
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("COM:MetaData")
|
||||
#endif
|
||||
};
|
@@ -19,8 +19,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "BLI_rect.h"
|
||||
#include "COM_MetaData.h"
|
||||
#include "COM_defines.h"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#ifdef WITH_CXX_GUARDEDALLOC
|
||||
# include "MEM_guardedalloc.h"
|
||||
#endif
|
||||
@@ -32,6 +36,7 @@ typedef enum PixelSampler {
|
||||
} PixelSampler;
|
||||
|
||||
class MemoryBuffer;
|
||||
|
||||
/**
|
||||
* \brief Helper class for reading socket data.
|
||||
* Only use this class for dispatching (un-ary and n-ary) executions.
|
||||
@@ -134,6 +139,14 @@ class SocketReader {
|
||||
return this->m_height;
|
||||
}
|
||||
|
||||
/* Return the meta data associated with this branch.
|
||||
*
|
||||
* The return parameter holds an instance or is an nullptr. */
|
||||
virtual std::unique_ptr<MetaData> getMetaData() const
|
||||
{
|
||||
return std::unique_ptr<MetaData>();
|
||||
}
|
||||
|
||||
#ifdef WITH_CXX_GUARDEDALLOC
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("COM:SocketReader")
|
||||
#endif
|
||||
|
@@ -50,7 +50,8 @@ void OutputFileNode::convertToOperations(NodeConverter &converter,
|
||||
OutputOpenExrMultiLayerOperation *outputOperation;
|
||||
|
||||
if (is_multiview && storage->format.views_format == R_IMF_VIEWS_MULTIVIEW) {
|
||||
outputOperation = new OutputOpenExrMultiLayerMultiViewOperation(context.getRenderData(),
|
||||
outputOperation = new OutputOpenExrMultiLayerMultiViewOperation(context.getScene(),
|
||||
context.getRenderData(),
|
||||
context.getbNodeTree(),
|
||||
storage->base_path,
|
||||
storage->format.exr_codec,
|
||||
@@ -58,7 +59,8 @@ void OutputFileNode::convertToOperations(NodeConverter &converter,
|
||||
context.getViewName());
|
||||
}
|
||||
else {
|
||||
outputOperation = new OutputOpenExrMultiLayerOperation(context.getRenderData(),
|
||||
outputOperation = new OutputOpenExrMultiLayerOperation(context.getScene(),
|
||||
context.getRenderData(),
|
||||
context.getbNodeTree(),
|
||||
storage->base_path,
|
||||
storage->format.exr_codec,
|
||||
|
@@ -143,13 +143,14 @@ void OutputOpenExrSingleLayerMultiViewOperation::deinitExecution()
|
||||
/************************************ OpenEXR Multilayer Multiview *******************************/
|
||||
|
||||
OutputOpenExrMultiLayerMultiViewOperation::OutputOpenExrMultiLayerMultiViewOperation(
|
||||
const Scene *scene,
|
||||
const RenderData *rd,
|
||||
const bNodeTree *tree,
|
||||
const char *path,
|
||||
char exr_codec,
|
||||
bool exr_half_float,
|
||||
const char *viewName)
|
||||
: OutputOpenExrMultiLayerOperation(rd, tree, path, exr_codec, exr_half_float, viewName)
|
||||
: OutputOpenExrMultiLayerOperation(scene, rd, tree, path, exr_codec, exr_half_float, viewName)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -195,12 +196,16 @@ void *OutputOpenExrMultiLayerMultiViewOperation::get_handle(const char *filename
|
||||
BLI_make_existing_file(filename);
|
||||
|
||||
/* prepare the file with all the channels for the header */
|
||||
if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec, nullptr) == 0) {
|
||||
StampData *stamp_data = createStampData();
|
||||
if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec, stamp_data) ==
|
||||
0) {
|
||||
printf("Error Writing Multilayer Multiview Openexr\n");
|
||||
IMB_exr_close(exrhandle);
|
||||
BKE_stamp_data_free(stamp_data);
|
||||
}
|
||||
else {
|
||||
IMB_exr_clear_channels(exrhandle);
|
||||
BKE_stamp_data_free(stamp_data);
|
||||
return exrhandle;
|
||||
}
|
||||
}
|
||||
|
@@ -48,7 +48,8 @@ class OutputOpenExrSingleLayerMultiViewOperation : public OutputSingleLayerOpera
|
||||
class OutputOpenExrMultiLayerMultiViewOperation : public OutputOpenExrMultiLayerOperation {
|
||||
private:
|
||||
public:
|
||||
OutputOpenExrMultiLayerMultiViewOperation(const RenderData *rd,
|
||||
OutputOpenExrMultiLayerMultiViewOperation(const Scene *scene,
|
||||
const RenderData *rd,
|
||||
const bNodeTree *tree,
|
||||
const char *path,
|
||||
char exr_codec,
|
||||
|
@@ -18,12 +18,15 @@
|
||||
|
||||
#include "COM_OutputFileOperation.h"
|
||||
|
||||
#include "COM_MetaData.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "BKE_cryptomatte.hh"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_main.h"
|
||||
@@ -36,6 +39,8 @@
|
||||
#include "IMB_imbuf.h"
|
||||
#include "IMB_imbuf_types.h"
|
||||
|
||||
#include "RE_pipeline.h"
|
||||
|
||||
void add_exr_channels(void *exrhandle,
|
||||
const char *layerName,
|
||||
const DataType datatype,
|
||||
@@ -299,13 +304,15 @@ OutputOpenExrLayer::OutputOpenExrLayer(const char *name_, DataType datatype_, bo
|
||||
this->imageInput = nullptr;
|
||||
}
|
||||
|
||||
OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation(const RenderData *rd,
|
||||
OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation(const Scene *scene,
|
||||
const RenderData *rd,
|
||||
const bNodeTree *tree,
|
||||
const char *path,
|
||||
char exr_codec,
|
||||
bool exr_half_float,
|
||||
const char *viewName)
|
||||
{
|
||||
this->m_scene = scene;
|
||||
this->m_rd = rd;
|
||||
this->m_tree = tree;
|
||||
|
||||
@@ -323,6 +330,26 @@ void OutputOpenExrMultiLayerOperation::add_layer(const char *name,
|
||||
this->m_layers.push_back(OutputOpenExrLayer(name, datatype, use_layer));
|
||||
}
|
||||
|
||||
StampData *OutputOpenExrMultiLayerOperation::createStampData() const
|
||||
{
|
||||
/* StampData API doesn't provide functions to modify an instance without having a RenderResult.
|
||||
*/
|
||||
RenderResult render_result;
|
||||
StampData *stamp_data = BKE_stamp_info_from_scene_static(m_scene);
|
||||
render_result.stamp_data = stamp_data;
|
||||
for (int i = 0; i < this->m_layers.size(); i++) {
|
||||
const OutputOpenExrLayer *layer = &this->m_layers[i];
|
||||
std::unique_ptr<MetaData> meta_data = layer->imageInput->getMetaData();
|
||||
if (meta_data) {
|
||||
blender::StringRef layer_name = blender::BKE_cryptomatte_extract_layer_name(
|
||||
blender::StringRef(layer->name, BLI_strnlen(layer->name, sizeof(layer->name))));
|
||||
meta_data->replaceHashNeutralCryptomatteKeys(layer_name);
|
||||
meta_data->addToRenderResult(&render_result);
|
||||
}
|
||||
}
|
||||
return stamp_data;
|
||||
}
|
||||
|
||||
void OutputOpenExrMultiLayerOperation::initExecution()
|
||||
{
|
||||
for (unsigned int i = 0; i < this->m_layers.size(); i++) {
|
||||
@@ -386,7 +413,8 @@ void OutputOpenExrMultiLayerOperation::deinitExecution()
|
||||
}
|
||||
|
||||
/* when the filename has no permissions, this can fail */
|
||||
if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec, nullptr)) {
|
||||
StampData *stamp_data = createStampData();
|
||||
if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec, stamp_data)) {
|
||||
IMB_exr_write_channels(exrhandle);
|
||||
}
|
||||
else {
|
||||
@@ -404,5 +432,6 @@ void OutputOpenExrMultiLayerOperation::deinitExecution()
|
||||
|
||||
this->m_layers[i].imageInput = nullptr;
|
||||
}
|
||||
BKE_stamp_data_free(stamp_data);
|
||||
}
|
||||
}
|
||||
|
@@ -91,6 +91,7 @@ class OutputOpenExrMultiLayerOperation : public NodeOperation {
|
||||
protected:
|
||||
typedef std::vector<OutputOpenExrLayer> LayerList;
|
||||
|
||||
const Scene *m_scene;
|
||||
const RenderData *m_rd;
|
||||
const bNodeTree *m_tree;
|
||||
|
||||
@@ -100,8 +101,11 @@ class OutputOpenExrMultiLayerOperation : public NodeOperation {
|
||||
LayerList m_layers;
|
||||
const char *m_viewName;
|
||||
|
||||
StampData *createStampData() const;
|
||||
|
||||
public:
|
||||
OutputOpenExrMultiLayerOperation(const RenderData *rd,
|
||||
OutputOpenExrMultiLayerOperation(const Scene *scene,
|
||||
const RenderData *rd,
|
||||
const bNodeTree *tree,
|
||||
const char *path,
|
||||
char exr_codec,
|
||||
|
@@ -18,8 +18,16 @@
|
||||
|
||||
#include "COM_RenderLayersProg.h"
|
||||
|
||||
#include "COM_MetaData.h"
|
||||
|
||||
#include "BKE_cryptomatte.hh"
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_scene.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "RE_pipeline.h"
|
||||
@@ -209,6 +217,82 @@ void RenderLayersProg::determineResolution(unsigned int resolution[2],
|
||||
}
|
||||
}
|
||||
|
||||
struct CallbackData {
|
||||
std::unique_ptr<MetaData> meta_data;
|
||||
std::string hash_key;
|
||||
std::string conversion_key;
|
||||
std::string manifest_key;
|
||||
|
||||
void addMetaData(blender::StringRef key, blender::StringRefNull value)
|
||||
{
|
||||
if (!meta_data) {
|
||||
meta_data = std::make_unique<MetaData>();
|
||||
}
|
||||
meta_data->add(key, value);
|
||||
}
|
||||
|
||||
void setCryptomatteKeys(blender::StringRef cryptomatte_layer_name)
|
||||
{
|
||||
manifest_key = blender::BKE_cryptomatte_meta_data_key(cryptomatte_layer_name, "manifest");
|
||||
hash_key = blender::BKE_cryptomatte_meta_data_key(cryptomatte_layer_name, "hash");
|
||||
conversion_key = blender::BKE_cryptomatte_meta_data_key(cryptomatte_layer_name, "conversion");
|
||||
}
|
||||
};
|
||||
|
||||
/* C type callback function (StampCallback). */
|
||||
static void extract_cryptomatte_meta_data(void *_data,
|
||||
const char *propname,
|
||||
char *propvalue,
|
||||
int UNUSED(len))
|
||||
{
|
||||
CallbackData *data = static_cast<CallbackData *>(_data);
|
||||
blender::StringRefNull key(propname);
|
||||
if (key == data->hash_key) {
|
||||
data->addMetaData(META_DATA_KEY_CRYPTOMATTE_HASH, propvalue);
|
||||
}
|
||||
else if (key == data->conversion_key) {
|
||||
data->addMetaData(META_DATA_KEY_CRYPTOMATTE_CONVERSION, propvalue);
|
||||
}
|
||||
else if (key == data->manifest_key) {
|
||||
data->addMetaData(META_DATA_KEY_CRYPTOMATTE_MANIFEST, propvalue);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<MetaData> RenderLayersProg::getMetaData() const
|
||||
{
|
||||
Scene *scene = this->getScene();
|
||||
Render *re = (scene) ? RE_GetSceneRender(scene) : nullptr;
|
||||
RenderResult *rr = nullptr;
|
||||
CallbackData callback_data = {nullptr};
|
||||
|
||||
if (re) {
|
||||
rr = RE_AcquireResultRead(re);
|
||||
}
|
||||
|
||||
if (rr && rr->stamp_data) {
|
||||
ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, getLayerId());
|
||||
if (view_layer) {
|
||||
std::string full_layer_name = std::string(
|
||||
view_layer->name,
|
||||
BLI_strnlen(view_layer->name, sizeof(view_layer->name))) +
|
||||
"." + m_passName;
|
||||
blender::StringRef cryptomatte_layer_name = blender::BKE_cryptomatte_extract_layer_name(
|
||||
full_layer_name);
|
||||
callback_data.setCryptomatteKeys(cryptomatte_layer_name);
|
||||
|
||||
BKE_stamp_info_callback(
|
||||
&callback_data, rr->stamp_data, extract_cryptomatte_meta_data, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (re) {
|
||||
RE_ReleaseResult(re);
|
||||
re = nullptr;
|
||||
}
|
||||
|
||||
return std::move(callback_data.meta_data);
|
||||
}
|
||||
|
||||
/* ******** Render Layers AO Operation ******** */
|
||||
void RenderLayersAOOperation::executePixelSampled(float output[4],
|
||||
float x,
|
||||
|
@@ -121,6 +121,8 @@ class RenderLayersProg : public NodeOperation {
|
||||
void initExecution();
|
||||
void deinitExecution();
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler);
|
||||
|
||||
std::unique_ptr<MetaData> getMetaData() const override;
|
||||
};
|
||||
|
||||
class RenderLayersAOOperation : public RenderLayersProg {
|
||||
|
@@ -24,3 +24,8 @@ SocketProxyOperation::SocketProxyOperation(DataType type, bool use_conversion)
|
||||
this->addInputSocket(type);
|
||||
this->addOutputSocket(type);
|
||||
}
|
||||
|
||||
std::unique_ptr<MetaData> SocketProxyOperation::getMetaData() const
|
||||
{
|
||||
return this->getInputSocket(0)->getReader()->getMetaData();
|
||||
}
|
||||
|
@@ -41,6 +41,7 @@ class SocketProxyOperation : public NodeOperation {
|
||||
{
|
||||
m_use_conversion = use_conversion;
|
||||
}
|
||||
std::unique_ptr<MetaData> getMetaData() const override;
|
||||
|
||||
private:
|
||||
bool m_use_conversion;
|
||||
|
@@ -83,6 +83,7 @@
|
||||
#include "BKE_key.h"
|
||||
#include "BKE_lattice.h"
|
||||
#include "BKE_layer.h"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_light.h"
|
||||
#include "BKE_mask.h"
|
||||
#include "BKE_material.h"
|
||||
@@ -152,12 +153,14 @@ DepsgraphNodeBuilder::~DepsgraphNodeBuilder()
|
||||
|
||||
IDNode *DepsgraphNodeBuilder::add_id_node(ID *id)
|
||||
{
|
||||
BLI_assert(id->session_uuid != MAIN_ID_SESSION_UUID_UNSET);
|
||||
|
||||
IDNode *id_node = nullptr;
|
||||
ID *id_cow = nullptr;
|
||||
IDComponentsMask previously_visible_components_mask = 0;
|
||||
uint32_t previous_eval_flags = 0;
|
||||
DEGCustomDataMeshMasks previous_customdata_masks;
|
||||
IDInfo *id_info = id_info_hash_.lookup_default(id, nullptr);
|
||||
IDInfo *id_info = id_info_hash_.lookup_default(id->session_uuid, nullptr);
|
||||
if (id_info != nullptr) {
|
||||
id_cow = id_info->id_cow;
|
||||
previously_visible_components_mask = id_info->previously_visible_components_mask;
|
||||
@@ -334,7 +337,8 @@ void DepsgraphNodeBuilder::begin_build()
|
||||
id_info->previously_visible_components_mask = id_node->visible_components_mask;
|
||||
id_info->previous_eval_flags = id_node->eval_flags;
|
||||
id_info->previous_customdata_masks = id_node->customdata_masks;
|
||||
id_info_hash_.add_new(id_node->id_orig, id_info);
|
||||
BLI_assert(!id_info_hash_.contains(id_node->id_orig_session_uuid));
|
||||
id_info_hash_.add_new(id_node->id_orig_session_uuid, id_info);
|
||||
id_node->id_cow = nullptr;
|
||||
}
|
||||
|
||||
|
@@ -285,8 +285,8 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
|
||||
* very root is visible (aka not restricted.). */
|
||||
bool is_parent_collection_visible_;
|
||||
|
||||
/* Indexed by original ID, values are IDInfo. */
|
||||
Map<const ID *, IDInfo *> id_info_hash_;
|
||||
/* Indexed by original ID.session_uuid, values are IDInfo. */
|
||||
Map<uint, IDInfo *> id_info_hash_;
|
||||
|
||||
/* Set of IDs which were already build. Makes it easier to keep track of
|
||||
* what was already built and what was not. */
|
||||
|
@@ -80,6 +80,7 @@ void IDNode::init(const ID *id, const char *UNUSED(subdata))
|
||||
/* Store ID-pointer. */
|
||||
id_type = GS(id->name);
|
||||
id_orig = (ID *)id;
|
||||
id_orig_session_uuid = id->session_uuid;
|
||||
eval_flags = 0;
|
||||
previous_eval_flags = 0;
|
||||
customdata_masks = DEGCustomDataMeshMasks();
|
||||
|
@@ -74,12 +74,22 @@ struct IDNode : public Node {
|
||||
|
||||
IDComponentsMask get_visible_components_mask() const;
|
||||
|
||||
/* ID Block referenced. */
|
||||
/* Type of the ID stored separately, so it's possible to perform check whether CoW is needed
|
||||
* without de-referencing the id_cow (which is not safe when ID is NOT covered by CoW and has
|
||||
* been deleted from the main database.) */
|
||||
ID_Type id_type;
|
||||
|
||||
/* ID Block referenced. */
|
||||
ID *id_orig;
|
||||
|
||||
/* Session-wide UUID of the id_orig.
|
||||
* Is used on relations update to map evaluated state from old nodes to the new ones, without
|
||||
* relying on pointers (which are not guaranteed to be unique) and without dereferencing id_orig
|
||||
* which could be "stale" pointer. */
|
||||
uint id_orig_session_uuid;
|
||||
|
||||
/* Evaluated datablock.
|
||||
* Will be covered by the copy-on-write system if the ID Type needs it. */
|
||||
ID *id_cow;
|
||||
|
||||
/* Hash to make it faster to look up components. */
|
||||
|
@@ -892,7 +892,7 @@ static void acf_group_name(bAnimListElem *ale, char *name)
|
||||
/* name property for group entries */
|
||||
static bool acf_group_name_prop(bAnimListElem *ale, PointerRNA *ptr, PropertyRNA **prop)
|
||||
{
|
||||
RNA_pointer_create(ale->id, &RNA_ActionGroup, ale->data, ptr);
|
||||
RNA_pointer_create(ale->fcurve_owner_id, &RNA_ActionGroup, ale->data, ptr);
|
||||
*prop = RNA_struct_name_property(ptr->type);
|
||||
|
||||
return (*prop != NULL);
|
||||
@@ -1013,7 +1013,7 @@ static bool acf_fcurve_name_prop(bAnimListElem *ale, PointerRNA *ptr, PropertyRN
|
||||
* as our "name" so that user can perform quick fixes
|
||||
*/
|
||||
if (fcu->flag & FCURVE_DISABLED) {
|
||||
RNA_pointer_create(ale->id, &RNA_FCurve, ale->data, ptr);
|
||||
RNA_pointer_create(ale->fcurve_owner_id, &RNA_FCurve, ale->data, ptr);
|
||||
*prop = RNA_struct_find_property(ptr, "data_path");
|
||||
}
|
||||
else {
|
||||
@@ -3965,7 +3965,7 @@ static void acf_nlaaction_name(bAnimListElem *ale, char *name)
|
||||
static bool acf_nlaaction_name_prop(bAnimListElem *ale, PointerRNA *ptr, PropertyRNA **prop)
|
||||
{
|
||||
if (ale->data) {
|
||||
RNA_pointer_create(ale->id, &RNA_Action, ale->data, ptr);
|
||||
RNA_pointer_create(ale->fcurve_owner_id, &RNA_Action, ale->data, ptr);
|
||||
*prop = RNA_struct_name_property(ptr->type);
|
||||
|
||||
return (*prop != NULL);
|
||||
|
@@ -5698,7 +5698,7 @@ static int curve_extrude_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
/* First test: curve? */
|
||||
if (obedit->type != OB_CURVE) {
|
||||
LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
|
||||
if ((nu->pntsv == 1) && (ED_curve_nurb_select_count(v3d, nu) == 1)) {
|
||||
if ((nu->pntsv == 1) && (ED_curve_nurb_select_count(v3d, nu) < nu->pntsu)) {
|
||||
as_curve = true;
|
||||
break;
|
||||
}
|
||||
|
@@ -645,6 +645,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES
|
||||
brush.sculpt.cloth
|
||||
brush.sculpt.crease
|
||||
brush.sculpt.displacement_eraser
|
||||
brush.sculpt.displacement_smear
|
||||
brush.sculpt.draw
|
||||
brush.sculpt.draw_face_sets
|
||||
brush.sculpt.draw_sharp
|
||||
|
@@ -148,10 +148,18 @@ static bool object_materials_supported_poll_ex(bContext *C, const Object *ob)
|
||||
if (!ED_operator_object_active_local_editable_ex(C, ob)) {
|
||||
return false;
|
||||
}
|
||||
if (!OB_TYPE_SUPPORT_MATERIAL(ob->type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Material linked to object. */
|
||||
if (ob->matbits && ob->actcol && ob->matbits[ob->actcol - 1]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Material linked to obdata. */
|
||||
const ID *data = ob->data;
|
||||
return (OB_TYPE_SUPPORT_MATERIAL(ob->type) &&
|
||||
/* Object data checks. */
|
||||
data && !ID_IS_LINKED(data) && !ID_IS_OVERRIDE_LIBRARY(data));
|
||||
return (data && !ID_IS_LINKED(data) && !ID_IS_OVERRIDE_LIBRARY(data));
|
||||
}
|
||||
|
||||
static bool object_materials_supported_poll(bContext *C)
|
||||
|
@@ -415,7 +415,12 @@ static float wpaint_undo_lock_relative(
|
||||
/* In auto-normalize mode, or when there is no unlocked weight,
|
||||
* compute based on locked weight. */
|
||||
if (auto_normalize || free_weight <= 0.0f) {
|
||||
weight *= (1.0f - locked_weight);
|
||||
if (locked_weight < 1.0f - VERTEX_WEIGHT_LOCK_EPSILON) {
|
||||
weight *= (1.0f - locked_weight);
|
||||
}
|
||||
else {
|
||||
weight = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* When dealing with full unlocked weight, don't paint, as it is always displayed as 1. */
|
||||
@@ -518,7 +523,7 @@ static bool do_weight_paint_normalize_all_locked(MDeformVert *dvert,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lock_weight >= 1.0f) {
|
||||
if (lock_weight >= 1.0f - VERTEX_WEIGHT_LOCK_EPSILON) {
|
||||
/* locked groups make it impossible to fully normalize,
|
||||
* zero out what we can and return false */
|
||||
for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
|
||||
@@ -779,7 +784,25 @@ static void do_weight_paint_vertex_single(
|
||||
index_mirr = vgroup_mirr = -1;
|
||||
}
|
||||
|
||||
if (wp->flag & VP_FLAG_VGROUP_RESTRICT) {
|
||||
/* Check if painting should create new deform weight entries. */
|
||||
bool restrict_to_existing = (wp->flag & VP_FLAG_VGROUP_RESTRICT) != 0;
|
||||
|
||||
if (wpi->do_lock_relative || wpi->do_auto_normalize) {
|
||||
/* Without do_lock_relative only dw_rel_locked is reliable, while dw_rel_free may be fake 0. */
|
||||
dw_rel_free = BKE_defvert_total_selected_weight(dv, wpi->defbase_tot, wpi->vgroup_unlocked);
|
||||
dw_rel_locked = BKE_defvert_total_selected_weight(dv, wpi->defbase_tot, wpi->vgroup_locked);
|
||||
CLAMP(dw_rel_locked, 0.0f, 1.0f);
|
||||
|
||||
/* Do not create entries if there is not enough free weight to paint.
|
||||
* This logic is the same as in wpaint_undo_lock_relative and auto-normalize. */
|
||||
if (wpi->do_auto_normalize || dw_rel_free <= 0.0f) {
|
||||
if (dw_rel_locked >= 1.0f - VERTEX_WEIGHT_LOCK_EPSILON) {
|
||||
restrict_to_existing = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (restrict_to_existing) {
|
||||
dw = BKE_defvert_find_index(dv, wpi->active.index);
|
||||
}
|
||||
else {
|
||||
@@ -827,10 +850,6 @@ static void do_weight_paint_vertex_single(
|
||||
|
||||
/* Handle weight caught up in locked defgroups for Lock Relative. */
|
||||
if (wpi->do_lock_relative) {
|
||||
dw_rel_free = BKE_defvert_total_selected_weight(dv, wpi->defbase_tot, wpi->vgroup_unlocked);
|
||||
dw_rel_locked = BKE_defvert_total_selected_weight(dv, wpi->defbase_tot, wpi->vgroup_locked);
|
||||
CLAMP(dw_rel_locked, 0.0f, 1.0f);
|
||||
|
||||
weight_cur = BKE_defvert_calc_lock_relative_weight(weight_cur, dw_rel_locked, dw_rel_free);
|
||||
}
|
||||
|
||||
@@ -1658,6 +1677,10 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
|
||||
wpd->lock_flags, wpd->vgroup_validmap, wpd->active.index) &&
|
||||
(!wpd->do_multipaint || BKE_object_defgroup_check_lock_relative_multi(
|
||||
defbase_tot, wpd->lock_flags, defbase_sel, defbase_tot_sel))) {
|
||||
wpd->do_lock_relative = true;
|
||||
}
|
||||
|
||||
if (wpd->do_lock_relative || (ts->auto_normalize && wpd->lock_flags && !wpd->do_multipaint)) {
|
||||
bool *unlocked = MEM_dupallocN(wpd->vgroup_validmap);
|
||||
|
||||
if (wpd->lock_flags) {
|
||||
@@ -1668,7 +1691,6 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
|
||||
}
|
||||
|
||||
wpd->vgroup_unlocked = unlocked;
|
||||
wpd->do_lock_relative = true;
|
||||
}
|
||||
|
||||
if (wpd->do_multipaint && ts->auto_normalize) {
|
||||
@@ -2383,7 +2405,8 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
|
||||
wpi.vgroup_unlocked = wpd->vgroup_unlocked;
|
||||
wpi.do_flip = RNA_boolean_get(itemptr, "pen_flip");
|
||||
wpi.do_multipaint = wpd->do_multipaint;
|
||||
wpi.do_auto_normalize = ((ts->auto_normalize != 0) && (wpi.vgroup_validmap != NULL));
|
||||
wpi.do_auto_normalize = ((ts->auto_normalize != 0) && (wpi.vgroup_validmap != NULL) &&
|
||||
(wpi.do_multipaint || wpi.vgroup_validmap[wpi.active.index]));
|
||||
wpi.do_lock_relative = wpd->do_lock_relative;
|
||||
wpi.is_normalized = wpi.do_auto_normalize || wpi.do_lock_relative;
|
||||
wpi.brush_alpha_value = brush_alpha_value;
|
||||
|
@@ -145,7 +145,7 @@ static void solve_camera_freejob(void *scv)
|
||||
BKE_reportf(scj->reports,
|
||||
RPT_INFO,
|
||||
"Average re-projection error: %.2f px",
|
||||
tracking->reconstruction.error);
|
||||
BKE_tracking_get_active_reconstruction(tracking)->error);
|
||||
}
|
||||
|
||||
/* Set currently solved clip as active for scene. */
|
||||
|
@@ -181,7 +181,7 @@ static void graph_panel_properties(const bContext *C, Panel *panel)
|
||||
}
|
||||
|
||||
/* F-Curve pointer */
|
||||
RNA_pointer_create(ale->id, &RNA_FCurve, fcu, &fcu_ptr);
|
||||
RNA_pointer_create(ale->fcurve_owner_id, &RNA_FCurve, fcu, &fcu_ptr);
|
||||
|
||||
/* user-friendly 'name' for F-Curve */
|
||||
col = uiLayoutColumn(layout, false);
|
||||
@@ -366,7 +366,7 @@ static void graph_panel_key_properties(const bContext *C, Panel *panel)
|
||||
int unit = B_UNIT_NONE;
|
||||
|
||||
/* RNA pointer to keyframe, to allow editing */
|
||||
RNA_pointer_create(ale->id, &RNA_Keyframe, bezt, &bezt_ptr);
|
||||
RNA_pointer_create(ale->fcurve_owner_id, &RNA_Keyframe, bezt, &bezt_ptr);
|
||||
|
||||
/* get property that F-Curve affects, for some unit-conversion magic */
|
||||
RNA_id_pointer_create(ale->id, &id_ptr);
|
||||
|
@@ -3228,6 +3228,35 @@ static void node_geometry_buts_attribute_math(uiLayout *layout,
|
||||
uiItemR(layout, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("Type B"), ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_geometry_buts_attribute_vector_math(uiLayout *layout,
|
||||
bContext *UNUSED(C),
|
||||
PointerRNA *ptr)
|
||||
{
|
||||
bNode *node = (bNode *)ptr->data;
|
||||
NodeAttributeVectorMath *node_storage = (NodeAttributeVectorMath *)node->storage;
|
||||
|
||||
uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE);
|
||||
uiItemR(layout, ptr, "input_type_a", DEFAULT_FLAGS, IFACE_("Type A"), ICON_NONE);
|
||||
|
||||
/* These "use input b / c" checks are copied from the node's code. They could be deduplicated if
|
||||
* the drawing code was moved to the node's file. */
|
||||
if (!ELEM(node_storage->operation,
|
||||
NODE_VECTOR_MATH_NORMALIZE,
|
||||
NODE_VECTOR_MATH_FLOOR,
|
||||
NODE_VECTOR_MATH_CEIL,
|
||||
NODE_VECTOR_MATH_FRACTION,
|
||||
NODE_VECTOR_MATH_ABSOLUTE,
|
||||
NODE_VECTOR_MATH_SINE,
|
||||
NODE_VECTOR_MATH_COSINE,
|
||||
NODE_VECTOR_MATH_TANGENT,
|
||||
NODE_VECTOR_MATH_LENGTH)) {
|
||||
uiItemR(layout, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("Type B"), ICON_NONE);
|
||||
}
|
||||
if (ELEM(node_storage->operation, NODE_VECTOR_MATH_WRAP)) {
|
||||
uiItemR(layout, ptr, "input_type_c", DEFAULT_FLAGS, IFACE_("Type C"), ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
static void node_geometry_buts_point_instance(uiLayout *layout,
|
||||
bContext *UNUSED(C),
|
||||
PointerRNA *ptr)
|
||||
@@ -3290,6 +3319,16 @@ static void node_geometry_buts_rotate_points(uiLayout *layout,
|
||||
}
|
||||
}
|
||||
|
||||
static void node_geometry_buts_align_rotation_to_vector(uiLayout *layout,
|
||||
bContext *UNUSED(C),
|
||||
PointerRNA *ptr)
|
||||
{
|
||||
uiItemR(layout, ptr, "axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
|
||||
uiLayout *col = uiLayoutColumn(layout, false);
|
||||
uiItemR(col, ptr, "input_type_factor", DEFAULT_FLAGS, IFACE_("Factor"), ICON_NONE);
|
||||
uiItemR(col, ptr, "input_type_vector", DEFAULT_FLAGS, IFACE_("Vector"), ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_geometry_set_butfunc(bNodeType *ntype)
|
||||
{
|
||||
switch (ntype->type) {
|
||||
@@ -3320,6 +3359,9 @@ static void node_geometry_set_butfunc(bNodeType *ntype)
|
||||
case GEO_NODE_ATTRIBUTE_MIX:
|
||||
ntype->draw_buttons = node_geometry_buts_attribute_mix;
|
||||
break;
|
||||
case GEO_NODE_ATTRIBUTE_VECTOR_MATH:
|
||||
ntype->draw_buttons = node_geometry_buts_attribute_vector_math;
|
||||
break;
|
||||
case GEO_NODE_POINT_DISTRIBUTE:
|
||||
ntype->draw_buttons = node_geometry_buts_attribute_point_distribute;
|
||||
break;
|
||||
@@ -3329,6 +3371,9 @@ static void node_geometry_set_butfunc(bNodeType *ntype)
|
||||
case GEO_NODE_ROTATE_POINTS:
|
||||
ntype->draw_buttons = node_geometry_buts_rotate_points;
|
||||
break;
|
||||
case GEO_NODE_ALIGN_ROTATION_TO_VECTOR:
|
||||
ntype->draw_buttons = node_geometry_buts_align_rotation_to_vector;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -484,7 +484,7 @@ void recalcData_nla(TransInfo *t)
|
||||
for (track = tdn->nlt->next, n = 0; (track) && (n < delta); track = track->next, n++) {
|
||||
/* check if space in this track for the strip */
|
||||
if (BKE_nlatrack_has_space(track, strip->start, strip->end) &&
|
||||
!BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, tdn->nlt)) {
|
||||
!BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, track)) {
|
||||
/* move strip to this track */
|
||||
BLI_remlink(&tdn->nlt->strips, strip);
|
||||
BKE_nlatrack_add_strip(track, strip, is_liboverride);
|
||||
@@ -504,7 +504,7 @@ void recalcData_nla(TransInfo *t)
|
||||
for (track = tdn->nlt->prev, n = 0; (track) && (n < delta); track = track->prev, n++) {
|
||||
/* check if space in this track for the strip */
|
||||
if (BKE_nlatrack_has_space(track, strip->start, strip->end) &&
|
||||
!BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, tdn->nlt)) {
|
||||
!BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, track)) {
|
||||
/* move strip to this track */
|
||||
BLI_remlink(&tdn->nlt->strips, strip);
|
||||
BKE_nlatrack_add_strip(track, strip, is_liboverride);
|
||||
|
@@ -159,11 +159,6 @@ void GLBackend::platform_init()
|
||||
GPG.support_level = GPU_SUPPORT_LEVEL_LIMITED;
|
||||
}
|
||||
}
|
||||
if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_OFFICIAL)) {
|
||||
if (is_faulty_T82856_platform(version, renderer)) {
|
||||
GPG.support_level = GPU_SUPPORT_LEVEL_LIMITED;
|
||||
}
|
||||
}
|
||||
}
|
||||
GPG.create_key(GPG.support_level, vendor, renderer, version);
|
||||
GPG.create_gpu_name(vendor, renderer, version);
|
||||
|
@@ -48,9 +48,9 @@
|
||||
|
||||
typedef struct anim_index_entry {
|
||||
int frameno;
|
||||
unsigned long long seek_pos;
|
||||
unsigned long long seek_pos_dts;
|
||||
unsigned long long pts;
|
||||
uint64_t seek_pos;
|
||||
uint64_t seek_pos_dts;
|
||||
uint64_t pts;
|
||||
} anim_index_entry;
|
||||
|
||||
struct anim_index {
|
||||
@@ -77,28 +77,25 @@ typedef struct anim_index_builder {
|
||||
} anim_index_builder;
|
||||
|
||||
anim_index_builder *IMB_index_builder_create(const char *name);
|
||||
void IMB_index_builder_add_entry(anim_index_builder *fp,
|
||||
int frameno,
|
||||
unsigned long long seek_pos,
|
||||
unsigned long long seek_pos_dts,
|
||||
unsigned long long pts);
|
||||
void IMB_index_builder_add_entry(
|
||||
anim_index_builder *fp, int frameno, uint64_t seek_pos, uint64_t seek_pos_dts, uint64_t pts);
|
||||
|
||||
void IMB_index_builder_proc_frame(anim_index_builder *fp,
|
||||
unsigned char *buffer,
|
||||
int data_size,
|
||||
int frameno,
|
||||
unsigned long long seek_pos,
|
||||
unsigned long long seek_pos_dts,
|
||||
unsigned long long pts);
|
||||
uint64_t seek_pos,
|
||||
uint64_t seek_pos_dts,
|
||||
uint64_t pts);
|
||||
|
||||
void IMB_index_builder_finish(anim_index_builder *fp, int rollback);
|
||||
|
||||
struct anim_index *IMB_indexer_open(const char *name);
|
||||
unsigned long long IMB_indexer_get_seek_pos(struct anim_index *idx, int frame_index);
|
||||
unsigned long long IMB_indexer_get_seek_pos_dts(struct anim_index *idx, int frame_index);
|
||||
uint64_t IMB_indexer_get_seek_pos(struct anim_index *idx, int frame_index);
|
||||
uint64_t IMB_indexer_get_seek_pos_dts(struct anim_index *idx, int frame_index);
|
||||
|
||||
int IMB_indexer_get_frame_index(struct anim_index *idx, int frameno);
|
||||
unsigned long long IMB_indexer_get_pts(struct anim_index *idx, int frame_index);
|
||||
uint64_t IMB_indexer_get_pts(struct anim_index *idx, int frame_index);
|
||||
int IMB_indexer_get_duration(struct anim_index *idx);
|
||||
|
||||
int IMB_indexer_can_scan(struct anim_index *idx, int old_frame_index, int new_frame_index);
|
||||
|
@@ -892,13 +892,12 @@ static int ffmpeg_decode_video_frame(struct anim *anim)
|
||||
while ((rval = av_read_frame(anim->pFormatCtx, &anim->next_packet)) >= 0) {
|
||||
av_log(anim->pFormatCtx,
|
||||
AV_LOG_DEBUG,
|
||||
"%sREAD: strID=%d (VID: %d) dts=%lld pts=%lld "
|
||||
"%s\n",
|
||||
"%sREAD: strID=%d (VID: %d) dts=%" PRId64 " pts=%" PRId64 " %s\n",
|
||||
(anim->next_packet.stream_index == anim->videoStream) ? "->" : " ",
|
||||
anim->next_packet.stream_index,
|
||||
anim->videoStream,
|
||||
(anim->next_packet.dts == AV_NOPTS_VALUE) ? -1 : (long long int)anim->next_packet.dts,
|
||||
(anim->next_packet.pts == AV_NOPTS_VALUE) ? -1 : (long long int)anim->next_packet.pts,
|
||||
(anim->next_packet.dts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet.dts,
|
||||
(anim->next_packet.pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet.pts,
|
||||
(anim->next_packet.flags & AV_PKT_FLAG_KEY) ? " KEY" : "");
|
||||
if (anim->next_packet.stream_index == anim->videoStream) {
|
||||
anim->pFrameComplete = 0;
|
||||
@@ -911,12 +910,10 @@ static int ffmpeg_decode_video_frame(struct anim *anim)
|
||||
|
||||
av_log(anim->pFormatCtx,
|
||||
AV_LOG_DEBUG,
|
||||
" FRAME DONE: next_pts=%lld "
|
||||
"pkt_pts=%lld, guessed_pts=%lld\n",
|
||||
(anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (long long int)anim->pFrame->pts,
|
||||
(anim->pFrame->pkt_pts == AV_NOPTS_VALUE) ? -1 :
|
||||
(long long int)anim->pFrame->pkt_pts,
|
||||
(long long int)anim->next_pts);
|
||||
" FRAME DONE: next_pts=%" PRId64 " pkt_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n",
|
||||
(anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts,
|
||||
(anim->pFrame->pkt_pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pkt_pts,
|
||||
(int64_t)anim->next_pts);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -946,11 +943,11 @@ static int ffmpeg_decode_video_frame(struct anim *anim)
|
||||
|
||||
av_log(anim->pFormatCtx,
|
||||
AV_LOG_DEBUG,
|
||||
" FRAME DONE (after EOF): next_pts=%lld "
|
||||
"pkt_pts=%lld, guessed_pts=%lld\n",
|
||||
(anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (long long int)anim->pFrame->pts,
|
||||
(anim->pFrame->pkt_pts == AV_NOPTS_VALUE) ? -1 : (long long int)anim->pFrame->pkt_pts,
|
||||
(long long int)anim->next_pts);
|
||||
" FRAME DONE (after EOF): next_pts=%" PRId64 " pkt_pts=%" PRId64
|
||||
", guessed_pts=%" PRId64 "\n",
|
||||
(anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts,
|
||||
(anim->pFrame->pkt_pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pkt_pts,
|
||||
(int64_t)anim->next_pts);
|
||||
rval = 0;
|
||||
}
|
||||
}
|
||||
@@ -975,16 +972,16 @@ static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_sea
|
||||
|
||||
av_log(anim->pFormatCtx,
|
||||
AV_LOG_DEBUG,
|
||||
"SCAN start: considering pts=%lld in search of %lld\n",
|
||||
(long long int)anim->next_pts,
|
||||
(long long int)pts_to_search);
|
||||
"SCAN start: considering pts=%" PRId64 " in search of %" PRId64 "\n",
|
||||
(int64_t)anim->next_pts,
|
||||
(int64_t)pts_to_search);
|
||||
|
||||
while (count > 0 && anim->next_pts < pts_to_search) {
|
||||
av_log(anim->pFormatCtx,
|
||||
AV_LOG_DEBUG,
|
||||
" WHILE: pts=%lld in search of %lld\n",
|
||||
(long long int)anim->next_pts,
|
||||
(long long int)pts_to_search);
|
||||
" WHILE: pts=%" PRId64 " in search of %" PRId64 "\n",
|
||||
(int64_t)anim->next_pts,
|
||||
(int64_t)pts_to_search);
|
||||
if (!ffmpeg_decode_video_frame(anim)) {
|
||||
break;
|
||||
}
|
||||
@@ -994,9 +991,9 @@ static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_sea
|
||||
av_log(anim->pFormatCtx,
|
||||
AV_LOG_ERROR,
|
||||
"SCAN failed: completely lost in stream, "
|
||||
"bailing out at PTS=%lld, searching for PTS=%lld\n",
|
||||
(long long int)anim->next_pts,
|
||||
(long long int)pts_to_search);
|
||||
"bailing out at PTS=%" PRId64 ", searching for PTS=%" PRId64 "\n",
|
||||
(int64_t)anim->next_pts,
|
||||
(int64_t)pts_to_search);
|
||||
}
|
||||
if (anim->next_pts == pts_to_search) {
|
||||
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "SCAN HAPPY: we found our PTS!\n");
|
||||
@@ -1091,9 +1088,9 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ
|
||||
|
||||
av_log(anim->pFormatCtx,
|
||||
AV_LOG_DEBUG,
|
||||
"FETCH: looking for PTS=%lld "
|
||||
"(pts_timebase=%g, frame_rate=%g, st_time=%lld)\n",
|
||||
(long long int)pts_to_search,
|
||||
"FETCH: looking for PTS=%" PRId64 " (pts_timebase=%g, frame_rate=%g, st_time=%" PRId64
|
||||
")\n",
|
||||
(int64_t)pts_to_search,
|
||||
pts_time_base,
|
||||
frame_rate,
|
||||
st_time);
|
||||
@@ -1101,9 +1098,9 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ
|
||||
if (anim->last_frame && anim->last_pts <= pts_to_search && anim->next_pts > pts_to_search) {
|
||||
av_log(anim->pFormatCtx,
|
||||
AV_LOG_DEBUG,
|
||||
"FETCH: frame repeat: last: %lld next: %lld\n",
|
||||
(long long int)anim->last_pts,
|
||||
(long long int)anim->next_pts);
|
||||
"FETCH: frame repeat: last: %" PRId64 " next: %" PRId64 "\n",
|
||||
(int64_t)anim->last_pts,
|
||||
(int64_t)anim->next_pts);
|
||||
IMB_refImBuf(anim->last_frame);
|
||||
anim->curposition = position;
|
||||
return anim->last_frame;
|
||||
@@ -1128,13 +1125,13 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ
|
||||
int ret;
|
||||
|
||||
if (tc_index) {
|
||||
unsigned long long dts;
|
||||
uint64_t dts;
|
||||
|
||||
pos = IMB_indexer_get_seek_pos(tc_index, new_frame_index);
|
||||
dts = IMB_indexer_get_seek_pos_dts(tc_index, new_frame_index);
|
||||
|
||||
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek pos = %lld\n", pos);
|
||||
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek dts = %llu\n", dts);
|
||||
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek pos = %" PRId64 "\n", pos);
|
||||
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek dts = %" PRIu64 "\n", dts);
|
||||
|
||||
if (ffmpeg_seek_by_byte(anim->pFormatCtx)) {
|
||||
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using BYTE pos\n");
|
||||
@@ -1152,7 +1149,7 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ
|
||||
|
||||
av_log(anim->pFormatCtx,
|
||||
AV_LOG_DEBUG,
|
||||
"NO INDEX seek pos = %lld, st_time = %lld\n",
|
||||
"NO INDEX seek pos = %" PRId64 ", st_time = %" PRId64 "\n",
|
||||
pos,
|
||||
(st_time != AV_NOPTS_VALUE) ? st_time : 0);
|
||||
|
||||
@@ -1164,7 +1161,7 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ
|
||||
pos += st_time;
|
||||
}
|
||||
|
||||
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek pos = %lld\n", pos);
|
||||
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek pos = %" PRId64 "\n", pos);
|
||||
|
||||
ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD);
|
||||
}
|
||||
@@ -1173,11 +1170,11 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ
|
||||
av_log(anim->pFormatCtx,
|
||||
AV_LOG_ERROR,
|
||||
"FETCH: "
|
||||
"error while seeking to DTS = %lld "
|
||||
"(frameno = %d, PTS = %lld): errcode = %d\n",
|
||||
"error while seeking to DTS = %" PRId64 " (frameno = %d, PTS = %" PRId64
|
||||
"): errcode = %d\n",
|
||||
pos,
|
||||
position,
|
||||
(long long int)pts_to_search,
|
||||
(int64_t)pts_to_search,
|
||||
ret);
|
||||
}
|
||||
|
||||
|
@@ -99,25 +99,22 @@ anim_index_builder *IMB_index_builder_create(const char *name)
|
||||
return rv;
|
||||
}
|
||||
|
||||
void IMB_index_builder_add_entry(anim_index_builder *fp,
|
||||
int frameno,
|
||||
unsigned long long seek_pos,
|
||||
unsigned long long seek_pos_dts,
|
||||
unsigned long long pts)
|
||||
void IMB_index_builder_add_entry(
|
||||
anim_index_builder *fp, int frameno, uint64_t seek_pos, uint64_t seek_pos_dts, uint64_t pts)
|
||||
{
|
||||
fwrite(&frameno, sizeof(int), 1, fp->fp);
|
||||
fwrite(&seek_pos, sizeof(unsigned long long), 1, fp->fp);
|
||||
fwrite(&seek_pos_dts, sizeof(unsigned long long), 1, fp->fp);
|
||||
fwrite(&pts, sizeof(unsigned long long), 1, fp->fp);
|
||||
fwrite(&seek_pos, sizeof(uint64_t), 1, fp->fp);
|
||||
fwrite(&seek_pos_dts, sizeof(uint64_t), 1, fp->fp);
|
||||
fwrite(&pts, sizeof(uint64_t), 1, fp->fp);
|
||||
}
|
||||
|
||||
void IMB_index_builder_proc_frame(anim_index_builder *fp,
|
||||
unsigned char *buffer,
|
||||
uchar *buffer,
|
||||
int data_size,
|
||||
int frameno,
|
||||
unsigned long long seek_pos,
|
||||
unsigned long long seek_pos_dts,
|
||||
unsigned long long pts)
|
||||
uint64_t seek_pos,
|
||||
uint64_t seek_pos_dts,
|
||||
uint64_t pts)
|
||||
{
|
||||
if (fp->proc_frame) {
|
||||
anim_index_entry e;
|
||||
@@ -186,10 +183,10 @@ struct anim_index *IMB_indexer_open(const char *name)
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
|
||||
idx->num_entries = (ftell(fp) - 12) / (sizeof(int) + /* framepos */
|
||||
sizeof(unsigned long long) + /* seek_pos */
|
||||
sizeof(unsigned long long) + /* seek_pos_dts */
|
||||
sizeof(unsigned long long) /* pts */
|
||||
idx->num_entries = (ftell(fp) - 12) / (sizeof(int) + /* framepos */
|
||||
sizeof(uint64_t) + /* seek_pos */
|
||||
sizeof(uint64_t) + /* seek_pos_dts */
|
||||
sizeof(uint64_t) /* pts */
|
||||
);
|
||||
|
||||
fseek(fp, 12, SEEK_SET);
|
||||
@@ -197,28 +194,28 @@ struct anim_index *IMB_indexer_open(const char *name)
|
||||
idx->entries = MEM_callocN(sizeof(struct anim_index_entry) * idx->num_entries,
|
||||
"anim_index_entries");
|
||||
|
||||
size_t items_read = 0;
|
||||
for (i = 0; i < idx->num_entries; i++) {
|
||||
size_t items_read = 0;
|
||||
items_read += fread(&idx->entries[i].frameno, sizeof(int), 1, fp);
|
||||
items_read += fread(&idx->entries[i].seek_pos, sizeof(unsigned long long), 1, fp);
|
||||
items_read += fread(&idx->entries[i].seek_pos_dts, sizeof(unsigned long long), 1, fp);
|
||||
items_read += fread(&idx->entries[i].pts, sizeof(unsigned long long), 1, fp);
|
||||
items_read += fread(&idx->entries[i].seek_pos, sizeof(uint64_t), 1, fp);
|
||||
items_read += fread(&idx->entries[i].seek_pos_dts, sizeof(uint64_t), 1, fp);
|
||||
items_read += fread(&idx->entries[i].pts, sizeof(uint64_t), 1, fp);
|
||||
}
|
||||
|
||||
if (items_read < 4) {
|
||||
perror("error reading animation index file");
|
||||
MEM_freeN(idx->entries);
|
||||
MEM_freeN(idx);
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
if (UNLIKELY(items_read != idx->num_entries * 4)) {
|
||||
perror("error reading animation index file");
|
||||
MEM_freeN(idx->entries);
|
||||
MEM_freeN(idx);
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (((ENDIAN_ORDER == B_ENDIAN) != (header[8] == 'V'))) {
|
||||
for (i = 0; i < idx->num_entries; i++) {
|
||||
BLI_endian_switch_int32(&idx->entries[i].frameno);
|
||||
BLI_endian_switch_int64((int64_t *)&idx->entries[i].seek_pos);
|
||||
BLI_endian_switch_int64((int64_t *)&idx->entries[i].seek_pos_dts);
|
||||
BLI_endian_switch_int64((int64_t *)&idx->entries[i].pts);
|
||||
BLI_endian_switch_uint64(&idx->entries[i].seek_pos);
|
||||
BLI_endian_switch_uint64(&idx->entries[i].seek_pos_dts);
|
||||
BLI_endian_switch_uint64(&idx->entries[i].pts);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,7 +224,7 @@ struct anim_index *IMB_indexer_open(const char *name)
|
||||
return idx;
|
||||
}
|
||||
|
||||
unsigned long long IMB_indexer_get_seek_pos(struct anim_index *idx, int frame_index)
|
||||
uint64_t IMB_indexer_get_seek_pos(struct anim_index *idx, int frame_index)
|
||||
{
|
||||
if (frame_index < 0) {
|
||||
frame_index = 0;
|
||||
@@ -238,7 +235,7 @@ unsigned long long IMB_indexer_get_seek_pos(struct anim_index *idx, int frame_in
|
||||
return idx->entries[frame_index].seek_pos;
|
||||
}
|
||||
|
||||
unsigned long long IMB_indexer_get_seek_pos_dts(struct anim_index *idx, int frame_index)
|
||||
uint64_t IMB_indexer_get_seek_pos_dts(struct anim_index *idx, int frame_index)
|
||||
{
|
||||
if (frame_index < 0) {
|
||||
frame_index = 0;
|
||||
@@ -281,7 +278,7 @@ int IMB_indexer_get_frame_index(struct anim_index *idx, int frameno)
|
||||
return first;
|
||||
}
|
||||
|
||||
unsigned long long IMB_indexer_get_pts(struct anim_index *idx, int frame_index)
|
||||
uint64_t IMB_indexer_get_pts(struct anim_index *idx, int frame_index)
|
||||
{
|
||||
if (frame_index < 0) {
|
||||
frame_index = 0;
|
||||
@@ -707,12 +704,12 @@ typedef struct FFmpegIndexBuilderContext {
|
||||
IMB_Timecode_Type tcs_in_use;
|
||||
IMB_Proxy_Size proxy_sizes_in_use;
|
||||
|
||||
unsigned long long seek_pos;
|
||||
unsigned long long last_seek_pos;
|
||||
unsigned long long seek_pos_dts;
|
||||
unsigned long long seek_pos_pts;
|
||||
unsigned long long last_seek_pos_dts;
|
||||
unsigned long long start_pts;
|
||||
uint64_t seek_pos;
|
||||
uint64_t last_seek_pos;
|
||||
uint64_t seek_pos_dts;
|
||||
uint64_t seek_pos_pts;
|
||||
uint64_t last_seek_pos_dts;
|
||||
uint64_t start_pts;
|
||||
double frame_rate;
|
||||
double pts_time_base;
|
||||
int frameno, frameno_gapless;
|
||||
@@ -847,9 +844,9 @@ static void index_rebuild_ffmpeg_proc_decoded_frame(FFmpegIndexBuilderContext *c
|
||||
AVFrame *in_frame)
|
||||
{
|
||||
int i;
|
||||
unsigned long long s_pos = context->seek_pos;
|
||||
unsigned long long s_dts = context->seek_pos_dts;
|
||||
unsigned long long pts = av_get_pts_from_frame(context->iFormatCtx, in_frame);
|
||||
uint64_t s_pos = context->seek_pos;
|
||||
uint64_t s_dts = context->seek_pos_dts;
|
||||
uint64_t pts = av_get_pts_from_frame(context->iFormatCtx, in_frame);
|
||||
|
||||
for (i = 0; i < context->num_proxy_sizes; i++) {
|
||||
add_to_proxy_output_ffmpeg(context->proxy_ctx[i], in_frame);
|
||||
|
@@ -1108,6 +1108,18 @@ typedef struct NodeAttributeMix {
|
||||
uint8_t input_type_b;
|
||||
} NodeAttributeMix;
|
||||
|
||||
typedef struct NodeAttributeVectorMath {
|
||||
/* NodeVectorMathOperation */
|
||||
uint8_t operation;
|
||||
|
||||
/* GeometryNodeAttributeInputMode */
|
||||
uint8_t input_type_a;
|
||||
uint8_t input_type_b;
|
||||
uint8_t input_type_c;
|
||||
|
||||
char _pad[4];
|
||||
} NodeAttributeVectorMath;
|
||||
|
||||
typedef struct NodeAttributeColorRamp {
|
||||
ColorBand color_ramp;
|
||||
} NodeAttributeColorRamp;
|
||||
@@ -1129,6 +1141,17 @@ typedef struct NodeGeometryRotatePoints {
|
||||
char _pad[3];
|
||||
} NodeGeometryRotatePoints;
|
||||
|
||||
typedef struct NodeGeometryAlignRotationToVector {
|
||||
/* GeometryNodeAlignRotationToVectorAxis */
|
||||
uint8_t axis;
|
||||
|
||||
/* GeometryNodeAttributeInputMode */
|
||||
uint8_t input_type_factor;
|
||||
uint8_t input_type_vector;
|
||||
|
||||
char _pad[5];
|
||||
} NodeGeometryAlignRotationToVector;
|
||||
|
||||
/* script node mode */
|
||||
#define NODE_SCRIPT_INTERNAL 0
|
||||
#define NODE_SCRIPT_EXTERNAL 1
|
||||
@@ -1368,7 +1391,7 @@ enum {
|
||||
};
|
||||
|
||||
/* Vector Math node operations. */
|
||||
enum {
|
||||
typedef enum NodeVectorMathOperation {
|
||||
NODE_VECTOR_MATH_ADD = 0,
|
||||
NODE_VECTOR_MATH_SUBTRACT = 1,
|
||||
NODE_VECTOR_MATH_MULTIPLY = 2,
|
||||
@@ -1396,7 +1419,7 @@ enum {
|
||||
NODE_VECTOR_MATH_SINE = 21,
|
||||
NODE_VECTOR_MATH_COSINE = 22,
|
||||
NODE_VECTOR_MATH_TANGENT = 23,
|
||||
};
|
||||
} NodeVectorMathOperation;
|
||||
|
||||
/* Boolean math node operations. */
|
||||
enum {
|
||||
@@ -1564,6 +1587,12 @@ typedef enum GeometryNodeRotatePointsSpace {
|
||||
GEO_NODE_ROTATE_POINTS_SPACE_POINT = 1,
|
||||
} GeometryNodeRotatePointsSpace;
|
||||
|
||||
typedef enum GeometryNodeAlignRotationToVectorAxis {
|
||||
GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X = 0,
|
||||
GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_Y = 1,
|
||||
GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_Z = 2,
|
||||
} GeometryNodeAlignRotationToVectorAxis;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -132,7 +132,10 @@ typedef struct ParticleData {
|
||||
*/
|
||||
/** Index to vert/edge/face. */
|
||||
int num;
|
||||
/** Index to derived mesh data (face) to avoid slow lookups. */
|
||||
/**
|
||||
* Index to derived mesh data (face) to avoid slow lookups. It can also have negative
|
||||
* values DMCACHE_NOTFOUND and DMCACHE_ISCHILD.
|
||||
*/
|
||||
int num_dmcache;
|
||||
|
||||
/** Coordinates on face/edge number "num" and depth along. */
|
||||
|
@@ -426,59 +426,6 @@ static const EnumPropertyItem rna_node_geometry_triangulate_ngon_method_items[]
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
# define ITEM_ATTRIBUTE \
|
||||
{ \
|
||||
GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE, "ATTRIBUTE", 0, "Attribute", "" \
|
||||
}
|
||||
# define ITEM_FLOAT \
|
||||
{ \
|
||||
GEO_NODE_ATTRIBUTE_INPUT_FLOAT, "FLOAT", 0, "Float", "" \
|
||||
}
|
||||
# define ITEM_VECTOR \
|
||||
{ \
|
||||
GEO_NODE_ATTRIBUTE_INPUT_VECTOR, "VECTOR", 0, "Vector", "" \
|
||||
}
|
||||
# define ITEM_COLOR \
|
||||
{ \
|
||||
GEO_NODE_ATTRIBUTE_INPUT_COLOR, "COLOR", 0, "Color", "" \
|
||||
}
|
||||
# define ITEM_BOOLEAN \
|
||||
{ \
|
||||
GEO_NODE_ATTRIBUTE_INPUT_BOOLEAN, "BOOLEAN", 0, "Boolean", "" \
|
||||
}
|
||||
|
||||
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_float[] = {
|
||||
ITEM_ATTRIBUTE,
|
||||
ITEM_FLOAT,
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_vector[] = {
|
||||
ITEM_ATTRIBUTE,
|
||||
ITEM_VECTOR,
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_no_boolean[] = {
|
||||
ITEM_ATTRIBUTE,
|
||||
ITEM_FLOAT,
|
||||
ITEM_VECTOR,
|
||||
ITEM_COLOR,
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_any[] = {
|
||||
ITEM_ATTRIBUTE,
|
||||
ITEM_FLOAT,
|
||||
ITEM_VECTOR,
|
||||
ITEM_COLOR,
|
||||
ITEM_BOOLEAN,
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
# undef ITEM_ATTRIBUTE
|
||||
# undef ITEM_FLOAT
|
||||
# undef ITEM_VECTOR
|
||||
# undef ITEM_COLOR
|
||||
# undef ITEM_BOOLEAN
|
||||
|
||||
static const EnumPropertyItem rna_node_geometry_point_distribute_method_items[] = {
|
||||
{GEO_NODE_POINT_DISTRIBUTE_RANDOM,
|
||||
"RANDOM",
|
||||
@@ -495,6 +442,65 @@ static const EnumPropertyItem rna_node_geometry_point_distribute_method_items[]
|
||||
|
||||
#endif
|
||||
|
||||
#define ITEM_ATTRIBUTE \
|
||||
{ \
|
||||
GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE, "ATTRIBUTE", 0, "Attribute", "" \
|
||||
}
|
||||
#define ITEM_FLOAT \
|
||||
{ \
|
||||
GEO_NODE_ATTRIBUTE_INPUT_FLOAT, "FLOAT", 0, "Float", "" \
|
||||
}
|
||||
#define ITEM_VECTOR \
|
||||
{ \
|
||||
GEO_NODE_ATTRIBUTE_INPUT_VECTOR, "VECTOR", 0, "Vector", "" \
|
||||
}
|
||||
#define ITEM_COLOR \
|
||||
{ \
|
||||
GEO_NODE_ATTRIBUTE_INPUT_COLOR, "COLOR", 0, "Color", "" \
|
||||
}
|
||||
#define ITEM_BOOLEAN \
|
||||
{ \
|
||||
GEO_NODE_ATTRIBUTE_INPUT_BOOLEAN, "BOOLEAN", 0, "Boolean", "" \
|
||||
}
|
||||
|
||||
/* Used in both runtime and static code. */
|
||||
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_any[] = {
|
||||
ITEM_ATTRIBUTE,
|
||||
ITEM_FLOAT,
|
||||
ITEM_VECTOR,
|
||||
ITEM_COLOR,
|
||||
ITEM_BOOLEAN,
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
#ifndef RNA_RUNTIME
|
||||
|
||||
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_vector[] = {
|
||||
ITEM_ATTRIBUTE,
|
||||
ITEM_VECTOR,
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_float[] = {
|
||||
ITEM_ATTRIBUTE,
|
||||
ITEM_FLOAT,
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_no_boolean[] = {
|
||||
ITEM_ATTRIBUTE,
|
||||
ITEM_FLOAT,
|
||||
ITEM_VECTOR,
|
||||
ITEM_COLOR,
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#undef ITEM_ATTRIBUTE
|
||||
#undef ITEM_FLOAT
|
||||
#undef ITEM_VECTOR
|
||||
#undef ITEM_COLOR
|
||||
#undef ITEM_BOOLEAN
|
||||
|
||||
#ifdef RNA_RUNTIME
|
||||
|
||||
# include "BLI_linklist.h"
|
||||
@@ -521,6 +527,8 @@ static const EnumPropertyItem rna_node_geometry_point_distribute_method_items[]
|
||||
# include "DNA_scene_types.h"
|
||||
# include "WM_api.h"
|
||||
|
||||
static void rna_Node_socket_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr);
|
||||
|
||||
int rna_node_tree_type_to_enum(bNodeTreeType *typeinfo)
|
||||
{
|
||||
int i = 0, result = -1;
|
||||
@@ -1971,6 +1979,69 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeMath_operation_itemf(
|
||||
return itemf_function_check(rna_enum_node_math_items, attribute_math_operation_supported);
|
||||
}
|
||||
|
||||
/**
|
||||
* This bit of ugly code makes sure the float / attribute option shows up instead of
|
||||
* vector / attribute if the node uses an operation that uses a float for input B.
|
||||
*/
|
||||
static const EnumPropertyItem *rna_GeometryNodeAttributeVectorMath_input_type_b_itemf(
|
||||
bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
|
||||
{
|
||||
bNode *node = ptr->data;
|
||||
NodeAttributeVectorMath *node_storage = (NodeAttributeVectorMath *)node->storage;
|
||||
|
||||
EnumPropertyItem *item_array = NULL;
|
||||
int items_len = 0;
|
||||
for (const EnumPropertyItem *item = rna_node_geometry_attribute_input_type_items_any;
|
||||
item->identifier != NULL;
|
||||
item++) {
|
||||
if (item->value == GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE) {
|
||||
RNA_enum_item_add(&item_array, &items_len, item);
|
||||
}
|
||||
else if (item->value == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) {
|
||||
if (node_storage->operation == NODE_VECTOR_MATH_SCALE) {
|
||||
RNA_enum_item_add(&item_array, &items_len, item);
|
||||
}
|
||||
}
|
||||
else if (item->value == GEO_NODE_ATTRIBUTE_INPUT_VECTOR) {
|
||||
if (node_storage->operation != NODE_VECTOR_MATH_SCALE) {
|
||||
RNA_enum_item_add(&item_array, &items_len, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
RNA_enum_item_end(&item_array, &items_len);
|
||||
|
||||
*r_free = true;
|
||||
return item_array;
|
||||
}
|
||||
|
||||
static void rna_GeometryNodeAttributeVectorMath_operation_update(Main *bmain,
|
||||
Scene *scene,
|
||||
PointerRNA *ptr)
|
||||
{
|
||||
bNode *node = ptr->data;
|
||||
NodeAttributeVectorMath *node_storage = (NodeAttributeVectorMath *)node->storage;
|
||||
|
||||
const NodeVectorMathOperation operation = (NodeVectorMathOperation)node_storage->operation;
|
||||
|
||||
/* The scale operation can't use a vector input, so reset
|
||||
* the input type enum in case it's set to vector. */
|
||||
if (operation == NODE_VECTOR_MATH_SCALE) {
|
||||
if (node_storage->input_type_b == GEO_NODE_ATTRIBUTE_INPUT_VECTOR) {
|
||||
node_storage->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
|
||||
}
|
||||
}
|
||||
|
||||
/* Scale is also the only operation that uses the float input type, so a
|
||||
* a check is also necessary for the other direction. */
|
||||
if (operation != NODE_VECTOR_MATH_SCALE) {
|
||||
if (node_storage->input_type_b == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) {
|
||||
node_storage->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
|
||||
}
|
||||
}
|
||||
|
||||
rna_Node_socket_update(bmain, scene, ptr);
|
||||
}
|
||||
|
||||
static StructRNA *rna_ShaderNode_register(Main *bmain,
|
||||
ReportList *reports,
|
||||
void *data,
|
||||
@@ -8503,6 +8574,40 @@ static void def_geo_attribute_math(StructRNA *srna)
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||
}
|
||||
|
||||
static void def_geo_attribute_vector_math(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
RNA_def_struct_sdna_from(srna, "NodeAttributeVectorMath", "storage");
|
||||
|
||||
prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "operation");
|
||||
RNA_def_property_enum_items(prop, rna_enum_node_vec_math_items);
|
||||
RNA_def_property_ui_text(prop, "Operation", "");
|
||||
RNA_def_property_update(
|
||||
prop, NC_NODE | NA_EDITED, "rna_GeometryNodeAttributeVectorMath_operation_update");
|
||||
|
||||
prop = RNA_def_property(srna, "input_type_a", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_bitflag_sdna(prop, NULL, "input_type_a");
|
||||
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
|
||||
RNA_def_property_ui_text(prop, "Input Type A", "");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||
|
||||
prop = RNA_def_property(srna, "input_type_b", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_bitflag_sdna(prop, NULL, "input_type_b");
|
||||
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_any);
|
||||
RNA_def_property_enum_funcs(
|
||||
prop, NULL, NULL, "rna_GeometryNodeAttributeVectorMath_input_type_b_itemf");
|
||||
RNA_def_property_ui_text(prop, "Input Type B", "");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||
|
||||
prop = RNA_def_property(srna, "input_type_c", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_bitflag_sdna(prop, NULL, "input_type_b");
|
||||
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
|
||||
RNA_def_property_ui_text(prop, "Input Type B", "");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||
}
|
||||
|
||||
static void def_geo_point_instance(StructRNA *srna)
|
||||
{
|
||||
static const EnumPropertyItem instance_type_items[] = {
|
||||
@@ -8669,6 +8774,47 @@ static void def_geo_rotate_points(StructRNA *srna)
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||
}
|
||||
|
||||
static void def_geo_align_rotation_to_vector(StructRNA *srna)
|
||||
{
|
||||
static const EnumPropertyItem axis_items[] = {
|
||||
{GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X,
|
||||
"X",
|
||||
ICON_NONE,
|
||||
"X",
|
||||
"Align the X axis with the vector"},
|
||||
{GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_Y,
|
||||
"Y",
|
||||
ICON_NONE,
|
||||
"Y",
|
||||
"Align the Y axis with the vector"},
|
||||
{GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_Z,
|
||||
"Z",
|
||||
ICON_NONE,
|
||||
"Z",
|
||||
"Align the Z axis with the vector"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
PropertyRNA *prop;
|
||||
|
||||
RNA_def_struct_sdna_from(srna, "NodeGeometryAlignRotationToVector", "storage");
|
||||
|
||||
prop = RNA_def_property(srna, "axis", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, axis_items);
|
||||
RNA_def_property_ui_text(prop, "Axis", "Axis to align to the vector");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "input_type_factor", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float);
|
||||
RNA_def_property_ui_text(prop, "Input Type Factor", "");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||
|
||||
prop = RNA_def_property(srna, "input_type_vector", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
|
||||
RNA_def_property_ui_text(prop, "Input Type Vector", "");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static void rna_def_shader_node(BlenderRNA *brna)
|
||||
|
@@ -403,16 +403,15 @@ static void rna_Particle_uv_on_emitter(ParticleData *particle,
|
||||
}
|
||||
BKE_mesh_tessface_ensure(modifier->mesh_final); /* BMESH - UNTIL MODIFIER IS UPDATED FOR MPoly */
|
||||
|
||||
if (num == DMCACHE_NOTFOUND) {
|
||||
if (ELEM(num, DMCACHE_NOTFOUND, DMCACHE_ISCHILD)) {
|
||||
if (particle->num < modifier->mesh_final->totface) {
|
||||
num = particle->num;
|
||||
}
|
||||
}
|
||||
|
||||
/* get uvco */
|
||||
if (r_uv && ELEM(from, PART_FROM_FACE, PART_FROM_VOLUME)) {
|
||||
|
||||
if (num != DMCACHE_NOTFOUND) {
|
||||
if (r_uv && ELEM(from, PART_FROM_FACE, PART_FROM_VOLUME) &&
|
||||
!ELEM(num, DMCACHE_NOTFOUND, DMCACHE_ISCHILD)) {
|
||||
MFace *mface;
|
||||
MTFace *mtface;
|
||||
|
||||
@@ -424,7 +423,6 @@ static void rna_Particle_uv_on_emitter(ParticleData *particle,
|
||||
psys_interpolate_uvs(mtface, mface->v4, particle->fuv, r_uv);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r_uv[0] = 0.0f;
|
||||
|
@@ -196,7 +196,8 @@ static float compute_voxel_size(const ModifierEvalContext *ctx,
|
||||
/* Compute the voxel size based on the desired number of voxels and the approximated bounding box
|
||||
* of the volume. */
|
||||
const BoundBox *bb = BKE_object_boundbox_get(mvmd->object);
|
||||
const float diagonal = distance(transform * float3(bb->vec[6]), transform * float3(bb->vec[0]));
|
||||
const float diagonal = float3::distance(transform * float3(bb->vec[6]),
|
||||
transform * float3(bb->vec[0]));
|
||||
const float approximate_volume_side_length = diagonal + mvmd->exterior_band_width * 2.0f;
|
||||
const float voxel_size = approximate_volume_side_length / mvmd->voxel_amount / volume_simplify;
|
||||
return voxel_size;
|
||||
|
@@ -140,19 +140,20 @@ set(SRC
|
||||
function/nodes/node_fn_switch.cc
|
||||
function/node_function_util.cc
|
||||
|
||||
geometry/nodes/node_geo_align_rotation_to_vector.cc
|
||||
geometry/nodes/node_geo_attribute_color_ramp.cc
|
||||
geometry/nodes/node_geo_attribute_compare.cc
|
||||
geometry/nodes/node_geo_attribute_fill.cc
|
||||
geometry/nodes/node_geo_attribute_math.cc
|
||||
geometry/nodes/node_geo_attribute_mix.cc
|
||||
geometry/nodes/node_geo_attribute_randomize.cc
|
||||
geometry/nodes/node_geo_attribute_vector_math.cc
|
||||
geometry/nodes/node_geo_boolean.cc
|
||||
geometry/nodes/node_geo_common.cc
|
||||
geometry/nodes/node_geo_edge_split.cc
|
||||
geometry/nodes/node_geo_join_geometry.cc
|
||||
geometry/nodes/node_geo_object_info.cc
|
||||
geometry/nodes/node_geo_point_distribute.cc
|
||||
geometry/nodes/node_geo_point_distribute_poisson_disk.cc
|
||||
geometry/nodes/node_geo_point_instance.cc
|
||||
geometry/nodes/node_geo_point_separate.cc
|
||||
geometry/nodes/node_geo_rotate_points.cc
|
||||
|
@@ -27,6 +27,7 @@ void register_node_tree_type_geo(void);
|
||||
void register_node_type_geo_group(void);
|
||||
|
||||
void register_node_type_geo_attribute_fill(void);
|
||||
void register_node_type_geo_attribute_vector_math(void);
|
||||
void register_node_type_geo_boolean(void);
|
||||
void register_node_type_geo_edge_split(void);
|
||||
void register_node_type_geo_transform(void);
|
||||
@@ -43,6 +44,7 @@ void register_node_type_geo_attribute_compare(void);
|
||||
void register_node_type_geo_attribute_mix(void);
|
||||
void register_node_type_geo_attribute_color_ramp(void);
|
||||
void register_node_type_geo_rotate_points(void);
|
||||
void register_node_type_geo_align_rotation_to_vector(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
|
||||
#include "BLI_float3.hh"
|
||||
#include "BLI_math_base_safe.h"
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_string_ref.hh"
|
||||
@@ -36,6 +37,7 @@ struct FloatMathOperationInfo {
|
||||
};
|
||||
|
||||
const FloatMathOperationInfo *get_float_math_operation_info(const int operation);
|
||||
const FloatMathOperationInfo *get_float3_math_operation_info(const int operation);
|
||||
const FloatMathOperationInfo *get_float_compare_operation_info(const int operation);
|
||||
|
||||
/**
|
||||
@@ -231,4 +233,228 @@ inline bool try_dispatch_float_math_fl_fl_to_bool(const FloatCompareOperation op
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is similar to try_dispatch_float_math_fl_to_fl, just with a different callback signature.
|
||||
*/
|
||||
template<typename Callback>
|
||||
inline bool try_dispatch_float_math_fl3_fl3_to_fl3(const NodeVectorMathOperation operation,
|
||||
Callback &&callback)
|
||||
{
|
||||
const FloatMathOperationInfo *info = get_float3_math_operation_info(operation);
|
||||
if (info == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* This is just a utility function to keep the individual cases smaller. */
|
||||
auto dispatch = [&](auto math_function) -> bool {
|
||||
callback(math_function, *info);
|
||||
return true;
|
||||
};
|
||||
|
||||
switch (operation) {
|
||||
case NODE_VECTOR_MATH_ADD:
|
||||
return dispatch([](float3 a, float3 b) { return a + b; });
|
||||
case NODE_VECTOR_MATH_SUBTRACT:
|
||||
return dispatch([](float3 a, float3 b) { return a - b; });
|
||||
case NODE_VECTOR_MATH_MULTIPLY:
|
||||
return dispatch([](float3 a, float3 b) { return a * b; });
|
||||
case NODE_VECTOR_MATH_DIVIDE:
|
||||
return dispatch([](float3 a, float3 b) {
|
||||
return float3(safe_divide(a.x, b.x), safe_divide(a.y, b.y), safe_divide(a.z, b.z));
|
||||
});
|
||||
case NODE_VECTOR_MATH_CROSS_PRODUCT:
|
||||
return dispatch([](float3 a, float3 b) { return float3::cross_high_precision(a, b); });
|
||||
case NODE_VECTOR_MATH_PROJECT:
|
||||
return dispatch([](float3 a, float3 b) {
|
||||
float length_squared = float3::dot(a, b);
|
||||
return (length_squared != 0.0) ? (float3::dot(a, b) / length_squared) * b : float3(0.0f);
|
||||
});
|
||||
case NODE_VECTOR_MATH_REFLECT:
|
||||
return dispatch([](float3 a, float3 b) {
|
||||
b.normalize();
|
||||
return a.reflected(b);
|
||||
});
|
||||
case NODE_VECTOR_MATH_SNAP:
|
||||
return dispatch([](float3 a, float3 b) {
|
||||
return float3(floor(safe_divide(a.x, b.x)),
|
||||
floor(safe_divide(a.y, b.y)),
|
||||
floor(safe_divide(a.z, b.z))) *
|
||||
b;
|
||||
});
|
||||
case NODE_VECTOR_MATH_MODULO:
|
||||
return dispatch([](float3 a, float3 b) {
|
||||
return float3(safe_modf(a.x, b.x), safe_modf(a.y, b.y), safe_modf(a.z, b.z));
|
||||
});
|
||||
case NODE_VECTOR_MATH_MINIMUM:
|
||||
return dispatch([](float3 a, float3 b) {
|
||||
return float3(min_ff(a.x, b.x), min_ff(a.y, b.y), min_ff(a.z, b.z));
|
||||
});
|
||||
case NODE_VECTOR_MATH_MAXIMUM:
|
||||
return dispatch([](float3 a, float3 b) {
|
||||
return float3(max_ff(a.x, b.x), max_ff(a.y, b.y), max_ff(a.z, b.z));
|
||||
});
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is similar to try_dispatch_float_math_fl_to_fl, just with a different callback signature.
|
||||
*/
|
||||
template<typename Callback>
|
||||
inline bool try_dispatch_float_math_fl3_fl3_to_fl(const NodeVectorMathOperation operation,
|
||||
Callback &&callback)
|
||||
{
|
||||
const FloatMathOperationInfo *info = get_float3_math_operation_info(operation);
|
||||
if (info == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* This is just a utility function to keep the individual cases smaller. */
|
||||
auto dispatch = [&](auto math_function) -> bool {
|
||||
callback(math_function, *info);
|
||||
return true;
|
||||
};
|
||||
|
||||
switch (operation) {
|
||||
case NODE_VECTOR_MATH_DOT_PRODUCT:
|
||||
return dispatch([](float3 a, float3 b) { return float3::dot(a, b); });
|
||||
case NODE_VECTOR_MATH_DISTANCE:
|
||||
return dispatch([](float3 a, float3 b) { return float3::distance(a, b); });
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is similar to try_dispatch_float_math_fl_to_fl, just with a different callback signature.
|
||||
*/
|
||||
template<typename Callback>
|
||||
inline bool try_dispatch_float_math_fl3_fl3_fl3_to_fl3(const NodeVectorMathOperation operation,
|
||||
Callback &&callback)
|
||||
{
|
||||
const FloatMathOperationInfo *info = get_float3_math_operation_info(operation);
|
||||
if (info == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* This is just a utility function to keep the individual cases smaller. */
|
||||
auto dispatch = [&](auto math_function) -> bool {
|
||||
callback(math_function, *info);
|
||||
return true;
|
||||
};
|
||||
|
||||
switch (operation) {
|
||||
case NODE_VECTOR_MATH_WRAP:
|
||||
return dispatch([](float3 a, float3 b, float3 c) {
|
||||
return float3(wrapf(a.x, b.x, c.x), wrapf(a.y, b.y, c.y), wrapf(a.z, b.z, c.z));
|
||||
});
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is similar to try_dispatch_float_math_fl_to_fl, just with a different callback signature.
|
||||
*/
|
||||
template<typename Callback>
|
||||
inline bool try_dispatch_float_math_fl3_to_fl(const NodeVectorMathOperation operation,
|
||||
Callback &&callback)
|
||||
{
|
||||
const FloatMathOperationInfo *info = get_float3_math_operation_info(operation);
|
||||
if (info == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* This is just a utility function to keep the individual cases smaller. */
|
||||
auto dispatch = [&](auto math_function) -> bool {
|
||||
callback(math_function, *info);
|
||||
return true;
|
||||
};
|
||||
|
||||
switch (operation) {
|
||||
case NODE_VECTOR_MATH_LENGTH:
|
||||
return dispatch([](float3 in) { return in.length(); });
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is similar to try_dispatch_float_math_fl_to_fl, just with a different callback signature.
|
||||
*/
|
||||
template<typename Callback>
|
||||
inline bool try_dispatch_float_math_fl3_fl_to_fl3(const NodeVectorMathOperation operation,
|
||||
Callback &&callback)
|
||||
{
|
||||
const FloatMathOperationInfo *info = get_float3_math_operation_info(operation);
|
||||
if (info == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* This is just a utility function to keep the individual cases smaller. */
|
||||
auto dispatch = [&](auto math_function) -> bool {
|
||||
callback(math_function, *info);
|
||||
return true;
|
||||
};
|
||||
|
||||
switch (operation) {
|
||||
case NODE_VECTOR_MATH_SCALE:
|
||||
return dispatch([](float3 a, float b) { return a * b; });
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is similar to try_dispatch_float_math_fl_to_fl, just with a different callback signature.
|
||||
*/
|
||||
template<typename Callback>
|
||||
inline bool try_dispatch_float_math_fl3_to_fl3(const NodeVectorMathOperation operation,
|
||||
Callback &&callback)
|
||||
{
|
||||
const FloatMathOperationInfo *info = get_float3_math_operation_info(operation);
|
||||
if (info == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* This is just a utility function to keep the individual cases smaller. */
|
||||
auto dispatch = [&](auto math_function) -> bool {
|
||||
callback(math_function, *info);
|
||||
return true;
|
||||
};
|
||||
|
||||
switch (operation) {
|
||||
case NODE_VECTOR_MATH_NORMALIZE:
|
||||
return dispatch([](float3 in) {
|
||||
float3 out = in;
|
||||
out.normalize();
|
||||
return out;
|
||||
}); /* Should be safe. */
|
||||
case NODE_VECTOR_MATH_FLOOR:
|
||||
return dispatch([](float3 in) { return float3(floor(in.x), floor(in.y), floor(in.z)); });
|
||||
case NODE_VECTOR_MATH_CEIL:
|
||||
return dispatch([](float3 in) { return float3(ceil(in.x), ceil(in.y), ceil(in.z)); });
|
||||
case NODE_VECTOR_MATH_FRACTION:
|
||||
return dispatch(
|
||||
[](float3 in) { return in - float3(floor(in.x), floor(in.y), floor(in.z)); });
|
||||
case NODE_VECTOR_MATH_ABSOLUTE:
|
||||
return dispatch([](float3 in) { return float3::abs(in); });
|
||||
case NODE_VECTOR_MATH_SINE:
|
||||
return dispatch([](float3 in) { return float3(sinf(in.x), sinf(in.y), sinf(in.z)); });
|
||||
case NODE_VECTOR_MATH_COSINE:
|
||||
return dispatch([](float3 in) { return float3(cosf(in.x), cosf(in.y), cosf(in.z)); });
|
||||
case NODE_VECTOR_MATH_TANGENT:
|
||||
return dispatch([](float3 in) { return float3(tanf(in.x), tanf(in.y), tanf(in.z)); });
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace blender::nodes
|
||||
|
@@ -285,6 +285,8 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ram
|
||||
DefNode(GeometryNode, GEO_NODE_POINT_SEPARATE, 0, "POINT_SEPARATE", PointSeparate, "Point Separate", "")
|
||||
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMPARE, def_geo_attribute_attribute_compare, "ATTRIBUTE_COMPARE", AttributeCompare, "Attribute Compare", "")
|
||||
DefNode(GeometryNode, GEO_NODE_ROTATE_POINTS, def_geo_rotate_points, "ROTATE_POINTS", RotatePoints, "Rotate Points", "")
|
||||
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_math, "ATTRIBUTE_VECTOR_MATH", AttributeVectorMath, "Attribute Vector Math", "")
|
||||
DefNode(GeometryNode, GEO_NODE_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "ALIGN_ROTATION_TO_VECTOR", AlignRotationToVector, "Align Rotation to Vector", "")
|
||||
|
||||
/* undefine macros */
|
||||
#undef DefNode
|
||||
|
@@ -58,7 +58,7 @@ static int attribute_data_type_complexity(const CustomDataType data_type)
|
||||
return 4;
|
||||
case CD_PROP_COLOR:
|
||||
return 5;
|
||||
#if 0 /* Attribute types are not supported yet. */
|
||||
#if 0 /* These attribute types are not supported yet. */
|
||||
case CD_MLOOPCOL:
|
||||
return 3;
|
||||
case CD_PROP_STRING:
|
||||
@@ -71,7 +71,7 @@ static int attribute_data_type_complexity(const CustomDataType data_type)
|
||||
}
|
||||
}
|
||||
|
||||
CustomDataType attribute_domain_highest_complexity(Span<CustomDataType> data_types)
|
||||
CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_types)
|
||||
{
|
||||
int highest_complexity = INT_MIN;
|
||||
CustomDataType most_complex_type = CD_PROP_COLOR;
|
||||
|
@@ -44,12 +44,7 @@ void update_attribute_input_socket_availabilities(bNode &node,
|
||||
const GeometryNodeAttributeInputMode mode,
|
||||
const bool name_is_available = true);
|
||||
|
||||
CustomDataType attribute_domain_highest_complexity(Span<CustomDataType>);
|
||||
|
||||
void poisson_disk_point_elimination(Vector<float3> const *input_points,
|
||||
Vector<float3> *output_points,
|
||||
float maximum_distance,
|
||||
float3 boundbox);
|
||||
CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType>);
|
||||
|
||||
Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component,
|
||||
const AttributeDomain domain);
|
||||
|
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
#include "BLI_math_rotation.h"
|
||||
|
||||
static bNodeSocketTemplate geo_node_align_rotation_to_vector_in[] = {
|
||||
{SOCK_GEOMETRY, N_("Geometry")},
|
||||
{SOCK_STRING, N_("Factor")},
|
||||
{SOCK_FLOAT, N_("Factor"), 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, PROP_FACTOR},
|
||||
{SOCK_STRING, N_("Vector")},
|
||||
{SOCK_VECTOR, N_("Vector"), 0.0, 0.0, 1.0, 0.0, -FLT_MAX, FLT_MAX, PROP_ANGLE},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
static bNodeSocketTemplate geo_node_align_rotation_to_vector_out[] = {
|
||||
{SOCK_GEOMETRY, N_("Geometry")},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
static void align_rotations_on_component(GeometryComponent &component,
|
||||
const GeoNodeExecParams ¶ms)
|
||||
{
|
||||
const bNode &node = params.node();
|
||||
const NodeGeometryAlignRotationToVector &storage = *(const NodeGeometryAlignRotationToVector *)
|
||||
node.storage;
|
||||
|
||||
WriteAttributePtr rotation_attribute = component.attribute_try_ensure_for_write(
|
||||
"rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
|
||||
if (!rotation_attribute) {
|
||||
return;
|
||||
}
|
||||
MutableSpan<float3> rotations = rotation_attribute->get_span().typed<float3>();
|
||||
|
||||
FloatReadAttribute factors = params.get_input_attribute<float>(
|
||||
"Factor", component, ATTR_DOMAIN_POINT, 1.0f);
|
||||
Float3ReadAttribute vectors = params.get_input_attribute<float3>(
|
||||
"Vector", component, ATTR_DOMAIN_POINT, {0, 0, 1});
|
||||
|
||||
float3 main_axis{0, 0, 0};
|
||||
main_axis[storage.axis] = 1;
|
||||
|
||||
const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
|
||||
for (const int i : IndexRange(domain_size)) {
|
||||
const float3 vector = vectors[i];
|
||||
if (is_zero_v3(vector)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float old_rotation[3][3];
|
||||
eul_to_mat3(old_rotation, rotations[i]);
|
||||
float3 old_axis;
|
||||
mul_v3_m3v3(old_axis, old_rotation, main_axis);
|
||||
|
||||
const float3 new_axis = vector.normalized();
|
||||
const float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis);
|
||||
const float full_angle = angle_normalized_v3v3(old_axis, new_axis);
|
||||
const float angle = factors[i] * full_angle;
|
||||
|
||||
float rotation[3][3];
|
||||
axis_angle_to_mat3(rotation, rotation_axis, angle);
|
||||
|
||||
float new_rotation_matrix[3][3];
|
||||
mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
|
||||
|
||||
float3 new_rotation;
|
||||
mat3_to_eul(new_rotation, new_rotation_matrix);
|
||||
|
||||
rotations[i] = new_rotation;
|
||||
}
|
||||
|
||||
rotation_attribute->apply_span();
|
||||
}
|
||||
|
||||
static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
align_rotations_on_component(geometry_set.get_component_for_write<MeshComponent>(), params);
|
||||
}
|
||||
if (geometry_set.has<PointCloudComponent>()) {
|
||||
align_rotations_on_component(geometry_set.get_component_for_write<PointCloudComponent>(),
|
||||
params);
|
||||
}
|
||||
|
||||
params.set_output("Geometry", geometry_set);
|
||||
}
|
||||
|
||||
static void geo_node_align_rotation_to_vector_init(bNodeTree *UNUSED(ntree), bNode *node)
|
||||
{
|
||||
NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
|
||||
MEM_callocN(sizeof(NodeGeometryAlignRotationToVector), __func__);
|
||||
|
||||
node_storage->axis = GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X;
|
||||
node_storage->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
|
||||
node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
|
||||
|
||||
node->storage = node_storage;
|
||||
}
|
||||
|
||||
static void geo_node_align_rotation_to_vector_update(bNodeTree *UNUSED(ntree), bNode *node)
|
||||
{
|
||||
NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
|
||||
node->storage;
|
||||
update_attribute_input_socket_availabilities(
|
||||
*node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor);
|
||||
update_attribute_input_socket_availabilities(
|
||||
*node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector);
|
||||
}
|
||||
|
||||
} // namespace blender::nodes
|
||||
|
||||
void register_node_type_geo_align_rotation_to_vector()
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
geo_node_type_base(&ntype,
|
||||
GEO_NODE_ALIGN_ROTATION_TO_VECTOR,
|
||||
"Align Rotation to Vector",
|
||||
NODE_CLASS_GEOMETRY,
|
||||
0);
|
||||
node_type_socket_templates(
|
||||
&ntype, geo_node_align_rotation_to_vector_in, geo_node_align_rotation_to_vector_out);
|
||||
node_type_init(&ntype, blender::nodes::geo_node_align_rotation_to_vector_init);
|
||||
node_type_update(&ntype, blender::nodes::geo_node_align_rotation_to_vector_update);
|
||||
node_type_storage(&ntype,
|
||||
"NodeGeometryAlignRotationToVector",
|
||||
node_free_standard_storage,
|
||||
node_copy_standard_storage);
|
||||
ntype.geometry_node_execute = blender::nodes::geo_node_align_rotation_to_vector_exec;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
@@ -37,12 +37,14 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon
|
||||
const bNode &bnode = params.node();
|
||||
NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)bnode.storage;
|
||||
|
||||
/* Use the type of the input attribute, but create a color attribute if it doesn't exist yet. */
|
||||
const CustomDataType result_type = params.get_input_attribute_data_type(
|
||||
"Attribute", component, CD_PROP_COLOR);
|
||||
|
||||
const std::string result_name = params.get_input<std::string>("Result");
|
||||
/* Once we support more domains at the user level, we have to decide how the result domain is
|
||||
* chosen. */
|
||||
const AttributeDomain result_domain = ATTR_DOMAIN_POINT;
|
||||
const CustomDataType result_type = CD_PROP_COLOR;
|
||||
|
||||
WriteAttributePtr attribute_result = component.attribute_try_ensure_for_write(
|
||||
result_name, result_domain, result_type);
|
||||
if (!attribute_result) {
|
||||
@@ -56,7 +58,7 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon
|
||||
input_name, result_domain, 0.0f);
|
||||
|
||||
Span<float> data_in = attribute_in.get_span();
|
||||
MutableSpan<Color4f> data_out = attribute_out.get_span();
|
||||
MutableSpan<Color4f> data_out = attribute_out.get_span_for_write_only();
|
||||
|
||||
ColorBand *color_ramp = &node_storage->color_ramp;
|
||||
for (const int i : data_in.index_range()) {
|
||||
|
@@ -216,14 +216,12 @@ static CustomDataType get_data_type(GeometryComponent &component,
|
||||
const NodeAttributeCompare &node_storage)
|
||||
{
|
||||
if (operation_tests_equality(node_storage)) {
|
||||
CustomDataType data_type_a = params.get_input_attribute_data_type(
|
||||
"A", component, CD_PROP_FLOAT);
|
||||
CustomDataType data_type_b = params.get_input_attribute_data_type(
|
||||
"B", component, CD_PROP_FLOAT);
|
||||
|
||||
/* Convert the input attributes to the same data type for the equality tests. Use the higher
|
||||
* complexity attribute type, otherwise information necessary to the comparison may be lost. */
|
||||
return attribute_domain_highest_complexity({data_type_a, data_type_b});
|
||||
return attribute_data_type_highest_complexity({
|
||||
params.get_input_attribute_data_type("A", component, CD_PROP_FLOAT),
|
||||
params.get_input_attribute_data_type("B", component, CD_PROP_FLOAT),
|
||||
});
|
||||
}
|
||||
|
||||
/* Use float compare for every operation besides equality. */
|
||||
@@ -263,7 +261,7 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
|
||||
}
|
||||
|
||||
BooleanWriteAttribute attribute_result_bool = std::move(attribute_result);
|
||||
MutableSpan<bool> result_span = attribute_result_bool.get_span();
|
||||
MutableSpan<bool> result_span = attribute_result_bool.get_span_for_write_only();
|
||||
|
||||
/* Use specific types for correct equality operations, but for other operations we use implicit
|
||||
* conversions and float comparison. In other words, the comparison is not element-wise. */
|
||||
|
@@ -78,7 +78,7 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams
|
||||
case CD_PROP_FLOAT: {
|
||||
FloatWriteAttribute float_attribute = std::move(attribute);
|
||||
const float value = params.get_input<float>("Value_001");
|
||||
MutableSpan<float> attribute_span = float_attribute.get_span();
|
||||
MutableSpan<float> attribute_span = float_attribute.get_span_for_write_only();
|
||||
attribute_span.fill(value);
|
||||
float_attribute.apply_span();
|
||||
break;
|
||||
@@ -86,7 +86,7 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams
|
||||
case CD_PROP_FLOAT3: {
|
||||
Float3WriteAttribute float3_attribute = std::move(attribute);
|
||||
const float3 value = params.get_input<float3>("Value");
|
||||
MutableSpan<float3> attribute_span = float3_attribute.get_span();
|
||||
MutableSpan<float3> attribute_span = float3_attribute.get_span_for_write_only();
|
||||
attribute_span.fill(value);
|
||||
float3_attribute.apply_span();
|
||||
break;
|
||||
@@ -94,7 +94,7 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams
|
||||
case CD_PROP_COLOR: {
|
||||
Color4fWriteAttribute color4f_attribute = std::move(attribute);
|
||||
const Color4f value = params.get_input<Color4f>("Value_002");
|
||||
MutableSpan<Color4f> attribute_span = color4f_attribute.get_span();
|
||||
MutableSpan<Color4f> attribute_span = color4f_attribute.get_span_for_write_only();
|
||||
attribute_span.fill(value);
|
||||
color4f_attribute.apply_span();
|
||||
break;
|
||||
@@ -102,7 +102,7 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams
|
||||
case CD_PROP_BOOL: {
|
||||
BooleanWriteAttribute boolean_attribute = std::move(attribute);
|
||||
const bool value = params.get_input<bool>("Value_003");
|
||||
MutableSpan<bool> attribute_span = boolean_attribute.get_span();
|
||||
MutableSpan<bool> attribute_span = boolean_attribute.get_span_for_write_only();
|
||||
attribute_span.fill(value);
|
||||
boolean_attribute.apply_span();
|
||||
break;
|
||||
|
@@ -75,7 +75,7 @@ static void do_math_operation(const FloatReadAttribute &input_a,
|
||||
|
||||
Span<float> span_a = input_a.get_span();
|
||||
Span<float> span_b = input_b.get_span();
|
||||
MutableSpan<float> span_result = result.get_span();
|
||||
MutableSpan<float> span_result = result.get_span_for_write_only();
|
||||
|
||||
bool success = try_dispatch_float_math_fl_fl_to_fl(
|
||||
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
|
||||
|
@@ -136,14 +136,21 @@ static void attribute_mix_calc(GeometryComponent &component, const GeoNodeExecPa
|
||||
const bNode &node = params.node();
|
||||
const NodeAttributeMix *node_storage = (const NodeAttributeMix *)node.storage;
|
||||
|
||||
CustomDataType result_type = CD_PROP_COLOR;
|
||||
AttributeDomain result_domain = ATTR_DOMAIN_POINT;
|
||||
/* Use the highest complexity data type among the inputs and outputs, that way the node will
|
||||
* never "remove information". Use CD_PROP_BOOL as the lowest complexity data type, but in any
|
||||
* real situation it won't be returned. */
|
||||
const CustomDataType result_type = attribute_data_type_highest_complexity({
|
||||
params.get_input_attribute_data_type("A", component, CD_PROP_BOOL),
|
||||
params.get_input_attribute_data_type("B", component, CD_PROP_BOOL),
|
||||
params.get_input_attribute_data_type("Result", component, CD_PROP_BOOL),
|
||||
});
|
||||
|
||||
/* Use type and domain from the result attribute, if it exists already. */
|
||||
/* Once we support more domains at the user level, we have to decide how the result domain is
|
||||
* chosen. */
|
||||
AttributeDomain result_domain = ATTR_DOMAIN_POINT;
|
||||
const std::string result_name = params.get_input<std::string>("Result");
|
||||
const ReadAttributePtr result_attribute_read = component.attribute_try_get_for_read(result_name);
|
||||
if (result_attribute_read) {
|
||||
result_type = result_attribute_read->custom_data_type();
|
||||
result_domain = result_attribute_read->domain();
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,432 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
#include "BKE_attribute.h"
|
||||
#include "BKE_attribute_access.hh"
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_math_base_safe.h"
|
||||
#include "BLI_rand.hh"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_pointcloud_types.h"
|
||||
|
||||
#include "NOD_math_functions.hh"
|
||||
|
||||
static bNodeSocketTemplate geo_node_attribute_vector_math_in[] = {
|
||||
{SOCK_GEOMETRY, N_("Geometry")},
|
||||
{SOCK_STRING, N_("A")},
|
||||
{SOCK_VECTOR, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
|
||||
{SOCK_STRING, N_("B")},
|
||||
{SOCK_VECTOR, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
|
||||
{SOCK_FLOAT, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
|
||||
{SOCK_STRING, N_("C")},
|
||||
{SOCK_VECTOR, N_("C"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
|
||||
{SOCK_STRING, N_("Result")},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
static bNodeSocketTemplate geo_node_attribute_vector_math_out[] = {
|
||||
{SOCK_GEOMETRY, N_("Geometry")},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
static void geo_node_attribute_vector_math_init(bNodeTree *UNUSED(tree), bNode *node)
|
||||
{
|
||||
NodeAttributeVectorMath *data = (NodeAttributeVectorMath *)MEM_callocN(
|
||||
sizeof(NodeAttributeVectorMath), __func__);
|
||||
|
||||
data->operation = NODE_VECTOR_MATH_ADD;
|
||||
data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
|
||||
data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
|
||||
node->storage = data;
|
||||
}
|
||||
|
||||
static CustomDataType operation_get_read_type_b(const NodeVectorMathOperation operation)
|
||||
{
|
||||
if (operation == NODE_VECTOR_MATH_SCALE) {
|
||||
return CD_PROP_FLOAT;
|
||||
}
|
||||
return CD_PROP_FLOAT3;
|
||||
}
|
||||
|
||||
static bool operation_use_input_b(const NodeVectorMathOperation operation)
|
||||
{
|
||||
return !ELEM(operation,
|
||||
NODE_VECTOR_MATH_NORMALIZE,
|
||||
NODE_VECTOR_MATH_FLOOR,
|
||||
NODE_VECTOR_MATH_CEIL,
|
||||
NODE_VECTOR_MATH_FRACTION,
|
||||
NODE_VECTOR_MATH_ABSOLUTE,
|
||||
NODE_VECTOR_MATH_SINE,
|
||||
NODE_VECTOR_MATH_COSINE,
|
||||
NODE_VECTOR_MATH_TANGENT,
|
||||
NODE_VECTOR_MATH_LENGTH);
|
||||
}
|
||||
|
||||
static bool operation_use_input_c(const NodeVectorMathOperation operation)
|
||||
{
|
||||
return operation == NODE_VECTOR_MATH_WRAP;
|
||||
}
|
||||
|
||||
static CustomDataType operation_get_result_type(const NodeVectorMathOperation operation)
|
||||
{
|
||||
switch (operation) {
|
||||
case NODE_VECTOR_MATH_ADD:
|
||||
case NODE_VECTOR_MATH_SUBTRACT:
|
||||
case NODE_VECTOR_MATH_MULTIPLY:
|
||||
case NODE_VECTOR_MATH_DIVIDE:
|
||||
case NODE_VECTOR_MATH_CROSS_PRODUCT:
|
||||
case NODE_VECTOR_MATH_PROJECT:
|
||||
case NODE_VECTOR_MATH_REFLECT:
|
||||
case NODE_VECTOR_MATH_SCALE:
|
||||
case NODE_VECTOR_MATH_NORMALIZE:
|
||||
case NODE_VECTOR_MATH_SNAP:
|
||||
case NODE_VECTOR_MATH_FLOOR:
|
||||
case NODE_VECTOR_MATH_CEIL:
|
||||
case NODE_VECTOR_MATH_MODULO:
|
||||
case NODE_VECTOR_MATH_FRACTION:
|
||||
case NODE_VECTOR_MATH_ABSOLUTE:
|
||||
case NODE_VECTOR_MATH_MINIMUM:
|
||||
case NODE_VECTOR_MATH_MAXIMUM:
|
||||
case NODE_VECTOR_MATH_WRAP:
|
||||
case NODE_VECTOR_MATH_SINE:
|
||||
case NODE_VECTOR_MATH_COSINE:
|
||||
case NODE_VECTOR_MATH_TANGENT:
|
||||
return CD_PROP_FLOAT3;
|
||||
case NODE_VECTOR_MATH_DOT_PRODUCT:
|
||||
case NODE_VECTOR_MATH_DISTANCE:
|
||||
case NODE_VECTOR_MATH_LENGTH:
|
||||
return CD_PROP_FLOAT;
|
||||
}
|
||||
|
||||
BLI_assert(false);
|
||||
return CD_PROP_FLOAT3;
|
||||
}
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
static void geo_node_attribute_vector_math_update(bNodeTree *UNUSED(ntree), bNode *node)
|
||||
{
|
||||
const NodeAttributeVectorMath *node_storage = (NodeAttributeVectorMath *)node->storage;
|
||||
const NodeVectorMathOperation operation = (const NodeVectorMathOperation)node_storage->operation;
|
||||
|
||||
update_attribute_input_socket_availabilities(
|
||||
*node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a);
|
||||
update_attribute_input_socket_availabilities(
|
||||
*node,
|
||||
"B",
|
||||
(GeometryNodeAttributeInputMode)node_storage->input_type_b,
|
||||
operation_use_input_b(operation));
|
||||
update_attribute_input_socket_availabilities(
|
||||
*node,
|
||||
"C",
|
||||
(GeometryNodeAttributeInputMode)node_storage->input_type_c,
|
||||
operation_use_input_c(operation));
|
||||
}
|
||||
|
||||
static void do_math_operation_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a,
|
||||
const Float3ReadAttribute &input_b,
|
||||
Float3WriteAttribute result,
|
||||
const NodeVectorMathOperation operation)
|
||||
{
|
||||
const int size = input_a.size();
|
||||
|
||||
Span<float3> span_a = input_a.get_span();
|
||||
Span<float3> span_b = input_b.get_span();
|
||||
MutableSpan<float3> span_result = result.get_span_for_write_only();
|
||||
|
||||
bool success = try_dispatch_float_math_fl3_fl3_to_fl3(
|
||||
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
|
||||
for (const int i : IndexRange(size)) {
|
||||
const float3 a = span_a[i];
|
||||
const float3 b = span_b[i];
|
||||
const float3 out = math_function(a, b);
|
||||
span_result[i] = out;
|
||||
}
|
||||
});
|
||||
|
||||
result.apply_span();
|
||||
|
||||
/* The operation is not supported by this node currently. */
|
||||
BLI_assert(success);
|
||||
UNUSED_VARS_NDEBUG(success);
|
||||
}
|
||||
|
||||
static void do_math_operation_fl3_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a,
|
||||
const Float3ReadAttribute &input_b,
|
||||
const Float3ReadAttribute &input_c,
|
||||
Float3WriteAttribute result,
|
||||
const NodeVectorMathOperation operation)
|
||||
{
|
||||
const int size = input_a.size();
|
||||
|
||||
Span<float3> span_a = input_a.get_span();
|
||||
Span<float3> span_b = input_b.get_span();
|
||||
Span<float3> span_c = input_c.get_span();
|
||||
MutableSpan<float3> span_result = result.get_span_for_write_only();
|
||||
|
||||
bool success = try_dispatch_float_math_fl3_fl3_fl3_to_fl3(
|
||||
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
|
||||
for (const int i : IndexRange(size)) {
|
||||
const float3 a = span_a[i];
|
||||
const float3 b = span_b[i];
|
||||
const float3 c = span_c[i];
|
||||
const float3 out = math_function(a, b, c);
|
||||
span_result[i] = out;
|
||||
}
|
||||
});
|
||||
|
||||
result.apply_span();
|
||||
|
||||
/* The operation is not supported by this node currently. */
|
||||
BLI_assert(success);
|
||||
UNUSED_VARS_NDEBUG(success);
|
||||
}
|
||||
|
||||
static void do_math_operation_fl3_fl3_to_fl(const Float3ReadAttribute &input_a,
|
||||
const Float3ReadAttribute &input_b,
|
||||
FloatWriteAttribute result,
|
||||
const NodeVectorMathOperation operation)
|
||||
{
|
||||
const int size = input_a.size();
|
||||
|
||||
Span<float3> span_a = input_a.get_span();
|
||||
Span<float3> span_b = input_b.get_span();
|
||||
MutableSpan<float> span_result = result.get_span_for_write_only();
|
||||
|
||||
bool success = try_dispatch_float_math_fl3_fl3_to_fl(
|
||||
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
|
||||
for (const int i : IndexRange(size)) {
|
||||
const float3 a = span_a[i];
|
||||
const float3 b = span_b[i];
|
||||
const float out = math_function(a, b);
|
||||
span_result[i] = out;
|
||||
}
|
||||
});
|
||||
|
||||
result.apply_span();
|
||||
|
||||
/* The operation is not supported by this node currently. */
|
||||
BLI_assert(success);
|
||||
UNUSED_VARS_NDEBUG(success);
|
||||
}
|
||||
|
||||
static void do_math_operation_fl3_fl_to_fl3(const Float3ReadAttribute &input_a,
|
||||
const FloatReadAttribute &input_b,
|
||||
Float3WriteAttribute result,
|
||||
const NodeVectorMathOperation operation)
|
||||
{
|
||||
const int size = input_a.size();
|
||||
|
||||
Span<float3> span_a = input_a.get_span();
|
||||
Span<float> span_b = input_b.get_span();
|
||||
MutableSpan<float3> span_result = result.get_span_for_write_only();
|
||||
|
||||
bool success = try_dispatch_float_math_fl3_fl_to_fl3(
|
||||
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
|
||||
for (const int i : IndexRange(size)) {
|
||||
const float3 a = span_a[i];
|
||||
const float b = span_b[i];
|
||||
const float3 out = math_function(a, b);
|
||||
span_result[i] = out;
|
||||
}
|
||||
});
|
||||
|
||||
result.apply_span();
|
||||
|
||||
/* The operation is not supported by this node currently. */
|
||||
BLI_assert(success);
|
||||
UNUSED_VARS_NDEBUG(success);
|
||||
}
|
||||
|
||||
static void do_math_operation_fl3_to_fl3(const Float3ReadAttribute &input_a,
|
||||
Float3WriteAttribute result,
|
||||
const NodeVectorMathOperation operation)
|
||||
{
|
||||
const int size = input_a.size();
|
||||
|
||||
Span<float3> span_a = input_a.get_span();
|
||||
MutableSpan<float3> span_result = result.get_span_for_write_only();
|
||||
|
||||
bool success = try_dispatch_float_math_fl3_to_fl3(
|
||||
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
|
||||
for (const int i : IndexRange(size)) {
|
||||
const float3 in = span_a[i];
|
||||
const float3 out = math_function(in);
|
||||
span_result[i] = out;
|
||||
}
|
||||
});
|
||||
|
||||
result.apply_span();
|
||||
|
||||
/* The operation is not supported by this node currently. */
|
||||
BLI_assert(success);
|
||||
UNUSED_VARS_NDEBUG(success);
|
||||
}
|
||||
|
||||
static void do_math_operation_fl3_to_fl(const Float3ReadAttribute &input_a,
|
||||
FloatWriteAttribute result,
|
||||
const NodeVectorMathOperation operation)
|
||||
{
|
||||
const int size = input_a.size();
|
||||
|
||||
Span<float3> span_a = input_a.get_span();
|
||||
MutableSpan<float> span_result = result.get_span_for_write_only();
|
||||
|
||||
bool success = try_dispatch_float_math_fl3_to_fl(
|
||||
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
|
||||
for (const int i : IndexRange(size)) {
|
||||
const float3 in = span_a[i];
|
||||
const float out = math_function(in);
|
||||
span_result[i] = out;
|
||||
}
|
||||
});
|
||||
|
||||
result.apply_span();
|
||||
|
||||
/* The operation is not supported by this node currently. */
|
||||
BLI_assert(success);
|
||||
UNUSED_VARS_NDEBUG(success);
|
||||
}
|
||||
|
||||
static void attribute_vector_math_calc(GeometryComponent &component,
|
||||
const GeoNodeExecParams ¶ms)
|
||||
{
|
||||
const bNode &node = params.node();
|
||||
const NodeAttributeVectorMath *node_storage = (const NodeAttributeVectorMath *)node.storage;
|
||||
const NodeVectorMathOperation operation = (NodeVectorMathOperation)node_storage->operation;
|
||||
|
||||
/* The number and type of the input attribute depend on the operation. */
|
||||
const CustomDataType read_type_a = CD_PROP_FLOAT3;
|
||||
const bool use_input_b = operation_use_input_b(operation);
|
||||
const CustomDataType read_type_b = operation_get_read_type_b(operation);
|
||||
const bool use_input_c = operation_use_input_c(operation);
|
||||
const CustomDataType read_type_c = CD_PROP_FLOAT3;
|
||||
|
||||
/* The result domain is always point for now. */
|
||||
const CustomDataType result_type = operation_get_result_type(operation);
|
||||
const AttributeDomain result_domain = ATTR_DOMAIN_POINT;
|
||||
|
||||
ReadAttributePtr attribute_a = params.get_input_attribute(
|
||||
"A", component, result_domain, read_type_a, nullptr);
|
||||
if (!attribute_a) {
|
||||
return;
|
||||
}
|
||||
ReadAttributePtr attribute_b;
|
||||
ReadAttributePtr attribute_c;
|
||||
if (use_input_b) {
|
||||
attribute_b = params.get_input_attribute("B", component, result_domain, read_type_b, nullptr);
|
||||
if (!attribute_b) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (use_input_c) {
|
||||
attribute_c = params.get_input_attribute("C", component, result_domain, read_type_c, nullptr);
|
||||
if (!attribute_c) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get result attribute first, in case it has to overwrite one of the existing attributes. */
|
||||
const std::string result_name = params.get_input<std::string>("Result");
|
||||
WriteAttributePtr attribute_result = component.attribute_try_ensure_for_write(
|
||||
result_name, result_domain, result_type);
|
||||
if (!attribute_result) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (operation) {
|
||||
case NODE_VECTOR_MATH_ADD:
|
||||
case NODE_VECTOR_MATH_SUBTRACT:
|
||||
case NODE_VECTOR_MATH_MULTIPLY:
|
||||
case NODE_VECTOR_MATH_DIVIDE:
|
||||
case NODE_VECTOR_MATH_CROSS_PRODUCT:
|
||||
case NODE_VECTOR_MATH_PROJECT:
|
||||
case NODE_VECTOR_MATH_REFLECT:
|
||||
case NODE_VECTOR_MATH_SNAP:
|
||||
case NODE_VECTOR_MATH_MODULO:
|
||||
case NODE_VECTOR_MATH_MINIMUM:
|
||||
case NODE_VECTOR_MATH_MAXIMUM:
|
||||
do_math_operation_fl3_fl3_to_fl3(
|
||||
std::move(attribute_a), std::move(attribute_b), std::move(attribute_result), operation);
|
||||
break;
|
||||
case NODE_VECTOR_MATH_DOT_PRODUCT:
|
||||
case NODE_VECTOR_MATH_DISTANCE:
|
||||
do_math_operation_fl3_fl3_to_fl(
|
||||
std::move(attribute_a), std::move(attribute_b), std::move(attribute_result), operation);
|
||||
break;
|
||||
case NODE_VECTOR_MATH_LENGTH:
|
||||
do_math_operation_fl3_to_fl(std::move(attribute_a), std::move(attribute_result), operation);
|
||||
break;
|
||||
case NODE_VECTOR_MATH_SCALE:
|
||||
do_math_operation_fl3_fl_to_fl3(
|
||||
std::move(attribute_a), std::move(attribute_b), std::move(attribute_result), operation);
|
||||
break;
|
||||
case NODE_VECTOR_MATH_NORMALIZE:
|
||||
case NODE_VECTOR_MATH_FLOOR:
|
||||
case NODE_VECTOR_MATH_CEIL:
|
||||
case NODE_VECTOR_MATH_FRACTION:
|
||||
case NODE_VECTOR_MATH_ABSOLUTE:
|
||||
case NODE_VECTOR_MATH_SINE:
|
||||
case NODE_VECTOR_MATH_COSINE:
|
||||
case NODE_VECTOR_MATH_TANGENT:
|
||||
do_math_operation_fl3_to_fl3(std::move(attribute_a), std::move(attribute_result), operation);
|
||||
break;
|
||||
case NODE_VECTOR_MATH_WRAP:
|
||||
do_math_operation_fl3_fl3_fl3_to_fl3(std::move(attribute_a),
|
||||
std::move(attribute_b),
|
||||
std::move(attribute_c),
|
||||
std::move(attribute_result),
|
||||
operation);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void geo_node_attribute_vector_math_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
attribute_vector_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params);
|
||||
}
|
||||
if (geometry_set.has<PointCloudComponent>()) {
|
||||
attribute_vector_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(),
|
||||
params);
|
||||
}
|
||||
|
||||
params.set_output("Geometry", geometry_set);
|
||||
}
|
||||
|
||||
} // namespace blender::nodes
|
||||
|
||||
void register_node_type_geo_attribute_vector_math()
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
geo_node_type_base(
|
||||
&ntype, GEO_NODE_ATTRIBUTE_VECTOR_MATH, "Attribute Vector Math", NODE_CLASS_ATTRIBUTE, 0);
|
||||
node_type_socket_templates(
|
||||
&ntype, geo_node_attribute_vector_math_in, geo_node_attribute_vector_math_out);
|
||||
ntype.geometry_node_execute = blender::nodes::geo_node_attribute_vector_math_exec;
|
||||
node_type_update(&ntype, blender::nodes::geo_node_attribute_vector_math_update);
|
||||
node_type_init(&ntype, geo_node_attribute_vector_math_init);
|
||||
node_type_storage(
|
||||
&ntype, "NodeAttributeVectorMath", node_free_standard_storage, node_copy_standard_storage);
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
@@ -182,7 +182,7 @@ static void join_attributes(Span<const GeometryComponent *> src_components,
|
||||
write_attribute->domain() != domain) {
|
||||
continue;
|
||||
}
|
||||
fn::GMutableSpan dst_span = write_attribute->get_span();
|
||||
fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only();
|
||||
fill_new_attribute(src_components, attribute_name, data_type, domain, dst_span);
|
||||
write_attribute->apply_span();
|
||||
}
|
||||
|
@@ -16,9 +16,11 @@
|
||||
|
||||
#include "BLI_float3.hh"
|
||||
#include "BLI_hash.h"
|
||||
#include "BLI_kdtree.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_rand.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_timeit.hh"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
@@ -34,7 +36,7 @@
|
||||
|
||||
static bNodeSocketTemplate geo_node_point_distribute_in[] = {
|
||||
{SOCK_GEOMETRY, N_("Geometry")},
|
||||
{SOCK_FLOAT, N_("Distance Min"), 0.1f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
|
||||
{SOCK_FLOAT, N_("Distance Min"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
|
||||
{SOCK_FLOAT, N_("Density Max"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
|
||||
{SOCK_STRING, N_("Density Attribute")},
|
||||
{SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
|
||||
@@ -55,203 +57,208 @@ static void node_point_distribute_update(bNodeTree *UNUSED(ntree), bNode *node)
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
static Vector<float3> random_scatter_points_from_mesh(const Mesh *mesh,
|
||||
const float density,
|
||||
const FloatReadAttribute &density_factors,
|
||||
Vector<int> &r_ids,
|
||||
const int seed)
|
||||
/**
|
||||
* Use an arbitrary choice of axes for a usable rotation attribute directly out of this node.
|
||||
*/
|
||||
static float3 normal_to_euler_rotation(const float3 normal)
|
||||
{
|
||||
float quat[4];
|
||||
vec_to_quat(quat, normal, OB_NEGZ, OB_POSY);
|
||||
float3 rotation;
|
||||
quat_to_eul(rotation, quat);
|
||||
return rotation;
|
||||
}
|
||||
|
||||
static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
|
||||
{
|
||||
/* This only updates a cache and can be considered to be logically const. */
|
||||
const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(mesh));
|
||||
const int looptris_len = BKE_mesh_runtime_looptri_len(mesh);
|
||||
const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh));
|
||||
const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh);
|
||||
return {looptris, looptris_len};
|
||||
}
|
||||
|
||||
Vector<float3> points;
|
||||
static void sample_mesh_surface(const Mesh &mesh,
|
||||
const float base_density,
|
||||
const FloatReadAttribute *density_factors,
|
||||
const int seed,
|
||||
Vector<float3> &r_positions,
|
||||
Vector<float3> &r_bary_coords,
|
||||
Vector<int> &r_looptri_indices)
|
||||
{
|
||||
Span<MLoopTri> looptris = get_mesh_looptris(mesh);
|
||||
|
||||
for (const int looptri_index : IndexRange(looptris_len)) {
|
||||
for (const int looptri_index : looptris.index_range()) {
|
||||
const MLoopTri &looptri = looptris[looptri_index];
|
||||
const int v0_index = mesh->mloop[looptri.tri[0]].v;
|
||||
const int v1_index = mesh->mloop[looptri.tri[1]].v;
|
||||
const int v2_index = mesh->mloop[looptri.tri[2]].v;
|
||||
const float3 v0_pos = mesh->mvert[v0_index].co;
|
||||
const float3 v1_pos = mesh->mvert[v1_index].co;
|
||||
const float3 v2_pos = mesh->mvert[v2_index].co;
|
||||
const float v0_density_factor = std::max(0.0f, density_factors[v0_index]);
|
||||
const float v1_density_factor = std::max(0.0f, density_factors[v1_index]);
|
||||
const float v2_density_factor = std::max(0.0f, density_factors[v2_index]);
|
||||
const float looptri_density_factor = (v0_density_factor + v1_density_factor +
|
||||
v2_density_factor) /
|
||||
3.0f;
|
||||
const int v0_index = mesh.mloop[looptri.tri[0]].v;
|
||||
const int v1_index = mesh.mloop[looptri.tri[1]].v;
|
||||
const int v2_index = mesh.mloop[looptri.tri[2]].v;
|
||||
const float3 v0_pos = mesh.mvert[v0_index].co;
|
||||
const float3 v1_pos = mesh.mvert[v1_index].co;
|
||||
const float3 v2_pos = mesh.mvert[v2_index].co;
|
||||
|
||||
float looptri_density_factor = 1.0f;
|
||||
if (density_factors != nullptr) {
|
||||
const float v0_density_factor = std::max(0.0f, (*density_factors)[v0_index]);
|
||||
const float v1_density_factor = std::max(0.0f, (*density_factors)[v1_index]);
|
||||
const float v2_density_factor = std::max(0.0f, (*density_factors)[v2_index]);
|
||||
looptri_density_factor = (v0_density_factor + v1_density_factor + v2_density_factor) / 3.0f;
|
||||
}
|
||||
const float area = area_tri_v3(v0_pos, v1_pos, v2_pos);
|
||||
|
||||
const int looptri_seed = BLI_hash_int(looptri_index + seed);
|
||||
RandomNumberGenerator looptri_rng(looptri_seed);
|
||||
|
||||
const float points_amount_fl = area * density * looptri_density_factor;
|
||||
const float points_amount_fl = area * base_density * looptri_density_factor;
|
||||
const float add_point_probability = fractf(points_amount_fl);
|
||||
const bool add_point = add_point_probability > looptri_rng.get_float();
|
||||
const int point_amount = (int)points_amount_fl + (int)add_point;
|
||||
|
||||
for (int i = 0; i < point_amount; i++) {
|
||||
const float3 bary_coords = looptri_rng.get_barycentric_coordinates();
|
||||
const float3 bary_coord = looptri_rng.get_barycentric_coordinates();
|
||||
float3 point_pos;
|
||||
interp_v3_v3v3v3(point_pos, v0_pos, v1_pos, v2_pos, bary_coords);
|
||||
points.append(point_pos);
|
||||
|
||||
/* Build a hash stable even when the mesh is deformed. */
|
||||
r_ids.append(((int)(bary_coords.hash()) + looptri_index));
|
||||
interp_v3_v3v3v3(point_pos, v0_pos, v1_pos, v2_pos, bary_coord);
|
||||
r_positions.append(point_pos);
|
||||
r_bary_coords.append(bary_coord);
|
||||
r_looptri_indices.append(looptri_index);
|
||||
}
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
struct RayCastAll_Data {
|
||||
void *bvhdata;
|
||||
|
||||
BVHTree_RayCastCallback raycast_callback;
|
||||
|
||||
/** The original coordinate the result point was projected from. */
|
||||
float2 raystart;
|
||||
|
||||
const Mesh *mesh;
|
||||
float base_weight;
|
||||
FloatReadAttribute *density_factors;
|
||||
Vector<float3> *projected_points;
|
||||
Vector<int> *stable_ids;
|
||||
float cur_point_weight;
|
||||
};
|
||||
|
||||
static void project_2d_bvh_callback(void *userdata,
|
||||
int index,
|
||||
const BVHTreeRay *ray,
|
||||
BVHTreeRayHit *hit)
|
||||
BLI_NOINLINE static KDTree_3d *build_kdtree(Span<float3> positions)
|
||||
{
|
||||
struct RayCastAll_Data *data = (RayCastAll_Data *)userdata;
|
||||
data->raycast_callback(data->bvhdata, index, ray, hit);
|
||||
if (hit->index != -1) {
|
||||
/* This only updates a cache and can be considered to be logically const. */
|
||||
const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(data->mesh));
|
||||
const MVert *mvert = data->mesh->mvert;
|
||||
KDTree_3d *kdtree = BLI_kdtree_3d_new(positions.size());
|
||||
for (const int i : positions.index_range()) {
|
||||
BLI_kdtree_3d_insert(kdtree, i, positions[i]);
|
||||
}
|
||||
BLI_kdtree_3d_balance(kdtree);
|
||||
return kdtree;
|
||||
}
|
||||
|
||||
const MLoopTri &looptri = looptris[index];
|
||||
const FloatReadAttribute &density_factors = data->density_factors[0];
|
||||
BLI_NOINLINE static void update_elimination_mask_for_close_points(
|
||||
Span<float3> positions, const float minimum_distance, MutableSpan<bool> elimination_mask)
|
||||
{
|
||||
if (minimum_distance <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int v0_index = data->mesh->mloop[looptri.tri[0]].v;
|
||||
const int v1_index = data->mesh->mloop[looptri.tri[1]].v;
|
||||
const int v2_index = data->mesh->mloop[looptri.tri[2]].v;
|
||||
KDTree_3d *kdtree = build_kdtree(positions);
|
||||
|
||||
for (const int i : positions.index_range()) {
|
||||
if (elimination_mask[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct CallbackData {
|
||||
int index;
|
||||
MutableSpan<bool> elimination_mask;
|
||||
} callback_data = {i, elimination_mask};
|
||||
|
||||
BLI_kdtree_3d_range_search_cb(
|
||||
kdtree,
|
||||
positions[i],
|
||||
minimum_distance,
|
||||
[](void *user_data, int index, const float *UNUSED(co), float UNUSED(dist_sq)) {
|
||||
CallbackData &callback_data = *static_cast<CallbackData *>(user_data);
|
||||
if (index != callback_data.index) {
|
||||
callback_data.elimination_mask[index] = true;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
&callback_data);
|
||||
}
|
||||
BLI_kdtree_3d_free(kdtree);
|
||||
}
|
||||
|
||||
BLI_NOINLINE static void update_elimination_mask_based_on_density_factors(
|
||||
const Mesh &mesh,
|
||||
const FloatReadAttribute &density_factors,
|
||||
Span<float3> bary_coords,
|
||||
Span<int> looptri_indices,
|
||||
MutableSpan<bool> elimination_mask)
|
||||
{
|
||||
Span<MLoopTri> looptris = get_mesh_looptris(mesh);
|
||||
for (const int i : bary_coords.index_range()) {
|
||||
if (elimination_mask[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const MLoopTri &looptri = looptris[looptri_indices[i]];
|
||||
const float3 bary_coord = bary_coords[i];
|
||||
|
||||
const int v0_index = mesh.mloop[looptri.tri[0]].v;
|
||||
const int v1_index = mesh.mloop[looptri.tri[1]].v;
|
||||
const int v2_index = mesh.mloop[looptri.tri[2]].v;
|
||||
|
||||
const float v0_density_factor = std::max(0.0f, density_factors[v0_index]);
|
||||
const float v1_density_factor = std::max(0.0f, density_factors[v1_index]);
|
||||
const float v2_density_factor = std::max(0.0f, density_factors[v2_index]);
|
||||
|
||||
/* Calculate barycentric weights for hit point. */
|
||||
float3 weights;
|
||||
interp_weights_tri_v3(
|
||||
weights, mvert[v0_index].co, mvert[v1_index].co, mvert[v2_index].co, hit->co);
|
||||
const float probablity = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y +
|
||||
v2_density_factor * bary_coord.z;
|
||||
|
||||
float point_weight = weights[0] * v0_density_factor + weights[1] * v1_density_factor +
|
||||
weights[2] * v2_density_factor;
|
||||
|
||||
point_weight *= data->base_weight;
|
||||
|
||||
if (point_weight >= FLT_EPSILON && data->cur_point_weight <= point_weight) {
|
||||
data->projected_points->append(hit->co);
|
||||
|
||||
/* Build a hash stable even when the mesh is deformed. */
|
||||
data->stable_ids->append((int)data->raystart.hash());
|
||||
const float hash = BLI_hash_int_01(bary_coord.hash());
|
||||
if (hash > probablity) {
|
||||
elimination_mask[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Vector<float3> poisson_scatter_points_from_mesh(const Mesh *mesh,
|
||||
const float density,
|
||||
const float minimum_distance,
|
||||
const FloatReadAttribute &density_factors,
|
||||
Vector<int> &r_ids,
|
||||
const int seed)
|
||||
BLI_NOINLINE static void eliminate_points_based_on_mask(Span<bool> elimination_mask,
|
||||
Vector<float3> &positions,
|
||||
Vector<float3> &bary_coords,
|
||||
Vector<int> &looptri_indices)
|
||||
{
|
||||
Vector<float3> points;
|
||||
|
||||
if (minimum_distance <= FLT_EPSILON || density <= FLT_EPSILON) {
|
||||
return points;
|
||||
}
|
||||
|
||||
/* Scatter points randomly on the mesh with higher density (5-7) times higher than desired for
|
||||
* good quality possion disk distributions. */
|
||||
int quality = 5;
|
||||
const int output_points_target = 1000;
|
||||
points.resize(output_points_target * quality);
|
||||
|
||||
const float required_area = output_points_target *
|
||||
(2.0f * sqrtf(3.0f) * minimum_distance * minimum_distance);
|
||||
const float point_scale_multiplier = sqrtf(required_area);
|
||||
|
||||
{
|
||||
const int rnd_seed = BLI_hash_int(seed);
|
||||
RandomNumberGenerator point_rng(rnd_seed);
|
||||
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
points[i].x = point_rng.get_float() * point_scale_multiplier;
|
||||
points[i].y = point_rng.get_float() * point_scale_multiplier;
|
||||
points[i].z = 0.0f;
|
||||
for (int i = positions.size() - 1; i >= 0; i--) {
|
||||
if (elimination_mask[i]) {
|
||||
positions.remove_and_reorder(i);
|
||||
bary_coords.remove_and_reorder(i);
|
||||
looptri_indices.remove_and_reorder(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Eliminate the scattered points until we get a possion distribution. */
|
||||
Vector<float3> output_points(output_points_target);
|
||||
BLI_NOINLINE static void compute_remaining_point_data(const Mesh &mesh,
|
||||
Span<float3> bary_coords,
|
||||
Span<int> looptri_indices,
|
||||
MutableSpan<float3> r_normals,
|
||||
MutableSpan<int> r_ids,
|
||||
MutableSpan<float3> r_rotations)
|
||||
{
|
||||
Span<MLoopTri> looptris = get_mesh_looptris(mesh);
|
||||
for (const int i : bary_coords.index_range()) {
|
||||
const int looptri_index = looptri_indices[i];
|
||||
const MLoopTri &looptri = looptris[looptri_index];
|
||||
const float3 &bary_coord = bary_coords[i];
|
||||
|
||||
const float3 bounds_max = float3(point_scale_multiplier, point_scale_multiplier, 0);
|
||||
poisson_disk_point_elimination(&points, &output_points, 2.0f * minimum_distance, bounds_max);
|
||||
Vector<float3> final_points;
|
||||
r_ids.reserve(output_points_target);
|
||||
final_points.reserve(output_points_target);
|
||||
const int v0_index = mesh.mloop[looptri.tri[0]].v;
|
||||
const int v1_index = mesh.mloop[looptri.tri[1]].v;
|
||||
const int v2_index = mesh.mloop[looptri.tri[2]].v;
|
||||
const float3 v0_pos = mesh.mvert[v0_index].co;
|
||||
const float3 v1_pos = mesh.mvert[v1_index].co;
|
||||
const float3 v2_pos = mesh.mvert[v2_index].co;
|
||||
|
||||
/* Check if we have any points we should remove from the final possion distribition. */
|
||||
BVHTreeFromMesh treedata;
|
||||
BKE_bvhtree_from_mesh_get(&treedata, const_cast<Mesh *>(mesh), BVHTREE_FROM_LOOPTRI, 2);
|
||||
|
||||
float3 bb_min, bb_max;
|
||||
BLI_bvhtree_get_bounding_box(treedata.tree, bb_min, bb_max);
|
||||
|
||||
struct RayCastAll_Data data;
|
||||
data.bvhdata = &treedata;
|
||||
data.raycast_callback = treedata.raycast_callback;
|
||||
data.mesh = mesh;
|
||||
data.projected_points = &final_points;
|
||||
data.stable_ids = &r_ids;
|
||||
data.density_factors = const_cast<FloatReadAttribute *>(&density_factors);
|
||||
data.base_weight = std::min(
|
||||
1.0f, density / (output_points.size() / (point_scale_multiplier * point_scale_multiplier)));
|
||||
|
||||
const float max_dist = bb_max[2] - bb_min[2] + 2.0f;
|
||||
const float3 dir = float3(0, 0, -1);
|
||||
float3 raystart;
|
||||
raystart.z = bb_max[2] + 1.0f;
|
||||
|
||||
float tile_start_x_coord = bb_min[0];
|
||||
int tile_repeat_x = ceilf((bb_max[0] - bb_min[0]) / point_scale_multiplier);
|
||||
|
||||
float tile_start_y_coord = bb_min[1];
|
||||
int tile_repeat_y = ceilf((bb_max[1] - bb_min[1]) / point_scale_multiplier);
|
||||
|
||||
for (int x = 0; x < tile_repeat_x; x++) {
|
||||
float tile_curr_x_coord = x * point_scale_multiplier + tile_start_x_coord;
|
||||
for (int y = 0; y < tile_repeat_y; y++) {
|
||||
float tile_curr_y_coord = y * point_scale_multiplier + tile_start_y_coord;
|
||||
for (int idx = 0; idx < output_points.size(); idx++) {
|
||||
raystart.x = output_points[idx].x + tile_curr_x_coord;
|
||||
raystart.y = output_points[idx].y + tile_curr_y_coord;
|
||||
|
||||
data.cur_point_weight = (float)idx / (float)output_points.size();
|
||||
data.raystart = raystart;
|
||||
|
||||
BLI_bvhtree_ray_cast_all(
|
||||
treedata.tree, raystart, dir, 0.0f, max_dist, project_2d_bvh_callback, &data);
|
||||
}
|
||||
}
|
||||
r_ids[i] = (int)(bary_coord.hash()) + looptri_index;
|
||||
normal_tri_v3(r_normals[i], v0_pos, v1_pos, v2_pos);
|
||||
r_rotations[i] = normal_to_euler_rotation(r_normals[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return final_points;
|
||||
static void sample_mesh_surface_with_minimum_distance(const Mesh &mesh,
|
||||
const float max_density,
|
||||
const float minimum_distance,
|
||||
const FloatReadAttribute &density_factors,
|
||||
const int seed,
|
||||
Vector<float3> &r_positions,
|
||||
Vector<float3> &r_bary_coords,
|
||||
Vector<int> &r_looptri_indices)
|
||||
{
|
||||
sample_mesh_surface(
|
||||
mesh, max_density, nullptr, seed, r_positions, r_bary_coords, r_looptri_indices);
|
||||
Array<bool> elimination_mask(r_positions.size(), false);
|
||||
update_elimination_mask_for_close_points(r_positions, minimum_distance, elimination_mask);
|
||||
update_elimination_mask_based_on_density_factors(
|
||||
mesh, density_factors, r_bary_coords, r_looptri_indices, elimination_mask);
|
||||
eliminate_points_based_on_mask(elimination_mask, r_positions, r_bary_coords, r_looptri_indices);
|
||||
}
|
||||
|
||||
static void geo_node_point_distribute_exec(GeoNodeExecParams params)
|
||||
@@ -287,24 +294,37 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
|
||||
density_attribute, ATTR_DOMAIN_POINT, 1.0f);
|
||||
const int seed = params.get_input<int>("Seed");
|
||||
|
||||
Vector<int> stable_ids;
|
||||
Vector<float3> points;
|
||||
Vector<float3> positions;
|
||||
Vector<float3> bary_coords;
|
||||
Vector<int> looptri_indices;
|
||||
switch (distribute_method) {
|
||||
case GEO_NODE_POINT_DISTRIBUTE_RANDOM:
|
||||
points = random_scatter_points_from_mesh(
|
||||
mesh_in, density, density_factors, stable_ids, seed);
|
||||
sample_mesh_surface(
|
||||
*mesh_in, density, &density_factors, seed, positions, bary_coords, looptri_indices);
|
||||
break;
|
||||
case GEO_NODE_POINT_DISTRIBUTE_POISSON:
|
||||
const float min_dist = params.extract_input<float>("Distance Min");
|
||||
points = poisson_scatter_points_from_mesh(
|
||||
mesh_in, density, min_dist, density_factors, stable_ids, seed);
|
||||
const float minimum_distance = params.extract_input<float>("Distance Min");
|
||||
sample_mesh_surface_with_minimum_distance(*mesh_in,
|
||||
density,
|
||||
minimum_distance,
|
||||
density_factors,
|
||||
seed,
|
||||
positions,
|
||||
bary_coords,
|
||||
looptri_indices);
|
||||
break;
|
||||
}
|
||||
const int tot_points = positions.size();
|
||||
Array<float3> normals(tot_points);
|
||||
Array<int> stable_ids(tot_points);
|
||||
Array<float3> rotations(tot_points);
|
||||
compute_remaining_point_data(
|
||||
*mesh_in, bary_coords, looptri_indices, normals, stable_ids, rotations);
|
||||
|
||||
PointCloud *pointcloud = BKE_pointcloud_new_nomain(points.size());
|
||||
memcpy(pointcloud->co, points.data(), sizeof(float3) * points.size());
|
||||
for (const int i : points.index_range()) {
|
||||
*(float3 *)(pointcloud->co + i) = points[i];
|
||||
PointCloud *pointcloud = BKE_pointcloud_new_nomain(tot_points);
|
||||
memcpy(pointcloud->co, positions.data(), sizeof(float3) * tot_points);
|
||||
for (const int i : positions.index_range()) {
|
||||
*(float3 *)(pointcloud->co + i) = positions[i];
|
||||
pointcloud->radius[i] = 0.05f;
|
||||
}
|
||||
|
||||
@@ -312,11 +332,29 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
|
||||
geometry_set_out.get_component_for_write<PointCloudComponent>();
|
||||
point_component.replace(pointcloud);
|
||||
|
||||
Int32WriteAttribute stable_id_attribute = point_component.attribute_try_ensure_for_write(
|
||||
"id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
|
||||
MutableSpan<int> stable_ids_span = stable_id_attribute.get_span();
|
||||
stable_ids_span.copy_from(stable_ids);
|
||||
stable_id_attribute.apply_span();
|
||||
{
|
||||
Int32WriteAttribute stable_id_attribute = point_component.attribute_try_ensure_for_write(
|
||||
"id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
|
||||
MutableSpan<int> stable_ids_span = stable_id_attribute.get_span();
|
||||
stable_ids_span.copy_from(stable_ids);
|
||||
stable_id_attribute.apply_span();
|
||||
}
|
||||
|
||||
{
|
||||
Float3WriteAttribute normals_attribute = point_component.attribute_try_ensure_for_write(
|
||||
"normal", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
|
||||
MutableSpan<float3> normals_span = normals_attribute.get_span();
|
||||
normals_span.copy_from(normals);
|
||||
normals_attribute.apply_span();
|
||||
}
|
||||
|
||||
{
|
||||
Float3WriteAttribute rotations_attribute = point_component.attribute_try_ensure_for_write(
|
||||
"rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
|
||||
MutableSpan<float3> rotations_span = rotations_attribute.get_span();
|
||||
rotations_span.copy_from(rotations);
|
||||
rotations_attribute.apply_span();
|
||||
}
|
||||
|
||||
params.set_output("Geometry", std::move(geometry_set_out));
|
||||
}
|
||||
|
@@ -1,281 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Based on Cem Yuksel. 2015. Sample Elimination for Generating Poisson Disk Sample
|
||||
* ! Sets. Computer Graphics Forum 34, 2 (May 2015), 25-32.
|
||||
* ! http://www.cemyuksel.com/research/sampleelimination/
|
||||
* Copyright (c) 2016, Cem Yuksel <cem@cemyuksel.com>
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
#include "BLI_inplace_priority_queue.hh"
|
||||
#include "BLI_kdtree.h"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
static void tile_point(Vector<float3> *tiled_points,
|
||||
Vector<size_t> *indices,
|
||||
const float maximum_distance,
|
||||
const float3 boundbox,
|
||||
float3 const &point,
|
||||
size_t index,
|
||||
int dimension = 0)
|
||||
{
|
||||
for (int dimension_iter = dimension; dimension_iter < 3; dimension_iter++) {
|
||||
if (boundbox[dimension_iter] - point[dimension_iter] < maximum_distance) {
|
||||
float3 point_tiled = point;
|
||||
point_tiled[dimension_iter] -= boundbox[dimension_iter];
|
||||
|
||||
tiled_points->append(point_tiled);
|
||||
indices->append(index);
|
||||
|
||||
tile_point(tiled_points,
|
||||
indices,
|
||||
maximum_distance,
|
||||
boundbox,
|
||||
point_tiled,
|
||||
index,
|
||||
dimension_iter + 1);
|
||||
}
|
||||
|
||||
if (point[dimension_iter] < maximum_distance) {
|
||||
float3 point_tiled = point;
|
||||
point_tiled[dimension_iter] += boundbox[dimension_iter];
|
||||
|
||||
tiled_points->append(point_tiled);
|
||||
indices->append(index);
|
||||
|
||||
tile_point(tiled_points,
|
||||
indices,
|
||||
maximum_distance,
|
||||
boundbox,
|
||||
point_tiled,
|
||||
index,
|
||||
dimension_iter + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the weight the point gets based on the distance to another point.
|
||||
*/
|
||||
static float point_weight_influence_get(const float maximum_distance,
|
||||
const float minimum_distance,
|
||||
float distance)
|
||||
{
|
||||
const float alpha = 8.0f;
|
||||
|
||||
if (distance < minimum_distance) {
|
||||
distance = minimum_distance;
|
||||
}
|
||||
|
||||
return std::pow(1.0f - distance / maximum_distance, alpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* Weight each point based on their proximity to its neighbors
|
||||
*
|
||||
* For each index in the weight array add a weight based on the proximity the
|
||||
* corresponding point has with its neighbors.
|
||||
*/
|
||||
static void points_distance_weight_calculate(Vector<float> *weights,
|
||||
const size_t point_id,
|
||||
const float3 *input_points,
|
||||
const void *kd_tree,
|
||||
const float minimum_distance,
|
||||
const float maximum_distance,
|
||||
InplacePriorityQueue<float> *heap)
|
||||
{
|
||||
KDTreeNearest_3d *nearest_point = nullptr;
|
||||
int neighbors = BLI_kdtree_3d_range_search(
|
||||
(KDTree_3d *)kd_tree, input_points[point_id], &nearest_point, maximum_distance);
|
||||
|
||||
for (int i = 0; i < neighbors; i++) {
|
||||
size_t neighbor_point_id = nearest_point[i].index;
|
||||
|
||||
if (neighbor_point_id >= weights->size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* The point should not influence itself. */
|
||||
if (neighbor_point_id == point_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const float weight_influence = point_weight_influence_get(
|
||||
maximum_distance, minimum_distance, nearest_point[i].dist);
|
||||
|
||||
/* In the first pass we just the weights. */
|
||||
if (heap == nullptr) {
|
||||
(*weights)[point_id] += weight_influence;
|
||||
}
|
||||
/* When we run again we need to update the weights and the heap. */
|
||||
else {
|
||||
(*weights)[neighbor_point_id] -= weight_influence;
|
||||
heap->priority_decreased(neighbor_point_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (nearest_point) {
|
||||
MEM_freeN(nearest_point);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum radius fraction used by the default weight function.
|
||||
*/
|
||||
static float weight_limit_fraction_get(const size_t input_size, const size_t output_size)
|
||||
{
|
||||
const float beta = 0.65f;
|
||||
const float gamma = 1.5f;
|
||||
float ratio = float(output_size) / float(input_size);
|
||||
return (1.0f - std::pow(ratio, gamma)) * beta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tile the input points.
|
||||
*/
|
||||
static void points_tiling(const float3 *input_points,
|
||||
const size_t input_size,
|
||||
void **kd_tree,
|
||||
const float maximum_distance,
|
||||
const float3 boundbox)
|
||||
|
||||
{
|
||||
Vector<float3> tiled_points(input_points, input_points + input_size);
|
||||
Vector<size_t> indices(input_size);
|
||||
|
||||
for (size_t i = 0; i < input_size; i++) {
|
||||
indices[i] = i;
|
||||
}
|
||||
|
||||
/* Tile the tree based on the boundbox. */
|
||||
for (size_t i = 0; i < input_size; i++) {
|
||||
tile_point(&tiled_points, &indices, maximum_distance, boundbox, input_points[i], i);
|
||||
}
|
||||
|
||||
/* Build a new tree with the new indices and tiled points. */
|
||||
*kd_tree = BLI_kdtree_3d_new(tiled_points.size());
|
||||
for (size_t i = 0; i < tiled_points.size(); i++) {
|
||||
BLI_kdtree_3d_insert(*(KDTree_3d **)kd_tree, indices[i], tiled_points[i]);
|
||||
}
|
||||
BLI_kdtree_3d_balance(*(KDTree_3d **)kd_tree);
|
||||
}
|
||||
|
||||
static void weighted_sample_elimination(const float3 *input_points,
|
||||
const size_t input_size,
|
||||
float3 *output_points,
|
||||
const size_t output_size,
|
||||
const float maximum_distance,
|
||||
const float3 boundbox,
|
||||
const bool do_copy_eliminated)
|
||||
{
|
||||
const float minimum_distance = maximum_distance *
|
||||
weight_limit_fraction_get(input_size, output_size);
|
||||
|
||||
void *kd_tree = nullptr;
|
||||
points_tiling(input_points, input_size, &kd_tree, maximum_distance, boundbox);
|
||||
|
||||
/* Assign weights to each sample. */
|
||||
Vector<float> weights(input_size, 0.0f);
|
||||
for (size_t point_id = 0; point_id < weights.size(); point_id++) {
|
||||
points_distance_weight_calculate(
|
||||
&weights, point_id, input_points, kd_tree, minimum_distance, maximum_distance, nullptr);
|
||||
}
|
||||
|
||||
/* Remove the points based on their weight. */
|
||||
InplacePriorityQueue<float> heap(weights);
|
||||
|
||||
size_t sample_size = input_size;
|
||||
while (sample_size > output_size) {
|
||||
/* For each sample around it, remove its weight contribution and update the heap. */
|
||||
size_t point_id = heap.pop_index();
|
||||
points_distance_weight_calculate(
|
||||
&weights, point_id, input_points, kd_tree, minimum_distance, maximum_distance, &heap);
|
||||
sample_size--;
|
||||
}
|
||||
|
||||
/* Copy the samples to the output array. */
|
||||
size_t target_size = do_copy_eliminated ? input_size : output_size;
|
||||
for (size_t i = 0; i < target_size; i++) {
|
||||
size_t index = heap.all_indices()[i];
|
||||
output_points[i] = input_points[index];
|
||||
}
|
||||
|
||||
/* Cleanup. */
|
||||
BLI_kdtree_3d_free((KDTree_3d *)kd_tree);
|
||||
}
|
||||
|
||||
static void progressive_sampling_reorder(Vector<float3> *output_points,
|
||||
float maximum_density,
|
||||
float3 boundbox)
|
||||
{
|
||||
/* Re-order the points for progressive sampling. */
|
||||
Vector<float3> temporary_points(output_points->size());
|
||||
float3 *source_points = output_points->data();
|
||||
float3 *dest_points = temporary_points.data();
|
||||
size_t source_size = output_points->size();
|
||||
size_t dest_size = 0;
|
||||
|
||||
while (source_size >= 3) {
|
||||
dest_size = source_size * 0.5f;
|
||||
|
||||
/* Changes the weight function radius using half of the number of samples.
|
||||
* It is used for progressive sampling. */
|
||||
maximum_density *= std::sqrt(2.0f);
|
||||
weighted_sample_elimination(
|
||||
source_points, source_size, dest_points, dest_size, maximum_density, boundbox, true);
|
||||
|
||||
if (dest_points != output_points->data()) {
|
||||
memcpy((*output_points)[dest_size],
|
||||
dest_points[dest_size],
|
||||
(source_size - dest_size) * sizeof(float3));
|
||||
}
|
||||
|
||||
/* Swap the arrays around. */
|
||||
float3 *points_iter = source_points;
|
||||
source_points = dest_points;
|
||||
dest_points = points_iter;
|
||||
source_size = dest_size;
|
||||
}
|
||||
if (source_points != output_points->data()) {
|
||||
memcpy(output_points->data(), source_points, dest_size * sizeof(float3));
|
||||
}
|
||||
}
|
||||
|
||||
void poisson_disk_point_elimination(Vector<float3> const *input_points,
|
||||
Vector<float3> *output_points,
|
||||
float maximum_distance,
|
||||
float3 boundbox)
|
||||
{
|
||||
weighted_sample_elimination(input_points->data(),
|
||||
input_points->size(),
|
||||
output_points->data(),
|
||||
output_points->size(),
|
||||
maximum_distance,
|
||||
boundbox,
|
||||
false);
|
||||
|
||||
progressive_sampling_reorder(output_points, maximum_distance, boundbox);
|
||||
}
|
||||
|
||||
} // namespace blender::nodes
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user