Jump to content

Clarisse Work Flow


Atom

Recommended Posts

Hi All,

 

I have been trying out isotropix Clarisse iFX 2.0 program for rendering object sequences. I have a Houdini scene that is using a Camera Switcher to switch views throughout the animation. Clarisse can not swap cameras between frames AFAIK. But you are free to animate any Clarisse camera as you like.

 

This python script examines a Houdini camera switcher node and generates a single animated Clarisse camera, in the form of a Clarisse Python script that you execute inside of Clarrise, with transformations applied from all of the Houdini switched cameras over time. The end result is that you have a single animated camera in Clarisse that matches all your camera moves from the camera switcher in Houdini.

 

 

# This Script Exports a series of switched Houdini cameras
# as a single Clarisse iFX 2.0 SP7 animated advanced perspective camera.
# (c) 2016 Atom.
#
#   USAGE:
#   Set your cam_names list to the same names merged into the Houdini camera switcher.
#   Set the file_name_out to match a folder on your system with write permissions.
#   In Clarisse open the Python Editor and load the generated script.
#   Execute the RunScript

# Master path
master_path = "/obj/"

# Camera names from inside the switcher, change to match order found in your node.
cam_names = ["cam1","cam2","cam3","cam4","cam5"]

# Switcher
node_path = "%s%s" % (master_path,"switcher1")
switcher = hou.node(node_path)

# Initial camera.
cur_cam_index = switcher.parm("camswitch").eval()
cur_cam_name = cam_names[cur_cam_index]
cam_path = "%s%s" % (master_path,cur_cam_name)
n = hou.node(cam_path)
camera_name = 'houdini_camera'  #% n.name()

# Houdini camera properties.
fl = n.parm("focal").eval()
fu = n.parm("focalunits").eval()
ap = n.parm("aperture").eval()
nc = n.parm("near").eval()
fc = n.parm("far").eval()
ic = n.parm("iconscale").eval()
rx = n.parm("resx").eval()
ry = n.parm("resy").eval()
pr = n.parm("aspect").eval()
sh = n.parm("shutter").eval()
fd = n.parm("focus").eval()
st = n.parm("fstop").eval()

sensorhorizontal = fl / 1000                                            #mm to meters.
sensorvertical = float(ry)/rx*sensorhorizontal

# Framerange.
frame_start = hou.playbar.playbackRange()[0]
frame_end   = hou.playbar.playbackRange()[1]

# Creates Python File for Clarisse API.
file_name_out = r"C:\Users\Developer\Desktop\houdini_2_clarisse_camera.py"
file = open(file_name_out, 'w')

# Python File Header for Clarisse
file.writelines('# Creates a Clarisse Camera from Houdini Camera: ' + str(node_path) + '\n')
file.writelines('# Load this script in the Clarisse Script Editor and Run it\n')
file.writelines('# License Type: Public Domain\n')
file.writelines('__Author__ = "Atom"\n')
file.writelines('__Version__ = 0.1\n\n')
#file.writelines('import math\n')

# Make a camera object and link it to the scene.
file.writelines('oScene = ix.application.get_builtin_commands() \n')
file.writelines('oScene.set_current_frame_range(%s, %s) \n' %(frame_start, frame_end))
file.writelines('newCamera = ix.create_object(\'%s\', \'CameraPerspectiveAdvanced\') \n' % camera_name)
file.writelines('newCamera.attrs.horizontal_aperture = %f \n' % sensorhorizontal)
file.writelines('newCamera.attrs.vertical_aperture = %f \n' % sensorvertical)
file.writelines('#newCamera.attrs.field_of_view = %f \n' %fl)
file.writelines('#newCamera.attrs.focus_distance = %f \n' % fd)
file.writelines('#newCamera.attrs.f_stop = %f \n' % st)
file.writelines('#newCamera.attrs.enable_dof = True \n\n')

# In my setup I have a python node setting a new index on the switcher every frame.
yes_i_am_using_python_to_control_my_switcher = False         # Set to False for typical animated camera switch index.
last_cam_index = -1
frame = frame_start
# loop through timeline frames.
while frame <= frame_end:

    # Houdini set Frame and getting Transition / Rotation
    hou.setFrame(frame)

    if yes_i_am_using_python_to_control_my_switcher:
        # Force python camera switching script to run.
        node_path = "%s%s" % (master_path,"python_camera_switcher")
        pcs = hou.node(node_path)
        pcs.cook()                                          # Force python script to run.
    else:
        # Most cases should end up here.
        # The switcher index is typically animated thus updated when the frame change.
        pass

    # Get the current camera index from the switcher.
    cur_cam_index = switcher.parm("camswitch").evalAtFrame(frame)
    cur_cam_name = cam_names[cur_cam_index]
    cam_path = "%s%s" % (master_path,cur_cam_name)
    local_node = hou.node(cam_path)
    
    if last_cam_index != cur_cam_index:
        file.writelines('\n# Switching to camera [%s] on frame #%d.\n' % (cur_cam_name,frame))    
        print "Switching to camera [%s] on frame #%d" % (cur_cam_name,frame)

    file.writelines('ix.set_current_frame(%s) \n' % frame)
    
    #Atom's coordinate transfer attempt.
    mtx = local_node.worldTransform()
    pos=mtx.extractTranslates()
    rot=mtx.extractRotates()
    
    file.writelines('newCamera.attrs.rotate[0]=%f \n' % rot[0])
    file.writelines('newCamera.attrs.rotate[1]=%f \n' % rot[1])
    file.writelines('newCamera.attrs.rotate[2]=%f \n' % rot[2])
    file.writelines('for i in range(0,3): \n')
    file.writelines('   oScene.set_key("%s.rotate[%s]" % (newCamera,i))')
    file.writelines('\n')
    
    file.writelines('newCamera.attrs.translate[0]=%f \n' % pos[0])
    file.writelines('newCamera.attrs.translate[1]=%f \n' % pos[1])
    file.writelines('newCamera.attrs.translate[2]=%f \n' % pos[2])
    file.writelines('for i in range(0,3): \n')
    file.writelines('   oScene.set_key("%s.translate[%s]" %(newCamera,i))')
    file.writelines('\n')
    
    file.writelines('\n')

    frame += 1
    last_cam_index = cur_cam_index

file.close()
hou.setFrame(1)
Edited by Atom
Link to comment
Share on other sites

is this wise?

how do you handle motion blur in the case of a camera moving to another location in the space of 1 frame?

That was my immediate response as well.

If Clarisse supports keyframes on subframes this issue could be partially solved by setting two keys as close as possible right in the middle of the two frames where the camera switches.

Even better if Clarisse would support the breaking of values for a single keyframe (like Houdini does) so you could set a different value for the incoming and outgoing segment.

This would work for any shutter setting up to half a frame before and after the current frame. You should not really need any more than that other than for creative purposes.

-dennis

Link to comment
Share on other sites

Hmm.. I had not even considered motion blur. But Clarisse renders so fast that if I end up with an extremely blurred out frame during a camera switch I can always render that frame again with motion blur off. I am, after all, doing a cut at that point so losing a frame is not a big show stopper for me.

 

I wrote another python script to "fix-up" Houdini's OBJ output for Clarisse. By default, the Houdini OBJ exporter, exports all vertices inside a single group then attempts to map faces into other groups from that master pool of vertices. Clarisse expects a set of vertices to be paired with the faces that make up that group. This is how Blender's OBJ exporter works as well.

 

While geometry transfers fine from Houdini to Clarisse, the Clarise OBJ file, has only a single group and thus can only hold a single material.

 

Place the script at this link inside the Post-Write event of a ROP Output Driver or File Cache node. It re-writes the OBJ file so that there is a usemtl tag written after every group definition in the OBJ file. This is how Clarisse detects materials for groups. The end result is that by re-writing the OBJ file your final OBJ will have a Clarisse material slot created for each group in the OBJ. This allows you to re-materialize your OBJ inside of Clarisse. It works well with OBJ sequences too.

Edited by Atom
  • Thanks 1
Link to comment
Share on other sites

  • 3 weeks later...

I put together a very simple OBJect exporter from Houdini 15 to Clarisse 2.0 SP7.

The code scans the Houdini scene for geo based objects and then looks inside the geo SOP context for the final node with the display flag set. The code then issues a saveToFile for that node, generating an OBJ file on the disk that Clarisse can read. Transformations are exported from the object level as well. Transformations defined within the SOP context are not transferred at this time. A material is also generated and linked to the object with a magenta color. The camera is also exported, it is assumed to be named cam1.
 
If you would like to try and export a simple Houdini geo based non-animated scene to Clarisse 2.0 SP7, place the attached code into a Houdini Python Shelf Tool and click the button. A python file, conforming to the Clarisse API, will appear on your desktop. Run this python file inside the Clarisse Script Editor.

 

Update: 03/20/2016:

 I have added material detection of diffuse color and some light type exports. This code can detect diffuse color from the Object level material assignment for Mantra Surface, Principled Shader, FBX Shader and Mantra Surface Builder. Light types supported are Point, Spotlight, Area (both disk and square), Distant, Sun and Environment Ambient Occlusion.

 

Update: 03/21/2016:

I have embedded the OBJ re-write code, mentioned above, into the export process. This way the correct number of material slots will appear in Clarisse when the resulting OBJ file is imported. I have added support for Geometry nodes inside a subnet. Now you can export an imported FBX or the default characters. The script generates a new folder structure on the desktop of the currently logged in user. (for non-english desktop names you may have to alter the code to point to the name of your desktop ~line #313) All generated files are placed inside this new folder named Houdini2Clarisse. I have added a new feature that will scan the subnet of a geo type object for any material nodes. When detected, material nodes are generated and linked to current object.
 

Update: 02/22/2016:

I have added the detection of diffuse and opacity maps for the Mantra Surface and the Principled Shader. When detected these generate Map File items in Clarisse and link them to the material they are associated with.

I am attaching an example scene that I have been testing with.
Houdini:
post-12295-0-80357100-1458416348_thumb.j
Clarisse:
post-12295-0-95768700-1458416341_thumb.j

ap_clarisse_export_test_scene_1a.hipnc

clarisse_exporter_032216.zip

Edited by Atom
  • Thanks 1
Link to comment
Share on other sites

  • 3 months later...

I may not be 100% understanding what you're trying to do, but would it be plausible to instead of moving the camera to instead create individual cameras at the location of the Houdini camera on the first frame after the switch and have the script also create a new Clarisse Image with it's 3D layer referencing to the corresponding camera and frame output range corresponding to the switch node?

Is there any way to write custom data into alembic cameras? If so, then you could just assign an attribute to the camera that reflects camera order and then use Clarisse's API to generate the switch. Otherwise you could just use a naming convention.

If you can manage all this, a camera switcher script within Clarisse wouldn't be that far off either and would probably be a better place to start. Let me know if I'm not being clear.

Edited by shawn_kearney
Link to comment
Share on other sites

Ah, but you assume that Clarisse has the ability to run code when the frame changes, which it sadly does not. I have asked Isotropix about that feature and while they acknowledge it would be useful there are no plans to include that anytime soon, even with the upcoming 3.0 release. So any scripting that you do inside Clarisse has to be a one-shot generator.

Also if you switch between cameras inside of Clarisse you have to setup a render output for each camera as well. By actually creating a single animated camera that passes through the various switched cameras you only have to setup a single output filename. But beware of the motion blur issue mentioned above.

I wrote this script while i was still using Apprentice, which does not have Alembic output support. Now that I have Houdini Indie I would probably just kick out an Alembic and be done with it. Clarisse has a robust Alembic support.

  • Thanks 1
Link to comment
Share on other sites

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...