forked from LeenkxTeam/LNXSDK
Patch_2
This commit is contained in:
@ -595,6 +595,20 @@ class LeenkxExporter:
|
|||||||
return space.region_3d.window_matrix, space.region_3d.is_perspective
|
return space.region_3d.window_matrix, space.region_3d.is_perspective
|
||||||
return None, False
|
return None, False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_viewport_lens_fov() -> Optional[float]:
|
||||||
|
"""Get FOV from viewport lens setting."""
|
||||||
|
play_area = LeenkxExporter.get_view3d_area()
|
||||||
|
if play_area is None:
|
||||||
|
return None
|
||||||
|
for space in play_area.spaces:
|
||||||
|
if space.type == 'VIEW_3D':
|
||||||
|
lens = space.lens
|
||||||
|
sensor = 32.0
|
||||||
|
fov = 2.0 * math.atan(sensor / (2.0 * lens))
|
||||||
|
return fov
|
||||||
|
return None
|
||||||
|
|
||||||
def write_bone_matrices(self, scene, action):
|
def write_bone_matrices(self, scene, action):
|
||||||
# profile_time = time.time()
|
# profile_time = time.time()
|
||||||
begin_frame, end_frame = int(action.frame_range[0]), int(action.frame_range[1])
|
begin_frame, end_frame = int(action.frame_range[0]), int(action.frame_range[1])
|
||||||
@ -2785,7 +2799,7 @@ class LeenkxExporter:
|
|||||||
|
|
||||||
wrd = bpy.data.worlds['Lnx']
|
wrd = bpy.data.worlds['Lnx']
|
||||||
phys_enabled = wrd.lnx_physics != 'Disabled'
|
phys_enabled = wrd.lnx_physics != 'Disabled'
|
||||||
phys_pkg = 'bullet' if wrd.lnx_physics_engine == 'Bullet' else 'oimo'
|
phys_pkg = 'bullet' if wrd.lnx_physics_engine == 'Bullet' else ('jolt' if wrd.lnx_physics_engine == 'Jolt' else 'oimo')
|
||||||
|
|
||||||
# Rigid body trait
|
# Rigid body trait
|
||||||
if bobject.rigid_body is not None and phys_enabled:
|
if bobject.rigid_body is not None and phys_enabled:
|
||||||
@ -3099,7 +3113,7 @@ class LeenkxExporter:
|
|||||||
if wrd.lnx_physics != 'Disabled' and LeenkxExporter.export_physics:
|
if wrd.lnx_physics != 'Disabled' and LeenkxExporter.export_physics:
|
||||||
if 'traits' not in self.output:
|
if 'traits' not in self.output:
|
||||||
self.output['traits'] = []
|
self.output['traits'] = []
|
||||||
phys_pkg = 'bullet' if wrd.lnx_physics_engine == 'Bullet' else 'oimo'
|
phys_pkg = 'bullet' if wrd.lnx_physics_engine == 'Bullet' else ('jolt' if wrd.lnx_physics_engine == 'Jolt' else 'oimo')
|
||||||
|
|
||||||
out_trait = {
|
out_trait = {
|
||||||
'type': 'Script',
|
'type': 'Script',
|
||||||
@ -3204,7 +3218,7 @@ class LeenkxExporter:
|
|||||||
LeenkxExporter.export_physics = True
|
LeenkxExporter.export_physics = True
|
||||||
assets.add_khafile_def('lnx_physics_soft')
|
assets.add_khafile_def('lnx_physics_soft')
|
||||||
|
|
||||||
phys_pkg = 'bullet' if bpy.data.worlds['Lnx'].lnx_physics_engine == 'Bullet' else 'oimo'
|
phys_pkg = 'bullet' if bpy.data.worlds['Lnx'].lnx_physics_engine == 'Bullet' else ('jolt' if bpy.data.worlds['Lnx'].lnx_physics_engine == 'Jolt' else 'oimo')
|
||||||
out_trait = {'type': 'Script', 'class_name': 'leenkx.trait.physics.' + phys_pkg + '.SoftBody'}
|
out_trait = {'type': 'Script', 'class_name': 'leenkx.trait.physics.' + phys_pkg + '.SoftBody'}
|
||||||
# ClothModifier
|
# ClothModifier
|
||||||
if modifier.type == 'CLOTH':
|
if modifier.type == 'CLOTH':
|
||||||
@ -3228,7 +3242,7 @@ class LeenkxExporter:
|
|||||||
def add_hook_mod(o, bobject: bpy.types.Object, target_name, group_name):
|
def add_hook_mod(o, bobject: bpy.types.Object, target_name, group_name):
|
||||||
LeenkxExporter.export_physics = True
|
LeenkxExporter.export_physics = True
|
||||||
|
|
||||||
phys_pkg = 'bullet' if bpy.data.worlds['Lnx'].lnx_physics_engine == 'Bullet' else 'oimo'
|
phys_pkg = 'bullet' if bpy.data.worlds['Lnx'].lnx_physics_engine == 'Bullet' else ('jolt' if bpy.data.worlds['Lnx'].lnx_physics_engine == 'Jolt' else 'oimo')
|
||||||
out_trait = {'type': 'Script', 'class_name': 'leenkx.trait.physics.' + phys_pkg + '.PhysicsHook'}
|
out_trait = {'type': 'Script', 'class_name': 'leenkx.trait.physics.' + phys_pkg + '.PhysicsHook'}
|
||||||
|
|
||||||
verts = []
|
verts = []
|
||||||
@ -3254,7 +3268,7 @@ class LeenkxExporter:
|
|||||||
return
|
return
|
||||||
|
|
||||||
LeenkxExporter.export_physics = True
|
LeenkxExporter.export_physics = True
|
||||||
phys_pkg = 'bullet' if bpy.data.worlds['Lnx'].lnx_physics_engine == 'Bullet' else 'oimo'
|
phys_pkg = 'bullet' if bpy.data.worlds['Lnx'].lnx_physics_engine == 'Bullet' else ('jolt' if bpy.data.worlds['Lnx'].lnx_physics_engine == 'Jolt' else 'oimo')
|
||||||
breaking_threshold = rbc.breaking_threshold if rbc.use_breaking else 0
|
breaking_threshold = rbc.breaking_threshold if rbc.use_breaking else 0
|
||||||
|
|
||||||
trait = {
|
trait = {
|
||||||
|
|||||||
@ -37,6 +37,7 @@ else:
|
|||||||
_active_threads: Dict[threading.Thread, Callable] = {}
|
_active_threads: Dict[threading.Thread, Callable] = {}
|
||||||
_last_poll_time = 0.0
|
_last_poll_time = 0.0
|
||||||
_consecutive_empty_polls = 0
|
_consecutive_empty_polls = 0
|
||||||
|
_last_render_engine = None
|
||||||
|
|
||||||
@persistent
|
@persistent
|
||||||
def on_depsgraph_update_post(self):
|
def on_depsgraph_update_post(self):
|
||||||
@ -141,6 +142,43 @@ def always() -> float:
|
|||||||
return 0.5
|
return 0.5
|
||||||
|
|
||||||
|
|
||||||
|
def check_render_engine() -> float:
|
||||||
|
global _last_render_engine
|
||||||
|
|
||||||
|
try:
|
||||||
|
scene = None
|
||||||
|
if hasattr(bpy.context, 'scene') and bpy.context.scene is not None:
|
||||||
|
scene = bpy.context.scene
|
||||||
|
elif len(bpy.data.scenes) > 0:
|
||||||
|
scene = bpy.data.scenes[0]
|
||||||
|
|
||||||
|
if scene is None:
|
||||||
|
return 1.0
|
||||||
|
|
||||||
|
current_engine = scene.render.engine
|
||||||
|
|
||||||
|
if _last_render_engine != current_engine:
|
||||||
|
if current_engine == 'KROM_VIEWPORT':
|
||||||
|
try:
|
||||||
|
import lnx.make_world as make_world
|
||||||
|
make_world.build()
|
||||||
|
except Exception as e:
|
||||||
|
log.warn(f'World shader build failed: {e}')
|
||||||
|
|
||||||
|
elif _last_render_engine == 'KROM_VIEWPORT':
|
||||||
|
try:
|
||||||
|
make.stop_viewport()
|
||||||
|
except Exception as e:
|
||||||
|
log.warn(f'Failed to stop viewport: {e}')
|
||||||
|
|
||||||
|
_last_render_engine = current_engine
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Engine Error: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
return 0.5
|
||||||
|
|
||||||
|
|
||||||
def poll_threads() -> float:
|
def poll_threads() -> float:
|
||||||
"""
|
"""
|
||||||
@ -389,6 +427,7 @@ def register():
|
|||||||
|
|
||||||
bpy.app.timers.register(always, persistent=True)
|
bpy.app.timers.register(always, persistent=True)
|
||||||
bpy.app.timers.register(poll_threads, persistent=True)
|
bpy.app.timers.register(poll_threads, persistent=True)
|
||||||
|
bpy.app.timers.register(check_render_engine, persistent=True)
|
||||||
|
|
||||||
if lnx.utils.get_fp() != '':
|
if lnx.utils.get_fp() != '':
|
||||||
# TODO: On windows, on_load_post is not called when opening .blend file from explorer
|
# TODO: On windows, on_load_post is not called when opening .blend file from explorer
|
||||||
@ -405,6 +444,7 @@ def register():
|
|||||||
def unregister():
|
def unregister():
|
||||||
unload_py_libraries()
|
unload_py_libraries()
|
||||||
|
|
||||||
|
bpy.app.timers.unregister(check_render_engine)
|
||||||
bpy.app.timers.unregister(poll_threads)
|
bpy.app.timers.unregister(poll_threads)
|
||||||
bpy.app.timers.unregister(always)
|
bpy.app.timers.unregister(always)
|
||||||
|
|
||||||
|
|||||||
@ -37,6 +37,8 @@ def register():
|
|||||||
kmn.keymap_items.new('lnx.edit_group_tree', 'TAB', 'PRESS')
|
kmn.keymap_items.new('lnx.edit_group_tree', 'TAB', 'PRESS')
|
||||||
kmn.keymap_items.new('node.tree_path_parent', 'TAB', 'PRESS', ctrl=True)
|
kmn.keymap_items.new('node.tree_path_parent', 'TAB', 'PRESS', ctrl=True)
|
||||||
kmn.keymap_items.new('lnx.ungroup_group_tree', 'G', 'PRESS', alt=True)
|
kmn.keymap_items.new('lnx.ungroup_group_tree', 'G', 'PRESS', alt=True)
|
||||||
|
# Use custom frame operator that works with all node trees including custom ones
|
||||||
|
kmn.keymap_items.new('lnx.frame_selected_nodes', 'J', 'PRESS', ctrl=True)
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
@ -50,3 +52,4 @@ def unregister():
|
|||||||
kmn.keymap_items.remove(kmn.keymap_items['lnx.edit_group_tree'])
|
kmn.keymap_items.remove(kmn.keymap_items['lnx.edit_group_tree'])
|
||||||
kmn.keymap_items.remove(kmn.keymap_items['node.tree_path_parent'])
|
kmn.keymap_items.remove(kmn.keymap_items['node.tree_path_parent'])
|
||||||
kmn.keymap_items.remove(kmn.keymap_items['lnx.ungroup_group_tree'])
|
kmn.keymap_items.remove(kmn.keymap_items['lnx.ungroup_group_tree'])
|
||||||
|
kmn.keymap_items.remove(kmn.keymap_items['lnx.frame_selected_nodes'])
|
||||||
|
|||||||
@ -1,12 +1,9 @@
|
|||||||
import bpy, math, os, gpu, importlib
|
import bpy, math, os, gpu, bgl, importlib
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from . import utility
|
from . import utility
|
||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
from gpu_extras.batch import batch_for_shader
|
from gpu_extras.batch import batch_for_shader
|
||||||
|
|
||||||
if bpy.app.version < (4, 0, 0):
|
|
||||||
import bgl
|
|
||||||
|
|
||||||
def splitLogLuvAlphaAtlas(imageIn, outDir, quality):
|
def splitLogLuvAlphaAtlas(imageIn, outDir, quality):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,6 @@
|
|||||||
import bpy, blf, os, gpu
|
import bpy, blf, bgl, os, gpu
|
||||||
from gpu_extras.batch import batch_for_shader
|
from gpu_extras.batch import batch_for_shader
|
||||||
|
|
||||||
if bpy.app.version < (4, 0, 0):
|
|
||||||
import bgl
|
|
||||||
|
|
||||||
class ViewportDraw:
|
class ViewportDraw:
|
||||||
|
|
||||||
def __init__(self, context, text):
|
def __init__(self, context, text):
|
||||||
|
|||||||
@ -197,7 +197,10 @@ class CreateStyleNode(LnxLogicTreeNode):
|
|||||||
properties += self.inputs[ind].name + ':'
|
properties += self.inputs[ind].name + ':'
|
||||||
ind += 1
|
ind += 1
|
||||||
|
|
||||||
self['property1'] = properties
|
try:
|
||||||
|
self['property1'] = properties
|
||||||
|
except AttributeError:
|
||||||
|
pass # Skip write if context doesn't allow it
|
||||||
|
|
||||||
return self.get('property0', 60)
|
return self.get('property0', 60)
|
||||||
|
|
||||||
|
|||||||
@ -537,6 +537,40 @@ class LnxAddCallGroupNode(bpy.types.Operator):
|
|||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
class LnxFrameSelectedNodes(bpy.types.Operator):
|
||||||
|
"""Frame selected nodes - works with custom node trees"""
|
||||||
|
bl_idname = 'lnx.frame_selected_nodes'
|
||||||
|
bl_label = 'Frame Selected Nodes'
|
||||||
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
if context.space_data is None or context.space_data.type != 'NODE_EDITOR':
|
||||||
|
return False
|
||||||
|
return context.space_data.edit_tree is not None
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
tree = context.space_data.edit_tree
|
||||||
|
selected_nodes = [n for n in tree.nodes if n.select]
|
||||||
|
|
||||||
|
if not selected_nodes:
|
||||||
|
self.report({'WARNING'}, "No nodes selected")
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
frame = tree.nodes.new('NodeFrame')
|
||||||
|
frame.label = "Frame"
|
||||||
|
|
||||||
|
for node in selected_nodes:
|
||||||
|
node.parent = frame
|
||||||
|
|
||||||
|
for node in tree.nodes:
|
||||||
|
node.select = False
|
||||||
|
frame.select = True
|
||||||
|
tree.nodes.active = frame
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
class LNX_PT_LogicGroupPanel(bpy.types.Panel):
|
class LNX_PT_LogicGroupPanel(bpy.types.Panel):
|
||||||
bl_label = 'Leenkx Logic Group'
|
bl_label = 'Leenkx Logic Group'
|
||||||
bl_idname = 'LNX_PT_LogicGroupPanel'
|
bl_idname = 'LNX_PT_LogicGroupPanel'
|
||||||
@ -575,6 +609,7 @@ __REG_CLASSES = (
|
|||||||
TreeVarNameConflictItem,
|
TreeVarNameConflictItem,
|
||||||
LnxUngroupGroupTree,
|
LnxUngroupGroupTree,
|
||||||
LnxAddCallGroupNode,
|
LnxAddCallGroupNode,
|
||||||
|
LnxFrameSelectedNodes,
|
||||||
LNX_PT_LogicGroupPanel
|
LNX_PT_LogicGroupPanel
|
||||||
)
|
)
|
||||||
register, unregister = bpy.utils.register_classes_factory(__REG_CLASSES)
|
register, unregister = bpy.utils.register_classes_factory(__REG_CLASSES)
|
||||||
|
|||||||
@ -586,6 +586,298 @@ def play_done():
|
|||||||
log.clear()
|
log.clear()
|
||||||
live_patch.stop()
|
live_patch.stop()
|
||||||
|
|
||||||
|
|
||||||
|
_viewport_processes = {}
|
||||||
|
|
||||||
|
def _generate_viewport_id(space_data=None):
|
||||||
|
"""Generate a unique viewport ID from space_data pointer or random."""
|
||||||
|
if space_data is not None:
|
||||||
|
try:
|
||||||
|
return hex(space_data.as_pointer())[-6:]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
import random
|
||||||
|
return hex(random.randint(0, 0xFFFFFF))[2:].zfill(6)
|
||||||
|
|
||||||
|
def _get_viewport_shmem_name(viewport_id):
|
||||||
|
"""Get shared memory name for a specific viewport."""
|
||||||
|
return f"KROM_VIEWPORT_FB_{viewport_id}"
|
||||||
|
|
||||||
|
def _kill_viewport_process(viewport_id):
|
||||||
|
"""Kill a specific viewport's Krom process."""
|
||||||
|
global _viewport_processes
|
||||||
|
|
||||||
|
if viewport_id in _viewport_processes:
|
||||||
|
proc = _viewport_processes[viewport_id]
|
||||||
|
try:
|
||||||
|
proc.terminate()
|
||||||
|
proc.wait(timeout=2)
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
proc.kill()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
del _viewport_processes[viewport_id]
|
||||||
|
|
||||||
|
def _kill_all_viewport_processes():
|
||||||
|
"""Kill all viewport Krom processes."""
|
||||||
|
global _viewport_processes
|
||||||
|
|
||||||
|
for viewport_id in list(_viewport_processes.keys()):
|
||||||
|
_kill_viewport_process(viewport_id)
|
||||||
|
|
||||||
|
if lnx.utils.get_os() == 'win':
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
['tasklist', '/FI', 'IMAGENAME eq Krom.exe', '/FO', 'CSV', '/NH'],
|
||||||
|
capture_output=True, text=True, timeout=5
|
||||||
|
)
|
||||||
|
if 'Krom.exe' in result.stdout:
|
||||||
|
subprocess.run(['taskkill', '/F', '/IM', 'Krom.exe'],
|
||||||
|
capture_output=True, timeout=5)
|
||||||
|
import time
|
||||||
|
time.sleep(0.3)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def run_viewport_runtime(viewport_id, width=1920, height=1080):
|
||||||
|
"""Launch a viewport which gets its own Krom process with unique shared memory."""
|
||||||
|
global _viewport_processes
|
||||||
|
|
||||||
|
if 'Lnx' not in bpy.data.worlds:
|
||||||
|
log.warn('No Lnx world found - cannot start viewport server')
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
_kill_viewport_process(viewport_id)
|
||||||
|
|
||||||
|
shmem_name = _get_viewport_shmem_name(viewport_id)
|
||||||
|
|
||||||
|
wrd = bpy.data.worlds['Lnx']
|
||||||
|
krom_location, krom_path = lnx.utils.krom_paths()
|
||||||
|
path = lnx.utils.get_fp_build() + '/debug/krom'
|
||||||
|
path_resources = path + '-resources'
|
||||||
|
|
||||||
|
if not os.path.exists(path + '/krom.js'):
|
||||||
|
log.warn(f'Krom build not found at {path}/krom.js - build project first')
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
os.chdir(krom_location)
|
||||||
|
|
||||||
|
cmd = [krom_path, path, path_resources]
|
||||||
|
if lnx.utils.get_os() == 'win':
|
||||||
|
cmd.append('--consolepid')
|
||||||
|
cmd.append(str(os.getpid()))
|
||||||
|
if wrd.lnx_audio == 'Disabled':
|
||||||
|
cmd.append('--nosound')
|
||||||
|
|
||||||
|
cmd.append('--viewport-server')
|
||||||
|
cmd.append('--shmem')
|
||||||
|
cmd.append(shmem_name)
|
||||||
|
cmd.append('--viewport-width')
|
||||||
|
cmd.append(str(width))
|
||||||
|
cmd.append('--viewport-height')
|
||||||
|
cmd.append(str(height))
|
||||||
|
|
||||||
|
try:
|
||||||
|
proc = subprocess.Popen(cmd)
|
||||||
|
_viewport_processes[viewport_id] = proc
|
||||||
|
log.info(f'Started: {viewport_id} (shmem={shmem_name})')
|
||||||
|
return proc, shmem_name
|
||||||
|
except Exception as e:
|
||||||
|
log.error(f'Failed to start viewport runtime: {e}')
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
def stop_viewport_runtime(viewport_id):
|
||||||
|
"""Stop a specific viewport's Krom process."""
|
||||||
|
_kill_viewport_process(viewport_id)
|
||||||
|
|
||||||
|
_viewport_build_in_progress = False
|
||||||
|
_viewport_pending_launches = [] # List of (viewport_id, width, height) tuples
|
||||||
|
_viewport_proc_build = None # Separate process tracker for viewport builds
|
||||||
|
|
||||||
|
def build_viewport(viewport_id, width=1920, height=1080):
|
||||||
|
"""Build project for viewport"""
|
||||||
|
global _viewport_build_in_progress, _viewport_pending_launches, profile_time
|
||||||
|
|
||||||
|
wrd = bpy.data.worlds.get('Lnx')
|
||||||
|
if not wrd:
|
||||||
|
log.warn('No Lnx world found - cannot build for viewport')
|
||||||
|
return
|
||||||
|
|
||||||
|
krom_js_path = lnx.utils.get_fp_build() + '/debug/krom/krom.js'
|
||||||
|
if os.path.exists(krom_js_path) and not wrd.lnx_recompile:
|
||||||
|
log.info(f'Using cached viewport build for {viewport_id}')
|
||||||
|
run_viewport_runtime(viewport_id, width, height)
|
||||||
|
return
|
||||||
|
|
||||||
|
pending_entry = (viewport_id, width, height)
|
||||||
|
if pending_entry not in _viewport_pending_launches:
|
||||||
|
_viewport_pending_launches.append(pending_entry)
|
||||||
|
log.info(f'Queued viewport {viewport_id} for launch after build')
|
||||||
|
|
||||||
|
# If a build is already in progress and there is an actual build process, wait
|
||||||
|
# _viewport_proc_build checks viewport ,not state.proc_build (which is for play button)
|
||||||
|
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_id} - launching when ready')
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
log.info(f'Resetting stale viewport build state')
|
||||||
|
_viewport_build_in_progress = False
|
||||||
|
|
||||||
|
_viewport_build_in_progress = True
|
||||||
|
profile_time = time.time()
|
||||||
|
|
||||||
|
log.info(f'Starting viewport build for {len(_viewport_pending_launches)} viewport(s)')
|
||||||
|
|
||||||
|
# Set viewport mode flags but not the is_play flag meant for external launcher
|
||||||
|
state.is_viewport = True
|
||||||
|
state.viewport_width = width
|
||||||
|
state.viewport_height = height
|
||||||
|
state.target = 'krom'
|
||||||
|
state.is_play = False # NOT play mode
|
||||||
|
state.is_publish = False
|
||||||
|
state.is_export = False
|
||||||
|
|
||||||
|
log.clear(clear_warnings=True, clear_errors=True)
|
||||||
|
|
||||||
|
sdk_path = lnx.utils.get_sdk_path()
|
||||||
|
fp = lnx.utils.get_fp()
|
||||||
|
os.chdir(fp)
|
||||||
|
|
||||||
|
sources_path = 'Sources/' + lnx.utils.safestr(wrd.lnx_project_package)
|
||||||
|
if not os.path.exists(sources_path):
|
||||||
|
os.makedirs(sources_path)
|
||||||
|
|
||||||
|
log.info('Exporting scene data...')
|
||||||
|
export_data(fp, sdk_path)
|
||||||
|
|
||||||
|
log.info('Starting Krom compilation for viewport...')
|
||||||
|
compile_viewport(assets_only=(not wrd.lnx_recompile))
|
||||||
|
|
||||||
|
def compile_viewport(assets_only=False):
|
||||||
|
"""Compile for viewport mode using separate process tracking."""
|
||||||
|
global _viewport_proc_build
|
||||||
|
|
||||||
|
wrd = bpy.data.worlds['Lnx']
|
||||||
|
fp = lnx.utils.get_fp()
|
||||||
|
os.chdir(fp)
|
||||||
|
|
||||||
|
node_path = lnx.utils.get_node_path()
|
||||||
|
khamake_path = lnx.utils.get_khamake_path()
|
||||||
|
cmd = [node_path, khamake_path, 'krom']
|
||||||
|
|
||||||
|
ffmpeg_path = lnx.utils.get_ffmpeg_path()
|
||||||
|
if ffmpeg_path not in (None, ''):
|
||||||
|
cmd.append('--ffmpeg')
|
||||||
|
cmd.append(ffmpeg_path)
|
||||||
|
|
||||||
|
cmd.append('-g')
|
||||||
|
cmd.append(lnx.utils.get_gapi())
|
||||||
|
cmd.append('--shaderversion')
|
||||||
|
cmd.append('330')
|
||||||
|
|
||||||
|
if lnx.utils.get_khamake_threads() != 1:
|
||||||
|
cmd.append('--parallelAssetConversion')
|
||||||
|
cmd.append(str(lnx.utils.get_khamake_threads()))
|
||||||
|
|
||||||
|
cmd.append('--to')
|
||||||
|
cmd.append(lnx.utils.build_dir() + '/debug')
|
||||||
|
|
||||||
|
if not wrd.lnx_verbose_output:
|
||||||
|
cmd.append("--quiet")
|
||||||
|
|
||||||
|
if assets_only:
|
||||||
|
cmd.append('--nohaxe')
|
||||||
|
cmd.append('--noproject')
|
||||||
|
|
||||||
|
log.info(f'Running: {" ".join(cmd)}')
|
||||||
|
_viewport_proc_build = run_proc(cmd, viewport_build_done)
|
||||||
|
|
||||||
|
def viewport_build_done():
|
||||||
|
"""Called when viewport build completes - launches all pending Krom processes."""
|
||||||
|
global _viewport_build_in_progress, _viewport_pending_launches, _viewport_proc_build
|
||||||
|
|
||||||
|
log.info('Viewport compilation finished')
|
||||||
|
|
||||||
|
if _viewport_proc_build is None:
|
||||||
|
_viewport_build_in_progress = False
|
||||||
|
return
|
||||||
|
|
||||||
|
result = _viewport_proc_build.poll()
|
||||||
|
_viewport_proc_build = None
|
||||||
|
_viewport_build_in_progress = False
|
||||||
|
state.redraw_ui = True
|
||||||
|
|
||||||
|
if result == 0:
|
||||||
|
bpy.data.worlds['Lnx'].lnx_recompile = False
|
||||||
|
|
||||||
|
if _viewport_pending_launches:
|
||||||
|
pending = _viewport_pending_launches.copy()
|
||||||
|
_viewport_pending_launches.clear()
|
||||||
|
log.info(f'Launching {len(pending)} Krom instance(s)')
|
||||||
|
for viewport_id, width, height in pending:
|
||||||
|
run_viewport_runtime(viewport_id, width, height)
|
||||||
|
else:
|
||||||
|
log.info('No pending viewports to launch')
|
||||||
|
else:
|
||||||
|
_viewport_pending_launches.clear()
|
||||||
|
log.error('Viewport build failed, check console')
|
||||||
|
|
||||||
|
def play_viewport(viewport_id, width=1920, height=1080):
|
||||||
|
"""Launch Krom in a viewport"""
|
||||||
|
global _viewport_build_in_progress, _viewport_pending_launches, _viewport_proc_build
|
||||||
|
|
||||||
|
if not viewport_id:
|
||||||
|
return
|
||||||
|
|
||||||
|
if 'Lnx' not in bpy.data.worlds:
|
||||||
|
return
|
||||||
|
|
||||||
|
wrd = bpy.data.worlds['Lnx']
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
pending_entry = (viewport_id, width, height)
|
||||||
|
if pending_entry not in _viewport_pending_launches:
|
||||||
|
_viewport_pending_launches.append(pending_entry)
|
||||||
|
|
||||||
|
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 # Reset stale state
|
||||||
|
|
||||||
|
_viewport_build_in_progress = True
|
||||||
|
|
||||||
|
sdk_path = lnx.utils.get_sdk_path()
|
||||||
|
fp = lnx.utils.get_fp()
|
||||||
|
os.chdir(fp)
|
||||||
|
|
||||||
|
sources_path = 'Sources/' + lnx.utils.safestr(wrd.lnx_project_package)
|
||||||
|
if not os.path.exists(sources_path):
|
||||||
|
os.makedirs(sources_path)
|
||||||
|
|
||||||
|
# export data same as play() but without setting state.is_play
|
||||||
|
state.target = 'krom'
|
||||||
|
state.is_publish = False
|
||||||
|
state.is_export = False
|
||||||
|
export_data(fp, sdk_path)
|
||||||
|
|
||||||
|
compile_viewport(assets_only=(not wrd.lnx_recompile))
|
||||||
|
|
||||||
|
|
||||||
|
def stop_viewport(viewport_id=None):
|
||||||
|
"""Stop a specific viewport or all viewports."""
|
||||||
|
if viewport_id:
|
||||||
|
_kill_viewport_process(viewport_id)
|
||||||
|
else:
|
||||||
|
_kill_all_viewport_processes()
|
||||||
|
|
||||||
def assets_done():
|
def assets_done():
|
||||||
if state.proc_build == None:
|
if state.proc_build == None:
|
||||||
return
|
return
|
||||||
@ -761,6 +1053,17 @@ def build_success():
|
|||||||
cmd.append(str(pid))
|
cmd.append(str(pid))
|
||||||
if wrd.lnx_audio == 'Disabled':
|
if wrd.lnx_audio == 'Disabled':
|
||||||
cmd.append('--nosound')
|
cmd.append('--nosound')
|
||||||
|
if state.is_viewport:
|
||||||
|
cmd.append('--viewport-server')
|
||||||
|
cmd.append('--shmem')
|
||||||
|
if state.viewport_id:
|
||||||
|
cmd.append(f'KROM_VIEWPORT_FB_{state.viewport_id}')
|
||||||
|
else:
|
||||||
|
cmd.append('KROM_VIEWPORT_FB')
|
||||||
|
cmd.append('--viewport-width')
|
||||||
|
cmd.append(str(state.viewport_width))
|
||||||
|
cmd.append('--viewport-height')
|
||||||
|
cmd.append(str(state.viewport_height))
|
||||||
|
|
||||||
elif state.target.startswith(('windows-hl', 'linux-hl', 'macos-hl')):
|
elif state.target.startswith(('windows-hl', 'linux-hl', 'macos-hl')):
|
||||||
log.info(f"Runtime Hashlink/C target: {state.target}")
|
log.info(f"Runtime Hashlink/C target: {state.target}")
|
||||||
|
|||||||
@ -79,12 +79,6 @@ def add_world_defs():
|
|||||||
assets.add_khafile_def('lnx_shadowmap_atlas_lod')
|
assets.add_khafile_def('lnx_shadowmap_atlas_lod')
|
||||||
assets.add_khafile_def('rp_shadowmap_atlas_lod_subdivisions={0}'.format(int(rpdat.rp_shadowmap_atlas_lod_subdivisions)))
|
assets.add_khafile_def('rp_shadowmap_atlas_lod_subdivisions={0}'.format(int(rpdat.rp_shadowmap_atlas_lod_subdivisions)))
|
||||||
|
|
||||||
# SS
|
|
||||||
if rpdat.rp_ssgi == 'RTGI' or rpdat.rp_ssgi == 'RTAO':
|
|
||||||
if rpdat.rp_ssgi == 'RTGI':
|
|
||||||
wrd.world_defs += '_RTGI'
|
|
||||||
if rpdat.lnx_ssgi_rays == '9':
|
|
||||||
wrd.world_defs += '_SSGICone9'
|
|
||||||
if rpdat.rp_autoexposure:
|
if rpdat.rp_autoexposure:
|
||||||
wrd.world_defs += '_AutoExposure'
|
wrd.world_defs += '_AutoExposure'
|
||||||
|
|
||||||
@ -306,19 +300,25 @@ def build():
|
|||||||
if rpdat.rp_supersampling == '4':
|
if rpdat.rp_supersampling == '4':
|
||||||
assets.add_shader_pass('supersample_resolve')
|
assets.add_shader_pass('supersample_resolve')
|
||||||
|
|
||||||
assets.add_khafile_def('rp_ssgi={0}'.format(rpdat.rp_ssgi))
|
if rpdat.rp_fsr1 != 'Off':
|
||||||
if rpdat.rp_ssgi != 'Off':
|
assets.add_khafile_def('rp_fsr1')
|
||||||
if rpdat.rp_ssgi == 'SSAO':
|
wrd.world_defs += '_FSR1_{0}'.format(rpdat.rp_fsr1)
|
||||||
wrd.world_defs += '_SSAO'
|
assets.add_shader_pass('fsr1_easu_pass')
|
||||||
assets.add_shader_pass('ssao_pass')
|
assets.add_shader_pass('fsr1_rcas_pass')
|
||||||
assets.add_shader_pass('blur_edge_pass')
|
|
||||||
elif rpdat.rp_ssgi == 'SSGI':
|
if rpdat.rp_ssao:
|
||||||
wrd.world_defs += '_SSGI'
|
assets.add_khafile_def('rp_ssao')
|
||||||
assets.add_shader_pass('ssgi_pass')
|
wrd.world_defs += '_SSAO'
|
||||||
assets.add_shader_pass('blur_edge_pass')
|
assets.add_shader_pass('ssao_pass')
|
||||||
else:
|
assets.add_shader_pass('blur_edge_pass')
|
||||||
assets.add_shader_pass('ssgi_pass')
|
if rpdat.lnx_ssao_half_res:
|
||||||
assets.add_shader_pass('blur_edge_pass')
|
assets.add_khafile_def('rp_ssao_half')
|
||||||
|
|
||||||
|
if rpdat.rp_ssgi:
|
||||||
|
assets.add_khafile_def('rp_ssgi')
|
||||||
|
wrd.world_defs += '_SSGI'
|
||||||
|
assets.add_shader_pass('ssgi_pass')
|
||||||
|
assets.add_shader_pass('ssgi_blur_pass')
|
||||||
if rpdat.lnx_ssgi_half_res:
|
if rpdat.lnx_ssgi_half_res:
|
||||||
assets.add_khafile_def('rp_ssgi_half')
|
assets.add_khafile_def('rp_ssgi_half')
|
||||||
|
|
||||||
@ -362,6 +362,7 @@ def build():
|
|||||||
assets.add_khafile_def('rp_stereo')
|
assets.add_khafile_def('rp_stereo')
|
||||||
assets.add_khafile_def('lnx_vr')
|
assets.add_khafile_def('lnx_vr')
|
||||||
wrd.world_defs += '_VR'
|
wrd.world_defs += '_VR'
|
||||||
|
wrd.world_defs += '_VRStereo'
|
||||||
|
|
||||||
has_voxels = lnx.utils.voxel_support()
|
has_voxels = lnx.utils.voxel_support()
|
||||||
if rpdat.rp_voxels != "Off" and has_voxels and rpdat.lnx_material_model == 'Full':
|
if rpdat.rp_voxels != "Off" and has_voxels and rpdat.lnx_material_model == 'Full':
|
||||||
@ -421,7 +422,7 @@ def build():
|
|||||||
wrd.world_defs += '_SSS'
|
wrd.world_defs += '_SSS'
|
||||||
assets.add_shader_pass('sss_pass')
|
assets.add_shader_pass('sss_pass')
|
||||||
|
|
||||||
if (rpdat.rp_ssr and rpdat.lnx_ssr_half_res) or (rpdat.rp_ssgi != 'Off' and rpdat.lnx_ssgi_half_res) or rpdat.rp_voxels != "Off":
|
if (rpdat.rp_ssr and rpdat.lnx_ssr_half_res) or (rpdat.rp_ssao and rpdat.lnx_ssao_half_res) or (rpdat.rp_ssgi and rpdat.lnx_ssgi_half_res) or rpdat.rp_voxels != "Off":
|
||||||
assets.add_shader_pass('downsample_depth')
|
assets.add_shader_pass('downsample_depth')
|
||||||
|
|
||||||
if rpdat.rp_motionblur != 'Off':
|
if rpdat.rp_motionblur != 'Off':
|
||||||
|
|||||||
@ -18,3 +18,7 @@ if not lnx.is_reload(__name__):
|
|||||||
is_export = False
|
is_export = False
|
||||||
is_play = False
|
is_play = False
|
||||||
is_publish = False
|
is_publish = False
|
||||||
|
is_viewport = False
|
||||||
|
viewport_width = 1920
|
||||||
|
viewport_height = 1080
|
||||||
|
viewport_id = None
|
||||||
|
|||||||
@ -16,11 +16,13 @@
|
|||||||
#
|
#
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
import subprocess
|
||||||
from typing import Any, Dict, Optional, Tuple
|
from typing import Any, Dict, Optional, Tuple
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
import os
|
||||||
|
|
||||||
import lnx.assets
|
import lnx.assets as assets
|
||||||
import lnx.log as log
|
import lnx.log as log
|
||||||
import lnx.make_state
|
import lnx.make_state
|
||||||
import lnx.material.cycles_functions as c_functions
|
import lnx.material.cycles_functions as c_functions
|
||||||
@ -1003,7 +1005,12 @@ def make_texture(
|
|||||||
wrd = bpy.data.worlds['Lnx']
|
wrd = bpy.data.worlds['Lnx']
|
||||||
max_size = int(wrd.lnx_max_texture_size)
|
max_size = int(wrd.lnx_max_texture_size)
|
||||||
if max_size > 0 and image is not None:
|
if max_size > 0 and image is not None:
|
||||||
|
original_filepath = filepath
|
||||||
filepath = resize_texture_if_needed(image, filepath, max_size)
|
filepath = resize_texture_if_needed(image, filepath, max_size)
|
||||||
|
|
||||||
|
if filepath != original_filepath:
|
||||||
|
resized_filename = lnx.utils.extract_filename(filepath)
|
||||||
|
tex['file'] = lnx.utils.safestr(resized_filename)
|
||||||
|
|
||||||
# Link image path to assets
|
# Link image path to assets
|
||||||
# TODO: Khamake converts .PNG to .jpg? Convert ext to lowercase on windows
|
# TODO: Khamake converts .PNG to .jpg? Convert ext to lowercase on windows
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
from __future__ import annotations
|
|
||||||
import bpy
|
import bpy
|
||||||
from bpy.types import NodeSocket
|
from bpy.types import NodeSocket
|
||||||
|
|
||||||
@ -36,12 +35,17 @@ def parse_mixshader(node: bpy.types.ShaderNodeMixShader, out_socket: NodeSocket,
|
|||||||
state.curshader.write('{0}float {1} = 1.0 - {2};'.format(prefix, fac_inv_var, fac_var))
|
state.curshader.write('{0}float {1} = 1.0 - {2};'.format(prefix, fac_inv_var, fac_var))
|
||||||
|
|
||||||
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
|
||||||
bc1, rough1, met1, occ1, spec1, opac1, ior1, emi1 = c.parse_shader_input(node.inputs[1])
|
bc1, rough1, met1, occ1, spec1, opac1, ior1, emi1 = c.parse_shader_input(node.inputs[1])
|
||||||
|
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
|
||||||
bc2, rough2, met2, occ2, spec2, opac2, ior2, emi2 = c.parse_shader_input(node.inputs[2])
|
bc2, rough2, met2, occ2, spec2, opac2, ior2, emi2 = c.parse_shader_input(node.inputs[2])
|
||||||
|
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
|
||||||
|
|
||||||
if state.parse_surface:
|
if state.parse_surface:
|
||||||
state.out_basecol = '({0} * {3} + {1} * {2})'.format(bc1, bc2, fac_var, fac_inv_var)
|
state.out_basecol = '({0} * {3} + {1} * {2})'.format(bc1, bc2, fac_var, fac_inv_var)
|
||||||
@ -57,12 +61,17 @@ def parse_mixshader(node: bpy.types.ShaderNodeMixShader, out_socket: NodeSocket,
|
|||||||
|
|
||||||
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
|
||||||
bc1, rough1, met1, occ1, spec1, opac1, ior1, emi1 = c.parse_shader_input(node.inputs[0])
|
bc1, rough1, met1, occ1, spec1, opac1, ior1, emi1 = c.parse_shader_input(node.inputs[0])
|
||||||
|
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
|
||||||
bc2, rough2, met2, occ2, spec2, opac2, ior2, emi2 = c.parse_shader_input(node.inputs[1])
|
bc2, rough2, met2, occ2, spec2, opac2, ior2, emi2 = c.parse_shader_input(node.inputs[1])
|
||||||
|
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
|
||||||
|
|
||||||
if state.parse_surface:
|
if state.parse_surface:
|
||||||
state.out_basecol = '({0} + {1})'.format(bc1, bc2)
|
state.out_basecol = '({0} + {1})'.format(bc1, bc2)
|
||||||
@ -82,6 +91,8 @@ if bpy.app.version < (2, 92, 0):
|
|||||||
if state.parse_surface:
|
if state.parse_surface:
|
||||||
c.write_normal(node.inputs[20])
|
c.write_normal(node.inputs[20])
|
||||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
||||||
|
if node.inputs[1].is_linked or node.inputs[1].default_value > 0.0:
|
||||||
|
mat_state.needs_sss = True
|
||||||
state.out_metallic = c.parse_value_input(node.inputs[4])
|
state.out_metallic = c.parse_value_input(node.inputs[4])
|
||||||
state.out_specular = c.parse_value_input(node.inputs[5])
|
state.out_specular = c.parse_value_input(node.inputs[5])
|
||||||
state.out_roughness = c.parse_value_input(node.inputs[7])
|
state.out_roughness = c.parse_value_input(node.inputs[7])
|
||||||
@ -103,7 +114,8 @@ if bpy.app.version >= (2, 92, 0) and bpy.app.version <= (4, 1, 0):
|
|||||||
if state.parse_surface:
|
if state.parse_surface:
|
||||||
c.write_normal(node.inputs[22])
|
c.write_normal(node.inputs[22])
|
||||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
||||||
# subsurface = c.parse_vector_input(node.inputs[1])
|
if node.inputs[1].is_linked or node.inputs[1].default_value > 0.0:
|
||||||
|
mat_state.needs_sss = True
|
||||||
# subsurface_radius = c.parse_vector_input(node.inputs[2])
|
# subsurface_radius = c.parse_vector_input(node.inputs[2])
|
||||||
# subsurface_color = c.parse_vector_input(node.inputs[3])
|
# subsurface_color = c.parse_vector_input(node.inputs[3])
|
||||||
state.out_metallic = c.parse_value_input(node.inputs[6])
|
state.out_metallic = c.parse_value_input(node.inputs[6])
|
||||||
@ -138,14 +150,27 @@ if bpy.app.version > (4, 1, 0):
|
|||||||
if state.parse_surface:
|
if state.parse_surface:
|
||||||
c.write_normal(node.inputs[5])
|
c.write_normal(node.inputs[5])
|
||||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
||||||
|
|
||||||
|
sss_input = node.inputs.get('Subsurface Weight') or node.inputs.get('Subsurface')
|
||||||
|
if sss_input is not None:
|
||||||
|
if sss_input.is_linked or sss_input.default_value > 0.0:
|
||||||
|
mat_state.needs_sss = True
|
||||||
|
|
||||||
subsurface = c.parse_value_input(node.inputs[7])
|
subsurface = c.parse_value_input(node.inputs[7])
|
||||||
subsurface_radius = c.parse_vector_input(node.inputs[9])
|
subsurface_radius = c.parse_vector_input(node.inputs[9])
|
||||||
subsurface_color = c.parse_vector_input(node.inputs[8])
|
subsurface_color = c.parse_vector_input(node.inputs[8])
|
||||||
state.out_metallic = c.parse_value_input(node.inputs[1])
|
state.out_metallic = c.parse_value_input(node.inputs[1])
|
||||||
if bpy.app.version > (4, 2, 4):
|
|
||||||
state.out_specular = c.parse_value_input(node.inputs[13])
|
specular_socket = node.inputs.get('Specular IOR Level')
|
||||||
|
if specular_socket is not None:
|
||||||
|
state.out_specular = f'({c.parse_value_input(specular_socket)} * 2.0)'
|
||||||
else:
|
else:
|
||||||
state.out_specular = c.parse_value_input(node.inputs[12])
|
specular_socket = node.inputs.get('Specular')
|
||||||
|
if specular_socket is not None:
|
||||||
|
state.out_specular = c.parse_value_input(specular_socket)
|
||||||
|
else:
|
||||||
|
state.out_specular = '1.0'
|
||||||
|
|
||||||
state.out_roughness = c.parse_value_input(node.inputs[2])
|
state.out_roughness = c.parse_value_input(node.inputs[2])
|
||||||
# Prevent black material when metal = 1.0 and roughness = 0.0
|
# Prevent black material when metal = 1.0 and roughness = 0.0
|
||||||
try:
|
try:
|
||||||
@ -278,6 +303,8 @@ def parse_bsdfrefraction(node: bpy.types.ShaderNodeBsdfRefraction, out_socket: N
|
|||||||
|
|
||||||
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
|
||||||
|
mat_state.needs_sss = True
|
||||||
if bpy.app.version < (4, 1, 0):
|
if bpy.app.version < (4, 1, 0):
|
||||||
c.write_normal(node.inputs[4])
|
c.write_normal(node.inputs[4])
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import lnx.log as log
|
|||||||
import lnx.material.cycles as cycles
|
import lnx.material.cycles as cycles
|
||||||
import lnx.material.make_shader as make_shader
|
import lnx.material.make_shader as make_shader
|
||||||
import lnx.material.mat_batch as mat_batch
|
import lnx.material.mat_batch as mat_batch
|
||||||
|
import lnx.material.mat_state as mat_state
|
||||||
import lnx.material.mat_utils as mat_utils
|
import lnx.material.mat_utils as mat_utils
|
||||||
import lnx.node_utils
|
import lnx.node_utils
|
||||||
import lnx.utils
|
import lnx.utils
|
||||||
@ -17,6 +18,7 @@ if lnx.is_reload(__name__):
|
|||||||
cycles = lnx.reload_module(cycles)
|
cycles = lnx.reload_module(cycles)
|
||||||
make_shader = lnx.reload_module(make_shader)
|
make_shader = lnx.reload_module(make_shader)
|
||||||
mat_batch = lnx.reload_module(mat_batch)
|
mat_batch = lnx.reload_module(mat_batch)
|
||||||
|
mat_state = lnx.reload_module(mat_state)
|
||||||
mat_utils = lnx.reload_module(mat_utils)
|
mat_utils = lnx.reload_module(mat_utils)
|
||||||
lnx.node_utils = lnx.reload_module(lnx.node_utils)
|
lnx.node_utils = lnx.reload_module(lnx.node_utils)
|
||||||
lnx.utils = lnx.reload_module(lnx.utils)
|
lnx.utils = lnx.reload_module(lnx.utils)
|
||||||
@ -60,6 +62,7 @@ def parse(material: Material, mat_data, mat_users: Dict[Material, List[Object]],
|
|||||||
shader_data_name = material.lnx_custom_material
|
shader_data_name = material.lnx_custom_material
|
||||||
bind_constants = {'mesh': []}
|
bind_constants = {'mesh': []}
|
||||||
bind_textures = {'mesh': []}
|
bind_textures = {'mesh': []}
|
||||||
|
mat_uses_sss = False
|
||||||
|
|
||||||
make_shader.make_instancing_and_skinning(material, mat_users)
|
make_shader.make_instancing_and_skinning(material, mat_users)
|
||||||
|
|
||||||
@ -77,10 +80,11 @@ def parse(material: Material, mat_data, mat_users: Dict[Material, List[Object]],
|
|||||||
log.warn(f'Material "{material.name}": skipping export of bind texture at slot {idx + 1} ("{item.uniform_name}") with no image selected')
|
log.warn(f'Material "{material.name}": skipping export of bind texture at slot {idx + 1} ("{item.uniform_name}") with no image selected')
|
||||||
|
|
||||||
elif not wrd.lnx_batch_materials or material.name.startswith('lnxdefault'):
|
elif not wrd.lnx_batch_materials or material.name.startswith('lnxdefault'):
|
||||||
rpasses, shader_data, shader_data_name, bind_constants, bind_textures = make_shader.build(material, mat_users, mat_lnxusers)
|
rpasses, shader_data, shader_data_name, bind_constants, bind_textures, mat_uses_sss = make_shader.build(material, mat_users, mat_lnxusers)
|
||||||
sd = shader_data.sd
|
sd = shader_data.sd
|
||||||
else:
|
else:
|
||||||
rpasses, shader_data, shader_data_name, bind_constants, bind_textures = mat_batch.get(material)
|
result = mat_batch.get(material)
|
||||||
|
rpasses, shader_data, shader_data_name, bind_constants, bind_textures, mat_uses_sss = result
|
||||||
sd = shader_data.sd
|
sd = shader_data.sd
|
||||||
|
|
||||||
sss_used = False
|
sss_used = False
|
||||||
@ -106,9 +110,12 @@ def parse(material: Material, mat_data, mat_users: Dict[Material, List[Object]],
|
|||||||
|
|
||||||
elif rpdat.rp_sss_state != 'Off':
|
elif rpdat.rp_sss_state != 'Off':
|
||||||
const = {'name': 'materialID'}
|
const = {'name': 'materialID'}
|
||||||
if needs_sss:
|
# Use per-material SSS flag from shader build
|
||||||
|
if mat_uses_sss:
|
||||||
const['intValue'] = 2
|
const['intValue'] = 2
|
||||||
sss_used = True
|
sss_used = True
|
||||||
|
if '_SSS' not in wrd.world_defs:
|
||||||
|
wrd.world_defs += '_SSS'
|
||||||
else:
|
else:
|
||||||
const['intValue'] = 0
|
const['intValue'] = 0
|
||||||
c['bind_constants'].append(const)
|
c['bind_constants'].append(const)
|
||||||
@ -167,4 +174,10 @@ def material_needs_sss(material: Material) -> bool:
|
|||||||
if sss_node is not None and sss_node.outputs[0].is_linked and (sss_node.inputs[8].is_linked or sss_node.inputs[8].default_value != 0.0):
|
if sss_node is not None and sss_node.outputs[0].is_linked and (sss_node.inputs[8].is_linked or sss_node.inputs[8].default_value != 0.0):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
for principled_node in lnx.node_utils.iter_nodes_by_type(material.node_tree, 'BSDF_PRINCIPLED'):
|
||||||
|
if principled_node is not None and principled_node.outputs[0].is_linked:
|
||||||
|
sss_input = principled_node.inputs.get('Subsurface Weight') or principled_node.inputs.get('Subsurface')
|
||||||
|
if sss_input is not None and (sss_input.is_linked or sss_input.default_value > 0.0):
|
||||||
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|||||||
@ -107,7 +107,7 @@ def write(vert: shader.Shader, frag: shader.Shader):
|
|||||||
if '_MicroShadowing' in wrd.world_defs and not is_mobile:
|
if '_MicroShadowing' in wrd.world_defs and not is_mobile:
|
||||||
frag.write('\t, occlusion')
|
frag.write('\t, occlusion')
|
||||||
if '_SSRS' in wrd.world_defs:
|
if '_SSRS' in wrd.world_defs:
|
||||||
frag.add_uniform('sampler2D gbufferD')
|
frag.add_uniform('sampler2D gbufferD', top=True)
|
||||||
frag.add_uniform('mat4 invVP', '_inverseViewProjectionMatrix')
|
frag.add_uniform('mat4 invVP', '_inverseViewProjectionMatrix')
|
||||||
frag.add_uniform('vec3 eye', '_cameraPosition')
|
frag.add_uniform('vec3 eye', '_cameraPosition')
|
||||||
frag.write(', gbufferD, invVP, eye')
|
frag.write(', gbufferD, invVP, eye')
|
||||||
|
|||||||
@ -198,7 +198,7 @@ def make_deferred(con_mesh, rpasses):
|
|||||||
rpdat = lnx.utils.get_rp()
|
rpdat = lnx.utils.get_rp()
|
||||||
|
|
||||||
lnx_discard = mat_state.material.lnx_discard
|
lnx_discard = mat_state.material.lnx_discard
|
||||||
parse_opacity = lnx_discard or 'translucent' or 'refraction' in rpasses
|
parse_opacity = lnx_discard or 'translucent' in rpasses or 'refraction' in rpasses
|
||||||
|
|
||||||
make_base(con_mesh, parse_opacity=parse_opacity)
|
make_base(con_mesh, parse_opacity=parse_opacity)
|
||||||
|
|
||||||
@ -213,6 +213,7 @@ def make_deferred(con_mesh, rpasses):
|
|||||||
opac = '0.9999' # 1.0 - eps
|
opac = '0.9999' # 1.0 - eps
|
||||||
frag.write('if (opacity < {0}) discard;'.format(opac))
|
frag.write('if (opacity < {0}) discard;'.format(opac))
|
||||||
|
|
||||||
|
|
||||||
frag.add_out(f'vec4 fragColor[GBUF_SIZE]')
|
frag.add_out(f'vec4 fragColor[GBUF_SIZE]')
|
||||||
|
|
||||||
if '_gbuffer2' in wrd.world_defs:
|
if '_gbuffer2' in wrd.world_defs:
|
||||||
@ -281,7 +282,7 @@ def make_deferred(con_mesh, rpasses):
|
|||||||
frag.write('#endif')
|
frag.write('#endif')
|
||||||
|
|
||||||
if '_SSRefraction' in wrd.world_defs or '_VoxelRefract' in wrd.world_defs:
|
if '_SSRefraction' in wrd.world_defs or '_VoxelRefract' in wrd.world_defs:
|
||||||
frag.write('fragColor[GBUF_IDX_REFRACTION] = vec4(1.0, 1.0, 0.0, 1.0);')
|
frag.write('fragColor[GBUF_IDX_REFRACTION] = vec4(1.0, 0.0, 0.0, 1.0);')
|
||||||
|
|
||||||
return con_mesh
|
return con_mesh
|
||||||
|
|
||||||
@ -559,7 +560,7 @@ def make_forward(con_mesh):
|
|||||||
frag.write('fragColor[0] = vec4(direct + indirect, packFloat2(occlusion, specular));')
|
frag.write('fragColor[0] = vec4(direct + indirect, packFloat2(occlusion, specular));')
|
||||||
frag.write('fragColor[1] = vec4(n.xy, roughness, metallic);')
|
frag.write('fragColor[1] = vec4(n.xy, roughness, metallic);')
|
||||||
if rpdat.rp_ss_refraction or rpdat.lnx_voxelgi_refract:
|
if rpdat.rp_ss_refraction or rpdat.lnx_voxelgi_refract:
|
||||||
frag.write(f'fragColor[2] = vec4(1.0, 1.0, 0.0, 0.0);')
|
frag.write(f'fragColor[2] = vec4(1.0, 0.0, 0.0, 1.0);')
|
||||||
|
|
||||||
else:
|
else:
|
||||||
frag.add_out('vec4 fragColor[1]')
|
frag.add_out('vec4 fragColor[1]')
|
||||||
@ -716,8 +717,12 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
|
|||||||
else:
|
else:
|
||||||
frag.write('vec3 indirect = envl;')
|
frag.write('vec3 indirect = envl;')
|
||||||
|
|
||||||
|
if '_VoxelShadow' in wrd.world_defs or '_VoxelGI' in wrd.world_defs:
|
||||||
|
velocity_already_defined = '_gbuffer2' in wrd.world_defs and '_Veloc' in wrd.world_defs
|
||||||
|
if not velocity_already_defined:
|
||||||
|
frag.write('vec2 velocity = gl_FragCoord.xy;')
|
||||||
|
|
||||||
if '_VoxelGI' in wrd.world_defs:
|
if '_VoxelGI' in wrd.world_defs:
|
||||||
frag.write('vec2 velocity = gl_FragCoord.xy;')
|
|
||||||
frag.write('vec4 diffuse_indirect = traceDiffuse(wposition, n, voxels, clipmaps);')
|
frag.write('vec4 diffuse_indirect = traceDiffuse(wposition, n, voxels, clipmaps);')
|
||||||
frag.write('indirect = (diffuse_indirect.rgb * albedo * (1.0 - F) + envl * (1.0 - diffuse_indirect.a)) * voxelgiDiff;')
|
frag.write('indirect = (diffuse_indirect.rgb * albedo * (1.0 - F) + envl * (1.0 - diffuse_indirect.a)) * voxelgiDiff;')
|
||||||
frag.write('if (roughness < 1.0 && specular > 0.0) {')
|
frag.write('if (roughness < 1.0 && specular > 0.0) {')
|
||||||
@ -810,12 +815,11 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
|
|||||||
frag.write(', true, spotData.x, spotData.y, spotDir, spotData.zw, spotRight')
|
frag.write(', true, spotData.x, spotData.y, spotDir, spotData.zw, spotRight')
|
||||||
if '_VoxelShadow' in wrd.world_defs:
|
if '_VoxelShadow' in wrd.world_defs:
|
||||||
frag.write(', voxels, voxelsSDF, clipmaps')
|
frag.write(', voxels, voxelsSDF, clipmaps')
|
||||||
if '_Veloc' in wrd.world_defs or '_VoxelShadow' in wrd.world_defs:
|
|
||||||
frag.write(', velocity')
|
frag.write(', velocity')
|
||||||
if '_MicroShadowing' in wrd.world_defs:
|
if '_MicroShadowing' in wrd.world_defs:
|
||||||
frag.write(', occlusion')
|
frag.write(', occlusion')
|
||||||
if '_SSRS' in wrd.world_defs:
|
if '_SSRS' in wrd.world_defs:
|
||||||
frag.add_uniform('sampler2D gbufferD')
|
frag.add_uniform('sampler2D gbufferD', top=True)
|
||||||
frag.add_uniform('mat4 invVP', '_inverseViewProjectionMatrix')
|
frag.add_uniform('mat4 invVP', '_inverseViewProjectionMatrix')
|
||||||
frag.add_uniform('vec3 eye', '_cameraPosition')
|
frag.add_uniform('vec3 eye', '_cameraPosition')
|
||||||
frag.write(', gbufferD, invVP, eye')
|
frag.write(', gbufferD, invVP, eye')
|
||||||
|
|||||||
@ -6,6 +6,8 @@ else:
|
|||||||
lnx.enable_reload(__name__)
|
lnx.enable_reload(__name__)
|
||||||
|
|
||||||
def morph_pos(vert):
|
def morph_pos(vert):
|
||||||
|
if vert.has_attrib('vec2 texCoordMorph = morph * texUnpack;'):
|
||||||
|
return
|
||||||
rpdat = lnx.utils.get_rp()
|
rpdat = lnx.utils.get_rp()
|
||||||
vert.add_include('compiled.inc')
|
vert.add_include('compiled.inc')
|
||||||
vert.add_include('std/morph_target.glsl')
|
vert.add_include('std/morph_target.glsl')
|
||||||
@ -22,6 +24,8 @@ def morph_pos(vert):
|
|||||||
vert.write_attrib('spos.xyz /= posUnpack;')
|
vert.write_attrib('spos.xyz /= posUnpack;')
|
||||||
|
|
||||||
def morph_nor(vert, is_bone, prep):
|
def morph_nor(vert, is_bone, prep):
|
||||||
|
if vert.has_attrib('vec3 morphNor = vec3(0, 0, 0);'):
|
||||||
|
return
|
||||||
vert.write_attrib('vec3 morphNor = vec3(0, 0, 0);')
|
vert.write_attrib('vec3 morphNor = vec3(0, 0, 0);')
|
||||||
vert.write_attrib('getMorphedNormal(texCoordMorph, vec3(nor.xy, pos.w), morphNor);')
|
vert.write_attrib('getMorphedNormal(texCoordMorph, vec3(nor.xy, pos.w), morphNor);')
|
||||||
if not is_bone:
|
if not is_bone:
|
||||||
|
|||||||
@ -18,7 +18,15 @@ else:
|
|||||||
|
|
||||||
|
|
||||||
def make(context_id):
|
def make(context_id):
|
||||||
con_refract = mat_state.data.add_context({ 'name': context_id, 'depth_write': True, 'compare_mode': 'less', 'cull_mode': 'clockwise' })
|
con_refract = mat_state.data.add_context({
|
||||||
|
'name': context_id,
|
||||||
|
'depth_write': False,
|
||||||
|
'compare_mode': 'less',
|
||||||
|
'cull_mode': 'clockwise',
|
||||||
|
'blend_source': 'blend_one',
|
||||||
|
'blend_destination': 'inverse_source_alpha',
|
||||||
|
'blend_operation': 'add'
|
||||||
|
})
|
||||||
make_mesh.make_forward_base(con_refract, parse_opacity=True, transluc_pass=True)
|
make_mesh.make_forward_base(con_refract, parse_opacity=True, transluc_pass=True)
|
||||||
|
|
||||||
vert = con_refract.vert
|
vert = con_refract.vert
|
||||||
@ -51,14 +59,16 @@ def make(context_id):
|
|||||||
frag.write('const uint matid = 0;')
|
frag.write('const uint matid = 0;')
|
||||||
|
|
||||||
if rpdat.rp_renderer == 'Deferred':
|
if rpdat.rp_renderer == 'Deferred':
|
||||||
frag.write('fragColor[0] = vec4(n.xy, roughness, packFloatInt16(metallic, matid));')
|
frag.write('fragColor[0] = vec4(n.xy, roughness, 1.0);')
|
||||||
frag.write('fragColor[1] = vec4(direct + indirect, packFloat2(occlusion, specular));')
|
frag.write('vec3 finalColor = direct + indirect;')
|
||||||
|
frag.write('fragColor[1] = vec4(finalColor * opacity, opacity);')
|
||||||
else:
|
else:
|
||||||
frag.write('fragColor[0] = vec4(direct + indirect, packFloat2(occlusion, specular));')
|
frag.write('vec3 finalColor = direct + indirect;')
|
||||||
frag.write('fragColor[1] = vec4(n.xy, roughness, metallic);')
|
frag.write('fragColor[0] = vec4(finalColor * opacity, opacity);')
|
||||||
|
frag.write('fragColor[1] = vec4(n.xy, roughness, 1.0);')
|
||||||
|
|
||||||
frag.write('fragColor[2] = vec4(ior, opacity, 0.0, 1.0);')
|
frag.write('fragColor[2] = vec4(ior, 1.0 - opacity, gl_FragCoord.z, 1.0);')
|
||||||
# frag.write('fragColor[2] = vec4(ior, opacity, packFloat2(basecol.r, basecol.g), basecol.b);')
|
# frag.write('fragColor[2] = vec4(ior, 1.0 - opacity, packFloat2(basecol.r, basecol.g), basecol.b);')
|
||||||
|
|
||||||
make_finalize.make(con_refract)
|
make_finalize.make(con_refract)
|
||||||
|
|
||||||
|
|||||||
@ -56,6 +56,9 @@ def build(material: Material, mat_users: Dict[Material, List[Object]], mat_lnxus
|
|||||||
if mat_state.output_node is None:
|
if mat_state.output_node is None:
|
||||||
# Place empty material output to keep compiler happy..
|
# Place empty material output to keep compiler happy..
|
||||||
mat_state.output_node = mat_state.nodes.new('ShaderNodeOutputMaterial')
|
mat_state.output_node = mat_state.nodes.new('ShaderNodeOutputMaterial')
|
||||||
|
|
||||||
|
# reset for each material
|
||||||
|
mat_state.needs_sss = False
|
||||||
|
|
||||||
wrd = bpy.data.worlds['Lnx']
|
wrd = bpy.data.worlds['Lnx']
|
||||||
rpdat = lnx.utils.get_rp()
|
rpdat = lnx.utils.get_rp()
|
||||||
@ -130,7 +133,8 @@ def build(material: Material, mat_users: Dict[Material, List[Object]], mat_lnxus
|
|||||||
shader_data_path = lnx.utils.get_fp_build() + '/compiled/Shaders/' + shader_data_name + '.lnx'
|
shader_data_path = lnx.utils.get_fp_build() + '/compiled/Shaders/' + shader_data_name + '.lnx'
|
||||||
assets.add_shader_data(shader_data_path)
|
assets.add_shader_data(shader_data_path)
|
||||||
|
|
||||||
return rpasses, mat_state.data, shader_data_name, bind_constants, bind_textures
|
needs_sss_result = mat_state.needs_sss
|
||||||
|
return rpasses, mat_state.data, shader_data_name, bind_constants, bind_textures, needs_sss_result
|
||||||
|
|
||||||
|
|
||||||
def write_shaders(rel_path: str, con: ShaderContext, rpass: str, matname: str) -> None:
|
def write_shaders(rel_path: str, con: ShaderContext, rpass: str, matname: str) -> None:
|
||||||
@ -146,6 +150,11 @@ def write_shader(rel_path: str, shader: Shader, ext: str, rpass: str, matname: s
|
|||||||
if shader is None or shader.is_linked:
|
if shader is None or shader.is_linked:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
validation_issues = shader.validate()
|
||||||
|
if validation_issues:
|
||||||
|
for issue in validation_issues:
|
||||||
|
log.warn(f"Shader validation issue in {matname}_{rpass}.{ext}: {issue}. ")
|
||||||
|
|
||||||
# TODO: blend context
|
# TODO: blend context
|
||||||
if rpass == 'mesh' and mat_state.material.lnx_blending:
|
if rpass == 'mesh' and mat_state.material.lnx_blending:
|
||||||
rpass = 'blend'
|
rpass = 'blend'
|
||||||
|
|||||||
@ -7,6 +7,9 @@ else:
|
|||||||
|
|
||||||
|
|
||||||
def skin_pos(vert):
|
def skin_pos(vert):
|
||||||
|
if vert.has_attrib('vec4 skinA;'):
|
||||||
|
return
|
||||||
|
|
||||||
vert.add_include('compiled.inc')
|
vert.add_include('compiled.inc')
|
||||||
|
|
||||||
rpdat = lnx.utils.get_rp()
|
rpdat = lnx.utils.get_rp()
|
||||||
@ -25,6 +28,11 @@ def skin_pos(vert):
|
|||||||
|
|
||||||
|
|
||||||
def skin_nor(vert, is_morph, prep):
|
def skin_nor(vert, is_morph, prep):
|
||||||
|
morph_normal_code = 'wnormal = normalize(N * (morphNor + 2.0 * cross(skinA.xyz'
|
||||||
|
static_normal_code = 'wnormal = normalize(N * (vec3(nor.xy, pos.w) + 2.0 * cross(skinA.xyz'
|
||||||
|
if vert.has_attrib(morph_normal_code) or vert.has_attrib(static_normal_code):
|
||||||
|
return
|
||||||
|
|
||||||
rpdat = lnx.utils.get_rp()
|
rpdat = lnx.utils.get_rp()
|
||||||
if(is_morph):
|
if(is_morph):
|
||||||
vert.write_attrib(prep + 'wnormal = normalize(N * (morphNor + 2.0 * cross(skinA.xyz, cross(skinA.xyz, morphNor) + skinA.w * morphNor)));')
|
vert.write_attrib(prep + 'wnormal = normalize(N * (morphNor + 2.0 * cross(skinA.xyz, cross(skinA.xyz, morphNor) + skinA.w * morphNor)));')
|
||||||
|
|||||||
@ -41,7 +41,7 @@ def make(context_id):
|
|||||||
frag.write('n /= (abs(n.x) + abs(n.y) + abs(n.z));')
|
frag.write('n /= (abs(n.x) + abs(n.y) + abs(n.z));')
|
||||||
frag.write('n.xy = n.z >= 0.0 ? n.xy : octahedronWrap(n.xy);')
|
frag.write('n.xy = n.z >= 0.0 ? n.xy : octahedronWrap(n.xy);')
|
||||||
|
|
||||||
frag.write('vec4 premultipliedReflect = vec4(vec3(direct + indirect * 0.5) * opacity, opacity);');
|
frag.write('vec4 premultipliedReflect = vec4(vec3(direct + indirect) * opacity, opacity);');
|
||||||
frag.write('float w = clamp(pow(min(1.0, premultipliedReflect.a * 10.0) + 0.01, 3.0) * 1e8 * pow(1.0 - (gl_FragCoord.z) * 0.9, 3.0), 1e-2, 3e3);')
|
frag.write('float w = clamp(pow(min(1.0, premultipliedReflect.a * 10.0) + 0.01, 3.0) * 1e8 * pow(1.0 - (gl_FragCoord.z) * 0.9, 3.0), 1e-2, 3e3);')
|
||||||
frag.write('fragColor[0] = vec4(premultipliedReflect.rgb * w, premultipliedReflect.a);')
|
frag.write('fragColor[0] = vec4(premultipliedReflect.rgb * w, premultipliedReflect.a);')
|
||||||
frag.write('fragColor[1] = vec4(premultipliedReflect.a * w, 0.0, 0.0, 1.0);')
|
frag.write('fragColor[1] = vec4(premultipliedReflect.a * w, 0.0, 0.0, 1.0);')
|
||||||
|
|||||||
@ -38,3 +38,4 @@ texture_grad = False # Sample textures using textureGrad()
|
|||||||
con_mesh = None # Mesh context
|
con_mesh = None # Mesh context
|
||||||
uses_instancing = False # Whether the current material has at least one user with instancing enabled
|
uses_instancing = False # Whether the current material has at least one user with instancing enabled
|
||||||
emission_type = EmissionType.NO_EMISSION
|
emission_type = EmissionType.NO_EMISSION
|
||||||
|
needs_sss = False
|
||||||
|
|||||||
@ -361,9 +361,14 @@ class Shader:
|
|||||||
def write_header(self, s):
|
def write_header(self, s):
|
||||||
self.header += s + '\n'
|
self.header += s + '\n'
|
||||||
|
|
||||||
def write_attrib(self, s):
|
def write_attrib(self, s, unique=False):
|
||||||
|
if unique and s in self.main_attribs:
|
||||||
|
return
|
||||||
self.main_attribs += '\t' + s + '\n'
|
self.main_attribs += '\t' + s + '\n'
|
||||||
|
|
||||||
|
def has_attrib(self, s):
|
||||||
|
return s in self.main_attribs
|
||||||
|
|
||||||
def is_equal(self, sh):
|
def is_equal(self, sh):
|
||||||
self.vstruct_to_vsin()
|
self.vstruct_to_vsin()
|
||||||
return self.ins == sh.ins and \
|
return self.ins == sh.ins and \
|
||||||
@ -394,6 +399,25 @@ class Shader:
|
|||||||
for e in vs:
|
for e in vs:
|
||||||
self.add_in('vec' + self.data_size(e['data']) + ' ' + e['name'])
|
self.add_in('vec' + self.data_size(e['data']) + ' ' + e['name'])
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
import re
|
||||||
|
issues = []
|
||||||
|
|
||||||
|
# Check for duplicate variable declarations in main_attribs
|
||||||
|
var_pattern = re.compile(r'\b(vec[234]|float|int|mat[234])\s+(\w+)\s*[;=]')
|
||||||
|
declared_vars = {}
|
||||||
|
|
||||||
|
for line in self.main_attribs.split('\n'):
|
||||||
|
match = var_pattern.search(line)
|
||||||
|
if match:
|
||||||
|
var_type, var_name = match.groups()
|
||||||
|
if var_name in declared_vars:
|
||||||
|
issues.append(f"Duplicate variable declaration: '{var_name}' (type: {var_type})")
|
||||||
|
else:
|
||||||
|
declared_vars[var_name] = var_type
|
||||||
|
|
||||||
|
return issues
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
if self.noprocessing:
|
if self.noprocessing:
|
||||||
return self.main
|
return self.main
|
||||||
|
|||||||
@ -205,7 +205,8 @@ def init_properties():
|
|||||||
name="Physics", default='Auto', update=assets.invalidate_compiler_cache)
|
name="Physics", default='Auto', update=assets.invalidate_compiler_cache)
|
||||||
bpy.types.World.lnx_physics_engine = EnumProperty(
|
bpy.types.World.lnx_physics_engine = EnumProperty(
|
||||||
items=[('Bullet', 'Bullet', 'Bullet'),
|
items=[('Bullet', 'Bullet', 'Bullet'),
|
||||||
('Oimo', 'Oimo', 'Oimo')],
|
('Oimo', 'Oimo', 'Oimo'),
|
||||||
|
('Jolt', 'Jolt', 'Jolt')],
|
||||||
name="Physics Engine", default='Bullet', update=assets.invalidate_compiler_cache)
|
name="Physics Engine", default='Bullet', update=assets.invalidate_compiler_cache)
|
||||||
bpy.types.World.lnx_physics_fixed_step = FloatProperty(
|
bpy.types.World.lnx_physics_fixed_step = FloatProperty(
|
||||||
name="Fixed Step", default=1/60, min=0, max=1,
|
name="Fixed Step", default=1/60, min=0, max=1,
|
||||||
|
|||||||
@ -72,7 +72,8 @@ def update_preset(self, context):
|
|||||||
rpdat.rp_antialiasing = 'SMAA'
|
rpdat.rp_antialiasing = 'SMAA'
|
||||||
rpdat.rp_compositornodes = True
|
rpdat.rp_compositornodes = True
|
||||||
rpdat.rp_volumetriclight = False
|
rpdat.rp_volumetriclight = False
|
||||||
rpdat.rp_ssgi = 'SSAO'
|
rpdat.rp_ssao = True
|
||||||
|
rpdat.rp_ssgi = False
|
||||||
rpdat.lnx_ssrs = False
|
rpdat.lnx_ssrs = False
|
||||||
rpdat.lnx_micro_shadowing = False
|
rpdat.lnx_micro_shadowing = False
|
||||||
rpdat.rp_ssr = False
|
rpdat.rp_ssr = False
|
||||||
@ -110,7 +111,8 @@ def update_preset(self, context):
|
|||||||
rpdat.rp_antialiasing = 'Off'
|
rpdat.rp_antialiasing = 'Off'
|
||||||
rpdat.rp_compositornodes = False
|
rpdat.rp_compositornodes = False
|
||||||
rpdat.rp_volumetriclight = False
|
rpdat.rp_volumetriclight = False
|
||||||
rpdat.rp_ssgi = 'Off'
|
rpdat.rp_ssao = False
|
||||||
|
rpdat.rp_ssgi = False
|
||||||
rpdat.lnx_ssrs = False
|
rpdat.lnx_ssrs = False
|
||||||
rpdat.lnx_micro_shadowing = False
|
rpdat.lnx_micro_shadowing = False
|
||||||
rpdat.rp_ssr = False
|
rpdat.rp_ssr = False
|
||||||
@ -152,8 +154,9 @@ def update_preset(self, context):
|
|||||||
rpdat.rp_antialiasing = 'TAA'
|
rpdat.rp_antialiasing = 'TAA'
|
||||||
rpdat.rp_compositornodes = True
|
rpdat.rp_compositornodes = True
|
||||||
rpdat.rp_volumetriclight = False
|
rpdat.rp_volumetriclight = False
|
||||||
rpdat.rp_ssgi = 'SSGI'
|
rpdat.rp_ssao = True
|
||||||
rpdat.lnx_ssrs = False
|
rpdat.rp_ssgi = True
|
||||||
|
rpdat.lnx_ssrs = True
|
||||||
rpdat.lnx_micro_shadowing = True
|
rpdat.lnx_micro_shadowing = True
|
||||||
rpdat.rp_ssr = True
|
rpdat.rp_ssr = True
|
||||||
rpdat.rp_ss_refraction = True
|
rpdat.rp_ss_refraction = True
|
||||||
@ -193,7 +196,8 @@ def update_preset(self, context):
|
|||||||
rpdat.rp_antialiasing = 'Off'
|
rpdat.rp_antialiasing = 'Off'
|
||||||
rpdat.rp_compositornodes = False
|
rpdat.rp_compositornodes = False
|
||||||
rpdat.rp_volumetriclight = False
|
rpdat.rp_volumetriclight = False
|
||||||
rpdat.rp_ssgi = 'Off'
|
rpdat.rp_ssao = False
|
||||||
|
rpdat.rp_ssgi = False
|
||||||
rpdat.lnx_ssrs = False
|
rpdat.lnx_ssrs = False
|
||||||
rpdat.lnx_micro_shadowing = False
|
rpdat.lnx_micro_shadowing = False
|
||||||
rpdat.rp_ssr = False
|
rpdat.rp_ssr = False
|
||||||
@ -380,6 +384,15 @@ class LnxRPListItem(bpy.types.PropertyGroup):
|
|||||||
('2', '2', '2'),
|
('2', '2', '2'),
|
||||||
('4', '4', '4')],
|
('4', '4', '4')],
|
||||||
name="Super Sampling", description="Screen resolution multiplier", default='1', update=update_renderpath)
|
name="Super Sampling", description="Screen resolution multiplier", default='1', update=update_renderpath)
|
||||||
|
rp_fsr1: EnumProperty(
|
||||||
|
items=[('Off', 'Off', 'Disable FSR'),
|
||||||
|
('Ultra_Quality', 'Ultra Quality', 'FSR Ultra Quality - Maximum sharpening, best quality'),
|
||||||
|
('Quality', 'Quality', 'FSR Quality - High quality sharpening'),
|
||||||
|
('Balanced', 'Balanced', 'FSR Balanced - Balance between quality and performance'),
|
||||||
|
('Performance', 'Performance', 'FSR Performance - Minimal sharpening, best performance'),
|
||||||
|
('Custom', 'Custom', 'FSR Custom - Set your own sharpness value')],
|
||||||
|
name="FSR", description="AMD FidelityFX Super Resolution 1 quality preset", default='Quality', update=update_renderpath)
|
||||||
|
rp_fsr1_sharpness: FloatProperty(name="FSR Sharpness", description="Custom sharpness (0 = max sharp, 1 = no sharpening)", default=0.25, min=0.0, max=1.0, update=update_renderpath)
|
||||||
rp_antialiasing: EnumProperty(
|
rp_antialiasing: EnumProperty(
|
||||||
items=[('Off', 'No AA', 'Off'),
|
items=[('Off', 'No AA', 'Off'),
|
||||||
('FXAA', 'FXAA', 'FXAA'),
|
('FXAA', 'FXAA', 'FXAA'),
|
||||||
@ -389,14 +402,8 @@ class LnxRPListItem(bpy.types.PropertyGroup):
|
|||||||
rp_volumetriclight: BoolProperty(name="Volumetric Light", description="Use volumetric lighting", default=False, update=update_renderpath)
|
rp_volumetriclight: BoolProperty(name="Volumetric Light", description="Use volumetric lighting", default=False, update=update_renderpath)
|
||||||
rp_ssr: BoolProperty(name="SSR", description="Screen space reflections", default=False, update=update_renderpath)
|
rp_ssr: BoolProperty(name="SSR", description="Screen space reflections", default=False, update=update_renderpath)
|
||||||
rp_ss_refraction: BoolProperty(name="SSRefraction", description="Screen space refractions", default=False, update=update_renderpath)
|
rp_ss_refraction: BoolProperty(name="SSRefraction", description="Screen space refractions", default=False, update=update_renderpath)
|
||||||
rp_ssgi: EnumProperty(
|
rp_ssao: BoolProperty(name="SSAO", description="Screen space ambient occlusion", default=False, update=update_renderpath)
|
||||||
items=[('Off', 'No AO', 'Off'),
|
rp_ssgi: BoolProperty(name="SSGI", description="Screen space global illumination", default=False, update=update_renderpath)
|
||||||
('SSAO', 'SSAO', 'Screen space ambient occlusion'),
|
|
||||||
('SSGI', 'SSGI', 'Screen space global illumination')
|
|
||||||
#('RTAO', 'RTAO', 'Ray-traced ambient occlusion')
|
|
||||||
# ('RTGI', 'RTGI', 'Ray-traced global illumination')
|
|
||||||
],
|
|
||||||
name="SSGI", description="Screen space global illumination", default='SSAO', update=update_renderpath)
|
|
||||||
rp_bloom: BoolProperty(name="Bloom", description="Bloom processing", default=False, update=update_renderpath)
|
rp_bloom: BoolProperty(name="Bloom", description="Bloom processing", default=False, update=update_renderpath)
|
||||||
lnx_bloom_follow_blender: BoolProperty(name="Use Blender Settings", description="Use Blender settings instead of Leenkx settings", default=True)
|
lnx_bloom_follow_blender: BoolProperty(name="Use Blender Settings", description="Use Blender settings instead of Leenkx settings", default=True)
|
||||||
rp_motionblur: EnumProperty(
|
rp_motionblur: EnumProperty(
|
||||||
@ -548,17 +555,14 @@ class LnxRPListItem(bpy.types.PropertyGroup):
|
|||||||
lnx_water_density: FloatProperty(name="Density", default=1.0, update=assets.invalidate_shader_cache)
|
lnx_water_density: FloatProperty(name="Density", default=1.0, update=assets.invalidate_shader_cache)
|
||||||
lnx_water_refract: FloatProperty(name="Refract", default=1.0, update=assets.invalidate_shader_cache)
|
lnx_water_refract: FloatProperty(name="Refract", default=1.0, update=assets.invalidate_shader_cache)
|
||||||
lnx_water_reflect: FloatProperty(name="Reflect", default=1.0, update=assets.invalidate_shader_cache)
|
lnx_water_reflect: FloatProperty(name="Reflect", default=1.0, update=assets.invalidate_shader_cache)
|
||||||
lnx_ssgi_strength: FloatProperty(name="Strength", default=1.250, update=assets.invalidate_shader_cache)
|
lnx_ssao_strength: FloatProperty(name="Strength", default=1.0, update=assets.invalidate_shader_cache)
|
||||||
lnx_ssgi_radius: FloatProperty(name="Radius", default=0.750, update=assets.invalidate_shader_cache)
|
lnx_ssao_radius: FloatProperty(name="Radius", default=1.0, update=assets.invalidate_shader_cache)
|
||||||
|
lnx_ssao_samples: IntProperty(name="Samples", default=8, update=assets.invalidate_shader_cache)
|
||||||
|
lnx_ssao_half_res: BoolProperty(name="Half Res", description="Trace in half resolution", default=False, update=assets.invalidate_shader_cache)
|
||||||
|
lnx_ssgi_strength: FloatProperty(name="Strength", default=1.0, update=assets.invalidate_shader_cache)
|
||||||
|
lnx_ssgi_radius: FloatProperty(name="Radius", default=1.0, update=assets.invalidate_shader_cache)
|
||||||
lnx_ssgi_step: FloatProperty(name="Step", default=2.0, update=assets.invalidate_shader_cache)
|
lnx_ssgi_step: FloatProperty(name="Step", default=2.0, update=assets.invalidate_shader_cache)
|
||||||
lnx_ssgi_samples: IntProperty(name="Samples", default=32, update=assets.invalidate_shader_cache)
|
lnx_ssgi_samples: IntProperty(name="Samples", default=16, update=assets.invalidate_shader_cache)
|
||||||
"""
|
|
||||||
lnx_ssgi_rays: EnumProperty(
|
|
||||||
items=[('9', '9', '9'),
|
|
||||||
('5', '5', '5'),
|
|
||||||
],
|
|
||||||
name="Rays", description="Number of rays to trace for RTAO", default='5', update=assets.invalidate_shader_cache)
|
|
||||||
"""
|
|
||||||
lnx_ssgi_half_res: BoolProperty(name="Half Res", description="Trace in half resolution", default=False, update=assets.invalidate_shader_cache)
|
lnx_ssgi_half_res: BoolProperty(name="Half Res", description="Trace in half resolution", default=False, update=assets.invalidate_shader_cache)
|
||||||
lnx_bloom_threshold: FloatProperty(name="Threshold", description="Brightness above which a pixel is contributing to the bloom effect", min=0, default=0.8, update=assets.invalidate_shader_cache)
|
lnx_bloom_threshold: FloatProperty(name="Threshold", description="Brightness above which a pixel is contributing to the bloom effect", min=0, default=0.8, update=assets.invalidate_shader_cache)
|
||||||
lnx_bloom_knee: FloatProperty(name="Knee", description="Smoothen transition around the threshold (higher values = smoother transition)", min=0, max=1, default=0.5, update=assets.invalidate_shader_cache)
|
lnx_bloom_knee: FloatProperty(name="Knee", description="Smoothen transition around the threshold (higher values = smoother transition)", min=0, max=1, default=0.5, update=assets.invalidate_shader_cache)
|
||||||
|
|||||||
@ -385,6 +385,7 @@ class LNX_PT_WorldPropsPanel(bpy.types.Panel):
|
|||||||
|
|
||||||
layout.prop(world, 'lnx_light_ies_texture')
|
layout.prop(world, 'lnx_light_ies_texture')
|
||||||
layout.prop(world, 'lnx_light_clouds_texture')
|
layout.prop(world, 'lnx_light_clouds_texture')
|
||||||
|
layout.separator()
|
||||||
layout.prop(world, 'lnx_use_clouds')
|
layout.prop(world, 'lnx_use_clouds')
|
||||||
col = layout.column(align=True)
|
col = layout.column(align=True)
|
||||||
col.enabled = world.lnx_use_clouds
|
col.enabled = world.lnx_use_clouds
|
||||||
@ -1962,6 +1963,9 @@ class LNX_PT_RenderPathPostProcessPanel(bpy.types.Panel):
|
|||||||
col = layout.column()
|
col = layout.column()
|
||||||
col.prop(rpdat, "rp_antialiasing")
|
col.prop(rpdat, "rp_antialiasing")
|
||||||
col.prop(rpdat, "rp_supersampling")
|
col.prop(rpdat, "rp_supersampling")
|
||||||
|
col.prop(rpdat, "rp_fsr1")
|
||||||
|
if rpdat.rp_fsr1 == 'Custom':
|
||||||
|
col.prop(rpdat, "rp_fsr1_sharpness")
|
||||||
|
|
||||||
col = layout.column()
|
col = layout.column()
|
||||||
col.prop(rpdat, 'lnx_rp_resolution')
|
col.prop(rpdat, 'lnx_rp_resolution')
|
||||||
@ -1971,12 +1975,21 @@ class LNX_PT_RenderPathPostProcessPanel(bpy.types.Panel):
|
|||||||
col.prop(rpdat, 'rp_dynres')
|
col.prop(rpdat, 'rp_dynres')
|
||||||
layout.separator()
|
layout.separator()
|
||||||
|
|
||||||
|
col = layout.column()
|
||||||
|
col.prop(rpdat, "rp_ssao")
|
||||||
|
sub = col.column()
|
||||||
|
sub.enabled = rpdat.rp_ssao
|
||||||
|
sub.prop(rpdat, 'lnx_ssao_half_res')
|
||||||
|
sub.prop(rpdat, 'lnx_ssao_radius')
|
||||||
|
sub.prop(rpdat, 'lnx_ssao_strength')
|
||||||
|
sub.prop(rpdat, 'lnx_ssao_samples')
|
||||||
|
layout.separator()
|
||||||
|
|
||||||
col = layout.column()
|
col = layout.column()
|
||||||
col.prop(rpdat, "rp_ssgi")
|
col.prop(rpdat, "rp_ssgi")
|
||||||
sub = col.column()
|
sub = col.column()
|
||||||
sub.enabled = rpdat.rp_ssgi != 'Off'
|
sub.enabled = rpdat.rp_ssgi
|
||||||
sub.prop(rpdat, 'lnx_ssgi_half_res')
|
sub.prop(rpdat, 'lnx_ssgi_half_res')
|
||||||
#sub.prop(rpdat, 'lnx_ssgi_rays')
|
|
||||||
sub.prop(rpdat, 'lnx_ssgi_radius')
|
sub.prop(rpdat, 'lnx_ssgi_radius')
|
||||||
sub.prop(rpdat, 'lnx_ssgi_strength')
|
sub.prop(rpdat, 'lnx_ssgi_strength')
|
||||||
sub.prop(rpdat, 'lnx_ssgi_samples')
|
sub.prop(rpdat, 'lnx_ssgi_samples')
|
||||||
@ -2880,10 +2893,10 @@ class LNX_PT_PhysicsProps(bpy.types.Panel):
|
|||||||
layout.use_property_decorate = False
|
layout.use_property_decorate = False
|
||||||
wrd = bpy.data.worlds['Lnx']
|
wrd = bpy.data.worlds['Lnx']
|
||||||
|
|
||||||
if wrd.lnx_physics_engine != 'Bullet' and wrd.lnx_physics_engine != 'Oimo':
|
if wrd.lnx_physics_engine != 'Bullet' and wrd.lnx_physics_engine != 'Oimo' and wrd.lnx_physics_engine != 'Jolt':
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.alert = True
|
row.alert = True
|
||||||
row.label(text="Physics debug drawing is only supported for the Bullet and Oimo physics engines")
|
row.label(text="Physics debug drawing is only supported for the Bullet, Oimo, and Jolt physics engines")
|
||||||
|
|
||||||
col = layout.column(align=False)
|
col = layout.column(align=False)
|
||||||
col.prop(wrd, "lnx_physics_fixed_step")
|
col.prop(wrd, "lnx_physics_fixed_step")
|
||||||
@ -2906,10 +2919,10 @@ class LNX_PT_PhysicsDebugDrawingPanel(bpy.types.Panel):
|
|||||||
layout.use_property_decorate = False
|
layout.use_property_decorate = False
|
||||||
wrd = bpy.data.worlds['Lnx']
|
wrd = bpy.data.worlds['Lnx']
|
||||||
|
|
||||||
if wrd.lnx_physics_engine != 'Bullet' and wrd.lnx_physics_engine != 'Oimo':
|
if wrd.lnx_physics_engine != 'Bullet' and wrd.lnx_physics_engine != 'Oimo' and wrd.lnx_physics_engine != 'Jolt':
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.alert = True
|
row.alert = True
|
||||||
row.label(text="Physics debug drawing is only supported for the Bullet and Oimo physics engines")
|
row.label(text="Physics debug drawing is only supported for the Bullet, Oimo, and Jolt physics engines")
|
||||||
|
|
||||||
col = layout.column(align=False)
|
col = layout.column(align=False)
|
||||||
col.prop(wrd, "lnx_physics_dbg_draw_wireframe")
|
col.prop(wrd, "lnx_physics_dbg_draw_wireframe")
|
||||||
|
|||||||
1341
leenkx/blender/lnx/render_engine.py
Normal file
1341
leenkx/blender/lnx/render_engine.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -154,6 +154,17 @@ project.addSources('Sources');
|
|||||||
assets.add_khafile_def('lnx_oimo')
|
assets.add_khafile_def('lnx_oimo')
|
||||||
if not os.path.exists('Libraries/oimo'):
|
if not os.path.exists('Libraries/oimo'):
|
||||||
khafile.write(add_leenkx_library(sdk_path + '/lib/', 'oimo', rel_path=do_relpath_sdk))
|
khafile.write(add_leenkx_library(sdk_path + '/lib/', 'oimo', rel_path=do_relpath_sdk))
|
||||||
|
elif wrd.lnx_physics_engine == 'Jolt':
|
||||||
|
assets.add_khafile_def('lnx_jolt')
|
||||||
|
if not os.path.exists('Libraries/haxejolt'):
|
||||||
|
khafile.write(add_leenkx_library(sdk_path + '/lib/', 'haxejolt', rel_path=do_relpath_sdk))
|
||||||
|
if state.target.startswith('krom') or state.target == 'html5' or state.target == 'node':
|
||||||
|
joltjs_path = sdk_path + '/lib/haxejolt/jolt/jolt.wasm.js'
|
||||||
|
joltjs_path = joltjs_path.replace('\\', '/').replace('//', '/')
|
||||||
|
khafile.write(add_assets(joltjs_path, rel_path=do_relpath_sdk))
|
||||||
|
joltjs_wasm_path = sdk_path + '/lib/haxejolt/jolt/jolt.wasm.wasm'
|
||||||
|
joltjs_wasm_path = joltjs_wasm_path.replace('\\', '/').replace('//', '/')
|
||||||
|
khafile.write(add_assets(joltjs_wasm_path, rel_path=do_relpath_sdk))
|
||||||
|
|
||||||
if export_navigation:
|
if export_navigation:
|
||||||
assets.add_khafile_def('lnx_navigation')
|
assets.add_khafile_def('lnx_navigation')
|
||||||
@ -209,6 +220,8 @@ project.addSources('Sources');
|
|||||||
|
|
||||||
if wrd.lnx_render_viewport:
|
if wrd.lnx_render_viewport:
|
||||||
assets.add_khafile_def('lnx_render_viewport')
|
assets.add_khafile_def('lnx_render_viewport')
|
||||||
|
if state.is_viewport:
|
||||||
|
assets.add_khafile_def('lnx_viewport')
|
||||||
import_traits = list(set(import_traits))
|
import_traits = list(set(import_traits))
|
||||||
for i in range(0, len(import_traits)):
|
for i in range(0, len(import_traits)):
|
||||||
khafile.write("project.addParameter('" + import_traits[i] + "');\n")
|
khafile.write("project.addParameter('" + import_traits[i] + "');\n")
|
||||||
@ -447,9 +460,12 @@ def write_config(resx, resy):
|
|||||||
'window_msaa': int(rpdat.lnx_samples_per_pixel),
|
'window_msaa': int(rpdat.lnx_samples_per_pixel),
|
||||||
'window_scale': 1.0,
|
'window_scale': 1.0,
|
||||||
'rp_supersample': float(rpdat.rp_supersampling),
|
'rp_supersample': float(rpdat.rp_supersampling),
|
||||||
|
'rp_fsr1': rpdat.rp_fsr1 if rpdat.rp_fsr1 != 'Off' else False,
|
||||||
|
'rp_fsr1_sharpness': rpdat.rp_fsr1_sharpness if rpdat.rp_fsr1 == 'Custom' else 0.25,
|
||||||
'rp_shadowmap_cube': rp_shadowmap_cube,
|
'rp_shadowmap_cube': rp_shadowmap_cube,
|
||||||
'rp_shadowmap_cascade': rp_shadowmap_cascade,
|
'rp_shadowmap_cascade': rp_shadowmap_cascade,
|
||||||
'rp_ssgi': rpdat.rp_ssgi != 'Off',
|
'rp_ssao': rpdat.rp_ssao,
|
||||||
|
'rp_ssgi': rpdat.rp_ssgi,
|
||||||
'rp_ssr': rpdat.rp_ssr != 'Off',
|
'rp_ssr': rpdat.rp_ssr != 'Off',
|
||||||
'rp_ss_refraction': rpdat.rp_ss_refraction != 'Off',
|
'rp_ss_refraction': rpdat.rp_ss_refraction != 'Off',
|
||||||
'rp_bloom': rpdat.rp_bloom != 'Off',
|
'rp_bloom': rpdat.rp_bloom != 'Off',
|
||||||
@ -672,18 +688,20 @@ const float waterReflect = """ + str(round(rpdat.lnx_water_reflect * 100) / 100)
|
|||||||
f'const float ditherStrengthValue = {rpdat.lnx_dithering_strength};\n'
|
f'const float ditherStrengthValue = {rpdat.lnx_dithering_strength};\n'
|
||||||
)
|
)
|
||||||
|
|
||||||
if rpdat.rp_ssgi == 'SSAO' or rpdat.rp_ssgi == 'SSGI' or rpdat.rp_ssgi == 'RTAO' or rpdat.rp_volumetriclight:
|
if rpdat.rp_ssao or rpdat.rp_volumetriclight:
|
||||||
f.write(
|
f.write(
|
||||||
"""const float ssaoRadius = """ + str(round(rpdat.lnx_ssgi_radius * 100) / 100) + """;
|
"""const float ssaoRadius = """ + str(round(rpdat.lnx_ssao_radius * 100) / 100) + """;
|
||||||
const float ssaoStrength = """ + str(round(rpdat.lnx_ssgi_strength * 100) / 100) + """;
|
const float ssaoStrength = """ + str(round(rpdat.lnx_ssao_strength * 100) / 100) + """;
|
||||||
const float ssaoScale = """ + ("2.0" if rpdat.lnx_ssgi_half_res else "20.0") + """;
|
const float ssaoScale = """ + ("2.0" if rpdat.lnx_ssao_half_res else "20.0") + """;
|
||||||
|
const int ssaoSamples = """ + str(rpdat.lnx_ssao_samples) + """;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
if rpdat.rp_ssgi == 'RTGI' or rpdat.rp_ssgi == 'RTAO' or rpdat.rp_ssgi == 'SSGI' :
|
if rpdat.rp_ssgi:
|
||||||
f.write(
|
f.write(
|
||||||
"""const int ssgiSamples = """ + str(rpdat.lnx_ssgi_samples) + """;
|
"""const int ssgiSamples = """ + str(rpdat.lnx_ssgi_samples) + """;
|
||||||
const float ssgiRayStep = 0.005 * """ + str(round(rpdat.lnx_ssgi_step * 100) / 100) + """;
|
const float ssgiRayStep = 0.005 * """ + str(round(rpdat.lnx_ssgi_step * 100) / 100) + """;
|
||||||
const float ssgiStrength = """ + str(round(rpdat.lnx_ssgi_strength * 100) / 100) + """;
|
const float ssgiStrength = """ + str(round(rpdat.lnx_ssgi_strength * 100) / 100) + """;
|
||||||
|
const float ssgiRadius = """ + str(round(rpdat.lnx_ssgi_radius * 100) / 100) + """;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
if rpdat.rp_bloom:
|
if rpdat.rp_bloom:
|
||||||
@ -836,7 +854,7 @@ const float voxelgiDiff = """ + str(round(rpdat.lnx_voxelgi_diff * 100) / 100) +
|
|||||||
const float voxelgiRefl = """ + str(round(rpdat.lnx_voxelgi_spec * 100) / 100) + """;
|
const float voxelgiRefl = """ + str(round(rpdat.lnx_voxelgi_spec * 100) / 100) + """;
|
||||||
const float voxelgiRefr = """ + str(round(rpdat.lnx_voxelgi_refr * 100) / 100) + """;
|
const float voxelgiRefr = """ + str(round(rpdat.lnx_voxelgi_refr * 100) / 100) + """;
|
||||||
""")
|
""")
|
||||||
if rpdat.rp_sss:
|
if rpdat.rp_sss or '_SSS' in wrd.world_defs:
|
||||||
f.write(f"const float sssWidth = {rpdat.lnx_sss_width / 10.0};\n")
|
f.write(f"const float sssWidth = {rpdat.lnx_sss_width / 10.0};\n")
|
||||||
|
|
||||||
# Skinning
|
# Skinning
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import lnx.props_camera_render_filter
|
|||||||
import lnx.handlers
|
import lnx.handlers
|
||||||
import lnx.utils
|
import lnx.utils
|
||||||
import lnx.keymap
|
import lnx.keymap
|
||||||
|
import lnx.render_engine
|
||||||
|
|
||||||
reload_started = 0
|
reload_started = 0
|
||||||
|
|
||||||
@ -49,6 +50,7 @@ if lnx.is_reload(__name__):
|
|||||||
lnx.handlers = lnx.reload_module(lnx.handlers)
|
lnx.handlers = lnx.reload_module(lnx.handlers)
|
||||||
lnx.utils = lnx.reload_module(lnx.utils)
|
lnx.utils = lnx.reload_module(lnx.utils)
|
||||||
lnx.keymap = lnx.reload_module(lnx.keymap)
|
lnx.keymap = lnx.reload_module(lnx.keymap)
|
||||||
|
lnx.render_engine = lnx.reload_module(lnx.render_engine)
|
||||||
else:
|
else:
|
||||||
lnx.enable_reload(__name__)
|
lnx.enable_reload(__name__)
|
||||||
|
|
||||||
@ -76,6 +78,7 @@ def register(local_sdk=False):
|
|||||||
lnx.keymap.register()
|
lnx.keymap.register()
|
||||||
lnx.handlers.register()
|
lnx.handlers.register()
|
||||||
lnx.props_collision_filter_mask.register()
|
lnx.props_collision_filter_mask.register()
|
||||||
|
lnx.render_engine.register()
|
||||||
|
|
||||||
lnx.handlers.post_register()
|
lnx.handlers.post_register()
|
||||||
|
|
||||||
@ -104,3 +107,4 @@ def unregister():
|
|||||||
lnx.props_properties.unregister()
|
lnx.props_properties.unregister()
|
||||||
lnx.props_collision_filter_mask.unregister()
|
lnx.props_collision_filter_mask.unregister()
|
||||||
lnx.props_camera_render_filter.unregister()
|
lnx.props_camera_render_filter.unregister()
|
||||||
|
lnx.render_engine.unregister()
|
||||||
|
|||||||
Reference in New Issue
Block a user