diff options
author | Hans Goudey <h.goudey@me.com> | 2022-01-21 18:38:52 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2022-01-21 18:38:52 +0300 |
commit | 5ad0321cfa644febbd7c2a19dbeaec62d630d59c (patch) | |
tree | 0113b02cbff9bd95a7cd669089bca41f9aa7e403 | |
parent | 3d27612d23f7a8c16d9851c1bd7d70f0d1b6fb1f (diff) | |
parent | c39d514a4eacd4a883775a3fcd8b5a7d8e8e52cc (diff) |
Merge branch 'master' into temp-geometry-nodes-extrude-mesh
32 files changed, 981 insertions, 137 deletions
diff --git a/intern/cycles/device/metal/bvh.h b/intern/cycles/device/metal/bvh.h index cbc5ca7d2c3..58d71e3928f 100644 --- a/intern/cycles/device/metal/bvh.h +++ b/intern/cycles/device/metal/bvh.h @@ -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); }; diff --git a/intern/cycles/device/metal/bvh.mm b/intern/cycles/device/metal/bvh.mm index 1953102cb41..8b252f1a5ec 100644 --- a/intern/cycles/device/metal/bvh.mm +++ b/intern/cycles/device/metal/bvh.mm @@ -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; } diff --git a/intern/cycles/device/metal/kernel.h b/intern/cycles/device/metal/kernel.h index 4874af1bfa6..a4bfb30436d 100644 --- a/intern/cycles/device/metal/kernel.h +++ b/intern/cycles/device/metal/kernel.h @@ -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 }; diff --git a/intern/cycles/device/metal/kernel.mm b/intern/cycles/device/metal/kernel.mm index f948a8a0a0f..e9bd1cea5df 100644 --- a/intern/cycles/device/metal/kernel.mm +++ b/intern/cycles/device/metal/kernel.mm @@ -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,37 +402,51 @@ 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]; NSMutableSet *unique_functions = [NSMutableSet diff --git a/intern/cycles/kernel/device/metal/kernel.metal b/intern/cycles/kernel/device/metal/kernel.metal index deb7dafe55e..3303b541487 100644 --- a/intern/cycles/kernel/device/metal/kernel.metal +++ b/intern/cycles/kernel/device/metal/kernel.metal @@ -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__ */ diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py index 49f196640d8..11e2cd84903 100644 --- a/release/scripts/startup/bl_ui/properties_data_mesh.py +++ b/release/scripts/startup/bl_ui/properties_data_mesh.py @@ -82,6 +82,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): @@ -615,6 +623,10 @@ class DATA_PT_mesh_attributes(MeshButtonsPanel, Panel): col.operator("geometry.attribute_add", icon='ADD', text="") col.operator("geometry.attribute_remove", icon='REMOVE', text="") + col.separator() + + col.menu("MESH_MT_attribute_context_menu", icon='DOWNARROW_HLT', text="") + self.draw_attribute_warnings(context, layout) def draw_attribute_warnings(self, context, layout): @@ -652,6 +664,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, diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index f0d91ce5de8..4ec1cb4af5b 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -143,6 +143,7 @@ def mesh_node_items(context): yield NodeItem("GeometryNodeDualMesh") yield NodeItem("GeometryNodeExtrudeMesh") + yield NodeItem("GeometryNodeFlipFaces") yield NodeItem("GeometryNodeMeshBoolean") yield NodeItem("GeometryNodeMeshToCurve") yield NodeItem("GeometryNodeMeshToPoints") diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index bf958b0868a..bb7d647e713 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1630,7 +1630,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #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_EXTRUDE_MESH 1150 +#define GEO_NODE_FLIP_FACES 1150 +#define GEO_NODE_EXTRUDE_MESH 1151 /** \} */ diff --git a/source/blender/blenkernel/intern/idprop_serialize.cc b/source/blender/blenkernel/intern/idprop_serialize.cc index 289bc39f4b5..92dce49500c 100644 --- a/source/blender/blenkernel/intern/idprop_serialize.cc +++ b/source/blender/blenkernel/intern/idprop_serialize.cc @@ -29,7 +29,7 @@ using namespace blender::io::serialize; /* Forward declarations */ class IDPropertySerializer; -class DictionaryEntryParser; +struct DictionaryEntryParser; static IDProperty *idprop_from_value(const DictionaryValue &value); static const IDPropertySerializer &serializer_for(eIDPropertyType property_type); static const IDPropertySerializer &serializer_for(StringRef idprop_typename); diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 6271561ae23..56841368893 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -4770,6 +4770,7 @@ static void registerGeometryNodes() register_node_type_geo_edge_split(); register_node_type_geo_extrude_mesh(); 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(); diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 78ec2276af7..81fc6086951 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -2551,6 +2551,8 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) ntree, GEO_NODE_STRING_TO_CURVES, "Curves", "Curve Instances"); version_node_output_socket_name( ntree, GEO_NODE_INPUT_MESH_EDGE_ANGLE, "Angle", "Unsigned Angle"); + version_node_output_socket_name( + ntree, GEO_NODE_INPUT_MESH_ISLAND, "Index", "Island Index"); version_node_input_socket_name(ntree, GEO_NODE_TRANSFER_ATTRIBUTE, "Target", "Source"); } } diff --git a/source/blender/editors/geometry/CMakeLists.txt b/source/blender/editors/geometry/CMakeLists.txt index 65b9633da98..8c920915937 100644 --- a/source/blender/editors/geometry/CMakeLists.txt +++ b/source/blender/editors/geometry/CMakeLists.txt @@ -20,9 +20,11 @@ set(INC ../../blenkernel ../../blenlib ../../depsgraph + ../../functions ../../makesdna ../../makesrna ../../windowmanager + ../../../../intern/guardedalloc ) set(INC_SYS diff --git a/source/blender/editors/geometry/geometry_attributes.cc b/source/blender/editors/geometry/geometry_attributes.cc index 9c0f6728701..56ecd108bba 100644 --- a/source/blender/editors/geometry/geometry_attributes.cc +++ b/source/blender/editors/geometry/geometry_attributes.cc @@ -21,8 +21,18 @@ * \ingroup edgeometry */ +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" + #include "BKE_attribute.h" #include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_geometry_set.hh" +#include "BKE_object_deform.h" +#include "BKE_report.h" #include "RNA_access.h" #include "RNA_define.h" @@ -33,10 +43,19 @@ #include "WM_api.h" #include "WM_types.h" +#include "UI_interface.h" +#include "UI_resources.h" + #include "ED_object.h" #include "geometry_intern.hh" +namespace blender::ed::geometry { + +using fn::CPPType; +using fn::GArray; +using fn::GVArray; + /*********************** Attribute Operators ************************/ static bool geometry_attributes_poll(bContext *C) @@ -76,7 +95,7 @@ static const EnumPropertyItem *geometry_attribute_domain_itemf(bContext *C, return DummyRNA_NULL_items; } - return rna_enum_attribute_domain_itemf(static_cast<ID *>(ob->data), r_free); + return rna_enum_attribute_domain_itemf(static_cast<ID *>(ob->data), false, r_free); } static int geometry_attribute_add_exec(bContext *C, wmOperator *op) @@ -180,3 +199,187 @@ void GEOMETRY_OT_attribute_remove(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + +enum class ConvertAttributeMode { + Generic, + UVMap, + VertexGroup, + VertexColor, +}; + +static bool geometry_attribute_convert_poll(bContext *C) +{ + if (!geometry_attributes_poll(C)) { + return false; + } + + Object *ob = ED_object_context(C); + ID *data = static_cast<ID *>(ob->data); + if (GS(data->name) != ID_ME) { + return false; + } + CustomDataLayer *layer = BKE_id_attributes_active_get(data); + if (layer == nullptr) { + return false; + } + return true; +} + +static int geometry_attribute_convert_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_context(C); + ID *ob_data = static_cast<ID *>(ob->data); + CustomDataLayer *layer = BKE_id_attributes_active_get(ob_data); + const std::string name = layer->name; + + const ConvertAttributeMode mode = static_cast<ConvertAttributeMode>( + RNA_enum_get(op->ptr, "mode")); + + Mesh *mesh = reinterpret_cast<Mesh *>(ob_data); + MeshComponent mesh_component; + mesh_component.replace(mesh, GeometryOwnershipType::Editable); + + /* General conversion steps are always the same: + * 1. Convert old data to right domain and data type. + * 2. Copy the data into a new array so that it does not depend on the old attribute anymore. + * 3. Delete the old attribute. + * 4. Create a new attribute based on the previously copied data. */ + switch (mode) { + case ConvertAttributeMode::Generic: { + const AttributeDomain dst_domain = static_cast<AttributeDomain>( + RNA_enum_get(op->ptr, "domain")); + const CustomDataType dst_type = static_cast<CustomDataType>( + RNA_enum_get(op->ptr, "data_type")); + + if (ELEM(dst_type, CD_PROP_STRING, CD_MLOOPCOL)) { + BKE_report(op->reports, RPT_ERROR, "Cannot convert to the selected type"); + return OPERATOR_CANCELLED; + } + + GVArray src_varray = mesh_component.attribute_get_for_read(name, dst_domain, dst_type); + const CPPType &cpp_type = src_varray.type(); + void *new_data = MEM_malloc_arrayN(src_varray.size(), cpp_type.size(), __func__); + src_varray.materialize_to_uninitialized(new_data); + mesh_component.attribute_try_delete(name); + mesh_component.attribute_try_create(name, dst_domain, dst_type, AttributeInitMove(new_data)); + break; + } + case ConvertAttributeMode::UVMap: { + MLoopUV *dst_uvs = static_cast<MLoopUV *>( + MEM_calloc_arrayN(mesh->totloop, sizeof(MLoopUV), __func__)); + VArray<float2> src_varray = mesh_component.attribute_get_for_read<float2>( + name, ATTR_DOMAIN_CORNER, {0.0f, 0.0f}); + for (const int i : IndexRange(mesh->totloop)) { + copy_v2_v2(dst_uvs[i].uv, src_varray[i]); + } + mesh_component.attribute_try_delete(name); + CustomData_add_layer_named( + &mesh->ldata, CD_MLOOPUV, CD_ASSIGN, dst_uvs, mesh->totloop, name.c_str()); + break; + } + case ConvertAttributeMode::VertexColor: { + MLoopCol *dst_colors = static_cast<MLoopCol *>( + MEM_calloc_arrayN(mesh->totloop, sizeof(MLoopCol), __func__)); + VArray<ColorGeometry4f> src_varray = mesh_component.attribute_get_for_read<ColorGeometry4f>( + name, ATTR_DOMAIN_CORNER, ColorGeometry4f{0.0f, 0.0f, 0.0f, 1.0f}); + for (const int i : IndexRange(mesh->totloop)) { + ColorGeometry4b encoded_color = src_varray[i].encode(); + copy_v4_v4_uchar(&dst_colors[i].r, &encoded_color.r); + } + mesh_component.attribute_try_delete(name); + CustomData_add_layer_named( + &mesh->ldata, CD_MLOOPCOL, CD_ASSIGN, dst_colors, mesh->totloop, name.c_str()); + break; + } + case ConvertAttributeMode::VertexGroup: { + Array<float> src_weights(mesh->totvert); + VArray<float> src_varray = mesh_component.attribute_get_for_read<float>( + name, ATTR_DOMAIN_POINT, 0.0f); + src_varray.materialize(src_weights); + mesh_component.attribute_try_delete(name); + + bDeformGroup *defgroup = BKE_object_defgroup_new(ob, name.c_str()); + const int defgroup_index = BLI_findindex(BKE_id_defgroup_list_get(&mesh->id), defgroup); + MDeformVert *dverts = BKE_object_defgroup_data_create(&mesh->id); + for (const int i : IndexRange(mesh->totvert)) { + const float weight = src_weights[i]; + if (weight > 0.0f) { + BKE_defvert_add_index_notest(dverts + i, defgroup_index, weight); + } + } + break; + } + } + + int *active_index = BKE_id_attributes_active_index_p(&mesh->id); + if (*active_index > 0) { + *active_index -= 1; + } + + DEG_id_tag_update(&mesh->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &mesh->id); + + return OPERATOR_FINISHED; +} + +static void geometry_attribute_convert_ui(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout; + + uiItemR(layout, op->ptr, "mode", 0, nullptr, ICON_NONE); + + const ConvertAttributeMode mode = static_cast<ConvertAttributeMode>( + RNA_enum_get(op->ptr, "mode")); + + if (mode == ConvertAttributeMode::Generic) { + uiItemR(layout, op->ptr, "domain", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "data_type", 0, nullptr, ICON_NONE); + } +} + +static int geometry_attribute_convert_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + return WM_operator_props_dialog_popup(C, op, 300); +} + +void GEOMETRY_OT_attribute_convert(wmOperatorType *ot) +{ + ot->name = "Convert Attribute"; + ot->description = "Change how the attribute is stored"; + ot->idname = "GEOMETRY_OT_attribute_convert"; + + ot->invoke = geometry_attribute_convert_invoke; + ot->exec = geometry_attribute_convert_exec; + ot->poll = geometry_attribute_convert_poll; + ot->ui = geometry_attribute_convert_ui; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + static EnumPropertyItem mode_items[] = { + {int(ConvertAttributeMode::Generic), "GENERIC", 0, "Generic", ""}, + {int(ConvertAttributeMode::UVMap), "UV_MAP", 0, "UV Map", ""}, + {int(ConvertAttributeMode::VertexGroup), "VERTEX_GROUP", 0, "Vertex Group", ""}, + {int(ConvertAttributeMode::VertexColor), "VERTEX_COLOR", 0, "Vertex Color", ""}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + PropertyRNA *prop; + + RNA_def_enum( + ot->srna, "mode", mode_items, static_cast<int>(ConvertAttributeMode::Generic), "Mode", ""); + + prop = RNA_def_enum(ot->srna, + "domain", + rna_enum_attribute_domain_items, + ATTR_DOMAIN_POINT, + "Domain", + "Which geometry element to move the attribute to"); + RNA_def_enum_funcs(prop, geometry_attribute_domain_itemf); + + RNA_def_enum( + ot->srna, "data_type", rna_enum_attribute_type_items, CD_PROP_FLOAT, "Data Type", ""); +} + +} // namespace blender::ed::geometry diff --git a/source/blender/editors/geometry/geometry_intern.hh b/source/blender/editors/geometry/geometry_intern.hh index 14992476edd..30a2a1d6eb1 100644 --- a/source/blender/editors/geometry/geometry_intern.hh +++ b/source/blender/editors/geometry/geometry_intern.hh @@ -25,6 +25,11 @@ struct wmOperatorType; +namespace blender::ed::geometry { + /* *** geometry_attributes.cc *** */ void GEOMETRY_OT_attribute_add(struct wmOperatorType *ot); void GEOMETRY_OT_attribute_remove(struct wmOperatorType *ot); +void GEOMETRY_OT_attribute_convert(struct wmOperatorType *ot); + +} // namespace blender::ed::geometry diff --git a/source/blender/editors/geometry/geometry_ops.cc b/source/blender/editors/geometry/geometry_ops.cc index ba8afe0e62f..8933b2a7f00 100644 --- a/source/blender/editors/geometry/geometry_ops.cc +++ b/source/blender/editors/geometry/geometry_ops.cc @@ -31,6 +31,9 @@ void ED_operatortypes_geometry(void) { + using namespace blender::ed::geometry; + WM_operatortype_append(GEOMETRY_OT_attribute_add); WM_operatortype_append(GEOMETRY_OT_attribute_remove); + WM_operatortype_append(GEOMETRY_OT_attribute_convert); } diff --git a/source/blender/editors/interface/interface_template_search_menu.cc b/source/blender/editors/interface/interface_template_search_menu.cc index 7a079e01e61..0ce3a0d8af1 100644 --- a/source/blender/editors/interface/interface_template_search_menu.cc +++ b/source/blender/editors/interface/interface_template_search_menu.cc @@ -465,6 +465,9 @@ static MenuSearch_Data *menu_items_from_ui_create( const char *idname_array[] = { /* While we could include this, it's just showing filenames to load. */ "TOPBAR_MT_file_open_recent", + /* Showing undo history is not helpful since users may accidentally undo + * an action they intend to run. */ + "TOPBAR_MT_undo_history", }; for (int i = 0; i < ARRAY_SIZE(idname_array); i++) { MenuType *mt = WM_menutype_find(idname_array[i], false); diff --git a/source/blender/gpu/intern/gpu_shader_builtin.c b/source/blender/gpu/intern/gpu_shader_builtin.c index 13f1774df03..9c8dbf8401f 100644 --- a/source/blender/gpu/intern/gpu_shader_builtin.c +++ b/source/blender/gpu/intern/gpu_shader_builtin.c @@ -192,8 +192,18 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = { .create_info = "gpu_shader_2D_flat_color"}, [GPU_SHADER_2D_SMOOTH_COLOR] = {.name = "GPU_SHADER_2D_SMOOTH_COLOR", .create_info = "gpu_shader_2D_smooth_color"}, - [GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE] = {.name = "GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE", - .create_info = "gpu_shader_2D_image_overlays_merge"}, + [GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE] = + { + .name = "GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE", +#ifdef __APPLE__ + /* GPUShaderCreateInfo is disabled on MacOS due to mismatch with OCIO shader. See + * T95052 for more details. */ + .vert = datatoc_gpu_shader_2D_image_vert_glsl, + .frag = datatoc_gpu_shader_image_overlays_merge_frag_glsl, +#else + .create_info = "gpu_shader_2D_image_overlays_merge", +#endif + }, [GPU_SHADER_2D_IMAGE_OVERLAYS_STEREO_MERGE] = {.name = "GPU_SHADER_2D_IMAGE_OVERLAYS_STEREO_MERGE", .create_info = "gpu_shader_2D_image_overlays_stereo_merge"}, diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_rect_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_rect_color_info.hh index b94556bbb25..4e10b91ef39 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_rect_color_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_rect_color_info.hh @@ -25,8 +25,6 @@ #include "gpu_shader_create_info.hh" GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_rect_color) - .vertex_in(0, Type::VEC2, "pos") - .vertex_in(1, Type::VEC2, "texCoord") .vertex_out(smooth_tex_coord_interp_iface) .fragment_out(0, Type::VEC4, "fragColor") .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_flat_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_flat_color_info.hh index 70b2dac8266..14a6986f478 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_3D_flat_color_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_flat_color_info.hh @@ -30,7 +30,6 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_flat_color) .vertex_out(flat_color_iface) .fragment_out(0, Type::VEC4, "fragColor") .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") - .push_constant(1, Type::BOOL, "srgbTarget") .vertex_source("gpu_shader_3D_flat_color_vert.glsl") .fragment_source("gpu_shader_flat_color_frag.glsl") .additional_info("gpu_srgb_to_framebuffer_space") diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_point_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_point_info.hh index b62c8fe7518..27357eef8c9 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_3D_point_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_point_info.hh @@ -29,7 +29,6 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_point_fixed_size_varying_color) .vertex_out(smooth_color_iface) .fragment_out(0, Type::VEC4, "fragColor") .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") - .push_constant(16, Type::FLOAT, "size") .vertex_source("gpu_shader_3D_point_fixed_size_varying_color_vert.glsl") .fragment_source("gpu_shader_point_varying_color_frag.glsl") .do_static_compilation(true); @@ -52,7 +51,6 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_point_uniform_size_uniform_color_aa) .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix") .push_constant(16, Type::VEC4, "color") .push_constant(20, Type::FLOAT, "size") - .push_constant(24, Type::FLOAT, "outlineWidth") .vertex_source("gpu_shader_3D_point_uniform_size_aa_vert.glsl") .fragment_source("gpu_shader_point_uniform_color_aa_frag.glsl") .do_static_compilation(true); diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc index 8c845c34db2..d31353c4a76 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc @@ -394,7 +394,7 @@ MTLWriter::MTLWriter(const char *obj_filepath) noexcept(false) if (!ok) { throw std::system_error(ENAMETOOLONG, std::system_category(), ""); } - file_handler_ = std::make_unique<FileHandler<eFileType::MTL>>(mtl_filepath_); + file_handler_ = std::make_unique<FormattedFileHandler<eFileType::MTL>>(mtl_filepath_); } void MTLWriter::write_header(const char *blen_filepath) const diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh index 1cad179a70c..7385d9fabe2 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh @@ -49,14 +49,14 @@ struct IndexOffsets { class OBJWriter : NonMovable, NonCopyable { private: const OBJExportParams &export_params_; - std::unique_ptr<FileHandler<eFileType::OBJ>> file_handler_ = nullptr; + std::unique_ptr<FormattedFileHandler<eFileType::OBJ>> file_handler_ = nullptr; IndexOffsets index_offsets_{0, 0, 0}; public: OBJWriter(const char *filepath, const OBJExportParams &export_params) noexcept(false) : export_params_(export_params) { - file_handler_ = std::make_unique<FileHandler<eFileType::OBJ>>(filepath); + file_handler_ = std::make_unique<FormattedFileHandler<eFileType::OBJ>>(filepath); } void write_header() const; @@ -172,7 +172,7 @@ class OBJWriter : NonMovable, NonCopyable { */ class MTLWriter : NonMovable, NonCopyable { private: - std::unique_ptr<FileHandler<eFileType::MTL>> file_handler_ = nullptr; + std::unique_ptr<FormattedFileHandler<eFileType::MTL>> file_handler_ = nullptr; std::string mtl_filepath_; Vector<MTLMaterial> mtlmaterials_; /* Map from a Material* to an index into mtlmaterials_. */ diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh index a6f0174d68b..e88a76fc4e8 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh @@ -88,6 +88,7 @@ enum class eMTLSyntaxElement { template<eFileType filetype> struct FileTypeTraits; +/* Used to prevent mixing of say OBJ file format with MTL syntax elements. */ template<> struct FileTypeTraits<eFileType::OBJ> { using SyntaxType = eOBJSyntaxElement; }; @@ -96,15 +97,19 @@ template<> struct FileTypeTraits<eFileType::MTL> { using SyntaxType = eMTLSyntaxElement; }; -template<eFileType type> struct Formatting { +struct FormattingSyntax { + /* Formatting syntax with the file format key like `newmtl %s\n`. */ const char *fmt = nullptr; + /* Number of arguments needed by the syntax. */ const int total_args = 0; - /* Fail to compile by default. */ - const bool is_type_valid = false; + /* Whether types of the given arguments are accepted by the syntax above. Fail to compile by + * default. + */ + const bool are_types_valid = false; }; /** - * Type dependent but always false. Use to add a conditional compile-time error. + * Type dependent but always false. Use to add a constexpr-conditional compile-time error. */ template<typename T> struct always_false : std::false_type { }; @@ -118,9 +123,8 @@ constexpr bool is_type_integral = (... && std::is_integral_v<std::decay_t<T>>); template<typename... T> constexpr bool is_type_string_related = (... && std::is_constructible_v<std::string, T>); -template<eFileType filetype, typename... T> -constexpr std::enable_if_t<filetype == eFileType::OBJ, Formatting<filetype>> -syntax_elem_to_formatting(const eOBJSyntaxElement key) +template<typename... T> +constexpr FormattingSyntax syntax_elem_to_formatting(const eOBJSyntaxElement key) { switch (key) { case eOBJSyntaxElement::vertex_coords: { @@ -201,9 +205,8 @@ syntax_elem_to_formatting(const eOBJSyntaxElement key) } } -template<eFileType filetype, typename... T> -constexpr std::enable_if_t<filetype == eFileType::MTL, Formatting<filetype>> -syntax_elem_to_formatting(const eMTLSyntaxElement key) +template<typename... T> +constexpr FormattingSyntax syntax_elem_to_formatting(const eMTLSyntaxElement key) { switch (key) { case eMTLSyntaxElement::newmtl: { @@ -261,21 +264,25 @@ syntax_elem_to_formatting(const eMTLSyntaxElement key) } } -template<eFileType filetype> class FileHandler : NonCopyable, NonMovable { +/** + * File format and syntax agnostic file writer. + */ +template<eFileType filetype> class FormattedFileHandler : NonCopyable, NonMovable { private: - FILE *outfile_ = nullptr; + std::FILE *outfile_ = nullptr; std::string outfile_path_; public: - FileHandler(std::string outfile_path) noexcept(false) : outfile_path_(std::move(outfile_path)) + FormattedFileHandler(std::string outfile_path) noexcept(false) + : outfile_path_(std::move(outfile_path)) { outfile_ = std::fopen(outfile_path_.c_str(), "w"); if (!outfile_) { - throw std::system_error(errno, std::system_category(), "Cannot open file"); + throw std::system_error(errno, std::system_category(), "Cannot open file " + outfile_path_); } } - ~FileHandler() + ~FormattedFileHandler() { if (outfile_ && std::fclose(outfile_)) { std::cerr << "Error: could not close the file '" << outfile_path_ @@ -283,17 +290,24 @@ template<eFileType filetype> class FileHandler : NonCopyable, NonMovable { } } + /** + * Example invocation: `writer->write<eMTLSyntaxElement::newmtl>("foo")`. + * + * \param key Must match what the instance's filetype expects; i.e., `eMTLSyntaxElement` for + * `eFileType::MTL`. + */ template<typename FileTypeTraits<filetype>::SyntaxType key, typename... T> constexpr void write(T &&...args) const { - constexpr Formatting<filetype> fmt_nargs_valid = syntax_elem_to_formatting<filetype, T...>( - key); - write__impl<fmt_nargs_valid.total_args>(fmt_nargs_valid.fmt, std::forward<T>(args)...); - /* Types of all arguments and the number of arguments should match - * what the formatting specifies. */ - return std::enable_if_t < fmt_nargs_valid.is_type_valid && - (sizeof...(T) == fmt_nargs_valid.total_args), - void > (); + /* Get format syntax, number of arguments expected and whether types of given arguments are + * valid. + */ + constexpr FormattingSyntax fmt_nargs_valid = syntax_elem_to_formatting<T...>(key); + BLI_STATIC_ASSERT(fmt_nargs_valid.are_types_valid && + (sizeof...(T) == fmt_nargs_valid.total_args), + "Types of all arguments and the number of arguments should match what the " + "formatting specifies."); + write_impl(fmt_nargs_valid.fmt, std::forward<T>(args)...); } private: @@ -301,11 +315,11 @@ template<eFileType filetype> class FileHandler : NonCopyable, NonMovable { template<typename T> using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>; /** - * Make #std::string etc., usable for `fprintf` family. + * Make #std::string etc., usable for `fprintf` family. int float etc. are not affected. * \return: `const char *` or the original argument if the argument is * not related to #std::string. */ - template<typename T> constexpr auto string_to_primitive(T &&arg) const + template<typename T> constexpr auto convert_to_primitive(T &&arg) const { if constexpr (std::is_same_v<remove_cvref_t<T>, std::string> || std::is_same_v<remove_cvref_t<T>, blender::StringRefNull>) { @@ -319,21 +333,19 @@ template<eFileType filetype> class FileHandler : NonCopyable, NonMovable { return; } else { + /* For int, float etc. */ return std::forward<T>(arg); } } - template<int total_args, typename... T> - constexpr std::enable_if_t<(total_args != 0), void> write__impl(const char *fmt, - T &&...args) const - { - std::fprintf(outfile_, fmt, string_to_primitive(std::forward<T>(args))...); - } - template<int total_args, typename... T> - constexpr std::enable_if_t<(total_args == 0), void> write__impl(const char *fmt, - T &&...args) const + template<typename... T> constexpr void write_impl(const char *fmt, T &&...args) const { - std::fputs(fmt, outfile_); + if constexpr (sizeof...(T) == 0) { + std::fputs(fmt, outfile_); + } + else { + std::fprintf(outfile_, fmt, convert_to_primitive(std::forward<T>(args))...); + } } }; diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index da07f1043a7..fcae1009c8b 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -35,7 +35,9 @@ struct bNodeType; #define DEF_ENUM(id) extern const EnumPropertyItem id[]; #include "RNA_enum_items.h" -extern const EnumPropertyItem *rna_enum_attribute_domain_itemf(struct ID *id, bool *r_free); +extern const EnumPropertyItem *rna_enum_attribute_domain_itemf(struct ID *id, + bool include_instances, + bool *r_free); /** * For ID filters (#FILTER_ID_AC, #FILTER_ID_AR, ...) an int isn't enough. This version allows 64 diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c index 78c15444308..35da353a043 100644 --- a/source/blender/makesrna/intern/rna_attribute.c +++ b/source/blender/makesrna/intern/rna_attribute.c @@ -166,7 +166,9 @@ static int rna_Attribute_type_get(PointerRNA *ptr) return layer->type; } -const EnumPropertyItem *rna_enum_attribute_domain_itemf(ID *id, bool *r_free) +const EnumPropertyItem *rna_enum_attribute_domain_itemf(ID *id, + bool include_instances, + bool *r_free) { EnumPropertyItem *item = NULL; const EnumPropertyItem *domain_item = NULL; @@ -188,6 +190,9 @@ const EnumPropertyItem *rna_enum_attribute_domain_itemf(ID *id, bool *r_free) if (id_type == ID_ME && ELEM(domain_item->value, ATTR_DOMAIN_CURVE)) { continue; } + if (!include_instances && domain_item->value == ATTR_DOMAIN_INSTANCE) { + continue; + } if (domain_item->value == ATTR_DOMAIN_POINT && id_type == ID_ME) { RNA_enum_item_add(&item, &totitem, &mesh_vertex_domain_item); @@ -207,7 +212,7 @@ static const EnumPropertyItem *rna_Attribute_domain_itemf(bContext *UNUSED(C), PropertyRNA *UNUSED(prop), bool *r_free) { - return rna_enum_attribute_domain_itemf(ptr->owner_id, r_free); + return rna_enum_attribute_domain_itemf(ptr->owner_id, true, r_free); } static int rna_Attribute_domain_get(PointerRNA *ptr) diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 8c128292fd7..c64a47fc2ab 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -965,7 +965,7 @@ static void rna_wmKeyMapItem_keymodifier_set(PointerRNA *ptr, int value) if (value == EVT_ESCKEY) { /* pass */ } - else if (value >= EVT_AKEY) { + else if (ISKEYBOARD(value) && !ISKEYMODIFIER(value)) { kmi->keymodifier = value; } else { diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 8f6ab1eeb13..0d0e9462891 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -101,6 +101,7 @@ void register_node_type_geo_dual_mesh(void); void register_node_type_geo_edge_split(void); void register_node_type_geo_extrude_mesh(void); void register_node_type_geo_field_at_index(void); +void register_node_type_geo_flip_faces(void); void register_node_type_geo_geometry_to_instance(void); void register_node_type_geo_image_texture(void); void register_node_type_geo_input_curve_handles(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 5c2814e5a33..eae9e5e63ee 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -356,6 +356,7 @@ DefNode(GeometryNode, GEO_NODE_EXTRUDE_MESH, def_geo_extrude_mesh, "EXTRUDE_MESH DefNode(GeometryNode, GEO_NODE_FIELD_AT_INDEX, def_geo_field_at_index, "FIELD_AT_INDEX", FieldAtIndex, "Field at Index", "") DefNode(GeometryNode, GEO_NODE_FILL_CURVE, def_geo_curve_fill, "FILL_CURVE", FillCurve, "Fill Curve", "") DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE", FilletCurve, "Fillet Curve", "") +DefNode(GeometryNode, GEO_NODE_FLIP_FACES, 0, "FLIP_FACES", FlipFaces, "Flip Faces", "") DefNode(GeometryNode, GEO_NODE_GEOMETRY_TO_INSTANCE, 0, "GEOMETRY_TO_INSTANCE", GeometryToInstance, "Geometry to Instance", "") DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "") DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES", InputCurveHandlePositions, "Curve Handle Positions", "") diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt index 6112f358fe9..ab1fefb5ad9 100644 --- a/source/blender/nodes/geometry/CMakeLists.txt +++ b/source/blender/nodes/geometry/CMakeLists.txt @@ -119,6 +119,7 @@ set(SRC nodes/node_geo_edge_split.cc nodes/node_geo_extrude_mesh.cc nodes/node_geo_field_at_index.cc + nodes/node_geo_flip_faces.cc nodes/node_geo_geometry_to_instance.cc nodes/node_geo_image_texture.cc nodes/node_geo_input_curve_handles.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc new file mode 100644 index 00000000000..41970d75dfe --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc @@ -0,0 +1,114 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_attribute_math.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_flip_faces_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output<decl::Geometry>(N_("Mesh")); +} + +static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selection_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); + if (domain_size == 0) { + return; + } + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(selection_field); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_as_mask(0); + + Mesh *mesh = component.get_for_write(); + + mesh->mloop = (MLoop *)CustomData_duplicate_referenced_layer( + &mesh->ldata, CD_MLOOP, mesh->totloop); + Span<MPoly> polys{mesh->mpoly, mesh->totpoly}; + MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; + + for (const int i : selection.index_range()) { + const MPoly &poly = polys[selection[i]]; + int start = poly.loopstart; + for (const int j : IndexRange(poly.totloop / 2)) { + const int index1 = start + j + 1; + const int index2 = start + poly.totloop - j - 1; + std::swap(loops[index1].v, loops[index2].v); + std::swap(loops[index1 - 1].e, loops[index2].e); + } + } + + component.attribute_foreach( + [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (meta_data.domain == ATTR_DOMAIN_CORNER) { + OutputAttribute attribute = component.attribute_try_get_for_output( + attribute_id, ATTR_DOMAIN_CORNER, meta_data.data_type, nullptr); + attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + MutableSpan<T> dst_span = attribute.as_span<T>(); + for (const int j : selection.index_range()) { + const MPoly &poly = polys[selection[j]]; + dst_span.slice(poly.loopstart + 1, poly.totloop - 1).reverse(); + } + }); + attribute.save(); + } + return true; + }); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); + + const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_mesh()) { + return; + } + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + mesh_flip_faces(mesh_component, selection_field); + }); + + params.set_output("Mesh", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_flip_faces_cc + +void register_node_type_geo_flip_faces() +{ + namespace file_ns = blender::nodes::node_geo_flip_faces_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_FLIP_FACES, "Flip Faces", NODE_CLASS_GEOMETRY); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc index 629279a44e9..68bb93bbb64 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc @@ -27,10 +27,13 @@ namespace blender::nodes::node_geo_input_mesh_island_cc { static void node_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Int>(N_("Index")) + b.add_output<decl::Int>(N_("Island Index")) .field_source() .description(N_("Island indices are based on the order of the lowest-numbered vertex " "contained in each island")); + b.add_output<decl::Int>(N_("Island Count")) + .field_source() + .description(N_("The total number of mesh islands")); } class IslandFieldInput final : public GeometryFieldInput { @@ -81,10 +84,63 @@ class IslandFieldInput final : public GeometryFieldInput { } }; +class IslandCountFieldInput final : public GeometryFieldInput { + public: + IslandCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Island Count") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const final + { + if (component.type() != GEO_COMPONENT_TYPE_MESH) { + return {}; + } + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + const Mesh *mesh = mesh_component.get_for_read(); + if (mesh == nullptr) { + return {}; + } + + DisjointSet islands(mesh->totvert); + for (const int i : IndexRange(mesh->totedge)) { + islands.join(mesh->medge[i].v1, mesh->medge[i].v2); + } + + Set<int> island_list; + for (const int i_vert : IndexRange(mesh->totvert)) { + const int64_t root = islands.find_root(i_vert); + island_list.add(root); + } + + return VArray<int>::ForSingle(island_list.size(), + mesh_component.attribute_domain_size(domain)); + } + + uint64_t hash() const override + { + /* Some random hash. */ + return 45634572457; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const IslandCountFieldInput *>(&other) != nullptr; + } +}; + static void node_geo_exec(GeoNodeExecParams params) { - Field<int> island_field{std::make_shared<IslandFieldInput>()}; - params.set_output("Index", std::move(island_field)); + if (params.output_is_required("Island Index")) { + Field<int> field{std::make_shared<IslandFieldInput>()}; + params.set_output("Island Index", std::move(field)); + } + if (params.output_is_required("Island Count")) { + Field<int> field{std::make_shared<IslandCountFieldInput>()}; + params.set_output("Island Count", std::move(field)); + } } } // namespace blender::nodes::node_geo_input_mesh_island_cc diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index 4ed1b7b9d0d..e58e051c66e 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -29,7 +29,7 @@ extern "C" { #endif -/* customdata type */ +/** #wmEvent.customdata type */ enum { EVT_DATA_TIMER = 2, EVT_DATA_DRAGDROP = 3, @@ -37,7 +37,11 @@ enum { EVT_DATA_XR = 5, }; -/* tablet active, matches GHOST_TTabletMode */ +/** + * #wmTabletData.active tablet active, matches #GHOST_TTabletMode. + * + * Typically access via `event->tablet.active`. + */ enum { EVT_TABLET_NONE = 0, EVT_TABLET_STYLUS = 1, @@ -95,10 +99,14 @@ enum { TABLET_STYLUS = 0x001a, TABLET_ERASER = 0x001b, - /* *** Start of keyboard codes. *** */ +/* *** Start of keyboard codes. *** */ + +/* Minimum keyboard value (inclusive). */ +#define _EVT_KEYBOARD_MIN 0x0020 /* Standard keyboard. - * From 0x0020 to 0x00ff, and 0x012c to 0x0143 for function keys! */ + * - 0x0020 to 0x00ff [#_EVT_KEYBOARD_MIN to #_EVT_KEYBOARD_MAX] inclusive - for keys. + * - 0x012c to 0x0143 [#EVT_F1KEY to #EVT_F24KEY] inclusive - for function keys. */ EVT_ZEROKEY = 0x0030, /* '0' (48). */ EVT_ONEKEY = 0x0031, /* '1' (49). */ @@ -210,6 +218,12 @@ enum { EVT_LEFTBRACKETKEY = 0x00eb, /* 235 */ EVT_RIGHTBRACKETKEY = 0x00ec, /* 236 */ +/* Maximum keyboard value (inclusive). */ +#define _EVT_KEYBOARD_MAX 0x00ff /* 255 */ + + /* WARNING: 0x010x are used for internal events + * (but are still stored in the key-map). */ + EVT_F1KEY = 0x012c, /* 300 */ EVT_F2KEY = 0x012d, /* 301 */ EVT_F3KEY = 0x012e, /* 302 */ @@ -237,60 +251,65 @@ enum { /* *** End of keyboard codes. *** */ - /* NDOF (from SpaceNavigator & friends) - * These should be kept in sync with GHOST_NDOFManager.h + /* NDOF (from "Space Navigator" & friends) + * These must be kept in sync with `GHOST_NDOFManager.h`. * Ordering matters, exact values do not. */ + NDOF_MOTION = 0x0190, /* 400 */ + +#define _NDOF_MIN NDOF_MOTION + /* used internally, never sent */ NDOF_BUTTON_NONE = NDOF_MOTION, /* these two are available from any 3Dconnexion device */ - NDOF_BUTTON_MENU, - NDOF_BUTTON_FIT, + + NDOF_BUTTON_MENU = 0x0191, /* 401 */ + NDOF_BUTTON_FIT = 0x0192, /* 402 */ /* standard views */ - NDOF_BUTTON_TOP, - NDOF_BUTTON_BOTTOM, - NDOF_BUTTON_LEFT, - NDOF_BUTTON_RIGHT, - NDOF_BUTTON_FRONT, - NDOF_BUTTON_BACK, + NDOF_BUTTON_TOP = 0x0193, /* 403 */ + NDOF_BUTTON_BOTTOM = 0x0194, /* 404 */ + NDOF_BUTTON_LEFT = 0x0195, /* 405 */ + NDOF_BUTTON_RIGHT = 0x0196, /* 406 */ + NDOF_BUTTON_FRONT = 0x0197, /* 407 */ + NDOF_BUTTON_BACK = 0x0198, /* 408 */ /* more views */ - NDOF_BUTTON_ISO1, - NDOF_BUTTON_ISO2, + NDOF_BUTTON_ISO1 = 0x0199, /* 409 */ + NDOF_BUTTON_ISO2 = 0x019a, /* 410 */ /* 90 degree rotations */ - NDOF_BUTTON_ROLL_CW, - NDOF_BUTTON_ROLL_CCW, - NDOF_BUTTON_SPIN_CW, - NDOF_BUTTON_SPIN_CCW, - NDOF_BUTTON_TILT_CW, - NDOF_BUTTON_TILT_CCW, + NDOF_BUTTON_ROLL_CW = 0x019b, /* 411 */ + NDOF_BUTTON_ROLL_CCW = 0x019c, /* 412 */ + NDOF_BUTTON_SPIN_CW = 0x019d, /* 413 */ + NDOF_BUTTON_SPIN_CCW = 0x019e, /* 414 */ + NDOF_BUTTON_TILT_CW = 0x019f, /* 415 */ + NDOF_BUTTON_TILT_CCW = 0x01a0, /* 416 */ /* device control */ - NDOF_BUTTON_ROTATE, - NDOF_BUTTON_PANZOOM, - NDOF_BUTTON_DOMINANT, - NDOF_BUTTON_PLUS, - NDOF_BUTTON_MINUS, + NDOF_BUTTON_ROTATE = 0x01a1, /* 417 */ + NDOF_BUTTON_PANZOOM = 0x01a2, /* 418 */ + NDOF_BUTTON_DOMINANT = 0x01a3, /* 419 */ + NDOF_BUTTON_PLUS = 0x01a4, /* 420 */ + NDOF_BUTTON_MINUS = 0x01a5, /* 421 */ /* keyboard emulation */ - NDOF_BUTTON_ESC, - NDOF_BUTTON_ALT, - NDOF_BUTTON_SHIFT, - NDOF_BUTTON_CTRL, + NDOF_BUTTON_ESC = 0x01a6, /* 422 */ + NDOF_BUTTON_ALT = 0x01a7, /* 423 */ + NDOF_BUTTON_SHIFT = 0x01a8, /* 424 */ + NDOF_BUTTON_CTRL = 0x01a9, /* 425 */ /* general-purpose buttons */ - NDOF_BUTTON_1, - NDOF_BUTTON_2, - NDOF_BUTTON_3, - NDOF_BUTTON_4, - NDOF_BUTTON_5, - NDOF_BUTTON_6, - NDOF_BUTTON_7, - NDOF_BUTTON_8, - NDOF_BUTTON_9, - NDOF_BUTTON_10, + NDOF_BUTTON_1 = 0x01aa, /* 426 */ + NDOF_BUTTON_2 = 0x01ab, /* 427 */ + NDOF_BUTTON_3 = 0x01ac, /* 428 */ + NDOF_BUTTON_4 = 0x01ad, /* 429 */ + NDOF_BUTTON_5 = 0x01ae, /* 430 */ + NDOF_BUTTON_6 = 0x01af, /* 431 */ + NDOF_BUTTON_7 = 0x01b0, /* 432 */ + NDOF_BUTTON_8 = 0x01b1, /* 433 */ + NDOF_BUTTON_9 = 0x01b2, /* 434 */ + NDOF_BUTTON_10 = 0x01b3, /* 435 */ /* more general-purpose buttons */ - NDOF_BUTTON_A, - NDOF_BUTTON_B, - NDOF_BUTTON_C, - /* the end */ - NDOF_LAST, + NDOF_BUTTON_A = 0x01b4, /* 436 */ + NDOF_BUTTON_B = 0x01b5, /* 437 */ + NDOF_BUTTON_C = 0x01b6, /* 438 */ + +#define _NDOF_MAX NDOF_BUTTON_C /* ********** End of Input devices. ********** */ @@ -318,13 +337,13 @@ enum { EVT_ACTIONZONE_REGION = 0x5001, /* 20481 */ EVT_ACTIONZONE_FULLSCREEN = 0x5011, /* 20497 */ - /* NOTE: these values are saved in keymap files, do not change them but just add new ones */ + /* NOTE: these values are saved in key-map files, do not change them but just add new ones. */ /* Tweak events: * Sent as additional event with the mouse coordinates * from where the initial click was placed. */ - /* tweak events for L M R mousebuttons */ + /* Tweak events for L M R mouse-buttons. */ EVT_TWEAK_L = 0x5002, /* 20482 */ EVT_TWEAK_M = 0x5003, /* 20483 */ EVT_TWEAK_R = 0x5004, /* 20484 */ @@ -348,9 +367,11 @@ enum { /* ********** End of Blender internal events. ********** */ }; -/* *********** wmEvent.type helpers. ********** */ +/* -------------------------------------------------------------------- */ +/** \name #wmEvent.type Helpers + * \{ */ -/* test whether the event is timer event */ +/** Test whether the event is timer event. */ #define ISTIMER(event_type) ((event_type) >= TIMER && (event_type) <= TIMERF) /* for event checks */ @@ -359,22 +380,24 @@ enum { // #define ISTEXTINPUT(event_type) ((event_type) >= ' ' && (event_type) <= 255) /* NOTE: an alternative could be to check `event->utf8_buf`. */ -/* test whether the event is a key on the keyboard */ +/** Test whether the event is a key on the keyboard (including modifier keys). */ #define ISKEYBOARD(event_type) \ - (((event_type) >= 0x0020 && (event_type) <= 0x00ff) || \ - ((event_type) >= 0x012c && (event_type) <= 0x0143)) + (((event_type) >= _EVT_KEYBOARD_MIN && (event_type) <= _EVT_KEYBOARD_MAX) || \ + ((event_type) >= EVT_F1KEY && (event_type) <= EVT_F24KEY)) -/* test whether the event is a modifier key */ +/** Test whether the event is a modifier key. */ #define ISKEYMODIFIER(event_type) \ (((event_type) >= EVT_LEFTCTRLKEY && (event_type) <= EVT_LEFTSHIFTKEY) || \ (event_type) == EVT_OSKEY) -/* test whether the event is a mouse button */ +/** Test whether the event is a mouse button. */ #define ISMOUSE(event_type) \ (((event_type) >= LEFTMOUSE && (event_type) <= BUTTON7MOUSE) || (event_type) == MOUSESMARTZOOM) - +/** Test whether the event is a mouse wheel. */ #define ISMOUSE_WHEEL(event_type) ((event_type) >= WHEELUPMOUSE && (event_type) <= WHEELOUTMOUSE) +/** Test whether the event is a mouse (track-pad) gesture. */ #define ISMOUSE_GESTURE(event_type) ((event_type) >= MOUSEPAN && (event_type) <= MOUSEROTATE) +/** Test whether the event is a mouse button (excluding mouse-wheel). */ #define ISMOUSE_BUTTON(event_type) \ (ELEM(event_type, \ LEFTMOUSE, \ @@ -385,16 +408,16 @@ enum { BUTTON6MOUSE, \ BUTTON7MOUSE)) -/* test whether the event is tweak event */ +/** Test whether the event is tweak event. */ #define ISTWEAK(event_type) ((event_type) >= EVT_TWEAK_L && (event_type) <= EVT_TWEAK_R) -/* test whether the event is a NDOF event */ -#define ISNDOF(event_type) ((event_type) >= NDOF_MOTION && (event_type) < NDOF_LAST) +/** Test whether the event is a NDOF event. */ +#define ISNDOF(event_type) ((event_type) >= _NDOF_MIN && (event_type) <= _NDOF_MAX) #define IS_EVENT_ACTIONZONE(event_type) \ ELEM(event_type, EVT_ACTIONZONE_AREA, EVT_ACTIONZONE_REGION, EVT_ACTIONZONE_FULLSCREEN) -/* test whether event type is acceptable as hotkey, excluding modifiers */ +/** Test whether event type is acceptable as hotkey (excluding modifiers). */ #define ISHOTKEY(event_type) \ ((ISKEYBOARD(event_type) || ISMOUSE(event_type) || ISNDOF(event_type)) && \ (ISKEYMODIFIER(event_type) == false)) @@ -409,23 +432,23 @@ enum { #define IS_EVENT_MOD(...) VA_NARGS_CALL_OVERLOAD(_VA_IS_EVENT_MOD, __VA_ARGS__) enum eEventType_Mask { - /* ISKEYMODIFIER */ + /** #ISKEYMODIFIER */ EVT_TYPE_MASK_KEYBOARD_MODIFIER = (1 << 0), - /* ISKEYBOARD */ + /** #ISKEYBOARD */ EVT_TYPE_MASK_KEYBOARD = (1 << 1), - /* ISMOUSE_WHEEL */ + /** #ISMOUSE_WHEEL */ EVT_TYPE_MASK_MOUSE_WHEEL = (1 << 2), - /* ISMOUSE_BUTTON */ + /** #ISMOUSE_BUTTON */ EVT_TYPE_MASK_MOUSE_GESTURE = (1 << 3), - /* ISMOUSE_GESTURE */ + /** #ISMOUSE_GESTURE */ EVT_TYPE_MASK_MOUSE_BUTTON = (1 << 4), - /* ISMOUSE */ + /** #ISMOUSE */ EVT_TYPE_MASK_MOUSE = (1 << 5), - /* ISNDOF */ + /** #ISNDOF */ EVT_TYPE_MASK_NDOF = (1 << 6), - /* ISTWEAK */ + /** #ISTWEAK */ EVT_TYPE_MASK_TWEAK = (1 << 7), - /* IS_EVENT_ACTIONZONE */ + /** #IS_EVENT_ACTIONZONE */ EVT_TYPE_MASK_ACTIONZONE = (1 << 8), }; #define EVT_TYPE_MASK_ALL \ @@ -438,7 +461,11 @@ enum eEventType_Mask { bool WM_event_type_mask_test(int event_type, enum eEventType_Mask mask); -/* ********** wmEvent.val ********** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #wmEvent.val Values + * \{ */ /* Gestures */ /* NOTE: these values are saved in keymap files, do not change them but just add new ones */ @@ -505,6 +532,8 @@ enum { GESTURE_MODAL_FLIP = 14, }; +/** \} */ + #ifdef __cplusplus } #endif |