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:
-rw-r--r--add_advanced_objects_menu/__init__.py363
-rw-r--r--add_advanced_objects_menu/cubester.py111
-rw-r--r--add_curve_ivygen.py398
-rw-r--r--ant_landscape/add_mesh_ant_landscape.py8
-rw-r--r--ant_landscape/ant_functions.py5
-rw-r--r--archipack/archipack_material.py34
-rw-r--r--bone_selection_sets.py260
-rw-r--r--camera_turnaround.py10
-rw-r--r--depsgraph_debug.py4
-rw-r--r--development_iskeyfree.py545
-rw-r--r--development_ui_classes.py35
-rw-r--r--io_export_after_effects.py39
-rw-r--r--io_scene_fbx/__init__.py2
-rw-r--r--io_scene_fbx/export_fbx_bin.py30
-rw-r--r--io_scene_fbx/fbx_utils.py2
-rw-r--r--io_scene_fbx/import_fbx.py72
-rwxr-xr-xio_scene_fbx/json2fbx.py4
-rw-r--r--io_scene_obj/import_obj.py2
-rw-r--r--materials_library_vx/__init__.py37
-rw-r--r--materials_utils/__init__.py1429
-rw-r--r--materials_utils/material_converter.py19
-rw-r--r--materials_utils/materials_cycles_converter.py112
-rw-r--r--materials_utils/texture_rename.py76
-rw-r--r--materials_utils/warning_messages_utils.py36
-rw-r--r--measureit/__init__.py4
-rw-r--r--measureit/measureit_geometry.py10
-rw-r--r--measureit/measureit_main.py64
-rw-r--r--measureit/measureit_render.py12
-rw-r--r--mesh_carver.py1489
-rw-r--r--mesh_tiny_cad/BIX.py8
-rw-r--r--mesh_tiny_cad/CCEN.py8
-rw-r--r--mesh_tiny_cad/CFG.py8
-rw-r--r--mesh_tiny_cad/E2F.py8
-rw-r--r--mesh_tiny_cad/V2X.py8
-rw-r--r--mesh_tiny_cad/VTX.py8
-rw-r--r--mesh_tiny_cad/XALL.py8
-rw-r--r--mesh_tiny_cad/__init__.py17
-rw-r--r--modules/cycles_shader_compat.py3
-rw-r--r--modules/rna_manual_reference.py115
-rw-r--r--netrender/__init__.py6
-rw-r--r--node_wrangler.py2
-rw-r--r--object_boolean_tools.py47
-rw-r--r--object_print3d_utils/mesh_helpers.py16
-rw-r--r--object_print3d_utils/operators.py34
-rw-r--r--oscurart_tools/__init__.py3
-rw-r--r--oscurart_tools/oscurart_meshes.py86
-rw-r--r--oscurart_tools/oscurart_render.py33
-rw-r--r--paint_palette.py437
-rw-r--r--presets/pov/lamp/01_(5400K)_Direct_Sun.py10
-rw-r--r--presets/pov/lamp/02_(5400K)_High_Noon_Sun.py17
-rw-r--r--presets/pov/lamp/03_(6000K)_Daylight_Window.py13
-rw-r--r--presets/pov/lamp/04_(6000K)_2500W_HMI_(Halogen_Metal_Iodide).py14
-rw-r--r--presets/pov/lamp/05_(4000K)_100W_Metal_Halide.py14
-rw-r--r--presets/pov/lamp/06_(3200K)_100W_Quartz_Halogen.py16
-rw-r--r--presets/pov/lamp/07_(2850K)_100w_Tungsten.py10
-rw-r--r--presets/pov/lamp/08_(2600K)_40w_Tungsten.py10
-rw-r--r--presets/pov/lamp/09_(5000K)_75W_Full_Spectrum_Fluorescent_T12.py14
-rw-r--r--presets/pov/lamp/10_(4300K)_40W_Vintage_Fluorescent_T12.py14
-rw-r--r--presets/pov/lamp/11_(5000K)_18W_Standard_Fluorescent_T8.py13
-rw-r--r--presets/pov/lamp/12_(4200K)_18W_Cool_White_Fluorescent_T8.py15
-rw-r--r--presets/pov/lamp/13_(3000K)_18W_Warm_Fluorescent_T8.py14
-rw-r--r--presets/pov/lamp/14_(6500K)_54W_Grow_Light_Fluorescent_T5-HO.py13
-rw-r--r--presets/pov/lamp/15_(3200K)_40W_Induction_ Fluorescent.py14
-rw-r--r--presets/pov/lamp/16_(2100K)_150W_High_Pressure_Sodium.py13
-rw-r--r--presets/pov/lamp/17_(1700K)_135W_Low_Pressure_Sodium.py11
-rw-r--r--presets/pov/lamp/18_(6800K)_175W_Mercury_Vapor.py13
-rw-r--r--presets/pov/lamp/19_(5200K)_700W_Carbon_Arc.py18
-rw-r--r--presets/pov/lamp/20_(6500K)_15W_LED_Spot.py12
-rw-r--r--presets/pov/lamp/21_(2700K)_7W_OLED_Panel.py14
-rw-r--r--presets/pov/lamp/22_(30000K)_40W_Black_Light_Fluorescent.py12
-rw-r--r--presets/pov/lamp/23_(30000K)_40W_Black_Light_Bulb.py10
-rw-r--r--presets/pov/lamp/24_(1850K)_Candle.py24
-rw-r--r--presets/pov/radiosity/01_Debug.py20
-rw-r--r--presets/pov/radiosity/02_Fast.py19
-rw-r--r--presets/pov/radiosity/03_Normal.py20
-rw-r--r--presets/pov/radiosity/04_Two_Bounces.py20
-rw-r--r--presets/pov/radiosity/05_Final.py20
-rw-r--r--presets/pov/radiosity/06_Outdoor_Low_Quality.py20
-rw-r--r--presets/pov/radiosity/07_Outdoor_High_Quality.py20
-rw-r--r--presets/pov/radiosity/08_Outdoor_(Sun)Light.py20
-rw-r--r--presets/pov/radiosity/09_Indoor_Low_Quality.py20
-rw-r--r--presets/pov/radiosity/10_Indoor_High_Quality.py20
-rw-r--r--presets/pov/world/1_Clear_Blue_Sky.py36
-rw-r--r--presets/pov/world/2_Partly_Hazy_Sky.py36
-rw-r--r--presets/pov/world/3_Overcast_Sky.py20
-rw-r--r--presets/pov/world/4_Cartoony_Sky.py19
-rw-r--r--presets/pov/world/5_Under_Water.py19
-rw-r--r--render_povray/__init__.py110
-rw-r--r--render_povray/render.py177
-rw-r--r--render_povray/shading.py13
-rw-r--r--render_povray/ui.py197
-rw-r--r--space_view3d_stored_views/__init__.py45
-rw-r--r--space_view3d_stored_views/io.py78
-rw-r--r--space_view3d_stored_views/ui.py10
-rw-r--r--ui_layer_manager.py2
-rw-r--r--uv_magic_uv/__init__.py103
-rw-r--r--uv_magic_uv/common.py604
-rw-r--r--uv_magic_uv/muv_common.py83
-rw-r--r--uv_magic_uv/muv_cpuv_selseq_ops.py279
-rw-r--r--uv_magic_uv/muv_menu.py138
-rw-r--r--uv_magic_uv/muv_preferences.py144
-rw-r--r--uv_magic_uv/muv_props.py148
-rw-r--r--uv_magic_uv/op/__init__.py72
-rw-r--r--uv_magic_uv/op/align_uv.py757
-rw-r--r--uv_magic_uv/op/align_uv_cursor.py154
-rw-r--r--uv_magic_uv/op/copy_paste_uv.py (renamed from uv_magic_uv/muv_cpuv_ops.py)435
-rw-r--r--uv_magic_uv/op/copy_paste_uv_object.py252
-rw-r--r--uv_magic_uv/op/copy_paste_uv_uvedit.py144
-rw-r--r--uv_magic_uv/op/flip_rotate_uv.py (renamed from uv_magic_uv/muv_fliprot_ops.py)9
-rw-r--r--uv_magic_uv/op/mirror_uv.py (renamed from uv_magic_uv/muv_mirroruv_ops.py)9
-rw-r--r--uv_magic_uv/op/move_uv.py (renamed from uv_magic_uv/muv_mvuv_ops.py)13
-rw-r--r--uv_magic_uv/op/pack_uv.py (renamed from uv_magic_uv/muv_packuv_ops.py)119
-rw-r--r--uv_magic_uv/op/preserve_uv_aspect.py (renamed from uv_magic_uv/muv_preserve_uv_aspect.py)28
-rw-r--r--uv_magic_uv/op/smooth_uv.py215
-rw-r--r--uv_magic_uv/op/texture_lock.py (renamed from uv_magic_uv/muv_texlock_ops.py)32
-rw-r--r--uv_magic_uv/op/texture_projection.py (renamed from uv_magic_uv/muv_texproj_ops.py)67
-rw-r--r--uv_magic_uv/op/texture_wrap.py212
-rw-r--r--uv_magic_uv/op/transfer_uv.py (renamed from uv_magic_uv/muv_transuv_ops.py)17
-rw-r--r--uv_magic_uv/op/unwrap_constraint.py (renamed from uv_magic_uv/muv_unwrapconst_ops.py)16
-rw-r--r--uv_magic_uv/op/uv_bounding_box.py (renamed from uv_magic_uv/muv_uvbb_ops.py)65
-rw-r--r--uv_magic_uv/op/uv_inspection.py623
-rw-r--r--uv_magic_uv/op/uv_sculpt.py360
-rw-r--r--uv_magic_uv/op/uvw.py (renamed from uv_magic_uv/muv_uvw_ops.py)43
-rw-r--r--uv_magic_uv/op/world_scale_uv.py (renamed from uv_magic_uv/muv_wsuv_ops.py)130
-rw-r--r--uv_magic_uv/preferences.py216
-rw-r--r--uv_magic_uv/properites.py765
-rw-r--r--uv_magic_uv/ui/__init__.py44
-rw-r--r--uv_magic_uv/ui/uvedit_copy_paste_uv.py54
-rw-r--r--uv_magic_uv/ui/uvedit_editor_enhance.py136
-rw-r--r--uv_magic_uv/ui/uvedit_uv_manipulation.py117
-rw-r--r--uv_magic_uv/ui/view3d_copy_paste_uv_editmode.py81
-rw-r--r--uv_magic_uv/ui/view3d_copy_paste_uv_objectmode.py56
-rw-r--r--uv_magic_uv/ui/view3d_uv_manipulation.py180
-rw-r--r--uv_magic_uv/ui/view3d_uv_mapping.py99
134 files changed, 10157 insertions, 4218 deletions
diff --git a/add_advanced_objects_menu/__init__.py b/add_advanced_objects_menu/__init__.py
index 46a3dffc..42a33445 100644
--- a/add_advanced_objects_menu/__init__.py
+++ b/add_advanced_objects_menu/__init__.py
@@ -25,7 +25,7 @@
bl_info = {
"name": "Add Advanced Objects",
"author": "Meta Androcto",
- "version": (0, 1, 5),
+ "version": (0, 1, 6),
"blender": (2, 78, 0),
"location": "View3D > Add ",
"description": "Add Object & Camera extras",
@@ -55,7 +55,6 @@ if "bpy" in locals():
importlib.reload(arrange_on_curve)
importlib.reload(mesh_easylattice)
-
else:
from . import add_light_template
from . import scene_objects_bi
@@ -78,18 +77,18 @@ else:
import bpy
from bpy.types import (
- Menu,
- AddonPreferences,
- PropertyGroup,
- )
+ AddonPreferences,
+ Menu,
+ PropertyGroup,
+)
from bpy.props import (
- BoolProperty,
- EnumProperty,
- FloatProperty,
- IntProperty,
- StringProperty,
- PointerProperty,
- )
+ BoolProperty,
+ EnumProperty,
+ FloatProperty,
+ IntProperty,
+ StringProperty,
+ PointerProperty,
+)
# Define the "Scenes" menu
@@ -204,15 +203,15 @@ class AdvancedObjPreferences(AddonPreferences):
bl_idname = __name__
show_menu_list = BoolProperty(
- name="Menu List",
- description="Show/Hide the Add Menu items",
- default=False
- )
+ name="Menu List",
+ description="Show/Hide the Add Menu items",
+ default=False
+ )
show_panel_list = BoolProperty(
- name="Panels List",
- description="Show/Hide the Panel items",
- default=False
- )
+ name="Panels List",
+ description="Show/Hide the Panel items",
+ default=False
+ )
def draw(self, context):
layout = self.layout
@@ -315,214 +314,214 @@ class AdvancedObjProperties(PropertyGroup):
# cubester
# main properties
cubester_check_audio = BoolProperty(
- name="",
- default=False
- )
+ name="",
+ default=False
+ )
cubester_audio_image = EnumProperty(
- name="Input Type",
- items=(("image", "Image",
- "Use an Image as input for generating Geometry", "IMAGE_COL", 0),
- ("audio", "Audio",
- "Use a Sound Strip as input for generating Geometry", "FILE_SOUND", 1))
- )
+ name="Input Type",
+ items=(("image", "Image",
+ "Use an Image as input for generating Geometry", "IMAGE_COL", 0),
+ ("audio", "Audio",
+ "Use a Sound Strip as input for generating Geometry", "FILE_SOUND", 1))
+ )
cubester_audio_file_length = IntProperty(
- default=0
- )
+ default=0
+ )
# audio
cubester_audio_path = StringProperty(
- default="",
- name="Audio File",
- subtype="FILE_PATH",
- update=find_audio_length
- )
+ default="",
+ name="Audio File",
+ subtype="FILE_PATH",
+ update=find_audio_length
+ )
cubester_audio_min_freq = IntProperty(
- name="Minimum Frequency",
- min=20, max=100000,
- default=20
- )
+ name="Minimum Frequency",
+ min=20, max=100000,
+ default=20
+ )
cubester_audio_max_freq = IntProperty(
- name="Maximum Frequency",
- min=21, max=999999,
- default=5000
- )
+ name="Maximum Frequency",
+ min=21, max=999999,
+ default=5000
+ )
cubester_audio_offset_type = EnumProperty(
- name="Offset Type",
- items=(("freq", "Frequency Offset", ""),
- ("frame", "Frame Offset", "")),
- description="Type of offset per row of mesh"
- )
+ name="Offset Type",
+ items=(("freq", "Frequency Offset", ""),
+ ("frame", "Frame Offset", "")),
+ description="Type of offset per row of mesh"
+ )
cubester_audio_frame_offset = IntProperty(
- name="Frame Offset",
- min=0, max=10,
- default=2
- )
+ name="Frame Offset",
+ min=0, max=10,
+ default=2
+ )
cubester_audio_block_layout = EnumProperty(
- name="Block Layout",
- items=(("rectangle", "Rectangular", ""),
- ("radial", "Radial", ""))
- )
+ name="Block Layout",
+ items=(("rectangle", "Rectangular", ""),
+ ("radial", "Radial", ""))
+ )
cubester_audio_width_blocks = IntProperty(
- name="Width Block Count",
- min=1, max=10000,
- default=5
- )
+ name="Width Block Count",
+ min=1, max=10000,
+ default=5
+ )
cubester_audio_length_blocks = IntProperty(
- name="Length Block Count",
- min=1, max=10000,
- default=50
- )
+ name="Length Block Count",
+ min=1, max=10000,
+ default=50
+ )
# image
cubester_load_type = EnumProperty(
- name="Image Input Type",
- items=(("single", "Single Image", ""),
- ("multiple", "Image Sequence", ""))
- )
+ name="Image Input Type",
+ items=(("single", "Single Image", ""),
+ ("multiple", "Image Sequence", ""))
+ )
cubester_image = StringProperty(
- default="",
- name=""
- )
+ default="",
+ name=""
+ )
cubester_load_image = StringProperty(
- default="",
- name="Load Image",
- subtype="FILE_PATH",
- update=adjust_selected_image
- )
+ default="",
+ name="Load Image",
+ subtype="FILE_PATH",
+ update=adjust_selected_image
+ )
cubester_skip_images = IntProperty(
- name="Image Step",
- min=1, max=30,
- default=1,
- description="Step from image to image by this number"
- )
+ name="Image Step",
+ min=1, max=30,
+ default=1,
+ description="Step from image to image by this number"
+ )
cubester_max_images = IntProperty(
- name="Max Number Of Images",
- min=2, max=1000,
- default=10,
- description="Maximum number of images to be used"
- )
+ name="Max Number Of Images",
+ min=2, max=1000,
+ default=10,
+ description="Maximum number of images to be used"
+ )
cubester_frame_step = IntProperty(
- name="Frame Step Size",
- min=1, max=10,
- default=4,
- description="The number of frames each picture is used"
- )
+ name="Frame Step Size",
+ min=1, max=10,
+ default=4,
+ description="The number of frames each picture is used"
+ )
cubester_skip_pixels = IntProperty(
- name="Skip # Pixels",
- min=0, max=256,
- default=64,
- description="Skip this number of pixels before placing the next"
- )
+ name="Skip # Pixels",
+ min=0, max=256,
+ default=64,
+ description="Skip this number of pixels before placing the next"
+ )
cubester_mesh_style = EnumProperty(
- name="Mesh Type",
- items=(("blocks", "Blocks", ""),
- ("plane", "Plane", "")),
- description="Compose mesh of multiple blocks or of a single plane"
- )
+ name="Mesh Type",
+ items=(("blocks", "Blocks", ""),
+ ("plane", "Plane", "")),
+ description="Compose mesh of multiple blocks or of a single plane"
+ )
cubester_block_style = EnumProperty(
- name="Block Style",
- items=(("size", "Vary Size", ""),
- ("position", "Vary Position", "")),
- description="Vary Z-size of block, or vary Z-position"
- )
+ name="Block Style",
+ items=(("size", "Vary Size", ""),
+ ("position", "Vary Position", "")),
+ description="Vary Z-size of block, or vary Z-position"
+ )
cubester_height_scale = FloatProperty(
- name="Height Scale",
- subtype="DISTANCE",
- min=0.1, max=2,
- default=0.2
- )
+ name="Height Scale",
+ subtype="DISTANCE",
+ min=0.1, max=2,
+ default=0.2
+ )
cubester_invert = BoolProperty(
- name="Invert Height",
- default=False
- )
+ name="Invert Height",
+ default=False
+ )
# general adjustments
cubester_size_per_hundred_pixels = FloatProperty(
- name="Size Per 100 Blocks/Points",
- subtype="DISTANCE",
- min=0.001, max=5,
- default=1
- )
+ name="Size Per 100 Blocks/Points",
+ subtype="DISTANCE",
+ min=0.001, max=5,
+ default=1
+ )
# material based stuff
cubester_materials = EnumProperty(
- name="Material",
- items=(("vertex", "Vertex Colors", ""),
- ("image", "Image", "")),
- description="Color with vertex colors, or uv unwrap and use an image"
- )
+ name="Material",
+ items=(("vertex", "Vertex Colors", ""),
+ ("image", "Image", "")),
+ description="Color with vertex colors, or uv unwrap and use an image"
+ )
cubester_use_image_color = BoolProperty(
- name="Use Original Image Colors'?",
- default=True,
- description="Use original image colors, or replace with an another one"
- )
+ name="Use Original Image Colors'?",
+ default=True,
+ description="Use original image colors, or replace with an another one"
+ )
cubester_color_image = StringProperty(
- default="",
- name=""
- )
+ default="",
+ name=""
+ )
cubester_load_color_image = StringProperty(
- default="",
- name="Load Color Image",
- subtype="FILE_PATH",
- update=adjust_selected_color_image
- )
+ default="",
+ name="Load Color Image",
+ subtype="FILE_PATH",
+ update=adjust_selected_color_image
+ )
cubester_vertex_colors = {}
# advanced
cubester_advanced = BoolProperty(
- name="Advanced Options",
- default=False
- )
+ name="Advanced Options",
+ default=False
+ )
cubester_random_weights = BoolProperty(
- name="Random Weights",
- default=False
- )
+ name="Random Weights",
+ default=False
+ )
cubester_weight_r = FloatProperty(
- name="Red",
- subtype="FACTOR",
- min=0.01, max=1.0,
- default=0.25
- )
+ name="Red",
+ subtype="FACTOR",
+ min=0.01, max=1.0,
+ default=0.25
+ )
cubester_weight_g = FloatProperty(
- name="Green",
- subtype="FACTOR",
- min=0.01, max=1.0,
- default=0.25
- )
+ name="Green",
+ subtype="FACTOR",
+ min=0.01, max=1.0,
+ default=0.25
+ )
cubester_weight_b = FloatProperty(
- name="Blue",
- subtype="FACTOR",
- min=0.01, max=1.0,
- default=0.25
- )
+ name="Blue",
+ subtype="FACTOR",
+ min=0.01, max=1.0,
+ default=0.25
+ )
cubester_weight_a = FloatProperty(
- name="Alpha",
- subtype="FACTOR",
- min=0.01, max=1.0,
- default=0.25
- )
+ name="Alpha",
+ subtype="FACTOR",
+ min=0.01, max=1.0,
+ default=0.25
+ )
# arrange_on_curve
arrange_c_use_selected = BoolProperty(
- name="Use Selected",
- description="Use the selected objects to duplicate",
- default=True,
- )
+ name="Use Selected",
+ description="Use the selected objects to duplicate",
+ default=True,
+ )
arrange_c_obj_arranjar = StringProperty(
- name=""
- )
+ name=""
+ )
arrange_c_select_type = EnumProperty(
- name="Type",
- description="Select object or group",
- items=[
- ('O', "Object", "Make duplicates of a specific object"),
- ('G', "Group", "Make duplicates of the objects in a group"),
- ],
- default='O',
- )
+ name="Type",
+ description="Select object or group",
+ items=[
+ ('O', "Object", "Make duplicates of a specific object"),
+ ('G', "Group", "Make duplicates of the objects in a group"),
+ ],
+ default='O',
+ )
def register():
bpy.utils.register_module(__name__)
bpy.types.Scene.advanced_objects = PointerProperty(
- type=AdvancedObjProperties
- )
+ type=AdvancedObjProperties
+ )
# Add "Extras" menu to the "Add" menu
bpy.types.INFO_MT_add.append(menu)
@@ -543,10 +542,6 @@ def unregister():
bpy.utils.unregister_module(__name__)
del bpy.types.Scene.advanced_objects
- # cleanup Easy Lattice Scene Property if it was created
- if hasattr(bpy.types.Scene, "activelatticeobject"):
- del bpy.types.Scene.activelatticeobject
-
if __name__ == "__main__":
register()
diff --git a/add_advanced_objects_menu/cubester.py b/add_advanced_objects_menu/cubester.py
index f1a8f581..87322f4f 100644
--- a/add_advanced_objects_menu/cubester.py
+++ b/add_advanced_objects_menu/cubester.py
@@ -25,28 +25,28 @@
bl_info = {
"name": "CubeSter",
"author": "Jacob Morris",
- "version": (0, 7, 1),
+ "version": (0, 7, 2),
"blender": (2, 78, 0),
"location": "View 3D > Toolbar > CubeSter",
"description": "Takes image, image sequence, or audio file and converts it "
"into a height map based on pixel color and alpha values",
"category": "Add Mesh"
- }
+}
import bpy
import bmesh
from bpy.types import (
- Operator,
- Panel,
- )
+ Operator,
+ Panel,
+)
import timeit
from random import uniform
from math import radians
from os import (
- path,
- listdir,
- )
+ path,
+ listdir,
+)
# create block at center position x, y with block width 2 * hx and 2 * hy and height of h
@@ -146,18 +146,18 @@ def create_material(scene, ob, name):
if adv_obj.cubester_materials == "image":
mat.node_tree.links.new(
- nodes["Image Texture"].outputs[0],
- nodes["Diffuse BSDF"].inputs[0]
- )
+ nodes["Image Texture"].outputs[0],
+ nodes["Diffuse BSDF"].inputs[0]
+ )
mat.node_tree.links.new(
- nodes["Texture Coordinate"].outputs[2],
- nodes["Image Texture"].inputs[0]
- )
+ nodes["Texture Coordinate"].outputs[2],
+ nodes["Image Texture"].inputs[0]
+ )
else:
mat.node_tree.links.new(
- nodes["Attribute"].outputs[0],
- nodes["Diffuse BSDF"].inputs[0]
- )
+ nodes["Attribute"].outputs[0],
+ nodes["Diffuse BSDF"].inputs[0]
+ )
else:
if adv_obj.cubester_materials == "image" or scene.render.engine != "BLENDER_RENDER":
tex = bpy.data.textures.new("CubeSter_" + name, "IMAGE")
@@ -177,9 +177,11 @@ def create_mesh_from_audio(self, scene, verts, faces):
audio_filepath = adv_obj.cubester_audio_path
width = adv_obj.cubester_audio_width_blocks
length = adv_obj.cubester_audio_length_blocks
- size_per_hundred = adv_obj.cubester_size_per_hundred_pixels
+ size_per_hundred = adv_obj.cubester_size_per_hundred_pixels
size = size_per_hundred / 100
+ # Note: used for compatibility with vertex colors changes
+ bl_version = bool(bpy.app.version >= (2, 79, 1))
# create all blocks
y = -(width / 2) * size + (size / 2)
@@ -212,7 +214,8 @@ def create_mesh_from_audio(self, scene, verts, faces):
# go through each column, step by appropriate amount
for column in range(0, picture.size[0] * 4, 4 + skip_x * 4):
r, g, b, a = get_pixel_values(picture, pixels, row, column)
- vert_colors += [(r, g, b) for i in range(24)]
+ get_colors = (r, g, b, a) if bl_version else (r, g, b)
+ vert_colors += [get_colors for i in range(24)]
bpy.ops.mesh.vertex_color_add()
@@ -244,7 +247,8 @@ def create_mesh_from_audio(self, scene, verts, faces):
for row in range(0, picture.size[1], skip_y + 1):
for column in range(0, picture.size[0] * 4, 4 + skip_x * 4):
r, g, b, a = get_pixel_values(picture, pixels, row, column)
- frame_colors += [(r, g, b) for i in range(24)]
+ get_colors = (r, g, b, a) if bl_version else (r, g, b)
+ frame_colors += [get_colors for i in range(24)]
frames_vert_colors.append(frame_colors)
@@ -354,6 +358,8 @@ def create_mesh_from_image(self, scene, verts, faces):
adv_obj = scene.advanced_objects
picture = bpy.data.images[adv_obj.cubester_image]
pixels = list(picture.pixels)
+ # Note: used for compatibility with vertex colors changes
+ bl_version = bool(bpy.app.version >= (2, 79, 1))
x_pixels = picture.size[0] / (adv_obj.cubester_skip_pixels + 1)
y_pixels = picture.size[1] / (adv_obj.cubester_skip_pixels + 1)
@@ -367,7 +373,6 @@ def create_mesh_from_image(self, scene, verts, faces):
y = -height / 2 + half_width
vert_colors = []
- weights = [uniform(0.0, 1.0) for i in range(4)] # random weights
rows = 0
# go through each row of pixels stepping by adv_obj.cubester_skip_pixels + 1
@@ -377,16 +382,17 @@ def create_mesh_from_image(self, scene, verts, faces):
# go through each column, step by appropriate amount
for column in range(0, picture.size[0] * 4, 4 + adv_obj.cubester_skip_pixels * 4):
r, g, b, a = get_pixel_values(picture, pixels, row, column)
+ get_colors = (r, g, b, a) if bl_version else (r, g, b)
h = find_point_height(r, g, b, a, scene)
# if not transparent
if h != -1:
if adv_obj.cubester_mesh_style == "blocks":
create_block(x, y, half_width, h, verts, faces)
- vert_colors += [(r, g, b) for i in range(24)]
+ vert_colors += [get_colors for i in range(24)]
else:
verts += [(x, y, h)]
- vert_colors += [(r, g, b) for i in range(4)]
+ vert_colors += [get_colors for i in range(4)]
x += step
y += step
@@ -464,14 +470,15 @@ def create_mesh_from_image(self, scene, verts, faces):
for row in range(0, picture.size[1], adv_obj.cubester_skip_pixels + 1):
for column in range(0, picture.size[0] * 4, 4 + adv_obj.cubester_skip_pixels * 4):
r, g, b, a = get_pixel_values(picture, pixels, row, column)
+ get_colors = (r, g, b, a) if bl_version else (r, g, b)
h = find_point_height(r, g, b, a, scene)
if h != -1:
frame_heights.append(h)
if adv_obj.cubester_mesh_style == "blocks":
- frame_colors += [(r, g, b) for i in range(24)]
+ frame_colors += [get_colors for i in range(24)]
else:
- frame_colors += [(r, g, b) for i in range(4)]
+ frame_colors += [get_colors for i in range(4)]
if adv_obj.cubester_mesh_style == "plane":
del vert_colors[len(vert_colors) - 4:len(vert_colors)]
@@ -482,24 +489,24 @@ def create_mesh_from_image(self, scene, verts, faces):
# determine what data to use
if adv_obj.cubester_materials == "vertex" or scene.render.engine == "BLENDER_ENGINE":
adv_obj.cubester_vertex_colors[ob.name] = {
- "type": "vertex", "frames": frames_vert_colors,
- "frame_skip": adv_obj.cubester_frame_step,
- "total_images": max_images
- }
+ "type": "vertex", "frames": frames_vert_colors,
+ "frame_skip": adv_obj.cubester_frame_step,
+ "total_images": max_images
+ }
else:
adv_obj.cubester_vertex_colors[ob.name] = {
- "type": "image", "frame_skip": scene.cubester_frame_step,
- "total_images": max_images
- }
+ "type": "image", "frame_skip": adv_obj.cubester_frame_step,
+ "total_images": max_images
+ }
att = get_image_node(ob.data.materials[0])
att.image_user.frame_duration = len(frames) * adv_obj.cubester_frame_step
# animate mesh
create_f_curves(
- mesh, frames,
- adv_obj.cubester_frame_step,
- adv_obj.cubester_mesh_style
- )
+ mesh, frames,
+ adv_obj.cubester_frame_step,
+ adv_obj.cubester_mesh_style
+ )
# generate uv map for object
@@ -879,11 +886,12 @@ class CubeSterPanel(Panel):
class CubeSter(Operator):
bl_idname = "mesh.cubester"
- bl_label = "Generate Mesh"
+ bl_label = "Generate CubeSter Mesh"
bl_description = "Generate a mesh from an Image or Sound File"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
+
verts, faces = [], []
start = timeit.default_timer()
@@ -901,7 +909,8 @@ class CubeSter(Operator):
return {"CANCELLED"}
else:
if (adv_obj.cubester_audio_path != "" and
- path.isfile(adv_obj.cubester_audio_path) and adv_obj.cubester_check_audio is True):
+ path.isfile(adv_obj.cubester_audio_path) and
+ adv_obj.cubester_check_audio is True):
create_mesh_from_audio(self, scene, verts, faces)
created = adv_obj.cubester_audio_file_length
@@ -913,19 +922,21 @@ class CubeSter(Operator):
stop = timeit.default_timer()
if adv_obj.cubester_mesh_style == "blocks" or adv_obj.cubester_audio_image == "audio":
- self.report({"INFO"},
- "CubeSter: {} blocks and {} frame(s) "
- "in {}s".format(str(int(len(verts) / 8)),
- str(created),
- str(round(stop - start, 4)))
- )
+ self.report(
+ {"INFO"},
+ "CubeSter: {} blocks and {} frame(s) "
+ "in {}s".format(str(int(len(verts) / 8)),
+ str(created),
+ str(round(stop - start, 4)))
+ )
else:
- self.report({"INFO"},
- "CubeSter: {} points and {} frame(s) "
- "in {}s" .format(str(len(verts)),
- str(created),
- str(round(stop - start, 4)))
- )
+ self.report(
+ {"INFO"},
+ "CubeSter: {} points and {} frame(s) "
+ "in {}s" .format(str(len(verts)),
+ str(created),
+ str(round(stop - start, 4)))
+ )
return {"FINISHED"}
diff --git a/add_curve_ivygen.py b/add_curve_ivygen.py
index dd523102..7a90b3b9 100644
--- a/add_curve_ivygen.py
+++ b/add_curve_ivygen.py
@@ -21,9 +21,9 @@
bl_info = {
"name": "IvyGen",
"author": "testscreenings, PKHG, TrumanBlending",
- "version": (0, 1, 2),
+ "version": (0, 1, 4),
"blender": (2, 59, 0),
- "location": "View3D > Add > Curve",
+ "location": "View3D > Tool Shelf > Create > Ivy Generator",
"description": "Adds generated ivy to a mesh object starting "
"at the 3D cursor",
"warning": "",
@@ -34,10 +34,16 @@ bl_info = {
import bpy
+from bpy.types import (
+ Operator,
+ Panel,
+ PropertyGroup,
+ )
from bpy.props import (
+ BoolProperty,
FloatProperty,
IntProperty,
- BoolProperty,
+ PointerProperty,
)
from mathutils import (
Vector,
@@ -191,7 +197,7 @@ def createIvyGeometry(IVY, growLeaves):
if growLeaves:
faceList = [[4 * i + l for l in range(4)] for i in
- range(len(vertList) // 4)]
+ range(len(vertList) // 4)]
# Generate the new leaf mesh and link
me = bpy.data.meshes.new('IvyLeaf')
@@ -212,24 +218,6 @@ def createIvyGeometry(IVY, growLeaves):
ob.parent = newCurve
-'''
-def computeBoundingSphere(ob):
- # Get the mesh data
- me = ob.data
- # Intialise the center
- center = Vector((0.0, 0.0, 0.0))
- # Add all vertex coords
- for v in me.vertices:
- center += v.co
- # Average over all verts
- center /= len(me.vertices)
- # Create the iterator and find its max
- length_iter = ((center - v.co).length for v in me.vertices)
- radius = max(length_iter)
- return radius
-'''
-
-
class IvyNode:
""" The basic class used for each point on the ivy which is grown."""
__slots__ = ('pos', 'primaryDir', 'adhesionVector', 'adhesionLength',
@@ -463,12 +451,204 @@ def check_mesh_faces(ob):
return False
-class IvyGen(bpy.types.Operator):
+class IvyGen(Operator):
bl_idname = "curve.ivy_gen"
bl_label = "IvyGen"
bl_description = "Generate Ivy on an Mesh Object"
bl_options = {'REGISTER', 'UNDO'}
+ updateIvy = BoolProperty(
+ name="Update Ivy",
+ description="Update the Ivy location based on the cursor and Panel settings",
+ default=False
+ )
+ defaultIvy = BoolProperty(
+ name="Default Ivy",
+ options={"HIDDEN", "SKIP_SAVE"},
+ default=False
+ )
+
+ @classmethod
+ def poll(self, context):
+ # Check if there's an object and whether it's a mesh
+ ob = context.active_object
+ return ((ob is not None) and
+ (ob.type == 'MESH') and
+ (context.mode == 'OBJECT'))
+
+ def invoke(self, context, event):
+ self.updateIvy = True
+ return self.execute(context)
+
+ def execute(self, context):
+ # scene = context.scene
+ ivyProps = context.window_manager.ivy_gen_props
+
+ if not self.updateIvy:
+ return {'PASS_THROUGH'}
+
+ # assign the variables, check if it is default
+ # Note: update the values if window_manager props defaults are changed
+ randomSeed = ivyProps.randomSeed if not self.defaultIvy else 0
+ maxTime = ivyProps.maxTime if not self.defaultIvy else 0
+ maxIvyLength = ivyProps.maxIvyLength if not self.defaultIvy else 1.0
+ ivySize = ivyProps.ivySize if not self.defaultIvy else 0.02
+ maxFloatLength = ivyProps.maxFloatLength if not self.defaultIvy else 0.5
+ maxAdhesionDistance = ivyProps.maxAdhesionDistance if not self.defaultIvy else 1.0
+ primaryWeight = ivyProps.primaryWeight if not self.defaultIvy else 0.5
+ randomWeight = ivyProps.randomWeight if not self.defaultIvy else 0.2
+ gravityWeight = ivyProps.gravityWeight if not self.defaultIvy else 1.0
+ adhesionWeight = ivyProps.adhesionWeight if not self.defaultIvy else 0.1
+ branchingProbability = ivyProps.branchingProbability if not self.defaultIvy else 0.05
+ leafProbability = ivyProps.leafProbability if not self.defaultIvy else 0.35
+ ivyBranchSize = ivyProps.ivyBranchSize if not self.defaultIvy else 0.001
+ ivyLeafSize = ivyProps.ivyLeafSize if not self.defaultIvy else 0.02
+ growLeaves = ivyProps.growLeaves if not self.defaultIvy else True
+
+ bpy.ops.object.mode_set(mode='EDIT', toggle=False)
+ bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
+
+ # Get the selected object
+ ob = context.active_object
+
+ # Check if the mesh has at least one polygon since some functions
+ # are expecting them in the object's data (see T51753)
+ check_face = check_mesh_faces(ob)
+ if check_face is False:
+ self.report({'WARNING'},
+ "Mesh Object doesn't have at least one Face. "
+ "Operation Cancelled")
+ return {"CANCELLED"}
+
+ # Compute bounding sphere radius
+ # radius = computeBoundingSphere(ob) # Not needed anymore
+
+ # Get the seeding point
+ seedPoint = context.scene.cursor_location
+
+ # Fix the random seed
+ rand_seed(randomSeed)
+
+ # Make the new ivy
+ IVY = Ivy(
+ primaryWeight=primaryWeight,
+ randomWeight=randomWeight,
+ gravityWeight=gravityWeight,
+ adhesionWeight=adhesionWeight,
+ branchingProbability=branchingProbability,
+ leafProbability=leafProbability,
+ ivySize=ivySize,
+ ivyLeafSize=ivyLeafSize,
+ ivyBranchSize=ivyBranchSize,
+ maxFloatLength=maxFloatLength,
+ maxAdhesionDistance=maxAdhesionDistance
+ )
+ # Generate first root and node
+ IVY.seed(seedPoint)
+
+ checkTime = False
+ maxLength = maxIvyLength # * radius
+
+ # If we need to check time set the flag
+ if maxTime != 0.0:
+ checkTime = True
+
+ t = time.time()
+ startPercent = 0.0
+ checkAliveIter = [True, ]
+
+ # Grow until 200 roots is reached or backup counter exceeds limit
+ while (any(checkAliveIter) and
+ (IVY.maxLength < maxLength) and
+ (not checkTime or (time.time() - t < maxTime))):
+ # Grow the ivy for this iteration
+ IVY.grow(ob)
+
+ # Print the proportion of ivy growth to console
+ if (IVY.maxLength / maxLength * 100) > 10 * startPercent // 10:
+ print('%0.2f%% of Ivy nodes have grown' %
+ (IVY.maxLength / maxLength * 100))
+ startPercent += 10
+ if IVY.maxLength / maxLength > 1:
+ print("Halting Growth")
+
+ # Make an iterator to check if all are alive
+ checkAliveIter = (r.alive for r in IVY.ivyRoots)
+
+ # Create the curve and leaf geometry
+ createIvyGeometry(IVY, growLeaves)
+ print("Geometry Generation Complete")
+
+ print("Ivy generated in %0.2f s" % (time.time() - t))
+
+ self.updateIvy = False
+ self.defaultIvy = False
+
+ return {'FINISHED'}
+
+ def draw(self, context):
+ layout = self.layout
+
+ layout.prop(self, "updateIvy", icon="FILE_REFRESH")
+
+
+class CURVE_PT_IvyGenPanel(Panel):
+ bl_label = "Ivy Generator"
+ bl_idname = "CURVE_PT_IvyGenPanel"
+ bl_space_type = "VIEW_3D"
+ bl_region_type = "TOOLS"
+ bl_category = "Create"
+ bl_context = 'objectmode'
+ bl_options = {"DEFAULT_CLOSED"}
+
+ def draw(self, context):
+ layout = self.layout
+ wm = context.window_manager
+ col = layout.column(align=True)
+
+ prop_new = col.operator("curve.ivy_gen", text="Add New Ivy", icon="OUTLINER_OB_CURVE")
+ prop_new.defaultIvy = False
+ prop_new.updateIvy = True
+
+ prop_def = col.operator("curve.ivy_gen", text="Add New Default Ivy", icon="CURVE_DATA")
+ prop_def.defaultIvy = True
+ prop_def.updateIvy = True
+
+ col = layout.column(align=True)
+ col.label("Generation Settings:")
+ col.prop(wm.ivy_gen_props, "randomSeed")
+ col.prop(wm.ivy_gen_props, "maxTime")
+
+ col = layout.column(align=True)
+ col.label("Size Settings:")
+ col.prop(wm.ivy_gen_props, "maxIvyLength")
+ col.prop(wm.ivy_gen_props, "ivySize")
+ col.prop(wm.ivy_gen_props, "maxFloatLength")
+ col.prop(wm.ivy_gen_props, "maxAdhesionDistance")
+
+ col = layout.column(align=True)
+ col.label("Weight Settings:")
+ col.prop(wm.ivy_gen_props, "primaryWeight")
+ col.prop(wm.ivy_gen_props, "randomWeight")
+ col.prop(wm.ivy_gen_props, "gravityWeight")
+ col.prop(wm.ivy_gen_props, "adhesionWeight")
+
+ col = layout.column(align=True)
+ col.label("Branch Settings:")
+ col.prop(wm.ivy_gen_props, "branchingProbability")
+ col.prop(wm.ivy_gen_props, "ivyBranchSize")
+
+ col = layout.column(align=True)
+ col.prop(wm.ivy_gen_props, "growLeaves")
+
+ if wm.ivy_gen_props.growLeaves:
+ col = layout.column(align=True)
+ col.label("Leaf Settings:")
+ col.prop(wm.ivy_gen_props, "ivyLeafSize")
+ col.prop(wm.ivy_gen_props, "leafProbability")
+
+
+class IvyGenProperties(PropertyGroup):
maxIvyLength = FloatProperty(
name="Max Ivy Length",
description="Maximum ivy length in Blender Units",
@@ -551,7 +731,8 @@ class IvyGen(bpy.types.Operator):
"can live while floating",
default=0.5,
min=0.0,
- soft_max=1.0)
+ soft_max=1.0
+ )
maxAdhesionDistance = FloatProperty(
name="Max Adhesion Length",
description="The maximum distance that a branch "
@@ -580,168 +761,29 @@ class IvyGen(bpy.types.Operator):
description="Grow leaves or not",
default=True
)
- updateIvy = BoolProperty(
- name="Update Ivy",
- default=False
- )
-
- @classmethod
- def poll(self, context):
- # Check if there's an object and whether it's a mesh
- ob = context.active_object
- return ((ob is not None) and
- (ob.type == 'MESH') and
- (context.mode == 'OBJECT'))
-
- def invoke(self, context, event):
- self.updateIvy = True
- return self.execute(context)
-
- def execute(self, context):
- if not self.updateIvy:
- return {'PASS_THROUGH'}
-
- bpy.ops.object.mode_set(mode='EDIT', toggle=False)
- bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
-
- # Get the selected object
- ob = context.active_object
-
- # Check if the mesh has at least one polygon since some functions
- # are expecting them in the object's data (see T51753)
- check_face = check_mesh_faces(ob)
- if check_face is False:
- self.report({'WARNING'},
- "Mesh Object doesn't have at least one Face. "
- "Operation Cancelled")
- return {"CANCELLED"}
-
- # Compute bounding sphere radius
- # radius = computeBoundingSphere(ob) # Not needed anymore
-
- # Get the seeding point
- seedPoint = context.scene.cursor_location
-
- # Fix the random seed
- rand_seed(self.randomSeed)
-
- # Make the new ivy
- IVY = Ivy(**self.as_keywords(ignore=('randomSeed', 'growLeaves',
- 'maxIvyLength', 'maxTime', 'updateIvy')))
-
- # Generate first root and node
- IVY.seed(seedPoint)
-
- checkTime = False
- maxLength = self.maxIvyLength # * radius
-
- # If we need to check time set the flag
- if self.maxTime != 0.0:
- checkTime = True
- t = time.time()
- startPercent = 0.0
- checkAliveIter = [True, ]
-
- # Grow until 200 roots is reached or backup counter exceeds limit
- while (any(checkAliveIter) and
- (IVY.maxLength < maxLength) and
- (not checkTime or (time.time() - t < self.maxTime))):
- # Grow the ivy for this iteration
- IVY.grow(ob)
- # Print the proportion of ivy growth to console
- if (IVY.maxLength / maxLength * 100) > 10 * startPercent // 10:
- print('%0.2f%% of Ivy nodes have grown' %
- (IVY.maxLength / maxLength * 100))
- startPercent += 10
- if IVY.maxLength / maxLength > 1:
- print("Halting Growth")
-
- # Make an iterator to check if all are alive
- checkAliveIter = (r.alive for r in IVY.ivyRoots)
-
- # Create the curve and leaf geometry
- createIvyGeometry(IVY, self.growLeaves)
- print("Geometry Generation Complete")
-
- print("Ivy generated in %0.2f s" % (time.time() - t))
-
- self.updateIvy = False
-
- return {'FINISHED'}
-
- def draw(self, context):
- layout = self.layout
-
- layout.prop(self, 'updateIvy', icon='CURVE_DATA')
-
- properties = layout.operator('curve.ivy_gen', text="Add New Ivy")
- properties.randomSeed = self.randomSeed
- properties.maxTime = self.maxTime
- properties.maxIvyLength = self.maxIvyLength
- properties.ivySize = self.ivySize
- properties.maxFloatLength = self.maxFloatLength
- properties.maxAdhesionDistance = self.maxAdhesionDistance
- properties.primaryWeight = self.primaryWeight
- properties.randomWeight = self.randomWeight
- properties.gravityWeight = self.gravityWeight
- properties.adhesionWeight = self.adhesionWeight
- properties.branchingProbability = self.branchingProbability
- properties.leafProbability = self.leafProbability
- properties.ivyBranchSize = self.ivyBranchSize
- properties.ivyLeafSize = self.ivyLeafSize
- properties.updateIvy = True
-
- prop_def = layout.operator('curve.ivy_gen', text="Add New Default Ivy")
- prop_def.updateIvy = True
-
- layout.prop(self, 'growLeaves')
-
- box = layout.box()
- box.label("Generation Settings:")
- box.prop(self, 'randomSeed')
- box.prop(self, 'maxTime')
-
- box = layout.box()
- box.label("Size Settings:")
- box.prop(self, 'maxIvyLength')
- box.prop(self, 'ivySize')
- box.prop(self, 'maxFloatLength')
- box.prop(self, 'maxAdhesionDistance')
-
- box = layout.box()
- box.label("Weight Settings:")
- box.prop(self, 'primaryWeight')
- box.prop(self, 'randomWeight')
- box.prop(self, 'gravityWeight')
- box.prop(self, 'adhesionWeight')
-
- box = layout.box()
- box.label("Branch Settings:")
- box.prop(self, 'branchingProbability')
- box.prop(self, 'ivyBranchSize')
-
- if self.growLeaves:
- box = layout.box()
- box.label("Leaf Settings:")
- box.prop(self, 'ivyLeafSize')
- box.prop(self, 'leafProbability')
-
-
-def menu_func(self, context):
- self.layout.operator(IvyGen.bl_idname, text="Add Ivy to Mesh",
- icon='OUTLINER_DATA_CURVE').updateIvy = True
+classes = (
+ IvyGen,
+ IvyGenProperties,
+ CURVE_PT_IvyGenPanel
+)
def register():
- bpy.utils.register_module(__name__)
- bpy.types.INFO_MT_curve_add.append(menu_func)
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bpy.types.WindowManager.ivy_gen_props = PointerProperty(
+ type=IvyGenProperties
+ )
def unregister():
- bpy.types.INFO_MT_curve_add.remove(menu_func)
- bpy.utils.unregister_module(__name__)
+ del bpy.types.WindowManager.ivy_gen_props
+
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
if __name__ == "__main__":
diff --git a/ant_landscape/add_mesh_ant_landscape.py b/ant_landscape/add_mesh_ant_landscape.py
index 5d25cf24..6a36c42f 100644
--- a/ant_landscape/add_mesh_ant_landscape.py
+++ b/ant_landscape/add_mesh_ant_landscape.py
@@ -586,6 +586,14 @@ class AntAddLandscape(bpy.types.Operator):
description="Automatic refresh"
)
+ @classmethod
+ def poll(self, context):
+ ob = context.object
+ if ob is not None:
+ if ob.mode == 'EDIT':
+ return False
+ return True
+
def draw(self, context):
draw_ant_refresh(self, context)
draw_ant_main(self, context, generate=True)
diff --git a/ant_landscape/ant_functions.py b/ant_landscape/ant_functions.py
index 3b515933..3c0f2c34 100644
--- a/ant_landscape/ant_functions.py
+++ b/ant_landscape/ant_functions.py
@@ -193,7 +193,10 @@ class AntLandscapeRegenerate(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return bpy.context.active_object.ant_landscape
+ ob = bpy.context.active_object
+ if ob.mode == 'EDIT':
+ return False
+ return ob.ant_landscape
def execute(self, context):
diff --git a/archipack/archipack_material.py b/archipack/archipack_material.py
index c226c7fb..22da496d 100644
--- a/archipack/archipack_material.py
+++ b/archipack/archipack_material.py
@@ -223,6 +223,8 @@ class MaterialSetManager():
Store sets for each object type
"""
self.objects = {}
+ # hold reference of dynamic enumerator
+ self.enums = {}
def get_filename(self, object_type):
@@ -234,6 +236,7 @@ class MaterialSetManager():
def cleanup(self):
self.objects.clear()
+ self.enums.clear()
def register_set(self, object_type, set_name, materials_names):
@@ -275,16 +278,20 @@ class MaterialSetManager():
finally:
f.close()
- for s_key in material_sets.keys():
+ s_keys = material_sets.keys()
+ for s_key in s_keys:
self.register_set(object_type, s_key, material_sets[s_key])
+ self.make_enum(object_type, s_keys)
+
def save(self, object_type):
# always save in user prefs
filename = self.get_filename(object_type)
# print("filename:%s" % filename)
o_dict = self.objects[object_type]
lines = []
- for s_key in o_dict.keys():
+ s_keys = o_dict.keys()
+ for s_key in s_keys:
for mat in o_dict[s_key]:
lines.append("{}##|##{}\n".format(s_key, mat))
try:
@@ -296,6 +303,8 @@ class MaterialSetManager():
finally:
f.close()
+ self.make_enum(object_type, s_keys)
+
def add(self, context, set_name):
o = context.active_object
if "archipack_material" in o:
@@ -311,9 +320,11 @@ class MaterialSetManager():
d = o.archipack_material[0]
object_type = d.category
set_name = d.material
- if set_name in self.objects[object_type].keys():
+ s_keys = self.objects[object_type].keys()
+ if set_name in s_keys:
self.objects[object_type].pop(set_name)
self.save(object_type)
+ self.make_enum(object_type, s_keys)
def get_materials(self, object_type, set_name):
if object_type not in self.objects.keys():
@@ -326,7 +337,13 @@ class MaterialSetManager():
return None
return self.objects[object_type][set_name]
- def make_enum(self, object_type):
+ def make_enum(self, object_type, s_keys):
+ if len(s_keys) < 1:
+ self.enums[object_type] = [('DEFAULT', 'Default', '', 0)]
+ else:
+ self.enums[object_type] = [(s.upper(), s.capitalize(), '', i) for i, s in enumerate(s_keys)]
+
+ def get_enum(self, object_type):
if object_type not in self.objects.keys():
self.load(object_type)
@@ -334,19 +351,14 @@ class MaterialSetManager():
if object_type not in self.objects.keys():
self.objects[object_type] = {}
- s_keys = self.objects[object_type].keys()
-
- if len(s_keys) < 1:
- return [('DEFAULT', 'Default', '', 0)]
-
- return [(s.upper(), s.capitalize(), '', i) for i, s in enumerate(s_keys)]
+ return self.enums[object_type]
def material_enum(self, context):
global setman
if setman is None:
setman = MaterialSetManager()
- return setman.make_enum(self.category)
+ return setman.get_enum(self.category)
def update(self, context):
diff --git a/bone_selection_sets.py b/bone_selection_sets.py
index 46aa7f25..686eb808 100644
--- a/bone_selection_sets.py
+++ b/bone_selection_sets.py
@@ -18,8 +18,8 @@
bl_info = {
"name": "Bone Selection Sets",
- "author": "Inês Almeida, Antony Riakiotakis, Dan Eicher",
- "version": (2, 0, 1),
+ "author": "Inês Almeida, Sybren A. Stüvel, Antony Riakiotakis, Dan Eicher",
+ "version": (2, 1, 1),
"blender": (2, 75, 0),
"location": "Properties > Object Data (Armature) > Selection Sets",
"description": "List of Bone sets for easy selection while animating",
@@ -31,18 +31,18 @@ bl_info = {
import bpy
from bpy.types import (
- Operator,
- Menu,
- Panel,
- UIList,
- PropertyGroup,
- )
+ Operator,
+ Menu,
+ Panel,
+ UIList,
+ PropertyGroup,
+)
from bpy.props import (
- StringProperty,
- IntProperty,
- EnumProperty,
- CollectionProperty,
- )
+ StringProperty,
+ IntProperty,
+ EnumProperty,
+ CollectionProperty,
+)
# Data Structure ##############################################################
@@ -68,6 +68,8 @@ class POSE_MT_selection_sets_specials(Menu):
layout.operator("pose.selection_set_delete_all", icon='X')
layout.operator("pose.selection_set_remove_bones", icon='X')
+ layout.operator("pose.selection_set_copy", icon='COPYDOWN')
+ layout.operator("pose.selection_set_paste", icon='PASTEDOWN')
class POSE_PT_selection_sets(Panel):
@@ -128,21 +130,35 @@ class POSE_UL_selection_set(UIList):
layout.prop(set, "name", text="", icon='GROUP_BONE', emboss=False)
-class POSE_MT_create_new_selection_set(Menu):
- bl_idname = "POSE_MT_selection_set_create"
+class POSE_MT_selection_set_create(Menu):
bl_label = "Choose Selection Set"
def draw(self, context):
layout = self.layout
layout.operator("pose.selection_set_add_and_assign",
- text="New Selection Set")
+ text="New Selection Set")
+
+
+class POSE_MT_selection_sets(Menu):
+ bl_label = 'Select Selection Set'
+
+ @classmethod
+ def poll(cls, context):
+ return POSE_OT_selection_set_select.poll(context)
+
+ def draw(self, context):
+ layout = self.layout
+ layout.operator_context = 'EXEC_DEFAULT'
+ for idx, sel_set in enumerate(context.object.selection_sets):
+ props = layout.operator(POSE_OT_selection_set_select.bl_idname, text=sel_set.name)
+ props.selection_set_index = idx
# Operators ###################################################################
class PluginOperator(Operator):
@classmethod
- def poll(self, context):
+ def poll(cls, context):
return (context.object and
context.object.type == 'ARMATURE' and
context.mode == 'POSE')
@@ -150,12 +166,11 @@ class PluginOperator(Operator):
class NeedSelSetPluginOperator(PluginOperator):
@classmethod
- def poll(self, context):
- if super().poll(context):
- arm = context.object
- return (arm.active_selection_set < len(arm.selection_sets) and
- arm.active_selection_set >= 0)
- return False
+ def poll(cls, context):
+ if not super().poll(context):
+ return False
+ arm = context.object
+ return 0 <= arm.active_selection_set < len(arm.selection_sets)
class POSE_OT_selection_set_delete_all(PluginOperator):
@@ -206,11 +221,11 @@ class POSE_OT_selection_set_move(NeedSelSetPluginOperator):
)
@classmethod
- def poll(self, context):
- if super().poll(context):
- arm = context.object
- return len(arm.selection_sets) > 1
- return False
+ def poll(cls, context):
+ if not super().poll(context):
+ return False
+ arm = context.object
+ return len(arm.selection_sets) > 1
def execute(self, context):
arm = context.object
@@ -235,30 +250,12 @@ class POSE_OT_selection_set_add(PluginOperator):
def execute(self, context):
arm = context.object
-
- new_sel_set = arm.selection_sets.add()
-
- # naming
- if "SelectionSet" not in arm.selection_sets:
- new_sel_set.name = "SelectionSet"
- else:
- sorted_sets = []
- for selset in arm.selection_sets:
- if selset.name.startswith("SelectionSet."):
- index = selset.name[13:]
- if index.isdigit():
- sorted_sets.append(index)
- sorted_sets = sorted(sorted_sets)
- min_index = 1
- for num in sorted_sets:
- num = int(num)
- if min_index < num:
- break
- min_index = num + 1
- new_sel_set.name = "SelectionSet.{:03d}".format(min_index)
+ sel_sets = arm.selection_sets
+ new_sel_set = sel_sets.add()
+ new_sel_set.name = uniqify("SelectionSet", sel_sets.keys())
# select newly created set
- arm.active_selection_set = len(arm.selection_sets) - 1
+ arm.active_selection_set = len(sel_sets) - 1
return {'FINISHED'}
@@ -293,7 +290,7 @@ class POSE_OT_selection_set_assign(PluginOperator):
if not (arm.active_selection_set < len(arm.selection_sets)):
bpy.ops.wm.call_menu("INVOKE_DEFAULT",
- name="POSE_MT_selection_set_create")
+ name="POSE_MT_selection_set_create")
else:
bpy.ops.pose.selection_set_assign('EXEC_DEFAULT')
@@ -337,12 +334,22 @@ class POSE_OT_selection_set_select(NeedSelSetPluginOperator):
bl_description = "Add Selection Set bones to current selection"
bl_options = {'UNDO', 'REGISTER'}
+ selection_set_index = IntProperty(
+ name='Selection Set Index',
+ default=-1,
+ description='Which Selection Set to select; -1 uses the active Selection Set')
+
def execute(self, context):
arm = context.object
- act_sel_set = arm.selection_sets[arm.active_selection_set]
+
+ if self.selection_set_index == -1:
+ idx = arm.active_selection_set
+ else:
+ idx = self.selection_set_index
+ sel_set = arm.selection_sets[idx]
for bone in context.visible_pose_bones:
- if bone.name in act_sel_set.bone_ids:
+ if bone.name in sel_set.bone_ids:
bone.bone.select = True
return {'FINISHED'}
@@ -377,11 +384,44 @@ class POSE_OT_selection_set_add_and_assign(PluginOperator):
return {'FINISHED'}
+class POSE_OT_selection_set_copy(NeedSelSetPluginOperator):
+ bl_idname = "pose.selection_set_copy"
+ bl_label = "Copy Selection Set to Clipboard"
+ bl_description = "Converts the Selection Set to JSON and places it on the clipboard"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ def execute(self, context):
+ context.window_manager.clipboard = to_json(context)
+ self.report({'INFO'}, 'Copied Selection Set to Clipboard')
+ return {'FINISHED'}
+
+
+class POSE_OT_selection_set_paste(PluginOperator):
+ bl_idname = "pose.selection_set_paste"
+ bl_label = "Paste Selection Set from Clipboard"
+ bl_description = "Adds a new Selection Set from copied JSON on the clipboard"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ def execute(self, context):
+ import json
+
+ try:
+ from_json(context, context.window_manager.clipboard)
+ except (json.JSONDecodeError, KeyError):
+ self.report({'ERROR'}, 'The clipboard does not contain a Selection Set')
+ else:
+ # Select the pasted Selection Set.
+ context.object.active_selection_set = len(context.object.selection_sets) - 1
+
+ return {'FINISHED'}
+
+
# Registry ####################################################################
classes = (
- POSE_MT_create_new_selection_set,
+ POSE_MT_selection_set_create,
POSE_MT_selection_sets_specials,
+ POSE_MT_selection_sets,
POSE_PT_selection_sets,
POSE_UL_selection_set,
SelectionEntry,
@@ -396,23 +436,111 @@ classes = (
POSE_OT_selection_set_select,
POSE_OT_selection_set_deselect,
POSE_OT_selection_set_add_and_assign,
+ POSE_OT_selection_set_copy,
+ POSE_OT_selection_set_paste,
)
+def add_sss_button(self, context):
+ self.layout.menu('POSE_MT_selection_sets')
+
+
+def to_json(context) -> str:
+ """Convert the active bone selection set of the current rig to JSON."""
+ import json
+
+ arm = context.object
+ active_idx = arm.active_selection_set
+ sel_set = arm.selection_sets[active_idx]
+
+ return json.dumps({
+ 'name': sel_set.name,
+ 'bones': [bone_id.name for bone_id in sel_set.bone_ids]
+ })
+
+
+def from_json(context, as_json: str):
+ """Add the single bone selection set from JSON to the current rig."""
+ import json
+
+ sel_set = json.loads(as_json)
+
+ sel_sets = context.object.selection_sets
+ new_sel_set = sel_sets.add()
+ new_sel_set.name = uniqify(sel_set['name'], sel_sets.keys())
+
+ for bone_name in sel_set['bones']:
+ bone_id = new_sel_set.bone_ids.add()
+ bone_id.name = bone_name
+
+
+def uniqify(name: str, other_names: list) -> str:
+ """Return a unique name with .xxx suffix if necessary.
+
+ >>> uniqify('hey', ['there'])
+ 'hey'
+ >>> uniqify('hey', ['hey.001', 'hey.005'])
+ 'hey'
+ >>> uniqify('hey', ['hey', 'hey.001', 'hey.005'])
+ 'hey.002'
+ >>> uniqify('hey', ['hey', 'hey.005', 'hey.001'])
+ 'hey.002'
+ >>> uniqify('hey', ['hey', 'hey.005', 'hey.001', 'hey.left'])
+ 'hey.002'
+ >>> uniqify('hey', ['hey', 'hey.001', 'hey.002'])
+ 'hey.003'
+
+ It also works with a dict_keys object:
+ >>> uniqify('hey', {'hey': 1, 'hey.005': 1, 'hey.001': 1}.keys())
+ 'hey.002'
+ """
+
+ if name not in other_names:
+ return name
+
+ # Construct the list of numbers already in use.
+ offset = len(name) + 1
+ others = (n[offset:] for n in other_names
+ if n.startswith(name + '.'))
+ numbers = sorted(int(suffix) for suffix in others
+ if suffix.isdigit())
+
+ # Find the first unused number.
+ min_index = 1
+ for num in numbers:
+ if min_index < num:
+ break
+ min_index = num + 1
+ return "{}.{:03d}".format(name, min_index)
+
+
+# store keymaps here to access after registration
+addon_keymaps = []
+
+
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Object.selection_sets = CollectionProperty(
- type=SelectionSet,
- name="Selection Sets",
- description="List of groups of bones for easy selection"
- )
+ type=SelectionSet,
+ name="Selection Sets",
+ description="List of groups of bones for easy selection"
+ )
bpy.types.Object.active_selection_set = IntProperty(
- name="Active Selection Set",
- description="Index of the currently active selection set",
- default=0
- )
+ name="Active Selection Set",
+ description="Index of the currently active selection set",
+ default=0
+ )
+
+ wm = bpy.context.window_manager
+ km = wm.keyconfigs.addon.keymaps.new(name='Pose')
+
+ kmi = km.keymap_items.new('wm.call_menu', 'W', 'PRESS', alt=True, shift=True)
+ kmi.properties.name = 'POSE_MT_selection_sets'
+ addon_keymaps.append((km, kmi))
+
+ bpy.types.VIEW3D_MT_select_pose.append(add_sss_button)
def unregister():
@@ -422,6 +550,14 @@ def unregister():
del bpy.types.Object.selection_sets
del bpy.types.Object.active_selection_set
+ # handle the keymap
+ for km, kmi in addon_keymaps:
+ km.keymap_items.remove(kmi)
+ addon_keymaps.clear()
+
if __name__ == "__main__":
+ import doctest
+
+ doctest.testmod()
register()
diff --git a/camera_turnaround.py b/camera_turnaround.py
index d1a27e98..6a6e710f 100644
--- a/camera_turnaround.py
+++ b/camera_turnaround.py
@@ -21,7 +21,7 @@ bl_info = {
"author": "Antonio Vazquez (antonioya)",
"version": (0, 2, 5),
"blender": (2, 68, 0),
- "location": "View3D > Toolshelf > Turnaround camera",
+ "location": "View3D > Toolshelf > Animation Tab > Turnaround Camera",
"description": "Add a camera rotation around selected object",
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
"Scripts/Animation/TurnaroundCamera",
@@ -58,7 +58,7 @@ class RunAction(Operator):
scene = context.scene
turn_camera = scene.turn_camera
selectobject = context.active_object
- camera = bpy.data.objects[bpy.context.scene.camera.name]
+ camera = context.scene.camera
savedcursor = bpy.context.scene.cursor_location.copy() # cursor position
savedframe = scene.frame_current
if turn_camera.use_cursor is False:
@@ -68,7 +68,7 @@ class RunAction(Operator):
# Create empty and parent
# -------------------------
bpy.ops.object.empty_add(type='PLAIN_AXES')
- myempty = bpy.data.objects[bpy.context.active_object.name]
+ myempty = context.active_object
myempty.location = selectobject.location
savedstate = myempty.matrix_world
@@ -89,7 +89,7 @@ class RunAction(Operator):
# -------------------------
bpy.ops.object.select_all(False)
myempty.select = True
- bpy.context.scene.objects.active = myempty
+ context.scene.objects.active = myempty
# save current configuration
savedinterpolation = context.user_preferences.edit.keyframe_new_interpolation_type
# change interpolation mode
@@ -97,7 +97,7 @@ class RunAction(Operator):
# create first frame
myempty.rotation_euler = (0, 0, 0)
myempty.empty_draw_size = 0.1
- bpy.context.scene.frame_set(scene.frame_start)
+ context.scene.frame_set(scene.frame_start)
myempty.keyframe_insert(data_path='rotation_euler', frame=scene.frame_start)
# Clear the Camera Animations if the option is checked
diff --git a/depsgraph_debug.py b/depsgraph_debug.py
index 9e4c67f0..438d4885 100644
--- a/depsgraph_debug.py
+++ b/depsgraph_debug.py
@@ -40,8 +40,8 @@ def _get_depsgraph(context):
if bpy.app.version < (2, 80, 0,):
return scene.depsgraph
else:
- scene_layer = scene.view_layers.active
- return scene_layer.depsgraph
+ view_layer = context.view_layer
+ return view_layer.depsgraph
###############################################################################
diff --git a/development_iskeyfree.py b/development_iskeyfree.py
index 0408a648..7851afee 100644
--- a/development_iskeyfree.py
+++ b/development_iskeyfree.py
@@ -21,33 +21,33 @@
bl_info = {
"name": "Is key Free",
"author": "Antonio Vazquez (antonioya)",
- "version": (1, 0, 2),
+ "version": (1, 1, 1),
"blender": (2, 6, 9),
"location": "Text Editor > Props Shelf (Ctrl/t > IsKeyFree Tools",
- "description": "Find free shortcuts and inform of used keys",
+ "description": "Find free shortcuts, inform about used and print a key list",
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6"
"/Py/Scripts/Development/IsKeyFree",
- "category": "Development"}
+ "category": "Development"
+}
import bpy
from bpy.props import (
- StringProperty,
- BoolProperty,
- EnumProperty,
- PointerProperty,
- )
+ BoolProperty,
+ EnumProperty,
+ StringProperty,
+ PointerProperty,
+)
from bpy.types import (
- Operator,
- Panel,
- PropertyGroup,
- )
+ Operator,
+ Panel,
+ PropertyGroup,
+)
# ------------------------------------------------------
# Class to find keymaps
# ------------------------------------------------------
-
class MyChecker():
lastfind = None
lastkey = None
@@ -191,16 +191,11 @@ mychecker = MyChecker() # Global class handler
# ------------------------------------------------------
# Button: Class for search button
# ------------------------------------------------------
-
-
class RunActionCheck(Operator):
bl_idname = "iskeyfree.action_check"
bl_label = ""
bl_description = "Verify if the selected shortcut is free"
- # ------------------------------
- # Execute
- # ------------------------------
# noinspection PyUnusedLocal
def execute(self, context):
scene = context.scene.is_keyfree
@@ -239,6 +234,8 @@ class UIControlPanel(Panel):
row = layout.row()
row.prop(scene, "numpad")
+ layout.operator("iskeyfree.run_export_keys", icon="FILE_TEXT")
+
global mychecker
mylist = mychecker.getlist()
oldcontext = None
@@ -270,8 +267,6 @@ class UIControlPanel(Panel):
# ------------------------------------------------------
# Update key (special values) event handler
# ------------------------------------------------------
-
-
# noinspection PyUnusedLocal
def update_data(self, context):
scene = context.scene.is_keyfree
@@ -281,230 +276,320 @@ def update_data(self, context):
class IskeyFreeProperties(PropertyGroup):
data = StringProperty(
- name="Key", maxlen=32,
- description="Shortcut to verify"
- )
+ name="Key", maxlen=32,
+ description="Shortcut to verify"
+ )
use_crtl = BoolProperty(
- name="Ctrl",
- description="Ctrl key used in shortcut",
- default=False
- )
+ name="Ctrl",
+ description="Ctrl key used in shortcut",
+ default=False
+ )
use_alt = BoolProperty(
- name="Alt",
- description="Alt key used in shortcut",
- default=False
- )
+ name="Alt",
+ description="Alt key used in shortcut",
+ default=False
+ )
use_shift = BoolProperty(
- name="Shift",
- description="Shift key used in shortcut",
- default=False
- )
+ name="Shift",
+ description="Shift key used in shortcut",
+ default=False
+ )
use_oskey = BoolProperty(
- name="OsKey",
- description="Operating system key used in shortcut",
- default=False
- )
+ name="OsKey",
+ description="Operating system key used in shortcut",
+ default=False
+ )
numpad = EnumProperty(
- items=(('NONE', "Select key", ""),
- ("LEFTMOUSE", "LEFTMOUSE", ""),
- ("MIDDLEMOUSE", "MIDDLEMOUSE", ""),
- ("RIGHTMOUSE", "RIGHTMOUSE", ""),
- ("BUTTON4MOUSE", "BUTTON4MOUSE", ""),
- ("BUTTON5MOUSE", "BUTTON5MOUSE", ""),
- ("BUTTON6MOUSE", "BUTTON6MOUSE", ""),
- ("BUTTON7MOUSE", "BUTTON7MOUSE", ""),
- ("ACTIONMOUSE", "ACTIONMOUSE", ""),
- ("SELECTMOUSE", "SELECTMOUSE", ""),
- ("MOUSEMOVE", "MOUSEMOVE", ""),
- ("INBETWEEN_MOUSEMOVE", "INBETWEEN_MOUSEMOVE", ""),
- ("TRACKPADPAN", "TRACKPADPAN", ""),
- ("TRACKPADZOOM", "TRACKPADZOOM", ""),
- ("MOUSEROTATE", "MOUSEROTATE", ""),
- ("WHEELUPMOUSE", "WHEELUPMOUSE", ""),
- ("WHEELDOWNMOUSE", "WHEELDOWNMOUSE", ""),
- ("WHEELINMOUSE", "WHEELINMOUSE", ""),
- ("WHEELOUTMOUSE", "WHEELOUTMOUSE", ""),
- ("EVT_TWEAK_L", "EVT_TWEAK_L", ""),
- ("EVT_TWEAK_M", "EVT_TWEAK_M", ""),
- ("EVT_TWEAK_R", "EVT_TWEAK_R", ""),
- ("EVT_TWEAK_A", "EVT_TWEAK_A", ""),
- ("EVT_TWEAK_S", "EVT_TWEAK_S", ""),
- ("A", "A", ""),
- ("B", "B", ""),
- ("C", "C", ""),
- ("D", "D", ""),
- ("E", "E", ""),
- ("F", "F", ""),
- ("G", "G", ""),
- ("H", "H", ""),
- ("I", "I", ""),
- ("J", "J", ""),
- ("K", "K", ""),
- ("L", "L", ""),
- ("M", "M", ""),
- ("N", "N", ""),
- ("O", "O", ""),
- ("P", "P", ""),
- ("Q", "Q", ""),
- ("R", "R", ""),
- ("S", "S", ""),
- ("T", "T", ""),
- ("U", "U", ""),
- ("V", "V", ""),
- ("W", "W", ""),
- ("X", "X", ""),
- ("Y", "Y", ""),
- ("Z", "Z", ""),
- ("ZERO", "ZERO", ""),
- ("ONE", "ONE", ""),
- ("TWO", "TWO", ""),
- ("THREE", "THREE", ""),
- ("FOUR", "FOUR", ""),
- ("FIVE", "FIVE", ""),
- ("SIX", "SIX", ""),
- ("SEVEN", "SEVEN", ""),
- ("EIGHT", "EIGHT", ""),
- ("NINE", "NINE", ""),
- ("LEFT_CTRL", "LEFT_CTRL", ""),
- ("LEFT_ALT", "LEFT_ALT", ""),
- ("LEFT_SHIFT", "LEFT_SHIFT", ""),
- ("RIGHT_ALT", "RIGHT_ALT", ""),
- ("RIGHT_CTRL", "RIGHT_CTRL", ""),
- ("RIGHT_SHIFT", "RIGHT_SHIFT", ""),
- ("OSKEY", "OSKEY", ""),
- ("GRLESS", "GRLESS", ""),
- ("ESC", "ESC", ""),
- ("TAB", "TAB", ""),
- ("RET", "RET", ""),
- ("SPACE", "SPACE", ""),
- ("LINE_FEED", "LINE_FEED", ""),
- ("BACK_SPACE", "BACK_SPACE", ""),
- ("DEL", "DEL", ""),
- ("SEMI_COLON", "SEMI_COLON", ""),
- ("PERIOD", "PERIOD", ""),
- ("COMMA", "COMMA", ""),
- ("QUOTE", "QUOTE", ""),
- ("ACCENT_GRAVE", "ACCENT_GRAVE", ""),
- ("MINUS", "MINUS", ""),
- ("SLASH", "SLASH", ""),
- ("BACK_SLASH", "BACK_SLASH", ""),
- ("EQUAL", "EQUAL", ""),
- ("LEFT_BRACKET", "LEFT_BRACKET", ""),
- ("RIGHT_BRACKET", "RIGHT_BRACKET", ""),
- ("LEFT_ARROW", "LEFT_ARROW", ""),
- ("DOWN_ARROW", "DOWN_ARROW", ""),
- ("RIGHT_ARROW", "RIGHT_ARROW", ""),
- ("UP_ARROW", "UP_ARROW", ""),
- ("NUMPAD_1", "NUMPAD_1", ""),
- ("NUMPAD_2", "NUMPAD_2", ""),
- ("NUMPAD_3", "NUMPAD_3", ""),
- ("NUMPAD_4", "NUMPAD_4", ""),
- ("NUMPAD_5", "NUMPAD_5", ""),
- ("NUMPAD_6", "NUMPAD_6", ""),
- ("NUMPAD_7", "NUMPAD_7", ""),
- ("NUMPAD_8", "NUMPAD_8", ""),
- ("NUMPAD_9", "NUMPAD_9", ""),
- ("NUMPAD_0", "NUMPAD_0", ""),
- ("NUMPAD_PERIOD", "NUMPAD_PERIOD", ""),
- ("NUMPAD_SLASH", "NUMPAD_SLASH", ""),
- ("NUMPAD_ASTERIX", "NUMPAD_ASTERIX", ""),
- ("NUMPAD_MINUS", "NUMPAD_MINUS", ""),
- ("NUMPAD_ENTER", "NUMPAD_ENTER", ""),
- ("NUMPAD_PLUS", "NUMPAD_PLUS", ""),
- ("F1", "F1", ""),
- ("F2", "F2", ""),
- ("F3", "F3", ""),
- ("F4", "F4", ""),
- ("F5", "F5", ""),
- ("F6", "F6", ""),
- ("F7", "F7", ""),
- ("F8", "F8", ""),
- ("F9", "F9", ""),
- ("F10", "F10", ""),
- ("F11", "F11", ""),
- ("F12", "F12", ""),
- ("F13", "F13", ""),
- ("F14", "F14", ""),
- ("F15", "F15", ""),
- ("F16", "F16", ""),
- ("F17", "F17", ""),
- ("F18", "F18", ""),
- ("F19", "F19", ""),
- ("PAUSE", "PAUSE", ""),
- ("INSERT", "INSERT", ""),
- ("HOME", "HOME", ""),
- ("PAGE_UP", "PAGE_UP", ""),
- ("PAGE_DOWN", "PAGE_DOWN", ""),
- ("END", "END", ""),
- ("MEDIA_PLAY", "MEDIA_PLAY", ""),
- ("MEDIA_STOP", "MEDIA_STOP", ""),
- ("MEDIA_FIRST", "MEDIA_FIRST", ""),
- ("MEDIA_LAST", "MEDIA_LAST", ""),
- ("TEXTINPUT", "TEXTINPUT", ""),
- ("WINDOW_DEACTIVATE", "WINDOW_DEACTIVATE", ""),
- ("TIMER", "TIMER", ""),
- ("TIMER0", "TIMER0", ""),
- ("TIMER1", "TIMER1", ""),
- ("TIMER2", "TIMER2", ""),
- ("TIMER_JOBS", "TIMER_JOBS", ""),
- ("TIMER_AUTOSAVE", "TIMER_AUTOSAVE", ""),
- ("TIMER_REPORT", "TIMER_REPORT", ""),
- ("TIMERREGION", "TIMERREGION", ""),
- ("NDOF_MOTION", "NDOF_MOTION", ""),
- ("NDOF_BUTTON_MENU", "NDOF_BUTTON_MENU", ""),
- ("NDOF_BUTTON_FIT", "NDOF_BUTTON_FIT", ""),
- ("NDOF_BUTTON_TOP", "NDOF_BUTTON_TOP", ""),
- ("NDOF_BUTTON_BOTTOM", "NDOF_BUTTON_BOTTOM", ""),
- ("NDOF_BUTTON_LEFT", "NDOF_BUTTON_LEFT", ""),
- ("NDOF_BUTTON_RIGHT", "NDOF_BUTTON_RIGHT", ""),
- ("NDOF_BUTTON_FRONT", "NDOF_BUTTON_FRONT", ""),
- ("NDOF_BUTTON_BACK", "NDOF_BUTTON_BACK", ""),
- ("NDOF_BUTTON_ISO1", "NDOF_BUTTON_ISO1", ""),
- ("NDOF_BUTTON_ISO2", "NDOF_BUTTON_ISO2", ""),
- ("NDOF_BUTTON_ROLL_CW", "NDOF_BUTTON_ROLL_CW", ""),
- ("NDOF_BUTTON_ROLL_CCW", "NDOF_BUTTON_ROLL_CCW", ""),
- ("NDOF_BUTTON_SPIN_CW", "NDOF_BUTTON_SPIN_CW", ""),
- ("NDOF_BUTTON_SPIN_CCW", "NDOF_BUTTON_SPIN_CCW", ""),
- ("NDOF_BUTTON_TILT_CW", "NDOF_BUTTON_TILT_CW", ""),
- ("NDOF_BUTTON_TILT_CCW", "NDOF_BUTTON_TILT_CCW", ""),
- ("NDOF_BUTTON_ROTATE", "NDOF_BUTTON_ROTATE", ""),
- ("NDOF_BUTTON_PANZOOM", "NDOF_BUTTON_PANZOOM", ""),
- ("NDOF_BUTTON_DOMINANT", "NDOF_BUTTON_DOMINANT", ""),
- ("NDOF_BUTTON_PLUS", "NDOF_BUTTON_PLUS", ""),
- ("NDOF_BUTTON_MINUS", "NDOF_BUTTON_MINUS", ""),
- ("NDOF_BUTTON_ESC", "NDOF_BUTTON_ESC", ""),
- ("NDOF_BUTTON_ALT", "NDOF_BUTTON_ALT", ""),
- ("NDOF_BUTTON_SHIFT", "NDOF_BUTTON_SHIFT", ""),
- ("NDOF_BUTTON_CTRL", "NDOF_BUTTON_CTRL", ""),
- ("NDOF_BUTTON_1", "NDOF_BUTTON_1", ""),
- ("NDOF_BUTTON_2", "NDOF_BUTTON_2", ""),
- ("NDOF_BUTTON_3", "NDOF_BUTTON_3", ""),
- ("NDOF_BUTTON_4", "NDOF_BUTTON_4", ""),
- ("NDOF_BUTTON_5", "NDOF_BUTTON_5", ""),
- ("NDOF_BUTTON_6", "NDOF_BUTTON_6", ""),
- ("NDOF_BUTTON_7", "NDOF_BUTTON_7", ""),
- ("NDOF_BUTTON_8", "NDOF_BUTTON_8", ""),
- ("NDOF_BUTTON_9", "NDOF_BUTTON_9", ""),
- ("NDOF_BUTTON_10", "NDOF_BUTTON_10", ""),
- ("NDOF_BUTTON_A", "NDOF_BUTTON_A", ""),
- ("NDOF_BUTTON_B", "NDOF_BUTTON_B", ""),
- ("NDOF_BUTTON_C", "NDOF_BUTTON_C", "")
- ),
- name="Quick Type",
- description="Enter key code in find text",
- update=update_data
- )
+ items=(
+ ('NONE', "Select key", ""),
+ ("LEFTMOUSE", "LEFTMOUSE", ""),
+ ("MIDDLEMOUSE", "MIDDLEMOUSE", ""),
+ ("RIGHTMOUSE", "RIGHTMOUSE", ""),
+ ("BUTTON4MOUSE", "BUTTON4MOUSE", ""),
+ ("BUTTON5MOUSE", "BUTTON5MOUSE", ""),
+ ("BUTTON6MOUSE", "BUTTON6MOUSE", ""),
+ ("BUTTON7MOUSE", "BUTTON7MOUSE", ""),
+ ("ACTIONMOUSE", "ACTIONMOUSE", ""),
+ ("SELECTMOUSE", "SELECTMOUSE", ""),
+ ("MOUSEMOVE", "MOUSEMOVE", ""),
+ ("INBETWEEN_MOUSEMOVE", "INBETWEEN_MOUSEMOVE", ""),
+ ("TRACKPADPAN", "TRACKPADPAN", ""),
+ ("TRACKPADZOOM", "TRACKPADZOOM", ""),
+ ("MOUSEROTATE", "MOUSEROTATE", ""),
+ ("WHEELUPMOUSE", "WHEELUPMOUSE", ""),
+ ("WHEELDOWNMOUSE", "WHEELDOWNMOUSE", ""),
+ ("WHEELINMOUSE", "WHEELINMOUSE", ""),
+ ("WHEELOUTMOUSE", "WHEELOUTMOUSE", ""),
+ ("EVT_TWEAK_L", "EVT_TWEAK_L", ""),
+ ("EVT_TWEAK_M", "EVT_TWEAK_M", ""),
+ ("EVT_TWEAK_R", "EVT_TWEAK_R", ""),
+ ("EVT_TWEAK_A", "EVT_TWEAK_A", ""),
+ ("EVT_TWEAK_S", "EVT_TWEAK_S", ""),
+ ("A", "A", ""),
+ ("B", "B", ""),
+ ("C", "C", ""),
+ ("D", "D", ""),
+ ("E", "E", ""),
+ ("F", "F", ""),
+ ("G", "G", ""),
+ ("H", "H", ""),
+ ("I", "I", ""),
+ ("J", "J", ""),
+ ("K", "K", ""),
+ ("L", "L", ""),
+ ("M", "M", ""),
+ ("N", "N", ""),
+ ("O", "O", ""),
+ ("P", "P", ""),
+ ("Q", "Q", ""),
+ ("R", "R", ""),
+ ("S", "S", ""),
+ ("T", "T", ""),
+ ("U", "U", ""),
+ ("V", "V", ""),
+ ("W", "W", ""),
+ ("X", "X", ""),
+ ("Y", "Y", ""),
+ ("Z", "Z", ""),
+ ("ZERO", "ZERO", ""),
+ ("ONE", "ONE", ""),
+ ("TWO", "TWO", ""),
+ ("THREE", "THREE", ""),
+ ("FOUR", "FOUR", ""),
+ ("FIVE", "FIVE", ""),
+ ("SIX", "SIX", ""),
+ ("SEVEN", "SEVEN", ""),
+ ("EIGHT", "EIGHT", ""),
+ ("NINE", "NINE", ""),
+ ("LEFT_CTRL", "LEFT_CTRL", ""),
+ ("LEFT_ALT", "LEFT_ALT", ""),
+ ("LEFT_SHIFT", "LEFT_SHIFT", ""),
+ ("RIGHT_ALT", "RIGHT_ALT", ""),
+ ("RIGHT_CTRL", "RIGHT_CTRL", ""),
+ ("RIGHT_SHIFT", "RIGHT_SHIFT", ""),
+ ("OSKEY", "OSKEY", ""),
+ ("GRLESS", "GRLESS", ""),
+ ("ESC", "ESC", ""),
+ ("TAB", "TAB", ""),
+ ("RET", "RET", ""),
+ ("SPACE", "SPACE", ""),
+ ("LINE_FEED", "LINE_FEED", ""),
+ ("BACK_SPACE", "BACK_SPACE", ""),
+ ("DEL", "DEL", ""),
+ ("SEMI_COLON", "SEMI_COLON", ""),
+ ("PERIOD", "PERIOD", ""),
+ ("COMMA", "COMMA", ""),
+ ("QUOTE", "QUOTE", ""),
+ ("ACCENT_GRAVE", "ACCENT_GRAVE", ""),
+ ("MINUS", "MINUS", ""),
+ ("SLASH", "SLASH", ""),
+ ("BACK_SLASH", "BACK_SLASH", ""),
+ ("EQUAL", "EQUAL", ""),
+ ("LEFT_BRACKET", "LEFT_BRACKET", ""),
+ ("RIGHT_BRACKET", "RIGHT_BRACKET", ""),
+ ("LEFT_ARROW", "LEFT_ARROW", ""),
+ ("DOWN_ARROW", "DOWN_ARROW", ""),
+ ("RIGHT_ARROW", "RIGHT_ARROW", ""),
+ ("UP_ARROW", "UP_ARROW", ""),
+ ("NUMPAD_1", "NUMPAD_1", ""),
+ ("NUMPAD_2", "NUMPAD_2", ""),
+ ("NUMPAD_3", "NUMPAD_3", ""),
+ ("NUMPAD_4", "NUMPAD_4", ""),
+ ("NUMPAD_5", "NUMPAD_5", ""),
+ ("NUMPAD_6", "NUMPAD_6", ""),
+ ("NUMPAD_7", "NUMPAD_7", ""),
+ ("NUMPAD_8", "NUMPAD_8", ""),
+ ("NUMPAD_9", "NUMPAD_9", ""),
+ ("NUMPAD_0", "NUMPAD_0", ""),
+ ("NUMPAD_PERIOD", "NUMPAD_PERIOD", ""),
+ ("NUMPAD_SLASH", "NUMPAD_SLASH", ""),
+ ("NUMPAD_ASTERIX", "NUMPAD_ASTERIX", ""),
+ ("NUMPAD_MINUS", "NUMPAD_MINUS", ""),
+ ("NUMPAD_ENTER", "NUMPAD_ENTER", ""),
+ ("NUMPAD_PLUS", "NUMPAD_PLUS", ""),
+ ("F1", "F1", ""),
+ ("F2", "F2", ""),
+ ("F3", "F3", ""),
+ ("F4", "F4", ""),
+ ("F5", "F5", ""),
+ ("F6", "F6", ""),
+ ("F7", "F7", ""),
+ ("F8", "F8", ""),
+ ("F9", "F9", ""),
+ ("F10", "F10", ""),
+ ("F11", "F11", ""),
+ ("F12", "F12", ""),
+ ("F13", "F13", ""),
+ ("F14", "F14", ""),
+ ("F15", "F15", ""),
+ ("F16", "F16", ""),
+ ("F17", "F17", ""),
+ ("F18", "F18", ""),
+ ("F19", "F19", ""),
+ ("PAUSE", "PAUSE", ""),
+ ("INSERT", "INSERT", ""),
+ ("HOME", "HOME", ""),
+ ("PAGE_UP", "PAGE_UP", ""),
+ ("PAGE_DOWN", "PAGE_DOWN", ""),
+ ("END", "END", ""),
+ ("MEDIA_PLAY", "MEDIA_PLAY", ""),
+ ("MEDIA_STOP", "MEDIA_STOP", ""),
+ ("MEDIA_FIRST", "MEDIA_FIRST", ""),
+ ("MEDIA_LAST", "MEDIA_LAST", ""),
+ ("TEXTINPUT", "TEXTINPUT", ""),
+ ("WINDOW_DEACTIVATE", "WINDOW_DEACTIVATE", ""),
+ ("TIMER", "TIMER", ""),
+ ("TIMER0", "TIMER0", ""),
+ ("TIMER1", "TIMER1", ""),
+ ("TIMER2", "TIMER2", ""),
+ ("TIMER_JOBS", "TIMER_JOBS", ""),
+ ("TIMER_AUTOSAVE", "TIMER_AUTOSAVE", ""),
+ ("TIMER_REPORT", "TIMER_REPORT", ""),
+ ("TIMERREGION", "TIMERREGION", ""),
+ ("NDOF_MOTION", "NDOF_MOTION", ""),
+ ("NDOF_BUTTON_MENU", "NDOF_BUTTON_MENU", ""),
+ ("NDOF_BUTTON_FIT", "NDOF_BUTTON_FIT", ""),
+ ("NDOF_BUTTON_TOP", "NDOF_BUTTON_TOP", ""),
+ ("NDOF_BUTTON_BOTTOM", "NDOF_BUTTON_BOTTOM", ""),
+ ("NDOF_BUTTON_LEFT", "NDOF_BUTTON_LEFT", ""),
+ ("NDOF_BUTTON_RIGHT", "NDOF_BUTTON_RIGHT", ""),
+ ("NDOF_BUTTON_FRONT", "NDOF_BUTTON_FRONT", ""),
+ ("NDOF_BUTTON_BACK", "NDOF_BUTTON_BACK", ""),
+ ("NDOF_BUTTON_ISO1", "NDOF_BUTTON_ISO1", ""),
+ ("NDOF_BUTTON_ISO2", "NDOF_BUTTON_ISO2", ""),
+ ("NDOF_BUTTON_ROLL_CW", "NDOF_BUTTON_ROLL_CW", ""),
+ ("NDOF_BUTTON_ROLL_CCW", "NDOF_BUTTON_ROLL_CCW", ""),
+ ("NDOF_BUTTON_SPIN_CW", "NDOF_BUTTON_SPIN_CW", ""),
+ ("NDOF_BUTTON_SPIN_CCW", "NDOF_BUTTON_SPIN_CCW", ""),
+ ("NDOF_BUTTON_TILT_CW", "NDOF_BUTTON_TILT_CW", ""),
+ ("NDOF_BUTTON_TILT_CCW", "NDOF_BUTTON_TILT_CCW", ""),
+ ("NDOF_BUTTON_ROTATE", "NDOF_BUTTON_ROTATE", ""),
+ ("NDOF_BUTTON_PANZOOM", "NDOF_BUTTON_PANZOOM", ""),
+ ("NDOF_BUTTON_DOMINANT", "NDOF_BUTTON_DOMINANT", ""),
+ ("NDOF_BUTTON_PLUS", "NDOF_BUTTON_PLUS", ""),
+ ("NDOF_BUTTON_MINUS", "NDOF_BUTTON_MINUS", ""),
+ ("NDOF_BUTTON_ESC", "NDOF_BUTTON_ESC", ""),
+ ("NDOF_BUTTON_ALT", "NDOF_BUTTON_ALT", ""),
+ ("NDOF_BUTTON_SHIFT", "NDOF_BUTTON_SHIFT", ""),
+ ("NDOF_BUTTON_CTRL", "NDOF_BUTTON_CTRL", ""),
+ ("NDOF_BUTTON_1", "NDOF_BUTTON_1", ""),
+ ("NDOF_BUTTON_2", "NDOF_BUTTON_2", ""),
+ ("NDOF_BUTTON_3", "NDOF_BUTTON_3", ""),
+ ("NDOF_BUTTON_4", "NDOF_BUTTON_4", ""),
+ ("NDOF_BUTTON_5", "NDOF_BUTTON_5", ""),
+ ("NDOF_BUTTON_6", "NDOF_BUTTON_6", ""),
+ ("NDOF_BUTTON_7", "NDOF_BUTTON_7", ""),
+ ("NDOF_BUTTON_8", "NDOF_BUTTON_8", ""),
+ ("NDOF_BUTTON_9", "NDOF_BUTTON_9", ""),
+ ("NDOF_BUTTON_10", "NDOF_BUTTON_10", ""),
+ ("NDOF_BUTTON_A", "NDOF_BUTTON_A", ""),
+ ("NDOF_BUTTON_B", "NDOF_BUTTON_B", ""),
+ ("NDOF_BUTTON_C", "NDOF_BUTTON_C", "")
+ ),
+ name="Quick Type",
+ description="Enter key code in find text",
+ update=update_data
+ )
+
+
+class IsKeyFreeRunExportKeys(Operator):
+ bl_idname = "iskeyfree.run_export_keys"
+ bl_label = "List all Shortcuts"
+ bl_description = ("List all existing shortcuts in a text block\n"
+ "The newly generated list will be made active in the Text Editor\n"
+ "To access the previous ones, select them from the Header dropdown")
+
+ def all_shortcuts_name(self, context):
+ new_name, def_name, ext = "", "All_Shortcuts", ".txt"
+ suffix = 1
+ try:
+ # first slap a simple linear count + 1 for numeric suffix, if it fails
+ # harvest for the rightmost numbers and append the max value
+ list_txt = []
+ data_txt = bpy.data.texts
+ list_txt = [txt.name for txt in data_txt if txt.name.startswith("All_Shortcuts")]
+ new_name = "{}_{}{}".format(def_name, len(list_txt) + 1, ext)
+
+ if new_name in list_txt:
+ from re import findall
+ test_num = [findall("\d+", words) for words in list_txt]
+ suffix += max([int(l[-1]) for l in test_num])
+ new_name = "{}_{}{}".format(def_name, suffix, ext)
+ return new_name
+ except:
+ return None
+
+ def execute(self, context):
+ wm = bpy.context.window_manager
+ from collections import defaultdict
+ mykeys = defaultdict(list)
+ file_name = self.all_shortcuts_name(context) or "All_Shortcut.txt"
+ start_note = "# Note: Some of the shortcuts entries don't have a name. Mostly Modal stuff\n"
+ col_width, col_shortcuts = 2, 2
+
+ for ctx_type, keyboardmap in wm.keyconfigs.user.keymaps.items():
+ for myitem in keyboardmap.keymap_items:
+ padding = len(myitem.name)
+ col_width = padding + 2 if padding > col_width else col_width
+
+ short_type = myitem.type if myitem.type else "UNKNOWN"
+ is_ctrl = " Ctrl" if myitem.ctrl is True else ""
+ is_alt = " Alt" if myitem.alt is True else ""
+ is_shift = " Shift" if myitem.shift is True else ""
+ is_oskey = " OsKey" if myitem.oskey is True else ""
+ short_cuts = "{}{}{}{}{}".format(short_type, is_ctrl, is_alt, is_shift, is_oskey)
+
+ t = (
+ myitem.name if myitem.name else "No Name",
+ short_cuts,
+ )
+ mykeys[ctx_type].append(t)
+ padding_s = len(short_cuts) + 2
+ col_shortcuts = padding_s if padding_s > col_shortcuts else col_shortcuts
+
+ max_line = col_shortcuts + col_width + 4
+ textblock = bpy.data.texts.new(file_name)
+ total = sum([len(mykeys[ctxs]) for ctxs in mykeys])
+ textblock.write('# %d Total Shortcuts\n\n' % total)
+ textblock.write(start_note)
+
+ for ctx in mykeys:
+ textblock.write("\n[%s]\nEntries: %s\n\n" % (ctx, len(mykeys[ctx])))
+ line_k = sorted(mykeys[ctx])
+ for keys in line_k:
+ add_ticks = "-" * (max_line - (len(keys[0]) + len(keys[1])))
+ entries = "{ticks} {entry}".format(ticks=add_ticks, entry=keys[1])
+ textblock.write("{name} {entry}\n".format(name=keys[0], entry=entries))
+
+ textblock.write("\n\n")
+
+ # try to set the created text block to active
+ if context.area.type in {"TEXT_EDITOR"}:
+ bpy.context.space_data.text = bpy.data.texts[file_name]
+
+ self.report({'INFO'}, "See %s textblock" % file_name)
+
+ return {"FINISHED"}
# -----------------------------------------------------
# Registration
# ------------------------------------------------------
+classes = (
+ IskeyFreeProperties,
+ RunActionCheck,
+ UIControlPanel,
+ IsKeyFreeRunExportKeys,
+)
+
def register():
- bpy.utils.register_module(__name__)
+ for cls in classes:
+ bpy.utils.register_class(cls)
bpy.types.Scene.is_keyfree = PointerProperty(type=IskeyFreeProperties)
def unregister():
- bpy.utils.unregister_module(__name__)
+ for cls in classes:
+ bpy.utils.unregister_class(cls)
del bpy.types.Scene.is_keyfree
diff --git a/development_ui_classes.py b/development_ui_classes.py
index 18b692f4..f03b7bb5 100644
--- a/development_ui_classes.py
+++ b/development_ui_classes.py
@@ -21,7 +21,7 @@
bl_info = {
"name": "UI Classes Overview",
"author": "lijenstina",
- "version": (1, 0, 1),
+ "version": (1, 0, 2),
"blender": (2, 78, 0),
"location": "Text Editor > Properties",
"description": "Print the UI classes in a text-block",
@@ -33,15 +33,15 @@ bl_info = {
import bpy
from bpy.types import (
- Operator,
- Panel,
- PropertyGroup,
- )
+ Operator,
+ Panel,
+ PropertyGroup,
+)
from bpy.props import (
- BoolProperty,
- EnumProperty,
- PointerProperty,
- )
+ BoolProperty,
+ EnumProperty,
+ PointerProperty,
+)
class TEXT_PT_ui_cheat_sheet(Panel):
@@ -128,7 +128,10 @@ class TEXT_OT_ui_cheat_sheet(Operator):
textblock.write(('\n' if not searchable else '').join(sorted(op_string_ui[cls_name])))
textblock.write(close_line)
- context.space_data.text = bpy.data.texts[file_name]
+ # try to set the created text block to active
+ if context.area.type in {"TEXT_EDITOR"}:
+ bpy.context.space_data.text = bpy.data.texts[file_name]
+
self.report({'INFO'}, "See %s textblock" % file_name)
return {'FINISHED'}
@@ -152,21 +155,21 @@ class text_ui_cheat_props(PropertyGroup):
('Header', "Headers", "Print Header UI types"),
),
default='all',
- )
+ )
searchable = BoolProperty(
name="Format searchable",
description="Generate the list as Python dictionary,\n"
"using the format Class: Path",
default=False
- )
+ )
# Register
classes = (
- TEXT_OT_ui_cheat_sheet,
- TEXT_PT_ui_cheat_sheet,
- text_ui_cheat_props
- )
+ TEXT_OT_ui_cheat_sheet,
+ TEXT_PT_ui_cheat_sheet,
+ text_ui_cheat_props
+)
def register():
diff --git a/io_export_after_effects.py b/io_export_after_effects.py
index aafa8ecb..4a5c6b92 100644
--- a/io_export_after_effects.py
+++ b/io_export_after_effects.py
@@ -23,8 +23,8 @@ bl_info = {
"description": "Export cameras, selected objects & camera solution "
"3D Markers to Adobe After Effects CS3 and above",
"author": "Bartek Skorupa",
- "version": (0, 64),
- "blender": (2, 69, 0),
+ "version": (0, 65),
+ "blender": (2, 79, 0),
"location": "File > Export > Adobe After Effects (.jsx)",
"warning": "",
"wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
@@ -150,7 +150,7 @@ def convert_name(name):
# get object's blender's location rotation and scale and return AE's Position, Rotation/Orientation and scale
# this function will be called for every object for every frame
-def convert_transform_matrix(matrix, width, height, aspect, x_rot_correction=False):
+def convert_transform_matrix(matrix, width, height, aspect, x_rot_correction=False, ae_size=100.0):
# get blender transform data for ob
b_loc = matrix.to_translation()
@@ -159,9 +159,9 @@ def convert_transform_matrix(matrix, width, height, aspect, x_rot_correction=Fal
# convert to AE Position Rotation and Scale
# Axes in AE are different. AE's X is blender's X, AE's Y is negative Blender's Z, AE's Z is Blender's Y
- x = (b_loc.x * 100.0) / aspect + width / 2.0 # calculate AE's X position
- y = (-b_loc.z * 100.0) + (height / 2.0) # calculate AE's Y position
- z = b_loc.y * 100.0 # calculate AE's Z position
+ x = (b_loc.x * ae_size) / aspect + width / 2.0 # calculate AE's X position
+ y = (-b_loc.z * ae_size) + (height / 2.0) # calculate AE's Y position
+ z = b_loc.y * ae_size # calculate AE's Z position
# Convert rotations to match AE's orientation.
rx = degrees(b_rot.x) # if not x_rot_correction - AE's X orientation = blender's X rotation if 'ZYX' euler.
ry = -degrees(b_rot.y) # AE's Y orientation is negative blender's Y rotation if 'ZYX' euler
@@ -240,7 +240,7 @@ def convert_lens(camera, width, height, aspect):
# jsx script for AE creation
-def write_jsx_file(file, data, selection, include_animation, include_active_cam, include_selected_cams, include_selected_objects, include_cam_bundles):
+def write_jsx_file(file, data, selection, include_animation, include_active_cam, include_selected_cams, include_selected_objects, include_cam_bundles, ae_size):
print("\n---------------------------\n- Export to After Effects -\n---------------------------")
# store the current frame to restore it at the end of export
@@ -382,7 +382,7 @@ def write_jsx_file(file, data, selection, include_animation, include_active_cam,
# bundles are in camera space. Transpose to world space
matrix = Matrix.Translation(cam.matrix_basis.copy() * track.bundle)
# convert the position into AE space
- ae_transform = convert_transform_matrix(matrix, data['width'], data['height'], data['aspect'], x_rot_correction=False)
+ ae_transform = convert_transform_matrix(matrix, data['width'], data['height'], data['aspect'], False, ae_size)
js_data['bundles_cam'][name_ae]['position'] += '[%f,%f,%f],' % (ae_transform[0], ae_transform[1], ae_transform[2])
# get all keyframes for each object and store in dico
@@ -407,7 +407,7 @@ def write_jsx_file(file, data, selection, include_animation, include_active_cam,
# get cam name
name_ae = active_cam_name
# convert cam transform properties to AE space
- ae_transform = convert_transform_matrix(active_cam.matrix_world.copy(), data['width'], data['height'], data['aspect'], x_rot_correction=True)
+ ae_transform = convert_transform_matrix(active_cam.matrix_world.copy(), data['width'], data['height'], data['aspect'], True, ae_size)
# convert Blender's lens to AE's zoom in pixels
zoom = convert_lens(active_cam, data['width'], data['height'], data['aspect'])
# store all values in dico
@@ -437,7 +437,7 @@ def write_jsx_file(file, data, selection, include_animation, include_active_cam,
# get cam name
name_ae = selection['cameras'][i][1]
# convert cam transform properties to AE space
- ae_transform = convert_transform_matrix(cam[0].matrix_world.copy(), data['width'], data['height'], data['aspect'], x_rot_correction=True)
+ ae_transform = convert_transform_matrix(cam[0].matrix_world.copy(), data['width'], data['height'], data['aspect'], True, ae_size)
# convert Blender's lens to AE's zoom in pixels
zoom = convert_lens(cam[0], data['width'], data['height'], data['aspect'])
# store all values in dico
@@ -475,7 +475,7 @@ def write_jsx_file(file, data, selection, include_animation, include_active_cam,
name_ae = selection['lights'][i][1]
type = selection['lights'][i][0].data.type
# convert ob transform properties to AE space
- ae_transform = convert_transform_matrix(ob[0].matrix_world.copy(), data['width'], data['height'], data['aspect'], x_rot_correction=True)
+ ae_transform = convert_transform_matrix(ob[0].matrix_world.copy(), data['width'], data['height'], data['aspect'], True, ae_size)
color = ob[0].data.color
# store all values in dico
position = '[%f,%f,%f],' % (ae_transform[0], ae_transform[1], ae_transform[2])
@@ -522,7 +522,7 @@ def write_jsx_file(file, data, selection, include_animation, include_active_cam,
# get object name
name_ae = selection['nulls'][i][1]
# convert ob transform properties to AE space
- ae_transform = convert_transform_matrix(ob[0].matrix_world.copy(), data['width'], data['height'], data['aspect'], x_rot_correction=True)
+ ae_transform = convert_transform_matrix(ob[0].matrix_world.copy(), data['width'], data['height'], data['aspect'], True, ae_size)
# store all values in dico
position = '[%f,%f,%f],' % (ae_transform[0], ae_transform[1], ae_transform[2])
orientation = '[%f,%f,%f],' % (ae_transform[3], ae_transform[4], ae_transform[5])
@@ -676,10 +676,10 @@ def write_jsx_file(file, data, selection, include_animation, include_active_cam,
##########################################
-def main(file, context, include_animation, include_active_cam, include_selected_cams, include_selected_objects, include_cam_bundles):
+def main(file, context, include_animation, include_active_cam, include_selected_cams, include_selected_objects, include_cam_bundles, ae_size):
data = get_comp_data(context)
selection = get_selected(context)
- write_jsx_file(file, data, selection, include_animation, include_active_cam, include_selected_cams, include_selected_objects, include_cam_bundles)
+ write_jsx_file(file, data, selection, include_animation, include_active_cam, include_selected_cams, include_selected_objects, include_cam_bundles, ae_size)
print ("\nExport to After Effects Completed")
return {'FINISHED'}
@@ -688,7 +688,7 @@ def main(file, context, include_animation, include_active_cam, include_selected_
##########################################
from bpy_extras.io_utils import ExportHelper
-from bpy.props import StringProperty, BoolProperty
+from bpy.props import StringProperty, BoolProperty, FloatProperty
class ExportJsx(bpy.types.Operator, ExportHelper):
@@ -728,11 +728,18 @@ class ExportJsx(bpy.types.Operator, ExportHelper):
# description="Include 3D Markers of Object Motion Solution for selected cameras",
# default=True,
# )
+ ae_size = FloatProperty(
+ name="AE Size",
+ description="Size of AE Composition (pixels per 1BU)",
+ default=100.0,
+ )
def draw(self, context):
layout = self.layout
box = layout.box()
+ box.label('Size fo AE Comp (pixels per 1 BU)')
+ box.prop(self, 'ae_size')
box.label('Animation:')
box.prop(self, 'include_animation')
box.label('Include Cameras and Objects:')
@@ -752,7 +759,7 @@ class ExportJsx(bpy.types.Operator, ExportHelper):
return ok
def execute(self, context):
- return main(self.filepath, context, self.include_animation, self.include_active_cam, self.include_selected_cams, self.include_selected_objects, self.include_cam_bundles)
+ return main(self.filepath, context, self.include_animation, self.include_active_cam, self.include_selected_cams, self.include_selected_objects, self.include_cam_bundles, self.ae_size)
def menu_func(self, context):
diff --git a/io_scene_fbx/__init__.py b/io_scene_fbx/__init__.py
index 943df0b4..9611cb1f 100644
--- a/io_scene_fbx/__init__.py
+++ b/io_scene_fbx/__init__.py
@@ -21,7 +21,7 @@
bl_info = {
"name": "FBX format",
"author": "Campbell Barton, Bastien Montagne, Jens Restemeier",
- "version": (3, 8, 4),
+ "version": (3, 9, 1),
"blender": (2, 79, 1),
"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 58bd4586..b75a8977 100644
--- a/io_scene_fbx/export_fbx_bin.py
+++ b/io_scene_fbx/export_fbx_bin.py
@@ -1896,8 +1896,9 @@ def fbx_animations_do(scene_data, ref_id, f_start, f_end, start_zero, objects=No
ACNW(ob_obj.key, 'LCL_SCALING', force_key, force_sek, scale))
p_rots[ob_obj] = rot
- animdata_shapes = OrderedDict()
force_key = (simplify_fac == 0.0)
+
+ animdata_shapes = OrderedDict()
for me, (me_key, _shapes_key, shapes) in scene_data.data_deformers_shape.items():
# Ignore absolute shape keys for now!
if not me.shape_keys.use_relative:
@@ -1908,6 +1909,12 @@ def fbx_animations_do(scene_data, ref_id, f_start, f_end, start_zero, objects=No
acnode.add_group(me_key, shape.name, shape.name, (shape.name,))
animdata_shapes[channel_key] = (acnode, me, shape)
+ animdata_cameras = OrderedDict()
+ for cam_obj, cam_key in scene_data.data_cameras.items():
+ cam = cam_obj.bdata.data
+ acnode = AnimationCurveNodeWrapper(cam_key, 'CAMERA_FOCAL', force_key, force_sek, (cam.lens,))
+ animdata_cameras[cam_key] = (acnode, cam)
+
currframe = f_start
while currframe <= f_end:
real_currframe = currframe - f_start if start_zero else currframe
@@ -1927,6 +1934,8 @@ def fbx_animations_do(scene_data, ref_id, f_start, f_end, start_zero, objects=No
ob_obj.dupli_list_clear()
for anim_shape, me, shape in animdata_shapes.values():
anim_shape.add_keyframe(real_currframe, (shape.value * 100.0,))
+ for anim_camera, camera in animdata_cameras.values():
+ anim_camera.add_keyframe(real_currframe, (camera.lens,))
currframe += bake_step
scene.frame_set(back_currframe, 0.0)
@@ -1958,6 +1967,18 @@ def fbx_animations_do(scene_data, ref_id, f_start, f_end, start_zero, objects=No
anim_data = animations[elem_key] = ("dummy_unused_key", OrderedDict())
anim_data[1][fbx_group] = (group_key, group, fbx_gname)
+ # And cameras' lens keys.
+ for cam_key, (anim_camera, camera) in animdata_cameras.items():
+ final_keys = OrderedDict()
+ anim_camera.simplify(simplify_fac, bake_step, force_keep)
+ if not anim_camera:
+ continue
+ for elem_key, group_key, group, fbx_group, fbx_gname in anim_camera.get_final_data(scene, ref_id, force_keep):
+ anim_data = animations.get(elem_key)
+ if anim_data is None:
+ anim_data = animations[elem_key] = ("dummy_unused_key", OrderedDict())
+ anim_data[1][fbx_group] = (group_key, group, fbx_gname)
+
astack_key = get_blender_anim_stack_key(scene, ref_id)
alayer_key = get_blender_anim_layer_key(scene, ref_id)
name = (get_blenderID_name(ref_id) if ref_id else scene.name).encode()
@@ -2194,8 +2215,11 @@ def fbx_data_from_scene(scene, settings):
if mod.show_render:
use_org_data = False
if not use_org_data:
- tmp_me = ob.to_mesh(scene, apply_modifiers=True,
- settings='RENDER' if settings.use_mesh_modifiers_render else 'PREVIEW')
+ tmp_me = ob.to_mesh(
+ scene,
+ apply_modifiers=settings.use_mesh_modifiers,
+ settings='RENDER' if settings.use_mesh_modifiers_render else 'PREVIEW',
+ )
data_meshes[ob_obj] = (get_blenderID_key(tmp_me), tmp_me, True)
# Re-enable temporary disabled modifiers.
for mod, show_render in tmp_mods:
diff --git a/io_scene_fbx/fbx_utils.py b/io_scene_fbx/fbx_utils.py
index 16a8256f..82e17fe2 100644
--- a/io_scene_fbx/fbx_utils.py
+++ b/io_scene_fbx/fbx_utils.py
@@ -94,6 +94,7 @@ FBX_LIGHT_DECAY_TYPES = {
'CONSTANT': 0, # None.
'INVERSE_LINEAR': 1, # Linear.
'INVERSE_SQUARE': 2, # Quadratic.
+ 'INVERSE_COEFFICIENTS': 2, # Quadratic...
'CUSTOM_CURVE': 2, # Quadratic.
'LINEAR_QUADRATIC_WEIGHTED': 2, # Quadratic.
}
@@ -728,6 +729,7 @@ class AnimationCurveNodeWrapper:
'LCL_ROTATION': ("Lcl Rotation", "R", ("X", "Y", "Z")),
'LCL_SCALING': ("Lcl Scaling", "S", ("X", "Y", "Z")),
'SHAPE_KEY': ("DeformPercent", "DeformPercent", ("DeformPercent",)),
+ 'CAMERA_FOCAL': ("FocalLength", "FocalLength", ("FocalLength",)),
}
def __init__(self, elem_key, kind, force_keying, force_startend_keying, default_values=...):
diff --git a/io_scene_fbx/import_fbx.py b/io_scene_fbx/import_fbx.py
index 4757d2ef..446be0ff 100644
--- a/io_scene_fbx/import_fbx.py
+++ b/io_scene_fbx/import_fbx.py
@@ -64,6 +64,18 @@ MAT_CONVERT_LAMP = fbx_utils.MAT_CONVERT_LAMP.inverted()
MAT_CONVERT_CAMERA = fbx_utils.MAT_CONVERT_CAMERA.inverted()
+def validate_blend_names(name):
+ assert(type(name) == bytes)
+ # Blender typically does not accept names over 63 bytes...
+ if len(name) > 63:
+ import hashlib
+ h = hashlib.sha1(name).hexdigest()
+ return name[:55].decode('utf-8', 'replace') + "_" + h[:7]
+ else:
+ # We use 'replace' even though FBX 'specs' say it should always be utf8, see T53841.
+ return name.decode('utf-8', 'replace')
+
+
def elem_find_first(elem, id_search, default=None):
for fbx_item in elem.elems:
if fbx_item.id == id_search:
@@ -82,7 +94,7 @@ def elem_find_first_string(elem, id_search):
if fbx_item is not None and fbx_item.props: # Do not error on complete empty properties (see T45291).
assert(len(fbx_item.props) == 1)
assert(fbx_item.props_type[0] == data_types.STRING)
- return fbx_item.props[0].decode('utf-8')
+ return fbx_item.props[0].decode('utf-8', 'replace')
return None
@@ -124,14 +136,14 @@ def elem_name_ensure_class(elem, clss=...):
elem_name, elem_class = elem_split_name_class(elem)
if clss is not ...:
assert(elem_class == clss)
- return elem_name.decode('utf-8')
+ return validate_blend_names(elem_name)
def elem_name_ensure_classes(elem, clss=...):
elem_name, elem_class = elem_split_name_class(elem)
if clss is not ...:
assert(elem_class in clss)
- return elem_name.decode('utf-8')
+ return validate_blend_names(elem_name)
def elem_split_name_class_nodeattr(elem):
@@ -308,13 +320,14 @@ def blen_read_custom_properties(fbx_obj, blen_obj, settings):
# Special case for 3DS Max user properties:
assert(fbx_prop.props[1] == b'KString')
assert(fbx_prop.props_type[4] == data_types.STRING)
- items = fbx_prop.props[4].decode('utf-8')
+ items = fbx_prop.props[4].decode('utf-8', 'replace')
for item in items.split('\r\n'):
if item:
prop_name, prop_value = item.split('=', 1)
- blen_obj[prop_name.strip()] = prop_value.strip()
+ prop_name = validate_blend_names(prop_name.strip().encode('utf-8'))
+ blen_obj[prop_name] = prop_value.strip()
else:
- prop_name = fbx_prop.props[0].decode('utf-8')
+ prop_name = validate_blend_names(fbx_prop.props[0])
prop_type = fbx_prop.props[1]
if prop_type in {b'Vector', b'Vector3D', b'Color', b'ColorRGB'}:
assert(fbx_prop.props_type[4:7] == bytes((data_types.FLOAT64,)) * 3)
@@ -330,7 +343,7 @@ def blen_read_custom_properties(fbx_obj, blen_obj, settings):
blen_obj[prop_name] = fbx_prop.props[4]
elif prop_type == b'KString':
assert(fbx_prop.props_type[4] == data_types.STRING)
- blen_obj[prop_name] = fbx_prop.props[4].decode('utf-8')
+ blen_obj[prop_name] = fbx_prop.props[4].decode('utf-8', 'replace')
elif prop_type in {b'Number', b'double', b'Double'}:
assert(fbx_prop.props_type[4] == data_types.FLOAT64)
blen_obj[prop_name] = fbx_prop.props[4]
@@ -344,13 +357,13 @@ def blen_read_custom_properties(fbx_obj, blen_obj, settings):
assert(fbx_prop.props_type[4:6] == bytes((data_types.INT32, data_types.STRING)))
val = fbx_prop.props[4]
if settings.use_custom_props_enum_as_string and fbx_prop.props[5]:
- enum_items = fbx_prop.props[5].decode('utf-8').split('~')
+ enum_items = fbx_prop.props[5].decode('utf-8', 'replace').split('~')
assert(val >= 0 and val < len(enum_items))
blen_obj[prop_name] = enum_items[val]
else:
blen_obj[prop_name] = val
else:
- print ("WARNING: User property type '%s' is not supported" % prop_type.decode('utf-8'))
+ print ("WARNING: User property type '%s' is not supported" % prop_type.decode('utf-8', 'replace'))
def blen_read_object_transform_do(transform_data):
@@ -544,7 +557,7 @@ def blen_read_animations_action_item(action, item, cnodes, fps, anim_offset):
'Bake' loc/rot/scale into the action,
taking any pre_ and post_ matrix into account to transform from fbx into blender space.
"""
- from bpy.types import Object, PoseBone, ShapeKey, Material
+ from bpy.types import Object, PoseBone, ShapeKey, Material, Camera
from itertools import chain
fbx_curves = []
@@ -564,6 +577,8 @@ def blen_read_animations_action_item(action, item, cnodes, fps, anim_offset):
props = [("diffuse_color", 3, grpname or "Diffuse Color")]
elif isinstance(item, ShapeKey):
props = [(item.path_from_id("value"), 1, "Key")]
+ elif isinstance(item, Camera):
+ props = [(item.path_from_id("lens"), 1, "Camera")]
else: # Object or PoseBone:
if item.is_bone:
bl_obj = item.bl_obj.pose.bones[item.bl_bone]
@@ -611,6 +626,17 @@ def blen_read_animations_action_item(action, item, cnodes, fps, anim_offset):
for fc, v in zip(blen_curves, (value,)):
fc.keyframe_points.insert(frame, v, {'NEEDED', 'FAST'}).interpolation = 'LINEAR'
+ elif isinstance(item, Camera):
+ for frame, values in blen_read_animations_curves_iter(fbx_curves, anim_offset, 0, fps):
+ value = 0.0
+ for v, (fbxprop, channel, _fbx_acdata) in values:
+ assert(fbxprop == b'FocalLength')
+ assert(channel == 0)
+ value = v
+
+ for fc, v in zip(blen_curves, (value,)):
+ fc.keyframe_points.insert(frame, v, {'NEEDED', 'FAST'}).interpolation = 'LINEAR'
+
else: # Object or PoseBone:
if item.is_bone:
bl_obj = item.bl_obj.pose.bones[item.bl_bone]
@@ -673,7 +699,7 @@ def blen_read_animations(fbx_tmpl_astack, fbx_tmpl_alayer, stacks, scene, anim_o
Only the first found action is linked to objects, more complex setups are not handled,
it's up to user to reproduce them!
"""
- from bpy.types import ShapeKey, Material
+ from bpy.types import ShapeKey, Material, Camera
actions = {}
for as_uuid, ((fbx_asdata, _blen_data), alayers) in stacks.items():
@@ -685,6 +711,8 @@ def blen_read_animations(fbx_tmpl_astack, fbx_tmpl_alayer, stacks, scene, anim_o
id_data = item
elif isinstance(item, ShapeKey):
id_data = item.id_data
+ elif isinstance(item, Camera):
+ id_data = item
else:
id_data = item.bl_obj
# XXX Ignore rigged mesh animations - those are a nightmare to handle, see note about it in
@@ -715,7 +743,7 @@ def blen_read_animations(fbx_tmpl_astack, fbx_tmpl_alayer, stacks, scene, anim_o
def blen_read_geom_layerinfo(fbx_layer):
return (
- elem_find_first_string(fbx_layer, b'Name'),
+ validate_blend_names(elem_find_first_string_as_bytes(fbx_layer, b'Name')),
elem_find_first_string_as_bytes(fbx_layer, b'MappingInformationType'),
elem_find_first_string_as_bytes(fbx_layer, b'ReferenceInformationType'),
)
@@ -1270,18 +1298,17 @@ def blen_read_shape(fbx_tmpl, fbx_sdata, fbx_bcdata, meshes, scene):
if me.shape_keys is None:
objects[0].shape_key_add(name="Basis", from_mix=False)
- objects[0].shape_key_add(name=elem_name_utf8, from_mix=False)
+ kb = objects[0].shape_key_add(name=elem_name_utf8, from_mix=False)
me.shape_keys.use_relative = True # Should already be set as such.
- kb = me.shape_keys.key_blocks[elem_name_utf8]
for idx, co in vcos:
kb.data[idx].co[:] = co
kb.value = weight
# Add vgroup if necessary.
if create_vg:
- add_vgroup_to_objects(indices, vgweights, elem_name_utf8, objects)
- kb.vertex_group = elem_name_utf8
+ vgoups = add_vgroup_to_objects(indices, vgweights, kb.name, objects)
+ kb.vertex_group = kb.name
keyblocks.append(kb)
@@ -2825,6 +2852,13 @@ def load(operator, context, filepath="",
if keyblocks is None:
continue
items += [(kb, lnk_prop) for kb in keyblocks]
+ elif lnk_prop == b'FocalLength': # Camera lens.
+ from bpy.types import Camera
+ fbx_item = fbx_table_nodes.get(n_uuid, None)
+ if fbx_item is None or not isinstance(fbx_item[1], Camera):
+ continue
+ cam = fbx_item[1]
+ items.append((cam, lnk_prop))
elif lnk_prop == b'DiffuseColor':
from bpy.types import Material
fbx_item = fbx_table_nodes.get(n_uuid, None)
@@ -2861,7 +2895,11 @@ def load(operator, context, filepath="",
continue
# Note this is an infamous simplification of the compound props stuff,
# seems to be standard naming but we'll probably have to be smarter to handle more exotic files?
- channel = {b'd|X': 0, b'd|Y': 1, b'd|Z': 2, b'd|DeformPercent': 0}.get(acn_ctype.props[3], None)
+ channel = {
+ b'd|X': 0, b'd|Y': 1, b'd|Z': 2,
+ b'd|DeformPercent': 0,
+ b'd|FocalLength': 0
+ }.get(acn_ctype.props[3], None)
if channel is None:
continue
curvenodes[acn_uuid][ac_uuid] = (fbx_acitem, channel)
diff --git a/io_scene_fbx/json2fbx.py b/io_scene_fbx/json2fbx.py
index dc4c5bcc..579b45a7 100755
--- a/io_scene_fbx/json2fbx.py
+++ b/io_scene_fbx/json2fbx.py
@@ -108,9 +108,9 @@ def parse_json_rec(fbx_root, json_node):
elif dt == "d":
e.add_float64_array(d)
elif dt == "b":
- e.add_byte_array(d)
- elif dt == "c":
e.add_bool_array(d)
+ elif dt == "c":
+ e.add_byte_array(d)
if name == "FBXVersion":
assert(data_types == "I")
diff --git a/io_scene_obj/import_obj.py b/io_scene_obj/import_obj.py
index 7ca22160..f3e16fcf 100644
--- a/io_scene_obj/import_obj.py
+++ b/io_scene_obj/import_obj.py
@@ -109,7 +109,7 @@ def create_materials(filepath, relpath,
curr_token = []
for token in img_data[:-1]:
- if token.startswith(b'-'):
+ if token.startswith(b'-') and token[1:].isalpha():
if curr_token:
map_options[curr_token[0]] = curr_token[1:]
curr_token[:] = []
diff --git a/materials_library_vx/__init__.py b/materials_library_vx/__init__.py
index 9a8b47f9..07131862 100644
--- a/materials_library_vx/__init__.py
+++ b/materials_library_vx/__init__.py
@@ -21,7 +21,7 @@
bl_info = {
"name": "Material Library",
"author": "Mackraken (mackraken2023@hotmail.com)",
- "version": (0, 5, 7),
+ "version": (0, 5, 8),
"blender": (2, 7, 8),
"api": 60995,
"location": "Properties > Material",
@@ -114,7 +114,7 @@ def check_index(collection, index):
def send_command(cmd, output="sendmat.py"):
bin = winpath(bpy.app.binary_path)
- scriptpath = winpath(os.path.join(matlib_path, output))
+ scriptpath = winpath(os.path.join(bpy.app.tempdir, output))
with open(scriptpath, "w") as f:
f.write(cmd)
@@ -484,11 +484,8 @@ if mat:
# self.current_library.materials[self.mat_index].category = cat
#remove mat from any category
else:
- matnode = xml.find("material", mat.name, lib)
- if matnode:
- xml.deleteNode(matnode)
mat.category = ""
- self.current_library.materials[self.mat_index].category = ""
+ self.all_materials[self.mat_index].category = ""
else:
return "WARNING", "Select a material"
@@ -741,8 +738,7 @@ bpy.ops.wm.save_mainfile(filepath="%s", check_existing=False, compress=True)'''
# Scene.matlib = PointerProperty(type = matlibProperties)
### MENUS
-class matlibLibsMenu(Menu):
- bl_idname = "matlib.libs_menu"
+class MATLIB_MT_LibsMenu(Menu):
bl_label = "Libraries Menu"
def draw(self, context):
@@ -752,8 +748,7 @@ class matlibLibsMenu(Menu):
for i, lib in enumerate(libs):
layout.operator("matlib.operator", text=lib.shortname).cmd="lib"+str(i)
-class matlibCatsMenu(Menu):
- bl_idname = "matlib.cats_menu"
+class MATLIB_MT_CatsMenu(Menu):
bl_label = "Categories Menu"
def draw(self, context):
@@ -779,7 +774,7 @@ class matlibCatsMenu(Menu):
-class MatlibAdd(Operator):
+class MATLIB_OT_add(Operator):
"""Add active material to library"""
bl_idname = "matlib.add"
bl_label = "Add active material"
@@ -797,7 +792,7 @@ class MatlibAdd(Operator):
self.report({success[0]}, success[1])
return {'FINISHED'}
-class MatlibRemove(Operator):
+class MATLIB_OT_remove(Operator):
"""Remove material from library"""
bl_idname = "matlib.remove"
bl_label = "Remove material from library"
@@ -815,7 +810,7 @@ class MatlibRemove(Operator):
self.report({success[0]}, success[1])
return {'FINISHED'}
-class MatlibReload(Operator):
+class MATLIB_OT_remove(Operator):
"""Reload library"""
bl_idname = "matlib.reload"
bl_label = "Reload library"
@@ -836,7 +831,7 @@ class MatlibReload(Operator):
return {'FINISHED'}
-class MatlibApply(Operator):
+class MATLIB_OT_apply(Operator):
"""Apply selected material"""
bl_idname = "matlib.apply"
bl_label = "Apply material"
@@ -858,7 +853,7 @@ class MatlibApply(Operator):
return {'FINISHED'}
-class MatlibPreview(Operator):
+class MATLIB_OT_preview(Operator):
"""Preview selected material"""
bl_idname = "matlib.preview"
bl_label = "Preview selected material"
@@ -880,7 +875,7 @@ class MatlibPreview(Operator):
return {'FINISHED'}
-class MatlibFlush(Operator):
+class MATLIB_OT_flush(Operator):
"""Flush unused materials"""
bl_idname = "matlib.flush"
bl_label = "Flush unused materials"
@@ -917,7 +912,7 @@ class MatlibFlush(Operator):
return {'FINISHED'}
-class matlibOperator(Operator):
+class MATLIB_OT_operator(Operator):
"""Add, Remove, Reload, Apply, Preview, Clean Material"""
bl_label = "New"
bl_idname = "matlib.operator"
@@ -1118,7 +1113,7 @@ for mat in mats:
return {'FINISHED'}
-class matlibvxPanel(Panel):
+class MATLIB_PT_vxPanel(Panel):
bl_label = "Material Library VX"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
@@ -1144,7 +1139,7 @@ class matlibvxPanel(Panel):
else:
text = "Select a Library"
- row.menu("matlib.libs_menu",text=text)
+ row.menu("MATLIB_MT_LibsMenu",text=text)
row.operator("matlib.operator", icon="ZOOMIN", text="").cmd = "LIBRARY_ADD"
if matlib.active_material:
row.label(matlib.active_material.category)
@@ -1175,7 +1170,7 @@ class matlibvxPanel(Panel):
row = layout.row(align=True)
text = "All"
if matlib.current_category: text = matlib.current_category
- row.menu("matlib.cats_menu",text=text)
+ row.menu("MATLIB_MT_CatsMenu",text=text)
row.prop(matlib, "filter", icon="FILTER", text="")
row.operator("matlib.operator", icon="FILE_PARENT", text="").cmd="FILTER_SET"
row.operator("matlib.operator", icon="ZOOMIN", text="").cmd="FILTER_ADD"
@@ -1198,7 +1193,7 @@ class matlibvxPanel(Panel):
# else:
# row.label("Library not found!.")
-#classes = [matlibvxPanel, matlibOperator, matlibLibsMenu, matlibCatsMenu]
+#classes = [MATLIB_PT_vxPanel, MATLIB_OT_operator, MATLIB_MT_LibsMenu, MATLIB_MT_CatsMenu]
#print(bpy.context.scene)
diff --git a/materials_utils/__init__.py b/materials_utils/__init__.py
index c768d770..ea85a104 100644
--- a/materials_utils/__init__.py
+++ b/materials_utils/__init__.py
@@ -26,7 +26,7 @@
bl_info = {
"name": "Materials Utils Specials",
"author": "Community",
- "version": (1, 0, 3),
+ "version": (1, 0, 5),
"blender": (2, 77, 0),
"location": "Materials Properties Specials > Shift Q",
"description": "Materials Utils and Convertors",
@@ -34,7 +34,7 @@ bl_info = {
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
"Scripts/3D_interaction/Materials_Utils",
"category": "Material"
- }
+}
if "bpy" in locals():
import importlib
@@ -51,27 +51,35 @@ else:
import bpy
import os
from os import (
- path as os_path,
- access as os_access,
- remove as os_remove,
- )
+ path as os_path,
+ access as os_access,
+ remove as os_remove,
+)
from bpy.props import (
- StringProperty,
- BoolProperty,
- EnumProperty,
- PointerProperty,
- )
+ BoolProperty,
+ CollectionProperty,
+ EnumProperty,
+ IntProperty,
+ StringProperty,
+ PointerProperty,
+)
from bpy.types import (
- Menu,
- Operator,
- Panel,
- AddonPreferences,
- PropertyGroup,
- )
+ AddonPreferences,
+ Menu,
+ Operator,
+ Panel,
+ PropertyGroup,
+ UIList,
+)
from .warning_messages_utils import (
- warning_messages,
- c_data_has_materials,
- )
+ warning_messages,
+ c_data_has_materials,
+ c_obj_data_has_materials,
+)
+
+# Globals
+UNDO_MESSAGE = "*Only Undo is available*"
+COLUMN_SPLIT = 20
# Functions
@@ -146,7 +154,7 @@ def replace_material(m1, m2, all_objects=False, update_selection=False, operator
if m.material == matorg:
m.material = matrep
# don't break the loop as the material can be
- # ref'd more than once
+ # referenced more than once
# Indicate which objects were affected
if update_selection:
@@ -169,7 +177,7 @@ def select_material_by_name(find_mat_name):
if find_mat is None:
return
- # check for editmode
+ # check for edit mode
editmode = False
scn = bpy.context.scene
@@ -200,7 +208,7 @@ def select_material_by_name(find_mat_name):
else:
ob.select = False
else:
- # it's editmode, so select the polygons
+ # it's edit mode, so select the polygons
ob = actob
ms = ob.material_slots
@@ -227,7 +235,7 @@ def select_material_by_name(find_mat_name):
def mat_to_texface(operator=None):
# assigns the first image in each material to the polygons in the active
- # uvlayer for all selected objects
+ # uv layer for all selected objects
# check for editmode
editmode = False
@@ -282,7 +290,7 @@ def mat_to_texface(operator=None):
bpy.ops.mesh.uv_texture_add()
scn.objects.active = actob
- # get active uvlayer
+ # get active uv layer
for t in me.uv_textures:
if t.active:
uvtex = t.data
@@ -316,7 +324,7 @@ def assignmatslots(ob, matlist):
scn.objects.active = ob
for s in ob.material_slots:
- bpy.ops.object.material_slot_remove()
+ remove_material_slot()
# re-add them and assign material
if matlist:
@@ -330,7 +338,7 @@ def assignmatslots(ob, matlist):
# to face indices, mat tries to get an '' as mat index
pass
- # restore active object:
+ # restore active object
scn.objects.active = ob_active
@@ -352,65 +360,97 @@ def cleanmatslots(operator=None):
objs = bpy.context.selected_editable_objects
# collect all object names for warning_messages
message_a = []
- # Flag if there are non MESH objects selected
- mixed_obj = False
+ # Flags if there are non MESH objects selected
+ mixed_obj, mixed_obj_slot = False, False
for ob in objs:
- if ob.type == 'MESH':
- mats = ob.material_slots.keys()
-
- # if mats is empty then mats[faceindex] will be out of range
- if mats:
- # check the polygons on the mesh to build a list of used materials
- usedMatIndex = [] # we'll store used materials indices here
- faceMats = []
- me = ob.data
- for f in me.polygons:
- # get the material index for this face...
- faceindex = f.material_index
-
- # indices will be lost: Store face mat use by name
- currentfacemat = mats[faceindex]
- faceMats.append(currentfacemat)
-
- # check if index is already listed as used or not
- found = False
- for m in usedMatIndex:
- if m == faceindex:
- found = True
- # break
-
- if found is False:
- # add this index to the list
- usedMatIndex.append(faceindex)
-
- # re-assign the used mats to the mesh and leave out the unused
- ml = []
- mnames = []
- for u in usedMatIndex:
- ml.append(mats[u])
- # we'll need a list of names to get the face indices...
- mnames.append(mats[u])
-
- assignmatslots(ob, ml)
-
- # restore face indices:
- i = 0
- for f in me.polygons:
- matindex = mnames.index(faceMats[i])
- f.material_index = matindex
- i += 1
- else:
- message_a.append(getattr(ob, "name", "NO NAME"))
- continue
- else:
+ if ob.type != 'MESH':
+ mat_empty = []
message_a.append(getattr(ob, "name", "NO NAME"))
if mixed_obj is False:
mixed_obj = True
+
+ # at least try to remove empty material slots
+ if ob.type in {'CURVE', 'SURFACE', 'FONT', 'META'}:
+ mats = ob.material_slots
+ mat_empty = [i for i, slot in enumerate(mats) if not slot.material]
+
+ if not mat_empty:
+ continue
+
+ if mixed_obj_slot is False:
+ mixed_obj_slot = True
+
+ # Ctx - copy the context for operator override
+ Ctx = bpy.context.copy()
+ # for this operator it needs only the active object replaced
+ Ctx['object'] = ob
+
+ for index in mat_empty:
+ try:
+ ob.active_material_index = index
+ bpy.ops.object.material_slot_remove(Ctx)
+ except:
+ continue
+ continue
+
+ mats = ob.material_slots.keys()
+ maxindex = len(mats) - 1 # indices start from zero
+
+ # if mats is empty then mats[faceindex] will be out of range
+ if not mats:
+ message_a.append(getattr(ob, "name", "NO NAME"))
continue
+ # check the polygons on the mesh to build a list of used materials
+ usedMatIndex = [] # we'll store used materials indices here
+ faceMats = []
+ badIndices = set() # collect face indices that are out material slot's range
+ me = ob.data
+ for f in me.polygons:
+ # get the material index for this face...
+ faceindex = f.material_index
+ # check if the mats[faceindex] is not out of range
+ if faceindex > maxindex:
+ badIndices.add(faceindex)
+ continue
+
+ # indices will be lost: Store face mat use by name
+ currentfacemat = mats[faceindex]
+ faceMats.append(currentfacemat)
+
+ # check if index is already listed as used or not
+ found = False
+ for m in usedMatIndex:
+ if m == faceindex:
+ found = True
+
+ if found is False:
+ # add this index to the list
+ usedMatIndex.append(faceindex)
+
+ # re-assign the used mats to the mesh and leave out the unused
+ ml = []
+ mnames = []
+ for u in usedMatIndex:
+ if u not in badIndices:
+ ml.append(mats[u])
+ # we'll need a list of names to get the face indices...
+ mnames.append(mats[u])
+
+ assignmatslots(ob, ml)
+
+ # restore face indices:
+ i = 0
+ for f in me.polygons:
+ if i not in badIndices:
+ matindex = mnames.index(faceMats[i])
+ f.material_index = matindex
+ i += 1
+
if message_a and operator:
- warn_mess = ('C_OB_MIX_NO_MAT' if mixed_obj is True else 'C_OB_NO_MAT')
+ warn_s = 'C_OB_MIX_NO_MAT' if mixed_obj is True else 'C_OB_NO_MAT'
+ warn_mess = 'C_OB_MIX_SLOT_MAT' if mixed_obj_slot else warn_s
warning_messages(operator, warn_mess, message_a)
if actob:
@@ -426,12 +466,11 @@ def cleanmatslots(operator=None):
def assign_mat_mesh_edit(matname="Default", operator=None):
actob = bpy.context.active_object
found = False
- for m in bpy.data.materials:
- if m.name == matname:
- target = m
- found = True
- break
- if not found:
+
+ # check if material exists, if it doesn't then create it
+ target = bpy.data.materials.get(matname)
+
+ if not target:
target = bpy.data.materials.new(matname)
if (actob.type in {'MESH'} and actob.mode in {'EDIT'}):
@@ -447,8 +486,8 @@ def assign_mat_mesh_edit(matname="Default", operator=None):
break
i += 1
+ # the material is not attached to the object
if not found:
- # the material is not attached to the object
actob.data.materials.append(target)
# is selected ?
@@ -472,7 +511,7 @@ def assign_mat_mesh_edit(matname="Default", operator=None):
def assign_mat(matname="Default", operator=None):
- # get active object so we can restore it later
+ # get the active object so we can restore it later
actob = bpy.context.active_object
# is active object selected ?
@@ -480,19 +519,15 @@ def assign_mat(matname="Default", operator=None):
actob.select = True
# check if material exists, if it doesn't then create it
- found = False
- for m in bpy.data.materials:
- if m.name == matname:
- target = m
- found = True
- break
+ target = bpy.data.materials.get(matname)
- if not found:
+ if not target:
target = bpy.data.materials.new(matname)
- # if objectmode then set all polygons
+ # if object mode then set all polygons
editmode = False
allpolygons = True
+
if actob.mode == 'EDIT':
editmode = True
allpolygons = False
@@ -596,7 +631,7 @@ def check_texture(img, mat):
def texface_to_mat(operator=None):
- # editmode check here!
+ # edit mode check here!
editmode = False
ob = bpy.context.object
if ob.mode == 'EDIT':
@@ -604,7 +639,6 @@ def texface_to_mat(operator=None):
bpy.ops.object.mode_set()
for ob in bpy.context.selected_editable_objects:
-
faceindex = []
unique_images = []
# collect object names for warning messages
@@ -668,27 +702,30 @@ def remove_materials(operator=None, setting="SLOT"):
actob = bpy.context.active_object
actob_name = getattr(actob, "name", "NO NAME")
- if actob:
- if not included_object_types(actob.type):
- if operator:
- warning_messages(operator, 'OB_CANT_MAT', actob_name)
- else:
- if (hasattr(actob.data, "materials") and
- len(actob.data.materials) > 0):
- if setting == "SLOT":
- bpy.ops.object.material_slot_remove()
- elif setting == "ALL":
- for mat in actob.data.materials:
- try:
- bpy.ops.object.material_slot_remove()
- except:
- pass
-
- if operator:
- warn_mess = ('R_ACT_MAT_ALL' if setting == "ALL" else 'R_ACT_MAT')
- warning_messages(operator, warn_mess, actob_name)
- elif operator:
- warning_messages(operator, 'R_OB_NO_MAT', actob_name)
+ if not actob:
+ return
+
+ if not included_object_types(actob.type):
+ if operator:
+ warning_messages(operator, 'OB_CANT_MAT', actob_name)
+ return
+
+ if (hasattr(actob.data, "materials") and len(actob.data.materials) > 0):
+ if setting == "SLOT":
+ remove_material_slot()
+ elif setting == "ALL":
+ for mat in actob.data.materials:
+ try:
+ remove_material_slot()
+ except:
+ pass
+
+ if operator:
+ warn_mess = 'R_ACT_MAT_ALL' if setting == "ALL" else 'R_ACT_MAT'
+ warning_messages(operator, warn_mess, actob_name)
+
+ elif operator:
+ warning_messages(operator, 'R_OB_NO_MAT', actob_name)
def remove_materials_all(operator=None):
@@ -700,11 +737,10 @@ def remove_materials_all(operator=None):
if not included_object_types(ob.type):
continue
else:
- # code from blender stackexchange (by CoDEmanX)
+ # code from blender stack exchange (by CoDEmanX)
ob.active_material_index = 0
- if (hasattr(ob.data, "materials") and
- len(ob.material_slots) >= 1):
+ if (hasattr(ob.data, "materials") and len(ob.material_slots) >= 1):
mat_count = True
# Ctx - copy the context for operator override
@@ -740,9 +776,10 @@ class VIEW3D_OT_show_mat_preview(Operator):
@classmethod
def poll(cls, context):
- return (context.active_object is not None and
- context.object.active_material is not None and
- included_object_types(context.object.type))
+ obj = context.active_object
+ return (obj is not None and
+ obj.active_material is not None and
+ included_object_types(obj.type))
def invoke(self, context, event):
self.is_not_undo = True
@@ -753,89 +790,107 @@ class VIEW3D_OT_show_mat_preview(Operator):
ob = context.active_object
prw_size = size_preview()
- if self.is_not_undo is True:
- if ob and hasattr(ob, "active_material"):
- mat = ob.active_material
- is_opaque = (True if (ob and hasattr(ob, "show_transparent") and
- ob.show_transparent is True)
- else False)
- is_opaque_bi = (True if (mat and hasattr(mat, "use_transparency") and
- mat.use_transparency is True)
- else False)
- is_mesh = (True if ob.type == 'MESH' else False)
-
- if size_type_is_preview():
- layout.template_ID_preview(ob, "active_material", new="material.new",
- rows=prw_size['Width'], cols=prw_size['Height'])
- else:
- layout.template_ID(ob, "active_material", new="material.new")
- layout.separator()
-
- if c_render_engine("Both"):
- layout.prop(mat, "use_nodes", icon='NODETREE')
-
- if c_need_of_viewport_colors():
- color_txt = ("Viewport Color:" if c_render_engine("Cycles") else "Diffuse")
- spec_txt = ("Viewport Specular:" if c_render_engine("Cycles") else "Specular")
- col = layout.column(align=True)
- col.label(color_txt)
- col.prop(mat, "diffuse_color", text="")
- if c_render_engine("BI"):
- # Blender Render
- col.prop(mat, "diffuse_intensity", text="Intensity")
- col.separator()
-
- col.label(spec_txt)
- col.prop(mat, "specular_color", text="")
- col.prop(mat, "specular_hardness")
-
- if (c_render_engine("BI") and not c_context_use_nodes()):
- # Blender Render
- col.separator()
- col.prop(mat, "use_transparency")
- col.separator()
- if is_opaque_bi:
- col.prop(mat, "transparency_method", text="")
- col.separator()
- col.prop(mat, "alpha")
- elif (c_render_engine("Cycles") and is_mesh):
- # Cycles
- col.separator()
- col.prop(ob, "show_transparent", text="Transparency")
- if is_opaque:
- col.separator()
- col.prop(mat, "alpha")
- layout.separator()
- else:
- other_render = ("*Unavailable with this Renderer*" if not c_render_engine("Both")
- else "*Unavailable in this Context*")
- no_col_label = ("*Only available in Solid Shading*" if c_render_engine("Cycles")
- else other_render)
- layout.label(no_col_label, icon="INFO")
+ if self.is_not_undo is False:
+ layout.label(text=UNDO_MESSAGE, icon="INFO")
+ return
+
+ if not (ob and hasattr(ob, "active_material")):
+ layout.label(text="No Active Object or Active Material", icon="INFO")
+ return
+
+ mat = ob.active_material
+ is_opaque = (
+ True if (ob and hasattr(ob, "show_transparent") and
+ ob.show_transparent is True) else False
+ )
+ is_opaque_bi = (
+ True if (mat and hasattr(mat, "use_transparency") and
+ mat.use_transparency is True) else False
+ )
+ is_mesh = True if ob.type == 'MESH' else False
+
+ if size_type_is_preview():
+ layout.template_ID_preview(ob, "active_material", new="material.new",
+ rows=prw_size['Width'], cols=prw_size['Height'])
else:
- layout.label(text="**Only Undo is available**", icon="INFO")
+ layout.template_ID(ob, "active_material", new="material.new")
+ layout.separator()
+
+ if c_render_engine("Both"):
+ layout.prop(mat, "use_nodes", icon='NODETREE')
+
+ if not c_need_of_viewport_colors():
+ other_render = (
+ "*Unavailable with this Renderer*" if not c_render_engine("Both") else
+ "*Unavailable in this Context*"
+ )
+ no_col_label = (
+ "*Only available in Solid Shading*" if c_render_engine("Cycles") else
+ other_render
+ )
+ layout.label(no_col_label, icon="INFO")
+ return
+
+ color_txt = "Viewport Color:" if c_render_engine("Cycles") else "Diffuse"
+ spec_txt = "Viewport Specular:" if c_render_engine("Cycles") else "Specular"
+ col = layout.column(align=True)
+ col.label(color_txt)
+ col.prop(mat, "diffuse_color", text="")
+
+ if c_render_engine("BI"):
+ # Blender Render
+ col.prop(mat, "diffuse_intensity", text="Intensity")
+ col.separator()
+
+ col.label(spec_txt)
+ col.prop(mat, "specular_color", text="")
+ col.prop(mat, "specular_hardness")
+
+ if (c_render_engine("BI") and not c_context_use_nodes()):
+ # Blender Render
+ col.separator()
+ col.prop(mat, "use_transparency")
+ col.separator()
+ if is_opaque_bi:
+ col.prop(mat, "transparency_method", text="")
+ col.separator()
+ col.prop(mat, "alpha")
+ elif (c_render_engine("Cycles") and is_mesh):
+ # Cycles
+ col.separator()
+ col.prop(ob, "show_transparent", text="Transparency")
+ if is_opaque:
+ col.separator()
+ col.prop(mat, "alpha")
+ col.separator()
+ col.label("Viewport Alpha:")
+ col.prop(mat.game_settings, "alpha_blend", text="")
+ layout.separator()
def check(self, context):
return self.is_not_undo
def execute(self, context):
self.is_not_undo = False
+
return {'FINISHED'}
class VIEW3D_OT_copy_material_to_selected(Operator):
bl_idname = "view3d.copy_material_to_selected"
bl_label = "Copy Materials to others"
- bl_description = ("Copy Material From Active to Selected objects \n"
+ bl_description = ("Copy Material From Active to Selected objects\n"
+ "In case of multiple materials, only the first slot is assigned\n"
"Works on Object's Data linked Materials")
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
+ obj = context.active_object
return (c_data_has_materials() and
- context.active_object is not None and
- included_object_types(context.active_object.type) and
- context.object.active_material is not None and
+ obj is not None and
+ included_object_types(obj.type) and
+ obj.active_material is not None and
context.selected_editable_objects)
def execute(self, context):
@@ -853,6 +908,7 @@ class VIEW3D_OT_copy_material_to_selected(Operator):
return {'CANCELLED'}
warning_messages(self, warn_mess)
+
return {'FINISHED'}
@@ -872,13 +928,14 @@ class VIEW3D_OT_texface_to_material(Operator):
return context.window_manager.invoke_confirm(self, event)
def execute(self, context):
- if context.selected_editable_objects:
- texface_to_mat(self)
- return {'FINISHED'}
- else:
+ if not context.selected_editable_objects:
warning_messages(self, 'TEX_MAT_NO_SL')
return {'CANCELLED'}
+ texface_to_mat(self)
+
+ return {'FINISHED'}
+
class VIEW3D_OT_set_new_material_name(Operator):
bl_idname = "view3d.set_new_material_name"
@@ -901,6 +958,7 @@ class VIEW3D_OT_set_new_material_name(Operator):
layout.prop(scene, "use_tweak")
def execute(self, context):
+
return {'FINISHED'}
@@ -908,34 +966,61 @@ class VIEW3D_OT_assign_material(Operator):
bl_idname = "view3d.assign_material"
bl_label = "Assign Material"
bl_description = "Assign a material to the selection"
+ bl_property = "matname"
bl_options = {'REGISTER', 'UNDO'}
is_existing = BoolProperty(
name="Is it a new Material",
options={'HIDDEN'},
default=True,
- )
+ )
matname = StringProperty(
name="Material Name",
description="Name of the Material to Assign",
options={'HIDDEN'},
default="Material_New",
maxlen=128,
- )
+ )
+ is_not_undo = False # prevent drawing props on undo
@classmethod
def poll(cls, context):
return context.active_object is not None
def invoke(self, context, event):
- return self.execute(context)
+ self.is_not_undo = True
+
+ if not self.is_existing or use_mat_menu_type() not in {'POPUP'}:
+ return self.execute(context)
+
+ materials_lists_fill_names(context, refresh=True, is_object=False)
+ return context.window_manager.invoke_props_dialog(self, width=400, height=200)
+
+ def check(self, context):
+ return self.is_not_undo
+
+ def draw(self, context):
+ draw_ui_list_popups(self, context, obj_data=False)
def execute(self, context):
actob = context.active_object
- mn = self.matname
scene = context.scene.mat_specials
+ mn = self.matname
tweak = scene.use_tweak
+ if use_mat_menu_type() == 'POPUP' and self.is_existing:
+ mats_col = context.scene.mat_specials_mats
+ len_mats = len(mats_col)
+ mat_index = scene.index_mat
+
+ if not (len_mats > 0 and mat_index is not None and mat_index <= len_mats):
+ self.report({'WARNING'},
+ "No Materials in the Scene. Please use the Add New option")
+ return {"CANCELLED"}
+
+ mats_up = mats_col[mat_index].name
+ mn = mats_up
+
if not self.is_existing:
new_name = check_mat_name_unique(scene.set_material_name)
mn = new_name
@@ -951,6 +1036,8 @@ class VIEW3D_OT_assign_material(Operator):
mat_to_texface()
self.is_not_undo = False
+ activate_mat_slot(actob, mn)
+
if tweak and not self.is_existing:
try:
bpy.ops.view3d.show_mat_preview('INVOKE_DEFAULT')
@@ -976,6 +1063,7 @@ class VIEW3D_OT_clean_material_slots(Operator):
def execute(self, context):
cleanmatslots(self)
+
return {'FINISHED'}
@@ -993,18 +1081,19 @@ class VIEW3D_OT_material_to_texface(Operator):
context.active_object is not None)
def execute(self, context):
- if context.selected_editable_objects:
- mat_to_texface(self)
- return {'FINISHED'}
- else:
+ if not context.selected_editable_objects:
warning_messages(self, "MAT_TEX_NO_SL")
return {'CANCELLED'}
+ mat_to_texface(self)
+
+ return {'FINISHED'}
+
class VIEW3D_OT_material_remove_slot(Operator):
bl_idname = "view3d.material_remove_slot"
bl_label = "Remove Active Slot (Active Object)"
- bl_description = ("Remove active material slot from active object\n"
+ bl_description = ("Remove active material slot from active object \n"
"Can't be used in Edit Mode")
bl_options = {'REGISTER', 'UNDO'}
@@ -1013,21 +1102,22 @@ class VIEW3D_OT_material_remove_slot(Operator):
# materials can't be removed in Edit mode
return (c_data_has_materials() and
context.active_object is not None and
- not context.object.mode == 'EDIT')
+ not context.active_object.mode == 'EDIT')
def execute(self, context):
- if context.selected_editable_objects:
- remove_materials(self, "SLOT")
- return {'FINISHED'}
- else:
+ if not context.selected_editable_objects:
warning_messages(self, 'R_NO_SL_MAT')
return {'CANCELLED'}
+ remove_materials(self, "SLOT")
+
+ return {'FINISHED'}
+
class VIEW3D_OT_material_remove_object(Operator):
bl_idname = "view3d.material_remove_object"
bl_label = "Remove all Slots (Active Object)"
- bl_description = ("Remove all material slots from active object\n"
+ bl_description = ("Remove all material slots from active object \n"
"Can't be used in Edit Mode")
bl_options = {'REGISTER', 'UNDO'}
@@ -1036,16 +1126,17 @@ class VIEW3D_OT_material_remove_object(Operator):
# materials can't be removed in Edit mode
return (c_data_has_materials() and
context.active_object is not None and
- not context.object.mode == 'EDIT')
+ not context.active_object.mode == 'EDIT')
def execute(self, context):
- if context.selected_editable_objects:
- remove_materials(self, "ALL")
- return {'FINISHED'}
- else:
+ if not context.selected_editable_objects:
warning_messages(self, 'R_NO_SL_MAT')
return {'CANCELLED'}
+ remove_materials(self, "ALL")
+
+ return {'FINISHED'}
+
class VIEW3D_OT_material_remove_all(Operator):
bl_idname = "view3d.material_remove_all"
@@ -1059,19 +1150,20 @@ class VIEW3D_OT_material_remove_all(Operator):
# materials can't be removed in Edit mode
return (c_data_has_materials() and
context.active_object is not None and
- not context.object.mode == 'EDIT')
+ not context.active_object.mode == 'EDIT')
def invoke(self, context, event):
return context.window_manager.invoke_confirm(self, event)
def execute(self, context):
- if context.selected_editable_objects:
- remove_materials_all(self)
- return {'FINISHED'}
- else:
+ if not context.selected_editable_objects:
warning_messages(self, 'R_NO_SL_MAT')
return {'CANCELLED'}
+ remove_materials_all(self)
+
+ return {'FINISHED'}
+
class VIEW3D_OT_select_material_by_name(Operator):
bl_idname = "view3d.select_material_by_name"
@@ -1080,20 +1172,59 @@ class VIEW3D_OT_select_material_by_name(Operator):
bl_options = {'REGISTER', 'UNDO'}
matname = StringProperty(
- name='Material Name',
- description='Name of Material to Select',
- maxlen=63,
- )
+ name="Material Name",
+ description="Name of Material to Select",
+ maxlen=63,
+ )
+ is_not_undo = False
+ is_edit = False
@classmethod
def poll(cls, context):
+ obj = context.active_object
return (c_data_has_materials() and
- context.active_object is not None)
+ obj is not None and obj.mode in {"OBJECT", "EDIT"})
+
+ def invoke(self, context, event):
+ self.is_not_undo = True
+
+ if use_mat_menu_type() not in {'POPUP'}:
+ return self.execute(context)
+
+ obj = context.active_object
+ self.is_edit = bool(obj.mode == 'EDIT')
+ materials_lists_fill_names(context, refresh=True, is_object=self.is_edit)
+
+ return context.window_manager.invoke_props_dialog(self, width=400, height=200)
+
+ def check(self, context):
+ return self.is_not_undo
+
+ def draw(self, context):
+ draw_ui_list_popups(self, context, obj_data=self.is_edit)
def execute(self, context):
- mn = self.matname
+ if use_mat_menu_type() == 'POPUP':
+ mats_col = context.scene.mat_specials_mats
+ scene = context.scene.mat_specials
+ len_mats = len(mats_col)
+ mat_index = scene.index_mat
+
+ if not (len_mats > 0 and mat_index is not None and mat_index <= len_mats):
+ self.report({'WARNING'},
+ "No materials found. Operation Cancelled")
+ return {"CANCELLED"}
+
+ mats_up = mats_col[mat_index].name
+ mn = mats_up
+ else:
+ mn = self.matname
+
select_material_by_name(mn)
- warning_messages(self, 'SL_MAT_BY_NAME', mn)
+ message = 'SL_MAT_EDIT_BY_NAME' if self.is_edit else 'SL_MAT_BY_NAME'
+ warning_messages(self, message, mn)
+ self.is_not_undo = False
+
return {'FINISHED'}
@@ -1104,25 +1235,26 @@ class VIEW3D_OT_replace_material(Operator):
bl_options = {'REGISTER', 'UNDO'}
matorg = StringProperty(
- name="Original",
- description="Material to replace",
- maxlen=63,
- )
+ name="Original",
+ description="Material to replace",
+ maxlen=63,
+ )
matrep = StringProperty(
- name="Replacement",
- description="Replacement material",
- maxlen=63,
- )
+ name="Replacement",
+ description="Replacement material",
+ maxlen=63,
+ )
all_objects = BoolProperty(
- name="All objects",
- description="Replace for all objects in this blend file",
- default=True,
- )
+ name="All objects",
+ description="If enabled, replace for all objects in this blend file\n"
+ "If disabled, only selected objects will be affected",
+ default=False,
+ )
update_selection = BoolProperty(
- name="Update Selection",
- description="Select affected objects and deselect unaffected",
- default=True,
- )
+ name="Update Selection",
+ description="Select affected objects and deselect unaffected",
+ default=True,
+ )
@classmethod
def poll(cls, context):
@@ -1139,9 +1271,12 @@ class VIEW3D_OT_replace_material(Operator):
return context.window_manager.invoke_props_dialog(self)
def execute(self, context):
- replace_material(self.matorg, self.matrep, self.all_objects,
- self.update_selection, self)
+ replace_material(
+ self.matorg, self.matrep, self.all_objects,
+ self.update_selection, self
+ )
self.matorg, self.matrep = "", ""
+
return {'FINISHED'}
@@ -1152,22 +1287,22 @@ class VIEW3D_OT_fake_user_set(Operator):
bl_options = {'REGISTER', 'UNDO'}
fake_user = EnumProperty(
- name="Fake User",
- description="Turn fake user on or off",
- items=(('ON', "On", "Enable fake user"), ('OFF', "Off", "Disable fake user")),
- default='ON',
- )
+ name="Fake User",
+ description="Turn fake user on or off",
+ items=(('ON', "On", "Enable fake user"), ('OFF', "Off", "Disable fake user")),
+ default='ON',
+ )
materials = EnumProperty(
- name="Materials",
- description="Chose what objects and materials to affect",
- items=(('ACTIVE', "Active object", "Materials of active object only"),
- ('SELECTED', "Selected objects", "Materials of selected objects"),
- ('SCENE', "Scene objects", "Materials of objects in current scene"),
- ('USED', "Used", "All materials used by objects"),
- ('UNUSED', "Unused", "Currently unused materials"),
- ('ALL', "All", "All materials in this blend file")),
- default='UNUSED',
- )
+ name="Materials",
+ description="Chose what objects and materials to affect",
+ items=(('ACTIVE', "Active object", "Materials of active object only"),
+ ('SELECTED', "Selected objects", "Materials of selected objects"),
+ ('SCENE', "Scene objects", "Materials of objects in current scene"),
+ ('USED', "Used", "All materials used by objects"),
+ ('UNUSED', "Unused", "Currently unused materials"),
+ ('ALL', "All", "All materials in this blend file")),
+ default='UNUSED',
+ )
@classmethod
def poll(cls, context):
@@ -1183,6 +1318,7 @@ class VIEW3D_OT_fake_user_set(Operator):
def execute(self, context):
fake_user_set(self.fake_user, self.materials, self)
+
return {'FINISHED'}
@@ -1196,15 +1332,15 @@ class MATERIAL_OT_set_transparent_back_side(Operator):
@classmethod
def poll(cls, context):
obj = context.active_object
- if (not obj):
+ if not obj:
return False
mat = obj.active_material
- if (not mat):
+ if not mat:
return False
- if (mat.node_tree):
+ if mat.node_tree:
if (len(mat.node_tree.nodes) == 0):
return True
- if (not mat.use_nodes):
+ if not mat.use_nodes:
return True
return False
@@ -1246,7 +1382,7 @@ class MATERIAL_OT_move_slot_top(Operator):
@classmethod
def poll(cls, context):
obj = context.active_object
- if (not obj):
+ if not obj:
return False
if (len(obj.material_slots) <= 2):
return False
@@ -1276,7 +1412,7 @@ class MATERIAL_OT_move_slot_bottom(Operator):
@classmethod
def poll(cls, context):
obj = context.active_object
- if (not obj):
+ if not obj:
return False
if (len(obj.material_slots) <= 2):
return False
@@ -1306,15 +1442,14 @@ class MATERIAL_OT_link_to_base_names(Operator):
bl_options = {'REGISTER', 'UNDO'}
mat_keep = StringProperty(
- name="Material to keep",
- default="",
- )
+ name="Material to keep",
+ default="",
+ )
is_auto = BoolProperty(
- name="Auto Rename/Replace",
- description=("Automatically Replace names "
- "by stripping numerical suffix"),
- default=False,
- )
+ name="Auto Rename/Replace",
+ description="Automatically Replace names by stripping numerical suffix",
+ default=False,
+ )
mat_error = [] # collect mat for warning messages
is_not_undo = False # prevent drawing props on undo
check_no_name = True # check if no name is passed
@@ -1325,16 +1460,18 @@ class MATERIAL_OT_link_to_base_names(Operator):
def draw(self, context):
layout = self.layout
- if self.is_not_undo is True:
- boxee = layout.box()
- boxee.prop_search(self, "mat_keep", bpy.data, "materials")
- boxee.enabled = not self.is_auto
- layout.separator()
-
- boxs = layout.box()
- boxs.prop(self, "is_auto", text="Auto Rename/Replace", icon="SYNTAX_ON")
- else:
- layout.label(text="**Only Undo is available**", icon="INFO")
+
+ if self.is_not_undo is False:
+ layout.label(text=UNDO_MESSAGE, icon="INFO")
+ return
+
+ boxee = layout.box()
+ boxee.prop_search(self, "mat_keep", bpy.data, "materials")
+ boxee.enabled = not self.is_auto
+ layout.separator()
+
+ boxs = layout.box()
+ boxs.prop(self, "is_auto", text="Auto Rename/Replace", icon="SYNTAX_ON")
def invoke(self, context, event):
self.is_not_undo = True
@@ -1430,13 +1567,14 @@ class MATERIAL_OT_link_to_base_names(Operator):
warning_messages(self, 'MAT_LINK_ERROR', self.mat_error, 'MAT')
self.is_not_undo = False
+
return {'FINISHED'}
class MATERIAL_OT_check_converter_path(Operator):
bl_idname = "material.check_converter_path"
bl_label = "Check Converters images/data save path"
- bl_description = "Check if the given path is writeable (has OS writing privileges)"
+ bl_description = "Check if the given path is writable (has OS writing privileges)"
bl_options = {'REGISTER', 'INTERNAL'}
def check_valid_path(self, context):
@@ -1447,24 +1585,24 @@ class MATERIAL_OT_check_converter_path(Operator):
warning_messages(self, "DIR_PATH_EMPTY", override=True)
return False
- if os_path.exists(paths):
- if os_access(paths, os.W_OK | os.X_OK):
- try:
- path_test = os_path.join(paths, "XYfoobartestXY.txt")
- with open(path_test, 'w') as f:
- f.closed
- os_remove(path_test)
- return True
- except (OSError, IOError):
- warning_messages(self, 'DIR_PATH_W_ERROR', override=True)
- return False
- else:
- warning_messages(self, 'DIR_PATH_A_ERROR', override=True)
- return False
- else:
+ if not os_path.exists(paths):
warning_messages(self, 'DIR_PATH_N_ERROR', override=True)
return False
+ if not os_access(paths, os.W_OK | os.X_OK):
+ warning_messages(self, 'DIR_PATH_A_ERROR', override=True)
+ return False
+
+ try:
+ path_test = os_path.join(paths, "XYfoobartestXY.txt")
+ with open(path_test, 'w') as f:
+ f.closed
+ os_remove(path_test)
+ return True
+ except (OSError, IOError):
+ warning_messages(self, 'DIR_PATH_W_ERROR', override=True)
+ return False
+
return True
def execute(self, context):
@@ -1476,6 +1614,67 @@ class MATERIAL_OT_check_converter_path(Operator):
return {'FINISHED'}
+# Material selections pop-up
+
+class VIEW3D_UL_assign_material_popup_ui(UIList):
+
+ def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
+ self.use_filter_show = True
+
+ col = layout.column(align=True)
+ col.alignment = "LEFT"
+ mat = bpy.data.materials.get(item.name, None)
+ if not mat:
+ col.label(text="{} - is not available".format(item.name), icon="ERROR")
+ else:
+ split = col.split(percentage=0.75, align=True)
+ row = split.row(align=True)
+ row.label(text=item.name, translate=False, icon="MATERIAL_DATA")
+ subrow = split.row(align=True)
+ subrow.alignment = "RIGHT"
+ subrow.label(text="", icon="PINNED" if item.mat_fake_user else "BLANK1")
+ subrow = split.row(align=True)
+ subrow.alignment = "RIGHT"
+ subrow.label(text="", icon="LINK_BLEND" if item.mat_lib else "BLANK1")
+
+
+def draw_ui_list_popups(self, context, obj_data=False):
+ layout = self.layout
+
+ if not self.is_not_undo:
+ layout.label(text=UNDO_MESSAGE, icon="INFO")
+ return
+
+ matlen = len(context.scene.mat_specials_mats)
+ matdata = "in Object's Data" if obj_data else "in Data"
+ matgramma = "Material" if matlen == 1 else "Materials"
+ matcount = "No" if matlen < 1 else matlen
+
+ box = layout.box()
+ col = box.column(align=True)
+ col.label(text="{} {} {}".format(matcount, matgramma, matdata),
+ icon="INFO")
+ sub_split = col.split(percentage=0.7, align=True)
+ sub_box_1 = sub_split.box()
+ sub_box_1.label("Name")
+ sub_split_2 = sub_split.split(percentage=0.5, align=True)
+ sub_box_2 = sub_split_2.box()
+ sub_box_2.label("Fake")
+ sub_box_3 = sub_split_2.box()
+ sub_box_3.label("Lib")
+
+ col.template_list(
+ "VIEW3D_UL_assign_material_popup_ui",
+ 'mat_specials',
+ context.scene,
+ 'mat_specials_mats',
+ context.scene.mat_specials,
+ 'index_mat',
+ rows=10
+ )
+ return
+
+
# Menu classes
class VIEW3D_MT_assign_material(Menu):
@@ -1485,26 +1684,45 @@ class VIEW3D_MT_assign_material(Menu):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
- if c_data_has_materials():
- # no materials
- for material_name in bpy.data.materials.keys():
- mats = layout.operator("view3d.assign_material",
- text=material_name,
- icon='MATERIAL_DATA')
- mats.matname = material_name
- mats.is_existing = True
- use_separator(self, context)
-
if (not context.active_object):
# info why the add material is innactive
layout.label(text="*No active Object in the Scene*", icon="INFO")
use_separator(self, context)
+
mat_prop_name = context.scene.mat_specials.set_material_name
- add_new = layout.operator("view3d.assign_material",
- text="Add New", icon='ZOOMIN')
+ add_new = layout.operator(
+ "view3d.assign_material",
+ text="Add New", icon='ZOOMIN'
+ )
add_new.matname = mat_prop_name
add_new.is_existing = False
+ if use_mat_menu_type() != 'POPUP':
+ use_separator(self, context)
+
+ if c_data_has_materials():
+ mat_entry = layout.column()
+
+ if use_mat_menu_type() == 'POPUP':
+ matp = mat_entry.operator(
+ "view3d.assign_material",
+ icon='MATERIAL_DATA'
+ )
+ matp.is_existing = True
+ elif use_mat_menu_type() == 'COLUMNS':
+ get_col_size = switch_to_column()
+ mat_entry = layout.column_flow(columns=get_col_size)
+
+ if use_mat_menu_type() in {'COLUMNS', 'STANDARD'}:
+ for material_name in bpy.data.materials.keys():
+ mats = mat_entry.operator(
+ "view3d.assign_material",
+ text=material_name,
+ icon='MATERIAL_DATA'
+ )
+ mats.matname = material_name
+ mats.is_existing = True
+
class VIEW3D_MT_select_material(Menu):
bl_label = "Select by Material"
@@ -1512,28 +1730,58 @@ class VIEW3D_MT_select_material(Menu):
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
- ob = context.object
+ obj = context.active_object
+ has_users = False
+ col = layout.column()
+
+ if not c_data_has_materials():
+ layout.label(text="*No Materials in the Data*", icon="INFO")
+ return
+ elif not obj:
+ layout.label(text="*No Active Object*", icon="INFO")
+ return
+ elif obj.mode == "EDIT" and not c_obj_data_has_materials(obj):
+ layout.label(text="*No Materials in the Object's Data*", icon="INFO")
+ return
+ elif use_mat_menu_type() == 'POPUP':
+ col.operator(
+ "view3d.select_material_by_name",
+ text="Select by Material",
+ icon='HAND',
+ )
+ return
- if (not c_data_has_materials()):
- layout.label(text="*No Materials in the data*", icon="INFO")
- elif (not ob):
- layout.label(text="*No Objects to select*", icon="INFO")
+ if obj.mode == 'OBJECT':
+ if use_mat_menu_type() == 'COLUMNS':
+ get_col_size = switch_to_column(is_edit=False)
+ col = layout.column_flow(columns=get_col_size)
+ # show all used materials in entire blend file
+ for material_name, material in bpy.data.materials.items():
+ if material.users > 0:
+ has_users = True
+ col.operator(
+ "view3d.select_material_by_name",
+ text=material_name,
+ icon='MATERIAL_DATA',
+ ).matname = material_name
+ if not has_users:
+ layout.label(text="*No users for Materials in the data*", icon="INFO")
+ return
+ elif obj.mode == 'EDIT':
+ if use_mat_menu_type() == 'COLUMNS':
+ get_col_size = switch_to_column(is_edit=True)
+ col = layout.column_flow(columns=get_col_size)
+ # show only the materials on this object
+ mats = obj.material_slots.keys()
+ for m in mats:
+ if m not in "": # skip empty slots
+ col.operator(
+ "view3d.select_material_by_name",
+ text=m,
+ icon='MATERIAL_DATA'
+ ).matname = m
else:
- if ob.mode == 'OBJECT':
- # show all used materials in entire blend file
- for material_name, material in bpy.data.materials.items():
- if (material.users > 0):
- layout.operator("view3d.select_material_by_name",
- text=material_name,
- icon='MATERIAL_DATA',
- ).matname = material_name
- elif ob.mode == 'EDIT':
- # show only the materials on this object
- mats = ob.material_slots.keys()
- for m in mats:
- layout.operator("view3d.select_material_by_name",
- text=m,
- icon='MATERIAL_DATA').matname = m
+ layout.label(text="*Works only in Object and Edit mode*", icon="INFO")
class VIEW3D_MT_remove_material(Menu):
@@ -1543,24 +1791,35 @@ class VIEW3D_MT_remove_material(Menu):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
- layout.operator("view3d.clean_material_slots",
- text="Clean Material Slots",
- icon='COLOR_BLUE')
- use_separator(self, context)
+ if context.mode in {'PAINT_TEXTURE'}:
+ layout.label(
+ text="Removing materials can lead to loss of painting data",
+ icon="INFO"
+ )
+ use_separator(self, context)
- if not c_render_engine("Lux"):
- layout.operator("view3d.material_remove_slot", icon='COLOR_GREEN')
- layout.operator("view3d.material_remove_object", icon='COLOR_RED')
+ layout.operator(
+ "view3d.clean_material_slots",
+ text="Clean Material Slots",
+ icon='COLOR_BLUE'
+ )
+ use_separator(self, context)
- if use_remove_mat_all():
- use_separator(self, context)
- layout.operator("view3d.material_remove_all",
- text="Remove Material Slots "
- "(All Selected Objects)",
- icon='CANCEL')
- else:
+ if c_render_engine("Lux"):
layout.label(text="Sorry, other Menu functions are", icon="INFO")
layout.label(text="unvailable with Lux Renderer")
+ return
+
+ layout.operator("view3d.material_remove_slot", icon='COLOR_GREEN')
+ layout.operator("view3d.material_remove_object", icon='COLOR_RED')
+
+ if use_remove_mat_all():
+ use_separator(self, context)
+ layout.operator(
+ "view3d.material_remove_all",
+ text="Remove Material Slots (All Selected Objects)",
+ icon='CANCEL'
+ )
class VIEW3D_MT_master_material(Menu):
@@ -1574,8 +1833,13 @@ class VIEW3D_MT_master_material(Menu):
layout.operator("view3d.show_mat_preview", icon="VISIBLE_IPO_ON")
use_separator(self, context)
- layout.menu("VIEW3D_MT_assign_material", icon='ZOOMIN')
- layout.menu("VIEW3D_MT_select_material", icon='HAND')
+ if use_mat_menu_type() == 'POPUP':
+ VIEW3D_MT_assign_material.draw(self, context)
+ use_separator(self, context)
+ VIEW3D_MT_select_material.draw(self, context)
+ else:
+ layout.menu("VIEW3D_MT_assign_material", icon='ZOOMIN')
+ layout.menu("VIEW3D_MT_select_material", icon='HAND')
use_separator(self, context)
layout.operator("view3d.copy_material_to_selected", icon="COPY_ID")
@@ -1665,8 +1929,15 @@ def menu_func(self, context):
layout.operator_context = 'INVOKE_REGION_WIN'
use_separator(self, context)
- layout.menu("VIEW3D_MT_assign_material", icon='ZOOMIN')
- layout.menu("VIEW3D_MT_select_material", icon='HAND')
+ if use_mat_menu_type() == 'POPUP':
+ VIEW3D_MT_assign_material.draw(self, context)
+ use_separator(self, context)
+ VIEW3D_MT_select_material.draw(self, context)
+ else:
+ layout.menu("VIEW3D_MT_assign_material", icon='ZOOMIN')
+ layout.menu("VIEW3D_MT_select_material", icon='HAND')
+ use_separator(self, context)
+
layout.operator("view3d.replace_material",
text='Replace Material',
icon='ARROW_LEFTRIGHT')
@@ -1741,11 +2012,15 @@ class MATERIAL_PT_scenemassive(Panel):
def poll(cls, context):
return (enable_converters() is True and converter_type('BI_CONV'))
+ def draw_header(self, context):
+ layout = self.layout
+ help_panel_header(layout, menu_type="HELP_MT_biconvert")
+
def draw(self, context):
layout = self.layout
sc = context.scene
- row = layout.row()
- box = row.box()
+ col = layout.column(align=True)
+ box = col.box()
split = box.box().split(0.5)
split.operator("ml.refresh",
@@ -1757,14 +2032,9 @@ class MATERIAL_PT_scenemassive(Panel):
icon='MATERIAL')
ml_restore.switcher = False
ml_restore.renderer = "BI"
-
- box = layout.box()
- row = box.row()
row.menu("scenemassive.opt", text="Advanced Options", icon='SCRIPTWIN')
- row.menu("HELP_MT_biconvert",
- text="Usage Information Guide", icon="INFO")
- box = layout.box()
+ box = col.box()
box.label("Save Directory")
split = box.split(0.85)
split.prop(sc.mat_specials, "conv_path", text="", icon="RENDER_RESULT")
@@ -1783,28 +2053,32 @@ class MATERIAL_PT_xps_convert(Panel):
def poll(cls, context):
return (enable_converters() is True and converter_type('CYC_CONV'))
+ def draw_header(self, context):
+ layout = self.layout
+ help_panel_header(layout, menu_type="help.nodeconvert")
+
def draw(self, context):
layout = self.layout
- row = layout.row()
- box = row.box()
+ col = layout.column(align=True)
+ box = col.box()
- box.label(text="Multi Image Support (Imports)")
+ box.label(text="Multi Image Support (Imports)", icon="INFO")
split = box.box().split(0.5)
- split.operator("xps_tools.convert_to_cycles_all",
- text="Convert All to Nodes", icon="TEXTURE")
- split.operator("xps_tools.convert_to_cycles_selected",
- text="Convert Selected to Nodes", icon="TEXTURE")
+ split.operator(
+ "xps_tools.convert_to_cycles_all",
+ text="Convert All to Nodes", icon="TEXTURE"
+ )
+ split.operator(
+ "xps_tools.convert_to_cycles_selected",
+ text="Convert Selected to Nodes", icon="TEXTURE"
+ )
- box = layout.box()
- row = box.row()
- ml_restore = row.operator("ml.restore", text="To BI Nodes ON",
+ box = col.box()
+ ml_restore = box.operator("ml.restore", text="To BI Nodes ON",
icon='MATERIAL')
ml_restore.switcher = True
ml_restore.renderer = "BI"
- row.menu("help.nodeconvert",
- text="Usage Information Guide", icon="INFO")
-
# Converters Help #
@@ -1826,12 +2100,12 @@ class MATERIAL_MT_biconv_help(Menu):
layout.label(text="Select the texture loaded in the image node")
layout.label(text="Press Ctrl/T to create the image nodes")
layout.label(text="In the Node Editor, Select the Diffuse Node")
- layout.label(text="Enable Node Wrangler addon", icon="NODETREE")
+ layout.label(text="Enable Node Wrangler add-on", icon="NODETREE")
layout.label(text="If Unconnected or No Image Node Error:", icon="MOD_EXPLODE")
use_separator(self, context)
layout.label(text="Extract Alpha: the images have to have alpha channel")
layout.label(text="The default path is the folder where the current .blend is")
- layout.label(text="During Baking, the script will check writting privileges")
+ layout.label(text="During Baking, the script will check writing privileges")
layout.label(text="Set the save path for extracting images with full access")
layout.label(text="May Require Run As Administrator on Windows OS", icon="ERROR")
layout.label(text="Converts Bi Textures to Image Files:", icon="MOD_EXPLODE")
@@ -1861,7 +2135,7 @@ class MATERIAL_MT_nodeconv_help(Menu):
layout.label(text="Select the texture loaded in the image node")
layout.label(text="Press Ctrl/T to create the image nodes")
layout.label(text="In the Node Editor, Select the Diffuse Node")
- layout.label(text="Enable Node Wrangler addon", icon="NODETREE")
+ layout.label(text="Enable Node Wrangler add-on", icon="NODETREE")
layout.label(text="If Unconnected or No Image Node Error:", icon="MOD_EXPLODE")
use_separator(self, context)
layout.label(text="For Specular Nodes, Image color influence has to be enabled")
@@ -1869,7 +2143,7 @@ class MATERIAL_MT_nodeconv_help(Menu):
layout.label(text="The Converter report can point out to some failures")
layout.label(text="Not all Files will produce good results", icon="ERROR")
layout.label(text="fbx, .dae, .obj, .3ds, .xna and more")
- layout.label(text="**Supports Imported Files**:", icon="IMPORT")
+ layout.label(text="*Supports Imported Files*:", icon="IMPORT")
use_separator(self, context)
layout.label(text="For some file types")
layout.label(text="Supports Alpha, Normals, Specular and Diffuse")
@@ -1880,7 +2154,7 @@ class MATERIAL_MT_nodeconv_help(Menu):
# Make Report
-class material_converter_report(Operator):
+class MATERIAL_OT_converter_report(Operator):
bl_idname = "mat_converter.reports"
bl_label = "Material Converter Report"
bl_description = "Report about done Material Conversions"
@@ -1901,208 +2175,309 @@ class material_converter_report(Operator):
return context.window_manager.invoke_props_dialog(self, width=500)
def execute(self, context):
+
return {'FINISHED'}
# Scene Properties
+class material_specials_scene_mats(PropertyGroup):
+ name = StringProperty()
+ mat_lib = BoolProperty(
+ default=False
+ )
+ mat_fake_user = BoolProperty(
+ default=False
+ )
+
+
class material_specials_scene_props(PropertyGroup):
conv_path = StringProperty(
- name="Save Directory",
- description=("Path to save images during conversion \n"
- "Default is the location of the blend file"),
- default="//",
- subtype='DIR_PATH',
- )
+ name="Save Directory",
+ description="Path to save images during conversion\n"
+ "Default is the location of the blend file",
+ default="//",
+ subtype='DIR_PATH'
+ )
EXTRACT_ALPHA = BoolProperty(
- attr="EXTRACT_ALPHA",
- default=False,
- description=("Extract Alpha channel from non-procedural images \n"
- "Don't use this option if the image doesn't have Alpha"),
- )
+ attr="EXTRACT_ALPHA",
+ default=False,
+ description="Extract Alpha channel from non-procedural images\n"
+ "Don't use this option if the image doesn't have Alpha"
+ )
SET_FAKE_USER = BoolProperty(
- attr="SET_FAKE_USER",
- default=False,
- description="Set fake user on unused images, so they can be kept in the .blend",
- )
+ attr="SET_FAKE_USER",
+ default=False,
+ description="Set fake user on unused images, so they can be kept in the .blend"
+ )
EXTRACT_PTEX = BoolProperty(
- attr="EXTRACT_PTEX",
- default=False,
- description="Extract procedural images and bake them to jpeg",
- )
+ attr="EXTRACT_PTEX",
+ default=False,
+ description="Extract procedural images and bake them to jpeg"
+ )
EXTRACT_OW = BoolProperty(
- attr="Overwrite",
- default=False,
- description="Extract textures again instead of re-using priorly extracted textures",
- )
+ attr="Overwrite",
+ default=False,
+ description="Extract textures again instead of re-using previously extracted textures"
+ )
SCULPT_PAINT = BoolProperty(
- attr="SCULPT_PAINT",
- default=False,
- description=("Conversion geared towards sculpting and painting.\n"
- "Creates a diffuse, glossy mixed with layer weight. \n"
- "Image nodes are not connected"),
- )
+ attr="SCULPT_PAINT",
+ default=False,
+ description="Conversion geared towards sculpting and painting.\n"
+ "Creates a diffuse, glossy mixed with layer weight.\n"
+ "Image nodes are not connected"
+ )
UV_UNWRAP = BoolProperty(
- attr="UV_UNWRAP",
- default=False,
- description=("Use automatical Angle based UV Unwrap of the active Object"),
- )
+ attr="UV_UNWRAP",
+ default=False,
+ description="Use automatic Angle based UV Unwrap for the Active Object"
+ )
enable_report = BoolProperty(
- attr="enable_report",
- default=False,
- description=("Enable Converter Report in the UI"),
- )
+ attr="enable_report",
+ default=False,
+ description="Enable Converter Report in the UI"
+ )
img_bake_size = EnumProperty(
- name="Bake Image Size",
- description="Set the resolution size of baked images \n",
- items=(('512', "Set : 512 x 512", "Bake Resolution 512 x 512"),
- ('1024', "Set : 1024 x 1024", "Bake Resolution 1024 x 1024"),
- ('2048', "Set : 2048 x 2048", "Bake Resolution 2048 x 2048")),
- default='1024',
- )
+ name="Bake Image Size",
+ description="Set the resolution size of baked images \n",
+ items=(
+ ('512', "Set : 512 x 512", "Bake Resolution 512 x 512"),
+ ('1024', "Set : 1024 x 1024", "Bake Resolution 1024 x 1024"),
+ ('2048', "Set : 2048 x 2048", "Bake Resolution 2048 x 2048")
+ ),
+ default='1024'
+ )
set_material_name = StringProperty(
- name="New Material name",
- description="What Base name pattern to use for a new created Material\n"
- "It is appended by an automatic numeric pattern depending\n"
- "on the number of Scene's materials containing the Base",
- default="Material_New",
- maxlen=128,
- )
+ name="New Material name",
+ description="What Base name pattern to use for a new created Material\n"
+ "It is appended by an automatic numeric pattern depending\n"
+ "on the number of Scene's materials containing the Base",
+ default="Material_New",
+ maxlen=128
+ )
use_tweak = BoolProperty(
name="Tweak Settings",
description="Open Preview Active Material after new Material creation",
- default=False,
- )
+ default=False
+ )
+ index_mat = IntProperty(
+ name="index",
+ options={"HIDDEN"}
+ )
-# Addon Preferences
+# Add-on Preferences
class VIEW3D_MT_material_utils_pref(AddonPreferences):
bl_idname = __name__
show_warnings = BoolProperty(
- name="Enable Warning messages",
- default=False,
- description="Show warning messages \n"
- "when an action is executed or failed.\n \n"
- "Advisable if you don't know how the tool works",
- )
+ name="Enable Warning messages",
+ default=False,
+ description="Show warning messages when an action is executed or failed"
+ )
show_remove_mat = BoolProperty(
- name="Enable Remove all Materials",
- default=False,
- description="Enable Remove all Materials for all Selected Objects\n\n"
- "Use with care - if you want to keep materials after\n"
- "closing or reloading Blender, Set Fake User for them",
- )
+ name="Enable Remove all Materials",
+ default=False,
+ description="Enable Remove all Materials for all Selected Objects\n\n"
+ "Use with care - if you want to keep materials after\n"
+ "closing or reloading Blender, Set Fake User for them"
+ )
show_mat_preview = BoolProperty(
- name="Enable Material Preview",
- default=True,
- description="Material Preview of the Active Object \n"
- "Contains the preview of the active Material, \n"
- "Use nodes, Color, Specular and Transparency \n"
- "settings depending on the Context and Preferences",
- )
+ name="Enable Material Preview",
+ default=True,
+ description="Material Preview of the Active Object\n"
+ "Contains the preview of the active Material,\n"
+ "Use nodes, Color, Specular and Transparency\n"
+ "settings depending on the Context and Preferences"
+ )
set_cleanmatslots = BoolProperty(
- name="Enable Auto Clean",
- default=True,
- description="Enable Automatic Removal of unused Material Slots \n"
- "called together with the Assign Material menu option. \n \n"
- "Apart from preference and the cases when it affects \n"
- "adding materials, enabling it can have some \n"
- "performance impact on very dense meshes",
- )
+ name="Enable Auto Clean",
+ default=True,
+ description="Enable Automatic Removal of unused Material Slots\n"
+ "called together with the Assign Material menu option.\n"
+ "Apart from preference and the cases when it affects\n"
+ "adding materials, enabling it can have some\n"
+ "performance impact on very dense meshes"
+ )
show_separators = BoolProperty(
- name="Use Separators in the menus",
- default=True,
- description="Use separators in the menus, a trade-off between \n"
- "readability vs. using more space for displaying items",
- )
+ name="Use Separators in the menus",
+ default=True,
+ description="Use separators in the menus, a trade-off between\n"
+ "readability vs. using more space for displaying items"
+ )
show_converters = BoolProperty(
- name="Enable Converters",
- default=False,
- description="Enable Material Converters",
- )
+ name="Enable Converters",
+ default=False,
+ description="Enable Material Converters"
+ )
set_preview_size = EnumProperty(
- name="Preview Menu Size",
- description="Set the preview menu size \n"
- "depending on the number of materials \n"
- "in the scene (width and height)",
- items=(('2x2', "Size 2x2", "Width 2 Height 2"),
- ('2x3', "Size 2x3", "Width 3 Height 2"),
- ('3x3', "Size 3x3", "Width 3 Height 3"),
- ('3x4', "Size 3x4", "Width 4 Height 3"),
- ('4x4', "Size 4x4", "Width 4 Height 4"),
- ('5x5', "Size 5x5", "Width 5 Height 5"),
- ('6x6', "Size 6x6", "Width 6 Height 6"),
- ('0x0', "List", "Display as a List")),
- default='3x3',
- )
+ name="Preview Menu Size",
+ description="Set the preview menu size\n"
+ "depending on the number of materials\n"
+ "in the scene (width and height)",
+ items=(
+ ('2x2', "Size 2x2", "Width 2 Height 2"),
+ ('2x3', "Size 2x3", "Width 3 Height 2"),
+ ('3x3', "Size 3x3", "Width 3 Height 3"),
+ ('3x4', "Size 3x4", "Width 4 Height 3"),
+ ('4x4', "Size 4x4", "Width 4 Height 4"),
+ ('5x5', "Size 5x5", "Width 5 Height 5"),
+ ('6x6', "Size 6x6", "Width 6 Height 6"),
+ ('0x0', "List", "Display as a List")
+ ),
+ default='3x3'
+ )
set_preview_type = EnumProperty(
- name="Preview Menu Type",
- description="Set the the Preview menu type",
- items=(('LIST', "Classic",
- "Display as a Classic List like in Blender Propreties.\n"
- "Preview of Active Material is not available"),
- ('PREVIEW', "Preview Display",
- "Display as a preview of Thumbnails\n"
- "It can have some performance issues with scenes containing a lot of materials\n"
- "Preview of Active Material is available")),
- default='PREVIEW',
- )
+ name="Preview Menu Type",
+ description="Set the the Preview menu type",
+ items=(
+ ('LIST', "Classic",
+ "Display as a Classic List like in Blender Properties.\n"
+ "Preview of Active Material is not available\n\n"
+ "Note: Choosing a different material from the list will replace the active one"),
+ ('PREVIEW', "Preview Display",
+ "Display as a preview of Thumbnails\n"
+ "It can have some performance issues with scenes containing a lot of materials\n"
+ "Preview of Active Material is available\n\n"
+ "Note: Choosing a different material from the list will replace the active one")
+ ),
+ default='PREVIEW'
+ )
set_experimental_type = EnumProperty(
- name="Experimental Features",
- description="Set the Type of converters enabled",
- items=(('ALL', "All Converters",
- "Enable all Converters"),
- ('CYC_CONV', "BI and Cycles Nodes",
- "Enable Cycles related Convert"),
- ('BI_CONV', "BI To Cycles",
- "Enable Blender Internal related Converters")),
- default='ALL',
- )
+ name="Experimental Features",
+ description="Set the type of converters enabled",
+ items=(
+ ('ALL', "All Converters",
+ "Enable all Converters"),
+ ('CYC_CONV', "BI and Cycles Nodes",
+ "Enable Cycles related Convert"),
+ ('BI_CONV', "BI To Cycles",
+ "Enable Blender Internal related Converters")
+ ),
+ default='ALL',
+ )
+ set_add_material_menu = EnumProperty(
+ name="Add Material Menu",
+ description="Set the type of Add Material menu",
+ items=(
+ ('STANDARD', "Standard Menu",
+ "Material entries in the menu are bellow each other"),
+ ('COLUMNS', "Column Menu",
+ "Material entries are placed in column blocks"),
+ ('POPUP', "Pop up Menu",
+ "Material entries are placed in a scrollable list inside a pop-up menu")
+ ),
+ default='POPUP'
+ )
def draw(self, context):
layout = self.layout
sc = context.scene
- box = layout.box()
+ col_m = layout.column(align=True)
+
+ box = col_m.box()
box.label("Save Directory")
split = box.split(0.85)
split.prop(sc.mat_specials, "conv_path", text="", icon="RENDER_RESULT")
- split.operator("material.check_converter_path",
- text="", icon="EXTERNAL_DATA")
-
- box = layout.box()
+ split.operator(
+ "material.check_converter_path",
+ text="", icon="EXTERNAL_DATA"
+ )
+ box = col_m.box()
split = box.split(align=True)
- col = split.column()
+ col = split.column(align=True)
col.prop(self, "show_warnings")
col.prop(self, "show_remove_mat")
-
- col = split.column()
- col.alignment = 'RIGHT'
col.prop(self, "set_cleanmatslots")
col.prop(self, "show_separators")
- boxie = box.box()
- split = boxie.split(percentage=0.3)
+ col = split.column(align=True)
+ col.label("Apply / Select Material mode:")
+ col.prop(self, "set_add_material_menu", expand=True)
+
+ box = col_m.box()
+ size_split = 0.3 if self.show_mat_preview else 1.0
+ split = box.split(percentage=size_split, align=True)
split.prop(self, "show_mat_preview")
+
if self.show_mat_preview:
- rowsy = split.row(align=True)
- rowsy.enabled = True if self.show_mat_preview else False
- rowsy.prop(self, "set_preview_type", expand=True)
- rowsa = boxie.row(align=True)
- rowsa.enabled = True if self.set_preview_type in {'PREVIEW'} else False
- rowsa.prop(self, "set_preview_size", expand=True)
-
- boxif = box.box()
- split = boxif.split(percentage=0.3)
+ subsplit = split.split(percentage=0.7, align=True)
+ row = subsplit.row(align=True)
+ row.prop(self, "set_preview_type", expand=True)
+
+ subrow = subsplit.row(align=True)
+ subrow.enabled = True if self.set_preview_type in {'PREVIEW'} else False
+ subrow.prop(self, "set_preview_size", text="")
+
+ box = col_m.box()
+ size_split = 0.3 if self.show_converters else 1.0
+ split = box.split(percentage=size_split, align=True)
split.prop(self, "show_converters")
+
if self.show_converters:
- rowe = split.row(align=True)
- rowe.prop(self, "set_experimental_type", expand=True)
+ row = split.row(align=True)
+ row.prop(self, "set_experimental_type", expand=True)
# utility functions:
+def help_panel_header(layout, menu_type="VIEW3D_MT_master_material"):
+ layout.separator()
+ box = layout.box()
+ box.scale_y = 0.5
+ box.menu(menu_type, text="", icon="INFO")
+ layout.separator()
+
+
+def activate_mat_slot(actob, matname):
+ mats = actob.material_slots
+ for i, m in enumerate(mats):
+ if m.name == matname:
+ # make slot active
+ actob.active_material_index = i
+ break
+
+
+def materials_lists_fill_names(context, refresh=False, is_object=False):
+ mats_list = context.scene.mat_specials_mats
+ if refresh:
+ for key in mats_list.keys():
+ index = mats_list.find(key)
+ if index != -1:
+ mats_list.remove(index)
+
+ obj = context.active_object
+ mat_collection = []
+ if (is_object and obj):
+ mat_collection = [
+ slot.material for slot in obj.material_slots if
+ slot.material
+ ]
+ else:
+ mat_collection = bpy.data.materials
+
+ for mat in mat_collection:
+ if mat.name not in mats_list.keys() or refresh:
+ prop = mats_list.add()
+ prop.name = mat.name
+ prop.mat_lib = bool(mat.library)
+ prop.mat_fake_user = mat.use_fake_user
+
+
+def switch_to_column(is_edit=False):
+ obj = bpy.context.active_object
+ collect = obj.material_slots if is_edit else bpy.data.materials
+ col_size = int(round(len(collect) / COLUMN_SPLIT))
+
+ return col_size if col_size > 0 else 1
+
+
+def remove_material_slot():
+ if bpy.ops.object.material_slot_remove.poll():
+ bpy.ops.object.material_slot_remove()
+
+
def check_mat_name_unique(name_id="Material_new"):
# check if the new name pattern is in materials' data
name_list = []
@@ -2229,6 +2604,13 @@ def use_remove_mat_all():
return bool(show_rmv_mat)
+def use_mat_menu_type():
+ pref = return_preferences()
+ use_menu_mat = pref.set_add_material_menu
+
+ return use_menu_mat
+
+
def use_mat_preview():
pref = return_preferences()
show_mat_prw = pref.show_mat_preview
@@ -2283,8 +2665,12 @@ def register():
# Register Scene Properties
bpy.types.Scene.mat_specials = PointerProperty(
- type=material_specials_scene_props
- )
+ type=material_specials_scene_props
+ )
+ bpy.types.Scene.mat_specials_mats = CollectionProperty(
+ name="Material name",
+ type=material_specials_scene_mats
+ )
kc = bpy.context.window_manager.keyconfigs.addon
if kc:
@@ -2310,6 +2696,7 @@ def unregister():
bpy.types.MATERIAL_MT_specials.remove(menu_func)
del bpy.types.Scene.mat_specials
+ del bpy.types.Scene.mat_specials_mats
bpy.utils.unregister_module(__name__)
diff --git a/materials_utils/material_converter.py b/materials_utils/material_converter.py
index dfde5c50..bf52bec1 100644
--- a/materials_utils/material_converter.py
+++ b/materials_utils/material_converter.py
@@ -1,14 +1,15 @@
# -*- coding: utf-8 -*-
import bpy
+import math
from mathutils import Vector
from bpy.types import Operator
from .warning_messages_utils import (
- warning_messages,
- c_is_cycles_addon_enabled,
- c_data_has_materials,
- collect_report,
- )
+ warning_messages,
+ c_is_cycles_addon_enabled,
+ c_data_has_materials,
+ collect_report,
+)
# -----------------------------------------------------------------------------
# Globals
@@ -665,7 +666,7 @@ def makeCyclesFromBI(cmat):
# Make Diffuse and Output nodes
mainShader = makeMainShader(TreeNodes)
- mainShader.inputs['Roughness'].default_value = cmat.specular_intensity
+ mainShader.inputs['Roughness'].default_value = math.sqrt(max(cmat.specular_intensity, 0.0))
mainDiffuse = mainShader
materialOutput = makeMaterialOutput(TreeNodes)
links.new(mainShader.outputs['BSDF'], materialOutput.inputs['Surface'])
@@ -752,6 +753,7 @@ class material_convert_all(Operator):
def execute(self, context):
AutoNode(False, self)
+
return {'FINISHED'}
@@ -766,13 +768,12 @@ class material_convert_selected(Operator):
def poll(cls, context):
return (bpy.data.filepath != "" and c_data_has_materials() and
c_is_cycles_addon_enabled() and
- bool(next((obj for obj in context.selected_objects if obj.type == 'MESH'),
- None)
- )
+ bool(next((obj for obj in context.selected_objects if obj.type == 'MESH'), None))
)
def execute(self, context):
AutoNode(True, self)
+
return {'FINISHED'}
diff --git a/materials_utils/materials_cycles_converter.py b/materials_utils/materials_cycles_converter.py
index c9dd994d..13e1e1ec 100644
--- a/materials_utils/materials_cycles_converter.py
+++ b/materials_utils/materials_cycles_converter.py
@@ -6,18 +6,18 @@ import bpy
from os import path as os_path
from bpy.types import Operator
from math import (
- log2, ceil,
- )
+ log2, ceil, sqrt,
+)
from bpy.props import (
- BoolProperty,
- EnumProperty,
- )
+ BoolProperty,
+ EnumProperty,
+)
from .warning_messages_utils import (
- warning_messages,
- c_is_cycles_addon_enabled,
- c_data_has_materials,
- collect_report,
- )
+ warning_messages,
+ c_is_cycles_addon_enabled,
+ c_data_has_materials,
+ collect_report,
+)
# -----------------------------------------------------------------------------
# Globals
@@ -130,7 +130,7 @@ def BakingText(tex, mode, tex_type=None):
img = bpy.data.images.get("TMP_BAKING")
img.file_format = ("JPEG" if not mode == "ALPHA" else "PNG")
- # switch temporarly to 'IMAGE EDITOR', other approaches are not reliable
+ # switch temporarily to 'IMAGE EDITOR', other approaches are not reliable
check_area = False
store_area = bpy.context.area.type
collect_report("INFO: Temporarly switching context to Image Editor")
@@ -267,8 +267,7 @@ def AutoNode(active=False, operator=None):
for n in TreeNodes.nodes:
TreeNodes.nodes.remove(n)
- # Starting point is diffuse BSDF and output material
- # and a Color Ramp node
+ # Starting point is diffuse BSDF and output material and a Color Ramp node
shader = TreeNodes.nodes.new('ShaderNodeBsdfDiffuse')
shader.location = 10, 10
shader_val = TreeNodes.nodes.new('ShaderNodeValToRGB')
@@ -382,7 +381,7 @@ def AutoNode(active=False, operator=None):
else:
# Create Clay Material (Diffuse, Glossy, Layer Weight)
shader.inputs['Color'].default_value = PAINT_SC_COLOR
- shader.inputs['Roughness'].default_value = 0.9
+ shader.inputs['Roughness'].default_value = 0.9486
# remove Color Ramp and links from the default shader and reroute
try:
@@ -416,10 +415,10 @@ def AutoNode(active=False, operator=None):
shader.inputs['Roughness'].default_value = cmat.specular_intensity
if shader.type == 'ShaderNodeBsdfGlossy':
- shader.inputs['Roughness'].default_value = 1 - cmat.raytrace_mirror.gloss_factor
+ shader.inputs['Roughness'].default_value = sqrt(max(1 - cmat.raytrace_mirror.gloss_factor, 0.0))
if shader.type == 'ShaderNodeBsdfGlass':
- shader.inputs['Roughness'].default_value = 1 - cmat.raytrace_mirror.gloss_factor
+ shader.inputs['Roughness'].default_value = sqrt(max(1 - cmat.raytrace_mirror.gloss_factor, 0.0))
shader.inputs['IOR'].default_value = cmat.raytrace_transparency.ior
if shader.type == 'ShaderNodeEmission':
@@ -848,6 +847,21 @@ def create_mix_node(TreeNodes, links, nodes, loc, start, median_point, row, fram
return mix_node
+def unwrap_active_object(context):
+ enable_unwrap = context.scene.mat_specials.UV_UNWRAP
+ if enable_unwrap:
+ obj_name = getattr(context.active_object, "name", "UNNAMED OBJECT")
+ try:
+ # it's possible that the active object would fail UV Unwrap
+ bpy.ops.object.editmode_toggle()
+ bpy.ops.uv.unwrap(method='ANGLE_BASED', margin=0.001)
+ bpy.ops.object.editmode_toggle()
+ collect_report("INFO: UV Unwrapping active object {}".format(obj_name))
+ except:
+ collect_report("ERROR: UV Unwrapping failed for "
+ "active object {}".format(obj_name))
+
+
# -----------------------------------------------------------------------------
# Operator Classes
@@ -866,10 +880,8 @@ class mllock(Operator):
TreeNodes = cmat.node_tree
for n in TreeNodes.nodes:
if n.type == 'ShaderNodeOutputMaterial':
- if n.label == 'Locked':
- n.label = ''
- else:
- n.label = 'Locked'
+ n.label = "" if n.label == "Locked" else "Locked"
+
return {'FINISHED'}
@@ -882,28 +894,17 @@ class mlrefresh(Operator):
@classmethod
def poll(cls, context):
- return (bpy.data.filepath != ""and c_is_cycles_addon_enabled() and
+ return (bpy.data.filepath != "" and c_is_cycles_addon_enabled() and
c_data_has_materials())
def execute(self, context):
AutoNodeInitiate(False, self)
if CHECK_AUTONODE is True:
- enable_unwrap = bpy.context.scene.mat_specials.UV_UNWRAP
- if enable_unwrap:
- obj_name = getattr(context.active_object, "name", "UNNAMED OBJECT")
- try:
- # it's possible to the active object would fail UV Unwrap
- bpy.ops.object.editmode_toggle()
- bpy.ops.uv.unwrap(method='ANGLE_BASED', margin=0.001)
- bpy.ops.object.editmode_toggle()
- collect_report("INFO: UV Unwrapping active object "
- "{}".format(obj_name))
- except:
- collect_report("ERROR: UV Unwrapping failed for "
- "active object {}".format(obj_name))
+ unwrap_active_object(context)
collect_report("Conversion finished !", False, True)
+
return {'FINISHED'}
@@ -922,20 +923,10 @@ class mlrefresh_active(Operator):
def execute(self, context):
AutoNodeInitiate(True, self)
if CHECK_AUTONODE is True:
- obj_name = getattr(context.active_object, "name", "UNNAMED OBJECT")
- enable_unwrap = bpy.context.scene.mat_specials.UV_UNWRAP
- if enable_unwrap:
- try:
- # you can already guess it, what could happen here
- bpy.ops.object.editmode_toggle()
- bpy.ops.uv.unwrap(method='ANGLE_BASED', margin=0.001)
- bpy.ops.object.editmode_toggle()
- collect_report("INFO: UV Unwrapping object {}".format(obj_name))
- except:
- collect_report("ERROR: UV Unwrapping failed for "
- "object {}".format(obj_name))
+ unwrap_active_object(context)
collect_report("Conversion finished !", False, True)
+
return {'FINISHED'}
@@ -947,27 +938,28 @@ class mlrestore(Operator):
bl_options = {'REGISTER', 'UNDO'}
switcher = BoolProperty(
- name="Use Nodes",
- description="When restoring, switch Use Nodes On/Off",
- default=True
- )
+ name="Use Nodes",
+ description="When restoring, switch Use Nodes On/Off",
+ default=True
+ )
renderer = EnumProperty(
- name="Renderer",
- description="Choose Cycles or Blender Internal",
- items=(('CYCLES', "Cycles", "Switch to Cycles"),
- ('BI', "Blender Internal", "Switch to Blender Internal")),
- default='CYCLES',
- )
+ name="Renderer",
+ description="Choose Cycles or Blender Internal",
+ items=(
+ ('CYCLES', "Cycles", "Switch to Cycles"),
+ ('BI', "Blender Internal", "Switch to Blender Internal")
+ ),
+ default='CYCLES',
+ )
@classmethod
def poll(cls, context):
return c_is_cycles_addon_enabled()
def execute(self, context):
- if self.switcher:
- AutoNodeSwitch(self.renderer, "ON", self)
- else:
- AutoNodeSwitch(self.renderer, "OFF", self)
+ switch = "ON" if self.switcher else "OFF"
+ AutoNodeSwitch(self.renderer, switch, self)
+
return {'FINISHED'}
diff --git a/materials_utils/texture_rename.py b/materials_utils/texture_rename.py
index c803295e..585a3a7d 100644
--- a/materials_utils/texture_rename.py
+++ b/materials_utils/texture_rename.py
@@ -3,31 +3,41 @@
import bpy
from bpy.types import (
- Operator,
- Panel,
- )
-from bpy.props import StringProperty
+ Operator,
+ Panel,
+)
+from bpy.props import (
+ BoolProperty,
+ StringProperty,
+)
from .warning_messages_utils import (
- warning_messages,
- c_data_has_images,
- )
+ warning_messages,
+ c_data_has_images,
+)
class TEXTURE_OT_patern_rename(Operator):
bl_idname = "texture.patern_rename"
bl_label = "Texture Renamer"
bl_description = ("Replace the Texture names pattern with the attached Image ones\n"
- "Works on all Textures (Including Brushes) \n \n"
- "The First field - the name pattern to replace \n"
- "The Second - searches for existing names \n")
+ "Works on all Textures (Including Brushes)\n"
+ "The First field - the name pattern to replace\n"
+ "The Second - search for existing names")
bl_options = {'REGISTER', 'UNDO'}
def_name = "Texture" # default name
is_not_undo = False # prevent drawing props on undo
+
named = StringProperty(
- name="Search for name",
- default=def_name
- )
+ name="Search for name",
+ description="Enter the name pattern or choose the one from the dropdown list below",
+ default=def_name
+ )
+ replace_all = BoolProperty(
+ name="Replace all",
+ description="Replace all the Textures in the data with the names of the images attached",
+ default=False
+ )
@classmethod
def poll(cls, context):
@@ -35,28 +45,35 @@ class TEXTURE_OT_patern_rename(Operator):
def draw(self, context):
layout = self.layout
- if self.is_not_undo is True:
- box = layout.box()
- box.prop(self, "named", text="Name pattern", icon="SYNTAX_ON")
- layout.separator()
+ if not self.is_not_undo:
+ layout.label(text="*Only Undo is available*", icon="INFO")
+ return
+
+ layout.prop(self, "replace_all")
- box = layout.box()
- box.prop_search(self, "named", bpy.data, "textures")
- else:
- layout.label(text="**Only Undo is available**", icon="INFO")
+ box = layout.box()
+ box.enabled = not self.replace_all
+ box.prop(self, "named", text="Name pattern", icon="SYNTAX_ON")
+
+ box = layout.box()
+ box.enabled = not self.replace_all
+ box.prop_search(self, "named", bpy.data, "textures")
def invoke(self, context, event):
self.is_not_undo = True
return context.window_manager.invoke_props_dialog(self)
+ def check(self, context):
+ return self.is_not_undo
+
def execute(self, context):
errors = [] # collect texture names without images attached
- tex_count = 0 # check if there is textures at all
+ tex_count = len(bpy.data.textures)
for texture in bpy.data.textures:
try:
- if texture and self.named in texture.name and texture.type in {"IMAGE"}:
- tex_count += 1
+ is_allowed = self.named in texture.name if not self.replace_all else True
+ if texture and is_allowed and texture.type in {"IMAGE"}:
textname = ""
img = (bpy.data.textures[texture.name].image if bpy.data.textures[texture.name] else None)
if not img:
@@ -67,7 +84,7 @@ class TEXTURE_OT_patern_rename(Operator):
else:
break
texture.name = textname
- if texture.type != "IMAGE": # rename specific textures as clouds, environnement map,...
+ if texture.type != "IMAGE": # rename specific textures as clouds, environment map...
texture.name = texture.type.lower()
except:
continue
@@ -86,7 +103,6 @@ class TEXTURE_OT_patern_rename(Operator):
class TEXTURE_PT_rename_panel(Panel):
- # Creates a Panel in the scene context of the properties editor
bl_label = "Texture Rename"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
@@ -98,13 +114,13 @@ class TEXTURE_PT_rename_panel(Panel):
def register():
- bpy.utils.register_module(__name__)
- pass
+ bpy.utils.register_class(TEXTURE_OT_patern_rename)
+ bpy.utils.register_class(TEXTURE_PT_rename_panel)
def unregister():
- bpy.utils.unregister_module(__name__)
- pass
+ bpy.utils.unregister_class(TEXTURE_PT_rename_panel)
+ bpy.utils.unregister_class(TEXTURE_OT_patern_rename)
if __name__ == "__main__":
diff --git a/materials_utils/warning_messages_utils.py b/materials_utils/warning_messages_utils.py
index 2e5f3b69..00f4a719 100644
--- a/materials_utils/warning_messages_utils.py
+++ b/materials_utils/warning_messages_utils.py
@@ -21,7 +21,7 @@ def warning_messages(operator=None, warn='DEFAULT', object_name="", is_mat=None,
# a list of strings can be passed and concatenated in obj_name too
# is_mat a switch to change to materials or textures for obj_name('MAT','TEX', 'FILE', None)
# fake - optional string that can be passed
- # MAX_COUNT - max members of an list to be displayed
+ # MAX_COUNT - max members of an list to be displayed in UI report
# override - important messages that should be enabled, no matter the setting
# pass the show_warnings bool to enable/disable them
@@ -72,6 +72,8 @@ def warning_messages(operator=None, warn='DEFAULT', object_name="", is_mat=None,
"not cleaned"),
'C_OB_MIX_NO_MAT': "{}{}".format(obj_name, "No Materials or an Object type that "
"can't have Materials (Clean Material Slots)"),
+ 'C_OB_MIX_SLOT_MAT': "{}{}".format(obj_name, "No Materials or only empty Slots are removed "
+ "(Clean Material Slots)"),
'R_OB_NO_MAT': "{}{}".format(obj_name, "No Materials. Nothing to remove"),
'R_OB_FAIL_MAT': "{}{}".format(obj_name, "Failed to remove materials - (Operator Error)"),
'R_NO_SL_MAT': "No Selection. Material slots are not removed",
@@ -79,6 +81,7 @@ def warning_messages(operator=None, warn='DEFAULT', object_name="", is_mat=None,
'R_ALL_NO_MAT': "Object(s) have no materials to remove",
'R_ACT_MAT': "{}{}".format(obj_name, "Removed active Material"),
'R_ACT_MAT_ALL': "{}{}".format(obj_name, "Removed all Material from the Object"),
+ 'SL_MAT_EDIT_BY_NAME': "{}{}{}".format("Geometry with the Material ", obj_name, "been selected"),
'SL_MAT_BY_NAME': "{}{}{}".format("Objects with the Material ", obj_name, "been selected"),
'OB_CANT_MAT': "{}{}".format(obj_name, "Object type that can't have Materials"),
'REP_MAT_NONE': "Replace Material: No materials replaced",
@@ -123,17 +126,18 @@ def warning_messages(operator=None, warn='DEFAULT', object_name="", is_mat=None,
operator.report({'INFO'}, message[warn])
if obj_size_big is True:
- print("\n** MATERIAL SPECIALS **: \n Full list for the Info message is: \n",
- ", ".join(object_name), "\n")
+ print("\n[Materials Utils Specials]:\nFull list for the Info message is:\n\n",
+ " ".join(names + "," + "\n" * ((i + 1) % 10 == 0) for i, names in enumerate(object_name)),
+ "\n")
- # restore settings if overriden
+ # restore settings if overridden
if override:
addon.preferences.show_warnings = get_warn
def collect_report(collection="", is_start=False, is_final=False):
# collection passes a string for appending to COLLECT_REPORT global
- # is_final swithes to the final report with the operator in __init__
+ # is_final switches to the final report with the operator in __init__
global COLLECT_REPORT
scene = bpy.context.scene.mat_specials
use_report = scene.enable_report
@@ -164,20 +168,14 @@ def c_data_has_materials():
return (len(bpy.data.materials) > 0)
+def c_obj_data_has_materials(obj):
+ # check for material presence in object's data
+ matlen = 0
+ if obj:
+ matlen = len(obj.data.materials)
+ return (matlen > 0)
+
+
def c_data_has_images():
# check for image presence in data
return (len(bpy.data.images) > 0)
-
-
-def register():
- bpy.utils.register_module(__name__)
- pass
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
- pass
-
-
-if __name__ == "__main__":
- register()
diff --git a/measureit/__init__.py b/measureit/__init__.py
index 489e0a9e..b5791bbf 100644
--- a/measureit/__init__.py
+++ b/measureit/__init__.py
@@ -29,7 +29,7 @@ bl_info = {
"name": "MeasureIt",
"author": "Antonio Vazquez (antonioya)",
"location": "View3D > Tools Panel /Properties panel",
- "version": (1, 7, 0),
+ "version": (1, 7, 1),
"blender": (2, 7, 4),
"description": "Tools for measuring objects.",
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/"
@@ -37,8 +37,6 @@ bl_info = {
"category": "3D View"
}
-import sys
-import os
# ----------------------------------------------
# Import modules
diff --git a/measureit/measureit_geometry.py b/measureit/measureit_geometry.py
index 8b0b8371..1617ed5a 100644
--- a/measureit/measureit_geometry.py
+++ b/measureit/measureit_geometry.py
@@ -75,7 +75,7 @@ def draw_segments(context, myobj, op, region, rv3d):
# --------------------
# Loop
# --------------------
- for idx in range(0, op.measureit_num):
+ for idx in range(op.measureit_num):
ms = op.measureit_segments[idx]
if ovr is False:
fsize = ms.glfont_size
@@ -557,7 +557,7 @@ def draw_segments(context, myobj, op, region, rv3d):
p_02a = None
p_02b = None
# draw the arc
- for i in range(0, int(n_step)):
+ for i in range(int(n_step)):
p2 = mat_trans2 * mat_rot1 * mat_trans1 * p1
p1_ = (p1[0] + vi[0], p1[1] + vi[1], p1[2] + vi[2])
# First Point
@@ -606,7 +606,7 @@ def draw_segments(context, myobj, op, region, rv3d):
for face in ms.measureit_faces:
myvertices = []
for v in face.measureit_index:
- myvertices.extend([v.glidx])
+ myvertices.append(v.glidx)
area = get_area_and_paint(myvertices, myobj, obverts, region, rv3d)
tot += area
@@ -668,7 +668,7 @@ def get_area_and_paint(myvertices, myobj, obverts, region, rv3d):
bm = from_edit_mesh(myobj.data)
myv = []
for v in bm.verts:
- myv.extend([v.co])
+ myv.append(v.co)
tris = mesh_utils.ngon_tessellate(myv, myvertices)
for t in tris:
@@ -750,7 +750,7 @@ def get_group_sum(myobj, tag):
scale = bpy.context.scene.unit_settings.scale_length
tot = 0.0
obverts = get_mesh_vertices(myobj)
- for idx in range(0, mp.measureit_num):
+ for idx in range(mp.measureit_num):
ms = mp.measureit_segments[idx]
if (ms.gltype == 1 or ms.gltype == 12 or
ms.gltype == 13 or ms.gltype == 14) and ms.gltot != '99' \
diff --git a/measureit/measureit_main.py b/measureit/measureit_main.py
index 89a48799..c85de95d 100644
--- a/measureit/measureit_main.py
+++ b/measureit/measureit_main.py
@@ -408,7 +408,7 @@ class MeasureitEditPanel(Panel):
row = box.row(True)
row.operator("measureit.expandallsegmentbutton", text="Expand all", icon="ZOOMIN")
row.operator("measureit.collapseallsegmentbutton", text="Collapse all", icon="ZOOMOUT")
- for idx in range(0, mp.measureit_num):
+ for idx in range(mp.measureit_num):
if mp.measureit_segments[idx].glfree is False:
add_item(box, idx, mp.measureit_segments[idx])
@@ -426,7 +426,7 @@ class MeasureitEditPanel(Panel):
myobj = context.object
obverts = get_mesh_vertices(myobj)
viewtot = False
- for idx in range(0, mp.measureit_num):
+ for idx in range(mp.measureit_num):
ms = mp.measureit_segments[idx]
if (ms.gltype == 1 or ms.gltype == 12
or ms.gltype == 13 or ms.gltype == 14) and ms.gltot != '99' \
@@ -472,7 +472,7 @@ class MeasureitEditPanel(Panel):
box = layout.box()
box.label("Totals", icon='SOLO_ON')
final = 0
- for idx in range(0, len(tot)):
+ for idx in range(len(tot)):
if ac[idx] is True:
final += tot[idx]
tx_dist = format_distance(fmt, units, tot[idx])
@@ -1030,7 +1030,7 @@ class AddSegmentOrtoButton(Operator):
mainobject.MeasureGenerator.add()
mp = mainobject.MeasureGenerator[0]
- for x in range(0, len(mylist)):
+ for x in range(len(mylist)):
# -----------------------
# Only if not exist
# -----------------------
@@ -1658,7 +1658,7 @@ class DeleteAllSumButton(Operator):
if context.object is not None:
if 'MeasureGenerator' in context.object:
mp = context.object.MeasureGenerator[0]
- for idx in range(0, mp.measureit_num):
+ for idx in range(mp.measureit_num):
ms = mp.measureit_segments[idx]
ms.gltot = '99'
@@ -2011,19 +2011,22 @@ def draw_main(context):
rv3d = context.space_data.region_quadviews[i]
scene = bpy.context.scene
- # Get visible layers
+ local_view = context.area.spaces.active.local_view is not None
layers = []
- if bpy.context.space_data.lock_camera_and_layers is True:
- for x in range(0, 20):
- if bpy.context.scene.layers[x] is True:
- layers.extend([x])
- else:
- for x in range(20):
- if bpy.context.space_data.layers[x] is True:
- layers.extend([x])
+ if local_view is False:
+ # Get visible layers
+ if bpy.context.space_data.lock_camera_and_layers is True:
+ for x in range(20):
+ if bpy.context.scene.layers[x] is True:
+ layers.append(x)
+ else:
+ # Lock disabled, use view dependent visible layers
+ for x in range(20):
+ if bpy.context.space_data.layers[x] is True:
+ layers.append(x)
# Display selected or all
- if scene.measureit_gl_ghost is False:
+ if scene.measureit_gl_ghost is False or local_view is True:
objlist = context.selected_objects
else:
objlist = context.scene.objects
@@ -2036,13 +2039,18 @@ def draw_main(context):
for myobj in objlist:
if myobj.hide is False:
if 'MeasureGenerator' in myobj:
- # verify visible layer
- for x in range(0, 20):
- if myobj.layers[x] is True:
- if x in layers:
+ if local_view is False:
+ # verify visible layer
+ for x in range(20):
+ if myobj.layers[x] is True and x in layers:
op = myobj.MeasureGenerator[0]
draw_segments(context, myobj, op, region, rv3d)
- break
+ break
+ else:
+ # Layer check not needed here, selected objects are not
+ # added to context.selected_objects if in disabled layers
+ op = myobj.MeasureGenerator[0]
+ draw_segments(context, myobj, op, region, rv3d)
# ---------------------------------------
# Generate all OpenGL calls for debug
# ---------------------------------------
@@ -2120,7 +2128,7 @@ def get_selected_vertex(myobject):
tv = len(bm.verts)
for v in bm.verts:
if v.select:
- mylist.extend([v.index])
+ mylist.append(v.index)
if flag is True:
bpy.ops.object.editmode_toggle()
@@ -2154,7 +2162,7 @@ def get_selected_vertex_history(myobject):
bm = from_edit_mesh(myobject.data)
for v in bm.select_history:
- mylist.extend([v.index])
+ mylist.append(v.index)
if flag is True:
bpy.ops.object.editmode_toggle()
@@ -2185,8 +2193,8 @@ def get_smart_selected(myobject):
bm = from_edit_mesh(myobject.data)
for e in bm.edges:
if e.select is True:
- mylist.extend([e.verts[0].index])
- mylist.extend([e.verts[1].index])
+ mylist.append(e.verts[0].index)
+ mylist.append(e.verts[1].index)
if flag is True:
bpy.ops.object.editmode_toggle()
@@ -2216,12 +2224,12 @@ def get_selected_faces(myobject):
bm = from_edit_mesh(myobject.data)
for e in bm.faces:
- myface = []
+ myfaces = []
if e.select is True:
- for i in range(0, len(e.verts)):
- myface.extend([e.verts[i].index])
+ for i in range(len(e.verts)):
+ myfaces.append(e.verts[i].index)
- mylist.extend([myface])
+ mylist.extend([myfaces])
if flag is True:
bpy.ops.object.editmode_toggle()
diff --git a/measureit/measureit_render.py b/measureit/measureit_render.py
index eb04c92d..d5eb6430 100644
--- a/measureit/measureit_render.py
+++ b/measureit/measureit_render.py
@@ -56,9 +56,9 @@ def render_main(self, context, animation=False):
# Get visible layers
layers = []
scene = context.scene
- for x in range(0, 20):
+ for x in range(20):
if scene.layers[x] is True:
- layers.extend([x])
+ layers.append(x)
# Get object list
objlist = context.scene.objects
@@ -124,8 +124,8 @@ def render_main(self, context, animation=False):
# --------------------------------
# Loop for all tiles
# --------------------------------
- for row in range(0, row_num):
- for col in range(0, col_num):
+ for row in range(row_num):
+ for col in range(col_num):
buffer = bgl.Buffer(bgl.GL_FLOAT, width * height * 4)
bgl.glDisable(bgl.GL_SCISSOR_TEST) # if remove this line, get blender screenshot not image
bgl.glViewport(0, 0, tile_x, tile_y)
@@ -170,7 +170,7 @@ def render_main(self, context, animation=False):
if myobj.hide is False:
if 'MeasureGenerator' in myobj:
# verify visible layer
- for x in range(0, 20):
+ for x in range(20):
if myobj.layers[x] is True:
if x in layers:
op = myobj.MeasureGenerator[0]
@@ -216,7 +216,7 @@ def render_main(self, context, animation=False):
# --------------------------------
bgl.glFinish()
bgl.glReadPixels(0, 0, width, height, bgl.GL_RGBA, bgl.GL_FLOAT, buffer) # read image data
- for y in range(0, tile_y):
+ for y in range(tile_y):
# final image pixels position
p1 = (y * width * 4) + (row * tile_y * width * 4) + (col * tile_x * 4)
p2 = p1 + (tile_x * 4)
diff --git a/mesh_carver.py b/mesh_carver.py
index aa388a15..16308aa4 100644
--- a/mesh_carver.py
+++ b/mesh_carver.py
@@ -21,37 +21,44 @@ bl_info = {
"name": "Carver MT",
"category": "Object",
"author": "Pixivore, Cedric LEPILLER, Ted Milker",
- "version": (1, 1, 7),
- "blender": (2, 77, 0),
+ "version": (1, 1, 8),
+ "blender": (2, 79, 2),
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
"Scripts/Modeling/Carver",
"description": "Multiple tools to carve or to create objects",
- }
+}
import bpy
import bgl
import blf
import math
-import mathutils
import sys
import random
import bmesh
+from mathutils import (
+ Color,
+ Euler,
+ Matrix,
+ Vector,
+ Quaternion,
+)
import bpy_extras
from bpy.props import (
- BoolProperty,
- EnumProperty,
- IntProperty,
- StringProperty,
- )
+ BoolProperty,
+ IntProperty,
+ PointerProperty,
+ StringProperty,
+)
from bpy_extras import view3d_utils
from bpy_extras.view3d_utils import (
- region_2d_to_vector_3d,
- region_2d_to_location_3d,
- )
+ region_2d_to_vector_3d,
+ region_2d_to_location_3d,
+)
+
Profils = [
("CTP_4882",
- mathutils.Vector((2.61824, -5.56469, 0)),
+ Vector((2.61824, -5.56469, 0)),
[(-1.156501, 0.799282, 0.032334),
(-0.967583, 0.838861, 0.032334),
(-1.10386, 0.846403, 0.032334),
@@ -108,7 +115,7 @@ Profils = [
[38, 39, 47], [34, 32, 35], [39, 46, 47], [43, 34, 35], [35, 38, 47], [47, 42, 35], [32, 33, 35],
[42, 40, 43], [36, 37, 39], [42, 43, 35], [44, 45, 47], [46, 44, 47]]),
("CTP_8354",
- mathutils.Vector((-0.06267, -2.43829, -0.0)),
+ Vector((-0.06267, -2.43829, -0.0)),
[(-0.534254, -1.0, 0.032334),
(-1.0, -0.534254, 0.032334),
(-0.654798, -0.98413, 0.032334),
@@ -212,7 +219,7 @@ Profils = [
[48, 71, 80, 47], [67, 37, 38, 83], [82, 83, 87, 86], [81, 82, 86, 85], [80, 81, 85, 84], [47, 80, 84, 46],
[83, 38, 39, 87]]),
("CTP_5585",
- mathutils.Vector((5.0114, -2.4281, 0.0)),
+ Vector((5.0114, -2.4281, 0.0)),
[(-0.490711, -1.0, 0.032334),
(-1.0, -0.490711, 0.032334),
(1.0, -0.490711, 0.032334),
@@ -233,7 +240,7 @@ Profils = [
[[11, 12, 13, 14], [9, 8, 4, 1], [10, 9, 1, 0], [11, 10, 0, 3], [12, 11, 3, 2], [13, 12, 2, 7],
[14, 13, 7, 6], [15, 14, 6, 5], [8, 15, 5, 4], [9, 10, 15, 8], [10, 11, 14, 15]]),
("CTP_6960",
- mathutils.Vector((-0.11417, 2.48371, -0.0)),
+ Vector((-0.11417, 2.48371, -0.0)),
[(0.0, 1.0, 0.016827),
(-0.382683, 0.92388, 0.016827),
(-0.707107, 0.707107, 0.016827),
@@ -271,7 +278,7 @@ Profils = [
[6, 22, 21, 5], [14, 30, 29, 13], [7, 23, 22, 6], [15, 31, 30, 14], [8, 24, 23, 7], [1, 17, 16, 0],
[0, 16, 31, 15], [9, 25, 24, 8], [2, 18, 17, 1], [10, 26, 25, 9]]),
("CTP_5359",
- mathutils.Vector((5.50446, 2.41669, -0.0)),
+ Vector((5.50446, 2.41669, -0.0)),
[(0.0, 0.714247, 0.023261),
(-0.382683, 0.659879, 0.023261),
(-0.707107, 0.505049, 0.023261),
@@ -309,7 +316,7 @@ Profils = [
[6, 22, 21, 5], [14, 30, 29, 13], [7, 23, 22, 6], [15, 31, 30, 14], [8, 24, 23, 7], [1, 17, 16, 0],
[0, 16, 31, 15], [9, 25, 24, 8], [2, 18, 17, 1], [10, 26, 25, 9]]),
("CTP_5424",
- mathutils.Vector((2.61824, 2.34147, 0.0)),
+ Vector((2.61824, 2.34147, 0.0)),
[(1.0, -1.0, 0.032334),
(-1.0, 1.0, 0.032334),
(1.0, 1.0, 0.032334),
@@ -328,7 +335,7 @@ Profils = [
[[3, 0, 2], [10, 9, 2], [2, 1, 4], [2, 4, 13], [5, 3, 2], [6, 5, 2], [2, 13, 12], [2, 12, 11], [7, 6, 2],
[8, 7, 2], [2, 11, 10], [9, 8, 2]]),
("CTP_3774",
- mathutils.Vector((2.61824, -2.52425, 0.0)),
+ Vector((2.61824, -2.52425, 0.0)),
[(1.0, 0.0, 0.020045),
(-1.0, 0.0, 0.020045),
(0.31903, -0.664947, 0.020045),
@@ -367,7 +374,7 @@ Profils = [
[11, 30, 9, 12], [17, 16, 27, 26], [14, 17, 26, 24], [24, 25, 0, 14], [15, 13, 27, 16], [9, 30, 23, 4],
[31, 29, 7, 18], [28, 31, 18, 21], [30, 28, 21, 23]]),
("CTP_4473",
- mathutils.Vector((7.31539, 0.0, 0.0)),
+ Vector((7.31539, 0.0, 0.0)),
[(0.24549, -1.0, 0.022454),
(-0.24549, -1.0, 0.022454),
(-0.24549, 1.0, 0.022454),
@@ -383,7 +390,7 @@ Profils = [
],
[[8, 3, 2, 10], [0, 9, 11, 1], [4, 8, 9, 5], [8, 10, 11, 9], [10, 7, 6, 11]]),
("CTP_4003",
- mathutils.Vector((4.91276, 0.0, 0.0)),
+ Vector((4.91276, 0.0, 0.0)),
[(-1.0, -1.0, 0.026945),
(1.0, -1.0, 0.026945),
(-1.0, 1.0, 0.026945),
@@ -424,7 +431,7 @@ Profils = [
[23, 25, 31, 29], [24, 23, 29, 30], [25, 22, 28, 31], [26, 22, 15, 20], [10, 27, 33, 5], [31, 28, 3, 11],
[33, 30, 13, 14], [29, 31, 11, 12], [5, 33, 14, 1], [30, 29, 12, 13], [32, 28, 22, 26]]),
("CTP_3430",
- mathutils.Vector((2.61824, 0.0, 0.0)),
+ Vector((2.61824, 0.0, 0.0)),
[(-1.0, -1.0, 0.032334),
(1.0, -1.0, 0.032334),
(-1.0, 1.0, 0.032334),
@@ -432,7 +439,7 @@ Profils = [
],
[[0, 1, 3, 2]]),
("CTP_7175",
- mathutils.Vector((0.0, 0.0, 0.0)),
+ Vector((0.0, 0.0, 0.0)),
[(-1.0, -1.0, 0.032334),
(1.0, -1.0, 0.032334),
(-1.0, 1.0, 0.032334),
@@ -464,33 +471,33 @@ class CarverPrefs(bpy.types.AddonPreferences):
bl_idname = __name__
Enable_Tab_01 = BoolProperty(
- name="Info",
- description="Some general information and settings about the add-on",
- default=False
- )
+ name="Info",
+ description="Some general information and settings about the add-on",
+ default=False
+ )
Enable_Tab_02 = BoolProperty(
- name="Hotkeys",
- description="List of the shortcuts used during carving",
- default=False
- )
+ name="Hotkeys",
+ description="List of the shortcuts used during carving",
+ default=False
+ )
bpy.types.Scene.Key_Create = StringProperty(
- name="Object creation",
- description="Object creation",
- maxlen=1,
- default="C"
- )
+ name="Object creation",
+ description="Object creation",
+ maxlen=1,
+ default="C"
+ )
bpy.types.Scene.Key_Update = StringProperty(
- name="Auto Bevel Update",
- description="Auto Bevel Update",
- maxlen=1,
- default="A",
- )
+ name="Auto Bevel Update",
+ description="Auto Bevel Update",
+ maxlen=1,
+ default="A",
+ )
bpy.types.Scene.Key_Bool = StringProperty(
- name="Boolean type",
- description="Boolean operation type",
- maxlen=1,
- default="T",
- )
+ name="Boolean type",
+ description="Boolean operation type",
+ maxlen=1,
+ default="T",
+ )
bpy.types.Scene.Key_Brush = StringProperty(
name="Brush Mode",
description="Brush Mode",
@@ -498,167 +505,150 @@ class CarverPrefs(bpy.types.AddonPreferences):
default="B",
)
bpy.types.Scene.Key_Help = StringProperty(
- name="Help display",
- description="Help display",
- maxlen=1,
- default="H",
- )
+ name="Help display",
+ description="Help display",
+ maxlen=1,
+ default="H",
+ )
bpy.types.Scene.Key_Instant = StringProperty(
- name="Instantiate",
- description="Instantiate object",
- maxlen=1,
- default="I",
- )
+ name="Instantiate",
+ description="Instantiate object",
+ maxlen=1,
+ default="I",
+ )
bpy.types.Scene.Key_Close = StringProperty(
- name="Close polygonal shape",
- description="Close polygonal shape",
- maxlen=1,
- default="X",
- )
+ name="Close polygonal shape",
+ description="Close polygonal shape",
+ maxlen=1,
+ default="X",
+ )
bpy.types.Scene.Key_Apply = StringProperty(
- name="Apply operation",
- description="Apply operation",
- maxlen=1,
- default="Q",
- )
+ name="Apply operation",
+ description="Apply operation",
+ maxlen=1,
+ default="Q",
+ )
bpy.types.Scene.Key_Scale = StringProperty(
- name="Scale object",
- description="Scale object",
- maxlen=1,
- default="S",
- )
+ name="Scale object",
+ description="Scale object",
+ maxlen=1,
+ default="S",
+ )
bpy.types.Scene.Key_Gapy = StringProperty(
- name="Gap rows",
- description="Scale gap between columns",
- maxlen=1,
- default="J",
- )
+ name="Gap rows",
+ description="Scale gap between columns",
+ maxlen=1,
+ default="J",
+ )
bpy.types.Scene.Key_Gapx = StringProperty(
- name="Gap columns",
- description="Scale gap between columns",
- maxlen=1,
- default="U",
- )
+ name="Gap columns",
+ description="Scale gap between columns",
+ maxlen=1,
+ default="U",
+ )
bpy.types.Scene.Key_Depth = StringProperty(
- name="Depth",
- description="Cursor depth or solidify pattern",
- maxlen=1,
- default="D",
- )
+ name="Depth",
+ description="Cursor depth or solidify pattern",
+ maxlen=1,
+ default="D",
+ )
bpy.types.Scene.Key_BrushDepth = StringProperty(
- name="Brush Depth",
- description="Brush depth",
- maxlen=1,
- default="C",
- )
+ name="Brush Depth",
+ description="Brush depth",
+ maxlen=1,
+ default="C",
+ )
bpy.types.Scene.Key_Subadd = StringProperty(
- name="Add subdivision",
- description="Add subdivision",
- maxlen=1,
- default="X",
- )
+ name="Add subdivision",
+ description="Add subdivision",
+ maxlen=1,
+ default="X",
+ )
bpy.types.Scene.Key_Subrem = StringProperty(
- name="Remove subdivision",
- description="Remove subdivision",
- maxlen=1,
- default="W",
- )
+ name="Remove subdivision",
+ description="Remove subdivision",
+ maxlen=1,
+ default="W",
+ )
bpy.types.Scene.Key_Randrot = StringProperty(
- name="Random rotation",
- description="Random rotation",
- maxlen=1,
- default="R",
- )
- bpy.types.Scene.Key_Solver = StringProperty(
- name="Solver",
- description="Switch between Carve and BMesh Boolean solver\n"
- "depending on a specific use case",
- maxlen=1,
- default="V",
- )
+ name="Random rotation",
+ description="Random rotation",
+ maxlen=1,
+ default="R",
+ )
bpy.types.Scene.ProfilePrefix = StringProperty(
- name="Profile prefix",
- description="Prefix to look for profiles with",
- default="Carver_Profile-"
- )
- bpy.types.Scene.CarverSolver = EnumProperty(
- name="Boolean Solver",
- description="Boolean solver to use by default\n",
- default="CARVE",
- items=(
- ('CARVE', 'Carve', "Carve solver, as the legacy one, can handle\n"
- "basic coplanar but can often fail with\n"
- "non-closed geometry"),
- ('BMESH', 'BMesh', "BMesh solver is faster, but cannot handle\n"
- "coplanar and self-intersecting geometry")
- )
- )
+ name="Profile prefix",
+ description="Prefix to look for profiles with",
+ default="Carver_Profile-"
+ )
def draw(self, context):
scene = context.scene
layout = self.layout
- layout.prop(self, "Enable_Tab_01", text="Info and Settings", icon="QUESTION")
+ icon_1 = "TRIA_RIGHT" if not self.Enable_Tab_01 else "TRIA_DOWN"
+ box = layout.box()
+ box.prop(self, "Enable_Tab_01", text="Info and Settings", emboss=False, icon=icon_1)
if self.Enable_Tab_01:
- layout.label(text="Carver Operator:", icon="LAYER_ACTIVE")
- layout.label(text="Select a Mesh Object and press [CTRL]+[SHIFT]+[X] to carve",
+ box.label(text="Carver Operator:", icon="LAYER_ACTIVE")
+ box.label(text="Select a Mesh Object and press [CTRL]+[SHIFT]+[X] to carve",
icon="LAYER_USED")
- layout.label(text="To finish carving press [ESC] or [RIGHT CLICK]",
+ box.label(text="To finish carving press [ESC] or [RIGHT CLICK]",
icon="LAYER_USED")
+ box.prop(scene, "ProfilePrefix", text="Profile prefix")
- layout.prop(scene, "ProfilePrefix", text="Profile prefix")
- layout.prop(scene, "CarverSolver", text="Solver")
-
- layout.prop(self, "Enable_Tab_02", text="Keys", icon="KEYINGSET")
+ icon_2 = "TRIA_RIGHT" if not self.Enable_Tab_02 else "TRIA_DOWN"
+ box = layout.box()
+ box.prop(self, "Enable_Tab_02", text="Keys", emboss=False, icon=icon_2)
if self.Enable_Tab_02:
- split = layout.split()
- col = split.column()
+ split = box.split(align=True)
+ box = split.box()
+ col = box.column(align=True)
col.label("Object Creation:")
col.prop(scene, "Key_Create", text="")
col.label("Auto bevel update:")
col.prop(scene, "Key_Update", text="")
col.label("Boolean operation type:")
col.prop(scene, "Key_Bool", text="")
- col.label("Solver:")
- col.prop(scene, "Key_Solver", text="")
+ col.label("Brush Depth:")
+ col.prop(scene, "Key_BrushDepth", text="")
- col = split.column()
+ box = split.box()
+ col = box.column(align=True)
col.label("Brush Mode:")
col.prop(scene, "Key_Brush", text="")
col.label("Help display:")
col.prop(scene, "Key_Help", text="")
col.label("Instantiate object:")
col.prop(scene, "Key_Instant", text="")
- col.label("Brush Depth:")
- col.prop(scene, "Key_BrushDepth", text="")
+ col.label("Random rotation:")
+ col.prop(scene, "Key_Randrot", text="")
- col = split.column()
+ box = split.box()
+ col = box.column(align=True)
col.label("Close polygonal shape:")
col.prop(scene, "Key_Close", text="")
col.label("Apply operation:")
col.prop(scene, "Key_Apply", text="")
col.label("Scale object:")
col.prop(scene, "Key_Scale", text="")
+ col.label("Subdiv add:")
+ col.prop(scene, "Key_Subadd", text="")
- col = split.column()
+ box = split.box()
+ col = box.column(align=True)
col.label("Gap rows:")
col.prop(scene, "Key_Gapy", text="")
col.label("Gap columns:")
col.prop(scene, "Key_Gapx", text="")
col.label("Depth / Solidify:")
col.prop(scene, "Key_Depth", text="")
-
- col = split.column()
- col.label("Subdiv add:")
- col.prop(scene, "Key_Subadd", text="")
col.label("Subdiv Remove:")
col.prop(scene, "Key_Subrem", text="")
- col.label("Random rotation:")
- col.prop(scene, "Key_Randrot", text="")
# Draw Text (Center position)
-def DrawCenterText(text, xt, yt, Size, Color, self):
+def DrawCenterText(text, xt, yt, Size, colors, self):
font_id = 0
# Offset Shadow
Sshadow_x = 2
@@ -670,16 +660,16 @@ def DrawCenterText(text, xt, yt, Size, Color, self):
blf.draw(font_id, text)
blf.position(font_id, xt - blf.dimensions(font_id, text)[0] / 2, yt, 0)
- if Color is not None:
- mColor = mathutils.Color((Color[0], Color[1], Color[2]))
- bgl.glColor4f(mColor.r, mColor.g, mColor.b, 1.0)
+ if colors is not None:
+ mcolor = Color((colors[0], colors[1], colors[2]))
+ bgl.glColor4f(mcolor.r, mcolor.g, mcolor.b, 1.0)
else:
bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
blf.draw(font_id, text)
# Draw text (Left position)
-def DrawLeftText(text, xt, yt, Size, Color, self):
+def DrawLeftText(text, xt, yt, Size, colors, self):
font_id = 0
# Offset Shadow
Sshadow_x = 2
@@ -690,16 +680,16 @@ def DrawLeftText(text, xt, yt, Size, Color, self):
bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
blf.draw(font_id, text)
blf.position(font_id, xt, yt, 0)
- if Color is not None:
- mColor = mathutils.Color((Color[0], Color[1], Color[2]))
- bgl.glColor4f(mColor.r, mColor.g, mColor.b, 1.0)
+ if colors is not None:
+ mcolor = Color((colors[0], colors[1], colors[2]))
+ bgl.glColor4f(mcolor.r, mcolor.g, mcolor.b, 1.0)
else:
bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
blf.draw(font_id, text)
# Draw text (Right position)
-def DrawRightText(text, xt, yt, Size, Color, self):
+def DrawRightText(text, xt, yt, Size, colors, self):
font_id = 0
# Offset Shadow
Sshadow_x = 2
@@ -710,9 +700,9 @@ def DrawRightText(text, xt, yt, Size, Color, self):
bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
blf.draw(font_id, text)
blf.position(font_id, xt - blf.dimensions(font_id, text)[0], yt, 0)
- if Color is not None:
- mColor = mathutils.Color((Color[0], Color[1], Color[2]))
- bgl.glColor4f(mColor.r, mColor.g, mColor.b, 1.0)
+ if colors is not None:
+ mcolor = Color((colors[0], colors[1], colors[2]))
+ bgl.glColor4f(mcolor.r, mcolor.g, mcolor.b, 1.0)
else:
bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
blf.draw(font_id, text)
@@ -743,28 +733,18 @@ def draw_callback_px(self, context):
BooleanMode = "Create"
else:
if self.ObjectMode or self.ProfileMode:
- if self.BoolOps == DIFFERENCE:
- BooleanType = "Difference) [T]"
- else:
- BooleanType = "Union) [T]"
-
- if self.ObjectMode:
- BooleanMode = "Object Brush (" + BooleanType
- else:
- BooleanMode = "Profil Brush (" + BooleanType
+ BooleanType = "Difference) [T]" if self.BoolOps == DIFFERENCE else "Union) [T]"
+ BooleanMode = \
+ "Object Brush (" + BooleanType if self.ObjectMode else "Profil Brush (" + BooleanType
else:
- if (self.shift is False) and (self.ForceRebool is False):
- BooleanMode = "Difference"
- else:
- BooleanMode = "Rebool"
+ BooleanMode = \
+ "Difference" if (self.shift is False) and (self.ForceRebool is False) else "Rebool"
UIColor = (0.992, 0.5518, 0.0, 1.0)
# Display boolean mode
- if region.width >= 850:
- DrawCenterText(BooleanMode, xt, yt, 40, UIColor, self)
- else:
- DrawCenterText(BooleanMode, xt, yt, 20, UIColor, self)
+ text_size = 40 if region.width >= 850 else 20
+ DrawCenterText(BooleanMode, xt, yt, text_size, UIColor, self)
# Separator (Line)
LineWidth = 75
@@ -790,12 +770,11 @@ def draw_callback_px(self, context):
# Variables according to screen size
IFontSize = 12
yInterval = 20
- xCmd = 0
yCmd = yt - 30
+
if region.width >= 850:
IFontSize = 18
yInterval = 25
- xCmd = 100
# Color
Color0 = None
@@ -816,11 +795,9 @@ def draw_callback_px(self, context):
# Depth Cursor
TypeStr = "Cursor Depth [" + context.scene.Key_Depth + "] : "
- if self.snapCursor:
- BoolStr = "(ON)"
- else:
- BoolStr = "(OFF)"
+ BoolStr = "(ON)" if self.snapCursor else "(OFF)"
OpsStr = TypeStr + BoolStr
+
TotalWidth = blf.dimensions(font_id, OpsStr)[0]
xLeft = region.width / 2 - TotalWidth / 2
xLeftP = xLeft + blf.dimensions(font_id, TypeStr)[0]
@@ -830,11 +807,9 @@ def draw_callback_px(self, context):
if self.CreateMode is False:
# Apply Booleans
TypeStr = "Apply Operations [" + context.scene.Key_Apply + "] : "
- if self.DontApply:
- BoolStr = "(OFF)"
- else:
- BoolStr = "(ON)"
+ BoolStr = "(OFF)" if self.DontApply else "(ON)"
OpsStr = TypeStr + BoolStr
+
TotalWidth = blf.dimensions(font_id, OpsStr)[0]
xLeft = region.width / 2 - TotalWidth / 2
xLeftP = xLeft + blf.dimensions(font_id, TypeStr)[0]
@@ -843,11 +818,9 @@ def draw_callback_px(self, context):
# Auto update for bevel
TypeStr = "Bevel Update [" + context.scene.Key_Update + "] : "
- if self.Auto_BevelUpdate:
- BoolStr = "(ON)"
- else:
- BoolStr = "(OFF)"
+ BoolStr = "(ON)" if self.Auto_BevelUpdate else "(OFF)"
OpsStr = TypeStr + BoolStr
+
TotalWidth = blf.dimensions(font_id, OpsStr)[0]
xLeft = region.width / 2 - TotalWidth / 2
xLeftP = xLeft + blf.dimensions(font_id, TypeStr)[0]
@@ -856,10 +829,7 @@ def draw_callback_px(self, context):
# Subdivisions
if self.CutMode == CIRCLE:
- if self.CreateMode is False:
- y = yCmd - yInterval * 4
- else:
- y = yCmd - yInterval * 2
+ y = yCmd - yInterval * 4 if self.CreateMode is False else yCmd - yInterval * 2
TypeStr = "Subdivisions [" + context.scene.Key_Subrem + "][" + context.scene.Key_Subadd + "] : "
BoolStr = str((int(360 / self.stepAngle[self.step])))
OpsStr = TypeStr + BoolStr
@@ -872,11 +842,9 @@ def draw_callback_px(self, context):
else:
# INSTANTIATE:
TypeStr = "Instantiate [" + context.scene.Key_Instant + "] : "
- if self.Instantiate:
- BoolStr = "(ON)"
- else:
- BoolStr = "(OFF)"
+ BoolStr = "(ON)" if self.Instantiate else "(OFF)"
OpsStr = TypeStr + BoolStr
+
blf.size(font_id, IFontSize, 72)
TotalWidth = blf.dimensions(font_id, OpsStr)[0]
xLeft = region.width / 2 - TotalWidth / 2
@@ -887,11 +855,9 @@ def draw_callback_px(self, context):
# RANDOM ROTATION:
if self.alt:
TypeStr = "Random Rotation [" + context.scene.Key_Randrot + "] : "
- if self.RandomRotation:
- BoolStr = "(ON)"
- else:
- BoolStr = "(OFF)"
+ BoolStr = "(ON)" if self.RandomRotation else "(OFF)"
OpsStr = TypeStr + BoolStr
+
blf.size(font_id, IFontSize, 72)
TotalWidth = blf.dimensions(font_id, OpsStr)[0]
xLeft = region.width / 2 - TotalWidth / 2
@@ -911,42 +877,38 @@ def draw_callback_px(self, context):
TotalWidth = blf.dimensions(font_id, OpsStr)[0]
xLeft = region.width / 2 - TotalWidth / 2
xLeftP = xLeft + blf.dimensions(font_id, TypeStr)[0]
- if self.alt:
- DrawLeftText(TypeStr, xLeft, yCmd - yInterval * 2, IFontSize, Color0, self)
- DrawLeftText(BoolStr, xLeftP, yCmd - yInterval * 2, IFontSize, Color1, self)
- else:
- DrawLeftText(TypeStr, xLeft, yCmd - yInterval, IFontSize, Color0, self)
- DrawLeftText(BoolStr, xLeftP, yCmd - yInterval, IFontSize, Color1, self)
+
+ self_alt_y = 2 if self.alt else 1
+ DrawLeftText(TypeStr, xLeft, yCmd - yInterval * self_alt_y, IFontSize, Color0, self)
+ DrawLeftText(BoolStr, xLeftP, yCmd - yInterval * self_alt_y, IFontSize, Color1, self)
# BRUSH DEPTH:
if (self.ObjectMode):
TypeStr = "Carve Depth [" + context.scene.Key_Depth + "] : "
BoolStr = str(round(self.ObjectBrush.data.vertices[0].co.z, 2))
OpsStr = TypeStr + BoolStr
+
blf.size(font_id, IFontSize, 72)
TotalWidth = blf.dimensions(font_id, OpsStr)[0]
xLeft = region.width / 2 - TotalWidth / 2
xLeftP = xLeft + blf.dimensions(font_id, TypeStr)[0]
- if self.alt:
- DrawLeftText(TypeStr, xLeft, yCmd - yInterval * 2, IFontSize, Color0, self)
- DrawLeftText(BoolStr, xLeftP, yCmd - yInterval * 2, IFontSize, Color1, self)
- else:
- DrawLeftText(TypeStr, xLeft, yCmd - yInterval, IFontSize, Color0, self)
- DrawLeftText(BoolStr, xLeftP, yCmd - yInterval, IFontSize, Color1, self)
+
+ self_alt_y = 2 if self.alt else 1
+ DrawLeftText(TypeStr, xLeft, yCmd - yInterval * self_alt_y, IFontSize, Color0, self)
+ DrawLeftText(BoolStr, xLeftP, yCmd - yInterval * self_alt_y, IFontSize, Color1, self)
TypeStr = "Brush Depth [" + context.scene.Key_BrushDepth + "] : "
BoolStr = str(round(self.BrushDepthOffset, 2))
OpsStr = TypeStr + BoolStr
+
blf.size(font_id, IFontSize, 72)
TotalWidth = blf.dimensions(font_id, OpsStr)[0]
xLeft = region.width / 2 - TotalWidth / 2
xLeftP = xLeft + blf.dimensions(font_id, TypeStr)[0]
- if self.alt:
- DrawLeftText(TypeStr, xLeft, yCmd - yInterval * 3, IFontSize, Color0, self)
- DrawLeftText(BoolStr, xLeftP, yCmd - yInterval * 3, IFontSize, Color1, self)
- else:
- DrawLeftText(TypeStr, xLeft, yCmd - yInterval * 2, IFontSize, Color0, self)
- DrawLeftText(BoolStr, xLeftP, yCmd - yInterval * 2, IFontSize, Color1, self)
+
+ self_alt_y = 3 if self.alt else 2
+ DrawLeftText(TypeStr, xLeft, yCmd - yInterval * self_alt_y, IFontSize, Color0, self)
+ DrawLeftText(BoolStr, xLeftP, yCmd - yInterval * self_alt_y, IFontSize, Color1, self)
bgl.glEnable(bgl.GL_BLEND)
if region.width >= 850:
@@ -969,21 +931,23 @@ def draw_callback_px(self, context):
if self.ObjectMode or self.ProfileMode:
if self.ProfileMode:
DrawLeftText("[" + context.scene.Key_Brush + "]", xHelp, yHelp +
- Help_Interval * 2, Help_FontSize, UIColor, self)
+ Help_Interval * 2, Help_FontSize, UIColor, self)
DrawLeftText(": Object Mode", 150 + t_panel_width, yHelp +
- Help_Interval * 2, Help_FontSize, None, self)
+ Help_Interval * 2, Help_FontSize, None, self)
else:
DrawLeftText("[" + context.scene.Key_Brush + "]", xHelp, yHelp +
- Help_Interval * 2, Help_FontSize, UIColor, self)
- DrawLeftText(": Return", 150 + t_panel_width, yHelp + Help_Interval * 2, Help_FontSize, None, self)
+ Help_Interval * 2, Help_FontSize, UIColor, self)
+ DrawLeftText(": Return", 150 + t_panel_width, yHelp +
+ Help_Interval * 2, Help_FontSize, None, self)
else:
DrawLeftText("[" + context.scene.Key_Brush + "]", xHelp, yHelp +
- Help_Interval * 2, Help_FontSize, UIColor, self)
+ Help_Interval * 2, Help_FontSize, UIColor, self)
DrawLeftText(": Profil Brush", 150 + t_panel_width, yHelp +
- Help_Interval * 2, Help_FontSize, None, self)
- DrawLeftText("[Ctrl + LMB]", xHelp, yHelp - Help_Interval * 6, Help_FontSize, UIColor, self)
+ Help_Interval * 2, Help_FontSize, None, self)
+ DrawLeftText("[Ctrl + LMB]", xHelp, yHelp - Help_Interval * 6,
+ Help_FontSize, UIColor, self)
DrawLeftText(": Move Cursor", 150 + t_panel_width, yHelp -
- Help_Interval * 6, Help_FontSize, None, self)
+ Help_Interval * 6, Help_FontSize, None, self)
if (self.ObjectMode is False) and (self.ProfileMode is False):
if self.CreateMode is False:
@@ -994,235 +958,225 @@ def draw_callback_px(self, context):
else:
DrawLeftText("[" + context.scene.Key_Create + "]", xHelp,
yHelp + Help_Interval, Help_FontSize, UIColor, self)
- DrawLeftText(": Cut", 150 + t_panel_width, yHelp + Help_Interval, Help_FontSize, None, self)
+ DrawLeftText(": Cut", 150 + t_panel_width, yHelp + Help_Interval,
+ Help_FontSize, None, self)
if self.CutMode == RECTANGLE:
DrawLeftText("MouseMove", xHelp, yHelp, Help_FontSize, UIColor, self)
DrawLeftText("[Alt]", xHelp, yHelp - Help_Interval, Help_FontSize, UIColor, self)
- DrawLeftText("[" + context.scene.Key_Solver + "]",
- xHelp, yHelp - Help_Interval * 2, Help_FontSize, UIColor, self)
DrawLeftText(": Dimension", 150 + t_panel_width, yHelp, Help_FontSize, None, self)
- DrawLeftText(": Move all", 150 + t_panel_width, yHelp - Help_Interval, Help_FontSize, None, self)
- DrawLeftText(": Solver [" + context.scene.CarverSolver + "]", 150 + t_panel_width,
- yHelp - Help_Interval * 2, Help_FontSize, None, self)
+ DrawLeftText(": Move all", 150 + t_panel_width, yHelp - Help_Interval,
+ Help_FontSize, None, self)
if self.CutMode == CIRCLE:
DrawLeftText("MouseMove", xHelp, yHelp, Help_FontSize, UIColor, self)
DrawLeftText("[Alt]", xHelp, yHelp - Help_Interval, Help_FontSize, UIColor, self)
DrawLeftText("[" + context.scene.Key_Subrem + "] [" + context.scene.Key_Subadd + "]",
- xHelp, yHelp - Help_Interval * 2, Help_FontSize, UIColor, self)
+ xHelp, yHelp - Help_Interval * 2, Help_FontSize, UIColor, self)
DrawLeftText("[Ctrl]", xHelp, yHelp - Help_Interval * 3, Help_FontSize, UIColor, self)
- DrawLeftText("[" + context.scene.Key_Solver + "]",
- xHelp, yHelp - Help_Interval * 4, Help_FontSize, UIColor, self)
DrawLeftText(": Rotation and Radius", 150 + t_panel_width, yHelp, Help_FontSize, None, self)
- DrawLeftText(": Move all", 150 + t_panel_width, yHelp - Help_Interval, Help_FontSize, None, self)
+ DrawLeftText(": Move all", 150 + t_panel_width, yHelp - Help_Interval,
+ Help_FontSize, None, self)
DrawLeftText(": Subdivision", 150 + t_panel_width, yHelp -
- Help_Interval * 2, Help_FontSize, None, self)
+ Help_Interval * 2, Help_FontSize, None, self)
DrawLeftText(": Incremental rotation", 150 + t_panel_width,
- yHelp - Help_Interval * 3, Help_FontSize, None, self)
- DrawLeftText(": Solver [" + context.scene.CarverSolver + "]",
- 150 + t_panel_width, yHelp - Help_Interval * 4, Help_FontSize, None, self)
+ yHelp - Help_Interval * 3, Help_FontSize, None, self)
if self.CutMode == LINE:
DrawLeftText("MouseMove", xHelp, yHelp, Help_FontSize, UIColor, self)
DrawLeftText("[Alt]", xHelp, yHelp - Help_Interval, Help_FontSize, UIColor, self)
DrawLeftText("[Space]", xHelp, yHelp - Help_Interval * 2, Help_FontSize, UIColor, self)
DrawLeftText("[Ctrl]", xHelp, yHelp - Help_Interval * 3, Help_FontSize, UIColor, self)
- DrawLeftText("[" + context.scene.Key_Solver + "]",
- xHelp, yHelp - Help_Interval * 4, Help_FontSize, UIColor, self)
DrawLeftText(": Dimension", 150 + t_panel_width, yHelp, Help_FontSize, None, self)
- DrawLeftText(": Move all", 150 + t_panel_width, yHelp - Help_Interval, Help_FontSize, None, self)
+ DrawLeftText(": Move all", 150 + t_panel_width, yHelp - Help_Interval,
+ Help_FontSize, None, self)
DrawLeftText(": Validate", 150 + t_panel_width, yHelp -
Help_Interval * 2, Help_FontSize, None, self)
DrawLeftText(": Incremental", 150 + t_panel_width, yHelp -
- Help_Interval * 3, Help_FontSize, None, self)
- DrawLeftText(": Solver [" + context.scene.CarverSolver + "]",
- 150 + t_panel_width, yHelp - Help_Interval * 4, Help_FontSize, None, self)
+ Help_Interval * 3, Help_FontSize, None, self)
if self.CreateMode:
DrawLeftText("[" + context.scene.Key_Subadd + "]", xHelp, yHelp -
- Help_Interval * 4, Help_FontSize, UIColor, self)
+ Help_Interval * 4, Help_FontSize, UIColor, self)
DrawLeftText(": Close geometry", 150 + t_panel_width, yHelp -
- Help_Interval * 4, Help_FontSize, None, self)
+ Help_Interval * 4, Help_FontSize, None, self)
else:
DrawLeftText("[Space]", xHelp, yHelp + Help_Interval, Help_FontSize, UIColor, self)
- DrawLeftText(": Difference", 150 + t_panel_width, yHelp + Help_Interval, Help_FontSize, None, self)
+ DrawLeftText(": Difference", 150 + t_panel_width, yHelp + Help_Interval,
+ Help_FontSize, None, self)
DrawLeftText("[Shift][Space]", xHelp, yHelp, Help_FontSize, UIColor, self)
DrawLeftText(": Rebool", 150 + t_panel_width, yHelp, Help_FontSize, None, self)
DrawLeftText("[Alt][Space]", xHelp, yHelp - Help_Interval, Help_FontSize, UIColor, self)
- DrawLeftText(": Duplicate", 150 + t_panel_width, yHelp - Help_Interval, Help_FontSize, None, self)
+ DrawLeftText(": Duplicate", 150 + t_panel_width, yHelp - Help_Interval,
+ Help_FontSize, None, self)
DrawLeftText("[" + context.scene.Key_Scale + "]", xHelp, yHelp -
- Help_Interval * 2, Help_FontSize, UIColor, self)
- DrawLeftText(": Scale", 150 + t_panel_width, yHelp - Help_Interval * 2, Help_FontSize, None, self)
+ Help_Interval * 2, Help_FontSize, UIColor, self)
+ DrawLeftText(": Scale", 150 + t_panel_width, yHelp - Help_Interval * 2,
+ Help_FontSize, None, self)
DrawLeftText("[LMB][Move]", xHelp, yHelp - Help_Interval * 3, Help_FontSize, UIColor, self)
- DrawLeftText(": Rotation", 150 + t_panel_width, yHelp - Help_Interval * 3, Help_FontSize, None, self)
- DrawLeftText("[Ctrl][LMB][Move]", xHelp, yHelp - Help_Interval * 4, Help_FontSize, UIColor, self)
- DrawLeftText(": Step Angle", 150 + t_panel_width, yHelp - Help_Interval * 4, Help_FontSize, None, self)
+ DrawLeftText(": Rotation", 150 + t_panel_width, yHelp - Help_Interval * 3,
+ Help_FontSize, None, self)
+ DrawLeftText("[Ctrl][LMB][Move]", xHelp, yHelp - Help_Interval * 4,
+ Help_FontSize, UIColor, self)
+ DrawLeftText(": Step Angle", 150 + t_panel_width, yHelp - Help_Interval * 4,
+ Help_FontSize, None, self)
if self.ProfileMode:
DrawLeftText("[" + context.scene.Key_Subadd + "][" + context.scene.Key_Subrem + "]",
- xHelp, yHelp - Help_Interval * 5, Help_FontSize, UIColor, self)
+ xHelp, yHelp - Help_Interval * 5, Help_FontSize, UIColor, self)
DrawLeftText(": Previous or Next Profile", 150 + t_panel_width,
yHelp - Help_Interval * 5, Help_FontSize, None, self)
DrawLeftText("[ARROWS]", xHelp, yHelp - Help_Interval * 6, Help_FontSize, UIColor, self)
DrawLeftText(": Create / Delete rows or columns", 150 + t_panel_width,
- yHelp - Help_Interval * 6, Help_FontSize, None, self)
+ yHelp - Help_Interval * 6, Help_FontSize, None, self)
DrawLeftText("[" + context.scene.Key_Gapy + "][" + context.scene.Key_Gapx + "]",
xHelp, yHelp - Help_Interval * 7, Help_FontSize, UIColor, self)
DrawLeftText(": Gap between rows or columns", 150 + t_panel_width,
- yHelp - Help_Interval * 7, Help_FontSize, None, self)
- DrawLeftText("[" + context.scene.Key_Solver + "]",
- xHelp, yHelp - Help_Interval * 8, Help_FontSize, UIColor, self)
- DrawLeftText(": Solver [" + context.scene.CarverSolver + "]",
- 150 + t_panel_width, yHelp - Help_Interval * 8, Help_FontSize, None, self)
+ yHelp - Help_Interval * 7, Help_FontSize, None, self)
- # Opengl Initialise
+ # Opengl Initialize
bgl.glEnable(bgl.GL_BLEND)
bgl.glColor4f(0.512, 0.919, 0.04, 1.0)
bgl.glLineWidth(2)
- # if context.space_data.region_3d.is_perspective is False:
- if 1:
- bgl.glEnable(bgl.GL_POINT_SMOOTH)
-
- bgl.glPointSize(6)
+ bgl.glEnable(bgl.GL_POINT_SMOOTH)
+ bgl.glPointSize(6)
- if self.ProfileMode:
- xrect = region.width - t_panel_width - 80
- yrect = 80
- bgl.glColor4f(0.0, 0.0, 0.0, 0.3)
- bgl.glRecti(xrect, yrect, xrect + 60, yrect - 60)
-
- faces = self.Profils[self.nProfil][3]
- vertices = self.Profils[self.nProfil][2]
- WidthProfil = 50
- location = mathutils.Vector((region.width - t_panel_width - WidthProfil, 50, 0))
- ProfilScale = 20.0
- bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 0.7)
- for f in faces:
- if len(f) == 4:
- bgl.glBegin(bgl.GL_QUADS)
- bgl.glVertex3f(vertices[f[0]][0] * ProfilScale + location.x, vertices[f[0]][1] *
- ProfilScale + location.y, vertices[f[0]][2] * ProfilScale + location.z)
- bgl.glVertex3f(vertices[f[1]][0] * ProfilScale + location.x, vertices[f[1]][1] *
- ProfilScale + location.y, vertices[f[1]][2] * ProfilScale + location.z)
- bgl.glVertex3f(vertices[f[2]][0] * ProfilScale + location.x, vertices[f[2]][1] *
- ProfilScale + location.y, vertices[f[2]][2] * ProfilScale + location.z)
- bgl.glVertex3f(vertices[f[3]][0] * ProfilScale + location.x, vertices[f[3]][1] *
- ProfilScale + location.y, vertices[f[3]][2] * ProfilScale + location.z)
- bgl.glEnd()
- if len(f) == 3:
- bgl.glBegin(bgl.GL_TRIANGLES)
- bgl.glVertex3f(vertices[f[0]][0] * ProfilScale + location.x, vertices[f[0]][1] *
- ProfilScale + location.y, vertices[f[0]][2] * ProfilScale + location.z)
- bgl.glVertex3f(vertices[f[1]][0] * ProfilScale + location.x, vertices[f[1]][1] *
- ProfilScale + location.y, vertices[f[1]][2] * ProfilScale + location.z)
- bgl.glVertex3f(vertices[f[2]][0] * ProfilScale + location.x, vertices[f[2]][1] *
- ProfilScale + location.y, vertices[f[2]][2] * ProfilScale + location.z)
- bgl.glEnd()
-
- if self.bDone:
- if len(self.mouse_path) > 1:
- x0 = self.mouse_path[0][0]
- y0 = self.mouse_path[0][1]
- x1 = self.mouse_path[1][0]
- y1 = self.mouse_path[1][1]
-
- # Cut Line
- if self.CutMode == LINE:
- if (self.shift) or (self.CreateMode and self.Closed):
- bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 0.5)
-
- bgl.glBegin(bgl.GL_POLYGON)
- for x, y in self.mouse_path:
- bgl.glVertex2i(x + self.xpos, y + self.ypos)
- bgl.glEnd()
-
- bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 1.0)
- bgl.glBegin(bgl.GL_LINE_STRIP)
- for x, y in self.mouse_path:
- bgl.glVertex2i(x + self.xpos, y + self.ypos)
+ if self.ProfileMode:
+ xrect = region.width - t_panel_width - 80
+ yrect = 80
+ bgl.glColor4f(0.0, 0.0, 0.0, 0.3)
+ bgl.glRecti(xrect, yrect, xrect + 60, yrect - 60)
+
+ faces = self.Profils[self.nProfil][3]
+ vertices = self.Profils[self.nProfil][2]
+ WidthProfil = 50
+ location = Vector((region.width - t_panel_width - WidthProfil, 50, 0))
+ ProfilScale = 20.0
+ bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 0.7)
+ for f in faces:
+ if len(f) == 4:
+ bgl.glBegin(bgl.GL_QUADS)
+ bgl.glVertex3f(vertices[f[0]][0] * ProfilScale + location.x, vertices[f[0]][1] *
+ ProfilScale + location.y, vertices[f[0]][2] * ProfilScale + location.z)
+ bgl.glVertex3f(vertices[f[1]][0] * ProfilScale + location.x, vertices[f[1]][1] *
+ ProfilScale + location.y, vertices[f[1]][2] * ProfilScale + location.z)
+ bgl.glVertex3f(vertices[f[2]][0] * ProfilScale + location.x, vertices[f[2]][1] *
+ ProfilScale + location.y, vertices[f[2]][2] * ProfilScale + location.z)
+ bgl.glVertex3f(vertices[f[3]][0] * ProfilScale + location.x, vertices[f[3]][1] *
+ ProfilScale + location.y, vertices[f[3]][2] * ProfilScale + location.z)
bgl.glEnd()
- if (self.CreateMode is False) or (self.CreateMode and self.Closed):
- bgl.glBegin(bgl.GL_LINE_STRIP)
- bgl.glVertex2i(self.mouse_path[len(self.mouse_path) - 1][0] + self.xpos,
- self.mouse_path[len(self.mouse_path) - 1][1] + self.ypos)
- bgl.glVertex2i(self.mouse_path[0][0] + self.xpos, self.mouse_path[0][1] + self.ypos)
- bgl.glEnd()
-
- bgl.glPointSize(6)
- bgl.glBegin(bgl.GL_POINTS)
- for x, y in self.mouse_path:
- bgl.glVertex2i(x + self.xpos, y + self.ypos)
+ if len(f) == 3:
+ bgl.glBegin(bgl.GL_TRIANGLES)
+ bgl.glVertex3f(vertices[f[0]][0] * ProfilScale + location.x, vertices[f[0]][1] *
+ ProfilScale + location.y, vertices[f[0]][2] * ProfilScale + location.z)
+ bgl.glVertex3f(vertices[f[1]][0] * ProfilScale + location.x, vertices[f[1]][1] *
+ ProfilScale + location.y, vertices[f[1]][2] * ProfilScale + location.z)
+ bgl.glVertex3f(vertices[f[2]][0] * ProfilScale + location.x, vertices[f[2]][1] *
+ ProfilScale + location.y, vertices[f[2]][2] * ProfilScale + location.z)
bgl.glEnd()
- # Cut rectangle
- if self.CutMode == RECTANGLE:
+ if self.bDone:
+ if len(self.mouse_path) > 1:
+ x0 = self.mouse_path[0][0]
+ y0 = self.mouse_path[0][1]
+ x1 = self.mouse_path[1][0]
+ y1 = self.mouse_path[1][1]
+
+ # Cut Line
+ if self.CutMode == LINE:
+ if (self.shift) or (self.CreateMode and self.Closed):
bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 0.5)
- # if SHIFT, fill primitive
- if self.shift or self.CreateMode:
- bgl.glBegin(bgl.GL_QUADS)
- bgl.glVertex2i(x0 + self.xpos, y0 + self.ypos)
- bgl.glVertex2i(x1 + self.xpos, y0 + self.ypos)
- bgl.glVertex2i(x1 + self.xpos, y1 + self.ypos)
- bgl.glVertex2i(x0 + self.xpos, y1 + self.ypos)
- bgl.glEnd()
+ bgl.glBegin(bgl.GL_POLYGON)
+ for x, y in self.mouse_path:
+ bgl.glVertex2i(x + self.xpos, y + self.ypos)
+ bgl.glEnd()
+ bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 1.0)
+ bgl.glBegin(bgl.GL_LINE_STRIP)
+ for x, y in self.mouse_path:
+ bgl.glVertex2i(x + self.xpos, y + self.ypos)
+ bgl.glEnd()
+ if (self.CreateMode is False) or (self.CreateMode and self.Closed):
bgl.glBegin(bgl.GL_LINE_STRIP)
- bgl.glVertex2i(x0 + self.xpos, y0 + self.ypos)
- bgl.glVertex2i(x1 + self.xpos, y0 + self.ypos)
- bgl.glVertex2i(x1 + self.xpos, y1 + self.ypos)
- bgl.glVertex2i(x0 + self.xpos, y1 + self.ypos)
- bgl.glVertex2i(x0 + self.xpos, y0 + self.ypos)
+ bgl.glVertex2i(self.mouse_path[len(self.mouse_path) - 1][0] + self.xpos,
+ self.mouse_path[len(self.mouse_path) - 1][1] + self.ypos)
+ bgl.glVertex2i(self.mouse_path[0][0] + self.xpos, self.mouse_path[0][1] + self.ypos)
bgl.glEnd()
- bgl.glPointSize(6)
- bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 1.0)
- bgl.glBegin(bgl.GL_POINTS)
+ bgl.glPointSize(6)
+ bgl.glBegin(bgl.GL_POINTS)
+ for x, y in self.mouse_path:
+ bgl.glVertex2i(x + self.xpos, y + self.ypos)
+ bgl.glEnd()
+
+ # Cut rectangle
+ if self.CutMode == RECTANGLE:
+ bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 0.5)
+
+ # if SHIFT, fill primitive
+ if self.shift or self.CreateMode:
+ bgl.glBegin(bgl.GL_QUADS)
bgl.glVertex2i(x0 + self.xpos, y0 + self.ypos)
bgl.glVertex2i(x1 + self.xpos, y0 + self.ypos)
bgl.glVertex2i(x1 + self.xpos, y1 + self.ypos)
bgl.glVertex2i(x0 + self.xpos, y1 + self.ypos)
bgl.glEnd()
- # Circle Cut
- if self.CutMode == CIRCLE:
- DEG2RAD = 3.14159 / 180
- v0 = mathutils.Vector((self.mouse_path[0][0], self.mouse_path[0][1], 0))
- v1 = mathutils.Vector((self.mouse_path[1][0], self.mouse_path[1][1], 0))
- v0 -= v1
- radius = self.mouse_path[1][0] - self.mouse_path[0][0]
- DEG2RAD = 3.14159 / (180.0 / self.stepAngle[self.step])
- if self.ctrl:
- self.stepR = (self.mouse_path[1][1] - self.mouse_path[0][1]) / 25
- shift = (3.14159 / (360.0 / 60.0)) * int(self.stepR)
- else:
- shift = (self.mouse_path[1][1] - self.mouse_path[0][1]) / 50
-
- if self.shift or self.CreateMode:
- bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 0.5)
- bgl.glBegin(bgl.GL_TRIANGLE_FAN)
- bgl.glVertex2f(x0 + self.xpos, y0 + self.ypos)
- for i in range(0, int(360 / self.stepAngle[self.step])):
- degInRad = i * DEG2RAD
- bgl.glVertex2f(x0 + self.xpos + math.cos(degInRad + shift) * radius,
- y0 + self.ypos + math.sin(degInRad + shift) * radius)
- bgl.glVertex2f(x0 + self.xpos + math.cos(0 + shift) * radius,
- y0 + self.ypos + math.sin(0 + shift) * radius)
- bgl.glEnd()
-
- bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 1.0)
- bgl.glBegin(bgl.GL_LINE_LOOP)
+ bgl.glBegin(bgl.GL_LINE_STRIP)
+ bgl.glVertex2i(x0 + self.xpos, y0 + self.ypos)
+ bgl.glVertex2i(x1 + self.xpos, y0 + self.ypos)
+ bgl.glVertex2i(x1 + self.xpos, y1 + self.ypos)
+ bgl.glVertex2i(x0 + self.xpos, y1 + self.ypos)
+ bgl.glVertex2i(x0 + self.xpos, y0 + self.ypos)
+ bgl.glEnd()
+ bgl.glPointSize(6)
+
+ bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 1.0)
+ bgl.glBegin(bgl.GL_POINTS)
+ bgl.glVertex2i(x0 + self.xpos, y0 + self.ypos)
+ bgl.glVertex2i(x1 + self.xpos, y0 + self.ypos)
+ bgl.glVertex2i(x1 + self.xpos, y1 + self.ypos)
+ bgl.glVertex2i(x0 + self.xpos, y1 + self.ypos)
+ bgl.glEnd()
+
+ # Circle Cut
+ if self.CutMode == CIRCLE:
+ DEG2RAD = 3.14159 / 180
+ v0 = Vector((self.mouse_path[0][0], self.mouse_path[0][1], 0))
+ v1 = Vector((self.mouse_path[1][0], self.mouse_path[1][1], 0))
+ v0 -= v1
+ radius = self.mouse_path[1][0] - self.mouse_path[0][0]
+ DEG2RAD = 3.14159 / (180.0 / self.stepAngle[self.step])
+ if self.ctrl:
+ self.stepR = (self.mouse_path[1][1] - self.mouse_path[0][1]) / 25
+ shift = (3.14159 / (360.0 / 60.0)) * int(self.stepR)
+ else:
+ shift = (self.mouse_path[1][1] - self.mouse_path[0][1]) / 50
+
+ if self.shift or self.CreateMode:
+ bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 0.5)
+ bgl.glBegin(bgl.GL_TRIANGLE_FAN)
+ bgl.glVertex2f(x0 + self.xpos, y0 + self.ypos)
for i in range(0, int(360 / self.stepAngle[self.step])):
degInRad = i * DEG2RAD
bgl.glVertex2f(x0 + self.xpos + math.cos(degInRad + shift) * radius,
y0 + self.ypos + math.sin(degInRad + shift) * radius)
+ bgl.glVertex2f(x0 + self.xpos + math.cos(0 + shift) * radius,
+ y0 + self.ypos + math.sin(0 + shift) * radius)
bgl.glEnd()
+ bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 1.0)
+ bgl.glBegin(bgl.GL_LINE_LOOP)
+ for i in range(0, int(360 / self.stepAngle[self.step])):
+ degInRad = i * DEG2RAD
+ bgl.glVertex2f(x0 + self.xpos + math.cos(degInRad + shift) * radius,
+ y0 + self.ypos + math.sin(degInRad + shift) * radius)
+ bgl.glEnd()
+
if self.ObjectMode or self.ProfileMode:
if self.ShowCursor:
region = context.region
rv3d = context.space_data.region_3d
- view_width = context.region.width
if self.ObjectMode:
ob = self.ObjectBrush
@@ -1233,7 +1187,7 @@ def draw_callback_px(self, context):
# 50% alpha, 2 pixel width line
bgl.glEnable(bgl.GL_BLEND)
- bbox = [mat * mathutils.Vector(b) for b in ob.bound_box]
+ bbox = [mat * Vector(b) for b in ob.bound_box]
if self.shift:
bgl.glLineWidth(4)
@@ -1260,12 +1214,12 @@ def draw_callback_px(self, context):
# Object display
if self.qRot is not None:
ob.location = self.CurLoc
- v = mathutils.Vector()
+ v = Vector()
v.x = v.y = 0.0
v.z = self.BrushDepthOffset
ob.location += self.qRot * v
- e = mathutils.Euler()
+ e = Euler()
e.x = 0.0
e.y = 0.0
e.z = self.aRotZ / 25.0
@@ -1319,10 +1273,10 @@ def isect_line_plane_v3(p0, p1, p_co, p_no, epsilon=1e-6):
# The segment is parallel to plane
return None
+
# ----------------------
# generic math functions
-
def add_v3v3(v0, v1):
return (
v0[0] + v1[0],
@@ -1358,21 +1312,19 @@ def mul_v3_fl(v0, f):
v0[2] * f,
)
-# Cut Square
-
+# Cut Square
def CreateCutSquare(self, context):
FAR_LIMIT = 10000.0
# New mesh
me = bpy.data.meshes.new('CMT_Square')
-
# New object
ob = bpy.data.objects.new('CMT_Square', me)
# Save new object
self.CurrentObj = ob
- # Scene informations
- scene = context.scene
+
+ # Scene information
region = context.region
rv3d = context.region_data
coord = self.mouse_path[0][0], self.mouse_path[0][1]
@@ -1382,14 +1334,10 @@ def CreateCutSquare(self, context):
if self.snapCursor:
PlanePoint = context.scene.cursor_location
else:
- if self.OpsObj is not None:
- PlanePoint = self.OpsObj.location
- else:
- PlanePoint = mathutils.Vector((0.0, 0.0, 0.0))
+ PlanePoint = self.OpsObj.location if self.OpsObj is not None else Vector((0.0, 0.0, 0.0))
PlaneNormal = depthLocation
PlaneNormalised = PlaneNormal.normalized()
- d = -PlanePoint.x * PlaneNormalised.x - PlanePoint.y * PlaneNormalised.y - PlanePoint.z * PlaneNormalised.z
# Link object to scene
context.scene.objects.link(ob)
@@ -1448,20 +1396,15 @@ def CreateCutLine(self, context):
ob = bpy.data.objects.new('CMT_Line', me)
self.CurrentObj = ob
- scene = context.scene
region = context.region
rv3d = context.region_data
coord = self.mouse_path[0][0], self.mouse_path[0][1]
depthLocation = region_2d_to_vector_3d(region, rv3d, coord)
self.ViewVector = depthLocation
- if self.snapCursor:
- PlanePoint = context.scene.cursor_location
- else:
- PlanePoint = mathutils.Vector((0.0, 0.0, 0.0))
+ PlanePoint = context.scene.cursor_location if self.snapCursor else Vector((0.0, 0.0, 0.0))
PlaneNormal = depthLocation
PlaneNormalised = PlaneNormal.normalized()
- d = -PlanePoint.x * PlaneNormalised.x - PlanePoint.y * PlaneNormalised.y - PlanePoint.z * PlaneNormalised.z
context.scene.objects.link(ob)
@@ -1473,8 +1416,9 @@ def CreateCutLine(self, context):
bLine = False
- if (len(self.mouse_path) == 2) or ((len(self.mouse_path) <= 3) and (self.mouse_path[1] == self.mouse_path[2])):
- PlanePoint = mathutils.Vector((0.0, 0.0, 0.0))
+ if (len(self.mouse_path) == 2) or ((len(self.mouse_path) <= 3) and
+ (self.mouse_path[1] == self.mouse_path[2])):
+ PlanePoint = Vector((0.0, 0.0, 0.0))
PlaneNormal = depthLocation
PlaneNormalised = PlaneNormal.normalized()
# Force rebool
@@ -1496,7 +1440,6 @@ def CreateCutLine(self, context):
Index += 1
if NbVertices == 1:
t_v0 = t_bm.verts.new(loc0)
- t_init = t_v0
LocInit = loc0
t_bm.verts.index_update()
else:
@@ -1504,7 +1447,6 @@ def CreateCutLine(self, context):
t_edges = t_bm.edges.new([t_v0, t_v1])
NbVertices = 1
t_v0 = t_v1
-
else:
for x, y in self.mouse_path:
v0 = x + self.xpos, y + self.ypos
@@ -1518,7 +1460,6 @@ def CreateCutLine(self, context):
NbVertices += 1
if NbVertices == 1:
t_v0 = t_bm.verts.new(loc0)
- t_init = t_v0
LocInit = loc0
t_bm.verts.index_update()
FacesList.append(t_v0)
@@ -1554,20 +1495,15 @@ def CreateCutCircle(self, context):
ob = bpy.data.objects.new('CMT_Circle', me)
self.CurrentObj = ob
- scene = context.scene
region = context.region
rv3d = context.region_data
coord = self.mouse_path[0][0], self.mouse_path[0][1]
depthLocation = region_2d_to_vector_3d(region, rv3d, coord)
self.ViewVector = depthLocation
- if self.snapCursor:
- PlanePoint = context.scene.cursor_location
- else:
- PlanePoint = mathutils.Vector((0.0, 0.0, 0.0))
+ PlanePoint = context.scene.cursor_location if self.snapCursor else Vector((0.0, 0.0, 0.0))
PlaneNormal = depthLocation
PlaneNormalised = PlaneNormal.normalized()
- d = -PlanePoint.x * PlaneNormalised.x - PlanePoint.y * PlaneNormalised.y - PlanePoint.z * PlaneNormalised.z
context.scene.objects.link(ob)
@@ -1576,11 +1512,9 @@ def CreateCutCircle(self, context):
x0 = self.mouse_path[0][0]
y0 = self.mouse_path[0][1]
- x1 = self.mouse_path[1][0]
- y1 = self.mouse_path[1][1]
- v0 = mathutils.Vector((self.mouse_path[0][0], self.mouse_path[0][1], 0))
- v1 = mathutils.Vector((self.mouse_path[1][0], self.mouse_path[1][1], 0))
+ v0 = Vector((self.mouse_path[0][0], self.mouse_path[0][1], 0))
+ v1 = Vector((self.mouse_path[1][0], self.mouse_path[1][1], 0))
v0 -= v1
radius = self.mouse_path[1][0] - self.mouse_path[0][0]
DEG2RAD = math.pi / (180.0 / self.stepAngle[self.step])
@@ -1594,7 +1528,8 @@ def CreateCutCircle(self, context):
FacesList = []
for i in range(0, int(360.0 / self.stepAngle[self.step])):
degInRad = i * DEG2RAD
- v0 = x0 + self.xpos + math.cos(degInRad + shift) * radius, y0 + self.ypos + math.sin(degInRad + shift) * radius
+ v0 = x0 + self.xpos + math.cos(degInRad + shift) * radius, \
+ y0 + self.ypos + math.sin(degInRad + shift) * radius
vec = region_2d_to_vector_3d(region, rv3d, v0)
loc0 = region_2d_to_location_3d(region, rv3d, v0, vec)
@@ -1607,7 +1542,6 @@ def CreateCutCircle(self, context):
FacesList.append(t_v0)
t_bm.verts.index_update()
-
t_face = t_bm.faces.new(FacesList)
t_bm.to_mesh(me)
@@ -1699,9 +1633,8 @@ def update_bevel(context):
obj.select = True
context.scene.objects.active = active
-# Create bevel
-
+# Create bevel
def CreateBevel(context, CurrentObject):
# Save active object
SavActive = context.active_object
@@ -1752,7 +1685,7 @@ def CreateBevel(context, CurrentObject):
context.object.data.use_auto_smooth = True
context.object.data.auto_smooth_angle = 1.0471975
- # Remet l'objet actif par défaut
+ # Restore the active object
context.scene.objects.active = SavActive
@@ -1767,7 +1700,6 @@ def Picking(context, event):
# get the ray from the viewport and mouse
view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord)
ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord)
-
ray_target = ray_origin + view_vector
def visible_objects_and_duplis():
@@ -1794,8 +1726,7 @@ def Picking(context, event):
success, location, normal, face_index = obj.ray_cast(ray_origin_obj, ray_direction_obj)
if success:
return location, normal, face_index
- else:
- return None, None, None
+ return None, None, None
# cast rays and find the closest object
best_length_squared = -1.0
@@ -1820,7 +1751,6 @@ def Picking(context, event):
def CreatePrimitive(self, _AngleStep, _radius):
- CLRaw = []
Angle = 0.0
self.NbPointsInPrimitive = 0
while(Angle < 360.0):
@@ -1838,7 +1768,7 @@ def CreatePrimitive(self, _AngleStep, _radius):
def MoveCursor(qRot, location, self):
if qRot is not None:
self.CLR_C.clear()
- vc = mathutils.Vector()
+ vc = Vector()
idx = 0
for i in range(int(len(self.CircleListRaw) / 3)):
vc.x = self.CircleListRaw[idx * 3] * self.CRadius
@@ -1855,12 +1785,12 @@ def RBenVe(Object, Dir):
ObjectV = Object.normalized()
DirV = Dir.normalized()
cosTheta = ObjectV.dot(DirV)
- rotationAxis = mathutils.Vector((0.0, 0.0, 0.0))
+ rotationAxis = Vector((0.0, 0.0, 0.0))
if (cosTheta < -1 + 0.001):
- v = mathutils.Vector((0.0, 1.0, 0.0))
+ v = Vector((0.0, 1.0, 0.0))
rotationAxis = ObjectV.cross(v)
rotationAxis = rotationAxis.normalized()
- q = mathutils.Quaternion()
+ q = Quaternion()
q.w = 0.0
q.x = rotationAxis.x
q.y = rotationAxis.y
@@ -1869,7 +1799,7 @@ def RBenVe(Object, Dir):
rotationAxis = ObjectV.cross(DirV)
s = math.sqrt((1.0 + cosTheta) * 2.0)
invs = 1 / s
- q = mathutils.Quaternion()
+ q = Quaternion()
q.w = s * 0.5
q.x = rotationAxis.x * invs
q.y = rotationAxis.y * invs
@@ -1878,7 +1808,6 @@ def RBenVe(Object, Dir):
def Pick(context, event, self, ray_max=10000.0):
- scene = context.scene
region = context.region
rv3d = context.region_data
coord = event.mouse_region_x, event.mouse_region_y
@@ -1893,8 +1822,7 @@ def Pick(context, event, self, ray_max=10000.0):
success, hit, normal, face_index = obj.ray_cast(ray_origin_obj, ray_target_obj)
if success:
return hit, normal, face_index
- else:
- return None, None, None
+ return None, None, None
best_length_squared = ray_max * ray_max
best_obj = None
@@ -1914,8 +1842,8 @@ def Pick(context, event, self, ray_max=10000.0):
if best_obj is not None:
return hits, ns, fs, rotation
- else:
- return None, None, None
+
+ return None, None, None
def SelectObject(self, copyobj):
@@ -1939,7 +1867,7 @@ def UndoAdd(self, type, OpsObj):
return
if type != "DUPLICATE":
ob = OpsObj
- # Creation du mesh de 'sauvegarde'
+ # Create the 'backup' mesh
bm = bmesh.new()
bm.from_mesh(ob.data)
@@ -2011,7 +1939,7 @@ def duplicateObject(self):
ob_new = bpy.context.active_object
ob_new.location = self.CurLoc
- v = mathutils.Vector()
+ v = Vector()
v.x = v.y = 0.0
v.z = self.BrushDepthOffset
ob_new.location += self.qRot * v
@@ -2021,7 +1949,7 @@ def duplicateObject(self):
if self.ProfileMode:
ob_new.scale = self.ProfileBrush.scale
- e = mathutils.Euler()
+ e = Euler()
e.x = e.y = 0.0
e.z = self.aRotZ / 25.0
@@ -2075,7 +2003,10 @@ def update_grid(self, context):
# Get the data from the profils or the object
if self.ProfileMode:
- brush = bpy.data.objects.new(self.Profils[self.nProfil][0], bpy.data.meshes[self.Profils[self.nProfil][0]])
+ brush = bpy.data.objects.new(
+ self.Profils[self.nProfil][0],
+ bpy.data.meshes[self.Profils[self.nProfil][0]]
+ )
obj = bpy.data.objects["CT_Profil"]
obfaces = brush.data.polygons
obverts = brush.data.vertices
@@ -2107,7 +2038,7 @@ def update_grid(self, context):
# Add random rotation
if (self.RandomRotation) and not (self.GridScaleX or self.GridScaleY):
- rotmat = mathutils.Matrix.Rotation(math.radians(360 * random.random()), 4, 'Z')
+ rotmat = Matrix.Rotation(math.radians(360 * random.random()), 4, 'Z')
for v in obverts:
v.co = v.co * rotmat
@@ -2124,47 +2055,25 @@ def update_grid(self, context):
mymesh.update(calc_edges=True)
# Update data
obj.data = mymesh
- # Make the the object the active one to remove double
+ # Make the object active to remove doubles
context.scene.objects.active = obj
-def boolean_difference():
+def boolean_operation(bool_type="DIFFERENCE"):
ActiveObj = bpy.context.active_object
+ sel_index = 0 if bpy.context.selected_objects[0] != bpy.context.active_object else 1
- if bpy.context.selected_objects[0] != bpy.context.active_object:
- bpy.ops.object.modifier_apply(apply_as='DATA', modifier="CT_SOLIDIFY")
- BoolMod = ActiveObj.modifiers.new("CT_" + bpy.context.selected_objects[0].name, "BOOLEAN")
- BoolMod.object = bpy.context.selected_objects[0]
- BoolMod.operation = "DIFFERENCE"
- BoolMod.solver = bpy.context.scene.CarverSolver
- bpy.context.selected_objects[0].draw_type = 'WIRE'
- else:
- BoolMod = ActiveObj.modifiers.new("CT_" + bpy.context.selected_objects[1].name, "BOOLEAN")
- BoolMod.object = bpy.context.selected_objects[1]
- BoolMod.operation = "DIFFERENCE"
- BoolMod.solver = bpy.context.scene.CarverSolver
- bpy.context.selected_objects[1].draw_type = 'WIRE'
-
-
-def boolean_union():
- ActiveObj = bpy.context.active_object
-
- if bpy.context.selected_objects[0] != bpy.context.active_object:
- BoolMod = ActiveObj.modifiers.new("CT_" + bpy.context.selected_objects[0].name, "BOOLEAN")
- BoolMod.object = bpy.context.selected_objects[0]
- BoolMod.operation = "UNION"
- bpy.context.selected_objects[0].draw_type = 'WIRE'
- else:
- BoolMod = ActiveObj.modifiers.new("CT_" + bpy.context.selected_objects[1].name, "BOOLEAN")
- BoolMod.object = bpy.context.selected_objects[1]
- BoolMod.operation = "UNION"
- bpy.context.selected_objects[1].draw_type = 'WIRE'
+ bpy.ops.object.modifier_apply(apply_as='DATA', modifier="CT_SOLIDIFY")
+ BoolMod = ActiveObj.modifiers.new(
+ "CT_" + bpy.context.selected_objects[sel_index].name,
+ "BOOLEAN"
+ )
+ BoolMod.object = bpy.context.selected_objects[sel_index]
+ BoolMod.operation = bool_type
+ bpy.context.selected_objects[sel_index].draw_type = 'WIRE'
def Rebool(context, self):
- SelObj_Name = []
- BoolObj = []
-
LastObj = context.active_object
Brush = context.selected_objects[0]
@@ -2204,12 +2113,10 @@ def Rebool(context, self):
m = LastObjectCreated.modifiers.new("CT_INTERSECT", "BOOLEAN")
m.operation = "INTERSECT"
- m.solver = context.scene.CarverSolver
m.object = Brush
m = obj.modifiers.new("CT_DIFFERENCE", "BOOLEAN")
m.operation = "DIFFERENCE"
- m.solver = context.scene.CarverSolver
m.object = Brush
for mb in LastObj.modifiers:
@@ -2262,14 +2169,14 @@ def createMeshFromData(self):
if "CT_Profil" not in bpy.data.objects:
ob = bpy.data.objects.new("CT_Profil", bpy.data.meshes[self.Profils[self.nProfil][0]])
- ob.location = mathutils.Vector((0.0, 0.0, 0.0))
+ ob.location = Vector((0.0, 0.0, 0.0))
# Link object to scene and make active
scn = bpy.context.scene
scn.objects.link(ob)
scn.objects.active = ob
ob.select = True
- ob.location = mathutils.Vector((10000.0, 0.0, 0.0))
+ ob.location = Vector((10000.0, 0.0, 0.0))
ob.draw_type = "WIRE"
self.SolidifyPossible = True
@@ -2277,25 +2184,38 @@ def createMeshFromData(self):
bpy.data.objects["CT_Profil"].data = bpy.data.meshes[self.Profils[self.nProfil][0]]
+def Selection_Save_Restore(self):
+ if "CT_Profil" in bpy.data.objects:
+ Selection_Save(self)
+ bpy.ops.object.select_all(action='DESELECT')
+ bpy.data.objects["CT_Profil"].select = True
+ bpy.context.scene.objects.active = bpy.data.objects["CT_Profil"]
+ if bpy.data.objects["CT_Profil"] in self.SavSel:
+ self.SavSel.remove(bpy.data.objects["CT_Profil"])
+ bpy.ops.object.delete(use_global=False)
+ Selection_Restore(self)
+
+
def Selection_Save(self):
+ obj_name = getattr(bpy.context.active_object, "name", None)
self.SavSel = bpy.context.selected_objects.copy()
- self.Sav_ac = bpy.context.active_object
+ self.Sav_ac = obj_name
def Selection_Restore(self):
for o in self.SavSel:
o.select = True
- bpy.context.scene.objects.active = self.Sav_ac
+ if self.Sav_ac:
+ bpy.context.scene.objects.active = bpy.data.objects.get(self.Sav_ac, None)
# Modal Operator
class Carver(bpy.types.Operator):
bl_idname = "object.carver"
bl_label = "Carver"
- bl_description = "Cut or create in object mode"
+ bl_description = "Cut or create Meshes in Object mode"
bl_options = {'REGISTER', 'UNDO'}
- # --------------------------------------------------------------------------------------------------
@classmethod
def poll(cls, context):
ob = None
@@ -2306,14 +2226,12 @@ class Carver(bpy.types.Operator):
(ob and ob.type == 'MESH' and context.mode == 'OBJECT') or
(context.mode == 'OBJECT' and ob is None) or
(context.mode == 'EDIT_MESH'))
- # --------------------------------------------------------------------------------------------------
- # --------------------------------------------------------------------------------------------------
def modal(self, context, event):
PI = 3.14156
-
region_types = {'WINDOW', 'UI'}
win = context.window
+
for area in win.screen.areas:
if area.type in ('VIEW_3D'):
for region in area.regions:
@@ -2327,14 +2245,10 @@ class Carver(bpy.types.Operator):
return {'PASS_THROUGH'}
try:
# [Shift]
- self.shift = False
- if event.shift:
- self.shift = True
+ self.shift = True if event.shift else False
# [Ctrl]
- self.ctrl = False
- if event.ctrl:
- self.ctrl = True
+ self.ctrl = True if event.ctrl else False
# [Alt]
self.alt = False
@@ -2349,7 +2263,7 @@ class Carver(bpy.types.Operator):
self.alt = True
# [Alt] release
if self.InitPosition and self.alt is False:
- # Update coordonnee
+ # Update coordinates
for i in range(0, len(self.mouse_path)):
l = list(self.mouse_path[i])
l[0] += self.xpos
@@ -2417,15 +2331,14 @@ class Carver(bpy.types.Operator):
self.CreateGeometry()
bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
# Cursor Snap
- context.scene.DepthCursor = self.snapCursor
+ context.scene.mesh_carver.DepthCursor = self.snapCursor
# Object Instantiate
- context.scene.OInstanciate = self.Instantiate
+ context.scene.mesh_carver.OInstanciate = self.Instantiate
# Random rotation
- context.scene.ORandom = self.RandomRotation
+ context.scene.mesh_carver.ORandom = self.RandomRotation
return {'FINISHED'}
else:
- # Cut
self.Cut()
UndoListUpdate(self)
@@ -2459,28 +2372,13 @@ class Carver(bpy.types.Operator):
self.BrushSolidify = False
self.CList = self.OB_List
- if "CT_Profil" in bpy.data.objects:
- Selection_Save(self)
- bpy.ops.object.select_all(action='DESELECT')
- bpy.data.objects["CT_Profil"].select = True
- context.scene.objects.active = bpy.data.objects["CT_Profil"]
- bpy.ops.object.delete(use_global=False)
- Selection_Restore(self)
-
- context.scene.nProfile = self.nProfil
-
+ Selection_Save_Restore(self)
+ context.scene.mesh_carver.nProfile = self.nProfil
else:
self.ObjectMode = False
else:
self.BrushSolidify = False
-
- if "CT_Profil" in bpy.data.objects:
- Selection_Save(self)
- bpy.ops.object.select_all(action='DESELECT')
- bpy.data.objects["CT_Profil"].select = True
- context.scene.objects.active = bpy.data.objects["CT_Profil"]
- bpy.ops.object.delete(use_global=False)
- Selection_Restore(self)
+ Selection_Save_Restore(self)
if self.ProfileMode:
createMeshFromData(self)
@@ -2496,7 +2394,6 @@ class Carver(bpy.types.Operator):
bpy.ops.object.modifier_add(type='SOLIDIFY')
context.object.modifiers["Solidify"].name = "CT_SOLIDIFY"
-
context.object.modifiers["CT_SOLIDIFY"].thickness = 0.1
Selection_Restore(self)
@@ -2525,7 +2422,6 @@ class Carver(bpy.types.Operator):
Selection_Restore(self)
else:
-
if self.Solidify_Active_Start:
Selection_Save(self)
self.BrushSolidify = True
@@ -2556,12 +2452,6 @@ class Carver(bpy.types.Operator):
if event.type == context.scene.Key_Apply and event.value == 'PRESS':
self.DontApply = not self.DontApply
- if event.type == context.scene.Key_Solver and event.value == 'PRESS':
- if context.scene.CarverSolver == "CARVE":
- context.scene.CarverSolver = "BMESH"
- else:
- context.scene.CarverSolver = "CARVE"
-
# Scale object
if event.type == context.scene.Key_Scale and event.value == 'PRESS':
if self.ObjectScale is False:
@@ -2731,7 +2621,6 @@ class Carver(bpy.types.Operator):
self.ProfileBrush.scale.x -= float(self.ascale) / 150.0
self.ProfileBrush.scale.y -= float(self.ascale) / 150.0
self.ProfileBrush.scale.z -= float(self.ascale) / 150.0
-
else:
if self.LMB:
if self.ctrl:
@@ -2746,10 +2635,9 @@ class Carver(bpy.types.Operator):
vBack = Pick(context, event, self)
if vBack[0] is not None:
self.ShowCursor = True
- NormalObject = mathutils.Vector((0.0, 0.0, 1.0))
+ NormalObject = Vector((0.0, 0.0, 1.0))
qR = RBenVe(NormalObject, vBack[1])
self.qRot = vBack[3] * qR
- Pos = vBack[0]
MoveCursor(qR, vBack[0], self)
self.SavCurLoc = vBack[0]
if self.ctrl:
@@ -2758,13 +2646,13 @@ class Carver(bpy.types.Operator):
yEcart = abs(self.SavMousePos.y - self.SavCurLoc.y)
zEcart = abs(self.SavMousePos.z - self.SavCurLoc.z)
if (xEcart > yEcart) and (xEcart > zEcart):
- self.CurLoc = mathutils.Vector(
+ self.CurLoc = Vector(
(vBack[0].x, self.SavMousePos.y, self.SavMousePos.z))
if (yEcart > xEcart) and (yEcart > zEcart):
- self.CurLoc = mathutils.Vector(
+ self.CurLoc = Vector(
(self.SavMousePos.x, vBack[0].y, self.SavMousePos.z))
if (zEcart > xEcart) and (zEcart > yEcart):
- self.CurLoc = mathutils.Vector(
+ self.CurLoc = Vector(
(self.SavMousePos.x, self.SavMousePos.y, vBack[0].z))
else:
self.CurLoc = vBack[0]
@@ -2807,7 +2695,7 @@ class Carver(bpy.types.Operator):
if self.LMB is False:
vBack = Pick(context, event, self)
if vBack[0] is not None:
- NormalObject = mathutils.Vector((0.0, 0.0, 1.0))
+ NormalObject = Vector((0.0, 0.0, 1.0))
self.aqR = RBenVe(NormalObject, vBack[1])
self.qRot = vBack[3] * self.aqR
self.am = event.mouse_region_x, event.mouse_region_y
@@ -2871,13 +2759,13 @@ class Carver(bpy.types.Operator):
self.CreateGeometry()
bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
# Depth Cursor
- context.scene.DepthCursor = self.snapCursor
+ context.scene.mesh_carver.DepthCursor = self.snapCursor
# Instantiate object
- context.scene.OInstanciate = self.Instantiate
+ context.scene.mesh_carver.OInstanciate = self.Instantiate
# Random rotation
- context.scene.ORandom = self.RandomRotation
+ context.scene.mesh_carver.ORandom = self.RandomRotation
# Apply operation
- context.scene.DontApply = self.DontApply
+ context.scene.mesh_carver.DontApply = self.DontApply
# if Object mode, set intiale state
if self.ObjectBrush is not None:
@@ -2900,14 +2788,12 @@ class Carver(bpy.types.Operator):
Selection_Restore(self)
- context.scene.nProfile = self.nProfil
+ context.scene.mesh_carver.nProfile = self.nProfil
return {'FINISHED'}
else:
- # Cut
self.Cut()
UndoListUpdate(self)
-
else:
# Line
self.mouse_path.append((event.mouse_region_x, event.mouse_region_y))
@@ -2941,17 +2827,16 @@ class Carver(bpy.types.Operator):
else:
if self.step > 0:
self.step -= 1
-
# Quit
elif event.type in {'RIGHTMOUSE', 'ESC'}:
# Depth Cursor
- context.scene.DepthCursor = self.snapCursor
+ context.scene.mesh_carver.DepthCursor = self.snapCursor
# Instantiate object
- context.scene.OInstanciate = self.Instantiate
+ context.scene.mesh_carver.OInstanciate = self.Instantiate
# Random Rotation
- context.scene.ORandom = self.RandomRotation
+ context.scene.mesh_carver.ORandom = self.RandomRotation
# Apply boolean operation
- context.scene.DontApply = self.DontApply
+ context.scene.mesh_carver.DontApply = self.DontApply
# Reset Object
if self.ObjectBrush is not None:
@@ -2971,24 +2856,16 @@ class Carver(bpy.types.Operator):
context.scene.objects.active = self.ObjectBrush
bpy.ops.object.modifier_remove(modifier="CT_SOLIDIFY")
-
bpy.ops.object.select_all(action='TOGGLE')
Selection_Restore(self)
- if "CT_Profil" in bpy.data.objects:
- Selection_Save(self)
- bpy.ops.object.select_all(action='DESELECT')
- bpy.data.objects["CT_Profil"].select = True
- context.scene.objects.active = bpy.data.objects["CT_Profil"]
- bpy.ops.object.delete(use_global=False)
- Selection_Restore(self)
-
+ Selection_Save_Restore(self)
context.scene.objects.active = self.CurrentActive
-
- context.scene.nProfile = self.nProfil
+ context.scene.mesh_carver.nProfile = self.nProfil
bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
+
# Remove Copy Object Brush
if bpy.data.objects.get("CarverBrushCopy") is not None:
brush = bpy.data.objects["CarverBrushCopy"]
@@ -3003,7 +2880,6 @@ class Carver(bpy.types.Operator):
except:
print("\n[Carver MT ERROR]\n")
-
import traceback
traceback.print_exc()
@@ -3016,242 +2892,246 @@ class Carver(bpy.types.Operator):
return {'FINISHED'}
- # --------------------------------------------------------------------------------------------------
+ def cancel(self, context):
+ # Note: used to prevent memory leaks on quiting Blender while the modal operator
+ # is still running, gets called on return {"CANCELLED"}
+ bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
- # --------------------------------------------------------------------------------------------------
def invoke(self, context, event):
- if context.area.type == 'VIEW_3D':
- if context.mode == 'EDIT_MESH':
- bpy.ops.object.mode_set(mode='OBJECT')
-
- win = context.window
-
- # Get default patterns
- self.Profils = []
- for p in Profils:
- self.Profils.append((p[0], p[1], p[2], p[3]))
+ if context.area.type != 'VIEW_3D':
+ self.report({'WARNING'},
+ "View3D not found or not currently active. Operation Cancelled")
+ return {'CANCELLED'}
- for o in context.scene.objects:
- if not o.name.startswith(context.scene.ProfilePrefix):
- continue
+ if context.mode == 'EDIT_MESH':
+ bpy.ops.object.mode_set(mode='OBJECT')
- # In-scene profiles may have changed, remove them to refresh
- for m in bpy.data.meshes:
- if m.name.startswith(context.scene.ProfilePrefix):
- bpy.data.meshes.remove(m)
+ # test if some other object types are selected that are not meshes
+ test_selection = True
+ for obj in context.selected_objects:
+ if obj.type != "MESH":
+ test_selection = False
+ break
+ if not test_selection:
+ self.report({'WARNING'},
+ "Some selected objects are not of the Mesh type. Operation Cancelled")
+ return {"CANCELLED"}
+
+ # Get default patterns
+ self.Profils = []
+ for p in Profils:
+ self.Profils.append((p[0], p[1], p[2], p[3]))
+
+ for o in context.scene.objects:
+ if not o.name.startswith(context.scene.ProfilePrefix):
+ continue
+
+ # In-scene profiles may have changed, remove them to refresh
+ for m in bpy.data.meshes:
+ if m.name.startswith(context.scene.ProfilePrefix):
+ bpy.data.meshes.remove(m)
+
+ vertices = []
+ for v in o.data.vertices:
+ vertices.append((v.co.x, v.co.y, v.co.z))
+
+ faces = []
+ for f in o.data.polygons:
+ face = []
+ for v in f.vertices:
+ face.append(v)
+
+ faces.append(face)
+
+ self.Profils.append(
+ (o.name,
+ Vector((o.location.x, o.location.y, o.location.z)),
+ vertices, faces)
+ )
- vertices = []
- for v in o.data.vertices:
- vertices.append((v.co.x, v.co.y, v.co.z))
+ self.nProfil = context.scene.mesh_carver.nProfile
+ self.MaxProfil = len(self.Profils)
- faces = []
- for f in o.data.polygons:
- face = []
+ # reset selected profile if last profile exceeds length of array
+ if self.nProfil >= self.MaxProfil:
+ self.nProfil = context.scene.mesh_carver.nProfile = 0
- for v in f.vertices:
- face.append(v)
+ # Save selection
+ self.CurrentSelection = context.selected_objects.copy()
+ self.CurrentActive = context.active_object
+ self.SavSel = context.selected_objects.copy()
+ self.Sav_ac = None
- faces.append(face)
+ args = (self, context)
- self.Profils.append(
- (o.name,
- mathutils.Vector((o.location.x, o.location.y, o.location.z)),
- vertices, faces)
- )
+ self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL')
- self.nProfil = context.scene.nProfile
- self.MaxProfil = len(self.Profils)
+ self.mouse_path = [(0, 0), (0, 0)]
- # reset selected profile if last profile exceeds length of array
- if self.nProfil >= self.MaxProfil:
- self.nProfil = context.scene.nProfile = 0
+ self.shift = False
+ self.ctrl = False
+ self.alt = False
+ self.bDone = False
- # Save selection
- self.CurrentSelection = context.selected_objects.copy()
- self.CurrentActive = context.active_object
- self.SavSel = context.selected_objects.copy()
- self.Sav_ac = None
+ self.DontApply = context.scene.mesh_carver.DontApply
+ self.Auto_BevelUpdate = True
- args = (self, context)
+ # Cut type (Rectangle, Circle, Line)
+ self.CutMode = 0
+ self.BoolOps = DIFFERENCE
- self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL')
+ # Circle variables
+ self.stepAngle = [2, 4, 5, 6, 9, 10, 15, 20, 30, 40, 45, 60, 72, 90]
+ self.step = 4
+ self.stepRotation = 0
- self.mouse_path = [(0, 0), (0, 0)]
+ # Primitives Position
+ self.xpos = 0
+ self.ypos = 0
+ self.InitPosition = False
- self.shift = False
- self.ctrl = False
- self.alt = False
+ # Line Increment
+ self.Increment = 15
+ # Close polygonal shape
+ self.Closed = False
- self.bDone = False
+ # Depth Cursor
+ self.snapCursor = context.scene.mesh_carver.DepthCursor
- self.DontApply = context.scene.DontApply
- self.Auto_BevelUpdate = True
+ # Help
+ self.AskHelp = False
- # Cut type (Rectangle, Circle, Line)
- self.CutMode = 0
- self.BoolOps = DIFFERENCE
+ # Working object
+ self.OpsObj = context.active_object
- # Circle variables
- self.stepAngle = [2, 4, 5, 6, 9, 10, 15, 20, 30, 40, 45, 60, 72, 90]
- self.step = 4
- self.stepRotation = 0
+ # Create mode
+ self.CreateMode = False
+ self.ExclusiveCreateMode = False
+ if len(context.selected_objects) == 0:
+ self.ExclusiveCreateMode = True
+ self.CreateMode = True
- # Primitives Position
- self.xpos = 0
- self.ypos = 0
- self.InitPosition = False
+ # Rebool forced (cut line)
+ self.ForceRebool = False
- # Line Increment
- self.Increment = 15
- # Close polygonal shape
- self.Closed = False
-
- # Depth Cursor
- self.snapCursor = context.scene.DepthCursor
-
- # Help
- self.AskHelp = False
-
- # Working object
- self.OpsObj = context.active_object
-
- # Create mode
- self.CreateMode = False
- self.ExclusiveCreateMode = False
- if len(context.selected_objects) == 0:
- self.ExclusiveCreateMode = True
- self.CreateMode = True
-
- # Rebool forced (cut line)
- self.ForceRebool = False
-
- self.ViewVector = mathutils.Vector()
-
- self.CurrentObj = None
-
- # Brush
- self.BrushSolidify = False
- self.WidthSolidify = False
- self.CarveDepth = False
- self.BrushDepth = False
- self.BrushDepthOffset = 0.0
-
- self.ObjectScale = False
-
- self.CircleListRaw = []
- self.CLR_C = []
- self.CurLoc = mathutils.Vector((0.0, 0.0, 0.0))
- self.SavCurLoc = mathutils.Vector((0.0, 0.0, 0.0))
- self.CRadius = 1.0
- CreatePrimitive(self, 10.0, 1.0)
- self.VertsList = []
- self.FacesList = []
-
- self.am = -1, -1
- self.SavMousePos = None
- self.xSavMouse = 0
-
- self.ascale = 0
- self.aRotZ = 0
- self.nRotZ = 0
- self.aqR = None
- self.qRot = None
-
- self.RandomRotation = context.scene.ORandom
-
- self.ShowCursor = True
-
- self.ObjectMode = False
- self.ProfileMode = False
- self.Instantiate = context.scene.OInstanciate
-
- self.ProfileBrush = None
- self.ObjectBrush = None
- self.InitBrushPosition = None
- self.InitBrushScale = None
- self.InitBrushQRotation = None
- self.InitBrushERotation = None
- self.InitBrushARotation = None
-
- self.ObjectBrush_DT = "WIRE"
- self.XRay = False
-
- # Grid mesh
- self.nbcol = 1
- self.nbrow = 1
- self.gapx = 0
- self.gapy = 0
- self.scale_x = 1
- self.scale_y = 1
-
- self.GridScaleX = False
- self.GridScaleY = False
-
- if len(context.selected_objects) > 1:
- self.ObjectBrush = context.active_object
-
- # Copy the brush object
- ob = bpy.data.objects.new("CarverBrushCopy", context.object.data.copy())
- ob.location = self.ObjectBrush.location
- scene = context.scene
- scene.objects.link(ob)
- scene.update()
-
- # Get default variables
- self.InitBrushPosition = self.ObjectBrush.location.copy()
- self.InitBrushScale = self.ObjectBrush.scale.copy()
- self.InitBrushQRotation = self.ObjectBrush.rotation_quaternion.copy()
- self.InitBrushERotation = self.ObjectBrush.rotation_euler.copy()
- self.ObjectBrush_DT = self.ObjectBrush.draw_type
- self.XRay = self.ObjectBrush.show_x_ray
- # Test if flat object
- z = self.ObjectBrush.data.vertices[0].co.z
- ErrorMarge = 0.01
- self.Solidify_Active_Start = True
- for v in self.ObjectBrush.data.vertices:
- if abs(v.co.z - z) > ErrorMarge:
- self.Solidify_Active_Start = False
- break
- self.SolidifyPossible = False
-
- self.CList = []
- self.OPList = []
- self.RList = []
- self.OB_List = []
-
- for ent in context.selected_objects:
- if ent != self.ObjectBrush:
- self.OB_List.append(ent)
-
- # Left button
- self.LMB = False
-
- # Undo Variables
- self.undo_index = 0
- self.undo_limit = context.user_preferences.edit.undo_steps
- self.undo_list = []
-
- # Boolean operations type
- self.BooleanType = 0
-
- self.UList = []
- self.UList_Index = -1
- self.UndoOps = []
-
- context.window_manager.modal_handler_add(self)
- return {'RUNNING_MODAL'}
- else:
- self.report({'WARNING'}, "View3D not found, cannot run operator")
- return {'CANCELLED'}
- # --------------------------------------------------------------------------------------------------
+ self.ViewVector = Vector()
+ self.CurrentObj = None
+
+ # Brush
+ self.BrushSolidify = False
+ self.WidthSolidify = False
+ self.CarveDepth = False
+ self.BrushDepth = False
+ self.BrushDepthOffset = 0.0
+
+ self.ObjectScale = False
+
+ self.CircleListRaw = []
+ self.CLR_C = []
+ self.CurLoc = Vector((0.0, 0.0, 0.0))
+ self.SavCurLoc = Vector((0.0, 0.0, 0.0))
+ self.CRadius = 1.0
+ CreatePrimitive(self, 10.0, 1.0)
+ self.VertsList = []
+ self.FacesList = []
+
+ self.am = -1, -1
+ self.SavMousePos = None
+ self.xSavMouse = 0
+
+ self.ascale = 0
+ self.aRotZ = 0
+ self.nRotZ = 0
+ self.aqR = None
+ self.qRot = None
+
+ self.RandomRotation = context.scene.mesh_carver.ORandom
+
+ self.ShowCursor = True
+ self.ObjectMode = False
+ self.ProfileMode = False
+ self.Instantiate = context.scene.mesh_carver.OInstanciate
+
+ self.ProfileBrush = None
+ self.ObjectBrush = None
+ self.InitBrushPosition = None
+ self.InitBrushScale = None
+ self.InitBrushQRotation = None
+ self.InitBrushERotation = None
+ self.InitBrushARotation = None
+
+ self.ObjectBrush_DT = "WIRE"
+ self.XRay = False
+
+ # Grid mesh
+ self.nbcol = 1
+ self.nbrow = 1
+ self.gapx = 0
+ self.gapy = 0
+ self.scale_x = 1
+ self.scale_y = 1
+
+ self.GridScaleX = False
+ self.GridScaleY = False
+
+ if len(context.selected_objects) > 1:
+ self.ObjectBrush = context.active_object
+
+ # Copy the brush object
+ ob = bpy.data.objects.new("CarverBrushCopy", context.object.data.copy())
+ ob.location = self.ObjectBrush.location
+ scene = context.scene
+ scene.objects.link(ob)
+ scene.update()
+
+ # Get default variables
+ self.InitBrushPosition = self.ObjectBrush.location.copy()
+ self.InitBrushScale = self.ObjectBrush.scale.copy()
+ self.InitBrushQRotation = self.ObjectBrush.rotation_quaternion.copy()
+ self.InitBrushERotation = self.ObjectBrush.rotation_euler.copy()
+ self.ObjectBrush_DT = self.ObjectBrush.draw_type
+ self.XRay = self.ObjectBrush.show_x_ray
+ # Test if flat object
+ z = self.ObjectBrush.data.vertices[0].co.z
+ ErrorMarge = 0.01
+ self.Solidify_Active_Start = True
+ for v in self.ObjectBrush.data.vertices:
+ if abs(v.co.z - z) > ErrorMarge:
+ self.Solidify_Active_Start = False
+ break
+ self.SolidifyPossible = False
+
+ self.CList = []
+ self.OPList = []
+ self.RList = []
+ self.OB_List = []
+
+ for ent in context.selected_objects:
+ if ent != self.ObjectBrush:
+ self.OB_List.append(ent)
+
+ # Left button
+ self.LMB = False
+
+ # Undo Variables
+ self.undo_index = 0
+ self.undo_limit = context.user_preferences.edit.undo_steps
+ self.undo_list = []
+
+ # Boolean operations type
+ self.BooleanType = 0
+
+ self.UList = []
+ self.UList_Index = -1
+ self.UndoOps = []
+
+ context.window_manager.modal_handler_add(self)
+ return {'RUNNING_MODAL'}
- # --------------------------------------------------------------------------------------------------
def CreateGeometry(self):
context = bpy.context
-
- region_id = context.region.id
-
bLocalView = False
+
for area in context.screen.areas:
if area.type == 'VIEW_3D':
if area.spaces[0].local_view is not None:
@@ -3309,14 +3189,10 @@ class Carver(bpy.types.Operator):
self.bDone = False
self.mouse_path.clear()
self.mouse_path = [(0, 0), (0, 0)]
- # --------------------------------------------------------------------------------------------------
- # --------------------------------------------------------------------------------------------------
def Cut(self):
context = bpy.context
- UNDO = []
-
# Local view ?
bLocalView = False
for area in context.screen.areas:
@@ -3358,7 +3234,7 @@ class Carver(bpy.types.Operator):
bpy.ops.mesh.normals_make_consistent()
bpy.ops.object.mode_set(mode='OBJECT')
else:
- # Create liste
+ # Create list
if self.ObjectMode:
for o in self.CurrentSelection:
if o != self.ObjectBrush:
@@ -3380,7 +3256,7 @@ class Carver(bpy.types.Operator):
if len(context.selected_objects) > 0:
bpy.ops.object.select_all(action='TOGGLE')
- # Testif intitiale object has bevel
+ # Test if initial object has bevel
BevelAO = False
for obj in ActiveObjList:
for mb in obj.modifiers:
@@ -3407,14 +3283,11 @@ class Carver(bpy.types.Operator):
if (self.shift is False) and (self.ForceRebool is False):
if self.ObjectMode or self.ProfileMode:
if self.BoolOps == UNION:
- # Union
- boolean_union()
+ boolean_operation(bool_type="UNION")
else:
- # Cut object
- boolean_difference()
+ boolean_operation(bool_type="DIFFERENCE")
else:
- # Cut
- boolean_difference()
+ boolean_operation(bool_type="DIFFERENCE")
# Apply booleans
if self.DontApply is False:
@@ -3487,7 +3360,6 @@ class Carver(bpy.types.Operator):
if len(context.selected_objects) > 0:
bpy.ops.object.select_all(action='TOGGLE')
bpy.data.objects[self.CurrentObj.name].select = True
- cname = self.CurrentObj.name
bpy.ops.object.delete(use_global=False)
else:
if self.ObjectMode:
@@ -3496,7 +3368,7 @@ class Carver(bpy.types.Operator):
if len(context.selected_objects) > 0:
bpy.ops.object.select_all(action='TOGGLE')
- # Select cutted objects
+ # Select cut objects
for obj in lastSelected:
bpy.data.objects[obj.name].select = True
@@ -3508,10 +3380,10 @@ class Carver(bpy.types.Operator):
if self.Auto_BevelUpdate:
update_bevel(context)
- # Reselect intiale objects
+ # Re-select initial objects
bpy.ops.object.select_all(action='TOGGLE')
if self.ObjectMode:
- # Reselect brush
+ # Re-select brush
self.ObjectBrush.select = True
for ActiveObj in ActiveObjList:
bpy.data.objects[ActiveObj.name].select = True
@@ -3535,23 +3407,45 @@ class Carver(bpy.types.Operator):
self.ForceRebool = False
-classes = (
- Carver,
+class CarverProperties(bpy.types.PropertyGroup):
+ DepthCursor = BoolProperty(
+ name="DepthCursor",
+ default=False
+ )
+ OInstanciate = BoolProperty(
+ name="Obj_Instantiate",
+ default=False
+ )
+ ORandom = BoolProperty(
+ name="Random_Rotation",
+ default=False
+ )
+ DontApply = BoolProperty(
+ name="Dont_Apply",
+ default=False
)
+ nProfile = IntProperty(
+ name="Num_Profile",
+ default=0
+ )
+
addon_keymaps = []
+classes = (
+ CarverPrefs,
+ CarverProperties,
+ Carver,
+)
-def register():
- bpy.types.Scene.DepthCursor = BoolProperty(name="DepthCursor", default=False)
- bpy.types.Scene.OInstanciate = BoolProperty(name="Obj_Instantiate", default=False)
- bpy.types.Scene.ORandom = BoolProperty(name="Random_Rotation", default=False)
- bpy.types.Scene.DontApply = BoolProperty(name="Dont_Apply", default=False)
- bpy.types.Scene.nProfile = IntProperty(name="Num_Profile", default=0)
- bpy.utils.register_class(CarverPrefs)
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
- bpy.utils.register_class(Carver)
+ bpy.types.Scene.mesh_carver = PointerProperty(
+ type=CarverProperties
+ )
# add keymap entry
kcfg = bpy.context.window_manager.keyconfigs.addon
if kcfg:
@@ -3561,14 +3455,15 @@ def register():
def unregister():
- bpy.utils.unregister_class(CarverPrefs)
+ for cls in classes:
+ bpy.utils.unregister_class(cls)
# remove keymap entry
for km, kmi in addon_keymaps:
km.keymap_items.remove(kmi)
addon_keymaps.clear()
- bpy.utils.unregister_class(Carver)
+ del bpy.types.Scene.mesh_carver
if __name__ == "__main__":
diff --git a/mesh_tiny_cad/BIX.py b/mesh_tiny_cad/BIX.py
index faac5212..89908f60 100644
--- a/mesh_tiny_cad/BIX.py
+++ b/mesh_tiny_cad/BIX.py
@@ -92,11 +92,3 @@ class TCLineOnBisection(bpy.types.Operator):
def execute(self, context):
add_line_to_bisection(self)
return {'FINISHED'}
-
-
-def register():
- bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
diff --git a/mesh_tiny_cad/CCEN.py b/mesh_tiny_cad/CCEN.py
index f625504b..ef2b5eae 100644
--- a/mesh_tiny_cad/CCEN.py
+++ b/mesh_tiny_cad/CCEN.py
@@ -157,11 +157,3 @@ class TCCircleCenter(bpy.types.Operator):
def execute(self, context):
dispatch(context, mode=1)
return {'FINISHED'}
-
-
-def register():
- bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
diff --git a/mesh_tiny_cad/CFG.py b/mesh_tiny_cad/CFG.py
index ed703a2f..55e0ff9f 100644
--- a/mesh_tiny_cad/CFG.py
+++ b/mesh_tiny_cad/CFG.py
@@ -74,11 +74,3 @@ def unregister_icons():
for pcoll in icon_collection.values():
bpy.utils.previews.remove(pcoll)
icon_collection.clear()
-
-
-def register():
- bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
diff --git a/mesh_tiny_cad/E2F.py b/mesh_tiny_cad/E2F.py
index 8c95f126..25f17e9f 100644
--- a/mesh_tiny_cad/E2F.py
+++ b/mesh_tiny_cad/E2F.py
@@ -100,11 +100,3 @@ class TCEdgeToFace(bpy.types.Operator):
def execute(self, context):
extend_vertex(self)
return {'FINISHED'}
-
-
-def register():
- bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
diff --git a/mesh_tiny_cad/V2X.py b/mesh_tiny_cad/V2X.py
index c230f4a2..c8683297 100644
--- a/mesh_tiny_cad/V2X.py
+++ b/mesh_tiny_cad/V2X.py
@@ -60,11 +60,3 @@ class TCVert2Intersection(bpy.types.Operator):
def execute(self, context):
add_vertex_to_intersection()
return {'FINISHED'}
-
-
-def register():
- bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
diff --git a/mesh_tiny_cad/VTX.py b/mesh_tiny_cad/VTX.py
index 5ad035b2..5cdb2fcf 100644
--- a/mesh_tiny_cad/VTX.py
+++ b/mesh_tiny_cad/VTX.py
@@ -173,11 +173,3 @@ class TCAutoVTX(bpy.types.Operator):
bmesh.update_edit_mesh(me, True)
return {'FINISHED'}
-
-
-def register():
- bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
diff --git a/mesh_tiny_cad/XALL.py b/mesh_tiny_cad/XALL.py
index 83ecdb52..fc0ed76e 100644
--- a/mesh_tiny_cad/XALL.py
+++ b/mesh_tiny_cad/XALL.py
@@ -174,11 +174,3 @@ class TCIntersectAllEdges(bpy.types.Operator):
print('must be in edit mode')
return {'FINISHED'}
-
-
-def register():
- bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
diff --git a/mesh_tiny_cad/__init__.py b/mesh_tiny_cad/__init__.py
index 611c67c4..4a9a62f0 100644
--- a/mesh_tiny_cad/__init__.py
+++ b/mesh_tiny_cad/__init__.py
@@ -50,7 +50,7 @@ if "bpy" in locals():
import bpy
-from .CFG import TinyCADProperties
+from .CFG import TinyCADProperties, VIEW3D_MT_edit_mesh_tinycad
from .CFG import register_icons, unregister_icons
from . import VTX, V2X, XALL, BIX, CCEN, E2F
@@ -59,10 +59,20 @@ def menu_func(self, context):
self.layout.menu("VIEW3D_MT_edit_mesh_tinycad")
self.layout.separator()
+classes = [
+ TinyCADProperties, VIEW3D_MT_edit_mesh_tinycad,
+ VTX.TCAutoVTX,
+ XALL.TCIntersectAllEdges,
+ V2X.TCVert2Intersection,
+ E2F.TCEdgeToFace,
+ CCEN.TCCallBackCCEN, CCEN.TCCircleCenter,
+ BIX.TCLineOnBisection
+]
def register():
register_icons()
- bpy.utils.register_module(__name__)
+ for cls in classes:
+ bpy.utils.register_class(cls)
bpy.types.Scene.tinycad_props = bpy.props.PointerProperty(
name="TinyCAD props", type=TinyCADProperties)
bpy.types.VIEW3D_MT_edit_mesh_specials.prepend(menu_func)
@@ -70,6 +80,7 @@ def register():
def unregister():
bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_func)
- bpy.utils.unregister_module(__name__)
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
del bpy.types.Scene.tinycad_props
unregister_icons()
diff --git a/modules/cycles_shader_compat.py b/modules/cycles_shader_compat.py
index 8a967f46..c85ba667 100644
--- a/modules/cycles_shader_compat.py
+++ b/modules/cycles_shader_compat.py
@@ -19,6 +19,7 @@
# <pep8 compliant>
import bpy
+import math
__all__ = (
"CyclesShaderWrapper",
@@ -392,7 +393,7 @@ class CyclesShaderWrapper():
def hardness_value_set(self, value):
node = self.node_mix_color_hard
- node.inputs["Color1"].default_value = (value,) * 4
+ node.inputs["Color1"].default_value = (math.sqrt(max(value, 0.0)),) * 4
def hardness_image_set(self, image):
node = self.node_mix_color_hard
diff --git a/modules/rna_manual_reference.py b/modules/rna_manual_reference.py
index 3cf680cb..a477d3ec 100644
--- a/modules/rna_manual_reference.py
+++ b/modules/rna_manual_reference.py
@@ -31,6 +31,7 @@ if LANG is not None:
url_manual_mapping = (
("bpy.types.cyclesobjectsettings.use_adaptive_subdivision*", "render/cycles/settings/objects/adaptive_subsurf.html#bpy-types-cyclesobjectsettings-use-adaptive-subdivision"),
("bpy.types.toolsettings.gpencil_stroke_placement_view3d*", "interface/grease_pencil/drawing/introduction.html#bpy-types-toolsettings-gpencil-stroke-placement-view3d"),
+ ("bpy.types.cyclesrendersettings.offscreen_dicing_scale*", "render/cycles/settings/scene/render/geometry.html#bpy-types-cyclesrendersettings-offscreen-dicing-scale"),
("bpy.types.linestylegeometrymodifier_backbonestretcher*", "render/freestyle/parameter_editor/line_style/modifiers/geometry.html#bpy-types-linestylegeometrymodifier-backbonestretcher"),
("bpy.types.linestylegeometrymodifier_sinusdisplacement*", "render/freestyle/parameter_editor/line_style/modifiers/geometry.html#bpy-types-linestylegeometrymodifier-sinusdisplacement"),
("bpy.types.toolsettings.use_gpencil_continuous_drawing*", "interface/grease_pencil/drawing/introduction.html#bpy-types-toolsettings-use-gpencil-continuous-drawing"),
@@ -46,7 +47,7 @@ url_manual_mapping = (
("bpy.types.linestylegeometrymodifier_guidinglines*", "render/freestyle/parameter_editor/line_style/modifiers/geometry.html#bpy-types-linestylegeometrymodifier-guidinglines"),
("bpy.types.linestylegeometrymodifier_spatialnoise*", "render/freestyle/parameter_editor/line_style/modifiers/geometry.html#bpy-types-linestylegeometrymodifier-spatialnoise"),
("bpy.types.linestylethicknessmodifier_calligraphy*", "render/freestyle/parameter_editor/line_style/modifiers/properties.html#bpy-types-linestylethicknessmodifier-calligraphy"),
- ("bpy.types.cyclesrendersettings.max_subdivisions*", "render/cycles/settings/scene/render/integrator.html#bpy-types-cyclesrendersettings-max-subdivisions"),
+ ("bpy.types.cyclesrendersettings.max_subdivisions*", "render/cycles/settings/scene/render/geometry.html#bpy-types-cyclesrendersettings-max-subdivisions"),
("bpy.types.linestyle*modifier_distancefromcamera*", "render/freestyle/parameter_editor/line_style/modifiers/properties.html#bpy-types-linestyle-modifier-distancefromcamera"),
("bpy.types.linestyle*modifier_distancefromobject*", "render/freestyle/parameter_editor/line_style/modifiers/properties.html#bpy-types-linestyle-modifier-distancefromobject"),
("bpy.types.linestylegeometrymodifier_2dtransform*", "render/freestyle/parameter_editor/line_style/modifiers/geometry.html#bpy-types-linestylegeometrymodifier-2dtransform"),
@@ -57,27 +58,31 @@ url_manual_mapping = (
("bpy.types.cyclesmaterialsettings.displacement*", "render/cycles/materials/settings.html#bpy-types-cyclesmaterialsettings-displacement"),
("bpy.types.linestylegeometrymodifier_blueprint*", "render/freestyle/parameter_editor/line_style/modifiers/geometry.html#bpy-types-linestylegeometrymodifier-blueprint"),
("bpy.types.rendersettings.simplify_subdivision*", "data_system/scenes/properties.html#bpy-types-rendersettings-simplify-subdivision"),
+ ("bpy.types.cyclesrendersettings.dicing_camera*", "render/cycles/settings/scene/render/geometry.html#bpy-types-cyclesrendersettings-dicing-camera"),
("bpy.types.cyclesrendersettings.texture_limit*", "render/cycles/settings/scene/introduction.html#bpy-types-cyclesrendersettings-texture-limit"),
("bpy.types.linestylegeometrymodifier_2doffset*", "render/freestyle/parameter_editor/line_style/modifiers/geometry.html#bpy-types-linestylegeometrymodifier-2doffset"),
("bpy.types.linestylegeometrymodifier_sampling*", "render/freestyle/parameter_editor/line_style/modifiers/geometry.html#bpy-types-linestylegeometrymodifier-sampling"),
("bpy.types.spaceview3d.show_background_images*", "editors/3dview/properties/background_images.html#bpy-types-spaceview3d-show-background-images"),
- ("bpy.types.cyclesrendersettings.*dicing_rate*", "render/cycles/settings/scene/render/integrator.html#bpy-types-cyclesrendersettings-dicing-rate"),
+ ("bpy.types.cyclesrendersettings.*dicing_rate*", "render/cycles/settings/scene/render/geometry.html#bpy-types-cyclesrendersettings-dicing-rate"),
+ ("bpy.types.rendersettings.use_file_extension*", "render/output/output.html#bpy-types-rendersettings-use-file-extension"),
("bpy.types.spaceview3d.transform_orientation*", "editors/3dview/object/editing/transform/control/orientations.html#bpy-types-spaceview3d-transform-orientation"),
("bpy.ops.object.constraint_add_with_targets*", "rigging/constraints/interface/adding_removing.html#bpy-ops-object-constraint-add-with-targets"),
("bpy.types.cyclesobjectsettings.dicing_rate*", "render/cycles/settings/objects/adaptive_subsurf.html#bpy-types-cyclesobjectsettings-dicing-rate"),
- ("bpy.types.spaceuveditor.use_snap_to_pixels*", "editors/uv_image/uv_editing/layout_editing.html#bpy-types-spaceuveditor-use-snap-to-pixels"),
+ ("bpy.types.spaceuveditor.use_snap_to_pixels*", "editors/uv_image/uv/editing/layout.html#bpy-types-spaceuveditor-use-snap-to-pixels"),
("bpy.types.linestyle*modifier_curvature_3d*", "render/freestyle/parameter_editor/line_style/modifiers/properties.html#bpy-types-linestyle-modifier-curvature-3d"),
+ ("bpy.types.rendersettings.use_render_cache*", "render/output/output.html#bpy-types-rendersettings-use-render-cache"),
("bpy.ops.object.anim_transforms_to_deltas*", "editors/3dview/object/editing/transform/clear_apply.html#bpy-ops-object-anim-transforms-to-deltas"),
("bpy.types.compositornodeplanetrackdeform*", "compositing/types/distort/plane_track_deform.html#bpy-types-compositornodeplanetrackdeform"),
("bpy.types.gpencilsculptsettings.lockaxis*", "interface/grease_pencil/drawing/introduction.html#bpy-types-gpencilsculptsettings-lockaxis"),
("bpy.types.linestyle*modifier_alongstroke*", "render/freestyle/parameter_editor/line_style/modifiers/properties.html#bpy-types-linestyle-modifier-alongstroke"),
("bpy.types.linestyle*modifier_creaseangle*", "render/freestyle/parameter_editor/line_style/modifiers/properties.html#bpy-types-linestyle-modifier-creaseangle"),
+ ("bpy.types.rendersettings.use_placeholder*", "render/output/output.html#bpy-types-rendersettings-use-placeholder"),
("bpy.types.shadernodesubsurfacescattering*", "render/cycles/nodes/types/shaders/sss.html#bpy-types-shadernodesubsurfacescattering"),
("bpy.types.compositornodecolorcorrection*", "compositing/types/color/color_correction.html#bpy-types-compositornodecolorcorrection"),
("bpy.types.compositornodemoviedistortion*", "compositing/types/distort/movie_distortion.html#bpy-types-compositornodemoviedistortion"),
("bpy.types.ffmpegsettings.audio_channels*", "data_system/scenes/properties.html#bpy-types-ffmpegsettings-audio-channels"),
- ("bpy.types.fmodifierenvelopecontrolpoint*", "editors/graph_editor/fcurves/fmodifiers.html#bpy-types-fmodifierenvelopecontrolpoint"),
- ("bpy.types.spaceuveditor.use_live_unwrap*", "editors/uv_image/uv_editing/layout_editing.html#bpy-types-spaceuveditor-use-live-unwrap"),
+ ("bpy.types.fmodifierenvelopecontrolpoint*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierenvelopecontrolpoint"),
+ ("bpy.types.spaceuveditor.use_live_unwrap*", "editors/uv_image/uv/editing/layout.html#bpy-types-spaceuveditor-use-live-unwrap"),
("bpy.types.vertexweightproximitymodifier*", "modeling/modifiers/modify/weight_proximity.html#bpy-types-vertexweightproximitymodifier"),
("bpy.types.compositornodebrightcontrast*", "compositing/types/color/bright_contrast.html#bpy-types-compositornodebrightcontrast"),
("bpy.types.compositornodedoubleedgemask*", "compositing/types/matte/double_edge_mask.html#bpy-types-compositornodedoubleedgemask"),
@@ -85,6 +90,8 @@ url_manual_mapping = (
("bpy.types.material.preview_render_type*", "render/blender_render/materials/properties/preview.html#bpy-types-material-preview-render-type"),
("bpy.types.materialraytracetransparency*", "render/blender_render/materials/properties/transparency.html#bpy-types-materialraytracetransparency"),
("bpy.types.materialsubsurfacescattering*", "render/blender_render/materials/properties/subsurface_scattering.html#bpy-types-materialsubsurfacescattering"),
+ ("bpy.types.rendersettings.use_overwrite*", "render/output/output.html#bpy-types-rendersettings-use-overwrite"),
+ ("bpy.types.shadernodevectordisplacement*", "render/cycles/nodes/types/vector/vector_displacement.html#bpy-types-shadernodevectordisplacement"),
("bpy.ops.object.visual_transform_apply*", "editors/3dview/object/editing/transform/clear_apply.html#bpy-ops-object-visual-transform-apply"),
("bpy.types.compositornodebilateralblur*", "compositing/types/filter/bilateral_blur.html#bpy-types-compositornodebilateralblur"),
("bpy.types.compositornodedistancematte*", "compositing/types/matte/distance_key.html#bpy-types-compositornodedistancematte"),
@@ -97,12 +104,13 @@ url_manual_mapping = (
("bpy.types.compositornodecolorbalance*", "compositing/types/color/color_balance.html#bpy-types-compositornodecolorbalance"),
("bpy.types.compositornodekeyingscreen*", "compositing/types/matte/keying_screen.html#bpy-types-compositornodekeyingscreen"),
("bpy.types.dynamicpaintcanvassettings*", "physics/dynamic_paint/canvas.html#bpy-types-dynamicpaintcanvassettings"),
- ("bpy.types.fmodifierfunctiongenerator*", "editors/graph_editor/fcurves/fmodifiers.html#bpy-types-fmodifierfunctiongenerator"),
+ ("bpy.types.fmodifierfunctiongenerator*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierfunctiongenerator"),
("bpy.types.linestyle*modifier_tangent*", "render/freestyle/parameter_editor/line_style/modifiers/properties.html#bpy-types-linestyle-modifier-tangent"),
("bpy.types.movietrackingstabilization*", "editors/movie_clip_editor/tracking/clip/properties/stabilization/index.html#bpy-types-movietrackingstabilization"),
("bpy.types.shadernodeambientocclusion*", "render/cycles/nodes/types/shaders/ao.html#bpy-types-shadernodeambientocclusion"),
("bpy.types.shadernodevolumeabsorption*", "render/cycles/nodes/types/shaders/volume_absorption.html#bpy-types-shadernodevolumeabsorption"),
- ("bpy.types.toolsettings.use_uv_sculpt*", "editors/uv_image/uv_editing/uv_sculpt.html#bpy-types-toolsettings-use-uv-sculpt"),
+ ("bpy.types.shadernodevolumeprincipled*", "render/cycles/nodes/types/shaders/volume_principled.html#bpy-types-shadernodevolumeprincipled"),
+ ("bpy.types.toolsettings.use_uv_sculpt*", "editors/uv_image/uv/editing/uv_sculpt.html#bpy-types-toolsettings-use-uv-sculpt"),
("bpy.ops.object.duplicates_make_real*", "editors/3dview/object/editing/transform/clear_apply.html#bpy-ops-object-duplicates-make-real"),
("bpy.ops.object.transforms_to_deltas*", "editors/3dview/object/editing/transform/clear_apply.html#bpy-ops-object-transforms-to-deltas"),
("bpy.types.compositornodechromamatte*", "compositing/types/matte/chroma_key.html#bpy-types-compositornodechromamatte"),
@@ -112,27 +120,29 @@ url_manual_mapping = (
("bpy.types.dynamicpaintbrushsettings*", "physics/dynamic_paint/brush.html#bpy-types-dynamicpaintbrushsettings"),
("bpy.types.object.slow_parent_offset*", "editors/3dview/object/properties/relations/extras.html#bpy-types-object-slow-parent-offset"),
("bpy.types.object.use_dupli_vertices*", "editors/3dview/object/properties/duplication/dupliverts.html#bpy-types-object-use-dupli-vertices"),
+ ("bpy.types.rendersettings.use_border*", "render/output/output.html#bpy-types-rendersettings-use-border"),
("bpy.types.shadernodebsdfanisotropic*", "render/cycles/nodes/types/shaders/anisotropic.html#bpy-types-shadernodebsdfanisotropic"),
("bpy.types.shadernodebsdftranslucent*", "render/cycles/nodes/types/shaders/translucent.html#bpy-types-shadernodebsdftranslucent"),
("bpy.types.shadernodebsdftransparent*", "render/cycles/nodes/types/shaders/transparent.html#bpy-types-shadernodebsdftransparent"),
("bpy.types.shadernodevectortransform*", "render/cycles/nodes/types/vector/transform.html#bpy-types-shadernodevectortransform"),
- ("bpy.types.spaceuveditor.lock_bounds*", "editors/uv_image/uv_editing/layout_editing.html#bpy-types-spaceuveditor-lock-bounds"),
+ ("bpy.types.spaceuveditor.lock_bounds*", "editors/uv_image/uv/editing/layout.html#bpy-types-spaceuveditor-lock-bounds"),
("bpy.ops.object.datalayout_transfer*", "modeling/meshes/editing/data_transfer.html#bpy-ops-object-datalayout-transfer"),
("bpy.ops.object.randomize_transform*", "editors/3dview/object/editing/transform/tools.html#bpy-ops-object-randomize-transform"),
("bpy.types.compositornodebokehimage*", "compositing/types/input/bokeh_image.html#bpy-types-compositornodebokehimage"),
("bpy.types.compositornodecolormatte*", "compositing/types/matte/color_key.html#bpy-types-compositornodecolormatte"),
- ("bpy.types.compositornodecolorspill*", "compositing/types/matte/color_spill_key.html#bpy-types-compositornodecolorspill"),
+ ("bpy.types.compositornodecolorspill*", "compositing/types/matte/color_spill.html#bpy-types-compositornodecolorspill"),
("bpy.types.compositornodehuecorrect*", "compositing/types/color/hue_correct.html#bpy-types-compositornodehuecorrect"),
("bpy.types.compositornodeoutputfile*", "compositing/types/output/file.html#bpy-types-compositornodeoutputfile"),
("bpy.types.compositornodeswitchview*", "compositing/types/converter/switch_view.html#bpy-types-compositornodeswitchview"),
("bpy.types.copytransformsconstraint*", "rigging/constraints/transform/copy_transforms.html#bpy-types-copytransformsconstraint"),
("bpy.types.correctivesmoothmodifier*", "modeling/modifiers/deform/corrective_smooth.html#bpy-types-correctivesmoothmodifier"),
- ("bpy.types.cyclesvisibilitysettings*", "render/cycles/settings/scene/render/light_paths.html#bpy-types-cyclesvisibilitysettings"),
+ ("bpy.types.cyclesvisibilitysettings*", "render/cycles/settings/objects/object_data.html#bpy-types-cyclesvisibilitysettings"),
("bpy.types.linestyle*modifier_noise*", "render/freestyle/parameter_editor/line_style/modifiers/properties.html#bpy-types-linestyle-modifier-noise"),
("bpy.types.maintainvolumeconstraint*", "rigging/constraints/transform/maintain_volume.html#bpy-types-maintainvolumeconstraint"),
("bpy.types.particleinstancemodifier*", "modeling/modifiers/simulate/particle_instance.html#bpy-types-particleinstancemodifier"),
("bpy.types.rigidbodyjointconstraint*", "rigging/constraints/relationship/rigid_body_joint.html#bpy-types-rigidbodyjointconstraint"),
("bpy.types.shadernodebrightcontrast*", "render/cycles/nodes/types/color/bright_contrast.html#bpy-types-shadernodebrightcontrast"),
+ ("bpy.types.shadernodebsdfprincipled*", "render/cycles/nodes/types/shaders/principled.html#bpy-types-shadernodebsdfprincipled"),
("bpy.types.shadernodebsdfrefraction*", "render/cycles/nodes/types/shaders/refraction.html#bpy-types-shadernodebsdfrefraction"),
("bpy.types.shadernodeoutputmaterial*", "render/cycles/nodes/types/output/material.html#bpy-types-shadernodeoutputmaterial"),
("bpy.types.shadernodetexenvironment*", "render/cycles/nodes/types/textures/environment.html#bpy-types-shadernodetexenvironment"),
@@ -164,7 +174,7 @@ url_manual_mapping = (
("bpy.types.shadernodevolumescatter*", "render/cycles/nodes/types/shaders/volume_scatter.html#bpy-types-shadernodevolumescatter"),
("bpy.types.vertexweightmixmodifier*", "modeling/modifiers/modify/weight_mix.html#bpy-types-vertexweightmixmodifier"),
("bpy.ops.object.constraints_clear*", "rigging/constraints/interface/adding_removing.html#bpy-ops-object-constraints-clear"),
- ("bpy.ops.uv.average_islands_scale*", "editors/uv_image/uv_editing/layout_editing.html#bpy-ops-uv-average-islands-scale"),
+ ("bpy.ops.uv.average_islands_scale*", "editors/uv_image/uv/editing/layout.html#bpy-ops-uv-average-islands-scale"),
("bpy.ops.view3d.edit_mesh_extrude*", "modeling/meshes/editing/duplicating/extrude.html#bpy-ops-view3d-edit-mesh-extrude"),
("bpy.types.brightcontrastmodifier*", "editors/vse/sequencer/properties/modifiers.html#bpy-types-brightcontrastmodifier"),
("bpy.types.camerasolverconstraint*", "rigging/constraints/motion_tracking/camera_solver.html#bpy-types-camerasolverconstraint"),
@@ -172,7 +182,7 @@ url_manual_mapping = (
("bpy.types.compositornodecurvergb*", "compositing/types/color/rgb_curves.html#bpy-types-compositornodecurvergb"),
("bpy.types.compositornodecurvevec*", "compositing/types/vector/vector_curves.html#bpy-types-compositornodecurvevec"),
("bpy.types.compositornodedisplace*", "compositing/types/distort/displace.html#bpy-types-compositornodedisplace"),
- ("bpy.types.compositornodelensdist*", "compositing/types/distort/lens.html#bpy-types-compositornodelensdist"),
+ ("bpy.types.compositornodelensdist*", "compositing/types/distort/lens_distortion.html#bpy-types-compositornodelensdist"),
("bpy.types.compositornodemaprange*", "compositing/types/vector/map_range.html#bpy-types-compositornodemaprange"),
("bpy.types.compositornodemapvalue*", "compositing/types/vector/map_value.html#bpy-types-compositornodemapvalue"),
("bpy.types.compositornodepixelate*", "compositing/types/filter/pixelate.html#bpy-types-compositornodepixelate"),
@@ -188,6 +198,7 @@ url_manual_mapping = (
("bpy.types.object.use_slow_parent*", "editors/3dview/object/properties/relations/extras.html#bpy-types-object-use-slow-parent"),
("bpy.types.objectsolverconstraint*", "rigging/constraints/motion_tracking/object_solver.html#bpy-types-objectsolverconstraint"),
("bpy.types.particlesystemmodifier*", "physics/particles/index.html#bpy-types-particlesystemmodifier"),
+ ("bpy.types.shadernodedisplacement*", "render/cycles/nodes/types/vector/displacement.html#bpy-types-shadernodedisplacement"),
("bpy.types.shadernodelightfalloff*", "render/cycles/nodes/types/color/light_falloff.html#bpy-types-shadernodelightfalloff"),
("bpy.types.shadernodeparticleinfo*", "render/cycles/nodes/types/input/particle_info.html#bpy-types-shadernodeparticleinfo"),
("bpy.ops.object.constraints_copy*", "rigging/constraints/interface/adding_removing.html#bpy-ops-object-constraints-copy"),
@@ -271,8 +282,8 @@ url_manual_mapping = (
("bpy.ops.object.select_by_type*", "editors/3dview/object/selecting/tools.html#bpy-ops-object-select-by-type"),
("bpy.ops.object.select_grouped*", "editors/3dview/object/selecting/tools.html#bpy-ops-object-select-grouped"),
("bpy.ops.object.select_pattern*", "editors/3dview/object/selecting/tools.html#bpy-ops-object-select-pattern"),
- ("bpy.ops.screen.repeat_history*", "interface/undo_and_redo.html#bpy-ops-screen-repeat-history"),
- ("bpy.ops.uv.seams_from_islands*", "editors/uv_image/uv_editing/unwrapping/seams.html#bpy-ops-uv-seams-from-islands"),
+ ("bpy.ops.screen.repeat_history*", "interface/undo_redo.html#bpy-ops-screen-repeat-history"),
+ ("bpy.ops.uv.seams_from_islands*", "editors/uv_image/uv/editing/unwrapping/seams.html#bpy-ops-uv-seams-from-islands"),
("bpy.types.compositornodedblur*", "compositing/types/filter/directional_blur.html#bpy-types-compositornodedblur"),
("bpy.types.compositornodegamma*", "compositing/types/color/gamma.html#bpy-types-compositornodegamma"),
("bpy.types.compositornodeglare*", "compositing/types/filter/glare.html#bpy-types-compositornodeglare"),
@@ -290,7 +301,6 @@ url_manual_mapping = (
("bpy.types.movietrackingcamera*", "editors/movie_clip_editor/tracking/clip/properties/camera_data.html#bpy-types-movietrackingcamera"),
("bpy.types.object.dupli_frames*", "editors/3dview/object/properties/duplication/dupliframes.html#bpy-types-object-dupli-frames"),
("bpy.types.particledupliweight*", "physics/particles/emitter/vertex_groups.html#bpy-types-particledupliweight"),
- ("bpy.types.pointdensitytexture*", "render/blender_render/textures/types/volume/point_density.html#bpy-types-pointdensitytexture"),
("bpy.types.poseboneconstraints*", "rigging/armatures/posing/bone_constraints/index.html#bpy-types-poseboneconstraints"),
("bpy.types.rigidbodyconstraint*", "physics/rigid_body/constraints/index.html#bpy-types-rigidbodyconstraint"),
("bpy.types.shadernodeaddshader*", "render/cycles/nodes/types/shaders/add.html#bpy-types-shadernodeaddshader"),
@@ -334,7 +344,7 @@ url_manual_mapping = (
("bpy.types.compositornodemath*", "compositing/types/converter/math.html#bpy-types-compositornodemath"),
("bpy.types.compositornodetime*", "compositing/types/input/time.html#bpy-types-compositornodetime"),
("bpy.types.fluidfluidsettings*", "physics/fluid/types/fluid_object.html#bpy-types-fluidfluidsettings"),
- ("bpy.types.fmodifiergenerator*", "editors/graph_editor/fcurves/fmodifiers.html#bpy-types-fmodifiergenerator"),
+ ("bpy.types.fmodifiergenerator*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiergenerator"),
("bpy.types.freestylelinestyle*", "render/freestyle/parameter_editor/line_style/index.html#bpy-types-freestylelinestyle"),
("bpy.types.gammacrosssequence*", "editors/vse/sequencer/strips/effects/cross.html#bpy-types-gammacrosssequence"),
("bpy.types.huecorrectmodifier*", "editors/vse/sequencer/properties/modifiers.html#bpy-types-huecorrectmodifier"),
@@ -363,11 +373,11 @@ url_manual_mapping = (
("bpy.ops.curve.primitive*add*", "modeling/curves/primitives.html#bpy-ops-curve-primitive-add"),
("bpy.ops.mesh.duplicate_move*", "modeling/meshes/editing/duplicating/duplicate.html#bpy-ops-mesh-duplicate-move"),
("bpy.ops.object.parent_clear*", "editors/3dview/object/properties/relations/parents.html#bpy-ops-object-parent-clear"),
- ("bpy.ops.object.shade_smooth*", "modeling/meshes/editing/smoothing.html#bpy-ops-object-shade-smooth"),
+ ("bpy.ops.object.shade_smooth*", "modeling/meshes/editing/normals.html#bpy-ops-object-shade-smooth"),
("bpy.ops.transform.push_pull*", "modeling/meshes/editing/transform/push_pull.html#bpy-ops-transform-push-pull"),
("bpy.ops.transform.transform*", "editors/3dview/object/editing/transform/control/orientations.html#bpy-ops-transform-transform"),
("bpy.ops.transform.translate*", "editors/3dview/object/editing/transform/basics.html#bpy-ops-transform-translate"),
- ("bpy.ops.uv.minimize_stretch*", "editors/uv_image/uv_editing/layout_editing.html#bpy-ops-uv-minimize-stretch"),
+ ("bpy.ops.uv.minimize_stretch*", "editors/uv_image/uv/editing/layout.html#bpy-ops-uv-minimize-stretch"),
("bpy.ops.view3d.select_lasso*", "editors/3dview/object/selecting/tools.html#bpy-ops-view3d-select-lasso"),
("bpy.types.alphaoversequence*", "editors/vse/sequencer/strips/effects/alpha_over_under_overdrop.html#bpy-types-alphaoversequence"),
("bpy.types.armatureeditbones*", "rigging/armatures/bones/editing/index.html#bpy-types-armatureeditbones"),
@@ -378,8 +388,8 @@ url_manual_mapping = (
("bpy.types.compositornodergb*", "compositing/types/input/rgb.html#bpy-types-compositornodergb"),
("bpy.types.compositornodesep*", "render/blender_render/textures/nodes/types/color/combine_separate.html#bpy-types-compositornodesep"),
("bpy.types.edgesplitmodifier*", "modeling/modifiers/generate/edge_split.html#bpy-types-edgesplitmodifier"),
- ("bpy.types.fmodifierenvelope*", "editors/graph_editor/fcurves/fmodifiers.html#bpy-types-fmodifierenvelope"),
- ("bpy.types.freestylesettings*", "render/freestyle/viewmap.html#bpy-types-freestylesettings"),
+ ("bpy.types.fmodifierenvelope*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierenvelope"),
+ ("bpy.types.freestylesettings*", "render/freestyle/view_map.html#bpy-types-freestylesettings"),
("bpy.types.material.specular*", "render/blender_render/materials/properties/specular_shaders.html#bpy-types-material-specular"),
("bpy.types.meshcachemodifier*", "modeling/modifiers/modify/mesh_cache.html#bpy-types-meshcachemodifier"),
("bpy.types.movieclipsequence*", "editors/vse/sequencer/strips/clip_mask.html#bpy-types-movieclipsequence"),
@@ -394,7 +404,7 @@ url_manual_mapping = (
("bpy.types.shadernodergbtobw*", "render/cycles/nodes/types/converter/rgb_to_bw.html#bpy-types-shadernodergbtobw"),
("bpy.types.shadernodetangent*", "render/cycles/nodes/types/input/tangent.html#bpy-types-shadernodetangent"),
("bpy.types.shadernodetexwave*", "render/cycles/nodes/types/textures/wave.html#bpy-types-shadernodetexwave"),
- ("bpy.types.smokecollsettings*", "physics/smoke/types/collisions.html#bpy-types-smokecollsettings"),
+ ("bpy.types.smokecollsettings*", "physics/smoke/types/collision.html#bpy-types-smokecollsettings"),
("bpy.types.smokeflowsettings*", "physics/smoke/types/flow_object.html#bpy-types-smokeflowsettings"),
("bpy.types.texturenodebricks*", "render/blender_render/textures/nodes/types/patterns/bricks.html#bpy-types-texturenodebricks"),
("bpy.types.texturenodemixrgb*", "render/blender_render/textures/nodes/types/color/mix_rgb.html#bpy-types-texturenodemixrgb"),
@@ -404,20 +414,21 @@ url_manual_mapping = (
("bpy.types.uvprojectmodifier*", "modeling/modifiers/modify/uv_project.html#bpy-types-uvprojectmodifier"),
("bpy.types.wireframemodifier*", "modeling/modifiers/generate/wireframe.html#bpy-types-wireframemodifier"),
("bpy.types.worldmistsettings*", "render/blender_render/world/mist.html#bpy-types-worldmistsettings"),
- ("bpy.ops.mesh.loopcut_slide*", "modeling/meshes/editing/subdividing/loop_subdivide.html#bpy-ops-mesh-loopcut-slide"),
+ ("bpy.ops.mesh.loopcut_slide*", "modeling/meshes/editing/subdividing/loop.html#bpy-ops-mesh-loopcut-slide"),
("bpy.ops.mesh.primitive*add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-add"),
("bpy.ops.object.join_shapes*", "animation/shape_keys/shape_keys_panel.html#bpy-ops-object-join-shapes"),
("bpy.ops.object.select_less*", "editors/3dview/object/selecting/tools.html#bpy-ops-object-select-less"),
("bpy.ops.object.select_more*", "editors/3dview/object/selecting/tools.html#bpy-ops-object-select-more"),
("bpy.ops.object.track_clear*", "rigging/constraints/interface/adding_removing.html#bpy-ops-object-track-clear"),
- ("bpy.ops.screen.repeat_last*", "interface/undo_and_redo.html#bpy-ops-screen-repeat-last"),
+ ("bpy.ops.screen.repeat_last*", "interface/undo_redo.html#bpy-ops-screen-repeat-last"),
("bpy.ops.transform.tosphere*", "modeling/meshes/editing/transform/to_sphere.html#bpy-ops-transform-tosphere"),
("bpy.types.actionconstraint*", "rigging/constraints/relationship/action.html#bpy-types-actionconstraint"),
("bpy.types.addonpreferences*", "preferences/addons.html#bpy-types-addonpreferences"),
("bpy.types.armaturemodifier*", "modeling/modifiers/deform/armature.html#bpy-types-armaturemodifier"),
+ ("bpy.types.decimatemodifier*", "modeling/modifiers/generate/decimate.html#bpy-types-decimatemodifier"),
("bpy.types.displacemodifier*", "modeling/modifiers/deform/displace.html#bpy-types-displacemodifier"),
("bpy.types.displaysafeareas*", "render/blender_render/camera/object_data.html#bpy-types-displaysafeareas"),
- ("bpy.types.fmodifierstepped*", "editors/graph_editor/fcurves/fmodifiers.html#bpy-types-fmodifierstepped"),
+ ("bpy.types.fmodifierstepped*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierstepped"),
("bpy.types.freestylelineset*", "render/freestyle/parameter_editor/line_set.html#bpy-types-freestylelineset"),
("bpy.types.material.ambient*", "render/blender_render/materials/properties/shading.html#bpy-types-material-ambient"),
("bpy.types.material.diffuse*", "render/blender_render/materials/properties/diffuse_shaders.html#bpy-types-material-diffuse"),
@@ -436,9 +447,11 @@ url_manual_mapping = (
("bpy.types.shadernodescript*", "render/cycles/nodes/types/script.html#bpy-types-shadernodescript"),
("bpy.types.shadernodetexsky*", "render/cycles/nodes/types/textures/sky.html#bpy-types-shadernodetexsky"),
("bpy.types.softbodymodifier*", "physics/soft_body/index.html#bpy-types-softbodymodifier"),
+ ("bpy.types.softbodysettings*", "physics/soft_body/settings.html#bpy-types-softbodysettings"),
("bpy.types.solidifymodifier*", "modeling/modifiers/generate/solidify.html#bpy-types-solidifymodifier"),
("bpy.types.spacefilebrowser*", "editors/file_browser/index.html#bpy-types-spacefilebrowser"),
("bpy.types.spacegrapheditor*", "editors/graph_editor/index.html#bpy-types-spacegrapheditor"),
+ ("bpy.types.spaceimageeditor*", "editors/uv_image/image/index.html#bpy-types-spaceimageeditor"),
("bpy.types.spacelogiceditor*", "editors/logic_editor.html#bpy-types-spacelogiceditor"),
("bpy.types.sphfluidsettings*", "physics/fluid/index.html#bpy-types-sphfluidsettings"),
("bpy.types.subtractsequence*", "editors/vse/sequencer/strips/effects/subtract.html#bpy-types-subtractsequence"),
@@ -454,18 +467,18 @@ url_manual_mapping = (
("bpy.ops.object.parent_set*", "editors/3dview/object/properties/relations/parents.html#bpy-ops-object-parent-set"),
("bpy.ops.object.proxy_make*", "data_system/linked_libraries.html#bpy-ops-object-proxy-make"),
("bpy.ops.object.select_all*", "editors/3dview/object/selecting/tools.html#bpy-ops-object-select-all"),
- ("bpy.ops.object.shade_flat*", "modeling/meshes/editing/smoothing.html#bpy-ops-object-shade-flat"),
+ ("bpy.ops.object.shade_flat*", "modeling/meshes/editing/normals.html#bpy-ops-object-shade-flat"),
("bpy.ops.screen.area_dupli*", "interface/window_system/areas.html#bpy-ops-screen-area-dupli"),
- ("bpy.ops.uv.remove_doubles*", "editors/uv_image/uv_editing/layout_editing.html#bpy-ops-uv-remove-doubles"),
+ ("bpy.ops.uv.remove_doubles*", "editors/uv_image/uv/editing/layout.html#bpy-ops-uv-remove-doubles"),
("bpy.types.backgroundimage*", "editors/3dview/properties/background_images.html#bpy-types-backgroundimage"),
("bpy.types.booleanmodifier*", "modeling/modifiers/generate/booleans.html#bpy-types-booleanmodifier"),
("bpy.types.constraint.mute*", "rigging/constraints/interface/header.html#bpy-types-constraint-mute"),
("bpy.types.explodemodifier*", "modeling/modifiers/simulate/explode.html#bpy-types-explodemodifier"),
- ("bpy.types.fcurvemodifiers*", "editors/graph_editor/fcurves/fmodifiers.html#bpy-types-fcurvemodifiers"),
+ ("bpy.types.fcurvemodifiers*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fcurvemodifiers"),
("bpy.types.floorconstraint*", "rigging/constraints/relationship/floor.html#bpy-types-floorconstraint"),
("bpy.types.fluidmeshvertex*", "physics/fluid/index.html#bpy-types-fluidmeshvertex"),
- ("bpy.types.fmodifiercycles*", "editors/graph_editor/fcurves/fmodifiers.html#bpy-types-fmodifiercycles"),
- ("bpy.types.fmodifierlimits*", "editors/graph_editor/fcurves/fmodifiers.html#bpy-types-fmodifierlimits"),
+ ("bpy.types.fmodifiercycles*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiercycles"),
+ ("bpy.types.fmodifierlimits*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierlimits"),
("bpy.types.gpussaosettings*", "editors/3dview/properties/shading.html#bpy-types-gpussaosettings"),
("bpy.types.latticemodifier*", "modeling/modifiers/deform/lattice.html#bpy-types-latticemodifier"),
("bpy.types.musgravetexture*", "render/blender_render/textures/types/procedural/musgrave.html#bpy-types-musgravetexture"),
@@ -474,6 +487,7 @@ url_manual_mapping = (
("bpy.types.particlehairkey*", "physics/particles/emitter/physics/keyed.html#bpy-types-particlehairkey"),
("bpy.types.pivotconstraint*", "rigging/constraints/relationship/pivot.html#bpy-types-pivotconstraint"),
("bpy.types.rigidbodyobject*", "physics/rigid_body/index.html#bpy-types-rigidbodyobject"),
+ ("bpy.types.shadernodebevel*", "render/cycles/nodes/types/input/bevel.html#bpy-types-shadernodebevel"),
("bpy.types.shadernodegamma*", "render/cycles/nodes/types/color/gamma.html#bpy-types-shadernodegamma"),
("bpy.types.shadernodegroup*", "render/cycles/nodes/types/groups.html#bpy-types-shadernodegroup"),
("bpy.types.shadernodeuvmap*", "render/cycles/nodes/types/input/uv_map.html#bpy-types-shadernodeuvmap"),
@@ -494,7 +508,7 @@ url_manual_mapping = (
("bpy.types.curvesmodifier*", "editors/vse/sequencer/properties/modifiers.html#bpy-types-curvesmodifier"),
("bpy.types.effectsequence*", "editors/vse/sequencer/properties/filter.html#bpy-types-effectsequence"),
("bpy.types.ffmpegsettings*", "render/output/video.html#bpy-types-ffmpegsettings"),
- ("bpy.types.fmodifiernoise*", "editors/graph_editor/fcurves/fmodifiers.html#bpy-types-fmodifiernoise"),
+ ("bpy.types.fmodifiernoise*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiernoise"),
("bpy.types.gpudofsettings*", "editors/3dview/properties/shading.html#bpy-types-gpudofsettings"),
("bpy.types.materialstrand*", "render/blender_render/materials/properties/strands.html#bpy-types-materialstrand"),
("bpy.types.materialvolume*", "render/blender_render/materials/special_effects/volume.html#bpy-types-materialvolume"),
@@ -515,12 +529,12 @@ url_manual_mapping = (
("bpy.types.voronoitexture*", "render/blender_render/textures/types/procedural/voronoi.html#bpy-types-voronoitexture"),
("bpy.types.walknavigation*", "editors/3dview/navigate/walk_fly.html#bpy-types-walknavigation"),
("bpy.ops.anim.keying_set*", "animation/keyframes/keying_sets.html#bpy-ops-anim-keying-set"),
- ("bpy.ops.ed.undo_history*", "interface/undo_and_redo.html#bpy-ops-ed-undo-history"),
+ ("bpy.ops.ed.undo_history*", "interface/undo_redo.html#bpy-ops-ed-undo-history"),
("bpy.ops.gpencil.convert*", "interface/grease_pencil/convert_to_geometry.html#bpy-ops-gpencil-convert"),
("bpy.ops.object.armature*", "rigging/armatures/index.html#bpy-ops-object-armature"),
("bpy.ops.rigidbody.world*", "physics/rigid_body/world.html#bpy-ops-rigidbody-world"),
("bpy.ops.transform.shear*", "modeling/meshes/editing/transform/shear.html#bpy-ops-transform-shear"),
- ("bpy.ops.uv.pack_islands*", "editors/uv_image/uv_editing/layout_editing.html#bpy-ops-uv-pack-islands"),
+ ("bpy.ops.uv.pack_islands*", "editors/uv_image/uv/editing/layout.html#bpy-ops-uv-pack-islands"),
("bpy.types.armaturebones*", "rigging/armatures/bones/index.html#bpy-types-armaturebones"),
("bpy.types.arraymodifier*", "modeling/modifiers/generate/array.html#bpy-types-arraymodifier"),
("bpy.types.bevelmodifier*", "modeling/modifiers/generate/bevel.html#bpy-types-bevelmodifier"),
@@ -534,10 +548,10 @@ url_manual_mapping = (
("bpy.types.fieldsettings*", "physics/force_fields/index.html#bpy-types-fieldsettings"),
("bpy.types.fluidsettings*", "physics/fluid/index.html#bpy-types-fluidsettings"),
("bpy.types.gpufxsettings*", "editors/3dview/properties/shading.html#bpy-types-gpufxsettings"),
- ("bpy.types.imagesequence*", "editors/vse/sequencer/strips/image_movie.html#bpy-types-imagesequence"),
+ ("bpy.types.imagesequence*", "editors/vse/sequencer/strips/movie_image.html#bpy-types-imagesequence"),
("bpy.types.marbletexture*", "render/blender_render/textures/types/procedural/marble.html#bpy-types-marbletexture"),
("bpy.types.modifier.show*", "modeling/modifiers/introduction.html#bpy-types-modifier-show"),
- ("bpy.types.moviesequence*", "editors/vse/sequencer/strips/image_movie.html#bpy-types-moviesequence"),
+ ("bpy.types.moviesequence*", "editors/vse/sequencer/strips/movie_image.html#bpy-types-moviesequence"),
("bpy.types.movietracking*", "editors/movie_clip_editor/tracking/index.html#bpy-types-movietracking"),
("bpy.types.object.layers*", "editors/3dview/object/properties/relations/layers.html#bpy-types-object-layers"),
("bpy.types.object.parent*", "editors/3dview/object/properties/relations/parents.html#bpy-types-object-parent"),
@@ -549,10 +563,10 @@ url_manual_mapping = (
("bpy.types.sequenceproxy*", "editors/vse/sequencer/properties/proxy_timecode.html#bpy-types-sequenceproxy"),
("bpy.types.shadernodergb*", "render/cycles/nodes/types/input/rgb.html#bpy-types-shadernodergb"),
("bpy.types.smokemodifier*", "physics/smoke/index.html#bpy-types-smokemodifier"),
- ("bpy.types.soundsequence*", "editors/vse/sequencer/strips/audio.html#bpy-types-soundsequence"),
+ ("bpy.types.soundsequence*", "editors/vse/sequencer/strips/sound.html#bpy-types-soundsequence"),
("bpy.types.spaceoutliner*", "editors/outliner.html#bpy-types-spaceoutliner"),
("bpy.types.spacetimeline*", "editors/timeline.html#bpy-types-spacetimeline"),
- ("bpy.types.spaceuveditor*", "editors/uv_image/uv_editing/index.html#bpy-types-spaceuveditor"),
+ ("bpy.types.spaceuveditor*", "editors/uv_image/uv/index.html#bpy-types-spaceuveditor"),
("bpy.types.stuccitexture*", "render/blender_render/textures/types/procedural/stucci.html#bpy-types-stuccitexture"),
("bpy.types.windowmanager*", "interface/index.html#bpy-types-windowmanager"),
("bpy.types.worldlighting*", "render/blender_render/world/ambient_occlusion.html#bpy-types-worldlighting"),
@@ -613,8 +627,8 @@ url_manual_mapping = (
("bpy.ops.object.align*", "editors/3dview/object/editing/transform/tools.html#bpy-ops-object-align"),
("bpy.ops.object.empty*", "modeling/empties.html#bpy-ops-object-empty"),
("bpy.ops.object.quick*", "physics/introduction.html#bpy-ops-object-quick"),
- ("bpy.ops.uv.mark_seam*", "editors/uv_image/uv_editing/unwrapping/seams.html#bpy-ops-uv-mark-seam"),
- ("bpy.ops.view3d.ruler*", "interface/ruler_and_protractor.html#bpy-ops-view3d-ruler"),
+ ("bpy.ops.uv.mark_seam*", "editors/uv_image/uv/editing/unwrapping/seams.html#bpy-ops-uv-mark-seam"),
+ ("bpy.ops.view3d.ruler*", "interface/ruler_protractor.html#bpy-ops-view3d-ruler"),
("bpy.types.areaspaces*", "interface/window_system/areas.html#bpy-types-areaspaces"),
("bpy.types.bpy_struct*", "data_system/custom_properties.html#bpy-types-bpy-struct"),
("bpy.types.compositor*", "compositing/index.html#bpy-types-compositor"),
@@ -629,10 +643,11 @@ url_manual_mapping = (
("bpy.ops.object.join*", "editors/3dview/object/editing/introduction.html#bpy-ops-object-join"),
("bpy.ops.object.text*", "modeling/texts/index.html#bpy-ops-object-text"),
("bpy.ops.view3d.snap*", "editors/3dview/object/editing/transform/control/snap.html#bpy-ops-view3d-snap"),
+ ("bpy.ops.view3d.view*", "editors/3dview/navigate/introduction.html#bpy-ops-view3d-view"),
("bpy.types.blenddata*", "data_system/data_blocks.html#bpy-types-blenddata"),
("bpy.types.colorramp*", "interface/controls/templates/color_ramp.html#bpy-types-colorramp"),
("bpy.types.dopesheet*", "editors/dope_sheet/index.html#bpy-types-dopesheet"),
- ("bpy.types.fmodifier*", "editors/graph_editor/fcurves/fmodifiers.html#bpy-types-fmodifier"),
+ ("bpy.types.fmodifier*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifier"),
("bpy.types.freestyle*", "render/freestyle/index.html#bpy-types-freestyle"),
("bpy.types.movieclip*", "editors/movie_clip_editor/index.html#bpy-types-movieclip"),
("bpy.types.nodeframe*", "editors/node_editor/nodes/frame.html#bpy-types-nodeframe"),
@@ -643,7 +658,7 @@ url_manual_mapping = (
("bpy.types.uipiemenu*", "interface/controls/buttons/menus.html#bpy-types-uipiemenu"),
("bpy.ops.constraint*", "rigging/constraints/index.html#bpy-ops-constraint"),
("bpy.ops.curve.draw*", "modeling/curves/editing/draw.html#bpy-ops-curve-draw"),
- ("bpy.ops.mesh.knife*", "modeling/meshes/editing/subdividing/knife_subdivide.html#bpy-ops-mesh-knife"),
+ ("bpy.ops.mesh.knife*", "modeling/meshes/editing/subdividing/knife.html#bpy-ops-mesh-knife"),
("bpy.ops.mesh.noise*", "modeling/meshes/editing/transform/noise.html#bpy-ops-mesh-noise"),
("bpy.ops.mesh.screw*", "modeling/meshes/editing/duplicating/screw.html#bpy-ops-mesh-screw"),
("bpy.ops.safe_areas*", "render/blender_render/camera/object_data.html#bpy-ops-safe-areas"),
@@ -669,8 +684,8 @@ url_manual_mapping = (
("bpy.ops.rigidbody*", "physics/rigid_body/index.html#bpy-ops-rigidbody"),
("bpy.ops.sequencer*", "editors/vse/index.html#bpy-ops-sequencer"),
("bpy.ops.transform*", "editors/3dview/object/editing/transform/index.html#bpy-ops-transform"),
- ("bpy.ops.uv.stitch*", "editors/uv_image/uv_editing/layout_editing.html#bpy-ops-uv-stitch"),
- ("bpy.ops.uv.unwrap*", "editors/uv_image/uv_editing/unwrapping/mapping_types.html#bpy-ops-uv-unwrap"),
+ ("bpy.ops.uv.stitch*", "editors/uv_image/uv/editing/layout.html#bpy-ops-uv-stitch"),
+ ("bpy.ops.uv.unwrap*", "editors/uv_image/uv/editing/unwrapping/mapping_types.html#bpy-ops-uv-unwrap"),
("bpy.types.animviz*", "animation/motion_paths.html#bpy-types-animviz"),
("bpy.types.lattice*", "rigging/lattice.html#bpy-types-lattice"),
("bpy.types.library*", "data_system/linked_libraries.html#bpy-types-library"),
@@ -683,7 +698,7 @@ url_manual_mapping = (
("bpy.ops.nla.bake*", "animation/actions.html#bpy-ops-nla-bake"),
("bpy.ops.outliner*", "editors/outliner.html#bpy-ops-outliner"),
("bpy.ops.particle*", "physics/particles/index.html#bpy-ops-particle"),
- ("bpy.ops.uv.align*", "editors/uv_image/uv_editing/layout_editing.html#bpy-ops-uv-align"),
+ ("bpy.ops.uv.align*", "editors/uv_image/uv/editing/layout.html#bpy-ops-uv-align"),
("bpy.ops.wm.addon*", "preferences/addons.html#bpy-ops-wm-addon"),
("bpy.types.action*", "animation/actions.html#bpy-types-action"),
("bpy.types.camera*", "render/blender_render/camera/index.html#bpy-types-camera"),
@@ -700,15 +715,15 @@ url_manual_mapping = (
("bpy.types.window*", "interface/index.html#bpy-types-window"),
("bpy.ops.buttons*", "interface/index.html#bpy-ops-buttons"),
("bpy.ops.console*", "editors/python_console.html#bpy-ops-console"),
- ("bpy.ops.ed.redo*", "interface/undo_and_redo.html#bpy-ops-ed-redo"),
- ("bpy.ops.ed.undo*", "interface/undo_and_redo.html#bpy-ops-ed-undo"),
+ ("bpy.ops.ed.redo*", "interface/undo_redo.html#bpy-ops-ed-redo"),
+ ("bpy.ops.ed.undo*", "interface/undo_redo.html#bpy-ops-ed-undo"),
("bpy.ops.gpencil*", "interface/grease_pencil/index.html#bpy-ops-gpencil"),
("bpy.ops.lattice*", "rigging/lattice.html#bpy-ops-lattice"),
("bpy.ops.poselib*", "rigging/armatures/properties/pose_library.html#bpy-ops-poselib"),
("bpy.ops.ptcache*", "physics/baking.html#bpy-ops-ptcache"),
("bpy.ops.surface*", "modeling/surfaces/index.html#bpy-ops-surface"),
("bpy.ops.texture*", "render/blender_render/textures/index.html#bpy-ops-texture"),
- ("bpy.ops.uv.weld*", "editors/uv_image/uv_editing/layout_editing.html#bpy-ops-uv-weld"),
+ ("bpy.ops.uv.weld*", "editors/uv_image/uv/editing/layout.html#bpy-ops-uv-weld"),
("bpy.types.addon*", "preferences/addons.html#bpy-types-addon"),
("bpy.types.brush*", "sculpt_paint/brush.html#bpy-types-brush"),
("bpy.types.curve*", "modeling/curves/index.html#bpy-types-curve"),
@@ -733,8 +748,8 @@ url_manual_mapping = (
("bpy.ops.screen*", "interface/window_system/screens.html#bpy-ops-screen"),
("bpy.ops.script*", "advanced/scripting/index.html#bpy-ops-script"),
("bpy.ops.sculpt*", "sculpt_paint/sculpting/index.html#bpy-ops-sculpt"),
- ("bpy.ops.sketch*", "rigging/armatures/bones/editing/sketching.html#bpy-ops-sketch"),
- ("bpy.ops.uv.pin*", "editors/uv_image/uv_editing/layout_editing.html#bpy-ops-uv-pin"),
+ ("bpy.ops.sketch*", "rigging/armatures/bones/editing/sketching/index.html#bpy-ops-sketch"),
+ ("bpy.ops.uv.pin*", "editors/uv_image/uv/editing/layout.html#bpy-ops-uv-pin"),
("bpy.ops.view3d*", "editors/3dview/index.html#bpy-ops-view3d"),
("bpy.types.area*", "interface/window_system/areas.html#bpy-types-area"),
("bpy.types.boid*", "physics/particles/emitter/physics/boids.html#bpy-types-boid"),
@@ -773,9 +788,9 @@ url_manual_mapping = (
("bpy.ops.time*", "editors/timeline.html#bpy-ops-time"),
("bpy.types.id*", "data_system/data_blocks.html#bpy-types-id"),
("bpy.ops.nla*", "editors/nla/index.html#bpy-ops-nla"),
- ("bpy.ops.ed*", "interface/undo_and_redo.html#bpy-ops-ed"),
+ ("bpy.ops.ed*", "interface/undo_redo.html#bpy-ops-ed"),
("bpy.ops.ui*", "interface/index.html#bpy-ops-ui"),
- ("bpy.ops.uv*", "editors/uv_image/uv_editing/index.html#bpy-ops-uv"),
+ ("bpy.ops.uv*", "editors/uv_image/uv/index.html#bpy-ops-uv"),
("bpy.ops.wm*", "interface/index.html#bpy-ops-wm"),
("bpy.types*", "index.html#bpy-types"),
("bpy.ops*", "index.html#bpy-ops"),
diff --git a/netrender/__init__.py b/netrender/__init__.py
index 4186f8b9..aae33c11 100644
--- a/netrender/__init__.py
+++ b/netrender/__init__.py
@@ -21,13 +21,13 @@
bl_info = {
"name": "Network Renderer",
"author": "Martin Poirier",
- "version": (1, 8),
+ "version": (1, 8, 1),
"blender": (2, 60, 0),
"location": "Render > Engine > Network Render",
"description": "Distributed rendering for Blender",
"warning": "Stable but still work in progress",
- "wiki_url": "http://wiki.blender.org/index.php/Doc:2.5/Manual/"
- "Render/Engines/Netrender",
+ "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
+ "Scripts/Render/Net_render",
"category": "Render",
}
diff --git a/node_wrangler.py b/node_wrangler.py
index 9b225af1..24e096c8 100644
--- a/node_wrangler.py
+++ b/node_wrangler.py
@@ -3161,7 +3161,7 @@ class NWSelectParentChildren(Operator, NWBase):
class NWDetachOutputs(Operator, NWBase):
- """Detach outputs of selected node leaving inluts liked"""
+ """Detach outputs of selected node leaving inputs linked"""
bl_idname = "node.nw_detach_outputs"
bl_label = "Detach Outputs"
bl_options = {'REGISTER', 'UNDO'}
diff --git a/object_boolean_tools.py b/object_boolean_tools.py
index 3c5b104b..df082abc 100644
--- a/object_boolean_tools.py
+++ b/object_boolean_tools.py
@@ -21,8 +21,8 @@
bl_info = {
"name": "Bool Tool",
"author": "Vitor Balbio, Mikhail Rachinskiy, TynkaTopi, Meta-Androcto",
- "version": (0, 3, 8),
- "blender": (2, 78, 0),
+ "version": (0, 3, 9),
+ "blender": (2, 79, 2),
"location": "View3D > Toolshelf",
"description": "Bool Tool Hotkey: Ctrl Shift B",
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
@@ -143,7 +143,6 @@ def Operation(context, _operation):
prefs = bpy.context.user_preferences.addons[__name__].preferences
useWire = prefs.use_wire
- solver = prefs.solver
for selObj in bpy.context.selected_objects:
if selObj != context.active_object and (selObj.type == "MESH" or selObj.type == "CURVE"):
@@ -180,7 +179,6 @@ def Operation(context, _operation):
clone["BoolToolRoot"] = True
newMod = actObj.modifiers.new("BTool_" + selObj.name, "BOOLEAN")
newMod.object = selObj
- newMod.solver = solver
if _operation == "SLICE":
newMod.operation = "INTERSECT"
else:
@@ -615,19 +613,6 @@ class BTool_Slice(Operator):
class Auto_Boolean:
- solver = EnumProperty(
- name="Boolean Solver",
- description="Specify solver for boolean operation",
- items=(('BMESH', "BMesh", "BMesh solver is faster, but less stable "
- "and cannot handle coplanar geometry"),
- ('CARVE', "Carve", "Carve solver is slower, but more stable "
- "and can handle simple cases of coplanar geometry")),
- options={'SKIP_SAVE'},
- )
-
- def __init__(self):
- self.solver = bpy.context.user_preferences.addons[__name__].preferences.solver
-
def objects_prepare(self):
for ob in bpy.context.selected_objects:
if ob.type != 'MESH':
@@ -663,7 +648,6 @@ class Auto_Boolean:
md = obj.modifiers.new("Auto Boolean", 'BOOLEAN')
md.show_viewport = False
md.operation = mode
- md.solver = self.solver
md.object = ob
bpy.ops.object.modifier_apply(modifier="Auto Boolean")
@@ -1288,15 +1272,6 @@ class PREFS_BoolTool_Props(AddonPreferences):
default="Tools",
update=update_panels,
)
- solver = EnumProperty(
- name="Boolean Solver",
- items=(('BMESH', "BMesh", "BMesh solver is faster, but less stable "
- "and cannot handle coplanar geometry"),
- ('CARVE', "Carve", "Carve solver is slower, but more stable "
- "and can handle simple cases of coplanar geometry")),
- default='BMESH',
- description="Specify solver for boolean operations",
- )
Enable_Tab_01 = BoolProperty(
default=False
)
@@ -1309,29 +1284,23 @@ class PREFS_BoolTool_Props(AddonPreferences):
col = split.column()
col.label(text="Tab Category:")
col = split.column()
- colrow = col.row()
- colrow.prop(self, "category", text="")
-
- split = layout.split(percentage=split_percent)
- col = split.column()
- col.label("Boolean Solver:")
- col = split.column()
- colrow = col.row()
- colrow.prop(self, "solver", expand=True)
+ col.prop(self, "category", text="")
split = layout.split(percentage=split_percent)
col = split.column()
col.label("Experimental Features:")
col = split.column()
- colrow = col.row(align=True)
- colrow.prop(self, "fast_transform", toggle=True)
- colrow.prop(self, "use_wire", text="Use Wire Instead Of Bbox", toggle=True)
+ col.prop(self, "fast_transform")
+ col.prop(self, "use_wire", text="Use Wire Instead Of Bbox")
+
layout.separator()
+
"""
# EXPERIMENTAL
col.prop(self, "make_vertex_groups")
col.prop(self, "make_boundary")
"""
+
layout.prop(self, "Enable_Tab_01", text="Hot Keys", icon="KEYINGSET")
if self.Enable_Tab_01:
row = layout.row()
diff --git a/object_print3d_utils/mesh_helpers.py b/object_print3d_utils/mesh_helpers.py
index ca6e0716..e1386b84 100644
--- a/object_print3d_utils/mesh_helpers.py
+++ b/object_print3d_utils/mesh_helpers.py
@@ -279,3 +279,19 @@ def object_merge(context, objects):
# return new object
return base_base
+
+
+def face_is_distorted(ele, angle_distort):
+ no = ele.normal
+ angle_fn = no.angle
+
+ for loop in ele.loops:
+ loopno = loop.calc_normal()
+
+ if loopno.dot(no) < 0.0:
+ loopno.negate()
+
+ if angle_fn(loopno, 1000.0) > angle_distort:
+ return True
+
+ return False
diff --git a/object_print3d_utils/operators.py b/object_print3d_utils/operators.py
index 8dcdf211..be8c323e 100644
--- a/object_print3d_utils/operators.py
+++ b/object_print3d_utils/operators.py
@@ -20,6 +20,8 @@
# All Operator
+import array
+
import bpy
from bpy.types import Operator
from bpy.props import (
@@ -129,8 +131,6 @@ class MESH_OT_Print3D_Check_Solid(Operator):
@staticmethod
def main_check(obj, info):
- import array
-
bm = mesh_helpers.bmesh_copy_from_object(obj, transform=False, triangulate=False)
edges_non_manifold = array.array('i', (i for i, ele in enumerate(bm.edges)
@@ -173,7 +173,6 @@ class MESH_OT_Print3D_Check_Degenerate(Operator):
@staticmethod
def main_check(obj, info):
- import array
scene = bpy.context.scene
print_3d = scene.print_3d
threshold = print_3d.threshold_zero
@@ -202,27 +201,17 @@ class MESH_OT_Print3D_Check_Distorted(Operator):
@staticmethod
def main_check(obj, info):
- import array
-
scene = bpy.context.scene
print_3d = scene.print_3d
angle_distort = print_3d.angle_distort
- def face_is_distorted(ele):
- no = ele.normal
- angle_fn = no.angle
- for loop in ele.loops:
- loopno = loop.calc_normal()
- if loopno.dot(no) < 0.0:
- loopno.negate()
- if angle_fn(loopno, 1000.0) > angle_distort:
- return True
- return False
-
bm = mesh_helpers.bmesh_copy_from_object(obj, transform=True, triangulate=False)
bm.normal_update()
- faces_distort = array.array('i', (i for i, ele in enumerate(bm.faces) if face_is_distorted(ele)))
+ faces_distort = array.array(
+ 'i',
+ (i for i, ele in enumerate(bm.faces) if mesh_helpers.face_is_distorted(ele, angle_distort))
+ )
info.append(("Non-Flat Faces: %d" % len(faces_distort),
(bmesh.types.BMFace, faces_distort)))
@@ -418,20 +407,11 @@ class MESH_OT_Print3D_Clean_Distorted(Operator):
print_3d = scene.print_3d
angle_distort = print_3d.angle_distort
- def face_is_distorted(ele):
- no = ele.normal
- angle_fn = no.angle
- for loop in ele.loops:
- if angle_fn(loop.calc_normal(), 1000.0) > angle_distort:
- return True
- return False
-
obj = context.active_object
bm = mesh_helpers.bmesh_from_object(obj)
bm.normal_update()
- elems_triangulate = [ele for ele in bm.faces if face_is_distorted(ele)]
+ elems_triangulate = [ele for ele in bm.faces if mesh_helpers.face_is_distorted(ele, angle_distort)]
- # edit
if elems_triangulate:
bmesh.ops.triangulate(bm, faces=elems_triangulate)
mesh_helpers.bmesh_to_object(obj, bm)
diff --git a/oscurart_tools/__init__.py b/oscurart_tools/__init__.py
index 8affc8bb..d0f54c24 100644
--- a/oscurart_tools/__init__.py
+++ b/oscurart_tools/__init__.py
@@ -196,6 +196,8 @@ class OscPanelMesh(Panel):
colrow = col.row(align=1)
colrow.operator("mesh.reconst_osc", icon="UV_SYNC_SELECT")
colrow = col.row(align=1)
+ colrow.operator("mesh.vertex_color_mask", icon="GROUP_VCOL")
+ colrow = col.row(align=1)
colrow.operator("mesh.overlap_uv_faces", icon="UV_FACESEL")
colrow = col.row(align=1)
colrow.operator("mesh.uv_island_copy", icon="COPYDOWN")
@@ -210,6 +212,7 @@ class OscPanelMesh(Panel):
colrow = col.row(align=1)
colrow.operator("mesh.create_edit_multimesh", icon="IMPORT", text= "StartEdit")
colrow.operator("mesh.apply_edit_multimesh", icon="EXPORT", text="FinishEdit")
+
class OscPanelShapes(Panel):
bl_idname = "Oscurart Shapes Tools"
diff --git a/oscurart_tools/oscurart_meshes.py b/oscurart_tools/oscurart_meshes.py
index ea899d53..3282c66a 100644
--- a/oscurart_tools/oscurart_meshes.py
+++ b/oscurart_tools/oscurart_meshes.py
@@ -19,6 +19,7 @@
# <pep8 compliant>
import bpy
+from mathutils import Vector
from bpy.types import Operator
from bpy.props import (
IntProperty,
@@ -31,6 +32,7 @@ import bmesh
import time
import blf
from bpy_extras.view3d_utils import location_3d_to_region_2d
+from random import uniform
C = bpy.context
D = bpy.data
@@ -152,7 +154,7 @@ class SelectMenor(Operator):
# -------------------------RESYM VG----------------------------------
-class resymVertexGroups(Operator):
+class rvg(Operator):
bl_idname = "mesh.resym_vertex_weights_osc"
bl_label = "Resym Vertex Weights"
bl_description = ("Copies the symetrical weight value of the vertices on the X axys\n"
@@ -580,12 +582,12 @@ def defCopyUvsIsland(self, context):
bpy.ops.object.mode_set(mode="EDIT")
-def defPasteUvsIsland(self, context):
+def defPasteUvsIsland(self, uvOffset, rotateUv,context):
bpy.ops.object.mode_set(mode="OBJECT")
selPolys = [poly.index for poly in bpy.context.object.data.polygons if poly.select]
-
+
for island in selPolys:
- bpy.ops.object.mode_set(mode="EDIT")
+ bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.mesh.select_all(action="DESELECT")
bpy.ops.object.mode_set(mode="OBJECT")
bpy.context.object.data.polygons[island].select = True
@@ -601,10 +603,20 @@ def defPasteUvsIsland(self, context):
TobLoop.append(li)
for source,target in zip(range(min(obLoop),max(obLoop)+1),range(min(TobLoop),max(TobLoop)+1)):
- bpy.context.object.data.uv_layers.active.data[target].uv = bpy.context.object.data.uv_layers.active.data[source].uv
-
+ bpy.context.object.data.uv_layers.active.data[target].uv = bpy.context.object.data.uv_layers.active.data[source].uv + Vector((uvOffset,0))
+
bpy.ops.object.mode_set(mode="EDIT")
+ if rotateUv:
+ bpy.ops.object.mode_set(mode="OBJECT")
+ for poly in selPolys:
+ bpy.context.object.data.polygons[poly].select = True
+ bpy.ops.object.mode_set(mode="EDIT")
+ bm = bmesh.from_edit_mesh(bpy.context.object.data)
+ bmesh.ops.reverse_uvs(bm, faces=[f for f in bm.faces if f.select])
+ bmesh.ops.rotate_uvs(bm, faces=[f for f in bm.faces if f.select])
+ #bmesh.update_edit_mesh(bpy.context.object.data, tessface=False, destructive=False)
+
class CopyUvIsland(Operator):
@@ -628,7 +640,16 @@ class PasteUvIsland(Operator):
bl_idname = "mesh.uv_island_paste"
bl_label = "Paste Uv Island"
bl_options = {"REGISTER", "UNDO"}
+
+ uvOffset = BoolProperty(
+ name="Uv Offset",
+ default=False
+ )
+ rotateUv = BoolProperty(
+ name="Rotate Uv Corner",
+ default=False
+ )
@classmethod
def poll(cls, context):
return (context.active_object is not None and
@@ -636,7 +657,7 @@ class PasteUvIsland(Operator):
context.active_object.mode == "EDIT")
def execute(self, context):
- defPasteUvsIsland(self, context)
+ defPasteUvsIsland(self, self.uvOffset, self.rotateUv, context)
return {'FINISHED'}
@@ -694,4 +715,53 @@ class ApplyEditMultimesh(Operator):
bpy.context.scene.objects.unlink(ob)
return {'FINISHED'}
- \ No newline at end of file
+# -------------------------VERTEX COLOR MASK----------------------------------
+
+
+class resymVertexGroups(Operator):
+ bl_idname = "mesh.vertex_color_mask"
+ bl_label = "Vertex Color Mask"
+ bl_description = ("Create a Vertex Color Mask")
+ bl_options = {"REGISTER", "UNDO"}
+
+ @classmethod
+ def poll(cls, context):
+ obj = context.active_object
+ return obj is not None
+
+ def execute(self, context):
+ obj = bpy.context.active_object
+ mesh= obj.data
+
+ bpy.ops.object.mode_set(mode='EDIT', toggle=False)
+ bpy.ops.mesh.select_all(action="DESELECT")
+
+ bm = bmesh.from_edit_mesh(mesh)
+ bm.faces.ensure_lookup_table()
+
+ islands = []
+ faces = bm.faces
+
+ try:
+ color_layer = bm.loops.layers.color["RGBMask"]
+ except:
+ color_layer = bm.loops.layers.color.new("RGBMask")
+
+ while faces:
+ faces[0].select_set(True)
+ bpy.ops.mesh.select_linked()
+ islands.append([f for f in faces if f.select])
+ bpy.ops.mesh.hide(unselected=False)
+ faces = [f for f in bm.faces if not f.hide]
+
+ bpy.ops.mesh.reveal()
+
+ for island in islands:
+ color = (uniform(0,1),uniform(0,1),uniform(0,1),1)
+ for face in island:
+ for loop in face.loops:
+ loop[color_layer] = color
+
+ bpy.ops.object.mode_set(mode="VERTEX_PAINT")
+
+ return {'FINISHED'} \ No newline at end of file
diff --git a/oscurart_tools/oscurart_render.py b/oscurart_tools/oscurart_render.py
index e65b06bf..a758fae1 100644
--- a/oscurart_tools/oscurart_render.py
+++ b/oscurart_tools/oscurart_render.py
@@ -70,12 +70,21 @@ def defRenderAll(frametype, scenes):
for i in scene.render.layers:
i.use = False
layer.use = 1
+
print("SCENE: %s" % scene.name)
print("LAYER: %s" % layer.name)
print("OVERRIDE: %s" % str(proptolist))
- scene.render.filepath = os.path.join(
- os.path.dirname(renpath), filename, scene.name, layer.name, "%s_%s_%s" %
- (filename, scene.name, layer.name))
+ #scene.render.filepath = os.path.join(
+ # os.path.dirname(renpath), filename, scene.name, layer.name, "%s_%s_%s" %
+ # (filename, scene.name, layer.name))
+ tokens = {
+ "$Scene":scene.name,
+ "$File":os.path.basename(bpy.data.filepath).split(".")[0],
+ "$Layer":layer.name,
+ "$Camera":scene.camera.name}
+
+ scene.render.filepath = renpath.replace("$Scene",tokens["$Scene"]).replace("$File",tokens["$File"]).replace("$Layer",tokens["$Layer"]).replace("$Camera",tokens["$Camera"])
+
bpy.context.window.screen.scene = scene
bpy.ops.render.render(
animation=True,
@@ -209,7 +218,15 @@ def defoscBatchMaker(TYPE, BIN):
SHFILE = os.path.join(
bpy.data.filepath.rpartition(SYSBAR)[0],
FILENAME + EXTSYS)
-
+
+ renpath = bpy.context.scene.render.filepath
+ tokens = {
+ "$Scene":bpy.context.scene.name,
+ "$File":os.path.basename(bpy.data.filepath).split(".")[0],
+ "$Layer":bpy.context.scene.render.layers.active.name,
+ "$Camera":bpy.context.scene.camera.name}
+
+ rfp = bpy.context.scene.render.filepath.replace("$Scene",tokens["$Scene"]).replace("$File",tokens["$File"]).replace("$Layer",tokens["$Layer"]).replace("$Camera",tokens["$Camera"])
with open(SHFILE, "w") as FILE:
# assign permission in linux
if EXTSYS == ".sh":
@@ -219,13 +236,13 @@ def defoscBatchMaker(TYPE, BIN):
print(
"** Oscurart Batch maker can not modify the permissions.")
if not BIN:
- FILE.writelines("%s%s%s -b %s -x 1 -o %s -P %s%s.py -s %s -e %s -a" %
- (QUOTES, BINDIR, QUOTES, bpy.data.filepath, bpy.context.scene.render.filepath,
+ FILE.writelines("%s%s%s -b %s -x 1 -P %s%s.py -s %s -e %s " %
+ (QUOTES, BINDIR, QUOTES, bpy.data.filepath,
bpy.data.filepath.rpartition(SYSBAR)[0] + SYSBAR, TYPE,
str(bpy.context.scene.frame_start), str(bpy.context.scene.frame_end)))
else:
- FILE.writelines("%s -b %s -x 1 -o %s -P %s%s.py -s %s -e %s -a" %
- ("blender", bpy.data.filepath, bpy.context.scene.render.filepath,
+ FILE.writelines("%s -b %s -x 1 -P %s%s.py -s %s -e %s " %
+ ("blender", bpy.data.filepath,
bpy.data.filepath.rpartition(SYSBAR)[0] + SYSBAR, TYPE,
str(bpy.context.scene.frame_start), str(bpy.context.scene.frame_end)))
diff --git a/paint_palette.py b/paint_palette.py
index 7597fde1..577b292b 100644
--- a/paint_palette.py
+++ b/paint_palette.py
@@ -22,7 +22,7 @@
bl_info = {
"name": "Paint Palettes",
"author": "Dany Lebel (Axon D)",
- "version": (0, 9, 3),
+ "version": (0, 9, 4),
"blender": (2, 63, 0),
"location": "Image Editor and 3D View > Any Paint mode > Color Palette or Weight Palette panel",
"description": "Palettes for color and weight paint modes",
@@ -44,20 +44,20 @@ with the brush by using the button under the color.
import bpy
from bpy.types import (
- Operator,
- Menu,
- Panel,
- PropertyGroup,
- )
+ Operator,
+ Menu,
+ Panel,
+ PropertyGroup,
+)
from bpy.props import (
- BoolProperty,
- FloatProperty,
- FloatVectorProperty,
- IntProperty,
- StringProperty,
- PointerProperty,
- CollectionProperty,
- )
+ BoolProperty,
+ FloatProperty,
+ FloatVectorProperty,
+ IntProperty,
+ StringProperty,
+ PointerProperty,
+ CollectionProperty,
+)
def update_panels():
@@ -97,6 +97,14 @@ def update_weight_value():
return None
+def check_path_return():
+ from os.path import normpath
+ preset_path = bpy.path.abspath(bpy.context.scene.palette_props.presets_folder)
+ paths = normpath(preset_path)
+
+ return paths if paths else ""
+
+
class PALETTE_MT_menu(Menu):
bl_label = "Presets"
preset_subdir = ""
@@ -109,61 +117,62 @@ class PALETTE_MT_menu(Menu):
import bpy.utils
layout = self.layout
+
+ if bpy.data.filepath == "":
+ layout.label("*Please save the .blend file first*")
+ return
+
if not searchpaths[0]:
layout.label("* Missing Paths *")
+ return
# collect paths
- else:
- files = []
- for directory in searchpaths:
- files.extend([(f, os.path.join(directory, f)) for f in os.listdir(directory)])
+ files = []
+ for directory in searchpaths:
+ files.extend([(f, os.path.join(directory, f)) for f in os.listdir(directory)])
- files.sort()
+ files.sort()
- for f, filepath in files:
+ for f, filepath in files:
- if f.startswith("."):
- continue
- # do not load everything from the given folder, only .gpl files
- if f[-4:] != ".gpl":
- continue
+ if f.startswith("."):
+ continue
+ # do not load everything from the given folder, only .gpl files
+ if f[-4:] != ".gpl":
+ continue
- preset_name = bpy.path.display_name(f)
- props = layout.operator(operator, text=preset_name)
+ preset_name = bpy.path.display_name(f)
+ props = layout.operator(operator, text=preset_name)
- for attr, value in props_default.items():
- setattr(props, attr, value)
+ for attr, value in props_default.items():
+ setattr(props, attr, value)
- props.filepath = filepath
- if operator == "palette.load_gimp_palette":
- props.menu_idname = self.bl_idname
+ props.filepath = filepath
+ if operator == "palette.load_gimp_palette":
+ props.menu_idname = self.bl_idname
def draw_preset(self, context):
- """Define these on the subclass
- - preset_operator
- - preset_subdir
- """
- import bpy
- self.path_menu([bpy.context.scene.palette_props.presets_folder], self.preset_operator)
+ paths = check_path_return()
+ self.path_menu([paths], self.preset_operator)
draw = draw_preset
-class LoadGimpPalette(Operator):
+class PALETTE_OT_load_gimp_palette(Operator):
"""Execute a preset"""
bl_idname = "palette.load_gimp_palette"
bl_label = "Load a Gimp palette"
filepath = StringProperty(
- name="Path",
- description="Path of the .gpl file to load",
- default=""
- )
+ name="Path",
+ description="Path of the .gpl file to load",
+ default=""
+ )
menu_idname = StringProperty(
- name="Menu ID Name",
- description="ID name of the menu this was called from",
- default=""
- )
+ name="Menu ID Name",
+ description="ID name of the menu this was called from",
+ default=""
+ )
def execute(self, context):
from os.path import basename
@@ -255,14 +264,16 @@ class WriteGimpPalette():
bl_options = {'REGISTER'} # only because invoke_props_popup requires
name = StringProperty(
- name="Name",
- description="Name of the preset, used to make the path name",
- maxlen=64, default=""
- )
+ name="Name",
+ description="Name of the preset, used to make the path name",
+ maxlen=64,
+ options={'SKIP_SAVE'},
+ default=""
+ )
remove_active = BoolProperty(
- default=False,
- options={'HIDDEN'}
- )
+ default=False,
+ options={'HIDDEN'}
+ )
@staticmethod
def as_filename(name): # could reuse for other presets
@@ -278,26 +289,25 @@ class WriteGimpPalette():
self.pre_cb(context)
preset_menu_class = getattr(bpy.types, self.preset_menu)
+ target_path = check_path_return()
- if not self.remove_active:
+ if not target_path:
+ self.report({'WARNING'}, "Failed to create presets path")
+ return {'CANCELLED'}
+
+ if not os.path.exists(target_path):
+ self.report({'WARNING'},
+ "Failure to open the saved Palettes Folder. Check if the path exists")
+ return {'CANCELLED'}
+ if not self.remove_active:
if not self.name:
+ self.report({'INFO'},
+ "No name is given for the preset entry. Operation Cancelled")
return {'FINISHED'}
filename = self.as_filename(self.name)
- target_path = pp.presets_folder
-
- if not target_path:
- self.report({'WARNING'}, "Failed to create presets path")
- return {'CANCELLED'}
-
- if not os.path.exists(target_path):
- self.report({'WARNING'},
- "Failure to open the saved Palletes Folder. Check if the path exists")
- return {'CANCELLED'}
-
filepath = os.path.join(target_path, filename) + ".gpl"
-
file_preset = open(filepath, 'wb')
gpl = "GIMP Palette\n"
gpl += "Name: %s\n" % filename
@@ -312,18 +322,19 @@ class WriteGimpPalette():
file_preset.close()
pp.palette_name = filename
+ preset_menu_class.bl_label = bpy.path.display_name(filename)
+
+ self.report({'INFO'}, "Created Palette: {}".format(filepath))
else:
preset_active = preset_menu_class.bl_label
+ filename = self.as_filename(preset_active)
- # fairly sloppy but convenient.
- filepath = bpy.utils.preset_find(preset_active, self.preset_subdir)
-
- if not filepath:
- filepath = bpy.utils.preset_find(preset_active,
- self.preset_subdir, display_name=True)
+ filepath = os.path.join(target_path, filename) + ".gpl"
- if not filepath:
+ if not filepath or not os.path.exists(filepath):
+ self.report({'WARNING'}, "Preset could not be found. Operation Cancelled")
+ self.reset_preset_name(preset_menu_class, pp)
return {'CANCELLED'}
if hasattr(self, "remove"):
@@ -331,18 +342,24 @@ class WriteGimpPalette():
else:
try:
os.remove(filepath)
+ self.report({'INFO'}, "Deleted palette: {}".format(filepath))
except:
import traceback
traceback.print_exc()
- # XXX, stupid!
- preset_menu_class.bl_label = "Presets"
+ self.reset_preset_name(preset_menu_class, pp)
if hasattr(self, "post_cb"):
self.post_cb(context)
return {'FINISHED'}
+ @staticmethod
+ def reset_preset_name(presets, props):
+ # XXX, still stupid!
+ presets.bl_label = "Presets"
+ props.palette_name = ""
+
def check(self, context):
self.name = self.as_filename(self.name)
@@ -350,11 +367,11 @@ class WriteGimpPalette():
if not self.remove_active:
wm = context.window_manager
return wm.invoke_props_dialog(self)
- else:
- return self.execute(context)
+ return self.execute(context)
-class AddPresetPalette(WriteGimpPalette, Operator):
+
+class PALETTE_OT_preset_add(WriteGimpPalette, Operator):
bl_idname = "palette.preset_add"
bl_label = "Add Palette Preset"
preset_menu = "PALETTE_MT_menu"
@@ -439,14 +456,13 @@ class IMAGE_OT_select_color(Operator):
def color_palette_draw(self, context):
- palette_props = bpy.context.scene.palette_props
+ palette_props = context.scene.palette_props
layout = self.layout
- bpy.types.PALETTE_MT_menu.preset_subdir = palette_props.presets_folder
row = layout.row(align=True)
- row.menu("PALETTE_MT_menu", text=palette_props.palette_name.rstrip())
- row.operator("palette.preset_add", text="", icon="ZOOMIN")
+ row.menu("PALETTE_MT_menu", text=PALETTE_MT_menu.bl_label)
+ row.operator("palette.preset_add", text="", icon="ZOOMIN").remove_active = False
row.operator("palette.preset_add", text="", icon="ZOOMOUT").remove_active = True
col = layout.column(align=True)
@@ -487,8 +503,6 @@ def color_palette_draw(self, context):
row = layout.row()
row.prop(palette_props, "presets_folder", text="")
- pass
-
class BrushButtonsPanel():
bl_space_type = 'IMAGE_EDITOR'
@@ -612,11 +626,12 @@ class VIEW3D_OT_reset_weight_palette(Operator):
def execute(self, context):
try:
palette_props = context.scene.palette_props
- dict_defs = {0: 0.0, 1: 0.1, 2: 0.25,
- 3: 0.333, 4: 0.4, 5: 0.5,
- 6: 0.6, 7: 0.6666, 8: 0.75,
- 9: 0.9, 10: 1.0
- }
+ dict_defs = {
+ 0: 0.0, 1: 0.1, 2: 0.25,
+ 3: 0.333, 4: 0.4, 5: 0.5,
+ 6: 0.6, 7: 0.6666, 8: 0.75,
+ 9: 0.9, 10: 1.0
+ }
current_idx = palette_props.current_weight_index
palette_props.weight = dict_defs[current_idx]
@@ -673,30 +688,20 @@ class VIEW3D_PT_weight_palette(PaintPanel, Panel):
row.operator("paint.reset_weight_palette", text="Reset")
-class Colors(PropertyGroup):
+class PALETTE_Colors(PropertyGroup):
"""Class for colors CollectionProperty"""
color = FloatVectorProperty(
- name="",
- description="",
- default=(0.8, 0.8, 0.8),
- min=0, max=1,
- step=1, precision=3,
- subtype='COLOR_GAMMA',
- size=3
- )
-
-
-class Weights(PropertyGroup):
- """Class for Weights Collection Property"""
- weight = FloatProperty(
- default=0.0,
- min=0.0,
- max=1.0,
- precision=3
- )
+ name="",
+ description="",
+ default=(0.8, 0.8, 0.8),
+ min=0, max=1,
+ step=1, precision=3,
+ subtype='COLOR_GAMMA',
+ size=3
+ )
-class PaletteProps(PropertyGroup):
+class PALETTE_Props(PropertyGroup):
def update_color_name(self, context):
pp = bpy.context.scene.palette_props
@@ -742,133 +747,151 @@ class PaletteProps(PropertyGroup):
return None
palette_name = StringProperty(
- name="Palette Name",
- default="Preset",
- subtype='FILE_NAME'
- )
+ name="Palette Name",
+ default="Preset",
+ subtype='FILE_NAME'
+ )
color_name = StringProperty(
- name="",
- description="Color Name",
- default="Untitled",
- update=update_color_name
- )
+ name="",
+ description="Color Name",
+ default="Untitled",
+ update=update_color_name
+ )
columns = IntProperty(
- name="Columns",
- description="Number of Columns",
- min=0, max=16,
- default=0
- )
+ name="Columns",
+ description="Number of Columns",
+ min=0, max=16,
+ default=0
+ )
index = IntProperty(
- name="Index",
- description="Move Selected Color",
- min=0,
- update=move_color
- )
+ name="Index",
+ description="Move Selected Color",
+ min=0,
+ update=move_color
+ )
notes = StringProperty(
- name="Palette Notes",
- default="#\n"
- )
+ name="Palette Notes",
+ default="#\n"
+ )
current_color_index = IntProperty(
- name="Current Color Index",
- description="",
- default=0,
- min=0
- )
+ name="Current Color Index",
+ description="",
+ default=0,
+ min=0
+ )
current_weight_index = IntProperty(
- name="Current Color Index",
- description="",
- default=10,
- min=-1
- )
+ name="Current Color Index",
+ description="",
+ default=10,
+ min=-1
+ )
presets_folder = StringProperty(name="",
- description="Palettes Folder",
- subtype="DIR_PATH"
- )
+ description="Palettes Folder",
+ subtype="DIR_PATH",
+ default="//"
+ )
colors = CollectionProperty(
- type=Colors
- )
+ type=PALETTE_Colors
+ )
weight = FloatProperty(
- name="Weight",
- description="Modify the active Weight preset slot value",
- default=0.0,
- min=0.0, max=1.0,
- precision=3,
- update=update_weight
- )
+ name="Weight",
+ description="Modify the active Weight preset slot value",
+ default=0.0,
+ min=0.0, max=1.0,
+ precision=3,
+ update=update_weight
+ )
weight_0 = FloatProperty(
- default=0.0,
- min=0.0, max=1.0,
- precision=3
- )
+ default=0.0,
+ min=0.0, max=1.0,
+ precision=3
+ )
weight_1 = FloatProperty(
- default=0.1,
- min=0.0, max=1.0,
- precision=3
- )
+ default=0.1,
+ min=0.0, max=1.0,
+ precision=3
+ )
weight_2 = FloatProperty(
- default=0.25,
- min=0.0, max=1.0,
- precision=3
- )
+ default=0.25,
+ min=0.0, max=1.0,
+ precision=3
+ )
weight_3 = FloatProperty(
- default=0.333,
- min=0.0, max=1.0,
- precision=3
- )
+ default=0.333,
+ min=0.0, max=1.0,
+ precision=3
+ )
weight_4 = FloatProperty(
- default=0.4,
- min=0.0, max=1.0,
- precision=3
- )
+ default=0.4,
+ min=0.0, max=1.0,
+ precision=3
+ )
weight_5 = FloatProperty(
- default=0.5,
- min=0.0, max=1.0,
- precision=3
- )
+ default=0.5,
+ min=0.0, max=1.0,
+ precision=3
+ )
weight_6 = FloatProperty(
- default=0.6,
- min=0.0, max=1.0,
- precision=3
- )
+ default=0.6,
+ min=0.0, max=1.0,
+ precision=3
+ )
weight_7 = FloatProperty(
- default=0.6666,
- min=0.0, max=1.0,
- precision=3
- )
+ default=0.6666,
+ min=0.0, max=1.0,
+ precision=3
+ )
weight_8 = FloatProperty(
- default=0.75,
- min=0.0, max=1.0,
- precision=3
- )
+ default=0.75,
+ min=0.0, max=1.0,
+ precision=3
+ )
weight_9 = FloatProperty(
- default=0.9,
- min=0.0, max=1.0,
- precision=3
- )
+ default=0.9,
+ min=0.0, max=1.0,
+ precision=3
+ )
weight_10 = FloatProperty(
- default=1.0,
- min=0.0, max=1.0,
- precision=3
- )
- pass
+ default=1.0,
+ min=0.0, max=1.0,
+ precision=3
+ )
+
+
+classes = (
+ PALETTE_MT_menu,
+ PALETTE_OT_load_gimp_palette,
+ PALETTE_OT_preset_add,
+ PALETTE_OT_add_color,
+ PALETTE_OT_remove_color,
+ PALETTE_OT_sample_tool_color,
+ IMAGE_OT_select_color,
+ IMAGE_PT_color_palette,
+ VIEW3D_PT_color_palette,
+ VIEW3D_OT_select_weight,
+ VIEW3D_OT_reset_weight_palette,
+ VIEW3D_PT_weight_palette,
+ PALETTE_Colors,
+ PALETTE_Props,
+)
def register():
- bpy.utils.register_module(__name__)
+ for cls in classes:
+ bpy.utils.register_class(cls)
bpy.types.Scene.palette_props = PointerProperty(
- type=PaletteProps,
- name="Palette Props",
- description=""
- )
- pass
+ type=PALETTE_Props,
+ name="Palette Props",
+ description=""
+ )
def unregister():
- bpy.utils.unregister_module(__name__)
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
del bpy.types.Scene.palette_props
- pass
if __name__ == "__main__":
diff --git a/presets/pov/lamp/01_(5400K)_Direct_Sun.py b/presets/pov/lamp/01_(5400K)_Direct_Sun.py
new file mode 100644
index 00000000..7f53ce06
--- /dev/null
+++ b/presets/pov/lamp/01_(5400K)_Direct_Sun.py
@@ -0,0 +1,10 @@
+#Since the dawn of time
+
+import bpy
+bpy.context.object.data.type = 'SUN'
+lampdata = bpy.context.object.data
+
+lampdata.color = (1.0, 1.0, 0.9843137264251709)
+lampdata.energy = 1.2 #100 000lux
+#lampdata.distance = 0.001
+#lampdata.falloff_type = 'INVERSE_SQUARE' \ No newline at end of file
diff --git a/presets/pov/lamp/02_(5400K)_High_Noon_Sun.py b/presets/pov/lamp/02_(5400K)_High_Noon_Sun.py
new file mode 100644
index 00000000..6d3083a6
--- /dev/null
+++ b/presets/pov/lamp/02_(5400K)_High_Noon_Sun.py
@@ -0,0 +1,17 @@
+#Since the dawn of time
+
+import bpy
+bpy.context.object.rotation_euler = (0,0,0)#And loc HIGH
+bpy.context.object.location = (0,0,700000000)
+bpy.context.object.data.type = 'AREA'
+lampdata = bpy.context.object.data
+
+lampdata.shape = 'SQUARE'
+lampdata.size = 30000000#0.02
+#lampdata.size_y = 0.02
+lampdata.shadow_ray_samples_x = 2
+#lampdata.shadow_ray_samples_y = 3
+lampdata.color = (1.0, 1.0, 1.0)
+lampdata.energy = 1.094316#91193 #lux
+lampdata.distance =695699968
+
diff --git a/presets/pov/lamp/03_(6000K)_Daylight_Window.py b/presets/pov/lamp/03_(6000K)_Daylight_Window.py
new file mode 100644
index 00000000..a9781f57
--- /dev/null
+++ b/presets/pov/lamp/03_(6000K)_Daylight_Window.py
@@ -0,0 +1,13 @@
+#Since the dawn of time
+
+import bpy
+bpy.context.object.data.type = 'AREA'
+lampdata = bpy.context.object.data
+
+lampdata.size = 1.2
+lampdata.size_y = 2.10
+lampdata.shadow_ray_samples_x = 2
+lampdata.shadow_ray_samples_y = 3
+lampdata.color = (1.0, 1.0, 1.0)
+lampdata.energy = 1.094316#91193 #lux
+lampdata.distance = 1.0
diff --git a/presets/pov/lamp/04_(6000K)_2500W_HMI_(Halogen_Metal_Iodide).py b/presets/pov/lamp/04_(6000K)_2500W_HMI_(Halogen_Metal_Iodide).py
new file mode 100644
index 00000000..0bfa95d8
--- /dev/null
+++ b/presets/pov/lamp/04_(6000K)_2500W_HMI_(Halogen_Metal_Iodide).py
@@ -0,0 +1,14 @@
+#After 1969
+#made specifically for film and entertainment applications
+
+import bpy
+bpy.context.object.data.type = 'SPOT'
+lampdata = bpy.context.object.data
+
+lampdata.show_cone = True
+lampdata.spot_size = 0.872665
+lampdata.spot_blend = 0.9
+lampdata.color = (0.99, 0.9882352948188782, 0.998)
+lampdata.energy = 223.81796 #240000lm/21.446(=lux)*0.004*2.5(distance) *2 for distance is the point of half strength
+lampdata.distance = 0.001
+lampdata.falloff_type = 'INVERSE_SQUARE' \ No newline at end of file
diff --git a/presets/pov/lamp/05_(4000K)_100W_Metal_Halide.py b/presets/pov/lamp/05_(4000K)_100W_Metal_Halide.py
new file mode 100644
index 00000000..e91d3f9a
--- /dev/null
+++ b/presets/pov/lamp/05_(4000K)_100W_Metal_Halide.py
@@ -0,0 +1,14 @@
+#After 1962
+#Common uses: outdoor lighting where good color rendering is needed, television/film lighting, sports fields, car headlights, flood lights, heavy flashlights, green house applications
+
+import bpy
+bpy.context.object.data.type = 'SPOT'
+lampdata = bpy.context.object.data
+
+lampdata.show_cone = True
+lampdata.spot_size = 0.6
+lampdata.spot_blend = 0.9
+lampdata.color = (0.9490196108818054, 0.9882352948188782, 1.0)
+lampdata.energy = 20.98293#9000lm/21.446(=lux)*0.004*6.25(distance) *2 for distance is the point of half strength
+lampdata.distance = 0.025
+lampdata.falloff_type = 'INVERSE_SQUARE' \ No newline at end of file
diff --git a/presets/pov/lamp/06_(3200K)_100W_Quartz_Halogen.py b/presets/pov/lamp/06_(3200K)_100W_Quartz_Halogen.py
new file mode 100644
index 00000000..b58edf17
--- /dev/null
+++ b/presets/pov/lamp/06_(3200K)_100W_Quartz_Halogen.py
@@ -0,0 +1,16 @@
+#since 1960, no longer manufactured since 2016
+#8mm projectors
+#used in many automobiles headlamps ; outdoor lighting systems ; watercraft ; desktop lamps (smaller power).
+#theatrical and studio (film and television) fixtures, including Ellipsoidal reflector spotlights, Source Four, and Fresnels; PAR Cans
+
+import bpy
+bpy.context.object.data.type = 'SPOT'
+lampdata = bpy.context.object.data
+
+lampdata.show_cone = True
+lampdata.spot_size = 1.9
+lampdata.spot_blend = 0.9
+lampdata.color = (1.0, 0.9450980424880981, 0.8784313797950745)
+lampdata.energy = 12.43433#5000/21.446 #lumen values/20 or lux when available used as a basis
+lampdata.distance = 0.015#energy calculated for length 0.075 but width gives better result
+lampdata.falloff_type = 'INVERSE_SQUARE'
diff --git a/presets/pov/lamp/07_(2850K)_100w_Tungsten.py b/presets/pov/lamp/07_(2850K)_100w_Tungsten.py
new file mode 100644
index 00000000..72675de3
--- /dev/null
+++ b/presets/pov/lamp/07_(2850K)_100w_Tungsten.py
@@ -0,0 +1,10 @@
+#1908 - today
+
+import bpy
+bpy.context.object.data.type = 'POINT'
+lampdata = bpy.context.object.data
+
+lampdata.color = (1.0, 0.8392156958580017, 0.6666666865348816)
+lampdata.energy = 7.46060#3.7303#1000/21.446/(lampdistance/candledistance) #lumen values/21.446 or lux when available used as a basis
+lampdata.distance = 0.05
+lampdata.falloff_type = 'INVERSE_SQUARE' \ No newline at end of file
diff --git a/presets/pov/lamp/08_(2600K)_40w_Tungsten.py b/presets/pov/lamp/08_(2600K)_40w_Tungsten.py
new file mode 100644
index 00000000..88ef46a0
--- /dev/null
+++ b/presets/pov/lamp/08_(2600K)_40w_Tungsten.py
@@ -0,0 +1,10 @@
+#since 1908
+
+import bpy
+bpy.context.object.data.type = 'POINT'
+lampdata = bpy.context.object.data
+
+lampdata.color = (1.0, 0.8196078431372549, 0.6980392156862745)
+lampdata.energy = 2.98424#400/21.446 #lumen values/21.446 or lux when available used as a basis
+lampdata.distance = 0.05
+lampdata.falloff_type = 'INVERSE_SQUARE' \ No newline at end of file
diff --git a/presets/pov/lamp/09_(5000K)_75W_Full_Spectrum_Fluorescent_T12.py b/presets/pov/lamp/09_(5000K)_75W_Full_Spectrum_Fluorescent_T12.py
new file mode 100644
index 00000000..b0a23eda
--- /dev/null
+++ b/presets/pov/lamp/09_(5000K)_75W_Full_Spectrum_Fluorescent_T12.py
@@ -0,0 +1,14 @@
+#Available since 1979 (Triphosphor lamps)
+
+import bpy
+bpy.context.object.data.type = 'AREA'
+lampdata = bpy.context.object.data
+
+lampdata.size = 0.038
+lampdata.size_y = 2.40284
+lampdata.shadow_ray_samples_x = 1
+lampdata.shadow_ray_samples_y = 2
+lampdata.color = (1.0, 0.95686274766922, 0.9490200281143188)
+lampdata.energy = 4.45304#4775lm/21.446(=lux)*0.004(distance) *2 for distance is the point of half strength 6200lm?
+lampdata.distance = 1.0 #dist values multiplied by 10 for area lights for same power as bulb/spot/...
+#lampdata.falloff_type = 'INVERSE_SQUARE' \ No newline at end of file
diff --git a/presets/pov/lamp/10_(4300K)_40W_Vintage_Fluorescent_T12.py b/presets/pov/lamp/10_(4300K)_40W_Vintage_Fluorescent_T12.py
new file mode 100644
index 00000000..d03ce6fd
--- /dev/null
+++ b/presets/pov/lamp/10_(4300K)_40W_Vintage_Fluorescent_T12.py
@@ -0,0 +1,14 @@
+#since 1939 , T12 no longer manufactured since T8 propagated
+
+import bpy
+bpy.context.object.data.type = 'AREA'
+lampdata = bpy.context.object.data
+
+lampdata.size = 0.038
+lampdata.size_y = 1.2192
+lampdata.shadow_ray_samples_x = 1
+lampdata.shadow_ray_samples_y = 2
+lampdata.color = (0.901, 1.0, 0.979)
+lampdata.energy = 2.14492#2300lm/21.446(=lux)*0.004*2.5(distance) *2 for distance is the point of half strength
+lampdata.distance = 1.0 #dist values multiplied by 10 for area lights for same power as bulb/spot/...
+#lampdata.falloff_type = 'INVERSE_SQUARE' \ No newline at end of file
diff --git a/presets/pov/lamp/11_(5000K)_18W_Standard_Fluorescent_T8.py b/presets/pov/lamp/11_(5000K)_18W_Standard_Fluorescent_T8.py
new file mode 100644
index 00000000..6fef1763
--- /dev/null
+++ b/presets/pov/lamp/11_(5000K)_18W_Standard_Fluorescent_T8.py
@@ -0,0 +1,13 @@
+#since 1973
+
+import bpy
+bpy.context.object.data.type = 'AREA'
+lampdata = bpy.context.object.data
+
+lampdata.size = 0.026
+lampdata.size_y = 0.59
+lampdata.shadow_ray_samples_x = 1
+lampdata.shadow_ray_samples_y = 2
+lampdata.color = (0.95686274766922, 1.0, 0.9803921580314636)
+lampdata.energy = 1.25898#1350lm/21.446(=lux)*0.004*2.5(distance) *2 for distance is the point of half strength
+lampdata.distance = 1.0 #dist values multiplied by 10 for area lights for same power as bulb/spot/...
diff --git a/presets/pov/lamp/12_(4200K)_18W_Cool_White_Fluorescent_T8.py b/presets/pov/lamp/12_(4200K)_18W_Cool_White_Fluorescent_T8.py
new file mode 100644
index 00000000..83f8dd4e
--- /dev/null
+++ b/presets/pov/lamp/12_(4200K)_18W_Cool_White_Fluorescent_T8.py
@@ -0,0 +1,15 @@
+#Available since 1979
+#more common than the warm white
+
+import bpy
+bpy.context.object.data.type = 'AREA'
+lampdata = bpy.context.object.data
+
+lampdata.size = 0.026
+lampdata.size_y = 0.59
+lampdata.shadow_ray_samples_x = 1
+lampdata.shadow_ray_samples_y = 2
+lampdata.color = (0.8313725590705872, 0.9215686321258545, 1.0)
+lampdata.energy = 1.25898#1350lm/21.446(=lux)*0.004*2.5(distance) *2 for distance is the point of half strength
+lampdata.distance = 1.0 #dist values multiplied by 10 for area lights for same power as bulb/spot/...
+#lampdata.falloff_type = 'INVERSE_SQUARE' \ No newline at end of file
diff --git a/presets/pov/lamp/13_(3000K)_18W_Warm_Fluorescent_T8.py b/presets/pov/lamp/13_(3000K)_18W_Warm_Fluorescent_T8.py
new file mode 100644
index 00000000..e1cee557
--- /dev/null
+++ b/presets/pov/lamp/13_(3000K)_18W_Warm_Fluorescent_T8.py
@@ -0,0 +1,14 @@
+#Available since 1979
+#developed to get closer to tungsten atmospher in interiors
+
+import bpy
+bpy.context.object.data.type = 'AREA'
+lampdata = bpy.context.object.data
+
+lampdata.size = 0.026
+lampdata.size_y = 0.59
+lampdata.shadow_ray_samples_x = 1
+lampdata.shadow_ray_samples_y = 2
+lampdata.color = (1.0, 0.95686274766922, 0.8980392217636108)
+lampdata.energy = 1.25898#1350lm/21.446(=lux)*0.004*2.5(distance) *2 for distance is the point of half strength
+lampdata.distance = 1.0 #dist values multiplied by 10 for area lights for same power as bulb/spot/...
diff --git a/presets/pov/lamp/14_(6500K)_54W_Grow_Light_Fluorescent_T5-HO.py b/presets/pov/lamp/14_(6500K)_54W_Grow_Light_Fluorescent_T5-HO.py
new file mode 100644
index 00000000..55f84ab8
--- /dev/null
+++ b/presets/pov/lamp/14_(6500K)_54W_Grow_Light_Fluorescent_T5-HO.py
@@ -0,0 +1,13 @@
+#Available since 1995
+
+import bpy
+bpy.context.object.data.type = 'AREA'
+lampdata = bpy.context.object.data
+
+lampdata.size = 0.016
+lampdata.size_y = 1.149
+lampdata.shadow_ray_samples_x = 1
+lampdata.shadow_ray_samples_y = 2
+lampdata.color = (1.0, 0.83, 0.986274528503418)
+lampdata.energy = 4.66287 #0.93257#4.66287#5000lm/21.446(=lux)*0.004*2.5(distance) *2 for distance is the point of half strength
+lampdata.distance = 0.1 #dist values multiplied by 10 for area lights for same power as bulb/spot/...
diff --git a/presets/pov/lamp/15_(3200K)_40W_Induction_ Fluorescent.py b/presets/pov/lamp/15_(3200K)_40W_Induction_ Fluorescent.py
new file mode 100644
index 00000000..1d0851e5
--- /dev/null
+++ b/presets/pov/lamp/15_(3200K)_40W_Induction_ Fluorescent.py
@@ -0,0 +1,14 @@
+#since the 1990's,
+#Often circular or rectangular closed loop electrodeless fluorescent lamps
+
+import bpy
+bpy.context.object.data.type = 'SPOT'
+lampdata = bpy.context.object.data
+
+#lampdata.use_halo = True
+lampdata.spot_size = 3.14
+lampdata.spot_blend = 0.9
+lampdata.color = (1.0, 0.9450980424880981, 0.8784313797950745)
+lampdata.energy = 2.61121#2800/21.446 #lumen values/20 or lux when available used as a basis
+lampdata.distance = 0.15#energy calculated for length 0.075 but width gives better result
+lampdata.falloff_type = 'INVERSE_SQUARE'
diff --git a/presets/pov/lamp/16_(2100K)_150W_High_Pressure_Sodium.py b/presets/pov/lamp/16_(2100K)_150W_High_Pressure_Sodium.py
new file mode 100644
index 00000000..9ddb32cb
--- /dev/null
+++ b/presets/pov/lamp/16_(2100K)_150W_High_Pressure_Sodium.py
@@ -0,0 +1,13 @@
+#Starting from 1964
+
+import bpy
+bpy.context.object.data.type = 'SPOT'
+lampdata = bpy.context.object.data
+
+lampdata.show_cone = True
+lampdata.color = (1.0, 0.772549033164978, 0.5607843399047852)
+lampdata.energy = 4.47636#12000lm/21.446(=lux)*0.004(distance) *2 for distance is the point of half strength
+lampdata.distance = 1.0
+lampdata.spot_size = 1.9
+lampdata.spot_blend = 0.9
+lampdata.falloff_type = 'INVERSE_SQUARE'
diff --git a/presets/pov/lamp/17_(1700K)_135W_Low_Pressure_Sodium.py b/presets/pov/lamp/17_(1700K)_135W_Low_Pressure_Sodium.py
new file mode 100644
index 00000000..d5e42678
--- /dev/null
+++ b/presets/pov/lamp/17_(1700K)_135W_Low_Pressure_Sodium.py
@@ -0,0 +1,11 @@
+#(1700K) 135W Low Pressure Sodium Vapor Starting from 1932
+#Mostly used for Outdoor city lighting, security lighting, long tunnel lighting
+
+import bpy
+bpy.context.object.data.type = 'POINT'
+lampdata = bpy.context.object.data
+
+lampdata.color = (1.0, 0.5764706134796143, 0.16078431904315948)
+lampdata.energy = 8.43048#22600lm/21.446(=lux)*0.004(distance) *2 for distance is the point of half strength
+lampdata.distance = 1.0
+lampdata.falloff_type = 'INVERSE_SQUARE' \ No newline at end of file
diff --git a/presets/pov/lamp/18_(6800K)_175W_Mercury_Vapor.py b/presets/pov/lamp/18_(6800K)_175W_Mercury_Vapor.py
new file mode 100644
index 00000000..e1a5143c
--- /dev/null
+++ b/presets/pov/lamp/18_(6800K)_175W_Mercury_Vapor.py
@@ -0,0 +1,13 @@
+#Starting from 1901
+
+import bpy
+bpy.context.object.data.type = 'SPOT'
+lampdata = bpy.context.object.data
+
+lampdata.show_cone = True
+lampdata.spot_size = 1.25
+lampdata.spot_blend = 0.9
+lampdata.color = (0.8470588326454163, 0.9686274528503418, 1.0)
+lampdata.energy = 17.25263#7400lm/21.446(=lux)*0.004*6.25(distance) *2 for distance is the point of half strength
+lampdata.distance = 0.025
+lampdata.falloff_type = 'INVERSE_SQUARE'
diff --git a/presets/pov/lamp/19_(5200K)_700W_Carbon_Arc.py b/presets/pov/lamp/19_(5200K)_700W_Carbon_Arc.py
new file mode 100644
index 00000000..139dc8bb
--- /dev/null
+++ b/presets/pov/lamp/19_(5200K)_700W_Carbon_Arc.py
@@ -0,0 +1,18 @@
+#Starting from 1876 (first type of commercial lamps developed with electricity)
+#Carbon arc lamps were being phased out after the 1910s.
+#For general lighting the lamp was replaced by the 1920s and 30s in most cities.
+#The lamp continued to be used for spot lights, film production lighting and film projector lamps.
+#Most of the remaining carbon arc lamps ceased production by the 1980s
+
+import bpy
+bpy.context.object.data.type = 'SPOT'
+lampdata = bpy.context.object.data
+
+#lampdata.use_halo = True
+lampdata.show_cone = True
+lampdata.spot_size = 1.5
+lampdata.spot_blend = 0.3
+lampdata.color = (1.0, 0.9803921580314636, 0.95686274766922)
+lampdata.energy = 51.29162#55000lm/21.446(=lux)*0.004*2.5(distance) *2 for distance is the point of half strength
+lampdata.distance = 0.01
+lampdata.falloff_type = 'INVERSE_SQUARE' \ No newline at end of file
diff --git a/presets/pov/lamp/20_(6500K)_15W_LED_Spot.py b/presets/pov/lamp/20_(6500K)_15W_LED_Spot.py
new file mode 100644
index 00000000..1b70b9f1
--- /dev/null
+++ b/presets/pov/lamp/20_(6500K)_15W_LED_Spot.py
@@ -0,0 +1,12 @@
+#since 2008
+
+import bpy
+bpy.context.object.data.type = 'SPOT'
+lampdata = bpy.context.object.data
+
+lampdata.show_cone = True
+lampdata.spot_size = 1.39626 #80 degrees in radian
+lampdata.spot_blend = 0.5
+lampdata.color = (1.0, 0.9372549057006836, 0.9686274528503418)
+lampdata.energy = 1.39886#1500lm/21.446(=lux)*0.004*2.5(distance) *2 for distance is the point of half strength
+lampdata.distance = 1.18 #dist values multiplied by 10 for area lights for same power as bulb/spot/...
diff --git a/presets/pov/lamp/21_(2700K)_7W_OLED_Panel.py b/presets/pov/lamp/21_(2700K)_7W_OLED_Panel.py
new file mode 100644
index 00000000..8f2ebb8f
--- /dev/null
+++ b/presets/pov/lamp/21_(2700K)_7W_OLED_Panel.py
@@ -0,0 +1,14 @@
+#since 2025
+#inspired by OSRAM Early Future / IKEA Vitsand / OTI Lumionics Aerelight
+
+import bpy
+bpy.context.object.data.type = 'AREA'
+lampdata = bpy.context.object.data
+
+lampdata.size = 0.033
+lampdata.size_y = 0.133
+lampdata.shadow_ray_samples_x = 2
+lampdata.shadow_ray_samples_y = 2
+lampdata.color = (1.0, 0.8292156958580017, 0.6966666865348816)
+lampdata.energy = 0.83932#900lm/21.446(=lux)*0.004*2.5(distance) *2 for distance is the point of half strength
+lampdata.distance = 1.18 #dist values multiplied by 10 for area lights for same power as bulb/spot/...
diff --git a/presets/pov/lamp/22_(30000K)_40W_Black_Light_Fluorescent.py b/presets/pov/lamp/22_(30000K)_40W_Black_Light_Fluorescent.py
new file mode 100644
index 00000000..ef6b4725
--- /dev/null
+++ b/presets/pov/lamp/22_(30000K)_40W_Black_Light_Fluorescent.py
@@ -0,0 +1,12 @@
+#Starting from 1939 (World War II Byler's tubes)
+
+import bpy
+bpy.context.object.data.type = 'AREA'
+lampdata = bpy.context.object.data
+
+lampdata.size = 0.038
+lampdata.size_y = 1.2192
+lampdata.color = (0.6549019813537598, 0.0, 1.0)
+lampdata.energy = 1.86515#100/21.446 #lumen values/21.446 or lux when available used as a basis
+lampdata.distance = 0.4 #dist values multiplied by 10 for area lights for same power as bulb/spot/...
+
diff --git a/presets/pov/lamp/23_(30000K)_40W_Black_Light_Bulb.py b/presets/pov/lamp/23_(30000K)_40W_Black_Light_Bulb.py
new file mode 100644
index 00000000..baad8234
--- /dev/null
+++ b/presets/pov/lamp/23_(30000K)_40W_Black_Light_Bulb.py
@@ -0,0 +1,10 @@
+#Starting from 1918 (World War I Wood's glass)
+
+import bpy
+bpy.context.object.data.type = 'POINT'
+lampdata = bpy.context.object.data
+
+lampdata.color = (0.6549019813537598, 0.0, 1.0)
+lampdata.energy = 1.86515#100/21.446 #lumen values/21.446 or lux when available used as a basis
+lampdata.distance = 0.01
+lampdata.falloff_type = 'INVERSE_SQUARE'
diff --git a/presets/pov/lamp/24_(1850K)_Candle.py b/presets/pov/lamp/24_(1850K)_Candle.py
new file mode 100644
index 00000000..5d7ac61d
--- /dev/null
+++ b/presets/pov/lamp/24_(1850K)_Candle.py
@@ -0,0 +1,24 @@
+#Starting from 1825 (stearin)
+
+import bpy
+bpy.context.object.data.type = 'POINT'
+lampdata = bpy.context.object.data
+
+lampdata.color = (1.0, 0.7176470756530762, 0.2980392277240753)
+#lampdata.color = (1.0, 0.5764706134796143, 0.16078431904315948)
+#http://terpconnect.umd.edu/~pbs/2011-Sunderland-et-al-PCI.pdf
+#https://blog.1000bulbs.com/home/whats-the-difference-between-candela-lux-and-lumens
+#Environment Typical Lux
+#Hospital Theatre 1,000
+#Supermarket, Workshop, Sports Hall 750
+#Office, Show Rooms, Laboratories, Kitchens 500
+#Warehouse Loading Bays 300 to 400
+#School Classroom, University Lecture Hall 250
+#Lobbies, Public Corridors, Stairwells 200
+#Warehouse Aisles 100 to 200
+#Homes, Theatres 150
+#Family Living Room 50
+#Sunset & Sunrise 400 lux
+lampdata.energy = 2.0 #two times lux value
+lampdata.distance = 0.004
+lampdata.falloff_type = 'INVERSE_SQUARE'
diff --git a/presets/pov/radiosity/01_Debug.py b/presets/pov/radiosity/01_Debug.py
new file mode 100644
index 00000000..420dbd7e
--- /dev/null
+++ b/presets/pov/radiosity/01_Debug.py
@@ -0,0 +1,20 @@
+import bpy
+scene = bpy.context.scene
+
+scene.pov.radio_display_advanced = True
+scene.pov.radio_adc_bailout = 0.005
+scene.pov.radio_always_sample = False
+scene.pov.radio_brightness = 1.0
+scene.pov.radio_count = 10
+scene.pov.radio_error_bound = 0.3
+scene.pov.radio_gray_threshold = 0.0
+scene.pov.radio_low_error_factor = 0.8
+scene.pov.radio_media = False
+scene.pov.radio_subsurface = False
+scene.pov.radio_minimum_reuse = 0.015
+scene.pov.radio_maximum_reuse = 0.2
+scene.pov.radio_nearest_count = 1
+scene.pov.radio_normal = False
+scene.pov.radio_recursion_limit = 1
+scene.pov.radio_pretrace_start = 0.08
+scene.pov.radio_pretrace_end = 0.01
diff --git a/presets/pov/radiosity/02_Fast.py b/presets/pov/radiosity/02_Fast.py
new file mode 100644
index 00000000..08864c83
--- /dev/null
+++ b/presets/pov/radiosity/02_Fast.py
@@ -0,0 +1,19 @@
+import bpy
+scene = bpy.context.scene
+
+scene.pov.radio_adc_bailout = 0.005
+scene.pov.radio_always_sample = False
+scene.pov.radio_brightness = 1.0
+scene.pov.radio_count = 80
+scene.pov.radio_error_bound = 0.4
+scene.pov.radio_gray_threshold = 0.0
+scene.pov.radio_low_error_factor = 0.9
+scene.pov.radio_media = False
+scene.pov.radio_subsurface = False
+scene.pov.radio_minimum_reuse = 0.025
+scene.pov.radio_maximum_reuse = 0.2
+scene.pov.radio_nearest_count = 5
+scene.pov.radio_normal = False
+scene.pov.radio_recursion_limit = 1
+scene.pov.radio_pretrace_start = 0.08
+scene.pov.radio_pretrace_end = 0.02
diff --git a/presets/pov/radiosity/03_Normal.py b/presets/pov/radiosity/03_Normal.py
new file mode 100644
index 00000000..b66a1b2a
--- /dev/null
+++ b/presets/pov/radiosity/03_Normal.py
@@ -0,0 +1,20 @@
+import bpy
+scene = bpy.context.scene
+
+scene.pov.radio_display_advanced = True
+scene.pov.radio_adc_bailout = 0.005
+scene.pov.radio_always_sample = False
+scene.pov.radio_brightness = 1.0
+scene.pov.radio_count = 200
+scene.pov.radio_error_bound = 0.3
+scene.pov.radio_gray_threshold = 0.0
+scene.pov.radio_low_error_factor = 0.75
+scene.pov.radio_media = False
+scene.pov.radio_subsurface = False
+scene.pov.radio_minimum_reuse = 0.017
+scene.pov.radio_maximum_reuse = 0.2
+scene.pov.radio_nearest_count = 7
+scene.pov.radio_normal = False
+scene.pov.radio_recursion_limit = 1
+scene.pov.radio_pretrace_start = 0.08
+scene.pov.radio_pretrace_end = 0.01
diff --git a/presets/pov/radiosity/04_Two_Bounces.py b/presets/pov/radiosity/04_Two_Bounces.py
new file mode 100644
index 00000000..2072247a
--- /dev/null
+++ b/presets/pov/radiosity/04_Two_Bounces.py
@@ -0,0 +1,20 @@
+import bpy
+scene = bpy.context.scene
+
+scene.pov.radio_display_advanced = True
+scene.pov.radio_adc_bailout = 0.005
+scene.pov.radio_always_sample = False
+scene.pov.radio_brightness = 1.0
+scene.pov.radio_count = 200
+scene.pov.radio_error_bound = 0.3
+scene.pov.radio_gray_threshold = 0.0
+scene.pov.radio_low_error_factor = 0.75
+scene.pov.radio_media = False
+scene.pov.radio_subsurface = False
+scene.pov.radio_minimum_reuse = 0.017
+scene.pov.radio_maximum_reuse = 0.2
+scene.pov.radio_nearest_count = 7
+scene.pov.radio_normal = False
+scene.pov.radio_recursion_limit = 2
+scene.pov.radio_pretrace_start = 0.08
+scene.pov.radio_pretrace_end = 0.01
diff --git a/presets/pov/radiosity/05_Final.py b/presets/pov/radiosity/05_Final.py
new file mode 100644
index 00000000..52a9d5c3
--- /dev/null
+++ b/presets/pov/radiosity/05_Final.py
@@ -0,0 +1,20 @@
+import bpy
+scene = bpy.context.scene
+
+scene.pov.radio_display_advanced = True
+scene.pov.radio_adc_bailout = 0.005
+scene.pov.radio_always_sample = False
+scene.pov.radio_brightness = 1.0
+scene.pov.radio_count = 800
+scene.pov.radio_error_bound = 0.2
+scene.pov.radio_gray_threshold = 0.0
+scene.pov.radio_low_error_factor = 0.7
+scene.pov.radio_media = False
+scene.pov.radio_subsurface = False
+scene.pov.radio_minimum_reuse = 0.01
+scene.pov.radio_maximum_reuse = 0.2
+scene.pov.radio_nearest_count = 9
+scene.pov.radio_normal = False
+scene.pov.radio_recursion_limit = 1
+scene.pov.radio_pretrace_start = 0.08
+scene.pov.radio_pretrace_end = 0.01
diff --git a/presets/pov/radiosity/06_Outdoor_Low_Quality.py b/presets/pov/radiosity/06_Outdoor_Low_Quality.py
new file mode 100644
index 00000000..53553264
--- /dev/null
+++ b/presets/pov/radiosity/06_Outdoor_Low_Quality.py
@@ -0,0 +1,20 @@
+import bpy
+scene = bpy.context.scene
+
+scene.pov.radio_display_advanced = True
+scene.pov.radio_adc_bailout = 0.005
+scene.pov.radio_always_sample = False
+scene.pov.radio_brightness = 1.0
+scene.pov.radio_count = 80
+scene.pov.radio_error_bound = 0.6
+scene.pov.radio_gray_threshold = 0.0
+scene.pov.radio_low_error_factor = 0.8
+scene.pov.radio_media = False
+scene.pov.radio_subsurface = False
+scene.pov.radio_minimum_reuse = 0.015
+scene.pov.radio_maximum_reuse = 0.2
+scene.pov.radio_nearest_count = 4
+scene.pov.radio_normal = False
+scene.pov.radio_recursion_limit = 1
+scene.pov.radio_pretrace_start = 0.08
+scene.pov.radio_pretrace_end = 0.01
diff --git a/presets/pov/radiosity/07_Outdoor_High_Quality.py b/presets/pov/radiosity/07_Outdoor_High_Quality.py
new file mode 100644
index 00000000..8b06dabe
--- /dev/null
+++ b/presets/pov/radiosity/07_Outdoor_High_Quality.py
@@ -0,0 +1,20 @@
+import bpy
+scene = bpy.context.scene
+
+scene.pov.radio_display_advanced = True
+scene.pov.radio_adc_bailout = 0.005
+scene.pov.radio_always_sample = False
+scene.pov.radio_brightness = 1.0
+scene.pov.radio_count = 500
+scene.pov.radio_error_bound = 0.1
+scene.pov.radio_gray_threshold = 0.0
+scene.pov.radio_low_error_factor = 0.5
+scene.pov.radio_media = False
+scene.pov.radio_subsurface = False
+scene.pov.radio_minimum_reuse = 0.015
+scene.pov.radio_maximum_reuse = 0.2
+scene.pov.radio_nearest_count = 7
+scene.pov.radio_normal = False
+scene.pov.radio_recursion_limit = 1
+scene.pov.radio_pretrace_start = 0.08
+scene.pov.radio_pretrace_end = 0.004
diff --git a/presets/pov/radiosity/08_Outdoor_(Sun)Light.py b/presets/pov/radiosity/08_Outdoor_(Sun)Light.py
new file mode 100644
index 00000000..94448c1f
--- /dev/null
+++ b/presets/pov/radiosity/08_Outdoor_(Sun)Light.py
@@ -0,0 +1,20 @@
+import bpy
+scene = bpy.context.scene
+
+scene.pov.radio_display_advanced = True
+scene.pov.radio_adc_bailout = 0.005
+scene.pov.radio_always_sample = False
+scene.pov.radio_brightness = 1.0
+scene.pov.radio_count = 50
+scene.pov.radio_error_bound = 0.8
+scene.pov.radio_gray_threshold = 0.0
+scene.pov.radio_low_error_factor = 0.9
+scene.pov.radio_media = False
+scene.pov.radio_subsurface = False
+scene.pov.radio_minimum_reuse = 0.015
+scene.pov.radio_maximum_reuse = 0.2
+scene.pov.radio_nearest_count = 4
+scene.pov.radio_normal = False
+scene.pov.radio_recursion_limit = 1
+scene.pov.radio_pretrace_start = 0.08
+scene.pov.radio_pretrace_end = 0.01
diff --git a/presets/pov/radiosity/09_Indoor_Low_Quality.py b/presets/pov/radiosity/09_Indoor_Low_Quality.py
new file mode 100644
index 00000000..e5fa7bb6
--- /dev/null
+++ b/presets/pov/radiosity/09_Indoor_Low_Quality.py
@@ -0,0 +1,20 @@
+import bpy
+scene = bpy.context.scene
+
+scene.pov.radio_display_advanced = True
+scene.pov.radio_adc_bailout = 0.005
+scene.pov.radio_always_sample = False
+scene.pov.radio_brightness = 1.0
+scene.pov.radio_count = 80
+scene.pov.radio_error_bound = 0.7
+scene.pov.radio_gray_threshold = 0.0
+scene.pov.radio_low_error_factor = 0.8
+scene.pov.radio_media = False
+scene.pov.radio_subsurface = False
+scene.pov.radio_minimum_reuse = 0.015
+scene.pov.radio_maximum_reuse = 0.2
+scene.pov.radio_nearest_count = 5
+scene.pov.radio_normal = False
+scene.pov.radio_recursion_limit = 2
+scene.pov.radio_pretrace_start = 0.08
+scene.pov.radio_pretrace_end = 0.01
diff --git a/presets/pov/radiosity/10_Indoor_High_Quality.py b/presets/pov/radiosity/10_Indoor_High_Quality.py
new file mode 100644
index 00000000..5a752f6c
--- /dev/null
+++ b/presets/pov/radiosity/10_Indoor_High_Quality.py
@@ -0,0 +1,20 @@
+import bpy
+scene = bpy.context.scene
+
+scene.pov.radio_display_advanced = True
+scene.pov.radio_adc_bailout = 0.005
+scene.pov.radio_always_sample = False
+scene.pov.radio_brightness = 1.0
+scene.pov.radio_count = 400
+scene.pov.radio_error_bound = 0.15
+scene.pov.radio_gray_threshold = 0.0
+scene.pov.radio_low_error_factor = 0.5
+scene.pov.radio_media = False
+scene.pov.radio_subsurface = False
+scene.pov.radio_minimum_reuse = 0.015
+scene.pov.radio_maximum_reuse = 0.2
+scene.pov.radio_nearest_count = 8
+scene.pov.radio_normal = False
+scene.pov.radio_recursion_limit = 3
+scene.pov.radio_pretrace_start = 0.08
+scene.pov.radio_pretrace_end = 0.004
diff --git a/presets/pov/world/1_Clear_Blue_Sky.py b/presets/pov/world/1_Clear_Blue_Sky.py
new file mode 100644
index 00000000..0dcd5e05
--- /dev/null
+++ b/presets/pov/world/1_Clear_Blue_Sky.py
@@ -0,0 +1,36 @@
+import bpy
+scene = bpy.context.scene
+
+scene.world.use_sky_blend = True
+#below multiplied by two for a better proportion Clear vs Overcast sky
+#since Clear sky is 19807 lux vs 2000 for overcast (sun is min 32000 max 100000)
+#http://www.pssurvival.com/PS/Lighting/Typical_LUX_Intensities_for_Day_and_Night-2017.pdf
+#https://en.wikipedia.org/wiki/Daylight
+#https://www.engineeringtoolbox.com/light-level-rooms-d_708.html
+#https://www.cactus2000.de/fr/unit/masslux.shtml
+#https://blendergrid.com/news/cycles-physically-correct-brightness
+#researched result blue is
+ #Hue: 0.6
+ #Saturation: 0.533
+ #Lightness: 0.7
+#put scattering scale at 0.0002 and scattering color rgb <0.2061, 0.3933, 1.0>
+#with very small value like round rgb 0.00002 0.00002 0.00008
+#Sky color at zenith, sun 90° elevation = hsl <0.6, 0.533, 0.698>
+#Ground color = rgb <0.996, 0.965, 0.855> = hsl <0.128, 0.141, 0.996>
+#Ground Brighness = 0.996
+
+scene.world.horizon_color = (0.047, 0.034, 0.025) #24000 or 22000 lux roughly equals (sun+sky)/5
+scene.world.zenith_color = (0.006, 0.013, 0.033) #19807 lux roughly equals hign noon Sun / 5
+scene.world.ambient_color = (0.0, 0.0, 0.0)
+scene.world.mist_settings.use_mist = False
+scene.world.mist_settings.intensity = 0.0
+scene.world.mist_settings.depth = 25.0
+scene.world.mist_settings.start = 5.0
+scene.pov.media_enable = True
+scene.pov.media_scattering_type = '4'
+scene.pov.media_samples = 35
+scene.pov.media_diffusion_scale = (0.00002)
+scene.pov.media_diffusion_color = (0.000001, 0.000002, 0.000005)
+scene.pov.media_absorption_scale = (0.00002)
+scene.pov.media_absorption_color = (0.0000006067, 0.0000007939, 0.0)#up to 0.00007
+scene.pov.media_eccentricity = 0.0
diff --git a/presets/pov/world/2_Partly_Hazy_Sky.py b/presets/pov/world/2_Partly_Hazy_Sky.py
new file mode 100644
index 00000000..2f6b628d
--- /dev/null
+++ b/presets/pov/world/2_Partly_Hazy_Sky.py
@@ -0,0 +1,36 @@
+import bpy
+scene = bpy.context.scene
+
+scene.world.use_sky_blend = True
+#below multiplied by two for a better proportion Clear vs Overcast sky
+#since Clear sky is 19807 lux vs 2000 for overcast (sun is min 32000 max 100000)
+#http://www.pssurvival.com/PS/Lighting/Typical_LUX_Intensities_for_Day_and_Night-2017.pdf
+#https://en.wikipedia.org/wiki/Daylight
+#https://www.engineeringtoolbox.com/light-level-rooms-d_708.html
+#https://www.cactus2000.de/fr/unit/masslux.shtml
+#https://blendergrid.com/news/cycles-physically-correct-brightness
+#researched result blue is
+ #Hue: 0.6
+ #Saturation: 0.533
+ #Lightness: 0.7
+#put scattering scale at 0.0002 and scattering color rgb <0.2061, 0.3933, 1.0>
+#with very small value like round rgb 0.00002 0.00002 0.00008
+#Sky color at zenith, sun 90° elevation = hsl <0.6, 0.533, 0.698>
+#Ground color = rgb <0.996, 0.965, 0.855> = hsl <0.128, 0.141, 0.996>
+#Ground Brighness = 0.996
+
+scene.world.horizon_color = (0.380, 0.262, 0.183) #24000 or 22000 lux roughly equals (sun+sky)/5 + urban light pollution
+scene.world.zenith_color = (0.006, 0.013, 0.033) #19807 lux roughly equals hign noon Sun / 5
+scene.world.ambient_color = (0.0, 0.0, 0.0)
+scene.world.mist_settings.use_mist = False
+scene.world.mist_settings.intensity = 0.0
+scene.world.mist_settings.depth = 25.0
+scene.world.mist_settings.start = 5.0
+scene.pov.media_enable = True
+scene.pov.media_scattering_type = '4'
+scene.pov.media_samples = 35
+scene.pov.media_diffusion_scale = (0.00002)
+scene.pov.media_diffusion_color = (0.000001, 0.000002, 0.000005)
+scene.pov.media_absorption_scale = (0.00002)
+scene.pov.media_absorption_color = (0.0000006067, 0.0000007939, 0.0)#up to 0.00007
+scene.pov.media_eccentricity = 0.0
diff --git a/presets/pov/world/3_Overcast_Sky.py b/presets/pov/world/3_Overcast_Sky.py
new file mode 100644
index 00000000..013a5d40
--- /dev/null
+++ b/presets/pov/world/3_Overcast_Sky.py
@@ -0,0 +1,20 @@
+import bpy
+scene = bpy.context.scene
+
+scene.world.use_sky_blend = True
+scene.world.horizon_color = (0.477, 0.536, 0.604)
+#below divided by ten for a better proportion Clear vs Overcast sky
+#since Clear sky is 20000 lux vs 2000 up to 10000 for overcast
+scene.world.zenith_color = (0.034, 0.043, 0.047)
+scene.world.ambient_color = (0.0, 0.0, 0.0)
+scene.world.mist_settings.use_mist = False
+scene.world.mist_settings.intensity = 0.0
+scene.world.mist_settings.depth = 25.0
+scene.world.mist_settings.start = 5.0
+scene.pov.media_enable = False
+scene.pov.media_scattering_type = '1'
+scene.pov.media_samples = 35
+scene.pov.media_diffusion_scale = (1.0)
+scene.pov.media_diffusion_color = (0.58, 0.66, 0.75)
+scene.pov.media_absorption_color = (0.0, 0.0, 0.0)
+scene.pov.media_eccentricity = 0.0
diff --git a/presets/pov/world/4_Cartoony_Sky.py b/presets/pov/world/4_Cartoony_Sky.py
new file mode 100644
index 00000000..e3c552c0
--- /dev/null
+++ b/presets/pov/world/4_Cartoony_Sky.py
@@ -0,0 +1,19 @@
+import bpy
+scene = bpy.context.scene
+
+scene.world.use_sky_blend = True
+#below multiplied by two for a better proportion Clear vs Overcast sky
+#since Clear sky is 20000 lux vs 2000 for overcast
+scene.world.horizon_color = (0.350*2, 0.611*2, 1.0*2)
+scene.world.zenith_color = (0.05000000074505806*2, 0.125*2, 0.5*2)
+scene.world.ambient_color = (0.0, 0.0, 0.0)
+scene.world.mist_settings.use_mist = False
+scene.world.mist_settings.intensity = 0.0
+scene.world.mist_settings.depth = 25.0
+scene.world.mist_settings.start = 5.0
+scene.pov.media_enable = False
+scene.pov.media_scattering_type = '4'
+scene.pov.media_samples = 35
+scene.pov.media_diffusion_color = (0.20000000298023224, 0.4000000059604645, 1.0)
+scene.pov.media_absorption_color = (0.0, 0.0, 0.0)
+scene.pov.media_eccentricity = 0.0 \ No newline at end of file
diff --git a/presets/pov/world/5_Under_Water.py b/presets/pov/world/5_Under_Water.py
new file mode 100644
index 00000000..e6659016
--- /dev/null
+++ b/presets/pov/world/5_Under_Water.py
@@ -0,0 +1,19 @@
+import bpy
+scene = bpy.context.scene
+
+scene.world.use_sky_blend = True
+#below multiplied by two for a better proportion Clear vs Overcast sky
+#since Clear sky is 20000 lux vs 2000 for overcast
+scene.world.horizon_color = (0.0, 0.0, 0.0)
+scene.world.zenith_color = (0.250980406999588, 0.6117647290229797, 1.0)
+scene.world.ambient_color = (0.0, 0.0, 0.0)
+scene.world.mist_settings.use_mist = False
+scene.world.mist_settings.intensity = 0.0
+scene.world.mist_settings.depth = 25.0
+scene.world.mist_settings.start = 5.0
+scene.pov.media_enable = True
+scene.pov.media_scattering_type = '5'
+scene.pov.media_samples = 35
+scene.pov.media_diffusion_color = (0.000034, 0.000034, 0.000017)
+scene.pov.media_absorption_color = (0.00000455, 0.00000165, 0.00000031)
+scene.pov.media_eccentricity = 0.7 \ No newline at end of file
diff --git a/render_povray/__init__.py b/render_povray/__init__.py
index cfb45f5a..90d80f13 100644
--- a/render_povray/__init__.py
+++ b/render_povray/__init__.py
@@ -19,14 +19,13 @@
# <pep8 compliant>
bl_info = {
- "name": "POVRAY-3.7",
+ "name": "POV-3.7",
"author": "Campbell Barton, Silvio Falcinelli, Maurice Raybaud, "
"Constantin Rahn, Bastien Montagne, Leonid Desyatkov",
- "version": (0, 0, 9),
- "blender": (2, 75, 0),
+ "version": (0, 1, 0),
+ "blender": (2, 79, 0),
"location": "Render > Engine > POV-Ray 3.7",
"description": "Basic POV-Ray 3.7 integration for blender",
- "warning": "this script is RC",
"wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
"Scripts/Render/POV-Ray",
"category": "Render",
@@ -44,6 +43,7 @@ else:
#import addon_utils # To use some other addons
import nodeitems_utils #for Nodes
from nodeitems_utils import NodeCategory, NodeItem #for Nodes
+ from bl_operators.presets import AddPresetBase
from bpy.types import (
AddonPreferences,
PropertyGroup,
@@ -73,6 +73,11 @@ def string_strip_hyphen(name):
# Scene POV properties.
###############################################################################
class RenderPovSettingsScene(PropertyGroup):
+ #Linux SDL-window enable
+ sdl_window_enable = BoolProperty(
+ name="Enable SDL window",
+ description="Enable the SDL window in Linux OS",
+ default=True)
# File Options
text_block = StringProperty(
name="Text Scene Name",
@@ -121,7 +126,7 @@ class RenderPovSettingsScene(PropertyGroup):
radio_enable = BoolProperty(
name="Enable Radiosity",
description="Enable POV-Rays radiosity calculation",
- default=False)
+ default=True)
radio_display_advanced = BoolProperty(
name="Advanced Options",
@@ -132,19 +137,88 @@ class RenderPovSettingsScene(PropertyGroup):
name="Enable Media",
description="Enable POV-Rays atmospheric media",
default=False)
+
media_samples = IntProperty(
name="Samples",
description="Number of samples taken from camera to first object "
"encountered along ray path for media calculation",
min=1, max=100, default=35)
-
- media_color = FloatVectorProperty(
- name="Media Color", description="The atmospheric media color",
+
+ media_scattering_type = EnumProperty(
+ name="Scattering Type",
+ description="Scattering model",
+ items=(('1', "1 Isotropic", "The simplest form of scattering because"
+ " it is independent of direction."),
+ ('2', "2 Mie haze ", "For relatively small particles such as "
+ "minuscule water droplets of fog, cloud "
+ "particles, and particles responsible "
+ "for the polluted sky. In this model the"
+ " scattering is extremely directional in"
+ " the forward direction i.e. the amount "
+ "of scattered light is largest when the "
+ "incident light is anti-parallel to the "
+ "viewing direction (the light goes "
+ "directly to the viewer). It is smallest"
+ " when the incident light is parallel to"
+ " the viewing direction. "),
+ ('3', "3 Mie murky", "Like haze but much more directional"),
+ ('4', "4 Rayleigh", "For extremely small particles such as "
+ "molecules of the air. The amount of "
+ "scattered light depends on the incident"
+ " light angle. It is largest when the "
+ "incident light is parallel or "
+ "anti-parallel to the viewing direction "
+ "and smallest when the incident light is "
+ "perpendicular to viewing direction."),
+ ('5', "5 Henyey-Greenstein", "The default eccentricity value "
+ "of zero defines isotropic "
+ "scattering while positive "
+ "values lead to scattering in "
+ "the direction of the light and "
+ "negative values lead to "
+ "scattering in the opposite "
+ "direction of the light. Larger "
+ "values of e (or smaller values "
+ "in the negative case) increase "
+ "the directional property of the"
+ " scattering.")),
+ default='1')
+
+ media_diffusion_scale = FloatProperty(
+ name="Scale", description="Scale factor of Media Diffusion Color",
+ precision=12, step=0.00000001, min=0.000000001, max=1.0,
+ default=(1.0))
+
+ media_diffusion_color = FloatVectorProperty(
+ name="Media Diffusion Color", description="The atmospheric media color",
precision=4, step=0.01, min=0, soft_max=1,
default=(0.001, 0.001, 0.001),
options={'ANIMATABLE'},
subtype='COLOR')
+ media_absorption_scale = FloatProperty(
+ name="Scale", description="Scale factor of Media Absorption Color. "
+ "use 1/depth of media volume in meters",
+ precision=12, step=0.000001, min=0.000000001, max=1.0,
+ default=(0.00002))
+
+ media_absorption_color = FloatVectorProperty(
+ name="Media Absorption Color", description="The atmospheric media absorption color",
+ precision=4, step=0.01, min=0, soft_max=1,
+ default=(0.0, 0.0, 0.0),
+ options={'ANIMATABLE'},
+ subtype='COLOR')
+
+ media_eccentricity = FloatProperty(
+ name="Media Eccenticity Factor", description="Positive values lead"
+ " to scattering in the direction of the light and negative "
+ "values lead to scattering in the opposite direction of the "
+ "light. Larger values of e (or smaller values in the negative"
+ " case) increase the directional property of the scattering.",
+ precision=2, step=0.01, min=-1.0, max=1.0,
+ default=(0.0),
+ options={'ANIMATABLE'})
+
baking_enable = BoolProperty(
name="Enable Baking",
description="Enable POV-Rays texture baking",
@@ -377,7 +451,7 @@ class RenderPovSettingsScene(PropertyGroup):
description="",
maxlen=1024, subtype="FILE_PATH")
-
+ #########RADIOSITY########
radio_adc_bailout = FloatProperty(
name="ADC Bailout",
description="The adc_bailout for radiosity rays. Use "
@@ -422,7 +496,7 @@ class RenderPovSettingsScene(PropertyGroup):
radio_media = BoolProperty(
name="Media", description="Radiosity estimation can be affected by media",
- default=False)
+ default=True)
radio_subsurface = BoolProperty(
name="Subsurface", description="Radiosity estimation can be affected by Subsurface Light Transport",
@@ -468,7 +542,6 @@ class RenderPovSettingsScene(PropertyGroup):
"in the mosaic preview last pass",
min=0.000925, max=1.00, soft_min=0.01, soft_max=1.00, default=0.04, precision=3)
-
###############################################################################
# Material POV properties.
###############################################################################
@@ -2199,20 +2272,14 @@ class PovrayPreferences(AddonPreferences):
-
-
-
-
-
-
-
-
-
def register():
bpy.utils.register_module(__name__)
bpy.types.INFO_MT_add.prepend(ui.menu_func_add)
bpy.types.INFO_MT_file_import.append(ui.menu_func_import)
bpy.types.TEXT_MT_templates.append(ui.menu_func_templates)
+ bpy.types.RENDER_PT_povray_radiosity.prepend(ui.rad_panel_func)
+ bpy.types.LAMP_PT_POV_lamp.prepend(ui.lamp_panel_func)
+ bpy.types.WORLD_PT_world.prepend(ui.world_panel_func)
# was used for parametric objects but made the other addon unreachable on
# unregister for other tools to use created a user action call instead
#addon_utils.enable("add_mesh_extra_objects", default_set=False, persistent=True)
@@ -2244,6 +2311,9 @@ def unregister():
#bpy.types.TEXTURE_PT_context_texture.remove(TEXTURE_PT_povray_type)
#addon_utils.disable("add_mesh_extra_objects", default_set=False)
+ bpy.types.WORLD_PT_world.remove(ui.world_panel_func)
+ bpy.types.LAMP_PT_POV_lamp.remove(ui.lamp_panel_func)
+ bpy.types.RENDER_PT_povray_radiosity.remove(ui.rad_panel_func)
bpy.types.TEXT_MT_templates.remove(ui.menu_func_templates)
bpy.types.INFO_MT_file_import.remove(ui.menu_func_import)
bpy.types.INFO_MT_add.remove(ui.menu_func_add)
diff --git a/render_povray/render.py b/render_povray/render.py
index d3695780..9838f25d 100644
--- a/render_povray/render.py
+++ b/render_povray/render.py
@@ -186,13 +186,17 @@ def safety(name, Level):
##############end safety string name material
##############################EndSF###########################
+csg_list = []
+
def is_renderable(scene, ob):
- return (ob.is_visible(scene) and not ob.hide_render)
+ return (ob.is_visible(scene) and not ob.hide_render and ob not in csg_list)
def renderable_objects(scene):
return [ob for ob in bpy.data.objects if is_renderable(scene, ob)]
+def no_renderable_objects(scene):
+ return [ob for ob in csg_list]
tabLevel = 0
unpacked_images=[]
@@ -628,7 +632,7 @@ def write_pov(filename, scene=None, info_callback=None):
tabWrite("fade_distance %.6f\n" % (lamp.distance / 2.0))
# Area lights have no falloff type, so always use blenders lamp quad equivalent
# for those?
- tabWrite("fade_power %d\n" % 0)
+ tabWrite("fade_power %d\n" % 2)
size_x = lamp.size
samples_x = lamp.shadow_ray_samples_x
if lamp.shape == 'SQUARE':
@@ -1446,7 +1450,7 @@ def write_pov(filename, scene=None, info_callback=None):
file.write(' BuildWriteMesh2(VecArr, NormArr, UVArr, Iter_U, Iter_V, FileName)\n')
file.write(' #end\n')
file.write('#end\n\n')
- # Empty curves
+ # Empty curves
if len(ob.data.splines)==0:
tabWrite("\n//dummy sphere to represent empty curve location\n")
tabWrite("#declare %s =\n"%dataname)
@@ -1793,7 +1797,7 @@ def write_pov(filename, scene=None, info_callback=None):
# objectNames = {}
DEF_OBJ_NAME = "Default"
- def exportMeshes(scene, sel):
+ def exportMeshes(scene, sel, csg):
# obmatslist = []
# def hasUniqueMaterial():
# # Grab materials attached to object instances ...
@@ -2416,7 +2420,7 @@ def write_pov(filename, scene=None, info_callback=None):
tabWrite("#declare %s = plane{ <0,0,1>,1\n"%povdataname)
povMatName = "Default_texture"
if ob.active_material:
- #povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
+ #povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
try:
material = ob.active_material
writeObjectMaterial(material, ob)
@@ -3009,7 +3013,7 @@ def write_pov(filename, scene=None, info_callback=None):
# POV object modifiers such as
# hollow / sturm / double_illuminate etc.
write_object_modifiers(scene,ob,file)
-
+
#Importance for radiosity sampling added here:
tabWrite("radiosity { \n")
tabWrite("importance %3g \n" % importance)
@@ -3254,7 +3258,7 @@ def write_pov(filename, scene=None, info_callback=None):
ob.pov.inside_vector[1],
ob.pov.inside_vector[2]))
onceCSG = 1
-
+
if me.materials:
try:
material = me.materials[0] # dodgy
@@ -3277,38 +3281,69 @@ def write_pov(filename, scene=None, info_callback=None):
bpy.data.meshes.remove(me)
- duplidata_ref = []
- for ob in sel:
- #matrix = global_matrix * ob.matrix_world
- if ob.is_duplicator:
- tabWrite("\n//--DupliObjects in %s--\n\n"% ob.name)
- ob.dupli_list_create(scene)
- dup = ""
- if ob.is_modified(scene, 'RENDER'):
- #modified object always unique so using object name rather than data name
- dup = "#declare OB%s = union{\n" %(string_strip_hyphen(bpy.path.clean_name(ob.name)))
+ if csg:
+ duplidata_ref = []
+ for ob in sel:
+ #matrix = global_matrix * ob.matrix_world
+ if ob.is_duplicator:
+ tabWrite("\n//--DupliObjects in %s--\n\n"% ob.name)
+ ob.dupli_list_create(scene)
+ dup = ""
+ if ob.is_modified(scene, 'RENDER'):
+ #modified object always unique so using object name rather than data name
+ dup = "#declare OB%s = union{\n" %(string_strip_hyphen(bpy.path.clean_name(ob.name)))
+ else:
+ dup = "#declare DATA%s = union{\n" %(string_strip_hyphen(bpy.path.clean_name(ob.name)))
+ for eachduplicate in ob.dupli_list:
+ duplidataname = "OB"+string_strip_hyphen(bpy.path.clean_name(bpy.data.objects[eachduplicate.object.name].data.name))
+ dup += ("\tobject {\n\t\tDATA%s\n\t\t%s\t}\n" %(string_strip_hyphen(bpy.path.clean_name(bpy.data.objects[eachduplicate.object.name].data.name)), MatrixAsPovString(ob.matrix_world.inverted() * eachduplicate.matrix)))
+ #add object to a list so that it is not rendered for some dupli_types
+ if ob.dupli_type not in {'GROUP'} and duplidataname not in duplidata_ref:
+ duplidata_ref.append(duplidataname) #older key [string_strip_hyphen(bpy.path.clean_name("OB"+ob.name))]
+ dup += "}\n"
+ ob.dupli_list_clear()
+ tabWrite(dup)
else:
- dup = "#declare DATA%s = union{\n" %(string_strip_hyphen(bpy.path.clean_name(ob.name)))
- for eachduplicate in ob.dupli_list:
- duplidataname = "OB"+string_strip_hyphen(bpy.path.clean_name(bpy.data.objects[eachduplicate.object.name].data.name))
- dup += ("\tobject {\n\t\tDATA%s\n\t\t%s\t}\n" %(string_strip_hyphen(bpy.path.clean_name(bpy.data.objects[eachduplicate.object.name].data.name)), MatrixAsPovString(ob.matrix_world.inverted() * eachduplicate.matrix)))
- #add object to a list so that it is not rendered for some dupli_types
- if ob.dupli_type not in {'GROUP'} and duplidataname not in duplidata_ref:
- duplidata_ref.append(duplidataname) #older key [string_strip_hyphen(bpy.path.clean_name("OB"+ob.name))]
- dup += "}\n"
- ob.dupli_list_clear()
- tabWrite(dup)
- else:
- continue
- print(duplidata_ref)
- for data_name, inst in data_ref.items():
- for ob_name, matrix_str in inst:
- if ob_name not in duplidata_ref: #.items() for a dictionary
- tabWrite("\n//----Blender Object Name:%s----\n" % ob_name)
- tabWrite("object { \n")
- tabWrite("%s\n" % data_name)
- tabWrite("%s\n" % matrix_str)
- tabWrite("}\n")
+ continue
+ print(duplidata_ref)
+ for data_name, inst in data_ref.items():
+ for ob_name, matrix_str in inst:
+ if ob_name not in duplidata_ref: #.items() for a dictionary
+ tabWrite("\n//----Blender Object Name:%s----\n" % ob_name)
+ if ob.pov.object_as == '':
+ tabWrite("object { \n")
+ tabWrite("%s\n" % data_name)
+ tabWrite("%s\n" % matrix_str)
+ tabWrite("}\n")
+ else:
+ no_boolean = True
+ for mod in ob.modifiers:
+ if mod.type == 'BOOLEAN':
+ operation = None
+ no_boolean = False
+ if mod.operation == 'INTERSECT':
+ operation = 'intersection'
+ else:
+ operation = mod.operation.lower()
+ mod_ob_name = string_strip_hyphen(bpy.path.clean_name(mod.object.name))
+ mod_matrix = global_matrix * mod.object.matrix_world
+ mod_ob_matrix = MatrixAsPovString(mod_matrix)
+ tabWrite("%s { \n"%operation)
+ tabWrite("object { \n")
+ tabWrite("%s\n" % data_name)
+ tabWrite("%s\n" % matrix_str)
+ tabWrite("}\n")
+ tabWrite("object { \n")
+ tabWrite("%s\n" % ('DATA'+ mod_ob_name))
+ tabWrite("%s\n" % mod_ob_matrix)
+ tabWrite("}\n")
+ tabWrite("}\n")
+ break
+ if no_boolean:
+ tabWrite("object { \n")
+ tabWrite("%s\n" % data_name)
+ tabWrite("%s\n" % matrix_str)
+ tabWrite("}\n")
def exportWorld(world):
render = scene.render
@@ -3444,18 +3479,33 @@ def write_pov(filename, scene=None, info_callback=None):
if mist.use_mist:
tabWrite("fog {\n")
- tabWrite("distance %.6f\n" % mist.depth)
+ if mist.falloff=='LINEAR':
+ tabWrite("distance %.6f\n" % ((mist.start+mist.depth)*0.368))
+ elif mist.falloff=='QUADRATIC': # n**2 or squrt(n)?
+ tabWrite("distance %.6f\n" % ((mist.start+mist.depth)**2*0.368))
+ elif mist.falloff=='INVERSE_QUADRATIC': # n**2 or squrt(n)?
+ tabWrite("distance %.6f\n" % ((mist.start+mist.depth)**2*0.368))
tabWrite("color rgbt<%.3g, %.3g, %.3g, %.3g>\n" % \
(*world.horizon_color, 1.0 - mist.intensity))
- #tabWrite("fog_offset %.6f\n" % mist.start)
- #tabWrite("fog_alt 5\n")
+ #tabWrite("fog_offset %.6f\n" % mist.start) #create a pov property to prepend
+ #tabWrite("fog_alt %.6f\n" % mist.height) #XXX right?
#tabWrite("turbulence 0.2\n")
#tabWrite("turb_depth 0.3\n")
- tabWrite("fog_type 1\n")
+ tabWrite("fog_type 1\n") #type2 for height
tabWrite("}\n")
if scene.pov.media_enable:
tabWrite("media {\n")
- tabWrite("scattering { 1, rgb <%.4g, %.4g, %.4g>}\n" % scene.pov.media_color[:])
+ tabWrite("scattering { %d, rgb %.12f*<%.4g, %.4g, %.4g>\n" % \
+ (int(scene.pov.media_scattering_type),
+ (scene.pov.media_diffusion_scale),
+ *(scene.pov.media_diffusion_color[:])))
+ if scene.pov.media_scattering_type == '5':
+ tabWrite("eccentricity %.3g\n" % scene.pov.media_eccentricity)
+ tabWrite("}\n")
+ tabWrite("absorption %.12f*<%.4g, %.4g, %.4g>\n" % \
+ (scene.pov.media_absorption_scale,
+ *(scene.pov.media_absorption_color[:])))
+ tabWrite("\n")
tabWrite("samples %.d\n" % scene.pov.media_samples)
tabWrite("}\n")
@@ -3559,7 +3609,7 @@ def write_pov(filename, scene=None, info_callback=None):
file.write(txt.as_string())
file.write("\n")
- sel = renderable_objects(scene)
+ #sel = renderable_objects(scene) #removed for booleans
if comments:
file.write("//----------------------------------------------\n" \
"//--Exported with POV-Ray exporter for Blender--\n" \
@@ -3602,6 +3652,20 @@ def write_pov(filename, scene=None, info_callback=None):
if comments:
file.write("\n//--Lamps--\n\n")
+ for ob in bpy.data.objects:
+ if ob.type == 'MESH':
+ for mod in ob.modifiers:
+ if mod.type == 'BOOLEAN':
+ if mod.object not in csg_list:
+ csg_list.append(mod.object)
+ if csg_list != []:
+ csg = False
+ sel = no_renderable_objects(scene)
+ exportMeshes(scene, sel, csg)
+
+ csg = True
+ sel = renderable_objects(scene)
+
exportLamps([L for L in sel if (L.type == 'LAMP' and L.pov.object_as != 'RAINBOW')])
if comments:
@@ -3655,7 +3719,7 @@ def write_pov(filename, scene=None, info_callback=None):
if comments:
file.write("//--Mesh objects--\n")
- exportMeshes(scene, sel)
+ exportMeshes(scene, sel, csg)
#What follow used to happen here:
#exportCamera()
@@ -3745,7 +3809,7 @@ def write_pov_ini(scene, filename_ini, filename_log, filename_pov, filename_imag
class PovrayRender(bpy.types.RenderEngine):
bl_idname = 'POVRAY_RENDER'
- bl_label = "POV-Ray 3.7"
+ bl_label = "Persitence Of Vision"
DELAY = 0.5
@staticmethod
@@ -3951,8 +4015,8 @@ class PovrayRender(bpy.types.RenderEngine):
self._temp_file_in = os.path.join(preview_dir, povPath)
self._temp_file_ini = os.path.join(preview_dir, (os.path.splitext(self._temp_file_in)[0]+".INI"))
self._temp_file_log = os.path.join(preview_dir, "alltext.out")
-
-
+
+
'''
try:
os.remove(self._temp_file_in) # so as not to load the old file
@@ -4000,7 +4064,14 @@ class PovrayRender(bpy.types.RenderEngine):
# Start Rendering!
try:
- _process = subprocess.Popen([pov_binary, self._temp_file_ini] + extra_args,
+ if sys.platform[:3] != "win" and scene.pov.sdl_window_enable: #segfault on linux == False !!!
+ env = {'POV_DISPLAY_SCALED': 'off'}
+ env.update(os.environ)
+ self._process = subprocess.Popen([pov_binary, self._temp_file_ini],
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
+ env=env)
+ else:
+ self._process = subprocess.Popen([pov_binary, self._temp_file_ini] + extra_args,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
except OSError:
# TODO, report api
@@ -4015,7 +4086,7 @@ class PovrayRender(bpy.types.RenderEngine):
print("Command line arguments passed: " + str(extra_args))
#return True
self.update_stats("", "POV-Ray 3.7: Parsing File")
-
+
# Indented in main function now so repeated here but still not working
@@ -4040,7 +4111,7 @@ class PovrayRender(bpy.types.RenderEngine):
print(f.read())
self.update_stats("", "")
-
+
if scene.pov.tempfiles_enable or scene.pov.deletefiles_enable:
self._cleanup()
else:
@@ -4394,7 +4465,7 @@ class RunPovTextRender(Operator):
bpy.ops.render.render()
-
+
#empty text name property engain
scene.pov.text_block = ""
- return {'FINISHED'} \ No newline at end of file
+ return {'FINISHED'} \ No newline at end of file
diff --git a/render_povray/shading.py b/render_povray/shading.py
index 5a371707..03a63a9d 100644
--- a/render_povray/shading.py
+++ b/render_povray/shading.py
@@ -245,13 +245,11 @@ def writeMaterial(using_uberpov, DEF_MAT_NAME, scene, tabWrite, safety, comments
for t in material.texture_slots:
if t and t.use and t.texture is not None:
if (t.texture.type == 'IMAGE' and t.texture.image) or t.texture.type != 'IMAGE':
- validPath=True
- else:
- validPath=False
- if(t and t.use and validPath and
- (t.use_map_specular or t.use_map_raymir or t.use_map_normal or t.use_map_alpha)):
- special_texture_found = True
- continue # Some texture found
+ #validPath
+ if(t and t.use and
+ (t.use_map_specular or t.use_map_raymir or t.use_map_normal or t.use_map_alpha)):
+ special_texture_found = True
+ continue # Some texture found
if special_texture_found or colored_specular_found:
# Level=1 Means No specular nor Mirror reflection
@@ -1087,7 +1085,6 @@ def writeTextureInfluence(mater, materialNames, LocalMaterialNames, path_image,
else:
if texturesDif and texturesDif.startswith("PAT_"):
tabWrite("pigment{%s %s}\n" %(texturesDif, mappingDif))
- print('XXXMEEEERDE!')
else:
tabWrite("pigment {\n")
tabWrite("uv_mapping image_map {\n")
diff --git a/render_povray/ui.py b/render_povray/ui.py
index ca6a5db2..b4a08292 100644
--- a/render_povray/ui.py
+++ b/render_povray/ui.py
@@ -22,6 +22,7 @@ import bpy
import sys #really import here and in render.py?
import os #really import here and in render.py?
from os.path import isfile
+from bl_operators.presets import AddPresetBase
# Use some of the existing buttons.
from bl_ui import properties_render
@@ -41,6 +42,61 @@ properties_world.WORLD_PT_world.COMPAT_ENGINES.add('POVRAY_RENDER')
properties_world.WORLD_PT_mist.COMPAT_ENGINES.add('POVRAY_RENDER')
del properties_world
+class POV_WORLD_MT_presets(bpy.types.Menu):
+ bl_label = "World Presets"
+ preset_subdir = "pov/world"
+ preset_operator = "script.execute_preset"
+ draw = bpy.types.Menu.draw_preset
+
+
+class AddPresetWorld(AddPresetBase, bpy.types.Operator):
+ '''Add a World Preset'''
+ bl_idname = "object.world_preset_add"
+ bl_label = "Add World Preset"
+ preset_menu = "POV_WORLD_MT_presets"
+
+ # variable used for all preset values
+ preset_defines = [
+ "scene = bpy.context.scene"
+ ]
+
+ # properties to store in the preset
+ preset_values = [
+ "scene.world.use_sky_blend",
+ "scene.world.horizon_color",
+ "scene.world.zenith_color",
+ "scene.world.ambient_color",
+ "scene.world.mist_settings.use_mist",
+ "scene.world.mist_settings.intensity",
+ "scene.world.mist_settings.depth",
+ "scene.world.mist_settings.start",
+ "scene.pov.media_enable",
+ "scene.pov.media_scattering_type",
+ "scene.pov.media_samples",
+ "scene.pov.media_diffusion_scale",
+ "scene.pov.media_diffusion_color",
+ "scene.pov.media_absorption_scale",
+ "scene.pov.media_absorption_color",
+ "scene.pov.media_eccentricity",
+ ]
+
+ # where to store the preset
+ preset_subdir = "pov/world"
+
+# Draw into an existing panel
+def world_panel_func(self, context):
+ layout = self.layout
+
+ row = layout.row(align=True)
+ row.menu(POV_WORLD_MT_presets.__name__, text=POV_WORLD_MT_presets.bl_label)
+ row.operator(AddPresetWorld.bl_idname, text="", icon='ZOOMIN')
+ row.operator(AddPresetWorld.bl_idname, text="", icon='ZOOMOUT').remove_active = True
+
+
+classes = (
+ POV_WORLD_MT_presets,
+ AddPresetWorld,
+ )
# Example of wrapping every class 'as is'
from bl_ui import properties_texture
@@ -450,6 +506,52 @@ class LAMP_PT_POV_lamp(PovLampButtonsPanel, bpy.types.Panel):
draw = properties_data_lamp.DATA_PT_lamp.draw
+class POV_LAMP_MT_presets(bpy.types.Menu):
+ bl_label = "Lamp Presets"
+ preset_subdir = "pov/lamp"
+ preset_operator = "script.execute_preset"
+ draw = bpy.types.Menu.draw_preset
+
+
+class AddPresetLamp(AddPresetBase, bpy.types.Operator):
+ '''Add a Lamp Preset'''
+ bl_idname = "object.lamp_preset_add"
+ bl_label = "Add Lamp Preset"
+ preset_menu = "POV_LAMP_MT_presets"
+
+ # variable used for all preset values
+ preset_defines = [
+ "lampdata = bpy.context.object.data"
+ ]
+
+ # properties to store in the preset
+ preset_values = [
+ "lampdata.type",
+ "lampdata.color",
+ ]
+
+ # where to store the preset
+ preset_subdir = "pov/lamp"
+
+
+
+
+
+# Draw into an existing panel
+def lamp_panel_func(self, context):
+ layout = self.layout
+
+ row = layout.row(align=True)
+ row.menu(POV_LAMP_MT_presets.__name__, text=POV_LAMP_MT_presets.bl_label)
+ row.operator(AddPresetLamp.bl_idname, text="", icon='ZOOMIN')
+ row.operator(AddPresetLamp.bl_idname, text="", icon='ZOOMOUT').remove_active = True
+
+
+classes = (
+ POV_LAMP_MT_presets,
+ AddPresetLamp,
+ )
+
class LAMP_PT_POV_sunsky(PovLampButtonsPanel, bpy.types.Panel):
bl_label = properties_data_lamp.DATA_PT_sunsky.bl_label
@@ -606,7 +708,10 @@ class RENDER_PT_povray_render_settings(RenderButtonsPanel, bpy.types.Panel):
scene = context.scene
#layout.active = (scene.pov.max_trace_level != 0)
-
+
+ if sys.platform[:3] != "win":
+ layout.prop(scene.pov, "sdl_window_enable", text="POV-Ray SDL Window")
+
col = layout.column()
col.label(text="Global Settings:")
@@ -784,7 +889,71 @@ class RENDER_PT_povray_radiosity(RenderButtonsPanel, bpy.types.Panel):
col.prop(scene.pov, "radio_subsurface")
-
+
+
+
+class POV_RADIOSITY_MT_presets(bpy.types.Menu):
+ bl_label = "Radiosity Presets"
+ preset_subdir = "pov/radiosity"
+ preset_operator = "script.execute_preset"
+ draw = bpy.types.Menu.draw_preset
+
+
+class AddPresetRadiosity(AddPresetBase, bpy.types.Operator):
+ '''Add a Radiosity Preset'''
+ bl_idname = "scene.radiosity_preset_add"
+ bl_label = "Add Radiosity Preset"
+ preset_menu = "POV_RADIOSITY_MT_presets"
+
+ # variable used for all preset values
+ preset_defines = [
+ "scene = bpy.context.scene"
+ ]
+
+ # properties to store in the preset
+ preset_values = [
+ "scene.pov.radio_display_advanced",
+ "scene.pov.radio_adc_bailout",
+ "scene.pov.radio_always_sample",
+ "scene.pov.radio_brightness",
+ "scene.pov.radio_count",
+ "scene.pov.radio_error_bound",
+ "scene.pov.radio_gray_threshold",
+ "scene.pov.radio_low_error_factor",
+ "scene.pov.radio_media",
+ "scene.pov.radio_subsurface",
+ "scene.pov.radio_minimum_reuse",
+ "scene.pov.radio_maximum_reuse",
+ "scene.pov.radio_nearest_count",
+ "scene.pov.radio_normal",
+ "scene.pov.radio_recursion_limit",
+ "scene.pov.radio_pretrace_start",
+ "scene.pov.radio_pretrace_end",
+ ]
+
+ # where to store the preset
+ preset_subdir = "pov/radiosity"
+
+
+
+
+
+# Draw into an existing panel
+def rad_panel_func(self, context):
+ layout = self.layout
+
+ row = layout.row(align=True)
+ row.menu(POV_RADIOSITY_MT_presets.__name__, text=POV_RADIOSITY_MT_presets.bl_label)
+ row.operator(AddPresetRadiosity.bl_idname, text="", icon='ZOOMIN')
+ row.operator(AddPresetRadiosity.bl_idname, text="", icon='ZOOMOUT').remove_active = True
+
+
+classes = (
+ POV_RADIOSITY_MT_presets,
+ AddPresetRadiosity,
+ )
+
+
class RENDER_PT_povray_media(WorldButtonsPanel, bpy.types.Panel):
bl_label = "Atmosphere Media"
COMPAT_ENGINES = {'POVRAY_RENDER'}
@@ -801,10 +970,22 @@ class RENDER_PT_povray_media(WorldButtonsPanel, bpy.types.Panel):
layout.active = scene.pov.media_enable
- row = layout.row()
- row.prop(scene.pov, "media_samples", text="Samples")
- row.prop(scene.pov, "media_color", text="")
-
+ col = layout.column()
+ col.prop(scene.pov, "media_scattering_type", text="")
+ col = layout.column()
+ col.prop(scene.pov, "media_samples", text="Samples")
+ split = layout.split()
+ col = split.column(align=True)
+ col.label(text="Scattering:")
+ col.prop(scene.pov, "media_diffusion_scale")
+ col.prop(scene.pov, "media_diffusion_color", text="")
+ col = split.column(align=True)
+ col.label(text="Absorption:")
+ col.prop(scene.pov, "media_absorption_scale")
+ col.prop(scene.pov, "media_absorption_color", text="")
+ if scene.pov.media_scattering_type == '5':
+ col = layout.column()
+ col.prop(scene.pov, "media_eccentricity", text="Eccentricity")
##class RENDER_PT_povray_baking(RenderButtonsPanel, bpy.types.Panel):
## bl_label = "Baking"
## COMPAT_ENGINES = {'POVRAY_RENDER'}
@@ -821,7 +1002,7 @@ class RENDER_PT_povray_media(WorldButtonsPanel, bpy.types.Panel):
## rd = scene.render
##
## layout.active = scene.pov.baking_enable
-'''XXX WIP preparing for CSG
+
class MODIFIERS_PT_povray_modifiers(ModifierButtonsPanel, bpy.types.Panel):
bl_label = "POV-Ray"
COMPAT_ENGINES = {'POVRAY_RENDER'}
@@ -850,7 +1031,7 @@ class MODIFIERS_PT_povray_modifiers(ModifierButtonsPanel, bpy.types.Panel):
col = layout.column()
# Inside Vector for CSG
col.prop(ob.pov, "inside_vector")
-'''
+
class MATERIAL_PT_povray_activate_node(MaterialButtonsPanel, bpy.types.Panel):
bl_label = "Activate Node Settings"
diff --git a/space_view3d_stored_views/__init__.py b/space_view3d_stored_views/__init__.py
index 8deca8f0..a9767bff 100644
--- a/space_view3d_stored_views/__init__.py
+++ b/space_view3d_stored_views/__init__.py
@@ -20,13 +20,14 @@ bl_info = {
"name": "Stored Views",
"description": "Save and restore User defined views, pov, layers and display configs",
"author": "nfloyd, Francesco Siddi",
- "version": (0, 3, 6),
+ "version": (0, 3, 7),
"blender": (2, 7, 8),
"location": "View3D > Properties > Stored Views",
"warning": "",
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.5/"
"Py/Scripts/3D_interaction/stored_views",
- "category": "3D View"}
+ "category": "3D View"
+}
"""
ACKNOWLEDGMENT
@@ -65,14 +66,14 @@ else:
import bpy
from bpy.props import (
- BoolProperty,
- IntProperty,
- PointerProperty,
- )
+ BoolProperty,
+ IntProperty,
+ PointerProperty,
+)
from bpy.types import (
- AddonPreferences,
- Operator,
- )
+ AddonPreferences,
+ Operator,
+)
class VIEW3D_stored_views_initialize(Operator):
@@ -99,20 +100,20 @@ class VIEW3D_stored_views_preferences(AddonPreferences):
bl_idname = __name__
show_exporters = BoolProperty(
- name="Enable I/O Operators",
- default=False,
- description="Enable Import/Export Operations in the UI:\n"
- "Import Stored Views preset,\n"
- "Export Stored Views preset and \n"
- "Import stored views from scene",
- )
+ name="Enable I/O Operators",
+ default=False,
+ description="Enable Import/Export Operations in the UI:\n"
+ "Import Stored Views preset,\n"
+ "Export Stored Views preset and \n"
+ "Import stored views from scene",
+ )
view_3d_update_rate = IntProperty(
- name="3D view update",
- description="Update rate of the 3D view redraw\n"
- "Increse the value if the UI feels sluggish",
- min=1, max=10,
- default=1
- )
+ name="3D view update",
+ description="Update rate of the 3D view redraw\n"
+ "Increse the value if the UI feels sluggish",
+ min=1, max=10,
+ default=1
+ )
def draw(self, context):
layout = self.layout
diff --git a/space_view3d_stored_views/io.py b/space_view3d_stored_views/io.py
index b6aec4dd..e9c325ff 100644
--- a/space_view3d_stored_views/io.py
+++ b/space_view3d_stored_views/io.py
@@ -8,13 +8,13 @@ import shutil
import bpy
from bpy.types import Operator
from bpy.props import (
- BoolProperty,
- StringProperty
- )
+ BoolProperty,
+ StringProperty,
+)
from bpy_extras.io_utils import (
- ExportHelper,
- ImportHelper,
- )
+ ExportHelper,
+ ImportHelper,
+)
from . import bl_info
from .core import get_preferences
from .operators import DataStore
@@ -162,11 +162,12 @@ class IO_Utils():
return False
# io_filters = sv.settings.io_filters
- sv_data = {"point_of_views": sv.pov_list,
- "views": sv.view_list,
- "layers": sv.layers_list,
- "displays": sv.display_list}
-
+ sv_data = {
+ "point_of_views": sv.pov_list,
+ "views": sv.view_list,
+ "layers": sv.layers_list,
+ "displays": sv.display_list
+ }
for sv_struct, props in dump["data"].items():
"""
is_filtered = getattr(io_filters, sv_struct)
@@ -203,20 +204,20 @@ class IO_Utils():
class VIEW3D_stored_views_import(Operator, ImportHelper):
- bl_idname = "stored_views.import"
+ bl_idname = "stored_views.import_blsv"
bl_label = "Import Stored Views preset"
bl_description = "Import a .blsv preset file to the current Stored Views"
filename_ext = ".blsv"
filter_glob = StringProperty(
- default="*.blsv",
- options={'HIDDEN'}
- )
+ default="*.blsv",
+ options={'HIDDEN'}
+ )
replace = BoolProperty(
- name="Replace",
- default=True,
- description="Replace current stored views, otherwise append"
- )
+ name="Replace",
+ default=True,
+ description="Replace current stored views, otherwise append"
+ )
@classmethod
def poll(cls, context):
@@ -227,7 +228,7 @@ class VIEW3D_stored_views_import(Operator, ImportHelper):
exists = os.path.isfile(self.filepath) if self.filepath else False
if not exists:
self.report({'WARNING'},
- "No filepath specified, or file could not be found. Operation Cancelled")
+ "No filepath specified or file could not be found. Operation Cancelled")
return {'CANCELLED'}
# apply chosen preset
@@ -258,15 +259,15 @@ class VIEW3D_stored_views_import_from_scene(Operator):
bl_description = "Import currently stored views from an another scene"
scene_name = StringProperty(
- name="Scene Name",
- description="A current blend scene",
- default=""
- )
+ name="Scene Name",
+ description="A current blend scene",
+ default=""
+ )
replace = BoolProperty(
- name="Replace",
- default=True,
- description="Replace current stored views, otherwise append"
- )
+ name="Replace",
+ default=True,
+ description="Replace current stored views, otherwise append"
+ )
@classmethod
def poll(cls, context):
@@ -300,23 +301,23 @@ class VIEW3D_stored_views_import_from_scene(Operator):
class VIEW3D_stored_views_export(Operator, ExportHelper):
- bl_idname = "stored_views.export"
+ bl_idname = "stored_views.export_blsv"
bl_label = "Export Stored Views preset"
bl_description = "Export the current Stored Views to a .blsv preset file"
filename_ext = ".blsv"
filepath = StringProperty(
- default=os.path.join(IO_Utils.get_preset_path()[0], "untitled")
- )
+ default=os.path.join(IO_Utils.get_preset_path()[0], "untitled")
+ )
filter_glob = StringProperty(
- default="*.blsv",
- options={'HIDDEN'}
- )
+ default="*.blsv",
+ options={'HIDDEN'}
+ )
preset_name = StringProperty(
- name="Preset name",
- default="",
- description="Name of the stored views preset"
- )
+ name="Preset name",
+ default="",
+ description="Name of the stored views preset"
+ )
@classmethod
def poll(cls, context):
@@ -324,4 +325,5 @@ class VIEW3D_stored_views_export(Operator, ExportHelper):
def execute(self, context):
IO_Utils.stored_views_export_to_blsv(self.filepath, self.preset_name)
+
return{'FINISHED'}
diff --git a/space_view3d_stored_views/ui.py b/space_view3d_stored_views/ui.py
index 17d8c729..5025db19 100644
--- a/space_view3d_stored_views/ui.py
+++ b/space_view3d_stored_views/ui.py
@@ -7,9 +7,9 @@ import bpy
import blf
from . import core
from bpy.types import (
- Operator,
- Panel,
- )
+ Operator,
+ Panel,
+)
"""
If view name display is enabled,
@@ -185,8 +185,8 @@ class VIEW3D_PT_properties_stored_views(Panel):
if core.get_preferences():
row = layout.row(align=True)
row.operator("stored_views.import_from_scene", text="Import from Scene")
- row.operator("stored_views.import", text="", icon="IMPORT")
- row.operator("stored_views.export", text="", icon="EXPORT")
+ row.operator("stored_views.import_blsv", text="", icon="IMPORT")
+ row.operator("stored_views.export_blsv", text="", icon="EXPORT")
data_store = core.DataStore()
list = data_store.list
diff --git a/ui_layer_manager.py b/ui_layer_manager.py
index b354b1ec..018aad26 100644
--- a/ui_layer_manager.py
+++ b/ui_layer_manager.py
@@ -364,7 +364,7 @@ class SCENE_OT_namedlayer_select_objects_by_layer(Operator):
for obj in objects:
obj.select = False
else:
- bpy.ops.object.select_by_layer(extend=self.extend, layers=layer_idx + 1)
+ bpy.ops.object.select_by_layer(match='SHARED', extend=self.extend, layers=layer_idx + 1)
return {'FINISHED'}
diff --git a/uv_magic_uv/__init__.py b/uv_magic_uv/__init__.py
index 171a5ac4..080d2414 100644
--- a/uv_magic_uv/__init__.py
+++ b/uv_magic_uv/__init__.py
@@ -20,18 +20,18 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
bl_info = {
"name": "Magic UV",
- "author": "Nutti, Mifth, Jace Priester, kgeogeo, mem, "
+ "author": "Nutti, Mifth, Jace Priester, kgeogeo, mem, imdjs"
"Keith (Wahooney) Boshoff, McBuff, MaxRobinot, Alexander Milovsky",
- "version": (4, 5, 0),
+ "version": (5, 1, 0),
"blender": (2, 79, 0),
"location": "See Add-ons Preferences",
- "description": "UV Manipulator Tools. See Add-ons Preferences for details",
+ "description": "UV Toolset. See Add-ons Preferences for details",
"warning": "",
"support": "COMMUNITY",
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/"
@@ -42,98 +42,29 @@ bl_info = {
if "bpy" in locals():
import importlib
- importlib.reload(muv_preferences)
- importlib.reload(muv_menu)
- importlib.reload(muv_common)
- importlib.reload(muv_props)
- importlib.reload(muv_cpuv_ops)
- importlib.reload(muv_cpuv_selseq_ops)
- importlib.reload(muv_fliprot_ops)
- importlib.reload(muv_transuv_ops)
- importlib.reload(muv_uvbb_ops)
- importlib.reload(muv_mvuv_ops)
- importlib.reload(muv_texproj_ops)
- importlib.reload(muv_packuv_ops)
- importlib.reload(muv_texlock_ops)
- importlib.reload(muv_mirroruv_ops)
- importlib.reload(muv_wsuv_ops)
- importlib.reload(muv_unwrapconst_ops)
- importlib.reload(muv_preserve_uv_aspect)
- importlib.reload(muv_uvw_ops)
+ importlib.reload(op)
+ importlib.reload(ui)
+ importlib.reload(common)
+ importlib.reload(preferences)
+ importlib.reload(properites)
else:
- from . import muv_preferences
- from . import muv_menu
- from . import muv_common
- from . import muv_props
- from . import muv_cpuv_ops
- from . import muv_cpuv_selseq_ops
- from . import muv_fliprot_ops
- from . import muv_transuv_ops
- from . import muv_uvbb_ops
- from . import muv_mvuv_ops
- from . import muv_texproj_ops
- from . import muv_packuv_ops
- from . import muv_texlock_ops
- from . import muv_mirroruv_ops
- from . import muv_wsuv_ops
- from . import muv_unwrapconst_ops
- from . import muv_preserve_uv_aspect
- from . import muv_uvw_ops
+ from . import op
+ from . import ui
+ from . import common
+ from . import preferences
+ from . import properites
import bpy
-def view3d_uvmap_menu_fn(self, context):
- self.layout.separator()
- self.layout.menu(muv_menu.MUV_CPUVMenu.bl_idname, icon="IMAGE_COL")
- self.layout.operator(
- muv_fliprot_ops.MUV_FlipRot.bl_idname, icon="IMAGE_COL")
- self.layout.menu(muv_menu.MUV_TransUVMenu.bl_idname, icon="IMAGE_COL")
- self.layout.operator(muv_mvuv_ops.MUV_MVUV.bl_idname, icon="IMAGE_COL")
- self.layout.menu(muv_menu.MUV_TexLockMenu.bl_idname, icon="IMAGE_COL")
- self.layout.operator(
- muv_mirroruv_ops.MUV_MirrorUV.bl_idname, icon="IMAGE_COL")
- self.layout.menu(muv_menu.MUV_WSUVMenu.bl_idname, icon="IMAGE_COL")
- self.layout.operator(
- muv_unwrapconst_ops.MUV_UnwrapConstraint.bl_idname, icon='IMAGE_COL')
- self.layout.menu(
- muv_preserve_uv_aspect.MUV_PreserveUVAspectMenu.bl_idname,
- icon='IMAGE_COL')
- self.layout.menu(muv_menu.MUV_UVWMenu.bl_idname, icon="IMAGE_COL")
-
-
-def image_uvs_menu_fn(self, context):
- self.layout.separator()
- self.layout.operator(muv_packuv_ops.MUV_PackUV.bl_idname, icon="IMAGE_COL")
-
-
-def view3d_object_menu_fn(self, context):
- self.layout.separator()
- self.layout.menu(muv_menu.MUV_CPUVObjMenu.bl_idname, icon="IMAGE_COL")
-
-
def register():
bpy.utils.register_module(__name__)
- bpy.types.VIEW3D_MT_uv_map.append(view3d_uvmap_menu_fn)
- bpy.types.IMAGE_MT_uvs.append(image_uvs_menu_fn)
- bpy.types.VIEW3D_MT_object.append(view3d_object_menu_fn)
- try:
- bpy.types.VIEW3D_MT_Object.append(view3d_object_menu_fn)
- except:
- pass
- muv_props.init_props(bpy.types.Scene)
+ properites.init_props(bpy.types.Scene)
def unregister():
bpy.utils.unregister_module(__name__)
- bpy.types.VIEW3D_MT_uv_map.remove(view3d_uvmap_menu_fn)
- bpy.types.IMAGE_MT_uvs.remove(image_uvs_menu_fn)
- bpy.types.VIEW3D_MT_object.remove(view3d_object_menu_fn)
- try:
- bpy.types.VIEW3D_MT_Object.remove(view3d_object_menu_fn)
- except:
- pass
- muv_props.clear_props(bpy.types.Scene)
+ properites.clear_props(bpy.types.Scene)
if __name__ == "__main__":
diff --git a/uv_magic_uv/common.py b/uv_magic_uv/common.py
new file mode 100644
index 00000000..6d3d9df7
--- /dev/null
+++ b/uv_magic_uv/common.py
@@ -0,0 +1,604 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
+
+from collections import defaultdict
+from pprint import pprint
+from math import fabs, sqrt
+
+import bpy
+from mathutils import Vector
+import bmesh
+
+
+DEBUG = False
+
+
+def debug_print(*s):
+ """
+ Print message to console in debugging mode
+ """
+
+ if DEBUG:
+ pprint(s)
+
+
+def check_version(major, minor, _):
+ """
+ Check blender version
+ """
+
+ if bpy.app.version[0] == major and bpy.app.version[1] == minor:
+ return 0
+ if bpy.app.version[0] > major:
+ return 1
+ if bpy.app.version[1] > minor:
+ return 1
+ return -1
+
+
+def redraw_all_areas():
+ """
+ Redraw all areas
+ """
+
+ for area in bpy.context.screen.areas:
+ area.tag_redraw()
+
+
+def get_space(area_type, region_type, space_type):
+ """
+ Get current area/region/space
+ """
+
+ area = None
+ region = None
+ space = None
+
+ for area in bpy.context.screen.areas:
+ if area.type == area_type:
+ break
+ else:
+ return (None, None, None)
+ for region in area.regions:
+ if region.type == region_type:
+ break
+ for space in area.spaces:
+ if space.type == space_type:
+ break
+
+ return (area, region, space)
+
+
+def __get_island_info(uv_layer, islands):
+ """
+ get information about each island
+ """
+
+ island_info = []
+ for isl in islands:
+ info = {}
+ max_uv = Vector((-10000000.0, -10000000.0))
+ min_uv = Vector((10000000.0, 10000000.0))
+ ave_uv = Vector((0.0, 0.0))
+ num_uv = 0
+ for face in isl:
+ n = 0
+ a = Vector((0.0, 0.0))
+ ma = Vector((-10000000.0, -10000000.0))
+ mi = Vector((10000000.0, 10000000.0))
+ for l in face['face'].loops:
+ uv = l[uv_layer].uv
+ ma.x = max(uv.x, ma.x)
+ ma.y = max(uv.y, ma.y)
+ mi.x = min(uv.x, mi.x)
+ mi.y = min(uv.y, mi.y)
+ a = a + uv
+ n = n + 1
+ ave_uv = ave_uv + a
+ num_uv = num_uv + n
+ a = a / n
+ max_uv.x = max(ma.x, max_uv.x)
+ max_uv.y = max(ma.y, max_uv.y)
+ min_uv.x = min(mi.x, min_uv.x)
+ min_uv.y = min(mi.y, min_uv.y)
+ face['max_uv'] = ma
+ face['min_uv'] = mi
+ face['ave_uv'] = a
+ ave_uv = ave_uv / num_uv
+
+ info['center'] = ave_uv
+ info['size'] = max_uv - min_uv
+ info['num_uv'] = num_uv
+ info['group'] = -1
+ info['faces'] = isl
+ info['max'] = max_uv
+ info['min'] = min_uv
+
+ island_info.append(info)
+
+ return island_info
+
+
+def __parse_island(bm, face_idx, faces_left, island,
+ face_to_verts, vert_to_faces):
+ """
+ Parse island
+ """
+
+ if face_idx in faces_left:
+ faces_left.remove(face_idx)
+ island.append({'face': bm.faces[face_idx]})
+ for v in face_to_verts[face_idx]:
+ connected_faces = vert_to_faces[v]
+ if connected_faces:
+ for cf in connected_faces:
+ __parse_island(bm, cf, faces_left, island, face_to_verts,
+ vert_to_faces)
+
+
+def __get_island(bm, face_to_verts, vert_to_faces):
+ """
+ Get island list
+ """
+
+ uv_island_lists = []
+ faces_left = set(face_to_verts.keys())
+ while faces_left:
+ current_island = []
+ face_idx = list(faces_left)[0]
+ __parse_island(bm, face_idx, faces_left, current_island,
+ face_to_verts, vert_to_faces)
+ uv_island_lists.append(current_island)
+
+ return uv_island_lists
+
+
+def __create_vert_face_db(faces, uv_layer):
+ # create mesh database for all faces
+ face_to_verts = defaultdict(set)
+ vert_to_faces = defaultdict(set)
+ for f in faces:
+ for l in f.loops:
+ id_ = l[uv_layer].uv.to_tuple(5), l.vert.index
+ face_to_verts[f.index].add(id_)
+ vert_to_faces[id_].add(f.index)
+
+ return (face_to_verts, vert_to_faces)
+
+
+def get_island_info(obj, only_selected=True):
+ bm = bmesh.from_edit_mesh(obj.data)
+ if check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
+
+ return get_island_info_from_bmesh(bm, only_selected)
+
+
+def get_island_info_from_bmesh(bm, only_selected=True):
+ if not bm.loops.layers.uv:
+ return None
+ uv_layer = bm.loops.layers.uv.verify()
+
+ # create database
+ if only_selected:
+ selected_faces = [f for f in bm.faces if f.select]
+ else:
+ selected_faces = [f for f in bm.faces]
+
+ return get_island_info_from_faces(bm, selected_faces, uv_layer)
+
+
+def get_island_info_from_faces(bm, faces, uv_layer):
+ ftv, vtf = __create_vert_face_db(faces, uv_layer)
+
+ # Get island information
+ uv_island_lists = __get_island(bm, ftv, vtf)
+ island_info = __get_island_info(uv_layer, uv_island_lists)
+
+ return island_info
+
+
+def get_uvimg_editor_board_size(area):
+ if area.spaces.active.image:
+ return area.spaces.active.image.size
+
+ return (255.0, 255.0)
+
+
+def calc_polygon_2d_area(points):
+ area = 0.0
+ for i, p1 in enumerate(points):
+ p2 = points[(i + 1) % len(points)]
+ v1 = p1 - points[0]
+ v2 = p2 - points[0]
+ a = v1.x * v2.y - v1.y * v2.x
+ area = area + a
+
+ return fabs(0.5 * area)
+
+
+def calc_polygon_3d_area(points):
+ area = 0.0
+ for i, p1 in enumerate(points):
+ p2 = points[(i + 1) % len(points)]
+ v1 = p1 - points[0]
+ v2 = p2 - points[0]
+ cx = v1.y * v2.z - v1.z * v2.y
+ cy = v1.z * v2.x - v1.x * v2.z
+ cz = v1.x * v2.y - v1.y * v2.x
+ a = sqrt(cx * cx + cy * cy + cz * cz)
+ area = area + a
+
+ return 0.5 * area
+
+
+def measure_mesh_area(obj):
+ bm = bmesh.from_edit_mesh(obj.data)
+ if check_version(2, 73, 0) >= 0:
+ bm.verts.ensure_lookup_table()
+ bm.edges.ensure_lookup_table()
+ bm.faces.ensure_lookup_table()
+
+ sel_faces = [f for f in bm.faces if f.select]
+
+ # measure
+ mesh_area = 0.0
+ for f in sel_faces:
+ verts = [l.vert.co for l in f.loops]
+ f_mesh_area = calc_polygon_3d_area(verts)
+ mesh_area = mesh_area + f_mesh_area
+
+ return mesh_area
+
+
+def measure_uv_area(obj):
+ bm = bmesh.from_edit_mesh(obj.data)
+ if check_version(2, 73, 0) >= 0:
+ bm.verts.ensure_lookup_table()
+ bm.edges.ensure_lookup_table()
+ bm.faces.ensure_lookup_table()
+
+ if not bm.loops.layers.uv:
+ return None
+ uv_layer = bm.loops.layers.uv.verify()
+
+ if not bm.faces.layers.tex:
+ return None
+ tex_layer = bm.faces.layers.tex.verify()
+
+ sel_faces = [f for f in bm.faces if f.select]
+
+ # measure
+ uv_area = 0.0
+ for f in sel_faces:
+ uvs = [l[uv_layer].uv for l in f.loops]
+ f_uv_area = calc_polygon_2d_area(uvs)
+
+ if not tex_layer:
+ return None
+ img = f[tex_layer].image
+ # not found, try to search from node
+ if not img:
+ for mat in obj.material_slots:
+ for node in mat.material.node_tree.nodes:
+ tex_node_types = [
+ 'TEX_ENVIRONMENT',
+ 'TEX_IMAGE',
+ ]
+ if (node.type in tex_node_types) and node.image:
+ img = node.image
+ if not img:
+ return None
+ uv_area = uv_area + f_uv_area * img.size[0] * img.size[1]
+
+ return uv_area
+
+
+def diff_point_to_segment(a, b, p):
+ ab = b - a
+ normal_ab = ab.normalized()
+
+ ap = p - a
+ dist_ax = normal_ab.dot(ap)
+
+ # cross point
+ x = a + normal_ab * dist_ax
+
+ # difference between cross point and point
+ xp = p - x
+
+ return xp, x
+
+
+# get selected loop pair whose loops are connected each other
+def __get_loop_pairs(l, uv_layer):
+
+ def __get_loop_pairs_internal(l_, pairs_, uv_layer_, parsed_):
+ parsed_.append(l_)
+ for ll in l_.vert.link_loops:
+ # forward direction
+ lln = ll.link_loop_next
+ # if there is same pair, skip it
+ found = False
+ for p in pairs_:
+ if (ll in p) and (lln in p):
+ found = True
+ break
+ # two loops must be selected
+ if ll[uv_layer_].select and lln[uv_layer_].select:
+ if not found:
+ pairs_.append([ll, lln])
+ if lln not in parsed_:
+ __get_loop_pairs_internal(lln, pairs_, uv_layer_, parsed_)
+
+ # backward direction
+ llp = ll.link_loop_prev
+ # if there is same pair, skip it
+ found = False
+ for p in pairs_:
+ if (ll in p) and (llp in p):
+ found = True
+ break
+ # two loops must be selected
+ if ll[uv_layer_].select and llp[uv_layer_].select:
+ if not found:
+ pairs_.append([ll, llp])
+ if llp not in parsed_:
+ __get_loop_pairs_internal(llp, pairs_, uv_layer_, parsed_)
+
+ pairs = []
+ parsed = []
+ __get_loop_pairs_internal(l, pairs, uv_layer, parsed)
+
+ return pairs
+
+
+# sort pair by vertex
+# (v0, v1) - (v1, v2) - (v2, v3) ....
+def __sort_loop_pairs(uv_layer, pairs, closed):
+ rest = pairs
+ sorted_pairs = [rest[0]]
+ rest.remove(rest[0])
+
+ # prepend
+ while True:
+ p1 = sorted_pairs[0]
+ for p2 in rest:
+ if p1[0].vert == p2[0].vert:
+ sorted_pairs.insert(0, [p2[1], p2[0]])
+ rest.remove(p2)
+ break
+ elif p1[0].vert == p2[1].vert:
+ sorted_pairs.insert(0, [p2[0], p2[1]])
+ rest.remove(p2)
+ break
+ else:
+ break
+
+ # append
+ while True:
+ p1 = sorted_pairs[-1]
+ for p2 in rest:
+ if p1[1].vert == p2[0].vert:
+ sorted_pairs.append([p2[0], p2[1]])
+ rest.remove(p2)
+ break
+ elif p1[1].vert == p2[1].vert:
+ sorted_pairs.append([p2[1], p2[0]])
+ rest.remove(p2)
+ break
+ else:
+ break
+
+ begin_vert = sorted_pairs[0][0].vert
+ end_vert = sorted_pairs[-1][-1].vert
+ if begin_vert != end_vert:
+ return sorted_pairs, ""
+ if closed and (begin_vert == end_vert):
+ # if the sequence of UV is circular, it is ok
+ return sorted_pairs, ""
+
+ # if the begin vertex and the end vertex are same, search the UVs which
+ # are separated each other
+ tmp_pairs = sorted_pairs
+ for i, (p1, p2) in enumerate(zip(tmp_pairs[:-1], tmp_pairs[1:])):
+ diff = p2[0][uv_layer].uv - p1[-1][uv_layer].uv
+ if diff.length > 0.000000001:
+ # UVs are separated
+ sorted_pairs = tmp_pairs[i + 1:]
+ sorted_pairs.extend(tmp_pairs[:i + 1])
+ break
+ else:
+ p1 = tmp_pairs[0]
+ p2 = tmp_pairs[-1]
+ diff = p2[-1][uv_layer].uv - p1[0][uv_layer].uv
+ if diff.length < 0.000000001:
+ # all UVs are not separated
+ return None, "All UVs are not separted"
+
+ return sorted_pairs, ""
+
+
+# get index of the island group which includes loop
+def __get_island_group_include_loop(loop, island_info):
+ for i, isl in enumerate(island_info):
+ for f in isl['faces']:
+ for l in f['face'].loops:
+ if l == loop:
+ return i # found
+
+ return -1 # not found
+
+
+# get index of the island group which includes pair.
+# if island group is not same between loops, it will be invalid
+def __get_island_group_include_pair(pair, island_info):
+ l1_grp = __get_island_group_include_loop(pair[0], island_info)
+ if l1_grp == -1:
+ return -1 # not found
+
+ for p in pair[1:]:
+ l2_grp = __get_island_group_include_loop(p, island_info)
+ if (l2_grp == -1) or (l1_grp != l2_grp):
+ return -1 # not found or invalid
+
+ return l1_grp
+
+
+# x ---- x <- next_loop_pair
+# | |
+# o ---- o <- pair
+def __get_next_loop_pair(pair):
+ lp = pair[0].link_loop_prev
+ if lp.vert == pair[1].vert:
+ lp = pair[0].link_loop_next
+ if lp.vert == pair[1].vert:
+ # no loop is found
+ return None
+
+ ln = pair[1].link_loop_next
+ if ln.vert == pair[0].vert:
+ ln = pair[1].link_loop_prev
+ if ln.vert == pair[0].vert:
+ # no loop is found
+ return None
+
+ # tri-face
+ if lp == ln:
+ return [lp]
+
+ # quad-face
+ return [lp, ln]
+
+
+# | ---- |
+# % ---- % <- next_poly_loop_pair
+# x ---- x <- next_loop_pair
+# | |
+# o ---- o <- pair
+def __get_next_poly_loop_pair(pair):
+ v1 = pair[0].vert
+ v2 = pair[1].vert
+ for l1 in v1.link_loops:
+ if l1 == pair[0]:
+ continue
+ for l2 in v2.link_loops:
+ if l2 == pair[1]:
+ continue
+ if l1.link_loop_next == l2:
+ return [l1, l2]
+ elif l1.link_loop_prev == l2:
+ return [l1, l2]
+
+ # no next poly loop is found
+ return None
+
+
+# get loop sequence in the same island
+def __get_loop_sequence_internal(uv_layer, pairs, island_info, closed):
+ loop_sequences = []
+ for pair in pairs:
+ seqs = [pair]
+ p = pair
+ isl_grp = __get_island_group_include_pair(pair, island_info)
+ if isl_grp == -1:
+ return None, "Can not find the island or invalid island"
+
+ while True:
+ nlp = __get_next_loop_pair(p)
+ if not nlp:
+ break # no more loop pair
+ nlp_isl_grp = __get_island_group_include_pair(nlp, island_info)
+ if nlp_isl_grp != isl_grp:
+ break # another island
+ for nlpl in nlp:
+ if nlpl[uv_layer].select:
+ return None, "Do not select UV which does not belong to " \
+ "the end edge"
+
+ seqs.append(nlp)
+
+ # when face is triangle, it indicates CLOSED
+ if (len(nlp) == 1) and closed:
+ break
+
+ nplp = __get_next_poly_loop_pair(nlp)
+ if not nplp:
+ break # no more loop pair
+ nplp_isl_grp = __get_island_group_include_pair(nplp, island_info)
+ if nplp_isl_grp != isl_grp:
+ break # another island
+
+ # check if the UVs are already parsed.
+ # this check is needed for the mesh which has the circular
+ # sequence of the verticies
+ matched = False
+ for p1 in seqs:
+ p2 = nplp
+ if ((p1[0] == p2[0]) and (p1[1] == p2[1])) or \
+ ((p1[0] == p2[1]) and (p1[1] == p2[0])):
+ matched = True
+ if matched:
+ debug_print("This is a circular sequence")
+ break
+
+ for nlpl in nplp:
+ if nlpl[uv_layer].select:
+ return None, "Do not select UV which does not belong to " \
+ "the end edge"
+
+ seqs.append(nplp)
+
+ p = nplp
+
+ loop_sequences.append(seqs)
+ return loop_sequences, ""
+
+
+def get_loop_sequences(bm, uv_layer, closed=False):
+ sel_faces = [f for f in bm.faces if f.select]
+
+ # get candidate loops
+ cand_loops = []
+ for f in sel_faces:
+ for l in f.loops:
+ if l[uv_layer].select:
+ cand_loops.append(l)
+
+ if len(cand_loops) < 2:
+ return None, "More than 2 UVs must be selected"
+
+ first_loop = cand_loops[0]
+ isl_info = get_island_info_from_bmesh(bm, False)
+ loop_pairs = __get_loop_pairs(first_loop, uv_layer)
+ loop_pairs, err = __sort_loop_pairs(uv_layer, loop_pairs, closed)
+ if not loop_pairs:
+ return None, err
+ loop_seqs, err = __get_loop_sequence_internal(uv_layer, loop_pairs,
+ isl_info, closed)
+ if not loop_seqs:
+ return None, err
+
+ return loop_seqs, ""
diff --git a/uv_magic_uv/muv_common.py b/uv_magic_uv/muv_common.py
deleted file mode 100644
index b52971ec..00000000
--- a/uv_magic_uv/muv_common.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# <pep8-80 compliant>
-
-# ##### BEGIN GPL LICENSE BLOCK #####
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# ##### END GPL LICENSE BLOCK #####
-
-__author__ = "Nutti <nutti.metro@gmail.com>"
-__status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
-
-import bpy
-from . import muv_props
-
-
-def debug_print(*s):
- """
- Print message to console in debugging mode
- """
-
- if muv_props.DEBUG:
- print(s)
-
-
-def check_version(major, minor, _):
- """
- Check blender version
- """
-
- if bpy.app.version[0] == major and bpy.app.version[1] == minor:
- return 0
- if bpy.app.version[0] > major:
- return 1
- if bpy.app.version[1] > minor:
- return 1
- return -1
-
-
-def redraw_all_areas():
- """
- Redraw all areas
- """
-
- for area in bpy.context.screen.areas:
- area.tag_redraw()
-
-
-def get_space(area_type, region_type, space_type):
- """
- Get current area/region/space
- """
-
- area = None
- region = None
- space = None
-
- for area in bpy.context.screen.areas:
- if area.type == area_type:
- break
- else:
- return (None, None, None)
- for region in area.regions:
- if region.type == region_type:
- break
- for space in area.spaces:
- if space.type == space_type:
- break
-
- return (area, region, space)
diff --git a/uv_magic_uv/muv_cpuv_selseq_ops.py b/uv_magic_uv/muv_cpuv_selseq_ops.py
deleted file mode 100644
index 3cf69ff7..00000000
--- a/uv_magic_uv/muv_cpuv_selseq_ops.py
+++ /dev/null
@@ -1,279 +0,0 @@
-# <pep8-80 compliant>
-
-# ##### BEGIN GPL LICENSE BLOCK #####
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# ##### END GPL LICENSE BLOCK #####
-
-__author__ = "Nutti <nutti.metro@gmail.com>"
-__status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
-
-import bpy
-import bmesh
-from bpy.props import (
- StringProperty,
- BoolProperty,
- IntProperty,
- EnumProperty,
-)
-from . import muv_common
-
-
-class MUV_CPUVSelSeqCopyUV(bpy.types.Operator):
- """
- Operation class: Copy UV coordinate by selection sequence
- """
-
- bl_idname = "uv.muv_cpuv_selseq_copy_uv"
- bl_label = "Copy UV (Selection Sequence) (Operation)"
- bl_description = "Copy UV data by selection sequence (Operation)"
- bl_options = {'REGISTER', 'UNDO'}
-
- uv_map = StringProperty(options={'HIDDEN'})
-
- def execute(self, context):
- props = context.scene.muv_props.cpuv_selseq
- if self.uv_map == "":
- self.report({'INFO'}, "Copy UV coordinate (selection sequence)")
- else:
- self.report(
- {'INFO'},
- "Copy UV coordinate (selection sequence) (UV map:%s)"
- % (self.uv_map))
- obj = context.active_object
- bm = bmesh.from_edit_mesh(obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
- bm.faces.ensure_lookup_table()
-
- # get UV layer
- if self.uv_map == "":
- if not bm.loops.layers.uv:
- self.report(
- {'WARNING'}, "Object must have more than one UV map")
- return {'CANCELLED'}
- uv_layer = bm.loops.layers.uv.verify()
- else:
- uv_layer = bm.loops.layers.uv[self.uv_map]
-
- # get selected face
- props.src_uvs = []
- props.src_pin_uvs = []
- props.src_seams = []
- for hist in bm.select_history:
- if isinstance(hist, bmesh.types.BMFace) and hist.select:
- uvs = [l[uv_layer].uv.copy() for l in hist.loops]
- pin_uvs = [l[uv_layer].pin_uv for l in hist.loops]
- seams = [l.edge.seam for l in hist.loops]
- props.src_uvs.append(uvs)
- props.src_pin_uvs.append(pin_uvs)
- props.src_seams.append(seams)
- if not props.src_uvs or not props.src_pin_uvs:
- self.report({'WARNING'}, "No faces are selected")
- return {'CANCELLED'}
- self.report({'INFO'}, "%d face(s) are selected" % len(props.src_uvs))
-
- return {'FINISHED'}
-
-
-class MUV_CPUVSelSeqCopyUVMenu(bpy.types.Menu):
- """
- Menu class: Copy UV coordinate by selection sequence
- """
-
- bl_idname = "uv.muv_cpuv_selseq_copy_uv_menu"
- bl_label = "Copy UV (Selection Sequence)"
- bl_description = "Copy UV coordinate by selection sequence"
-
- def draw(self, context):
- layout = self.layout
- obj = context.active_object
- bm = bmesh.from_edit_mesh(obj.data)
- uv_maps = bm.loops.layers.uv.keys()
- layout.operator(
- MUV_CPUVSelSeqCopyUV.bl_idname,
- text="[Default]", icon="IMAGE_COL").uv_map = ""
- for m in uv_maps:
- layout.operator(
- MUV_CPUVSelSeqCopyUV.bl_idname,
- text=m, icon="IMAGE_COL").uv_map = m
-
-
-class MUV_CPUVSelSeqPasteUV(bpy.types.Operator):
- """
- Operation class: Paste UV coordinate by selection sequence
- """
-
- bl_idname = "uv.muv_cpuv_selseq_paste_uv"
- bl_label = "Paste UV (Selection Sequence) (Operation)"
- bl_description = "Paste UV coordinate by selection sequence (Operation)"
- bl_options = {'REGISTER', 'UNDO'}
-
- uv_map = StringProperty(options={'HIDDEN'})
- strategy = EnumProperty(
- name="Strategy",
- description="Paste Strategy",
- items=[
- ('N_N', 'N:N', 'Number of faces must be equal to source'),
- ('N_M', 'N:M', 'Number of faces must not be equal to source')
- ],
- default="N_M"
- )
- flip_copied_uv = BoolProperty(
- name="Flip Copied UV",
- description="Flip Copied UV...",
- default=False
- )
- rotate_copied_uv = IntProperty(
- default=0,
- name="Rotate Copied UV",
- min=0,
- max=30
- )
- copy_seams = BoolProperty(
- name="Copy Seams",
- description="Copy Seams",
- default=True
- )
-
- def execute(self, context):
- props = context.scene.muv_props.cpuv_selseq
- if not props.src_uvs or not props.src_pin_uvs:
- self.report({'WARNING'}, "Need copy UV at first")
- return {'CANCELLED'}
- if self.uv_map == "":
- self.report({'INFO'}, "Paste UV coordinate (selection sequence)")
- else:
- self.report(
- {'INFO'},
- "Paste UV coordinate (selection sequence) (UV map:%s)"
- % (self.uv_map))
-
- obj = context.active_object
- bm = bmesh.from_edit_mesh(obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
- bm.faces.ensure_lookup_table()
-
- # get UV layer
- if self.uv_map == "":
- if not bm.loops.layers.uv:
- self.report(
- {'WARNING'}, "Object must have more than one UV map")
- return {'CANCELLED'}
- uv_layer = bm.loops.layers.uv.verify()
- else:
- uv_layer = bm.loops.layers.uv[self.uv_map]
-
- # get selected face
- dest_uvs = []
- dest_pin_uvs = []
- dest_seams = []
- dest_face_indices = []
- for hist in bm.select_history:
- if isinstance(hist, bmesh.types.BMFace) and hist.select:
- dest_face_indices.append(hist.index)
- uvs = [l[uv_layer].uv.copy() for l in hist.loops]
- pin_uvs = [l[uv_layer].pin_uv for l in hist.loops]
- seams = [l.edge.seam for l in hist.loops]
- dest_uvs.append(uvs)
- dest_pin_uvs.append(pin_uvs)
- dest_seams.append(seams)
- if not dest_uvs or not dest_pin_uvs:
- self.report({'WARNING'}, "No faces are selected")
- return {'CANCELLED'}
- if self.strategy == 'N_N' and len(props.src_uvs) != len(dest_uvs):
- self.report(
- {'WARNING'},
- "Number of selected faces is different from copied faces " +
- "(src:%d, dest:%d)"
- % (len(props.src_uvs), len(dest_uvs)))
- return {'CANCELLED'}
-
- # paste
- for i, idx in enumerate(dest_face_indices):
- suv = None
- spuv = None
- ss = None
- duv = None
- if self.strategy == 'N_N':
- suv = props.src_uvs[i]
- spuv = props.src_pin_uvs[i]
- ss = props.src_seams[i]
- duv = dest_uvs[i]
- elif self.strategy == 'N_M':
- suv = props.src_uvs[i % len(props.src_uvs)]
- spuv = props.src_pin_uvs[i % len(props.src_pin_uvs)]
- ss = props.src_seams[i % len(props.src_seams)]
- duv = dest_uvs[i]
- if len(suv) != len(duv):
- self.report({'WARNING'}, "Some faces are different size")
- return {'CANCELLED'}
- suvs_fr = [uv for uv in suv]
- spuvs_fr = [pin_uv for pin_uv in spuv]
- ss_fr = [s for s in ss]
- # flip UVs
- if self.flip_copied_uv is True:
- suvs_fr.reverse()
- spuvs_fr.reverse()
- ss_fr.reverse()
- # rotate UVs
- for _ in range(self.rotate_copied_uv):
- uv = suvs_fr.pop()
- pin_uv = spuvs_fr.pop()
- s = ss_fr.pop()
- suvs_fr.insert(0, uv)
- spuvs_fr.insert(0, pin_uv)
- ss_fr.insert(0, s)
- # paste UVs
- for l, suv, spuv, ss in zip(bm.faces[idx].loops, suvs_fr,
- spuvs_fr, ss_fr):
- l[uv_layer].uv = suv
- l[uv_layer].pin_uv = spuv
- if self.copy_seams is True:
- l.edge.seam = ss
-
- self.report({'INFO'}, "%d face(s) are copied" % len(dest_uvs))
-
- bmesh.update_edit_mesh(obj.data)
- if self.copy_seams is True:
- obj.data.show_edge_seams = True
-
- return {'FINISHED'}
-
-
-class MUV_CPUVSelSeqPasteUVMenu(bpy.types.Menu):
- """
- Menu class: Paste UV coordinate by selection sequence
- """
-
- bl_idname = "uv.muv_cpuv_selseq_paste_uv_menu"
- bl_label = "Paste UV (Selection Sequence)"
- bl_description = "Paste UV coordinate by selection sequence"
-
- def draw(self, context):
- layout = self.layout
- # create sub menu
- obj = context.active_object
- bm = bmesh.from_edit_mesh(obj.data)
- uv_maps = bm.loops.layers.uv.keys()
- layout.operator(
- MUV_CPUVSelSeqPasteUV.bl_idname,
- text="[Default]", icon="IMAGE_COL").uv_map = ""
- for m in uv_maps:
- layout.operator(
- MUV_CPUVSelSeqPasteUV.bl_idname,
- text=m, icon="IMAGE_COL").uv_map = m
diff --git a/uv_magic_uv/muv_menu.py b/uv_magic_uv/muv_menu.py
deleted file mode 100644
index 47c79bbd..00000000
--- a/uv_magic_uv/muv_menu.py
+++ /dev/null
@@ -1,138 +0,0 @@
-# <pep8-80 compliant>
-
-# ##### BEGIN GPL LICENSE BLOCK #####
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# ##### END GPL LICENSE BLOCK #####
-
-__author__ = "Nutti <nutti.metro@gmail.com>"
-__status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
-
-import bpy
-from . import muv_cpuv_ops
-from . import muv_cpuv_selseq_ops
-from . import muv_transuv_ops
-from . import muv_texlock_ops
-from . import muv_wsuv_ops
-from . import muv_uvw_ops
-
-
-class MUV_CPUVMenu(bpy.types.Menu):
- """
- Menu class: Master menu of Copy/Paste UV coordinate
- """
-
- bl_idname = "uv.muv_cpuv_menu"
- bl_label = "Copy/Paste UV"
- bl_description = "Copy and Paste UV coordinate"
-
- def draw(self, _):
- self.layout.menu(
- muv_cpuv_ops.MUV_CPUVCopyUVMenu.bl_idname, icon="IMAGE_COL")
- self.layout.menu(
- muv_cpuv_ops.MUV_CPUVPasteUVMenu.bl_idname, icon="IMAGE_COL")
- self.layout.menu(
- muv_cpuv_selseq_ops.MUV_CPUVSelSeqCopyUVMenu.bl_idname,
- icon="IMAGE_COL")
- self.layout.menu(
- muv_cpuv_selseq_ops.MUV_CPUVSelSeqPasteUVMenu.bl_idname,
- icon="IMAGE_COL")
-
-
-class MUV_CPUVObjMenu(bpy.types.Menu):
- """
- Menu class: Master menu of Copy/Paste UV coordinate per object
- """
-
- bl_idname = "object.muv_cpuv_obj_menu"
- bl_label = "Copy/Paste UV"
- bl_description = "Copy and Paste UV coordinate per object"
-
- def draw(self, _):
- self.layout.menu(
- muv_cpuv_ops.MUV_CPUVObjCopyUVMenu.bl_idname, icon="IMAGE_COL")
- self.layout.menu(
- muv_cpuv_ops.MUV_CPUVObjPasteUVMenu.bl_idname, icon="IMAGE_COL")
-
-
-class MUV_TransUVMenu(bpy.types.Menu):
- """
- Menu class: Master menu of Transfer UV coordinate
- """
-
- bl_idname = "uv.muv_transuv_menu"
- bl_label = "Transfer UV"
- bl_description = "Transfer UV coordinate"
-
- def draw(self, _):
- self.layout.operator(
- muv_transuv_ops.MUV_TransUVCopy.bl_idname, icon="IMAGE_COL")
- self.layout.operator(
- muv_transuv_ops.MUV_TransUVPaste.bl_idname, icon="IMAGE_COL")
-
-
-class MUV_TexLockMenu(bpy.types.Menu):
- """
- Menu class: Master menu of Texture Lock
- """
-
- bl_idname = "uv.muv_texlock_menu"
- bl_label = "Texture Lock"
- bl_description = "Lock texture when vertices of mesh (Preserve UV)"
-
- def draw(self, _):
- self.layout.operator(
- muv_texlock_ops.MUV_TexLockStart.bl_idname, icon="IMAGE_COL")
- self.layout.operator(
- muv_texlock_ops.MUV_TexLockStop.bl_idname, icon="IMAGE_COL")
- self.layout.operator(
- muv_texlock_ops.MUV_TexLockIntrStart.bl_idname, icon="IMAGE_COL")
- self.layout.operator(
- muv_texlock_ops.MUV_TexLockIntrStop.bl_idname, icon="IMAGE_COL")
-
-
-class MUV_WSUVMenu(bpy.types.Menu):
- """
- Menu class: Master menu of world scale UV
- """
-
- bl_idname = "uv.muv_wsuv_menu"
- bl_label = "World Scale UV"
- bl_description = ""
-
- def draw(self, _):
- self.layout.operator(
- muv_wsuv_ops.MUV_WSUVMeasure.bl_idname, icon="IMAGE_COL")
- self.layout.operator(
- muv_wsuv_ops.MUV_WSUVApply.bl_idname, icon="IMAGE_COL")
-
-
-class MUV_UVWMenu(bpy.types.Menu):
- """
- Menu class: Master menu of UVW
- """
-
- bl_idname = "uv.muv_uvw_menu"
- bl_label = "UVW"
- bl_description = ""
-
- def draw(self, _):
- self.layout.operator(
- muv_uvw_ops.MUV_UVWBoxMap.bl_idname, icon="IMAGE_COL")
- self.layout.operator(
- muv_uvw_ops.MUV_UVWBestPlanerMap.bl_idname, icon="IMAGE_COL")
diff --git a/uv_magic_uv/muv_preferences.py b/uv_magic_uv/muv_preferences.py
deleted file mode 100644
index e14ce99b..00000000
--- a/uv_magic_uv/muv_preferences.py
+++ /dev/null
@@ -1,144 +0,0 @@
-# <pep8-80 compliant>
-
-# ##### BEGIN GPL LICENSE BLOCK #####
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# ##### END GPL LICENSE BLOCK #####
-
-__author__ = "Nutti <nutti.metro@gmail.com>"
-__status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
-
-from bpy.props import (
- BoolProperty,
- FloatProperty,
- FloatVectorProperty,
-)
-from bpy.types import AddonPreferences
-
-
-class MUV_Preferences(AddonPreferences):
- """Preferences class: Preferences for this add-on"""
-
- bl_idname = __package__
-
- # enable/disable switcher
- enable_texproj = BoolProperty(
- name="Texture Projection",
- default=True)
- enable_uvbb = BoolProperty(
- name="Bounding Box",
- default=True)
-
- # for Texture Projection
- texproj_canvas_padding = FloatVectorProperty(
- name="Canvas Padding",
- description="Canvas Padding",
- size=2,
- max=50.0,
- min=0.0,
- default=(20.0, 20.0))
-
- # for UV Bounding Box
- uvbb_cp_size = FloatProperty(
- name="Size",
- description="Control Point Size",
- default=6.0,
- min=3.0,
- max=100.0)
- uvbb_cp_react_size = FloatProperty(
- name="React Size",
- description="Size event fired",
- default=10.0,
- min=3.0,
- max=100.0)
-
- def draw(self, _):
- layout = self.layout
-
- layout.label("Switch Enable/Disable and Configurate Features:")
-
- layout.prop(self, "enable_texproj")
- if self.enable_texproj:
- sp = layout.split(percentage=0.05)
- col = sp.column() # spacer
- sp = sp.split(percentage=0.3)
- col = sp.column()
- col.label("Texture Display: ")
- col.prop(self, "texproj_canvas_padding")
-
- layout.prop(self, "enable_uvbb")
- if self.enable_uvbb:
- sp = layout.split(percentage=0.05)
- col = sp.column() # spacer
- sp = sp.split(percentage=0.3)
- col = sp.column()
- col.label("Control Point: ")
- col.prop(self, "uvbb_cp_size")
- col.prop(self, "uvbb_cp_react_size")
-
- layout.label("Description:")
- column = layout.column(align=True)
- column.label("Magic UV is composed of many UV editing features.")
- column.label("See tutorial page if you are new to this add-on.")
- column.label("https://github.com/nutti/Magic-UV/wiki/Tutorial")
-
- layout.label("Location:")
-
- row = layout.row(align=True)
- sp = row.split(percentage=0.3)
- sp.label("View3D > U")
- sp = sp.split(percentage=1.0)
- col = sp.column(align=True)
- col.label("Copy/Paste UV Coordinates")
- col.label("Copy/Paste UV Coordinates (by selection sequence)")
- col.label("Flip/Rotate UVs")
- col.label("Transfer UV")
- col.label("Move UV from 3D View")
- col.label("Texture Lock")
- col.label("Mirror UV")
- col.label("World Scale UV")
- col.label("Unwrap Constraint")
- col.label("Preserve UV Aspect")
-
- row = layout.row(align=True)
- sp = row.split(percentage=0.3)
- sp.label("View3D > Object")
- sp = sp.split(percentage=1.0)
- col = sp.column(align=True)
- col.label("Copy/Paste UV Coordinates (Among same objects)")
-
- row = layout.row(align=True)
- sp = row.split(percentage=0.3)
- sp.label("ImageEditor > Property Panel")
- sp = sp.split(percentage=1.0)
- col = sp.column(align=True)
- col.label("Manipulate UV with Bounding Box in UV Editor")
-
- row = layout.row(align=True)
- sp = row.split(percentage=0.3)
- sp.label("View3D > Property Panel")
- sp = sp.split(percentage=1.0)
- col = sp.column(align=True)
- col.label("Texture Projection")
-
- row = layout.row(align=True)
- sp = row.split(percentage=0.3)
- sp.label("ImageEditor > UVs")
- sp = sp.split(percentage=1.0)
- col = sp.column(align=True)
- col.label("Pack UV (with same UV island packing)")
diff --git a/uv_magic_uv/muv_props.py b/uv_magic_uv/muv_props.py
deleted file mode 100644
index c0a7d961..00000000
--- a/uv_magic_uv/muv_props.py
+++ /dev/null
@@ -1,148 +0,0 @@
-# <pep8-80 compliant>
-
-# ##### BEGIN GPL LICENSE BLOCK #####
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# ##### END GPL LICENSE BLOCK #####
-
-__author__ = "Nutti <nutti.metro@gmail.com>"
-__status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
-
-import bpy
-from bpy.props import (
- FloatProperty,
- EnumProperty,
- BoolProperty,
-)
-
-
-DEBUG = False
-
-
-def get_loaded_texture_name(_, __):
- items = [(key, key, "") for key in bpy.data.images.keys()]
- items.append(("None", "None", ""))
- return items
-
-
-# Properties used in this add-on.
-class MUV_Properties():
- cpuv = None
- cpuv_obj = None
- cpuv_selseq = None
- transuv = None
- uvbb = None
- texproj = None
- texlock = None
- texwrap = None
- wsuv = None
-
- def __init__(self):
- self.cpuv = MUV_CPUVProps()
- self.cpuv_obj = MUV_CPUVProps()
- self.cpuv_selseq = MUV_CPUVSelSeqProps()
- self.transuv = MUV_TransUVProps()
- self.uvbb = MUV_UVBBProps()
- self.texproj = MUV_TexProjProps()
- self.texlock = MUV_TexLockProps()
- self.texwrap = MUV_TexWrapProps()
- self.wsuv = MUV_WSUVProps()
-
-
-class MUV_CPUVProps():
- src_uvs = []
- src_pin_uvs = []
- src_seams = []
-
-
-class MUV_CPUVSelSeqProps():
- src_uvs = []
- src_pin_uvs = []
- src_seams = []
-
-
-class MUV_TransUVProps():
- topology_copied = []
-
-
-class MUV_UVBBProps():
- uv_info_ini = []
- ctrl_points_ini = []
- ctrl_points = []
- running = False
-
-
-class MUV_TexProjProps():
- running = False
-
-
-class MUV_TexLockProps():
- verts_orig = None
- intr_verts_orig = None
- intr_running = False
-
-
-class MUV_TexWrapProps():
- src_face_index = -1
-
-
-class MUV_WSUVProps():
- ref_sv = None
- ref_suv = None
-
-
-def init_props(scene):
- scene.muv_props = MUV_Properties()
- scene.muv_uvbb_uniform_scaling = BoolProperty(
- name="Uniform Scaling",
- description="Enable Uniform Scaling",
- default=False)
- scene.muv_texproj_tex_magnitude = FloatProperty(
- name="Magnitude",
- description="Texture Magnitude",
- default=0.5,
- min=0.0,
- max=100.0)
- scene.muv_texproj_tex_image = EnumProperty(
- name="Image",
- description="Texture Image",
- items=get_loaded_texture_name)
- scene.muv_texproj_tex_transparency = FloatProperty(
- name="Transparency",
- description="Texture Transparency",
- default=0.2,
- min=0.0,
- max=1.0)
- scene.muv_texproj_adjust_window = BoolProperty(
- name="Adjust Window",
- description="Size of renderered texture is fitted to window",
- default=True)
- scene.muv_texproj_apply_tex_aspect = BoolProperty(
- name="Texture Aspect Ratio",
- description="Apply Texture Aspect ratio to displayed texture",
- default=True)
-
-
-def clear_props(scene):
- del scene.muv_props
- del scene.muv_uvbb_uniform_scaling
- del scene.muv_texproj_tex_magnitude
- del scene.muv_texproj_tex_image
- del scene.muv_texproj_tex_transparency
- del scene.muv_texproj_adjust_window
- del scene.muv_texproj_apply_tex_aspect
diff --git a/uv_magic_uv/op/__init__.py b/uv_magic_uv/op/__init__.py
new file mode 100644
index 00000000..75885ef6
--- /dev/null
+++ b/uv_magic_uv/op/__init__.py
@@ -0,0 +1,72 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
+
+if "bpy" in locals():
+ import importlib
+ importlib.reload(align_uv)
+ importlib.reload(align_uv_cursor)
+ importlib.reload(copy_paste_uv)
+ importlib.reload(copy_paste_uv_object)
+ importlib.reload(copy_paste_uv_uvedit)
+ importlib.reload(flip_rotate_uv)
+ importlib.reload(mirror_uv)
+ importlib.reload(move_uv)
+ importlib.reload(pack_uv)
+ importlib.reload(preserve_uv_aspect)
+ importlib.reload(smooth_uv)
+ importlib.reload(texture_lock)
+ importlib.reload(texture_projection)
+ importlib.reload(texture_wrap)
+ importlib.reload(transfer_uv)
+ importlib.reload(unwrap_constraint)
+ importlib.reload(uv_bounding_box)
+ importlib.reload(uv_inspection)
+ importlib.reload(uv_sculpt)
+ importlib.reload(uvw)
+ importlib.reload(world_scale_uv)
+else:
+ from . import align_uv
+ from . import align_uv_cursor
+ from . import copy_paste_uv
+ from . import copy_paste_uv_object
+ from . import copy_paste_uv_uvedit
+ from . import flip_rotate_uv
+ from . import mirror_uv
+ from . import move_uv
+ from . import pack_uv
+ from . import preserve_uv_aspect
+ from . import smooth_uv
+ from . import texture_lock
+ from . import texture_projection
+ from . import texture_wrap
+ from . import transfer_uv
+ from . import unwrap_constraint
+ from . import uv_bounding_box
+ from . import uv_inspection
+ from . import uv_sculpt
+ from . import uvw
+ from . import world_scale_uv
+
+import bpy
diff --git a/uv_magic_uv/op/align_uv.py b/uv_magic_uv/op/align_uv.py
new file mode 100644
index 00000000..dcfb57c3
--- /dev/null
+++ b/uv_magic_uv/op/align_uv.py
@@ -0,0 +1,757 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
+
+import math
+from math import atan2, tan, sin, cos
+
+import bpy
+import bmesh
+from mathutils import Vector
+from bpy.props import EnumProperty, BoolProperty
+
+from .. import common
+
+
+# get sum vertex length of loop sequences
+def get_loop_vert_len(loops):
+ length = 0
+ for l1, l2 in zip(loops[:-1], loops[1:]):
+ diff = l2.vert.co - l1.vert.co
+ length = length + abs(diff.length)
+
+ return length
+
+
+# get sum uv length of loop sequences
+def get_loop_uv_len(loops, uv_layer):
+ length = 0
+ for l1, l2 in zip(loops[:-1], loops[1:]):
+ diff = l2[uv_layer].uv - l1[uv_layer].uv
+ length = length + abs(diff.length)
+
+ return length
+
+
+# get center/radius of circle by 3 vertices
+def get_circle(v):
+ alpha = atan2((v[0].y - v[1].y), (v[0].x - v[1].x)) + math.pi / 2
+ beta = atan2((v[1].y - v[2].y), (v[1].x - v[2].x)) + math.pi / 2
+ ex = (v[0].x + v[1].x) / 2.0
+ ey = (v[0].y + v[1].y) / 2.0
+ fx = (v[1].x + v[2].x) / 2.0
+ fy = (v[1].y + v[2].y) / 2.0
+ cx = (ey - fy - ex * tan(alpha) + fx * tan(beta)) / \
+ (tan(beta) - tan(alpha))
+ cy = ey - (ex - cx) * tan(alpha)
+ center = Vector((cx, cy))
+
+ r = v[0] - center
+ radian = r.length
+
+ return center, radian
+
+
+# get position on circle with same arc length
+def calc_v_on_circle(v, center, radius):
+ base = v[0]
+ theta = atan2(base.y - center.y, base.x - center.x)
+ new_v = []
+ for i in range(len(v)):
+ angle = theta + i * 2 * math.pi / len(v)
+ new_v.append(Vector((center.x + radius * sin(angle),
+ center.y + radius * cos(angle))))
+
+ return new_v
+
+
+class MUV_AUVCircle(bpy.types.Operator):
+
+ bl_idname = "uv.muv_auv_circle"
+ bl_label = "Circle"
+ bl_description = "Align UV coordinates to Circle"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ transmission = BoolProperty(
+ name="Transmission",
+ description="Align linked UVs",
+ default=False
+ )
+ select = BoolProperty(
+ name="Select",
+ description="Select UVs which are aligned",
+ default=False
+ )
+
+ @classmethod
+ def poll(cls, context):
+ return context.mode == 'EDIT_MESH'
+
+ def execute(self, context):
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
+ uv_layer = bm.loops.layers.uv.verify()
+
+ # loop_seqs[horizontal][vertical][loop]
+ loop_seqs, error = common.get_loop_sequences(bm, uv_layer, True)
+ if not loop_seqs:
+ self.report({'WARNING'}, error)
+ return {'CANCELLED'}
+
+ # get circle and new UVs
+ uvs = [hseq[0][0][uv_layer].uv.copy() for hseq in loop_seqs]
+ c, r = get_circle(uvs[0:3])
+ new_uvs = calc_v_on_circle(uvs, c, r)
+
+ # check center UV of circle
+ center = loop_seqs[0][-1][0].vert
+ for hseq in loop_seqs[1:]:
+ if len(hseq[-1]) != 1:
+ self.report({'WARNING'}, "Last face must be triangle")
+ return {'CANCELLED'}
+ if hseq[-1][0].vert != center:
+ self.report({'WARNING'}, "Center must be identical")
+ return {'CANCELLED'}
+
+ # align to circle
+ if self.transmission:
+ for hidx, hseq in enumerate(loop_seqs):
+ for vidx, pair in enumerate(hseq):
+ all_ = int((len(hseq) + 1) / 2)
+ r = (all_ - int((vidx + 1) / 2)) / all_
+ pair[0][uv_layer].uv = c + (new_uvs[hidx] - c) * r
+ if self.select:
+ pair[0][uv_layer].select = True
+
+ if len(pair) < 2:
+ continue
+ # for quad polygon
+ next_hidx = (hidx + 1) % len(loop_seqs)
+ pair[1][uv_layer].uv = c + ((new_uvs[next_hidx]) - c) * r
+ if self.select:
+ pair[1][uv_layer].select = True
+ else:
+ for hidx, hseq in enumerate(loop_seqs):
+ pair = hseq[0]
+ pair[0][uv_layer].uv = new_uvs[hidx]
+ pair[1][uv_layer].uv = new_uvs[(hidx + 1) % len(loop_seqs)]
+ if self.select:
+ pair[0][uv_layer].select = True
+ pair[1][uv_layer].select = True
+
+ bmesh.update_edit_mesh(obj.data)
+
+ return {'FINISHED'}
+
+
+# get horizontal differential of UV influenced by mesh vertex
+def get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, pair_idx):
+ common.debug_print(
+ "vidx={0}, hidx={1}, pair_idx={2}".format(vidx, hidx, pair_idx))
+
+ # get total vertex length
+ hloops = []
+ for s in loop_seqs:
+ hloops.extend([s[vidx][0], s[vidx][1]])
+ vert_total_hlen = get_loop_vert_len(hloops)
+ common.debug_print(vert_total_hlen)
+
+ # target vertex length
+ hloops = []
+ for s in loop_seqs[:hidx]:
+ hloops.extend([s[vidx][0], s[vidx][1]])
+ for pidx, l in enumerate(loop_seqs[hidx][vidx]):
+ if pidx > pair_idx:
+ break
+ hloops.append(l)
+ vert_hlen = get_loop_vert_len(hloops)
+ common.debug_print(vert_hlen)
+
+ # get total UV length
+ # uv_all_hdiff = loop_seqs[-1][0][-1][uv_layer].uv -
+ # loop_seqs[0][0][0][uv_layer].uv
+ uv_total_hlen = loop_seqs[-1][vidx][-1][uv_layer].uv -\
+ loop_seqs[0][vidx][0][uv_layer].uv
+ common.debug_print(uv_total_hlen)
+
+ return uv_total_hlen * vert_hlen / vert_total_hlen
+
+
+# get vertical differential of UV influenced by mesh vertex
+def get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, pair_idx):
+ common.debug_print(
+ "vidx={0}, hidx={1}, pair_idx={2}".format(vidx, hidx, pair_idx))
+
+ # get total vertex length
+ hloops = []
+ for s in loop_seqs[hidx]:
+ hloops.append(s[pair_idx])
+ vert_total_hlen = get_loop_vert_len(hloops)
+ common.debug_print(vert_total_hlen)
+
+ # target vertex length
+ hloops = []
+ for s in loop_seqs[hidx][:vidx + 1]:
+ hloops.append(s[pair_idx])
+ vert_hlen = get_loop_vert_len(hloops)
+ common.debug_print(vert_hlen)
+
+ # get total UV length
+ # uv_all_hdiff = loop_seqs[0][-1][pair_idx][uv_layer].uv - \
+ # loop_seqs[0][0][pair_idx][uv_layer].uv
+ uv_total_hlen = loop_seqs[hidx][-1][pair_idx][uv_layer].uv -\
+ loop_seqs[hidx][0][pair_idx][uv_layer].uv
+ common.debug_print(uv_total_hlen)
+
+ return uv_total_hlen * vert_hlen / vert_total_hlen
+
+
+# get horizontal differential of UV no influenced
+def get_hdiff_uv(uv_layer, loop_seqs, hidx):
+ base_uv = loop_seqs[0][0][0][uv_layer].uv.copy()
+ h_uv = loop_seqs[-1][0][1][uv_layer].uv.copy() - base_uv
+
+ return hidx * h_uv / len(loop_seqs)
+
+
+# get vertical differential of UV no influenced
+def get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx):
+ base_uv = loop_seqs[0][0][0][uv_layer].uv.copy()
+ v_uv = loop_seqs[0][-1][0][uv_layer].uv.copy() - base_uv
+
+ hseq = loop_seqs[hidx]
+ return int((vidx + 1) / 2) * v_uv / (len(hseq) / 2)
+
+
+class MUV_AUVStraighten(bpy.types.Operator):
+
+ bl_idname = "uv.muv_auv_straighten"
+ bl_label = "Straighten"
+ bl_description = "Straighten UV coordinates"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ transmission = BoolProperty(
+ name="Transmission",
+ description="Align linked UVs",
+ default=False
+ )
+ select = BoolProperty(
+ name="Select",
+ description="Select UVs which are aligned",
+ default=False
+ )
+ vertical = BoolProperty(
+ name="Vert-Infl (Vertical)",
+ description="Align vertical direction influenced "
+ "by mesh vertex proportion",
+ default=False
+ )
+ horizontal = BoolProperty(
+ name="Vert-Infl (Horizontal)",
+ description="Align horizontal direction influenced "
+ "by mesh vertex proportion",
+ default=False
+ )
+
+ @classmethod
+ def poll(cls, context):
+ return context.mode == 'EDIT_MESH'
+
+ # selected and paralleled UV loop sequence will be aligned
+ def __align_w_transmission(self, loop_seqs, uv_layer):
+ base_uv = loop_seqs[0][0][0][uv_layer].uv.copy()
+
+ # calculate diff UVs
+ diff_uvs = []
+ # hseq[vertical][loop]
+ for hidx, hseq in enumerate(loop_seqs):
+ # pair[loop]
+ diffs = []
+ for vidx in range(0, len(hseq), 2):
+ if self.horizontal:
+ hdiff_uvs = [
+ get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 0),
+ get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1),
+ get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+ hidx, 0),
+ get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+ hidx, 1),
+ ]
+ else:
+ hdiff_uvs = [
+ get_hdiff_uv(uv_layer, loop_seqs, hidx),
+ get_hdiff_uv(uv_layer, loop_seqs, hidx + 1),
+ get_hdiff_uv(uv_layer, loop_seqs, hidx),
+ get_hdiff_uv(uv_layer, loop_seqs, hidx + 1)
+ ]
+ if self.vertical:
+ vdiff_uvs = [
+ get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 0),
+ get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1),
+ get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+ hidx, 0),
+ get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+ hidx, 1),
+ ]
+ else:
+ vdiff_uvs = [
+ get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx),
+ get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx),
+ get_vdiff_uv(uv_layer, loop_seqs, vidx + 1, hidx),
+ get_vdiff_uv(uv_layer, loop_seqs, vidx + 1, hidx)
+ ]
+ diffs.append([hdiff_uvs, vdiff_uvs])
+ diff_uvs.append(diffs)
+
+ # update UV
+ for hseq, diffs in zip(loop_seqs, diff_uvs):
+ for vidx in range(0, len(hseq), 2):
+ loops = [
+ hseq[vidx][0], hseq[vidx][1],
+ hseq[vidx + 1][0], hseq[vidx + 1][1]
+ ]
+ for l, hdiff, vdiff in zip(loops, diffs[int(vidx / 2)][0],
+ diffs[int(vidx / 2)][1]):
+ l[uv_layer].uv = base_uv + hdiff + vdiff
+ if self.select:
+ l[uv_layer].select = True
+
+ # only selected UV loop sequence will be aligned
+ def __align_wo_transmission(self, loop_seqs, uv_layer):
+ base_uv = loop_seqs[0][0][0][uv_layer].uv.copy()
+
+ h_uv = loop_seqs[-1][0][1][uv_layer].uv.copy() - base_uv
+ for hidx, hseq in enumerate(loop_seqs):
+ # only selected loop pair is targeted
+ pair = hseq[0]
+ hdiff_uv_0 = hidx * h_uv / len(loop_seqs)
+ hdiff_uv_1 = (hidx + 1) * h_uv / len(loop_seqs)
+ pair[0][uv_layer].uv = base_uv + hdiff_uv_0
+ pair[1][uv_layer].uv = base_uv + hdiff_uv_1
+ if self.select:
+ pair[0][uv_layer].select = True
+ pair[1][uv_layer].select = True
+
+ def __align(self, loop_seqs, uv_layer):
+ if self.transmission:
+ self.__align_w_transmission(loop_seqs, uv_layer)
+ else:
+ self.__align_wo_transmission(loop_seqs, uv_layer)
+
+ def execute(self, context):
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
+ uv_layer = bm.loops.layers.uv.verify()
+
+ # loop_seqs[horizontal][vertical][loop]
+ loop_seqs, error = common.get_loop_sequences(bm, uv_layer)
+ if not loop_seqs:
+ self.report({'WARNING'}, error)
+ return {'CANCELLED'}
+
+ # align
+ self.__align(loop_seqs, uv_layer)
+
+ bmesh.update_edit_mesh(obj.data)
+
+ return {'FINISHED'}
+
+
+class MUV_AUVAxis(bpy.types.Operator):
+
+ bl_idname = "uv.muv_auv_axis"
+ bl_label = "XY-Axis"
+ bl_description = "Align UV to XY-axis"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ transmission = BoolProperty(
+ name="Transmission",
+ description="Align linked UVs",
+ default=False
+ )
+ select = BoolProperty(
+ name="Select",
+ description="Select UVs which are aligned",
+ default=False
+ )
+ vertical = BoolProperty(
+ name="Vert-Infl (Vertical)",
+ description="Align vertical direction influenced "
+ "by mesh vertex proportion",
+ default=False
+ )
+ horizontal = BoolProperty(
+ name="Vert-Infl (Horizontal)",
+ description="Align horizontal direction influenced "
+ "by mesh vertex proportion",
+ default=False
+ )
+ location = EnumProperty(
+ name="Location",
+ description="Align location",
+ items=[
+ ('LEFT_TOP', "Left/Top", "Align to Left or Top"),
+ ('MIDDLE', "Middle", "Align to middle"),
+ ('RIGHT_BOTTOM', "Right/Bottom", "Align to Right or Bottom")
+ ],
+ default='MIDDLE'
+ )
+
+ @classmethod
+ def poll(cls, context):
+ return context.mode == 'EDIT_MESH'
+
+ # get min/max of UV
+ def __get_uv_max_min(self, loop_seqs, uv_layer):
+ uv_max = Vector((-1000000.0, -1000000.0))
+ uv_min = Vector((1000000.0, 1000000.0))
+ for hseq in loop_seqs:
+ for l in hseq[0]:
+ uv = l[uv_layer].uv
+ uv_max.x = max(uv.x, uv_max.x)
+ uv_max.y = max(uv.y, uv_max.y)
+ uv_min.x = min(uv.x, uv_min.x)
+ uv_min.y = min(uv.y, uv_min.y)
+
+ return uv_max, uv_min
+
+ # get UV differentiation when UVs are aligned to X-axis
+ def __get_x_axis_align_diff_uvs(self, loop_seqs, uv_layer, uv_min,
+ width, height):
+ diff_uvs = []
+ for hidx, hseq in enumerate(loop_seqs):
+ pair = hseq[0]
+ luv0 = pair[0][uv_layer]
+ luv1 = pair[1][uv_layer]
+ target_uv0 = Vector((0.0, 0.0))
+ target_uv1 = Vector((0.0, 0.0))
+ if self.location == 'RIGHT_BOTTOM':
+ target_uv0.y = target_uv1.y = uv_min.y
+ elif self.location == 'MIDDLE':
+ target_uv0.y = target_uv1.y = uv_min.y + height * 0.5
+ elif self.location == 'LEFT_TOP':
+ target_uv0.y = target_uv1.y = uv_min.y + height
+ if luv0.uv.x < luv1.uv.x:
+ target_uv0.x = uv_min.x + hidx * width / len(loop_seqs)
+ target_uv1.x = uv_min.x + (hidx + 1) * width / len(loop_seqs)
+ else:
+ target_uv0.x = uv_min.x + (hidx + 1) * width / len(loop_seqs)
+ target_uv1.x = uv_min.x + hidx * width / len(loop_seqs)
+ diff_uvs.append([target_uv0 - luv0.uv, target_uv1 - luv1.uv])
+
+ return diff_uvs
+
+ # get UV differentiation when UVs are aligned to Y-axis
+ def __get_y_axis_align_diff_uvs(self, loop_seqs, uv_layer, uv_min,
+ width, height):
+ diff_uvs = []
+ for hidx, hseq in enumerate(loop_seqs):
+ pair = hseq[0]
+ luv0 = pair[0][uv_layer]
+ luv1 = pair[1][uv_layer]
+ target_uv0 = Vector((0.0, 0.0))
+ target_uv1 = Vector((0.0, 0.0))
+ if self.location == 'RIGHT_BOTTOM':
+ target_uv0.x = target_uv1.x = uv_min.x + width
+ elif self.location == 'MIDDLE':
+ target_uv0.x = target_uv1.x = uv_min.x + width * 0.5
+ elif self.location == 'LEFT_TOP':
+ target_uv0.x = target_uv1.x = uv_min.x
+ if luv0.uv.y < luv1.uv.y:
+ target_uv0.y = uv_min.y + hidx * height / len(loop_seqs)
+ target_uv1.y = uv_min.y + (hidx + 1) * height / len(loop_seqs)
+ else:
+ target_uv0.y = uv_min.y + (hidx + 1) * height / len(loop_seqs)
+ target_uv1.y = uv_min.y + hidx * height / len(loop_seqs)
+ diff_uvs.append([target_uv0 - luv0.uv, target_uv1 - luv1.uv])
+
+ return diff_uvs
+
+ # only selected UV loop sequence will be aligned along to X-axis
+ def __align_to_x_axis_wo_transmission(self, loop_seqs, uv_layer,
+ uv_min, width, height):
+ # reverse if the UV coordinate is not sorted by position
+ need_revese = loop_seqs[0][0][0][uv_layer].uv.x > \
+ loop_seqs[-1][0][0][uv_layer].uv.x
+ if need_revese:
+ loop_seqs.reverse()
+ for hidx, hseq in enumerate(loop_seqs):
+ for vidx, pair in enumerate(hseq):
+ tmp = loop_seqs[hidx][vidx][0]
+ loop_seqs[hidx][vidx][0] = loop_seqs[hidx][vidx][1]
+ loop_seqs[hidx][vidx][1] = tmp
+
+ # get UV differential
+ diff_uvs = self.__get_x_axis_align_diff_uvs(loop_seqs, uv_layer,
+ uv_min, width, height)
+
+ # update UV
+ for hseq, duv in zip(loop_seqs, diff_uvs):
+ pair = hseq[0]
+ luv0 = pair[0][uv_layer]
+ luv1 = pair[1][uv_layer]
+ luv0.uv = luv0.uv + duv[0]
+ luv1.uv = luv1.uv + duv[1]
+
+ # only selected UV loop sequence will be aligned along to Y-axis
+ def __align_to_y_axis_wo_transmission(self, loop_seqs, uv_layer,
+ uv_min, width, height):
+ # reverse if the UV coordinate is not sorted by position
+ need_revese = loop_seqs[0][0][0][uv_layer].uv.y > \
+ loop_seqs[-1][0][0][uv_layer].uv.y
+ if need_revese:
+ loop_seqs.reverse()
+ for hidx, hseq in enumerate(loop_seqs):
+ for vidx, pair in enumerate(hseq):
+ tmp = loop_seqs[hidx][vidx][0]
+ loop_seqs[hidx][vidx][0] = loop_seqs[hidx][vidx][1]
+ loop_seqs[hidx][vidx][1] = tmp
+
+ # get UV differential
+ diff_uvs = self.__get_y_axis_align_diff_uvs(loop_seqs, uv_layer,
+ uv_min, width, height)
+
+ # update UV
+ for hseq, duv in zip(loop_seqs, diff_uvs):
+ pair = hseq[0]
+ luv0 = pair[0][uv_layer]
+ luv1 = pair[1][uv_layer]
+ luv0.uv = luv0.uv + duv[0]
+ luv1.uv = luv1.uv + duv[1]
+
+ # selected and paralleled UV loop sequence will be aligned along to X-axis
+ def __align_to_x_axis_w_transmission(self, loop_seqs, uv_layer,
+ uv_min, width, height):
+ # reverse if the UV coordinate is not sorted by position
+ need_revese = loop_seqs[0][0][0][uv_layer].uv.x > \
+ loop_seqs[-1][0][0][uv_layer].uv.x
+ if need_revese:
+ loop_seqs.reverse()
+ for hidx, hseq in enumerate(loop_seqs):
+ for vidx in range(len(hseq)):
+ tmp = loop_seqs[hidx][vidx][0]
+ loop_seqs[hidx][vidx][0] = loop_seqs[hidx][vidx][1]
+ loop_seqs[hidx][vidx][1] = tmp
+
+ # get offset UVs when the UVs are aligned to X-axis
+ align_diff_uvs = self.__get_x_axis_align_diff_uvs(loop_seqs, uv_layer,
+ uv_min, width,
+ height)
+ base_uv = loop_seqs[0][0][0][uv_layer].uv.copy()
+ offset_uvs = []
+ for hseq, aduv in zip(loop_seqs, align_diff_uvs):
+ luv0 = hseq[0][0][uv_layer]
+ luv1 = hseq[0][1][uv_layer]
+ offset_uvs.append([luv0.uv + aduv[0] - base_uv,
+ luv1.uv + aduv[1] - base_uv])
+
+ # get UV differential
+ diff_uvs = []
+ # hseq[vertical][loop]
+ for hidx, hseq in enumerate(loop_seqs):
+ # pair[loop]
+ diffs = []
+ for vidx in range(0, len(hseq), 2):
+ if self.horizontal:
+ hdiff_uvs = [
+ get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 0),
+ get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1),
+ get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+ hidx, 0),
+ get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+ hidx, 1),
+ ]
+ hdiff_uvs[0].y = hdiff_uvs[0].y + offset_uvs[hidx][0].y
+ hdiff_uvs[1].y = hdiff_uvs[1].y + offset_uvs[hidx][1].y
+ hdiff_uvs[2].y = hdiff_uvs[2].y + offset_uvs[hidx][0].y
+ hdiff_uvs[3].y = hdiff_uvs[3].y + offset_uvs[hidx][1].y
+ else:
+ hdiff_uvs = [
+ offset_uvs[hidx][0],
+ offset_uvs[hidx][1],
+ offset_uvs[hidx][0],
+ offset_uvs[hidx][1],
+ ]
+ if self.vertical:
+ vdiff_uvs = [
+ get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 0),
+ get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1),
+ get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+ hidx, 0),
+ get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+ hidx, 1),
+ ]
+ else:
+ vdiff_uvs = [
+ get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx),
+ get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx),
+ get_vdiff_uv(uv_layer, loop_seqs, vidx + 1, hidx),
+ get_vdiff_uv(uv_layer, loop_seqs, vidx + 1, hidx)
+ ]
+ diffs.append([hdiff_uvs, vdiff_uvs])
+ diff_uvs.append(diffs)
+
+ # update UV
+ for hseq, diffs in zip(loop_seqs, diff_uvs):
+ for vidx in range(0, len(hseq), 2):
+ loops = [
+ hseq[vidx][0], hseq[vidx][1],
+ hseq[vidx + 1][0], hseq[vidx + 1][1]
+ ]
+ for l, hdiff, vdiff in zip(loops, diffs[int(vidx / 2)][0],
+ diffs[int(vidx / 2)][1]):
+ l[uv_layer].uv = base_uv + hdiff + vdiff
+ if self.select:
+ l[uv_layer].select = True
+
+ # selected and paralleled UV loop sequence will be aligned along to Y-axis
+ def __align_to_y_axis_w_transmission(self, loop_seqs, uv_layer,
+ uv_min, width, height):
+ # reverse if the UV coordinate is not sorted by position
+ need_revese = loop_seqs[0][0][0][uv_layer].uv.y > \
+ loop_seqs[-1][0][-1][uv_layer].uv.y
+ if need_revese:
+ loop_seqs.reverse()
+ for hidx, hseq in enumerate(loop_seqs):
+ for vidx in range(len(hseq)):
+ tmp = loop_seqs[hidx][vidx][0]
+ loop_seqs[hidx][vidx][0] = loop_seqs[hidx][vidx][1]
+ loop_seqs[hidx][vidx][1] = tmp
+
+ # get offset UVs when the UVs are aligned to Y-axis
+ align_diff_uvs = self.__get_y_axis_align_diff_uvs(loop_seqs, uv_layer,
+ uv_min, width,
+ height)
+ base_uv = loop_seqs[0][0][0][uv_layer].uv.copy()
+ offset_uvs = []
+ for hseq, aduv in zip(loop_seqs, align_diff_uvs):
+ luv0 = hseq[0][0][uv_layer]
+ luv1 = hseq[0][1][uv_layer]
+ offset_uvs.append([luv0.uv + aduv[0] - base_uv,
+ luv1.uv + aduv[1] - base_uv])
+
+ # get UV differential
+ diff_uvs = []
+ # hseq[vertical][loop]
+ for hidx, hseq in enumerate(loop_seqs):
+ # pair[loop]
+ diffs = []
+ for vidx in range(0, len(hseq), 2):
+ if self.horizontal:
+ hdiff_uvs = [
+ get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 0),
+ get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1),
+ get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+ hidx, 0),
+ get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+ hidx, 1),
+ ]
+ hdiff_uvs[0].x = hdiff_uvs[0].x + offset_uvs[hidx][0].x
+ hdiff_uvs[1].x = hdiff_uvs[1].x + offset_uvs[hidx][1].x
+ hdiff_uvs[2].x = hdiff_uvs[2].x + offset_uvs[hidx][0].x
+ hdiff_uvs[3].x = hdiff_uvs[3].x + offset_uvs[hidx][1].x
+ else:
+ hdiff_uvs = [
+ offset_uvs[hidx][0],
+ offset_uvs[hidx][1],
+ offset_uvs[hidx][0],
+ offset_uvs[hidx][1],
+ ]
+ if self.vertical:
+ vdiff_uvs = [
+ get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 0),
+ get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1),
+ get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+ hidx, 0),
+ get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+ hidx, 1),
+ ]
+ else:
+ vdiff_uvs = [
+ get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx),
+ get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx),
+ get_vdiff_uv(uv_layer, loop_seqs, vidx + 1, hidx),
+ get_vdiff_uv(uv_layer, loop_seqs, vidx + 1, hidx)
+ ]
+ diffs.append([hdiff_uvs, vdiff_uvs])
+ diff_uvs.append(diffs)
+
+ # update UV
+ for hseq, diffs in zip(loop_seqs, diff_uvs):
+ for vidx in range(0, len(hseq), 2):
+ loops = [
+ hseq[vidx][0], hseq[vidx][1],
+ hseq[vidx + 1][0], hseq[vidx + 1][1]
+ ]
+ for l, hdiff, vdiff in zip(loops, diffs[int(vidx / 2)][0],
+ diffs[int(vidx / 2)][1]):
+ l[uv_layer].uv = base_uv + hdiff + vdiff
+ if self.select:
+ l[uv_layer].select = True
+
+ def __align(self, loop_seqs, uv_layer, uv_min, width, height):
+ # align along to x-axis
+ if width > height:
+ if self.transmission:
+ self.__align_to_x_axis_w_transmission(loop_seqs, uv_layer,
+ uv_min, width, height)
+ else:
+ self.__align_to_x_axis_wo_transmission(loop_seqs, uv_layer,
+ uv_min, width, height)
+ # align along to y-axis
+ else:
+ if self.transmission:
+ self.__align_to_y_axis_w_transmission(loop_seqs, uv_layer,
+ uv_min, width, height)
+ else:
+ self.__align_to_y_axis_wo_transmission(loop_seqs, uv_layer,
+ uv_min, width, height)
+
+ def execute(self, context):
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
+ uv_layer = bm.loops.layers.uv.verify()
+
+ # loop_seqs[horizontal][vertical][loop]
+ loop_seqs, error = common.get_loop_sequences(bm, uv_layer)
+ if not loop_seqs:
+ self.report({'WARNING'}, error)
+ return {'CANCELLED'}
+
+ # get height and width
+ uv_max, uv_min = self.__get_uv_max_min(loop_seqs, uv_layer)
+ width = uv_max.x - uv_min.x
+ height = uv_max.y - uv_min.y
+
+ self.__align(loop_seqs, uv_layer, uv_min, width, height)
+
+ bmesh.update_edit_mesh(obj.data)
+
+ return {'FINISHED'}
diff --git a/uv_magic_uv/op/align_uv_cursor.py b/uv_magic_uv/op/align_uv_cursor.py
new file mode 100644
index 00000000..cae1c89a
--- /dev/null
+++ b/uv_magic_uv/op/align_uv_cursor.py
@@ -0,0 +1,154 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
+
+import bpy
+from mathutils import Vector
+from bpy.props import EnumProperty
+import bmesh
+
+from .. import common
+
+
+class MUV_AUVCAlignOps(bpy.types.Operator):
+
+ bl_idname = "uv.muv_auvc_align"
+ bl_label = "Align"
+ bl_description = "Align cursor to the center of UV island"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ position = EnumProperty(
+ items=(
+ ('CENTER', "Center", "Align to Center"),
+ ('LEFT_TOP', "Left Top", "Align to Left Top"),
+ ('LEFT_MIDDLE', "Left Middle", "Align to Left Middle"),
+ ('LEFT_BOTTOM', "Left Bottom", "Align to Left Bottom"),
+ ('MIDDLE_TOP', "Middle Top", "Align to Middle Top"),
+ ('MIDDLE_BOTTOM', "Middle Bottom", "Align to Middle Bottom"),
+ ('RIGHT_TOP', "Right Top", "Align to Right Top"),
+ ('RIGHT_MIDDLE', "Right Middle", "Align to Right Middle"),
+ ('RIGHT_BOTTOM', "Right Bottom", "Align to Right Bottom")
+ ),
+ name="Position",
+ description="Align position",
+ default='CENTER'
+ )
+ base = EnumProperty(
+ items=(
+ ('TEXTURE', "Texture", "Align based on Texture"),
+ ('UV', "UV", "Align to UV"),
+ ('UV_SEL', "UV (Selected)", "Align to Selected UV")
+ ),
+ name="Base",
+ description="Align base",
+ default='TEXTURE'
+ )
+
+ def execute(self, context):
+ area, _, space = common.get_space('IMAGE_EDITOR', 'WINDOW',
+ 'IMAGE_EDITOR')
+ bd_size = common.get_uvimg_editor_board_size(area)
+
+ if self.base == 'UV':
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ if not bm.loops.layers.uv:
+ return None
+ uv_layer = bm.loops.layers.uv.verify()
+
+ max_ = Vector((-10000000.0, -10000000.0))
+ min_ = Vector((10000000.0, 10000000.0))
+ for f in bm.faces:
+ if not f.select:
+ continue
+ for l in f.loops:
+ uv = l[uv_layer].uv
+ max_.x = max(max_.x, uv.x)
+ max_.y = max(max_.y, uv.y)
+ min_.x = min(min_.x, uv.x)
+ min_.y = min(min_.y, uv.y)
+ center = Vector(((max_.x + min_.x) / 2.0, (max_.y + min_.y) / 2.0))
+
+ elif self.base == 'UV_SEL':
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ if not bm.loops.layers.uv:
+ return None
+ uv_layer = bm.loops.layers.uv.verify()
+
+ max_ = Vector((-10000000.0, -10000000.0))
+ min_ = Vector((10000000.0, 10000000.0))
+ for f in bm.faces:
+ if not f.select:
+ continue
+ for l in f.loops:
+ if not l[uv_layer].select:
+ continue
+ uv = l[uv_layer].uv
+ max_.x = max(max_.x, uv.x)
+ max_.y = max(max_.y, uv.y)
+ min_.x = min(min_.x, uv.x)
+ min_.y = min(min_.y, uv.y)
+ center = Vector(((max_.x + min_.x) / 2.0, (max_.y + min_.y) / 2.0))
+
+ elif self.base == 'TEXTURE':
+ min_ = Vector((0.0, 0.0))
+ max_ = Vector((1.0, 1.0))
+ center = Vector((0.5, 0.5))
+ else:
+ self.report({'ERROR'}, "Unknown Operation")
+
+ if self.position == 'CENTER':
+ cx = center.x * bd_size[0]
+ cy = center.y * bd_size[1]
+ elif self.position == 'LEFT_TOP':
+ cx = min_.x * bd_size[0]
+ cy = max_.y * bd_size[1]
+ elif self.position == 'LEFT_MIDDLE':
+ cx = min_.x * bd_size[0]
+ cy = center.y * bd_size[1]
+ elif self.position == 'LEFT_BOTTOM':
+ cx = min_.x * bd_size[0]
+ cy = min_.y * bd_size[1]
+ elif self.position == 'MIDDLE_TOP':
+ cx = center.x * bd_size[0]
+ cy = max_.y * bd_size[1]
+ elif self.position == 'MIDDLE_BOTTOM':
+ cx = center.x * bd_size[0]
+ cy = min_.y * bd_size[1]
+ elif self.position == 'RIGHT_TOP':
+ cx = max_.x * bd_size[0]
+ cy = max_.y * bd_size[1]
+ elif self.position == 'RIGHT_MIDDLE':
+ cx = max_.x * bd_size[0]
+ cy = center.y * bd_size[1]
+ elif self.position == 'RIGHT_BOTTOM':
+ cx = max_.x * bd_size[0]
+ cy = min_.y * bd_size[1]
+ else:
+ self.report({'ERROR'}, "Unknown Operation")
+
+ space.cursor_location = Vector((cx, cy))
+
+ return {'FINISHED'}
diff --git a/uv_magic_uv/muv_cpuv_ops.py b/uv_magic_uv/op/copy_paste_uv.py
index 82f043c6..ee89b5e9 100644
--- a/uv_magic_uv/muv_cpuv_ops.py
+++ b/uv_magic_uv/op/copy_paste_uv.py
@@ -18,10 +18,13 @@
#
# ##### END GPL LICENSE BLOCK #####
-__author__ = "Nutti <nutti.metro@gmail.com>, Jace Priester"
+__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
+
+import math
+from math import atan2, sin, cos
import bpy
import bmesh
@@ -31,16 +34,9 @@ from bpy.props import (
IntProperty,
EnumProperty,
)
-from . import muv_common
-
+from mathutils import Vector
-def memorize_view_3d_mode(fn):
- def __memorize_view_3d_mode(self, context):
- mode_orig = bpy.context.object.mode
- result = fn(self, context)
- bpy.ops.object.mode_set(mode=mode_orig)
- return result
- return __memorize_view_3d_mode
+from .. import common
class MUV_CPUVCopyUV(bpy.types.Operator):
@@ -64,7 +60,7 @@ class MUV_CPUVCopyUV(bpy.types.Operator):
{'INFO'}, "Copy UV coordinate (UV map:%s)" % (self.uv_map))
obj = context.active_object
bm = bmesh.from_edit_mesh(obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
+ if common.check_version(2, 73, 0) >= 0:
bm.faces.ensure_lookup_table()
# get UV layer
@@ -174,7 +170,7 @@ class MUV_CPUVPasteUV(bpy.types.Operator):
{'INFO'}, "Paste UV coordinate (UV map:%s)" % (self.uv_map))
obj = context.active_object
bm = bmesh.from_edit_mesh(obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
+ if common.check_version(2, 73, 0) >= 0:
bm.faces.ensure_lookup_table()
# get UV layer
@@ -273,46 +269,158 @@ class MUV_CPUVPasteUVMenu(bpy.types.Menu):
bl_description = "Paste UV coordinate"
def draw(self, context):
+ sc = context.scene
layout = self.layout
# create sub menu
obj = context.active_object
bm = bmesh.from_edit_mesh(obj.data)
uv_maps = bm.loops.layers.uv.keys()
- layout.operator(
- MUV_CPUVPasteUV.bl_idname,
- text="[Default]", icon="IMAGE_COL").uv_map = ""
+ ops = layout.operator(MUV_CPUVPasteUV.bl_idname, text="[Default]")
+ ops.uv_map = ""
+ ops.copy_seams = sc.muv_cpuv_copy_seams
+ ops.strategy = sc.muv_cpuv_strategy
for m in uv_maps:
- layout.operator(
- MUV_CPUVPasteUV.bl_idname,
- text=m, icon="IMAGE_COL").uv_map = m
+ ops = layout.operator(MUV_CPUVPasteUV.bl_idname, text=m)
+ ops.uv_map = m
+ ops.copy_seams = sc.muv_cpuv_copy_seams
+ ops.strategy = sc.muv_cpuv_strategy
-class MUV_CPUVObjCopyUV(bpy.types.Operator):
+class MUV_CPUVIECopyUV(bpy.types.Operator):
"""
- Operation class: Copy UV coordinate per object
+ Operation class: Copy UV coordinate on UV/Image Editor
"""
- bl_idname = "object.muv_cpuv_obj_copy_uv"
+ bl_idname = "uv.muv_cpuv_ie_copy_uv"
bl_label = "Copy UV"
- bl_description = "Copy UV coordinate"
+ bl_description = "Copy UV coordinate (only selected in UV/Image Editor)"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ return context.mode == 'EDIT_MESH'
+
+ def execute(self, context):
+ props = context.scene.muv_props.cpuv
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ uv_layer = bm.loops.layers.uv.verify()
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
+
+ for face in bm.faces:
+ if not face.select:
+ continue
+ skip = False
+ for l in face.loops:
+ if not l[uv_layer].select:
+ skip = True
+ break
+ if skip:
+ continue
+ props.src_uvs.append([l[uv_layer].uv.copy() for l in face.loops])
+
+ return {'FINISHED'}
+
+
+class MUV_CPUVIEPasteUV(bpy.types.Operator):
+ """
+ Operation class: Paste UV coordinate on UV/Image Editor
+ """
+
+ bl_idname = "uv.muv_cpuv_ie_paste_uv"
+ bl_label = "Paste UV"
+ bl_description = "Paste UV coordinate (only selected in UV/Image Editor)"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ return context.mode == 'EDIT_MESH'
+
+ def execute(self, context):
+ props = context.scene.muv_props.cpuv
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ uv_layer = bm.loops.layers.uv.verify()
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
+
+ dest_uvs = []
+ dest_face_indices = []
+ for face in bm.faces:
+ if not face.select:
+ continue
+ skip = False
+ for l in face.loops:
+ if not l[uv_layer].select:
+ skip = True
+ break
+ if skip:
+ continue
+ dest_face_indices.append(face.index)
+ uvs = [l[uv_layer].uv.copy() for l in face.loops]
+ dest_uvs.append(uvs)
+
+ for suvs, duvs in zip(props.src_uvs, dest_uvs):
+ src_diff = suvs[1] - suvs[0]
+ dest_diff = duvs[1] - duvs[0]
+
+ src_base = suvs[0]
+ dest_base = duvs[0]
+
+ src_rad = atan2(src_diff.y, src_diff.x)
+ dest_rad = atan2(dest_diff.y, dest_diff.x)
+ if src_rad < dest_rad:
+ radian = dest_rad - src_rad
+ elif src_rad > dest_rad:
+ radian = math.pi * 2 - (src_rad - dest_rad)
+ else: # src_rad == dest_rad
+ radian = 0.0
+
+ ratio = dest_diff.length / src_diff.length
+ break
+
+ for suvs, fidx in zip(props.src_uvs, dest_face_indices):
+ for l, suv in zip(bm.faces[fidx].loops, suvs):
+ base = suv - src_base
+ radian_ref = atan2(base.y, base.x)
+ radian_fin = (radian + radian_ref)
+ length = base.length
+ turn = Vector((length * cos(radian_fin),
+ length * sin(radian_fin)))
+ target_uv = Vector((turn.x * ratio, turn.y * ratio)) + \
+ dest_base
+ l[uv_layer].uv = target_uv
+
+ bmesh.update_edit_mesh(obj.data)
+
+ return {'FINISHED'}
+
+
+class MUV_CPUVSelSeqCopyUV(bpy.types.Operator):
+ """
+ Operation class: Copy UV coordinate by selection sequence
+ """
+
+ bl_idname = "uv.muv_cpuv_selseq_copy_uv"
+ bl_label = "Copy UV (Selection Sequence) (Operation)"
+ bl_description = "Copy UV data by selection sequence (Operation)"
bl_options = {'REGISTER', 'UNDO'}
uv_map = StringProperty(options={'HIDDEN'})
- @memorize_view_3d_mode
def execute(self, context):
- props = context.scene.muv_props.cpuv_obj
+ props = context.scene.muv_props.cpuv_selseq
if self.uv_map == "":
- self.report({'INFO'}, "Copy UV coordinate per object")
+ self.report({'INFO'}, "Copy UV coordinate (selection sequence)")
else:
self.report(
{'INFO'},
- "Copy UV coordinate per object (UV map:%s)" % (self.uv_map))
- bpy.ops.object.mode_set(mode='EDIT')
-
+ "Copy UV coordinate (selection sequence) (UV map:%s)"
+ % (self.uv_map))
obj = context.active_object
bm = bmesh.from_edit_mesh(obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
+ if common.check_version(2, 73, 0) >= 0:
bm.faces.ensure_lookup_table()
# get UV layer
@@ -329,171 +437,210 @@ class MUV_CPUVObjCopyUV(bpy.types.Operator):
props.src_uvs = []
props.src_pin_uvs = []
props.src_seams = []
- for face in bm.faces:
- uvs = [l[uv_layer].uv.copy() for l in face.loops]
- pin_uvs = [l[uv_layer].pin_uv for l in face.loops]
- seams = [l.edge.seam for l in face.loops]
- props.src_uvs.append(uvs)
- props.src_pin_uvs.append(pin_uvs)
- props.src_seams.append(seams)
-
- self.report({'INFO'}, "%s's UV coordinates are copied" % (obj.name))
+ for hist in bm.select_history:
+ if isinstance(hist, bmesh.types.BMFace) and hist.select:
+ uvs = [l[uv_layer].uv.copy() for l in hist.loops]
+ pin_uvs = [l[uv_layer].pin_uv for l in hist.loops]
+ seams = [l.edge.seam for l in hist.loops]
+ props.src_uvs.append(uvs)
+ props.src_pin_uvs.append(pin_uvs)
+ props.src_seams.append(seams)
+ if not props.src_uvs or not props.src_pin_uvs:
+ self.report({'WARNING'}, "No faces are selected")
+ return {'CANCELLED'}
+ self.report({'INFO'}, "%d face(s) are selected" % len(props.src_uvs))
return {'FINISHED'}
-class MUV_CPUVObjCopyUVMenu(bpy.types.Menu):
+class MUV_CPUVSelSeqCopyUVMenu(bpy.types.Menu):
"""
- Menu class: Copy UV coordinate per object
+ Menu class: Copy UV coordinate by selection sequence
"""
- bl_idname = "object.muv_cpuv_obj_copy_uv_menu"
- bl_label = "Copy UV"
- bl_description = "Copy UV coordinate per object"
+ bl_idname = "uv.muv_cpuv_selseq_copy_uv_menu"
+ bl_label = "Copy UV (Selection Sequence)"
+ bl_description = "Copy UV coordinate by selection sequence"
- def draw(self, _):
+ def draw(self, context):
layout = self.layout
- # create sub menu
- uv_maps = bpy.context.active_object.data.uv_textures.keys()
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ uv_maps = bm.loops.layers.uv.keys()
layout.operator(
- MUV_CPUVObjCopyUV.bl_idname,
+ MUV_CPUVSelSeqCopyUV.bl_idname,
text="[Default]", icon="IMAGE_COL").uv_map = ""
for m in uv_maps:
layout.operator(
- MUV_CPUVObjCopyUV.bl_idname,
+ MUV_CPUVSelSeqCopyUV.bl_idname,
text=m, icon="IMAGE_COL").uv_map = m
-class MUV_CPUVObjPasteUV(bpy.types.Operator):
+class MUV_CPUVSelSeqPasteUV(bpy.types.Operator):
"""
- Operation class: Paste UV coordinate per object
+ Operation class: Paste UV coordinate by selection sequence
"""
- bl_idname = "object.muv_cpuv_obj_paste_uv"
- bl_label = "Paste UV"
- bl_description = "Paste UV coordinate"
+ bl_idname = "uv.muv_cpuv_selseq_paste_uv"
+ bl_label = "Paste UV (Selection Sequence) (Operation)"
+ bl_description = "Paste UV coordinate by selection sequence (Operation)"
bl_options = {'REGISTER', 'UNDO'}
uv_map = StringProperty(options={'HIDDEN'})
+ strategy = EnumProperty(
+ name="Strategy",
+ description="Paste Strategy",
+ items=[
+ ('N_N', 'N:N', 'Number of faces must be equal to source'),
+ ('N_M', 'N:M', 'Number of faces must not be equal to source')
+ ],
+ default="N_M"
+ )
+ flip_copied_uv = BoolProperty(
+ name="Flip Copied UV",
+ description="Flip Copied UV...",
+ default=False
+ )
+ rotate_copied_uv = IntProperty(
+ default=0,
+ name="Rotate Copied UV",
+ min=0,
+ max=30
+ )
copy_seams = BoolProperty(
name="Copy Seams",
description="Copy Seams",
default=True
)
- @memorize_view_3d_mode
def execute(self, context):
- props = context.scene.muv_props.cpuv_obj
+ props = context.scene.muv_props.cpuv_selseq
if not props.src_uvs or not props.src_pin_uvs:
self.report({'WARNING'}, "Need copy UV at first")
return {'CANCELLED'}
+ if self.uv_map == "":
+ self.report({'INFO'}, "Paste UV coordinate (selection sequence)")
+ else:
+ self.report(
+ {'INFO'},
+ "Paste UV coordinate (selection sequence) (UV map:%s)"
+ % (self.uv_map))
- for o in bpy.data.objects:
- if not hasattr(o.data, "uv_textures") or not o.select:
- continue
-
- bpy.ops.object.mode_set(mode='OBJECT')
- bpy.context.scene.objects.active = o
- bpy.ops.object.mode_set(mode='EDIT')
-
- obj = context.active_object
- bm = bmesh.from_edit_mesh(obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
- bm.faces.ensure_lookup_table()
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
- if (self.uv_map == "" or
- self.uv_map not in bm.loops.layers.uv.keys()):
- self.report({'INFO'}, "Paste UV coordinate per object")
- else:
+ # get UV layer
+ if self.uv_map == "":
+ if not bm.loops.layers.uv:
self.report(
- {'INFO'},
- "Paste UV coordinate per object (UV map: %s)"
- % (self.uv_map))
-
- # get UV layer
- if (self.uv_map == "" or
- self.uv_map not in bm.loops.layers.uv.keys()):
- if not bm.loops.layers.uv:
- self.report(
- {'WARNING'}, "Object must have more than one UV map")
- return {'CANCELLED'}
- uv_layer = bm.loops.layers.uv.verify()
- else:
- uv_layer = bm.loops.layers.uv[self.uv_map]
-
- # get selected face
- dest_uvs = []
- dest_pin_uvs = []
- dest_seams = []
- dest_face_indices = []
- for face in bm.faces:
- dest_face_indices.append(face.index)
- uvs = [l[uv_layer].uv.copy() for l in face.loops]
- pin_uvs = [l[uv_layer].pin_uv for l in face.loops]
- seams = [l.edge.seam for l in face.loops]
+ {'WARNING'}, "Object must have more than one UV map")
+ return {'CANCELLED'}
+ uv_layer = bm.loops.layers.uv.verify()
+ else:
+ uv_layer = bm.loops.layers.uv[self.uv_map]
+
+ # get selected face
+ dest_uvs = []
+ dest_pin_uvs = []
+ dest_seams = []
+ dest_face_indices = []
+ for hist in bm.select_history:
+ if isinstance(hist, bmesh.types.BMFace) and hist.select:
+ dest_face_indices.append(hist.index)
+ uvs = [l[uv_layer].uv.copy() for l in hist.loops]
+ pin_uvs = [l[uv_layer].pin_uv for l in hist.loops]
+ seams = [l.edge.seam for l in hist.loops]
dest_uvs.append(uvs)
dest_pin_uvs.append(pin_uvs)
dest_seams.append(seams)
- if len(props.src_uvs) != len(dest_uvs):
- self.report(
- {'WARNING'},
- "Number of faces is different from copied " +
- "(src:%d, dest:%d)"
- % (len(props.src_uvs), len(dest_uvs))
- )
- return {'CANCELLED'}
+ if not dest_uvs or not dest_pin_uvs:
+ self.report({'WARNING'}, "No faces are selected")
+ return {'CANCELLED'}
+ if self.strategy == 'N_N' and len(props.src_uvs) != len(dest_uvs):
+ self.report(
+ {'WARNING'},
+ "Number of selected faces is different from copied faces " +
+ "(src:%d, dest:%d)"
+ % (len(props.src_uvs), len(dest_uvs)))
+ return {'CANCELLED'}
- # paste
- for i, idx in enumerate(dest_face_indices):
+ # paste
+ for i, idx in enumerate(dest_face_indices):
+ suv = None
+ spuv = None
+ ss = None
+ duv = None
+ if self.strategy == 'N_N':
suv = props.src_uvs[i]
spuv = props.src_pin_uvs[i]
ss = props.src_seams[i]
duv = dest_uvs[i]
- if len(suv) != len(duv):
- self.report({'WARNING'}, "Some faces are different size")
- return {'CANCELLED'}
- suvs_fr = [uv for uv in suv]
- spuvs_fr = [pin_uv for pin_uv in spuv]
- ss_fr = [s for s in ss]
- # paste UVs
- for l, suv, spuv, ss in zip(
- bm.faces[idx].loops, suvs_fr, spuvs_fr, ss_fr):
- l[uv_layer].uv = suv
- l[uv_layer].pin_uv = spuv
- if self.copy_seams is True:
- l.edge.seam = ss
-
- bmesh.update_edit_mesh(obj.data)
- if self.copy_seams is True:
- obj.data.show_edge_seams = True
+ elif self.strategy == 'N_M':
+ suv = props.src_uvs[i % len(props.src_uvs)]
+ spuv = props.src_pin_uvs[i % len(props.src_pin_uvs)]
+ ss = props.src_seams[i % len(props.src_seams)]
+ duv = dest_uvs[i]
+ if len(suv) != len(duv):
+ self.report({'WARNING'}, "Some faces are different size")
+ return {'CANCELLED'}
+ suvs_fr = [uv for uv in suv]
+ spuvs_fr = [pin_uv for pin_uv in spuv]
+ ss_fr = [s for s in ss]
+ # flip UVs
+ if self.flip_copied_uv is True:
+ suvs_fr.reverse()
+ spuvs_fr.reverse()
+ ss_fr.reverse()
+ # rotate UVs
+ for _ in range(self.rotate_copied_uv):
+ uv = suvs_fr.pop()
+ pin_uv = spuvs_fr.pop()
+ s = ss_fr.pop()
+ suvs_fr.insert(0, uv)
+ spuvs_fr.insert(0, pin_uv)
+ ss_fr.insert(0, s)
+ # paste UVs
+ for l, suv, spuv, ss in zip(bm.faces[idx].loops, suvs_fr,
+ spuvs_fr, ss_fr):
+ l[uv_layer].uv = suv
+ l[uv_layer].pin_uv = spuv
+ if self.copy_seams is True:
+ l.edge.seam = ss
- self.report(
- {'INFO'}, "%s's UV coordinates are pasted" % (obj.name))
+ self.report({'INFO'}, "%d face(s) are copied" % len(dest_uvs))
+
+ bmesh.update_edit_mesh(obj.data)
+ if self.copy_seams is True:
+ obj.data.show_edge_seams = True
return {'FINISHED'}
-class MUV_CPUVObjPasteUVMenu(bpy.types.Menu):
+class MUV_CPUVSelSeqPasteUVMenu(bpy.types.Menu):
"""
- Menu class: Paste UV coordinate per object
+ Menu class: Paste UV coordinate by selection sequence
"""
- bl_idname = "object.muv_cpuv_obj_paste_uv_menu"
- bl_label = "Paste UV"
- bl_description = "Paste UV coordinate per object"
+ bl_idname = "uv.muv_cpuv_selseq_paste_uv_menu"
+ bl_label = "Paste UV (Selection Sequence)"
+ bl_description = "Paste UV coordinate by selection sequence"
- def draw(self, _):
+ def draw(self, context):
+ sc = context.scene
layout = self.layout
# create sub menu
- uv_maps = []
- for obj in bpy.data.objects:
- if hasattr(obj.data, "uv_textures") and obj.select:
- uv_maps.extend(obj.data.uv_textures.keys())
- uv_maps = list(set(uv_maps))
- layout.operator(
- MUV_CPUVObjPasteUV.bl_idname,
- text="[Default]", icon="IMAGE_COL").uv_map = ""
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ uv_maps = bm.loops.layers.uv.keys()
+ ops = layout.operator(MUV_CPUVSelSeqPasteUV.bl_idname,
+ text="[Default]")
+ ops.uv_map = ""
+ ops.copy_seams = sc.muv_cpuv_copy_seams
+ ops.strategy = sc.muv_cpuv_strategy
for m in uv_maps:
- layout.operator(
- MUV_CPUVObjPasteUV.bl_idname,
- text=m, icon="IMAGE_COL").uv_map = m
+ ops = layout.operator(MUV_CPUVSelSeqPasteUV.bl_idname, text=m)
+ ops.uv_map = m
+ ops.copy_seams = sc.muv_cpuv_copy_seams
+ ops.strategy = sc.muv_cpuv_strategy
diff --git a/uv_magic_uv/op/copy_paste_uv_object.py b/uv_magic_uv/op/copy_paste_uv_object.py
new file mode 100644
index 00000000..d80ee415
--- /dev/null
+++ b/uv_magic_uv/op/copy_paste_uv_object.py
@@ -0,0 +1,252 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
+
+import bpy
+import bmesh
+from bpy.props import (
+ StringProperty,
+ BoolProperty,
+)
+
+from .. import common
+
+
+def memorize_view_3d_mode(fn):
+ def __memorize_view_3d_mode(self, context):
+ mode_orig = bpy.context.object.mode
+ result = fn(self, context)
+ bpy.ops.object.mode_set(mode=mode_orig)
+ return result
+ return __memorize_view_3d_mode
+
+
+class MUV_CPUVObjCopyUV(bpy.types.Operator):
+ """
+ Operation class: Copy UV coordinate per object
+ """
+
+ bl_idname = "object.muv_cpuv_obj_copy_uv"
+ bl_label = "Copy UV"
+ bl_description = "Copy UV coordinate"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ uv_map = StringProperty(options={'HIDDEN'})
+
+ @memorize_view_3d_mode
+ def execute(self, context):
+ props = context.scene.muv_props.cpuv_obj
+ if self.uv_map == "":
+ self.report({'INFO'}, "Copy UV coordinate per object")
+ else:
+ self.report(
+ {'INFO'},
+ "Copy UV coordinate per object (UV map:%s)" % (self.uv_map))
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
+
+ # get UV layer
+ if self.uv_map == "":
+ if not bm.loops.layers.uv:
+ self.report(
+ {'WARNING'}, "Object must have more than one UV map")
+ return {'CANCELLED'}
+ uv_layer = bm.loops.layers.uv.verify()
+ else:
+ uv_layer = bm.loops.layers.uv[self.uv_map]
+
+ # get selected face
+ props.src_uvs = []
+ props.src_pin_uvs = []
+ props.src_seams = []
+ for face in bm.faces:
+ uvs = [l[uv_layer].uv.copy() for l in face.loops]
+ pin_uvs = [l[uv_layer].pin_uv for l in face.loops]
+ seams = [l.edge.seam for l in face.loops]
+ props.src_uvs.append(uvs)
+ props.src_pin_uvs.append(pin_uvs)
+ props.src_seams.append(seams)
+
+ self.report({'INFO'}, "%s's UV coordinates are copied" % (obj.name))
+
+ return {'FINISHED'}
+
+
+class MUV_CPUVObjCopyUVMenu(bpy.types.Menu):
+ """
+ Menu class: Copy UV coordinate per object
+ """
+
+ bl_idname = "object.muv_cpuv_obj_copy_uv_menu"
+ bl_label = "Copy UV"
+ bl_description = "Copy UV coordinate per object"
+
+ def draw(self, _):
+ layout = self.layout
+ # create sub menu
+ uv_maps = bpy.context.active_object.data.uv_textures.keys()
+ layout.operator(MUV_CPUVObjCopyUV.bl_idname, text="[Default]")\
+ .uv_map = ""
+ for m in uv_maps:
+ layout.operator(MUV_CPUVObjCopyUV.bl_idname, text=m).uv_map = m
+
+
+class MUV_CPUVObjPasteUV(bpy.types.Operator):
+ """
+ Operation class: Paste UV coordinate per object
+ """
+
+ bl_idname = "object.muv_cpuv_obj_paste_uv"
+ bl_label = "Paste UV"
+ bl_description = "Paste UV coordinate"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ uv_map = StringProperty(options={'HIDDEN'})
+ copy_seams = BoolProperty(
+ name="Copy Seams",
+ description="Copy Seams",
+ default=True
+ )
+
+ @memorize_view_3d_mode
+ def execute(self, context):
+ props = context.scene.muv_props.cpuv_obj
+ if not props.src_uvs or not props.src_pin_uvs:
+ self.report({'WARNING'}, "Need copy UV at first")
+ return {'CANCELLED'}
+
+ for o in bpy.data.objects:
+ if not hasattr(o.data, "uv_textures") or not o.select:
+ continue
+
+ bpy.ops.object.mode_set(mode='OBJECT')
+ bpy.context.scene.objects.active = o
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
+
+ if (self.uv_map == "" or
+ self.uv_map not in bm.loops.layers.uv.keys()):
+ self.report({'INFO'}, "Paste UV coordinate per object")
+ else:
+ self.report(
+ {'INFO'},
+ "Paste UV coordinate per object (UV map: %s)"
+ % (self.uv_map))
+
+ # get UV layer
+ if (self.uv_map == "" or
+ self.uv_map not in bm.loops.layers.uv.keys()):
+ if not bm.loops.layers.uv:
+ self.report(
+ {'WARNING'}, "Object must have more than one UV map")
+ return {'CANCELLED'}
+ uv_layer = bm.loops.layers.uv.verify()
+ else:
+ uv_layer = bm.loops.layers.uv[self.uv_map]
+
+ # get selected face
+ dest_uvs = []
+ dest_pin_uvs = []
+ dest_seams = []
+ dest_face_indices = []
+ for face in bm.faces:
+ dest_face_indices.append(face.index)
+ uvs = [l[uv_layer].uv.copy() for l in face.loops]
+ pin_uvs = [l[uv_layer].pin_uv for l in face.loops]
+ seams = [l.edge.seam for l in face.loops]
+ dest_uvs.append(uvs)
+ dest_pin_uvs.append(pin_uvs)
+ dest_seams.append(seams)
+ if len(props.src_uvs) != len(dest_uvs):
+ self.report(
+ {'WARNING'},
+ "Number of faces is different from copied " +
+ "(src:%d, dest:%d)"
+ % (len(props.src_uvs), len(dest_uvs))
+ )
+ return {'CANCELLED'}
+
+ # paste
+ for i, idx in enumerate(dest_face_indices):
+ suv = props.src_uvs[i]
+ spuv = props.src_pin_uvs[i]
+ ss = props.src_seams[i]
+ duv = dest_uvs[i]
+ if len(suv) != len(duv):
+ self.report({'WARNING'}, "Some faces are different size")
+ return {'CANCELLED'}
+ suvs_fr = [uv for uv in suv]
+ spuvs_fr = [pin_uv for pin_uv in spuv]
+ ss_fr = [s for s in ss]
+ # paste UVs
+ for l, suv, spuv, ss in zip(
+ bm.faces[idx].loops, suvs_fr, spuvs_fr, ss_fr):
+ l[uv_layer].uv = suv
+ l[uv_layer].pin_uv = spuv
+ if self.copy_seams is True:
+ l.edge.seam = ss
+
+ bmesh.update_edit_mesh(obj.data)
+ if self.copy_seams is True:
+ obj.data.show_edge_seams = True
+
+ self.report(
+ {'INFO'}, "%s's UV coordinates are pasted" % (obj.name))
+
+ return {'FINISHED'}
+
+
+class MUV_CPUVObjPasteUVMenu(bpy.types.Menu):
+ """
+ Menu class: Paste UV coordinate per object
+ """
+
+ bl_idname = "object.muv_cpuv_obj_paste_uv_menu"
+ bl_label = "Paste UV"
+ bl_description = "Paste UV coordinate per object"
+
+ def draw(self, context):
+ sc = context.scene
+ layout = self.layout
+ # create sub menu
+ uv_maps = []
+ for obj in bpy.data.objects:
+ if hasattr(obj.data, "uv_textures") and obj.select:
+ uv_maps.extend(obj.data.uv_textures.keys())
+ uv_maps = list(set(uv_maps))
+ ops = layout.operator(MUV_CPUVObjPasteUV.bl_idname, text="[Default]")
+ ops.uv_map = ""
+ ops.copy_seams = sc.muv_cpuv_copy_seams
+ for m in uv_maps:
+ ops = layout.operator(MUV_CPUVObjPasteUV.bl_idname, text=m)
+ ops.uv_map = m
+ ops.copy_seams = sc.muv_cpuv_copy_seams
diff --git a/uv_magic_uv/op/copy_paste_uv_uvedit.py b/uv_magic_uv/op/copy_paste_uv_uvedit.py
new file mode 100644
index 00000000..96908020
--- /dev/null
+++ b/uv_magic_uv/op/copy_paste_uv_uvedit.py
@@ -0,0 +1,144 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>, Jace Priester"
+__status__ = "production"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
+
+import math
+from math import atan2, sin, cos
+
+import bpy
+import bmesh
+from mathutils import Vector
+
+from .. import common
+
+
+class MUV_CPUVIECopyUV(bpy.types.Operator):
+ """
+ Operation class: Copy UV coordinate on UV/Image Editor
+ """
+
+ bl_idname = "uv.muv_cpuv_ie_copy_uv"
+ bl_label = "Copy UV"
+ bl_description = "Copy UV coordinate (only selected in UV/Image Editor)"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ return context.mode == 'EDIT_MESH'
+
+ def execute(self, context):
+ props = context.scene.muv_props.cpuv
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ uv_layer = bm.loops.layers.uv.verify()
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
+
+ for face in bm.faces:
+ if not face.select:
+ continue
+ skip = False
+ for l in face.loops:
+ if not l[uv_layer].select:
+ skip = True
+ break
+ if skip:
+ continue
+ props.src_uvs.append([l[uv_layer].uv.copy() for l in face.loops])
+
+ return {'FINISHED'}
+
+
+class MUV_CPUVIEPasteUV(bpy.types.Operator):
+ """
+ Operation class: Paste UV coordinate on UV/Image Editor
+ """
+
+ bl_idname = "uv.muv_cpuv_ie_paste_uv"
+ bl_label = "Paste UV"
+ bl_description = "Paste UV coordinate (only selected in UV/Image Editor)"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ return context.mode == 'EDIT_MESH'
+
+ def execute(self, context):
+ props = context.scene.muv_props.cpuv
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ uv_layer = bm.loops.layers.uv.verify()
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
+
+ dest_uvs = []
+ dest_face_indices = []
+ for face in bm.faces:
+ if not face.select:
+ continue
+ skip = False
+ for l in face.loops:
+ if not l[uv_layer].select:
+ skip = True
+ break
+ if skip:
+ continue
+ dest_face_indices.append(face.index)
+ uvs = [l[uv_layer].uv.copy() for l in face.loops]
+ dest_uvs.append(uvs)
+
+ for suvs, duvs in zip(props.src_uvs, dest_uvs):
+ src_diff = suvs[1] - suvs[0]
+ dest_diff = duvs[1] - duvs[0]
+
+ src_base = suvs[0]
+ dest_base = duvs[0]
+
+ src_rad = atan2(src_diff.y, src_diff.x)
+ dest_rad = atan2(dest_diff.y, dest_diff.x)
+ if src_rad < dest_rad:
+ radian = dest_rad - src_rad
+ elif src_rad > dest_rad:
+ radian = math.pi * 2 - (src_rad - dest_rad)
+ else: # src_rad == dest_rad
+ radian = 0.0
+
+ ratio = dest_diff.length / src_diff.length
+ break
+
+ for suvs, fidx in zip(props.src_uvs, dest_face_indices):
+ for l, suv in zip(bm.faces[fidx].loops, suvs):
+ base = suv - src_base
+ radian_ref = atan2(base.y, base.x)
+ radian_fin = (radian + radian_ref)
+ length = base.length
+ turn = Vector((length * cos(radian_fin),
+ length * sin(radian_fin)))
+ target_uv = Vector((turn.x * ratio, turn.y * ratio)) + \
+ dest_base
+ l[uv_layer].uv = target_uv
+
+ bmesh.update_edit_mesh(obj.data)
+
+ return {'FINISHED'}
diff --git a/uv_magic_uv/muv_fliprot_ops.py b/uv_magic_uv/op/flip_rotate_uv.py
index 334eb14c..30f6b0f7 100644
--- a/uv_magic_uv/muv_fliprot_ops.py
+++ b/uv_magic_uv/op/flip_rotate_uv.py
@@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
import bpy
import bmesh
@@ -29,7 +29,8 @@ from bpy.props import (
BoolProperty,
IntProperty,
)
-from . import muv_common
+
+from .. import common
class MUV_FlipRot(bpy.types.Operator):
@@ -63,7 +64,7 @@ class MUV_FlipRot(bpy.types.Operator):
self.report({'INFO'}, "Flip/Rotate UV")
obj = context.active_object
bm = bmesh.from_edit_mesh(obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
+ if common.check_version(2, 73, 0) >= 0:
bm.faces.ensure_lookup_table()
# get UV layer
diff --git a/uv_magic_uv/muv_mirroruv_ops.py b/uv_magic_uv/op/mirror_uv.py
index 63eb9bd5..f4849d18 100644
--- a/uv_magic_uv/muv_mirroruv_ops.py
+++ b/uv_magic_uv/op/mirror_uv.py
@@ -20,8 +20,8 @@
__author__ = "Keith (Wahooney) Boshoff, Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
import bpy
from bpy.props import (
@@ -30,7 +30,8 @@ from bpy.props import (
)
import bmesh
from mathutils import Vector
-from . import muv_common
+
+from .. import common
class MUV_MirrorUV(bpy.types.Operator):
@@ -113,7 +114,7 @@ class MUV_MirrorUV(bpy.types.Operator):
error = self.error
axis = self.axis
- if muv_common.check_version(2, 73, 0) >= 0:
+ if common.check_version(2, 73, 0) >= 0:
bm.faces.ensure_lookup_table()
if not bm.loops.layers.uv:
self.report({'WARNING'}, "Object must have more than one UV map")
diff --git a/uv_magic_uv/muv_mvuv_ops.py b/uv_magic_uv/op/move_uv.py
index 28346270..6382376c 100644
--- a/uv_magic_uv/muv_mvuv_ops.py
+++ b/uv_magic_uv/op/move_uv.py
@@ -20,8 +20,8 @@
__author__ = "kgeogeo, mem, Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
import bpy
import bmesh
@@ -64,6 +64,7 @@ class MUV_MVUV(bpy.types.Operator):
return context.edit_object
def modal(self, context, event):
+ props = context.scene.muv_props.mvuv
if self.__first_time is True:
self.__prev_mouse = Vector((
event.mouse_region_x, event.mouse_region_y))
@@ -84,7 +85,7 @@ class MUV_MVUV(bpy.types.Operator):
event.mouse_region_x, event.mouse_region_y))
# check if operation is started
- if self.__running is True:
+ if self.__running:
if event.type == 'LEFTMOUSE' and event.value == 'RELEASE':
self.__running = False
return {'RUNNING_MODAL'}
@@ -110,16 +111,20 @@ class MUV_MVUV(bpy.types.Operator):
if event.type == cancel_btn and event.value == 'PRESS':
for (fidx, vidx), uv in zip(self.__topology_dict, self.__ini_uvs):
bm.faces[fidx].loops[vidx][active_uv].uv = uv
+ props.running = False
return {'FINISHED'}
# confirmed
if event.type == confirm_btn and event.value == 'PRESS':
+ props.running = False
return {'FINISHED'}
return {'RUNNING_MODAL'}
def execute(self, context):
- self.__first_time = True
+ props = context.scene.muv_props.mvuv
+ props.running = True
self.__running = True
+ self.__first_time = True
context.window_manager.modal_handler_add(self)
self.__topology_dict, self.__ini_uvs = self.__find_uv(context)
return {'RUNNING_MODAL'}
diff --git a/uv_magic_uv/muv_packuv_ops.py b/uv_magic_uv/op/pack_uv.py
index f663e662..a780af3e 100644
--- a/uv_magic_uv/muv_packuv_ops.py
+++ b/uv_magic_uv/op/pack_uv.py
@@ -20,11 +20,10 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
from math import fabs
-from collections import defaultdict
import bpy
import bmesh
@@ -36,7 +35,7 @@ from bpy.props import (
)
from mathutils import Vector
-from . import muv_common
+from .. import common
class MUV_PackUV(bpy.types.Operator):
@@ -69,23 +68,21 @@ class MUV_PackUV(bpy.types.Operator):
min=0.000001,
max=0.1,
default=(0.001, 0.001),
- size=2)
+ size=2
+ )
allowable_size_deviation = FloatVectorProperty(
name="Allowable Size Deviation",
description="Allowable sizse deviation to judge same UV island",
min=0.000001,
max=0.1,
default=(0.001, 0.001),
- size=2)
+ size=2
+ )
- def __init__(self):
- self.__face_to_verts = defaultdict(set)
- self.__vert_to_faces = defaultdict(set)
-
- def execute(self, _):
- obj = bpy.context.active_object
+ def execute(self, context):
+ obj = context.active_object
bm = bmesh.from_edit_mesh(obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
+ if common.check_version(2, 73, 0) >= 0:
bm.faces.ensure_lookup_table()
if not bm.loops.layers.uv:
self.report({'WARNING'}, "Object must have more than one UV map")
@@ -93,17 +90,7 @@ class MUV_PackUV(bpy.types.Operator):
uv_layer = bm.loops.layers.uv.verify()
selected_faces = [f for f in bm.faces if f.select]
-
- # create mesh database
- for f in selected_faces:
- for l in f.loops:
- id_ = l[uv_layer].uv.to_tuple(5), l.vert.index
- self.__face_to_verts[f.index].add(id_)
- self.__vert_to_faces[id_].add(f.index)
-
- # Group island
- uv_island_lists = self.__get_island(bm)
- island_info = self.__get_island_info(uv_layer, uv_island_lists)
+ island_info = common.get_island_info(obj)
num_group = self.__group_island(island_info)
loop_lists = [l for f in bm.faces for l in f.loops]
@@ -183,13 +170,17 @@ class MUV_PackUV(bpy.types.Operator):
dsx = isl_2['size'].x - isl_1['size'].x
dsy = isl_2['size'].y - isl_1['size'].y
center_x_matched = (
- fabs(dcx) < self.allowable_center_deviation[0])
+ fabs(dcx) < self.allowable_center_deviation[0]
+ )
center_y_matched = (
- fabs(dcy) < self.allowable_center_deviation[1])
+ fabs(dcy) < self.allowable_center_deviation[1]
+ )
size_x_matched = (
- fabs(dsx) < self.allowable_size_deviation[0])
+ fabs(dsx) < self.allowable_size_deviation[0]
+ )
size_y_matched = (
- fabs(dsy) < self.allowable_size_deviation[1])
+ fabs(dsy) < self.allowable_size_deviation[1]
+ )
center_matched = center_x_matched and center_y_matched
size_matched = size_x_matched and size_y_matched
num_uv_matched = (isl_2['num_uv'] == isl_1['num_uv'])
@@ -214,75 +205,3 @@ class MUV_PackUV(bpy.types.Operator):
num_group = num_group + 1
return num_group
-
- def __get_island_info(self, uv_layer, islands):
- """
- get information about each island
- """
-
- island_info = []
- for isl in islands:
- info = {}
- max_uv = Vector((-10000000.0, -10000000.0))
- min_uv = Vector((10000000.0, 10000000.0))
- ave_uv = Vector((0.0, 0.0))
- num_uv = 0
- for face in isl:
- n = 0
- a = Vector((0.0, 0.0))
- for l in face['face'].loops:
- uv = l[uv_layer].uv
- if uv.x > max_uv.x:
- max_uv.x = uv.x
- if uv.y > max_uv.y:
- max_uv.y = uv.y
- if uv.x < min_uv.x:
- min_uv.x = uv.x
- if uv.y < min_uv.y:
- min_uv.y = uv.y
- a = a + uv
- n = n + 1
- ave_uv = ave_uv + a
- num_uv = num_uv + n
- a = a / n
- face['ave_uv'] = a
- ave_uv = ave_uv / num_uv
-
- info['center'] = ave_uv
- info['size'] = max_uv - min_uv
- info['num_uv'] = num_uv
- info['group'] = -1
- info['faces'] = isl
-
- island_info.append(info)
-
- return island_info
-
- def __parse_island(self, bm, face_idx, faces_left, island):
- """
- Parse island
- """
-
- if face_idx in faces_left:
- faces_left.remove(face_idx)
- island.append({'face': bm.faces[face_idx]})
- for v in self.__face_to_verts[face_idx]:
- connected_faces = self.__vert_to_faces[v]
- if connected_faces:
- for cf in connected_faces:
- self.__parse_island(bm, cf, faces_left, island)
-
- def __get_island(self, bm):
- """
- Get island list
- """
-
- uv_island_lists = []
- faces_left = set(self.__face_to_verts.keys())
- while faces_left:
- current_island = []
- face_idx = list(faces_left)[0]
- self.__parse_island(bm, face_idx, faces_left, current_island)
- uv_island_lists.append(current_island)
-
- return uv_island_lists
diff --git a/uv_magic_uv/muv_preserve_uv_aspect.py b/uv_magic_uv/op/preserve_uv_aspect.py
index 68e75f74..bc2f1b81 100644
--- a/uv_magic_uv/muv_preserve_uv_aspect.py
+++ b/uv_magic_uv/op/preserve_uv_aspect.py
@@ -20,14 +20,15 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
import bpy
import bmesh
from bpy.props import StringProperty, EnumProperty
from mathutils import Vector
-from . import muv_common
+
+from .. import common
class MUV_PreserveUVAspect(bpy.types.Operator):
@@ -71,7 +72,7 @@ class MUV_PreserveUVAspect(bpy.types.Operator):
obj = context.active_object
bm = bmesh.from_edit_mesh(obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
+ if common.check_version(2, 73, 0) >= 0:
bm.faces.ensure_lookup_table()
if not bm.loops.layers.uv:
@@ -202,22 +203,3 @@ class MUV_PreserveUVAspect(bpy.types.Operator):
bmesh.update_edit_mesh(obj.data)
return {'FINISHED'}
-
-
-class MUV_PreserveUVAspectMenu(bpy.types.Menu):
- """
- Menu class: Preserve UV Aspect
- """
-
- bl_idname = "uv.muv_preserve_uv_aspect_menu"
- bl_label = "Preserve UV Aspect"
- bl_description = "Preserve UV Aspect"
-
- def draw(self, _):
- layout = self.layout
-
- # create sub menu
- for key in bpy.data.images.keys():
- layout.operator(
- MUV_PreserveUVAspect.bl_idname,
- text=key, icon="IMAGE_COL").dest_img_name = key
diff --git a/uv_magic_uv/op/smooth_uv.py b/uv_magic_uv/op/smooth_uv.py
new file mode 100644
index 00000000..aa9b22c0
--- /dev/null
+++ b/uv_magic_uv/op/smooth_uv.py
@@ -0,0 +1,215 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
+
+import bpy
+import bmesh
+from bpy.props import BoolProperty, FloatProperty
+
+from .. import common
+
+
+class MUV_AUVSmooth(bpy.types.Operator):
+
+ bl_idname = "uv.muv_auv_smooth"
+ bl_label = "Smooth"
+ bl_description = "Smooth UV coordinates"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ transmission = BoolProperty(
+ name="Transmission",
+ description="Smooth linked UVs",
+ default=False
+ )
+ mesh_infl = FloatProperty(
+ name="Mesh Influence",
+ description="Influence rate of mesh vertex",
+ min=0.0,
+ max=1.0,
+ default=0.0
+ )
+ select = BoolProperty(
+ name="Select",
+ description="Select UVs which are smoothed",
+ default=False
+ )
+
+ @classmethod
+ def poll(cls, context):
+ return context.mode == 'EDIT_MESH'
+
+ def __smooth_wo_transmission(self, loop_seqs, uv_layer):
+ # calculate path length
+ loops = []
+ for hseq in loop_seqs:
+ loops.extend([hseq[0][0], hseq[0][1]])
+ full_vlen = 0
+ accm_vlens = [0.0]
+ full_uvlen = 0
+ accm_uvlens = [0.0]
+ orig_uvs = [loop_seqs[0][0][0][uv_layer].uv.copy()]
+ for l1, l2 in zip(loops[:-1], loops[1:]):
+ diff_v = l2.vert.co - l1.vert.co
+ full_vlen = full_vlen + diff_v.length
+ accm_vlens.append(full_vlen)
+ diff_uv = l2[uv_layer].uv - l1[uv_layer].uv
+ full_uvlen = full_uvlen + diff_uv.length
+ accm_uvlens.append(full_uvlen)
+ orig_uvs.append(l2[uv_layer].uv.copy())
+
+ for hidx, hseq in enumerate(loop_seqs):
+ pair = hseq[0]
+ for pidx, l in enumerate(pair):
+ if self.select:
+ l[uv_layer].select = True
+
+ # ignore start/end loop
+ if (hidx == 0 and pidx == 0) or\
+ ((hidx == len(loop_seqs) - 1) and (pidx == len(pair) - 1)):
+ continue
+
+ # calculate target path length
+ # target = no influenced * (1 - infl) + influenced * infl
+ tgt_noinfl = full_uvlen * (hidx + pidx) / (len(loop_seqs))
+ tgt_infl = full_uvlen * accm_vlens[hidx * 2 + pidx] / full_vlen
+ target_length = tgt_noinfl * (1 - self.mesh_infl) + \
+ tgt_infl * self.mesh_infl
+
+ # get target UV
+ for i in range(len(accm_uvlens[:-1])):
+ # get line segment which UV will be placed
+ if ((accm_uvlens[i] <= target_length) and
+ (accm_uvlens[i + 1] > target_length)):
+ tgt_seg_len = target_length - accm_uvlens[i]
+ seg_len = accm_uvlens[i + 1] - accm_uvlens[i]
+ uv1 = orig_uvs[i]
+ uv2 = orig_uvs[i + 1]
+ target_uv = uv1 + (uv2 - uv1) * tgt_seg_len / seg_len
+ break
+ else:
+ self.report({'ERROR'}, "Failed to get target UV")
+ return {'CANCELLED'}
+
+ # update UV
+ l[uv_layer].uv = target_uv
+
+ def __smooth_w_transmission(self, loop_seqs, uv_layer):
+ # calculate path length
+ loops = []
+ for vidx in range(len(loop_seqs[0])):
+ ls = []
+ for hseq in loop_seqs:
+ ls.extend(hseq[vidx])
+ loops.append(ls)
+
+ orig_uvs = []
+ accm_vlens = []
+ full_vlens = []
+ accm_uvlens = []
+ full_uvlens = []
+ for ls in loops:
+ full_v = 0.0
+ accm_v = [0.0]
+ full_uv = 0.0
+ accm_uv = [0.0]
+ uvs = [ls[0][uv_layer].uv.copy()]
+ for l1, l2 in zip(ls[:-1], ls[1:]):
+ diff_v = l2.vert.co - l1.vert.co
+ full_v = full_v + diff_v.length
+ accm_v.append(full_v)
+ diff_uv = l2[uv_layer].uv - l1[uv_layer].uv
+ full_uv = full_uv + diff_uv.length
+ accm_uv.append(full_uv)
+ uvs.append(l2[uv_layer].uv.copy())
+ accm_vlens.append(accm_v)
+ full_vlens.append(full_v)
+ accm_uvlens.append(accm_uv)
+ full_uvlens.append(full_uv)
+ orig_uvs.append(uvs)
+
+ for hidx, hseq in enumerate(loop_seqs):
+ for vidx, (pair, uvs, accm_v, full_v, accm_uv, full_uv)\
+ in enumerate(zip(hseq, orig_uvs, accm_vlens, full_vlens,
+ accm_uvlens, full_uvlens)):
+ for pidx, l in enumerate(pair):
+ if self.select:
+ l[uv_layer].select = True
+
+ # ignore start/end loop
+ if hidx == 0 and pidx == 0:
+ continue
+ if hidx == len(loop_seqs) - 1 and pidx == len(pair) - 1:
+ continue
+
+ # calculate target path length
+ # target = no influenced * (1 - infl) + influenced * infl
+ tgt_noinfl = full_uv * (hidx + pidx) / (len(loop_seqs))
+ tgt_infl = full_uv * accm_v[hidx * 2 + pidx] / full_v
+ target_length = tgt_noinfl * (1 - self.mesh_infl) + \
+ tgt_infl * self.mesh_infl
+
+ # get target UV
+ for i in range(len(accm_uv[:-1])):
+ # get line segment to be placed
+ if ((accm_uv[i] <= target_length) and
+ (accm_uv[i + 1] > target_length)):
+ tgt_seg_len = target_length - accm_uv[i]
+ seg_len = accm_uv[i + 1] - accm_uv[i]
+ uv1 = uvs[i]
+ uv2 = uvs[i + 1]
+ target_uv = uv1 +\
+ (uv2 - uv1) * tgt_seg_len / seg_len
+ break
+ else:
+ self.report({'ERROR'}, "Failed to get target UV")
+ return {'CANCELLED'}
+
+ # update UV
+ l[uv_layer].uv = target_uv
+
+ def __smooth(self, loop_seqs, uv_layer):
+ if self.transmission:
+ self.__smooth_w_transmission(loop_seqs, uv_layer)
+ else:
+ self.__smooth_wo_transmission(loop_seqs, uv_layer)
+
+ def execute(self, context):
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
+ uv_layer = bm.loops.layers.uv.verify()
+
+ # loop_seqs[horizontal][vertical][loop]
+ loop_seqs, error = common.get_loop_sequences(bm, uv_layer)
+ if not loop_seqs:
+ self.report({'WARNING'}, error)
+ return {'CANCELLED'}
+
+ # smooth
+ self.__smooth(loop_seqs, uv_layer)
+
+ bmesh.update_edit_mesh(obj.data)
+
+ return {'FINISHED'}
diff --git a/uv_magic_uv/muv_texlock_ops.py b/uv_magic_uv/op/texture_lock.py
index bfc95129..d6c56f5a 100644
--- a/uv_magic_uv/muv_texlock_ops.py
+++ b/uv_magic_uv/op/texture_lock.py
@@ -20,20 +20,18 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
import math
-from math import (
- atan2, cos,
- sqrt, sin, fabs,
-)
+from math import atan2, cos, sqrt, sin, fabs
import bpy
import bmesh
from mathutils import Vector
from bpy.props import BoolProperty
-from . import muv_common
+
+from .. import common
def get_vco(verts_orig, loop):
@@ -195,7 +193,7 @@ class MUV_TexLockStart(bpy.types.Operator):
props = context.scene.muv_props.texlock
obj = bpy.context.active_object
bm = bmesh.from_edit_mesh(obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
+ if common.check_version(2, 73, 0) >= 0:
bm.verts.ensure_lookup_table()
bm.edges.ensure_lookup_table()
bm.faces.ensure_lookup_table()
@@ -224,13 +222,15 @@ class MUV_TexLockStop(bpy.types.Operator):
connect = BoolProperty(
name="Connect UV",
- default=True)
+ default=True
+ )
def execute(self, context):
- props = context.scene.muv_props.texlock
+ sc = context.scene
+ props = sc.muv_props.texlock
obj = bpy.context.active_object
bm = bmesh.from_edit_mesh(obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
+ if common.check_version(2, 73, 0) >= 0:
bm.verts.ensure_lookup_table()
bm.edges.ensure_lookup_table()
bm.faces.ensure_lookup_table()
@@ -297,14 +297,14 @@ class MUV_TexLockUpdater(bpy.types.Operator):
props = context.scene.muv_props.texlock
obj = bpy.context.active_object
bm = bmesh.from_edit_mesh(obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
+ if common.check_version(2, 73, 0) >= 0:
bm.verts.ensure_lookup_table()
bm.edges.ensure_lookup_table()
bm.faces.ensure_lookup_table()
if not bm.loops.layers.uv:
self.report({'WARNING'}, "Object must have more than one UV map")
- return {'CANCELLED'}
+ return
uv_layer = bm.loops.layers.uv.verify()
verts = [v.index for v in bm.verts if v.select]
@@ -313,7 +313,7 @@ class MUV_TexLockUpdater(bpy.types.Operator):
for vidx, v_orig in zip(verts, verts_orig):
if vidx != v_orig["vidx"]:
self.report({'ERROR'}, "Internal Error")
- return {"CANCELLED"}
+ return
v = bm.verts[vidx]
link_loops = get_link_loops(v)
@@ -336,7 +336,7 @@ class MUV_TexLockUpdater(bpy.types.Operator):
v_orig["moved"] = True
bmesh.update_edit_mesh(obj.data)
- muv_common.redraw_all_areas()
+ common.redraw_all_areas()
props.intr_verts_orig = [
{"vidx": v.index, "vco": v.co.copy(), "moved": False}
for v in bm.verts if v.select]
@@ -395,7 +395,7 @@ class MUV_TexLockIntrStart(bpy.types.Operator):
obj = bpy.context.active_object
bm = bmesh.from_edit_mesh(obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
+ if common.check_version(2, 73, 0) >= 0:
bm.verts.ensure_lookup_table()
bm.edges.ensure_lookup_table()
bm.faces.ensure_lookup_table()
diff --git a/uv_magic_uv/muv_texproj_ops.py b/uv_magic_uv/op/texture_projection.py
index ffa4e789..77a81aa0 100644
--- a/uv_magic_uv/muv_texproj_ops.py
+++ b/uv_magic_uv/op/texture_projection.py
@@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
from collections import namedtuple
@@ -31,7 +31,7 @@ import bmesh
import mathutils
from bpy_extras import view3d_utils
-from . import muv_common
+from .. import common
Rect = namedtuple('Rect', 'x0 y0 x1 y1')
@@ -237,28 +237,28 @@ class MUV_TexProjProject(bpy.types.Operator):
def execute(self, context):
sc = context.scene
- if context.mode != "EDIT_MESH":
- self.report({'WARNING'}, "Mesh must be in Edit mode")
- return {'CANCELLED'}
-
if sc.muv_texproj_tex_image == "None":
self.report({'WARNING'}, "No textures are selected")
return {'CANCELLED'}
- _, region, space = muv_common.get_space(
+ _, region, space = common.get_space(
'VIEW_3D', 'WINDOW', 'VIEW_3D')
# get faces to be texture projected
obj = context.active_object
world_mat = obj.matrix_world
bm = bmesh.from_edit_mesh(obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
+ if common.check_version(2, 73, 0) >= 0:
bm.faces.ensure_lookup_table()
# get UV and texture layer
if not bm.loops.layers.uv:
- self.report({'WARNING'}, "Object must have more than one UV map")
- return {'CANCELLED'}
+ if sc.muv_texproj_assign_uvmap:
+ bm.loops.layers.uv.new()
+ else:
+ self.report({'WARNING'},
+ "Object must have more than one UV map")
+ return {'CANCELLED'}
uv_layer = bm.loops.layers.uv.verify()
tex_layer = bm.faces.layers.tex.verify()
@@ -290,50 +290,7 @@ class MUV_TexProjProject(bpy.types.Operator):
l[uv_layer].uv = v_canvas[i].to_2d()
i = i + 1
- muv_common.redraw_all_areas()
+ common.redraw_all_areas()
bmesh.update_edit_mesh(obj.data)
return {'FINISHED'}
-
-
-class OBJECT_PT_TP(bpy.types.Panel):
- """
- Panel class: Texture Projection Menu on Property Panel on View3D
- """
-
- bl_label = "Texture Projection"
- bl_description = "Texture Projection Menu"
- bl_space_type = 'VIEW_3D'
- bl_region_type = 'UI'
- bl_context = 'mesh_edit'
-
- @classmethod
- def poll(cls, context):
- prefs = context.user_preferences.addons["uv_magic_uv"].preferences
- return prefs.enable_texproj
-
- def draw_header(self, _):
- layout = self.layout
- layout.label(text="", icon='IMAGE_COL')
-
- def draw(self, context):
- sc = context.scene
- layout = self.layout
- props = sc.muv_props.texproj
- if props.running is False:
- layout.operator(
- MUV_TexProjStart.bl_idname, text="Start", icon='PLAY')
- else:
- layout.operator(
- MUV_TexProjStop.bl_idname, text="Stop", icon='PAUSE')
- layout.prop(sc, "muv_texproj_tex_image", text="Image")
- layout.prop(
- sc, "muv_texproj_tex_transparency", text="Transparency"
- )
- layout.prop(sc, "muv_texproj_adjust_window", text="Adjust Window")
- if not sc.muv_texproj_adjust_window:
- layout.prop(sc, "muv_texproj_tex_magnitude", text="Magnitude")
- layout.prop(
- sc, "muv_texproj_apply_tex_aspect", text="Texture Aspect Ratio"
- )
- layout.operator(MUV_TexProjProject.bl_idname, text="Project")
diff --git a/uv_magic_uv/op/texture_wrap.py b/uv_magic_uv/op/texture_wrap.py
new file mode 100644
index 00000000..01e507bd
--- /dev/null
+++ b/uv_magic_uv/op/texture_wrap.py
@@ -0,0 +1,212 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
+
+import bpy
+import bmesh
+
+from .. import common
+
+
+class MUV_TexWrapRefer(bpy.types.Operator):
+ """
+ Operation class: Refer UV
+ """
+
+ bl_idname = "uv.muv_texwrap_refer"
+ bl_label = "Refer"
+ bl_description = "Refer UV"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ def execute(self, context):
+ props = context.scene.muv_props.texwrap
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
+
+ if not bm.loops.layers.uv:
+ self.report({'WARNING'}, "Object must have more than one UV map")
+ return {'CANCELLED'}
+
+ sel_faces = [f for f in bm.faces if f.select]
+ if len(sel_faces) != 1:
+ self.report({'WARNING'}, "Must select only one face")
+ return {'CANCELLED'}
+
+ props.ref_face_index = sel_faces[0].index
+ props.ref_obj = obj
+
+ return {'FINISHED'}
+
+
+class MUV_TexWrapSet(bpy.types.Operator):
+ """
+ Operation class: Set UV
+ """
+
+ bl_idname = "uv.muv_texwrap_set"
+ bl_label = "Set"
+ bl_description = "Set UV"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ def execute(self, context):
+ sc = context.scene
+ props = sc.muv_props.texwrap
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
+
+ if not bm.loops.layers.uv:
+ self.report({'WARNING'}, "Object must have more than one UV map")
+ return {'CANCELLED'}
+ uv_layer = bm.loops.layers.uv.verify()
+
+ if sc.muv_texwrap_selseq:
+ sel_faces = []
+ for hist in bm.select_history:
+ if isinstance(hist, bmesh.types.BMFace) and hist.select:
+ sel_faces.append(hist)
+ if not sel_faces:
+ self.report({'WARNING'}, "Must select more than one face")
+ return {'CANCELLED'}
+ else:
+ sel_faces = [f for f in bm.faces if f.select]
+ if len(sel_faces) != 1:
+ self.report({'WARNING'}, "Must select only one face")
+ return {'CANCELLED'}
+
+ ref_face_index = props.ref_face_index
+ for face in sel_faces:
+ tgt_face_index = face.index
+ if ref_face_index == tgt_face_index:
+ self.report({'WARNING'}, "Must select different face")
+ return {'CANCELLED'}
+
+ if props.ref_obj != obj:
+ self.report({'WARNING'}, "Object must be same")
+ return {'CANCELLED'}
+
+ ref_face = bm.faces[ref_face_index]
+ tgt_face = bm.faces[tgt_face_index]
+
+ # get common vertices info
+ common_verts = []
+ for sl in ref_face.loops:
+ for dl in tgt_face.loops:
+ if sl.vert == dl.vert:
+ info = {"vert": sl.vert, "ref_loop": sl,
+ "tgt_loop": dl}
+ common_verts.append(info)
+ break
+
+ if len(common_verts) != 2:
+ self.report({'WARNING'},
+ "2 verticies must be shared among faces")
+ return {'CANCELLED'}
+
+ # get reference other vertices info
+ ref_other_verts = []
+ for sl in ref_face.loops:
+ for ci in common_verts:
+ if sl.vert == ci["vert"]:
+ break
+ else:
+ info = {"vert": sl.vert, "loop": sl}
+ ref_other_verts.append(info)
+
+ if not ref_other_verts:
+ self.report({'WARNING'}, "More than 1 vertex must be unshared")
+ return {'CANCELLED'}
+
+ # get reference info
+ ref_info = {}
+ cv0 = common_verts[0]["vert"].co
+ cv1 = common_verts[1]["vert"].co
+ cuv0 = common_verts[0]["ref_loop"][uv_layer].uv
+ cuv1 = common_verts[1]["ref_loop"][uv_layer].uv
+ ov0 = ref_other_verts[0]["vert"].co
+ ouv0 = ref_other_verts[0]["loop"][uv_layer].uv
+ ref_info["vert_vdiff"] = cv1 - cv0
+ ref_info["uv_vdiff"] = cuv1 - cuv0
+ ref_info["vert_hdiff"], _ = common.diff_point_to_segment(
+ cv0, cv1, ov0)
+ ref_info["uv_hdiff"], _ = common.diff_point_to_segment(
+ cuv0, cuv1, ouv0)
+
+ # get target other vertices info
+ tgt_other_verts = []
+ for dl in tgt_face.loops:
+ for ci in common_verts:
+ if dl.vert == ci["vert"]:
+ break
+ else:
+ info = {"vert": dl.vert, "loop": dl}
+ tgt_other_verts.append(info)
+
+ if not tgt_other_verts:
+ self.report({'WARNING'}, "More than 1 vertex must be unshared")
+ return {'CANCELLED'}
+
+ # get target info
+ for info in tgt_other_verts:
+ cv0 = common_verts[0]["vert"].co
+ cv1 = common_verts[1]["vert"].co
+ cuv0 = common_verts[0]["ref_loop"][uv_layer].uv
+ ov = info["vert"].co
+ info["vert_hdiff"], x = common.diff_point_to_segment(
+ cv0, cv1, ov)
+ info["vert_vdiff"] = x - common_verts[0]["vert"].co
+
+ # calclulate factor
+ fact_h = -info["vert_hdiff"].length / \
+ ref_info["vert_hdiff"].length
+ fact_v = info["vert_vdiff"].length / \
+ ref_info["vert_vdiff"].length
+ duv_h = ref_info["uv_hdiff"] * fact_h
+ duv_v = ref_info["uv_vdiff"] * fact_v
+
+ # get target UV
+ info["target_uv"] = cuv0 + duv_h + duv_v
+
+ # apply to common UVs
+ for info in common_verts:
+ info["tgt_loop"][uv_layer].uv = \
+ info["ref_loop"][uv_layer].uv.copy()
+ # apply to other UVs
+ for info in tgt_other_verts:
+ info["loop"][uv_layer].uv = info["target_uv"]
+
+ common.debug_print("===== Target Other Verticies =====")
+ common.debug_print(tgt_other_verts)
+
+ bmesh.update_edit_mesh(obj.data)
+
+ ref_face_index = tgt_face_index
+
+ if sc.muv_texwrap_set_and_refer:
+ props.ref_face_index = tgt_face_index
+
+ return {'FINISHED'}
diff --git a/uv_magic_uv/muv_transuv_ops.py b/uv_magic_uv/op/transfer_uv.py
index ed0a3c46..132f395e 100644
--- a/uv_magic_uv/muv_transuv_ops.py
+++ b/uv_magic_uv/op/transfer_uv.py
@@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>, Mifth, MaxRobinot"
__status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
from collections import OrderedDict
@@ -29,8 +29,7 @@ import bpy
import bmesh
from bpy.props import BoolProperty
-from . import muv_props
-from . import muv_common
+from .. import common
class MUV_TransUVCopy(bpy.types.Operator):
@@ -48,7 +47,7 @@ class MUV_TransUVCopy(bpy.types.Operator):
props = context.scene.muv_props.transuv
active_obj = context.scene.objects.active
bm = bmesh.from_edit_mesh(active_obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
+ if common.check_version(2, 73, 0) >= 0:
bm.faces.ensure_lookup_table()
# get UV layer
@@ -115,7 +114,7 @@ class MUV_TransUVPaste(bpy.types.Operator):
props = context.scene.muv_props.transuv
active_obj = context.scene.objects.active
bm = bmesh.from_edit_mesh(active_obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
+ if common.check_version(2, 73, 0) >= 0:
bm.faces.ensure_lookup_table()
# get UV layer
@@ -291,19 +290,19 @@ def parse_faces(
vert1 = sorted_edge.verts[0]
vert2 = sorted_edge.verts[1]
- muv_common.debug_print(face_stuff[0], vert1, vert2)
+ common.debug_print(face_stuff[0], vert1, vert2)
if face_stuff[0].index(vert1) > face_stuff[0].index(vert2):
vert1 = sorted_edge.verts[1]
vert2 = sorted_edge.verts[0]
- muv_common.debug_print(shared_face.verts, vert1, vert2)
+ common.debug_print(shared_face.verts, vert1, vert2)
new_face_stuff = get_other_verts_edges(
shared_face, vert1, vert2, sorted_edge, uv_layer)
all_sorted_faces[shared_face] = new_face_stuff
used_verts.update(shared_face.verts)
used_edges.update(shared_face.edges)
- if muv_props.DEBUG:
+ if common.DEBUG:
shared_face.select = True # test which faces are parsed
new_shared_faces.append(shared_face)
diff --git a/uv_magic_uv/muv_unwrapconst_ops.py b/uv_magic_uv/op/unwrap_constraint.py
index 1a691119..e98879b7 100644
--- a/uv_magic_uv/muv_unwrapconst_ops.py
+++ b/uv_magic_uv/op/unwrap_constraint.py
@@ -18,8 +18,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
import bpy
import bmesh
@@ -28,7 +28,8 @@ from bpy.props import (
EnumProperty,
FloatProperty,
)
-from . import muv_common
+
+from .. import common
class MUV_UnwrapConstraint(bpy.types.Operator):
@@ -74,18 +75,21 @@ class MUV_UnwrapConstraint(bpy.types.Operator):
u_const = BoolProperty(
name="U-Constraint",
description="Keep UV U-axis coordinate",
- default=False)
+ default=False
+ )
v_const = BoolProperty(
name="V-Constraint",
description="Keep UV V-axis coordinate",
- default=False)
+ default=False
+ )
def execute(self, _):
obj = bpy.context.active_object
bm = bmesh.from_edit_mesh(obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
+ if common.check_version(2, 73, 0) >= 0:
bm.faces.ensure_lookup_table()
+ # bpy.ops.uv.unwrap() makes one UV map at least
if not bm.loops.layers.uv:
self.report({'WARNING'}, "Object must have more than one UV map")
return {'CANCELLED'}
diff --git a/uv_magic_uv/muv_uvbb_ops.py b/uv_magic_uv/op/uv_bounding_box.py
index 4f7b0631..9ebc76c4 100644
--- a/uv_magic_uv/muv_uvbb_ops.py
+++ b/uv_magic_uv/op/uv_bounding_box.py
@@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
from enum import IntEnum
import math
@@ -31,7 +31,7 @@ import bgl
import mathutils
import bmesh
-from . import muv_common
+from .. import common
MAX_VALUE = 100000.0
@@ -602,17 +602,23 @@ class MUV_UVBBUpdater(bpy.types.Operator):
"""
Get UV coordinate
"""
+ sc = context.scene
obj = context.active_object
uv_info = []
bm = bmesh.from_edit_mesh(obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
+ if common.check_version(2, 73, 0) >= 0:
bm.faces.ensure_lookup_table()
if not bm.loops.layers.uv:
return None
uv_layer = bm.loops.layers.uv.verify()
for f in bm.faces:
- if f.select:
- for i, l in enumerate(f.loops):
+ if not f.select:
+ continue
+ for i, l in enumerate(f.loops):
+ if sc.muv_uvbb_boundary == 'UV_SEL':
+ if l[uv_layer].select:
+ uv_info.append((f.index, i, l[uv_layer].uv.copy()))
+ elif sc.muv_uvbb_boundary == 'UV':
uv_info.append((f.index, i, l[uv_layer].uv.copy()))
if not uv_info:
return None
@@ -661,7 +667,7 @@ class MUV_UVBBUpdater(bpy.types.Operator):
"""
obj = context.active_object
bm = bmesh.from_edit_mesh(obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
+ if common.check_version(2, 73, 0) >= 0:
bm.faces.ensure_lookup_table()
if not bm.loops.layers.uv:
return
@@ -683,10 +689,17 @@ class MUV_UVBBUpdater(bpy.types.Operator):
def modal(self, context, event):
props = context.scene.muv_props.uvbb
- muv_common.redraw_all_areas()
+ common.redraw_all_areas()
if props.running is False:
self.__handle_remove(context)
return {'FINISHED'}
+
+ area, _, _ = common.get_space('VIEW_3D', 'WINDOW', 'VIEW_3D')
+
+ if event.mouse_region_x < 0 or event.mouse_region_x > area.width or \
+ event.mouse_region_y < 0 or event.mouse_region_y > area.height:
+ return {'PASS_THROUGH'}
+
if event.type == 'TIMER':
trans_mat = self.__cmd_exec.execute()
self.__update_uvs(context, props.uv_info_ini, trans_mat)
@@ -695,7 +708,7 @@ class MUV_UVBBUpdater(bpy.types.Operator):
self.__state_mgr.update(context, props.ctrl_points, event)
- return {'PASS_THROUGH'}
+ return {'RUNNING_MODAL'}
def execute(self, context):
props = context.scene.muv_props.uvbb
@@ -717,37 +730,3 @@ class MUV_UVBBUpdater(bpy.types.Operator):
props.running = True
return {'RUNNING_MODAL'}
-
-
-class IMAGE_PT_MUV_UVBB(bpy.types.Panel):
- """
- Panel class: UV Bounding Box Menu on Property Panel on UV/ImageEditor
- """
-
- bl_space_type = 'IMAGE_EDITOR'
- bl_region_type = 'UI'
- bl_label = "UV Bounding Box"
- bl_context = 'mesh_edit'
-
- @classmethod
- def poll(cls, context):
- prefs = context.user_preferences.addons["uv_magic_uv"].preferences
- return prefs.enable_uvbb
-
- def draw_header(self, _):
- layout = self.layout
- layout.label(text="", icon='IMAGE_COL')
-
- def draw(self, context):
- sc = context.scene
- props = sc.muv_props.uvbb
- layout = self.layout
- if props.running is False:
- layout.operator(
- MUV_UVBBUpdater.bl_idname, text="Display UV Bounding Box",
- icon='PLAY')
- else:
- layout.operator(
- MUV_UVBBUpdater.bl_idname, text="Hide UV Bounding Box",
- icon='PAUSE')
- layout.prop(sc, "muv_uvbb_uniform_scaling", text="Uniform Scaling")
diff --git a/uv_magic_uv/op/uv_inspection.py b/uv_magic_uv/op/uv_inspection.py
new file mode 100644
index 00000000..60a754a3
--- /dev/null
+++ b/uv_magic_uv/op/uv_inspection.py
@@ -0,0 +1,623 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
+
+import bpy
+import bmesh
+import bgl
+from mathutils import Vector
+
+from .. import common
+
+
+def is_polygon_same(points1, points2):
+ if len(points1) != len(points2):
+ return False
+
+ pts1 = points1.as_list()
+ pts2 = points2.as_list()
+
+ for p1 in pts1:
+ for p2 in pts2:
+ diff = p2 - p1
+ if diff.length < 0.0000001:
+ pts2.remove(p2)
+ break
+ else:
+ return False
+
+ return True
+
+
+def is_segment_intersect(start1, end1, start2, end2):
+ seg1 = end1 - start1
+ seg2 = end2 - start2
+
+ a1 = -seg1.y
+ b1 = seg1.x
+ d1 = -(a1 * start1.x + b1 * start1.y)
+
+ a2 = -seg2.y
+ b2 = seg2.x
+ d2 = -(a2 * start2.x + b2 * start2.y)
+
+ seg1_line2_start = a2 * start1.x + b2 * start1.y + d2
+ seg1_line2_end = a2 * end1.x + b2 * end1.y + d2
+
+ seg2_line1_start = a1 * start2.x + b1 * start2.y + d1
+ seg2_line1_end = a1 * end2.x + b1 * end2.y + d1
+
+ if (seg1_line2_start * seg1_line2_end >= 0) or \
+ (seg2_line1_start * seg2_line1_end >= 0):
+ return False, None
+
+ u = seg1_line2_start / (seg1_line2_start - seg1_line2_end)
+ out = start1 + u * seg1
+
+ return True, out
+
+
+class RingBuffer:
+ def __init__(self, arr):
+ self.__buffer = arr.copy()
+ self.__pointer = 0
+
+ def __repr__(self):
+ return repr(self.__buffer)
+
+ def __len__(self):
+ return len(self.__buffer)
+
+ def insert(self, val, offset=0):
+ self.__buffer.insert(self.__pointer + offset, val)
+
+ def head(self):
+ return self.__buffer[0]
+
+ def tail(self):
+ return self.__buffer[-1]
+
+ def get(self, offset=0):
+ size = len(self.__buffer)
+ val = self.__buffer[(self.__pointer + offset) % size]
+ return val
+
+ def next(self):
+ size = len(self.__buffer)
+ self.__pointer = (self.__pointer + 1) % size
+
+ def reset(self):
+ self.__pointer = 0
+
+ def find(self, obj):
+ try:
+ idx = self.__buffer.index(obj)
+ except ValueError:
+ return None
+ return self.__buffer[idx]
+
+ def find_and_next(self, obj):
+ size = len(self.__buffer)
+ idx = self.__buffer.index(obj)
+ self.__pointer = (idx + 1) % size
+
+ def find_and_set(self, obj):
+ idx = self.__buffer.index(obj)
+ self.__pointer = idx
+
+ def as_list(self):
+ return self.__buffer.copy()
+
+ def reverse(self):
+ self.__buffer.reverse()
+ self.reset()
+
+
+# clip: reference polygon
+# subject: tested polygon
+def do_weiler_atherton_cliping(clip, subject, uv_layer, mode):
+
+ clip_uvs = RingBuffer([l[uv_layer].uv.copy() for l in clip.loops])
+ if is_polygon_flipped(clip_uvs):
+ clip_uvs.reverse()
+ subject_uvs = RingBuffer([l[uv_layer].uv.copy() for l in subject.loops])
+ if is_polygon_flipped(subject_uvs):
+ subject_uvs.reverse()
+
+ common.debug_print("===== Clip UV List =====")
+ common.debug_print(clip_uvs)
+ common.debug_print("===== Subject UV List =====")
+ common.debug_print(subject_uvs)
+
+ # check if clip and subject is overlapped completely
+ if is_polygon_same(clip_uvs, subject_uvs):
+ polygons = [subject_uvs.as_list()]
+ common.debug_print("===== Polygons Overlapped Completely =====")
+ common.debug_print(polygons)
+ return True, polygons
+
+ # check if subject is in clip
+ if is_points_in_polygon(subject_uvs, clip_uvs):
+ polygons = [subject_uvs.as_list()]
+ return True, polygons
+
+ # check if clip is in subject
+ if is_points_in_polygon(clip_uvs, subject_uvs):
+ polygons = [subject_uvs.as_list()]
+ return True, polygons
+
+ # check if clip and subject is overlapped partially
+ intersections = []
+ while True:
+ subject_uvs.reset()
+ while True:
+ uv_start1 = clip_uvs.get()
+ uv_end1 = clip_uvs.get(1)
+ uv_start2 = subject_uvs.get()
+ uv_end2 = subject_uvs.get(1)
+ intersected, point = is_segment_intersect(uv_start1, uv_end1,
+ uv_start2, uv_end2)
+ if intersected:
+ clip_uvs.insert(point, 1)
+ subject_uvs.insert(point, 1)
+ intersections.append([point,
+ [clip_uvs.get(), clip_uvs.get(1)]])
+ subject_uvs.next()
+ if subject_uvs.get() == subject_uvs.head():
+ break
+ clip_uvs.next()
+ if clip_uvs.get() == clip_uvs.head():
+ break
+
+ common.debug_print("===== Intersection List =====")
+ common.debug_print(intersections)
+
+ # no intersection, so subject and clip is not overlapped
+ if not intersections:
+ return False, None
+
+ def get_intersection_pair(intersections, key):
+ for sect in intersections:
+ if sect[0] == key:
+ return sect[1]
+
+ return None
+
+ # make enter/exit pair
+ subject_uvs.reset()
+ subject_entering = []
+ subject_exiting = []
+ clip_entering = []
+ clip_exiting = []
+ intersect_uv_list = []
+ while True:
+ pair = get_intersection_pair(intersections, subject_uvs.get())
+ if pair:
+ sub = subject_uvs.get(1) - subject_uvs.get(-1)
+ inter = pair[1] - pair[0]
+ cross = sub.x * inter.y - inter.x * sub.y
+ if cross < 0:
+ subject_entering.append(subject_uvs.get())
+ clip_exiting.append(subject_uvs.get())
+ else:
+ subject_exiting.append(subject_uvs.get())
+ clip_entering.append(subject_uvs.get())
+ intersect_uv_list.append(subject_uvs.get())
+
+ subject_uvs.next()
+ if subject_uvs.get() == subject_uvs.head():
+ break
+
+ common.debug_print("===== Enter List =====")
+ common.debug_print(clip_entering)
+ common.debug_print(subject_entering)
+ common.debug_print("===== Exit List =====")
+ common.debug_print(clip_exiting)
+ common.debug_print(subject_exiting)
+
+ # for now, can't handle the situation when fulfill all below conditions
+ # * two faces have common edge
+ # * each face is intersected
+ # * Show Mode is "Part"
+ # so for now, ignore this situation
+ if len(subject_entering) != len(subject_exiting):
+ if mode == 'FACE':
+ polygons = [subject_uvs.as_list()]
+ return True, polygons
+ return False, None
+
+ def traverse(current_list, entering, exiting, poly, current, other_list):
+ result = current_list.find(current)
+ if not result:
+ return None
+ if result != current:
+ print("Internal Error")
+ return None
+
+ # enter
+ if entering.count(current) >= 1:
+ entering.remove(current)
+
+ current_list.find_and_next(current)
+ current = current_list.get()
+
+ while exiting.count(current) == 0:
+ poly.append(current.copy())
+ current_list.find_and_next(current)
+ current = current_list.get()
+
+ # exit
+ poly.append(current.copy())
+ exiting.remove(current)
+
+ other_list.find_and_set(current)
+ return other_list.get()
+
+ # Traverse
+ polygons = []
+ current_uv_list = subject_uvs
+ other_uv_list = clip_uvs
+ current_entering = subject_entering
+ current_exiting = subject_exiting
+
+ poly = []
+ current_uv = current_entering[0]
+
+ while True:
+ current_uv = traverse(current_uv_list, current_entering,
+ current_exiting, poly, current_uv, other_uv_list)
+
+ if current_uv_list == subject_uvs:
+ current_uv_list = clip_uvs
+ other_uv_list = subject_uvs
+ current_entering = clip_entering
+ current_exiting = clip_exiting
+ common.debug_print("-- Next: Clip --")
+ else:
+ current_uv_list = subject_uvs
+ other_uv_list = clip_uvs
+ current_entering = subject_entering
+ current_exiting = subject_exiting
+ common.debug_print("-- Next: Subject --")
+
+ common.debug_print(clip_entering)
+ common.debug_print(clip_exiting)
+ common.debug_print(subject_entering)
+ common.debug_print(subject_exiting)
+
+ if not clip_entering and not clip_exiting \
+ and not subject_entering and not subject_exiting:
+ break
+
+ polygons.append(poly)
+
+ common.debug_print("===== Polygons Overlapped Partially =====")
+ common.debug_print(polygons)
+
+ return True, polygons
+
+
+class MUV_UVInspRenderer(bpy.types.Operator):
+ """
+ Operation class: Render UV Inspection
+ No operation (only rendering)
+ """
+
+ bl_idname = "uv.muv_uvinsp_renderer"
+ bl_description = "Render overlapped/flipped UVs"
+ bl_label = "Overlapped/Flipped UV renderer"
+
+ __handle = None
+
+ @staticmethod
+ def handle_add(obj, context):
+ sie = bpy.types.SpaceImageEditor
+ MUV_UVInspRenderer.__handle = sie.draw_handler_add(
+ MUV_UVInspRenderer.draw, (obj, context), 'WINDOW', 'POST_PIXEL')
+
+ @staticmethod
+ def handle_remove():
+ if MUV_UVInspRenderer.__handle is not None:
+ bpy.types.SpaceImageEditor.draw_handler_remove(
+ MUV_UVInspRenderer.__handle, 'WINDOW')
+ MUV_UVInspRenderer.__handle = None
+
+ @staticmethod
+ def draw(_, context):
+ sc = context.scene
+ props = sc.muv_props.uvinsp
+ prefs = context.user_preferences.addons["uv_magic_uv"].preferences
+
+ # OpenGL configuration
+ bgl.glEnable(bgl.GL_BLEND)
+
+ # render overlapped UV
+ if sc.muv_uvinsp_show_overlapped:
+ color = prefs.uvinsp_overlapped_color
+ for info in props.overlapped_info:
+ if sc.muv_uvinsp_show_mode == 'PART':
+ for poly in info["polygons"]:
+ bgl.glBegin(bgl.GL_TRIANGLE_FAN)
+ bgl.glColor4f(color[0], color[1], color[2], color[3])
+ for uv in poly:
+ x, y = context.region.view2d.view_to_region(
+ uv.x, uv.y)
+ bgl.glVertex2f(x, y)
+ bgl.glEnd()
+ elif sc.muv_uvinsp_show_mode == 'FACE':
+ bgl.glBegin(bgl.GL_TRIANGLE_FAN)
+ bgl.glColor4f(color[0], color[1], color[2], color[3])
+ for uv in info["subject_uvs"]:
+ x, y = context.region.view2d.view_to_region(uv.x, uv.y)
+ bgl.glVertex2f(x, y)
+ bgl.glEnd()
+
+ # render flipped UV
+ if sc.muv_uvinsp_show_flipped:
+ color = prefs.uvinsp_flipped_color
+ for info in props.flipped_info:
+ if sc.muv_uvinsp_show_mode == 'PART':
+ for poly in info["polygons"]:
+ bgl.glBegin(bgl.GL_TRIANGLE_FAN)
+ bgl.glColor4f(color[0], color[1], color[2], color[3])
+ for uv in poly:
+ x, y = context.region.view2d.view_to_region(
+ uv.x, uv.y)
+ bgl.glVertex2f(x, y)
+ bgl.glEnd()
+ elif sc.muv_uvinsp_show_mode == 'FACE':
+ bgl.glBegin(bgl.GL_TRIANGLE_FAN)
+ bgl.glColor4f(color[0], color[1], color[2], color[3])
+ for uv in info["uvs"]:
+ x, y = context.region.view2d.view_to_region(uv.x, uv.y)
+ bgl.glVertex2f(x, y)
+ bgl.glEnd()
+
+
+def is_polygon_flipped(points):
+ area = 0.0
+ for i in range(len(points)):
+ uv1 = points.get(i)
+ uv2 = points.get(i + 1)
+ a = uv1.x * uv2.y - uv1.y * uv2.x
+ area = area + a
+ if area < 0:
+ # clock-wise
+ return True
+ return False
+
+
+def is_point_in_polygon(point, subject_points):
+ count = 0
+ for i in range(len(subject_points)):
+ uv_start1 = subject_points.get(i)
+ uv_end1 = subject_points.get(i + 1)
+ uv_start2 = point
+ uv_end2 = Vector((1000000.0, point.y))
+ intersected, _ = is_segment_intersect(uv_start1, uv_end1,
+ uv_start2, uv_end2)
+ if intersected:
+ count = count + 1
+
+ return count % 2
+
+
+def is_points_in_polygon(points, subject_points):
+ for i in range(len(points)):
+ internal = is_point_in_polygon(points.get(i), subject_points)
+ if not internal:
+ return False
+
+ return True
+
+
+def get_overlapped_uv_info(bm, faces, uv_layer, mode):
+ # at first, check island overlapped
+ isl = common.get_island_info_from_faces(bm, faces, uv_layer)
+ overlapped_isl_pairs = []
+ for i, i1 in enumerate(isl):
+ for i2 in isl[i + 1:]:
+ if (i1["max"].x < i2["min"].x) or (i2["max"].x < i1["min"].x) or \
+ (i1["max"].y < i2["min"].y) or (i2["max"].y < i1["min"].y):
+ continue
+ overlapped_isl_pairs.append([i1, i2])
+
+ # next, check polygon overlapped
+ overlapped_uvs = []
+ for oip in overlapped_isl_pairs:
+ for clip in oip[0]["faces"]:
+ f_clip = clip["face"]
+ for subject in oip[1]["faces"]:
+ f_subject = subject["face"]
+
+ # fast operation, apply bounding box algorithm
+ if (clip["max_uv"].x < subject["min_uv"].x) or \
+ (subject["max_uv"].x < clip["min_uv"].x) or \
+ (clip["max_uv"].y < subject["min_uv"].y) or \
+ (subject["max_uv"].y < clip["min_uv"].y):
+ continue
+
+ # slow operation, apply Weiler-Atherton cliping algorithm
+ result, polygons = do_weiler_atherton_cliping(f_clip,
+ f_subject,
+ uv_layer, mode)
+ if result:
+ subject_uvs = [l[uv_layer].uv.copy()
+ for l in f_subject.loops]
+ overlapped_uvs.append({"clip_face": f_clip,
+ "subject_face": f_subject,
+ "subject_uvs": subject_uvs,
+ "polygons": polygons})
+
+ return overlapped_uvs
+
+
+def get_flipped_uv_info(faces, uv_layer):
+ flipped_uvs = []
+ for f in faces:
+ polygon = RingBuffer([l[uv_layer].uv.copy() for l in f.loops])
+ if is_polygon_flipped(polygon):
+ uvs = [l[uv_layer].uv.copy() for l in f.loops]
+ flipped_uvs.append({"face": f, "uvs": uvs,
+ "polygons": [polygon.as_list()]})
+
+ return flipped_uvs
+
+
+def update_uvinsp_info(context):
+ sc = context.scene
+ props = sc.muv_props.uvinsp
+
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
+ uv_layer = bm.loops.layers.uv.verify()
+
+ if context.tool_settings.use_uv_select_sync:
+ sel_faces = [f for f in bm.faces]
+ else:
+ sel_faces = [f for f in bm.faces if f.select]
+ props.overlapped_info = get_overlapped_uv_info(bm, sel_faces, uv_layer,
+ sc.muv_uvinsp_show_mode)
+ props.flipped_info = get_flipped_uv_info(sel_faces, uv_layer)
+
+
+class MUV_UVInspUpdate(bpy.types.Operator):
+ """
+ Operation class: Update
+ """
+
+ bl_idname = "uv.muv_uvinsp_update"
+ bl_label = "Update"
+ bl_description = "Update Overlapped/Flipped UV"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ def execute(self, context):
+ update_uvinsp_info(context)
+
+ if context.area:
+ context.area.tag_redraw()
+
+ return {'FINISHED'}
+
+
+class MUV_UVInspDisplay(bpy.types.Operator):
+ """
+ Operation class: Display
+ """
+
+ bl_idname = "uv.muv_uvinsp_display"
+ bl_label = "Display"
+ bl_description = "Display Overlapped/Flipped UV"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ def execute(self, context):
+ sc = context.scene
+ props = sc.muv_props.uvinsp
+ if not props.display_running:
+ update_uvinsp_info(context)
+ MUV_UVInspRenderer.handle_add(self, context)
+ props.display_running = True
+ else:
+ MUV_UVInspRenderer.handle_remove()
+ props.display_running = False
+
+ if context.area:
+ context.area.tag_redraw()
+
+ return {'FINISHED'}
+
+
+class MUV_UVInspSelectOverlapped(bpy.types.Operator):
+ """
+ Operation class: Select faces which have overlapped UVs
+ """
+
+ bl_idname = "uv.muv_uvinsp_select_overlapped"
+ bl_label = "Overlapped"
+ bl_description = "Select faces which have overlapped UVs"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ def execute(self, context):
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
+ uv_layer = bm.loops.layers.uv.verify()
+
+ if context.tool_settings.use_uv_select_sync:
+ sel_faces = [f for f in bm.faces]
+ else:
+ sel_faces = [f for f in bm.faces if f.select]
+
+ overlapped_info = get_overlapped_uv_info(bm, sel_faces, uv_layer,
+ 'FACE')
+
+ for info in overlapped_info:
+ if context.tool_settings.use_uv_select_sync:
+ info["subject_face"].select = True
+ else:
+ for l in info["subject_face"].loops:
+ l[uv_layer].select = True
+
+ bmesh.update_edit_mesh(obj.data)
+
+ return {'FINISHED'}
+
+
+class MUV_UVInspSelectFlipped(bpy.types.Operator):
+ """
+ Operation class: Select faces which have flipped UVs
+ """
+
+ bl_idname = "uv.muv_uvinsp_select_flipped"
+ bl_label = "Flipped"
+ bl_description = "Select faces which have flipped UVs"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ def execute(self, context):
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
+ uv_layer = bm.loops.layers.uv.verify()
+
+ if context.tool_settings.use_uv_select_sync:
+ sel_faces = [f for f in bm.faces]
+ else:
+ sel_faces = [f for f in bm.faces if f.select]
+
+ flipped_info = get_flipped_uv_info(sel_faces, uv_layer)
+
+ for info in flipped_info:
+ if context.tool_settings.use_uv_select_sync:
+ info["face"].select = True
+ else:
+ for l in info["face"].loops:
+ l[uv_layer].select = True
+
+ bmesh.update_edit_mesh(obj.data)
+
+ return {'FINISHED'}
diff --git a/uv_magic_uv/op/uv_sculpt.py b/uv_magic_uv/op/uv_sculpt.py
new file mode 100644
index 00000000..2bf76abd
--- /dev/null
+++ b/uv_magic_uv/op/uv_sculpt.py
@@ -0,0 +1,360 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
+
+from math import pi, cos, tan, sin
+
+import bpy
+import bmesh
+import bgl
+from mathutils import Vector
+from bpy_extras import view3d_utils
+from mathutils.bvhtree import BVHTree
+from mathutils.geometry import barycentric_transform
+
+from .. import common
+
+
+class MUV_UVSculptRenderer(bpy.types.Operator):
+ """
+ Operation class: Render Brush
+ """
+
+ bl_idname = "uv.muv_uvsculpt_renderer"
+ bl_label = "Brush Renderer"
+ bl_description = "Brush Renderer in View3D"
+
+ __handle = None
+
+ @staticmethod
+ def handle_add(obj, context):
+ if MUV_UVSculptRenderer.__handle is None:
+ sv = bpy.types.SpaceView3D
+ MUV_UVSculptRenderer.__handle = sv.draw_handler_add(
+ MUV_UVSculptRenderer.draw_brush,
+ (obj, context), "WINDOW", "POST_PIXEL")
+
+ @staticmethod
+ def handle_remove():
+ if MUV_UVSculptRenderer.__handle is not None:
+ sv = bpy.types.SpaceView3D
+ sv.draw_handler_remove(
+ MUV_UVSculptRenderer.__handle, "WINDOW")
+ MUV_UVSculptRenderer.__handle = None
+
+ @staticmethod
+ def draw_brush(obj, context):
+ sc = context.scene
+ prefs = context.user_preferences.addons["uv_magic_uv"].preferences
+
+ num_segment = 180
+ theta = 2 * pi / num_segment
+ fact_t = tan(theta)
+ fact_r = cos(theta)
+ color = prefs.uvsculpt_brush_color
+
+ bgl.glBegin(bgl.GL_LINE_STRIP)
+ bgl.glColor4f(color[0], color[1], color[2], color[3])
+ x = sc.muv_uvsculpt_radius * cos(0.0)
+ y = sc.muv_uvsculpt_radius * sin(0.0)
+ for _ in range(num_segment):
+ bgl.glVertex2f(x + obj.current_mco.x, y + obj.current_mco.y)
+ tx = -y
+ ty = x
+ x = x + tx * fact_t
+ y = y + ty * fact_t
+ x = x * fact_r
+ y = y * fact_r
+ bgl.glEnd()
+
+
+class MUV_UVSculptOps(bpy.types.Operator):
+ """
+ Operation class: UV Sculpt in View3D
+ """
+
+ bl_idname = "uv.muv_uvsculpt_ops"
+ bl_label = "UV Sculpt"
+ bl_description = "UV Sculpt in View3D"
+ bl_options = {'REGISTER'}
+
+ def __init__(self):
+ self.__timer = None
+ self.__loop_info = []
+ self.__stroking = False
+ self.current_mco = Vector((0.0, 0.0))
+ self.__initial_mco = Vector((0.0, 0.0))
+
+ def __get_strength(self, p, len_, factor):
+ f = factor
+
+ if p > len_:
+ return 0.0
+
+ if p < 0.0:
+ return f
+
+ return (len_ - p) * f / len_
+
+ def __stroke_init(self, context, _):
+ sc = context.scene
+
+ self.__initial_mco = self.current_mco
+
+ # get influenced UV
+ obj = context.active_object
+ world_mat = obj.matrix_world
+ bm = bmesh.from_edit_mesh(obj.data)
+ uv_layer = bm.loops.layers.uv.verify()
+ _, region, space = common.get_space('VIEW_3D', 'WINDOW', 'VIEW_3D')
+
+ self.__loop_info = []
+ for f in bm.faces:
+ if not f.select:
+ continue
+ for i, l in enumerate(f.loops):
+ loc_2d = view3d_utils.location_3d_to_region_2d(
+ region, space.region_3d, world_mat * l.vert.co)
+ diff = loc_2d - self.__initial_mco
+ if diff.length < sc.muv_uvsculpt_radius:
+ info = {
+ "face_idx": f.index,
+ "loop_idx": i,
+ "initial_vco": l.vert.co.copy(),
+ "initial_vco_2d": loc_2d,
+ "initial_uv": l[uv_layer].uv.copy(),
+ "strength": self.__get_strength(
+ diff.length, sc.muv_uvsculpt_radius,
+ sc.muv_uvsculpt_strength)
+ }
+ self.__loop_info.append(info)
+
+ def __stroke_apply(self, context, _):
+ sc = context.scene
+ obj = context.active_object
+ world_mat = obj.matrix_world
+ bm = bmesh.from_edit_mesh(obj.data)
+ uv_layer = bm.loops.layers.uv.verify()
+ mco = self.current_mco
+
+ if sc.muv_uvsculpt_tools == 'GRAB':
+ for info in self.__loop_info:
+ diff_uv = (mco - self.__initial_mco) * info["strength"]
+ l = bm.faces[info["face_idx"]].loops[info["loop_idx"]]
+ l[uv_layer].uv = info["initial_uv"] + diff_uv / 100.0
+
+ elif sc.muv_uvsculpt_tools == 'PINCH':
+ _, region, space = common.get_space('VIEW_3D', 'WINDOW', 'VIEW_3D')
+ loop_info = []
+ for f in bm.faces:
+ if not f.select:
+ continue
+ for i, l in enumerate(f.loops):
+ loc_2d = view3d_utils.location_3d_to_region_2d(
+ region, space.region_3d, world_mat * l.vert.co)
+ diff = loc_2d - self.__initial_mco
+ if diff.length < sc.muv_uvsculpt_radius:
+ info = {
+ "face_idx": f.index,
+ "loop_idx": i,
+ "initial_vco": l.vert.co.copy(),
+ "initial_vco_2d": loc_2d,
+ "initial_uv": l[uv_layer].uv.copy(),
+ "strength": self.__get_strength(
+ diff.length, sc.muv_uvsculpt_radius,
+ sc.muv_uvsculpt_strength)
+ }
+ loop_info.append(info)
+
+ # mouse coordinate to UV coordinate
+ ray_vec = view3d_utils.region_2d_to_vector_3d(region,
+ space.region_3d, mco)
+ ray_vec.normalize()
+ ray_orig = view3d_utils.region_2d_to_origin_3d(region,
+ space.region_3d,
+ mco)
+ ray_tgt = ray_orig + ray_vec * 1000000.0
+ mwi = world_mat.inverted()
+ ray_orig_obj = mwi * ray_orig
+ ray_tgt_obj = mwi * ray_tgt
+ ray_dir_obj = ray_tgt_obj - ray_orig_obj
+ ray_dir_obj.normalize()
+ tree = BVHTree.FromBMesh(bm)
+ loc, _, fidx, _ = tree.ray_cast(ray_orig_obj, ray_dir_obj)
+ if not loc:
+ return
+ loops = [l for l in bm.faces[fidx].loops]
+ uvs = [Vector((l[uv_layer].uv.x, l[uv_layer].uv.y, 0.0))
+ for l in loops]
+ target_uv = barycentric_transform(
+ loc, loops[0].vert.co, loops[1].vert.co, loops[2].vert.co,
+ uvs[0], uvs[1], uvs[2])
+ target_uv = Vector((target_uv.x, target_uv.y))
+
+ # move to target UV coordinate
+ for info in loop_info:
+ l = bm.faces[info["face_idx"]].loops[info["loop_idx"]]
+ if sc.muv_uvsculpt_pinch_invert:
+ diff_uv = (l[uv_layer].uv - target_uv) * info["strength"]
+ else:
+ diff_uv = (target_uv - l[uv_layer].uv) * info["strength"]
+ l[uv_layer].uv = l[uv_layer].uv + diff_uv / 10.0
+
+ elif sc.muv_uvsculpt_tools == 'RELAX':
+ _, region, space = common.get_space('VIEW_3D', 'WINDOW', 'VIEW_3D')
+
+ # get vertex and loop relation
+ vert_db = {}
+ for f in bm.faces:
+ for l in f.loops:
+ if l.vert in vert_db:
+ vert_db[l.vert]["loops"].append(l)
+ else:
+ vert_db[l.vert] = {"loops": [l]}
+
+ # get relaxation information
+ for k in vert_db.keys():
+ d = vert_db[k]
+ d["uv_sum"] = Vector((0.0, 0.0))
+ d["uv_count"] = 0
+
+ for l in d["loops"]:
+ ln = l.link_loop_next
+ lp = l.link_loop_prev
+ d["uv_sum"] = d["uv_sum"] + ln[uv_layer].uv
+ d["uv_sum"] = d["uv_sum"] + lp[uv_layer].uv
+ d["uv_count"] = d["uv_count"] + 2
+ d["uv_p"] = d["uv_sum"] / d["uv_count"]
+ d["uv_b"] = d["uv_p"] - d["loops"][0][uv_layer].uv
+ for k in vert_db.keys():
+ d = vert_db[k]
+ d["uv_sum_b"] = Vector((0.0, 0.0))
+ for l in d["loops"]:
+ ln = l.link_loop_next
+ lp = l.link_loop_prev
+ dn = vert_db[ln.vert]
+ dp = vert_db[lp.vert]
+ d["uv_sum_b"] = d["uv_sum_b"] + dn["uv_b"] + dp["uv_b"]
+
+ # apply
+ for f in bm.faces:
+ if not f.select:
+ continue
+ for i, l in enumerate(f.loops):
+ loc_2d = view3d_utils.location_3d_to_region_2d(
+ region, space.region_3d, world_mat * l.vert.co)
+ diff = loc_2d - self.__initial_mco
+ if diff.length >= sc.muv_uvsculpt_radius:
+ continue
+ db = vert_db[l.vert]
+ strength = self.__get_strength(diff.length,
+ sc.muv_uvsculpt_radius,
+ sc.muv_uvsculpt_strength)
+
+ base = (1.0 - strength) * l[uv_layer].uv
+ if sc.muv_uvsculpt_relax_method == 'HC':
+ t = 0.5 * (db["uv_b"] + db["uv_sum_b"] / d["uv_count"])
+ diff = strength * (db["uv_p"] - t)
+ target_uv = base + diff
+ elif sc.muv_uvsculpt_relax_method == 'LAPLACIAN':
+ diff = strength * db["uv_p"]
+ target_uv = base + diff
+ else:
+ continue
+
+ l[uv_layer].uv = target_uv
+
+ bmesh.update_edit_mesh(obj.data)
+
+ def __stroke_exit(self, context, _):
+ sc = context.scene
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ uv_layer = bm.loops.layers.uv.verify()
+ mco = self.current_mco
+
+ if sc.muv_uvsculpt_tools == 'GRAB':
+ for info in self.__loop_info:
+ diff_uv = (mco - self.__initial_mco) * info["strength"]
+ l = bm.faces[info["face_idx"]].loops[info["loop_idx"]]
+ l[uv_layer].uv = info["initial_uv"] + diff_uv / 100.0
+
+ bmesh.update_edit_mesh(obj.data)
+
+ def modal(self, context, event):
+ props = context.scene.muv_props.uvsculpt
+
+ if context.area:
+ context.area.tag_redraw()
+
+ if not props.running:
+ if self.__timer is not None:
+ MUV_UVSculptRenderer.handle_remove()
+ context.window_manager.event_timer_remove(self.__timer)
+ self.__timer = None
+ return {'FINISHED'}
+
+ self.current_mco = Vector((event.mouse_region_x, event.mouse_region_y))
+ area, _, _ = common.get_space('VIEW_3D', 'WINDOW', 'VIEW_3D')
+
+ if self.current_mco.x < 0 or self.current_mco.x > area.width or \
+ self.current_mco.y < 0 or self.current_mco.y > area.height:
+ return {'PASS_THROUGH'}
+
+ if event.type == 'LEFTMOUSE':
+ if event.value == 'PRESS':
+ if not self.__stroking:
+ self.__stroke_init(context, event)
+ self.__stroking = True
+ elif event.value == 'RELEASE':
+ if self.__stroking:
+ self.__stroke_exit(context, event)
+ self.__stroking = False
+ elif event.type == 'MOUSEMOVE':
+ if self.__stroking:
+ self.__stroke_apply(context, event)
+ elif event.type == 'TIMER':
+ if self.__stroking:
+ self.__stroke_apply(context, event)
+
+ return {'RUNNING_MODAL'}
+
+ def invoke(self, context, _):
+ props = context.scene.muv_props.uvsculpt
+
+ if context.area:
+ context.area.tag_redraw()
+
+ if props.running:
+ props.running = False
+ return {'FINISHED'}
+
+ props.running = True
+ if self.__timer is None:
+ self.__timer = context.window_manager.event_timer_add(
+ 0.1, context.window)
+ context.window_manager.modal_handler_add(self)
+ MUV_UVSculptRenderer.handle_add(self, context)
+
+ return {'RUNNING_MODAL'}
diff --git a/uv_magic_uv/muv_uvw_ops.py b/uv_magic_uv/op/uvw.py
index eb366e97..10202677 100644
--- a/uv_magic_uv/muv_uvw_ops.py
+++ b/uv_magic_uv/op/uvw.py
@@ -20,9 +20,8 @@
__author__ = "Alexander Milovsky, Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
-
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
from math import sin, cos, pi
@@ -30,11 +29,12 @@ import bpy
import bmesh
from bpy.props import (
FloatProperty,
- FloatVectorProperty
+ FloatVectorProperty,
+ BoolProperty
)
from mathutils import Vector
-from . import muv_common
+from .. import common
class MUV_UVWBoxMap(bpy.types.Operator):
@@ -62,6 +62,11 @@ class MUV_UVWBoxMap(bpy.types.Operator):
default=1.0,
precision=4
)
+ assign_uvmap = BoolProperty(
+ name="Assign UVMap",
+ description="Assign UVMap when no UVmaps are available",
+ default=True
+ )
@classmethod
def poll(cls, context):
@@ -71,15 +76,17 @@ class MUV_UVWBoxMap(bpy.types.Operator):
def execute(self, context):
obj = context.active_object
bm = bmesh.from_edit_mesh(obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
+ if common.check_version(2, 73, 0) >= 0:
bm.faces.ensure_lookup_table()
# get UV layer
if not bm.loops.layers.uv:
- self.report(
- {'WARNING'}, "Object must have more than one UV map")
- return {'CANCELLED'}
-
+ if self.assign_uvmap:
+ bm.loops.layers.uv.new()
+ else:
+ self.report(
+ {'WARNING'}, "Object must have more than one UV map")
+ return {'CANCELLED'}
uv_layer = bm.loops.layers.uv.verify()
scale = 1.0 / self.size
@@ -168,6 +175,11 @@ class MUV_UVWBestPlanerMap(bpy.types.Operator):
default=1.0,
precision=4
)
+ assign_uvmap = BoolProperty(
+ name="Assign UVMap",
+ description="Assign UVMap when no UVmaps are available",
+ default=True
+ )
@classmethod
def poll(cls, context):
@@ -177,14 +189,17 @@ class MUV_UVWBestPlanerMap(bpy.types.Operator):
def execute(self, context):
obj = context.active_object
bm = bmesh.from_edit_mesh(obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
+ if common.check_version(2, 73, 0) >= 0:
bm.faces.ensure_lookup_table()
# get UV layer
if not bm.loops.layers.uv:
- self.report(
- {'WARNING'}, "Object must have more than one UV map")
- return {'CANCELLED'}
+ if self.assign_uvmap:
+ bm.loops.layers.uv.new()
+ else:
+ self.report(
+ {'WARNING'}, "Object must have more than one UV map")
+ return {'CANCELLED'}
uv_layer = bm.loops.layers.uv.verify()
diff --git a/uv_magic_uv/muv_wsuv_ops.py b/uv_magic_uv/op/world_scale_uv.py
index 4ee8b4f9..e256fbac 100644
--- a/uv_magic_uv/muv_wsuv_ops.py
+++ b/uv_magic_uv/op/world_scale_uv.py
@@ -20,43 +20,32 @@
__author__ = "McBuff, Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
+from math import sqrt
import bpy
import bmesh
from mathutils import Vector
-from bpy.props import (
- FloatProperty,
- BoolProperty,
- EnumProperty
-)
-from . import muv_common
+from bpy.props import EnumProperty
+from .. import common
-def calc_edge_scale(uv_layer, loop0, loop1):
- v0 = loop0.vert.co
- v1 = loop1.vert.co
- uv0 = loop0[uv_layer].uv.copy()
- uv1 = loop1[uv_layer].uv.copy()
- dv = v1 - v0
- duv = uv1 - uv0
+def measure_wsuv_info(obj):
+ mesh_area = common.measure_mesh_area(obj)
+ uv_area = common.measure_uv_area(obj)
- scale = 0.0
- if dv.magnitude > 0.00000001:
- scale = duv.magnitude / dv.magnitude
+ if not uv_area:
+ return None, None, None
- return scale
+ if mesh_area == 0.0:
+ density = 0.0
+ else:
+ density = sqrt(uv_area) / sqrt(mesh_area)
-
-def calc_face_scale(uv_layer, face):
- es = 0.0
- for i, l in enumerate(face.loops[1:]):
- es = es + calc_edge_scale(uv_layer, face.loops[i], l)
-
- return es
+ return uv_area, mesh_area, density
class MUV_WSUVMeasure(bpy.types.Operator):
@@ -70,30 +59,22 @@ class MUV_WSUVMeasure(bpy.types.Operator):
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
- props = context.scene.muv_props.wsuv
- obj = bpy.context.active_object
- bm = bmesh.from_edit_mesh(obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
- bm.verts.ensure_lookup_table()
- bm.edges.ensure_lookup_table()
- bm.faces.ensure_lookup_table()
+ sc = context.scene
+ obj = context.active_object
- if not bm.loops.layers.uv:
- self.report({'WARNING'}, "Object must have more than one UV map")
+ uv_area, mesh_area, density = measure_wsuv_info(obj)
+ if not uv_area:
+ self.report({'WARNING'},
+ "Object must have more than one UV map and texture")
return {'CANCELLED'}
- uv_layer = bm.loops.layers.uv.verify()
- sel_faces = [f for f in bm.faces if f.select]
-
- # measure average face size
- scale = 0.0
- for f in sel_faces:
- scale = scale + calc_face_scale(uv_layer, f)
+ sc.muv_wsuv_src_uv_area = uv_area
+ sc.muv_wsuv_src_mesh_area = mesh_area
+ sc.muv_wsuv_src_density = density
- props.ref_scale = scale / len(sel_faces)
-
- self.report(
- {'INFO'}, "Average face size: {0}".format(props.ref_scale))
+ self.report({'INFO'},
+ "UV Area: {0}, Mesh Area: {1}, Texel Density: {2}"
+ .format(uv_area, mesh_area, density))
return {'FINISHED'}
@@ -108,16 +89,6 @@ class MUV_WSUVApply(bpy.types.Operator):
bl_description = "Apply scaled UV based on scale calculation"
bl_options = {'REGISTER', 'UNDO'}
- proportional_scaling = BoolProperty(
- name="Proportional Scaling",
- default=True
- )
- scaling_factor = FloatProperty(
- name="Scaling Factor",
- default=1.0,
- max=1000.0,
- min=0.00001
- )
origin = EnumProperty(
name="Origin",
description="Aspect Origin",
@@ -139,43 +110,38 @@ class MUV_WSUVApply(bpy.types.Operator):
def draw(self, _):
layout = self.layout
- row = layout.row()
- row.prop(self, "proportional_scaling")
- row = layout.row()
- row.prop(self, "scaling_factor")
- if self.proportional_scaling:
- row.enabled = False
+ layout.prop(self, "origin")
def execute(self, context):
- props = context.scene.muv_props.wsuv
- obj = bpy.context.active_object
+ sc = context.scene
+ obj = context.active_object
bm = bmesh.from_edit_mesh(obj.data)
- if muv_common.check_version(2, 73, 0) >= 0:
+ if common.check_version(2, 73, 0) >= 0:
bm.verts.ensure_lookup_table()
bm.edges.ensure_lookup_table()
bm.faces.ensure_lookup_table()
- if not bm.loops.layers.uv:
- self.report(
- {'WARNING'}, "Object must have more than one UV map")
- return {'CANCELLED'}
- uv_layer = bm.loops.layers.uv.verify()
-
sel_faces = [f for f in bm.faces if f.select]
- # measure average face size
- scale = 0.0
- for f in sel_faces:
- scale = scale + calc_face_scale(uv_layer, f)
- scale = scale / len(sel_faces)
+ uv_area, mesh_area, density = measure_wsuv_info(obj)
+ if not uv_area:
+ self.report({'WARNING'},
+ "Object must have more than one UV map and texture")
+ return {'CANCELLED'}
- self.report(
- {'INFO'}, "Average face size: {0}".format(scale))
+ uv_layer = bm.loops.layers.uv.verify()
- if self.proportional_scaling:
- factor = props.ref_scale / scale
- else:
- factor = self.scaling_factor
+ if sc.muv_wsuv_mode == 'PROPORTIONAL':
+ tgt_density = sc.muv_wsuv_src_density * sqrt(mesh_area) / \
+ sqrt(sc.muv_wsuv_src_mesh_area)
+ elif sc.muv_wsuv_mode == 'SCALING':
+ tgt_density = sc.muv_wsuv_src_density * sc.muv_wsuv_scaling_factor
+ elif sc.muv_wsuv_mode == 'USER':
+ tgt_density = sc.muv_wsuv_tgt_density
+ elif sc.muv_wsuv_mode == 'CONSTANT':
+ tgt_density = sc.muv_wsuv_src_density
+
+ factor = tgt_density / density
# calculate origin
if self.origin == 'CENTER':
diff --git a/uv_magic_uv/preferences.py b/uv_magic_uv/preferences.py
new file mode 100644
index 00000000..d8cdf86b
--- /dev/null
+++ b/uv_magic_uv/preferences.py
@@ -0,0 +1,216 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
+
+from bpy.props import (
+ FloatProperty,
+ FloatVectorProperty,
+)
+from bpy.types import AddonPreferences
+
+
+class MUV_Preferences(AddonPreferences):
+ """Preferences class: Preferences for this add-on"""
+
+ bl_idname = __package__
+
+ # for UV Sculpt
+ uvsculpt_brush_color = FloatVectorProperty(
+ name="Color",
+ description="Color",
+ default=(1.0, 0.4, 0.4, 1.0),
+ min=0.0,
+ max=1.0,
+ size=4,
+ subtype='COLOR'
+ )
+
+ # for Overlapped UV
+ uvinsp_overlapped_color = FloatVectorProperty(
+ name="Color",
+ description="Color",
+ default=(0.0, 0.0, 1.0, 0.3),
+ min=0.0,
+ max=1.0,
+ size=4,
+ subtype='COLOR'
+ )
+
+ # for Flipped UV
+ uvinsp_flipped_color = FloatVectorProperty(
+ name="Color",
+ description="Color",
+ default=(1.0, 0.0, 0.0, 0.3),
+ min=0.0,
+ max=1.0,
+ size=4,
+ subtype='COLOR'
+ )
+
+ # for Texture Projection
+ texproj_canvas_padding = FloatVectorProperty(
+ name="Canvas Padding",
+ description="Canvas Padding",
+ size=2,
+ max=50.0,
+ min=0.0,
+ default=(20.0, 20.0))
+
+ # for UV Bounding Box
+ uvbb_cp_size = FloatProperty(
+ name="Size",
+ description="Control Point Size",
+ default=6.0,
+ min=3.0,
+ max=100.0)
+ uvbb_cp_react_size = FloatProperty(
+ name="React Size",
+ description="Size event fired",
+ default=10.0,
+ min=3.0,
+ max=100.0)
+
+ def draw(self, _):
+ layout = self.layout
+
+ layout.label("[Configuration]")
+
+ layout.label("UV Sculpt:")
+ sp = layout.split(percentage=0.05)
+ col = sp.column() # spacer
+ sp = sp.split(percentage=0.3)
+ col = sp.column()
+ col.label("Brush Color:")
+ col.prop(self, "uvsculpt_brush_color", text="")
+
+ layout.separator()
+
+ layout.label("UV Inspection:")
+ sp = layout.split(percentage=0.05)
+ col = sp.column() # spacer
+ sp = sp.split(percentage=0.3)
+ col = sp.column()
+ col.label("Overlapped UV Color:")
+ col.prop(self, "uvinsp_overlapped_color", text="")
+ sp = sp.split(percentage=0.45)
+ col = sp.column()
+ col.label("Flipped UV Color:")
+ col.prop(self, "uvinsp_flipped_color", text="")
+
+ layout.separator()
+
+ layout.label("Texture Projection:")
+ sp = layout.split(percentage=0.05)
+ col = sp.column() # spacer
+ sp = sp.split(percentage=0.3)
+ col = sp.column()
+ col.prop(self, "texproj_canvas_padding")
+
+ layout.separator()
+
+ layout.label("UV Bounding Box:")
+ sp = layout.split(percentage=0.05)
+ col = sp.column() # spacer
+ sp = sp.split(percentage=0.3)
+ col = sp.column()
+ col.label("Control Point:")
+ col.prop(self, "uvbb_cp_size")
+ col.prop(self, "uvbb_cp_react_size")
+
+ layout.label("--------------------------------------")
+
+ layout.label("[Description]")
+ column = layout.column(align=True)
+ column.label("Magic UV is composed of many UV editing features.")
+ column.label("See tutorial page if you are new to this add-on.")
+ column.label("https://github.com/nutti/Magic-UV/wiki/Tutorial")
+
+ layout.label("--------------------------------------")
+
+ layout.label("[Location]")
+
+ row = layout.row(align=True)
+ sp = row.split(percentage=0.5)
+ sp.label("3D View > Tool shelf > Copy/Paste UV (Object mode)")
+ sp = sp.split(percentage=1.0)
+ col = sp.column(align=True)
+ col.label("Copy/Paste UV (Among objects)")
+
+ row = layout.row(align=True)
+ sp = row.split(percentage=0.5)
+ sp.label("3D View > Tool shelf > Copy/Paste UV (Edit mode)")
+ sp = sp.split(percentage=1.0)
+ col = sp.column(align=True)
+ col.label("Copy/Paste UV (Among faces in 3D View)")
+ col.label("Transfer UV")
+
+ row = layout.row(align=True)
+ sp = row.split(percentage=0.5)
+ sp.label("3D View > Tool shelf > UV Manipulation (Edit mode)")
+ sp = sp.split(percentage=1.0)
+ col = sp.column(align=True)
+ col.label("Flip/Rotate UV")
+ col.label("Mirror UV")
+ col.label("Move UV")
+ col.label("World Scale UV")
+ col.label("Preserve UV Aspect")
+ col.label("Texture Lock")
+ col.label("Texture Wrap")
+ col.label("UV Sculpt")
+
+ row = layout.row(align=True)
+ sp = row.split(percentage=0.5)
+ sp.label("3D View > Tool shelf > UV Manipulation (Edit mode)")
+ sp = sp.split(percentage=1.0)
+ col = sp.column(align=True)
+ col.label("Unwrap Constraint")
+ col.label("Texture Projection")
+ col.label("UVW")
+
+ row = layout.row(align=True)
+ sp = row.split(percentage=0.5)
+ sp.label("UV/Image Editor > Tool shelf > Copy/Paste UV")
+ sp = sp.split(percentage=1.0)
+ col = sp.column(align=True)
+ col.label("Copy/Paste UV (Among faces in UV/Image Editor)")
+
+ row = layout.row(align=True)
+ sp = row.split(percentage=0.5)
+ sp.label("UV/Image Editor > Tool shelf > UV Manipulation")
+ sp = sp.split(percentage=1.0)
+ col = sp.column(align=True)
+ col.label("Align UV")
+ col.label("Smooth UV")
+ col.label("Select UV")
+ col.label("Pack UV (Extension)")
+
+ row = layout.row(align=True)
+ sp = row.split(percentage=0.5)
+ sp.label("UV/Image Editor > Tool shelf > Editor Enhancement")
+ sp = sp.split(percentage=1.0)
+ col = sp.column(align=True)
+ col.label("Align UV Cursor")
+ col.label("UV Cursor Location")
+ col.label("UV Bounding Box")
+ col.label("UV Inspection")
diff --git a/uv_magic_uv/properites.py b/uv_magic_uv/properites.py
new file mode 100644
index 00000000..3b61fcfc
--- /dev/null
+++ b/uv_magic_uv/properites.py
@@ -0,0 +1,765 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
+
+import bpy
+from bpy.props import (
+ FloatProperty,
+ EnumProperty,
+ BoolProperty,
+ FloatVectorProperty,
+ IntProperty
+)
+from mathutils import Vector
+
+from . import common
+
+
+def get_loaded_texture_name(_, __):
+ items = [(key, key, "") for key in bpy.data.images.keys()]
+ items.append(("None", "None", ""))
+ return items
+
+
+# Properties used in this add-on.
+class MUV_Properties():
+ cpuv = None
+ cpuv_obj = None
+ cpuv_selseq = None
+ transuv = None
+ uvbb = None
+ texlock = None
+ texproj = None
+ texwrap = None
+ mvuv = None
+ uvinsp = None
+ uvsculpt = None
+
+ def __init__(self):
+ self.cpuv = MUV_CPUVProps()
+ self.cpuv_obj = MUV_CPUVProps()
+ self.cpuv_selseq = MUV_CPUVSelSeqProps()
+ self.transuv = MUV_TransUVProps()
+ self.uvbb = MUV_UVBBProps()
+ self.texlock = MUV_TexLockProps()
+ self.texproj = MUV_TexProjProps()
+ self.texwrap = MUV_TexWrapProps()
+ self.mvuv = MUV_MVUVProps()
+ self.uvinsp = MUV_UVInspProps()
+ self.uvsculpt = MUV_UVSculptProps()
+
+
+class MUV_CPUVProps():
+ src_uvs = []
+ src_pin_uvs = []
+ src_seams = []
+
+
+class MUV_CPUVSelSeqProps():
+ src_uvs = []
+ src_pin_uvs = []
+ src_seams = []
+
+
+class MUV_TransUVProps():
+ topology_copied = []
+
+
+class MUV_TexProjProps():
+ running = False
+
+
+class MUV_UVBBProps():
+ uv_info_ini = []
+ ctrl_points_ini = []
+ ctrl_points = []
+ running = False
+
+
+class MUV_TexLockProps():
+ verts_orig = None
+ intr_verts_orig = None
+ intr_running = False
+
+
+class MUV_TexWrapProps():
+ ref_face_index = -1
+ ref_obj = None
+
+
+class MUV_MVUVProps():
+ running = False
+
+
+class MUV_UVInspProps():
+ display_running = False
+ overlapped_info = []
+ flipped_info = []
+
+
+class MUV_UVSculptProps():
+ running = False
+
+
+def init_props(scene):
+ scene.muv_props = MUV_Properties()
+
+ # UV Sculpt
+ scene.muv_uvsculpt_enabled = BoolProperty(
+ name="UV Sculpt",
+ description="UV Sculpt is enabled",
+ default=False
+ )
+ scene.muv_uvsculpt_radius = IntProperty(
+ name="Radius",
+ description="Radius of the brush",
+ min=1,
+ max=500,
+ default=30
+ )
+ scene.muv_uvsculpt_strength = FloatProperty(
+ name="Strength",
+ description="How powerful the effect of the brush when applied",
+ min=0.0,
+ max=1.0,
+ default=0.03,
+ )
+ scene.muv_uvsculpt_tools = EnumProperty(
+ name="Tools",
+ description="Select Tools for the UV sculpt brushes",
+ items=[
+ ('GRAB', "Grab", "Grab UVs"),
+ ('RELAX', "Relax", "Relax UVs"),
+ ('PINCH', "Pinch", "Pinch UVs")
+ ],
+ default='GRAB'
+ )
+ scene.muv_uvsculpt_show_brush = BoolProperty(
+ name="Show Brush",
+ description="Show Brush",
+ default=True
+ )
+ scene.muv_uvsculpt_pinch_invert = BoolProperty(
+ name="Invert",
+ description="Pinch UV to invert direction",
+ default=False
+ )
+ scene.muv_uvsculpt_relax_method = EnumProperty(
+ name="Method",
+ description="Algorithm used for relaxation",
+ items=[
+ ('HC', "HC", "Use HC method for relaxation"),
+ ('LAPLACIAN', "Laplacian", "Use laplacian method for relaxation")
+ ],
+ default='HC'
+ )
+
+ # Texture Wrap
+ scene.muv_texwrap_enabled = BoolProperty(
+ name="Texture Wrap",
+ description="Texture Wrap is enabled",
+ default=False
+ )
+ scene.muv_texwrap_set_and_refer = BoolProperty(
+ name="Set and Refer",
+ description="Refer and set UV",
+ default=True
+ )
+ scene.muv_texwrap_selseq = BoolProperty(
+ name="Selection Sequence",
+ description="Set UV sequentially",
+ default=False
+ )
+
+ # UV inspection
+ scene.muv_seluv_enabled = BoolProperty(
+ name="Select UV Enabled",
+ description="Select UV is enabled",
+ default=False
+ )
+ scene.muv_uvinsp_enabled = BoolProperty(
+ name="UV Inspection Enabled",
+ description="UV Inspection is enabled",
+ default=False
+ )
+ scene.muv_uvinsp_show_overlapped = BoolProperty(
+ name="Overlapped",
+ description="Show overlapped UVs",
+ default=False
+ )
+ scene.muv_uvinsp_show_flipped = BoolProperty(
+ name="Flipped",
+ description="Show flipped UVs",
+ default=False
+ )
+ scene.muv_uvinsp_show_mode = EnumProperty(
+ name="Mode",
+ description="Show mode",
+ items=[
+ ('PART', "Part", "Show only overlapped/flipped part"),
+ ('FACE', "Face", "Show overlapped/flipped face")
+ ],
+ default='PART'
+ )
+
+ # Align UV
+ scene.muv_auv_enabled = BoolProperty(
+ name="Aline UV Enabled",
+ description="Align UV is enabled",
+ default=False
+ )
+ scene.muv_auv_transmission = BoolProperty(
+ name="Transmission",
+ description="Align linked UVs",
+ default=False
+ )
+ scene.muv_auv_select = BoolProperty(
+ name="Select",
+ description="Select UVs which are aligned",
+ default=False
+ )
+ scene.muv_auv_vertical = BoolProperty(
+ name="Vert-Infl (Vertical)",
+ description="Align vertical direction influenced "
+ "by mesh vertex proportion",
+ default=False
+ )
+ scene.muv_auv_horizontal = BoolProperty(
+ name="Vert-Infl (Horizontal)",
+ description="Align horizontal direction influenced "
+ "by mesh vertex proportion",
+ default=False
+ )
+ scene.muv_auv_location = EnumProperty(
+ name="Location",
+ description="Align location",
+ items=[
+ ('LEFT_TOP', "Left/Top", "Align to Left or Top"),
+ ('MIDDLE', "Middle", "Align to middle"),
+ ('RIGHT_BOTTOM', "Right/Bottom", "Align to Right or Bottom")
+ ],
+ default='MIDDLE'
+ )
+
+ # Smooth UV
+ scene.muv_smuv_enabled = BoolProperty(
+ name="Smooth UV Enabled",
+ description="Smooth UV is enabled",
+ default=False
+ )
+ scene.muv_smuv_transmission = BoolProperty(
+ name="Transmission",
+ description="Smooth linked UVs",
+ default=False
+ )
+ scene.muv_smuv_mesh_infl = FloatProperty(
+ name="Mesh Influence",
+ description="Influence rate of mesh vertex",
+ min=0.0,
+ max=1.0,
+ default=0.0
+ )
+ scene.muv_smuv_select = BoolProperty(
+ name="Select",
+ description="Select UVs which are smoothed",
+ default=False
+ )
+
+ # UV Bounding Box
+ scene.muv_uvbb_enabled = BoolProperty(
+ name="UV Bounding Box Enabled",
+ description="UV Bounding Box is enabled",
+ default=False
+ )
+ scene.muv_uvbb_uniform_scaling = BoolProperty(
+ name="Uniform Scaling",
+ description="Enable Uniform Scaling",
+ default=False
+ )
+ scene.muv_uvbb_boundary = EnumProperty(
+ name="Boundary",
+ description="Boundary",
+ default='UV_SEL',
+ items=[
+ ('UV', "UV", "Boundary is decided by UV"),
+ ('UV_SEL', "UV (Selected)", "Boundary is decided by Selected UV")
+ ]
+ )
+
+ # Pack UV
+ scene.muv_packuv_enabled = BoolProperty(
+ name="Pack UV Enabled",
+ description="Pack UV is enabled",
+ default=False
+ )
+ scene.muv_packuv_allowable_center_deviation = FloatVectorProperty(
+ name="Allowable Center Deviation",
+ description="Allowable center deviation to judge same UV island",
+ min=0.000001,
+ max=0.1,
+ default=(0.001, 0.001),
+ size=2
+ )
+ scene.muv_packuv_allowable_size_deviation = FloatVectorProperty(
+ name="Allowable Size Deviation",
+ description="Allowable sizse deviation to judge same UV island",
+ min=0.000001,
+ max=0.1,
+ default=(0.001, 0.001),
+ size=2
+ )
+
+ # Move UV
+ scene.muv_mvuv_enabled = BoolProperty(
+ name="Move UV Enabled",
+ description="Move UV is enabled",
+ default=False
+ )
+
+ # UVW
+ scene.muv_uvw_enabled = BoolProperty(
+ name="UVW Enabled",
+ description="UVW is enabled",
+ default=False
+ )
+ scene.muv_uvw_assign_uvmap = BoolProperty(
+ name="Assign UVMap",
+ description="Assign UVMap when no UVmaps are available",
+ default=True
+ )
+
+ # Texture Projection
+ scene.muv_texproj_enabled = BoolProperty(
+ name="Texture Projection Enabled",
+ description="Texture Projection is enabled",
+ default=False
+ )
+ scene.muv_texproj_tex_magnitude = FloatProperty(
+ name="Magnitude",
+ description="Texture Magnitude",
+ default=0.5,
+ min=0.0,
+ max=100.0
+ )
+ scene.muv_texproj_tex_image = EnumProperty(
+ name="Image",
+ description="Texture Image",
+ items=get_loaded_texture_name
+ )
+ scene.muv_texproj_tex_transparency = FloatProperty(
+ name="Transparency",
+ description="Texture Transparency",
+ default=0.2,
+ min=0.0,
+ max=1.0
+ )
+ scene.muv_texproj_adjust_window = BoolProperty(
+ name="Adjust Window",
+ description="Size of renderered texture is fitted to window",
+ default=True
+ )
+ scene.muv_texproj_apply_tex_aspect = BoolProperty(
+ name="Texture Aspect Ratio",
+ description="Apply Texture Aspect ratio to displayed texture",
+ default=True
+ )
+ scene.muv_texproj_assign_uvmap = BoolProperty(
+ name="Assign UVMap",
+ description="Assign UVMap when no UVmaps are available",
+ default=True
+ )
+
+ # Texture Lock
+ scene.muv_texlock_enabled = BoolProperty(
+ name="Texture Lock Enabled",
+ description="Texture Lock is enabled",
+ default=False
+ )
+ scene.muv_texlock_connect = BoolProperty(
+ name="Connect UV",
+ default=True
+ )
+
+ # World Scale UV
+ scene.muv_wsuv_enabled = BoolProperty(
+ name="World Scale UV Enabled",
+ description="World Scale UV is enabled",
+ default=False
+ )
+ scene.muv_wsuv_src_mesh_area = FloatProperty(
+ name="Mesh Area",
+ description="Source Mesh Area",
+ default=0.0,
+ min=0.0
+ )
+ scene.muv_wsuv_src_uv_area = FloatProperty(
+ name="UV Area",
+ description="Source UV Area",
+ default=0.0,
+ min=0.0
+ )
+ scene.muv_wsuv_src_density = FloatProperty(
+ name="Density",
+ description="Source Texel Density",
+ default=0.0,
+ min=0.0
+ )
+ scene.muv_wsuv_tgt_density = FloatProperty(
+ name="Density",
+ description="Target Texel Density",
+ default=0.0,
+ min=0.0
+ )
+ scene.muv_wsuv_mode = EnumProperty(
+ name="Mode",
+ description="Density calculation mode",
+ items=[
+ ('PROPORTIONAL', 'Proportional', 'Scale proportionally by mesh'),
+ ('SCALING', 'Scaling', 'Specify scale factor'),
+ ('USER', 'User', 'Specify density'),
+ ('CONSTANT', 'Constant', 'Constant density')
+ ],
+ default='CONSTANT'
+ )
+ scene.muv_wsuv_scaling_factor = FloatProperty(
+ name="Scaling Factor",
+ default=1.0,
+ max=1000.0,
+ min=0.00001
+ )
+ scene.muv_wsuv_origin = EnumProperty(
+ name="Origin",
+ description="Aspect Origin",
+ items=[
+ ('CENTER', 'Center', 'Center'),
+ ('LEFT_TOP', 'Left Top', 'Left Bottom'),
+ ('LEFT_CENTER', 'Left Center', 'Left Center'),
+ ('LEFT_BOTTOM', 'Left Bottom', 'Left Bottom'),
+ ('CENTER_TOP', 'Center Top', 'Center Top'),
+ ('CENTER_BOTTOM', 'Center Bottom', 'Center Bottom'),
+ ('RIGHT_TOP', 'Right Top', 'Right Top'),
+ ('RIGHT_CENTER', 'Right Center', 'Right Center'),
+ ('RIGHT_BOTTOM', 'Right Bottom', 'Right Bottom')
+
+ ],
+ default='CENTER'
+ )
+
+ # Unwrap Constraint
+ scene.muv_unwrapconst_enabled = BoolProperty(
+ name="Unwrap Constraint Enabled",
+ description="Unwrap Constraint is enabled",
+ default=False
+ )
+ scene.muv_unwrapconst_u_const = BoolProperty(
+ name="U-Constraint",
+ description="Keep UV U-axis coordinate",
+ default=False
+ )
+ scene.muv_unwrapconst_v_const = BoolProperty(
+ name="V-Constraint",
+ description="Keep UV V-axis coordinate",
+ default=False
+ )
+
+ # Preserve UV Aspect
+ scene.muv_preserve_uv_enabled = BoolProperty(
+ name="Preserve UV Aspect Enabled",
+ description="Preserve UV Aspect is enabled",
+ default=False
+ )
+ scene.muv_preserve_uv_tex_image = EnumProperty(
+ name="Image",
+ description="Texture Image",
+ items=get_loaded_texture_name
+ )
+ scene.muv_preserve_uv_origin = EnumProperty(
+ name="Origin",
+ description="Aspect Origin",
+ items=[
+ ('CENTER', 'Center', 'Center'),
+ ('LEFT_TOP', 'Left Top', 'Left Bottom'),
+ ('LEFT_CENTER', 'Left Center', 'Left Center'),
+ ('LEFT_BOTTOM', 'Left Bottom', 'Left Bottom'),
+ ('CENTER_TOP', 'Center Top', 'Center Top'),
+ ('CENTER_BOTTOM', 'Center Bottom', 'Center Bottom'),
+ ('RIGHT_TOP', 'Right Top', 'Right Top'),
+ ('RIGHT_CENTER', 'Right Center', 'Right Center'),
+ ('RIGHT_BOTTOM', 'Right Bottom', 'Right Bottom')
+
+ ],
+ default="CENTER"
+ )
+
+ # Flip/Rotate UV
+ scene.muv_fliprot_enabled = BoolProperty(
+ name="Flip/Rotate UV Enabled",
+ description="Flip/Rotate UV is enabled",
+ default=False
+ )
+ scene.muv_fliprot_seams = BoolProperty(
+ name="Seams",
+ description="Seams",
+ default=True
+ )
+
+ # Mirror UV
+ scene.muv_mirroruv_enabled = BoolProperty(
+ name="Mirror UV Enabled",
+ description="Mirror UV is enabled",
+ default=False
+ )
+ scene.muv_mirroruv_axis = EnumProperty(
+ items=[
+ ('X', "X", "Mirror Along X axis"),
+ ('Y', "Y", "Mirror Along Y axis"),
+ ('Z', "Z", "Mirror Along Z axis")
+ ],
+ name="Axis",
+ description="Mirror Axis",
+ default='X'
+ )
+
+ # Copy/Paste UV
+ scene.muv_cpuv_enabled = BoolProperty(
+ name="Copy/Paste UV Enabled",
+ description="Copy/Paste UV is enabled",
+ default=False
+ )
+ scene.muv_cpuv_copy_seams = BoolProperty(
+ name="Copy Seams",
+ description="Copy Seams",
+ default=True
+ )
+ scene.muv_cpuv_mode = EnumProperty(
+ items=[
+ ('DEFAULT', "Default", "Default Mode"),
+ ('SEL_SEQ', "Selection Sequence", "Selection Sequence Mode")
+ ],
+ name="Copy/Paste UV Mode",
+ description="Copy/Paste UV Mode",
+ default='DEFAULT'
+ )
+ scene.muv_cpuv_strategy = EnumProperty(
+ name="Strategy",
+ description="Paste Strategy",
+ items=[
+ ('N_N', 'N:N', 'Number of faces must be equal to source'),
+ ('N_M', 'N:M', 'Number of faces must not be equal to source')
+ ],
+ default='N_M'
+ )
+
+ # Transfer UV
+ scene.muv_transuv_enabled = BoolProperty(
+ name="Transfer UV Enabled",
+ description="Transfer UV is enabled",
+ default=False
+ )
+ scene.muv_transuv_invert_normals = BoolProperty(
+ name="Invert Normals",
+ description="Invert Normals",
+ default=False
+ )
+ scene.muv_transuv_copy_seams = BoolProperty(
+ name="Copy Seams",
+ description="Copy Seams",
+ default=True
+ )
+
+ # Align UV Cursor
+ def auvc_get_cursor_loc(self):
+ area, _, space = common.get_space('IMAGE_EDITOR', 'WINDOW',
+ 'IMAGE_EDITOR')
+ bd_size = common.get_uvimg_editor_board_size(area)
+ loc = space.cursor_location
+ if bd_size[0] < 0.000001:
+ cx = 0.0
+ else:
+ cx = loc[0] / bd_size[0]
+ if bd_size[1] < 0.000001:
+ cy = 0.0
+ else:
+ cy = loc[1] / bd_size[1]
+ self['muv_auvc_cursor_loc'] = Vector((cx, cy))
+ return self.get('muv_auvc_cursor_loc', (0.0, 0.0))
+
+ def auvc_set_cursor_loc(self, value):
+ self['muv_auvc_cursor_loc'] = value
+ area, _, space = common.get_space('IMAGE_EDITOR', 'WINDOW',
+ 'IMAGE_EDITOR')
+ bd_size = common.get_uvimg_editor_board_size(area)
+ cx = bd_size[0] * value[0]
+ cy = bd_size[1] * value[1]
+ space.cursor_location = Vector((cx, cy))
+
+ scene.muv_auvc_enabled = BoolProperty(
+ name="Align UV Cursor Enabled",
+ description="Align UV Cursor is enabled",
+ default=False
+ )
+ scene.muv_auvc_cursor_loc = FloatVectorProperty(
+ name="UV Cursor Location",
+ size=2,
+ precision=4,
+ soft_min=-1.0,
+ soft_max=1.0,
+ step=1,
+ default=(0.000, 0.000),
+ get=auvc_get_cursor_loc,
+ set=auvc_set_cursor_loc
+ )
+ scene.muv_auvc_align_menu = EnumProperty(
+ name="Align Method",
+ description="Align Method",
+ default='TEXTURE',
+ items=[
+ ('TEXTURE', "Texture", "Align to texture"),
+ ('UV', "UV", "Align to UV"),
+ ('UV_SEL', "UV (Selected)", "Align to Selected UV")
+ ]
+ )
+
+ # UV Cursor Location
+ scene.muv_uvcloc_enabled = BoolProperty(
+ name="UV Cursor Location Enabled",
+ description="UV Cursor Location is enabled",
+ default=False
+ )
+
+
+def clear_props(scene):
+ del scene.muv_props
+
+ # UV Sculpt
+ del scene.muv_uvsculpt_enabled
+ del scene.muv_uvsculpt_radius
+ del scene.muv_uvsculpt_strength
+ del scene.muv_uvsculpt_tools
+ del scene.muv_uvsculpt_show_brush
+ del scene.muv_uvsculpt_pinch_invert
+ del scene.muv_uvsculpt_relax_method
+
+ # Texture Wrap
+ del scene.muv_texwrap_enabled
+ del scene.muv_texwrap_set_and_refer
+ del scene.muv_texwrap_selseq
+
+ # UV Inspection
+ del scene.muv_seluv_enabled
+ del scene.muv_uvinsp_enabled
+ del scene.muv_uvinsp_show_overlapped
+ del scene.muv_uvinsp_show_flipped
+ del scene.muv_uvinsp_show_mode
+
+ # Align UV
+ del scene.muv_auv_enabled
+ del scene.muv_auv_transmission
+ del scene.muv_auv_select
+ del scene.muv_auv_vertical
+ del scene.muv_auv_horizontal
+ del scene.muv_auv_location
+
+ # Smooth UV
+ del scene.muv_smuv_enabled
+ del scene.muv_smuv_transmission
+ del scene.muv_smuv_mesh_infl
+ del scene.muv_smuv_select
+
+ # UV Bounding Box
+ del scene.muv_uvbb_enabled
+ del scene.muv_uvbb_uniform_scaling
+ del scene.muv_uvbb_boundary
+
+ # Pack UV
+ del scene.muv_packuv_enabled
+ del scene.muv_packuv_allowable_center_deviation
+ del scene.muv_packuv_allowable_size_deviation
+
+ # Move UV
+ del scene.muv_mvuv_enabled
+
+ # UVW
+ del scene.muv_uvw_enabled
+ del scene.muv_uvw_assign_uvmap
+
+ # Texture Projection
+ del scene.muv_texproj_enabled
+ del scene.muv_texproj_tex_magnitude
+ del scene.muv_texproj_tex_image
+ del scene.muv_texproj_tex_transparency
+ del scene.muv_texproj_adjust_window
+ del scene.muv_texproj_apply_tex_aspect
+ del scene.muv_texproj_assign_uvmap
+
+ # Texture Lock
+ del scene.muv_texlock_enabled
+ del scene.muv_texlock_connect
+
+ # World Scale UV
+ del scene.muv_wsuv_enabled
+ del scene.muv_wsuv_src_mesh_area
+ del scene.muv_wsuv_src_uv_area
+ del scene.muv_wsuv_src_density
+ del scene.muv_wsuv_tgt_density
+ del scene.muv_wsuv_mode
+ del scene.muv_wsuv_scaling_factor
+ del scene.muv_wsuv_origin
+
+ # Unwrap Constraint
+ del scene.muv_unwrapconst_enabled
+ del scene.muv_unwrapconst_u_const
+ del scene.muv_unwrapconst_v_const
+
+ # Preserve UV Aspect
+ del scene.muv_preserve_uv_enabled
+ del scene.muv_preserve_uv_tex_image
+ del scene.muv_preserve_uv_origin
+
+ # Flip/Rotate UV
+ del scene.muv_fliprot_enabled
+ del scene.muv_fliprot_seams
+
+ # Mirror UV
+ del scene.muv_mirroruv_enabled
+ del scene.muv_mirroruv_axis
+
+ # Copy/Paste UV
+ del scene.muv_cpuv_enabled
+ del scene.muv_cpuv_copy_seams
+ del scene.muv_cpuv_mode
+ del scene.muv_cpuv_strategy
+
+ # Transfer UV
+ del scene.muv_transuv_enabled
+ del scene.muv_transuv_invert_normals
+ del scene.muv_transuv_copy_seams
+
+ # Align UV Cursor
+ del scene.muv_auvc_enabled
+ del scene.muv_auvc_cursor_loc
+ del scene.muv_auvc_align_menu
+
+ # UV Cursor Location
+ del scene.muv_uvcloc_enabled
diff --git a/uv_magic_uv/ui/__init__.py b/uv_magic_uv/ui/__init__.py
new file mode 100644
index 00000000..ad56aeb3
--- /dev/null
+++ b/uv_magic_uv/ui/__init__.py
@@ -0,0 +1,44 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
+
+if "bpy" in locals():
+ import importlib
+ importlib.reload(view3d_copy_paste_uv_objectmode)
+ importlib.reload(view3d_copy_paste_uv_editmode)
+ importlib.reload(view3d_uv_manipulation)
+ importlib.reload(view3d_uv_mapping)
+ importlib.reload(uvedit_copy_paste_uv)
+ importlib.reload(uvedit_uv_manipulation)
+ importlib.reload(uvedit_editor_enhance)
+else:
+ from . import view3d_copy_paste_uv_objectmode
+ from . import view3d_copy_paste_uv_editmode
+ from . import view3d_uv_manipulation
+ from . import view3d_uv_mapping
+ from . import uvedit_copy_paste_uv
+ from . import uvedit_uv_manipulation
+ from . import uvedit_editor_enhance
+
+import bpy
diff --git a/uv_magic_uv/ui/uvedit_copy_paste_uv.py b/uv_magic_uv/ui/uvedit_copy_paste_uv.py
new file mode 100644
index 00000000..d87dbef3
--- /dev/null
+++ b/uv_magic_uv/ui/uvedit_copy_paste_uv.py
@@ -0,0 +1,54 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
+
+import bpy
+
+from ..op import copy_paste_uv_uvedit
+
+
+class IMAGE_PT_MUV_CPUV(bpy.types.Panel):
+ """
+ Panel class: Copy/Paste UV on Property Panel on UV/ImageEditor
+ """
+
+ bl_space_type = 'IMAGE_EDITOR'
+ bl_region_type = 'TOOLS'
+ bl_label = "Copy/Paste UV"
+ bl_category = "Magic UV"
+ bl_context = 'mesh_edit'
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw_header(self, _):
+ layout = self.layout
+ layout.label(text="", icon='IMAGE_COL')
+
+ def draw(self, _):
+ layout = self.layout
+
+ row = layout.row(align=True)
+ row.operator(copy_paste_uv_uvedit.MUV_CPUVIECopyUV.bl_idname,
+ text="Copy")
+ row.operator(copy_paste_uv_uvedit.MUV_CPUVIEPasteUV.bl_idname,
+ text="Paste")
diff --git a/uv_magic_uv/ui/uvedit_editor_enhance.py b/uv_magic_uv/ui/uvedit_editor_enhance.py
new file mode 100644
index 00000000..88a2492c
--- /dev/null
+++ b/uv_magic_uv/ui/uvedit_editor_enhance.py
@@ -0,0 +1,136 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
+
+import bpy
+
+from ..op import align_uv_cursor
+from ..op import uv_bounding_box
+from ..op import uv_inspection
+
+
+class IMAGE_PT_MUV_EE(bpy.types.Panel):
+ """
+ Panel class: UV/Image Editor Enhancement
+ """
+
+ bl_space_type = 'IMAGE_EDITOR'
+ bl_region_type = 'TOOLS'
+ bl_label = "Editor Enhancement"
+ bl_category = "Magic UV"
+ bl_context = 'mesh_edit'
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw_header(self, _):
+ layout = self.layout
+ layout.label(text="", icon='IMAGE_COL')
+
+ def draw(self, context):
+ layout = self.layout
+ sc = context.scene
+ props = sc.muv_props
+
+ box = layout.box()
+ box.prop(sc, "muv_auvc_enabled", text="Align UV Cursor")
+ if sc.muv_auvc_enabled:
+ box.prop(sc, "muv_auvc_align_menu", expand=True)
+
+ col = box.column(align=True)
+
+ row = col.row(align=True)
+ ops = row.operator(align_uv_cursor.MUV_AUVCAlignOps.bl_idname,
+ text="Left Top")
+ ops.position = 'LEFT_TOP'
+ ops.base = sc.muv_auvc_align_menu
+ ops = row.operator(align_uv_cursor.MUV_AUVCAlignOps.bl_idname,
+ text="Middle Top")
+ ops.position = 'MIDDLE_TOP'
+ ops.base = sc.muv_auvc_align_menu
+ ops = row.operator(align_uv_cursor.MUV_AUVCAlignOps.bl_idname,
+ text="Right Top")
+ ops.position = 'RIGHT_TOP'
+ ops.base = sc.muv_auvc_align_menu
+
+ row = col.row(align=True)
+ ops = row.operator(align_uv_cursor.MUV_AUVCAlignOps.bl_idname,
+ text="Left Middle")
+ ops.position = 'LEFT_MIDDLE'
+ ops.base = sc.muv_auvc_align_menu
+ ops = row.operator(align_uv_cursor.MUV_AUVCAlignOps.bl_idname,
+ text="Center")
+ ops.position = 'CENTER'
+ ops.base = sc.muv_auvc_align_menu
+ ops = row.operator(align_uv_cursor.MUV_AUVCAlignOps.bl_idname,
+ text="Right Middle")
+ ops.position = 'RIGHT_MIDDLE'
+ ops.base = sc.muv_auvc_align_menu
+
+ row = col.row(align=True)
+ ops = row.operator(align_uv_cursor.MUV_AUVCAlignOps.bl_idname,
+ text="Left Bottom")
+ ops.position = 'LEFT_BOTTOM'
+ ops.base = sc.muv_auvc_align_menu
+ ops = row.operator(align_uv_cursor.MUV_AUVCAlignOps.bl_idname,
+ text="Middle Bottom")
+ ops.position = 'MIDDLE_BOTTOM'
+ ops.base = sc.muv_auvc_align_menu
+ ops = row.operator(align_uv_cursor.MUV_AUVCAlignOps.bl_idname,
+ text="Right Bottom")
+ ops.position = 'RIGHT_BOTTOM'
+ ops.base = sc.muv_auvc_align_menu
+
+ box = layout.box()
+ box.prop(sc, "muv_uvcloc_enabled", text="UV Cursor Location")
+ if sc.muv_uvcloc_enabled:
+ box.prop(sc, "muv_auvc_cursor_loc", text="")
+
+ box = layout.box()
+ box.prop(sc, "muv_uvbb_enabled", text="UV Bounding Box")
+ if sc.muv_uvbb_enabled:
+ if props.uvbb.running is False:
+ box.operator(uv_bounding_box.MUV_UVBBUpdater.bl_idname,
+ text="Display", icon='PLAY')
+ else:
+ box.operator(uv_bounding_box.MUV_UVBBUpdater.bl_idname,
+ text="Hide", icon='PAUSE')
+ box.prop(sc, "muv_uvbb_uniform_scaling", text="Uniform Scaling")
+ box.prop(sc, "muv_uvbb_boundary", text="Boundary")
+
+ box = layout.box()
+ box.prop(sc, "muv_uvinsp_enabled", text="UV Inspection")
+ if sc.muv_uvinsp_enabled:
+ row = box.row()
+ if not sc.muv_props.uvinsp.display_running:
+ row.operator(uv_inspection.MUV_UVInspDisplay.bl_idname,
+ text="Display", icon='PLAY')
+ else:
+ row.operator(uv_inspection.MUV_UVInspDisplay.bl_idname,
+ text="Hide", icon='PAUSE')
+ row.operator(uv_inspection.MUV_UVInspUpdate.bl_idname,
+ text="Update")
+ row = box.row()
+ row.prop(sc, "muv_uvinsp_show_overlapped")
+ row.prop(sc, "muv_uvinsp_show_flipped")
+ row = box.row()
+ row.prop(sc, "muv_uvinsp_show_mode")
diff --git a/uv_magic_uv/ui/uvedit_uv_manipulation.py b/uv_magic_uv/ui/uvedit_uv_manipulation.py
new file mode 100644
index 00000000..f391c4cb
--- /dev/null
+++ b/uv_magic_uv/ui/uvedit_uv_manipulation.py
@@ -0,0 +1,117 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
+
+import bpy
+
+from ..op import uv_inspection
+from ..op import align_uv
+from ..op import smooth_uv
+from ..op import pack_uv
+
+
+class IMAGE_PT_MUV_UVManip(bpy.types.Panel):
+ """
+ Panel class: UV Manipulation on Property Panel on UV/ImageEditor
+ """
+
+ bl_space_type = 'IMAGE_EDITOR'
+ bl_region_type = 'TOOLS'
+ bl_label = "UV Manipulation"
+ bl_category = "Magic UV"
+ bl_context = 'mesh_edit'
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw_header(self, _):
+ layout = self.layout
+ layout.label(text="", icon='IMAGE_COL')
+
+ def draw(self, context):
+ sc = context.scene
+ layout = self.layout
+
+ box = layout.box()
+ box.prop(sc, "muv_auv_enabled", text="Align UV")
+ if sc.muv_auv_enabled:
+ col = box.column()
+ row = col.row(align=True)
+ ops = row.operator(align_uv.MUV_AUVCircle.bl_idname, text="Circle")
+ ops.transmission = sc.muv_auv_transmission
+ ops.select = sc.muv_auv_select
+ ops = row.operator(align_uv.MUV_AUVStraighten.bl_idname,
+ text="Straighten")
+ ops.transmission = sc.muv_auv_transmission
+ ops.select = sc.muv_auv_select
+ ops.vertical = sc.muv_auv_vertical
+ ops.horizontal = sc.muv_auv_horizontal
+ row = col.row()
+ ops = row.operator(align_uv.MUV_AUVAxis.bl_idname, text="XY-axis")
+ ops.transmission = sc.muv_auv_transmission
+ ops.select = sc.muv_auv_select
+ ops.vertical = sc.muv_auv_vertical
+ ops.horizontal = sc.muv_auv_horizontal
+ ops.location = sc.muv_auv_location
+ row.prop(sc, "muv_auv_location", text="")
+
+ col = box.column(align=True)
+ row = col.row(align=True)
+ row.prop(sc, "muv_auv_transmission", text="Transmission")
+ row.prop(sc, "muv_auv_select", text="Select")
+ row = col.row(align=True)
+ row.prop(sc, "muv_auv_vertical", text="Vertical")
+ row.prop(sc, "muv_auv_horizontal", text="Horizontal")
+
+ box = layout.box()
+ box.prop(sc, "muv_smuv_enabled", text="Smooth UV")
+ if sc.muv_smuv_enabled:
+ ops = box.operator(smooth_uv.MUV_AUVSmooth.bl_idname,
+ text="Smooth")
+ ops.transmission = sc.muv_smuv_transmission
+ ops.select = sc.muv_smuv_select
+ ops.mesh_infl = sc.muv_smuv_mesh_infl
+ col = box.column(align=True)
+ row = col.row(align=True)
+ row.prop(sc, "muv_smuv_transmission", text="Transmission")
+ row.prop(sc, "muv_smuv_select", text="Select")
+ col.prop(sc, "muv_smuv_mesh_infl", text="Mesh Influence")
+
+ box = layout.box()
+ box.prop(sc, "muv_seluv_enabled", text="Select UV")
+ if sc.muv_seluv_enabled:
+ row = box.row(align=True)
+ row.operator(uv_inspection.MUV_UVInspSelectOverlapped.bl_idname)
+ row.operator(uv_inspection.MUV_UVInspSelectFlipped.bl_idname)
+
+ box = layout.box()
+ box.prop(sc, "muv_packuv_enabled", text="Pack UV (Extension)")
+ if sc.muv_packuv_enabled:
+ ops = box.operator(pack_uv.MUV_PackUV.bl_idname, text="Pack UV")
+ ops.allowable_center_deviation = \
+ sc.muv_packuv_allowable_center_deviation
+ ops.allowable_size_deviation = \
+ sc.muv_packuv_allowable_size_deviation
+ box.label("Allowable Center Deviation:")
+ box.prop(sc, "muv_packuv_allowable_center_deviation", text="")
+ box.label("Allowable Size Deviation:")
+ box.prop(sc, "muv_packuv_allowable_size_deviation", text="")
diff --git a/uv_magic_uv/ui/view3d_copy_paste_uv_editmode.py b/uv_magic_uv/ui/view3d_copy_paste_uv_editmode.py
new file mode 100644
index 00000000..a22adf03
--- /dev/null
+++ b/uv_magic_uv/ui/view3d_copy_paste_uv_editmode.py
@@ -0,0 +1,81 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
+
+import bpy
+
+from ..op import copy_paste_uv
+from ..op import transfer_uv
+
+
+class OBJECT_PT_MUV_CPUV(bpy.types.Panel):
+ """
+ Panel class: Copy/Paste UV on Property Panel on View3D
+ """
+
+ bl_space_type = 'VIEW_3D'
+ bl_region_type = 'TOOLS'
+ bl_label = "Copy/Paste UV"
+ bl_category = "Magic UV"
+ bl_context = 'mesh_edit'
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw_header(self, _):
+ layout = self.layout
+ layout.label(text="", icon='IMAGE_COL')
+
+ def draw(self, context):
+ sc = context.scene
+ layout = self.layout
+
+ box = layout.box()
+ box.prop(sc, "muv_cpuv_enabled", text="Copy/Paste UV")
+ if sc.muv_cpuv_enabled:
+ row = box.row(align=True)
+ if sc.muv_cpuv_mode == 'DEFAULT':
+ row.menu(copy_paste_uv.MUV_CPUVCopyUVMenu.bl_idname,
+ text="Copy")
+ row.menu(copy_paste_uv.MUV_CPUVPasteUVMenu.bl_idname,
+ text="Paste")
+ elif sc.muv_cpuv_mode == 'SEL_SEQ':
+ row.menu(copy_paste_uv.MUV_CPUVSelSeqCopyUVMenu.bl_idname,
+ text="Copy")
+ row.menu(copy_paste_uv.MUV_CPUVSelSeqPasteUVMenu.bl_idname,
+ text="Paste")
+ box.prop(sc, "muv_cpuv_mode", expand=True)
+ box.prop(sc, "muv_cpuv_copy_seams", text="Seams")
+ box.prop(sc, "muv_cpuv_strategy", text="Strategy")
+
+ box = layout.box()
+ box.prop(sc, "muv_transuv_enabled", text="Transfer UV")
+ if sc.muv_transuv_enabled:
+ row = box.row(align=True)
+ row.operator(transfer_uv.MUV_TransUVCopy.bl_idname, text="Copy")
+ ops = row.operator(transfer_uv.MUV_TransUVPaste.bl_idname,
+ text="Paste")
+ ops.invert_normals = sc.muv_transuv_invert_normals
+ ops.copy_seams = sc.muv_transuv_copy_seams
+ row = box.row()
+ row.prop(sc, "muv_transuv_invert_normals", text="Invert Normals")
+ row.prop(sc, "muv_transuv_copy_seams", text="Seams")
diff --git a/uv_magic_uv/ui/view3d_copy_paste_uv_objectmode.py b/uv_magic_uv/ui/view3d_copy_paste_uv_objectmode.py
new file mode 100644
index 00000000..f9e2bec0
--- /dev/null
+++ b/uv_magic_uv/ui/view3d_copy_paste_uv_objectmode.py
@@ -0,0 +1,56 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
+
+import bpy
+
+from ..op import copy_paste_uv_object
+
+
+class OBJECT_PT_MUV_CPUVObj(bpy.types.Panel):
+ """
+ Panel class: Copy/Paste UV on Property Panel on View3D
+ """
+
+ bl_space_type = 'VIEW_3D'
+ bl_region_type = 'TOOLS'
+ bl_label = "Copy/Paste UV"
+ bl_category = "Magic UV"
+ bl_context = 'objectmode'
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw_header(self, _):
+ layout = self.layout
+ layout.label(text="", icon='IMAGE_COL')
+
+ def draw(self, context):
+ sc = context.scene
+ layout = self.layout
+
+ row = layout.row(align=True)
+ row.menu(copy_paste_uv_object.MUV_CPUVObjCopyUVMenu.bl_idname,
+ text="Copy")
+ row.menu(copy_paste_uv_object.MUV_CPUVObjPasteUVMenu.bl_idname,
+ text="Paste")
+ layout.prop(sc, "muv_cpuv_copy_seams", text="Copy Seams")
diff --git a/uv_magic_uv/ui/view3d_uv_manipulation.py b/uv_magic_uv/ui/view3d_uv_manipulation.py
new file mode 100644
index 00000000..1e9b7d7e
--- /dev/null
+++ b/uv_magic_uv/ui/view3d_uv_manipulation.py
@@ -0,0 +1,180 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
+
+import bpy
+
+from ..op import flip_rotate_uv
+from ..op import mirror_uv
+from ..op import move_uv
+from ..op import preserve_uv_aspect
+from ..op import texture_lock
+from ..op import texture_wrap
+from ..op import uv_sculpt
+from ..op import world_scale_uv
+
+
+class OBJECT_PT_MUV_UVManip(bpy.types.Panel):
+ """
+ Panel class: UV Manipulation on Property Panel on View3D
+ """
+
+ bl_space_type = 'VIEW_3D'
+ bl_region_type = 'TOOLS'
+ bl_label = "UV Manipulation"
+ bl_category = "Magic UV"
+ bl_context = 'mesh_edit'
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw_header(self, _):
+ layout = self.layout
+ layout.label(text="", icon='IMAGE_COL')
+
+ def draw(self, context):
+ sc = context.scene
+ props = sc.muv_props
+ layout = self.layout
+
+ box = layout.box()
+ box.prop(sc, "muv_fliprot_enabled", text="Flip/Rotate UV")
+ if sc.muv_fliprot_enabled:
+ row = box.row()
+ ops = row.operator(flip_rotate_uv.MUV_FlipRot.bl_idname,
+ text="Flip/Rotate")
+ ops.seams = sc.muv_fliprot_seams
+ row.prop(sc, "muv_fliprot_seams", text="Seams")
+
+ box = layout.box()
+ box.prop(sc, "muv_mirroruv_enabled", text="Mirror UV")
+ if sc.muv_mirroruv_enabled:
+ row = box.row()
+ ops = row.operator(mirror_uv.MUV_MirrorUV.bl_idname, text="Mirror")
+ ops.axis = sc.muv_mirroruv_axis
+ row.prop(sc, "muv_mirroruv_axis", text="")
+
+ box = layout.box()
+ box.prop(sc, "muv_mvuv_enabled", text="Move UV")
+ if sc.muv_mvuv_enabled:
+ col = box.column()
+ col.operator(move_uv.MUV_MVUV.bl_idname, icon='PLAY', text="Start")
+ if props.mvuv.running:
+ col.enabled = False
+ else:
+ col.enabled = True
+
+ box = layout.box()
+ box.prop(sc, "muv_wsuv_enabled", text="World Scale UV")
+ if sc.muv_wsuv_enabled:
+ row = box.row(align=True)
+ row.operator(world_scale_uv.MUV_WSUVMeasure.bl_idname,
+ text="Measure")
+ ops = row.operator(world_scale_uv.MUV_WSUVApply.bl_idname,
+ text="Apply")
+ ops.origin = sc.muv_wsuv_origin
+ box.label("Source:")
+ sp = box.split(percentage=0.7)
+ col = sp.column(align=True)
+ col.prop(sc, "muv_wsuv_src_mesh_area", text="Mesh Area")
+ col.prop(sc, "muv_wsuv_src_uv_area", text="UV Area")
+ col.prop(sc, "muv_wsuv_src_density", text="Density")
+ col.enabled = False
+ sp = sp.split(percentage=1.0)
+ col = sp.column(align=True)
+ col.label("cm x cm")
+ col.label("px x px")
+ col.label("px/cm")
+ col.enabled = False
+ sp = box.split(percentage=0.3)
+ sp.label("Mode:")
+ sp = sp.split(percentage=1.0)
+ col = sp.column()
+ col.prop(sc, "muv_wsuv_mode", text="")
+ if sc.muv_wsuv_mode == 'USER':
+ col.prop(sc, "muv_wsuv_tgt_density", text="Density")
+ if sc.muv_wsuv_mode == 'SCALING':
+ col.prop(sc, "muv_wsuv_scaling_factor", text="Scaling Factor")
+ box.prop(sc, "muv_wsuv_origin", text="Origin")
+
+ box = layout.box()
+ box.prop(sc, "muv_preserve_uv_enabled", text="Preserve UV Aspect")
+ if sc.muv_preserve_uv_enabled:
+ row = box.row()
+ ops = row.operator(
+ preserve_uv_aspect.MUV_PreserveUVAspect.bl_idname,
+ text="Change Image")
+ ops.dest_img_name = sc.muv_preserve_uv_tex_image
+ ops.origin = sc.muv_preserve_uv_origin
+ row.prop(sc, "muv_preserve_uv_tex_image", text="")
+ box.prop(sc, "muv_preserve_uv_origin", text="Origin")
+
+ box = layout.box()
+ box.prop(sc, "muv_texlock_enabled", text="Texture Lock")
+ if sc.muv_texlock_enabled:
+ row = box.row(align=True)
+ col = row.column(align=True)
+ col.label("Normal Mode:")
+ col = row.column(align=True)
+ col.operator(texture_lock.MUV_TexLockStart.bl_idname, text="Lock")
+ ops = col.operator(texture_lock.MUV_TexLockStop.bl_idname,
+ text="Unlock")
+ ops.connect = sc.muv_texlock_connect
+ col.prop(sc, "muv_texlock_connect", text="Connect")
+
+ row = box.row(align=True)
+ row.label("Interactive Mode:")
+ if not props.texlock.intr_running:
+ row.operator(texture_lock.MUV_TexLockIntrStart.bl_idname,
+ icon='PLAY', text="Start")
+ else:
+ row.operator(texture_lock.MUV_TexLockIntrStop.bl_idname,
+ icon="PAUSE", text="Stop")
+
+ box = layout.box()
+ box.prop(sc, "muv_texwrap_enabled", text="Texture Wrap")
+ if sc.muv_texwrap_enabled:
+ row = box.row(align=True)
+ row.operator(texture_wrap.MUV_TexWrapRefer.bl_idname, text="Refer")
+ row.operator(texture_wrap.MUV_TexWrapSet.bl_idname, text="Set")
+ box.prop(sc, "muv_texwrap_set_and_refer")
+ box.prop(sc, "muv_texwrap_selseq")
+
+ box = layout.box()
+ box.prop(sc, "muv_uvsculpt_enabled", text="UV Sculpt")
+ if sc.muv_uvsculpt_enabled:
+ if not props.uvsculpt.running:
+ box.operator(uv_sculpt.MUV_UVSculptOps.bl_idname,
+ icon='PLAY', text="Start")
+ else:
+ box.operator(uv_sculpt.MUV_UVSculptOps.bl_idname,
+ icon='PAUSE', text="Stop")
+ col = box.column()
+ col.label("Brush:")
+ col.prop(sc, "muv_uvsculpt_radius")
+ col.prop(sc, "muv_uvsculpt_strength")
+ box.prop(sc, "muv_uvsculpt_tools")
+ if sc.muv_uvsculpt_tools == 'PINCH':
+ box.prop(sc, "muv_uvsculpt_pinch_invert")
+ elif sc.muv_uvsculpt_tools == 'RELAX':
+ box.prop(sc, "muv_uvsculpt_relax_method")
+ box.prop(sc, "muv_uvsculpt_show_brush")
diff --git a/uv_magic_uv/ui/view3d_uv_mapping.py b/uv_magic_uv/ui/view3d_uv_mapping.py
new file mode 100644
index 00000000..2dc241c0
--- /dev/null
+++ b/uv_magic_uv/ui/view3d_uv_mapping.py
@@ -0,0 +1,99 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.1"
+__date__ = "24 Feb 2018"
+
+import bpy
+
+from ..op import texture_projection
+from ..op import unwrap_constraint
+from ..op import uvw
+
+
+class OBJECT_PT_MUV_UVMapping(bpy.types.Panel):
+ """
+ Panel class: UV Mapping on Property Panel on View3D
+ """
+
+ bl_space_type = 'VIEW_3D'
+ bl_region_type = 'TOOLS'
+ bl_label = "UV Mapping"
+ bl_category = "Magic UV"
+ bl_context = 'mesh_edit'
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw_header(self, _):
+ layout = self.layout
+ layout.label(text="", icon='IMAGE_COL')
+
+ def draw(self, context):
+ sc = context.scene
+ props = sc.muv_props
+ layout = self.layout
+
+ box = layout.box()
+ box.prop(sc, "muv_unwrapconst_enabled", text="Unwrap Constraint")
+ if sc.muv_unwrapconst_enabled:
+ ops = box.operator(
+ unwrap_constraint.MUV_UnwrapConstraint.bl_idname,
+ text="Unwrap")
+ ops.u_const = sc.muv_unwrapconst_u_const
+ ops.v_const = sc.muv_unwrapconst_v_const
+ row = box.row(align=True)
+ row.prop(sc, "muv_unwrapconst_u_const", text="U-Constraint")
+ row.prop(sc, "muv_unwrapconst_v_const", text="V-Constraint")
+
+ box = layout.box()
+ box.prop(sc, "muv_texproj_enabled", text="Texture Projection")
+ if sc.muv_texproj_enabled:
+ row = box.row()
+ if not props.texproj.running:
+ row.operator(texture_projection.MUV_TexProjStart.bl_idname,
+ text="Start", icon='PLAY')
+ else:
+ row.operator(texture_projection.MUV_TexProjStop.bl_idname,
+ text="Stop", icon='PAUSE')
+ row.prop(sc, "muv_texproj_tex_image", text="")
+ box.prop(sc, "muv_texproj_tex_transparency", text="Transparency")
+ col = box.column(align=True)
+ row = col.row()
+ row.prop(sc, "muv_texproj_adjust_window", text="Adjust Window")
+ if not sc.muv_texproj_adjust_window:
+ row.prop(sc, "muv_texproj_tex_magnitude", text="Magnitude")
+ col.prop(sc, "muv_texproj_apply_tex_aspect",
+ text="Texture Aspect Ratio")
+ col.prop(sc, "muv_texproj_assign_uvmap", text="Assign UVMap")
+ if props.texproj.running:
+ box.operator(texture_projection.MUV_TexProjProject.bl_idname,
+ text="Project")
+
+ box = layout.box()
+ box.prop(sc, "muv_uvw_enabled", text="UVW")
+ if sc.muv_uvw_enabled:
+ row = box.row(align=True)
+ ops = row.operator(uvw.MUV_UVWBoxMap.bl_idname, text="Box")
+ ops.assign_uvmap = sc.muv_uvw_assign_uvmap
+ ops = row.operator(uvw.MUV_UVWBestPlanerMap.bl_idname,
+ text="Best Planner")
+ ops.assign_uvmap = sc.muv_uvw_assign_uvmap
+ box.prop(sc, "muv_uvw_assign_uvmap", text="Assign UVMap")