diff --git a/docs/nodes/transforms/texture_displace.rst b/docs/nodes/transforms/texture_displace.rst new file mode 100644 index 0000000000000000000000000000000000000000..d874f6ad27d09df46491910d3c58959213f5beb7 --- /dev/null +++ b/docs/nodes/transforms/texture_displace.rst @@ -0,0 +1,86 @@ +Texture Displace +================ + +This node displaces a list of Vectors using a texture. + +Inputs & Parameters +------------------- + ++----------------+-------------------------------------------------------------------------+ +| Parameters | Description | ++================+=========================================================================+ +| Direction | Displacement direction. | +| | Available modes: Normal, X, Y, Z, Custom Axis, RGB to XYZ, HSV to XYZ | +| | and HLV to XYZ. | ++----------------+-------------------------------------------------------------------------+ +| Texture Coord. | Modes to match vertices and texture: | +| | | +| | - UV: Input a second set of verts to be the UV coordinates of | +| | the mesh. | +| | - Mesh Matrix: Matrix to mutiply the verts to get their UV coord. | +| | - Texture Matrix: Matrix of external object to set where in global | +| | space is the origin location and rotation of texture. | ++----------------+-------------------------------------------------------------------------+ +| Channel | Color channel to use as base of the displacement | +| | Offers: Red, Green, Blue, Hue, Saturation, Value, Alpha, RGB Average | +| | and Luminosity. | +| | Only in Normal, X, Y, Z, Custom Axis. | ++----------------+-------------------------------------------------------------------------+ +| Vertices | Vertices of the mesh to displace | ++----------------+-------------------------------------------------------------------------+ +| Polygons | Polygons of the mesh to displace | ++----------------+-------------------------------------------------------------------------+ +| Texture | Texture(s) to use as base | ++----------------+-------------------------------------------------------------------------+ +| Scale Out | Vector to multiply the added vector | ++----------------+-------------------------------------------------------------------------+ +| UV Coordinates | Second set of vertices to be the UV coordinates of the mesh | ++----------------+-------------------------------------------------------------------------+ +| Mesh Matrix | Matrix to multiply the vertices to get their UV coordinates | ++----------------+-------------------------------------------------------------------------+ +| Texture Matrix | Matrix of external object to set where in global space is the origin | +| | location and rotation of texture | ++----------------+-------------------------------------------------------------------------+ +| Middle Level | Texture Evaluation - Middle Level = displacement | ++----------------+-------------------------------------------------------------------------+ +| Strength | Displacement multiplier | ++----------------+-------------------------------------------------------------------------+ + +Examples +-------- + +Basic example + +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/transforms/texture_displace/texture_displace_sverchok_blender_example_1.png + +Multiple textures can be used with the hep of the Object ID selector: + +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/transforms/texture_displace/texture_displace_sverchok_blender_example_2.png + +When joining the texture list (to be [[texture, texture,...],[texture,...],...]) multiple textures can be used with a single mesh. (The node will match one texture per vertex) + +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/transforms/texture_displace/texture_displace_sverchok_blender_example_3.png + +"Axis Scale out" with multiply the effect multiplying each displace vector component-wise + +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/transforms/texture_displace/texture_displace_sverchok_blender_example_4.png + +The Texture Matrix mode of "Texture Coordinates" will work as the Displace Modifier with the texture Coordinates set to Object + +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/transforms/texture_displace/texture_displace_sverchok_blender_example_5.png + +The Mesh Matrix mode of "Texture Coordinates" will work as the Displace Modifier with the texture Coordinates set to Global in which the Matrix is treated as the mesh matrix + +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/transforms/texture_displace/texture_displace_sverchok_blender_example_6.png + +The "UV" mode of "Texture Coordinates" will work as the Displace Modifier with the texture Coordinates set to UV in which the new set of vertices is used as the UV Coordinates of the first mesh + +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/transforms/texture_displace/texture_displace_sverchok_blender_example_7.png + +The node can use also "Image or Movie" textures + +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/transforms/texture_displace/texture_displace_sverchok_blender_example_8.png + +One matrix per point can be passed if the matrix list is wrapped, note that the "Flat Output" checkbox of the matrix in is un-checked + +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/transforms/texture_displace/texture_evaluate_sverchok_blender_example_9.png diff --git a/docs/nodes/transforms/transforms_index.rst b/docs/nodes/transforms/transforms_index.rst index 793790683de5280774076b07b81516c529b5ff4f..d05350dcbc62c91996772adf708dbdf42d443d78 100644 --- a/docs/nodes/transforms/transforms_index.rst +++ b/docs/nodes/transforms/transforms_index.rst @@ -17,5 +17,6 @@ Transforms symmetrize transform_select noise_displace + texture_displace randomize align_mesh_by_mesh diff --git a/docs/nodes/vector/texture_evaluate.rst b/docs/nodes/vector/texture_evaluate.rst new file mode 100644 index 0000000000000000000000000000000000000000..cf1fa648a1ef78a8375f08ec1f1174caeb319af6 --- /dev/null +++ b/docs/nodes/vector/texture_evaluate.rst @@ -0,0 +1,43 @@ +Texture Evaluate +================ + +This node evaluates a texture at specific coordinates + +Inputs & Parameters +------------------- + ++----------------+-------------------------------------------------------------------------+ +| Parameters | Description | ++================+=========================================================================+ +| Channel | Color channel to output | +| | Offers: Red, Green, Blue, Hue, Saturation, Value, Alpha, RGB Average, | +| | Luminosity and Color | ++----------------+-------------------------------------------------------------------------+ +| Use Alpha | Toggle to add alpha channel (Only when Channel is set to Color) | ++----------------+-------------------------------------------------------------------------+ +| Vertices | Vertices of the mesh to displace | ++----------------+-------------------------------------------------------------------------+ +| Texture | Texture(s) to use as base | ++----------------+-------------------------------------------------------------------------+ + + +Examples +-------- + + + +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/vector/texture_evaluate/texture_evaluate_sverchok_blender_example_1.png + +Mixing 3 textures + +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/vector/texture_evaluate/texture_evaluate_sverchok_blender_example_2.png + +Masking mesh with texture + +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/vector/texture_evaluate/texture_evaluate_sverchok_blender_example_3.png + +Blender procedural textures work in 3D space + +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/vector/texture_evaluate/texture_evaluate_sverchok_blender_example_4.png + +You can input multiple textures, when joined [[texture,texture,..],[..],..] they will be used in a single set of vertices with a single output list diff --git a/docs/nodes/vector/vector_index.rst b/docs/nodes/vector/vector_index.rst index 1130c531bef6d46672cec393852310a0d536daf4..07e1950f2ba1f4113a7076ade772fea928101cd1 100644 --- a/docs/nodes/vector/vector_index.rst +++ b/docs/nodes/vector/vector_index.rst @@ -27,3 +27,4 @@ Vector turbulence linear_approx color_input + texture_evaluate diff --git a/index.md b/index.md index d4f2318c20a4030221807d2c3321bec772d1a61b..0d211ba4bd820367115c8e8beea7913cdd4be3a4 100644 --- a/index.md +++ b/index.md @@ -99,6 +99,7 @@ SvSimpleDeformNode SvBendAlongPathNode SvBendAlongSurfaceNode + SvDisplaceNode SvNoiseDisplaceNode SvRandomizeVerticesNode @@ -377,6 +378,7 @@ SvColorsInNodeMK1 SvColorInputNode SvColorsOutNodeMK1 + SvTextureEvaluateNode --- SvSculptMaskNode SvSelectMeshVerts diff --git a/nodes/transforms/texture_displace.py b/nodes/transforms/texture_displace.py new file mode 100644 index 0000000000000000000000000000000000000000..c72a035cd6d70e4aeb2444efae27c4094a72aa2f --- /dev/null +++ b/nodes/transforms/texture_displace.py @@ -0,0 +1,387 @@ +# ##### 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 ##### +from colorsys import rgb_to_hls +from itertools import repeat +import bpy +from bpy.props import EnumProperty, FloatProperty, FloatVectorProperty, StringProperty +from mathutils import Vector, Matrix, Color + +from sverchok.node_tree import SverchCustomTreeNode, throttled +from sverchok.core.socket_data import SvGetSocketInfo +from sverchok.data_structure import updateNode, list_match_func, numpy_list_match_modes, iter_list_match_func +from sverchok.utils.sv_itertools import recurse_f_level_control +from sverchok.utils.sv_bmesh_utils import bmesh_from_pydata +from sverchok.utils.modules.color_utils import color_channels + +class EmptyTexture(): + def evaluate(self, vec): + return [1, 1, 1, 1] + +def end_vector(vertex, eval_v, mid_level, strength, scale_out): + vx = vertex[0] + (eval_v[0] - mid_level) * strength * scale_out[0] + vy = vertex[1] + (eval_v[1] - mid_level) * strength * scale_out[1] + vz = vertex[2] + (eval_v[2] - mid_level) * strength * scale_out[2] + return [vx, vy, vz] + +def texture_displace_rgb(params, mapper_func): + vertex, texture, scale_out, matrix, mid_level, strength = params + v_vertex = Vector(vertex) + eval_v = texture.evaluate(mapper_func(v_vertex, matrix))[:] + + return end_vector(vertex, eval_v, mid_level, strength, scale_out) + + +def texture_displace_hsv(params, mapper_func): + vertex, texture, scale_out, matrix, mid_level, strength = params + v_vertex = Vector(vertex) + eval_v = Color(texture.evaluate(mapper_func(v_vertex, matrix))[:3]).hsv + return end_vector(vertex, eval_v, mid_level, strength, scale_out) + +def texture_displace_hls(params, mapper_func): + vertex, texture, scale_out, matrix, mid_level, strength = params + v_vertex = Vector(vertex) + eval_v = rgb_to_hls(*texture.evaluate(mapper_func(v_vertex, matrix))[:3]) + return end_vector(vertex, eval_v, mid_level, strength, scale_out) + + + +def texture_displace_vector_channel(params, mapper_func, extract_func): + vertex, texture, scale_out, matrix, mid_level, strength, normal = params + v_vertex = Vector(vertex) + col = texture.evaluate(mapper_func(v_vertex, matrix)) + eval_s = (extract_func(col) - mid_level) * strength + + vx = vertex[0] + normal[0] * eval_s * scale_out[0] + vy = vertex[1] + normal[1] * eval_s * scale_out[1] + vz = vertex[2] + normal[2] * eval_s * scale_out[2] + return [vx, vy, vz] + + +def apply_texture_displace_rgb(verts, pols, m_prop, channel, mapper_func, result): + func = texture_displace_rgb + result.append([func(v_prop, mapper_func) for v_prop in zip(verts, *m_prop)]) + + +def apply_texture_displace_hsv(verts, pols, m_prop, channel, mapper_func, result): + func = texture_displace_hsv + result.append([func(v_prop, mapper_func) for v_prop in zip(verts, *m_prop)]) + +def apply_texture_displace_hls(verts, pols, m_prop, channel, mapper_func, result): + func = texture_displace_hls + result.append([func(v_prop, mapper_func) for v_prop in zip(verts, *m_prop)]) + + +def apply_texture_displace_axis_x(verts, pols, m_prop, channel, mapper_func, result): + apply_texture_displace_axis(verts, pols, m_prop, channel, mapper_func, result, [1, 0, 0]) + + +def apply_texture_displace_axis_y(verts, pols, m_prop, channel, mapper_func, result): + apply_texture_displace_axis(verts, pols, m_prop, channel, mapper_func, result, [0, 1, 0]) + + +def apply_texture_displace_axis_z(verts, pols, m_prop, channel, mapper_func, result): + apply_texture_displace_axis(verts, pols, m_prop, channel, mapper_func, result, [0, 0, 1]) + +def apply_texture_displace_axis_custom(verts, pols, m_prop, channel, mapper_func, result): + func = texture_displace_vector_channel + extract_func = color_channels[channel][1] + result.append([func(v_prop, mapper_func, extract_func) for v_prop in zip(verts, *m_prop[:-1], m_prop[-1])]) + +def apply_texture_displace_axis(verts, pols, m_prop, channel, mapper_func, result, axis): + func = texture_displace_vector_channel + extract_func = color_channels[channel][1] + result.append([func(v_prop, mapper_func, extract_func) for v_prop in zip(verts, *m_prop, repeat(axis))]) + + + +def apply_texture_displace_normal(verts, pols, m_prop, channel, mapper_func, result): + bm = bmesh_from_pydata(verts, [], pols, normal_update=True) + normals = [v.normal for v in bm.verts] + func = texture_displace_vector_channel + extract_func = color_channels[channel][1] + result.append([func(v_prop, mapper_func, extract_func) for v_prop in zip(verts, *m_prop, normals)]) + bm.free() + +def meshes_texture_diplace(params, constant, matching_f): + ''' + This function prepares the data to pass to the different displace functions. + + params are verts, pols, texture, scale_out, matrix, mid_level, strength, axis + - verts, scale_out, and axis should be list as [[[float, float, float],],] (Level 3) + - pols should be list as [[[int, int, int, ...],],] (Level 3) + - texture can be [texture, texture] or [[texture, texture],[texture]] for per vertex texture + - matrix can be [matrix, matrix] or [[matrix, matrix],[texture]] for per vertex matrix, + in case of UV Coors in mapping_mode it should be [[[float, float, float],],] (Level 3) + mid_level and strength should be list as [[float, float, ..], [float, ..], ..] (Level 2) + desired_levels = [3, 3, 2, 3, 2 or 3, 2, 2, 3] + constant are the function options (data that does not need to be matched) + matching_f stands for list matching formula to use + ''' + result = [] + displace_mode, displace_function, color_channel, match_mode, mapping_mode = constant + params = matching_f(params) + local_match = iter_list_match_func[match_mode] + mapper_func = mapper_funcs[mapping_mode] + for props in zip(*params): + verts, pols, texture, scale_out, matrix, mid_level, strength, axis = props + if mapping_mode == 'Texture Matrix': + if type(matrix) == list: + matrix = [m.inverted() for m in matrix] + else: + matrix = [matrix.inverted()] + elif mapping_mode == 'Mesh Matrix': + if not type(matrix) == list: + matrix = [matrix] + + if not type(texture) == list: + texture = [texture] + if displace_mode == 'Custom Axis': + axis_n = [Vector(v).normalized() for v in axis] + m_prop = local_match([texture, scale_out, matrix, mid_level, strength, axis_n]) + else: + m_prop = local_match([texture, scale_out, matrix, mid_level, strength]) + displace_function(verts, pols, m_prop, color_channel, mapper_func, result) + + return result + + +color_channels_modes = [(t, t, t, '', color_channels[t][0]) for t in color_channels] + +displace_funcs = { + 'NORMAL': apply_texture_displace_normal, + 'X': apply_texture_displace_axis_x, + 'Y': apply_texture_displace_axis_y, + 'Z': apply_texture_displace_axis_z, + 'Custom Axis': apply_texture_displace_axis_custom, + 'RGB to XYZ': apply_texture_displace_rgb, + 'HSV to XYZ': apply_texture_displace_hsv, + 'HLS to XYZ': apply_texture_displace_hls + } + +mapper_funcs = { + 'UV': lambda v, v_uv: Vector((v_uv[0]*2-1, v_uv[1]*2-1, v_uv[2])), + 'Mesh Matrix': lambda v, m: m @ v, + 'Texture Matrix': lambda v, m: m @ v +} + +class SvDisplaceNode(bpy.types.Node, SverchCustomTreeNode): + """ + Triggers: Add texture to verts + Tooltip: Affect input verts/mesh with a scene texture. Mimics Blender Displace modifier + + """ + + bl_idname = 'SvDisplaceNode' + bl_label = 'Texture Displace' + bl_icon = 'MOD_DISPLACE' + + out_modes = [ + ('NORMAL', 'Normal', 'Texture displacement along Vertex Normal', '', 1), + ('X', 'X', 'Texture displacement along X axis', '', 2), + ('Y', 'Y', 'Texture displacement along Y axis', '', 3), + ('Z', 'Z', 'Texture displacement along Z axis', '', 4), + ('Custom Axis', 'Custom Axis', 'Texture displacement along Custom Axis', '', 5), + ('RGB to XYZ', 'RGB to XYZ', 'Texture displacement with RGB as vector', '', 6), + ('HSV to XYZ', 'HSV to XYZ', 'Texture displacement with HSV as vector', '', 7), + ('HLS to XYZ', 'HLS to XYZ', 'Texture displacement with HSV as vector', '', 8)] + + texture_coord_modes = [ + ('UV', 'UV', 'Input UV coordinates to evaluate texture', '', 1), + ('Mesh Matrix', 'Mesh Matrix', 'Matrix to apply to verts before evaluating texture', '', 2), + ('Texture Matrix', 'Texture Matrix', 'Matrix of texture (External Object matrix)', '', 3), + + ] + @throttled + def change_mode(self, context): + inputs = self.inputs + if self.tex_coord_type == 'Texture Matrix': + if 'Texture Matrix' not in inputs: + if 'UV coords' in inputs: + inputs[4].hide_safe = False + inputs[4].replace_socket('SvMatrixSocket', 'Texture Matrix') + + elif self.tex_coord_type == 'Mesh Matrix': + if 'Mesh Matrix' not in inputs: + if 'UV coords' in inputs: + inputs[4].hide_safe = False + inputs[4].replace_socket('SvMatrixSocket', 'Mesh Matrix') + + elif self.tex_coord_type == 'UV': + if 'UV Coordinates' not in inputs: + inputs[4].hide_safe = False + inputs[4].replace_socket('SvVerticesSocket', 'UV Coordinates') + + @throttled + def change_direction_sockets(self, context): + inputs = self.inputs + if self.out_mode == 'Custom Axis': + if inputs['Custom Axis'].hide_safe: + inputs['Custom Axis'].hide_safe = False + else: + inputs['Custom Axis'].hide_safe = True + + + name_texture: StringProperty( + name='image_name', + description='image name', + default='', + update=updateNode) + + out_mode: EnumProperty( + name='Direction', + items=out_modes, + default='NORMAL', + description='Apply Mode', + update=change_direction_sockets) + + color_channel: EnumProperty( + name='Component', + items=color_channels_modes[:9], + default='Alpha', + description="Channel to use from texture", + update=updateNode) + + tex_coord_type: EnumProperty( + name='Texture Coord', + items=texture_coord_modes, + default='Texture Matrix', + description="Mapping method", + update=change_mode) + + scale_out_v: FloatVectorProperty( + name='Axis Scale Out', description='Scale of the added vector', + size=3, default=(1, 1, 1), + update=updateNode) + + custom_axis: FloatVectorProperty( + name='Custom Axis', description='Scale of the added vector', + size=3, default=(1, 1, 1), + update=updateNode) + + strength: FloatProperty( + name='Strength', description='Size of line', + default=1.0, update=updateNode) + + mid_level: FloatProperty( + name='Middle Level', description='Size of line', + default=0.0, update=updateNode) + + list_match: EnumProperty( + name="List Match", + description="Behavior on different list lengths", + items=numpy_list_match_modes, default="REPEAT", + update=updateNode) + + def sv_init(self, context): + self.width = 200 + self.inputs.new('SvVerticesSocket', 'Vertices') + self.inputs.new('SvStringsSocket', 'Polygons') + self.inputs.new('SvStringsSocket', 'Texture').custom_draw = 'draw_texture_socket' + self.inputs.new('SvVerticesSocket', 'Axis Scale Out').prop_name = 'scale_out_v' + self.inputs.new('SvMatrixSocket', 'Texture Matrix') + self.inputs.new('SvStringsSocket', 'Middle Level').prop_name = 'mid_level' + self.inputs.new('SvStringsSocket', 'Strength').prop_name = 'strength' + self.inputs.new('SvVerticesSocket', 'Custom Axis').prop_name = 'custom_axis' + self.inputs['Custom Axis'].hide_safe = True + + self.outputs.new('SvVerticesSocket', 'Vertices') + + + def draw_texture_socket(self, socket, context, layout): + if not socket.is_linked: + c = layout.split(factor=0.3, align=False) + + c.label(text=socket.name+ ':') + c.prop_search(self, "name_texture", bpy.data, 'textures', text="") + else: + layout.label(text=socket.name+ '. ' + SvGetSocketInfo(socket)) + def draw_buttons(self, context, layout): + is_vector = self.out_mode in ['RGB to XYZ', 'HSV to XYZ', 'HLS to XYZ'] + + c = layout.split(factor=0.5, align=False) + r = c.column(align=False) + r.label(text='Direction'+ ':') + r.prop(self, 'out_mode', expand=False, text='') + + r = c.column(align=False) + r.label(text='Texture Coord.'+ ':') + r.prop(self, 'tex_coord_type', expand=False, text='') + if not is_vector: + r = layout.split(factor=0.3, align=False) + r.label(text='Channel'+ ':') + r.prop(self, 'color_channel', expand=False, text='') + # layout.prop(self, 'tex_coord_type', text="Tex. Coord") + + def draw_buttons_ext(self, context, layout): + '''draw buttons on the N-panel''' + self.draw_buttons(context, layout) + layout.prop(self, 'list_match', expand=False) + + def rclick_menu(self, context, layout): + layout.prop_menu_enum(self, "list_match", text="List Match") + + def process(self): + inputs, outputs = self.inputs, self.outputs + + if not outputs[0].is_linked: + return + + result = [] + + params = [si.sv_get(default=[[]], deepcopy=False) for si in inputs[:4]] + if not inputs[2].is_linked: + if not self.name_texture: + params[2] = [[EmptyTexture()]] + # return + else: + params[2] = [[bpy.data.textures[self.name_texture]]] + if not self.tex_coord_type == 'UV': + params.append(inputs[4].sv_get(default=[Matrix()], deepcopy=False)) + mat_level = 2 + else: + if inputs[4].is_linked: + params.append(inputs[4].sv_get(default=[[]], deepcopy=False)) + else: + params.append(params[0]) + mat_level = 3 + params.append(inputs[5].sv_get(default=[[]], deepcopy=False)) + params.append(inputs[6].sv_get(default=[[]], deepcopy=False)) + params.append(inputs[7].sv_get(default=[[]], deepcopy=False)) + + matching_f = list_match_func[self.list_match] + desired_levels = [3, 3, 2, 3, mat_level, 2, 2, 3] + ops = [self.out_mode, displace_funcs[self.out_mode], self.color_channel, self.list_match, self.tex_coord_type] + + result = recurse_f_level_control(params, ops, meshes_texture_diplace, matching_f, desired_levels) + + self.outputs[0].sv_set(result) + + + + def draw_label(self): + if self.hide: + if not self.inputs['Texture'].is_linked: + texture = ' ' + self.name_texture + else: + texture = ' + texture(s)' + return 'Displace' + texture +' ' + self.color_channel.title() + ' channel' + else: + return self.label or self.name + +classes = [SvDisplaceNode] +register, unregister = bpy.utils.register_classes_factory(classes) diff --git a/nodes/vector/texture_evaluate.py b/nodes/vector/texture_evaluate.py new file mode 100644 index 0000000000000000000000000000000000000000..5d55f2ba9cc08d5ad33c8e5806a6317e48b6093b --- /dev/null +++ b/nodes/vector/texture_evaluate.py @@ -0,0 +1,198 @@ +# ##### 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 ##### +from colorsys import rgb_to_hls +from itertools import repeat +import bpy +from bpy.props import EnumProperty, FloatProperty, FloatVectorProperty, StringProperty, BoolProperty +from mathutils import Vector, Matrix, Color + +from sverchok.node_tree import SverchCustomTreeNode, throttled +from sverchok.core.socket_data import SvGetSocketInfo +from sverchok.data_structure import updateNode, list_match_func, numpy_list_match_modes, iter_list_match_func +from sverchok.utils.sv_itertools import recurse_f_level_control +from sverchok.utils.modules.color_utils import color_channels + +class EmptyTexture(): + def evaluate(self, vec): + return [1, 1, 1, 1] + +def texture_evaluate(params, extract_func): + vertex, texture = params + v_vertex = Vector(vertex) + col = texture.evaluate(v_vertex) + eval_s = extract_func(col) + return eval_s + + +def meshes_texture_evaluate(params, constant, matching_f): + ''' + This function prepares the data to pass to the evaluate function. + + params are verts, texture, + - verts should be list as [[[float, float, float],],] (Level 3) + - texture can be [texture, texture] or [[texture, texture],[texture]] for per vertex texture + + desired_levels = [3, 2 or 3] + constant are the function options (data that does not need to be matched) + matching_f stands for list matching formula to use + ''' + result = [] + color_channel, match_mode = constant + params = matching_f(params) + local_match = iter_list_match_func[match_mode] + extract_func = color_channels[color_channel][1] + for props in zip(*params): + verts, texture = props + if not type(texture) == list: + texture = [texture] + m_texture = local_match([texture])[0] + result.append([texture_evaluate(v_prop, extract_func) for v_prop in zip(verts, m_texture)]) + + return result + +color_channels_modes = [(t, t, t, '', color_channels[t][0]) for t in color_channels if not t == 'RGBA'] + + +class SvTextureEvaluateNode(bpy.types.Node, SverchCustomTreeNode): + """ + Triggers: Scence Texture In + Tooltip: Evaluate Scene texture at input coordinates + + """ + + bl_idname = 'SvTextureEvaluateNode' + bl_label = 'Texture Evaluate' + bl_icon = 'FORCE_TEXTURE' + + + out_modes = [ + ('NORMAL', 'Single Channel', 'Texture displacement along Vertex Normal', '', 1), + ('RGB to XYZ', 'RGB', 'Texture displacement with RGB as vector', '', 2), + ('HSV to XYZ', 'HSV', 'Texture displacement with HSV as vector', '', 3), + ('HLS to XYZ', 'HLS', 'Texture displacement with HSV as vector', '', 4)] + + texture_coord_modes = [ + ('UV', 'UV coords', 'Input UV coordinates to evaluate texture', '', 1), + ('Mesh Matrix', 'Mesh Matrix', 'Matrix to apply to verts before evaluating texture', '', 2), + ('Texture Matrix', 'Texture Matrix', 'Matrix of texture (External Object matrix)', '', 3), + + ] + + @throttled + def change_mode(self, context): + outputs = self.outputs + if self.color_channel not in ['Color', 'RGBA']: + outputs[0].replace_socket('SvStringsSocket', 'Value') + else: + outputs[0].replace_socket('SvColorSocket', 'Color') + + name_texture: StringProperty( + name='image_name', + description='image name', + default='', + update=updateNode) + + color_channel: EnumProperty( + name='Component', + items=color_channels_modes, + default='Alpha', + description="Channel to use from texture", + update=change_mode) + + use_alpha: BoolProperty(default=False, update=updateNode) + + list_match: EnumProperty( + name="List Match", + description="Behavior on different list lengths", + items=numpy_list_match_modes, default="REPEAT", + update=updateNode) + + def sv_init(self, context): + self.width = 200 + self.inputs.new('SvVerticesSocket', 'Vertices') + self.inputs.new('SvStringsSocket', 'Texture').custom_draw = 'draw_texture_socket' + + self.outputs.new('SvStringsSocket', 'Value') + + + def draw_texture_socket(self, socket, context, layout): + if not socket.is_linked: + c = layout.split(factor=0.3, align=False) + c.label(text=socket.name+ ':') + + c.prop_search(self, "name_texture", bpy.data, 'textures', text="") + else: + layout.label(text=socket.name+ '. ' + SvGetSocketInfo(socket)) + def draw_buttons(self, context, layout): + c = layout.split(factor=0.3, align=True) + c.label(text='Channel:') + c.prop(self, 'color_channel', text="") + if self.color_channel == 'Color': + layout.prop(self, 'use_alpha', text="Use Alpha") + + def draw_buttons_ext(self, context, layout): + '''draw buttons on the N-panel''' + self.draw_buttons(context, layout) + layout.prop(self, 'list_match', expand=False) + + def rclick_menu(self, context, layout): + layout.prop_menu_enum(self, "list_match", text="List Match") + + def process(self): + inputs, outputs = self.inputs, self.outputs + + if not outputs[0].is_linked: + return + + result = [] + + params = [] + params.append(inputs[0].sv_get(default=[[]], deepcopy=False)) + if not inputs[1].is_linked: + if not self.name_texture: + params.append([[EmptyTexture()]]) + else: + params.append([[bpy.data.textures[self.name_texture]]]) + else: + params.append(inputs[1].sv_get(default=[[]], deepcopy=False)) + + + matching_f = list_match_func[self.list_match] + desired_levels = [3, 2] + if self.color_channel == 'Color' and self.use_alpha: + ops = ['RGBA', self.list_match] + else: + ops = [self.color_channel, self.list_match] + result = recurse_f_level_control(params, ops, meshes_texture_evaluate, matching_f, desired_levels) + + self.outputs[0].sv_set(result) + + + + def draw_label(self): + if self.hide: + if not self.inputs['Texture'].is_linked: + texture = ' ' + self.name_texture + else: + texture = ' + texture(s)' + return 'Displace' + texture +' ' + self.color_channel.title() + ' channel' + else: + return self.label or self.name + +classes = [SvTextureEvaluateNode] +register, unregister = bpy.utils.register_classes_factory(classes) diff --git a/utils/modules/color_utils.py b/utils/modules/color_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..13f5e928eacaf32b1426dacbbdb013290773a9d2 --- /dev/null +++ b/utils/modules/color_utils.py @@ -0,0 +1,24 @@ +# This file is part of project Sverchok. It's copyrighted by the contributors +# recorded in the version control history of the file, available from +# its original location https://github.com/nortikin/sverchok/commit/master +# +# SPDX-License-Identifier: GPL3 +# License-Filename: LICENSE +# pylint: disable=C0326 + +from mathutils import Color + + +color_channels = { + 'Red': (1, lambda x: x[0]), + 'Green': (2, lambda x: x[1]), + 'Blue': (3, lambda x: x[2]), + 'Hue': (4, lambda x: Color(x[:3]).h), + 'Saturation': (5, lambda x: Color(x[:3]).s), + 'Value': (6, lambda x: Color(x[:3]).v), + 'Alpha': (7, lambda x: x[3]), + 'RGB Average':(8, lambda x: sum(x[:3])/3), + 'Luminosity': (9, lambda x: 0.21*x[0] + 0.72*x[1] + 0.07*x[2]), + 'Color': (10, lambda x: x[:3]), + 'RGBA': (11, lambda x: x[:]), + }