diff options
Diffstat (limited to 'oscurart_tools/oscurart_meshes.py')
-rw-r--r-- | oscurart_tools/oscurart_meshes.py | 562 |
1 files changed, 562 insertions, 0 deletions
diff --git a/oscurart_tools/oscurart_meshes.py b/oscurart_tools/oscurart_meshes.py new file mode 100644 index 00000000..99116a08 --- /dev/null +++ b/oscurart_tools/oscurart_meshes.py @@ -0,0 +1,562 @@ +# ##### 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 ( + IntProperty, + BoolProperty, + FloatProperty, + EnumProperty, + ) +import os +import bmesh +import time +import blf +from bpy_extras.view3d_utils import location_3d_to_region_2d + + +# -----------------------------RECONST--------------------------- + +def defReconst(self, OFFSET): + bpy.ops.object.mode_set(mode='EDIT', toggle=False) + bpy.context.tool_settings.mesh_select_mode = (True, True, True) + ob = bpy.context.active_object + bm = bmesh.from_edit_mesh(ob.data) + bm.select_flush(False) + for vertice in bm.verts[:]: + if abs(vertice.co[0]) < OFFSET: + vertice.co[0] = 0 + for vertice in bm.verts[:]: + if vertice.co[0] < 0: + bm.verts.remove(vertice) + bmesh.update_edit_mesh(ob.data) + # mod = ob.modifiers.new("Mirror", "MIRROR") + # uv = ob.data.uv_textures.new(name="SYMMETRICAL") + for v in bm.faces: + v.select = 1 + bmesh.update_edit_mesh(ob.data) + ob.data.uv_textures.active = ob.data.uv_textures['SYMMETRICAL'] + bpy.ops.uv.unwrap( + method='ANGLE_BASED', + fill_holes=True, + correct_aspect=False, + use_subsurf_data=0) + bpy.ops.object.mode_set(mode="OBJECT", toggle=False) + bpy.ops.object.modifier_apply(apply_as='DATA', modifier="Mirror") + bpy.ops.object.mode_set(mode="EDIT", toggle=False) + bm = bmesh.from_edit_mesh(ob.data) + bm.select_flush(0) + # uv = ob.data.uv_textures.new(name="ASYMMETRICAL") + ob.data.uv_textures.active = ob.data.uv_textures['ASYMMETRICAL'] + bpy.ops.uv.unwrap( + method='ANGLE_BASED', + fill_holes=True, + correct_aspect=False, + use_subsurf_data=0) + + +class reConst(Operator): + bl_idname = "mesh.reconst_osc" + bl_label = "ReConst Mesh" + bl_options = {"REGISTER", "UNDO"} + OFFSET = FloatProperty( + name="Offset", + default=0.001, + min=-0, + max=0.1 + ) + + @classmethod + def poll(cls, context): + return (context.active_object is not None and + context.active_object.type == 'MESH') + + def execute(self, context): + defReconst(self, self.OFFSET) + return {'FINISHED'} + + +# -----------------------------------SELECT LEFT--------------------- + +def side(self, nombre, offset): + + bpy.ops.object.mode_set(mode="EDIT", toggle=0) + OBJECT = bpy.context.active_object + ODATA = bmesh.from_edit_mesh(OBJECT.data) + bpy.context.tool_settings.mesh_select_mode = (True, False, False) + for VERTICE in ODATA.verts[:]: + VERTICE.select = False + if nombre is False: + for VERTICES in ODATA.verts[:]: + if VERTICES.co[0] < (offset): + VERTICES.select = 1 + else: + for VERTICES in ODATA.verts[:]: + if VERTICES.co[0] > (offset): + VERTICES.select = 1 + ODATA.select_flush(False) + bpy.ops.object.mode_set(mode="EDIT", toggle=0) + + +class SelectMenor (Operator): + bl_idname = "mesh.select_side_osc" + bl_label = "Select Side" + bl_options = {"REGISTER", "UNDO"} + + @classmethod + def poll(cls, context): + return (context.active_object is not None and + context.active_object.type == 'MESH') + + side = BoolProperty( + name="Greater than zero", + default=False + ) + offset = FloatProperty( + name="Offset", + default=0 + ) + + def execute(self, context): + + side(self, self.side, self.offset) + + return {'FINISHED'} + + +# -------------------------RESYM VG---------------------------------- + +class resymVertexGroups(Operator): + bl_idname = "mesh.resym_vertex_weights_osc" + bl_label = "Resym Vertex Weights" + bl_options = {"REGISTER", "UNDO"} + + @classmethod + def poll(cls, context): + return (context.active_object is not None and + context.active_object.type == 'MESH') + + def execute(self, context): + + with open("%s_%s_SYM_TEMPLATE.xml" % (os.path.join(os.path.dirname(bpy.data.filepath), + bpy.context.scene.name), bpy.context.object.name)) as file: + ob = bpy.context.object + actgr = ob.vertex_groups.active + actind = ob.vertex_groups.active_index + ls = eval(file.read()) + wdict = {left: actgr.weight(right) for left, right in ls.items() + for group in ob.data.vertices[right].groups if group.group == actind} + actgr.remove( + [vert.index for vert in ob.data.vertices if vert.co[0] <= 0]) + for ind, weight in wdict.items(): + actgr.add([ind], weight, 'REPLACE') + bpy.context.object.data.update() + + return {'FINISHED'} + + +# ------------------------IMPORT EXPORT GROUPS-------------------- + +class OscExportVG(Operator): + bl_idname = "file.export_groups_osc" + bl_label = "Export Groups" + bl_options = {"REGISTER", "UNDO"} + + @classmethod + def poll(cls, context): + return (context.active_object is not None and + context.active_object.type == 'MESH') + + def execute(self, context): + + ob = bpy.context.object + with open(os.path.join(os.path.dirname(bpy.data.filepath), ob.name + ".txt"), mode="w") as file: + vgindex = {vg.index: vg.name for vg in ob.vertex_groups[:]} + vgdict = {} + for vert in ob.data.vertices: + for vg in vert.groups: + vgdict.setdefault(vg.group, []).append( + (vert.index, vg.weight)) + file.write(str(vgdict) + "\n") + file.write(str(vgindex)) + + return {'FINISHED'} + + +class OscImportVG(Operator): + bl_idname = "file.import_groups_osc" + bl_label = "Import Groups" + bl_options = {"REGISTER", "UNDO"} + + @classmethod + def poll(cls, context): + return (context.active_object is not None and + context.active_object.type == 'MESH') + + def execute(self, context): + + ob = bpy.context.object + with open(os.path.join(os.path.dirname(bpy.data.filepath), ob.name + ".txt"), mode="r") as file: + vgdict = eval(file.readlines(1)[0].replace("\n", "")) + vgindex = eval(file.readlines(2)[0].replace("\n", "")) + + for index, name in vgindex.items(): + ob.vertex_groups.new(name=name) + + for group, vdata in vgdict.items(): + for index, weight in vdata: + ob.vertex_groups[group].add( + index=[index], + weight=weight, + type="REPLACE") + + return {'FINISHED'} + + +# ------------------------------------ RESYM MESH------------------------- + +def reSymSave(self, quality): + + bpy.ops.object.mode_set(mode='OBJECT') + + object = bpy.context.object + + rdqual = quality + rd = lambda x: round(x, rdqual) + absol = lambda x: (abs(x[0]), x[1], x[2]) + + inddict = { + tuple(map(rd, vert.co[:])): vert.index for vert in object.data.vertices[:]} + reldict = {inddict[vert]: inddict.get(absol(vert), inddict[vert]) + for vert in inddict if vert[0] <= 0} + + ENTFILEPATH = "%s_%s_SYM_TEMPLATE.xml" % ( + os.path.join(os.path.dirname(bpy.data.filepath), + bpy.context.scene.name), + bpy.context.object.name) + with open(ENTFILEPATH, mode="w") as file: + file.writelines(str(reldict)) + reldict.clear() + + +def reSymMesh(self, SELECTED, SIDE): + bpy.ops.object.mode_set(mode='EDIT') + ENTFILEPATH = "%s_%s_SYM_TEMPLATE.xml" % ( + os.path.join(os.path.dirname(bpy.data.filepath), + bpy.context.scene.name), + bpy.context.object.name) + with open(ENTFILEPATH, mode="r") as file: + SYMAP = eval(file.readlines()[0]) + bm = bmesh.from_edit_mesh(bpy.context.object.data) + object = bpy.context.object + + def MAME(SYMAP): + if SELECTED: + for vert in SYMAP: + if bm.verts[SYMAP[vert]].select: + bm.verts[vert].co = (-1 * bm.verts[SYMAP[vert]].co[0], + bm.verts[SYMAP[vert]].co[1], + bm.verts[SYMAP[vert]].co[2]) + else: + for vert in SYMAP: + bm.verts[vert].co = (-1 * bm.verts[SYMAP[vert]].co[0], + bm.verts[SYMAP[vert]].co[1], + bm.verts[SYMAP[vert]].co[2]) + bmesh.update_edit_mesh(object.data) + + def MEMA(SYMAP): + if SELECTED: + for vert in SYMAP: + if bm.verts[vert].select: + bm.verts[SYMAP[vert]].co = (-1 * bm.verts[vert].co[0], + bm.verts[vert].co[1], + bm.verts[vert].co[2]) + else: + for vert in SYMAP: + bm.verts[SYMAP[vert]].co = (-1 * bm.verts[vert].co[0], + bm.verts[vert].co[1], + bm.verts[vert].co[2]) + bmesh.update_edit_mesh(object.data) + + if SIDE == "+-": + MAME(SYMAP) + else: + MEMA(SYMAP) + + +class OscResymSave(Operator): + bl_idname = "mesh.resym_save_map" + bl_label = "Resym save XML Map" + bl_options = {"REGISTER", "UNDO"} + + @classmethod + def poll(cls, context): + return (context.active_object is not None and + context.active_object.type == 'MESH') + + quality = IntProperty( + default=4, + name="Quality" + ) + + def execute(self, context): + reSymSave(self, self.quality) + return {'FINISHED'} + + +class OscResymMesh(Operator): + bl_idname = "mesh.resym_mesh" + bl_label = "Resym save Apply XML" + bl_options = {"REGISTER", "UNDO"} + + @classmethod + def poll(cls, context): + return (context.active_object is not None and + context.active_object.type == 'MESH') + + selected = BoolProperty( + default=False, + name="Only Selected" + ) + side = EnumProperty( + name="Side:", + description="Select Side", + items=(('+-', "+X to -X", "+X to -X"), + ('-+', "-X to +X", "-X to +X")), + default='+-', + ) + + def execute(self, context): + reSymMesh(self, self.selected, self.side) + return {'FINISHED'} + + +# -------------------------- OBJECT TO MESH ------------------------------ + +def DefOscObjectToMesh(): + ACTOBJ = bpy.context.object + MESH = ACTOBJ.to_mesh( + scene=bpy.context.scene, + apply_modifiers=True, + settings="RENDER", + calc_tessface=True) + OBJECT = bpy.data.objects.new(("%s_Freeze") % (ACTOBJ.name), MESH) + bpy.context.scene.objects.link(OBJECT) + + +class OscObjectToMesh(Operator): + bl_idname = "mesh.object_to_mesh_osc" + bl_label = "Object To Mesh" + bl_description = "Works on Meshes, Meta objects, Curves and Surfaces" + + @classmethod + def poll(cls, context): + return (context.active_object is not None and + context.active_object.type in + {'MESH', 'META', 'CURVE', 'SURFACE'}) + + def execute(self, context): + print("Active type object is", context.object.type) + DefOscObjectToMesh() + return {'FINISHED'} + + +# ----------------------------- OVERLAP UV ------------------------------- + +def DefOscOverlapUv(valpresicion): + inicio = time.time() + mode = bpy.context.object.mode + bpy.ops.object.mode_set(mode='OBJECT', toggle=False) + + rd = valpresicion + ob = bpy.context.object + absco = lambda x: (abs(round(x[0], rd)), round(x[1], rd), round(x[2], rd)) + rounder = lambda x: (round(x[0], rd), round(x[1], rd), round(x[2], rd)) + + # vertice a vertex + vertvertex = {} + for vert in ob.data.loops: + vertvertex.setdefault(vert.vertex_index, []).append(vert.index) + + vertexvert = {} + for vertex in ob.data.loops: + vertexvert[vertex.index] = vertex.vertex_index + + # posicion de cada vertice y cada face + vertloc = {rounder(vert.co[:]): vert for vert in ob.data.vertices} + faceloc = {rounder(poly.center[:]): poly for poly in ob.data.polygons} + + # relativo de cada vertice y cada face + verteqind = {vert.index: vertloc.get( + absco(co), + vertloc[co]).index for co, + vert in vertloc.items() if co[0] <= 0} + polyeq = {face: faceloc.get( + absco(center), + faceloc[center]) for center, + face in faceloc.items() if center[0] <= 0} + + # loops in faces + lif = {poly: [i for i in poly.loop_indices] for poly in ob.data.polygons} + + # acomoda + for l, r in polyeq.items(): + if l.select: + for lloop in lif[l]: + for rloop in lif[r]: + if (verteqind[vertexvert[lloop]] == vertexvert[rloop] and + ob.data.uv_layers.active.data[rloop].select): + + ob.data.uv_layers.active.data[ + lloop].uv = ob.data.uv_layers.active.data[ + rloop].uv + + bpy.ops.object.mode_set(mode=mode, toggle=False) + + print("Time elapsed: %4s seconds" % (time.time() - inicio)) + + +class OscOverlapUv(Operator): + 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') + + presicion = IntProperty( + default=4, + min=1, + max=10, + name="precision" + ) + + def execute(self, context): + DefOscOverlapUv(self.presicion) + return {'FINISHED'} + + +# ------------------------------- IO VERTEX COLORS -------------------- + +def DefOscExportVC(): + with open(os.path.join(os.path.dirname(bpy.data.filepath), bpy.context.object.name) + ".vc", mode="w") as file: + ob = bpy.context.object + di = {loopind: ob.data.vertex_colors.active.data[loopind].color[:] + for face in ob.data.polygons for loopind in face.loop_indices[:]} + file.write(str(di)) + + +def DefOscImportVC(): + with open(os.path.join(os.path.dirname(bpy.data.filepath), bpy.context.object.name) + ".vc", mode="r") as file: + di = eval(file.read()) + for loopind in di: + bpy.context.object.data.vertex_colors.active.data[ + loopind].color = di[loopind] + + +class OscExportVC(Operator): + bl_idname = "mesh.export_vertex_colors" + bl_label = "Export Vertex Colors" + bl_options = {"REGISTER", "UNDO"} + + @classmethod + def poll(cls, context): + return (context.active_object is not None and + context.active_object.type == 'MESH') + + def execute(self, context): + DefOscExportVC() + return {'FINISHED'} + + +class OscImportVC(Operator): + bl_idname = "mesh.import_vertex_colors" + bl_label = "Import Vertex Colors" + bl_options = {"REGISTER", "UNDO"} + + @classmethod + def poll(cls, context): + return (context.active_object is not None and + context.active_object.type == 'MESH') + + def execute(self, context): + DefOscImportVC() + return {'FINISHED'} + + +# ------------------ PRINT VERTICES ---------------------- + +def dibuja_callback(self, context): + font_id = 0 + bm = bmesh.from_edit_mesh(bpy.context.object.data) + for v in bm.verts: + cord = location_3d_to_region_2d( + context.region, + context.space_data.region_3d, + self.matr * v.co) + blf.position(font_id, cord[0], cord[1], 0) + blf.size(font_id, self.tsize, 72) + blf.draw(font_id, str(v.index)) + + +class ModalIndexOperator(Operator): + bl_idname = "view3d.modal_operator" + bl_label = "Print Vertices" + + @classmethod + def poll(cls, context): + return (context.active_object is not None and + context.active_object.type == 'MESH') + + def modal(self, context, event): + context.area.tag_redraw() + if event.type == 'MOUSEMOVE': + self.x = event.mouse_region_x + self.matr = context.object.matrix_world + elif event.type == 'LEFTMOUSE': + bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') + return {'FINISHED'} + elif event.type == 'PAGE_UP': + self.tsize += 1 + elif event.type == 'PAGE_DOWN': + self.tsize -= 1 + elif event.type in {'RIGHTMOUSE', 'ESC', 'TAB'}: + bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') + context.area.header_text_set() + return {'CANCELLED'} + + return {'PASS_THROUGH'} + + def invoke(self, context, event): + if context.area.type == "VIEW_3D": + context.area.header_text_set("Esc: exit, PageUP/Down: text size") + bpy.ops.object.mode_set(mode="EDIT") + self.tsize = 20 + args = (self, context) + self._handle = bpy.types.SpaceView3D.draw_handler_add( + dibuja_callback, args, "WINDOW", "POST_PIXEL") + context.window_manager.modal_handler_add(self) + return{'RUNNING_MODAL'} + else: + self.report({"WARNING"}, "Is not a 3D Space") + return {'CANCELLED'} |