forked from bartvdbraak/blender
Merge branch 'master' into blender2.8
This commit is contained in:
commit
1d5ba269c1
@ -228,6 +228,14 @@ public:
|
||||
DeviceInfo info;
|
||||
virtual const string& error_message() { return error_msg; }
|
||||
bool have_error() { return !error_message().empty(); }
|
||||
virtual void set_error(const string& error)
|
||||
{
|
||||
if(!have_error()) {
|
||||
error_msg = error;
|
||||
}
|
||||
fprintf(stderr, "%s\n", error.c_str());
|
||||
fflush(stderr);
|
||||
}
|
||||
virtual bool show_samples() const { return false; }
|
||||
|
||||
/* statistics */
|
||||
|
@ -205,6 +205,7 @@ bool DeviceSplitKernel::path_trace(DeviceTask *task,
|
||||
*/
|
||||
device->mem_zero(work_pool_wgs);
|
||||
device->mem_zero(split_data);
|
||||
device->mem_zero(ray_state);
|
||||
|
||||
if(!enqueue_split_kernel_data_init(KernelDimensions(global_size, local_size),
|
||||
subtile,
|
||||
@ -254,7 +255,15 @@ bool DeviceSplitKernel::path_trace(DeviceTask *task,
|
||||
activeRaysAvailable = false;
|
||||
|
||||
for(int rayStateIter = 0; rayStateIter < global_size[0] * global_size[1]; ++rayStateIter) {
|
||||
if(int8_t(ray_state.get_data()[rayStateIter]) != RAY_INACTIVE) {
|
||||
int8_t state = ray_state.get_data()[rayStateIter];
|
||||
|
||||
if(state != RAY_INACTIVE) {
|
||||
if(state == RAY_INVALID) {
|
||||
/* Something went wrong, abort to avoid looping endlessly. */
|
||||
device->set_error("Split kernel error: invalid ray state");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Not all rays are RAY_INACTIVE. */
|
||||
activeRaysAvailable = true;
|
||||
break;
|
||||
|
@ -356,10 +356,10 @@ bool OpenCLDeviceBase::OpenCLProgram::compile_kernel(const string *debug_src)
|
||||
cl_int ciErr;
|
||||
|
||||
program = clCreateProgramWithSource(device->cxContext,
|
||||
1,
|
||||
&source_str,
|
||||
&source_len,
|
||||
&ciErr);
|
||||
1,
|
||||
&source_str,
|
||||
&source_len,
|
||||
&ciErr);
|
||||
|
||||
if(ciErr != CL_SUCCESS) {
|
||||
add_error(string("OpenCL program creation failed: ") + clewErrorString(ciErr));
|
||||
@ -761,10 +761,10 @@ void OpenCLInfo::get_usable_devices(vector<OpenCLPlatformDevice> *usable_devices
|
||||
num_devices = 0;
|
||||
cl_int ciErr;
|
||||
if((ciErr = clGetDeviceIDs(platform_id,
|
||||
device_type,
|
||||
0,
|
||||
NULL,
|
||||
&num_devices)) != CL_SUCCESS || num_devices == 0)
|
||||
device_type,
|
||||
0,
|
||||
NULL,
|
||||
&num_devices)) != CL_SUCCESS || num_devices == 0)
|
||||
{
|
||||
FIRST_VLOG(2) << "Ignoring platform " << platform_name
|
||||
<< ", failed to fetch number of devices: " << string(clewErrorString(ciErr));
|
||||
|
@ -62,7 +62,7 @@ ccl_device_inline ShaderClosure *bsdf_alloc(ShaderData *sd, int size, float3 wei
|
||||
{
|
||||
ShaderClosure *sc = closure_alloc(sd, size, CLOSURE_NONE_ID, weight);
|
||||
|
||||
if(!sc)
|
||||
if(sc == NULL)
|
||||
return NULL;
|
||||
|
||||
float sample_weight = fabsf(average(weight));
|
||||
|
@ -266,7 +266,7 @@ ccl_device bool bsdf_microfacet_merge(const ShaderClosure *a, const ShaderClosur
|
||||
(bsdf_a->alpha_y == bsdf_b->alpha_y) &&
|
||||
(isequal_float3(bsdf_a->T, bsdf_b->T)) &&
|
||||
(bsdf_a->ior == bsdf_b->ior) &&
|
||||
((!bsdf_a->extra && !bsdf_b->extra) ||
|
||||
((bsdf_a->extra == NULL && bsdf_b->extra == NULL) ||
|
||||
((bsdf_a->extra && bsdf_b->extra) &&
|
||||
(isequal_float3(bsdf_a->extra->color, bsdf_b->extra->color))));
|
||||
}
|
||||
|
@ -22,9 +22,10 @@ CCL_NAMESPACE_BEGIN
|
||||
/*
|
||||
* Queue utility functions for split kernel
|
||||
*/
|
||||
|
||||
#ifdef __KERNEL_OPENCL__
|
||||
#pragma OPENCL EXTENSION cl_khr_global_int32_base_atomics : enable
|
||||
#pragma OPENCL EXTENSION cl_khr_local_int32_base_atomics : enable
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enqueue ray index into the queue
|
||||
|
@ -1297,20 +1297,19 @@ enum QueueNumber {
|
||||
#define RAY_STATE_MASK 0x007
|
||||
#define RAY_FLAG_MASK 0x0F8
|
||||
enum RayState {
|
||||
RAY_INVALID = 0,
|
||||
/* Denotes ray is actively involved in path-iteration. */
|
||||
RAY_ACTIVE = 0,
|
||||
RAY_ACTIVE,
|
||||
/* Denotes ray has completed processing all samples and is inactive. */
|
||||
RAY_INACTIVE = 1,
|
||||
RAY_INACTIVE,
|
||||
/* Denoted ray has exited path-iteration and needs to update output buffer. */
|
||||
RAY_UPDATE_BUFFER = 2,
|
||||
RAY_UPDATE_BUFFER,
|
||||
/* Donotes ray has hit background */
|
||||
RAY_HIT_BACKGROUND = 3,
|
||||
RAY_HIT_BACKGROUND,
|
||||
/* Denotes ray has to be regenerated */
|
||||
RAY_TO_REGENERATE = 4,
|
||||
RAY_TO_REGENERATE,
|
||||
/* Denotes ray has been regenerated */
|
||||
RAY_REGENERATED = 5,
|
||||
/* Denotes ray should skip direct lighting */
|
||||
RAY_SKIP_DL = 6,
|
||||
RAY_REGENERATED,
|
||||
/* Flag's ray has to execute shadow blocked function in AO part */
|
||||
RAY_SHADOW_RAY_CAST_AO = 16,
|
||||
/* Flag's ray has to execute shadow blocked function in direct lighting part. */
|
||||
|
@ -16,58 +16,27 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Note on kernel_background_buffer_update kernel.
|
||||
* This is the fourth kernel in the ray tracing logic, and the third
|
||||
* of the path iteration kernels. This kernel takes care of rays that hit
|
||||
* the background (sceneintersect kernel), and for the rays of
|
||||
* state RAY_UPDATE_BUFFER it updates the ray's accumulated radiance in
|
||||
* the output buffer. This kernel also takes care of rays that have been determined
|
||||
* to-be-regenerated.
|
||||
/* This kernel takes care of rays that hit the background (sceneintersect
|
||||
* kernel), and for the rays of state RAY_UPDATE_BUFFER it updates the ray's
|
||||
* accumulated radiance in the output buffer. This kernel also takes care of
|
||||
* rays that have been determined to-be-regenerated.
|
||||
*
|
||||
* We will empty QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS queue in this kernel
|
||||
* We will empty QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS queue in this kernel.
|
||||
*
|
||||
* Typically all rays that are in state RAY_HIT_BACKGROUND, RAY_UPDATE_BUFFER
|
||||
* will be eventually set to RAY_TO_REGENERATE state in this kernel. Finally all rays of ray_state
|
||||
* RAY_TO_REGENERATE will be regenerated and put in queue QUEUE_ACTIVE_AND_REGENERATED_RAYS.
|
||||
* will be eventually set to RAY_TO_REGENERATE state in this kernel.
|
||||
* Finally all rays of ray_state RAY_TO_REGENERATE will be regenerated and put
|
||||
* in queue QUEUE_ACTIVE_AND_REGENERATED_RAYS.
|
||||
*
|
||||
* The input and output are as follows,
|
||||
*
|
||||
* rng_coop ---------------------------------------------|--- kernel_background_buffer_update --|--- PathRadiance_coop
|
||||
* throughput_coop --------------------------------------| |--- L_transparent_coop
|
||||
* per_sample_output_buffers ----------------------------| |--- per_sample_output_buffers
|
||||
* Ray_coop ---------------------------------------------| |--- ray_state
|
||||
* PathState_coop ---------------------------------------| |--- Queue_data (QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS)
|
||||
* L_transparent_coop -----------------------------------| |--- Queue_data (QUEUE_ACTIVE_AND_REGENERATED_RAYS)
|
||||
* ray_state --------------------------------------------| |--- Queue_index (QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS)
|
||||
* Queue_data (QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS) ----| |--- Queue_index (QUEUE_ACTIVE_AND_REGENERATED_RAYS)
|
||||
* Queue_index (QUEUE_ACTIVE_AND_REGENERATED_RAYS) ------| |--- work_array
|
||||
* parallel_samples -------------------------------------| |--- PathState_coop
|
||||
* end_sample -------------------------------------------| |--- throughput_coop
|
||||
* kg (globals) -----------------------------------------| |--- rng_coop
|
||||
* rng_state --------------------------------------------| |--- Ray
|
||||
* PathRadiance_coop ------------------------------------| |
|
||||
* sw ---------------------------------------------------| |
|
||||
* sh ---------------------------------------------------| |
|
||||
* sx ---------------------------------------------------| |
|
||||
* sy ---------------------------------------------------| |
|
||||
* stride -----------------------------------------------| |
|
||||
* work_array -------------------------------------------| |--- work_array
|
||||
* queuesize --------------------------------------------| |
|
||||
* start_sample -----------------------------------------| |--- work_pool_wgs
|
||||
* work_pool_wgs ----------------------------------------| |
|
||||
* num_samples ------------------------------------------| |
|
||||
*
|
||||
* note on sd : sd argument is neither an input nor an output for this kernel. It is just filled and consumed here itself.
|
||||
* Note on Queues :
|
||||
* This kernel fetches rays from QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS queue.
|
||||
*
|
||||
* State of queues when this kernel is called :
|
||||
* State of queues when this kernel is called:
|
||||
* At entry,
|
||||
* QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE rays
|
||||
* QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with RAY_UPDATE_BUFFER, RAY_HIT_BACKGROUND, RAY_TO_REGENERATE rays
|
||||
* - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE rays.
|
||||
* - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with
|
||||
* RAY_UPDATE_BUFFER, RAY_HIT_BACKGROUND, RAY_TO_REGENERATE rays.
|
||||
* At exit,
|
||||
* QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE and RAY_REGENERATED rays
|
||||
* QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be empty
|
||||
* - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE and
|
||||
* RAY_REGENERATED rays.
|
||||
* - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be empty.
|
||||
*/
|
||||
ccl_device void kernel_buffer_update(KernelGlobals *kg)
|
||||
{
|
||||
@ -225,4 +194,3 @@ ccl_device void kernel_buffer_update(KernelGlobals *kg)
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
|
@ -16,16 +16,16 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Note on kernel_data_initialization kernel
|
||||
* This kernel Initializes structures needed in path-iteration kernels.
|
||||
/* This kernel Initializes structures needed in path-iteration kernels.
|
||||
*
|
||||
* Note on Queues :
|
||||
* Note on Queues:
|
||||
* All slots in queues are initialized to queue empty slot;
|
||||
* The number of elements in the queues is initialized to 0;
|
||||
*/
|
||||
|
||||
/* distributes an amount of work across all threads
|
||||
* note: work done inside the loop may not show up to all threads till after the current kernel has completed
|
||||
/* Distributes an amount of work across all threads
|
||||
* note: work done inside the loop may not show up to all threads till after
|
||||
* the current kernel has completed
|
||||
*/
|
||||
#define parallel_for(kg, iter_name, work_size) \
|
||||
for(size_t _size = (work_size), \
|
||||
@ -151,4 +151,3 @@ void KERNEL_FUNCTION_FULL_NAME(data_init)(
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
|
@ -16,36 +16,29 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Note on kernel_direct_lighting kernel.
|
||||
* This is the eighth kernel in the ray tracing logic. This is the seventh
|
||||
* of the path iteration kernels. This kernel takes care of direct lighting
|
||||
* logic. However, the "shadow ray cast" part of direct lighting is handled
|
||||
/* This kernel takes care of direct lighting logic.
|
||||
* However, the "shadow ray cast" part of direct lighting is handled
|
||||
* in the next kernel.
|
||||
*
|
||||
* This kernels determines the rays for which a shadow_blocked() function associated with direct lighting should be executed.
|
||||
* Those rays for which a shadow_blocked() function for direct-lighting must be executed, are marked with flag RAY_SHADOW_RAY_CAST_DL and
|
||||
* enqueued into the queue QUEUE_SHADOW_RAY_CAST_DL_RAYS
|
||||
* This kernels determines the rays for which a shadow_blocked() function
|
||||
* associated with direct lighting should be executed. Those rays for which
|
||||
* a shadow_blocked() function for direct-lighting must be executed, are
|
||||
* marked with flag RAY_SHADOW_RAY_CAST_DL and enqueued into the queue
|
||||
* QUEUE_SHADOW_RAY_CAST_DL_RAYS
|
||||
*
|
||||
* The input and output are as follows,
|
||||
* Note on Queues:
|
||||
* This kernel only reads from the QUEUE_ACTIVE_AND_REGENERATED_RAYS queue
|
||||
* and processes only the rays of state RAY_ACTIVE; If a ray needs to execute
|
||||
* the corresponding shadow_blocked part, after direct lighting, the ray is
|
||||
* marked with RAY_SHADOW_RAY_CAST_DL flag.
|
||||
*
|
||||
* rng_coop -----------------------------------------|--- kernel_direct_lighting --|--- BSDFEval_coop
|
||||
* PathState_coop -----------------------------------| |--- ISLamp_coop
|
||||
* sd -----------------------------------------------| |--- LightRay_coop
|
||||
* ray_state ----------------------------------------| |--- ray_state
|
||||
* Queue_data (QUEUE_ACTIVE_AND_REGENERATED_RAYS) ---| |
|
||||
* kg (globals) -------------------------------------| |
|
||||
* queuesize ----------------------------------------| |
|
||||
*
|
||||
* Note on Queues :
|
||||
* This kernel only reads from the QUEUE_ACTIVE_AND_REGENERATED_RAYS queue and processes
|
||||
* only the rays of state RAY_ACTIVE; If a ray needs to execute the corresponding shadow_blocked
|
||||
* part, after direct lighting, the ray is marked with RAY_SHADOW_RAY_CAST_DL flag.
|
||||
*
|
||||
* State of queues when this kernel is called :
|
||||
* state of queues QUEUE_ACTIVE_AND_REGENERATED_RAYS and QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be same
|
||||
* before and after this kernel call.
|
||||
* QUEUE_SHADOW_RAY_CAST_DL_RAYS queue will be filled with rays for which a shadow_blocked function must be executed, after this
|
||||
* kernel call. Before this kernel call the QUEUE_SHADOW_RAY_CAST_DL_RAYS will be empty.
|
||||
* State of queues when this kernel is called:
|
||||
* - State of queues QUEUE_ACTIVE_AND_REGENERATED_RAYS and
|
||||
* QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be same before and after this
|
||||
* kernel call.
|
||||
* - QUEUE_SHADOW_RAY_CAST_DL_RAYS queue will be filled with rays for which a
|
||||
* shadow_blocked function must be executed, after this kernel call
|
||||
* Before this kernel call the QUEUE_SHADOW_RAY_CAST_DL_RAYS will be empty.
|
||||
*/
|
||||
ccl_device void kernel_direct_lighting(KernelGlobals *kg)
|
||||
{
|
||||
|
@ -16,59 +16,41 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Note on kernel_holdout_emission_blurring_pathtermination_ao kernel.
|
||||
* This is the sixth kernel in the ray tracing logic. This is the fifth
|
||||
* of the path iteration kernels. This kernel takes care of the logic to process
|
||||
* "material of type holdout", indirect primitive emission, bsdf blurring,
|
||||
* probabilistic path termination and AO.
|
||||
/* This kernel takes care of the logic to process "material of type holdout",
|
||||
* indirect primitive emission, bsdf blurring, probabilistic path termination
|
||||
* and AO.
|
||||
*
|
||||
* This kernels determines the rays for which a shadow_blocked() function associated with AO should be executed.
|
||||
* Those rays for which a shadow_blocked() function for AO must be executed are marked with flag RAY_SHADOW_RAY_CAST_ao and
|
||||
* enqueued into the queue QUEUE_SHADOW_RAY_CAST_AO_RAYS
|
||||
* This kernels determines the rays for which a shadow_blocked() function
|
||||
* associated with AO should be executed. Those rays for which a
|
||||
* shadow_blocked() function for AO must be executed are marked with flag
|
||||
* RAY_SHADOW_RAY_CAST_ao and enqueued into the queue
|
||||
* QUEUE_SHADOW_RAY_CAST_AO_RAYS
|
||||
*
|
||||
* Ray state of rays that are terminated in this kernel are changed to RAY_UPDATE_BUFFER
|
||||
*
|
||||
* The input and output are as follows,
|
||||
* Note on Queues:
|
||||
* This kernel fetches rays from the queue QUEUE_ACTIVE_AND_REGENERATED_RAYS
|
||||
* and processes only the rays of state RAY_ACTIVE.
|
||||
* There are different points in this kernel where a ray may terminate and
|
||||
* reach RAY_UPDATE_BUFFER state. These rays are enqueued into
|
||||
* QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS queue. These rays will still be present
|
||||
* in QUEUE_ACTIVE_AND_REGENERATED_RAYS queue, but since their ray-state has
|
||||
* been changed to RAY_UPDATE_BUFFER, there is no problem.
|
||||
*
|
||||
* rng_coop ---------------------------------------------|--- kernel_holdout_emission_blurring_pathtermination_ao ---|--- Queue_index (QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS)
|
||||
* throughput_coop --------------------------------------| |--- PathState_coop
|
||||
* PathRadiance_coop ------------------------------------| |--- throughput_coop
|
||||
* Intersection_coop ------------------------------------| |--- L_transparent_coop
|
||||
* PathState_coop ---------------------------------------| |--- per_sample_output_buffers
|
||||
* L_transparent_coop -----------------------------------| |--- PathRadiance_coop
|
||||
* sd ---------------------------------------------------| |--- ShaderData
|
||||
* ray_state --------------------------------------------| |--- ray_state
|
||||
* Queue_data (QUEUE_ACTIVE_AND_REGENERATED_RAYS) -------| |--- Queue_data (QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS)
|
||||
* Queue_index (QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS) ---| |--- AOAlpha_coop
|
||||
* kg (globals) -----------------------------------------| |--- AOBSDF_coop
|
||||
* parallel_samples -------------------------------------| |--- AOLightRay_coop
|
||||
* per_sample_output_buffers ----------------------------| |
|
||||
* sw ---------------------------------------------------| |
|
||||
* sh ---------------------------------------------------| |
|
||||
* sx ---------------------------------------------------| |
|
||||
* sy ---------------------------------------------------| |
|
||||
* stride -----------------------------------------------| |
|
||||
* work_array -------------------------------------------| |
|
||||
* queuesize --------------------------------------------| |
|
||||
* start_sample -----------------------------------------| |
|
||||
*
|
||||
* Note on Queues :
|
||||
* This kernel fetches rays from the queue QUEUE_ACTIVE_AND_REGENERATED_RAYS and processes only
|
||||
* the rays of state RAY_ACTIVE.
|
||||
* There are different points in this kernel where a ray may terminate and reach RAY_UPDATE_BUFFER
|
||||
* state. These rays are enqueued into QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS queue. These rays will
|
||||
* still be present in QUEUE_ACTIVE_AND_REGENERATED_RAYS queue, but since their ray-state has been
|
||||
* changed to RAY_UPDATE_BUFFER, there is no problem.
|
||||
*
|
||||
* State of queues when this kernel is called :
|
||||
* State of queues when this kernel is called:
|
||||
* At entry,
|
||||
* QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE and RAY_REGENERATED rays
|
||||
* QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with RAY_TO_REGENERATE rays.
|
||||
* QUEUE_SHADOW_RAY_CAST_AO_RAYS will be empty.
|
||||
* - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE and
|
||||
* RAY_REGENERATED rays
|
||||
* - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with
|
||||
* RAY_TO_REGENERATE rays.
|
||||
* - QUEUE_SHADOW_RAY_CAST_AO_RAYS will be empty.
|
||||
* At exit,
|
||||
* QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE, RAY_REGENERATED and RAY_UPDATE_BUFFER rays
|
||||
* QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with RAY_TO_REGENERATE and RAY_UPDATE_BUFFER rays
|
||||
* QUEUE_SHADOW_RAY_CAST_AO_RAYS will be filled with rays marked with flag RAY_SHADOW_RAY_CAST_AO
|
||||
* - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE,
|
||||
* RAY_REGENERATED and RAY_UPDATE_BUFFER rays.
|
||||
* - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with
|
||||
* RAY_TO_REGENERATE and RAY_UPDATE_BUFFER rays.
|
||||
* - QUEUE_SHADOW_RAY_CAST_AO_RAYS will be filled with rays marked with
|
||||
* flag RAY_SHADOW_RAY_CAST_AO
|
||||
*/
|
||||
ccl_device void kernel_holdout_emission_blurring_pathtermination_ao(KernelGlobals *kg)
|
||||
{
|
||||
@ -288,4 +270,3 @@ ccl_device void kernel_holdout_emission_blurring_pathtermination_ao(KernelGlobal
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
|
@ -18,40 +18,39 @@ CCL_NAMESPACE_BEGIN
|
||||
|
||||
ccl_device void kernel_indirect_background(KernelGlobals *kg)
|
||||
{
|
||||
/*
|
||||
ccl_local unsigned int local_queue_atomics;
|
||||
if(ccl_local_id(0) == 0 && ccl_local_id(1) == 0) {
|
||||
local_queue_atomics = 0;
|
||||
}
|
||||
ccl_barrier(CCL_LOCAL_MEM_FENCE);
|
||||
// */
|
||||
|
||||
int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0);
|
||||
ray_index = get_ray_index(kg, ray_index,
|
||||
ccl_global char *ray_state = kernel_split_state.ray_state;
|
||||
|
||||
int thread_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0);
|
||||
int ray_index;
|
||||
|
||||
if(kernel_data.integrator.ao_bounces) {
|
||||
ray_index = get_ray_index(kg, thread_index,
|
||||
QUEUE_ACTIVE_AND_REGENERATED_RAYS,
|
||||
kernel_split_state.queue_data,
|
||||
kernel_split_params.queue_size,
|
||||
0);
|
||||
|
||||
if(ray_index != QUEUE_EMPTY_SLOT) {
|
||||
if(IS_STATE(ray_state, ray_index, RAY_ACTIVE)) {
|
||||
ccl_global PathState *state = &kernel_split_state.path_state[ray_index];
|
||||
if(state->bounce > kernel_data.integrator.ao_bounces) {
|
||||
ASSIGN_RAY_STATE(ray_state, ray_index, RAY_UPDATE_BUFFER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ray_index = get_ray_index(kg, thread_index,
|
||||
QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS,
|
||||
kernel_split_state.queue_data,
|
||||
kernel_split_params.queue_size,
|
||||
0);
|
||||
|
||||
#ifdef __COMPUTE_DEVICE_GPU__
|
||||
/* If we are executing on a GPU device, we exit all threads that are not
|
||||
* required.
|
||||
*
|
||||
* If we are executing on a CPU device, then we need to keep all threads
|
||||
* active since we have barrier() calls later in the kernel. CPU devices,
|
||||
* expect all threads to execute barrier statement.
|
||||
*/
|
||||
if(ray_index == QUEUE_EMPTY_SLOT) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __COMPUTE_DEVICE_GPU__
|
||||
if(ray_index != QUEUE_EMPTY_SLOT) {
|
||||
#endif
|
||||
|
||||
|
||||
ccl_global char *ray_state = kernel_split_state.ray_state;
|
||||
ccl_global PathState *state = &kernel_split_state.path_state[ray_index];
|
||||
PathRadiance *L = &kernel_split_state.path_radiance[ray_index];
|
||||
ccl_global Ray *ray = &kernel_split_state.ray[ray_index];
|
||||
@ -78,9 +77,6 @@ ccl_device void kernel_indirect_background(KernelGlobals *kg)
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef __COMPUTE_DEVICE_GPU__
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
@ -16,25 +16,9 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Note on kernel_lamp_emission
|
||||
* This is the 3rd kernel in the ray-tracing logic. This is the second of the
|
||||
* path-iteration kernels. This kernel takes care of the indirect lamp emission logic.
|
||||
* This kernel operates on QUEUE_ACTIVE_AND_REGENERATED_RAYS. It processes rays of state RAY_ACTIVE
|
||||
* and RAY_HIT_BACKGROUND.
|
||||
/* This kernel operates on QUEUE_ACTIVE_AND_REGENERATED_RAYS.
|
||||
* It processes rays of state RAY_ACTIVE and RAY_HIT_BACKGROUND.
|
||||
* We will empty QUEUE_ACTIVE_AND_REGENERATED_RAYS queue in this kernel.
|
||||
* The input/output of the kernel is as follows,
|
||||
* Throughput_coop ------------------------------------|--- kernel_lamp_emission --|--- PathRadiance_coop
|
||||
* Ray_coop -------------------------------------------| |--- Queue_data(QUEUE_ACTIVE_AND_REGENERATED_RAYS)
|
||||
* PathState_coop -------------------------------------| |--- Queue_index(QUEUE_ACTIVE_AND_REGENERATED_RAYS)
|
||||
* kg (globals) ---------------------------------------| |
|
||||
* Intersection_coop ----------------------------------| |
|
||||
* ray_state ------------------------------------------| |
|
||||
* Queue_data (QUEUE_ACTIVE_AND_REGENERATED_RAYS) -----| |
|
||||
* Queue_index (QUEUE_ACTIVE_AND_REGENERATED_RAYS) ----| |
|
||||
* queuesize ------------------------------------------| |
|
||||
* use_queues_flag ------------------------------------| |
|
||||
* sw -------------------------------------------------| |
|
||||
* sh -------------------------------------------------| |
|
||||
*/
|
||||
ccl_device void kernel_lamp_emission(KernelGlobals *kg)
|
||||
{
|
||||
|
@ -16,48 +16,33 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Note on kernel_setup_next_iteration kernel.
|
||||
* This is the tenth kernel in the ray tracing logic. This is the ninth
|
||||
* of the path iteration kernels. This kernel takes care of setting up
|
||||
* Ray for the next iteration of path-iteration and accumulating radiance
|
||||
* corresponding to AO and direct-lighting
|
||||
/*This kernel takes care of setting up ray for the next iteration of
|
||||
* path-iteration and accumulating radiance corresponding to AO and
|
||||
* direct-lighting
|
||||
*
|
||||
* Ray state of rays that are terminated in this kernel are changed to RAY_UPDATE_BUFFER
|
||||
* Ray state of rays that are terminated in this kernel are changed
|
||||
* to RAY_UPDATE_BUFFER.
|
||||
*
|
||||
* The input and output are as follows,
|
||||
* Note on queues:
|
||||
* This kernel fetches rays from the queue QUEUE_ACTIVE_AND_REGENERATED_RAYS
|
||||
* and processes only the rays of state RAY_ACTIVE.
|
||||
* There are different points in this kernel where a ray may terminate and
|
||||
* reach RAY_UPDATE_BUFF state. These rays are enqueued into
|
||||
* QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS queue. These rays will still be present
|
||||
* in QUEUE_ACTIVE_AND_REGENERATED_RAYS queue, but since their ray-state has
|
||||
* been changed to RAY_UPDATE_BUFF, there is no problem.
|
||||
*
|
||||
* rng_coop ---------------------------------------------|--- kernel_next_iteration_setup -|--- Queue_index (QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS)
|
||||
* throughput_coop --------------------------------------| |--- Queue_data (QUEUE_HITBF_BUFF_UPDATE_TOREGEN_RAYS)
|
||||
* PathRadiance_coop ------------------------------------| |--- throughput_coop
|
||||
* PathState_coop ---------------------------------------| |--- PathRadiance_coop
|
||||
* sd ---------------------------------------------------| |--- PathState_coop
|
||||
* ray_state --------------------------------------------| |--- ray_state
|
||||
* Queue_data (QUEUE_ACTIVE_AND_REGENERATD_RAYS) --------| |--- Ray_coop
|
||||
* Queue_index (QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS) ---| |--- use_queues_flag
|
||||
* Ray_coop ---------------------------------------------| |
|
||||
* kg (globals) -----------------------------------------| |
|
||||
* LightRay_dl_coop -------------------------------------|
|
||||
* ISLamp_coop ------------------------------------------|
|
||||
* BSDFEval_coop ----------------------------------------|
|
||||
* LightRay_ao_coop -------------------------------------|
|
||||
* AOBSDF_coop ------------------------------------------|
|
||||
* AOAlpha_coop -----------------------------------------|
|
||||
*
|
||||
* Note on queues,
|
||||
* This kernel fetches rays from the queue QUEUE_ACTIVE_AND_REGENERATED_RAYS and processes only
|
||||
* the rays of state RAY_ACTIVE.
|
||||
* There are different points in this kernel where a ray may terminate and reach RAY_UPDATE_BUFF
|
||||
* state. These rays are enqueued into QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS queue. These rays will
|
||||
* still be present in QUEUE_ACTIVE_AND_REGENERATED_RAYS queue, but since their ray-state has been
|
||||
* changed to RAY_UPDATE_BUFF, there is no problem.
|
||||
*
|
||||
* State of queues when this kernel is called :
|
||||
* State of queues when this kernel is called:
|
||||
* At entry,
|
||||
* QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE, RAY_REGENERATED, RAY_UPDATE_BUFFER rays.
|
||||
* QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with RAY_TO_REGENERATE and RAY_UPDATE_BUFFER rays
|
||||
* - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE,
|
||||
* RAY_REGENERATED, RAY_UPDATE_BUFFER rays.
|
||||
* - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with
|
||||
* RAY_TO_REGENERATE and RAY_UPDATE_BUFFER rays.
|
||||
* At exit,
|
||||
* QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE, RAY_REGENERATED and more RAY_UPDATE_BUFFER rays.
|
||||
* QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with RAY_TO_REGENERATE and more RAY_UPDATE_BUFFER rays
|
||||
* - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE,
|
||||
* RAY_REGENERATED and more RAY_UPDATE_BUFFER rays.
|
||||
* - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with
|
||||
* RAY_TO_REGENERATE and more RAY_UPDATE_BUFFER rays.
|
||||
*/
|
||||
ccl_device void kernel_next_iteration_setup(KernelGlobals *kg)
|
||||
{
|
||||
@ -182,4 +167,3 @@ ccl_device void kernel_next_iteration_setup(KernelGlobals *kg)
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
|
@ -21,7 +21,6 @@ CCL_NAMESPACE_BEGIN
|
||||
*
|
||||
* Ray state of rays outside the tile-boundary will be marked RAY_INACTIVE
|
||||
*/
|
||||
|
||||
ccl_device void kernel_path_init(KernelGlobals *kg) {
|
||||
int ray_index = ccl_global_id(0) + ccl_global_id(1) * ccl_global_size(0);
|
||||
|
||||
@ -101,4 +100,3 @@ ccl_device void kernel_path_init(KernelGlobals *kg) {
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
|
@ -16,36 +16,24 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/*
|
||||
* The kernel "kernel_queue_enqueue" enqueues rays of
|
||||
* different ray state into their appropriate Queues;
|
||||
/* This kernel enqueues rays of different ray state into their
|
||||
* appropriate queues:
|
||||
*
|
||||
* 1. Rays that have been determined to hit the background from the
|
||||
* "kernel_scene_intersect" kernel
|
||||
* are enqueued in QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS;
|
||||
* 2. Rays that have been determined to be actively participating in path-iteration will be enqueued into QUEUE_ACTIVE_AND_REGENERATED_RAYS.
|
||||
* "kernel_scene_intersect" kernel are enqueued in
|
||||
* QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS;
|
||||
* 2. Rays that have been determined to be actively participating in pat
|
||||
* -iteration will be enqueued into QUEUE_ACTIVE_AND_REGENERATED_RAYS.
|
||||
*
|
||||
* The input and output of the kernel is as follows,
|
||||
*
|
||||
* ray_state -------------------------------------------|--- kernel_queue_enqueue --|--- Queue_data (QUEUE_ACTIVE_AND_REGENERATED_RAYS & QUEUE_HITBF_BUFF_UPDATE_TOREGEN_RAYS)
|
||||
* Queue_index(QUEUE_ACTIVE_AND_REGENERATED_RAYS) ------| |--- Queue_index (QUEUE_ACTIVE_AND_REGENERATED_RAYS & QUEUE_HITBF_BUFF_UPDATE_TOREGEN_RAYS)
|
||||
* Queue_index(QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS) ---| |
|
||||
* queuesize -------------------------------------------| |
|
||||
*
|
||||
* Note on Queues :
|
||||
* State of queues during the first time this kernel is called :
|
||||
* State of queue during other times this kernel is called:
|
||||
* At entry,
|
||||
* Both QUEUE_ACTIVE_AND_REGENERATED_RAYS and QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be empty.
|
||||
* - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be empty.
|
||||
* - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will contain RAY_TO_REGENERATE
|
||||
* and RAY_UPDATE_BUFFER rays.
|
||||
* At exit,
|
||||
* QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE rays
|
||||
* QUEUE_HITBF_BUFF_UPDATE_TOREGEN_RAYS will be filled with RAY_HIT_BACKGROUND rays.
|
||||
*
|
||||
* State of queue during other times this kernel is called :
|
||||
* At entry,
|
||||
* QUEUE_ACTIVE_AND_REGENERATED_RAYS will be empty.
|
||||
* QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will contain RAY_TO_REGENERATE and RAY_UPDATE_BUFFER rays.
|
||||
* At exit,
|
||||
* QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE rays.
|
||||
* QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with RAY_TO_REGENERATE, RAY_UPDATE_BUFFER, RAY_HIT_BACKGROUND rays.
|
||||
* - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE rays.
|
||||
* - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with
|
||||
* RAY_TO_REGENERATE, RAY_UPDATE_BUFFER, RAY_HIT_BACKGROUND rays.
|
||||
*/
|
||||
ccl_device void kernel_queue_enqueue(KernelGlobals *kg)
|
||||
{
|
||||
@ -101,4 +89,3 @@ ccl_device void kernel_queue_enqueue(KernelGlobals *kg)
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
|
@ -16,51 +16,13 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Note on kernel_scene_intersect kernel.
|
||||
* This is the second kernel in the ray tracing logic. This is the first
|
||||
* of the path iteration kernels. This kernel takes care of scene_intersect function.
|
||||
/* This kernel takes care of scene_intersect function.
|
||||
*
|
||||
* This kernel changes the ray_state of RAY_REGENERATED rays to RAY_ACTIVE.
|
||||
* This kernel processes rays of ray state RAY_ACTIVE
|
||||
* This kernel determines the rays that have hit the background and changes their ray state to RAY_HIT_BACKGROUND.
|
||||
*
|
||||
* The input and output are as follows,
|
||||
*
|
||||
* Ray_coop ---------------------------------------|--------- kernel_scene_intersect----------|--- PathState
|
||||
* PathState_coop ---------------------------------| |--- Intersection
|
||||
* ray_state --------------------------------------| |--- ray_state
|
||||
* use_queues_flag --------------------------------| |
|
||||
* QueueData(QUEUE_ACTIVE_AND_REGENERATED_RAYS) ---| |
|
||||
* kg (globals) -----------------------------------| |
|
||||
* rng_coop ---------------------------------------| |
|
||||
* sw ---------------------------------------------| |
|
||||
* sh ---------------------------------------------| |
|
||||
* queuesize --------------------------------------| |
|
||||
*
|
||||
* Note on Queues :
|
||||
* Ideally we would want kernel_scene_intersect to work on queues.
|
||||
* But during the very first time, the queues will be empty and hence we perform a direct mapping
|
||||
* between ray-index and thread-index; From the next time onward, the queue will be filled and
|
||||
* we may start operating on queues.
|
||||
*
|
||||
* State of queue during the first time this kernel is called :
|
||||
* QUEUE_ACTIVE_AND_REGENERATED_RAYS and QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be empty.before and after this kernel
|
||||
*
|
||||
* State of queues during other times this kernel is called :
|
||||
* At entry,
|
||||
* QUEUE_ACTIVE_AND_REGENERATED_RAYS will have a mix of RAY_ACTIVE, RAY_UPDATE_BUFFER and RAY_REGENERATED rays;
|
||||
* QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with RAY_TO_REGENERATE and RAY_UPDATE_BUFFER rays ;
|
||||
* (The rays that are in the state RAY_UPDATE_BUFFER in both the queues are actually the same rays; These
|
||||
* are the rays that were in RAY_ACTIVE state during the initial enqueue but on further processing
|
||||
* , by different kernels, have turned into RAY_UPDATE_BUFFER rays. Since all kernel, even after fetching from
|
||||
* QUEUE_ACTIVE_AND_REGENERATED_RAYS, proceed further based on ray state information, RAY_UPDATE_BUFFER rays
|
||||
* being present in QUEUE_ACTIVE_AND_REGENERATED_RAYS does not cause any logical issues)
|
||||
* At exit,
|
||||
* QUEUE_ACTIVE_AND_REGENERATED_RAYS - All RAY_REGENERATED rays will have been converted to RAY_ACTIVE and
|
||||
* Some rays in QUEUE_ACTIVE_AND_REGENERATED_RAYS queue will move to state RAY_HIT_BACKGROUND
|
||||
* QUEUE_HITBF_BUFF_UPDATE_TOREGEN_RAYS - no change
|
||||
* This kernel determines the rays that have hit the background and changes
|
||||
* their ray state to RAY_HIT_BACKGROUND.
|
||||
*/
|
||||
|
||||
ccl_device void kernel_scene_intersect(KernelGlobals *kg)
|
||||
{
|
||||
/* Fetch use_queues_flag */
|
||||
@ -116,6 +78,11 @@ ccl_device void kernel_scene_intersect(KernelGlobals *kg)
|
||||
lcg_state = lcg_state_init(&rng, &state, 0x51633e2d);
|
||||
}
|
||||
|
||||
if(state.bounce > kernel_data.integrator.ao_bounces) {
|
||||
visibility = PATH_RAY_SHADOW;
|
||||
ray.t = kernel_data.background.ao_distance;
|
||||
}
|
||||
|
||||
bool hit = scene_intersect(kg, ray, visibility, &isect, &lcg_state, difl, extmax);
|
||||
#else
|
||||
bool hit = scene_intersect(kg, ray, visibility, &isect, NULL, 0.0f, 0.0f);
|
||||
@ -141,4 +108,3 @@ ccl_device void kernel_scene_intersect(KernelGlobals *kg)
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
|
@ -16,35 +16,12 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Note on kernel_shader_eval kernel
|
||||
* This kernel is the 5th kernel in the ray tracing logic. This is
|
||||
* the 4rd kernel in path iteration. This kernel sets up the ShaderData
|
||||
* structure from the values computed by the previous kernels. It also identifies
|
||||
* the rays of state RAY_TO_REGENERATE and enqueues them in QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS queue.
|
||||
/* This kernel sets up the ShaderData structure from the values computed
|
||||
* by the previous kernels.
|
||||
*
|
||||
* The input and output of the kernel is as follows,
|
||||
* rng_coop -------------------------------------------|--- kernel_shader_eval --|--- sd
|
||||
* Ray_coop -------------------------------------------| |--- Queue_data (QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS)
|
||||
* PathState_coop -------------------------------------| |--- Queue_index (QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS)
|
||||
* Intersection_coop ----------------------------------| |
|
||||
* Queue_data (QUEUE_ACTIVE_AND_REGENERATD_RAYS)-------| |
|
||||
* Queue_index(QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS)---| |
|
||||
* ray_state ------------------------------------------| |
|
||||
* kg (globals) ---------------------------------------| |
|
||||
* queuesize ------------------------------------------| |
|
||||
*
|
||||
* Note on Queues :
|
||||
* This kernel reads from the QUEUE_ACTIVE_AND_REGENERATED_RAYS queue and processes
|
||||
* only the rays of state RAY_ACTIVE;
|
||||
* State of queues when this kernel is called,
|
||||
* at entry,
|
||||
* QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE and RAY_REGENERATED rays
|
||||
* QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be empty.
|
||||
* at exit,
|
||||
* QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE and RAY_REGENERATED rays
|
||||
* QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with RAY_TO_REGENERATE rays
|
||||
* It also identifies the rays of state RAY_TO_REGENERATE and enqueues them
|
||||
* in QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS queue.
|
||||
*/
|
||||
|
||||
ccl_device void kernel_shader_eval(KernelGlobals *kg)
|
||||
{
|
||||
/* Enqeueue RAY_TO_REGENERATE rays into QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS queue. */
|
||||
@ -91,4 +68,3 @@ ccl_device void kernel_shader_eval(KernelGlobals *kg)
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
|
@ -16,33 +16,7 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Note on kernel_shadow_blocked kernel.
|
||||
* This is the ninth kernel in the ray tracing logic. This is the eighth
|
||||
* of the path iteration kernels. This kernel takes care of "shadow ray cast"
|
||||
* logic of the direct lighting and AO part of ray tracing.
|
||||
*
|
||||
* The input and output are as follows,
|
||||
*
|
||||
* PathState_coop ----------------------------------|--- kernel_shadow_blocked --|
|
||||
* LightRay_dl_coop --------------------------------| |--- LightRay_dl_coop
|
||||
* LightRay_ao_coop --------------------------------| |--- LightRay_ao_coop
|
||||
* ray_state ---------------------------------------| |--- ray_state
|
||||
* Queue_data(QUEUE_SHADOW_RAY_CAST_AO_RAYS & | |--- Queue_data (QUEUE_SHADOW_RAY_CAST_AO_RAYS & QUEUE_SHADOW_RAY_CAST_AO_RAYS)
|
||||
QUEUE_SHADOW_RAY_CAST_DL_RAYS) -------| |
|
||||
* Queue_index(QUEUE_SHADOW_RAY_CAST_AO_RAYS&
|
||||
QUEUE_SHADOW_RAY_CAST_DL_RAYS) -------| |
|
||||
* kg (globals) ------------------------------------| |
|
||||
* queuesize ---------------------------------------| |
|
||||
*
|
||||
* Note on sd_shadow : sd_shadow is neither input nor output to this kernel. sd_shadow is filled and consumed in this kernel itself.
|
||||
* Note on queues :
|
||||
* The kernel fetches from QUEUE_SHADOW_RAY_CAST_AO_RAYS queue. We will empty this queues in this kernel.
|
||||
* State of queues when this kernel is called :
|
||||
* state of queues QUEUE_ACTIVE_AND_REGENERATED_RAYS and QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be same
|
||||
* before and after this kernel call.
|
||||
* QUEUE_SHADOW_RAY_CAST_AO_RAYS will be filled with rays marked with flags RAY_SHADOW_RAY_CAST_AO during kernel entry.
|
||||
* QUEUE_SHADOW_RAY_CAST_AO_RAYS will be empty at kernel exit.
|
||||
*/
|
||||
/* Shadow ray cast for AO. */
|
||||
ccl_device void kernel_shadow_blocked_ao(KernelGlobals *kg)
|
||||
{
|
||||
int lidx = ccl_local_id(1) * ccl_local_id(0) + ccl_local_id(0);
|
||||
|
@ -16,33 +16,7 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Note on kernel_shadow_blocked kernel.
|
||||
* This is the ninth kernel in the ray tracing logic. This is the eighth
|
||||
* of the path iteration kernels. This kernel takes care of "shadow ray cast"
|
||||
* logic of the direct lighting and AO part of ray tracing.
|
||||
*
|
||||
* The input and output are as follows,
|
||||
*
|
||||
* PathState_coop ----------------------------------|--- kernel_shadow_blocked --|
|
||||
* LightRay_dl_coop --------------------------------| |--- LightRay_dl_coop
|
||||
* LightRay_ao_coop --------------------------------| |--- LightRay_ao_coop
|
||||
* ray_state ---------------------------------------| |--- ray_state
|
||||
* Queue_data(QUEUE_SHADOW_RAY_CAST_AO_RAYS & | |--- Queue_data (QUEUE_SHADOW_RAY_CAST_AO_RAYS & QUEUE_SHADOW_RAY_CAST_AO_RAYS)
|
||||
QUEUE_SHADOW_RAY_CAST_DL_RAYS) -------| |
|
||||
* Queue_index(QUEUE_SHADOW_RAY_CAST_AO_RAYS&
|
||||
QUEUE_SHADOW_RAY_CAST_DL_RAYS) -------| |
|
||||
* kg (globals) ------------------------------------| |
|
||||
* queuesize ---------------------------------------| |
|
||||
*
|
||||
* Note on sd_shadow : sd_shadow is neither input nor output to this kernel. sd_shadow is filled and consumed in this kernel itself.
|
||||
* Note on queues :
|
||||
* The kernel fetches from QUEUE_SHADOW_RAY_CAST_DL_RAYS queue. We will empty this queue in this kernel.
|
||||
* State of queues when this kernel is called :
|
||||
* state of queues QUEUE_ACTIVE_AND_REGENERATED_RAYS and QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be same
|
||||
* before and after this kernel call.
|
||||
* QUEUE_SHADOW_RAY_CAST_DL_RAYS will be filled with rays marked with flags RAY_SHADOW_RAY_CAST_DL, during kernel entry.
|
||||
* QUEUE_SHADOW_RAY_CAST_DL_RAYS will be empty at kernel exit.
|
||||
*/
|
||||
/* Shadow ray cast for direct visible light. */
|
||||
ccl_device void kernel_shadow_blocked_dl(KernelGlobals *kg)
|
||||
{
|
||||
int lidx = ccl_local_id(1) * ccl_local_id(0) + ccl_local_id(0);
|
||||
@ -88,4 +62,3 @@ ccl_device void kernel_shadow_blocked_dl(KernelGlobals *kg)
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
|
@ -1,4 +1,18 @@
|
||||
|
||||
/*
|
||||
* Copyright 2011-2017 Blender Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
|
@ -725,11 +725,30 @@ class Header(StructRNA, _GenericUI, metaclass=RNAMeta):
|
||||
class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
|
||||
__slots__ = ()
|
||||
|
||||
def path_menu(self, searchpaths, operator,
|
||||
props_default=None, filter_ext=None):
|
||||
def path_menu(self, searchpaths, operator, *,
|
||||
props_default=None, prop_filepath="filepath",
|
||||
filter_ext=None, display_name=None):
|
||||
"""
|
||||
Populate a menu from a list of paths.
|
||||
|
||||
:arg searchpaths: Paths to scan.
|
||||
:type searchpaths: sequence of strings.
|
||||
:arg operator: The operator id to use with each file.
|
||||
:type operator: string
|
||||
:arg prop_filepath: Optional operator filepath property (defaults to "filepath").
|
||||
:type prop_filepath: string
|
||||
:arg props_default: Properties to assign to each operator.
|
||||
:type props_default: dict
|
||||
:arg filter_ext: Optional callback that takes the file extensions.
|
||||
|
||||
Returning false excludes the file from the list.
|
||||
|
||||
:type filter_ext: Callable that takes a string and returns a bool.
|
||||
:arg display_name: Optional callback that takes the full path, returns the name to display.
|
||||
:type display_name: Callable that takes a string and returns a string.
|
||||
"""
|
||||
|
||||
layout = self.layout
|
||||
# hard coded to set the operators 'filepath' to the filename.
|
||||
|
||||
import os
|
||||
import bpy.utils
|
||||
@ -752,15 +771,19 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
|
||||
files.sort()
|
||||
|
||||
for f, filepath in files:
|
||||
props = layout.operator(operator,
|
||||
text=bpy.path.display_name(f),
|
||||
translate=False)
|
||||
# Intentionally pass the full path to 'display_name' callback,
|
||||
# since the callback may want to use part a directory in the name.
|
||||
props = layout.operator(
|
||||
operator,
|
||||
text=display_name(filepath) if display_name else bpy.path.display_name(f),
|
||||
translate=False,
|
||||
)
|
||||
|
||||
if props_default is not None:
|
||||
for attr, value in props_default.items():
|
||||
setattr(props, attr, value)
|
||||
|
||||
props.filepath = filepath
|
||||
setattr(props, prop_filepath, filepath)
|
||||
if operator == "script.execute_preset":
|
||||
props.menu_idname = self.bl_idname
|
||||
|
||||
|
@ -135,7 +135,7 @@ class AddPresetBase:
|
||||
|
||||
file_preset.write("%s = %r\n" % (rna_path_step, value))
|
||||
|
||||
file_preset = open(filepath, 'w')
|
||||
file_preset = open(filepath, 'w', encoding="utf-8")
|
||||
file_preset.write("import bpy\n")
|
||||
|
||||
if hasattr(self, "preset_defines"):
|
||||
|
@ -215,20 +215,22 @@ class TEXT_MT_templates_py(Menu):
|
||||
bl_label = "Python"
|
||||
|
||||
def draw(self, context):
|
||||
self.path_menu(bpy.utils.script_paths("templates_py"),
|
||||
"text.open",
|
||||
{"internal": True},
|
||||
)
|
||||
self.path_menu(
|
||||
bpy.utils.script_paths("templates_py"),
|
||||
"text.open",
|
||||
props_default={"internal": True},
|
||||
)
|
||||
|
||||
|
||||
class TEXT_MT_templates_osl(Menu):
|
||||
bl_label = "Open Shading Language"
|
||||
|
||||
def draw(self, context):
|
||||
self.path_menu(bpy.utils.script_paths("templates_osl"),
|
||||
"text.open",
|
||||
{"internal": True},
|
||||
)
|
||||
self.path_menu(
|
||||
bpy.utils.script_paths("templates_osl"),
|
||||
"text.open",
|
||||
props_default={"internal": True},
|
||||
)
|
||||
|
||||
|
||||
class TEXT_MT_templates(Menu):
|
||||
|
@ -2149,6 +2149,8 @@ static int split_faces_prepare_new_verts(
|
||||
/* If vert is already used by another smooth fan, we need a new vert for this one. */
|
||||
const int new_vert_idx = vert_used ? num_verts++ : vert_idx;
|
||||
|
||||
BLI_assert(*lnor_space);
|
||||
|
||||
if ((*lnor_space)->loops) {
|
||||
for (LinkNode *lnode = (*lnor_space)->loops; lnode; lnode = lnode->next) {
|
||||
const int ml_fan_idx = GET_INT_FROM_POINTER(lnode->link);
|
||||
@ -2381,19 +2383,24 @@ void BKE_mesh_split_faces(Mesh *mesh, bool free_loop_normals)
|
||||
* loops' vertex and edge indices to new, to-be-created split ones). */
|
||||
|
||||
const int num_new_edges = split_faces_prepare_new_edges(mesh, &new_edges, memarena);
|
||||
BLI_assert(num_new_edges > 0);
|
||||
/* We can have to split a vertex without having to add a single new edge... */
|
||||
const bool do_edges = (num_new_edges > 0);
|
||||
|
||||
/* Reallocate all vert and edge related data. */
|
||||
mesh->totvert += num_new_verts;
|
||||
mesh->totedge += num_new_edges;
|
||||
CustomData_realloc(&mesh->vdata, mesh->totvert);
|
||||
CustomData_realloc(&mesh->edata, mesh->totedge);
|
||||
if (do_edges) {
|
||||
CustomData_realloc(&mesh->edata, mesh->totedge);
|
||||
}
|
||||
/* Update pointers to a newly allocated memory. */
|
||||
BKE_mesh_update_customdata_pointers(mesh, false);
|
||||
|
||||
/* Perform actual split of vertices and edges. */
|
||||
split_faces_split_new_verts(mesh, new_verts, num_new_verts);
|
||||
split_faces_split_new_edges(mesh, new_edges, num_new_edges);
|
||||
if (do_edges) {
|
||||
split_faces_split_new_edges(mesh, new_edges, num_new_edges);
|
||||
}
|
||||
}
|
||||
|
||||
/* Note: after this point mesh is expected to be valid again. */
|
||||
|
@ -304,13 +304,13 @@ void BKE_mesh_calc_normals_poly(
|
||||
void BKE_mesh_calc_normals(Mesh *mesh)
|
||||
{
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_START(BKE_mesh_calc_normals);
|
||||
TIMEIT_START_AVERAGED(BKE_mesh_calc_normals);
|
||||
#endif
|
||||
BKE_mesh_calc_normals_poly(mesh->mvert, NULL, mesh->totvert,
|
||||
mesh->mloop, mesh->mpoly, mesh->totloop, mesh->totpoly,
|
||||
NULL, false);
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_END(BKE_mesh_calc_normals);
|
||||
TIMEIT_END_AVERAGED(BKE_mesh_calc_normals);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -630,7 +630,6 @@ typedef struct LoopSplitTaskDataCommon {
|
||||
* Note we do not need to protect it, though, since two different tasks will *always* affect different
|
||||
* elements in the arrays. */
|
||||
MLoopNorSpaceArray *lnors_spacearr;
|
||||
BLI_bitmap *sharp_verts;
|
||||
float (*loopnors)[3];
|
||||
short (*clnors_data)[2];
|
||||
|
||||
@ -643,11 +642,8 @@ typedef struct LoopSplitTaskDataCommon {
|
||||
const int *loop_to_poly;
|
||||
const float (*polynors)[3];
|
||||
|
||||
int numLoops;
|
||||
int numPolys;
|
||||
|
||||
/* ***** Workers communication. ***** */
|
||||
ThreadQueue *task_queue;
|
||||
|
||||
} LoopSplitTaskDataCommon;
|
||||
|
||||
#define INDEX_UNSET INT_MIN
|
||||
@ -655,6 +651,50 @@ typedef struct LoopSplitTaskDataCommon {
|
||||
/* See comment about edge_to_loops below. */
|
||||
#define IS_EDGE_SHARP(_e2l) (ELEM((_e2l)[1], INDEX_UNSET, INDEX_INVALID))
|
||||
|
||||
static void loop_manifold_fan_around_vert_next(
|
||||
const MLoop *mloops, const MPoly *mpolys,
|
||||
const int *loop_to_poly, const int *e2lfan_curr, const uint mv_pivot_index,
|
||||
const MLoop **r_mlfan_curr, int *r_mlfan_curr_index, int *r_mlfan_vert_index, int *r_mpfan_curr_index)
|
||||
{
|
||||
const MLoop *mlfan_next;
|
||||
const MPoly *mpfan_next;
|
||||
|
||||
/* Warning! This is rather complex!
|
||||
* We have to find our next edge around the vertex (fan mode).
|
||||
* First we find the next loop, which is either previous or next to mlfan_curr_index, depending
|
||||
* whether both loops using current edge are in the same direction or not, and whether
|
||||
* mlfan_curr_index actually uses the vertex we are fanning around!
|
||||
* mlfan_curr_index is the index of mlfan_next here, and mlfan_next is not the real next one
|
||||
* (i.e. not the future mlfan_curr)...
|
||||
*/
|
||||
*r_mlfan_curr_index = (e2lfan_curr[0] == *r_mlfan_curr_index) ? e2lfan_curr[1] : e2lfan_curr[0];
|
||||
*r_mpfan_curr_index = loop_to_poly[*r_mlfan_curr_index];
|
||||
|
||||
BLI_assert(*r_mlfan_curr_index >= 0);
|
||||
BLI_assert(*r_mpfan_curr_index >= 0);
|
||||
|
||||
mlfan_next = &mloops[*r_mlfan_curr_index];
|
||||
mpfan_next = &mpolys[*r_mpfan_curr_index];
|
||||
if (((*r_mlfan_curr)->v == mlfan_next->v && (*r_mlfan_curr)->v == mv_pivot_index) ||
|
||||
((*r_mlfan_curr)->v != mlfan_next->v && (*r_mlfan_curr)->v != mv_pivot_index))
|
||||
{
|
||||
/* We need the previous loop, but current one is our vertex's loop. */
|
||||
*r_mlfan_vert_index = *r_mlfan_curr_index;
|
||||
if (--(*r_mlfan_curr_index) < mpfan_next->loopstart) {
|
||||
*r_mlfan_curr_index = mpfan_next->loopstart + mpfan_next->totloop - 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* We need the next loop, which is also our vertex's loop. */
|
||||
if (++(*r_mlfan_curr_index) >= mpfan_next->loopstart + mpfan_next->totloop) {
|
||||
*r_mlfan_curr_index = mpfan_next->loopstart;
|
||||
}
|
||||
*r_mlfan_vert_index = *r_mlfan_curr_index;
|
||||
}
|
||||
*r_mlfan_curr = &mloops[*r_mlfan_curr_index];
|
||||
/* And now we are back in sync, mlfan_curr_index is the index of mlfan_curr! Pff! */
|
||||
}
|
||||
|
||||
static void split_loop_nor_single_do(LoopSplitTaskDataCommon *common_data, LoopSplitTaskData *data)
|
||||
{
|
||||
MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr;
|
||||
@ -680,7 +720,7 @@ static void split_loop_nor_single_do(LoopSplitTaskDataCommon *common_data, LoopS
|
||||
*/
|
||||
copy_v3_v3(*lnor, polynors[mp_index]);
|
||||
|
||||
/* printf("BASIC: handling loop %d / edge %d / vert %d / poly %d\n", ml_curr_index, ml_curr->e, ml_curr->v, mp_index); */
|
||||
// printf("BASIC: handling loop %d / edge %d / vert %d / poly %d\n", ml_curr_index, ml_curr->e, ml_curr->v, mp_index);
|
||||
|
||||
/* If needed, generate this (simple!) lnor space. */
|
||||
if (lnors_spacearr) {
|
||||
@ -747,8 +787,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli
|
||||
const MEdge *me_org = &medges[ml_curr->e]; /* ml_curr would be mlfan_prev if we needed that one */
|
||||
const int *e2lfan_curr;
|
||||
float vec_curr[3], vec_prev[3], vec_org[3];
|
||||
const MLoop *mlfan_curr, *mlfan_next;
|
||||
const MPoly *mpfan_next;
|
||||
const MLoop *mlfan_curr;
|
||||
float lnor[3] = {0.0f, 0.0f, 0.0f};
|
||||
/* mlfan_vert_index: the loop of our current edge might not be the loop of our current vertex! */
|
||||
int mlfan_curr_index, mlfan_vert_index, mpfan_curr_index;
|
||||
@ -787,7 +826,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli
|
||||
}
|
||||
}
|
||||
|
||||
/* printf("FAN: vert %d, start edge %d\n", mv_pivot_index, ml_curr->e); */
|
||||
// printf("FAN: vert %d, start edge %d\n", mv_pivot_index, ml_curr->e);
|
||||
|
||||
while (true) {
|
||||
const MEdge *me_curr = &medges[mlfan_curr->e];
|
||||
@ -803,7 +842,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli
|
||||
normalize_v3(vec_curr);
|
||||
}
|
||||
|
||||
/* printf("\thandling edge %d / loop %d\n", mlfan_curr->e, mlfan_curr_index); */
|
||||
// printf("\thandling edge %d / loop %d\n", mlfan_curr->e, mlfan_curr_index);
|
||||
|
||||
{
|
||||
/* Code similar to accumulate_vertex_normals_poly. */
|
||||
@ -845,46 +884,16 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli
|
||||
/* Current edge is sharp and we have finished with this fan of faces around this vert,
|
||||
* or this vert is smooth, and we have completed a full turn around it.
|
||||
*/
|
||||
/* printf("FAN: Finished!\n"); */
|
||||
// printf("FAN: Finished!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
copy_v3_v3(vec_prev, vec_curr);
|
||||
|
||||
/* Warning! This is rather complex!
|
||||
* We have to find our next edge around the vertex (fan mode).
|
||||
* First we find the next loop, which is either previous or next to mlfan_curr_index, depending
|
||||
* whether both loops using current edge are in the same direction or not, and whether
|
||||
* mlfan_curr_index actually uses the vertex we are fanning around!
|
||||
* mlfan_curr_index is the index of mlfan_next here, and mlfan_next is not the real next one
|
||||
* (i.e. not the future mlfan_curr)...
|
||||
*/
|
||||
mlfan_curr_index = (e2lfan_curr[0] == mlfan_curr_index) ? e2lfan_curr[1] : e2lfan_curr[0];
|
||||
mpfan_curr_index = loop_to_poly[mlfan_curr_index];
|
||||
|
||||
BLI_assert(mlfan_curr_index >= 0);
|
||||
BLI_assert(mpfan_curr_index >= 0);
|
||||
|
||||
mlfan_next = &mloops[mlfan_curr_index];
|
||||
mpfan_next = &mpolys[mpfan_curr_index];
|
||||
if ((mlfan_curr->v == mlfan_next->v && mlfan_curr->v == mv_pivot_index) ||
|
||||
(mlfan_curr->v != mlfan_next->v && mlfan_curr->v != mv_pivot_index))
|
||||
{
|
||||
/* We need the previous loop, but current one is our vertex's loop. */
|
||||
mlfan_vert_index = mlfan_curr_index;
|
||||
if (--mlfan_curr_index < mpfan_next->loopstart) {
|
||||
mlfan_curr_index = mpfan_next->loopstart + mpfan_next->totloop - 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* We need the next loop, which is also our vertex's loop. */
|
||||
if (++mlfan_curr_index >= mpfan_next->loopstart + mpfan_next->totloop) {
|
||||
mlfan_curr_index = mpfan_next->loopstart;
|
||||
}
|
||||
mlfan_vert_index = mlfan_curr_index;
|
||||
}
|
||||
mlfan_curr = &mloops[mlfan_curr_index];
|
||||
/* And now we are back in sync, mlfan_curr_index is the index of mlfan_curr! Pff! */
|
||||
/* Find next loop of the smooth fan. */
|
||||
loop_manifold_fan_around_vert_next(
|
||||
mloops, mpolys, loop_to_poly, e2lfan_curr, mv_pivot_index,
|
||||
&mlfan_curr, &mlfan_curr_index, &mlfan_vert_index, &mpfan_curr_index);
|
||||
|
||||
e2lfan_curr = edge_to_loops[mlfan_curr->e];
|
||||
}
|
||||
@ -955,31 +964,25 @@ static void loop_split_worker_do(
|
||||
}
|
||||
}
|
||||
|
||||
static void loop_split_worker(TaskPool * __restrict UNUSED(pool), void *taskdata, int UNUSED(threadid))
|
||||
static void loop_split_worker(TaskPool * __restrict pool, void *taskdata, int UNUSED(threadid))
|
||||
{
|
||||
LoopSplitTaskDataCommon *common_data = taskdata;
|
||||
LoopSplitTaskData *data_buff;
|
||||
LoopSplitTaskDataCommon *common_data = BLI_task_pool_userdata(pool);
|
||||
LoopSplitTaskData *data = taskdata;
|
||||
|
||||
/* Temp edge vectors stack, only used when computing lnor spacearr. */
|
||||
BLI_Stack *edge_vectors = common_data->lnors_spacearr ? BLI_stack_new(sizeof(float[3]), __func__) : NULL;
|
||||
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_START(loop_split_worker);
|
||||
TIMEIT_START_AVERAGED(loop_split_worker);
|
||||
#endif
|
||||
|
||||
while ((data_buff = BLI_thread_queue_pop(common_data->task_queue))) {
|
||||
LoopSplitTaskData *data = data_buff;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LOOP_SPLIT_TASK_BLOCK_SIZE; i++, data++) {
|
||||
/* A NULL ml_curr is used to tag ended data! */
|
||||
if (data->ml_curr == NULL) {
|
||||
break;
|
||||
}
|
||||
loop_split_worker_do(common_data, data, edge_vectors);
|
||||
for (int i = 0; i < LOOP_SPLIT_TASK_BLOCK_SIZE; i++, data++) {
|
||||
/* A NULL ml_curr is used to tag ended data! */
|
||||
if (data->ml_curr == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
MEM_freeN(data_buff);
|
||||
loop_split_worker_do(common_data, data, edge_vectors);
|
||||
}
|
||||
|
||||
if (edge_vectors) {
|
||||
@ -987,38 +990,104 @@ static void loop_split_worker(TaskPool * __restrict UNUSED(pool), void *taskdata
|
||||
}
|
||||
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_END(loop_split_worker);
|
||||
TIMEIT_END_AVERAGED(loop_split_worker);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void loop_split_generator_do(LoopSplitTaskDataCommon *common_data, const bool threaded)
|
||||
/* Check whether gievn loop is part of an unknown-so-far cyclic smooth fan, or not.
|
||||
* Needed because cyclic smooth fans have no obvious 'entry point', and yet we need to walk them once, and only once. */
|
||||
static bool loop_split_generator_check_cyclic_smooth_fan(
|
||||
const MLoop *mloops, const MPoly *mpolys,
|
||||
const int (*edge_to_loops)[2], const int *loop_to_poly, const int *e2l_prev, BLI_bitmap *skip_loops,
|
||||
const MLoop *ml_curr, const MLoop *ml_prev, const int ml_curr_index, const int ml_prev_index,
|
||||
const int mp_curr_index)
|
||||
{
|
||||
const unsigned int mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */
|
||||
const int *e2lfan_curr;
|
||||
const MLoop *mlfan_curr;
|
||||
/* mlfan_vert_index: the loop of our current edge might not be the loop of our current vertex! */
|
||||
int mlfan_curr_index, mlfan_vert_index, mpfan_curr_index;
|
||||
|
||||
e2lfan_curr = e2l_prev;
|
||||
if (IS_EDGE_SHARP(e2lfan_curr)) {
|
||||
/* Sharp loop, so not a cyclic smooth fan... */
|
||||
return false;
|
||||
}
|
||||
|
||||
mlfan_curr = ml_prev;
|
||||
mlfan_curr_index = ml_prev_index;
|
||||
mlfan_vert_index = ml_curr_index;
|
||||
mpfan_curr_index = mp_curr_index;
|
||||
|
||||
BLI_assert(mlfan_curr_index >= 0);
|
||||
BLI_assert(mlfan_vert_index >= 0);
|
||||
BLI_assert(mpfan_curr_index >= 0);
|
||||
|
||||
BLI_assert(!BLI_BITMAP_TEST(skip_loops, mlfan_vert_index));
|
||||
BLI_BITMAP_ENABLE(skip_loops, mlfan_vert_index);
|
||||
|
||||
while(true) {
|
||||
/* Find next loop of the smooth fan. */
|
||||
loop_manifold_fan_around_vert_next(
|
||||
mloops, mpolys, loop_to_poly, e2lfan_curr, mv_pivot_index,
|
||||
&mlfan_curr, &mlfan_curr_index, &mlfan_vert_index, &mpfan_curr_index);
|
||||
|
||||
e2lfan_curr = edge_to_loops[mlfan_curr->e];
|
||||
|
||||
if (IS_EDGE_SHARP(e2lfan_curr)) {
|
||||
/* Sharp loop/edge, so not a cyclic smooth fan... */
|
||||
return false;
|
||||
}
|
||||
/* Smooth loop/edge... */
|
||||
else if (BLI_BITMAP_TEST(skip_loops, mlfan_vert_index)) {
|
||||
if (mlfan_vert_index == ml_curr_index) {
|
||||
/* We walked around a whole cyclic smooth fan without finding any already-processed loop, means we can
|
||||
* use initial ml_curr/ml_prev edge as start for this smooth fan. */
|
||||
return true;
|
||||
}
|
||||
/* ... already checked in some previous looping, we can abort. */
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
/* ... we can skip it in future, and keep checking the smooth fan. */
|
||||
BLI_BITMAP_ENABLE(skip_loops, mlfan_vert_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common_data)
|
||||
{
|
||||
MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr;
|
||||
BLI_bitmap *sharp_verts = common_data->sharp_verts;
|
||||
float (*loopnors)[3] = common_data->loopnors;
|
||||
|
||||
const MLoop *mloops = common_data->mloops;
|
||||
const MPoly *mpolys = common_data->mpolys;
|
||||
const int *loop_to_poly = common_data->loop_to_poly;
|
||||
const int (*edge_to_loops)[2] = common_data->edge_to_loops;
|
||||
const int numLoops = common_data->numLoops;
|
||||
const int numPolys = common_data->numPolys;
|
||||
|
||||
const MPoly *mp;
|
||||
int mp_index;
|
||||
|
||||
LoopSplitTaskData *data, *data_buff = NULL, data_mem;
|
||||
const MLoop *ml_curr;
|
||||
const MLoop *ml_prev;
|
||||
int ml_curr_index;
|
||||
int ml_prev_index;
|
||||
|
||||
BLI_bitmap *skip_loops = BLI_BITMAP_NEW(numLoops, __func__);
|
||||
|
||||
LoopSplitTaskData *data_buff = NULL;
|
||||
int data_idx = 0;
|
||||
|
||||
/* Temp edge vectors stack, only used when computing lnor spacearr (and we are not multi-threading). */
|
||||
BLI_Stack *edge_vectors = NULL;
|
||||
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_START(loop_split_generator);
|
||||
TIMEIT_START_AVERAGED(loop_split_generator);
|
||||
#endif
|
||||
|
||||
if (!threaded) {
|
||||
memset(&data_mem, 0, sizeof(data_mem));
|
||||
data = &data_mem;
|
||||
|
||||
if (!pool) {
|
||||
if (lnors_spacearr) {
|
||||
edge_vectors = BLI_stack_new(sizeof(float[3]), __func__);
|
||||
}
|
||||
@ -1028,11 +1097,10 @@ static void loop_split_generator_do(LoopSplitTaskDataCommon *common_data, const
|
||||
* Now, time to generate the normals.
|
||||
*/
|
||||
for (mp = mpolys, mp_index = 0; mp_index < numPolys; mp++, mp_index++) {
|
||||
const MLoop *ml_curr, *ml_prev;
|
||||
float (*lnors)[3];
|
||||
const int ml_last_index = (mp->loopstart + mp->totloop) - 1;
|
||||
int ml_curr_index = mp->loopstart;
|
||||
int ml_prev_index = ml_last_index;
|
||||
ml_curr_index = mp->loopstart;
|
||||
ml_prev_index = ml_last_index;
|
||||
|
||||
ml_curr = &mloops[ml_curr_index];
|
||||
ml_prev = &mloops[ml_prev_index];
|
||||
@ -1042,23 +1110,40 @@ static void loop_split_generator_do(LoopSplitTaskDataCommon *common_data, const
|
||||
const int *e2l_curr = edge_to_loops[ml_curr->e];
|
||||
const int *e2l_prev = edge_to_loops[ml_prev->e];
|
||||
|
||||
if (!IS_EDGE_SHARP(e2l_curr) && (!lnors_spacearr || BLI_BITMAP_TEST_BOOL(sharp_verts, ml_curr->v))) {
|
||||
/* A smooth edge, and we are not generating lnor_spacearr, or the related vertex is sharp.
|
||||
* We skip it because it is either:
|
||||
* - in the middle of a 'smooth fan' already computed (or that will be as soon as we hit
|
||||
* one of its ends, i.e. one of its two sharp edges), or...
|
||||
* - the related vertex is a "full smooth" one, in which case pre-populated normals from vertex
|
||||
* are just fine (or it has already be handled in a previous loop in case of needed lnors spacearr)!
|
||||
*/
|
||||
/* printf("Skipping loop %d / edge %d / vert %d(%d)\n", ml_curr_index, ml_curr->e, ml_curr->v, sharp_verts[ml_curr->v]); */
|
||||
// printf("Checking loop %d / edge %u / vert %u (sharp edge: %d, skiploop: %d)...",
|
||||
// ml_curr_index, ml_curr->e, ml_curr->v, IS_EDGE_SHARP(e2l_curr), BLI_BITMAP_TEST_BOOL(skip_loops, ml_curr_index));
|
||||
|
||||
/* A smooth edge, we have to check for cyclic smooth fan case.
|
||||
* If we find a new, never-processed cyclic smooth fan, we can do it now using that loop/edge as
|
||||
* 'entry point', otherwise we can skip it. */
|
||||
/* Note: In theory, we could make loop_split_generator_check_cyclic_smooth_fan() store
|
||||
* mlfan_vert_index'es and edge indexes in two stacks, to avoid having to fan again around the vert during
|
||||
* actual computation of clnor & clnorspace. However, this would complicate the code, add more memory usage,
|
||||
* and despite its logical complexity, loop_manifold_fan_around_vert_next() is quite cheap in term of
|
||||
* CPU cycles, so really think it's not worth it. */
|
||||
if (!IS_EDGE_SHARP(e2l_curr) &&
|
||||
(BLI_BITMAP_TEST(skip_loops, ml_curr_index) ||
|
||||
!loop_split_generator_check_cyclic_smooth_fan(
|
||||
mloops, mpolys, edge_to_loops, loop_to_poly, e2l_prev, skip_loops,
|
||||
ml_curr, ml_prev, ml_curr_index, ml_prev_index, mp_index)))
|
||||
{
|
||||
// printf("SKIPPING!\n");
|
||||
}
|
||||
else {
|
||||
if (threaded) {
|
||||
LoopSplitTaskData *data, data_local;
|
||||
|
||||
// printf("PROCESSING!\n");
|
||||
|
||||
if (pool) {
|
||||
if (data_idx == 0) {
|
||||
data_buff = MEM_callocN(sizeof(*data_buff) * LOOP_SPLIT_TASK_BLOCK_SIZE, __func__);
|
||||
}
|
||||
data = &data_buff[data_idx];
|
||||
}
|
||||
else {
|
||||
data = &data_local;
|
||||
memset(data, 0, sizeof(*data));
|
||||
}
|
||||
|
||||
if (IS_EDGE_SHARP(e2l_curr) && IS_EDGE_SHARP(e2l_prev)) {
|
||||
data->lnor = lnors;
|
||||
@ -1094,22 +1179,18 @@ static void loop_split_generator_do(LoopSplitTaskDataCommon *common_data, const
|
||||
data->mp_index = mp_index;
|
||||
if (lnors_spacearr) {
|
||||
data->lnor_space = BKE_lnor_space_create(lnors_spacearr);
|
||||
/* Tag related vertex as sharp, to avoid fanning around it again (in case it was a smooth one).
|
||||
* This *has* to be done outside of workers tasks! */
|
||||
BLI_BITMAP_ENABLE(sharp_verts, ml_curr->v);
|
||||
}
|
||||
}
|
||||
|
||||
if (threaded) {
|
||||
if (pool) {
|
||||
data_idx++;
|
||||
if (data_idx == LOOP_SPLIT_TASK_BLOCK_SIZE) {
|
||||
BLI_thread_queue_push(common_data->task_queue, data_buff);
|
||||
BLI_task_pool_push(pool, loop_split_worker, data_buff, true, TASK_PRIORITY_LOW);
|
||||
data_idx = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
loop_split_worker_do(common_data, data, edge_vectors);
|
||||
memset(data, 0, sizeof(data_mem));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1118,38 +1199,27 @@ static void loop_split_generator_do(LoopSplitTaskDataCommon *common_data, const
|
||||
}
|
||||
}
|
||||
|
||||
if (threaded) {
|
||||
/* Last block of data... Since it is calloc'ed and we use first NULL item as stopper, everything is fine. */
|
||||
if (LIKELY(data_idx)) {
|
||||
BLI_thread_queue_push(common_data->task_queue, data_buff);
|
||||
}
|
||||
|
||||
/* This will signal all other worker threads to wake up and finish! */
|
||||
BLI_thread_queue_nowait(common_data->task_queue);
|
||||
/* Last block of data... Since it is calloc'ed and we use first NULL item as stopper, everything is fine. */
|
||||
if (pool && data_idx) {
|
||||
BLI_task_pool_push(pool, loop_split_worker, data_buff, true, TASK_PRIORITY_LOW);
|
||||
}
|
||||
|
||||
if (edge_vectors) {
|
||||
BLI_stack_free(edge_vectors);
|
||||
}
|
||||
MEM_freeN(skip_loops);
|
||||
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_END(loop_split_generator);
|
||||
TIMEIT_END_AVERAGED(loop_split_generator);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void loop_split_generator(TaskPool * __restrict UNUSED(pool), void *taskdata, int UNUSED(threadid))
|
||||
{
|
||||
LoopSplitTaskDataCommon *common_data = taskdata;
|
||||
|
||||
loop_split_generator_do(common_data, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute split normals, i.e. vertex normals associated with each poly (hence 'loop normals').
|
||||
* Useful to materialize sharp edges (or non-smooth faces) without actually modifying the geometry (splitting edges).
|
||||
*/
|
||||
void BKE_mesh_normals_loop_split(
|
||||
const MVert *mverts, const int numVerts, MEdge *medges, const int numEdges,
|
||||
const MVert *mverts, const int UNUSED(numVerts), MEdge *medges, const int numEdges,
|
||||
MLoop *mloops, float (*r_loopnors)[3], const int numLoops,
|
||||
MPoly *mpolys, const float (*polynors)[3], const int numPolys,
|
||||
const bool use_split_normals, float split_angle,
|
||||
@ -1187,8 +1257,6 @@ void BKE_mesh_normals_loop_split(
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
/* Mapping edge -> loops.
|
||||
* If that edge is used by more than two loops (polys), it is always sharp (and tagged as such, see below).
|
||||
* We also use the second loop index as a kind of flag: smooth edge: > 0,
|
||||
@ -1198,33 +1266,25 @@ void BKE_mesh_normals_loop_split(
|
||||
* store the negated value of loop index instead of INDEX_INVALID to retrieve the real value later in code).
|
||||
* Note also that lose edges always have both values set to 0!
|
||||
*/
|
||||
int (*edge_to_loops)[2] = MEM_callocN(sizeof(int[2]) * (size_t)numEdges, __func__);
|
||||
int (*edge_to_loops)[2] = MEM_callocN(sizeof(*edge_to_loops) * (size_t)numEdges, __func__);
|
||||
|
||||
/* Simple mapping from a loop to its polygon index. */
|
||||
int *loop_to_poly = r_loop_to_poly ? r_loop_to_poly : MEM_mallocN(sizeof(int) * (size_t)numLoops, __func__);
|
||||
int *loop_to_poly = r_loop_to_poly ? r_loop_to_poly : MEM_mallocN(sizeof(*loop_to_poly) * (size_t)numLoops, __func__);
|
||||
|
||||
MPoly *mp;
|
||||
int mp_index, me_index;
|
||||
bool check_angle = (split_angle < (float)M_PI);
|
||||
int i;
|
||||
int mp_index;
|
||||
|
||||
/* When using custom loop normals, disable the angle feature! */
|
||||
const bool check_angle = (split_angle < (float)M_PI) && (clnors_data == NULL);
|
||||
|
||||
BLI_bitmap *sharp_verts = NULL;
|
||||
MLoopNorSpaceArray _lnors_spacearr = {NULL};
|
||||
|
||||
LoopSplitTaskDataCommon common_data = {NULL};
|
||||
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_START(BKE_mesh_normals_loop_split);
|
||||
TIMEIT_START_AVERAGED(BKE_mesh_normals_loop_split);
|
||||
#endif
|
||||
|
||||
if (check_angle) {
|
||||
/* When using custom loop normals, disable the angle feature! */
|
||||
if (clnors_data) {
|
||||
check_angle = false;
|
||||
}
|
||||
else {
|
||||
split_angle = cosf(split_angle);
|
||||
}
|
||||
split_angle = cosf(split_angle);
|
||||
}
|
||||
|
||||
if (!r_lnors_spacearr && clnors_data) {
|
||||
@ -1233,7 +1293,6 @@ void BKE_mesh_normals_loop_split(
|
||||
}
|
||||
if (r_lnors_spacearr) {
|
||||
BKE_lnor_spacearr_init(r_lnors_spacearr, numLoops);
|
||||
sharp_verts = BLI_BITMAP_NEW((size_t)numVerts, __func__);
|
||||
}
|
||||
|
||||
/* This first loop check which edges are actually smooth, and compute edge vectors. */
|
||||
@ -1287,60 +1346,38 @@ void BKE_mesh_normals_loop_split(
|
||||
}
|
||||
}
|
||||
|
||||
if (r_lnors_spacearr) {
|
||||
/* Tag vertices that have at least one sharp edge as 'sharp' (used for the lnor spacearr computation).
|
||||
* XXX This third loop over edges is a bit disappointing, could not find any other way yet.
|
||||
* Not really performance-critical anyway.
|
||||
*/
|
||||
for (me_index = 0; me_index < numEdges; me_index++) {
|
||||
const int *e2l = edge_to_loops[me_index];
|
||||
const MEdge *me = &medges[me_index];
|
||||
if (IS_EDGE_SHARP(e2l)) {
|
||||
BLI_BITMAP_ENABLE(sharp_verts, me->v1);
|
||||
BLI_BITMAP_ENABLE(sharp_verts, me->v2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Init data common to all tasks. */
|
||||
common_data.lnors_spacearr = r_lnors_spacearr;
|
||||
common_data.loopnors = r_loopnors;
|
||||
common_data.clnors_data = clnors_data;
|
||||
|
||||
common_data.mverts = mverts;
|
||||
common_data.medges = medges;
|
||||
common_data.mloops = mloops;
|
||||
common_data.mpolys = mpolys;
|
||||
common_data.sharp_verts = sharp_verts;
|
||||
common_data.edge_to_loops = (const int(*)[2])edge_to_loops;
|
||||
common_data.loop_to_poly = loop_to_poly;
|
||||
common_data.polynors = polynors;
|
||||
common_data.numPolys = numPolys;
|
||||
LoopSplitTaskDataCommon common_data = {
|
||||
.lnors_spacearr = r_lnors_spacearr,
|
||||
.loopnors = r_loopnors,
|
||||
.clnors_data = clnors_data,
|
||||
.mverts = mverts,
|
||||
.medges = medges,
|
||||
.mloops = mloops,
|
||||
.mpolys = mpolys,
|
||||
.edge_to_loops = (const int(*)[2])edge_to_loops,
|
||||
.loop_to_poly = loop_to_poly,
|
||||
.polynors = polynors,
|
||||
.numLoops = numLoops,
|
||||
.numPolys = numPolys,
|
||||
};
|
||||
|
||||
if (numLoops < LOOP_SPLIT_TASK_BLOCK_SIZE * 8) {
|
||||
/* Not enough loops to be worth the whole threading overhead... */
|
||||
loop_split_generator_do(&common_data, false);
|
||||
loop_split_generator(NULL, &common_data);
|
||||
}
|
||||
else {
|
||||
TaskScheduler *task_scheduler;
|
||||
TaskPool *task_pool;
|
||||
int nbr_workers;
|
||||
|
||||
common_data.task_queue = BLI_thread_queue_init();
|
||||
|
||||
task_scheduler = BLI_task_scheduler_get();
|
||||
task_pool = BLI_task_pool_create(task_scheduler, NULL);
|
||||
task_pool = BLI_task_pool_create(task_scheduler, &common_data);
|
||||
|
||||
loop_split_generator(task_pool, &common_data);
|
||||
|
||||
nbr_workers = max_ii(2, BLI_task_scheduler_num_threads(task_scheduler));
|
||||
for (i = 1; i < nbr_workers; i++) {
|
||||
BLI_task_pool_push(task_pool, loop_split_worker, &common_data, false, TASK_PRIORITY_HIGH);
|
||||
}
|
||||
BLI_task_pool_push(task_pool, loop_split_generator, &common_data, false, TASK_PRIORITY_HIGH);
|
||||
BLI_task_pool_work_and_wait(task_pool);
|
||||
|
||||
BLI_task_pool_free(task_pool);
|
||||
|
||||
BLI_thread_queue_free(common_data.task_queue);
|
||||
}
|
||||
|
||||
MEM_freeN(edge_to_loops);
|
||||
@ -1349,17 +1386,14 @@ void BKE_mesh_normals_loop_split(
|
||||
}
|
||||
|
||||
if (r_lnors_spacearr) {
|
||||
MEM_freeN(sharp_verts);
|
||||
if (r_lnors_spacearr == &_lnors_spacearr) {
|
||||
BKE_lnor_spacearr_free(r_lnors_spacearr);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_END(BKE_mesh_normals_loop_split);
|
||||
TIMEIT_END_AVERAGED(BKE_mesh_normals_loop_split);
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#undef INDEX_UNSET
|
||||
|
@ -486,8 +486,7 @@ static void bm_mesh_edges_sharp_tag(
|
||||
BMesh *bm, const float (*vnos)[3], const float (*fnos)[3], float split_angle,
|
||||
float (*r_lnos)[3])
|
||||
{
|
||||
BMIter eiter, viter;
|
||||
BMVert *v;
|
||||
BMIter eiter;
|
||||
BMEdge *e;
|
||||
int i;
|
||||
|
||||
@ -498,19 +497,13 @@ static void bm_mesh_edges_sharp_tag(
|
||||
}
|
||||
|
||||
{
|
||||
char htype = BM_LOOP;
|
||||
char htype = BM_VERT | BM_LOOP;
|
||||
if (fnos) {
|
||||
htype |= BM_FACE;
|
||||
}
|
||||
BM_mesh_elem_index_ensure(bm, htype);
|
||||
}
|
||||
|
||||
/* Clear all vertices' tags (means they are all smooth for now). */
|
||||
BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) {
|
||||
BM_elem_index_set(v, i); /* set_inline */
|
||||
BM_elem_flag_disable(v, BM_ELEM_TAG);
|
||||
}
|
||||
|
||||
/* This first loop checks which edges are actually smooth, and pre-populate lnos with vnos (as if they were
|
||||
* all smooth).
|
||||
*/
|
||||
@ -551,20 +544,45 @@ static void bm_mesh_edges_sharp_tag(
|
||||
no = vnos ? vnos[BM_elem_index_get(l_b->v)] : l_b->v->no;
|
||||
copy_v3_v3(r_lnos[BM_elem_index_get(l_b)], no);
|
||||
}
|
||||
else {
|
||||
/* Sharp edge, tag its verts as such. */
|
||||
BM_elem_flag_enable(e->v1, BM_ELEM_TAG);
|
||||
BM_elem_flag_enable(e->v2, BM_ELEM_TAG);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Sharp edge, tag its verts as such. */
|
||||
BM_elem_flag_enable(e->v1, BM_ELEM_TAG);
|
||||
BM_elem_flag_enable(e->v2, BM_ELEM_TAG);
|
||||
}
|
||||
}
|
||||
|
||||
bm->elem_index_dirty &= ~(BM_EDGE | BM_VERT);
|
||||
bm->elem_index_dirty &= ~BM_EDGE;
|
||||
}
|
||||
|
||||
/* Check whether gievn loop is part of an unknown-so-far cyclic smooth fan, or not.
|
||||
* Needed because cyclic smooth fans have no obvious 'entry point', and yet we need to walk them once, and only once. */
|
||||
static bool bm_mesh_loop_check_cyclic_smooth_fan(BMLoop *l_curr)
|
||||
{
|
||||
BMLoop *lfan_pivot_next = l_curr;
|
||||
BMEdge *e_next = l_curr->e;
|
||||
|
||||
BLI_assert(!BM_elem_flag_test(lfan_pivot_next, BM_ELEM_TAG));
|
||||
BM_elem_flag_enable(lfan_pivot_next, BM_ELEM_TAG);
|
||||
|
||||
while (true) {
|
||||
/* Much simpler than in sibling code with basic Mesh data! */
|
||||
lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot_next, &e_next);
|
||||
|
||||
if (!lfan_pivot_next || !BM_elem_flag_test(e_next, BM_ELEM_TAG)) {
|
||||
/* Sharp loop/edge, so not a cyclic smooth fan... */
|
||||
return false;
|
||||
}
|
||||
/* Smooth loop/edge... */
|
||||
else if (BM_elem_flag_test(lfan_pivot_next, BM_ELEM_TAG)) {
|
||||
if (lfan_pivot_next == l_curr) {
|
||||
/* We walked around a whole cyclic smooth fan without finding any already-processed loop, means we can
|
||||
* use initial l_curr/l_prev edge as start for this smooth fan. */
|
||||
return true;
|
||||
}
|
||||
/* ... already checked in some previous looping, we can abort. */
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
/* ... we can skip it in future, and keep checking the smooth fan. */
|
||||
BM_elem_flag_enable(lfan_pivot_next, BM_ELEM_TAG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* BMesh version of BKE_mesh_normals_loop_split() in mesh_evaluate.c
|
||||
@ -587,13 +605,11 @@ static void bm_mesh_loops_calc_normals(
|
||||
BLI_Stack *edge_vectors = NULL;
|
||||
|
||||
{
|
||||
char htype = BM_LOOP;
|
||||
char htype = 0;
|
||||
if (vcos) {
|
||||
htype |= BM_VERT;
|
||||
}
|
||||
if (fnos) {
|
||||
htype |= BM_FACE;
|
||||
}
|
||||
/* Face/Loop indices are set inline below. */
|
||||
BM_mesh_elem_index_ensure(bm, htype);
|
||||
}
|
||||
|
||||
@ -606,6 +622,21 @@ static void bm_mesh_loops_calc_normals(
|
||||
edge_vectors = BLI_stack_new(sizeof(float[3]), __func__);
|
||||
}
|
||||
|
||||
/* Clear all loops' tags (means none are to be skipped for now). */
|
||||
int index_face, index_loop = 0;
|
||||
BM_ITER_MESH_INDEX (f_curr, &fiter, bm, BM_FACES_OF_MESH, index_face) {
|
||||
BMLoop *l_curr, *l_first;
|
||||
|
||||
BM_elem_index_set(f_curr, index_face); /* set_inline */
|
||||
|
||||
l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr);
|
||||
do {
|
||||
BM_elem_index_set(l_curr, index_loop++); /* set_inline */
|
||||
BM_elem_flag_disable(l_curr, BM_ELEM_TAG);
|
||||
} while ((l_curr = l_curr->next) != l_first);
|
||||
}
|
||||
bm->elem_index_dirty &= ~(BM_FACE|BM_LOOP);
|
||||
|
||||
/* We now know edges that can be smoothed (they are tagged), and edges that will be hard (they aren't).
|
||||
* Now, time to generate the normals.
|
||||
*/
|
||||
@ -614,16 +645,16 @@ static void bm_mesh_loops_calc_normals(
|
||||
|
||||
l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr);
|
||||
do {
|
||||
/* A smooth edge, we have to check for cyclic smooth fan case.
|
||||
* If we find a new, never-processed cyclic smooth fan, we can do it now using that loop/edge as
|
||||
* 'entry point', otherwise we can skip it. */
|
||||
/* Note: In theory, we could make bm_mesh_loop_check_cyclic_smooth_fan() store mlfan_pivot's in a stack,
|
||||
* to avoid having to fan again around the vert during actual computation of clnor & clnorspace.
|
||||
* However, this would complicate the code, add more memory usage, and BM_vert_step_fan_loop()
|
||||
* is quite cheap in term of CPU cycles, so really think it's not worth it. */
|
||||
if (BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
|
||||
(!r_lnors_spacearr || BM_elem_flag_test(l_curr->v, BM_ELEM_TAG)))
|
||||
(BM_elem_flag_test(l_curr, BM_ELEM_TAG) || !bm_mesh_loop_check_cyclic_smooth_fan(l_curr)))
|
||||
{
|
||||
/* A smooth edge, and we are not generating lnors_spacearr, or the related vertex is sharp.
|
||||
* We skip it because it is either:
|
||||
* - in the middle of a 'smooth fan' already computed (or that will be as soon as we hit
|
||||
* one of its ends, i.e. one of its two sharp edges), or...
|
||||
* - the related vertex is a "full smooth" one, in which case pre-populated normals from vertex
|
||||
* are just fine!
|
||||
*/
|
||||
}
|
||||
else if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
|
||||
!BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG))
|
||||
@ -2008,4 +2039,4 @@ void BM_mesh_toolflags_set(BMesh *bm, bool use_toolflags)
|
||||
vpool_dst, epool_dst, NULL, fpool_dst);
|
||||
|
||||
bm->use_toolflags = use_toolflags;
|
||||
}
|
||||
}
|
||||
|
@ -1284,7 +1284,7 @@ static BMOpDefine bmo_bisect_plane_def = {
|
||||
{"clear_inner", BMO_OP_SLOT_BOOL}, /* when enabled. remove all geometry on the negative side of the plane */
|
||||
{{'\0'}},
|
||||
},
|
||||
{{"geom_cut.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_VERT | BM_EDGE}}, /* output new geometry from the cut */
|
||||
{{"geom_cut.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_VERT | BM_EDGE}}, /* output geometry aligned with the plane (new and existing) */
|
||||
{"geom.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_VERT | BM_EDGE | BM_FACE}}, /* input and output geometry (result of cut) */
|
||||
{{'\0'}}},
|
||||
bmo_bisect_plane_exec,
|
||||
|
@ -38,7 +38,8 @@
|
||||
#include "intern/bmesh_operators_private.h" /* own include */
|
||||
|
||||
#define ELE_NEW 1
|
||||
#define ELE_INPUT 2
|
||||
#define ELE_CUT 2
|
||||
#define ELE_INPUT 4
|
||||
|
||||
void bmo_bisect_plane_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
@ -69,7 +70,7 @@ void bmo_bisect_plane_exec(BMesh *bm, BMOperator *op)
|
||||
|
||||
|
||||
BM_mesh_bisect_plane(bm, plane, use_snap_center, true,
|
||||
ELE_NEW, dist);
|
||||
ELE_CUT, ELE_NEW, dist);
|
||||
|
||||
|
||||
if (clear_outer || clear_inner) {
|
||||
@ -108,5 +109,5 @@ void bmo_bisect_plane_exec(BMesh *bm, BMOperator *op)
|
||||
}
|
||||
|
||||
BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "geom.out", BM_ALL_NOLOOP, ELE_NEW | ELE_INPUT);
|
||||
BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "geom_cut.out", BM_VERT | BM_EDGE, ELE_NEW);
|
||||
BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "geom_cut.out", BM_VERT | BM_EDGE, ELE_CUT);
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ static int bm_vert_sortval_cb(const void *v_a_v, const void *v_b_v)
|
||||
}
|
||||
|
||||
|
||||
static void bm_face_bisect_verts(BMesh *bm, BMFace *f, const float plane[4], const short oflag_center)
|
||||
static void bm_face_bisect_verts(BMesh *bm, BMFace *f, const float plane[4], const short oflag_center, const short oflag_new)
|
||||
{
|
||||
/* unlikely more than 2 verts are needed */
|
||||
const unsigned int f_len_orig = (unsigned int)f->len;
|
||||
@ -154,10 +154,11 @@ static void bm_face_bisect_verts(BMesh *bm, BMFace *f, const float plane[4], con
|
||||
/* common case, just cut the face once */
|
||||
BM_face_split(bm, f, l_a, l_b, &l_new, NULL, true);
|
||||
if (l_new) {
|
||||
if (oflag_center) {
|
||||
BMO_edge_flag_enable(bm, l_new->e, oflag_center);
|
||||
BMO_face_flag_enable(bm, l_new->f, oflag_center);
|
||||
BMO_face_flag_enable(bm, f, oflag_center);
|
||||
if (oflag_center | oflag_new) {
|
||||
BMO_edge_flag_enable(bm, l_new->e, oflag_center | oflag_new);
|
||||
}
|
||||
if (oflag_new) {
|
||||
BMO_face_flag_enable(bm, l_new->f, oflag_new);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -269,10 +270,11 @@ static void bm_face_bisect_verts(BMesh *bm, BMFace *f, const float plane[4], con
|
||||
f_tmp = BM_face_split(bm, face_split_arr[j], l_a, l_b, &l_new, NULL, true);
|
||||
|
||||
if (l_new) {
|
||||
if (oflag_center) {
|
||||
BMO_edge_flag_enable(bm, l_new->e, oflag_center);
|
||||
BMO_face_flag_enable(bm, l_new->f, oflag_center);
|
||||
BMO_face_flag_enable(bm, face_split_arr[j], oflag_center);
|
||||
if (oflag_center | oflag_new) {
|
||||
BMO_edge_flag_enable(bm, l_new->e, oflag_center | oflag_new);
|
||||
}
|
||||
if (oflag_new) {
|
||||
BMO_face_flag_enable(bm, l_new->f, oflag_new);
|
||||
}
|
||||
}
|
||||
|
||||
@ -307,7 +309,7 @@ finally:
|
||||
void BM_mesh_bisect_plane(
|
||||
BMesh *bm, const float plane[4],
|
||||
const bool use_snap_center, const bool use_tag,
|
||||
const short oflag_center, const float eps)
|
||||
const short oflag_center, const short oflag_new, const float eps)
|
||||
{
|
||||
unsigned int einput_len;
|
||||
unsigned int i;
|
||||
@ -390,7 +392,7 @@ void BM_mesh_bisect_plane(
|
||||
const float dist[2] = {BM_VERT_DIST(e->v1), BM_VERT_DIST(e->v2)};
|
||||
|
||||
if (side[0] && side[1] && (side[0] != side[1])) {
|
||||
const float e_fac = fabsf(dist[0]) / fabsf(dist[0] - dist[1]);
|
||||
const float e_fac = dist[0] / (dist[0] - dist[1]);
|
||||
BMVert *v_new;
|
||||
|
||||
if (e->l) {
|
||||
@ -404,10 +406,17 @@ void BM_mesh_bisect_plane(
|
||||
} while ((l_iter = l_iter->radial_next) != l_first);
|
||||
}
|
||||
|
||||
v_new = BM_edge_split(bm, e, e->v1, NULL, e_fac);
|
||||
{
|
||||
BMEdge *e_new;
|
||||
v_new = BM_edge_split(bm, e, e->v1, &e_new, e_fac);
|
||||
if (oflag_new) {
|
||||
BMO_edge_flag_enable(bm, e_new, oflag_new);
|
||||
}
|
||||
}
|
||||
|
||||
vert_is_center_enable(v_new);
|
||||
if (oflag_center) {
|
||||
BMO_vert_flag_enable(bm, v_new, oflag_center);
|
||||
if (oflag_new | oflag_center) {
|
||||
BMO_vert_flag_enable(bm, v_new, oflag_new | oflag_center);
|
||||
}
|
||||
|
||||
BM_VERT_DIR(v_new) = 0;
|
||||
@ -448,7 +457,7 @@ void BM_mesh_bisect_plane(
|
||||
MEM_freeN(edges_arr);
|
||||
|
||||
while ((f = BLI_LINKSTACK_POP(face_stack))) {
|
||||
bm_face_bisect_verts(bm, f, plane, oflag_center);
|
||||
bm_face_bisect_verts(bm, f, plane, oflag_center, oflag_new);
|
||||
}
|
||||
|
||||
/* now we have all faces to split in the stack */
|
||||
|
@ -30,6 +30,6 @@
|
||||
void BM_mesh_bisect_plane(
|
||||
BMesh *bm, const float plane[4],
|
||||
const bool use_snap_center, const bool use_tag,
|
||||
const short oflag_center, const float eps);
|
||||
const short oflag_center, const short oflag_new, const float eps);
|
||||
|
||||
#endif /* __BMESH_BISECT_PLANE_H__ */
|
||||
|
@ -156,11 +156,6 @@ void ArmatureExporter::find_objects_using_armature(Object *ob_arm, std::vector<O
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string ArmatureExporter::get_joint_sid(Bone *bone, Object *ob_arm)
|
||||
{
|
||||
return get_joint_id(bone, ob_arm);
|
||||
}
|
||||
|
||||
// parent_mat is armature-space
|
||||
void ArmatureExporter::add_bone_node(Bone *bone, Object *ob_arm, Scene *sce,
|
||||
SceneExporter *se,
|
||||
|
@ -83,8 +83,6 @@ private:
|
||||
void find_objects_using_armature(Object *ob_arm, std::vector<Object *>& objects, Scene *sce);
|
||||
#endif
|
||||
|
||||
std::string get_joint_sid(Bone *bone, Object *ob_arm);
|
||||
|
||||
// Scene, SceneExporter and the list of child_objects
|
||||
// are required for writing bone parented objects
|
||||
void add_bone_node(Bone *bone, Object *ob_arm, Scene *sce, SceneExporter *se,
|
||||
|
@ -106,7 +106,7 @@ int ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBon
|
||||
*/
|
||||
|
||||
std::map<COLLADAFW::UniqueId, SkinInfo>::iterator skin_it;
|
||||
bool bone_is_not_skinned = true;
|
||||
bool bone_is_skinned = false;
|
||||
for (skin_it = skin_by_data_uid.begin(); skin_it != skin_by_data_uid.end(); skin_it++) {
|
||||
|
||||
SkinInfo *b = &skin_it->second;
|
||||
@ -123,13 +123,13 @@ int ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBon
|
||||
mul_m4_m4m4(mat, invmat, mat);
|
||||
}
|
||||
|
||||
bone_is_not_skinned = false;
|
||||
bone_is_skinned = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// create a bone even if there's no joint data for it (i.e. it has no influence)
|
||||
if (bone_is_not_skinned) {
|
||||
if (!bone_is_skinned) {
|
||||
float obmat[4][4];
|
||||
// bone-space
|
||||
get_node_mat(obmat, node, NULL, NULL);
|
||||
@ -141,6 +141,7 @@ int ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBon
|
||||
else {
|
||||
copy_m4_m4(mat, obmat);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (parent) bone->parent = parent;
|
||||
|
@ -157,11 +157,6 @@ void ArmatureExporter::find_objects_using_armature(Object *ob_arm, std::vector<O
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string ControllerExporter::get_joint_sid(Bone *bone, Object *ob_arm)
|
||||
{
|
||||
return get_joint_id(bone, ob_arm);
|
||||
}
|
||||
|
||||
std::string ControllerExporter::get_controller_id(Object *ob_arm, Object *ob)
|
||||
{
|
||||
return translate_id(id_name(ob_arm)) + "_" + translate_id(id_name(ob)) + SKIN_CONTROLLER_ID_SUFFIX;
|
||||
|
@ -84,8 +84,6 @@ private:
|
||||
void find_objects_using_armature(Object *ob_arm, std::vector<Object *>& objects, Scene *sce);
|
||||
#endif
|
||||
|
||||
std::string get_joint_sid(Bone *bone, Object *ob_arm);
|
||||
|
||||
std::string get_controller_id(Object *ob_arm, Object *ob);
|
||||
|
||||
std::string get_controller_id(Key *key, Object *ob);
|
||||
|
@ -341,7 +341,12 @@ std::string get_light_id(Object *ob)
|
||||
|
||||
std::string get_joint_id(Bone *bone, Object *ob_arm)
|
||||
{
|
||||
return translate_id(/*id_name(ob_arm) + "_" +*/ bone->name);
|
||||
return translate_id(id_name(ob_arm) + "_" + bone->name);
|
||||
}
|
||||
|
||||
std::string get_joint_sid(Bone *bone, Object *ob_arm)
|
||||
{
|
||||
return translate_id(bone->name);
|
||||
}
|
||||
|
||||
std::string get_camera_id(Object *ob)
|
||||
|
@ -104,6 +104,7 @@ extern std::string get_geometry_id(Object *ob, bool use_instantiation);
|
||||
extern std::string get_light_id(Object *ob);
|
||||
|
||||
extern std::string get_joint_id(Bone *bone, Object *ob_arm);
|
||||
extern std::string get_joint_sid(Bone *bone, Object *ob_arm);
|
||||
|
||||
extern std::string get_camera_id(Object *ob);
|
||||
|
||||
|
@ -2908,10 +2908,23 @@ static void SCREEN_OT_spacedata_cleanup(wmOperatorType *ot)
|
||||
|
||||
static int repeat_last_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
wmOperator *lastop = CTX_wm_manager(C)->operators.last;
|
||||
|
||||
if (lastop)
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
wmOperator *lastop = wm->operators.last;
|
||||
|
||||
/* Seek last registered operator */
|
||||
while (lastop) {
|
||||
if (lastop->type->flag & OPTYPE_REGISTER) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
lastop = lastop->prev;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastop) {
|
||||
WM_operator_free_all_after(wm, lastop);
|
||||
WM_operator_repeat(C, lastop);
|
||||
}
|
||||
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
@ -2946,8 +2959,9 @@ static int repeat_history_invoke(bContext *C, wmOperator *op, const wmEvent *UNU
|
||||
layout = UI_popup_menu_layout(pup);
|
||||
|
||||
for (i = items - 1, lastop = wm->operators.last; lastop; lastop = lastop->prev, i--)
|
||||
if (WM_operator_repeat_check(C, lastop))
|
||||
if ((lastop->type->flag & OPTYPE_REGISTER) && WM_operator_repeat_check(C, lastop)) {
|
||||
uiItemIntO(layout, RNA_struct_ui_name(lastop->type->srna), ICON_NONE, op->type->idname, "index", i);
|
||||
}
|
||||
|
||||
UI_popup_menu_end(C, pup);
|
||||
|
||||
|
@ -1449,7 +1449,7 @@ static void draw_b_bone(const short dt, int armflag, int boneflag, short constfl
|
||||
else {
|
||||
/* wire */
|
||||
if (armflag & ARM_POSEMODE) {
|
||||
if (constflag) {
|
||||
if (constflag && ((G.f & G_PICKSEL) == 0)) {
|
||||
/* set constraint colors */
|
||||
if (set_pchan_color(PCHAN_COLOR_CONSTS, boneflag, constflag)) {
|
||||
glEnable(GL_BLEND);
|
||||
@ -1604,7 +1604,7 @@ static void draw_bone(const short dt, int armflag, int boneflag, short constflag
|
||||
set_ebone_color(boneflag);
|
||||
}
|
||||
else if (armflag & ARM_POSEMODE) {
|
||||
if (constflag) {
|
||||
if (constflag && ((G.f & G_PICKSEL) == 0)) {
|
||||
/* draw constraint colors */
|
||||
if (set_pchan_color(PCHAN_COLOR_CONSTS, boneflag, constflag)) {
|
||||
glEnable(GL_BLEND);
|
||||
|
@ -420,6 +420,9 @@ int ED_undo_operator_repeat(bContext *C, struct wmOperator *op)
|
||||
|
||||
if (G.debug & G_DEBUG)
|
||||
printf("redo_cb: operator redo %s\n", op->type->name);
|
||||
|
||||
WM_operator_free_all_after(wm, op);
|
||||
|
||||
ED_undo_pop_op(C, op);
|
||||
|
||||
if (op->type->check) {
|
||||
|
@ -87,6 +87,11 @@ static void rna_Operator_report(wmOperator *op, int type, const char *msg)
|
||||
BKE_report(op->reports, type, msg);
|
||||
}
|
||||
|
||||
static int rna_Operator_is_repeat(wmOperator *op, bContext *C)
|
||||
{
|
||||
return WM_operator_is_repeat(C, op);
|
||||
}
|
||||
|
||||
/* since event isn't needed... */
|
||||
static void rna_Operator_enum_search_invoke(bContext *C, wmOperator *op)
|
||||
{
|
||||
@ -521,6 +526,12 @@ void RNA_api_operator(StructRNA *srna)
|
||||
parm = RNA_def_string(func, "message", NULL, 0, "Report Message", "");
|
||||
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
|
||||
|
||||
/* utility, not for registering */
|
||||
func = RNA_def_function(srna, "is_repeat", "rna_Operator_is_repeat");
|
||||
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
|
||||
/* return */
|
||||
parm = RNA_def_boolean(func, "result", 0, "result", "");
|
||||
RNA_def_function_return(func, parm);
|
||||
|
||||
/* Registration */
|
||||
|
||||
|
@ -263,6 +263,7 @@ int WM_operator_confirm_message(struct bContext *C, struct wmOperator *o
|
||||
|
||||
/* operator api */
|
||||
void WM_operator_free (struct wmOperator *op);
|
||||
void WM_operator_free_all_after(wmWindowManager *wm, struct wmOperator *op);
|
||||
void WM_operator_type_set(struct wmOperator *op, struct wmOperatorType *ot);
|
||||
void WM_operator_stack_clear(struct wmWindowManager *wm);
|
||||
void WM_operator_handlers_clear(wmWindowManager *wm, struct wmOperatorType *ot);
|
||||
@ -287,6 +288,7 @@ int WM_operator_call (struct bContext *C, struct wmOperator *op);
|
||||
int WM_operator_call_notest(struct bContext *C, struct wmOperator *op);
|
||||
int WM_operator_repeat (struct bContext *C, struct wmOperator *op);
|
||||
bool WM_operator_repeat_check(const struct bContext *C, struct wmOperator *op);
|
||||
bool WM_operator_is_repeat(const struct bContext *C, const struct wmOperator *op);
|
||||
int WM_operator_name_call_ptr(struct bContext *C, struct wmOperatorType *ot, short context, struct PointerRNA *properties);
|
||||
int WM_operator_name_call(struct bContext *C, const char *opstring, short context, struct PointerRNA *properties);
|
||||
int WM_operator_call_py(struct bContext *C, struct wmOperatorType *ot, short context, struct PointerRNA *properties, struct ReportList *reports, const bool is_undo);
|
||||
|
@ -107,6 +107,17 @@ void WM_operator_free(wmOperator *op)
|
||||
MEM_freeN(op);
|
||||
}
|
||||
|
||||
void WM_operator_free_all_after(wmWindowManager *wm, struct wmOperator *op)
|
||||
{
|
||||
op = op->next;
|
||||
while (op != NULL) {
|
||||
wmOperator *op_next = op->next;
|
||||
BLI_remlink(&wm->operators, op);
|
||||
WM_operator_free(op);
|
||||
op = op_next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use with extreme care!,
|
||||
* properties, customdata etc - must be compatible.
|
||||
@ -149,18 +160,23 @@ static void wm_reports_free(wmWindowManager *wm)
|
||||
void wm_operator_register(bContext *C, wmOperator *op)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
int tot;
|
||||
int tot = 0;
|
||||
|
||||
BLI_addtail(&wm->operators, op);
|
||||
tot = BLI_listbase_count(&wm->operators);
|
||||
|
||||
while (tot > MAX_OP_REGISTERED) {
|
||||
wmOperator *opt = wm->operators.first;
|
||||
BLI_remlink(&wm->operators, opt);
|
||||
WM_operator_free(opt);
|
||||
tot--;
|
||||
|
||||
/* only count registered operators */
|
||||
while (op) {
|
||||
wmOperator *op_prev = op->prev;
|
||||
if (op->type->flag & OPTYPE_REGISTER) {
|
||||
tot += 1;
|
||||
}
|
||||
if (tot > MAX_OP_REGISTERED) {
|
||||
BLI_remlink(&wm->operators, op);
|
||||
WM_operator_free(op);
|
||||
}
|
||||
op = op_prev;
|
||||
}
|
||||
|
||||
|
||||
/* so the console is redrawn */
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO_REPORT, NULL);
|
||||
WM_event_add_notifier(C, NC_WM | ND_HISTORY, NULL);
|
||||
|
@ -713,7 +713,9 @@ static void wm_operator_reports(bContext *C, wmOperator *op, int retval, bool ca
|
||||
*/
|
||||
static bool wm_operator_register_check(wmWindowManager *wm, wmOperatorType *ot)
|
||||
{
|
||||
return wm && (wm->op_undo_depth == 0) && (ot->flag & OPTYPE_REGISTER);
|
||||
/* Check undo flag here since undo operators are also added to the list,
|
||||
* to support checking if the same operator is run twice. */
|
||||
return wm && (wm->op_undo_depth == 0) && (ot->flag & (OPTYPE_REGISTER | OPTYPE_UNDO));
|
||||
}
|
||||
|
||||
static void wm_operator_finished(bContext *C, wmOperator *op, const bool repeat)
|
||||
@ -876,6 +878,20 @@ bool WM_operator_repeat_check(const bContext *UNUSED(C), wmOperator *op)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WM_operator_is_repeat(const bContext *C, const wmOperator *op)
|
||||
{
|
||||
/* may be in the operators list or not */
|
||||
wmOperator *op_prev;
|
||||
if (op->prev == NULL && op->next == NULL) {
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
op_prev = wm->operators.last;
|
||||
}
|
||||
else {
|
||||
op_prev = op->prev;
|
||||
}
|
||||
return (op_prev && (op->type == op_prev->type));
|
||||
}
|
||||
|
||||
static wmOperator *wm_operator_create(wmWindowManager *wm, wmOperatorType *ot,
|
||||
PointerRNA *properties, ReportList *reports)
|
||||
{
|
||||
|
@ -1426,7 +1426,16 @@ static int wm_homefile_read_exec(bContext *C, wmOperator *op)
|
||||
G.fileflags &= ~G_FILE_NO_UI;
|
||||
}
|
||||
|
||||
return wm_homefile_read(C, op->reports, from_memory, filepath) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
|
||||
if (wm_homefile_read(C, op->reports, from_memory, filepath)) {
|
||||
/* Load a file but keep the splash open */
|
||||
if (RNA_boolean_get(op->ptr, "use_splash")) {
|
||||
WM_init_splash(C);
|
||||
}
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
else {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
void WM_OT_read_homefile(wmOperatorType *ot)
|
||||
@ -1449,6 +1458,10 @@ void WM_OT_read_homefile(wmOperatorType *ot)
|
||||
"Load user interface setup from the .blend file");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
|
||||
/* So the splash can be kept open after loading a file (for templates). */
|
||||
prop = RNA_def_boolean(ot->srna, "use_splash", true, "Splash", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
|
||||
/* omit poll to run in background mode */
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user