Skip to content

Instantly share code, notes, and snippets.

@GainDeveloper
Created October 6, 2020 13:18
Show Gist options
  • Save GainDeveloper/ce7580122d8ab263e4723ae0a16de3bf to your computer and use it in GitHub Desktop.
Save GainDeveloper/ce7580122d8ab263e4723ae0a16de3bf to your computer and use it in GitHub Desktop.
Freezes scale or rotations whilst maintaining vertex normals. Place in lxserv folder.
#/*
#================================================================================================
# Part of the ModoMLTools for The Foundry's Modo.
# Author: Myles Lambert : [email protected]
# Description: Freezes rotation whilst also updating vertex normals.
#================================================================================================
#*/
import lx, lxifc, lxu
import modo
class NormalUpdateVisitor(lxifc.Visitor):
def __init__(self, polyAccessor, mapID, rotationMatrix, scale, timesFlipped, freezeRotation, freezeScale):
self.polyAccessor = polyAccessor
self.mapID = mapID
self.storage = lx.object.storage('f',3)
self.pointStorage = lx.object.storage('p',3)
self.rotationMatrix = rotationMatrix
self.scale = scale
self.output = []
self.timesFlipped = timesFlipped
self.freezeScale = freezeScale
self.freezeRotation = freezeRotation
def vis_Evaluate(self):
nVerts = self.polyAccessor.VertexCount()
for eachVert in range(nVerts):
vertID = self.polyAccessor.VertexByIndex(eachVert)
if self.polyAccessor.MapEvaluate(self.mapID, vertID, self.storage):
currentValue = modo.Vector3(self.storage.get())
if self.freezeScale:
currentValue.values = [a*b for a,b in zip(currentValue.values, self.scale)]
if self.freezeRotation:
currentValue.mulByMatrixAsPoint(self.rotationMatrix)
currentValue.normalize()
if self.freezeScale:
if self.timesFlipped > 0:
currentValue *= -1
if self.timesFlipped == 2:
# Flip Binding Order
vlist = []
for vi in range(self.polyAccessor.VertexCount()):
pt = self.polyAccessor.VertexByIndex (vi)
vlist.append (pt)
self.pointStorage.setSize(len(vlist))
self.pointStorage.set(vlist)
self.polyAccessor.SetVertexList(self.pointStorage, len(vlist), 1)
self.storage.set((currentValue[0], currentValue[1], currentValue[2]))
self.polyAccessor.SetMapValue(vertID, self.mapID, self.storage)
class QueryMapsVisitor(lxifc.Visitor):
def __init__(self, meshmap, filter_types=None):
self.meshmap = meshmap
self.msv = lx.service.Mesh()
self.mapInfos = []
self.filter_types = filter_types
def vis_Evaluate(self):
name = self.meshmap.Name()
if name:
map_type = self.meshmap.Type()
if self.filter_types:
if map_type not in self.filter_types:
return
type_name = self.msv.VMapLookupName(map_type)
vmap = {"name": name, "type_name": type_name, "ID": self.meshmap.ID(), "map_type": map_type}
self.mapInfos.append(vmap)
class CmdMLFreezeRotationNormals(lxu.command.BasicCommand):
def __init__(self):
lxu.command.BasicCommand.__init__(self)
self.dyna_Add('rotationFreeze', lx.symbol.sTYPE_BOOLEAN)
self.dyna_Add('scaleFreeze', lx.symbol.sTYPE_BOOLEAN)
def cmd_Flags(self):
return lx.symbol.fCMD_MODEL | lx.symbol.fCMD_UNDO
def basic_Enable(self, msg):
return True
def cmd_UserName(self):
return "Freeze Transform Normals"
def cmd_Desc(self):
return "Freezes rotation or scale whilst maintaining normal orientation."
def basic_Execute(self, msg, flags):
layerService = lx.service.Layer()
layerScanObject = lx.object.LayerScan(layerService.ScanAllocate(lx.symbol.f_LAYERSCAN_EDIT))
freezeRotation = bool(self.dyna_Int(0, 1))
freezeScale = bool(self.dyna_Int(1, 1))
for n in xrange(layerScanObject.Count()):
localizedMesh = lx.object.Mesh(layerScanObject.MeshEdit(n))
localizedItem = modo.Item(layerScanObject.MeshItem(n))
matrixObject = localizedItem.channel( lx.symbol.sICHAN_XFRMCORE_WROTMATRIX ).get()
worldMatrix = modo.Matrix4(matrixObject)
try:
xfrmScale = lx.object.Locator(localizedItem).GetTransformItem(lx.symbol.iXFRM_SCALE)
channelRead = lx.object.ChannelRead(localizedItem.Context().Channels(lx.symbol.s_ACTIONLAYER_EDIT, 0))
scaleChannels = [xfrmScale.ChannelLookup('scl.X'), xfrmScale.ChannelLookup('scl.Y'), xfrmScale.ChannelLookup('scl.Z')]
scale = [channelRead.Double(xfrmScale, x) for x in scaleChannels]
except:
scale = [1,1,1]
timesFlipped = len([x for x in scale if x < 0])
# Create Accessors
polyAccessor = localizedMesh.PolygonAccessor()
mapAccessor = localizedMesh.MeshMapAccessor()
# Get Map ID #
visitor = QueryMapsVisitor(mapAccessor, [lx.symbol.i_VMAP_NORMAL])
mapAccessor.Enumerate(lx.symbol.iMARK_ANY, visitor, 0)
for map in visitor.mapInfos:
visitorInstance = NormalUpdateVisitor(polyAccessor, map['ID'], worldMatrix, scale, timesFlipped, freezeRotation, freezeScale)
polyAccessor.Enumerate(lx.symbol.iMARK_ANY, visitorInstance, 0)
if timesFlipped == 2:
layerScanObject.SetMeshChange(n, lx.symbol.f_MESHEDIT_MAP_OTHER | lx.symbol.f_MESHEDIT_GEOMETRY)
else:
layerScanObject.SetMeshChange(n, lx.symbol.f_MESHEDIT_MAP_OTHER)
layerScanObject.Apply()
if freezeRotation:
lx.eval('transform.freeze rotation')
if freezeScale:
lx.eval('!!transform.freeze scale')
lx.bless(CmdMLFreezeRotationNormals, "ML.FreezeRotationNormals")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment