Merge sculpt-dev -> master

This commit is contained in:
Joseph Eagar 2022-01-23 12:27:34 -08:00
parent 1acb608f52
commit ccbde00a83
393 changed files with 7658 additions and 4950 deletions

View File

@ -63,6 +63,7 @@ include(cmake/jpeg.cmake)
include(cmake/blosc.cmake)
include(cmake/pthreads.cmake)
include(cmake/openexr.cmake)
include(cmake/brotli.cmake)
include(cmake/freetype.cmake)
include(cmake/freeglut.cmake)
include(cmake/glew.cmake)

View File

@ -94,3 +94,4 @@ download_source(POTRACE)
download_source(HARU)
download_source(ZSTD)
download_source(FLEX)
download_source(BROTLI)

View File

@ -23,9 +23,12 @@ set(FREETYPE_EXTRA_ARGS
-DWITH_HarfBuzz=OFF
-DFT_WITH_HARFBUZZ=OFF
-DFT_WITH_BZIP2=OFF
-DFT_WITH_BROTLI=ON
-DCMAKE_DISABLE_FIND_PACKAGE_HarfBuzz=TRUE
-DCMAKE_DISABLE_FIND_PACKAGE_BZip2=TRUE
-DCMAKE_DISABLE_FIND_PACKAGE_BrotliDec=TRUE)
-DPC_BROTLIDEC_INCLUDEDIR=${LIBDIR}/brotli/include
-DPC_BROTLIDEC_LIBDIR=${LIBDIR}/brotli/lib
)
ExternalProject_Add(external_freetype
URL file://${PACKAGE_DIR}/${FREETYPE_FILE}
@ -36,6 +39,11 @@ ExternalProject_Add(external_freetype
INSTALL_DIR ${LIBDIR}/freetype
)
add_dependencies(
external_freetype
external_brotli
)
if(BUILD_MODE STREQUAL Release AND WIN32)
ExternalProject_Add_Step(external_freetype after_install
COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/freetype ${HARVEST_TARGET}/freetype

View File

@ -79,6 +79,8 @@ endfunction()
harvest(alembic/include alembic/include "*.h")
harvest(alembic/lib/libAlembic.a alembic/lib/libAlembic.a)
harvest(alembic/bin alembic/bin "*")
harvest(brotli/include brotli/include "*.h")
harvest(brotli/lib brotli/lib "*.a")
harvest(boost/include boost/include "*")
harvest(boost/lib boost/lib "*.a")
harvest(ffmpeg/include ffmpeg/include "*.h")

View File

@ -83,9 +83,9 @@ else()
set(OPENEXR_VERSION_POSTFIX)
endif()
set(FREETYPE_VERSION 2.10.2)
set(FREETYPE_VERSION 2.11.0)
set(FREETYPE_URI http://prdownloads.sourceforge.net/freetype/freetype-${FREETYPE_VERSION}.tar.gz)
set(FREETYPE_HASH b1cb620e4c875cd4d1bfa04945400945)
set(FREETYPE_HASH cf09172322f6b50cf8f568bf8fe14bde)
set(FREETYPE_HASH_TYPE MD5)
set(FREETYPE_FILE freetype-${FREETYPE_VERSION}.tar.gz)
@ -500,3 +500,10 @@ set(ZSTD_FILE zstd-${ZSTD_VERSION}.tar.gz)
set(SSE2NEON_GIT https://github.com/DLTcollab/sse2neon.git)
set(SSE2NEON_GIT_HASH fe5ff00bb8d19b327714a3c290f3e2ce81ba3525)
set(BROTLI_VERSION v1.0.9)
set(BROTLI_URI https://github.com/google/brotli/archive/refs/tags/${BROTLI_VERSION}.tar.gz)
set(BROTLI_HASH f9e8d81d0405ba66d181529af42a3354f838c939095ff99930da6aa9cdf6fe46)
set(BROTLI_HASH_TYPE SHA256)
set(BROTLI_FILE brotli-${BROTLI_VERSION}.tar.gz)

View File

@ -492,7 +492,7 @@ OIIO_SKIP=false
LLVM_VERSION="12.0.0"
LLVM_VERSION_SHORT="12.0"
LLVM_VERSION_MIN="11.0"
LLVM_VERSION_MEX="13.0"
LLVM_VERSION_MEX="14.0"
LLVM_VERSION_FOUND=""
LLVM_FORCE_BUILD=false
LLVM_FORCE_REBUILD=false

View File

@ -166,7 +166,11 @@ if(WITH_FFTW3)
find_package(Fftw3)
endif()
# FreeType compiled with Brotli compression for woff2.
find_package(Freetype REQUIRED)
list(APPEND FREETYPE_LIBRARIES
${LIBDIR}/brotli/lib/libbrotlicommon-static.a
${LIBDIR}/brotli/lib/libbrotlidec-static.a)
if(WITH_IMAGE_OPENEXR)
find_package(OpenEXR)

View File

@ -700,14 +700,18 @@ if(CMAKE_COMPILER_IS_GNUCC)
find_path(
MOLD_BIN_DIR "ld"
HINTS "${MOLD_PREFIX}"
PATH_SUFFIXES "lib/mold" "lib64/mold"
# The default path is `libexec`, Arch Linux for e.g.
# replaces this with `lib` so check both.
PATH_SUFFIXES "libexec/mold" "lib/mold" "lib64/mold"
NO_DEFAULT_PATH
NO_CACHE
)
if(NOT MOLD_BIN_DIR)
message(STATUS
"The mold linker could not find the directory containing the linker command "
"(typically \"${MOLD_PREFIX}/lib/mold\"), using system linker.")
"(typically "
"\"${MOLD_PREFIX}/libexec/mold/ld\") or "
"\"${MOLD_PREFIX}/lib/mold/ld\") using system linker.")
set(WITH_LINKER_MOLD OFF)
endif()
unset(MOLD_PREFIX)

View File

@ -347,7 +347,11 @@ set(FREETYPE_INCLUDE_DIRS
${LIBDIR}/freetype/include
${LIBDIR}/freetype/include/freetype2
)
set(FREETYPE_LIBRARY ${LIBDIR}/freetype/lib/freetype2ST.lib)
set(FREETYPE_LIBRARIES
${LIBDIR}/freetype/lib/freetype2ST.lib
${LIBDIR}/brotli/lib/brotlidec-static.lib
${LIBDIR}/brotli/lib/brotlicommon-static.lib
)
windows_find_package(freetype REQUIRED)
if(WITH_FFTW3)

View File

@ -8,27 +8,42 @@ def set_pose_matrices(obj, matrix_map):
"Assign pose space matrices of all bones at once, ignoring constraints."
def rec(pbone, parent_matrix):
matrix = matrix_map[pbone.name]
if pbone.name in matrix_map:
matrix = matrix_map[pbone.name]
## Instead of:
# pbone.matrix = matrix
# bpy.context.view_layer.update()
## Instead of:
# pbone.matrix = matrix
# bpy.context.view_layer.update()
# Compute and assign local matrix, using the new parent matrix
if pbone.parent:
pbone.matrix_basis = pbone.bone.convert_local_to_pose(
matrix,
pbone.bone.matrix_local,
parent_matrix=parent_matrix,
parent_matrix_local=pbone.parent.bone.matrix_local,
invert=True
)
# Compute and assign local matrix, using the new parent matrix
if pbone.parent:
pbone.matrix_basis = pbone.bone.convert_local_to_pose(
matrix,
pbone.bone.matrix_local,
parent_matrix=parent_matrix,
parent_matrix_local=pbone.parent.bone.matrix_local,
invert=True
)
else:
pbone.matrix_basis = pbone.bone.convert_local_to_pose(
matrix,
pbone.bone.matrix_local,
invert=True
)
else:
pbone.matrix_basis = pbone.bone.convert_local_to_pose(
matrix,
pbone.bone.matrix_local,
invert=True
)
# Compute the updated pose matrix from local and new parent matrix
if pbone.parent:
matrix = pbone.bone.convert_local_to_pose(
pbone.matrix_basis,
pbone.bone.matrix_local,
parent_matrix=parent_matrix,
parent_matrix_local=pbone.parent.bone.matrix_local,
)
else:
matrix = pbone.bone.convert_local_to_pose(
pbone.matrix_basis,
pbone.bone.matrix_local,
)
# Recursively process children, passing the new matrix through
for child in pbone.children:

View File

@ -1762,6 +1762,7 @@ except ModuleNotFoundError:
fw("html_show_sphinx = False\n")
fw("html_baseurl = 'https://docs.blender.org/api/current/'\n")
fw("html_use_opensearch = 'https://docs.blender.org/api/current'\n")
fw("html_show_search_summary = True\n")
fw("html_split_index = True\n")
fw("html_static_path = ['static']\n")
fw("html_extra_path = ['static/favicon.ico', 'static/blender_logo.svg']\n")

View File

@ -119,12 +119,6 @@ def use_optix(context):
return (get_device_type(context) == 'OPTIX' and cscene.device == 'GPU')
def use_sample_all_lights(context):
cscene = context.scene.cycles
return cscene.sample_all_lights_direct or cscene.sample_all_lights_indirect
def show_device_active(context):
cscene = context.scene.cycles
if cscene.device != 'GPU':
@ -1803,18 +1797,45 @@ class CYCLES_RENDER_PT_bake_output(CyclesButtonsPanel, Panel):
rd = scene.render
if rd.use_bake_multires:
layout.prop(rd, "bake_margin")
layout.prop(rd, "use_bake_clear", text="Clear Image")
if rd.bake_type == 'DISPLACEMENT':
layout.prop(rd, "use_bake_lores_mesh")
else:
layout.prop(cbk, "target")
if cbk.target == 'IMAGE_TEXTURES':
layout.prop(cbk, "margin")
layout.prop(cbk, "use_clear", text="Clear Image")
class CYCLES_RENDER_PT_bake_output_margin(CyclesButtonsPanel, Panel):
bl_label = "Margin"
bl_context = "render"
bl_parent_id = "CYCLES_RENDER_PT_bake_output"
COMPAT_ENGINES = {'CYCLES'}
@classmethod
def poll(cls, context):
scene = context.scene
cbk = scene.render.bake
return cbk.target == 'IMAGE_TEXTURES'
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
scene = context.scene
cscene = scene.cycles
cbk = scene.render.bake
rd = scene.render
if rd.use_bake_multires:
layout.prop(rd, "bake_margin_type", text="Type")
layout.prop(rd, "bake_margin", text="Size")
else:
if cbk.target == 'IMAGE_TEXTURES':
layout.prop(cbk, "margin_type", text="Type")
layout.prop(cbk, "margin", text="Size")
class CYCLES_RENDER_PT_debug(CyclesDebugButtonsPanel, Panel):
bl_label = "Debug"
@ -2183,6 +2204,7 @@ classes = (
CYCLES_RENDER_PT_bake_influence,
CYCLES_RENDER_PT_bake_selected_to_active,
CYCLES_RENDER_PT_bake_output,
CYCLES_RENDER_PT_bake_output_margin,
CYCLES_RENDER_PT_debug,
node_panel(CYCLES_MATERIAL_PT_settings),
node_panel(CYCLES_MATERIAL_PT_settings_surface),

View File

@ -1071,7 +1071,15 @@ static void create_subd_mesh(Scene *scene,
for (BL::MeshEdge &e : b_mesh.edges) {
if (e.crease() != 0.0f) {
mesh->add_crease(e.vertices()[0], e.vertices()[1], e.crease());
mesh->add_edge_crease(e.vertices()[0], e.vertices()[1], e.crease());
}
}
for (BL::MeshVertexCreaseLayer &c : b_mesh.vertex_creases) {
for (int i = 0; i < c.data.length(); ++i) {
if (c.data[i].value() != 0.0f) {
mesh->add_vertex_crease(i, c.data[i].value());
}
}
}

View File

@ -529,6 +529,17 @@ void BlenderSync::sync_procedural(BL::Object &b_ob,
string absolute_path = blender_absolute_path(b_data, b_ob, b_mesh_cache.cache_file().filepath());
procedural->set_filepath(ustring(absolute_path));
array<ustring> layers;
for (BL::CacheFileLayer &layer : cache_file.layers) {
if (layer.hide_layer()) {
continue;
}
absolute_path = blender_absolute_path(b_data, b_ob, layer.filepath());
layers.push_back_slow(ustring(absolute_path));
}
procedural->set_layers(layers);
procedural->set_scale(cache_file.scale());
procedural->set_use_prefetch(cache_file.use_prefetch());

View File

@ -51,7 +51,7 @@ bool BlenderOutputDriver::read_render_tile(const Tile &tile)
BL::RenderLayer b_rlay = *b_single_rlay;
vector<float> pixels(tile.size.x * tile.size.y * 4);
vector<float> pixels(static_cast<size_t>(tile.size.x) * tile.size.y * 4);
/* Copy each pass.
* TODO:copy only the required ones for better performance? */
@ -109,7 +109,7 @@ void BlenderOutputDriver::write_render_tile(const Tile &tile)
BL::RenderLayer b_rlay = *b_single_rlay;
vector<float> pixels(tile.size.x * tile.size.y * 4);
vector<float> pixels(static_cast<size_t>(tile.size.x) * tile.size.y * 4);
/* Copy each pass. */
for (BL::RenderPass &b_pass : b_rlay.passes) {

View File

@ -58,6 +58,11 @@ class BVHMetal : public BVH {
id<MTLCommandQueue> queue,
Geometry *const geom,
bool refit);
bool build_BLAS_pointcloud(Progress &progress,
id<MTLDevice> device,
id<MTLCommandQueue> queue,
Geometry *const geom,
bool refit);
bool build_TLAS(Progress &progress, id<MTLDevice> device, id<MTLCommandQueue> queue, bool refit);
};

View File

@ -19,6 +19,7 @@
# include "scene/hair.h"
# include "scene/mesh.h"
# include "scene/object.h"
# include "scene/pointcloud.h"
# include "util/progress.h"
@ -475,6 +476,220 @@ bool BVHMetal::build_BLAS_hair(Progress &progress,
return false;
}
bool BVHMetal::build_BLAS_pointcloud(Progress &progress,
id<MTLDevice> device,
id<MTLCommandQueue> queue,
Geometry *const geom,
bool refit)
{
if (@available(macos 12.0, *)) {
/* Build BLAS for point cloud */
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
if (pointcloud->num_points() == 0) {
return false;
}
/*------------------------------------------------*/
BVH_status("Building pointcloud BLAS | %7d points | %s",
(int)pointcloud->num_points(),
geom->name.c_str());
/*------------------------------------------------*/
const size_t num_points = pointcloud->get_points().size();
const float3 *points = pointcloud->get_points().data();
const float *radius = pointcloud->get_radius().data();
const bool use_fast_trace_bvh = (params.bvh_type == BVH_TYPE_STATIC);
size_t num_motion_steps = 1;
Attribute *motion_keys = pointcloud->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
if (motion_blur && pointcloud->get_use_motion_blur() && motion_keys) {
num_motion_steps = pointcloud->get_motion_steps();
}
const size_t num_aabbs = num_motion_steps;
MTLResourceOptions storage_mode;
if (device.hasUnifiedMemory) {
storage_mode = MTLResourceStorageModeShared;
}
else {
storage_mode = MTLResourceStorageModeManaged;
}
/* Allocate a GPU buffer for the AABB data and populate it */
id<MTLBuffer> aabbBuf = [device
newBufferWithLength:num_aabbs * sizeof(MTLAxisAlignedBoundingBox)
options:storage_mode];
MTLAxisAlignedBoundingBox *aabb_data = (MTLAxisAlignedBoundingBox *)[aabbBuf contents];
/* Get AABBs for each motion step */
size_t center_step = (num_motion_steps - 1) / 2;
for (size_t step = 0; step < num_motion_steps; ++step) {
/* The center step for motion vertices is not stored in the attribute */
if (step != center_step) {
size_t attr_offset = (step > center_step) ? step - 1 : step;
points = motion_keys->data_float3() + attr_offset * num_points;
}
for (size_t j = 0; j < num_points; ++j) {
const PointCloud::Point point = pointcloud->get_point(j);
BoundBox bounds = BoundBox::empty;
point.bounds_grow(points, radius, bounds);
const size_t index = step * num_points + j;
aabb_data[index].min = (MTLPackedFloat3 &)bounds.min;
aabb_data[index].max = (MTLPackedFloat3 &)bounds.max;
}
}
if (storage_mode == MTLResourceStorageModeManaged) {
[aabbBuf didModifyRange:NSMakeRange(0, aabbBuf.length)];
}
# if 0
for (size_t i=0; i<num_aabbs && i < 400; i++) {
MTLAxisAlignedBoundingBox& bb = aabb_data[i];
printf(" %d: %.1f,%.1f,%.1f -- %.1f,%.1f,%.1f\n", int(i), bb.min.x, bb.min.y, bb.min.z, bb.max.x, bb.max.y, bb.max.z);
}
# endif
MTLAccelerationStructureGeometryDescriptor *geomDesc;
if (motion_blur) {
std::vector<MTLMotionKeyframeData *> aabb_ptrs;
aabb_ptrs.reserve(num_motion_steps);
for (size_t step = 0; step < num_motion_steps; ++step) {
MTLMotionKeyframeData *k = [MTLMotionKeyframeData data];
k.buffer = aabbBuf;
k.offset = step * num_points * sizeof(MTLAxisAlignedBoundingBox);
aabb_ptrs.push_back(k);
}
MTLAccelerationStructureMotionBoundingBoxGeometryDescriptor *geomDescMotion =
[MTLAccelerationStructureMotionBoundingBoxGeometryDescriptor descriptor];
geomDescMotion.boundingBoxBuffers = [NSArray arrayWithObjects:aabb_ptrs.data()
count:aabb_ptrs.size()];
geomDescMotion.boundingBoxCount = num_points;
geomDescMotion.boundingBoxStride = sizeof(aabb_data[0]);
geomDescMotion.intersectionFunctionTableOffset = 2;
/* Force a single any-hit call, so shadow record-all behavior works correctly */
/* (Match optix behavior: unsigned int build_flags =
* OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL;) */
geomDescMotion.allowDuplicateIntersectionFunctionInvocation = false;
geomDescMotion.opaque = true;
geomDesc = geomDescMotion;
}
else {
MTLAccelerationStructureBoundingBoxGeometryDescriptor *geomDescNoMotion =
[MTLAccelerationStructureBoundingBoxGeometryDescriptor descriptor];
geomDescNoMotion.boundingBoxBuffer = aabbBuf;
geomDescNoMotion.boundingBoxBufferOffset = 0;
geomDescNoMotion.boundingBoxCount = int(num_aabbs);
geomDescNoMotion.boundingBoxStride = sizeof(aabb_data[0]);
geomDescNoMotion.intersectionFunctionTableOffset = 2;
/* Force a single any-hit call, so shadow record-all behavior works correctly */
/* (Match optix behavior: unsigned int build_flags =
* OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL;) */
geomDescNoMotion.allowDuplicateIntersectionFunctionInvocation = false;
geomDescNoMotion.opaque = true;
geomDesc = geomDescNoMotion;
}
MTLPrimitiveAccelerationStructureDescriptor *accelDesc =
[MTLPrimitiveAccelerationStructureDescriptor descriptor];
accelDesc.geometryDescriptors = @[ geomDesc ];
if (motion_blur) {
accelDesc.motionStartTime = 0.0f;
accelDesc.motionEndTime = 1.0f;
accelDesc.motionStartBorderMode = MTLMotionBorderModeVanish;
accelDesc.motionEndBorderMode = MTLMotionBorderModeVanish;
accelDesc.motionKeyframeCount = num_motion_steps;
}
if (!use_fast_trace_bvh) {
accelDesc.usage |= (MTLAccelerationStructureUsageRefit |
MTLAccelerationStructureUsagePreferFastBuild);
}
MTLAccelerationStructureSizes accelSizes = [device
accelerationStructureSizesWithDescriptor:accelDesc];
id<MTLAccelerationStructure> accel_uncompressed = [device
newAccelerationStructureWithSize:accelSizes.accelerationStructureSize];
id<MTLBuffer> scratchBuf = [device newBufferWithLength:accelSizes.buildScratchBufferSize
options:MTLResourceStorageModePrivate];
id<MTLBuffer> sizeBuf = [device newBufferWithLength:8 options:MTLResourceStorageModeShared];
id<MTLCommandBuffer> accelCommands = [queue commandBuffer];
id<MTLAccelerationStructureCommandEncoder> accelEnc =
[accelCommands accelerationStructureCommandEncoder];
if (refit) {
[accelEnc refitAccelerationStructure:accel_struct
descriptor:accelDesc
destination:accel_uncompressed
scratchBuffer:scratchBuf
scratchBufferOffset:0];
}
else {
[accelEnc buildAccelerationStructure:accel_uncompressed
descriptor:accelDesc
scratchBuffer:scratchBuf
scratchBufferOffset:0];
}
if (use_fast_trace_bvh) {
[accelEnc writeCompactedAccelerationStructureSize:accel_uncompressed
toBuffer:sizeBuf
offset:0
sizeDataType:MTLDataTypeULong];
}
[accelEnc endEncoding];
[accelCommands addCompletedHandler:^(id<MTLCommandBuffer> command_buffer) {
/* free temp resources */
[scratchBuf release];
[aabbBuf release];
if (use_fast_trace_bvh) {
/* Compact the accel structure */
uint64_t compressed_size = *(uint64_t *)sizeBuf.contents;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
id<MTLCommandBuffer> accelCommands = [queue commandBuffer];
id<MTLAccelerationStructureCommandEncoder> accelEnc =
[accelCommands accelerationStructureCommandEncoder];
id<MTLAccelerationStructure> accel = [device
newAccelerationStructureWithSize:compressed_size];
[accelEnc copyAndCompactAccelerationStructure:accel_uncompressed
toAccelerationStructure:accel];
[accelEnc endEncoding];
[accelCommands addCompletedHandler:^(id<MTLCommandBuffer> command_buffer) {
uint64_t allocated_size = [accel allocatedSize];
stats.mem_alloc(allocated_size);
accel_struct = accel;
[accel_uncompressed release];
accel_struct_building = false;
}];
[accelCommands commit];
});
}
else {
/* set our acceleration structure to the uncompressed structure */
accel_struct = accel_uncompressed;
uint64_t allocated_size = [accel_struct allocatedSize];
stats.mem_alloc(allocated_size);
accel_struct_building = false;
}
[sizeBuf release];
}];
accel_struct_building = true;
[accelCommands commit];
return true;
}
return false;
}
bool BVHMetal::build_BLAS(Progress &progress,
id<MTLDevice> device,
id<MTLCommandQueue> queue,
@ -491,6 +706,8 @@ bool BVHMetal::build_BLAS(Progress &progress,
return build_BLAS_mesh(progress, device, queue, geom, refit);
case Geometry::HAIR:
return build_BLAS_hair(progress, device, queue, geom, refit);
case Geometry::POINTCLOUD:
return build_BLAS_pointcloud(progress, device, queue, geom, refit);
default:
return false;
}

View File

@ -115,6 +115,8 @@ class MetalDevice : public Device {
void load_texture_info();
void erase_allocation(device_memory &mem);
virtual bool should_use_graphics_interop() override;
virtual unique_ptr<DeviceQueue> gpu_queue_create() override;

View File

@ -87,17 +87,14 @@ MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profile
default:
break;
case METAL_GPU_INTEL: {
use_metalrt = false;
max_threads_per_threadgroup = 64;
break;
}
case METAL_GPU_AMD: {
use_metalrt = false;
max_threads_per_threadgroup = 128;
break;
}
case METAL_GPU_APPLE: {
use_metalrt = true;
max_threads_per_threadgroup = 512;
break;
}
@ -432,6 +429,25 @@ void MetalDevice::load_texture_info()
}
}
void MetalDevice::erase_allocation(device_memory &mem)
{
stats.mem_free(mem.device_size);
mem.device_pointer = 0;
mem.device_size = 0;
auto it = metal_mem_map.find(&mem);
if (it != metal_mem_map.end()) {
MetalMem *mmem = it->second.get();
/* blank out reference to MetalMem* in the launch params (fixes crash T94736) */
if (mmem->pointer_index >= 0) {
device_ptr *pointers = (device_ptr *)&launch_params;
pointers[mmem->pointer_index] = 0;
}
metal_mem_map.erase(it);
}
}
MetalDevice::MetalMem *MetalDevice::generic_alloc(device_memory &mem)
{
size_t size = mem.memory_size();
@ -561,11 +577,7 @@ void MetalDevice::generic_free(device_memory &mem)
mmem.mtlBuffer = nil;
}
stats.mem_free(mem.device_size);
mem.device_pointer = 0;
mem.device_size = 0;
metal_mem_map.erase(&mem);
erase_allocation(mem);
}
}
@ -954,10 +966,7 @@ void MetalDevice::tex_free(device_texture &mem)
delayed_free_list.push_back(mmem.mtlTexture);
mmem.mtlTexture = nil;
}
stats.mem_free(mem.device_size);
mem.device_pointer = 0;
mem.device_size = 0;
metal_mem_map.erase(&mem);
erase_allocation(mem);
}
}

View File

@ -36,6 +36,8 @@ enum {
METALRT_FUNC_CURVE_RIBBON_SHADOW,
METALRT_FUNC_CURVE_ALL,
METALRT_FUNC_CURVE_ALL_SHADOW,
METALRT_FUNC_POINT,
METALRT_FUNC_POINT_SHADOW,
METALRT_FUNC_NUM
};

View File

@ -358,6 +358,8 @@ bool MetalDeviceKernels::load(MetalDevice *device, int kernel_type)
"__intersection__curve_ribbon_shadow",
"__intersection__curve_all",
"__intersection__curve_all_shadow",
"__intersection__point",
"__intersection__point_shadow",
};
assert(sizeof(function_names) / sizeof(function_names[0]) == METALRT_FUNC_NUM);
@ -400,36 +402,50 @@ bool MetalDeviceKernels::load(MetalDevice *device, int kernel_type)
NSArray *function_list = nil;
if (device->use_metalrt) {
id<MTLFunction> box_intersect_default = nil;
id<MTLFunction> box_intersect_shadow = nil;
id<MTLFunction> curve_intersect_default = nil;
id<MTLFunction> curve_intersect_shadow = nil;
id<MTLFunction> point_intersect_default = nil;
id<MTLFunction> point_intersect_shadow = nil;
if (device->kernel_features & KERNEL_FEATURE_HAIR) {
/* Add curve intersection programs. */
if (device->kernel_features & KERNEL_FEATURE_HAIR_THICK) {
/* Slower programs for thick hair since that also slows down ribbons.
* Ideally this should not be needed. */
box_intersect_default = rt_intersection_funcs[kernel_type][METALRT_FUNC_CURVE_ALL];
box_intersect_shadow = rt_intersection_funcs[kernel_type][METALRT_FUNC_CURVE_ALL_SHADOW];
curve_intersect_default = rt_intersection_funcs[kernel_type][METALRT_FUNC_CURVE_ALL];
curve_intersect_shadow =
rt_intersection_funcs[kernel_type][METALRT_FUNC_CURVE_ALL_SHADOW];
}
else {
box_intersect_default = rt_intersection_funcs[kernel_type][METALRT_FUNC_CURVE_RIBBON];
box_intersect_shadow =
curve_intersect_default = rt_intersection_funcs[kernel_type][METALRT_FUNC_CURVE_RIBBON];
curve_intersect_shadow =
rt_intersection_funcs[kernel_type][METALRT_FUNC_CURVE_RIBBON_SHADOW];
}
}
if (device->kernel_features & KERNEL_FEATURE_POINTCLOUD) {
point_intersect_default = rt_intersection_funcs[kernel_type][METALRT_FUNC_POINT];
point_intersect_shadow = rt_intersection_funcs[kernel_type][METALRT_FUNC_POINT_SHADOW];
}
table_functions[METALRT_TABLE_DEFAULT] = [NSArray
arrayWithObjects:rt_intersection_funcs[kernel_type][METALRT_FUNC_DEFAULT_TRI],
box_intersect_default ?
box_intersect_default :
curve_intersect_default ?
curve_intersect_default :
rt_intersection_funcs[kernel_type][METALRT_FUNC_DEFAULT_BOX],
point_intersect_default ?
point_intersect_default :
rt_intersection_funcs[kernel_type][METALRT_FUNC_DEFAULT_BOX],
nil];
table_functions[METALRT_TABLE_SHADOW] = [NSArray
arrayWithObjects:rt_intersection_funcs[kernel_type][METALRT_FUNC_SHADOW_TRI],
box_intersect_shadow ?
box_intersect_shadow :
curve_intersect_shadow ?
curve_intersect_shadow :
rt_intersection_funcs[kernel_type][METALRT_FUNC_SHADOW_BOX],
point_intersect_shadow ?
point_intersect_shadow :
rt_intersection_funcs[kernel_type][METALRT_FUNC_SHADOW_BOX],
nil];
table_functions[METALRT_TABLE_LOCAL] = [NSArray
arrayWithObjects:rt_intersection_funcs[kernel_type][METALRT_FUNC_LOCAL_TRI],
rt_intersection_funcs[kernel_type][METALRT_FUNC_LOCAL_BOX],
rt_intersection_funcs[kernel_type][METALRT_FUNC_LOCAL_BOX],
nil];

View File

@ -141,6 +141,7 @@ bool PassAccessor::get_render_tile_pixels(const RenderBuffers *render_buffers,
const PassType type = pass_access_info_.type;
const PassMode mode = pass_access_info_.mode;
const PassInfo pass_info = Pass::get_info(type, pass_access_info_.include_albedo);
int num_written_components = pass_info.num_components;
if (pass_info.num_components == 1) {
/* Single channel passes. */
@ -188,8 +189,10 @@ bool PassAccessor::get_render_tile_pixels(const RenderBuffers *render_buffers,
else if ((pass_info.divide_type != PASS_NONE || pass_info.direct_type != PASS_NONE ||
pass_info.indirect_type != PASS_NONE) &&
mode != PassMode::DENOISED) {
/* RGB lighting passes that need to divide out color and/or sum direct and indirect. */
/* RGB lighting passes that need to divide out color and/or sum direct and indirect.
* These can also optionally write alpha like the combined pass. */
get_pass_light_path(render_buffers, buffer_params, destination);
num_written_components = 4;
}
else {
/* Passes that need no special computation, or denoised passes that already
@ -215,7 +218,7 @@ bool PassAccessor::get_render_tile_pixels(const RenderBuffers *render_buffers,
}
}
pad_pixels(buffer_params, destination, pass_info.num_components);
pad_pixels(buffer_params, destination, num_written_components);
return true;
}

View File

@ -148,12 +148,23 @@ ccl_device_inline
/* intersect ray against primitive */
for (; prim_addr < prim_addr2; prim_addr++) {
kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
/* Only intersect with matching object, for instanced objects we
* already know we are only intersecting the right object. */
if (object == OBJECT_NONE) {
if (kernel_tex_fetch(__prim_object, prim_addr) != local_object) {
continue;
}
}
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
if (triangle_intersect_local(kg,
local_isect,
P,
dir,
object,
local_object,
prim,
prim_addr,
isect_t,
lcg_state,
@ -168,13 +179,24 @@ ccl_device_inline
/* intersect ray against primitive */
for (; prim_addr < prim_addr2; prim_addr++) {
kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
/* Only intersect with matching object, for instanced objects we
* already know we are only intersecting the right object. */
if (object == OBJECT_NONE) {
if (kernel_tex_fetch(__prim_object, prim_addr) != local_object) {
continue;
}
}
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
if (motion_triangle_intersect_local(kg,
local_isect,
P,
dir,
ray->time,
object,
local_object,
prim,
prim_addr,
isect_t,
lcg_state,

View File

@ -146,7 +146,7 @@ ccl_device_inline
--stack_ptr;
/* primitive intersection */
while (prim_addr < prim_addr2) {
for (; prim_addr < prim_addr2; prim_addr++) {
kernel_assert((kernel_tex_fetch(__prim_type, prim_addr) & PRIMITIVE_ALL) ==
(type & PRIMITIVE_ALL));
bool hit;
@ -156,16 +156,29 @@ ccl_device_inline
* might give a few % performance improvement */
Intersection isect ccl_optional_struct_init;
const int prim_object = (object == OBJECT_NONE) ?
kernel_tex_fetch(__prim_object, prim_addr) :
object;
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
switch (type & PRIMITIVE_ALL) {
case PRIMITIVE_TRIANGLE: {
hit = triangle_intersect(
kg, &isect, P, dir, t_max_current, visibility, object, prim_addr);
kg, &isect, P, dir, t_max_current, visibility, prim_object, prim, prim_addr);
break;
}
#if BVH_FEATURE(BVH_MOTION)
case PRIMITIVE_MOTION_TRIANGLE: {
hit = motion_triangle_intersect(
kg, &isect, P, dir, t_max_current, ray->time, visibility, object, prim_addr);
hit = motion_triangle_intersect(kg,
&isect,
P,
dir,
t_max_current,
ray->time,
visibility,
prim_object,
prim,
prim_addr);
break;
}
#endif
@ -182,20 +195,9 @@ ccl_device_inline
}
}
const int curve_object = (object == OBJECT_NONE) ?
kernel_tex_fetch(__prim_object, prim_addr) :
object;
const int curve_type = kernel_tex_fetch(__prim_type, prim_addr);
const int curve_prim = kernel_tex_fetch(__prim_index, prim_addr);
hit = curve_intersect(kg,
&isect,
P,
dir,
t_max_current,
curve_object,
curve_prim,
ray->time,
curve_type);
hit = curve_intersect(
kg, &isect, P, dir, t_max_current, prim_object, prim, ray->time, curve_type);
break;
}
@ -211,20 +213,9 @@ ccl_device_inline
}
}
const int point_object = (object == OBJECT_NONE) ?
kernel_tex_fetch(__prim_object, prim_addr) :
object;
const int point_prim = kernel_tex_fetch(__prim_index, prim_addr);
const int point_type = kernel_tex_fetch(__prim_type, prim_addr);
hit = point_intersect(kg,
&isect,
P,
dir,
t_max_current,
point_object,
point_prim,
ray->time,
point_type);
hit = point_intersect(
kg, &isect, P, dir, t_max_current, prim_object, prim, ray->time, point_type);
break;
}
#endif /* BVH_FEATURE(BVH_POINTCLOUD) */
@ -301,8 +292,6 @@ ccl_device_inline
integrator_state_write_shadow_isect(state, &isect, record_index);
}
}
prim_addr++;
}
}
else {

View File

@ -137,8 +137,14 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg,
case PRIMITIVE_TRIANGLE: {
for (; prim_addr < prim_addr2; prim_addr++) {
kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
const int prim_object = (object == OBJECT_NONE) ?
kernel_tex_fetch(__prim_object, prim_addr) :
object;
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
if (triangle_intersect(
kg, isect, P, dir, isect->t, visibility, object, prim_addr)) {
kg, isect, P, dir, isect->t, visibility, prim_object, prim, prim_addr)) {
/* shadow ray early termination */
if (visibility & PATH_RAY_SHADOW_OPAQUE)
return true;
@ -150,8 +156,22 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg,
case PRIMITIVE_MOTION_TRIANGLE: {
for (; prim_addr < prim_addr2; prim_addr++) {
kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
if (motion_triangle_intersect(
kg, isect, P, dir, isect->t, ray->time, visibility, object, prim_addr)) {
const int prim_object = (object == OBJECT_NONE) ?
kernel_tex_fetch(__prim_object, prim_addr) :
object;
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
if (motion_triangle_intersect(kg,
isect,
P,
dir,
isect->t,
ray->time,
visibility,
prim_object,
prim,
prim_addr)) {
/* shadow ray early termination */
if (visibility & PATH_RAY_SHADOW_OPAQUE)
return true;
@ -173,13 +193,14 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg,
}
}
const int curve_object = (object == OBJECT_NONE) ?
kernel_tex_fetch(__prim_object, prim_addr) :
object;
const int curve_prim = kernel_tex_fetch(__prim_index, prim_addr);
const int prim_object = (object == OBJECT_NONE) ?
kernel_tex_fetch(__prim_object, prim_addr) :
object;
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
const int curve_type = kernel_tex_fetch(__prim_type, prim_addr);
const bool hit = curve_intersect(
kg, isect, P, dir, isect->t, curve_object, curve_prim, ray->time, curve_type);
kg, isect, P, dir, isect->t, prim_object, prim, ray->time, curve_type);
if (hit) {
/* shadow ray early termination */
if (visibility & PATH_RAY_SHADOW_OPAQUE)
@ -200,13 +221,14 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg,
}
}
const int point_object = (object == OBJECT_NONE) ?
kernel_tex_fetch(__prim_object, prim_addr) :
object;
const int point_prim = kernel_tex_fetch(__prim_index, prim_addr);
const int prim_object = (object == OBJECT_NONE) ?
kernel_tex_fetch(__prim_object, prim_addr) :
object;
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
const int point_type = kernel_tex_fetch(__prim_type, prim_addr);
const bool hit = point_intersect(
kg, isect, P, dir, isect->t, point_object, point_prim, ray->time, point_type);
kg, isect, P, dir, isect->t, prim_object, prim, ray->time, point_type);
if (hit) {
/* shadow ray early termination */
if (visibility & PATH_RAY_SHADOW_OPAQUE)

View File

@ -140,14 +140,17 @@ ccl_device_inline
for (; prim_addr < prim_addr2; prim_addr++) {
kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
/* only primitives from volume object */
uint tri_object = (object == OBJECT_NONE) ?
kernel_tex_fetch(__prim_object, prim_addr) :
object;
int object_flag = kernel_tex_fetch(__object_flag, tri_object);
const int prim_object = (object == OBJECT_NONE) ?
kernel_tex_fetch(__prim_object, prim_addr) :
object;
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
int object_flag = kernel_tex_fetch(__object_flag, prim_object);
if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) {
continue;
}
triangle_intersect(kg, isect, P, dir, isect->t, visibility, object, prim_addr);
triangle_intersect(
kg, isect, P, dir, isect->t, visibility, prim_object, prim, prim_addr);
}
break;
}
@ -157,15 +160,24 @@ ccl_device_inline
for (; prim_addr < prim_addr2; prim_addr++) {
kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
/* only primitives from volume object */
uint tri_object = (object == OBJECT_NONE) ?
kernel_tex_fetch(__prim_object, prim_addr) :
object;
int object_flag = kernel_tex_fetch(__object_flag, tri_object);
const int prim_object = (object == OBJECT_NONE) ?
kernel_tex_fetch(__prim_object, prim_addr) :
object;
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
int object_flag = kernel_tex_fetch(__object_flag, prim_object);
if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) {
continue;
}
motion_triangle_intersect(
kg, isect, P, dir, isect->t, ray->time, visibility, object, prim_addr);
motion_triangle_intersect(kg,
isect,
P,
dir,
isect->t,
ray->time,
visibility,
prim_object,
prim,
prim_addr);
}
break;
}

View File

@ -143,15 +143,16 @@ ccl_device_inline
for (; prim_addr < prim_addr2; prim_addr++) {
kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
/* only primitives from volume object */
uint tri_object = (object == OBJECT_NONE) ?
kernel_tex_fetch(__prim_object, prim_addr) :
object;
int object_flag = kernel_tex_fetch(__object_flag, tri_object);
const int prim_object = (object == OBJECT_NONE) ?
kernel_tex_fetch(__prim_object, prim_addr) :
object;
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
int object_flag = kernel_tex_fetch(__object_flag, prim_object);
if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) {
continue;
}
hit = triangle_intersect(
kg, isect_array, P, dir, isect_t, visibility, object, prim_addr);
kg, isect_array, P, dir, isect_t, visibility, prim_object, prim, prim_addr);
if (hit) {
/* Move on to next entry in intersections array. */
isect_array++;
@ -183,15 +184,24 @@ ccl_device_inline
for (; prim_addr < prim_addr2; prim_addr++) {
kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
/* only primitives from volume object */
uint tri_object = (object == OBJECT_NONE) ?
kernel_tex_fetch(__prim_object, prim_addr) :
object;
int object_flag = kernel_tex_fetch(__object_flag, tri_object);
const int prim_object = (object == OBJECT_NONE) ?
kernel_tex_fetch(__prim_object, prim_addr) :
object;
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
int object_flag = kernel_tex_fetch(__object_flag, prim_object);
if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) {
continue;
}
hit = motion_triangle_intersect(
kg, isect_array, P, dir, isect_t, ray->time, visibility, object, prim_addr);
hit = motion_triangle_intersect(kg,
isect_array,
P,
dir,
isect_t,
ray->time,
visibility,
prim_object,
prim,
prim_addr);
if (hit) {
/* Move on to next entry in intersections array. */
isect_array++;

View File

@ -199,22 +199,18 @@ ccl_device int volume_sample_channel(float3 albedo,
* Tracing". Matt Jen-Yuan Chiang, Peter Kutz, Brent Burley. SIGGRAPH 2016. */
float3 weights = fabs(throughput * albedo);
float sum_weights = weights.x + weights.y + weights.z;
float3 weights_pdf;
if (sum_weights > 0.0f) {
weights_pdf = weights / sum_weights;
*pdf = weights / sum_weights;
}
else {
weights_pdf = make_float3(1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f);
*pdf = make_float3(1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f);
}
*pdf = weights_pdf;
/* OpenCL does not support -> on float3, so don't use pdf->x. */
if (rand < weights_pdf.x) {
if (rand < pdf->x) {
return 0;
}
else if (rand < weights_pdf.x + weights_pdf.y) {
else if (rand < pdf->x + pdf->y) {
return 1;
}
else {

View File

@ -576,6 +576,150 @@ __intersection__curve_all_shadow(constant KernelParamsMetal &launch_params_metal
return result;
}
#endif /* __HAIR__ */
#ifdef __POINTCLOUD__
ccl_device_inline
void metalrt_intersection_point(constant KernelParamsMetal &launch_params_metal,
ray_data MetalKernelContext::MetalRTIntersectionPayload &payload,
const uint object,
const uint prim,
const uint type,
const float3 ray_origin,
const float3 ray_direction,
float time,
const float ray_tmax,
thread BoundingBoxIntersectionResult &result)
{
# ifdef __VISIBILITY_FLAG__
const uint visibility = payload.visibility;
if ((kernel_tex_fetch(__objects, object).visibility & visibility) == 0) {
return;
}
# endif
float3 P = ray_origin;
float3 dir = ray_direction;
/* The direction is not normalized by default, but the point intersection routine expects that */
float len;
dir = normalize_len(dir, &len);
Intersection isect;
isect.t = ray_tmax;
/* Transform maximum distance into object space. */
if (isect.t != FLT_MAX)
isect.t *= len;
MetalKernelContext context(launch_params_metal);
if (context.point_intersect(NULL, &isect, P, dir, isect.t, object, prim, time, type)) {
result = metalrt_visibility_test<BoundingBoxIntersectionResult, METALRT_HIT_BOUNDING_BOX>(
launch_params_metal, payload, object, prim, isect.u);
if (result.accept) {
result.distance = isect.t / len;
payload.u = isect.u;
payload.v = isect.v;
payload.prim = prim;
payload.type = type;
}
}
}
ccl_device_inline
void metalrt_intersection_point_shadow(constant KernelParamsMetal &launch_params_metal,
ray_data MetalKernelContext::MetalRTIntersectionShadowPayload &payload,
const uint object,
const uint prim,
const uint type,
const float3 ray_origin,
const float3 ray_direction,
float time,
const float ray_tmax,
thread BoundingBoxIntersectionResult &result)
{
const uint visibility = payload.visibility;
float3 P = ray_origin;
float3 dir = ray_direction;
/* The direction is not normalized by default, but the point intersection routine expects that */
float len;
dir = normalize_len(dir, &len);
Intersection isect;
isect.t = ray_tmax;
/* Transform maximum distance into object space */
if (isect.t != FLT_MAX)
isect.t *= len;
MetalKernelContext context(launch_params_metal);
if (context.point_intersect(NULL, &isect, P, dir, isect.t, object, prim, time, type)) {
result.continue_search = metalrt_shadow_all_hit<METALRT_HIT_BOUNDING_BOX>(
launch_params_metal, payload, object, prim, float2(isect.u, isect.v), ray_tmax);
result.accept = !result.continue_search;
if (result.accept) {
result.distance = isect.t / len;
}
}
}
[[intersection(bounding_box, triangle_data, METALRT_TAGS)]]
BoundingBoxIntersectionResult
__intersection__point(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
ray_data MetalKernelContext::MetalRTIntersectionPayload &payload [[payload]],
const uint object [[user_instance_id]],
const uint primitive_id [[primitive_id]],
const float3 ray_origin [[origin]],
const float3 ray_direction [[direction]],
const float ray_tmax [[max_distance]])
{
const uint prim = primitive_id + kernel_tex_fetch(__object_prim_offset, object);
const int type = kernel_tex_fetch(__objects, object).primitive_type;
BoundingBoxIntersectionResult result;
result.accept = false;
result.continue_search = true;
result.distance = ray_tmax;
metalrt_intersection_point(launch_params_metal, payload, object, prim, type, ray_origin, ray_direction,
# if defined(__METALRT_MOTION__)
payload.time,
# else
0.0f,
# endif
ray_tmax, result);
return result;
}
[[intersection(bounding_box, triangle_data, METALRT_TAGS)]]
BoundingBoxIntersectionResult
__intersection__point_shadow(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
ray_data MetalKernelContext::MetalRTIntersectionShadowPayload &payload [[payload]],
const uint object [[user_instance_id]],
const uint primitive_id [[primitive_id]],
const float3 ray_origin [[origin]],
const float3 ray_direction [[direction]],
const float ray_tmax [[max_distance]])
{
const uint prim = primitive_id + kernel_tex_fetch(__object_prim_offset, object);
const int type = kernel_tex_fetch(__objects, object).primitive_type;
BoundingBoxIntersectionResult result;
result.accept = false;
result.continue_search = true;
result.distance = ray_tmax;
metalrt_intersection_point_shadow(launch_params_metal, payload, object, prim, type, ray_origin, ray_direction,
# if defined(__METALRT_MOTION__)
payload.time,
# else
0.0f,
# endif
ray_tmax, result);
return result;
}
#endif /* __POINTCLOUD__ */
#endif /* __METALRT__ */

View File

@ -214,6 +214,21 @@ ccl_device_inline void film_get_pass_pixel_light_path(
pixel[0] = f.x;
pixel[1] = f.y;
pixel[2] = f.z;
/* Optional alpha channel. */
if (kfilm_convert->num_components >= 4) {
if (kfilm_convert->pass_combined != PASS_UNUSED) {
float scale, scale_exposure;
film_get_scale_and_scale_exposure(kfilm_convert, buffer, &scale, &scale_exposure);
ccl_global const float *in_combined = buffer + kfilm_convert->pass_combined;
const float alpha = in_combined[3] * scale;
pixel[3] = film_transparency_to_alpha(alpha);
}
else {
pixel[3] = 1.0f;
}
}
}
ccl_device_inline void film_get_pass_pixel_float3(ccl_global const KernelFilmConvert *ccl_restrict

View File

@ -153,14 +153,12 @@ ccl_device_inline bool motion_triangle_intersect(KernelGlobals kg,
float time,
uint visibility,
int object,
int prim,
int prim_addr)
{
/* Primitive index for vertex location lookup. */
int prim = kernel_tex_fetch(__prim_index, prim_addr);
int fobject = (object == OBJECT_NONE) ? kernel_tex_fetch(__prim_object, prim_addr) : object;
/* Get vertex locations for intersection. */
float3 verts[3];
motion_triangle_vertices(kg, fobject, prim, time, verts);
motion_triangle_vertices(kg, object, prim, time, verts);
/* Ray-triangle intersection, unoptimized. */
float t, u, v;
if (ray_triangle_intersect(P, dir, tmax, verts[0], verts[1], verts[2], &u, &v, &t)) {
@ -175,8 +173,7 @@ ccl_device_inline bool motion_triangle_intersect(KernelGlobals kg,
isect->u = u;
isect->v = v;
isect->prim = prim;
isect->object = (object == OBJECT_NONE) ? kernel_tex_fetch(__prim_object, prim_addr) :
object;
isect->object = object;
isect->type = PRIMITIVE_MOTION_TRIANGLE;
return true;
}
@ -196,25 +193,15 @@ ccl_device_inline bool motion_triangle_intersect_local(KernelGlobals kg,
float3 dir,
float time,
int object,
int local_object,
int prim,
int prim_addr,
float tmax,
ccl_private uint *lcg_state,
int max_hits)
{
/* Only intersect with matching object, for instanced objects we
* already know we are only intersecting the right object. */
if (object == OBJECT_NONE) {
if (kernel_tex_fetch(__prim_object, prim_addr) != local_object) {
return false;
}
}
/* Primitive index for vertex location lookup. */
int prim = kernel_tex_fetch(__prim_index, prim_addr);
/* Get vertex locations for intersection. */
float3 verts[3];
motion_triangle_vertices(kg, local_object, prim, time, verts);
motion_triangle_vertices(kg, object, prim, time, verts);
/* Ray-triangle intersection, unoptimized. */
float t, u, v;
if (!ray_triangle_intersect(P, dir, tmax, verts[0], verts[1], verts[2], &u, &v, &t)) {
@ -266,7 +253,7 @@ ccl_device_inline bool motion_triangle_intersect_local(KernelGlobals kg,
isect->u = u;
isect->v = v;
isect->prim = prim;
isect->object = local_object;
isect->object = object;
isect->type = PRIMITIVE_MOTION_TRIANGLE;
/* Record geometric normal. */

View File

@ -33,9 +33,9 @@ ccl_device_inline bool triangle_intersect(KernelGlobals kg,
float tmax,
uint visibility,
int object,
int prim,
int prim_addr)
{
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
const uint tri_vindex = kernel_tex_fetch(__tri_vindex, prim).w;
const float3 tri_a = kernel_tex_fetch(__tri_verts, tri_vindex + 0),
tri_b = kernel_tex_fetch(__tri_verts, tri_vindex + 1),
@ -49,8 +49,7 @@ ccl_device_inline bool triangle_intersect(KernelGlobals kg,
if (kernel_tex_fetch(__prim_visibility, prim_addr) & visibility)
#endif
{
isect->object = (object == OBJECT_NONE) ? kernel_tex_fetch(__prim_object, prim_addr) :
object;
isect->object = object;
isect->prim = prim;
isect->type = PRIMITIVE_TRIANGLE;
isect->u = u;
@ -74,21 +73,12 @@ ccl_device_inline bool triangle_intersect_local(KernelGlobals kg,
float3 P,
float3 dir,
int object,
int local_object,
int prim,
int prim_addr,
float tmax,
ccl_private uint *lcg_state,
int max_hits)
{
/* Only intersect with matching object, for instanced objects we
* already know we are only intersecting the right object. */
if (object == OBJECT_NONE) {
if (kernel_tex_fetch(__prim_object, prim_addr) != local_object) {
return false;
}
}
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
const uint tri_vindex = kernel_tex_fetch(__tri_vindex, prim).w;
const float3 tri_a = kernel_tex_fetch(__tri_verts, tri_vindex + 0),
tri_b = kernel_tex_fetch(__tri_verts, tri_vindex + 1),
@ -139,7 +129,7 @@ ccl_device_inline bool triangle_intersect_local(KernelGlobals kg,
/* Record intersection. */
ccl_private Intersection *isect = &local_isect->hits[hit];
isect->prim = prim;
isect->object = local_object;
isect->object = object;
isect->type = PRIMITIVE_TRIANGLE;
isect->u = u;
isect->v = v;

View File

@ -188,7 +188,7 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg,
const uint16_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce);
uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag);
shadow_flag |= (is_light) ? PATH_RAY_SHADOW_FOR_LIGHT : 0;
shadow_flag |= PATH_RAY_SURFACE_PASS;
shadow_flag |= (shadow_flag & PATH_RAY_ANY_PASS) ? 0 : PATH_RAY_SURFACE_PASS;
const float3 throughput = INTEGRATOR_STATE(state, path, throughput) * bsdf_eval_sum(&bsdf_eval);
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {

View File

@ -797,7 +797,7 @@ ccl_device_forceinline void integrate_volume_direct_light(
const uint16_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce);
uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag);
shadow_flag |= (is_light) ? PATH_RAY_SHADOW_FOR_LIGHT : 0;
shadow_flag |= PATH_RAY_VOLUME_PASS;
shadow_flag |= (shadow_flag & PATH_RAY_ANY_PASS) ? 0 : PATH_RAY_VOLUME_PASS;
const float3 throughput_phase = throughput * bsdf_eval_sum(&phase_eval);
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {

View File

@ -87,7 +87,9 @@ ccl_device_noinline void svm_node_attr(KernelGlobals kg,
if (node.y == ATTR_STD_GENERATED && desc.element == ATTR_ELEMENT_NONE) {
/* No generated attribute, fall back to object coordinates. */
float3 f = sd->P;
object_inverse_position_transform(kg, sd, &f);
if (sd->object != OBJECT_NONE) {
object_inverse_position_transform(kg, sd, &f);
}
if (type == NODE_ATTR_OUTPUT_FLOAT) {
stack_store_float(stack, out_offset, average(f));
}
@ -179,7 +181,9 @@ ccl_device_noinline void svm_node_attr_bump_dx(KernelGlobals kg,
if (node.y == ATTR_STD_GENERATED && desc.element == ATTR_ELEMENT_NONE) {
/* No generated attribute, fall back to object coordinates. */
float3 f = sd->P + sd->dP.dx;
object_inverse_position_transform(kg, sd, &f);
if (sd->object != OBJECT_NONE) {
object_inverse_position_transform(kg, sd, &f);
}
if (type == NODE_ATTR_OUTPUT_FLOAT) {
stack_store_float(stack, out_offset, average(f));
}
@ -275,7 +279,9 @@ ccl_device_noinline void svm_node_attr_bump_dy(KernelGlobals kg,
if (node.y == ATTR_STD_GENERATED && desc.element == ATTR_ELEMENT_NONE) {
/* No generated attribute, fall back to object coordinates. */
float3 f = sd->P + sd->dP.dy;
object_inverse_position_transform(kg, sd, &f);
if (sd->object != OBJECT_NONE) {
object_inverse_position_transform(kg, sd, &f);
}
if (type == NODE_ATTR_OUTPUT_FLOAT) {
stack_store_float(stack, out_offset, average(f));
}

View File

@ -742,6 +742,7 @@ NODE_DEFINE(AlembicProcedural)
NodeType *type = NodeType::add("alembic", create);
SOCKET_STRING(filepath, "Filename", ustring());
SOCKET_STRING_ARRAY(layers, "Layers", array<ustring>());
SOCKET_FLOAT(frame, "Frame", 1.0f);
SOCKET_FLOAT(start_frame, "Start Frame", 1.0f);
SOCKET_FLOAT(end_frame, "End Frame", 1.0f);
@ -839,14 +840,26 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress)
return;
}
if (!archive.valid()) {
if (!archive.valid() || filepath_is_modified() || layers_is_modified()) {
Alembic::AbcCoreFactory::IFactory factory;
factory.setPolicy(Alembic::Abc::ErrorHandler::kQuietNoopPolicy);
archive = factory.getArchive(filepath.c_str());
std::vector<std::string> filenames;
filenames.push_back(filepath.c_str());
for (const ustring &layer : layers) {
filenames.push_back(layer.c_str());
}
/* We need to reverse the order as overriding archives should come first. */
std::reverse(filenames.begin(), filenames.end());
archive = factory.getArchive(filenames);
if (!archive.valid()) {
/* avoid potential infinite update loops in viewport synchronization */
filepath.clear();
layers.clear();
clear_modified();
return;
}
@ -1165,6 +1178,12 @@ void AlembicProcedural::read_subd(AlembicObject *abc_object, Abc::chrono_t frame
cached_data.subd_creases_weight.copy_to_socket(
frame_time, mesh, mesh->get_subd_creases_weight_socket());
cached_data.subd_vertex_crease_indices.copy_to_socket(
frame_time, mesh, mesh->get_subd_vert_creases_socket());
cached_data.subd_vertex_crease_weights.copy_to_socket(
frame_time, mesh, mesh->get_subd_vert_creases_weight_socket());
mesh->set_num_subd_faces(mesh->get_subd_shader().size());
/* Update attributes. */

View File

@ -320,6 +320,8 @@ struct CachedData {
DataStore<int> num_ngons;
DataStore<array<int>> subd_creases_edge;
DataStore<array<float>> subd_creases_weight;
DataStore<array<int>> subd_vertex_crease_indices;
DataStore<array<float>> subd_vertex_crease_weights;
/* hair data */
DataStore<array<float3>> curve_keys;
@ -479,6 +481,10 @@ class AlembicProcedural : public Procedural {
/* The file path to the Alembic archive */
NODE_SOCKET_API(ustring, filepath)
/* Layers for the Alembic archive. Layers are in the order in which they override data, with the
* latter elements overriding the former ones. */
NODE_SOCKET_API_ARRAY(array<ustring>, layers)
/* The current frame to render. */
NODE_SOCKET_API(float, frame)

View File

@ -478,7 +478,9 @@ static void add_subd_polygons(CachedData &cached_data, const SubDSchemaData &dat
cached_data.uv_loops.add_data(uv_loops, time);
}
static void add_subd_creases(CachedData &cached_data, const SubDSchemaData &data, chrono_t time)
static void add_subd_edge_creases(CachedData &cached_data,
const SubDSchemaData &data,
chrono_t time)
{
if (!(data.crease_indices.valid() && data.crease_indices.valid() &&
data.crease_sharpnesses.valid())) {
@ -517,6 +519,37 @@ static void add_subd_creases(CachedData &cached_data, const SubDSchemaData &data
}
}
static void add_subd_vertex_creases(CachedData &cached_data,
const SubDSchemaData &data,
chrono_t time)
{
if (!(data.corner_indices.valid() && data.crease_sharpnesses.valid())) {
return;
}
const ISampleSelector iss = ISampleSelector(time);
const Int32ArraySamplePtr creases_indices = data.crease_indices.getValue(iss);
const FloatArraySamplePtr creases_sharpnesses = data.crease_sharpnesses.getValue(iss);
if (!(creases_indices && creases_sharpnesses) ||
creases_indices->size() != creases_sharpnesses->size()) {
return;
}
array<float> sharpnesses;
sharpnesses.reserve(creases_indices->size());
array<int> indices;
indices.reserve(creases_indices->size());
for (size_t i = 0; i < creases_indices->size(); i++) {
indices.push_back_reserved((*creases_indices)[i]);
sharpnesses.push_back_reserved((*creases_sharpnesses)[i]);
}
cached_data.subd_vertex_crease_indices.add_data(indices, time);
cached_data.subd_vertex_crease_weights.add_data(sharpnesses, time);
}
static void read_subd_geometry(CachedData &cached_data, const SubDSchemaData &data, chrono_t time)
{
const ISampleSelector iss = ISampleSelector(time);
@ -525,7 +558,8 @@ static void read_subd_geometry(CachedData &cached_data, const SubDSchemaData &da
if (data.topology_variance != kHomogenousTopology || cached_data.shader.size() == 0) {
add_subd_polygons(cached_data, data, time);
add_subd_creases(cached_data, data, time);
add_subd_edge_creases(cached_data, data, time);
add_subd_vertex_creases(cached_data, data, time);
}
}

View File

@ -76,9 +76,10 @@ struct SubDSchemaData {
vector<FaceSetShaderIndexPair> shader_face_sets;
// Those are unsupported for now.
Alembic::AbcGeom::IInt32ArrayProperty corner_indices;
Alembic::AbcGeom::IFloatArrayProperty corner_sharpnesses;
// Those are unsupported for now.
Alembic::AbcGeom::IInt32Property face_varying_interpolate_boundary;
Alembic::AbcGeom::IInt32Property face_varying_propagate_corners;
Alembic::AbcGeom::IInt32Property interpolate_boundary;

View File

@ -263,7 +263,9 @@ template<typename T> inline void cast_from_float4(T *data, float4 value)
/* Slower versions for other all data types, which needs to convert to float and back. */
template<typename T, bool compress_as_srgb = false>
inline void processor_apply_pixels(const OCIO::Processor *processor, T *pixels, size_t num_pixels)
inline void processor_apply_pixels_rgba(const OCIO::Processor *processor,
T *pixels,
size_t num_pixels)
{
/* TODO: implement faster version for when we know the conversion
* is a simple matrix transform between linear spaces. In that case
@ -310,25 +312,79 @@ inline void processor_apply_pixels(const OCIO::Processor *processor, T *pixels,
}
}
}
template<typename T, bool compress_as_srgb = false>
inline void processor_apply_pixels_grayscale(const OCIO::Processor *processor,
T *pixels,
size_t num_pixels)
{
OCIO::ConstCPUProcessorRcPtr device_processor = processor->getDefaultCPUProcessor();
/* Process large images in chunks to keep temporary memory requirement down. */
const size_t chunk_size = std::min((size_t)(16 * 1024 * 1024), num_pixels);
vector<float> float_pixels(chunk_size * 3);
for (size_t j = 0; j < num_pixels; j += chunk_size) {
size_t width = std::min(chunk_size, num_pixels - j);
/* Convert to 3 channels, since that's the minimum required by OpenColorIO. */
{
const T *pixel = pixels + j;
float *fpixel = float_pixels.data();
for (size_t i = 0; i < width; i++, pixel++, fpixel += 3) {
const float f = util_image_cast_to_float<T>(*pixel);
fpixel[0] = f;
fpixel[1] = f;
fpixel[2] = f;
}
}
OCIO::PackedImageDesc desc((float *)float_pixels.data(), width, 1, 3);
device_processor->apply(desc);
{
T *pixel = pixels + j;
const float *fpixel = float_pixels.data();
for (size_t i = 0; i < width; i++, pixel++, fpixel += 3) {
float f = average(make_float3(fpixel[0], fpixel[1], fpixel[2]));
if (compress_as_srgb) {
f = color_linear_to_srgb(f);
}
*pixel = util_image_cast_from_float<T>(f);
}
}
}
}
#endif
template<typename T>
void ColorSpaceManager::to_scene_linear(ustring colorspace,
T *pixels,
size_t num_pixels,
bool compress_as_srgb)
void ColorSpaceManager::to_scene_linear(
ustring colorspace, T *pixels, size_t num_pixels, bool is_rgba, bool compress_as_srgb)
{
#ifdef WITH_OCIO
const OCIO::Processor *processor = (const OCIO::Processor *)get_processor(colorspace);
if (processor) {
if (compress_as_srgb) {
/* Compress output as sRGB. */
processor_apply_pixels<T, true>(processor, pixels, num_pixels);
if (is_rgba) {
if (compress_as_srgb) {
/* Compress output as sRGB. */
processor_apply_pixels_rgba<T, true>(processor, pixels, num_pixels);
}
else {
/* Write output as scene linear directly. */
processor_apply_pixels_rgba<T>(processor, pixels, num_pixels);
}
}
else {
/* Write output as scene linear directly. */
processor_apply_pixels<T>(processor, pixels, num_pixels);
if (compress_as_srgb) {
/* Compress output as sRGB. */
processor_apply_pixels_grayscale<T, true>(processor, pixels, num_pixels);
}
else {
/* Write output as scene linear directly. */
processor_apply_pixels_grayscale<T>(processor, pixels, num_pixels);
}
}
}
#else
@ -348,6 +404,11 @@ void ColorSpaceManager::to_scene_linear(ColorSpaceProcessor *processor_,
if (processor) {
OCIO::ConstCPUProcessorRcPtr device_processor = processor->getDefaultCPUProcessor();
if (channels == 1) {
float3 rgb = make_float3(pixel[0], pixel[0], pixel[0]);
device_processor->applyRGB(&rgb.x);
pixel[0] = average(rgb);
}
if (channels == 3) {
device_processor->applyRGB(pixel);
}
@ -390,9 +451,9 @@ void ColorSpaceManager::free_memory()
}
/* Template instantiations so we don't have to inline functions. */
template void ColorSpaceManager::to_scene_linear(ustring, uchar *, size_t, bool);
template void ColorSpaceManager::to_scene_linear(ustring, ushort *, size_t, bool);
template void ColorSpaceManager::to_scene_linear(ustring, half *, size_t, bool);
template void ColorSpaceManager::to_scene_linear(ustring, float *, size_t, bool);
template void ColorSpaceManager::to_scene_linear(ustring, uchar *, size_t, bool, bool);
template void ColorSpaceManager::to_scene_linear(ustring, ushort *, size_t, bool, bool);
template void ColorSpaceManager::to_scene_linear(ustring, half *, size_t, bool, bool);
template void ColorSpaceManager::to_scene_linear(ustring, float *, size_t, bool, bool);
CCL_NAMESPACE_END

View File

@ -43,10 +43,8 @@ class ColorSpaceManager {
/* Convert pixels in the specified colorspace to scene linear color for
* rendering. Must be a colorspace returned from detect_known_colorspace. */
template<typename T>
static void to_scene_linear(ustring colorspace,
T *pixels,
size_t num_pixels,
bool compress_as_srgb);
static void to_scene_linear(
ustring colorspace, T *pixels, size_t num_pixels, bool is_rgba, bool compress_as_srgb);
/* Efficiently convert pixels to scene linear colorspace at render time,
* for OSL where the image texture cache contains original pixels. The

View File

@ -441,9 +441,13 @@ void ConstantFolder::fold_mapping(NodeMappingType type) const
if (is_zero(scale_in)) {
make_zero();
}
else if ((is_zero(location_in) || type == NODE_MAPPING_TYPE_VECTOR ||
type == NODE_MAPPING_TYPE_NORMAL) &&
is_zero(rotation_in) && is_one(scale_in)) {
else if (
/* Can't constant fold since we always need to normalize the output. */
(type != NODE_MAPPING_TYPE_NORMAL) &&
/* Check all use values are zero, note location is not used by vector and normal types. */
(is_zero(location_in) || type == NODE_MAPPING_TYPE_VECTOR ||
type == NODE_MAPPING_TYPE_NORMAL) &&
is_zero(rotation_in) && is_one(scale_in)) {
try_bypass_or_make_constant(vector_in);
}
}

View File

@ -576,13 +576,13 @@ bool ImageManager::file_load_image(Image *img, int texture_limit)
pixels[i * 4 + 3] = one;
}
}
}
if (img->metadata.colorspace != u_colorspace_raw &&
img->metadata.colorspace != u_colorspace_srgb) {
/* Convert to scene linear. */
ColorSpaceManager::to_scene_linear(
img->metadata.colorspace, pixels, num_pixels, img->metadata.compress_as_srgb);
}
if (img->metadata.colorspace != u_colorspace_raw &&
img->metadata.colorspace != u_colorspace_srgb) {
/* Convert to scene linear. */
ColorSpaceManager::to_scene_linear(
img->metadata.colorspace, pixels, num_pixels, is_rgba, img->metadata.compress_as_srgb);
}
/* Make sure we don't have buggy values. */

View File

@ -141,6 +141,9 @@ NODE_DEFINE(Mesh)
subdivision_type_enum.insert("catmull_clark", SUBDIVISION_CATMULL_CLARK);
SOCKET_ENUM(subdivision_type, "Subdivision Type", subdivision_type_enum, SUBDIVISION_NONE);
SOCKET_INT_ARRAY(subd_vert_creases, "Subdivision Vertex Crease", array<int>());
SOCKET_FLOAT_ARRAY(
subd_vert_creases_weight, "Subdivision Vertex Crease Weights", array<float>());
SOCKET_INT_ARRAY(subd_creases_edge, "Subdivision Crease Edges", array<int>());
SOCKET_FLOAT_ARRAY(subd_creases_weight, "Subdivision Crease Weights", array<float>());
SOCKET_INT_ARRAY(subd_face_corners, "Subdivision Face Corners", array<int>());
@ -408,7 +411,7 @@ Mesh::SubdFace Mesh::get_subd_face(size_t index) const
return s;
}
void Mesh::add_crease(int v0, int v1, float weight)
void Mesh::add_edge_crease(int v0, int v1, float weight)
{
subd_creases_edge.push_back_slow(v0);
subd_creases_edge.push_back_slow(v1);
@ -419,6 +422,17 @@ void Mesh::add_crease(int v0, int v1, float weight)
tag_subd_creases_weight_modified();
}
void Mesh::add_vertex_crease(int v, float weight)
{
assert(v < verts.size());
subd_vert_creases.push_back_slow(v);
subd_vert_creases_weight.push_back_slow(weight);
tag_subd_vert_creases_modified();
tag_subd_vert_creases_weight_modified();
}
void Mesh::copy_center_to_motion_step(const int motion_step)
{
Attribute *attr_mP = attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);

View File

@ -160,6 +160,9 @@ class Mesh : public Geometry {
NODE_SOCKET_API_ARRAY(array<int>, subd_creases_edge)
NODE_SOCKET_API_ARRAY(array<float>, subd_creases_weight)
NODE_SOCKET_API_ARRAY(array<int>, subd_vert_creases)
NODE_SOCKET_API_ARRAY(array<float>, subd_vert_creases_weight)
/* Subdivisions parameters */
NODE_SOCKET_API(float, subd_dicing_rate)
NODE_SOCKET_API(int, subd_max_level)
@ -210,7 +213,8 @@ class Mesh : public Geometry {
void add_vertex_slow(float3 P);
void add_triangle(int v0, int v1, int v2, int shader, bool smooth);
void add_subd_face(int *corners, int num_corners, int shader_, bool smooth_);
void add_crease(int v0, int v1, float weight);
void add_edge_crease(int v0, int v1, float weight);
void add_vertex_crease(int v, float weight);
void copy_center_to_motion_step(const int motion_step);

View File

@ -82,24 +82,54 @@ template<>
bool TopologyRefinerFactory<ccl::Mesh>::assignComponentTags(TopologyRefiner &refiner,
ccl::Mesh const &mesh)
{
/* Historical maximum crease weight used at Pixar, influencing the maximum in OpenSubDiv. */
static constexpr float CREASE_SCALE = 10.0f;
size_t num_creases = mesh.get_subd_creases_weight().size();
size_t num_vertex_creases = mesh.get_subd_vert_creases().size();
/* The last loop is over the vertices, so early exit to avoid iterating them needlessly. */
if (num_creases == 0 && num_vertex_creases == 0) {
return true;
}
for (int i = 0; i < num_creases; i++) {
ccl::Mesh::SubdEdgeCrease crease = mesh.get_subd_crease(i);
Index edge = findBaseEdge(refiner, crease.v[0], crease.v[1]);
if (edge != INDEX_INVALID) {
setBaseEdgeSharpness(refiner, edge, crease.crease * 10.0f);
setBaseEdgeSharpness(refiner, edge, crease.crease * CREASE_SCALE);
}
}
std::map<int, float> vertex_creases;
for (size_t i = 0; i < num_vertex_creases; ++i) {
const int vertex_idx = mesh.get_subd_vert_creases()[i];
const float weight = mesh.get_subd_vert_creases_weight()[i];
vertex_creases[vertex_idx] = weight * CREASE_SCALE;
}
for (int i = 0; i < mesh.get_verts().size(); i++) {
float sharpness = 0.0f;
std::map<int, float>::const_iterator iter = vertex_creases.find(i);
if (iter != vertex_creases.end()) {
sharpness = iter->second;
}
ConstIndexArray vert_edges = getBaseVertexEdges(refiner, i);
if (vert_edges.size() == 2) {
float sharpness = refiner.getLevel(0).getEdgeSharpness(vert_edges[0]);
sharpness = ccl::min(sharpness, refiner.getLevel(0).getEdgeSharpness(vert_edges[1]));
const float sharpness0 = refiner.getLevel(0).getEdgeSharpness(vert_edges[0]);
const float sharpness1 = refiner.getLevel(0).getEdgeSharpness(vert_edges[1]);
sharpness += ccl::min(sharpness0, sharpness1);
sharpness = ccl::min(sharpness, CREASE_SCALE);
}
if (sharpness != 0.0f) {
setBaseVertexSharpness(refiner, i, sharpness);
}
}

View File

@ -64,7 +64,7 @@ template<typename T, size_t alignment = MIN_ALIGNMENT_CPU_DATA_TYPES> class arra
else {
data_ = mem_allocate(from.datasize_);
if (from.datasize_ > 0) {
memcpy(data_, from.data_, from.datasize_ * sizeof(T));
mem_copy(data_, from.data_, from.datasize_);
}
datasize_ = from.datasize_;
capacity_ = datasize_;
@ -76,7 +76,7 @@ template<typename T, size_t alignment = MIN_ALIGNMENT_CPU_DATA_TYPES> class arra
if (this != &from) {
resize(from.size());
if (datasize_ > 0) {
memcpy((void *)data_, from.data_, datasize_ * sizeof(T));
mem_copy(data_, from.data_, datasize_);
}
}
@ -88,7 +88,7 @@ template<typename T, size_t alignment = MIN_ALIGNMENT_CPU_DATA_TYPES> class arra
resize(from.size());
if (from.size() > 0 && datasize_ > 0) {
memcpy(data_, &from[0], datasize_ * sizeof(T));
mem_copy(data_, from.data(), datasize_);
}
return *this;
@ -161,8 +161,7 @@ template<typename T, size_t alignment = MIN_ALIGNMENT_CPU_DATA_TYPES> class arra
return NULL;
}
else if (data_ != NULL) {
memcpy(
(void *)newdata, data_, ((datasize_ < newsize) ? datasize_ : newsize) * sizeof(T));
mem_copy(newdata, data_, ((datasize_ < newsize) ? datasize_ : newsize));
mem_free(data_, capacity_);
}
data_ = newdata;
@ -246,7 +245,7 @@ template<typename T, size_t alignment = MIN_ALIGNMENT_CPU_DATA_TYPES> class arra
if (newcapacity > capacity_) {
T *newdata = mem_allocate(newcapacity);
if (data_ != NULL) {
memcpy(newdata, data_, ((datasize_ < newcapacity) ? datasize_ : newcapacity) * sizeof(T));
mem_copy(newdata, data_, ((datasize_ < newcapacity) ? datasize_ : newcapacity));
mem_free(data_, capacity_);
}
data_ = newdata;
@ -280,7 +279,7 @@ template<typename T, size_t alignment = MIN_ALIGNMENT_CPU_DATA_TYPES> class arra
if (from.size()) {
size_t old_size = size();
resize(old_size + from.size());
memcpy(data_ + old_size, from.data(), sizeof(T) * from.size());
mem_copy(data_ + old_size, from.data(), from.size());
}
}
@ -308,6 +307,11 @@ template<typename T, size_t alignment = MIN_ALIGNMENT_CPU_DATA_TYPES> class arra
}
}
inline void mem_copy(T *mem_to, const T *mem_from, const size_t N)
{
memcpy((void *)mem_to, mem_from, sizeof(T) * N);
}
T *data_;
size_t datasize_;
size_t capacity_;

View File

@ -131,10 +131,7 @@ ccl_device_inline int4 clamp(const int4 &a, const int4 &mn, const int4 &mx)
ccl_device_inline int4 select(const int4 &mask, const int4 &a, const int4 &b)
{
# ifdef __KERNEL_SSE__
const __m128 m = _mm_cvtepi32_ps(mask);
/* TODO(sergey): avoid cvt. */
return int4(_mm_castps_si128(
_mm_or_ps(_mm_and_ps(m, _mm_castsi128_ps(a)), _mm_andnot_ps(m, _mm_castsi128_ps(b)))));
return int4(_mm_or_si128(_mm_and_si128(mask, a), _mm_andnot_si128(mask, b)));
# else
return make_int4(
(mask.x) ? a.x : b.x, (mask.y) ? a.y : b.y, (mask.z) ? a.z : b.z, (mask.w) ? a.w : b.w);

View File

@ -544,7 +544,7 @@ void GHOST_WindowX11::refreshXInputDevices()
std::vector<XEventClass> xevents;
for (GHOST_SystemX11::GHOST_TabletX11 &xtablet : m_system->GetXTablets()) {
/* With modern XInput (xlib 1.6.2 at least and/or evdev 2.9.0) and some 'no-name' tablets
/* With modern XInput (XLIB 1.6.2 at least and/or EVDEV 2.9.0) and some 'no-name' tablets
* like 'UC-LOGIC Tablet WP5540U', we also need to 'select' ButtonPress for motion event,
* otherwise we do not get any tablet motion event once pen is pressed... See T43367.
*/

View File

@ -130,8 +130,7 @@ GHOST_Wintab *GHOST_Wintab::loadWintab(HWND hwnd)
}
}
return new GHOST_Wintab(hwnd,
std::move(handle),
return new GHOST_Wintab(std::move(handle),
info,
get,
set,
@ -174,8 +173,7 @@ void GHOST_Wintab::extractCoordinates(LOGCONTEXT &lc, Coord &tablet, Coord &syst
system.y.ext = -lc.lcSysExtY;
}
GHOST_Wintab::GHOST_Wintab(HWND hwnd,
unique_hmodule handle,
GHOST_Wintab::GHOST_Wintab(unique_hmodule handle,
GHOST_WIN32_WTInfo info,
GHOST_WIN32_WTGet get,
GHOST_WIN32_WTSet set,

View File

@ -214,8 +214,7 @@ class GHOST_Wintab {
/** Most recently received tablet data, or none if pen is not in range. */
GHOST_TabletData m_lastTabletData = GHOST_TABLET_DATA_NONE;
GHOST_Wintab(HWND hwnd,
unique_hmodule handle,
GHOST_Wintab(unique_hmodule handle,
GHOST_WIN32_WTInfo info,
GHOST_WIN32_WTGet get,
GHOST_WIN32_WTSet set,

View File

@ -292,7 +292,7 @@ target_link_libraries(multitest_c
guardedalloc_lib
wcwidth_lib
${OPENGL_gl_LIBRARY}
${FREETYPE_LIBRARY}
${FREETYPE_LIBRARIES}
${ZLIB_LIBRARIES}
${CMAKE_DL_LIBS}
${PLATFORM_LINKLIBS}

@ -1 +1 @@
Subproject commit d46eacffd92496ae9702a71dfc4da235eaf7731b
Subproject commit 68f8874599d7fbac10fa332535450d8a78fafda2

@ -1 +1 @@
Subproject commit d4fcda5935c5c561e77aadd32a32500cf280dcaa
Subproject commit b825c2d31ad39218fe39c99037d31bc2479211bd

@ -1 +1 @@
Subproject commit 7936dde9ece881d531b1a2ee6c45ddb56d30038c
Subproject commit 61e45814503f51963c91c51aaf764612e7c5dc72

View File

@ -245,7 +245,7 @@ def RKS_GEN_custom_props(_ksi, _context, ks, data):
# Can technically happen, but there is no known case.
continue
if rna_property is None:
# In this case the property cannot be converted to an
# In this case the property cannot be converted to an
# FCurve-compatible value, so we can't keyframe it anyways.
continue
if rna_property.rna_type not in prop_type_compat:

View File

@ -2467,18 +2467,22 @@ class WM_OT_batch_rename(Operator):
name="Type",
items=(
('OBJECT', "Objects", ""),
('COLLECTION', "Collections", ""),
('MATERIAL', "Materials", ""),
None,
# Enum identifiers are compared with 'object.type'.
# Follow order in "Add" menu.
('MESH', "Meshes", ""),
('CURVE', "Curves", ""),
('META', "Metaballs", ""),
('VOLUME', "Volumes", ""),
('GPENCIL', "Grease Pencils", ""),
('ARMATURE', "Armatures", ""),
('LATTICE', "Lattices", ""),
('GPENCIL', "Grease Pencils", ""),
('LIGHT', "Light", ""),
('LIGHT_PROBE', "Light Probes", ""),
('CAMERA', "Cameras", ""),
('SPEAKER', "Speakers", ""),
('LIGHT_PROBE', "Light Probes", ""),
None,
('BONE', "Bones", ""),
('NODE', "Nodes", ""),
@ -2498,7 +2502,26 @@ class WM_OT_batch_rename(Operator):
actions: CollectionProperty(type=BatchRenameAction)
@staticmethod
def _data_from_context(context, data_type, only_selected, *, check_context=False):
def _selected_ids_from_outliner_by_type(context, ty):
return [
id for id in context.selected_ids
if isinstance(id, ty)
if id.library is None
]
@staticmethod
def _selected_ids_from_outliner_by_type_for_object_data(context, ty):
# Include selected object-data as well as the selected ID's.
from bpy.types import Object
# De-duplicate the result as object-data may cause duplicates.
return tuple(set([
id for id_base in context.selected_ids
if isinstance(id := id_base.data if isinstance(id_base, Object) else id_base, ty)
if id.library is None
]))
@classmethod
def _data_from_context(cls, context, data_type, only_selected, *, check_context=False):
mode = context.mode
scene = context.scene
@ -2512,10 +2535,9 @@ class WM_OT_batch_rename(Operator):
return data_type_test
if data_type == data_type_test:
data = (
# TODO, we don't have access to seqbasep, this won't work when inside metas.
[seq for seq in context.scene.sequence_editor.sequences_all if seq.select]
context.selected_sequences
if only_selected else
context.scene.sequence_editor.sequences_all,
scene.sequence_editor.sequences_all,
"name",
"Strip(s)",
)
@ -2531,6 +2553,18 @@ class WM_OT_batch_rename(Operator):
"name",
"Node(s)",
)
elif space_type == 'OUTLINER':
data_type_test = 'COLLECTION'
if check_context:
return data_type_test
if data_type == data_type_test:
data = (
cls._selected_ids_from_outliner_by_type(context, bpy.types.Collection)
if only_selected else
scene.collection.children_recursive,
"name",
"Collection(s)",
)
else:
if mode == 'POSE' or (mode == 'WEIGHT_PAINT' and context.pose_object):
data_type_test = 'BONE'
@ -2561,15 +2595,17 @@ class WM_OT_batch_rename(Operator):
return 'OBJECT'
object_data_type_attrs_map = {
'MESH': ("meshes", "Mesh(es)"),
'CURVE': ("curves", "Curve(s)"),
'META': ("metaballs", "Metaball(s)"),
'ARMATURE': ("armatures", "Armature(s)"),
'LATTICE': ("lattices", "Lattice(s)"),
'GPENCIL': ("grease_pencils", "Grease Pencil(s)"),
'CAMERA': ("cameras", "Camera(s)"),
'SPEAKER': ("speakers", "Speaker(s)"),
'LIGHT_PROBE': ("light_probes", "Light Probe(s)"),
'MESH': ("meshes", "Mesh(es)", bpy.types.Mesh),
'CURVE': ("curves", "Curve(s)", bpy.types.Curve),
'META': ("metaballs", "Metaball(s)", bpy.types.MetaBall),
'VOLUME': ("volumes", "Volume(s)", bpy.types.Volume),
'GPENCIL': ("grease_pencils", "Grease Pencil(s)", bpy.types.GreasePencil),
'ARMATURE': ("armatures", "Armature(s)", bpy.types.Armature),
'LATTICE': ("lattices", "Lattice(s)", bpy.types.Lattice),
'LIGHT': ("lights", "Light(s)", bpy.types.Light),
'LIGHT_PROBE': ("light_probes", "Light Probe(s)", bpy.types.LightProbe),
'CAMERA': ("cameras", "Camera(s)", bpy.types.Camera),
'SPEAKER': ("speakers", "Speaker(s)", bpy.types.Speaker),
}
# Finish with space types.
@ -2577,34 +2613,67 @@ class WM_OT_batch_rename(Operator):
if data_type == 'OBJECT':
data = (
context.selected_editable_objects
(
# Outliner.
cls._selected_ids_from_outliner_by_type(context, bpy.types.Object)
if space_type == 'OUTLINER' else
# 3D View (default).
context.selected_editable_objects
)
if only_selected else
[id for id in bpy.data.objects if id.library is None],
"name",
"Object(s)",
)
elif data_type == 'COLLECTION':
data = (
# Outliner case is handled already.
tuple(set(
ob.instance_collection
for ob in context.selected_objects
if ((ob.instance_type == 'COLLECTION') and
(collection := ob.instance_collection) is not None and
(collection.library is None))
))
if only_selected else
[id for id in bpy.data.collections if id.library is None],
"name",
"Collection(s)",
)
elif data_type == 'MATERIAL':
data = (
tuple(set(
slot.material
for ob in context.selected_objects
for slot in ob.material_slots
if slot.material is not None
))
(
# Outliner.
cls._selected_ids_from_outliner_by_type(context, bpy.types.Material)
if space_type == 'OUTLINER' else
# 3D View (default).
tuple(set(
id
for ob in context.selected_objects
for slot in ob.material_slots
if (id := slot.material) is not None and id.library is None
))
)
if only_selected else
[id for id in bpy.data.materials if id.library is None],
"name",
"Material(s)",
)
elif data_type in object_data_type_attrs_map.keys():
attr, descr = object_data_type_attrs_map[data_type]
attr, descr, ty = object_data_type_attrs_map[data_type]
data = (
tuple(set(
id
for ob in context.selected_objects
if ob.type == data_type
if (id := ob.data) is not None and id.library is None
))
(
# Outliner.
cls._selected_ids_from_outliner_by_type_for_object_data(context, ty)
if space_type == 'OUTLINER' else
# 3D View (default).
tuple(set(
id
for ob in context.selected_objects
if ob.type == data_type
if (id := ob.data) is not None and id.library is None
))
)
if only_selected else
[id for id in getattr(bpy.data, attr) if id.library is None],
"name",

View File

@ -1161,6 +1161,11 @@ class ConstraintButtonsSubPanel:
context, self.layout.template_cache_file_time_settings
)
def draw_transform_cache_layers(self, context):
self.draw_transform_cache_subpanel(
context, self.layout.template_cache_file_layers
)
def draw_transform_cache_subpanel(self, context, template_func):
con = self.get_constraint(context)
if con.cache_file is None:
@ -1574,6 +1579,22 @@ class BONE_PT_bTransformCacheConstraint_velocity(BoneConstraintPanel, Constraint
self.draw_transform_cache_velocity(context)
class OBJECT_PT_bTransformCacheConstraint_layers(ObjectConstraintPanel, ConstraintButtonsSubPanel, Panel):
bl_parent_id = "OBJECT_PT_bTransformCacheConstraint"
bl_label = "Override Layers"
def draw(self, context):
self.draw_transform_cache_layers(context)
class BONE_PT_bTransformCacheConstraint_layers(BoneConstraintPanel, ConstraintButtonsSubPanel, Panel):
bl_parent_id = "BONE_PT_bTransformCacheConstraint"
bl_label = "Override Layers"
def draw(self, context):
self.draw_transform_cache_layers(context)
class OBJECT_PT_bTransformCacheConstraint_procedural(ObjectConstraintPanel, ConstraintButtonsSubPanel, Panel):
bl_parent_id = "OBJECT_PT_bTransformCacheConstraint"
bl_label = "Render Procedural"
@ -1695,6 +1716,7 @@ classes = (
OBJECT_PT_bTransformCacheConstraint_time,
OBJECT_PT_bTransformCacheConstraint_procedural,
OBJECT_PT_bTransformCacheConstraint_velocity,
OBJECT_PT_bTransformCacheConstraint_layers,
OBJECT_PT_bPythonConstraint,
OBJECT_PT_bArmatureConstraint,
OBJECT_PT_bArmatureConstraint_bones,
@ -1735,6 +1757,7 @@ classes = (
BONE_PT_bTransformCacheConstraint_time,
BONE_PT_bTransformCacheConstraint_procedural,
BONE_PT_bTransformCacheConstraint_velocity,
BONE_PT_bTransformCacheConstraint_layers,
BONE_PT_bPythonConstraint,
BONE_PT_bArmatureConstraint,
BONE_PT_bArmatureConstraint_bones,

View File

@ -76,6 +76,14 @@ class MESH_MT_shape_key_context_menu(Menu):
layout.operator("object.shape_key_move", icon='TRIA_UP_BAR', text="Move to Top").type = 'TOP'
layout.operator("object.shape_key_move", icon='TRIA_DOWN_BAR', text="Move to Bottom").type = 'BOTTOM'
class MESH_MT_attribute_context_menu(Menu):
bl_label = "Attribute Specials"
def draw(self, context):
layout = self.layout
layout.operator("geometry.attribute_convert")
class MESH_UL_vgroups(UIList):
def draw_item(self, _context, layout, _data, item, icon, _active_data_, _active_propname, _index):
@ -601,6 +609,7 @@ class DATA_PT_customdata(MeshButtonsPanel, Panel):
col.enabled = obj is not None and obj.mode != 'EDIT'
col.prop(me, "use_customdata_vertex_bevel", text="Vertex Bevel Weight")
col.prop(me, "use_customdata_edge_bevel", text="Edge Bevel Weight")
col.prop(me, "use_customdata_vertex_crease", text="Vertex Crease")
col.prop(me, "use_customdata_edge_crease", text="Edge Crease")
@ -670,11 +679,9 @@ class DATA_PT_mesh_attributes(MeshButtonsPanel, Panel):
col.operator("geometry.attribute_add", icon='ADD', text="")
col.operator("geometry.attribute_remove", icon='REMOVE', text="")
active = mesh.attributes.active
if active and (active.domain == "POINT" and active.data_type == "FLOAT_COLOR"):
layout.operator("sculpt.vertex_to_loop_colors", text="Save To Corners")
layout.operator("sculpt.loop_to_vertex_colors", text="Load From Corners")
col.separator()
col.menu("MESH_MT_attribute_context_menu", icon='DOWNARROW_HLT', text="")
self.draw_attribute_warnings(context, layout)
@ -712,6 +719,7 @@ class DATA_PT_mesh_attributes(MeshButtonsPanel, Panel):
classes = (MESH_MT_vertex_group_context_menu,
MESH_MT_shape_key_context_menu,
MESH_MT_attribute_context_menu,
MESH_UL_vgroups,
MESH_UL_fmaps,
MESH_UL_shape_keys,

View File

@ -184,24 +184,18 @@ class OBJECT_PT_collections(ObjectButtonsPanel, Panel):
row.operator("object.collection_add", text="Add to Collection")
row.operator("object.collection_add", text="", icon='ADD')
obj_name = obj.name
for collection in bpy.data.collections:
# XXX this is slow and stupid!, we need 2 checks, one that's fast
# and another that we can be sure its not a name collision
# from linked library data
collection_objects = collection.objects
if obj_name in collection.objects and obj in collection_objects[:]:
col = layout.column(align=True)
for collection in obj.users_collection:
col = layout.column(align=True)
col.context_pointer_set("collection", collection)
col.context_pointer_set("collection", collection)
row = col.box().row()
row.prop(collection, "name", text="")
row.operator("object.collection_remove", text="", icon='X', emboss=False)
row.menu("COLLECTION_MT_context_menu", icon='DOWNARROW_HLT', text="")
row = col.box().row()
row.prop(collection, "name", text="")
row.operator("object.collection_remove", text="", icon='X', emboss=False)
row.menu("COLLECTION_MT_context_menu", icon='DOWNARROW_HLT', text="")
row = col.box().row()
row.prop(collection, "instance_offset", text="")
row = col.box().row()
row.prop(collection, "instance_offset", text="")
class OBJECT_PT_display(ObjectButtonsPanel, Panel):

View File

@ -2374,8 +2374,13 @@ def brush_basic_texpaint_settings(layout, context, brush, *, compact=False):
capabilities = brush.image_paint_capabilities
if capabilities.has_color:
UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="")
if context.mode != "SCULT":
row = layout.row(align=True)
row.ui_units_x = 4
UnifiedPaintPanel.prop_unified_color(row, context, brush, "color", text="")
UnifiedPaintPanel.prop_unified_color(row, context, brush, "secondary_color", text="")
row.separator()
if context.mode != "SCULPT":
layout.prop(brush, "blend", text="" if compact else "Blend")
else:
UnifiedPaintPanel.channel_unified(layout,

View File

@ -2249,6 +2249,7 @@ class USERPREF_PT_experimental_debugging(ExperimentalPanel, Panel):
({"property": "use_cycles_debug"}, None),
({"property": "use_geometry_nodes_legacy"}, "T91274"),
({"property": "show_asset_debug_info"}, None),
({"property": "use_asset_indexing"}, None),
),
)

View File

@ -4079,6 +4079,10 @@ class VIEW3D_MT_edit_mesh_vertices(Menu):
layout.separator()
layout.operator("transform.vert_crease")
layout.separator()
layout.operator("mesh.blend_from_shape")
layout.operator("mesh.shape_propagate_to_all", text="Propagate to Shapes")

View File

@ -142,6 +142,7 @@ def mesh_node_items(context):
yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
yield NodeItem("GeometryNodeDualMesh")
yield NodeItem("GeometryNodeFlipFaces")
yield NodeItem("GeometryNodeMeshBoolean")
yield NodeItem("GeometryNodeMeshToCurve")
yield NodeItem("GeometryNodeMeshToPoints")
@ -149,6 +150,7 @@ def mesh_node_items(context):
yield NodeItem("GeometryNodeSubdivideMesh")
yield NodeItem("GeometryNodeSubdivisionSurface")
yield NodeItem("GeometryNodeTriangulate")
yield NodeItem("GeometryNodeScaleElements")
yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
yield NodeItem("GeometryNodeInputMeshEdgeAngle")
yield NodeItem("GeometryNodeInputMeshEdgeNeighbors")
@ -704,6 +706,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeCurvePrimitiveCircle"),
NodeItem("GeometryNodeCurveStar"),
NodeItem("GeometryNodeCurveSpiral"),
NodeItem("GeometryNodeCurveArc"),
NodeItem("GeometryNodeCurveQuadraticBezier"),
NodeItem("GeometryNodeCurvePrimitiveQuadrilateral"),
NodeItem("GeometryNodeCurvePrimitiveBezierSegment"),
@ -757,6 +760,7 @@ geometry_node_categories = [
]),
GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[
NodeItem("GeometryNodeAccumulateField"),
NodeItem("GeometryNodeFieldAtIndex"),
NodeItem("ShaderNodeMapRange"),
NodeItem("ShaderNodeFloatCurve"),
NodeItem("ShaderNodeClamp"),

View File

@ -54,7 +54,7 @@ set(LIB
bf_gpu
bf_intern_guardedalloc
${FREETYPE_LIBRARY}
${FREETYPE_LIBRARIES}
)
if(WIN32)

View File

@ -150,7 +150,7 @@ typedef enum eBlendfileLinkAppendForeachItemFlag {
*
* IMPORTANT: Those 'indirect' items currently may not cover **all** indirectly linked data.
* See comments in #foreach_libblock_link_append_callback. */
BKE_BLENDFILE_LINK_APPEND_FOREACH_ITEM_FLAG_DO_INDIRECT = 1 << 0,
BKE_BLENDFILE_LINK_APPEND_FOREACH_ITEM_FLAG_DO_INDIRECT = 1 << 1,
} eBlendfileLinkAppendForeachItemFlag;
/**
* Callback called by #BKE_blendfile_link_append_context_item_foreach over each (or a subset of

View File

@ -316,8 +316,8 @@ void BKE_brush_channelset_merge(BrushChannelSet *dst,
BrushChannelSet *parent);
void BKE_brush_channel_apply_mapping_flags(BrushChannel *dst,
BrushChannel *child,
BrushChannel *parent);
const BrushChannel *child,
const BrushChannel *parent);
bool BKE_brush_mapping_is_enabled(BrushChannel *child, BrushChannel *parent, int mapping);

View File

@ -1,129 +1,252 @@
#include "DNA_brush_enums.h"
#include "DNA_brush_types.h"
#include "DNA_color_types.h"
#include "DNA_curveprofile_types.h"
#include "DNA_material_types.h"
#include "DNA_node_types.h"
#include "DNA_sculpt_brush_types.h"
#if 1
#include "BLI_compiler_attrs.h"
#include "BLI_compiler_compat.h"
#include "BLI_ghash.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_rand.h"
#include "BLI_rect.h"
#include "BLI_smallhash.h"
#include "BLI_utildefines.h"
# include "DNA_brush_enums.h"
# include "DNA_brush_types.h"
# include "DNA_color_types.h"
# include "DNA_curveprofile_types.h"
# include "DNA_material_types.h"
# include "DNA_node_types.h"
# include "DNA_sculpt_brush_types.h"
#include "BKE_brush_engine.h"
# include "BLI_compiler_attrs.h"
# include "BLI_compiler_compat.h"
# include "BLI_ghash.h"
# include "BLI_listbase.h"
# include "BLI_math.h"
# include "BLI_rand.h"
# include "BLI_rect.h"
# include "BLI_smallhash.h"
# include "BLI_utildefines.h"
#include "BKE_brush.h"
#include "BKE_brush_engine.h"
#include "BKE_colorband.h"
#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_curvemapping_cache.h"
#include "BKE_curveprofile.h"
#include "BKE_lib_override.h"
#include "BKE_lib_query.h"
#include "BKE_main.h"
#include "BKE_node.h"
#include "BKE_paint.h"
# include "BKE_brush_engine.h"
#include <algorithm>
#include <cmath>
#include <type_traits>
# include "BKE_brush.h"
# include "BKE_brush_engine.h"
# include "BKE_colorband.h"
# include "BKE_colortools.h"
# include "BKE_context.h"
# include "BKE_curvemapping_cache.h"
# include "BKE_curveprofile.h"
# include "BKE_lib_override.h"
# include "BKE_lib_query.h"
# include "BKE_main.h"
# include "BKE_node.h"
# include "BKE_paint.h"
# include <algorithm>
# include <cmath>
# include <cstdio>
# include <string>
# include <type_traits>
# include "intern/brush_channel_names.hh"
# define IS_CACHE_CURVE(curve) BKE_curvemapping_in_cache(curve)
// frees curve if it wasn't cached, returns cache curved
# define GET_CACHE_CURVE(curve) BKE_curvemapping_cache_get(brush_curve_cache, curve, true)
# define RELEASE_CACHE_CURVE(curve) BKE_curvemapping_cache_release(brush_curve_cache, curve)
# define RELEASE_OR_FREE_CURVE(curve) \
curve ? (BKE_curvemapping_cache_release_or_free(brush_curve_cache, curve), nullptr) : nullptr
# define CURVE_ADDREF(curve) BKE_curvemapping_cache_aquire(brush_curve_cache, curve)
struct CurveMappingCache *brush_curve_cache = NULL;
extern BrushChannelType brush_builtin_channels[];
extern int brush_builtin_channel_len;
/* build compile time list of channel idnames */
/** return eithers a reference to a brush channel type,
or if T is BrushCurve the (non-reference) BrushCurveIF wrapper type*/
# define BRUSH_VALUE_REF(T) typename std::conditional<std::is_same_v<T, BrushCurve>, BrushCurveIF, T &>::type
template<class T> struct extract_float_array {
using type = typename std::conditional<std::is_array_v<T>, float, T>::type;
};
namespace blender {
namespace brush {
class BrushCurveIF {
public:
BrushCurveIF(BrushCurve *curve) : _curve(curve)
{
}
BrushCurveIF(const BrushCurveIF &b)
{
_curve = b._curve;
}
float evaluate(float f, float maxval = 1.0f)
{
/* ensure that curve is in valid state */
initCurve(false);
return BKE_brush_curve_strength_ex(_curve->preset, _curve->curve, f, maxval);
}
void initCurve(bool forceCreate = false)
{
if ((forceCreate || _curve->preset == BRUSH_CURVE_CUSTOM) && !_curve->curve) {
CurveMapping *cumap = _curve->curve = static_cast<CurveMapping *>(
MEM_callocN(sizeof(CurveMapping), "channel CurveMapping"));
int preset = CURVE_PRESET_LINE;
/* brush and curvemapping presets aren't perfectly compatible,
try to convert in reasonable manner*/
switch (_curve->preset) {
case BRUSH_CURVE_SMOOTH:
case BRUSH_CURVE_SMOOTHER:
preset = CURVE_PRESET_SMOOTH;
break;
case BRUSH_CURVE_SHARP:
preset = CURVE_PRESET_SHARP;
break;
case BRUSH_CURVE_POW4:
preset = CURVE_PRESET_POW3;
break;
}
struct rctf rect;
rect.xmin = rect.ymin = 0.0f;
rect.xmax = rect.ymax = 1.0f;
BKE_curvemapping_set_defaults(cumap, 1, 0.0f, 0.0f, 1.0f, 1.0f);
BKE_curvemap_reset(cumap->cm, &rect, preset, _curve->preset_slope_negative ? 0 : 1);
BKE_curvemapping_init(cumap);
}
}
void ensureWrite()
{
initCurve(true);
if (IS_CACHE_CURVE(_curve->curve)) {
_curve->curve = BKE_curvemapping_copy(_curve->curve);
}
}
CurveMapping *curve()
{
initCurve(false);
return _curve->curve;
}
eBrushCurvePreset &preset()
{
eBrushCurvePreset *p = reinterpret_cast<eBrushCurvePreset *>(&_curve->preset);
return *p;
}
private:
BrushCurve *_curve;
};
template<typename T> class BrushChannelIF {
public:
BrushChannelIF()
{
_channel = nullptr;
}
BrushChannelIF(BrushChannel *source) : _channel(source)
{
}
/* evaluation functions */
/** int evaluator */
std::enable_if_t<std::is_same<T, int>::value> evaluate(BrushMappingData *mapdata = nullptr)
eBrushChannelFlag &flag()
{
double val = static_cast<double>(_channel->ivalue);
eBrushChannelFlag *f = reinterpret_cast<eBrushChannelFlag *>(&_channel->flag);
val = _evaluate(val, t, 0UL, mapdata);
return static_cast<int>(val);
return *f;
}
/** float evaluator */
std::enable_if_t<std::is_same<T, float>::value> evaluate(BrushMappingData *mapdata = nullptr)
BrushChannelIF(const BrushChannelIF<T> &b)
{
double val = static_cast<double>(_channel->fvalue);
val = _evaluate(val, t, 0UL, mapdata);
return static_cast<float>(val);
_channel = b._channel;
}
/** bool evaluator */
std::enable_if_t<std::is_same<T, bool>::value> evaluate(BrushMappingData *mapdata = nullptr)
{
double val = static_cast<double>(_channel->fvalue);
BrushChannelIF<T> &operator=(const BrushChannelIF<T> &a) = default;
val = _evaluate(val, t, 0UL, mapdata);
return std::floor(val) != 0;
const BrushChannel &channel()
{
return *_channel;
}
/** Curve evaluator. Unlike other channel types, this takes a float
argument, runs it through the brush curve and returns the result as
a float.
\param t value to evaluate with brush curve
*/
std::enable_if_t<
std::conditional<std::is_same<T, BrushCurve>::value, float, std::false_type>::value>
evaluate(float t, BrushMappingData *mapdata = nullptr)
const char *idname()
{
t = BKE_brush_curve_strength_ex(_channel->curve.preset, _channel->curve.curve, 1.0f - t, 1.0f);
double val = static_cast<double>(t);
return static_cast<float>(_evaluate(val, t, 0UL, mapdata));
return _channel->idname;
}
/** value getter for int channels */
std::add_lvalue_reference_t<std::enable_if_t<std::is_same<T, int>::value>> value()
const bool isValid() const
{
return _channel->ivalue;
return _channel != nullptr;
}
/** value getter for float channels */
std::add_lvalue_reference_t<std::enable_if_t<std::is_same<T, float>::value>> value()
/**
Returns a reference to value of a brush channel.
Note that if T is BrushCurve then a BrushCurveIF
wrapper will be returned instead.
*/
BRUSH_VALUE_REF(T) value()
{
return _channel->fvalue;
if constexpr (std::is_same_v<T, float>) {
return _channel->fvalue;
}
else if constexpr (std::is_same_v<T, int>) {
return _channel->ivalue;
}
else if constexpr (std::is_same_v<T, bool>) {
bool *boolval = reinterpret_cast<bool *>(&_channel->ivalue);
return *boolval;
}
else if constexpr (std::is_same_v<T, BrushCurveIF>) {
return BrushCurveIF(&_channel->curve);
}
else if constexpr (std::is_same_v<T, float[3]>) {
float(*vec3)[3] = reinterpret_cast<float(*)[3]>(_channel->vector);
return vec3[0];
}
else if constexpr (std::is_same_v<T, float[4]>) {
return _channel->vector;
}
T ret;
return ret;
}
/** value getter for bool channels */
std::add_lvalue_reference_t<std::enable_if_t<std::is_same<T, bool>::value>> value()
/* vectorIndex is only used for float[3] and float[4] specializations*/
typename extract_float_array<T>::type evaluate(BrushMappingData *mapping = nullptr, int vectorIndex = 0)
{
return *(reinterpret_cast<bool *>(&_channel->ivalue));
}
if constexpr (std::is_same_v<T, float>) {
return (float)_evaluate((double)_channel->fvalue, 0, mapping);
}
else if constexpr (std::is_same_v<T, int>) {
return (int)_evaluate((double)_channel->ivalue, 0, mapping);
}
else if constexpr (std::is_same_v<T, bool>) {
return fabs(_evaluate((double)(_channel->ivalue & 1), 0, mapping)) > FLT_EPSILON;
}
else if constexpr (std::is_same_v<T, float[3]> || std::is_same_v<T, float[4]>) {
return (float)_evaluate((double)_channel->vector, vectorIndex, mapping);
}
/** value getter for BrushCurve channels */
std::add_lvalue_reference_t<std::enable_if_t<std::is_same<T, BrushCurve>::value>> value()
{
return _channel->curve;
static_assert(!std::is_same_v<T, BrushCurveIF>, "cannot use evaluate with brush curves");
}
private:
double _evaluate(double val, float t, unsigned int idx, BrushMappingData *mapdata = nullptr)
double _evaluate(double val, unsigned int idx, BrushMappingData *mapping = nullptr)
{
if (idx == 3 && !(ch->flag & BRUSH_CHANNEL_APPLY_MAPPING_TO_ALPHA)) {
return f;
if (idx == 3 && !(_channel->flag & BRUSH_CHANNEL_APPLY_MAPPING_TO_ALPHA)) {
return val;
}
if (mapdata) {
double factor = f; // 1.0f;
if (mapping) {
double factor = val;
for (int i = 0; i < BRUSH_MAPPING_MAX; i++) {
BrushMapping *mp = _channel->mappings + i;
@ -132,7 +255,7 @@ template<typename T> class BrushChannelIF {
continue;
}
float inputf = ((float *)mapdata)[i] * mp->premultiply;
float inputf = (reinterpret_cast<float *>(mapping))[i] * mp->premultiply;
switch ((BrushMappingFunc)mp->mapfunc) {
case BRUSH_MAPFUNC_NONE:
@ -191,34 +314,157 @@ template<typename T> class BrushChannelIF {
f2 = factor - f2;
break;
case MA_RAMP_DIFF:
f2 = fabsf(factor - f2);
f2 = std::abs(factor - f2);
break;
default:
printf("Unsupported brush mapping blend mode for %s (%s); will mix instead\n",
ch->name,
ch->idname);
_channel->name,
_channel->idname);
break;
}
factor += (f2 - factor) * mp->factor;
}
f = factor;
CLAMP(f, _channel->def->min, _channel->def->max);
val = factor;
CLAMP(val, (double)_channel->def->min, (double)_channel->def->max);
}
return f;
return val;
}
BrushChannel *_channel;
};
template<typename T> class BrushChannelSetIF {
class BrushChannelSetIF {
public:
BrushChannelSetIF(BrushChannelSet *chset) : _chset(chset)
{
}
BrushChannelSetIF(const BrushChannelSetIF &b)
{
_chset = b._chset;
}
bool isValid()
{
return _chset != nullptr;
}
void destroy()
{
if (_chset) {
if (_chset->namemap) {
BLI_ghash_free(_chset->namemap, nullptr, nullptr);
}
LISTBASE_FOREACH (BrushChannel *, ch, &_chset->channels) {
if (ch->curve.curve) {
RELEASE_OR_FREE_CURVE(ch->curve.curve);
}
}
BLI_freelistN(&_chset->channels);
MEM_SAFE_FREE(_chset);
}
_chset = nullptr;
}
void ensureChannel(const char *idname)
{
}
template<typename T> BrushChannelIF<T> lookup(const char *idname)
{
BrushChannel *ch = static_cast<BrushChannel *>(
BLI_ghash_lookup(_chset->namemap, static_cast<const void *>(idname)));
BrushChannelIF<T> chif(ch);
return chif;
}
template<typename T>
BRUSH_VALUE_REF(T)
getFinalValue(BrushChannelSet *parentset,
BrushChannelIF<T> ch,
BrushMappingData *mapping = nullptr,
int vectorIndex = 0)
{
return getFinalValue(BrushChannelSetIF(parentset), ch, mapping, vectorIndex);
}
/**
Looks up channel with same idname as ch in parentset.
If it exists, returns the evaluated value of that channel
taking all inheritance flags and input mappings into
account.
if it doesn't exist then the value of ch with
input mappings evaluated will be returned.
Note that if T is BrushCurve then we simply return
a BrushCurveIF wrapper of either ch or the
one in parentset, depending on inheritance flags.
*/
template<typename T>
typename extract_float_array<T>::type
getFinalValue(BrushChannelSetIF &parentSet,
BrushChannelIF<T> ch,
BrushMappingData *mapping = nullptr,
int vectorIndex = 0)
{
BrushChannelIF<T> ch2;
if (parentSet.isValid()) {
ch2 = parentSet.lookup<T>(ch.idname());
}
if (!parentSet.isValid() || !ch2.isValid()) {
if constexpr (std::is_same_v<T, BrushCurve>) { // curve?
return ch.value();
}
else if constexpr (std::is_array_v<T>) {
return ch.evaluate(mapping, vectorIndex);
}
}
if constexpr (std::is_same_v<T, BrushCurve>) {
return (int)ch.flag() & (int)BRUSH_CHANNEL_INHERIT ? ch2.value() : ch.value();
}
BrushChannel _cpy = (int)ch.flag() & (int)BRUSH_CHANNEL_INHERIT ? ch2.channel() : ch.channel();
BrushChannelIF<T> cpy(&_cpy);
BKE_brush_channel_apply_mapping_flags(&_cpy, &ch.channel(), &ch2.channel());
return cpy.evaluate(mapping, vectorIndex);
}
/*
We want to validate channel names at compile time,
but we can't do compile-time validation of string literals
without c++20. Instead we use macros to make
lots of accessor methods.
examples:
BrushChannelIF<float> strength();
BrushChannelIF<float> radius();
BrushChannelIF<bool> dyntopo_disabled();
auto ch = chset->strength();
BrushChanneIF<float> ch = chset->strength();
if (ch->isValid()) {
float val = ch->value();
ch->value() = val * val;
}
*/
# define BRUSH_CHANNEL_MAKE_CPP_LOOKUPS
# include "intern/brush_channel_define.h"
private:
BrushChannelSet *_chset;
@ -226,3 +472,4 @@ template<typename T> class BrushChannelSetIF {
} // namespace brush
} // namespace blender
#endif

View File

@ -28,6 +28,7 @@ extern "C" {
#endif
struct CacheFile;
struct CacheFileLayer;
struct CacheReader;
struct Depsgraph;
struct Main;
@ -69,6 +70,15 @@ bool BKE_cache_file_uses_render_procedural(const struct CacheFile *cache_file,
struct Scene *scene,
int dag_eval_mode);
/* Add a layer to the cache_file. Return NULL if the filename is already that of an existing layer
* or if the number of layers exceeds the maximum allowed layer count. */
struct CacheFileLayer *BKE_cachefile_add_layer(struct CacheFile *cache_file,
const char filename[1024]);
struct CacheFileLayer *BKE_cachefile_get_active_layer(struct CacheFile *cache_file);
void BKE_cachefile_remove_layer(struct CacheFile *cache_file, struct CacheFileLayer *layer);
#ifdef __cplusplus
}
#endif

View File

@ -787,6 +787,14 @@ void CustomData_bmesh_asan_poison(const CustomData *data, void *block);
void CustomData_bmesh_asan_unpoison(const CustomData *data, void *block);
int CustomData_get_named_offset(const CustomData *data, int type, const char *name);
#ifndef NDEBUG
struct DynStr;
/** Use to inspect mesh data when debugging. */
void CustomData_debug_info_from_layers(const struct CustomData *data,
const char *indent,
struct DynStr *dynstr);
#endif /* NDEBUG */
#ifdef __cplusplus
}
#endif

View File

@ -406,6 +406,13 @@ int set_listbasepointers(struct Main *main, struct ListBase *lb[]);
((main)->versionfile < (ver) || \
((main)->versionfile == (ver) && (main)->subversionfile < (subver)))
/**
* The size of thumbnails (optionally) stored in the `.blend` files header.
*
* NOTE(@campbellbarton): This is kept small as it's stored uncompressed in the `.blend` file,
* where a larger size would increase the size of every `.blend` file unreasonably.
* If we wanted to increase the size, we'd want to use compression (JPEG or similar).
*/
#define BLEN_THUMB_SIZE 128
#define BLEN_THUMB_MEMSIZE(_x, _y) \

View File

@ -25,6 +25,7 @@
#include "BKE_attribute.h"
#include "BKE_customdata.h"
#include "BKE_mesh_types.h"
#include "BLI_compiler_attrs.h"
#include "BLI_utildefines.h"
struct BLI_Stack;
@ -1041,6 +1042,13 @@ void BKE_mesh_batch_cache_free(struct Mesh *me);
extern void (*BKE_mesh_batch_cache_dirty_tag_cb)(struct Mesh *me, eMeshBatchDirtyMode mode);
extern void (*BKE_mesh_batch_cache_free_cb)(struct Mesh *me);
/* mesh_debug.c */
#ifndef NDEBUG
char *BKE_mesh_debug_info(const struct Mesh *me)
ATTR_NONNULL(1) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
void BKE_mesh_debug_print(const struct Mesh *me) ATTR_NONNULL(1);
#endif
/* Inlines */
/* NOTE(@sybren): Instead of -1 that function uses ORIGINDEX_NONE as defined in BKE_customdata.h,

View File

@ -116,10 +116,6 @@ void BKE_mesh_runtime_eval_to_meshkey(struct Mesh *me_deformed,
struct KeyBlock *kb);
#ifndef NDEBUG
char *BKE_mesh_runtime_debug_info(struct Mesh *me_eval);
void BKE_mesh_runtime_debug_print(struct Mesh *me_eval);
/* XXX Should go in customdata file? */
void BKE_mesh_runtime_debug_print_cdlayers(struct CustomData *data);
bool BKE_mesh_runtime_is_valid(struct Mesh *me_eval);
#endif /* NDEBUG */

View File

@ -1628,6 +1628,10 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_INPUT_SCENE_TIME 1145
#define GEO_NODE_ACCUMULATE_FIELD 1146
#define GEO_NODE_INPUT_MESH_EDGE_ANGLE 1147
#define GEO_NODE_FIELD_AT_INDEX 1148
#define GEO_NODE_CURVE_PRIMITIVE_ARC 1149
#define GEO_NODE_FLIP_FACES 1150
#define GEO_NODE_SCALE_ELEMENTS 1151
/** \} */

View File

@ -306,8 +306,8 @@ BLI_INLINE void BKE_subdiv_rotate_grid_to_quad(
int corner, float grid_u, float grid_v, float *r_quad_u, float *r_quad_v);
/* Convert Blender edge crease value to OpenSubdiv sharpness. */
BLI_INLINE float BKE_subdiv_edge_crease_to_sharpness_f(float edge_crease);
BLI_INLINE float BKE_subdiv_edge_crease_to_sharpness_char(char edge_crease);
BLI_INLINE float BKE_subdiv_crease_to_sharpness_f(float edge_crease);
BLI_INLINE float BKE_subdiv_crease_to_sharpness_char(char edge_crease);
#ifdef __cplusplus
}

View File

@ -40,8 +40,10 @@ void BKE_subsurf_modifier_subdiv_settings_init(struct SubdivSettings *settings,
const struct SubsurfModifierData *smd,
bool use_render_params);
/* If skip_check_is_last is true, we assume that the modifier passed is the last enabled modifier
* in the stack. */
/**
* \param skip_check_is_last: When true, we assume that the modifier passed is the last enabled
* modifier in the stack.
*/
bool BKE_subsurf_modifier_can_do_gpu_subdiv_ex(const struct Scene *scene,
const struct Object *ob,
const struct SubsurfModifierData *smd,
@ -54,6 +56,10 @@ bool BKE_subsurf_modifier_can_do_gpu_subdiv(const struct Scene *scene,
extern void (*BKE_subsurf_modifier_free_gpu_cache_cb)(struct Subdiv *subdiv);
/**
* Main goal of this function is to give usable subdivision surface descriptor
* which matches settings and topology.
*/
struct Subdiv *BKE_subsurf_modifier_subdiv_descriptor_ensure(
const struct SubsurfModifierData *smd,
const struct SubdivSettings *subdiv_settings,
@ -62,8 +68,10 @@ struct Subdiv *BKE_subsurf_modifier_subdiv_descriptor_ensure(
struct SubsurfRuntimeData *BKE_subsurf_modifier_ensure_runtime(struct SubsurfModifierData *smd);
/* Return the #ModifierMode required for the evaluation of the subsurf modifier, which should be
* used to check if the modifier is enabled. */
/**
* Return the #ModifierMode required for the evaluation of the subsurf modifier,
* which should be used to check if the modifier is enabled.
*/
int BKE_subsurf_modifier_eval_required_mode(bool is_final_render, bool is_edit_mode);
#ifdef __cplusplus

View File

@ -181,6 +181,11 @@ bool BKE_volume_grid_bounds(openvdb::GridBase::ConstPtr grid,
blender::float3 &r_min,
blender::float3 &r_max);
/**
* Return a new grid pointer with only the metadata and transform changed.
* This is useful for instances, where there is a separate transform on top of the original
* grid transform that must be applied for some operations that only take a grid argument.
*/
openvdb::GridBase::ConstPtr BKE_volume_grid_shallow_transform(openvdb::GridBase::ConstPtr grid,
const blender::float4x4 &transform);

View File

@ -56,11 +56,20 @@ struct Mesh *volume_to_mesh(const openvdb::GridBase &grid,
float threshold,
float adaptivity);
/**
* Convert an OpenVDB volume grid to corresponding mesh data: vertex positions and quad and
* triangle indices.
*/
struct OpenVDBMeshData volume_to_mesh_data(const openvdb::GridBase &grid,
const VolumeToMeshResolution &resolution,
float threshold,
float adaptivity);
/**
* Convert mesh data from the format provided by OpenVDB into Blender's #Mesh data structure.
* This can be used to add mesh data from a grid into an existing mesh rather than merging multiple
* meshes later on.
*/
void fill_mesh_from_openvdb_data(const Span<openvdb::Vec3s> vdb_verts,
const Span<openvdb::Vec3I> vdb_tris,
const Span<openvdb::Vec4I> vdb_quads,

View File

@ -108,6 +108,7 @@ set(SRC
intern/bpath.c
intern/brush.c
intern/brush_channel_define.h
intern/brush_channel_define_header.h
intern/brush_engine.c
intern/brush_engine.cc
intern/brush_engine_presets.c
@ -169,6 +170,8 @@ set(SRC
intern/icons.cc
intern/icons_rasterize.c
intern/idprop.c
intern/idprop_create.cc
intern/idprop_serialize.cc
intern/idprop_utils.c
intern/idtype.c
intern/image.c
@ -204,6 +207,7 @@ set(SRC
intern/mesh.cc
intern/mesh_boolean_convert.cc
intern/mesh_convert.cc
intern/mesh_debug.cc
intern/mesh_evaluate.cc
intern/mesh_fair.cc
intern/mesh_iterators.c
@ -393,6 +397,7 @@ set(SRC
BKE_hair.h
BKE_icons.h
BKE_idprop.h
BKE_idprop.hh
BKE_idtype.h
BKE_image.h
BKE_image_save.h
@ -484,7 +489,7 @@ set(SRC
BKE_workspace.h
BKE_world.h
BKE_writeavi.h
intern/brush_channel_names.h
intern/brush_channel_names.hh
BKE_brush_engine.h
BKE_brush_engine.hh
@ -533,7 +538,7 @@ set(LIB
bf_simulation
# For `vfontdata_freetype.c`.
${FREETYPE_LIBRARY}
${FREETYPE_LIBRARIES}
)
if(WITH_BINRELOC)
@ -841,9 +846,11 @@ if(WITH_GTESTS)
intern/bpath_test.cc
intern/cryptomatte_test.cc
intern/fcurve_test.cc
intern/idprop_serialize_test.cc
intern/lattice_deform_test.cc
intern/layer_test.cc
intern/lib_id_test.cc
intern/lib_remap_test.cc
intern/tracking_test.cc
)
set(TEST_INC

View File

@ -1356,9 +1356,12 @@ static void ease_handle_axis(const float deriv1[3], const float deriv2[3], float
copy_v3_v3(r_axis, deriv1);
float len1 = len_squared_v3(deriv1), len2 = len_squared_v3(deriv2);
float ratio = len1 / len2;
const float len2 = len_squared_v3(deriv2);
if (UNLIKELY(len2 == 0.0f)) {
return;
}
const float len1 = len_squared_v3(deriv1);
const float ratio = len1 / len2;
if (ratio < gap * gap) {
madd_v3_v3fl(r_axis, deriv2, gap - sqrtf(ratio));
}

View File

@ -174,7 +174,7 @@ struct ReliefOptimizer {
/* copy mlooptri in case it's later freed */
mlooptri = new MLoopTri[totlooptri];
mlooptri = new MLoopTri[(uint)totlooptri];
for (int i = 0; i < totlooptri; i++) {
mlooptri[i] = _mlooptri[i];
}
@ -184,7 +184,7 @@ struct ReliefOptimizer {
}
arena = BLI_memarena_new(1024 * 32, "relief optimizer arena");
verts = new ReliefVertex[totvert];
verts = new ReliefVertex[(uint)totvert];
compress_ratio = 0.5f;
const MVert *mv = mvert_;

View File

@ -681,21 +681,25 @@ static void loose_data_instantiate_collection_process(
Collection *collection = (Collection *)id;
bool do_add_collection = (id->tag & LIB_TAG_DOIT) != 0;
if (!do_add_collection) {
continue;
}
/* When instantiated into view-layer, do not add collections if one of their parents is also
* instantiated. In case of empty-instantiation though, instantiation of all user-selected
* collections is the desired behavior. */
if (!do_add_collection ||
(!do_instantiate_as_empty &&
loose_data_instantiate_collection_parents_check_recursive(collection))) {
* instantiated. */
if (!do_instantiate_as_empty &&
loose_data_instantiate_collection_parents_check_recursive(collection)) {
continue;
}
/* When instantiated as empty, do not add indirectly linked (i.e. non-user-selected)
* collections. */
if (do_instantiate_as_empty && (item->tag & LINK_APPEND_TAG_INDIRECT) != 0) {
continue;
}
loose_data_instantiate_ensure_active_collection(instantiate_context);
Collection *active_collection = instantiate_context->active_collection;
/* In case user requested instantiation of collections as empties, do so for the one they
* explicitly selected (originally directly linked IDs) only. */
if (do_instantiate_as_empty && (item->tag & LINK_APPEND_TAG_INDIRECT) == 0) {
if (do_instantiate_as_empty) {
/* BKE_object_add(...) messes with the selection. */
Object *ob = BKE_object_add_only_object(bmain, OB_EMPTY, collection->id.name + 2);
ob->type = OB_EMPTY;

View File

@ -237,7 +237,7 @@ void BKE_bpath_missing_files_check(Main *bmain, ReportList *reports)
.bmain = bmain,
.callback_function = check_missing_files_foreach_path_cb,
.flag = BKE_BPATH_FOREACH_PATH_ABSOLUTE | BKE_BPATH_FOREACH_PATH_SKIP_PACKED |
BKE_BPATH_FOREACH_PATH_RESOLVE_TOKEN,
BKE_BPATH_FOREACH_PATH_RESOLVE_TOKEN | BKE_BPATH_TRAVERSE_SKIP_WEAK_REFERENCES,
.user_data = reports});
}

View File

@ -13,112 +13,7 @@ places in rna_engine_codebase are relevent:
*/
/* static name checking stuff */
#if defined(BRUSH_CHANNEL_DEFINE_TYPES) || defined(BRUSH_CHANNEL_DEFINE_EXTERNAL) || \
defined(BRUSH_CHANNEL_MAKE_NAMES)
# ifdef MAKE_FLOAT
# undef MAKE_FLOAT
# endif
# ifdef MAKE_FLOAT_EX
# undef MAKE_FLOAT_EX
# endif
# ifdef MAKE_FLOAT_EX_EX
# undef MAKE_FLOAT_EX_EX
# endif
# ifdef MAKE_FLOAT_EX_INV
# undef MAKE_FLOAT_EX_INV
# endif
# ifdef MAKE_FLOAT3
# undef MAKE_FLOAT3
# endif
# ifdef MAKE_FLOAT3_EX
# undef MAKE_FLOAT3_EX
# endif
# ifdef MAKE_INT
# undef MAKE_INT
# endif
# ifdef MAKE_INT_EX
# undef MAKE_INT_EX
# endif
# ifdef MAKE_COLOR3
# undef MAKE_COLOR3
# endif
# ifdef MAKE_COLOR4
# undef MAKE_COLOR4
# endif
# ifdef MAKE_BOOL
# undef MAKE_BOOL
# endif
# ifdef MAKE_BOOL_EX
# undef MAKE_BOOL_EX
# endif
# ifdef MAKE_ENUM
# undef MAKE_ENUM
# endif
# ifdef MAKE_FLAGS
# undef MAKE_FLAGS
# endif
# ifdef MAKE_ENUM_EX
# undef MAKE_ENUM_EX
# endif
# ifdef MAKE_FLAGS_EX
# undef MAKE_FLAGS_EX
# endif
# ifdef MAKE_CURVE
# undef MAKE_CURVE
# endif
# ifdef MAKE_CURVE_EX
# undef MAKE_CURVE_EX
# endif
# ifdef MAKE_BUILTIN_CH_DEF
# undef MAKE_BUILTIN_CH_DEF
# endif
# ifdef MAKE_FLOAT_EX_FLAG
# undef MAKE_FLOAT_EX_FLAG
# endif
# ifdef BRUSH_CHANNEL_DEFINE_TYPES
# define MAKE_BUILTIN_CH_DEF(idname) const char *BRUSH_BUILTIN_##idname = # idname;
# elif BRUSH_CHANNEL_MAKE_NAMES
# define MAKE_BUILTIN_CH_DEF(idname) #idname,
# else
# define MAKE_BUILTIN_CH_DEF(idname) extern const char *BRUSH_BUILTIN_##idname;
# endif
# define MAKE_FLOAT_EX(idname, name, tooltip, val, min, max, smin, smax, pressure_enabled) \
MAKE_BUILTIN_CH_DEF(idname)
# define MAKE_FLOAT_EX_FLAG( \
idname, name, tooltip, val, min, max, smin, smax, pressure_enabled, flag) \
MAKE_BUILTIN_CH_DEF(idname)
# define MAKE_FLOAT_EX_INV(idname, name, tooltip, val, min, max, smin, smax, pressure_enabled) \
MAKE_BUILTIN_CH_DEF(idname)
# define MAKE_FLOAT_EX_EX( \
idname, name, tooltip, val, min, max, smin, smax, pressure_enabled, inv, flag) \
MAKE_BUILTIN_CH_DEF(idname)
# define MAKE_FLOAT(idname, name, tooltip, val, min, max) MAKE_BUILTIN_CH_DEF(idname);
# define MAKE_INT_EX(idname, name, tooltip, val, min, max, smin, smax) \
MAKE_BUILTIN_CH_DEF(idname);
# define MAKE_INT(idname, name, tooltip, val, min, max) MAKE_BUILTIN_CH_DEF(idname);
# define MAKE_BOOL(idname, name, tooltip, val) MAKE_BUILTIN_CH_DEF(idname);
# define MAKE_BOOL_EX(idname, name, tooltip, val, flag) MAKE_BUILTIN_CH_DEF(idname);
# define MAKE_COLOR3(idname, name, tooltip, r, g, b) MAKE_BUILTIN_CH_DEF(idname);
# define MAKE_COLOR4(idname, name, tooltip, r, g, b, a) MAKE_BUILTIN_CH_DEF(idname);
# define MAKE_FLOAT3(idname, name, tooltip, x, y, z, min, max) MAKE_BUILTIN_CH_DEF(idname);
# define MAKE_FLOAT3_EX(idname, name, tooltip, x, y, z, min, max, smin, smax, flag) \
MAKE_BUILTIN_CH_DEF(idname);
# define MAKE_ENUM(idname1, name1, tooltip1, value1, enumdef1, ...) MAKE_BUILTIN_CH_DEF(idname1);
# define MAKE_ENUM_EX(idname1, name1, tooltip1, value1, flag1, enumdef1, ...) \
MAKE_BUILTIN_CH_DEF(idname1);
# define MAKE_FLAGS(idname1, name1, tooltip1, value1, enumdef1, ...) MAKE_BUILTIN_CH_DEF(idname1);
# define MAKE_FLAGS_EX(idname1, name1, tooltip1, value1, flag1, enumdef1, ...) \
MAKE_BUILTIN_CH_DEF(idname1);
# define MAKE_CURVE(idname1, name1, tooltip1, preset1) MAKE_BUILTIN_CH_DEF(idname1);
# define MAKE_CURVE_EX(idname1, name1, tooltip1, preset1, flag, preset_slope_neg) \
MAKE_BUILTIN_CH_DEF(idname1);
#else
#endif
#include "brush_channel_define_header.h"
/* clang-format off */
MAKE_FLOAT_EX(radius,"Radius","Radius of the brush in pixels",50.0f,0.5f,MAX_BRUSH_PIXEL_RADIUS * 10,0.5,MAX_BRUSH_PIXEL_RADIUS,false)

View File

@ -0,0 +1,163 @@
/* check if we need to undef various macros */
#if defined(BRUSH_CHANNEL_MAKE_CPP_LOOKUPS) || defined(BRUSH_CHANNEL_DEFINE_TYPES) || \
defined(BRUSH_CHANNEL_DEFINE_EXTERNAL) || defined(BRUSH_CHANNEL_MAKE_NAMES)
# ifdef MAKE_FLOAT
# undef MAKE_FLOAT
# endif
# ifdef MAKE_FLOAT_EX
# undef MAKE_FLOAT_EX
# endif
# ifdef MAKE_FLOAT_EX_EX
# undef MAKE_FLOAT_EX_EX
# endif
# ifdef MAKE_FLOAT_EX_INV
# undef MAKE_FLOAT_EX_INV
# endif
# ifdef MAKE_FLOAT3
# undef MAKE_FLOAT3
# endif
# ifdef MAKE_FLOAT3_EX
# undef MAKE_FLOAT3_EX
# endif
# ifdef MAKE_INT
# undef MAKE_INT
# endif
# ifdef MAKE_INT_EX
# undef MAKE_INT_EX
# endif
# ifdef MAKE_COLOR3
# undef MAKE_COLOR3
# endif
# ifdef MAKE_COLOR4
# undef MAKE_COLOR4
# endif
# ifdef MAKE_BOOL
# undef MAKE_BOOL
# endif
# ifdef MAKE_BOOL_EX
# undef MAKE_BOOL_EX
# endif
# ifdef MAKE_ENUM
# undef MAKE_ENUM
# endif
# ifdef MAKE_FLAGS
# undef MAKE_FLAGS
# endif
# ifdef MAKE_ENUM_EX
# undef MAKE_ENUM_EX
# endif
# ifdef MAKE_FLAGS_EX
# undef MAKE_FLAGS_EX
# endif
# ifdef MAKE_CURVE
# undef MAKE_CURVE
# endif
# ifdef MAKE_CURVE_EX
# undef MAKE_CURVE_EX
# endif
# ifdef MAKE_BUILTIN_CH_DEF
# undef MAKE_BUILTIN_CH_DEF
# endif
# ifdef MAKE_FLOAT_EX_FLAG
# undef MAKE_FLOAT_EX_FLAG
# endif
# ifdef MAKE_BUILTIN_CH_DEF
# undef MAKE_BUILTIN_CH_DEF
# endif
#endif
#ifdef BRUSH_CHANNEL_MAKE_CPP_LOOKUPS
# define MAKE_BUILTIN_CH_DEF(idname, type) \
BrushChannelIF<type> idname() \
{ \
return lookup<type>(#idname); \
}
// BrushChannel *ch = lookup(#idname); \
// return BrushChannelIF<type>(ch); \
// }
# define MAKE_FLOAT_EX(idname, name, tooltip, val, min, max, smin, smax, pressure_enabled) \
MAKE_BUILTIN_CH_DEF(idname, float)
# define MAKE_FLOAT_EX_FLAG( \
idname, name, tooltip, val, min, max, smin, smax, pressure_enabled, flag) \
MAKE_BUILTIN_CH_DEF(idname, float)
# define MAKE_FLOAT_EX_INV(idname, name, tooltip, val, min, max, smin, smax, pressure_enabled) \
MAKE_BUILTIN_CH_DEF(idname, float)
# define MAKE_FLOAT_EX_EX( \
idname, name, tooltip, val, min, max, smin, smax, pressure_enabled, inv, flag) \
MAKE_BUILTIN_CH_DEF(idname, float)
# define MAKE_FLOAT(idname, name, tooltip, val, min, max) MAKE_BUILTIN_CH_DEF(idname, float);
# define MAKE_INT_EX(idname, name, tooltip, val, min, max, smin, smax) \
MAKE_BUILTIN_CH_DEF(idname, int);
# define MAKE_INT(idname, name, tooltip, val, min, max) MAKE_BUILTIN_CH_DEF(idname, int);
# define MAKE_BOOL(idname, name, tooltip, val) MAKE_BUILTIN_CH_DEF(idname, bool);
# define MAKE_BOOL_EX(idname, name, tooltip, val, flag) MAKE_BUILTIN_CH_DEF(idname, bool);
# define MAKE_COLOR3(idname, name, tooltip, r, g, b) MAKE_BUILTIN_CH_DEF(idname, float[3]);
# define MAKE_COLOR4(idname, name, tooltip, r, g, b, a) MAKE_BUILTIN_CH_DEF(idname, float[4]);
# define MAKE_FLOAT3(idname, name, tooltip, x, y, z, min, max) \
MAKE_BUILTIN_CH_DEF(idname, float[3]);
# define MAKE_FLOAT3_EX(idname, name, tooltip, x, y, z, min, max, smin, smax, flag) \
MAKE_BUILTIN_CH_DEF(idname, float[3]);
# define MAKE_ENUM(idname1, name1, tooltip1, value1, enumdef1, ...) \
MAKE_BUILTIN_CH_DEF(idname1, int);
# define MAKE_ENUM_EX(idname1, name1, tooltip1, value1, flag1, enumdef1, ...) \
MAKE_BUILTIN_CH_DEF(idname1, int);
# define MAKE_FLAGS(idname1, name1, tooltip1, value1, enumdef1, ...) \
MAKE_BUILTIN_CH_DEF(idname1, int);
# define MAKE_FLAGS_EX(idname1, name1, tooltip1, value1, flag1, enumdef1, ...) \
MAKE_BUILTIN_CH_DEF(idname1, int);
# define MAKE_CURVE(idname1, name1, tooltip1, preset1) MAKE_BUILTIN_CH_DEF(idname1, BrushCurve);
# define MAKE_CURVE_EX(idname1, name1, tooltip1, preset1, flag, preset_slope_neg) \
MAKE_BUILTIN_CH_DEF(idname1, BrushCurve);
/* static name checking stuff */
#elif defined(BRUSH_CHANNEL_DEFINE_TYPES) || defined(BRUSH_CHANNEL_DEFINE_EXTERNAL) || \
defined(BRUSH_CHANNEL_MAKE_NAMES)
# ifdef BRUSH_CHANNEL_DEFINE_TYPES
# define MAKE_BUILTIN_CH_DEF(idname) const char *BRUSH_BUILTIN_##idname = # idname;
# elif defined(BRUSH_CHANNEL_MAKE_NAMES)
# define MAKE_BUILTIN_CH_DEF(idname) # idname,
# else
# define MAKE_BUILTIN_CH_DEF(idname) extern const char *BRUSH_BUILTIN_##idname;
# endif
# define MAKE_FLOAT_EX(idname, name, tooltip, val, min, max, smin, smax, pressure_enabled) \
MAKE_BUILTIN_CH_DEF(idname)
# define MAKE_FLOAT_EX_FLAG( \
idname, name, tooltip, val, min, max, smin, smax, pressure_enabled, flag) \
MAKE_BUILTIN_CH_DEF(idname)
# define MAKE_FLOAT_EX_INV(idname, name, tooltip, val, min, max, smin, smax, pressure_enabled) \
MAKE_BUILTIN_CH_DEF(idname)
# define MAKE_FLOAT_EX_EX( \
idname, name, tooltip, val, min, max, smin, smax, pressure_enabled, inv, flag) \
MAKE_BUILTIN_CH_DEF(idname)
# define MAKE_FLOAT(idname, name, tooltip, val, min, max) MAKE_BUILTIN_CH_DEF(idname)
# define MAKE_INT_EX(idname, name, tooltip, val, min, max, smin, smax) MAKE_BUILTIN_CH_DEF(idname)
# define MAKE_INT(idname, name, tooltip, val, min, max) MAKE_BUILTIN_CH_DEF(idname)
# define MAKE_BOOL(idname, name, tooltip, val) MAKE_BUILTIN_CH_DEF(idname)
# define MAKE_BOOL_EX(idname, name, tooltip, val, flag) MAKE_BUILTIN_CH_DEF(idname)
# define MAKE_COLOR3(idname, name, tooltip, r, g, b) MAKE_BUILTIN_CH_DEF(idname)
# define MAKE_COLOR4(idname, name, tooltip, r, g, b, a) MAKE_BUILTIN_CH_DEF(idname)
# define MAKE_FLOAT3(idname, name, tooltip, x, y, z, min, max) MAKE_BUILTIN_CH_DEF(idname)
# define MAKE_FLOAT3_EX(idname, name, tooltip, x, y, z, min, max, smin, smax, flag) \
MAKE_BUILTIN_CH_DEF(idname)
# define MAKE_ENUM(idname1, name1, tooltip1, value1, enumdef1, ...) MAKE_BUILTIN_CH_DEF(idname1)
# define MAKE_ENUM_EX(idname1, name1, tooltip1, value1, flag1, enumdef1, ...) \
MAKE_BUILTIN_CH_DEF(idname1)
# define MAKE_FLAGS(idname1, name1, tooltip1, value1, enumdef1, ...) MAKE_BUILTIN_CH_DEF(idname1)
# define MAKE_FLAGS_EX(idname1, name1, tooltip1, value1, flag1, enumdef1, ...) \
MAKE_BUILTIN_CH_DEF(idname1)
# define MAKE_CURVE(idname1, name1, tooltip1, preset1) MAKE_BUILTIN_CH_DEF(idname1)
# define MAKE_CURVE_EX(idname1, name1, tooltip1, preset1, flag, preset_slope_neg) \
MAKE_BUILTIN_CH_DEF(idname1)
#else
#endif

View File

@ -2,7 +2,11 @@
#define BRUSH_CHANNEL_MAKE_NAMES
static std::basic_string<char*> brush_channel_idnames[] = {
#ifdef BRUSH_CHANNEL_DEFINE_TYPES
# undef BRUSH_CHANNEL_DEFINE_TYPES
#endif
static std::basic_string<char> brush_channel_idnames[] = {
#include "intern/brush_channel_define.h"
};

View File

@ -1191,8 +1191,8 @@ bool BKE_brush_mapping_is_enabled(BrushChannel *child, BrushChannel *parent, int
}
void BKE_brush_channel_apply_mapping_flags(BrushChannel *dst,
BrushChannel *child,
BrushChannel *parent)
const BrushChannel *child,
const BrushChannel *parent)
{
for (int i = 0; i < BRUSH_MAPPING_MAX; i++) {
BrushMapping *mp = dst->mappings + i;
@ -1289,7 +1289,7 @@ void BKE_brush_channelset_set_final_int(BrushChannelSet *child,
BKE_brush_channel_set_int(ch, value);
}
float BKE_brush_channelset_get_final_float(BrushChannelSet *child,
float old_BKE_brush_channelset_get_final_float(BrushChannelSet *child,
BrushChannelSet *parent,
const char *idname,
BrushMappingData *mapdata)

View File

@ -1,2 +1,37 @@
#if 1
#include "BKE_brush_engine.h"
#include "BKE_brush_engine.hh"
#include "DNA_sculpt_brush_types.h"
using BrushChannelSetIF = blender::brush::BrushChannelSetIF;
using BrushChannelFloat = blender::brush::BrushChannelIF<float>;
float BKE_brush_channelset_get_final_float(BrushChannelSet *child,
BrushChannelSet *parent,
const char *idname,
BrushMappingData *mapdata)
{
BrushChannelSetIF chset_child(child);
BrushChannelSetIF chset_parent(parent);
if (!child && parent) {
BrushChannelFloat ch = chset_parent.lookup<float>(idname);
return ch.isValid() ? ch.evaluate(mapdata) : 0.0f;
}
else if (!parent) {
BrushChannelFloat ch = chset_child.lookup<float>(idname);
return ch.isValid() ? ch.evaluate(mapdata) : 0.0f;
}
BrushChannelFloat ch = chset_child.lookup<float>(idname);
return chset_child.getFinalValue<float>(chset_parent, ch, mapdata);
}
namespace blender {
namespace brush {
} // namespace brush
} // namespace blender
#endif

View File

@ -54,6 +54,8 @@
#include "BLO_read_write.h"
#include "MEM_guardedalloc.h"
#ifdef WITH_ALEMBIC
# include "ABC_alembic.h"
#endif
@ -86,6 +88,7 @@ static void cache_file_copy_data(Main *UNUSED(bmain),
cache_file_dst->handle = NULL;
cache_file_dst->handle_readers = NULL;
BLI_duplicatelist(&cache_file_dst->object_paths, &cache_file_src->object_paths);
BLI_duplicatelist(&cache_file_dst->layers, &cache_file_src->layers);
}
static void cache_file_free_data(ID *id)
@ -93,6 +96,7 @@ static void cache_file_free_data(ID *id)
CacheFile *cache_file = (CacheFile *)id;
cachefile_handle_free(cache_file);
BLI_freelistN(&cache_file->object_paths);
BLI_freelistN(&cache_file->layers);
}
static void cache_file_foreach_path(ID *id, BPathForeachPathData *bpath_data)
@ -117,6 +121,11 @@ static void cache_file_blend_write(BlendWriter *writer, ID *id, const void *id_a
if (cache_file->adt) {
BKE_animdata_blend_write(writer, cache_file->adt);
}
/* write layers */
LISTBASE_FOREACH (CacheFileLayer *, layer, &cache_file->layers) {
BLO_write_struct(writer, CacheFileLayer, layer);
}
}
static void cache_file_blend_read_data(BlendDataReader *reader, ID *id)
@ -130,6 +139,9 @@ static void cache_file_blend_read_data(BlendDataReader *reader, ID *id)
/* relink animdata */
BLO_read_data_address(reader, &cache_file->adt);
BKE_animdata_blend_read_data(reader, cache_file->adt);
/* relink layers */
BLO_read_list(reader, &cache_file->layers);
}
IDTypeInfo IDType_ID_CF = {
@ -364,7 +376,8 @@ void BKE_cachefile_eval(Main *bmain, Depsgraph *depsgraph, CacheFile *cache_file
#ifdef WITH_ALEMBIC
if (BLI_path_extension_check_glob(filepath, "*abc")) {
cache_file->type = CACHEFILE_TYPE_ALEMBIC;
cache_file->handle = ABC_create_handle(bmain, filepath, &cache_file->object_paths);
cache_file->handle = ABC_create_handle(
bmain, filepath, cache_file->layers.first, &cache_file->object_paths);
BLI_strncpy(cache_file->handle_filepath, filepath, FILE_MAX);
}
#endif
@ -435,3 +448,35 @@ bool BKE_cache_file_uses_render_procedural(const CacheFile *cache_file,
const bool is_final_render = (eEvaluationMode)dag_eval_mode == DAG_EVAL_RENDER;
return cache_file->use_render_procedural && !is_final_render;
}
CacheFileLayer *BKE_cachefile_add_layer(CacheFile *cache_file, const char filename[1024])
{
for (CacheFileLayer *layer = cache_file->layers.first; layer; layer = layer->next) {
if (STREQ(layer->filepath, filename)) {
return NULL;
}
}
const int num_layers = BLI_listbase_count(&cache_file->layers);
CacheFileLayer *layer = MEM_callocN(sizeof(CacheFileLayer), "CacheFileLayer");
BLI_strncpy(layer->filepath, filename, sizeof(layer->filepath));
BLI_addtail(&cache_file->layers, layer);
cache_file->active_layer = (char)(num_layers + 1);
return layer;
}
CacheFileLayer *BKE_cachefile_get_active_layer(CacheFile *cache_file)
{
return BLI_findlink(&cache_file->layers, cache_file->active_layer - 1);
}
void BKE_cachefile_remove_layer(CacheFile *cache_file, CacheFileLayer *layer)
{
cache_file->active_layer = 0;
BLI_remlink(&cache_file->layers, layer);
MEM_freeN(layer);
}

View File

@ -30,6 +30,7 @@
#include "BLI_blenlib.h"
#include "BLI_endian_switch.h"
#include "BLI_ghash.h"
#include "BLI_index_range.hh"
#include "BLI_math.h"
#include "BLI_utildefines.h"
@ -67,10 +68,12 @@
#include "BLO_read_write.h"
using blender::IndexRange;
/* globals */
/* local */
static CLG_LogRef LOG = {"bke.curve"};
// static CLG_LogRef LOG = {"bke.curve"};
static void curve_init_data(ID *id)
{
@ -1160,81 +1163,34 @@ void BKE_nurb_bpoint_calc_plane(struct Nurb *nu, BPoint *bp, float r_plane[3])
static void calcknots(float *knots, const int pnts, const short order, const short flag)
{
/* knots: number of pnts NOT corrected for cyclic */
const int pnts_order = pnts + order;
float k;
int a;
const bool is_cyclic = flag & CU_NURB_CYCLIC;
const bool is_bezier = flag & CU_NURB_BEZIER && !(flag & CU_NURB_ENDPOINT);
const bool is_end_point = flag & CU_NURB_ENDPOINT && !(flag & CU_NURB_BEZIER);
/* Inner knots are always repeated once except on Bezier case. */
const int repeat_inner = is_bezier ? order - 1 : 1;
/* How many times to repeat 0.0 at the beginning of knot. */
const int head = is_end_point && !is_cyclic ? order : (is_bezier ? order / 2 : 1);
/* Number of knots replicating widths of the starting knots.
* Covers both Cyclic and EndPoint cases. */
const int tail = is_cyclic ? 2 * order - 1 : (is_end_point ? order : 0);
switch (flag & (CU_NURB_ENDPOINT | CU_NURB_BEZIER)) {
case CU_NURB_ENDPOINT:
k = 0.0;
for (a = 1; a <= pnts_order; a++) {
knots[a - 1] = k;
if (a >= order && a <= pnts) {
k += 1.0f;
}
}
break;
case CU_NURB_BEZIER:
/* Warning, the order MUST be 2 or 4,
* if this is not enforced, the displist will be corrupt */
if (order == 4) {
k = 0.34;
for (a = 0; a < pnts_order; a++) {
knots[a] = floorf(k);
k += (1.0f / 3.0f);
}
}
else if (order == 3) {
k = 0.6f;
for (a = 0; a < pnts_order; a++) {
if (a >= order && a <= pnts) {
k += 0.5f;
}
knots[a] = floorf(k);
}
}
else {
CLOG_ERROR(&LOG, "bez nurb curve order is not 3 or 4, should never happen");
}
break;
default:
for (a = 0; a < pnts_order; a++) {
knots[a] = (float)a;
}
break;
}
}
const int knot_count = pnts + order + (is_cyclic ? order - 1 : 0);
static void makecyclicknots(float *knots, int pnts, short order)
/* pnts, order: number of pnts NOT corrected for cyclic */
{
int a, b, order2, c;
int r = head;
float current = 0.0f;
if (knots == nullptr) {
return;
}
order2 = order - 1;
/* do first long rows (order -1), remove identical knots at endpoints */
if (order > 2) {
b = pnts + order2;
for (a = 1; a < order2; a++) {
if (knots[b] != knots[b - a]) {
break;
}
}
if (a == order2) {
knots[pnts + order - 2] += 1.0f;
for (const int i : IndexRange(knot_count - tail)) {
knots[i] = current;
r--;
if (r == 0) {
current += 1.0;
r = repeat_inner;
}
}
b = order;
c = pnts + order + order2;
for (a = pnts + order2; a < c; a++) {
knots[a] = knots[a - 1] + (knots[b] - knots[b - 1]);
b--;
const int tail_index = knot_count - tail;
for (const int i : IndexRange(tail)) {
knots[tail_index + i] = current + (knots[i] - knots[0]);
}
}
@ -1247,13 +1203,7 @@ static void makeknots(Nurb *nu, short uv)
}
if (BKE_nurb_check_valid_u(nu)) {
nu->knotsu = (float *)MEM_calloc_arrayN(KNOTSU(nu) + 1, sizeof(float), "makeknots");
if (nu->flagu & CU_NURB_CYCLIC) {
calcknots(nu->knotsu, nu->pntsu, nu->orderu, 0); /* cyclic should be uniform */
makecyclicknots(nu->knotsu, nu->pntsu, nu->orderu);
}
else {
calcknots(nu->knotsu, nu->pntsu, nu->orderu, nu->flagu);
}
calcknots(nu->knotsu, nu->pntsu, nu->orderu, nu->flagu);
}
else {
nu->knotsu = nullptr;
@ -1265,13 +1215,7 @@ static void makeknots(Nurb *nu, short uv)
}
if (BKE_nurb_check_valid_v(nu)) {
nu->knotsv = (float *)MEM_calloc_arrayN(KNOTSV(nu) + 1, sizeof(float), "makeknots");
if (nu->flagv & CU_NURB_CYCLIC) {
calcknots(nu->knotsv, nu->pntsv, nu->orderv, 0); /* cyclic should be uniform */
makecyclicknots(nu->knotsv, nu->pntsv, nu->orderv);
}
else {
calcknots(nu->knotsv, nu->pntsv, nu->orderv, nu->flagv);
}
calcknots(nu->knotsv, nu->pntsv, nu->orderv, nu->flagv);
}
else {
nu->knotsv = nullptr;

View File

@ -46,6 +46,10 @@
#include "BLI_string_utils.h"
#include "BLI_utildefines.h"
#ifndef NDEBUG
# include "BLI_dynstr.h"
#endif
#include "BLT_translation.h"
#include "BKE_anonymous_attribute.h"
@ -1804,7 +1808,9 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
/* 29: CD_BWEIGHT */
{sizeof(float), "", 0, N_("BevelWeight"), nullptr, nullptr, layerInterp_bweight},
/* 30: CD_CREASE */
{sizeof(float), "", 0, N_("SubSurfCrease"), nullptr, nullptr, layerInterp_bweight},
/* NOTE: we do not interpolate crease data as it should be either inherited for subdivided
* edges, or for vertex creases, only present on the original vertex. */
{sizeof(float), "", 0, N_("SubSurfCrease"), nullptr, nullptr, nullptr},
/* 31: CD_ORIGSPACE_MLOOP */
{sizeof(OrigSpaceLoop),
"OrigSpaceLoop",
@ -2088,7 +2094,7 @@ const CustomData_MeshMasks CD_MASK_BAREMESH_ORIGINDEX = {
};
const CustomData_MeshMasks CD_MASK_MESH = {
/* vmask */ (CD_MASK_MVERT | CD_MASK_MDEFORMVERT | CD_MASK_MVERT_SKIN | CD_MASK_PAINT_MASK |
CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR | CD_MASK_MESH_ID),
CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR | CD_MASK_CREASE | CD_MASK_MESH_ID),
/* emask */ (CD_MASK_MEDGE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL | CD_MASK_MESH_ID),
/* fmask */ 0,
/* pmask */
@ -2100,7 +2106,8 @@ const CustomData_MeshMasks CD_MASK_MESH = {
};
const CustomData_MeshMasks CD_MASK_EDITMESH = {
/* vmask */ (CD_MASK_MDEFORMVERT | CD_MASK_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_SHAPEKEY |
CD_MASK_SHAPE_KEYINDEX | CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR | CD_MASK_MESH_ID),
CD_MASK_SHAPE_KEYINDEX | CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR | CD_MASK_CREASE |
CD_MASK_MESH_ID),
/* emask */ (CD_MASK_PROP_ALL | CD_MASK_MESH_ID),
/* fmask */ 0,
/* pmask */ (CD_MASK_FACEMAP | CD_MASK_PROP_ALL | CD_MASK_SCULPT_FACE_SETS | CD_MASK_MESH_ID),
@ -2111,7 +2118,7 @@ const CustomData_MeshMasks CD_MASK_EDITMESH = {
const CustomData_MeshMasks CD_MASK_DERIVEDMESH = {
/* vmask */ (CD_MASK_ORIGINDEX | CD_MASK_MDEFORMVERT | CD_MASK_SHAPEKEY | CD_MASK_MVERT_SKIN |
CD_MASK_PAINT_MASK | CD_MASK_ORCO | CD_MASK_CLOTH_ORCO | CD_MASK_PROP_ALL |
CD_MASK_PROP_COLOR | CD_MASK_MESH_ID),
CD_MASK_PROP_COLOR | CD_MASK_CREASE | CD_MASK_MESH_ID),
/* emask */ (CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL | CD_MASK_MESH_ID),
/* fmask */ (CD_MASK_ORIGINDEX | CD_MASK_ORIGSPACE | CD_MASK_PREVIEW_MCOL | CD_MASK_TANGENT),
/* pmask */
@ -2125,7 +2132,7 @@ const CustomData_MeshMasks CD_MASK_DERIVEDMESH = {
const CustomData_MeshMasks CD_MASK_BMESH = {
/* vmask */ (CD_MASK_MDEFORMVERT | CD_MASK_BWEIGHT | CD_MASK_MVERT_SKIN | CD_MASK_SHAPEKEY |
CD_MASK_SHAPE_KEYINDEX | CD_MASK_PAINT_MASK | CD_MASK_PROP_ALL |
CD_MASK_PROP_COLOR | CD_MASK_MESH_ID | CD_MASK_DYNTOPO_VERT),
CD_MASK_PROP_COLOR | CD_MASK_CREASE | CD_MASK_MESH_ID | CD_MASK_DYNTOPO_VERT),
/* emask */
(CD_MASK_BWEIGHT | CD_MASK_CREASE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL |
CD_MASK_MESH_ID),
@ -2155,7 +2162,7 @@ const CustomData_MeshMasks CD_MASK_EVERYTHING = {
/* vmask */ (CD_MASK_MVERT | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_NORMAL |
CD_MASK_MDEFORMVERT | CD_MASK_BWEIGHT | CD_MASK_MVERT_SKIN | CD_MASK_ORCO |
CD_MASK_CLOTH_ORCO | CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX |
CD_MASK_PAINT_MASK | CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR),
CD_MASK_PAINT_MASK | CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR | CD_MASK_CREASE),
/* emask */
(CD_MASK_MEDGE | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_BWEIGHT | CD_MASK_CREASE |
CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL),
@ -2371,7 +2378,8 @@ void CustomData_copy_all_layout(const struct CustomData *source, struct CustomDa
}
if (source->layers) {
dest->layers = static_cast<CustomDataLayer*>(MEM_mallocN(sizeof(*dest->layers) * source->totlayer, __func__));
dest->layers = static_cast<CustomDataLayer *>(
MEM_mallocN(sizeof(*dest->layers) * source->totlayer, __func__));
for (int i = 0; i < source->totlayer; i++) {
dest->layers[i] = source->layers[i];
@ -5046,7 +5054,12 @@ bool CustomData_verify_versions(struct CustomData *data, int index)
/* 0 structnum is used in writing code to tag layer types that should not be written. */
else if (typeInfo->structnum == 0 &&
/* XXX Not sure why those three are exception, maybe that should be fixed? */
!ELEM(layer->type, CD_PAINT_MASK, CD_FACEMAP, CD_MTEXPOLY, CD_SCULPT_FACE_SETS)) {
!ELEM(layer->type,
CD_PAINT_MASK,
CD_FACEMAP,
CD_MTEXPOLY,
CD_SCULPT_FACE_SETS,
CD_CREASE)) {
keeplayer = false;
CLOG_WARN(&LOG, ".blend file read: removing a data layer that should not have been written");
}
@ -5717,6 +5730,10 @@ void CustomData_blend_write(BlendWriter *writer,
const bool *layer_data = static_cast<const bool *>(layer->data);
BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data);
}
else if (layer->type == CD_CREASE) {
const float *layer_data = static_cast<const float *>(layer->data);
BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data);
}
else {
const char *structname;
int structnum;
@ -5830,3 +5847,33 @@ void CustomData_blend_read(BlendDataReader *reader, CustomData *data, int count)
CustomData_update_typemap(data);
CustomData_regen_active_refs(data); // check for corrupted active layer refs
}
#ifndef NDEBUG
void CustomData_debug_info_from_layers(const CustomData *data, const char *indent, DynStr *dynstr)
{
for (int type = 0; type < CD_NUMTYPES; type++) {
if (CustomData_has_layer(data, type)) {
/* NOTE: doesn't account for multiple layers. */
const char *name = CustomData_layertype_name(type);
const int size = CustomData_sizeof(type);
const void *pt = CustomData_get_layer(data, type);
const int pt_size = pt ? (int)(MEM_allocN_len(pt) / size) : 0;
const char *structname;
int structnum;
CustomData_file_write_info(type, &structname, &structnum);
BLI_dynstr_appendf(
dynstr,
"%sdict(name='%s', struct='%s', type=%d, ptr='%p', elem=%d, length=%d),\n",
indent,
name,
structname,
type,
(const void *)pt,
size,
pt_size);
}
}
}
#endif /* NDEBUG */

View File

@ -15,6 +15,7 @@
*/
#include "BLI_listbase.h"
#include "BLI_task.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@ -266,89 +267,54 @@ static GVArray adapt_mesh_domain_corner_to_point(const Mesh &mesh, const GVArray
/**
* Each corner's value is simply a copy of the value at its vertex.
*
* \note Theoretically this interpolation does not need to compute all values at once.
* However, doing that makes the implementation simpler, and this can be optimized in the future if
* only some values are required.
*/
template<typename T>
static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh,
const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totloop);
for (const int loop_index : IndexRange(mesh.totloop)) {
const int vertex_index = mesh.mloop[loop_index].v;
r_values[loop_index] = old_values[vertex_index];
}
}
static GVArray adapt_mesh_domain_point_to_corner(const Mesh &mesh, const GVArray &varray)
{
GVArray new_varray;
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
using T = decltype(dummy);
Array<T> values(mesh.totloop);
adapt_mesh_domain_point_to_corner_impl<T>(mesh, varray.typed<T>(), values);
new_varray = VArray<T>::ForContainer(std::move(values));
new_varray = VArray<T>::ForFunc(mesh.totloop,
[mesh, varray = varray.typed<T>()](const int64_t loop_index) {
const int vertex_index = mesh.mloop[loop_index].v;
return varray[vertex_index];
});
});
return new_varray;
}
/**
* \note Theoretically this interpolation does not need to compute all values at once.
* However, doing that makes the implementation simpler, and this can be optimized in the future if
* only some values are required.
*/
template<typename T>
static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
attribute_math::DefaultMixer<T> mixer(r_values);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const T value = old_values[loop_index];
mixer.mix_in(poly_index, value);
}
}
mixer.finalize();
}
/* A face is selected if all of its corners were selected. */
template<>
void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
const VArray<bool> &old_values,
MutableSpan<bool> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
r_values.fill(true);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
if (!old_values[loop_index]) {
r_values[poly_index] = false;
break;
}
}
}
}
static GVArray adapt_mesh_domain_corner_to_face(const Mesh &mesh, const GVArray &varray)
{
GVArray new_varray;
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totpoly);
adapt_mesh_domain_corner_to_face_impl<T>(mesh, varray.typed<T>(), values);
new_varray = VArray<T>::ForContainer(std::move(values));
if constexpr (std::is_same_v<T, bool>) {
new_varray = VArray<T>::ForFunc(
mesh.totpoly, [mesh, varray = varray.typed<bool>()](const int face_index) {
/* A face is selected if all of its corners were selected. */
const MPoly &poly = mesh.mpoly[face_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
if (!varray[loop_index]) {
return false;
}
}
return true;
});
}
else {
new_varray = VArray<T>::ForFunc(
mesh.totpoly, [mesh, varray = varray.typed<T>()](const int face_index) {
T return_value;
attribute_math::DefaultMixer<T> mixer({&return_value, 1});
const MPoly &poly = mesh.mpoly[face_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const T value = varray[loop_index];
mixer.mix_in(0, value);
}
mixer.finalize();
return return_value;
});
}
}
});
return new_varray;
@ -406,11 +372,13 @@ void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
}
/* Deselect loose edges without corners that are still selected from the 'true' default. */
for (const int edge_index : IndexRange(mesh.totedge)) {
if (loose_edges[edge_index]) {
r_values[edge_index] = false;
threading::parallel_for(IndexRange(mesh.totedge), 2048, [&](const IndexRange range) {
for (const int edge_index : range) {
if (loose_edges[edge_index]) {
r_values[edge_index] = false;
}
}
}
});
}
static GVArray adapt_mesh_domain_corner_to_edge(const Mesh &mesh, const GVArray &varray)
@ -491,11 +459,13 @@ void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
{
BLI_assert(r_values.size() == mesh.totloop);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
MutableSpan<T> poly_corner_values = r_values.slice(poly.loopstart, poly.totloop);
poly_corner_values.fill(old_values[poly_index]);
}
threading::parallel_for(IndexRange(mesh.totpoly), 1024, [&](const IndexRange range) {
for (const int poly_index : range) {
const MPoly &poly = mesh.mpoly[poly_index];
MutableSpan<T> poly_corner_values = r_values.slice(poly.loopstart, poly.totloop);
poly_corner_values.fill(old_values[poly_index]);
}
});
}
static GVArray adapt_mesh_domain_face_to_corner(const Mesh &mesh, const GVArray &varray)
@ -566,111 +536,72 @@ static GVArray adapt_mesh_domain_face_to_edge(const Mesh &mesh, const GVArray &v
return new_varray;
}
/**
* \note Theoretically this interpolation does not need to compute all values at once.
* However, doing that makes the implementation simpler, and this can be optimized in the future if
* only some values are required.
*/
template<typename T>
static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
attribute_math::DefaultMixer<T> mixer(r_values);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
MLoop &loop = mesh.mloop[loop_index];
const int point_index = loop.v;
mixer.mix_in(poly_index, old_values[point_index]);
}
}
mixer.finalize();
}
/* A face is selected if all of its vertices were selected too. */
template<>
void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
const VArray<bool> &old_values,
MutableSpan<bool> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
r_values.fill(true);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
MLoop &loop = mesh.mloop[loop_index];
const int vert_index = loop.v;
if (!old_values[vert_index]) {
r_values[poly_index] = false;
break;
}
}
}
}
static GVArray adapt_mesh_domain_point_to_face(const Mesh &mesh, const GVArray &varray)
{
GVArray new_varray;
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totpoly);
adapt_mesh_domain_point_to_face_impl<T>(mesh, varray.typed<T>(), values);
new_varray = VArray<T>::ForContainer(std::move(values));
if constexpr (std::is_same_v<T, bool>) {
new_varray = VArray<T>::ForFunc(
mesh.totpoly, [mesh, varray = varray.typed<bool>()](const int face_index) {
/* A face is selected if all of its vertices were selected. */
const MPoly &poly = mesh.mpoly[face_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
if (!varray[loop.v]) {
return false;
}
}
return true;
});
}
else {
new_varray = VArray<T>::ForFunc(
mesh.totpoly, [mesh, varray = varray.typed<T>()](const int face_index) {
T return_value;
attribute_math::DefaultMixer<T> mixer({&return_value, 1});
const MPoly &poly = mesh.mpoly[face_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
const T value = varray[loop.v];
mixer.mix_in(0, value);
}
mixer.finalize();
return return_value;
});
}
}
});
return new_varray;
}
/**
* \note Theoretically this interpolation does not need to compute all values at once.
* However, doing that makes the implementation simpler, and this can be optimized in the future if
* only some values are required.
*/
template<typename T>
static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
attribute_math::DefaultMixer<T> mixer(r_values);
for (const int edge_index : IndexRange(mesh.totedge)) {
const MEdge &edge = mesh.medge[edge_index];
mixer.mix_in(edge_index, old_values[edge.v1]);
mixer.mix_in(edge_index, old_values[edge.v2]);
}
mixer.finalize();
}
/* An edge is selected if both of its vertices were selected. */
template<>
void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
const VArray<bool> &old_values,
MutableSpan<bool> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
for (const int edge_index : IndexRange(mesh.totedge)) {
const MEdge &edge = mesh.medge[edge_index];
r_values[edge_index] = old_values[edge.v1] && old_values[edge.v2];
}
}
static GVArray adapt_mesh_domain_point_to_edge(const Mesh &mesh, const GVArray &varray)
{
GVArray new_varray;
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totedge);
adapt_mesh_domain_point_to_edge_impl<T>(mesh, varray.typed<T>(), values);
new_varray = VArray<T>::ForContainer(std::move(values));
if constexpr (std::is_same_v<T, bool>) {
/* An edge is selected if both of its vertices were selected. */
new_varray = VArray<bool>::ForFunc(
mesh.totedge, [mesh, varray = varray.typed<bool>()](const int edge_index) {
const MEdge &edge = mesh.medge[edge_index];
return varray[edge.v1] && varray[edge.v2];
});
}
else {
new_varray = VArray<T>::ForFunc(
mesh.totedge, [mesh, varray = varray.typed<T>()](const int edge_index) {
T return_value;
attribute_math::DefaultMixer<T> mixer({&return_value, 1});
const MEdge &edge = mesh.medge[edge_index];
mixer.mix_in(0, varray[edge.v1]);
mixer.mix_in(0, varray[edge.v2]);
mixer.finalize();
return return_value;
});
}
}
});
return new_varray;
@ -787,61 +718,41 @@ static GVArray adapt_mesh_domain_edge_to_point(const Mesh &mesh, const GVArray &
return new_varray;
}
/**
* \note Theoretically this interpolation does not need to compute all values at once.
* However, doing that makes the implementation simpler, and this can be optimized in the future if
* only some values are required.
*/
template<typename T>
static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
attribute_math::DefaultMixer<T> mixer(r_values);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
mixer.mix_in(poly_index, old_values[loop.e]);
}
}
mixer.finalize();
}
/* A face is selected if all of its edges are selected. */
template<>
void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
const VArray<bool> &old_values,
MutableSpan<bool> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
r_values.fill(true);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
const int edge_index = loop.e;
if (!old_values[edge_index]) {
r_values[poly_index] = false;
break;
}
}
}
}
static GVArray adapt_mesh_domain_edge_to_face(const Mesh &mesh, const GVArray &varray)
{
GVArray new_varray;
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totpoly);
adapt_mesh_domain_edge_to_face_impl<T>(mesh, varray.typed<T>(), values);
new_varray = VArray<T>::ForContainer(std::move(values));
if constexpr (std::is_same_v<T, bool>) {
/* A face is selected if all of its edges are selected. */
new_varray = VArray<bool>::ForFunc(
mesh.totpoly, [mesh, varray = varray.typed<T>()](const int face_index) {
const MPoly &poly = mesh.mpoly[face_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
if (!varray[loop.e]) {
return false;
}
}
return true;
});
}
else {
new_varray = VArray<T>::ForFunc(
mesh.totpoly, [mesh, varray = varray.typed<T>()](const int face_index) {
T return_value;
attribute_math::DefaultMixer<T> mixer({&return_value, 1});
const MPoly &poly = mesh.mpoly[face_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
const T value = varray[loop.e];
mixer.mix_in(0, value);
}
mixer.finalize();
return return_value;
});
}
}
});
return new_varray;

View File

@ -151,6 +151,8 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
BKE_mesh_update_customdata_pointers(mesh_dst, do_tessface);
mesh_dst->cd_flag = mesh_src->cd_flag;
mesh_dst->edit_mesh = nullptr;
mesh_dst->mselect = (MSelect *)MEM_dupallocN(mesh_dst->mselect);

View File

@ -623,10 +623,18 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh,
}
/* Copy over data. #CustomData_add_layer can do this, need to look it up. */
memcpy(result->mvert, mvert, sizeof(MVert) * STACK_SIZE(mvert));
memcpy(result->medge, medge, sizeof(MEdge) * STACK_SIZE(medge));
memcpy(result->mloop, mloop, sizeof(MLoop) * STACK_SIZE(mloop));
memcpy(result->mpoly, mpoly, sizeof(MPoly) * STACK_SIZE(mpoly));
if (STACK_SIZE(mvert)) {
memcpy(result->mvert, mvert, sizeof(MVert) * STACK_SIZE(mvert));
}
if (STACK_SIZE(medge)) {
memcpy(result->medge, medge, sizeof(MEdge) * STACK_SIZE(medge));
}
if (STACK_SIZE(mloop)) {
memcpy(result->mloop, mloop, sizeof(MLoop) * STACK_SIZE(mloop));
}
if (STACK_SIZE(mpoly)) {
memcpy(result->mpoly, mpoly, sizeof(MPoly) * STACK_SIZE(mpoly));
}
MEM_freeN(mvert);
MEM_freeN(medge);

View File

@ -298,129 +298,10 @@ void BKE_mesh_batch_cache_free(Mesh *me)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Mesh Runtime Debug Helpers
/** \name Mesh Runtime Validation
* \{ */
/* Evaluated mesh info printing function, to help track down differences output. */
#ifndef NDEBUG
# include "BLI_dynstr.h"
static void mesh_runtime_debug_info_layers(DynStr *dynstr, CustomData *cd)
{
int type;
for (type = 0; type < CD_NUMTYPES; type++) {
if (CustomData_has_layer(cd, type)) {
/* NOTE: doesn't account for multiple layers. */
const char *name = CustomData_layertype_name(type);
const int size = CustomData_sizeof(type);
const void *pt = CustomData_get_layer(cd, type);
const int pt_size = pt ? (int)(MEM_allocN_len(pt) / size) : 0;
const char *structname;
int structnum;
CustomData_file_write_info(type, &structname, &structnum);
BLI_dynstr_appendf(
dynstr,
" dict(name='%s', struct='%s', type=%d, ptr='%p', elem=%d, length=%d),\n",
name,
structname,
type,
(const void *)pt,
size,
pt_size);
}
}
}
char *BKE_mesh_runtime_debug_info(Mesh *me_eval)
{
DynStr *dynstr = BLI_dynstr_new();
char *ret;
BLI_dynstr_append(dynstr, "{\n");
BLI_dynstr_appendf(dynstr, " 'ptr': '%p',\n", (void *)me_eval);
# if 0
const char *tstr;
switch (me_eval->type) {
case DM_TYPE_CDDM:
tstr = "DM_TYPE_CDDM";
break;
case DM_TYPE_CCGDM:
tstr = "DM_TYPE_CCGDM";
break;
default:
tstr = "UNKNOWN";
break;
}
BLI_dynstr_appendf(dynstr, " 'type': '%s',\n", tstr);
# endif
BLI_dynstr_appendf(dynstr, " 'totvert': %d,\n", me_eval->totvert);
BLI_dynstr_appendf(dynstr, " 'totedge': %d,\n", me_eval->totedge);
BLI_dynstr_appendf(dynstr, " 'totface': %d,\n", me_eval->totface);
BLI_dynstr_appendf(dynstr, " 'totpoly': %d,\n", me_eval->totpoly);
BLI_dynstr_appendf(dynstr, " 'deformed_only': %d,\n", me_eval->runtime.deformed_only);
BLI_dynstr_append(dynstr, " 'vertexLayers': (\n");
mesh_runtime_debug_info_layers(dynstr, &me_eval->vdata);
BLI_dynstr_append(dynstr, " ),\n");
BLI_dynstr_append(dynstr, " 'edgeLayers': (\n");
mesh_runtime_debug_info_layers(dynstr, &me_eval->edata);
BLI_dynstr_append(dynstr, " ),\n");
BLI_dynstr_append(dynstr, " 'loopLayers': (\n");
mesh_runtime_debug_info_layers(dynstr, &me_eval->ldata);
BLI_dynstr_append(dynstr, " ),\n");
BLI_dynstr_append(dynstr, " 'polyLayers': (\n");
mesh_runtime_debug_info_layers(dynstr, &me_eval->pdata);
BLI_dynstr_append(dynstr, " ),\n");
BLI_dynstr_append(dynstr, " 'tessFaceLayers': (\n");
mesh_runtime_debug_info_layers(dynstr, &me_eval->fdata);
BLI_dynstr_append(dynstr, " ),\n");
BLI_dynstr_append(dynstr, "}\n");
ret = BLI_dynstr_get_cstring(dynstr);
BLI_dynstr_free(dynstr);
return ret;
}
void BKE_mesh_runtime_debug_print(Mesh *me_eval)
{
char *str = BKE_mesh_runtime_debug_info(me_eval);
puts(str);
fflush(stdout);
MEM_freeN(str);
}
void BKE_mesh_runtime_debug_print_cdlayers(CustomData *data)
{
int i;
const CustomDataLayer *layer;
printf("{\n");
for (i = 0, layer = data->layers; i < data->totlayer; i++, layer++) {
const char *name = CustomData_layertype_name(layer->type);
const int size = CustomData_sizeof(layer->type);
const char *structname;
int structnum;
CustomData_file_write_info(layer->type, &structname, &structnum);
printf(" dict(name='%s', struct='%s', type=%d, ptr='%p', elem=%d, length=%d),\n",
name,
structname,
layer->type,
(const void *)layer->data,
size,
(int)(MEM_allocN_len(layer->data) / size));
}
printf("}\n");
}
bool BKE_mesh_runtime_is_valid(Mesh *me_eval)
{

View File

@ -106,6 +106,9 @@ typedef struct MultiresReshapeContext {
/* Indexed by base face index, returns first ptex face index corresponding
* to that base face. */
int *face_ptex_offset;
/* Vertex crease custom data layer, null if none is present. */
const float *cd_vertex_crease;
} MultiresReshapeContext;
/**

View File

@ -108,6 +108,7 @@ typedef struct Vertex {
int num_grid_coords;
GridCoord *grid_coords;
float sharpness;
bool is_infinite_sharp;
} Vertex;
@ -522,19 +523,33 @@ static int get_reshape_level_resolution(const MultiresReshapeContext *reshape_co
return (1 << reshape_context->reshape.level) + 1;
}
static bool is_crease_supported(const MultiresReshapeSmoothContext *reshape_smooth_context)
{
return !ELEM(reshape_smooth_context->smoothing_type,
MULTIRES_SUBDIVIDE_LINEAR,
MULTIRES_SUBDIVIDE_SIMPLE);
}
/* Get crease which will be used for communication to OpenSubdiv topology.
* Note that simple subdivision treats all base edges as infinitely sharp. */
static char get_effective_edge_crease_char(
const MultiresReshapeSmoothContext *reshape_smooth_context, const MEdge *base_edge)
static char get_effective_crease_char(const MultiresReshapeSmoothContext *reshape_smooth_context,
const MEdge *base_edge)
{
if (ELEM(reshape_smooth_context->smoothing_type,
MULTIRES_SUBDIVIDE_LINEAR,
MULTIRES_SUBDIVIDE_SIMPLE)) {
if (!is_crease_supported(reshape_smooth_context)) {
return 255;
}
return base_edge->crease;
}
static float get_effective_crease_float(const MultiresReshapeSmoothContext *reshape_smooth_context,
const float crease)
{
if (!is_crease_supported(reshape_smooth_context)) {
return 1.0f;
}
return crease;
}
static void context_init(MultiresReshapeSmoothContext *reshape_smooth_context,
const MultiresReshapeContext *reshape_context,
const eMultiresSubdivideModeType mode)
@ -629,6 +644,7 @@ static bool foreach_topology_info(const SubdivForeachContext *foreach_context,
static void foreach_single_vertex(const SubdivForeachContext *foreach_context,
const GridCoord *grid_coord,
const int coarse_vertex_index,
const int subdiv_vertex_index)
{
const MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data;
@ -641,11 +657,32 @@ static void foreach_single_vertex(const SubdivForeachContext *foreach_context,
sizeof(Vertex) * (vertex->num_grid_coords + 1));
vertex->grid_coords[vertex->num_grid_coords] = *grid_coord;
++vertex->num_grid_coords;
if (coarse_vertex_index == -1) {
return;
}
const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
const float *cd_vertex_crease = reshape_context->cd_vertex_crease;
if (cd_vertex_crease == NULL) {
return;
}
float crease = cd_vertex_crease[coarse_vertex_index];
if (crease == 0.0f) {
return;
}
crease = get_effective_crease_float(reshape_smooth_context, crease);
vertex->sharpness = BKE_subdiv_crease_to_sharpness_f(crease);
}
/* TODO(sergey): De-duplicate with similar function in multires_reshape_vertcos.c */
static void foreach_vertex(const SubdivForeachContext *foreach_context,
const PTexCoord *ptex_coord,
const int coarse_vertex_index,
const int subdiv_vertex_index)
{
const MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data;
@ -665,12 +702,13 @@ static void foreach_vertex(const SubdivForeachContext *foreach_context,
for (int current_corner = 0; current_corner < num_corners; ++current_corner) {
GridCoord corner_grid_coord = grid_coord;
corner_grid_coord.grid_index = start_grid_index + current_corner;
foreach_single_vertex(foreach_context, &corner_grid_coord, subdiv_vertex_index);
foreach_single_vertex(
foreach_context, &corner_grid_coord, coarse_vertex_index, subdiv_vertex_index);
}
return;
}
foreach_single_vertex(foreach_context, &grid_coord, subdiv_vertex_index);
foreach_single_vertex(foreach_context, &grid_coord, coarse_vertex_index, subdiv_vertex_index);
if (grid_coord.u == 0.0f) {
GridCoord prev_grid_coord;
@ -678,7 +716,8 @@ static void foreach_vertex(const SubdivForeachContext *foreach_context,
prev_grid_coord.u = grid_coord.v;
prev_grid_coord.v = 0.0f;
foreach_single_vertex(foreach_context, &prev_grid_coord, subdiv_vertex_index);
foreach_single_vertex(
foreach_context, &prev_grid_coord, coarse_vertex_index, subdiv_vertex_index);
}
if (grid_coord.v == 0.0f) {
@ -687,7 +726,8 @@ static void foreach_vertex(const SubdivForeachContext *foreach_context,
next_grid_coord.u = 0.0f;
next_grid_coord.v = grid_coord.u;
foreach_single_vertex(foreach_context, &next_grid_coord, subdiv_vertex_index);
foreach_single_vertex(
foreach_context, &next_grid_coord, coarse_vertex_index, subdiv_vertex_index);
}
}
@ -705,7 +745,7 @@ static void foreach_vertex_inner(const struct SubdivForeachContext *foreach_cont
.u = ptex_face_u,
.v = ptex_face_v,
};
foreach_vertex(foreach_context, &ptex_coord, subdiv_vertex_index);
foreach_vertex(foreach_context, &ptex_coord, -1, subdiv_vertex_index);
}
static void foreach_vertex_every_corner(const struct SubdivForeachContext *foreach_context,
@ -713,7 +753,7 @@ static void foreach_vertex_every_corner(const struct SubdivForeachContext *forea
const int ptex_face_index,
const float ptex_face_u,
const float ptex_face_v,
const int UNUSED(coarse_vertex_index),
const int coarse_vertex_index,
const int UNUSED(coarse_face_index),
const int UNUSED(coarse_face_corner),
const int subdiv_vertex_index)
@ -723,7 +763,7 @@ static void foreach_vertex_every_corner(const struct SubdivForeachContext *forea
.u = ptex_face_u,
.v = ptex_face_v,
};
foreach_vertex(foreach_context, &ptex_coord, subdiv_vertex_index);
foreach_vertex(foreach_context, &ptex_coord, coarse_vertex_index, subdiv_vertex_index);
}
static void foreach_vertex_every_edge(const struct SubdivForeachContext *foreach_context,
@ -741,7 +781,7 @@ static void foreach_vertex_every_edge(const struct SubdivForeachContext *foreach
.u = ptex_face_u,
.v = ptex_face_v,
};
foreach_vertex(foreach_context, &ptex_coord, subdiv_vertex_index);
foreach_vertex(foreach_context, &ptex_coord, -1, subdiv_vertex_index);
}
static void foreach_loop(const struct SubdivForeachContext *foreach_context,
@ -811,7 +851,7 @@ static void store_edge(MultiresReshapeSmoothContext *reshape_smooth_context,
Edge *edge = &reshape_smooth_context->geometry.edges[edge_index];
edge->v1 = subdiv_v1;
edge->v2 = subdiv_v2;
edge->sharpness = BKE_subdiv_edge_crease_to_sharpness_char(crease);
edge->sharpness = BKE_subdiv_crease_to_sharpness_char(crease);
}
static void foreach_edge(const struct SubdivForeachContext *foreach_context,
@ -842,7 +882,7 @@ static void foreach_edge(const struct SubdivForeachContext *foreach_context,
/* Edges without crease are to be ignored as well. */
const Mesh *base_mesh = reshape_context->base_mesh;
const MEdge *base_edge = &base_mesh->medge[coarse_edge_index];
const char crease = get_effective_edge_crease_char(reshape_smooth_context, base_edge);
const char crease = get_effective_crease_char(reshape_smooth_context, base_edge);
if (crease == 0) {
return;
}
@ -868,8 +908,7 @@ static void geometry_init_loose_information(MultiresReshapeSmoothContext *reshap
if (!BLI_BITMAP_TEST_BOOL(reshape_smooth_context->non_loose_base_edge_map, loop->e)) {
BLI_BITMAP_ENABLE(reshape_smooth_context->non_loose_base_edge_map, loop->e);
const char crease = get_effective_edge_crease_char(reshape_smooth_context,
&base_edge[loop->e]);
const char crease = get_effective_crease_char(reshape_smooth_context, &base_edge[loop->e]);
if (crease != 0) {
++num_used_edges;
}
@ -1012,6 +1051,15 @@ static float get_edge_sharpness(const OpenSubdiv_Converter *converter, const int
return edge->sharpness;
}
static float get_vertex_sharpness(const OpenSubdiv_Converter *converter, const int vertex_index)
{
const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data;
BLI_assert(vertex_index < reshape_smooth_context->geometry.num_vertices);
const Vertex *vertex = &reshape_smooth_context->geometry.vertices[vertex_index];
return vertex->sharpness;
}
static bool is_infinite_sharp_vertex(const OpenSubdiv_Converter *converter, int vertex_index)
{
const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data;
@ -1048,7 +1096,7 @@ static void converter_init(const MultiresReshapeSmoothContext *reshape_smooth_co
converter->getNumVertexFaces = NULL;
converter->getVertexFaces = NULL;
converter->isInfiniteSharpVertex = is_infinite_sharp_vertex;
converter->getVertexSharpness = NULL;
converter->getVertexSharpness = get_vertex_sharpness;
converter->getNumUVLayers = NULL;
converter->precalcUVLayer = NULL;

View File

@ -211,6 +211,8 @@ bool multires_reshape_context_create_from_object(MultiresReshapeContext *reshape
reshape_context->top.level = mmd->totlvl;
reshape_context->top.grid_size = BKE_subdiv_grid_size_from_level(reshape_context->top.level);
reshape_context->cd_vertex_crease = CustomData_get_layer(&base_mesh->vdata, CD_CREASE);
context_init_commoon(reshape_context);
return context_verify_or_free(reshape_context);

View File

@ -4746,6 +4746,7 @@ static void registerGeometryNodes()
register_node_type_geo_curve_fillet();
register_node_type_geo_curve_handle_type_selection();
register_node_type_geo_curve_length();
register_node_type_geo_curve_primitive_arc();
register_node_type_geo_curve_primitive_bezier_segment();
register_node_type_geo_curve_primitive_circle();
register_node_type_geo_curve_primitive_line();
@ -4767,6 +4768,8 @@ static void registerGeometryNodes()
register_node_type_geo_distribute_points_on_faces();
register_node_type_geo_dual_mesh();
register_node_type_geo_edge_split();
register_node_type_geo_field_at_index();
register_node_type_geo_flip_faces();
register_node_type_geo_geometry_to_instance();
register_node_type_geo_image_texture();
register_node_type_geo_input_curve_handles();
@ -4822,6 +4825,7 @@ static void registerGeometryNodes()
register_node_type_geo_realize_instances();
register_node_type_geo_rotate_instances();
register_node_type_geo_sample_texture();
register_node_type_geo_scale_elements();
register_node_type_geo_scale_instances();
register_node_type_geo_separate_components();
register_node_type_geo_separate_geometry();

View File

@ -1742,6 +1742,7 @@ static void sculpt_update_object(Depsgraph *depsgraph,
ss->totvert = me->totvert;
ss->totpoly = me->totpoly;
ss->totfaces = me->totpoly;
ss->vert_normals = BKE_mesh_vertex_normals_ensure(me);
ss->mvert = me->mvert;
ss->medge = me->medge;
ss->mpoly = me->mpoly;

Some files were not shown because too many files have changed in this diff Show More