def create_world_shaders(world: bpy.types.World): """Creates fragment and vertex shaders for the given world.""" global shader_datas world_name = arm.utils.safestr(world.name) pass_name = 'World_' + world_name shader_props = { 'name': world_name, 'depth_write': False, 'compare_mode': 'less', 'cull_mode': 'clockwise', 'color_attachments': ['_HDR'], 'vertex_elements': [{'name': 'pos', 'data': 'float3'}, {'name': 'nor', 'data': 'float3'}] } shader_data = {'name': world_name + '_data', 'contexts': [shader_props]} # ShaderContext expects a material, but using a world also works shader_context = ShaderContext(world, shader_data, shader_props) vert = shader_context.make_vert(custom_name="World_" + world_name) frag = shader_context.make_frag(custom_name="World_" + world_name) # Update name, make_vert() and make_frag() above need another name # to work shader_context.data['name'] = pass_name vert.add_out('vec3 normal') vert.add_uniform('mat4 SMVP', link="_skydomeMatrix") frag.add_include('compiled.inc') frag.add_in('vec3 normal') frag.add_out('vec4 fragColor') frag.write_attrib('vec3 n = normalize(normal);') vert.write('''normal = nor; vec4 position = SMVP * vec4(pos, 1.0); gl_Position = vec4(position);''') build_node_tree(world, frag, vert, shader_context) # TODO: Rework shader export so that it doesn't depend on materials # to prevent workaround code like this rel_path = os.path.join(arm.utils.build_dir(), 'compiled', 'Shaders') full_path = os.path.join(arm.utils.get_fp(), rel_path) if not os.path.exists(full_path): os.makedirs(full_path) # Output: World_[world_name].[frag/vert].glsl make_shader.write_shader(rel_path, shader_context.vert, 'vert', world_name, 'World') make_shader.write_shader(rel_path, shader_context.frag, 'frag', world_name, 'World') # Write shader data file shader_data_file = pass_name + '_data.arm' arm.utils.write_arm(os.path.join(full_path, shader_data_file), {'contexts': [shader_context.data]}) shader_data_path = os.path.join(arm.utils.get_fp_build(), 'compiled', 'Shaders', shader_data_file) assets.add_shader_data(shader_data_path) assets.add_shader_pass(pass_name) assets.shader_passes_assets[pass_name] = shader_context.data shader_datas.append({'contexts': [shader_context.data], 'name': pass_name})
def write_norpos(con_mesh: shader.ShaderContext, vert: shader.Shader, declare=False, write_nor=True): is_bone = con_mesh.is_elem('bone') if is_bone: make_skin.skin_pos(vert) if write_nor: prep = 'vec3 ' if declare else '' if is_bone: make_skin.skin_nor(vert, prep) else: vert.write_attrib(prep + 'wnormal = normalize(N * vec3(nor.xy, pos.w));') if con_mesh.is_elem('ipos'): make_inst.inst_pos(con_mesh, vert)
def write_norpos(con_mesh: ShaderContext, vert: Shader, declare=False, write_nor=True): is_bone = con_mesh.is_elem('bone') is_morph = con_mesh.is_elem('morph') if is_morph: make_morph_target.morph_pos(vert) if is_bone: make_skin.skin_pos(vert) if write_nor: prep = 'vec3 ' if declare else '' if is_morph: make_morph_target.morph_nor(vert, is_bone, prep) if is_bone: make_skin.skin_nor(vert, is_morph, prep) if not is_morph and not is_bone: vert.write_attrib(prep + 'wnormal = normalize(N * vec3(nor.xy, pos.w));') if con_mesh.is_elem('ipos'): make_inst.inst_pos(con_mesh, vert)
def write_tex_coords(con_mesh: ShaderContext, vert: Shader, frag: Shader, tese: Optional[Shader]): rpdat = arm.utils.get_rp() if con_mesh.is_elem('tex'): vert.add_out('vec2 texCoord') vert.add_uniform('float texUnpack', link='_texUnpack') if mat_state.material.arm_tilesheet_flag: if mat_state.material.arm_particle_flag and rpdat.arm_particles == 'On': make_particle.write_tilesheet(vert) else: vert.add_uniform('vec2 tilesheetOffset', '_tilesheetOffset') vert.write_attrib( 'texCoord = tex * texUnpack + tilesheetOffset;') else: vert.write_attrib('texCoord = tex * texUnpack;') if tese is not None: tese.write_pre = True make_tess.interpolate(tese, 'texCoord', 2, declare_out=frag.contains('texCoord')) tese.write_pre = False if con_mesh.is_elem('tex1'): vert.add_out('vec2 texCoord1') vert.add_uniform('float texUnpack', link='_texUnpack') vert.write_attrib('texCoord1 = tex1 * texUnpack;') if tese is not None: tese.write_pre = True make_tess.interpolate(tese, 'texCoord1', 2, declare_out=frag.contains('texCoord1')) tese.write_pre = False
def make(con_mesh: ShaderContext): vert = con_mesh.vert frag = con_mesh.frag geom = con_mesh.geom tesc = con_mesh.tesc tese = con_mesh.tese # Additional values referenced in cycles # TODO: enable from cycles.py if frag.contains('dotNV') and not frag.contains('float dotNV'): frag.write_init('float dotNV = max(dot(n, vVec), 0.0);') # n is not always defined yet (in some shadowmap shaders e.g.) if not frag.contains('vec3 n'): vert.add_out('vec3 wnormal') vert.add_uniform('mat3 N', '_normalMatrix') vert.write_attrib('wnormal = normalize(N * vec3(nor.xy, pos.w));') frag.write_attrib('vec3 n = normalize(wnormal);') # If not yet added, add nor vertex data vertex_elems = con_mesh.data['vertex_elements'] has_normals = False for elem in vertex_elems: if elem['name'] == 'nor': has_normals = True break if not has_normals: vertex_elems.append({'name': 'nor', 'data': 'short2norm'}) write_wpos = False if frag.contains('vVec') and not frag.contains('vec3 vVec'): if tese is not None: tese.add_out('vec3 eyeDir') tese.add_uniform('vec3 eye', '_cameraPosition') tese.write('eyeDir = eye - wposition;') else: if not vert.contains('wposition'): write_wpos = True vert.add_out('vec3 eyeDir') vert.add_uniform('vec3 eye', '_cameraPosition') vert.write('eyeDir = eye - wposition;') frag.write_attrib('vec3 vVec = normalize(eyeDir);') export_wpos = False if frag.contains('wposition') and not frag.contains('vec3 wposition'): export_wpos = True if tese is not None: export_wpos = True if vert.contains('wposition'): write_wpos = True if export_wpos: vert.add_uniform('mat4 W', '_worldMatrix') vert.add_out('vec3 wposition') vert.write_attrib('wposition = vec4(W * spos).xyz;') elif write_wpos: vert.add_uniform('mat4 W', '_worldMatrix') vert.write_attrib('vec3 wposition = vec4(W * spos).xyz;') frag_mpos = ( frag.contains('mposition') and not frag.contains('vec3 mposition')) or vert.contains('mposition') if frag_mpos: vert.add_out('vec3 mposition') vert.add_uniform('float posUnpack', link='_posUnpack') vert.write_attrib('mposition = spos.xyz * posUnpack;') if tese is not None: if frag_mpos: make_tess.interpolate(tese, 'mposition', 3, declare_out=True) elif tese.contains( 'mposition') and not tese.contains('vec3 mposition'): vert.add_out('vec3 mposition') vert.write_pre = True vert.add_uniform('float posUnpack', link='_posUnpack') vert.write('mposition = spos.xyz * posUnpack;') vert.write_pre = False make_tess.interpolate(tese, 'mposition', 3, declare_out=False) frag_bpos = ( frag.contains('bposition') and not frag.contains('vec3 bposition')) or vert.contains('bposition') if frag_bpos: vert.add_out('vec3 bposition') vert.add_uniform('vec3 dim', link='_dim') vert.add_uniform('vec3 hdim', link='_halfDim') vert.add_uniform('float posUnpack', link='_posUnpack') vert.write_attrib('bposition = (spos.xyz * posUnpack + hdim) / dim;') vert.write_attrib('if (dim.z == 0) bposition.z = 0;') vert.write_attrib('if (dim.y == 0) bposition.y = 0;') vert.write_attrib('if (dim.x == 0) bposition.x = 0;') if tese is not None: if frag_bpos: make_tess.interpolate(tese, 'bposition', 3, declare_out=True) elif tese.contains( 'bposition') and not tese.contains('vec3 bposition'): vert.add_out('vec3 bposition') vert.add_uniform('vec3 dim', link='_dim') vert.add_uniform('vec3 hdim', link='_halfDim') vert.add_uniform('float posUnpack', link='_posUnpack') vert.write_attrib( 'bposition = (spos.xyz * posUnpack + hdim) / dim;') make_tess.interpolate(tese, 'bposition', 3, declare_out=False) frag_wtan = ( frag.contains('wtangent') and not frag.contains('vec3 wtangent')) or vert.contains('wtangent') if frag_wtan: # Indicate we want tang attrib in finalizer to prevent TBN generation con_mesh.add_elem('tex', 'short2norm') con_mesh.add_elem('tang', 'short4norm') vert.add_out('vec3 wtangent') vert.write_pre = True vert.write('wtangent = normalize(N * tang.xyz);') vert.write_pre = False if tese is not None: if frag_wtan: make_tess.interpolate(tese, 'wtangent', 3, declare_out=True) elif tese.contains('wtangent') and not tese.contains('vec3 wtangent'): vert.add_out('vec3 wtangent') vert.write_pre = True vert.write('wtangent = normalize(N * tang.xyz);') vert.write_pre = False make_tess.interpolate(tese, 'wtangent', 3, declare_out=False) if frag.contains('vVecCam'): vert.add_out('vec3 eyeDirCam') vert.add_uniform('mat4 WV', '_worldViewMatrix') vert.write('eyeDirCam = vec4(WV * spos).xyz; eyeDirCam.z *= -1;') frag.write_attrib('vec3 vVecCam = normalize(eyeDirCam);') if frag.contains('nAttr'): vert.add_out('vec3 nAttr') vert.write_attrib('nAttr = vec3(nor.xy, pos.w);') wrd = bpy.data.worlds['Arm'] if '_Legacy' in wrd.world_defs: frag.replace('sampler2DShadow', 'sampler2D') frag.replace('samplerCubeShadow', 'samplerCube')