def execute(self, context): sdk_path = armutils.get_sdk_path() project_path = armutils.get_fp() pkg = bpy.data.worlds['Arm'].arm_project_package item = context.object.my_traitlist[context.object.traitlist_index] source_hx_path = sdk_path + '/armory/Sources/armory/trait/' + item.class_name_prop + '.hx' target_hx_path = project_path + '/Sources/' + pkg + '/' + item.class_name_prop + '.hx' if not os.path.isfile(target_hx_path): # Rewrite package and copy sf = open(source_hx_path) sf.readline() tf = open(target_hx_path, 'w') tf.write('package ' + pkg + ';\n') shutil.copyfileobj(sf, tf) sf.close() tf.close() armutils.fetch_script_names() # From bundled to script item.type_prop = 'Haxe Script' # Edit in Kode Studio bpy.ops.arm.edit_script('EXEC_DEFAULT') return {'FINISHED'}
def on_compiled(mode): # build, play, play_viewport, publish log.clear() sdk_path = armutils.get_sdk_path() # Print info if mode == 'publish': target_name = make_utils.get_kha_target(bpy.data.worlds['Arm'].arm_project_target) print('Project published') files_path = armutils.get_fp() + '/build/' + target_name if target_name == 'html5': print('HTML5 files are located in ' + files_path) elif target_name == 'ios' or target_name == 'osx': # TODO: to macos print('XCode project files are located in ' + files_path + '-build') elif target_name == 'windows': print('VisualStudio 2015 project files are located in ' + files_path + '-build') elif target_name == 'android-native': print('Android Studio project files are located in ' + files_path + '-build') else: print('Makefiles are located in ' + files_path + '-build') return # Launch project in new window elif mode =='play': wrd = bpy.data.worlds['Arm'] if wrd.arm_play_runtime == 'Electron': electron_app_path = './build/electron.js' if armutils.get_os() == 'win': electron_path = sdk_path + 'win32/Kode Studio.exe' elif armutils.get_os() == 'mac': electron_path = sdk_path + 'Kode Studio.app/Contents/MacOS/Electron' else: electron_path = sdk_path + 'linux64/kodestudio' state.playproc = subprocess.Popen([electron_path, '--chromedebug', '--remote-debugging-port=9222', '--enable-logging', electron_app_path], stderr=subprocess.PIPE) watch_play() elif wrd.arm_play_runtime == 'Browser': # Start server os.chdir(armutils.get_fp()) t = threading.Thread(name='localserver', target=lib.server.run) t.daemon = True t.start() html5_app_path = 'http://localhost:8040/build/html5' webbrowser.open(html5_app_path) elif wrd.arm_play_runtime == 'Krom': if armutils.get_os() == 'win': krom_location = sdk_path + '/win32/Krom/win32' krom_path = krom_location + '/Krom.exe' elif armutils.get_os() == 'mac': krom_location = sdk_path + '/Kode Studio.app/Contents/Krom/macos/Krom.app/Contents/MacOS' krom_path = krom_location + '/Krom' else: krom_location = sdk_path + '/linux64/Krom/linux' krom_path = krom_location + '/Krom' os.chdir(krom_location) state.playproc = subprocess.Popen([krom_path, armutils.get_fp() + '/build/window/krom', armutils.get_fp() + '/build/window/krom-resources'], stderr=subprocess.PIPE) watch_play()
def on_compiled(mode): # build, play, play_viewport, publish log.clear() sdk_path = armutils.get_sdk_path() wrd = bpy.data.worlds['Arm'] # Print info if mode == 'publish': target_name = make_utils.get_kha_target(wrd.arm_project_target) print('Project published') files_path = armutils.get_fp() + '/build/' + target_name if target_name == 'html5': print('HTML5 files are located in ' + files_path) elif target_name == 'ios' or target_name == 'osx': # TODO: to macos print('XCode project files are located in ' + files_path + '-build') elif target_name == 'windows': print('VisualStudio 2015 project files are located in ' + files_path + '-build') elif target_name == 'android-native': print('Android Studio project files are located in ' + files_path + '-build/' + wrd.arm_project_name) else: print('Makefiles are located in ' + files_path + '-build') return # Launch project in new window elif mode =='play': if wrd.arm_play_runtime == 'Electron': electron_app_path = './build/electron.js' if armutils.get_os() == 'win': electron_path = sdk_path + 'win32/Kode Studio.exe' elif armutils.get_os() == 'mac': electron_path = sdk_path + 'Kode Studio.app/Contents/MacOS/Electron' else: electron_path = sdk_path + 'linux64/kodestudio' state.playproc = subprocess.Popen([electron_path, '--chromedebug', '--remote-debugging-port=9222', '--enable-logging', electron_app_path], stderr=subprocess.PIPE) watch_play() elif wrd.arm_play_runtime == 'Browser': # Start server os.chdir(armutils.get_fp()) t = threading.Thread(name='localserver', target=lib.server.run) t.daemon = True t.start() html5_app_path = 'http://localhost:8040/build/html5' webbrowser.open(html5_app_path) elif wrd.arm_play_runtime == 'Krom': if armutils.get_os() == 'win': krom_location = sdk_path + '/win32/Krom/win32' krom_path = krom_location + '/Krom.exe' elif armutils.get_os() == 'mac': krom_location = sdk_path + '/Kode Studio.app/Contents/Krom/macos/Krom.app/Contents/MacOS' krom_path = krom_location + '/Krom' else: krom_location = sdk_path + '/linux64/Krom/linux' krom_path = krom_location + '/Krom' os.chdir(krom_location) state.playproc = subprocess.Popen([krom_path, armutils.get_fp() + '/build/window/krom', armutils.get_fp() + '/build/window/krom-resources'], stderr=subprocess.PIPE) watch_play()
def kode_studio(): sdk_path = armutils.get_sdk_path() project_path = armutils.get_fp() if armutils.get_os() == 'win': kode_path = sdk_path + '/win32/Kode Studio.exe' subprocess.Popen([kode_path, armutils.get_fp()]) elif armutils.get_os() == 'mac': kode_path = '"' + sdk_path + '/Kode Studio.app/Contents/MacOS/Electron"' subprocess.Popen([kode_path + ' ' + armutils.get_fp()], shell=True) else: kode_path = sdk_path + '/linux64/kodestudio' subprocess.Popen([kode_path, armutils.get_fp()])
def execute(self, context): project_path = armutils.get_fp() item = context.object.my_traitlist[context.object.traitlist_index] hx_path = project_path + '/Sources/' + bpy.data.worlds['Arm'].arm_project_package + '/' + item.class_name_prop + '.hx' sdk_path = armutils.get_sdk_path() if armutils.get_os() == 'win': kode_path = sdk_path + '/win32/Kode Studio.exe' subprocess.Popen([kode_path, armutils.get_fp(), hx_path]) elif armutils.get_os() == 'mac': kode_path = '"' + sdk_path + '/Kode Studio.app/Contents/MacOS/Electron"' subprocess.Popen([kode_path + ' ' + armutils.get_fp() + ' ' + hx_path], shell=True) else: kode_path = sdk_path + '/linux64/kodestudio' subprocess.Popen([kode_path, armutils.get_fp(), hx_path]) return{'FINISHED'}
def load_library(asset_name, rename=None): sdk_path = armutils.get_sdk_path() data_path = sdk_path + '/armory/blender/data/data.blend' data_names = [asset_name] # Remove old if rename != None and rename in bpy.data.node_groups and asset_name != 'Armory PBR': bpy.data.node_groups.remove(bpy.data.node_groups[rename], do_unlink=True) # Import data_refs = data_names.copy() with bpy.data.libraries.load(data_path, link=False) as (data_from, data_to): data_to.node_groups = data_refs for ref in data_refs: ref.use_fake_user = True if rename != None: ref.name = rename
def execute(self, context): project_path = armutils.get_fp() item = context.object.my_traitlist[context.object.traitlist_index] hx_path = project_path + '/Sources/' + bpy.data.worlds[ 'Arm'].arm_project_package + '/' + item.class_name_prop + '.hx' sdk_path = armutils.get_sdk_path() if armutils.get_os() == 'win': kode_path = sdk_path + '/win32/Kode Studio.exe' subprocess.Popen([kode_path, armutils.get_fp(), hx_path]) elif armutils.get_os() == 'mac': kode_path = '"' + sdk_path + '/Kode Studio.app/Contents/MacOS/Electron"' subprocess.Popen( [kode_path + ' ' + armutils.get_fp() + ' ' + hx_path], shell=True) else: kode_path = sdk_path + '/linux64/kodestudio' subprocess.Popen([kode_path, armutils.get_fp(), hx_path]) return {'FINISHED'}
def write_probes(image_filepath, disable_hdr, cached_num_mips, generate_radiance=True): envpath = 'build/compiled/Assets/envmaps' if not os.path.exists(envpath): os.makedirs(envpath) base_name = armutils.extract_filename(image_filepath).rsplit('.', 1)[0] # Assets to be generated output_file_irr = envpath + '/' + base_name + '_irradiance' if generate_radiance: output_file_rad = envpath + '/' + base_name + '_radiance' rad_format = 'jpg' if disable_hdr else 'hdr' # Radiance & irradiance exists, keep cache basep = envpath + '/' + base_name if os.path.exists(basep + '_irradiance.arm'): if not generate_radiance or os.path.exists(basep + '_radiance_0.' + rad_format): add_irr_assets(output_file_irr) if generate_radiance: add_rad_assets(output_file_rad, rad_format, cached_num_mips) return cached_num_mips # Get paths sdk_path = armutils.get_sdk_path() if armutils.get_os() == 'win': cmft_path = sdk_path + '/armory/tools/cmft/cmft.exe' kraffiti_path = sdk_path + '/win32/Kha/Kore/Tools/kraffiti/kraffiti.exe' elif armutils.get_os() == 'mac': cmft_path = '"' + sdk_path + '/armory/tools/cmft/cmft-osx"' kraffiti_path = '"' + sdk_path + '/Kode Studio.app/Contents/Kha/Kore/Tools/kraffiti/kraffiti-osx"' else: cmft_path = '"' + sdk_path + '/armory/tools/cmft/cmft-linux64"' kraffiti_path = '"' + sdk_path + '/linux64/Kha/Kore/Tools/kraffiti/kraffiti-linux64"' output_gama_numerator = '1.0' if disable_hdr else '2.2' input_file = armutils.safe_assetpath(image_filepath) # Scale map wrd = bpy.data.worlds['Arm'] target_w = int(wrd.generate_radiance_size) target_h = int(target_w / 2) scaled_file = output_file_rad + '.' + rad_format if armutils.get_os() == 'win': output = subprocess.check_output([ \ kraffiti_path, 'from=' + input_file.replace(' ', '\ '), 'to=' + scaled_file.replace(' ', '\ '), 'format=' + rad_format, 'width=' + str(target_w), 'height=' + str(target_h)]) else: output = subprocess.check_output([ \ kraffiti_path + \ ' from="' + input_file + '"' + \ ' to="' + scaled_file + '"' + \ ' format=' + rad_format + \ ' width=' + str(target_w) + \ ' height=' + str(target_h)], shell=True) # Generate irradiance # gama_options = '' # if disable_hdr: # gama_options = \ # ' --inputGammaNumerator 2.2' + \ # ' --inputGammaDenominator 1.0' + \ # ' --outputGammaNumerator 1.0' + \ # ' --outputGammaDenominator ' + output_gama_numerator # Irradiance spherical harmonics if armutils.get_os() == 'win': subprocess.call([ \ cmft_path, '--input', scaled_file.replace(' ', '\ '), '--filter', 'shcoeffs', #gama_options + \ '--outputNum', '1', '--output0', output_file_irr]) else: subprocess.call([ \ cmft_path + \ ' --input ' + '"' + scaled_file + '"' + \ ' --filter shcoeffs' + \ #gama_options + \ ' --outputNum 1' + \ ' --output0 ' + output_file_irr], shell=True) sh_to_json(output_file_irr) add_irr_assets(output_file_irr) # Mip-mapped radiance if generate_radiance == False: return cached_num_mips # 4096 = 256 face # 2048 = 128 face # 1024 = 64 face face_size = target_w / 8 if target_w == 2048: mip_count = 9 elif target_w == 1024: mip_count = 8 else: mip_count = 7 use_opencl = 'true' if wrd.arm_gpu_processing else 'false' if armutils.get_os() == 'win': subprocess.call([ \ cmft_path, '--input', input_file.replace(' ', '\ '), '--filter', 'radiance', '--dstFaceSize', str(face_size), '--srcFaceSize', str(face_size), '--excludeBase', 'false', # '--mipCount', str(mip_count), '--glossScale', '7', '--glossBias', '3', '--lightingModel', 'blinnbrdf', '--edgeFixup', 'none', '--numCpuProcessingThreads', '4', '--useOpenCL', use_opencl, '--clVendor', 'anyGpuVendor', '--deviceType', 'gpu', '--deviceIndex', '0', '--generateMipChain', 'true', '--inputGammaNumerator', '2.2', '--inputGammaDenominator', '1.0', '--outputGammaNumerator', '1.0', '--outputGammaDenominator', output_gama_numerator, '--outputNum', '1', '--output0', output_file_rad.replace(' ', '\ '), '--output0params', 'hdr,rgbe,latlong']) else: subprocess.call([ \ cmft_path + \ ' --input "' + input_file + '"' + \ ' --filter radiance' + \ ' --dstFaceSize ' + str(face_size) + \ ' --srcFaceSize ' + str(face_size) + \ ' --excludeBase false' + \ #' --mipCount ' + str(mip_count) + \ ' --glossScale 7' + \ ' --glossBias 3' + \ ' --lightingModel blinnbrdf' + \ ' --edgeFixup none' + \ ' --numCpuProcessingThreads 4' + \ ' --useOpenCL ' + use_opencl + \ ' --clVendor anyGpuVendor' + \ ' --deviceType gpu' + \ ' --deviceIndex 0' + \ ' --generateMipChain true' + \ ' --inputGammaNumerator 2.2' + \ ' --inputGammaDenominator 1.0' + \ ' --outputGammaNumerator 1.0' + \ ' --outputGammaDenominator ' + output_gama_numerator + \ ' --outputNum 1' + \ ' --output0 "' + output_file_rad + '"' + \ ' --output0params hdr,rgbe,latlong'], shell=True) # Remove size extensions in file name mip_w = int(face_size * 4) mip_base = output_file_rad + '_' mip_num = 0 while mip_w >= 4: mip_name = mip_base + str(mip_num) os.rename( mip_name + '_' + str(mip_w) + 'x' + str(int(mip_w / 2)) + '.hdr', mip_name + '.hdr') mip_w = int(mip_w / 2) mip_num += 1 # Append mips generated_files = [] for i in range(0, mip_count): generated_files.append(output_file_rad + '_' + str(i)) # Convert to jpgs if disable_hdr is True: for f in generated_files: if armutils.get_os() == 'win': subprocess.call([ \ kraffiti_path, 'from=' + f + '.hdr', 'to=' + f + '.jpg', 'format=jpg']) else: subprocess.call([ \ kraffiti_path + \ ' from=' + f + '.hdr' + \ ' to=' + f + '.jpg' + \ ' format=jpg'], shell=True) os.remove(f + '.hdr') # Scale from (4x2 to 1x1> for i in range(0, 2): last = generated_files[-1] out = output_file_rad + '_' + str(mip_count + i) if armutils.get_os() == 'win': subprocess.call([ \ kraffiti_path, 'from=' + last + '.' + rad_format, 'to=' + out + '.' + rad_format, 'scale=0.5', 'format=' + rad_format], shell=True) else: subprocess.call([ \ kraffiti_path + \ ' from=' + last + '.' + rad_format + \ ' to=' + out + '.' + rad_format + \ ' scale=0.5' + \ ' format=' + rad_format], shell=True) generated_files.append(out) mip_count += 2 add_rad_assets(output_file_rad, rad_format, mip_count) return mip_count
def write_khafilejs(is_play, export_physics, export_navigation, dce_full=False): sdk_path = armutils.get_sdk_path() # Merge duplicates and sort shader_references = sorted(list(set(assets.shaders))) shader_data_references = sorted(list(set(assets.shader_datas))) asset_references = sorted(list(set(assets.assets))) wrd = bpy.data.worlds['Arm'] with open('khafile.js', 'w') as f: f.write( """// Auto-generated let project = new Project('""" + wrd.arm_project_name + """'); project.addSources('Sources'); """) f.write(add_armory_library(sdk_path, 'armory')) f.write(add_armory_library(sdk_path, 'iron')) if export_physics: f.write("project.addDefine('arm_physics');\n") f.write(add_armory_library(sdk_path + '/lib/', 'haxebullet')) if state.target == 'krom' or state.target == 'html5': ammojs_path = sdk_path + '/lib/haxebullet/js/ammo/ammo.js' ammojs_path = ammojs_path.replace('\\', '/') f.write("project.addAssets('" + ammojs_path + "');\n") if export_navigation: f.write("project.addDefine('arm_navigation');\n") 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("project.addAssets('" + recastjs_path + "');\n") if dce_full: f.write("project.addParameter('-dce full');") for ref in shader_references: f.write("project.addShaders('" + ref + "');\n") for ref in shader_data_references: ref = ref.replace('\\', '/') f.write("project.addAssets('" + ref + "');\n") for ref in asset_references: ref = ref.replace('\\', '/') f.write("project.addAssets('" + ref + "');\n") if wrd.arm_play_console: f.write("project.addDefine('arm_profile');\n") f.write(add_armory_library(sdk_path, 'lib/zui')) font_path = sdk_path + '/armory/Assets/droid_sans.ttf' font_path = font_path.replace('\\', '/') f.write('project.addAssets("' + font_path + '");\n') # f.write(add_armory_library(sdk_path, 'lib/haxeui/haxeui-core')) # f.write(add_armory_library(sdk_path, 'lib/haxeui/haxeui-kha')) # f.write(add_armory_library(sdk_path, 'lib/haxeui/hscript')) if wrd.arm_minimize == False: f.write("project.addDefine('arm_json');\n") if wrd.arm_deinterleaved_buffers == True: f.write("project.addDefine('arm_deinterleaved');\n") if wrd.generate_gpu_skin == False: f.write("project.addDefine('arm_cpu_skin');\n") 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()) f.write("\n\nresolve(project);\n")
def build_node_tree(world): output = {} dat = {} output['material_datas'] = [dat] dat['name'] = armutils.safe_filename(world.name) + '_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 = world.name world.world_envtex_irr_name = world.name write_probes.write_color_irradiance(world.name, 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 = armutils.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' # Alternative models if wrd.diffuse_model == 'Oren Nayar': wrd.world_defs += '_OrenNayar' # 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' if cam.rp_voxelgi: voxelgi = True if voxelgi: assets.add_khafile_def('arm_voxelgi') wrd.world_defs += '_VoxelGI' wrd.world_defs += '_Rad' # Always do radiance for voxels wrd.world_defs += '_Irr' # 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( armutils.safe_assetpath(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'] = armutils.extract_filename(image.filepath) tex['file'] = armutils.safe_filename(tex['file']) 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 = armutils.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): armutils.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 = armutils.get_fp( ) + '/build/compiled/Assets/unpacked/' + tex['file'] filepath = converted_path # TODO: delete cache when file changes if not os.path.isfile(converted_path): armutils.write_image(image, converted_path, file_format=target_format) assets.add(converted_path) else: # Link image path to assets assets.add(armutils.safe_assetpath(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 = '/build/compiled/Assets/unpacked' unpack_path = armutils.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(armutils.safe_assetpath(image.filepath)) # Reference image name tex['file'] = armutils.extract_filename(image.filepath) tex['file'] = armutils.safe_filename(tex['file']) # 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 world.world_envtex_irr_name = world.name write_probes.write_sky_irradiance(world.name) # 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 = armutils.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_value(node, socket): if node.type == 'GROUP': if node.node_tree.name.startswith('Armory PBR'): # Displacement if socket == node.outputs[1]: res = parse_value_input(node.inputs[10]) if node.inputs[11].is_linked or node.inputs[11].default_value != 1.0: res = "({0} * {1})".format(res, parse_value_input(node.inputs[11])) return res else: return None else: return parse_group(node, socket) elif node.type == 'GROUP_INPUT': return parse_input_group(node, socket) elif node.type == 'ATTRIBUTE': # Pass time till drivers are implemented if node.attribute_name == 'time': curshader.add_uniform('float time', link='_time') return 'time' else: return None elif node.type == 'CAMERA': # View Z Depth if socket == node.outputs[1]: return 'gl_FragCoord.z' # View Distance else: return 'length(eyeDir)' elif node.type == 'FRESNEL': ior = parse_value_input(node.inputs[0]) #nor = parse_vectorZ_input(node.inputs[1]) return 'pow(1.0 - dotNV, 7.25 / {0})'.format(ior) # max(dotNV, 0.0) elif node.type == 'NEW_GEOMETRY': if socket == node.outputs[6]: # Backfacing return '0.0' elif socket == node.outputs[7]: # Pointiness return '0.0' elif node.type == 'HAIR_INFO': # Is Strand # Intercept # Thickness pass elif node.type == 'LAYER_WEIGHT': blend = parse_value_input(node.inputs[0]) # nor = parse_vector_input(node.inputs[1]) if socket == node.outputs[0]: # Fresnel return 'clamp(pow(1.0 - dotNV, (1.0 - {0}) * 10.0), 0.0, 1.0)'.format(blend) elif socket == node.outputs[1]: # Facing return '((1.0 - dotNV) * {0})'.format(blend) elif node.type == 'LIGHT_PATH': if socket == node.outputs[0]: # Is Camera Ray return '1.0' elif socket == node.outputs[0]: # Is Shadow Ray return '0.0' elif socket == node.outputs[0]: # Is Diffuse Ray return '1.0' elif socket == node.outputs[0]: # Is Glossy Ray return '1.0' elif socket == node.outputs[0]: # Is Singular Ray return '0.0' elif socket == node.outputs[0]: # Is Reflection Ray return '0.0' elif socket == node.outputs[0]: # Is Transmission Ray return '0.0' elif socket == node.outputs[0]: # Ray Length return '0.0' elif socket == node.outputs[0]: # Ray Depth return '0.0' elif socket == node.outputs[0]: # Transparent Depth return '0.0' elif socket == node.outputs[0]: # Transmission Depth return '0.0' elif node.type == 'OBJECT_INFO': if socket == node.outputs[0]: # Object Index return '0.0' elif socket == node.outputs[0]: # Material Index return '0.0' elif socket == node.outputs[0]: # Random return '0.0' elif node.type == 'PARTICLE_INFO': if socket == node.outputs[0]: # Index return '0.0' elif socket == node.outputs[1]: # Age return '0.0' elif socket == node.outputs[2]: # Lifetime return '0.0' elif socket == node.outputs[4]: # Size return '0.0' elif node.type == 'VALUE': return tovec1(node.outputs[0].default_value) elif node.type == 'WIREFRAME': #node.use_pixel_size # size = parse_value_input(node.inputs[0]) return '0.0' elif node.type == 'TEX_BRICK': return '0.0' elif node.type == 'TEX_CHECKER': # TODO: do not recompute when color socket is also connected curshader.add_function(functions.str_tex_checker) if node.inputs[0].is_linked: co = parse_vector_input(node.inputs[0]) else: co = 'wposition' col1 = parse_vector_input(node.inputs[1]) col2 = parse_vector_input(node.inputs[2]) scale = parse_value_input(node.inputs[3]) return 'tex_checker({0}, {1}, {2}, {3}).r'.format(co, col1, col2, scale) elif node.type == 'TEX_GRADIENT': return '0.0' elif node.type == 'TEX_IMAGE': # Already fetched if res_var_name(node, node.outputs[0]) in parsed: return '{0}.a'.format(store_var_name(node)) tex_name = armutils.safe_source_name(node.name) tex = texture.make_texture(node, tex_name) if tex != None: return '{0}.a'.format(texture_store(node, tex, tex_name)) else: return '0.0' elif node.type == 'TEX_MAGIC': return '0.0' elif node.type == 'TEX_MUSGRAVE': # Fall back to noise curshader.add_function(functions.str_tex_noise) if node.inputs[0].is_linked: co = parse_vector_input(node.inputs[0]) else: co = 'wposition' scale = parse_value_input(node.inputs[1]) # detail = parse_value_input(node.inputs[2]) # distortion = parse_value_input(node.inputs[3]) return 'tex_noise_f({0} * {1})'.format(co, scale) elif node.type == 'TEX_NOISE': curshader.add_function(functions.str_tex_noise) if node.inputs[0].is_linked: co = parse_vector_input(node.inputs[0]) else: co = 'wposition' scale = parse_value_input(node.inputs[1]) # detail = parse_value_input(node.inputs[2]) # distortion = parse_value_input(node.inputs[3]) return 'tex_noise({0} * {1})'.format(co, scale) elif node.type == 'TEX_POINTDENSITY': return '0.0' elif node.type == 'TEX_VORONOI': curshader.add_function(functions.str_tex_voronoi) assets.add(armutils.get_sdk_path() + '/armory/Assets/' + 'noise64.png') assets.add_embedded_data('noise64.png') curshader.add_uniform('sampler2D snoise', link='_noise64') if node.inputs[0].is_linked: co = parse_vector_input(node.inputs[0]) else: co = 'wposition' scale = parse_value_input(node.inputs[1]) if node.coloring == 'INTENSITY': return 'tex_voronoi({0} * {1}).a'.format(co, scale) else: # CELLS return 'tex_voronoi({0} * {1}).r'.format(co, scale) elif node.type == 'TEX_WAVE': return '0.0' elif node.type == 'LIGHT_FALLOFF': return '0.0' elif node.type == 'NORMAL': nor = parse_vector_input(node.inputs[0]) return 'dot({0}, {1})'.format(tovec3(node.outputs[0].default_value), nor) elif node.type == 'VALTORGB': # ColorRamp return '1.0' elif node.type == 'MATH': val1 = parse_value_input(node.inputs[0]) val2 = parse_value_input(node.inputs[1]) op = node.operation if op == 'ADD': out_val = '({0} + {1})'.format(val1, val2) elif op == 'SUBTRACT': out_val = '({0} - {1})'.format(val1, val2) elif op == 'MULTIPLY': out_val = '({0} * {1})'.format(val1, val2) elif op == 'DIVIDE': out_val = '({0} / {1})'.format(val1, val2) elif op == 'SINE': out_val = 'sin({0})'.format(val1) elif op == 'COSINE': out_val = 'cos({0})'.format(val1) elif op == 'TANGENT': out_val = 'tan({0})'.format(val1) elif op == 'ARCSINE': out_val = 'asin({0})'.format(val1) elif op == 'ARCCOSINE': out_val = 'acos({0})'.format(val1) elif op == 'ARCTANGENT': out_val = 'atan({0})'.format(val1) elif op == 'POWER': out_val = 'pow({0}, {1})'.format(val1, val2) elif op == 'LOGARITHM': out_val = 'log({0})'.format(val1) elif op == 'MINIMUM': out_val = 'min({0}, {1})'.format(val1, val2) elif op == 'MAXIMUM': out_val = 'max({0}, {1})'.format(val1, val2) elif op == 'ROUND': # out_val = 'round({0})'.format(val1) out_val = 'floor({0} + 0.5)'.format(val1) elif op == 'LESS_THAN': out_val = 'float({0} < {1})'.format(val1, val2) elif op == 'GREATER_THAN': out_val = 'float({0} > {1})'.format(val1, val2) elif op == 'MODULO': # out_val = 'float({0} % {1})'.format(val1, val2) out_val = 'mod({0}, {1})'.format(val1, val2) elif op == 'ABSOLUTE': out_val = 'abs({0})'.format(val1) if node.use_clamp: return 'clamp({0}, 0.0, 1.0)'.format(out_val) else: return out_val elif node.type == 'RGBTOBW': col = parse_vector_input(node.inputs[0]) return '((({0}.r * 0.3 + {0}.g * 0.59 + {0}.b * 0.11) / 3.0) * 2.5)'.format(col) elif node.type == 'SEPHSV': return '0.0' elif node.type == 'SEPRGB': col = parse_vector_input(node.inputs[0]) if socket == node.outputs[0]: return '{0}.r'.format(col) elif socket == node.outputs[1]: return '{0}.g'.format(col) elif socket == node.outputs[2]: return '{0}.b'.format(col) elif node.type == 'SEPXYZ': vec = parse_vector_input(node.inputs[0]) if socket == node.outputs[0]: return '{0}.x'.format(vec) elif socket == node.outputs[1]: return '{0}.y'.format(vec) elif socket == node.outputs[2]: return '{0}.z'.format(vec) elif node.type == 'VECT_MATH': vec1 = parse_vector_input(node.inputs[0]) vec2 = parse_vector_input(node.inputs[1]) op = node.operation if op == 'DOT_PRODUCT': return 'dot({0}, {1})'.format(vec1, vec2) else: return '0.0'
def parse_rgb(node, socket): if node.type == 'GROUP': return parse_group(node, socket) elif node.type == 'GROUP_INPUT': return parse_input_group(node, socket) elif node.type == 'ATTRIBUTE': # Vcols only for now # node.attribute_name mat_state.data.add_elem('col', 3) return 'vcolor' elif node.type == 'RGB': return tovec3(socket.default_value) elif node.type == 'TEX_BRICK': # Pass through return tovec3([0.0, 0.0, 0.0]) elif node.type == 'TEX_CHECKER': curshader.add_function(functions.str_tex_checker) if node.inputs[0].is_linked: co = parse_vector_input(node.inputs[0]) else: co = 'wposition' col1 = parse_vector_input(node.inputs[1]) col2 = parse_vector_input(node.inputs[2]) scale = parse_value_input(node.inputs[3]) return 'tex_checker({0}, {1}, {2}, {3})'.format(co, col1, col2, scale) elif node.type == 'TEX_ENVIRONMENT': # Pass through return tovec3([0.0, 0.0, 0.0]) elif node.type == 'TEX_GRADIENT': if node.inputs[0].is_linked: co = parse_vector_input(node.inputs[0]) else: co = 'wposition' grad = node.gradient_type if grad == 'LINEAR': f = '{0}.x'.format(co) elif grad == 'QUADRATIC': f = '0.0' elif grad == 'EASING': f = '0.0' elif grad == 'DIAGONAL': f = '({0}.x + {0}.y) * 0.5'.format(co) elif grad == 'RADIAL': f = 'atan({0}.y, {0}.x) / PI2 + 0.5'.format(co) elif grad == 'QUADRATIC_SPHERE': f = '0.0' elif grad == 'SPHERICAL': f = 'max(1.0 - sqrt({0}.x * {0}.x + {0}.y * {0}.y + {0}.z * {0}.z), 0.0)'.format(co) return 'vec3(clamp({0}, 0.0, 1.0))'.format(f) elif node.type == 'TEX_IMAGE': # Already fetched if res_var_name(node, node.outputs[1]) in parsed: return '{0}.rgb'.format(store_var_name(node)) tex_name = armutils.safe_source_name(node.name) tex = texture.make_texture(node, tex_name) if tex != None: return '{0}.rgb'.format(texture_store(node, tex, tex_name)) else: return tovec3([0.0, 0.0, 0.0]) elif node.type == 'TEX_MAGIC': # Pass through return tovec3([0.0, 0.0, 0.0]) elif node.type == 'TEX_MUSGRAVE': # Fall back to noise curshader.add_function(functions.str_tex_noise) if node.inputs[0].is_linked: co = parse_vector_input(node.inputs[0]) else: co = 'wposition' scale = parse_value_input(node.inputs[1]) # detail = parse_value_input(node.inputs[2]) # distortion = parse_value_input(node.inputs[3]) return 'vec3(tex_noise_f({0} * {1}))'.format(co, scale) elif node.type == 'TEX_NOISE': curshader.add_function(functions.str_tex_noise) if node.inputs[0].is_linked: co = parse_vector_input(node.inputs[0]) else: co = 'wposition' scale = parse_value_input(node.inputs[1]) # detail = parse_value_input(node.inputs[2]) # distortion = parse_value_input(node.inputs[3]) # Slow.. return 'vec3(tex_noise({0} * {1}), tex_noise({0} * {1} + vec3(0.33)), tex_noise({0} * {1} + vec3(0.66)))'.format(co, scale) elif node.type == 'TEX_POINTDENSITY': # Pass through return tovec3([0.0, 0.0, 0.0]) elif node.type == 'TEX_SKY': # Pass through return tovec3([0.0, 0.0, 0.0]) elif node.type == 'TEX_VORONOI': curshader.add_function(functions.str_tex_voronoi) assets.add(armutils.get_sdk_path() + '/armory/Assets/' + 'noise64.png') assets.add_embedded_data('noise64.png') curshader.add_uniform('sampler2D snoise', link='_noise64') if node.inputs[0].is_linked: co = parse_vector_input(node.inputs[0]) else: co = 'wposition' scale = parse_value_input(node.inputs[1]) if node.coloring == 'INTENSITY': return 'vec3(tex_voronoi({0} / {1}).a)'.format(co, scale) else: # CELLS return 'tex_voronoi({0} / {1}).rgb'.format(co, scale) elif node.type == 'TEX_WAVE': # Pass through return tovec3([0.0, 0.0, 0.0]) elif node.type == 'BRIGHTCONTRAST': out_col = parse_vector_input(node.inputs[0]) bright = parse_value_input(node.inputs[1]) contr = parse_value_input(node.inputs[2]) curshader.add_function(\ """vec3 brightcontrast(const vec3 col, const float bright, const float contr) { float a = 1.0 + contr; float b = bright - contr * 0.5; return max(a * col + b, 0.0); } """) return 'brightcontrast({0}, {1}, {2})'.format(out_col, bright, contr) elif node.type == 'GAMMA': out_col = parse_vector_input(node.inputs[0]) gamma = parse_value_input(node.inputs[1]) return 'pow({0}, vec3({1}))'.format(out_col, gamma) elif node.type == 'HUE_SAT': # hue = parse_value_input(node.inputs[0]) # sat = parse_value_input(node.inputs[1]) # val = parse_value_input(node.inputs[2]) # fac = parse_value_input(node.inputs[3]) out_col = parse_vector_input(node.inputs[4]) # curshader.add_function(\ # """vec3 hue_sat(const float hue, const float sat, const float val, const float fac, const vec3 col) { # } # """) return out_col elif node.type == 'INVERT': fac = parse_value_input(node.inputs[0]) out_col = parse_vector_input(node.inputs[1]) return 'mix({0}, vec3(1.0) - ({0}), {1})'.format(out_col, fac) elif node.type == 'MIX_RGB': fac = parse_value_input(node.inputs[0]) fac_var = node_name(node.name) + '_fac' curshader.write('float {0} = {1};'.format(fac_var, fac)) col1 = parse_vector_input(node.inputs[1]) col2 = parse_vector_input(node.inputs[2]) blend = node.blend_type if blend == 'MIX': out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) elif blend == 'ADD': out_col = 'mix({0}, {0} + {1}, {2})'.format(col1, col2, fac_var) elif blend == 'MULTIPLY': out_col = 'mix({0}, {0} * {1}, {2})'.format(col1, col2, fac_var) elif blend == 'SUBTRACT': out_col = 'mix({0}, {0} - {1}, {2})'.format(col1, col2, fac_var) elif blend == 'SCREEN': out_col = '(vec3(1.0) - (vec3(1.0 - {2}) + {2} * (vec3(1.0) - {1})) * (vec3(1.0) - {0}))'.format(col1, col2, fac_var) elif blend == 'DIVIDE': out_col = '(vec3((1.0 - {2}) * {0} + {2} * {0} / {1}))'.format(col1, col2, fac_var) elif blend == 'DIFFERENCE': out_col = 'mix({0}, abs({0} - {1}), {2})'.format(col1, col2, fac_var) elif blend == 'DARKEN': out_col = 'min({0}, {1} * {2})'.format(col1, col2, fac_var) elif blend == 'LIGHTEN': out_col = 'max({0}, {1} * {2})'.format(col1, col2, fac_var) elif blend == 'OVERLAY': out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix elif blend == 'DODGE': out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix elif blend == 'BURN': out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix elif blend == 'HUE': out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix elif blend == 'SATURATION': out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix elif blend == 'VALUE': out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix elif blend == 'COLOR': out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix elif blend == 'SOFT_LIGHT': out_col = '((1.0 - {2}) * {0} + {2} * ((vec3(1.0) - {0}) * {1} * {0} + {0} * (vec3(1.0) - (vec3(1.0) - {1}) * (vec3(1.0) - {0}))));'.format(col1, col2, fac) elif blend == 'LINEAR_LIGHT': out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix # out_col = '({0} + {2} * (2.0 * ({1} - vec3(0.5))))'.format(col1, col2, fac_var) if node.use_clamp: return 'clamp({0}, vec3(0.0), vec3(1.0))'.format(out_col) else: return out_col elif node.type == 'CURVE_RGB': # Pass throuh return parse_vector_input(node.inputs[1]) elif node.type == 'BLACKBODY': # Pass constant return tovec3([0.84, 0.38, 0.0]) elif node.type == 'VALTORGB': # ColorRamp fac = parse_value_input(node.inputs[0]) interp = node.color_ramp.interpolation elems = node.color_ramp.elements if len(elems) == 1: return tovec3(elems[0].color) if interp == 'CONSTANT': fac_var = node_name(node.name) + '_fac' curshader.write('float {0} = {1};'.format(fac_var, fac)) # Get index out_i = '0' for i in range(1, len(elems)): out_i += ' + ({0} > {1} ? 1 : 0)'.format(fac_var, elems[i].position) # Write cols array cols_var = node_name(node.name) + '_cols' curshader.write('vec3 {0}[{1}];'.format(cols_var, len(elems))) for i in range(0, len(elems)): curshader.write('{0}[{1}] = vec3({2}, {3}, {4});'.format(cols_var, i, elems[i].color[0], elems[i].color[1], elems[i].color[2])) return '{0}[{1}]'.format(cols_var, out_i) else: # Linear, .. - 2 elems only, end pos assumed to be 1 # float f = clamp((pos - start) * (1.0 / (1.0 - start)), 0.0, 1.0); return 'mix({0}, {1}, clamp(({2} - {3}) * (1.0 / (1.0 - {3})), 0.0, 1.0))'.format(tovec3(elems[0].color), tovec3(elems[1].color), fac, elems[0].position) elif node.type == 'COMBHSV': # vec3 hsv2rgb(vec3 c) { # vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); # vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); # return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); # } # vec3 rgb2hsv(vec3 c) { # vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); # vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); # vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); # float d = q.x - min(q.w, q.y); # float e = 1.0e-10; # return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); # } # Pass constant return tovec3([0.0, 0.0, 0.0]) elif node.type == 'COMBRGB': r = parse_value_input(node.inputs[0]) g = parse_value_input(node.inputs[1]) b = parse_value_input(node.inputs[2]) return 'vec3({0}, {1}, {2})'.format(r, g, b) elif node.type == 'WAVELENGTH': # Pass constant return tovec3([0.0, 0.27, 0.19])
def patch_project(): sdk_path = armutils.get_sdk_path() fp = armutils.get_fp() os.chdir(fp) export_data(fp, sdk_path, is_play=True)
def build_project(is_play=False, is_publish=False, in_viewport=False, target=None): wrd = bpy.data.worlds['Arm'] # Set target if target == None: state.target = wrd.arm_project_target.lower() # Clear flag state.in_viewport = False # Save blend bpy.ops.wm.save_mainfile() log.clear() # Set camera in active scene active_scene = bpy.context.screen.scene if wrd.arm_play_active_scene else bpy.data.scenes[wrd.arm_project_scene] if active_scene.camera == None: for o in active_scene.objects: if o.type == 'CAMERA': active_scene.camera = o break # Get paths sdk_path = armutils.get_sdk_path() raw_shaders_path = sdk_path + '/armory/Shaders/' # Set dir fp = armutils.get_fp() os.chdir(fp) # Create directories if not os.path.exists('Sources'): os.makedirs('Sources') # Compile path tracer shaders if len(bpy.data.cameras) > 0 and bpy.data.cameras[0].renderpath_path == 'pathtrace_path': path_tracer.compile(raw_shaders_path + 'pt_trace_pass/pt_trace_pass.frag.glsl') # Save external scripts edited inside Blender write_texts = False for text in bpy.data.texts: if text.filepath != '' and text.is_dirty: write_texts = True break if write_texts: area = bpy.context.area old_type = area.type area.type = 'TEXT_EDITOR' for text in bpy.data.texts: if text.filepath != '' and text.is_dirty: area.spaces[0].text = text bpy.ops.text.save() area.type = old_type # Save internal Haxe scripts for text in bpy.data.texts: if text.filepath == '' and text.name[-3:] == '.hx': with open('Sources/' + bpy.data.worlds['Arm'].arm_project_package + '/' + text.name, 'w') as f: f.write(text.as_string()) # Export data export_data(fp, sdk_path, is_play=is_play, is_publish=is_publish, in_viewport=in_viewport) if state.playproc == None: log.print_progress(50)
def write_probes(image_filepath, disable_hdr, cached_num_mips, generate_radiance=True): if not os.path.exists('build/compiled/Assets/envmaps'): os.makedirs('build/compiled/Assets/envmaps') base_name = image_filepath.rsplit(os.path.sep, 1)[1].rsplit('.', 1)[0] # Extract file name without extension # Assets to be generated output_file_irr = 'build/compiled/Assets/envmaps/' + base_name + '_irradiance' if generate_radiance: output_file_rad = 'build/compiled/Assets/envmaps/' + base_name + '_radiance' rad_format = 'jpg' if disable_hdr else 'hdr' # Irradiance exists, keep cache if os.path.exists('build/compiled/Assets/envmaps/' + base_name + '_irradiance.arm'): add_irr_assets(output_file_irr) if generate_radiance: add_rad_assets(output_file_rad, rad_format, cached_num_mips) return cached_num_mips # Get paths sdk_path = armutils.get_sdk_path() if armutils.get_os() == 'win': cmft_path = sdk_path + '/armory/tools/cmft/cmft.exe' kraffiti_path = sdk_path + '/win32/resources/app/extensions/kha/Kha/Kore/Tools/kraffiti/kraffiti.exe' elif armutils.get_os() == 'mac': cmft_path = sdk_path + '/armory/tools/cmft/cmft-osx' kraffiti_path = sdk_path + '/"Kode Studio.app"/Contents/Resources/app/extensions/kha/Kha/Kore/Tools/kraffiti/kraffiti-osx' else: cmft_path = sdk_path + '/armory/tools/cmft/cmft-linux64' kraffiti_path = sdk_path + '/linux64/resources/app/extensions/kha/Kha/Kore/Tools/kraffiti/kraffiti-linux64' output_gama_numerator = '1.0' if disable_hdr else '2.2' input_file = armutils.safe_assetpath(image_filepath) # Scale map wrd = bpy.data.worlds['Arm'] target_w = int(wrd.generate_radiance_size) target_h = int(target_w / 2) scaled_file = output_file_rad + '.' + rad_format if armutils.get_os() == 'win': output = subprocess.check_output([ \ kraffiti_path, 'from=' + input_file, 'to=' + scaled_file, 'format=' + rad_format, 'width=' + str(target_w), 'height=' + str(target_h)]) else: output = subprocess.check_output([ \ kraffiti_path + \ ' from=' + input_file + \ ' to=' + scaled_file + \ ' format=' + rad_format + \ ' width=' + str(target_w) + \ ' height=' + str(target_h)], shell=True) # Generate irradiance # gama_options = '' # if disable_hdr: # gama_options = \ # ' --inputGammaNumerator 2.2' + \ # ' --inputGammaDenominator 1.0' + \ # ' --outputGammaNumerator 1.0' + \ # ' --outputGammaDenominator ' + output_gama_numerator # Irradiance spherical harmonics if armutils.get_os() == 'win': subprocess.call([ \ cmft_path, '--input', scaled_file, '--filter', 'shcoeffs', #gama_options + \ '--outputNum', '1', '--output0', output_file_irr]) else: subprocess.call([ \ cmft_path + \ ' --input ' + scaled_file + \ ' --filter shcoeffs' + \ #gama_options + \ ' --outputNum 1' + \ ' --output0 ' + output_file_irr], shell=True) sh_to_json(output_file_irr) add_irr_assets(output_file_irr) # Mip-mapped radiance if generate_radiance == False: return cached_num_mips # 4096 = 256 face # 2048 = 128 face # 1024 = 64 face face_size = target_w / 8 if target_w == 2048: mip_count = 9 elif target_w == 1024: mip_count = 8 else: mip_count = 7 if armutils.get_os() == 'win': subprocess.call([ \ cmft_path, '--input', input_file, '--filter radiance', '--dstFaceSize', str(face_size), '--srcFaceSize', str(face_size), '--excludeBase', 'false', # '--mipCount', str(mip_count), '--glossScale', '7', '--glossBias', '3', '--lightingModel', 'blinnbrdf', '--edgeFixup', 'none', '--numCpuProcessingThreads', '4', '--useOpenCL', 'true', '--clVendor', 'anyGpuVendor', '--deviceType', 'gpu', '--deviceIndex', '0', '--generateMipChain', 'true', '--inputGammaNumerator', '2.2', '--inputGammaDenominator', '1.0', '--outputGammaNumerator', '1.0', '--outputGammaDenominator', output_gama_numerator, '--outputNum', '1', '--output0', output_file_rad, '--output0params', 'hdr,rgbe,latlong']) else: subprocess.call([ \ cmft_path + \ ' --input ' + input_file + \ ' --filter radiance' + \ ' --dstFaceSize ' + str(face_size) + \ ' --srcFaceSize ' + str(face_size) + \ ' --excludeBase false' + \ #' --mipCount ' + str(mip_count) + \ ' --glossScale 7' + \ ' --glossBias 3' + \ ' --lightingModel blinnbrdf' + \ ' --edgeFixup none' + \ ' --numCpuProcessingThreads 4' + \ ' --useOpenCL true' + \ ' --clVendor anyGpuVendor' + \ ' --deviceType gpu' + \ ' --deviceIndex 0' + \ ' --generateMipChain true' + \ ' --inputGammaNumerator 2.2' + \ ' --inputGammaDenominator 1.0' + \ ' --outputGammaNumerator 1.0' + \ ' --outputGammaDenominator ' + output_gama_numerator + \ ' --outputNum 1' + \ ' --output0 ' + output_file_rad + \ ' --output0params hdr,rgbe,latlong'], shell=True) # Remove size extensions in file name mip_w = int(face_size * 4) mip_base = output_file_rad + '_' mip_num = 0 while mip_w >= 4: mip_name = mip_base + str(mip_num) os.rename( mip_name + '_' + str(mip_w) + 'x' + str(int(mip_w / 2)) + '.hdr', mip_name + '.hdr') mip_w = int(mip_w / 2) mip_num += 1 # Append mips generated_files = [] for i in range(0, mip_count): generated_files.append(output_file_rad + '_' + str(i)) # Convert to jpgs if disable_hdr is True: for f in generated_files: if armutils.get_os() == 'win': subprocess.call([ \ kraffiti_path, 'from=' + f + '.hdr', 'to=' + f + '.jpg', 'format=jpg']) else: subprocess.call([ \ kraffiti_path + \ ' from=' + f + '.hdr' + \ ' to=' + f + '.jpg' + \ ' format=jpg'], shell=True) os.remove(f + '.hdr') # Scale from (4x2 to 1x1> for i in range (0, 2): last = generated_files[-1] out = output_file_rad + '_' + str(mip_count + i) if armutils.get_os() == 'win': subprocess.call([ \ kraffiti_path, 'from=' + last + '.' + rad_format, 'to=' + out + '.' + rad_format, 'scale=0.5', 'format=' + rad_format], shell=True) else: subprocess.call([ \ kraffiti_path + \ ' from=' + last + '.' + rad_format + \ ' to=' + out + '.' + rad_format + \ ' scale=0.5' + \ ' format=' + rad_format], shell=True) generated_files.append(out) mip_count += 2 add_rad_assets(output_file_rad, rad_format, mip_count) return mip_count
def compile_project(target_name=None, is_publish=False, watch=False, patch=False): sdk_path = armutils.get_sdk_path() ffmpeg_path = armutils.get_ffmpeg_path() wrd = bpy.data.worlds['Arm'] # Set build command if target_name == None: target_name = wrd.arm_project_target if armutils.get_os() == 'win': node_path = sdk_path + '/nodejs/node.exe' khamake_path = sdk_path + '/win32/Kha/make' elif armutils.get_os() == 'mac': node_path = sdk_path + '/nodejs/node-osx' khamake_path = sdk_path + '/Kode Studio.app/Contents/Kha/make' else: node_path = sdk_path + '/nodejs/node-linux64' khamake_path = sdk_path + '/linux64/Kha/make' kha_target_name = make_utils.get_kha_target(target_name) cmd = [node_path, khamake_path, kha_target_name] if ffmpeg_path != '': cmd.append('--ffmpeg') cmd.append('"' + ffmpeg_path + '"') if armutils.get_os() == 'win': # OpenGL for now cmd.append('-g') cmd.append('opengl2') if kha_target_name == 'krom': if state.in_viewport or patch: if armutils.glsl_version() >= 330: cmd.append('--shaderversion') cmd.append('330') else: cmd.append('--shaderversion') cmd.append('110') else: cmd.append('--to') cmd.append('build/window') # User defined commands cmd_text = wrd.arm_command_line if cmd_text != '': for s in bpy.data.texts[cmd_text].as_string().split(' '): cmd.append(s) if state.krom_running: if state.compileproc == None: # Already compiling # Patch running game, stay silent, disable krafix and haxe # cmd.append('--silent') cmd.append('--noproject') cmd.append('--haxe') cmd.append('""') cmd.append('--krafix') cmd.append('""') # Khamake throws error when krafix is not found, hide for now state.compileproc = subprocess.Popen(cmd, stderr=subprocess.PIPE) threading.Timer(0.1, watch_patch).start() return state.compileproc elif watch == True: state.compileproc = subprocess.Popen(cmd) threading.Timer(0.1, watch_compile, ['build']).start() return state.compileproc else: return subprocess.Popen(cmd)
def write_khafilejs(is_play, export_physics, export_navigation, dce_full=False): sdk_path = armutils.get_sdk_path() # Merge duplicates and sort shader_references = sorted(list(set(assets.shaders))) shader_data_references = sorted(list(set(assets.shader_datas))) asset_references = sorted(list(set(assets.assets))) wrd = bpy.data.worlds['Arm'] with open('khafile.js', 'w') as f: f.write( """// Auto-generated let project = new Project('""" + wrd.arm_project_name + """'); project.addSources('Sources'); """) # Auto-add assets located in Bundled directory if os.path.exists('Bundled'): f.write('project.addAssets("Bundled/**");\n') if os.path.exists('Libraries/armory'): f.write('project.addLibrary("armory")') else: f.write(add_armory_library(sdk_path, 'armory')) if os.path.exists('Libraries/iron'): f.write('project.addLibrary("iron")') else: f.write(add_armory_library(sdk_path, 'iron')) # Project libraries for lib in wrd.my_librarytraitlist: if lib.enabled_prop: f.write('project.addLibrary("{0}");\n'.format(lib.name)) if export_physics: f.write("project.addDefine('arm_physics');\n") f.write(add_armory_library(sdk_path + '/lib/', 'haxebullet')) if state.target == 'krom' or state.target == 'html5': ammojs_path = sdk_path + '/lib/haxebullet/js/ammo/ammo.js' ammojs_path = ammojs_path.replace('\\', '/') f.write("project.addAssets('" + ammojs_path + "');\n") if export_navigation: f.write("project.addDefine('arm_navigation');\n") 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("project.addAssets('" + recastjs_path + "');\n") if dce_full: f.write("project.addParameter('-dce full');") for ref in shader_references: f.write("project.addShaders('" + ref + "');\n") for ref in shader_data_references: ref = ref.replace('\\', '/') f.write("project.addAssets('" + ref + "');\n") for ref in asset_references: ref = ref.replace('\\', '/') f.write("project.addAssets('" + ref + "');\n") if wrd.arm_play_console: f.write("project.addDefine('arm_profile');\n") if wrd.arm_play_console or wrd.arm_ui: f.write(add_armory_library(sdk_path, 'lib/zui')) p = sdk_path + '/armory/Assets/droid_sans.ttf' f.write('project.addAssets("' + p.replace('\\', '/') + '");\n') # if wrd.arm_ui: # f.write(add_armory_library(sdk_path, 'lib/haxeui-core')) # f.write(add_armory_library(sdk_path, 'lib/haxeui-kha')) # f.write(add_armory_library(sdk_path, 'lib/hscript')) if wrd.arm_minimize == False: f.write("project.addDefine('arm_json');\n") if wrd.arm_deinterleaved_buffers == True: f.write("project.addDefine('arm_deinterleaved');\n") if wrd.generate_gpu_skin == False: f.write("project.addDefine('arm_cpu_skin');\n") 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()) f.write("\n\nresolve(project);\n")
def parse_value(node, socket): if node.type == 'GROUP': if node.node_tree.name.startswith('Armory PBR'): # Displacement if socket == node.outputs[1]: res = parse_value_input(node.inputs[10]) if node.inputs[ 11].is_linked or node.inputs[11].default_value != 1.0: res = "({0} * {1})".format( res, parse_value_input(node.inputs[11])) return res else: return None else: return parse_group(node, socket) elif node.type == 'GROUP_INPUT': return parse_group_input(node, socket) elif node.type == 'ATTRIBUTE': # Pass time till drivers are implemented if node.attribute_name == 'time': curshader.add_uniform('float time', link='_time') return 'time' else: return '0.0' elif node.type == 'CAMERA': # View Z Depth if socket == node.outputs[1]: return 'gl_FragCoord.z' # View Distance else: return 'length(eyeDir)' elif node.type == 'FRESNEL': ior = parse_value_input(node.inputs[0]) #nor = parse_vectorZ_input(node.inputs[1]) return 'pow(1.0 - dotNV, 7.25 / {0})'.format(ior) # max(dotNV, 0.0) elif node.type == 'NEW_GEOMETRY': if socket == node.outputs[6]: # Backfacing return '0.0' elif socket == node.outputs[7]: # Pointiness return '0.0' elif node.type == 'HAIR_INFO': # Is Strand # Intercept # Thickness return '0.5' elif node.type == 'LAYER_WEIGHT': blend = parse_value_input(node.inputs[0]) # nor = parse_vector_input(node.inputs[1]) if socket == node.outputs[0]: # Fresnel return 'clamp(pow(1.0 - dotNV, (1.0 - {0}) * 10.0), 0.0, 1.0)'.format( blend) elif socket == node.outputs[1]: # Facing return '((1.0 - dotNV) * {0})'.format(blend) elif node.type == 'LIGHT_PATH': if socket == node.outputs[0]: # Is Camera Ray return '1.0' elif socket == node.outputs[1]: # Is Shadow Ray return '0.0' elif socket == node.outputs[2]: # Is Diffuse Ray return '1.0' elif socket == node.outputs[3]: # Is Glossy Ray return '1.0' elif socket == node.outputs[4]: # Is Singular Ray return '0.0' elif socket == node.outputs[5]: # Is Reflection Ray return '0.0' elif socket == node.outputs[6]: # Is Transmission Ray return '0.0' elif socket == node.outputs[7]: # Ray Length return '0.0' elif socket == node.outputs[8]: # Ray Depth return '0.0' elif socket == node.outputs[9]: # Transparent Depth return '0.0' elif socket == node.outputs[10]: # Transmission Depth return '0.0' elif node.type == 'OBJECT_INFO': if socket == node.outputs[0]: # Object Index return '0.0' elif socket == node.outputs[1]: # Material Index return '0.0' elif socket == node.outputs[2]: # Random return '0.0' elif node.type == 'PARTICLE_INFO': if socket == node.outputs[0]: # Index return '0.0' elif socket == node.outputs[1]: # Age return '0.0' elif socket == node.outputs[2]: # Lifetime return '0.0' elif socket == node.outputs[4]: # Size return '0.0' elif node.type == 'VALUE': return tovec1(node.outputs[0].default_value) elif node.type == 'WIREFRAME': #node.use_pixel_size # size = parse_value_input(node.inputs[0]) return '0.0' elif node.type == 'TEX_BRICK': return '0.0' elif node.type == 'TEX_CHECKER': # TODO: do not recompute when color socket is also connected curshader.add_function(functions.str_tex_checker) if node.inputs[0].is_linked: co = parse_vector_input(node.inputs[0]) else: co = 'wposition' col1 = parse_vector_input(node.inputs[1]) col2 = parse_vector_input(node.inputs[2]) scale = parse_value_input(node.inputs[3]) return 'tex_checker({0}, {1}, {2}, {3}).r'.format( co, col1, col2, scale) elif node.type == 'TEX_GRADIENT': return '0.0' elif node.type == 'TEX_IMAGE': # Already fetched if res_var_name(node, node.outputs[0]) in parsed: return '{0}.a'.format(store_var_name(node)) tex_name = armutils.safe_source_name(node.name) tex = texture.make_texture(node, tex_name) if tex != None: return '{0}.a'.format(texture_store(node, tex, tex_name)) else: tex_store = store_var_name(node) # Pink color for missing texture curshader.write( 'vec4 {0} = vec4(1.0, 0.0, 1.0, 1.0);'.format(tex_store)) return '{0}.a'.format(tex_store) elif node.type == 'TEX_MAGIC': return '0.0' elif node.type == 'TEX_MUSGRAVE': # Fall back to noise curshader.add_function(functions.str_tex_noise) if node.inputs[0].is_linked: co = parse_vector_input(node.inputs[0]) else: co = 'wposition' scale = parse_value_input(node.inputs[1]) # detail = parse_value_input(node.inputs[2]) # distortion = parse_value_input(node.inputs[3]) return 'tex_noise_f({0} * {1})'.format(co, scale) elif node.type == 'TEX_NOISE': curshader.add_function(functions.str_tex_noise) if node.inputs[0].is_linked: co = parse_vector_input(node.inputs[0]) else: co = 'wposition' scale = parse_value_input(node.inputs[1]) # detail = parse_value_input(node.inputs[2]) # distortion = parse_value_input(node.inputs[3]) return 'tex_noise({0} * {1})'.format(co, scale) elif node.type == 'TEX_POINTDENSITY': return '0.0' elif node.type == 'TEX_VORONOI': curshader.add_function(functions.str_tex_voronoi) assets.add(armutils.get_sdk_path() + '/armory/Assets/' + 'noise64.png') assets.add_embedded_data('noise64.png') curshader.add_uniform('sampler2D snoise', link='_noise64') if node.inputs[0].is_linked: co = parse_vector_input(node.inputs[0]) else: co = 'wposition' scale = parse_value_input(node.inputs[1]) if node.coloring == 'INTENSITY': return 'tex_voronoi({0} * {1}).a'.format(co, scale) else: # CELLS return 'tex_voronoi({0} * {1}).r'.format(co, scale) elif node.type == 'TEX_WAVE': return '0.0' elif node.type == 'LIGHT_FALLOFF': # Constant, linear, quadratic # Shaders default to quadratic for now return '1.0' elif node.type == 'NORMAL': nor = parse_vector_input(node.inputs[0]) return 'dot({0}, {1})'.format(tovec3(node.outputs[0].default_value), nor) elif node.type == 'VALTORGB': # ColorRamp return '1.0' elif node.type == 'MATH': val1 = parse_value_input(node.inputs[0]) val2 = parse_value_input(node.inputs[1]) op = node.operation if op == 'ADD': out_val = '({0} + {1})'.format(val1, val2) elif op == 'SUBTRACT': out_val = '({0} - {1})'.format(val1, val2) elif op == 'MULTIPLY': out_val = '({0} * {1})'.format(val1, val2) elif op == 'DIVIDE': out_val = '({0} / {1})'.format(val1, val2) elif op == 'SINE': out_val = 'sin({0})'.format(val1) elif op == 'COSINE': out_val = 'cos({0})'.format(val1) elif op == 'TANGENT': out_val = 'tan({0})'.format(val1) elif op == 'ARCSINE': out_val = 'asin({0})'.format(val1) elif op == 'ARCCOSINE': out_val = 'acos({0})'.format(val1) elif op == 'ARCTANGENT': out_val = 'atan({0})'.format(val1) elif op == 'POWER': out_val = 'pow({0}, {1})'.format(val1, val2) elif op == 'LOGARITHM': out_val = 'log({0})'.format(val1) elif op == 'MINIMUM': out_val = 'min({0}, {1})'.format(val1, val2) elif op == 'MAXIMUM': out_val = 'max({0}, {1})'.format(val1, val2) elif op == 'ROUND': # out_val = 'round({0})'.format(val1) out_val = 'floor({0} + 0.5)'.format(val1) elif op == 'LESS_THAN': out_val = 'float({0} < {1})'.format(val1, val2) elif op == 'GREATER_THAN': out_val = 'float({0} > {1})'.format(val1, val2) elif op == 'MODULO': # out_val = 'float({0} % {1})'.format(val1, val2) out_val = 'mod({0}, {1})'.format(val1, val2) elif op == 'ABSOLUTE': out_val = 'abs({0})'.format(val1) if node.use_clamp: return 'clamp({0}, 0.0, 1.0)'.format(out_val) else: return out_val elif node.type == 'RGBTOBW': col = parse_vector_input(node.inputs[0]) return '((({0}.r * 0.3 + {0}.g * 0.59 + {0}.b * 0.11) / 3.0) * 2.5)'.format( col) elif node.type == 'SEPHSV': return '0.0' elif node.type == 'SEPRGB': col = parse_vector_input(node.inputs[0]) if socket == node.outputs[0]: return '{0}.r'.format(col) elif socket == node.outputs[1]: return '{0}.g'.format(col) elif socket == node.outputs[2]: return '{0}.b'.format(col) elif node.type == 'SEPXYZ': vec = parse_vector_input(node.inputs[0]) if socket == node.outputs[0]: return '{0}.x'.format(vec) elif socket == node.outputs[1]: return '{0}.y'.format(vec) elif socket == node.outputs[2]: return '{0}.z'.format(vec) elif node.type == 'VECT_MATH': vec1 = parse_vector_input(node.inputs[0]) vec2 = parse_vector_input(node.inputs[1]) op = node.operation if op == 'DOT_PRODUCT': return 'dot({0}, {1})'.format(vec1, vec2) else: return '0.0'
def parse_rgb(node, socket): if node.type == 'GROUP': return parse_group(node, socket) elif node.type == 'GROUP_INPUT': return parse_group_input(node, socket) elif node.type == 'ATTRIBUTE': # Vcols only for now # node.attribute_name mat_state.data.add_elem('col', 3) return 'vcolor' elif node.type == 'RGB': return tovec3(socket.default_value) elif node.type == 'TEX_BRICK': # Pass through return tovec3([0.0, 0.0, 0.0]) elif node.type == 'TEX_CHECKER': curshader.add_function(functions.str_tex_checker) if node.inputs[0].is_linked: co = parse_vector_input(node.inputs[0]) else: co = 'wposition' col1 = parse_vector_input(node.inputs[1]) col2 = parse_vector_input(node.inputs[2]) scale = parse_value_input(node.inputs[3]) return 'tex_checker({0}, {1}, {2}, {3})'.format(co, col1, col2, scale) elif node.type == 'TEX_ENVIRONMENT': # Pass through return tovec3([0.0, 0.0, 0.0]) elif node.type == 'TEX_GRADIENT': if node.inputs[0].is_linked: co = parse_vector_input(node.inputs[0]) else: co = 'wposition' grad = node.gradient_type if grad == 'LINEAR': f = '{0}.x'.format(co) elif grad == 'QUADRATIC': f = '0.0' elif grad == 'EASING': f = '0.0' elif grad == 'DIAGONAL': f = '({0}.x + {0}.y) * 0.5'.format(co) elif grad == 'RADIAL': f = 'atan({0}.y, {0}.x) / PI2 + 0.5'.format(co) elif grad == 'QUADRATIC_SPHERE': f = '0.0' elif grad == 'SPHERICAL': f = 'max(1.0 - sqrt({0}.x * {0}.x + {0}.y * {0}.y + {0}.z * {0}.z), 0.0)'.format( co) return 'vec3(clamp({0}, 0.0, 1.0))'.format(f) elif node.type == 'TEX_IMAGE': # Already fetched if res_var_name(node, node.outputs[1]) in parsed: return '{0}.rgb'.format(store_var_name(node)) tex_name = armutils.safe_source_name(node.name) tex = texture.make_texture(node, tex_name) if tex != None: to_linear = parsing_basecol and not tex['file'].endswith('.hdr') return '{0}.rgb'.format( texture_store(node, tex, tex_name, to_linear)) elif node.image == None: # Empty texture tex = {} tex['name'] = tex_name tex['file'] = '' return '{0}.rgb'.format(texture_store(node, tex, tex_name, True)) else: tex_store = store_var_name(node) # Pink color for missing texture curshader.write( 'vec4 {0} = vec4(1.0, 0.0, 1.0, 1.0);'.format(tex_store)) return '{0}.rgb'.format(tex_store) elif node.type == 'TEX_MAGIC': # Pass through return tovec3([0.0, 0.0, 0.0]) elif node.type == 'TEX_MUSGRAVE': # Fall back to noise curshader.add_function(functions.str_tex_noise) if node.inputs[0].is_linked: co = parse_vector_input(node.inputs[0]) else: co = 'wposition' scale = parse_value_input(node.inputs[1]) # detail = parse_value_input(node.inputs[2]) # distortion = parse_value_input(node.inputs[3]) return 'vec3(tex_noise_f({0} * {1}))'.format(co, scale) elif node.type == 'TEX_NOISE': curshader.add_function(functions.str_tex_noise) if node.inputs[0].is_linked: co = parse_vector_input(node.inputs[0]) else: co = 'wposition' scale = parse_value_input(node.inputs[1]) # detail = parse_value_input(node.inputs[2]) # distortion = parse_value_input(node.inputs[3]) # Slow.. return 'vec3(tex_noise({0} * {1}), tex_noise({0} * {1} + 0.33), tex_noise({0} * {1} + 0.66))'.format( co, scale) elif node.type == 'TEX_POINTDENSITY': # Pass through return tovec3([0.0, 0.0, 0.0]) elif node.type == 'TEX_SKY': # Pass through return tovec3([0.0, 0.0, 0.0]) elif node.type == 'TEX_VORONOI': curshader.add_function(functions.str_tex_voronoi) assets.add(armutils.get_sdk_path() + '/armory/Assets/' + 'noise64.png') assets.add_embedded_data('noise64.png') curshader.add_uniform('sampler2D snoise', link='_noise64') if node.inputs[0].is_linked: co = parse_vector_input(node.inputs[0]) else: co = 'wposition' scale = parse_value_input(node.inputs[1]) if node.coloring == 'INTENSITY': return 'vec3(tex_voronoi({0} / {1}).a)'.format(co, scale) else: # CELLS return 'tex_voronoi({0} / {1}).rgb'.format(co, scale) elif node.type == 'TEX_WAVE': # Pass through return tovec3([0.0, 0.0, 0.0]) elif node.type == 'BRIGHTCONTRAST': out_col = parse_vector_input(node.inputs[0]) bright = parse_value_input(node.inputs[1]) contr = parse_value_input(node.inputs[2]) curshader.add_function(\ """vec3 brightcontrast(const vec3 col, const float bright, const float contr) { float a = 1.0 + contr; float b = bright - contr * 0.5; return max(a * col + b, 0.0); } """) return 'brightcontrast({0}, {1}, {2})'.format(out_col, bright, contr) elif node.type == 'GAMMA': out_col = parse_vector_input(node.inputs[0]) gamma = parse_value_input(node.inputs[1]) return 'pow({0}, vec3({1}))'.format(out_col, gamma) elif node.type == 'HUE_SAT': # hue = parse_value_input(node.inputs[0]) # sat = parse_value_input(node.inputs[1]) # val = parse_value_input(node.inputs[2]) # fac = parse_value_input(node.inputs[3]) out_col = parse_vector_input(node.inputs[4]) # curshader.add_function(\ # """vec3 hue_sat(const float hue, const float sat, const float val, const float fac, const vec3 col) { # } # """) return out_col elif node.type == 'INVERT': fac = parse_value_input(node.inputs[0]) out_col = parse_vector_input(node.inputs[1]) return 'mix({0}, vec3(1.0) - ({0}), {1})'.format(out_col, fac) elif node.type == 'MIX_RGB': fac = parse_value_input(node.inputs[0]) fac_var = node_name(node.name) + '_fac' curshader.write('float {0} = {1};'.format(fac_var, fac)) col1 = parse_vector_input(node.inputs[1]) col2 = parse_vector_input(node.inputs[2]) blend = node.blend_type if blend == 'MIX': out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) elif blend == 'ADD': out_col = 'mix({0}, {0} + {1}, {2})'.format(col1, col2, fac_var) elif blend == 'MULTIPLY': out_col = 'mix({0}, {0} * {1}, {2})'.format(col1, col2, fac_var) elif blend == 'SUBTRACT': out_col = 'mix({0}, {0} - {1}, {2})'.format(col1, col2, fac_var) elif blend == 'SCREEN': out_col = '(vec3(1.0) - (vec3(1.0 - {2}) + {2} * (vec3(1.0) - {1})) * (vec3(1.0) - {0}))'.format( col1, col2, fac_var) elif blend == 'DIVIDE': out_col = '(vec3((1.0 - {2}) * {0} + {2} * {0} / {1}))'.format( col1, col2, fac_var) elif blend == 'DIFFERENCE': out_col = 'mix({0}, abs({0} - {1}), {2})'.format( col1, col2, fac_var) elif blend == 'DARKEN': out_col = 'min({0}, {1} * {2})'.format(col1, col2, fac_var) elif blend == 'LIGHTEN': out_col = 'max({0}, {1} * {2})'.format(col1, col2, fac_var) elif blend == 'OVERLAY': out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix elif blend == 'DODGE': out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix elif blend == 'BURN': out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix elif blend == 'HUE': out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix elif blend == 'SATURATION': out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix elif blend == 'VALUE': out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix elif blend == 'COLOR': out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix elif blend == 'SOFT_LIGHT': out_col = '((1.0 - {2}) * {0} + {2} * ((vec3(1.0) - {0}) * {1} * {0} + {0} * (vec3(1.0) - (vec3(1.0) - {1}) * (vec3(1.0) - {0}))));'.format( col1, col2, fac) elif blend == 'LINEAR_LIGHT': out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix # out_col = '({0} + {2} * (2.0 * ({1} - vec3(0.5))))'.format(col1, col2, fac_var) if node.use_clamp: return 'clamp({0}, vec3(0.0), vec3(1.0))'.format(out_col) else: return out_col elif node.type == 'CURVE_RGB': # Pass throuh return parse_vector_input(node.inputs[1]) elif node.type == 'BLACKBODY': # Pass constant return tovec3([0.84, 0.38, 0.0]) elif node.type == 'VALTORGB': # ColorRamp fac = parse_value_input(node.inputs[0]) interp = node.color_ramp.interpolation elems = node.color_ramp.elements if len(elems) == 1: return tovec3(elems[0].color) if interp == 'CONSTANT': fac_var = node_name(node.name) + '_fac' curshader.write('float {0} = {1};'.format(fac_var, fac)) # Get index out_i = '0' for i in range(1, len(elems)): out_i += ' + ({0} > {1} ? 1 : 0)'.format( fac_var, elems[i].position) # Write cols array cols_var = node_name(node.name) + '_cols' curshader.write('vec3 {0}[{1}];'.format(cols_var, len(elems))) for i in range(0, len(elems)): curshader.write('{0}[{1}] = vec3({2}, {3}, {4});'.format( cols_var, i, elems[i].color[0], elems[i].color[1], elems[i].color[2])) return '{0}[{1}]'.format(cols_var, out_i) else: # Linear, .. - 2 elems only, end pos assumed to be 1 # float f = clamp((pos - start) * (1.0 / (1.0 - start)), 0.0, 1.0); return 'mix({0}, {1}, clamp(({2} - {3}) * (1.0 / (1.0 - {3})), 0.0, 1.0))'.format( tovec3(elems[0].color), tovec3(elems[1].color), fac, elems[0].position) elif node.type == 'COMBHSV': # vec3 hsv2rgb(vec3 c) { # vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); # vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); # return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); # } # vec3 rgb2hsv(vec3 c) { # vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); # vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); # vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); # float d = q.x - min(q.w, q.y); # float e = 1.0e-10; # return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); # } # Pass constant return tovec3([0.0, 0.0, 0.0]) elif node.type == 'COMBRGB': r = parse_value_input(node.inputs[0]) g = parse_value_input(node.inputs[1]) b = parse_value_input(node.inputs[2]) return 'vec3({0}, {1}, {2})'.format(r, g, b) elif node.type == 'WAVELENGTH': # Pass constant return tovec3([0.0, 0.27, 0.19])
def build_project(is_play=False, is_publish=False, in_viewport=False, target=None): wrd = bpy.data.worlds['Arm'] # Set target if target == None: state.target = wrd.arm_project_target.lower() # Clear flag state.in_viewport = False # Save blend if armutils.get_save_on_build(): bpy.ops.wm.save_mainfile() log.clear() # Set camera in active scene active_scene = bpy.context.screen.scene if wrd.arm_play_active_scene else bpy.data.scenes[wrd.arm_project_scene] if active_scene.camera == None: for o in active_scene.objects: if o.type == 'CAMERA': active_scene.camera = o break # Get paths sdk_path = armutils.get_sdk_path() raw_shaders_path = sdk_path + '/armory/Shaders/' # Set dir fp = armutils.get_fp() os.chdir(fp) # Create directories sources_path = 'Sources/' + wrd.arm_project_package if not os.path.exists(sources_path): os.makedirs(sources_path) # Compile path tracer shaders if len(bpy.data.cameras) > 0 and bpy.data.cameras[0].renderpath_path == 'pathtrace_path': path_tracer.compile(raw_shaders_path + 'pt_trace_pass/pt_trace_pass.frag.glsl') # Save external scripts edited inside Blender write_texts = False for text in bpy.data.texts: if text.filepath != '' and text.is_dirty: write_texts = True break if write_texts: area = bpy.context.area old_type = area.type area.type = 'TEXT_EDITOR' for text in bpy.data.texts: if text.filepath != '' and text.is_dirty and os.path.isfile(text.filepath): area.spaces[0].text = text bpy.ops.text.save() area.type = old_type # Save internal Haxe scripts for text in bpy.data.texts: if text.filepath == '' and text.name[-3:] == '.hx': with open('Sources/' + bpy.data.worlds['Arm'].arm_project_package + '/' + text.name, 'w') as f: f.write(text.as_string()) # Export data export_data(fp, sdk_path, is_play=is_play, is_publish=is_publish, in_viewport=in_viewport) if state.playproc == None: log.print_progress(50)