145 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			145 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|  | import bpy | ||
|  | import lnx | ||
|  | import lnx.material.cycles as cycles | ||
|  | import lnx.material.make_shader as make_shader | ||
|  | import lnx.material.mat_state as mat_state | ||
|  | import lnx.utils as lnx_utils | ||
|  | 
 | ||
|  | if lnx.is_reload(__name__): | ||
|  |     cycles = lnx.reload_module(cycles) | ||
|  |     make_shader = lnx.reload_module(make_shader) | ||
|  |     mat_state = lnx.reload_module(mat_state) | ||
|  |     lnx.utils = lnx.reload_module(lnx.utils) | ||
|  | else: | ||
|  |     lnx.enable_reload(__name__) | ||
|  | 
 | ||
|  | # TODO: handle groups | ||
|  | # TODO: handle cached shaders | ||
|  | 
 | ||
|  | batchDict = None | ||
|  | signatureDict = None | ||
|  | 
 | ||
|  | def traverse_tree(node, sign): | ||
|  |     sign += node.type + '-' | ||
|  |     for inp in node.inputs: | ||
|  |         if inp.is_linked: | ||
|  |             sign = traverse_tree(inp.links[0].from_node, sign) | ||
|  |         else: | ||
|  |             sign += 'o' # Unconnected socket | ||
|  |     return sign | ||
|  | 
 | ||
|  | def get_signature(mat, object: bpy.types.Object): | ||
|  |     nodes = mat.node_tree.nodes | ||
|  |     output_node = cycles.node_by_type(nodes, 'OUTPUT_MATERIAL') | ||
|  | 
 | ||
|  |     if output_node != None: | ||
|  |         sign = traverse_tree(output_node, '') | ||
|  |         # Append flags | ||
|  |         sign += '1' if mat.lnx_cast_shadow else '0' | ||
|  |         sign += '1' if mat.lnx_ignore_irradiance else '0' | ||
|  |         if mat.lnx_two_sided: | ||
|  |             sign += '2' | ||
|  |         elif mat.lnx_cull_mode == 'Clockwise': | ||
|  |             sign += '1' | ||
|  |         else: | ||
|  |             sign += '0' | ||
|  |         sign += str(mat.lnx_material_id) | ||
|  |         sign += '1' if mat.lnx_depth_read else '0' | ||
|  |         sign += '1' if mat.lnx_overlay else '0' | ||
|  |         sign += '1' if mat.lnx_decal else '0' | ||
|  |         if mat.lnx_discard: | ||
|  |             sign += '1' | ||
|  |             sign += str(round(mat.lnx_discard_opacity, 2)) | ||
|  |             sign += str(round(mat.lnx_discard_opacity_shadows, 2)) | ||
|  |         else: | ||
|  |             sign += '000' | ||
|  |         sign += mat.lnx_custom_material if mat.lnx_custom_material != '' else '0' | ||
|  |         sign += mat.lnx_skip_context if mat.lnx_skip_context != '' else '0' | ||
|  |         sign += '1' if mat.lnx_particle_fade else '0' | ||
|  |         sign += mat.lnx_billboard | ||
|  |         sign += '_skin' if lnx.utils.export_bone_data(object) else '0' | ||
|  |         sign += '_morph' if lnx.utils.export_morph_targets(object) else '0' | ||
|  |         return sign | ||
|  | 
 | ||
|  | def traverse_tree2(node, ar): | ||
|  |     ar.append(node) | ||
|  |     for inp in node.inputs: | ||
|  |         inp.is_uniform = False | ||
|  |         if inp.is_linked: | ||
|  |             traverse_tree2(inp.links[0].from_node, ar) | ||
|  | 
 | ||
|  | def get_sorted(mat): | ||
|  |     nodes = mat.node_tree.nodes | ||
|  |     output_node = cycles.node_by_type(nodes, 'OUTPUT_MATERIAL') | ||
|  | 
 | ||
|  |     if output_node != None: | ||
|  |         ar = [] | ||
|  |         traverse_tree2(output_node, ar) | ||
|  |         return ar | ||
|  | 
 | ||
|  | def mark_uniforms(mats): | ||
|  |     ars = [] | ||
|  |     for m in mats: | ||
|  |         ars.append(get_sorted(m)) | ||
|  | 
 | ||
|  |     # Buckle up.. | ||
|  |     for i in range(0, len(ars[0])): # Traverse nodes | ||
|  |         for j in range(0, len(ars[0][i].inputs)): # Traverse inputs | ||
|  |             inp = ars[0][i].inputs[j] | ||
|  |             if not inp.is_linked and hasattr(inp, 'default_value'): | ||
|  |                 for k in range(1, len(ars)): # Compare default values | ||
|  |                     inp2 = ars[k][i].inputs[j] | ||
|  |                     diff = False | ||
|  |                     if str(type(inp.default_value)) == "<class 'bpy_prop_array'>": | ||
|  |                         for l in range(0, len(inp.default_value)): | ||
|  |                             if inp.default_value[l] != inp2.default_value[l]: | ||
|  |                                 diff = True | ||
|  |                                 break | ||
|  |                     elif inp.default_value != inp2.default_value: | ||
|  |                         diff = True | ||
|  |                     if diff: # Diff found | ||
|  |                         for ar in ars: | ||
|  |                             ar[i].inputs[j].is_uniform = True | ||
|  |                         break | ||
|  | 
 | ||
|  | def build(materialArray, mat_users, mat_lnxusers): | ||
|  |     global batchDict | ||
|  |     batchDict = dict() # Stores shader data for given material | ||
|  |     signatureDict = dict() # Stores materials for given signature | ||
|  | 
 | ||
|  |     # Update signatures | ||
|  |     for mat in materialArray: | ||
|  |         if mat.signature == '' or not mat.lnx_cached: | ||
|  |             mat.signature = get_signature(mat, mat_users[mat][0]) | ||
|  |         # Group signatures | ||
|  |         if mat.signature in signatureDict: | ||
|  |             signatureDict[mat.signature].append(mat) | ||
|  |         else: | ||
|  |             signatureDict[mat.signature] = [mat] | ||
|  | 
 | ||
|  |     # Mark different inputs | ||
|  |     for ref in signatureDict: | ||
|  |         mats = signatureDict[ref] | ||
|  |         if len(mats) > 1: | ||
|  |             mark_uniforms(mats) | ||
|  | 
 | ||
|  |     mat_state.batch = True | ||
|  | 
 | ||
|  |     # Build unique shaders | ||
|  |     for mat in materialArray: | ||
|  |         for mat2 in materialArray: | ||
|  |             # Signature not found - build it | ||
|  |             if mat == mat2: | ||
|  |                 batchDict[mat] = make_shader.build(mat, mat_users, mat_lnxusers) | ||
|  |                 break | ||
|  | 
 | ||
|  |             # Already batched | ||
|  |             if mat.signature == mat2.signature: | ||
|  |                 batchDict[mat] = batchDict[mat2] | ||
|  |                 break | ||
|  | 
 | ||
|  |     mat_state.batch = False | ||
|  | 
 | ||
|  | def get(mat): | ||
|  |     return batchDict[mat] |