-
-
Save zeffii/be5fa97d1ec5053c43b9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ##### 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 ##### | |
import itertools | |
import random | |
import re | |
import bpy | |
from bpy.props import BoolProperty, StringProperty | |
from mathutils import Matrix, Vector | |
from sverchok.node_tree import ( | |
SverchCustomTreeNode, VerticesSocket, MatrixSocket, StringsSocket) | |
from sverchok.data_structure import dataCorrect, fullList, updateNode, SvGetSocketAnyType | |
from sverchok.utils.sv_bmesh_utils import bmesh_from_pydata | |
def matrix_sanitizer(matrix): | |
# reduces all values below threshold (+ or -) to 0.0, to avoid meaningless | |
# wandering floats. | |
coord_strip = lambda c: 0.0 if (-1.6e-5 <= c <= 1.6e-5) else c | |
san = lambda v: Vector((coord_strip(c) for c in v[:])) | |
return Matrix([san(v) for v in matrix]) | |
def default_mesh(name): | |
verts = [(1, 1, -1), (1, -1, -1), (-1, -1, -1)] | |
faces = [(0, 1, 2)] | |
mesh_data = bpy.data.meshes.new(name) | |
mesh_data.from_pydata(verts, [], faces) | |
mesh_data.update() | |
return mesh_data | |
def assign_empty_mesh(): | |
mt_name = 'empty_skin_mesh_sv' | |
if mt_name in bpy.data.meshes: | |
return bpy.data.meshes[mt_name] | |
else: | |
return bpy.data.meshes.new(mt_name) | |
def force_pydata(mesh, verts, edges, add_mesh_data=True): | |
if add_mesh_data: | |
mesh.vertices.add(len(verts)) | |
f_v = list(itertools.chain.from_iterable(verts)) | |
mesh.vertices.foreach_set('co', f_v) | |
mesh.update() | |
if add_mesh_data: | |
mesh.vertices.add(len(verts)) | |
mesh.edges.add(len(edges)) | |
f_e = list(itertools.chain.from_iterable(edges)) | |
mesh.edges.foreach_set('vertices', f_e) | |
mesh.update(calc_edges=True) | |
def make_bmesh_geometry(node, context, name, geometry): | |
scene = context.scene | |
meshes = bpy.data.meshes | |
objects = bpy.data.objects | |
verts, edges, matrix = geometry | |
change_in_place = False | |
# update in place, if possible. | |
if (name in objects): | |
mesh = objects[name].data | |
v_current_n = len(mesh.vertices) | |
v_propose_n = len(verts) | |
e_current_n = len(mesh.edges) | |
e_propose_n = len(edges) | |
# can simply overwrite verts+edges, in place | |
if (v_current_n == v_propose_n) and (e_current_n == e_propose_n): | |
force_pydata(mesh, verts, edges, add_mesh_data=False) | |
obj = objects[name] | |
if not ('sv_skin' in obj.modifiers): | |
obj.modifiers.new(type='SKIN', name='sv_skin') | |
else: | |
# remove object | |
if name in objects: | |
obj = objects[name] | |
# assign the object an empty mesh, this allows the current mesh | |
# to be uncoupled and removed from bpy.data.meshes | |
obj.data = assign_empty_mesh() | |
# remove mesh uncoupled mesh, and add it straight back. | |
if name in meshes: | |
meshes.remove(meshes[name]) | |
mesh = meshes.new(name) | |
obj.data = mesh | |
else: | |
# this is only executed once, upon the first run. | |
mesh = meshes.new(name) | |
obj = objects.new(name, mesh) | |
scene.objects.link(obj) | |
# at this point the mesh is always fresh and empty | |
force_pydata(obj.data, verts, edges) | |
# obj.update_tag(refresh={'OBJECT', 'DATA'}) | |
# context.scene.update() | |
if not obj.data.skin_vertices: | |
# if modifier present, remove | |
if 'sv_skin' in obj.modifiers: | |
sk = obj.modifiers['sv_skin'] | |
obj.modifiers.remove(sk) | |
# (re)add. | |
obj.modifiers.new(type='SKIN', name='sv_skin') | |
obj = objects[name] | |
if matrix: | |
matrix = matrix_sanitizer(matrix) | |
obj.matrix_local = matrix | |
else: | |
obj.matrix_local = Matrix.Identity(4) | |
class SkinNodeCallback(bpy.types.Operator): | |
bl_idname = "node.sv_skinnode_modifier_add" | |
bl_label = "Skin Node add Modifier" | |
# bl_options = {'REGISTER', 'UNDO'} | |
fn_name = bpy.props.StringProperty(default='') | |
# obj_type = bpy.props.StringProperty(default='MESH') | |
def dispatch(self, context, type_op): | |
n = context.node | |
if type_op == 'add_skin_modifier': | |
obj = bpy.data.objects[n.basemesh_name] | |
obj.modifiers.new(type='SKIN', name='sv_skin') | |
def execute(self, context): | |
self.dispatch(context, self.fn_name) | |
return {'FINISHED'} | |
class SkinViewerNode(bpy.types.Node, SverchCustomTreeNode): | |
bl_idname = 'SkinViewerNode' | |
bl_label = 'Skin Viewer Draw' | |
bl_icon = 'OUTLINER_OB_EMPTY' | |
activate = BoolProperty( | |
name='Show', | |
description='When enabled this will process incoming data', | |
default=True, | |
update=updateNode) | |
basemesh_name = StringProperty( | |
default='Alpha', | |
update=updateNode, | |
description="sets which base name the object will use, " | |
"use N-panel to pick alternative random names") | |
material = StringProperty(default='', update=updateNode) | |
autosmooth = BoolProperty( | |
default=False, | |
update=updateNode, | |
description="This auto sets all faces to smooth shade") | |
def sv_init(self, context): | |
self.use_custom_color = True | |
self.inputs.new('VerticesSocket', 'vertices', 'vertices') | |
self.inputs.new('StringsSocket', 'edges', 'edges') | |
self.inputs.new('MatrixSocket', 'matrix', 'matrix') | |
def draw_buttons(self, context, layout): | |
view_icon = 'MOD_ARMATURE' if self.activate else 'ARMATURE_DATA' | |
r = layout.row(align=True) | |
r.prop(self, "activate", text="", toggle=True, icon=view_icon) | |
r.prop(self, "basemesh_name", text="", icon='OUTLINER_OB_MESH') | |
r2 = layout.row(align=True) | |
cb = 'node.sv_skinnode_modifier_add' | |
r2.operator(cb, text='Add Skin').fn_name = 'add_skin_modifier' | |
def draw_buttons_ext(self, context, layout): | |
col = layout.column(align=True) | |
box = col.box() | |
if box: | |
box.label(text="Beta options") | |
box.prop(self, 'autosmooth', text='smooth shade') | |
def get_geometry_from_sockets(self): | |
i = self.inputs | |
mverts = i['vertices'].sv_get(default=[])[0] | |
medges = i['edges'].sv_get(default=[])[0] | |
mmtrix = i['matrix'].sv_get(default=[[]])[0] | |
return mverts, medges, mmtrix | |
def process(self): | |
if not self.activate: | |
return | |
# only interested in the first | |
geometry = self.get_geometry_from_sockets() | |
make_bmesh_geometry(self, bpy.context, self.basemesh_name, geometry) | |
obj = bpy.data.objects[self.basemesh_name] | |
if bpy.data.materials.get(self.material): | |
self.set_corresponding_materials(obj) | |
if self.autosmooth: | |
self.set_autosmooth(obj) | |
def set_corresponding_materials(self, obj): | |
obj.active_material = bpy.data.materials[self.material] | |
def set_autosmooth(self, obj): | |
mesh = obj.data | |
smooth_states = [True] * len(mesh.polygons) | |
mesh.polygons.foreach_set('use_smooth', smooth_states) | |
mesh.update() | |
def register(): | |
bpy.utils.register_class(SkinNodeCallback) | |
bpy.utils.register_class(SkinViewerNode) | |
def unregister(): | |
bpy.utils.unregister_class(SkinViewerNode) | |
bpy.utils.unregister_class(SkinNodeCallback) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment