Created
October 6, 2020 13:18
-
-
Save GainDeveloper/ce7580122d8ab263e4723ae0a16de3bf to your computer and use it in GitHub Desktop.
Freezes scale or rotations whilst maintaining vertex normals. Place in lxserv folder.
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
#/* | |
#================================================================================================ | |
# 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