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