forked from LeenkxTeam/LNXSDK
Compare commits
15 Commits
57cf4955a1
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 0b4184ccc2 | |||
| ddc12e8607 | |||
| 6ad647ae56 | |||
| b77aca926a | |||
| c2bb20f905 | |||
| 14c6a7be03 | |||
| 85d63e8413 | |||
| 78452aaf67 | |||
| 1f72636350 | |||
| 62433ce86a | |||
| 2be36398f7 | |||
| 2675138ddc | |||
| 572665e8e6 | |||
| 0839f39dfa | |||
| c52ae2e4f1 |
BIN
Krom/Krom.exe
BIN
Krom/Krom.exe
Binary file not shown.
Binary file not shown.
@ -7,7 +7,7 @@ bl_info = {
|
|||||||
"description": "Full Stack SDK",
|
"description": "Full Stack SDK",
|
||||||
"author": "Leenkx.com",
|
"author": "Leenkx.com",
|
||||||
"version": (2026, 5, 0),
|
"version": (2026, 5, 0),
|
||||||
"blender": (4, 5, 0),
|
"blender": (5, 2, 0),
|
||||||
"doc_url": "https://leenkx.com/",
|
"doc_url": "https://leenkx.com/",
|
||||||
"tracker_url": "https://leenkx.com/support"
|
"tracker_url": "https://leenkx.com/support"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -136,7 +136,10 @@ def always() -> float:
|
|||||||
# Force ui redraw
|
# Force ui redraw
|
||||||
if state.redraw_ui:
|
if state.redraw_ui:
|
||||||
if bpy.context.screen is not None:
|
if bpy.context.screen is not None:
|
||||||
|
krom_active = bool(lnx.render_engine._active_krom_engines)
|
||||||
for area in bpy.context.screen.areas:
|
for area in bpy.context.screen.areas:
|
||||||
|
if area.type == 'VIEW_3D' and krom_active:
|
||||||
|
continue
|
||||||
if area.type in ('NODE_EDITOR', 'PROPERTIES', 'VIEW_3D'):
|
if area.type in ('NODE_EDITOR', 'PROPERTIES', 'VIEW_3D'):
|
||||||
area.tag_redraw()
|
area.tag_redraw()
|
||||||
state.redraw_ui = False
|
state.redraw_ui = False
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import bpy
|
||||||
import importlib
|
import importlib
|
||||||
import inspect
|
import inspect
|
||||||
import pkgutil
|
import pkgutil
|
||||||
|
|||||||
@ -804,41 +804,71 @@ def viewport_build_done():
|
|||||||
_viewport_pending_launches.clear()
|
_viewport_pending_launches.clear()
|
||||||
log.error('Viewport build failed, check console')
|
log.error('Viewport build failed, check console')
|
||||||
|
|
||||||
def play_viewport(viewport_id, width=1920, height=1080):
|
def play(viewport_id=None, width=1920, height=1080):
|
||||||
"""Launch Krom in a viewport"""
|
"""Launch player external or embedded when viewport_id is passed"""
|
||||||
global _viewport_build_in_progress, _viewport_pending_launches, _viewport_proc_build
|
global _viewport_build_in_progress, _viewport_pending_launches, _viewport_proc_build, scripts_mtime
|
||||||
|
|
||||||
if not viewport_id:
|
if viewport_id and 'Lnx' not in bpy.data.worlds:
|
||||||
log.error('No viewport_id: play_viewport requires an id')
|
|
||||||
return
|
|
||||||
|
|
||||||
if 'Lnx' not in bpy.data.worlds:
|
|
||||||
log.error('No Lnx world found - cannot start viewport server')
|
log.error('No Lnx world found - cannot start viewport server')
|
||||||
return
|
return
|
||||||
|
|
||||||
wrd = bpy.data.worlds['Lnx']
|
wrd = bpy.data.worlds['Lnx']
|
||||||
|
target = 'krom' if viewport_id else runtime_to_target()
|
||||||
|
|
||||||
krom_js_path = lnx.utils.get_fp_build() + '/debug/krom/krom.js'
|
khajs_path = get_khajs_path(target)
|
||||||
if os.path.exists(krom_js_path) and not wrd.lnx_recompile:
|
if not wrd.lnx_cache_build or \
|
||||||
run_viewport_runtime(viewport_id, width, height)
|
not os.path.isfile(khajs_path) or \
|
||||||
return
|
assets.khafile_defs_last != assets.khafile_defs or \
|
||||||
|
state.last_target != state.target:
|
||||||
|
wrd.lnx_recompile = True
|
||||||
|
|
||||||
pending_entry = (viewport_id, width, height)
|
state.last_target = state.target
|
||||||
if pending_entry not in _viewport_pending_launches:
|
|
||||||
_viewport_pending_launches.append(pending_entry)
|
|
||||||
|
|
||||||
if _viewport_build_in_progress:
|
state.mod_scripts = []
|
||||||
if _viewport_proc_build is not None and _viewport_proc_build.poll() is None:
|
script_path = lnx.utils.get_fp() + '/Sources/' + lnx.utils.safestr(wrd.lnx_project_package)
|
||||||
log.info(f'Build in progress, viewport {viewport_id} will launch when ready')
|
if os.path.isdir(script_path):
|
||||||
|
new_mtime = scripts_mtime
|
||||||
|
for fn in glob.iglob(os.path.join(script_path, '**', '*.hx'), recursive=True):
|
||||||
|
mtime = os.path.getmtime(fn)
|
||||||
|
if scripts_mtime < mtime:
|
||||||
|
lnx.utils.fetch_script_props(fn)
|
||||||
|
fn = fn.split('Sources/')[1]
|
||||||
|
fn = fn[:-3]
|
||||||
|
fn = fn.replace('/', '.')
|
||||||
|
state.mod_scripts.append(fn)
|
||||||
|
wrd.lnx_recompile = True
|
||||||
|
if new_mtime < mtime:
|
||||||
|
new_mtime = mtime
|
||||||
|
scripts_mtime = new_mtime
|
||||||
|
if len(state.mod_scripts) > 0:
|
||||||
|
lnx.utils.fetch_trait_props()
|
||||||
|
|
||||||
|
if viewport_id:
|
||||||
|
krom_js_path = lnx.utils.get_fp_build() + '/debug/krom/krom.js'
|
||||||
|
if os.path.exists(krom_js_path) and not wrd.lnx_recompile:
|
||||||
|
run_viewport_runtime(viewport_id, width, height)
|
||||||
return
|
return
|
||||||
else:
|
|
||||||
_viewport_build_in_progress = False
|
|
||||||
|
|
||||||
_viewport_build_in_progress = True
|
pending_entry = (viewport_id, width, height)
|
||||||
|
if pending_entry not in _viewport_pending_launches:
|
||||||
|
_viewport_pending_launches.append(pending_entry)
|
||||||
|
|
||||||
build(target='krom', is_viewport=True, viewport_width=width, viewport_height=height)
|
if _viewport_build_in_progress:
|
||||||
|
if _viewport_proc_build is not None and _viewport_proc_build.poll() is None:
|
||||||
|
log.info(f'Build in progress, viewport {viewport_id} will launch when ready')
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
_viewport_build_in_progress = False
|
||||||
|
|
||||||
compile_viewport(assets_only=(not wrd.lnx_recompile))
|
_viewport_build_in_progress = True
|
||||||
|
|
||||||
|
assets.invalidate_enabled = False
|
||||||
|
build(target='krom', is_viewport=True, viewport_width=width, viewport_height=height)
|
||||||
|
compile_viewport(assets_only=(not wrd.lnx_recompile))
|
||||||
|
assets.invalidate_enabled = True
|
||||||
|
else:
|
||||||
|
build(target=target, is_play=True)
|
||||||
|
compile(assets_only=(not wrd.lnx_recompile))
|
||||||
|
|
||||||
|
|
||||||
def stop_viewport(viewport_id=None):
|
def stop_viewport(viewport_id=None):
|
||||||
@ -905,43 +935,6 @@ def get_khajs_path(target):
|
|||||||
return lnx.utils.build_dir() + '/debug/krom/krom.js'
|
return lnx.utils.build_dir() + '/debug/krom/krom.js'
|
||||||
return lnx.utils.build_dir() + '/debug/html5/kha.js'
|
return lnx.utils.build_dir() + '/debug/html5/kha.js'
|
||||||
|
|
||||||
def play():
|
|
||||||
global scripts_mtime
|
|
||||||
wrd = bpy.data.worlds['Lnx']
|
|
||||||
|
|
||||||
build(target=runtime_to_target(), is_play=True)
|
|
||||||
|
|
||||||
khajs_path = get_khajs_path(state.target)
|
|
||||||
if not wrd.lnx_cache_build or \
|
|
||||||
not os.path.isfile(khajs_path) or \
|
|
||||||
assets.khafile_defs_last != assets.khafile_defs or \
|
|
||||||
state.last_target != state.target:
|
|
||||||
wrd.lnx_recompile = True
|
|
||||||
|
|
||||||
state.last_target = state.target
|
|
||||||
|
|
||||||
# Trait sources modified
|
|
||||||
state.mod_scripts = []
|
|
||||||
script_path = lnx.utils.get_fp() + '/Sources/' + lnx.utils.safestr(wrd.lnx_project_package)
|
|
||||||
if os.path.isdir(script_path):
|
|
||||||
new_mtime = scripts_mtime
|
|
||||||
for fn in glob.iglob(os.path.join(script_path, '**', '*.hx'), recursive=True):
|
|
||||||
mtime = os.path.getmtime(fn)
|
|
||||||
if scripts_mtime < mtime:
|
|
||||||
lnx.utils.fetch_script_props(fn) # Trait props
|
|
||||||
fn = fn.split('Sources/')[1]
|
|
||||||
fn = fn[:-3] #.hx
|
|
||||||
fn = fn.replace('/', '.')
|
|
||||||
state.mod_scripts.append(fn)
|
|
||||||
wrd.lnx_recompile = True
|
|
||||||
if new_mtime < mtime:
|
|
||||||
new_mtime = mtime
|
|
||||||
scripts_mtime = new_mtime
|
|
||||||
if len(state.mod_scripts) > 0: # Trait props
|
|
||||||
lnx.utils.fetch_trait_props()
|
|
||||||
|
|
||||||
compile(assets_only=(not wrd.lnx_recompile))
|
|
||||||
|
|
||||||
def build_success():
|
def build_success():
|
||||||
log.clear()
|
log.clear()
|
||||||
wrd = bpy.data.worlds['Lnx']
|
wrd = bpy.data.worlds['Lnx']
|
||||||
|
|||||||
@ -219,7 +219,15 @@ def parse_material_output(node: bpy.types.Node, custom_particle_node: bpy.types.
|
|||||||
curshader = state.frag
|
curshader = state.frag
|
||||||
state.curshader = curshader
|
state.curshader = curshader
|
||||||
|
|
||||||
out_basecol, out_roughness, out_metallic, out_occlusion, out_specular, out_opacity, out_ior, out_emission_col = parse_shader_input(node.inputs[0])
|
outs = parse_shader_input(node.inputs[0])
|
||||||
|
out_basecol = outs[0]
|
||||||
|
out_roughness = outs[1]
|
||||||
|
out_metallic = outs[2]
|
||||||
|
out_occlusion = outs[3]
|
||||||
|
out_specular = outs[4]
|
||||||
|
out_opacity = outs[5]
|
||||||
|
out_ior = outs[6]
|
||||||
|
out_emission_col = outs[7]
|
||||||
if parse_surface:
|
if parse_surface:
|
||||||
curshader.write(f'basecol = {out_basecol};')
|
curshader.write(f'basecol = {out_basecol};')
|
||||||
curshader.write(f'roughness = {out_roughness};')
|
curshader.write(f'roughness = {out_roughness};')
|
||||||
@ -227,6 +235,21 @@ def parse_material_output(node: bpy.types.Node, custom_particle_node: bpy.types.
|
|||||||
curshader.write(f'occlusion = {out_occlusion};')
|
curshader.write(f'occlusion = {out_occlusion};')
|
||||||
curshader.write(f'specular = {out_specular};')
|
curshader.write(f'specular = {out_specular};')
|
||||||
curshader.write(f'emissionCol = {out_emission_col};')
|
curshader.write(f'emissionCol = {out_emission_col};')
|
||||||
|
curshader.write(f'subsurface = {outs[8]};')
|
||||||
|
curshader.write(f'subsurfaceRadius = {outs[9]};')
|
||||||
|
curshader.write(f'subsurfaceColor = {outs[10]};')
|
||||||
|
curshader.write(f'specularTint = {outs[11]};')
|
||||||
|
curshader.write(f'anisotropy = {outs[12]};')
|
||||||
|
curshader.write(f'anisoRot = {outs[13]};')
|
||||||
|
curshader.write(f'sheen = {outs[14]};')
|
||||||
|
curshader.write(f'sheenRough = {outs[15]};')
|
||||||
|
curshader.write(f'sheenTint = {outs[16]};')
|
||||||
|
curshader.write(f'clearcoat = {outs[17]};')
|
||||||
|
curshader.write(f'clearcoatRough = {outs[18]};')
|
||||||
|
curshader.write(f'transmission = {outs[19]};')
|
||||||
|
curshader.write(f'transmissionRough = {outs[20]};')
|
||||||
|
curshader.write(f'thinWall = {outs[21]};')
|
||||||
|
curshader.write(f'tangent = {outs[22]};')
|
||||||
|
|
||||||
if mat_state.emission_type == mat_state.EmissionType.SHADELESS:
|
if mat_state.emission_type == mat_state.EmissionType.SHADELESS:
|
||||||
if '_EmissionShadeless' not in wrd.world_defs:
|
if '_EmissionShadeless' not in wrd.world_defs:
|
||||||
@ -330,6 +353,10 @@ def parse_shader(node: bpy.types.Node, socket: bpy.types.NodeSocket) -> Tuple[st
|
|||||||
'BSDF_GLASS',
|
'BSDF_GLASS',
|
||||||
'HOLDOUT',
|
'HOLDOUT',
|
||||||
'SUBSURFACE_SCATTERING',
|
'SUBSURFACE_SCATTERING',
|
||||||
|
'BSDF_REFRACTION',
|
||||||
|
'BSDF_TOON',
|
||||||
|
'BSDF_HAIR',
|
||||||
|
'BSDF_HAIR_PRINCIPLED',
|
||||||
'BSDF_TRANSLUCENT',
|
'BSDF_TRANSLUCENT',
|
||||||
'BSDF_TRANSPARENT',
|
'BSDF_TRANSPARENT',
|
||||||
'BSDF_VELVET',
|
'BSDF_VELVET',
|
||||||
|
|||||||
@ -16,19 +16,59 @@ if lnx.is_reload(__name__):
|
|||||||
else:
|
else:
|
||||||
lnx.enable_reload(__name__)
|
lnx.enable_reload(__name__)
|
||||||
|
|
||||||
|
# Principled BSDF socket names that differ between Blender versions
|
||||||
|
if bpy.app.version < (4, 0, 0):
|
||||||
|
EMISSION_COLOR = 'Emission'
|
||||||
|
SUBSURFACE = 'Subsurface'
|
||||||
|
SUBSURFACE_RADIUS = 'Subsurface Radius'
|
||||||
|
SUBSURFACE_COLOR = 'Subsurface Color'
|
||||||
|
SPECULAR = 'Specular'
|
||||||
|
SPECULAR_TINT = 'Specular Tint'
|
||||||
|
ANISOTROPIC = 'Anisotropic'
|
||||||
|
ANISOTROPIC_ROT = 'Anisotropic Rotation'
|
||||||
|
SHEEN = 'Sheen'
|
||||||
|
SHEEN_TINT = 'Sheen Tint'
|
||||||
|
CLEARCOAT = 'Clearcoat'
|
||||||
|
CLEARCOAT_ROUGHNESS = 'Clearcoat Roughness'
|
||||||
|
CLEARCOAT_NORMAL = 'Clearcoat Normal'
|
||||||
|
TRANSMISSION = 'Transmission'
|
||||||
|
TRANSMISSION_ROUGHNESS = 'Transmission Roughness'
|
||||||
|
TANGENT = 'Tangent'
|
||||||
|
else:
|
||||||
|
EMISSION_COLOR = 'Emission Color'
|
||||||
|
SUBSURFACE = 'Subsurface Weight'
|
||||||
|
SUBSURFACE_RADIUS = 'Subsurface Radius'
|
||||||
|
SUBSURFACE_COLOR = 'Subsurface Color'
|
||||||
|
SPECULAR = 'Specular IOR Level'
|
||||||
|
SPECULAR_TINT = 'Specular Tint'
|
||||||
|
ANISOTROPIC = 'Anisotropic'
|
||||||
|
ANISOTROPIC_ROT = 'Anisotropic Rotation'
|
||||||
|
SHEEN = 'Sheen Weight'
|
||||||
|
SHEEN_TINT = 'Sheen Tint'
|
||||||
|
SHEEN_ROUGHNESS = 'Sheen Roughness'
|
||||||
|
CLEARCOAT = 'Coat Weight'
|
||||||
|
CLEARCOAT_ROUGHNESS = 'Coat Roughness'
|
||||||
|
CLEARCOAT_NORMAL = 'Coat Normal'
|
||||||
|
COAT_IOR = 'Coat IOR'
|
||||||
|
COAT_TINT = 'Coat Tint'
|
||||||
|
TRANSMISSION = 'Transmission Weight'
|
||||||
|
TRANSMISSION_ROUGHNESS = 'Transmission Roughness'
|
||||||
|
TANGENT = 'Tangent'
|
||||||
|
THIN_WALL = 'Thin Wall'
|
||||||
|
|
||||||
|
|
||||||
def parse_mixshader(node: bpy.types.ShaderNodeMixShader, out_socket: NodeSocket, state: ParserState) -> None:
|
def parse_mixshader(node: bpy.types.ShaderNodeMixShader, out_socket: NodeSocket, state: ParserState) -> None:
|
||||||
# Skip mixing if only one input is effectively used
|
# Skip mixing if only one input is effectively used
|
||||||
if not node.inputs[0].is_linked:
|
if not node.inputs['Fac'].is_linked:
|
||||||
if node.inputs[0].default_value <= 0.0:
|
if node.inputs['Fac'].default_value <= 0.0:
|
||||||
c.parse_shader_input(node.inputs[1])
|
c.parse_shader_input(node.inputs[1])
|
||||||
return
|
return
|
||||||
elif node.inputs[0].default_value >= 1.0:
|
elif node.inputs['Fac'].default_value >= 1.0:
|
||||||
c.parse_shader_input(node.inputs[2])
|
c.parse_shader_input(node.inputs[2])
|
||||||
return
|
return
|
||||||
|
|
||||||
prefix = '' if node.inputs[0].is_linked else 'const '
|
prefix = '' if node.inputs['Fac'].is_linked else 'const '
|
||||||
fac = c.parse_value_input(node.inputs[0])
|
fac = c.parse_value_input(node.inputs['Fac'])
|
||||||
fac_var = c.node_name(node.name) + '_fac' + state.get_parser_pass_suffix()
|
fac_var = c.node_name(node.name) + '_fac' + state.get_parser_pass_suffix()
|
||||||
fac_inv_var = c.node_name(node.name) + '_fac_inv'
|
fac_inv_var = c.node_name(node.name) + '_fac_inv'
|
||||||
state.curshader.write('{0}float {1} = clamp({2}, 0.0, 1.0);'.format(prefix, fac_var, fac))
|
state.curshader.write('{0}float {1} = clamp({2}, 0.0, 1.0);'.format(prefix, fac_var, fac))
|
||||||
@ -36,134 +76,193 @@ def parse_mixshader(node: bpy.types.ShaderNodeMixShader, out_socket: NodeSocket,
|
|||||||
|
|
||||||
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
||||||
sss_before_1 = mat_state.needs_sss
|
sss_before_1 = mat_state.needs_sss
|
||||||
bc1, rough1, met1, occ1, spec1, opac1, ior1, emi1 = c.parse_shader_input(node.inputs[1])
|
o1 = c.parse_shader_input(node.inputs[1])
|
||||||
sss_1 = mat_state.needs_sss
|
sss_1 = mat_state.needs_sss
|
||||||
ek1 = mat_state.emission_type
|
ek1 = mat_state.emission_type
|
||||||
|
|
||||||
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
||||||
mat_state.needs_sss = sss_before_1 # Reset to state before parsing input 1
|
mat_state.needs_sss = sss_before_1 # Reset to state before parsing input 1
|
||||||
bc2, rough2, met2, occ2, spec2, opac2, ior2, emi2 = c.parse_shader_input(node.inputs[2])
|
o2 = c.parse_shader_input(node.inputs[2])
|
||||||
sss_2 = mat_state.needs_sss
|
sss_2 = mat_state.needs_sss
|
||||||
ek2 = mat_state.emission_type
|
ek2 = mat_state.emission_type
|
||||||
mat_state.needs_sss = sss_1 or sss_2
|
mat_state.needs_sss = sss_1 or sss_2
|
||||||
|
|
||||||
if state.parse_surface:
|
if state.parse_surface:
|
||||||
state.out_basecol = '({0} * {3} + {1} * {2})'.format(bc1, bc2, fac_var, fac_inv_var)
|
fm = '{0} * {3} + {1} * {2}' # fac mix template: a*fac_inv + b*fac
|
||||||
state.out_roughness = '({0} * {3} + {1} * {2})'.format(rough1, rough2, fac_var, fac_inv_var)
|
fv = (fac_var, fac_inv_var)
|
||||||
state.out_metallic = '({0} * {3} + {1} * {2})'.format(met1, met2, fac_var, fac_inv_var)
|
state.out_basecol = fm.format(o1[0], o2[0], fv[0], fv[1])
|
||||||
state.out_occlusion = '({0} * {3} + {1} * {2})'.format(occ1, occ2, fac_var, fac_inv_var)
|
state.out_roughness = fm.format(o1[1], o2[1], fv[0], fv[1])
|
||||||
state.out_specular = '({0} * {3} + {1} * {2})'.format(spec1, spec2, fac_var, fac_inv_var)
|
state.out_metallic = fm.format(o1[2], o2[2], fv[0], fv[1])
|
||||||
state.out_emission_col = '({0} * {3} + {1} * {2})'.format(emi1, emi2, fac_var, fac_inv_var)
|
state.out_occlusion = fm.format(o1[3], o2[3], fv[0], fv[1])
|
||||||
|
state.out_specular = fm.format(o1[4], o2[4], fv[0], fv[1])
|
||||||
|
state.out_emission_col = fm.format(o1[7], o2[7], fv[0], fv[1])
|
||||||
|
state.out_subsurface = fm.format(o1[8], o2[8], fv[0], fv[1])
|
||||||
|
state.out_subsurface_radius = fm.format(o1[9], o2[9], fv[0], fv[1])
|
||||||
|
state.out_subsurface_color = fm.format(o1[10], o2[10], fv[0], fv[1])
|
||||||
|
state.out_specular_tint = fm.format(o1[11], o2[11], fv[0], fv[1])
|
||||||
|
state.out_anisotropy = fm.format(o1[12], o2[12], fv[0], fv[1])
|
||||||
|
state.out_aniso_rot = fm.format(o1[13], o2[13], fv[0], fv[1])
|
||||||
|
state.out_sheen = fm.format(o1[14], o2[14], fv[0], fv[1])
|
||||||
|
state.out_sheen_rough = fm.format(o1[15], o2[15], fv[0], fv[1])
|
||||||
|
state.out_sheen_tint = fm.format(o1[16], o2[16], fv[0], fv[1])
|
||||||
|
state.out_clearcoat = fm.format(o1[17], o2[17], fv[0], fv[1])
|
||||||
|
state.out_clearcoat_rough = fm.format(o1[18], o2[18], fv[0], fv[1])
|
||||||
|
state.out_transmission = fm.format(o1[19], o2[19], fv[0], fv[1])
|
||||||
|
state.out_transmission_rough = fm.format(o1[20], o2[20], fv[0], fv[1])
|
||||||
|
state.out_thin_wall = fm.format(o1[21], o2[21], fv[0], fv[1])
|
||||||
|
state.out_tangent = fm.format(o1[22], o2[22], fv[0], fv[1])
|
||||||
mat_state.emission_type = mat_state.EmissionType.get_effective_combination(ek1, ek2)
|
mat_state.emission_type = mat_state.EmissionType.get_effective_combination(ek1, ek2)
|
||||||
if state.parse_opacity:
|
if state.parse_opacity:
|
||||||
state.out_opacity = '({0} * {3} + {1} * {2})'.format(opac1, opac2, fac_var, fac_inv_var)
|
fm = '{0} * {3} + {1} * {2}'
|
||||||
state.out_ior = '({0} * {3} + {1} * {2})'.format(ior1, ior2, fac_var, fac_inv_var)
|
fv = (fac_var, fac_inv_var)
|
||||||
|
state.out_opacity = fm.format(o1[5], o2[5], fv[0], fv[1])
|
||||||
|
state.out_ior = fm.format(o1[6], o2[6], fv[0], fv[1])
|
||||||
|
|
||||||
def parse_addshader(node: bpy.types.ShaderNodeAddShader, out_socket: NodeSocket, state: ParserState) -> None:
|
def parse_addshader(node: bpy.types.ShaderNodeAddShader, out_socket: NodeSocket, state: ParserState) -> None:
|
||||||
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
||||||
sss_before_1 = mat_state.needs_sss
|
sss_before_1 = mat_state.needs_sss
|
||||||
bc1, rough1, met1, occ1, spec1, opac1, ior1, emi1 = c.parse_shader_input(node.inputs[0])
|
o1 = c.parse_shader_input(node.inputs[0])
|
||||||
sss_1 = mat_state.needs_sss
|
sss_1 = mat_state.needs_sss
|
||||||
ek1 = mat_state.emission_type
|
ek1 = mat_state.emission_type
|
||||||
|
|
||||||
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
||||||
mat_state.needs_sss = sss_before_1 # Reset to state before parsing input 0
|
mat_state.needs_sss = sss_before_1 # Reset to state before parsing input 0
|
||||||
bc2, rough2, met2, occ2, spec2, opac2, ior2, emi2 = c.parse_shader_input(node.inputs[1])
|
o2 = c.parse_shader_input(node.inputs[1])
|
||||||
sss_2 = mat_state.needs_sss
|
sss_2 = mat_state.needs_sss
|
||||||
ek2 = mat_state.emission_type
|
ek2 = mat_state.emission_type
|
||||||
mat_state.needs_sss = sss_1 or sss_2
|
mat_state.needs_sss = sss_1 or sss_2
|
||||||
|
|
||||||
if state.parse_surface:
|
if state.parse_surface:
|
||||||
state.out_basecol = '({0} + {1})'.format(bc1, bc2)
|
am = '{0} * 0.5 + {1} * 0.5' # add mix template (average)
|
||||||
state.out_roughness = '({0} * 0.5 + {1} * 0.5)'.format(rough1, rough2)
|
state.out_basecol = '({0} + {1})'.format(o1[0], o2[0])
|
||||||
state.out_metallic = '({0} * 0.5 + {1} * 0.5)'.format(met1, met2)
|
state.out_roughness = am.format(o1[1], o2[1])
|
||||||
state.out_occlusion = '({0} * 0.5 + {1} * 0.5)'.format(occ1, occ2)
|
state.out_metallic = am.format(o1[2], o2[2])
|
||||||
state.out_specular = '({0} * 0.5 + {1} * 0.5)'.format(spec1, spec2)
|
state.out_occlusion = am.format(o1[3], o2[3])
|
||||||
state.out_emission_col = '({0} + {1})'.format(emi1, emi2)
|
state.out_specular = am.format(o1[4], o2[4])
|
||||||
|
state.out_emission_col = '({0} + {1})'.format(o1[7], o2[7])
|
||||||
|
state.out_subsurface = am.format(o1[8], o2[8])
|
||||||
|
state.out_subsurface_radius = am.format(o1[9], o2[9])
|
||||||
|
state.out_subsurface_color = am.format(o1[10], o2[10])
|
||||||
|
state.out_specular_tint = am.format(o1[11], o2[11])
|
||||||
|
state.out_anisotropy = am.format(o1[12], o2[12])
|
||||||
|
state.out_aniso_rot = am.format(o1[13], o2[13])
|
||||||
|
state.out_sheen = am.format(o1[14], o2[14])
|
||||||
|
state.out_sheen_rough = am.format(o1[15], o2[15])
|
||||||
|
state.out_sheen_tint = am.format(o1[16], o2[16])
|
||||||
|
state.out_clearcoat = am.format(o1[17], o2[17])
|
||||||
|
state.out_clearcoat_rough = am.format(o1[18], o2[18])
|
||||||
|
state.out_transmission = am.format(o1[19], o2[19])
|
||||||
|
state.out_transmission_rough = am.format(o1[20], o2[20])
|
||||||
|
state.out_thin_wall = am.format(o1[21], o2[21])
|
||||||
|
state.out_tangent = am.format(o1[22], o2[22])
|
||||||
mat_state.emission_type = mat_state.EmissionType.get_effective_combination(ek1, ek2)
|
mat_state.emission_type = mat_state.EmissionType.get_effective_combination(ek1, ek2)
|
||||||
if state.parse_opacity:
|
if state.parse_opacity:
|
||||||
state.out_opacity = '({0} * 0.5 + {1} * 0.5)'.format(opac1, opac2)
|
am = '{0} * 0.5 + {1} * 0.5'
|
||||||
state.out_ior = '({0} * 0.5 + {1} * 0.5)'.format(ior1, ior2)
|
state.out_opacity = am.format(o1[5], o2[5])
|
||||||
|
state.out_ior = am.format(o1[6], o2[6])
|
||||||
|
|
||||||
# TODO: Refactor using c.get_*_input()
|
# TODO: Refactor using c.get_*_input()
|
||||||
|
|
||||||
|
|
||||||
if bpy.app.version < (2, 92, 0):
|
if bpy.app.version < (2, 91, 0):
|
||||||
def parse_bsdfprincipled(node: bpy.types.ShaderNodeBsdfPrincipled, out_socket: NodeSocket, state: ParserState) -> None:
|
def parse_bsdfprincipled(node: bpy.types.ShaderNodeBsdfPrincipled, out_socket: NodeSocket, state: ParserState) -> None:
|
||||||
if state.parse_surface:
|
if state.parse_surface:
|
||||||
c.write_normal(node.inputs[20])
|
c.write_normal(node.inputs['Normal'])
|
||||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
state.out_basecol = c.parse_vector_input(node.inputs['Base Color'])
|
||||||
if node.inputs[1].is_linked or node.inputs[1].default_value > 0.0:
|
if node.inputs[SUBSURFACE].is_linked or node.inputs[SUBSURFACE].default_value > 0.0:
|
||||||
mat_state.needs_sss = True
|
mat_state.needs_sss = True
|
||||||
state.out_metallic = c.parse_value_input(node.inputs[4])
|
state.out_subsurface = c.parse_value_input(node.inputs[SUBSURFACE])
|
||||||
state.out_specular = c.parse_value_input(node.inputs[5])
|
state.out_subsurface_radius = c.parse_vector_input(node.inputs[SUBSURFACE_RADIUS])
|
||||||
state.out_roughness = c.parse_value_input(node.inputs[7])
|
state.out_subsurface_color = c.parse_vector_input(node.inputs[SUBSURFACE_COLOR])
|
||||||
if node.inputs['Emission'].is_linked or not mat_utils.equals_color_socket(node.inputs['Emission'], (0.0, 0.0, 0.0), comp_alpha=False):
|
state.out_metallic = c.parse_value_input(node.inputs['Metallic'])
|
||||||
emission_col = c.parse_vector_input(node.inputs[17])
|
state.out_specular = c.parse_value_input(node.inputs[SPECULAR])
|
||||||
|
state.out_specular_tint = 'vec3({})'.format(c.parse_value_input(node.inputs[SPECULAR_TINT]))
|
||||||
|
state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
|
||||||
|
state.out_anisotropy = c.parse_value_input(node.inputs[ANISOTROPIC])
|
||||||
|
state.out_aniso_rot = c.parse_value_input(node.inputs[ANISOTROPIC_ROT])
|
||||||
|
state.out_sheen = c.parse_value_input(node.inputs[SHEEN])
|
||||||
|
state.out_sheen_tint = 'vec3({})'.format(c.parse_value_input(node.inputs[SHEEN_TINT]))
|
||||||
|
state.out_clearcoat = c.parse_value_input(node.inputs[CLEARCOAT])
|
||||||
|
state.out_clearcoat_rough = c.parse_value_input(node.inputs[CLEARCOAT_ROUGHNESS])
|
||||||
|
state.out_transmission = c.parse_value_input(node.inputs[TRANSMISSION])
|
||||||
|
trans_rough_socket = node.inputs.get(TRANSMISSION_ROUGHNESS)
|
||||||
|
if trans_rough_socket is not None:
|
||||||
|
state.out_transmission_rough = c.parse_value_input(trans_rough_socket)
|
||||||
|
state.out_tangent = c.parse_vector_input(node.inputs[TANGENT])
|
||||||
|
if node.inputs[EMISSION_COLOR].is_linked or not mat_utils.equals_color_socket(node.inputs[EMISSION_COLOR], (0.0, 0.0, 0.0), comp_alpha=False):
|
||||||
|
emission_col = c.parse_vector_input(node.inputs[EMISSION_COLOR])
|
||||||
state.out_emission_col = emission_col
|
state.out_emission_col = emission_col
|
||||||
mat_state.emission_type = mat_state.EmissionType.SHADED
|
mat_state.emission_type = mat_state.EmissionType.SHADED
|
||||||
else:
|
else:
|
||||||
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
||||||
if state.parse_opacity:
|
if state.parse_opacity:
|
||||||
state.out_ior = c.parse_value_input(node.inputs[14])
|
state.out_ior = c.parse_value_input(node.inputs['IOR'])
|
||||||
# In Blender 2.83, Alpha socket is at index 18, not 19
|
|
||||||
if 'Alpha' in node.inputs:
|
if 'Alpha' in node.inputs:
|
||||||
state.out_opacity = c.parse_value_input(node.inputs['Alpha'])
|
state.out_opacity = c.parse_value_input(node.inputs['Alpha'])
|
||||||
else:
|
else:
|
||||||
state.out_opacity = '1.0'
|
state.out_opacity = '1.0'
|
||||||
if bpy.app.version >= (2, 92, 0) and bpy.app.version <= (4, 1, 0):
|
if bpy.app.version >= (2, 91, 0) and bpy.app.version < (4, 0, 0):
|
||||||
def parse_bsdfprincipled(node: bpy.types.ShaderNodeBsdfPrincipled, out_socket: NodeSocket, state: ParserState) -> None:
|
def parse_bsdfprincipled(node: bpy.types.ShaderNodeBsdfPrincipled, out_socket: NodeSocket, state: ParserState) -> None:
|
||||||
if state.parse_surface:
|
if state.parse_surface:
|
||||||
c.write_normal(node.inputs[22])
|
c.write_normal(node.inputs['Normal'])
|
||||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
state.out_basecol = c.parse_vector_input(node.inputs['Base Color'])
|
||||||
if node.inputs[1].is_linked or node.inputs[1].default_value > 0.0:
|
if node.inputs[SUBSURFACE].is_linked or node.inputs[SUBSURFACE].default_value > 0.0:
|
||||||
mat_state.needs_sss = True
|
mat_state.needs_sss = True
|
||||||
# subsurface_radius = c.parse_vector_input(node.inputs[2])
|
state.out_subsurface = c.parse_value_input(node.inputs[SUBSURFACE])
|
||||||
# subsurface_color = c.parse_vector_input(node.inputs[3])
|
state.out_subsurface_radius = c.parse_vector_input(node.inputs[SUBSURFACE_RADIUS])
|
||||||
state.out_metallic = c.parse_value_input(node.inputs[6])
|
state.out_subsurface_color = c.parse_vector_input(node.inputs[SUBSURFACE_COLOR])
|
||||||
state.out_specular = c.parse_value_input(node.inputs[7])
|
state.out_metallic = c.parse_value_input(node.inputs['Metallic'])
|
||||||
# specular_tint = c.parse_vector_input(node.inputs[6])
|
state.out_specular = c.parse_value_input(node.inputs[SPECULAR])
|
||||||
state.out_roughness = c.parse_value_input(node.inputs[9])
|
state.out_specular_tint = 'vec3({})'.format(c.parse_value_input(node.inputs[SPECULAR_TINT]))
|
||||||
# aniso = c.parse_vector_input(node.inputs[8])
|
state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
|
||||||
# aniso_rot = c.parse_vector_input(node.inputs[9])
|
state.out_anisotropy = c.parse_value_input(node.inputs[ANISOTROPIC])
|
||||||
# sheen = c.parse_vector_input(node.inputs[10])
|
state.out_aniso_rot = c.parse_value_input(node.inputs[ANISOTROPIC_ROT])
|
||||||
# sheen_tint = c.parse_vector_input(node.inputs[11])
|
state.out_sheen = c.parse_value_input(node.inputs[SHEEN])
|
||||||
# clearcoat = c.parse_vector_input(node.inputs[12])
|
state.out_sheen_tint = 'vec3({})'.format(c.parse_value_input(node.inputs[SHEEN_TINT]))
|
||||||
# clearcoat_rough = c.parse_vector_input(node.inputs[13])
|
state.out_clearcoat = c.parse_value_input(node.inputs[CLEARCOAT])
|
||||||
# ior = c.parse_vector_input(node.inputs[14])
|
state.out_clearcoat_rough = c.parse_value_input(node.inputs[CLEARCOAT_ROUGHNESS])
|
||||||
# transmission = c.parse_vector_input(node.inputs[15])
|
state.out_transmission = c.parse_value_input(node.inputs[TRANSMISSION])
|
||||||
# transmission_roughness = c.parse_vector_input(node.inputs[16])
|
state.out_transmission_rough = c.parse_value_input(node.inputs[TRANSMISSION_ROUGHNESS])
|
||||||
|
state.out_tangent = c.parse_vector_input(node.inputs[TANGENT])
|
||||||
if (node.inputs['Emission Strength'].is_linked or node.inputs['Emission Strength'].default_value != 0.0)\
|
if (node.inputs['Emission Strength'].is_linked or node.inputs['Emission Strength'].default_value != 0.0)\
|
||||||
and (node.inputs['Emission'].is_linked or not mat_utils.equals_color_socket(node.inputs['Emission'], (0.0, 0.0, 0.0), comp_alpha=False)):
|
and (node.inputs[EMISSION_COLOR].is_linked or not mat_utils.equals_color_socket(node.inputs[EMISSION_COLOR], (0.0, 0.0, 0.0), comp_alpha=False)):
|
||||||
emission_col = c.parse_vector_input(node.inputs[19])
|
emission_col = c.parse_vector_input(node.inputs[EMISSION_COLOR])
|
||||||
emission_strength = c.parse_value_input(node.inputs[20])
|
emission_strength = c.parse_value_input(node.inputs['Emission Strength'])
|
||||||
state.out_emission_col = '({0} * {1})'.format(emission_col, emission_strength)
|
state.out_emission_col = '({0} * {1})'.format(emission_col, emission_strength)
|
||||||
mat_state.emission_type = mat_state.EmissionType.SHADED
|
mat_state.emission_type = mat_state.EmissionType.SHADED
|
||||||
else:
|
else:
|
||||||
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
||||||
# clearcoar_normal = c.parse_vector_input(node.inputs[21])
|
|
||||||
# tangent = c.parse_vector_input(node.inputs[22])
|
|
||||||
if state.parse_opacity:
|
if state.parse_opacity:
|
||||||
state.out_ior = c.parse_value_input(node.inputs[16])
|
state.out_ior = c.parse_value_input(node.inputs['IOR'])
|
||||||
if len(node.inputs) >= 21:
|
if 'Alpha' in node.inputs:
|
||||||
state.out_opacity = c.parse_value_input(node.inputs[21])
|
state.out_opacity = c.parse_value_input(node.inputs['Alpha'])
|
||||||
if bpy.app.version > (4, 1, 0):
|
else:
|
||||||
|
state.out_opacity = '1.0'
|
||||||
|
if bpy.app.version >= (4, 0, 0):
|
||||||
def parse_bsdfprincipled(node: bpy.types.ShaderNodeBsdfPrincipled, out_socket: NodeSocket, state: ParserState) -> None:
|
def parse_bsdfprincipled(node: bpy.types.ShaderNodeBsdfPrincipled, out_socket: NodeSocket, state: ParserState) -> None:
|
||||||
if state.parse_surface:
|
if state.parse_surface:
|
||||||
c.write_normal(node.inputs[5])
|
c.write_normal(node.inputs['Normal'])
|
||||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
state.out_basecol = c.parse_vector_input(node.inputs['Base Color'])
|
||||||
|
|
||||||
sss_input = node.inputs.get('Subsurface Weight') or node.inputs.get('Subsurface')
|
sss_input = node.inputs.get(SUBSURFACE)
|
||||||
if sss_input is not None:
|
if sss_input is not None:
|
||||||
if sss_input.is_linked or sss_input.default_value > 0.0:
|
if sss_input.is_linked or sss_input.default_value > 0.0:
|
||||||
mat_state.needs_sss = True
|
mat_state.needs_sss = True
|
||||||
|
state.out_subsurface = c.parse_value_input(sss_input)
|
||||||
|
|
||||||
subsurface = c.parse_value_input(node.inputs[7])
|
sss_radius_input = node.inputs.get(SUBSURFACE_RADIUS)
|
||||||
subsurface_radius = c.parse_vector_input(node.inputs[9])
|
if sss_radius_input is not None:
|
||||||
subsurface_color = c.parse_vector_input(node.inputs[8])
|
state.out_subsurface_radius = c.parse_vector_input(sss_radius_input)
|
||||||
state.out_metallic = c.parse_value_input(node.inputs[1])
|
|
||||||
|
|
||||||
specular_socket = node.inputs.get('Specular IOR Level')
|
sss_color_input = node.inputs.get(SUBSURFACE_COLOR)
|
||||||
|
if sss_color_input is not None:
|
||||||
|
state.out_subsurface_color = c.parse_vector_input(sss_color_input)
|
||||||
|
else:
|
||||||
|
state.out_subsurface_color = c.parse_vector_input(node.inputs['Base Color'])
|
||||||
|
|
||||||
|
state.out_metallic = c.parse_value_input(node.inputs['Metallic'])
|
||||||
|
|
||||||
|
specular_socket = node.inputs.get(SPECULAR)
|
||||||
if specular_socket is not None:
|
if specular_socket is not None:
|
||||||
state.out_specular = f'({c.parse_value_input(specular_socket)} * 2.0)'
|
state.out_specular = f'({c.parse_value_input(specular_socket)} * 2.0)'
|
||||||
else:
|
else:
|
||||||
@ -173,88 +272,118 @@ if bpy.app.version > (4, 1, 0):
|
|||||||
else:
|
else:
|
||||||
state.out_specular = '1.0'
|
state.out_specular = '1.0'
|
||||||
|
|
||||||
state.out_roughness = c.parse_value_input(node.inputs[2])
|
spec_tint_socket = node.inputs.get(SPECULAR_TINT)
|
||||||
|
if spec_tint_socket is not None:
|
||||||
|
state.out_specular_tint = c.parse_vector_input(spec_tint_socket)
|
||||||
|
|
||||||
|
state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
|
||||||
# Prevent black material when metal = 1.0 and roughness = 0.0
|
# Prevent black material when metal = 1.0 and roughness = 0.0
|
||||||
try:
|
try:
|
||||||
if float(state.out_roughness) < 0.00101:
|
if float(state.out_roughness) < 0.00101:
|
||||||
state.out_roughness = '0.001'
|
state.out_roughness = '0.001'
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
aniso_socket = node.inputs.get(ANISOTROPIC)
|
||||||
|
if aniso_socket is not None:
|
||||||
|
state.out_anisotropy = c.parse_value_input(aniso_socket)
|
||||||
|
aniso_rot_socket = node.inputs.get(ANISOTROPIC_ROT)
|
||||||
|
if aniso_rot_socket is not None:
|
||||||
|
state.out_aniso_rot = c.parse_value_input(aniso_rot_socket)
|
||||||
|
|
||||||
|
sheen_socket = node.inputs.get(SHEEN)
|
||||||
|
if sheen_socket is not None:
|
||||||
|
state.out_sheen = c.parse_value_input(sheen_socket)
|
||||||
|
sheen_rough_socket = node.inputs.get(SHEEN_ROUGHNESS)
|
||||||
|
if sheen_rough_socket is not None:
|
||||||
|
state.out_sheen_rough = c.parse_value_input(sheen_rough_socket)
|
||||||
|
sheen_tint_socket = node.inputs.get(SHEEN_TINT)
|
||||||
|
if sheen_tint_socket is not None:
|
||||||
|
state.out_sheen_tint = c.parse_vector_input(sheen_tint_socket)
|
||||||
|
|
||||||
|
coat_socket = node.inputs.get(CLEARCOAT)
|
||||||
|
if coat_socket is not None:
|
||||||
|
state.out_clearcoat = c.parse_value_input(coat_socket)
|
||||||
|
coat_rough_socket = node.inputs.get(CLEARCOAT_ROUGHNESS)
|
||||||
|
if coat_rough_socket is not None:
|
||||||
|
state.out_clearcoat_rough = c.parse_value_input(coat_rough_socket)
|
||||||
|
|
||||||
|
trans_socket = node.inputs.get(TRANSMISSION)
|
||||||
|
if trans_socket is not None:
|
||||||
|
state.out_transmission = c.parse_value_input(trans_socket)
|
||||||
|
trans_rough_socket = node.inputs.get(TRANSMISSION_ROUGHNESS)
|
||||||
|
if trans_rough_socket is not None:
|
||||||
|
state.out_transmission_rough = c.parse_value_input(trans_rough_socket)
|
||||||
|
|
||||||
|
thin_wall_socket = node.inputs.get(THIN_WALL)
|
||||||
|
if thin_wall_socket is not None:
|
||||||
|
state.out_thin_wall = c.parse_value_input(thin_wall_socket)
|
||||||
|
|
||||||
|
tangent_socket = node.inputs.get(TANGENT)
|
||||||
|
if tangent_socket is not None:
|
||||||
|
state.out_tangent = c.parse_vector_input(tangent_socket)
|
||||||
|
|
||||||
if (node.inputs['Emission Strength'].is_linked or node.inputs['Emission Strength'].default_value != 0.0)\
|
if (node.inputs['Emission Strength'].is_linked or node.inputs['Emission Strength'].default_value != 0.0)\
|
||||||
and (node.inputs['Emission Color'].is_linked or not mat_utils.equals_color_socket(node.inputs['Emission Color'], (0.0, 0.0, 0.0), comp_alpha=False)):
|
and (node.inputs[EMISSION_COLOR].is_linked or not mat_utils.equals_color_socket(node.inputs[EMISSION_COLOR], (0.0, 0.0, 0.0), comp_alpha=False)):
|
||||||
if bpy.app.version >= (4, 4, 0):
|
emission_col = c.parse_vector_input(node.inputs[EMISSION_COLOR])
|
||||||
emission_col = c.parse_vector_input(node.inputs[27])
|
emission_strength = c.parse_value_input(node.inputs['Emission Strength'])
|
||||||
emission_strength = c.parse_value_input(node.inputs[28])
|
|
||||||
else:
|
|
||||||
emission_col = c.parse_vector_input(node.inputs[26])
|
|
||||||
emission_strength = c.parse_value_input(node.inputs[27])
|
|
||||||
state.out_emission_col = '({0} * {1})'.format(emission_col, emission_strength)
|
state.out_emission_col = '({0} * {1})'.format(emission_col, emission_strength)
|
||||||
mat_state.emission_type = mat_state.EmissionType.SHADED
|
mat_state.emission_type = mat_state.EmissionType.SHADED
|
||||||
else:
|
else:
|
||||||
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
||||||
#state.out_occlusion = state.out_roughness
|
|
||||||
#state.out_aniso = c.parse_vector_input(node.inputs[14])
|
|
||||||
#state.out_aniso_rot = c.parse_vector_input(node.inputs[15])
|
|
||||||
#state.out_sheen = c.parse_vector_input(node.inputs[23])
|
|
||||||
#state.out_sheen_tint = c.parse_vector_input(node.inputs[25])
|
|
||||||
#state.out_clearcoat = c.parse_vector_input(node.inputs[18])
|
|
||||||
#state.out_clearcoat_rough = c.parse_vector_input(node.inputs[19])
|
|
||||||
#state.out_ior = c.parse_value_input(node.inputs[3])
|
|
||||||
#state.out_transmission = c.parse_vector_input(node.inputs[17])
|
|
||||||
#state.out_transmission_roughness = state.out_roughness
|
|
||||||
if state.parse_opacity:
|
if state.parse_opacity:
|
||||||
state.out_ior = c.parse_value_input(node.inputs[3])
|
state.out_ior = c.parse_value_input(node.inputs['IOR'])
|
||||||
state.out_opacity = c.parse_value_input(node.inputs[4])
|
state.out_opacity = c.parse_value_input(node.inputs['Alpha'])
|
||||||
|
|
||||||
def parse_bsdfdiffuse(node: bpy.types.ShaderNodeBsdfDiffuse, out_socket: NodeSocket, state: ParserState) -> None:
|
def parse_bsdfdiffuse(node: bpy.types.ShaderNodeBsdfDiffuse, out_socket: NodeSocket, state: ParserState) -> None:
|
||||||
if state.parse_surface:
|
if state.parse_surface:
|
||||||
c.write_normal(node.inputs[2])
|
c.write_normal(node.inputs['Normal'])
|
||||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
|
||||||
state.out_roughness = c.parse_value_input(node.inputs[1])
|
state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
|
||||||
state.out_specular = '0.0'
|
state.out_specular = '0.0'
|
||||||
|
|
||||||
if bpy.app.version >= (4, 0, 0):
|
if bpy.app.version >= (4, 0, 0):
|
||||||
def parse_bsdfsheen(node: bpy.types.ShaderNodeBsdfSheen, out_socket: NodeSocket, state: ParserState) -> None:
|
def parse_bsdfsheen(node: bpy.types.ShaderNodeBsdfSheen, out_socket: NodeSocket, state: ParserState) -> None:
|
||||||
if state.parse_surface:
|
if state.parse_surface:
|
||||||
c.write_normal(node.inputs[2])
|
c.write_normal(node.inputs['Normal'])
|
||||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
|
||||||
state.out_roughness = c.parse_value_input(node.inputs[1])
|
state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
|
||||||
|
|
||||||
if bpy.app.version < (4, 1, 0):
|
if bpy.app.version < (4, 0, 0):
|
||||||
def parse_bsdfglossy(node: bpy.types.ShaderNodeBsdfGlossy, out_socket: NodeSocket, state: ParserState) -> None:
|
def parse_bsdfglossy(node: bpy.types.ShaderNodeBsdfGlossy, out_socket: NodeSocket, state: ParserState) -> None:
|
||||||
if state.parse_surface:
|
if state.parse_surface:
|
||||||
c.write_normal(node.inputs[2])
|
c.write_normal(node.inputs['Normal'])
|
||||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
|
||||||
state.out_roughness = c.parse_value_input(node.inputs[1])
|
state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
|
||||||
state.out_metallic = '1.0'
|
state.out_metallic = '1.0'
|
||||||
else:
|
else:
|
||||||
def parse_bsdfglossy(node: bpy.types.ShaderNodeBsdfAnisotropic, out_socket: NodeSocket, state: ParserState) -> None:
|
def parse_bsdfglossy(node: bpy.types.ShaderNodeBsdfAnisotropic, out_socket: NodeSocket, state: ParserState) -> None:
|
||||||
if state.parse_surface:
|
if state.parse_surface:
|
||||||
c.write_normal(node.inputs[4])
|
c.write_normal(node.inputs['Normal'])
|
||||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
|
||||||
state.out_roughness = c.parse_value_input(node.inputs[1])
|
state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
|
||||||
state.out_metallic = '1.0'
|
state.out_metallic = '1.0'
|
||||||
|
|
||||||
|
|
||||||
def parse_ambientocclusion(node: bpy.types.ShaderNodeAmbientOcclusion, out_socket: NodeSocket, state: ParserState) -> None:
|
def parse_ambientocclusion(node: bpy.types.ShaderNodeAmbientOcclusion, out_socket: NodeSocket, state: ParserState) -> None:
|
||||||
if state.parse_surface:
|
if state.parse_surface:
|
||||||
# Single channel
|
# Single channel
|
||||||
state.out_occlusion = c.parse_vector_input(node.inputs[0]) + '.r'
|
state.out_occlusion = c.parse_vector_input(node.inputs['Distance']) + '.r'
|
||||||
|
|
||||||
|
|
||||||
def parse_bsdfanisotropic(node: bpy.types.ShaderNodeBsdfAnisotropic, out_socket: NodeSocket, state: ParserState) -> None:
|
def parse_bsdfanisotropic(node: bpy.types.ShaderNodeBsdfAnisotropic, out_socket: NodeSocket, state: ParserState) -> None:
|
||||||
if state.parse_surface:
|
if state.parse_surface:
|
||||||
c.write_normal(node.inputs[4])
|
c.write_normal(node.inputs['Normal'])
|
||||||
# Revert to glossy
|
# Revert to glossy
|
||||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
|
||||||
state.out_roughness = c.parse_value_input(node.inputs[1])
|
state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
|
||||||
state.out_metallic = '1.0'
|
state.out_metallic = '1.0'
|
||||||
|
|
||||||
|
|
||||||
def parse_emission(node: bpy.types.ShaderNodeEmission, out_socket: NodeSocket, state: ParserState) -> None:
|
def parse_emission(node: bpy.types.ShaderNodeEmission, out_socket: NodeSocket, state: ParserState) -> None:
|
||||||
if state.parse_surface:
|
if state.parse_surface:
|
||||||
emission_col = c.parse_vector_input(node.inputs[0])
|
emission_col = c.parse_vector_input(node.inputs['Color'])
|
||||||
emission_strength = c.parse_value_input(node.inputs[1])
|
emission_strength = c.parse_value_input(node.inputs['Strength'])
|
||||||
state.out_emission_col = '({0} * {1})'.format(emission_col, emission_strength)
|
state.out_emission_col = '({0} * {1})'.format(emission_col, emission_strength)
|
||||||
state.out_basecol = 'vec3(0.0)'
|
state.out_basecol = 'vec3(0.0)'
|
||||||
state.out_specular = '0.0'
|
state.out_specular = '0.0'
|
||||||
@ -264,16 +393,77 @@ def parse_emission(node: bpy.types.ShaderNodeEmission, out_socket: NodeSocket, s
|
|||||||
|
|
||||||
def parse_bsdfglass(node: bpy.types.ShaderNodeBsdfGlass, out_socket: NodeSocket, state: ParserState) -> None:
|
def parse_bsdfglass(node: bpy.types.ShaderNodeBsdfGlass, out_socket: NodeSocket, state: ParserState) -> None:
|
||||||
if state.parse_surface:
|
if state.parse_surface:
|
||||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
|
||||||
c.write_normal(node.inputs[3])
|
c.write_normal(node.inputs['Normal'])
|
||||||
state.out_roughness = c.parse_value_input(node.inputs[1])
|
state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
|
||||||
if state.parse_opacity:
|
if state.parse_opacity:
|
||||||
state.out_opacity = '1.0'
|
state.out_opacity = '1.0'
|
||||||
state.out_ior = c.parse_value_input(node.inputs[2])
|
state.out_ior = c.parse_value_input(node.inputs['IOR'])
|
||||||
|
|
||||||
|
|
||||||
def parse_bsdfhair(node: bpy.types.ShaderNodeBsdfHair, out_socket: NodeSocket, state: ParserState) -> None:
|
def parse_bsdfhair(node: bpy.types.ShaderNodeBsdfHair, out_socket: NodeSocket, state: ParserState) -> None:
|
||||||
pass
|
if state.parse_surface:
|
||||||
|
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
|
||||||
|
state.out_roughness = c.parse_value_input(node.inputs['RoughnessU'])
|
||||||
|
state.out_specular = '0.0'
|
||||||
|
rough_v_socket = node.inputs.get('RoughnessV')
|
||||||
|
if rough_v_socket is not None:
|
||||||
|
state.out_aniso_rot = c.parse_value_input(rough_v_socket)
|
||||||
|
tangent_socket = node.inputs.get('Tangent')
|
||||||
|
if tangent_socket is not None:
|
||||||
|
state.out_tangent = c.parse_vector_input(tangent_socket)
|
||||||
|
|
||||||
|
|
||||||
|
if bpy.app.version >= (2, 83, 0):
|
||||||
|
def parse_bsdfhairprincipled(node: 'bpy.types.ShaderNodeBsdfHairPrincipled', out_socket: NodeSocket, state: ParserState) -> None:
|
||||||
|
if state.parse_surface:
|
||||||
|
c.write_normal(node.inputs['Normal'])
|
||||||
|
parametrization = getattr(node, 'parametrization', 'COLOR')
|
||||||
|
if parametrization == 'COLOR':
|
||||||
|
color_socket = node.inputs.get('Color')
|
||||||
|
if color_socket is not None:
|
||||||
|
state.out_basecol = c.parse_vector_input(color_socket)
|
||||||
|
elif parametrization == 'MELANIN':
|
||||||
|
melanin_socket = node.inputs.get('Melanin')
|
||||||
|
if melanin_socket is not None:
|
||||||
|
state.out_subsurface = c.parse_value_input(melanin_socket)
|
||||||
|
melanin_red_socket = node.inputs.get('Melanin Redness')
|
||||||
|
if melanin_red_socket is not None:
|
||||||
|
state.out_specular_tint = 'vec3({})'.format(c.parse_value_input(melanin_red_socket))
|
||||||
|
elif parametrization == 'ABSORPTION':
|
||||||
|
absorption_socket = node.inputs.get('Absorption Coefficient')
|
||||||
|
if absorption_socket is not None:
|
||||||
|
state.out_specular_tint = c.parse_vector_input(absorption_socket)
|
||||||
|
tint_socket = node.inputs.get('Tint')
|
||||||
|
if tint_socket is not None:
|
||||||
|
state.out_subsurface_color = c.parse_vector_input(tint_socket)
|
||||||
|
rough_socket = node.inputs.get('Roughness')
|
||||||
|
if rough_socket is not None:
|
||||||
|
state.out_roughness = c.parse_value_input(rough_socket)
|
||||||
|
model = getattr(node, 'model', 'CHIANG')
|
||||||
|
if model == 'CHIANG':
|
||||||
|
radial_rough_socket = node.inputs.get('Radial Roughness')
|
||||||
|
if radial_rough_socket is not None:
|
||||||
|
state.out_aniso_rot = c.parse_value_input(radial_rough_socket)
|
||||||
|
coat_socket = node.inputs.get('Coat')
|
||||||
|
if coat_socket is not None:
|
||||||
|
state.out_clearcoat = c.parse_value_input(coat_socket)
|
||||||
|
random_color_socket = node.inputs.get('Random Color')
|
||||||
|
if random_color_socket is not None:
|
||||||
|
state.out_sheen = c.parse_value_input(random_color_socket)
|
||||||
|
random_rough_socket = node.inputs.get('Random Roughness')
|
||||||
|
if random_rough_socket is not None:
|
||||||
|
state.out_sheen_rough = c.parse_value_input(random_rough_socket)
|
||||||
|
offset_socket = node.inputs.get('Offset')
|
||||||
|
if offset_socket is not None:
|
||||||
|
state.out_anisotropy = c.parse_value_input(offset_socket)
|
||||||
|
state.out_specular = '0.0'
|
||||||
|
state.out_metallic = '0.0'
|
||||||
|
if state.parse_opacity:
|
||||||
|
ior_socket = node.inputs.get('IOR')
|
||||||
|
if ior_socket is not None:
|
||||||
|
state.out_ior = c.parse_value_input(ior_socket)
|
||||||
|
state.out_opacity = '1.0'
|
||||||
|
|
||||||
|
|
||||||
def parse_holdout(node: bpy.types.ShaderNodeHoldout, out_socket: NodeSocket, state: ParserState) -> None:
|
def parse_holdout(node: bpy.types.ShaderNodeHoldout, out_socket: NodeSocket, state: ParserState) -> None:
|
||||||
@ -284,46 +474,50 @@ def parse_holdout(node: bpy.types.ShaderNodeHoldout, out_socket: NodeSocket, sta
|
|||||||
|
|
||||||
def parse_bsdfrefraction(node: bpy.types.ShaderNodeBsdfRefraction, out_socket: NodeSocket, state: ParserState) -> None:
|
def parse_bsdfrefraction(node: bpy.types.ShaderNodeBsdfRefraction, out_socket: NodeSocket, state: ParserState) -> None:
|
||||||
if state.parse_surface:
|
if state.parse_surface:
|
||||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
|
||||||
c.write_normal(node.inputs[3])
|
c.write_normal(node.inputs['Normal'])
|
||||||
state.out_roughness = c.parse_value_input(node.inputs[1])
|
state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
|
||||||
if state.parse_opacity:
|
if state.parse_opacity:
|
||||||
state.out_opacity = '1.0'
|
state.out_opacity = '1.0'
|
||||||
state.out_ior = c.parse_value_input(node.inputs[2])
|
state.out_ior = c.parse_value_input(node.inputs['IOR'])
|
||||||
|
|
||||||
def parse_subsurfacescattering(node: bpy.types.ShaderNodeSubsurfaceScattering, out_socket: NodeSocket, state: ParserState) -> None:
|
def parse_subsurfacescattering(node: bpy.types.ShaderNodeSubsurfaceScattering, out_socket: NodeSocket, state: ParserState) -> None:
|
||||||
if state.parse_surface:
|
if state.parse_surface:
|
||||||
# Mark that this material needs SSS
|
# Mark that this material needs SSS
|
||||||
mat_state.needs_sss = True
|
mat_state.needs_sss = True
|
||||||
if bpy.app.version < (4, 1, 0):
|
c.write_normal(node.inputs['Normal'])
|
||||||
c.write_normal(node.inputs[4])
|
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
|
||||||
else:
|
state.out_subsurface = c.parse_value_input(node.inputs['Scale'])
|
||||||
c.write_normal(node.inputs[6])
|
state.out_subsurface_radius = c.parse_vector_input(node.inputs['Radius'])
|
||||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
state.out_subsurface_color = c.parse_vector_input(node.inputs['Color'])
|
||||||
|
state.out_specular = '0.0'
|
||||||
|
|
||||||
|
|
||||||
def parse_bsdftoon(node: bpy.types.ShaderNodeBsdfToon, out_socket: NodeSocket, state: ParserState) -> None:
|
def parse_bsdftoon(node: bpy.types.ShaderNodeBsdfToon, out_socket: NodeSocket, state: ParserState) -> None:
|
||||||
# c.write_normal(node.inputs[3])
|
if state.parse_surface:
|
||||||
pass
|
c.write_normal(node.inputs['Normal'])
|
||||||
|
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
|
||||||
|
state.out_roughness = c.parse_value_input(node.inputs['Size'])
|
||||||
|
state.out_specular = '0.0'
|
||||||
|
|
||||||
|
|
||||||
def parse_bsdftranslucent(node: bpy.types.ShaderNodeBsdfTranslucent, out_socket: NodeSocket, state: ParserState) -> None:
|
def parse_bsdftranslucent(node: bpy.types.ShaderNodeBsdfTranslucent, out_socket: NodeSocket, state: ParserState) -> None:
|
||||||
if state.parse_surface:
|
if state.parse_surface:
|
||||||
c.write_normal(node.inputs[1])
|
c.write_normal(node.inputs['Normal'])
|
||||||
if state.parse_opacity:
|
if state.parse_opacity:
|
||||||
state.out_opacity = '(1.0 - {0}.r)'.format(c.parse_vector_input(node.inputs[0]))
|
state.out_opacity = '(1.0 - {0}.r)'.format(c.parse_vector_input(node.inputs['Color']))
|
||||||
state.out_ior = '1.0'
|
state.out_ior = '1.0'
|
||||||
|
|
||||||
|
|
||||||
def parse_bsdftransparent(node: bpy.types.ShaderNodeBsdfTransparent, out_socket: NodeSocket, state: ParserState) -> None:
|
def parse_bsdftransparent(node: bpy.types.ShaderNodeBsdfTransparent, out_socket: NodeSocket, state: ParserState) -> None:
|
||||||
if state.parse_opacity:
|
if state.parse_opacity:
|
||||||
state.out_opacity = '(1.0 - {0}.r)'.format(c.parse_vector_input(node.inputs[0]))
|
state.out_opacity = '(1.0 - {0}.r)'.format(c.parse_vector_input(node.inputs['Color']))
|
||||||
state.out_ior = '1.0'
|
state.out_ior = '1.0'
|
||||||
|
|
||||||
if bpy.app.version < (4, 1, 0):
|
if bpy.app.version < (4, 0, 0):
|
||||||
def parse_bsdfvelvet(node: bpy.types.ShaderNodeBsdfVelvet, out_socket: NodeSocket, state: ParserState) -> None:
|
def parse_bsdfvelvet(node: bpy.types.ShaderNodeBsdfVelvet, out_socket: NodeSocket, state: ParserState) -> None:
|
||||||
if state.parse_surface:
|
if state.parse_surface:
|
||||||
c.write_normal(node.inputs[2])
|
c.write_normal(node.inputs['Normal'])
|
||||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
|
||||||
state.out_roughness = '1.0'
|
state.out_roughness = '1.0'
|
||||||
state.out_metallic = '1.0'
|
state.out_metallic = '1.0'
|
||||||
|
|||||||
@ -856,6 +856,22 @@ def _write_material_attribs_default(frag: shader.Shader, parse_opacity: bool):
|
|||||||
# We may not use emission, but the attribute will then be removed
|
# We may not use emission, but the attribute will then be removed
|
||||||
# by the shader compiler
|
# by the shader compiler
|
||||||
frag.write('vec3 emissionCol;')
|
frag.write('vec3 emissionCol;')
|
||||||
|
# Extended BRDF parameters
|
||||||
|
frag.write('float subsurface = 0.0;')
|
||||||
|
frag.write('vec3 subsurfaceRadius = vec3(0.0);')
|
||||||
|
frag.write('vec3 subsurfaceColor = vec3(0.8);')
|
||||||
|
frag.write('vec3 specularTint = vec3(1.0);')
|
||||||
|
frag.write('float anisotropy = 0.0;')
|
||||||
|
frag.write('float anisoRot = 0.0;')
|
||||||
|
frag.write('float sheen = 0.0;')
|
||||||
|
frag.write('float sheenRough = 0.5;')
|
||||||
|
frag.write('vec3 sheenTint = vec3(1.0);')
|
||||||
|
frag.write('float clearcoat = 0.0;')
|
||||||
|
frag.write('float clearcoatRough = 0.03;')
|
||||||
|
frag.write('float transmission = 0.0;')
|
||||||
|
frag.write('float transmissionRough = 0.0;')
|
||||||
|
frag.write('float thinWall = 0.0;')
|
||||||
|
frag.write('vec3 tangent = vec3(0.0);')
|
||||||
if parse_opacity:
|
if parse_opacity:
|
||||||
frag.write('float opacity;')
|
frag.write('float opacity;')
|
||||||
frag.write('float ior = 1.45;')
|
frag.write('float ior = 1.45;')
|
||||||
|
|||||||
@ -165,6 +165,10 @@ ALL_NODES: Dict[str, MaterialNodeMeta] = {
|
|||||||
'TRANSPARENT_BSDF': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdftransparent),
|
'TRANSPARENT_BSDF': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdftransparent),
|
||||||
'BSDF_REFRACTION': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfrefraction),
|
'BSDF_REFRACTION': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfrefraction),
|
||||||
'REFRACTION_BSDF': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfrefraction),
|
'REFRACTION_BSDF': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfrefraction),
|
||||||
|
'BSDF_TOON': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdftoon),
|
||||||
|
'TOON_BSDF': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdftoon),
|
||||||
|
'BSDF_HAIR': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfhair),
|
||||||
|
'HAIR_BSDF': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfhair),
|
||||||
'EMISSION': MaterialNodeMeta(parse_func=nodes_shader.parse_emission),
|
'EMISSION': MaterialNodeMeta(parse_func=nodes_shader.parse_emission),
|
||||||
'HOLDOUT': MaterialNodeMeta(
|
'HOLDOUT': MaterialNodeMeta(
|
||||||
parse_func=nodes_shader.parse_holdout,
|
parse_func=nodes_shader.parse_holdout,
|
||||||
@ -205,11 +209,14 @@ ALL_NODES: Dict[str, MaterialNodeMeta] = {
|
|||||||
if bpy.app.version > (3, 2, 0):
|
if bpy.app.version > (3, 2, 0):
|
||||||
ALL_NODES['SEPARATE_COLOR'] = MaterialNodeMeta(parse_func=nodes_converter.parse_separate_color)
|
ALL_NODES['SEPARATE_COLOR'] = MaterialNodeMeta(parse_func=nodes_converter.parse_separate_color)
|
||||||
ALL_NODES['COMBINE_COLOR'] = MaterialNodeMeta(parse_func=nodes_converter.parse_combine_color)
|
ALL_NODES['COMBINE_COLOR'] = MaterialNodeMeta(parse_func=nodes_converter.parse_combine_color)
|
||||||
if bpy.app.version < (4, 1, 0):
|
if bpy.app.version < (4, 0, 0):
|
||||||
ALL_NODES['BSDF_VELVET'] = MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfvelvet)
|
ALL_NODES['BSDF_VELVET'] = MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfvelvet)
|
||||||
|
if bpy.app.version < (4, 1, 0):
|
||||||
ALL_NODES['TEX_MUSGRAVE'] = MaterialNodeMeta(parse_func=nodes_texture.parse_tex_musgrave)
|
ALL_NODES['TEX_MUSGRAVE'] = MaterialNodeMeta(parse_func=nodes_texture.parse_tex_musgrave)
|
||||||
if bpy.app.version >= (4, 0, 0):
|
if bpy.app.version >= (4, 0, 0):
|
||||||
ALL_NODES['BSDF_SHEEN'] = MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfsheen)
|
ALL_NODES['BSDF_SHEEN'] = MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfsheen)
|
||||||
|
if bpy.app.version >= (2, 83, 0):
|
||||||
|
ALL_NODES['BSDF_HAIR_PRINCIPLED'] = MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfhairprincipled)
|
||||||
if bpy.app.version < (5, 0, 0):
|
if bpy.app.version < (5, 0, 0):
|
||||||
ALL_NODES['TEX_POINTDENSITY'] = MaterialNodeMeta(parse_func=nodes_texture.parse_tex_pointdensity, compute_dxdy_variants=ComputeDXDYVariant.NEVER)
|
ALL_NODES['TEX_POINTDENSITY'] = MaterialNodeMeta(parse_func=nodes_texture.parse_tex_pointdensity, compute_dxdy_variants=ComputeDXDYVariant.NEVER)
|
||||||
|
|
||||||
|
|||||||
@ -99,6 +99,21 @@ class ParserState:
|
|||||||
self.out_opacity: floatstr = '1.0'
|
self.out_opacity: floatstr = '1.0'
|
||||||
self.out_ior: floatstr = '1.450'
|
self.out_ior: floatstr = '1.450'
|
||||||
self.out_emission_col: vec3str = 'vec3(0.0)'
|
self.out_emission_col: vec3str = 'vec3(0.0)'
|
||||||
|
self.out_subsurface: floatstr = '0.0'
|
||||||
|
self.out_subsurface_radius: vec3str = 'vec3(0.0)'
|
||||||
|
self.out_subsurface_color: vec3str = 'vec3(0.8)'
|
||||||
|
self.out_specular_tint: vec3str = 'vec3(1.0)'
|
||||||
|
self.out_anisotropy: floatstr = '0.0'
|
||||||
|
self.out_aniso_rot: floatstr = '0.0'
|
||||||
|
self.out_sheen: floatstr = '0.0'
|
||||||
|
self.out_sheen_rough: floatstr = '0.5'
|
||||||
|
self.out_sheen_tint: vec3str = 'vec3(1.0)'
|
||||||
|
self.out_clearcoat: floatstr = '0.0'
|
||||||
|
self.out_clearcoat_rough: floatstr = '0.03'
|
||||||
|
self.out_transmission: floatstr = '0.0'
|
||||||
|
self.out_transmission_rough: floatstr = '0.0'
|
||||||
|
self.out_thin_wall: floatstr = '0.0'
|
||||||
|
self.out_tangent: vec3str = 'vec3(0.0)'
|
||||||
|
|
||||||
def reset_outs(self):
|
def reset_outs(self):
|
||||||
"""Reset the shader output values to their default values."""
|
"""Reset the shader output values to their default values."""
|
||||||
@ -110,11 +125,34 @@ class ParserState:
|
|||||||
self.out_opacity = '1.0'
|
self.out_opacity = '1.0'
|
||||||
self.out_ior = '1.450'
|
self.out_ior = '1.450'
|
||||||
self.out_emission_col = 'vec3(0.0)'
|
self.out_emission_col = 'vec3(0.0)'
|
||||||
|
self.out_subsurface = '0.0'
|
||||||
|
self.out_subsurface_radius = 'vec3(0.0)'
|
||||||
|
self.out_subsurface_color = 'vec3(0.8)'
|
||||||
|
self.out_specular_tint = 'vec3(1.0)'
|
||||||
|
self.out_anisotropy = '0.0'
|
||||||
|
self.out_aniso_rot = '0.0'
|
||||||
|
self.out_sheen = '0.0'
|
||||||
|
self.out_sheen_rough = '0.5'
|
||||||
|
self.out_sheen_tint = 'vec3(1.0)'
|
||||||
|
self.out_clearcoat = '0.0'
|
||||||
|
self.out_clearcoat_rough = '0.03'
|
||||||
|
self.out_transmission = '0.0'
|
||||||
|
self.out_transmission_rough = '0.0'
|
||||||
|
self.out_thin_wall = '0.0'
|
||||||
|
self.out_tangent = 'vec3(0.0)'
|
||||||
|
|
||||||
def get_outs(self) -> Tuple[vec3str, floatstr, floatstr, floatstr, floatstr, floatstr, floatstr, vec3str]:
|
def get_outs(self) -> Tuple:
|
||||||
"""Return the shader output values as a tuple."""
|
"""Return the shader output values as a tuple."""
|
||||||
return (self.out_basecol, self.out_roughness, self.out_metallic, self.out_occlusion, self.out_specular,
|
return (self.out_basecol, self.out_roughness, self.out_metallic,
|
||||||
self.out_opacity, self.out_ior, self.out_emission_col)
|
self.out_occlusion, self.out_specular, self.out_opacity,
|
||||||
|
self.out_ior, self.out_emission_col,
|
||||||
|
self.out_subsurface, self.out_subsurface_radius,
|
||||||
|
self.out_subsurface_color, self.out_specular_tint,
|
||||||
|
self.out_anisotropy, self.out_aniso_rot,
|
||||||
|
self.out_sheen, self.out_sheen_rough, self.out_sheen_tint,
|
||||||
|
self.out_clearcoat, self.out_clearcoat_rough,
|
||||||
|
self.out_transmission, self.out_transmission_rough,
|
||||||
|
self.out_thin_wall, self.out_tangent)
|
||||||
|
|
||||||
|
|
||||||
def get_parser_pass_suffix(self) -> str:
|
def get_parser_pass_suffix(self) -> str:
|
||||||
|
|||||||
@ -11,12 +11,16 @@ import sys
|
|||||||
import os
|
import os
|
||||||
import array
|
import array
|
||||||
import atexit
|
import atexit
|
||||||
|
import time
|
||||||
from mathutils import Quaternion, Matrix, Vector
|
from mathutils import Quaternion, Matrix, Vector
|
||||||
import lnx.make_state as state
|
import lnx.make_state as state
|
||||||
|
|
||||||
HAS_GPU_STATE = bpy.app.version >= (3, 0, 0)
|
HAS_GPU_STATE = bpy.app.version >= (3, 0, 0)
|
||||||
if not HAS_GPU_STATE:
|
try:
|
||||||
import bgl
|
import bgl
|
||||||
|
HAS_BGL = callable(getattr(bgl, 'glGenTextures', None))
|
||||||
|
except ImportError:
|
||||||
|
HAS_BGL = False
|
||||||
SHADER_UNIFORM_COLOR = 'UNIFORM_COLOR' if bpy.app.version >= (3, 4, 0) else '2D_UNIFORM_COLOR'
|
SHADER_UNIFORM_COLOR = 'UNIFORM_COLOR' if bpy.app.version >= (3, 4, 0) else '2D_UNIFORM_COLOR'
|
||||||
SHADER_IMAGE = 'IMAGE' if bpy.app.version >= (3, 4, 0) else '2D_IMAGE'
|
SHADER_IMAGE = 'IMAGE' if bpy.app.version >= (3, 4, 0) else '2D_IMAGE'
|
||||||
HAS_GPU_TEXTURE = bpy.app.version >= (3, 0, 0)
|
HAS_GPU_TEXTURE = bpy.app.version >= (3, 0, 0)
|
||||||
@ -124,6 +128,8 @@ class KromViewportEngine(bpy.types.RenderEngine):
|
|||||||
self._last_frame_id = 0
|
self._last_frame_id = 0
|
||||||
self._width = 0
|
self._width = 0
|
||||||
self._height = 0
|
self._height = 0
|
||||||
|
self._tex_width = 0
|
||||||
|
self._tex_height = 0
|
||||||
self._initialized = False
|
self._initialized = False
|
||||||
self._shader = None
|
self._shader = None
|
||||||
self._batch = None
|
self._batch = None
|
||||||
@ -140,6 +146,8 @@ class KromViewportEngine(bpy.types.RenderEngine):
|
|||||||
self._last_region_dims = None
|
self._last_region_dims = None
|
||||||
self._header_buffer = (ctypes.c_ubyte * VIEWPORT_HEADER_SIZE)()
|
self._header_buffer = (ctypes.c_ubyte * VIEWPORT_HEADER_SIZE)()
|
||||||
self._engine_id = id(self)
|
self._engine_id = id(self)
|
||||||
|
self._viewport_image = None
|
||||||
|
self._float_buffer = None
|
||||||
# print(f"New engine instance created: {self._engine_id}")
|
# print(f"New engine instance created: {self._engine_id}")
|
||||||
|
|
||||||
def _restore_overlay(self, context=None):
|
def _restore_overlay(self, context=None):
|
||||||
@ -247,7 +255,7 @@ class KromViewportEngine(bpy.types.RenderEngine):
|
|||||||
def deferred_launch():
|
def deferred_launch():
|
||||||
try:
|
try:
|
||||||
import lnx.make as make
|
import lnx.make as make
|
||||||
make.play_viewport(viewport_id, width, height)
|
make.play(viewport_id, width, height)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Failed to launch viewport: {e}")
|
print(f"Failed to launch viewport: {e}")
|
||||||
import traceback
|
import traceback
|
||||||
@ -498,7 +506,7 @@ class KromViewportEngine(bpy.types.RenderEngine):
|
|||||||
"""Handle invalid/stale shared memory by cleaning up and allowing re-init."""
|
"""Handle invalid/stale shared memory by cleaning up and allowing re-init."""
|
||||||
self._cleanup_shared_memory()
|
self._cleanup_shared_memory()
|
||||||
self._initialized = False
|
self._initialized = False
|
||||||
if not HAS_GPU_TEXTURE and hasattr(self, '_gl_texture_buf') and self._gl_texture_buf[0] != 0:
|
if HAS_BGL and hasattr(self, '_gl_texture_buf') and self._gl_texture_buf[0] != 0:
|
||||||
try:
|
try:
|
||||||
bgl.glDeleteTextures(1, self._gl_texture_buf)
|
bgl.glDeleteTextures(1, self._gl_texture_buf)
|
||||||
except:
|
except:
|
||||||
@ -783,21 +791,8 @@ class KromViewportEngine(bpy.types.RenderEngine):
|
|||||||
if len(pixels) < num_pixels:
|
if len(pixels) < num_pixels:
|
||||||
return
|
return
|
||||||
|
|
||||||
if HAS_GPU_TEXTURE:
|
if HAS_BGL:
|
||||||
if HAS_NUMPY:
|
# Fast path: bgl with GL_UNSIGNED_BYTE, no float conversion, in-place update
|
||||||
pixels[3::4] = 255
|
|
||||||
float_array = pixels.astype(np.float32) / 255.0
|
|
||||||
buffer = gpu.types.Buffer('FLOAT', num_pixels, float_array)
|
|
||||||
else:
|
|
||||||
pixel_array = array.array('B', pixels[:num_pixels])
|
|
||||||
for i in range(3, len(pixel_array), 4):
|
|
||||||
pixel_array[i] = 255
|
|
||||||
float_pixels = [p / 255.0 for p in pixel_array]
|
|
||||||
buffer = gpu.types.Buffer('FLOAT', num_pixels, float_pixels)
|
|
||||||
|
|
||||||
self._texture = gpu.types.GPUTexture((width, height), format='SRGB8_A8', data=buffer)
|
|
||||||
else:
|
|
||||||
# bgl path for Blender < 3.0
|
|
||||||
if not hasattr(self, '_gl_texture_buf') or self._gl_texture_buf[0] == 0:
|
if not hasattr(self, '_gl_texture_buf') or self._gl_texture_buf[0] == 0:
|
||||||
self._gl_texture_buf = bgl.Buffer(bgl.GL_INT, 1)
|
self._gl_texture_buf = bgl.Buffer(bgl.GL_INT, 1)
|
||||||
bgl.glGenTextures(1, self._gl_texture_buf)
|
bgl.glGenTextures(1, self._gl_texture_buf)
|
||||||
@ -817,11 +812,42 @@ class KromViewportEngine(bpy.types.RenderEngine):
|
|||||||
pixel_buffer = bgl.Buffer(bgl.GL_UNSIGNED_BYTE, num_pixels, bytes(pixel_array))
|
pixel_buffer = bgl.Buffer(bgl.GL_UNSIGNED_BYTE, num_pixels, bytes(pixel_array))
|
||||||
|
|
||||||
internal_format = getattr(bgl, 'GL_SRGB8_ALPHA8', bgl.GL_RGBA8)
|
internal_format = getattr(bgl, 'GL_SRGB8_ALPHA8', bgl.GL_RGBA8)
|
||||||
bgl.glTexImage2D(bgl.GL_TEXTURE_2D, 0, internal_format, width, height, 0,
|
if self._tex_width == width and self._tex_height == height:
|
||||||
bgl.GL_RGBA, bgl.GL_UNSIGNED_BYTE, pixel_buffer)
|
bgl.glTexSubImage2D(bgl.GL_TEXTURE_2D, 0, 0, 0, width, height,
|
||||||
|
bgl.GL_RGBA, bgl.GL_UNSIGNED_BYTE, pixel_buffer)
|
||||||
|
else:
|
||||||
|
bgl.glTexImage2D(bgl.GL_TEXTURE_2D, 0, internal_format, width, height, 0,
|
||||||
|
bgl.GL_RGBA, bgl.GL_UNSIGNED_BYTE, pixel_buffer)
|
||||||
bgl.glBindTexture(bgl.GL_TEXTURE_2D, 0)
|
bgl.glBindTexture(bgl.GL_TEXTURE_2D, 0)
|
||||||
|
|
||||||
self._texture = tex_id
|
self._texture = tex_id
|
||||||
|
elif HAS_GPU_TEXTURE:
|
||||||
|
# Blender 4.x+ path: use bpy.Image + gpu.texture.from_image
|
||||||
|
# from_image returns a cached GPUTexture (no 20ms constructor)
|
||||||
|
# pixels updated via foreach_set (fast C-level copy) + update()
|
||||||
|
if self._viewport_image is None or self._viewport_image.size[0] != width or self._viewport_image.size[1] != height:
|
||||||
|
if self._viewport_image is not None:
|
||||||
|
bpy.data.images.remove(self._viewport_image)
|
||||||
|
self._viewport_image = bpy.data.images.new(
|
||||||
|
"_lnx_viewport", width, height, float_buffer=False)
|
||||||
|
self._float_buffer = None
|
||||||
|
|
||||||
|
if HAS_NUMPY:
|
||||||
|
pixels[3::4] = 255
|
||||||
|
if self._float_buffer is None or len(self._float_buffer) != num_pixels:
|
||||||
|
self._float_buffer = np.empty(num_pixels, dtype=np.float32)
|
||||||
|
self._float_buffer[:num_pixels] = pixels[:num_pixels]
|
||||||
|
self._float_buffer *= (1.0 / 255.0)
|
||||||
|
self._viewport_image.pixels.foreach_set(self._float_buffer)
|
||||||
|
else:
|
||||||
|
pixel_array = array.array('B', pixels[:num_pixels])
|
||||||
|
for i in range(3, len(pixel_array), 4):
|
||||||
|
pixel_array[i] = 255
|
||||||
|
float_pixels = [p / 255.0 for p in pixel_array]
|
||||||
|
self._viewport_image.pixels.foreach_set(float_pixels)
|
||||||
|
|
||||||
|
self._viewport_image.update()
|
||||||
|
self._texture = gpu.texture.from_image(self._viewport_image)
|
||||||
|
|
||||||
self._tex_width = width
|
self._tex_width = width
|
||||||
self._tex_height = height
|
self._tex_height = height
|
||||||
@ -838,11 +864,17 @@ class KromViewportEngine(bpy.types.RenderEngine):
|
|||||||
"""Called by Blender when the engine instance is destroyed."""
|
"""Called by Blender when the engine instance is destroyed."""
|
||||||
self._stop_krom_process()
|
self._stop_krom_process()
|
||||||
self._restore_overlay()
|
self._restore_overlay()
|
||||||
if not HAS_GPU_TEXTURE and hasattr(self, '_gl_texture_buf') and self._gl_texture_buf[0] != 0:
|
if HAS_BGL and hasattr(self, '_gl_texture_buf') and self._gl_texture_buf[0] != 0:
|
||||||
try:
|
try:
|
||||||
bgl.glDeleteTextures(1, self._gl_texture_buf)
|
bgl.glDeleteTextures(1, self._gl_texture_buf)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
if self._viewport_image is not None:
|
||||||
|
try:
|
||||||
|
bpy.data.images.remove(self._viewport_image)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
self._viewport_image = None
|
||||||
if self._viewport_id and self._viewport_id in _active_krom_engines:
|
if self._viewport_id and self._viewport_id in _active_krom_engines:
|
||||||
del _active_krom_engines[self._viewport_id]
|
del _active_krom_engines[self._viewport_id]
|
||||||
|
|
||||||
@ -928,6 +960,7 @@ class KromViewportEngine(bpy.types.RenderEngine):
|
|||||||
self._last_region_dims = dims
|
self._last_region_dims = dims
|
||||||
self._request_resize(region.width, region.height)
|
self._request_resize(region.width, region.height)
|
||||||
|
|
||||||
|
self._write_camera_matrices(context)
|
||||||
self._read_krom_camera(context)
|
self._read_krom_camera(context)
|
||||||
|
|
||||||
pixels = self._read_framebuffer()
|
pixels = self._read_framebuffer()
|
||||||
@ -966,7 +999,8 @@ class KromViewportEngine(bpy.types.RenderEngine):
|
|||||||
self._batch_dims = dims
|
self._batch_dims = dims
|
||||||
|
|
||||||
self._shader.bind()
|
self._shader.bind()
|
||||||
if HAS_GPU_TEXTURE:
|
_is_gl_texture = isinstance(self._texture, int)
|
||||||
|
if not _is_gl_texture:
|
||||||
self._shader.uniform_sampler("image", self._texture)
|
self._shader.uniform_sampler("image", self._texture)
|
||||||
else:
|
else:
|
||||||
bgl.glActiveTexture(bgl.GL_TEXTURE0)
|
bgl.glActiveTexture(bgl.GL_TEXTURE0)
|
||||||
@ -975,7 +1009,7 @@ class KromViewportEngine(bpy.types.RenderEngine):
|
|||||||
|
|
||||||
self._batch.draw(self._shader)
|
self._batch.draw(self._shader)
|
||||||
|
|
||||||
if not HAS_GPU_TEXTURE:
|
if _is_gl_texture:
|
||||||
bgl.glBindTexture(bgl.GL_TEXTURE_2D, 0)
|
bgl.glBindTexture(bgl.GL_TEXTURE_2D, 0)
|
||||||
|
|
||||||
if HAS_GPU_STATE:
|
if HAS_GPU_STATE:
|
||||||
|
|||||||
@ -875,7 +875,7 @@ def check_blender_version(op: bpy.types.Operator):
|
|||||||
"""Check whether the Blender version is supported by Leenkx,
|
"""Check whether the Blender version is supported by Leenkx,
|
||||||
if not, report in UI.
|
if not, report in UI.
|
||||||
"""
|
"""
|
||||||
if bpy.app.version[:2] not in [(4, 5), (4, 4), (4, 2), (3, 6), (3, 3)]:
|
if bpy.app.version[:2] not in [(5, 2), (4, 5), (4, 4), (4, 2), (3, 6), (3, 3)]:
|
||||||
op.report({'INFO'}, 'INFO: For Leenkx to work correctly, use a Blender LTS version')
|
op.report({'INFO'}, 'INFO: For Leenkx to work correctly, use a Blender LTS version')
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user