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 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):
|
||||
# profile_time = time.time()
|
||||
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']
|
||||
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
|
||||
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 'traits' not in self.output:
|
||||
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 = {
|
||||
'type': 'Script',
|
||||
@ -3204,7 +3218,7 @@ class LeenkxExporter:
|
||||
LeenkxExporter.export_physics = True
|
||||
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'}
|
||||
# ClothModifier
|
||||
if modifier.type == 'CLOTH':
|
||||
@ -3228,7 +3242,7 @@ class LeenkxExporter:
|
||||
def add_hook_mod(o, bobject: bpy.types.Object, target_name, group_name):
|
||||
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'}
|
||||
|
||||
verts = []
|
||||
@ -3254,7 +3268,7 @@ class LeenkxExporter:
|
||||
return
|
||||
|
||||
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
|
||||
|
||||
trait = {
|
||||
|
||||
@ -37,6 +37,7 @@ else:
|
||||
_active_threads: Dict[threading.Thread, Callable] = {}
|
||||
_last_poll_time = 0.0
|
||||
_consecutive_empty_polls = 0
|
||||
_last_render_engine = None
|
||||
|
||||
@persistent
|
||||
def on_depsgraph_update_post(self):
|
||||
@ -141,6 +142,43 @@ def always() -> float:
|
||||
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:
|
||||
"""
|
||||
@ -389,6 +427,7 @@ def register():
|
||||
|
||||
bpy.app.timers.register(always, persistent=True)
|
||||
bpy.app.timers.register(poll_threads, persistent=True)
|
||||
bpy.app.timers.register(check_render_engine, persistent=True)
|
||||
|
||||
if lnx.utils.get_fp() != '':
|
||||
# TODO: On windows, on_load_post is not called when opening .blend file from explorer
|
||||
@ -405,6 +444,7 @@ def register():
|
||||
def unregister():
|
||||
unload_py_libraries()
|
||||
|
||||
bpy.app.timers.unregister(check_render_engine)
|
||||
bpy.app.timers.unregister(poll_threads)
|
||||
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('node.tree_path_parent', 'TAB', 'PRESS', ctrl=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():
|
||||
@ -50,3 +52,4 @@ def unregister():
|
||||
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['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
|
||||
from . import utility
|
||||
from fractions import Fraction
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
|
||||
if bpy.app.version < (4, 0, 0):
|
||||
import bgl
|
||||
|
||||
def splitLogLuvAlphaAtlas(imageIn, outDir, quality):
|
||||
pass
|
||||
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
import bpy, blf, os, gpu
|
||||
import bpy, blf, bgl, os, gpu
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
|
||||
if bpy.app.version < (4, 0, 0):
|
||||
import bgl
|
||||
|
||||
class ViewportDraw:
|
||||
|
||||
def __init__(self, context, text):
|
||||
|
||||
@ -197,7 +197,10 @@ class CreateStyleNode(LnxLogicTreeNode):
|
||||
properties += self.inputs[ind].name + ':'
|
||||
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)
|
||||
|
||||
|
||||
@ -537,6 +537,40 @@ class LnxAddCallGroupNode(bpy.types.Operator):
|
||||
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):
|
||||
bl_label = 'Leenkx Logic Group'
|
||||
bl_idname = 'LNX_PT_LogicGroupPanel'
|
||||
@ -575,6 +609,7 @@ __REG_CLASSES = (
|
||||
TreeVarNameConflictItem,
|
||||
LnxUngroupGroupTree,
|
||||
LnxAddCallGroupNode,
|
||||
LnxFrameSelectedNodes,
|
||||
LNX_PT_LogicGroupPanel
|
||||
)
|
||||
register, unregister = bpy.utils.register_classes_factory(__REG_CLASSES)
|
||||
|
||||
@ -586,6 +586,298 @@ def play_done():
|
||||
log.clear()
|
||||
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():
|
||||
if state.proc_build == None:
|
||||
return
|
||||
@ -761,6 +1053,17 @@ def build_success():
|
||||
cmd.append(str(pid))
|
||||
if wrd.lnx_audio == 'Disabled':
|
||||
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')):
|
||||
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('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:
|
||||
wrd.world_defs += '_AutoExposure'
|
||||
|
||||
@ -306,19 +300,25 @@ def build():
|
||||
if rpdat.rp_supersampling == '4':
|
||||
assets.add_shader_pass('supersample_resolve')
|
||||
|
||||
assets.add_khafile_def('rp_ssgi={0}'.format(rpdat.rp_ssgi))
|
||||
if rpdat.rp_ssgi != 'Off':
|
||||
if rpdat.rp_ssgi == 'SSAO':
|
||||
wrd.world_defs += '_SSAO'
|
||||
assets.add_shader_pass('ssao_pass')
|
||||
assets.add_shader_pass('blur_edge_pass')
|
||||
elif rpdat.rp_ssgi == 'SSGI':
|
||||
wrd.world_defs += '_SSGI'
|
||||
assets.add_shader_pass('ssgi_pass')
|
||||
assets.add_shader_pass('blur_edge_pass')
|
||||
else:
|
||||
assets.add_shader_pass('ssgi_pass')
|
||||
assets.add_shader_pass('blur_edge_pass')
|
||||
if rpdat.rp_fsr1 != 'Off':
|
||||
assets.add_khafile_def('rp_fsr1')
|
||||
wrd.world_defs += '_FSR1_{0}'.format(rpdat.rp_fsr1)
|
||||
assets.add_shader_pass('fsr1_easu_pass')
|
||||
assets.add_shader_pass('fsr1_rcas_pass')
|
||||
|
||||
if rpdat.rp_ssao:
|
||||
assets.add_khafile_def('rp_ssao')
|
||||
wrd.world_defs += '_SSAO'
|
||||
assets.add_shader_pass('ssao_pass')
|
||||
assets.add_shader_pass('blur_edge_pass')
|
||||
if rpdat.lnx_ssao_half_res:
|
||||
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:
|
||||
assets.add_khafile_def('rp_ssgi_half')
|
||||
|
||||
@ -362,6 +362,7 @@ def build():
|
||||
assets.add_khafile_def('rp_stereo')
|
||||
assets.add_khafile_def('lnx_vr')
|
||||
wrd.world_defs += '_VR'
|
||||
wrd.world_defs += '_VRStereo'
|
||||
|
||||
has_voxels = lnx.utils.voxel_support()
|
||||
if rpdat.rp_voxels != "Off" and has_voxels and rpdat.lnx_material_model == 'Full':
|
||||
@ -421,7 +422,7 @@ def build():
|
||||
wrd.world_defs += '_SSS'
|
||||
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')
|
||||
|
||||
if rpdat.rp_motionblur != 'Off':
|
||||
|
||||
@ -18,3 +18,7 @@ if not lnx.is_reload(__name__):
|
||||
is_export = False
|
||||
is_play = False
|
||||
is_publish = False
|
||||
is_viewport = False
|
||||
viewport_width = 1920
|
||||
viewport_height = 1080
|
||||
viewport_id = None
|
||||
|
||||
@ -16,11 +16,13 @@
|
||||
#
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from typing import Any, Dict, Optional, Tuple
|
||||
|
||||
import bpy
|
||||
import os
|
||||
|
||||
import lnx.assets
|
||||
import lnx.assets as assets
|
||||
import lnx.log as log
|
||||
import lnx.make_state
|
||||
import lnx.material.cycles_functions as c_functions
|
||||
@ -1003,7 +1005,12 @@ def make_texture(
|
||||
wrd = bpy.data.worlds['Lnx']
|
||||
max_size = int(wrd.lnx_max_texture_size)
|
||||
if max_size > 0 and image is not None:
|
||||
original_filepath = filepath
|
||||
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
|
||||
# TODO: Khamake converts .PNG to .jpg? Convert ext to lowercase on windows
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
from __future__ import annotations
|
||||
import bpy
|
||||
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))
|
||||
|
||||
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])
|
||||
sss_1 = mat_state.needs_sss
|
||||
ek1 = mat_state.emission_type
|
||||
|
||||
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])
|
||||
sss_2 = mat_state.needs_sss
|
||||
ek2 = mat_state.emission_type
|
||||
mat_state.needs_sss = sss_1 or sss_2
|
||||
|
||||
if state.parse_surface:
|
||||
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:
|
||||
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])
|
||||
sss_1 = mat_state.needs_sss
|
||||
ek1 = mat_state.emission_type
|
||||
|
||||
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])
|
||||
sss_2 = mat_state.needs_sss
|
||||
ek2 = mat_state.emission_type
|
||||
mat_state.needs_sss = sss_1 or sss_2
|
||||
|
||||
if state.parse_surface:
|
||||
state.out_basecol = '({0} + {1})'.format(bc1, bc2)
|
||||
@ -82,6 +91,8 @@ if bpy.app.version < (2, 92, 0):
|
||||
if state.parse_surface:
|
||||
c.write_normal(node.inputs[20])
|
||||
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_specular = c.parse_value_input(node.inputs[5])
|
||||
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:
|
||||
c.write_normal(node.inputs[22])
|
||||
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_color = c.parse_vector_input(node.inputs[3])
|
||||
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:
|
||||
c.write_normal(node.inputs[5])
|
||||
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_radius = c.parse_vector_input(node.inputs[9])
|
||||
subsurface_color = c.parse_vector_input(node.inputs[8])
|
||||
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:
|
||||
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])
|
||||
# Prevent black material when metal = 1.0 and roughness = 0.0
|
||||
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:
|
||||
if state.parse_surface:
|
||||
# Mark that this material needs SSS
|
||||
mat_state.needs_sss = True
|
||||
if bpy.app.version < (4, 1, 0):
|
||||
c.write_normal(node.inputs[4])
|
||||
else:
|
||||
|
||||
@ -8,6 +8,7 @@ import lnx.log as log
|
||||
import lnx.material.cycles as cycles
|
||||
import lnx.material.make_shader as make_shader
|
||||
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.node_utils
|
||||
import lnx.utils
|
||||
@ -17,6 +18,7 @@ if lnx.is_reload(__name__):
|
||||
cycles = lnx.reload_module(cycles)
|
||||
make_shader = lnx.reload_module(make_shader)
|
||||
mat_batch = lnx.reload_module(mat_batch)
|
||||
mat_state = lnx.reload_module(mat_state)
|
||||
mat_utils = lnx.reload_module(mat_utils)
|
||||
lnx.node_utils = lnx.reload_module(lnx.node_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
|
||||
bind_constants = {'mesh': []}
|
||||
bind_textures = {'mesh': []}
|
||||
mat_uses_sss = False
|
||||
|
||||
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')
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
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':
|
||||
const = {'name': 'materialID'}
|
||||
if needs_sss:
|
||||
# Use per-material SSS flag from shader build
|
||||
if mat_uses_sss:
|
||||
const['intValue'] = 2
|
||||
sss_used = True
|
||||
if '_SSS' not in wrd.world_defs:
|
||||
wrd.world_defs += '_SSS'
|
||||
else:
|
||||
const['intValue'] = 0
|
||||
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):
|
||||
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
|
||||
|
||||
@ -107,7 +107,7 @@ def write(vert: shader.Shader, frag: shader.Shader):
|
||||
if '_MicroShadowing' in wrd.world_defs and not is_mobile:
|
||||
frag.write('\t, occlusion')
|
||||
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('vec3 eye', '_cameraPosition')
|
||||
frag.write(', gbufferD, invVP, eye')
|
||||
|
||||
@ -198,7 +198,7 @@ def make_deferred(con_mesh, rpasses):
|
||||
rpdat = lnx.utils.get_rp()
|
||||
|
||||
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)
|
||||
|
||||
@ -213,6 +213,7 @@ def make_deferred(con_mesh, rpasses):
|
||||
opac = '0.9999' # 1.0 - eps
|
||||
frag.write('if (opacity < {0}) discard;'.format(opac))
|
||||
|
||||
|
||||
frag.add_out(f'vec4 fragColor[GBUF_SIZE]')
|
||||
|
||||
if '_gbuffer2' in wrd.world_defs:
|
||||
@ -281,7 +282,7 @@ def make_deferred(con_mesh, rpasses):
|
||||
frag.write('#endif')
|
||||
|
||||
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
|
||||
|
||||
@ -559,7 +560,7 @@ def make_forward(con_mesh):
|
||||
frag.write('fragColor[0] = vec4(direct + indirect, packFloat2(occlusion, specular));')
|
||||
frag.write('fragColor[1] = vec4(n.xy, roughness, metallic);')
|
||||
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:
|
||||
frag.add_out('vec4 fragColor[1]')
|
||||
@ -716,8 +717,12 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
|
||||
else:
|
||||
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:
|
||||
frag.write('vec2 velocity = gl_FragCoord.xy;')
|
||||
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('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')
|
||||
if '_VoxelShadow' in wrd.world_defs:
|
||||
frag.write(', voxels, voxelsSDF, clipmaps')
|
||||
if '_Veloc' in wrd.world_defs or '_VoxelShadow' in wrd.world_defs:
|
||||
frag.write(', velocity')
|
||||
if '_MicroShadowing' in wrd.world_defs:
|
||||
frag.write(', occlusion')
|
||||
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('vec3 eye', '_cameraPosition')
|
||||
frag.write(', gbufferD, invVP, eye')
|
||||
|
||||
@ -6,6 +6,8 @@ else:
|
||||
lnx.enable_reload(__name__)
|
||||
|
||||
def morph_pos(vert):
|
||||
if vert.has_attrib('vec2 texCoordMorph = morph * texUnpack;'):
|
||||
return
|
||||
rpdat = lnx.utils.get_rp()
|
||||
vert.add_include('compiled.inc')
|
||||
vert.add_include('std/morph_target.glsl')
|
||||
@ -22,6 +24,8 @@ def morph_pos(vert):
|
||||
vert.write_attrib('spos.xyz /= posUnpack;')
|
||||
|
||||
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('getMorphedNormal(texCoordMorph, vec3(nor.xy, pos.w), morphNor);')
|
||||
if not is_bone:
|
||||
|
||||
@ -18,7 +18,15 @@ else:
|
||||
|
||||
|
||||
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)
|
||||
|
||||
vert = con_refract.vert
|
||||
@ -51,14 +59,16 @@ def make(context_id):
|
||||
frag.write('const uint matid = 0;')
|
||||
|
||||
if rpdat.rp_renderer == 'Deferred':
|
||||
frag.write('fragColor[0] = vec4(n.xy, roughness, packFloatInt16(metallic, matid));')
|
||||
frag.write('fragColor[1] = vec4(direct + indirect, packFloat2(occlusion, specular));')
|
||||
frag.write('fragColor[0] = vec4(n.xy, roughness, 1.0);')
|
||||
frag.write('vec3 finalColor = direct + indirect;')
|
||||
frag.write('fragColor[1] = vec4(finalColor * opacity, opacity);')
|
||||
else:
|
||||
frag.write('fragColor[0] = vec4(direct + indirect, packFloat2(occlusion, specular));')
|
||||
frag.write('fragColor[1] = vec4(n.xy, roughness, metallic);')
|
||||
frag.write('vec3 finalColor = direct + indirect;')
|
||||
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, opacity, packFloat2(basecol.r, basecol.g), basecol.b);')
|
||||
frag.write('fragColor[2] = vec4(ior, 1.0 - opacity, gl_FragCoord.z, 1.0);')
|
||||
# frag.write('fragColor[2] = vec4(ior, 1.0 - opacity, packFloat2(basecol.r, basecol.g), basecol.b);')
|
||||
|
||||
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:
|
||||
# Place empty material output to keep compiler happy..
|
||||
mat_state.output_node = mat_state.nodes.new('ShaderNodeOutputMaterial')
|
||||
|
||||
# reset for each material
|
||||
mat_state.needs_sss = False
|
||||
|
||||
wrd = bpy.data.worlds['Lnx']
|
||||
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'
|
||||
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:
|
||||
@ -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:
|
||||
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
|
||||
if rpass == 'mesh' and mat_state.material.lnx_blending:
|
||||
rpass = 'blend'
|
||||
|
||||
@ -7,6 +7,9 @@ else:
|
||||
|
||||
|
||||
def skin_pos(vert):
|
||||
if vert.has_attrib('vec4 skinA;'):
|
||||
return
|
||||
|
||||
vert.add_include('compiled.inc')
|
||||
|
||||
rpdat = lnx.utils.get_rp()
|
||||
@ -25,6 +28,11 @@ def skin_pos(vert):
|
||||
|
||||
|
||||
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()
|
||||
if(is_morph):
|
||||
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.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('fragColor[0] = vec4(premultipliedReflect.rgb * w, premultipliedReflect.a);')
|
||||
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
|
||||
uses_instancing = False # Whether the current material has at least one user with instancing enabled
|
||||
emission_type = EmissionType.NO_EMISSION
|
||||
needs_sss = False
|
||||
|
||||
@ -361,9 +361,14 @@ class Shader:
|
||||
def write_header(self, s):
|
||||
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'
|
||||
|
||||
def has_attrib(self, s):
|
||||
return s in self.main_attribs
|
||||
|
||||
def is_equal(self, sh):
|
||||
self.vstruct_to_vsin()
|
||||
return self.ins == sh.ins and \
|
||||
@ -394,6 +399,25 @@ class Shader:
|
||||
for e in vs:
|
||||
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):
|
||||
if self.noprocessing:
|
||||
return self.main
|
||||
|
||||
@ -205,7 +205,8 @@ def init_properties():
|
||||
name="Physics", default='Auto', update=assets.invalidate_compiler_cache)
|
||||
bpy.types.World.lnx_physics_engine = EnumProperty(
|
||||
items=[('Bullet', 'Bullet', 'Bullet'),
|
||||
('Oimo', 'Oimo', 'Oimo')],
|
||||
('Oimo', 'Oimo', 'Oimo'),
|
||||
('Jolt', 'Jolt', 'Jolt')],
|
||||
name="Physics Engine", default='Bullet', update=assets.invalidate_compiler_cache)
|
||||
bpy.types.World.lnx_physics_fixed_step = FloatProperty(
|
||||
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_compositornodes = True
|
||||
rpdat.rp_volumetriclight = False
|
||||
rpdat.rp_ssgi = 'SSAO'
|
||||
rpdat.rp_ssao = True
|
||||
rpdat.rp_ssgi = False
|
||||
rpdat.lnx_ssrs = False
|
||||
rpdat.lnx_micro_shadowing = False
|
||||
rpdat.rp_ssr = False
|
||||
@ -110,7 +111,8 @@ def update_preset(self, context):
|
||||
rpdat.rp_antialiasing = 'Off'
|
||||
rpdat.rp_compositornodes = False
|
||||
rpdat.rp_volumetriclight = False
|
||||
rpdat.rp_ssgi = 'Off'
|
||||
rpdat.rp_ssao = False
|
||||
rpdat.rp_ssgi = False
|
||||
rpdat.lnx_ssrs = False
|
||||
rpdat.lnx_micro_shadowing = False
|
||||
rpdat.rp_ssr = False
|
||||
@ -152,8 +154,9 @@ def update_preset(self, context):
|
||||
rpdat.rp_antialiasing = 'TAA'
|
||||
rpdat.rp_compositornodes = True
|
||||
rpdat.rp_volumetriclight = False
|
||||
rpdat.rp_ssgi = 'SSGI'
|
||||
rpdat.lnx_ssrs = False
|
||||
rpdat.rp_ssao = True
|
||||
rpdat.rp_ssgi = True
|
||||
rpdat.lnx_ssrs = True
|
||||
rpdat.lnx_micro_shadowing = True
|
||||
rpdat.rp_ssr = True
|
||||
rpdat.rp_ss_refraction = True
|
||||
@ -193,7 +196,8 @@ def update_preset(self, context):
|
||||
rpdat.rp_antialiasing = 'Off'
|
||||
rpdat.rp_compositornodes = False
|
||||
rpdat.rp_volumetriclight = False
|
||||
rpdat.rp_ssgi = 'Off'
|
||||
rpdat.rp_ssao = False
|
||||
rpdat.rp_ssgi = False
|
||||
rpdat.lnx_ssrs = False
|
||||
rpdat.lnx_micro_shadowing = False
|
||||
rpdat.rp_ssr = False
|
||||
@ -380,6 +384,15 @@ class LnxRPListItem(bpy.types.PropertyGroup):
|
||||
('2', '2', '2'),
|
||||
('4', '4', '4')],
|
||||
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(
|
||||
items=[('Off', 'No AA', 'Off'),
|
||||
('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_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_ssgi: EnumProperty(
|
||||
items=[('Off', 'No AO', 'Off'),
|
||||
('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_ssao: BoolProperty(name="SSAO", description="Screen space ambient occlusion", default=False, update=update_renderpath)
|
||||
rp_ssgi: BoolProperty(name="SSGI", description="Screen space global illumination", 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)
|
||||
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_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_ssgi_strength: FloatProperty(name="Strength", default=1.250, update=assets.invalidate_shader_cache)
|
||||
lnx_ssgi_radius: FloatProperty(name="Radius", default=0.750, update=assets.invalidate_shader_cache)
|
||||
lnx_ssao_strength: FloatProperty(name="Strength", default=1.0, 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_samples: IntProperty(name="Samples", default=32, 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_samples: IntProperty(name="Samples", default=16, 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_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_clouds_texture')
|
||||
layout.separator()
|
||||
layout.prop(world, 'lnx_use_clouds')
|
||||
col = layout.column(align=True)
|
||||
col.enabled = world.lnx_use_clouds
|
||||
@ -1962,6 +1963,9 @@ class LNX_PT_RenderPathPostProcessPanel(bpy.types.Panel):
|
||||
col = layout.column()
|
||||
col.prop(rpdat, "rp_antialiasing")
|
||||
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.prop(rpdat, 'lnx_rp_resolution')
|
||||
@ -1971,12 +1975,21 @@ class LNX_PT_RenderPathPostProcessPanel(bpy.types.Panel):
|
||||
col.prop(rpdat, 'rp_dynres')
|
||||
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.prop(rpdat, "rp_ssgi")
|
||||
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_rays')
|
||||
sub.prop(rpdat, 'lnx_ssgi_radius')
|
||||
sub.prop(rpdat, 'lnx_ssgi_strength')
|
||||
sub.prop(rpdat, 'lnx_ssgi_samples')
|
||||
@ -2880,10 +2893,10 @@ class LNX_PT_PhysicsProps(bpy.types.Panel):
|
||||
layout.use_property_decorate = False
|
||||
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.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.prop(wrd, "lnx_physics_fixed_step")
|
||||
@ -2906,10 +2919,10 @@ class LNX_PT_PhysicsDebugDrawingPanel(bpy.types.Panel):
|
||||
layout.use_property_decorate = False
|
||||
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.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.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')
|
||||
if not os.path.exists('Libraries/oimo'):
|
||||
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:
|
||||
assets.add_khafile_def('lnx_navigation')
|
||||
@ -209,6 +220,8 @@ project.addSources('Sources');
|
||||
|
||||
if wrd.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))
|
||||
for i in range(0, len(import_traits)):
|
||||
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_scale': 1.0,
|
||||
'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_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_ss_refraction': rpdat.rp_ss_refraction != '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'
|
||||
)
|
||||
|
||||
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(
|
||||
"""const float ssaoRadius = """ + str(round(rpdat.lnx_ssgi_radius * 100) / 100) + """;
|
||||
const float ssaoStrength = """ + str(round(rpdat.lnx_ssgi_strength * 100) / 100) + """;
|
||||
const float ssaoScale = """ + ("2.0" if rpdat.lnx_ssgi_half_res else "20.0") + """;
|
||||
"""const float ssaoRadius = """ + str(round(rpdat.lnx_ssao_radius * 100) / 100) + """;
|
||||
const float ssaoStrength = """ + str(round(rpdat.lnx_ssao_strength * 100) / 100) + """;
|
||||
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(
|
||||
"""const int ssgiSamples = """ + str(rpdat.lnx_ssgi_samples) + """;
|
||||
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 ssgiRadius = """ + str(round(rpdat.lnx_ssgi_radius * 100) / 100) + """;
|
||||
""")
|
||||
|
||||
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 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")
|
||||
|
||||
# Skinning
|
||||
|
||||
@ -20,6 +20,7 @@ import lnx.props_camera_render_filter
|
||||
import lnx.handlers
|
||||
import lnx.utils
|
||||
import lnx.keymap
|
||||
import lnx.render_engine
|
||||
|
||||
reload_started = 0
|
||||
|
||||
@ -49,6 +50,7 @@ if lnx.is_reload(__name__):
|
||||
lnx.handlers = lnx.reload_module(lnx.handlers)
|
||||
lnx.utils = lnx.reload_module(lnx.utils)
|
||||
lnx.keymap = lnx.reload_module(lnx.keymap)
|
||||
lnx.render_engine = lnx.reload_module(lnx.render_engine)
|
||||
else:
|
||||
lnx.enable_reload(__name__)
|
||||
|
||||
@ -76,6 +78,7 @@ def register(local_sdk=False):
|
||||
lnx.keymap.register()
|
||||
lnx.handlers.register()
|
||||
lnx.props_collision_filter_mask.register()
|
||||
lnx.render_engine.register()
|
||||
|
||||
lnx.handlers.post_register()
|
||||
|
||||
@ -104,3 +107,4 @@ def unregister():
|
||||
lnx.props_properties.unregister()
|
||||
lnx.props_collision_filter_mask.unregister()
|
||||
lnx.props_camera_render_filter.unregister()
|
||||
lnx.render_engine.unregister()
|
||||
|
||||
Reference in New Issue
Block a user