diff options
author | Eugenio Pignataro <info@oscurart.com.ar> | 2018-12-21 17:32:12 +0300 |
---|---|---|
committer | Eugenio Pignataro <info@oscurart.com.ar> | 2018-12-21 17:32:12 +0300 |
commit | 1f36b19604c4b62ac6acf47f93ad5863af2032de (patch) | |
tree | feb1aba800597300d202160e0ce5908883db2a69 /oscurart_tools | |
parent | 7fbe716603d46e6718893c15fe9548e0f1ff646f (diff) |
Add Oscurart Tools for 2.8
Diffstat (limited to 'oscurart_tools')
-rw-r--r-- | oscurart_tools/__init__.py | 166 | ||||
-rw-r--r-- | oscurart_tools/files/collect_images.py | 59 | ||||
-rw-r--r-- | oscurart_tools/files/reload_images.py | 37 | ||||
-rw-r--r-- | oscurart_tools/files/save_incremental.py | 70 | ||||
-rw-r--r-- | oscurart_tools/mesh/apply_linked_meshes.py | 60 | ||||
-rw-r--r-- | oscurart_tools/mesh/overlap_island.py | 100 | ||||
-rw-r--r-- | oscurart_tools/mesh/overlap_uvs.py | 131 | ||||
-rw-r--r-- | oscurart_tools/mesh/select_doubles.py | 76 | ||||
-rw-r--r-- | oscurart_tools/mesh/shapes_to_objects.py | 58 | ||||
-rw-r--r-- | oscurart_tools/object/distribute.py | 70 | ||||
-rw-r--r-- | oscurart_tools/object/search_and_select.py | 62 | ||||
-rw-r--r-- | oscurart_tools/object/selection.py | 65 |
12 files changed, 954 insertions, 0 deletions
diff --git a/oscurart_tools/__init__.py b/oscurart_tools/__init__.py new file mode 100644 index 00000000..9b2817cd --- /dev/null +++ b/oscurart_tools/__init__.py @@ -0,0 +1,166 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +bl_info = { + "name": "Oscurart Tools", + "author": "Oscurart, CodemanX", + "version": (4, 0, 0), + "blender": (2, 80, 0), + "location": "View3D > Toolbar and View3D > Specials (W-key)", + "description": "Tools for objects, render, shapes, and files.", + "warning": "", + "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/" + "Scripts/3D_interaction/Oscurart_Tools", + "category": "Object", + } + + +import bpy +from bpy.types import Menu +from oscurart_tools.files import reload_images +from oscurart_tools.files import save_incremental +from oscurart_tools.files import collect_images +from oscurart_tools.mesh import overlap_uvs +from oscurart_tools.mesh import overlap_island +from oscurart_tools.mesh import select_doubles +from oscurart_tools.mesh import shapes_to_objects +from oscurart_tools.object import distribute +from oscurart_tools.object import selection +from oscurart_tools.object import search_and_select +from oscurart_tools.mesh import apply_linked_meshes + +from bpy.types import ( + AddonPreferences, + Panel, + PropertyGroup, + ) +from bpy.props import ( + StringProperty, + BoolProperty, + IntProperty, + PointerProperty, + CollectionProperty, + ) + +# mesh +class VIEW3D_MT_edit_mesh_oscurarttools(Menu): + bl_label = "OscurartTools" + + def draw(self, context): + layout = self.layout + + layout.operator("mesh.uv_island_copy") + layout.operator("mesh.uv_island_paste") + layout.operator("mesh.select_doubles") + layout.separator() + layout.operator("image.reload_images_osc") + layout.operator("file.save_incremental_backup") + layout.operator("file.collect_all_images") + +def menu_funcMesh(self, context): + self.layout.menu("VIEW3D_MT_edit_mesh_oscurarttools") + self.layout.separator() + +# image +class IMAGE_MT_uvs_oscurarttools(Menu): + bl_label = "OscurartTools" + + def draw(self, context): + layout = self.layout + + layout.operator("mesh.uv_island_copy") + layout.operator("mesh.uv_island_paste") + layout.operator("mesh.overlap_uv_faces") + layout.separator() + layout.operator("image.reload_images_osc") + layout.operator("file.save_incremental_backup") + layout.operator("file.collect_all_images") + +def menu_funcImage(self, context): + self.layout.menu("IMAGE_MT_uvs_oscurarttools") + self.layout.separator() + + +# object +class VIEW3D_MT_object_oscurarttools(Menu): + bl_label = "OscurartTools" + + def draw(self, context): + layout = self.layout + + layout.operator("object.distribute_osc") + layout.operator("object.search_and_select_osc") + layout.operator("object.shape_key_to_objects_osc") + layout.operator("mesh.apply_linked_meshes") + layout.separator() + layout.operator("image.reload_images_osc") + layout.operator("file.save_incremental_backup") + layout.operator("file.collect_all_images") + +def menu_funcObject(self, context): + self.layout.menu("VIEW3D_MT_object_oscurarttools") + self.layout.separator() + +# ========================= End of Scripts ========================= + + +classes = ( + VIEW3D_MT_edit_mesh_oscurarttools, + IMAGE_MT_uvs_oscurarttools, + VIEW3D_MT_object_oscurarttools, + reload_images.reloadImages, + overlap_uvs.CopyUvIsland, + overlap_uvs.PasteUvIsland, + distribute.DistributeOsc, + selection.OscSelection, + save_incremental.saveIncrementalBackup, + collect_images.collectImagesOsc, + overlap_island.OscOverlapUv, + select_doubles.SelectDoubles, + shapes_to_objects.ShapeToObjects, + search_and_select.SearchAndSelectOt, + apply_linked_meshes.ApplyLRT, + ) + +def register(): + from bpy.types import Scene + Scene.multimeshedit = StringProperty() + bpy.types.VIEW3D_MT_edit_mesh_specials.prepend(menu_funcMesh) + bpy.types.IMAGE_MT_specials.prepend(menu_funcImage) + bpy.types.VIEW3D_MT_object_specials.prepend(menu_funcObject) + + from bpy.utils import register_class + for cls in classes: + register_class(cls) + + +def unregister(): + del bpy.types.Scene.SearchAndSelectOt + bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_funcMesh) + bpy.types.IMAGE_MT_specials.remove(menu_funcImage) + bpy.types.VIEW3D_MT_object_specials.remove(menu_funcObject) + + from bpy.utils import unregister_class + for cls in reversed(classes): + unregister_class(cls) + + +if __name__ == "__main__": + register() diff --git a/oscurart_tools/files/collect_images.py b/oscurart_tools/files/collect_images.py new file mode 100644 index 00000000..ce22700a --- /dev/null +++ b/oscurart_tools/files/collect_images.py @@ -0,0 +1,59 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import bpy +from bpy.types import Operator +import os +import shutil + + +class collectImagesOsc(Operator): + """Collect all images in the blend file and put them in IMAGES folder""" + bl_idname = "file.collect_all_images" + bl_label = "Collect Images" + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + + imagespath = "%s/IMAGES" % (os.path.dirname(bpy.data.filepath)) + + if not os.path.exists(imagespath): + os.mkdir(imagespath) + + bpy.ops.file.make_paths_absolute() + + for image in bpy.data.images: + try: + image.update() + + if image.has_data: + if not os.path.exists(os.path.join(imagespath,os.path.basename(image.filepath))): + shutil.copy(image.filepath, os.path.join(imagespath,os.path.basename(image.filepath))) + image.filepath = os.path.join(imagespath,os.path.basename(image.filepath)) + else: + print("%s exists." % (image.name)) + else: + print("%s missing path." % (image.name)) + except: + print("%s missing path." % (image.name)) + + bpy.ops.file.make_paths_relative() + + return {'FINISHED'} diff --git a/oscurart_tools/files/reload_images.py b/oscurart_tools/files/reload_images.py new file mode 100644 index 00000000..68b5c61b --- /dev/null +++ b/oscurart_tools/files/reload_images.py @@ -0,0 +1,37 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import bpy +from bpy.types import Operator + + + +class reloadImages (Operator): + """Reloads all bitmaps in the scene""" + bl_idname = "image.reload_images_osc" + bl_label = "Reload Images" + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + for imgs in bpy.data.images: + imgs.reload() + return {'FINISHED'} + + diff --git a/oscurart_tools/files/save_incremental.py b/oscurart_tools/files/save_incremental.py new file mode 100644 index 00000000..d655b08e --- /dev/null +++ b/oscurart_tools/files/save_incremental.py @@ -0,0 +1,70 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import bpy +from bpy.types import Operator +import os + + +def saveBkp (self, context): + fileFolder = os.path.dirname(bpy.data.filepath) + versionFolder = os.path.join(fileFolder,"VERSIONS") + + #creo folder + if os.path.exists(versionFolder): + print("existe") + else: + os.mkdir(versionFolder) + + #sin version a versionada + if not bpy.data.filepath.count("_v"): + filelist = [file for file in os.listdir(versionFolder) if file.count("_v") and not file.count("blend1")] + + filelower = 0 + print(filelist) + for file in filelist: + if int(file.split(".")[0][-2:]) > filelower: + filelower = int(file.split(".")[0][-2:]) + + savepath = "%s/VERSIONS/%s_v%02d.blend" % (os.path.dirname(bpy.data.filepath),bpy.path.basename(bpy.data.filepath).split('.')[0],filelower+1) + print("Copia versionada guardada.") + bpy.ops.wm.save_as_mainfile() + bpy.ops.wm.save_as_mainfile(filepath=savepath, copy=True) + + else: + #versionada a sin version + if bpy.data.filepath.count("_v"): + filename = "%s/../%s.blend" % (os.path.dirname(bpy.data.filepath),os.path.basename(bpy.data.filepath).rpartition(".")[0].rpartition("_")[0]) + print(filename) + bpy.ops.wm.save_as_mainfile(filepath=filename, copy=True) + print("Copia sin version guardada.") + + +class saveIncrementalBackup (bpy.types.Operator): + """Save incremental backup in versions folder""" + bl_idname = "file.save_incremental_backup" + bl_label = "Save Incremental Backup" + bl_options = {'REGISTER', 'UNDO'} + + def execute(self, context): + saveBkp(self, context) + return {'FINISHED'} + + diff --git a/oscurart_tools/mesh/apply_linked_meshes.py b/oscurart_tools/mesh/apply_linked_meshes.py new file mode 100644 index 00000000..7615c426 --- /dev/null +++ b/oscurart_tools/mesh/apply_linked_meshes.py @@ -0,0 +1,60 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import bpy + + + +def applyLRTEx(self, context): + actObj = bpy.context.active_object + actObjMatrixWorld = actObj.matrix_world.copy() + bpy.ops.object.select_linked(extend=False, type="OBDATA") + linkedObjects = bpy.context.selected_objects + linkedObjects.remove(actObj) + + for vert in actObj.data.vertices: + vert.co = actObjMatrixWorld @ vert.co + actObj.location = (0,0,0) + actObj.rotation_euler = (0,0,0) + actObj.scale = (1,1,1) + + for ob in linkedObjects: + ob.matrix_world = ob.matrix_world @ actObj.matrix_world.inverted() + + +class ApplyLRT(bpy.types.Operator): + """Apply LRT with linked mesh data""" + bl_idname = "mesh.apply_linked_meshes" + bl_label = "Apply LRT with linked meshes" + bl_options = {"REGISTER", "UNDO"} + + @classmethod + def poll(cls, context): + return (context.view_layer.objects.active is not None and + context.view_layer.objects.active.type == 'MESH') + + def execute(self, context): + applyLRTEx(self, context) + return {'FINISHED'} + + + + + diff --git a/oscurart_tools/mesh/overlap_island.py b/oscurart_tools/mesh/overlap_island.py new file mode 100644 index 00000000..68b3d339 --- /dev/null +++ b/oscurart_tools/mesh/overlap_island.py @@ -0,0 +1,100 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import bpy +from mathutils import Vector +from bpy.types import Operator +from bpy.props import ( + IntProperty, + BoolProperty, + FloatProperty, + EnumProperty, + ) +import bmesh +import time + +C = bpy.context +D = bpy.data + + +def DefOscOverlapUv(self,offset,rotate): + me = bpy.context.object.data + bm = bmesh.from_edit_mesh(me) + bm.faces.ensure_lookup_table() + faces = [face for face in bm.faces if face.select] + uv_layer = bm.loops.layers.uv[bpy.context.object.data.uv_layers.active.name] + + faceDict = {} + faceReverse = [] + bm.select_mode = {'FACE'} + for face in faces: + bpy.ops.mesh.select_all(action="DESELECT") + face.select = True + bpy.ops.mesh.select_mirror() + faceDict[face.index] = [mirrorface for mirrorface in bm.faces if mirrorface.select][0].index + faceReverse.append([mirrorface for mirrorface in bm.faces if mirrorface.select][0]) + + + + for selFace,mirrorFace in faceDict.items(): + for loop,mirrorLoop in zip(bm.faces[selFace].loops,bm.faces[mirrorFace].loops): + mirrorLoop.copy_from(loop) + if offset: + for loop,mirrorLoop in zip(bm.faces[selFace].loops,bm.faces[mirrorFace].loops): + mirrorLoop[uv_layer].uv += Vector((1,0)) + + + #invierto direcciones + bmesh.ops.reverse_uvs(bm, faces=[f for f in faceReverse]) + bmesh.ops.rotate_uvs(bm, faces=[f for f in faceReverse]) + if rotate: + bmesh.ops.rotate_uvs(bm, faces=[f for f in faceReverse]) + + bmesh.update_edit_mesh(me) + + +class OscOverlapUv(Operator): + """Overlaps the uvs on one side of the model symmetry plane. """ \ + """Useful to get more detail on fixed resolution bitmaps""" + bl_idname = "mesh.overlap_uv_faces" + bl_label = "Overlap Uvs" + bl_options = {"REGISTER", "UNDO"} + + @classmethod + def poll(cls, context): + return (context.active_object is not None and + context.active_object.type == 'MESH') + + + offset : BoolProperty( + default=True, + name="Offset" + ) + rotate : BoolProperty( + default=False, + name="Rotate" + ) + + def execute(self, context): + DefOscOverlapUv(self,self.offset,self.rotate) + return {'FINISHED'} + + + diff --git a/oscurart_tools/mesh/overlap_uvs.py b/oscurart_tools/mesh/overlap_uvs.py new file mode 100644 index 00000000..2960bb3d --- /dev/null +++ b/oscurart_tools/mesh/overlap_uvs.py @@ -0,0 +1,131 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import bpy +from mathutils import Vector +from bpy.types import Operator +from bpy.props import ( + IntProperty, + BoolProperty, + FloatProperty, + EnumProperty, + ) +import os +import bmesh + +C = bpy.context +D = bpy.data + + + +# -------------------------- OVERLAP UV ISLANDS + +def defCopyUvsIsland(self, context): + bpy.ops.object.mode_set(mode="OBJECT") + global obLoop + global islandFaces + obLoop = [] + islandFaces = [] + for poly in bpy.context.object.data.polygons: + if poly.select: + islandFaces.append(poly.index) + for li in poly.loop_indices: + obLoop.append(li) + + bpy.ops.object.mode_set(mode="EDIT") + +def defPasteUvsIsland(self, uvOffset, rotateUv,context): + bpy.ops.object.mode_set(mode="OBJECT") + selPolys = [poly.index for poly in bpy.context.object.data.polygons if poly.select] + + for island in selPolys: + bpy.ops.object.mode_set(mode="EDIT") + bpy.ops.mesh.select_all(action="DESELECT") + bpy.ops.object.mode_set(mode="OBJECT") + bpy.context.object.data.polygons[island].select = True + bpy.ops.object.mode_set(mode="EDIT") + bpy.ops.mesh.select_linked() + bpy.ops.object.mode_set(mode="OBJECT") + TobLoop = [] + TislandFaces = [] + for poly in bpy.context.object.data.polygons: + if poly.select: + TislandFaces.append(poly.index) + for li in poly.loop_indices: + TobLoop.append(li) + + for source,target in zip(range(min(obLoop),max(obLoop)+1),range(min(TobLoop),max(TobLoop)+1)): + bpy.context.object.data.uv_layers.active.data[target].uv = bpy.context.object.data.uv_layers.active.data[source].uv + Vector((uvOffset,0)) + + bpy.ops.object.mode_set(mode="EDIT") + + if rotateUv: + bpy.ops.object.mode_set(mode="OBJECT") + for poly in selPolys: + bpy.context.object.data.polygons[poly].select = True + bpy.ops.object.mode_set(mode="EDIT") + bm = bmesh.from_edit_mesh(bpy.context.object.data) + bmesh.ops.reverse_uvs(bm, faces=[f for f in bm.faces if f.select]) + bmesh.ops.rotate_uvs(bm, faces=[f for f in bm.faces if f.select]) + #bmesh.update_edit_mesh(bpy.context.object.data, tessface=False, destructive=False) + + + +class CopyUvIsland(Operator): + """Copy Uv Island""" + bl_idname = "mesh.uv_island_copy" + bl_label = "Copy Uv Island" + bl_options = {"REGISTER", "UNDO"} + + @classmethod + def poll(cls, context): + return (context.active_object is not None and + context.active_object.type == 'MESH' and + context.active_object.mode == "EDIT") + + def execute(self, context): + defCopyUvsIsland(self, context) + return {'FINISHED'} + +class PasteUvIsland(Operator): + """Paste Uv Island""" + bl_idname = "mesh.uv_island_paste" + bl_label = "Paste Uv Island" + bl_options = {"REGISTER", "UNDO"} + + uvOffset : BoolProperty( + name="Uv Offset", + default=False + ) + + rotateUv : BoolProperty( + name="Rotate Uv Corner", + default=False + ) + @classmethod + def poll(cls, context): + return (context.active_object is not None and + context.active_object.type == 'MESH' and + context.active_object.mode == "EDIT") + + def execute(self, context): + defPasteUvsIsland(self, self.uvOffset, self.rotateUv, context) + return {'FINISHED'} + diff --git a/oscurart_tools/mesh/select_doubles.py b/oscurart_tools/mesh/select_doubles.py new file mode 100644 index 00000000..15282b0b --- /dev/null +++ b/oscurart_tools/mesh/select_doubles.py @@ -0,0 +1,76 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import bpy +from mathutils import Vector +from bpy.types import Operator +from bpy.props import ( + IntProperty, + BoolProperty, + FloatProperty, + EnumProperty, + ) +import os +import bmesh + +C = bpy.context +D = bpy.data + + + +def SelDoubles(self, context): + bm = bmesh.from_edit_mesh(bpy.context.object.data) + + for v in bm.verts: + v.select = 0 + + dictloc = {} + + rd = lambda x: (round(x[0], 4), round(x[1], 4), round(x[2], 4)) + + for vert in bm.verts: + dictloc.setdefault(rd(vert.co), []).append(vert.index) + + for loc, ind in dictloc.items(): + if len(ind) > 1: + for v in ind: + bm.verts[v].select = 1 + + bpy.context.view_layer.objects.active = bpy.context.view_layer.objects.active + + +class SelectDoubles(Operator): + """Selects duplicated vertex without merge them""" + bl_idname = "mesh.select_doubles" + bl_label = "Select Doubles" + bl_options = {"REGISTER", "UNDO"} + + @classmethod + def poll(cls, context): + return (context.view_layer.objects.active is not None and + context.view_layer.objects.active.type == 'MESH' and + context.view_layer.objects.active.mode == "EDIT") + + def execute(self, context): + SelDoubles(self, context) + return {'FINISHED'} + + + diff --git a/oscurart_tools/mesh/shapes_to_objects.py b/oscurart_tools/mesh/shapes_to_objects.py new file mode 100644 index 00000000..3b9a80a1 --- /dev/null +++ b/oscurart_tools/mesh/shapes_to_objects.py @@ -0,0 +1,58 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import bpy +from bpy.types import Operator +from bpy.props import ( + BoolProperty, + FloatProperty, + ) +import math + + + +class ShapeToObjects(Operator): + """It creates a new object for every shapekey in the selected object, ideal to export to other 3D software Apps""" + bl_idname = "object.shape_key_to_objects_osc" + bl_label = "Shapes To Objects" + bl_options = {"REGISTER", "UNDO"} + + @classmethod + def poll(cls, context): + return (context.view_layer.objects.active is not None and + context.view_layer.objects.active.type in + {'MESH', 'SURFACE', 'CURVE'}) + + def execute(self, context): + OBJACT = bpy.context.view_layer.objects.active + has_keys = hasattr(getattr(OBJACT.data, "shape_keys", None), "key_blocks") + if has_keys: + for SHAPE in OBJACT.data.shape_keys.key_blocks[:]: + print(SHAPE.name) + bpy.ops.object.shape_key_clear() + SHAPE.value = 1 + mesh = OBJACT.to_mesh(bpy.context.depsgraph, True, calc_undeformed=False) + object = bpy.data.objects.new(SHAPE.name, mesh) + bpy.context.scene.collection.objects.link(object) + else: + self.report({'INFO'}, message="Active object doesn't have shape keys") + return {'CANCELLED'} + + return {'FINISHED'} diff --git a/oscurart_tools/object/distribute.py b/oscurart_tools/object/distribute.py new file mode 100644 index 00000000..991d664b --- /dev/null +++ b/oscurart_tools/object/distribute.py @@ -0,0 +1,70 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import bpy +import os +from bpy.types import Operator +from bpy.props import BoolProperty + + + +def ObjectDistributeOscurart(self, X, Y, Z): + if len(bpy.selection_osc[:]) > 1: + # VARIABLES + dif = bpy.selection_osc[-1].location - bpy.selection_osc[0].location + chunkglobal = dif / (len(bpy.selection_osc[:]) - 1) + chunkx = 0 + chunky = 0 + chunkz = 0 + deltafst = bpy.selection_osc[0].location + + # ORDENA + for OBJECT in bpy.selection_osc[:]: + if X: + OBJECT.location.x = deltafst[0] + chunkx + if Y: + OBJECT.location[1] = deltafst[1] + chunky + if Z: + OBJECT.location.z = deltafst[2] + chunkz + chunkx += chunkglobal[0] + chunky += chunkglobal[1] + chunkz += chunkglobal[2] + else: + self.report({'INFO'}, "Needs at least two selected objects") + + +class DistributeOsc(Operator): + """Distribute evenly the selected objects in x y z""" + bl_idname = "object.distribute_osc" + bl_label = "Distribute Objects" + Boolx : BoolProperty(name="X") + Booly : BoolProperty(name="Y") + Boolz : BoolProperty(name="Z") + + def execute(self, context): + ObjectDistributeOscurart(self, self.Boolx, self.Booly, self.Boolz) + return {'FINISHED'} + + def invoke(self, context, event): + self.Boolx = True + self.Booly = True + self.Boolz = True + return context.window_manager.invoke_props_dialog(self) + diff --git a/oscurart_tools/object/search_and_select.py b/oscurart_tools/object/search_and_select.py new file mode 100644 index 00000000..a576457c --- /dev/null +++ b/oscurart_tools/object/search_and_select.py @@ -0,0 +1,62 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import bpy +from bpy.types import Operator +from bpy.props import BoolProperty +from bpy.props import StringProperty + +# ------------------------ SEARCH AND SELECT ------------------------ + + +class SearchAndSelectOt(bpy.types.Operator): + """Search and select objects, by name""" + bl_idname = "object.search_and_select_osc" + bl_label = "Search And Select" + bl_options = {"REGISTER", "UNDO"} + + keyword : StringProperty(name="Keyword", default="Type Here") + start : BoolProperty(name="Start With", default=True) + count : BoolProperty(name="Contain", default=True) + end : BoolProperty(name="End", default=True) + + def execute(self, context): + for objeto in bpy.context.scene.objects: + variableNombre = self.keyword + if self.start: + if objeto.name.startswith(variableNombre): + objeto.select_set(True) + if self.count: + if objeto.name.count(variableNombre): + objeto.select_set(True) + if self.end: + if objeto.name.count(variableNombre): + objeto.select_set(True) + return {'FINISHED'} + + def invoke(self, context, event): + self.keyword = "Type Here" + self.start = True + self.count = True + self.end = True + return context.window_manager.invoke_props_dialog(self) + + + diff --git a/oscurart_tools/object/selection.py b/oscurart_tools/object/selection.py new file mode 100644 index 00000000..c2a264bc --- /dev/null +++ b/oscurart_tools/object/selection.py @@ -0,0 +1,65 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import bpy + + + +# ------------------------------------ SELECTION ------------------------- +bpy.selection_osc = [] + + +def select_osc(): + if bpy.context.mode == "OBJECT": + obj = bpy.context.object + sel = len(bpy.context.selected_objects) + + if sel == 0: + bpy.selection_osc = [] + else: + if sel == 1: + bpy.selection_osc = [] + bpy.selection_osc.append(obj) + elif sel > len(bpy.selection_osc): + for sobj in bpy.context.selected_objects: + if (sobj in bpy.selection_osc) is False: + bpy.selection_osc.append(sobj) + + elif sel < len(bpy.selection_osc): + for it in bpy.selection_osc: + if (it in bpy.context.selected_objects) is False: + bpy.selection_osc.remove(it) + + +class OscSelection(bpy.types.Header): + bl_label = "Selection Osc" + bl_space_type = "VIEW_3D" + + def __init__(self): + select_osc() + + def draw(self, context): + """ + layout = self.layout + row = layout.row() + row.label("Sels: "+str(len(bpy.selection_osc))) + """ + + |