def calculateFaceforwardNormal(geo_info, camera_location, obj_location): ''' calculate the face normal forward to the camera returned data is [normal, face_center, cam_position] in world space ''' if not geo_info: print 'Error, invalid geo_info' return [] vertex_list_local = geo_info[geo_info.keys()[0]]['vertex'] face_center_local = geo_info[geo_info.keys()[0]]['center'] # get object matrix obj_matrix = kcf.getWorldXform(obj_location) if not obj_matrix: print 'Error, failed to get world matrix at location: '+obj_location return [] # convert to world space vertex_list = [] face_center = [] obj_matrix = km.list_to_matrix(obj_matrix[0]) if not km.is_same_matrix(obj_matrix, km.matrix_identity(4)): for v in vertex_list_local: vertex_list.append( list(km.matrix_mul(v+[1], obj_matrix)[0][:-1]) ) face_center.extend( list(km.matrix_mul(face_center_local+[1], obj_matrix)[0][:-1]) ) else: vertex_list = vertex_list_local face_center = face_center_local # get the normal if len(vertex_list)<3: print 'Error, need at least three vertices to calculate the face normal' return [] v0 = km.subtract( vertex_list[0], vertex_list[1] ) v1 = km.subtract( vertex_list[1], vertex_list[2] ) normal = km.vector_unit( km.cross(v0, v1) ) # get the camera matrix cam_matrix = kcf.getWorldXform(camera_location) if not cam_matrix or not cam_matrix[0]: print 'Error, failed to get camera matrix' return [] cam_matrix = km.list_to_matrix(list(cam_matrix[0])) cam_position = [cam_matrix[3][0], cam_matrix[3][1], cam_matrix[3][2]] # get unit z from camera in world space cam_z = km.vector_unit( km.matrix_mul( km.list_to_matrix([0,0,1,0]), cam_matrix )[0][:-1] ) # test face forward if km.dot(cam_z, normal)<0: normal = -normal return [list(normal), face_center, cam_position]
def barndoorRmsToPxr(top=0.0, bottom=0.0, left=0.0, right=0.0, mode='expand', time=1.0): # this function convert barndoors attribute in RMS Light to parameters in Pxr light filter # note: the input angle should be greater than 0 and less than 90 degree # also, you should select the barn filter in scene graph of Katana to run this function # this function does changes the values of the selected nodes or scene graph location min_angle = 1.0 top = min_angle if top<min_angle else (90.0-min_angle if top>(90.0-min_angle) else top) bottom = min_angle if bottom<min_angle else (90.0-min_angle if bottom>(90.0-min_angle) else bottom) left = min_angle if left<min_angle else (90.0-min_angle if left>(90.0-min_angle) else left) right = min_angle if right<min_angle else (90.0-min_angle if right>(90.0-min_angle) else right) max_angle = max([top, bottom, left, right]) # get scene graph location of the selected light filters locations = kcf.getSelectedLocations() nodes = kcf.locationsToNodes(locations) for (l, n) in nodes.iteritems(): if not n: continue # get parent light matrix light_location = os.path.dirname(l) lgt_matrix = tf.list_to_matrix(kcf.getWorldXform(light_location)[0]) scale, shear, angles, trans, persp = tf.decompose_matrix(lgt_matrix) if scale[0]<=0 or scale[1]<=0 or scale[2]<=0: print 'Error: '+os.path.basename(light_location)+' has zero scale, ignored!' continue # calculate distance to the light, so that the angle between the light and filter meets the max angle dist_to_light_x = math.tan(max_angle/180.0*math.pi) * scale[0] dist_to_light_y = math.tan(max_angle/180.0*math.pi) * scale[1] dist_to_light = 0 if mode=='expand': dist_to_light = max(dist_to_light_x, dist_to_light_y) else: dist_to_light = min(dist_to_light_x, dist_to_light_y) dist_to_light = 1 # calculate refine shape of the barn door top_edge = (dist_to_light / math.tan(top/180.0*math.pi) - scale[1])/scale[1] bottom_edge = (dist_to_light / math.tan(bottom/180.0*math.pi) - scale[1])/scale[1] left_edge = (dist_to_light / math.tan(left/180.0*math.pi) - scale[0])/scale[0] right_edge = (dist_to_light / math.tan(right/180.0*math.pi) - scale[0])/scale[0] # let's set the parameters on the selected light filters # light filter is a group, so let's get their children and set the transform parameters light_create_nodes = [i for i in n.getChildren() if i.getType().lower()=='lightcreate'] if not light_create_nodes: print 'Error: failed to find lightCreate node in '+os.path.basename(l)+"'s children, ignored!" continue param_value_dict = {'transform.translate.x':[0, time], \ 'transform.translate.y':[0, time], \ 'transform.translate.z':[-dist_to_light, time]} kcf.setParameters(param_value_dict, light_create_nodes[0]) # set the refine edges of the barn door light_filter_nodes = [i for i in n.getChildren() if i.getType().lower()=='material'] if not light_create_nodes: print 'Error: failed to find lightFilter material in '+os.path.basename(l)+"'s children, ignored!" continue param_value_dict = {'top.value':[top_edge, time], 'bottom.value':[bottom_edge, time], \ 'left.value':[left_edge, time], 'right.value':[right_edge, time]} kcf.setParameters(param_value_dict, light_filter_nodes[0])
def frustumSelectionIterator(location=None, filter_type='component', fov_extend_h=0, fov_extend_v=0, \ cam_location='', inverse_selection=True, animation=False, step=5, \ nearD=0.1, farD=99999999, debug=False): ''' return the list of location within the frustum of camera. The filter_type should be component if the component represents the bounding box. we make this function iterator(generator), so the pyqt progress bar can benefit from the yield statment in order to know the progress of the running function. ''' root_producer = kcf.getRootProducer() sgv = kcf.getSceneGraphView() if not location: location = kcf.getSelectedLocations() if not location: yield 'Error: Please select a location in the scene graph to proceed!' return if not isinstance(location, list): location = [location] filter_type = filterTypeMap(filter_type) locations_inside_list = [] locations_outside_list = [] current_frame = NodegraphAPI.NodegraphGlobals.GetCurrentTime() start_frame = NodegraphAPI.NodegraphGlobals.GetInTime() end_frame = NodegraphAPI.NodegraphGlobals.GetOutTime() frames = range(start_frame, end_frame+1, step) if end_frame not in frames: frames.append(end_frame) if not animation: frames = [current_frame] progress = 0 progress_step = 100.0 / len(frames) for f in frames: yield 'frame' + str(f) + '\nGet camera matrix datas...' NodegraphAPI.NodegraphGlobals.SetCurrentTime(f) cam_data = getCameraDatas(cam_location) if fov_extend_h != 0 or fov_extend_v != 0: cam_data['fov_horizontal'] += fov_extend_h cam_data['fov_vertical'] += fov_extend_v cam_data['ratio'] = cam_data['fov_horizontal'] / cam_data['fov_vertical'] frustum = km.Frustum() frustum.nearD = nearD frustum.farD = farD frustum.setCamInternals(cam_data['fov_vertical'], cam_data['ratio']) frustum.setCamDef(cam_data['position'], cam_data['forward'], cam_data['up']) if debug: # put the sphere in the corner of frustum to visually see if we get # the correct frustum shape print(cam_data['ratio'], cam_data['fov_horizontal'], cam_data['fov_vertical']) nodes = kcf.getSelectedNodes() corners = [frustum.ntl, frustum.ntr, frustum.nbl, frustum.nbr, \ frustum.nc, frustum.fc] for i in range(6): if i > len(nodes) - 1: break kcf.setTransform(nodes[i], translate=list(corners[i]), scale=[10,10,10]) return sub_process = 0 sub_process_step = progress_step / 100.0 yield 'start to iterate scene graph locations...' for l in location: location_producer = root_producer.getProducerByPath(l) for i in kcf.sg_iteratorByType(location_producer, type_=filter_type, to_leaf=False): bounds = getBound(i) if type_ == 'light': bounds = [] # if the type is mesh light without bbox, or one of the rect, sphere, # disk light, we use center of point to decide the visibility in frustum # instead of bbox light_shader = i.getAttribute('material.prmanLightShader').getData() if not light_shader: # light without valid light shader, skip continue light_shader = light_shader[0].lower() if 'mesh' not in light_shader and 'rect' not in light_shader \ and 'sphere' not in light_shader and 'disk' not in light_shader: continue if 'mesh' in light_shader: # if it's mesh light, let's check the source geometry src = i.getAttribute('geometry.areaLightGeometrySource').getData() if src: bounds = getBound(root_producer.getProducerByPath(src[0])) isOutside = False if not bounds: # if bounding box info is invalid, we try to use xform instead world_xform = kcf.getWorldXform(i.getFullName()) if not world_xform: continue world_xform = world_xform[0] center = world_xform[-4:-1] if frustum.pointInFrustum(center) == frustum.status['outside']: isOutside = True else: aabox = km.AABox( bbox_list=bounds ) if frustum.boxInFrustum(aabox) == frustum.status['outside']: isOutside = True if isOutside: locations_outside_list.append(i.getFullName()) else: locations_inside_list.append(i.getFullName()) if sub_process < progress_step: sub_process += sub_process_step yield math.floor(progress + sub_process) progress += progress_step yield math.floor(progress) locations_inside_list = list(set(locations_inside_list)) locations_outside_list = list(set(locations_outside_list)) locations_outside_list = list(set(locations_outside_list).difference(locations_inside_list)) yield 'Completed!' if inverse_selection: yield locations_outside_list return yield locations_inside_list
def getBound(sg_location): ''' if there is no bound info on the current location, it will find recursivly the bound in the children location ''' if not sg_location: return [] location_producer = None if isinstance(sg_location, str): root_producer = kcf.getRootRroducer() location_producer = root_producer.getProducerByPath(sg_location) else: location_producer = sg_location bound_attr = location_producer.getAttribute('bound') if not bound_attr: # find the child bounds bounds_collect = [] bounds = [] for c in location_producer.iterChildren(): tmp = getBound(c) if tmp: bounds_collect.append(tmp) # calculate the combined bounds if bounds_collect: bounds = range(6) for i in range(0, 6, 2): bounds[i] = min([b[i] for b in bounds_collect]) for i in range(1, 6, 2): bounds[i] = max([b[i] for b in bounds_collect]) return bounds local_bound = location_producer.getAttribute('bound').getData() parent_matrix = kcf.getWorldXform(location_producer.getFullName()) if not parent_matrix: return local_bound parent_matrix = km.list_to_matrix(parent_matrix[0]) if km.is_same_matrix(parent_matrix, km.matrix_identity()): return local_bound # convert local bound to world space # caculate 8 corners of bounding box bmin = [local_bound[0], local_bound[2], local_bound[4]] bmax = [local_bound[1], local_bound[3], local_bound[5]] ftr = [bmax[0], bmax[1], bmax[2]] fbr = [bmax[0], bmin[1], bmax[2]] fbl = [bmin[0], bmin[1], bmax[2]] ftl = [bmin[0], bmax[1], bmax[2]] btr = [bmax[0], bmax[1], bmin[2]] bbr = [bmax[0], bmin[1], bmin[2]] bbl = [bmin[0], bmin[1], bmin[2]] btl = [bmin[0], bmax[1], bmin[2]] # convert to world space ftr = km.matrix_mul( km.list_to_matrix(ftr+[1]), parent_matrix )[0][:-1] fbr = km.matrix_mul( km.list_to_matrix(fbr+[1]), parent_matrix )[0][:-1] fbl = km.matrix_mul( km.list_to_matrix(fbl+[1]), parent_matrix )[0][:-1] ftl = km.matrix_mul( km.list_to_matrix(ftl+[1]), parent_matrix )[0][:-1] btr = km.matrix_mul( km.list_to_matrix(btr+[1]), parent_matrix )[0][:-1] bbr = km.matrix_mul( km.list_to_matrix(bbr+[1]), parent_matrix )[0][:-1] bbl = km.matrix_mul( km.list_to_matrix(bbl+[1]), parent_matrix )[0][:-1] btl = km.matrix_mul( km.list_to_matrix(btl+[1]), parent_matrix )[0][:-1] # recaculate bounding box bmin[0] = min(ftr[0], fbr[0], fbl[0], ftl[0], btr[0], bbr[0], bbl[0], btl[0]) bmin[1] = min(ftr[1], fbr[1], fbl[1], ftl[1], btr[1], bbr[1], bbl[1], btl[1]) bmin[2] = min(ftr[2], fbr[2], fbl[2], ftl[2], btr[2], bbr[2], bbl[2], btl[2]) bmax[0] = max(ftr[0], fbr[0], fbl[0], ftl[0], btr[0], bbr[0], bbl[0], btl[0]) bmax[1] = max(ftr[1], fbr[1], fbl[1], ftl[1], btr[1], bbr[1], bbl[1], btl[1]) bmax[2] = max(ftr[2], fbr[2], fbl[2], ftl[2], btr[2], bbr[2], bbl[2], btl[2]) return [bmin[0], bmax[0], bmin[1], bmax[1], bmin[2], bmax[2]]
def getDistance(a, b): # a and b should be the location string xforms = kcf.getWorldXform([a,b]) a_position = (xforms[0][-4], xforms[0][-3], xforms[0][-2]) b_position = (xforms[1][-4], xforms[1][-3], xforms[1][-2]) return math.sqrt(sum( (a_position - b_position)**2 for a_position, b_position in zip(a_position, b_position)))
def setSelectedLightAtReflectedPosition(normal, face_center, cam_position, \ invert_normal=False, time=0.0, print_log=False): ''' the normal should be the returned data from getSelectedFaceNormal ''' normal = np.array(normal) if invert_normal: normal = -normal face_center = np.array(face_center) cam_position = np.array(cam_position) cam_to_face_vector = face_center - cam_position reflect = km.vector_unit( km.vector_reflect(cam_to_face_vector, normal) ) if print_log: print 'reflect vector' print reflect root_producer = kcf.getRootProducer() # get selected light light_locations = kcf.getSelectedLocations() for l in light_locations: # if the selected item is light? location_producer = root_producer.getProducerByPath(l) if location_producer.getType().lower() != 'light': continue light_Node = kcf.locationsToNodes(l)[l] # get light world matrix matrix_world_light = kcf.getWorldXform(l) if not matrix_world_light: print light_Node.getName()+": Failed to get light's world matrix, ignored." continue matrix_world_light = km.list_to_matrix( matrix_world_light[0] ) if print_log: print 'light world matrix' print matrix_world_light # get light local matrix transform = kcf.getTransform(light_Node) if print_log: print 'light transform' print transform matrix_local_light = km.list_to_matrix( transform[light_Node]['matrix'] ) if print_log: print 'light local matrix' print matrix_local_light # get the intermediate matrix: M_i = M_light_local_inverse * M_world matrix_intermediate = km.matrix_mul( km.matrix_inverse(matrix_local_light), matrix_world_light ) if print_log: print 'intermediate matrix' print matrix_intermediate # compose the reflect world matrix distance_light_to_face = float( km.vector_norm(matrix_world_light[3][:-1] - face_center) ) if print_log: print 'light to face distance' print distance_light_to_face position = reflect * distance_light_to_face + face_center rotation = km.vector_to_rotation(-reflect) if print_log: print 'light new position' print position print 'light new rotation' print rotation print km.rad_to_deg(rotation) matrix_reflect = km.matrix_compose(scale=np.array([1,1,1]), angles=rotation, translate=position) if print_log: print 'world reflect matrix' print matrix_reflect # compute the new light local matrix: M_light = M_reflect * M_intermediate_inverse new_matrix_light = km.matrix_mul(matrix_reflect, km.matrix_inverse(matrix_intermediate)) if print_log: print 'new light local matrix' print new_matrix_light # then get the translate, rotation and scale components scale, shear, angles, translate, perspective = km.matrix_decompose(new_matrix_light) angles = km.rad_to_deg(angles) print (light_Node.getName()+', target transform: \ntranslate: [%f, %f, %f]\n'+\ 'rotate: [%f, %f, %f]\nscale: [%f, %f, %f]')%(translate[0], translate[1], translate[2],\ angles[0], angles[1], angles[2], scale[0], scale[1], scale[2]) # let's move the light! kcf.setTransform(light_Node, translate=list(translate), rotate=list(angles), \ scale=list(scale), rotation_order='XYZ') # change the center of interest at the face center kcf.setParameters({'centerOfInterest':distance_light_to_face}, light_Node)