diff --git a/docs/nodes/object_nodes/points_from_uv_to_mesh.rst b/docs/nodes/object_nodes/points_from_uv_to_mesh.rst new file mode 100644 index 0000000000..124a3b21da --- /dev/null +++ b/docs/nodes/object_nodes/points_from_uv_to_mesh.rst @@ -0,0 +1,33 @@ +Find UV Coord on Surface +======================== + +.. image:: https://github.com/nortikin/sverchok/assets/14288520/bc7f7630-6e01-4356-a214-af4546a8b04b + :target: https://github.com/nortikin/sverchok/assets/14288520/bc7f7630-6e01-4356-a214-af4546a8b04b + +Description +----------- + +.. image:: https://github.com/nortikin/sverchok/assets/14288520/63b0de53-4ba2-4097-9389-9ecf9c09a665 + :target: https://github.com/nortikin/sverchok/assets/14288520/63b0de53-4ba2-4097-9389-9ecf9c09a665 + +.. image:: https://github.com/nortikin/sverchok/assets/14288520/248f59bf-d468-415b-a7ec-0ef04d4f014e + :target: https://github.com/nortikin/sverchok/assets/14288520/248f59bf-d468-415b-a7ec-0ef04d4f014e + +.. image:: https://github.com/nortikin/sverchok/assets/14288520/445fde34-d6a1-4365-80dd-0b0e6fb32501 + :target: https://github.com/nortikin/sverchok/assets/14288520/445fde34-d6a1-4365-80dd-0b0e6fb32501 + +If you see this exception then check UV Map property. Here must at least one element: + +.. image:: https://github.com/nortikin/sverchok/assets/14288520/630537e2-65b5-4501-aba9-ff27c6d16e88 + :target: https://github.com/nortikin/sverchok/assets/14288520/630537e2-65b5-4501-aba9-ff27c6d16e88 + +Property +-------- + +Examples +-------- + +Nodes of description: + +.. image:: https://github.com/nortikin/sverchok/assets/14288520/9cbd1034-111c-40f0-8680-b5cbe53b9347 + :target: https://github.com/nortikin/sverchok/assets/14288520/9cbd1034-111c-40f0-8680-b5cbe53b9347 \ No newline at end of file diff --git a/index.yaml b/index.yaml index dc5ef7262b..e6edaf5752 100644 --- a/index.yaml +++ b/index.yaml @@ -767,7 +767,7 @@ - SvSetCustomUVMap - SvUVtextureNode - SvMeshUVColorNode - - SvUVPointonMeshNode + - SvUVPointonMeshNodeMK2 - SvSampleUVColorNode - SvArmaturePropsNode - SvLatticePropsNode diff --git a/menus/full_by_data_type.yaml b/menus/full_by_data_type.yaml index dffd188384..e905e2ae10 100644 --- a/menus/full_by_data_type.yaml +++ b/menus/full_by_data_type.yaml @@ -796,7 +796,7 @@ - SvSetCustomUVMap - SvUVtextureNode - SvMeshUVColorNode - - SvUVPointonMeshNode + - SvUVPointonMeshNodeMK2 - SvSampleUVColorNode - SvArmaturePropsNode - SvLatticePropsNode diff --git a/menus/full_nortikin.yaml b/menus/full_nortikin.yaml index 0e573b2c5c..f83e340cec 100644 --- a/menus/full_nortikin.yaml +++ b/menus/full_nortikin.yaml @@ -913,7 +913,7 @@ - SvSetCustomUVMap - SvUVtextureNode - SvMeshUVColorNode - - SvUVPointonMeshNode + - SvUVPointonMeshNodeMK2 - SvSampleUVColorNode - SvArmaturePropsNode - SvLatticePropsNode diff --git a/nodes/object_nodes/points_from_uv_to_mesh.py b/nodes/object_nodes/points_from_uv_to_mesh.py index 273a2109fe..e884f94b23 100644 --- a/nodes/object_nodes/points_from_uv_to_mesh.py +++ b/nodes/object_nodes/points_from_uv_to_mesh.py @@ -21,19 +21,14 @@ from mathutils.bvhtree import BVHTree from mathutils.geometry import barycentric_transform import numpy as np -from bpy.props import BoolProperty, StringProperty, FloatVectorProperty +from bpy.props import BoolProperty, StringProperty, FloatVectorProperty, EnumProperty from sverchok.node_tree import SverchCustomTreeNode from sverchok.data_structure import (updateNode) -def UV(self, object): +def UV(self, bm, uv_layer): # makes UV from layout texture area to sverchok vertices and polygons. - bm = bmesh.new() - bm.from_mesh(object.data) - uv_layer = bm.loops.layers.uv[0] - bm.verts.ensure_lookup_table() - bm.faces.ensure_lookup_table() vertices_dict = {} polygons_new = [] polygons_new_append = polygons_new.append @@ -49,13 +44,12 @@ def UV(self, object): polygons_new_append(polygons_new_pol) vertices_new = list( vertices_dict.values() ) - bm.clear() return [vertices_new, polygons_new] -class SvUVPointonMeshNode(SverchCustomTreeNode, bpy.types.Node): +class SvUVPointonMeshNodeMK2(SverchCustomTreeNode, bpy.types.Node): ''' Transform vectors from UV space to Object space ''' - bl_idname = 'SvUVPointonMeshNode' + bl_idname = 'SvUVPointonMeshNodeMK2' bl_label = 'Find UV Coord on Surface' bl_icon = 'GROUP_UVS' is_scene_dependent = True @@ -63,6 +57,35 @@ class SvUVPointonMeshNode(SverchCustomTreeNode, bpy.types.Node): object_ref: StringProperty(default='', update=updateNode) + apply_modifiers: BoolProperty( + name="Apply Modifiers", description="Off: use original object from scene\nOn: Apply modifiers before select UV Map", + default=False, update=updateNode) + + uv_select_modes = [ + ('active_item', "Active Selected", "UV Map selected by an active elem in the list of UV Maps of object data", 0), + ('active_render', "Active Render", "UV Map selected by property active_render in the list of UV Maps of object data (actived photo icon)", 1) + ] + + uv_select_mode : EnumProperty( + name = "Select UV Map by", + description = "UV Map select from object data property by", + items = uv_select_modes, + default = 'active_item', + update = updateNode) + + def sv_draw_buttons(self, context, layout): + row = layout.row() + col = row.column() + col.label(text='Apply midifiers:') + col = row.column() + col.alignment = 'LEFT' + col.prop(self, 'apply_modifiers', expand=True, text='') + row = layout.row() + row.column().label(text="Select UV Map by:") + row.column().prop(self, 'uv_select_mode', expand=True ) #, text='') + + + def sv_init(self, context): si, so = self.inputs.new, self.outputs.new si('SvObjectSocket', 'Mesh Object') @@ -75,31 +98,67 @@ def process(self): Object, PointsUV = self.inputs Pom, uvV, uvP = self.outputs obj = Object.sv_get()[0] # triangulate faces - UVMAPV, UVMAPP = UV(self,obj) + if not obj.data.uv_layers: + raise Exception(f"Object '{obj.data.name}' has no UV Maps. Open Properties->Data->UV Maps and check list of UV Maps.") + + # get all UV Maps name in object UV Maps list + uv_layer_active_render_name = obj.data.uv_layers[0].name + for uv in obj.data.uv_layers: + if uv.active_render==True: + uv_layer_active_render_name = uv.name # get UV Map name active render (photo mark) + break + + bm = bmesh.new() + if self.apply_modifiers: + # apply modifiers and build mesh after it + sv_depsgraph = bpy.context.evaluated_depsgraph_get() + scene_object = sv_depsgraph.objects[ obj.name ] + object_to_mesh = scene_object.to_mesh(preserve_all_data_layers=True, depsgraph=sv_depsgraph) + bm.from_mesh(object_to_mesh) + scene_object.to_mesh_clear() + else: + # get mesh of original object from scene + bm.from_mesh(obj.data) + + uv_layer_active = bm.loops.layers.uv.active + uv_layer_active_render = obj.data.uv_layers[0] + for uv in bm.loops.layers.uv: + if uv.name==uv_layer_active_render_name: + uv_layer_active_render = uv + break + + if self.uv_select_mode=='active_item': + uv_layer = uv_layer_active + else: #if self.uv_select_mode=='active_render': + uv_layer = uv_layer_active_render + + bm.verts.ensure_lookup_table() + bm.faces.ensure_lookup_table() + UVMAPV, UVMAPP = UV(self, bm, uv_layer) if Pom.is_linked: + # resore UV to 3D pointuv = PointsUV.sv_get()[0] bvh = BVHTree.FromPolygons(UVMAPV, UVMAPP, all_triangles=False, epsilon=0.0) - ran = range(3) - out = [] - uvMap = obj.data.uv_layers[0].data + out = [] # result in 3D for Puv in pointuv: loc, norm, ind, dist = bvh.find_nearest(Puv) - found_poly = obj.data.polygons[ind] - verticesIndices = found_poly.vertices - p1, p2, p3 = [obj.data.vertices[verticesIndices[i]].co for i in ran] - uvMapIndices = found_poly.loop_indices - uv1, uv2, uv3 = [uvMap[uvMapIndices[i]].uv.to_3d() for i in ran] - V = barycentric_transform(Puv, uv1, uv2, uv3, p1, p2, p3) - out.append(V[:]) + _found_poly = bm.faces[ind] + _p1, _p2, _p3 = [v.co for v in bm.faces[ind].verts[0:3] ] + _uv1, _uv2, _uv3 = [l[uv_layer].uv.to_3d() for l in _found_poly.loops[0:3] ] + _V = barycentric_transform(Puv, _uv1, _uv2, _uv3, _p1, _p2, _p3) + out.append(_V[:]) + Pom.sv_set([out]) + bm.clear() + if uvV.is_linked: uvV.sv_set([UVMAPV]) uvP.sv_set([UVMAPP]) def register(): - bpy.utils.register_class(SvUVPointonMeshNode) + bpy.utils.register_class(SvUVPointonMeshNodeMK2) def unregister(): - bpy.utils.unregister_class(SvUVPointonMeshNode) + bpy.utils.unregister_class(SvUVPointonMeshNodeMK2) diff --git a/old_nodes/points_from_uv_to_mesh.py b/old_nodes/points_from_uv_to_mesh.py new file mode 100644 index 0000000000..273a2109fe --- /dev/null +++ b/old_nodes/points_from_uv_to_mesh.py @@ -0,0 +1,105 @@ +# ##### 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 bpy +import bmesh +from mathutils.bvhtree import BVHTree +from mathutils.geometry import barycentric_transform +import numpy as np +from bpy.props import BoolProperty, StringProperty, FloatVectorProperty +from sverchok.node_tree import SverchCustomTreeNode + +from sverchok.data_structure import (updateNode) + + +def UV(self, object): + # makes UV from layout texture area to sverchok vertices and polygons. + bm = bmesh.new() + bm.from_mesh(object.data) + uv_layer = bm.loops.layers.uv[0] + bm.verts.ensure_lookup_table() + bm.faces.ensure_lookup_table() + vertices_dict = {} + polygons_new = [] + polygons_new_append = polygons_new.append + for fi in bm.faces: + polygons_new_pol = [] + polygons_new_pol_append = polygons_new_pol.append + for loop in fi.loops: + li = loop.index + polygons_new_pol_append(li) + uv = loop[uv_layer].uv + vertices_dict[li] = [ uv.x, uv.y, 0.0] + + polygons_new_append(polygons_new_pol) + + vertices_new = list( vertices_dict.values() ) + bm.clear() + return [vertices_new, polygons_new] + + +class SvUVPointonMeshNode(SverchCustomTreeNode, bpy.types.Node): + ''' Transform vectors from UV space to Object space ''' + bl_idname = 'SvUVPointonMeshNode' + bl_label = 'Find UV Coord on Surface' + bl_icon = 'GROUP_UVS' + is_scene_dependent = True + is_animation_dependent = True + + object_ref: StringProperty(default='', update=updateNode) + + def sv_init(self, context): + si, so = self.inputs.new, self.outputs.new + si('SvObjectSocket', 'Mesh Object') + si('SvVerticesSocket', 'Point on UV') + so('SvVerticesSocket', 'Point on mesh') + so('SvVerticesSocket', 'UVMapVert') + so('SvStringsSocket', 'UVMapPoly') + + def process(self): + Object, PointsUV = self.inputs + Pom, uvV, uvP = self.outputs + obj = Object.sv_get()[0] # triangulate faces + UVMAPV, UVMAPP = UV(self,obj) + if Pom.is_linked: + pointuv = PointsUV.sv_get()[0] + bvh = BVHTree.FromPolygons(UVMAPV, UVMAPP, all_triangles=False, epsilon=0.0) + ran = range(3) + out = [] + uvMap = obj.data.uv_layers[0].data + for Puv in pointuv: + loc, norm, ind, dist = bvh.find_nearest(Puv) + found_poly = obj.data.polygons[ind] + verticesIndices = found_poly.vertices + p1, p2, p3 = [obj.data.vertices[verticesIndices[i]].co for i in ran] + uvMapIndices = found_poly.loop_indices + uv1, uv2, uv3 = [uvMap[uvMapIndices[i]].uv.to_3d() for i in ran] + V = barycentric_transform(Puv, uv1, uv2, uv3, p1, p2, p3) + out.append(V[:]) + Pom.sv_set([out]) + if uvV.is_linked: + uvV.sv_set([UVMAPV]) + uvP.sv_set([UVMAPP]) + + +def register(): + bpy.utils.register_class(SvUVPointonMeshNode) + + +def unregister(): + bpy.utils.unregister_class(SvUVPointonMeshNode) diff --git a/tests/docs_tests.py b/tests/docs_tests.py index 24415fc4e0..1be75cbd2c 100644 --- a/tests/docs_tests.py +++ b/tests/docs_tests.py @@ -37,7 +37,7 @@ def test_uniq_node_modules(self): continue with self.subTest(node_module=f"{file_name}.py"): if file_name in modules: - self.fail(f'There are tow modules with the same name "{file_name}" \n' + self.fail(f'There are two modules with the same name "{file_name}" \n' f'File 1: {Path(modules[file_name]).relative_to(sv_root)} \n' f'file 2: {Path(path).relative_to(sv_root)}') else: @@ -59,7 +59,6 @@ def test_node_docs_existance(self): scene_raycast2.py sculpt_mask.py set_blenddata2.py -points_from_uv_to_mesh.py custom_mesh_normals.py color_uv_texture.py filter_blenddata.py