def make_apply_ssao_pass(stages, node_group, node): make_quad_pass(stages, node_group, node, target_index=2, bind_target_indices=[4, 5], bind_target_constants=['gbufferD', 'gbuffer0'], shader_context='ssao_pass/ssao_pass/ssao_pass') make_quad_pass( stages, node_group, node, target_index=3, bind_target_indices=[2, 5], bind_target_constants=['tex', 'gbuffer0'], shader_context='blur_edge_pass/blur_edge_pass/blur_edge_pass_x') make_quad_pass( stages, node_group, node, target_index=1, bind_target_indices=[3, 5], bind_target_constants=['tex', 'gbuffer0'], shader_context='blur_edge_pass/blur_edge_pass/blur_edge_pass_y_blend') assets.add(build_node_trees.assets_path + 'noise8.png') assets.add_embedded_data('noise8.png')
def make_ssao_pass(stages, node_group, node): sc = 0.5 if bpy.data.worlds['Arm'].generate_ssao_half_res else 1.0 make_quad_pass(stages, node_group, node, target_index=1, bind_target_indices=[3, 4], bind_target_constants=['gbufferD', 'gbuffer0'], shader_context='ssao_pass/ssao_pass/ssao_pass', viewport_scale=sc) make_quad_pass( stages, node_group, node, target_index=2, bind_target_indices=[1, 4], bind_target_constants=['tex', 'gbuffer0'], shader_context='blur_edge_pass/blur_edge_pass/blur_edge_pass_x', viewport_scale=sc) make_quad_pass( stages, node_group, node, target_index=1, bind_target_indices=[2, 4], bind_target_constants=['tex', 'gbuffer0'], shader_context='blur_edge_pass/blur_edge_pass/blur_edge_pass_y') assets.add(build_node_trees.assets_path + 'noise8.png') assets.add_embedded_data('noise8.png')
def make_ssao_reproject_pass(stages, node_group, node): make_quad_pass( stages, node_group, node, target_index=1, bind_target_indices=[3, 4, 2, 5], bind_target_constants=['gbufferD', 'gbuffer0', 'slast', 'sveloc'], shader_context= 'ssao_reproject_pass/ssao_reproject_pass/ssao_reproject_pass') make_quad_pass( stages, node_group, node, target_index=2, bind_target_indices=[1, 4], bind_target_constants=['tex', 'gbuffer0'], shader_context='blur_edge_pass/blur_edge_pass/blur_edge_pass_x') make_quad_pass( stages, node_group, node, target_index=1, bind_target_indices=[2, 4], bind_target_constants=['tex', 'gbuffer0'], shader_context='blur_edge_pass/blur_edge_pass/blur_edge_pass_y') assets.add(build_node_trees.assets_path + 'noise8.png') assets.add_embedded_data('noise8.png')
def build_node_trees(assets_path): s = bpy.data.filepath.split(os.path.sep) s.pop() fp = os.path.sep.join(s) os.chdir(fp) # Make sure Assets dir exists if not os.path.exists('build/compiled/Assets/renderpaths'): os.makedirs('build/compiled/Assets/renderpaths') build_node_trees.assets_path = assets_path # Always include assets.add(assets_path + 'brdf.png') assets.add_embedded_data('brdf.png') bpy.data.worlds['Arm'].rp_defs = '' # Export render path for each camera parsed_paths = [] for cam in bpy.data.cameras: # if cam.game_export if cam.renderpath_path not in parsed_paths: node_group = bpy.data.node_groups[cam.renderpath_path] build_node_tree(cam, node_group) parsed_paths.append(cam.renderpath_path)
def build_node_tree(cam, node_group): build_node_tree.cam = cam output = {} dat = {} output['renderpath_datas'] = [dat] path = 'build/compiled/Assets/renderpaths/' node_group_name = node_group.name.replace('.', '_') rn = get_root_node(node_group) if rn == None: return dat['name'] = node_group_name # Store main context names dat['mesh_context'] = 'mesh' dat['shadows_context'] = 'shadowmap' dat['render_targets'], dat['depth_buffers'] = preprocess_renderpath( rn, node_group) dat['stages'] = [] buildNode(dat['stages'], rn, node_group) asset_path = path + node_group_name + '.arm' arm.utils.write_arm(asset_path, output) assets.add(asset_path)
def write_sky_irradiance(base_name): # Hosek spherical harmonics irradiance_floats = [ 1.5519331988822218, 2.3352207154503266, 2.997277451988076, 0.2673894962434794, 0.4305630474135794, 0.11331825259716752, -0.04453633521758638, -0.038753175134160295, -0.021302768541875794, 0.00055858020486499, 0.000371654770334503, 0.000126606145406403, -0.000135708721978705, -0.000787399554583089, -0.001550090690860059, 0.021947399048903773, 0.05453650591711572, 0.08783641266630278, 0.17053593578630663, 0.14734127083304463, 0.07775404698816404, -2.6924363189795e-05, -7.9350169701934e-05, -7.559914435231e-05, 0.27035455385870993, 0.23122918445556914, 0.12158817295211832 ] for i in range(0, len(irradiance_floats)): irradiance_floats[i] /= 2 envpath = arm.utils.get_fp_build() + '/compiled/Assets/envmaps' if not os.path.exists(envpath): os.makedirs(envpath) output_file = envpath + '/' + base_name + '_irradiance' sh_json = {} sh_json['irradiance'] = irradiance_floats arm.utils.write_arm(output_file + '.arm', sh_json) assets.add(output_file + '.arm')
def parse_sky_hosekwilkie(node: bpy.types.ShaderNodeTexSky, state: ParserState) -> vec3str: world = state.world curshader = state.curshader # Match to cycles world.arm_envtex_strength *= 0.1 assets.add_khafile_def('arm_hosek') curshader.add_uniform('vec3 A', link="_hosekA") curshader.add_uniform('vec3 B', link="_hosekB") curshader.add_uniform('vec3 C', link="_hosekC") curshader.add_uniform('vec3 D', link="_hosekD") curshader.add_uniform('vec3 E', link="_hosekE") curshader.add_uniform('vec3 F', link="_hosekF") curshader.add_uniform('vec3 G', link="_hosekG") curshader.add_uniform('vec3 H', link="_hosekH") curshader.add_uniform('vec3 I', link="_hosekI") curshader.add_uniform('vec3 Z', link="_hosekZ") curshader.add_uniform('vec3 hosekSunDirection', link="_hosekSunDirection") curshader.add_function("""vec3 hosekWilkie(float cos_theta, float gamma, float cos_gamma) { \tvec3 chi = (1 + cos_gamma * cos_gamma) / pow(1 + H * H - 2 * cos_gamma * H, vec3(1.5)); \treturn (1 + A * exp(B / (cos_theta + 0.01))) * (C + D * exp(E * gamma) + F * (cos_gamma * cos_gamma) + G * chi + I * sqrt(cos_theta)); }""") world.arm_envtex_sun_direction = [node.sun_direction[0], node.sun_direction[1], node.sun_direction[2]] world.arm_envtex_turbidity = node.turbidity world.arm_envtex_ground_albedo = node.ground_albedo wrd = bpy.data.worlds['Arm'] rpdat = arm.utils.get_rp() mobile_mat = rpdat.arm_material_model == 'Mobile' or rpdat.arm_material_model == 'Solid' if not state.radiance_written: # Irradiance json file name wname = arm.utils.safestr(world.name) world.arm_envtex_irr_name = wname write_probes.write_sky_irradiance(wname) # Radiance if rpdat.arm_radiance and rpdat.arm_irradiance and not mobile_mat: wrd.world_defs += '_Rad' hosek_path = 'armory/Assets/hosek/' sdk_path = arm.utils.get_sdk_path() # Use fake maps for now assets.add(sdk_path + '/' + hosek_path + 'hosek_radiance.hdr') for i in range(0, 8): assets.add(sdk_path + '/' + hosek_path + 'hosek_radiance_' + str(i) + '.hdr') world.arm_envtex_name = 'hosek' world.arm_envtex_num_mips = 8 state.radiance_written = True curshader.write('float cos_theta = clamp(pos.z, 0.0, 1.0);') curshader.write('float cos_gamma = dot(pos, hosekSunDirection);') curshader.write('float gamma_val = acos(cos_gamma);') return 'Z * hosekWilkie(cos_theta, gamma_val, cos_gamma) * envmapStrength;'
def make_draw_world(stage, node_group, node, dome=True): if dome: stage['command'] = 'draw_skydome' else: stage['command'] = 'draw_material_quad' # stage['params'].append(wname + '_material/' + wname + '_material/world') stage['params'].append('_worldMaterial') # Link to active world # Link assets if '_EnvClouds' in bpy.data.worlds['Arm'].world_defs: assets.add(build_node_trees.assets_path + 'noise256.png') assets.add_embedded_data('noise256.png')
def make_smaa_pass(stages, node_group, node): stage = {} stage['params'] = [] make_set_target(stage, node_group, node, target_index=2) stages.append(stage) stage = {} stage['params'] = [] make_clear_target(stage, color_val=[0.0, 0.0, 0.0, 0.0]) stages.append(stage) make_quad_pass( stages, node_group, node, target_index=None, bind_target_indices=[4], bind_target_constants=['colorTex'], shader_context='smaa_edge_detect/smaa_edge_detect/smaa_edge_detect') stage = {} stage['params'] = [] make_set_target(stage, node_group, node, target_index=3) stages.append(stage) stage = {} stage['params'] = [] make_clear_target(stage, color_val=[0.0, 0.0, 0.0, 0.0]) stages.append(stage) make_quad_pass( stages, node_group, node, target_index=None, bind_target_indices=[2], bind_target_constants=['edgesTex'], shader_context='smaa_blend_weight/smaa_blend_weight/smaa_blend_weight') make_quad_pass( stages, node_group, node, target_index=1, bind_target_indices=[4, 3, 5], bind_target_constants=['colorTex', 'blendTex', 'sveloc'], shader_context= 'smaa_neighborhood_blend/smaa_neighborhood_blend/smaa_neighborhood_blend' ) assets.add(build_node_trees.assets_path + 'smaa_area.png') assets.add(build_node_trees.assets_path + 'smaa_search.png') assets.add_embedded_data('smaa_area.png') assets.add_embedded_data('smaa_search.png')
def write_output(output): # Add datas to khafile dir_name = 'world' data_name = 'world' # Reference correct shader context dat = output['material_datas'][0] dat['shader'] = data_name + '/' + data_name assets.add_shader2(dir_name, data_name) # Write material json path = arm.utils.build_dir() + '/compiled/Assets/materials/' asset_path = path + dat['name'] + '.arm' arm.utils.write_arm(asset_path, output) assets.add(asset_path)
def write_output(output): # Add datas to khafile dir_name = 'world' # Append world defs wrd = bpy.data.worlds['Arm'] data_name = 'world' + wrd.world_defs + wrd.rp_defs # Reference correct shader context dat = output['material_datas'][0] dat['shader'] = data_name + '/' + data_name assets.add_shader2(dir_name, data_name) # Write material json path = 'build/compiled/Assets/materials/' asset_path = path + dat['name'] + '.arm' arm.utils.write_arm(asset_path, output) assets.add(asset_path)
def write_color_irradiance(base_name, col): # Constant color irradiance_floats = [col[0] * 1.13, col[1] * 1.13, col[2] * 1.13] # Adjust to Cycles for i in range(0, 24): irradiance_floats.append(0.0) envpath = arm.utils.build_dir() + '/compiled/Assets/envmaps' if not os.path.exists(envpath): os.makedirs(envpath) output_file = envpath + '/' + base_name + '_irradiance' sh_json = {} sh_json['irradiance'] = irradiance_floats arm.utils.write_arm(output_file + '.arm', sh_json) assets.add(output_file + '.arm')
def write_color_irradiance(base_name, col): """Constant color irradiance""" # Adjust to Cycles irradiance_floats = [col[0] * 1.13, col[1] * 1.13, col[2] * 1.13] for i in range(0, 24): irradiance_floats.append(0.0) envpath = os.path.join(arm.utils.get_fp_build(), 'compiled', 'Assets', 'envmaps') if not os.path.exists(envpath): os.makedirs(envpath) output_file = os.path.join(envpath, base_name + '_irradiance') sh_json = {'irradiance': irradiance_floats} arm.utils.write_arm(output_file + '.arm', sh_json) assets.add(output_file + '.arm')
def write_sky_irradiance(base_name): wrd = bpy.data.worlds['Arm'] if wrd.generate_radiance_sky_type == 'Hosek': # Hosek spherical harmonics irradiance_floats = [ 1.5519331988822218, 2.3352207154503266, 2.997277451988076, 0.2673894962434794, 0.4305630474135794, 0.11331825259716752, -0.04453633521758638, -0.038753175134160295, -0.021302768541875794, 0.00055858020486499, 0.000371654770334503, 0.000126606145406403, -0.000135708721978705, -0.000787399554583089, -0.001550090690860059, 0.021947399048903773, 0.05453650591711572, 0.08783641266630278, 0.17053593578630663, 0.14734127083304463, 0.07775404698816404, -2.6924363189795e-05, -7.9350169701934e-05, -7.559914435231e-05, 0.27035455385870993, 0.23122918445556914, 0.12158817295211832 ] for i in range(0, len(irradiance_floats)): irradiance_floats[i] /= 2 else: # Fake irradiance_floats = [ 0.5282714503101548, 0.6576873502619733, 1.0692444882409775, 0.17108712865136044, -0.08840906601412168, -0.5016437779078063, -0.05123227009753221, -0.06724088656181595, -0.07651659183264257, -0.09740705087869408, -0.19569235551561795, -0.3087497307203731, 0.056717192983076405, 0.1109186355691673, 0.20616582000220154, 0.013898321643280141, 0.05985657405787638, 0.12638202463080392, -0.003224443014484806, 0.013764449325286695, 0.04288850064700093, 0.1796545401960917, 0.21595731080039757, 0.29144356515614844, 0.10152875101705996, 0.2651761450155488, 0.4778582813756466 ] envpath = 'build/compiled/Assets/envmaps' if not os.path.exists(envpath): os.makedirs(envpath) output_file = envpath + '/' + base_name + '_irradiance' sh_json = {} sh_json['irradiance'] = irradiance_floats arm.utils.write_arm(output_file + '.arm', sh_json) assets.add(output_file + '.arm')
def build(): rpdat = arm.utils.get_rp() if rpdat.rp_driver != 'Armory' and arm.api.drivers[ rpdat.rp_driver]['make_rpath'] != None: arm.api.drivers[rpdat.rp_driver]['make_rpath']() return assets_path = arm.utils.get_sdk_path() + '/armory/Assets/' wrd = bpy.data.worlds['Arm'] add_world_defs() mobile_mat = rpdat.arm_material_model == 'Mobile' or rpdat.arm_material_model == 'Solid' if not mobile_mat: # Always include assets.add(assets_path + 'brdf.png') assets.add_embedded_data('brdf.png') if rpdat.rp_hdr: assets.add_khafile_def('rp_hdr') assets.add_khafile_def('rp_renderer={0}'.format(rpdat.rp_renderer)) if rpdat.rp_depthprepass: assets.add_khafile_def('rp_depthprepass') if rpdat.rp_shadows: assets.add_khafile_def('rp_shadowmap') assets.add_khafile_def('rp_shadowmap_cascade={0}'.format( arm.utils.get_cascade_size(rpdat))) assets.add_khafile_def('rp_shadowmap_cube={0}'.format( rpdat.rp_shadowmap_cube)) assets.add_khafile_def('rp_background={0}'.format(rpdat.rp_background)) if rpdat.rp_background == 'World': assets.add_shader_pass('world_pass') if '_EnvClouds' in wrd.world_defs: assets.add(assets_path + 'clouds_base.raw') assets.add_embedded_data('clouds_base.raw') assets.add(assets_path + 'clouds_detail.raw') assets.add_embedded_data('clouds_detail.raw') assets.add(assets_path + 'clouds_map.png') assets.add_embedded_data('clouds_map.png') if rpdat.rp_renderer == 'Deferred' and not rpdat.rp_compositornodes: assets.add_shader_pass('copy_pass') if rpdat.rp_render_to_texture: assets.add_khafile_def('rp_render_to_texture') if rpdat.rp_renderer == 'Forward' and not rpdat.rp_compositornodes: assets.add_shader_pass('copy_pass') if rpdat.rp_compositornodes: assets.add_khafile_def('rp_compositornodes') compo_depth = False if rpdat.arm_tonemap != 'Off': wrd.compo_defs = '_CTone' + rpdat.arm_tonemap if rpdat.rp_antialiasing == 'FXAA': wrd.compo_defs += '_CFXAA' if rpdat.arm_letterbox: wrd.compo_defs += '_CLetterbox' if rpdat.arm_grain: wrd.compo_defs += '_CGrain' if rpdat.arm_sharpen: wrd.compo_defs += '_CSharpen' if bpy.data.scenes[0].view_settings.exposure != 0.0: wrd.compo_defs += '_CExposure' if rpdat.arm_fog: wrd.compo_defs += '_CFog' compo_depth = True focus_distance = 0.0 if len(bpy.data.cameras) > 0 and bpy.data.cameras[0].dof.use_dof: focus_distance = bpy.data.cameras[0].dof.focus_distance if focus_distance > 0.0: wrd.compo_defs += '_CDOF' compo_depth = True if rpdat.arm_lens_texture != '': wrd.compo_defs += '_CLensTex' assets.add_embedded_data('lenstexture.jpg') if rpdat.arm_lens_texture_masking: wrd.compo_defs += '_CLensTexMasking' if rpdat.arm_fisheye: wrd.compo_defs += '_CFishEye' if rpdat.arm_vignette: wrd.compo_defs += '_CVignette' if rpdat.arm_lensflare: wrd.compo_defs += '_CGlare' compo_depth = True if rpdat.arm_lut_texture != '': wrd.compo_defs += '_CLUT' assets.add_embedded_data('luttexture.jpg') if '_CDOF' in wrd.compo_defs or '_CFXAA' in wrd.compo_defs or '_CSharpen' in wrd.compo_defs: wrd.compo_defs += '_CTexStep' if '_CDOF' in wrd.compo_defs or '_CFog' in wrd.compo_defs or '_CGlare' in wrd.compo_defs: wrd.compo_defs += '_CCameraProj' if compo_depth: wrd.compo_defs += '_CDepth' assets.add_khafile_def('rp_compositordepth') if rpdat.rp_pp: wrd.compo_defs += '_CPostprocess' assets.add_shader_pass('compositor_pass') assets.add_khafile_def('rp_antialiasing={0}'.format( rpdat.rp_antialiasing)) if rpdat.rp_antialiasing == 'SMAA' or rpdat.rp_antialiasing == 'TAA': assets.add_shader_pass('smaa_edge_detect') assets.add_shader_pass('smaa_blend_weight') assets.add_shader_pass('smaa_neighborhood_blend') assets.add(assets_path + 'smaa_area.png') assets.add(assets_path + 'smaa_search.png') assets.add_embedded_data('smaa_area.png') assets.add_embedded_data('smaa_search.png') wrd.world_defs += '_SMAA' if rpdat.rp_antialiasing == 'TAA': assets.add_shader_pass('taa_pass') assets.add_shader_pass('copy_pass') if rpdat.rp_antialiasing == 'TAA' or rpdat.rp_motionblur == 'Object': assets.add_khafile_def('arm_veloc') wrd.world_defs += '_Veloc' if rpdat.rp_antialiasing == 'TAA': assets.add_khafile_def('arm_taa') assets.add_khafile_def('rp_supersampling={0}'.format( rpdat.rp_supersampling)) if rpdat.rp_supersampling == '4': assets.add_shader_pass('supersample_resolve') assets.add_khafile_def('rp_ssgi={0}'.format(rpdat.rp_ssgi)) if rpdat.rp_ssgi != 'Off': wrd.world_defs += '_SSAO' if rpdat.rp_ssgi == 'SSAO': assets.add_shader_pass('ssao_pass') assets.add_shader_pass('blur_edge_pass') else: assets.add_shader_pass('ssgi_pass') assets.add_shader_pass('blur_edge_pass') if rpdat.arm_ssgi_half_res: assets.add_khafile_def('rp_ssgi_half') if rpdat.rp_bloom: assets.add_khafile_def('rp_bloom') assets.add_shader_pass('bloom_pass') assets.add_shader_pass('blur_gaus_pass') if rpdat.rp_ssr: assets.add_khafile_def('rp_ssr') assets.add_shader_pass('ssr_pass') assets.add_shader_pass('blur_adaptive_pass') if rpdat.arm_ssr_half_res: assets.add_khafile_def('rp_ssr_half') if rpdat.rp_overlays: assets.add_khafile_def('rp_overlays') if rpdat.rp_translucency: assets.add_khafile_def('rp_translucency') assets.add_shader_pass('translucent_resolve') if rpdat.rp_stereo: assets.add_khafile_def('rp_stereo') assets.add_khafile_def('arm_vr') wrd.world_defs += '_VR' has_voxels = arm.utils.voxel_support() if rpdat.rp_voxelao and has_voxels and rpdat.arm_material_model == 'Full': assets.add_khafile_def('rp_voxelao') assets.add_khafile_def('rp_voxelgi_resolution={0}'.format( rpdat.rp_voxelgi_resolution)) assets.add_khafile_def('rp_voxelgi_resolution_z={0}'.format( rpdat.rp_voxelgi_resolution_z)) if rpdat.arm_rp_resolution == 'Custom': assets.add_khafile_def('rp_resolution_filter={0}'.format( rpdat.arm_rp_resolution_filter)) if rpdat.rp_renderer == 'Deferred': if rpdat.arm_material_model == 'Full': assets.add_shader_pass('deferred_light') else: # mobile, solid assets.add_shader_pass('deferred_light_' + rpdat.arm_material_model.lower()) assets.add_khafile_def('rp_material_' + rpdat.arm_material_model.lower()) if len(bpy.data.lightprobes) > 0: wrd.world_defs += '_Probes' assets.add_khafile_def('rp_probes') assets.add_shader_pass('probe_planar') assets.add_shader_pass('probe_cubemap') assets.add_shader_pass('copy_pass') if rpdat.rp_volumetriclight: assets.add_khafile_def('rp_volumetriclight') assets.add_shader_pass('volumetric_light') assets.add_shader_pass('blur_bilat_pass') assets.add_shader_pass('blur_bilat_blend_pass') assets.add(assets_path + 'blue_noise64.png') assets.add_embedded_data('blue_noise64.png') if rpdat.rp_decals: assets.add_khafile_def('rp_decals') if rpdat.rp_water: assets.add_khafile_def('rp_water') assets.add_shader_pass('water_pass') assets.add_shader_pass('copy_pass') assets.add(assets_path + 'water_base.png') assets.add_embedded_data('water_base.png') assets.add(assets_path + 'water_detail.png') assets.add_embedded_data('water_detail.png') assets.add(assets_path + 'water_foam.png') assets.add_embedded_data('water_foam.png') if rpdat.rp_blending: assets.add_khafile_def('rp_blending') if rpdat.rp_sss: assets.add_khafile_def('rp_sss') wrd.world_defs += '_SSS' assets.add_shader_pass('sss_pass') if (rpdat.rp_ssr and rpdat.arm_ssr_half_res) or (rpdat.rp_ssgi != 'Off' and rpdat.arm_ssgi_half_res): assets.add_shader_pass('downsample_depth') if rpdat.rp_motionblur != 'Off': assets.add_khafile_def('rp_motionblur={0}'.format(rpdat.rp_motionblur)) assets.add_shader_pass('copy_pass') if rpdat.rp_motionblur == 'Camera': assets.add_shader_pass('motion_blur_pass') else: assets.add_shader_pass('motion_blur_veloc_pass') if rpdat.rp_compositornodes and rpdat.rp_autoexposure: assets.add_khafile_def('rp_autoexposure') assets.add_shader_pass('histogram_pass') if rpdat.rp_dynres: assets.add_khafile_def('rp_dynres') if rpdat.rp_pp: assets.add_khafile_def('rp_pp') if rpdat.rp_chromatic_aberration: assets.add_shader_pass('copy_pass') assets.add_khafile_def('rp_chromatic_aberration') assets.add_shader_pass('chromatic_aberration_pass') gbuffer2 = '_Veloc' in wrd.world_defs if gbuffer2: assets.add_khafile_def('rp_gbuffer2') wrd.world_defs += '_gbuffer2' if callback != None: callback()
def add_irr_assets(output_file_irr): assets.add(output_file_irr + '.arm')
def add_rad_assets(output_file_rad, rad_format, num_mips): assets.add(output_file_rad + '.' + rad_format) for i in range(0, num_mips): assets.add(output_file_rad + '_' + str(i) + '.' + rad_format)
def export_data(fp, sdk_path): global exporter wrd = bpy.data.worlds['Arm'] print('\nArmory v{0} ({1})'.format(wrd.arm_version, wrd.arm_commit)) print('OS: ' + arm.utils.get_os() + ', Target: ' + state.target + ', GAPI: ' + arm.utils.get_gapi() + ', Blender: ' + bpy.app.version_string) # Clean compiled variants if cache is disabled build_dir = arm.utils.get_fp_build() if wrd.arm_cache_shaders == False: if os.path.isdir(build_dir + '/debug/html5-resources'): shutil.rmtree(build_dir + '/debug/html5-resources', onerror=remove_readonly) if os.path.isdir(build_dir + '/krom-resources'): shutil.rmtree(build_dir + '/krom-resources', onerror=remove_readonly) if os.path.isdir(build_dir + '/debug/krom-resources'): shutil.rmtree(build_dir + '/debug/krom-resources', onerror=remove_readonly) if os.path.isdir(build_dir + '/windows-resources'): shutil.rmtree(build_dir + '/windows-resources', onerror=remove_readonly) if os.path.isdir(build_dir + '/linux-resources'): shutil.rmtree(build_dir + '/linux-resources', onerror=remove_readonly) if os.path.isdir(build_dir + '/osx-resources'): shutil.rmtree(build_dir + '/osx-resources', onerror=remove_readonly) if os.path.isdir(build_dir + '/compiled/Shaders'): shutil.rmtree(build_dir + '/compiled/Shaders', onerror=remove_readonly) raw_shaders_path = sdk_path + '/armory/Shaders/' assets_path = sdk_path + '/armory/Assets/' export_physics = bpy.data.worlds['Arm'].arm_physics != 'Disabled' export_navigation = bpy.data.worlds['Arm'].arm_navigation != 'Disabled' export_ui = bpy.data.worlds['Arm'].arm_ui != 'Disabled' assets.reset() # Build node trees ArmoryExporter.import_traits = [] make_logic.build() make_world.build() make_renderpath.build() # Export scene data assets.embedded_data = sorted(list(set(assets.embedded_data))) physics_found = False navigation_found = False ui_found = False ArmoryExporter.compress_enabled = state.is_publish and wrd.arm_asset_compression for scene in bpy.data.scenes: if scene.arm_export: ext = '.zip' if (scene.arm_compress and state.is_publish) else '.arm' asset_path = build_dir + '/compiled/Assets/' + arm.utils.safestr( scene.name) + ext exporter.execute(bpy.context, asset_path, scene=scene) if ArmoryExporter.export_physics: physics_found = True if ArmoryExporter.export_navigation: navigation_found = True if ArmoryExporter.export_ui: ui_found = True assets.add(asset_path) if physics_found == False: # Disable physics if no rigid body is exported export_physics = False if navigation_found == False: export_navigation = False if ui_found == False: export_ui = False if wrd.arm_ui == 'Enabled': export_ui = True modules = [] if wrd.arm_audio == 'Enabled': modules.append('audio') if export_physics: modules.append('physics') if export_navigation: modules.append('navigation') if export_ui: modules.append('ui') if wrd.arm_hscript == 'Enabled': modules.append('hscript') if wrd.arm_formatlib == 'Enabled': modules.append('format') print('Exported modules: ' + str(modules)) defs = arm.utils.def_strings_to_array(wrd.world_defs) cdefs = arm.utils.def_strings_to_array(wrd.compo_defs) print('Shader flags: ' + str(defs)) if wrd.arm_play_console: print('Khafile flags: ' + str(assets.khafile_defs)) # Write compiled.inc shaders_path = build_dir + '/compiled/Shaders' if not os.path.exists(shaders_path): os.makedirs(shaders_path) write_data.write_compiledglsl(defs + cdefs) # Write referenced shader passes if not os.path.isfile(build_dir + '/compiled/Shaders/shader_datas.arm' ) or state.last_world_defs != wrd.world_defs: res = {} res['shader_datas'] = [] for ref in assets.shader_passes: # Ensure shader pass source exists if not os.path.exists(raw_shaders_path + '/' + ref): continue assets.shader_passes_assets[ref] = [] if ref.startswith('compositor_pass'): compile_shader_pass(res, raw_shaders_path, ref, defs + cdefs) # elif ref.startswith('grease_pencil'): # compile_shader_pass(res, raw_shaders_path, ref, []) else: compile_shader_pass(res, raw_shaders_path, ref, defs) arm.utils.write_arm(shaders_path + '/shader_datas.arm', res) for ref in assets.shader_passes: for s in assets.shader_passes_assets[ref]: assets.add_shader(shaders_path + '/' + s + '.glsl') for file in assets.shaders_external: name = file.split('/')[-1].split('\\')[-1] target = build_dir + '/compiled/Shaders/' + name if not os.path.exists(target): shutil.copy(file, target) state.last_world_defs = wrd.world_defs # Reset path os.chdir(fp) # Copy std shaders if not os.path.isdir(build_dir + '/compiled/Shaders/std'): shutil.copytree(raw_shaders_path + 'std', build_dir + '/compiled/Shaders/std') # Write config.arm resx, resy = arm.utils.get_render_resolution(arm.utils.get_active_scene()) if wrd.arm_write_config: write_data.write_config(resx, resy) # Write khafile.js enable_dce = state.is_publish and wrd.arm_dce import_logic = not state.is_publish and arm.utils.logic_editor_space( ) != None write_data.write_khafilejs(state.is_play, export_physics, export_navigation, export_ui, state.is_publish, enable_dce, state.is_viewport, ArmoryExporter.import_traits, import_logic) # Write Main.hx - depends on write_khafilejs for writing number of assets scene_name = arm.utils.get_project_scene_name() write_data.write_mainhx(scene_name, resx, resy, state.is_play, state.is_viewport, state.is_publish) if scene_name != state.last_scene or resx != state.last_resx or resy != state.last_resy: wrd.arm_recompile = True state.last_resx = resx state.last_resy = resy state.last_scene = scene_name
def export_data(fp, sdk_path): wrd = bpy.data.worlds['Arm'] print('Armory v{0} ({1})'.format(wrd.arm_version, wrd.arm_commit)) if wrd.arm_verbose_output: print( f'Blender: {bpy.app.version_string}, Target: {state.target}, GAPI: {arm.utils.get_gapi()}' ) # Clean compiled variants if cache is disabled build_dir = arm.utils.get_fp_build() if not wrd.arm_cache_build: if os.path.isdir(build_dir + '/debug/html5-resources'): shutil.rmtree(build_dir + '/debug/html5-resources', onerror=remove_readonly) if os.path.isdir(build_dir + '/krom-resources'): shutil.rmtree(build_dir + '/krom-resources', onerror=remove_readonly) if os.path.isdir(build_dir + '/debug/krom-resources'): shutil.rmtree(build_dir + '/debug/krom-resources', onerror=remove_readonly) if os.path.isdir(build_dir + '/windows-resources'): shutil.rmtree(build_dir + '/windows-resources', onerror=remove_readonly) if os.path.isdir(build_dir + '/linux-resources'): shutil.rmtree(build_dir + '/linux-resources', onerror=remove_readonly) if os.path.isdir(build_dir + '/osx-resources'): shutil.rmtree(build_dir + '/osx-resources', onerror=remove_readonly) if os.path.isdir(build_dir + '/compiled/Shaders'): shutil.rmtree(build_dir + '/compiled/Shaders', onerror=remove_readonly) raw_shaders_path = sdk_path + '/armory/Shaders/' assets_path = sdk_path + '/armory/Assets/' export_physics = bpy.data.worlds['Arm'].arm_physics != 'Disabled' export_navigation = bpy.data.worlds['Arm'].arm_navigation != 'Disabled' export_ui = bpy.data.worlds['Arm'].arm_ui != 'Disabled' assets.reset() # Build node trees ArmoryExporter.import_traits = [] make_logic.build() make_world.build() make_renderpath.build() # Export scene data assets.embedded_data = sorted(list(set(assets.embedded_data))) physics_found = False navigation_found = False ui_found = False ArmoryExporter.compress_enabled = state.is_publish and wrd.arm_asset_compression ArmoryExporter.optimize_enabled = state.is_publish and wrd.arm_optimize_data if not os.path.exists(build_dir + '/compiled/Assets'): os.makedirs(build_dir + '/compiled/Assets') # have a "zoo" collection in the current scene export_coll = bpy.data.collections.new("export_coll") bpy.context.scene.collection.children.link(export_coll) for scene in bpy.data.scenes: if scene == bpy.context.scene: continue for o in scene.collection.all_objects: if o.type == "MESH" or o.type == "EMPTY": if o.name not in export_coll.all_objects.keys(): export_coll.objects.link(o) depsgraph = bpy.context.evaluated_depsgraph_get() bpy.data.collections.remove(export_coll) # destroy "zoo" collection for scene in bpy.data.scenes: if scene.arm_export: ext = '.lz4' if ArmoryExporter.compress_enabled else '.arm' asset_path = build_dir + '/compiled/Assets/' + arm.utils.safestr( scene.name) + ext ArmoryExporter.export_scene(bpy.context, asset_path, scene=scene, depsgraph=depsgraph) if ArmoryExporter.export_physics: physics_found = True if ArmoryExporter.export_navigation: navigation_found = True if ArmoryExporter.export_ui: ui_found = True assets.add(asset_path) if physics_found == False: # Disable physics if no rigid body is exported export_physics = False if navigation_found == False: export_navigation = False if ui_found == False: export_ui = False if wrd.arm_ui == 'Enabled': export_ui = True modules = [] if wrd.arm_audio == 'Enabled': modules.append('audio') if export_physics: modules.append('physics') if export_navigation: modules.append('navigation') if export_ui: modules.append('ui') defs = arm.utils.def_strings_to_array(wrd.world_defs) cdefs = arm.utils.def_strings_to_array(wrd.compo_defs) if wrd.arm_verbose_output: print('Exported modules:', ', '.join(modules)) print('Shader flags:', ' '.join(defs)) print('Compositor flags:', ' '.join(cdefs)) print('Khafile flags:', ' '.join(assets.khafile_defs)) # Render path is configurable at runtime has_config = wrd.arm_write_config or os.path.exists(arm.utils.get_fp() + '/Bundled/config.arm') # Write compiled.inc shaders_path = build_dir + '/compiled/Shaders' if not os.path.exists(shaders_path): os.makedirs(shaders_path) write_data.write_compiledglsl(defs + cdefs, make_variants=has_config) # Write referenced shader passes if not os.path.isfile(build_dir + '/compiled/Shaders/shader_datas.arm' ) or state.last_world_defs != wrd.world_defs: res = {'shader_datas': []} for ref in assets.shader_passes: # Ensure shader pass source exists if not os.path.exists(raw_shaders_path + '/' + ref): continue assets.shader_passes_assets[ref] = [] if ref.startswith('compositor_pass'): compile_shader_pass(res, raw_shaders_path, ref, defs + cdefs, make_variants=has_config) else: compile_shader_pass(res, raw_shaders_path, ref, defs, make_variants=has_config) # Workaround to also export non-material world shaders res['shader_datas'] += make_world.shader_datas arm.utils.write_arm(shaders_path + '/shader_datas.arm', res) for ref in assets.shader_passes: for s in assets.shader_passes_assets[ref]: assets.add_shader(shaders_path + '/' + s + '.glsl') for file in assets.shaders_external: name = file.split('/')[-1].split('\\')[-1] target = build_dir + '/compiled/Shaders/' + name if not os.path.exists(target): shutil.copy(file, target) state.last_world_defs = wrd.world_defs # Reset path os.chdir(fp) # Copy std shaders if not os.path.isdir(build_dir + '/compiled/Shaders/std'): shutil.copytree(raw_shaders_path + 'std', build_dir + '/compiled/Shaders/std') # Write config.arm resx, resy = arm.utils.get_render_resolution(arm.utils.get_active_scene()) if wrd.arm_write_config: write_data.write_config(resx, resy) # Change project version (Build, Publish) if (not state.is_play) and (wrd.arm_project_version_autoinc): wrd.arm_project_version = arm.utils.arm.utils.change_version_project( wrd.arm_project_version) # Write khafile.js enable_dce = state.is_publish and wrd.arm_dce import_logic = not state.is_publish and arm.utils.logic_editor_space( ) != None write_data.write_khafilejs(state.is_play, export_physics, export_navigation, export_ui, state.is_publish, enable_dce, ArmoryExporter.import_traits, import_logic) # Write Main.hx - depends on write_khafilejs for writing number of assets scene_name = arm.utils.get_project_scene_name() write_data.write_mainhx(scene_name, resx, resy, state.is_play, state.is_publish) if scene_name != state.last_scene or resx != state.last_resx or resy != state.last_resy: wrd.arm_recompile = True state.last_resx = resx state.last_resy = resy state.last_scene = scene_name
def write_khafilejs(is_play, export_physics, export_navigation, export_ui, is_publish, enable_dce, in_viewport, import_traits, import_logicnodes): global check_dot_path sdk_path = arm.utils.get_sdk_path() wrd = bpy.data.worlds['Arm'] with open('khafile.js', 'w') as f: f.write("""// Auto-generated let project = new Project('""" + arm.utils.safestr(wrd.arm_project_name) + """'); project.addSources('Sources'); """) # TODO: Khamake bug workaround - assets & shaders located in folder starting with '.' get discarded - copy them to project check_dot_path = False if '/.' in sdk_path: check_dot_path = True if not os.path.exists(arm.utils.build_dir() + '/compiled/KhaShaders'): kha_shaders_path = arm.utils.get_kha_path( ) + '/Sources/Shaders' shutil.copytree(kha_shaders_path, arm.utils.build_dir() + '/compiled/KhaShaders') f.write("project.addShaders('" + arm.utils.build_dir() + "/compiled/KhaShaders/**');\n") # Auto-add assets located in Bundled directory if os.path.exists('Bundled'): for file in glob.glob("Bundled/**", recursive=True): if os.path.isfile(file): assets.add(file) if os.path.exists('Shaders'): for file in glob.glob("Shaders/**", recursive=True): if os.path.isfile(file): assets.add_shader(file) if not os.path.exists('Libraries/armory'): f.write(add_armory_library(sdk_path, 'armory')) if not os.path.exists('Libraries/iron'): f.write(add_armory_library(sdk_path, 'iron')) # Project libraries if os.path.exists('Libraries'): libs = os.listdir('Libraries') for lib in libs: if os.path.isdir('Libraries/' + lib): f.write('project.addLibrary("{0}");\n'.format(lib)) if export_physics: assets.add_khafile_def('arm_physics') if wrd.arm_physics == 'Bullet': assets.add_khafile_def('arm_bullet') if not os.path.exists('Libraries/haxebullet'): f.write( add_armory_library(sdk_path + '/lib/', 'haxebullet')) if state.target == 'krom' or state.target == 'html5' or state.target == 'node': ammojs_path = sdk_path + '/lib/haxebullet/js/ammo/ammo.js' ammojs_path = ammojs_path.replace('\\', '/') f.write(add_assets(ammojs_path)) elif wrd.arm_physics == 'Oimo': assets.add_khafile_def('arm_oimo') if not os.path.exists('Libraries/oimo'): f.write(add_armory_library(sdk_path + '/lib/', 'oimo')) if export_navigation: assets.add_khafile_def('arm_navigation') if not os.path.exists('Libraries/haxerecast'): f.write(add_armory_library(sdk_path + '/lib/', 'haxerecast')) if state.target == 'krom' or state.target == 'html5': recastjs_path = sdk_path + '/lib/haxerecast/js/recast/recast.js' recastjs_path = recastjs_path.replace('\\', '/') f.write(add_assets(recastjs_path)) if not is_publish: f.write( """project.addParameter("--macro include('armory.trait')");\n""" ) f.write( """project.addParameter("--macro include('armory.trait.internal')");\n""" ) if export_physics: f.write( """project.addParameter("--macro include('armory.trait.physics')");\n""" ) if wrd.arm_physics == 'Bullet': f.write( """project.addParameter("--macro include('armory.trait.physics.bullet')");\n""" ) else: f.write( """project.addParameter("--macro include('armory.trait.physics.oimo')");\n""" ) if export_navigation: f.write( """project.addParameter("--macro include('armory.trait.navigation')");\n""" ) if import_logicnodes: # Live patching for logic nodes f.write( """project.addParameter("--macro include('armory.logicnode')");\n""" ) if enable_dce: f.write("project.addParameter('-dce full');\n") if in_viewport: assets.add_khafile_def('arm_viewport') import_traits.append('armory.trait.internal.Bridge') import_traits = list(set(import_traits)) for i in range(0, len(import_traits)): f.write("project.addParameter('" + import_traits[i] + "');\n") f.write("""project.addParameter("--macro keep('""" + import_traits[i] + """')");\n""") if state.is_render: assets.add_khafile_def('arm_render') if state.is_render_anim: assets.add_khafile_def('arm_render_anim') if not os.path.exists('Libraries/iron_format'): f.write( add_armory_library(sdk_path + '/lib/', 'iron_format')) shaderload = state.target == 'krom' or state.target == 'html5' if wrd.arm_cache_compiler and shaderload and not is_publish: # Load shaders manually assets.add_khafile_def('arm_shaderload') sceneload = state.target == 'krom' if wrd.arm_play_live_patch and is_play and in_viewport and sceneload: # Scene patch assets.add_khafile_def('arm_sceneload') shader_references = sorted(list(set(assets.shaders))) for ref in shader_references: ref = ref.replace('\\', '/') f.write("project.addShaders('" + ref + "');\n") shader_data_references = sorted(list(set(assets.shader_datas))) for ref in shader_data_references: ref = ref.replace('\\', '/') f.write(add_assets(ref)) asset_references = sorted(list(set(assets.assets))) for ref in asset_references: ref = ref.replace('\\', '/') quality = 1.0 s = ref.lower() if s.endswith('.wav'): quality = wrd.arm_sound_quality elif s.endswith('.png') or s.endswith('.jpg'): quality = wrd.arm_texture_quality f.write(add_assets(ref, quality=quality)) if wrd.arm_sound_quality < 1.0 or state.target == 'html5': assets.add_khafile_def('arm_soundcompress') if wrd.arm_texture_quality < 1.0: assets.add_khafile_def('arm_texcompress') if wrd.arm_play_console: assets.add_khafile_def('arm_debug') f.write("project.addShaders('" + sdk_path + "/armory/Shaders/debug_draw/**');\n") if export_ui: if not os.path.exists('Libraries/zui'): f.write(add_armory_library(sdk_path, 'lib/zui')) p = sdk_path + '/armory/Assets/droid_sans.ttf' f.write(add_assets(p.replace('\\', '/'))) assets.add_khafile_def('arm_ui') if wrd.arm_hscript == 'Enabled': if not os.path.exists('Libraries/hscript'): f.write(add_armory_library(sdk_path, 'lib/hscript')) assets.add_khafile_def('arm_hscript') if wrd.arm_minimize == False: assets.add_khafile_def('arm_json') if wrd.arm_deinterleaved_buffers == True: assets.add_khafile_def('arm_deinterleaved') if wrd.arm_batch_meshes == True: assets.add_khafile_def('arm_batch') if wrd.arm_stream_scene: assets.add_khafile_def('arm_stream') if wrd.arm_skin == 'CPU': assets.add_khafile_def('arm_skin_cpu') elif wrd.arm_skin == 'GPU (Matrix)': assets.add_khafile_def('arm_skin_mat') if arm.utils.get_viewport_controls() == 'azerty': assets.add_khafile_def('arm_azerty') for d in assets.khafile_defs: f.write("project.addDefine('" + d + "');\n") config_text = wrd.arm_khafile if config_text != '': f.write(bpy.data.texts[config_text].as_string()) if wrd.arm_winorient != 'Multi': if state.target == 'android-native': f.write( "project.targetOptions.android_native.screenOrientation = '{0}';\n" .format(wrd.arm_winorient.lower())) f.write("\n\nresolve(project);\n")
def build(): rpdat = arm.utils.get_rp() if rpdat.rp_driver != 'Armory' and arm.api.drivers[ rpdat.rp_driver]['make_rpath'] != None: arm.api.drivers[rpdat.rp_driver]['make_rpath']() return assets_path = arm.utils.get_sdk_path() + '/armory/Assets/' wrd = bpy.data.worlds['Arm'] add_world_defs() mobile_mat = rpdat.arm_material_model == 'Mobile' or rpdat.arm_material_model == 'Solid' if not mobile_mat: # Always include assets.add(assets_path + 'brdf.png') assets.add_embedded_data('brdf.png') if rpdat.rp_hdr: assets.add_khafile_def('rp_hdr') assets.add_khafile_def('rp_renderer={0}'.format(rpdat.rp_renderer)) if rpdat.rp_depthprepass: assets.add_khafile_def('rp_depthprepass') if rpdat.rp_shadowmap != 'Off': assets.add_khafile_def('rp_shadowmap') assets.add_khafile_def('rp_shadowmap_size={0}'.format( rpdat.rp_shadowmap)) assets.add_khafile_def('rp_background={0}'.format(rpdat.rp_background)) if rpdat.rp_background == 'World': assets.add_shader_pass('world_pass') if '_EnvClouds' in wrd.world_defs: assets.add(assets_path + 'noise256.png') assets.add_embedded_data('noise256.png') if rpdat.rp_renderer == 'Deferred' and not rpdat.rp_compositornodes: assets.add_shader_pass('copy_pass') if rpdat.rp_renderer == 'Forward' and not rpdat.rp_compositornodes and rpdat.rp_render_to_texture: assets.add_shader_pass('copy_pass') if rpdat.rp_render_to_texture: assets.add_khafile_def('rp_render_to_texture') if rpdat.rp_compositornodes: assets.add_khafile_def('rp_compositornodes') compo_depth = False if rpdat.arm_tonemap != 'Off': wrd.compo_defs = '_CTone' + rpdat.arm_tonemap if rpdat.rp_antialiasing == 'FXAA': wrd.compo_defs += '_CFXAA' if rpdat.arm_letterbox: wrd.compo_defs += '_CLetterbox' if rpdat.arm_grain: wrd.compo_defs += '_CGrain' if rpdat.arm_sharpen: wrd.compo_defs += '_CSharpen' if bpy.data.scenes[0].cycles.film_exposure != 1.0: wrd.compo_defs += '_CExposure' if rpdat.arm_fog: wrd.compo_defs += '_CFog' compo_depth = True if len(bpy.data.cameras ) > 0 and bpy.data.cameras[0].dof_distance > 0.0: wrd.compo_defs += '_CDOF' compo_depth = True if compo_depth: wrd.compo_defs += '_CDepth' assets.add_khafile_def('rp_compositordepth') if rpdat.arm_lens_texture != '': wrd.compo_defs += '_CLensTex' assets.add_embedded_data('lenstexture.jpg') if rpdat.arm_fisheye: wrd.compo_defs += '_CFishEye' if rpdat.arm_vignette: wrd.compo_defs += '_CVignette' if rpdat.arm_lensflare: wrd.compo_defs += '_CGlare' if rpdat.arm_lut_texture != '': wrd.compo_defs += '_CLUT' assets.add_embedded_data('luttexture.jpg') if '_CDOF' in wrd.compo_defs or '_CFXAA' in wrd.compo_defs or '_CSharpen' in wrd.compo_defs: wrd.compo_defs += '_CTexStep' if '_CDOF' in wrd.compo_defs or '_CFog' in wrd.compo_defs or '_CGlare' in wrd.compo_defs: wrd.compo_defs += '_CCameraProj' assets.add_shader_pass('compositor_pass') assets.add_khafile_def('rp_antialiasing={0}'.format( rpdat.rp_antialiasing)) if rpdat.rp_antialiasing == 'SMAA' or rpdat.rp_antialiasing == 'TAA': assets.add_shader_pass('smaa_edge_detect') assets.add_shader_pass('smaa_blend_weight') assets.add_shader_pass('smaa_neighborhood_blend') assets.add(assets_path + 'smaa_area.png') assets.add(assets_path + 'smaa_search.png') assets.add_embedded_data('smaa_area.png') assets.add_embedded_data('smaa_search.png') wrd.world_defs += '_SMAA' if rpdat.rp_antialiasing == 'TAA': assets.add_shader_pass('taa_pass') assets.add_shader_pass('copy_pass') if rpdat.rp_antialiasing == 'TAA' or rpdat.rp_motionblur == 'Object': assets.add_khafile_def('arm_veloc') wrd.world_defs += '_Veloc' if rpdat.rp_antialiasing == 'TAA': assets.add_khafile_def('arm_taa') assets.add_khafile_def('rp_supersampling={0}'.format( rpdat.rp_supersampling)) if rpdat.rp_supersampling == '4': assets.add_shader_pass('supersample_resolve') if rpdat.rp_overlays: assets.add_khafile_def('rp_overlays') if rpdat.rp_translucency: assets.add_khafile_def('rp_translucency') assets.add_shader_pass('translucent_resolve') if rpdat.rp_stereo: assets.add_khafile_def('rp_stereo') assets.add_khafile_def('arm_vr') wrd.world_defs += '_VR' assets.add(assets_path + 'vr.png') assets.add_embedded_data('vr.png') rp_gi = rpdat.rp_gi has_voxels = arm.utils.voxel_support() if not has_voxels: rp_gi = 'Off' assets.add_khafile_def('rp_gi={0}'.format(rp_gi)) if rpdat.rp_gi != 'Off': if has_voxels: assets.add_khafile_def('rp_gi={0}'.format(rpdat.rp_gi)) assets.add_khafile_def('rp_voxelgi_resolution={0}'.format( rpdat.rp_voxelgi_resolution)) assets.add_khafile_def('rp_voxelgi_resolution_z={0}'.format( rpdat.rp_voxelgi_resolution_z)) if rpdat.rp_voxelgi_hdr: assets.add_khafile_def('rp_voxelgi_hdr') if rpdat.arm_voxelgi_shadows: assets.add_khafile_def('rp_voxelgi_shadows') if rpdat.arm_voxelgi_refraction: assets.add_khafile_def('rp_voxelgi_refraction') else: log.warn( 'Disabling Voxel GI - unsupported target - use Krom instead') if rpdat.arm_rp_resolution == 'Custom': assets.add_khafile_def('rp_resolution_filter={0}'.format( rpdat.arm_rp_resolution_filter)) assets.add_khafile_def('rp_ssgi={0}'.format(rpdat.rp_ssgi)) if rpdat.rp_ssgi != 'Off': wrd.world_defs += '_SSAO' if rpdat.rp_ssgi == 'SSAO': assets.add_shader_pass('ssao_pass') assets.add_shader_pass('blur_edge_pass') assets.add(assets_path + 'noise8.png') assets.add_embedded_data('noise8.png') else: assets.add_shader_pass('ssgi_pass') assets.add_shader_pass('ssgi_blur_pass') if rpdat.rp_renderer == 'Deferred': assets.add_shader_pass('deferred_indirect') assets.add_shader_pass('deferred_light') assets.add_shader_pass('deferred_light_quad') if rpdat.rp_volumetriclight: assets.add_khafile_def('rp_volumetriclight') assets.add_shader_pass('volumetric_light_quad') assets.add_shader_pass('volumetric_light') assets.add_shader_pass('blur_bilat_pass') assets.add_shader_pass('blur_bilat_blend_pass') assets.add(assets_path + 'blue_noise64.png') assets.add_embedded_data('blue_noise64.png') if rpdat.rp_decals: assets.add_khafile_def('rp_decals') if rpdat.rp_ocean: assets.add_khafile_def('rp_ocean') assets.add_shader_pass('water_pass') if rpdat.rp_blending: assets.add_khafile_def('rp_blending') if rpdat.rp_bloom: assets.add_khafile_def('rp_bloom') assets.add_shader_pass('bloom_pass') assets.add_shader_pass('blur_gaus_pass') if rpdat.rp_sss: assets.add_khafile_def('rp_sss') wrd.world_defs += '_SSS' assets.add_shader_pass('sss_pass') if rpdat.rp_ssr: assets.add_khafile_def('rp_ssr') assets.add_shader_pass('ssr_pass') assets.add_shader_pass('blur_adaptive_pass') if rpdat.arm_ssr_half_res: assets.add_khafile_def('rp_ssr_half') if rpdat.rp_ssr_z_only: wrd.world_defs += '_SSRZOnly' if rpdat.rp_motionblur != 'Off': assets.add_khafile_def('rp_motionblur={0}'.format(rpdat.rp_motionblur)) assets.add_shader_pass('copy_pass') if rpdat.rp_motionblur == 'Camera': assets.add_shader_pass('motion_blur_pass') else: assets.add_shader_pass('motion_blur_veloc_pass') if rpdat.rp_compositornodes and rpdat.rp_autoexposure: assets.add_khafile_def('rp_autoexposure') if rpdat.rp_dynres: assets.add_khafile_def('rp_dynres') if rpdat.arm_soft_shadows == 'On': if rpdat.rp_shadowmap_cascades == '1': assets.add_shader_pass('dilate_pass') assets.add_shader_pass('visibility_pass') assets.add_shader_pass('blur_shadow_pass') assets.add_khafile_def('rp_soft_shadows') wrd.world_defs += '_SoftShadows' if rpdat.arm_soft_shadows_penumbra != 1: wrd.world_defs += '_PenumbraScale' else: log.warn( 'Disabling soft shadows - "Armory Render Path - Cascades" requires to be set to 1 for now' ) gbuffer2_direct = '_SSS' in wrd.world_defs or '_Hair' in wrd.world_defs or rpdat.arm_voxelgi_refraction gbuffer2 = '_Veloc' in wrd.world_defs or gbuffer2_direct if gbuffer2: assets.add_khafile_def('rp_gbuffer2') wrd.world_defs += '_gbuffer2' if gbuffer2_direct: assets.add_khafile_def('rp_gbuffer2_direct') wrd.world_defs += '_gbuffer2direct' if callback != None: callback()
def write_khafilejs(is_play, export_physics, export_navigation, export_ui, is_publish, enable_dce, is_viewport, import_traits, import_logicnodes): sdk_path = arm.utils.get_sdk_path() rel_path = arm.utils.get_relative_paths() # Convert absolute paths to relative wrd = bpy.data.worlds['Arm'] with open('khafile.js', 'w') as f: f.write( """// Auto-generated let project = new Project('""" + arm.utils.safestr(wrd.arm_project_name) + """'); project.addSources('Sources'); """) # Auto-add assets located in Bundled directory if os.path.exists('Bundled'): for file in glob.glob("Bundled/**", recursive=True): if os.path.isfile(file): assets.add(file) if os.path.exists('Shaders'): # Copy to enable includes if os.path.exists(arm.utils.build_dir() + '/compiled/Shaders/Project'): shutil.rmtree(arm.utils.build_dir() + '/compiled/Shaders/Project', onerror=remove_readonly) shutil.copytree('Shaders', arm.utils.build_dir() + '/compiled/Shaders/Project') f.write("project.addShaders('" + arm.utils.build_dir() + "/compiled/Shaders/Project/**');\n") # for file in glob.glob("Shaders/**", recursive=True): # if os.path.isfile(file): # assets.add_shader(file) if not os.path.exists('Libraries/armory'): f.write(add_armory_library(sdk_path, 'armory', rel_path=rel_path)) if not os.path.exists('Libraries/iron'): f.write(add_armory_library(sdk_path, 'iron', rel_path=rel_path)) # Project libraries if os.path.exists('Libraries'): libs = os.listdir('Libraries') for lib in libs: if os.path.isdir('Libraries/' + lib): f.write('project.addLibrary("{0}");\n'.format(lib.replace('//', '/'))) # Subprojects, merge this with libraries if os.path.exists('Subprojects'): libs = os.listdir('Subprojects') for lib in libs: if os.path.isdir('Subprojects/' + lib): f.write('await project.addProject("Subprojects/{0}");\n'.format(lib)) if wrd.arm_audio == 'Disabled': assets.add_khafile_def('arm_no_audio') assets.add_khafile_def('kha_no_ogg') if export_physics: assets.add_khafile_def('arm_physics') if wrd.arm_physics_engine == 'Bullet': assets.add_khafile_def('arm_bullet') if not os.path.exists('Libraries/haxebullet'): f.write(add_armory_library(sdk_path + '/lib/', 'haxebullet', rel_path=rel_path)) if state.target.startswith('krom') or state.target == 'html5' or state.target == 'node': ammojs_path = sdk_path + '/lib/haxebullet/js/ammo/ammo.js' ammojs_path = ammojs_path.replace('\\', '/').replace('//', '/') f.write(add_assets(ammojs_path, rel_path=rel_path)) # haxe.macro.Compiler.includeFile(ammojs_path) elif wrd.arm_physics_engine == 'Oimo': assets.add_khafile_def('arm_oimo') if not os.path.exists('Libraries/oimo'): f.write(add_armory_library(sdk_path + '/lib/', 'oimo', rel_path=rel_path)) if export_navigation: assets.add_khafile_def('arm_navigation') if not os.path.exists('Libraries/haxerecast'): f.write(add_armory_library(sdk_path + '/lib/', 'haxerecast', rel_path=rel_path)) if state.target.startswith('krom') or state.target == 'html5': recastjs_path = sdk_path + '/lib/haxerecast/js/recast/recast.js' recastjs_path = recastjs_path.replace('\\', '/').replace('//', '/') f.write(add_assets(recastjs_path, rel_path=rel_path)) if is_publish: assets.add_khafile_def('arm_published') if wrd.arm_asset_compression: assets.add_khafile_def('arm_compress') else: pass # f.write("""project.addParameter("--macro include('armory.trait')");\n""") # f.write("""project.addParameter("--macro include('armory.trait.internal')");\n""") # if export_physics: # f.write("""project.addParameter("--macro include('armory.trait.physics')");\n""") # if wrd.arm_physics_engine == 'Bullet': # f.write("""project.addParameter("--macro include('armory.trait.physics.bullet')");\n""") # else: # f.write("""project.addParameter("--macro include('armory.trait.physics.oimo')");\n""") # if export_navigation: # f.write("""project.addParameter("--macro include('armory.trait.navigation')");\n""") # if import_logicnodes: # Live patching for logic nodes # f.write("""project.addParameter("--macro include('armory.logicnode')");\n""") if not wrd.arm_compiler_inline: f.write("project.addParameter('--no-inline');\n") if enable_dce: f.write("project.addParameter('-dce full');\n") if is_viewport or wrd.arm_debug_console: import_traits.append('armory.trait.internal.Bridge') import_traits = list(set(import_traits)) for i in range(0, len(import_traits)): f.write("project.addParameter('" + import_traits[i] + "');\n") f.write("""project.addParameter("--macro keep('""" + import_traits[i] + """')");\n""") shaderload = state.target == 'krom' or state.target == 'html5' if wrd.arm_cache_build and shaderload and not is_publish: # Load shaders manually assets.add_khafile_def('arm_shaderload') shaders_path = arm.utils.build_dir() + '/compiled/Shaders/*.glsl' if rel_path: shaders_path = os.path.relpath(shaders_path, arm.utils.get_fp()).replace('\\', '/') f.write('project.addShaders("' + shaders_path + '");\n') if arm.utils.get_gapi() == 'direct3d11': # noprocessing flag - gets renamed to .d3d11 shaders_path = arm.utils.build_dir() + '/compiled/Hlsl/*.glsl' if rel_path: shaders_path = os.path.relpath(shaders_path, arm.utils.get_fp()).replace('\\', '/') f.write('project.addShaders("' + shaders_path + '", { noprocessing: true });\n') # Move assets for published game to /data folder use_data_dir = is_publish and (state.target == 'krom-windows' or state.target == 'krom-linux' or state.target == 'windows' or state.target == 'linux') if use_data_dir: assets.add_khafile_def('arm_data_dir') ext = 'arm' if wrd.arm_minimize else 'json' assets_path = arm.utils.build_dir() + '/compiled/Assets/**' assets_path_sh = arm.utils.build_dir() + '/compiled/Shaders/*.' + ext if rel_path: assets_path = os.path.relpath(assets_path, arm.utils.get_fp()).replace('\\', '/') assets_path_sh = os.path.relpath(assets_path_sh, arm.utils.get_fp()).replace('\\', '/') dest = '' if use_data_dir: dest += ', destination: "data/{name}"' f.write('project.addAssets("' + assets_path + '", { notinlist: true ' + dest + '});\n') f.write('project.addAssets("' + assets_path_sh + '", { notinlist: true ' + dest + '});\n') shader_data_references = sorted(list(set(assets.shader_datas))) for ref in shader_data_references: ref = ref.replace('\\', '/').replace('//', '/') if '/compiled/' in ref: # Asset already included continue f.write(add_assets(ref, use_data_dir=use_data_dir, rel_path=rel_path)) asset_references = sorted(list(set(assets.assets))) for ref in asset_references: ref = ref.replace('\\', '/').replace('//', '/') if '/compiled/' in ref: # Asset already included continue quality = 1.0 s = ref.lower() if s.endswith('.wav'): quality = wrd.arm_sound_quality elif s.endswith('.png') or s.endswith('.jpg'): quality = wrd.arm_texture_quality f.write(add_assets(ref, quality=quality, use_data_dir=use_data_dir, rel_path=rel_path)) if wrd.arm_sound_quality < 1.0 or state.target == 'html5': assets.add_khafile_def('arm_soundcompress') if wrd.arm_texture_quality < 1.0: assets.add_khafile_def('arm_texcompress') if wrd.arm_debug_console: assets.add_khafile_def('arm_debug') f.write(add_shaders(sdk_path + "/armory/Shaders/debug_draw/**", rel_path=rel_path)) if export_ui: if not os.path.exists('Libraries/zui'): f.write(add_armory_library(sdk_path, 'lib/zui', rel_path=rel_path)) p = sdk_path + '/armory/Assets/font_default.ttf' p = p.replace('//', '/') f.write(add_assets(p.replace('\\', '/'), use_data_dir=use_data_dir, rel_path=rel_path)) assets.add_khafile_def('arm_ui') if wrd.arm_hscript == 'Enabled': if not os.path.exists('Libraries/hscript'): f.write(add_armory_library(sdk_path, 'lib/hscript', rel_path=rel_path)) assets.add_khafile_def('arm_hscript') if wrd.arm_formatlib == 'Enabled': if not os.path.exists('Libraries/iron_format'): f.write(add_armory_library(sdk_path, 'lib/iron_format', rel_path=rel_path)) if wrd.arm_minimize == False: assets.add_khafile_def('arm_json') if wrd.arm_deinterleaved_buffers == True: assets.add_khafile_def('arm_deinterleaved') if wrd.arm_batch_meshes == True: assets.add_khafile_def('arm_batch') if wrd.arm_stream_scene: assets.add_khafile_def('arm_stream') rpdat = arm.utils.get_rp() if rpdat.arm_skin == 'CPU': assets.add_khafile_def('arm_skin_cpu') elif rpdat.arm_skin == 'GPU (Matrix)': assets.add_khafile_def('arm_skin_mat') if rpdat.arm_skin != 'Off': assets.add_khafile_def('arm_skin') if rpdat.arm_particles == 'GPU': assets.add_khafile_def('arm_particles_gpu') if rpdat.arm_particles != 'Off': assets.add_khafile_def('arm_particles') if rpdat.rp_draw_order == 'Distance': assets.add_khafile_def('arm_draworder_dist') if arm.utils.get_viewport_controls() == 'azerty': assets.add_khafile_def('arm_azerty') if os.path.exists(arm.utils.get_fp() + '/Bundled/config.arm'): assets.add_khafile_def('arm_config') if is_publish and wrd.arm_loadscreen: assets.add_khafile_def('arm_loadscreen') # if bpy.data.scenes[0].unit_settings.system_rotation == 'DEGREES': # assets.add_khafile_def('arm_degrees') assets.add_khafile_def('arm_fast') for d in assets.khafile_defs: f.write("project.addDefine('" + d + "');\n") khafile_text = wrd.arm_khafile if khafile_text != '': f.write(bpy.data.texts[khafile_text].as_string()) if state.target.startswith('android-native'): bundle = 'org.armory3d.' + wrd.arm_project_package if wrd.arm_project_bundle == '' else wrd.arm_project_bundle f.write("project.targetOptions.android_native.package = '{0}';\n".format(arm.utils.safestr(bundle))) if wrd.arm_winorient != 'Multi': f.write("project.targetOptions.android_native.screenOrientation = '{0}';\n".format(wrd.arm_winorient.lower())) elif state.target.startswith('ios'): bundle = 'org.armory3d.' + wrd.arm_project_package if wrd.arm_project_bundle == '' else wrd.arm_project_bundle f.write("project.targetOptions.ios.bundle = '{0}';\n".format(arm.utils.safestr(bundle))) if wrd.arm_project_icon != '': shutil.copy(bpy.path.abspath(wrd.arm_project_icon), arm.utils.get_fp() + '/icon.png') f.write("\n\nresolve(project);\n")
def export_data(fp, sdk_path, is_play=False, is_publish=False, in_viewport=False): global exporter wrd = bpy.data.worlds['Arm'] print('\nArmory v' + wrd.arm_version) print('OS: ' + arm.utils.get_os() + ', Target: ' + state.target + ', GAPI: ' + arm.utils.get_gapi()) # Clean compiled variants if cache is disabled build_dir = arm.utils.build_dir() if wrd.arm_cache_shaders == False: if os.path.isdir(build_dir + '/build/html5-resources'): shutil.rmtree(build_dir + '/build/html5-resources', onerror=remove_readonly) if os.path.isdir(build_dir + '/build/krom-resources'): shutil.rmtree(build_dir + '/build/krom-resources', onerror=remove_readonly) if os.path.isdir(build_dir + '/windowed/krom-resources'): shutil.rmtree(build_dir + '/windowed/krom-resources', onerror=remove_readonly) if os.path.isdir(build_dir + '/compiled/Shaders'): shutil.rmtree(build_dir + '/compiled/Shaders', onerror=remove_readonly) if os.path.isdir(build_dir + '/compiled/ShaderRaws'): shutil.rmtree(build_dir + '/compiled/ShaderRaws', onerror=remove_readonly) # Detect camera plane changes if len(bpy.data.cameras) > 0: cam = bpy.data.cameras[0] if state.last_clip_start == 0: state.last_clip_start = cam.clip_start state.last_clip_end = cam.clip_end elif cam.clip_start != state.last_clip_start or cam.clip_end != state.last_clip_end: if os.path.isdir(build_dir + '/compiled/Shaders'): shutil.rmtree(build_dir + '/compiled/Shaders', onerror=remove_readonly) state.last_clip_start = cam.clip_start state.last_clip_end = cam.clip_end raw_shaders_path = sdk_path + 'armory/Shaders/' assets_path = sdk_path + 'armory/Assets/' export_physics = bpy.data.worlds['Arm'].arm_physics != 'Disabled' export_navigation = bpy.data.worlds['Arm'].arm_navigation != 'Disabled' export_ui = bpy.data.worlds['Arm'].arm_ui != 'Disabled' assets.reset() # Build node trees # TODO: cache make_logic.build_node_trees() active_worlds = set() for scene in bpy.data.scenes: if scene.arm_export and scene.world != None: active_worlds.add(scene.world) world_outputs = make_world.build_node_trees(active_worlds) make_renderpath.build_node_trees(assets_path) for wout in world_outputs: make_world.write_output(wout) # Export scene data assets.embedded_data = sorted(list(set(assets.embedded_data))) physics_found = False navigation_found = False ui_found = False ArmoryExporter.compress_enabled = is_publish and wrd.arm_asset_compression ArmoryExporter.in_viewport = in_viewport ArmoryExporter.import_traits = [] for scene in bpy.data.scenes: if scene.arm_export: ext = '.zip' if (scene.arm_compress and is_publish) else '.arm' asset_path = arm.utils.build_dir( ) + '/compiled/Assets/' + arm.utils.safestr(scene.name) + ext exporter.execute(bpy.context, asset_path, scene=scene, write_capture_info=state.is_render_anim, play_area=state.play_area) if ArmoryExporter.export_physics: physics_found = True if ArmoryExporter.export_navigation: navigation_found = True if ArmoryExporter.export_ui: ui_found = True assets.add(asset_path) if physics_found == False: # Disable physics if no rigid body is exported export_physics = False if navigation_found == False: export_navigation = False if ui_found == False: export_ui = False if wrd.arm_ui == 'Enabled': export_ui = True modules = [] if export_physics: modules.append('physics') if export_navigation: modules.append('navigation') if export_ui: modules.append('ui') print('Exported modules: ' + str(modules)) # Write referenced shader variants for ref in assets.shader_datas: # Data does not exist yet if not os.path.isfile(fp + '/' + ref): shader_name = ref.split('/')[ 3] # Extract from 'build/compiled/...' defs = make_utils.def_strings_to_array(wrd.world_defs) if shader_name.startswith('compositor_pass'): defs += make_utils.def_strings_to_array(wrd.compo_defs) elif shader_name.startswith('grease_pencil'): defs = [] compile_shader(raw_shaders_path, shader_name, defs) # Reset path os.chdir(fp) # Copy std shaders if not os.path.isdir(arm.utils.build_dir() + '/compiled/Shaders/std'): shutil.copytree(raw_shaders_path + 'std', arm.utils.build_dir() + '/compiled/Shaders/std') # Write compiled.glsl write_data.write_compiledglsl() # Write khafile.js enable_dce = is_publish and wrd.arm_dce import_logic = not is_publish and arm.utils.logic_editor_space() != None write_data.write_khafilejs(is_play, export_physics, export_navigation, export_ui, is_publish, enable_dce, in_viewport, ArmoryExporter.import_traits, import_logic) # Write Main.hx - depends on write_khafilejs for writing number of assets resx, resy = arm.utils.get_render_resolution(arm.utils.get_active_scene()) # Import all logic nodes for patching if logic is being edited write_data.write_main(resx, resy, is_play, in_viewport, is_publish) if resx != state.last_resx or resy != state.last_resy: wrd.arm_recompile = True state.last_resx = resx state.last_resy = resy
def make_rpath(): assets_path = arm.utils.get_sdk_path() + 'armory/Assets/' wrd = bpy.data.worlds['Arm'] rpdat = arm.utils.get_rp() if rpdat.rp_hdr: assets.add_khafile_def('rp_hdr') else: wrd.world_defs += '_LDR' if rpdat.rp_shadows: wrd.world_defs += '_ShadowMap' assets.add_khafile_def('rp_shadowmap') assets.add_khafile_def('rp_shadowmap_cascade={0}'.format(rpdat.rp_shadowmap_cascade)) assets.add_khafile_def('rp_shadowmap_cube={0}'.format(rpdat.rp_shadowmap_cube)) assets.add_khafile_def('rp_background={0}'.format(rpdat.rp_background)) if rpdat.rp_background == 'World': assets.add_shader_pass('world_pass') if '_EnvClouds' in wrd.world_defs: assets.add(assets_path + 'noise256.png') assets.add_embedded_data('noise256.png') if rpdat.rp_render_to_texture: assets.add_khafile_def('rp_render_to_texture') if rpdat.rp_compositornodes: assets.add_khafile_def('rp_compositornodes') compo_depth = False if rpdat.arm_tonemap != 'Off': wrd.compo_defs = '_CTone' + rpdat.arm_tonemap if rpdat.rp_antialiasing == 'FXAA': wrd.compo_defs += '_CFXAA' if rpdat.arm_letterbox: wrd.compo_defs += '_CLetterbox' if rpdat.arm_grain: wrd.compo_defs += '_CGrain' if bpy.data.scenes[0].cycles.film_exposure != 1.0: wrd.compo_defs += '_CExposure' if rpdat.arm_fog: wrd.compo_defs += '_CFog' compo_depth = True if len(bpy.data.cameras) > 0 and bpy.data.cameras[0].dof_distance > 0.0: wrd.compo_defs += '_CDOF' compo_depth = True if compo_depth: wrd.compo_defs += '_CDepth' assets.add_khafile_def('rp_compositordepth') if rpdat.arm_lens_texture != '': wrd.compo_defs += '_CLensTex' assets.add_embedded_data('lenstexture.jpg') if rpdat.arm_fisheye: wrd.compo_defs += '_CFishEye' if rpdat.arm_vignette: wrd.compo_defs += '_CVignette' if rpdat.arm_lensflare: wrd.compo_defs += '_CGlare' if rpdat.arm_lut_texture != '': wrd.compo_defs += '_CLUT' assets.add_embedded_data('luttexture.jpg') if '_CDOF' in wrd.compo_defs or '_CFXAA' in wrd.compo_defs or '_CSharpen' in wrd.compo_defs: wrd.compo_defs += '_CTexStep' if '_CDOF' in wrd.compo_defs or '_CFog' in wrd.compo_defs or '_CGlare' in wrd.compo_defs: wrd.compo_defs += '_CCameraProj' assets.add_shader_pass('compositor_pass') else: assets.add_shader_pass('copy_pass') assets.add_khafile_def('rp_antialiasing={0}'.format(rpdat.rp_antialiasing)) if rpdat.rp_antialiasing == 'SMAA' or rpdat.rp_antialiasing == 'TAA': assets.add_shader_pass('smaa_edge_detect') assets.add_shader_pass('smaa_blend_weight') assets.add_shader_pass('smaa_neighborhood_blend') assets.add(assets_path + 'smaa_area.png') assets.add(assets_path + 'smaa_search.png') assets.add_embedded_data('smaa_area.png') assets.add_embedded_data('smaa_search.png') wrd.world_defs += '_SMAA' if rpdat.rp_antialiasing == 'TAA': assets.add_shader_pass('taa_pass') assets.add_shader_pass('copy_pass') if rpdat.rp_antialiasing == 'TAA' or rpdat.rp_motionblur == 'Object': assets.add_khafile_def('arm_veloc') wrd.world_defs += '_Veloc' if rpdat.rp_antialiasing == 'TAA': assets.add_khafile_def('arm_taa') assets.add_khafile_def('rp_supersampling={0}'.format(rpdat.rp_supersampling)) if rpdat.rp_supersampling == '4': assets.add_shader_pass('supersample_resolve') if rpdat.rp_volumetriclight: wrd.world_defs += '_Sun' assets.add_khafile_def('rp_volumetriclight') assets.add_shader_pass('volumetric_light') assets.add_shader_pass('blur_bilat_pass') assets.add_shader_pass('blur_bilat_blend_pass') assets.add(assets_path + 'blue_noise64.png') assets.add_embedded_data('blue_noise64.png') if rpdat.rp_bloom: assets.add_khafile_def('rp_bloom') assets.add_shader_pass('bloom_pass') assets.add_shader_pass('blur_gaus_pass') if rpdat.arm_rp_resolution == 'Custom': assets.add_khafile_def('rp_resolution_filter={0}'.format(rpdat.arm_rp_resolution_filter))
def export_data(fp, sdk_path, is_play=False, is_publish=False, in_viewport=False): global exporter wrd = bpy.data.worlds['Arm'] print('\nArmory v{0} ({1})'.format(wrd.arm_version, wrd.arm_commit)) print('OS: ' + arm.utils.get_os() + ', Target: ' + state.target + ', GAPI: ' + arm.utils.get_gapi()) # Clean compiled variants if cache is disabled build_dir = arm.utils.get_fp_build() if wrd.arm_cache_shaders == False: if os.path.isdir(build_dir + '/debug/html5-resources'): shutil.rmtree(build_dir + '/debug/html5-resources', onerror=remove_readonly) if os.path.isdir(build_dir + '/krom-resources'): shutil.rmtree(build_dir + '/krom-resources', onerror=remove_readonly) if os.path.isdir(build_dir + '/debug/krom-resources'): shutil.rmtree(build_dir + '/debug/krom-resources', onerror=remove_readonly) if os.path.isdir(build_dir + '/windows-resources'): shutil.rmtree(build_dir + '/windows-resources', onerror=remove_readonly) if os.path.isdir(build_dir + '/linux-resources'): shutil.rmtree(build_dir + '/linux-resources', onerror=remove_readonly) if os.path.isdir(build_dir + '/osx-resources'): shutil.rmtree(build_dir + '/osx-resources', onerror=remove_readonly) if os.path.isdir(build_dir + '/compiled/Shaders'): shutil.rmtree(build_dir + '/compiled/Shaders', onerror=remove_readonly) # Detect camera plane changes if len(bpy.data.cameras) > 0: cam = bpy.data.cameras[0] if state.last_clip_start == 0: state.last_clip_start = cam.clip_start state.last_clip_end = cam.clip_end elif cam.clip_start != state.last_clip_start or cam.clip_end != state.last_clip_end: if os.path.isdir(build_dir + '/compiled/Shaders'): shutil.rmtree(build_dir + '/compiled/Shaders', onerror=remove_readonly) state.last_clip_start = cam.clip_start state.last_clip_end = cam.clip_end raw_shaders_path = sdk_path + 'armory/Shaders/' assets_path = sdk_path + 'armory/Assets/' export_physics = bpy.data.worlds['Arm'].arm_physics != 'Disabled' export_navigation = bpy.data.worlds['Arm'].arm_navigation != 'Disabled' export_ui = bpy.data.worlds['Arm'].arm_ui != 'Disabled' assets.reset() # Build node trees ArmoryExporter.import_traits = [] make_logic.build() make_world.build() make_renderpath.build() # Export scene data assets.embedded_data = sorted(list(set(assets.embedded_data))) physics_found = False navigation_found = False ui_found = False ArmoryExporter.compress_enabled = is_publish and wrd.arm_asset_compression ArmoryExporter.in_viewport = in_viewport for scene in bpy.data.scenes: if scene.arm_export: ext = '.zip' if (scene.arm_compress and is_publish) else '.arm' asset_path = build_dir + '/compiled/Assets/' + arm.utils.safestr(scene.name) + ext exporter.execute(bpy.context, asset_path, scene=scene, write_capture_info=state.is_render_anim, play_area=state.play_area) if ArmoryExporter.export_physics: physics_found = True if ArmoryExporter.export_navigation: navigation_found = True if ArmoryExporter.export_ui: ui_found = True assets.add(asset_path) if physics_found == False: # Disable physics if no rigid body is exported export_physics = False if navigation_found == False: export_navigation = False if ui_found == False: export_ui = False if wrd.arm_ui == 'Enabled': export_ui = True modules = [] if export_physics: modules.append('physics') if export_navigation: modules.append('navigation') if export_ui: modules.append('ui') print('Exported modules: ' + str(modules)) defs = arm.utils.def_strings_to_array(wrd.world_defs) print('Shader flags: ' + str(defs)) # Write referenced shader passes path = build_dir + '/compiled/Shaders' if not os.path.isfile(build_dir + '/compiled/Shaders/shader_datas.arm') or state.last_world_defs != wrd.world_defs: path = build_dir + '/compiled/Shaders' if not os.path.exists(path): os.makedirs(path) res = {} res['shader_datas'] = [] for ref in assets.shader_passes: # Ensure shader pass source exists if not os.path.exists(raw_shaders_path + '/' + ref): continue assets.shader_passes_assets[ref] = [] if ref.startswith('compositor_pass'): cdefs = arm.utils.def_strings_to_array(wrd.compo_defs) compile_shader_pass(res, raw_shaders_path, ref, defs + cdefs) # elif ref.startswith('grease_pencil'): # compile_shader_pass(res, raw_shaders_path, ref, []) else: compile_shader_pass(res, raw_shaders_path, ref, defs) arm.utils.write_arm(path + '/shader_datas.arm', res) for ref in assets.shader_passes: for s in assets.shader_passes_assets[ref]: assets.add_shader(path + '/' + s + '.glsl') state.last_world_defs = wrd.world_defs # Reset path os.chdir(fp) # Copy std shaders if not os.path.isdir(build_dir + '/compiled/Shaders/std'): shutil.copytree(raw_shaders_path + 'std', build_dir + '/compiled/Shaders/std') # Write compiled.glsl write_data.write_compiledglsl() # Write khafile.js enable_dce = is_publish and wrd.arm_dce import_logic = not is_publish and arm.utils.logic_editor_space() != None write_data.write_khafilejs(is_play, export_physics, export_navigation, export_ui, is_publish, enable_dce, in_viewport, ArmoryExporter.import_traits, import_logic) # Write Main.hx - depends on write_khafilejs for writing number of assets scene_name = arm.utils.get_project_scene_name() resx, resy = arm.utils.get_render_resolution(arm.utils.get_active_scene()) # Import all logic nodes for patching if logic is being edited write_data.write_main(scene_name, resx, resy, is_play, in_viewport, is_publish) if scene_name != state.last_scene or resx != state.last_resx or resy != state.last_resy: wrd.arm_recompile = True state.last_resx = resx state.last_resy = resy state.last_scene = scene_name
def write_khafilejs(is_play, export_physics: bool, export_navigation: bool, export_ui: bool, is_publish: bool, import_traits: List[str]) -> None: wrd = bpy.data.worlds['Arm'] sdk_path = arm.utils.get_sdk_path() rel_path = arm.utils.get_relative_paths() # Convert absolute paths to relative project_path = arm.utils.get_fp() build_dir = arm.utils.build_dir() # Whether to use relative paths for paths inside the SDK do_relpath_sdk = rel_path and on_same_drive(sdk_path, project_path) with open('khafile.js', 'w', encoding="utf-8") as khafile: khafile.write( """// Auto-generated let project = new Project('""" + arm.utils.safesrc(wrd.arm_project_name + '-' + wrd.arm_project_version) + """'); project.addSources('Sources'); """) # Auto-add assets located in Bundled directory if os.path.exists('Bundled'): for file in glob.glob("Bundled/**", recursive=True): if os.path.isfile(file): assets.add(file) # Auto-add shape key textures if exists if os.path.exists('MorphTargets'): for file in glob.glob("MorphTargets/**", recursive=True): if os.path.isfile(file): assets.add(file) # Add project shaders if os.path.exists('Shaders'): # Copy to enable includes shader_path = os.path.join(build_dir, 'compiled', 'Shaders', 'Project') if os.path.exists(shader_path): shutil.rmtree(shader_path, onerror=remove_readonly) shutil.copytree('Shaders', shader_path) khafile.write("project.addShaders('" + build_dir + "/compiled/Shaders/Project/**');\n") # for file in glob.glob("Shaders/**", recursive=True): # if os.path.isfile(file): # assets.add_shader(file) # Add engine sources if the project does not use its own armory/iron versions if not os.path.exists(os.path.join('Libraries', 'armory')): khafile.write(add_armory_library(sdk_path, 'armory', rel_path=do_relpath_sdk)) if not os.path.exists(os.path.join('Libraries', 'iron')): khafile.write(add_armory_library(sdk_path, 'iron', rel_path=do_relpath_sdk)) # Project libraries if os.path.exists('Libraries'): libs = os.listdir('Libraries') for lib in libs: if os.path.isdir('Libraries/' + lib): khafile.write('project.addLibrary("{0}");\n'.format(lib.replace('//', '/'))) # Subprojects, merge this with libraries if os.path.exists('Subprojects'): libs = os.listdir('Subprojects') for lib in libs: if os.path.isdir('Subprojects/' + lib): khafile.write('await project.addProject("Subprojects/{0}");\n'.format(lib)) if state.target.startswith('krom'): assets.add_khafile_def('js-es=6') if export_physics: assets.add_khafile_def('arm_physics') if wrd.arm_physics_engine == 'Bullet': assets.add_khafile_def('arm_bullet') if not os.path.exists('Libraries/haxebullet'): khafile.write(add_armory_library(sdk_path + '/lib/', 'haxebullet', rel_path=do_relpath_sdk)) if state.target.startswith('krom'): ammojs_path = sdk_path + '/lib/haxebullet/ammo/ammo.wasm.js' ammojs_path = ammojs_path.replace('\\', '/').replace('//', '/') khafile.write(add_assets(ammojs_path, rel_path=do_relpath_sdk)) ammojs_wasm_path = sdk_path + '/lib/haxebullet/ammo/ammo.wasm.wasm' ammojs_wasm_path = ammojs_wasm_path.replace('\\', '/').replace('//', '/') khafile.write(add_assets(ammojs_wasm_path, rel_path=do_relpath_sdk)) elif state.target == 'html5' or state.target == 'node': ammojs_path = sdk_path + '/lib/haxebullet/ammo/ammo.js' ammojs_path = ammojs_path.replace('\\', '/').replace('//', '/') khafile.write(add_assets(ammojs_path, rel_path=do_relpath_sdk)) elif wrd.arm_physics_engine == 'Oimo': assets.add_khafile_def('arm_oimo') if not os.path.exists('Libraries/oimo'): khafile.write(add_armory_library(sdk_path + '/lib/', 'oimo', rel_path=do_relpath_sdk)) if export_navigation: assets.add_khafile_def('arm_navigation') if not os.path.exists('Libraries/haxerecast'): khafile.write(add_armory_library(sdk_path + '/lib/', 'haxerecast', rel_path=do_relpath_sdk)) if state.target.startswith('krom') or state.target == 'html5': recastjs_path = sdk_path + '/lib/haxerecast/js/recast/recast.js' recastjs_path = recastjs_path.replace('\\', '/').replace('//', '/') khafile.write(add_assets(recastjs_path, rel_path=do_relpath_sdk)) if is_publish: assets.add_khafile_def('arm_published') if wrd.arm_dce: khafile.write("project.addParameter('-dce full');\n") if wrd.arm_no_traces: khafile.write("project.addParameter('--no-traces');\n") if wrd.arm_asset_compression: assets.add_khafile_def('arm_compress') else: assets.add_khafile_def(f'arm_assert_level={wrd.arm_assert_level}') if wrd.arm_assert_quit: assets.add_khafile_def('arm_assert_quit') # khafile.write("""project.addParameter("--macro include('armory.trait')");\n""") # khafile.write("""project.addParameter("--macro include('armory.trait.internal')");\n""") # if export_physics: # khafile.write("""project.addParameter("--macro include('armory.trait.physics')");\n""") # if wrd.arm_physics_engine == 'Bullet': # khafile.write("""project.addParameter("--macro include('armory.trait.physics.bullet')");\n""") # else: # khafile.write("""project.addParameter("--macro include('armory.trait.physics.oimo')");\n""") # if export_navigation: # khafile.write("""project.addParameter("--macro include('armory.trait.navigation')");\n""") if not wrd.arm_compiler_inline: khafile.write("project.addParameter('--no-inline');\n") use_live_patch = arm.utils.is_livepatch_enabled() if wrd.arm_debug_console or use_live_patch: import_traits.append('armory.trait.internal.Bridge') if use_live_patch: assets.add_khafile_def('arm_patch') # Include all logic node classes so that they can later # get instantiated khafile.write("""project.addParameter("--macro include('armory.logicnode')");\n""") import_traits = list(set(import_traits)) for i in range(0, len(import_traits)): khafile.write("project.addParameter('" + import_traits[i] + "');\n") khafile.write("""project.addParameter("--macro keep('""" + import_traits[i] + """')");\n""") noembed = wrd.arm_cache_build and not is_publish and state.target == 'krom' if noembed: # Load shaders manually assets.add_khafile_def('arm_noembed') noembed = False # TODO: always embed shaders for now, check compatibility with haxe compile server shaders_path = build_dir + '/compiled/Shaders/*.glsl' if rel_path: shaders_path = os.path.relpath(shaders_path, project_path).replace('\\', '/') khafile.write('project.addShaders("' + shaders_path + '", { noembed: ' + str(noembed).lower() + '});\n') if arm.utils.get_gapi() == 'direct3d11': # noprocessing flag - gets renamed to .d3d11 shaders_path = build_dir + '/compiled/Hlsl/*.glsl' if rel_path: shaders_path = os.path.relpath(shaders_path, project_path).replace('\\', '/') khafile.write('project.addShaders("' + shaders_path + '", { noprocessing: true, noembed: ' + str(noembed).lower() + ' });\n') # Move assets for published game to /data folder use_data_dir = is_publish and (state.target == 'krom-windows' or state.target == 'krom-linux' or state.target == 'windows-hl' or state.target == 'linux-hl') if use_data_dir: assets.add_khafile_def('arm_data_dir') ext = 'arm' if wrd.arm_minimize else 'json' assets_path = build_dir + '/compiled/Assets/**' assets_path_sh = build_dir + '/compiled/Shaders/*.' + ext if rel_path: assets_path = os.path.relpath(assets_path, project_path).replace('\\', '/') assets_path_sh = os.path.relpath(assets_path_sh, project_path).replace('\\', '/') dest = '' if use_data_dir: dest += ', destination: "data/{name}"' khafile.write('project.addAssets("' + assets_path + '", { notinlist: true ' + dest + '});\n') khafile.write('project.addAssets("' + assets_path_sh + '", { notinlist: true ' + dest + '});\n') shader_data_references = sorted(list(set(assets.shader_datas))) for ref in shader_data_references: ref = ref.replace('\\', '/').replace('//', '/') if '/compiled/' in ref: # Asset already included continue do_relpath_shaders = rel_path and on_same_drive(ref, project_path) khafile.write(add_assets(ref, use_data_dir=use_data_dir, rel_path=do_relpath_shaders)) asset_references = sorted(list(set(assets.assets))) for ref in asset_references: ref = ref.replace('\\', '/').replace('//', '/') if '/compiled/' in ref: # Asset already included continue quality = 1.0 s = ref.lower() if s.endswith('.wav'): quality = wrd.arm_sound_quality elif s.endswith('.png') or s.endswith('.jpg'): quality = wrd.arm_texture_quality do_relpath_assets = rel_path and on_same_drive(ref, project_path) khafile.write(add_assets(ref, quality=quality, use_data_dir=use_data_dir, rel_path=do_relpath_assets)) if wrd.arm_sound_quality < 1.0 or state.target == 'html5': assets.add_khafile_def('arm_soundcompress') if wrd.arm_audio == 'Disabled': assets.add_khafile_def('kha_no_ogg') else: assets.add_khafile_def('arm_audio') if wrd.arm_texture_quality < 1.0: assets.add_khafile_def('arm_texcompress') if wrd.arm_debug_console: assets.add_khafile_def('arm_debug') khafile.write(add_shaders(sdk_path + "/armory/Shaders/debug_draw/**", rel_path=do_relpath_sdk)) if not is_publish and state.target == 'html5': khafile.write("project.addParameter('--debug');\n") if arm.utils.get_pref_or_default('haxe_times', False): khafile.write("project.addParameter('--times');\n") if export_ui: if not os.path.exists('Libraries/zui'): khafile.write(add_armory_library(sdk_path, 'lib/zui', rel_path=do_relpath_sdk)) p = sdk_path + '/armory/Assets/font_default.ttf' p = p.replace('//', '/') khafile.write(add_assets(p.replace('\\', '/'), use_data_dir=use_data_dir, rel_path=do_relpath_sdk)) assets.add_khafile_def('arm_ui') if not wrd.arm_minimize: assets.add_khafile_def('arm_json') if wrd.arm_deinterleaved_buffers: assets.add_khafile_def('arm_deinterleaved') if wrd.arm_batch_meshes: assets.add_khafile_def('arm_batch') if wrd.arm_stream_scene: assets.add_khafile_def('arm_stream') rpdat = arm.utils.get_rp() if rpdat.arm_skin != 'Off': assets.add_khafile_def('arm_skin') if rpdat.arm_morph_target != 'Off': assets.add_khafile_def('arm_morph_target') if rpdat.arm_particles != 'Off': assets.add_khafile_def('arm_particles') if rpdat.rp_draw_order == 'Shader': assets.add_khafile_def('arm_draworder_shader') if arm.utils.get_viewport_controls() == 'azerty': assets.add_khafile_def('arm_azerty') if os.path.exists(project_path + '/Bundled/config.arm'): assets.add_khafile_def('arm_config') if is_publish and wrd.arm_loadscreen: assets.add_khafile_def('arm_loadscreen') if wrd.arm_winresize or state.target == 'html5': assets.add_khafile_def('arm_resizable') # if bpy.data.scenes[0].unit_settings.system_rotation == 'DEGREES': # assets.add_khafile_def('arm_degrees') # Allow libraries to recognize Armory assets.add_khafile_def('armory') for d in assets.khafile_defs: khafile.write("project.addDefine('" + d + "');\n") for p in assets.khafile_params: khafile.write("project.addParameter('" + p + "');\n") if state.target.startswith('android'): bundle = 'org.armory3d.' + wrd.arm_project_package if wrd.arm_project_bundle == '' else wrd.arm_project_bundle khafile.write("project.targetOptions.android_native.package = '{0}';\n".format(arm.utils.safestr(bundle))) if wrd.arm_winorient != 'Multi': khafile.write("project.targetOptions.android_native.screenOrientation = '{0}';\n".format(wrd.arm_winorient.lower())) # Android SDK Versions khafile.write("project.targetOptions.android_native.compileSdkVersion = '{0}';\n".format(wrd.arm_project_android_sdk_compile)) khafile.write("project.targetOptions.android_native.minSdkVersion = '{0}';\n".format(wrd.arm_project_android_sdk_min)) khafile.write("project.targetOptions.android_native.targetSdkVersion = '{0}';\n".format(wrd.arm_project_android_sdk_target)) # Permissions if len(wrd.arm_exporter_android_permission_list) > 0: perms = '' for item in wrd.arm_exporter_android_permission_list: perm = "'android.permission."+ item.arm_android_permissions + "'" # Checking In if perms.find(perm) == -1: if len(perms) > 0: perms = perms + ', ' + perm else: perms = perm if len(perms) > 0: khafile.write("project.targetOptions.android_native.permissions = [{0}];\n".format(perms)) # Android ABI Filters if len(wrd.arm_exporter_android_abi_list) > 0: abis = '' for item in wrd.arm_exporter_android_abi_list: abi = "'"+ item.arm_android_abi + "'" # Checking In if abis.find(abi) == -1: if len(abis) > 0: abis = abis + ', ' + abi else: abis = abi if len(abis) > 0: khafile.write("project.targetOptions.android_native.abiFilters = [{0}];\n".format(abis)) elif state.target.startswith('ios'): bundle = 'org.armory3d.' + wrd.arm_project_package if wrd.arm_project_bundle == '' else wrd.arm_project_bundle khafile.write("project.targetOptions.ios.bundle = '{0}';\n".format(arm.utils.safestr(bundle))) if wrd.arm_project_icon != '': shutil.copy(bpy.path.abspath(wrd.arm_project_icon), project_path + '/icon.png') if wrd.arm_khafile is not None: khafile.write(wrd.arm_khafile.as_string()) khafile.write("\n\nresolve(project);\n")
def build_node_tree(world): output = {} dat = {} output['material_datas'] = [dat] wname = arm.utils.safestr(world.name) dat['name'] = wname + '_material' context = {} dat['contexts'] = [context] context['name'] = 'world' context['bind_constants'] = [] context['bind_textures'] = [] bpy.data.worlds['Arm'].world_defs = '' # Traverse world node tree output_node = nodes.get_node_by_type(world.node_tree, 'OUTPUT_WORLD') if output_node != None: parse_world_output(world, output_node, context) # Clear to color if no texture or sky is provided wrd = bpy.data.worlds['Arm'] if '_EnvSky' not in wrd.world_defs and '_EnvTex' not in wrd.world_defs: if '_EnvImg' not in wrd.world_defs: wrd.world_defs += '_EnvCol' # Irradiance json file name world.world_envtex_name = wname world.world_envtex_irr_name = wname write_probes.write_color_irradiance(wname, world.world_envtex_color) # Clouds enabled if wrd.generate_clouds: wrd.world_defs += '_EnvClouds' # Percentage closer soft shadows if wrd.generate_pcss_state == 'On': wrd.world_defs += '_PCSS' sdk_path = arm.utils.get_sdk_path() assets.add(sdk_path + 'armory/Assets/noise64.png') assets.add_embedded_data('noise64.png') # Screen-space ray-traced shadows if wrd.generate_ssrs: wrd.world_defs += '_SSRS' if wrd.generate_two_sided_area_lamp: wrd.world_defs += '_TwoSidedAreaLamp' # Alternative models if wrd.lighting_model == 'Cycles': wrd.world_defs += '_Cycles' # TODO: Lamp texture test.. if wrd.generate_lamp_texture != '': bpy.data.worlds['Arm'].world_defs += '_LampColTex' if not wrd.generate_lamp_falloff: bpy.data.worlds['Arm'].world_defs += '_NoLampFalloff' voxelgi = False for cam in bpy.data.cameras: if cam.is_probe: wrd.world_defs += '_Probes' if cam.rp_shadowmap == 'None': wrd.world_defs += '_NoShadows' assets.add_khafile_def('arm_no_shadows') if cam.rp_voxelgi: voxelgi = True if cam.rp_dfrs: wrd.world_defs += '_DFRS' assets.add_khafile_def('arm_sdf') if cam.rp_dfao: wrd.world_defs += '_DFAO' assets.add_khafile_def('arm_sdf') if voxelgi: assets.add_khafile_def('arm_voxelgi') if wrd.voxelgi_revoxelize: assets.add_khafile_def('arm_voxelgi_revox') if wrd.voxelgi_multibounce: wrd.world_defs += '_VoxelGIMulti' wrd.world_defs += '_VoxelGI' wrd.world_defs += '_Rad' # Always do radiance for voxels wrd.world_defs += '_Irr' if arm.utils.get_gapi().startswith( 'direct3d'): # Flip Y axis in drawQuad command wrd.world_defs += '_InvY' # Area lamps for lamp in bpy.data.lamps: if lamp.type == 'AREA': wrd.world_defs += '_PolyLight' break # Data will be written after render path has been processed to gather all defines return output
def parse_color(world, node, context, envmap_strength_const): wrd = bpy.data.worlds['Arm'] # Env map included if node.type == 'TEX_ENVIRONMENT' and node.image != None: image = node.image filepath = image.filepath if image.packed_file == None and not os.path.isfile( arm.utils.asset_path(filepath)): log.warn(world.name + ' - unable to open ' + image.filepath) return tex = {} context['bind_textures'].append(tex) tex['name'] = 'envmap' tex['u_addressing'] = 'clamp' tex['v_addressing'] = 'clamp' # Reference image name tex['file'] = arm.utils.extract_filename(image.filepath) base = tex['file'].rsplit('.', 1) ext = base[1].lower() if ext == 'hdr': target_format = 'HDR' else: target_format = 'JPEG' do_convert = ext != 'hdr' and ext != 'jpg' if do_convert: if ext == 'exr': tex['file'] = base[0] + '.hdr' target_format = 'HDR' else: tex['file'] = base[0] + '.jpg' target_format = 'JPEG' if image.packed_file != None: # Extract packed data unpack_path = arm.utils.get_fp_build( ) + '/compiled/Assets/unpacked' if not os.path.exists(unpack_path): os.makedirs(unpack_path) unpack_filepath = unpack_path + '/' + tex['file'] filepath = unpack_filepath if do_convert: if not os.path.isfile(unpack_filepath): arm.utils.write_image(image, unpack_filepath, file_format=target_format) elif os.path.isfile(unpack_filepath) == False or os.path.getsize( unpack_filepath) != image.packed_file.size: with open(unpack_filepath, 'wb') as f: f.write(image.packed_file.data) assets.add(unpack_filepath) else: if do_convert: converted_path = arm.utils.get_fp_build( ) + '/compiled/Assets/unpacked/' + tex['file'] filepath = converted_path # TODO: delete cache when file changes if not os.path.isfile(converted_path): arm.utils.write_image(image, converted_path, file_format=target_format) assets.add(converted_path) else: # Link image path to assets assets.add(arm.utils.asset_path(image.filepath)) # Generate prefiltered envmaps world.world_envtex_name = tex['file'] world.world_envtex_irr_name = tex['file'].rsplit('.', 1)[0] disable_hdr = target_format == 'JPEG' mip_count = world.world_envtex_num_mips mip_count = write_probes.write_probes( filepath, disable_hdr, mip_count, generate_radiance=wrd.generate_radiance) world.world_envtex_num_mips = mip_count # Append envtex define bpy.data.worlds['Arm'].world_defs += '_EnvTex' # Append LDR define if disable_hdr: bpy.data.worlds['Arm'].world_defs += '_EnvLDR' # Append radiance define if wrd.generate_irradiance and wrd.generate_radiance: bpy.data.worlds['Arm'].world_defs += '_Rad' # Static image background elif node.type == 'TEX_IMAGE': bpy.data.worlds['Arm'].world_defs += '_EnvImg' tex = {} context['bind_textures'].append(tex) tex['name'] = 'envmap' # No repeat for now tex['u_addressing'] = 'clamp' tex['v_addressing'] = 'clamp' image = node.image filepath = image.filepath if image.packed_file != None: # Extract packed data filepath = arm.utils.build_dir() + '/compiled/Assets/unpacked' unpack_path = arm.utils.get_fp() + filepath if not os.path.exists(unpack_path): os.makedirs(unpack_path) unpack_filepath = unpack_path + '/' + image.name if os.path.isfile(unpack_filepath) == False or os.path.getsize( unpack_filepath) != image.packed_file.size: with open(unpack_filepath, 'wb') as f: f.write(image.packed_file.data) assets.add(unpack_filepath) else: # Link image path to assets assets.add(arm.utils.asset_path(image.filepath)) # Reference image name tex['file'] = arm.utils.extract_filename(image.filepath) # Append sky define elif node.type == 'TEX_SKY': # Match to cycles envmap_strength_const['float'] *= 0.1 bpy.data.worlds['Arm'].world_defs += '_EnvSky' # Append sky properties to material const = {} const['name'] = 'sunDirection' sun_direction = [ node.sun_direction[0], node.sun_direction[1], node.sun_direction[2] ] sun_direction[1] *= -1 # Fix Y orientation const['vec3'] = list(sun_direction) context['bind_constants'].append(const) world.world_envtex_sun_direction = sun_direction world.world_envtex_turbidity = node.turbidity world.world_envtex_ground_albedo = node.ground_albedo # Irradiance json file name wname = arm.utils.safestr(world.name) world.world_envtex_irr_name = wname write_probes.write_sky_irradiance(wname) # Radiance if wrd.generate_radiance_sky and wrd.generate_radiance and wrd.generate_irradiance: bpy.data.worlds['Arm'].world_defs += '_Rad' if wrd.generate_radiance_sky_type == 'Hosek': hosek_path = 'armory/Assets/hosek/' else: hosek_path = 'armory/Assets/hosek_fake/' sdk_path = arm.utils.get_sdk_path() # Use fake maps for now assets.add(sdk_path + hosek_path + 'hosek_radiance.hdr') for i in range(0, 8): assets.add(sdk_path + hosek_path + 'hosek_radiance_' + str(i) + '.hdr') world.world_envtex_name = 'hosek' world.world_envtex_num_mips = 8
def parse_tex_image(node: bpy.types.ShaderNodeTexImage, out_socket: bpy.types.NodeSocket, state: ParserState) -> Union[floatstr, vec3str]: if state.context == ParserContext.OBJECT: # Color or Alpha output use_color_out = out_socket == node.outputs[0] # Already fetched if c.is_parsed(c.store_var_name(node)): if use_color_out: return f'{c.store_var_name(node)}.rgb' else: return f'{c.store_var_name(node)}.a' tex_name = c.node_name(node.name) tex = c.make_texture(node, tex_name) tex_link = None tex_default_file = None is_arm_mat_param = None if node.arm_material_param: tex_link = node.name is_arm_mat_param = True if tex is not None: state.curshader.write_textures += 1 if node.arm_material_param and tex['file'] is not None: tex_default_file = tex['file'] if use_color_out: to_linear = node.image is not None and node.image.colorspace_settings.name == 'sRGB' res = f'{c.texture_store(node, tex, tex_name, to_linear, tex_link=tex_link, default_value=tex_default_file, is_arm_mat_param=is_arm_mat_param)}.rgb' else: res = f'{c.texture_store(node, tex, tex_name, tex_link=tex_link, default_value=tex_default_file, is_arm_mat_param=is_arm_mat_param)}.a' state.curshader.write_textures -= 1 return res # Empty texture elif node.image is None: tex = { 'name': tex_name, 'file': '' } if use_color_out: return '{0}.rgb'.format(c.texture_store(node, tex, tex_name, to_linear=False, tex_link=tex_link, is_arm_mat_param=is_arm_mat_param)) return '{0}.a'.format(c.texture_store(node, tex, tex_name, to_linear=True, tex_link=tex_link, is_arm_mat_param=is_arm_mat_param)) # Pink color for missing texture else: tex_store = c.store_var_name(node) if use_color_out: state.parsed.add(tex_store) state.curshader.write_textures += 1 state.curshader.write(f'vec4 {tex_store} = vec4(1.0, 0.0, 1.0, 1.0);') state.curshader.write_textures -= 1 return f'{tex_store}.rgb' else: state.curshader.write(f'vec4 {tex_store} = vec4(1.0, 0.0, 1.0, 1.0);') return f'{tex_store}.a' # World context # TODO: Merge with above implementation to also allow mappings other than using view coordinates else: world = state.world world.world_defs += '_EnvImg' # Background texture state.curshader.add_uniform('sampler2D envmap', link='_envmap') state.curshader.add_uniform('vec2 screenSize', link='_screenSize') image = node.image filepath = image.filepath if image.packed_file is not None: # Extract packed data filepath = arm.utils.build_dir() + '/compiled/Assets/unpacked' unpack_path = arm.utils.get_fp() + filepath if not os.path.exists(unpack_path): os.makedirs(unpack_path) unpack_filepath = unpack_path + '/' + image.name if not os.path.isfile(unpack_filepath) or os.path.getsize(unpack_filepath) != image.packed_file.size: with open(unpack_filepath, 'wb') as f: f.write(image.packed_file.data) assets.add(unpack_filepath) else: # Link image path to assets assets.add(arm.utils.asset_path(image.filepath)) # Reference image name tex_file = arm.utils.extract_filename(image.filepath) base = tex_file.rsplit('.', 1) ext = base[1].lower() if ext == 'hdr': target_format = 'HDR' else: target_format = 'JPEG' # Generate prefiltered envmaps world.arm_envtex_name = tex_file world.arm_envtex_irr_name = tex_file.rsplit('.', 1)[0] disable_hdr = target_format == 'JPEG' from_srgb = image.colorspace_settings.name == "sRGB" rpdat = arm.utils.get_rp() mip_count = world.arm_envtex_num_mips mip_count = write_probes.write_probes(filepath, disable_hdr, from_srgb, mip_count, arm_radiance=rpdat.arm_radiance) world.arm_envtex_num_mips = mip_count # Will have to get rid of gl_FragCoord, pass texture coords from vertex shader state.curshader.write_init('vec2 texco = gl_FragCoord.xy / screenSize;') return 'texture(envmap, vec2(texco.x, 1.0 - texco.y)).rgb * envmapStrength'
def parse_tex_environment(node: bpy.types.ShaderNodeTexEnvironment, out_socket: bpy.types.NodeSocket, state: ParserState) -> vec3str: if state.context == ParserContext.OBJECT: log.warn('Environment Texture node is not supported for object node trees, using default value') return c.to_vec3([0.0, 0.0, 0.0]) if node.image is None: return c.to_vec3([1.0, 0.0, 1.0]) world = state.world world.world_defs += '_EnvTex' curshader = state.curshader curshader.add_include('std/math.glsl') curshader.add_uniform('sampler2D envmap', link='_envmap') image = node.image filepath = image.filepath if image.packed_file is None and not os.path.isfile(arm.utils.asset_path(filepath)): log.warn(world.name + ' - unable to open ' + image.filepath) return c.to_vec3([1.0, 0.0, 1.0]) # Reference image name tex_file = arm.utils.extract_filename(image.filepath) base = tex_file.rsplit('.', 1) ext = base[1].lower() if ext == 'hdr': target_format = 'HDR' else: target_format = 'JPEG' do_convert = ext != 'hdr' and ext != 'jpg' if do_convert: if ext == 'exr': tex_file = base[0] + '.hdr' target_format = 'HDR' else: tex_file = base[0] + '.jpg' target_format = 'JPEG' if image.packed_file is not None: # Extract packed data unpack_path = arm.utils.get_fp_build() + '/compiled/Assets/unpacked' if not os.path.exists(unpack_path): os.makedirs(unpack_path) unpack_filepath = unpack_path + '/' + tex_file filepath = unpack_filepath if do_convert: if not os.path.isfile(unpack_filepath): arm.utils.unpack_image(image, unpack_filepath, file_format=target_format) elif not os.path.isfile(unpack_filepath) or os.path.getsize(unpack_filepath) != image.packed_file.size: with open(unpack_filepath, 'wb') as f: f.write(image.packed_file.data) assets.add(unpack_filepath) else: if do_convert: unpack_path = arm.utils.get_fp_build() + '/compiled/Assets/unpacked' if not os.path.exists(unpack_path): os.makedirs(unpack_path) converted_path = unpack_path + '/' + tex_file filepath = converted_path # TODO: delete cache when file changes if not os.path.isfile(converted_path): arm.utils.convert_image(image, converted_path, file_format=target_format) assets.add(converted_path) else: # Link image path to assets assets.add(arm.utils.asset_path(image.filepath)) rpdat = arm.utils.get_rp() if not state.radiance_written: # Generate prefiltered envmaps world.arm_envtex_name = tex_file world.arm_envtex_irr_name = tex_file.rsplit('.', 1)[0] disable_hdr = target_format == 'JPEG' from_srgb = image.colorspace_settings.name == "sRGB" mip_count = world.arm_envtex_num_mips mip_count = write_probes.write_probes(filepath, disable_hdr, from_srgb, mip_count, arm_radiance=rpdat.arm_radiance) world.arm_envtex_num_mips = mip_count state.radiance_written = True # Append LDR define if disable_hdr: world.world_defs += '_EnvLDR' wrd = bpy.data.worlds['Arm'] mobile_mat = rpdat.arm_material_model == 'Mobile' or rpdat.arm_material_model == 'Solid' # Append radiance define if rpdat.arm_irradiance and rpdat.arm_radiance and not mobile_mat: wrd.world_defs += '_Rad' return 'texture(envmap, envMapEquirect(pos)).rgb * envmapStrength'