Merge sculpt-dev -> master
This commit is contained in:
parent
1acb608f52
commit
ccbde00a83
|
@ -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)
|
||||
|
|
|
@ -94,3 +94,4 @@ download_source(POTRACE)
|
|||
download_source(HARU)
|
||||
download_source(ZSTD)
|
||||
download_source(FLEX)
|
||||
download_source(BROTLI)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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++;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
|
@ -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:
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -54,7 +54,7 @@ set(LIB
|
|||
bf_gpu
|
||||
bf_intern_guardedalloc
|
||||
|
||||
${FREETYPE_LIBRARY}
|
||||
${FREETYPE_LIBRARIES}
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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});
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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"
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue