Sep 12 2012

Exporting .pc2 files straight out of maya..

So recently I’ve been working on maya to max pipeline…
i ran into some issues with the built in cacheFile mechanism in maya so i decided that seance .pc2 is such a simple binary format that i could just write my own .pc2 exporter!

here’s what i came up with πŸ™‚

ps. this heavily borrowed from the blender importer written by “Matt Ebb” http://mattebb.com

import maya.cmds as cmds
import struct

def frange(x, y, jump,):
    ar =[]
    while x < y:
        ar.append(x)
        x += jump
    return ar

    
def writePC2File(filename, shape, world=True, zUp=False, startF=0, endF=100, samplesPerFrame=1):
    sampleRate = 1.0/samplesPerFrame
    numPoints = cmds.polyEvaluate(shape,v=True)#len(me.verts)
    totalFrames = endF - startF
    timeRange = frange(startF, startF+totalFrames+sampleRate, sampleRate)
    numSamples = len(timeRange)
    
    headerFormat='<12ciiffi'
    headerStr = struct.pack(headerFormat, 'P','O','I','N','T','C','A','C','H','E','2','\0', 1, numPoints, startF, sampleRate, numSamples)
    
    file = open(filename, "wb")
    file.write(headerStr)
    #---loop through time..
    cmds.refresh(su=True)
    sampleFrames = [] #--generate sample frames...
    
    print "startF=%s endF=%s sampleRate=%s shape=%s" % (startF, startF+numSamples, sampleRate,shape)
    #print "range is ",range(startF, startF+numSamples, sampleRate)

    for i in timeRange:
        #print "time is ",i
        cmds.currentTime(i)
        for p in range(numPoints):
            vPos = cmds.pointPosition( shape+'.vtx['+str(p)+']',w=True)
            vFormat = "fff" #-- add a "less than" character at the begining of the string.. i removed it becuse my wordpress code syntax was doing goofy stuff"
            thisVertex = struct.pack(vFormat, vPos[0], vPos[1], vPos[2])
            file.write(thisVertex)

    
    cmds.refresh(su=False)
    file.flush()
    file.close()
    print filename," was sucessfully written out"
    return True
    
#example    
out = r'C:\testMaya\test.pc2'
shp = cmds.listRelatives(shapes=True)
writePC2File(out,shp[0],startF=1000,endF=1200,samplesPerFrame=2)

Jul 23 2012

.Net hash table and maxscript

don’t ask why but recently i have had to deal with .net hashtables inside of maxscript πŸ™‚
i found most of the information i needed to deal with this data type preaty eazly..
but the one thing that i could not find was how to print the key values of a given hash table (which is useful if your debugging stuff and you don’t know what the hash table currently holds)
Any way..
after doing some reserach on data types i cam came up with this solution… if there’s a better way to do it let me know..

fn printOutHashTableKeys =
(
	--build the has table
	hash = dotNetObject "System.Collections.Hashtable"
	hash.add "_valueA" 1
	hash.add "_valueB" 2
	hash.add "_valueC" 3
	hash.add "_valueD" 4
	hash.add "_valueE" 5

	--spit the posible keys out
	DNArray = dotNetObject "System.string[]" hash.keys.count
	hash.keys.copyTo DNArray 0
	for i = 1 to hash.keys.count do print (DNArray.get (i-1))
)
printOutHashTableKeys()

Jun 22 2012

Crank your engines!!

20120621-145508.jpg
Engine pistons/Cylinders are a complex task for any rigger..
They seem surprisingly easy at first but can take a about every trick on the book to get this things to work with out having the shafts wiggle at the pivots. Anyway I had some downtime at work so I set my self to find a logical mathematical approach to address this setup with a single expression πŸ™‚
The trick here.. Is knowing how much to offset the Cylinder inside along it’s path of least resistance, so that when we apply an aim constraint to the shaft the distance between pivots is always constant..
Here’s an initial image illustrating my method to solving for this position, let my warn you that, I have almost zero proper math education so this image is probably no where near the universe of proper math notation πŸ™‚

20120621-150302.jpg

So the basic idea for me was that “P” is a locator that describes the center of the Cylinder with one of it’s axis describing the direction in which it slides (“L”). “P1” is a locator that will serve as the position in which the the shaft will pivot in relationship to the crank, while “Dis” is the length of the Crank it self.Β  now the key to solving this problem, was to build upon the previously discussed ClosetPointOnRay algorythem we discussed early in this thread, which render “P2” the closet point to P1 along Ray “P”,”L”..

if you look closely the relationship betweenΒ  points p, p1, p2 is that of a right triangle. which means we can apply the “pythagoras theorem” to solve for “Dif” the distance between “P3” (the final solution to our problem) and “P2” once we have “Dif” then we can easily derive p3 by simple offsetting p2 along L by a distance of Dif..

here’s a snippet of the maxscript code i put together to test this..

dis = 62
p = $'Point'.transform.position
L = normalize $'Point002'.transform[2]
p1 = $'Point001'.position
p2 = closestPointOnRay (ray p L) p1
N = distance p1 p2
dif = sqrt((dis*dis)-(N*N))
p3 = p2 + (normalize(p - p2) * dif)
-- $'pOR'.position = p2 --this was added to visualize the closetPointOnRay
format "n=% dif=% p3D=%\n " N dif (distance p3 p1) --format to help debugging
p3

anyway here’s a quick video of the final result…
httpv://youtu.be/Urh9kYd0ywM


Jan 12 2012

Studio Level Managment Of Scripts and Tools in Maya

Today i started to do some online research on what are the current practice for script/and shelf deployment in maya.
the studio I’m currently working for is not a “Maya” house, but we’re getting to the point where often writing tools to convert scenes animated in maya, to something we can use in max. being that this is a somewhat esoteric topic i did not expect that my internet search would return much, but to my surprise i found this article by fellow TD Jonas Avrin.

he really does a great job at introducing the concept of the userSetup.mel as a ways of deploying his personal script from gig to gig.

which seems to me would also make a great starting point along with the mayaEnv.mel to starting the journey into the full time time management of Maya at the studio level.

this might not be anything new to maya TD’s but it was very informative to me πŸ™‚

cheers!

http://www.jonasavrin.com/2010/08/15/maya-env-configuration-of-variables-using-usersetup-mel/

http://www.jonasavrin.com/2011/02/18/maya-env-configuration-of-variables-using-usersetup-mel-and-usersetup-py/


Jan 10 2012

Singular Value Decomposition (SVD) and object oriented Bounding Boxes

So a continuation of my previous post.. while trying to figure out how to implement SVD (Singular Value Decomposition) algorithm to figure out the best possible aligned bounding box for a set of points.
my friend Ghram Fyffe pointed my to a python library called “numpy” which has great SVD functions that is incredibly easy to use πŸ™‚
seance 3ds max does not support python, i decided i would write the python code to read all the vertex information from a text file, and then overwrite the content of that text file with the resulting alignment matrix.
this method should be eazy to implement into any amp even if it does not have native python support.

i hope this code is helpful to you some one. here it is!

'''
PCA optimized axis generator generator (now threaded)

By Carlos Anguiano
this script will process a text file with point data
and generate and optimized object orient bounding box for the point set
c:\python26_64\python.exe
"C:\svnTools\trunk\S2\exchange\software\managed\pythonScripts\PCAOptOOBB\PCAOptOBB.py"
"C:\Users\SCANLI~2\AppData\Local\Temp\PointCloudData
'''
import time
import threading
import sys
import string
import numpy

import os
#this argument feed the script the folder which to parse for pointCloud data files
#PCFld = sys.argv[1]
PCFld = 'C:\Users\SCANLI~2\AppData\Local\Temp\PointCloudData'
maxThreads=8

def OptmizedTransformAxisFromPointCloadFile (infile):
     lines = open(infile,'r').readlines()
     lCount = len(lines)
     lCount
     vAr = ""
     for i in range(lCount):
         l = lines[i]
         vector = (l.rstrip())
         vAr += vector

         if i != (lCount-1):
             vAr += ";"
     #once you turn the file content into a string list (look up charArrays) the magic happens
     m = numpy.matrix(vAr)
     USV = numpy.linalg.svd(m)
     return USV[2]

def formatData(data):
     lines = []
     for i in range(3):
         A = numpy.asarray(data[i])
         x = A[0][0]
         y = A[0][1]
         z = A[0][2]
         s = "["+str(x)+","+str(y)+","+str(z)+"]\n"
         lines.append(s)
     return lines

class OptTransAxisFromPCF (threading.Thread):
     def __init__(self,filename):
         self.filename = filename
         threading.Thread.__init__(self)

     def run(self):
         filename = self.filename
         axisData = OptmizedTransformAxisFromPointCloadFile(filename)
         lines = formatData(axisData)
         f = open(filename,'w')
         f.writelines(lines)

def OptmizedTAxFromPCldFiles (Fldr):
     pcFiles = os.listdir(Fldr)
     if len(pcFiles) == 0:
         print "no files to process in this folder"
         return false

     for f in pcFiles:
         while(threading.activeCount()>=(maxThreads + 1)):
             print 'Waiting for a thread to free up...'
             time.sleep(.25)
         absF = Fldr+"\\"+f
         OptTransAxisFromPCF(absF).start()
     while(threading.activeCount() != 1):
         time.sleep(.25)

Optmiz1edTAxFromPCldFiles(PCFld)

Oct 11 2011

Object oriented minimum bounding box resources

I might put together a lib for auto creating low ress standing geometry for models..
Here’s some interesting stuff on creating an aligned bounding box that’s independent of the transform orientation of object it represents πŸ™‚
http://en.wikipedia.org/wiki/Minimum_bounding_box_algorithms


Oct 6 2011

C# .net custom forms

I’ve been playing around with the idea of learning c# so I can write custom .net widget..
Here’s a promising link with what seems to be a cool intro πŸ™‚
http://www.akadia.com/services/dotnet_user_controls.html


Jul 21 2011

Object oriented UIs in maxscript

(This is a work in progress so i hope that my poorly written ramblings are somewhat helpful even if they are a pain to read)
While maxscript has done a great job integrating the concept of object oriented programing through the “struc” constructor. Implementing rollouts and Ui elements that are fully embedded into their parent objects is not as straight forward as one would hope.
Not to long ago a good friend and coworker of mine, Lukas Lepicovsky, showed me a great way to embed the parent struct into almost any Ui element (via a local variable).
The method involves trading the “on open” event handler of a rollout for a initialization (fn init self) function that can be ran after the dialog has been opened, allowing to store the struct into the rollout, via a local variable (local self).

Let’s take a look at a sample struct with rollout function

Struct mytool
(
    /*rollout varible*/
     Ro,
    /*rollut creator function*/
    fn Ui =
    (
        Rollout ro01 "myrollout"
        (
            Button btn1
        )
        Ro01
    ),
    /*ui execution function*/
    Fn run =
    (
        Ro = Ui()
        Createdialog Ro
    )
)
/*make an instance of the struct*/
Mtool = myTool()
Mtool.run() /*--run the ui*/

In this example the code creates our object and runs our Ui creation function..
This method allows to interact with the rollout trough the struct instances (Mtool) but the rollout is unaware of the object instance.
If we wanted to use functions or access variables embedded in the struct we would have to do it by hard coding the global variable mTool in our rollout code, but we would be unable of having multiple instances of this same Ui open with out sharing the same object instance (bummer πŸ™ )

In order to make our struct and Ui fully intance-able. we will introduce the concept of self (a variable holding a link to the struct that created the Ui).
Here what this implementation looks like….

Struct mytool
(
    Self, /*--self varible will hold a link to the instance*/
    Ro, /*-- will hold value*/
    TestValue = 0, /*-- this is a test value you will you to test the live link between the object instance and the ui*/
    fn Ui = /*--create rollout function*/
    (
        Rollout ro01 "myrollout"
        (
            Local self /*--this will hold a link to our struct instance*/
            Button btn1 "go"
            Fn init pself = self = pself /*--this functions ran after dialog creation will embed a link to the instanced function into the local varialbe self of the rollout*/
            On btn1 pressed do print self.TestValue /*--print the testValue in the instanced struct*/
        )
        Ro01
    ),
    Fn run =
    (
        Ro = Ui() /*--make rollout*/
        Createdialog Ro
        Ro.init self /*--embed instanced struct into rollout*/
    )
)
Mtool = myTool() /*--create instance*/
Mtool.self = Mtool /*--make instance aware of it's self*/
Mtool.run() /*--run ui*/

So one might ask… why is this better? or why should i go through the trouble of making my ui aware of the object instance it created it?
well this while this type of aprouch might be over kill for simple tools, it facilitates the programing of tools that will be used in a way which will require them to be open multiple times but have different settings.

a good implementation of this might be ui for displaying all the keyframable objects of a character rigg.
in order to write this tool in a way that you can have multiple instances of the ui open pointing a different character riggs the ui needs to be able to store all it’s data in a nice package way that imune to changes by any other script in the application environment.
that’s where the struct comes πŸ™‚ and by letting the the ui refrence it’s own struct this programing task becomes much esier…

here’s a simple example of this pardigm using our struct example

/*--first instance*/
Mtool = myTool()
Mtool.self = Mtool
Mtool.run()
Mtool.testValue = 10

/*--second instance*/
Mtool = myTool()
Mtool.self = Mtool
Mtool.run()
Mtool.testValue = 3

this code should open two instances of the same ui… but pressing the go button will print different values based on the instance πŸ™‚


Jul 20 2011

converting activeX tabs to dotNet tabs in maxScript (repost)

This is an other repost from an old blog.. i figure i transfer it over πŸ™‚

Ok, so i started updating some scripts that I’ve used tabs for, this is what i have found so far.

the dot net controller syntax for tabs is..

dotNetControl axList "System.Windows.Forms.Tabcontrol"

this makes a tab controller with the variable name of axList
the previous activeX controller would make the tab group and give you one default tab,Γ‚ this is not the same for dotNet tabs. in dotNet Tabs you have to create each tab your self.

adding new tabs is very easy, here’s the syntax to add tabs….

Tab = axList.tabPages.add "myAwsomeTab"

tab information can be access through “tabPages”, for example to query how many tabs there are in the tab controller you can do this…

numberOfTabs = axList.tabPages.count

to query individual tabs you have to use add “.Item” and then the index of the node you want to acess, (keep in mind that dot net controller arrays start at 0 and not at 1 like activeX and maxscript arrays)

firstTab = axList.tapPages.item[0]

this will store the first tab in the tab controller to in the variable first Tab.

if you need to query the or change the selected node there are now two methods to do this

selectedIndex = axlist.selectedIndex
selectedTab = axlist.selectedTab

the last thing i found out is some simple way of changing the appearance of your tabs.

tabStyle = dotNetClassΓ‚  "System.Windows.Forms.TabAppearance"

this give you a dot net class from which you can apply 1 of the the following 3 styles

apClass.buttons
apClass.flatButtons
apClass.normal

this is how you apply the flatButtons style to your tab controller.

tabStyle = dotNetClass  "System.Windows.Forms.TabAppearance"
axlist.Appearance = apClass.flatButtons

this is what I’ve run into today, i don’t like how the tab’s are highlighted by default when selected i don’t think it is obvious enough. i will play around and see if a can find some good methods of changing that, i might play around with changing the color of selected nodes or something until then, good luck and good night.

cheers,

Los.


Jul 20 2011

controlling Dot Net Fonts in Maxscript (Repost)

This are some old notes, i had posted on another site regarding activeX to dotNet scripting… might be old news but i figured someone might find it usefull πŸ™‚

So today i began to look into dot net controllers inside of max so that i can brake the studios current dependency on max 8 (activeX controller in max 9 don’t work unless 8 is installed),

and while it seem that the general work flow of the activeX controllers carries over to dotNet, there are allot of thing that are not documented and have become way more complex.

the main thing i noticed is dealing with object fonts, sizes, and styles such as bold italic etc, has become a bit more of a chore. here are some of the things i’ve found out, that i couldn’t find any where on the net.

when dealing with tree view nodes under activeX this is how you would change the text

node.font = "Tahoma"
node.fontsize = 7
node.bold = true

this has become a bit more complex in dotNet. a font is now an object value which is created out of sevral other objects and classes…

“fontfamily” this is the actual windows font to use

“fontStyle” this is the style of the text which can be bold , italic, crossed, etc

size can be controlled by a single integer value

so the first thing is to create both of this objects

fontFam = dotNetObject "system.drawing.fontfamily" "tahoma" --this makes the font object
fontStyle = dotNetClass "system.drawing.fontStyle" --this creates a list from which style can be pulled

next we will put it all together

myFont = dotNetObject "system.drawing.font" fontFam 7 fontStyle.bold
--the node font can now be overwritten by this line
node.nodeFont = myFont

anwyas i hope this helps some of you out there some. i will try and make more of this blog as i keep working with the new dotNet controllers in maxscript.

cheers,

Los.