Jump to content

Writing .mc ncache (maya fluids) from houdini.


moomarkus

Recommended Posts

writeMCC.007.otl

 

#########################################################################################
#  Python script to process Volume from Houdini, then
#  Write it out to Maya's .mc fluid cache format
#
#  Author:  Markus Ng  
#  mark.c.ng@gmail.com
#  www.distill3d.com
#  Date  :  Oct 3, 2013
#
#  Limitations:   Resizing *FIXED* volumes seems to be un stable, so use consist volume size
#  problem was indexing of volume (cartiesian 3 space to 1 linear array mapping error )
#  Add Channel Data, ie: temperature, velocity, colour, etc if you wish
#  Density, resolution, and offset seems to be the least required data in caches...
#  ##  (or maybe Density aren't needed.   (Was not tested.)
#

#  Fixed bad resolution querys, expecially when velocity's fields are queried by mistake.
#  ** Disclaimer:
#  Free for all to use... thanks.  * Use at own risk as well *
#
#########################################################################################yy
#
#   Thanks to 1: https://github.com/westernx/mayatools/blob/master/docs/binary.rst
#   And       2: http://100cells.com/downloads/MayaCacheDLL/MayaCacheBitstreamDocumentation.pdf
#   Which help to dicpher much of the ncache fuild format (.mc)
#
#   Other help are from maya's  devkit directory for the .xml descriptor file
#   ie:   C:\Program Files\Autodesk\Maya2012\devkit\pythonScripts\*.py
#
#   TO DO:   Not implemented or test is the 'Single file ncache'  which is multiple frames'
#   caches are stored in one file.  
#   File formats are described in URL's 1: and 2: above.
#
#   Let me know if you found this useful.   Thanks!
#   
#   Note:   OpenVDB will probably make this script/converter obsolete
#   Houdini 12.5.xx has OpenVDB nodes already, but Maya 2013.5 has yet to have it yet.
#   Rumour as of September 2013, Maya suppose to have plug-in availabe 'later' this year.
#
#########################################################################################yy


import hou
import array
import sys,os


#def writeChannel(fileout,curChannelName,l_values,dataType,flag_convert_list=True,vector3Id=0,maya_name=""):
def writeChannel(fileout,curChannelName,l_values,dataType,vector3Id=0):

    ####################################
    # START Channel section
    # 28 + n bytes
    ####################################
    fileout.write("CHNM")
 
    # check if need to override name
    channelNameLen = len(curChannelName)
 
    padnum = 4-(channelNameLen%4)
    b = array.array('L',[channelNameLen+1 ])
    b.byteswap()
    # channel name length (4 bytes)
    # for now, max size is ff(hex) = 255
    fileout.write(B)
 
    # channel name (padded with modulus of 4)
    #     ie:  padding =4 -len(name)%4
    #for item in range(padnum):
    channelNamePadded=curChannelName+chr(0)*padnum
    fileout.write(channelNamePadded)
 
    #tag for channels's array size: "SIZE"
    fileout.write("SIZE")
 
    #arraysize data length(bytes)  4 bytes :"00 00 00 04"
    b = bytearray([0,0,0,4 ])
    fileout.write(B)
 
    #array size(number of elements) 4 bytes
    #b = bytearray([0,0,0,numElements ])
    #fout.write(B)
    #array size(4 bytes)
    #split array to 4 byte chunks
    num_elements = len(l_values)

    #
    b = array.array('L',[num_elements])
    b.byteswap()
    fileout.write(B)
 
 
    #
    #tag for datatype (4 bytes)  :  "DBLA","FVCA","DVCA","FBCA"
    # ************* Hardcoding toe FBCA for now...
    dataType = "FBCA"
    

    fileout.write(dataType)
 
    arraySize =0

    #####################################################################
    #  arraySize for follow (except for type : FBCA) is just a guess.  
    #  I leave it up to you to verify the results
    #####################################################################
    sizeVector=1
    # handle the case for vector field
    #if vector3Id !=0 :
    #    sizeVector=1
         
    if dataType =="DBLA":
         arraySize = num_elements * 4 * sizeVector
         arrayType = 'd'
    elif dataType =="FVCA":
         arraySize = num_elements * 4 * sizeVector
         arrayType = 'f'
    elif dataType =="DVCA":
         arraySize = num_elements * 4 * sizeVector
         arrayType = 'd'
    elif dataType =="FBCA":
         arraySize = num_elements * 4 * sizeVector
         arrayType = 'f'

    #####################################################################
    # write size of Data Value block
    #####################################################################
    b = array.array('L',[arraySize])
    b.byteswap()
    fileout.write(B)

    #############################################
    # THis is used ** if ** a list is to be passed in and used as-is when
    # outputted to file
    #############################################
    
         # for now, array's are of type 'f' or 'd', but vectors are not implemented yet
         # I'll leave this to the user to implement
#         if vector3Id == False:
             # write out single
#             b = array.array(arrayType,l_values)
#         else:
             # this is a vector float, so need to flatten this list.
    b = array.array(arrayType,l_values)
    b.byteswap()
    fileout.write(B)

 
 
    ################################
    ## END CHANNEL
    ################################
    numbytes = len(l_values)
    print "l_values:%d size:%d"%(numbytes,numbytes*4)

    return 28 + numbytes*4


def channelNameLenPadded(channelName):
    channelNameLen = len(channelName)
    padnum = 4-(channelNameLen%4)
    return channelNameLen + padnum

def normList(L,max_val = 0.0 ,bias=0.0, normalizeTo=1):
    '''normalize values of a list to make its max = normalizeTo'''

    # see if there's an max value for ceil specified...
    # if not, then find max value in this list, and normalize to that...
    if max_val == 0.0:
        vMax = max(L)
    else:
        vMax = max_val

    print "max value: ",vMax

    # prevent division by zero...
    if vMax == 0 :
        vMax = 1.0

    return [ (((x/vMax)*normalizeTo)+bias) for x in L]



def processVolume(l_curVolume,normalize=-1,norm_bias=0.0,normalizeTo=1.0,vector3Id=0):
    # get size
    (gSize_x,gSize_y,gSize_z) = l_curVolume[0].resolution()
    num_volumes = len(l_curVolume)
    print "processVolume: x: %d, y:%d, z:%d"%(gSize_x,gSize_y,gSize_z)
    # get values for current voxel
    # Should be of size:  (gSize_x * gSize_y * gSize_z)
    l_return=[0.0]*(gSize_x*gSize_y*gSize_z*num_volumes)
    l_return_max = gSize_x*gSize_y*gSize_z

    l_index =0
    for k in range(0,gSize_z):
        for j in range(0,gSize_y):
            for i in range(0,gSize_x):
                #cur_index =  + gSize_z*(j+(i*gSize_y))                
                for curVolume in l_curVolume:
                    l_return[l_index] = curVolume.voxel((i,j,k))
                    
                    up=15
                    lw=12
                    if (up>i)& (lw<i)& (up>j)& (lw<j) & (up>k)& (lw<k):
                        print "%d,%d,%d"%(i,j,k),":", type(curVolume.voxel((i,j,k)) ) ,":", l_return[l_index]

                    #print "i:%d j:%d k:%d index:%d >> val:"%(i,j,k,cur_index),l_return[cur_index]
                    l_index+=1
    # return normalize if specified
    if normalize!=-1:

        return normList(l_return,max_val=normalize,bias=norm_bias,normalizeTo=normalizeTo)

    return l_return

def processVector3Volume(l_curVolume,normalize=-1,norm_bias=0.0,normalizeTo=1.0,vector3Id=0,velocityTag = False):
    # get size
    (gSize_x,gSize_y,gSize_z) = l_curVolume[0].resolution()
    num_volumes = len(l_curVolume)

    (xr,yr,zr) = (gSize_x,gSize_y,gSize_z)

    #data_indices = (
            #i + (j * (xr + 1)) + (k * (xr + 1) *  yr     ),
            #i + (j *  xr     ) + (k *  xr      * (yr + 1)) + ((xr + 1) * yr * zr),
            #i + (j *  xr     ) + (k *  xr      *  yr     ) + ((xr + 1) * yr * zr) + (xr * (yr + 1) * zr),
    #)

    print "processVolume3d: x: %d, y:%d, z:%d"%(gSize_x,gSize_y,gSize_z)
    # get values for current voxel
    # Should be of size:  (gSize_x * gSize_y * gSize_z)
    
    if velocityTag :
        xcache_size = (gSize_x+1)*(gSize_y*gSize_z)
        ycache_size = (gSize_y+1)*(gSize_x*gSize_z)
        zcache_size = (gSize_z+1)*(gSize_x*gSize_y)
    else:
        xcache_size = (gSize_x)*(gSize_y*gSize_z)
        ycache_size = (gSize_y)*(gSize_x*gSize_z)
        zcache_size = (gSize_z)*(gSize_x*gSize_y)
    
    velvoxelsize_volumes = xcache_size + ycache_size + zcache_size
    print ">>>size:%d %d %d = %d"%(xcache_size,ycache_size, zcache_size,velvoxelsize_volumes)

    l_return=[0.0]*(velvoxelsize_volumes  )

    #l_return_max = (1+gSize_x)*(1+gSize_y)*(1+gSize_z)

    l_index =0
    for k in range(0,gSize_z):
        for j in range(0,gSize_y):
            for i in range(0,gSize_x):
                #cur_index =  + gSize_z*(j+(i*gSize_y))                
                #for curVolume in l_curVolume:
                    
                    if velocityTag:
                        data_indices = (
                            i + (j * (xr + 1)) + (k * (xr + 1) *  yr     ),
                            i + (j *  xr     ) + (k *  xr      * (yr + 1)) + ((xr + 1) * yr * zr),
                            i + (j *  xr     ) + (k *  xr      *  yr     ) + ((xr + 1) * yr * zr) + (xr * (yr + 1) * zr),
                        )
                    else:
                        data_indices = (
                            i + (j * (xr )) + (k * (xr ) *  yr     ),
                            i + (j *  xr     ) + (k *  xr      * (yr )) + ((xr ) * yr * zr),
                            i + (j *  xr     ) + (k *  xr      *  yr     ) + ((xr ) * yr * zr) + (xr * (yr ) * zr),
                        )
                    l_return[data_indices[0]] = l_curVolume[0].voxel((i,j,k))
                    l_return[data_indices[1]] = l_curVolume[1].voxel((i,j,k))
                    l_return[data_indices[2]] = l_curVolume[2].voxel((i,j,k))

                    x_l =data_indices[0]
                    y_l =data_indices[1]
                    z_l =data_indices[2]
                    
                    lw=0
                    up=2
                    if (up>i)& (lw<i)& (up>j)& (lw<j) & (up>k)& (lw<k):
                        print "%d,%d,%d"%(i,j,k),":", type(l_curVolume[0].voxel((i,j,k)) ) ,":%f,%f,%f"%(l_return[x_l],l_return[y_l],l_return[z_l])

                    #print "i:%d j:%d k:%d index:%d >> val:"%(i,j,k,cur_index),l_return[cur_index]
                    l_index+=1
    # return normalize if specified
    if normalize!=-1:

        return normList(l_return,max_val=normalize,bias=norm_bias,normalizeTo=normalizeTo)

    return l_return


####################################################
########################start#######################
####################################################



def writeFrames():

    this_node = hou.pwd()
    inputs = this_node.inputs()

    # Get the geometry from the second input
    # (first input=0, second input=1, third=2, etc.)
    first_input_geo = inputs[0].geometry()
    
    s = first_input_geo.sopNode()
    dop_str = s.path()

    print "Converting volume: ",dop_str
    #param 1
    #dop_str="/obj/import_pyro_build1/file1"
    #param 2
    startframe= this_node.parm("startFrame").evalAsInt()
    print "startFrame:",startframe

    #param 3
    endframe = this_node.parm("endFrame").evalAsInt()
    print "endFrame:",endframe

    #param 4
    #fluidPrefix= "fluidShape1"
    fluidPrefix = this_node.parm("fluidPrefix").evalAsString()
    print "fluid prefix:",fluidPrefix

    # create directory of output
    #param 5
    #outfilename_prefix="i:/maya/data/fireball7/fluidShape1Frame"
    outfilename_prefix = this_node.parm("outFile").evalAsString()
    if not os.path.exists(os.path.dirname(outfilename_prefix)):
        os.makedirs(os.path.dirname(outfilename_prefix))
        print "Creating directory:",os.path.dirname(outfilename_prefix)
    print "Output directory:",outfilename_prefix
    
    #param 6
    d_channel=  {}
    d_channel['density'] ={"index":0,"normalize":-1,"norm_bias":0.0, "maxClip":1.0, "vector3Id":0}    

    # if vector3Id = non-zero, then process channelnames = 'channelname.x','channelname.y','channelname.z' and dump into flatten vector array of x*y*z
    d_channel['vel'] ={"index":1,"normalize":-1,"norm_bias":0.0, "maxClip":1.0, "vector3Id":1, "override_name":"velocity"}
    d_channel['temperature'] ={"index":4,"normalize":-1,"norm_bias":0.0, "maxClip":1.0, "vector3Id":0}   

    #d_channel['heat'] = {"index":2,"normalize":-1,"norm_bias":0.0, "maxClip":1.0, "vector3Id":0}      
    #d_channel['fuel'] = {"index":2,"normalize":-1,"norm_bias":0.0, "maxClip":1.0, "vector3Id":0}    

    ###########>>>>>>>>>>>>>>>>>

    hou.setFrame(startframe)
    # get fluid object
    h=hou.node(dop_str)


    ### get initial size/rez

    densityVolume=h.geometry().prims()[0]

    # store init pos
    first_l_res = densityVolume.resolution()

    first_min_position = densityVolume.indexToPos((0,0,0))
    first_max_position = densityVolume.indexToPos(first_l_res)
    first_delta = first_max_position - first_min_position
    first_mid_point = hou.Vector3((first_delta[0]/2,first_delta[1]/2,first_delta[2]/2))
    first_mid_point += first_min_position
    print ">>>> ",type(first_mid_point)


    print "initial pos 1:",first_min_position
    print "initial pos 2:",first_max_position
      
    first_size_v = first_max_position-first_min_position
    print "initial size:",first_size_v
    #first_centre = hou.Vector3(first_size_v[0]/2.0,first_size_v[1]/2.0,first_size_v[2]/2.0 )
    first_centre=densityVolume.vertex(0).point().position()
    print "initial centre:",first_centre
    print "first calc'ed centre:",first_mid_point


    for curframe in range(startframe,endframe+1):

        # check for interupt.
        if hou.updateProgressAndCheckForInterrupt():
            break

        # increment the curframe
        hou.setFrame(curframe)
        h=hou.node(dop_str)

        #channel_lists
        # always include : mandatory channels:  resolution, offset




        # Construct file name
        outfilename = outfilename_prefix + "Frame%d.mc"%curframe
        print "outfile:",outfilename


        # get volume object,  initialize sum length:

        totalDataLen = 0

        for key in d_channel:
            maya_channelName = fluidPrefix+"_"+key

            override_name =""
            if d_channel[key].has_key('override_name'):
                override_name = d_channel[key]['override_name']
                if override_name != "":
                    maya_channelName = fluidPrefix+"_"+override_name

            d_channel[key]['maya_name']=maya_channelName
            prim_index = d_channel[key]['index']
            vector3Id = d_channel[key]['vector3Id']
            print "working on channel:",key,  "  index:",prim_index
            if vector3Id == 0 :
                d_channel[key]['volume_object']=[h.geometry().prims()[prim_index]  ]  
            else:
                print "getting vector volumes:",prim_index+1,prim_index+2
                d_channel[key]['volume_object']=[]
                d_channel[key]['volume_object'].append(h.geometry().prims()[prim_index]    )
                d_channel[key]['volume_object'].append(h.geometry().prims()[prim_index+1]    )
                d_channel[key]['volume_object'].append(h.geometry().prims()[prim_index+2]    )

            n_val=d_channel[key]["normalize"]
            nbias= d_channel[key]["norm_bias"]
            maxclip =d_channel[key]["maxClip" ]
            vector3Id =d_channel[key]["vector3Id" ]

            if vector3Id:
                if override_name == "velocity":
                    d_channel[key]['values_list'] =  processVector3Volume(d_channel[key]['volume_object'],normalize=n_val,norm_bias=nbias,normalizeTo=maxclip,vector3Id=vector3Id,velocityTag=True)
                else:
                    d_channel[key]['values_list'] =  processVector3Volume(d_channel[key]['volume_object'],normalize=n_val,norm_bias=nbias,normalizeTo=maxclip,vector3Id=vector3Id,velocityTag=False)
            else:
                d_channel[key]['values_list'] =  processVolume(d_channel[key]['volume_object'],normalize=n_val,norm_bias=nbias,normalizeTo=maxclip,vector3Id=vector3Id)
#                                            def processVolume(l_curVolume,normalize=-1,norm_bias=0.0,normalizeTo=1.0,vector3Id=0):
            d_channel[key]['PaddedNameLen'] = channelNameLenPadded(maya_channelName)
            totalDataLen += (28 + d_channel[key]['PaddedNameLen'] +(len(d_channel[key]['values_list'] )*4))

        #heatVolume=h.geometry().prims()[2]

        ##########################################
        # Get data values From source volume voxels
        ##########################################
        # get density values
        #l_densityValues = processVolume(densityVolume)

        # get resolution from first volume in list, but not velocity:
        l_res = None
        first_volume=None
        for key in d_channel.keys():
            if not key.startswith('vel'):
                first_volume=d_channel[key]['volume_object'][0]
                print "getting resolution from volume: ", key
        if first_volume ==None:
            print "Unable to get resolution from volumes."
            raise Exception("Unable to get resolution from volumes.  Need to be not 'vel.*', like 'density','temperature','heat','fuel'")

        l_res = first_volume.resolution()
        print "this l_res:", str (l_res)


        cur_centre=first_volume.vertex(0).point().position()

        array_resolutionValues = array.array('f',l_res)

        min_position = first_volume.indexToPos((0,0,0))
        max_position = first_volume.indexToPos(l_res)

        mid_point = max_position - min_position

        delta = max_position - min_position
        mid_point = hou.Vector3((delta[0]/2,delta[1]/2,delta[2]/2))
        mid_point += min_position


        print ">>>>",mid_point
    #    mid_point = mid_point /2

        size_v = max_position-min_position

        # set offset to [0,0,0]
        v_offset = mid_point - first_mid_point
        v_offset = first_centre = cur_centre
    #    v_offset=[0,0,0]
        print "current centre:",cur_centre
        
        array_offsetValues = array.array('f',v_offset)

        #############################################
        # open file to write converted fuild data
        #############################################
        fout = open (outfilename,"wb")

        ###############
        # header
        ###############

        #cache format
        fout.write("FOR4")

        #header data length (bytes)
        # value = 0x28
        b = bytearray([0,0,0,0x28])
        fout.write(B)

        #flag for cache verseion and cache version data length(bytes)
        fout.write("CACHVRSN")
        b = bytearray([0,0,0,0x04,0x30,0x2e,0x31,0x00])
        fout.write(B)

        #tag for start time #start time data length (bytes)
        fout.write("STIM")
        b = bytearray([0,0,0,0x04])
        fout.write(B)

        #current frame
        b = array.array('L',[curframe*250])
        b.byteswap()
        fout.write(B)

        #tag for end time and Endtime data length (bytes)
        fout.write("ETIM")
        b = bytearray([0,0,0,0x04])
        fout.write(B)

        #current frame**
        b = array.array('L',[curframe*250])
        b.byteswap()
        fout.write(B)

        #get padded length of channel names
        #densityPLen = channelNameLenPadded("fluidShape1_density")
        #temperaturePLen = channelNameLenPadded("fluidShape1_temperature")
        #fuelPLen = channelNameLenPadded("fluidShape1_fuel")

        resPLen = channelNameLenPadded("fluidShape1_resolution")
        offsetPLen = channelNameLenPadded("fluidShape1_offset")
      

        #calculate length of channel data block: density,resolution,offset
        totalDataLen +=  (28+ resPLen+(len(array_resolutionValues)*4)) \
                + (28+ offsetPLen+(len(array_offsetValues)*4))  \
                + len("MYCH")



        ###########################
        #  BLOCK HEADER  12+n bytes
        ###########################
        #cache format
        fout.write("FOR4")
      
        b = array.array('L',[totalDataLen])
        b.byteswap()
        fout.write(B)


        #tag for channels block
        fout.write("MYCH")

        ################################
        #  Channel start here
        ################################
        

        for key in d_channel:
        #  Write CHANNEL
            channelName = d_channel[key]['maya_name']
            dataType = "FBCA"
            l_values= d_channel[key]['values_list']
            vector3Id= d_channel[key]['vector3Id']
            maya_name= d_channel[key]['maya_name']
            writeChannel(fout,channelName,l_values,dataType,vector3Id)

        #  RESOLUTION CHANNEL
        channelName = "fluidShape1_resolution"
        dataType = "FBCA"
        vector3Id= 0
        writeChannel(fout,channelName,array_resolutionValues,dataType,vector3Id)

        # OFFSET CHANNEL
        channelName = "fluidShape1_offset"
        dataType = "FBCA"
        vector3Id= 0
        writeChannel(fout,channelName,array_offsetValues,dataType,vector3Id)


        # Close the file for this frame
        fout.close()

    ###############################################
    #  Author:  Markus Ng  
    #  mark.c.ng@gmail.com
    #  www.distill3d.com
    #  Date  :  Oct 3, 2013
    ###############################################

#def onCreated(node):
    #hou.ui.displayMessage("You created " + node.path())
    #startvalue= hou.playbar.playbackRange()[0]
    #endvalue= hou.playbar.playbackRange()[1]
    #node.setParms({"startFrame":startvalue})
    #node.setParms({"endFrame":int(endvalue)})

def onCreate(this_node):
    ## follow is for onCreate script of a otl node definition
    # get new created node
    this_node = kwargs['node']

    #set start and end values
    startvalue= hou.playbar.playbackRange()[0]
    endvalue= hou.playbar.playbackRange()[1]
    this_node.setParms({"startFrame":startvalue})
    this_node.setParms({"endFrame":int(endvalue)})

    this_node.setParms({"fluidPrefix":"fluidShape1_"})
    this_node.setParms({"outFile":"$HIP/"+"fluidShape1"})




import sys,os
import time


def writeMCC(outFileName,l_channelNames,start,end,sample_rate,res_x, res_y, res_z, dim_x,dim_y,dim_z, base_res =100,max_res=200 ):


    # optionally read from a separate text file
    #inFile = open ("I:/dev/mayafluid_template_xml.txt/","r")
    #text_buffer = inFile.read()

    # read from predefined in current source file (bottom of file)
    text_buffer = xmlTemplateData

    startmcc=str(start*sample_rate)
    endmcc=str(end*sample_rate)
    title="Generated from script"
    notes="Date:"+str(time.ctime())
    description = "Description: "
    shapename = "fluidShape1"


    text_buffer = text_buffer.replace("%START_MCC%",startmcc)
    text_buffer = text_buffer.replace("%END_MCC%",endmcc)
    text_buffer = text_buffer.replace("%SAMPLE_RATE%",str(sample_rate))
    text_buffer = text_buffer.replace("%CACHE_TITLE%",title)
    text_buffer = text_buffer.replace("%CACHE_NOTES%",notes)
    text_buffer = text_buffer.replace("%CACHE_DESCRIPTION%",description)
    text_buffer = text_buffer.replace("%CACHE_SHAPENAME%",shapename)
    text_buffer = text_buffer.replace("%VOLUME_DIM_X%",str(dim_x))
    text_buffer = text_buffer.replace("%VOLUME_DIM_Y%",str(dim_y))
    text_buffer = text_buffer.replace("%VOLUME_DIM_Z%",str(dim_z))
    text_buffer = text_buffer.replace("%VOLUME_RES_X%",str(res_x))
    text_buffer = text_buffer.replace("%VOLUME_RES_Y%",str(res_y))
    text_buffer = text_buffer.replace("%VOLUME_RES_Z%",str(res_z))
    text_buffer = text_buffer.replace("%MAX_RES%",str(max_res))
    text_buffer = text_buffer.replace("%BASE_RES%",str(base_res))


    #Add Channel block
    channelBlockText = ""
    channel_num =10

    for channel in l_channelNames:
        channel_template_string =   '<channel%CH_NUM% ChannelName="%CACHE_SHAPENAME%_%CHANNEL_NAME%" ChannelType="FloatArray" ChannelInterpretation="%CHANNEL_NAME%" SamplingType="Regular" SamplingRate="%SAMPLE_RATE%" StartTime="%START_MCC%" EndTime="%END_MCC%"/>'

        curText =channel_template_string.replace("%START_MCC%",startmcc)
        curText =curText.replace("%END_MCC%",endmcc)
        curText =curText.replace("%CACHE_SHAPENAME%",shapename)
        curText =curText.replace("%CHANNEL_NAME%",channel)
        curText =curText.replace("%CH_NUM%",str(channel_num))
        curText =curText.replace("%SAMPLE_RATE%",str(sample_rate))
        channelBlockText += curText +"\n"
        channel_num +=1

    text_buffer = text_buffer.replace("%CHANNEL_BLOCK%",channelBlockText)

    ########################
    # Writing to out file
    ########################
    outfile = open(outFileName,"w")
    outfile.write(text_buffer)

    ## check directory exists, and make it.
    dirpath = os.path.dirname(outFileName)
    if not os.path.exists(dirpath):
        os.makedirs(dirpath)

    print "writing file : ",outFileName
    outfile.close()


################## end of file ##################

#        channel_template_string="<channel0 ChannelName=\""\
#            + shapename+"_"+channel\
#            + " ChannelType=\"FloatArray\" ChannelInterpretation=\""\
#            + channel\
#            + "\" SamplingType=\"Regular\" SamplingRate=\"250\" StartTime=\""\
#            + startmcc + "\" EndTime=\""+endmcc+"\"/>"
#    <channel1 ChannelName="fluidShape1_temperature" ChannelType="FloatArray" ChannelInterpretation="temperature" SamplingType="Regular" SamplingRate="250" StartTime="250" EndTime="30000"/>
#    <channel2 ChannelName="fluidShape1_fuel" ChannelType="FloatArray" ChannelInterpretation="fuel" SamplingType="Regular" SamplingRate="250" StartTime="250" EndTime="30000"/>
#    <channel3 ChannelName="fluidShape1_resolution" ChannelType="FloatArray" ChannelInterpretation="resolution" SamplingType="Regular" SamplingRate="250" StartTime="250" EndTime="30000"/>
#    <channel4 ChannelName="fluidShape1_offset" ChannelType="FloatArray" ChannelInterpretation="offset" SamplingType="Regular" SamplingRate="250" StartTime="250" EndTime="30000"/>


###################
## Template data:
###################

xmlTemplateData= \
'''\
<?xml version="1.0"?>
<Autodesk_Cache_File>
  <cacheType Type="OneFilePerFrame" Format="mcc"/>
  <time Range="%START_MCC%-%END_MCC%"/>
  <cacheTimePerFrame TimePerFrame="%SAMPLE_RATE%"/>
  <cacheVersion Version="2.0"/>
  <extra>Houdini To Maya Script Generated cache file</extra>
  <extra>%CACHE_TITLE%</extra>
  <extra>%CACHE_NOTES%</extra>
  <extra>%CACHE_DESCRIPTION%</extra>
  <extra>%CACHE_SHAPENAME%</extra>
  <extra>Fluid Info for fluidShape1:</extra>
  <extra>fluidShape1.autoResize=1</extra>
  <extra>fluidShape1.dimensionsW=%VOLUME_DIM_X%</extra>
  <extra>fluidShape1.dimensionsH=%VOLUME_DIM_Y%</extra>
  <extra>fluidShape1.dimensionsD=%VOLUME_DIM_Z%</extra>
  <extra>fluidShape1.coordinateMethod=0</extra>
  <extra>fluidShape1.solverQuality=20</extra>
  <extra>fluidShape1.substeps=1</extra>
  <extra>fluidShape1.falloffMethod=0</extra>
  <extra>fluidShape1.densityMethod=2</extra>
  <extra>fluidShape1.velocityMethod=0</extra>
  <extra>fluidShape1.temperatureMethod=2</extra>
  <extra>fluidShape1.colorMethod=0</extra>
  <extra>fluidShape1.fuelMethod=2</extra>
  <extra>fluidShape1.startFrame=1</extra>
  <extra>fluidShape1.baseResolution=%BASE_RES%</extra>
  <extra>fluidShape1.resolutionW=%VOLUME_RES_X%</extra>
  <extra>fluidShape1.resolutionH=%VOLUME_RES_Y%</extra>
  <extra>fluidShape1.resolutionD=%VOLUME_RES_Z%</extra>
  <extra>fluidShape1.resizeClosedBoundaries=1</extra>
  <extra>fluidShape1.autoResizeThreshold=0.009999999776</extra>
  <extra>fluidShape1.maxResolution=%MAX_RES%</extra>
  <extra>fluidShape1.resizeToEmitter=1</extra>
  <extra>fluidShape1.resizeInSubsteps=1</extra>
  <extra>fluidShape1.autoResizeMargin=0</extra>
  <extra>fluidShape1.doFields=1</extra>
  <extra>fluidShape1.doEmission=1</extra>
  <extra>fluidShape1.inheritFactor=0</extra>
  <extra>fluidShape1.slices=2</extra>
  <extra>fluidShape1.voxelQuality=1</extra>
  <extra>fluidShape1.opacityPreviewGain=0.5</extra>
  <extra>fluidShape1.hardwareSelfShadow=1</extra>
  <extra>fluidShape1.selfShadow=1</extra>
  <extra>fluidShape1.gridInterpolator=0</extra>
  <extra>fluidShape1.solver=1</extra>
  <extra>fluidShape1.emitInSubsteps=0</extra>
  <extra>fluidShape1.highDetailSolve=0</extra>
  <extra>fluidShape1.enableLiquidSimulation=0</extra>
  <extra>fluidShape1.liquidMethod=1</extra>
  <extra>fluidShape1.liquidMinDensity=0.5</extra>
  <extra>fluidShape1.liquidMistFall=0</extra>
  <extra>fluidShape1.massRange=200</extra>
  <extra>fluidShape1.forwardAdvection=0</extra>
  <extra>fluidShape1.boundaryX=1</extra>
  <extra>fluidShape1.boundaryY=1</extra>
  <extra>fluidShape1.boundaryZ=1</extra>
  <extra>fluidShape1.massConversion=1</extra>
  <extra>fluidShape1.densityScale=0.5</extra>
  <extra>fluidShape1.densityDissipation=0</extra>
  <extra>fluidShape1.densityDiffusion=0</extra>
  <extra>fluidShape1.conserveMass=1</extra>
  <extra>fluidShape1.densityBuoyancy=1</extra>
  <extra>fluidShape1.densityGradientForce=0</extra>
  <extra>fluidShape1.densityTension=0</extra>
  <extra>fluidShape1.tensionForce=0</extra>
  <extra>fluidShape1.densityNoise=0</extra>
  <extra>fluidShape1.densityPressure=0</extra>
  <extra>fluidShape1.densityPressureThreshold=1</extra>
  <extra>fluidShape1.selfAttract=0.1000000015</extra>
  <extra>fluidShape1.selfRepel=0.1000000015</extra>
  <extra>fluidShape1.equilibriumValue=0.5</extra>
  <extra>fluidShape1.selfForceDistance=16</extra>
  <extra>fluidShape1.gravity=9.800000191</extra>
  <extra>fluidShape1.velocityScaleX=1</extra>
  <extra>fluidShape1.velocityScaleY=1</extra>
  <extra>fluidShape1.velocityScaleZ=1</extra>
  <extra>fluidShape1.viscosity=0</extra>
  <extra>fluidShape1.friction=0</extra>
  <extra>fluidShape1.velocitySwirl=0</extra>
  <extra>fluidShape1.velocityNoise=0</extra>
  <extra>fluidShape1.velocityDamp=0</extra>
  <extra>fluidShape1.velocityAdvect=1</extra>
  <extra>fluidShape1.velocityProject=1</extra>
  <extra>fluidShape1.turbulenceStrength=0</extra>
  <extra>fluidShape1.turbulenceFrequency=0.200000003</extra>
  <extra>fluidShape1.turbulenceSpeed=0.200000003</extra>
  <extra>fluidShape1.temperatureScale=1</extra>
  <extra>fluidShape1.temperatureDissipation=0.1</extra>
  <extra>fluidShape1.temperatureDiffusion=0.1</extra>
  <extra>fluidShape1.temperatureTurbulence=0.1000000015</extra>
  <extra>fluidShape1.temperatureNoise=0</extra>
  <extra>fluidShape1.temperaturePressure=0</extra>
  <extra>fluidShape1.temperaturePressureThreshold=0</extra>
  <extra>fluidShape1.buoyancy=3</extra>
  <extra>fluidShape1.temperatureTension=0</extra>
  <extra>fluidShape1.colorDissipation=0</extra>
  <extra>fluidShape1.colorDiffusion=0</extra>
  <extra>fluidShape1.fuelScale=1</extra>
  <extra>fluidShape1.reactionSpeed=0.05000000075</extra>
  <extra>fluidShape1.fuelIgnitionTemp=0</extra>
  <extra>fluidShape1.maxReactionTemp=1</extra>
  <extra>fluidShape1.airFuelRatio=0</extra>
  <extra>fluidShape1.heatReleased=1</extra>
  <extra>fluidShape1.lightReleased=0</extra>
  <extra>fluidShape1.lightColorR=1</extra>
  <extra>fluidShape1.lightColorG=1</extra>
  <extra>fluidShape1.lightColorB=1</extra>
  <extra>fluidShape1.matteOpacity=1</extra>
  <extra>fluidShape1.quality=1</extra>
  <extra>fluidShape1.renderInterpolator=1</extra>
  <extra>fluidShape1.color[0].color_Position=0</extra>
  <extra>fluidShape1.color[0].color_ColorR=1</extra>
  <extra>fluidShape1.color[0].color_ColorG=1</extra>
  <extra>fluidShape1.color[0].color_ColorB=1</extra>
  <extra>fluidShape1.color[0].color_Interp=1</extra>
  <extra>fluidShape1.colorInputBias=0</extra>
  <extra>fluidShape1.opacity[0].opacity_Position=0</extra>
  <extra>fluidShape1.opacity[0].opacity_FloatValue=0</extra>
  <extra>fluidShape1.opacity[0].opacity_Interp=1</extra>
  <extra>fluidShape1.opacity[1].opacity_Position=1</extra>
  <extra>fluidShape1.opacity[1].opacity_FloatValue=1</extra>
  <extra>fluidShape1.opacity[1].opacity_Interp=1</extra>
  <extra>fluidShape1.opacityInputBias=0</extra>
  <extra>fluidShape1.transparencyR=0.25</extra>
  <extra>fluidShape1.transparencyG=0.25</extra>
  <extra>fluidShape1.transparencyB=0.25</extra>
  <extra>fluidShape1.shadowOpacity=0.5</extra>
  <extra>fluidShape1.shadowDiffusion=0</extra>
  <extra>fluidShape1.lightBrightness=1</extra>
  <extra>fluidShape1.fluidLightColorR=1</extra>
  <extra>fluidShape1.fluidLightColorG=1</extra>
  <extra>fluidShape1.fluidLightColorB=1</extra>
  <extra>fluidShape1.ambientBrightness=0</extra>
  <extra>fluidShape1.ambientDiffusion=2</extra>
  <extra>fluidShape1.ambientColorR=0.5</extra>
  <extra>fluidShape1.ambientColorG=0.6999999881</extra>
  <extra>fluidShape1.ambientColorB=1</extra>
  <extra>fluidShape1.incandescence[0].incandescence_Position=0</extra>
  <extra>fluidShape1.incandescence[0].incandescence_ColorR=0</extra>
  <extra>fluidShape1.incandescence[0].incandescence_ColorG=0</extra>
  <extra>fluidShape1.incandescence[0].incandescence_ColorB=0</extra>
  <extra>fluidShape1.incandescence[0].incandescence_Interp=1</extra>
  <extra>fluidShape1.incandescence[1].incandescence_Position=0.8000000119</extra>
  <extra>fluidShape1.incandescence[1].incandescence_ColorR=0.8999999762</extra>
  <extra>fluidShape1.incandescence[1].incandescence_ColorG=0.200000003</extra>
  <extra>fluidShape1.incandescence[1].incandescence_ColorB=0</extra>
  <extra>fluidShape1.incandescence[1].incandescence_Interp=1</extra>
  <extra>fluidShape1.incandescence[2].incandescence_Position=1</extra>
  <extra>fluidShape1.incandescence[2].incandescence_ColorR=1.5</extra>
  <extra>fluidShape1.incandescence[2].incandescence_ColorG=1</extra>
  <extra>fluidShape1.incandescence[2].incandescence_ColorB=0</extra>
  <extra>fluidShape1.incandescence[2].incandescence_Interp=1</extra>
  <extra>fluidShape1.incandescenceInputBias=0</extra>
  <extra>fluidShape1.glowIntensity=0</extra>
  <extra>fluidShape1.specularColorR=0</extra>
  <extra>fluidShape1.specularColorG=0</extra>
  <extra>fluidShape1.specularColorB=0</extra>
  <extra>fluidShape1.cosinePower=20</extra>
  <extra>fluidShape1.environment[0].environment_Position=0</extra>
  <extra>fluidShape1.environment[0].environment_ColorR=0</extra>
  <extra>fluidShape1.environment[0].environment_ColorG=0</extra>
  <extra>fluidShape1.environment[0].environment_ColorB=0</extra>
  <extra>fluidShape1.environment[0].environment_Interp=1</extra>
  <extra>fluidShape1.edgeDropoff=0.05000000075</extra>
  <extra>fluidShape1.contrastTolerance=0.009999999776</extra>
  <extra>fluidShape1.surfaceThreshold=0.009999999776</extra>
  <extra>fluidShape1.surfaceTolerance=0.1000000015</extra>
  <extra>fluidShape1.meshResolution=2</extra>
  <extra>fluidShape1.refractiveIndex=1.799999952</extra>
  <extra>fluidShape1.pointLightX=0</extra>
  <extra>fluidShape1.pointLightY=0</extra>
  <extra>fluidShape1.pointLightZ=0</extra>
  <extra>fluidShape1.directionalLightX=0.5</extra>
  <extra>fluidShape1.directionalLightY=0.8000000119</extra>
  <extra>fluidShape1.directionalLightZ=0.5</extra>
  <extra>fluidShape1.textureType=0</extra>
  <extra>fluidShape1.colorTexture=0</extra>
  <extra>fluidShape1.colorTexGain=1</extra>
  <extra>fluidShape1.incandTexture=0</extra>
  <extra>fluidShape1.incandTexGain=1</extra>
  <extra>fluidShape1.opacityTexture=0</extra>
  <extra>fluidShape1.opacityTexGain=1</extra>
  <extra>fluidShape1.invertTexture=0</extra>
  <extra>fluidShape1.amplitude=1</extra>
  <extra>fluidShape1.ratio=0.7070000172</extra>
  <extra>fluidShape1.threshold=0</extra>
  <extra>fluidShape1.textureScaleX=1</extra>
  <extra>fluidShape1.textureScaleY=1</extra>
  <extra>fluidShape1.textureScaleZ=1</extra>
  <extra>fluidShape1.textureOriginX=0</extra>
  <extra>fluidShape1.textureOriginY=0</extra>
  <extra>fluidShape1.textureOriginZ=0</extra>
  <extra>fluidShape1.textureRotateX=0</extra>
  <extra>fluidShape1.textureRotateY=0</extra>
  <extra>fluidShape1.textureRotateZ=0</extra>
  <extra>fluidShape1.depthMax=2</extra>
  <extra>fluidShape1.frequency=1</extra>
  <extra>fluidShape1.frequencyRatio=2</extra>
  <extra>fluidShape1.inflection=0</extra>
  <extra>fluidShape1.textureTime=0</extra>
  <extra>fluidShape1.billowDensity=1</extra>
  <extra>fluidShape1.spottyness=0.1000000015</extra>
  <extra>fluidShape1.sizeRand=0</extra>
  <extra>fluidShape1.randomness=1</extra>
  <extra>fluidShape1.numWaves=5</extra>
  <extra>fluidShape1.implode=0</extra>
  <extra>fluidShape1.implodeCenterX=0</extra>
  <extra>fluidShape1.implodeCenterY=0</extra>
  <extra>fluidShape1.implodeCenterZ=0</extra>
  <extra>fluidShape1.focus=1</extra>
  <extra>fluidShape1.zoomFactor=1</extra>
  <extra>fluidShape1.escapeRadius=2</extra>
  <extra>fluidShape1.lobes=1</extra>
  <extra>fluidShape1.leafEffect=0</extra>
  <extra>fluidShape1.checker=0</extra>
  <extra>fluidShape1.lineBlending=0</extra>
  <extra>fluidShape1.lineFocus=0.5</extra>
  <extra>fluidShape1.points=0</extra>
  <extra>fluidShape1.stalksU=0</extra>
  <extra>fluidShape1.stalksV=0</extra>
  <extra>fluidShape1.circles=0</extra>
  <extra>fluidShape1.circleRadius=0.5</extra>
  <extra>fluidShape1.circleSizeRatio=1</extra>
  <extra>fluidShape1.lineOffsetU=0</extra>
  <extra>fluidShape1.lineOffsetV=0</extra>
  <extra>fluidShape1.lineOffsetRatio=1</extra>
  <extra>fluidShape1.juliaU=0</extra>
  <extra>fluidShape1.juliaV=0</extra>
  <extra>fluidShape1.boxRadius=1</extra>
  <extra>fluidShape1.boxMinRadius=0.5</extra>
  <extra>fluidShape1.boxRatio=-3</extra>
  <extra>fluidShape1.playFromCache=0</extra>
  <extra>fluidShape1.collide=1</extra>
  <extra>fluidShape1.surfaceShaderDepth=1</extra>
  <extra>fluidShape1.coordinateSpeed=0.200000003</extra>
  <Channels>
  %CHANNEL_BLOCK%</Channels>
</Autodesk_Cache_File>
'''


#############################################
#############################################
#  start main line
#############################################
#############################################

import sys
import getopt

def do_write_MCC():
    this_node = hou.pwd()
    inputs = this_node.inputs()

    # Get the geometry from the second input
    # (first input=0, second input=1, third=2, etc.)
    first_input_geo = inputs[0].geometry()
    print dir(first_input_geo)
    print "hello2:"
    print str(type(first_input_geo))
    s = first_input_geo.sopNode()
    print str(type(s))
    print "ahaha"
    print s.name()
    print this_node.path()

    print "xoxo"
    sample_rate = 250

    l_ch=['density','velocity','temperature','resolution','offset']
#    l_ch=d_channels.keys()
#    l_ch=['density','resolution','offset']
    print "blah"
    fluidPrefix = this_node.parm("fluidPrefix").evalAsString()
    print "fluid prefix, to be used as shape name:",fluidPrefix
#    shapeName = fluidPrefix

    outfilename_prefix = this_node.parm("outFile").evalAsString()
    if not os.path.exists(os.path.dirname(outfilename_prefix)):
        os.makedirs(os.path.dirname(outfilename_prefix))
        print "Creating directory:",os.path.dirname(outfilename_prefix)
    print "Output directory:",outfilename_prefix

    outpath= outfilename_prefix+".xml"


    this_node = hou.pwd()
    start= this_node.parm("startFrame").evalAsInt()
    print "startFrame:",start
    end = this_node.parm("endFrame").evalAsInt()
    print "endFrame:",end

    # store current playback frame
    curFrame = hou.frame()
    print "Current frame:",curFrame
    # goto first frame and get dimensions and size
    hou.setFrame(start)
    
    print type(s),">>>",dir(s.geometry().prims()[0])
    curVolume=s.geometry().prims()[0]

    # store init pos
    first_l_res = curVolume.resolution()
    (res_x,res_y,res_z) = curVolume.resolution()

    print "x: %d, y:%d, z:%d"%(res_x,res_y,res_z)
    
    first_min_position = curVolume.indexToPos((0,0,0))
    first_max_position = curVolume.indexToPos(first_l_res)
    [dim_x,dim_y,dim_z] = first_max_position-first_min_position
    print "size: %f, %f, %f"%(dim_x,dim_y,dim_z)
    

    # set base rez to 'y's' size
    base_res= dim_y
    writeMCC(outpath,l_ch,start,end, sample_rate, res_x, res_y, res_z, dim_x,dim_y,dim_z,base_res = 50, max_res=200 )
    print_maya_fluid_creation_script(res_x,res_y,res_z, dim_x,dim_y,dim_z)


    # set curFrame back to stored frame
    hou.setFrame(curFrame)



#    writeMCC(outpath,l_ch,start,end, sample_rate, res_x, res_y, res_z, dim_x,dim_y,dim_z,base_res = 50, max_res=200 )

#if __name__ == "__main__":
#    main()


def print_maya_fluid_creation_script(res_x,res_y,res_z, dim_x,dim_y,dim_z):

    mel_script = '''
string $cmdstr = "";
string $flname = "";
$cmdstr = "create3DFluid %d %d %d %f %f %f";
$flname = eval ($cmdstr);

$cmdstr = "setAttr \\""+$flname+".temperatureMethod\\" 2";
eval($cmdstr);
$cmdstr = "setAttr \\""+$flname+".densityMethod\\" 2";
eval($cmdstr);
$cmdstr = "setAttr \\""+$flname+".fuelMethod\\" 2";
eval($cmdstr);
    '''%(res_x,res_y,res_z, dim_x,dim_y,dim_z)
    print "// *********************************************************"
    print "// *********************************************************"
    print "// Run this in maya to create Fluid container:"
    print mel_script
    print "// End of script"
    print "// *********************************************************"
    print "// *********************************************************"



 

writeMCC.otl

writeMCC.002.otl

writeFluidMayaMCC.004.zip

Edited by moomarkus
Link to comment
Share on other sites

Actually, this is my first python script in Houdini....

I executed it in the "Python Source Editor". Is this the *Best* way to this? Or are there other ways to do this?

My initial approach was to use vex , and/or vex script in a custom ROP, but I couldn't wrap my head around how to write a file without open and closing a file for each voxel/particle/point (ie: if I used a volume wranger, or point wrangle node). <--- is this possible to do for my result?

THanks!

Please comment!

Markus

Link to comment
Share on other sites

Update on my current problem with this.

I have resize containers working now. The only problem is that when I bring into maya, the Volume container is self-centering, so it's shifting around. I'd like it grow from the base of the volume, but it's not.

One possible solution is to some how use the offset channel, but I'm not sure how to calculate that offset right now.

I suppose I can try:

In Houdini:

1. find base position (call it 'original positition) at start frame of sim. and get dimensions of volume

2. For each frame, get dimensions of volume and do a delta on 'original position' and calc offset(vector) : = delta(vector)/2

I'll post my findings later.

Link to comment
Share on other sites

  • 2 weeks later...

Yup, basically for now, I've regenerated an mcc xml in maya, then modify the values as needed by the channel / frame requirments of the file caches outputed from the first script. IE: use the maya mcc.xml as a template.

You can wrap that in a python function if it's too tedious. (Which is what I've done)

Link to comment
Share on other sites

I make xml in maya and laod this mc file , I got error report , saying my cache is empty or invalid , could you tell me why ? or maybe something wrong with xml , then could you tell me which value should I modify in xml file ? thank you .

Link to comment
Share on other sites

  • 2 weeks later...

HInt for velocity field in nFluid cache (mcc/mcx) :

- How is velocity laid out?

It is on the surfaces bettween voxels.

Test to set velocities:

for x in range(3):

for y in range(5):

for z in range(7):

cmds.setFluidAttr(at='density', xi=x, yi=y, zi=z, fv=(x + y + z))

cmds.setFluidAttr(at='velocity', xi=x, yi=y, zi=z, vv=(x + 1, y + 1, z + 1))

Duncan says:

Velocity is defined at voxel boundaries, not the centers (it takes a little thinking to appreciate this fully). Thus for a 10x10x10 grid the velocityX grid will be (11,10,10), the velocityY(10,11,10) and the velocityZ(10,10,11).

The API docs also clarify quite well:

http://download.auto...b82dbe223133529

Looking at the raw data set via cmds.setFluidAttr(..., lowerFace=True), the X values go (0, 1, 2, 2), the Y (0, 1, 2, 3, 4, 4),

and the Z do the same. They also iterate in the same Z,Y,X order that the density does. **1

References:

**1 : https://github.com/westernx/mayatools

http://download.auto...99211af08ae8d0c

http://blog3d.distill3d.com/

Edited by moomarkus
Link to comment
Share on other sites

  • 1 month later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...