PDA

View Full Version : Code Snippets (aka: "How to...")



Celshader
05-02-2012, 01:43 PM
If it helps anyone, feel free to use these functions in your Python scripts...



## the following returns a string containing
## the filepath to the content directory.

def getContentDir():
return lwsdk.LWDirInfoFunc('Content')




## Given a string ("p") containing a file path to an image, the following
## attempts to load that image into the Image Editor. It then
## returns the name of that image and the image ID.

def loadImage(p):
IM = lwsdk.LWImageList()
id = IM.load(p)
name = IM.name(id)
return name, id




## this returns the FPS of the scene.

def getFPS():
info = lwsdk.LWSceneInfo()
return info.framesPerSecond




## these return the Preview Start and Preview End frames.

def getPreviewStart():
info = lwsdk.LWInterfaceInfo()
return info.previewStart

def getPreviewEnd():
info = lwsdk.LWInterfaceInfo()
return info.previewEnd

Dexter2999
05-02-2012, 02:29 PM
Thanks, Jen!

Celshader
05-02-2012, 04:44 PM
Thanks, Jen!

:)

Here's another set of functions that go together:




## returns the item ID of a pyObj.

def getObjID(pyObj):
return lwsdk.itemid_to_str(pyObj)



## returns a list of pyObj representing the selected items in the scene.

def getSelectedItems():
interface_info = lwsdk.LWInterfaceInfo()
return interface_info.selected_items()



## given a list of pyObjs, this function returns a list of IDs. It relies
## on the "getObjID" function above.

def getIDs(pyObjs):
li = []
for pyObj in pyObjs:
id = getObjID(pyObj)
li.append(id)
return li



## This function relies on three of the above functions to return
## a list of selected item IDs. It needs "getObjID", "getIDs" and
## "getSelectedItems"

def getSelectedItemsIDs():
selected_items = getSelectedItems()
objIDs = getIDs(selected_items)
return objIDs



## This function returns the pyObj representing the current item.
## It relies on the "getSelectedItems" function, which is defined above.

def getCurrentItem():
return getSelectedItems()[0]



## This function returns the ID of the current item and relies on three
## of the above functions: "getCurrentItem", "getSelectedItems"
## and "getObjID"

def getCurrentItemID():
obj = getCurrentItem()
return getObjID(obj)

Celshader
05-03-2012, 01:39 PM
I prefer iterating over lists and dictionaries instead of while loops in Python, so these functions return dictionaries of specific item types in Layout:



## returns a dictionary of objects. The keys contain the
## ID numbers, while the items contain the pyObjs themselves.

def getObjects():
d = {}
info = lwsdk.LWItemInfo()
item = info.first(lwsdk.LWI_OBJECT, None)
while(item):
id = getObjID(item)
d[id] = {}
d[id] = item
item = info.next(item)
return d




## returns a dictionary of cameras. The keys contain the
## ID numbers, while the items contain the pyObjs themselves.

def getCameras():
d = {}
info = lwsdk.LWItemInfo()
item = info.first(lwsdk.LWI_CAMERA, None)
while(item):
id = getObjID(item)
d[id] = {}
d[id] = item
item = info.next(item)
return d




## returns a dictionary of lights. The keys contain the
## ID numbers, while the items contain the pyObjs themselves.

def getLights():
d = {}
info = lwsdk.LWItemInfo()
item = info.first(lwsdk.LWI_LIGHT, None)
while(item):
id = getObjID(item)
d[id] = {}
d[id] = item
item = info.next(item)
return d

BigHache
05-04-2012, 07:01 PM
Good stuff! Thank you!

xchrisx
07-09-2012, 09:49 AM
Just started to get into python coding for LW and this stuff helps a bunch. Thanks Jen.

Celshader
07-09-2012, 11:05 AM
Glad to help. Glancing at my code for getObjects/getCameras/getLights reveals a line that should probably be deleted.

This:



d[id] = {}
d[id] = item

...means the exact same thing as this:




d[id] = item

There's no point in assigning an empty dictionary to a dictionary entry if I'm just going to replace it with a pyObj in the next line. It must have been a leftover from an earlier draft of the code.

Celshader
07-18-2012, 04:04 PM
Two more "get directory" functions:



## configs directory

def getConfigsDir():
return lwsdk.LWDirInfoFunc('Settings')



## temporary path directory

def getTempDir():
return lwsdk.LWDirInfoFunc('Temp')

Celshader
07-18-2012, 05:40 PM
Typing in:


help(lwsdk)

...in PCore Console reveals a huge chunk of information which can be saved to a text file using "Save Log." This information includes these variables:



LWFTYPE_ANIMATION = 'Animations'
LWFTYPE_CONTENT = 'Content'
LWFTYPE_DYNAMICS = 'Dynamics'
LWFTYPE_ENVELOPE = 'Envelopes'
LWFTYPE_IMAGE = 'Images'
LWFTYPE_INSTALL = 'Install'
LWFTYPE_LIGHTS = 'Lights'
LWFTYPE_MOTION = 'Motions'
LWFTYPE_NODES = 'Nodes'
LWFTYPE_OBJECT = 'Objects'
LWFTYPE_PLUGIN = 'Plug-ins'
LWFTYPE_PREVIEW = 'Previews'
LWFTYPE_PSFONT = 'PSFonts'
LWFTYPE_RADIOSITY = 'Radiosity'
LWFTYPE_RIGS = 'Rigs'
LWFTYPE_SCENE = 'Scenes'
LWFTYPE_SETTING = 'Settings'
LWFTYPE_SOUNDS = 'Sounds'
LWFTYPE_SURFACE = 'Surfaces'
LWFTYPE_VERTCACHE = 'VertCache'

It looks like these variables and their values are what artists would use to get the directory paths they want using lwsdk.LWDirInfoFunc(). I've written code snippets above for getting the content directory, configs directory and temp path. Using the above information, here's a code snippet for getting the location of LWSN.exe:



import lwsdk
import os

def getLWSN():
return os.path.join( lwsdk.LWDirInfoFunc('Install'), "lwsn.exe" )



An alternative that uses the variable name for the same information:



import lwsdk
import os

def getLWSN():
return os.path.join( lwsdk.LWDirInfoFunc(LWFTYPE_INSTALL), "lwsn.exe" )



:lwicon:

skarloc
07-19-2012, 06:45 AM
Thanks awfully for these - they helped me (along with the LWPy videos on YouTube) to do 2 scripts. My first excursion into LW Scripting (and Python) and getting my head around (a) the Python syntax and (b) the LW data model was an interesting challenge.

For those interested, the 2 scripts are similar; one aligns the positions of objects in Layout with the last object selected (like the "Align" options, except it does all 3 at the same time, avoiding that annoying problem of the selection being inverted, meaning you have to reselect the items each time you want to align them). The other one does the same, but also sets the rotation to be the same as the last object (a kind of one-frame parenting).

If anyone wants them (even for study for your own scripts), leave me a message.

P.S. I hope these functions didn't existe elsewhere...

[Edit] Thanks also to GregMalick, for his channel code which put me on the right track for getting the current position. Thanks also to those who I haven't mentioned as well.

Celshader
07-19-2012, 09:04 PM
Glad to help, skarloc!

Here's another code snippet that a professional kindly handed me today:



## is SaveRGB turned on?

def getSaveRGB():
return (lwsdk.LWSceneInfo().renderOpts & lwsdk.LWROPT_SAVERGB) != 0


This status used the LWROPT_SAVERGB flag to reveal the status of SaveRGB. Typing in help(lwsdk) on the PCore Console reveals these other render flags:



LWROPT_BLURBACKGROUND = 1048576
LWROPT_CACHERADIOSITY = 67108864
LWROPT_CAUSTICS = 65536
LWROPT_CAUSTICSCACHE = 536870912
LWROPT_DEPTHOFFIELD = 64
LWROPT_DIRECTIONALRAYS = 16777216
LWROPT_ENHANCEDAA = 512
LWROPT_EVENFIELDS = 16
LWROPT_EYECAMERA = 1073741824
LWROPT_FIELDS = 8
LWROPT_INTERPOLATED = 524288
LWROPT_LIMITDYNAMICRANGE = 33554432
LWROPT_LIMITEDREGION = 128
LWROPT_MOTIONBLUR = 32
LWROPT_OCCLUSION = 131072
LWROPT_PARTICLEBLUR = 256
LWROPT_RADIOSITY = 32768
LWROPT_REFLECTTRACE = 2
LWROPT_REFRACTTRACE = 4
LWROPT_RENDERLINES = 262144
LWROPT_RTTRANSPARENCIES = 16384
LWROPT_SAVEALPHA = 4096
LWROPT_SAVEANIM = 1024
LWROPT_SAVERGB = 2048
LWROPT_SHADOWTRACE = 1
LWROPT_UNPREMULTIPLIEDALPHA = -2147483648
LWROPT_USEAMBIENT = 8388608
LWROPT_USEBEHINDTEST = 268435456
LWROPT_USEGRADIENTS = 134217728
LWROPT_USETRANSPARENCY = 2097152
LWROPT_VOLUMETRICRADIOSITY = 4194304
LWROPT_ZBUFFERAA = 8192

This is only for querying the status of render options and not setting them. Setting the status of render options involves stock LightWave SDK Commands. In a Generic script, I would do this within the Generic class to turn on SaveRGB:


class Example(lwsdk.IGeneric):
def __init__(self, context):
super(Example, self).__init__()

def process(self, ga):
## make sure SaveRGB is turned on.
ga.evaluate(ga.data, "SaveRGB 1")


"SaveRGB 1" is an example of a Command. A fantastic resource for Commands is LScript Commander in Layout. Launch LScript Commander and start poking around Layout. Half of the things you do will pop up in LScript Commander as proper LightWave SDK Commands.

For example, turning on SaveRGB would show up in LScript Commander as "SaveRGB 1" -- and that's what you would use to turn on SaveRGB in your Python script. That's also how the instruction would appear in the text of the *.lws file -- which seems more and more to be a collection of LightWave SDK Commands.

Celshader
07-20-2012, 11:55 AM
A Mac user informed me that the LWSN program is just called "lwsn" on the Mac instead of "lwsn.exe." With that in mind, here's a cross-platform approach to getting the path to LWSN:


import lwsdk
import os
import platform

def getLWSN():
test = platform.platform()
if test.startswith("Windows"):
lwsn = "lwsn.exe"
else:
lwsn = "lwsn"
return os.path.join( lwsdk.LWDirInfoFunc(LWFTYPE_INSTALL), lwsn )

skarloc
07-20-2012, 02:08 PM
Here are a few lines of code I use to ensure that Parent In Place is correct :

1) Before the process function :


def setPIP(self, state):
if (self._cur_pip != state):
lwsdk.command("ParentInPlace")
self._cur_pip = state
At the start of the process function :

init_pip = ((lwsdk.LWInterfaceInfo().generalFlags & lwsdk.LWGENF_PARENTINPLACE) == lwsdk.LWGENF_PARENTINPLACE)
self._cur_pip = init_pip
When I need to set the PIP :

self.setPIP(False)
self.setPIP(True)
On quitting the process function :

self.setPIP(init_pip)
That way, I don't need to keep track of what the PIP is (with what I'm doing, I need to change it often).

Celshader
07-20-2012, 04:33 PM
skarloc, thank you so much for that last post. Until you wrote that, I did not know one could execute commands just by writing:


lwsdk.command(<COMMAND>)

This is a lot less cumbersome than the ga.evaluate(ga.data, <COMMAND>) method I was using in my scripts.

:beerchug:

skarloc
07-20-2012, 05:16 PM
No problem - To be honest, I've never used that method (but I've seen it written - and you're right, it's a bit cumbersome).

Speaking of reducing cumbersonness, I've done a large number of wrappers for the functions so as to make the code more readable and homogeneous. For example :


lwsdk.command("Position %f %f %f" % (fb_x, fb_y, fb_z))
lwsdk.command("Rotation %f %f %f" % (fb_h, fb_p, fb_b))
if not autokey_is_on:
lwsdk.command("CreateKey %f" % current_time)

I've replaced with :

self.setPositionAndRotation(fb_x,fb_y,fb_z,fb_h,fb _p,fb_b,current_time)

where setPositionAndRotation is defined :


def setPositionAndRotation(self, x, y, z, h, p, b, cur_time):
lwsdk.command("Position %f %f %f" % (x, y, z))
lwsdk.command("Rotation %f %f %f" % (h, p, b))
if not self._autokey_on:
lwsdk.command("CreateKey %f" % cur_time)

This means (obviously) that in my main body, I've got 1 line of code and not 4. Also, I'm not worried about forgetting the autokey bit.

FYO, in order to read the position and rotation of a given object at a given time (which may or not be a frame) :


def getPositionAndRotation(self, object, cur_time):
# Note : this returns degrees - hence the "* 57.2957795"
groupID = lwsdk.LWItemInfo().chanGroup( object )
chanID = lwsdk.LWChannelInfo().nextChannel(groupID, None)
(x,y,z,h,p,b) = (0.0,0.0,0.0,0.0,0.0,0.0)
while chanID:
channelName = lwsdk.LWChannelInfo().channelName(chanID)
if channelName == "Position.X":
x = lwsdk.LWChannelInfo().channelEvaluate(chanID, cur_time)
elif channelName == "Position.Y":
y = lwsdk.LWChannelInfo().channelEvaluate(chanID, cur_time)
elif channelName == "Position.Z":
z = lwsdk.LWChannelInfo().channelEvaluate(chanID, cur_time)
elif channelName == "Rotation.H":
h = lwsdk.LWChannelInfo().channelEvaluate(chanID, cur_time) * 57.2957795
elif channelName == "Rotation.P":
p = lwsdk.LWChannelInfo().channelEvaluate(chanID, cur_time) * 57.2957795
elif channelName == "Rotation.B":
b = lwsdk.LWChannelInfo().channelEvaluate(chanID, cur_time) * 57.2957795
# else - we don't care !
chanID = lwsdk.LWChannelInfo().nextChannel(groupID, chanID)
return (x,y,z,h,p,b)

And to use it :


(fb_x,fb_y,fb_z,fb_h,fb_p,fb_b) = self.getPositionAndRotation(temp_obj,startTime)

NanoGator
05-18-2014, 03:06 PM
Hey All,

I do apologize for the necro-post, but this thread has been amazingly useful. I do have one question, though: I see you're using lwsdk.LWChannelInfo().channelEvaluate to ask a Channel what value it has. But I noticed in the documentation there's a ChannelAccess class that SEEMS to do that same job. Unfortunately I have not gotten that working. I was curious if a.) there was some benefit to using that class instead and b.) ...how exactly do you get that working? Passing a channelID doesn't seem to work.

B

Celshader
05-18-2014, 04:44 PM
Hey All,

I do apologize for the necro-post, but this thread has been amazingly useful. I do have one question, though: I see you're using lwsdk.LWChannelInfo().channelEvaluate to ask a Channel what value it has. But I noticed in the documentation there's a ChannelAccess class that SEEMS to do that same job. Unfortunately I have not gotten that working. I was curious if a.) there was some benefit to using that class instead and b.) ...how exactly do you get that working? Passing a channelID doesn't seem to work.

B

I hope skarloc answers, myself. I have not yet used channelEvaluate, but I have a script (http://forums.newtek.com/showthread.php?141133) that at one point loops through the keys of a channel and replaces the original key values with "overscan" values. I am posting that code here in case it is of use.


"""
Clone Overscan Camera by Jennifer Hachigian
2014-04-14
With thanks to Deuce Bennett.

Usage: select a camera in the scene and run this script to clone an
overscan version of that camera.
"""

import lwsdk

def error(arg1, arg2):
""" simplified way to call an error message. """
lwsdk.LWMessageFuncs().error(arg1, arg2)

def getZoomFactorEnvelope(item):
""" returns the ZoomFactor envelope if it exists, and
None if no ZoomFactor enveloper exists. """
zEnv = None
cGrp = lwsdk.LWItemInfo().chanGroup(item)
chan = lwsdk.LWChannelInfo().nextChannel(cGrp, None)
while chan:
chanName = lwsdk.LWChannelInfo().channelName(chan)
if chanName == "ZoomFactor":
zEnv = chan
chan = lwsdk.LWChannelInfo().nextChannel(cGrp, chan)
return zEnv

def overscanZoomFactorEnvelope(chan, overscan):
""" applies overscan to every key in the ZoomFactor envelope. """
env = lwsdk.LWChannelInfo().channelEnvelope(chan)
key = lwsdk.LWEnvelopeFuncs().nextKey(env, None)
while key:
val = lwsdk.LWEnvelopeFuncs().keyGet(env, key, lwsdk.LWKEY_VALUE)
old_zoom = val[1]
new_zoom = val[1]/(overscan)
lwsdk.LWEnvelopeFuncs().keySet(env, key, lwsdk.LWKEY_VALUE, new_zoom)
key = lwsdk.LWEnvelopeFuncs().nextKey(env, key)


def cloneOverscanCamera():
""" clones a selected camera, adjusts its resolution/zoom,
and renames it with the percentage of overscan involved. """
items = lwsdk.LWInterfaceInfo().selected_items()
if not items:
error("No selected items.", "Please select an item.")
return

item = items[0]
if lwsdk.LWItemInfo().type(item) != lwsdk.LWI_CAMERA:
error("No selected camera.", "Please select a camera.")
return

zEnv = getZoomFactorEnvelope(item)

curTime = lwsdk.LWInterfaceInfo().curTime
name = lwsdk.LWItemInfo().name(item)
w, h = lwsdk.LWCameraInfo().resolution(item)

""" get overscan value from user """
text = lwsdk.LWMessageFuncs().askName("Input new width.", "Width", str(w))
try:
overscan = float(text)/float(w)
except ValueError:
error("Wrong value.", "Enter a number next time!")
return
except TypeError:
error("Cancelled.", "User cancelled operation.")
return

new_w = int( text )
new_h = int( h * (overscan) )

lwsdk.command("Clone 1")
lwsdk.command("Rename %s_overscan_%d%%" % (name, (overscan-1.0) * 100))
lwsdk.command("FrameSize %d %d" % (new_w, new_h))

if zEnv:
""" apply overscan to the ZoomFactor envelope """
overscanZoomFactorEnvelope(zEnv, overscan)
else:
""" apply overscan to the static ZoomFactor value """
zoom = lwsdk.LWCameraInfo().zoomFactor(item, curTime)
new_z = zoom/(overscan)
lwsdk.command("ZoomFactor %f" % new_z)

cloneOverscanCamera()

dulo
05-20-2014, 11:09 AM
I am just writing a houdini pfx reader/writer.
The resulting python code could also be interesting for non-houdini users .. so I just wanted to share the houdini independant parts.


pfxDataFile = file('D:\\Root\\repositories\\tresuma\\source\\pyt hon\\common\\IO\\LW\\emitter.pfx')

format_version, total_particles, total_frames = struct.unpack('>iii', pfxDataFile.read(12))
print 'header', format_version, total_particles, total_frames
for particle in range(0, total_particles):
print 'current particle', particle
start_frame, end_frame, life_frame = struct.unpack('>iii', pfxDataFile.read(12))
print 'particle data', start_frame, end_frame, life_frame
for position in range(start_frame, end_frame):
xpos, ypos, zpos = struct.unpack('>fff', pfxDataFile.read(12))
print 'particle pos', xpos, ypos, zpos

pfxDataFile.close()

this is the reader which atm just prints the data to the console.
the writer contains a lot of hom code. so its not that interesting in this context

OnlineRender
05-20-2014, 11:58 AM
Thanks Jen ,brilliant thread ...

just started to mess around with python & LW

mainly >>> import urllib
>>> urllib.urlretrieve('http://www.example.com/myfile.zip', 'path/to/download/dir/myfile.zip')

essentially what I am doing is making a GUI with text fields/valuse that are assigned to download files * but lost lol

dulo
05-21-2014, 03:12 PM
Little fix for read pfx python script

pfxDataFile = file('emitter.pfx')
streamPos = pfxDataFile.tell()
format_version, total_particles, total_frames = struct.unpack('>iii', pfxDataFile.read(12))
print streamPos, 'header', format_version, total_particles, total_frames
for particle in range(0, total_particles):
streamPos = pfxDataFile.tell()
print streamPos, 'current particle', particle
start_frame, end_frame, life_frame, weight, resistance, size = struct.unpack('>iiifff', pfxDataFile.read(24))
streamPos = pfxDataFile.tell()
print streamPos, 'particle data', start_frame, end_frame, life_frame, weight, resistance, size
for position in range(start_frame, end_frame + 1):
streamPos = pfxDataFile.tell()
xpos, ypos, zpos = struct.unpack('>fff', pfxDataFile.read(12))
print streamPos, 'particle pos', xpos, ypos, zpos

pfxDataFile.close()

NanoGator
05-22-2014, 01:22 PM
I hope skarloc answers, myself. I have not yet used channelEvaluate, but I have a script (http://forums.newtek.com/showthread.php?141133) that at one point loops through the keys of a channel and replaces the original key values with "overscan" values. I am posting that code here in case it is of use.


"""
Clone Overscan Camera by Jennifer Hachigian
2014-04-14
With thanks to Deuce Bennett.

Usage: select a camera in the scene and run this script to clone an
overscan version of that camera.
"""

import lwsdk

def error(arg1, arg2):
""" simplified way to call an error message. """
lwsdk.LWMessageFuncs().error(arg1, arg2)

def getZoomFactorEnvelope(item):
""" returns the ZoomFactor envelope if it exists, and
None if no ZoomFactor enveloper exists. """
zEnv = None
cGrp = lwsdk.LWItemInfo().chanGroup(item)
chan = lwsdk.LWChannelInfo().nextChannel(cGrp, None)
while chan:
chanName = lwsdk.LWChannelInfo().channelName(chan)
if chanName == "ZoomFactor":
zEnv = chan
chan = lwsdk.LWChannelInfo().nextChannel(cGrp, chan)
return zEnv

def overscanZoomFactorEnvelope(chan, overscan):
""" applies overscan to every key in the ZoomFactor envelope. """
env = lwsdk.LWChannelInfo().channelEnvelope(chan)
key = lwsdk.LWEnvelopeFuncs().nextKey(env, None)
while key:
val = lwsdk.LWEnvelopeFuncs().keyGet(env, key, lwsdk.LWKEY_VALUE)
old_zoom = val[1]
new_zoom = val[1]/(overscan)
lwsdk.LWEnvelopeFuncs().keySet(env, key, lwsdk.LWKEY_VALUE, new_zoom)
key = lwsdk.LWEnvelopeFuncs().nextKey(env, key)


def cloneOverscanCamera():
""" clones a selected camera, adjusts its resolution/zoom,
and renames it with the percentage of overscan involved. """
items = lwsdk.LWInterfaceInfo().selected_items()
if not items:
error("No selected items.", "Please select an item.")
return

item = items[0]
if lwsdk.LWItemInfo().type(item) != lwsdk.LWI_CAMERA:
error("No selected camera.", "Please select a camera.")
return

zEnv = getZoomFactorEnvelope(item)

curTime = lwsdk.LWInterfaceInfo().curTime
name = lwsdk.LWItemInfo().name(item)
w, h = lwsdk.LWCameraInfo().resolution(item)

""" get overscan value from user """
text = lwsdk.LWMessageFuncs().askName("Input new width.", "Width", str(w))
try:
overscan = float(text)/float(w)
except ValueError:
error("Wrong value.", "Enter a number next time!")
return
except TypeError:
error("Cancelled.", "User cancelled operation.")
return

new_w = int( text )
new_h = int( h * (overscan) )

lwsdk.command("Clone 1")
lwsdk.command("Rename %s_overscan_%d%%" % (name, (overscan-1.0) * 100))
lwsdk.command("FrameSize %d %d" % (new_w, new_h))

if zEnv:
""" apply overscan to the ZoomFactor envelope """
overscanZoomFactorEnvelope(zEnv, overscan)
else:
""" apply overscan to the static ZoomFactor value """
zoom = lwsdk.LWCameraInfo().zoomFactor(item, curTime)
new_z = zoom/(overscan)
lwsdk.command("ZoomFactor %f" % new_z)

cloneOverscanCamera()

Thank you and thanks to everybody who is contributing code.. it has made my life lots easier, wanted you all to know it was worth your time.

NanoGator
07-11-2014, 08:31 PM
When you load an image into the image list, can you get its resolution and ask it what color a specified pixel is?

clintonman
07-11-2014, 08:48 PM
When you load an image into the image list, can you get its resolution and ask it what color a specified pixel is?
Yes. I don't have any basic code, but the "HDR to lights" script reads that information. You can look in the Lightwave specific portions of the code.

http://clintons3d.com/plugins/lightwave/index.html

The script is about half way down the page.

ncr100
07-29-2014, 01:50 AM
May be OT: but has anyone thought about creating a github (or bitbucket) repository to collect these python utilities? We could spread the responsibility for vetting incoming changes / pull requests across a team of admins.

I think of a utilities repository since these are not plugins but helper functions.

I know LW 12 is supposed to provide a more extensive wrapper, perhaps making the "Lightwave Commands" first class Python citizens...but we don't have to wait for years for NewTek to upgrade their library via a major platform release. In fact that LW Command wrapping could be generated code...take each Command and make a def with the right # of params and steal some Python documentation from the SDK docs.

jeric_synergy
10-11-2015, 01:44 PM
Just going to bump this thread, --I'm sure people have written other useful snippets since 7/29/2014. Please share, because this is one of the most useful threads in the forum.

Evil_Alan
02-14-2018, 10:08 AM
It's Valentine's Day. Lightwave needs some love!


Python module needed:
Transforms3d: https://matthew-brett.github.io/transforms3d/

Optional:
pynput: https://pythonhosted.org/pynput/keyboard.html
pynput installation: https://www.youtube.com/watch?v=DTnz8wA6wpw



import numpy as np
from transforms3d.euler import euler2mat, mat2euler
from pynput.keyboard import Key, Controller
from transforms3d.quaternions import quat2mat


from lwsdk import ModCommand, Vector
from lwsdk.pris.modeler import init, getpolygons, selpolygon, editbegin, editend

if not init(ModCommand()):
raise Exception('Failed to initialize PRIS')

keyboard = Controller()

#SKLGNS = False
SKLGNS = True




'''
skeleton = []
for x in xrange(numBones):
boneData = bdata.read()
skeleton.append(boneData)
'''


allptID = []
trans = []
if SKLGNS == True:
c = -1
cs_options = lwsdk.marshall_dynavalues("1") # set to layer with Skelegons
result = mod_command.execute(mod_command.data, cs_dict["SETLAYER"], cs_options, lwsdk.OPSEL_USER)
mesh_edit_op = mod_command.editBegin(0, 0, lwsdk.OPSEL_USER)
for bone in skeleton:
c += 1
BNps = (bone.posZ, bone.posY, bone.posX)
if bone.transformType == 1:
BNrt = (bone.rotW, bone.rotZ, bone.rotY, bone.rotX)
rt = quat2mat(BNrt)
else:
rt = euler2mat(bone.rotX, bone.rotY, bone.rotZ, axes='rzyx')
BNsc = (bone.scaleX, bone.scaleY, bone.scaleZ)
'''
BNsc = np.matrix(
[[bone.scaleX, 0, 0, 0], \
[0, bone.scaleY, 0, 0], \
[0, 0, bone.rotZ, 0], \
[0, 0, 0, 1]]
)
'''
bnXM = np.matrix(
[[rt[0][0], rt[0][1], rt[0][2], 0], \
[ rt[1][0], rt[1][1], rt[1][2], 0], \
[ rt[2][0], rt[2][1], rt[2][2], 0], \
[ BNps[0], BNps[1], BNps[2], 1]]
)
#bnXM *= BNsc
trans.append(bnXM)
boneName = BoneNameArray[c]
BNparent = bone.parentID

if BNparent != 0:
bnXM *= trans[BNparent]
pt = [bnXM[3,2], bnXM[3,1], bnXM[3,0]]
else:
pt = [bnXM[3,2], bnXM[3,1], bnXM[3,0]]


g = mesh_edit_op.addPoint(mesh_edit_op.state, pt)
allptID.append(g)


if BNparent != 0:
skelptID = []
skelptID.append(allptID[BNparent])
skelptID.append(allptID[-1])

# state, type, template, surf, point_array
skel = mesh_edit_op.addPoly (mesh_edit_op.state, lwsdk.LWPOLTYPE_BONE, None, "b_", skelptID)
mesh_edit_op.done(mesh_edit_op.state, edit_op_result, 0)






# Rename and set weightmaps
if not editbegin():
raise Exception('Failed to haze new guy.')
try:
polys = getpolygons()
except:
raise Exception('Call to getpolygons() got stuck in a corn field in Nebraska!')
if len(polys) == 0:
raise Exception('No polygons detected. Why you so stupid, stupid?!')
editend()

p = lwsdk.LWStateQueryFuncs().mode(lwsdk.LWM_MODE_SELE CTION)
print "\n"
print "MODE SELECTION is:\t " + str(p)
# 0:Point, 1:Polygon, 2:Volume, 3:Edge

cs_test0 = mod_command.lookup(mod_command.data, "cmdseq")
if p == 1:
for x in xrange(len(polys)):
name = BnNmArray[x]

lwsdk.pris.modeler.selmode(mode=1)
selpolygon('set', 'polyid', polys[x])

cs_options0 = lwsdk.marshall_dynavalues(("SetTag", "PART "+name))
result0 = mod_command.execute(mod_command.data, cs_test0, cs_options0, lwsdk.OPSEL_USER)

cs_options1 = lwsdk.marshall_dynavalues(("SetTag", "BONE "+name))
result1 = mod_command.execute(mod_command.data, cs_test0, cs_options1, lwsdk.OPSEL_USER)

'''
not all bones have weightmaps:
root, bones for weapons stored in another file, rig controls, etc
so check with list that has the weightmap names
'''
if name in bw:
cs_options2 = lwsdk.marshall_dynavalues(("SkelegonSetWeight", name))
result1 = mod_command.execute(mod_command.data, cs_test0, cs_options2, lwsdk.OPSEL_USER)

lwsdk.pris.modeler.selmode(mode=1)
selpolygon('clear', 'polyid', polys[x])
else:
raise Exception('Not in Polygon Mode - Skelegons not renamed')


# Fit All
keyboard.press('a')
keyboard.release('a')

'''
# Fit Selected
with keyboard.pressed(Key.shift):
keyboard.press('a')
keyboard.release('a')
'''



https://i.imgur.com/ZUTjVV2.png (https://orig00.deviantart.net/ca91/f/2018/045/1/0/skelegons_by_mrfuzzlebum-dc35gtl.png)