Created
August 31, 2022 15:16
-
-
Save Eterea/9dfe68418e3a469531291d2e0fdb045f to your computer and use it in GitHub Desktop.
This file contains hidden or 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
#python | |
# ------------------------------------------------------------------------------------------------ | |
# NAME: etr_python_api_power_utils.py | |
# VERS: 1.5 | |
# DATE: August 24, 2020 | |
# | |
# MADE: Cristobal Vila, etereaestudios.com | |
# With snippets kindly shared by Shawn Frueh, shawnfrueh.com/me/ and also 'robberyman'# | |
# ------------------------------------------------------------------------------------------------ | |
import lx | |
import lxu | |
import math | |
import modo | |
""" | |
------------------------------------------------------------------------------------------------ | |
SUPER VERTS POSITION CALCULATOR | |
------------------------------------------------------------------------------------------------ | |
Next pure Python API snippet kindly shared by Shawn Frueh: | |
https://foundry-modo.slack.com/archives/C6F918JEB/p1596930293212700 | |
THIS IS WHAT REALLY MAKES THESE TOOLS SO POWERFULL. A BIG THANKS TO SHAWN FOR THIS! | |
------------------------------------------------------------------------------------------------ | |
Here is a pure API method that uses the selection service to get all selected points on any object in Modo. | |
This will include points that are selected even in ghost mode of the procedural stack. | |
When getting the selection mesh, it should be the "deformed" version that you are seeing in the view-port, | |
thus working with procedurals. If you want to also handle global positions you will have to multiply the point | |
by it's items world matix. I hope this helps! Tried to break it down as simple as possible. | |
I does look like a lot of code but the joy is once you wrap this bad boy up into a package/library | |
that you just import you never have to worry about it again. That's essentially where this came from. | |
I have a method that's a bit more advanced than this that also handles the other elements. | |
------------------------------------------------------------------------------------------------ | |
TIP BY JAMES O'HARE AKA FARFARER ///////////////////////// | |
If you get a selection of verts indirectly (ie: selecting edges and converting to verts) | |
you could arrive to a situation with 'extra verts' selected, this is: discountinuous (disco) verts | |
Vertex selections are technically made up of a vertex/polygon pair | |
in that is the vertex ID and the polygon ID that it belongs to | |
(in the case where the polygon ID is 0/NULL, it's implied that the vertex ID selected | |
for all of its polygons - a continuous selection) | |
SUMMARIZING: | |
If you want the raw vertex selection count, regardless of discontinuity, | |
you'll want to ITERATE OVER THE SELECTIONS AND COUNT THE NUMBER OF UNIQUE VERTEX-IDs LISTED | |
In this function we really don't want the 'raw vert count', but applying this scheme we avoid the | |
extra calculation of positions for those extra disco verts. | |
""" | |
def fn_get_sel_verts_pos(world=False): | |
# Args: world (bool): If true, return the world positions, else return local | |
# Returns: list(Vector3): List with XYZ positions for each selected vert | |
# Initialize the selection service | |
SELECTION_SERVICE = lxu.service.Selection() | |
# Get the vertex selection int type: 1447383640 | |
vertex_selection_type = SELECTION_SERVICE.LookupType(lx.symbol.sSELTYP_VERTEX) | |
# Get the selection object: lxu.object.SelectionType | |
vertex_selection_object = SELECTION_SERVICE.Allocate(lx.symbol.sSELTYP_VERTEX) | |
# Convert that type into a packet so that we can access the selection data | |
vertex_packet = lx.object.VertexPacketTranslation(vertex_selection_object) | |
# Get the total count of selected points | |
selected_vertex_count = SELECTION_SERVICE.Count(vertex_selection_type) | |
vertex_id_list = [] # READ TIP BY JAMES O'HARE AKA FARFARER ///////////////////////// | |
position_list = [] | |
# Iterate over each point and get some data. These points could be coming from multiple meshes | |
# and so you will need to check the item they be | |
for vertex in range(selected_vertex_count): | |
# Get a pointer to the vertex data in the given index. | |
vertex_pointer = SELECTION_SERVICE.ByIndex(vertex_selection_type, vertex) | |
# If we don't get a pointer, skip to the next index in the loop. | |
# This is necessary to prevent any crashes. | |
if not vertex_pointer: | |
continue | |
# Get the id data from the pointer: ex. 495128304 | |
vertex_id = vertex_packet.Vertex(vertex_pointer) | |
if vertex_id not in vertex_id_list: # ///////////////////////// | |
vertex_id_list.append(vertex_id) # ///////////////////////// | |
# Get the item the point belongs to: lxu.object.Item | |
selection_item = vertex_packet.Item(vertex_pointer) | |
# Get the matrix channel index | |
matrix_index = selection_item.ChannelLookup("worldMatrix") | |
# Prep the selection item to be read at the current time. You want those animated points! | |
selection_item.ReadEvaluated(SELECTION_SERVICE.GetTime()) | |
# Get the matrix from the channel as a COM object and convert it to a matrix object. | |
item_world_matrix = lx.object.Matrix(selection_item.ChannelValue(matrix_index)) | |
# Get the mesh item the point belongs to: lxu.object.Mesh | |
selection_mesh = vertex_packet.Mesh(vertex_pointer) | |
# Get the point accessor of the mesh item: lxu.object.Point | |
point_item = selection_mesh.PointAccessor() | |
# Select the vertex in the accessor so that we can query any Point data. | |
point_item.Select(vertex_id) | |
# Get the position of the internally selected point. This point is relative to the mesh | |
# So if the mesh is not zero you will need to multiply this position by the transform | |
# data of the item it belongs to. | |
localPosition = point_item.Pos() | |
# Get the world position of the point by multiplying it by the world matrix of it's item. | |
worldPosition = item_world_matrix.MultiplyVector(point_item.Pos()) | |
# Deliver world or local depending on world argument ('true', 'false') | |
if world: | |
position_list.append(worldPosition) | |
else: | |
position_list.append(localPosition) | |
return position_list | |
# ------------------------------------------------------------------------------------------------ | |
# SUPER VERTS COUNT CALCULATOR | |
# To avoid problem with 'disco verts' Read TIP BY JAMES O'HARE AKA FARFARER | |
# ------------------------------------------------------------------------------------------------ | |
def fn_get_sel_verts_count(): | |
# Initialize the selection service | |
SELECTION_SERVICE = lxu.service.Selection() | |
# Get the vertex selection int type: 1447383640 | |
vertex_selection_type = SELECTION_SERVICE.LookupType(lx.symbol.sSELTYP_VERTEX) | |
# Get the selection object: lxu.object.SelectionType | |
vertex_selection_object = SELECTION_SERVICE.Allocate(lx.symbol.sSELTYP_VERTEX) | |
# Convert that type into a packet so that we can access the selection data | |
vertex_packet = lx.object.VertexPacketTranslation(vertex_selection_object) | |
# Get the total count of selected points | |
selected_vertex_count = SELECTION_SERVICE.Count(vertex_selection_type) | |
vertex_id_list = [] # READ TIP BY JAMES O'HARE AKA FARFARER ///////////////////////// | |
# Iterate over each point and get some data. These points could be coming from multiple meshes | |
# and so you will need to check the item they be | |
for vertex in range(selected_vertex_count): | |
# Get a pointer to the vertex data in the given index. | |
vertex_pointer = SELECTION_SERVICE.ByIndex(vertex_selection_type, vertex) | |
# If we don't get a pointer, skip to the next index in the loop. | |
# This is necessary to prevent any crashes. | |
if not vertex_pointer: | |
continue | |
# Get the id data from the pointer: ex. 495128304 | |
vertex_id = vertex_packet.Vertex(vertex_pointer) | |
if vertex_id not in vertex_id_list: # ///////////////////////// | |
vertex_id_list.append(vertex_id) # ///////////////////////// | |
return len(vertex_id_list) | |
""" | |
------------------------------------------------------------------------------------------------ | |
SUPER EDGES LENGTH CALCULATOR | |
------------------------------------------------------------------------------------------------ | |
Next pure Python API snippet is a mix between the one shared by Shawn Frueh: | |
https://foundry-modo.slack.com/archives/C6F918JEB/p1596930293212700 | |
And also this one shared by 'robberyman': | |
https://foundry-modo.slack.com/archives/C6F918JEB/p1597076342264900 | |
With this we can calculate the total accumulated length for all selected edges, no matter | |
these ones belong to different item meshes, procedural, deformed, animated... ALL! | |
""" | |
def fn_get_sel_edges_length(): | |
total_distance = 0 | |
# Initialize the selection service | |
SELECTION_SERVICE = lxu.service.Selection() | |
# Get the edge selection int type | |
edge_selection_type = SELECTION_SERVICE.LookupType(lx.symbol.sSELTYP_EDGE) | |
# Get the selection object: lxu.object.SelectionType | |
edge_selection_object = SELECTION_SERVICE.Allocate(lx.symbol.sSELTYP_EDGE) | |
# Convert that type into a packet so that we can access the selection data | |
edge_translation_packet = lx.object.EdgePacketTranslation(edge_selection_object) | |
# Get the total count of selected points | |
selected_edge_count = SELECTION_SERVICE.Count(edge_selection_type) | |
my_edges_distances_list = [] | |
# Iterate over each edge and get some data. These edges could be coming from multiple meshes | |
# and so you will need to check the item they be | |
for edge_index in range(selected_edge_count): | |
# Get a pointer to the vertex data in the given index. | |
edge_pointer = SELECTION_SERVICE.ByIndex(edge_selection_type, edge_index) | |
# If we don't get a pointer, skip to the next index in the loop. | |
# This is necessary to prevent any crashes. | |
if not edge_pointer: | |
continue | |
# Get the item the edge belongs to: lxu.object.Item | |
selection_item = edge_translation_packet.Item(edge_pointer) | |
item = lx.object.Item(selection_item) | |
# Get the matrix channel index | |
matrix_index = selection_item.ChannelLookup("worldMatrix") | |
# Prep the selection item to be read at the current time. You want those animated points! | |
selection_item.ReadEvaluated(SELECTION_SERVICE.GetTime()) | |
# Get the matrix from the channel as a COM object and convert it to a matrix object. | |
item_world_matrix = lx.object.Matrix(selection_item.ChannelValue(matrix_index)) | |
# Get the mesh item the edge belongs to: lxu.object.Mesh | |
selection_mesh = edge_translation_packet.Mesh(edge_pointer) | |
mesh = lx.object.Mesh(selection_mesh) | |
# IDs for the edge endpoints, | |
aPoint, bPoint = edge_translation_packet.Vertices(edge_pointer) | |
point = mesh.PointAccessor() | |
# Select and get the position of boths points. This will be relative to the mesh | |
# So if the mesh is not zero you will need to multiply this position by the transform data | |
# of the item it belongs to. We do this by multiplying it by the world matrix of it's item. | |
point.Select(aPoint) | |
posA = item_world_matrix.MultiplyVector(point.Pos()) | |
point.Select(bPoint) | |
posB = item_world_matrix.MultiplyVector(point.Pos()) | |
# Now lets calculate the individual X, Y, Z distances between both points | |
ABX = posA[0] - posB[0] | |
ABY = posA[1] - posB[1] | |
ABZ = posA[2] - posB[2] | |
AB_distance = math.sqrt(ABX**2 + ABY**2 + ABZ**2) # Pythagoras Theorem | |
my_edges_distances_list.append(AB_distance) # Append distance(s) to list | |
return my_edges_distances_list | |
# ------------------------------------------------------------------------------------------------ | |
# SUPER EDGES COUNT CALCULATOR | |
# ------------------------------------------------------------------------------------------------ | |
def fn_get_sel_edges_count(): | |
# Initialize the selection service | |
SELECTION_SERVICE = lxu.service.Selection() | |
# Get the edge selection int type | |
edge_selection_type = SELECTION_SERVICE.LookupType(lx.symbol.sSELTYP_EDGE) | |
# Get the total count of selected points | |
selected_edge_count = SELECTION_SERVICE.Count(edge_selection_type) | |
return selected_edge_count | |
# ------------------------------------------------------------------------------------------------ | |
# SUPER POLYS COUNT CALCULATOR | |
# ------------------------------------------------------------------------------------------------ | |
def fn_get_sel_polys_count(): | |
# Initialize the selection service | |
SELECTION_SERVICE = lxu.service.Selection() | |
# Get the polygon selection int type: 1347374169 | |
polygon_selection_type = SELECTION_SERVICE.LookupType(lx.symbol.sSELTYP_POLYGON) | |
# Get the total count of selected polygons | |
selected_poly_count = SELECTION_SERVICE.Count(polygon_selection_type) | |
return selected_poly_count | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment