├── .gitignore ├── LICENSE.txt ├── README.md ├── __init__.py ├── icons ├── bake_anti_alias.png ├── bake_color_space.png ├── bake_obj_cage.png ├── bake_obj_float.png ├── bake_obj_high.png ├── bake_obj_low.png ├── op_align_bottom.png ├── op_align_bottomleft.png ├── op_align_bottomright.png ├── op_align_center.png ├── op_align_horizontal.png ├── op_align_left.png ├── op_align_right.png ├── op_align_top.png ├── op_align_topleft.png ├── op_align_topright.png ├── op_align_vertical.png ├── op_bake.png ├── op_bake_explode.png ├── op_color_convert_texture.png ├── op_color_convert_vertex_colors.png ├── op_color_from_directions.png ├── op_color_from_elements.png ├── op_color_from_materials.png ├── op_extend_canvas_BL_active.png ├── op_extend_canvas_BR_active.png ├── op_extend_canvas_TL_active.png ├── op_extend_canvas_TR_active.png ├── op_extend_canvas_open.png ├── op_island_align_edge.png ├── op_island_align_sort_h.png ├── op_island_align_sort_v.png ├── op_island_align_world.png ├── op_island_centralize.png ├── op_island_mirror.png ├── op_island_mirror_H.png ├── op_island_mirror_V.png ├── op_island_rotate_90_left.png ├── op_island_rotate_90_right.png ├── op_island_straighten_edge_loops.png ├── op_meshtex_create.png ├── op_meshtex_pattern.png ├── op_meshtex_trim.png ├── op_meshtex_trim_collapse.png ├── op_meshtex_wrap.png ├── op_randomize.png ├── op_rectify.png ├── op_select_islands_flipped.png ├── op_select_islands_identical.png ├── op_select_islands_outline.png ├── op_select_islands_overlap.png ├── op_select_zero.png ├── op_smoothing_uv_islands.png ├── op_texel_checker_map.png ├── op_texture_open.png ├── op_texture_preview.png ├── op_texture_reload_all.png ├── op_texture_save.png ├── op_unwrap_edge_peel.png ├── op_unwrap_faces_iron.png ├── op_uv_crop.png ├── op_uv_fill.png ├── op_uv_unwrap.png └── texel_density.png ├── icons_bip ├── bake_anti_alias.bip ├── bake_color_space.bip ├── bake_obj_cage.bip ├── bake_obj_float.bip ├── bake_obj_high.bip ├── bake_obj_low.bip ├── op_align_bottom.bip ├── op_align_bottomleft.bip ├── op_align_bottomright.bip ├── op_align_center.bip ├── op_align_horizontal.bip ├── op_align_left.bip ├── op_align_right.bip ├── op_align_top.bip ├── op_align_topleft.bip ├── op_align_topright.bip ├── op_align_vertical.bip ├── op_bake.bip ├── op_bake_explode.bip ├── op_color_convert_texture.bip ├── op_color_convert_vertex_colors.bip ├── op_color_from_directions.bip ├── op_color_from_elements.bip ├── op_color_from_materials.bip ├── op_extend_canvas_BL_active.bip ├── op_extend_canvas_BR_active.bip ├── op_extend_canvas_TL_active.bip ├── op_extend_canvas_TR_active.bip ├── op_extend_canvas_open.bip ├── op_island_align_edge.bip ├── op_island_align_sort_h.bip ├── op_island_align_sort_v.bip ├── op_island_align_world.bip ├── op_island_centralize.bip ├── op_island_mirror.bip ├── op_island_mirror_H.bip ├── op_island_mirror_V.bip ├── op_island_rotate_90_left.bip ├── op_island_rotate_90_right.bip ├── op_island_straighten_edge_loops.bip ├── op_meshtex_create.bip ├── op_meshtex_pattern.bip ├── op_meshtex_trim.bip ├── op_meshtex_trim_collapse.bip ├── op_meshtex_wrap.bip ├── op_randomize.bip ├── op_rectify.bip ├── op_relax.bip ├── op_select_islands_flipped.bip ├── op_select_islands_identical.bip ├── op_select_islands_outline.bip ├── op_select_islands_overlap.bip ├── op_select_zero.bip ├── op_smoothing_uv_islands.bip ├── op_texel_checker_map.bip ├── op_texture_open.bip ├── op_texture_preview.bip ├── op_texture_reload_all.bip ├── op_texture_save.bip ├── op_unwrap_edge_peel.bip ├── op_unwrap_faces_iron.bip ├── op_uv_crop.bip ├── op_uv_fill.bip ├── op_uv_unwrap.bip └── texel_density.bip ├── op_align.py ├── op_bake.py ├── op_bake_explode.py ├── op_bake_organize_names.py ├── op_color_assign.py ├── op_color_clear.py ├── op_color_convert_texture.py ├── op_color_convert_vertex_colors.py ├── op_color_from_directions.py ├── op_color_from_elements.py ├── op_color_from_materials.py ├── op_color_io_export.py ├── op_color_io_import.py ├── op_color_select.py ├── op_color_select_vertex.py ├── op_edge_split_bevel.py ├── op_island_align_edge.py ├── op_island_align_sort.py ├── op_island_align_world.py ├── op_island_centralize.py ├── op_island_mirror.py ├── op_island_rotate_90.py ├── op_island_straighten_edge_loops.py ├── op_meshtex_create.py ├── op_meshtex_pattern.py ├── op_meshtex_trim.py ├── op_meshtex_trim_collapse.py ├── op_meshtex_wrap.py ├── op_randomize.py ├── op_rectify.py ├── op_relax.py ├── op_select_islands_flipped.py ├── op_select_islands_identical.py ├── op_select_islands_outline.py ├── op_select_islands_overlap.py ├── op_select_zero.py ├── op_smoothing_uv_islands.py ├── op_stitch.py ├── op_texel_checker_map.py ├── op_texel_checker_map_cleanup.py ├── op_texel_density_get.py ├── op_texel_density_set.py ├── op_texture_open.py ├── op_texture_preview.py ├── op_texture_preview_cleanup.py ├── op_texture_reload_all.py ├── op_texture_remove.py ├── op_texture_save.py ├── op_texture_select.py ├── op_unwrap_edge_peel.py ├── op_unwrap_faces_iron.py ├── op_uv_channel_add.py ├── op_uv_channel_remove.py ├── op_uv_channel_swap.py ├── op_uv_crop.py ├── op_uv_fill.py ├── op_uv_resize.py ├── op_uv_size_get.py ├── op_uv_unwrap.py ├── resources ├── bake_modes │ ├── alpha.png │ ├── anisotropic.png │ ├── anisotropic_rotation.png │ ├── ao.png │ ├── ao_legacy.png │ ├── base_color.png │ ├── bevel_mask.png │ ├── cavity.png │ ├── clearcoat.png │ ├── clearcoat_roughness.png │ ├── combined.png │ ├── curvature.png │ ├── diffuse.png │ ├── displacment.png │ ├── dust.png │ ├── emission.png │ ├── emission_strength.png │ ├── environment.png │ ├── glossiness.png │ ├── id_element.png │ ├── id_material.png │ ├── metallic.png │ ├── normal_object.png │ ├── normal_object_bevel.png │ ├── normal_tangent.png │ ├── normal_tangent_bevel.png │ ├── paint_base.png │ ├── position.png │ ├── roughness.png │ ├── selection.png │ ├── shadow.png │ ├── sheen.png │ ├── sheen_tint.png │ ├── smoothness.png │ ├── specular.png │ ├── specular_tint.png │ ├── sss_color.png │ ├── sss_strength.png │ ├── thickness.png │ ├── transmission.png │ ├── transmission_roughness.png │ ├── uv.png │ └── wireframe.png ├── bake_modes_bip │ ├── alpha.bip │ ├── anisotropic.bip │ ├── anisotropic_rotation.bip │ ├── ao.bip │ ├── base_color.bip │ ├── bevel_mask.bip │ ├── cavity.bip │ ├── clearcoat.bip │ ├── clearcoat_roughness.bip │ ├── combined.bip │ ├── curvature.bip │ ├── diffuse.bip │ ├── displacement.bip │ ├── dust.bip │ ├── emission.bip │ ├── emission_strength.bip │ ├── environment.bip │ ├── glossiness.bip │ ├── id_element.bip │ ├── id_material.bip │ ├── metallic.bip │ ├── normal_object.bip │ ├── normal_object_bevel.bip │ ├── normal_tangent.bip │ ├── normal_tangent_bevel.bip │ ├── paint_base.bip │ ├── position.bip │ ├── roughness.bip │ ├── selection.bip │ ├── shadow.bip │ ├── sheen.bip │ ├── sheen_tint.bip │ ├── specular.bip │ ├── specular_tint.bip │ ├── sss_color.bip │ ├── sss_strength.bip │ ├── thickness.bip │ ├── transmission.bip │ ├── transmission_roughness.bip │ ├── uv.bip │ └── wireframe.bip ├── compositing.blend ├── materials.blend ├── materials_2.80.blend └── materials_3.0.blend ├── settings.py ├── t3dn_bip ├── __init__.py ├── formats.py ├── previews.py └── utils.py ├── utilities_bake.py ├── utilities_bbox.py ├── utilities_color.py ├── utilities_meshtex.py ├── utilities_texel.py ├── utilities_ui.py └── utilities_uv.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | # Blender TexTools, 2 | # 3 | # Copyright (C) <2018> 4 | 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | 19 | # Credits 20 | # 21 | # UVSquares: 22 | # 23 | # Copyright (C) <2014> 24 | # https://github.com/JoseConseco/UvSquares/blob/master/uv_squares.py 25 | # 26 | # Current maintainers: franMarz, Sav Martin. 27 | # 28 | # Icons art. DavidRivera (activemotionpictures) 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TexTools for Blender # 2 | 3 | TexTools is a free addon for Blender with a set of professional UV and Texture tools. Fully compatible with Blender 3.2 and later, most features should work for Blender versions as old as 2.8, including: UV Layout tools (Align, Rectify, Sort, Randomize...), multiple out-of-the-box Texture Baking modes, Texel Density tools, smart UV Selection operators, Color ID tools and some UV related Mesh creation utilities. 4 | 5 | Back in 2009, @renderhjs released the [Original TexTools](http://renderhjs.net/textools/) for 3DS MAX, and later, the Blender add-on, which was discontinued from the 2.79 Blender version until @SavMartin ported it to the 2.8 Blender release. 6 | 7 | ## Installation ## 8 | 9 | 1. Download TexTools for Blender from [master](https://github.com/franMarz/TexTools-Blender/archive/refs/heads/master.zip)(best), or the latest release. 10 | 2. In Blender from the **File** menu open **User Preferences** ![](http://renderhjs.net/textools/blender/img/installation_open_preferences.png) 11 | 3. Go to the **Add-ons** tab ![](http://renderhjs.net/textools/blender/img/installation_addons.png). 12 | 4. Look for any old version of TexTools currently installed and uninstall it. 13 | 5. Hit **Install Addon-on from File...** ![](http://renderhjs.net/textools/blender/img/installation_install_addon_from_file.png) and Select the zip file. 14 | 6. Enable the TexTools Addon. 15 | 7. The TexTools panel can be found in the **UV/Image Editor** view ![](http://renderhjs.net/textools/blender/img/installation_uv_image_editor.png) in the left side Panel. 16 | 17 | ## Links ## 18 | * Blenderartists [discussion thread](https://blenderartists.org/forum/showthread.php?443182-TexTools-for-Blender) 19 | * Original author [renderhjs.net](http://www.renderhjs.net/) personal website, all written in haxe ;) 20 | * renderhjs's [Git repository](https://bitbucket.org/renderhjs/textools-blender) on BitBucket 21 | * renderhjs's [release log](http://renderhjs.net/textools/blender/log.html) 22 | * renderhjs's [3dsMax version](http://renderhjs.net/textools/) of TexTools 23 | * Polycount [discussion thread](http://polycount.com/discussion/197226/textools-for-blender) 24 | 25 | ## Documentation ## 26 | Visit the [Official Website & Documentation](http://renderhjs.net/textools/blender/) for an in depth overview of the original tools (outdated) 27 | -------------------------------------------------------------------------------- /icons/bake_anti_alias.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/bake_anti_alias.png -------------------------------------------------------------------------------- /icons/bake_color_space.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/bake_color_space.png -------------------------------------------------------------------------------- /icons/bake_obj_cage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/bake_obj_cage.png -------------------------------------------------------------------------------- /icons/bake_obj_float.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/bake_obj_float.png -------------------------------------------------------------------------------- /icons/bake_obj_high.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/bake_obj_high.png -------------------------------------------------------------------------------- /icons/bake_obj_low.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/bake_obj_low.png -------------------------------------------------------------------------------- /icons/op_align_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_align_bottom.png -------------------------------------------------------------------------------- /icons/op_align_bottomleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_align_bottomleft.png -------------------------------------------------------------------------------- /icons/op_align_bottomright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_align_bottomright.png -------------------------------------------------------------------------------- /icons/op_align_center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_align_center.png -------------------------------------------------------------------------------- /icons/op_align_horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_align_horizontal.png -------------------------------------------------------------------------------- /icons/op_align_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_align_left.png -------------------------------------------------------------------------------- /icons/op_align_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_align_right.png -------------------------------------------------------------------------------- /icons/op_align_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_align_top.png -------------------------------------------------------------------------------- /icons/op_align_topleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_align_topleft.png -------------------------------------------------------------------------------- /icons/op_align_topright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_align_topright.png -------------------------------------------------------------------------------- /icons/op_align_vertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_align_vertical.png -------------------------------------------------------------------------------- /icons/op_bake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_bake.png -------------------------------------------------------------------------------- /icons/op_bake_explode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_bake_explode.png -------------------------------------------------------------------------------- /icons/op_color_convert_texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_color_convert_texture.png -------------------------------------------------------------------------------- /icons/op_color_convert_vertex_colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_color_convert_vertex_colors.png -------------------------------------------------------------------------------- /icons/op_color_from_directions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_color_from_directions.png -------------------------------------------------------------------------------- /icons/op_color_from_elements.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_color_from_elements.png -------------------------------------------------------------------------------- /icons/op_color_from_materials.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_color_from_materials.png -------------------------------------------------------------------------------- /icons/op_extend_canvas_BL_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_extend_canvas_BL_active.png -------------------------------------------------------------------------------- /icons/op_extend_canvas_BR_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_extend_canvas_BR_active.png -------------------------------------------------------------------------------- /icons/op_extend_canvas_TL_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_extend_canvas_TL_active.png -------------------------------------------------------------------------------- /icons/op_extend_canvas_TR_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_extend_canvas_TR_active.png -------------------------------------------------------------------------------- /icons/op_extend_canvas_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_extend_canvas_open.png -------------------------------------------------------------------------------- /icons/op_island_align_edge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_island_align_edge.png -------------------------------------------------------------------------------- /icons/op_island_align_sort_h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_island_align_sort_h.png -------------------------------------------------------------------------------- /icons/op_island_align_sort_v.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_island_align_sort_v.png -------------------------------------------------------------------------------- /icons/op_island_align_world.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_island_align_world.png -------------------------------------------------------------------------------- /icons/op_island_centralize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_island_centralize.png -------------------------------------------------------------------------------- /icons/op_island_mirror.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_island_mirror.png -------------------------------------------------------------------------------- /icons/op_island_mirror_H.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_island_mirror_H.png -------------------------------------------------------------------------------- /icons/op_island_mirror_V.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_island_mirror_V.png -------------------------------------------------------------------------------- /icons/op_island_rotate_90_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_island_rotate_90_left.png -------------------------------------------------------------------------------- /icons/op_island_rotate_90_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_island_rotate_90_right.png -------------------------------------------------------------------------------- /icons/op_island_straighten_edge_loops.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_island_straighten_edge_loops.png -------------------------------------------------------------------------------- /icons/op_meshtex_create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_meshtex_create.png -------------------------------------------------------------------------------- /icons/op_meshtex_pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_meshtex_pattern.png -------------------------------------------------------------------------------- /icons/op_meshtex_trim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_meshtex_trim.png -------------------------------------------------------------------------------- /icons/op_meshtex_trim_collapse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_meshtex_trim_collapse.png -------------------------------------------------------------------------------- /icons/op_meshtex_wrap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_meshtex_wrap.png -------------------------------------------------------------------------------- /icons/op_randomize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_randomize.png -------------------------------------------------------------------------------- /icons/op_rectify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_rectify.png -------------------------------------------------------------------------------- /icons/op_select_islands_flipped.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_select_islands_flipped.png -------------------------------------------------------------------------------- /icons/op_select_islands_identical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_select_islands_identical.png -------------------------------------------------------------------------------- /icons/op_select_islands_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_select_islands_outline.png -------------------------------------------------------------------------------- /icons/op_select_islands_overlap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_select_islands_overlap.png -------------------------------------------------------------------------------- /icons/op_select_zero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_select_zero.png -------------------------------------------------------------------------------- /icons/op_smoothing_uv_islands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_smoothing_uv_islands.png -------------------------------------------------------------------------------- /icons/op_texel_checker_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_texel_checker_map.png -------------------------------------------------------------------------------- /icons/op_texture_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_texture_open.png -------------------------------------------------------------------------------- /icons/op_texture_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_texture_preview.png -------------------------------------------------------------------------------- /icons/op_texture_reload_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_texture_reload_all.png -------------------------------------------------------------------------------- /icons/op_texture_save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_texture_save.png -------------------------------------------------------------------------------- /icons/op_unwrap_edge_peel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_unwrap_edge_peel.png -------------------------------------------------------------------------------- /icons/op_unwrap_faces_iron.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_unwrap_faces_iron.png -------------------------------------------------------------------------------- /icons/op_uv_crop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_uv_crop.png -------------------------------------------------------------------------------- /icons/op_uv_fill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_uv_fill.png -------------------------------------------------------------------------------- /icons/op_uv_unwrap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/op_uv_unwrap.png -------------------------------------------------------------------------------- /icons/texel_density.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons/texel_density.png -------------------------------------------------------------------------------- /icons_bip/bake_anti_alias.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/bake_anti_alias.bip -------------------------------------------------------------------------------- /icons_bip/bake_color_space.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/bake_color_space.bip -------------------------------------------------------------------------------- /icons_bip/bake_obj_cage.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/bake_obj_cage.bip -------------------------------------------------------------------------------- /icons_bip/bake_obj_float.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/bake_obj_float.bip -------------------------------------------------------------------------------- /icons_bip/bake_obj_high.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/bake_obj_high.bip -------------------------------------------------------------------------------- /icons_bip/bake_obj_low.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/bake_obj_low.bip -------------------------------------------------------------------------------- /icons_bip/op_align_bottom.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_align_bottom.bip -------------------------------------------------------------------------------- /icons_bip/op_align_bottomleft.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_align_bottomleft.bip -------------------------------------------------------------------------------- /icons_bip/op_align_bottomright.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_align_bottomright.bip -------------------------------------------------------------------------------- /icons_bip/op_align_center.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_align_center.bip -------------------------------------------------------------------------------- /icons_bip/op_align_horizontal.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_align_horizontal.bip -------------------------------------------------------------------------------- /icons_bip/op_align_left.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_align_left.bip -------------------------------------------------------------------------------- /icons_bip/op_align_right.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_align_right.bip -------------------------------------------------------------------------------- /icons_bip/op_align_top.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_align_top.bip -------------------------------------------------------------------------------- /icons_bip/op_align_topleft.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_align_topleft.bip -------------------------------------------------------------------------------- /icons_bip/op_align_topright.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_align_topright.bip -------------------------------------------------------------------------------- /icons_bip/op_align_vertical.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_align_vertical.bip -------------------------------------------------------------------------------- /icons_bip/op_bake.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_bake.bip -------------------------------------------------------------------------------- /icons_bip/op_bake_explode.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_bake_explode.bip -------------------------------------------------------------------------------- /icons_bip/op_color_convert_texture.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_color_convert_texture.bip -------------------------------------------------------------------------------- /icons_bip/op_color_convert_vertex_colors.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_color_convert_vertex_colors.bip -------------------------------------------------------------------------------- /icons_bip/op_color_from_directions.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_color_from_directions.bip -------------------------------------------------------------------------------- /icons_bip/op_color_from_elements.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_color_from_elements.bip -------------------------------------------------------------------------------- /icons_bip/op_color_from_materials.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_color_from_materials.bip -------------------------------------------------------------------------------- /icons_bip/op_extend_canvas_BL_active.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_extend_canvas_BL_active.bip -------------------------------------------------------------------------------- /icons_bip/op_extend_canvas_BR_active.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_extend_canvas_BR_active.bip -------------------------------------------------------------------------------- /icons_bip/op_extend_canvas_TL_active.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_extend_canvas_TL_active.bip -------------------------------------------------------------------------------- /icons_bip/op_extend_canvas_TR_active.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_extend_canvas_TR_active.bip -------------------------------------------------------------------------------- /icons_bip/op_extend_canvas_open.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_extend_canvas_open.bip -------------------------------------------------------------------------------- /icons_bip/op_island_align_edge.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_island_align_edge.bip -------------------------------------------------------------------------------- /icons_bip/op_island_align_sort_h.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_island_align_sort_h.bip -------------------------------------------------------------------------------- /icons_bip/op_island_align_sort_v.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_island_align_sort_v.bip -------------------------------------------------------------------------------- /icons_bip/op_island_align_world.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_island_align_world.bip -------------------------------------------------------------------------------- /icons_bip/op_island_centralize.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_island_centralize.bip -------------------------------------------------------------------------------- /icons_bip/op_island_mirror.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_island_mirror.bip -------------------------------------------------------------------------------- /icons_bip/op_island_mirror_H.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_island_mirror_H.bip -------------------------------------------------------------------------------- /icons_bip/op_island_mirror_V.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_island_mirror_V.bip -------------------------------------------------------------------------------- /icons_bip/op_island_rotate_90_left.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_island_rotate_90_left.bip -------------------------------------------------------------------------------- /icons_bip/op_island_rotate_90_right.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_island_rotate_90_right.bip -------------------------------------------------------------------------------- /icons_bip/op_island_straighten_edge_loops.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_island_straighten_edge_loops.bip -------------------------------------------------------------------------------- /icons_bip/op_meshtex_create.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_meshtex_create.bip -------------------------------------------------------------------------------- /icons_bip/op_meshtex_pattern.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_meshtex_pattern.bip -------------------------------------------------------------------------------- /icons_bip/op_meshtex_trim.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_meshtex_trim.bip -------------------------------------------------------------------------------- /icons_bip/op_meshtex_trim_collapse.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_meshtex_trim_collapse.bip -------------------------------------------------------------------------------- /icons_bip/op_meshtex_wrap.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_meshtex_wrap.bip -------------------------------------------------------------------------------- /icons_bip/op_randomize.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_randomize.bip -------------------------------------------------------------------------------- /icons_bip/op_rectify.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_rectify.bip -------------------------------------------------------------------------------- /icons_bip/op_relax.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_relax.bip -------------------------------------------------------------------------------- /icons_bip/op_select_islands_flipped.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_select_islands_flipped.bip -------------------------------------------------------------------------------- /icons_bip/op_select_islands_identical.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_select_islands_identical.bip -------------------------------------------------------------------------------- /icons_bip/op_select_islands_outline.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_select_islands_outline.bip -------------------------------------------------------------------------------- /icons_bip/op_select_islands_overlap.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_select_islands_overlap.bip -------------------------------------------------------------------------------- /icons_bip/op_select_zero.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_select_zero.bip -------------------------------------------------------------------------------- /icons_bip/op_smoothing_uv_islands.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_smoothing_uv_islands.bip -------------------------------------------------------------------------------- /icons_bip/op_texel_checker_map.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_texel_checker_map.bip -------------------------------------------------------------------------------- /icons_bip/op_texture_open.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_texture_open.bip -------------------------------------------------------------------------------- /icons_bip/op_texture_preview.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_texture_preview.bip -------------------------------------------------------------------------------- /icons_bip/op_texture_reload_all.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_texture_reload_all.bip -------------------------------------------------------------------------------- /icons_bip/op_texture_save.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_texture_save.bip -------------------------------------------------------------------------------- /icons_bip/op_unwrap_edge_peel.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_unwrap_edge_peel.bip -------------------------------------------------------------------------------- /icons_bip/op_unwrap_faces_iron.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_unwrap_faces_iron.bip -------------------------------------------------------------------------------- /icons_bip/op_uv_crop.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_uv_crop.bip -------------------------------------------------------------------------------- /icons_bip/op_uv_fill.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_uv_fill.bip -------------------------------------------------------------------------------- /icons_bip/op_uv_unwrap.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/op_uv_unwrap.bip -------------------------------------------------------------------------------- /icons_bip/texel_density.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/icons_bip/texel_density.bip -------------------------------------------------------------------------------- /op_bake_explode.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import operator 3 | from mathutils import Vector 4 | 5 | from . import settings 6 | 7 | frame_range = 50 8 | 9 | 10 | 11 | class op(bpy.types.Operator): 12 | bl_idname = "uv.textools_bake_explode" 13 | bl_label = "Explode" 14 | bl_description = "Explode selected bake pairs with animation keyframes" 15 | bl_options = {'REGISTER', 'UNDO'} 16 | 17 | @classmethod 18 | def poll(cls, context): 19 | if len(settings.sets) <= 1: 20 | return False 21 | return True 22 | 23 | 24 | def execute(self, context): 25 | explode(self) 26 | return {'FINISHED'} 27 | 28 | 29 | 30 | def explode(self): 31 | sets = settings.sets 32 | 33 | set_bounds = {} 34 | set_volume = {} 35 | avg_side = 0 36 | for bset in sets: 37 | set_bounds[bset] = get_bbox_set(bset) 38 | set_volume[bset] = set_bounds[bset]['size'].x * set_bounds[bset]['size'].y * set_bounds[bset]['size'].z 39 | 40 | avg_side+=set_bounds[bset]['size'].x 41 | avg_side+=set_bounds[bset]['size'].y 42 | avg_side+=set_bounds[bset]['size'].z 43 | 44 | avg_side/=(len(sets)*3) 45 | 46 | sorted_set_volume = sorted(set_volume.items(), key=operator.itemgetter(1)) 47 | sorted_sets = [item[0] for item in sorted_set_volume] 48 | sorted_sets.reverse() 49 | 50 | # All combined bounding boxes 51 | bbox_all = merge_bounds(list(set_bounds.values())) 52 | bbox_max = set_bounds[ sorted_sets[0] ] # max_bbox(list(set_bounds.values())) 53 | 54 | # Offset sets into their direction 55 | dir_offset_last_bbox = {} 56 | for i in range(0,6): 57 | dir_offset_last_bbox[i] = bbox_max #bbox_all 58 | 59 | bpy.context.scene.frame_start = 0 60 | bpy.context.scene.frame_end = frame_range 61 | bpy.context.scene.frame_current = 0 62 | 63 | # Process each set 64 | for bset in sorted_sets[1:]: 65 | #if set_bounds[bset] != bbox_max: 66 | delta = set_bounds[bset]['center'] - bbox_all['center'] 67 | offset_set(bset, delta, avg_side*0.35, dir_offset_last_bbox) 68 | 69 | bpy.context.scene.frame_current = frame_range 70 | 71 | 72 | 73 | def offset_set(bset, delta, margin, dir_offset_last_bbox): 74 | objects = bset.objects_low + bset.objects_high + bset.objects_cage 75 | 76 | # Which Direction? 77 | delta_max = max(abs(delta.x), abs(delta.y), abs(delta.z)) 78 | direction = [0,0,0] 79 | if delta_max > 0: 80 | for i in range(0,3): 81 | if abs(delta[i]) == delta_max: 82 | direction[i] = delta[i]/abs(delta[i]) 83 | break 84 | else: 85 | direction[i] = 0 86 | else: 87 | # When no delta offset is measured, move up 88 | direction = [0,0,1] 89 | 90 | delta = Vector((direction[0], direction[1], direction[2])) 91 | 92 | # Get Key 93 | key = get_delta_key(delta) 94 | 95 | # Calculate Offset 96 | bbox = get_bbox_set(bset) 97 | bbox_last = dir_offset_last_bbox[key] 98 | 99 | offset = Vector((0,0,0)) 100 | 101 | if delta.x == 1: 102 | offset = delta * ( bbox_last['max'].x - bbox['min'].x ) 103 | elif delta.x == -1: 104 | offset = delta * -( bbox_last['min'].x - bbox['max'].x ) 105 | 106 | elif delta.y == 1: 107 | offset = delta * ( bbox_last['max'].y - bbox['min'].y ) 108 | elif delta.y == -1: 109 | offset = delta * -( bbox_last['min'].y - bbox['max'].y ) 110 | 111 | elif delta.z == 1: 112 | offset = delta * ( bbox_last['max'].z - bbox['min'].z ) 113 | elif delta.z == -1: 114 | offset = delta * -( bbox_last['min'].z - bbox['max'].z ) 115 | 116 | # Add margin 117 | offset += delta * margin 118 | 119 | for obj in objects: 120 | bpy.context.view_layer.update() 121 | obj.keyframe_insert(data_path="location", frame=0) 122 | obj.location += offset 123 | bpy.context.view_layer.update() 124 | obj.keyframe_insert(data_path="location", frame=frame_range) 125 | 126 | # Update last bbox in direction 127 | dir_offset_last_bbox[key] = get_bbox_set(bset) 128 | 129 | 130 | 131 | def get_delta_key(delta): 132 | if delta.x == -1: 133 | return 0 134 | elif delta.x == 1: 135 | return 1 136 | if delta.y == -1: 137 | return 2 138 | elif delta.y == 1: 139 | return 3 140 | if delta.z == -1: 141 | return 4 142 | elif delta.z == 1: 143 | return 5 144 | 145 | 146 | 147 | def merge_bounds(bounds): 148 | box_min = bounds[0]['min'].copy() 149 | box_max = bounds[0]['max'].copy() 150 | 151 | for bbox in bounds: 152 | box_min.x = min(box_min.x, bbox['min'].x) 153 | box_min.y = min(box_min.y, bbox['min'].y) 154 | box_min.z = min(box_min.z, bbox['min'].z) 155 | 156 | box_max.x = max(box_max.x, bbox['max'].x) 157 | box_max.y = max(box_max.y, bbox['max'].y) 158 | box_max.z = max(box_max.z, bbox['max'].z) 159 | 160 | return { 161 | 'min':box_min, 162 | 'max':box_max, 163 | 'size':(box_max-box_min), 164 | 'center':box_min+(box_max-box_min)/2 165 | } 166 | 167 | 168 | 169 | def get_bbox_set(bset): 170 | objects = bset.objects_low + bset.objects_high + bset.objects_cage 171 | bounds = [] 172 | for obj in objects: 173 | bounds.append( get_bbox(obj) ) 174 | return merge_bounds(bounds) 175 | 176 | 177 | 178 | def get_bbox(obj): 179 | corners = [obj.matrix_world @ Vector(corner) for corner in obj.bound_box] 180 | 181 | # Get world space Min / Max 182 | box_min = Vector((corners[0].x, corners[0].y, corners[0].z)) 183 | box_max = Vector((corners[0].x, corners[0].y, corners[0].z)) 184 | for corner in corners: 185 | # box_min.x = -8 186 | box_min.x = min(box_min.x, corner.x) 187 | box_min.y = min(box_min.y, corner.y) 188 | box_min.z = min(box_min.z, corner.z) 189 | 190 | box_max.x = max(box_max.x, corner.x) 191 | box_max.y = max(box_max.y, corner.y) 192 | box_max.z = max(box_max.z, corner.z) 193 | 194 | return { 195 | 'min':box_min, 196 | 'max':box_max, 197 | 'size':(box_max-box_min), 198 | 'center':box_min+(box_max-box_min)/2 199 | } 200 | -------------------------------------------------------------------------------- /op_bake_organize_names.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import operator 3 | 4 | from mathutils import Vector 5 | from . import utilities_bake 6 | 7 | 8 | class op(bpy.types.Operator): 9 | bl_idname = "uv.textools_bake_organize_names" 10 | bl_label = "Match Names" 11 | bl_description = "Match high poly object names to low poly objects by their bounding boxes." 12 | bl_options = {'REGISTER', 'UNDO'} 13 | 14 | @classmethod 15 | def poll(cls, context): 16 | # Require 2 or more objects to sort 17 | if len(bpy.context.selected_objects) <= 1: 18 | return False 19 | return True 20 | 21 | 22 | def execute(self, context): 23 | sort_objects(self) 24 | return {'FINISHED'} 25 | 26 | 27 | def sort_objects(self): 28 | # Collect objects 29 | objects = [] 30 | bounds = {} 31 | for obj in bpy.context.selected_objects: 32 | if obj.type == 'MESH': 33 | objects.append(obj) 34 | bounds[obj] = get_bbox(obj) 35 | 36 | print(f"Objects {len(objects)}x") 37 | 38 | # Get smallest side of any bounding box 39 | min_side = min(bounds[objects[0]]['size']) 40 | avg_side = 0 41 | for obj in bounds: 42 | min_side = min(min_side, *bounds[obj]['size']) 43 | avg_side += sum(bounds[obj]['size']) 44 | avg_side /= (len(bounds)*3) 45 | 46 | # Get all Low and high poly objects 47 | objects_low = [obj for obj in objects if utilities_bake.get_object_type(obj)=='low'] 48 | objects_high = [obj for obj in objects if utilities_bake.get_object_type(obj)=='high'] 49 | 50 | if 0 in (len(objects_low), len(objects_high)): 51 | self.report({'ERROR_INVALID_INPUT'}, f"There are no {'low' if len(objects_low) == 0 else 'high'} poly objects selected") 52 | 53 | print(f"Low {len(objects_low)}x, High {len(objects_high)}x") 54 | 55 | pairs_low_high = {} 56 | 57 | objects_left_high = objects_high.copy() 58 | for obj_A in objects_low: 59 | 60 | matches = {} 61 | for obj_B in objects_left_high: 62 | score = get_score(obj_A, obj_B) 63 | p = score / avg_side 64 | if 0 < p <= 0.65: 65 | matches[obj_B] = p 66 | else: 67 | print(f"Not matched: {p} ") 68 | 69 | if len(matches): 70 | sorted_matches = sorted(matches.items(), key=operator.itemgetter(1)) 71 | for i in range(0, len(sorted_matches)): 72 | A = obj_A 73 | B = sorted_matches[i][0] 74 | p = sorted_matches[i][1] 75 | print(f"Check: {int(p * 100.0)}% '{A.name}' = '{B.name}'") 76 | 77 | # Remove from list 78 | objects_left_high.remove(sorted_matches[0][0]) 79 | pairs_low_high[obj_A] = sorted_matches[0][0] 80 | print("") 81 | 82 | # objects_unsorted = [obj for obj in objects if (obj not in pairs_low_high.values() and obj not in pairs_low_high.keys() )] 83 | 84 | bpy.ops.object.select_all(action='DESELECT') 85 | for obj_A in pairs_low_high: 86 | obj_B = pairs_low_high[obj_A] 87 | try: 88 | obj_B.name = utilities_bake.get_set_name(obj_A)+" high" 89 | 90 | obj_A.select_set(True) 91 | obj_B.select_set(True) 92 | except: 93 | print("Fail") 94 | 95 | print(f"Matched {len(pairs_low_high)}x") 96 | 97 | 98 | 99 | def get_score(A, B): 100 | 101 | bbox_A = get_bbox(A) 102 | bbox_B = get_bbox(B) 103 | 104 | # Not colliding 105 | if not is_colliding(bbox_A, bbox_B): 106 | return -1.0 107 | 108 | # Position 109 | delta_pos = (bbox_B['center'] - bbox_A['center']).length 110 | 111 | # Volume 112 | volume_A = bbox_A['size'].x * bbox_A['size'].y * bbox_A['size'].z 113 | volume_B = bbox_B['size'].x * bbox_B['size'].y * bbox_B['size'].z 114 | delta_vol = (max(volume_A, volume_B) - min(volume_A, volume_B))/3.0 115 | 116 | # Longest side 117 | side_A_max = max(bbox_A['size'].x, bbox_A['size'].y, bbox_A['size'].z) 118 | side_B_max = max(bbox_B['size'].x, bbox_B['size'].y, bbox_B['size'].z) 119 | delta_size_max = abs(side_A_max - side_B_max) 120 | 121 | return delta_pos + delta_vol + delta_size_max 122 | 123 | 124 | 125 | def get_bbox(obj): 126 | corners = [obj.matrix_world @ Vector(corner) for corner in obj.bound_box] 127 | 128 | # Get world space Min / Max 129 | box_min = corners[0].copy() 130 | box_max = corners[0].copy() 131 | for corner in corners: 132 | # box_min.x = -8 133 | box_min.x = min(box_min.x, corner.x) 134 | box_min.y = min(box_min.y, corner.y) 135 | box_min.z = min(box_min.z, corner.z) 136 | 137 | box_max.x = max(box_max.x, corner.x) 138 | box_max.y = max(box_max.y, corner.y) 139 | box_max.z = max(box_max.z, corner.z) 140 | 141 | return { 142 | 'min': box_min, 143 | 'max': box_max, 144 | 'size': (box_max-box_min), 145 | 'center': box_min+(box_max-box_min)/2 146 | } 147 | 148 | 149 | def is_colliding(bbox_A, bbox_B): 150 | def is_collide_1D(A_min, A_max, B_min, B_max): 151 | # One line is inside the other 152 | length_A = A_max-A_min 153 | length_B = B_max-B_min 154 | center_A = A_min + length_A/2 155 | center_B = B_min + length_B/2 156 | 157 | return abs(center_A - center_B) <= (length_A+length_B)/2 158 | 159 | collide_x = is_collide_1D(bbox_A['min'].x, bbox_A['max'].x, bbox_B['min'].x, bbox_B['max'].x) 160 | collide_y = is_collide_1D(bbox_A['min'].y, bbox_A['max'].y, bbox_B['min'].y, bbox_B['max'].y) 161 | collide_z = is_collide_1D(bbox_A['min'].z, bbox_A['max'].z, bbox_B['min'].z, bbox_B['max'].z) 162 | 163 | return collide_x and collide_y and collide_z 164 | -------------------------------------------------------------------------------- /op_color_assign.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from . import utilities_color 4 | from . import utilities_ui 5 | from .settings import tt_settings, prefs 6 | 7 | 8 | gamma = 2.2 9 | 10 | 11 | class op(bpy.types.Operator): 12 | bl_idname = "uv.textools_color_assign" 13 | bl_label = "Assign Color" 14 | bl_description = "Assign color to selected Objects or faces in Edit Mode" 15 | bl_options = {'UNDO'} 16 | 17 | index: bpy.props.IntProperty(description="Color Index", default=0) 18 | 19 | @classmethod 20 | def poll(cls, context): 21 | if bpy.context.area.ui_type != 'UV': 22 | return False 23 | if not bpy.context.active_object: 24 | return False 25 | if bpy.context.active_object not in bpy.context.selected_objects: 26 | return False 27 | if bpy.context.active_object.type != 'MESH': 28 | return False 29 | return True 30 | 31 | def execute(self, context): 32 | assign_color(self, context, self.index) 33 | return {'FINISHED'} 34 | 35 | 36 | def assign_color(self, context, index): 37 | selected_obj = bpy.context.selected_objects.copy() 38 | 39 | previous_mode = 'OBJECT' 40 | if len(selected_obj) == 1: 41 | previous_mode = bpy.context.active_object.mode 42 | 43 | for obj in selected_obj: 44 | # Select object 45 | bpy.ops.object.mode_set(mode='OBJECT') 46 | bpy.ops.object.select_all(action='DESELECT') 47 | obj.select_set(True) 48 | bpy.context.view_layer.objects.active = obj 49 | 50 | # Enter Edit mode 51 | bpy.ops.object.mode_set(mode='EDIT') 52 | 53 | if previous_mode == 'OBJECT': 54 | bpy.ops.mesh.select_all(action='SELECT') 55 | 56 | if tt_settings().color_assign_mode == 'MATERIALS': 57 | # Verify material slots 58 | for _ in range(index+1): 59 | if index >= len(obj.material_slots): 60 | bpy.ops.object.material_slot_add() 61 | 62 | utilities_color.assign_slot(obj, index) 63 | 64 | # Assign to selection 65 | obj.active_material_index = index 66 | bpy.ops.object.material_slot_assign() 67 | 68 | else: # mode == VERTEXCOLORS 69 | color = utilities_color.get_color(index).copy() 70 | if prefs().bool_color_id_vertex_color_gamma: 71 | # Fix Gamma 72 | color[0] = pow(color[0], 1/gamma) 73 | color[1] = pow(color[1], 1/gamma) 74 | color[2] = pow(color[2], 1/gamma) 75 | 76 | # Manage Vertex Color layer 77 | context_override = utilities_ui.GetContextView3D() 78 | if not context_override: 79 | self.report({'ERROR_INVALID_INPUT'}, "This tool requires an available View3D view.") 80 | return {'CANCELLED'} 81 | if 'TexTools_colorID' not in obj.data.vertex_colors: 82 | obj.data.vertex_colors.new(name='TexTools_colorID') 83 | obj.data.vertex_colors['TexTools_colorID'].active = True 84 | 85 | # Paint 86 | bpy.ops.object.mode_set(mode='VERTEX_PAINT') 87 | bpy.context.tool_settings.vertex_paint.brush.color = color 88 | bpy.context.object.data.use_paint_mask = True 89 | with bpy.context.temp_override(**context_override): 90 | bpy.ops.paint.vertex_color_set() 91 | bpy.context.object.data.use_paint_mask = False 92 | 93 | # restore mode 94 | bpy.ops.object.mode_set(mode='OBJECT') 95 | bpy.ops.object.select_all(action='DESELECT') 96 | for obj in selected_obj: 97 | obj.select_set(True) 98 | bpy.ops.object.mode_set(mode=previous_mode) 99 | 100 | # Show Material or Data Tab 101 | utilities_color.update_properties_tab() 102 | 103 | # Change View mode 104 | utilities_color.update_view_mode() 105 | -------------------------------------------------------------------------------- /op_color_clear.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from . import utilities_color 4 | 5 | 6 | 7 | class op(bpy.types.Operator): 8 | bl_idname = "uv.textools_color_clear" 9 | bl_label = "Clear Colors" 10 | bl_description = "Clears the Materials or Vertex Colors on the active Object" 11 | bl_options = {'REGISTER', 'UNDO'} 12 | 13 | @classmethod 14 | def poll(cls, context): 15 | if bpy.context.area.ui_type != 'UV': 16 | return False 17 | if not bpy.context.active_object: 18 | return False 19 | if bpy.context.active_object not in bpy.context.selected_objects: 20 | return False 21 | if bpy.context.active_object.type != 'MESH': 22 | return False 23 | return True 24 | 25 | 26 | def execute(self, context): 27 | premode = bpy.context.active_object.mode 28 | clear_colors(self, context) 29 | bpy.ops.object.mode_set(mode=premode) 30 | return {'FINISHED'} 31 | 32 | 33 | 34 | def clear_colors(self, context): 35 | obj = bpy.context.active_object 36 | 37 | if bpy.context.scene.texToolsSettings.color_assign_mode == 'MATERIALS': 38 | previous_mode = bpy.context.active_object.mode 39 | bpy.ops.object.mode_set(mode='OBJECT') 40 | # Clear material slots 41 | count = len(obj.material_slots) 42 | for i in range(count): 43 | bpy.ops.object.material_slot_remove() 44 | # Delete materials if not used 45 | for material in bpy.data.materials: 46 | if utilities_color.material_prefix in material.name: 47 | if not material.users: 48 | bpy.data.materials.remove(material, do_unlink=True) 49 | bpy.ops.object.mode_set(mode=previous_mode) 50 | 51 | else: #mode == VERTEXCOLORS 52 | vclsNames = [vcl.name for vcl in obj.data.vertex_colors] 53 | if 'TexTools_colorID' in vclsNames : 54 | obj.data.vertex_colors.remove(obj.data.vertex_colors['TexTools_colorID']) 55 | 56 | # Show Material or Data Tab 57 | utilities_color.update_properties_tab() 58 | 59 | # Change View mode 60 | utilities_color.update_view_mode() 61 | 62 | # Enter and exit Edit Mode to force set a real vertex colors layer as active 63 | bpy.ops.object.mode_set(mode='EDIT') 64 | bpy.ops.object.mode_set(mode='OBJECT') 65 | -------------------------------------------------------------------------------- /op_color_convert_texture.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | import math 4 | 5 | from . import utilities_color 6 | from . import utilities_bake 7 | from .settings import tt_settings 8 | 9 | 10 | material_prefix = "TT_atlas_" 11 | gamma = 2.2 12 | 13 | 14 | 15 | class op(bpy.types.Operator): 16 | bl_idname = "uv.textools_color_convert_to_texture" 17 | bl_label = "Pack Texture" 18 | bl_description = "Pack ID Colors into single texture and UVs" 19 | bl_options = {'REGISTER', 'UNDO'} 20 | 21 | @classmethod 22 | def poll(cls, context): 23 | if bpy.context.area.ui_type != 'UV': 24 | return False 25 | if not bpy.context.active_object: 26 | return False 27 | if bpy.context.active_object not in bpy.context.selected_objects: 28 | return False 29 | if len(bpy.context.selected_objects) != 1: 30 | return False 31 | if bpy.context.active_object.type != 'MESH': 32 | return False 33 | return True 34 | 35 | 36 | def execute(self, context): 37 | pack_texture(self, context) 38 | return {'FINISHED'} 39 | 40 | 41 | 42 | def pack_texture(self, context): 43 | obj = bpy.context.active_object 44 | name = material_prefix+obj.name 45 | 46 | if obj.mode != 'OBJECT': 47 | bpy.ops.object.mode_set(mode='OBJECT') 48 | 49 | # Determine size 50 | size_pixel = 8 51 | size_square = math.ceil(math.sqrt(tt_settings().color_ID_count)) 52 | size_image = size_square * size_pixel 53 | size_image_pow = int(math.pow(2, math.ceil(math.log(size_image, 2)))) 54 | 55 | # Maximize pixel size 56 | size_pixel = math.floor(size_image_pow/size_square) 57 | 58 | print("{0} colors = {1} x {1} = ({2}pix) {3} x {3} | {4} x {4}".format( 59 | tt_settings().color_ID_count, 60 | size_square, 61 | size_pixel, 62 | size_image, 63 | size_image_pow 64 | )) 65 | 66 | # Create image 67 | image = bpy.data.images.new(name, width=size_image_pow, height=size_image_pow) 68 | pixels = [None] * size_image_pow * size_image_pow 69 | 70 | # Black pixels 71 | for x in range(size_image_pow): 72 | for y in range(size_image_pow): 73 | pixels[(y * size_image_pow) + x] = [0, 0, 0, 1] 74 | 75 | # Pixels 76 | for c in range(tt_settings().color_ID_count): 77 | x = c % size_square 78 | y = math.floor(c/size_square) 79 | color = utilities_color.get_color(c).copy() 80 | for i in range(3): 81 | color[i] = pow(color[i] , 1.0/gamma) 82 | 83 | for sx in range(size_pixel): 84 | for sy in range(size_pixel): 85 | _x = x*size_pixel + sx 86 | _y = y*size_pixel + sy 87 | pixels[(_y * size_image_pow) + _x] = [color[0], color[1], color[2], 1] 88 | 89 | 90 | # flatten list & assign pixels 91 | pixels = [chan for px in pixels for chan in px] 92 | image.pixels = pixels 93 | 94 | # Set background image 95 | for area in bpy.context.screen.areas: 96 | if area.ui_type == 'UV': 97 | area.spaces[0].image = image 98 | 99 | # Edit mesh 100 | bpy.ops.object.mode_set(mode='EDIT') 101 | bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE') 102 | bpy.ops.mesh.select_all(action='SELECT') 103 | # bpy.ops.uv.smart_project(angle_limit=1) 104 | bpy.ops.uv.unwrap(method='ANGLE_BASED', margin=0.0078) 105 | 106 | 107 | bm = bmesh.from_edit_mesh(bpy.context.active_object.data) 108 | uv_layers = bm.loops.layers.uv.verify() 109 | 110 | for face in bm.faces: 111 | index = face.material_index 112 | 113 | # Get UV coordinates for index 114 | x = index%size_square 115 | y = math.floor(index/size_square) 116 | 117 | x*= (size_pixel / size_image_pow) 118 | y*= (size_pixel / size_image_pow) 119 | x+= size_pixel/size_image_pow/2 120 | y+= size_pixel/size_image_pow/2 121 | 122 | for loop in face.loops: 123 | loop[uv_layers].uv = (x, y) 124 | 125 | # Remove Slots & add one 126 | bpy.ops.object.mode_set(mode='OBJECT') 127 | bpy.ops.uv.textools_color_clear() 128 | bpy.ops.object.material_slot_add() 129 | 130 | #Create material with image 131 | obj.material_slots[0].material = utilities_bake.get_image_material(image) 132 | 133 | #Display UVs 134 | bpy.ops.object.mode_set(mode='EDIT') 135 | 136 | # Switch textured shading 137 | for area in bpy.context.screen.areas: 138 | if area.type == 'VIEW_3D': 139 | for space in area.spaces: 140 | if space.type == 'VIEW_3D': 141 | if space.shading.type == 'RENDERED': 142 | continue 143 | elif space.shading.type == 'MATERIAL': 144 | continue 145 | space.shading.type = 'SOLID' 146 | space.shading.color_type = 'TEXTURE' 147 | 148 | bpy.ops.ui.textools_popup('INVOKE_DEFAULT', message=f"Packed texture with {tt_settings().color_ID_count} color IDs") 149 | -------------------------------------------------------------------------------- /op_color_convert_vertex_colors.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | 4 | from . import utilities_color 5 | from . import utilities_bake 6 | from . import utilities_ui 7 | 8 | 9 | gamma = 2.2 10 | 11 | 12 | 13 | class op(bpy.types.Operator): 14 | bl_idname = "uv.textools_color_convert_to_vertex_colors" 15 | bl_label = "Pack Texture" 16 | bl_description = "Pack ID Colors into single texture and UVs" 17 | bl_options = {'REGISTER', 'UNDO'} 18 | 19 | @classmethod 20 | def poll(cls, context): 21 | if bpy.context.area.ui_type != 'UV': 22 | return False 23 | if not bpy.context.active_object: 24 | return False 25 | if bpy.context.active_object not in bpy.context.selected_objects: 26 | return False 27 | if len(bpy.context.selected_objects) != 1: 28 | return False 29 | if bpy.context.active_object.type != 'MESH': 30 | return False 31 | return True 32 | 33 | 34 | def execute(self, context): 35 | convert_vertex_colors(self, context) 36 | return {'FINISHED'} 37 | 38 | 39 | 40 | def convert_vertex_colors(self, context): 41 | context_override = utilities_ui.GetContextView3D() 42 | if not context_override: 43 | self.report({'ERROR_INVALID_INPUT'}, "This tool requires an available View3D view.") 44 | return {'CANCELLED'} 45 | 46 | obj = bpy.context.active_object 47 | 48 | for i in range(len(obj.material_slots)): 49 | slot = obj.material_slots[i] 50 | if slot.material: 51 | 52 | # Select related faces 53 | bpy.ops.object.mode_set(mode='EDIT') 54 | bpy.ops.mesh.select_all(action='DESELECT') 55 | 56 | bm = bmesh.from_edit_mesh(bpy.context.active_object.data) 57 | for face in bm.faces: 58 | if face.material_index == i: 59 | face.select = True 60 | 61 | color = utilities_color.get_color(i).copy() 62 | # Fix Gamma 63 | color[0] = pow(color[0],1/gamma) 64 | color[1] = pow(color[1],1/gamma) 65 | color[2] = pow(color[2],1/gamma) 66 | 67 | utilities_bake.assign_vertex_color(obj) 68 | 69 | bpy.ops.object.mode_set(mode='VERTEX_PAINT') 70 | bpy.context.tool_settings.vertex_paint.brush.color = color 71 | bpy.context.object.data.use_paint_mask = True 72 | with bpy.context.temp_override(**context_override): 73 | bpy.ops.paint.vertex_color_set() 74 | 75 | # Back to object mode 76 | bpy.ops.object.mode_set(mode='VERTEX_PAINT') 77 | bpy.context.object.data.use_paint_mask = False 78 | 79 | # Switch Properties Tab 80 | for area in bpy.context.screen.areas: 81 | if area.type == 'PROPERTIES': 82 | for space in area.spaces: 83 | if space.type == 'PROPERTIES': 84 | space.context = 'DATA' 85 | 86 | # Switch textured shading 87 | for area in bpy.context.screen.areas: 88 | if area.type == 'VIEW_3D': 89 | for space in area.spaces: 90 | if space.type == 'VIEW_3D': 91 | space.shading.type = 'SOLID' 92 | 93 | # Clear materials? 94 | #bpy.ops.uv.textools_color_clear() 95 | 96 | bpy.ops.ui.textools_popup('INVOKE_DEFAULT', message="Vertex colors assigned") 97 | -------------------------------------------------------------------------------- /op_color_from_directions.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | 4 | from . import utilities_color 5 | 6 | 7 | 8 | class op(bpy.types.Operator): 9 | bl_idname = "uv.textools_color_from_directions" 10 | bl_label = "Color Directions" 11 | bl_description = "Assign a color ID to different face directions" 12 | bl_options = {'REGISTER', 'UNDO'} 13 | 14 | directions : bpy.props.EnumProperty(items= 15 | [('2', '2', 'Top & Bottom, Sides'), 16 | ('3', '3', 'Top & Bottom, Left & Right, Front & Back'), 17 | ('4', '4', 'Top, Left & Right, Front & Back, Bottom'), 18 | ('6', '6', 'All sides')], 19 | name = "Directions", 20 | default = '3' 21 | ) 22 | 23 | def invoke(self, context, event): 24 | wm = context.window_manager 25 | return wm.invoke_props_dialog(self) 26 | 27 | @classmethod 28 | def poll(cls, context): 29 | if bpy.context.area.ui_type != 'UV': 30 | return False 31 | if not bpy.context.active_object: 32 | return False 33 | if bpy.context.active_object not in bpy.context.selected_objects: 34 | return False 35 | if len(bpy.context.selected_objects) != 1: 36 | return False 37 | if bpy.context.active_object.type != 'MESH': 38 | return False 39 | return True 40 | 41 | 42 | def execute(self, context): 43 | color_elements(self, context) 44 | return {'FINISHED'} 45 | 46 | 47 | 48 | def color_elements(self, context): 49 | obj = bpy.context.active_object 50 | 51 | # Setup Edit & Face mode 52 | if obj.mode != 'EDIT': 53 | bpy.ops.object.mode_set(mode='EDIT') 54 | bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE') 55 | 56 | # Collect groups 57 | bm = bmesh.from_edit_mesh(obj.data) 58 | 59 | face_directions = { 60 | 'top':[], 61 | 'bottom':[], 62 | 'left':[], 63 | 'right':[], 64 | 'front':[], 65 | 'back':[] 66 | } 67 | 68 | for face in bm.faces: 69 | # Find dominant direction 70 | abs_x = abs(face.normal.x) 71 | abs_y = abs(face.normal.y) 72 | abs_z = abs(face.normal.z) 73 | max_xyz = max(abs_x, abs_y, abs_z) 74 | 75 | if max_xyz == abs_x: 76 | if face.normal.x > 0: 77 | face_directions['right'].append(face.index) 78 | else: 79 | face_directions['left'].append(face.index) 80 | elif max_xyz == abs_y: 81 | if face.normal.y > 0: 82 | face_directions['front'].append(face.index) 83 | else: 84 | face_directions['back'].append(face.index) 85 | elif max_xyz == abs_z: 86 | if face.normal.z > 0: 87 | face_directions['top'].append(face.index) 88 | else: 89 | face_directions['bottom'].append(face.index) 90 | 91 | count = int(self.directions) 92 | bpy.context.scene.texToolsSettings.color_ID_count = count 93 | 94 | groups = [] 95 | 96 | if self.directions == '2': 97 | groups.append(face_directions['top']+face_directions['bottom']) 98 | groups.append(face_directions['left']+face_directions['right']+face_directions['front']+face_directions['back']) 99 | if self.directions == '3': 100 | groups.append(face_directions['top']+face_directions['bottom']) 101 | groups.append(face_directions['left']+face_directions['right']) 102 | groups.append(face_directions['front']+face_directions['back']) 103 | elif self.directions == '4': 104 | groups.append(face_directions['top']) 105 | groups.append(face_directions['left']+face_directions['right']) 106 | groups.append(face_directions['front']+face_directions['back']) 107 | groups.append(face_directions['bottom']) 108 | elif self.directions == '6': 109 | groups.append(face_directions['top']) 110 | groups.append(face_directions['right']) 111 | groups.append(face_directions['left']) 112 | groups.append(face_directions['front']) 113 | groups.append(face_directions['back']) 114 | groups.append(face_directions['bottom']) 115 | 116 | # Assign Groups to colors 117 | index_color = 0 118 | for group in groups: 119 | # # rebuild bmesh data (e.g. left edit mode previous loop) 120 | bm = bmesh.from_edit_mesh(bpy.context.active_object.data) 121 | if hasattr(bm.faces, "ensure_lookup_table"): 122 | bm.faces.ensure_lookup_table() 123 | 124 | # Select group 125 | bpy.ops.mesh.select_all(action='DESELECT') 126 | for index_face in group: 127 | bm.faces[index_face].select = True 128 | 129 | # Assign to selection 130 | bpy.ops.uv.textools_color_assign(index=index_color) 131 | 132 | index_color = (index_color+1) % bpy.context.scene.texToolsSettings.color_ID_count 133 | 134 | bpy.ops.object.mode_set(mode='OBJECT') 135 | utilities_color.validate_face_colors(obj) 136 | -------------------------------------------------------------------------------- /op_color_from_elements.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | 4 | from . import utilities_color 5 | 6 | 7 | 8 | class op(bpy.types.Operator): 9 | bl_idname = "uv.textools_color_from_elements" 10 | bl_label = "Color Elements" 11 | bl_description = "Assign a color ID to each mesh element" 12 | bl_options = {'REGISTER', 'UNDO'} 13 | 14 | @classmethod 15 | def poll(cls, context): 16 | if bpy.context.area.ui_type != 'UV': 17 | return False 18 | if not bpy.context.active_object: 19 | return False 20 | if bpy.context.active_object not in bpy.context.selected_objects: 21 | return False 22 | if len(bpy.context.selected_objects) != 1: 23 | return False 24 | if bpy.context.active_object.type != 'MESH': 25 | return False 26 | return True 27 | 28 | 29 | def execute(self, context): 30 | color_elements(self, context) 31 | return {'FINISHED'} 32 | 33 | 34 | 35 | def color_elements(self, context): 36 | obj = bpy.context.active_object 37 | 38 | # Setup Edit & Face mode 39 | if obj.mode != 'EDIT': 40 | bpy.ops.object.mode_set(mode='EDIT') 41 | bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE') 42 | 43 | # Collect groups 44 | bm = bmesh.from_edit_mesh(bpy.context.active_object.data) 45 | 46 | faces_indices_processed = set(bm.faces) 47 | groups = [] 48 | 49 | while faces_indices_processed: 50 | # Select face & extend 51 | bpy.ops.mesh.select_all(action='DESELECT') 52 | face = faces_indices_processed.pop() 53 | face.select = True 54 | bpy.ops.mesh.select_linked(delimit={'NORMAL'}) 55 | 56 | faces = {f for f in faces_indices_processed if f.select} 57 | faces_indices_processed.difference_update(faces) 58 | 59 | faces.add(face) 60 | groups.append(faces) 61 | 62 | 63 | # Assign color count (caps automatically e.g. max 20) 64 | bpy.context.scene.texToolsSettings.color_ID_count = len(groups) 65 | gamma = 2.2 66 | 67 | for i in range(bpy.context.scene.texToolsSettings.color_ID_count): 68 | color = utilities_color.get_color_id(i, bpy.context.scene.texToolsSettings.color_ID_count) 69 | # Fix Gamma 70 | color[0] = pow(color[0] , gamma) 71 | color[1] = pow(color[1] , gamma) 72 | color[2] = pow(color[2], gamma) 73 | utilities_color.set_color(i, color) 74 | 75 | # Assign Groups to colors 76 | index_color = 0 77 | for group in groups: 78 | bpy.ops.mesh.select_all(action='DESELECT') 79 | for face in group: 80 | face.select = True 81 | 82 | for i in range(index_color+1): 83 | if index_color >= len(obj.material_slots): 84 | bpy.ops.object.material_slot_add() 85 | 86 | utilities_color.assign_slot(obj, index_color) 87 | 88 | # Assign to selection 89 | obj.active_material_index = index_color 90 | bpy.ops.object.material_slot_assign() 91 | 92 | index_color = (index_color+1) % bpy.context.scene.texToolsSettings.color_ID_count 93 | 94 | bpy.ops.object.mode_set(mode='OBJECT') 95 | utilities_color.validate_face_colors(obj) 96 | -------------------------------------------------------------------------------- /op_color_from_materials.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from . import utilities_color 4 | 5 | 6 | 7 | class op(bpy.types.Operator): 8 | bl_idname = "uv.textools_color_from_materials" 9 | bl_label = "Color Elements" 10 | bl_description = "Assign a color ID to each mesh material slot" 11 | bl_options = {'REGISTER', 'UNDO'} 12 | 13 | @classmethod 14 | def poll(cls, context): 15 | if bpy.context.area.ui_type != 'UV': 16 | return False 17 | if not bpy.context.active_object: 18 | return False 19 | if bpy.context.active_object not in bpy.context.selected_objects: 20 | return False 21 | if len(bpy.context.selected_objects) != 1: 22 | return False 23 | if bpy.context.active_object.type != 'MESH': 24 | return False 25 | return True 26 | 27 | 28 | def execute(self, context): 29 | color_materials(self, context) 30 | return {'FINISHED'} 31 | 32 | 33 | 34 | def color_materials(self, context): 35 | obj = bpy.context.active_object 36 | 37 | for s in range(len(obj.material_slots)): 38 | slot = obj.material_slots[s] 39 | if slot.material: 40 | utilities_color.assign_slot(obj, s) 41 | 42 | utilities_color.validate_face_colors(obj) 43 | -------------------------------------------------------------------------------- /op_color_io_export.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from . import utilities_color 4 | from .settings import tt_settings 5 | 6 | 7 | class op(bpy.types.Operator): 8 | bl_idname = "uv.textools_color_io_export" 9 | bl_label = "Export" 10 | bl_description = "Export current color palette to clipboard" 11 | 12 | @classmethod 13 | def poll(cls, context): 14 | if bpy.context.area.ui_type != 'UV': 15 | return False 16 | return True 17 | 18 | def execute(self, context): 19 | export_colors(self, context) 20 | return {'FINISHED'} 21 | 22 | 23 | def export_colors(self, context): 24 | hex_colors = [] 25 | for i in range(tt_settings().color_ID_count): 26 | color = getattr(tt_settings(), f"color_ID_color_{i}") 27 | hex_colors.append( utilities_color.color_to_hex( color) ) 28 | 29 | bpy.context.window_manager.clipboard = ", ".join(hex_colors) 30 | bpy.ops.ui.textools_popup('INVOKE_DEFAULT', message=f"{len(hex_colors)}x colors copied to clipboard") 31 | -------------------------------------------------------------------------------- /op_color_io_import.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import string 3 | 4 | from . import utilities_color 5 | 6 | 7 | 8 | class op(bpy.types.Operator): 9 | bl_idname = "uv.textools_color_io_import" 10 | bl_label = "Import" 11 | bl_description = "Import hex colors from the clipboard as current color palette" 12 | 13 | @classmethod 14 | def poll(cls, context): 15 | if bpy.context.area.ui_type != 'UV': 16 | return False 17 | return True 18 | 19 | 20 | def execute(self, context): 21 | import_colors(self, context) 22 | return {'FINISHED'} 23 | 24 | 25 | 26 | def import_colors(self, context): 27 | # Clipboard hex strings 28 | hex_strings = bpy.context.window_manager.clipboard.split(',') 29 | 30 | for i in range(len(hex_strings)): 31 | hex_strings[i] = hex_strings[i].strip().strip('#') 32 | if len(hex_strings[i]) != 6 or not all(c in string.hexdigits for c in hex_strings[i]): 33 | # Incorrect format 34 | self.report({'ERROR_INVALID_INPUT'}, f"Incorrect hex format '{hex_strings[i]}' use a #RRGGBB pattern") 35 | return 36 | else: 37 | name = "color_ID_color_{}".format(i) 38 | if hasattr(bpy.context.scene.texToolsSettings, name): 39 | # Color Index exists 40 | color = utilities_color.hex_to_color( hex_strings[i] ) 41 | setattr(bpy.context.scene.texToolsSettings, name, color) 42 | else: 43 | # More colors imported than supported 44 | self.report({'ERROR_INVALID_INPUT'}, f"Only {i}x colors have been imported instead of {len(hex_strings)}x") 45 | return 46 | 47 | # Set number of colors 48 | bpy.context.scene.texToolsSettings.color_ID_count = len(hex_strings) 49 | 50 | bpy.ops.ui.textools_popup('INVOKE_DEFAULT', message=f"{len(hex_strings)}x colors imported from clipboard") 51 | -------------------------------------------------------------------------------- /op_color_select.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | 4 | from . import utilities_color 5 | 6 | 7 | 8 | class op(bpy.types.Operator): 9 | bl_idname = "uv.textools_color_select" 10 | bl_label = "Select by Color" 11 | bl_description = "Select faces by this color" 12 | bl_options = {'UNDO'} 13 | 14 | index : bpy.props.IntProperty(description="Color Index", default=0) 15 | 16 | @classmethod 17 | def poll(cls, context): 18 | if bpy.context.area.ui_type != 'UV': 19 | return False 20 | if not bpy.context.active_object: 21 | return False 22 | if bpy.context.active_object not in bpy.context.selected_objects: 23 | return False 24 | if len(bpy.context.selected_objects) != 1: 25 | return False 26 | if bpy.context.active_object.type != 'MESH': 27 | return False 28 | return True 29 | 30 | 31 | def execute(self, context): 32 | select_color(self, context, self.index) 33 | return {'FINISHED'} 34 | 35 | 36 | 37 | def select_color(self, context, index): 38 | obj = bpy.context.active_object 39 | 40 | # Check for missing slots, materials,.. 41 | if index >= len(obj.material_slots): 42 | self.report({'ERROR_INVALID_INPUT'}, "No material slot for color '{}' found".format(index) ) 43 | return 44 | 45 | if not obj.material_slots[index].material: 46 | self.report({'ERROR_INVALID_INPUT'}, "No material found for material slot '{}'".format(index) ) 47 | return 48 | 49 | if obj.mode != 'EDIT': 50 | bpy.ops.object.mode_set(mode='EDIT') 51 | 52 | # Select faces 53 | bm = bmesh.from_edit_mesh(obj.data) 54 | bpy.ops.mesh.select_all(action='DESELECT') 55 | for face in bm.faces: 56 | if face.material_index == index: 57 | face.select = True 58 | 59 | # Show Material Tab 60 | utilities_color.update_properties_tab() 61 | 62 | #Change View mode 63 | utilities_color.update_view_mode() 64 | -------------------------------------------------------------------------------- /op_color_select_vertex.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | from mathutils import Color 4 | 5 | from . import utilities_color 6 | 7 | 8 | class op(bpy.types.Operator): 9 | bl_idname = "uv.textools_color_select_vertex" 10 | bl_label = "Select by Vertex Color" 11 | bl_description = "Select faces by this vertex color" 12 | bl_options = {'UNDO'} 13 | 14 | index : bpy.props.IntProperty(description="Color Index", default=0) 15 | 16 | @classmethod 17 | def poll(cls, context): 18 | if bpy.context.area.ui_type != 'UV': 19 | return False 20 | if not bpy.context.active_object: 21 | return False 22 | if bpy.context.active_object not in bpy.context.selected_objects: 23 | return False 24 | if len(bpy.context.selected_objects) != 1: 25 | return False 26 | if bpy.context.active_object.type != 'MESH': 27 | return False 28 | return True 29 | 30 | 31 | def execute(self, context): 32 | select_color(self, context, self.index) 33 | return {'FINISHED'} 34 | 35 | 36 | 37 | def select_color(self, context, index): 38 | obj = bpy.context.active_object 39 | 40 | if not obj.data.vertex_colors: 41 | self.report({'ERROR_INVALID_INPUT'}, "Object has no vertex colors") 42 | return 43 | 44 | if obj.mode != 'EDIT': 45 | bpy.ops.object.mode_set(mode='EDIT') 46 | 47 | bpy.ops.mesh.select_all(action='DESELECT') 48 | 49 | bpy.ops.object.mode_set(mode="OBJECT") 50 | colors = obj.data.vertex_colors.active.data 51 | selected_polygons = list(filter(lambda p: p.select, obj.data.polygons)) 52 | 53 | target_color = utilities_color.get_color(index).copy() 54 | 55 | # op_color_assign attempts to fix the gamma. So we need to do the same here 56 | # so we can match what was assigned 57 | gamma = 2.2 58 | target_color[0] = pow(target_color[0],1/gamma) 59 | target_color[1] = pow(target_color[1],1/gamma) 60 | target_color[2] = pow(target_color[2],1/gamma) 61 | 62 | # Due the averaging color calculation we need to have a threshold. 63 | # Depending on the colors this might need to be adjusted by the user 64 | threshold = bpy.context.scene.texToolsSettings.vertex_color_threshold 65 | 66 | r = g = b = 0 67 | for p in obj.data.polygons: 68 | r = g = b = 0 69 | for i in p.loop_indices: 70 | c = colors[i].color 71 | r += c[0] 72 | g += c[1] 73 | b += c[2] 74 | r /= p.loop_total 75 | g /= p.loop_total 76 | b /= p.loop_total 77 | source_color = Color((r, g, b)) 78 | 79 | if (abs(source_color.r - target_color.r) < threshold and 80 | abs(source_color.g - target_color.g) < threshold and 81 | abs(source_color.b - target_color.b) < threshold): 82 | 83 | p.select = True 84 | 85 | bpy.ops.object.editmode_toggle() 86 | -------------------------------------------------------------------------------- /op_island_align_edge.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | import math 4 | 5 | from . import utilities_uv 6 | 7 | class op(bpy.types.Operator): 8 | bl_idname = "uv.textools_island_align_edge" 9 | bl_label = "Align Island by Edge" 10 | bl_description = "Align Islands by selected Edge" 11 | bl_options = {'REGISTER', 'UNDO'} 12 | 13 | @classmethod 14 | def poll(cls, context): 15 | if not bpy.context.active_object: 16 | return False 17 | if bpy.context.active_object.type != 'MESH': 18 | return False 19 | if bpy.context.active_object.mode != 'EDIT': 20 | return False 21 | if bpy.context.scene.tool_settings.use_uv_select_sync: 22 | return False 23 | if bpy.context.scene.tool_settings.uv_select_mode not in ('EDGE', 'VERTEX'): 24 | return False 25 | return True 26 | 27 | def execute(self, context): 28 | return main(self, context) 29 | 30 | def main(self, context): 31 | counter = 0 32 | for obj in utilities_uv.selected_unique_objects_in_mode_with_uv(): 33 | bm = bmesh.from_edit_mesh(obj.data) 34 | uv_layers = bm.loops.layers.uv.verify() 35 | 36 | if not any(l[uv_layers].select_edge for f in bm.faces for l in f.loops): 37 | continue 38 | 39 | counter += 1 40 | for island in utilities_uv.get_selected_islands(bm, uv_layers, selected=False): 41 | luvs = (l for f in island for l in f.loops) 42 | for l in luvs: 43 | if l[uv_layers].select_edge: 44 | uv_vert0 = l[uv_layers].uv 45 | uv_vert1 = l.link_loop_next[uv_layers].uv 46 | diff = uv_vert1 - uv_vert0 47 | current_angle = math.atan2(diff.x, diff.y) 48 | angle_to_rotate = round(current_angle / (math.pi / 2)) * (math.pi / 2) - current_angle 49 | pivot = uv_vert0 + diff/2 50 | utilities_uv.rotate_island(island, uv_layers, angle_to_rotate, pivot) 51 | break 52 | bmesh.update_edit_mesh(obj.data, loop_triangles=False, destructive=False) 53 | 54 | if counter: 55 | return {'FINISHED'} 56 | 57 | self.report({'WARNING'}, "No object to align") 58 | return {'CANCELLED'} 59 | -------------------------------------------------------------------------------- /op_island_align_sort.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | 4 | from mathutils import Vector 5 | from . import utilities_uv 6 | from .utilities_bbox import BBox 7 | 8 | class op(bpy.types.Operator): 9 | bl_idname = "uv.textools_island_align_sort" 10 | bl_label = "Align & Sort" 11 | bl_description = "Rotate UV islands to minimal bounds and sort them horizontally or vertically" 12 | bl_options = {'REGISTER', 'UNDO'} 13 | 14 | is_vertical: bpy.props.BoolProperty(name='Vertical', description="Vertical or Horizontal orientation", default=True) 15 | align: bpy.props.BoolProperty(name='Align', description="Align Island orientation", default=True) 16 | padding: bpy.props.FloatProperty(name='Padding', description="Padding between UV islands", default=0.05) 17 | 18 | @classmethod 19 | def poll(cls, context): 20 | if not bpy.context.active_object: 21 | return False 22 | if bpy.context.active_object.type != 'MESH': 23 | return False 24 | if bpy.context.active_object.mode != 'EDIT': 25 | return False 26 | return True 27 | 28 | def execute(self, context): 29 | general_bbox = BBox() 30 | all_groups = [] 31 | update_obj = [] 32 | bmeshes = [] # bmesh objects must be saved, otherwise they will be deallocated 33 | selected_objs = utilities_uv.selected_unique_objects_in_mode_with_uv() 34 | 35 | if not selected_objs: 36 | self.report({'ERROR_INVALID_INPUT'}, "No object with UV.") 37 | return {'CANCELLED'} 38 | 39 | for obj in selected_objs: 40 | bm = bmesh.from_edit_mesh(obj.data) 41 | uv_layer = bm.loops.layers.uv.verify() 42 | islands = utilities_uv.get_selected_islands(bm, uv_layer, selected=False, extend_selection_to_islands=True) 43 | if not islands: 44 | continue 45 | for i, island in enumerate(islands): 46 | bbox_pre = BBox.calc_bbox_uv(island, uv_layer) 47 | general_bbox.union(bbox_pre) 48 | if self.align: 49 | angle = utilities_uv.calc_min_align_angle(island, uv_layer) 50 | utilities_uv.rotate_island(island, uv_layer, angle) 51 | 52 | bbox = BBox.calc_bbox_uv(island, uv_layer) if self.align else bbox_pre 53 | all_groups.append((island, bbox, uv_layer)) 54 | bmeshes.append(bm) 55 | update_obj.append(obj) 56 | 57 | if not all_groups: 58 | return {'CANCELLED'} 59 | 60 | all_groups.sort(key=lambda x: x[1].max_lenght, reverse=True) 61 | 62 | # transform 63 | if self.is_vertical: 64 | margin_x = general_bbox.xmin 65 | margin_y = general_bbox.ymin 66 | 67 | for island, bbox, uv_layer in all_groups: 68 | delta = Vector((margin_x, margin_y)) - bbox.left_bottom 69 | utilities_uv.translate_island(island, uv_layer, delta) 70 | margin_y += self.padding + bbox.height 71 | else: 72 | margin_x = general_bbox.xmin 73 | margin_y = general_bbox.ymin 74 | 75 | for island, bbox, uv_layer in all_groups: 76 | delta = Vector((margin_x, margin_y)) - bbox.min 77 | utilities_uv.translate_island(island, uv_layer, delta) 78 | margin_x += self.padding + bbox.width 79 | 80 | for obj in update_obj: 81 | bmesh.update_edit_mesh(obj.data) 82 | 83 | return {'FINISHED'} 84 | -------------------------------------------------------------------------------- /op_island_centralize.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | 4 | from . import utilities_uv 5 | from .utilities_bbox import BBox 6 | from mathutils import Vector 7 | 8 | 9 | 10 | class op(bpy.types.Operator): 11 | bl_idname = "uv.textools_island_centralize" 12 | bl_label = "Centralize" 13 | bl_description = "Move the selected faces the closest possible to the 0-1 UV area without changes in the textured object" 14 | bl_options = {'REGISTER', 'UNDO'} 15 | 16 | @classmethod 17 | def poll(cls, context): 18 | if not bpy.context.active_object: 19 | return False 20 | if bpy.context.active_object.mode != 'EDIT': 21 | return False 22 | return True 23 | 24 | def execute(self, context): 25 | _, column, row = utilities_uv.get_UDIM_tile_coords(bpy.context.active_object) 26 | return self.centralize(column, row) 27 | 28 | @staticmethod 29 | def centralize(column, row): 30 | selected_objs = utilities_uv.selected_unique_objects_in_mode_with_uv() 31 | update_obj = [] 32 | for obj in selected_objs: 33 | bm = bmesh.from_edit_mesh(obj.data) 34 | uv_layer = bm.loops.layers.uv.verify() 35 | islands = utilities_uv.get_selected_islands(bm, uv_layer) 36 | 37 | if not islands: 38 | continue 39 | 40 | changed = False 41 | for island in islands: 42 | center = BBox.calc_bbox_uv(island, uv_layer).center 43 | delta = Vector((round(-center.x + 0.5) + column, round(-center.y + 0.5) + row)) 44 | if delta != Vector((0, 0)): 45 | changed = True 46 | utilities_uv.translate_island(island, uv_layer, delta) 47 | if changed: 48 | update_obj.append(obj) 49 | 50 | if not update_obj: 51 | return {'CANCELLED'} 52 | 53 | for obj in update_obj: 54 | bmesh.update_edit_mesh(obj.data) 55 | 56 | return {'FINISHED'} 57 | -------------------------------------------------------------------------------- /op_island_mirror.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | 4 | 5 | class op(bpy.types.Operator): 6 | bl_idname = "uv.textools_island_mirror" 7 | bl_label = "Symmetry" 8 | bl_description = "Mirror selected faces with respect to the global Rotation/Scaling Pivot" 9 | bl_options = {'REGISTER', 'UNDO'} 10 | 11 | is_vertical : bpy.props.BoolProperty(name="is_vertical", options={'HIDDEN'}) 12 | 13 | @classmethod 14 | def poll(cls, context): 15 | if bpy.context.area.ui_type != 'UV': 16 | return False 17 | if not bpy.context.active_object: 18 | return False 19 | if bpy.context.active_object.type != 'MESH': 20 | return False 21 | if bpy.context.active_object.mode != 'EDIT': 22 | return False 23 | if not bpy.context.object.data.uv_layers: 24 | return False 25 | return True 26 | 27 | 28 | def execute(self, context): 29 | #bpy.ops.uv.select_linked() 30 | is_vertical = self.is_vertical 31 | if is_vertical: 32 | bpy.ops.transform.mirror(orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(False, True, False)) 33 | else: 34 | bpy.ops.transform.mirror(orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(True, False, False)) 35 | 36 | return {'FINISHED'} 37 | -------------------------------------------------------------------------------- /op_island_rotate_90.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from . import settings 4 | 5 | 6 | 7 | class op(bpy.types.Operator): 8 | bl_idname = "uv.textools_island_rotate_90" 9 | bl_label = "Rotate 90 degrees" 10 | bl_description = "Rotate the selection 90 degrees left or right around the global Rotation/Scaling Pivot" 11 | bl_options = {'REGISTER', 'UNDO'} 12 | 13 | angle : bpy.props.FloatProperty(name="Angle", options={'HIDDEN'}) 14 | 15 | @classmethod 16 | def poll(cls, context): 17 | if bpy.context.area.ui_type != 'UV': 18 | return False 19 | if not bpy.context.active_object: 20 | return False 21 | if bpy.context.active_object.type != 'MESH': 22 | return False 23 | if bpy.context.active_object.mode != 'EDIT': 24 | return False 25 | if not bpy.context.object.data.uv_layers: 26 | return False 27 | return True 28 | 29 | 30 | def execute(self, context): 31 | sync = bpy.context.scene.tool_settings.use_uv_select_sync 32 | if sync: 33 | selection_mode = tuple(bpy.context.scene.tool_settings.mesh_select_mode) 34 | bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE') 35 | 36 | angle = - self.angle 37 | if settings.bversion == 2.83 or settings.bversion == 2.91: 38 | angle = -angle 39 | bpy.ops.transform.rotate(value=-angle, orient_axis='Z', constraint_axis=(False, False, False), use_proportional_edit=False) 40 | 41 | if sync: 42 | bpy.context.scene.tool_settings.mesh_select_mode = selection_mode 43 | 44 | return {'FINISHED'} 45 | -------------------------------------------------------------------------------- /op_meshtex_trim.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | 4 | from . import utilities_meshtex 5 | 6 | 7 | 8 | class op(bpy.types.Operator): 9 | bl_idname = "uv.textools_meshtex_trim" 10 | bl_label = "Trim" 11 | bl_description = "Trim Mesh Texture" 12 | bl_options = {'REGISTER', 'UNDO'} 13 | 14 | @classmethod 15 | def poll(cls, context): 16 | if (not bpy.context.active_object) or bpy.context.active_object.mode != 'OBJECT': 17 | return False 18 | if len(bpy.context.selected_objects) >= 1: 19 | # Find a UV mesh 20 | if utilities_meshtex.find_uv_mesh(bpy.context.selected_objects): 21 | # Find 1 or more meshes to wrap 22 | if len(utilities_meshtex.find_texture_meshes(bpy.context.selected_objects)) > 0: 23 | return True 24 | return False 25 | 26 | 27 | def execute(self, context): 28 | trim(self) 29 | return {'FINISHED'} 30 | 31 | 32 | 33 | def trim(self): 34 | # Collect UV mesh 35 | obj_uv = utilities_meshtex.find_uv_mesh(bpy.context.selected_objects) 36 | if not obj_uv: 37 | self.report({'ERROR_INVALID_INPUT'}, "No UV mesh found" ) 38 | return 39 | 40 | # Collect texture meshes 41 | obj_textures = utilities_meshtex.find_texture_meshes( bpy.context.selected_objects ) 42 | 43 | if len(obj_textures) == 0: 44 | self.report({'ERROR_INVALID_INPUT'}, "No meshes found for mesh textures" ) 45 | return 46 | 47 | # Setup Thickness 48 | utilities_meshtex.uv_mesh_fit(obj_uv, obj_textures) 49 | 50 | # Apply bool modifier to trim 51 | for obj in obj_textures: 52 | name = "Trim UV" 53 | if name in obj.modifiers: 54 | obj.modifiers.remove( obj.modifiers[name] ) 55 | 56 | modifier_bool = obj.modifiers.new(name=name, type='BOOLEAN') 57 | modifier_bool.solver = 'FAST' 58 | modifier_bool.operation = 'INTERSECT' 59 | modifier_bool.operand_type = 'OBJECT' 60 | modifier_bool.object = obj_uv 61 | -------------------------------------------------------------------------------- /op_meshtex_trim_collapse.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | 4 | from . import utilities_meshtex 5 | 6 | 7 | 8 | def is_available(): 9 | # If the selection contains a boolean modifier 10 | obj_textures = utilities_meshtex.find_texture_meshes(bpy.context.selected_objects) 11 | for obj in obj_textures: 12 | for modifier in obj.modifiers: 13 | if modifier.type == 'BOOLEAN': 14 | return True 15 | return False 16 | 17 | 18 | 19 | class op(bpy.types.Operator): 20 | bl_idname = "uv.textools_meshtex_trimcollapse" 21 | bl_label = "Collapse" 22 | bl_description = "Trim Mesh Texture" 23 | bl_options = {'REGISTER', 'UNDO'} 24 | 25 | @classmethod 26 | def poll(cls, context): 27 | if (not bpy.context.active_object) or bpy.context.active_object.mode != 'OBJECT': 28 | return False 29 | return is_available() 30 | 31 | 32 | def execute(self, context): 33 | collapse(self) 34 | return {'FINISHED'} 35 | 36 | 37 | 38 | def collapse(self): 39 | # Collect texture meshes 40 | obj_textures = utilities_meshtex.find_texture_meshes( bpy.context.selected_objects ) 41 | 42 | previous_selection = bpy.context.selected_objects.copy() 43 | previous_active = bpy.context.view_layer.objects.active 44 | 45 | if len(obj_textures) == 0: 46 | self.report({'ERROR_INVALID_INPUT'}, "No meshes found for mesh textures" ) 47 | return 48 | 49 | # Apply bool modifier to trim 50 | for obj in obj_textures: 51 | bpy.ops.object.select_all(action='DESELECT') 52 | obj.select_set( state = True, view_layer = None) 53 | bpy.context.view_layer.objects.active = obj 54 | bpy.ops.object.convert(target='MESH') 55 | 56 | # restore selection 57 | bpy.ops.object.select_all(action='DESELECT') 58 | for obj in previous_selection: 59 | obj.select_set( state = True, view_layer = None) 60 | bpy.context.view_layer.objects.active = previous_active 61 | -------------------------------------------------------------------------------- /op_meshtex_wrap.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | 4 | from . import utilities_meshtex 5 | 6 | 7 | 8 | class op(bpy.types.Operator): 9 | bl_idname = "uv.textools_meshtex_wrap" 10 | bl_label = "Wrap Mesh Texture" 11 | bl_description = "Swap UV to XYZ coordinates" 12 | bl_options = {'REGISTER', 'UNDO'} 13 | 14 | @classmethod 15 | def poll(cls, context): 16 | if bpy.context.scene.texToolsSettings.meshtexture_wrap < 1: 17 | return False 18 | if (not bpy.context.active_object) or bpy.context.active_object.mode != 'OBJECT': 19 | return False 20 | if len(bpy.context.selected_objects) > 0: 21 | # Find 1 UV mesh 22 | if utilities_meshtex.find_uv_mesh(bpy.context.selected_objects): 23 | # Find 1 or more meshes to wrap 24 | if len( utilities_meshtex.find_texture_meshes(bpy.context.selected_objects)) > 0: 25 | return True 26 | return False 27 | 28 | 29 | def execute(self, context): 30 | wrap_meshtex(self) 31 | return {'FINISHED'} 32 | 33 | 34 | 35 | def wrap_meshtex(self): 36 | # Collect UV mesh 37 | obj_uv = utilities_meshtex.find_uv_mesh(bpy.context.selected_objects) 38 | if not obj_uv: 39 | self.report({'ERROR_INVALID_INPUT'}, "No UV mesh found" ) 40 | return 41 | 42 | # Collect texture meshes 43 | obj_textures = utilities_meshtex.find_texture_meshes( bpy.context.selected_objects ) 44 | 45 | if len(obj_textures) == 0: 46 | self.report({'ERROR_INVALID_INPUT'}, "No meshes found for mesh textures" ) 47 | return 48 | 49 | # Setup Thickness? This doesn't seem to be really needed 50 | #utilities_meshtex.uv_mesh_fit(obj_uv, obj_textures) 51 | 52 | for obj in obj_textures: 53 | # Delete previous modifiers 54 | for modifier in obj.modifiers: 55 | if modifier.type == 'SURFACE_DEFORM': 56 | obj.modifiers.remove(modifier) 57 | break 58 | 59 | # Add mesh modifier 60 | modifier_deform = obj.modifiers.new(name="SurfaceDeform", type='SURFACE_DEFORM') 61 | modifier_deform.target = obj_uv 62 | 63 | obj.select_set( state = True, view_layer = None) 64 | bpy.context.view_layer.objects.active = obj 65 | bpy.ops.object.surfacedeform_bind(modifier="SurfaceDeform") 66 | 67 | # Apply wrapped morph state 68 | bpy.context.scene.texToolsSettings.meshtexture_wrap = 0 69 | -------------------------------------------------------------------------------- /op_relax.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | 4 | from mathutils import Vector 5 | from itertools import chain 6 | from collections import defaultdict 7 | from . import op_meshtex_create 8 | from . import utilities_uv 9 | 10 | 11 | 12 | class op(bpy.types.Operator): 13 | bl_idname = "uv.textools_relax" 14 | bl_label = "Relax" 15 | bl_description = "Relax selected UVs" 16 | bl_options = {'REGISTER', 'UNDO'} 17 | 18 | iterations : bpy.props.IntProperty(name="Iterations", min=1, max=10, soft_max=4, default=1, description="Repeat Smooth the specified number of times.") 19 | area_preservation : bpy.props.FloatProperty(name="Area Preservation", min=0.0, max=1.0, default=0.95, description="Factor of rectification of the area shrink caused by the Smooth operator.") 20 | 21 | @classmethod 22 | def poll(cls, context): 23 | if not bpy.context.active_object: 24 | return False 25 | if bpy.context.active_object.mode != 'EDIT': 26 | return False 27 | if bpy.context.active_object.type != 'MESH': 28 | return False 29 | if not bpy.context.active_object.data.uv_layers: 30 | return False 31 | if context.scene.tool_settings.use_uv_select_sync: 32 | return False 33 | return True 34 | 35 | 36 | def execute(self, context): 37 | utilities_uv.multi_object_loop(relax, self, context) 38 | return {'FINISHED'} 39 | 40 | 41 | 42 | def relax(self, context): 43 | # UV to temporary mesh 44 | pre_selection_mode = tuple(bpy.context.scene.tool_settings.mesh_select_mode) 45 | obj = bpy.context.active_object 46 | obj_name = bpy.context.active_object.name 47 | 48 | bm, uv_layers, faces_by_island = op_meshtex_create.create_uv_mesh(self, context, obj, sk_create=False, bool_scale=False, delete_unselected=False, restore_selected=True) 49 | if bm == {'CANCELLED'}: 50 | return 51 | 52 | temp_obj = bpy.context.active_object 53 | temp_obj_data_name = temp_obj.data.name 54 | 55 | bpy.ops.mesh.select_all(action='DESELECT') 56 | for face in chain.from_iterable(faces_by_island): 57 | face.select_set(True) 58 | 59 | # Smooth mesh 60 | bpy.ops.mesh.vertices_smooth(factor=0.5, repeat=self.iterations) 61 | 62 | 63 | # Mesh to UV 64 | for faces in faces_by_island: 65 | if self.area_preservation > 0: 66 | edge_length = 0 67 | edge_uv_length = 0 68 | pre_center = Vector((0.0, 0.0)) 69 | n_loops = 0 70 | 71 | for face in faces: 72 | face.select = True 73 | for loop in face.loops: 74 | if self.area_preservation > 0: 75 | edge_length += (loop.link_loop_next.vert.co - loop.vert.co).length 76 | edge_uv_length += (loop.link_loop_next[uv_layers].uv - loop[uv_layers].uv).length 77 | pre_center += loop[uv_layers].uv 78 | n_loops += 1 79 | # Move UVs making sure future UV edges measures will be real 80 | if loop != face.loops[0]: 81 | if loop != face.loops[-1]: 82 | loop[uv_layers].uv = (loop.vert.co.x, loop.vert.co.y) 83 | else: 84 | loop[uv_layers].uv = (loop.vert.co.x, loop.vert.co.y) 85 | face.loops[0][uv_layers].uv = (face.loops[0].vert.co.x, face.loops[0].vert.co.y) 86 | 87 | scale = 1 88 | if self.area_preservation > 0 and edge_length > 0 and edge_uv_length > 0: 89 | pre_center /= n_loops 90 | scale = 1 + (edge_uv_length / edge_length - 1)*self.area_preservation 91 | 92 | if scale != 1: 93 | for face in faces: 94 | for loop in face.loops: 95 | loop[uv_layers].uv = pre_center + (loop[uv_layers].uv - pre_center)*scale 96 | 97 | 98 | # Copy and Paste UVs between temporary and original meshes 99 | copied_uvs = defaultdict(list) 100 | for face in bm.faces: 101 | if face.select: 102 | for loop in face.loops: 103 | copied_uvs[face.index].append(loop[uv_layers].uv.to_tuple()) 104 | 105 | bpy.ops.object.mode_set(mode='OBJECT', toggle=False) 106 | bpy.ops.object.select_all(action='DESELECT') 107 | bpy.context.view_layer.objects.active = bpy.data.objects[obj_name] 108 | bpy.ops.object.mode_set(mode='EDIT', toggle=False) 109 | 110 | bm = bmesh.from_edit_mesh(bpy.context.active_object.data) 111 | bm.faces.ensure_lookup_table() 112 | bm.verts.ensure_lookup_table() 113 | uv_layers = bm.loops.layers.uv.verify() 114 | 115 | for face_index in copied_uvs: 116 | for i, loop in enumerate(bm.faces[face_index].loops): 117 | if loop[uv_layers].select: 118 | loop[uv_layers].uv = copied_uvs[face_index][i] 119 | 120 | # Remove temporary mesh and restore selection mode altered by meshtex_create 121 | bpy.data.meshes.remove(bpy.data.meshes[temp_obj_data_name], do_unlink=True) 122 | bpy.context.scene.tool_settings.mesh_select_mode = pre_selection_mode 123 | -------------------------------------------------------------------------------- /op_select_islands_flipped.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | 4 | from . import utilities_uv 5 | 6 | 7 | class op(bpy.types.Operator): 8 | bl_idname = "uv.textools_select_islands_flipped" 9 | bl_label = "Select Flipped" 10 | bl_description = "Detect flipped UV faces across all polygons (even hidden) of the selected objects" 11 | bl_options = {'REGISTER', 'UNDO'} 12 | 13 | @classmethod 14 | def poll(cls, context): 15 | if bpy.context.area.ui_type != 'UV': 16 | return False 17 | if not bpy.context.active_object: 18 | return False 19 | if bpy.context.active_object.type != 'MESH': 20 | return False 21 | if bpy.context.active_object.mode != 'EDIT': 22 | return False 23 | if not bpy.context.object.data.uv_layers: 24 | return False 25 | return True 26 | 27 | def execute(self, context): 28 | return select_flipped(self) 29 | 30 | def select_flipped(self): 31 | bpy.ops.uv.select_all(action='DESELECT') 32 | sync = bpy.context.scene.tool_settings.use_uv_select_sync 33 | premode = bpy.context.scene.tool_settings.uv_select_mode 34 | 35 | if not sync and premode == 'VERTEX': 36 | bpy.ops.uv.select_mode(type='FACE') 37 | 38 | selected_objs = utilities_uv.selected_unique_objects_in_mode_with_uv() 39 | counter = 0 40 | for obj in selected_objs: 41 | bm = bmesh.from_edit_mesh(obj.data) 42 | uv_layer = bm.loops.layers.uv.verify() 43 | for f in bm.faces: 44 | area = 0.0 45 | uvs = [l[uv_layer].uv for l in f.loops] 46 | for i in range(len(uvs)): 47 | area += uvs[i - 1].cross(uvs[i]) 48 | if area < 0: 49 | counter += 1 50 | if sync: 51 | f.select_set(True) 52 | else: 53 | for l in f.loops: 54 | l[uv_layer].select = True 55 | 56 | if not counter: 57 | self.report({'INFO'}, 'Flipped faces not found') 58 | bpy.ops.uv.select_mode(type=premode) 59 | return {'CANCELLED'} 60 | 61 | # Workaround to flush the selected UVs from loops to faces 62 | if not sync: 63 | bpy.ops.uv.select_mode(type='VERTEX') 64 | sel_mode = 'FACE' if premode == 'ISLAND' else premode 65 | bpy.ops.uv.select_mode(type=sel_mode) 66 | 67 | self.report({'WARNING'}, f'Detected {counter} flipped UV faces (THE AFFECTED MESH POLYGONS MAY BE HIDDEN OR UNSELECTED!)') 68 | return {'FINISHED'} 69 | -------------------------------------------------------------------------------- /op_select_islands_identical.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | 4 | from . import utilities_uv 5 | 6 | 7 | 8 | class op(bpy.types.Operator): 9 | bl_idname = "uv.textools_select_islands_identical" 10 | bl_label = "Select similar" 11 | bl_description = "Select UV islands with similar topology with respect to the selected UVs" 12 | bl_options = {'REGISTER', 'UNDO'} 13 | 14 | @classmethod 15 | def poll(cls, context): 16 | if bpy.context.area.ui_type != 'UV': 17 | return False 18 | if not bpy.context.active_object: 19 | return False 20 | if bpy.context.active_object.type != 'MESH': 21 | return False 22 | if bpy.context.active_object.mode != 'EDIT': 23 | return False 24 | if not bpy.context.object.data.uv_layers: 25 | return False 26 | return True 27 | 28 | 29 | def execute(self, context): 30 | island_stats_source_list = utilities_uv.multi_object_loop(island_find, self, context, need_results = True) 31 | 32 | if not island_stats_source_list: 33 | return {'CANCELLED'} 34 | elif {'CANCELLED'} in island_stats_source_list: 35 | return {'CANCELLED'} 36 | elif len(list(filter(bool, island_stats_source_list))) > 1: 37 | self.report({'ERROR_INVALID_INPUT'}, "Please select only 1 UV Island") 38 | return {'CANCELLED'} 39 | 40 | island = None 41 | for island_stats_source in island_stats_source_list: 42 | if island_stats_source: 43 | island = island_stats_source 44 | break 45 | if island: 46 | utilities_uv.multi_object_loop(swap, self, context, island) 47 | 48 | return {'FINISHED'} 49 | 50 | 51 | 52 | def island_find(self, context): 53 | bm = bmesh.from_edit_mesh(bpy.context.active_object.data) 54 | uv_layers = bm.loops.layers.uv.verify() 55 | 56 | islands = utilities_uv.get_selected_islands(bm, uv_layers, selected=False, extend_selection_to_islands=True) 57 | if not islands: 58 | return {} 59 | if len(islands) > 1: 60 | self.report({'ERROR_INVALID_INPUT'}, "Please select only 1 UV Island") 61 | return {'CANCELLED'} 62 | 63 | island_stats_source = Island_stats(bm, islands[0]) 64 | return island_stats_source 65 | 66 | 67 | 68 | def swap(self, context, island_stats_source): 69 | bm = bmesh.from_edit_mesh(bpy.context.active_object.data) 70 | uv_layers = bm.loops.layers.uv.verify() 71 | sync = bpy.context.scene.tool_settings.use_uv_select_sync 72 | if sync: 73 | selection_mode = tuple(bpy.context.scene.tool_settings.mesh_select_mode) 74 | else: 75 | selection_mode = bpy.context.scene.tool_settings.uv_select_mode 76 | 77 | if sync: 78 | bpy.ops.mesh.select_all(action='SELECT') 79 | else: 80 | bpy.ops.uv.select_all(action='SELECT') 81 | islands_all = utilities_uv.get_selected_islands(bm, uv_layers) 82 | 83 | islands_equal = [] 84 | for island in islands_all: 85 | island_stats = Island_stats(bm, island) 86 | 87 | if island_stats_source.isEqual(island_stats): 88 | islands_equal.append(island) 89 | 90 | if sync: 91 | bpy.ops.mesh.select_all(action='DESELECT') 92 | bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE') 93 | for island in islands_equal: 94 | for face in island: 95 | face.select_set(True) 96 | else: 97 | bpy.ops.uv.select_all(action='DESELECT') 98 | for island in islands_equal: 99 | for face in island: 100 | for loop in face.loops: 101 | loop[uv_layers].select = True 102 | 103 | 104 | if sync: 105 | bpy.context.scene.tool_settings.mesh_select_mode = selection_mode 106 | else: 107 | # Workaround for selection not flushing properly from loops to EDGE Selection Mode, apparently since UV edge selection support was added to the UV space 108 | bpy.ops.uv.select_mode(type='VERTEX') 109 | bpy.context.scene.tool_settings.uv_select_mode = selection_mode 110 | 111 | 112 | 113 | class Island_stats: 114 | countFaces = 0 115 | countVerts = 0 116 | area = 0 117 | countLinkedEdges = 0 118 | countLinkedFaces = 0 119 | 120 | 121 | def __init__(self, bm, faces): 122 | # Collect topology stats 123 | self.countFaces = len(faces) 124 | 125 | verts = {v for f in faces for v in f.verts} 126 | self.countVerts = len(verts) 127 | for vert in verts: 128 | self.countLinkedEdges += len(vert.link_edges) 129 | self.countLinkedFaces += len(vert.link_faces) 130 | 131 | for face in faces: 132 | self.area += face.calc_area() 133 | 134 | 135 | def isEqual(self, other): 136 | if self.countVerts != other.countVerts: 137 | return False 138 | if self.countFaces != other.countFaces: 139 | return False 140 | if self.countLinkedEdges != other.countLinkedEdges: 141 | return False 142 | if self.countLinkedFaces != other.countLinkedFaces: 143 | return False 144 | 145 | # area needs to be 90%+ identical 146 | if min(self.area, other.area)/max(self.area, other.area) < 0.7: 147 | return False 148 | return True 149 | -------------------------------------------------------------------------------- /op_select_islands_outline.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | 4 | from . import utilities_uv 5 | 6 | 7 | 8 | class op(bpy.types.Operator): 9 | bl_idname = "uv.textools_select_islands_outline" 10 | bl_label = "Select Island outline" 11 | bl_description = "Reduce UV selection to Islands edge bounds" 12 | bl_options = {'REGISTER', 'UNDO'} 13 | 14 | @classmethod 15 | def poll(cls, context): 16 | if bpy.context.area.ui_type != 'UV': 17 | return False 18 | if not bpy.context.active_object: 19 | return False 20 | if bpy.context.active_object.mode != 'EDIT': 21 | return False 22 | if bpy.context.active_object.type != 'MESH': 23 | return False 24 | if not bpy.context.object.data.uv_layers: 25 | return False 26 | return True 27 | 28 | 29 | def execute(self, context): 30 | utilities_uv.multi_object_loop(select_outline, self, context) 31 | return {'FINISHED'} 32 | 33 | 34 | 35 | def select_outline(self, context, bm=None, uv_layers=None): #, linkloops=True added just for stitch to work, may slow down the script 36 | if bm is None: 37 | bm = bmesh.from_edit_mesh(bpy.context.active_object.data) 38 | uv_layers = bm.loops.layers.uv.verify() 39 | 40 | sync = bpy.context.scene.tool_settings.use_uv_select_sync 41 | 42 | if sync: 43 | selected_loops = {l for e in bm.edges for l in e.link_loops if e.select} 44 | else: 45 | selected_loops = {l for f in bm.faces for l in f.loops if l[uv_layers].select_edge and l.edge.select} 46 | 47 | # Store previous edge seams 48 | edges_seam = {l.edge for l in selected_loops if l.edge.seam} 49 | 50 | # Clear original seams 51 | for edge in edges_seam: 52 | edge.seam = False 53 | 54 | bpy.ops.uv.seams_from_islands(mark_seams=True, mark_sharp=False) 55 | 56 | if sync: 57 | boundary_edges = {l.edge for l in selected_loops if l.edge.seam or l.edge.is_boundary} 58 | else: 59 | boundary_loops = {l for l in selected_loops if l.edge.seam or l.edge.is_boundary} 60 | boundary_edges = {l.edge for l in boundary_loops} 61 | 62 | # Select bound edges from edges marked as seams and edge boundaries 63 | if sync: 64 | bpy.ops.mesh.select_all(action='DESELECT') 65 | bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE') 66 | for edge in boundary_edges: 67 | edge.select_set(True) 68 | else: 69 | bpy.ops.uv.select_all(action='DESELECT') 70 | bpy.ops.uv.select_mode(type='EDGE') 71 | for loop in boundary_loops: 72 | loop[uv_layers].select = True 73 | loop[uv_layers].select_edge = True 74 | # if linkloops: 75 | # loop.link_loop_next[uv_layers].select = True 76 | # Workaround for selection not flushing properly from loops to EDGE Selection Mode, apparently since UV edge selection support was added to the UV space 77 | # Not fully working though 78 | # bpy.ops.uv.select_mode(type='VERTEX') 79 | # bpy.ops.uv.select_mode(type='EDGE') 80 | 81 | # Restore seam selection 82 | for edge in boundary_edges: 83 | edge.seam = False 84 | for edge in edges_seam: 85 | edge.seam = True 86 | -------------------------------------------------------------------------------- /op_select_islands_overlap.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | 4 | from . import utilities_uv 5 | 6 | 7 | 8 | class op(bpy.types.Operator): 9 | bl_idname = "uv.textools_select_islands_overlap" 10 | bl_label = "Select outline" 11 | bl_description = "Select all overlapping UV islands but one" 12 | bl_options = {'REGISTER', 'UNDO'} 13 | 14 | @classmethod 15 | def poll(cls, context): 16 | if bpy.context.area.ui_type != 'UV': 17 | return False 18 | if not bpy.context.active_object: 19 | return False 20 | if bpy.context.active_object.type != 'MESH': 21 | return False 22 | if bpy.context.active_object.mode != 'EDIT': 23 | return False 24 | if not bpy.context.object.data.uv_layers: 25 | return False 26 | return True 27 | 28 | 29 | def execute(self, context): 30 | bpy.ops.uv.select_overlap() 31 | bpy.ops.uv.select_linked() 32 | utilities_uv.multi_object_loop(deselect, self, context) 33 | return {'FINISHED'} 34 | 35 | 36 | 37 | def deselect(self, context): 38 | bm = bmesh.from_edit_mesh(bpy.context.active_object.data) 39 | uv_layers = bm.loops.layers.uv.verify() 40 | sync = bpy.context.scene.tool_settings.use_uv_select_sync 41 | if sync: 42 | selection_mode = tuple(bpy.context.scene.tool_settings.mesh_select_mode) 43 | 44 | islands = utilities_uv.get_selected_islands(bm, uv_layers) 45 | 46 | if len(islands) > 1: 47 | if sync: 48 | bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE') 49 | for face in islands[0]: 50 | face.select_set(False) 51 | else: 52 | for face in islands[0]: 53 | for loop in face.loops: 54 | loop[uv_layers].select = False 55 | 56 | utilities_uv.multi_object_loop_stop = True 57 | 58 | if sync: 59 | bpy.context.scene.tool_settings.mesh_select_mode = selection_mode 60 | -------------------------------------------------------------------------------- /op_select_zero.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | import mathutils 4 | 5 | from . import utilities_uv 6 | 7 | 8 | class op(bpy.types.Operator): 9 | bl_idname = "uv.textools_select_zero" 10 | bl_label = "Select Degenerate" 11 | bl_description = "Detect degenerate UVs (zero area UV triangles) across all polygons (even hidden) of the selected objects" 12 | bl_options = {'REGISTER', 'UNDO'} 13 | 14 | precision: bpy.props.FloatProperty(name='Precision', default=0.00005, min=0, step=0.00001, precision=7) 15 | 16 | @classmethod 17 | def poll(cls, context): 18 | if bpy.context.area.ui_type != 'UV': 19 | return False 20 | if not bpy.context.active_object: 21 | return False 22 | if bpy.context.active_object.type != 'MESH': 23 | return False 24 | if bpy.context.active_object.mode != 'EDIT': 25 | return False 26 | if not bpy.context.object.data.uv_layers: 27 | return False 28 | return True 29 | 30 | def execute(self, context): 31 | return select_zero(self) 32 | 33 | 34 | def select_zero(self): 35 | bpy.ops.uv.select_all(action='DESELECT') 36 | sync = bpy.context.scene.tool_settings.use_uv_select_sync 37 | premode = bpy.context.scene.tool_settings.uv_select_mode 38 | 39 | counter = 0 40 | for obj in utilities_uv.selected_unique_objects_in_mode_with_uv(): 41 | bm = bmesh.from_edit_mesh(obj.data) 42 | uv_layer = bm.loops.layers.uv.verify() 43 | for f in bm.faces: 44 | for l in f.loops: 45 | l1 = l[uv_layer].uv 46 | l2 = l.link_loop_next[uv_layer].uv 47 | l3 = l.link_loop_prev[uv_layer].uv 48 | area = mathutils.geometry.area_tri(l1, l2, l3) 49 | thres = max((l1-l2).length, (l2-l3).length, (l1-l3).length)**2 * self.precision 50 | if area < thres: 51 | if sync: 52 | f.select_set(True) 53 | else: 54 | for i in f.loops: 55 | i[uv_layer].select = True 56 | counter += 1 57 | break 58 | elif len(f.loops) == 3: 59 | break 60 | 61 | if not counter: 62 | self.report({'INFO'}, f'Degenerate triangles not found') 63 | return {'FINISHED'} 64 | 65 | # Workaround to flush the selected UVs from loops to faces 66 | if not sync: 67 | if premode == 'EDGE': 68 | premode = 'VERTEX' 69 | bpy.ops.uv.select_mode(type='VERTEX') 70 | if premode == 'ISLAND': 71 | premode = 'FACE' 72 | bpy.ops.uv.select_mode(type=premode) 73 | 74 | self.report({'WARNING'}, f'Detected {counter} degenerate UV triangles (THE AFFECTED MESH POLYGONS MAY BE HIDDEN OR UNSELECTED!)') 75 | return {'FINISHED'} 76 | -------------------------------------------------------------------------------- /op_smoothing_uv_islands.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import math 3 | import bmesh 4 | 5 | from . import utilities_uv 6 | from . import settings 7 | 8 | 9 | 10 | class op(bpy.types.Operator): 11 | bl_idname = "uv.textools_smoothing_uv_islands" 12 | bl_label = "Sharp edges from Islands" 13 | bl_description = "Apply Smooth Normals and Sharp Edges to the UV Island borders of the Mesh" 14 | bl_options = {'REGISTER', 'UNDO'} 15 | 16 | soft_self_border: bpy.props.BoolProperty(name="Soften own border", description="Do not sharpen uv-borders from an island to itself", default=False) 17 | 18 | @classmethod 19 | def poll(cls, context): 20 | if not bpy.context.active_object: 21 | return False 22 | if bpy.context.active_object.type != 'MESH': 23 | return False 24 | if not bpy.context.object.data.uv_layers: 25 | return False 26 | return True 27 | 28 | 29 | def execute(self, context): 30 | premode = bpy.context.active_object.mode 31 | utilities_uv.multi_object_loop(smooth_uv_islands, self, context) 32 | bpy.ops.object.mode_set(mode=premode) 33 | return {'FINISHED'} 34 | 35 | 36 | 37 | def smooth_uv_islands(self, context): 38 | bpy.ops.object.mode_set(mode='EDIT') 39 | #utilities_uv.selection_store(bm, uv_layers) 40 | 41 | # Smooth everything 42 | bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE') 43 | bpy.ops.mesh.select_all(action='SELECT') 44 | bpy.ops.mesh.faces_shade_smooth() 45 | bpy.ops.mesh.mark_sharp(clear=True) 46 | 47 | bpy.ops.uv.select_all(action='SELECT') 48 | bpy.ops.uv.seams_from_islands(mark_seams=False, mark_sharp=True) 49 | 50 | # Do not create sharp edges if the uv island has a uv seam to itself. 51 | # Best example is the lateral surface of a cylinder - which doesn't need 52 | # a sharp edge when unrolled for normal map baking. 53 | 54 | if self.soft_self_border: 55 | bm = bmesh.from_edit_mesh(bpy.context.active_object.data) 56 | uv_layer = bm.loops.layers.uv.verify() 57 | 58 | islands = utilities_uv.getAllIslands(bm, uv_layer) 59 | bpy.ops.uv.select_all(action='SELECT') 60 | tested_edges = set() 61 | 62 | for island in islands: 63 | for face in list(island): 64 | for edge in face.edges: 65 | if edge in tested_edges: 66 | continue 67 | 68 | smooth_border = True 69 | for link_face in edge.link_faces: 70 | if link_face not in island: 71 | smooth_border = False 72 | break 73 | if smooth_border: 74 | edge.smooth = True 75 | 76 | tested_edges.add(edge) 77 | 78 | bpy.ops.mesh.customdata_custom_splitnormals_clear() 79 | if settings.bversion < 4.1: 80 | bpy.context.object.data.use_auto_smooth = True 81 | bpy.context.object.data.auto_smooth_angle = math.pi 82 | 83 | #utilities_uv.selection_restore(bm, uv_layers) 84 | -------------------------------------------------------------------------------- /op_stitch.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | import math 4 | 5 | from . import op_select_islands_outline 6 | from . import utilities_uv 7 | 8 | 9 | class op(bpy.types.Operator): 10 | bl_idname = "uv.textools_stitch" 11 | bl_label = "Stitch" 12 | bl_description = "Stitch other Islands to the selection" 13 | bl_options = {'REGISTER', 'UNDO'} 14 | 15 | @classmethod 16 | def poll(cls, context): 17 | if bpy.context.area.ui_type != 'UV': 18 | return False 19 | if not bpy.context.active_object: 20 | return False 21 | if bpy.context.active_object.mode != 'EDIT': 22 | return False 23 | if bpy.context.active_object.type != 'MESH': 24 | return False 25 | if context.scene.tool_settings.use_uv_select_sync: 26 | return False 27 | if not bpy.context.object.data.uv_layers: 28 | return False 29 | return True 30 | 31 | def execute(self, context): 32 | utilities_uv.multi_object_loop(main, self, context) 33 | return {'FINISHED'} 34 | 35 | 36 | def main(self, context): 37 | bm = bmesh.from_edit_mesh(bpy.context.active_object.data) 38 | uv_layers = bm.loops.layers.uv.verify() 39 | 40 | op_select_islands_outline.select_outline(self, context) 41 | selection = {loop for face in bm.faces if face.select for loop in face.loops if loop[uv_layers].select_edge} 42 | 43 | if not selection: 44 | return 45 | 46 | selectionFaces = {loop.face for loop in selection} 47 | selectionFacesTargets = {loop.link_loop_radial_next.face for loop in selection} 48 | extended_selection = {loop.link_loop_radial_next for loop in selection} 49 | extended_selection.update(selection) 50 | 51 | # Store selection 52 | utilities_uv.selection_store(bm, uv_layers) 53 | 54 | islands, target_islands = utilities_uv.getSelectedUnselectedIslands(bm, uv_layers, selected_faces=selectionFaces, target_faces=selectionFacesTargets) 55 | remaining_islands = islands.copy() 56 | remaining_islands.extend(target_islands) 57 | 58 | if not islands: 59 | utilities_uv.selection_restore() 60 | return 61 | 62 | for island in islands: 63 | del remaining_islands[0] 64 | if remaining_islands: 65 | bpy.ops.uv.select_all(action='DESELECT') 66 | loop1 = next(iter(island)).loops[0] 67 | loop1[uv_layers].select = True 68 | bpy.ops.uv.select_linked() 69 | 70 | # Selection original coordinates 71 | loop2 = loop1.link_loop_next 72 | coords_before = loop1[uv_layers].uv.copy(), loop2[uv_layers].uv.copy() 73 | # Stitch 74 | op_select_islands_outline.select_outline(self, context) 75 | selectionBorder = {loop for face in island for loop in face.loops if loop[uv_layers].select_edge} 76 | selectionBorder.intersection_update(extended_selection) 77 | 78 | loopsByTarget = [[] for _ in range(len(remaining_islands))] 79 | for loop in selectionBorder: 80 | targetFace = loop.link_loop_radial_next.face 81 | if targetFace not in island: 82 | for l, i in zip(loopsByTarget, remaining_islands): 83 | if targetFace in i: 84 | l.append(loop) 85 | break 86 | 87 | for grouped_loops in loopsByTarget: 88 | bpy.ops.uv.select_all(action='DESELECT') 89 | for base_loop in grouped_loops: 90 | base_loop[uv_layers].select = True 91 | base_loop[uv_layers].select_edge = True 92 | 93 | bpy.ops.uv.stitch(use_limit=False, snap_islands=True, midpoint_snap=False, clear_seams=True, mode='EDGE') 94 | 95 | # Relocate selection 96 | coords_after = loop1[uv_layers].uv, loop2[uv_layers].uv 97 | if coords_before != coords_after: 98 | loop1[uv_layers].select = True 99 | bpy.ops.uv.select_linked() 100 | new_island = utilities_uv.get_selected_uv_faces(bm, uv_layers) 101 | 102 | V1 = coords_before[1] - coords_before[0] 103 | V2 = coords_after[1] - coords_after[0] 104 | angle = math.atan2(V1.cross(V2), V1.dot(V2)) 105 | 106 | vec_origin = coords_after[0] 107 | displace = coords_before[0] - coords_after[0] 108 | 109 | utilities_uv.rotate_island(new_island, uv_layers, angle, vec_origin) 110 | utilities_uv.translate_island(new_island, uv_layers, displace) 111 | 112 | # Restore selection 113 | utilities_uv.selection_restore() 114 | -------------------------------------------------------------------------------- /op_texel_checker_map.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import os 3 | 4 | from . import utilities_uv 5 | 6 | 7 | 8 | class op(bpy.types.Operator): 9 | bl_idname = "uv.textools_texel_checker_map" 10 | bl_label = "Checker Map" 11 | bl_description = "Add different checker map overrides to the selected Objects and cycle between them and the original Materials" 12 | bl_options = {'REGISTER', 'UNDO'} 13 | 14 | @classmethod 15 | def poll(cls, context): 16 | if not bpy.context.active_object: 17 | return False 18 | if bpy.context.object.mode != 'EDIT' and bpy.context.object.mode != 'OBJECT': 19 | return False 20 | if bpy.context.object.mode == 'OBJECT' and len(bpy.context.selected_objects) == 0: 21 | return False 22 | if bpy.context.active_object.type != 'MESH': 23 | return False 24 | if not bpy.context.object.data.uv_layers: 25 | return False 26 | return True 27 | 28 | 29 | def execute(self, context): 30 | premode = bpy.context.active_object.mode 31 | utilities_uv.multi_object_loop(assign_checker_map) 32 | bpy.ops.object.mode_set(mode=premode) 33 | # Change Viewport Shading Type to MATERIAL 34 | for area in bpy.context.screen.areas: 35 | if area.type == 'VIEW_3D': 36 | for space in area.spaces: 37 | if space.type == 'VIEW_3D': 38 | space.shading.type = 'MATERIAL' 39 | #space.shading.color_type = 'TEXTURE' 40 | # Force redraw of viewport to update texture 41 | bpy.context.view_layer.update() 42 | return {'FINISHED'} 43 | 44 | 45 | 46 | def assign_checker_map(): 47 | obj = bpy.context.active_object 48 | if obj.type != 'MESH' or not obj.data.uv_layers: 49 | return 50 | 51 | bpy.ops.object.mode_set(mode='OBJECT') 52 | 53 | # Apply checker maps 54 | if obj.modifiers: 55 | for m in obj.modifiers: 56 | if m.name == 'TT-checker-override': 57 | if m.node_group.name == 'TT-checker-override-uvgrid': 58 | m.node_group = get_nodegroup('TT-checker-override-colorgrid') 59 | elif m.node_group.name == 'TT-checker-override-colorgrid': 60 | m.node_group = get_nodegroup('TT-checker-override-gravity') 61 | elif m.node_group.name == 'TT-checker-override-gravity': 62 | obj.modifiers.remove(m) 63 | break 64 | else: 65 | obj.modifiers.new(name='TT-checker-override', type='NODES') 66 | obj.modifiers.active.node_group = get_nodegroup('TT-checker-override-uvgrid') 67 | obj.modifiers.active.show_render = False 68 | else: 69 | obj.modifiers.new(name='TT-checker-override', type='NODES') 70 | obj.modifiers.active.node_group = get_nodegroup('TT-checker-override-uvgrid') 71 | obj.modifiers.active.show_render = False 72 | 73 | bpy.ops.object.modifier_move_to_index(modifier='TT-checker-override', index=len(obj.modifiers)-1) 74 | if 'TT_CM_Scale' not in obj: 75 | obj.TT_CM_Scale = 1 76 | 77 | 78 | 79 | def get_nodegroup(name): 80 | if bpy.data.node_groups.get(name) is None: 81 | path = os.path.join(os.path.dirname(__file__), "resources/materials_3.0.blend", "NodeTree") 82 | bpy.ops.wm.append(filename=name, directory=path, link=False, autoselect=False) 83 | 84 | return bpy.data.node_groups.get(name) 85 | -------------------------------------------------------------------------------- /op_texel_checker_map_cleanup.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from . import utilities_texel 4 | 5 | 6 | 7 | class op(bpy.types.Operator): 8 | bl_idname = "uv.textools_texel_checker_map_cleanup" 9 | bl_label = "Checker Map Cleanup" 10 | bl_description = "Remove and unlink checker map data from the selected Objects; + Alt , it's applied to all Objects in the Blendfile" 11 | bl_options = {'REGISTER', 'UNDO'} 12 | 13 | bool_all: bpy.props.BoolProperty(name="On all Objects", default=False) 14 | 15 | def invoke(self, context, event): 16 | self.bool_all = event.alt 17 | self.execute(context) 18 | return {'FINISHED'} 19 | 20 | 21 | def execute(self, context): 22 | premode = bpy.context.active_object.mode 23 | 24 | if self.bool_all: 25 | group = {ob for ob in bpy.data.objects if ob.type == 'MESH'} 26 | else: 27 | bpy.ops.object.mode_set(mode='EDIT', toggle=False) 28 | group = {ob for ob in bpy.context.objects_in_mode_unique_data if ob.type == 'MESH' and ob.select_get()} 29 | bpy.ops.object.mode_set(mode='OBJECT', toggle=False) 30 | 31 | for obj in group: 32 | if obj.modifiers: 33 | for m in obj.modifiers: 34 | if m.name == 'TT-checker-override': 35 | obj.modifiers.remove(m) 36 | del obj['TT_CM_Scale'] 37 | 38 | for nodegroup in bpy.data.node_groups: 39 | if nodegroup and 'TT-checker-override' in nodegroup.name: 40 | if not nodegroup.users: 41 | bpy.data.node_groups.remove(nodegroup, do_unlink=True) 42 | 43 | utilities_texel.checker_images_cleanup() 44 | 45 | bpy.ops.object.mode_set(mode=premode) 46 | return {'FINISHED'} 47 | -------------------------------------------------------------------------------- /op_texel_density_get.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | import mathutils 4 | from mathutils import Vector 5 | import math 6 | 7 | from . import utilities_texel 8 | from . import utilities_uv 9 | 10 | 11 | class op(bpy.types.Operator): 12 | bl_idname = "uv.textools_texel_density_get" 13 | bl_label = "Get Texel size" 14 | bl_description = "Get Pixel per unit ratio or Texel density" 15 | bl_options = {'REGISTER', 'UNDO'} 16 | 17 | @classmethod 18 | def poll(cls, context): 19 | if bpy.context.area.ui_type != 'UV': 20 | return False 21 | if not bpy.context.active_object: 22 | return False 23 | if bpy.context.object.mode != 'EDIT' and bpy.context.object.mode != 'OBJECT': 24 | return False 25 | if bpy.context.object.mode == 'OBJECT' and len(bpy.context.selected_objects) == 0: 26 | return False 27 | if bpy.context.active_object.type != 'MESH': 28 | return False 29 | if not bpy.context.object.data.uv_layers: 30 | return False 31 | return True 32 | 33 | 34 | def execute(self, context): 35 | edit_mode = bpy.context.object.mode == 'EDIT' 36 | getmode = bpy.context.scene.texToolsSettings.texel_get_mode 37 | sum_area_uv = 0 38 | sum_area_vt = 0 39 | 40 | area_pairs = utilities_uv.multi_object_loop(get_texel_density, self, context, edit_mode, getmode, need_results = True) 41 | 42 | for area_pair in area_pairs: 43 | sum_area_uv += area_pair[0] 44 | sum_area_vt += area_pair[1] 45 | 46 | if sum_area_uv != 0 and sum_area_vt != 0: 47 | bpy.context.scene.texToolsSettings.texel_density = (sum_area_uv / sum_area_vt) * bpy.context.preferences.addons[__package__].preferences.texel_density_scale 48 | 49 | if not edit_mode: 50 | bpy.ops.object.mode_set(mode='OBJECT') 51 | 52 | return {'FINISHED'} 53 | 54 | 55 | 56 | def get_texel_density(self, context, edit_mode, getmode): 57 | is_sync = bpy.context.scene.tool_settings.use_uv_select_sync 58 | obj = bpy.context.active_object 59 | if obj.type != 'MESH' or not obj.data.uv_layers: 60 | return 61 | 62 | bpy.ops.object.mode_set(mode='EDIT') 63 | bm = bmesh.from_edit_mesh(bpy.context.active_object.data) 64 | uv_layers = bm.loops.layers.uv.verify() 65 | 66 | if edit_mode: 67 | if is_sync: 68 | object_faces = [face for face in bm.faces if face.select] 69 | else: 70 | object_faces = utilities_uv.get_selected_uv_faces(bm, uv_layers) 71 | else: 72 | object_faces = bm.faces 73 | 74 | if not object_faces: 75 | #self.report({'INFO'}, "No UV maps or meshes selected" ) 76 | return [0, 0] 77 | 78 | if getmode == 'IMAGE': 79 | # Collect image/texture 80 | image = utilities_texel.get_object_texture_image(obj) 81 | if not image: 82 | self.report({'INFO'}, "No Texture found, assign Checker map or texture first" ) 83 | return [0, 0] 84 | if image.source =='TILED': 85 | udim_tile, column, row = utilities_uv.get_UDIM_tile_coords(obj) 86 | if udim_tile != 1001: 87 | size = utilities_texel.get_tile_size(self, image, udim_tile) 88 | if not size: 89 | return [0, 0] 90 | else: 91 | size = min(image.size[0], image.size[1]) 92 | else: 93 | size = min(image.size[0], image.size[1]) 94 | 95 | elif getmode == 'SIZE': 96 | size = min(bpy.context.scene.texToolsSettings.size[0], bpy.context.scene.texToolsSettings.size[1]) 97 | else: 98 | size = int(getmode) 99 | 100 | 101 | area_uv_sq = 0 102 | area_vt_sq = 0 103 | 104 | # Get area for each face in UV space and 3D View 105 | if getmode != 'IMAGE' or (image and getmode == 'IMAGE'): 106 | for face in object_faces: 107 | # Decomposed face into triagles to calculate area 108 | tris = len(face.loops)-2 109 | if tris <=0: 110 | continue 111 | 112 | index = None 113 | area_uv = 0 114 | area_vt = 0 115 | 116 | for _ in range(tris): 117 | vA = face.loops[0][uv_layers].uv 118 | if index is None: 119 | origin = face.loops[0].link_loop_next 120 | else: 121 | for loop in face.loops: 122 | if loop.vert.index == index: 123 | origin = loop.link_loop_next 124 | break 125 | vB = origin[uv_layers].uv 126 | vC = origin.link_loop_next[uv_layers].uv 127 | 128 | area_uv += mathutils.geometry.area_tri(Vector(vA), Vector(vB), Vector(vC)) 129 | 130 | index = origin.vert.index 131 | 132 | area_vt += face.calc_area() 133 | 134 | area_uv_sq += math.sqrt( area_uv ) * size 135 | area_vt_sq += math.sqrt( area_vt ) 136 | 137 | return [area_uv_sq, area_vt_sq] 138 | -------------------------------------------------------------------------------- /op_texture_open.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | import os, sys, subprocess 4 | 5 | 6 | 7 | class op(bpy.types.Operator): 8 | bl_idname = "uv.textools_texture_open" 9 | bl_label = "Open Texture" 10 | bl_description = "Open the texture on the system" 11 | 12 | name : bpy.props.StringProperty( 13 | name="image name", 14 | default = "" 15 | ) 16 | 17 | @classmethod 18 | def poll(cls, context): 19 | return True 20 | 21 | 22 | def execute(self, context): 23 | open_texture(self, context) 24 | return {'FINISHED'} 25 | 26 | 27 | 28 | def open_texture(self, context): 29 | if self.name in bpy.data.images: 30 | image = bpy.data.images[self.name] 31 | 32 | if image.filepath != "": 33 | path = bpy.path.abspath(image.filepath) 34 | # https://meshlogic.github.io/posts/blender/addons/extra-image-list/ 35 | # https://docs.blender.org/api/blender_python_api_2_78_release/bpy.ops.image.html 36 | print("Open: {}".format(path)) 37 | 38 | if sys.platform == "win32": 39 | os.startfile(path) 40 | else: 41 | opener ="open" if sys.platform == "darwin" else "xdg-open" 42 | subprocess.call([opener, path]) 43 | -------------------------------------------------------------------------------- /op_texture_preview.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import os 3 | 4 | from . import settings 5 | from . import utilities_bake 6 | 7 | 8 | material_prefix = "TT_atlas_" 9 | gamma = 2.2 10 | 11 | 12 | 13 | class op(bpy.types.Operator): 14 | bl_idname = "uv.textools_texture_preview" 15 | bl_label = "Preview Texture" 16 | bl_description = "Preview the background Image of the UV Editor as a Material override on the appropriate selected Object" 17 | bl_options = {'REGISTER', 'UNDO'} 18 | 19 | @classmethod 20 | def poll(cls, context): 21 | if not bpy.context.active_object: 22 | return False 23 | if len(settings.sets) == 0: 24 | return False 25 | for area in bpy.context.screen.areas: 26 | if area.ui_type == 'UV': 27 | return area.spaces[0].image 28 | return False 29 | 30 | 31 | def execute(self, context): 32 | premode = bpy.context.active_object.mode 33 | preview_texture(self, context) 34 | bpy.ops.object.mode_set(mode=premode) 35 | # Change View mode to TEXTURED 36 | for area in bpy.context.screen.areas: 37 | if area.type == 'VIEW_3D': 38 | for space in area.spaces: 39 | if space.type == 'VIEW_3D': 40 | if space.shading.type == 'SOLID': 41 | space.shading.color_type = 'TEXTURE' 42 | # Force redraw of viewport to update texture 43 | bpy.context.view_layer.update() 44 | return {'FINISHED'} 45 | 46 | 47 | 48 | def preview_texture(self, context): 49 | # Collect all low objects from bake sets 50 | objects = {ob for s in settings.sets for ob in s.objects_low if ob.data.uv_layers and ob.select_get()} 51 | 52 | if objects: 53 | # Get background image 54 | image = None 55 | for area in bpy.context.screen.areas: 56 | if area.ui_type == 'UV': 57 | image = area.spaces[0].image 58 | break 59 | 60 | if image: 61 | bpy.ops.object.mode_set(mode='OBJECT') 62 | base_nodegroup = get_override_nodegroup() 63 | base_material = bpy.data.materials.get('TT_material_override') 64 | preactiv_name = bpy.context.view_layer.objects.active.name 65 | 66 | for obj in objects: 67 | bpy.context.view_layer.objects.active = obj 68 | 69 | material_name = 'TT_material_override-' + obj.name 70 | if material_name in bpy.data.materials: 71 | material = bpy.data.materials[material_name] 72 | else: 73 | material = base_material.copy() 74 | material.name = 'TT_material_override-' + obj.name 75 | node_image = material.node_tree.nodes["image"] 76 | node_image.image = image 77 | 78 | if obj.modifiers: 79 | for m in obj.modifiers: 80 | if 'TT-material-override' in m.name: 81 | if m.node_group.name == 'TT-material-override-' + obj.name: 82 | break 83 | else: 84 | obj.modifiers.remove(m) 85 | else: 86 | obj.modifiers.new(name='TT-material-override', type='NODES') 87 | nodegroup = base_nodegroup.copy() 88 | nodegroup.name = 'TT-material-override-' + obj.name 89 | node_material = nodegroup.nodes["material"] 90 | node_material.inputs[2].default_value = material 91 | obj.modifiers.active.node_group = nodegroup 92 | obj.modifiers.active.show_render = False 93 | else: 94 | obj.modifiers.new(name='TT-material-override', type='NODES') 95 | nodegroup = base_nodegroup.copy() 96 | nodegroup.name = 'TT-material-override-' + obj.name 97 | node_material = nodegroup.nodes["material"] 98 | node_material.inputs[2].default_value = material 99 | obj.modifiers.active.node_group = nodegroup 100 | obj.modifiers.active.show_render = False 101 | bpy.ops.object.modifier_move_to_index(modifier='TT-material-override', index=len(obj.modifiers)-1) 102 | 103 | bpy.context.view_layer.objects.active = bpy.data.objects[preactiv_name] 104 | 105 | 106 | 107 | def get_override_nodegroup(): 108 | if bpy.data.node_groups.get('TT-material-override') is None: 109 | path = os.path.join(os.path.dirname(__file__), "resources/materials_3.0.blend", "NodeTree") 110 | bpy.ops.wm.append(filename='TT-material-override', directory=path, link=False, autoselect=False) 111 | 112 | return bpy.data.node_groups.get('TT-material-override') 113 | -------------------------------------------------------------------------------- /op_texture_preview_cleanup.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | 4 | 5 | class op(bpy.types.Operator): 6 | bl_idname = "uv.textools_texture_preview_cleanup" 7 | bl_label = "Texture Preview Cleanup" 8 | bl_description = "Remove and unlink texture preview data from the selected Objects; + Alt , it's applied to all Objects in the Blendfile" 9 | bl_options = {'REGISTER', 'UNDO'} 10 | 11 | bool_all: bpy.props.BoolProperty(name="On all Objects", default=False) 12 | 13 | def invoke(self, context, event): 14 | self.bool_all = event.alt 15 | self.execute(context) 16 | return {'FINISHED'} 17 | 18 | 19 | def execute(self, context): 20 | premode = bpy.context.active_object.mode 21 | 22 | if self.bool_all: 23 | group = {ob for ob in bpy.data.objects if ob.type == 'MESH'} 24 | else: 25 | bpy.ops.object.mode_set(mode='EDIT', toggle=False) 26 | group = {ob for ob in bpy.context.objects_in_mode_unique_data if ob.type == 'MESH' and ob.select_get()} 27 | bpy.ops.object.mode_set(mode='OBJECT', toggle=False) 28 | 29 | for obj in group: 30 | if obj.modifiers: 31 | for m in obj.modifiers: 32 | if m.name == 'TT-material-override': 33 | obj.modifiers.remove(m) 34 | 35 | for nodegroup in bpy.data.node_groups: 36 | if nodegroup and 'TT-material-override' in nodegroup.name: 37 | if not nodegroup.users: 38 | bpy.data.node_groups.remove(nodegroup, do_unlink=True) 39 | 40 | for material in bpy.data.materials: 41 | if material and 'TT_material_override' in material.name: 42 | if not material.users: 43 | bpy.data.materials.remove(material, do_unlink=True) 44 | 45 | bpy.ops.object.mode_set(mode=premode) 46 | return {'FINISHED'} 47 | -------------------------------------------------------------------------------- /op_texture_reload_all.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | 4 | 5 | class op(bpy.types.Operator): 6 | bl_idname = "uv.textools_texture_reload_all" 7 | bl_label = "Reload Textures and remove unused Textures" 8 | bl_description = "Reload all textures" 9 | 10 | @classmethod 11 | def poll(cls, context): 12 | return True 13 | 14 | 15 | def execute(self, context): 16 | main(context) 17 | return {'FINISHED'} 18 | 19 | 20 | 21 | def main(context): 22 | count_clear_mat = 0 23 | count_clear_img = 0 24 | count_reload = 0 25 | 26 | for material in bpy.data.materials: 27 | if not material.users: 28 | count_clear_mat += 1 29 | bpy.data.materials.remove(material, do_unlink=True) 30 | 31 | # Clean up unused images 32 | for image in bpy.data.images: 33 | if not image.users: 34 | count_clear_img += 1 35 | bpy.data.images.remove(image, do_unlink=True) 36 | 37 | #Reload all File images 38 | for img in bpy.data.images : 39 | if img.source == 'FILE': 40 | count_reload += 1 41 | img.reload() 42 | 43 | # Refresh vieport texture 44 | for window in bpy.context.window_manager.windows: 45 | screen = window.screen 46 | for area in screen.areas: 47 | area.tag_redraw() 48 | 49 | # Show popup on cleared & reloaded items 50 | message = "" 51 | if count_reload > 0: 52 | message += "{}x reloaded. ".format(count_reload) 53 | if count_clear_mat > 0: 54 | message += "{}x mat cleared. ".format(count_clear_mat) 55 | if count_clear_img > 0: 56 | message += "{}x img cleared.".format(count_clear_img) 57 | 58 | if len(message) > 0: 59 | bpy.ops.ui.textools_popup('INVOKE_DEFAULT', message=message) 60 | -------------------------------------------------------------------------------- /op_texture_remove.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | 4 | 5 | class op(bpy.types.Operator): 6 | bl_idname = "uv.textools_texture_remove" 7 | bl_label = "Remove Texture" 8 | 9 | name : bpy.props.StringProperty(name="Image name", default="") 10 | 11 | 12 | def execute(self, context): 13 | if self.name in bpy.data.images: 14 | #bpy.data.batch_remove([bpy.data.images[self.name]]) 15 | bpy.data.images.remove(bpy.data.images[self.name], do_unlink=True) 16 | return {'FINISHED'} 17 | -------------------------------------------------------------------------------- /op_texture_save.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from bpy.props import * 4 | 5 | 6 | 7 | class op(bpy.types.Operator): 8 | bl_idname = "uv.textools_texture_save" 9 | bl_label = "Save Texture" 10 | bl_description = "Save the texture" 11 | 12 | name : bpy.props.StringProperty( 13 | name="image name", 14 | default = "" 15 | ) 16 | 17 | # Properties used by the file browser 18 | # filepath = bpy.props.StringProperty(subtype="FILE_PATH") 19 | # http://nullege.com/codes/show/src%40b%40l%40blenderpython-HEAD%40scripts%40addons_extern%40io_scene_valvesource%40import_smd.py/90/bpy.context.window_manager.fileselect_add/python 20 | filepath : bpy.props.StringProperty(name="myName.png", description="Texture filepath", maxlen=1024, default="bla bla.png") 21 | filter_folder : BoolProperty(name="Filter folders", description="", default=True, options={'HIDDEN'}) 22 | filter_glob : StringProperty(default="*.png;*.tga;*.jpg;*.tif;*.exr", options={'HIDDEN'}) 23 | 24 | 25 | def invoke(self, context, event): 26 | # if self.filepath == "": 27 | # self.filepath = bpy.context.scene.FBXBundleSettings.path 28 | # blend_filepath = context.blend_data.filepath 29 | # https://blender.stackexchange.com/questions/6159/changing-default-text-value-in-file-dialogue 30 | context.window_manager.fileselect_add(self) 31 | return {'RUNNING_MODAL'} 32 | 33 | 34 | def draw(self, context): 35 | layout = self.layout 36 | layout.label(text="Choose your Unity Asset directory") 37 | 38 | @classmethod 39 | def poll(cls, context): 40 | return True 41 | 42 | 43 | def execute(self, context): 44 | save_texture(self.filepath) 45 | return {'FINISHED'} 46 | 47 | 48 | 49 | def save_texture(path): 50 | print("Save image.. "+path) 51 | 52 | 53 | # class op(bpy.types.Operator): 54 | # bl_idname = "uv.textools_texture_save" 55 | # bl_label = "Save Texture" 56 | # bl_description = "Save the texture" 57 | 58 | # name = bpy.props.StringProperty( 59 | # name="image name", 60 | # default = "" 61 | # ) 62 | 63 | # @classmethod 64 | # def poll(cls, context): 65 | # return True 66 | 67 | # def execute(self, context): 68 | # save_texture(self, context) 69 | # return {'FINISHED'} 70 | 71 | 72 | ''' 73 | class op_ui_image_save(bpy.types.Operator): 74 | bl_idname = "uv.textools_ui_image_save" 75 | bl_label = "Save image" 76 | bl_description = "Save this image" 77 | 78 | image_name = bpy.props.StringProperty( 79 | name="image name", 80 | default = "" 81 | ) 82 | 83 | @classmethod 84 | def poll(cls, context): 85 | return True 86 | 87 | def execute(self, context): 88 | # bpy.context.scene.tool_settings.use_uv_select_sync = False 89 | # bpy.ops.mesh.select_all(action='SELECT') 90 | 91 | print("Saving image {}".format(self.image_name)) 92 | # bpy.ops.image.save_as() 93 | return {'FINISHED'} 94 | ''' 95 | -------------------------------------------------------------------------------- /op_texture_select.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from . import op_bake 4 | 5 | 6 | 7 | class op(bpy.types.Operator): 8 | bl_idname = "uv.textools_texture_select" 9 | bl_label = "Select Texture" 10 | bl_description = "Select the texture and bake mode" 11 | 12 | name : bpy.props.StringProperty( 13 | name="image name", 14 | default = "" 15 | ) 16 | 17 | @classmethod 18 | def poll(cls, context): 19 | return True 20 | 21 | 22 | def execute(self, context): 23 | select_texture(self, context) 24 | return {'FINISHED'} 25 | 26 | 27 | 28 | def select_texture(self, context): 29 | # Set bake mode 30 | found_modes = [] 31 | for mode in op_bake.modes: 32 | if mode in self.name: 33 | found_modes.append(mode) 34 | 35 | mode = max(found_modes, key=len) 36 | print("Found mode: "+mode) 37 | 38 | prop = bpy.context.scene.bl_rna.properties["TT_bake_mode"] 39 | enum_values = [e.identifier for e in prop.enum_items] 40 | 41 | # find matching enum 42 | for key in enum_values: 43 | if (mode+".bip") == key: 44 | bpy.context.scene.TT_bake_mode = key 45 | break 46 | 47 | # Set background image 48 | if self.name in bpy.data.images: 49 | image = bpy.data.images[self.name] 50 | for area in bpy.context.screen.areas: 51 | if area.ui_type == 'UV': 52 | area.spaces[0].image = image 53 | -------------------------------------------------------------------------------- /op_unwrap_edge_peel.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | 4 | from . import utilities_ui 5 | from . import utilities_uv 6 | from . import op_rectify 7 | from . import settings 8 | 9 | 10 | 11 | class op(bpy.types.Operator): 12 | bl_idname = "uv.textools_unwrap_edge_peel" 13 | bl_label = "Edge Peel" 14 | bl_description = "Unwrap as a pipe along the edges selected in 3D Space" 15 | bl_options = {'REGISTER', 'UNDO'} 16 | 17 | @classmethod 18 | def poll(cls, context): 19 | if not bpy.context.active_object: 20 | return False 21 | if bpy.context.active_object.type != 'MESH': 22 | return False 23 | if bpy.context.active_object.mode != 'EDIT': 24 | return False 25 | if tuple(bpy.context.scene.tool_settings.mesh_select_mode)[1] == False: 26 | return False 27 | return True 28 | 29 | 30 | def execute(self, context): 31 | selection_mode = bpy.context.scene.tool_settings.uv_select_mode 32 | is_sync = bpy.context.scene.tool_settings.use_uv_select_sync 33 | context_override = utilities_ui.GetContextViewUV() 34 | if not context_override: 35 | self.report({'ERROR_INVALID_INPUT'}, "This tool requires an available UV/Image view") 36 | return {'CANCELLED'} 37 | 38 | padding = utilities_ui.get_padding() 39 | utilities_uv.multi_object_loop(unwrap_edges_pipe, self, context, padding) 40 | 41 | bpy.ops.uv.average_islands_scale() 42 | bpy.ops.uv.pack_islands(rotate=False, margin=padding) 43 | 44 | # Move to active UDIM Tile TODO pack if not {'CANCELLED'} in the active UDIM Tile when implemented in Blender master (watch out for versioning) 45 | udim_tile, column, row = utilities_uv.get_UDIM_tile_coords(bpy.context.active_object) 46 | if udim_tile != 1001: 47 | if settings.bversion >= 3.2: 48 | with bpy.context.temp_override(**context_override): 49 | bpy.ops.transform.translate(value=(column, row, 0), mirror=False, use_proportional_edit=False) 50 | else: 51 | bpy.ops.transform.translate(context_override, value=(column, row, 0), mirror=False, use_proportional_edit=False) 52 | 53 | # Workaround for selection not flushing properly from loops to EDGE Selection Mode, apparently since UV edge selection support was added to the UV space 54 | if settings.bversion >= 3.2: 55 | with bpy.context.temp_override(**context_override): 56 | bpy.ops.uv.select_mode(type='VERTEX') 57 | bpy.context.scene.tool_settings.uv_select_mode = selection_mode 58 | if is_sync: 59 | bpy.context.scene.tool_settings.use_uv_select_sync = True 60 | 61 | return {'FINISHED'} 62 | 63 | 64 | 65 | def unwrap_edges_pipe(self, context, padding): 66 | me = bpy.context.active_object.data 67 | bm = bmesh.from_edit_mesh(me) 68 | uv_layers = bm.loops.layers.uv.verify() 69 | 70 | # Verify that no faces are selected 71 | for face in bm.faces: 72 | if face.select: 73 | bpy.ops.mesh.select_all(action='DESELECT') 74 | self.report({'INFO'}, "No faces should be selected, only edge rings") 75 | return 76 | 77 | # Extend loop selection 78 | bpy.ops.mesh.loop_multi_select(ring=False) 79 | selected_edges = {edge for edge in bm.edges if edge.select} 80 | 81 | if len(selected_edges) == 0: 82 | bpy.ops.mesh.select_all(action='DESELECT') 83 | #self.report({'ERROR_INVALID_INPUT'}, "No edges selected in the view" ) 84 | return 85 | 86 | bpy.ops.mesh.select_linked(delimit=set()) 87 | bpy.ops.mesh.mark_seam(clear=True) 88 | selected_faces = {face for face in bm.faces if face.select} 89 | 90 | if not selected_faces: 91 | bpy.ops.mesh.select_all(action='DESELECT') 92 | self.report({'INFO'}, "It's not possible to perform the unwrap on loose edges" ) 93 | return 94 | 95 | for edge in selected_edges: 96 | edge.seam = True 97 | 98 | # "Follow active quad" kind of unwrap 99 | bpy.context.scene.tool_settings.use_uv_select_sync = False 100 | 101 | bpy.ops.mesh.select_all(action='DESELECT') 102 | bpy.ops.uv.select_all(action='DESELECT') 103 | for face in selected_faces: 104 | face.select = True 105 | for loop in face.loops: 106 | loop[uv_layers].select = True 107 | 108 | bpy.ops.uv.unwrap(method='ANGLE_BASED', margin=padding) 109 | 110 | 111 | # Rectify the unwrapped islands 112 | islands = utilities_uv.getSelectionIslands(bm, uv_layers, extend_selection_to_islands=True, selected_faces=selected_faces) 113 | 114 | for island in islands: 115 | unrectified_faces = set() 116 | rectified_faces = set() 117 | face_loops = {face : [loop for loop in face.loops] for face in island} 118 | active = {bm.faces.active} 119 | count = 3 120 | 121 | # Repeat Rectify until the result is not self-overlapping; max 3 iterations 122 | while count > 0: 123 | if count == 3: 124 | unrectified_faces = op_rectify.main(me, bm, uv_layers, island, face_loops, return_discarded_faces=True) 125 | rectified_faces = island.difference(unrectified_faces) 126 | for face in unrectified_faces: 127 | face.select_set(False) 128 | else: 129 | for f in rectified_faces: 130 | if f not in active: 131 | bm.faces.active = f 132 | active.add(f) 133 | break 134 | bpy.ops.mesh.select_all(action='DESELECT') 135 | for face in rectified_faces: 136 | face.select_set(True) 137 | for loop in face.loops: 138 | loop[uv_layers].select = True 139 | bpy.ops.uv.unwrap(method='ANGLE_BASED', margin=padding) 140 | op_rectify.main(me, bm, uv_layers, island, face_loops) 141 | 142 | if not rectified_faces: 143 | count = 0 144 | continue 145 | bpy.ops.uv.select_all(action='DESELECT') 146 | bpy.ops.uv.select_overlap(extend=False) 147 | for f in rectified_faces: 148 | if f.loops[0][uv_layers].select: 149 | count -= 1 150 | else: 151 | count = 0 152 | break 153 | 154 | # Reproject ngons and triangular faces 155 | if unrectified_faces: 156 | bpy.ops.mesh.select_all(action='DESELECT') 157 | for face in unrectified_faces: 158 | face.select_set(True) 159 | for loop in face.loops: 160 | loop[uv_layers].select = True 161 | bpy.ops.uv.unwrap(method='ANGLE_BASED', margin=padding) 162 | 163 | 164 | # Restore selection 165 | for face in selected_faces: 166 | face.select_set(True) 167 | for loop in face.loops: 168 | loop[uv_layers].select = True 169 | -------------------------------------------------------------------------------- /op_unwrap_faces_iron.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | 4 | from mathutils import Vector 5 | from . import utilities_uv 6 | from . import utilities_ui 7 | 8 | 9 | 10 | class op(bpy.types.Operator): 11 | bl_idname = "uv.textools_unwrap_faces_iron" 12 | bl_label = "Iron" 13 | bl_description = "Unwrap selected faces into a single UV Island" 14 | bl_options = {'REGISTER', 'UNDO'} 15 | 16 | @classmethod 17 | def poll(cls, context): 18 | if not bpy.context.active_object: 19 | return False 20 | if bpy.context.active_object.type != 'MESH': 21 | return False 22 | if bpy.context.active_object.mode != 'EDIT': 23 | return False 24 | return True 25 | 26 | 27 | def execute(self, context): 28 | udim_tile, column, row = utilities_uv.get_UDIM_tile_coords(bpy.context.active_object) 29 | utilities_uv.multi_object_loop(main, self, context, udim_tile, column, row) 30 | return {'FINISHED'} 31 | 32 | 33 | 34 | def main(self, context, udim_tile, column, row): 35 | pre_selection_mode = tuple(bpy.context.scene.tool_settings.mesh_select_mode) 36 | me = bpy.context.active_object.data 37 | bm = bmesh.from_edit_mesh(me) 38 | 39 | bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE') 40 | bpy.ops.mesh.mark_seam(clear=True) 41 | 42 | selected_faces = [f for f in bm.faces if f.select] 43 | 44 | # Hard edges to seams 45 | bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE') 46 | bpy.ops.mesh.region_to_loop() 47 | bpy.ops.mesh.mark_seam(clear=False) 48 | 49 | seams = False 50 | for face in selected_faces: 51 | face.select_set(True) 52 | if not seams: 53 | for edge in face.edges: 54 | if edge.seam: 55 | seams = True 56 | break 57 | if not seams: 58 | self.report({'INFO'}, "Unwrap not possible; don't select the entire object if it is manifold") 59 | return 60 | 61 | padding = utilities_ui.get_padding() 62 | bpy.ops.uv.unwrap(method='ANGLE_BASED', margin=padding) 63 | 64 | # Move to active UDIM Tile TODO unwrap in the active UDIM Tile when implemented in Blender master (watch out for versioning) 65 | uv_layers = bm.loops.layers.uv.verify() 66 | 67 | if udim_tile != 1001: 68 | for face in selected_faces: 69 | for loop in face.loops: 70 | loop[uv_layers].uv += Vector((column, row)) 71 | 72 | bpy.context.scene.tool_settings.mesh_select_mode = pre_selection_mode 73 | -------------------------------------------------------------------------------- /op_uv_channel_add.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from . import utilities_ui 4 | from . import utilities_uv 5 | 6 | 7 | 8 | class op(bpy.types.Operator): 9 | bl_idname = "uv.textools_uv_channel_add" 10 | bl_label = "Add UV Channel" 11 | bl_description = "Add a new UV channel to all the selected Objects" 12 | bl_options = {'REGISTER', 'UNDO'} 13 | 14 | @classmethod 15 | def poll(cls, context): 16 | if not bpy.context.active_object: 17 | return False 18 | if bpy.context.active_object.type != 'MESH': 19 | return False 20 | return True 21 | 22 | 23 | def execute(self, context): 24 | premode = bpy.context.active_object.mode 25 | utilities_uv.multi_object_loop(adduvs, self, context) 26 | bpy.ops.object.mode_set(mode=premode) 27 | return {'FINISHED'} 28 | 29 | 30 | 31 | def adduvs(self, context): 32 | if len( bpy.context.object.data.uv_layers ) == 0: 33 | bpy.context.active_object.data.uv_layers.new(name="UVMap") 34 | else: 35 | # Add new UV channel based on last 36 | #bpy.ops.mesh.uv_texture_add() 37 | name = "UVMap" + str(len(bpy.context.active_object.data.uv_layers)+1) 38 | bpy.context.active_object.data.uv_layers.new(name=name) 39 | 40 | # Get current index 41 | index = len(bpy.context.object.data.uv_layers)-1 42 | bpy.context.object.data.uv_layers.active_index = index 43 | bpy.context.scene.texToolsSettings.uv_channel = str(index) 44 | bpy.context.active_object.data.uv_layers[0].active_render = True 45 | -------------------------------------------------------------------------------- /op_uv_channel_remove.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from . import utilities_uv 3 | 4 | 5 | 6 | class op(bpy.types.Operator): 7 | bl_idname = "uv.textools_uv_channel_remove" 8 | bl_label = "Remove UV Channel" 9 | bl_description = "Remove the active UV channel from all the selected Objects" 10 | bl_options = {'REGISTER', 'UNDO'} 11 | 12 | @classmethod 13 | def poll(cls, context): 14 | if not bpy.context.active_object: 15 | return False 16 | if bpy.context.active_object.type != 'MESH': 17 | return False 18 | if not bpy.context.object.data.uv_layers: 19 | return False 20 | return True 21 | 22 | 23 | def execute(self, context): 24 | premode = bpy.context.active_object.mode 25 | utilities_uv.multi_object_loop(removeuvs, self, context) 26 | bpy.ops.object.mode_set(mode=premode) 27 | return {'FINISHED'} 28 | 29 | 30 | 31 | def removeuvs(self, context): 32 | if bpy.context.object.data.uv_layers: 33 | # Remove active UV channel 34 | bpy.context.active_object.data.uv_layers.remove(bpy.context.object.data.uv_layers.active) 35 | 36 | # Get current index 37 | index = len(bpy.context.object.data.uv_layers)-1 38 | if index >= 0: 39 | bpy.context.object.data.uv_layers.active_index = index 40 | bpy.context.scene.texToolsSettings.uv_channel = str(index) 41 | bpy.context.active_object.data.uv_layers[0].active_render = True 42 | -------------------------------------------------------------------------------- /op_uv_channel_swap.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from . import utilities_uv 4 | 5 | 6 | 7 | class op(bpy.types.Operator): 8 | bl_idname = "uv.textools_uv_channel_swap" 9 | bl_label = "Move UV Channel" 10 | bl_description = "Move active UV channel up or down in all the selected Objects" 11 | bl_options = {'REGISTER', 'UNDO'} 12 | 13 | is_down : bpy.props.BoolProperty(default=False, options={'HIDDEN'}) 14 | 15 | @classmethod 16 | def poll(cls, context): 17 | if bpy.context.active_object == None: 18 | return False 19 | if bpy.context.active_object.type != 'MESH': 20 | return False 21 | return True 22 | 23 | 24 | def execute(self, context): 25 | premode = bpy.context.active_object.mode 26 | utilities_uv.multi_object_loop(swapuvs, self, context) 27 | bpy.ops.object.mode_set(mode=premode) 28 | return {'FINISHED'} 29 | 30 | 31 | def swapuvs(self, context): 32 | uv_layers = bpy.context.object.data.uv_layers 33 | 34 | if uv_layers.active_index == 0 and not self.is_down: 35 | return {'FINISHED'} 36 | elif uv_layers.active_index == len(uv_layers)-1 and self.is_down: 37 | return {'FINISHED'} 38 | 39 | def get_index(name): 40 | return ([i for i in range(len(uv_layers)) if uv_layers[i].name == name])[0] 41 | 42 | def move_bottom(name): 43 | # Set index 44 | uv_layers.active_index = get_index(name) 45 | # Copy (to bottom) 46 | bpy.ops.mesh.uv_texture_add() 47 | # Delete previous 48 | uv_layers.active_index = get_index(name) 49 | bpy.ops.mesh.uv_texture_remove() 50 | # Rename new 51 | uv_layers.active_index = len(uv_layers)-1 52 | uv_layers.active.name = name 53 | 54 | count = len(uv_layers) 55 | 56 | index_A = uv_layers.active_index 57 | index_B = index_A + (1 if self.is_down else -1) 58 | 59 | if not self.is_down: 60 | # Move up 61 | for n in [uv_layers[i].name for i in range(index_B, count) if i != index_A]: 62 | move_bottom(n) 63 | bpy.context.scene.texToolsSettings.uv_channel = str(index_B) 64 | 65 | elif self.is_down: 66 | # Move down 67 | for n in [uv_layers[i].name for i in range(index_A, count) if i != index_B]: 68 | move_bottom(n) 69 | bpy.context.scene.texToolsSettings.uv_channel = str(index_B) 70 | -------------------------------------------------------------------------------- /op_uv_crop.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | from mathutils import Vector 4 | 5 | from . import utilities_uv 6 | from . import utilities_ui 7 | from . utilities_bbox import BBox 8 | 9 | 10 | class op(bpy.types.Operator): 11 | bl_idname = "uv.textools_uv_crop" 12 | bl_label = "Crop" 13 | bl_description = "Frame the selected UVs to the 0-1 UV area" 14 | bl_options = {'REGISTER', 'UNDO'} 15 | 16 | @classmethod 17 | def poll(cls, context): 18 | if bpy.context.area.ui_type != 'UV': 19 | return False 20 | if not bpy.context.active_object: 21 | return False 22 | if bpy.context.active_object.type != 'MESH': 23 | return False 24 | if bpy.context.active_object.mode != 'EDIT': 25 | return False 26 | if not bpy.context.object.data.uv_layers: 27 | return False 28 | return True 29 | 30 | def execute(self, context): 31 | return crop(self) 32 | 33 | 34 | def crop(self, distort=False, general_bbox=None): 35 | selected_obs = utilities_uv.selected_unique_objects_in_mode_with_uv() 36 | sync = bpy.context.scene.tool_settings.use_uv_select_sync 37 | if sync: 38 | selection_mode = tuple(bpy.context.scene.tool_settings.mesh_select_mode) 39 | bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE') 40 | else: 41 | selection_mode = bpy.context.scene.tool_settings.uv_select_mode 42 | # Clean selection so that only entirely selected UV faces remain selected 43 | bpy.ops.uv.select_split() 44 | 45 | if general_bbox is None: 46 | general_bbox = BBox() 47 | for obj in selected_obs: 48 | bm = bmesh.from_edit_mesh(obj.data) 49 | uv_layers = bm.loops.layers.uv.verify() 50 | if sync: 51 | selection = (f for f in bm.faces if f.select) 52 | else: 53 | selection = (f for f in bm.faces if f.loops[0][uv_layers].select and f.select) 54 | bbox = BBox.calc_bbox_uv(selection, uv_layers) 55 | general_bbox.union(bbox) 56 | 57 | if not general_bbox.is_valid: 58 | self.report({'ERROR'}, "Zero area") 59 | return {'CANCELLED'} 60 | 61 | prepivot = bpy.context.space_data.pivot_point 62 | precursor = bpy.context.space_data.cursor_location.copy() 63 | bpy.context.space_data.pivot_point = 'CURSOR' 64 | bpy.context.space_data.cursor_location = (0.0, 0.0) 65 | 66 | padding = utilities_ui.get_padding() 67 | 68 | # Scale to fit bounds 69 | 70 | scale_u = (1.0-padding) / general_bbox.width 71 | scale_v = (1.0-padding) / general_bbox.height 72 | 73 | if not distort: 74 | scale_u = scale_v = min(scale_u, scale_v) 75 | 76 | bpy.ops.transform.resize(value=(scale_u, scale_v, 1), constraint_axis=(False, False, False), mirror=False, use_proportional_edit=False) 77 | 78 | # Reposition 79 | delta_position = Vector((padding/2 - scale_u*general_bbox.min.x, padding/2 - scale_v*general_bbox.min.y, 0)) 80 | 81 | _, column, row = utilities_uv.get_UDIM_tile_coords(bpy.context.active_object) 82 | delta_position += Vector((column, row, 0)) 83 | bpy.ops.transform.translate(value=delta_position, mirror=False, use_proportional_edit=False) 84 | 85 | bpy.context.space_data.pivot_point = prepivot 86 | bpy.context.space_data.cursor_location = precursor 87 | if sync: 88 | bpy.context.scene.tool_settings.mesh_select_mode = selection_mode 89 | else: 90 | # Workaround for selection not flushing properly from loops to EDGE Selection Mode, apparently since UV edge selection support was added to the UV space 91 | bpy.ops.uv.select_mode(type='VERTEX') 92 | bpy.context.scene.tool_settings.uv_select_mode = selection_mode 93 | return {'FINISHED'} 94 | -------------------------------------------------------------------------------- /op_uv_fill.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | import mathutils 4 | 5 | from . import utilities_uv 6 | from . import op_uv_crop 7 | from . import settings 8 | from . utilities_bbox import BBox 9 | 10 | 11 | class op(bpy.types.Operator): 12 | bl_idname = "uv.textools_uv_fill" 13 | bl_label = "Fill" 14 | bl_description = "Fill the 0-1 UV area with the selected UVs" 15 | bl_options = {'REGISTER', 'UNDO'} 16 | 17 | align: bpy.props.BoolProperty(name='Align', description="Align orientation", default=False) 18 | 19 | @classmethod 20 | def poll(cls, context): 21 | if bpy.context.area.ui_type != 'UV': 22 | return False 23 | if not bpy.context.active_object: 24 | return False 25 | if bpy.context.active_object.type != 'MESH': 26 | return False 27 | if bpy.context.active_object.mode != 'EDIT': 28 | return False 29 | if not bpy.context.object.data.uv_layers: 30 | return False 31 | return True 32 | 33 | def execute(self, context): 34 | selected_obs = utilities_uv.selected_unique_objects_in_mode_with_uv() 35 | sync = bpy.context.scene.tool_settings.use_uv_select_sync 36 | if sync: 37 | selection_mode = tuple(bpy.context.scene.tool_settings.mesh_select_mode) 38 | bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE') 39 | else: 40 | # Clean selection so that only entirely selected UV faces remain selected 41 | bpy.ops.uv.select_split() 42 | 43 | points = [] 44 | bmesh_ref_count_save = [] 45 | for obj in selected_obs: 46 | bm = bmesh.from_edit_mesh(obj.data) 47 | uv_layers = bm.loops.layers.uv.verify() 48 | if sync: 49 | selection = (f for f in bm.faces if f.select) 50 | else: 51 | selection = (f for f in bm.faces if f.loops[0][uv_layers].select and f.select) 52 | points.extend(l[uv_layers].uv for f in selection for l in f.loops) 53 | bmesh_ref_count_save.append(bm) 54 | 55 | if not points: 56 | return {'CANCELLED'} 57 | 58 | points = [points[i] for i in mathutils.geometry.convex_hull_2d(points)] # It's relevant to reduce points 59 | rotatable = False 60 | if self.align: 61 | angle = utilities_uv.calc_min_align_angle_pt(points) 62 | rotatable = abs(angle) > 0.00001 63 | if rotatable: 64 | prepivot = bpy.context.space_data.pivot_point 65 | bpy.context.space_data.pivot_point = 'CENTER' 66 | if not (settings.bversion == 2.83 or settings.bversion == 2.91): 67 | angle = -angle 68 | bpy.ops.transform.rotate(value=angle) 69 | bpy.context.space_data.pivot_point = prepivot 70 | 71 | if self.align and rotatable: 72 | general_bbox = None 73 | else: 74 | general_bbox = BBox.calc_bbox(points) 75 | if not general_bbox.is_valid: 76 | self.report({'ERROR'}, "Zero area") 77 | return {'CANCELLED'} 78 | 79 | # Expand UV selection of all selected objects towards the UV space 0-1 limits 80 | ret = op_uv_crop.crop(self, distort=True, general_bbox=general_bbox) 81 | 82 | if sync: 83 | bpy.context.scene.tool_settings.mesh_select_mode = selection_mode 84 | 85 | return ret 86 | -------------------------------------------------------------------------------- /op_uv_size_get.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from . import utilities_texel 4 | 5 | 6 | 7 | class op(bpy.types.Operator): 8 | bl_idname = "uv.textools_uv_size_get" 9 | bl_label = "Get Size" 10 | bl_description = "Get the selected Object texture size" 11 | 12 | @classmethod 13 | def poll(cls, context): 14 | if not bpy.context.active_object: 15 | return False 16 | if bpy.context.active_object not in bpy.context.selected_objects: 17 | return False 18 | if bpy.context.active_object.type != 'MESH': 19 | return False 20 | return True 21 | 22 | 23 | def execute(self, context): 24 | get_size(self, context) 25 | return {'FINISHED'} 26 | 27 | 28 | 29 | def get_size(self, context): 30 | image = utilities_texel.get_object_texture_image(bpy.context.active_object) 31 | 32 | if not image: 33 | self.report({'ERROR_INVALID_INPUT'}, "No Texture found on selected object") 34 | return 35 | 36 | bpy.context.scene.texToolsSettings.size[0] = image.size[0] 37 | bpy.context.scene.texToolsSettings.size[1] = image.size[1] 38 | -------------------------------------------------------------------------------- /resources/bake_modes/alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/alpha.png -------------------------------------------------------------------------------- /resources/bake_modes/anisotropic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/anisotropic.png -------------------------------------------------------------------------------- /resources/bake_modes/anisotropic_rotation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/anisotropic_rotation.png -------------------------------------------------------------------------------- /resources/bake_modes/ao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/ao.png -------------------------------------------------------------------------------- /resources/bake_modes/ao_legacy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/ao_legacy.png -------------------------------------------------------------------------------- /resources/bake_modes/base_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/base_color.png -------------------------------------------------------------------------------- /resources/bake_modes/bevel_mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/bevel_mask.png -------------------------------------------------------------------------------- /resources/bake_modes/cavity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/cavity.png -------------------------------------------------------------------------------- /resources/bake_modes/clearcoat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/clearcoat.png -------------------------------------------------------------------------------- /resources/bake_modes/clearcoat_roughness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/clearcoat_roughness.png -------------------------------------------------------------------------------- /resources/bake_modes/combined.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/combined.png -------------------------------------------------------------------------------- /resources/bake_modes/curvature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/curvature.png -------------------------------------------------------------------------------- /resources/bake_modes/diffuse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/diffuse.png -------------------------------------------------------------------------------- /resources/bake_modes/displacment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/displacment.png -------------------------------------------------------------------------------- /resources/bake_modes/dust.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/dust.png -------------------------------------------------------------------------------- /resources/bake_modes/emission.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/emission.png -------------------------------------------------------------------------------- /resources/bake_modes/emission_strength.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/emission_strength.png -------------------------------------------------------------------------------- /resources/bake_modes/environment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/environment.png -------------------------------------------------------------------------------- /resources/bake_modes/glossiness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/glossiness.png -------------------------------------------------------------------------------- /resources/bake_modes/id_element.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/id_element.png -------------------------------------------------------------------------------- /resources/bake_modes/id_material.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/id_material.png -------------------------------------------------------------------------------- /resources/bake_modes/metallic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/metallic.png -------------------------------------------------------------------------------- /resources/bake_modes/normal_object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/normal_object.png -------------------------------------------------------------------------------- /resources/bake_modes/normal_object_bevel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/normal_object_bevel.png -------------------------------------------------------------------------------- /resources/bake_modes/normal_tangent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/normal_tangent.png -------------------------------------------------------------------------------- /resources/bake_modes/normal_tangent_bevel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/normal_tangent_bevel.png -------------------------------------------------------------------------------- /resources/bake_modes/paint_base.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/paint_base.png -------------------------------------------------------------------------------- /resources/bake_modes/position.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/position.png -------------------------------------------------------------------------------- /resources/bake_modes/roughness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/roughness.png -------------------------------------------------------------------------------- /resources/bake_modes/selection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/selection.png -------------------------------------------------------------------------------- /resources/bake_modes/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/shadow.png -------------------------------------------------------------------------------- /resources/bake_modes/sheen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/sheen.png -------------------------------------------------------------------------------- /resources/bake_modes/sheen_tint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/sheen_tint.png -------------------------------------------------------------------------------- /resources/bake_modes/smoothness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/smoothness.png -------------------------------------------------------------------------------- /resources/bake_modes/specular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/specular.png -------------------------------------------------------------------------------- /resources/bake_modes/specular_tint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/specular_tint.png -------------------------------------------------------------------------------- /resources/bake_modes/sss_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/sss_color.png -------------------------------------------------------------------------------- /resources/bake_modes/sss_strength.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/sss_strength.png -------------------------------------------------------------------------------- /resources/bake_modes/thickness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/thickness.png -------------------------------------------------------------------------------- /resources/bake_modes/transmission.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/transmission.png -------------------------------------------------------------------------------- /resources/bake_modes/transmission_roughness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/transmission_roughness.png -------------------------------------------------------------------------------- /resources/bake_modes/uv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/uv.png -------------------------------------------------------------------------------- /resources/bake_modes/wireframe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes/wireframe.png -------------------------------------------------------------------------------- /resources/bake_modes_bip/alpha.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/alpha.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/anisotropic.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/anisotropic.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/anisotropic_rotation.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/anisotropic_rotation.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/ao.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/ao.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/base_color.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/base_color.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/bevel_mask.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/bevel_mask.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/cavity.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/cavity.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/clearcoat.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/clearcoat.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/clearcoat_roughness.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/clearcoat_roughness.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/combined.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/combined.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/curvature.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/curvature.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/diffuse.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/diffuse.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/displacement.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/displacement.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/dust.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/dust.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/emission.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/emission.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/emission_strength.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/emission_strength.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/environment.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/environment.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/glossiness.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/glossiness.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/id_element.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/id_element.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/id_material.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/id_material.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/metallic.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/metallic.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/normal_object.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/normal_object.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/normal_object_bevel.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/normal_object_bevel.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/normal_tangent.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/normal_tangent.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/normal_tangent_bevel.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/normal_tangent_bevel.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/paint_base.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/paint_base.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/position.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/position.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/roughness.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/roughness.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/selection.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/selection.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/shadow.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/shadow.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/sheen.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/sheen.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/sheen_tint.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/sheen_tint.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/specular.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/specular.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/specular_tint.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/specular_tint.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/sss_color.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/sss_color.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/sss_strength.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/sss_strength.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/thickness.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/thickness.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/transmission.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/transmission.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/transmission_roughness.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/transmission_roughness.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/uv.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/uv.bip -------------------------------------------------------------------------------- /resources/bake_modes_bip/wireframe.bip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/bake_modes_bip/wireframe.bip -------------------------------------------------------------------------------- /resources/compositing.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/compositing.blend -------------------------------------------------------------------------------- /resources/materials.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/materials.blend -------------------------------------------------------------------------------- /resources/materials_2.80.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/materials_2.80.blend -------------------------------------------------------------------------------- /resources/materials_3.0.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franMarz/TexTools-Blender/89cb4cfe486de1643263b2e4d69d47c4ba3d886b/resources/materials_3.0.blend -------------------------------------------------------------------------------- /settings.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import re 3 | 4 | bversion_string = bpy.app.version_string 5 | bversion_reg = re.match("^(\d\.\d?\d)", bversion_string) 6 | bversion = float(bversion_reg.group(0)) 7 | 8 | selection_uv_mode = '' 9 | selection_uv_loops = set() 10 | selection_uv_pivot = '' 11 | selection_uv_pivot_pos = (0,0) 12 | 13 | use_uv_sync = False 14 | selection_mode = [False, False, True] 15 | selection_vert_indexies = set() 16 | selection_edge_indexies = set() 17 | selection_face_indexies = set() 18 | seam_edges = set() 19 | 20 | bake_error = '' 21 | bake_render_engine = '' 22 | bake_cycles_device = '' 23 | bake_cycles_samples = 1 24 | bake_target_mode = '' 25 | use_progressive_refine = False 26 | use_denoising = False 27 | bake_objects_hide_render = [] 28 | sets = [] 29 | 30 | 31 | def tt_settings(): 32 | return bpy.context.scene.texToolsSettings 33 | 34 | def prefs(): 35 | return bpy.context.preferences.addons[__package__].preferences -------------------------------------------------------------------------------- /t3dn_bip/__init__.py: -------------------------------------------------------------------------------- 1 | '''Load image previews in parallel and in any resolution. Supports BIP files.''' 2 | 3 | __version__ = '1.0.8.1' 4 | -------------------------------------------------------------------------------- /t3dn_bip/formats.py: -------------------------------------------------------------------------------- 1 | from base64 import b64decode 2 | 3 | 4 | class _BIPFormat: 5 | '''BIP format info.''' 6 | 7 | def __init__(self, magic: bytes): 8 | self.magic = magic 9 | 10 | 11 | BIP_FORMATS = { 12 | 'BIP2': _BIPFormat(magic=b'BIP2'), 13 | } 14 | 15 | MAGIC_LENGTH = max([len(spec.magic) for spec in BIP_FORMATS.values()]) 16 | -------------------------------------------------------------------------------- /t3dn_bip/utils.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import io 3 | from zlib import decompress 4 | from array import array 5 | from .formats import BIP_FORMATS, MAGIC_LENGTH 6 | 7 | 8 | 9 | def can_load(filepath: str) -> bool: 10 | '''Return whether an image can be loaded.''' 11 | # Read magic for format detection. 12 | with open(filepath, 'rb') as file: 13 | magic = file.read(MAGIC_LENGTH) 14 | 15 | # We support BIP (currently only BIP2). 16 | for spec in BIP_FORMATS.values(): 17 | if magic.startswith(spec.magic): 18 | return True 19 | 20 | return False 21 | 22 | 23 | def load_file(filepath: str) -> dict: 24 | '''Load image preview data from file. 25 | 26 | Args: 27 | filepath: The input file path. 28 | 29 | Returns: 30 | A dictionary with icon_size, icon_pixels, image_size, image_pixels. 31 | 32 | Raises: 33 | AssertionError: If pixel data type is not 32 bit. 34 | AssertionError: If pixel count does not match size. 35 | ''' 36 | with open(filepath, 'rb') as bip: 37 | magic = bip.read(MAGIC_LENGTH) 38 | 39 | if magic.startswith(BIP_FORMATS['BIP2'].magic): 40 | bip.seek(len(BIP_FORMATS['BIP2'].magic), io.SEEK_SET) 41 | 42 | count = int.from_bytes(bip.read(1), 'big') 43 | assert count > 0, 'the file contains no images' 44 | 45 | icon_size = [int.from_bytes(bip.read(2), 'big') for _ in range(2)] 46 | icon_length = int.from_bytes(bip.read(4), 'big') 47 | bip.seek(8 * (count - 2), io.SEEK_CUR) 48 | image_size = [int.from_bytes(bip.read(2), 'big') for _ in range(2)] 49 | image_length = int.from_bytes(bip.read(4), 'big') 50 | 51 | icon_content = decompress(bip.read(icon_length)) 52 | bip.seek(-image_length, io.SEEK_END) 53 | image_content = decompress(bip.read(image_length)) 54 | 55 | icon_pixels = array('i', icon_content) 56 | assert icon_pixels.itemsize == 4, 'unexpected bytes per pixel' 57 | length = icon_size[0] * icon_size[1] 58 | assert len(icon_pixels) == length, 'unexpected amount of pixels' 59 | 60 | image_pixels = array('i', image_content) 61 | assert image_pixels.itemsize == 4, 'unexpected bytes per pixel' 62 | length = image_size[0] * image_size[1] 63 | assert len(image_pixels) == length, 'unexpected amount of pixels' 64 | 65 | return { 66 | 'icon_size': icon_size, 67 | 'icon_pixels': icon_pixels, 68 | 'image_size': image_size, 69 | 'image_pixels': image_pixels, 70 | } 71 | 72 | raise ValueError('input is not a supported file format') 73 | 74 | 75 | def tag_redraw(): 76 | '''Redraw every region in Blender.''' 77 | for window in bpy.context.window_manager.windows: 78 | for area in window.screen.areas: 79 | for region in area.regions: 80 | region.tag_redraw() 81 | -------------------------------------------------------------------------------- /utilities_bbox.py: -------------------------------------------------------------------------------- 1 | import math 2 | from mathutils import Vector, Matrix 3 | 4 | 5 | class BBox: 6 | @classmethod 7 | def calc_bbox(cls, coords): 8 | xmin = math.inf 9 | xmax = -math.inf 10 | ymin = math.inf 11 | ymax = -math.inf 12 | 13 | for x, y in coords: 14 | if xmin > x: 15 | xmin = x 16 | if xmax < x: 17 | xmax = x 18 | if ymin > y: 19 | ymin = y 20 | if ymax < y: 21 | ymax = y 22 | return cls(xmin, xmax, ymin, ymax) 23 | 24 | @classmethod 25 | def calc_bbox_uv(cls, group, uv_layers, are_loops=False): 26 | xmin = math.inf 27 | xmax = -math.inf 28 | ymin = math.inf 29 | ymax = -math.inf 30 | 31 | if not are_loops: 32 | for face in group: 33 | for loop in face.loops: 34 | x, y = loop[uv_layers].uv 35 | if xmin > x: 36 | xmin = x 37 | if xmax < x: 38 | xmax = x 39 | if ymin > y: 40 | ymin = y 41 | if ymax < y: 42 | ymax = y 43 | else: 44 | for loop in group: 45 | x, y = loop[uv_layers].uv 46 | if xmin > x: 47 | xmin = x 48 | if xmax < x: 49 | xmax = x 50 | if ymin > y: 51 | ymin = y 52 | if ymax < y: 53 | ymax = y 54 | return cls(xmin, xmax, ymin, ymax) 55 | 56 | @classmethod 57 | def init_from_minmax(cls, min, max): 58 | bbox = cls(min[0], max[0], min[1], max[1]) 59 | bbox.sanitize() 60 | return bbox 61 | 62 | def __init__(self, xmin=math.inf, xmax=-math.inf, ymin=math.inf, ymax=-math.inf): 63 | self.xmin = xmin 64 | self.xmax = xmax 65 | self.ymin = ymin 66 | self.ymax = ymax 67 | 68 | def __str__(self): 69 | return f"xmin={self.xmin:.6}, xmax={self.xmax:.6}, ymin={self.ymin:.6}, ymax={self.ymax:.6}, width={self.width:.6}, height={self.height:.6}" 70 | 71 | @property 72 | def is_valid(self) -> bool: 73 | return (self.xmin < self.xmax) and (self.ymin < self.ymax) 74 | 75 | @property 76 | def max(self): 77 | return Vector((self.xmax, self.ymax)) 78 | 79 | @property 80 | def min(self): 81 | return Vector((self.xmin, self.ymin)) 82 | 83 | @property 84 | def left_upper(self): 85 | return Vector((self.xmin, self.ymax)) 86 | 87 | @property 88 | def left_bottom(self): 89 | return Vector((self.xmin, self.ymin)) 90 | 91 | @property 92 | def right_bottom(self): 93 | return Vector((self.xmax, self.ymin)) 94 | 95 | @property 96 | def right_upper(self): 97 | return Vector((self.xmax, self.ymax)) 98 | 99 | @property 100 | def upper(self): 101 | return Vector(((self.xmin + self.xmax) * 0.5, self.ymax)) 102 | 103 | @property 104 | def bottom(self): 105 | return Vector(((self.xmin + self.xmax) * 0.5, self.ymin)) 106 | 107 | @property 108 | def left(self): 109 | return Vector((self.xmin, (self.ymin + self.ymax) * 0.5)) 110 | 111 | @property 112 | def right(self): 113 | return Vector((self.xmax, (self.ymin + self.ymax) * 0.5)) 114 | 115 | @property 116 | def center(self): 117 | return Vector(((self.xmin + self.xmax) * 0.5, (self.ymin + self.ymax) * 0.5)) 118 | 119 | @property 120 | def width(self) -> float: 121 | return self.xmax - self.xmin 122 | 123 | @property 124 | def height(self) -> float: 125 | return self.ymax - self.ymin 126 | 127 | @property 128 | def max_lenght(self): 129 | return max(self.width, self.height) 130 | 131 | @property 132 | def min_lenght(self): 133 | return min(self.width, self.height) 134 | 135 | @property 136 | def half_width(self) -> float: 137 | return (self.xmax - self.xmin) * 0.5 138 | 139 | @property 140 | def half_height(self) -> float: 141 | return (self.ymax - self.ymin) * 0.5 142 | 143 | @property 144 | def area(self): 145 | return self.width * self.height 146 | 147 | @property 148 | def is_empty(self) -> bool: 149 | return (self.xmax <= self.xmin) or (self.ymax <= self.ymin) 150 | 151 | def union(self, other): 152 | if self.xmin > other.xmin: 153 | self.xmin = other.xmin 154 | if self.xmax < other.xmax: 155 | self.xmax = other.xmax 156 | if self.ymin > other.ymin: 157 | self.ymin = other.ymin 158 | if self.ymax < other.ymax: 159 | self.ymax = other.ymax 160 | return self 161 | 162 | def sanitize(self): 163 | if self.xmin > self.xmax: 164 | self.xmin, self.xmax = self.xmax, self.xmin 165 | if self.ymin > self.ymax: 166 | self.ymin, self.ymax = self.ymax, self.ymin 167 | # assert self.is_valid 168 | return self 169 | 170 | def do_minmax_v(self, xy): 171 | if xy[0] < self.xmin: 172 | self.xmin = xy[0] 173 | if xy[0] > self.xmax: 174 | self.xmax = xy[0] 175 | if xy[1] < self.ymin: 176 | self.ymin = xy[1] 177 | if xy[1] > self.ymax: 178 | self.ymax = xy[1] 179 | 180 | def clamp(self, xmin=0, ymin=0, xmax=1, ymax=1): 181 | if self.xmin < xmin: 182 | self.xmin = xmin 183 | if self.ymin < ymin: 184 | self.ymin = ymin 185 | if self.xmax > xmax: 186 | self.xmax = xmax 187 | if self.ymax > ymax: 188 | self.ymax = ymax 189 | 190 | def translate(self, delta): 191 | self.xmin, self.ymin = self.min + delta 192 | self.xmax, self.ymax = self.max + delta 193 | return self 194 | 195 | def rotate_expand(self, angle): 196 | center = self.center 197 | rot_matrix = Matrix.Rotation(-angle, 2) 198 | 199 | corner = self.right_upper - center 200 | corner_rot = corner @ rot_matrix 201 | corner_max = Vector((abs(corner_rot[0]), abs(corner_rot[1]))) 202 | 203 | corner.y *= -1 204 | corner_rot = corner @ rot_matrix 205 | corner_max[0] = max(corner_max[0], abs(corner_rot[0])) 206 | corner_max[1] = max(corner_max[1], abs(corner_rot[1])) 207 | 208 | self.xmin = center[0] - corner_max[0] 209 | self.xmax = center[0] + corner_max[0] 210 | self.ymin = center[1] - corner_max[1] 211 | self.ymax = center[1] + corner_max[1] 212 | 213 | return self 214 | 215 | def scale(self, scale): 216 | center = self.center 217 | self.xmin, self.ymin = (self.min - center) * scale + center 218 | self.xmax, self.ymax = (self.max - center) * scale + center 219 | return self.sanitize() 220 | 221 | 222 | def update(self, coords): 223 | for x, y in coords: 224 | if x < self.xmin: 225 | self.xmin = x 226 | if x > self.xmax: 227 | self.xmax = x 228 | if y < self.ymin: 229 | self.ymin = y 230 | if y > self.ymax: 231 | self.ymax = y 232 | -------------------------------------------------------------------------------- /utilities_meshtex.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | 4 | from mathutils import Vector 5 | 6 | 7 | 8 | # Find a mesh that contains UV mesh shape keys 9 | def find_uv_mesh(objects, insideModifiers=True): 10 | for obj in objects: 11 | # Requires mesh & UV channel 12 | if obj and obj.type == 'MESH': 13 | 14 | # Contains shape keys? 15 | if obj.data.shape_keys and len(obj.data.shape_keys.key_blocks) == 2: 16 | if "uv" in obj.data.shape_keys.key_blocks and "model" in obj.data.shape_keys.key_blocks: 17 | return obj 18 | 19 | if insideModifiers: 20 | # Find via mesh deform modifier 21 | if len(obj.modifiers) > 0: 22 | for modifier in obj.modifiers: 23 | if modifier.type == 'SURFACE_DEFORM': 24 | if modifier.target: 25 | if modifier.target.data.shape_keys and len(modifier.target.data.shape_keys.key_blocks) == 2: 26 | return modifier.target 27 | return None 28 | 29 | 30 | 31 | # # Find meshes that can be wrapped aka texture meshes 32 | def find_texture_meshes(objects): 33 | obj_textures = [] 34 | 35 | for obj in objects: 36 | if obj and obj.type == 'MESH': 37 | if find_uv_mesh([obj], insideModifiers=False) == None: 38 | if obj not in obj_textures: 39 | obj_textures.append(obj) 40 | 41 | return obj_textures 42 | 43 | 44 | 45 | def uv_mesh_clear(obj_uv): 46 | # Remove Previous Modifiers 47 | if "Solidify" in obj_uv.modifiers: 48 | obj_uv.modifiers.remove( obj_uv.modifiers["Solidify"] ) 49 | # Remove Solidify and push modifiers 50 | 51 | 52 | 53 | def uv_mesh_fit(obj_uv, obj_textures): 54 | # Clear first shape transition 55 | bpy.context.scene.texToolsSettings.meshtexture_wrap = 1 56 | 57 | # Clear modifiers first 58 | uv_mesh_clear(obj_uv) 59 | 60 | # Add solidify modifier 61 | modifier_solid = obj_uv.modifiers.new(name="Solidify", type='SOLIDIFY') 62 | modifier_solid.offset = 1 63 | modifier_solid.thickness = 0 #scale*0.1 #10% height 64 | modifier_solid.use_even_offset = True 65 | modifier_solid.thickness_clamp = 0 66 | modifier_solid.use_quality_normals = True 67 | 68 | min_z = obj_uv.location.z 69 | max_z = obj_uv.location.z 70 | for i in range(len(obj_textures)): 71 | obj = obj_textures[i] 72 | 73 | # Min Max Z 74 | if i == 0: 75 | min_z = get_bbox(obj)['min'].z 76 | max_z = get_bbox(obj)['max'].z 77 | else: 78 | min_z = min(min_z, get_bbox(obj)['min'].z) 79 | max_z = max(max_z, get_bbox(obj)['max'].z) 80 | 81 | # Set thickness 82 | size = max(0.1, (max_z - min_z)) 83 | min_z-= size*0.25 #Padding 84 | max_z+= size*0.25 85 | size = (max_z - min_z) 86 | 87 | modifier_solid.thickness = size 88 | 89 | # Set offset 90 | if size > 0: 91 | p_z = (obj_uv.location.z - min_z) / (max_z - min_z) 92 | modifier_solid.offset = -(p_z-0.5)/0.5 93 | else: 94 | modifier_solid.offset = 0 95 | 96 | 97 | 98 | def get_bbox(obj): 99 | corners = [obj.matrix_world @ Vector(corner) for corner in obj.bound_box] 100 | 101 | # Get world space Min / Max 102 | box_min = Vector((corners[0].x, corners[0].y, corners[0].z)) 103 | box_max = Vector((corners[0].x, corners[0].y, corners[0].z)) 104 | for corner in corners: 105 | # box_min.x = -8 106 | box_min.x = min(box_min.x, corner.x) 107 | box_min.y = min(box_min.y, corner.y) 108 | box_min.z = min(box_min.z, corner.z) 109 | 110 | box_max.x = max(box_max.x, corner.x) 111 | box_max.y = max(box_max.y, corner.y) 112 | box_max.z = max(box_max.z, corner.z) 113 | 114 | return { 115 | 'min':box_min, 116 | 'max':box_max, 117 | 'size':(box_max-box_min), 118 | 'center':box_min+(box_max-box_min)/2 119 | } 120 | -------------------------------------------------------------------------------- /utilities_texel.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | import math 4 | import re 5 | import os 6 | 7 | image_material_prefix = "TT_checker_" 8 | 9 | 10 | 11 | def get_object_texture_image(obj): 12 | # Search in material & texture slots 13 | for slot_mat in obj.material_slots: 14 | if slot_mat.material: 15 | 16 | # Check for traditional texture slots in material 17 | for slot_tex in slot_mat.material.texture_paint_slots: 18 | if slot_tex and slot_tex.texture and hasattr(slot_tex.texture , 'image'): 19 | return slot_tex.texture.image 20 | 21 | # Check if material uses Nodes 22 | if hasattr(slot_mat.material , 'node_tree'): 23 | if slot_mat.material.node_tree: 24 | for node in slot_mat.material.node_tree.nodes: 25 | if type(node) is bpy.types.ShaderNodeTexImage: 26 | if node.image: 27 | return node.image 28 | return None 29 | 30 | 31 | 32 | def image_resize(image, size_x, size_y): 33 | if image and image.source == 'FILE' or image.source == 'GENERATED': 34 | image.generated_width = int(size_x) 35 | image.generated_height = int(size_y) 36 | image.scale( int(size_x), int(size_y) ) 37 | 38 | 39 | 40 | def checker_images_cleanup(): 41 | #Unneeded materials have to be deleted before unneeded images because images have them as users 42 | for material in bpy.data.materials: 43 | if material and image_material_prefix in material.name: 44 | if not material.users: 45 | bpy.data.materials.remove(material, do_unlink=True) 46 | 47 | for image in bpy.data.images: 48 | if image and image_material_prefix in image.name: 49 | if not image.users: 50 | bpy.data.images.remove(image, do_unlink=True) 51 | 52 | 53 | def get_checker_name(mode, size_x, size_y): 54 | return f'{image_material_prefix}{size_x}x{size_y}_{mode}' 55 | 56 | 57 | stored_materials = {} 58 | stored_material_faces = {} 59 | def store_materials_clear(): 60 | stored_materials.clear() 61 | stored_material_faces.clear() 62 | 63 | 64 | 65 | def store_materials(obj): 66 | stored_materials[obj] = [] 67 | stored_material_faces[obj] = [] 68 | 69 | # Enter edit mode 70 | bpy.ops.object.select_all(action='DESELECT') 71 | obj.select_set(True) 72 | bpy.context.view_layer.objects.active = obj 73 | 74 | bpy.ops.object.mode_set(mode='EDIT') 75 | bm = bmesh.from_edit_mesh(obj.data) 76 | 77 | # for each slot backup the material 78 | for s in range(len(obj.material_slots)): 79 | slot = obj.material_slots[s] 80 | 81 | stored_materials[obj].append(slot.material) 82 | stored_material_faces[obj].append( [face.index for face in bm.faces if face.material_index == s] ) 83 | 84 | if slot and slot.material: 85 | slot.material.name = "backup_"+slot.material.name 86 | slot.material.use_fake_user = True 87 | 88 | # Back to object mode 89 | bpy.ops.object.mode_set(mode='OBJECT') 90 | 91 | 92 | 93 | def restore_materials(objs): 94 | if len(objs) == 0: 95 | return 96 | else: 97 | for obj in objs: 98 | if stored_materials.get(obj) == None : 99 | bpy.ops.object.mode_set(mode='OBJECT') 100 | bpy.ops.object.select_all(action='DESELECT') 101 | obj.select_set(True) 102 | bpy.context.view_layer.objects.active = obj 103 | count = len(obj.material_slots) 104 | for i in range(count): 105 | bpy.ops.object.material_slot_remove() 106 | objs = [obj for obj in objs if obj in stored_materials] 107 | 108 | for obj in objs: 109 | # Enter edit mode 110 | bpy.context.view_layer.objects.active = obj 111 | bpy.ops.object.mode_set(mode='EDIT') 112 | bm = bmesh.from_edit_mesh(obj.data) 113 | 114 | # Restore slots 115 | for index in range(len(stored_materials[obj])): 116 | material = stored_materials[obj][index] 117 | faces = stored_material_faces[obj][index] 118 | 119 | if material: 120 | material.name = material.name.replace('backup_', '') 121 | obj.material_slots[index].material = material 122 | material.use_fake_user = False 123 | 124 | # Face material indexies 125 | for face in bm.faces: 126 | if face.index in faces: 127 | face.material_index = index 128 | 129 | # Back to object mode 130 | bpy.ops.object.mode_set(mode='OBJECT') 131 | 132 | # Remove material slots if none before 133 | if len(stored_materials[obj]) == 0 : 134 | for i in range(len(obj.material_slots)): 135 | bpy.ops.object.material_slot_remove() 136 | 137 | 138 | def get_tile_size(self, image, udim_tile): 139 | tile_name = f"{image.name}.{udim_tile}.{image.file_format.lower()}" 140 | purge = False 141 | if tile_name not in bpy.data.images: 142 | base_image_location = bpy.path.abspath(image.filepath) 143 | base_tile = re.findall('\d{4}', base_image_location)[-1] 144 | image_location = base_image_location.replace(base_tile, str(udim_tile)) 145 | if not os.path.isfile(image_location): 146 | self.report({'INFO'}, f"Missing tile image {tile_name}") 147 | return 0 148 | else: 149 | bpy.data.images.load(image_location, check_existing=False) 150 | #bpy.ops.image.open(filepath=image_location, relative_path=False, use_udim_detecting=False) 151 | purge = True 152 | 153 | tile = bpy.data.images[tile_name] 154 | size = min(*tile.size) 155 | if purge: 156 | #bpy.data.batch_remove([tile]) 157 | bpy.data.images.remove(tile, do_unlink=True) 158 | 159 | return size 160 | -------------------------------------------------------------------------------- /utilities_ui.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import os 3 | 4 | from . import utilities_bake 5 | from . import op_bake 6 | from .t3dn_bip import previews 7 | from .settings import tt_settings, prefs 8 | 9 | from bpy.props import StringProperty 10 | 11 | class op_popup(bpy.types.Operator): 12 | bl_idname = "ui.textools_popup" 13 | bl_label = "Message" 14 | 15 | message: StringProperty() 16 | 17 | def execute(self, context): 18 | self.report({'INFO'}, self.message) 19 | print(self.message) 20 | return {'FINISHED'} 21 | 22 | def invoke(self, context, event): 23 | wm = context.window_manager 24 | return wm.invoke_popup(self, width=200) 25 | 26 | def draw(self, context): 27 | self.layout.label(text=self.message) 28 | 29 | 30 | size_textures = [ 31 | ('32', '32', ''), 32 | ('64', '64', ''), 33 | ('128', '128', ''), 34 | ('256', '256', ''), 35 | ('512', '512', ''), 36 | ('1024', '1024', ''), 37 | ('2048', '2048', ''), 38 | ('4096', '4096', ''), 39 | ('8192', '8192', '') 40 | ] 41 | 42 | # Preview collections created in the register function. 43 | preview_icons = previews.new(max_size=(32, 32)) 44 | thumbnail_previews: 'previews.ImagePreviewCollection | None' = None 45 | 46 | 47 | def icon_register(fileName): 48 | name = fileName.split('.')[0] # Don't include file extension 49 | icons_location = os.path.join(os.path.dirname(__file__), "icons_bip") 50 | preview_icons.load_safe(name, os.path.join(icons_location, fileName), 'IMAGE') 51 | 52 | 53 | def icon_get(name): 54 | return preview_icons[name].icon_id 55 | 56 | 57 | def generate_bake_mode_previews(): 58 | image_location = os.path.join(os.path.dirname(__file__), "resources/bake_modes_bip") 59 | enum_items = [] 60 | 61 | # Generate the thumbnails 62 | for i, image in enumerate(os.listdir(image_location)): 63 | mode = image[0:-4] 64 | if mode in op_bake.modes: 65 | filepath = os.path.join(image_location, image) 66 | thumb = thumbnail_previews.load_safe(filepath, filepath, 'IMAGE') 67 | enum_items.append((image, mode, "", thumb.icon_id, i)) 68 | 69 | return enum_items 70 | 71 | 72 | def GetContextView3D(): 73 | for window in bpy.context.window_manager.windows: 74 | screen = window.screen 75 | for area in screen.areas: 76 | if area.type == 'VIEW_3D': 77 | for region in area.regions: 78 | if region.type == 'WINDOW': 79 | # Stuff the override context with very common requests by operators. MORE COULD BE NEEDED! 80 | override = {'window': window, 'screen': screen, 'area': area, 'region': region, 'scene': bpy.context.scene, 81 | 'edit_object': bpy.context.edit_object, 'active_object': bpy.context.active_object, 82 | 'selected_objects': bpy.context.selected_objects} 83 | return override 84 | return None 85 | 86 | 87 | def GetContextViewUV(): 88 | for window in bpy.context.window_manager.windows: 89 | screen = window.screen 90 | for area in screen.areas: 91 | if area.ui_type == 'UV': 92 | for region in area.regions: 93 | if region.type == 'WINDOW': 94 | # Stuff the override context with very common requests by operators. MORE COULD BE NEEDED! 95 | override = {'window': window, 'screen': screen, 'area': area, 'region': region, 'scene': bpy.context.scene, 96 | 'edit_object': bpy.context.edit_object, 'active_object': bpy.context.active_object, 97 | 'selected_objects': bpy.context.selected_objects} 98 | return override 99 | return None 100 | 101 | 102 | def get_padding(): 103 | return tt_settings().padding / min(tt_settings().size) 104 | 105 | 106 | def get_bake_mode(): 107 | return str(bpy.context.scene.TT_bake_mode).replace('.bip', '').lower() 108 | 109 | 110 | def set_bake_color_space_int(bake_mode): 111 | color_space = prefs().bake_color_space_def 112 | if "normal_" in bake_mode: 113 | return 3 if color_space in ('ASTANDARD', 'APBR') else 1 114 | elif color_space == 'STANDARD': 115 | return 0 116 | elif color_space == 'ASTANDARD': 117 | return 2 118 | elif color_space in ('PBR', 'APBR'): 119 | if (op_bake.modes[bake_mode].material != "") or (bake_mode == 'transmission' and not prefs().bool_clean_transmission) or \ 120 | bake_mode in {'diffuse', 'base_color', 'sss_color', 'emission', 'environment', 'combined'}: 121 | return 0 if color_space == 'PBR' else 2 122 | return 1 if color_space == 'PBR' else 3 123 | 124 | if color_space not in {'PBR', 'APBR', 'STANDARD', 'ASTANDARD'}: 125 | raise NotImplementedError(f'{color_space=} not implement for set_bake_color_space_int()') 126 | raise NotImplementedError(f'{bake_mode=} is an invalid keyword argument for set_bake_color_space_int()') 127 | 128 | 129 | def on_bakemode_set(self, context): 130 | bake_mode = get_bake_mode() 131 | if set_bake_color_space_int(bake_mode) == 1: 132 | tt_settings().bake_color_space = 'Non-Color' 133 | elif set_bake_color_space_int(bake_mode) == 0: 134 | tt_settings().bake_color_space = 'sRGB' 135 | elif set_bake_color_space_int(bake_mode) == 3: 136 | tt_settings().bake_color_space = 'Utility - Linear - sRGB' 137 | else: 138 | tt_settings().bake_color_space = 'Utility - sRGB - Texture' 139 | utilities_bake.on_select_bake_mode(bake_mode) 140 | 141 | 142 | 143 | 144 | def register(): 145 | global thumbnail_previews 146 | thumbnail_previews = previews.new(max_size=(128, 128)) 147 | 148 | # This is an EnumProperty for storing all images 149 | # You really can save it anywhere in bpy.types.* Just make sure the location makes sense 150 | bpy.types.Scene.TT_bake_mode = bpy.props.EnumProperty( 151 | items=generate_bake_mode_previews(), 152 | update=on_bakemode_set, 153 | default='normal_tangent.bip' 154 | ) 155 | 156 | 157 | def unregister(): 158 | try: 159 | previews.remove(thumbnail_previews) 160 | previews.remove(preview_icons) 161 | # del bpy.types.Scene.TT_bake_mode 162 | 163 | except ResourceWarning as e: 164 | print(e) 165 | --------------------------------------------------------------------------------