Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Kim <pk15950@gmail.com>2022-09-08 07:00:43 +0300
committerPeter Kim <pk15950@gmail.com>2022-09-08 07:00:43 +0300
commite83f88c951e26b01893acee4f24b40a46062a41c (patch)
tree09f60bf7d4fc2ae1ef50668823e416fc675b252e
parentec84e3294593e2e26475f18c81e847bf00dc201e (diff)
parent0cd92169d40ae1c7e103ff269e850eaf1b901646 (diff)
Merge branch 'master' into xr-devxr-dev
-rw-r--r--add_camera_rigs/__init__.py4
-rw-r--r--add_camera_rigs/operators.py28
-rw-r--r--add_camera_rigs/ui_panels.py33
-rw-r--r--add_mesh_extra_objects/Blocks.py6
-rw-r--r--amaranth/__init__.py2
-rw-r--r--amaranth/scene/current_blend.py2
-rwxr-xr-xamaranth/scene/debug.py47
-rw-r--r--amaranth/scene/goto_library.py4
-rw-r--r--amaranth/scene/save_reload.py21
-rw-r--r--animation_animall/__init__.py (renamed from animation_animall.py)460
-rw-r--r--animation_animall/translations.py364
-rw-r--r--ant_landscape/__init__.py726
-rw-r--r--ant_landscape/add_mesh_ant_landscape.py838
-rw-r--r--ant_landscape/ant_functions.py621
-rw-r--r--ant_landscape/ant_noise.py187
-rw-r--r--ant_landscape/eroder.py411
-rw-r--r--ant_landscape/mesh_ant_displace.py759
-rw-r--r--ant_landscape/stats.py7
-rw-r--r--ant_landscape/test.py2
-rw-r--r--ant_landscape/utils.py4
-rw-r--r--archimesh/__init__.py4
-rw-r--r--archimesh/achm_gltools.py24
-rw-r--r--archimesh/achm_main_panel.py3
-rw-r--r--btrace/__init__.py2
-rw-r--r--btrace/bTrace.py4
-rw-r--r--curve_assign_shapekey.py8
-rw-r--r--curve_tools/auto_loft.py1
-rw-r--r--curve_tools/operators.py9
-rw-r--r--curve_tools/path_finder.py13
-rw-r--r--curve_tools/show_resolution.py1
-rw-r--r--curve_tools/splines_sequence.py4
-rw-r--r--depsgraph_debug.py32
-rw-r--r--greasepencil_tools/__init__.py4
-rw-r--r--greasepencil_tools/rotate_canvas.py9
-rw-r--r--greasepencil_tools/timeline_scrub.py13
-rw-r--r--io_import_images_as_planes.py98
-rw-r--r--io_mesh_stl/blender_utils.py2
-rw-r--r--io_mesh_stl/stl_utils.py2
-rw-r--r--io_mesh_uv_layout/__init__.py12
-rw-r--r--io_mesh_uv_layout/export_uv_eps.py6
-rw-r--r--io_mesh_uv_layout/export_uv_png.py47
-rw-r--r--io_mesh_uv_layout/export_uv_svg.py6
-rw-r--r--io_scene_fbx/__init__.py2
-rw-r--r--io_scene_fbx/export_fbx_bin.py6
-rw-r--r--io_scene_fbx/import_fbx.py2
-rwxr-xr-xio_scene_gltf2/__init__.py152
-rw-r--r--io_scene_gltf2/blender/com/gltf2_blender_default.py6
-rwxr-xr-xio_scene_gltf2/blender/com/gltf2_blender_material_helpers.py14
-rw-r--r--io_scene_gltf2/blender/com/gltf2_blender_ui.py442
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_export_keys.py1
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_extract.py43
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather.py9
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_animation_channels.py2
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py22
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py13
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py60
-rw-r--r--io_scene_gltf2/blender/exp/gltf2_blender_gather_drivers.py5
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_image.py95
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py260
-rw-r--r--io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_clearcoat.py81
-rw-r--r--io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_emission.py61
-rw-r--r--io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_ior.py35
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_materials_pbr_metallic_roughness.py8
-rw-r--r--io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_sheen.py68
-rw-r--r--io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_specular.py168
-rw-r--r--io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_transmission.py47
-rw-r--r--io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_unlit.py6
-rw-r--r--io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_variants.py18
-rw-r--r--io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_volume.py75
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_primitive_attributes.py31
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_primitives.py58
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_texture.py17
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_texture_info.py44
-rw-r--r--io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py55
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_get.py27
-rw-r--r--io_scene_gltf2/blender/exp/gltf2_blender_image.py46
-rw-r--r--io_scene_gltf2/blender/exp/gltf2_blender_texture_specular.py94
-rw-r--r--io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_ior.py12
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_pbrSpecularGlossiness.py17
-rw-r--r--io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_sheen.py88
-rw-r--r--io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_specular.py356
-rw-r--r--io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_transmission.py64
-rw-r--r--io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_unlit.py5
-rw-r--r--io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_volume.py82
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_gltf.py28
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_image.py2
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_material.py5
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_mesh.py56
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py218
-rw-r--r--io_scene_gltf2/blender/imp/gltf2_blender_texture.py19
-rwxr-xr-xio_scene_gltf2/io/com/gltf2_io_constants.py5
-rw-r--r--io_scene_gltf2/io/com/gltf2_io_draco_compression_extension.py15
-rw-r--r--io_scene_gltf2/io/com/gltf2_io_variants.py29
-rwxr-xr-xio_scene_gltf2/io/exp/gltf2_io_binary_data.py7
-rwxr-xr-xio_scene_gltf2/io/exp/gltf2_io_buffer.py2
-rwxr-xr-xio_scene_gltf2/io/imp/gltf2_io_gltf.py9
-rw-r--r--io_scene_obj/__init__.py6
-rw-r--r--io_scene_obj/import_obj.py4
-rw-r--r--io_scene_x3d/import_x3d.py4
-rw-r--r--magic_uv/common.py7
-rw-r--r--magic_uv/op/copy_paste_uv_object.py9
-rw-r--r--magic_uv/utils/graph.py2
-rw-r--r--materials_utils/functions.py2
-rw-r--r--measureit/__init__.py4
-rw-r--r--measureit/measureit_geometry.py7
-rw-r--r--measureit/measureit_main.py9
-rw-r--r--measureit/measureit_render.py10
-rw-r--r--mesh_bsurfaces.py4
-rw-r--r--mesh_snap_utilities_line/op_line.py28
-rw-r--r--mesh_tissue/utils.py4
-rw-r--r--mesh_tissue/weight_tools.py6
-rw-r--r--node_wrangler.py1233
-rw-r--r--object_boolean_tools.py2
-rw-r--r--object_collection_manager/__init__.py2
-rw-r--r--object_collection_manager/cm_init.py4
-rw-r--r--object_collection_manager/operator_utils.py6
-rw-r--r--object_collection_manager/qcd_init.py74
-rw-r--r--object_fracture_cell/__init__.py2
-rw-r--r--object_fracture_cell/fracture_cell_setup.py2
-rw-r--r--object_print3d_utils/export.py22
-rw-r--r--object_print3d_utils/mesh_helpers.py8
-rw-r--r--object_print3d_utils/operators.py47
-rw-r--r--object_scatter/__init__.py4
-rw-r--r--object_scatter/operator.py11
-rw-r--r--pose_library/__init__.py3
-rwxr-xr-xpower_sequencer/__init__.py9
-rw-r--r--power_sequencer/addon_preferences.py5
-rw-r--r--power_sequencer/addon_properties.py9
-rw-r--r--power_sequencer/handlers.py40
-rwxr-xr-xpower_sequencer/operators/__init__.py7
-rw-r--r--power_sequencer/operators/channel_offset.py14
-rw-r--r--power_sequencer/operators/concatenate_strips.py5
-rw-r--r--power_sequencer/operators/copy_selected_sequences.py5
-rw-r--r--power_sequencer/operators/crossfade_add.py7
-rw-r--r--power_sequencer/operators/crossfade_edit.py5
-rw-r--r--power_sequencer/operators/cut_strips_under_cursor.py5
-rw-r--r--power_sequencer/operators/delete_direct.py5
-rw-r--r--power_sequencer/operators/deselect_all_left_or_right.py5
-rw-r--r--power_sequencer/operators/deselect_handles_and_grab.py5
-rw-r--r--power_sequencer/operators/duplicate_move.py5
-rw-r--r--power_sequencer/operators/expand_to_surrounding_cuts.py11
-rw-r--r--power_sequencer/operators/fade_add.py10
-rw-r--r--power_sequencer/operators/fade_clear.py11
-rw-r--r--power_sequencer/operators/gap_remove.py5
-rw-r--r--power_sequencer/operators/grab.py5
-rw-r--r--power_sequencer/operators/grab_closest_handle_or_cut.py5
-rw-r--r--power_sequencer/operators/grab_sequence_handles.py9
-rw-r--r--power_sequencer/operators/import_local_footage.py5
-rw-r--r--power_sequencer/operators/jump_time_offset.py5
-rw-r--r--power_sequencer/operators/jump_to_cut.py62
-rw-r--r--power_sequencer/operators/make_hold_frame.py5
-rw-r--r--power_sequencer/operators/marker_delete_closest.py5
-rw-r--r--power_sequencer/operators/marker_delete_direct.py5
-rw-r--r--power_sequencer/operators/marker_snap_to_cursor.py5
-rw-r--r--power_sequencer/operators/markers_as_timecodes.py5
-rw-r--r--power_sequencer/operators/markers_create_from_selected.py5
-rw-r--r--power_sequencer/operators/markers_set_preview_in_between.py5
-rw-r--r--power_sequencer/operators/markers_snap_matching_strips.py5
-rw-r--r--power_sequencer/operators/meta_resize_to_content.py5
-rw-r--r--power_sequencer/operators/meta_trim_content_to_bounds.py5
-rw-r--r--power_sequencer/operators/meta_ungroup_and_trim.py5
-rw-r--r--power_sequencer/operators/mouse_toggle_mute.py5
-rw-r--r--power_sequencer/operators/mouse_trim_instantly.py5
-rw-r--r--power_sequencer/operators/mouse_trim_modal.py14
-rw-r--r--power_sequencer/operators/open_project_directory.py5
-rw-r--r--power_sequencer/operators/playback_speed_decrease.py52
-rw-r--r--power_sequencer/operators/playback_speed_increase.py52
-rw-r--r--power_sequencer/operators/playback_speed_set.py17
-rw-r--r--power_sequencer/operators/preview_closest_cut.py5
-rw-r--r--power_sequencer/operators/preview_to_selection.py5
-rw-r--r--power_sequencer/operators/render_apply_preset.py5
-rw-r--r--power_sequencer/operators/render_presets/twitter_720p.py5
-rw-r--r--power_sequencer/operators/render_presets/youtube_1080.py5
-rw-r--r--power_sequencer/operators/ripple_delete.py5
-rw-r--r--power_sequencer/operators/save_direct.py5
-rw-r--r--power_sequencer/operators/scene_create_from_selection.py5
-rw-r--r--power_sequencer/operators/scene_cycle.py5
-rw-r--r--power_sequencer/operators/scene_merge_from.py5
-rw-r--r--power_sequencer/operators/scene_open_from_strip.py5
-rw-r--r--power_sequencer/operators/scene_rename_with_strip.py5
-rw-r--r--power_sequencer/operators/select_all_left_or_right.py13
-rw-r--r--power_sequencer/operators/select_closest_to_mouse.py5
-rw-r--r--power_sequencer/operators/select_linked_effect.py5
-rw-r--r--power_sequencer/operators/select_linked_strips.py5
-rw-r--r--power_sequencer/operators/select_related_strips.py5
-rw-r--r--power_sequencer/operators/select_strips_under_cursor.py5
-rw-r--r--power_sequencer/operators/set_timeline_range.py5
-rw-r--r--power_sequencer/operators/snap.py11
-rw-r--r--power_sequencer/operators/snap_selection.py11
-rw-r--r--power_sequencer/operators/space_sequences.py5
-rw-r--r--power_sequencer/operators/speed_remove_effect.py5
-rw-r--r--power_sequencer/operators/speed_up_movie_strip.py23
-rw-r--r--power_sequencer/operators/swap_strips.py5
-rw-r--r--power_sequencer/operators/toggle_selected_mute.py5
-rw-r--r--power_sequencer/operators/toggle_waveforms.py5
-rw-r--r--power_sequencer/operators/transitions_remove.py5
-rw-r--r--power_sequencer/operators/trim_left_or_right_handles.py5
-rw-r--r--power_sequencer/operators/trim_three_point_edit.py5
-rw-r--r--power_sequencer/operators/trim_to_surrounding_cuts.py5
-rw-r--r--power_sequencer/operators/utils/__init__.py4
-rw-r--r--power_sequencer/operators/utils/doc.py5
-rw-r--r--power_sequencer/operators/utils/draw.py5
-rw-r--r--power_sequencer/operators/utils/functions.py31
-rw-r--r--power_sequencer/operators/utils/global_settings.py5
-rw-r--r--power_sequencer/operators/utils/info_progress_bar.py5
-rw-r--r--power_sequencer/operators/value_offset.py10
-rw-r--r--power_sequencer/tools/__init__.py7
-rw-r--r--power_sequencer/tools/trim.py5
-rw-r--r--power_sequencer/ui/__init__.py5
-rw-r--r--power_sequencer/ui/menu_contextual.py5
-rwxr-xr-xpower_sequencer/ui/menu_toolbar.py5
-rw-r--r--power_sequencer/utils/addon_auto_imports.py5
-rw-r--r--power_sequencer/utils/register_shortcuts.py5
-rw-r--r--precision_drawing_tools/__init__.py2
-rw-r--r--precision_drawing_tools/pdt_functions.py3
-rw-r--r--precision_drawing_tools/pdt_tangent.py4
-rw-r--r--render_povray/model_meta_topology.py4
-rw-r--r--render_povray/model_poly_topology.py2
-rw-r--r--render_povray/particles.py2
-rwxr-xr-xrender_povray/render_gui.py2
-rw-r--r--rigify/__init__.py12
-rw-r--r--rigify/generate.py11
-rw-r--r--rigify/metarigs/Animals/horse.py8
-rw-r--r--rigify/metarigs/Animals/wolf.py12
-rw-r--r--rigify/rig_ui_template.py3
-rw-r--r--rigify/rigs/limbs/limb_rigs.py50
-rw-r--r--rigify/rigs/spines/super_head.py2
-rw-r--r--rigify/ui.py16
-rw-r--r--rigify/utils/collections.py2
-rw-r--r--rigify/utils/node_merger.py4
-rw-r--r--rigify/utils/widgets.py3
-rw-r--r--space_view3d_math_vis/__init__.py4
-rw-r--r--space_view3d_math_vis/draw.py7
-rw-r--r--sun_position/__init__.py39
-rw-r--r--sun_position/properties.py3
-rw-r--r--sun_position/translations.py493
-rw-r--r--sun_position/ui_sun.py45
-rw-r--r--system_demo_mode/__init__.py2
-rw-r--r--system_demo_mode/config.py2
-rw-r--r--system_demo_mode/demo_mode.py2
-rw-r--r--system_property_chart.py16
-rw-r--r--viewport_vr_preview/__init__.py2
-rw-r--r--viewport_vr_preview/operators.py13
243 files changed, 6588 insertions, 4389 deletions
diff --git a/add_camera_rigs/__init__.py b/add_camera_rigs/__init__.py
index 878d4555..cbf83d95 100644
--- a/add_camera_rigs/__init__.py
+++ b/add_camera_rigs/__init__.py
@@ -3,8 +3,8 @@
bl_info = {
"name": "Add Camera Rigs",
"author": "Wayne Dixon, Brian Raschko, Kris Wittig, Damien Picard, Flavio Perez",
- "version": (1, 4, 4),
- "blender": (2, 80, 0),
+ "version": (1, 5, 0),
+ "blender": (3, 3, 0),
"location": "View3D > Add > Camera > Dolly or Crane Rig",
"description": "Adds a Camera Rig with UI",
"doc_url": "{BLENDER_MANUAL_URL}/addons/camera/camera_rigs.html",
diff --git a/add_camera_rigs/operators.py b/add_camera_rigs/operators.py
index 18300e08..29e77e3b 100644
--- a/add_camera_rigs/operators.py
+++ b/add_camera_rigs/operators.py
@@ -73,30 +73,16 @@ class ADD_CAMERA_RIGS_OT_add_marker_bind(Operator, CameraRigMixin):
return {'FINISHED'}
-class ADD_CAMERA_RIGS_OT_add_dof_object(Operator, CameraRigMixin):
- bl_idname = "add_camera_rigs.add_dof_object"
- bl_label = "Add DOF Object"
- bl_description = "Create Empty and add as DOF Object"
+class ADD_CAMERA_RIGS_OT_set_dof_bone(Operator, CameraRigMixin):
+ bl_idname = "add_camera_rigs.set_dof_bone"
+ bl_label = "Set DOF Bone"
+ bl_description = "Set the Aim bone as a DOF target"
def execute(self, context):
rig, cam = get_rig_and_cam(context.active_object)
- bone = rig.data.bones['Aim_shape_rotation-MCH']
- # Add Empty
- empty_obj = bpy.data.objects.new("EmptyDOF", None)
- context.scene.collection.objects.link(empty_obj)
-
- # Parent to Aim Child bone
- empty_obj.parent = rig
- empty_obj.parent_type = "BONE"
- empty_obj.parent_bone = "Aim_shape_rotation-MCH"
-
- # Move to bone head
- empty_obj.location = bone.head
-
- # Make this new empty the dof_object
- cam.data.dof.use_dof = True
- cam.data.dof.focus_object = empty_obj
+ cam.data.dof.focus_object = rig
+ cam.data.dof.focus_subtarget = 'Aim_shape_rotation-MCH'
return {'FINISHED'}
@@ -104,7 +90,7 @@ class ADD_CAMERA_RIGS_OT_add_dof_object(Operator, CameraRigMixin):
classes = (
ADD_CAMERA_RIGS_OT_set_scene_camera,
ADD_CAMERA_RIGS_OT_add_marker_bind,
- ADD_CAMERA_RIGS_OT_add_dof_object,
+ ADD_CAMERA_RIGS_OT_set_dof_bone,
)
diff --git a/add_camera_rigs/ui_panels.py b/add_camera_rigs/ui_panels.py
index c6066147..5d545d3a 100644
--- a/add_camera_rigs/ui_panels.py
+++ b/add_camera_rigs/ui_panels.py
@@ -32,20 +32,23 @@ class ADD_CAMERA_RIGS_PT_camera_rig_ui(Panel, CameraRigMixin):
layout.prop(cam_data, "type")
# DoF
- col = layout.column(align=True)
+ col = layout.column(align=False)
col.prop(cam_data.dof, "use_dof")
if cam_data.dof.use_dof:
- if rig["rig_id"].lower() in ("crane_rig", "dolly_rig"):
- if cam_data.dof.focus_object is None:
- col.operator("add_camera_rigs.add_dof_object",
- text="Add DOF Empty", icon="OUTLINER_OB_EMPTY")
- else:
- col.prop(cam_data.dof, "focus_object")
- row = col.row(align=True)
+ sub = col.column(align=True)
+ if cam_data.dof.focus_object is None:
+ sub.operator("add_camera_rigs.set_dof_bone")
+ sub.prop(cam_data.dof, "focus_object")
+ if (cam_data.dof.focus_object is not None
+ and cam_data.dof.focus_object.type == 'ARMATURE'):
+ sub.prop_search(cam_data.dof, "focus_subtarget",
+ cam_data.dof.focus_object.data, "bones")
+ sub = col.column(align=True)
+ row = sub.row(align=True)
row.active = cam_data.dof.focus_object is None
row.prop(pose_bones["Camera"],
'["focus_distance"]', text="Focus Distance")
- col.prop(pose_bones["Camera"],
+ sub.prop(pose_bones["Camera"],
'["aperture_fstop"]', text="F-Stop")
# Viewport display
@@ -74,9 +77,15 @@ class ADD_CAMERA_RIGS_PT_camera_rig_ui(Panel, CameraRigMixin):
if rig["rig_id"].lower() in ("dolly_rig", "crane_rig"):
# Track to Constraint
col = layout.column(align=True)
- col.label(text="Tracking:")
- col.prop(pose_bones["Camera"].constraints["Track To"],
- 'influence', text="Aim Lock", slider=True)
+ track_to_constraint = None
+ for con in pose_bones["Camera"].constraints:
+ if con.type == 'TRACK_TO':
+ track_to_constraint = con
+ break
+ if track_to_constraint is not None:
+ col.label(text="Tracking:")
+ col.prop(track_to_constraint, 'influence',
+ text="Aim Lock", slider=True)
# Crane arm stuff
if rig["rig_id"].lower() == "crane_rig":
diff --git a/add_mesh_extra_objects/Blocks.py b/add_mesh_extra_objects/Blocks.py
index 22676670..c3dcd0d5 100644
--- a/add_mesh_extra_objects/Blocks.py
+++ b/add_mesh_extra_objects/Blocks.py
@@ -269,7 +269,7 @@ def MakeABlock(bounds, segsize, vll=0, Offsets=None, FaceExclude=[],
bounds: a list of boundary positions:
0:left, 1:right, 2:bottom, 3:top, 4:back, 5:front
segsize: the maximum size before lengthwise subdivision occurs
- vll: the number of vertexes already in the mesh. len(mesh.verts) should
+ vll: the number of vertices already in the mesh. len(mesh.verts) should
give this number.
Offsets: list of coordinate delta values.
Offsets are lists, [x,y,z] in
@@ -365,7 +365,7 @@ def MakeAKeystone(xpos, width, zpos, ztop, zbtm, thick, bevel, vll=0, FaceExclud
zbtm: distance from zpos to the bottom
thick: thickness
bevel: the amount to raise the back vertex to account for arch beveling
- vll: the number of vertexes already in the mesh. len(mesh.verts) should give this number
+ vll: the number of vertices already in the mesh. len(mesh.verts) should give this number
faceExclude: list of faces to exclude from the faces list.
0:left, 1:right, 2:bottom, 3:top, 4:back, 5:front
xBevScl: how much to divide the end (+- x axis) bevel dimensions.
@@ -779,7 +779,7 @@ class rowOb:
def arch(ra, rt, x, z, archStart, archEnd, bevel, bevAngle, vll):
__doc__ = """\
- Makes a list of faces and vertexes for arches.
+ Makes a list of faces and vertices for arches.
ra: the radius of the arch, to the center of the bricks
rt: the thickness of the arch
x: x center location of the circular arc, as if the arch opening were centered on x = 0
diff --git a/amaranth/__init__.py b/amaranth/__init__.py
index 06a42c8e..55871cc1 100644
--- a/amaranth/__init__.py
+++ b/amaranth/__init__.py
@@ -74,7 +74,7 @@ from amaranth.misc import (
bl_info = {
"name": "Amaranth Toolset",
"author": "Pablo Vazquez, Bassam Kurdali, Sergey Sharybin, Lukas Tönne, Cesar Saez, CansecoGPC",
- "version": (1, 0, 10),
+ "version": (1, 0, 14),
"blender": (3, 2, 0),
"location": "Everywhere!",
"description": "A collection of tools and settings to improve productivity",
diff --git a/amaranth/scene/current_blend.py b/amaranth/scene/current_blend.py
index 7ab855c4..11ac3679 100644
--- a/amaranth/scene/current_blend.py
+++ b/amaranth/scene/current_blend.py
@@ -36,7 +36,7 @@ class FILEBROWSER_PT_amaranth(bpy.types.Panel):
@classmethod
def poll(cls, context):
- return panel_poll_is_upper_region(context.region)
+ return context.area.ui_type == 'FILES' and panel_poll_is_upper_region(context.region)
def draw(self, context):
layout = self.layout
diff --git a/amaranth/scene/debug.py b/amaranth/scene/debug.py
index 38692743..4f702d08 100755
--- a/amaranth/scene/debug.py
+++ b/amaranth/scene/debug.py
@@ -65,7 +65,8 @@ class AMTH_store_data():
'TEXTURE': [], # Textures (Psys, Brushes)
'MODIFIER': [], # Modifiers
'MESH_DATA': [], # Vertex Colors
- 'VIEW3D': [], # Background Images
+ 'OUTLINER_OB_CAMERA': [], # Background Images in Cameras
+ 'OUTLINER_OB_EMPTY': [], # Empty type Image
'NODETREE': [], # Compositor
}
libraries = [] # Libraries x type
@@ -632,6 +633,7 @@ class AMTH_SCENE_OT_list_users_for_x(Operator):
if name not in AMTH_store_data.users['MATERIAL']:
AMTH_store_data.users['MATERIAL'].append(name)
+
# Check Lights
for la in d.lights:
# Cycles
@@ -643,6 +645,7 @@ class AMTH_SCENE_OT_list_users_for_x(Operator):
no.image and no.image.name == x:
if la.name not in AMTH_store_data.users['LIGHT']:
AMTH_store_data.users['LIGHT'].append(la.name)
+
# Check World
for wo in d.worlds:
# Cycles
@@ -654,6 +657,7 @@ class AMTH_SCENE_OT_list_users_for_x(Operator):
no.image and no.image.name == x:
if wo.name not in AMTH_store_data.users['WORLD']:
AMTH_store_data.users['WORLD'].append(wo.name)
+
# Check Textures
for te in d.textures:
if te and te.type == 'IMAGE' and te.image:
@@ -662,6 +666,7 @@ class AMTH_SCENE_OT_list_users_for_x(Operator):
if name == x and \
name not in AMTH_store_data.users['TEXTURE']:
AMTH_store_data.users['TEXTURE'].append(te.name)
+
# Check Modifiers in Objects
for ob in d.objects:
for mo in ob.modifiers:
@@ -672,21 +677,31 @@ class AMTH_SCENE_OT_list_users_for_x(Operator):
name = '"{0}" modifier in {1}'.format(mo.name, ob.name)
if name not in AMTH_store_data.users['MODIFIER']:
AMTH_store_data.users['MODIFIER'].append(name)
- # Check Background Images in Viewports
- for scr in d.screens:
- for ar in scr.areas:
- if ar.type == 'VIEW_3D':
- if ar.spaces and \
- ar.spaces.active and \
- ar.spaces.active.background_images:
- for bg in ar.spaces.active.background_images:
- image = bg.image
-
- if bg and image and image.name == x:
- name = 'Background for 3D Viewport in Screen "{0}"'\
- .format(scr.name)
- if name not in AMTH_store_data.users['VIEW3D']:
- AMTH_store_data.users['VIEW3D'].append(name)
+
+ # Check Background Images in Cameras
+ for ob in d.objects:
+ if ob and ob.type == 'CAMERA' and ob.data.background_images:
+ for bg in ob.data.background_images:
+ image = bg.image
+
+ if bg and image and image.name == x:
+ name = 'Used as background for Camera "{0}"'\
+ .format(ob.name)
+ if name not in AMTH_store_data.users['OUTLINER_OB_CAMERA']:
+ AMTH_store_data.users['OUTLINER_OB_CAMERA'].append(name)
+
+ # Check Empties type Image
+ for ob in d.objects:
+ if ob and ob.type == 'EMPTY' and ob.image_user:
+ if ob.image_user.id_data.data:
+ image = ob.image_user.id_data.data
+
+ if image and image.name == x:
+ name = 'Used in Empty "{0}"'\
+ .format(ob.name)
+ if name not in AMTH_store_data.users['OUTLINER_OB_EMPTY']:
+ AMTH_store_data.users['OUTLINER_OB_EMPTY'].append(name)
+
# Check the Compositor
for sce in d.scenes:
if sce.node_tree and sce.node_tree.nodes:
diff --git a/amaranth/scene/goto_library.py b/amaranth/scene/goto_library.py
index b1ea9e5b..d38a62d3 100644
--- a/amaranth/scene/goto_library.py
+++ b/amaranth/scene/goto_library.py
@@ -19,6 +19,10 @@ class AMTH_FILE_PT_libraries(bpy.types.Panel):
bl_category = "Bookmarks"
bl_label = "Libraries"
+ @classmethod
+ def poll(cls, context):
+ return context.area.ui_type == 'FILES'
+
def draw(self, context):
layout = self.layout
diff --git a/amaranth/scene/save_reload.py b/amaranth/scene/save_reload.py
index f3bedb5e..ece61246 100644
--- a/amaranth/scene/save_reload.py
+++ b/amaranth/scene/save_reload.py
@@ -13,6 +13,23 @@ import bpy
KEYMAPS = list()
+def check_for_unsaved_images(self):
+ im_unsaved = []
+
+ for im in bpy.data.images:
+ if im.is_dirty:
+ im_unsaved.append(im.name)
+
+ if im_unsaved:
+ report_text = 'There are unsaved changes in {0} image(s), check console for details.'\
+ .format(len(im_unsaved))
+ self.report({"WARNING"}, report_text)
+
+ print("\nAmaranth found unsaved images when trying to save and reload.")
+ for im in im_unsaved:
+ print('* Image: "' + im + '" has unsaved changes.')
+ return True
+ return
class AMTH_WM_OT_save_reload(bpy.types.Operator):
"""Save and Reload the current blend file"""
@@ -23,6 +40,10 @@ class AMTH_WM_OT_save_reload(bpy.types.Operator):
if not path:
bpy.ops.wm.save_as_mainfile("INVOKE_AREA")
return
+
+ if check_for_unsaved_images(self):
+ return
+
bpy.ops.wm.save_mainfile()
self.report({"INFO"}, "Saved & Reloaded")
bpy.ops.wm.open_mainfile("EXEC_DEFAULT", filepath=path)
diff --git a/animation_animall.py b/animation_animall/__init__.py
index a7d76f53..d679c970 100644
--- a/animation_animall.py
+++ b/animation_animall/__init__.py
@@ -2,8 +2,8 @@
bl_info = {
"name": "AnimAll",
- "author": "Daniel Salazar <zanqdo@gmail.com>",
- "version": (0, 9, 1),
+ "author": "Daniel Salazar (ZanQdo), Damien Picard (pioverfour)",
+ "version": (0, 9, 6),
"blender": (3, 3, 0),
"location": "3D View > Toolbox > Animation tab > AnimAll",
"description": "Allows animation of mesh, lattice, curve and surface data",
@@ -12,86 +12,77 @@ bl_info = {
"category": "Animation",
}
-"""
-Thanks to Campbell Barton and Joshua Leung for hes API additions and fixes
-Daniel 'ZanQdo' Salazar
-"""
-
import bpy
-from bpy.types import (
- Operator,
- Panel,
- AddonPreferences,
- )
-from bpy.props import (
- BoolProperty,
- StringProperty,
- )
+from bpy.types import (Operator, Panel, AddonPreferences)
+from bpy.props import (BoolProperty, StringProperty)
from bpy.app.handlers import persistent
+from bpy.app.translations import (pgettext_iface as iface_,
+ pgettext_data as data_)
+from . import translations
# Property Definitions
class AnimallProperties(bpy.types.PropertyGroup):
key_selected: BoolProperty(
- name="Selected Only",
+ name="Key Selected Only",
description="Insert keyframes only on selected elements",
- default=True
- )
- key_shape: BoolProperty(
- name="Shape",
+ default=False)
+
+ # Generic attributes
+ key_point_location: BoolProperty(
+ name="Location",
+ description="Insert keyframes on point locations",
+ default=False)
+ key_shape_key: BoolProperty(
+ name="Shape Key",
description="Insert keyframes on active Shape Key layer",
- default=False
- )
- key_uvs: BoolProperty(
- name="UVs",
- description="Insert keyframes on active UV coordinates",
- default=False
- )
- key_ebevel: BoolProperty(
- name="E-Bevel",
- description="Insert keyframes on edge bevel weight",
- default=False
- )
- key_vbevel: BoolProperty(
- name="V-Bevel",
+ default=False)
+ key_material_index: BoolProperty(
+ name="Material Index",
+ description="Insert keyframes on face material indices",
+ default=False)
+
+ # Mesh attributes
+ key_vertex_bevel: BoolProperty(
+ name="Vertex Bevel",
description="Insert keyframes on vertex bevel weight",
- default=False
- )
- key_crease: BoolProperty(
- name="Crease",
+ default=False)
+ # key_vertex_crease: BoolProperty(
+ # name="Vertex Crease",
+ # description="Insert keyframes on vertex crease weight",
+ # default=False)
+ key_vertex_group: BoolProperty(
+ name="Vertex Group",
+ description="Insert keyframes on active vertex group values",
+ default=False)
+
+ key_edge_bevel: BoolProperty(
+ name="Edge Bevel",
+ description="Insert keyframes on edge bevel weight",
+ default=False)
+ key_edge_crease: BoolProperty(
+ name="Edge Crease",
description="Insert keyframes on edge creases",
- default=False
- )
- key_vgroups: BoolProperty(
- name="V-groups",
- description="Insert keyframes on active Vertex group values",
- default=False
- )
+ default=False)
+
key_attribute: BoolProperty(
- name="Active Attribute",
+ name="Attribute",
description="Insert keyframes on active attribute values",
- default=False
- )
- key_points: BoolProperty(
- name="Points",
- description="Insert keyframes on point locations",
- default=False
- )
- key_handle_type: BoolProperty(
- name="Handle Types",
- description="Insert keyframes on Bezier point types",
- default=False
- )
+ default=False)
+ key_uvs: BoolProperty(
+ name="UV Map",
+ description="Insert keyframes on active UV coordinates",
+ default=False)
+
+ # Curve and surface attributes
key_radius: BoolProperty(
name="Radius",
description="Insert keyframes on point radius (Shrink/Fatten)",
- default=False
- )
+ default=False)
key_tilt: BoolProperty(
name="Tilt",
description="Insert keyframes on point tilt",
- default=False
- )
+ default=False)
# Utility functions
@@ -134,111 +125,118 @@ class VIEW3D_PT_animall(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "Animate"
- bl_label = 'AnimAll'
- bl_options = {'DEFAULT_CLOSED'}
+ bl_label = ''
@classmethod
def poll(self, context):
return context.active_object and context.active_object.type in {'MESH', 'LATTICE', 'CURVE', 'SURFACE'}
+ def draw_header(self, context):
+
+ layout = self.layout
+ row = layout.row()
+ row.label (text = 'AnimAll', icon = 'ARMATURE_DATA')
+
def draw(self, context):
obj = context.active_object
- animall_properties = context.window_manager.animall_properties
+ animall_properties = context.scene.animall_properties
layout = self.layout
- col = layout.column(align=True)
- row = col.row()
- row.prop(animall_properties, "key_selected")
- col.separator()
- row = col.row()
+ layout.label(text='Key:')
+
+ layout.use_property_split = True
+ layout.use_property_decorate = False
if obj.type == 'LATTICE':
- row.prop(animall_properties, "key_points")
- row.prop(animall_properties, "key_shape")
+ col = layout.column(heading="Points", align=True)
+ col.prop(animall_properties, "key_point_location")
- elif obj.type == 'MESH':
- row.prop(animall_properties, "key_points")
- row.prop(animall_properties, "key_shape")
- row = col.row()
- row.prop(animall_properties, "key_ebevel")
- row.prop(animall_properties, "key_vbevel")
- row = col.row()
- row.prop(animall_properties, "key_crease")
- row.prop(animall_properties, "key_uvs")
- row = col.row()
- row.prop(animall_properties, "key_attribute")
- row.prop(animall_properties, "key_vgroups")
-
- # Vertex group update operator
- if (context.active_object is not None
- and context.active_object.type == 'MESH'
- and context.active_object.data.animation_data is not None
- and context.active_object.data.animation_data.action is not None):
- for fcurve in context.active_object.data.animation_data.action.fcurves:
- if fcurve.data_path.startswith("vertex_colors"):
- layout.separator()
- row = layout.row()
- row.label(text="Object includes old-style vertex colors. Consider updating them.", icon="ERROR")
- row = layout.row()
- row.operator("anim.update_vertex_color_animation_animall", icon="FILE_REFRESH")
- break
-
- elif obj.type == 'CURVE':
- row.prop(animall_properties, "key_points")
- row.prop(animall_properties, "key_shape")
- row = col.row()
- row.prop(animall_properties, "key_radius")
- row.prop(animall_properties, "key_tilt")
- row = col.row()
- row.prop(animall_properties, "key_handle_type")
-
- elif obj.type == 'SURFACE':
- row.prop(animall_properties, "key_points")
- row.prop(animall_properties, "key_shape")
- row = col.row()
- row.prop(animall_properties, "key_radius")
- row.prop(animall_properties, "key_tilt")
+ col = layout.column(heading="Others", align=True)
+ col.prop(animall_properties, "key_shape_key")
- layout.separator()
- row = layout.row(align=True)
- row.operator("anim.insert_keyframe_animall", icon="KEY_HLT")
- row.operator("anim.delete_keyframe_animall", icon="KEY_DEHLT")
- row = layout.row()
- row.operator("anim.clear_animation_animall", icon="X")
-
- if animall_properties.key_shape:
+ elif obj.type == 'MESH':
+ col = layout.column(heading="Points", align=True)
+ col.prop(animall_properties, "key_point_location")
+ col.prop(animall_properties, "key_vertex_bevel", text="Bevel")
+ col.prop(animall_properties, "key_vertex_group")
+
+ col = layout.column(heading="Edges", align=True)
+ col.prop(animall_properties, "key_edge_bevel", text="Bevel")
+ col.prop(animall_properties, "key_edge_crease", text="Crease")
+
+ col = layout.column(heading="Faces", align=True)
+ col.prop(animall_properties, "key_material_index")
+
+ col = layout.column(heading="Others", align=True)
+ col.prop(animall_properties, "key_attribute")
+ col.prop(animall_properties, "key_uvs")
+ col.prop(animall_properties, "key_shape_key")
+
+ # Vertex group update operator
+ if (obj.data.animation_data is not None
+ and obj.data.animation_data.action is not None):
+ for fcurve in context.active_object.data.animation_data.action.fcurves:
+ if fcurve.data_path.startswith("vertex_colors"):
+ col = layout.column(align=True)
+ col.label(text="Object includes old-style vertex colors. Consider updating them.", icon="ERROR")
+ col.operator("anim.update_vertex_color_animation_animall", icon="FILE_REFRESH")
+ break
+
+ elif obj.type in {'CURVE', 'SURFACE'}:
+ col = layout.column(align=True)
+ col = layout.column(heading="Points", align=True)
+ col.prop(animall_properties, "key_point_location")
+ col.prop(animall_properties, "key_radius")
+ col.prop(animall_properties, "key_tilt")
+
+ col = layout.column(heading="Splines", align=True)
+ col.prop(animall_properties, "key_material_index")
+
+ col = layout.column(heading="Others", align=True)
+ col.prop(animall_properties, "key_shape_key")
+
+ if animall_properties.key_shape_key:
shape_key = obj.active_shape_key
shape_key_index = obj.active_shape_key_index
- split = layout.split()
- row = split.row()
-
if shape_key_index > 0:
- row.label(text=shape_key.name, icon="SHAPEKEY_DATA")
- row.prop(shape_key, "value", text="")
+ col = layout.column(align=True)
+ row = col.row(align=True)
+ row.prop(shape_key, "value", text=shape_key.name, icon="SHAPEKEY_DATA")
row.prop(obj, "show_only_shape_key", text="")
if shape_key.value < 1:
- row = layout.row()
- row.label(text='Maybe set "%s" to 1.0?' % shape_key.name, icon="INFO")
- elif shape_key:
- row.label(text="Cannot key on Basis Shape", icon="ERROR")
+ col.label(text=iface_('Maybe set "%s" to 1.0?') % shape_key.name, icon="INFO")
+ elif shape_key is not None:
+ col = layout.column(align=True)
+ col.label(text="Cannot key on Basis Shape", icon="ERROR")
else:
- row.label(text="No active Shape Key", icon="ERROR")
+ col = layout.column(align=True)
+ col.label(text="No active Shape Key", icon="ERROR")
+
+ if animall_properties.key_point_location:
+ col.label(text='"Location" and "Shape Key" are redundant?', icon="INFO")
+
+ layout.use_property_split = False
+ layout.separator()
+ row = layout.row()
+ row.prop(animall_properties, "key_selected")
- if animall_properties.key_points and animall_properties.key_shape:
- row = layout.row()
- row.label(text='"Points" and "Shape" are redundant?', icon="INFO")
+ row = layout.row(align=True)
+ row.operator("anim.insert_keyframe_animall", icon="KEY_HLT")
+ row.operator("anim.delete_keyframe_animall", icon="KEY_DEHLT")
+ row = layout.row()
+ row.operator("anim.clear_animation_animall", icon="CANCEL")
class ANIM_OT_insert_keyframe_animall(Operator):
- bl_label = "Insert"
+ bl_label = "Insert Key"
bl_idname = "anim.insert_keyframe_animall"
bl_description = "Insert a Keyframe"
bl_options = {'REGISTER', 'UNDO'}
- def execute(op, context):
- animall_properties = context.window_manager.animall_properties
+ def execute(self, context):
+ animall_properties = context.scene.animall_properties
if context.mode == 'OBJECT':
objects = context.selected_objects
@@ -254,19 +252,26 @@ class ANIM_OT_insert_keyframe_animall(Operator):
data = obj.data
if obj.type == 'LATTICE':
- if animall_properties.key_shape:
+ if animall_properties.key_shape_key:
if obj.active_shape_key_index > 0:
sk_name = obj.active_shape_key.name
for p_i, point in enumerate(obj.active_shape_key.data):
if not animall_properties.key_selected or data.points[p_i].select:
- insert_key(point, 'co', group="%s Point %s" % (sk_name, p_i))
+ insert_key(point, 'co', group=data_("%s Point %s") % (sk_name, p_i))
- if animall_properties.key_points:
+ if animall_properties.key_point_location:
for p_i, point in enumerate(data.points):
if not animall_properties.key_selected or point.select:
- insert_key(point, 'co_deform', group="Point %s" % p_i)
+ insert_key(point, 'co_deform', group=data_("Point %s") % p_i)
else:
+ if animall_properties.key_material_index:
+ for s_i, spline in enumerate(data.splines):
+ if (not animall_properties.key_selected
+ or any(point.select for point in spline.points)
+ or any(point.select_control_point for point in spline.bezier_points)):
+ insert_key(spline, 'material_index', group=data_("Spline %s") % s_i)
+
for s_i, spline in enumerate(data.splines):
if spline.type == 'BEZIER':
for v_i, CV in enumerate(spline.bezier_points):
@@ -274,76 +279,68 @@ class ANIM_OT_insert_keyframe_animall(Operator):
or CV.select_control_point
or CV.select_left_handle
or CV.select_right_handle):
- if animall_properties.key_points:
- insert_key(CV, 'co', group="Spline %s CV %s" % (s_i, v_i))
- insert_key(CV, 'handle_left', group="Spline %s CV %s" % (s_i, v_i))
- insert_key(CV, 'handle_right', group="Spline %s CV %s" % (s_i, v_i))
+ if animall_properties.key_point_location:
+ insert_key(CV, 'co', group=data_("Spline %s CV %s") % (s_i, v_i))
+ insert_key(CV, 'handle_left', group=data_("Spline %s CV %s") % (s_i, v_i))
+ insert_key(CV, 'handle_right', group=data_("Spline %s CV %s") % (s_i, v_i))
if animall_properties.key_radius:
- insert_key(CV, 'radius', group="Spline %s CV %s" % (s_i, v_i))
-
- if animall_properties.key_handle_type:
- insert_key(CV, 'handle_left_type', group="spline %s CV %s" % (s_i, v_i))
- insert_key(CV, 'handle_right_type', group="spline %s CV %s" % (s_i, v_i))
+ insert_key(CV, 'radius', group=data_("Spline %s CV %s") % (s_i, v_i))
if animall_properties.key_tilt:
- insert_key(CV, 'tilt', group="Spline %s CV %s" % (s_i, v_i))
+ insert_key(CV, 'tilt', group=data_("Spline %s CV %s") % (s_i, v_i))
elif spline.type in ('POLY', 'NURBS'):
for v_i, CV in enumerate(spline.points):
if not animall_properties.key_selected or CV.select:
- if animall_properties.key_points:
- insert_key(CV, 'co', group="Spline %s CV %s" % (s_i, v_i))
+ if animall_properties.key_point_location:
+ insert_key(CV, 'co', group=data_("Spline %s CV %s") % (s_i, v_i))
if animall_properties.key_radius:
- insert_key(CV, 'radius', group="Spline %s CV %s" % (s_i, v_i))
+ insert_key(CV, 'radius', group=data_("Spline %s CV %s") % (s_i, v_i))
if animall_properties.key_tilt:
- insert_key(CV, 'tilt', group="Spline %s CV %s" % (s_i, v_i))
+ insert_key(CV, 'tilt', group=data_("Spline %s CV %s") % (s_i, v_i))
bpy.ops.object.mode_set(mode='OBJECT')
for obj in [o for o in objects if o.type in {'MESH', 'CURVE', 'SURFACE'}]:
data = obj.data
if obj.type == 'MESH':
- if animall_properties.key_points:
+ if animall_properties.key_point_location:
for v_i, vert in enumerate(data.vertices):
if not animall_properties.key_selected or vert.select:
- insert_key(vert, 'co', group="Vertex %s" % v_i)
+ insert_key(vert, 'co', group=data_("Vertex %s") % v_i)
- if animall_properties.key_vbevel:
+ if animall_properties.key_vertex_bevel:
for v_i, vert in enumerate(data.vertices):
if not animall_properties.key_selected or vert.select:
- insert_key(vert, 'bevel_weight', group="Vertex %s" % v_i)
+ insert_key(vert, 'bevel_weight', group=data_("Vertex %s") % v_i)
+ # if animall_properties.key_vertex_crease:
+ # for v_i, vert in enumerate(data.vertices):
+ # if not animall_properties.key_selected or vert.select:
+ # insert_key(vert, 'crease', group=data_("Vertex %s") % v_i)
- if animall_properties.key_vgroups:
+ if animall_properties.key_vertex_group:
for v_i, vert in enumerate(data.vertices):
if not animall_properties.key_selected or vert.select:
for group in vert.groups:
- insert_key(group, 'weight', group="Vertex %s" % v_i)
+ insert_key(group, 'weight', group=data_("Vertex %s") % v_i)
- if animall_properties.key_ebevel:
+ if animall_properties.key_edge_bevel:
for e_i, edge in enumerate(data.edges):
if not animall_properties.key_selected or edge.select:
- insert_key(edge, 'bevel_weight', group="Edge %s" % e_i)
+ insert_key(edge, 'bevel_weight', group=data_("Edge %s") % e_i)
- if animall_properties.key_crease:
+ if animall_properties.key_edge_crease:
for e_i, edge in enumerate(data.edges):
if not animall_properties.key_selected or edge.select:
- insert_key(edge, 'crease', group="Edge %s" % e_i)
+ insert_key(edge, 'crease', group=data_("Edge %s") % e_i)
- if animall_properties.key_shape:
- if obj.active_shape_key_index > 0:
- sk_name = obj.active_shape_key.name
- for v_i, vert in enumerate(obj.active_shape_key.data):
- if not animall_properties.key_selected or data.vertices[v_i].select:
- insert_key(vert, 'co', group="%s Vertex %s" % (sk_name, v_i))
-
- if animall_properties.key_uvs:
- if data.uv_layers.active is not None:
- for uv_i, uv in enumerate(data.uv_layers.active.data):
- if not animall_properties.key_selected or uv.select:
- insert_key(uv, 'uv', group="UV layer %s" % uv_i)
+ if animall_properties.key_material_index:
+ for p_i, polygon in enumerate(data.polygons):
+ if not animall_properties.key_selected or polygon.select:
+ insert_key(polygon, 'material_index', group=data_("Face %s") % p_i)
if animall_properties.key_attribute:
if data.attributes.active is not None:
@@ -358,13 +355,13 @@ class ANIM_OT_insert_keyframe_animall(Operator):
attribute_key = "vector"
if attribute.domain == 'POINT':
- group = "Vertex %s"
+ group = data_("Vertex %s")
elif attribute.domain == 'EDGE':
- group = "Edge %s"
+ group = data_("Edge %s")
elif attribute.domain == 'FACE':
- group = "Face %s"
+ group = data_("Face %s")
elif attribute.domain == 'CORNER':
- group = "Loop %s"
+ group = data_("Loop %s")
for e_i, _attribute_data in enumerate(attribute.data):
if (not animall_properties.key_selected
@@ -375,9 +372,22 @@ class ANIM_OT_insert_keyframe_animall(Operator):
insert_key(data, f'attributes["{attribute.name}"].data[{e_i}].{attribute_key}',
group=group % e_i)
+ if animall_properties.key_uvs:
+ if data.uv_layers.active is not None:
+ for uv_i, uv in enumerate(data.uv_layers.active.data):
+ if not animall_properties.key_selected or uv.select:
+ insert_key(uv, 'uv', group=data_("UV layer %s") % uv_i)
+
+ if animall_properties.key_shape_key:
+ if obj.active_shape_key_index > 0:
+ sk_name = obj.active_shape_key.name
+ for v_i, vert in enumerate(obj.active_shape_key.data):
+ if not animall_properties.key_selected or data.vertices[v_i].select:
+ insert_key(vert, 'co', group=data_("%s Vertex %s") % (sk_name, v_i))
+
elif obj.type in {'CURVE', 'SURFACE'}:
# Shape key keys have to be inserted in object mode for curves...
- if animall_properties.key_shape:
+ if animall_properties.key_shape_key:
sk_name = obj.active_shape_key.name
global_spline_index = 0 # numbering for shape keys, which have flattened indices
for s_i, spline in enumerate(data.splines):
@@ -389,11 +399,11 @@ class ANIM_OT_insert_keyframe_animall(Operator):
or CV.select_right_handle):
if obj.active_shape_key_index > 0:
CV = obj.active_shape_key.data[global_spline_index]
- insert_key(CV, 'co', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
- insert_key(CV, 'handle_left', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
- insert_key(CV, 'handle_right', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
- insert_key(CV, 'radius', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
- insert_key(CV, 'tilt', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
+ insert_key(CV, 'co', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
+ insert_key(CV, 'handle_left', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
+ insert_key(CV, 'handle_right', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
+ insert_key(CV, 'radius', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
+ insert_key(CV, 'tilt', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
global_spline_index += 1
elif spline.type in ('POLY', 'NURBS'):
@@ -401,9 +411,9 @@ class ANIM_OT_insert_keyframe_animall(Operator):
if not animall_properties.key_selected or CV.select:
if obj.active_shape_key_index > 0:
CV = obj.active_shape_key.data[global_spline_index]
- insert_key(CV, 'co', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
- insert_key(CV, 'radius', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
- insert_key(CV, 'tilt', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
+ insert_key(CV, 'co', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
+ insert_key(CV, 'radius', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
+ insert_key(CV, 'tilt', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
global_spline_index += 1
bpy.ops.object.mode_set(mode=mode)
@@ -413,14 +423,13 @@ class ANIM_OT_insert_keyframe_animall(Operator):
class ANIM_OT_delete_keyframe_animall(Operator):
- bl_label = "Delete"
+ bl_label = "Delete Key"
bl_idname = "anim.delete_keyframe_animall"
bl_description = "Delete a Keyframe"
bl_options = {'REGISTER', 'UNDO'}
-
- def execute(op, context):
- animall_properties = context.window_manager.animall_properties
+ def execute(self, context):
+ animall_properties = context.scene.animall_properties
if context.mode == 'OBJECT':
objects = context.selected_objects
@@ -432,33 +441,38 @@ class ANIM_OT_delete_keyframe_animall(Operator):
for obj in objects:
data = obj.data
if obj.type == 'MESH':
- if animall_properties.key_points:
+ if animall_properties.key_point_location:
for vert in data.vertices:
if not animall_properties.key_selected or vert.select:
delete_key(vert, 'co')
- if animall_properties.key_vbevel:
+ if animall_properties.key_vertex_bevel:
for vert in data.vertices:
if not animall_properties.key_selected or vert.select:
delete_key(vert, 'bevel_weight')
- if animall_properties.key_vgroups:
+ if animall_properties.key_vertex_group:
for vert in data.vertices:
if not animall_properties.key_selected or vert.select:
for group in vert.groups:
delete_key(group, 'weight')
- if animall_properties.key_ebevel:
+ # if animall_properties.key_vertex_crease:
+ # for vert in data.vertices:
+ # if not animall_properties.key_selected or vert.select:
+ # delete_key(vert, 'crease')
+
+ if animall_properties.key_edge_bevel:
for edge in data.edges:
if not animall_properties.key_selected or edge.select:
delete_key(edge, 'bevel_weight')
- if animall_properties.key_crease:
+ if animall_properties.key_edge_crease:
for edge in data.edges:
if not animall_properties.key_selected or vert.select:
delete_key(edge, 'crease')
- if animall_properties.key_shape:
+ if animall_properties.key_shape_key:
if obj.active_shape_key:
for v_i, vert in enumerate(obj.active_shape_key.data):
if not animall_properties.key_selected or data.vertices[v_i].select:
@@ -491,19 +505,19 @@ class ANIM_OT_delete_keyframe_animall(Operator):
delete_key(data, f'attributes["{attribute.name}"].data[{e_i}].{attribute_key}')
elif obj.type == 'LATTICE':
- if animall_properties.key_shape:
+ if animall_properties.key_shape_key:
if obj.active_shape_key:
for point in obj.active_shape_key.data:
delete_key(point, 'co')
- if animall_properties.key_points:
+ if animall_properties.key_point_location:
for point in data.points:
if not animall_properties.key_selected or point.select:
delete_key(point, 'co_deform')
elif obj.type in {'CURVE', 'SURFACE'}:
- # run this outside the splines loop (only once)
- if animall_properties.key_shape:
+ # Run this outside the splines loop (only once)
+ if animall_properties.key_shape_key:
if obj.active_shape_key_index > 0:
for CV in obj.active_shape_key.data:
delete_key(CV, 'co')
@@ -517,13 +531,10 @@ class ANIM_OT_delete_keyframe_animall(Operator):
or CV.select_control_point
or CV.select_left_handle
or CV.select_right_handle):
- if animall_properties.key_points:
+ if animall_properties.key_point_location:
delete_key(CV, 'co')
delete_key(CV, 'handle_left')
delete_key(CV, 'handle_right')
- if animall_properties.key_handle_type:
- delete_key(CV, 'handle_left_type')
- delete_key(CV, 'handle_right_type')
if animall_properties.key_radius:
delete_key(CV, 'radius')
if animall_properties.key_tilt:
@@ -532,7 +543,7 @@ class ANIM_OT_delete_keyframe_animall(Operator):
elif spline.type in ('POLY', 'NURBS'):
for CV in spline.points:
if not animall_properties.key_selected or CV.select:
- if animall_properties.key_points:
+ if animall_properties.key_point_location:
delete_key(CV, 'co')
if animall_properties.key_radius:
delete_key(CV, 'radius')
@@ -642,28 +653,21 @@ class AnimallAddonPreferences(AddonPreferences):
col.label(text="Tab Category:")
col.prop(self, "category", text="")
+register_classes, unregister_classes = bpy.utils.register_classes_factory(
+ (AnimallProperties, VIEW3D_PT_animall, ANIM_OT_insert_keyframe_animall,
+ ANIM_OT_delete_keyframe_animall, ANIM_OT_clear_animation_animall,
+ ANIM_OT_update_vertex_color_animation_animall, AnimallAddonPreferences))
def register():
- bpy.utils.register_class(AnimallProperties)
- bpy.types.WindowManager.animall_properties = bpy.props.PointerProperty(type=AnimallProperties)
- bpy.utils.register_class(VIEW3D_PT_animall)
- bpy.utils.register_class(ANIM_OT_insert_keyframe_animall)
- bpy.utils.register_class(ANIM_OT_delete_keyframe_animall)
- bpy.utils.register_class(ANIM_OT_clear_animation_animall)
- bpy.utils.register_class(ANIM_OT_update_vertex_color_animation_animall)
- bpy.utils.register_class(AnimallAddonPreferences)
+ register_classes()
+ bpy.types.Scene.animall_properties = bpy.props.PointerProperty(type=AnimallProperties)
update_panel(None, bpy.context)
-
+ bpy.app.translations.register(__name__, translations.translations_dict)
def unregister():
- del bpy.types.WindowManager.animall_properties
- bpy.utils.unregister_class(AnimallProperties)
- bpy.utils.unregister_class(VIEW3D_PT_animall)
- bpy.utils.unregister_class(ANIM_OT_insert_keyframe_animall)
- bpy.utils.unregister_class(ANIM_OT_delete_keyframe_animall)
- bpy.utils.unregister_class(ANIM_OT_clear_animation_animall)
- bpy.utils.unregister_class(ANIM_OT_update_vertex_color_animation_animall)
- bpy.utils.unregister_class(AnimallAddonPreferences)
+ bpy.app.translations.unregister(__name__)
+ del bpy.types.Scene.animall_properties
+ unregister_classes()
if __name__ == "__main__":
register()
diff --git a/animation_animall/translations.py b/animation_animall/translations.py
new file mode 100644
index 00000000..03b77e0b
--- /dev/null
+++ b/animation_animall/translations.py
@@ -0,0 +1,364 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# ##### BEGIN AUTOGENERATED I18N SECTION #####
+# NOTE: You can safely move around this auto-generated block (with the begin/end markers!),
+# and edit the translations by hand.
+# Just carefully respect the format of the tuple!
+
+# Tuple of tuples:
+# ((msgctxt, msgid), (sources, gen_comments), (lang, translation, (is_fuzzy, comments)), ...)
+translations_tuple = (
+ (("*", ""),
+ ((), ()),
+ ("fr_FR", "Project-Id-Version: AnimAll 0.9.6 (0)\n",
+ (False,
+ ("Blender's translation file (po format).",
+ "Copyright (C) 2022 The Blender Foundation.",
+ "This file is distributed under the same license as the Blender package.",
+ "Damien Picard <dam.pic@free.fr>, 2022."))),
+ ),
+ (("*", "Tab Category"),
+ (("bpy.types.AnimallAddonPreferences.category",),
+ ()),
+ ("fr_FR", "Catégorie d’onglet",
+ (False, ())),
+ ),
+ (("*", "Choose a name for the category of the panel"),
+ (("bpy.types.AnimallAddonPreferences.category",),
+ ()),
+ ("fr_FR", "Choisir un nom pour la catégorie du panneau",
+ (False, ())),
+ ),
+ (("Operator", "Insert Key"),
+ (("bpy.types.ANIM_OT_insert_keyframe_animall",
+ "bpy.types.ANIM_OT_insert_keyframe_animall"),
+ ()),
+ ("fr_FR", "Insérer une clé",
+ (False, ())),
+ ),
+ (("Operator", "Clear Animation"),
+ (("bpy.types.ANIM_OT_clear_animation_animall",),
+ ()),
+ ("fr_FR", "Effacer l’animation",
+ (False, ())),
+ ),
+ (("*", "Delete all keyframes for this object\nIf in a specific case it doesn't work\ntry to delete the keys manually"),
+ (("bpy.types.ANIM_OT_clear_animation_animall",),
+ ()),
+ ("fr_FR", "Supprimer toutes les images clés pour cet objet.\n"
+ "En cas d’échec, essayez de les supprimer manuellement",
+ (False, ())),
+ ),
+ (("*", "Insert a Keyframe"),
+ (("bpy.types.ANIM_OT_insert_keyframe_animall",),
+ ()),
+ ("fr_FR", "Insérer une image clé",
+ (False, ())),
+ ),
+ (("Operator", "Delete Key"),
+ (("bpy.types.ANIM_OT_delete_keyframe_animall",),
+ ()),
+ ("fr_FR", "Supprimer image clé",
+ (False, ())),
+ ),
+ (("*", "Delete a Keyframe"),
+ (("bpy.types.ANIM_OT_delete_keyframe_animall",),
+ ()),
+ ("fr_FR", "Supprimer une image clé",
+ (False, ())),
+ ),
+ (("*", "Animate"),
+ (("bpy.types.VIEW3D_PT_animall",),
+ ()),
+ ("fr_FR", "Animer",
+ (False, ())),
+ ),
+ (("*", "Insert keyframes on active attribute values"),
+ (("bpy.types.AnimallProperties.key_attribute",),
+ ()),
+ ("fr_FR", "Insérer des clés sur l’attribut actif",
+ (False, ())),
+ ),
+ (("*", "Insert keyframes on edge bevel weight"),
+ (("bpy.types.AnimallProperties.key_edge_bevel",),
+ ()),
+ ("fr_FR", "Insérer des clés sur les poids de biseau d’arête",
+ (False, ())),
+ ),
+ (("*", "Insert keyframes on edge creases"),
+ (("bpy.types.AnimallProperties.key_edge_crease",),
+ ()),
+ ("fr_FR", "Insérer des clés sur les plis d’arête",
+ (False, ())),
+ ),
+ (("*", "Insert keyframes on face material indices"),
+ (("bpy.types.AnimallProperties.key_material_index",),
+ ()),
+ ("fr_FR", "Insérer des clés sur les indices de matériaux",
+ (False, ())),
+ ),
+ (("*", "Insert keyframes on point locations"),
+ (("bpy.types.AnimallProperties.key_point_location",),
+ ()),
+ ("fr_FR", "Insérer des clés sur les positions des points",
+ (False, ())),
+ ),
+ (("*", "Insert keyframes on point radius (Shrink/Fatten)"),
+ (("bpy.types.AnimallProperties.key_radius",),
+ ()),
+ ("fr_FR", "Insérer des clés sur le rayon de rayon de point (épaisseur de la courbe)",
+ (False, ())),
+ ),
+ (("*", "Key Selected Only"),
+ (("bpy.types.AnimallProperties.key_selected",),
+ ()),
+ ("fr_FR", "Sélection uniquement",
+ (False, ())),
+ ),
+ (("*", "Insert keyframes only on selected elements"),
+ (("bpy.types.AnimallProperties.key_selected",),
+ ()),
+ ("fr_FR", "Insérer des images clés seulement sur les éléments sélectionnés",
+ (False, ())),
+ ),
+ (("*", "Insert keyframes on active Shape Key layer"),
+ (("bpy.types.AnimallProperties.key_shape_key",),
+ ()),
+ ("fr_FR", "Insérer des clés sur le calque de clé de forme actif",
+ (False, ())),
+ ),
+ (("*", "Insert keyframes on point tilt"),
+ (("bpy.types.AnimallProperties.key_tilt",),
+ ()),
+ ("fr_FR", "Insérer des clés sur l’inclinaison des points",
+ (False, ())),
+ ),
+ (("*", "Insert keyframes on active UV coordinates"),
+ (("bpy.types.AnimallProperties.key_uvs",),
+ ()),
+ ("fr_FR", "Insérer des clés sur les coordonnées UV actives",
+ (False, ())),
+ ),
+ (("*", "Insert keyframes on vertex bevel weight"),
+ (("bpy.types.AnimallProperties.key_vertex_bevel",),
+ ()),
+ ("fr_FR", "Insérer des clés sur les poids de biseau des sommets",
+ (False, ())),
+ ),
+ (("*", "Insert keyframes on active vertex group values"),
+ (("bpy.types.AnimallProperties.key_vertex_group",),
+ ()),
+ ("fr_FR", "Insérer des clés sur les valeurs des groupes de sommets",
+ (False, ())),
+ ),
+ (("*", "AnimAll"),
+ (("scripts/addons/animation_animall/__init__.py:138",
+ "Add-on AnimAll info: name"),
+ ()),
+ ("fr_FR", "AnimAll",
+ (False, ())),
+ ),
+ (("*", "Key:"),
+ (("scripts/addons/animation_animall/__init__.py:146",),
+ ()),
+ ("fr_FR", "Insérer :",
+ (False, ())),
+ ),
+ (("*", "Tab Category:"),
+ (("scripts/addons/animation_animall/__init__.py:653",),
+ ()),
+ ("fr_FR", "Catégorie d’onglet :",
+ (False, ())),
+ ),
+ (("*", "Points"),
+ (("scripts/addons/animation_animall/__init__.py:152",
+ "scripts/addons/animation_animall/__init__.py:159",
+ "scripts/addons/animation_animall/__init__.py:188"),
+ ()),
+ ("fr_FR", "Points",
+ (False, ())),
+ ),
+ (("*", "Others"),
+ (("scripts/addons/animation_animall/__init__.py:155",
+ "scripts/addons/animation_animall/__init__.py:171",
+ "scripts/addons/animation_animall/__init__.py:196"),
+ ()),
+ ("fr_FR", "Autres",
+ (False, ())),
+ ),
+ (("*", "Bevel"),
+ (("scripts/addons/animation_animall/__init__.py:161",
+ "scripts/addons/animation_animall/__init__.py:165"),
+ ()),
+ ("fr_FR", "Biseau",
+ (False, ())),
+ ),
+ (("*", "Edges"),
+ (("scripts/addons/animation_animall/__init__.py:164",),
+ ()),
+ ("fr_FR", "Arêtes",
+ (False, ())),
+ ),
+ (("*", "Crease"),
+ (("scripts/addons/animation_animall/__init__.py:166",),
+ ()),
+ ("fr_FR", "Plis",
+ (False, ())),
+ ),
+ (("*", "Faces"),
+ (("scripts/addons/animation_animall/__init__.py:168",),
+ ()),
+ ("fr_FR", "Faces",
+ (False, ())),
+ ),
+ (("*", "\"Location\" and \"Shape Key\" are redundant?"),
+ (("scripts/addons/animation_animall/__init__.py:218",),
+ ()),
+ ("fr_FR", "\"Position\" et \"Clé de forme\" sont redondants ?",
+ (False, ())),
+ ),
+ (("*", "Splines"),
+ (("scripts/addons/animation_animall/__init__.py:193",),
+ ()),
+ ("fr_FR", "Splines",
+ (False, ())),
+ ),
+ (("*", "Maybe set \"%s\" to 1.0?"),
+ (("scripts/addons/animation_animall/__init__.py:209",
+ "scripts/addons/animation_animall/__init__.py:209"),
+ ()),
+ ("fr_FR", "Essayez de mettre « %s » à 1.0 ?",
+ (False, ())),
+ ),
+ (("*", "Cannot key on Basis Shape"),
+ (("scripts/addons/animation_animall/__init__.py:212",),
+ ()),
+ ("fr_FR", "Impossible d’ajouter une clé sur la forme de base",
+ (False, ())),
+ ),
+ (("*", "No active Shape Key"),
+ (("scripts/addons/animation_animall/__init__.py:215",),
+ ()),
+ ("fr_FR", "Pas de clé de forme active",
+ (False, ())),
+ ),
+ (("*", "Clear Animation could not be performed"),
+ (("scripts/addons/animation_animall/__init__.py:581",),
+ ()),
+ ("fr_FR", "La suppression de l’animation n’a pas pu aboutir",
+ (False, ())),
+ ),
+ (("*", "Object includes old-style vertex colors. Consider updating them."),
+ (("scripts/addons/animation_animall/__init__.py:182",),
+ ()),
+ ("fr_FR", "L’objet contient des couleurs de sommets à l’ancien format. Veuillez les mettre à jour",
+ (False, ())),
+ ),
+ (("*", "Vertex %s"),
+ (("scripts/addons/animation_animall/__init__.py:358",
+ "scripts/addons/animation_animall/__init__.py:313",
+ "scripts/addons/animation_animall/__init__.py:318",
+ "scripts/addons/animation_animall/__init__.py:328"),
+ ()),
+ ("fr_FR", "Sommet %s",
+ (False, ())),
+ ),
+ (("*", "Edge %s"),
+ (("scripts/addons/animation_animall/__init__.py:360",
+ "scripts/addons/animation_animall/__init__.py:333",
+ "scripts/addons/animation_animall/__init__.py:338"),
+ ()),
+ ("fr_FR", "Arête %s",
+ (False, ())),
+ ),
+ (("*", "Point %s"),
+ (("scripts/addons/animation_animall/__init__.py:265",),
+ ()),
+ ("fr_FR", "Point %s",
+ (False, ())),
+ ),
+ (("*", "Spline %s"),
+ (("scripts/addons/animation_animall/__init__.py:273",),
+ ()),
+ ("fr_FR", "Spline %s",
+ (False, ())),
+ ),
+ (("*", "Face %s"),
+ (("scripts/addons/animation_animall/__init__.py:343",
+ "scripts/addons/animation_animall/__init__.py:362"),
+ ()),
+ ("fr_FR", "Face %s",
+ (False, ())),
+ ),
+ (("*", "%s Point %s"),
+ (("scripts/addons/animation_animall/__init__.py:260",),
+ ()),
+ ("fr_FR", "%s Point %s",
+ (False, ())),
+ ),
+ (("*", "Loop %s"),
+ (("scripts/addons/animation_animall/__init__.py:364",),
+ ()),
+ ("fr_FR", "Boucle %s",
+ (False, ())),
+ ),
+ (("*", "UV layer %s"),
+ (("scripts/addons/animation_animall/__init__.py:379",),
+ ()),
+ ("fr_FR", "Calque UV %s",
+ (False, ())),
+ ),
+ (("*", "%s Vertex %s"),
+ (("scripts/addons/animation_animall/__init__.py:386",),
+ ()),
+ ("fr_FR", "%s Sommet %s",
+ (False, ())),
+ ),
+ (("*", "Spline %s CV %s"),
+ (("scripts/addons/animation_animall/__init__.py:283",
+ "scripts/addons/animation_animall/__init__.py:284",
+ "scripts/addons/animation_animall/__init__.py:285",
+ "scripts/addons/animation_animall/__init__.py:288",
+ "scripts/addons/animation_animall/__init__.py:291",
+ "scripts/addons/animation_animall/__init__.py:297",
+ "scripts/addons/animation_animall/__init__.py:300",
+ "scripts/addons/animation_animall/__init__.py:303"),
+ ()),
+ ("fr_FR", "Spline %s Point %s",
+ (False, ())),
+ ),
+ (("*", "%s Spline %s CV %s"),
+ (("scripts/addons/animation_animall/__init__.py:402",
+ "scripts/addons/animation_animall/__init__.py:403",
+ "scripts/addons/animation_animall/__init__.py:404",
+ "scripts/addons/animation_animall/__init__.py:405",
+ "scripts/addons/animation_animall/__init__.py:406",
+ "scripts/addons/animation_animall/__init__.py:414",
+ "scripts/addons/animation_animall/__init__.py:415",
+ "scripts/addons/animation_animall/__init__.py:416"),
+ ()),
+ ("fr_FR", "%s Spline %s Point %s",
+ (False, ())),
+ ),
+ (("*", "3D View > Toolbox > Animation tab > AnimAll"),
+ (("Add-on AnimAll info: location",),
+ ()),
+ ("fr_FR", "Vue 3D > Panneau N > Onglet Animer > AnimAll",
+ (False, ())),
+ ),
+ (("*", "Allows animation of mesh, lattice, curve and surface data"),
+ (("Add-on AnimAll info: description",),
+ ()),
+ ("fr_FR", "Permet d’animer les données de maillages, de lattices, de courbes et de surfaces",
+ (False, ())),
+ ),
+)
+
+translations_dict = {}
+for msg in translations_tuple:
+ key = msg[0]
+ for lang, trans, (is_fuzzy, comments) in msg[2:]:
+ if trans and not is_fuzzy:
+ translations_dict.setdefault(lang, {})[key] = trans
+
+# ##### END AUTOGENERATED I18N SECTION #####
diff --git a/ant_landscape/__init__.py b/ant_landscape/__init__.py
index 295793b9..c330ad5e 100644
--- a/ant_landscape/__init__.py
+++ b/ant_landscape/__init__.py
@@ -30,23 +30,24 @@ else:
import bpy
from bpy.props import (
- BoolProperty,
- FloatProperty,
- IntProperty,
- StringProperty,
- PointerProperty,
- EnumProperty,
- )
+ BoolProperty,
+ FloatProperty,
+ IntProperty,
+ StringProperty,
+ PointerProperty,
+ EnumProperty,
+)
from .ant_functions import (
- draw_ant_refresh,
- draw_ant_main,
- draw_ant_noise,
- draw_ant_displace,
- )
+ draw_ant_refresh,
+ draw_ant_main,
+ draw_ant_noise,
+ draw_ant_displace,
+)
# ------------------------------------------------------------
# Menu's and panels
+
def menu_func_eroder(self, context):
ob = bpy.context.active_object
if ob and (ob.ant_landscape.keys() and not ob.ant_landscape['sphere_mesh']):
@@ -129,7 +130,7 @@ class AntMainSettingsPanel(bpy.types.Panel):
if ant.sphere_mesh:
split.prop(ant, "remove_double", toggle=True, text="Remove Doubles", icon='MESH_DATA')
box.prop(ant, "ant_terrain_name")
- box.prop_search(ant, "land_material", bpy.data, "materials")
+ box.prop_search(ant, "land_material", bpy.data, "materials")
col = box.column(align=True)
col.prop(ant, "subdivision_x")
col.prop(ant, "subdivision_y")
@@ -398,131 +399,131 @@ class AntDisplaceSettingsPanel(bpy.types.Panel):
class AntLandscapePropertiesGroup(bpy.types.PropertyGroup):
ant_terrain_name: StringProperty(
- name="Name",
- default="Landscape"
- )
+ name="Name",
+ default="Landscape"
+ )
land_material: StringProperty(
- name='Material',
- default="",
- description="Terrain material"
- )
+ name='Material',
+ default="",
+ description="Terrain material"
+ )
water_material: StringProperty(
- name='Material',
- default="",
- description="Water plane material"
- )
+ name='Material',
+ default="",
+ description="Water plane material"
+ )
texture_block: StringProperty(
- name="Texture",
- default=""
- )
+ name="Texture",
+ default=""
+ )
at_cursor: BoolProperty(
- name="Cursor",
- default=True,
- description="Place at cursor location",
- )
+ name="Cursor",
+ default=True,
+ description="Place at cursor location",
+ )
smooth_mesh: BoolProperty(
- name="Smooth",
- default=True,
- description="Shade smooth"
- )
+ name="Smooth",
+ default=True,
+ description="Shade smooth"
+ )
tri_face: BoolProperty(
- name="Triangulate",
- default=False,
- description="Triangulate faces"
- )
+ name="Triangulate",
+ default=False,
+ description="Triangulate faces"
+ )
sphere_mesh: BoolProperty(
- name="Sphere",
- default=False,
- description="Generate uv sphere - remove doubles when ready"
- )
+ name="Sphere",
+ default=False,
+ description="Generate uv sphere - remove doubles when ready"
+ )
subdivision_x: IntProperty(
- name="Subdivisions X",
- default=128,
- min=4,
- max=6400,
- description="Mesh X subdivisions"
- )
+ name="Subdivisions X",
+ default=128,
+ min=4,
+ max=6400,
+ description="Mesh X subdivisions"
+ )
subdivision_y: IntProperty(
- default=128,
- name="Subdivisions Y",
- min=4,
- max=6400,
- description="Mesh Y subdivisions"
- )
+ default=128,
+ name="Subdivisions Y",
+ min=4,
+ max=6400,
+ description="Mesh Y subdivisions"
+ )
mesh_size: FloatProperty(
- default=2.0,
- name="Mesh Size",
- min=0.01,
- max=100000.0,
- description="Mesh size"
- )
+ default=2.0,
+ name="Mesh Size",
+ min=0.01,
+ max=100000.0,
+ description="Mesh size"
+ )
mesh_size_x: FloatProperty(
- default=2.0,
- name="Mesh Size X",
- min=0.01,
- description="Mesh x size"
- )
+ default=2.0,
+ name="Mesh Size X",
+ min=0.01,
+ description="Mesh x size"
+ )
mesh_size_y: FloatProperty(
- name="Mesh Size Y",
- default=2.0,
- min=0.01,
- description="Mesh y size"
- )
+ name="Mesh Size Y",
+ default=2.0,
+ min=0.01,
+ description="Mesh y size"
+ )
random_seed: IntProperty(
- name="Random Seed",
- default=0,
- min=0,
- description="Randomize noise origin"
- )
+ name="Random Seed",
+ default=0,
+ min=0,
+ description="Randomize noise origin"
+ )
noise_offset_x: FloatProperty(
- name="Offset X",
- default=0.0,
- description="Noise X Offset"
- )
+ name="Offset X",
+ default=0.0,
+ description="Noise X Offset"
+ )
noise_offset_y: FloatProperty(
- name="Offset Y",
- default=0.0,
- description="Noise Y Offset"
- )
+ name="Offset Y",
+ default=0.0,
+ description="Noise Y Offset"
+ )
noise_offset_z: FloatProperty(
- name="Offset Z",
- default=0.0,
- description="Noise Z Offset"
- )
+ name="Offset Z",
+ default=0.0,
+ description="Noise Z Offset"
+ )
noise_size_x: FloatProperty(
- default=1.0,
- name="Size X",
- min=0.01,
- max=1000.0,
- description="Noise x size"
- )
+ default=1.0,
+ name="Size X",
+ min=0.01,
+ max=1000.0,
+ description="Noise x size"
+ )
noise_size_y: FloatProperty(
- name="Size Y",
- default=1.0,
- min=0.01,
- max=1000.0,
- description="Noise y size"
- )
+ name="Size Y",
+ default=1.0,
+ min=0.01,
+ max=1000.0,
+ description="Noise y size"
+ )
noise_size_z: FloatProperty(
- name="Size Z",
- default=1.0,
- min=0.01,
- max=1000.0,
- description="Noise Z size"
- )
+ name="Size Z",
+ default=1.0,
+ min=0.01,
+ max=1000.0,
+ description="Noise Z size"
+ )
noise_size: FloatProperty(
- name="Noise Size",
- default=1.0,
- min=0.01,
- max=1000.0,
- description="Noise size"
- )
+ name="Noise Size",
+ default=1.0,
+ min=0.01,
+ max=1000.0,
+ description="Noise size"
+ )
noise_type: EnumProperty(
- name="Noise Type",
- default='hetero_terrain',
- description="Noise type",
- items = [
+ name="Noise Type",
+ default='hetero_terrain',
+ description="Noise type",
+ items=[
('multi_fractal', "Multi Fractal", "Blender: Multi Fractal algorithm", 0),
('ridged_multi_fractal', "Ridged MFractal", "Blender: Ridged Multi Fractal", 1),
('hybrid_multi_fractal', "Hybrid MFractal", "Blender: Hybrid Multi Fractal", 2),
@@ -542,110 +543,110 @@ class AntLandscapePropertiesGroup(bpy.types.PropertyGroup):
('slick_rock', "Slick Rock", "A.N.T: slick rock", 16),
('planet_noise', "Planet Noise", "Planet Noise by: Farsthary", 17),
('blender_texture', "Blender Texture - Texture Nodes", "Blender texture data block", 18)]
- )
+ )
basis_type: EnumProperty(
- name="Noise Basis",
- default=ant_noise.noise_basis_default,
- description="Noise basis algorithms",
- items = ant_noise.noise_basis
- )
+ name="Noise Basis",
+ default=ant_noise.noise_basis_default,
+ description="Noise basis algorithms",
+ items=ant_noise.noise_basis
+ )
vl_basis_type: EnumProperty(
- name="vlNoise Basis",
- default=ant_noise.noise_basis_default,
- description="VLNoise basis algorithms",
- items = ant_noise.noise_basis
- )
+ name="vlNoise Basis",
+ default=ant_noise.noise_basis_default,
+ description="VLNoise basis algorithms",
+ items=ant_noise.noise_basis
+ )
distortion: FloatProperty(
- name="Distortion",
- default=1.0,
- min=0.01,
- max=100.0,
- description="Distortion amount"
- )
+ name="Distortion",
+ default=1.0,
+ min=0.01,
+ max=100.0,
+ description="Distortion amount"
+ )
hard_noise: EnumProperty(
- name="Soft Hard",
- default="0",
- description="Soft Noise, Hard noise",
- items = [
+ name="Soft Hard",
+ default="0",
+ description="Soft Noise, Hard noise",
+ items=[
("0", "Soft", "Soft Noise", 0),
("1", "Hard", "Hard noise", 1)]
- )
+ )
noise_depth: IntProperty(
- name="Depth",
- default=8,
- min=0,
- max=16,
- description="Noise Depth - number of frequencies in the fBm"
- )
+ name="Depth",
+ default=8,
+ min=0,
+ max=16,
+ description="Noise Depth - number of frequencies in the fBm"
+ )
amplitude: FloatProperty(
- name="Amp",
- default=0.5,
- min=0.01,
- max=1.0,
- description="Amplitude"
- )
+ name="Amp",
+ default=0.5,
+ min=0.01,
+ max=1.0,
+ description="Amplitude"
+ )
frequency: FloatProperty(
- name="Freq",
- default=2.0,
- min=0.01,
- max=5.0,
- description="Frequency"
- )
+ name="Freq",
+ default=2.0,
+ min=0.01,
+ max=5.0,
+ description="Frequency"
+ )
dimension: FloatProperty(
- name="Dimension",
- default=1.0,
- min=0.01,
- max=2.0,
- description="H - fractal dimension of the roughest areas"
- )
+ name="Dimension",
+ default=1.0,
+ min=0.01,
+ max=2.0,
+ description="H - fractal dimension of the roughest areas"
+ )
lacunarity: FloatProperty(
- name="Lacunarity",
- min=0.01,
- max=6.0,
- default=2.0,
- description="Lacunarity - gap between successive frequencies"
- )
+ name="Lacunarity",
+ min=0.01,
+ max=6.0,
+ default=2.0,
+ description="Lacunarity - gap between successive frequencies"
+ )
offset: FloatProperty(
- name="Offset",
- default=1.0,
- min=0.01,
- max=6.0,
- description="Offset - raises the terrain from sea level"
- )
+ name="Offset",
+ default=1.0,
+ min=0.01,
+ max=6.0,
+ description="Offset - raises the terrain from sea level"
+ )
gain: FloatProperty(
- name="Gain",
- default=1.0,
- min=0.01,
- max=6.0,
- description="Gain - scale factor"
- )
+ name="Gain",
+ default=1.0,
+ min=0.01,
+ max=6.0,
+ description="Gain - scale factor"
+ )
marble_bias: EnumProperty(
- name="Bias",
- default="0",
- description="Marble bias",
- items = [
+ name="Bias",
+ default="0",
+ description="Marble bias",
+ items=[
("0", "Sin", "Sin", 0),
("1", "Cos", "Cos", 1),
("2", "Tri", "Tri", 2),
("3", "Saw", "Saw", 3)]
- )
+ )
marble_sharp: EnumProperty(
- name="Sharp",
- default="0",
- description="Marble sharpness",
- items = [
+ name="Sharp",
+ default="0",
+ description="Marble sharpness",
+ items=[
("0", "Soft", "Soft", 0),
("1", "Sharp", "Sharp", 1),
("2", "Sharper", "Sharper", 2),
("3", "Soft inv.", "Soft", 3),
("4", "Sharp inv.", "Sharp", 4),
("5", "Sharper inv.", "Sharper", 5)]
- )
+ )
marble_shape: EnumProperty(
- name="Shape",
- default="0",
- description="Marble shape",
- items= [
+ name="Shape",
+ default="0",
+ description="Marble shape",
+ items=[
("0", "Default", "Default", 0),
("1", "Ring", "Ring", 1),
("2", "Swirl", "Swirl", 2),
@@ -654,38 +655,38 @@ class AntLandscapePropertiesGroup(bpy.types.PropertyGroup):
("5", "Z", "Z", 5),
("6", "Y", "Y", 6),
("7", "X", "X", 7)]
- )
+ )
height: FloatProperty(
- name="Height",
- default=0.5,
- min=-10000.0,
- max=10000.0,
- description="Noise intensity scale"
- )
+ name="Height",
+ default=0.5,
+ min=-10000.0,
+ max=10000.0,
+ description="Noise intensity scale"
+ )
height_invert: BoolProperty(
- name="Invert",
- default=False,
- description="Height invert",
- )
+ name="Invert",
+ default=False,
+ description="Height invert",
+ )
height_offset: FloatProperty(
- name="Offset",
- default=0.0,
- min=-10000.0,
- max=10000.0,
- description="Height offset"
- )
+ name="Offset",
+ default=0.0,
+ min=-10000.0,
+ max=10000.0,
+ description="Height offset"
+ )
fx_mixfactor: FloatProperty(
- name="Mix Factor",
- default=0.0,
- min=-1.0,
- max=1.0,
- description="Effect mix factor: -1.0 = Noise, +1.0 = Effect"
- )
+ name="Mix Factor",
+ default=0.0,
+ min=-1.0,
+ max=1.0,
+ description="Effect mix factor: -1.0 = Noise, +1.0 = Effect"
+ )
fx_mix_mode: EnumProperty(
- name="Effect Mix",
- default="0",
- description="Effect mix mode",
- items = [
+ name="Effect Mix",
+ default="0",
+ description="Effect mix mode",
+ items=[
("0", "Mix", "Mix", 0),
("1", "Add", "Add", 1),
("2", "Sub", "Subtract", 2),
@@ -695,13 +696,13 @@ class AntLandscapePropertiesGroup(bpy.types.PropertyGroup):
("6", "Mod", "Modulo", 6),
("7", "Min", "Minimum", 7),
("8", "Max", "Maximum", 8)
- ]
- )
+ ]
+ )
fx_type: EnumProperty(
- name="Effect Type",
- default="0",
- description="Effect type",
- items = [
+ name="Effect Type",
+ default="0",
+ description="Effect type",
+ items=[
("0", "None", "No effect", 0),
("1", "Gradient", "Gradient", 1),
("2", "Waves", "Waves - Bumps", 2),
@@ -724,183 +725,184 @@ class AntLandscapePropertiesGroup(bpy.types.PropertyGroup):
("19", "Stone", "Stone", 19),
("20", "Flat Turb", "Flat turbulence", 20),
("21", "Flat Voronoi", "Flat voronoi", 21)
- ]
- )
+ ]
+ )
fx_bias: EnumProperty(
- name="Effect Bias",
- default="0",
- description="Effect bias type",
- items = [
+ name="Effect Bias",
+ default="0",
+ description="Effect bias type",
+ items=[
("0", "Sin", "Sin", 0),
("1", "Cos", "Cos", 1),
("2", "Tri", "Tri", 2),
("3", "Saw", "Saw", 3),
("4", "None", "None", 4)]
- )
+ )
fx_turb: FloatProperty(
- name="Distortion",
- default=0.0,
- min=0.0,
- max=1000.0,
- description="Effect turbulence distortion"
- )
+ name="Distortion",
+ default=0.0,
+ min=0.0,
+ max=1000.0,
+ description="Effect turbulence distortion"
+ )
fx_depth: IntProperty(
- name="Depth",
- default=0,
- min=0,
- max=16,
- description="Effect depth - number of frequencies"
- )
+ name="Depth",
+ default=0,
+ min=0,
+ max=16,
+ description="Effect depth - number of frequencies"
+ )
fx_amplitude: FloatProperty(
- name="Amp",
- default=0.5,
- min=0.01,
- max=1.0,
- description="Amplitude"
- )
+ name="Amp",
+ default=0.5,
+ min=0.01,
+ max=1.0,
+ description="Amplitude"
+ )
fx_frequency: FloatProperty(
- name="Freq",
- default=2.0,
- min=0.01,
- max=5.0,
- description="Frequency"
- )
+ name="Freq",
+ default=2.0,
+ min=0.01,
+ max=5.0,
+ description="Frequency"
+ )
fx_size: FloatProperty(
- name="Effect Size",
- default=1.0,
- min=0.01,
- max=1000.0,
- description="Effect size"
- )
+ name="Effect Size",
+ default=1.0,
+ min=0.01,
+ max=1000.0,
+ description="Effect size"
+ )
fx_loc_x: FloatProperty(
- name="Offset X",
- default=0.0,
- description="Effect x offset"
- )
+ name="Offset X",
+ default=0.0,
+ description="Effect x offset"
+ )
fx_loc_y: FloatProperty(
- name="Offset Y",
- default=0.0,
- description="Effect y offset"
- )
+ name="Offset Y",
+ default=0.0,
+ description="Effect y offset"
+ )
fx_height: FloatProperty(
- name="Intensity",
- default=1.0,
- min=-1000.0,
- max=1000.0,
- description="Effect intensity scale"
- )
+ name="Intensity",
+ default=1.0,
+ min=-1000.0,
+ max=1000.0,
+ description="Effect intensity scale"
+ )
fx_invert: BoolProperty(
- name="Invert",
- default=False,
- description="Effect invert"
- )
+ name="Invert",
+ default=False,
+ description="Effect invert"
+ )
fx_offset: FloatProperty(
- name="Offset",
- default=0.0,
- min=-1000.0,
- max=1000.0,
- description="Effect height offset"
- )
+ name="Offset",
+ default=0.0,
+ min=-1000.0,
+ max=1000.0,
+ description="Effect height offset"
+ )
edge_falloff: EnumProperty(
- name="Falloff",
- default="3",
- description="Flatten edges",
- items = [
+ name="Falloff",
+ default="3",
+ description="Flatten edges",
+ items=[
("0", "None", "None", 0),
("1", "Y", "Y Falloff", 1),
("2", "X", "X Falloff", 2),
("3", "X Y", "X Y Falloff", 3)]
- )
+ )
falloff_x: FloatProperty(
- name="Falloff X",
- default=4.0,
- min=0.1,
- max=100.0,
- description="Falloff x scale"
- )
+ name="Falloff X",
+ default=4.0,
+ min=0.1,
+ max=100.0,
+ description="Falloff x scale"
+ )
falloff_y: FloatProperty(
- name="Falloff Y",
- default=4.0,
- min=0.1,
- max=100.0,
- description="Falloff y scale"
- )
+ name="Falloff Y",
+ default=4.0,
+ min=0.1,
+ max=100.0,
+ description="Falloff y scale"
+ )
edge_level: FloatProperty(
- name="Edge Level",
- default=0.0,
- min=-10000.0,
- max=10000.0,
- description="Edge level, sealevel offset"
- )
+ name="Edge Level",
+ default=0.0,
+ min=-10000.0,
+ max=10000.0,
+ description="Edge level, sealevel offset"
+ )
maximum: FloatProperty(
- name="Maximum",
- default=1.0,
- min=-10000.0,
- max=10000.0,
- description="Maximum, flattens terrain at plateau level"
- )
+ name="Maximum",
+ default=1.0,
+ min=-10000.0,
+ max=10000.0,
+ description="Maximum, flattens terrain at plateau level"
+ )
minimum: FloatProperty(
- name="Minimum",
- default=-1.0,
- min=-10000.0,
- max=10000.0,
- description="Minimum, flattens terrain at seabed level"
- )
+ name="Minimum",
+ default=-1.0,
+ min=-10000.0,
+ max=10000.0,
+ description="Minimum, flattens terrain at seabed level"
+ )
vert_group: StringProperty(
- name="Vertex Group",
- default=""
- )
+ name="Vertex Group",
+ default=""
+ )
strata: FloatProperty(
- name="Amount",
- default=5.0,
- min=0.01,
- max=1000.0,
- description="Strata layers / terraces"
- )
+ name="Amount",
+ default=5.0,
+ min=0.01,
+ max=1000.0,
+ description="Strata layers / terraces"
+ )
strata_type: EnumProperty(
- name="Strata",
- default="0",
- description="Strata types",
- items = [
+ name="Strata",
+ default="0",
+ description="Strata types",
+ items=[
("0", "None", "No strata", 0),
("1", "Smooth", "Smooth transitions", 1),
("2", "Sharp Sub", "Sharp subtract transitions", 2),
("3", "Sharp Add", "Sharp add transitions", 3),
("4", "Quantize", "Quantize", 4),
("5", "Quantize Mix", "Quantize mixed", 5)]
- )
+ )
water_plane: BoolProperty(
- name="Water Plane",
- default=False,
- description="Add water plane"
- )
+ name="Water Plane",
+ default=False,
+ description="Add water plane"
+ )
water_level: FloatProperty(
- name="Level",
- default=0.01,
- min=-10000.0,
- max=10000.0,
- description="Water level"
- )
+ name="Level",
+ default=0.01,
+ min=-10000.0,
+ max=10000.0,
+ description="Water level"
+ )
remove_double: BoolProperty(
- name="Remove Doubles",
- default=False,
- description="Remove doubles"
- )
+ name="Remove Doubles",
+ default=False,
+ description="Remove doubles"
+ )
refresh: BoolProperty(
- name="Refresh",
- default=False,
- description="Refresh"
- )
+ name="Refresh",
+ default=False,
+ description="Refresh"
+ )
auto_refresh: BoolProperty(
- name="Auto",
- default=True,
- description="Automatic refresh"
- )
+ name="Auto",
+ default=True,
+ description="Automatic refresh"
+ )
# ------------------------------------------------------------
# Register:
+
classes = (
AntLandscapeAddPanel,
AntLandscapeToolsPanel,
@@ -916,12 +918,16 @@ classes = (
ant_functions.Eroder,
)
+
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.VIEW3D_MT_mesh_add.append(menu_func_landscape)
- bpy.types.Object.ant_landscape = PointerProperty(type=AntLandscapePropertiesGroup, name="ANT_Landscape", description="Landscape properties")
+ bpy.types.Object.ant_landscape = PointerProperty(
+ type=AntLandscapePropertiesGroup,
+ name="ANT_Landscape",
+ description="Landscape properties")
bpy.types.VIEW3D_MT_paint_weight.append(menu_func_eroder)
diff --git a/ant_landscape/add_mesh_ant_landscape.py b/ant_landscape/add_mesh_ant_landscape.py
index a148f448..e12561bc 100644
--- a/ant_landscape/add_mesh_ant_landscape.py
+++ b/ant_landscape/add_mesh_ant_landscape.py
@@ -6,30 +6,32 @@
# import modules
import bpy
from bpy.props import (
- BoolProperty,
- EnumProperty,
- FloatProperty,
- IntProperty,
- StringProperty,
- FloatVectorProperty,
- )
+ BoolProperty,
+ EnumProperty,
+ FloatProperty,
+ IntProperty,
+ StringProperty,
+ FloatVectorProperty,
+)
from .ant_functions import (
- grid_gen,
- sphere_gen,
- create_mesh_object,
- store_properties,
- draw_ant_refresh,
- draw_ant_main,
- draw_ant_noise,
- draw_ant_displace,
- draw_ant_water,
- )
+ grid_gen,
+ sphere_gen,
+ create_mesh_object,
+ store_properties,
+ draw_ant_refresh,
+ draw_ant_main,
+ draw_ant_noise,
+ draw_ant_displace,
+ draw_ant_water,
+)
from ant_landscape import ant_noise
# ------------------------------------------------------------
# Add landscape
+
+
class AntAddLandscape(bpy.types.Operator):
bl_idname = "mesh.landscape_add"
bl_label = "Another Noise Tool - Landscape"
@@ -37,131 +39,131 @@ class AntAddLandscape(bpy.types.Operator):
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
ant_terrain_name: StringProperty(
- name="Name",
- default="Landscape"
- )
+ name="Name",
+ default="Landscape"
+ )
land_material: StringProperty(
- name='Material',
- default="",
- description="Terrain material"
- )
+ name='Material',
+ default="",
+ description="Terrain material"
+ )
water_material: StringProperty(
- name='Material',
- default="",
- description="Water plane material"
- )
+ name='Material',
+ default="",
+ description="Water plane material"
+ )
texture_block: StringProperty(
- name="Texture",
- default=""
- )
+ name="Texture",
+ default=""
+ )
at_cursor: BoolProperty(
- name="Cursor",
- default=True,
- description="Place at cursor location",
- )
+ name="Cursor",
+ default=True,
+ description="Place at cursor location",
+ )
smooth_mesh: BoolProperty(
- name="Smooth",
- default=True,
- description="Shade smooth"
- )
+ name="Smooth",
+ default=True,
+ description="Shade smooth"
+ )
tri_face: BoolProperty(
- name="Triangulate",
- default=False,
- description="Triangulate faces"
- )
+ name="Triangulate",
+ default=False,
+ description="Triangulate faces"
+ )
sphere_mesh: BoolProperty(
- name="Sphere",
- default=False,
- description="Generate uv sphere - remove doubles when ready"
- )
+ name="Sphere",
+ default=False,
+ description="Generate uv sphere - remove doubles when ready"
+ )
subdivision_x: IntProperty(
- name="Subdivisions X",
- default=128,
- min=4,
- max=6400,
- description="Mesh X subdivisions"
- )
+ name="Subdivisions X",
+ default=128,
+ min=4,
+ max=6400,
+ description="Mesh X subdivisions"
+ )
subdivision_y: IntProperty(
- default=128,
- name="Subdivisions Y",
- min=4,
- max=6400,
- description="Mesh Y subdivisions"
- )
+ default=128,
+ name="Subdivisions Y",
+ min=4,
+ max=6400,
+ description="Mesh Y subdivisions"
+ )
mesh_size: FloatProperty(
- default=2.0,
- name="Mesh Size",
- min=0.01,
- max=100000.0,
- description="Mesh size"
- )
+ default=2.0,
+ name="Mesh Size",
+ min=0.01,
+ max=100000.0,
+ description="Mesh size"
+ )
mesh_size_x: FloatProperty(
- default=2.0,
- name="Mesh Size X",
- min=0.01,
- description="Mesh x size"
- )
+ default=2.0,
+ name="Mesh Size X",
+ min=0.01,
+ description="Mesh x size"
+ )
mesh_size_y: FloatProperty(
- name="Mesh Size Y",
- default=2.0,
- min=0.01,
- description="Mesh y size"
- )
+ name="Mesh Size Y",
+ default=2.0,
+ min=0.01,
+ description="Mesh y size"
+ )
random_seed: IntProperty(
- name="Random Seed",
- default=0,
- min=0,
- description="Randomize noise origin"
- )
+ name="Random Seed",
+ default=0,
+ min=0,
+ description="Randomize noise origin"
+ )
noise_offset_x: FloatProperty(
- name="Offset X",
- default=0.0,
- description="Noise X Offset"
- )
+ name="Offset X",
+ default=0.0,
+ description="Noise X Offset"
+ )
noise_offset_y: FloatProperty(
- name="Offset Y",
- default=0.0,
- description="Noise Y Offset"
- )
+ name="Offset Y",
+ default=0.0,
+ description="Noise Y Offset"
+ )
noise_offset_z: FloatProperty(
- name="Offset Z",
- default=0.0,
- description="Noise Z Offset"
- )
+ name="Offset Z",
+ default=0.0,
+ description="Noise Z Offset"
+ )
noise_size_x: FloatProperty(
- default=1.0,
- name="Size X",
- min=0.01,
- max=1000.0,
- description="Noise x size"
- )
+ default=1.0,
+ name="Size X",
+ min=0.01,
+ max=1000.0,
+ description="Noise x size"
+ )
noise_size_y: FloatProperty(
- name="Size Y",
- default=1.0,
- min=0.01,
- max=1000.0,
- description="Noise y size"
- )
+ name="Size Y",
+ default=1.0,
+ min=0.01,
+ max=1000.0,
+ description="Noise y size"
+ )
noise_size_z: FloatProperty(
- name="Size Z",
- default=1.0,
- min=0.01,
- max=1000.0,
- description="Noise Z size"
- )
+ name="Size Z",
+ default=1.0,
+ min=0.01,
+ max=1000.0,
+ description="Noise Z size"
+ )
noise_size: FloatProperty(
- name="Noise Size",
- default=1.0,
- min=0.01,
- max=1000.0,
- description="Noise size"
- )
+ name="Noise Size",
+ default=1.0,
+ min=0.01,
+ max=1000.0,
+ description="Noise size"
+ )
noise_type: EnumProperty(
- name="Noise Type",
- default='hetero_terrain',
- description="Noise type",
- items = [
+ name="Noise Type",
+ default='hetero_terrain',
+ description="Noise type",
+ items=[
('multi_fractal', "Multi Fractal", "Blender: Multi Fractal algorithm", 0),
('ridged_multi_fractal', "Ridged MFractal", "Blender: Ridged Multi Fractal", 1),
('hybrid_multi_fractal', "Hybrid MFractal", "Blender: Hybrid Multi Fractal", 2),
@@ -181,110 +183,110 @@ class AntAddLandscape(bpy.types.Operator):
('slick_rock', "Slick Rock", "A.N.T: slick rock", 16),
('planet_noise', "Planet Noise", "Planet Noise by: Farsthary", 17),
('blender_texture', "Blender Texture - Texture Nodes", "Blender texture data block", 18)]
- )
+ )
basis_type: EnumProperty(
- name="Noise Basis",
- default=ant_noise.noise_basis_default,
- description="Noise basis algorithms",
- items = ant_noise.noise_basis
- )
+ name="Noise Basis",
+ default=ant_noise.noise_basis_default,
+ description="Noise basis algorithms",
+ items=ant_noise.noise_basis
+ )
vl_basis_type: EnumProperty(
- name="vlNoise Basis",
- default=ant_noise.noise_basis_default,
- description="VLNoise basis algorithms",
- items = ant_noise.noise_basis
- )
+ name="vlNoise Basis",
+ default=ant_noise.noise_basis_default,
+ description="VLNoise basis algorithms",
+ items=ant_noise.noise_basis
+ )
distortion: FloatProperty(
- name="Distortion",
- default=1.0,
- min=0.01,
- max=100.0,
- description="Distortion amount"
- )
+ name="Distortion",
+ default=1.0,
+ min=0.01,
+ max=100.0,
+ description="Distortion amount"
+ )
hard_noise: EnumProperty(
- name="Soft Hard",
- default="0",
- description="Soft Noise, Hard noise",
- items = [
+ name="Soft Hard",
+ default="0",
+ description="Soft Noise, Hard noise",
+ items=[
("0", "Soft", "Soft Noise", 0),
("1", "Hard", "Hard noise", 1)]
- )
+ )
noise_depth: IntProperty(
- name="Depth",
- default=8,
- min=0,
- max=16,
- description="Noise Depth - number of frequencies in the fBm"
- )
+ name="Depth",
+ default=8,
+ min=0,
+ max=16,
+ description="Noise Depth - number of frequencies in the fBm"
+ )
amplitude: FloatProperty(
- name="Amp",
- default=0.5,
- min=0.01,
- max=1.0,
- description="Amplitude"
- )
+ name="Amp",
+ default=0.5,
+ min=0.01,
+ max=1.0,
+ description="Amplitude"
+ )
frequency: FloatProperty(
- name="Freq",
- default=2.0,
- min=0.01,
- max=5.0,
- description="Frequency"
- )
+ name="Freq",
+ default=2.0,
+ min=0.01,
+ max=5.0,
+ description="Frequency"
+ )
dimension: FloatProperty(
- name="Dimension",
- default=1.0,
- min=0.01,
- max=2.0,
- description="H - fractal dimension of the roughest areas"
- )
+ name="Dimension",
+ default=1.0,
+ min=0.01,
+ max=2.0,
+ description="H - fractal dimension of the roughest areas"
+ )
lacunarity: FloatProperty(
- name="Lacunarity",
- min=0.01,
- max=6.0,
- default=2.0,
- description="Lacunarity - gap between successive frequencies"
- )
+ name="Lacunarity",
+ min=0.01,
+ max=6.0,
+ default=2.0,
+ description="Lacunarity - gap between successive frequencies"
+ )
offset: FloatProperty(
- name="Offset",
- default=1.0,
- min=0.01,
- max=6.0,
- description="Offset - raises the terrain from sea level"
- )
+ name="Offset",
+ default=1.0,
+ min=0.01,
+ max=6.0,
+ description="Offset - raises the terrain from sea level"
+ )
gain: FloatProperty(
- name="Gain",
- default=1.0,
- min=0.01,
- max=6.0,
- description="Gain - scale factor"
- )
+ name="Gain",
+ default=1.0,
+ min=0.01,
+ max=6.0,
+ description="Gain - scale factor"
+ )
marble_bias: EnumProperty(
- name="Bias",
- default="0",
- description="Marble bias",
- items = [
+ name="Bias",
+ default="0",
+ description="Marble bias",
+ items=[
("0", "Sin", "Sin", 0),
("1", "Cos", "Cos", 1),
("2", "Tri", "Tri", 2),
("3", "Saw", "Saw", 3)]
- )
+ )
marble_sharp: EnumProperty(
- name="Sharp",
- default="0",
- description="Marble sharpness",
- items = [
+ name="Sharp",
+ default="0",
+ description="Marble sharpness",
+ items=[
("0", "Soft", "Soft", 0),
("1", "Sharp", "Sharp", 1),
("2", "Sharper", "Sharper", 2),
("3", "Soft inv.", "Soft", 3),
("4", "Sharp inv.", "Sharp", 4),
("5", "Sharper inv.", "Sharper", 5)]
- )
+ )
marble_shape: EnumProperty(
- name="Shape",
- default="0",
- description="Marble shape",
- items= [
+ name="Shape",
+ default="0",
+ description="Marble shape",
+ items=[
("0", "Default", "Default", 0),
("1", "Ring", "Ring", 1),
("2", "Swirl", "Swirl", 2),
@@ -293,38 +295,38 @@ class AntAddLandscape(bpy.types.Operator):
("5", "Z", "Z", 5),
("6", "Y", "Y", 6),
("7", "X", "X", 7)]
- )
+ )
height: FloatProperty(
- name="Height",
- default=0.5,
- min=-10000.0,
- max=10000.0,
- description="Noise intensity scale"
- )
+ name="Height",
+ default=0.5,
+ min=-10000.0,
+ max=10000.0,
+ description="Noise intensity scale"
+ )
height_invert: BoolProperty(
- name="Invert",
- default=False,
- description="Height invert",
- )
+ name="Invert",
+ default=False,
+ description="Height invert",
+ )
height_offset: FloatProperty(
- name="Offset",
- default=0.0,
- min=-10000.0,
- max=10000.0,
- description="Height offset"
- )
+ name="Offset",
+ default=0.0,
+ min=-10000.0,
+ max=10000.0,
+ description="Height offset"
+ )
fx_mixfactor: FloatProperty(
- name="Mix Factor",
- default=0.0,
- min=-1.0,
- max=1.0,
- description="Effect mix factor: -1.0 = Noise, +1.0 = Effect"
- )
+ name="Mix Factor",
+ default=0.0,
+ min=-1.0,
+ max=1.0,
+ description="Effect mix factor: -1.0 = Noise, +1.0 = Effect"
+ )
fx_mix_mode: EnumProperty(
- name="Effect Mix",
- default="0",
- description="Effect mix mode",
- items = [
+ name="Effect Mix",
+ default="0",
+ description="Effect mix mode",
+ items=[
("0", "Mix", "Mix", 0),
("1", "Add", "Add", 1),
("2", "Sub", "Subtract", 2),
@@ -334,13 +336,13 @@ class AntAddLandscape(bpy.types.Operator):
("6", "Mod", "Modulo", 6),
("7", "Min", "Minimum", 7),
("8", "Max", "Maximum", 8)
- ]
- )
+ ]
+ )
fx_type: EnumProperty(
- name="Effect Type",
- default="0",
- description="Effect type",
- items = [
+ name="Effect Type",
+ default="0",
+ description="Effect type",
+ items=[
("0", "None", "No effect", 0),
("1", "Gradient", "Gradient", 1),
("2", "Waves", "Waves - Bumps", 2),
@@ -363,194 +365,194 @@ class AntAddLandscape(bpy.types.Operator):
("19", "Stone", "Stone", 19),
("20", "Flat Turb", "Flat turbulence", 20),
("21", "Flat Voronoi", "Flat voronoi", 21)
- ]
- )
+ ]
+ )
fx_bias: EnumProperty(
- name="Effect Bias",
- default="0",
- description="Effect bias type",
- items = [
+ name="Effect Bias",
+ default="0",
+ description="Effect bias type",
+ items=[
("0", "Sin", "Sin", 0),
("1", "Cos", "Cos", 1),
("2", "Tri", "Tri", 2),
("3", "Saw", "Saw", 3),
("4", "None", "None", 4)]
- )
+ )
fx_turb: FloatProperty(
- name="Distortion",
- default=0.0,
- min=0.0,
- max=1000.0,
- description="Effect turbulence distortion"
- )
+ name="Distortion",
+ default=0.0,
+ min=0.0,
+ max=1000.0,
+ description="Effect turbulence distortion"
+ )
fx_depth: IntProperty(
- name="Depth",
- default=0,
- min=0,
- max=16,
- description="Effect depth - number of frequencies"
- )
+ name="Depth",
+ default=0,
+ min=0,
+ max=16,
+ description="Effect depth - number of frequencies"
+ )
fx_amplitude: FloatProperty(
- name="Amp",
- default=0.5,
- min=0.01,
- max=1.0,
- description="Amplitude"
- )
+ name="Amp",
+ default=0.5,
+ min=0.01,
+ max=1.0,
+ description="Amplitude"
+ )
fx_frequency: FloatProperty(
- name="Freq",
- default=2.0,
- min=0.01,
- max=5.0,
- description="Frequency"
- )
+ name="Freq",
+ default=2.0,
+ min=0.01,
+ max=5.0,
+ description="Frequency"
+ )
fx_size: FloatProperty(
- name="Effect Size",
- default=1.0,
- min=0.01,
- max=1000.0,
- description="Effect size"
- )
+ name="Effect Size",
+ default=1.0,
+ min=0.01,
+ max=1000.0,
+ description="Effect size"
+ )
fx_loc_x: FloatProperty(
- name="Offset X",
- default=0.0,
- description="Effect x offset"
- )
+ name="Offset X",
+ default=0.0,
+ description="Effect x offset"
+ )
fx_loc_y: FloatProperty(
- name="Offset Y",
- default=0.0,
- description="Effect y offset"
- )
+ name="Offset Y",
+ default=0.0,
+ description="Effect y offset"
+ )
fx_height: FloatProperty(
- name="Intensity",
- default=1.0,
- min=-1000.0,
- max=1000.0,
- description="Effect intensity scale"
- )
+ name="Intensity",
+ default=1.0,
+ min=-1000.0,
+ max=1000.0,
+ description="Effect intensity scale"
+ )
fx_invert: BoolProperty(
- name="Invert",
- default=False,
- description="Effect invert"
- )
+ name="Invert",
+ default=False,
+ description="Effect invert"
+ )
fx_offset: FloatProperty(
- name="Offset",
- default=0.0,
- min=-1000.0,
- max=1000.0,
- description="Effect height offset"
- )
+ name="Offset",
+ default=0.0,
+ min=-1000.0,
+ max=1000.0,
+ description="Effect height offset"
+ )
edge_falloff: EnumProperty(
- name="Falloff",
- default="3",
- description="Flatten edges",
- items = [
+ name="Falloff",
+ default="3",
+ description="Flatten edges",
+ items=[
("0", "None", "None", 0),
("1", "Y", "Y Falloff", 1),
("2", "X", "X Falloff", 2),
("3", "X Y", "X Y Falloff", 3)]
- )
+ )
falloff_x: FloatProperty(
- name="Falloff X",
- default=4.0,
- min=0.1,
- max=100.0,
- description="Falloff x scale"
- )
+ name="Falloff X",
+ default=4.0,
+ min=0.1,
+ max=100.0,
+ description="Falloff x scale"
+ )
falloff_y: FloatProperty(
- name="Falloff Y",
- default=4.0,
- min=0.1,
- max=100.0,
- description="Falloff y scale"
- )
+ name="Falloff Y",
+ default=4.0,
+ min=0.1,
+ max=100.0,
+ description="Falloff y scale"
+ )
edge_level: FloatProperty(
- name="Edge Level",
- default=0.0,
- min=-10000.0,
- max=10000.0,
- description="Edge level, sealevel offset"
- )
+ name="Edge Level",
+ default=0.0,
+ min=-10000.0,
+ max=10000.0,
+ description="Edge level, sealevel offset"
+ )
maximum: FloatProperty(
- name="Maximum",
- default=1.0,
- min=-10000.0,
- max=10000.0,
- description="Maximum, flattens terrain at plateau level"
- )
+ name="Maximum",
+ default=1.0,
+ min=-10000.0,
+ max=10000.0,
+ description="Maximum, flattens terrain at plateau level"
+ )
minimum: FloatProperty(
- name="Minimum",
- default=-1.0,
- min=-10000.0,
- max=10000.0,
- description="Minimum, flattens terrain at seabed level"
- )
+ name="Minimum",
+ default=-1.0,
+ min=-10000.0,
+ max=10000.0,
+ description="Minimum, flattens terrain at seabed level"
+ )
vert_group: StringProperty(
- name="Vertex Group",
- default=""
- )
+ name="Vertex Group",
+ default=""
+ )
strata: FloatProperty(
- name="Amount",
- default=5.0,
- min=0.01,
- max=1000.0,
- description="Strata layers / terraces"
- )
+ name="Amount",
+ default=5.0,
+ min=0.01,
+ max=1000.0,
+ description="Strata layers / terraces"
+ )
strata_type: EnumProperty(
- name="Strata",
- default="0",
- description="Strata types",
- items = [
+ name="Strata",
+ default="0",
+ description="Strata types",
+ items=[
("0", "None", "No strata", 0),
("1", "Smooth", "Smooth transitions", 1),
("2", "Sharp Sub", "Sharp subtract transitions", 2),
("3", "Sharp Add", "Sharp add transitions", 3),
("4", "Quantize", "Quantize", 4),
("5", "Quantize Mix", "Quantize mixed", 5)]
- )
+ )
water_plane: BoolProperty(
- name="Water Plane",
- default=False,
- description="Add water plane"
- )
+ name="Water Plane",
+ default=False,
+ description="Add water plane"
+ )
water_level: FloatProperty(
- name="Level",
- default=0.01,
- min=-10000.0,
- max=10000.0,
- description="Water level"
- )
+ name="Level",
+ default=0.01,
+ min=-10000.0,
+ max=10000.0,
+ description="Water level"
+ )
remove_double: BoolProperty(
- name="Remove Doubles",
- default=False,
- description="Remove doubles"
- )
+ name="Remove Doubles",
+ default=False,
+ description="Remove doubles"
+ )
show_main_settings: BoolProperty(
- name="Main Settings",
- default=True,
- description="Show settings"
- )
+ name="Main Settings",
+ default=True,
+ description="Show settings"
+ )
show_noise_settings: BoolProperty(
- name="Noise Settings",
- default=True,
- description="Show noise settings"
- )
+ name="Noise Settings",
+ default=True,
+ description="Show noise settings"
+ )
show_displace_settings: BoolProperty(
- name="Displace Settings",
- default=True,
- description="Show displace settings"
- )
+ name="Displace Settings",
+ default=True,
+ description="Show displace settings"
+ )
refresh: BoolProperty(
- name="Refresh",
- default=False,
- description="Refresh"
- )
+ name="Refresh",
+ default=False,
+ description="Refresh"
+ )
auto_refresh: BoolProperty(
- name="Auto",
- default=True,
- description="Automatic refresh"
- )
+ name="Auto",
+ default=True,
+ description="Automatic refresh"
+ )
@classmethod
def poll(self, context):
@@ -567,12 +569,10 @@ class AntAddLandscape(bpy.types.Operator):
draw_ant_displace(self, context, generate=True)
draw_ant_water(self, context)
-
def invoke(self, context, event):
self.refresh = True
return self.execute(context)
-
def execute(self, context):
if not self.refresh:
return {'PASS_THROUGH'}
@@ -652,7 +652,7 @@ class AntAddLandscape(bpy.types.Operator):
self.fx_height,
self.fx_offset,
self.fx_invert
- ]
+ ]
scene = context.scene
vl = context.view_layer
@@ -661,37 +661,37 @@ class AntAddLandscape(bpy.types.Operator):
if self.ant_terrain_name != "":
new_name = self.ant_terrain_name
else:
- new_name = "Landscape"
+ new_name = "Landscape"
if self.sphere_mesh:
# sphere
verts, faces = sphere_gen(
- self.subdivision_y,
- self.subdivision_x,
- self.tri_face,
- self.mesh_size,
- ant_props,
- False,
- 0.0
- )
+ self.subdivision_y,
+ self.subdivision_x,
+ self.tri_face,
+ self.mesh_size,
+ ant_props,
+ False,
+ 0.0,
+ )
new_ob = create_mesh_object(context, verts, [], faces, new_name)
if self.remove_double:
new_ob.select_set(True)
- bpy.ops.object.mode_set(mode = 'EDIT')
+ bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.remove_doubles(threshold=0.0001, use_unselected=False)
- bpy.ops.object.mode_set(mode = 'OBJECT')
+ bpy.ops.object.mode_set(mode='OBJECT')
else:
# grid
verts, faces = grid_gen(
- self.subdivision_x,
- self.subdivision_y,
- self.tri_face,
- self.mesh_size_x,
- self.mesh_size_y,
- ant_props,
- False,
- 0.0
- )
+ self.subdivision_x,
+ self.subdivision_y,
+ self.tri_face,
+ self.mesh_size_x,
+ self.mesh_size_y,
+ ant_props,
+ False,
+ 0.0,
+ )
new_ob = create_mesh_object(context, verts, [], faces, new_name)
new_ob.select_set(True)
@@ -712,33 +712,33 @@ class AntAddLandscape(bpy.types.Operator):
if self.sphere_mesh:
# sphere
verts, faces = sphere_gen(
- self.subdivision_y,
- self.subdivision_x,
- self.tri_face,
- self.mesh_size,
- ant_props,
- self.water_plane,
- self.water_level
- )
- wobj = create_mesh_object(context, verts, [], faces, new_name+"_plane")
+ self.subdivision_y,
+ self.subdivision_x,
+ self.tri_face,
+ self.mesh_size,
+ ant_props,
+ self.water_plane,
+ self.water_level,
+ )
+ wobj = create_mesh_object(context, verts, [], faces, new_name + "_plane")
if self.remove_double:
wobj.select_set(True)
- bpy.ops.object.mode_set(mode = 'EDIT')
+ bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.remove_doubles(threshold=0.0001, use_unselected=False)
- bpy.ops.object.mode_set(mode = 'OBJECT')
+ bpy.ops.object.mode_set(mode='OBJECT')
else:
# grid
verts, faces = grid_gen(
- 2,
- 2,
- self.tri_face,
- self.mesh_size_x,
- self.mesh_size_y,
- ant_props,
- self.water_plane,
- self.water_level
- )
- wobj = create_mesh_object(context, verts, [], faces, new_name+"_plane")
+ 2,
+ 2,
+ self.tri_face,
+ self.mesh_size_x,
+ self.mesh_size_y,
+ ant_props,
+ self.water_plane,
+ self.water_level,
+ )
+ wobj = create_mesh_object(context, verts, [], faces, new_name + "_plane")
wobj.select_set(True)
diff --git a/ant_landscape/ant_functions.py b/ant_landscape/ant_functions.py
index f63747aa..1337533c 100644
--- a/ant_landscape/ant_functions.py
+++ b/ant_landscape/ant_functions.py
@@ -9,16 +9,16 @@
# import modules
import bpy
from bpy.props import (
- BoolProperty,
- FloatProperty,
- StringProperty,
- EnumProperty,
- IntProperty,
- PointerProperty,
- )
+ BoolProperty,
+ FloatProperty,
+ StringProperty,
+ EnumProperty,
+ IntProperty,
+ PointerProperty,
+)
from math import (
- sin, cos, pi,
- )
+ sin, cos, pi,
+)
from .ant_noise import noise_gen
# ------------------------------------------------------------
@@ -29,6 +29,7 @@ from .ant_noise import noise_gen
from bpy_extras import object_utils
+
def create_mesh_object(context, verts, edges, faces, name):
# Create new mesh
mesh = bpy.data.meshes.new(name)
@@ -45,7 +46,7 @@ def grid_gen(sub_d_x, sub_d_y, tri, meshsize_x, meshsize_y, props, water_plane,
faces = []
vappend = verts.append
fappend = faces.append
- for i in range (0, sub_d_x):
+ for i in range(0, sub_d_x):
x = meshsize_x * (i / (sub_d_x - 1) - 1 / 2)
for j in range(0, sub_d_y):
y = meshsize_y * (j / (sub_d_y - 1) - 1 / 2)
@@ -53,7 +54,7 @@ def grid_gen(sub_d_x, sub_d_y, tri, meshsize_x, meshsize_y, props, water_plane,
z = noise_gen((x, y, 0), props)
else:
z = water_level
- vappend((x,y,z))
+ vappend((x, y, z))
if i > 0 and j > 0:
A = i * sub_d_y + (j - 1)
@@ -89,8 +90,8 @@ def sphere_gen(sub_d_x, sub_d_y, tri, meshsize, props, water_plane, water_level)
vappend(((u + u * h), (v + v * h), (w + w * h)))
count = 0
- for i in range (0, sub_d_y * (sub_d_x - 1)):
- if count < sub_d_y - 1 :
+ for i in range(0, sub_d_y * (sub_d_x - 1)):
+ if count < sub_d_y - 1:
A = i + 1
B = i
C = (i + sub_d_y)
@@ -115,7 +116,6 @@ class AntLandscapeRefresh(bpy.types.Operator):
bl_description = "Refresh landscape with current settings"
bl_options = {'REGISTER', 'UNDO'}
-
@classmethod
def poll(cls, context):
ob = bpy.context.active_object
@@ -125,8 +125,8 @@ class AntLandscapeRefresh(bpy.types.Operator):
# ant object items
obj = bpy.context.active_object
- bpy.ops.object.mode_set(mode = 'EDIT')
- bpy.ops.object.mode_set(mode = 'OBJECT')
+ bpy.ops.object.mode_set(mode='EDIT')
+ bpy.ops.object.mode_set(mode='OBJECT')
keys = obj.ant_landscape.keys()
if keys:
@@ -158,13 +158,14 @@ class AntLandscapeRefresh(bpy.types.Operator):
# ------------------------------------------------------------
# Do regenerate
+
+
class AntLandscapeRegenerate(bpy.types.Operator):
bl_idname = "mesh.ant_landscape_regenerate"
bl_label = "Regenerate"
bl_description = "Regenerate landscape with current settings"
bl_options = {'REGISTER', 'UNDO'}
-
@classmethod
def poll(cls, context):
ob = bpy.context.active_object
@@ -172,7 +173,6 @@ class AntLandscapeRegenerate(bpy.types.Operator):
return False
return ob.ant_landscape
-
def execute(self, context):
view_layer = bpy.context.view_layer
@@ -192,32 +192,32 @@ class AntLandscapeRegenerate(bpy.types.Operator):
if ob['sphere_mesh']:
# sphere
verts, faces = sphere_gen(
- ob['subdivision_y'],
- ob['subdivision_x'],
- ob['tri_face'],
- ob['mesh_size'],
- ant_props,
- False,
- 0.0
- )
+ ob['subdivision_y'],
+ ob['subdivision_x'],
+ ob['tri_face'],
+ ob['mesh_size'],
+ ant_props,
+ False,
+ 0.0,
+ )
new_ob = create_mesh_object(context, verts, [], faces, new_name)
if ob['remove_double']:
new_ob.select_set(True)
- bpy.ops.object.mode_set(mode = 'EDIT')
+ bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.remove_doubles(threshold=0.0001, use_unselected=False)
- bpy.ops.object.mode_set(mode = 'OBJECT')
+ bpy.ops.object.mode_set(mode='OBJECT')
else:
# grid
verts, faces = grid_gen(
- ob['subdivision_x'],
- ob['subdivision_y'],
- ob['tri_face'],
- ob['mesh_size_x'],
- ob['mesh_size_y'],
- ant_props,
- False,
- 0.0
- )
+ ob['subdivision_x'],
+ ob['subdivision_y'],
+ ob['tri_face'],
+ ob['mesh_size_x'],
+ ob['mesh_size_y'],
+ ant_props,
+ False,
+ 0.0,
+ )
new_ob = create_mesh_object(context, verts, [], faces, new_name)
new_ob.select_set(True)
@@ -235,33 +235,33 @@ class AntLandscapeRegenerate(bpy.types.Operator):
if ob['sphere_mesh']:
# sphere
verts, faces = sphere_gen(
- ob['subdivision_y'],
- ob['subdivision_x'],
- ob['tri_face'],
- ob['mesh_size'],
- ant_props,
- ob['water_plane'],
- ob['water_level']
- )
- wobj = create_mesh_object(context, verts, [], faces, new_name+"_plane")
+ ob['subdivision_y'],
+ ob['subdivision_x'],
+ ob['tri_face'],
+ ob['mesh_size'],
+ ant_props,
+ ob['water_plane'],
+ ob['water_level'],
+ )
+ wobj = create_mesh_object(context, verts, [], faces, new_name + "_plane")
if ob['remove_double']:
wobj.select_set(True)
- bpy.ops.object.mode_set(mode = 'EDIT')
+ bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.remove_doubles(threshold=0.0001, use_unselected=False)
- bpy.ops.object.mode_set(mode = 'OBJECT')
+ bpy.ops.object.mode_set(mode='OBJECT')
else:
# grid
verts, faces = grid_gen(
- 2,
- 2,
- ob['tri_face'],
- ob['mesh_size_x'],
- ob['mesh_size_y'],
- ant_props,
- ob['water_plane'],
- ob['water_level']
- )
- wobj = create_mesh_object(context, verts, [], faces, new_name+"_plane")
+ 2,
+ 2,
+ ob['tri_face'],
+ ob['mesh_size_x'],
+ ob['mesh_size_y'],
+ ant_props,
+ ob['water_plane'],
+ ob['water_level'],
+ )
+ wobj = create_mesh_object(context, verts, [], faces, new_name + "_plane")
wobj.select_set(True)
@@ -310,41 +310,39 @@ class AntVgSlopeMap(bpy.types.Operator):
bl_options = {'REGISTER', 'UNDO'}
z_method: EnumProperty(
- name="Method:",
- default='SLOPE_Z',
- items=[
+ name="Method:",
+ default='SLOPE_Z',
+ items=[
('SLOPE_Z', "Z Slope", "Slope for planar mesh"),
('SLOPE_XYZ', "Sphere Slope", "Slope for spherical mesh")
- ])
+ ])
group_name: StringProperty(
- name="Vertex Group Name:",
- default="Slope",
- description="Name"
- )
+ name="Vertex Group Name:",
+ default="Slope",
+ description="Name"
+ )
select_flat: BoolProperty(
- name="Vert Select:",
- default=True,
- description="Select vertices on flat surface"
- )
+ name="Vert Select:",
+ default=True,
+ description="Select vertices on flat surface"
+ )
select_range: FloatProperty(
- name="Vert Select Range:",
- default=0.0,
- min=0.0,
- max=1.0,
- description="Increase to select more vertices on slopes"
- )
+ name="Vert Select Range:",
+ default=0.0,
+ min=0.0,
+ max=1.0,
+ description="Increase to select more vertices on slopes"
+ )
@classmethod
def poll(cls, context):
ob = context.object
return (ob and ob.type == 'MESH')
-
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
-
def execute(self, context):
message = "Popup Values: %d, %f, %s, %s" % \
(self.select_flat, self.select_range, self.group_name, self.z_method)
@@ -421,7 +419,7 @@ def draw_ant_main(self, context, generate=True):
split.prop(self, "remove_double", toggle=True)
box.prop(self, "ant_terrain_name")
- box.prop_search(self, "land_material", bpy.data, "materials")
+ box.prop_search(self, "land_material", bpy.data, "materials")
col = box.column(align=True)
col.prop(self, "subdivision_x")
@@ -450,11 +448,11 @@ def draw_ant_noise(self, context, generate=True):
col = box.column(align=True)
col.prop(self, "noise_offset_x")
col.prop(self, "noise_offset_y")
- if self.sphere_mesh == True or generate == False:
+ if self.sphere_mesh or generate == False:
col.prop(self, "noise_offset_z")
col.prop(self, "noise_size_x")
col.prop(self, "noise_size_y")
- if self.sphere_mesh == True or generate == False:
+ if self.sphere_mesh or generate == False:
col.prop(self, "noise_size_z")
col = box.column(align=True)
@@ -647,7 +645,7 @@ def draw_ant_displace(self, context, generate=True):
if not generate:
col = box.column(align=False)
- col.prop_search(self, "vert_group", bpy.context.object, "vertex_groups")
+ col.prop_search(self, "vert_group", bpy.context.object, "vertex_groups")
def draw_ant_water(self, context):
@@ -657,7 +655,7 @@ def draw_ant_water(self, context):
col.prop(self, "water_plane", toggle=True)
if self.water_plane:
col = box.column(align=True)
- col.prop_search(self, "water_material", bpy.data, "materials")
+ col.prop_search(self, "water_material", bpy.data, "materials")
col = box.column()
col.prop(self, "water_level")
@@ -744,8 +742,8 @@ from .utils import numexpr_available
def availableVertexGroupsOrNone(self, context):
- groups = [ ('None', 'None', 'None', 1) ]
- return groups + [(name, name, name, n+1) for n,name in enumerate(context.active_object.vertex_groups.keys())]
+ groups = [('None', 'None', 'None', 1)]
+ return groups + [(name, name, name, n + 1) for n, name in enumerate(context.active_object.vertex_groups.keys())]
class Eroder(bpy.types.Operator):
@@ -755,214 +753,190 @@ class Eroder(bpy.types.Operator):
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
Iterations: IntProperty(
- name="Iterations",
- description="Number of overall iterations",
- default=1,
- min=1,
- soft_max=100
- )
+ name="Iterations",
+ description="Number of overall iterations",
+ default=1,
+ min=1,
+ soft_max=100
+ )
IterRiver: IntProperty(
- name="River Iterations",
- description="Number of river iterations",
- default=30,
- min=1,
- soft_max=1000
- )
+ name="River Iterations",
+ description="Number of river iterations",
+ default=30,
+ min=1,
+ soft_max=1000
+ )
IterAva: IntProperty(
- name="Avalanche Iterations",
- description="Number of avalanche iterations",
- default=5,
- min=1,
- soft_max=10
- )
+ name="Avalanche Iterations",
+ description="Number of avalanche iterations",
+ default=5,
+ min=1,
+ soft_max=10
+ )
IterDiffuse: IntProperty(
- name="Diffuse Iterations",
- description="Number of diffuse iterations",
- default=5,
- min=1,
- soft_max=10
- )
+ name="Diffuse Iterations",
+ description="Number of diffuse iterations",
+ default=5,
+ min=1,
+ soft_max=10
+ )
Ef: FloatProperty(
- name="Rain on Plains",
- description="1 gives equal rain across the terrain, 0 rains more at the mountain tops",
- default=0.0,
- min=0,
- max=1
- )
+ name="Rain on Plains",
+ description="1 gives equal rain across the terrain, 0 rains more at the mountain tops",
+ default=0.0,
+ min=0,
+ max=1
+ )
Kd: FloatProperty(
- name="Kd",
- description="Thermal diffusion rate (1.0 is a fairly high rate)",
- default=0.1,
- min=0,
- soft_max=100
- )
+ name="Kd",
+ description="Thermal diffusion rate (1.0 is a fairly high rate)",
+ default=0.1,
+ min=0,
+ soft_max=100
+ )
Kt: FloatProperty(
- name="Kt",
- description="Maximum stable talus angle",
- default=radians(60),
- min=0,
- max=radians(90),
- subtype='ANGLE'
- )
+ name="Kt",
+ description="Maximum stable talus angle",
+ default=radians(60),
+ min=0,
+ max=radians(90),
+ subtype='ANGLE'
+ )
Kr: FloatProperty(
- name="Rain amount",
- description="Total Rain amount",
- default=.01,
- min=0,
- soft_max=1,
- precision=3
- )
+ name="Rain amount",
+ description="Total Rain amount",
+ default=.01,
+ min=0,
+ soft_max=1,
+ precision=3
+ )
Kv: FloatProperty(
- name="Rain variance",
- description="Rain variance (0 is constant, 1 is uniform)",
- default=0,
- min=0,
- max=1
- )
+ name="Rain variance",
+ description="Rain variance (0 is constant, 1 is uniform)",
+ default=0,
+ min=0,
+ max=1
+ )
userainmap: BoolProperty(
- name="Use rain map",
- description="Use active vertex group as a rain map",
- default=True
- )
+ name="Use rain map",
+ description="Use active vertex group as a rain map",
+ default=True
+ )
Ks: FloatProperty(
- name="Soil solubility",
- description="Soil solubility - how quickly water quickly reaches saturation point",
- default=0.5,
- min=0,
- soft_max=1
- )
+ name="Soil solubility",
+ description="Soil solubility - how quickly water quickly reaches saturation point",
+ default=0.5,
+ min=0,
+ soft_max=1
+ )
Kdep: FloatProperty(
- name="Deposition rate",
- description="Sediment deposition rate - how quickly silt is laid down once water stops flowing quickly",
- default=0.1,
- min=0,
- soft_max=1
- )
- Kz: FloatProperty(name="Fluvial Erosion Rate",
- description="Amount of sediment moved each main iteration - if 0, then rivers are formed but the mesh is not changed",
- default=0.3,
- min=0,
- soft_max=20
- )
+ name="Deposition rate",
+ description="Sediment deposition rate - how quickly silt is laid down once water stops flowing quickly",
+ default=0.1,
+ min=0,
+ soft_max=1
+ )
+ Kz: FloatProperty(
+ name="Fluvial Erosion Rate",
+ description="Amount of sediment moved each main iteration - if 0, then rivers are formed but the mesh is not changed",
+ default=0.3,
+ min=0,
+ soft_max=20)
Kc: FloatProperty(
- name="Carrying capacity",
- description="Base sediment carrying capacity",
- default=0.9,
- min=0,
- soft_max=1
- )
+ name="Carrying capacity",
+ description="Base sediment carrying capacity",
+ default=0.9,
+ min=0,
+ soft_max=1
+ )
Ka: FloatProperty(
- name="Slope dependence",
- description="Slope dependence of carrying capacity (not used)",
- default=1.0,
- min=0,
- soft_max=2
- )
+ name="Slope dependence",
+ description="Slope dependence of carrying capacity (not used)",
+ default=1.0,
+ min=0,
+ soft_max=2
+ )
Kev: FloatProperty(
- name="Evaporation",
- description="Evaporation Rate per grid square in % - causes sediment to be dropped closer to the hills",
- default=.5,
- min=0,
- soft_max=2
- )
+ name="Evaporation",
+ description="Evaporation Rate per grid square in % - causes sediment to be dropped closer to the hills",
+ default=.5,
+ min=0,
+ soft_max=2
+ )
numexpr: BoolProperty(
- name="Numexpr",
- description="Use numexpr module (if available)",
- default=True
- )
+ name="Numexpr",
+ description="Use numexpr module (if available)",
+ default=True
+ )
Pd: FloatProperty(
- name="Diffusion Amount",
- description="Diffusion probability",
- default=0.2,
- min=0,
- max=1
- )
+ name="Diffusion Amount",
+ description="Diffusion probability",
+ default=0.2,
+ min=0,
+ max=1
+ )
Pa: FloatProperty(
- name="Avalanche Amount",
- description="Avalanche amount",
- default=0.5,
- min=0,
- max=1
- )
+ name="Avalanche Amount",
+ description="Avalanche amount",
+ default=0.5,
+ min=0,
+ max=1
+ )
Pw: FloatProperty(
- name="River Amount",
- description="Water erosion probability",
- default=1,
- min=0,
- max=1
- )
+ name="River Amount",
+ description="Water erosion probability",
+ default=1,
+ min=0,
+ max=1
+ )
smooth: BoolProperty(
- name="Smooth",
- description="Set smooth shading",
- default=True
- )
+ name="Smooth",
+ description="Set smooth shading",
+ default=True
+ )
showiterstats: BoolProperty(
- name="Iteration Stats",
- description="Show iteraration statistics",
- default=False
- )
- showmeshstats: BoolProperty(name="Mesh Stats",
- description="Show mesh statistics",
- default=False
- )
+ name="Iteration Stats",
+ description="Show iteraration statistics",
+ default=False
+ )
+ showmeshstats: BoolProperty(
+ name="Mesh Stats",
+ description="Show mesh statistics",
+ default=False
+ )
stats = Stats()
- counts= {}
+ counts = {}
+ maps = {
+ 'rainmap': lambda g, r, c: g.rainmap[r, c],
+ 'scree': lambda g, r, c: g.avalanced[r, c],
+ 'avalanced': lambda g, r, c: -g.avalanced[r, c],
+ 'water': lambda g, r, c: g.water[r, c] / g.watermax,
+ 'scour': lambda g, r, c: g.scour[r, c] / max(g.scourmax, -g.scourmin),
+ 'deposit': lambda g, r, c: g.scour[r, c] / min(-g.scourmax, g.scourmin),
+ 'flowrate': lambda g, r, c: g.flowrate[r, c],
+ 'sediment': lambda g, r, c: g.sediment[r, c],
+ 'sedimentpct': lambda g, r, c: g.sedimentpct[r, c],
+ 'capacity': lambda g, r, c: g.capacity[r, c]
+ }
def execute(self, context):
ob = context.active_object
- me = ob.data
+ oldMesh = ob.data
self.stats.reset()
- try:
- vgActive = ob.vertex_groups.active.name
- except:
- vgActive = "capacity"
- print("ActiveGroup", vgActive)
- try:
- vg=ob.vertex_groups["rainmap"]
- except:
- vg=ob.vertex_groups.new(name="rainmap")
- try:
- vgscree=ob.vertex_groups["scree"]
- except:
- vgscree=ob.vertex_groups.new(name="scree")
- try:
- vgavalanced=ob.vertex_groups["avalanced"]
- except:
- vgavalanced=ob.vertex_groups.new(name="avalanced")
- try:
- vgw=ob.vertex_groups["water"]
- except:
- vgw=ob.vertex_groups.new(name="water")
- try:
- vgscour=ob.vertex_groups["scour"]
- except:
- vgscour=ob.vertex_groups.new(name="scour")
- try:
- vgdeposit=ob.vertex_groups["deposit"]
- except:
- vgdeposit=ob.vertex_groups.new(name="deposit")
- try:
- vgflowrate=ob.vertex_groups["flowrate"]
- except:
- vgflowrate=ob.vertex_groups.new(name="flowrate")
- try:
- vgsediment=ob.vertex_groups["sediment"]
- except:
- vgsediment=ob.vertex_groups.new(name="sediment")
- try:
- vgsedimentpct=ob.vertex_groups["sedimentpct"]
- except:
- vgsedimentpct=ob.vertex_groups.new(name="sedimentpct")
- try:
- vgcapacity=ob.vertex_groups["capacity"]
- except:
- vgcapacity=ob.vertex_groups.new(name="capacity")
-
- g = Grid.fromBlenderMesh(me, vg, self.Ef)
-
- me = bpy.data.meshes.new(me.name)
+ index_to_name = {}
+
+ for name in self.maps:
+ try:
+ ob.vertex_groups[name]
+ except:
+ ob.vertex_groups.new(name=name)
+ # Save a mapping from index to name, in case,
+ # the next iteration is different.
+ index_to_name[ob.vertex_groups[name].index] = name
+
+ g = Grid.fromBlenderMesh(oldMesh, ob.vertex_groups['rainmap'], self.Ef)
self.counts['diffuse'] = 0
self.counts['avalanche'] = 0
@@ -970,80 +944,60 @@ class Eroder(bpy.types.Operator):
for i in range(self.Iterations):
if self.IterRiver > 0:
for i in range(self.IterRiver):
- g.rivergeneration(self.Kr, self.Kv, self.userainmap, self.Kc, self.Ks, self.Kdep, self.Ka, self.Kev/100, 0,0,0,0, self.numexpr)
+ g.rivergeneration(
+ self.Kr,
+ self.Kv,
+ self.userainmap,
+ self.Kc,
+ self.Ks,
+ self.Kdep,
+ self.Ka,
+ self.Kev / 100,
+ 0,
+ 0,
+ 0,
+ 0,
+ self.numexpr,
+ )
if self.Kd > 0.0:
for k in range(self.IterDiffuse):
g.diffuse(self.Kd / 5, self.IterDiffuse, self.numexpr)
- self.counts['diffuse']+=1
+ self.counts['diffuse'] += 1
if self.Kt < radians(90) and self.Pa > 0:
for k in range(self.IterAva):
# since dx and dy are scaled to 1, tan(Kt) is the height for a given angle
g.avalanche(tan(self.Kt), self.IterAva, self.Pa, self.numexpr)
- self.counts['avalanche']+=1
+ self.counts['avalanche'] += 1
if self.Kz > 0:
- g.fluvial_erosion(self.Kr, self.Kv, self.userainmap, self.Kc, self.Ks, self.Kz*50, self.Ka, 0,0,0,0, self.numexpr)
- self.counts['water']+=1
-
- g.toBlenderMesh(me)
- ob.data = me
-
- if vg:
- for row in range(g.rainmap.shape[0]):
- for col in range(g.rainmap.shape[1]):
- i = row * g.rainmap.shape[1] + col
- vg.add([i],g.rainmap[row,col],'ADD')
- if vgscree:
- for row in range(g.rainmap.shape[0]):
- for col in range(g.rainmap.shape[1]):
- i = row * g.rainmap.shape[1] + col
- vgscree.add([i],g.avalanced[row,col],'ADD')
- if vgavalanced:
- for row in range(g.rainmap.shape[0]):
- for col in range(g.rainmap.shape[1]):
- i = row * g.rainmap.shape[1] + col
- vgavalanced.add([i],-g.avalanced[row,col],'ADD')
- if vgw:
- for row in range(g.rainmap.shape[0]):
- for col in range(g.rainmap.shape[1]):
- i = row * g.rainmap.shape[1] + col
- vgw.add([i],g.water[row,col]/g.watermax,'ADD')
- if vgscour:
- for row in range(g.rainmap.shape[0]):
- for col in range(g.rainmap.shape[1]):
- i = row * g.rainmap.shape[1] + col
- vgscour.add([i],g.scour[row,col]/max(g.scourmax, -g.scourmin),'ADD')
- if vgdeposit:
- for row in range(g.rainmap.shape[0]):
- for col in range(g.rainmap.shape[1]):
- i = row * g.rainmap.shape[1] + col
- vgdeposit.add([i],g.scour[row,col]/min(-g.scourmax, g.scourmin),'ADD')
- if vgflowrate:
- for row in range(g.rainmap.shape[0]):
- for col in range(g.rainmap.shape[1]):
- i = row * g.rainmap.shape[1] + col
- vgflowrate.add([i],g.flowrate[row,col],'ADD')
- if vgsediment:
- for row in range(g.rainmap.shape[0]):
- for col in range(g.rainmap.shape[1]):
- i = row * g.rainmap.shape[1] + col
- vgsediment.add([i],g.sediment[row,col],'ADD')
- if vgsedimentpct:
- for row in range(g.rainmap.shape[0]):
- for col in range(g.rainmap.shape[1]):
- i = row * g.rainmap.shape[1] + col
- vgsedimentpct.add([i],g.sedimentpct[row,col],'ADD')
- if vgcapacity:
- for row in range(g.rainmap.shape[0]):
- for col in range(g.rainmap.shape[1]):
- i = row * g.rainmap.shape[1] + col
- vgcapacity.add([i],g.capacity[row,col],'ADD')
- try:
- vg = ob.vertex_groups["vgActive"]
- except:
- vg = vgcapacity
- ob.vertex_groups.active = vg
+ g.fluvial_erosion(self.Kr, self.Kv, self.userainmap, self.Kc, self.Ks,
+ self.Kz * 50, self.Ka, 0, 0, 0, 0, self.numexpr)
+ self.counts['water'] += 1
+
+ newMesh = bpy.data.meshes.new(oldMesh.name)
+ g.toBlenderMesh(newMesh)
+
+ # This empties ob.vertex_groups.
+ ob.data = newMesh
+
+ # Copy vertex groups from the old mesh.
+ for name in self.maps:
+ ob.vertex_groups.new(name=name)
+ for vert in oldMesh.vertices:
+ for group in vert.groups:
+ name = index_to_name[group.group]
+ if name:
+ ob.vertex_groups[name].add([vert.index], group.weight, 'REPLACE')
+
+ # Add the new data.
+ for row in range(g.rainmap.shape[0]):
+ for col in range(g.rainmap.shape[1]):
+ i = row * g.rainmap.shape[1] + col
+ for name, fn in self.maps.items():
+ ob.vertex_groups[name].add([i], fn(g, row, col), 'ADD')
+
+ ob.vertex_groups.active = ob.vertex_groups['capacity']
if self.smooth:
bpy.ops.object.shade_smooth()
@@ -1054,11 +1008,10 @@ class Eroder(bpy.types.Operator):
return {'FINISHED'}
-
- def draw(self,context):
+ def draw(self, context):
layout = self.layout
- layout.operator('screen.repeat_last', text="Repeat", icon='FILE_REFRESH' )
+ layout.operator('screen.repeat_last', text="Repeat", icon='FILE_REFRESH')
layout.prop(self, 'Iterations')
@@ -1089,4 +1042,4 @@ class Eroder(bpy.types.Operator):
col.prop(self, 'Ef')
- layout.prop(self,'smooth')
+ layout.prop(self, 'smooth')
diff --git a/ant_landscape/ant_noise.py b/ant_landscape/ant_noise.py
index 7eec29b6..9385d4b4 100644
--- a/ant_landscape/ant_noise.py
+++ b/ant_landscape/ant_noise.py
@@ -5,23 +5,23 @@
import bpy
from mathutils.noise import (
- seed_set,
- noise,
- turbulence,
- turbulence_vector,
- fractal,
- hybrid_multi_fractal,
- multi_fractal,
- ridged_multi_fractal,
- hetero_terrain,
- random_unit_vector,
- variable_lacunarity,
- voronoi,
- )
+ seed_set,
+ noise,
+ turbulence,
+ turbulence_vector,
+ fractal,
+ hybrid_multi_fractal,
+ multi_fractal,
+ ridged_multi_fractal,
+ hetero_terrain,
+ random_unit_vector,
+ variable_lacunarity,
+ voronoi,
+)
from math import (
- floor, sqrt,
- sin, cos, pi,
- )
+ floor, sqrt,
+ sin, cos, pi,
+)
noise_basis_default = "BLENDER"
noise_basis = [
@@ -39,6 +39,8 @@ noise_basis = [
# ------------------------------------------------------------
# Height scale:
+
+
def Height_Scale(input, iscale, offset, invert):
if invert != 0:
return (1.0 - input) * iscale + offset
@@ -176,14 +178,15 @@ def vlnTurbMode(coords, distort, basis, vlbasis, hardnoise):
def vl_noise_turbulence(coords, distort, depth, basis, vlbasis, hardnoise, amp, freq):
x, y, z = coords
value = vlnTurbMode(coords, distort, basis, vlbasis, hardnoise)
- i=0
+ i = 0
for i in range(depth):
- i+=1
- value += vlnTurbMode((x * (freq * i), y * (freq * i), z * (freq * i)), distort, basis, vlbasis, hardnoise) * (amp * 0.5 / i)
+ i += 1
+ value += vlnTurbMode((x * (freq * i), y * (freq * i), z * (freq * i)),
+ distort, basis, vlbasis, hardnoise) * (amp * 0.5 / i)
return value
-## duo_multiFractal:
+# duo_multiFractal:
def double_multiFractal(coords, H, lacunarity, octaves, offset, gain, basis, vlbasis):
x, y, z = coords
n1 = multi_fractal((x * 1.5 + 1, y * 1.5 + 1, z * 1.5 + 1), 1.0, 1.0, 1.0, noise_basis=basis) * (offset * 0.5)
@@ -191,27 +194,27 @@ def double_multiFractal(coords, H, lacunarity, octaves, offset, gain, basis, vlb
return (n1 * n1 + n2 * n2) * 0.5
-## distorted_heteroTerrain:
+# distorted_heteroTerrain:
def distorted_heteroTerrain(coords, H, lacunarity, octaves, offset, distort, basis, vlbasis):
x, y, z = coords
h1 = (hetero_terrain((x, y, z), 1.0, 2.0, 1.0, 1.0, noise_basis=basis) * 0.5)
- d = h1 * distort
+ d = h1 * distort
h2 = (hetero_terrain((x + d, y + d, z + d), H, lacunarity, octaves, offset, noise_basis=vlbasis) * 0.25)
return (h1 * h1 + h2 * h2) * 0.5
-## SlickRock:
+# SlickRock:
def slick_rock(coords, H, lacunarity, octaves, offset, gain, distort, basis, vlbasis):
x, y, z = coords
- n = multi_fractal((x,y,z), 1.0, 2.0, 2.0, noise_basis=basis) * distort * 0.25
+ n = multi_fractal((x, y, z), 1.0, 2.0, 2.0, noise_basis=basis) * distort * 0.25
r = ridged_multi_fractal((x + n, y + n, z + n), H, lacunarity, octaves, offset + 0.1, gain * 2, noise_basis=vlbasis)
return (n + (n * r)) * 0.5
-## vlhTerrain
+# vlhTerrain
def vl_hTerrain(coords, H, lacunarity, octaves, offset, basis, vlbasis, distort):
x, y, z = coords
- ht = hetero_terrain((x, y, z), H, lacunarity, octaves, offset, noise_basis=basis ) * 0.25
+ ht = hetero_terrain((x, y, z), H, lacunarity, octaves, offset, noise_basis=basis) * 0.25
vl = ht * variable_lacunarity((x, y, z), distort, noise_type1=basis, noise_type2=vlbasis) * 0.5 + 0.5
return vl * ht
@@ -219,13 +222,14 @@ def vl_hTerrain(coords, H, lacunarity, octaves, offset, basis, vlbasis, distort)
# another turbulence
def ant_turbulence(coords, depth, hardnoise, nbasis, amp, freq, distortion):
x, y, z = coords
- t = turbulence_vector((x/2, y/2, z/2), depth, 0, noise_basis=nbasis, amplitude_scale=amp, frequency_scale=freq) * 0.5 * distortion
+ t = turbulence_vector((x / 2, y / 2, z / 2), depth, 0, noise_basis=nbasis,
+ amplitude_scale=amp, frequency_scale=freq) * 0.5 * distortion
return turbulence((t[0], t[1], t[2]), 2, hardnoise, noise_basis="VORONOI_F1") * 0.5 + 0.5
# rocks noise
def rocks_noise(coords, depth, hardnoise, nbasis, distortion):
- x,y,z = coords
+ x, y, z = coords
p = turbulence((x, y, z), 4, 0, noise_basis='BLENDER') * 0.125 * distortion
xx, yy, zz = x, y, z
a = turbulence((xx + p, yy + p, zz), 2, 0, noise_basis='VORONOI_F2F1')
@@ -269,16 +273,18 @@ def planet_noise(coords, oct=6, hard=0, noisebasis='PERLIN_ORIGINAL', nabla=0.00
return (zdy - ydz), (zdx - xdz), (ydx - xdy)
-###----------------------------------------------------------------------
+# ----------------------------------------------------------------------
# v.1.04 Effect functions:
def maximum(a, b):
- if (a > b): b = a
+ if (a > b):
+ b = a
return b
def minimum(a, b):
- if (a < b): b = a
+ if (a < b):
+ b = a
return b
@@ -286,38 +292,38 @@ def Mix_Modes(a, b, mixfactor, mode):
mode = int(mode)
a = a * (1.0 - mixfactor)
b = b * (1.0 + mixfactor)
- #1 mix
+ # 1 mix
if mode == 0:
return (a * (1.0 - 0.5) + b * 0.5)
- #2 add
+ # 2 add
elif mode == 1:
return (a + b)
- #3 sub.
+ # 3 sub.
elif mode == 2:
return (a - b)
- #4 mult.
+ # 4 mult.
elif mode == 3:
return (a * b)
- #5 abs diff.
+ # 5 abs diff.
elif mode == 4:
return (abs(a - b))
- #6 screen
+ # 6 screen
elif mode == 5:
return 1.0 - ((1.0 - a) * (1.0 - b) / 1.0)
- #7 addmodulo
+ # 7 addmodulo
elif mode == 6:
return (a + b) % 1.0
- #8 min.
+ # 8 min.
elif mode == 7:
return minimum(a, b)
- #9 max.
+ # 9 max.
elif mode == 8:
return maximum(a, b)
else:
return 0
-Bias_Types = [sin_bias, cos_bias, tri_bias, saw_bias, no_bias]
+Bias_Types = [sin_bias, cos_bias, tri_bias, saw_bias, no_bias]
Sharp_Types = [soft, sharp, sharper]
@@ -337,46 +343,47 @@ def Effect_Basis_Function(coords, type, bias):
iscale = 1.0
offset = 0.0
- ## gradient:
+ # gradient:
if type == 1:
effect = offset + iscale * (Bias_Types[bias](x + y))
- ## waves / bumps:
+ # waves / bumps:
elif type == 2:
effect = offset + iscale * 0.5 * (Bias_Types[bias](x * pi) + Bias_Types[bias](y * pi))
- ## zigzag:
+ # zigzag:
elif type == 3:
effect = offset + iscale * Bias_Types[bias](offset + iscale * sin(x * pi + sin(y * pi)))
- ## wavy:
+ # wavy:
elif type == 4:
effect = offset + iscale * (Bias_Types[bias](cos(x) + sin(y) + cos(x * 2 + y * 2) - sin(-x * 4 + y * 4)))
- ## sine bump:
+ # sine bump:
elif type == 5:
- effect = offset + iscale * 1 - Bias_Types[bias]((sin(x * pi) + sin(y * pi)))
- ## dots:
+ effect = offset + iscale * 1 - Bias_Types[bias]((sin(x * pi) + sin(y * pi)))
+ # dots:
elif type == 6:
effect = offset + iscale * (Bias_Types[bias](x * pi * 2) * Bias_Types[bias](y * pi * 2)) - 0.5
- ## rings:
+ # rings:
elif type == 7:
- effect = offset + iscale * (Bias_Types[bias ](1.0 - (x * x + y * y)))
- ## spiral:
+ effect = offset + iscale * (Bias_Types[bias](1.0 - (x * x + y * y)))
+ # spiral:
elif type == 8:
- effect = offset + iscale * Bias_Types[bias]( (x * sin(x * x + y * y) + y * cos(x * x + y * y)) / (x**2 + y**2 + 0.5)) * 2
- ## square / piramide:
+ effect = offset + iscale * \
+ Bias_Types[bias]((x * sin(x * x + y * y) + y * cos(x * x + y * y)) / (x**2 + y**2 + 0.5)) * 2
+ # square / piramide:
elif type == 9:
effect = offset + iscale * Bias_Types[bias](1.0 - sqrt((x * x)**10 + (y * y)**10)**0.1)
- ## blocks:
+ # blocks:
elif type == 10:
- effect = (0.5 - max(Bias_Types[bias](x * pi) , Bias_Types[bias](y * pi)))
+ effect = (0.5 - max(Bias_Types[bias](x * pi), Bias_Types[bias](y * pi)))
if effect > 0.0:
effect = 1.0
effect = offset + iscale * effect
- ## grid:
+ # grid:
elif type == 11:
effect = (0.025 - min(Bias_Types[bias](x * pi), Bias_Types[bias](y * pi)))
if effect > 0.0:
effect = 1.0
effect = offset + iscale * effect
- ## tech:
+ # tech:
elif type == 12:
a = max(Bias_Types[bias](x * pi), Bias_Types[bias](y * pi))
b = max(Bias_Types[bias](x * pi * 2 + 2), Bias_Types[bias](y * pi * 2 + 2))
@@ -384,51 +391,51 @@ def Effect_Basis_Function(coords, type, bias):
if effect > 0.5:
effect = 1.0
effect = offset + iscale * effect
- ## crackle:
+ # crackle:
elif type == 13:
t = turbulence((x, y, 0), 6, 0, noise_basis="BLENDER") * 0.25
effect = variable_lacunarity((x, y, t), 0.25, noise_type2='VORONOI_CRACKLE')
if effect > 0.5:
effect = 0.5
effect = offset + iscale * effect
- ## sparse cracks noise:
+ # sparse cracks noise:
elif type == 14:
effect = 2.5 * abs(noise((x, y, 0), noise_basis="PERLIN_ORIGINAL")) - 0.1
if effect > 0.25:
effect = 0.25
effect = offset + iscale * (effect * 2.5)
- ## shattered rock noise:
+ # shattered rock noise:
elif type == 15:
effect = 0.5 + noise((x, y, 0), noise_basis="VORONOI_F2F1")
if effect > 0.75:
effect = 0.75
effect = offset + iscale * effect
- ## lunar noise:
+ # lunar noise:
elif type == 16:
effect = 0.25 + 1.5 * voronoi((x, y, 0), distance_metric='DISTANCE_SQUARED')[0][0]
if effect > 0.5:
effect = 0.5
effect = offset + iscale * effect * 2
- ## cosine noise:
+ # cosine noise:
elif type == 17:
effect = cos(5 * noise((x, y, 0), noise_basis="BLENDER"))
effect = offset + iscale * (effect * 0.5)
- ## spikey noise:
+ # spikey noise:
elif type == 18:
n = 0.5 + 0.5 * turbulence((x * 5, y * 5, 0), 8, 0, noise_basis="BLENDER")
effect = ((n * n)**5)
effect = offset + iscale * effect
- ## stone noise:
+ # stone noise:
elif type == 19:
effect = offset + iscale * (noise((x * 2, y * 2, 0), noise_basis="BLENDER") * 1.5 - 0.75)
- ## Flat Turb:
+ # Flat Turb:
elif type == 20:
t = turbulence((x, y, 0), 6, 0, noise_basis="BLENDER")
effect = t * 2.0
if effect > 0.25:
effect = 0.25
effect = offset + iscale * effect
- ## Flat Voronoi:
+ # Flat Voronoi:
elif type == 21:
t = 1 - voronoi((x, y, 0), distance_metric='DISTANCE_SQUARED')[0][0]
effect = t * 2 - 1.5
@@ -448,19 +455,19 @@ def Effect_Basis_Function(coords, type, bias):
def Effect_Function(coords, type, bias, turb, depth, frequency, amplitude):
x, y, z = coords
- ## turbulence:
+ # turbulence:
if turb > 0.0:
- t = turb * ( 0.5 + 0.5 * turbulence(coords, 6, 0, noise_basis="BLENDER"))
+ t = turb * (0.5 + 0.5 * turbulence(coords, 6, 0, noise_basis="BLENDER"))
x = x + t
y = y + t
z = z + t
result = Effect_Basis_Function((x, y, z), type, bias) * amplitude
- ## fractalize:
+ # fractalize:
if depth != 0:
- i=0
+ i = 0
for i in range(depth):
- i+=1
+ i += 1
x *= frequency
y *= frequency
result += Effect_Basis_Function((x, y, z), type, bias) * amplitude / i
@@ -578,20 +585,21 @@ def noise_gen(coords, props):
value = fractal(ncoords, dimension, lacunarity, depth, noise_basis=nbasis)
elif ntype in [5, 'turbulence_vector']:
- value = turbulence_vector(ncoords, depth, hardnoise, noise_basis=nbasis, amplitude_scale=amp, frequency_scale=freq)[0]
+ value = turbulence_vector(ncoords, depth, hardnoise, noise_basis=nbasis,
+ amplitude_scale=amp, frequency_scale=freq)[0]
elif ntype in [6, 'variable_lacunarity']:
value = variable_lacunarity(ncoords, distortion, noise_type1=nbasis, noise_type2=vlbasis)
elif ntype in [7, 'marble_noise']:
value = marble_noise(
- (ncoords[0] - origin_x + x_offset),
- (ncoords[1] - origin_y + y_offset),
- (ncoords[2] - origin_z + z_offset),
- (origin[0] + x_offset, origin[1] + y_offset, origin[2] + z_offset), nsize,
- marbleshape, marblebias, marblesharpnes,
- distortion, depth, hardnoise, nbasis, amp, freq
- )
+ (ncoords[0] - origin_x + x_offset),
+ (ncoords[1] - origin_y + y_offset),
+ (ncoords[2] - origin_z + z_offset),
+ (origin[0] + x_offset, origin[1] + y_offset, origin[2] + z_offset), nsize,
+ marbleshape, marblebias, marblesharpnes,
+ distortion, depth, hardnoise, nbasis, amp, freq
+ )
elif ntype in [8, 'shattered_hterrain']:
value = shattered_hterrain(ncoords, dimension, lacunarity, depth, offset, distortion, nbasis)
@@ -617,7 +625,7 @@ def noise_gen(coords, props):
value = rocks_noise(ncoords, depth, hardnoise, nbasis, distortion)
elif ntype in [16, 'slick_rock']:
- value = slick_rock(ncoords,dimension, lacunarity, depth, offset, gain, distortion, nbasis, vlbasis)
+ value = slick_rock(ncoords, dimension, lacunarity, depth, offset, gain, distortion, nbasis, vlbasis)
elif ntype in [17, 'planet_noise']:
value = planet_noise(ncoords, depth, hardnoise, nbasis)[2] * 0.5 + 0.5
@@ -632,7 +640,7 @@ def noise_gen(coords, props):
# Effect mix
val = value
- if fx_type in [0,"0"]:
+ if fx_type in [0, "0"]:
fx_mixfactor = -1.0
fxval = val
else:
@@ -649,14 +657,15 @@ def noise_gen(coords, props):
if not sphere:
if falloff:
ratio_x, ratio_y = abs(x) * 2 / meshsize_x, abs(y) * 2 / meshsize_y
- fallofftypes = [0,
- sqrt(ratio_y**falloffsize_y),
- sqrt(ratio_x**falloffsize_x),
- sqrt(ratio_x**falloffsize_x + ratio_y**falloffsize_y)
- ]
+ fallofftypes = [
+ 0,
+ sqrt(ratio_y**falloffsize_y),
+ sqrt(ratio_x**falloffsize_x),
+ sqrt(ratio_x**falloffsize_x + ratio_y**falloffsize_y)
+ ]
dist = fallofftypes[falloff]
value -= edge_level
- if(dist < 1.0):
+ if dist < 1.0:
dist = (dist * dist * (3 - 2 * dist))
value = (value - value * dist) + edge_level
else:
@@ -682,11 +691,11 @@ def noise_gen(coords, props):
elif stratatype in [4, "4"]:
strata = strata / height
- value = int( value * strata ) * 1.0 / strata
+ value = int(value * strata) * 1.0 / strata
elif stratatype in [5, "5"]:
strata = strata / height
- steps = (int( value * strata ) * 1.0 / strata)
+ steps = (int(value * strata) * 1.0 / strata)
value = (value * (1.0 - 0.5) + steps * 0.5)
# Clamp height min max
diff --git a/ant_landscape/eroder.py b/ant_landscape/eroder.py
index 558d2edb..ae1326df 100644
--- a/ant_landscape/eroder.py
+++ b/ant_landscape/eroder.py
@@ -43,7 +43,6 @@ class Grid:
self.sedmax = 1.0
self.scourmin = 1.0
-
def init_water_and_sediment(self):
if self.water is None:
self.water = np.zeros(self.center.shape, dtype=np.single)
@@ -60,39 +59,34 @@ class Grid:
if self.avalanced is None:
self.avalanced = np.zeros(self.center.shape, dtype=np.single)
-
def __str__(self):
return ''.join(self.__str_iter__(fmt="%.3f"))
-
def __str_iter__(self, fmt):
for row in self.center[::]:
- values=[]
+ values = []
for v in row:
- values.append(fmt%v)
- yield ' '.join(values) + '\n'
-
+ values.append(fmt % v)
+ yield ' '.join(values) + '\n'
@staticmethod
def fromFile(filename):
if filename == '-':
filename = sys.stdin
- g=Grid()
- g.center=np.loadtxt(filename,np.single)
+ g = Grid()
+ g.center = np.loadtxt(filename, np.single)
return g
-
def toFile(self, filename, fmt="%.3f"):
- if filename == '-' :
+ if filename == '-':
filename = sys.stdout.fileno()
- with open(filename,"w") as f:
+ with open(filename, "w") as f:
for line in self.__str_iter__(fmt):
f.write(line)
-
- def raw(self,format="%.3f"):
- fstr=format+" "+ format+" "+ format+" "
- a=self.center / self.zscale
+ def raw(self, format="%.3f"):
+ fstr = format + " " + format + " " + format + " "
+ a = self.center / self.zscale
minx = 0.0 if self.minx is None else self.minx
miny = 0.0 if self.miny is None else self.miny
maxx = 1.0 if self.maxx is None else self.maxx
@@ -105,21 +99,19 @@ class Grid:
for col in range(a.shape[1] - 1):
col0 = minx + col * dx
col1 = col0 + dx
- yield (fstr%(row0 ,col0 ,a[row ][col ])+
- fstr%(row0 ,col1 ,a[row ][col+1])+
- fstr%(row1 ,col0 ,a[row+1][col ])+"\n")
- yield (fstr%(row0 ,col1 ,a[row ][col+1])+
- fstr%(row1 ,col0 ,a[row+1][col ])+
- fstr%(row1 ,col1 ,a[row+1][col+1])+"\n")
-
+ yield (fstr % (row0, col0, a[row][col]) +
+ fstr % (row0, col1, a[row][col + 1]) +
+ fstr % (row1, col0, a[row + 1][col]) + "\n")
+ yield (fstr % (row0, col1, a[row][col + 1]) +
+ fstr % (row1, col0, a[row + 1][col]) +
+ fstr % (row1, col1, a[row + 1][col + 1]) + "\n")
def toRaw(self, filename, infomap=None):
- with open(filename if type(filename) == str else sys.stdout.fileno() , "w") as f:
+ with open(filename if type(filename) == str else sys.stdout.fileno(), "w") as f:
f.writelines(self.raw())
if infomap:
- with open(os.path.splitext(filename)[0]+".inf" if type(filename) == str else sys.stdout.fileno() , "w") as f:
- f.writelines("\n".join("%-15s: %s"%t for t in sorted(infomap.items())))
-
+ with open(os.path.splitext(filename)[0] + ".inf" if type(filename) == str else sys.stdout.fileno(), "w") as f:
+ f.writelines("\n".join("%-15s: %s" % t for t in sorted(infomap.items())))
@staticmethod
def fromRaw(filename):
@@ -128,15 +120,14 @@ class Grid:
"""
g = Grid.fromFile(filename)
# we assume tris and an axis aligned grid
- g.center = np.reshape(g.center,(-1,3))
+ g.center = np.reshape(g.center, (-1, 3))
g._sort()
return g
-
def _sort(self, expfact):
# keep unique vertices only by creating a set and sort first on x then on y coordinate
# using rather slow python sort but couldn't wrap my head around np.lexsort
- verts = sorted(list({ tuple(t) for t in self.center[::] }))
+ verts = sorted(list({tuple(t) for t in self.center[::]}))
x = set(c[0] for c in verts)
y = set(c[1] for c in verts)
nx = len(x)
@@ -145,41 +136,40 @@ class Grid:
self.maxx = max(x)
self.miny = min(y)
self.maxy = max(y)
- xscale = (self.maxx-self.minx)/(nx-1)
- yscale = (self.maxy-self.miny)/(ny-1)
+ xscale = (self.maxx - self.minx) / (nx - 1)
+ yscale = (self.maxy - self.miny) / (ny - 1)
# note: a purely flat plane cannot be scaled
- if (yscale != 0.0) and (abs(xscale/yscale) - 1.0 > 1e-3):
- raise ValueError("Mesh spacing not square %d x %d %.4f x %4.f"%(nx,ny,xscale,yscale))
+ if (yscale != 0.0) and (abs(xscale / yscale) - 1.0 > 1e-3):
+ raise ValueError("Mesh spacing not square %d x %d %.4f x %4.f" % (nx, ny, xscale, yscale))
self.zscale = 1.0
- if abs(yscale) > 1e-6 :
- self.zscale = 1.0/yscale
+ if abs(yscale) > 1e-6:
+ self.zscale = 1.0 / yscale
# keep just the z-values and null any offset
- # we might catch a reshape error that will occur if nx*ny != # of vertices (if we are not dealing with a heightfield but with a mesh with duplicate x,y coords, like an axis aligned cube
- self.center = np.array([c[2] for c in verts],dtype=np.single).reshape(nx,ny)
- self.center = (self.center-np.amin(self.center))*self.zscale
+ # we might catch a reshape error that will occur if nx*ny != # of vertices
+ # (if we are not dealing with a heightfield but with a mesh with duplicate
+ # x,y coords, like an axis aligned cube
+ self.center = np.array([c[2] for c in verts], dtype=np.single).reshape(nx, ny)
+ self.center = (self.center - np.amin(self.center)) * self.zscale
if self.rainmap is not None:
rmscale = np.max(self.center)
- self.rainmap = expfact + (1-expfact)*(self.center/rmscale)
-
+ self.rainmap = expfact + (1 - expfact) * (self.center / rmscale)
@staticmethod
def fromBlenderMesh(me, vg, expfact):
g = Grid()
- g.center = np.asarray(list(tuple(v.co) for v in me.vertices), dtype=np.single )
+ g.center = np.asarray(list(tuple(v.co) for v in me.vertices), dtype=np.single)
g.rainmap = None
if vg is not None:
for v in me.vertices:
- vg.add([v.index],0.0,'ADD')
- g.rainmap=np.asarray(list( (v.co[0], v.co[1], vg.weight(v.index)) for v in me.vertices), dtype=np.single )
+ vg.add([v.index], 0.0, 'ADD')
+ g.rainmap = np.asarray(list((v.co[0], v.co[1], vg.weight(v.index)) for v in me.vertices), dtype=np.single)
g._sort(expfact)
return g
-
def setrainmap(self, rainmap):
self.rainmap = rainmap
-
def _verts(self, surface):
a = surface / self.zscale
minx = 0.0 if self.minx is None else self.minx
@@ -192,90 +182,81 @@ class Grid:
row0 = miny + row * dy
for col in range(a.shape[1]):
col0 = minx + col * dx
- yield (row0 ,col0 ,a[row ][col ])
-
+ yield (row0, col0, a[row][col])
def _faces(self):
nrow, ncol = self.center.shape
- for row in range(nrow-1):
- for col in range(ncol-1):
- vi = row * ncol + col
- yield (vi, vi+ncol, vi+1)
- yield (vi+1, vi+ncol, vi+ncol+1)
-
+ for row in range(nrow - 1):
+ for col in range(ncol - 1):
+ vi = row * ncol + col
+ yield (vi, vi + ncol, vi + 1)
+ yield (vi + 1, vi + ncol, vi + ncol + 1)
def toBlenderMesh(self, me):
# pass me as argument so that we don't need to import bpy and create a dependency
- # the docs state that from_pydata takes iterators as arguments but it will fail with generators because it does len(arg)
- me.from_pydata(list(self._verts(self.center)),[],list(self._faces()))
-
+ # the docs state that from_pydata takes iterators as arguments but it will
+ # fail with generators because it does len(arg)
+ me.from_pydata(list(self._verts(self.center)), [], list(self._faces()))
def toWaterMesh(self, me):
# pass me as argument so that we don't need to import bpy and create a dependency
- # the docs state that from_pydata takes iterators as arguments but it will fail with generators because it does len(arg)
- me.from_pydata(list(self._verts(self.water)),[],list(self._faces()))
-
+ # the docs state that from_pydata takes iterators as arguments but it will
+ # fail with generators because it does len(arg)
+ me.from_pydata(list(self._verts(self.water)), [], list(self._faces()))
def peak(self, value=1):
- nx,ny = self.center.shape
- self.center[int(nx/2),int(ny/2)] += value
-
+ nx, ny = self.center.shape
+ self.center[int(nx / 2), int(ny / 2)] += value
def shelf(self, value=1):
- nx,ny = self.center.shape
- self.center[:nx/2] += value
-
+ nx, ny = self.center.shape
+ self.center[:nx / 2] += value
def mesa(self, value=1):
- nx,ny = self.center.shape
- self.center[nx/4:3*nx/4,ny/4:3*ny/4] += value
-
+ nx, ny = self.center.shape
+ self.center[nx / 4:3 * nx / 4, ny / 4:3 * ny / 4] += value
def random(self, value=1):
- self.center += np.random.random_sample(self.center.shape)*value
-
+ self.center += np.random.random_sample(self.center.shape) * value
def neighborgrid(self):
- self.up = np.roll(self.center,-1,0)
- self.down = np.roll(self.center,1,0)
- self.left = np.roll(self.center,-1,1)
- self.right = np.roll(self.center,1,1)
-
+ self.up = np.roll(self.center, -1, 0)
+ self.down = np.roll(self.center, 1, 0)
+ self.left = np.roll(self.center, -1, 1)
+ self.right = np.roll(self.center, 1, 1)
def zeroedge(self, quantity=None):
c = self.center if quantity is None else quantity
- c[0,:] = 0
- c[-1,:] = 0
- c[:,0] = 0
- c[:,-1] = 0
-
+ c[0, :] = 0
+ c[-1, :] = 0
+ c[:, 0] = 0
+ c[:, -1] = 0
def diffuse(self, Kd, IterDiffuse, numexpr):
self.zeroedge()
- c = self.center[1:-1,1:-1]
- up = self.center[ :-2,1:-1]
- down = self.center[2: ,1:-1]
+ c = self.center[1:-1, 1:-1]
+ up = self.center[:-2, 1:-1]
+ down = self.center[2:, 1:-1]
left = self.center[1:-1, :-2]
- right = self.center[1:-1,2: ]
+ right = self.center[1:-1, 2:]
if(numexpr and numexpr_available):
- self.center[1:-1,1:-1] = ne.evaluate('c + Kd * (up + down + left + right - 4.0 * c)')
+ self.center[1:-1, 1:-1] = ne.evaluate('c + Kd * (up + down + left + right - 4.0 * c)')
else:
- self.center[1:-1,1:-1] = c + (Kd/IterDiffuse) * (up + down + left + right - 4.0 * c)
+ self.center[1:-1, 1:-1] = c + (Kd / IterDiffuse) * (up + down + left + right - 4.0 * c)
self.maxrss = max(getmemsize(), self.maxrss)
return self.center
-
def avalanche(self, delta, iterava, prob, numexpr):
self.zeroedge()
- c = self.center[1:-1,1:-1]
- up = self.center[ :-2,1:-1]
- down = self.center[2: ,1:-1]
- left = self.center[1:-1, :-2]
- right = self.center[1:-1,2: ]
+ c = self.center[1:-1, 1:-1]
+ up = self.center[:-2, 1:-1]
+ down = self.center[2:, 1:-1]
+ left = self.center[1:-1, :-2]
+ right = self.center[1:-1, 2:]
where = np.where
if(numexpr and numexpr_available):
- self.center[1:-1,1:-1] = ne.evaluate('c + where((up -c) > delta ,(up -c -delta)/2, 0) \
+ self.center[1:-1, 1:-1] = ne.evaluate('c + where((up -c) > delta ,(up -c -delta)/2, 0) \
+ where((down -c) > delta ,(down -c -delta)/2, 0) \
+ where((left -c) > delta ,(left -c -delta)/2, 0) \
+ where((right-c) > delta ,(right-c -delta)/2, 0) \
@@ -286,38 +267,36 @@ class Grid:
else:
sa = (
# incoming
- where((up -c) > delta ,(up -c -delta)/2, 0)
- + where((down -c) > delta ,(down -c -delta)/2, 0)
- + where((left -c) > delta ,(left -c -delta)/2, 0)
- + where((right-c) > delta ,(right-c -delta)/2, 0)
+ where((up - c) > delta, (up - c - delta) / 2, 0)
+ + where((down - c) > delta, (down - c - delta) / 2, 0)
+ + where((left - c) > delta, (left - c - delta) / 2, 0)
+ + where((right - c) > delta, (right - c - delta) / 2, 0)
# outgoing
- + where((up -c) < -delta,(up -c +delta)/2, 0)
- + where((down -c) < -delta,(down -c +delta)/2, 0)
- + where((left -c) < -delta,(left -c +delta)/2, 0)
- + where((right-c) < -delta,(right-c +delta)/2, 0)
- )
- randarray = np.random.randint(0,100,sa.shape) *0.01
+ + where((up - c) < -delta, (up - c + delta) / 2, 0)
+ + where((down - c) < -delta, (down - c + delta) / 2, 0)
+ + where((left - c) < -delta, (left - c + delta) / 2, 0)
+ + where((right - c) < -delta, (right - c + delta) / 2, 0)
+ )
+ randarray = np.random.randint(0, 100, sa.shape) * 0.01
sa = where(randarray < prob, sa, 0)
- self.avalanced[1:-1,1:-1] = self.avalanced[1:-1,1:-1] + sa/iterava
- self.center[1:-1,1:-1] = c + sa/iterava
+ self.avalanced[1:-1, 1:-1] = self.avalanced[1:-1, 1:-1] + sa / iterava
+ self.center[1:-1, 1:-1] = c + sa / iterava
self.maxrss = max(getmemsize(), self.maxrss)
return self.center
-
def rain(self, amount=1, variance=0, userainmap=False):
- self.water += (1.0 - np.random.random(self.water.shape) * variance) * (amount if ((self.rainmap is None) or (not userainmap)) else self.rainmap * amount)
-
+ self.water += (1.0 - np.random.random(self.water.shape) * variance) * \
+ (amount if ((self.rainmap is None) or (not userainmap)) else self.rainmap * amount)
def spring(self, amount, px, py, radius):
# px, py and radius are all fractions
nx, ny = self.center.shape
- rx = max(int(nx*radius),1)
- ry = max(int(ny*radius),1)
- px = int(nx*px)
- py = int(ny*py)
- self.water[px-rx:px+rx+1,py-ry:py+ry+1] += amount
-
+ rx = max(int(nx * radius), 1)
+ ry = max(int(ny * radius), 1)
+ px = int(nx * px)
+ py = int(ny * py)
+ self.water[px - rx:px + rx + 1, py - ry:py + ry + 1] += amount
def river(self, Kc, Ks, Kdep, Ka, Kev, numexpr):
zeros = np.zeros
@@ -328,11 +307,11 @@ class Grid:
arctan = np.arctan
sin = np.sin
- center = (slice( 1, -1,None),slice( 1, -1,None))
- up = (slice(None, -2,None),slice( 1, -1,None))
- down = (slice( 2, None,None),slice( 1, -1,None))
- left = (slice( 1, -1,None),slice(None, -2,None))
- right = (slice( 1, -1,None),slice( 2,None,None))
+ center = (slice(1, -1, None), slice(1, -1, None))
+ up = (slice(None, -2, None), slice(1, -1, None))
+ down = (slice(2, None, None), slice(1, -1, None))
+ left = (slice(1, -1, None), slice(None, -2, None))
+ right = (slice(1, -1, None), slice(2, None, None))
water = self.water
rock = self.center
@@ -348,7 +327,7 @@ class Grid:
svdw = zeros(water[center].shape)
sds = zeros(water[center].shape)
angle = zeros(water[center].shape)
- for d in (up,down,left,right):
+ for d in (up, down, left, right):
if(numexpr and numexpr_available):
hdd = height[d]
hcc = height[center]
@@ -356,25 +335,26 @@ class Grid:
inflow = ne.evaluate('dw > 0')
wdd = water[d]
wcc = water[center]
- dw = ne.evaluate('where(inflow, where(wdd<dw, wdd, dw), where(-wcc>dw, -wcc, dw))/4.0') # nested where() represent min() and max()
- sdw = ne.evaluate('sdw + dw')
- scd = sc[d]
- scc = sc[center]
- rockd= rock[d]
- rockc= rock[center]
- sds = ne.evaluate('sds + dw * where(inflow, scd, scc)')
+ # nested where() represent min() and max()
+ dw = ne.evaluate('where(inflow, where(wdd<dw, wdd, dw), where(-wcc>dw, -wcc, dw))/4.0')
+ sdw = ne.evaluate('sdw + dw')
+ scd = sc[d]
+ scc = sc[center]
+ rockd = rock[d]
+ rockc = rock[center]
+ sds = ne.evaluate('sds + dw * where(inflow, scd, scc)')
svdw = ne.evaluate('svdw + abs(dw)')
- angle= ne.evaluate('angle + arctan(abs(rockd-rockc))')
+ angle = ne.evaluate('angle + arctan(abs(rockd-rockc))')
else:
- dw = (height[d]-height[center])
+ dw = (height[d] - height[center])
inflow = dw > 0
- dw = where(inflow, min(water[d], dw), max(-water[center], dw))/4.0
- sdw = sdw + dw
- sds = sds + dw * where(inflow, sc[d], sc[center])
+ dw = where(inflow, min(water[d], dw), max(-water[center], dw)) / 4.0
+ sdw = sdw + dw
+ sds = sds + dw * where(inflow, sc[d], sc[center])
svdw = svdw + abs(dw)
- angle= angle + np.arctan(abs(rock[d]-rock[center]))
+ angle = angle + np.arctan(abs(rock[d] - rock[center]))
- if(numexpr and numexpr_available):
+ if numexpr and numexpr_available:
wcc = water[center]
scc = sediment[center]
rcc = rock[center]
@@ -391,10 +371,10 @@ class Grid:
wcc = water[center]
scc = sediment[center]
rcc = rock[center]
- water[center] = wcc * (1-Kev) + sdw
+ water[center] = wcc * (1 - Kev) + sdw
sediment[center] = scc + sds
sc = where(wcc > 0, scc / wcc, 2 * Kc)
- fKc = Kc*svdw
+ fKc = Kc * svdw
ds = where(fKc > sc, (fKc - sc) * Ks, (fKc - sc) * Kdep) * wcc
self.flowrate[center] = svdw
self.scour[center] = ds
@@ -402,7 +382,6 @@ class Grid:
self.capacity[center] = fKc
sediment[center] = scc + ds + sds
-
def flow(self, Kc, Ks, Kz, Ka, numexpr):
zeros = np.zeros
where = np.where
@@ -412,16 +391,30 @@ class Grid:
arctan = np.arctan
sin = np.sin
- center = (slice( 1, -1,None),slice( 1, -1,None))
+ center = (slice(1, -1, None), slice(1, -1, None))
rock = self.center
ds = self.scour[center]
rcc = rock[center]
rock[center] = rcc - ds * Kz
# there isn't really a bottom to the rock but negative values look ugly
- rock[center] = where(rcc<0,0,rcc)
-
-
- def rivergeneration(self, rainamount, rainvariance, userainmap, Kc, Ks, Kdep, Ka, Kev, Kspring, Kspringx, Kspringy, Kspringr, numexpr):
+ rock[center] = where(rcc < 0, 0, rcc)
+
+ def rivergeneration(
+ self,
+ rainamount,
+ rainvariance,
+ userainmap,
+ Kc,
+ Ks,
+ Kdep,
+ Ka,
+ Kev,
+ Kspring,
+ Kspringx,
+ Kspringy,
+ Kspringr,
+ numexpr,
+ ):
self.init_water_and_sediment()
self.rain(rainamount, rainvariance, userainmap)
self.zeroedge(self.water)
@@ -429,32 +422,43 @@ class Grid:
self.river(Kc, Ks, Kdep, Ka, Kev, numexpr)
self.watermax = np.max(self.water)
-
- def fluvial_erosion(self, rainamount, rainvariance, userainmap, Kc, Ks, Kdep, Ka, Kspring, Kspringx, Kspringy, Kspringr, numexpr):
+ def fluvial_erosion(
+ self,
+ rainamount,
+ rainvariance,
+ userainmap,
+ Kc,
+ Ks,
+ Kdep,
+ Ka,
+ Kspring,
+ Kspringx,
+ Kspringy,
+ Kspringr,
+ numexpr,
+ ):
self.flow(Kc, Ks, Kdep, Ka, numexpr)
self.flowratemax = np.max(self.flowrate)
self.scourmax = np.max(self.scour)
self.scourmin = np.min(self.scour)
self.sedmax = np.max(self.sediment)
-
def analyze(self):
self.neighborgrid()
# just looking at up and left to avoid needless double calculations
- slopes=np.concatenate((np.abs(self.left - self.center),np.abs(self.up - self.center)))
- return '\n'.join(["%-15s: %.3f"%t for t in [
- ('height average', np.average(self.center)),
- ('height median', np.median(self.center)),
- ('height max', np.max(self.center)),
- ('height min', np.min(self.center)),
- ('height std', np.std(self.center)),
- ('slope average', np.average(slopes)),
- ('slope median', np.median(slopes)),
- ('slope max', np.max(slopes)),
- ('slope min', np.min(slopes)),
- ('slope std', np.std(slopes))
- ]]
- )
+ slopes = np.concatenate((np.abs(self.left - self.center), np.abs(self.up - self.center)))
+ return '\n'.join(["%-15s: %.3f" % t for t in [
+ ('height average', np.average(self.center)),
+ ('height median', np.median(self.center)),
+ ('height max', np.max(self.center)),
+ ('height min', np.min(self.center)),
+ ('height std', np.std(self.center)),
+ ('slope average', np.average(slopes)),
+ ('slope median', np.median(slopes)),
+ ('slope max', np.max(slopes)),
+ ('slope min', np.min(slopes)),
+ ('slope std', np.std(slopes))
+ ]])
class TestGrid(unittest.TestCase):
@@ -462,12 +466,11 @@ class TestGrid(unittest.TestCase):
def test_diffuse(self):
g = Grid(5)
g.peak(1)
- self.assertEqual(g.center[2,2],1.0)
+ self.assertEqual(g.center[2, 2], 1.0)
g.diffuse(0.1, numexpr=False)
- for n in [(2,1),(2,3),(1,2),(3,2)]:
- self.assertAlmostEqual(g.center[n],0.1)
- self.assertAlmostEqual(g.center[2,2],0.6)
-
+ for n in [(2, 1), (2, 3), (1, 2), (3, 2)]:
+ self.assertAlmostEqual(g.center[n], 0.1)
+ self.assertAlmostEqual(g.center[2, 2], 0.6)
def test_diffuse_numexpr(self):
g = Grid(5)
@@ -476,8 +479,7 @@ class TestGrid(unittest.TestCase):
h = Grid(5)
h.peak(1)
h.diffuse(0.1, numexpr=True)
- self.assertEqual(list(g.center.flat),list(h.center.flat))
-
+ self.assertEqual(list(g.center.flat), list(h.center.flat))
def test_avalanche_numexpr(self):
g = Grid(5)
@@ -488,7 +490,7 @@ class TestGrid(unittest.TestCase):
h.avalanche(0.1, numexpr=True)
print(g)
print(h)
- np.testing.assert_almost_equal(g.center,h.center)
+ np.testing.assert_almost_equal(g.center, h.center)
if __name__ == "__main__":
@@ -501,7 +503,8 @@ if __name__ == "__main__":
parser.add_argument('-Kh', dest='Kh', type=float, default=6, help='Maximum stable cliff height')
parser.add_argument('-Kp', dest='Kp', type=float, default=0.1, help='Avalanche probability for unstable cliffs')
parser.add_argument('-Kr', dest='Kr', type=float, default=0.1, help='Average amount of rain per iteration')
- parser.add_argument('-Kspring', dest='Kspring', type=float, default=0.0, help='Average amount of wellwater per iteration')
+ parser.add_argument('-Kspring', dest='Kspring', type=float, default=0.0,
+ help='Average amount of wellwater per iteration')
parser.add_argument('-Kspringx', dest='Kspringx', type=float, default=0.5, help='relative x position of spring')
parser.add_argument('-Kspringy', dest='Kspringy', type=float, default=0.5, help='relative y position of spring')
parser.add_argument('-Kspringr', dest='Kspringr', type=float, default=0.02, help='radius of spring')
@@ -509,26 +512,45 @@ if __name__ == "__main__":
parser.add_argument('-Ks', dest='Ks', type=float, default=0.1, help='Soil softness constant')
parser.add_argument('-Kc', dest='Kc', type=float, default=1.0, help='Sediment capacity')
parser.add_argument('-Ka', dest='Ka', type=float, default=2.0, help='Slope dependency of erosion')
- parser.add_argument('-ri', action='store_true', dest='rawin', default=False, help='use Blender raw format for input')
- parser.add_argument('-ro', action='store_true', dest='rawout', default=False, help='use Blender raw format for output')
- parser.add_argument('-i', action='store_true', dest='useinputfile', default=False, help='use an inputfile (instead of just a synthesized grid)')
- parser.add_argument('-t', action='store_true', dest='timingonly', default=False, help='do not write anything to an output file')
+ parser.add_argument(
+ '-ri',
+ action='store_true',
+ dest='rawin',
+ default=False,
+ help='use Blender raw format for input')
+ parser.add_argument(
+ '-ro',
+ action='store_true',
+ dest='rawout',
+ default=False,
+ help='use Blender raw format for output')
+ parser.add_argument('-i', action='store_true', dest='useinputfile', default=False,
+ help='use an inputfile (instead of just a synthesized grid)')
+ parser.add_argument(
+ '-t',
+ action='store_true',
+ dest='timingonly',
+ default=False,
+ help='do not write anything to an output file')
parser.add_argument('-infile', type=str, default="-", help='input filename')
parser.add_argument('-outfile', type=str, default="-", help='output filename')
parser.add_argument('-Gn', dest='gridsize', type=int, default=20, help='Gridsize (always square)')
parser.add_argument('-Gp', dest='gridpeak', type=float, default=0, help='Add peak with given height')
parser.add_argument('-Gs', dest='gridshelf', type=float, default=0, help='Add shelve with given height')
parser.add_argument('-Gm', dest='gridmesa', type=float, default=0, help='Add mesa with given height')
- parser.add_argument('-Gr', dest='gridrandom', type=float, default=0, help='Add random values between 0 and given value')
+ parser.add_argument('-Gr', dest='gridrandom', type=float, default=0,
+ help='Add random values between 0 and given value')
parser.add_argument('-m', dest='threads', type=int, default=1, help='number of threads to use')
parser.add_argument('-u', action='store_true', dest='unittest', default=False, help='perform unittests')
- parser.add_argument('-a', action='store_true', dest='analyze', default=False, help='show some statistics of input and output meshes')
- parser.add_argument('-d', action='store_true', dest='dump', default=False, help='show sediment and water meshes at end of run')
+ parser.add_argument('-a', action='store_true', dest='analyze', default=False,
+ help='show some statistics of input and output meshes')
+ parser.add_argument('-d', action='store_true', dest='dump', default=False,
+ help='show sediment and water meshes at end of run')
parser.add_argument('-n', action='store_true', dest='usenumexpr', default=False, help='use numexpr optimizations')
args = parser.parse_args()
print("\nInput arguments:")
- print("\n".join("%-15s: %s"%t for t in sorted(vars(args).items())), file=sys.stderr)
+ print("\n".join("%-15s: %s" % t for t in sorted(vars(args).items())), file=sys.stderr)
if args.unittest:
unittest.main(argv=[sys.argv[0]])
@@ -542,13 +564,17 @@ if __name__ == "__main__":
else:
grid = Grid(args.gridsize)
- if args.gridpeak > 0 : grid.peak(args.gridpeak)
- if args.gridmesa > 0 : grid.mesa(args.gridmesa)
- if args.gridshelf > 0 : grid.shelf(args.gridshelf)
- if args.gridrandom > 0 : grid.random(args.gridrandom)
+ if args.gridpeak > 0:
+ grid.peak(args.gridpeak)
+ if args.gridmesa > 0:
+ grid.mesa(args.gridmesa)
+ if args.gridshelf > 0:
+ grid.shelf(args.gridshelf)
+ if args.gridrandom > 0:
+ grid.random(args.gridrandom)
if args.analyze:
- print('\nstatistics of the input grid:\n\n', grid.analyze(), file=sys.stderr, sep='' )
+ print('\nstatistics of the input grid:\n\n', grid.analyze(), file=sys.stderr, sep='')
t = getptime()
for g in range(args.iterations):
if args.Kd > 0:
@@ -556,9 +582,20 @@ if __name__ == "__main__":
if args.Kh > 0 and args.Kp > rand():
grid.avalanche(args.Kh, args.usenumexpr)
if args.Kr > 0 or args.Kspring > 0:
- grid.fluvial_erosion(args.Kr, args.Kc, args.Ks, args.Kdep, args.Ka, args.Kspring, args.Kspringx, args.Kspringy, args.Kspringr, args.usenumexpr)
+ grid.fluvial_erosion(
+ args.Kr,
+ args.Kc,
+ args.Ks,
+ args.Kdep,
+ args.Ka,
+ args.Kspring,
+ args.Kspringx,
+ args.Kspringy,
+ args.Kspringr,
+ args.usenumexpr,
+ )
t = getptime() - t
- print("\nElapsed time: %.1f seconds, max memory %.1f Mb.\n"%(t,grid.maxrss), file=sys.stderr)
+ print("\nElapsed time: %.1f seconds, max memory %.1f Mb.\n" % (t, grid.maxrss), file=sys.stderr)
if args.analyze:
print('\nstatistics of the output grid:\n\n', grid.analyze(), file=sys.stderr, sep='')
@@ -569,6 +606,6 @@ if __name__ == "__main__":
grid.toFile(args.outfile)
if args.dump:
- print("sediment\n", np.array_str(grid.sediment,precision=3), file=sys.stderr)
- print("water\n", np.array_str(grid.water,precision=3), file=sys.stderr)
- print("sediment concentration\n", np.array_str(grid.sediment/grid.water,precision=3), file=sys.stderr)
+ print("sediment\n", np.array_str(grid.sediment, precision=3), file=sys.stderr)
+ print("water\n", np.array_str(grid.water, precision=3), file=sys.stderr)
+ print("sediment concentration\n", np.array_str(grid.sediment / grid.water, precision=3), file=sys.stderr)
diff --git a/ant_landscape/mesh_ant_displace.py b/ant_landscape/mesh_ant_displace.py
index 5638d96a..132ca6fe 100644
--- a/ant_landscape/mesh_ant_displace.py
+++ b/ant_landscape/mesh_ant_displace.py
@@ -7,24 +7,26 @@
# import modules
import bpy
from bpy.props import (
- BoolProperty,
- EnumProperty,
- FloatProperty,
- IntProperty,
- StringProperty,
- FloatVectorProperty,
- )
+ BoolProperty,
+ EnumProperty,
+ FloatProperty,
+ IntProperty,
+ StringProperty,
+ FloatVectorProperty,
+)
from .ant_functions import (
- draw_ant_refresh,
- draw_ant_main,
- draw_ant_noise,
- draw_ant_displace,
- )
+ draw_ant_refresh,
+ draw_ant_main,
+ draw_ant_noise,
+ draw_ant_displace,
+)
from .ant_noise import noise_gen
from ant_landscape import ant_noise
# ------------------------------------------------------------
# Do vert displacement
+
+
class AntMeshDisplace(bpy.types.Operator):
bl_idname = "mesh.ant_displace"
bl_label = "Another Noise Tool - Displace"
@@ -32,131 +34,131 @@ class AntMeshDisplace(bpy.types.Operator):
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
ant_terrain_name: StringProperty(
- name="Name",
- default="Landscape"
- )
+ name="Name",
+ default="Landscape"
+ )
land_material: StringProperty(
- name='Material',
- default="",
- description="Terrain material"
- )
+ name='Material',
+ default="",
+ description="Terrain material"
+ )
water_material: StringProperty(
- name='Material',
- default="",
- description="Water plane material"
- )
+ name='Material',
+ default="",
+ description="Water plane material"
+ )
texture_block: StringProperty(
- name="Texture",
- default=""
- )
+ name="Texture",
+ default=""
+ )
at_cursor: BoolProperty(
- name="Cursor",
- default=True,
- description="Place at cursor location",
- )
+ name="Cursor",
+ default=True,
+ description="Place at cursor location",
+ )
smooth_mesh: BoolProperty(
- name="Smooth",
- default=True,
- description="Shade smooth"
- )
+ name="Smooth",
+ default=True,
+ description="Shade smooth"
+ )
tri_face: BoolProperty(
- name="Triangulate",
- default=False,
- description="Triangulate faces"
- )
+ name="Triangulate",
+ default=False,
+ description="Triangulate faces"
+ )
sphere_mesh: BoolProperty(
- name="Sphere",
- default=False,
- description="Generate uv sphere - remove doubles when ready"
- )
+ name="Sphere",
+ default=False,
+ description="Generate uv sphere - remove doubles when ready"
+ )
subdivision_x: IntProperty(
- name="Subdivisions X",
- default=128,
- min=4,
- max=6400,
- description="Mesh X subdivisions"
- )
+ name="Subdivisions X",
+ default=128,
+ min=4,
+ max=6400,
+ description="Mesh X subdivisions"
+ )
subdivision_y: IntProperty(
- default=128,
- name="Subdivisions Y",
- min=4,
- max=6400,
- description="Mesh Y subdivisions"
- )
+ default=128,
+ name="Subdivisions Y",
+ min=4,
+ max=6400,
+ description="Mesh Y subdivisions"
+ )
mesh_size: FloatProperty(
- default=2.0,
- name="Mesh Size",
- min=0.01,
- max=100000.0,
- description="Mesh size"
- )
+ default=2.0,
+ name="Mesh Size",
+ min=0.01,
+ max=100000.0,
+ description="Mesh size"
+ )
mesh_size_x: FloatProperty(
- default=2.0,
- name="Mesh Size X",
- min=0.01,
- description="Mesh x size"
- )
+ default=2.0,
+ name="Mesh Size X",
+ min=0.01,
+ description="Mesh x size"
+ )
mesh_size_y: FloatProperty(
- name="Mesh Size Y",
- default=2.0,
- min=0.01,
- description="Mesh y size"
- )
+ name="Mesh Size Y",
+ default=2.0,
+ min=0.01,
+ description="Mesh y size"
+ )
random_seed: IntProperty(
- name="Random Seed",
- default=0,
- min=0,
- description="Randomize noise origin"
- )
+ name="Random Seed",
+ default=0,
+ min=0,
+ description="Randomize noise origin"
+ )
noise_offset_x: FloatProperty(
- name="Offset X",
- default=0.0,
- description="Noise X Offset"
- )
+ name="Offset X",
+ default=0.0,
+ description="Noise X Offset"
+ )
noise_offset_y: FloatProperty(
- name="Offset Y",
- default=0.0,
- description="Noise Y Offset"
- )
+ name="Offset Y",
+ default=0.0,
+ description="Noise Y Offset"
+ )
noise_offset_z: FloatProperty(
- name="Offset Z",
- default=0.0,
- description="Noise Z Offset"
- )
+ name="Offset Z",
+ default=0.0,
+ description="Noise Z Offset"
+ )
noise_size_x: FloatProperty(
- default=1.0,
- name="Size X",
- min=0.01,
- max=1000.0,
- description="Noise x size"
- )
+ default=1.0,
+ name="Size X",
+ min=0.01,
+ max=1000.0,
+ description="Noise x size"
+ )
noise_size_y: FloatProperty(
- name="Size Y",
- default=1.0,
- min=0.01,
- max=1000.0,
- description="Noise y size"
- )
+ name="Size Y",
+ default=1.0,
+ min=0.01,
+ max=1000.0,
+ description="Noise y size"
+ )
noise_size_z: FloatProperty(
- name="Size Z",
- default=1.0,
- min=0.01,
- max=1000.0,
- description="Noise Z size"
- )
+ name="Size Z",
+ default=1.0,
+ min=0.01,
+ max=1000.0,
+ description="Noise Z size"
+ )
noise_size: FloatProperty(
- name="Noise Size",
- default=0.25,
- min=0.01,
- max=1000.0,
- description="Noise size"
- )
+ name="Noise Size",
+ default=0.25,
+ min=0.01,
+ max=1000.0,
+ description="Noise size"
+ )
noise_type: EnumProperty(
- name="Noise Type",
- default='hetero_terrain',
- description="Noise type",
- items = [
+ name="Noise Type",
+ default='hetero_terrain',
+ description="Noise type",
+ items=[
('multi_fractal', "Multi Fractal", "Blender: Multi Fractal algorithm", 0),
('ridged_multi_fractal', "Ridged MFractal", "Blender: Ridged Multi Fractal", 1),
('hybrid_multi_fractal', "Hybrid MFractal", "Blender: Hybrid Multi Fractal", 2),
@@ -176,110 +178,110 @@ class AntMeshDisplace(bpy.types.Operator):
('slick_rock', "Slick Rock", "A.N.T: slick rock", 16),
('planet_noise', "Planet Noise", "Planet Noise by: Farsthary", 17),
('blender_texture', "Blender Texture - Texture Nodes", "Blender texture data block", 18)]
- )
+ )
basis_type: EnumProperty(
- name="Noise Basis",
- default=ant_noise.noise_basis_default,
- description="Noise basis algorithms",
- items = ant_noise.noise_basis
- )
+ name="Noise Basis",
+ default=ant_noise.noise_basis_default,
+ description="Noise basis algorithms",
+ items=ant_noise.noise_basis
+ )
vl_basis_type: EnumProperty(
- name="vlNoise Basis",
- default=ant_noise.noise_basis_default,
- description="VLNoise basis algorithms",
- items = ant_noise.noise_basis
- )
+ name="vlNoise Basis",
+ default=ant_noise.noise_basis_default,
+ description="VLNoise basis algorithms",
+ items=ant_noise.noise_basis
+ )
distortion: FloatProperty(
- name="Distortion",
- default=1.0,
- min=0.01,
- max=100.0,
- description="Distortion amount"
- )
+ name="Distortion",
+ default=1.0,
+ min=0.01,
+ max=100.0,
+ description="Distortion amount"
+ )
hard_noise: EnumProperty(
- name="Soft Hard",
- default="0",
- description="Soft Noise, Hard noise",
- items = [
+ name="Soft Hard",
+ default="0",
+ description="Soft Noise, Hard noise",
+ items=[
("0", "Soft", "Soft Noise", 0),
("1", "Hard", "Hard noise", 1)]
- )
+ )
noise_depth: IntProperty(
- name="Depth",
- default=8,
- min=0,
- max=16,
- description="Noise Depth - number of frequencies in the fBm"
- )
+ name="Depth",
+ default=8,
+ min=0,
+ max=16,
+ description="Noise Depth - number of frequencies in the fBm"
+ )
amplitude: FloatProperty(
- name="Amp",
- default=0.5,
- min=0.01,
- max=1.0,
- description="Amplitude"
- )
+ name="Amp",
+ default=0.5,
+ min=0.01,
+ max=1.0,
+ description="Amplitude"
+ )
frequency: FloatProperty(
- name="Freq",
- default=2.0,
- min=0.01,
- max=5.0,
- description="Frequency"
- )
+ name="Freq",
+ default=2.0,
+ min=0.01,
+ max=5.0,
+ description="Frequency"
+ )
dimension: FloatProperty(
- name="Dimension",
- default=1.0,
- min=0.01,
- max=2.0,
- description="H - fractal dimension of the roughest areas"
- )
+ name="Dimension",
+ default=1.0,
+ min=0.01,
+ max=2.0,
+ description="H - fractal dimension of the roughest areas"
+ )
lacunarity: FloatProperty(
- name="Lacunarity",
- min=0.01,
- max=6.0,
- default=2.0,
- description="Lacunarity - gap between successive frequencies"
- )
+ name="Lacunarity",
+ min=0.01,
+ max=6.0,
+ default=2.0,
+ description="Lacunarity - gap between successive frequencies"
+ )
offset: FloatProperty(
- name="Offset",
- default=1.0,
- min=0.01,
- max=6.0,
- description="Offset - raises the terrain from sea level"
- )
+ name="Offset",
+ default=1.0,
+ min=0.01,
+ max=6.0,
+ description="Offset - raises the terrain from sea level"
+ )
gain: FloatProperty(
- name="Gain",
- default=1.0,
- min=0.01,
- max=6.0,
- description="Gain - scale factor"
- )
+ name="Gain",
+ default=1.0,
+ min=0.01,
+ max=6.0,
+ description="Gain - scale factor"
+ )
marble_bias: EnumProperty(
- name="Bias",
- default="0",
- description="Marble bias",
- items = [
+ name="Bias",
+ default="0",
+ description="Marble bias",
+ items=[
("0", "Sin", "Sin", 0),
("1", "Cos", "Cos", 1),
("2", "Tri", "Tri", 2),
("3", "Saw", "Saw", 3)]
- )
+ )
marble_sharp: EnumProperty(
- name="Sharp",
- default="0",
- description="Marble sharpness",
- items = [
+ name="Sharp",
+ default="0",
+ description="Marble sharpness",
+ items=[
("0", "Soft", "Soft", 0),
("1", "Sharp", "Sharp", 1),
("2", "Sharper", "Sharper", 2),
("3", "Soft inv.", "Soft", 3),
("4", "Sharp inv.", "Sharp", 4),
("5", "Sharper inv.", "Sharper", 5)]
- )
+ )
marble_shape: EnumProperty(
- name="Shape",
- default="0",
- description="Marble shape",
- items= [
+ name="Shape",
+ default="0",
+ description="Marble shape",
+ items=[
("0", "Default", "Default", 0),
("1", "Ring", "Ring", 1),
("2", "Swirl", "Swirl", 2),
@@ -288,39 +290,39 @@ class AntMeshDisplace(bpy.types.Operator):
("5", "Z", "Z", 5),
("6", "Y", "Y", 6),
("7", "X", "X", 7)]
- )
+ )
height: FloatProperty(
- name="Height",
- default=0.25,
- min=-10000.0,
- max=10000.0,
- description="Noise intensity scale"
- )
+ name="Height",
+ default=0.25,
+ min=-10000.0,
+ max=10000.0,
+ description="Noise intensity scale"
+ )
height_invert: BoolProperty(
- name="Invert",
- default=False,
- description="Height invert",
- )
+ name="Invert",
+ default=False,
+ description="Height invert",
+ )
height_offset: FloatProperty(
- name="Offset",
- default=0.0,
- min=-10000.0,
- max=10000.0,
- description="Height offset"
- )
+ name="Offset",
+ default=0.0,
+ min=-10000.0,
+ max=10000.0,
+ description="Height offset"
+ )
fx_mixfactor: FloatProperty(
- name="Mix Factor",
- default=0.0,
- min=-1.0,
- max=1.0,
- description="Effect mix factor: -1.0 = Noise, +1.0 = Effect"
- )
+ name="Mix Factor",
+ default=0.0,
+ min=-1.0,
+ max=1.0,
+ description="Effect mix factor: -1.0 = Noise, +1.0 = Effect"
+ )
fx_mix_mode: EnumProperty(
- name="Effect Mix",
- default="0",
- description="Effect mix mode",
- items = [
+ name="Effect Mix",
+ default="0",
+ description="Effect mix mode",
+ items=[
("0", "Mix", "Mix", 0),
("1", "Add", "Add", 1),
("2", "Sub", "Subtract", 2),
@@ -330,13 +332,13 @@ class AntMeshDisplace(bpy.types.Operator):
("6", "Mod", "Modulo", 6),
("7", "Min", "Minimum", 7),
("8", "Max", "Maximum", 8)
- ]
- )
+ ]
+ )
fx_type: EnumProperty(
- name="Effect Type",
- default="0",
- description="Effect type",
- items = [
+ name="Effect Type",
+ default="0",
+ description="Effect type",
+ items=[
("0", "None", "No effect", 0),
("1", "Gradient", "Gradient", 1),
("2", "Waves", "Waves - Bumps", 2),
@@ -359,223 +361,220 @@ class AntMeshDisplace(bpy.types.Operator):
("19", "Stone", "Stone", 19),
("20", "Flat Turb", "Flat turbulence", 20),
("21", "Flat Voronoi", "Flat voronoi", 21)
- ]
- )
+ ]
+ )
fx_bias: EnumProperty(
- name="Effect Bias",
- default="0",
- description="Effect bias type",
- items = [
+ name="Effect Bias",
+ default="0",
+ description="Effect bias type",
+ items=[
("0", "Sin", "Sin", 0),
("1", "Cos", "Cos", 1),
("2", "Tri", "Tri", 2),
("3", "Saw", "Saw", 3),
("4", "None", "None", 4)
- ]
- )
+ ]
+ )
fx_turb: FloatProperty(
- name="Distortion",
- default=0.0,
- min=0.0,
- max=1000.0,
- description="Effect turbulence distortion"
- )
+ name="Distortion",
+ default=0.0,
+ min=0.0,
+ max=1000.0,
+ description="Effect turbulence distortion"
+ )
fx_depth: IntProperty(
- name="Depth",
- default=0,
- min=0,
- max=16,
- description="Effect depth - number of frequencies"
- )
+ name="Depth",
+ default=0,
+ min=0,
+ max=16,
+ description="Effect depth - number of frequencies"
+ )
fx_amplitude: FloatProperty(
- name="Amp",
- default=0.5,
- min=0.01,
- max=1.0,
- description="Amplitude"
- )
+ name="Amp",
+ default=0.5,
+ min=0.01,
+ max=1.0,
+ description="Amplitude"
+ )
fx_frequency: FloatProperty(
- name="Freq",
- default=2.0,
- min=0.01,
- max=5.0,
- description="Frequency"
- )
+ name="Freq",
+ default=2.0,
+ min=0.01,
+ max=5.0,
+ description="Frequency"
+ )
fx_size: FloatProperty(
- name="Effect Size",
- default=1.0,
- min=0.01,
- max=1000.0,
- description="Effect size"
- )
+ name="Effect Size",
+ default=1.0,
+ min=0.01,
+ max=1000.0,
+ description="Effect size"
+ )
fx_loc_x: FloatProperty(
- name="Offset X",
- default=0.0,
- description="Effect x offset"
- )
+ name="Offset X",
+ default=0.0,
+ description="Effect x offset"
+ )
fx_loc_y: FloatProperty(
- name="Offset Y",
- default=0.0,
- description="Effect y offset"
- )
+ name="Offset Y",
+ default=0.0,
+ description="Effect y offset"
+ )
fx_height: FloatProperty(
- name="Intensity",
- default=1.0,
- min=-1000.0,
- max=1000.0,
- description="Effect intensity scale"
- )
+ name="Intensity",
+ default=1.0,
+ min=-1000.0,
+ max=1000.0,
+ description="Effect intensity scale"
+ )
fx_invert: BoolProperty(
- name="Invert",
- default=False,
- description="Effect invert"
- )
+ name="Invert",
+ default=False,
+ description="Effect invert"
+ )
fx_offset: FloatProperty(
- name="Offset",
- default=0.0,
- min=-1000.0,
- max=1000.0,
- description="Effect height offset"
- )
+ name="Offset",
+ default=0.0,
+ min=-1000.0,
+ max=1000.0,
+ description="Effect height offset"
+ )
edge_falloff: EnumProperty(
- name="Falloff",
- default="0",
- description="Flatten edges",
- items = [
+ name="Falloff",
+ default="0",
+ description="Flatten edges",
+ items=[
("0", "None", "None", 0),
("1", "Y", "Y Falloff", 1),
("2", "X", "X Falloff", 2),
("3", "X Y", "X Y Falloff", 3)]
- )
+ )
falloff_x: FloatProperty(
- name="Falloff X",
- default=4.0,
- min=0.1,
- max=100.0,
- description="Falloff x scale"
- )
+ name="Falloff X",
+ default=4.0,
+ min=0.1,
+ max=100.0,
+ description="Falloff x scale"
+ )
falloff_y: FloatProperty(
- name="Falloff Y",
- default=4.0,
- min=0.1,
- max=100.0,
- description="Falloff y scale"
- )
+ name="Falloff Y",
+ default=4.0,
+ min=0.1,
+ max=100.0,
+ description="Falloff y scale"
+ )
edge_level: FloatProperty(
- name="Edge Level",
- default=0.0,
- min=-10000.0,
- max=10000.0,
- description="Edge level, sealevel offset"
- )
+ name="Edge Level",
+ default=0.0,
+ min=-10000.0,
+ max=10000.0,
+ description="Edge level, sealevel offset"
+ )
maximum: FloatProperty(
- name="Maximum",
- default=1.0,
- min=-10000.0,
- max=10000.0,
- description="Maximum, flattens terrain at plateau level"
- )
+ name="Maximum",
+ default=1.0,
+ min=-10000.0,
+ max=10000.0,
+ description="Maximum, flattens terrain at plateau level"
+ )
minimum: FloatProperty(
- name="Minimum",
- default=-1.0,
- min=-10000.0,
- max=10000.0,
- description="Minimum, flattens terrain at seabed level"
- )
+ name="Minimum",
+ default=-1.0,
+ min=-10000.0,
+ max=10000.0,
+ description="Minimum, flattens terrain at seabed level"
+ )
vert_group: StringProperty(
- name="Vertex Group",
- default=""
- )
+ name="Vertex Group",
+ default=""
+ )
strata: FloatProperty(
- name="Amount",
- default=5.0,
- min=0.01,
- max=1000.0,
- description="Strata layers / terraces"
- )
+ name="Amount",
+ default=5.0,
+ min=0.01,
+ max=1000.0,
+ description="Strata layers / terraces"
+ )
strata_type: EnumProperty(
- name="Strata",
- default="0",
- description="Strata types",
- items = [
+ name="Strata",
+ default="0",
+ description="Strata types",
+ items=[
("0", "None", "No strata", 0),
("1", "Smooth", "Smooth transitions", 1),
("2", "Sharp Sub", "Sharp subtract transitions", 2),
("3", "Sharp Add", "Sharp add transitions", 3),
("4", "Quantize", "Quantize", 4),
("5", "Quantize Mix", "Quantize mixed", 5)]
- )
+ )
water_plane: BoolProperty(
- name="Water Plane",
- default=False,
- description="Add water plane"
- )
+ name="Water Plane",
+ default=False,
+ description="Add water plane"
+ )
water_level: FloatProperty(
- name="Level",
- default=0.01,
- min=-10000.0,
- max=10000.0,
- description="Water level"
- )
+ name="Level",
+ default=0.01,
+ min=-10000.0,
+ max=10000.0,
+ description="Water level"
+ )
remove_double: BoolProperty(
- name="Remove Doubles",
- default=False,
- description="Remove doubles"
- )
+ name="Remove Doubles",
+ default=False,
+ description="Remove doubles"
+ )
direction: EnumProperty(
- name="Direction",
- default="NORMAL",
- description="Displacement direction",
- items = [
+ name="Direction",
+ default="NORMAL",
+ description="Displacement direction",
+ items=[
("NORMAL", "Normal", "Displace along vertex normal direction", 0),
("Z", "Z", "Displace in the Z direction", 1),
("Y", "Y", "Displace in the Y direction", 2),
("X", "X", "Displace in the X direction", 3)]
- )
+ )
show_main_settings: BoolProperty(
- name="Main Settings",
- default=True,
- description="Show settings"
- )
+ name="Main Settings",
+ default=True,
+ description="Show settings"
+ )
show_noise_settings: BoolProperty(
- name="Noise Settings",
- default=True,
- description="Show noise settings"
- )
+ name="Noise Settings",
+ default=True,
+ description="Show noise settings"
+ )
show_displace_settings: BoolProperty(
- name="Displace Settings",
- default=True,
- description="Show terrain settings"
- )
+ name="Displace Settings",
+ default=True,
+ description="Show terrain settings"
+ )
refresh: BoolProperty(
- name="Refresh",
- default=False,
- description="Refresh"
- )
+ name="Refresh",
+ default=False,
+ description="Refresh"
+ )
auto_refresh: BoolProperty(
- name="Auto",
- default=False,
- description="Automatic refresh"
- )
+ name="Auto",
+ default=False,
+ description="Automatic refresh"
+ )
def draw(self, context):
draw_ant_refresh(self, context)
draw_ant_noise(self, context, generate=False)
draw_ant_displace(self, context, generate=False)
-
@classmethod
def poll(cls, context):
ob = context.object
return (ob and ob.type == 'MESH')
-
def invoke(self, context, event):
self.refresh = True
return self.execute(context)
-
def execute(self, context):
if not self.refresh:
return {'PASS_THROUGH'}
@@ -649,7 +648,7 @@ class AntMeshDisplace(bpy.types.Operator):
self.fx_height,
self.fx_offset,
self.fx_invert
- ]
+ ]
# do displace
mesh = ob.data
diff --git a/ant_landscape/stats.py b/ant_landscape/stats.py
index c8793dd9..b217a7b0 100644
--- a/ant_landscape/stats.py
+++ b/ant_landscape/stats.py
@@ -5,15 +5,16 @@ from time import time
try:
import psutil
# print('psutil available')
- psutil_available=True
+ psutil_available = True
except ImportError:
- psutil_available=False
+ psutil_available = False
+
class Stats:
def __init__(self):
self.memstats_available = False
if psutil_available:
- self.process=psutil.Process()
+ self.process = psutil.Process()
self.memstats_available = True
self.reset()
diff --git a/ant_landscape/test.py b/ant_landscape/test.py
index 39d060c4..65e641bd 100644
--- a/ant_landscape/test.py
+++ b/ant_landscape/test.py
@@ -18,6 +18,6 @@ if __name__ == '__main__':
a = cos(a)
print(stats.time())
print(stats.memory())
- a = cos(a)**2+sin(a)**2
+ a = cos(a) ** 2 + sin(a) ** 2
print(stats.time())
print(stats.memory())
diff --git a/ant_landscape/utils.py b/ant_landscape/utils.py
index 7687bd54..9451f83a 100644
--- a/ant_landscape/utils.py
+++ b/ant_landscape/utils.py
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-or-later
-numexpr_available=False
+numexpr_available = False
try:
import numexpr
- numexpr_available=True
+ numexpr_available = True
except ImportError:
pass
diff --git a/archimesh/__init__.py b/archimesh/__init__.py
index 261397f1..d79e02f7 100644
--- a/archimesh/__init__.py
+++ b/archimesh/__init__.py
@@ -11,8 +11,8 @@ bl_info = {
"name": "Archimesh",
"author": "Antonio Vazquez (antonioya)",
"location": "View3D > Add Mesh / Sidebar > Create Tab",
- "version": (1, 2, 2),
- "blender": (2, 80, 0),
+ "version": (1, 2, 3),
+ "blender": (3, 0, 0),
"description": "Generate rooms, doors, windows, and other architecture objects",
"doc_url": "{BLENDER_MANUAL_URL}/addons/add_mesh/archimesh.html",
"category": "Add Mesh"
diff --git a/archimesh/achm_gltools.py b/archimesh/achm_gltools.py
index de11226d..00160989 100644
--- a/archimesh/achm_gltools.py
+++ b/archimesh/achm_gltools.py
@@ -16,7 +16,6 @@ from mathutils import Vector
from bpy_extras import view3d_utils
from .achm_room_maker import get_wall_points
# GPU
-import bgl
import gpu
from gpu_extras.batch import batch_for_shader
@@ -39,7 +38,7 @@ def draw_main(context):
measure = scene.archimesh_gl_measure
dspname = scene.archimesh_gl_name
- bgl.glEnable(bgl.GL_BLEND)
+ gpu.state.blend_set('ALPHA')
# Display selected or all
if scene.archimesh_gl_ghost is False:
objlist = context.selected_objects
@@ -81,8 +80,8 @@ def draw_main(context):
# -----------------------
# restore opengl defaults
# -----------------------
- bgl.glLineWidth(1)
- bgl.glDisable(bgl.GL_BLEND)
+ gpu.state.line_width_set(1.0)
+ gpu.state.blend_set('NONE')
# -------------------------------------------------------------
@@ -175,8 +174,8 @@ def draw_room_data(myobj, op, region, rv3d, rgba, rgbaw, fsize, wfsize, space, m
screen_point_b2 = view3d_utils.location_3d_to_region_2d(region, rv3d, b2_s2)
# colour + line setup
- bgl.glEnable(bgl.GL_BLEND)
- bgl.glLineWidth(1)
+ gpu.state.blend_set('ALPHA')
+ gpu.state.line_width_set(1.0)
# --------------------------------
# Measures
# --------------------------------
@@ -284,8 +283,8 @@ def draw_door_data(myobj, op, region, rv3d, rgba, fsize, space, measure):
screen_point_ep3 = view3d_utils.location_3d_to_region_2d(region, rv3d, e_p3)
# colour + line setup
- bgl.glEnable(bgl.GL_BLEND)
- bgl.glLineWidth(1)
+ gpu.state.blend_set('ALPHA')
+ gpu.state.line_width_set(1.0)
# --------------------------------
# Measures
@@ -368,8 +367,8 @@ def draw_window_rail_data(myobj, op, region, rv3d, rgba, fsize, space, measure):
screen_point_tp3 = view3d_utils.location_3d_to_region_2d(region, rv3d, t_p3)
# colour + line setup
- bgl.glEnable(bgl.GL_BLEND)
- bgl.glLineWidth(1)
+ gpu.state.blend_set('ALPHA')
+ gpu.state.line_width_set(1.0)
# --------------------------------
# Measures
@@ -473,9 +472,10 @@ def draw_window_panel_data(myobj, op, region, rv3d, rgba, fsize, space, measure)
screen_point_gp3 = view3d_utils.location_3d_to_region_2d(region, rv3d, g_p3)
screen_point_gp4 = view3d_utils.location_3d_to_region_2d(region, rv3d, g_p4)
screen_point_gp5 = view3d_utils.location_3d_to_region_2d(region, rv3d, g_p5)
+
# colour + line setup
- bgl.glEnable(bgl.GL_BLEND)
- bgl.glLineWidth(1)
+ gpu.state.blend_set('ALPHA')
+ gpu.state.line_width_set(1.0)
# --------------------------------
# Measures
diff --git a/archimesh/achm_main_panel.py b/archimesh/achm_main_panel.py
index 97c7d19d..482e26fd 100644
--- a/archimesh/achm_main_panel.py
+++ b/archimesh/achm_main_panel.py
@@ -8,7 +8,6 @@
# noinspection PyUnresolvedReferences
import bpy
# noinspection PyUnresolvedReferences
-import bgl
from bpy.types import Operator, Panel, SpaceView3D
from math import sqrt, fabs, pi, asin
from .achm_tools import *
@@ -36,6 +35,7 @@ class ARCHIMESH_OT_Hole(Operator):
bl_label = "Auto Holes"
bl_description = "Enable windows and doors holes for any selected object (needs wall thickness)"
bl_category = 'View'
+ bl_options = {'UNDO', 'REGISTER'}
# ------------------------------
# Execute
@@ -170,6 +170,7 @@ class ARCHIMESH_OT_Pencil(Operator):
bl_label = "Room from Draw"
bl_description = "Create a room base on grease pencil strokes (draw from top view (7 key))"
bl_category = 'View'
+ bl_options = {'UNDO', 'REGISTER'}
# ------------------------------
# Execute
diff --git a/btrace/__init__.py b/btrace/__init__.py
index fd4bf57c..527ceb3f 100644
--- a/btrace/__init__.py
+++ b/btrace/__init__.py
@@ -4,7 +4,7 @@
bl_info = {
"name": "BTracer",
"author": "liero, crazycourier, Atom, Meta-Androcto, MacKracken",
- "version": (1, 2, 3),
+ "version": (1, 2, 4),
"blender": (2, 80, 0),
"location": "View3D > Sidebar > Create Tab",
"description": "Tools for converting/animating objects/particles into curves",
diff --git a/btrace/bTrace.py b/btrace/bTrace.py
index a17c3dd7..2ec92d72 100644
--- a/btrace/bTrace.py
+++ b/btrace/bTrace.py
@@ -303,9 +303,9 @@ class OBJECT_OT_particletrace(Operator):
spline = tracer[0].splines.new('BEZIER')
# add point to spline based on step size
- spline.bezier_points.add((x.lifetime - 1) // particle_step)
+ spline.bezier_points.add(int((x.lifetime - 1) // particle_step))
for t in list(range(int(x.lifetime))):
- bpy.context.scene.frame_set(t + x.birth_time)
+ bpy.context.scene.frame_set(int(t + x.birth_time))
if not t % particle_step:
p = spline.bezier_points[t // particle_step]
diff --git a/curve_assign_shapekey.py b/curve_assign_shapekey.py
index a87e89b0..75e42b1a 100644
--- a/curve_assign_shapekey.py
+++ b/curve_assign_shapekey.py
@@ -8,7 +8,7 @@
#
# https://github.com/Shriinivas/assignshapekey/blob/master/LICENSE
-import bpy, bmesh, bgl, gpu
+import bpy, bmesh, gpu
from gpu_extras.batch import batch_for_shader
from bpy.props import BoolProperty, EnumProperty, StringProperty
from collections import OrderedDict
@@ -21,8 +21,8 @@ from bpy.types import Panel, Operator, AddonPreferences
bl_info = {
"name": "Assign Shape Keys",
"author": "Shrinivas Kulkarni",
- "version": (1, 0, 1),
- "blender": (2, 80, 0),
+ "version": (1, 0, 2),
+ "blender": (3, 0, 0),
"location": "View 3D > Sidebar > Edit Tab",
"description": "Assigns one or more Bezier curves as shape keys to another Bezier curve",
"doc_url": "{BLENDER_MANUAL_URL}/addons/add_curve/assign_shape_keys.html",
@@ -835,7 +835,7 @@ class MarkerController:
context.area.tag_redraw()
def drawHandler(self):
- bgl.glPointSize(MarkerController.defPointSize)
+ gpu.state.point_size_set(MarkerController.defPointSize)
self.batch.draw(self.shader)
def removeMarkers(self, context):
diff --git a/curve_tools/auto_loft.py b/curve_tools/auto_loft.py
index 3092e6b8..b14aaf3b 100644
--- a/curve_tools/auto_loft.py
+++ b/curve_tools/auto_loft.py
@@ -12,6 +12,7 @@ class OperatorAutoLoftCurves(Operator):
bl_idname = "curvetools.create_auto_loft"
bl_label = "Loft"
bl_description = "Lofts selected curves"
+ bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
diff --git a/curve_tools/operators.py b/curve_tools/operators.py
index e4480f88..d4ada318 100644
--- a/curve_tools/operators.py
+++ b/curve_tools/operators.py
@@ -145,6 +145,7 @@ class OperatorOriginToSpline0Start(bpy.types.Operator):
bl_idname = "curvetools.operatororigintospline0start"
bl_label = "OriginToSpline0Start"
bl_description = "Sets the origin of the active/selected curve to the starting point of the (first) spline. Nice for curve modifiers"
+ bl_options = {'UNDO'}
@classmethod
@@ -182,6 +183,7 @@ class OperatorIntersectCurves(bpy.types.Operator):
bl_idname = "curvetools.operatorintersectcurves"
bl_label = "Intersect"
bl_description = "Intersects selected curves"
+ bl_options = {'UNDO'}
@classmethod
@@ -233,6 +235,7 @@ class OperatorLoftCurves(bpy.types.Operator):
bl_idname = "curvetools.operatorloftcurves"
bl_label = "Loft"
bl_description = "Lofts selected curves"
+ bl_options = {'UNDO'}
@classmethod
@@ -258,6 +261,7 @@ class OperatorSweepCurves(bpy.types.Operator):
bl_idname = "curvetools.operatorsweepcurves"
bl_label = "Sweep"
bl_description = "Sweeps the active curve along to other curve (rail)"
+ bl_options = {'UNDO'}
@classmethod
@@ -283,6 +287,7 @@ class OperatorBirail(bpy.types.Operator):
bl_idname = "curvetools.operatorbirail"
bl_label = "Birail"
bl_description = "Generates a birailed surface from 3 selected curves -- in order: rail1, rail2 and profile"
+ bl_options = {'UNDO'}
@classmethod
@@ -306,6 +311,7 @@ class OperatorSplinesSetResolution(bpy.types.Operator):
bl_idname = "curvetools.operatorsplinessetresolution"
bl_label = "SplinesSetResolution"
bl_description = "Sets the resolution of all splines"
+ bl_options = {'UNDO'}
@classmethod
@@ -330,6 +336,7 @@ class OperatorSplinesRemoveZeroSegment(bpy.types.Operator):
bl_idname = "curvetools.operatorsplinesremovezerosegment"
bl_label = "SplinesRemoveZeroSegment"
bl_description = "Removes splines with no segments -- they seem to creep up, sometimes"
+ bl_options = {'UNDO'}
@classmethod
@@ -364,6 +371,7 @@ class OperatorSplinesRemoveShort(bpy.types.Operator):
bl_idname = "curvetools.operatorsplinesremoveshort"
bl_label = "SplinesRemoveShort"
bl_description = "Removes splines with a length smaller than the threshold"
+ bl_options = {'UNDO'}
@classmethod
@@ -393,6 +401,7 @@ class OperatorSplinesJoinNeighbouring(bpy.types.Operator):
bl_idname = "curvetools.operatorsplinesjoinneighbouring"
bl_label = "SplinesJoinNeighbouring"
bl_description = "Joins neighbouring splines within a distance smaller than the threshold"
+ bl_options = {'UNDO'}
@classmethod
diff --git a/curve_tools/path_finder.py b/curve_tools/path_finder.py
index abf53a6c..242ed27c 100644
--- a/curve_tools/path_finder.py
+++ b/curve_tools/path_finder.py
@@ -3,8 +3,8 @@
bl_info = {
'name': 'PathFinder',
'author': 'Spivak Vladimir (cwolf3d)',
- 'version': (0, 5, 0),
- 'blender': (2, 80, 0),
+ 'version': (0, 5, 1),
+ 'blender': (3, 0, 0),
'location': 'Curve Tools addon. (N) Panel',
'description': 'PathFinder - quick search, selection, removal of splines',
'warning': '', # used for warning icon and text in addons panel
@@ -17,7 +17,6 @@ import time
import threading
import gpu
-import bgl
from gpu_extras.batch import batch_for_shader
import bpy
@@ -85,8 +84,8 @@ def draw_bezier_points(self, context, spline, matrix_world, path_color, path_thi
shader.bind()
shader.uniform_float("color", path_color)
- bgl.glEnable(bgl.GL_BLEND)
- bgl.glLineWidth(path_thickness)
+ gpu.state.blend_set('ALPHA')
+ gpu.state.line_width_set(path_thickness)
batch.draw(shader)
def draw_points(self, context, spline, matrix_world, path_color, path_thickness):
@@ -98,8 +97,8 @@ def draw_points(self, context, spline, matrix_world, path_color, path_thickness)
shader.bind()
shader.uniform_float("color", path_color)
- bgl.glEnable(bgl.GL_BLEND)
- bgl.glLineWidth(path_thickness)
+ gpu.state.blend_set('ALPHA')
+ gpu.state.line_width_set(path_thickness)
batch.draw(shader)
def near(location3D, point, radius):
diff --git a/curve_tools/show_resolution.py b/curve_tools/show_resolution.py
index b2dbda7f..07a06540 100644
--- a/curve_tools/show_resolution.py
+++ b/curve_tools/show_resolution.py
@@ -6,7 +6,6 @@ import bpy
from bpy import *
from bpy.props import *
-import bgl
import blf
import gpu
from gpu_extras.batch import batch_for_shader
diff --git a/curve_tools/splines_sequence.py b/curve_tools/splines_sequence.py
index 98766058..89b9624c 100644
--- a/curve_tools/splines_sequence.py
+++ b/curve_tools/splines_sequence.py
@@ -3,7 +3,6 @@
import bpy
-import bgl
import blf
import gpu
from gpu_extras.batch import batch_for_shader
@@ -66,7 +65,7 @@ def draw(self, context, splines, sequence_color, font_thickness, font_size, matr
batch = batch_for_shader(shader, 'LINES', {"pos": points})
shader.bind()
- bgl.glLineWidth(font_thickness)
+ gpu.state.line_width_set(font_thickness)
shader.uniform_float("color", sequence_color)
batch.draw(shader)
i += font_size + font_size * 0.5
@@ -215,6 +214,7 @@ class RearrangeSpline(bpy.types.Operator):
bl_idname = "curvetools.rearrange_spline"
bl_label = "Rearrange Spline"
bl_description = "Rearrange Spline"
+ bl_options = {'UNDO'}
Types = [('NEXT', "Next", "next"),
('PREV', "Prev", "prev")]
diff --git a/depsgraph_debug.py b/depsgraph_debug.py
index 4405eb8e..7c8784a2 100644
--- a/depsgraph_debug.py
+++ b/depsgraph_debug.py
@@ -211,6 +211,35 @@ class SCENE_OT_depsgraph_stats_image(Operator,
return True
+class SCENE_OT_depsgraph_relations_svg(Operator,
+ SCENE_OT_depsgraph_image_common):
+ bl_idname = "scene.depsgraph_relations_svg"
+ bl_label = "Depsgraph as SVG in Browser"
+ bl_description = "Create an SVG image from the dependency graph and open it in the web browser"
+
+ def performSave(self, context, depsgraph):
+ import os
+ import subprocess
+ import webbrowser
+ # Create temporary file.
+ dot_filepath = self._createTempFile(suffix=".dot")
+ # Save dependency graph to graphviz file.
+ depsgraph.debug_relations_graphviz(dot_filepath)
+ # Convert graphviz to SVG image.
+ svg_filepath = os.path.join(bpy.app.tempdir, "depsgraph.svg")
+ command = ("dot", "-Tsvg", dot_filepath, "-o", svg_filepath)
+ try:
+ subprocess.run(command)
+ webbrowser.open_new_tab("file://" + os.path.abspath(svg_filepath))
+ except:
+ self.report({'ERROR'}, "Error invoking dot command")
+ return False
+ finally:
+ # Remove graphviz file.
+ os.remove(dot_filepath)
+ return True
+
+
###############################################################################
# Interface.
@@ -224,6 +253,7 @@ class SCENE_PT_depsgraph_common:
row = col.row()
row.operator("scene.depsgraph_relations_graphviz")
row.operator("scene.depsgraph_relations_image")
+ col.operator("scene.depsgraph_relations_svg")
# Everything related on evaluaiton statistics.
col.label(text="Statistics:")
row = col.row()
@@ -264,6 +294,7 @@ class RENDERLAYER_PT_depsgraph(bpy.types.Panel, SCENE_PT_depsgraph_common):
def register():
bpy.utils.register_class(SCENE_OT_depsgraph_relations_graphviz)
bpy.utils.register_class(SCENE_OT_depsgraph_relations_image)
+ bpy.utils.register_class(SCENE_OT_depsgraph_relations_svg)
bpy.utils.register_class(SCENE_OT_depsgraph_stats_gnuplot)
bpy.utils.register_class(SCENE_OT_depsgraph_stats_image)
bpy.utils.register_class(SCENE_PT_depsgraph)
@@ -273,6 +304,7 @@ def register():
def unregister():
bpy.utils.unregister_class(SCENE_OT_depsgraph_relations_graphviz)
bpy.utils.unregister_class(SCENE_OT_depsgraph_relations_image)
+ bpy.utils.unregister_class(SCENE_OT_depsgraph_relations_svg)
bpy.utils.unregister_class(SCENE_OT_depsgraph_stats_gnuplot)
bpy.utils.unregister_class(SCENE_OT_depsgraph_stats_image)
bpy.utils.unregister_class(SCENE_PT_depsgraph)
diff --git a/greasepencil_tools/__init__.py b/greasepencil_tools/__init__.py
index 980493ed..c75d0442 100644
--- a/greasepencil_tools/__init__.py
+++ b/greasepencil_tools/__init__.py
@@ -4,8 +4,8 @@ bl_info = {
"name": "Grease Pencil Tools",
"description": "Extra tools for Grease Pencil",
"author": "Samuel Bernou, Antonio Vazquez, Daniel Martinez Lara, Matias Mendiola",
-"version": (1, 6, 0),
-"blender": (2, 91, 0),
+"version": (1, 6, 1),
+"blender": (3, 0, 0),
"location": "Sidebar > Grease Pencil > Grease Pencil Tools",
"warning": "",
"doc_url": "{BLENDER_MANUAL_URL}/addons/object/greasepencil_tools.html",
diff --git a/greasepencil_tools/rotate_canvas.py b/greasepencil_tools/rotate_canvas.py
index a733eb2d..c2482fbd 100644
--- a/greasepencil_tools/rotate_canvas.py
+++ b/greasepencil_tools/rotate_canvas.py
@@ -10,7 +10,6 @@ from bpy.props import BoolProperty, EnumProperty
from time import time
## draw utils
import gpu
-import bgl
import blf
from gpu_extras.batch import batch_for_shader
from gpu_extras.presets import draw_circle_2d
@@ -31,8 +30,8 @@ def draw_callback_px(self, context):
if context.area != self.current_area:
return
shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
- bgl.glEnable(bgl.GL_BLEND)
- bgl.glLineWidth(2)
+ gpu.state.blend_set('ALPHA')
+ gpu.state.line_width_set(2.0)
# init
batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": [self.center, self.initial_pos]})#self.vector_initial
@@ -55,8 +54,8 @@ def draw_callback_px(self, context):
# batch.draw(shader)
# restore opengl defaults
- bgl.glLineWidth(1)
- bgl.glDisable(bgl.GL_BLEND)
+ gpu.state.line_width_set(1.0)
+ gpu.state.blend_set('NONE')
## text
font_id = 0
diff --git a/greasepencil_tools/timeline_scrub.py b/greasepencil_tools/timeline_scrub.py
index 6a1913ae..1f6c2a41 100644
--- a/greasepencil_tools/timeline_scrub.py
+++ b/greasepencil_tools/timeline_scrub.py
@@ -8,7 +8,6 @@ import numpy as np
from time import time
import bpy
import gpu
-import bgl
import blf
from gpu_extras.batch import batch_for_shader
@@ -39,8 +38,8 @@ def draw_callback_px(self, context):
font_id = 0
shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR') # initiate shader
- bgl.glEnable(bgl.GL_BLEND)
- bgl.glLineWidth(1)
+ gpu.state.blend_set('ALPHA')
+ gpu.state.line_width_set(1.0)
# Draw HUD
if self.use_hud_time_line:
@@ -51,18 +50,18 @@ def draw_callback_px(self, context):
# Display keyframes
if self.use_hud_keyframes:
if self.keyframe_aspect == 'LINE':
- bgl.glLineWidth(3)
+ gpu.state.line_width_set(3.0)
shader.bind()
shader.uniform_float("color", self.color_timeline)
self.batch_keyframes.draw(shader)
else:
- bgl.glLineWidth(1)
+ gpu.state.line_width_set(1.0)
shader.bind()
shader.uniform_float("color", self.color_timeline)
self.batch_keyframes.draw(shader)
# Show current frame line
- bgl.glLineWidth(1)
+ gpu.state.line_width_set(1.0)
if self.use_hud_playhead:
playhead = [(self.cursor_x, self.my + self.playhead_size/2),
(self.cursor_x, self.my - self.playhead_size/2)]
@@ -72,7 +71,7 @@ def draw_callback_px(self, context):
batch.draw(shader)
# restore opengl defaults
- bgl.glDisable(bgl.GL_BLEND)
+ gpu.state.blend_set('NONE')
# Display current frame text
blf.color(font_id, *self.color_text)
diff --git a/io_import_images_as_planes.py b/io_import_images_as_planes.py
index 50a56238..ef63ec52 100644
--- a/io_import_images_as_planes.py
+++ b/io_import_images_as_planes.py
@@ -2,8 +2,8 @@
bl_info = {
"name": "Import Images as Planes",
- "author": "Florian Meyer (tstscr), mont29, matali, Ted Schundler (SpkyElctrc)",
- "version": (3, 4, 0),
+ "author": "Florian Meyer (tstscr), mont29, matali, Ted Schundler (SpkyElctrc), mrbimax",
+ "version": (3, 5, 0),
"blender": (2, 91, 0),
"location": "File > Import > Images as Planes or Add > Mesh > Images as Planes",
"description": "Imports images and creates planes with the appropriate aspect ratio. "
@@ -718,6 +718,34 @@ class IMPORT_IMAGE_OT_to_plane(Operator, AddObjectHelper):
name="Strength", min=0.0, default=1.0, soft_max=10.0,
step=100, description="Brightness of Emission Texture")
+ use_transparency: BoolProperty(
+ name="Use Alpha", default=True,
+ description="Use alpha channel for transparency")
+
+ BLEND_METHODS = (
+ ('BLEND',"Blend","Render polygon transparent, depending on alpha channel of the texture"),
+ ('CLIP', "Clip","Use the alpha threshold to clip the visibility (binary visibility)"),
+ ('HASHED', "Hashed","Use noise to dither the binary visibility (works well with multi-samples)"),
+ ('OPAQUE', "Opaque","Render surface without transparency"),
+ )
+ blend_method: EnumProperty(name="Blend Mode", items=BLEND_METHODS, default='BLEND', description="Blend Mode for Transparent Faces")
+
+ SHADOW_METHODS = (
+ ('CLIP', "Clip","Use the alpha threshold to clip the visibility (binary visibility)"),
+ ('HASHED', "Hashed","Use noise to dither the binary visibility (works well with multi-samples)"),
+ ('OPAQUE',"Opaque","Material will cast shadows without transparency"),
+ ('NONE',"None","Material will cast no shadow"),
+ )
+ shadow_method: EnumProperty(name="Shadow Mode", items=SHADOW_METHODS, default='CLIP', description="Shadow mapping method")
+
+ use_backface_culling: BoolProperty(
+ name="Backface Culling", default=False,
+ description="Use back face culling to hide the back side of faces")
+
+ show_transparent_back: BoolProperty(
+ name="Show Backface", default=True,
+ description="Render multiple transparent layers (may introduce transparency sorting problems)")
+
overwrite_material: BoolProperty(
name="Overwrite Material", default=True,
description="Overwrite existing Material (based on material name)")
@@ -729,9 +757,20 @@ class IMPORT_IMAGE_OT_to_plane(Operator, AddObjectHelper):
# ------------------
# Properties - Image
- use_transparency: BoolProperty(
- name="Use Alpha", default=True,
- description="Use alpha channel for transparency")
+ INTERPOLATION_MODES = (
+ ('Linear', "Linear", "Linear interpolation"),
+ ('Closest', "Closest", "No interpolation (sample closest texel)"),
+ ('Cubic', "Cubic", "Cubic interpolation"),
+ ('Smart', "Smart", "Bicubic when magnifying, else bilinear (OSL only)"),
+ )
+ interpolation: EnumProperty(name="Interpolation", items=INTERPOLATION_MODES, default='Linear', description="Texture interpolation")
+
+ EXTENSION_MODES = (
+ ('CLIP', "Clip", "Clip to image size and set exterior pixels as transparent"),
+ ('EXTEND', "Extend", "Extend by repeating edge pixels of the image"),
+ ('REPEAT', "Repeat", "Cause the image to repeat horizontally and vertically"),
+ )
+ extension: EnumProperty(name="Extension", items=EXTENSION_MODES, default='CLIP', description="How the image is extrapolated past its original bounds")
t = bpy.types.Image.bl_rna.properties["alpha_mode"]
alpha_mode_items = tuple((e.identifier, e.name, e.description) for e in t.enum_items)
@@ -766,27 +805,53 @@ class IMPORT_IMAGE_OT_to_plane(Operator, AddObjectHelper):
box.label(text="Compositing Nodes:", icon='RENDERLAYERS')
box.prop(self, "compositing_nodes")
-
+ layout = self.layout
+ box = layout.box()
box.label(text="Material Settings:", icon='MATERIAL')
+ box.label(text="Material Type")
row = box.row()
row.prop(self, 'shader', expand=True)
if self.shader == 'EMISSION':
box.prop(self, "emit_strength")
+ box.label(text="Blend Mode")
+ row = box.row()
+ row.prop(self, 'blend_method', expand=True)
+ if self.use_transparency and self.alpha_mode != "NONE" and self.blend_method == "OPAQUE":
+ box.label(text="'Opaque' does not support alpha", icon="ERROR")
+ if self.blend_method == 'BLEND':
+ row = box.row()
+ row.prop(self, "show_transparent_back")
+
+ box.label(text="Shadow Mode")
+ row = box.row()
+ row.prop(self, 'shadow_method', expand=True)
+
+ row = box.row()
+ row.prop(self, "use_backface_culling")
+
engine = context.scene.render.engine
if engine not in ('CYCLES', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'):
box.label(text="%s is not supported" % engine, icon='ERROR')
box.prop(self, "overwrite_material")
-
+ layout = self.layout
+ box = layout.box()
box.label(text="Texture Settings:", icon='TEXTURE')
+ box.label(text="Interpolation")
+ row = box.row()
+ row.prop(self, 'interpolation', expand=True)
+ box.label(text="Extension")
+ row = box.row()
+ row.prop(self, 'extension', expand=True)
row = box.row()
row.prop(self, "use_transparency")
- sub = row.row()
- sub.active = self.use_transparency
- sub.prop(self, "alpha_mode", text="")
- box.prop(self, "use_auto_refresh")
+ if self.use_transparency:
+ sub = row.row()
+ sub.prop(self, "alpha_mode", text="")
+ row = box.row()
+ row.prop(self, "use_auto_refresh")
def draw_spatial_config(self, context):
# --- Spatial Properties: Position, Size and Orientation --- #
@@ -970,6 +1035,8 @@ class IMPORT_IMAGE_OT_to_plane(Operator, AddObjectHelper):
tex_image = node_tree.nodes.new('ShaderNodeTexImage')
tex_image.image = img_spec.image
tex_image.show_texture = True
+ tex_image.interpolation = self.interpolation
+ tex_image.extension = self.extension
self.apply_texture_options(tex_image, img_spec)
return tex_image
@@ -985,8 +1052,13 @@ class IMPORT_IMAGE_OT_to_plane(Operator, AddObjectHelper):
material = bpy.data.materials.new(name=name_compat)
material.use_nodes = True
- if self.use_transparency:
- material.blend_method = 'BLEND'
+
+ material.blend_method = self.blend_method
+ material.shadow_method = self.shadow_method
+
+ material.use_backface_culling = self.use_backface_culling
+ material.show_transparent_back = self.show_transparent_back
+
node_tree = material.node_tree
out_node = clean_node_tree(node_tree)
diff --git a/io_mesh_stl/blender_utils.py b/io_mesh_stl/blender_utils.py
index fdf353df..5c84152e 100644
--- a/io_mesh_stl/blender_utils.py
+++ b/io_mesh_stl/blender_utils.py
@@ -48,7 +48,7 @@ def faces_from_mesh(ob, global_matrix, use_mesh_modifiers=False):
"""
From an object, return a generator over a list of faces.
- Each faces is a list of his vertexes. Each vertex is a tuple of
+ Each faces is a list of his vertices. Each vertex is a tuple of
his coordinate.
use_mesh_modifiers
diff --git a/io_mesh_stl/stl_utils.py b/io_mesh_stl/stl_utils.py
index 465806ab..e041c527 100644
--- a/io_mesh_stl/stl_utils.py
+++ b/io_mesh_stl/stl_utils.py
@@ -168,7 +168,7 @@ def _binary_write(filepath, faces):
for face in faces:
# calculate face normal
- # write normal + vertexes + pad as attributes
+ # write normal + vertices + pad as attributes
fw(struct.pack('<3f', *normal(*face)) + pack(*itertools.chain.from_iterable(face)))
# attribute byte count (unused)
fw(b'\0\0')
diff --git a/io_mesh_uv_layout/__init__.py b/io_mesh_uv_layout/__init__.py
index 324f2254..40d9b501 100644
--- a/io_mesh_uv_layout/__init__.py
+++ b/io_mesh_uv_layout/__init__.py
@@ -3,8 +3,8 @@
bl_info = {
"name": "UV Layout",
"author": "Campbell Barton, Matt Ebb",
- "version": (1, 1, 1),
- "blender": (2, 80, 0),
+ "version": (1, 1, 3),
+ "blender": (3, 0, 0),
"location": "Image-Window > UVs > Export UV Layout",
"description": "Export the UV layout as a 2D graphic",
"warning": "",
@@ -128,10 +128,10 @@ class ExportUVLayout(bpy.types.Operator):
polygon_data = list(self.iter_polygon_data_to_draw(context, meshes))
different_colors = set(color for _, color in polygon_data)
if self.modified:
- depsgraph = context.evaluated_depsgraph_get()
- for obj in self.iter_objects_to_export(context):
- obj_eval = obj.evaluated_get(depsgraph)
- obj_eval.to_mesh_clear()
+ depsgraph = context.evaluated_depsgraph_get()
+ for obj in self.iter_objects_to_export(context):
+ obj_eval = obj.evaluated_get(depsgraph)
+ obj_eval.to_mesh_clear()
export = self.get_exporter()
export(filepath, polygon_data, different_colors, self.size[0], self.size[1], self.opacity)
diff --git a/io_mesh_uv_layout/export_uv_eps.py b/io_mesh_uv_layout/export_uv_eps.py
index 04b8a38e..9e013e13 100644
--- a/io_mesh_uv_layout/export_uv_eps.py
+++ b/io_mesh_uv_layout/export_uv_eps.py
@@ -8,6 +8,7 @@ def export(filepath, face_data, colors, width, height, opacity):
for text in get_file_parts(face_data, colors, width, height, opacity):
file.write(text)
+
def get_file_parts(face_data, colors, width, height, opacity):
yield from header(width, height)
if opacity > 0.0:
@@ -35,6 +36,7 @@ def header(width, height):
yield "1 setlinejoin\n"
yield "1 setlinecap\n"
+
def prepare_colors(colors, out_name_by_color):
for i, color in enumerate(colors):
name = f"COLOR_{i}"
@@ -48,18 +50,21 @@ def prepare_colors(colors, out_name_by_color):
yield "0 setgray\n"
yield "} def\n"
+
def draw_colored_polygons(face_data, name_by_color, width, height):
for uvs, color in face_data:
yield from draw_polygon_path(uvs, width, height)
yield "closepath\n"
yield "%s\n" % name_by_color[color]
+
def draw_lines(face_data, width, height):
for uvs, _ in face_data:
yield from draw_polygon_path(uvs, width, height)
yield "closepath\n"
yield "stroke\n"
+
def draw_polygon_path(uvs, width, height):
yield "newpath\n"
for j, uv in enumerate(uvs):
@@ -69,6 +74,7 @@ def draw_polygon_path(uvs, width, height):
else:
yield "%.5f %.5f lineto\n" % uv_scale
+
def footer():
yield "showpage\n"
yield "%%EOF\n"
diff --git a/io_mesh_uv_layout/export_uv_png.py b/io_mesh_uv_layout/export_uv_png.py
index 958bac8e..b051c980 100644
--- a/io_mesh_uv_layout/export_uv_png.py
+++ b/io_mesh_uv_layout/export_uv_png.py
@@ -2,31 +2,30 @@
import bpy
import gpu
-import bgl
from mathutils import Vector, Matrix
from mathutils.geometry import tessellate_polygon
from gpu_extras.batch import batch_for_shader
+
def export(filepath, face_data, colors, width, height, opacity):
offscreen = gpu.types.GPUOffScreen(width, height)
offscreen.bind()
try:
- bgl.glClearColor(0.0, 0.0, 0.0, 0.0)
- bgl.glClear(bgl.GL_COLOR_BUFFER_BIT)
+ fb = gpu.state.active_framebuffer_get()
+ fb.clear(color=(0.0, 0.0, 0.0, 0.0))
draw_image(face_data, opacity)
- pixel_data = get_pixel_data_from_current_back_buffer(width, height)
+ pixel_data = fb.read_color(0, 0, width, height, 4, 0, 'UBYTE')
+ pixel_data.dimensions = width * height * 4
save_pixels(filepath, pixel_data, width, height)
finally:
offscreen.unbind()
offscreen.free()
+
def draw_image(face_data, opacity):
- bgl.glLineWidth(1)
- bgl.glEnable(bgl.GL_BLEND)
- bgl.glEnable(bgl.GL_LINE_SMOOTH)
- bgl.glHint(bgl.GL_LINE_SMOOTH_HINT, bgl.GL_NICEST)
+ gpu.state.blend_set('ALPHA_PREMULT')
with gpu.matrix.push_pop():
gpu.matrix.load_matrix(get_normalize_uvs_matrix())
@@ -35,8 +34,8 @@ def draw_image(face_data, opacity):
draw_background_colors(face_data, opacity)
draw_lines(face_data)
- bgl.glDisable(bgl.GL_BLEND)
- bgl.glDisable(bgl.GL_LINE_SMOOTH)
+ gpu.state.blend_set('NONE')
+
def get_normalize_uvs_matrix():
'''matrix maps x and y coordinates from [0, 1] to [-1, 1]'''
@@ -47,6 +46,7 @@ def get_normalize_uvs_matrix():
matrix[1][1] = 2
return matrix
+
def draw_background_colors(face_data, opacity):
coords = [uv for uvs, _ in face_data for uv in uvs]
colors = [(*color, opacity) for uvs, color in face_data for _ in range(len(uvs))]
@@ -59,35 +59,38 @@ def draw_background_colors(face_data, opacity):
offset += len(uvs)
shader = gpu.shader.from_builtin('2D_FLAT_COLOR')
- batch = batch_for_shader(shader, 'TRIS',
- {"pos" : coords,
- "color" : colors},
- indices=indices)
+ batch = batch_for_shader(
+ shader, 'TRIS',
+ {"pos": coords, "color": colors},
+ indices=indices,
+ )
batch.draw(shader)
+
def tessellate_uvs(uvs):
return tessellate_polygon([uvs])
+
def draw_lines(face_data):
coords = []
for uvs, _ in face_data:
for i in range(len(uvs)):
start = uvs[i]
- end = uvs[(i+1) % len(uvs)]
+ end = uvs[(i + 1) % len(uvs)]
coords.append((start[0], start[1]))
coords.append((end[0], end[1]))
- shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
- batch = batch_for_shader(shader, 'LINES', {"pos" : coords})
+ # Use '2D_UNIFORM_COLOR' in the `batch_for_shader` so we don't need to
+ # convert the coordinates to 3D as in the case of
+ # '3D_POLYLINE_UNIFORM_COLOR'.
+ batch = batch_for_shader(gpu.shader.from_builtin('2D_UNIFORM_COLOR'), 'LINES', {"pos": coords})
+ shader = gpu.shader.from_builtin('3D_POLYLINE_UNIFORM_COLOR')
shader.bind()
+ shader.uniform_float("viewportSize", gpu.state.viewport_get()[2:])
+ shader.uniform_float("lineWidth", 0.5)
shader.uniform_float("color", (0, 0, 0, 1))
batch.draw(shader)
-def get_pixel_data_from_current_back_buffer(width, height):
- buffer = bgl.Buffer(bgl.GL_BYTE, width * height * 4)
- bgl.glReadBuffer(bgl.GL_BACK)
- bgl.glReadPixels(0, 0, width, height, bgl.GL_RGBA, bgl.GL_UNSIGNED_BYTE, buffer)
- return buffer
def save_pixels(filepath, pixel_data, width, height):
image = bpy.data.images.new("temp", width, height, alpha=True)
diff --git a/io_mesh_uv_layout/export_uv_svg.py b/io_mesh_uv_layout/export_uv_svg.py
index d8e2b5a4..a4b6a3c3 100644
--- a/io_mesh_uv_layout/export_uv_svg.py
+++ b/io_mesh_uv_layout/export_uv_svg.py
@@ -4,16 +4,19 @@ import bpy
from os.path import basename
from xml.sax.saxutils import escape
+
def export(filepath, face_data, colors, width, height, opacity):
with open(filepath, 'w', encoding='utf-8') as file:
for text in get_file_parts(face_data, colors, width, height, opacity):
file.write(text)
+
def get_file_parts(face_data, colors, width, height, opacity):
yield from header(width, height)
yield from draw_polygons(face_data, width, height, opacity)
yield from footer()
+
def header(width, height):
yield '<?xml version="1.0" standalone="no"?>\n'
yield '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" \n'
@@ -23,6 +26,7 @@ def header(width, height):
desc = f"{basename(bpy.data.filepath)}, (Blender {bpy.app.version_string})"
yield f'<desc>{escape(desc)}</desc>\n'
+
def draw_polygons(face_data, width, height, opacity):
for uvs, color in face_data:
fill = f'fill="{get_color_string(color)}"'
@@ -37,10 +41,12 @@ def draw_polygons(face_data, width, height, opacity):
yield f'{x*width:.3f},{y*height:.3f} '
yield '" />\n'
+
def get_color_string(color):
r, g, b = color
return f"rgb({round(r*255)}, {round(g*255)}, {round(b*255)})"
+
def footer():
yield '\n'
yield '</svg>\n'
diff --git a/io_scene_fbx/__init__.py b/io_scene_fbx/__init__.py
index 1b7e646d..feea8436 100644
--- a/io_scene_fbx/__init__.py
+++ b/io_scene_fbx/__init__.py
@@ -3,7 +3,7 @@
bl_info = {
"name": "FBX format",
"author": "Campbell Barton, Bastien Montagne, Jens Restemeier",
- "version": (4, 36, 2),
+ "version": (4, 36, 3),
"blender": (3, 2, 0),
"location": "File > Import-Export",
"description": "FBX IO meshes, UV's, vertex colors, materials, textures, cameras, lamps and actions",
diff --git a/io_scene_fbx/export_fbx_bin.py b/io_scene_fbx/export_fbx_bin.py
index ae7be61c..f560b19c 100644
--- a/io_scene_fbx/export_fbx_bin.py
+++ b/io_scene_fbx/export_fbx_bin.py
@@ -865,9 +865,9 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
if last_subsurf:
elem_data_single_int32(geom, b"Smoothness", 2) # Display control mesh and smoothed
if last_subsurf.boundary_smooth == "PRESERVE_CORNERS":
- elem_data_single_int32(geom, b"BoundaryRule", 2) # CreaseAll
+ elem_data_single_int32(geom, b"BoundaryRule", 1) # CreaseAll
else:
- elem_data_single_int32(geom, b"BoundaryRule", 1) # CreaseEdge
+ elem_data_single_int32(geom, b"BoundaryRule", 2) # CreaseEdge
elem_data_single_int32(geom, b"PreviewDivisionLevels", last_subsurf.levels)
elem_data_single_int32(geom, b"RenderDivisionLevels", last_subsurf.render_levels)
@@ -1528,7 +1528,7 @@ def fbx_data_armature_elements(root, arm_obj, scene_data):
elem_data_single_int32(fbx_skin, b"Version", FBX_DEFORMER_SKIN_VERSION)
elem_data_single_float64(fbx_skin, b"Link_DeformAcuracy", 50.0) # Only vague idea what it is...
- # Pre-process vertex weights (also to check vertices assigned ot more than four bones).
+ # Pre-process vertex weights (also to check vertices assigned to more than four bones).
ob = ob_obj.bdata
bo_vg_idx = {bo_obj.bdata.name: ob.vertex_groups[bo_obj.bdata.name].index
for bo_obj in clusters.keys() if bo_obj.bdata.name in ob.vertex_groups}
diff --git a/io_scene_fbx/import_fbx.py b/io_scene_fbx/import_fbx.py
index 90f0c016..72d666c1 100644
--- a/io_scene_fbx/import_fbx.py
+++ b/io_scene_fbx/import_fbx.py
@@ -2929,7 +2929,7 @@ def load(operator, context, filepath="",
mod.levels = preview_levels
mod.render_levels = render_levels
boundary_rule = elem_prop_first(elem_find_first(fbx_sdata, b'BoundaryRule'), default=1)
- if boundary_rule == 2:
+ if boundary_rule == 1:
mod.boundary_smooth = "PRESERVE_CORNERS"
else:
mod.boundary_smooth = "ALL"
diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py
index ce48f293..459a8795 100755
--- a/io_scene_gltf2/__init__.py
+++ b/io_scene_gltf2/__init__.py
@@ -4,7 +4,7 @@
bl_info = {
'name': 'glTF 2.0 format',
'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors',
- "version": (3, 3, 5),
+ "version": (3, 4, 10),
'blender': (3, 3, 0),
'location': 'File > Import-Export',
'description': 'Import-Export as glTF 2.0',
@@ -259,6 +259,14 @@ class ExportGLTF2_Base:
default='EXPORT'
)
+ export_original_specular: BoolProperty(
+ name='Export original PBR Specular',
+ description=(
+ 'Export original glTF PBR Specular, instead of Blender Principled Shader Specular'
+ ),
+ default=False,
+ )
+
export_colors: BoolProperty(
name='Vertex Colors',
description='Export vertex colors with meshes',
@@ -372,9 +380,17 @@ class ExportGLTF2_Base:
default=True
)
+ export_nla_strips_merged_animation_name: StringProperty(
+ name='Merged Animation Name',
+ description=(
+ "Name of single glTF animation to be exported"
+ ),
+ default='Animation'
+ )
+
export_def_bones: BoolProperty(
name='Export Deformation Bones Only',
- description='Export Deformation bones only (and needed bones for hierarchy)',
+ description='Export Deformation bones only',
default=False
)
@@ -387,6 +403,15 @@ class ExportGLTF2_Base:
default=False
)
+ export_anim_single_armature: BoolProperty(
+ name='Export all Armature Actions',
+ description=(
+ "Export all actions, bound to a single armature. "
+ "WARNING: Option does not support exports including multiple armatures"
+ ),
+ default=True
+ )
+
export_current_frame: BoolProperty(
name='Use Current Frame',
description='Export the scene in the current animation frame',
@@ -430,13 +455,6 @@ class ExportGLTF2_Base:
default=False
)
- export_displacement: BoolProperty(
- name='Displacement Textures (EXPERIMENTAL)',
- description='EXPERIMENTAL: Export displacement textures. '
- 'Uses incomplete "KHR_materials_displacement" glTF extension',
- default=False
- )
-
will_save_settings: BoolProperty(
name='Remember Export Settings',
description='Store glTF export settings in the Blender project',
@@ -547,6 +565,7 @@ class ExportGLTF2_Base:
export_settings['gltf_colors'] = self.export_colors
export_settings['gltf_cameras'] = self.export_cameras
+ export_settings['gltf_original_specular'] = self.export_original_specular
export_settings['gltf_visible'] = self.use_visible
export_settings['gltf_renderable'] = self.use_renderable
@@ -560,26 +579,28 @@ class ExportGLTF2_Base:
export_settings['gltf_apply'] = self.export_apply
export_settings['gltf_current_frame'] = self.export_current_frame
export_settings['gltf_animations'] = self.export_animations
+ export_settings['gltf_def_bones'] = self.export_def_bones
if self.export_animations:
export_settings['gltf_frame_range'] = self.export_frame_range
export_settings['gltf_force_sampling'] = self.export_force_sampling
- if self.export_force_sampling:
- export_settings['gltf_def_bones'] = self.export_def_bones
- else:
+ if not self.export_force_sampling:
export_settings['gltf_def_bones'] = False
export_settings['gltf_nla_strips'] = self.export_nla_strips
+ export_settings['gltf_nla_strips_merged_animation_name'] = self.export_nla_strips_merged_animation_name
export_settings['gltf_optimize_animation'] = self.optimize_animation_size
+ export_settings['gltf_export_anim_single_armature'] = self.export_anim_single_armature
else:
export_settings['gltf_frame_range'] = False
export_settings['gltf_move_keyframes'] = False
export_settings['gltf_force_sampling'] = False
- export_settings['gltf_def_bones'] = False
export_settings['gltf_optimize_animation'] = False
+ export_settings['gltf_export_anim_single_armature'] = False
export_settings['gltf_skins'] = self.export_skins
if self.export_skins:
export_settings['gltf_all_vertex_influences'] = self.export_all_influences
else:
export_settings['gltf_all_vertex_influences'] = False
+ export_settings['gltf_def_bones'] = False
export_settings['gltf_frame_step'] = self.export_frame_step
export_settings['gltf_morph'] = self.export_morph
if self.export_morph:
@@ -592,7 +613,6 @@ class ExportGLTF2_Base:
export_settings['gltf_morph_tangent'] = False
export_settings['gltf_lights'] = self.export_lights
- export_settings['gltf_displacement'] = self.export_displacement
export_settings['gltf_binary'] = bytearray()
export_settings['gltf_binaryfilename'] = (
@@ -738,6 +758,22 @@ class GLTF_PT_export_geometry(bpy.types.Panel):
return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
def draw(self, context):
+ pass
+
+class GLTF_PT_export_geometry_mesh(bpy.types.Panel):
+ bl_space_type = 'FILE_BROWSER'
+ bl_region_type = 'TOOL_PROPS'
+ bl_label = "Mesh"
+ bl_parent_id = "GLTF_PT_export_geometry"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ sfile = context.space_data
+ operator = sfile.active_operator
+ return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
+
+ def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
@@ -757,11 +793,56 @@ class GLTF_PT_export_geometry(bpy.types.Panel):
col.prop(operator, 'use_mesh_edges')
col.prop(operator, 'use_mesh_vertices')
+
+class GLTF_PT_export_geometry_material(bpy.types.Panel):
+ bl_space_type = 'FILE_BROWSER'
+ bl_region_type = 'TOOL_PROPS'
+ bl_label = "Material"
+ bl_parent_id = "GLTF_PT_export_geometry"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ sfile = context.space_data
+ operator = sfile.active_operator
+ return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+ sfile = context.space_data
+ operator = sfile.active_operator
+
layout.prop(operator, 'export_materials')
col = layout.column()
col.active = operator.export_materials == "EXPORT"
col.prop(operator, 'export_image_format')
+class GLTF_PT_export_geometry_original_pbr(bpy.types.Panel):
+ bl_space_type = 'FILE_BROWSER'
+ bl_region_type = 'TOOL_PROPS'
+ bl_label = "PBR Extensions"
+ bl_parent_id = "GLTF_PT_export_geometry_material"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ sfile = context.space_data
+ operator = sfile.active_operator
+ return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+ sfile = context.space_data
+ operator = sfile.active_operator
+
+ layout.prop(operator, 'export_original_specular')
+
class GLTF_PT_export_geometry_compression(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
@@ -863,13 +944,10 @@ class GLTF_PT_export_animation_export(bpy.types.Panel):
layout.prop(operator, 'export_frame_step')
layout.prop(operator, 'export_force_sampling')
layout.prop(operator, 'export_nla_strips')
+ if operator.export_nla_strips is False:
+ layout.prop(operator, 'export_nla_strips_merged_animation_name')
layout.prop(operator, 'optimize_animation_size')
-
- row = layout.row()
- row.active = operator.export_force_sampling
- row.prop(operator, 'export_def_bones')
- if operator.export_force_sampling is False and operator.export_def_bones is True:
- layout.label(text="Export only deformation bones is not possible when not sampling animation")
+ layout.prop(operator, 'export_anim_single_armature')
class GLTF_PT_export_animation_shapekeys(bpy.types.Panel):
@@ -937,6 +1015,12 @@ class GLTF_PT_export_animation_skinning(bpy.types.Panel):
layout.active = operator.export_skins
layout.prop(operator, 'export_all_influences')
+ row = layout.row()
+ row.active = operator.export_force_sampling
+ row.prop(operator, 'export_def_bones')
+ if operator.export_force_sampling is False and operator.export_def_bones is True:
+ layout.label(text="Export only deformation bones is not possible when not sampling animation")
+
class GLTF_PT_export_user_extensions(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
@@ -1160,19 +1244,34 @@ class ImportGLTF2(Operator, ImportHelper):
self.loglevel = logging.NOTSET
+def gltf_variant_ui_update(self, context):
+ from .blender.com.gltf2_blender_ui import variant_register, variant_unregister
+ if self.KHR_materials_variants_ui is True:
+ # register all needed types
+ variant_register()
+ else:
+ variant_unregister()
+
class GLTF_AddonPreferences(bpy.types.AddonPreferences):
bl_idname = __package__
settings_node_ui : bpy.props.BoolProperty(
default= False,
- description="Displays glTF Settings node in Shader Editor (Menu Add > Ouput)"
+ description="Displays glTF Material Output node in Shader Editor (Menu Add > Output)"
)
+ KHR_materials_variants_ui : bpy.props.BoolProperty(
+ default= False,
+ description="Displays glTF UI to manage material variants",
+ update=gltf_variant_ui_update
+ )
+
def draw(self, context):
layout = self.layout
row = layout.row()
row.prop(self, "settings_node_ui", text="Shader Editor Add-ons")
+ row.prop(self, "KHR_materials_variants_ui", text="Material Variants")
def menu_func_import(self, context):
self.layout.operator(ImportGLTF2.bl_idname, text='glTF 2.0 (.glb/.gltf)')
@@ -1184,6 +1283,9 @@ classes = (
GLTF_PT_export_include,
GLTF_PT_export_transform,
GLTF_PT_export_geometry,
+ GLTF_PT_export_geometry_mesh,
+ GLTF_PT_export_geometry_material,
+ GLTF_PT_export_geometry_original_pbr,
GLTF_PT_export_geometry_compression,
GLTF_PT_export_animation,
GLTF_PT_export_animation_export,
@@ -1203,6 +1305,8 @@ def register():
# bpy.utils.register_module(__name__)
blender_ui.register()
+ if bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is True:
+ blender_ui.variant_register()
# add to the export / import menu
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
@@ -1211,6 +1315,10 @@ def register():
def unregister():
import io_scene_gltf2.blender.com.gltf2_blender_ui as blender_ui
+ blender_ui.unregister()
+ if bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is True:
+ blender_ui.variant_unregister()
+
for c in classes:
bpy.utils.unregister_class(c)
for f in exporter_extension_panel_unregister_functors:
@@ -1221,8 +1329,6 @@ def unregister():
f()
importer_extension_panel_unregister_functors.clear()
- blender_ui.unregister()
-
# bpy.utils.unregister_module(__name__)
# remove from the export / import menu
diff --git a/io_scene_gltf2/blender/com/gltf2_blender_default.py b/io_scene_gltf2/blender/com/gltf2_blender_default.py
new file mode 100644
index 00000000..c3951f4e
--- /dev/null
+++ b/io_scene_gltf2/blender/com/gltf2_blender_default.py
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2018-2022 The glTF-Blender-IO authors.
+
+BLENDER_IOR = 1.45
+BLENDER_SPECULAR = 0.5
+BLENDER_SPECULAR_TINT = 0.0 \ No newline at end of file
diff --git a/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py b/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py
index 7b90b0a3..4f8417e9 100755
--- a/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py
+++ b/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py
@@ -3,13 +3,23 @@
import bpy
-def get_gltf_node_name():
+# Get compatibility at export with old files
+def get_gltf_node_old_name():
return "glTF Settings"
+def get_gltf_node_name():
+ return "glTF Material Output"
+
def create_settings_group(name):
gltf_node_group = bpy.data.node_groups.new(name, 'ShaderNodeTree')
gltf_node_group.inputs.new("NodeSocketFloat", "Occlusion")
+ thicknessFactor = gltf_node_group.inputs.new("NodeSocketFloat", "Thickness")
+ thicknessFactor.default_value = 0.0
gltf_node_group.nodes.new('NodeGroupOutput')
gltf_node_group_input = gltf_node_group.nodes.new('NodeGroupInput')
+ specular = gltf_node_group.inputs.new("NodeSocketFloat", "Specular")
+ specular.default_value = 1.0
+ specularColor = gltf_node_group.inputs.new("NodeSocketColor", "Specular Color")
+ specularColor.default_value = [1.0,1.0,1.0,1.0]
gltf_node_group_input.location = -200, 0
- return gltf_node_group \ No newline at end of file
+ return gltf_node_group
diff --git a/io_scene_gltf2/blender/com/gltf2_blender_ui.py b/io_scene_gltf2/blender/com/gltf2_blender_ui.py
index 59c364fb..0788bbbc 100644
--- a/io_scene_gltf2/blender/com/gltf2_blender_ui.py
+++ b/io_scene_gltf2/blender/com/gltf2_blender_ui.py
@@ -4,6 +4,8 @@
import bpy
from ..com.gltf2_blender_material_helpers import get_gltf_node_name, create_settings_group
+################ glTF Material Output node ###########################################
+
def create_gltf_ao_group(operator, group_name):
# create a new group
@@ -13,8 +15,8 @@ def create_gltf_ao_group(operator, group_name):
class NODE_OT_GLTF_SETTINGS(bpy.types.Operator):
bl_idname = "node.gltf_settings_node_operator"
- bl_label = "glTF Settings"
-
+ bl_label = "glTF Material Output"
+ bl_description = "Add a node to the active tree for glTF export"
@classmethod
def poll(cls, context):
@@ -40,10 +42,446 @@ def add_gltf_settings_to_menu(self, context) :
if bpy.context.preferences.addons['io_scene_gltf2'].preferences.settings_node_ui is True:
self.layout.operator("node.gltf_settings_node_operator")
+################################### KHR_materials_variants ####################
+
+# Global UI panel
+
+class gltf2_KHR_materials_variants_variant(bpy.types.PropertyGroup):
+ variant_idx : bpy.props.IntProperty()
+ name : bpy.props.StringProperty(name="Variant Name")
+
+class SCENE_UL_gltf2_variants(bpy.types.UIList):
+ def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
+
+ if self.layout_type in {'DEFAULT', 'COMPACT'}:
+ layout.prop(item, "name", text="", emboss=False)
+
+ elif self.layout_type in {'GRID'}:
+ layout.alignment = 'CENTER'
+
+class SCENE_PT_gltf2_variants(bpy.types.Panel):
+ bl_label = "glTF Material Variants"
+ bl_space_type = 'VIEW_3D'
+ bl_region_type = 'UI'
+ bl_category = "glTF Variants"
+
+ @classmethod
+ def poll(self, context):
+ return bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is True
+
+ def draw(self, context):
+ layout = self.layout
+ row = layout.row()
+
+ if bpy.data.scenes[0].get('gltf2_KHR_materials_variants_variants') and len(bpy.data.scenes[0].gltf2_KHR_materials_variants_variants) > 0:
+
+ row.template_list("SCENE_UL_gltf2_variants", "", bpy.data.scenes[0], "gltf2_KHR_materials_variants_variants", bpy.data.scenes[0], "gltf2_active_variant")
+ col = row.column()
+ row = col.column(align=True)
+ row.operator("scene.gltf2_variant_add", icon="ADD", text="")
+ row.operator("scene.gltf2_variant_remove", icon="REMOVE", text="")
+
+ row = layout.row()
+ row.operator("scene.gltf2_display_variant", text="Display Variant")
+ row = layout.row()
+ row.operator("scene.gltf2_assign_to_variant", text="Assign To Variant")
+ row = layout.row()
+ row.operator("scene.gltf2_reset_to_original", text="Reset To Original")
+ row.operator("scene.gltf2_assign_as_original", text="Assign as Original")
+ else:
+ row.operator("scene.gltf2_variant_add", text="Add Material Variant")
+
+class SCENE_OT_gltf2_variant_add(bpy.types.Operator):
+ """Add a new Material Variant"""
+ bl_idname = "scene.gltf2_variant_add"
+ bl_label = "Add Material Variant"
+ bl_options = {'REGISTER'}
+
+ @classmethod
+ def poll(self, context):
+ return True
+
+ def execute(self, context):
+ var = bpy.data.scenes[0].gltf2_KHR_materials_variants_variants.add()
+ var.variant_idx = len(bpy.data.scenes[0].gltf2_KHR_materials_variants_variants) - 1
+ var.name = "VariantName"
+ bpy.data.scenes[0].gltf2_active_variant = len(bpy.data.scenes[0].gltf2_KHR_materials_variants_variants) - 1
+ return {'FINISHED'}
+
+class SCENE_OT_gltf2_variant_remove(bpy.types.Operator):
+ """Add a new Material Variant"""
+ bl_idname = "scene.gltf2_variant_remove"
+ bl_label = "Remove Variant"
+ bl_options = {'REGISTER'}
+
+ @classmethod
+ def poll(self, context):
+ return len(bpy.data.scenes[0].gltf2_KHR_materials_variants_variants) > 0
+
+ def execute(self, context):
+ bpy.data.scenes[0].gltf2_KHR_materials_variants_variants.remove(bpy.data.scenes[0].gltf2_active_variant)
+
+ # loop on all mesh
+ for obj in [o for o in bpy.data.objects if o.type == "MESH"]:
+ mesh = obj.data
+ remove_idx_data = []
+ for idx, i in enumerate(mesh.gltf2_variant_mesh_data):
+ remove_idx_variants = []
+ for idx_var, v in enumerate(i.variants):
+ if v.variant.variant_idx == bpy.data.scenes[0].gltf2_active_variant:
+ remove_idx_variants.append(idx_var)
+ elif v.variant.variant_idx > bpy.data.scenes[0].gltf2_active_variant:
+ v.variant.variant_idx -= 1
+
+ if len(remove_idx_variants) > 0:
+ for idx_var in remove_idx_variants:
+ i.variants.remove(idx_var)
+
+ if len(i.variants) == 0:
+ remove_idx_data.append(idx)
+
+ if len(remove_idx_data) > 0:
+ for idx_data in remove_idx_data:
+ mesh.gltf2_variant_mesh_data.remove(idx_data)
+
+ return {'FINISHED'}
+
+
+# Operator to display a variant
+class SCENE_OT_gltf2_display_variant(bpy.types.Operator):
+ bl_idname = "scene.gltf2_display_variant"
+ bl_label = "Display Variant"
+ bl_options = {'REGISTER'}
+
+
+ @classmethod
+ def poll(self, context):
+ return len(bpy.data.scenes[0].gltf2_KHR_materials_variants_variants) > 0
+
+ def execute(self, context):
+
+ gltf2_active_variant = bpy.data.scenes[0].gltf2_active_variant
+
+ # loop on all mesh
+ for obj in [o for o in bpy.data.objects if o.type == "MESH"]:
+ mesh = obj.data
+ for i in mesh.gltf2_variant_mesh_data:
+ if i.variants and gltf2_active_variant in [v.variant.variant_idx for v in i.variants]:
+ mat = i.material
+ slot = i.material_slot_index
+ if slot < len(obj.material_slots): # Seems user remove some slots...
+ obj.material_slots[slot].material = mat
+
+ return {'FINISHED'}
+
+# Operator to assign current mesh materials to a variant
+class SCENE_OT_gltf2_assign_to_variant(bpy.types.Operator):
+ bl_idname = "scene.gltf2_assign_to_variant"
+ bl_label = "Assign To Variant"
+ bl_options = {'REGISTER'}
+
+ @classmethod
+ def poll(self, context):
+ return len(bpy.data.scenes[0].gltf2_KHR_materials_variants_variants) > 0 \
+ and bpy.context.object.type == "MESH"
+
+ def execute(self, context):
+ gltf2_active_variant = bpy.data.scenes[0].gltf2_active_variant
+ obj = bpy.context.object
+
+ # loop on material slots ( primitives )
+ for mat_slot_idx, s in enumerate(obj.material_slots):
+ # Check if there is already data for this slot
+ found = False
+ for i in obj.data.gltf2_variant_mesh_data:
+ if i.material_slot_index == mat_slot_idx and i.material == s.material:
+ found = True
+ variant_primitive = i
+
+ if found is False:
+ variant_primitive = obj.data.gltf2_variant_mesh_data.add()
+ variant_primitive.material_slot_index = mat_slot_idx
+ variant_primitive.material = s.material
+
+ vari = variant_primitive.variants.add()
+ vari.variant.variant_idx = bpy.data.scenes[0].gltf2_active_variant
+
+ return {'FINISHED'}
+
+# Operator to reset mesh to original (using default material when exists)
+class SCENE_OT_gltf2_reset_to_original(bpy.types.Operator):
+ bl_idname = "scene.gltf2_reset_to_original"
+ bl_label = "Reset to Original"
+ bl_options = {'REGISTER'}
+
+ @classmethod
+ def poll(self, context):
+ return bpy.context.object.type == "MESH" and len(context.object.data.gltf2_variant_default_materials) > 0
+
+ def execute(self, context):
+ obj = bpy.context.object
+
+ # loop on material slots ( primitives )
+ for mat_slot_idx, s in enumerate(obj.material_slots):
+ # Check if there is a default material for this slot
+ found = False
+ for i in obj.data.gltf2_variant_default_materials:
+ if i.material_slot_index == mat_slot_idx:
+ s.material = i.default_material
+ break
+
+ return {'FINISHED'}
+
+# Operator to assign current materials as default materials
+class SCENE_OT_gltf2_assign_as_original(bpy.types.Operator):
+ bl_idname = "scene.gltf2_assign_as_original"
+ bl_label = "Assign as Original"
+ bl_options = {'REGISTER'}
+
+ @classmethod
+ def poll(self, context):
+ return bpy.context.object.type == "MESH"
+
+ def execute(self, context):
+ obj = bpy.context.object
+
+ # loop on material slots ( primitives )
+ for mat_slot_idx, s in enumerate(obj.material_slots):
+ # Check if there is a default material for this slot
+ found = False
+ for i in obj.data.gltf2_variant_default_materials:
+ if i.material_slot_index == mat_slot_idx:
+ found = True
+ # Update if needed
+ i.default_material = s.material
+ break
+
+ if found is False:
+ default_mat = obj.data.gltf2_variant_default_materials.add()
+ default_mat.material_slot_index = mat_slot_idx
+ default_mat.default_material = s.material
+
+ return {'FINISHED'}
+
+# Mesh Panel
+
+class gltf2_KHR_materials_variant_pointer(bpy.types.PropertyGroup):
+ variant: bpy.props.PointerProperty(type=gltf2_KHR_materials_variants_variant)
+
+class gltf2_KHR_materials_variants_default_material(bpy.types.PropertyGroup):
+ material_slot_index: bpy.props.IntProperty(name="Material Slot Index")
+ default_material: bpy.props.PointerProperty(type=bpy.types.Material)
+
+class gltf2_KHR_materials_variants_primitive(bpy.types.PropertyGroup):
+ material_slot_index : bpy.props.IntProperty(name="Material Slot Index")
+ material: bpy.props.PointerProperty(type=bpy.types.Material)
+ variants: bpy.props.CollectionProperty(type=gltf2_KHR_materials_variant_pointer)
+ active_variant_idx: bpy.props.IntProperty()
+
+class MESH_UL_gltf2_mesh_variants(bpy.types.UIList):
+ def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
+
+ vari = item.variant
+ layout.context_pointer_set("id", vari)
+
+ if self.layout_type in {'DEFAULT', 'COMPACT'}:
+ layout.prop(bpy.data.scenes[0].gltf2_KHR_materials_variants_variants[vari.variant_idx], "name", text="", emboss=False)
+ elif self.layout_type in {'GRID'}:
+ layout.alignment = 'CENTER'
+
+class MESH_PT_gltf2_mesh_variants(bpy.types.Panel):
+ bl_label = "glTF Material Variants"
+ bl_space_type = 'PROPERTIES'
+ bl_region_type = 'WINDOW'
+ bl_context = "material"
+
+ @classmethod
+ def poll(self, context):
+ return bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is True \
+ and len(bpy.context.object.material_slots) > 0
+
+ def draw(self, context):
+ layout = self.layout
+
+ active_material_slots = bpy.context.object.active_material_index
+
+ found = False
+ if 'gltf2_variant_mesh_data' in bpy.context.object.data.keys():
+ for idx, prim in enumerate(bpy.context.object.data.gltf2_variant_mesh_data):
+ if prim.material_slot_index == active_material_slots and id(prim.material) == id(bpy.context.object.material_slots[active_material_slots].material):
+ found = True
+ break
+
+ row = layout.row()
+ if found is True:
+ row.template_list("MESH_UL_gltf2_mesh_variants", "", prim, "variants", prim, "active_variant_idx")
+ col = row.column()
+ row = col.column(align=True)
+ row.operator("scene.gltf2_variants_slot_add", icon="ADD", text="")
+ row.operator("scene.gltf2_remove_material_variant", icon="REMOVE", text="")
+
+ row = layout.row()
+ if 'gltf2_KHR_materials_variants_variants' in bpy.data.scenes[0].keys() and len(bpy.data.scenes[0].gltf2_KHR_materials_variants_variants) > 0:
+ row.prop_search(context.object.data, "gltf2_variant_pointer", bpy.data.scenes[0], "gltf2_KHR_materials_variants_variants", text="Variant")
+ row = layout.row()
+ row.operator("scene.gltf2_material_to_variant", text="Assign To Variant")
+ else:
+ row.label(text="Please Create a Variant First")
+ else:
+ if 'gltf2_KHR_materials_variants_variants' in bpy.data.scenes[0].keys() and len(bpy.data.scenes[0].gltf2_KHR_materials_variants_variants) > 0:
+ row.operator("scene.gltf2_variants_slot_add", text="Add a new Variant Slot")
+ else:
+ row.label(text="Please Create a Variant First")
+
+
+class SCENE_OT_gltf2_variant_slot_add(bpy.types.Operator):
+ """Add a new Slot"""
+ bl_idname = "scene.gltf2_variants_slot_add"
+ bl_label = "Add new Slot"
+ bl_options = {'REGISTER'}
+
+ @classmethod
+ def poll(self, context):
+ return len(bpy.context.object.material_slots) > 0
+
+ def execute(self, context):
+ mesh = context.object.data
+ # Check if there is already a data for this slot_idx + material
+
+ found = False
+ for i in mesh.gltf2_variant_mesh_data:
+ if i.material_slot_index == context.object.active_material_index and i.material == context.object.material_slots[context.object.active_material_index].material:
+ found = True
+ variant_primitive = i
+
+ if found is False:
+ variant_primitive = mesh.gltf2_variant_mesh_data.add()
+ variant_primitive.material_slot_index = context.object.active_material_index
+ variant_primitive.material = context.object.material_slots[context.object.active_material_index].material
+
+ vari = variant_primitive.variants.add()
+ vari.variant.variant_idx = bpy.data.scenes[0].gltf2_active_variant
+
+ return {'FINISHED'}
+
+class SCENE_OT_gltf2_material_to_variant(bpy.types.Operator):
+ """Assign Variant to Slot"""
+ bl_idname = "scene.gltf2_material_to_variant"
+ bl_label = "Assign Material To Variant"
+ bl_options = {'REGISTER'}
+
+ @classmethod
+ def poll(self, context):
+ return len(bpy.context.object.material_slots) > 0 and context.object.data.gltf2_variant_pointer != ""
+
+ def execute(self, context):
+ mesh = context.object.data
+
+ found = False
+ for i in mesh.gltf2_variant_mesh_data:
+ if i.material_slot_index == context.object.active_material_index and i.material == context.object.material_slots[context.object.active_material_index].material:
+ found = True
+ variant_primitive = i
+
+ if found is False:
+ return {'CANCELLED'}
+
+ vari = variant_primitive.variants[variant_primitive.active_variant_idx]
+
+ # Retrieve variant idx
+ found = False
+ for v in bpy.data.scenes[0].gltf2_KHR_materials_variants_variants:
+ if v.name == context.object.data.gltf2_variant_pointer:
+ found = True
+ break
+
+ if found is False:
+ return {'CANCELLED'}
+
+ vari.variant.variant_idx = v.variant_idx
+
+ return {'FINISHED'}
+
+class SCENE_OT_gltf2_remove_material_variant(bpy.types.Operator):
+ """Remove a variant Slot"""
+ bl_idname = "scene.gltf2_remove_material_variant"
+ bl_label = "Remove a variant Slot"
+ bl_options = {'REGISTER'}
+
+ @classmethod
+ def poll(self, context):
+ return len(bpy.context.object.material_slots) > 0 and len(bpy.context.object.data.gltf2_variant_mesh_data) > 0
+
+ def execute(self, context):
+ mesh = context.object.data
+
+ found = False
+ found_idx = -1
+ for idx, i in enumerate(mesh.gltf2_variant_mesh_data):
+ if i.material_slot_index == context.object.active_material_index and i.material == context.object.material_slots[context.object.active_material_index].material:
+ found = True
+ variant_primitive = i
+ found_idx = idx
+
+ if found is False:
+ return {'CANCELLED'}
+
+ variant_primitive.variants.remove(variant_primitive.active_variant_idx)
+
+ if len(variant_primitive.variants) == 0:
+ mesh.gltf2_variant_mesh_data.remove(found_idx)
+
+ return {'FINISHED'}
+
+
+###############################################################################
def register():
bpy.utils.register_class(NODE_OT_GLTF_SETTINGS)
bpy.types.NODE_MT_category_SH_NEW_OUTPUT.append(add_gltf_settings_to_menu)
+def variant_register():
+ bpy.utils.register_class(SCENE_OT_gltf2_display_variant)
+ bpy.utils.register_class(SCENE_OT_gltf2_assign_to_variant)
+ bpy.utils.register_class(SCENE_OT_gltf2_reset_to_original)
+ bpy.utils.register_class(SCENE_OT_gltf2_assign_as_original)
+ bpy.utils.register_class(SCENE_OT_gltf2_remove_material_variant)
+ bpy.utils.register_class(gltf2_KHR_materials_variants_variant)
+ bpy.utils.register_class(gltf2_KHR_materials_variant_pointer)
+ bpy.utils.register_class(gltf2_KHR_materials_variants_primitive)
+ bpy.utils.register_class(gltf2_KHR_materials_variants_default_material)
+ bpy.utils.register_class(SCENE_UL_gltf2_variants)
+ bpy.utils.register_class(SCENE_PT_gltf2_variants)
+ bpy.utils.register_class(MESH_UL_gltf2_mesh_variants)
+ bpy.utils.register_class(MESH_PT_gltf2_mesh_variants)
+ bpy.utils.register_class(SCENE_OT_gltf2_variant_add)
+ bpy.utils.register_class(SCENE_OT_gltf2_variant_remove)
+ bpy.utils.register_class(SCENE_OT_gltf2_material_to_variant)
+ bpy.utils.register_class(SCENE_OT_gltf2_variant_slot_add)
+ bpy.types.Mesh.gltf2_variant_mesh_data = bpy.props.CollectionProperty(type=gltf2_KHR_materials_variants_primitive)
+ bpy.types.Mesh.gltf2_variant_default_materials = bpy.props.CollectionProperty(type=gltf2_KHR_materials_variants_default_material)
+ bpy.types.Mesh.gltf2_variant_pointer = bpy.props.StringProperty()
+ bpy.types.Scene.gltf2_KHR_materials_variants_variants = bpy.props.CollectionProperty(type=gltf2_KHR_materials_variants_variant)
+ bpy.types.Scene.gltf2_active_variant = bpy.props.IntProperty()
+
def unregister():
bpy.utils.unregister_class(NODE_OT_GLTF_SETTINGS)
+
+def variant_unregister():
+ bpy.utils.unregister_class(SCENE_OT_gltf2_variant_add)
+ bpy.utils.unregister_class(SCENE_OT_gltf2_variant_remove)
+ bpy.utils.unregister_class(SCENE_OT_gltf2_material_to_variant)
+ bpy.utils.unregister_class(SCENE_OT_gltf2_variant_slot_add)
+ bpy.utils.unregister_class(SCENE_OT_gltf2_display_variant)
+ bpy.utils.unregister_class(SCENE_OT_gltf2_assign_to_variant)
+ bpy.utils.unregister_class(SCENE_OT_gltf2_reset_to_original)
+ bpy.utils.unregister_class(SCENE_OT_gltf2_assign_as_original)
+ bpy.utils.unregister_class(SCENE_OT_gltf2_remove_material_variant)
+ bpy.utils.unregister_class(SCENE_PT_gltf2_variants)
+ bpy.utils.unregister_class(SCENE_UL_gltf2_variants)
+ bpy.utils.unregister_class(MESH_PT_gltf2_mesh_variants)
+ bpy.utils.unregister_class(MESH_UL_gltf2_mesh_variants)
+ bpy.utils.unregister_class(gltf2_KHR_materials_variants_default_material)
+ bpy.utils.unregister_class(gltf2_KHR_materials_variants_primitive)
+ bpy.utils.unregister_class(gltf2_KHR_materials_variants_variant)
+ bpy.utils.unregister_class(gltf2_KHR_materials_variant_pointer)
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_export_keys.py b/io_scene_gltf2/blender/exp/gltf2_blender_export_keys.py
index 812db3f9..96f97af1 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_export_keys.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_export_keys.py
@@ -20,7 +20,6 @@ RENDERABLE = 'gltf_renderable'
ACTIVE_COLLECTION = 'gltf_active_collection'
SKINS = 'gltf_skins'
DEF_BONES_ONLY = 'gltf_def_bones'
-DISPLACEMENT = 'gltf_displacement'
FORCE_SAMPLING = 'gltf_force_sampling'
FRAME_RANGE = 'gltf_frame_range'
FRAME_STEP = 'gltf_frame_step'
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_extract.py b/io_scene_gltf2/blender/exp/gltf2_blender_extract.py
index d4f34126..93947acb 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_extract.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_extract.py
@@ -39,6 +39,18 @@ def extract_primitives(blender_mesh, uuid_for_skined_data, blender_vertex_groups
if export_settings[gltf2_blender_export_keys.COLORS]:
color_max = len(blender_mesh.vertex_colors)
+ colors_attributes = []
+ rendered_color_idx = blender_mesh.attributes.render_color_index
+
+ if color_max > 0:
+ colors_attributes.append(rendered_color_idx)
+ # Then find other ones
+ colors_attributes.extend([
+ i for i in range(len(blender_mesh.color_attributes)) if i != rendered_color_idx \
+ and blender_mesh.vertex_colors.find(blender_mesh.color_attributes[i].name) != -1
+ ])
+
+
armature = None
skin = None
if blender_vertex_groups and export_settings[gltf2_blender_export_keys.SKINS]:
@@ -110,7 +122,7 @@ def extract_primitives(blender_mesh, uuid_for_skined_data, blender_vertex_groups
dot_fields += [('tx', np.float32), ('ty', np.float32), ('tz', np.float32), ('tw', np.float32)]
for uv_i in range(tex_coord_max):
dot_fields += [('uv%dx' % uv_i, np.float32), ('uv%dy' % uv_i, np.float32)]
- for col_i in range(color_max):
+ for col_i, _ in enumerate(colors_attributes):
dot_fields += [
('color%dr' % col_i, np.float32),
('color%dg' % col_i, np.float32),
@@ -163,8 +175,12 @@ def extract_primitives(blender_mesh, uuid_for_skined_data, blender_vertex_groups
dots['uv%dy' % uv_i] = uvs[:, 1]
del uvs
- for col_i in range(color_max):
- colors = __get_colors(blender_mesh, col_i)
+ colors_types = []
+ for col_i, blender_col_i in enumerate(colors_attributes):
+ colors, colors_type, domain = __get_colors(blender_mesh, col_i, blender_col_i)
+ if domain == "POINT":
+ colors = colors[dots['vertex_index']]
+ colors_types.append(colors_type)
dots['color%dr' % col_i] = colors[:, 0]
dots['color%dg' % col_i] = colors[:, 1]
dots['color%db' % col_i] = colors[:, 2]
@@ -251,13 +267,16 @@ def extract_primitives(blender_mesh, uuid_for_skined_data, blender_vertex_groups
uvs[:, 1] = prim_dots['uv%dy' % tex_coord_i]
attributes['TEXCOORD_%d' % tex_coord_i] = uvs
- for color_i in range(color_max):
+ for color_i, _ in enumerate(colors_attributes):
colors = np.empty((len(prim_dots), 4), dtype=np.float32)
colors[:, 0] = prim_dots['color%dr' % color_i]
colors[:, 1] = prim_dots['color%dg' % color_i]
colors[:, 2] = prim_dots['color%db' % color_i]
colors[:, 3] = prim_dots['color%da' % color_i]
- attributes['COLOR_%d' % color_i] = colors
+ attributes['COLOR_%d' % color_i] = {}
+ attributes['COLOR_%d' % color_i]["data"] = colors
+
+ attributes['COLOR_%d' % color_i]["norm"] = colors_types[color_i] == "BYTE_COLOR"
if skin:
joints = [[] for _ in range(num_joint_sets)]
@@ -525,13 +544,15 @@ def __get_uvs(blender_mesh, uv_i):
return uvs
-def __get_colors(blender_mesh, color_i):
- colors = np.empty(len(blender_mesh.loops) * 4, dtype=np.float32)
- layer = blender_mesh.vertex_colors[color_i]
- blender_mesh.color_attributes[layer.name].data.foreach_get('color', colors)
- colors = colors.reshape(len(blender_mesh.loops), 4)
+def __get_colors(blender_mesh, color_i, blender_color_i):
+ if blender_mesh.color_attributes[blender_color_i].domain == "POINT":
+ colors = np.empty(len(blender_mesh.vertices) * 4, dtype=np.float32) #POINT
+ else:
+ colors = np.empty(len(blender_mesh.loops) * 4, dtype=np.float32) #CORNER
+ blender_mesh.color_attributes[blender_color_i].data.foreach_get('color', colors)
+ colors = colors.reshape(-1, 4)
# colors are already linear, no need to switch color space
- return colors
+ return colors, blender_mesh.color_attributes[blender_color_i].data_type, blender_mesh.color_attributes[blender_color_i].domain
def __get_bone_data(blender_mesh, skin, blender_vertex_groups):
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather.py
index 6153bc33..721ef115 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather.py
@@ -59,6 +59,8 @@ def __gather_scene(blender_scene, export_settings):
# Now, we can filter tree if needed
vtree.filter()
+ vtree.variants_reset_to_original()
+
export_user_extensions('vtree_after_filter_hook', export_settings, vtree)
export_settings['vtree'] = vtree
@@ -94,9 +96,12 @@ def __gather_animations(blender_scene, export_settings):
if export_settings['gltf_nla_strips'] is False:
# Fake an animation with all animations of the scene
merged_tracks = {}
- merged_tracks['Animation'] = []
+ merged_tracks_name = 'Animation'
+ if(len(export_settings['gltf_nla_strips_merged_animation_name']) > 0):
+ merged_tracks_name = export_settings['gltf_nla_strips_merged_animation_name']
+ merged_tracks[merged_tracks_name] = []
for idx, animation in enumerate(animations):
- merged_tracks['Animation'].append(idx)
+ merged_tracks[merged_tracks_name].append(idx)
to_delete_idx = []
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_channels.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_channels.py
index 98ae8b82..3e67f1f7 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_channels.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_channels.py
@@ -37,7 +37,7 @@ def gather_animation_channels(obj_uuid: int,
if blender_action.use_frame_range is True:
bake_range_start = blender_action.frame_start
bake_range_end = blender_action.frame_end
- force_range = True # keyframe_points is read-only, we cant restrict here
+ force_range = True # keyframe_points is read-only, we can't restrict here
else:
groups = __get_channel_groups(blender_action, blender_object, export_settings)
# Note: channels has some None items only for SK if some SK are not animated
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py
index e1ed19ea..53d78945 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py
@@ -398,11 +398,17 @@ def gather_keyframes(blender_obj_uuid: str,
key.set_first_tangent()
else:
# otherwise construct an in tangent coordinate from the keyframes control points. We intermediately
- # use a point at t-1 to define the tangent. This allows the tangent control point to be transformed
- # normally
+ # use a point at t+1 to define the tangent. This allows the tangent control point to be transformed
+ # normally, but only works for locally linear transformation. The more non-linear a transform, the
+ # more imprecise this method is.
+ # We could use any other (v1, t1) for which (v1 - v0) / (t1 - t0) equals the tangent. By using t+1
+ # for both in and out tangents, we guarantee that (even if there are errors or numerical imprecisions)
+ # symmetrical control points translate to symmetrical tangents.
+ # Note: I am not sure that linearity is never broken with quaternions and their normalization.
+ # Especially at sign swap it might occur that the value gets negated but the control point not.
+ # I have however not once encountered an issue with this.
key.in_tangent = [
- c.keyframe_points[i].co[1] + ((c.keyframe_points[i].co[1] - c.keyframe_points[i].handle_left[1]
- ) / (frame - frames[i - 1]))
+ c.keyframe_points[i].co[1] + (c.keyframe_points[i].handle_left[1] - c.keyframe_points[i].co[1]) / (c.keyframe_points[i].handle_left[0] - c.keyframe_points[i].co[0])
for c in channels if c is not None
]
# Construct the out tangent
@@ -410,12 +416,10 @@ def gather_keyframes(blender_obj_uuid: str,
# end out-tangent should become all zero
key.set_last_tangent()
else:
- # otherwise construct an in tangent coordinate from the keyframes control points. We intermediately
- # use a point at t+1 to define the tangent. This allows the tangent control point to be transformed
- # normally
+ # otherwise construct an in tangent coordinate from the keyframes control points.
+ # This happens the same way how in tangents are handled above.
key.out_tangent = [
- c.keyframe_points[i].co[1] + ((c.keyframe_points[i].handle_right[1] - c.keyframe_points[i].co[1]
- ) / (frames[i + 1] - frame))
+ c.keyframe_points[i].co[1] + (c.keyframe_points[i].handle_right[1] - c.keyframe_points[i].co[1]) / (c.keyframe_points[i].handle_right[0] - c.keyframe_points[i].co[0])
for c in channels if c is not None
]
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py
index 1ee98a29..5c8011ed 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py
@@ -414,6 +414,7 @@ def __gather_output(channels: typing.Tuple[bpy.types.FCurve],
transform = parent_inverse
values = []
+ fps = bpy.context.scene.render.fps
for keyframe in keyframes:
# Transform the data and build gltf control points
value = gltf2_blender_math.transform(keyframe.value, target_datapath, transform, need_rotation_correction)
@@ -426,11 +427,11 @@ def __gather_output(channels: typing.Tuple[bpy.types.FCurve],
in_tangent = gltf2_blender_math.transform(keyframe.in_tangent, target_datapath, transform, need_rotation_correction)
if is_yup and blender_object_if_armature is None:
in_tangent = gltf2_blender_math.swizzle_yup(in_tangent, target_datapath)
- # the tangent in glTF is relative to the keyframe value
+ # the tangent in glTF is relative to the keyframe value and uses seconds
if not isinstance(value, list):
- in_tangent = value - in_tangent
+ in_tangent = fps * (in_tangent - value)
else:
- in_tangent = [value[i] - in_tangent[i] for i in range(len(value))]
+ in_tangent = [fps * (in_tangent[i] - value[i]) for i in range(len(value))]
keyframe_value = gltf2_blender_math.mathutils_to_gltf(in_tangent) + keyframe_value # append
if keyframe.out_tangent is not None:
@@ -438,11 +439,11 @@ def __gather_output(channels: typing.Tuple[bpy.types.FCurve],
out_tangent = gltf2_blender_math.transform(keyframe.out_tangent, target_datapath, transform, need_rotation_correction)
if is_yup and blender_object_if_armature is None:
out_tangent = gltf2_blender_math.swizzle_yup(out_tangent, target_datapath)
- # the tangent in glTF is relative to the keyframe value
+ # the tangent in glTF is relative to the keyframe value and uses seconds
if not isinstance(value, list):
- out_tangent = value - out_tangent
+ out_tangent = fps * (out_tangent - value)
else:
- out_tangent = [value[i] - out_tangent[i] for i in range(len(value))]
+ out_tangent = [fps * (out_tangent[i] - value[i]) for i in range(len(value))]
keyframe_value = keyframe_value + gltf2_blender_math.mathutils_to_gltf(out_tangent) # append
values += keyframe_value
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py
index 20a919dc..bdb2ee00 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py
@@ -64,21 +64,24 @@ def gather_animations( obj_uuid: int,
# No TRS animation are found for this object.
# But we need to bake, in case we export selection
# (Only when force sampling is ON)
- # If force sampling is OFF, can lead to inconsistant export anyway
+ # If force sampling is OFF, can lead to inconsistent export anyway
if export_settings['gltf_selected'] is True and blender_object.type != "ARMATURE" and export_settings['gltf_force_sampling'] is True:
- channels = __gather_channels_baked(obj_uuid, export_settings)
- if channels is not None:
- animation = gltf2_io.Animation(
- channels=channels,
- extensions=None, # as other animations
- extras=None, # Because there is no animation to get extras from
- name=blender_object.name, # Use object name as animation name
- samplers=[]
- )
-
- __link_samplers(animation, export_settings)
- if animation is not None:
- animations.append(animation)
+ # We also have to check if this is a skinned mesh, because we don't have to force animation baking on this case
+ # (skinned meshes TRS must be ignored, says glTF specification)
+ if export_settings['vtree'].nodes[obj_uuid].skin is None:
+ channels = __gather_channels_baked(obj_uuid, export_settings)
+ if channels is not None:
+ animation = gltf2_io.Animation(
+ channels=channels,
+ extensions=None, # as other animations
+ extras=None, # Because there is no animation to get extras from
+ name=blender_object.name, # Use object name as animation name
+ samplers=[]
+ )
+
+ __link_samplers(animation, export_settings)
+ if animation is not None:
+ animations.append(animation)
elif export_settings['gltf_selected'] is True and blender_object.type == "ARMATURE":
# We need to bake all bones. Because some bone can have some constraints linking to
# some other armature bones, for example
@@ -319,20 +322,21 @@ def __get_blender_actions(blender_object: bpy.types.Object,
action_on_type[strip.action.name] = "SHAPEKEY"
# If there are only 1 armature, include all animations, even if not in NLA
- if blender_object.type == "ARMATURE":
- if len(export_settings['vtree'].get_all_node_of_type(VExportNode.ARMATURE)) == 1:
- # Keep all actions on objects (no Shapekey animation)
- for act in [a for a in bpy.data.actions if a.id_root == "OBJECT"]:
- # We need to check this is an armature action
- # Checking that at least 1 bone is animated
- if not __is_armature_action(act):
- continue
- # Check if this action is already taken into account
- if act.name in blender_tracks.keys():
- continue
- blender_actions.append(act)
- blender_tracks[act.name] = None
- action_on_type[act.name] = "OBJECT"
+ if export_settings['gltf_export_anim_single_armature'] is True:
+ if blender_object.type == "ARMATURE":
+ if len(export_settings['vtree'].get_all_node_of_type(VExportNode.ARMATURE)) == 1:
+ # Keep all actions on objects (no Shapekey animation)
+ for act in [a for a in bpy.data.actions if a.id_root == "OBJECT"]:
+ # We need to check this is an armature action
+ # Checking that at least 1 bone is animated
+ if not __is_armature_action(act):
+ continue
+ # Check if this action is already taken into account
+ if act.name in blender_tracks.keys():
+ continue
+ blender_actions.append(act)
+ blender_tracks[act.name] = None
+ action_on_type[act.name] = "OBJECT"
export_user_extensions('gather_actions_hook', export_settings, blender_object, blender_actions, blender_tracks, action_on_type)
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_drivers.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_drivers.py
index b0e538c8..9da5cc65 100644
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_drivers.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_drivers.py
@@ -13,7 +13,7 @@ def get_sk_drivers(blender_armature_uuid, export_settings):
drivers = []
# Take into account skinned mesh, and mesh parented to a bone of the armature
- children_list = export_settings['vtree'].nodes[blender_armature_uuid].children
+ children_list = export_settings['vtree'].nodes[blender_armature_uuid].children.copy()
for bone in export_settings['vtree'].get_all_bones(blender_armature_uuid):
children_list.extend(export_settings['vtree'].nodes[bone].children)
@@ -74,7 +74,8 @@ def get_sk_drivers(blender_armature_uuid, export_settings):
else:
all_sorted_channels.append(existing_idx[i])
- if len(all_sorted_channels) > 0:
+ # Checks there are some driver on SK, and that there is not only invalid drivers
+ if len(all_sorted_channels) > 0 and not all([i is None for i in all_sorted_channels]):
drivers.append((child_uuid, tuple(all_sorted_channels)))
return tuple(drivers)
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py
index 0dfce9f9..81e79a50 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py
@@ -11,7 +11,7 @@ from io_scene_gltf2.blender.exp import gltf2_blender_search_node_tree
from io_scene_gltf2.io.exp import gltf2_io_binary_data
from io_scene_gltf2.io.exp import gltf2_io_image_data
from io_scene_gltf2.io.com import gltf2_io_debug
-from io_scene_gltf2.blender.exp.gltf2_blender_image import Channel, ExportImage, FillImage
+from io_scene_gltf2.blender.exp.gltf2_blender_image import Channel, ExportImage, FillImage, StoreImage, StoreData
from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached
from io_scene_gltf2.io.exp.gltf2_io_user_extensions import export_user_extensions
@@ -21,26 +21,31 @@ def gather_image(
blender_shader_sockets: typing.Tuple[bpy.types.NodeSocket],
export_settings):
if not __filter_image(blender_shader_sockets, export_settings):
- return None
+ return None, None
image_data = __get_image_data(blender_shader_sockets, export_settings)
if image_data.empty():
# The export image has no data
- return None
+ return None, None
mime_type = __gather_mime_type(blender_shader_sockets, image_data, export_settings)
name = __gather_name(image_data, export_settings)
+ factor = None
+
if image_data.original is None:
- uri = __gather_uri(image_data, mime_type, name, export_settings)
+ uri, factor_uri = __gather_uri(image_data, mime_type, name, export_settings)
else:
# Retrieve URI relative to exported glTF files
uri = __gather_original_uri(image_data.original.filepath, export_settings)
# In case we can't retrieve image (for example packed images, with original moved)
# We don't create invalid image without uri
+ factor_uri = None
if uri is None: return None
- buffer_view = __gather_buffer_view(image_data, mime_type, name, export_settings)
+ buffer_view, factor_buffer_view = __gather_buffer_view(image_data, mime_type, name, export_settings)
+
+ factor = factor_uri if uri is not None else factor_buffer_view
image = __make_image(
buffer_view,
@@ -54,7 +59,7 @@ def gather_image(
export_user_extensions('gather_image_hook', export_settings, image, blender_shader_sockets)
- return image
+ return image, factor
def __gather_original_uri(original_uri, export_settings):
@@ -98,8 +103,9 @@ def __filter_image(sockets, export_settings):
@cached
def __gather_buffer_view(image_data, mime_type, name, export_settings):
if export_settings[gltf2_blender_export_keys.FORMAT] != 'GLTF_SEPARATE':
- return gltf2_io_binary_data.BinaryData(data=image_data.encode(mime_type))
- return None
+ data, factor = image_data.encode(mime_type)
+ return gltf2_io_binary_data.BinaryData(data=data), factor
+ return None, None
def __gather_extensions(sockets, export_settings):
@@ -165,13 +171,14 @@ def __gather_name(export_image, export_settings):
def __gather_uri(image_data, mime_type, name, export_settings):
if export_settings[gltf2_blender_export_keys.FORMAT] == 'GLTF_SEPARATE':
# as usual we just store the data in place instead of already resolving the references
+ data, factor = image_data.encode(mime_type=mime_type)
return gltf2_io_image_data.ImageData(
- data=image_data.encode(mime_type=mime_type),
+ data=data,
mime_type=mime_type,
name=name
- )
+ ), factor
- return None
+ return None, None
def __get_image_data(sockets, export_settings) -> ExportImage:
@@ -179,6 +186,18 @@ def __get_image_data(sockets, export_settings) -> ExportImage:
# in a helper class. During generation of the glTF in the exporter these will then be combined to actual binary
# resources.
results = [__get_tex_from_socket(socket, export_settings) for socket in sockets]
+
+ # Check if we need a simple mapping or more complex calculation
+ if any([socket.name == "Specular" and socket.node.type == "BSDF_PRINCIPLED" for socket in sockets]):
+ return __get_image_data_specular(sockets, results, export_settings)
+ else:
+ return __get_image_data_mapping(sockets, results, export_settings)
+
+def __get_image_data_mapping(sockets, results, export_settings) -> ExportImage:
+ """
+ Simple mapping
+ Will fit for most of exported textures : RoughnessMetallic, Basecolor, normal, ...
+ """
composed_image = ExportImage()
for result, socket in zip(results, sockets):
# Assume that user know what he does, and that channels/images are already combined correctly for pbr
@@ -217,6 +236,12 @@ def __get_image_data(sockets, export_settings) -> ExportImage:
dst_chan = Channel.R
elif socket.name == 'Clearcoat Roughness':
dst_chan = Channel.G
+ elif socket.name == 'Thickness': # For KHR_materials_volume
+ dst_chan = Channel.G
+ elif socket.name == "Specular": # For original KHR_material_specular
+ dst_chan = Channel.A
+ elif socket.name == "Sigma": # For KHR_materials_sheen
+ dst_chan = Channel.A
if dst_chan is not None:
composed_image.fill_image(result.shader_node.image, dst_chan, src_chan)
@@ -243,6 +268,54 @@ def __get_image_data(sockets, export_settings) -> ExportImage:
return composed_image
+def __get_image_data_specular(sockets, results, export_settings) -> ExportImage:
+ """
+ calculating Specular Texture, settings needed data
+ """
+ from io_scene_gltf2.blender.exp.gltf2_blender_texture_specular import specular_calculation
+ composed_image = ExportImage()
+ composed_image.set_calc(specular_calculation)
+
+ composed_image.store_data("ior", sockets[4].default_value, type="Data")
+
+ results = [__get_tex_from_socket(socket, export_settings) for socket in sockets[:-1]] #Do not retrieve IOR --> No texture allowed
+
+ mapping = {
+ 0: "specular",
+ 1: "specular_tint",
+ 2: "base_color",
+ 3: "transmission"
+ }
+
+ for idx, result in enumerate(results):
+ if __get_tex_from_socket(sockets[idx], export_settings):
+
+ composed_image.store_data(mapping[idx], result.shader_node.image, type="Image")
+
+ # rudimentarily try follow the node tree to find the correct image data.
+ src_chan = None if idx == 2 else Channel.R
+ for elem in result.path:
+ if isinstance(elem.from_node, bpy.types.ShaderNodeSeparateColor):
+ src_chan = {
+ 'Red': Channel.R,
+ 'Green': Channel.G,
+ 'Blue': Channel.B,
+ }[elem.from_socket.name]
+ if elem.from_socket.name == 'Alpha':
+ src_chan = Channel.A
+ # For base_color, keep all channels, as this is a Vec, not scalar
+ if idx != 2:
+ composed_image.store_data(mapping[idx] + "_channel", src_chan, type="Data")
+ else:
+ if src_chan is not None:
+ composed_image.store_data(mapping[idx] + "_channel", src_chan, type="Data")
+
+ else:
+ composed_image.store_data(mapping[idx], sockets[idx].default_value, type="Data")
+
+ return composed_image
+
+# TODOExt deduplicate
@cached
def __get_tex_from_socket(blender_shader_socket: bpy.types.NodeSocket, export_settings):
result = gltf2_blender_search_node_tree.from_socket(
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py
index 56c3acff..c0d17fd3 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py
@@ -1,21 +1,27 @@
# SPDX-License-Identifier: Apache-2.0
-# Copyright 2018-2021 The glTF-Blender-IO authors.
+# Copyright 2018-2022 The glTF-Blender-IO authors.
from copy import deepcopy
import bpy
from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached, cached_by_key
from io_scene_gltf2.io.com import gltf2_io
-from io_scene_gltf2.io.com.gltf2_io_extensions import Extension
+from io_scene_gltf2.blender.exp import gltf2_blender_gather_materials_unlit
from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info, gltf2_blender_export_keys
-from io_scene_gltf2.blender.exp import gltf2_blender_search_node_tree
-
from io_scene_gltf2.blender.exp import gltf2_blender_gather_materials_pbr_metallic_roughness
-from io_scene_gltf2.blender.exp import gltf2_blender_gather_materials_unlit
from ..com.gltf2_blender_extras import generate_extras
from io_scene_gltf2.blender.exp import gltf2_blender_get
from io_scene_gltf2.io.exp.gltf2_io_user_extensions import export_user_extensions
from io_scene_gltf2.io.com.gltf2_io_debug import print_console
+from io_scene_gltf2.blender.exp.gltf2_blender_gather_materials_volume import export_volume
+from io_scene_gltf2.blender.exp.gltf2_blender_gather_materials_emission import export_emission_factor, \
+ export_emission_texture, export_emission_strength_extension
+from io_scene_gltf2.blender.exp.gltf2_blender_gather_materials_sheen import export_sheen
+from io_scene_gltf2.blender.exp.gltf2_blender_gather_materials_specular import export_specular
+from io_scene_gltf2.blender.exp.gltf2_blender_gather_materials_transmission import export_transmission
+from io_scene_gltf2.blender.exp.gltf2_blender_gather_materials_clearcoat import export_clearcoat
+from io_scene_gltf2.blender.exp.gltf2_blender_gather_materials_ior import export_ior
+from io_scene_gltf2.io.com.gltf2_io_extensions import Extension
@cached
def get_material_cache_key(blender_material, active_uvmap_index, export_settings):
@@ -39,24 +45,31 @@ def gather_material(blender_material, active_uvmap_index, export_settings):
if not __filter_material(blender_material, export_settings):
return None
- mat_unlit = __gather_material_unlit(blender_material, active_uvmap_index, export_settings)
+ mat_unlit = __export_unlit(blender_material, active_uvmap_index, export_settings)
if mat_unlit is not None:
export_user_extensions('gather_material_hook', export_settings, mat_unlit, blender_material)
return mat_unlit
orm_texture = __gather_orm_texture(blender_material, export_settings)
+ emissive_factor = __gather_emissive_factor(blender_material, export_settings)
emissive_texture, uvmap_actives_emissive_texture = __gather_emissive_texture(blender_material, export_settings)
- extensions, uvmap_actives_extensions = __gather_extensions(blender_material, export_settings)
+ extensions, uvmap_actives_extensions = __gather_extensions(blender_material, emissive_factor, export_settings)
normal_texture, uvmap_actives_normal_texture = __gather_normal_texture(blender_material, export_settings)
occlusion_texture, uvmap_actives_occlusion_texture = __gather_occlusion_texture(blender_material, orm_texture, export_settings)
pbr_metallic_roughness, uvmap_actives_pbr_metallic_roughness = __gather_pbr_metallic_roughness(blender_material, orm_texture, export_settings)
+ if any([i>1.0 for i in emissive_factor or []]) is True:
+ # Strength is set on extension
+ emission_strength = max(emissive_factor)
+ emissive_factor = [f / emission_strength for f in emissive_factor]
+
+
base_material = gltf2_io.Material(
alpha_cutoff=__gather_alpha_cutoff(blender_material, export_settings),
alpha_mode=__gather_alpha_mode(blender_material, export_settings),
double_sided=__gather_double_sided(blender_material, export_settings),
- emissive_factor=__gather_emissive_factor(blender_material, export_settings),
+ emissive_factor=emissive_factor,
emissive_texture=emissive_texture,
extensions=extensions,
extras=__gather_extras(blender_material, export_settings),
@@ -106,6 +119,14 @@ def gather_material(blender_material, active_uvmap_index, export_settings):
material.extensions["KHR_materials_clearcoat"].extension['clearcoatNormalTexture'].tex_coord = active_uvmap_index
elif tex == "transmissionTexture": #TODO not tested yet
material.extensions["KHR_materials_transmission"].extension['transmissionTexture'].tex_coord = active_uvmap_index
+ elif tex == "specularTexture":
+ material.extensions["KHR_materials_specular"].extension['specularTexture'].tex_coord = active_uvmap_index
+ elif tex == "specularColorTexture":
+ material.extensions["KHR_materials_specular"].extension['specularColorTexture'].tex_coord = active_uvmap_index
+ elif tex == "sheenColorTexture":
+ material.extensions["KHR_materials_sheen"].extension['sheenColorTexture'].tex_coord = active_uvmap_index
+ elif tex == "sheenRoughnessTexture":
+ material.extensions["KHR_materials_sheen"].extension['sheenRoughnessTexture'].tex_coord = active_uvmap_index
# If material is not using active UVMap, we need to return the same material,
# Even if multiples meshes are using different active UVMap
@@ -188,72 +209,61 @@ def __gather_double_sided(blender_material, export_settings):
def __gather_emissive_factor(blender_material, export_settings):
- emissive_socket = gltf2_blender_get.get_socket(blender_material, "Emissive")
- if emissive_socket is None:
- emissive_socket = gltf2_blender_get.get_socket_old(blender_material, "EmissiveFactor")
- if isinstance(emissive_socket, bpy.types.NodeSocket):
- if export_settings['gltf_image_format'] != "NONE":
- factor = gltf2_blender_get.get_factor_from_socket(emissive_socket, kind='RGB')
- else:
- factor = gltf2_blender_get.get_const_from_default_value_socket(emissive_socket, kind='RGB')
-
- if factor is None and emissive_socket.is_linked:
- # In glTF, the default emissiveFactor is all zeros, so if an emission texture is connected,
- # we have to manually set it to all ones.
- factor = [1.0, 1.0, 1.0]
-
- if factor is None: factor = [0.0, 0.0, 0.0]
-
- # Handle Emission Strength
- strength_socket = None
- if emissive_socket.node.type == 'EMISSION':
- strength_socket = emissive_socket.node.inputs['Strength']
- elif 'Emission Strength' in emissive_socket.node.inputs:
- strength_socket = emissive_socket.node.inputs['Emission Strength']
- strength = (
- gltf2_blender_get.get_const_from_socket(strength_socket, kind='VALUE')
- if strength_socket is not None
- else None
- )
- if strength is not None:
- factor = [f * strength for f in factor]
-
- # Clamp to range [0,1]
- factor = [min(1.0, f) for f in factor]
-
- if factor == [0, 0, 0]: factor = None
-
- return factor
-
- return None
-
+ return export_emission_factor(blender_material, export_settings)
def __gather_emissive_texture(blender_material, export_settings):
- emissive = gltf2_blender_get.get_socket(blender_material, "Emissive")
- if emissive is None:
- emissive = gltf2_blender_get.get_socket_old(blender_material, "Emissive")
- emissive_texture, use_actives_uvmap_emissive = gltf2_blender_gather_texture_info.gather_texture_info(emissive, (emissive,), export_settings)
- return emissive_texture, ["emissiveTexture"] if use_actives_uvmap_emissive else None
+ return export_emission_texture(blender_material, export_settings)
-def __gather_extensions(blender_material, export_settings):
+def __gather_extensions(blender_material, emissive_factor, export_settings):
extensions = {}
# KHR_materials_clearcoat
actives_uvmaps = []
- clearcoat_extension, use_actives_uvmap_clearcoat = __gather_clearcoat_extension(blender_material, export_settings)
+ clearcoat_extension, use_actives_uvmap_clearcoat = export_clearcoat(blender_material, export_settings)
if clearcoat_extension:
extensions["KHR_materials_clearcoat"] = clearcoat_extension
actives_uvmaps.extend(use_actives_uvmap_clearcoat)
# KHR_materials_transmission
- transmission_extension, use_actives_uvmap_transmission = __gather_transmission_extension(blender_material, export_settings)
+ transmission_extension, use_actives_uvmap_transmission = export_transmission(blender_material, export_settings)
if transmission_extension:
extensions["KHR_materials_transmission"] = transmission_extension
actives_uvmaps.extend(use_actives_uvmap_transmission)
+ # KHR_materials_emissive_strength
+ if any([i>1.0 for i in emissive_factor or []]):
+ emissive_strength_extension = export_emission_strength_extension(emissive_factor, export_settings)
+ if emissive_strength_extension:
+ extensions["KHR_materials_emissive_strength"] = emissive_strength_extension
+
+ # KHR_materials_volume
+
+ volume_extension, use_actives_uvmap_volume_thickness = export_volume(blender_material, export_settings)
+ if volume_extension:
+ extensions["KHR_materials_volume"] = volume_extension
+ actives_uvmaps.extend(use_actives_uvmap_volume_thickness)
+
+ # KHR_materials_specular
+ specular_extension, use_actives_uvmap_specular = export_specular(blender_material, export_settings)
+ if specular_extension:
+ extensions["KHR_materials_specular"] = specular_extension
+ actives_uvmaps.extend(use_actives_uvmap_specular)
+
+ # KHR_materials_sheen
+ sheen_extension, use_actives_uvmap_sheen = export_sheen(blender_material, export_settings)
+ if sheen_extension:
+ extensions["KHR_materials_sheen"] = sheen_extension
+ actives_uvmaps.extend(use_actives_uvmap_sheen)
+
+ # KHR_materials_ior
+ # Keep this extension at the end, because we export it only if some others are exported
+ ior_extension = export_ior(blender_material, extensions, export_settings)
+ if ior_extension:
+ extensions["KHR_materials_ior"] = ior_extension
+
return extensions, actives_uvmaps if extensions else None
@@ -271,7 +281,7 @@ def __gather_normal_texture(blender_material, export_settings):
normal = gltf2_blender_get.get_socket(blender_material, "Normal")
if normal is None:
normal = gltf2_blender_get.get_socket_old(blender_material, "Normal")
- normal_texture, use_active_uvmap_normal = gltf2_blender_gather_texture_info.gather_material_normal_texture_info_class(
+ normal_texture, use_active_uvmap_normal, _ = gltf2_blender_gather_texture_info.gather_material_normal_texture_info_class(
normal,
(normal,),
export_settings)
@@ -283,20 +293,20 @@ def __gather_orm_texture(blender_material, export_settings):
# If not fully shared, return None, so the images will be cached and processed separately.
occlusion = gltf2_blender_get.get_socket(blender_material, "Occlusion")
- if occlusion is None or not __has_image_node_from_socket(occlusion):
+ if occlusion is None or not gltf2_blender_get.has_image_node_from_socket(occlusion):
occlusion = gltf2_blender_get.get_socket_old(blender_material, "Occlusion")
- if occlusion is None or not __has_image_node_from_socket(occlusion):
+ if occlusion is None or not gltf2_blender_get.has_image_node_from_socket(occlusion):
return None
metallic_socket = gltf2_blender_get.get_socket(blender_material, "Metallic")
roughness_socket = gltf2_blender_get.get_socket(blender_material, "Roughness")
- hasMetal = metallic_socket is not None and __has_image_node_from_socket(metallic_socket)
- hasRough = roughness_socket is not None and __has_image_node_from_socket(roughness_socket)
+ hasMetal = metallic_socket is not None and gltf2_blender_get.has_image_node_from_socket(metallic_socket)
+ hasRough = roughness_socket is not None and gltf2_blender_get.has_image_node_from_socket(roughness_socket)
if not hasMetal and not hasRough:
metallic_roughness = gltf2_blender_get.get_socket_old(blender_material, "MetallicRoughness")
- if metallic_roughness is None or not __has_image_node_from_socket(metallic_roughness):
+ if metallic_roughness is None or not gltf2_blender_get.has_image_node_from_socket(metallic_roughness):
return None
result = (occlusion, metallic_roughness)
elif not hasMetal:
@@ -313,7 +323,7 @@ def __gather_orm_texture(blender_material, export_settings):
return None
# Double-check this will past the filter in texture_info
- info, info_use_active_uvmap = gltf2_blender_gather_texture_info.gather_texture_info(result[0], result, export_settings)
+ info, info_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info(result[0], result, export_settings)
if info is None:
return None
@@ -323,7 +333,7 @@ def __gather_occlusion_texture(blender_material, orm_texture, export_settings):
occlusion = gltf2_blender_get.get_socket(blender_material, "Occlusion")
if occlusion is None:
occlusion = gltf2_blender_get.get_socket_old(blender_material, "Occlusion")
- occlusion_texture, use_active_uvmap_occlusion = gltf2_blender_gather_texture_info.gather_material_occlusion_texture_info_class(
+ occlusion_texture, use_active_uvmap_occlusion, _ = gltf2_blender_gather_texture_info.gather_material_occlusion_texture_info_class(
occlusion,
orm_texture or (occlusion,),
export_settings)
@@ -336,129 +346,7 @@ def __gather_pbr_metallic_roughness(blender_material, orm_texture, export_settin
orm_texture,
export_settings)
-def __has_image_node_from_socket(socket):
- result = gltf2_blender_search_node_tree.from_socket(
- socket,
- gltf2_blender_search_node_tree.FilterByType(bpy.types.ShaderNodeTexImage))
- if not result:
- return False
- return True
-
-def __gather_clearcoat_extension(blender_material, export_settings):
- clearcoat_enabled = False
- has_clearcoat_texture = False
- has_clearcoat_roughness_texture = False
-
- clearcoat_extension = {}
- clearcoat_roughness_slots = ()
-
- clearcoat_socket = gltf2_blender_get.get_socket(blender_material, 'Clearcoat')
- clearcoat_roughness_socket = gltf2_blender_get.get_socket(blender_material, 'Clearcoat Roughness')
- clearcoat_normal_socket = gltf2_blender_get.get_socket(blender_material, 'Clearcoat Normal')
-
- if isinstance(clearcoat_socket, bpy.types.NodeSocket) and not clearcoat_socket.is_linked:
- clearcoat_extension['clearcoatFactor'] = clearcoat_socket.default_value
- clearcoat_enabled = clearcoat_extension['clearcoatFactor'] > 0
- elif __has_image_node_from_socket(clearcoat_socket):
- fac = gltf2_blender_get.get_factor_from_socket(clearcoat_socket, kind='VALUE')
- # default value in glTF is 0.0, but if there is a texture without factor, use 1
- clearcoat_extension['clearcoatFactor'] = fac if fac != None else 1.0
- has_clearcoat_texture = True
- clearcoat_enabled = True
-
- if not clearcoat_enabled:
- return None, None
-
- if isinstance(clearcoat_roughness_socket, bpy.types.NodeSocket) and not clearcoat_roughness_socket.is_linked:
- clearcoat_extension['clearcoatRoughnessFactor'] = clearcoat_roughness_socket.default_value
- elif __has_image_node_from_socket(clearcoat_roughness_socket):
- fac = gltf2_blender_get.get_factor_from_socket(clearcoat_roughness_socket, kind='VALUE')
- # default value in glTF is 0.0, but if there is a texture without factor, use 1
- clearcoat_extension['clearcoatRoughnessFactor'] = fac if fac != None else 1.0
- has_clearcoat_roughness_texture = True
-
- # Pack clearcoat (R) and clearcoatRoughness (G) channels.
- if has_clearcoat_texture and has_clearcoat_roughness_texture:
- clearcoat_roughness_slots = (clearcoat_socket, clearcoat_roughness_socket,)
- elif has_clearcoat_texture:
- clearcoat_roughness_slots = (clearcoat_socket,)
- elif has_clearcoat_roughness_texture:
- clearcoat_roughness_slots = (clearcoat_roughness_socket,)
-
- use_actives_uvmaps = []
-
- if len(clearcoat_roughness_slots) > 0:
- if has_clearcoat_texture:
- clearcoat_texture, clearcoat_texture_use_active_uvmap = gltf2_blender_gather_texture_info.gather_texture_info(
- clearcoat_socket,
- clearcoat_roughness_slots,
- export_settings,
- )
- clearcoat_extension['clearcoatTexture'] = clearcoat_texture
- if clearcoat_texture_use_active_uvmap:
- use_actives_uvmaps.append("clearcoatTexture")
- if has_clearcoat_roughness_texture:
- clearcoat_roughness_texture, clearcoat_roughness_texture_use_active_uvmap = gltf2_blender_gather_texture_info.gather_texture_info(
- clearcoat_roughness_socket,
- clearcoat_roughness_slots,
- export_settings,
- )
- clearcoat_extension['clearcoatRoughnessTexture'] = clearcoat_roughness_texture
- if clearcoat_roughness_texture_use_active_uvmap:
- use_actives_uvmaps.append("clearcoatRoughnessTexture")
- if __has_image_node_from_socket(clearcoat_normal_socket):
- clearcoat_normal_texture, clearcoat_normal_texture_use_active_uvmap = gltf2_blender_gather_texture_info.gather_material_normal_texture_info_class(
- clearcoat_normal_socket,
- (clearcoat_normal_socket,),
- export_settings
- )
- clearcoat_extension['clearcoatNormalTexture'] = clearcoat_normal_texture
- if clearcoat_normal_texture_use_active_uvmap:
- use_actives_uvmaps.append("clearcoatNormalTexture")
-
- return Extension('KHR_materials_clearcoat', clearcoat_extension, False), use_actives_uvmaps
-
-def __gather_transmission_extension(blender_material, export_settings):
- transmission_enabled = False
- has_transmission_texture = False
-
- transmission_extension = {}
- transmission_slots = ()
-
- transmission_socket = gltf2_blender_get.get_socket(blender_material, 'Transmission')
-
- if isinstance(transmission_socket, bpy.types.NodeSocket) and not transmission_socket.is_linked:
- transmission_extension['transmissionFactor'] = transmission_socket.default_value
- transmission_enabled = transmission_extension['transmissionFactor'] > 0
- elif __has_image_node_from_socket(transmission_socket):
- transmission_extension['transmissionFactor'] = 1.0
- has_transmission_texture = True
- transmission_enabled = True
-
- if not transmission_enabled:
- return None, None
-
- # Pack transmission channel (R).
- if has_transmission_texture:
- transmission_slots = (transmission_socket,)
-
- use_actives_uvmaps = []
-
- if len(transmission_slots) > 0:
- combined_texture, use_active_uvmap = gltf2_blender_gather_texture_info.gather_texture_info(
- transmission_socket,
- transmission_slots,
- export_settings,
- )
- if has_transmission_texture:
- transmission_extension['transmissionTexture'] = combined_texture
- if use_active_uvmap:
- use_actives_uvmaps.append("transmissionTexture")
-
- return Extension('KHR_materials_transmission', transmission_extension, False), use_actives_uvmaps
-
-
-def __gather_material_unlit(blender_material, active_uvmap_index, export_settings):
+def __export_unlit(blender_material, active_uvmap_index, export_settings):
gltf2_unlit = gltf2_blender_gather_materials_unlit
info = gltf2_unlit.detect_shadeless_material(blender_material, export_settings)
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_clearcoat.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_clearcoat.py
new file mode 100644
index 00000000..65c164b4
--- /dev/null
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_clearcoat.py
@@ -0,0 +1,81 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2018-2022 The glTF-Blender-IO authors.
+
+import bpy
+from io_scene_gltf2.io.com.gltf2_io_extensions import Extension
+from io_scene_gltf2.blender.exp import gltf2_blender_get
+from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info
+
+def export_clearcoat(blender_material, export_settings):
+ clearcoat_enabled = False
+ has_clearcoat_texture = False
+ has_clearcoat_roughness_texture = False
+
+ clearcoat_extension = {}
+ clearcoat_roughness_slots = ()
+
+ clearcoat_socket = gltf2_blender_get.get_socket(blender_material, 'Clearcoat')
+ clearcoat_roughness_socket = gltf2_blender_get.get_socket(blender_material, 'Clearcoat Roughness')
+ clearcoat_normal_socket = gltf2_blender_get.get_socket(blender_material, 'Clearcoat Normal')
+
+ if isinstance(clearcoat_socket, bpy.types.NodeSocket) and not clearcoat_socket.is_linked:
+ clearcoat_extension['clearcoatFactor'] = clearcoat_socket.default_value
+ clearcoat_enabled = clearcoat_extension['clearcoatFactor'] > 0
+ elif gltf2_blender_get.has_image_node_from_socket(clearcoat_socket):
+ fac = gltf2_blender_get.get_factor_from_socket(clearcoat_socket, kind='VALUE')
+ # default value in glTF is 0.0, but if there is a texture without factor, use 1
+ clearcoat_extension['clearcoatFactor'] = fac if fac != None else 1.0
+ has_clearcoat_texture = True
+ clearcoat_enabled = True
+
+ if not clearcoat_enabled:
+ return None, None
+
+ if isinstance(clearcoat_roughness_socket, bpy.types.NodeSocket) and not clearcoat_roughness_socket.is_linked:
+ clearcoat_extension['clearcoatRoughnessFactor'] = clearcoat_roughness_socket.default_value
+ elif gltf2_blender_get.has_image_node_from_socket(clearcoat_roughness_socket):
+ fac = gltf2_blender_get.get_factor_from_socket(clearcoat_roughness_socket, kind='VALUE')
+ # default value in glTF is 0.0, but if there is a texture without factor, use 1
+ clearcoat_extension['clearcoatRoughnessFactor'] = fac if fac != None else 1.0
+ has_clearcoat_roughness_texture = True
+
+ # Pack clearcoat (R) and clearcoatRoughness (G) channels.
+ if has_clearcoat_texture and has_clearcoat_roughness_texture:
+ clearcoat_roughness_slots = (clearcoat_socket, clearcoat_roughness_socket,)
+ elif has_clearcoat_texture:
+ clearcoat_roughness_slots = (clearcoat_socket,)
+ elif has_clearcoat_roughness_texture:
+ clearcoat_roughness_slots = (clearcoat_roughness_socket,)
+
+ use_actives_uvmaps = []
+
+ if len(clearcoat_roughness_slots) > 0:
+ if has_clearcoat_texture:
+ clearcoat_texture, clearcoat_texture_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info(
+ clearcoat_socket,
+ clearcoat_roughness_slots,
+ export_settings,
+ )
+ clearcoat_extension['clearcoatTexture'] = clearcoat_texture
+ if clearcoat_texture_use_active_uvmap:
+ use_actives_uvmaps.append("clearcoatTexture")
+ if has_clearcoat_roughness_texture:
+ clearcoat_roughness_texture, clearcoat_roughness_texture_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info(
+ clearcoat_roughness_socket,
+ clearcoat_roughness_slots,
+ export_settings,
+ )
+ clearcoat_extension['clearcoatRoughnessTexture'] = clearcoat_roughness_texture
+ if clearcoat_roughness_texture_use_active_uvmap:
+ use_actives_uvmaps.append("clearcoatRoughnessTexture")
+ if gltf2_blender_get.has_image_node_from_socket(clearcoat_normal_socket):
+ clearcoat_normal_texture, clearcoat_normal_texture_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_material_normal_texture_info_class(
+ clearcoat_normal_socket,
+ (clearcoat_normal_socket,),
+ export_settings
+ )
+ clearcoat_extension['clearcoatNormalTexture'] = clearcoat_normal_texture
+ if clearcoat_normal_texture_use_active_uvmap:
+ use_actives_uvmaps.append("clearcoatNormalTexture")
+
+ return Extension('KHR_materials_clearcoat', clearcoat_extension, False), use_actives_uvmaps \ No newline at end of file
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_emission.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_emission.py
new file mode 100644
index 00000000..562fc19d
--- /dev/null
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_emission.py
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2018-2022 The glTF-Blender-IO authors.
+
+import bpy
+from io_scene_gltf2.io.com.gltf2_io_extensions import Extension
+from io_scene_gltf2.blender.exp import gltf2_blender_get
+from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info
+
+def export_emission_factor(blender_material, export_settings):
+ emissive_socket = gltf2_blender_get.get_socket(blender_material, "Emissive")
+ if emissive_socket is None:
+ emissive_socket = gltf2_blender_get.get_socket_old(blender_material, "EmissiveFactor")
+ if isinstance(emissive_socket, bpy.types.NodeSocket):
+ if export_settings['gltf_image_format'] != "NONE":
+ factor = gltf2_blender_get.get_factor_from_socket(emissive_socket, kind='RGB')
+ else:
+ factor = gltf2_blender_get.get_const_from_default_value_socket(emissive_socket, kind='RGB')
+
+ if factor is None and emissive_socket.is_linked:
+ # In glTF, the default emissiveFactor is all zeros, so if an emission texture is connected,
+ # we have to manually set it to all ones.
+ factor = [1.0, 1.0, 1.0]
+
+ if factor is None: factor = [0.0, 0.0, 0.0]
+
+ # Handle Emission Strength
+ strength_socket = None
+ if emissive_socket.node.type == 'EMISSION':
+ strength_socket = emissive_socket.node.inputs['Strength']
+ elif 'Emission Strength' in emissive_socket.node.inputs:
+ strength_socket = emissive_socket.node.inputs['Emission Strength']
+ strength = (
+ gltf2_blender_get.get_const_from_socket(strength_socket, kind='VALUE')
+ if strength_socket is not None
+ else None
+ )
+ if strength is not None:
+ factor = [f * strength for f in factor]
+
+ # Clamp to range [0,1]
+ # Official glTF clamp to range [0,1]
+ # If we are outside, we need to use extension KHR_materials_emissive_strength
+
+ if factor == [0, 0, 0]: factor = None
+
+ return factor
+
+ return None
+
+def export_emission_texture(blender_material, export_settings):
+ emissive = gltf2_blender_get.get_socket(blender_material, "Emissive")
+ if emissive is None:
+ emissive = gltf2_blender_get.get_socket_old(blender_material, "Emissive")
+ emissive_texture, use_actives_uvmap_emissive, _ = gltf2_blender_gather_texture_info.gather_texture_info(emissive, (emissive,), export_settings)
+ return emissive_texture, ["emissiveTexture"] if use_actives_uvmap_emissive else None
+
+def export_emission_strength_extension(emissive_factor, export_settings):
+ emissive_strength_extension = {}
+ emissive_strength_extension['emissiveStrength'] = max(emissive_factor)
+
+ return Extension('KHR_materials_emissive_strength', emissive_strength_extension, False) \ No newline at end of file
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_ior.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_ior.py
new file mode 100644
index 00000000..fc219c01
--- /dev/null
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_ior.py
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2018-2022 The glTF-Blender-IO authors.
+
+from io_scene_gltf2.io.com.gltf2_io_extensions import Extension
+from io_scene_gltf2.blender.exp import gltf2_blender_get
+from io_scene_gltf2.io.com.gltf2_io_constants import GLTF_IOR
+
+def export_ior(blender_material, extensions, export_settings):
+ ior_socket = gltf2_blender_get.get_socket(blender_material, 'IOR')
+
+ if not ior_socket:
+ return None
+
+ # We don't manage case where socket is linked, always check default value
+ if ior_socket.is_linked:
+ # TODOExt: add warning?
+ return None
+
+ if ior_socket.default_value == GLTF_IOR:
+ return None
+
+ # Export only if the following extensions are exported:
+ need_to_export_ior = [
+ 'KHR_materials_transmission',
+ 'KHR_materials_volume',
+ 'KHR_materials_specular'
+ ]
+
+ if not any([e in extensions.keys() for e in need_to_export_ior]):
+ return None
+
+ ior_extension = {}
+ ior_extension['ior'] = ior_socket.default_value
+
+ return Extension('KHR_materials_ior', ior_extension, False) \ No newline at end of file
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_pbr_metallic_roughness.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_pbr_metallic_roughness.py
index 0b40ffd6..a5929c05 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_pbr_metallic_roughness.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_pbr_metallic_roughness.py
@@ -15,8 +15,8 @@ def gather_material_pbr_metallic_roughness(blender_material, orm_texture, export
if not __filter_pbr_material(blender_material, export_settings):
return None, None
- base_color_texture, use_active_uvmap_base_color_texture = __gather_base_color_texture(blender_material, export_settings)
- metallic_roughness_texture, use_active_uvmap_metallic_roughness_texture = __gather_metallic_roughness_texture(blender_material, orm_texture, export_settings)
+ base_color_texture, use_active_uvmap_base_color_texture, _ = __gather_base_color_texture(blender_material, export_settings)
+ metallic_roughness_texture, use_active_uvmap_metallic_roughness_texture, _ = __gather_metallic_roughness_texture(blender_material, orm_texture, export_settings)
material = gltf2_io.MaterialPBRMetallicRoughness(
base_color_factor=__gather_base_color_factor(blender_material, export_settings),
@@ -92,7 +92,7 @@ def __gather_base_color_texture(blender_material, export_settings):
if socket is not None and __has_image_node_from_socket(socket)
)
if not inputs:
- return None, None
+ return None, None, None
return gltf2_blender_gather_texture_info.gather_texture_info(inputs[0], inputs, export_settings)
@@ -128,7 +128,7 @@ def __gather_metallic_roughness_texture(blender_material, orm_texture, export_se
if not hasMetal and not hasRough:
metallic_roughness = gltf2_blender_get.get_socket_old(blender_material, "MetallicRoughness")
if metallic_roughness is None or not __has_image_node_from_socket(metallic_roughness):
- return None, None
+ return None, None, None
texture_input = (metallic_roughness,)
elif not hasMetal:
texture_input = (roughness_socket,)
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_sheen.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_sheen.py
new file mode 100644
index 00000000..03625ecb
--- /dev/null
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_sheen.py
@@ -0,0 +1,68 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2018-2022 The glTF-Blender-IO authors.
+
+import bpy
+from io_scene_gltf2.io.com.gltf2_io_extensions import Extension
+from io_scene_gltf2.blender.exp import gltf2_blender_get
+from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info
+
+
+def export_sheen(blender_material, export_settings):
+ sheen_extension = {}
+
+ sheenColor_socket = gltf2_blender_get.get_socket(blender_material, "sheenColor")
+ sheenRoughness_socket = gltf2_blender_get.get_socket(blender_material, "sheenRoughness")
+
+ if sheenColor_socket is None or sheenRoughness_socket is None:
+ return None, None
+
+ sheenColor_non_linked = isinstance(sheenColor_socket, bpy.types.NodeSocket) and not sheenColor_socket.is_linked
+ sheenRoughness_non_linked = isinstance(sheenRoughness_socket, bpy.types.NodeSocket) and not sheenRoughness_socket.is_linked
+
+
+ use_actives_uvmaps = []
+
+ if sheenColor_non_linked is True:
+ color = sheenColor_socket.default_value[:3]
+ if color != (0.0, 0.0, 0.0):
+ sheen_extension['sheenColorFactor'] = color
+ else:
+ # Factor
+ fac = gltf2_blender_get.get_factor_from_socket(sheenColor_socket, kind='RGB')
+ if fac is not None and fac != [0.0, 0.0, 0.0]:
+ sheen_extension['sheenColorFactor'] = fac
+
+ # Texture
+ if gltf2_blender_get.has_image_node_from_socket(sheenColor_socket):
+ original_sheenColor_texture, original_sheenColor_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info(
+ sheenColor_socket,
+ (sheenColor_socket,),
+ export_settings,
+ )
+ sheen_extension['sheenColorTexture'] = original_sheenColor_texture
+ if original_sheenColor_use_active_uvmap:
+ use_actives_uvmaps.append("sheenColorTexture")
+
+
+ if sheenRoughness_non_linked is True:
+ fac = sheenRoughness_socket.default_value
+ if fac != 0.0:
+ sheen_extension['sheenRoughnessFactor'] = fac
+ else:
+ # Factor
+ fac = gltf2_blender_get.get_factor_from_socket(sheenRoughness_socket, kind='VALUE')
+ if fac is not None and fac != 0.0:
+ sheen_extension['sheenRoughnessFactor'] = fac
+
+ # Texture
+ if gltf2_blender_get.has_image_node_from_socket(sheenRoughness_socket):
+ original_sheenRoughness_texture, original_sheenRoughness_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info(
+ sheenRoughness_socket,
+ (sheenRoughness_socket,),
+ export_settings,
+ )
+ sheen_extension['sheenRoughnessTexture'] = original_sheenRoughness_texture
+ if original_sheenRoughness_use_active_uvmap:
+ use_actives_uvmaps.append("sheenRoughnessTexture")
+
+ return Extension('KHR_materials_sheen', sheen_extension, False), use_actives_uvmaps
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_specular.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_specular.py
new file mode 100644
index 00000000..22414b13
--- /dev/null
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_specular.py
@@ -0,0 +1,168 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2018-2022 The glTF-Blender-IO authors.
+
+import bpy
+from io_scene_gltf2.io.com.gltf2_io_extensions import Extension
+from io_scene_gltf2.blender.exp import gltf2_blender_get
+from io_scene_gltf2.io.com.gltf2_io_constants import GLTF_IOR
+from io_scene_gltf2.blender.com.gltf2_blender_default import BLENDER_SPECULAR, BLENDER_SPECULAR_TINT
+from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info
+
+
+
+def export_original_specular(blender_material, export_settings):
+ specular_extension = {}
+
+ original_specular_socket = gltf2_blender_get.get_socket_old(blender_material, 'Specular')
+ original_specularcolor_socket = gltf2_blender_get.get_socket_old(blender_material, 'Specular Color')
+
+ if original_specular_socket is None or original_specularcolor_socket is None:
+ return None, None
+
+ specular_non_linked = isinstance(original_specular_socket, bpy.types.NodeSocket) and not original_specular_socket.is_linked
+ specularcolor_non_linked = isinstance(original_specularcolor_socket, bpy.types.NodeSocket) and not original_specularcolor_socket.is_linked
+
+
+ use_actives_uvmaps = []
+
+ if specular_non_linked is True:
+ fac = original_specular_socket.default_value
+ if fac != 1.0:
+ specular_extension['specularFactor'] = fac
+ else:
+ # Factor
+ fac = gltf2_blender_get.get_factor_from_socket(original_specular_socket, kind='VALUE')
+ if fac is not None and fac != 1.0:
+ specular_extension['specularFactor'] = fac
+
+ # Texture
+ if gltf2_blender_get.has_image_node_from_socket(original_specular_socket):
+ original_specular_texture, original_specular_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info(
+ original_specular_socket,
+ (original_specular_socket,),
+ export_settings,
+ )
+ specular_extension['specularTexture'] = original_specular_texture
+ if original_specular_use_active_uvmap:
+ use_actives_uvmaps.append("specularTexture")
+
+
+ if specularcolor_non_linked is True:
+ color = original_specularcolor_socket.default_value[:3]
+ if color != [1.0, 1.0, 1.0]:
+ specular_extension['specularColorFactor'] = color
+ else:
+ # Factor
+ fac = gltf2_blender_get.get_factor_from_socket(original_specularcolor_socket, kind='RGB')
+ if fac is not None and fac != [1.0, 1.0, 1.0]:
+ specular_extension['specularColorFactor'] = fac
+
+ # Texture
+ if gltf2_blender_get.has_image_node_from_socket(original_specularcolor_socket):
+ original_specularcolor_texture, original_specularcolor_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info(
+ original_specularcolor_socket,
+ (original_specularcolor_socket,),
+ export_settings,
+ )
+ specular_extension['specularColorTexture'] = original_specularcolor_texture
+ if original_specularcolor_use_active_uvmap:
+ use_actives_uvmaps.append("specularColorTexture")
+
+ return Extension('KHR_materials_specular', specular_extension, False), use_actives_uvmaps
+
+def export_specular(blender_material, export_settings):
+
+ if export_settings['gltf_original_specular'] is True:
+ return export_original_specular(blender_material, export_settings)
+
+ specular_extension = {}
+ specular_ext_enabled = False
+
+ specular_socket = gltf2_blender_get.get_socket(blender_material, 'Specular')
+ specular_tint_socket = gltf2_blender_get.get_socket(blender_material, 'Specular Tint')
+ base_color_socket = gltf2_blender_get.get_socket(blender_material, 'Base Color')
+ transmission_socket = gltf2_blender_get.get_socket(blender_material, 'Transmission')
+ ior_socket = gltf2_blender_get.get_socket(blender_material, 'IOR')
+
+ if base_color_socket is None:
+ return None, None
+
+ # TODOExt replace by __has_image_node_from_socket calls
+ specular_not_linked = isinstance(specular_socket, bpy.types.NodeSocket) and not specular_socket.is_linked
+ specular_tint_not_linked = isinstance(specular_tint_socket, bpy.types.NodeSocket) and not specular_tint_socket.is_linked
+ base_color_not_linked = isinstance(base_color_socket, bpy.types.NodeSocket) and not base_color_socket.is_linked
+ transmission_not_linked = isinstance(transmission_socket, bpy.types.NodeSocket) and not transmission_socket.is_linked
+ ior_not_linked = isinstance(ior_socket, bpy.types.NodeSocket) and not ior_socket.is_linked
+
+ specular = specular_socket.default_value if specular_not_linked else None
+ specular_tint = specular_tint_socket.default_value if specular_tint_not_linked else None
+ transmission = transmission_socket.default_value if transmission_not_linked else None
+ ior = ior_socket.default_value if ior_not_linked else GLTF_IOR # textures not supported #TODOExt add warning?
+ base_color = base_color_socket.default_value[0:3]
+
+ no_texture = (transmission_not_linked and specular_not_linked and specular_tint_not_linked and
+ (specular_tint == 0.0 or (specular_tint != 0.0 and base_color_not_linked)))
+
+ use_actives_uvmaps = []
+
+ if no_texture:
+ if specular != BLENDER_SPECULAR or specular_tint != BLENDER_SPECULAR_TINT:
+ import numpy as np
+ # See https://gist.github.com/proog128/d627c692a6bbe584d66789a5a6437a33
+ specular_ext_enabled = True
+
+ def normalize(c):
+ luminance = lambda c: 0.3 * c[0] + 0.6 * c[1] + 0.1 * c[2]
+ assert(len(c) == 3)
+ l = luminance(c)
+ if l == 0:
+ return np.array(c)
+ return np.array([c[0] / l, c[1] / l, c[2] / l])
+
+ f0_from_ior = ((ior - 1)/(ior + 1))**2
+ tint_strength = (1 - specular_tint) + normalize(base_color) * specular_tint
+ specular_color = (1 - transmission) * (1 / f0_from_ior) * 0.08 * specular * tint_strength + transmission * tint_strength
+ specular_extension['specularColorFactor'] = list(specular_color)
+ else:
+ if specular_not_linked and specular == BLENDER_SPECULAR and specular_tint_not_linked and specular_tint == BLENDER_SPECULAR_TINT:
+ return None, None
+
+ # Trying to identify cases where exporting a texture will not be needed
+ if specular_not_linked and transmission_not_linked and \
+ specular == 0.0 and transmission == 0.0:
+
+ specular_extension['specularColorFactor'] = [0.0, 0.0, 0.0]
+ return specular_extension, []
+
+
+ # There will be a texture, with a complex calculation (no direct channel mapping)
+ sockets = (specular_socket, specular_tint_socket, base_color_socket, transmission_socket, ior_socket)
+ # Set primary socket having a texture
+ primary_socket = specular_socket
+ if specular_not_linked:
+ primary_socket = specular_tint_socket
+ if specular_tint_not_linked:
+ primary_socket = base_color_socket
+ if base_color_not_linked:
+ primary_socket = transmission_socket
+
+ specularColorTexture, use_active_uvmap, specularColorFactor = gltf2_blender_gather_texture_info.gather_texture_info(
+ primary_socket,
+ sockets,
+ export_settings,
+ filter_type='ANY')
+ if specularColorTexture is None:
+ return None, None
+ if use_active_uvmap:
+ use_actives_uvmaps.append("specularColorTexture")
+
+ specular_ext_enabled = True
+ specular_extension['specularColorTexture'] = specularColorTexture
+
+
+ if specularColorFactor is not None:
+ specular_extension['specularColorFactor'] = specularColorFactor
+
+
+ specular_extension = Extension('KHR_materials_specular', specular_extension, False) if specular_ext_enabled else None
+ return specular_extension, use_actives_uvmaps \ No newline at end of file
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_transmission.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_transmission.py
new file mode 100644
index 00000000..fdc5d8c7
--- /dev/null
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_transmission.py
@@ -0,0 +1,47 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2018-2022 The glTF-Blender-IO authors.
+
+import bpy
+from io_scene_gltf2.io.com.gltf2_io_extensions import Extension
+from io_scene_gltf2.blender.exp import gltf2_blender_get
+from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info
+
+def export_transmission(blender_material, export_settings):
+ transmission_enabled = False
+ has_transmission_texture = False
+
+ transmission_extension = {}
+ transmission_slots = ()
+
+ transmission_socket = gltf2_blender_get.get_socket(blender_material, 'Transmission')
+
+ if isinstance(transmission_socket, bpy.types.NodeSocket) and not transmission_socket.is_linked:
+ transmission_extension['transmissionFactor'] = transmission_socket.default_value
+ transmission_enabled = transmission_extension['transmissionFactor'] > 0
+ elif gltf2_blender_get.has_image_node_from_socket(transmission_socket):
+ fac = gltf2_blender_get.get_factor_from_socket(transmission_socket, kind='VALUE')
+ transmission_extension['transmissionFactor'] = fac if fac is not None else 1.0
+ has_transmission_texture = True
+ transmission_enabled = True
+
+ if not transmission_enabled:
+ return None, None
+
+ # Pack transmission channel (R).
+ if has_transmission_texture:
+ transmission_slots = (transmission_socket,)
+
+ use_actives_uvmaps = []
+
+ if len(transmission_slots) > 0:
+ combined_texture, use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info(
+ transmission_socket,
+ transmission_slots,
+ export_settings,
+ )
+ if has_transmission_texture:
+ transmission_extension['transmissionTexture'] = combined_texture
+ if use_active_uvmap:
+ use_actives_uvmaps.append("transmissionTexture")
+
+ return Extension('KHR_materials_transmission', transmission_extension, False), use_actives_uvmaps \ No newline at end of file
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_unlit.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_unlit.py
index e104b7f1..b501f98f 100644
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_unlit.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_unlit.py
@@ -1,9 +1,9 @@
# SPDX-License-Identifier: Apache-2.0
-# Copyright 2018-2021 The glTF-Blender-IO authors.
+# Copyright 2018-2022 The glTF-Blender-IO authors.
from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info
from io_scene_gltf2.blender.exp import gltf2_blender_get
-
+from io_scene_gltf2.io.com.gltf2_io_extensions import Extension
def detect_shadeless_material(blender_material, export_settings):
"""Detect if this material is "shadeless" ie. should be exported
@@ -127,7 +127,7 @@ def gather_base_color_texture(info, export_settings):
# because gather_image determines how to pack images based on the
# names of sockets, and the names are hard-coded to a Principled
# style graph.
- unlit_texture, unlit_use_active_uvmap = gltf2_blender_gather_texture_info.gather_texture_info(
+ unlit_texture, unlit_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info(
sockets[0],
sockets,
export_settings,
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_variants.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_variants.py
new file mode 100644
index 00000000..4c452e6a
--- /dev/null
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_variants.py
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2018-2022 The glTF-Blender-IO authors.
+
+import bpy
+from typing import Dict, Any
+from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached
+from io_scene_gltf2.io.com import gltf2_io_variants
+
+
+@cached
+def gather_variant(variant_idx, export_settings) -> Dict[str, Any]:
+
+ variant = gltf2_io_variants.Variant(
+ name=bpy.data.scenes[0].gltf2_KHR_materials_variants_variants[variant_idx].name,
+ extensions=None,
+ extras=None
+ )
+ return variant.to_dict()
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_volume.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_volume.py
new file mode 100644
index 00000000..8a69e3f6
--- /dev/null
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_volume.py
@@ -0,0 +1,75 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2018-2022 The glTF-Blender-IO authors.
+
+import bpy
+from io_scene_gltf2.io.com.gltf2_io_extensions import Extension
+from io_scene_gltf2.blender.exp import gltf2_blender_get
+from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info
+
+
+def export_volume(blender_material, export_settings):
+ # Implementation based on https://github.com/KhronosGroup/glTF-Blender-IO/issues/1454#issuecomment-928319444
+
+ # If no transmission --> No volume
+ transmission_enabled = False
+ transmission_socket = gltf2_blender_get.get_socket(blender_material, 'Transmission')
+ if isinstance(transmission_socket, bpy.types.NodeSocket) and not transmission_socket.is_linked:
+ transmission_enabled = transmission_socket.default_value > 0
+ elif gltf2_blender_get.has_image_node_from_socket(transmission_socket):
+ transmission_enabled = True
+
+ if transmission_enabled is False:
+ return None, None
+
+ volume_extension = {}
+ has_thickness_texture = False
+ thickness_slots = ()
+
+ thicknesss_socket = gltf2_blender_get.get_socket_old(blender_material, 'Thickness')
+ if thicknesss_socket is None:
+ # If no thickness (here because there is no glTF Material Output node), no volume extension export
+ return None, None
+
+ density_socket = gltf2_blender_get.get_socket(blender_material, 'Density', volume=True)
+ attenuation_color_socket = gltf2_blender_get.get_socket(blender_material, 'Color', volume=True)
+ # Even if density or attenuation are not set, we export volume extension
+
+ if isinstance(attenuation_color_socket, bpy.types.NodeSocket):
+ rgb = gltf2_blender_get.get_const_from_default_value_socket(attenuation_color_socket, kind='RGB')
+ volume_extension['attenuationColor'] = rgb
+
+ if isinstance(density_socket, bpy.types.NodeSocket):
+ density = gltf2_blender_get.get_const_from_default_value_socket(density_socket, kind='VALUE')
+ volume_extension['attenuationDistance'] = 1.0 / density if density != 0 else None # infinity (Using None as glTF default)
+
+
+ if isinstance(thicknesss_socket, bpy.types.NodeSocket) and not thicknesss_socket.is_linked:
+ val = thicknesss_socket.default_value
+ if val == 0.0:
+ # If no thickness, no volume extension export
+ return None, None
+ volume_extension['thicknessFactor'] = val
+ elif gltf2_blender_get.has_image_node_from_socket(thicknesss_socket):
+ fac = gltf2_blender_get.get_factor_from_socket(thicknesss_socket, kind='VALUE')
+ # default value in glTF is 0.0, but if there is a texture without factor, use 1
+ volume_extension['thicknessFactor'] = fac if fac != None else 1.0
+ has_thickness_texture = True
+
+ # Pack thickness channel (R).
+ if has_thickness_texture:
+ thickness_slots = (thicknesss_socket,)
+
+ use_actives_uvmaps = []
+
+ if len(thickness_slots) > 0:
+ combined_texture, use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info(
+ thicknesss_socket,
+ thickness_slots,
+ export_settings,
+ )
+ if has_thickness_texture:
+ volume_extension['thicknessTexture'] = combined_texture
+ if use_active_uvmap:
+ use_actives_uvmaps.append("thicknessTexture")
+
+ return Extension('KHR_materials_volume', volume_extension, False), use_actives_uvmaps \ No newline at end of file
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitive_attributes.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitive_attributes.py
index 72f0268c..1af588b9 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitive_attributes.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitive_attributes.py
@@ -44,7 +44,7 @@ def array_to_accessor(array, component_type, data_type, include_max_and_min=Fals
amin = np.amin(array, axis=0).tolist()
return gltf2_io.Accessor(
- buffer_view=gltf2_io_binary_data.BinaryData(array.tobytes()),
+ buffer_view=gltf2_io_binary_data.BinaryData(array.tobytes(), gltf2_io_constants.BufferViewTarget.ARRAY_BUFFER),
byte_offset=None,
component_type=component_type,
count=len(array),
@@ -124,28 +124,34 @@ def __gather_colors(blender_primitive, export_settings):
color_index = 0
color_id = 'COLOR_' + str(color_index)
while blender_primitive["attributes"].get(color_id) is not None:
- colors = blender_primitive["attributes"][color_id]
+ colors = blender_primitive["attributes"][color_id]["data"]
if type(colors) is not np.ndarray:
colors = np.array(colors, dtype=np.float32)
colors = colors.reshape(len(colors) // 4, 4)
- # Convert to normalized ushorts
- colors *= 65535
- colors += 0.5 # bias for rounding
- colors = colors.astype(np.uint16)
+ if blender_primitive["attributes"][color_id]["norm"] is True:
+ comp_type = gltf2_io_constants.ComponentType.UnsignedShort
+
+ # Convert to normalized ushorts
+ colors *= 65535
+ colors += 0.5 # bias for rounding
+ colors = colors.astype(np.uint16)
+
+ else:
+ comp_type = gltf2_io_constants.ComponentType.Float
attributes[color_id] = gltf2_io.Accessor(
- buffer_view=gltf2_io_binary_data.BinaryData(colors.tobytes()),
+ buffer_view=gltf2_io_binary_data.BinaryData(colors.tobytes(), gltf2_io_constants.BufferViewTarget.ARRAY_BUFFER),
byte_offset=None,
- component_type=gltf2_io_constants.ComponentType.UnsignedShort,
+ component_type=comp_type,
count=len(colors),
extensions=None,
extras=None,
max=None,
min=None,
name=None,
- normalized=True,
+ normalized=blender_primitive["attributes"][color_id]["norm"],
sparse=None,
type=gltf2_io_constants.DataType.Vec4,
)
@@ -167,6 +173,13 @@ def __gather_skins(blender_primitive, export_settings):
max_bone_set_index += 1
max_bone_set_index -= 1
+ # Here, a set represents a group of 4 weights.
+ # So max_bone_set_index value:
+ # if -1 => No weights
+ # if 1 => Max 4 weights
+ # if 2 => Max 8 weights
+ # etc...
+
# If no skinning
if max_bone_set_index < 0:
return attributes
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitives.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitives.py
index 367c30f5..576a1418 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitives.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitives.py
@@ -12,10 +12,12 @@ from io_scene_gltf2.blender.exp import gltf2_blender_extract
from io_scene_gltf2.blender.exp import gltf2_blender_gather_accessors
from io_scene_gltf2.blender.exp import gltf2_blender_gather_primitive_attributes
from io_scene_gltf2.blender.exp import gltf2_blender_gather_materials
+from io_scene_gltf2.blender.exp import gltf2_blender_gather_materials_variants
from io_scene_gltf2.io.com import gltf2_io
from io_scene_gltf2.io.exp import gltf2_io_binary_data
from io_scene_gltf2.io.com import gltf2_io_constants
+from io_scene_gltf2.io.com import gltf2_io_extensions
from io_scene_gltf2.io.com.gltf2_io_debug import print_console
@@ -86,7 +88,7 @@ def gather_primitives(
primitive = gltf2_io.MeshPrimitive(
attributes=internal_primitive['attributes'],
- extensions=None,
+ extensions=__gather_extensions(blender_mesh, material_idx, active_uvmap_idx, export_settings),
extras=None,
indices=internal_primitive['indices'],
material=material,
@@ -148,7 +150,7 @@ def __gather_indices(blender_primitive, blender_mesh, modifiers, export_settings
return None
element_type = gltf2_io_constants.DataType.Scalar
- binary_data = gltf2_io_binary_data.BinaryData(indices.tobytes())
+ binary_data = gltf2_io_binary_data.BinaryData(indices.tobytes(), bufferViewTarget=gltf2_io_constants.BufferViewTarget.ELEMENT_ARRAY_BUFFER)
return gltf2_blender_gather_accessors.gather_accessor(
binary_data,
component_type,
@@ -214,3 +216,55 @@ def __gather_targets(blender_primitive, blender_mesh, modifiers, export_settings
morph_index += 1
return targets
return None
+
+def __gather_extensions(blender_mesh,
+ material_idx: int,
+ active_uvmap_idx,
+ export_settings):
+ extensions = {}
+
+ if bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is False:
+ return None
+
+ if bpy.data.scenes[0].get('gltf2_KHR_materials_variants_variants') is None:
+ return None
+ if len(bpy.data.scenes[0]['gltf2_KHR_materials_variants_variants']) == 0:
+ return None
+
+ # Material idx is the slot idx. Retrieve associated variant, if any
+ mapping = []
+ for i in [v for v in blender_mesh.gltf2_variant_mesh_data if v.material_slot_index == material_idx]:
+ variants = []
+ for idx, v in enumerate(i.variants):
+ if v.variant.variant_idx in [o.variant.variant_idx for o in i.variants[:idx]]:
+ # Avoid duplicates
+ continue
+ vari = gltf2_blender_gather_materials_variants.gather_variant(v.variant.variant_idx, export_settings)
+ if vari is not None:
+ variant_extension = gltf2_io_extensions.ChildOfRootExtension(
+ name="KHR_materials_variants",
+ path=["variants"],
+ extension=vari
+ )
+ variants.append(variant_extension)
+ if len(variants) > 0:
+ if i.material:
+ mat = gltf2_blender_gather_materials.gather_material(
+ i.material,
+ active_uvmap_idx,
+ export_settings
+ )
+ else:
+ # empty slot
+ mat = None
+ mapping.append({'material': mat, 'variants': variants})
+
+ if len(mapping) > 0:
+ extensions["KHR_materials_variants"] = gltf2_io_extensions.Extension(
+ name="KHR_materials_variants",
+ extension={
+ "mappings": mapping
+ }
+ )
+
+ return extensions if extensions else None
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture.py
index e8c6baf1..ccfd42e5 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture.py
@@ -26,24 +26,26 @@ def gather_texture(
"""
if not __filter_texture(blender_shader_sockets, export_settings):
- return None
+ return None, None
+
+ source, factor = __gather_source(blender_shader_sockets, export_settings)
texture = gltf2_io.Texture(
extensions=__gather_extensions(blender_shader_sockets, export_settings),
extras=__gather_extras(blender_shader_sockets, export_settings),
name=__gather_name(blender_shader_sockets, export_settings),
sampler=__gather_sampler(blender_shader_sockets, export_settings),
- source=__gather_source(blender_shader_sockets, export_settings)
+ source= source
)
# although valid, most viewers can't handle missing source properties
# This can have None source for "keep original", when original can't be found
if texture.source is None:
- return None
+ return None, None
export_user_extensions('gather_texture_hook', export_settings, texture, blender_shader_sockets)
- return texture
+ return texture, factor
def __filter_texture(blender_shader_sockets, export_settings):
@@ -66,13 +68,14 @@ def __gather_name(blender_shader_sockets, export_settings):
def __gather_sampler(blender_shader_sockets, export_settings):
- shader_nodes = [__get_tex_from_socket(socket).shader_node for socket in blender_shader_sockets]
+ shader_nodes = [__get_tex_from_socket(socket) for socket in blender_shader_sockets]
if len(shader_nodes) > 1:
gltf2_io_debug.print_console("WARNING",
"More than one shader node tex image used for a texture. "
"The resulting glTF sampler will behave like the first shader node tex image.")
+ first_valid_shader_node = next(filter(lambda x: x is not None, shader_nodes)).shader_node
return gltf2_blender_gather_sampler.gather_sampler(
- shader_nodes[0],
+ first_valid_shader_node,
export_settings)
@@ -81,7 +84,7 @@ def __gather_source(blender_shader_sockets, export_settings):
# Helpers
-
+# TODOExt deduplicate
def __get_tex_from_socket(socket):
result = gltf2_blender_search_node_tree.from_socket(
socket,
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture_info.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture_info.py
index 15b101ad..5fe2da32 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture_info.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture_info.py
@@ -19,14 +19,14 @@ from io_scene_gltf2.io.exp.gltf2_io_user_extensions import export_user_extension
# occlusion the primary_socket would be the occlusion socket, and
# blender_shader_sockets would be the (O,R,M) sockets.
-def gather_texture_info(primary_socket, blender_shader_sockets, export_settings):
- return __gather_texture_info_helper(primary_socket, blender_shader_sockets, 'DEFAULT', export_settings)
+def gather_texture_info(primary_socket, blender_shader_sockets, export_settings, filter_type='ALL'):
+ return __gather_texture_info_helper(primary_socket, blender_shader_sockets, 'DEFAULT', filter_type, export_settings)
-def gather_material_normal_texture_info_class(primary_socket, blender_shader_sockets, export_settings):
- return __gather_texture_info_helper(primary_socket, blender_shader_sockets, 'NORMAL', export_settings)
+def gather_material_normal_texture_info_class(primary_socket, blender_shader_sockets, export_settings, filter_type='ALL'):
+ return __gather_texture_info_helper(primary_socket, blender_shader_sockets, 'NORMAL', filter_type, export_settings)
-def gather_material_occlusion_texture_info_class(primary_socket, blender_shader_sockets, export_settings):
- return __gather_texture_info_helper(primary_socket, blender_shader_sockets, 'OCCLUSION', export_settings)
+def gather_material_occlusion_texture_info_class(primary_socket, blender_shader_sockets, export_settings, filter_type='ALL'):
+ return __gather_texture_info_helper(primary_socket, blender_shader_sockets, 'OCCLUSION', filter_type, export_settings)
@cached
@@ -34,16 +34,19 @@ def __gather_texture_info_helper(
primary_socket: bpy.types.NodeSocket,
blender_shader_sockets: typing.Tuple[bpy.types.NodeSocket],
kind: str,
+ filter_type: str,
export_settings):
- if not __filter_texture_info(primary_socket, blender_shader_sockets, export_settings):
- return None, None
+ if not __filter_texture_info(primary_socket, blender_shader_sockets, filter_type, export_settings):
+ return None, None, None
tex_transform, tex_coord, use_active_uvmap = __gather_texture_transform_and_tex_coord(primary_socket, export_settings)
+ index, factor = __gather_index(blender_shader_sockets, export_settings)
+
fields = {
'extensions': __gather_extensions(tex_transform, export_settings),
'extras': __gather_extras(blender_shader_sockets, export_settings),
- 'index': __gather_index(blender_shader_sockets, export_settings),
+ 'index': index,
'tex_coord': tex_coord
}
@@ -59,14 +62,14 @@ def __gather_texture_info_helper(
texture_info = gltf2_io.MaterialOcclusionTextureInfoClass(**fields)
if texture_info.index is None:
- return None, None
+ return None, None, None
export_user_extensions('gather_texture_info_hook', export_settings, texture_info, blender_shader_sockets)
- return texture_info, use_active_uvmap
+ return texture_info, use_active_uvmap, factor
-def __filter_texture_info(primary_socket, blender_shader_sockets, export_settings):
+def __filter_texture_info(primary_socket, blender_shader_sockets, filter_type, export_settings):
if primary_socket is None:
return False
if __get_tex_from_socket(primary_socket) is None:
@@ -75,9 +78,18 @@ def __filter_texture_info(primary_socket, blender_shader_sockets, export_setting
return False
if not all([elem is not None for elem in blender_shader_sockets]):
return False
- if any([__get_tex_from_socket(socket) is None for socket in blender_shader_sockets]):
- # sockets do not lead to a texture --> discard
- return False
+ if filter_type == "ALL":
+ # Check that all sockets link to texture
+ if any([__get_tex_from_socket(socket) is None for socket in blender_shader_sockets]):
+ # sockets do not lead to a texture --> discard
+ return False
+ elif filter_type == "ANY":
+ # Check that at least one socket link to texture
+ if all([__get_tex_from_socket(socket) is None for socket in blender_shader_sockets]):
+ return False
+ elif filter_type == "NONE":
+ # No check
+ pass
return True
@@ -163,7 +175,7 @@ def __gather_texture_transform_and_tex_coord(primary_socket, export_settings):
return texture_transform, texcoord_idx or None, use_active_uvmap
-
+# TODOExt deduplicate
def __get_tex_from_socket(socket):
result = gltf2_blender_search_node_tree.from_socket(
socket,
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py
index ba63e049..c654b445 100644
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py
@@ -95,10 +95,18 @@ class VExportTree:
bpy.context.window.scene = blender_scene
depsgraph = bpy.context.evaluated_depsgraph_get()
+ # Gather parent/children information once, as calling bobj.children is
+ # very expensive operation : takes O(len(bpy.data.objects)) time.
+ blender_children = dict()
+ for bobj in bpy.data.objects:
+ bparent = bobj.parent
+ blender_children.setdefault(bobj, [])
+ blender_children.setdefault(bparent, []).append(bobj)
+
for blender_object in [obj.original for obj in depsgraph.scene_eval.objects if obj.parent is None]:
- self.recursive_node_traverse(blender_object, None, None, Matrix.Identity(4))
+ self.recursive_node_traverse(blender_object, None, None, Matrix.Identity(4), blender_children)
- def recursive_node_traverse(self, blender_object, blender_bone, parent_uuid, parent_coll_matrix_world, armature_uuid=None, dupli_world_matrix=None):
+ def recursive_node_traverse(self, blender_object, blender_bone, parent_uuid, parent_coll_matrix_world, blender_children, armature_uuid=None, dupli_world_matrix=None):
node = VExportNode()
node.uuid = str(uuid.uuid4())
node.parent_uuid = parent_uuid
@@ -163,10 +171,16 @@ class VExportTree:
# So real world matrix is collection world_matrix @ "world_matrix" of object
node.matrix_world = parent_coll_matrix_world @ blender_object.matrix_world.copy()
if node.blender_type == VExportNode.CAMERA and self.export_settings[gltf2_blender_export_keys.CAMERAS]:
- correction = Quaternion((2**0.5/2, -2**0.5/2, 0.0, 0.0))
+ if self.export_settings[gltf2_blender_export_keys.YUP]:
+ correction = Quaternion((2**0.5/2, -2**0.5/2, 0.0, 0.0))
+ else:
+ correction = Matrix.Identity(4).to_quaternion()
node.matrix_world @= correction.to_matrix().to_4x4()
elif node.blender_type == VExportNode.LIGHT and self.export_settings[gltf2_blender_export_keys.LIGHTS]:
- correction = Quaternion((2**0.5/2, -2**0.5/2, 0.0, 0.0))
+ if self.export_settings[gltf2_blender_export_keys.YUP]:
+ correction = Quaternion((2**0.5/2, -2**0.5/2, 0.0, 0.0))
+ else:
+ correction = Matrix.Identity(4).to_quaternion()
node.matrix_world @= correction.to_matrix().to_4x4()
elif node.blender_type == VExportNode.BONE:
if self.export_settings['gltf_current_frame'] is True:
@@ -193,42 +207,42 @@ class VExportTree:
# standard children
if blender_bone is None and blender_object.is_instancer is False:
- for child_object in blender_object.children:
+ for child_object in blender_children[blender_object]:
if child_object.parent_bone:
# Object parented to bones
# Will be manage later
continue
else:
# Classic parenting
- self.recursive_node_traverse(child_object, None, node.uuid, parent_coll_matrix_world)
+ self.recursive_node_traverse(child_object, None, node.uuid, parent_coll_matrix_world, blender_children)
# Collections
if blender_object.instance_type == 'COLLECTION' and blender_object.instance_collection:
for dupli_object in blender_object.instance_collection.all_objects:
if dupli_object.parent is not None:
continue
- self.recursive_node_traverse(dupli_object, None, node.uuid, node.matrix_world)
+ self.recursive_node_traverse(dupli_object, None, node.uuid, node.matrix_world, blender_children)
# Armature : children are bones with no parent
if blender_object.type == "ARMATURE" and blender_bone is None:
for b in [b for b in blender_object.pose.bones if b.parent is None]:
- self.recursive_node_traverse(blender_object, b, node.uuid, parent_coll_matrix_world, node.uuid)
+ self.recursive_node_traverse(blender_object, b, node.uuid, parent_coll_matrix_world, blender_children, node.uuid)
# Bones
if blender_object.type == "ARMATURE" and blender_bone is not None:
for b in blender_bone.children:
- self.recursive_node_traverse(blender_object, b, node.uuid, parent_coll_matrix_world, armature_uuid)
+ self.recursive_node_traverse(blender_object, b, node.uuid, parent_coll_matrix_world, blender_children, armature_uuid)
# Object parented to bone
if blender_bone is not None:
- for child_object in [c for c in blender_object.children if c.parent_bone is not None and c.parent_bone == blender_bone.name]:
- self.recursive_node_traverse(child_object, None, node.uuid, parent_coll_matrix_world)
+ for child_object in [c for c in blender_children[blender_object] if c.parent_bone is not None and c.parent_bone == blender_bone.name]:
+ self.recursive_node_traverse(child_object, None, node.uuid, parent_coll_matrix_world, blender_children)
# Duplis
if blender_object.is_instancer is True and blender_object.instance_type != 'COLLECTION':
depsgraph = bpy.context.evaluated_depsgraph_get()
for (dupl, mat) in [(dup.object.original, dup.matrix_world.copy()) for dup in depsgraph.object_instances if dup.parent and id(dup.parent.original) == id(blender_object)]:
- self.recursive_node_traverse(dupl, None, node.uuid, parent_coll_matrix_world, dupli_world_matrix=mat)
+ self.recursive_node_traverse(dupl, None, node.uuid, parent_coll_matrix_world, blender_children, dupli_world_matrix=mat)
def get_all_objects(self):
return [n.uuid for n in self.nodes.values() if n.blender_type != VExportNode.BONE]
@@ -462,3 +476,20 @@ class VExportTree:
skin = gather_skin(n.uuid, self.export_settings)
skins.append(skin)
return skins
+
+ def variants_reset_to_original(self):
+ # Only if Variants are displayed and exported
+ if bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is False:
+ return
+ objects = [self.nodes[o].blender_object for o in self.get_all_node_of_type(VExportNode.OBJECT) if self.nodes[o].blender_object.type == "MESH" \
+ and self.nodes[o].blender_object.data.get('gltf2_variant_default_materials') is not None]
+ for obj in objects:
+ # loop on material slots ( primitives )
+ for mat_slot_idx, s in enumerate(obj.material_slots):
+ # Check if there is a default material for this slot
+ for i in obj.data.gltf2_variant_default_materials:
+ if i.material_slot_index == mat_slot_idx:
+ s.material = i.default_material
+ break
+
+ # If not found, keep current material as default
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_get.py b/io_scene_gltf2/blender/exp/gltf2_blender_get.py
index e38906e6..9e468186 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_get.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_get.py
@@ -4,9 +4,10 @@
import bpy
from mathutils import Vector, Matrix
-from ..com.gltf2_blender_material_helpers import get_gltf_node_name
+from ..com.gltf2_blender_material_helpers import get_gltf_node_name, get_gltf_node_old_name
from ...blender.com.gltf2_blender_conversion import texture_transform_blender_to_gltf
from io_scene_gltf2.io.com import gltf2_io_debug
+from io_scene_gltf2.blender.exp import gltf2_blender_search_node_tree
def get_animation_target(action_group: bpy.types.ActionGroup):
@@ -47,7 +48,7 @@ def get_node_socket(blender_material, type, name):
return None
-def get_socket(blender_material: bpy.types.Material, name: str):
+def get_socket(blender_material: bpy.types.Material, name: str, volume=False):
"""
For a given material input name, retrieve the corresponding node tree socket.
@@ -70,8 +71,15 @@ def get_socket(blender_material: bpy.types.Material, name: str):
elif name == "Background":
type = bpy.types.ShaderNodeBackground
name = "Color"
+ elif name == "sheenColor":
+ return get_node_socket(blender_material, bpy.types.ShaderNodeBsdfVelvet, "Color")
+ elif name == "sheenRoughness":
+ return get_node_socket(blender_material, bpy.types.ShaderNodeBsdfVelvet, "Sigma")
else:
- type = bpy.types.ShaderNodeBsdfPrincipled
+ if volume is False:
+ type = bpy.types.ShaderNodeBsdfPrincipled
+ else:
+ type = bpy.types.ShaderNodeVolumeAbsorption
return get_node_socket(blender_material, type, name)
@@ -86,11 +94,11 @@ def get_socket_old(blender_material: bpy.types.Material, name: str):
:param name: the name of the socket
:return: a blender NodeSocket
"""
- gltf_node_group_name = get_gltf_node_name().lower()
+ gltf_node_group_names = [get_gltf_node_name().lower(), get_gltf_node_old_name().lower()]
if blender_material.node_tree and blender_material.use_nodes:
nodes = [n for n in blender_material.node_tree.nodes if \
isinstance(n, bpy.types.ShaderNodeGroup) and \
- (n.node_tree.name.startswith('glTF Metallic Roughness') or n.node_tree.name.lower() == gltf_node_group_name)]
+ (n.node_tree.name.startswith('glTF Metallic Roughness') or n.node_tree.name.lower() in gltf_node_group_names)]
inputs = sum([[input for input in node.inputs if input.name == name] for node in nodes], [])
if inputs:
return inputs[0]
@@ -297,3 +305,12 @@ def previous_node(socket):
if prev_socket is not None:
return prev_socket.node
return None
+
+#TODOExt is this the same as __get_tex_from_socket from gather_image ?
+def has_image_node_from_socket(socket):
+ result = gltf2_blender_search_node_tree.from_socket(
+ socket,
+ gltf2_blender_search_node_tree.FilterByType(bpy.types.ShaderNodeTexImage))
+ if not result:
+ return False
+ return True
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_image.py b/io_scene_gltf2/blender/exp/gltf2_blender_image.py
index 8b9db89a..6730f479 100644
--- a/io_scene_gltf2/blender/exp/gltf2_blender_image.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_image.py
@@ -27,6 +27,18 @@ class FillWhite:
"""Fills a channel with all ones (1.0)."""
pass
+class StoreData:
+ def __init__(self, data):
+ """Store numeric data (not an image channel"""
+ self.data = data
+
+class StoreImage:
+ """
+ Store a channel with the channel src_chan from a Blender image.
+ This channel will be used for numpy calculation (no direct channel mapping)
+ """
+ def __init__(self, image: bpy.types.Image):
+ self.image = image
class ExportImage:
"""Custom image class.
@@ -55,9 +67,13 @@ class ExportImage:
def __init__(self, original=None):
self.fills = {}
+ self.stored = {}
- # In case of keeping original texture images
- self.original = original
+ self.original = original # In case of keeping original texture images
+ self.numpy_calc = None
+
+ def set_calc(self, numpy_calc):
+ self.numpy_calc = numpy_calc # In case of numpy calculation (no direct channel mapping)
@staticmethod
def from_blender_image(image: bpy.types.Image):
@@ -73,6 +89,12 @@ class ExportImage:
def fill_image(self, image: bpy.types.Image, dst_chan: Channel, src_chan: Channel):
self.fills[dst_chan] = FillImage(image, src_chan)
+ def store_data(self, identifier, data, type='Image'):
+ if type == "Image": # This is an image
+ self.stored[identifier] = StoreImage(data)
+ else: # This is a numeric value
+ self.stored[identifier] = StoreData(data)
+
def fill_white(self, dst_chan: Channel):
self.fills[dst_chan] = FillWhite()
@@ -81,7 +103,7 @@ class ExportImage:
def empty(self) -> bool:
if self.original is None:
- return not self.fills
+ return not (self.fills or self.stored)
else:
return False
@@ -103,7 +125,7 @@ class ExportImage:
len(set(fill.image.name for fill in self.fills.values())) == 1
)
- def encode(self, mime_type: Optional[str]) -> bytes:
+ def encode(self, mime_type: Optional[str]) -> Tuple[bytes, bool]:
self.file_format = {
"image/jpeg": "JPEG",
"image/png": "PNG"
@@ -111,10 +133,14 @@ class ExportImage:
# Happy path = we can just use an existing Blender image
if self.__on_happy_path():
- return self.__encode_happy()
+ return self.__encode_happy(), None
- # Unhappy path = we need to create the image self.fills describes.
- return self.__encode_unhappy()
+ # Unhappy path = we need to create the image self.fills describes or self.stores describes
+ if self.numpy_calc is None:
+ return self.__encode_unhappy(), None
+ else:
+ pixels, width, height, factor = self.numpy_calc(self.stored)
+ return self.__encode_from_numpy_array(pixels, (width, height)), factor
def __encode_happy(self) -> bytes:
return self.__encode_from_image(self.blender_image())
@@ -147,7 +173,7 @@ class ExportImage:
else:
# Image is the wrong size; make a temp copy and scale it.
with TmpImageGuard() as guard:
- _make_temp_image_copy(guard, src_image=image)
+ make_temp_image_copy(guard, src_image=image)
tmp_image = guard.image
tmp_image.scale(width, height)
tmp_image.pixels.foreach_get(tmp_buf)
@@ -197,7 +223,7 @@ class ExportImage:
# Copy to a temp image and save.
with TmpImageGuard() as guard:
- _make_temp_image_copy(guard, src_image=image)
+ make_temp_image_copy(guard, src_image=image)
tmp_image = guard.image
return _encode_temp_image(tmp_image, self.file_format)
@@ -228,7 +254,7 @@ class TmpImageGuard:
bpy.data.images.remove(self.image, do_unlink=True)
-def _make_temp_image_copy(guard: TmpImageGuard, src_image: bpy.types.Image):
+def make_temp_image_copy(guard: TmpImageGuard, src_image: bpy.types.Image):
"""Makes a temporary copy of src_image. Will be cleaned up with guard."""
guard.image = src_image.copy()
tmp_image = guard.image
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_texture_specular.py b/io_scene_gltf2/blender/exp/gltf2_blender_texture_specular.py
new file mode 100644
index 00000000..6321f128
--- /dev/null
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_texture_specular.py
@@ -0,0 +1,94 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2018-2022 The glTF-Blender-IO authors.
+
+import bpy
+import numpy as np
+from .gltf2_blender_gather_image import StoreImage, StoreData
+from .gltf2_blender_image import TmpImageGuard, make_temp_image_copy
+
+def specular_calculation(stored):
+
+ # See https://gist.github.com/proog128/d627c692a6bbe584d66789a5a6437a33
+
+ # Find all Blender images used
+ images = []
+ for fill in stored.values():
+ if isinstance(fill, StoreImage):
+ if fill.image not in images:
+ images.append(fill.image)
+
+ if not images:
+ # No ImageFills; use a 1x1 white pixel
+ pixels = np.array([1.0, 1.0, 1.0, 1.0], np.float32)
+ return pixels, 1, 1
+
+ width = max(image.size[0] for image in images)
+ height = max(image.size[1] for image in images)
+
+ buffers = {}
+
+ for identifier, image in [(ident, store.image) for (ident, store) in stored.items() if isinstance(store, StoreImage)]:
+ tmp_buf = np.empty(width * height * 4, np.float32)
+
+ if image.size[0] == width and image.size[1] == height:
+ image.pixels.foreach_get(tmp_buf)
+ else:
+ # Image is the wrong size; make a temp copy and scale it.
+ with TmpImageGuard() as guard:
+ make_temp_image_copy(guard, src_image=image)
+ tmp_image = guard.image
+ tmp_image.scale(width, height)
+ tmp_image.pixels.foreach_get(tmp_buf)
+
+ buffers[identifier] = np.reshape(tmp_buf, [width, height, 4])
+
+ # keep only needed channels
+ ## scalar
+ for i in ['specular', 'specular_tint', 'transmission']:
+ if i in buffers.keys():
+ buffers[i] = buffers[i][:,:,stored[i + "_channel"].data]
+ else:
+ buffers[i] = np.full((width, height, 1), stored[i].data)
+
+ # Vector 3
+ for i in ['base_color']:
+ if i in buffers.keys():
+ if i + "_channel" not in stored.keys():
+ buffers[i] = buffers[i][:,:,:3]
+ else:
+ # keep only needed channel
+ for c in range(3):
+ if c != stored[i+"_channel"].data:
+ buffers[i][:, :, c] = 0.0
+ buffers[i] = buffers[i][:,:,:3]
+ else:
+ buffers[i] = np.full((width, height, 3), stored[i].data[0:3])
+
+ ior = stored['ior'].data
+
+ # calculation
+ stack3 = lambda v: np.dstack([v]*3)
+
+ def normalize(c):
+ luminance = lambda c: 0.3 * c[:,:,0] + 0.6 * c[:,:,1] + 0.1 * c[:,:,2]
+ l = luminance(c)
+ # TODOExt Manage all 0
+ return c / stack3(l)
+
+
+ f0_from_ior = ((ior - 1)/(ior + 1))**2
+ tint_strength = (1 - stack3(buffers['specular_tint'])) + normalize(buffers['base_color']) * stack3(buffers['specular_tint'])
+ out_buf = (1 - stack3(buffers['transmission'])) * (1 / f0_from_ior) * 0.08 * stack3(buffers['specular']) * tint_strength + stack3(buffers['transmission']) * tint_strength
+
+ # Manage values > 1.0 -> Need to apply factor
+ factor = None
+ factors = [np.amax(out_buf[:, :, i]) for i in range(3)]
+
+ if any([f > 1.0 for f in factors]):
+ factor = [1.0 if f < 1.0 else f for f in factors]
+ out_buf /= factor
+
+ out_buf = np.dstack((out_buf, np.ones((width, height)))) # Set alpha (glTF specular) to 1
+ out_buf = np.reshape(out_buf, (width * height * 4))
+
+ return np.float32(out_buf), width, height, [float(f) for f in factor] if factor else None
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_ior.py b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_ior.py
new file mode 100644
index 00000000..ab5b2e7e
--- /dev/null
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_ior.py
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2018-2021 The glTF-Blender-IO authors.
+
+from ...io.com.gltf2_io_constants import GLTF_IOR
+
+def ior(mh, ior_socket):
+ try:
+ ext = mh.pymat.extensions['KHR_materials_ior']
+ except Exception:
+ return
+ ior = ext.get('ior', GLTF_IOR)
+ ior_socket.default_value = ior
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_pbrSpecularGlossiness.py b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_pbrSpecularGlossiness.py
index bed63f7f..19a394b9 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_pbrSpecularGlossiness.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_pbrSpecularGlossiness.py
@@ -22,19 +22,24 @@ def pbr_specular_glossiness(mh):
mh.node_tree.links.new(add_node.inputs[0], glossy_node.outputs[0])
mh.node_tree.links.new(add_node.inputs[1], diffuse_node.outputs[0])
- emission_socket, alpha_socket = make_output_nodes(
+ emission_socket, alpha_socket, _, _ = make_output_nodes(
mh,
location=(370, 250),
+ additional_location=None, #No additional location needed for SpecGloss
shader_socket=add_node.outputs[0],
make_emission_socket=mh.needs_emissive(),
make_alpha_socket=not mh.is_opaque(),
+ make_volume_socket=None, # No possible to have KHR_materials_volume with specular/glossiness
+ make_velvet_socket=None # No possible to have KHR_materials_volume with specular/glossiness
)
- emission(
- mh,
- location=(-200, 860),
- color_socket=emission_socket,
- )
+ if emission_socket:
+ emission(
+ mh,
+ location=(-200, 860),
+ color_socket=emission_socket,
+ strength_socket=emission_socket.node.inputs['Strength']
+ )
base_color(
mh,
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_sheen.py b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_sheen.py
new file mode 100644
index 00000000..aa5cef75
--- /dev/null
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_sheen.py
@@ -0,0 +1,88 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2018-2022 The glTF-Blender-IO authors.
+
+from ...io.com.gltf2_io import TextureInfo
+from .gltf2_blender_texture import texture
+from .gltf2_blender_image import BlenderImage
+from ..exp.gltf2_blender_image import TmpImageGuard
+import numpy as np
+import bpy
+
+def sheen( mh,
+ location_sheenColor,
+ location_sheenRoughness,
+ sheenColor_socket,
+ sheenRoughness_socket
+ ):
+
+ x_sheenColor, y_sheenColor = location_sheenColor
+ x_sheenRoughness, y_sheenRoughness = location_sheenRoughness
+
+ try:
+ ext = mh.pymat.extensions['KHR_materials_sheen']
+ except Exception:
+ return
+
+ sheenColorFactor = ext.get('sheenColorFactor', [0.0, 0.0, 0.0])
+ tex_info_color = ext.get('sheenColorTexture')
+ if tex_info_color is not None:
+ tex_info_color = TextureInfo.from_dict(tex_info_color)
+
+ sheenRoughnessFactor = ext.get('sheenRoughnessFactor', 0.0)
+ tex_info_roughness = ext.get('sheenRoughnessTexture')
+ if tex_info_roughness is not None:
+ tex_info_roughness = TextureInfo.from_dict(tex_info_roughness)
+
+ if tex_info_color is None:
+ sheenColorFactor.extend([1.0])
+ sheenColor_socket.default_value = sheenColorFactor
+ else:
+ # Mix sheenColor factor
+ sheenColorFactor = sheenColorFactor + [1.0]
+ if sheenColorFactor != [1.0, 1.0, 1.0, 1.0]:
+ node = mh.node_tree.nodes.new('ShaderNodeMixRGB')
+ node.label = 'sheenColor Factor'
+ node.location = x_sheenColor - 140, y_sheenColor
+ node.blend_type = 'MULTIPLY'
+ # Outputs
+ mh.node_tree.links.new(sheenColor_socket, node.outputs[0])
+ # Inputs
+ node.inputs['Fac'].default_value = 1.0
+ sheenColor_socket = node.inputs['Color1']
+ node.inputs['Color2'].default_value = sheenColorFactor
+ x_sheenColor -= 200
+
+ texture(
+ mh,
+ tex_info=tex_info_color,
+ label='SHEEN COLOR',
+ location=(x_sheenColor, y_sheenColor),
+ color_socket=sheenColor_socket
+ )
+
+ if tex_info_roughness is None:
+ sheenRoughness_socket.default_value = sheenRoughnessFactor
+ else:
+ # Mix sheenRoughness factor
+ if sheenRoughnessFactor != 1.0:
+ node = mh.node_tree.nodes.new('ShaderNodeMath')
+ node.label = 'shennRoughness Factor'
+ node.location = x_sheenRoughness - 140, y_sheenRoughness
+ node.operation = 'MULTIPLY'
+ # Outputs
+ mh.node_tree.links.new(sheenRoughness_socket, node.outputs[0])
+ # Inputs
+ sheenRoughness_socket = node.inputs[0]
+ node.inputs[1].default_value = sheenRoughnessFactor
+ x_sheenRoughness -= 200
+
+ texture(
+ mh,
+ tex_info=tex_info_roughness,
+ label='SHEEN ROUGHNESS',
+ location=(x_sheenRoughness, y_sheenRoughness),
+ is_data=True,
+ color_socket=None,
+ alpha_socket=sheenRoughness_socket
+ )
+ return \ No newline at end of file
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_specular.py b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_specular.py
new file mode 100644
index 00000000..3441b5ad
--- /dev/null
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_specular.py
@@ -0,0 +1,356 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2018-2021 The glTF-Blender-IO authors.
+
+import bpy
+from ...io.com.gltf2_io import TextureInfo
+from .gltf2_blender_texture import texture
+from io_scene_gltf2.io.com.gltf2_io_constants import GLTF_IOR
+from .gltf2_blender_image import BlenderImage
+from ..exp.gltf2_blender_image import TmpImageGuard, make_temp_image_copy
+
+
+def specular(mh, location_specular,
+ location_specular_tint,
+ specular_socket,
+ specular_tint_socket,
+ original_specular_socket,
+ original_specularcolor_socket,
+ location_original_specular,
+ location_original_specularcolor):
+ x_specular, y_specular = location_specular
+ x_tint, y_tint = location_specular_tint
+
+ if specular_socket is None:
+ return
+ if specular_tint_socket is None:
+ return
+
+ try:
+ ext = mh.pymat.extensions['KHR_materials_specular']
+ except Exception:
+ return
+
+ import numpy as np
+
+ # Retrieve image names
+ try:
+ tex_info = mh.pymat.pbr_metallic_roughness.base_color_texture
+ pytexture = mh.gltf.data.textures[tex_info.index]
+ pyimg = mh.gltf.data.images[pytexture.source]
+ base_color_image_name = pyimg.blender_image_name
+ except:
+ base_color_image_name = None
+
+ # First check if we need a texture or not -> retrieve all info needed
+ specular_factor = ext.get('specularFactor', 1.0)
+ tex_specular_info = ext.get('specularTexture')
+ if tex_specular_info is not None:
+ tex_specular_info = TextureInfo.from_dict(tex_specular_info)
+
+ specular_color_factor = np.array(ext.get('specularColorFactor', [1.0, 1.0, 1.0])[:3])
+ tex_specular_color_info = ext.get('specularColorTexture')
+ if tex_specular_color_info is not None:
+ tex_specular_color_info = TextureInfo.from_dict(tex_specular_color_info)
+
+ base_color_not_linked = base_color_image_name is None
+ base_color = np.array(mh.pymat.pbr_metallic_roughness.base_color_factor or [1, 1, 1])
+ tex_base_color = mh.pymat.pbr_metallic_roughness.base_color_texture
+ base_color = base_color[:3]
+
+ try:
+ ext_transmission = mh.pymat.extensions['KHR_materials_transmission']
+ transmission_factor = ext_transmission.get('transmissionFactor', 0)
+ tex_transmission_info = ext_transmission.get('transmissionTexture')
+ if tex_transmission_info is not None:
+ tex_transmission_info = TextureInfo.from_dict(tex_transmission_info)
+ pytexture = mh.gltf.data.textures[tex_transmission_info.index]
+ pyimg = mh.gltf.data.images[pytexture.source]
+ transmission_image_name = pyimg.blender_image_name
+ else:
+ transmission_image_name = None
+ except Exception:
+ transmission_factor = 0
+ tex_transmission_info = None
+ transmission_image_name = None
+
+ transmission_not_linked = transmission_image_name is None
+
+ try:
+ ext_ior = mh.pymat.extensions['KHR_materials_ior']
+ ior = ext_ior.get('ior', GLTF_IOR)
+ except:
+ ior = GLTF_IOR
+
+ use_texture = tex_specular_info is not None or tex_specular_color_info is not None \
+ or transmission_not_linked is False or base_color_not_linked is False
+
+
+ # Before creating converted textures,
+ # Also plug non converted data into glTF PBR Non Converted Extensions node
+ original_specular( mh,
+ specular_factor,
+ tex_specular_info,
+ specular_color_factor,
+ tex_specular_color_info,
+ original_specular_socket,
+ original_specularcolor_socket,
+ location_original_specular,
+ location_original_specularcolor
+ )
+
+
+ if not use_texture:
+
+ def luminance(c):
+ return 0.3 * c[0] + 0.6 * c[1] + 0.1 * c[2]
+
+ def normalize(c):
+ assert(len(c) == 3)
+ l = luminance(c)
+ if l == 0:
+ return c
+ return np.array([c[0] / l, c[1] / l, c[2] / l])
+
+ f0_from_ior = ((ior - 1)/(ior + 1))**2
+ lum_specular_color = luminance(specular_color_factor)
+ blender_specular = ((lum_specular_color - transmission_factor) / (1 - transmission_factor)) * (1 / 0.08) * f0_from_ior
+ if not all([i == 0 for i in normalize(base_color) - 1]):
+ blender_specular_tint = luminance((normalize(specular_color_factor) - 1) / (normalize(base_color) - 1))
+ if blender_specular_tint < 0 or blender_specular_tint > 1:
+ # TODOExt Warning clamping
+ blender_specular_tint = np.maximum(np.minimum(blender_specular_tint, 1), 0)
+ else:
+ blender_specular_tint = 1.0
+
+ specular_socket.default_value = blender_specular
+ specular_tint_socket.default_value = blender_specular_tint
+ # Note: blender_specular can be greater 1. The Blender documentation permits this.
+
+ return
+ else:
+ # Need to create a texture
+ # First, retrieve and create all images needed
+
+ # Base Color is already created
+ # Transmission is already created
+ # specularTexture is just created by original specular function
+ specular_image_name = None
+ try:
+ pytexture = mh.gltf.data.textures[tex_specular_info.index]
+ pyimg = mh.gltf.data.images[pytexture.source]
+ specular_image_name = pyimg.blender_image_name
+ except:
+ specular_image_name = None
+
+
+ # specularColorTexture is just created by original specular function
+ specularcolor_image_name = None
+ try:
+ pytexture = mh.gltf.data.textures[tex_specular_color_info.index]
+ pyimg = mh.gltf.data.images[pytexture.source]
+ specularcolor_image_name = pyimg.blender_image_name
+ except:
+ specularcolor_image_name = None
+
+ stack3 = lambda v: np.dstack([v]*3)
+
+ texts = {
+ base_color_image_name : 'basecolor',
+ transmission_image_name : 'transmission',
+ specularcolor_image_name : 'speccolor',
+ specular_image_name: 'spec'
+ }
+ images = [(name, bpy.data.images[name]) for name in [base_color_image_name, transmission_image_name, specularcolor_image_name, specular_image_name] if name is not None]
+
+ width = max(image[1].size[0] for image in images)
+ height = max(image[1].size[1] for image in images)
+
+ buffers = {}
+ for name, image in images:
+ tmp_buf = np.empty(width * height * 4, np.float32)
+
+ if image.size[0] == width and image.size[1] == height:
+ image.pixels.foreach_get(tmp_buf)
+ else:
+ # Image is the wrong size; make a temp copy and scale it.
+ with TmpImageGuard() as guard:
+ make_temp_image_copy(guard, src_image=image)
+ tmp_image = guard.image
+ tmp_image.scale(width, height)
+ tmp_image.pixels.foreach_get(tmp_buf)
+
+ buffers[texts[name]] = np.reshape(tmp_buf, [width, height, 4])
+ buffers[texts[name]] = buffers[texts[name]][:,:,:3]
+
+ # Manage factors
+ if name == transmission_image_name:
+ buffers[texts[name]] = stack3(buffers[texts[name]][:,:,0]) # Transmission : keep only R channel
+
+ buffers[texts[name]] *= stack3(transmission_factor)
+
+ elif name == base_color_image_name:
+ buffers[texts[name]] *= base_color
+
+ elif name == specularcolor_image_name:
+ buffers[texts[name]] *= specular_color_factor
+
+ # Create buffer if there is no image
+ if 'basecolor' not in buffers.keys():
+ buffers['basecolor'] = np.full((width, height, 3), base_color)
+ if 'transmission' not in buffers.keys():
+ buffers['transmission'] = np.full((width, height, 3), transmission_factor)
+ if 'speccolor' not in buffers.keys():
+ buffers['speccolor'] = np.full((width, height, 3), specular_color_factor)
+
+ # Calculation
+
+ luminance = lambda c: 0.3 * c[:,:,0] + 0.6 * c[:,:,1] + 0.1 * c[:,:,2]
+ def normalize(c):
+ l = luminance(c)
+ if np.all(l == 0.0):
+ return np.array(c)
+ return c / stack3(l)
+
+ f0_from_ior = ((ior - 1)/(ior + 1))**2
+ lum_specular_color = stack3(luminance(buffers['speccolor']))
+ blender_specular = ((lum_specular_color - buffers['transmission']) / (1 - buffers['transmission'])) * (1 / 0.08) * f0_from_ior
+ if not np.all(normalize(buffers['basecolor']) - 1 == 0.0):
+ blender_specular_tint = luminance((normalize(buffers['speccolor']) - 1) / (normalize(buffers['basecolor']) - 1))
+ np.nan_to_num(blender_specular_tint, copy=False)
+ blender_specular_tint = np.clip(blender_specular_tint, 0.0, 1.0)
+ blender_specular_tint = stack3(blender_specular_tint)
+ else:
+ blender_specular_tint = stack3(np.ones((width, height)))
+
+ blender_specular = np.dstack((blender_specular, np.ones((width, height)))) # Set alpha to 1
+ blender_specular_tint = np.dstack((blender_specular_tint, np.ones((width, height)))) # Set alpha to 1
+
+ # Check if we really need to create a texture
+ blender_specular_tex_not_needed = np.all(np.isclose(blender_specular, blender_specular[0][0]))
+ blender_specular_tint_tex_not_needed = np.all(np.isclose(blender_specular_tint, blender_specular_tint[0][0]))
+
+ if blender_specular_tex_not_needed == True:
+ lum = lambda c: 0.3 * c[0] + 0.6 * c[1] + 0.1 * c[2]
+ specular_socket.default_value = lum(blender_specular[0][0][:3])
+ else:
+ blender_specular = np.reshape(blender_specular, width * height * 4)
+ # Create images in Blender, width and height are dummy values, then set packed file data
+ blender_image_spec = bpy.data.images.new('Specular', width, height)
+ blender_image_spec.pixels.foreach_set(np.float32(blender_specular))
+ blender_image_spec.pack()
+
+ # Create Textures in Blender
+ tex_info = tex_specular_info
+ if tex_info is None:
+ tex_info = tex_specular_color_info
+ if tex_info is None:
+ tex_info = tex_transmission_info
+ if tex_info is None:
+ tex_info = tex_base_color
+
+ texture(
+ mh,
+ tex_info=tex_info,
+ label='SPECULAR',
+ location=(x_specular, y_specular),
+ is_data=True,
+ color_socket=specular_socket,
+ forced_image=blender_image_spec
+ )
+
+ if blender_specular_tint_tex_not_needed == True:
+ lum = lambda c: 0.3 * c[0] + 0.6 * c[1] + 0.1 * c[2]
+ specular_tint_socket.default_value = lum(blender_specular_tint[0][0])
+ else:
+ blender_specular_tint = np.reshape(blender_specular_tint, width * height * 4)
+ # Create images in Blender, width and height are dummy values, then set packed file data
+ blender_image_tint = bpy.data.images.new('Specular Tint', width, height)
+ blender_image_tint.pixels.foreach_set(np.float32(blender_specular_tint))
+ blender_image_tint.pack()
+
+ # Create Textures in Blender
+ tex_info = tex_specular_color_info
+ if tex_info is None:
+ tex_info = tex_specular_info
+ if tex_info is None:
+ tex_info = tex_transmission_info
+ if tex_info is None:
+ tex_info = tex_base_color
+
+ texture(
+ mh,
+ tex_info=tex_info,
+ label='SPECULAR TINT',
+ location=(x_tint, y_tint),
+ is_data=True,
+ color_socket=specular_tint_socket,
+ forced_image=blender_image_tint
+ )
+
+def original_specular( mh,
+ specular_factor,
+ tex_specular_info,
+ specular_color_factor,
+ tex_specular_color_info,
+ original_specular_socket,
+ original_specularcolor_socket,
+ location_original_specular,
+ location_original_specularcolor
+ ):
+
+ x_specular, y_specular = location_original_specular
+ x_specularcolor, y_specularcolor = location_original_specularcolor
+
+ if tex_specular_info is None:
+ original_specular_socket.default_value = specular_factor
+ else:
+ # Mix specular factor
+ if specular_factor != 1.0:
+ node = mh.node_tree.nodes.new('ShaderNodeMath')
+ node.label = 'Specular Factor'
+ node.location = x_specular - 140, y_specular
+ node.operation = 'MULTIPLY'
+ # Outputs
+ mh.node_tree.links.new(original_specular_socket, node.outputs[0])
+ # Inputs
+ original_specular_socket = node.inputs[0]
+ node.inputs[1].default_value = specular_factor
+ x_specular -= 200
+
+ texture(
+ mh,
+ tex_info=tex_specular_info,
+ label='SPECULAR',
+ location=(x_specular, y_specular),
+ is_data=True,
+ color_socket=None,
+ alpha_socket=original_specular_socket
+ )
+
+ if tex_specular_color_info is None:
+ specular_color_factor = list(specular_color_factor)
+ specular_color_factor.extend([1.0])
+ original_specularcolor_socket.default_value = specular_color_factor
+ else:
+ specular_color_factor = list(specular_color_factor) + [1.0]
+ if specular_color_factor != [1.0, 1.0, 1.0, 1.0]:
+ # Mix specularColorFactor
+ node = mh.node_tree.nodes.new('ShaderNodeMixRGB')
+ node.label = 'SpecularColor Factor'
+ node.location = x_specularcolor - 140, y_specularcolor
+ node.blend_type = 'MULTIPLY'
+ # Outputs
+ mh.node_tree.links.new(original_specularcolor_socket, node.outputs[0])
+ # Inputs
+ node.inputs['Fac'].default_value = 1.0
+ original_specularcolor_socket = node.inputs['Color1']
+ node.inputs['Color2'].default_value = specular_color_factor
+ x_specularcolor -= 200
+
+ texture(
+ mh,
+ tex_info=tex_specular_color_info,
+ label='SPECULAR COLOR',
+ location=(x_specularcolor, y_specularcolor),
+ color_socket=original_specularcolor_socket,
+ ) \ No newline at end of file
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_transmission.py b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_transmission.py
new file mode 100644
index 00000000..dab25d14
--- /dev/null
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_transmission.py
@@ -0,0 +1,64 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2018-2022 The glTF-Blender-IO authors.
+
+
+from ...io.com.gltf2_io import TextureInfo, MaterialNormalTextureInfoClass
+from .gltf2_blender_texture import texture
+
+
+# [Texture] => [Separate R] => [Transmission Factor] =>
+def transmission(mh, location, transmission_socket):
+ x, y = location
+ try:
+ ext = mh.pymat.extensions['KHR_materials_transmission']
+ except Exception:
+ return
+ transmission_factor = ext.get('transmissionFactor', 0)
+
+ # Default value is 0, so no transmission
+ if transmission_factor == 0:
+ return
+
+ tex_info = ext.get('transmissionTexture')
+ if tex_info is not None:
+ tex_info = TextureInfo.from_dict(tex_info)
+
+ if transmission_socket is None:
+ return
+
+ if tex_info is None:
+ transmission_socket.default_value = transmission_factor
+ return
+
+ # Mix transmission factor
+ if transmission_factor != 1:
+ node = mh.node_tree.nodes.new('ShaderNodeMath')
+ node.label = 'Transmission Factor'
+ node.location = x - 140, y
+ node.operation = 'MULTIPLY'
+ # Outputs
+ mh.node_tree.links.new(transmission_socket, node.outputs[0])
+ # Inputs
+ transmission_socket = node.inputs[0]
+ node.inputs[1].default_value = transmission_factor
+
+ x -= 200
+
+ # Separate RGB
+ node = mh.node_tree.nodes.new('ShaderNodeSeparateColor')
+ node.location = x - 150, y - 75
+ # Outputs
+ mh.node_tree.links.new(transmission_socket, node.outputs['Red'])
+ # Inputs
+ transmission_socket = node.inputs[0]
+
+ x -= 200
+
+ texture(
+ mh,
+ tex_info=tex_info,
+ label='TRANSMISSION',
+ location=(x, y),
+ is_data=True,
+ color_socket=transmission_socket,
+ )
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_unlit.py b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_unlit.py
index 48ad46fd..1ffdc7e4 100644
--- a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_unlit.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_unlit.py
@@ -24,12 +24,15 @@ def unlit(mh):
mh.node_tree.links.new(mix_node.inputs[1], transparent_node.outputs[0])
mh.node_tree.links.new(mix_node.inputs[2], emission_node.outputs[0])
- _emission_socket, alpha_socket = make_output_nodes(
+ _emission_socket, alpha_socket, _, _ = make_output_nodes(
mh,
location=(420, 280) if mh.is_opaque() else (150, 130),
+ additional_location=None, #No additional location needed for Unlit
shader_socket=mix_node.outputs[0],
make_emission_socket=False,
make_alpha_socket=not mh.is_opaque(),
+ make_volume_socket=None, # Not possible to have KHR_materials_volume with unlit
+ make_velvet_socket=None #Not possible to have KHR_materials_sheen with unlit
)
base_color(
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_volume.py b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_volume.py
new file mode 100644
index 00000000..f909c7f6
--- /dev/null
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_volume.py
@@ -0,0 +1,82 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2018-2021 The glTF-Blender-IO authors.
+
+from ...io.com.gltf2_io import TextureInfo, MaterialNormalTextureInfoClass
+from .gltf2_blender_texture import texture
+
+def volume(mh, location, volume_socket, thickness_socket):
+ # implementation based on https://github.com/KhronosGroup/glTF-Blender-IO/issues/1454#issuecomment-928319444
+ try:
+ ext = mh.pymat.extensions['KHR_materials_volume']
+ except Exception:
+ return
+
+ # Attenuation Color
+ attenuationColor = \
+ mh.pymat.extensions['KHR_materials_volume'] \
+ .get('attenuationColor')
+ # glTF is color3, Blender adds alpha
+ if attenuationColor is None:
+ attenuationColor = [1.0, 1.0, 1.0, 1.0]
+ else:
+ attenuationColor.extend([1.0])
+ volume_socket.node.inputs[0].default_value = attenuationColor
+
+ # Attenuation Distance / Density
+ attenuationDistance = mh.pymat.extensions['KHR_materials_volume'].get('attenuationDistance')
+ if attenuationDistance is None:
+ density = 0
+ else:
+ density = 1.0 / attenuationDistance
+ volume_socket.node.inputs[1].default_value = density
+
+ # thicknessFactor / thicknessTexture
+ x, y = location
+ try:
+ ext = mh.pymat.extensions['KHR_materials_volume']
+ except Exception:
+ return
+ thickness_factor = ext.get('thicknessFactor', 0)
+ tex_info = ext.get('thicknessTexture')
+ if tex_info is not None:
+ tex_info = TextureInfo.from_dict(tex_info)
+
+ if thickness_socket is None:
+ return
+
+ if tex_info is None:
+ thickness_socket.default_value = thickness_factor
+ return
+
+ # Mix thickness factor
+ if thickness_factor != 1:
+ node = mh.node_tree.nodes.new('ShaderNodeMath')
+ node.label = 'Thickness Factor'
+ node.location = x - 140, y
+ node.operation = 'MULTIPLY'
+ # Outputs
+ mh.node_tree.links.new(thickness_socket, node.outputs[0])
+ # Inputs
+ thickness_socket = node.inputs[0]
+ node.inputs[1].default_value = thickness_factor
+
+ x -= 200
+
+ # Separate RGB
+ node = mh.node_tree.nodes.new('ShaderNodeSeparateColor')
+ node.location = x - 150, y - 75
+ # Outputs
+ mh.node_tree.links.new(thickness_socket, node.outputs['Green'])
+ # Inputs
+ thickness_socket = node.inputs[0]
+
+ x -= 200
+
+ texture(
+ mh,
+ tex_info=tex_info,
+ label='THICKNESS',
+ location=(x, y),
+ is_data=True,
+ color_socket=thickness_socket,
+ )
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py b/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py
index f2556465..33713b97 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py
@@ -4,6 +4,8 @@
import bpy
from mathutils import Vector, Quaternion, Matrix
from .gltf2_blender_scene import BlenderScene
+from ..com.gltf2_blender_ui import gltf2_KHR_materials_variants_variant, gltf2_KHR_materials_variants_primitive, gltf2_KHR_materials_variants_default_material
+from .gltf2_blender_material import BlenderMaterial
class BlenderGlTF():
@@ -180,6 +182,10 @@ class BlenderGlTF():
mesh.shapekey_names.append(shapekey_name)
+ # Manage KHR_materials_variants
+ BlenderGlTF.manage_material_variants(gltf)
+
+
@staticmethod
def find_unused_name(haystack, desired_name):
"""Finds a name not in haystack and <= 63 UTF-8 bytes.
@@ -201,3 +207,25 @@ class BlenderGlTF():
suffix = '.%03d' % cntr
cntr += 1
+
+
+ @staticmethod
+ def manage_material_variants(gltf):
+ if not (gltf.data.extensions is not None and 'KHR_materials_variants' in gltf.data.extensions.keys()):
+ gltf.KHR_materials_variants = False
+ return
+
+ gltf.KHR_materials_variants = True
+ # If there is no KHR_materials_variants data in scene, create it
+ if bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is False:
+ bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui = True
+ # Setting preferences as dirty, to be sure that option is saved
+ bpy.context.preferences.is_dirty = True
+
+ if len(bpy.data.scenes[0].gltf2_KHR_materials_variants_variants) > 0:
+ bpy.data.scenes[0].gltf2_KHR_materials_variants_variants.clear()
+
+ for idx_variant, variant in enumerate(gltf.data.extensions['KHR_materials_variants']['variants']):
+ var = bpy.data.scenes[0].gltf2_KHR_materials_variants_variants.add()
+ var.name = variant['name']
+ var.variant_idx = idx_variant
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_image.py b/io_scene_gltf2/blender/imp/gltf2_blender_image.py
index 4f9af799..abce0354 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_image.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_image.py
@@ -83,7 +83,7 @@ def create_from_data(gltf, img_idx):
img_data = BinaryData.get_image_data(gltf, img_idx)
if img_data is None:
return
- img_name = 'Image_%d' % img_idx
+ img_name = gltf.data.images[img_idx].name or 'Image_%d' % img_idx
# Create image, width and height are dummy values
blender_image = bpy.data.images.new(img_name, 8, 8)
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_material.py b/io_scene_gltf2/blender/imp/gltf2_blender_material.py
index 1d18c65d..9a582f7e 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_material.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_material.py
@@ -48,6 +48,11 @@ class BlenderMaterial():
else:
pbr_metallic_roughness(mh)
+ # Manage KHR_materials_variants
+ # We need to store link between material idx in glTF and Blender Material id
+ if gltf.KHR_materials_variants is True:
+ gltf.variant_mapping[str(material_idx) + str(vertex_color)] = mat
+
import_user_extensions('gather_import_material_after_hook', gltf, pymaterial, vertex_color, mat)
@staticmethod
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py b/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py
index a3c1bd34..b886dd25 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py
@@ -11,6 +11,7 @@ from .gltf2_blender_material import BlenderMaterial
from ...io.com.gltf2_io_debug import print_console
from .gltf2_io_draco_compression_extension import decode_primitive
from io_scene_gltf2.io.imp.gltf2_io_user_extensions import import_user_extensions
+from ..com.gltf2_blender_ui import gltf2_KHR_materials_variants_primitive, gltf2_KHR_materials_variants_variant, gltf2_KHR_materials_variants_default_material
class BlenderMesh():
@@ -343,12 +344,19 @@ def do_primitives(gltf, mesh_idx, skin_idx, mesh, ob):
# ----
# Assign materials to faces
has_materials = any(prim.material is not None for prim in pymesh.primitives)
+ # Even if no primitive have material, we need to create slots if some primitives have some variant
+ if has_materials is False:
+ has_materials = any(prim.extensions is not None and 'KHR_materials_variants' in prim.extensions.keys() for prim in pymesh.primitives)
+
+ has_variant = prim.extensions is not None and 'KHR_materials_variants' in prim.extensions.keys() \
+ and 'mappings' in prim.extensions['KHR_materials_variants'].keys()
+
if has_materials:
material_indices = np.empty(num_faces, dtype=np.uint32)
empty_material_slot_index = None
f = 0
- for prim in pymesh.primitives:
+ for idx_prim, prim in enumerate(pymesh.primitives):
if prim.material is not None:
# Get the material
pymaterial = gltf.data.materials[prim.material]
@@ -358,19 +366,55 @@ def do_primitives(gltf, mesh_idx, skin_idx, mesh, ob):
material_name = pymaterial.blender_material[vertex_color]
# Put material in slot (if not there)
- if material_name not in mesh.materials:
+ if not has_variant:
+ if material_name not in mesh.materials:
+ mesh.materials.append(bpy.data.materials[material_name])
+ material_index = mesh.materials.find(material_name)
+ else:
+ # In case of variant, do not merge slots
mesh.materials.append(bpy.data.materials[material_name])
- material_index = mesh.materials.find(material_name)
+ material_index = len(mesh.materials) - 1
else:
- if empty_material_slot_index is None:
+ if not has_variant:
+ if empty_material_slot_index is None:
+ mesh.materials.append(None)
+ empty_material_slot_index = len(mesh.materials) - 1
+ material_index = empty_material_slot_index
+ else:
+ # In case of variant, do not merge slots
mesh.materials.append(None)
- empty_material_slot_index = len(mesh.materials) - 1
- material_index = empty_material_slot_index
+ material_index = len(mesh.materials) - 1
material_indices[f:f + prim.num_faces].fill(material_index)
f += prim.num_faces
+ # Manage variants
+ if has_variant:
+
+ # Store default material
+ default_mat = mesh.gltf2_variant_default_materials.add()
+ default_mat.material_slot_index = material_index
+ default_mat.default_material = bpy.data.materials[material_name] if prim.material is not None else None
+
+ for mapping in prim.extensions['KHR_materials_variants']['mappings']:
+ # Store, for each variant, the material link to this primitive
+
+ variant_primitive = mesh.gltf2_variant_mesh_data.add()
+ variant_primitive.material_slot_index = material_index
+ if 'material' not in mapping.keys():
+ # Default material
+ variant_primitive.material = None
+ else:
+ vertex_color = 'COLOR_0' if 'COLOR_0' in prim.attributes else None
+ if str(mapping['material']) + str(vertex_color) not in gltf.variant_mapping.keys():
+ BlenderMaterial.create(gltf, mapping['material'], vertex_color)
+ variant_primitive.material = gltf.variant_mapping[str(mapping['material']) + str(vertex_color)]
+
+ for variant in mapping['variants']:
+ vari = variant_primitive.variants.add()
+ vari.variant.variant_idx = variant
+
mesh.polygons.foreach_set('material_index', material_indices)
# ----
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py
index 4c32c35d..b6b8e19f 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py
@@ -1,13 +1,19 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2018-2021 The glTF-Blender-IO authors.
+from re import M
import bpy
from ...io.com.gltf2_io import TextureInfo, MaterialPBRMetallicRoughness
from ..com.gltf2_blender_material_helpers import get_gltf_node_name, create_settings_group
from .gltf2_blender_texture import texture
from .gltf2_blender_KHR_materials_clearcoat import \
clearcoat, clearcoat_roughness, clearcoat_normal
-
+from .gltf2_blender_KHR_materials_transmission import transmission
+from .gltf2_blender_KHR_materials_ior import ior
+from .gltf2_blender_KHR_materials_volume import volume
+from .gltf2_blender_KHR_materials_specular import specular
+from .gltf2_blender_KHR_materials_sheen import sheen
+from ...io.com.gltf2_io_constants import GLTF_IOR
class MaterialHelper:
"""Helper class. Stores material stuff to be passed around everywhere."""
@@ -20,6 +26,7 @@ class MaterialHelper:
if pymat.pbr_metallic_roughness is None:
pymat.pbr_metallic_roughness = \
MaterialPBRMetallicRoughness.from_dict({})
+ self.settings_node = None
def is_opaque(self):
alpha_mode = self.pymat.alpha_mode
@@ -36,15 +43,52 @@ def pbr_metallic_roughness(mh: MaterialHelper):
"""Creates node tree for pbrMetallicRoughness materials."""
pbr_node = mh.node_tree.nodes.new('ShaderNodeBsdfPrincipled')
pbr_node.location = 10, 300
-
- make_output_nodes(
+ additional_location = 40, -370 # For occlusion and/or volume / original PBR extensions
+
+ # Set IOR to 1.5, this is the default in glTF
+ # This value may be overridden later if IOR extension is set on file
+ pbr_node.inputs['IOR'].default_value = GLTF_IOR
+
+ if mh.pymat.occlusion_texture is not None or (mh.pymat.extensions and 'KHR_materials_specular' in mh.pymat.extensions):
+ if mh.settings_node is None:
+ mh.settings_node = make_settings_node(mh)
+ mh.settings_node.location = additional_location
+ mh.settings_node.width = 180
+ additional_location = additional_location[0], additional_location[1] - 150
+
+ need_volume_node = False
+ if mh.pymat.extensions and 'KHR_materials_volume' in mh.pymat.extensions:
+ if 'thicknessFactor' in mh.pymat.extensions['KHR_materials_volume'] \
+ and mh.pymat.extensions['KHR_materials_volume']['thicknessFactor'] != 0.0:
+
+ need_volume_node = True
+
+ # We also need glTF Material Output Node, to set thicknessFactor and thicknessTexture
+ mh.settings_node = make_settings_node(mh)
+ mh.settings_node.location = additional_location
+ mh.settings_node.width = 180
+ volume_location = additional_location
+ additional_location = additional_location[0], additional_location[1] - 150
+
+ need_velvet_node = False
+ if mh.pymat.extensions and 'KHR_materials_sheen' in mh.pymat.extensions:
+ need_velvet_node = True
+
+ _, _, volume_socket, velvet_node = make_output_nodes(
mh,
location=(250, 260),
+ additional_location=additional_location,
shader_socket=pbr_node.outputs[0],
- make_emission_socket=False,
- make_alpha_socket=False,
+ make_emission_socket=False, # is managed by Principled shader node
+ make_alpha_socket=False, # is managed by Principled shader node
+ make_volume_socket=need_volume_node,
+ make_velvet_socket=need_velvet_node
)
+
+ if mh.pymat.extensions and 'KHR_materials_sheen':
+ pass #TOTOEXT
+
locs = calc_locations(mh)
emission(
@@ -75,13 +119,10 @@ def pbr_metallic_roughness(mh: MaterialHelper):
)
if mh.pymat.occlusion_texture is not None:
- node = make_settings_node(mh)
- node.location = 40, -370
- node.width = 180
occlusion(
mh,
location=locs['occlusion'],
- occlusion_socket=node.inputs['Occlusion'],
+ occlusion_socket=mh.settings_node.inputs['Occlusion'],
)
clearcoat(
@@ -102,6 +143,46 @@ def pbr_metallic_roughness(mh: MaterialHelper):
normal_socket=pbr_node.inputs['Clearcoat Normal'],
)
+ transmission(
+ mh,
+ location=locs['transmission'],
+ transmission_socket=pbr_node.inputs['Transmission']
+ )
+
+ if need_volume_node:
+ volume(
+ mh,
+ location=locs['volume_thickness'],
+ volume_socket=volume_socket,
+ thickness_socket=mh.settings_node.inputs[1] if mh.settings_node else None
+ )
+
+ specular(
+ mh,
+ location_specular=locs['specularTexture'],
+ location_specular_tint=locs['specularColorTexture'],
+ specular_socket=pbr_node.inputs['Specular'],
+ specular_tint_socket=pbr_node.inputs['Specular Tint'],
+ original_specular_socket=mh.settings_node.inputs[2] if mh.settings_node else None,
+ original_specularcolor_socket=mh.settings_node.inputs[3] if mh.settings_node else None,
+ location_original_specular=locs['original_specularTexture'],
+ location_original_specularcolor=locs['original_specularColorTexture']
+ )
+
+ if need_velvet_node:
+ sheen(
+ mh,
+ location_sheenColor=locs['sheenColorTexture'],
+ location_sheenRoughness=locs['sheenRoughnessTexture'],
+ sheenColor_socket=velvet_node.inputs[0],
+ sheenRoughness_socket=velvet_node.inputs[1]
+ )
+
+ ior(
+ mh,
+ ior_socket=pbr_node.inputs['IOR']
+ )
+
def calc_locations(mh):
"""Calculate locations to place each bit of the node graph at."""
@@ -116,18 +197,53 @@ def calc_locations(mh):
except Exception:
clearcoat_ext = {}
+ try:
+ transmission_ext = mh.pymat.exntesions['KHR_materials_transmission']
+ except:
+ transmission_ext = {}
+
+ try:
+ volume_ext = mh.pymat.extensions['KHR_materials_volume']
+ except Exception:
+ volume_ext = {}
+
+ try:
+ specular_ext = mh.pymat.extensions['KHR_materials_specular']
+ except:
+ specular_ext = {}
+
+ try:
+ sheen_ext = mh.pymat.extensions['KHR_materials_sheen']
+ except:
+ sheen_ext = {}
+
+ locs['sheenColorTexture'] = (x, y)
+ if 'sheenColorTexture' in sheen_ext:
+ y -= height
+ locs['sheenRoughnessTexture'] = (x, y)
+ if 'sheenRoughnessTexture' in sheen_ext:
+ y -= height
locs['base_color'] = (x, y)
if mh.pymat.pbr_metallic_roughness.base_color_texture is not None or mh.vertex_color:
y -= height
locs['metallic_roughness'] = (x, y)
if mh.pymat.pbr_metallic_roughness.metallic_roughness_texture is not None:
y -= height
+ locs['specularTexture'] = (x, y)
+ if 'specularTexture' in specular_ext:
+ y -= height
+ locs['specularColorTexture'] = (x, y)
+ if 'specularColorTexture' in specular_ext:
+ y -= height
locs['clearcoat'] = (x, y)
if 'clearcoatTexture' in clearcoat_ext:
y -= height
locs['clearcoat_roughness'] = (x, y)
if 'clearcoatRoughnessTexture' in clearcoat_ext:
y -= height
+ locs['transmission'] = (x, y)
+ if 'transmissionTexture' in transmission_ext:
+ y -= height
locs['emission'] = (x, y)
if mh.pymat.emissive_texture is not None:
y -= height
@@ -140,6 +256,22 @@ def calc_locations(mh):
locs['occlusion'] = (x, y)
if mh.pymat.occlusion_texture is not None:
y -= height
+ locs['volume_thickness'] = (x, y)
+ if 'thicknessTexture' in volume_ext:
+ y -= height
+ locs['original_specularTexture'] = (x, y)
+ if 'specularTexture' in specular_ext:
+ y -= height
+ locs['original_specularColorTexture'] = (x, y)
+ if 'specularColorTexture' in specular_ext:
+ y -= height
+ locs['original_sheenColorTexture'] = (x, y)
+ if 'sheenColorTexture' in sheen_ext:
+ y -= height
+ locs['original_sheenRoughnessTexture'] = (x, y)
+ if 'sheenRoughnessTexture' in sheen_ext:
+ y -= height
+
# Center things
total_height = -y
@@ -157,21 +289,29 @@ def calc_locations(mh):
# [Texture] => [Emissive Factor] =>
-def emission(mh: MaterialHelper, location, color_socket, strength_socket=None):
+def emission(mh: MaterialHelper, location, color_socket, strength_socket):
x, y = location
emissive_factor = mh.pymat.emissive_factor or [0, 0, 0]
+ strength = 1
+ try:
+ # Get strength from KHR_materials_emissive_strength if exists
+ strength = mh.pymat.extensions['KHR_materials_emissive_strength']['emissiveStrength']
+ except Exception:
+ pass
+
if color_socket is None:
return
if mh.pymat.emissive_texture is None:
color_socket.default_value = emissive_factor + [1]
+ strength_socket.default_value = strength
return
# Put grayscale emissive factors into the Emission Strength
e0, e1, e2 = emissive_factor
if strength_socket and e0 == e1 == e2:
- strength_socket.default_value = e0
+ strength_socket.default_value = e0 * strength
# Otherwise, use a multiply node for it
else:
@@ -189,6 +329,8 @@ def emission(mh: MaterialHelper, location, color_socket, strength_socket=None):
x -= 200
+ strength_socket.default_value = strength
+
texture(
mh,
tex_info=mh.pymat.emissive_texture,
@@ -466,17 +608,22 @@ def occlusion(mh: MaterialHelper, location, occlusion_socket):
)
-# => [Add Emission] => [Mix Alpha] => [Material Output]
+# => [Add Emission] => [Mix Alpha] => [Material Output] if needed, only for SpecGlossiness
+# => [Volume] => [Add Shader] => [Material Output] if needed
+# => [Velvet] => [Add Shader] => [Material Output] if needed
def make_output_nodes(
mh: MaterialHelper,
location,
+ additional_location,
shader_socket,
make_emission_socket,
make_alpha_socket,
+ make_volume_socket,
+ make_velvet_socket, # For sheen
):
"""
Creates the Material Output node and connects shader_socket to it.
- If requested, it can also create places to hookup the emission/alpha
+ If requested, it can also create places to hookup the emission/alpha.sheen
in between shader_socket and the Output node too.
:return: a pair containing the sockets you should put emission and alpha
@@ -484,6 +631,7 @@ def make_output_nodes(
"""
x, y = location
emission_socket = None
+ velvet_node = None
alpha_socket = None
# Create an Emission node and add it to the shader.
@@ -512,6 +660,31 @@ def make_output_nodes(
x += 380
y += 125
+ # Create an Velvet node add add it to the shader
+ # Note that you can not have Emission & Velvet at the same time
+ if make_velvet_socket:
+ # Velvet
+ node = mh.node_tree.nodes.new("ShaderNodeBsdfVelvet")
+ node.location = x + 50, y + 250
+ # Node
+ velvet_node = node
+ # Outputs
+ velvet_output = node.outputs[0]
+
+ # Add
+ node = mh.node_tree.nodes.new('ShaderNodeAddShader')
+ node.location = x + 250, y + 160
+ # Inputs
+ mh.node_tree.links.new(node.inputs[0], velvet_output)
+ mh.node_tree.links.new(node.inputs[1], shader_socket)
+ # Outputs
+ shader_socket = node.outputs[0]
+
+
+ x += 380
+ y += 125
+
+
# Mix with a Transparent BSDF. Mixing factor is the alpha value.
if make_alpha_socket:
# Transparent BSDF
@@ -535,12 +708,23 @@ def make_output_nodes(
y -= 210
# Material output
- node = mh.node_tree.nodes.new('ShaderNodeOutputMaterial')
- node.location = x + 70, y + 10
+ node_output = mh.node_tree.nodes.new('ShaderNodeOutputMaterial')
+ node_output.location = x + 70, y + 10
+
# Outputs
- mh.node_tree.links.new(node.inputs[0], shader_socket)
+ mh.node_tree.links.new(node_output.inputs[0], shader_socket)
+
+ # Volume Node
+ volume_socket = None
+ if make_volume_socket:
+ node = mh.node_tree.nodes.new('ShaderNodeVolumeAbsorption')
+ node.location = additional_location
+ # Outputs
+ mh.node_tree.links.new(node_output.inputs[1], node.outputs[0])
+ volume_socket = node.outputs[0]
+
- return emission_socket, alpha_socket
+ return emission_socket, alpha_socket, volume_socket, velvet_node
def make_settings_node(mh):
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_texture.py b/io_scene_gltf2/blender/imp/gltf2_blender_texture.py
index 24c9df7c..12e6d594 100644
--- a/io_scene_gltf2/blender/imp/gltf2_blender_texture.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_texture.py
@@ -17,6 +17,7 @@ def texture(
color_socket,
alpha_socket=None,
is_data=False,
+ forced_image=None
):
"""Creates nodes for a TextureInfo and hooks up the color/alpha outputs."""
x, y = location
@@ -36,12 +37,15 @@ def texture(
tex_img.location = x - 240, y
tex_img.label = label
# Get image
- if pytexture.source is not None:
- BlenderImage.create(mh.gltf, pytexture.source)
- pyimg = mh.gltf.data.images[pytexture.source]
- blender_image_name = pyimg.blender_image_name
- if blender_image_name:
- tex_img.image = bpy.data.images[blender_image_name]
+ if forced_image is None:
+ if pytexture.source is not None:
+ BlenderImage.create(mh.gltf, pytexture.source)
+ pyimg = mh.gltf.data.images[pytexture.source]
+ blender_image_name = pyimg.blender_image_name
+ if blender_image_name:
+ tex_img.image = bpy.data.images[blender_image_name]
+ else:
+ tex_img.image = forced_image
# Set colorspace for data images
if is_data:
if tex_img.image:
@@ -49,7 +53,8 @@ def texture(
# Set filtering
set_filtering(tex_img, pysampler)
# Outputs
- mh.node_tree.links.new(color_socket, tex_img.outputs['Color'])
+ if color_socket is not None:
+ mh.node_tree.links.new(color_socket, tex_img.outputs['Color'])
if alpha_socket is not None:
mh.node_tree.links.new(alpha_socket, tex_img.outputs['Alpha'])
# Inputs
diff --git a/io_scene_gltf2/io/com/gltf2_io_constants.py b/io_scene_gltf2/io/com/gltf2_io_constants.py
index 19ead516..816220d9 100755
--- a/io_scene_gltf2/io/com/gltf2_io_constants.py
+++ b/io_scene_gltf2/io/com/gltf2_io_constants.py
@@ -118,6 +118,9 @@ class TextureWrap(IntEnum):
MirroredRepeat = 33648
Repeat = 10497
+class BufferViewTarget(IntEnum):
+ ARRAY_BUFFER = 34962
+ ELEMENT_ARRAY_BUFFER = 34963
#################
# LEGACY DEFINES
@@ -145,3 +148,5 @@ GLTF_DATA_TYPE_VEC4 = "VEC4"
GLTF_DATA_TYPE_MAT2 = "MAT2"
GLTF_DATA_TYPE_MAT3 = "MAT3"
GLTF_DATA_TYPE_MAT4 = "MAT4"
+
+GLTF_IOR = 1.5 \ No newline at end of file
diff --git a/io_scene_gltf2/io/com/gltf2_io_draco_compression_extension.py b/io_scene_gltf2/io/com/gltf2_io_draco_compression_extension.py
index 28f06e51..b9f9ccec 100644
--- a/io_scene_gltf2/io/com/gltf2_io_draco_compression_extension.py
+++ b/io_scene_gltf2/io/com/gltf2_io_draco_compression_extension.py
@@ -27,7 +27,7 @@ def dll_path() -> Path:
'darwin': blender_root.parent / 'Resources' / python_lib / python_version / 'site-packages'
}.get(sys.platform)
else:
- path = Path(path)
+ return Path(path)
library_name = {
'win32': '{}.dll'.format(lib_name),
@@ -46,12 +46,11 @@ def dll_exists(quiet=False) -> bool:
Checks whether the DLL path exists.
:return: True if the DLL exists.
"""
- exists = dll_path().exists()
+ path = dll_path()
+ exists = path.exists() and path.is_file()
if quiet is False:
- print("'{}' ".format(dll_path().absolute()) + ("exists, draco mesh compression is available" if exists else
- "{} {} {}".format(
- "does not exist, draco mesh compression not available,",
- "please add it or create environment variable BLENDER_EXTERN_DRACO_LIBRARY_PATH",
- "pointing to the folder"
- )))
+ if exists:
+ print_console('INFO', 'Draco mesh compression is available, use library at %s' % dll_path().absolute())
+ else:
+ print_console('ERROR', 'Draco mesh compression is not available because library could not be found at %s' % dll_path().absolute())
return exists
diff --git a/io_scene_gltf2/io/com/gltf2_io_variants.py b/io_scene_gltf2/io/com/gltf2_io_variants.py
new file mode 100644
index 00000000..3824fee4
--- /dev/null
+++ b/io_scene_gltf2/io/com/gltf2_io_variants.py
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2018-2022 The glTF-Blender-IO authors.
+
+from io_scene_gltf2.io.com.gltf2_io import from_dict, from_union, from_none, from_float, from_str, from_list
+from io_scene_gltf2.io.com.gltf2_io import to_float, to_class
+
+class Variant:
+ """defines variant for use with glTF 2.0."""
+ def __init__(self, name, extensions, extras):
+ self.name = name
+ self.extensions = extensions
+ self.extras = extras
+
+ @staticmethod
+ def from_dict(obj):
+ assert isinstance(obj, dict)
+ name = from_union([from_str, from_none], obj.get("name"))
+ extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none],
+ obj.get("extensions"))
+ extras = obj.get("extras")
+ return Variant(name, extensions, extras)
+
+ def to_dict(self):
+ result = {}
+ result["name"] = from_union([from_str, from_none], self.name)
+ result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none],
+ self.extensions)
+ result["extras"] = self.extras
+ return result
diff --git a/io_scene_gltf2/io/exp/gltf2_io_binary_data.py b/io_scene_gltf2/io/exp/gltf2_io_binary_data.py
index 10405551..6a617628 100755
--- a/io_scene_gltf2/io/exp/gltf2_io_binary_data.py
+++ b/io_scene_gltf2/io/exp/gltf2_io_binary_data.py
@@ -9,10 +9,11 @@ from io_scene_gltf2.io.com import gltf2_io_constants
class BinaryData:
"""Store for gltf binary data that can later be stored in a buffer."""
- def __init__(self, data: bytes):
+ def __init__(self, data: bytes, bufferViewTarget=None):
if not isinstance(data, bytes):
raise TypeError("Data is not a bytes array")
self.data = data
+ self.bufferViewTarget = bufferViewTarget
def __eq__(self, other):
return self.data == other.data
@@ -21,9 +22,9 @@ class BinaryData:
return hash(self.data)
@classmethod
- def from_list(cls, lst: typing.List[typing.Any], gltf_component_type: gltf2_io_constants.ComponentType):
+ def from_list(cls, lst: typing.List[typing.Any], gltf_component_type: gltf2_io_constants.ComponentType, bufferViewTarget=None):
format_char = gltf2_io_constants.ComponentType.to_type_code(gltf_component_type)
- return BinaryData(array.array(format_char, lst).tobytes())
+ return BinaryData(array.array(format_char, lst).tobytes(), bufferViewTarget)
@property
def byte_length(self):
diff --git a/io_scene_gltf2/io/exp/gltf2_io_buffer.py b/io_scene_gltf2/io/exp/gltf2_io_buffer.py
index 5fae3834..4b70e789 100755
--- a/io_scene_gltf2/io/exp/gltf2_io_buffer.py
+++ b/io_scene_gltf2/io/exp/gltf2_io_buffer.py
@@ -35,7 +35,7 @@ class Buffer:
extensions=None,
extras=None,
name=None,
- target=None
+ target=binary_data.bufferViewTarget
)
return buffer_view
diff --git a/io_scene_gltf2/io/imp/gltf2_io_gltf.py b/io_scene_gltf2/io/imp/gltf2_io_gltf.py
index 9f096e69..75ce7265 100755
--- a/io_scene_gltf2/io/imp/gltf2_io_gltf.py
+++ b/io_scene_gltf2/io/imp/gltf2_io_gltf.py
@@ -28,6 +28,7 @@ class glTFImporter():
self.accessor_cache = {}
self.decode_accessor_cache = {}
self.import_user_extensions = import_settings['import_user_extensions']
+ self.variant_mapping = {} # Used to map between mgltf material idx and blender material, for Variants
if 'loglevel' not in self.import_settings.keys():
self.import_settings['loglevel'] = logging.ERROR
@@ -44,7 +45,13 @@ class glTFImporter():
'KHR_texture_transform',
'KHR_materials_clearcoat',
'KHR_mesh_quantization',
- 'KHR_draco_mesh_compression'
+ 'KHR_draco_mesh_compression',
+ 'KHR_materials_variants',
+ 'KHR_materials_emissive_strength',
+ 'KHR_materials_transmission',
+ 'KHR_materials_specular',
+ 'KHR_materials_sheen',
+ 'KHR_materials_ior'
]
# Add extensions required supported by custom import extensions
diff --git a/io_scene_obj/__init__.py b/io_scene_obj/__init__.py
index 0ec62410..d49f8367 100644
--- a/io_scene_obj/__init__.py
+++ b/io_scene_obj/__init__.py
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-or-later
bl_info = {
- "name": "Wavefront OBJ format",
+ "name": "Wavefront OBJ format (legacy)",
"author": "Campbell Barton, Bastien Montagne",
"version": (3, 9, 0),
"blender": (3, 0, 0),
@@ -459,11 +459,11 @@ class OBJ_PT_export_geometry(bpy.types.Panel):
def menu_func_import(self, context):
- self.layout.operator(ImportOBJ.bl_idname, text="Wavefront (.obj)")
+ self.layout.operator(ImportOBJ.bl_idname, text="Wavefront (.obj) (legacy)")
def menu_func_export(self, context):
- self.layout.operator(ExportOBJ.bl_idname, text="Wavefront (.obj)")
+ self.layout.operator(ExportOBJ.bl_idname, text="Wavefront (.obj) (legacy)")
classes = (
diff --git a/io_scene_obj/import_obj.py b/io_scene_obj/import_obj.py
index d41e72f8..63c4d0e1 100644
--- a/io_scene_obj/import_obj.py
+++ b/io_scene_obj/import_obj.py
@@ -584,7 +584,7 @@ def create_mesh(new_objects,
len_face_vert_loc_indices = len(face_vert_loc_indices)
if len_face_vert_loc_indices == 1:
- faces.pop(f_idx) # cant add single vert faces
+ faces.pop(f_idx) # can't add single vert faces
# Face with a single item in face_vert_nor_indices is actually a polyline!
elif face_is_edge(face):
@@ -979,7 +979,7 @@ def load(context,
# when there are faces that end with \
# it means they are multiline-
- # since we use xreadline we cant skip to the next line
+ # since we use xreadline we can't skip to the next line
# so we need to know whether
context_multi_line = b''
diff --git a/io_scene_x3d/import_x3d.py b/io_scene_x3d/import_x3d.py
index 6d13bb79..228e0507 100644
--- a/io_scene_x3d/import_x3d.py
+++ b/io_scene_x3d/import_x3d.py
@@ -1003,13 +1003,13 @@ class vrmlNode(object):
print('\tWarning: Inline URL could not be found:', url)
else:
if url == self.getFilename():
- print('\tWarning: cant Inline yourself recursively:', url)
+ print('\tWarning: can\'t Inline yourself recursively:', url)
else:
try:
data = gzipOpen(url)
except:
- print('\tWarning: cant open the file:', url)
+ print('\tWarning: can\'t open the file:', url)
data = None
if data:
diff --git a/magic_uv/common.py b/magic_uv/common.py
index fccce1c4..f76fcc67 100644
--- a/magic_uv/common.py
+++ b/magic_uv/common.py
@@ -1242,11 +1242,14 @@ def __is_points_in_polygon(points, subject_points):
def get_uv_editable_objects(context):
if compat.check_version(2, 80, 0) < 0:
- objs = [context.active_object]
+ objs = []
else:
objs = [o for o in bpy.data.objects
if compat.get_object_select(o) and o.type == 'MESH']
- objs.append(context.active_object)
+
+ ob = context.active_object
+ if ob is not None:
+ objs.append(ob)
objs = list(set(objs))
return objs
diff --git a/magic_uv/op/copy_paste_uv_object.py b/magic_uv/op/copy_paste_uv_object.py
index 39795b52..897891e4 100644
--- a/magic_uv/op/copy_paste_uv_object.py
+++ b/magic_uv/op/copy_paste_uv_object.py
@@ -30,15 +30,16 @@ def _is_valid_context(context):
if not common.is_valid_space(context, ['VIEW_3D']):
return False
+ # Only object mode is allowed to execute.
+ ob = context.object
+ if ob is not None and ob.mode != 'OBJECT':
+ return False
+
# Multiple objects editing mode is not supported in this feature.
objs = common.get_uv_editable_objects(context)
if len(objs) != 1:
return False
- # only object mode is allowed to execute
- if context.object.mode != 'OBJECT':
- return False
-
return True
diff --git a/magic_uv/utils/graph.py b/magic_uv/utils/graph.py
index bebabf63..277800bc 100644
--- a/magic_uv/utils/graph.py
+++ b/magic_uv/utils/graph.py
@@ -29,7 +29,7 @@ class Edge:
raise RuntimeError("Loop edge in {} is not supported."
.format(node.key))
if node not in (self.node_1, self.node_2):
- raise RuntimeError("Node {} does not belog this edge."
+ raise RuntimeError("Node {} does not belong to this edge."
.format(node.key))
if self.node_1 == node:
return self.node_2
diff --git a/materials_utils/functions.py b/materials_utils/functions.py
index 9397257a..93d700d8 100644
--- a/materials_utils/functions.py
+++ b/materials_utils/functions.py
@@ -333,7 +333,7 @@ def mu_select_by_material_name(self, find_material_name, extend_selection = Fals
elif not internal:
# Some object types are not supported
# mostly because don't really support selecting by material (like Font/Text objects)
- # ore that they don't support multiple materials/are just "weird" (i.e. Meta balls)
+ # or that they don't support multiple materials/are just "weird" (i.e. Meta balls)
self.report({'WARNING'}, "The type '" +
obj.type +
"' isn't supported in Edit mode by Material Utilities!")
diff --git a/measureit/__init__.py b/measureit/__init__.py
index 8d4b9576..c7f4fc7e 100644
--- a/measureit/__init__.py
+++ b/measureit/__init__.py
@@ -11,8 +11,8 @@ bl_info = {
"name": "MeasureIt",
"author": "Antonio Vazquez (antonioya)",
"location": "View3D > Sidebar > View Tab",
- "version": (1, 8, 1),
- "blender": (2, 80, 0),
+ "version": (1, 8, 2),
+ "blender": (3, 0, 0),
"description": "Tools for measuring objects.",
"doc_url": "{BLENDER_MANUAL_URL}/addons/3d_view/measureit.html",
"category": "3D View"
diff --git a/measureit/measureit_geometry.py b/measureit/measureit_geometry.py
index a1e90b9e..56efad0e 100644
--- a/measureit/measureit_geometry.py
+++ b/measureit/measureit_geometry.py
@@ -17,7 +17,6 @@ from bpy_extras import view3d_utils, mesh_utils
import bpy_extras.object_utils as object_utils
from sys import exc_info
# GPU
-import bgl
import gpu
from gpu_extras.batch import batch_for_shader
@@ -477,7 +476,7 @@ def draw_segments(context, myobj, op, region, rv3d):
# ------------------------------------
# Draw lines
# ------------------------------------
- bgl.glEnable(bgl.GL_BLEND)
+ gpu.state.blend_set('ALPHA')
if ms.gltype == 1: # Segment
draw_line(screen_point_ap1, screen_point_v11, rgba)
@@ -1175,7 +1174,7 @@ def draw_faces(context, myobj, region, rv3d):
a_p2 = (a_p1[0] + normal[0] * ln, a_p1[1] + normal[1] * ln, a_p1[2] + normal[2] * ln)
# line setup
- bgl.glEnable(bgl.GL_BLEND)
+ gpu.state.blend_set('ALPHA')
imm_set_line_width(th)
# converting to screen coordinates
txtpoint2d = get_2d_point(region, rv3d, a_p1)
@@ -1185,7 +1184,7 @@ def draw_faces(context, myobj, region, rv3d):
draw_text(myobj, txtpoint2d, str(f.index), rgba, fsize)
# Draw Normal
if scene.measureit_debug_normals is True:
- bgl.glEnable(bgl.GL_BLEND)
+ gpu.state.blend_set('ALPHA')
draw_arrow(txtpoint2d, point2, rgba, 10, "99", "1")
if len(obverts) > 2 and scene.measureit_debug_normal_details is True:
diff --git a/measureit/measureit_main.py b/measureit/measureit_main.py
index 23e9de46..d69ba268 100644
--- a/measureit/measureit_main.py
+++ b/measureit/measureit_main.py
@@ -11,7 +11,6 @@ import bpy
import bmesh
from bmesh import from_edit_mesh
# noinspection PyUnresolvedReferences
-import bgl
from bpy.types import PropertyGroup, Panel, Object, Operator, SpaceView3D
from bpy.props import IntProperty, CollectionProperty, FloatVectorProperty, BoolProperty, StringProperty, \
FloatProperty, EnumProperty
@@ -1933,8 +1932,8 @@ def draw_main(context):
else:
objlist = context.view_layer.objects
- # Enable GL drawing
- bgl.glEnable(bgl.GL_BLEND)
+ # Enable drawing
+ gpu.state.blend_set('ALPHA')
# ---------------------------------------
# Generate all OpenGL calls for measures
# ---------------------------------------
@@ -1964,9 +1963,9 @@ def draw_main(context):
draw_faces(context, myobj, region, rv3d)
# -----------------------
- # restore opengl defaults
+ # restore defaults
# -----------------------
- bgl.glDisable(bgl.GL_BLEND)
+ gpu.state.blend_set('NONE')
# -------------------------------------------------------------
diff --git a/measureit/measureit_render.py b/measureit/measureit_render.py
index efc5c1b0..093149d5 100644
--- a/measureit/measureit_render.py
+++ b/measureit/measureit_render.py
@@ -8,7 +8,6 @@
# noinspection PyUnresolvedReferences
import bpy
import gpu
-import bgl
# noinspection PyUnresolvedReferences
import blf
from os import path, remove
@@ -54,8 +53,8 @@ def render_main(self, context, animation=False):
[0, 0, 0, 1]])
with offscreen.bind():
- bgl.glClearColor(0.0, 0.0, 0.0, 0.0)
- bgl.glClear(bgl.GL_COLOR_BUFFER_BIT)
+ fb = gpu.state.active_framebuffer_get()
+ fb.clear(color=(0.0, 0.0, 0.0, 0.0))
gpu.matrix.reset()
gpu.matrix.load_matrix(view_matrix)
gpu.matrix.load_projection_matrix(Matrix.Identity(4))
@@ -101,9 +100,8 @@ def render_main(self, context, animation=False):
y2 = height - y1
draw_rectangle((x1, y1), (x2, y2), rfcolor)
- buffer = bgl.Buffer(bgl.GL_BYTE, width * height * 4)
- bgl.glReadBuffer(bgl.GL_COLOR_ATTACHMENT0)
- bgl.glReadPixels(0, 0, width, height, bgl.GL_RGBA, bgl.GL_UNSIGNED_BYTE, buffer)
+ buffer = fb.read_color(0, 0, width, height, 4, 0, 'UBYTE')
+ buffer.dimensions = width * height * 4
offscreen.free()
diff --git a/mesh_bsurfaces.py b/mesh_bsurfaces.py
index 58ddd7aa..c980ed04 100644
--- a/mesh_bsurfaces.py
+++ b/mesh_bsurfaces.py
@@ -4,7 +4,7 @@
bl_info = {
"name": "Bsurfaces GPL Edition",
"author": "Eclectiel, Vladimir Spivak (cwolf3d)",
- "version": (1, 8, 0),
+ "version": (1, 8, 1),
"blender": (2, 80, 0),
"location": "View3D EditMode > Sidebar > Edit Tab",
"description": "Modeling and retopology tool",
@@ -2517,7 +2517,7 @@ class MESH_OT_SURFSK_add_surface(Operator):
self.average_gp_segment_length
)
for t in range(2):
- bpy.ops.curve.subdivide('INVOKE_REGION_WIN', number_cuts=segments)
+ bpy.ops.curve.subdivide('INVOKE_REGION_WIN', number_cuts=int(segments))
# Delete the other vertices and make it non-cyclic to
# keep only the needed verts of the "closing segment"
diff --git a/mesh_snap_utilities_line/op_line.py b/mesh_snap_utilities_line/op_line.py
index 7dcb4a52..eacc551b 100644
--- a/mesh_snap_utilities_line/op_line.py
+++ b/mesh_snap_utilities_line/op_line.py
@@ -51,10 +51,10 @@ def get_closest_edge(bm, point, dist):
return r_edge
-def get_loose_linked_edges(bmvert):
- linked = [e for e in bmvert.link_edges if not e.link_faces]
+def get_loose_linked_edges(vert):
+ linked = [e for e in vert.link_edges if e.is_wire]
for e in linked:
- linked += [le for v in e.verts if not v.link_faces for le in v.link_edges if le not in linked]
+ linked += [le for v in e.verts if v.is_wire for le in v.link_edges if le not in linked]
return linked
@@ -170,8 +170,28 @@ def make_line(self, bm_geom, location):
break
ed_list.update(get_loose_linked_edges(v2))
+ ed_list = list(ed_list)
+
+ # WORKAROUND: `edgenet_fill` only works with loose edges or boundary
+ # edges, so remove the other edges and create temporary elements to
+ # replace them.
+ targetmap = {}
+ ed_new = []
+ for edge in ed_list:
+ if not edge.is_wire and not edge.is_boundary:
+ v1, v2 = edge.verts
+ tmp_vert = bm.verts.new(v2.co)
+ e1 = bm.edges.new([v1, tmp_vert])
+ e2 = bm.edges.new([tmp_vert, v2])
+ ed_list.remove(edge)
+ ed_new.append(e1)
+ ed_new.append(e2)
+ targetmap[tmp_vert] = v2
+
+ bmesh.ops.edgenet_fill(bm, edges=ed_list + ed_new)
+ if targetmap:
+ bmesh.ops.weld_verts(bm, targetmap=targetmap)
- bmesh.ops.edgenet_fill(bm, edges=list(ed_list))
update_edit_mesh = True
# print('face created')
diff --git a/mesh_tissue/utils.py b/mesh_tissue/utils.py
index b617ac93..b4331084 100644
--- a/mesh_tissue/utils.py
+++ b/mesh_tissue/utils.py
@@ -1251,7 +1251,7 @@ def get_weight(vertex_group, n_verts):
:type vertex_group: :class:'bpy.types.VertexGroup'
:arg n_verts: Number of Vertices (output list size).
:type n_verts: int
- :return: Readed weight values.
+ :return: Read weight values.
:rtype: list
"""
weight = [0]*n_verts
@@ -1267,7 +1267,7 @@ def get_weight_numpy(vertex_group, n_verts):
:type vertex_group: :class:'bpy.types.VertexGroup'
:arg n_verts: Number of Vertices (output list size).
:type n_verts: int
- :return: Readed weight values as numpy array.
+ :return: Read weight values as numpy array.
:rtype: :class:'numpy.ndarray'
"""
weight = [0]*n_verts
diff --git a/mesh_tissue/weight_tools.py b/mesh_tissue/weight_tools.py
index 2736945e..8576b88c 100644
--- a/mesh_tissue/weight_tools.py
+++ b/mesh_tissue/weight_tools.py
@@ -2513,7 +2513,7 @@ class vertex_group_to_vertex_colors(Operator):
bpy.ops.object.mode_set(mode='OBJECT')
group_name = obj.vertex_groups[group_id].name
- bpy.ops.mesh.vertex_color_add()
+ me.vertex_colors.new()
colors_id = obj.data.vertex_colors.active_index
colors_name = group_name
@@ -2694,8 +2694,8 @@ class curvature_to_vertex_groups(Operator):
def execute(self, context):
bpy.ops.object.mode_set(mode='OBJECT')
- bpy.ops.mesh.vertex_color_add()
vertex_colors = context.active_object.data.vertex_colors
+ vertex_colors.new()
vertex_colors[-1].active = True
vertex_colors[-1].active_render = True
vertex_colors[-1].name = "Curvature"
@@ -2706,7 +2706,7 @@ class curvature_to_vertex_groups(Operator):
blur_iterations=self.blur_iterations, clean_angle=self.max_angle,
dirt_angle=self.min_angle)
bpy.ops.object.vertex_colors_to_vertex_groups(invert=self.invert)
- bpy.ops.mesh.vertex_color_remove()
+ vertex_colors.remove(vertex_colors.active)
return {'FINISHED'}
class face_area_to_vertex_groups(Operator):
diff --git a/node_wrangler.py b/node_wrangler.py
index 1a815dc6..cc1b85fe 100644
--- a/node_wrangler.py
+++ b/node_wrangler.py
@@ -3,7 +3,7 @@
bl_info = {
"name": "Node Wrangler",
"author": "Bartek Skorupa, Greg Zaal, Sebastian Koenig, Christian Brinkmann, Florian Meyer",
- "version": (3, 40),
+ "version": (3, 41),
"blender": (2, 93, 0),
"location": "Node Editor Toolbar or Shift-W",
"description": "Various tools to enhance and speed up node-based workflow",
@@ -72,406 +72,6 @@ rl_outputs = (
RL_entry('use_pass_z', 'Z', 'Depth', True, True),
)
-# shader nodes
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-# Keeping things in alphabetical order so we don't need to sort later.
-shaders_input_nodes_props = (
- ('ShaderNodeAmbientOcclusion', 'AMBIENT_OCCLUSION', 'Ambient Occlusion'),
- ('ShaderNodeAttribute', 'ATTRIBUTE', 'Attribute'),
- ('ShaderNodeBevel', 'BEVEL', 'Bevel'),
- ('ShaderNodeCameraData', 'CAMERA', 'Camera Data'),
- ('ShaderNodeFresnel', 'FRESNEL', 'Fresnel'),
- ('ShaderNodeNewGeometry', 'NEW_GEOMETRY', 'Geometry'),
- ('ShaderNodeHairInfo', 'HAIR_INFO', 'Hair Info'),
- ('ShaderNodeLayerWeight', 'LAYER_WEIGHT', 'Layer Weight'),
- ('ShaderNodeLightPath', 'LIGHT_PATH', 'Light Path'),
- ('ShaderNodeObjectInfo', 'OBJECT_INFO', 'Object Info'),
- ('ShaderNodeParticleInfo', 'PARTICLE_INFO', 'Particle Info'),
- ('ShaderNodeRGB', 'RGB', 'RGB'),
- ('ShaderNodeTangent', 'TANGENT', 'Tangent'),
- ('ShaderNodeTexCoord', 'TEX_COORD', 'Texture Coordinate'),
- ('ShaderNodeUVMap', 'UVMAP', 'UV Map'),
- ('ShaderNodeValue', 'VALUE', 'Value'),
- ('ShaderNodeVertexColor', 'VERTEX_COLOR', 'Vertex Color'),
- ('ShaderNodeVolumeInfo', 'VOLUME_INFO', 'Volume Info'),
- ('ShaderNodeWireframe', 'WIREFRAME', 'Wireframe'),
-
-)
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-# Keeping things in alphabetical order so we don't need to sort later.
-shaders_output_nodes_props = (
- ('ShaderNodeOutputAOV', 'OUTPUT_AOV', 'AOV Output'),
- ('ShaderNodeOutputLight', 'OUTPUT_LIGHT', 'Light Output'),
- ('ShaderNodeOutputMaterial', 'OUTPUT_MATERIAL', 'Material Output'),
- ('ShaderNodeOutputWorld', 'OUTPUT_WORLD', 'World Output'),
-)
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-# Keeping things in alphabetical order so we don't need to sort later.
-shaders_shader_nodes_props = (
- ('ShaderNodeAddShader', 'ADD_SHADER', 'Add Shader'),
- ('ShaderNodeBsdfAnisotropic', 'BSDF_ANISOTROPIC', 'Anisotropic BSDF'),
- ('ShaderNodeBsdfDiffuse', 'BSDF_DIFFUSE', 'Diffuse BSDF'),
- ('ShaderNodeEmission', 'EMISSION', 'Emission'),
- ('ShaderNodeBsdfGlass', 'BSDF_GLASS', 'Glass BSDF'),
- ('ShaderNodeBsdfGlossy', 'BSDF_GLOSSY', 'Glossy BSDF'),
- ('ShaderNodeBsdfHair', 'BSDF_HAIR', 'Hair BSDF'),
- ('ShaderNodeHoldout', 'HOLDOUT', 'Holdout'),
- ('ShaderNodeMixShader', 'MIX_SHADER', 'Mix Shader'),
- ('ShaderNodeBsdfPrincipled', 'BSDF_PRINCIPLED', 'Principled BSDF'),
- ('ShaderNodeBsdfHairPrincipled', 'BSDF_HAIR_PRINCIPLED', 'Principled Hair BSDF'),
- ('ShaderNodeVolumePrincipled', 'PRINCIPLED_VOLUME', 'Principled Volume'),
- ('ShaderNodeBsdfRefraction', 'BSDF_REFRACTION', 'Refraction BSDF'),
- ('ShaderNodeSubsurfaceScattering', 'SUBSURFACE_SCATTERING', 'Subsurface Scattering'),
- ('ShaderNodeBsdfToon', 'BSDF_TOON', 'Toon BSDF'),
- ('ShaderNodeBsdfTranslucent', 'BSDF_TRANSLUCENT', 'Translucent BSDF'),
- ('ShaderNodeBsdfTransparent', 'BSDF_TRANSPARENT', 'Transparent BSDF'),
- ('ShaderNodeBsdfVelvet', 'BSDF_VELVET', 'Velvet BSDF'),
- ('ShaderNodeBackground', 'BACKGROUND', 'Background'),
- ('ShaderNodeVolumeAbsorption', 'VOLUME_ABSORPTION', 'Volume Absorption'),
- ('ShaderNodeVolumeScatter', 'VOLUME_SCATTER', 'Volume Scatter'),
-)
-# (rna_type.identifier, type, rna_type.name)
-# Keeping things in alphabetical order so we don't need to sort later.
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-shaders_texture_nodes_props = (
- ('ShaderNodeTexBrick', 'TEX_BRICK', 'Brick Texture'),
- ('ShaderNodeTexChecker', 'TEX_CHECKER', 'Checker Texture'),
- ('ShaderNodeTexEnvironment', 'TEX_ENVIRONMENT', 'Environment Texture'),
- ('ShaderNodeTexGradient', 'TEX_GRADIENT', 'Gradient Texture'),
- ('ShaderNodeTexIES', 'TEX_IES', 'IES Texture'),
- ('ShaderNodeTexImage', 'TEX_IMAGE', 'Image Texture'),
- ('ShaderNodeTexMagic', 'TEX_MAGIC', 'Magic Texture'),
- ('ShaderNodeTexMusgrave', 'TEX_MUSGRAVE', 'Musgrave Texture'),
- ('ShaderNodeTexNoise', 'TEX_NOISE', 'Noise Texture'),
- ('ShaderNodeTexPointDensity', 'TEX_POINTDENSITY', 'Point Density'),
- ('ShaderNodeTexSky', 'TEX_SKY', 'Sky Texture'),
- ('ShaderNodeTexVoronoi', 'TEX_VORONOI', 'Voronoi Texture'),
- ('ShaderNodeTexWave', 'TEX_WAVE', 'Wave Texture'),
- ('ShaderNodeTexWhiteNoise', 'TEX_WHITE_NOISE', 'White Noise'),
-)
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-# Keeping things in alphabetical order so we don't need to sort later.
-shaders_color_nodes_props = (
- ('ShaderNodeBrightContrast', 'BRIGHTCONTRAST', 'Bright Contrast'),
- ('ShaderNodeGamma', 'GAMMA', 'Gamma'),
- ('ShaderNodeHueSaturation', 'HUE_SAT', 'Hue Saturation Value'),
- ('ShaderNodeInvert', 'INVERT', 'Invert'),
- ('ShaderNodeLightFalloff', 'LIGHT_FALLOFF', 'Light Falloff'),
- ('ShaderNodeMixRGB', 'MIX_RGB', 'MixRGB'),
- ('ShaderNodeRGBCurve', 'CURVE_RGB', 'RGB Curves'),
-)
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-# Keeping things in alphabetical order so we don't need to sort later.
-shaders_vector_nodes_props = (
- ('ShaderNodeBump', 'BUMP', 'Bump'),
- ('ShaderNodeDisplacement', 'DISPLACEMENT', 'Displacement'),
- ('ShaderNodeMapping', 'MAPPING', 'Mapping'),
- ('ShaderNodeNormal', 'NORMAL', 'Normal'),
- ('ShaderNodeNormalMap', 'NORMAL_MAP', 'Normal Map'),
- ('ShaderNodeVectorCurve', 'CURVE_VEC', 'Vector Curves'),
- ('ShaderNodeVectorDisplacement', 'VECTOR_DISPLACEMENT', 'Vector Displacement'),
- ('ShaderNodeVectorTransform', 'VECT_TRANSFORM', 'Vector Transform'),
-)
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-# Keeping things in alphabetical order so we don't need to sort later.
-shaders_converter_nodes_props = (
- ('ShaderNodeBlackbody', 'BLACKBODY', 'Blackbody'),
- ('ShaderNodeClamp', 'CLAMP', 'Clamp'),
- ('ShaderNodeValToRGB', 'VALTORGB', 'ColorRamp'),
- ('ShaderNodeCombineHSV', 'COMBHSV', 'Combine HSV'),
- ('ShaderNodeCombineRGB', 'COMBRGB', 'Combine RGB'),
- ('ShaderNodeCombineXYZ', 'COMBXYZ', 'Combine XYZ'),
- ('ShaderNodeMapRange', 'MAP_RANGE', 'Map Range'),
- ('ShaderNodeMath', 'MATH', 'Math'),
- ('ShaderNodeRGBToBW', 'RGBTOBW', 'RGB to BW'),
- ('ShaderNodeSeparateRGB', 'SEPRGB', 'Separate RGB'),
- ('ShaderNodeSeparateXYZ', 'SEPXYZ', 'Separate XYZ'),
- ('ShaderNodeSeparateHSV', 'SEPHSV', 'Separate HSV'),
- ('ShaderNodeVectorMath', 'VECT_MATH', 'Vector Math'),
- ('ShaderNodeWavelength', 'WAVELENGTH', 'Wavelength'),
-)
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-# Keeping things in alphabetical order so we don't need to sort later.
-shaders_layout_nodes_props = (
- ('NodeFrame', 'FRAME', 'Frame'),
- ('NodeReroute', 'REROUTE', 'Reroute'),
-)
-
-# compositing nodes
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-# Keeping things in alphabetical order so we don't need to sort later.
-compo_input_nodes_props = (
- ('CompositorNodeBokehImage', 'BOKEHIMAGE', 'Bokeh Image'),
- ('CompositorNodeImage', 'IMAGE', 'Image'),
- ('CompositorNodeMask', 'MASK', 'Mask'),
- ('CompositorNodeMovieClip', 'MOVIECLIP', 'Movie Clip'),
- ('CompositorNodeRLayers', 'R_LAYERS', 'Render Layers'),
- ('CompositorNodeRGB', 'RGB', 'RGB'),
- ('CompositorNodeTexture', 'TEXTURE', 'Texture'),
- ('CompositorNodeTime', 'TIME', 'Time'),
- ('CompositorNodeTrackPos', 'TRACKPOS', 'Track Position'),
- ('CompositorNodeValue', 'VALUE', 'Value'),
-)
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-# Keeping things in alphabetical order so we don't need to sort later.
-compo_output_nodes_props = (
- ('CompositorNodeComposite', 'COMPOSITE', 'Composite'),
- ('CompositorNodeOutputFile', 'OUTPUT_FILE', 'File Output'),
- ('CompositorNodeLevels', 'LEVELS', 'Levels'),
- ('CompositorNodeSplitViewer', 'SPLITVIEWER', 'Split Viewer'),
- ('CompositorNodeViewer', 'VIEWER', 'Viewer'),
-)
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-# Keeping things in alphabetical order so we don't need to sort later.
-compo_color_nodes_props = (
- ('CompositorNodeAlphaOver', 'ALPHAOVER', 'Alpha Over'),
- ('CompositorNodeBrightContrast', 'BRIGHTCONTRAST', 'Bright/Contrast'),
- ('CompositorNodeColorBalance', 'COLORBALANCE', 'Color Balance'),
- ('CompositorNodeColorCorrection', 'COLORCORRECTION', 'Color Correction'),
- ('CompositorNodeGamma', 'GAMMA', 'Gamma'),
- ('CompositorNodeHueCorrect', 'HUECORRECT', 'Hue Correct'),
- ('CompositorNodeHueSat', 'HUE_SAT', 'Hue Saturation Value'),
- ('CompositorNodeInvert', 'INVERT', 'Invert'),
- ('CompositorNodeMixRGB', 'MIX_RGB', 'Mix'),
- ('CompositorNodeCurveRGB', 'CURVE_RGB', 'RGB Curves'),
- ('CompositorNodeTonemap', 'TONEMAP', 'Tonemap'),
- ('CompositorNodeZcombine', 'ZCOMBINE', 'Z Combine'),
-)
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-# Keeping things in alphabetical order so we don't need to sort later.
-compo_converter_nodes_props = (
- ('CompositorNodePremulKey', 'PREMULKEY', 'Alpha Convert'),
- ('CompositorNodeValToRGB', 'VALTORGB', 'ColorRamp'),
- ('CompositorNodeCombHSVA', 'COMBHSVA', 'Combine HSVA'),
- ('CompositorNodeCombRGBA', 'COMBRGBA', 'Combine RGBA'),
- ('CompositorNodeCombYCCA', 'COMBYCCA', 'Combine YCbCrA'),
- ('CompositorNodeCombYUVA', 'COMBYUVA', 'Combine YUVA'),
- ('CompositorNodeIDMask', 'ID_MASK', 'ID Mask'),
- ('CompositorNodeMath', 'MATH', 'Math'),
- ('CompositorNodeRGBToBW', 'RGBTOBW', 'RGB to BW'),
- ('CompositorNodeSepRGBA', 'SEPRGBA', 'Separate RGBA'),
- ('CompositorNodeSepHSVA', 'SEPHSVA', 'Separate HSVA'),
- ('CompositorNodeSepYUVA', 'SEPYUVA', 'Separate YUVA'),
- ('CompositorNodeSepYCCA', 'SEPYCCA', 'Separate YCbCrA'),
- ('CompositorNodeSetAlpha', 'SETALPHA', 'Set Alpha'),
- ('CompositorNodeSwitchView', 'VIEWSWITCH', 'View Switch'),
-)
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-# Keeping things in alphabetical order so we don't need to sort later.
-compo_filter_nodes_props = (
- ('CompositorNodeBilateralblur', 'BILATERALBLUR', 'Bilateral Blur'),
- ('CompositorNodeBlur', 'BLUR', 'Blur'),
- ('CompositorNodeBokehBlur', 'BOKEHBLUR', 'Bokeh Blur'),
- ('CompositorNodeDefocus', 'DEFOCUS', 'Defocus'),
- ('CompositorNodeDenoise', 'DENOISE', 'Denoise'),
- ('CompositorNodeDespeckle', 'DESPECKLE', 'Despeckle'),
- ('CompositorNodeDilateErode', 'DILATEERODE', 'Dilate/Erode'),
- ('CompositorNodeDBlur', 'DBLUR', 'Directional Blur'),
- ('CompositorNodeFilter', 'FILTER', 'Filter'),
- ('CompositorNodeGlare', 'GLARE', 'Glare'),
- ('CompositorNodeInpaint', 'INPAINT', 'Inpaint'),
- ('CompositorNodePixelate', 'PIXELATE', 'Pixelate'),
- ('CompositorNodeSunBeams', 'SUNBEAMS', 'Sun Beams'),
- ('CompositorNodeVecBlur', 'VECBLUR', 'Vector Blur'),
-)
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-# Keeping things in alphabetical order so we don't need to sort later.
-compo_vector_nodes_props = (
- ('CompositorNodeMapRange', 'MAP_RANGE', 'Map Range'),
- ('CompositorNodeMapValue', 'MAP_VALUE', 'Map Value'),
- ('CompositorNodeNormal', 'NORMAL', 'Normal'),
- ('CompositorNodeNormalize', 'NORMALIZE', 'Normalize'),
- ('CompositorNodeCurveVec', 'CURVE_VEC', 'Vector Curves'),
-)
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-# Keeping things in alphabetical order so we don't need to sort later.
-compo_matte_nodes_props = (
- ('CompositorNodeBoxMask', 'BOXMASK', 'Box Mask'),
- ('CompositorNodeChannelMatte', 'CHANNEL_MATTE', 'Channel Key'),
- ('CompositorNodeChromaMatte', 'CHROMA_MATTE', 'Chroma Key'),
- ('CompositorNodeColorMatte', 'COLOR_MATTE', 'Color Key'),
- ('CompositorNodeColorSpill', 'COLOR_SPILL', 'Color Spill'),
- ('CompositorNodeCryptomatte', 'CRYPTOMATTE', 'Cryptomatte'),
- ('CompositorNodeDiffMatte', 'DIFF_MATTE', 'Difference Key'),
- ('CompositorNodeDistanceMatte', 'DISTANCE_MATTE', 'Distance Key'),
- ('CompositorNodeDoubleEdgeMask', 'DOUBLEEDGEMASK', 'Double Edge Mask'),
- ('CompositorNodeEllipseMask', 'ELLIPSEMASK', 'Ellipse Mask'),
- ('CompositorNodeKeying', 'KEYING', 'Keying'),
- ('CompositorNodeKeyingScreen', 'KEYINGSCREEN', 'Keying Screen'),
- ('CompositorNodeLumaMatte', 'LUMA_MATTE', 'Luminance Key'),
-)
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-# Keeping things in alphabetical order so we don't need to sort later.
-compo_distort_nodes_props = (
- ('CompositorNodeCornerPin', 'CORNERPIN', 'Corner Pin'),
- ('CompositorNodeCrop', 'CROP', 'Crop'),
- ('CompositorNodeDisplace', 'DISPLACE', 'Displace'),
- ('CompositorNodeFlip', 'FLIP', 'Flip'),
- ('CompositorNodeLensdist', 'LENSDIST', 'Lens Distortion'),
- ('CompositorNodeMapUV', 'MAP_UV', 'Map UV'),
- ('CompositorNodeMovieDistortion', 'MOVIEDISTORTION', 'Movie Distortion'),
- ('CompositorNodePlaneTrackDeform', 'PLANETRACKDEFORM', 'Plane Track Deform'),
- ('CompositorNodeRotate', 'ROTATE', 'Rotate'),
- ('CompositorNodeScale', 'SCALE', 'Scale'),
- ('CompositorNodeStabilize', 'STABILIZE2D', 'Stabilize 2D'),
- ('CompositorNodeTransform', 'TRANSFORM', 'Transform'),
- ('CompositorNodeTranslate', 'TRANSLATE', 'Translate'),
-)
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-# Keeping things in alphabetical order so we don't need to sort later.
-compo_layout_nodes_props = (
- ('NodeFrame', 'FRAME', 'Frame'),
- ('NodeReroute', 'REROUTE', 'Reroute'),
- ('CompositorNodeSwitch', 'SWITCH', 'Switch'),
-)
-# Blender Render material nodes
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-blender_mat_input_nodes_props = (
- ('ShaderNodeMaterial', 'MATERIAL', 'Material'),
- ('ShaderNodeCameraData', 'CAMERA', 'Camera Data'),
- ('ShaderNodeLightData', 'LIGHT', 'Light Data'),
- ('ShaderNodeValue', 'VALUE', 'Value'),
- ('ShaderNodeRGB', 'RGB', 'RGB'),
- ('ShaderNodeTexture', 'TEXTURE', 'Texture'),
- ('ShaderNodeGeometry', 'GEOMETRY', 'Geometry'),
- ('ShaderNodeExtendedMaterial', 'MATERIAL_EXT', 'Extended Material'),
-)
-
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-blender_mat_output_nodes_props = (
- ('ShaderNodeOutput', 'OUTPUT', 'Output'),
-)
-
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-blender_mat_color_nodes_props = (
- ('ShaderNodeMixRGB', 'MIX_RGB', 'MixRGB'),
- ('ShaderNodeRGBCurve', 'CURVE_RGB', 'RGB Curves'),
- ('ShaderNodeInvert', 'INVERT', 'Invert'),
- ('ShaderNodeHueSaturation', 'HUE_SAT', 'Hue Saturation Value'),
-)
-
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-blender_mat_vector_nodes_props = (
- ('ShaderNodeNormal', 'NORMAL', 'Normal'),
- ('ShaderNodeMapping', 'MAPPING', 'Mapping'),
- ('ShaderNodeVectorCurve', 'CURVE_VEC', 'Vector Curves'),
-)
-
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-blender_mat_converter_nodes_props = (
- ('ShaderNodeValToRGB', 'VALTORGB', 'ColorRamp'),
- ('ShaderNodeRGBToBW', 'RGBTOBW', 'RGB to BW'),
- ('ShaderNodeMath', 'MATH', 'Math'),
- ('ShaderNodeVectorMath', 'VECT_MATH', 'Vector Math'),
- ('ShaderNodeSqueeze', 'SQUEEZE', 'Squeeze Value'),
- ('ShaderNodeSeparateRGB', 'SEPRGB', 'Separate RGB'),
- ('ShaderNodeCombineRGB', 'COMBRGB', 'Combine RGB'),
- ('ShaderNodeSeparateHSV', 'SEPHSV', 'Separate HSV'),
- ('ShaderNodeCombineHSV', 'COMBHSV', 'Combine HSV'),
-)
-
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-blender_mat_layout_nodes_props = (
- ('NodeReroute', 'REROUTE', 'Reroute'),
-)
-
-# Texture Nodes
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-texture_input_nodes_props = (
- ('TextureNodeCurveTime', 'CURVE_TIME', 'Curve Time'),
- ('TextureNodeCoordinates', 'COORD', 'Coordinates'),
- ('TextureNodeTexture', 'TEXTURE', 'Texture'),
- ('TextureNodeImage', 'IMAGE', 'Image'),
-)
-
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-texture_output_nodes_props = (
- ('TextureNodeOutput', 'OUTPUT', 'Output'),
- ('TextureNodeViewer', 'VIEWER', 'Viewer'),
-)
-
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-texture_color_nodes_props = (
- ('TextureNodeMixRGB', 'MIX_RGB', 'Mix RGB'),
- ('TextureNodeCurveRGB', 'CURVE_RGB', 'RGB Curves'),
- ('TextureNodeInvert', 'INVERT', 'Invert'),
- ('TextureNodeHueSaturation', 'HUE_SAT', 'Hue Saturation Value'),
- ('TextureNodeCompose', 'COMPOSE', 'Combine RGBA'),
- ('TextureNodeDecompose', 'DECOMPOSE', 'Separate RGBA'),
-)
-
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-texture_pattern_nodes_props = (
- ('TextureNodeChecker', 'CHECKER', 'Checker'),
- ('TextureNodeBricks', 'BRICKS', 'Bricks'),
-)
-
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-texture_textures_nodes_props = (
- ('TextureNodeTexNoise', 'TEX_NOISE', 'Noise'),
- ('TextureNodeTexDistNoise', 'TEX_DISTNOISE', 'Distorted Noise'),
- ('TextureNodeTexClouds', 'TEX_CLOUDS', 'Clouds'),
- ('TextureNodeTexBlend', 'TEX_BLEND', 'Blend'),
- ('TextureNodeTexVoronoi', 'TEX_VORONOI', 'Voronoi'),
- ('TextureNodeTexMagic', 'TEX_MAGIC', 'Magic'),
- ('TextureNodeTexMarble', 'TEX_MARBLE', 'Marble'),
- ('TextureNodeTexWood', 'TEX_WOOD', 'Wood'),
- ('TextureNodeTexMusgrave', 'TEX_MUSGRAVE', 'Musgrave'),
- ('TextureNodeTexStucci', 'TEX_STUCCI', 'Stucci'),
-)
-
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-texture_converter_nodes_props = (
- ('TextureNodeMath', 'MATH', 'Math'),
- ('TextureNodeValToRGB', 'VALTORGB', 'ColorRamp'),
- ('TextureNodeRGBToBW', 'RGBTOBW', 'RGB to BW'),
- ('TextureNodeValToNor', 'VALTONOR', 'Value to Normal'),
- ('TextureNodeDistance', 'DISTANCE', 'Distance'),
-)
-
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-texture_distort_nodes_props = (
- ('TextureNodeScale', 'SCALE', 'Scale'),
- ('TextureNodeTranslate', 'TRANSLATE', 'Translate'),
- ('TextureNodeRotate', 'ROTATE', 'Rotate'),
- ('TextureNodeAt', 'AT', 'At'),
-)
-
-# (rna_type.identifier, type, rna_type.name)
-# Keeping mixed case to avoid having to translate entries when adding new nodes in operators.
-texture_layout_nodes_props = (
- ('NodeReroute', 'REROUTE', 'Reroute'),
-)
-
# list of blend types of "Mix" nodes in a form that can be used as 'items' for EnumProperty.
# used list, not tuple for easy merging with other lists.
blend_types = [
@@ -596,6 +196,13 @@ def get_nodes_from_category(category_name, context):
if category.name == category_name:
return sorted(category.items(context), key=lambda node: node.label)
+def get_first_enabled_output(node):
+ for output in node.outputs:
+ if output.enabled:
+ return output
+ else:
+ return node.outputs[0]
+
def is_visible_socket(socket):
return not socket.hide and socket.enabled and socket.type != 'CUSTOM'
@@ -813,8 +420,7 @@ def draw_circle_2d_filled(shader, mx, my, radius, colour=(1.0, 1.0, 1.0, 0.7)):
def draw_rounded_node_border(shader, node, radius=8, colour=(1.0, 1.0, 1.0, 0.7)):
- area_width = bpy.context.area.width - (16*dpifac()) - 1
- bottom_bar = (16*dpifac()) + 1
+ area_width = bpy.context.area.width
sides = 16
radius = radius*dpifac()
@@ -840,7 +446,7 @@ def draw_rounded_node_border(shader, node, radius=8, colour=(1.0, 1.0, 1.0, 0.7)
vertices = [(mx,my)]
for i in range(sides+1):
if (4<=i<=8):
- if my > bottom_bar and mx < area_width:
+ if mx < area_width:
cosine = radius * cos(i * 2 * pi / sides) + mx
sine = radius * sin(i * 2 * pi / sides) + my
vertices.append((cosine,sine))
@@ -854,7 +460,7 @@ def draw_rounded_node_border(shader, node, radius=8, colour=(1.0, 1.0, 1.0, 0.7)
vertices = [(mx,my)]
for i in range(sides+1):
if (0<=i<=4):
- if my > bottom_bar and mx < area_width:
+ if mx < area_width:
cosine = radius * cos(i * 2 * pi / sides) + mx
sine = radius * sin(i * 2 * pi / sides) + my
vertices.append((cosine,sine))
@@ -868,7 +474,7 @@ def draw_rounded_node_border(shader, node, radius=8, colour=(1.0, 1.0, 1.0, 0.7)
vertices = [(mx,my)]
for i in range(sides+1):
if (8<=i<=12):
- if my > bottom_bar and mx < area_width:
+ if mx < area_width:
cosine = radius * cos(i * 2 * pi / sides) + mx
sine = radius * sin(i * 2 * pi / sides) + my
vertices.append((cosine,sine))
@@ -882,7 +488,7 @@ def draw_rounded_node_border(shader, node, radius=8, colour=(1.0, 1.0, 1.0, 0.7)
vertices = [(mx,my)]
for i in range(sides+1):
if (12<=i<=16):
- if my > bottom_bar and mx < area_width:
+ if mx < area_width:
cosine = radius * cos(i * 2 * pi / sides) + mx
sine = radius * sin(i * 2 * pi / sides) + my
vertices.append((cosine,sine))
@@ -911,18 +517,15 @@ def draw_rounded_node_border(shader, node, radius=8, colour=(1.0, 1.0, 1.0, 0.7)
m2x, m2y = bpy.context.region.view2d.view_to_region(nlocx + ndimx, nlocy, clip=False)
m1x = min(m1x, area_width)
m2x = min(m2x, area_width)
- if m1y > bottom_bar and m2y > bottom_bar:
- vertices.extend([(m1x,m1y), (m2x,m1y),
- (m2x,m1y+radius), (m1x,m1y+radius)])
- indices.extend([(id_last, id_last+1, id_last+3),
- (id_last+3, id_last+1, id_last+2)])
- id_last += 4
+ vertices.extend([(m1x,m1y), (m2x,m1y),
+ (m2x,m1y+radius), (m1x,m1y+radius)])
+ indices.extend([(id_last, id_last+1, id_last+3),
+ (id_last+3, id_last+1, id_last+2)])
+ id_last += 4
# Right edge
m1x, m1y = bpy.context.region.view2d.view_to_region(nlocx + ndimx, nlocy, clip=False)
m2x, m2y = bpy.context.region.view2d.view_to_region(nlocx + ndimx, nlocy - ndimy, clip=False)
- m1y = max(m1y, bottom_bar)
- m2y = max(m2y, bottom_bar)
if m1x < area_width and m2x < area_width:
vertices.extend([(m1x,m2y), (m1x+radius,m2y),
(m1x+radius,m1y), (m1x,m1y)])
@@ -935,11 +538,10 @@ def draw_rounded_node_border(shader, node, radius=8, colour=(1.0, 1.0, 1.0, 0.7)
m2x, m2y = bpy.context.region.view2d.view_to_region(nlocx + ndimx, nlocy-ndimy, clip=False)
m1x = min(m1x, area_width)
m2x = min(m2x, area_width)
- if m1y > bottom_bar and m2y > bottom_bar:
- vertices.extend([(m1x,m2y), (m2x,m2y),
- (m2x,m1y-radius), (m1x,m1y-radius)])
- indices.extend([(id_last, id_last+1, id_last+3),
- (id_last+3, id_last+1, id_last+2)])
+ vertices.extend([(m1x,m2y), (m2x,m2y),
+ (m2x,m1y-radius), (m1x,m1y-radius)])
+ indices.extend([(id_last, id_last+1, id_last+3),
+ (id_last+3, id_last+1, id_last+2)])
# now draw all edges in one batch
if len(vertices) != 0:
@@ -1051,7 +653,7 @@ def get_internal_socket(socket):
return iterator[i]
def is_viewer_link(link, output_node):
- if "Emission Viewer" in link.to_node.name or link.to_node == output_node and link.to_socket == output_node.inputs[0]:
+ if link.to_node == output_node and link.to_socket == output_node.inputs[0]:
return True
if link.to_node.type == 'GROUP_OUTPUT':
socket = get_internal_socket(link.to_socket)
@@ -1068,8 +670,6 @@ def get_output_location(tree):
# get right-most location
sorted_by_xloc = (sorted(tree.nodes, key=lambda x: x.location.x))
max_xloc_node = sorted_by_xloc[-1]
- if max_xloc_node.name == 'Emission Viewer':
- max_xloc_node = sorted_by_xloc[-2]
# get average y location
sum_yloc = 0
@@ -1686,7 +1286,7 @@ class NWAddAttrNode(Operator, NWBase):
class NWPreviewNode(Operator, NWBase):
bl_idname = "node.nw_preview_node"
bl_label = "Preview Node"
- bl_description = "Connect active node to Emission Shader for shadeless previews, or to the geometry node tree's output"
+ bl_description = "Connect active node to the Node Group output or the Material Output"
bl_options = {'REGISTER', 'UNDO'}
# If false, the operator is not executed if the current node group happens to be a geometry nodes group.
@@ -1697,7 +1297,6 @@ class NWPreviewNode(Operator, NWBase):
def __init__(self):
self.shader_output_type = ""
self.shader_output_ident = ""
- self.shader_viewer_ident = ""
@classmethod
def poll(cls, context):
@@ -1748,16 +1347,13 @@ class NWPreviewNode(Operator, NWBase):
if space.id not in [light for light in bpy.data.lights]: # cannot use bpy.data.lights directly as iterable
self.shader_output_type = "OUTPUT_MATERIAL"
self.shader_output_ident = "ShaderNodeOutputMaterial"
- self.shader_viewer_ident = "ShaderNodeEmission"
else:
self.shader_output_type = "OUTPUT_LIGHT"
self.shader_output_ident = "ShaderNodeOutputLight"
- self.shader_viewer_ident = "ShaderNodeEmission"
elif shader_type == 'WORLD':
self.shader_output_type = "OUTPUT_WORLD"
self.shader_output_ident = "ShaderNodeOutputWorld"
- self.shader_viewer_ident = "ShaderNodeBackground"
def get_shader_output_node(self, tree):
for node in tree.nodes:
@@ -1820,8 +1416,7 @@ class NWPreviewNode(Operator, NWBase):
self.used_viewer_sockets_active_mat = []
materialout = self.get_shader_output_node(bpy.context.space_data.node_tree)
if materialout:
- emission = self.get_viewer_node(materialout)
- self.search_sockets((emission if emission else materialout), self.used_viewer_sockets_active_mat)
+ self.search_sockets(materialout, self.used_viewer_sockets_active_mat)
return socket in self.used_viewer_sockets_active_mat
def is_socket_used_other_mats(self, socket):
@@ -1834,18 +1429,9 @@ class NWPreviewNode(Operator, NWBase):
# get viewer node
materialout = self.get_shader_output_node(mat.node_tree)
if materialout:
- emission = self.get_viewer_node(materialout)
- self.search_sockets((emission if emission else materialout), self.used_viewer_sockets_other_mats)
+ self.search_sockets(materialout, self.used_viewer_sockets_other_mats)
return socket in self.used_viewer_sockets_other_mats
- @staticmethod
- def get_viewer_node(materialout):
- input_socket = materialout.inputs[0]
- if len(input_socket.links) > 0:
- node = input_socket.links[0].from_node
- if node.type == 'EMISSION' and node.name == "Emission Viewer":
- return node
-
def invoke(self, context, event):
space = context.space_data
# Ignore operator when running in wrong context.
@@ -1854,7 +1440,6 @@ class NWPreviewNode(Operator, NWBase):
shader_type = space.shader_type
self.init_shader_variables(space, shader_type)
- shader_types = [x[1] for x in shaders_shader_nodes_props]
mlocx = event.mouse_region_x
mlocy = event.mouse_region_y
select_node = bpy.ops.node.select(location=(mlocx, mlocy), extend=False)
@@ -1864,8 +1449,7 @@ class NWPreviewNode(Operator, NWBase):
base_node_tree = space.node_tree
active = nodes.active
- # For geometry node trees we just connect to the group output,
- # because there is no "viewer node" yet.
+ # For geometry node trees we just connect to the group output
if space.tree_type == "GeometryNodeTree":
valid = False
if active:
@@ -1903,7 +1487,6 @@ class NWPreviewNode(Operator, NWBase):
out_i = valid_outputs[0]
make_links = [] # store sockets for new links
- delete_nodes = [] # store unused nodes to delete in the end
if active.outputs:
# If there is no 'GEOMETRY' output type - We can't preview the node
if out_i is None:
@@ -1944,10 +1527,6 @@ class NWPreviewNode(Operator, NWBase):
tree = socket.id_data
tree.outputs.remove(socket)
- # Delete nodes
- for tree, node in delete_nodes:
- tree.nodes.remove(node)
-
nodes.active = active
active.select = True
force_update(context)
@@ -1955,10 +1534,11 @@ class NWPreviewNode(Operator, NWBase):
# What follows is code for the shader editor
- output_types = [x[1] for x in shaders_output_nodes_props]
+ output_types = [x.nodetype for x in
+ get_nodes_from_category('Output', context)]
valid = False
if active:
- if (active.name != "Emission Viewer") and (active.type not in output_types):
+ if active.rna_type.identifier not in output_types:
for out in active.outputs:
if is_visible_socket(out):
valid = True
@@ -1976,7 +1556,7 @@ class NWPreviewNode(Operator, NWBase):
materialout = base_node_tree.nodes.new(self.shader_output_ident)
materialout.location = get_output_location(base_node_tree)
materialout.select = False
- # Analyze outputs, add "Emission Viewer" if needed, make links
+ # Analyze outputs
out_i = None
valid_outputs = []
for i, out in enumerate(active.outputs):
@@ -1994,56 +1574,11 @@ class NWPreviewNode(Operator, NWBase):
out_i = valid_outputs[0]
make_links = [] # store sockets for new links
- delete_nodes = [] # store unused nodes to delete in the end
if active.outputs:
- # If output type not 'SHADER' - "Emission Viewer" needed
- if active.outputs[out_i].type != 'SHADER':
- socket_type = 'NodeSocketColor'
- # get Emission Viewer node
- emission_exists = False
- emission_placeholder = base_node_tree.nodes[0]
- for node in base_node_tree.nodes:
- if "Emission Viewer" in node.name:
- emission_exists = True
- emission_placeholder = node
- if not emission_exists:
- emission = base_node_tree.nodes.new(self.shader_viewer_ident)
- emission.hide = True
- emission.location = [materialout.location.x, (materialout.location.y + 40)]
- emission.label = "Viewer"
- emission.name = "Emission Viewer"
- emission.use_custom_color = True
- emission.color = (0.6, 0.5, 0.4)
- emission.select = False
- else:
- emission = emission_placeholder
- output_socket = emission.inputs[0]
-
- # If Viewer is connected to output by user, don't change those connections (patch by gandalf3)
- if emission.outputs[0].links.__len__() > 0:
- if not emission.outputs[0].links[0].to_node == materialout:
- make_links.append((emission.outputs[0], materialout.inputs[0]))
- else:
- make_links.append((emission.outputs[0], materialout.inputs[0]))
-
- # Set brightness of viewer to compensate for Film and CM exposure
- if context.scene.render.engine == 'CYCLES' and hasattr(context.scene, 'cycles'):
- intensity = 1/context.scene.cycles.film_exposure # Film exposure is a multiplier
- else:
- intensity = 1
-
- intensity /= pow(2, (context.scene.view_settings.exposure)) # CM exposure is measured in stops/EVs (2^x)
- emission.inputs[1].default_value = intensity
-
- else:
- # Output type is 'SHADER', no Viewer needed. Delete Viewer if exists.
- socket_type = 'NodeSocketShader'
- materialout_index = 1 if active.outputs[out_i].name == "Volume" else 0
- make_links.append((active.outputs[out_i], materialout.inputs[materialout_index]))
- output_socket = materialout.inputs[materialout_index]
- for node in base_node_tree.nodes:
- if node.name == 'Emission Viewer':
- delete_nodes.append((base_node_tree, node))
+ socket_type = 'NodeSocketShader'
+ materialout_index = 1 if active.outputs[out_i].name == "Volume" else 0
+ make_links.append((active.outputs[out_i], materialout.inputs[materialout_index]))
+ output_socket = materialout.inputs[materialout_index]
for li_from, li_to in make_links:
base_node_tree.links.new(li_from, li_to)
@@ -2069,10 +1604,6 @@ class NWPreviewNode(Operator, NWBase):
tree = socket.id_data
tree.outputs.remove(socket)
- # Delete nodes
- for tree, node in delete_nodes:
- tree.nodes.remove(node)
-
nodes.active = active
active.select = True
@@ -2094,14 +1625,27 @@ class NWFrameSelected(Operator, NWBase):
description='The visual name of the frame node',
default=' '
)
+ use_custom_color_prop: BoolProperty(
+ name="Custom Color",
+ description="Use custom color for the frame node",
+ default=False
+ )
color_prop: FloatVectorProperty(
name="Color",
description="The color of the frame node",
- default=(0.6, 0.6, 0.6),
+ default=(0.604, 0.604, 0.604),
min=0, max=1, step=1, precision=3,
subtype='COLOR_GAMMA', size=3
)
+ def draw(self, context):
+ layout = self.layout
+ layout.prop(self, 'label_prop')
+ layout.prop(self, 'use_custom_color_prop')
+ col = layout.column()
+ col.active = self.use_custom_color_prop
+ col.prop(self, 'color_prop', text="")
+
def execute(self, context):
nodes, links = get_nodes_links(context)
selected = []
@@ -2112,7 +1656,7 @@ class NWFrameSelected(Operator, NWBase):
bpy.ops.node.add_node(type='NodeFrame')
frm = nodes.active
frm.label = self.label_prop
- frm.use_custom_color = True
+ frm.use_custom_color = self.use_custom_color_prop
frm.color = self.color_prop
for node in selected:
@@ -2170,51 +1714,17 @@ class NWSwitchNodeType(Operator, NWBase):
bl_label = "Switch Node Type"
bl_options = {'REGISTER', 'UNDO'}
- to_type: EnumProperty(
- name="Switch to type",
- items=list(shaders_input_nodes_props) +
- list(shaders_output_nodes_props) +
- list(shaders_shader_nodes_props) +
- list(shaders_texture_nodes_props) +
- list(shaders_color_nodes_props) +
- list(shaders_vector_nodes_props) +
- list(shaders_converter_nodes_props) +
- list(shaders_layout_nodes_props) +
- list(compo_input_nodes_props) +
- list(compo_output_nodes_props) +
- list(compo_color_nodes_props) +
- list(compo_converter_nodes_props) +
- list(compo_filter_nodes_props) +
- list(compo_vector_nodes_props) +
- list(compo_matte_nodes_props) +
- list(compo_distort_nodes_props) +
- list(compo_layout_nodes_props) +
- list(blender_mat_input_nodes_props) +
- list(blender_mat_output_nodes_props) +
- list(blender_mat_color_nodes_props) +
- list(blender_mat_vector_nodes_props) +
- list(blender_mat_converter_nodes_props) +
- list(blender_mat_layout_nodes_props) +
- list(texture_input_nodes_props) +
- list(texture_output_nodes_props) +
- list(texture_color_nodes_props) +
- list(texture_pattern_nodes_props) +
- list(texture_textures_nodes_props) +
- list(texture_converter_nodes_props) +
- list(texture_distort_nodes_props) +
- list(texture_layout_nodes_props)
- )
-
- geo_to_type: StringProperty(
+ to_type: StringProperty(
name="Switch to type",
default = '',
)
def execute(self, context):
- nodes, links = get_nodes_links(context)
to_type = self.to_type
- if self.geo_to_type != '':
- to_type = self.geo_to_type
+ if len(to_type) == 0:
+ return {'CANCELLED'}
+
+ nodes, links = get_nodes_links(context)
# Those types of nodes will not swap.
src_excludes = ('NodeFrame')
# Those attributes of nodes will be copied if possible
@@ -2511,8 +2021,8 @@ class NWMergeNodes(Operator, NWBase):
mode = 'MIX'
if (merge_type != 'MATH' and merge_type != 'GEOMETRY') and tree_type == 'GEOMETRY':
merge_type = 'AUTO'
- # The math nodes used for geometry nodes are of type 'ShaderNode'
- if merge_type == 'MATH' and tree_type == 'GEOMETRY':
+ # The MixRGB node and math nodes used for geometry nodes are of type 'ShaderNode'
+ if (merge_type == 'MATH' or merge_type == 'MIX') and tree_type == 'GEOMETRY':
node_type = 'ShaderNode'
selected_mix = [] # entry = [index, loc]
selected_shader = [] # entry = [index, loc]
@@ -2532,7 +2042,8 @@ class NWMergeNodes(Operator, NWBase):
('VALUE', [t[0] for t in operations], selected_math),
('VECTOR', [], selected_vector),
):
- output_type = node.outputs[0].type
+ output = get_first_enabled_output(node)
+ output_type = output.type
valid_mode = mode in types_list
# When mode is 'MIX' we have to cheat since the mix node is not used in
# geometry nodes.
@@ -2583,7 +2094,7 @@ class NWMergeNodes(Operator, NWBase):
# Change the node type for math nodes in a geometry node tree.
if tree_type == 'GEOMETRY':
- if nodes_list is selected_math or nodes_list is selected_vector:
+ if nodes_list is selected_math or nodes_list is selected_vector or nodes_list is selected_mix:
node_type = 'ShaderNode'
if mode == 'MIX':
mode = 'ADD'
@@ -2708,8 +2219,9 @@ class NWMergeNodes(Operator, NWBase):
# Special case:
# Two nodes were selected and first selected has no output links, second selected has output links.
# Then add links from last add to all links 'to_socket' of out links of second selected.
+ first_selected_output = get_first_enabled_output(first_selected)
if len(nodes_list) == 2:
- if not first_selected.outputs[0].links:
+ if not first_selected_output.links:
second_selected = nodes[nodes_list[1][0]]
for ss_link in second_selected.outputs[0].links:
# Prevent cyclic dependencies when nodes to be merged are linked to one another.
@@ -2717,16 +2229,16 @@ class NWMergeNodes(Operator, NWBase):
if not self.link_creates_cycle(ss_link, invalid_nodes):
links.new(last_add.outputs[0], ss_link.to_socket)
# add links from last_add to all links 'to_socket' of out links of first selected.
- for fs_link in first_selected.outputs[0].links:
+ for fs_link in first_selected_output.links:
# Link only if "to_node" index not in invalid indexes list.
if not self.link_creates_cycle(fs_link, invalid_nodes):
links.new(last_add.outputs[0], fs_link.to_socket)
# add link from "first" selected and "first" add node
node_to = nodes[count_after - 1]
- links.new(first_selected.outputs[0], node_to.inputs[first])
+ links.new(first_selected_output, node_to.inputs[first])
if node_to.type == 'ZCOMBINE':
for fs_out in first_selected.outputs:
- if fs_out != first_selected.outputs[0] and fs_out.name in ('Z', 'Depth'):
+ if fs_out != first_selected_output and fs_out.name in ('Z', 'Depth'):
links.new(fs_out, node_to.inputs[1])
break
# add links between added ADD nodes and between selected and ADD nodes
@@ -2736,20 +2248,20 @@ class NWMergeNodes(Operator, NWBase):
node_to = nodes[index - 1]
node_to_input_i = first
node_to_z_i = 1 # if z combine - link z to first z input
- links.new(node_from.outputs[0], node_to.inputs[node_to_input_i])
+ links.new(get_first_enabled_output(node_from), node_to.inputs[node_to_input_i])
if node_to.type == 'ZCOMBINE':
for from_out in node_from.outputs:
- if from_out != node_from.outputs[0] and from_out.name in ('Z', 'Depth'):
+ if from_out != get_first_enabled_output(node_from) and from_out.name in ('Z', 'Depth'):
links.new(from_out, node_to.inputs[node_to_z_i])
if len(nodes_list) > 1:
node_from = nodes[nodes_list[i + 1][0]]
node_to = nodes[index]
node_to_input_i = second
node_to_z_i = 3 # if z combine - link z to second z input
- links.new(node_from.outputs[0], node_to.inputs[node_to_input_i])
+ links.new(get_first_enabled_output(node_from), node_to.inputs[node_to_input_i])
if node_to.type == 'ZCOMBINE':
for from_out in node_from.outputs:
- if from_out != node_from.outputs[0] and from_out.name in ('Z', 'Depth'):
+ if from_out != get_first_enabled_output(node_from) and from_out.name in ('Z', 'Depth'):
links.new(from_out, node_to.inputs[node_to_z_i])
index -= 1
# set "last" of added nodes as active
@@ -3072,71 +2584,74 @@ class NWAddTextureSetup(Operator, NWBase):
@classmethod
def poll(cls, context):
- valid = False
if nw_check(context):
space = context.space_data
if space.tree_type == 'ShaderNodeTree':
- valid = True
- return valid
+ return True
+ return False
def execute(self, context):
nodes, links = get_nodes_links(context)
- shader_types = [x[1] for x in shaders_shader_nodes_props if x[1] not in {'MIX_SHADER', 'ADD_SHADER'}]
- texture_types = [x[1] for x in shaders_texture_nodes_props]
+
+ texture_types = [x.nodetype for x in
+ get_nodes_from_category('Texture', context)]
selected_nodes = [n for n in nodes if n.select]
- for t_node in selected_nodes:
- valid = False
+
+ for node in selected_nodes:
+ if not node.inputs:
+ continue
+
input_index = 0
- if t_node.inputs:
- for index, i in enumerate(t_node.inputs):
- if not i.is_linked:
- valid = True
- input_index = index
+ target_input = node.inputs[0]
+ for input in node.inputs:
+ if input.enabled:
+ input_index += 1
+ if not input.is_linked:
+ target_input = input
break
- if valid:
- locx = t_node.location.x
- locy = t_node.location.y - t_node.dimensions.y/2
-
- xoffset = [500, 700]
- is_texture = False
- if t_node.type in texture_types + ['MAPPING']:
- xoffset = [290, 500]
- is_texture = True
-
- coordout = 2
- image_type = 'ShaderNodeTexImage'
-
- if (t_node.type in texture_types and t_node.type != 'TEX_IMAGE') or (t_node.type == 'BACKGROUND'):
- coordout = 0 # image texture uses UVs, procedural textures and Background shader use Generated
- if t_node.type == 'BACKGROUND':
- image_type = 'ShaderNodeTexEnvironment'
-
- if not is_texture:
- tex = nodes.new(image_type)
- tex.location = [locx - 200, locy + 112]
- nodes.active = tex
- links.new(tex.outputs[0], t_node.inputs[input_index])
-
- t_node.select = False
- if self.add_mapping or is_texture:
- if t_node.type != 'MAPPING':
- m = nodes.new('ShaderNodeMapping')
- m.location = [locx - xoffset[0], locy + 141]
- m.width = 240
- else:
- m = t_node
- coord = nodes.new('ShaderNodeTexCoord')
- coord.location = [locx - (200 if t_node.type == 'MAPPING' else xoffset[1]), locy + 124]
-
- if not is_texture:
- links.new(m.outputs[0], tex.inputs[0])
- links.new(coord.outputs[coordout], m.inputs[0])
- else:
- nodes.active = m
- links.new(m.outputs[0], t_node.inputs[input_index])
- links.new(coord.outputs[coordout], m.inputs[0])
else:
- self.report({'WARNING'}, "No free inputs for node: "+t_node.name)
+ self.report({'WARNING'}, "No free inputs for node: " + node.name)
+ continue
+
+ x_offset = 0
+ padding = 40.0
+ locx = node.location.x
+ locy = node.location.y - (input_index * padding)
+
+ is_texture_node = node.rna_type.identifier in texture_types
+ use_environment_texture = node.type == 'BACKGROUND'
+
+ # Add an image texture before normal shader nodes.
+ if not is_texture_node:
+ image_texture_type = 'ShaderNodeTexEnvironment' if use_environment_texture else 'ShaderNodeTexImage'
+ image_texture_node = nodes.new(image_texture_type)
+ x_offset = x_offset + image_texture_node.width + padding
+ image_texture_node.location = [locx - x_offset, locy]
+ nodes.active = image_texture_node
+ links.new(image_texture_node.outputs[0], target_input)
+
+ # The mapping setup following this will connect to the firrst input of this image texture.
+ target_input = image_texture_node.inputs[0]
+
+ node.select = False
+
+ if is_texture_node or self.add_mapping:
+ # Add Mapping node.
+ mapping_node = nodes.new('ShaderNodeMapping')
+ x_offset = x_offset + mapping_node.width + padding
+ mapping_node.location = [locx - x_offset, locy]
+ links.new(mapping_node.outputs[0], target_input)
+
+ # Add Texture Coordinates node.
+ tex_coord_node = nodes.new('ShaderNodeTexCoord')
+ x_offset = x_offset + tex_coord_node.width + padding
+ tex_coord_node.location = [locx - x_offset, locy]
+
+ is_procedural_texture = is_texture_node and node.type != 'TEX_IMAGE'
+ use_generated_coordinates = is_procedural_texture or use_environment_texture
+ tex_coord_output = tex_coord_node.outputs[0 if use_generated_coordinates else 2]
+ links.new(tex_coord_output, mapping_node.inputs[0])
+
return {'FINISHED'}
@@ -3759,37 +3274,32 @@ class NWLinkToOutputNode(Operator):
def execute(self, context):
nodes, links = get_nodes_links(context)
active = nodes.active
- output_node = None
output_index = None
tree_type = context.space_data.tree_type
- if tree_type == 'ShaderNodeTree':
- output_types = [x[1] for x in shaders_output_nodes_props] + ['OUTPUT']
- elif tree_type == 'CompositorNodeTree':
- output_types = ['COMPOSITE']
- elif tree_type == 'TextureNodeTree':
- output_types = ['OUTPUT']
- elif tree_type == 'GeometryNodeTree':
- output_types = ['GROUP_OUTPUT']
+ shader_outputs = {'OBJECT': 'ShaderNodeOutputMaterial',
+ 'WORLD': 'ShaderNodeOutputWorld',
+ 'LINESTYLE': 'ShaderNodeOutputLineStyle'}
+ output_type = {
+ 'ShaderNodeTree': shader_outputs[context.space_data.shader_type],
+ 'CompositorNodeTree': 'CompositorNodeComposite',
+ 'TextureNodeTree': 'TextureNodeOutput',
+ 'GeometryNodeTree': 'NodeGroupOutput',
+ }[tree_type]
for node in nodes:
- if node.type in output_types:
+ # check whether the node is an output node and,
+ # if supported, whether it's the active one
+ if node.rna_type.identifier == output_type \
+ and (node.is_active_output if hasattr(node, 'is_active_output')
+ else True):
output_node = node
break
- if not output_node:
+ else: # No output node exists
bpy.ops.node.select_all(action="DESELECT")
- if tree_type == 'ShaderNodeTree':
- if context.space_data.shader_type == 'OBJECT':
- output_node = nodes.new('ShaderNodeOutputMaterial')
- elif context.space_data.shader_type == 'WORLD':
- output_node = nodes.new('ShaderNodeOutputWorld')
- elif tree_type == 'CompositorNodeTree':
- output_node = nodes.new('CompositorNodeComposite')
- elif tree_type == 'TextureNodeTree':
- output_node = nodes.new('TextureNodeOutput')
- elif tree_type == 'GeometryNodeTree':
- output_node = nodes.new('NodeGroupOutput')
+ output_node = nodes.new(output_type)
output_node.location.x = active.location.x + active.dimensions.x + 80
output_node.location.y = active.location.y
- if (output_node and active.outputs):
+
+ if active.outputs:
for i, output in enumerate(active.outputs):
if is_visible_socket(output):
output_index = i
@@ -3803,7 +3313,7 @@ class NWLinkToOutputNode(Operator):
if tree_type == 'ShaderNodeTree':
if active.outputs[output_index].name == 'Volume':
out_input_index = 1
- elif active.outputs[output_index].type != 'SHADER': # connect to displacement if not a shader
+ elif active.outputs[output_index].name == 'Displacement':
out_input_index = 2
elif tree_type == 'GeometryNodeTree':
if active.outputs[output_index].type != 'GEOMETRY':
@@ -4417,12 +3927,10 @@ class NWConnectionListOutputs(Menu, NWBase):
nodes, links = get_nodes_links(context)
n1 = nodes[context.scene.NWLazySource]
- index=0
- for o in n1.outputs:
+ for index, output in enumerate(n1.outputs):
# Only show sockets that are exposed.
- if o.enabled:
- layout.operator(NWCallInputsMenu.bl_idname, text=o.name, icon="RADIOBUT_OFF").from_socket=index
- index+=1
+ if output.enabled:
+ layout.operator(NWCallInputsMenu.bl_idname, text=output.name, icon="RADIOBUT_OFF").from_socket=index
class NWConnectionListInputs(Menu, NWBase):
@@ -4435,17 +3943,15 @@ class NWConnectionListInputs(Menu, NWBase):
n2 = nodes[context.scene.NWLazyTarget]
- index = 0
- for i in n2.inputs:
+ for index, input in enumerate(n2.inputs):
# Only show sockets that are exposed.
# This prevents, for example, the scale value socket
# of the vector math node being added to the list when
# the mode is not 'SCALE'.
- if i.enabled:
- op = layout.operator(NWMakeLink.bl_idname, text=i.name, icon="FORWARD")
+ if input.enabled:
+ op = layout.operator(NWMakeLink.bl_idname, text=input.name, icon="FORWARD")
op.from_socket = context.scene.NWSourceSocket
op.to_socket = index
- index+=1
class NWMergeMathMenu(Menu, NWBase):
@@ -4628,391 +4134,17 @@ class NWSwitchNodeTypeMenu(Menu, NWBase):
def draw(self, context):
layout = self.layout
- tree = context.space_data.node_tree
- if tree.type == 'SHADER':
- layout.menu(NWSwitchShadersInputSubmenu.bl_idname)
- layout.menu(NWSwitchShadersOutputSubmenu.bl_idname)
- layout.menu(NWSwitchShadersShaderSubmenu.bl_idname)
- layout.menu(NWSwitchShadersTextureSubmenu.bl_idname)
- layout.menu(NWSwitchShadersColorSubmenu.bl_idname)
- layout.menu(NWSwitchShadersVectorSubmenu.bl_idname)
- layout.menu(NWSwitchShadersConverterSubmenu.bl_idname)
- layout.menu(NWSwitchShadersLayoutSubmenu.bl_idname)
- if tree.type == 'COMPOSITING':
- layout.menu(NWSwitchCompoInputSubmenu.bl_idname)
- layout.menu(NWSwitchCompoOutputSubmenu.bl_idname)
- layout.menu(NWSwitchCompoColorSubmenu.bl_idname)
- layout.menu(NWSwitchCompoConverterSubmenu.bl_idname)
- layout.menu(NWSwitchCompoFilterSubmenu.bl_idname)
- layout.menu(NWSwitchCompoVectorSubmenu.bl_idname)
- layout.menu(NWSwitchCompoMatteSubmenu.bl_idname)
- layout.menu(NWSwitchCompoDistortSubmenu.bl_idname)
- layout.menu(NWSwitchCompoLayoutSubmenu.bl_idname)
- if tree.type == 'TEXTURE':
- layout.menu(NWSwitchTexInputSubmenu.bl_idname)
- layout.menu(NWSwitchTexOutputSubmenu.bl_idname)
- layout.menu(NWSwitchTexColorSubmenu.bl_idname)
- layout.menu(NWSwitchTexPatternSubmenu.bl_idname)
- layout.menu(NWSwitchTexTexturesSubmenu.bl_idname)
- layout.menu(NWSwitchTexConverterSubmenu.bl_idname)
- layout.menu(NWSwitchTexDistortSubmenu.bl_idname)
- layout.menu(NWSwitchTexLayoutSubmenu.bl_idname)
- if tree.type == 'GEOMETRY':
- categories = [c for c in node_categories_iter(context)
+ categories = [c for c in node_categories_iter(context)
if c.name not in ['Group', 'Script']]
- for cat in categories:
- idname = f"NODE_MT_nw_switch_{cat.identifier}_submenu"
- if hasattr(bpy.types, idname):
- layout.menu(idname)
- else:
- layout.label(text="Unable to load altered node lists.")
- layout.label(text="Please re-enable Node Wrangler.")
- break
-
-
-class NWSwitchShadersInputSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_shaders_input_submenu"
- bl_label = "Input"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in shaders_input_nodes_props:
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchShadersOutputSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_shaders_output_submenu"
- bl_label = "Output"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in shaders_output_nodes_props:
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchShadersShaderSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_shaders_shader_submenu"
- bl_label = "Shader"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in shaders_shader_nodes_props:
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchShadersTextureSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_shaders_texture_submenu"
- bl_label = "Texture"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in shaders_texture_nodes_props:
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchShadersColorSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_shaders_color_submenu"
- bl_label = "Color"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in shaders_color_nodes_props:
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchShadersVectorSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_shaders_vector_submenu"
- bl_label = "Vector"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in shaders_vector_nodes_props:
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchShadersConverterSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_shaders_converter_submenu"
- bl_label = "Converter"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in shaders_converter_nodes_props:
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchShadersLayoutSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_shaders_layout_submenu"
- bl_label = "Layout"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in shaders_layout_nodes_props:
- if node_type != 'FRAME':
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchCompoInputSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_compo_input_submenu"
- bl_label = "Input"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in compo_input_nodes_props:
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchCompoOutputSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_compo_output_submenu"
- bl_label = "Output"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in compo_output_nodes_props:
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchCompoColorSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_compo_color_submenu"
- bl_label = "Color"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in compo_color_nodes_props:
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchCompoConverterSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_compo_converter_submenu"
- bl_label = "Converter"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in compo_converter_nodes_props:
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchCompoFilterSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_compo_filter_submenu"
- bl_label = "Filter"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in compo_filter_nodes_props:
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchCompoVectorSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_compo_vector_submenu"
- bl_label = "Vector"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in compo_vector_nodes_props:
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchCompoMatteSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_compo_matte_submenu"
- bl_label = "Matte"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in compo_matte_nodes_props:
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchCompoDistortSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_compo_distort_submenu"
- bl_label = "Distort"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in compo_distort_nodes_props:
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchCompoLayoutSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_compo_layout_submenu"
- bl_label = "Layout"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in compo_layout_nodes_props:
- if node_type != 'FRAME':
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchMatInputSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_mat_input_submenu"
- bl_label = "Input"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in sorted(blender_mat_input_nodes_props, key=lambda k: k[2]):
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchMatOutputSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_mat_output_submenu"
- bl_label = "Output"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in sorted(blender_mat_output_nodes_props, key=lambda k: k[2]):
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchMatColorSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_mat_color_submenu"
- bl_label = "Color"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in sorted(blender_mat_color_nodes_props, key=lambda k: k[2]):
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchMatVectorSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_mat_vector_submenu"
- bl_label = "Vector"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in sorted(blender_mat_vector_nodes_props, key=lambda k: k[2]):
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchMatConverterSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_mat_converter_submenu"
- bl_label = "Converter"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in sorted(blender_mat_converter_nodes_props, key=lambda k: k[2]):
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchMatLayoutSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_mat_layout_submenu"
- bl_label = "Layout"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in sorted(blender_mat_layout_nodes_props, key=lambda k: k[2]):
- if node_type != 'FRAME':
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchTexInputSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_tex_input_submenu"
- bl_label = "Input"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in sorted(texture_input_nodes_props, key=lambda k: k[2]):
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchTexOutputSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_tex_output_submenu"
- bl_label = "Output"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in sorted(texture_output_nodes_props, key=lambda k: k[2]):
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchTexColorSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_tex_color_submenu"
- bl_label = "Color"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in sorted(texture_color_nodes_props, key=lambda k: k[2]):
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchTexPatternSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_tex_pattern_submenu"
- bl_label = "Pattern"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in sorted(texture_pattern_nodes_props, key=lambda k: k[2]):
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchTexTexturesSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_tex_textures_submenu"
- bl_label = "Textures"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in sorted(texture_textures_nodes_props, key=lambda k: k[2]):
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchTexConverterSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_tex_converter_submenu"
- bl_label = "Converter"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in sorted(texture_converter_nodes_props, key=lambda k: k[2]):
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchTexDistortSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_tex_distort_submenu"
- bl_label = "Distort"
-
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in sorted(texture_distort_nodes_props, key=lambda k: k[2]):
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
-
-
-class NWSwitchTexLayoutSubmenu(Menu, NWBase):
- bl_idname = "NODE_MT_nw_switch_tex_layout_submenu"
- bl_label = "Layout"
+ for cat in categories:
+ idname = f"NODE_MT_nw_switch_{cat.identifier}_submenu"
+ if hasattr(bpy.types, idname):
+ layout.menu(idname)
+ else:
+ layout.label(text="Unable to load altered node lists.")
+ layout.label(text="Please re-enable Node Wrangler.")
+ break
- def draw(self, context):
- layout = self.layout
- for ident, node_type, rna_name in sorted(texture_layout_nodes_props, key=lambda k: k[2]):
- if node_type != 'FRAME':
- props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name)
- props.to_type = ident
def draw_switch_category_submenu(self, context):
layout = self.layout
@@ -5027,7 +4159,7 @@ def draw_switch_category_submenu(self, context):
node.draw(self, layout, context)
continue
props = layout.operator(NWSwitchNodeType.bl_idname, text=node.label)
- props.geo_to_type = node.nodetype
+ props.to_type = node.nodetype
#
# APPENDAGES TO EXISTING UI
@@ -5071,12 +4203,12 @@ def reset_nodes_button(self, context):
node_ignore = ["FRAME","REROUTE", "GROUP"]
# Check if active node is in the selection and respective type
- if (len(node_selected) == 1) and node_active.select and node_active.type not in node_ignore:
+ if (len(node_selected) == 1) and node_active and node_active.select and node_active.type not in node_ignore:
row = self.layout.row()
row.operator("node.nw_reset_nodes", text="Reset Node", icon="FILE_REFRESH")
self.layout.separator()
- elif (len(node_selected) == 1) and node_active.select and node_active.type == "FRAME":
+ elif (len(node_selected) == 1) and node_active and node_active.select and node_active.type == "FRAME":
row = self.layout.row()
row.operator("node.nw_reset_nodes", text="Reset Nodes in Frame", icon="FILE_REFRESH")
self.layout.separator()
@@ -5245,8 +4377,8 @@ kmi_defs = (
(NWDeleteUnused.bl_idname, 'X', 'PRESS', False, False, True, None, "Delete unused nodes"),
# Frame Selected
(NWFrameSelected.bl_idname, 'P', 'PRESS', False, True, False, None, "Frame selected nodes"),
- # Swap Outputs
- (NWSwapLinks.bl_idname, 'S', 'PRESS', False, False, True, None, "Swap Outputs"),
+ # Swap Links
+ (NWSwapLinks.bl_idname, 'S', 'PRESS', False, False, True, None, "Swap Links"),
# Preview Node
(NWPreviewNode.bl_idname, 'LEFTMOUSE', 'PRESS', True, True, False, (('run_in_geometry_nodes', False),), "Preview node output"),
(NWPreviewNode.bl_idname, 'LEFTMOUSE', 'PRESS', False, True, True, (('run_in_geometry_nodes', True),), "Preview node output"),
@@ -5330,37 +4462,6 @@ classes = (
NWLinkUseOutputsNamesMenu,
NWAttributeMenu,
NWSwitchNodeTypeMenu,
- NWSwitchShadersInputSubmenu,
- NWSwitchShadersOutputSubmenu,
- NWSwitchShadersShaderSubmenu,
- NWSwitchShadersTextureSubmenu,
- NWSwitchShadersColorSubmenu,
- NWSwitchShadersVectorSubmenu,
- NWSwitchShadersConverterSubmenu,
- NWSwitchShadersLayoutSubmenu,
- NWSwitchCompoInputSubmenu,
- NWSwitchCompoOutputSubmenu,
- NWSwitchCompoColorSubmenu,
- NWSwitchCompoConverterSubmenu,
- NWSwitchCompoFilterSubmenu,
- NWSwitchCompoVectorSubmenu,
- NWSwitchCompoMatteSubmenu,
- NWSwitchCompoDistortSubmenu,
- NWSwitchCompoLayoutSubmenu,
- NWSwitchMatInputSubmenu,
- NWSwitchMatOutputSubmenu,
- NWSwitchMatColorSubmenu,
- NWSwitchMatVectorSubmenu,
- NWSwitchMatConverterSubmenu,
- NWSwitchMatLayoutSubmenu,
- NWSwitchTexInputSubmenu,
- NWSwitchTexOutputSubmenu,
- NWSwitchTexColorSubmenu,
- NWSwitchTexPatternSubmenu,
- NWSwitchTexTexturesSubmenu,
- NWSwitchTexConverterSubmenu,
- NWSwitchTexDistortSubmenu,
- NWSwitchTexLayoutSubmenu,
)
def register():
@@ -5417,7 +4518,7 @@ def register():
# switch submenus
switch_category_menus.clear()
for cat in node_categories_iter(None):
- if cat.name not in ['Group', 'Script'] and cat.identifier.startswith('GEO'):
+ if cat.name not in ['Group', 'Script']:
idname = f"NODE_MT_nw_switch_{cat.identifier}_submenu"
switch_category_type = type(idname, (bpy.types.Menu,), {
"bl_space_type": 'NODE_EDITOR',
diff --git a/object_boolean_tools.py b/object_boolean_tools.py
index f95f19cc..149433ca 100644
--- a/object_boolean_tools.py
+++ b/object_boolean_tools.py
@@ -1090,7 +1090,7 @@ class PREFS_BoolTool_Props(AddonPreferences):
"for a custom version that can optimize the visualization of Brushes",
)
use_wire: BoolProperty(
- name="Display As Wirewrame",
+ name="Display As Wireframe",
description="Display brush as wireframe instead of bounding box",
)
category: StringProperty(
diff --git a/object_collection_manager/__init__.py b/object_collection_manager/__init__.py
index fd53b291..7467fcbd 100644
--- a/object_collection_manager/__init__.py
+++ b/object_collection_manager/__init__.py
@@ -6,7 +6,7 @@ bl_info = {
"name": "Collection Manager",
"description": "Manage collections and their objects",
"author": "Ryan Inch",
- "version": (2, 24, 1),
+ "version": (2, 24, 4),
"blender": (2, 80, 0),
"location": "View3D - Object Mode (Shortcut - M)",
"warning": '', # used for warning icon and text in addons panel
diff --git a/object_collection_manager/cm_init.py b/object_collection_manager/cm_init.py
index 38080cb8..679d8d3e 100644
--- a/object_collection_manager/cm_init.py
+++ b/object_collection_manager/cm_init.py
@@ -144,6 +144,10 @@ def disable_objects_menu_addition(self, context):
def register_disable_objects_hotkeys():
+ if addon_disable_objects_hotkey_keymaps:
+ # guard to handle default value updates (mouse hover + backspace)
+ return
+
wm = bpy.context.window_manager
if wm.keyconfigs.addon: # not present when started with --background
km = wm.keyconfigs.addon.keymaps.new(name='Object Mode')
diff --git a/object_collection_manager/operator_utils.py b/object_collection_manager/operator_utils.py
index 51b4385d..4394cf3a 100644
--- a/object_collection_manager/operator_utils.py
+++ b/object_collection_manager/operator_utils.py
@@ -604,10 +604,10 @@ def select_collection_objects(is_master_collection, collection_name, replace, ne
if replace:
bpy.ops.object.select_all(action='DESELECT')
- def select_objects(collection, selection_state):
- if selection_state == None:
- selection_state = get_move_selection().isdisjoint(collection.objects)
+ if selection_state == None:
+ selection_state = get_move_selection().isdisjoint(target_collection.objects)
+ def select_objects(collection, selection_state):
for obj in collection.objects:
try:
obj.select_set(selection_state)
diff --git a/object_collection_manager/qcd_init.py b/object_collection_manager/qcd_init.py
index 1273e6e0..6d4ac7e7 100644
--- a/object_collection_manager/qcd_init.py
+++ b/object_collection_manager/qcd_init.py
@@ -27,6 +27,7 @@ from bpy.app.handlers import persistent
addon_qcd_keymaps = []
addon_qcd_view_hotkey_keymaps = []
addon_qcd_view_edit_mode_hotkey_keymaps = []
+qcd_registered = False
qcd_classes = (
@@ -76,6 +77,11 @@ def load_pre_handler(dummy):
def register_qcd():
+ global qcd_registered
+ if qcd_registered:
+ # guard to handle default value updates (mouse hover + backspace)
+ return
+
for cls in qcd_classes:
bpy.utils.register_class(cls)
@@ -107,8 +113,14 @@ def register_qcd():
if prefs.enable_qcd_3dview_header_widget:
register_qcd_3dview_header_widget()
+ qcd_registered = True
+
def register_qcd_view_hotkeys():
+ if addon_qcd_view_hotkey_keymaps:
+ # guard to handle default value updates (mouse hover + backspace)
+ return
+
wm = bpy.context.window_manager
if wm.keyconfigs.addon: # not present when started with --background
# create qcd hotkeys
@@ -135,8 +147,8 @@ def register_qcd_view_hotkeys():
["ZERO", True, "20"],
]
- for key in qcd_hotkeys:
- for mode in ['Object Mode', 'Pose', 'Weight Paint']:
+ for mode in ['Object Mode', 'Pose', 'Weight Paint']:
+ for key in qcd_hotkeys:
km = wm.keyconfigs.addon.keymaps.new(name=mode)
kmi = km.keymap_items.new('view3d.view_qcd_slot', key[0], 'PRESS', alt=key[1])
kmi.properties.slot = key[2]
@@ -149,37 +161,41 @@ def register_qcd_view_hotkeys():
kmi.properties.toggle = True
addon_qcd_view_hotkey_keymaps.append((km, kmi))
- km = wm.keyconfigs.addon.keymaps.new(name=mode)
- kmi = km.keymap_items.new('view3d.enable_all_qcd_slots', 'PLUS', 'PRESS', shift=True)
- addon_qcd_view_hotkey_keymaps.append((km, kmi))
+ km = wm.keyconfigs.addon.keymaps.new(name=mode)
+ kmi = km.keymap_items.new('view3d.enable_all_qcd_slots', 'PLUS', 'PRESS', shift=True)
+ addon_qcd_view_hotkey_keymaps.append((km, kmi))
- km = wm.keyconfigs.addon.keymaps.new(name=mode)
- kmi = km.keymap_items.new('view3d.isolate_selected_objects_collections', 'EQUAL', 'PRESS')
- addon_qcd_view_hotkey_keymaps.append((km, kmi))
+ km = wm.keyconfigs.addon.keymaps.new(name=mode)
+ kmi = km.keymap_items.new('view3d.isolate_selected_objects_collections', 'EQUAL', 'PRESS')
+ addon_qcd_view_hotkey_keymaps.append((km, kmi))
- km = wm.keyconfigs.addon.keymaps.new(name=mode)
- kmi = km.keymap_items.new('view3d.disable_selected_objects_collections', 'MINUS', 'PRESS')
- addon_qcd_view_hotkey_keymaps.append((km, kmi))
+ km = wm.keyconfigs.addon.keymaps.new(name=mode)
+ kmi = km.keymap_items.new('view3d.disable_selected_objects_collections', 'MINUS', 'PRESS')
+ addon_qcd_view_hotkey_keymaps.append((km, kmi))
- km = wm.keyconfigs.addon.keymaps.new(name=mode)
- kmi = km.keymap_items.new('view3d.disable_all_non_qcd_slots', 'PLUS', 'PRESS', shift=True, alt=True)
- addon_qcd_view_hotkey_keymaps.append((km, kmi))
+ km = wm.keyconfigs.addon.keymaps.new(name=mode)
+ kmi = km.keymap_items.new('view3d.disable_all_non_qcd_slots', 'PLUS', 'PRESS', shift=True, alt=True)
+ addon_qcd_view_hotkey_keymaps.append((km, kmi))
- km = wm.keyconfigs.addon.keymaps.new(name=mode)
- kmi = km.keymap_items.new('view3d.disable_all_collections', 'EQUAL', 'PRESS', alt=True, ctrl=True)
- addon_qcd_view_hotkey_keymaps.append((km, kmi))
+ km = wm.keyconfigs.addon.keymaps.new(name=mode)
+ kmi = km.keymap_items.new('view3d.disable_all_collections', 'EQUAL', 'PRESS', alt=True, ctrl=True)
+ addon_qcd_view_hotkey_keymaps.append((km, kmi))
- km = wm.keyconfigs.addon.keymaps.new(name=mode)
- kmi = km.keymap_items.new('view3d.select_all_qcd_objects', 'PLUS', 'PRESS', shift=True, ctrl=True)
- addon_qcd_view_hotkey_keymaps.append((km, kmi))
+ km = wm.keyconfigs.addon.keymaps.new(name=mode)
+ kmi = km.keymap_items.new('view3d.select_all_qcd_objects', 'PLUS', 'PRESS', shift=True, ctrl=True)
+ addon_qcd_view_hotkey_keymaps.append((km, kmi))
- km = wm.keyconfigs.addon.keymaps.new(name=mode)
- kmi = km.keymap_items.new('view3d.discard_qcd_history', 'EQUAL', 'PRESS', alt=True)
- addon_qcd_view_hotkey_keymaps.append((km, kmi))
+ km = wm.keyconfigs.addon.keymaps.new(name=mode)
+ kmi = km.keymap_items.new('view3d.discard_qcd_history', 'EQUAL', 'PRESS', alt=True)
+ addon_qcd_view_hotkey_keymaps.append((km, kmi))
def register_qcd_view_edit_mode_hotkeys():
+ if addon_qcd_view_edit_mode_hotkey_keymaps:
+ # guard to handle default value updates (mouse hover + backspace)
+ return
+
wm = bpy.context.window_manager
if wm.keyconfigs.addon: # not present when started with --background
# create qcd hotkeys
@@ -248,12 +264,22 @@ def register_qcd_view_edit_mode_hotkeys():
def register_qcd_3dview_header_widget():
+ # unregister first to guard against default value updates (mouse hover + backspace)
+ # if the widget isn't registered it will just do nothing
+ unregister_qcd_3dview_header_widget()
+
+ # add the widget to the header, and an update function to the top bar to get view layer changes
bpy.types.VIEW3D_HT_header.append(ui.view3d_header_qcd_slots)
bpy.types.TOPBAR_HT_upper_bar.append(ui.view_layer_update)
def unregister_qcd():
+ global qcd_registered
+ if not qcd_registered:
+ # guard to handle default value updates (mouse hover + backspace)
+ return
+
unregister_qcd_3dview_header_widget()
for cls in qcd_classes:
@@ -279,6 +305,8 @@ def unregister_qcd():
unregister_qcd_view_edit_mode_hotkeys()
+ qcd_registered = False
+
def unregister_qcd_view_hotkeys():
# remove keymaps when qcd view hotkeys are deactivated
diff --git a/object_fracture_cell/__init__.py b/object_fracture_cell/__init__.py
index e9f70a44..543f86f1 100644
--- a/object_fracture_cell/__init__.py
+++ b/object_fracture_cell/__init__.py
@@ -482,7 +482,7 @@ class FractureCell(Operator):
rowsub.prop(self, "use_data_match")
rowsub = col.row()
- # on same row for even layout but infact are not all that related
+ # on same row for even layout but in fact are not all that related
rowsub.prop(self, "material_index")
rowsub.prop(self, "use_interior_vgroup")
diff --git a/object_fracture_cell/fracture_cell_setup.py b/object_fracture_cell/fracture_cell_setup.py
index 8171e44e..2e735495 100644
--- a/object_fracture_cell/fracture_cell_setup.py
+++ b/object_fracture_cell/fracture_cell_setup.py
@@ -145,7 +145,7 @@ def cell_fracture_objects(
random.shuffle(points)
points[source_limit:] = []
- # saddly we cant be sure there are no doubles
+ # sadly we can't be sure there are no doubles
from mathutils import Vector
to_tuple = Vector.to_tuple
points = list({to_tuple(p, 4): p for p in points}.values())
diff --git a/object_print3d_utils/export.py b/object_print3d_utils/export.py
index aec19732..11ce5e00 100644
--- a/object_print3d_utils/export.py
+++ b/object_print3d_utils/export.py
@@ -5,6 +5,8 @@
import bpy
+from bpy.app.translations import pgettext_tip as tip_
+
def image_get(mat):
from bpy_extras import node_shader_utils
@@ -79,7 +81,7 @@ def write_mesh(context, report_cb):
# first ensure the path is created
if export_path:
# this can fail with strange errors,
- # if the dir cant be made then we get an error later.
+ # if the dir can't be made then we get an error later.
try:
os.makedirs(export_path, exist_ok=True)
except:
@@ -132,17 +134,17 @@ def write_mesh(context, report_cb):
use_normals=export_data_layers,
)
elif export_format == 'OBJ':
- addon_ensure("io_scene_obj")
filepath = bpy.path.ensure_ext(filepath, ".obj")
- ret = bpy.ops.export_scene.obj(
+ ret = bpy.ops.wm.obj_export(
filepath=filepath,
- use_mesh_modifiers=True,
- use_selection=True,
- global_scale=global_scale,
+ apply_modifiers=True,
+ export_selected_objects=True,
+ scaling_factor=global_scale,
path_mode=path_mode,
- use_normals=export_data_layers,
- use_uvs=export_data_layers,
- use_materials=export_data_layers,
+ export_normals=export_data_layers,
+ export_uv=export_data_layers,
+ export_materials=export_data_layers,
+ export_colors=export_data_layers,
)
else:
assert 0
@@ -153,7 +155,7 @@ def write_mesh(context, report_cb):
if 'FINISHED' in ret:
if report_cb is not None:
- report_cb({'INFO'}, f"Exported: {filepath!r}")
+ report_cb({'INFO'}, tip_("Exported: {!r}").format(filepath))
return True
diff --git a/object_print3d_utils/mesh_helpers.py b/object_print3d_utils/mesh_helpers.py
index 7d23a078..444df1f1 100644
--- a/object_print3d_utils/mesh_helpers.py
+++ b/object_print3d_utils/mesh_helpers.py
@@ -32,7 +32,13 @@ def bmesh_copy_from_object(obj, transform=True, triangulate=True, apply_modifier
# would save ram
if transform:
- bm.transform(obj.matrix_world)
+ matrix = obj.matrix_world.copy()
+ if not matrix.is_identity:
+ bm.transform(matrix)
+ # Update normals if the matrix has no rotation.
+ matrix.translation.zero()
+ if not matrix.is_identity:
+ bm.normal_update()
if triangulate:
bmesh.ops.triangulate(bm, faces=bm.faces)
diff --git a/object_print3d_utils/operators.py b/object_print3d_utils/operators.py
index 85f268e7..d47aa84a 100644
--- a/object_print3d_utils/operators.py
+++ b/object_print3d_utils/operators.py
@@ -13,6 +13,8 @@ from bpy.props import (
)
import bmesh
+from bpy.app.translations import pgettext_tip as tip_
+
from . import report
@@ -87,7 +89,7 @@ class MESH_OT_print3d_info_volume(Operator):
volume_str = clean_float(volume_unit, 4)
volume_fmt = f"{volume_str} {symbol}"
- report.update((f"Volume: {volume_fmt}³", None))
+ report.update((tip_("Volume: {}³").format(volume_fmt), None))
return {'FINISHED'}
@@ -118,7 +120,7 @@ class MESH_OT_print3d_info_area(Operator):
area_str = clean_float(area_unit, 4)
area_fmt = f"{area_str} {symbol}"
- report.update((f"Area: {area_fmt}²", None))
+ report.update((tip_("Area: {}²").format(area_fmt), None))
return {'FINISHED'}
@@ -161,8 +163,12 @@ class MESH_OT_print3d_check_solid(Operator):
(i for i, ele in enumerate(bm.edges) if ele.is_manifold and (not ele.is_contiguous)),
)
- info.append((f"Non Manifold Edge: {len(edges_non_manifold)}", (bmesh.types.BMEdge, edges_non_manifold)))
- info.append((f"Bad Contig. Edges: {len(edges_non_contig)}", (bmesh.types.BMEdge, edges_non_contig)))
+ info.append(
+ (tip_("Non Manifold Edge: {}").format(
+ len(edges_non_manifold)),
+ (bmesh.types.BMEdge,
+ edges_non_manifold)))
+ info.append((tip_("Bad Contig. Edges: {}").format(len(edges_non_contig)), (bmesh.types.BMEdge, edges_non_contig)))
bm.free()
@@ -180,7 +186,7 @@ class MESH_OT_print3d_check_intersections(Operator):
from . import mesh_helpers
faces_intersect = mesh_helpers.bmesh_check_self_intersect_object(obj)
- info.append((f"Intersect Face: {len(faces_intersect)}", (bmesh.types.BMFace, faces_intersect)))
+ info.append((tip_("Intersect Face: {}").format(len(faces_intersect)), (bmesh.types.BMFace, faces_intersect)))
def execute(self, context):
return execute_check(self, context)
@@ -208,8 +214,8 @@ class MESH_OT_print3d_check_degenerate(Operator):
faces_zero = array.array('i', (i for i, ele in enumerate(bm.faces) if ele.calc_area() <= threshold))
edges_zero = array.array('i', (i for i, ele in enumerate(bm.edges) if ele.calc_length() <= threshold))
- info.append((f"Zero Faces: {len(faces_zero)}", (bmesh.types.BMFace, faces_zero)))
- info.append((f"Zero Edges: {len(edges_zero)}", (bmesh.types.BMEdge, edges_zero)))
+ info.append((tip_("Zero Faces: {}").format(len(faces_zero)), (bmesh.types.BMFace, faces_zero)))
+ info.append((tip_("Zero Edges: {}").format(len(edges_zero)), (bmesh.types.BMEdge, edges_zero)))
bm.free()
@@ -239,7 +245,7 @@ class MESH_OT_print3d_check_distorted(Operator):
(i for i, ele in enumerate(bm.faces) if mesh_helpers.face_is_distorted(ele, angle_distort))
)
- info.append((f"Non-Flat Faces: {len(faces_distort)}", (bmesh.types.BMFace, faces_distort)))
+ info.append((tip_("Non-Flat Faces: {}").format(len(faces_distort)), (bmesh.types.BMFace, faces_distort)))
bm.free()
@@ -263,7 +269,7 @@ class MESH_OT_print3d_check_thick(Operator):
print_3d = scene.print_3d
faces_error = mesh_helpers.bmesh_check_thick_object(obj, print_3d.thickness_min)
- info.append((f"Thin Faces: {len(faces_error)}", (bmesh.types.BMFace, faces_error)))
+ info.append((tip_("Thin Faces: {}").format(len(faces_error)), (bmesh.types.BMFace, faces_error)))
def execute(self, context):
return execute_check(self, context)
@@ -290,7 +296,7 @@ class MESH_OT_print3d_check_sharp(Operator):
if ele.is_manifold and ele.calc_face_angle_signed() > angle_sharp
]
- info.append((f"Sharp Edge: {len(edges_sharp)}", (bmesh.types.BMEdge, edges_sharp)))
+ info.append((tip_("Sharp Edge: {}").format(len(edges_sharp)), (bmesh.types.BMEdge, edges_sharp)))
bm.free()
def execute(self, context):
@@ -327,7 +333,7 @@ class MESH_OT_print3d_check_overhang(Operator):
if z_down_angle(ele.normal, 4.0) < angle_overhang
]
- info.append((f"Overhang Face: {len(faces_overhang)}", (bmesh.types.BMFace, faces_overhang)))
+ info.append((tip_("Overhang Face: {}").format(len(faces_overhang)), (bmesh.types.BMFace, faces_overhang)))
bm.free()
def execute(self, context):
@@ -390,7 +396,7 @@ class MESH_OT_print3d_clean_distorted(Operator):
bmesh.ops.triangulate(bm, faces=elems_triangulate)
mesh_helpers.bmesh_to_object(obj, bm)
- self.report({'INFO'}, f"Triangulated {len(elems_triangulate)} faces")
+ self.report({'INFO'}, tip_("Triangulated {} faces").format(len(elems_triangulate)))
return {'FINISHED'}
@@ -441,7 +447,7 @@ class MESH_OT_print3d_clean_non_manifold(Operator):
edges = bm_key[1] - bm_key_orig[1]
faces = bm_key[2] - bm_key_orig[2]
- self.report({'INFO'}, f"Modified: {verts:+} vertices, {edges:+} edges, {faces:+} faces")
+ self.report({'INFO'}, tip_("Modified: {:+} vertices, {:+} edges, {:+} faces").format(verts, edges, faces))
return {'FINISHED'}
@@ -616,7 +622,7 @@ def _scale(scale, report=None, report_suffix=""):
bpy.ops.transform.resize(value=(scale,) * 3)
if report is not None:
scale_fmt = clean_float(scale, 6)
- report({'INFO'}, f"Scaled by {scale_fmt}{report_suffix}")
+ report({'INFO'}, tip_("Scaled by {}{}").format(scale_fmt, report_suffix))
class MESH_OT_print3d_scale_to_volume(Operator):
@@ -638,7 +644,7 @@ class MESH_OT_print3d_scale_to_volume(Operator):
def execute(self, context):
scale = math.pow(self.volume, 1 / 3) / math.pow(self.volume_init, 1 / 3)
scale_fmt = clean_float(scale, 6)
- self.report({'INFO'}, f"Scaled by {scale_fmt}")
+ self.report({'INFO'}, tip_("Scaled by {}").format(scale_fmt))
_scale(scale, self.report)
return {'FINISHED'}
@@ -689,7 +695,7 @@ class MESH_OT_print3d_scale_to_bounds(Operator):
def execute(self, context):
scale = self.length / self.length_init
axis = "XYZ"[self.axis_init]
- _scale(scale, report=self.report, report_suffix=f", Clamping {axis}-Axis")
+ _scale(scale, report=self.report, report_suffix=tip_(", Clamping {}-Axis").format(axis))
return {'FINISHED'}
def invoke(self, context, event):
@@ -763,7 +769,10 @@ class MESH_OT_print3d_align_to_xy(Operator):
normal = Vector((0.0, 0.0, 0.0))
if face_areas:
for face in faces:
- normal += (face.normal * face.calc_area())
+ if mode_orig == 'EDIT_MESH':
+ normal += (face.normal * face.calc_area())
+ else:
+ normal += (face.normal * face.area)
else:
for face in faces:
normal += face.normal
@@ -777,9 +786,9 @@ class MESH_OT_print3d_align_to_xy(Operator):
if len(skip_invalid) > 0:
for name in skip_invalid:
- print(f"Align to XY: Skipping object {name}. No faces selected.")
+ print(tip_("Align to XY: Skipping object {}. No faces selected.").format(name))
if len(skip_invalid) == 1:
- self.report({'WARNING'}, "Skipping object. No faces selected" % skip_invalid[0])
+ self.report({'WARNING'}, tip_("Skipping object {}. No faces selected").format(skip_invalid[0]))
else:
self.report({'WARNING'}, "Skipping some objects. No faces selected. See terminal")
return {'FINISHED'}
diff --git a/object_scatter/__init__.py b/object_scatter/__init__.py
index 12bc8ca0..8edeac71 100644
--- a/object_scatter/__init__.py
+++ b/object_scatter/__init__.py
@@ -3,8 +3,8 @@
bl_info = {
"name": "Scatter Objects",
"author": "Jacques Lucke",
- "version": (0, 1),
- "blender": (2, 80, 0),
+ "version": (0, 2),
+ "blender": (3, 0, 0),
"location": "3D View",
"description": "Distribute object instances on another object.",
"warning": "",
diff --git a/object_scatter/operator.py b/object_scatter/operator.py
index 07bf3884..8be78672 100644
--- a/object_scatter/operator.py
+++ b/object_scatter/operator.py
@@ -2,7 +2,6 @@
import bpy
import gpu
-import bgl
import blf
import math
import enum
@@ -340,14 +339,14 @@ def draw_matrices_batches(batches):
shader.bind()
shader.uniform_float("color", (0.4, 0.4, 1.0, 0.3))
- bgl.glEnable(bgl.GL_BLEND)
- bgl.glDepthMask(bgl.GL_FALSE)
+ gpu.state.blend_set('ALPHA')
+ gpu.state.depth_mask_set(False)
for batch in batches:
batch.draw(shader)
- bgl.glDisable(bgl.GL_BLEND)
- bgl.glDepthMask(bgl.GL_TRUE)
+ gpu.state.blend_set('NONE')
+ gpu.state.depth_mask_set(True)
def create_batch_for_matrices(matrices, base_scale):
coords = []
@@ -367,7 +366,7 @@ def create_batch_for_matrices(matrices, base_scale):
def draw_line_strip_batch(batch, color, thickness=1):
shader = get_uniform_color_shader()
- bgl.glLineWidth(thickness)
+ gpu.state.line_width_set(thickness)
shader.bind()
shader.uniform_float("color", color)
batch.draw(shader)
diff --git a/pose_library/__init__.py b/pose_library/__init__.py
index 33ca777f..b13d1bf0 100644
--- a/pose_library/__init__.py
+++ b/pose_library/__init__.py
@@ -10,9 +10,8 @@ bl_info = {
"author": "Sybren A. Stüvel",
"version": (2, 0),
"blender": (3, 0, 0),
- "warning": "In heavily development, things may change",
"location": "Asset Browser -> Animations, and 3D Viewport -> Animation panel",
- # "doc_url": "{BLENDER_MANUAL_URL}/addons/animation/pose_library.html",
+ "doc_url": "{BLENDER_MANUAL_URL}/animation/armatures/posing/editing/pose_library.html",
"support": "OFFICIAL",
"category": "Animation",
}
diff --git a/power_sequencer/__init__.py b/power_sequencer/__init__.py
index c646da7c..877de302 100755
--- a/power_sequencer/__init__.py
+++ b/power_sequencer/__init__.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
from typing import List, Tuple, Type
import bpy
@@ -27,8 +24,8 @@ bl_info = {
"name": "Power Sequencer",
"description": "Video editing tools for content creators",
"author": "Nathan Lovato",
- "version": (1, 5, 0),
- "blender": (2, 81, 0),
+ "version": (2, 0, 2),
+ "blender": (2, 93, 3),
"location": "Sequencer",
"tracker_url": "https://github.com/GDquest/Blender-power-sequencer/issues",
"wiki_url": "https://www.gdquest.com/docs/documentation/power-sequencer/",
diff --git a/power_sequencer/addon_preferences.py b/power_sequencer/addon_preferences.py
index 4220e89c..cfa21334 100644
--- a/power_sequencer/addon_preferences.py
+++ b/power_sequencer/addon_preferences.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
"""
Add-on preferences and interface in the Blender preferences window.
"""
diff --git a/power_sequencer/addon_properties.py b/power_sequencer/addon_properties.py
index 575a3954..3e7a4df3 100644
--- a/power_sequencer/addon_properties.py
+++ b/power_sequencer/addon_properties.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
@@ -10,8 +7,6 @@ class PowerSequencerProperties(bpy.types.PropertyGroup):
playback_speed: bpy.props.EnumProperty(
items=[
("NORMAL", "Normal (1x)", ""),
- ("FAST", "Fast (1.33x)", ""),
- ("FASTER", "Faster (1.66x)", ""),
("DOUBLE", "Double (2x)", ""),
("TRIPLE", "Triple (3x)", ""),
],
@@ -19,8 +14,6 @@ class PowerSequencerProperties(bpy.types.PropertyGroup):
default="NORMAL",
)
- frame_pre: bpy.props.IntProperty(name="Frame before frame_change", default=0, min=0)
-
active_tab: bpy.props.StringProperty(
name="Active Tab", description="The name of the active tab in the UI", default="Sequencer"
)
diff --git a/power_sequencer/handlers.py b/power_sequencer/handlers.py
index d9c107da..c743d18b 100644
--- a/power_sequencer/handlers.py
+++ b/power_sequencer/handlers.py
@@ -1,22 +1,10 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from bpy.app.handlers import persistent
@persistent
-def power_sequencer_load_file_post(arg):
- """
- Called after loading the blend file
- """
- for scene in bpy.data.scenes:
- scene.power_sequencer.frame_pre = bpy.context.scene.frame_current
-
-
-@persistent
def power_sequencer_playback_speed_post(scene):
"""
Handler function for faster playback
@@ -24,27 +12,23 @@ def power_sequencer_playback_speed_post(scene):
It steps over frame rather than increase the playback speed smoothly,
but it's still useful for faster editing
"""
+
+ # Calling this function triggers a callback to this function via the frame
+ # changed handler, causing a stack overflow. We use a property to prevent
+ # errors.
if bpy.context.screen and not bpy.context.screen.is_animation_playing:
return
playback_speed = scene.power_sequencer.playback_speed
- frame_start = scene.frame_current
- frame_post = scene.frame_current
-
- if playback_speed == "FAST" and frame_start % 3 == 0:
- frame_post += 1
- elif playback_speed == "FASTER" and frame_start % 2 == 0:
- frame_post += 1
- elif playback_speed == "DOUBLE":
- # 2.5x -> skip 5 frames for 2. 2 then 3 then 2 etc.
- frame_post += 1
+ target_frame = scene.frame_current
+ if playback_speed == "DOUBLE":
+ target_frame += 1
elif playback_speed == "TRIPLE":
- frame_post += 2
+ target_frame += 2
- if frame_start != frame_post:
- bpy.ops.screen.frame_offset(delta=frame_post - frame_start)
- scene.power_sequencer.frame_pre = scene.frame_current
+ if target_frame != scene.frame_current:
+ bpy.ops.screen.frame_offset(delta=target_frame - scene.frame_current)
def draw_playback_speed(self, context):
@@ -64,7 +48,6 @@ def register_handlers():
bpy.types.SEQUENCER_HT_header.append(draw_playback_speed)
# Handlers
- bpy.app.handlers.load_post.append(power_sequencer_load_file_post)
bpy.app.handlers.frame_change_post.append(power_sequencer_playback_speed_post)
@@ -74,5 +57,4 @@ def unregister_handlers():
bpy.types.SEQUENCER_HT_header.remove(draw_playback_speed)
# Handlers
- bpy.app.handlers.load_post.remove(power_sequencer_load_file_post)
bpy.app.handlers.frame_change_post.remove(power_sequencer_playback_speed_post)
diff --git a/power_sequencer/operators/__init__.py b/power_sequencer/operators/__init__.py
index b1222a18..57dbe2bb 100755
--- a/power_sequencer/operators/__init__.py
+++ b/power_sequencer/operators/__init__.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import importlib
import os
@@ -16,7 +13,7 @@ def get_operator_classes():
module_paths = ["." + os.path.splitext(f)[0] for f in module_files]
classes = []
for path in module_paths:
- module = importlib.import_module(path, package="power_sequencer.operators")
+ module = importlib.import_module(path, package=__package__)
operator_names = [entry for entry in dir(module) if entry.startswith("POWER_SEQUENCER_OT")]
classes.extend([getattr(module, name) for name in operator_names])
return classes
diff --git a/power_sequencer/operators/channel_offset.py b/power_sequencer/operators/channel_offset.py
index b7569afa..1e9c97f7 100644
--- a/power_sequencer/operators/channel_offset.py
+++ b/power_sequencer/operators/channel_offset.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
from operator import attrgetter
import bpy
@@ -117,12 +114,11 @@ class POWER_SEQUENCER_OT_channel_offset(bpy.types.Operator):
context, s.frame_final_start, s.frame_final_end, to_trim, to_delete
)
- if not self.keep_selection_offset:
- s.channel = comparison_function(limit_channel, s.channel + channel_offset)
- if s.channel == limit_channel:
- move_selection(context, [s], 0, 0)
+ s.channel = comparison_function(limit_channel, s.channel + channel_offset)
+ if s.channel == limit_channel:
+ move_selection(context, [s], 0, 0)
- if self.keep_selection_offset:
+ if self.keep_selection_offset and not self.trim_target_channel:
start_frame = head.frame_final_start
x_difference = 0
while not head.channel == limit_channel:
diff --git a/power_sequencer/operators/concatenate_strips.py b/power_sequencer/operators/concatenate_strips.py
index 32f5f59b..17ff4ee4 100644
--- a/power_sequencer/operators/concatenate_strips.py
+++ b/power_sequencer/operators/concatenate_strips.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from operator import attrgetter
diff --git a/power_sequencer/operators/copy_selected_sequences.py b/power_sequencer/operators/copy_selected_sequences.py
index 643ecb33..338f1cff 100644
--- a/power_sequencer/operators/copy_selected_sequences.py
+++ b/power_sequencer/operators/copy_selected_sequences.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from operator import attrgetter
diff --git a/power_sequencer/operators/crossfade_add.py b/power_sequencer/operators/crossfade_add.py
index 73644c80..25ce7ae7 100644
--- a/power_sequencer/operators/crossfade_add.py
+++ b/power_sequencer/operators/crossfade_add.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.functions import find_sequences_after
@@ -94,7 +91,7 @@ class POWER_SEQUENCER_OT_crossfade_add(bpy.types.Operator):
Moves the handles of the two sequences before adding the crossfade
"""
fade_duration = convert_duration_to_frames(context, self.crossfade_duration)
- fade_offset = fade_duration / 2
+ fade_offset = int(fade_duration / 2)
if hasattr(sequence_1, "input_1"):
sequence_1.input_1.frame_final_end -= fade_offset
diff --git a/power_sequencer/operators/crossfade_edit.py b/power_sequencer/operators/crossfade_edit.py
index 0e330e10..6d3be976 100644
--- a/power_sequencer/operators/crossfade_edit.py
+++ b/power_sequencer/operators/crossfade_edit.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.global_settings import SequenceTypes
diff --git a/power_sequencer/operators/cut_strips_under_cursor.py b/power_sequencer/operators/cut_strips_under_cursor.py
index 4a66a782..51bf0789 100644
--- a/power_sequencer/operators/cut_strips_under_cursor.py
+++ b/power_sequencer/operators/cut_strips_under_cursor.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
diff --git a/power_sequencer/operators/delete_direct.py b/power_sequencer/operators/delete_direct.py
index bda5face..1fc8bf60 100644
--- a/power_sequencer/operators/delete_direct.py
+++ b/power_sequencer/operators/delete_direct.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.functions import get_mouse_frame_and_channel
diff --git a/power_sequencer/operators/deselect_all_left_or_right.py b/power_sequencer/operators/deselect_all_left_or_right.py
index 6769d173..3ba79483 100644
--- a/power_sequencer/operators/deselect_all_left_or_right.py
+++ b/power_sequencer/operators/deselect_all_left_or_right.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
diff --git a/power_sequencer/operators/deselect_handles_and_grab.py b/power_sequencer/operators/deselect_handles_and_grab.py
index 468baf8b..36f547df 100644
--- a/power_sequencer/operators/deselect_handles_and_grab.py
+++ b/power_sequencer/operators/deselect_handles_and_grab.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
diff --git a/power_sequencer/operators/duplicate_move.py b/power_sequencer/operators/duplicate_move.py
index 32c62a56..cce7a6ed 100644
--- a/power_sequencer/operators/duplicate_move.py
+++ b/power_sequencer/operators/duplicate_move.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.functions import get_mouse_frame_and_channel
diff --git a/power_sequencer/operators/expand_to_surrounding_cuts.py b/power_sequencer/operators/expand_to_surrounding_cuts.py
index 7129e42d..4b8a6f93 100644
--- a/power_sequencer/operators/expand_to_surrounding_cuts.py
+++ b/power_sequencer/operators/expand_to_surrounding_cuts.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.functions import slice_selection
@@ -22,7 +19,11 @@ class POWER_SEQUENCER_OT_expand_to_surrounding_cuts(bpy.types.Operator):
"demo": "",
"description": doc_description(__doc__),
"shortcuts": [
- ({"type": "E", "value": "PRESS", "ctrl": True}, {}, "Expand to Surrounding Cuts",)
+ (
+ {"type": "E", "value": "PRESS", "ctrl": True},
+ {},
+ "Expand to Surrounding Cuts",
+ )
],
"keymap": "Sequencer",
}
diff --git a/power_sequencer/operators/fade_add.py b/power_sequencer/operators/fade_add.py
index 39fe18a9..9a3d1973 100644
--- a/power_sequencer/operators/fade_add.py
+++ b/power_sequencer/operators/fade_add.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from mathutils import Vector
from math import floor
@@ -40,7 +37,10 @@ class POWER_SEQUENCER_OT_fade_add(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
duration_seconds: bpy.props.FloatProperty(
- name="Fade Duration", description="Duration of the fade in seconds", default=1.0, min=0.01,
+ name="Fade Duration",
+ description="Duration of the fade in seconds",
+ default=1.0,
+ min=0.01,
)
type: bpy.props.EnumProperty(
items=[
diff --git a/power_sequencer/operators/fade_clear.py b/power_sequencer/operators/fade_clear.py
index c74b27cf..d73c7347 100644
--- a/power_sequencer/operators/fade_clear.py
+++ b/power_sequencer/operators/fade_clear.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
@@ -21,7 +18,11 @@ class POWER_SEQUENCER_OT_fade_clear(bpy.types.Operator):
"demo": "",
"description": doc_description(__doc__),
"shortcuts": [
- ({"type": "F", "value": "PRESS", "alt": True, "ctrl": True}, {}, "Clear Fades",)
+ (
+ {"type": "F", "value": "PRESS", "alt": True, "ctrl": True},
+ {},
+ "Clear Fades",
+ )
],
"keymap": "Sequencer",
}
diff --git a/power_sequencer/operators/gap_remove.py b/power_sequencer/operators/gap_remove.py
index 7f2ec5ae..64070667 100644
--- a/power_sequencer/operators/gap_remove.py
+++ b/power_sequencer/operators/gap_remove.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from operator import attrgetter
diff --git a/power_sequencer/operators/grab.py b/power_sequencer/operators/grab.py
index de53d2a4..57299d72 100644
--- a/power_sequencer/operators/grab.py
+++ b/power_sequencer/operators/grab.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.functions import get_mouse_frame_and_channel
diff --git a/power_sequencer/operators/grab_closest_handle_or_cut.py b/power_sequencer/operators/grab_closest_handle_or_cut.py
index 55440b95..7a7a056d 100644
--- a/power_sequencer/operators/grab_closest_handle_or_cut.py
+++ b/power_sequencer/operators/grab_closest_handle_or_cut.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
"""
Selects and grabs the strip handle or cut closest to the mouse cursor.
Hover near a cut and use this operator to slide it.
diff --git a/power_sequencer/operators/grab_sequence_handles.py b/power_sequencer/operators/grab_sequence_handles.py
index ecbe8c4b..d8137b0f 100644
--- a/power_sequencer/operators/grab_sequence_handles.py
+++ b/power_sequencer/operators/grab_sequence_handles.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.global_settings import SequenceTypes
@@ -34,8 +31,8 @@ class POWER_SEQUENCER_OT_grab_sequence_handles(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
always_find_closest: bpy.props.BoolProperty(name="Always find closest", default=False)
- frame: bpy.props.IntProperty(name="Frame", default=-1, options={"HIDDEN"})
- channel: bpy.props.IntProperty(name="Channel", default=-1, options={"HIDDEN"})
+ frame: bpy.props.FloatProperty(name="Frame", default=-1.0, options={"HIDDEN"})
+ channel: bpy.props.FloatProperty(name="Channel", default=-1.0, options={"HIDDEN"})
@classmethod
def poll(cls, context):
diff --git a/power_sequencer/operators/import_local_footage.py b/power_sequencer/operators/import_local_footage.py
index 8612210a..7eb7120b 100644
--- a/power_sequencer/operators/import_local_footage.py
+++ b/power_sequencer/operators/import_local_footage.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import json
import os
import re
diff --git a/power_sequencer/operators/jump_time_offset.py b/power_sequencer/operators/jump_time_offset.py
index ee9b1f9b..79941827 100644
--- a/power_sequencer/operators/jump_time_offset.py
+++ b/power_sequencer/operators/jump_time_offset.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.functions import convert_duration_to_frames
diff --git a/power_sequencer/operators/jump_to_cut.py b/power_sequencer/operators/jump_to_cut.py
index add0dab9..02a0b9f9 100644
--- a/power_sequencer/operators/jump_to_cut.py
+++ b/power_sequencer/operators/jump_to_cut.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from operator import attrgetter
@@ -45,8 +42,8 @@ class POWER_SEQUENCER_OT_jump_to_cut(bpy.types.Operator):
name="Direction",
description="Jump direction, either forward or backward",
items=[
- ("RIGHT", "Forward", "Jump forward in time"),
- ("LEFT", "Backward", "Jump backward in time"),
+ ("RIGHT", "Right", "Jump forward in time"),
+ ("LEFT", "Left", "Jump backward in time"),
],
)
@@ -56,9 +53,6 @@ class POWER_SEQUENCER_OT_jump_to_cut(bpy.types.Operator):
def execute(self, context):
frame_current = context.scene.frame_current
- sorted_sequences = sorted(
- context.sequences, key=attrgetter("frame_final_start", "frame_final_end")
- )
fcurves = []
animation_data = context.scene.animation_data
@@ -66,43 +60,53 @@ class POWER_SEQUENCER_OT_jump_to_cut(bpy.types.Operator):
fcurves = animation_data.action.fcurves
frame_target = -1
+
+ # First find the closest cut, then if that sequence has an associated
+ # fcurve, loop through the curve's keyframes.
if self.direction == "RIGHT":
- sequences = [s for s in sorted_sequences if s.frame_final_end > frame_current]
- for s in sequences:
+ frame_target = 100_000_000
+ for s in context.sequences:
+ if s.frame_final_end <= frame_current:
+ continue
+
+ candidates = [frame_target, s.frame_final_end]
+ if s.frame_final_start > frame_current:
+ candidates.append(s.frame_final_start)
- frame_target = (
- s.frame_final_end
- if s.frame_final_start <= frame_current
- else s.frame_final_start
- )
+ frame_target = min(candidates)
for f in fcurves:
+ if s.name not in f.data_path:
+ continue
+
for k in f.keyframe_points:
frame = k.co[0]
- if frame <= context.scene.frame_current:
+ if frame <= frame_current:
continue
frame_target = min(frame_target, frame)
- break
elif self.direction == "LEFT":
- sequences = [
- s for s in reversed(sorted_sequences) if s.frame_final_start < frame_current
- ]
- for s in sequences:
+ for s in context.sequences:
+ if s.frame_final_start >= frame_current:
+ continue
+
+ candidates = [frame_target, s.frame_final_start]
+ if s.frame_final_end < frame_current:
+ candidates.append(s.frame_final_end)
- frame_target = (
- s.frame_final_start if s.frame_final_end >= frame_current else s.frame_final_end
- )
+ frame_target = max(candidates)
for f in fcurves:
+ if s.name not in f.data_path:
+ continue
+
for k in f.keyframe_points:
frame = k.co[0]
- if frame >= context.scene.frame_current:
+ if frame >= frame_current:
continue
frame_target = max(frame_target, frame)
- break
- if frame_target != -1:
- context.scene.frame_current = max(1, frame_target)
+ if frame_target > 0 and frame_target != 100_000_000:
+ context.scene.frame_current = int(frame_target)
return {"FINISHED"}
diff --git a/power_sequencer/operators/make_hold_frame.py b/power_sequencer/operators/make_hold_frame.py
index 4c1f110d..3650a4cb 100644
--- a/power_sequencer/operators/make_hold_frame.py
+++ b/power_sequencer/operators/make_hold_frame.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
import operator
diff --git a/power_sequencer/operators/marker_delete_closest.py b/power_sequencer/operators/marker_delete_closest.py
index a4756958..68659b51 100644
--- a/power_sequencer/operators/marker_delete_closest.py
+++ b/power_sequencer/operators/marker_delete_closest.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
diff --git a/power_sequencer/operators/marker_delete_direct.py b/power_sequencer/operators/marker_delete_direct.py
index efc8ba66..3b6f9297 100644
--- a/power_sequencer/operators/marker_delete_direct.py
+++ b/power_sequencer/operators/marker_delete_direct.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
diff --git a/power_sequencer/operators/marker_snap_to_cursor.py b/power_sequencer/operators/marker_snap_to_cursor.py
index dbb3a6c7..8fe80653 100644
--- a/power_sequencer/operators/marker_snap_to_cursor.py
+++ b/power_sequencer/operators/marker_snap_to_cursor.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
diff --git a/power_sequencer/operators/markers_as_timecodes.py b/power_sequencer/operators/markers_as_timecodes.py
index 52658838..74b42794 100644
--- a/power_sequencer/operators/markers_as_timecodes.py
+++ b/power_sequencer/operators/markers_as_timecodes.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
import datetime as dt
diff --git a/power_sequencer/operators/markers_create_from_selected.py b/power_sequencer/operators/markers_create_from_selected.py
index bd346d84..620995de 100644
--- a/power_sequencer/operators/markers_create_from_selected.py
+++ b/power_sequencer/operators/markers_create_from_selected.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
diff --git a/power_sequencer/operators/markers_set_preview_in_between.py b/power_sequencer/operators/markers_set_preview_in_between.py
index 60724312..8ac0e9f6 100644
--- a/power_sequencer/operators/markers_set_preview_in_between.py
+++ b/power_sequencer/operators/markers_set_preview_in_between.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.functions import find_neighboring_markers
diff --git a/power_sequencer/operators/markers_snap_matching_strips.py b/power_sequencer/operators/markers_snap_matching_strips.py
index da475eeb..b9eab7b4 100644
--- a/power_sequencer/operators/markers_snap_matching_strips.py
+++ b/power_sequencer/operators/markers_snap_matching_strips.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
diff --git a/power_sequencer/operators/meta_resize_to_content.py b/power_sequencer/operators/meta_resize_to_content.py
index ed54ebca..b5d0b29f 100644
--- a/power_sequencer/operators/meta_resize_to_content.py
+++ b/power_sequencer/operators/meta_resize_to_content.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.functions import get_frame_range
diff --git a/power_sequencer/operators/meta_trim_content_to_bounds.py b/power_sequencer/operators/meta_trim_content_to_bounds.py
index 11b48369..e309f8e1 100644
--- a/power_sequencer/operators/meta_trim_content_to_bounds.py
+++ b/power_sequencer/operators/meta_trim_content_to_bounds.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.global_settings import SequenceTypes
diff --git a/power_sequencer/operators/meta_ungroup_and_trim.py b/power_sequencer/operators/meta_ungroup_and_trim.py
index 00c79cf4..e467492d 100644
--- a/power_sequencer/operators/meta_ungroup_and_trim.py
+++ b/power_sequencer/operators/meta_ungroup_and_trim.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
diff --git a/power_sequencer/operators/mouse_toggle_mute.py b/power_sequencer/operators/mouse_toggle_mute.py
index 362b2e9d..cd135829 100644
--- a/power_sequencer/operators/mouse_toggle_mute.py
+++ b/power_sequencer/operators/mouse_toggle_mute.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
"""Toggle mute a sequence as you click on it"""
import bpy
from math import floor
diff --git a/power_sequencer/operators/mouse_trim_instantly.py b/power_sequencer/operators/mouse_trim_instantly.py
index 332053fc..3a79a5a7 100644
--- a/power_sequencer/operators/mouse_trim_instantly.py
+++ b/power_sequencer/operators/mouse_trim_instantly.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from math import floor
diff --git a/power_sequencer/operators/mouse_trim_modal.py b/power_sequencer/operators/mouse_trim_modal.py
index a138933b..8136adae 100644
--- a/power_sequencer/operators/mouse_trim_modal.py
+++ b/power_sequencer/operators/mouse_trim_modal.py
@@ -1,10 +1,6 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
-import bgl
import gpu
import math
from mathutils import Vector
@@ -361,8 +357,8 @@ def draw(self, context, frame_start=-1, frame_end=-1, target_strips=[], draw_arr
rect_origin = Vector((start_x, start_y))
rect_size = Vector((end_x - start_x, abs(start_y - end_y)))
- bgl.glEnable(bgl.GL_BLEND)
- bgl.glLineWidth(3)
+ gpu.state.blend_set('ALPHA')
+ gpu.state.line_width_set(3.0)
draw_rectangle(SHADER, rect_origin, rect_size, color_fill)
# Vertical lines
draw_line(SHADER, Vector((start_x, start_y)), Vector((start_x, end_y)), color_line)
@@ -377,8 +373,8 @@ def draw(self, context, frame_start=-1, frame_end=-1, target_strips=[], draw_arr
draw_triangle_equilateral(SHADER, center_1, radius, color=color_line)
draw_triangle_equilateral(SHADER, center_2, radius, math.pi, color=color_line)
- bgl.glLineWidth(1)
- bgl.glDisable(bgl.GL_BLEND)
+ gpu.state.line_width_set(1)
+ gpu.state.blend_set('NONE')
def get_frame_and_channel(event):
diff --git a/power_sequencer/operators/open_project_directory.py b/power_sequencer/operators/open_project_directory.py
index f32f2ac2..02c72de1 100644
--- a/power_sequencer/operators/open_project_directory.py
+++ b/power_sequencer/operators/open_project_directory.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
import os
from platform import system
diff --git a/power_sequencer/operators/playback_speed_decrease.py b/power_sequencer/operators/playback_speed_decrease.py
deleted file mode 100644
index a856ac4b..00000000
--- a/power_sequencer/operators/playback_speed_decrease.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
-import bpy
-
-from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
-
-
-class POWER_SEQUENCER_OT_playback_speed_decrease(bpy.types.Operator):
- """
- *brief* Decrease playback speed incrementally down to normal
-
-
- Playback speed may be set to any of the following speeds:
-
- * Normal (1x)
- * Fast (1.33x)
- * Faster (1.66x)
- * Double (2x)
- * Triple (3x)
-
- Activating this operator will decrease playback speed through each
- of these steps until minimum speed is reached.
- """
-
- doc = {
- "name": doc_name(__qualname__),
- "demo": "",
- "description": doc_description(__doc__),
- "shortcuts": [({"type": "COMMA", "value": "PRESS"}, {}, "Decrease Playback Speed")],
- "keymap": "Sequencer",
- }
- bl_idname = doc_idname(__qualname__)
- bl_label = doc["name"]
- bl_description = doc_brief(doc["description"])
-
- @classmethod
- def poll(cls, context):
- return context.sequences
-
- def execute(self, context):
- scene = context.scene
-
- speeds = ["NORMAL", "FAST", "FASTER", "DOUBLE", "TRIPLE"]
- playback_speed = scene.power_sequencer.playback_speed
-
- index = max(0, speeds.index(playback_speed) - 1)
- scene.power_sequencer.playback_speed = speeds[index]
-
- return {"FINISHED"}
diff --git a/power_sequencer/operators/playback_speed_increase.py b/power_sequencer/operators/playback_speed_increase.py
deleted file mode 100644
index 7c2f2ad2..00000000
--- a/power_sequencer/operators/playback_speed_increase.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
-import bpy
-
-from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
-
-
-class POWER_SEQUENCER_OT_playback_speed_increase(bpy.types.Operator):
- """
- *brief* Increase playback speed up to triple
-
-
- Playback speed may be set to any of the following speeds:
-
- * Normal (1x)
- * Fast (1.33x)
- * Faster (1.66x)
- * Double (2x)
- * Triple (3x)
-
- Activating this operator will increase playback speed through each
- of these steps until maximum speed is reached.
- """
-
- doc = {
- "name": doc_name(__qualname__),
- "demo": "",
- "description": doc_description(__doc__),
- "shortcuts": [({"type": "PERIOD", "value": "PRESS"}, {}, "Increase playback speed")],
- "keymap": "Sequencer",
- }
- bl_idname = doc_idname(__qualname__)
- bl_label = doc["name"]
- bl_description = doc_brief(doc["description"])
-
- @classmethod
- def poll(cls, context):
- return context.sequences
-
- def execute(self, context):
- scene = context.scene
-
- speeds = ["NORMAL", "FAST", "FASTER", "DOUBLE", "TRIPLE"]
- playback_speed = scene.power_sequencer.playback_speed
-
- index = min(speeds.index(playback_speed) + 1, len(speeds) - 1)
- scene.power_sequencer.playback_speed = speeds[index]
-
- return {"FINISHED"}
diff --git a/power_sequencer/operators/playback_speed_set.py b/power_sequencer/operators/playback_speed_set.py
index 9f576247..82c45b7a 100644
--- a/power_sequencer/operators/playback_speed_set.py
+++ b/power_sequencer/operators/playback_speed_set.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
@@ -19,14 +16,8 @@ class POWER_SEQUENCER_OT_playback_speed_set(bpy.types.Operator):
"description": doc_description(__doc__),
"shortcuts": [
({"type": "ONE", "ctrl": True, "value": "PRESS"}, {"speed": "NORMAL"}, "Speed to 1x"),
- ({"type": "TWO", "ctrl": True, "value": "PRESS"}, {"speed": "FAST"}, "Speed to 1.33x"),
- (
- {"type": "THREE", "ctrl": True, "value": "PRESS"},
- {"speed": "FASTER"},
- "Speed to 1.66x",
- ),
- ({"type": "FOUR", "ctrl": True, "value": "PRESS"}, {"speed": "DOUBLE"}, "Speed to 2x"),
- ({"type": "FIVE", "ctrl": True, "value": "PRESS"}, {"speed": "TRIPLE"}, "Speed to 3x"),
+ ({"type": "TWO", "ctrl": True, "value": "PRESS"}, {"speed": "DOUBLE"}, "Speed to 2x"),
+ ({"type": "THREE", "ctrl": True, "value": "PRESS"}, {"speed": "TRIPLE"}, "Speed to 3x"),
],
"keymap": "Sequencer",
}
@@ -38,8 +29,6 @@ class POWER_SEQUENCER_OT_playback_speed_set(bpy.types.Operator):
speed: bpy.props.EnumProperty(
items=[
("NORMAL", "Normal (1x)", ""),
- ("FAST", "Fast (1.33x)", ""),
- ("FASTER", "Faster (1.66x)", ""),
("DOUBLE", "Double (2x)", ""),
("TRIPLE", "Triple (3x)", ""),
],
diff --git a/power_sequencer/operators/preview_closest_cut.py b/power_sequencer/operators/preview_closest_cut.py
index 312be788..2375c21a 100644
--- a/power_sequencer/operators/preview_closest_cut.py
+++ b/power_sequencer/operators/preview_closest_cut.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.functions import get_frame_range
diff --git a/power_sequencer/operators/preview_to_selection.py b/power_sequencer/operators/preview_to_selection.py
index ea6c0ed7..7ad8e2c7 100644
--- a/power_sequencer/operators/preview_to_selection.py
+++ b/power_sequencer/operators/preview_to_selection.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.functions import get_frame_range
diff --git a/power_sequencer/operators/render_apply_preset.py b/power_sequencer/operators/render_apply_preset.py
index dc3b0ed1..d7ad5b38 100644
--- a/power_sequencer/operators/render_apply_preset.py
+++ b/power_sequencer/operators/render_apply_preset.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
import os
diff --git a/power_sequencer/operators/render_presets/twitter_720p.py b/power_sequencer/operators/render_presets/twitter_720p.py
index 109090cc..eadb4266 100644
--- a/power_sequencer/operators/render_presets/twitter_720p.py
+++ b/power_sequencer/operators/render_presets/twitter_720p.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
if __name__ == "__main__":
import bpy
diff --git a/power_sequencer/operators/render_presets/youtube_1080.py b/power_sequencer/operators/render_presets/youtube_1080.py
index 5bf6e826..61bab65d 100644
--- a/power_sequencer/operators/render_presets/youtube_1080.py
+++ b/power_sequencer/operators/render_presets/youtube_1080.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
if __name__ == "__main__":
import bpy
diff --git a/power_sequencer/operators/ripple_delete.py b/power_sequencer/operators/ripple_delete.py
index 939da4e0..515f318f 100644
--- a/power_sequencer/operators/ripple_delete.py
+++ b/power_sequencer/operators/ripple_delete.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_brief, doc_description, doc_idname, doc_name
diff --git a/power_sequencer/operators/save_direct.py b/power_sequencer/operators/save_direct.py
index f41c3d4f..09b130a3 100644
--- a/power_sequencer/operators/save_direct.py
+++ b/power_sequencer/operators/save_direct.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
diff --git a/power_sequencer/operators/scene_create_from_selection.py b/power_sequencer/operators/scene_create_from_selection.py
index 206e385a..15ed0237 100644
--- a/power_sequencer/operators/scene_create_from_selection.py
+++ b/power_sequencer/operators/scene_create_from_selection.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from operator import attrgetter
diff --git a/power_sequencer/operators/scene_cycle.py b/power_sequencer/operators/scene_cycle.py
index 1be39ab9..05b861e7 100644
--- a/power_sequencer/operators/scene_cycle.py
+++ b/power_sequencer/operators/scene_cycle.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
diff --git a/power_sequencer/operators/scene_merge_from.py b/power_sequencer/operators/scene_merge_from.py
index b411e039..9d790baf 100644
--- a/power_sequencer/operators/scene_merge_from.py
+++ b/power_sequencer/operators/scene_merge_from.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
diff --git a/power_sequencer/operators/scene_open_from_strip.py b/power_sequencer/operators/scene_open_from_strip.py
index 0b992356..8c868bc3 100644
--- a/power_sequencer/operators/scene_open_from_strip.py
+++ b/power_sequencer/operators/scene_open_from_strip.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
diff --git a/power_sequencer/operators/scene_rename_with_strip.py b/power_sequencer/operators/scene_rename_with_strip.py
index 8bc6432f..b5327439 100644
--- a/power_sequencer/operators/scene_rename_with_strip.py
+++ b/power_sequencer/operators/scene_rename_with_strip.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
diff --git a/power_sequencer/operators/select_all_left_or_right.py b/power_sequencer/operators/select_all_left_or_right.py
index 7d9087ee..794a9378 100644
--- a/power_sequencer/operators/select_all_left_or_right.py
+++ b/power_sequencer/operators/select_all_left_or_right.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
@@ -51,4 +48,10 @@ class POWER_SEQUENCER_OT_select_all_left_or_right(bpy.types.Operator):
return context.sequences
def execute(self, context):
- return bpy.ops.sequencer.select("INVOKE_DEFAULT", left_right=self.side)
+ if self.side == "LEFT":
+ for s in context.sequences:
+ s.select = s.frame_final_end < context.scene.frame_current
+ else:
+ for s in context.sequences:
+ s.select = s.frame_final_start > context.scene.frame_current
+ return {"FINISHED"}
diff --git a/power_sequencer/operators/select_closest_to_mouse.py b/power_sequencer/operators/select_closest_to_mouse.py
index f14bc309..dee7e967 100644
--- a/power_sequencer/operators/select_closest_to_mouse.py
+++ b/power_sequencer/operators/select_closest_to_mouse.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.functions import find_strips_mouse
diff --git a/power_sequencer/operators/select_linked_effect.py b/power_sequencer/operators/select_linked_effect.py
index a63096f1..d00a7a4a 100644
--- a/power_sequencer/operators/select_linked_effect.py
+++ b/power_sequencer/operators/select_linked_effect.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.functions import find_linked
diff --git a/power_sequencer/operators/select_linked_strips.py b/power_sequencer/operators/select_linked_strips.py
index 5716d13d..29022257 100644
--- a/power_sequencer/operators/select_linked_strips.py
+++ b/power_sequencer/operators/select_linked_strips.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
diff --git a/power_sequencer/operators/select_related_strips.py b/power_sequencer/operators/select_related_strips.py
index 0e442ebd..e7101fee 100644
--- a/power_sequencer/operators/select_related_strips.py
+++ b/power_sequencer/operators/select_related_strips.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.global_settings import SequenceTypes
diff --git a/power_sequencer/operators/select_strips_under_cursor.py b/power_sequencer/operators/select_strips_under_cursor.py
index a47d6e80..0c4712b5 100644
--- a/power_sequencer/operators/select_strips_under_cursor.py
+++ b/power_sequencer/operators/select_strips_under_cursor.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.functions import get_sequences_under_cursor
diff --git a/power_sequencer/operators/set_timeline_range.py b/power_sequencer/operators/set_timeline_range.py
index 65a90f4d..a9bbd163 100644
--- a/power_sequencer/operators/set_timeline_range.py
+++ b/power_sequencer/operators/set_timeline_range.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
diff --git a/power_sequencer/operators/snap.py b/power_sequencer/operators/snap.py
index 793f5bd9..24a4f52a 100644
--- a/power_sequencer/operators/snap.py
+++ b/power_sequencer/operators/snap.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.functions import get_sequences_under_cursor
@@ -21,7 +18,11 @@ class POWER_SEQUENCER_OT_snap(bpy.types.Operator):
"demo": "",
"description": doc_description(__doc__),
"shortcuts": [
- ({"type": "S", "value": "PRESS", "shift": True}, {}, "Snap sequences to cursor",)
+ (
+ {"type": "S", "value": "PRESS", "shift": True},
+ {},
+ "Snap sequences to cursor",
+ )
],
"keymap": "Sequencer",
}
diff --git a/power_sequencer/operators/snap_selection.py b/power_sequencer/operators/snap_selection.py
index 38da4c17..b73c5561 100644
--- a/power_sequencer/operators/snap_selection.py
+++ b/power_sequencer/operators/snap_selection.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.functions import get_sequences_under_cursor, move_selection
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
@@ -21,7 +18,11 @@ class POWER_SEQUENCER_OT_snap_selection(bpy.types.Operator):
"demo": "",
"description": doc_description(__doc__),
"shortcuts": [
- ({"type": "S", "value": "PRESS", "alt": True}, {}, "Snap selection to cursor",)
+ (
+ {"type": "S", "value": "PRESS", "alt": True},
+ {},
+ "Snap selection to cursor",
+ )
],
"keymap": "Sequencer",
}
diff --git a/power_sequencer/operators/space_sequences.py b/power_sequencer/operators/space_sequences.py
index ddb28dfe..da3a0ac5 100644
--- a/power_sequencer/operators/space_sequences.py
+++ b/power_sequencer/operators/space_sequences.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.functions import convert_duration_to_frames
diff --git a/power_sequencer/operators/speed_remove_effect.py b/power_sequencer/operators/speed_remove_effect.py
index ae2a4d35..69348461 100644
--- a/power_sequencer/operators/speed_remove_effect.py
+++ b/power_sequencer/operators/speed_remove_effect.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
diff --git a/power_sequencer/operators/speed_up_movie_strip.py b/power_sequencer/operators/speed_up_movie_strip.py
index c004e2fc..9e863f36 100644
--- a/power_sequencer/operators/speed_up_movie_strip.py
+++ b/power_sequencer/operators/speed_up_movie_strip.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from math import ceil
@@ -24,9 +21,21 @@ class POWER_SEQUENCER_OT_speed_up_movie_strip(bpy.types.Operator):
"demo": "https://i.imgur.com/ZyEd0jD.gif",
"description": doc_description(__doc__),
"shortcuts": [
- ({"type": "TWO", "value": "PRESS", "alt": True}, {"speed_factor": 2.0}, "Speed x2",),
- ({"type": "THREE", "value": "PRESS", "alt": True}, {"speed_factor": 3.0}, "Speed x3",),
- ({"type": "FOUR", "value": "PRESS", "alt": True}, {"speed_factor": 4.0}, "Speed x4",),
+ (
+ {"type": "TWO", "value": "PRESS", "alt": True},
+ {"speed_factor": 2.0},
+ "Speed x2",
+ ),
+ (
+ {"type": "THREE", "value": "PRESS", "alt": True},
+ {"speed_factor": 3.0},
+ "Speed x3",
+ ),
+ (
+ {"type": "FOUR", "value": "PRESS", "alt": True},
+ {"speed_factor": 4.0},
+ "Speed x4",
+ ),
],
"keymap": "Sequencer",
}
diff --git a/power_sequencer/operators/swap_strips.py b/power_sequencer/operators/swap_strips.py
index 44537c43..6b0460fa 100644
--- a/power_sequencer/operators/swap_strips.py
+++ b/power_sequencer/operators/swap_strips.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from operator import attrgetter
diff --git a/power_sequencer/operators/toggle_selected_mute.py b/power_sequencer/operators/toggle_selected_mute.py
index 3ee1a022..1a79237f 100644
--- a/power_sequencer/operators/toggle_selected_mute.py
+++ b/power_sequencer/operators/toggle_selected_mute.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
diff --git a/power_sequencer/operators/toggle_waveforms.py b/power_sequencer/operators/toggle_waveforms.py
index 3e064182..12104493 100644
--- a/power_sequencer/operators/toggle_waveforms.py
+++ b/power_sequencer/operators/toggle_waveforms.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.global_settings import SequenceTypes
diff --git a/power_sequencer/operators/transitions_remove.py b/power_sequencer/operators/transitions_remove.py
index d1db0d74..1fb1b0d0 100644
--- a/power_sequencer/operators/transitions_remove.py
+++ b/power_sequencer/operators/transitions_remove.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from operator import attrgetter
diff --git a/power_sequencer/operators/trim_left_or_right_handles.py b/power_sequencer/operators/trim_left_or_right_handles.py
index aea81610..c02b8650 100644
--- a/power_sequencer/operators/trim_left_or_right_handles.py
+++ b/power_sequencer/operators/trim_left_or_right_handles.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
from operator import attrgetter
import bpy
diff --git a/power_sequencer/operators/trim_three_point_edit.py b/power_sequencer/operators/trim_three_point_edit.py
index f174589e..6d173258 100644
--- a/power_sequencer/operators/trim_three_point_edit.py
+++ b/power_sequencer/operators/trim_three_point_edit.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.functions import get_mouse_frame_and_channel
diff --git a/power_sequencer/operators/trim_to_surrounding_cuts.py b/power_sequencer/operators/trim_to_surrounding_cuts.py
index ab5df939..3057dfe5 100644
--- a/power_sequencer/operators/trim_to_surrounding_cuts.py
+++ b/power_sequencer/operators/trim_to_surrounding_cuts.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
"""
Find the two closest cuts, trims and deletes all strips above in the range but leaves some
margin. Removes the newly formed gap.
diff --git a/power_sequencer/operators/utils/__init__.py b/power_sequencer/operators/utils/__init__.py
index 71fca8c4..be8d8b53 100644
--- a/power_sequencer/operators/utils/__init__.py
+++ b/power_sequencer/operators/utils/__init__.py
@@ -1,4 +1,2 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
diff --git a/power_sequencer/operators/utils/doc.py b/power_sequencer/operators/utils/doc.py
index 8bdae410..be52c7c2 100644
--- a/power_sequencer/operators/utils/doc.py
+++ b/power_sequencer/operators/utils/doc.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
"""
Utilities to convert operator names and docstrings to human-readable text.
Used to generate names for Blender's operator search, and to generate Power Sequencer's documentation.
diff --git a/power_sequencer/operators/utils/draw.py b/power_sequencer/operators/utils/draw.py
index 5a156bea..3cede778 100644
--- a/power_sequencer/operators/utils/draw.py
+++ b/power_sequencer/operators/utils/draw.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
"""Drawing utilities. A list of functions to draw common elements"""
# import bgl
import blf
diff --git a/power_sequencer/operators/utils/functions.py b/power_sequencer/operators/utils/functions.py
index 0d5c9fcf..c4552c2d 100644
--- a/power_sequencer/operators/utils/functions.py
+++ b/power_sequencer/operators/utils/functions.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
from math import floor, sqrt
from operator import attrgetter
@@ -279,9 +276,7 @@ def trim_strips(context, frame_start, frame_end, to_trim, to_delete=[]):
elif s.frame_final_end > trim_start and s.frame_final_start < trim_start:
s.frame_final_end = trim_start
- for s in to_delete:
- bpy.context.sequences.remove(s)
-
+ delete_strips(to_delete)
for s in initial_selection:
s.select = True
return {"FINISHED"}
@@ -331,7 +326,8 @@ def get_sequences_under_cursor(context):
def ripple_move(context, sequences, duration_frames, delete=False):
- """Moves sequences in the list and ripples the change to all sequences after them, in the corresponding channels
+ """
+ Moves sequences in the list and ripples the change to all sequences after them, in the corresponding channels
The `duration_frames` can be positive or negative.
If `delete` is True, deletes every sequence in `sequences`.
"""
@@ -344,10 +340,7 @@ def ripple_move(context, sequences, duration_frames, delete=False):
]
if delete:
- bpy.ops.sequencer.select_all(action="DESELECT")
- for s in sequences:
- s.select = True
- bpy.ops.sequencer.delete()
+ delete_strips(sequences)
else:
to_ripple = set(to_ripple + sequences)
@@ -390,8 +383,20 @@ def find_strips_in_range(frame_start, frame_end, sequences, find_overlapping=Tru
return strips_inside_range, strips_overlapping_range
+def delete_strips(to_delete):
+ """
+ Deletes the list of sequences `to_delete`
+ """
+ # Effect strips get deleted with their source so we skip them to avoid errors.
+ to_delete = [s for s in to_delete if s.type in SequenceTypes.CUTABLE]
+ sequences = bpy.context.scene.sequence_editor.sequences
+ for s in to_delete:
+ sequences.remove(s)
+
+
def move_selection(context, sequences, frame_offset, channel_offset=0):
- """Offsets the selected `sequences` horizontally and vertically and preserves
+ """
+ Offsets the selected `sequences` horizontally and vertically and preserves
the current selected sequences.
"""
if not sequences:
diff --git a/power_sequencer/operators/utils/global_settings.py b/power_sequencer/operators/utils/global_settings.py
index a5c9756b..6c5ed55a 100644
--- a/power_sequencer/operators/utils/global_settings.py
+++ b/power_sequencer/operators/utils/global_settings.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
class ProjectSettings:
RESOLUTION_X = 1920
RESOLUTION_Y = 1080
diff --git a/power_sequencer/operators/utils/info_progress_bar.py b/power_sequencer/operators/utils/info_progress_bar.py
index e645c762..e479aecf 100644
--- a/power_sequencer/operators/utils/info_progress_bar.py
+++ b/power_sequencer/operators/utils/info_progress_bar.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
diff --git a/power_sequencer/operators/value_offset.py b/power_sequencer/operators/value_offset.py
index 919e8a43..f7ea129f 100644
--- a/power_sequencer/operators/value_offset.py
+++ b/power_sequencer/operators/value_offset.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .utils.doc import doc_brief, doc_description, doc_idname, doc_name
@@ -10,8 +7,9 @@ from .utils.functions import convert_duration_to_frames
class POWER_SEQUENCER_OT_value_offset(bpy.types.Operator):
- """Instantly offset selected strips, either using frames or seconds. """ \
- """Allows to nudge the selection quickly, using keyboard shortcuts"""
+ """Instantly offset selected strips, either using frames or seconds. Allows to
+ nudge the selection quickly, using keyboard shortcuts.
+ """
doc = {
"name": doc_name(__qualname__),
diff --git a/power_sequencer/tools/__init__.py b/power_sequencer/tools/__init__.py
index e7ac0ddc..6c24a950 100644
--- a/power_sequencer/tools/__init__.py
+++ b/power_sequencer/tools/__init__.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import importlib
import os
@@ -16,7 +13,7 @@ def get_tool_classes():
module_paths = ["." + os.path.splitext(f)[0] for f in module_files]
classes = []
for path in module_paths:
- module = importlib.import_module(path, package="power_sequencer.tools")
+ module = importlib.import_module(path, package=__package__)
tool_names = [entry for entry in dir(module) if entry.startswith("POWER_SEQUENCER_TOOL")]
classes.extend([getattr(module, name) for name in tool_names])
return classes
diff --git a/power_sequencer/tools/trim.py b/power_sequencer/tools/trim.py
index 9c60b2d0..7b740828 100644
--- a/power_sequencer/tools/trim.py
+++ b/power_sequencer/tools/trim.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from bpy.types import WorkSpaceTool
diff --git a/power_sequencer/ui/__init__.py b/power_sequencer/ui/__init__.py
index 9b5b5c6b..b81bdea2 100644
--- a/power_sequencer/ui/__init__.py
+++ b/power_sequencer/ui/__init__.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from .menu_contextual import POWER_SEQUENCER_MT_contextual
from .menu_toolbar import (
diff --git a/power_sequencer/ui/menu_contextual.py b/power_sequencer/ui/menu_contextual.py
index 41628448..56b1b33a 100644
--- a/power_sequencer/ui/menu_contextual.py
+++ b/power_sequencer/ui/menu_contextual.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
from ..operators.utils.global_settings import SequenceTypes
diff --git a/power_sequencer/ui/menu_toolbar.py b/power_sequencer/ui/menu_toolbar.py
index 4feb1ea7..6372f139 100755
--- a/power_sequencer/ui/menu_toolbar.py
+++ b/power_sequencer/ui/menu_toolbar.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
diff --git a/power_sequencer/utils/addon_auto_imports.py b/power_sequencer/utils/addon_auto_imports.py
index 47f430b9..21a45615 100644
--- a/power_sequencer/utils/addon_auto_imports.py
+++ b/power_sequencer/utils/addon_auto_imports.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import pkgutil
import importlib
diff --git a/power_sequencer/utils/register_shortcuts.py b/power_sequencer/utils/register_shortcuts.py
index f928f7ff..29694cee 100644
--- a/power_sequencer/utils/register_shortcuts.py
+++ b/power_sequencer/utils/register_shortcuts.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-
-# This file is part of Power Sequencer.
-
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
import bpy
import operator as op
from .. import operators
diff --git a/precision_drawing_tools/__init__.py b/precision_drawing_tools/__init__.py
index 6cb9ab52..6fdb72f2 100644
--- a/precision_drawing_tools/__init__.py
+++ b/precision_drawing_tools/__init__.py
@@ -12,7 +12,7 @@
bl_info = {
"name": "Precision Drawing Tools (PDT)",
"author": "Alan Odom (Clockmender), Rune Morling (ermo)",
- "version": (1, 5, 2),
+ "version": (1, 5, 3),
"blender": (3, 0, 0),
"location": "View3D > UI > PDT",
"description": "Precision Drawing Tools for Accurate Modelling",
diff --git a/precision_drawing_tools/pdt_functions.py b/precision_drawing_tools/pdt_functions.py
index 66bf1de2..9352b66f 100644
--- a/precision_drawing_tools/pdt_functions.py
+++ b/precision_drawing_tools/pdt_functions.py
@@ -8,7 +8,6 @@
import bpy
import bmesh
-import bgl
import gpu
import numpy as np
from mathutils import Vector, Quaternion
@@ -584,7 +583,7 @@ def draw_3d(coords, gtype, rgba, context):
try:
if coords is not None:
- bgl.glEnable(bgl.GL_BLEND)
+ gpu.state.blend_set('ALPHA')
SHADER.bind()
SHADER.uniform_float("color", rgba)
batch.draw(SHADER)
diff --git a/precision_drawing_tools/pdt_tangent.py b/precision_drawing_tools/pdt_tangent.py
index 88abd2d0..f9ccda51 100644
--- a/precision_drawing_tools/pdt_tangent.py
+++ b/precision_drawing_tools/pdt_tangent.py
@@ -199,8 +199,8 @@ def tangent_setup(context, pg, plane, obj_data, centre_0, centre_1, centre_2, ra
obj_data: All the data of the chosen object
centre_0: Centre coordinates of the first arc
centre_1: Centre coordinates of the second arc
- centre_2: Coordinates fo the point
- radius_0: Radius if the first Arc
+ centre_2: Coordinates of the point
+ radius_0: Radius of the first Arc
radius_1: Radius of the second Arc
Returns:
diff --git a/render_povray/model_meta_topology.py b/render_povray/model_meta_topology.py
index 33a335fa..47e874c5 100644
--- a/render_povray/model_meta_topology.py
+++ b/render_povray/model_meta_topology.py
@@ -180,7 +180,7 @@ def export_meta(file, metas, tab_write, DEF_MAT_NAME):
try:
one_material = elems[1].data.materials[
0
- ] # lame! - blender cant do enything else.
+ ] # lame! - blender can't do enything else.
except BaseException as e:
print(e.__doc__)
print('An exception occurred: {}'.format(e))
@@ -236,7 +236,7 @@ def export_meta(file, metas, tab_write, DEF_MAT_NAME):
importance = ob.pov.importance_value
try:
- material = meta.materials[0] # lame! - blender cant do enything else.
+ material = meta.materials[0] # lame! - blender can't do anything else.
except:
material = None
diff --git a/render_povray/model_poly_topology.py b/render_povray/model_poly_topology.py
index c8a990ea..b4d200ec 100644
--- a/render_povray/model_poly_topology.py
+++ b/render_povray/model_poly_topology.py
@@ -52,7 +52,7 @@ def export_mesh(file,
except BaseException as e:
print(e.__doc__)
print("An exception occurred: {}".format(e))
- # also happens when curves cant be made into meshes because of no-data
+ # also happens when curves can't be made into meshes because of no-data
return False # To continue object loop
if me:
me.calc_loop_triangles()
diff --git a/render_povray/particles.py b/render_povray/particles.py
index 18cc0ac7..fdbd4647 100644
--- a/render_povray/particles.py
+++ b/render_povray/particles.py
@@ -75,7 +75,7 @@ def export_hair(file, ob, mod, p_sys, global_matrix):
# When you render, the entire dependency graph will be
# evaluated at render resolution, including the particles.
# In the viewport it will be at viewport resolution.
- # So there is no need fo render engines to use this function anymore,
+ # So there is no need for render engines to use this function anymore,
# it's automatic now.
steps = p_sys_settings.display_step
steps = 2**steps # or + 1 # Formerly : len(particle.hair_keys)
diff --git a/render_povray/render_gui.py b/render_povray/render_gui.py
index 7baa7aff..e00e3705 100755
--- a/render_povray/render_gui.py
+++ b/render_povray/render_gui.py
@@ -242,7 +242,7 @@ class RENDER_PT_POV_hues(RenderButtonsPanel, Panel):
class RENDER_PT_POV_pattern_rules(RenderButtonsPanel, Panel):
- """Use this class to change pov sets of texture generating algorythms."""
+ """Use this class to change pov sets of texture generating algorithms."""
bl_label = "Pattern Rules"
bl_parent_id = "RENDER_PT_POV_render_settings"
diff --git a/rigify/__init__.py b/rigify/__init__.py
index 10e90b4c..386c8055 100644
--- a/rigify/__init__.py
+++ b/rigify/__init__.py
@@ -539,8 +539,8 @@ def register():
IDStore.rigify_types = CollectionProperty(type=RigifyName)
IDStore.rigify_active_type = IntProperty(name="Rigify Active Type", description="The selected rig type")
- bpy.types.Armature.rigify_force_widget_update = BoolProperty(name="Force Widget Update",
- description="Forces Rigify to delete and rebuild all the rig widgets. if unset, only missing widgets will be created",
+ bpy.types.Armature.rigify_force_widget_update = BoolProperty(name="Overwrite Widget Meshes",
+ description="Forces Rigify to delete and rebuild all of the rig widget objects. By default, already existing widgets are reused as-is to facilitate manual editing",
default=False)
bpy.types.Armature.rigify_mirror_widgets = BoolProperty(name="Mirror Widgets",
@@ -550,14 +550,18 @@ def register():
name="Widgets Collection",
description="Defines which collection to place widget objects in. If unset, a new one will be created based on the name of the rig")
+ bpy.types.Armature.rigify_rig_basename = StringProperty(name="Rigify Rig Name",
+ description="Optional. If specified, this name will be used for the newly generated rig, widget collection and script. Otherwise, a name is generated based on the name of the metarig object by replacing 'metarig' with 'rig', 'META' with 'RIG', or prefixing with 'RIG-'. When updating an already generated rig its name is never changed",
+ default="")
+
bpy.types.Armature.rigify_target_rig = PointerProperty(type=bpy.types.Object,
name="Rigify Target Rig",
- description="Defines which rig to overwrite. If unset, a new one called 'rig' will be created",
+ description="Defines which rig to overwrite. If unset, a new one will be created with name based on the Rig Name option or the name of the metarig",
poll=lambda self, obj: obj.type == 'ARMATURE' and obj.data is not self)
bpy.types.Armature.rigify_rig_ui = PointerProperty(type=bpy.types.Text,
name="Rigify Target Rig UI",
- description="Defines the UI to overwrite. If unset, 'rig_ui.py' will be used")
+ description="Defines the UI to overwrite. If unset, a new one will be created and named based on the name of the rig")
bpy.types.Armature.rigify_finalize_script = PointerProperty(type=bpy.types.Text,
name="Finalize Script",
diff --git a/rigify/generate.py b/rigify/generate.py
index 3acc2e40..8a2aa942 100644
--- a/rigify/generate.py
+++ b/rigify/generate.py
@@ -65,7 +65,9 @@ class Generator(base_generate.BaseGenerator):
target_rig = meta_data.rigify_target_rig
if not target_rig:
- if "metarig" in self.metarig.name:
+ if meta_data.rigify_rig_basename:
+ rig_new_name = meta_data.rigify_rig_basename
+ elif "metarig" in self.metarig.name:
rig_new_name = self.metarig.name.replace("metarig", "rig")
elif "META" in self.metarig.name:
rig_new_name = self.metarig.name.replace("META", "RIG")
@@ -76,7 +78,7 @@ class Generator(base_generate.BaseGenerator):
target_rig.display_type = 'WIRE'
# If the object is already added to the scene, switch to its collection
- if target_rig.name in self.context.scene.collection.all_objects:
+ if target_rig in list(self.context.scene.collection.all_objects):
self.__switch_to_usable_collection(target_rig)
else:
# Otherwise, add to the selected collection or the metarig collection if unusable
@@ -117,11 +119,14 @@ class Generator(base_generate.BaseGenerator):
wgts_group_name = "WGTS_" + self.obj.name
old_collection = bpy.data.collections.get(wgts_group_name)
+ if old_collection and old_collection.library:
+ old_collection = None
+
if not old_collection:
# Update the old 'Widgets' collection
legacy_collection = bpy.data.collections.get('Widgets')
- if legacy_collection and wgts_group_name in legacy_collection.objects:
+ if legacy_collection and wgts_group_name in legacy_collection.objects and not legacy_collection.library:
legacy_collection.name = wgts_group_name
old_collection = legacy_collection
diff --git a/rigify/metarigs/Animals/horse.py b/rigify/metarigs/Animals/horse.py
index cd393c5c..c9be9d54 100644
--- a/rigify/metarigs/Animals/horse.py
+++ b/rigify/metarigs/Animals/horse.py
@@ -618,28 +618,28 @@ def create(obj):
bone.tail = 0.1990, -1.4668, 1.7420
bone.roll = 0.0000
bone.use_connect = False
- bone.parent = arm.edit_bones[bones['ear.L']]
+ bone.parent = arm.edit_bones[bones['head']]
bones['eye.L'] = bone.name
bone = arm.edit_bones.new('nose.L')
bone.head = 0.0450, -1.6240, 1.4228
bone.tail = 0.1039, -1.6613, 1.4269
bone.roll = 0.0000
bone.use_connect = False
- bone.parent = arm.edit_bones[bones['ear.L']]
+ bone.parent = arm.edit_bones[bones['head']]
bones['nose.L'] = bone.name
bone = arm.edit_bones.new('eye.R')
bone.head = -0.0988, -1.4596, 1.7351
bone.tail = -0.1990, -1.4668, 1.7420
bone.roll = -0.0000
bone.use_connect = False
- bone.parent = arm.edit_bones[bones['ear.L']]
+ bone.parent = arm.edit_bones[bones['head']]
bones['eye.R'] = bone.name
bone = arm.edit_bones.new('nose.R')
bone.head = -0.0450, -1.6240, 1.4228
bone.tail = -0.1039, -1.6613, 1.4269
bone.roll = -0.0000
bone.use_connect = False
- bone.parent = arm.edit_bones[bones['ear.L']]
+ bone.parent = arm.edit_bones[bones['head']]
bones['nose.R'] = bone.name
bone = arm.edit_bones.new('ear.R.001')
bone.head = -0.1056, -1.4118, 1.9537
diff --git a/rigify/metarigs/Animals/wolf.py b/rigify/metarigs/Animals/wolf.py
index a2ade910..87d74c87 100644
--- a/rigify/metarigs/Animals/wolf.py
+++ b/rigify/metarigs/Animals/wolf.py
@@ -169,7 +169,7 @@ def create(obj):
bone = arm.edit_bones.new('spine.004')
bone.head = 0.0000, 0.4085, 0.7928
- bone.tail = 0.0000, 0.2416, 0.7928
+ bone.tail = 0.0000, 0.2416, 0.7927
bone.roll = 0.0000
bone.use_connect = False
bones['spine.004'] = bone.name
@@ -181,7 +181,7 @@ def create(obj):
bone.parent = arm.edit_bones[bones['spine.004']]
bones['spine.003'] = bone.name
bone = arm.edit_bones.new('spine.005')
- bone.head = 0.0000, 0.2416, 0.7928
+ bone.head = 0.0000, 0.2416, 0.7927
bone.tail = 0.0000, 0.1202, 0.7773
bone.roll = 0.0000
bone.use_connect = True
@@ -189,14 +189,14 @@ def create(obj):
bones['spine.005'] = bone.name
bone = arm.edit_bones.new('spine.002')
bone.head = 0.0000, 0.5555, 0.7567
- bone.tail = 0.0000, 0.7816, 0.7412
+ bone.tail = 0.0000, 0.7816, 0.7411
bone.roll = 0.0000
bone.use_connect = True
bone.parent = arm.edit_bones[bones['spine.003']]
bones['spine.002'] = bone.name
bone = arm.edit_bones.new('spine.006')
bone.head = 0.0000, 0.1202, 0.7773
- bone.tail = 0.0000, 0.0096, 0.7773
+ bone.tail = 0.0000, 0.0096, 0.7772
bone.roll = 0.0000
bone.use_connect = True
bone.parent = arm.edit_bones[bones['spine.005']]
@@ -230,14 +230,14 @@ def create(obj):
bone.parent = arm.edit_bones[bones['spine.005']]
bones['thigh.R'] = bone.name
bone = arm.edit_bones.new('spine.001')
- bone.head = 0.0000, 0.7816, 0.7412
+ bone.head = 0.0000, 0.7816, 0.7411
bone.tail = 0.0000, 0.9624, 0.7412
bone.roll = 0.0000
bone.use_connect = True
bone.parent = arm.edit_bones[bones['spine.002']]
bones['spine.001'] = bone.name
bone = arm.edit_bones.new('spine.007')
- bone.head = 0.0000, 0.0096, 0.7773
+ bone.head = 0.0000, 0.0096, 0.7772
bone.tail = 0.0000, -0.0980, 0.7945
bone.roll = 0.0000
bone.use_connect = True
diff --git a/rigify/rig_ui_template.py b/rigify/rig_ui_template.py
index b98907ee..d581805f 100644
--- a/rigify/rig_ui_template.py
+++ b/rigify/rig_ui_template.py
@@ -1159,7 +1159,8 @@ class ScriptGenerator(base_generate.GeneratorPlugin):
if script:
script.clear()
else:
- script = bpy.data.texts.new("rig_ui.py")
+ script_name = self.generator.obj.name + "_ui.py"
+ script = bpy.data.texts.new(script_name)
metarig.data.rigify_rig_ui = script
for s in OrderedDict.fromkeys(self.ui_imports):
diff --git a/rigify/rigs/limbs/limb_rigs.py b/rigify/rigs/limbs/limb_rigs.py
index fa20dd31..a094e176 100644
--- a/rigify/rigs/limbs/limb_rigs.py
+++ b/rigify/rigs/limbs/limb_rigs.py
@@ -52,6 +52,7 @@ class BaseLimbRig(BaseRig):
self.segments = self.params.segments
self.bbone_segments = self.params.bbones
self.use_ik_pivot = self.params.make_custom_pivot
+ self.use_uniform_scale = self.params.limb_uniform_scale
rot_axis = self.params.rotation_axis
@@ -144,6 +145,8 @@ class BaseLimbRig(BaseRig):
# FK chain parents (or None)
# ik_pivot
# Custom IK pivot result (optional).
+ # ik_scale
+ # Helper bone that implements uniform scaling.
# ik_swing
# Bone that tracks ik_target to manually handle limb swing.
# ik_target
@@ -178,14 +181,15 @@ class BaseLimbRig(BaseRig):
bone = self.get_bone(self.bones.ctrl.master)
bone.lock_location = (True, True, True)
bone.lock_rotation = (True, True, True)
- bone.lock_scale = (False, False, False)
+ bone.lock_scale = (not self.use_uniform_scale,) * 3
bone.lock_rotation_w = True
@stage.rig_bones
def rig_master_control(self):
mch = self.bones.mch
self.make_constraint(mch.master, 'COPY_SCALE', 'root', use_make_uniform=True)
- self.make_constraint(mch.master, 'COPY_SCALE', self.bones.ctrl.master, use_offset=True, space='LOCAL')
+ if self.use_uniform_scale:
+ self.make_constraint(mch.master, 'COPY_SCALE', self.bones.ctrl.master, use_offset=True, space='LOCAL')
@stage.generate_widgets
def make_master_control_widget(self):
@@ -221,10 +225,12 @@ class BaseLimbRig(BaseRig):
mch = self.bones.mch.follow
self.make_constraint(mch, 'COPY_SCALE', 'root', use_make_uniform=True)
- self.make_constraint(
- mch, 'COPY_SCALE', self.bones.ctrl.master,
- use_make_uniform=True, use_offset=True, space='LOCAL'
- )
+
+ if self.use_uniform_scale:
+ self.make_constraint(
+ mch, 'COPY_SCALE', self.bones.ctrl.master,
+ use_make_uniform=True, use_offset=True, space='LOCAL'
+ )
con = self.make_constraint(mch, 'COPY_ROTATION', 'root')
@@ -361,9 +367,12 @@ class BaseLimbRig(BaseRig):
self.bones.ctrl.ik_base = self.make_ik_base_bone(orgs)
self.bones.ctrl.ik_pole = self.make_ik_pole_bone(orgs)
- self.bones.ctrl.ik = ik_name = self.make_ik_control_bone(orgs)
+ self.bones.ctrl.ik = ik_name = parent = self.make_ik_control_bone(orgs)
+
+ if self.use_uniform_scale:
+ self.bones.mch.ik_scale = parent = self.make_ik_scale_bone(ik_name, orgs)
- self.component_ik_pivot = self.build_ik_pivot(ik_name)
+ self.component_ik_pivot = self.build_ik_pivot(ik_name, parent=parent)
self.build_ik_parent_switch(SwitchParentBuilder(self.generator))
def make_ik_base_bone(self, orgs):
@@ -382,13 +391,18 @@ class BaseLimbRig(BaseRig):
def make_ik_control_bone(self, orgs):
return self.copy_bone(orgs[2], make_derived_name(orgs[2], 'ctrl', '_ik'))
+ def make_ik_scale_bone(self, ctrl, orgs):
+ return self.copy_bone(ctrl, make_derived_name(orgs[2], 'mch', '_ik_scale'), scale=1/2)
+
def build_ik_pivot(self, ik_name, **args):
if self.use_ik_pivot:
- return CustomPivotControl(self, 'ik_pivot', ik_name, parent=ik_name, **args)
+ return CustomPivotControl(self, 'ik_pivot', ik_name, **args)
def get_ik_control_output(self):
if self.component_ik_pivot:
return self.component_ik_pivot.output
+ elif self.use_uniform_scale:
+ return self.bones.mch.ik_scale
else:
return self.bones.ctrl.ik
@@ -430,6 +444,9 @@ class BaseLimbRig(BaseRig):
else:
self.set_bone_parent(self.bones.ctrl.ik_base, self.bones.mch.ik_swing)
+ if self.use_uniform_scale:
+ self.set_bone_parent(self.bones.mch.ik_scale, self.bones.ctrl.ik)
+
self.set_ik_local_location(self.bones.ctrl.ik)
self.set_ik_local_location(self.bones.ctrl.ik_pole)
@@ -445,11 +462,13 @@ class BaseLimbRig(BaseRig):
@stage.rig_bones
def rig_ik_controls(self):
self.rig_hide_pole_control(self.bones.ctrl.ik_pole)
- self.rig_ik_control_scale(self.bones.ctrl.ik)
- def rig_ik_control_scale(self, ctrl):
+ if self.use_uniform_scale:
+ self.rig_ik_control_scale(self.bones.mch.ik_scale)
+
+ def rig_ik_control_scale(self, mch):
self.make_constraint(
- ctrl, 'COPY_SCALE', self.bones.ctrl.master,
+ mch, 'COPY_SCALE', self.bones.ctrl.master,
use_make_uniform=True, use_offset=True, space='LOCAL',
)
@@ -937,6 +956,12 @@ class BaseLimbRig(BaseRig):
description = "Specifies the value of the Local Location option for IK controls, which decides if the location channels are aligned to the local control orientation or world",
)
+ params.limb_uniform_scale = bpy.props.BoolProperty(
+ name = "Support Uniform Scaling",
+ default = False,
+ description = "Support uniformly scaling the limb via the gear control at the base"
+ )
+
# Setting up extra layers for the FK and tweak
ControlLayersOption.FK.add_parameters(params)
ControlLayersOption.TWEAK.add_parameters(params)
@@ -958,6 +983,7 @@ class BaseLimbRig(BaseRig):
r = layout.row()
r.prop(params, "bbones")
+ layout.prop(params, 'limb_uniform_scale')
layout.prop(params, 'make_custom_pivot', text="Custom IK Pivot")
layout.prop(params, 'ik_local_location')
diff --git a/rigify/rigs/spines/super_head.py b/rigify/rigs/spines/super_head.py
index e2932ad2..49f1ce3e 100644
--- a/rigify/rigs/spines/super_head.py
+++ b/rigify/rigs/spines/super_head.py
@@ -292,6 +292,8 @@ class Rig(BaseHeadTailRig):
influence=nfactor, space='LOCAL'
)
+ self.make_constraint(mch, 'COPY_SCALE', ctrl.neck)
+
####################################################
# Tweak bones
diff --git a/rigify/ui.py b/rigify/ui.py
index 68cfd330..8b719a3f 100644
--- a/rigify/ui.py
+++ b/rigify/ui.py
@@ -137,10 +137,20 @@ class DATA_PT_rigify_advanced(bpy.types.Panel):
armature_id_store = context.object.data
col = layout.column()
- col.row().prop(armature_id_store, "rigify_target_rig", text="Target Rig")
- col.row().prop(armature_id_store, "rigify_rig_ui", text="Rig UI Script")
+
+ row = col.row()
+ row.active = not armature_id_store.rigify_target_rig
+ row.prop(armature_id_store, "rigify_rig_basename", text="Rig Name")
+
+ col.separator()
+
+ col2 = col.box().column()
+ col2.label(text="Overwrite Existing:")
+ col2.row().prop(armature_id_store, "rigify_target_rig", text="Target Rig")
+ col2.row().prop(armature_id_store, "rigify_rig_ui", text="Rig UI Script")
+ col2.row().prop(armature_id_store, "rigify_widgets_collection")
+
col.separator()
- col.row().prop(armature_id_store, "rigify_widgets_collection")
col.row().prop(armature_id_store, "rigify_force_widget_update")
col.row().prop(armature_id_store, "rigify_mirror_widgets")
col.separator()
diff --git a/rigify/utils/collections.py b/rigify/utils/collections.py
index 8a299a9b..9eeaac51 100644
--- a/rigify/utils/collections.py
+++ b/rigify/utils/collections.py
@@ -53,7 +53,7 @@ def ensure_collection(context, collection_name, hidden=False) -> bpy.types.Colle
active_collection = active_layer_coll.collection
collection = bpy.data.collections.get(collection_name)
- if not collection:
+ if not collection or collection.library:
# Create the collection
collection = bpy.data.collections.new(collection_name)
collection.hide_viewport = hidden
diff --git a/rigify/utils/node_merger.py b/rigify/utils/node_merger.py
index 617b99df..2da48ada 100644
--- a/rigify/utils/node_merger.py
+++ b/rigify/utils/node_merger.py
@@ -73,7 +73,9 @@ class NodeMerger(GeneratorPlugin):
while pending:
added = set()
for j in pending:
- for co, idx, dist in tree.find_range(nodes[j].point, self.epsilon):
+ point = nodes[j].point
+ eps = max(1, point.length) * self.epsilon
+ for co, idx, dist in tree.find_range(point, eps):
added.add(idx)
pending = added.difference(merge_set)
merge_set.update(added)
diff --git a/rigify/utils/widgets.py b/rigify/utils/widgets.py
index e02f3387..5a16065b 100644
--- a/rigify/utils/widgets.py
+++ b/rigify/utils/widgets.py
@@ -86,6 +86,9 @@ def create_widget(rig, bone_name, bone_transform_name=None, *, widget_name=None,
if not obj:
# Search the scene by name
obj = scene.objects.get(obj_name)
+ if obj and obj.library:
+ local_objs = [obj for obj in scene.objects if obj.name == obj_name and not obj.library]
+ obj = local_objs[0] if local_objs else None
if obj:
# Record the generated widget
diff --git a/space_view3d_math_vis/__init__.py b/space_view3d_math_vis/__init__.py
index b6aac3cd..ac05ffc6 100644
--- a/space_view3d_math_vis/__init__.py
+++ b/space_view3d_math_vis/__init__.py
@@ -3,8 +3,8 @@
bl_info = {
"name": "Math Vis (Console)",
"author": "Campbell Barton",
- "version": (0, 2, 1),
- "blender": (2, 80, 0),
+ "version": (0, 2, 2),
+ "blender": (3, 0, 0),
"location": "Properties: Scene > Math Vis Console and Python Console: Menu",
"description": "Display console defined mathutils variables in the 3D view",
"doc_url": "{BLENDER_MANUAL_URL}/addons/3d_view/math_vis_console.html",
diff --git a/space_view3d_math_vis/draw.py b/space_view3d_math_vis/draw.py
index 28722a99..0b7084eb 100644
--- a/space_view3d_math_vis/draw.py
+++ b/space_view3d_math_vis/draw.py
@@ -3,7 +3,6 @@
import bpy
import blf
import gpu
-import bgl
from gpu_extras.batch import batch_for_shader
from . import utils
@@ -29,7 +28,7 @@ COLOR_BOUNDING_BOX_ACTIVE = (1.0, 0.5, 0.0, 1.0)
def tag_redraw_areas():
context = bpy.context
- # Py cant access notifers
+ # Py can't access notifers
for window in context.window_manager.windows:
for area in window.screen.areas:
if area.type in ['VIEW_3D', 'PROPERTIES']:
@@ -126,9 +125,9 @@ def draw_callback_view():
with_bounding_box = not settings.bbox_hide
if settings.in_front:
- bgl.glDepthFunc(bgl.GL_ALWAYS)
+ gpu.state.depth_test_set('ALWAYS')
else:
- bgl.glDepthFunc(bgl.GL_LESS)
+ gpu.state.depth_test_set('LESS')
data_matrix, data_quat, data_euler, data_vector, data_vector_array = utils.console_math_data()
if settings.index in range(0,len(prop_states)):
diff --git a/sun_position/__init__.py b/sun_position/__init__.py
index 01e22df0..5b2a89ce 100644
--- a/sun_position/__init__.py
+++ b/sun_position/__init__.py
@@ -29,39 +29,32 @@ if "bpy" in locals():
importlib.reload(properties)
importlib.reload(ui_sun)
importlib.reload(hdr)
+ importlib.reload(translations)
else:
- from . import properties, ui_sun, hdr
+ from . import properties, ui_sun, hdr, translations
import bpy
+register_classes, unregister_classes = bpy.utils.register_classes_factory(
+ (properties.SunPosProperties,
+ properties.SunPosAddonPreferences, ui_sun.SUNPOS_OT_AddPreset,
+ ui_sun.SUNPOS_MT_Presets, ui_sun.SUNPOS_PT_Panel,
+ ui_sun.SUNPOS_PT_Location, ui_sun.SUNPOS_PT_Time, hdr.SUNPOS_OT_ShowHdr))
+
+
def register():
- bpy.utils.register_class(properties.SunPosProperties)
+ register_classes()
bpy.types.Scene.sun_pos_properties = (
bpy.props.PointerProperty(type=properties.SunPosProperties,
- name="Sun Position",
- description="Sun Position Settings"))
- bpy.utils.register_class(properties.SunPosAddonPreferences)
- bpy.utils.register_class(ui_sun.SUNPOS_OT_AddPreset)
- bpy.utils.register_class(ui_sun.SUNPOS_MT_Presets)
- bpy.utils.register_class(ui_sun.SUNPOS_PT_Panel)
- bpy.utils.register_class(ui_sun.SUNPOS_PT_Location)
- bpy.utils.register_class(ui_sun.SUNPOS_PT_Time)
- bpy.utils.register_class(hdr.SUNPOS_OT_ShowHdr)
-
+ name="Sun Position",
+ description="Sun Position Settings"))
bpy.app.handlers.frame_change_post.append(sun_calc.sun_handler)
-
+ bpy.app.translations.register(__name__, translations.translations_dict)
def unregister():
- bpy.utils.unregister_class(hdr.SUNPOS_OT_ShowHdr)
- bpy.utils.unregister_class(ui_sun.SUNPOS_PT_Panel)
- bpy.utils.unregister_class(ui_sun.SUNPOS_PT_Location)
- bpy.utils.unregister_class(ui_sun.SUNPOS_PT_Time)
- bpy.utils.unregister_class(ui_sun.SUNPOS_MT_Presets)
- bpy.utils.unregister_class(ui_sun.SUNPOS_OT_AddPreset)
- bpy.utils.unregister_class(properties.SunPosAddonPreferences)
- del bpy.types.Scene.sun_pos_properties
- bpy.utils.unregister_class(properties.SunPosProperties)
-
+ bpy.app.translations.unregister(__name__)
bpy.app.handlers.frame_change_post.remove(sun_calc.sun_handler)
+ del bpy.types.Scene.sun_pos_properties
+ unregister_classes()
diff --git a/sun_position/properties.py b/sun_position/properties.py
index ef3a21e3..af2734a4 100644
--- a/sun_position/properties.py
+++ b/sun_position/properties.py
@@ -136,7 +136,7 @@ class SunPosProperties(PropertyGroup):
object_collection_type: EnumProperty(
name="Display type",
- description="Show object group as sun motion",
+ description="Show object collection as sun motion",
items=(
('ANALEMMA', "Analemma", ""),
('DIURNAL', "Diurnal", ""),
@@ -174,6 +174,7 @@ class SunPosProperties(PropertyGroup):
update=sun_update)
bind_to_sun: BoolProperty(
+ name="Bind Texture to Sun",
description="If true, Environment texture moves with sun",
default=False,
update=sun_update)
diff --git a/sun_position/translations.py b/sun_position/translations.py
new file mode 100644
index 00000000..c50b797c
--- /dev/null
+++ b/sun_position/translations.py
@@ -0,0 +1,493 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# ##### BEGIN AUTOGENERATED I18N SECTION #####
+# NOTE: You can safely move around this auto-generated block (with the begin/end markers!),
+# and edit the translations by hand.
+# Just carefully respect the format of the tuple!
+
+# Tuple of tuples:
+# ((msgctxt, msgid), (sources, gen_comments), (lang, translation, (is_fuzzy, comments)), ...)
+translations_tuple = (
+ (("*", ""),
+ ((), ()),
+ ("fr_FR", "Project-Id-Version: Sun Position 3.1.2 (0)\n",
+ (False,
+ ("Blender's translation file (po format).",
+ "Copyright (C) 2022 The Blender Foundation.",
+ "This file is distributed under the same license as the Blender package.",
+ "Damien Picard <dam.pic@free.fr>, 2022."))),
+ ),
+ (("*", "Azimuth and elevation info"),
+ (("bpy.types.SunPosAddonPreferences.show_az_el",),
+ ()),
+ ("fr_FR", "Infos d’azimut et de hauteur",
+ (False, ())),
+ ),
+ (("*", "Show azimuth and solar elevation info"),
+ (("bpy.types.SunPosAddonPreferences.show_az_el",),
+ ()),
+ ("fr_FR", "Afficher les infos d’azimut et de hauteur du soleil",
+ (False, ())),
+ ),
+ (("*", "Daylight savings"),
+ (("bpy.types.SunPosAddonPreferences.show_daylight_savings",
+ "bpy.types.SunPosProperties.use_daylight_savings"),
+ ()),
+ ("fr_FR", "Heure d’été",
+ (False, ())),
+ ),
+ (("*", "Show daylight savings time choice"),
+ (("bpy.types.SunPosAddonPreferences.show_daylight_savings",),
+ ()),
+ ("fr_FR", "Afficher l’option de changement d’heure",
+ (False, ())),
+ ),
+ (("*", "D° M' S\""),
+ (("bpy.types.SunPosAddonPreferences.show_dms",),
+ ()),
+ ("fr_FR", "",
+ (False, ())),
+ ),
+ (("*", "Show lat/long degrees, minutes, seconds labels"),
+ (("bpy.types.SunPosAddonPreferences.show_dms",),
+ ()),
+ ("fr_FR", "Afficher les étiquettes de latitude et longitude en degrés, minutes, secondes",
+ (False, ())),
+ ),
+ (("*", "Show North"),
+ (("bpy.types.SunPosAddonPreferences.show_north",
+ "bpy.types.SunPosProperties.show_north"),
+ ()),
+ ("fr_FR", "Afficher le nord",
+ (False, ())),
+ ),
+ (("*", "Show north offset choice and slider"),
+ (("bpy.types.SunPosAddonPreferences.show_north",),
+ ()),
+ ("fr_FR", "Afficher l’option et le curseur de décalage du nord",
+ (False, ())),
+ ),
+ (("*", "Refraction"),
+ (("bpy.types.SunPosAddonPreferences.show_refraction",),
+ ()),
+ ("fr_FR", "Réfraction",
+ (False, ())),
+ ),
+ (("*", "Show sun refraction choice"),
+ (("bpy.types.SunPosAddonPreferences.show_refraction",),
+ ()),
+ ("fr_FR", "Afficher l’option de réfraction du soleil",
+ (False, ())),
+ ),
+ (("*", "Sunrise and sunset info"),
+ (("bpy.types.SunPosAddonPreferences.show_rise_set",),
+ ()),
+ ("fr_FR", "Infos de lever et coucher",
+ (False, ())),
+ ),
+ (("*", "Show sunrise and sunset labels"),
+ (("bpy.types.SunPosAddonPreferences.show_rise_set",),
+ ()),
+ ("fr_FR", "Afficher les informations de lever et coucher du soleil",
+ (False, ())),
+ ),
+ (("*", "Time and place presets"),
+ (("bpy.types.SunPosAddonPreferences.show_time_place",),
+ ()),
+ ("fr_FR", "Préréglages d’heure et de lieu",
+ (False, ())),
+ ),
+ (("*", "Show time/place presets"),
+ (("bpy.types.SunPosAddonPreferences.show_time_place",),
+ ()),
+ ("fr_FR", "Afficher les préréglages d’heure et de lieu",
+ (False, ())),
+ ),
+ (("*", "Sun Position"),
+ (("bpy.types.Scene.sun_pos_properties",
+ "bpy.types.SUNPOS_PT_Panel",
+ "Add-on Sun Position info: name"),
+ ()),
+ ("fr_FR", "Position du Soleil",
+ (False, ())),
+ ),
+ (("*", "Sun Position Settings"),
+ (("bpy.types.Scene.sun_pos_properties",),
+ ()),
+ ("fr_FR", "Options de Position du Soleil",
+ (False, ())),
+ ),
+ (("*", "Sun Position Presets"),
+ (("bpy.types.SUNPOS_MT_Presets",),
+ ()),
+ ("fr_FR", "Préréglages de position du Soleil",
+ (False, ())),
+ ),
+ (("Operator", "Sync Sun to Texture"),
+ (("bpy.types.WORLD_OT_sunpos_show_hdr",),
+ ()),
+ ("fr_FR", "Synchroniser Soleil et texture",
+ (False, ())),
+ ),
+ (("*", "UTC zone"),
+ (("bpy.types.SunPosProperties.UTC_zone",),
+ ()),
+ ("fr_FR", "Fuseau horaire",
+ (False, ())),
+ ),
+ (("*", "Time zone: Difference from Greenwich, England in hours"),
+ (("bpy.types.SunPosProperties.UTC_zone",),
+ ()),
+ ("fr_FR", "Fuseau horaire : différence avec Greenwich, Angleterre, en heures",
+ (False, ())),
+ ),
+ (("*", "Bind Texture to Sun"),
+ (("bpy.types.SunPosProperties.bind_to_sun",
+ "scripts/addons/sun_position/ui_sun.py:119"),
+ ()),
+ ("fr_FR", "Lier la texture au Soleil",
+ (False, ())),
+ ),
+ (("*", "If true, Environment texture moves with sun"),
+ (("bpy.types.SunPosProperties.bind_to_sun",),
+ ()),
+ ("fr_FR", "Si actif, la texture d’environnement tourne avec le Soleil",
+ (False, ())),
+ ),
+ (("*", "Enter coordinates"),
+ (("bpy.types.SunPosProperties.co_parser",),
+ ()),
+ ("fr_FR", "Saisir coordonnées",
+ (False, ())),
+ ),
+ (("*", "Enter coordinates from an online map"),
+ (("bpy.types.SunPosProperties.co_parser",),
+ ()),
+ ("fr_FR", "Saisir des coordonnées depuis une carte",
+ (False, ())),
+ ),
+ (("*", "Day"),
+ (("bpy.types.SunPosProperties.day",),
+ ()),
+ ("fr_FR", "Jour",
+ (False, ())),
+ ),
+ (("*", "Day of year"),
+ (("bpy.types.SunPosProperties.day_of_year",),
+ ()),
+ ("fr_FR", "Jour de l’année",
+ (False, ())),
+ ),
+ (("*", "Rotation angle of sun and environment texture"),
+ (("bpy.types.SunPosProperties.hdr_azimuth",),
+ ()),
+ ("fr_FR", "Angle de rotation du Soleil et de la texture d’environnement",
+ (False, ())),
+ ),
+ (("*", "Elevation"),
+ (("bpy.types.SunPosProperties.hdr_elevation",),
+ ()),
+ ("fr_FR", "Hauteur",
+ (False, ())),
+ ),
+ (("*", "Elevation angle of sun"),
+ (("bpy.types.SunPosProperties.hdr_elevation",),
+ ()),
+ ("fr_FR", "Angle de hauteur du Soleil",
+ (False, ())),
+ ),
+ (("*", "Name of texture to use. World nodes must be enabled and color set to Environment Texture"),
+ (("bpy.types.SunPosProperties.hdr_texture",),
+ ()),
+ ("fr_FR", "Nom de la texture à utiliser. Les nœuds de shader du monde doivent être activés, et la couleur utiliser une texture d’environnement",
+ (False, ())),
+ ),
+ (("*", "Latitude"),
+ (("bpy.types.SunPosProperties.latitude",),
+ ()),
+ ("fr_FR", "Latitude",
+ (False, ())),
+ ),
+ (("*", "Latitude: (+) Northern (-) Southern"),
+ (("bpy.types.SunPosProperties.latitude",),
+ ()),
+ ("fr_FR", "Latitude : (+) nord (-) sud",
+ (False, ())),
+ ),
+ (("*", "Longitude"),
+ (("bpy.types.SunPosProperties.longitude",),
+ ()),
+ ("fr_FR", "Longitude",
+ (False, ())),
+ ),
+ (("*", "Longitude: (-) West of Greenwich (+) East of Greenwich"),
+ (("bpy.types.SunPosProperties.longitude",),
+ ()),
+ ("fr_FR", "Longitude : (-) ouest depuis Greenwich (+) est depuis Greenwich",
+ (False, ())),
+ ),
+ (("*", "Month"),
+ (("bpy.types.SunPosProperties.month",),
+ ()),
+ ("fr_FR", "Mois",
+ (False, ())),
+ ),
+ (("*", "North Offset"),
+ (("bpy.types.SunPosProperties.north_offset",),
+ ()),
+ ("fr_FR", "Décalage du nord",
+ (False, ())),
+ ),
+ (("*", "Rotate the scene to choose North direction"),
+ (("bpy.types.SunPosProperties.north_offset",),
+ ()),
+ ("fr_FR", "Tourner la scène pour choisir la direction du nord",
+ (False, ())),
+ ),
+ (("*", "Collection of objects used to visualize sun motion"),
+ (("bpy.types.SunPosProperties.object_collection",),
+ ()),
+ ("fr_FR", "Collection d’objets utilisée pour visualiser la trajectoire du Soleil",
+ (False, ())),
+ ),
+ (("*", "Show object collection as sun motion"),
+ (("bpy.types.SunPosProperties.object_collection_type",),
+ ()),
+ ("fr_FR", "Afficher la collection en tant que",
+ (False, ())),
+ ),
+ (("*", "Analemma"),
+ (("bpy.types.SunPosProperties.object_collection_type:'ANALEMMA'",),
+ ()),
+ ("fr_FR", "Analemme",
+ (False, ())),
+ ),
+ (("*", "Diurnal"),
+ (("bpy.types.SunPosProperties.object_collection_type:'DIURNAL'",),
+ ()),
+ ("fr_FR", "Diurne",
+ (False, ())),
+ ),
+ (("*", "Draw line pointing north"),
+ (("bpy.types.SunPosProperties.show_north",),
+ ()),
+ ("fr_FR", "Afficher une ligne pointant le nord",
+ (False, ())),
+ ),
+ (("*", "Name of sky texture to be used"),
+ (("bpy.types.SunPosProperties.sky_texture",),
+ ()),
+ ("fr_FR", "Nom de la texture à utiliser",
+ (False, ())),
+ ),
+ (("*", "Distance to sun from origin"),
+ (("bpy.types.SunPosProperties.sun_distance",),
+ ()),
+ ("fr_FR", "Distance entre l’origine et le Soleil",
+ (False, ())),
+ ),
+ (("*", "Sun Object"),
+ (("bpy.types.SunPosProperties.sun_object",
+ "scripts/addons/sun_position/ui_sun.py:101"),
+ ()),
+ ("fr_FR", "Objet soleil",
+ (False, ())),
+ ),
+ (("*", "Sun object to set in the scene"),
+ (("bpy.types.SunPosProperties.sun_object",),
+ ()),
+ ("fr_FR", "Objet soleil à utiliser dans la scène",
+ (False, ())),
+ ),
+ (("*", "Time of the day"),
+ (("bpy.types.SunPosProperties.time",),
+ ()),
+ ("fr_FR", "Heure du jour",
+ (False, ())),
+ ),
+ (("*", "Time Spread"),
+ (("bpy.types.SunPosProperties.time_spread",),
+ ()),
+ ("fr_FR", "Plage horaire",
+ (False, ())),
+ ),
+ (("*", "Time period in which to spread object collection"),
+ (("bpy.types.SunPosProperties.time_spread",),
+ ()),
+ ("fr_FR", "Plage horaire à visualiser par les objets de la collection",
+ (False, ())),
+ ),
+ (("*", "Usage mode"),
+ (("bpy.types.SunPosProperties.usage_mode",),
+ ()),
+ ("fr_FR", "Mode",
+ (False, ())),
+ ),
+ (("*", "Operate in normal mode or environment texture mode"),
+ (("bpy.types.SunPosProperties.usage_mode",),
+ ()),
+ ("fr_FR", "Passer en mode normal ou texture d’environnement",
+ (False, ())),
+ ),
+ (("*", "Sun + HDR texture"),
+ (("bpy.types.SunPosProperties.usage_mode:'HDR'",),
+ ()),
+ ("fr_FR", "Soleil + texture HDRI",
+ (False, ())),
+ ),
+ (("*", "Use day of year"),
+ (("bpy.types.SunPosProperties.use_day_of_year",),
+ ()),
+ ("fr_FR", "Utiliser le jour de l’année",
+ (False, ())),
+ ),
+ (("*", "Use a single value for day of year"),
+ (("bpy.types.SunPosProperties.use_day_of_year",),
+ ()),
+ ("fr_FR", "Utiliser une seule valeur pour le jour de l’année",
+ (False, ())),
+ ),
+ (("*", "Daylight savings time adds 1 hour to standard time"),
+ (("bpy.types.SunPosProperties.use_daylight_savings",),
+ ()),
+ ("fr_FR", "L’heure d’été ajoute une heure à l’heure standard",
+ (False, ())),
+ ),
+ (("*", "Use refraction"),
+ (("bpy.types.SunPosProperties.use_refraction",),
+ ()),
+ ("fr_FR", "Utiliser la réfraction",
+ (False, ())),
+ ),
+ (("*", "Show apparent sun position due to refraction"),
+ (("bpy.types.SunPosProperties.use_refraction",),
+ ()),
+ ("fr_FR", "Afficher la position apparente du Soleil due à la réfraction",
+ (False, ())),
+ ),
+ (("*", "Year"),
+ (("bpy.types.SunPosProperties.year",),
+ ()),
+ ("fr_FR", "Année",
+ (False, ())),
+ ),
+ (("*", "Could not find 3D View"),
+ (("scripts/addons/sun_position/hdr.py:262",),
+ ()),
+ ("fr_FR", "Impossible de trouver la vue 3D",
+ (False, ())),
+ ),
+ (("*", "Please select an Environment Texture node"),
+ (("scripts/addons/sun_position/hdr.py:268",),
+ ()),
+ ("fr_FR", "Veuillez utiliser un nœud de texture d’environnement",
+ (False, ())),
+ ),
+ (("*", "Unknown projection"),
+ (("scripts/addons/sun_position/hdr.py:180",),
+ ()),
+ ("fr_FR", "Projection inconnue",
+ (False, ())),
+ ),
+ (("*", "Show options or labels:"),
+ (("scripts/addons/sun_position/properties.py:242",),
+ ()),
+ ("fr_FR", "Afficher les options et étiquettes :",
+ (False, ())),
+ ),
+ (("*", "Usage Mode"),
+ (("scripts/addons/sun_position/ui_sun.py:71",),
+ ()),
+ ("fr_FR", "Mode",
+ (False, ())),
+ ),
+ (("*", "Environment Texture"),
+ (("scripts/addons/sun_position/ui_sun.py:85",),
+ ()),
+ ("fr_FR", "Texture d’environnement",
+ (False, ())),
+ ),
+ (("*", "Enter Coordinates"),
+ (("scripts/addons/sun_position/ui_sun.py:174",),
+ ()),
+ ("fr_FR", "Saisir coordonnées",
+ (False, ())),
+ ),
+ (("*", "Local:"),
+ (("scripts/addons/sun_position/ui_sun.py:269",),
+ ()),
+ ("fr_FR", "Locale :",
+ (False, ())),
+ ),
+ (("*", "UTC:"),
+ (("scripts/addons/sun_position/ui_sun.py:272",),
+ ()),
+ ("fr_FR", "UTC : ",
+ (False, ())),
+ ),
+ (("*", "Please select World in the World panel."),
+ (("scripts/addons/sun_position/ui_sun.py:95",
+ "scripts/addons/sun_position/ui_sun.py:153"),
+ ()),
+ ("fr_FR", "Veuillez sélectionner le monde dans le panneau Monde",
+ (False, ())),
+ ),
+ (("*", "Release binding"),
+ (("scripts/addons/sun_position/ui_sun.py:116",),
+ ()),
+ ("fr_FR", "Annuler le lien",
+ (False, ())),
+ ),
+ (("*", "Azimuth:"),
+ (("scripts/addons/sun_position/ui_sun.py:205",),
+ ()),
+ ("fr_FR", "Azimut :",
+ (False, ())),
+ ),
+ (("*", "Elevation:"),
+ (("scripts/addons/sun_position/ui_sun.py:208",),
+ ()),
+ ("fr_FR", "Hauteur :",
+ (False, ())),
+ ),
+ (("*", "Sunrise:"),
+ (("scripts/addons/sun_position/ui_sun.py:284",),
+ ()),
+ ("fr_FR", "Lever : ",
+ (False, ())),
+ ),
+ (("*", "Sunset:"),
+ (("scripts/addons/sun_position/ui_sun.py:287",),
+ ()),
+ ("fr_FR", "Coucher : ",
+ (False, ())),
+ ),
+ (("*", "Please activate Use Nodes in the World panel."),
+ (("scripts/addons/sun_position/ui_sun.py:92",
+ "scripts/addons/sun_position/ui_sun.py:150"),
+ ()),
+ ("fr_FR", "Veuillez activer Utiliser nœuds dans le panneau Monde",
+ (False, ())),
+ ),
+ (("*", "World > Sun Position"),
+ (("Add-on Sun Position info: location",),
+ ()),
+ ("fr_FR", "Monde > Position du Soleil",
+ (False, ())),
+ ),
+ (("*", "Show sun position with objects and/or sky texture"),
+ (("Add-on Sun Position info: description",),
+ ()),
+ ("fr_FR", "Afficher la position du Soleil avec des objets ou une texture de ciel",
+ (False, ())),
+ ),
+)
+
+translations_dict = {}
+for msg in translations_tuple:
+ key = msg[0]
+ for lang, trans, (is_fuzzy, comments) in msg[2:]:
+ if trans and not is_fuzzy:
+ translations_dict.setdefault(lang, {})[key] = trans
+
+# ##### END AUTOGENERATED I18N SECTION #####
diff --git a/sun_position/ui_sun.py b/sun_position/ui_sun.py
index 1f4f8f34..c6eebc33 100644
--- a/sun_position/ui_sun.py
+++ b/sun_position/ui_sun.py
@@ -112,11 +112,11 @@ class SUNPOS_PT_Panel(bpy.types.Panel):
col = flow.column(align=True)
if sp.bind_to_sun:
- prop_text="Release binding"
+ col.prop(sp, "bind_to_sun", toggle=True, icon="CONSTRAINT",
+ text="Release binding")
else:
- prop_text="Bind Texture to Sun "
- col.prop(sp, "bind_to_sun", toggle=True, icon="CONSTRAINT",
- text=prop_text)
+ col.prop(sp, "bind_to_sun", toggle=True, icon="CONSTRAINT",
+ text="Bind Texture to Sun")
row = col.row(align=True)
row.enabled = not sp.bind_to_sun
@@ -201,14 +201,12 @@ class SUNPOS_PT_Location(bpy.types.Panel):
if p.show_az_el:
col = flow.column(align=True)
- row = col.row()
- row.alignment = 'RIGHT'
- row.label(text="Azimuth: " +
- str(round(sun.azimuth, 3)) + "°")
- row = col.row()
- row.alignment = 'RIGHT'
- row.label(text="Elevation: " +
- str(round(sun.elevation, 3)) + "°")
+ split = col.split(factor=0.4, align=True)
+ split.label(text="Azimuth:")
+ split.label(text=str(round(sun.azimuth, 3)) + "°")
+ split = col.split(factor=0.4, align=True)
+ split.label(text="Elevation:")
+ split.label(text=str(round(sun.elevation, 3)) + "°")
col.separator()
if p.show_refraction:
@@ -266,16 +264,27 @@ class SUNPOS_PT_Time(bpy.types.Panel):
sp.longitude,
sp.UTC_zone)
col.alignment = 'CENTER'
- col.label(text="Local: " + lt, icon='TIME')
- col.label(text=" UTC: " + ut, icon='PREVIEW_RANGE')
+
+ split = col.split(factor=0.5, align=True)
+ split.label(text="Local:", icon='TIME')
+ split.label(text=lt)
+ split = col.split(factor=0.5, align=True)
+ split.label(text="UTC:", icon='PREVIEW_RANGE')
+ split.label(text=ut)
col.separator()
+
col = flow.column(align=True)
col.alignment = 'CENTER'
if p.show_rise_set:
sr = format_hms(sun.sunrise.time)
ss = format_hms(sun.sunset.time)
- tsr = "Sunrise: " + sr
- tss = " Sunset: " + ss
- col.label(text=tsr, icon='LIGHT_SUN')
- col.label(text=tss, icon='SOLO_ON')
+
+ split = col.split(factor=0.5, align=True)
+ split.label(text="Sunrise:", icon='LIGHT_SUN')
+ split.label(text=sr)
+ split = col.split(factor=0.5, align=True)
+ split.label(text="Sunset:", icon='SOLO_ON')
+ split.label(text=ss)
+
+ col.separator()
diff --git a/system_demo_mode/__init__.py b/system_demo_mode/__init__.py
index 9af8b217..71870532 100644
--- a/system_demo_mode/__init__.py
+++ b/system_demo_mode/__init__.py
@@ -190,7 +190,7 @@ class DemoModeRun(bpy.types.Operator):
if extern_demo_mode_run():
return {'FINISHED'}
else:
- self.report({'ERROR'}, "Cant load demo.py config, run: File -> Demo Mode (Setup)")
+ self.report({'ERROR'}, "Can't load demo.py config, run: File -> Demo Mode (Setup)")
return {'CANCELLED'}
diff --git a/system_demo_mode/config.py b/system_demo_mode/config.py
index 3c0550ee..5d939959 100644
--- a/system_demo_mode/config.py
+++ b/system_demo_mode/config.py
@@ -40,7 +40,7 @@ def as_string(dirpath, random_order, exit, **kwargs):
"# generated file\n",
"\n",
"# edit the search path so other systems may find the files below\n",
- "# based on name only if the absolute paths cant be found\n",
+ "# based on name only if the absolute paths cannot be found\n",
"# Use '//' for current blend file path.\n",
"\n",
"search_path = %r\n" % dirpath,
diff --git a/system_demo_mode/demo_mode.py b/system_demo_mode/demo_mode.py
index 4def3031..f412869b 100644
--- a/system_demo_mode/demo_mode.py
+++ b/system_demo_mode/demo_mode.py
@@ -523,7 +523,7 @@ def load_config(cfg_name=DEMO_CFG):
if not os.path.exists(filepath_test):
filepath_test = lookup_file(filepath_test) # attempt to get from searchpath
if not os.path.exists(filepath_test):
- print("Cant find %r or %r, skipping!")
+ print("Can't find %r or %r, skipping!")
continue
filecfg["file"] = os.path.normpath(filepath_test)
diff --git a/system_property_chart.py b/system_property_chart.py
index b1fa54c2..bb6d4682 100644
--- a/system_property_chart.py
+++ b/system_property_chart.py
@@ -219,16 +219,26 @@ def _property_chart_copy(self, context):
data_path = self.data_path
- # quick & nasty method!
+ data_path_with_dot = data_path
+ if not data_path_with_dot.startswith("["):
+ data_path_with_dot = "." + data_path_with_dot
+
+ code = compile(
+ "obj_iter%s = obj%s" % (data_path_with_dot, data_path_with_dot),
+ "<property_chart>",
+ 'exec',
+ )
+
for obj_iter in selected_objects:
if obj != obj_iter:
try:
- exec("obj_iter.%s = obj.%s" % (data_path, data_path))
+ exec(code, {}, {"obj": obj, "obj_iter": obj_iter})
except:
# just in case we need to know what went wrong!
import traceback
traceback.print_exc()
+
from bpy.props import StringProperty
@@ -252,6 +262,7 @@ class CopyPropertyChart(Operator):
# List The Classes #
+
classes = (
AddPresetProperties,
SCENE_MT_properties_presets,
@@ -260,6 +271,7 @@ classes = (
CopyPropertyChart
)
+
def register():
for cls in classes:
bpy.utils.register_class(cls)
diff --git a/viewport_vr_preview/__init__.py b/viewport_vr_preview/__init__.py
index eb7dca4d..cc4580ae 100644
--- a/viewport_vr_preview/__init__.py
+++ b/viewport_vr_preview/__init__.py
@@ -3,7 +3,7 @@
bl_info = {
"name": "VR Scene Inspection",
"author": "Julian Eisel (Severin), Sebastian Koenig, Peter Kim (muxed-reality)",
- "version": (0, 11, 0),
+ "version": (0, 11, 1),
"blender": (3, 2, 0),
"location": "3D View > Sidebar > VR",
"description": ("View the viewport with virtual reality glasses "
diff --git a/viewport_vr_preview/operators.py b/viewport_vr_preview/operators.py
index 735d2bd9..8a0e1a2a 100644
--- a/viewport_vr_preview/operators.py
+++ b/viewport_vr_preview/operators.py
@@ -15,7 +15,6 @@ from bpy.types import (
Operator,
)
from bpy_extras.io_utils import ExportHelper, ImportHelper
-import bgl
import math
from math import radians
from mathutils import Euler, Matrix, Quaternion, Vector
@@ -962,8 +961,8 @@ class VIEW3D_GT_vr_camera_cone(Gizmo):
'LINES', lines_shape_verts)
# Ensure correct GL state (otherwise other gizmos might mess that up)
- bgl.glLineWidth(1)
- bgl.glEnable(bgl.GL_BLEND)
+ gpu.state.line_width_set(1.0)
+ gpu.state.blend_set('ALPHA')
self.draw_custom_shape(self.frame_shape)
self.draw_custom_shape(self.lines_shape)
@@ -973,8 +972,8 @@ class VIEW3D_GT_vr_controller_grip(Gizmo):
bl_idname = "VIEW_3D_GT_vr_controller_grip"
def draw(self, context):
- bgl.glLineWidth(1)
- bgl.glEnable(bgl.GL_BLEND)
+ gpu.state.line_width_set(1.0)
+ gpu.state.blend_set('ALPHA')
self.color = 0.422, 0.438, 0.446
self.draw_preset_circle(self.matrix_basis, axis='POS_X')
@@ -986,8 +985,8 @@ class VIEW3D_GT_vr_controller_aim(Gizmo):
bl_idname = "VIEW_3D_GT_vr_controller_aim"
def draw(self, context):
- bgl.glLineWidth(1)
- bgl.glEnable(bgl.GL_BLEND)
+ gpu.state.line_width_set(1.0)
+ gpu.state.blend_set('ALPHA')
self.color = 1.0, 0.2, 0.322
self.draw_preset_arrow(self.matrix_basis, axis='POS_X')